/* ============== BotSetUserInfo ============== */ void BotSetUserInfo(bot_state_t *bs, char *key, char *value) { char userinfo[MAX_INFO_STRING]; trap_GetUserinfo(bs->client, userinfo, sizeof(userinfo)); Info_SetValueForKey(userinfo, key, value); trap_SetUserinfo(bs->client, userinfo); ClientUserinfoChanged( bs->client ); }
//----(SA) modified this for head separation gentity_t *AICast_AddCastToGame( gentity_t *ent, char *castname, char *model, char *head, char *sex, char *color, char *handicap ) { int clientNum; gentity_t *bot; char userinfo[MAX_INFO_STRING]; usercmd_t cmd; // create the bot's userinfo userinfo[0] = '\0'; Info_SetValueForKey( userinfo, "name", castname ); Info_SetValueForKey( userinfo, "rate", "25000" ); Info_SetValueForKey( userinfo, "snaps", "20" ); Info_SetValueForKey( userinfo, "handicap", handicap ); Info_SetValueForKey( userinfo, "model", model ); Info_SetValueForKey( userinfo, "head", head ); Info_SetValueForKey( userinfo, "color", color ); // have the server allocate a client slot clientNum = trap_BotAllocateClient(); if ( clientNum == -1 ) { G_Printf( S_COLOR_RED "BotAllocateClient failed\n" ); return NULL; } bot = &g_entities[ clientNum ]; bot->r.svFlags |= SVF_BOT; bot->r.svFlags |= SVF_CASTAI; // flag it for special Cast AI behaviour // register the userinfo trap_SetUserinfo( bot->s.number, userinfo ); // have it connect to the game as a normal client //----(SA) ClientConnect requires a third 'isbot' parameter. setting to qfalse and noting ClientConnect( bot->s.number, qtrue, qfalse ); //----(SA) end // copy the origin/angles across VectorCopy( ent->s.origin, bot->s.origin ); VectorCopy( ent->s.angles, bot->s.angles ); memset( &cmd, 0, sizeof( cmd ) ); ClientBegin( bot->s.number ); // set up the ai AICast_SetupClient( bot->s.number ); return bot; }
/* ================== Cmd_TeamTask_f ================== */ void Cmd_TeamTask_f( gentity_t *ent ) { char userinfo[MAX_INFO_STRING]; char arg[MAX_TOKEN_CHARS]; int task; int client = ent->client - level.clients; if ( trap_Argc() != 2 ) { return; } trap_Argv( 1, arg, sizeof( arg ) ); task = atoi( arg ); trap_GetUserinfo(client, userinfo, sizeof(userinfo)); Info_SetValueForKey(userinfo, "teamtask", va("%d", task)); trap_SetUserinfo(client, userinfo); ClientUserinfoChanged(client); }
/* =============== G_AddBot =============== */ static void G_AddBot( const char *name, float skill, const char *team, const char *pclass, int delay, char *altname) { int clientNum; char *botinfo; gentity_t *bot; char *key; char *s; char *botname; char *model; char userinfo[MAX_INFO_STRING]; int preTeam = 0; // get the botinfo from bots.txt botinfo = G_GetBotInfoByName( name ); if ( !botinfo ) { G_Printf( S_COLOR_RED "Error: Bot '%s' not defined\n", name ); return; } // create the bot's userinfo userinfo[0] = '\0'; botname = Info_ValueForKey( botinfo, "funname" ); if( !botname[0] ) { botname = Info_ValueForKey( botinfo, "name" ); } // check for an alternative name if (altname && altname[0]) { botname = altname; } Info_SetValueForKey( userinfo, "name", botname ); Info_SetValueForKey( userinfo, "rate", "25000" ); Info_SetValueForKey( userinfo, "snaps", "20" ); Info_SetValueForKey( userinfo, "skill", va("%1.2f", skill) ); /* if ( skill >= 1 && skill < 2 ) { Info_SetValueForKey( userinfo, "handicap", "50" ); } else if ( skill >= 2 && skill < 3 ) { Info_SetValueForKey( userinfo, "handicap", "70" ); } else if ( skill >= 3 && skill < 4 ) { Info_SetValueForKey( userinfo, "handicap", "90" ); } */ key = "model"; model = Info_ValueForKey( botinfo, key ); if ( !*model ) { model = "munro/main/default"; //RPG-X MODEL SYSTEM } Info_SetValueForKey( userinfo, key, model ); key = "gender"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) { s = "male"; } Info_SetValueForKey( userinfo, "sex", s ); key = "color"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) { s = "4"; } Info_SetValueForKey( userinfo, key, s ); s = Info_ValueForKey(botinfo, "aifile"); if (!*s ) { trap_Printf( S_COLOR_RED "Error: bot has no aifile specified\n" ); return; } // have the server allocate a client slot clientNum = trap_BotAllocateClient(); if ( clientNum == -1 ) { G_Printf( S_COLOR_RED "Unable to add bot. All player slots are in use.\n" ); G_Printf( S_COLOR_RED "Start server with more 'open' slots (or check setting of sv_maxclients cvar).\n" ); return; } // initialize the bot settings if( !team || !*team ) { if( g_gametype.integer >= GT_TEAM ) { if( PickTeam(clientNum) == TEAM_RED) { team = "red"; } else { team = "blue"; } } else { team = "red"; } } Info_SetValueForKey( userinfo, "characterfile", Info_ValueForKey( botinfo, "aifile" ) ); Info_SetValueForKey( userinfo, "skill", va( "%5.2f", skill ) ); Info_SetValueForKey( userinfo, "team", team ); bot = &g_entities[ clientNum ]; bot->r.svFlags |= SVF_BOT; bot->inuse = qtrue; // register the userinfo trap_SetUserinfo( clientNum, userinfo ); if (g_gametype.integer >= GT_TEAM) { if (team && Q_stricmp(team, "red") == 0) { bot->client->sess.sessionTeam = TEAM_RED; } else if (team && Q_stricmp(team, "blue") == 0) { bot->client->sess.sessionTeam = TEAM_BLUE; } else { bot->client->sess.sessionTeam = PickTeam( -1 ); } } preTeam = bot->client->sess.sessionTeam; // have it connect to the game as a normal client if ( ClientConnect( clientNum, qtrue, qtrue ) ) { return; } if (bot->client->sess.sessionTeam != preTeam) { trap_GetUserinfo(clientNum, userinfo, MAX_INFO_STRING); if (bot->client->sess.sessionTeam == TEAM_SPECTATOR) { bot->client->sess.sessionTeam = preTeam; } if (bot->client->sess.sessionTeam == TEAM_RED) { team = "Red"; } else { team = "Blue"; } Info_SetValueForKey( userinfo, "team", team ); trap_SetUserinfo( clientNum, userinfo ); bot->client->ps.persistant[ PERS_TEAM ] = bot->client->sess.sessionTeam; G_ReadSessionData( bot->client ); ClientUserinfoChanged( clientNum ); } if( delay == 0 ) { ClientBegin( clientNum, qfalse, qfalse, qfalse ); return; } AddBotToSpawnQueue( clientNum, delay ); }
/* ============= BotInitialize Initialize all internal data in the bot state ============= */ void BotInitialize(bot_state_t *bs) { int i; char gender[MAX_CHARACTERISTIC_PATH]; char userinfo[MAX_INFO_STRING]; // No valid last command time exists, but make the bot do its AI as if nothing // special has happened recently bs->last_command_time_ms = server_time_ms; // Set the team (red, blue, or free) when not in tournament mode if (g_gametype.integer != GT_TOURNAMENT) trap_EA_Command(bs->client, va("team %s", bs->settings.team)); // Set the bot gender trap_Characteristic_String(bs->character, CHARACTERISTIC_GENDER, gender, sizeof(gender)); trap_GetUserinfo(bs->client, userinfo, sizeof(userinfo)); Info_SetValueForKey(userinfo, "sex", gender); trap_SetUserinfo(bs->client, userinfo); // Set the chat gender if (gender[0] == 'm' || gender[0] == 'M') trap_BotSetChatGender(bs->cs, CHAT_GENDERMALE); else if (gender[0] == 'f' || gender[0] == 'F') trap_BotSetChatGender(bs->cs, CHAT_GENDERFEMALE); else trap_BotSetChatGender(bs->cs, CHAT_GENDERLESS); // Set the chat name trap_BotSetChatName(bs->cs, EntityNameFast(bs->ent), bs->client); #ifdef DEBUG_AI // Initialize debug settings bs->debug_flags = 0x00000000; bs->use_weapon = WP_NONE; #endif // Load the skill and accuracy characteristics for each weapon for (i = 0; i < WP_NUM_WEAPONS; i++) BotWeaponCharsLoad(bs, i); // Load the bot's reaction times BotReactionLoad(bs); // Cache the chat attack characteristic, since it's used a lot bs->chat_attack = (.5 <= trap_Characteristic_BFloat(bs->character, CHARACTERISTIC_EASY_FRAGGER, 0, 1)); // Initialize enemies BotAimEnemySet(bs, NULL, NULL); bs->goal_enemy = NULL; // Initialize the awareness engine BotAwarenessReset(bs); // Initialize the main goal BotGoalReset(bs); // Item tracking, timing, and statistics BotItemReset(bs); // Accuracy data for different aim zones BotAccuracyReset(bs); }
/* =============== G_AddBot =============== */ static void G_AddBot( const char *name, float skill, const char *team, int delay, char *altname) { int clientNum; char *botinfo; gentity_t *bot; char *key; char *s; char *botname; char *model; // char *headmodel; char userinfo[MAX_INFO_STRING]; int preTeam = 0; // get the botinfo from bots.txt botinfo = G_GetBotInfoByName( name ); if ( !botinfo ) { G_Printf( S_COLOR_RED "Error: Bot '%s' not defined\n", name ); return; } // create the bot's userinfo userinfo[0] = '\0'; botname = Info_ValueForKey( botinfo, "funname" ); if( !botname[0] ) { botname = Info_ValueForKey( botinfo, "name" ); } // check for an alternative name if (altname && altname[0]) { botname = altname; } Info_SetValueForKey( userinfo, "name", botname ); Info_SetValueForKey( userinfo, "rate", "25000" ); Info_SetValueForKey( userinfo, "snaps", "20" ); Info_SetValueForKey( userinfo, "skill", va("%1.2f", skill) ); if ( skill >= 1 && skill < 2 ) { Info_SetValueForKey( userinfo, "handicap", "50" ); } else if ( skill >= 2 && skill < 3 ) { Info_SetValueForKey( userinfo, "handicap", "70" ); } else if ( skill >= 3 && skill < 4 ) { Info_SetValueForKey( userinfo, "handicap", "90" ); } key = "model"; model = Info_ValueForKey( botinfo, key ); if ( !*model ) { model = "kyle/default"; } Info_SetValueForKey( userinfo, key, model ); /* key = "headmodel"; headmodel = Info_ValueForKey( botinfo, key ); if ( !*headmodel ) { headmodel = model; } Info_SetValueForKey( userinfo, key, headmodel ); key = "team_headmodel"; Info_SetValueForKey( userinfo, key, headmodel ); */ key = "gender"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) { s = "male"; } Info_SetValueForKey( userinfo, "sex", s ); key = "color1"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) { s = "4"; } Info_SetValueForKey( userinfo, key, s ); key = "color2"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) { s = "4"; } Info_SetValueForKey( userinfo, key, s ); key = "saber1"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) { s = "single_1"; } Info_SetValueForKey( userinfo, key, s ); key = "saber2"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) { s = "none"; } Info_SetValueForKey( userinfo, key, s ); s = Info_ValueForKey(botinfo, "personality"); if (!*s ) { Info_SetValueForKey( userinfo, "personality", "botfiles/default.jkb" ); } else { Info_SetValueForKey( userinfo, "personality", s ); } // have the server allocate a client slot clientNum = trap_BotAllocateClient(); if ( clientNum == -1 ) { // G_Printf( S_COLOR_RED "Unable to add bot. All player slots are in use.\n" ); // G_Printf( S_COLOR_RED "Start server with more 'open' slots.\n" ); trap_SendServerCommand( -1, va("print \"%s\n\"", G_GetStringEdString("MP_SVGAME", "UNABLE_TO_ADD_BOT"))); return; } // initialize the bot settings if( !team || !*team ) { if( g_gametype.integer >= GT_TEAM ) { if( PickTeam(clientNum) == TEAM_RED) { team = "red"; } else { team = "blue"; } } else { team = "red"; } } // Info_SetValueForKey( userinfo, "characterfile", Info_ValueForKey( botinfo, "aifile" ) ); Info_SetValueForKey( userinfo, "skill", va( "%5.2f", skill ) ); Info_SetValueForKey( userinfo, "team", team ); bot = &g_entities[ clientNum ]; bot->r.svFlags |= SVF_BOT; bot->inuse = qtrue; // register the userinfo trap_SetUserinfo( clientNum, userinfo ); if (g_gametype.integer >= GT_TEAM) { if (team && Q_stricmp(team, "red") == 0) { bot->client->sess.sessionTeam = TEAM_RED; } else if (team && Q_stricmp(team, "blue") == 0) { bot->client->sess.sessionTeam = TEAM_BLUE; } else { bot->client->sess.sessionTeam = PickTeam( -1 ); } } if (g_gametype.integer == GT_SIEGE) { bot->client->sess.siegeDesiredTeam = bot->client->sess.sessionTeam; bot->client->sess.sessionTeam = TEAM_SPECTATOR; } preTeam = bot->client->sess.sessionTeam; // have it connect to the game as a normal client if ( ClientConnect( clientNum, qtrue, qtrue ) ) { return; } if (bot->client->sess.sessionTeam != preTeam) { trap_GetUserinfo(clientNum, userinfo, MAX_INFO_STRING); if (bot->client->sess.sessionTeam == TEAM_SPECTATOR) { bot->client->sess.sessionTeam = preTeam; } if (bot->client->sess.sessionTeam == TEAM_RED) { team = "Red"; } else { if (g_gametype.integer == GT_SIEGE) { if (bot->client->sess.sessionTeam == TEAM_BLUE) { team = "Blue"; } else { team = "s"; } } else { team = "Blue"; } } Info_SetValueForKey( userinfo, "team", team ); trap_SetUserinfo( clientNum, userinfo ); bot->client->ps.persistant[ PERS_TEAM ] = bot->client->sess.sessionTeam; G_ReadSessionData( bot->client ); ClientUserinfoChanged( clientNum ); } if (g_gametype.integer == GT_DUEL || g_gametype.integer == GT_POWERDUEL) { int loners = 0; int doubles = 0; bot->client->sess.duelTeam = 0; G_PowerDuelCount(&loners, &doubles, qtrue); if (!doubles || loners > (doubles/2)) { bot->client->sess.duelTeam = DUELTEAM_DOUBLE; } else { bot->client->sess.duelTeam = DUELTEAM_LONE; } bot->client->sess.sessionTeam = TEAM_SPECTATOR; SetTeam(bot, "s"); } else { if( delay == 0 ) { ClientBegin( clientNum, qfalse ); return; } AddBotToSpawnQueue( clientNum, delay ); } }
/* =============== G_AddBot =============== */ static void G_AddBot( const char *name, float skill, const char *team, int delay, char *altname) { int value; int connectionNum; int clientNum; int t; char *botinfo; char *key; char *s; char *botname; char *model; char *headmodel; char userinfo[MAX_INFO_STRING]; qboolean modelSet; // have the server allocate a client slot value = trap_BotAllocateClient(); if ( value == -1 ) { G_Printf( S_COLOR_RED "Unable to add bot. All player slots are in use.\n" ); G_Printf( S_COLOR_RED "Start server with more 'open' slots (or check setting of sv_maxclients cvar).\n" ); return; } // get connection and client numbers connectionNum = value & 0xFFFF; clientNum = value >> 16; // set default team if( !team || !*team ) { if( g_gametype.integer >= GT_TEAM ) { if( PickTeam(clientNum) == TEAM_RED) { team = "red"; } else { team = "blue"; } } else { team = "free"; } } // get the botinfo from bots.txt if (Q_stricmp(name, "random") == 0) { if (Q_stricmp(team, "blue") == 0) t = TEAM_BLUE; else if (Q_stricmp(team, "red") == 0) t = TEAM_RED; else t = TEAM_FREE; // get info of a randomly selected bot botinfo = G_GetBotInfoByNumber( G_SelectRandomBotForAdd( t ) ); } else { // get info of the bot botinfo = G_GetBotInfoByName( name ); } if ( !botinfo ) { G_Printf( S_COLOR_RED "Error: Bot '%s' not defined\n", name ); trap_BotFreeClient(clientNum); return; } // create the bot's userinfo userinfo[0] = '\0'; botname = Info_ValueForKey( botinfo, "funname" ); if( !botname[0] ) { botname = Info_ValueForKey( botinfo, "name" ); } // check for an alternative name if (altname && altname[0]) { botname = altname; } Info_SetValueForKey( userinfo, "name", botname ); Info_SetValueForKey( userinfo, "rate", "25000" ); Info_SetValueForKey( userinfo, "snaps", "20" ); Info_SetValueForKey( userinfo, "skill", va("%.2f", skill) ); Info_SetValueForKey( userinfo, "teampref", team ); if ( skill >= 1 && skill < 2 ) { Info_SetValueForKey( userinfo, "handicap", "50" ); } else if ( skill >= 2 && skill < 3 ) { Info_SetValueForKey( userinfo, "handicap", "70" ); } else if ( skill >= 3 && skill < 4 ) { Info_SetValueForKey( userinfo, "handicap", "90" ); } key = "model"; model = Info_ValueForKey( botinfo, key ); modelSet = ( *model ); if ( !modelSet ) { model = DEFAULT_MODEL; } Info_SetValueForKey( userinfo, key, model ); key = "team_model"; Info_SetValueForKey( userinfo, key, model ); key = "headmodel"; headmodel = Info_ValueForKey( botinfo, key ); if ( !*headmodel ) { if (!modelSet) { headmodel = DEFAULT_HEAD; } else { headmodel = model; } } Info_SetValueForKey( userinfo, key, headmodel ); key = "team_headmodel"; Info_SetValueForKey( userinfo, key, headmodel ); key = "color1"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) { s = va("%d", DEFAULT_CLIENT_COLOR1); } Info_SetValueForKey( userinfo, key, s ); key = "color2"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) { s = va("%d", DEFAULT_CLIENT_COLOR2); } Info_SetValueForKey( userinfo, key, s ); s = Info_ValueForKey(botinfo, "aifile"); if (!*s ) { trap_Print( S_COLOR_RED "Error: bot has no aifile specified\n" ); trap_BotFreeClient(clientNum); return; } Info_SetValueForKey( userinfo, "characterfile", s ); // register the userinfo trap_SetUserinfo( clientNum, userinfo ); // have it connect to the game as a normal client if ( ClientConnect( clientNum, qtrue, qtrue, connectionNum, 0 ) ) { return; } if( delay == 0 ) { ClientBegin( clientNum ); return; } AddBotToSpawnQueue( clientNum, delay ); }
/* =========== ClientUserInfoChanged Called from ClientConnect when the player first connects and directly by the server system when the player updates a userinfo variable. The game can override any of the settings and call trap_SetUserinfo if desired. ============ */ void ClientUserinfoChanged(int clientNum) { gentity_t *ent; char *s; char oldname[MAX_STRING_CHARS]; char userinfo[MAX_INFO_STRING]; gclient_t *client; char *ip = NULL; // Nico, used to store client ip char *rate = NULL; // Nico, used to store client rate char *snaps = NULL; // Nico, used to store client snaps char *name = NULL; // Nico, used to store client name char oldAuthToken[MAX_QPATH]; // Nico, used to see if auth token was changed ent = g_entities + clientNum; client = ent->client; client->ps.clientNum = clientNum; // Nico, flood protection if (ClientIsFlooding(ent)) { G_LogPrintf("Dropping client %d: flooded userinfo\n", clientNum); trap_DropClient(clientNum, "^1You were kicked because of flooded userinfo", 0); return; } trap_GetUserinfo(clientNum, userinfo, sizeof (userinfo)); // Nico, perform security checks on userinfo string if (!checkUserinfoString(clientNum, userinfo)) { return; } if (g_developer.integer || *g_log.string || g_dedicated.integer) { G_Printf("Userinfo: %s\n", userinfo); } // check for local client ip = Info_ValueForKey(userinfo, "ip"); Q_strncpyz(client->pers.ip, ip, IP_MAX_LENGTH); if (ip && !strcmp(ip, "localhost")) { client->pers.localClient = qtrue; level.fLocalHost = qtrue; client->sess.referee = RL_REFEREE; } // Nico, store rate and snaps rate = Info_ValueForKey(userinfo, "rate"); client->pers.rate = atoi(rate); snaps = Info_ValueForKey(userinfo, "snaps"); client->pers.snaps = atoi(snaps); // Nico, backup old auth token Q_strncpyz(oldAuthToken, client->pers.authToken, sizeof (oldAuthToken)); s = Info_ValueForKey(userinfo, "cg_uinfo"); sscanf(s, "%i %i %i %i %s %i %i %i %i %i %i %i %i %i", &client->pers.clientFlags, &client->pers.clientTimeNudge, &client->pers.clientMaxPackets, // Nico, max FPS &client->pers.maxFPS, // Nico, auth Token (char *)&client->pers.authToken, // Nico, load view angles on load &client->pers.loadViewAngles, // Nico, load weapon on load &client->pers.loadWeapon, // Nico, load position when player dies &client->pers.autoLoad, // Nico, cgaz &client->pers.cgaz, // Nico, hideme &client->pers.hideme, // Nico, client auto demo record setting &client->pers.autoDemo, // Nico, automatically load checkpoints &client->pers.autoLoadCheckpoints, // Nico, persistant specLock (int *)&client->sess.specLocked, // Nico, keep all demos &client->pers.keepAllDemos ); // Nico, check if auth token was changed if (oldAuthToken[0] != '\0' && Q_stricmp(oldAuthToken, client->pers.authToken) && client->sess.logged) { // Nico, auth token was changed => logout player if he was logged in CP("cp \"You are no longer logged in!\n\""); G_LogPrintf("ClientUserinfoChanged: authToken changed for client %d, forcing logout\n", clientNum); ent->client->sess.logged = qfalse; } client->pers.autoActivate = (client->pers.clientFlags & CGF_AUTOACTIVATE) ? PICKUP_TOUCH : PICKUP_ACTIVATE; client->pers.predictItemPickup = ((client->pers.clientFlags & CGF_PREDICTITEMS) != 0); if (client->pers.clientFlags & CGF_AUTORELOAD) { client->pers.bAutoReloadAux = qtrue; client->pmext.bAutoReload = qtrue; } else { client->pers.bAutoReloadAux = qfalse; client->pmext.bAutoReload = qfalse; } // Nico, pmove_fixed client->pers.pmoveFixed = client->pers.clientFlags & CGF_PMOVEFIXED; // Nico, autologin client->pers.autoLogin = client->pers.clientFlags & CGF_AUTOLOGIN; // // Nico, name handling // // Backup old name Q_strncpyz(oldname, client->pers.netname, sizeof (oldname)); // Get new name from userinfo string name = Info_ValueForKey(userinfo, "name"); // Clean the new name ClientCleanName(name, client->pers.netname, sizeof (client->pers.netname)); // Check it's valid if (CheckName(client->pers.netname) != qtrue) { // Invalid name, restore old name Q_strncpyz(client->pers.netname, oldname, sizeof (client->pers.netname)); Info_SetValueForKey(userinfo, "name", oldname); trap_SetUserinfo(clientNum, userinfo); CPx(clientNum, "print \"^1Invalid name, name change refused\n\""); G_LogPrintf("Client %d name change refused\n", clientNum); } else { // Name is valid // Now, check if name was changed or not if (client->pers.connected == CON_CONNECTED && strcmp(oldname, client->pers.netname) != 0) { // Name was changed // Now, check name changes limit if (g_maxNameChanges.integer > -1 && client->pers.nameChanges >= g_maxNameChanges.integer) { // Nico, limit reached, forbid name change Q_strncpyz(client->pers.netname, oldname, sizeof (client->pers.netname)); Info_SetValueForKey(userinfo, "name", oldname); trap_SetUserinfo(clientNum, userinfo); CPx(clientNum, "print \"^1You had too many namechanges\n\""); G_LogPrintf("Client %d name change refused\n", clientNum); return; } client->pers.nameChanges++; trap_SendServerCommand(-1, va("print \"%s" S_COLOR_WHITE " renamed to %s\n\"", oldname, client->pers.netname)); } } // // Nico, end of name handling // client->ps.stats[STAT_MAX_HEALTH] = client->pers.maxHealth; // To communicate it to cgame client->ps.stats[STAT_PLAYER_CLASS] = client->sess.playerType; // Gordon: Not needed any more as it's in clientinfo? // send over a subset of the userinfo keys so other clients can // print scoreboards, display models, and play custom sounds s = va("n\\%s\\t\\%i\\c\\%i\\w\\%i\\lw\\%i\\sw\\%i\\mu\\%i\\ref\\%i\\pm\\%i\\l\\%i\\h\\%i\\cc\\%i", client->pers.netname, client->sess.sessionTeam, client->sess.playerType, client->sess.playerWeapon, client->sess.latchPlayerWeapon, client->sess.latchPlayerWeapon2, client->sess.muted ? 1 : 0, client->sess.referee, client->pers.pmoveFixed ? 1 : 0, // Nico, pmove_fixed client->sess.logged ? 1 : 0, // Nico, login status client->pers.hideme, // Nico, hideme client->sess.countryCode// Nico, country code (GeoIP) ); trap_GetConfigstring(CS_PLAYERS + clientNum, oldname, sizeof (oldname)); trap_SetConfigstring(CS_PLAYERS + clientNum, s); if (!Q_stricmp(oldname, s)) { return; } G_LogPrintf("ClientUserinfoChanged: %i %s\n", clientNum, s); G_DPrintf("ClientUserinfoChanged: %i :: %s\n", clientNum, s); }
/* =========== ClientUserInfoChanged Called from ClientConnect when the player first connects and directly by the server system when the player updates a userinfo variable. The game can override any of the settings and call trap_SetUserinfo if desired. ============ */ void ClientUserinfoChanged( int clientNum ) { gentity_t *ent; int health; char *s; char model[ MAX_QPATH ]; char buffer[ MAX_QPATH ]; char filename[ MAX_QPATH ]; char oldname[ MAX_NAME_LENGTH ]; char newname[ MAX_NAME_LENGTH ]; char err[ MAX_STRING_CHARS ]; qboolean revertName = qfalse; gclient_t *client; char c1[ MAX_INFO_STRING ]; char c2[ MAX_INFO_STRING ]; char userinfo[ MAX_INFO_STRING ]; ent = g_entities + clientNum; client = ent->client; trap_GetUserinfo( clientNum, userinfo, sizeof( userinfo ) ); // check for malformed or illegal info strings if( !Info_Validate(userinfo) ) strcpy( userinfo, "\\name\\badinfo" ); // set name Q_strncpyz( oldname, client->pers.netname, sizeof( oldname ) ); s = Info_ValueForKey( userinfo, "name" ); G_ClientCleanName( s, newname, sizeof( newname ) ); if( strcmp( oldname, newname ) ) { // in case we need to revert and there's no oldname if( client->pers.connected != CON_CONNECTED ) Q_strncpyz( oldname, "UnnamedPlayer", sizeof( oldname ) ); if( client->pers.nameChangeTime && ( level.time - client->pers.nameChangeTime ) <= ( g_minNameChangePeriod.value * 1000 ) ) { trap_SendServerCommand( ent - g_entities, va( "print \"Name change spam protection (g_minNameChangePeriod = %d)\n\"", g_minNameChangePeriod.integer ) ); revertName = qtrue; } else if( g_maxNameChanges.integer > 0 && client->pers.nameChanges >= g_maxNameChanges.integer ) { trap_SendServerCommand( ent - g_entities, va( "print \"Maximum name changes reached (g_maxNameChanges = %d)\n\"", g_maxNameChanges.integer ) ); revertName = qtrue; } else if( !G_admin_name_check( ent, newname, err, sizeof( err ) ) ) { trap_SendServerCommand( ent - g_entities, va( "print \"%s\n\"", err ) ); revertName = qtrue; } if( revertName ) { Q_strncpyz( client->pers.netname, oldname, sizeof( client->pers.netname ) ); Info_SetValueForKey( userinfo, "name", oldname ); trap_SetUserinfo( clientNum, userinfo ); } else { Q_strncpyz( client->pers.netname, newname, sizeof( client->pers.netname ) ); if( client->pers.connected == CON_CONNECTED ) { client->pers.nameChangeTime = level.time; client->pers.nameChanges++; } } } if( client->sess.spectatorState == SPECTATOR_SCOREBOARD ) Q_strncpyz( client->pers.netname, "scoreboard", sizeof( client->pers.netname ) ); if( client->pers.connected == CON_CONNECTED ) { if( strcmp( oldname, client->pers.netname ) ) { trap_SendServerCommand( -1, va( "print \"%s" S_COLOR_WHITE " renamed to %s\n\"", oldname, client->pers.netname ) ); G_LogPrintf( "ClientRename: %i [%s] (%s) \"%s\" -> \"%s\"\n", clientNum, client->pers.ip, client->pers.guid, oldname, client->pers.netname ); G_admin_namelog_update( client, qfalse ); } } // set max health health = atoi( Info_ValueForKey( userinfo, "handicap" ) ); client->pers.maxHealth = health; if( client->pers.maxHealth < 1 || client->pers.maxHealth > 100 ) client->pers.maxHealth = 100; if( client->pers.classSelection == PCL_NONE ) { //This looks hacky and frankly it is. The clientInfo string needs to hold different //model details to that of the spawning class or the info change will not be //registered and an axis appears instead of the player model. There is zero chance //the player can spawn with the battlesuit, hence this choice. Com_sprintf( buffer, MAX_QPATH, "%s/%s", BG_ClassConfig( PCL_HUMAN_BSUIT )->modelName, BG_ClassConfig( PCL_HUMAN_BSUIT )->skinName ); } else { Com_sprintf( buffer, MAX_QPATH, "%s/%s", BG_ClassConfig( client->pers.classSelection )->modelName, BG_ClassConfig( client->pers.classSelection )->skinName ); //model segmentation Com_sprintf( filename, sizeof( filename ), "models/players/%s/animation.cfg", BG_ClassConfig( client->pers.classSelection )->modelName ); if( G_NonSegModel( filename ) ) client->ps.persistant[ PERS_STATE ] |= PS_NONSEGMODEL; else client->ps.persistant[ PERS_STATE ] &= ~PS_NONSEGMODEL; } Q_strncpyz( model, buffer, sizeof( model ) ); // wallwalk follow s = Info_ValueForKey( userinfo, "cg_wwFollow" ); if( atoi( s ) ) client->ps.persistant[ PERS_STATE ] |= PS_WALLCLIMBINGFOLLOW; else client->ps.persistant[ PERS_STATE ] &= ~PS_WALLCLIMBINGFOLLOW; // wallwalk toggle s = Info_ValueForKey( userinfo, "cg_wwToggle" ); if( atoi( s ) ) client->ps.persistant[ PERS_STATE ] |= PS_WALLCLIMBINGTOGGLE; else client->ps.persistant[ PERS_STATE ] &= ~PS_WALLCLIMBINGTOGGLE; // teamInfo s = Info_ValueForKey( userinfo, "teamoverlay" ); if( !*s || atoi( s ) != 0 ) client->pers.teamInfo = qtrue; else client->pers.teamInfo = qfalse; // colors strcpy( c1, Info_ValueForKey( userinfo, "color1" ) ); strcpy( c2, Info_ValueForKey( userinfo, "color2" ) ); Q_strncpyz( client->pers.voice, Info_ValueForKey( userinfo, "voice" ), sizeof( client->pers.voice ) ); // send over a subset of the userinfo keys so other clients can // print scoreboards, display models, and play custom sounds Com_sprintf( userinfo, sizeof( userinfo ), "n\\%s\\t\\%i\\model\\%s\\c1\\%s\\c2\\%s\\" "hc\\%i\\ig\\%16s\\v\\%s", client->pers.netname, client->pers.teamSelection, model, c1, c2, client->pers.maxHealth, BG_ClientListString( &client->sess.ignoreList ), client->pers.voice ); trap_SetConfigstring( CS_PLAYERS + clientNum, userinfo ); /*G_LogPrintf( "ClientUserinfoChanged: %i %s\n", clientNum, userinfo );*/ }
//[TABBot] //added bot type varible static void G_AddBot( const char *name, float skill, const char *team, int delay, char *altname, int bottype) { //static void G_AddBot( const char *name, float skill, const char *team, int delay, char *altname) { //[/TABBot] int clientNum; char *botinfo; gentity_t *bot; char *key; char *s; char *botname; char *model; // char *headmodel; char userinfo[MAX_INFO_STRING]; int preTeam = 0; //[DuelGuns][EnhancedImpliment] /* char *firearm; // ** change gun model qboolean bot_dualguns = qfalse; int gunoption=0; */ //[/DuelGuns][EnhancedImpliment] // get the botinfo from bots.txt botinfo = G_GetBotInfoByName( name ); if ( !botinfo ) { G_Printf( S_COLOR_RED "Error: Bot '%s' not defined\n", name ); return; } // create the bot's userinfo userinfo[0] = '\0'; botname = Info_ValueForKey( botinfo, "funname" ); if( !botname[0] ) { botname = Info_ValueForKey( botinfo, "name" ); } // check for an alternative name if (altname && altname[0]) { botname = altname; } Info_SetValueForKey( userinfo, "name", botname ); Info_SetValueForKey( userinfo, "rate", "25000" ); Info_SetValueForKey( userinfo, "snaps", "20" ); Info_SetValueForKey( userinfo, "skill", va("%1.2f", skill) ); if ( skill >= 1 && skill < 2 ) { Info_SetValueForKey( userinfo, "handicap", "50" ); } else if ( skill >= 2 && skill < 3 ) { Info_SetValueForKey( userinfo, "handicap", "70" ); } else if ( skill >= 3 && skill < 4 ) { Info_SetValueForKey( userinfo, "handicap", "90" ); } key = "model"; model = Info_ValueForKey( botinfo, key ); if ( !*model ) { model = "kyle/default"; } Info_SetValueForKey( userinfo, key, model ); /* key = "headmodel"; headmodel = Info_ValueForKey( botinfo, key ); if ( !*headmodel ) { headmodel = model; } Info_SetValueForKey( userinfo, key, headmodel ); key = "team_headmodel"; Info_SetValueForKey( userinfo, key, headmodel ); */ key = "gender"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) { s = "male"; } Info_SetValueForKey( userinfo, "sex", s ); key = "color1"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) { s = "4"; } Info_SetValueForKey( userinfo, key, s ); key = "color2"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) { s = "4"; } Info_SetValueForKey( userinfo, key, s ); key = "saber1"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) { s = "single_1"; } Info_SetValueForKey( userinfo, key, s ); key = "saber2"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) { s = "none"; } Info_SetValueForKey( userinfo, key, s ); s = Info_ValueForKey(botinfo, "personality"); if (!*s ) { Info_SetValueForKey( userinfo, "personality", "botfiles/default.jkb" ); } else { Info_SetValueForKey( userinfo, "personality", s ); } //[DuelGuns][EnhancedImpliment] /* // if(1)//f_dualguns.integer) // { key = "dualgun"; s = Info_ValueForKey(botinfo, key); if (*s) { gunoption = atoi(s); } if(gunoption>0) bot_dualguns = qtrue; // } firearm = Info_ValueForKey( botinfo, "firearm"); if (!*firearm) { Info_SetValueForKey( userinfo, "firearm", botname ); } else { Info_SetValueForKey( userinfo, "firearm", firearm); } */ //[/DuelGuns][EnhancedImpliment] //[RGBSabers] key = "rgb_saber1"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) { s = "255,0,0"; } Info_SetValueForKey( userinfo, key, s ); key = "rgb_saber2"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) { s = "0,255,255"; } Info_SetValueForKey( userinfo, key, s ); key = "rgb_script1"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) { s = "none"; } Info_SetValueForKey( userinfo, key, s ); key = "rgb_script2"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) { s = "none"; } Info_SetValueForKey( userinfo, key, s ); //[/RGBSabers] //[ClientPlugInDetect] //set it so that the bots are assumed to have the OJP client plugin //this should be CURRENT_OJPENHANCED_CLIENTVERSION Info_SetValueForKey( userinfo, "ojp_clientplugin", CURRENT_OJPENHANCED_CLIENTVERSION ); //[/ClientPlugInDetect] // have the server allocate a client slot clientNum = trap_BotAllocateClient(); if ( clientNum == -1 ) { // G_Printf( S_COLOR_RED "Unable to add bot. All player slots are in use.\n" ); // G_Printf( S_COLOR_RED "Start server with more 'open' slots.\n" ); trap_SendServerCommand( -1, va("print \"%s\n\"", G_GetStringEdString("MP_SVGAME", "UNABLE_TO_ADD_BOT"))); return; } //[DuelGuns][EnhancedImpliment] /* if(bot_dualguns) { g_entities[clientNum].client->ps.dualguns = 1; } */ //[/DuelGuns][EnhancedImpliment] // initialize the bot settings if( !team || !*team ) { if( g_gametype.integer >= GT_TEAM ) { //[AdminSys] if( PickTeam(clientNum, qtrue) == TEAM_RED) { //if( PickTeam(clientNum) == TEAM_RED) { //[/AdminSys] team = "red"; } else { team = "blue"; } } else { team = "red"; } } // Info_SetValueForKey( userinfo, "characterfile", Info_ValueForKey( botinfo, "aifile" ) ); Info_SetValueForKey( userinfo, "skill", va( "%5.2f", skill ) ); Info_SetValueForKey( userinfo, "team", team ); //[TABBot] Info_SetValueForKey( userinfo, "bottype", va( "%i", bottype) ); //[/TABBot] bot = &g_entities[ clientNum ]; bot->r.svFlags |= SVF_BOT; bot->inuse = qtrue; // register the userinfo trap_SetUserinfo( clientNum, userinfo ); //[NewGameTypes][EnhancedImpliment] //if (g_gametype.integer >= GT_TEAM && g_gametype.integer != GT_RPG) if (g_gametype.integer >= GT_TEAM) //[/NewGameTypes][EnhancedImpliment] { if (team && Q_stricmp(team, "red") == 0) { bot->client->sess.sessionTeam = TEAM_RED; } else if (team && Q_stricmp(team, "blue") == 0) { bot->client->sess.sessionTeam = TEAM_BLUE; } else { //[AdminSys] bot->client->sess.sessionTeam = PickTeam( -1, qtrue ); //bot->client->sess.sessionTeam = PickTeam( -1 ); //[/AdminSys] } } if (g_gametype.integer == GT_SIEGE) { bot->client->sess.siegeDesiredTeam = bot->client->sess.sessionTeam; bot->client->sess.sessionTeam = TEAM_SPECTATOR; } preTeam = bot->client->sess.sessionTeam; // have it connect to the game as a normal client if ( ClientConnect( clientNum, qtrue, qtrue ) ) { return; } if (bot->client->sess.sessionTeam != preTeam) { trap_GetUserinfo(clientNum, userinfo, MAX_INFO_STRING); if (bot->client->sess.sessionTeam == TEAM_SPECTATOR) { bot->client->sess.sessionTeam = preTeam; } if (bot->client->sess.sessionTeam == TEAM_RED) { team = "Red"; } else { if (g_gametype.integer == GT_SIEGE) { if (bot->client->sess.sessionTeam == TEAM_BLUE) { team = "Blue"; } else { team = "s"; } } else { team = "Blue"; } } Info_SetValueForKey( userinfo, "team", team ); trap_SetUserinfo( clientNum, userinfo ); bot->client->ps.persistant[ PERS_TEAM ] = bot->client->sess.sessionTeam; G_ReadSessionData( bot->client ); ClientUserinfoChanged( clientNum ); } if (g_gametype.integer == GT_DUEL || g_gametype.integer == GT_POWERDUEL) { int loners = 0; int doubles = 0; bot->client->sess.duelTeam = 0; G_PowerDuelCount(&loners, &doubles, qtrue); if (!doubles || loners > (doubles/2)) { bot->client->sess.duelTeam = DUELTEAM_DOUBLE; } else { bot->client->sess.duelTeam = DUELTEAM_LONE; } bot->client->sess.sessionTeam = TEAM_SPECTATOR; SetTeam(bot, "s"); } else { if( delay == 0 ) { ClientBegin( clientNum, qfalse ); //UNIQUEFIX - what's the purpose of this? //ClientUserinfoChanged( clientNum ); return; } AddBotToSpawnQueue( clientNum, delay ); //UNIQUEFIX - what's the purpose of this? //ClientUserinfoChanged( clientNum ); } }
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; }
void JMP_AdminExecuteCommand( gentity_t *pEntity, sAdminData_t *am, int trueTarget ) { char zUserInfo[MAX_TOKEN_CHARS]; char par1[MAX_TOKEN_CHARS]; // Used char par2[MAX_TOKEN_CHARS]; char par3[MAX_TOKEN_CHARS]; char par4[MAX_TOKEN_CHARS]; char par5[MAX_TOKEN_CHARS]; int clientNum = pEntity->s.number; int targetNum = trueTarget; int i = 0; trap_Argv( 1, par1, sizeof( par1 )); trap_Argv( 2, par2, sizeof( par2 )); trap_Argv( 3, par3, sizeof( par3 )); trap_Argv( 4, par4, sizeof( par4 )); trap_Argv( 5, par5, sizeof( par5 )); if ( targetNum == -1 ) { if ( JMP_AdminNoAccess( clientNum, am->iCmdFlag )) { return; } targetNum = JMP_GetTarget( pEntity, par1 ); if ( targetNum == -1 && !am->bAllowAll ) { JMP_AdminMessage( clientNum, "You are not allowed to use this command on multiple persons" ); return; } else if ( targetNum == -2 ) { JMP_AdminMessage( clientNum, "No target has been found in your crosshair" ); return; } else if ( targetNum == -3 ) { JMP_AdminMessage( clientNum, "No target matching your specifications has been found" ); return; } else if ( targetNum == -4 ) { JMP_AdminMessage( clientNum, "Multiple matches have been found, only one is allowed" ); return; } else if ( targetNum == clientNum && !am->bAllowSelf ) { #ifndef _DEBUG JMP_AdminMessage( clientNum, "This command may not be executed upon yourself" ); return; #endif } else if ( targetNum != -1 && targetNum != clientNum && sPlayerData[targetNum]->iAdminLogged && sPlayerData[targetNum]->iAdminLogged <= sPlayerData[clientNum]->iAdminLogged && JMP_AdminEvilCommand( am->iCmdFlag )) { JMP_AdminMessage( clientNum, "This command may not be executed upon an equal/higher ranked admin" ); return; } } if ( targetNum == -1 ) { for ( i = 0; i < MAX_CLIENTS; i++ ) { if ( !sPlayerData[i]->iInUse || ( i == clientNum && !am->bAllowSelf ) || ( i != clientNum && sPlayerData[i]->iAdminLogged && sPlayerData[i]->iAdminLogged <= sPlayerData[clientNum]->iAdminLogged && JMP_AdminEvilCommand( am->iCmdFlag ))) { continue; } JMP_AdminExecuteCommand( pEntity, am, i ); } return; } if ( !sPlayerData[targetNum]->iInUse ) { return; } switch( am->iCmdFlag ) { case DS_KICK: { if ( !strlen( par2 )) { JMP_DropPlayer( g_entities[targetNum], "has been kicked." ); } else { JMP_DropPlayer( g_entities[targetNum], va( "has been kicked. (^3 Reason^7: ^1%s ^7)", ConcatArgs( 2 ))); } break; } case DS_KICKBAN: case DS_BANRANGE: { // Initialize the ban for each type of command. if ( am->iCmdFlag == DS_KICKBAN ) { trap_SendConsoleCommand( EXEC_APPEND, va("addip %i.%i.%i.%i", sPlayerData[targetNum]->iPlayerIP[0], sPlayerData[targetNum]->iPlayerIP[1], sPlayerData[targetNum]->iPlayerIP[2], sPlayerData[targetNum]->iPlayerIP[3] )); } else { trap_SendConsoleCommand( EXEC_APPEND, va("addip %i.%i.0.0", sPlayerData[targetNum]->iPlayerIP[0], sPlayerData[targetNum]->iPlayerIP[1] )); } // Give the message for the ban, good bye player! if ( !strlen( par2 )) { JMP_DropPlayer( g_entities[targetNum], "has been banned." ); } else { JMP_DropPlayer( g_entities[targetNum], va( "has been banned. (^3 Reason^7: ^1%s ^7)", ConcatArgs( 2 ))); } break; } case DS_SLAP: { if ( g_clients[targetNum]->stats[STAT_HEALTH] <= 0 ) { JMP_AdminMessage( clientNum, "This player is not alive!" ); break; } if ( !( sPlayerData[targetNum]->iClientEffect & ( 1 << F_SLEEP )) && !( sPlayerData[targetNum]->iClientEffect & ( 1 << F_PUNISH ))) { int iAngle; vec3_t vDir; if ( g_clients[targetNum]->saberHolstered != 2 ) { g_clients[targetNum]->saberHolstered = 2; } VectorNormalize2( g_clients[clientNum]->velocity, vDir ); VectorScale( vDir, -1, vDir ); iAngle = g_clients[clientNum]->viewangles[YAW] * ( M_PI * 2 / 360 ); vDir[1] = ( sin( iAngle ) * 500 ); vDir[0] = ( cos( iAngle ) * 500 ); g_clients[targetNum]->velocity[0] = vDir[0]; g_clients[targetNum]->velocity[1] = vDir[1]; g_clients[targetNum]->velocity[2] = 500; g_clients[targetNum]->forceHandExtend = HANDEXTEND_KNOCKDOWN + bMovieBattles; g_clients[targetNum]->forceHandExtendTime = sWorldData->iTime + 3000; g_clients[targetNum]->forceDodgeAnim = 0; } break; } case DS_SLEEP: { if ( !( sPlayerData[targetNum]->iClientEffect & ( 1 << F_SLEEP )) && !( sPlayerData[targetNum]->iClientEffect & ( 1 << F_PUNISH ))) { if ( g_clients[targetNum]->saberHolstered != 2 ) { g_clients[targetNum]->saberHolstered = 2; } sPlayerData[targetNum]->iClientEffect |= ( 1 << F_SLEEP ); g_clients[targetNum]->forceHandExtend = HANDEXTEND_KNOCKDOWN + bMovieBattles; g_clients[targetNum]->forceHandExtendTime = sWorldData->iTime + INFINITE; g_clients[targetNum]->forceDodgeAnim = 0; } break; } case DS_WAKE: { if ( sPlayerData[targetNum]->iClientEffect & ( 1 << F_SLEEP )) { sPlayerData[targetNum]->iClientEffect &= ~( 1 << F_SLEEP ); g_clients[targetNum]->forceHandExtendTime = sWorldData->iTime + 500; } break; } case DS_PUNISH: { if ( !( sPlayerData[targetNum]->iClientEffect & ( 1 << F_SLEEP )) && !( sPlayerData[targetNum]->iClientEffect & ( 1 << F_PUNISH ))) { if ( g_clients[targetNum]->saberHolstered != 2 ) { g_clients[targetNum]->saberHolstered = 2; } sPlayerData[targetNum]->iClientEffect |= ( 1 << F_PUNISH ); g_clients[targetNum]->forceHandExtend = HANDEXTEND_CHOKE + bMovieBattles; g_clients[targetNum]->forceHandExtendTime = sWorldData->iTime + INFINITE; g_clients[targetNum]->forceGripChangeMovetype = PM_FLOAT; } break; } case DS_UNPUNISH: { if ( sPlayerData[targetNum]->iClientEffect & ( 1 << F_PUNISH )) { sPlayerData[targetNum]->iClientEffect &= ~( 1 << F_PUNISH ); g_clients[targetNum]->forceHandExtend = HANDEXTEND_NONE + bMovieBattles; g_clients[targetNum]->forceHandExtendTime = sWorldData->iTime; g_clients[targetNum]->forceGripChangeMovetype = PM_NORMAL; } break; } case DS_SILENCE: { if ( !( sPlayerData[targetNum]->iClientEffect & ( 1 << F_SILENCE ))) { sPlayerData[targetNum]->iClientEffect |= ( 1 << F_SILENCE ); } break; } case DS_UNSILENCE: { if ( sPlayerData[targetNum]->iClientEffect & ( 1 << F_SILENCE )) { sPlayerData[targetNum]->iClientEffect &= ~( 1 << F_SILENCE ); } break; } case DS_PSAY: { if ( trap_Argc() < 2 ) { JMP_AdminMessage( clientNum, "Enter a message to broadcast" ); return; } JMP_StringEscape( ConcatArgs( 2 ), zUserInfo ); JMP_SendCommand( targetNum, "cp", zUserInfo ); break; } case DS_RENAME: { if ( trap_Argc() < 3 ) { JMP_AdminMessage( clientNum, "Please enter a new name to set for the player" ); return; } JMP_SendCommand( -1, "print", va( "%s ^7has been renamed to %s ^7by %s^7.\n", sPlayerData[targetNum]->zPlayerName, ConcatArgs( 2 ), sPlayerData[clientNum]->zPlayerName )); trap_GetUserinfo( targetNum, zUserInfo, MAX_INFO_STRING ); Info_SetValueForKey( zUserInfo, "name", ConcatArgs( 2 )); trap_SetUserinfo( targetNum, zUserInfo ); ( *pVmMain )( GAME_CLIENT_USERINFO_CHANGED, targetNum, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ); break; } case DS_EXPLODE: { if ( g_clients[targetNum]->stats[STAT_HEALTH] <= 0 ) { JMP_AdminMessage( clientNum, "This player is not alive!" ); break; } if ( g_clients[targetNum]->clientNum == targetNum ) { void ( *pCmd_Kill_f )( gentity_t * ) = ( void ( * )( gentity_t * )) dlsym( hHandle, "Cmd_Kill_f" ); if ( pCmd_Kill_f == NULL ) { JMP_AdminMessage( clientNum, "This function is not supported on this modification or operating system!" ); return; } pCmd_Kill_f( g_entities[targetNum] ); } break; } case DS_FORCETEAM: { if ( par2[0] == 'R' || par2[0] == 'r' || Q_stricmpn( par2, "red", 3 ) || par2[0] == 'B' || par2[0] == 'b' || Q_stricmpn( par2, "blue", 4 ) || par2[0] == 'S' || par2[0] == 's' || Q_stricmpn( par2, "spec", 4 )) { trap_SendConsoleCommand( EXEC_APPEND, va( "forceteam %i %s", targetNum, par2 )); } else { JMP_AdminMessage( clientNum, "The specified team is not recognized!" ); } break; } case DS_CONTROLHUMAN: { if ( sPlayerData[clientNum]->bControlMaster ) { if ( sPlayerData[clientNum]->iControlMaster == targetNum ) { // Release him from your control. sPlayerData[sPlayerData[clientNum]->iControlMaster]->bControlSlave = qfalse; sPlayerData[sPlayerData[clientNum]->iControlMaster]->iControlMaster = 0; // Release self from underpression. sPlayerData[clientNum]->bControlMaster = qfalse; sPlayerData[clientNum]->iControlMaster = 0; } else { // Release previous target from your control. sPlayerData[sPlayerData[clientNum]->iControlMaster]->bControlSlave = qfalse; sPlayerData[sPlayerData[clientNum]->iControlMaster]->iControlMaster = 0; // Put the new target under your juristiction! sPlayerData[targetNum]->bControlSlave = qtrue; sPlayerData[targetNum]->iControlMaster = clientNum; // Set self to static mode. sPlayerData[clientNum]->bControlMaster = qtrue; sPlayerData[clientNum]->iControlMaster = targetNum; } } else { // Put the new target under your juristiction! sPlayerData[targetNum]->bControlSlave = qtrue; sPlayerData[targetNum]->iControlMaster = clientNum; // Set self to static mode. sPlayerData[clientNum]->bControlMaster = qtrue; sPlayerData[clientNum]->iControlMaster = targetNum; } break; } case DS_IGNORE: { if ( sPlayerData[clientNum]->iIgnoreList & ( 1 << targetNum )) { sPlayerData[clientNum]->iIgnoreList &= ~( 1 << targetNum ); JMP_AdminMessage( clientNum, va( "%s ^7is now unignored", sPlayerData[targetNum]->zPlayerName )); } else { sPlayerData[clientNum]->iIgnoreList |= ( 1 << targetNum ); JMP_AdminMessage( clientNum, va( "%s ^7is now ignored", sPlayerData[targetNum]->zPlayerName )); } break; } } }
static void G_AddBot( const char *name, int skill, const char *team, const char *spawnPoint, int playerClass, int playerWeapon, int characerIndex, const char *respawn, const char *scriptName, int rank, int skills[], qboolean pow ) { #define MAX_BOTNAMES 1024 int clientNum; char *botinfo; gentity_t *bot; char *key; char *s; char *botname; // char *model; char userinfo[MAX_INFO_STRING]; // get the botinfo from bots.txt botinfo = G_GetBotInfoByName( "wolfbot" ); if ( !botinfo ) { G_Printf( S_COLOR_RED "Error: Bot '%s' not defined\n", name ); return; } // create the bot's userinfo userinfo[0] = '\0'; botname = Info_ValueForKey( botinfo, "funname" ); if( !botname[0] ) { botname = Info_ValueForKey( botinfo, "name" ); } Info_SetValueForKey( userinfo, "name", botname ); Info_SetValueForKey( userinfo, "rate", "25000" ); Info_SetValueForKey( userinfo, "snaps", "20" ); Info_SetValueForKey( userinfo, "skill", va("%i", skill) ); s = Info_ValueForKey(botinfo, "aifile"); if (!*s ) { trap_Printf( S_COLOR_RED "Error: bot has no aifile specified\n" ); return; } // have the server allocate a client slot clientNum = trap_BotAllocateClient( 0 ); // Arnout: 0 means no prefered clientslot if ( clientNum == -1 ) { G_Printf( S_COLOR_RED "Unable to add bot. All player slots are in use.\n" ); G_Printf( S_COLOR_RED "Start server with more 'open' slots (or check setting of sv_maxclients cvar).\n" ); return; } // initialize the bot settings if( !team || !*team ) { if( PickTeam(clientNum) == TEAM_AXIS) { team = "red"; } else { team = "blue"; } } Info_SetValueForKey( userinfo, "characterfile", Info_ValueForKey( botinfo, "aifile" ) ); //Info_SetValueForKey( userinfo, "skill", va( "%i", skill ) ); Info_SetValueForKey( userinfo, "team", team ); if( spawnPoint && spawnPoint[0] ) { Info_SetValueForKey( userinfo, "spawnPoint", spawnPoint ); } if (scriptName && scriptName[0]) { Info_SetValueForKey( userinfo, "scriptName", scriptName ); } /* if (playerClass > 0) { Info_SetValueForKey( userinfo, "pClass", va("%i", playerClass) ); } if (playerWeapon) { Info_SetValueForKey( userinfo, "pWeapon", va("%i", playerWeapon) ); }*/ // END Mad Doc - TDF key = "wolfbot"; if (!Q_stricmp( (char *)name, key )) { // read the botnames file, and pick a name that doesnt exist fileHandle_t f; int len, i, j, k; qboolean setname = qfalse; char botnames[8192], *pbotnames, *listbotnames[MAX_BOTNAMES], *token, *oldpbotnames; int lengthbotnames[MAX_BOTNAMES]; len = trap_FS_FOpenFile( "botfiles/botnames.txt", &f, FS_READ ); if (len >= 0) { if (len > sizeof(botnames)) { G_Error( "botfiles/botnames.txt is too big (max = %i)", (int)sizeof(botnames) ); } memset( botnames, 0, sizeof(botnames) ); trap_FS_Read( botnames, len, f ); pbotnames = botnames; // read them in i = 0; oldpbotnames = pbotnames; while ((token = COM_Parse( &pbotnames ))) { if (!token[0]) break; listbotnames[i] = strstr( oldpbotnames, token ); lengthbotnames[i] = strlen(token); listbotnames[i][lengthbotnames[i]] = 0; oldpbotnames = pbotnames; if (++i == MAX_BOTNAMES) break; } // if (i > 2) { j = rand() % (i-1); // start at a random spot inthe list for( k = j + 1; k != j; k++ ) { if( k == i ) { k = -1; // gets increased on next loop continue; } if (ClientFromName( listbotnames[k] ) == -1) { // found an unused name Info_SetValueForKey( userinfo, "name", listbotnames[k] ); setname = qtrue; break; } } } // trap_FS_FCloseFile( f ); } if (!setname) { Info_SetValueForKey( userinfo, "name", va("wolfbot_%i", clientNum+1) ); } } else { Info_SetValueForKey( userinfo, "name", name ); } // if a character was specified, put the index of that character filename in the CS_CHARACTERS table in the userinfo if( characerIndex != -1 ) { Info_SetValueForKey( userinfo, "ch", va( "%i", characerIndex ) ); } // if a rank was specified, use that /* if (rank != -1) { Info_SetValueForKey(userinfo, "rank", va("%i", rank)); }*/ // END Mad Doc - TDF bot = &g_entities[ clientNum ]; bot->r.svFlags |= SVF_BOT; if( pow ) { bot->r.svFlags |= SVF_POW; } bot->inuse = qtrue; bot->aiName = bot->client->pers.netname; // register the userinfo trap_SetUserinfo( clientNum, userinfo ); // have it connect to the game as a normal client if ((s = ClientConnect( clientNum, qtrue, qtrue ))) { G_Printf( S_COLOR_RED "Unable to add bot: %s\n", s ); return; } SetTeam( bot, (char *)team, qtrue, -1, -1, qfalse ); /* if( skills ) { int i; for( i = 0; i < SK_NUM_SKILLS; i++ ) { bot->client->sess.skill[i] = skills[i]; } }*/ return; }
/* =========== ClientUserInfoChanged Called from ClientConnect when the player first connects and directly by the server system when the player updates a userinfo variable. The game can override any of the settings and call trap_SetUserinfo if desired. ============ */ char *ClientUserinfoChanged( int clientNum, qboolean forceName ) { gentity_t *ent; char *s; char model[ MAX_QPATH ]; char buffer[ MAX_QPATH ]; char filename[ MAX_QPATH ]; char oldname[ MAX_NAME_LENGTH ]; char newname[ MAX_NAME_LENGTH ]; char err[ MAX_STRING_CHARS ]; qboolean revertName = qfalse; gclient_t *client; char userinfo[ MAX_INFO_STRING ]; ent = g_entities + clientNum; client = ent->client; trap_GetUserinfo( clientNum, userinfo, sizeof( userinfo ) ); // check for malformed or illegal info strings if( !Info_Validate(userinfo) ) { trap_SendServerCommand( ent - g_entities, "disconnect \"illegal or malformed userinfo\n\"" ); trap_DropClient( ent - g_entities, "dropped: illegal or malformed userinfo"); return "Illegal or malformed userinfo"; } // If their userinfo overflowed, tremded is in the process of disconnecting them. // If we send our own disconnect, it won't work, so just return to prevent crashes later // in this function. This check must come after the Info_Validate call. else if( !userinfo[ 0 ] ) return "Empty (overflowed) userinfo"; // stickyspec toggle s = Info_ValueForKey( userinfo, "cg_stickySpec" ); client->pers.stickySpec = atoi( s ) != 0; // set name Q_strncpyz( oldname, client->pers.netname, sizeof( oldname ) ); s = Info_ValueForKey( userinfo, "name" ); G_ClientCleanName( s, newname, sizeof( newname ) ); if( strcmp( oldname, newname ) ) { if( !forceName && client->pers.namelog->nameChangeTime && level.time - client->pers.namelog->nameChangeTime <= g_minNameChangePeriod.value * 1000 ) { trap_SendServerCommand( ent - g_entities, va( "print \"Name change spam protection (g_minNameChangePeriod = %d)\n\"", g_minNameChangePeriod.integer ) ); revertName = qtrue; } else if( !forceName && g_maxNameChanges.integer > 0 && client->pers.namelog->nameChanges >= g_maxNameChanges.integer ) { trap_SendServerCommand( ent - g_entities, va( "print \"Maximum name changes reached (g_maxNameChanges = %d)\n\"", g_maxNameChanges.integer ) ); revertName = qtrue; } else if( !forceName && client->pers.namelog->muted ) { trap_SendServerCommand( ent - g_entities, "print \"You cannot change your name while you are muted\n\"" ); revertName = qtrue; } else if( !G_admin_name_check( ent, newname, err, sizeof( err ) ) ) { trap_SendServerCommand( ent - g_entities, va( "print \"%s\n\"", err ) ); revertName = qtrue; } if( revertName ) { Q_strncpyz( client->pers.netname, *oldname ? oldname : "UnnamedPlayer", sizeof( client->pers.netname ) ); Info_SetValueForKey( userinfo, "name", oldname ); trap_SetUserinfo( clientNum, userinfo ); } else { G_CensorString( client->pers.netname, newname, sizeof( client->pers.netname ), ent ); if( !forceName && client->pers.connected == CON_CONNECTED ) { client->pers.namelog->nameChangeTime = level.time; client->pers.namelog->nameChanges++; } if( *oldname ) { G_LogPrintf( "ClientRename: %i [%s] (%s) \"%s^7\" -> \"%s^7\" \"%c%s%c^7\"\n", clientNum, client->pers.ip.str, client->pers.guid, oldname, client->pers.netname, DECOLOR_OFF, client->pers.netname, DECOLOR_ON ); } } G_namelog_update_name( client ); } if( client->pers.classSelection == PCL_NONE ) { //This looks hacky and frankly it is. The clientInfo string needs to hold different //model details to that of the spawning class or the info change will not be //registered and an axis appears instead of the player model. There is zero chance //the player can spawn with the battlesuit, hence this choice. Com_sprintf( buffer, MAX_QPATH, "%s/%s", BG_ClassConfig( PCL_HUMAN_BSUIT )->modelName, BG_ClassConfig( PCL_HUMAN_BSUIT )->skinName ); } else { Com_sprintf( buffer, MAX_QPATH, "%s/%s", BG_ClassConfig( client->pers.classSelection )->modelName, BG_ClassConfig( client->pers.classSelection )->skinName ); //model segmentation Com_sprintf( filename, sizeof( filename ), "models/players/%s/animation.cfg", BG_ClassConfig( client->pers.classSelection )->modelName ); if( G_NonSegModel( filename ) ) client->ps.persistant[ PERS_STATE ] |= PS_NONSEGMODEL; else client->ps.persistant[ PERS_STATE ] &= ~PS_NONSEGMODEL; } Q_strncpyz( model, buffer, sizeof( model ) ); // wallwalk follow s = Info_ValueForKey( userinfo, "cg_wwFollow" ); if( atoi( s ) ) client->ps.persistant[ PERS_STATE ] |= PS_WALLCLIMBINGFOLLOW; else client->ps.persistant[ PERS_STATE ] &= ~PS_WALLCLIMBINGFOLLOW; // wallwalk toggle s = Info_ValueForKey( userinfo, "cg_wwToggle" ); if( atoi( s ) ) client->ps.persistant[ PERS_STATE ] |= PS_WALLCLIMBINGTOGGLE; else client->ps.persistant[ PERS_STATE ] &= ~PS_WALLCLIMBINGTOGGLE; // always sprint s = Info_ValueForKey( userinfo, "cg_sprintToggle" ); if( atoi( s ) ) client->ps.persistant[ PERS_STATE ] |= PS_SPRINTTOGGLE; else client->ps.persistant[ PERS_STATE ] &= ~PS_SPRINTTOGGLE; // fly speed s = Info_ValueForKey( userinfo, "cg_flySpeed" ); if( *s ) client->pers.flySpeed = atoi( s ); else client->pers.flySpeed = BG_Class( PCL_NONE )->speed; // disable blueprint errors s = Info_ValueForKey( userinfo, "cg_disableBlueprintErrors" ); if( atoi( s ) ) client->pers.disableBlueprintErrors = qtrue; else client->pers.disableBlueprintErrors = qfalse; // teamInfo s = Info_ValueForKey( userinfo, "teamoverlay" ); if( atoi( s ) != 0 ) { // teamoverlay was enabled so we need an update if( client->pers.teamInfo == 0 ) client->pers.teamInfo = 1; } else client->pers.teamInfo = 0; s = Info_ValueForKey( userinfo, "cg_unlagged" ); if( !s[0] || atoi( s ) != 0 ) client->pers.useUnlagged = qtrue; else client->pers.useUnlagged = qfalse; Q_strncpyz( client->pers.voice, Info_ValueForKey( userinfo, "voice" ), sizeof( client->pers.voice ) ); // send over a subset of the userinfo keys so other clients can // print scoreboards, display models, and play custom sounds Com_sprintf( userinfo, sizeof( userinfo ), "n\\%s\\t\\%i\\model\\%s\\ig\\%16s\\v\\%s", client->pers.netname, client->pers.teamSelection, model, Com_ClientListString( &client->sess.ignoreList ), client->pers.voice ); trap_SetConfigstring( CS_PLAYERS + clientNum, userinfo ); /*G_LogPrintf( "ClientUserinfoChanged: %i %s\n", clientNum, userinfo );*/ return NULL; }
/* =========== ClientBegin called when a client has finished connecting, and is ready to be placed into the level. This will happen every level load, and on transition between teams, but doesn't happen on respawns ============ */ void ClientBegin( int clientNum, qboolean allowTeamReset ) { gentity_t *ent; gclient_t *client; gentity_t *tent; int flags, i; char userinfo[MAX_INFO_VALUE], *modelname; ent = g_entities + clientNum; if ((ent->r.svFlags & SVF_BOT) && g_gametype.integer >= GT_TEAM) { if (allowTeamReset) { const char *team = "Red"; int preSess; //SetTeam(ent, ""); ent->client->sess.sessionTeam = PickTeam(-1); trap_GetUserinfo(clientNum, userinfo, MAX_INFO_STRING); if (ent->client->sess.sessionTeam == TEAM_SPECTATOR) { ent->client->sess.sessionTeam = TEAM_RED; } if (ent->client->sess.sessionTeam == TEAM_RED) { team = "Red"; } else { team = "Blue"; } Info_SetValueForKey( userinfo, "team", team ); trap_SetUserinfo( clientNum, userinfo ); ent->client->ps.persistant[ PERS_TEAM ] = ent->client->sess.sessionTeam; preSess = ent->client->sess.sessionTeam; G_ReadSessionData( ent->client ); ent->client->sess.sessionTeam = preSess; G_WriteClientSessionData(ent->client); ClientUserinfoChanged( clientNum ); ClientBegin(clientNum, qfalse); return; } } client = level.clients + clientNum; if ( ent->r.linked ) { trap_UnlinkEntity( ent ); } G_InitGentity( ent ); ent->touch = 0; ent->pain = 0; ent->client = client; client->pers.connected = CON_CONNECTED; client->pers.enterTime = level.time; client->pers.teamState.state = TEAM_BEGIN; // save eflags around this, because changing teams will // cause this to happen with a valid entity, and we // want to make sure the teleport bit is set right // so the viewpoint doesn't interpolate through the // world to the new position flags = client->ps.eFlags; i = 0; while (i < NUM_FORCE_POWERS) { if (ent->client->ps.fd.forcePowersActive & (1 << i)) { WP_ForcePowerStop(ent, i); } i++; } i = TRACK_CHANNEL_1; while (i < NUM_TRACK_CHANNELS) { if (ent->client->ps.fd.killSoundEntIndex[i-50] && ent->client->ps.fd.killSoundEntIndex[i-50] < MAX_GENTITIES && ent->client->ps.fd.killSoundEntIndex[i-50] > 0) { G_MuteSound(ent->client->ps.fd.killSoundEntIndex[i-50], CHAN_VOICE); } i++; } i = 0; memset( &client->ps, 0, sizeof( client->ps ) ); client->ps.eFlags = flags; client->ps.hasDetPackPlanted = qfalse; //first-time force power initialization WP_InitForcePowers( ent ); //init saber ent WP_SaberInitBladeData( ent ); // First time model setup for that player. trap_GetUserinfo( clientNum, userinfo, sizeof(userinfo) ); modelname = Info_ValueForKey (userinfo, "model"); SetupGameGhoul2Model(client, modelname); if (ent->client->ghoul2) { ent->bolt_Head = trap_G2API_AddBolt(ent->client->ghoul2, 0, "cranium"); ent->bolt_Waist = trap_G2API_AddBolt(ent->client->ghoul2, 0, "thoracic"); ent->bolt_LArm = trap_G2API_AddBolt(ent->client->ghoul2, 0, "lradius"); ent->bolt_RArm = trap_G2API_AddBolt(ent->client->ghoul2, 0, "rradius"); ent->bolt_LLeg = trap_G2API_AddBolt(ent->client->ghoul2, 0, "ltibia"); ent->bolt_RLeg = trap_G2API_AddBolt(ent->client->ghoul2, 0, "rtibia"); ent->bolt_Motion = trap_G2API_AddBolt(ent->client->ghoul2, 0, "Motion"); } // locate ent at a spawn point ClientSpawn( ent ); if ( client->sess.sessionTeam != TEAM_SPECTATOR ) { // send event tent = G_TempEntity( ent->client->ps.origin, EV_PLAYER_TELEPORT_IN ); tent->s.clientNum = ent->s.clientNum; if ( g_gametype.integer != GT_TOURNAMENT ) { trap_SendServerCommand( -1, va("print \"%s" S_COLOR_WHITE " %s\n\"", client->pers.netname, G_GetStripEdString("SVINGAME", "PLENTER")) ); } } G_LogPrintf( "ClientBegin: %i\n", clientNum ); // count current clients and rank for scoreboard CalculateRanks(); G_ClearClientLog(clientNum); }
/* =========== ClientUserInfoChanged Called from ClientConnect when the player first connects and directly by the server system when the player updates a userinfo variable. The game can override any of the settings and call trap_SetUserinfo if desired. ============ */ void ClientUserinfoChanged( int clientNum ) { gentity_t *ent; int teamTask, teamLeader, health; char *s; char model[ MAX_QPATH ]; char buffer[ MAX_QPATH ]; char filename[ MAX_QPATH ]; char oldname[ MAX_NAME_LENGTH ]; char newname[ MAX_NAME_LENGTH ]; char err[ MAX_STRING_CHARS ]; qboolean revertName = qfalse; qboolean showRenameMsg = qtrue; gclient_t *client; char c1[ MAX_INFO_STRING ]; char c2[ MAX_INFO_STRING ]; char userinfo[ MAX_INFO_STRING ]; team_t team; ent = g_entities + clientNum; client = ent->client; trap_GetUserinfo( clientNum, userinfo, sizeof( userinfo ) ); // check for malformed or illegal info strings if( !Info_Validate(userinfo) ) strcpy( userinfo, "\\name\\badinfo" ); // check for local client s = Info_ValueForKey( userinfo, "ip" ); if( !strcmp( s, "localhost" ) ) client->pers.localClient = qtrue; // check the item prediction s = Info_ValueForKey( userinfo, "cg_predictItems" ); if( !atoi( s ) ) client->pers.predictItemPickup = qfalse; else client->pers.predictItemPickup = qtrue; // set name Q_strncpyz( oldname, client->pers.netname, sizeof( oldname ) ); s = Info_ValueForKey( userinfo, "name" ); ClientCleanName( s, newname, sizeof( newname ) ); if( strcmp( oldname, newname ) ) { if( !strlen( oldname ) && client->pers.connected != CON_CONNECTED ) showRenameMsg = qfalse; // in case we need to revert and there's no oldname ClientCleanName( va( "%s", client->pers.netname ), oldname, sizeof( oldname ) ); if( g_newbieNumbering.integer ) { if( !strcmp( newname, "UnnamedPlayer" ) ) Q_strncpyz( newname, G_NextNewbieName( ent ), sizeof( newname ) ); if( !strcmp( oldname, "UnnamedPlayer" ) ) Q_strncpyz( oldname, G_NextNewbieName( ent ), sizeof( oldname ) ); } if( client->pers.muted ) { trap_SendServerCommand( ent - g_entities, "print \"You cannot change your name while you are muted\n\"" ); revertName = qtrue; } else if( client->pers.nameChangeTime && ( level.time - client->pers.nameChangeTime ) <= ( g_minNameChangePeriod.value * 1000 ) ) { trap_SendServerCommand( ent - g_entities, va( "print \"Name change spam protection (g_minNameChangePeriod = %d)\n\"", g_minNameChangePeriod.integer ) ); revertName = qtrue; } else if( g_maxNameChanges.integer > 0 && client->pers.nameChanges >= g_maxNameChanges.integer ) { trap_SendServerCommand( ent - g_entities, va( "print \"Maximum name changes reached (g_maxNameChanges = %d)\n\"", g_maxNameChanges.integer ) ); revertName = qtrue; } else if( !G_admin_name_check( ent, newname, err, sizeof( err ) ) ) { trap_SendServerCommand( ent - g_entities, va( "print \"%s\n\"", err ) ); revertName = qtrue; } else if( client->pers.nlocked ) { trap_SendServerCommand( ent - g_entities, "print \"Your name is locked, you can no longer rename.\n\"" ); revertName = qtrue; } if( revertName ) { Q_strncpyz( client->pers.netname, oldname, sizeof( client->pers.netname ) ); Info_SetValueForKey( userinfo, "name", oldname ); trap_SetUserinfo( clientNum, userinfo ); } else { Q_strncpyz( client->pers.netname, newname, sizeof( client->pers.netname ) ); Info_SetValueForKey( userinfo, "name", newname ); trap_SetUserinfo( clientNum, userinfo ); if( client->pers.connected == CON_CONNECTED ) { client->pers.nameChangeTime = level.time; client->pers.nameChanges++; } } } if( client->sess.sessionTeam == TEAM_SPECTATOR ) { if( client->sess.spectatorState == SPECTATOR_SCOREBOARD ) Q_strncpyz( client->pers.netname, "scoreboard", sizeof( client->pers.netname ) ); } if( client->pers.connected >= CON_CONNECTING && showRenameMsg ) { if( strcmp( oldname, client->pers.netname ) ) { trap_SendServerCommand( -1, va( "print \"%s" S_COLOR_WHITE " renamed to %s^7\n\"", oldname, client->pers.netname ) ); if( g_decolourLogfiles.integer) { char decoloured[ MAX_STRING_CHARS ] = ""; if( g_decolourLogfiles.integer == 1 ) { Com_sprintf( decoloured, sizeof(decoloured), " (\"%s^7\" -> \"%s^7\")", oldname, client->pers.netname ); G_DecolorString( decoloured, decoloured ); G_LogPrintfColoured( "ClientRename: %i [%s] (%s) \"%s^7\" -> \"%s^7\"%s\n", clientNum, client->pers.ip, client->pers.guid, oldname, client->pers.netname, decoloured ); } else { G_LogPrintf( "ClientRename: %i [%s] (%s) \"%s^7\" -> \"%s^7\"%s\n", clientNum, client->pers.ip, client->pers.guid, oldname, client->pers.netname, decoloured ); } } else { G_LogPrintf( "ClientRename: %i [%s] (%s) \"%s^7\" -> \"%s^7\"\n", clientNum, client->pers.ip, client->pers.guid, oldname, client->pers.netname ); } G_admin_namelog_update( client, qfalse ); } } // set max health health = atoi( Info_ValueForKey( userinfo, "handicap" ) ); client->pers.maxHealth = health; if( client->pers.maxHealth < 1 || client->pers.maxHealth > 100 ) client->pers.maxHealth = 100; //hack to force a client update if the config string does not change between spawning if( client->pers.classSelection == PCL_NONE ) client->pers.maxHealth = 0; // set model if( client->ps.stats[ STAT_PCLASS ] == PCL_HUMAN && BG_InventoryContainsUpgrade( UP_BATTLESUIT, client->ps.stats ) ) { Com_sprintf( buffer, MAX_QPATH, "%s/%s", BG_FindModelNameForClass( PCL_HUMAN_BSUIT ), BG_FindSkinNameForClass( PCL_HUMAN_BSUIT ) ); } else if( client->pers.classSelection == PCL_NONE ) { //This looks hacky and frankly it is. The clientInfo string needs to hold different //model details to that of the spawning class or the info change will not be //registered and an axis appears instead of the player model. There is zero chance //the player can spawn with the battlesuit, hence this choice. Com_sprintf( buffer, MAX_QPATH, "%s/%s", BG_FindModelNameForClass( PCL_HUMAN_BSUIT ), BG_FindSkinNameForClass( PCL_HUMAN_BSUIT ) ); } else { Com_sprintf( buffer, MAX_QPATH, "%s/%s", BG_FindModelNameForClass( client->pers.classSelection ), BG_FindSkinNameForClass( client->pers.classSelection ) ); } Q_strncpyz( model, buffer, sizeof( model ) ); //don't bother setting model type if spectating if( client->pers.classSelection != PCL_NONE ) { //model segmentation Com_sprintf( filename, sizeof( filename ), "models/players/%s/animation.cfg", BG_FindModelNameForClass( client->pers.classSelection ) ); if( G_NonSegModel( filename ) ) client->ps.persistant[ PERS_STATE ] |= PS_NONSEGMODEL; else client->ps.persistant[ PERS_STATE ] &= ~PS_NONSEGMODEL; } // wallwalk follow s = Info_ValueForKey( userinfo, "cg_wwFollow" ); if( atoi( s ) ) client->ps.persistant[ PERS_STATE ] |= PS_WALLCLIMBINGFOLLOW; else client->ps.persistant[ PERS_STATE ] &= ~PS_WALLCLIMBINGFOLLOW; // wallwalk toggle s = Info_ValueForKey( userinfo, "cg_wwToggle" ); if( atoi( s ) ) client->ps.persistant[ PERS_STATE ] |= PS_WALLCLIMBINGTOGGLE; else client->ps.persistant[ PERS_STATE ] &= ~PS_WALLCLIMBINGTOGGLE; // teamInfo s = Info_ValueForKey( userinfo, "teamoverlay" ); if( ! *s || atoi( s ) != 0 ) client->pers.teamInfo = qtrue; else client->pers.teamInfo = qfalse; s = Info_ValueForKey( userinfo, "cg_unlagged" ); if( !s[0] || atoi( s ) != 0 ) client->pers.useUnlagged = qtrue; else client->pers.useUnlagged = qfalse; // team task (0 = none, 1 = offence, 2 = defence) teamTask = atoi( Info_ValueForKey( userinfo, "teamtask" ) ); // team Leader (1 = leader, 0 is normal player) teamLeader = client->sess.teamLeader; // colors strcpy( c1, Info_ValueForKey( userinfo, "color1" ) ); strcpy( c2, Info_ValueForKey( userinfo, "color2" ) ); team = client->pers.teamSelection; // send over a subset of the userinfo keys so other clients can // print scoreboards, display models, and play custom sounds Com_sprintf( userinfo, sizeof( userinfo ), "n\\%s\\t\\%i\\model\\%s\\hmodel\\%s\\c1\\%s\\c2\\%s\\" "hc\\%i\\w\\%i\\l\\%i\\tt\\%d\\" "tl\\%d\\ig\\%16s", client->pers.netname, team, model, model, c1, c2, client->pers.maxHealth, client->sess.wins, client->sess.losses, teamTask, teamLeader, BG_ClientListString( &client->sess.ignoreList ) ); trap_SetConfigstring( CS_PLAYERS + clientNum, userinfo ); /*G_LogPrintf( "ClientUserinfoChanged: %i %s\n", clientNum, userinfo );*/ }
/* =============== G_AddBot =============== */ static void G_AddBot( const char *name, float skill, const char *team, int delay, char *altname) { int clientNum; char *botinfo; gentity_t *bot; char *key; char *s; char *botname; char *model; char *headmodel; char *logo; char userinfo[MAX_INFO_STRING]; // get the botinfo from bots.txt botinfo = G_GetBotInfoByName( name ); if ( !botinfo ) { G_Printf( S_COLOR_RED "Error: Bot '%s' not defined\n", name ); return; } // create the bot's userinfo userinfo[0] = '\0'; botname = Info_ValueForKey( botinfo, "funname" ); if( !botname[0] ) { botname = Info_ValueForKey( botinfo, "name" ); } // check for an alternative name if (altname && altname[0]) { botname = altname; } Info_SetValueForKey( userinfo, "name", botname ); Info_SetValueForKey( userinfo, "rate", "25000" ); Info_SetValueForKey( userinfo, "snaps", "20" ); Info_SetValueForKey( userinfo, "skill", va("%1.2f", skill) ); if ( skill >= 1 && skill < 2 ) { Info_SetValueForKey( userinfo, "handicap", "50" ); } else if ( skill >= 2 && skill < 3 ) { Info_SetValueForKey( userinfo, "handicap", "70" ); } else if ( skill >= 3 && skill < 4 ) { Info_SetValueForKey( userinfo, "handicap", "90" ); } key = "spraylogo"; logo = Info_ValueForKey( botinfo, key); if ( !*logo ) { logo = "15_padlogo"; } Info_SetValueForKey( userinfo, key, logo ); key = "model"; model = Info_ValueForKey( botinfo, key ); if ( !*model ) { model = "padman/default"; } else if ( g_gametype.integer < GT_TEAM ) { // If a team is given, but we're not in a team-gametype // this is caused by the ui, so we're forcing a skin. // This is rather nasty.. // TODO: Check whether the resulting model exists if ( *team ) { char *color = NULL; if ( !Q_stricmp( team, "red" ) || !Q_stricmp( team, "r" ) || !Q_stricmp( team, "0" ) ) { color = "red"; } else if ( !Q_stricmp( team, "blue" ) || !Q_stricmp( team, "b" ) || !Q_stricmp( team, "1" ) ) { color = "blue"; } // user decided to add a skin and specify a team // use skin color (way easier to implement) // FIXME: Explicitly search for color after ('/' or) '_' if ( strstr( model, "_red" ) || strstr( model, "_blue" ) ) { color = NULL; } if ( color ) { if ( strrchr( model, '/' ) ) { // model is a skin already, append colorname to skin // fatpad/fatty _red model = va( "%s_%s", model, color ); } else { // model is a "basemodel", append skin // fatpad /red model = va( "%s/%s", model, color ); } } } } Info_SetValueForKey( userinfo, key, model ); key = "team_model"; Info_SetValueForKey( userinfo, key, model ); key = "headmodel"; headmodel = Info_ValueForKey( botinfo, key ); if ( !*headmodel ) { headmodel = model; } Info_SetValueForKey( userinfo, key, headmodel ); key = "team_headmodel"; Info_SetValueForKey( userinfo, key, headmodel ); key = "gender"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) { s = "male"; } Info_SetValueForKey( userinfo, "sex", s ); key = "color1"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) { s = "4"; } Info_SetValueForKey( userinfo, key, s ); key = "color2"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) { s = "5"; } Info_SetValueForKey( userinfo, key, s ); s = Info_ValueForKey(botinfo, "aifile"); if (!*s ) { trap_Printf( S_COLOR_RED "Error: bot has no aifile specified\n" ); return; } // have the server allocate a client slot clientNum = trap_BotAllocateClient(); if ( clientNum == -1 ) { G_Printf( S_COLOR_RED "Unable to add bot. All player slots are in use.\n" ); G_Printf( S_COLOR_RED "Start server with more 'open' slots (or check setting of sv_maxclients cvar).\n" ); return; } // initialize the bot settings if( !team || !*team ) { if( g_gametype.integer >= GT_TEAM ) { if( PickTeam(clientNum) == TEAM_RED) { team = "red"; } else { team = "blue"; } } else { team = "red"; } } Info_SetValueForKey( userinfo, "characterfile", Info_ValueForKey( botinfo, "aifile" ) ); Info_SetValueForKey( userinfo, "skill", va( "%5.2f", skill ) ); Info_SetValueForKey( userinfo, "team", team ); bot = &g_entities[ clientNum ]; bot->r.svFlags |= SVF_BOT; bot->inuse = qtrue; // register the userinfo trap_SetUserinfo( clientNum, userinfo ); // have it connect to the game as a normal client if ( ClientConnect( clientNum, qtrue, qtrue ) ) { return; } if( delay == 0 ) { ClientBegin( clientNum ); return; } AddBotToSpawnQueue( clientNum, delay ); }
/* =============== G_AddBot =============== */ static void G_AddBot( const char *name, float skill, const char *team, int delay ) { int clientNum; char *botinfo; char *key; char *s; char *botname; char *model; char userinfo[MAX_INFO_STRING]; // have the server allocate a client slot clientNum = trap_BotAllocateClient(); if ( clientNum == -1 ) { G_Printf( S_COLOR_RED "Unable to add bot. All player slots are in use.\n" ); G_Printf( S_COLOR_RED "Start server with more 'open' slots (or check setting of sv_maxclients cvar).\n" ); return; } // get the botinfo from bots.txt botinfo = G_GetBotInfoByName( name ); if ( !botinfo ) { G_Printf( S_COLOR_RED "Error: Bot '%s' not defined\n", name ); trap_BotFreeClient( clientNum ); return; } // create the bot's userinfo userinfo[0] = '\0'; botname = Info_ValueForKey( botinfo, "funname" ); if ( !botname[0] ) { botname = Info_ValueForKey( botinfo, "name" ); } Info_SetValueForKey( userinfo, "name", botname ); Info_SetValueForKey( userinfo, "rate", "25000" ); Info_SetValueForKey( userinfo, "snaps", "20" ); Info_SetValueForKey( userinfo, "skill", va("%.2f", skill) ); if ( skill == 1 ) { Info_SetValueForKey( userinfo, "handicap", "50" ); } else if ( skill == 2 ) { Info_SetValueForKey( userinfo, "handicap", "70" ); } else if ( skill == 3 ) { Info_SetValueForKey( userinfo, "handicap", "90" ); } key = "model"; model = Info_ValueForKey( botinfo, key ); if ( !*model ) { model = "visor/default"; } Info_SetValueForKey( userinfo, key, model ); key = "gender"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) { s = "male"; } Info_SetValueForKey( userinfo, "sex", s ); key = "color"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) { s = "4"; } Info_SetValueForKey( userinfo, key, s ); s = Info_ValueForKey( botinfo, "aifile" ); if ( !*s ) { trap_Print( S_COLOR_RED "Error: bot has no aifile specified\n" ); trap_BotFreeClient( clientNum ); return; } Info_SetValueForKey( userinfo, "characterfile", s ); if ( !team || !*team ) { if ( g_gametype.integer == GT_TEAM || g_gametype.integer == GT_CTF ) { if ( PickTeam( clientNum ) == TEAM_RED ) { team = "red"; } else { team = "blue"; } } else { team = "red"; } } Info_SetValueForKey( userinfo, "team", team ); // register the userinfo trap_SetUserinfo( clientNum, userinfo ); // have it connect to the game as a normal client if ( ClientConnect( clientNum, qtrue, qtrue ) ) { return; } if ( delay == 0 ) { ClientBegin( clientNum ); return; } AddBotToSpawnQueue( clientNum, delay ); }
/* =============== G_AddBot =============== */ static void G_AddBot( const char *name, float skill, const char *team, int delay, char *altname) { int clientNum; char *botinfo; gentity_t *bot; char *key; char *s; char *botname; char *model; // char *headmodel; char userinfo[MAX_INFO_STRING]; // get the botinfo from bots.txt botinfo = G_GetBotInfoByName( name ); if ( !botinfo ) { G_Printf( S_COLOR_RED "Error: Bot '%s' not defined\n", name ); return; } // create the bot's userinfo userinfo[0] = '\0'; botname = Info_ValueForKey( botinfo, "funname" ); if( !botname[0] ) { botname = Info_ValueForKey( botinfo, "name" ); } // check for an alternative name if (altname && altname[0]) { botname = altname; } Info_SetValueForKey( userinfo, "name", botname ); Info_SetValueForKey( userinfo, "rate", "25000" ); Info_SetValueForKey( userinfo, "snaps", "20" ); Info_SetValueForKey( userinfo, "skill", va("%1.2f", skill) ); /* if ( skill >= 1 && skill < 2 ) { Info_SetValueForKey( userinfo, "handicap", "50" ); } else if ( skill >= 2 && skill < 3 ) { Info_SetValueForKey( userinfo, "handicap", "70" ); } else if ( skill >= 3 && skill < 4 ) { Info_SetValueForKey( userinfo, "handicap", "90" ); } */ key = "model"; model = Info_ValueForKey( botinfo, key ); if ( !*model ) { model = "visor/default"; } Info_SetValueForKey( userinfo, key, model ); key = "team_model"; Info_SetValueForKey( userinfo, key, model ); /* key = "headmodel"; headmodel = Info_ValueForKey( botinfo, key ); if ( !*headmodel ) { headmodel = model; } Info_SetValueForKey( userinfo, key, headmodel ); key = "team_headmodel"; Info_SetValueForKey( userinfo, key, headmodel ); */ key = "gender"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) { s = "male"; } Info_SetValueForKey( userinfo, "sex", s ); key = "color1"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) { s = "4"; } Info_SetValueForKey( userinfo, key, s ); key = "color2"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) { s = "5"; } Info_SetValueForKey( userinfo, key, s ); s = Info_ValueForKey(botinfo, "personality"); if (!*s ) { Info_SetValueForKey( userinfo, "personality", "botfiles/default.jkb" ); } else { Info_SetValueForKey( userinfo, "personality", s ); } // have the server allocate a client slot clientNum = trap_BotAllocateClient(); if ( clientNum == -1 ) { // G_Printf( S_COLOR_RED "Unable to add bot. All player slots are in use.\n" ); // G_Printf( S_COLOR_RED "Start server with more 'open' slots.\n" ); trap_SendServerCommand( -1, va("print \"%s\n\"", G_GetStripEdString("SVINGAME", "UNABLE_TO_ADD_BOT"))); return; } // initialize the bot settings if( !team || !*team || ( Q_stricmp(team, "red") && Q_stricmp(team, "blue") ) ) { team = ""; } // Info_SetValueForKey( userinfo, "characterfile", Info_ValueForKey( botinfo, "aifile" ) ); Info_SetValueForKey( userinfo, "skill", va( "%5.2f", skill ) ); Info_SetValueForKey( userinfo, "team", team ); bot = &g_entities[ clientNum ]; // register the userinfo trap_SetUserinfo( clientNum, userinfo ); // have it connect to the game as a normal client if ( ClientConnect( clientNum, qtrue, qtrue ) ) { return; } if( delay == 0 ) { ClientBegin( clientNum, qfalse ); return; } AddBotToSpawnQueue( clientNum, delay ); }
/* =============== G_AddBot =============== */ static void G_AddBot(const char *name, float skill, const char *team, int delay, char *altname) { int clientNum; char *botinfo; gentity_t *bot; char *s; char *botname; char *model; char *headmodel; char userinfo[MAX_INFO_STRING]; weapon_t tpWeapon = WP_M4; holdable_t tpItem = HI_LASER; // get the botinfo from bots.txt botinfo = G_GetBotInfoByName(name); if (!botinfo) { G_Printf(S_COLOR_RED "Error: Bot '%s' not defined\n", name); return; } // create the bot's userinfo userinfo[0] = '\0'; botname = Info_ValueForKey(botinfo, "funname"); if (!botname[0]) { botname = Info_ValueForKey(botinfo, "name"); } // check for an alternative name if (altname && altname[0]) { botname = altname; } Info_SetValueForKey(userinfo, "name", botname); Info_SetValueForKey(userinfo, "rate", "25000"); Info_SetValueForKey(userinfo, "snaps", "20"); Info_SetValueForKey(userinfo, "skill", va("%1.2f", skill)); if (skill >= 1 && skill < 2) { Info_SetValueForKey(userinfo, "handicap", "50"); } else if (skill >= 2 && skill < 3) { Info_SetValueForKey(userinfo, "handicap", "70"); } else if (skill >= 3 && skill < 4) { Info_SetValueForKey(userinfo, "handicap", "90"); } model = Info_ValueForKey(botinfo, "model"); if (!*model) { // Elder: changed to our default model = "reactionmale/default"; } Info_SetValueForKey(userinfo, "model", model); Info_SetValueForKey(userinfo, "team_model", model); headmodel = Info_ValueForKey(botinfo, "headmodel"); if (!*headmodel) { headmodel = model; } Info_SetValueForKey(userinfo, "headmodel", headmodel); Info_SetValueForKey(userinfo, "team_headmodel", headmodel); s = Info_ValueForKey(botinfo, "gender"); if (!*s) { s = "male"; } Info_SetValueForKey(userinfo, "sex", s); s = Info_ValueForKey(botinfo, "color1"); if (!*s) { s = "4"; } Info_SetValueForKey(userinfo, "color1", s); s = Info_ValueForKey(botinfo, "color2"); if (!*s) { s = "5"; } Info_SetValueForKey(userinfo, "color2", s); s = Info_ValueForKey(botinfo, "aifile"); if (!*s) { trap_Printf(S_COLOR_RED "Error: bot has no aifile specified\n"); return; } // have the server allocate a client slot clientNum = trap_BotAllocateClient(); if (clientNum == -1) { G_Printf(S_COLOR_RED "Unable to add bot. All player slots are in use.\n"); G_Printf(S_COLOR_RED "Start server with more 'open' slots (or check setting of sv_maxclients cvar).\n"); return; } // initialize the bot settings if (!team || !*team) { if (g_gametype.integer >= GT_TEAM) { if (PickTeam(clientNum) == TEAM_RED) { team = "red"; } else { team = "blue"; } } else { team = "red"; } } Info_SetValueForKey(userinfo, "characterfile", Info_ValueForKey(botinfo, "aifile")); Info_SetValueForKey(userinfo, "skill", va("%5.2f", skill)); Info_SetValueForKey(userinfo, "team", team); if (g_gametype.integer == GT_TEAMPLAY) { //Makro - load custom weapon/item from bot file tpWeapon = CharToWeapon(Info_ValueForKey(botinfo, "weapon"), WP_M4); tpItem = CharToItem(Info_ValueForKey(botinfo, "item"), HI_LASER); } Info_SetValueForKey(userinfo, "tpw", va("%i", tpWeapon - WP_NONE)); Info_SetValueForKey(userinfo, "tpi", va("%i", tpItem - HI_NONE)); bot = &g_entities[clientNum]; bot->r.svFlags |= SVF_BOT; bot->inuse = qtrue; // register the userinfo trap_SetUserinfo(clientNum, userinfo); // have it connect to the game as a normal client if (ClientConnect(clientNum, qtrue, qtrue)) { return; } if (delay == 0) { ClientBegin(clientNum); //Makro - load custom weapon/item from bot file if (g_gametype.integer == GT_TEAMPLAY) { bot->client->teamplayWeapon = tpWeapon; bot->client->teamplayItem = tpItem; } return; } AddBotToSpawnQueue(clientNum, delay); //Makro - load custom weapon/item from bot file if (g_gametype.integer == GT_TEAMPLAY) { bot->client->teamplayWeapon = tpWeapon; bot->client->teamplayItem = tpItem; } }
/* =============== G_AddBot =============== */ static void G_AddBot( const char *name, float skill, const char *team, int delay, char *altname) { int clientNum; int teamNum; int botinfoNum; char *botinfo; char *key; char *s; char *botname; char *model; char *headmodel; char userinfo[MAX_INFO_STRING]; // have the server allocate a client slot clientNum = trap_BotAllocateClient(); if ( clientNum == -1 ) { G_Printf( S_COLOR_RED "Unable to add bot. All player slots are in use.\n" ); G_Printf( S_COLOR_RED "Start server with more 'open' slots (or check setting of sv_maxclients cvar).\n" ); return; } // set default team if( !team || !*team ) { if( g_gametype.integer >= GT_TEAM ) { if( PickTeam(clientNum) == TEAM_RED) { team = "red"; } else { team = "blue"; } } else { team = "free"; } } // get the botinfo from bots.txt if ( Q_stricmp( name, "random" ) == 0 ) { if ( Q_stricmp( team, "red" ) == 0 || Q_stricmp( team, "r" ) == 0 ) { teamNum = TEAM_RED; } else if ( Q_stricmp( team, "blue" ) == 0 || Q_stricmp( team, "b" ) == 0 ) { teamNum = TEAM_BLUE; } else if ( !Q_stricmp( team, "spectator" ) || !Q_stricmp( team, "s" ) ) { teamNum = TEAM_SPECTATOR; } else { teamNum = TEAM_FREE; } botinfoNum = G_SelectRandomBotInfo( teamNum ); if ( botinfoNum < 0 ) { G_Printf( S_COLOR_RED "Error: Cannot add random bot, no bot info available.\n" ); trap_BotFreeClient( clientNum ); return; } botinfo = G_GetBotInfoByNumber( botinfoNum ); } else { botinfo = G_GetBotInfoByName( name ); } if ( !botinfo ) { G_Printf( S_COLOR_RED "Error: Bot '%s' not defined\n", name ); trap_BotFreeClient( clientNum ); return; } // create the bot's userinfo userinfo[0] = '\0'; botname = Info_ValueForKey( botinfo, "funname" ); if( !botname[0] ) { botname = Info_ValueForKey( botinfo, "name" ); } // check for an alternative name if (altname && altname[0]) { botname = altname; } Info_SetValueForKey( userinfo, "name", botname ); Info_SetValueForKey( userinfo, "rate", "25000" ); Info_SetValueForKey( userinfo, "snaps", "20" ); Info_SetValueForKey( userinfo, "skill", va("%.2f", skill) ); Info_SetValueForKey( userinfo, "teampref", team ); key = "model"; model = Info_ValueForKey( botinfo, key ); if ( !*model ) { model = "visor/default"; } Info_SetValueForKey( userinfo, key, model ); key = "team_model"; Info_SetValueForKey( userinfo, key, model ); key = "headmodel"; headmodel = Info_ValueForKey( botinfo, key ); if ( !*headmodel ) { headmodel = model; } Info_SetValueForKey( userinfo, key, headmodel ); key = "team_headmodel"; Info_SetValueForKey( userinfo, key, headmodel ); key = "gender"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) { s = "male"; } Info_SetValueForKey( userinfo, "sex", s ); key = "color1"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) { s = "4"; } Info_SetValueForKey( userinfo, key, s ); key = "color2"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) { s = "5"; } Info_SetValueForKey( userinfo, key, s ); s = Info_ValueForKey(botinfo, "aifile"); if (!*s ) { trap_Print( S_COLOR_RED "Error: bot has no aifile specified\n" ); trap_BotFreeClient( clientNum ); return; } Info_SetValueForKey( userinfo, "characterfile", s ); // don't send tinfo to bots, they don't parse it Info_SetValueForKey( userinfo, "teamoverlay", "0" ); // register the userinfo trap_SetUserinfo( clientNum, userinfo ); // have it connect to the game as a normal client if ( ClientConnect( clientNum, qtrue, qtrue ) ) { return; } if( delay == 0 ) { ClientBegin( clientNum ); return; } AddBotToSpawnQueue( clientNum, delay ); }
/* =============== G_AddBot =============== */ static void G_AddBot( const char *name, float skill, const char *team, int delay, char *altname) { int clientNum; char *botinfo; gentity_t *bot; char *key; char *s; char *botname; char *model; char *headmodel; char userinfo[MAX_INFO_STRING]; // get the botinfo from bots.txt botinfo = G_GetBotInfoByName( name ); if ( !botinfo ) { G_Printf( S_COLOR_RED "Error: Bot '%s' not defined\n", name ); return; } // create the bot's userinfo userinfo[0] = '\0'; botname = Info_ValueForKey( botinfo, "funname" ); if( !botname[0] ) { botname = Info_ValueForKey( botinfo, "name" ); } // check for an alternative name if (altname && altname[0]) { botname = altname; } Info_SetValueForKey( userinfo, "name", botname ); Info_SetValueForKey( userinfo, "rate", "25000" ); Info_SetValueForKey( userinfo, "snaps", "20" ); Info_SetValueForKey( userinfo, "skill", va("%1.2f", skill) ); if ( skill >= 1 && skill < 2 ) { Info_SetValueForKey( userinfo, "handicap", "50" ); } else if ( skill >= 2 && skill < 3 ) { Info_SetValueForKey( userinfo, "handicap", "70" ); } else if ( skill >= 3 && skill < 4 ) { Info_SetValueForKey( userinfo, "handicap", "90" ); } key = "model"; model = Info_ValueForKey( botinfo, key ); if ( !*model ) { model = "sarge/default"; } Info_SetValueForKey( userinfo, key, model ); key = "team_model"; Info_SetValueForKey( userinfo, key, model ); key = "headmodel"; headmodel = Info_ValueForKey( botinfo, key ); if ( !*headmodel ) { headmodel = model; } Info_SetValueForKey( userinfo, key, headmodel ); key = "team_headmodel"; Info_SetValueForKey( userinfo, key, headmodel ); key = "gender"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) { s = "male"; } Info_SetValueForKey( userinfo, "sex", s ); key = "color1"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) { s = "4"; } Info_SetValueForKey( userinfo, key, s ); key = "color2"; s = Info_ValueForKey( botinfo, key ); if ( !*s ) { s = "5"; } Info_SetValueForKey( userinfo, key, s ); s = Info_ValueForKey(botinfo, "aifile"); if (!*s ) { trap_Printf( S_COLOR_RED "Error: bot has no aifile specified\n" ); return; } // have the server allocate a client slot clientNum = trap_BotAllocateClient(); if ( clientNum == -1 ) { G_Printf( S_COLOR_RED "Unable to add bot. All player slots are in use.\n" ); G_Printf( S_COLOR_RED "Start server with more 'open' slots (or check setting of sv_maxclients cvar).\n" ); return; } // initialize the bot settings if( !team || !*team ) { if( g_gametype.integer >= GT_TEAM && g_ffa_gt!=1) { if( PickTeam(clientNum) == TEAM_RED) { team = "red"; } else { team = "blue"; } } else { team = "red"; } } Info_SetValueForKey( userinfo, "characterfile", Info_ValueForKey( botinfo, "aifile" ) ); Info_SetValueForKey( userinfo, "skill", va( "%5.2f", skill ) ); Info_SetValueForKey( userinfo, "team", team ); bot = &g_entities[ clientNum ]; bot->r.svFlags |= SVF_BOT; bot->inuse = qtrue; // register the userinfo trap_SetUserinfo( clientNum, userinfo ); // have it connect to the game as a normal client if ( ClientConnect( clientNum, qtrue, qtrue ) ) { return; } if( delay == 0 ) { ClientBegin( clientNum ); return; } AddBotToSpawnQueue( clientNum, delay ); }
/** * Add one bot into a team * @param name [string] bot's name * @param team [team_t] alien or human */ qboolean G_BotAdd( char *name, team_t team ) { int i; int clientNum; char userinfo[MAX_INFO_STRING]; int reservedSlots = 0; gentity_t *ent; namelog_t *namelog; char name_s[ MAX_NAME_LENGTH ]; char name_tmp_s[ MAX_NAME_LENGTH ] = ""; reservedSlots = trap_Cvar_VariableIntegerValue( "sv_privateclients" ); //If bot name does not contains [BOT], prepend it. if(g_bot_tagname.integer == 1) { G_DecolorString(name, name_s, MAX_NAME_LENGTH); if (! (Com_StringContains(name_s, "[BOT]", 0))) { if(team == TEAM_HUMANS) { strcat(name_tmp_s, "^4[BOT] "); } else if(team == TEAM_ALIENS) { strcat(name_tmp_s, "^1[BOT] "); } strcat(name_tmp_s, name); strcpy(name, name_tmp_s); } } // LEPE: check if no player/bot exists with that name G_SanitiseString(name, name_s, sizeof(name_s) ); for( namelog = level.namelogs; namelog; namelog = namelog->next ) { if( namelog->slot >= 0 ) { for( i = 0; i < MAX_NAMELOG_NAMES && namelog->name[ i ][ 0 ]; i++ ) { G_SanitiseString(namelog->name[ i ], name_tmp_s, sizeof(name_tmp_s) ); if( i == namelog->nameOffset && namelog->slot > -1 && !strcmp( name_s, name_tmp_s ) ) { trap_Print("Nick already exists\n"); return qfalse; } } } } // find what clientNum to use for bot // LEPE: clientNum calculation was modified to prevent player hijacking // We will assign slots from maxclients - 1 to maxclients - reservedSlots - 1 clientNum = -1; for( i = level.maxclients - 1; i > level.maxclients - reservedSlots - 1; i-- ) { if( !g_entities[i].inuse ) { clientNum = i; break; } } if(clientNum == -1) { trap_Print("Error: Bots were not assigned correctly\n"); //LEPE return qfalse; } if(clientNum < level.maxclients - reservedSlots - 1) { trap_Print("no more slots for bot\n"); return qfalse; } ent = &g_entities[ clientNum ]; ent->inuse = qtrue; ent->bot = (bot_t *)BG_Alloc( sizeof(bot_t) ); // ent->bot->path.crumb = BG_Alloc( sizeof(level.paths) ); ent->r.svFlags |= SVF_BOT; // register user information userinfo[0] = '\0'; Info_SetValueForKey( userinfo, "name", name ); Info_SetValueForKey( userinfo, "rate", "25000" ); //25000 Info_SetValueForKey( userinfo, "snaps", "40" ); //so we can connect if server is password protected if(g_needpass.integer == 1) { Info_SetValueForKey( userinfo, "password", g_password.string); } trap_SetUserinfo( clientNum, userinfo ); // have it connect to the game as a normal client if(ClientConnect(clientNum, qtrue) != NULL ) { G_Printf("Something weird happened. Bot was not added."); // won't let us join return qfalse; } ClientBegin( clientNum ); G_BotDebug(ent, BOT_VERB_IMPORTANT, BOT_DEBUG_GENERAL, "Bot Added\n"); G_ChangeTeam( ent, team ); BotInit( ent ); if(team == TEAM_HUMANS) { BotInitHuman( ent ); level.humanBots++; } else if(team == TEAM_ALIENS) { BotInitAlien( ent ); level.alienBots++; } //TODO: load profile return qtrue; }