home *** CD-ROM | disk | FTP | other *** search
/ Micromanía 92 / CDMM92_1.ISO / SOF 2 SDK / sof2sdk-101.msi / _92D6AC311BB48EBA344BBABC89DA6AB0 / _AC2B07EE23024458B3C3E5A318E8CAC1 < prev    next >
Encoding:
Text File  |  2002-06-05  |  21.1 KB  |  1,083 lines

  1. // Copyright (C) 2001-2002 Raven Software
  2. //
  3. // g_bot.c
  4.  
  5. #include "g_local.h"
  6.  
  7.  
  8. static int        g_numBots;
  9. static char        *g_botInfos[MAX_BOTS];
  10.  
  11.  
  12. int                g_numArenas;
  13. static char        *g_arenaInfos[MAX_ARENAS];
  14.  
  15.  
  16. #define BOT_BEGIN_DELAY_BASE        2000
  17. #define BOT_BEGIN_DELAY_INCREMENT    1500
  18.  
  19. #define BOT_SPAWN_QUEUE_DEPTH    16
  20.  
  21. typedef struct {
  22.     int        clientNum;
  23.     int        spawnTime;
  24. } botSpawnQueue_t;
  25.  
  26. //static int            botBeginDelay = 0;  // bk001206 - unused, init
  27. static botSpawnQueue_t    botSpawnQueue[BOT_SPAWN_QUEUE_DEPTH];
  28.  
  29. vmCvar_t bot_minplayers;
  30.  
  31. float trap_Cvar_VariableValue( const char *var_name ) {
  32.     char buf[128];
  33.  
  34.     trap_Cvar_VariableStringBuffer(var_name, buf, sizeof(buf));
  35.     return atof(buf);
  36. }
  37.  
  38.  
  39.  
  40. /*
  41. ===============
  42. G_ParseInfos
  43. ===============
  44. */
  45. int G_ParseInfos( const char *buf, int max, char *infos[] ) {
  46.     const char    *token;
  47.     int            count;
  48.     char        key[MAX_TOKEN_CHARS];
  49.     char        info[MAX_INFO_STRING];
  50.  
  51.     count = 0;
  52.  
  53.     while ( 1 ) {
  54.         token = COM_Parse( &buf );
  55.         if ( !token[0] ) {
  56.             break;
  57.         }
  58.         if ( strcmp( token, "{" ) ) {
  59.             Com_Printf( "Missing { in info file\n" );
  60.             break;
  61.         }
  62.  
  63.         if ( count == max ) {
  64.             Com_Printf( "Max infos exceeded\n" );
  65.             break;
  66.         }
  67.  
  68.         info[0] = '\0';
  69.         while ( 1 ) {
  70.             token = COM_ParseExt( &buf, qtrue );
  71.             if ( !token[0] ) {
  72.                 Com_Printf( "Unexpected end of info file\n" );
  73.                 break;
  74.             }
  75.             if ( !strcmp( token, "}" ) ) {
  76.                 break;
  77.             }
  78.             Q_strncpyz( key, token, sizeof( key ) );
  79.  
  80.             token = COM_ParseExt( &buf, qfalse );
  81.             if ( !token[0] ) 
  82.             {
  83.                 Info_SetValueForKey( info, key, "<NULL>" );
  84.             }
  85.             else
  86.             {
  87.                 Info_SetValueForKey( info, key, token );
  88.             }
  89.         }
  90.         //NOTE: extra space for arena number
  91.         infos[count] = trap_VM_LocalAlloc(strlen(info) + strlen("\\num\\") + strlen(va("%d", MAX_ARENAS)) + 1);
  92.         if (infos[count]) {
  93.             strcpy(infos[count], info);
  94.             count++;
  95.         }
  96.     }
  97.     return count;
  98. }
  99.  
  100. /*
  101. ===============
  102. G_LoadArenasFromFile
  103. ===============
  104. */
  105. static void G_LoadArenasFromFile( char *filename ) {
  106.     int                len;
  107.     fileHandle_t    f;
  108.     char            buf[MAX_ARENAS_TEXT];
  109.  
  110.     len = trap_FS_FOpenFile( filename, &f, FS_READ );
  111.     if ( !f ) {
  112.         trap_Printf( va( S_COLOR_RED "file not found: %s\n", filename ) );
  113.         return;
  114.     }
  115.     if ( len >= MAX_ARENAS_TEXT ) {
  116.         trap_Printf( va( S_COLOR_RED "file too large: %s is %i, max allowed is %i", filename, len, MAX_ARENAS_TEXT ) );
  117.         trap_FS_FCloseFile( f );
  118.         return;
  119.     }
  120.  
  121.     trap_FS_Read( buf, len, f );
  122.     buf[len] = 0;
  123.     trap_FS_FCloseFile( f );
  124.  
  125.     g_numArenas += G_ParseInfos( buf, MAX_ARENAS - g_numArenas, &g_arenaInfos[g_numArenas] );
  126. }
  127.  
  128. /*
  129. ===============
  130. G_LoadArenas
  131. ===============
  132. */
  133. void G_LoadArenas( void ) 
  134. {
  135.     int            numdirs;
  136.     char        filename[128];
  137.     char        dirlist[1024];
  138.     char*        dirptr;
  139.     int            i, n;
  140.     int            dirlen;
  141.  
  142.     g_numArenas = 0;
  143.  
  144.     // get all arenas from .arena files
  145.     numdirs = trap_FS_GetFileList("scripts", ".arena", dirlist, 1024 );
  146.     dirptr  = dirlist;
  147.     for (i = 0; i < numdirs; i++, dirptr += dirlen+1) 
  148.     {
  149.         dirlen = strlen(dirptr);
  150.         strcpy(filename, "scripts/");
  151.         strcat(filename, dirptr);
  152.         G_LoadArenasFromFile(filename);
  153.     }
  154.  
  155. #ifdef _DEBUG
  156.     Com_Printf ( "%i arenas parsed\n", g_numArenas );
  157. #endif
  158.     
  159.     for( n = 0; n < g_numArenas; n++ ) 
  160.     {
  161.         Info_SetValueForKey( g_arenaInfos[n], "num", va( "%i", n ) );
  162.     }
  163. }
  164.  
  165. /*
  166. ===============
  167. G_GetArenaInfoByNumber
  168. ===============
  169. */
  170. const char *G_GetArenaInfoByMap( const char *map ) 
  171. {
  172.     int n;
  173.  
  174.     for( n = 0; n < g_numArenas; n++ ) 
  175.     {
  176.         if( Q_stricmp( Info_ValueForKey( g_arenaInfos[n], "map" ), map ) == 0 ) 
  177.         {
  178.             return g_arenaInfos[n];
  179.         }
  180.     }
  181.  
  182.     return NULL;
  183. }
  184.  
  185. /*
  186. ===============
  187. G_DoesMapSupportGametype
  188.  
  189. determines whether or not the current map supports the given gametype
  190. ===============
  191. */
  192. qboolean G_DoesMapSupportGametype ( const char* gametype )
  193. {
  194.     char        mapname[MAX_QPATH];
  195.     const char* info;
  196.     const char*    type;
  197.     char*        token;
  198.  
  199.     // Figure out the current map name first
  200.     if ( RMG.integer )
  201.     {
  202.         Com_sprintf ( mapname, MAX_QPATH, "*random" );
  203.     }
  204.     else
  205.     {
  206.         trap_Cvar_VariableStringBuffer ( "mapname", mapname, MAX_QPATH );
  207.     }
  208.  
  209.     // Get the arena info for the current map 
  210.     info = G_GetArenaInfoByMap ( mapname );
  211.     if ( !info )
  212.     {
  213.         // If they dont have an area file for this map then
  214.         // just assume it supports all gametypes
  215.         return qtrue;
  216.     }
  217.  
  218.     // Get the supported gametypes
  219.     type = Info_ValueForKey( info, "type" );
  220.  
  221.     while ( 1 )
  222.     {
  223.         token = COM_Parse ( &type );
  224.         if ( !token || !*token )
  225.         {
  226.             break;
  227.         }
  228.  
  229.         if ( Q_stricmp ( gametype, token ) == 0 )
  230.         {
  231.             return qtrue;
  232.         }
  233.     }
  234.  
  235.     return qfalse;
  236. }
  237.  
  238. /*
  239. =================
  240. PlayerIntroSound
  241. =================
  242. */
  243. static void PlayerIntroSound( const char *modelAndSkin ) {
  244.     char    model[MAX_QPATH];
  245.     char    *skin;
  246.  
  247.     Q_strncpyz( model, modelAndSkin, sizeof(model) );
  248.     skin = Q_strrchr( model, '/' );
  249.     if ( skin ) {
  250.         *skin++ = '\0';
  251.     }
  252.     else {
  253.         skin = model;
  254.     }
  255.  
  256.     if( Q_stricmp( skin, "default" ) == 0 ) {
  257.         skin = model;
  258.     }
  259.  
  260.     trap_SendConsoleCommand( EXEC_APPEND, va( "play sound/player/announce/%s.wav\n", skin ) );
  261. }
  262.  
  263. /*
  264. ===============
  265. G_AddRandomBot
  266. ===============
  267. */
  268. void G_AddRandomBot( int team ) 
  269. {
  270.     int            i;
  271.     int            n;
  272.     int            num;
  273.     float        skill;
  274.     char        netname[36];
  275.     char        *value;
  276.     char        *teamstr;
  277.     gclient_t    *cl;
  278.  
  279.     num = 0;
  280.     
  281.     for ( n = 0; n < g_numBots ; n++ ) 
  282.     {
  283.         value = Info_ValueForKey( g_botInfos[n], "name" );
  284.     
  285.         //
  286.         for ( i=0 ; i< g_maxclients.integer ; i++ ) 
  287.         {
  288.             cl = level.clients + i;
  289.             if ( cl->pers.connected != CON_CONNECTED ) 
  290.             {
  291.                 continue;
  292.             }
  293.             
  294.             if ( !(g_entities[cl->ps.clientNum].r.svFlags & SVF_BOT) ) 
  295.             {
  296.                 continue;
  297.             }
  298.             
  299.             if ( team >= 0 && cl->sess.team != team ) 
  300.             {
  301.                 continue;
  302.             }
  303.             
  304.             if ( !Q_stricmp( value, cl->pers.netname ) ) 
  305.             {
  306.                 break;
  307.             }
  308.         }
  309.         
  310.         if (i >= g_maxclients.integer) 
  311.         {
  312.             num++;
  313.         }
  314.     }
  315.     
  316.     num = random() * num;
  317.     
  318.     for ( n = 0; n < g_numBots ; n++ ) 
  319.     {
  320.         value = Info_ValueForKey( g_botInfos[n], "name" );
  321.         //
  322.         for ( i=0 ; i< g_maxclients.integer ; i++ ) 
  323.         {
  324.             cl = level.clients + i;
  325.             if ( cl->pers.connected != CON_CONNECTED ) 
  326.             {
  327.                 continue;
  328.             }
  329.             
  330.             if ( !(g_entities[cl->ps.clientNum].r.svFlags & SVF_BOT) ) 
  331.             {
  332.                 continue;
  333.             }
  334.             
  335.             if ( team >= 0 && cl->sess.team != team ) 
  336.             {
  337.                 continue;
  338.             }
  339.             
  340.             if ( !Q_stricmp( value, cl->pers.netname ) ) 
  341.             {
  342.                 break;
  343.             }
  344.         }
  345.         
  346.         if (i >= g_maxclients.integer) 
  347.         {
  348.             num--;
  349.             
  350.             if (num <= 0) 
  351.             {
  352.                 skill = trap_Cvar_VariableValue( "g_botSkill" );
  353.                 if (team == TEAM_RED) teamstr = "red";
  354.                 else if (team == TEAM_BLUE) teamstr = "blue";
  355.                 else teamstr = "";
  356.                 strncpy(netname, value, sizeof(netname)-1);
  357.                 netname[sizeof(netname)-1] = '\0';
  358.                 Q_CleanStr(netname);
  359.                 trap_SendConsoleCommand( EXEC_INSERT, va("addbot %s %f %s %i\n", netname, skill, teamstr, 0) );
  360.                 return;
  361.             }
  362.         }
  363.     }
  364. }
  365.  
  366. /*
  367. ===============
  368. G_RemoveRandomBot
  369. ===============
  370. */
  371. int G_RemoveRandomBot( int team ) 
  372. {
  373.     int            i;
  374.     char        netname[36];
  375.     gclient_t    *cl;
  376.  
  377.     for ( i=0 ; i< g_maxclients.integer ; i++ ) 
  378.     {
  379.         cl = level.clients + i;
  380.         
  381.         if ( cl->pers.connected != CON_CONNECTED ) 
  382.         {
  383.             continue;
  384.         }
  385.         
  386.         if ( !(g_entities[cl->ps.clientNum].r.svFlags & SVF_BOT) ) 
  387.         {
  388.             continue;
  389.         }
  390.         
  391.         if ( team >= 0 && cl->sess.team != team ) 
  392.         {
  393.             continue;
  394.         }
  395.     
  396.         strcpy(netname, cl->pers.netname);
  397.         Q_CleanStr(netname);
  398.         trap_SendConsoleCommand( EXEC_INSERT, va("kick \"%s\"\n", netname) );
  399.         return qtrue;
  400.     }
  401.  
  402.     return qfalse;
  403. }
  404.  
  405. /*
  406. ===============
  407. G_CountHumanPlayers
  408. ===============
  409. */
  410. int G_CountHumanPlayers( int team ) 
  411. {
  412.     int            i;
  413.     int            num;
  414.     gclient_t    *cl;
  415.  
  416.     num = 0;
  417.     for ( i=0 ; i< g_maxclients.integer ; i++ ) 
  418.     {
  419.         cl = level.clients + i;
  420.         if ( cl->pers.connected != CON_CONNECTED ) 
  421.         {
  422.             continue;
  423.         }
  424.  
  425.         if ( g_entities[cl->ps.clientNum].r.svFlags & SVF_BOT ) 
  426.         {
  427.             continue;
  428.         }
  429.  
  430.         if ( team >= 0 && cl->sess.team != team ) 
  431.         {
  432.             continue;
  433.         }
  434.  
  435.         num++;
  436.     }
  437.  
  438.     return num;
  439. }
  440.  
  441. /*
  442. ===============
  443. G_CountBotPlayers
  444. ===============
  445. */
  446. int G_CountBotPlayers( int team ) 
  447. {
  448.     int            i;
  449.     int            n;
  450.     int            num;
  451.     gclient_t    *cl;
  452.  
  453.     num = 0;
  454.     
  455.     for ( i=0 ; i< g_maxclients.integer ; i++ ) 
  456.     {
  457.         cl = level.clients + i;
  458.         if ( cl->pers.connected != CON_CONNECTED ) 
  459.         {
  460.             continue;
  461.         }
  462.  
  463.         if ( !(g_entities[cl->ps.clientNum].r.svFlags & SVF_BOT) ) 
  464.         {
  465.             continue;
  466.         }
  467.  
  468.         if ( team >= 0 && cl->sess.team != team ) 
  469.         {
  470.             continue;
  471.         }
  472.  
  473.         num++;
  474.     }
  475.     
  476.     for( n = 0; n < BOT_SPAWN_QUEUE_DEPTH; n++ ) 
  477.     {
  478.         if( !botSpawnQueue[n].spawnTime ) 
  479.         {
  480.             continue;
  481.         }
  482.         
  483.         if ( botSpawnQueue[n].spawnTime > level.time ) 
  484.         {
  485.             continue;
  486.         }
  487.         
  488.         num++;
  489.     }
  490.  
  491.     return num;
  492. }
  493.  
  494. /*
  495. ===============
  496. G_CheckMinimumPlayers
  497. ===============
  498. */
  499. void G_CheckMinimumPlayers( void ) 
  500. {
  501.     int            minplayers;
  502.     int            humanplayers;
  503.     int            botplayers;
  504.     static int    checkminimumplayers_time;
  505.  
  506.     if (level.intermissiontime) 
  507.     {
  508.         return;
  509.     }
  510.  
  511.     // only check once each 10 seconds
  512.     if (checkminimumplayers_time > level.time - 10000) 
  513.     {
  514.         return;
  515.     }
  516.  
  517.     checkminimumplayers_time = level.time;
  518.     trap_Cvar_Update(&bot_minplayers);
  519.     minplayers = bot_minplayers.integer;
  520.  
  521.     if (minplayers <= 0) 
  522.     {
  523.         return;
  524.     }
  525.  
  526.     if ( level.gametypeData->teams ) 
  527.     {
  528.         if (minplayers >= g_maxclients.integer / 2) 
  529.         {
  530.             minplayers = (g_maxclients.integer / 2) -1;
  531.         }
  532.  
  533.         humanplayers = G_CountHumanPlayers( TEAM_RED );
  534.         botplayers = G_CountBotPlayers(    TEAM_RED );
  535.         
  536.         //
  537.         if (humanplayers + botplayers < minplayers) 
  538.         {
  539.             G_AddRandomBot( TEAM_RED );
  540.         } 
  541.         else if (humanplayers + botplayers > minplayers && botplayers) 
  542.         {
  543.             G_RemoveRandomBot( TEAM_RED );
  544.         }
  545.         
  546.         //
  547.         humanplayers = G_CountHumanPlayers( TEAM_BLUE );
  548.         botplayers = G_CountBotPlayers( TEAM_BLUE );
  549.         
  550.         //
  551.         if (humanplayers + botplayers < minplayers) 
  552.         {
  553.             G_AddRandomBot( TEAM_BLUE );
  554.         } 
  555.         else if (humanplayers + botplayers > minplayers && botplayers) 
  556.         {
  557.             G_RemoveRandomBot( TEAM_BLUE );
  558.         }
  559.     }
  560.     else 
  561.     {
  562.         if (minplayers >= g_maxclients.integer) 
  563.         {
  564.             minplayers = g_maxclients.integer-1;
  565.         }
  566.  
  567.         humanplayers = G_CountHumanPlayers( TEAM_FREE );
  568.         botplayers = G_CountBotPlayers( TEAM_FREE );
  569.         //
  570.         
  571.         if (humanplayers + botplayers < minplayers) 
  572.         {
  573.             G_AddRandomBot( TEAM_FREE );
  574.         } 
  575.         else if (humanplayers + botplayers > minplayers && botplayers) 
  576.         {
  577.             G_RemoveRandomBot( TEAM_FREE );
  578.         }
  579.     }
  580. }
  581.  
  582. /*
  583. ===============
  584. G_CheckBotSpawn
  585. ===============
  586. */
  587. void G_CheckBotSpawn( void ) 
  588. {
  589.     int        n;
  590.  
  591.     G_CheckMinimumPlayers();
  592.  
  593.     for( n = 0; n < BOT_SPAWN_QUEUE_DEPTH; n++ ) 
  594.     {
  595.         if( !botSpawnQueue[n].spawnTime ) 
  596.         {
  597.             continue;
  598.         }
  599.         if ( botSpawnQueue[n].spawnTime > level.time ) 
  600.         {
  601.             continue;
  602.         }
  603.         ClientBegin( botSpawnQueue[n].clientNum );
  604.         botSpawnQueue[n].spawnTime = 0;
  605.     }
  606. }
  607.  
  608.  
  609. /*
  610. ===============
  611. AddBotToSpawnQueue
  612. ===============
  613. */
  614. static void AddBotToSpawnQueue( int clientNum, int delay ) 
  615. {
  616.     int        n;
  617.  
  618.     for( n = 0; n < BOT_SPAWN_QUEUE_DEPTH; n++ ) 
  619.     {
  620.         if( !botSpawnQueue[n].spawnTime ) 
  621.         {
  622.             botSpawnQueue[n].spawnTime = level.time + delay;
  623.             botSpawnQueue[n].clientNum = clientNum;
  624.             return;
  625.         }
  626.     }
  627.  
  628.     Com_Printf( S_COLOR_YELLOW "Unable to delay spawn\n" );
  629.     ClientBegin( clientNum );
  630. }
  631.  
  632. /*
  633. ===============
  634. G_RemoveQueuedBotBegin
  635.  
  636. Called on client disconnect to make sure the delayed spawn
  637. doesn't happen on a freed index
  638. ===============
  639. */
  640. void G_RemoveQueuedBotBegin( int clientNum ) {
  641.     int        n;
  642.  
  643.     for( n = 0; n < BOT_SPAWN_QUEUE_DEPTH; n++ ) {
  644.         if( botSpawnQueue[n].clientNum == clientNum ) {
  645.             botSpawnQueue[n].spawnTime = 0;
  646.             return;
  647.         }
  648.     }
  649. }
  650.  
  651.  
  652. /*
  653. ===============
  654. G_BotConnect
  655. ===============
  656. */
  657. qboolean G_BotConnect( int clientNum, qboolean restart ) {
  658.     bot_settings_t    settings;
  659.     char            userinfo[MAX_INFO_STRING];
  660.  
  661.     trap_GetUserinfo( clientNum, userinfo, sizeof(userinfo) );
  662.  
  663.     Q_strncpyz( settings.personalityfile, Info_ValueForKey( userinfo, "personality" ), sizeof(settings.personalityfile) );
  664.     settings.skill = atof( Info_ValueForKey( userinfo, "skill" ) );
  665.     Q_strncpyz( settings.team, Info_ValueForKey( userinfo, "team" ), sizeof(settings.team) );
  666.  
  667.     if (!BotAISetupClient( clientNum, &settings, restart )) {
  668.         trap_DropClient( clientNum, "BotAISetupClient failed" );
  669.         return qfalse;
  670.     }
  671.  
  672.     return qtrue;
  673. }
  674.  
  675.  
  676. /*
  677. ===============
  678. G_AddBot
  679. ===============
  680. */
  681. static void G_AddBot( const char *name, float skill, const char *team, int delay, char *altname) 
  682. {
  683.     int                clientNum;
  684.     char            *botinfo;
  685.     gentity_t        *bot;
  686.     char            *key;
  687.     char            *s;
  688.     char            *botname;
  689.     char            *identity;
  690.     char            userinfo[MAX_INFO_STRING];
  691.  
  692.     // get the botinfo from bots.txt
  693.     botinfo = G_GetBotInfoByName( name );
  694.     if ( !botinfo ) {
  695.         Com_Printf( S_COLOR_RED "Error: Bot '%s' not defined\n", name );
  696.         return;
  697.     }
  698.  
  699.     // create the bot's userinfo
  700.     userinfo[0] = '\0';
  701.  
  702.     botname = Info_ValueForKey( botinfo, "funname" );
  703.     if( !botname[0] ) {
  704.         botname = Info_ValueForKey( botinfo, "name" );
  705.     }
  706.     // check for an alternative name
  707.     if (altname && altname[0]) {
  708.         botname = altname;
  709.     }
  710.     Info_SetValueForKey( userinfo, "name", botname );
  711.     Info_SetValueForKey( userinfo, "rate", "25000" );
  712.     Info_SetValueForKey( userinfo, "snaps", "20" );
  713.     Info_SetValueForKey( userinfo, "skill", va("%1.2f", skill) );
  714.  
  715.     if ( skill >= 1 && skill < 2 ) {
  716.         Info_SetValueForKey( userinfo, "handicap", "50" );
  717.     }
  718.     else if ( skill >= 2 && skill < 3 ) {
  719.         Info_SetValueForKey( userinfo, "handicap", "70" );
  720.     }
  721.     else if ( skill >= 3 && skill < 4 ) {
  722.         Info_SetValueForKey( userinfo, "handicap", "90" );
  723.     }
  724.  
  725.     key = "identity";
  726.     identity = Info_ValueForKey( botinfo, key );
  727.     if ( !*identity ) 
  728.     {
  729.         identity = "mullinsjungle";
  730.     }
  731.     Info_SetValueForKey( userinfo, key, identity );
  732.  
  733.     s = Info_ValueForKey(botinfo, "personality");
  734.     if (!*s )
  735.     {
  736.         Info_SetValueForKey( userinfo, "personality", "botfiles/default.jkb" );
  737.     }
  738.     else
  739.     {
  740.         Info_SetValueForKey( userinfo, "personality", s );
  741.     }
  742.  
  743.     // have the server allocate a client slot
  744.     clientNum = trap_BotAllocateClient();
  745.     if ( clientNum == -1 ) {
  746.         Com_Printf( S_COLOR_RED "Unable to add bot.  All player slots are in use.\n" );
  747.         Com_Printf( S_COLOR_RED "Start server with more 'open' slots (or check setting of sv_maxclients cvar).\n" );
  748.         return;
  749.     }
  750.  
  751.     // initialize the bot settings
  752.     if( !team || !*team || !Q_stricmp ( team, "auto" )) 
  753.     {
  754.         if( level.gametypeData->teams ) 
  755.         {
  756.             if( PickTeam(clientNum) == TEAM_RED) 
  757.             {
  758.                 team = "red";
  759.             }
  760.             else 
  761.             {
  762.                 team = "blue";
  763.             }
  764.         }
  765.         else 
  766.         {
  767.             team = "red";
  768.         }
  769.     }
  770. //    Info_SetValueForKey( userinfo, "characterfile", Info_ValueForKey( botinfo, "aifile" ) );
  771.     Info_SetValueForKey( userinfo, "skill", va( "%5.2f", skill ) );
  772.     Info_SetValueForKey( userinfo, "team", team );
  773.  
  774.     bot = &g_entities[ clientNum ];
  775.     bot->r.svFlags |= SVF_BOT;
  776.     bot->inuse = qtrue;
  777.  
  778.     // register the userinfo
  779.     trap_SetUserinfo( clientNum, userinfo );
  780.  
  781.     // have it connect to the game as a normal client
  782.     if ( ClientConnect( clientNum, qtrue, qtrue ) ) {
  783.         return;
  784.     }
  785.  
  786.     if ( level.gametypeData->teams )
  787.     {
  788.         if (team && Q_stricmp(team, "red") == 0)
  789.         {
  790.             bot->client->sess.team = TEAM_RED;
  791.         }
  792.         else if (team && Q_stricmp(team, "blue") == 0)
  793.         {
  794.             bot->client->sess.team = TEAM_BLUE;
  795.         }
  796.         else
  797.         {
  798.             bot->client->sess.team = PickTeam( -1 );
  799.         }
  800.     }
  801.  
  802.     if( delay == 0 ) {
  803.         ClientBegin( clientNum );
  804.         return;
  805.     }
  806.  
  807.     AddBotToSpawnQueue( clientNum, delay );
  808. }
  809.  
  810.  
  811. /*
  812. ===============
  813. Svcmd_AddBot_f
  814. ===============
  815. */
  816. void Svcmd_AddBot_f( void ) {
  817.     float            skill;
  818.     int                delay;
  819.     char            name[MAX_TOKEN_CHARS];
  820.     char            altname[MAX_TOKEN_CHARS];
  821.     char            string[MAX_TOKEN_CHARS];
  822.     char            team[MAX_TOKEN_CHARS];
  823.  
  824.     // are bots enabled?
  825.     if ( !trap_Cvar_VariableIntegerValue( "bot_enable" ) ) {
  826.         return;
  827.     }
  828.  
  829.     // name
  830.     trap_Argv( 1, name, sizeof( name ) );
  831.     if ( !name[0] ) {
  832.         trap_Printf( "Usage: Addbot <botname> [skill 1-5] [team] [msec delay] [altname]\n" );
  833.         return;
  834.     }
  835.  
  836.     // skill
  837.     trap_Argv( 2, string, sizeof( string ) );
  838.     if ( !string[0] ) {
  839.         skill = 4;
  840.     }
  841.     else {
  842.         skill = atof( string );
  843.     }
  844.  
  845.     // team
  846.     trap_Argv( 3, team, sizeof( team ) );
  847.  
  848.     // delay
  849.     trap_Argv( 4, string, sizeof( string ) );
  850.     if ( !string[0] ) {
  851.         delay = 0;
  852.     }
  853.     else {
  854.         delay = atoi( string );
  855.     }
  856.  
  857.     // alternative name
  858.     trap_Argv( 5, altname, sizeof( altname ) );
  859.  
  860.     G_AddBot( name, skill, team, delay, altname );
  861.  
  862.     // if this was issued during gameplay and we are playing locally,
  863.     // go ahead and load the bot's media immediately
  864.     if ( level.time - level.startTime > 1000 && trap_Cvar_VariableIntegerValue( "cl_running" ) ) 
  865.     {
  866.         trap_SendServerCommand( -1, "loaddeferred\n" );
  867.     }
  868. }
  869.  
  870. /*
  871. ===============
  872. Svcmd_BotList_f
  873. ===============
  874. */
  875. void Svcmd_BotList_f( void ) {
  876.     int i;
  877.     char name[MAX_TOKEN_CHARS];
  878.     char funname[MAX_TOKEN_CHARS];
  879.     char model[MAX_TOKEN_CHARS];
  880.     char personality[MAX_TOKEN_CHARS];
  881.  
  882.     trap_Printf("^1name             model            personality              funname\n");
  883.     for (i = 0; i < g_numBots; i++) {
  884.         strcpy(name, Info_ValueForKey( g_botInfos[i], "name" ));
  885.         if ( !*name ) {
  886.             strcpy(name, "UnnamedPlayer");
  887.         }
  888.         strcpy(funname, Info_ValueForKey( g_botInfos[i], "funname" ));
  889.         if ( !*funname ) {
  890.             strcpy(funname, "");
  891.         }
  892.         strcpy(model, Info_ValueForKey( g_botInfos[i], "model" ));
  893.         if ( !*model ) {
  894.             strcpy(model, "visor/default");
  895.         }
  896.         strcpy(personality, Info_ValueForKey( g_botInfos[i], "personality"));
  897.         if (!*personality ) {
  898.             strcpy(personality, "botfiles/default.jkb");
  899.         }
  900.         trap_Printf(va("%-16s %-16s %-20s %-20s\n", name, model, personality, funname));
  901.     }
  902. }
  903.  
  904.  
  905. /*
  906. ===============
  907. G_SpawnBots
  908. ===============
  909. */
  910. static void G_SpawnBots( char *botList, int baseDelay ) {
  911.     char        *bot;
  912.     char        *p;
  913.     float        skill;
  914.     int            delay;
  915.     char        bots[MAX_INFO_VALUE];
  916.  
  917.     skill = trap_Cvar_VariableValue( "g_botSkill" );
  918.     if( skill < 2 ) 
  919.     {
  920.         trap_Cvar_Set( "g_botSkill", "2" );
  921.         skill = 2;
  922.     }
  923.     else if ( skill > 5 ) 
  924.     {
  925.         trap_Cvar_Set( "g_botSkill", "5" );
  926.         skill = 5;
  927.     }
  928.  
  929.     Q_strncpyz( bots, botList, sizeof(bots) );
  930.     p = &bots[0];
  931.     delay = baseDelay;
  932.     while( *p ) {
  933.         //skip spaces
  934.         while( *p && *p == ' ' ) {
  935.             p++;
  936.         }
  937.         if( !p ) {
  938.             break;
  939.         }
  940.  
  941.         // mark start of bot name
  942.         bot = p;
  943.  
  944.         // skip until space of null
  945.         while( *p && *p != ' ' ) {
  946.             p++;
  947.         }
  948.         if( *p ) {
  949.             *p++ = 0;
  950.         }
  951.  
  952.         // we must add the bot this way, calling G_AddBot directly at this stage
  953.         // does "Bad Things"
  954.         trap_SendConsoleCommand( EXEC_INSERT, va("addbot %s %f free %i\n", bot, skill, delay) );
  955.  
  956.         delay += BOT_BEGIN_DELAY_INCREMENT;
  957.     }
  958. }
  959.  
  960.  
  961. /*
  962. ===============
  963. G_LoadBotsFromFile
  964. ===============
  965. */
  966. static void G_LoadBotsFromFile( char *filename ) {
  967.     int                len;
  968.     fileHandle_t    f;
  969.     char            buf[MAX_BOTS_TEXT];
  970.  
  971.     len = trap_FS_FOpenFile( filename, &f, FS_READ );
  972.     if ( !f ) {
  973.         trap_Printf( va( S_COLOR_RED "file not found: %s\n", filename ) );
  974.         return;
  975.     }
  976.     if ( len >= MAX_BOTS_TEXT ) {
  977.         trap_Printf( va( S_COLOR_RED "file too large: %s is %i, max allowed is %i", filename, len, MAX_BOTS_TEXT ) );
  978.         trap_FS_FCloseFile( f );
  979.         return;
  980.     }
  981.  
  982.     trap_FS_Read( buf, len, f );
  983.     buf[len] = 0;
  984.     trap_FS_FCloseFile( f );
  985.  
  986.     g_numBots += G_ParseInfos( buf, MAX_BOTS - g_numBots, &g_botInfos[g_numBots] );
  987. }
  988.  
  989. /*
  990. ===============
  991. G_LoadBots
  992. ===============
  993. */
  994. static void G_LoadBots( void ) {
  995.     vmCvar_t    botsFile;
  996.     int            numdirs;
  997.     char        filename[128];
  998.     char        dirlist[1024];
  999.     char*        dirptr;
  1000.     int            i;
  1001.     int            dirlen;
  1002.  
  1003.     if ( !trap_Cvar_VariableIntegerValue( "bot_enable" ) ) {
  1004.         return;
  1005.     }
  1006.  
  1007.     g_numBots = 0;
  1008.  
  1009.     trap_Cvar_Register( &botsFile, "g_botsFile", "", CVAR_INIT|CVAR_ROM, 0.0, 0.0 );
  1010.     if( *botsFile.string ) {
  1011.         G_LoadBotsFromFile(botsFile.string);
  1012.     }
  1013.     else {
  1014.         //G_LoadBotsFromFile("scripts/bots.txt");
  1015.         G_LoadBotsFromFile("botfiles/bots.txt");
  1016.     }
  1017.  
  1018.     // get all bots from .bot files
  1019.     numdirs = trap_FS_GetFileList("scripts", ".bot", dirlist, 1024 );
  1020.     dirptr  = dirlist;
  1021.     for (i = 0; i < numdirs; i++, dirptr += dirlen+1) {
  1022.         dirlen = strlen(dirptr);
  1023.         strcpy(filename, "scripts/");
  1024.         strcat(filename, dirptr);
  1025.         G_LoadBotsFromFile(filename);
  1026.     }
  1027.     trap_Printf( va( "%i bots parsed\n", g_numBots ) );
  1028. }
  1029.  
  1030.  
  1031.  
  1032. /*
  1033. ===============
  1034. G_GetBotInfoByNumber
  1035. ===============
  1036. */
  1037. char *G_GetBotInfoByNumber( int num ) {
  1038.     if( num < 0 || num >= g_numBots ) {
  1039.         trap_Printf( va( S_COLOR_RED "Invalid bot number: %i\n", num ) );
  1040.         return NULL;
  1041.     }
  1042.     return g_botInfos[num];
  1043. }
  1044.  
  1045.  
  1046. /*
  1047. ===============
  1048. G_GetBotInfoByName
  1049. ===============
  1050. */
  1051. char *G_GetBotInfoByName( const char *name ) {
  1052.     int        n;
  1053.     char    *value;
  1054.  
  1055.     for ( n = 0; n < g_numBots ; n++ ) {
  1056.         value = Info_ValueForKey( g_botInfos[n], "name" );
  1057.         if ( !Q_stricmp( value, name ) ) {
  1058.             return g_botInfos[n];
  1059.         }
  1060.     }
  1061.  
  1062.     return NULL;
  1063. }
  1064.  
  1065. //rww - pd
  1066. void LoadPath_ThisLevel(void);
  1067. //end rww
  1068.  
  1069. /*
  1070. ===============
  1071. G_InitBots
  1072. ===============
  1073. */
  1074. void G_InitBots( qboolean restart ) 
  1075. {
  1076.     G_LoadBots();
  1077.  
  1078.     trap_Cvar_Register( &bot_minplayers, "bot_minplayers", "0", CVAR_SERVERINFO, 0.0, 0.0 );
  1079.  
  1080.     //rww - new bot route stuff
  1081.     LoadPath_ThisLevel();
  1082. }
  1083.