00001
00002
00003
00004
00005 #include "g_local.h"
00006
00007
00008 static int g_numBots;
00009 static char *g_botInfos[MAX_BOTS];
00010
00011
00012 int g_numArenas;
00013 static char *g_arenaInfos[MAX_ARENAS];
00014
00015
00016 #define BOT_BEGIN_DELAY_BASE 2000
00017 #define BOT_BEGIN_DELAY_INCREMENT 1500
00018
00019 #define BOT_SPAWN_QUEUE_DEPTH 16
00020
00021 typedef struct {
00022 int clientNum;
00023 int spawnTime;
00024 } botSpawnQueue_t;
00025
00026
00027 static botSpawnQueue_t botSpawnQueue[BOT_SPAWN_QUEUE_DEPTH];
00028
00029 vmCvar_t bot_minplayers;
00030
00031 extern gentity_t *podium1;
00032 extern gentity_t *podium2;
00033 extern gentity_t *podium3;
00034
00035 #include "../namespace_begin.h"
00036 float trap_Cvar_VariableValue( const char *var_name ) {
00037 char buf[128];
00038
00039 trap_Cvar_VariableStringBuffer(var_name, buf, sizeof(buf));
00040 return atof(buf);
00041 }
00042 #include "../namespace_end.h"
00043
00044
00045
00046
00047
00048
00049
00050 int G_ParseInfos( char *buf, int max, char *infos[] ) {
00051 char *token;
00052 int count;
00053 char key[MAX_TOKEN_CHARS];
00054 char info[MAX_INFO_STRING];
00055
00056 count = 0;
00057
00058 while ( 1 ) {
00059 token = COM_Parse( (const char **)(&buf) );
00060 if ( !token[0] ) {
00061 break;
00062 }
00063 if ( strcmp( token, "{" ) ) {
00064 Com_Printf( "Missing { in info file\n" );
00065 break;
00066 }
00067
00068 if ( count == max ) {
00069 Com_Printf( "Max infos exceeded\n" );
00070 break;
00071 }
00072
00073 info[0] = '\0';
00074 while ( 1 ) {
00075 token = COM_ParseExt( (const char **)(&buf), qtrue );
00076 if ( !token[0] ) {
00077 Com_Printf( "Unexpected end of info file\n" );
00078 break;
00079 }
00080 if ( !strcmp( token, "}" ) ) {
00081 break;
00082 }
00083 Q_strncpyz( key, token, sizeof( key ) );
00084
00085 token = COM_ParseExt( (const char **)(&buf), qfalse );
00086 if ( !token[0] ) {
00087 strcpy( token, "<NULL>" );
00088 }
00089 Info_SetValueForKey( info, key, token );
00090 }
00091
00092 infos[count] = (char *) G_Alloc(strlen(info) + strlen("\\num\\") + strlen(va("%d", MAX_ARENAS)) + 1);
00093 if (infos[count]) {
00094 strcpy(infos[count], info);
00095 count++;
00096 }
00097 }
00098 return count;
00099 }
00100
00101
00102
00103
00104
00105
00106 static void G_LoadArenasFromFile( char *filename ) {
00107 int len;
00108 fileHandle_t f;
00109 char buf[MAX_ARENAS_TEXT];
00110
00111 len = trap_FS_FOpenFile( filename, &f, FS_READ );
00112 if ( !f ) {
00113 trap_Printf( va( S_COLOR_RED "file not found: %s\n", filename ) );
00114 return;
00115 }
00116 if ( len >= MAX_ARENAS_TEXT ) {
00117 trap_Printf( va( S_COLOR_RED "file too large: %s is %i, max allowed is %i", filename, len, MAX_ARENAS_TEXT ) );
00118 trap_FS_FCloseFile( f );
00119 return;
00120 }
00121
00122 trap_FS_Read( buf, len, f );
00123 buf[len] = 0;
00124 trap_FS_FCloseFile( f );
00125
00126 g_numArenas += G_ParseInfos( buf, MAX_ARENAS - g_numArenas, &g_arenaInfos[g_numArenas] );
00127 }
00128
00129 int G_GetMapTypeBits(char *type)
00130 {
00131 int typeBits = 0;
00132
00133 if( *type ) {
00134 if( strstr( type, "ffa" ) ) {
00135 typeBits |= (1 << GT_FFA);
00136 typeBits |= (1 << GT_TEAM);
00137 }
00138 if( strstr( type, "holocron" ) ) {
00139 typeBits |= (1 << GT_HOLOCRON);
00140 }
00141 if( strstr( type, "jedimaster" ) ) {
00142 typeBits |= (1 << GT_JEDIMASTER);
00143 }
00144 if( strstr( type, "duel" ) ) {
00145 typeBits |= (1 << GT_DUEL);
00146 typeBits |= (1 << GT_POWERDUEL);
00147 }
00148 if( strstr( type, "powerduel" ) ) {
00149 typeBits |= (1 << GT_DUEL);
00150 typeBits |= (1 << GT_POWERDUEL);
00151 }
00152 if( strstr( type, "siege" ) ) {
00153 typeBits |= (1 << GT_SIEGE);
00154 }
00155 if( strstr( type, "ctf" ) ) {
00156 typeBits |= (1 << GT_CTF);
00157 }
00158 if( strstr( type, "cty" ) ) {
00159 typeBits |= (1 << GT_CTY);
00160 }
00161 } else {
00162 typeBits |= (1 << GT_FFA);
00163 }
00164
00165 return typeBits;
00166 }
00167
00168 qboolean G_DoesMapSupportGametype(const char *mapname, int gametype)
00169 {
00170 int typeBits = 0;
00171 int thisLevel = -1;
00172 int n = 0;
00173 char *type = NULL;
00174
00175 if (!g_arenaInfos[0])
00176 {
00177 return qfalse;
00178 }
00179
00180 if (!mapname || !mapname[0])
00181 {
00182 return qfalse;
00183 }
00184
00185 for( n = 0; n < g_numArenas; n++ )
00186 {
00187 type = Info_ValueForKey( g_arenaInfos[n], "map" );
00188
00189 if (Q_stricmp(mapname, type) == 0)
00190 {
00191 thisLevel = n;
00192 break;
00193 }
00194 }
00195
00196 if (thisLevel == -1)
00197 {
00198 return qfalse;
00199 }
00200
00201 type = Info_ValueForKey(g_arenaInfos[thisLevel], "type");
00202
00203 typeBits = G_GetMapTypeBits(type);
00204 if (typeBits & (1 << gametype))
00205 {
00206 return qtrue;
00207 }
00208
00209 return qfalse;
00210 }
00211
00212
00213 const char *G_RefreshNextMap(int gametype, qboolean forced)
00214 {
00215 int typeBits = 0;
00216 int thisLevel = 0;
00217 int desiredMap = 0;
00218 int n = 0;
00219 char *type = NULL;
00220 qboolean loopingUp = qfalse;
00221 vmCvar_t mapname;
00222
00223 if (!g_autoMapCycle.integer && !forced)
00224 {
00225 return NULL;
00226 }
00227
00228 if (!g_arenaInfos[0])
00229 {
00230 return NULL;
00231 }
00232
00233 trap_Cvar_Register( &mapname, "mapname", "", CVAR_SERVERINFO | CVAR_ROM );
00234 for( n = 0; n < g_numArenas; n++ )
00235 {
00236 type = Info_ValueForKey( g_arenaInfos[n], "map" );
00237
00238 if (Q_stricmp(mapname.string, type) == 0)
00239 {
00240 thisLevel = n;
00241 break;
00242 }
00243 }
00244
00245 desiredMap = thisLevel;
00246
00247 n = thisLevel+1;
00248 while (n != thisLevel)
00249 {
00250 if (!g_arenaInfos[n] || n >= g_numArenas)
00251 {
00252 if (loopingUp)
00253 {
00254
00255 break;
00256 }
00257 n = 0;
00258 loopingUp = qtrue;
00259 }
00260
00261 type = Info_ValueForKey(g_arenaInfos[n], "type");
00262
00263 typeBits = G_GetMapTypeBits(type);
00264 if (typeBits & (1 << gametype))
00265 {
00266 desiredMap = n;
00267 break;
00268 }
00269
00270 n++;
00271 }
00272
00273 if (desiredMap == thisLevel)
00274 {
00275
00276 trap_Cvar_Set( "nextmap", "map_restart 0");
00277 }
00278 else
00279 {
00280 type = Info_ValueForKey( g_arenaInfos[desiredMap], "map" );
00281 trap_Cvar_Set( "nextmap", va("map %s", type));
00282 }
00283
00284 return Info_ValueForKey( g_arenaInfos[desiredMap], "map" );
00285 }
00286
00287
00288
00289
00290
00291
00292 static void G_LoadArenas( void ) {
00293 int numdirs;
00294 char filename[128];
00295 char dirlist[1024];
00296 char* dirptr;
00297 int i, n;
00298 int dirlen;
00299
00300 g_numArenas = 0;
00301
00302
00303 numdirs = trap_FS_GetFileList("scripts", ".arena", dirlist, 1024 );
00304 dirptr = dirlist;
00305 for (i = 0; i < numdirs; i++, dirptr += dirlen+1) {
00306 dirlen = strlen(dirptr);
00307 strcpy(filename, "scripts/");
00308 strcat(filename, dirptr);
00309 G_LoadArenasFromFile(filename);
00310 }
00311
00312
00313 for( n = 0; n < g_numArenas; n++ ) {
00314 Info_SetValueForKey( g_arenaInfos[n], "num", va( "%i", n ) );
00315 }
00316
00317 G_RefreshNextMap(g_gametype.integer, qfalse);
00318 }
00319
00320
00321
00322
00323
00324
00325
00326 const char *G_GetArenaInfoByMap( const char *map ) {
00327 int n;
00328
00329 for( n = 0; n < g_numArenas; n++ ) {
00330 if( Q_stricmp( Info_ValueForKey( g_arenaInfos[n], "map" ), map ) == 0 ) {
00331 return g_arenaInfos[n];
00332 }
00333 }
00334
00335 return NULL;
00336 }
00337
00338 #if 0
00339
00340
00341
00342
00343
00344 static void PlayerIntroSound( const char *modelAndSkin ) {
00345 char model[MAX_QPATH];
00346 char *skin;
00347
00348 Q_strncpyz( model, modelAndSkin, sizeof(model) );
00349 skin = Q_strrchr( model, '/' );
00350 if ( skin ) {
00351 *skin++ = '\0';
00352 }
00353 else {
00354 skin = model;
00355 }
00356
00357 if( Q_stricmp( skin, "default" ) == 0 ) {
00358 skin = model;
00359 }
00360
00361 trap_SendConsoleCommand( EXEC_APPEND, va( "play sound/player/announce/%s.wav\n", skin ) );
00362 }
00363 #endif
00364
00365
00366
00367
00368
00369
00370 void G_AddRandomBot( int team ) {
00371 int i, n, num;
00372 float skill;
00373 char *value, netname[36], *teamstr;
00374 gclient_t *cl;
00375
00376 num = 0;
00377 for ( n = 0; n < g_numBots ; n++ ) {
00378 value = Info_ValueForKey( g_botInfos[n], "name" );
00379
00380 for ( i=0 ; i< g_maxclients.integer ; i++ ) {
00381 cl = level.clients + i;
00382 if ( cl->pers.connected != CON_CONNECTED ) {
00383 continue;
00384 }
00385 if ( !(g_entities[cl->ps.clientNum].r.svFlags & SVF_BOT) ) {
00386 continue;
00387 }
00388 if (g_gametype.integer == GT_SIEGE)
00389 {
00390 if ( team >= 0 && cl->sess.siegeDesiredTeam != team ) {
00391 continue;
00392 }
00393 }
00394 else
00395 {
00396 if ( team >= 0 && cl->sess.sessionTeam != team ) {
00397 continue;
00398 }
00399 }
00400 if ( !Q_stricmp( value, cl->pers.netname ) ) {
00401 break;
00402 }
00403 }
00404 if (i >= g_maxclients.integer) {
00405 num++;
00406 }
00407 }
00408 num = random() * num;
00409 for ( n = 0; n < g_numBots ; n++ ) {
00410 value = Info_ValueForKey( g_botInfos[n], "name" );
00411
00412 for ( i=0 ; i< g_maxclients.integer ; i++ ) {
00413 cl = level.clients + i;
00414 if ( cl->pers.connected != CON_CONNECTED ) {
00415 continue;
00416 }
00417 if ( !(g_entities[cl->ps.clientNum].r.svFlags & SVF_BOT) ) {
00418 continue;
00419 }
00420 if (g_gametype.integer == GT_SIEGE)
00421 {
00422 if ( team >= 0 && cl->sess.siegeDesiredTeam != team ) {
00423 continue;
00424 }
00425 }
00426 else
00427 {
00428 if ( team >= 0 && cl->sess.sessionTeam != team ) {
00429 continue;
00430 }
00431 }
00432 if ( !Q_stricmp( value, cl->pers.netname ) ) {
00433 break;
00434 }
00435 }
00436 if (i >= g_maxclients.integer) {
00437 num--;
00438 if (num <= 0) {
00439 skill = trap_Cvar_VariableValue( "g_spSkill" );
00440 if (team == TEAM_RED) teamstr = "red";
00441 else if (team == TEAM_BLUE) teamstr = "blue";
00442 else teamstr = "";
00443 strncpy(netname, value, sizeof(netname)-1);
00444 netname[sizeof(netname)-1] = '\0';
00445 Q_CleanStr(netname);
00446 trap_SendConsoleCommand( EXEC_INSERT, va("addbot \"%s\" %f %s %i\n", netname, skill, teamstr, 0) );
00447 return;
00448 }
00449 }
00450 }
00451 }
00452
00453
00454
00455
00456
00457
00458 int G_RemoveRandomBot( int team ) {
00459 int i;
00460 char netname[36];
00461 gclient_t *cl;
00462
00463 for ( i=0 ; i< g_maxclients.integer ; i++ ) {
00464 cl = level.clients + i;
00465 if ( cl->pers.connected != CON_CONNECTED ) {
00466 continue;
00467 }
00468 if ( !(g_entities[cl->ps.clientNum].r.svFlags & SVF_BOT) ) {
00469 continue;
00470 }
00471 if (g_gametype.integer == GT_SIEGE)
00472 {
00473 if ( team >= 0 && cl->sess.siegeDesiredTeam != team ) {
00474 continue;
00475 }
00476 }
00477 else
00478 {
00479 if ( team >= 0 && cl->sess.sessionTeam != team ) {
00480 continue;
00481 }
00482 }
00483 strcpy(netname, cl->pers.netname);
00484 Q_CleanStr(netname);
00485 trap_SendConsoleCommand( EXEC_INSERT, va("kick \"%s\"\n", netname) );
00486 return qtrue;
00487 }
00488 return qfalse;
00489 }
00490
00491
00492
00493
00494
00495
00496 int G_CountHumanPlayers( int team ) {
00497 int i, num;
00498 gclient_t *cl;
00499
00500 num = 0;
00501 for ( i=0 ; i< g_maxclients.integer ; i++ ) {
00502 cl = level.clients + i;
00503 if ( cl->pers.connected != CON_CONNECTED ) {
00504 continue;
00505 }
00506 if ( g_entities[cl->ps.clientNum].r.svFlags & SVF_BOT ) {
00507 continue;
00508 }
00509 if ( team >= 0 && cl->sess.sessionTeam != team ) {
00510 continue;
00511 }
00512 num++;
00513 }
00514 return num;
00515 }
00516
00517
00518
00519
00520
00521
00522 int G_CountBotPlayers( int team ) {
00523 int i, n, num;
00524 gclient_t *cl;
00525
00526 num = 0;
00527 for ( i=0 ; i< g_maxclients.integer ; i++ ) {
00528 cl = level.clients + i;
00529 if ( cl->pers.connected != CON_CONNECTED ) {
00530 continue;
00531 }
00532 if ( !(g_entities[cl->ps.clientNum].r.svFlags & SVF_BOT) ) {
00533 continue;
00534 }
00535 if (g_gametype.integer == GT_SIEGE)
00536 {
00537 if ( team >= 0 && cl->sess.siegeDesiredTeam != team ) {
00538 continue;
00539 }
00540 }
00541 else
00542 {
00543 if ( team >= 0 && cl->sess.sessionTeam != team ) {
00544 continue;
00545 }
00546 }
00547 num++;
00548 }
00549 for( n = 0; n < BOT_SPAWN_QUEUE_DEPTH; n++ ) {
00550 if( !botSpawnQueue[n].spawnTime ) {
00551 continue;
00552 }
00553 if ( botSpawnQueue[n].spawnTime > level.time ) {
00554 continue;
00555 }
00556 num++;
00557 }
00558 return num;
00559 }
00560
00561
00562
00563
00564
00565
00566 void G_CheckMinimumPlayers( void ) {
00567 int minplayers;
00568 int humanplayers, botplayers;
00569 static int checkminimumplayers_time;
00570
00571 if (g_gametype.integer == GT_SIEGE)
00572 {
00573 return;
00574 }
00575
00576 if (level.intermissiontime) return;
00577
00578 if (checkminimumplayers_time > level.time - 10000) {
00579 return;
00580 }
00581 checkminimumplayers_time = level.time;
00582 trap_Cvar_Update(&bot_minplayers);
00583 minplayers = bot_minplayers.integer;
00584 if (minplayers <= 0) return;
00585
00586 if (minplayers > g_maxclients.integer)
00587 {
00588 minplayers = g_maxclients.integer;
00589 }
00590
00591 humanplayers = G_CountHumanPlayers( -1 );
00592 botplayers = G_CountBotPlayers( -1 );
00593
00594 if ((humanplayers+botplayers) < minplayers)
00595 {
00596 G_AddRandomBot(-1);
00597 }
00598 else if ((humanplayers+botplayers) > minplayers && botplayers)
00599 {
00600
00601 if (!G_RemoveRandomBot(TEAM_SPECTATOR))
00602 {
00603
00604 G_RemoveRandomBot(-1);
00605 }
00606 }
00607
00608
00609
00610
00611
00612
00613
00614
00615
00616
00617
00618
00619
00620
00621
00622
00623
00624
00625
00626
00627
00628
00629
00630
00631
00632
00633
00634
00635
00636
00637
00638
00639
00640
00641
00642
00643
00644
00645
00646
00647
00648
00649
00650
00651
00652
00653
00654
00655
00656
00657
00658
00659
00660
00661
00662
00663
00664
00665
00666
00667
00668
00669
00670
00671
00672
00673
00674
00675
00676
00677
00678
00679
00680
00681
00682
00683
00684
00685
00686
00687 }
00688
00689
00690
00691
00692
00693
00694 void G_CheckBotSpawn( void ) {
00695 int n;
00696
00697 G_CheckMinimumPlayers();
00698
00699 for( n = 0; n < BOT_SPAWN_QUEUE_DEPTH; n++ ) {
00700 if( !botSpawnQueue[n].spawnTime ) {
00701 continue;
00702 }
00703 if ( botSpawnQueue[n].spawnTime > level.time ) {
00704 continue;
00705 }
00706 ClientBegin( botSpawnQueue[n].clientNum, qfalse );
00707 botSpawnQueue[n].spawnTime = 0;
00708
00709
00710
00711
00712
00713
00714
00715 }
00716 }
00717
00718
00719
00720
00721
00722
00723
00724 static void AddBotToSpawnQueue( int clientNum, int delay ) {
00725 int n;
00726
00727 for( n = 0; n < BOT_SPAWN_QUEUE_DEPTH; n++ ) {
00728 if( !botSpawnQueue[n].spawnTime ) {
00729 botSpawnQueue[n].spawnTime = level.time + delay;
00730 botSpawnQueue[n].clientNum = clientNum;
00731 return;
00732 }
00733 }
00734
00735 G_Printf( S_COLOR_YELLOW "Unable to delay spawn\n" );
00736 ClientBegin( clientNum, qfalse );
00737 }
00738
00739
00740
00741
00742
00743
00744
00745
00746
00747
00748 void G_RemoveQueuedBotBegin( int clientNum ) {
00749 int n;
00750
00751 for( n = 0; n < BOT_SPAWN_QUEUE_DEPTH; n++ ) {
00752 if( botSpawnQueue[n].clientNum == clientNum ) {
00753 botSpawnQueue[n].spawnTime = 0;
00754 return;
00755 }
00756 }
00757 }
00758
00759
00760
00761
00762
00763
00764
00765 qboolean G_BotConnect( int clientNum, qboolean restart ) {
00766 bot_settings_t settings;
00767 char userinfo[MAX_INFO_STRING];
00768
00769 trap_GetUserinfo( clientNum, userinfo, sizeof(userinfo) );
00770
00771 Q_strncpyz( settings.personalityfile, Info_ValueForKey( userinfo, "personality" ), sizeof(settings.personalityfile) );
00772 settings.skill = atof( Info_ValueForKey( userinfo, "skill" ) );
00773 Q_strncpyz( settings.team, Info_ValueForKey( userinfo, "team" ), sizeof(settings.team) );
00774
00775 if (!BotAISetupClient( clientNum, &settings, restart )) {
00776 trap_DropClient( clientNum, "BotAISetupClient failed" );
00777 return qfalse;
00778 }
00779
00780 return qtrue;
00781 }
00782
00783
00784
00785
00786
00787
00788
00789 static void G_AddBot( const char *name, float skill, const char *team, int delay, char *altname) {
00790 int clientNum;
00791 char *botinfo;
00792 gentity_t *bot;
00793 char *key;
00794 char *s;
00795 char *botname;
00796 char *model;
00797
00798 char userinfo[MAX_INFO_STRING];
00799 int preTeam = 0;
00800
00801
00802 botinfo = G_GetBotInfoByName( name );
00803 if ( !botinfo ) {
00804 G_Printf( S_COLOR_RED "Error: Bot '%s' not defined\n", name );
00805 return;
00806 }
00807
00808
00809 userinfo[0] = '\0';
00810
00811 botname = Info_ValueForKey( botinfo, "funname" );
00812 if( !botname[0] ) {
00813 botname = Info_ValueForKey( botinfo, "name" );
00814 }
00815
00816 if (altname && altname[0]) {
00817 botname = altname;
00818 }
00819 Info_SetValueForKey( userinfo, "name", botname );
00820 Info_SetValueForKey( userinfo, "rate", "25000" );
00821 Info_SetValueForKey( userinfo, "snaps", "20" );
00822 Info_SetValueForKey( userinfo, "skill", va("%1.2f", skill) );
00823
00824 if ( skill >= 1 && skill < 2 ) {
00825 Info_SetValueForKey( userinfo, "handicap", "50" );
00826 }
00827 else if ( skill >= 2 && skill < 3 ) {
00828 Info_SetValueForKey( userinfo, "handicap", "70" );
00829 }
00830 else if ( skill >= 3 && skill < 4 ) {
00831 Info_SetValueForKey( userinfo, "handicap", "90" );
00832 }
00833
00834 key = "model";
00835 model = Info_ValueForKey( botinfo, key );
00836 if ( !*model ) {
00837 model = "kyle/default";
00838 }
00839 Info_SetValueForKey( userinfo, key, model );
00840
00841
00842
00843
00844
00845
00846
00847
00848
00849
00850 key = "gender";
00851 s = Info_ValueForKey( botinfo, key );
00852 if ( !*s ) {
00853 s = "male";
00854 }
00855 Info_SetValueForKey( userinfo, "sex", s );
00856
00857 key = "color1";
00858 s = Info_ValueForKey( botinfo, key );
00859 if ( !*s ) {
00860 s = "4";
00861 }
00862 Info_SetValueForKey( userinfo, key, s );
00863
00864 key = "color2";
00865 s = Info_ValueForKey( botinfo, key );
00866 if ( !*s ) {
00867 s = "4";
00868 }
00869 Info_SetValueForKey( userinfo, key, s );
00870
00871 key = "saber1";
00872 s = Info_ValueForKey( botinfo, key );
00873 if ( !*s ) {
00874 s = "single_1";
00875 }
00876 Info_SetValueForKey( userinfo, key, s );
00877
00878 key = "saber2";
00879 s = Info_ValueForKey( botinfo, key );
00880 if ( !*s ) {
00881 s = "none";
00882 }
00883 Info_SetValueForKey( userinfo, key, s );
00884
00885 s = Info_ValueForKey(botinfo, "personality");
00886 if (!*s )
00887 {
00888 Info_SetValueForKey( userinfo, "personality", "botfiles/default.jkb" );
00889 }
00890 else
00891 {
00892 Info_SetValueForKey( userinfo, "personality", s );
00893 }
00894
00895
00896 clientNum = trap_BotAllocateClient();
00897 if ( clientNum == -1 ) {
00898
00899
00900 trap_SendServerCommand( -1, va("print \"%s\n\"", G_GetStringEdString("MP_SVGAME", "UNABLE_TO_ADD_BOT")));
00901 return;
00902 }
00903
00904
00905 if( !team || !*team ) {
00906 if( g_gametype.integer >= GT_TEAM ) {
00907 if( PickTeam(clientNum) == TEAM_RED) {
00908 team = "red";
00909 }
00910 else {
00911 team = "blue";
00912 }
00913 }
00914 else {
00915 team = "red";
00916 }
00917 }
00918
00919 Info_SetValueForKey( userinfo, "skill", va( "%5.2f", skill ) );
00920 Info_SetValueForKey( userinfo, "team", team );
00921
00922 bot = &g_entities[ clientNum ];
00923 bot->r.svFlags |= SVF_BOT;
00924 bot->inuse = qtrue;
00925
00926
00927 trap_SetUserinfo( clientNum, userinfo );
00928
00929 if (g_gametype.integer >= GT_TEAM)
00930 {
00931 if (team && Q_stricmp(team, "red") == 0)
00932 {
00933 bot->client->sess.sessionTeam = TEAM_RED;
00934 }
00935 else if (team && Q_stricmp(team, "blue") == 0)
00936 {
00937 bot->client->sess.sessionTeam = TEAM_BLUE;
00938 }
00939 else
00940 {
00941 bot->client->sess.sessionTeam = PickTeam( -1 );
00942 }
00943 }
00944
00945 if (g_gametype.integer == GT_SIEGE)
00946 {
00947 bot->client->sess.siegeDesiredTeam = bot->client->sess.sessionTeam;
00948 bot->client->sess.sessionTeam = TEAM_SPECTATOR;
00949 }
00950
00951 preTeam = bot->client->sess.sessionTeam;
00952
00953
00954 if ( ClientConnect( clientNum, qtrue, qtrue ) ) {
00955 return;
00956 }
00957
00958 if (bot->client->sess.sessionTeam != preTeam)
00959 {
00960 trap_GetUserinfo(clientNum, userinfo, MAX_INFO_STRING);
00961
00962 if (bot->client->sess.sessionTeam == TEAM_SPECTATOR)
00963 {
00964 bot->client->sess.sessionTeam = preTeam;
00965 }
00966
00967 if (bot->client->sess.sessionTeam == TEAM_RED)
00968 {
00969 team = "Red";
00970 }
00971 else
00972 {
00973 if (g_gametype.integer == GT_SIEGE)
00974 {
00975 if (bot->client->sess.sessionTeam == TEAM_BLUE)
00976 {
00977 team = "Blue";
00978 }
00979 else
00980 {
00981 team = "s";
00982 }
00983 }
00984 else
00985 {
00986 team = "Blue";
00987 }
00988 }
00989
00990 Info_SetValueForKey( userinfo, "team", team );
00991
00992 trap_SetUserinfo( clientNum, userinfo );
00993
00994 bot->client->ps.persistant[ PERS_TEAM ] = bot->client->sess.sessionTeam;
00995
00996 G_ReadSessionData( bot->client );
00997 ClientUserinfoChanged( clientNum );
00998 }
00999
01000 if (g_gametype.integer == GT_DUEL ||
01001 g_gametype.integer == GT_POWERDUEL)
01002 {
01003 int loners = 0;
01004 int doubles = 0;
01005
01006 bot->client->sess.duelTeam = 0;
01007 G_PowerDuelCount(&loners, &doubles, qtrue);
01008
01009 if (!doubles || loners > (doubles/2))
01010 {
01011 bot->client->sess.duelTeam = DUELTEAM_DOUBLE;
01012 }
01013 else
01014 {
01015 bot->client->sess.duelTeam = DUELTEAM_LONE;
01016 }
01017
01018 bot->client->sess.sessionTeam = TEAM_SPECTATOR;
01019 SetTeam(bot, "s");
01020 }
01021 else
01022 {
01023 if( delay == 0 ) {
01024 ClientBegin( clientNum, qfalse );
01025 return;
01026 }
01027
01028 AddBotToSpawnQueue( clientNum, delay );
01029 }
01030 }
01031
01032
01033
01034
01035
01036
01037
01038 void Svcmd_AddBot_f( void ) {
01039 float skill;
01040 int delay;
01041 char name[MAX_TOKEN_CHARS];
01042 char altname[MAX_TOKEN_CHARS];
01043 char string[MAX_TOKEN_CHARS];
01044 char team[MAX_TOKEN_CHARS];
01045
01046
01047 if ( !trap_Cvar_VariableIntegerValue( "bot_enable" ) ) {
01048 return;
01049 }
01050
01051
01052 trap_Argv( 1, name, sizeof( name ) );
01053 if ( !name[0] ) {
01054 trap_Printf( "Usage: Addbot <botname> [skill 1-5] [team] [msec delay] [altname]\n" );
01055 return;
01056 }
01057
01058
01059 trap_Argv( 2, string, sizeof( string ) );
01060 if ( !string[0] ) {
01061 skill = 4;
01062 }
01063 else {
01064 skill = atof( string );
01065 }
01066
01067
01068 trap_Argv( 3, team, sizeof( team ) );
01069
01070
01071 trap_Argv( 4, string, sizeof( string ) );
01072 if ( !string[0] ) {
01073 delay = 0;
01074 }
01075 else {
01076 delay = atoi( string );
01077 }
01078
01079
01080 trap_Argv( 5, altname, sizeof( altname ) );
01081
01082 G_AddBot( name, skill, team, delay, altname );
01083
01084
01085
01086 if ( level.time - level.startTime > 1000 &&
01087 trap_Cvar_VariableIntegerValue( "cl_running" ) ) {
01088 trap_SendServerCommand( -1, "loaddefered\n" );
01089 }
01090 }
01091
01092
01093
01094
01095
01096
01097 void Svcmd_BotList_f( void ) {
01098 int i;
01099 char name[MAX_TOKEN_CHARS];
01100 char funname[MAX_TOKEN_CHARS];
01101 char model[MAX_TOKEN_CHARS];
01102 char personality[MAX_TOKEN_CHARS];
01103
01104 trap_Printf("^1name model personality funname\n");
01105 for (i = 0; i < g_numBots; i++) {
01106 strcpy(name, Info_ValueForKey( g_botInfos[i], "name" ));
01107 if ( !*name ) {
01108 strcpy(name, "Padawan");
01109 }
01110 strcpy(funname, Info_ValueForKey( g_botInfos[i], "funname" ));
01111 if ( !*funname ) {
01112 strcpy(funname, "");
01113 }
01114 strcpy(model, Info_ValueForKey( g_botInfos[i], "model" ));
01115 if ( !*model ) {
01116 strcpy(model, "kyle/default");
01117 }
01118 strcpy(personality, Info_ValueForKey( g_botInfos[i], "personality"));
01119 if (!*personality ) {
01120 strcpy(personality, "botfiles/kyle.jkb");
01121 }
01122 trap_Printf(va("%-16s %-16s %-20s %-20s\n", name, model, personality, funname));
01123 }
01124 }
01125
01126 #if 0
01127
01128
01129
01130
01131
01132 static void G_SpawnBots( char *botList, int baseDelay ) {
01133 char *bot;
01134 char *p;
01135 float skill;
01136 int delay;
01137 char bots[MAX_INFO_VALUE];
01138
01139 podium1 = NULL;
01140 podium2 = NULL;
01141 podium3 = NULL;
01142
01143 skill = trap_Cvar_VariableValue( "g_spSkill" );
01144 if( skill < 1 ) {
01145 trap_Cvar_Set( "g_spSkill", "1" );
01146 skill = 1;
01147 }
01148 else if ( skill > 5 ) {
01149 trap_Cvar_Set( "g_spSkill", "5" );
01150 skill = 5;
01151 }
01152
01153 Q_strncpyz( bots, botList, sizeof(bots) );
01154 p = &bots[0];
01155 delay = baseDelay;
01156 while( *p ) {
01157
01158 while( *p && *p == ' ' ) {
01159 p++;
01160 }
01161 if( !p ) {
01162 break;
01163 }
01164
01165
01166 bot = p;
01167
01168
01169 while( *p && *p != ' ' ) {
01170 p++;
01171 }
01172 if( *p ) {
01173 *p++ = 0;
01174 }
01175
01176
01177
01178 trap_SendConsoleCommand( EXEC_INSERT, va("addbot \"%s\" %f free %i\n", bot, skill, delay) );
01179
01180 delay += BOT_BEGIN_DELAY_INCREMENT;
01181 }
01182 }
01183 #endif
01184
01185
01186
01187
01188
01189
01190
01191 static void G_LoadBotsFromFile( char *filename ) {
01192 int len;
01193 fileHandle_t f;
01194 char buf[MAX_BOTS_TEXT];
01195
01196 len = trap_FS_FOpenFile( filename, &f, FS_READ );
01197 if ( !f ) {
01198 trap_Printf( va( S_COLOR_RED "file not found: %s\n", filename ) );
01199 return;
01200 }
01201 if ( len >= MAX_BOTS_TEXT ) {
01202 trap_Printf( va( S_COLOR_RED "file too large: %s is %i, max allowed is %i", filename, len, MAX_BOTS_TEXT ) );
01203 trap_FS_FCloseFile( f );
01204 return;
01205 }
01206
01207 trap_FS_Read( buf, len, f );
01208 buf[len] = 0;
01209 trap_FS_FCloseFile( f );
01210
01211 g_numBots += G_ParseInfos( buf, MAX_BOTS - g_numBots, &g_botInfos[g_numBots] );
01212 }
01213
01214
01215
01216
01217
01218
01219 static void G_LoadBots( void ) {
01220 vmCvar_t botsFile;
01221 int numdirs;
01222 char filename[128];
01223 char dirlist[1024];
01224 char* dirptr;
01225 int i;
01226 int dirlen;
01227
01228 if ( !trap_Cvar_VariableIntegerValue( "bot_enable" ) ) {
01229 return;
01230 }
01231
01232 g_numBots = 0;
01233
01234 trap_Cvar_Register( &botsFile, "g_botsFile", "", CVAR_INIT|CVAR_ROM );
01235 if( *botsFile.string ) {
01236 G_LoadBotsFromFile(botsFile.string);
01237 }
01238 else {
01239
01240 G_LoadBotsFromFile("botfiles/bots.txt");
01241 }
01242
01243
01244 numdirs = trap_FS_GetFileList("scripts", ".bot", dirlist, 1024 );
01245 dirptr = dirlist;
01246 for (i = 0; i < numdirs; i++, dirptr += dirlen+1) {
01247 dirlen = strlen(dirptr);
01248 strcpy(filename, "scripts/");
01249 strcat(filename, dirptr);
01250 G_LoadBotsFromFile(filename);
01251 }
01252
01253 }
01254
01255
01256
01257
01258
01259
01260
01261
01262 char *G_GetBotInfoByNumber( int num ) {
01263 if( num < 0 || num >= g_numBots ) {
01264 trap_Printf( va( S_COLOR_RED "Invalid bot number: %i\n", num ) );
01265 return NULL;
01266 }
01267 return g_botInfos[num];
01268 }
01269
01270
01271
01272
01273
01274
01275
01276 char *G_GetBotInfoByName( const char *name ) {
01277 int n;
01278 char *value;
01279
01280 for ( n = 0; n < g_numBots ; n++ ) {
01281 value = Info_ValueForKey( g_botInfos[n], "name" );
01282 if ( !Q_stricmp( value, name ) ) {
01283 return g_botInfos[n];
01284 }
01285 }
01286
01287 return NULL;
01288 }
01289
01290
01291 void LoadPath_ThisLevel(void);
01292
01293
01294
01295
01296
01297
01298
01299 void G_InitBots( qboolean restart ) {
01300 G_LoadBots();
01301 G_LoadArenas();
01302
01303 trap_Cvar_Register( &bot_minplayers, "bot_minplayers", "0", CVAR_SERVERINFO );
01304
01305
01306 LoadPath_ThisLevel();
01307
01308 }