codemp/game/g_cmds.c

Go to the documentation of this file.
00001 // Copyright (C) 1999-2000 Id Software, Inc.
00002 //
00003 #include "g_local.h"
00004 #include "bg_saga.h"
00005 
00006 #include "../../ui/menudef.h"                   // for the voice chats
00007 
00008 //rww - for getting bot commands...
00009 int AcceptBotCommand(char *cmd, gentity_t *pl);
00010 //end rww
00011 
00012 #include "../namespace_begin.h"
00013 void WP_SetSaber( int entNum, saberInfo_t *sabers, int saberNum, const char *saberName );
00014 #include "../namespace_end.h"
00015 
00016 void Cmd_NPC_f( gentity_t *ent );
00017 void SetTeamQuick(gentity_t *ent, int team, qboolean doBegin);
00018 
00019 /*
00020 ==================
00021 DeathmatchScoreboardMessage
00022 
00023 ==================
00024 */
00025 void DeathmatchScoreboardMessage( gentity_t *ent ) {
00026         char            entry[1024];
00027         char            string[1400];
00028         int                     stringlength;
00029         int                     i, j;
00030         gclient_t       *cl;
00031         int                     numSorted, scoreFlags, accuracy, perfect;
00032 
00033         // send the latest information on all clients
00034         string[0] = 0;
00035         stringlength = 0;
00036         scoreFlags = 0;
00037 
00038         numSorted = level.numConnectedClients;
00039         
00040         if (numSorted > MAX_CLIENT_SCORE_SEND)
00041         {
00042                 numSorted = MAX_CLIENT_SCORE_SEND;
00043         }
00044 
00045         for (i=0 ; i < numSorted ; i++) {
00046                 int             ping;
00047 
00048                 cl = &level.clients[level.sortedClients[i]];
00049 
00050                 if ( cl->pers.connected == CON_CONNECTING ) {
00051                         ping = -1;
00052                 } else {
00053                         ping = cl->ps.ping < 999 ? cl->ps.ping : 999;
00054                 }
00055 
00056                 if( cl->accuracy_shots ) {
00057                         accuracy = cl->accuracy_hits * 100 / cl->accuracy_shots;
00058                 }
00059                 else {
00060                         accuracy = 0;
00061                 }
00062                 perfect = ( cl->ps.persistant[PERS_RANK] == 0 && cl->ps.persistant[PERS_KILLED] == 0 ) ? 1 : 0;
00063 
00064                 Com_sprintf (entry, sizeof(entry),
00065                         " %i %i %i %i %i %i %i %i %i %i %i %i %i %i", level.sortedClients[i],
00066                         cl->ps.persistant[PERS_SCORE], ping, (level.time - cl->pers.enterTime)/60000,
00067                         scoreFlags, g_entities[level.sortedClients[i]].s.powerups, accuracy, 
00068                         cl->ps.persistant[PERS_IMPRESSIVE_COUNT],
00069                         cl->ps.persistant[PERS_EXCELLENT_COUNT],
00070                         cl->ps.persistant[PERS_GAUNTLET_FRAG_COUNT], 
00071                         cl->ps.persistant[PERS_DEFEND_COUNT], 
00072                         cl->ps.persistant[PERS_ASSIST_COUNT], 
00073                         perfect,
00074                         cl->ps.persistant[PERS_CAPTURES]);
00075                 j = strlen(entry);
00076                 if (stringlength + j > 1022)
00077                         break;
00078                 strcpy (string + stringlength, entry);
00079                 stringlength += j;
00080         }
00081 
00082         //still want to know the total # of clients
00083         i = level.numConnectedClients;
00084 
00085         trap_SendServerCommand( ent-g_entities, va("scores %i %i %i%s", i, 
00086                 level.teamScores[TEAM_RED], level.teamScores[TEAM_BLUE],
00087                 string ) );
00088 }
00089 
00090 
00091 /*
00092 ==================
00093 Cmd_Score_f
00094 
00095 Request current scoreboard information
00096 ==================
00097 */
00098 void Cmd_Score_f( gentity_t *ent ) {
00099         DeathmatchScoreboardMessage( ent );
00100 }
00101 
00102 
00103 
00104 /*
00105 ==================
00106 CheatsOk
00107 ==================
00108 */
00109 qboolean        CheatsOk( gentity_t *ent ) {
00110         if ( !g_cheats.integer ) {
00111                 trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", G_GetStringEdString("MP_SVGAME", "NOCHEATS")));
00112                 return qfalse;
00113         }
00114         if ( ent->health <= 0 ) {
00115                 trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", G_GetStringEdString("MP_SVGAME", "MUSTBEALIVE")));
00116                 return qfalse;
00117         }
00118         return qtrue;
00119 }
00120 
00121 
00122 /*
00123 ==================
00124 ConcatArgs
00125 ==================
00126 */
00127 char    *ConcatArgs( int start ) {
00128         int             i, c, tlen;
00129         static char     line[MAX_STRING_CHARS];
00130         int             len;
00131         char    arg[MAX_STRING_CHARS];
00132 
00133         len = 0;
00134         c = trap_Argc();
00135         for ( i = start ; i < c ; i++ ) {
00136                 trap_Argv( i, arg, sizeof( arg ) );
00137                 tlen = strlen( arg );
00138                 if ( len + tlen >= MAX_STRING_CHARS - 1 ) {
00139                         break;
00140                 }
00141                 memcpy( line + len, arg, tlen );
00142                 len += tlen;
00143                 if ( i != c - 1 ) {
00144                         line[len] = ' ';
00145                         len++;
00146                 }
00147         }
00148 
00149         line[len] = 0;
00150 
00151         return line;
00152 }
00153 
00154 /*
00155 ==================
00156 SanitizeString
00157 
00158 Remove case and control characters
00159 ==================
00160 */
00161 void SanitizeString( char *in, char *out ) {
00162         while ( *in ) {
00163                 if ( *in == 27 ) {
00164                         in += 2;                // skip color code
00165                         continue;
00166                 }
00167                 if ( *in < 32 ) {
00168                         in++;
00169                         continue;
00170                 }
00171                 *out++ = tolower( (unsigned char) *in++ );
00172         }
00173 
00174         *out = 0;
00175 }
00176 
00177 /*
00178 ==================
00179 ClientNumberFromString
00180 
00181 Returns a player number for either a number or name string
00182 Returns -1 if invalid
00183 ==================
00184 */
00185 int ClientNumberFromString( gentity_t *to, char *s ) {
00186         gclient_t       *cl;
00187         int                     idnum;
00188         char            s2[MAX_STRING_CHARS];
00189         char            n2[MAX_STRING_CHARS];
00190 
00191         // numeric values are just slot numbers
00192         if (s[0] >= '0' && s[0] <= '9') {
00193                 idnum = atoi( s );
00194                 if ( idnum < 0 || idnum >= level.maxclients ) {
00195                         trap_SendServerCommand( to-g_entities, va("print \"Bad client slot: %i\n\"", idnum));
00196                         return -1;
00197                 }
00198 
00199                 cl = &level.clients[idnum];
00200                 if ( cl->pers.connected != CON_CONNECTED ) {
00201                         trap_SendServerCommand( to-g_entities, va("print \"Client %i is not active\n\"", idnum));
00202                         return -1;
00203                 }
00204                 return idnum;
00205         }
00206 
00207         // check for a name match
00208         SanitizeString( s, s2 );
00209         for ( idnum=0,cl=level.clients ; idnum < level.maxclients ; idnum++,cl++ ) {
00210                 if ( cl->pers.connected != CON_CONNECTED ) {
00211                         continue;
00212                 }
00213                 SanitizeString( cl->pers.netname, n2 );
00214                 if ( !strcmp( n2, s2 ) ) {
00215                         return idnum;
00216                 }
00217         }
00218 
00219         trap_SendServerCommand( to-g_entities, va("print \"User %s is not on the server\n\"", s));
00220         return -1;
00221 }
00222 
00223 /*
00224 ==================
00225 Cmd_Give_f
00226 
00227 Give items to a client
00228 ==================
00229 */
00230 void Cmd_Give_f (gentity_t *cmdent, int baseArg)
00231 {
00232         char            name[MAX_TOKEN_CHARS];
00233         gentity_t       *ent;
00234         gitem_t         *it;
00235         int                     i;
00236         qboolean        give_all;
00237         gentity_t               *it_ent;
00238         trace_t         trace;
00239         char            arg[MAX_TOKEN_CHARS];
00240 
00241         if ( !CheatsOk( cmdent ) ) {
00242                 return;
00243         }
00244 
00245         if (baseArg)
00246         {
00247                 char otherindex[MAX_TOKEN_CHARS];
00248 
00249                 trap_Argv( 1, otherindex, sizeof( otherindex ) );
00250 
00251                 if (!otherindex[0])
00252                 {
00253                         Com_Printf("giveother requires that the second argument be a client index number.\n");
00254                         return;
00255                 }
00256 
00257                 i = atoi(otherindex);
00258 
00259                 if (i < 0 || i >= MAX_CLIENTS)
00260                 {
00261                         Com_Printf("%i is not a client index\n", i);
00262                         return;
00263                 }
00264 
00265                 ent = &g_entities[i];
00266 
00267                 if (!ent->inuse || !ent->client)
00268                 {
00269                         Com_Printf("%i is not an active client\n", i);
00270                         return;
00271                 }
00272         }
00273         else
00274         {
00275                 ent = cmdent;
00276         }
00277 
00278         trap_Argv( 1+baseArg, name, sizeof( name ) );
00279 
00280         if (Q_stricmp(name, "all") == 0)
00281                 give_all = qtrue;
00282         else
00283                 give_all = qfalse;
00284 
00285         if (give_all)
00286         {
00287                 i = 0;
00288                 while (i < HI_NUM_HOLDABLE)
00289                 {
00290                         ent->client->ps.stats[STAT_HOLDABLE_ITEMS] |= (1 << i);
00291                         i++;
00292                 }
00293                 i = 0;
00294         }
00295 
00296         if (give_all || Q_stricmp( name, "health") == 0)
00297         {
00298                 if (trap_Argc() == 3+baseArg) {
00299                         trap_Argv( 2+baseArg, arg, sizeof( arg ) );
00300                         ent->health = atoi(arg);
00301                         if (ent->health > ent->client->ps.stats[STAT_MAX_HEALTH]) {
00302                                 ent->health = ent->client->ps.stats[STAT_MAX_HEALTH];
00303                         }
00304                 }
00305                 else {
00306                         ent->health = ent->client->ps.stats[STAT_MAX_HEALTH];
00307                 }
00308                 if (!give_all)
00309                         return;
00310         }
00311 
00312         if (give_all || Q_stricmp(name, "weapons") == 0)
00313         {
00314                 ent->client->ps.stats[STAT_WEAPONS] = (1 << (LAST_USEABLE_WEAPON+1))  - ( 1 << WP_NONE );
00315                 if (!give_all)
00316                         return;
00317         }
00318         
00319         if ( !give_all && Q_stricmp(name, "weaponnum") == 0 )
00320         {
00321                 trap_Argv( 2+baseArg, arg, sizeof( arg ) );
00322                 ent->client->ps.stats[STAT_WEAPONS] |= (1 << atoi(arg));
00323                 return;
00324         }
00325 
00326         if (give_all || Q_stricmp(name, "ammo") == 0)
00327         {
00328                 int num = 999;
00329                 if (trap_Argc() == 3+baseArg) {
00330                         trap_Argv( 2+baseArg, arg, sizeof( arg ) );
00331                         num = atoi(arg);
00332                 }
00333                 for ( i = 0 ; i < MAX_WEAPONS ; i++ ) {
00334                         ent->client->ps.ammo[i] = num;
00335                 }
00336                 if (!give_all)
00337                         return;
00338         }
00339 
00340         if (give_all || Q_stricmp(name, "armor") == 0)
00341         {
00342                 if (trap_Argc() == 3+baseArg) {
00343                         trap_Argv( 2+baseArg, arg, sizeof( arg ) );
00344                         ent->client->ps.stats[STAT_ARMOR] = atoi(arg);
00345                 } else {
00346                         ent->client->ps.stats[STAT_ARMOR] = ent->client->ps.stats[STAT_MAX_HEALTH];
00347                 }
00348 
00349                 if (!give_all)
00350                         return;
00351         }
00352 
00353         if (Q_stricmp(name, "excellent") == 0) {
00354                 ent->client->ps.persistant[PERS_EXCELLENT_COUNT]++;
00355                 return;
00356         }
00357         if (Q_stricmp(name, "impressive") == 0) {
00358                 ent->client->ps.persistant[PERS_IMPRESSIVE_COUNT]++;
00359                 return;
00360         }
00361         if (Q_stricmp(name, "gauntletaward") == 0) {
00362                 ent->client->ps.persistant[PERS_GAUNTLET_FRAG_COUNT]++;
00363                 return;
00364         }
00365         if (Q_stricmp(name, "defend") == 0) {
00366                 ent->client->ps.persistant[PERS_DEFEND_COUNT]++;
00367                 return;
00368         }
00369         if (Q_stricmp(name, "assist") == 0) {
00370                 ent->client->ps.persistant[PERS_ASSIST_COUNT]++;
00371                 return;
00372         }
00373 
00374         // spawn a specific item right on the player
00375         if ( !give_all ) {
00376                 it = BG_FindItem (name);
00377                 if (!it) {
00378                         return;
00379                 }
00380 
00381                 it_ent = G_Spawn();
00382                 VectorCopy( ent->r.currentOrigin, it_ent->s.origin );
00383                 it_ent->classname = it->classname;
00384                 G_SpawnItem (it_ent, it);
00385                 FinishSpawningItem(it_ent );
00386                 memset( &trace, 0, sizeof( trace ) );
00387                 Touch_Item (it_ent, ent, &trace);
00388                 if (it_ent->inuse) {
00389                         G_FreeEntity( it_ent );
00390                 }
00391         }
00392 }
00393 
00394 /*
00395 ==================
00396 Cmd_God_f
00397 
00398 Sets client to godmode
00399 
00400 argv(0) god
00401 ==================
00402 */
00403 void Cmd_God_f (gentity_t *ent)
00404 {
00405         char    *msg;
00406 
00407         if ( !CheatsOk( ent ) ) {
00408                 return;
00409         }
00410 
00411         ent->flags ^= FL_GODMODE;
00412         if (!(ent->flags & FL_GODMODE) )
00413                 msg = "godmode OFF\n";
00414         else
00415                 msg = "godmode ON\n";
00416 
00417         trap_SendServerCommand( ent-g_entities, va("print \"%s\"", msg));
00418 }
00419 
00420 
00421 /*
00422 ==================
00423 Cmd_Notarget_f
00424 
00425 Sets client to notarget
00426 
00427 argv(0) notarget
00428 ==================
00429 */
00430 void Cmd_Notarget_f( gentity_t *ent ) {
00431         char    *msg;
00432 
00433         if ( !CheatsOk( ent ) ) {
00434                 return;
00435         }
00436 
00437         ent->flags ^= FL_NOTARGET;
00438         if (!(ent->flags & FL_NOTARGET) )
00439                 msg = "notarget OFF\n";
00440         else
00441                 msg = "notarget ON\n";
00442 
00443         trap_SendServerCommand( ent-g_entities, va("print \"%s\"", msg));
00444 }
00445 
00446 
00447 /*
00448 ==================
00449 Cmd_Noclip_f
00450 
00451 argv(0) noclip
00452 ==================
00453 */
00454 void Cmd_Noclip_f( gentity_t *ent ) {
00455         char    *msg;
00456 
00457         if ( !CheatsOk( ent ) ) {
00458                 return;
00459         }
00460 
00461         if ( ent->client->noclip ) {
00462                 msg = "noclip OFF\n";
00463         } else {
00464                 msg = "noclip ON\n";
00465         }
00466         ent->client->noclip = !ent->client->noclip;
00467 
00468         trap_SendServerCommand( ent-g_entities, va("print \"%s\"", msg));
00469 }
00470 
00471 
00472 /*
00473 ==================
00474 Cmd_LevelShot_f
00475 
00476 This is just to help generate the level pictures
00477 for the menus.  It goes to the intermission immediately
00478 and sends over a command to the client to resize the view,
00479 hide the scoreboard, and take a special screenshot
00480 ==================
00481 */
00482 void Cmd_LevelShot_f( gentity_t *ent ) {
00483         if ( !CheatsOk( ent ) ) {
00484                 return;
00485         }
00486 
00487         // doesn't work in single player
00488         if ( g_gametype.integer != 0 ) {
00489                 trap_SendServerCommand( ent-g_entities, 
00490                         "print \"Must be in g_gametype 0 for levelshot\n\"" );
00491                 return;
00492         }
00493 
00494         BeginIntermission();
00495         trap_SendServerCommand( ent-g_entities, "clientLevelShot" );
00496 }
00497 
00498 
00499 /*
00500 ==================
00501 Cmd_TeamTask_f
00502 
00503 From TA.
00504 ==================
00505 */
00506 void Cmd_TeamTask_f( gentity_t *ent ) {
00507         char userinfo[MAX_INFO_STRING];
00508         char            arg[MAX_TOKEN_CHARS];
00509         int task;
00510         int client = ent->client - level.clients;
00511 
00512         if ( trap_Argc() != 2 ) {
00513                 return;
00514         }
00515         trap_Argv( 1, arg, sizeof( arg ) );
00516         task = atoi( arg );
00517 
00518         trap_GetUserinfo(client, userinfo, sizeof(userinfo));
00519         Info_SetValueForKey(userinfo, "teamtask", va("%d", task));
00520         trap_SetUserinfo(client, userinfo);
00521         ClientUserinfoChanged(client);
00522 }
00523 
00524 
00525 
00526 /*
00527 =================
00528 Cmd_Kill_f
00529 =================
00530 */
00531 void Cmd_Kill_f( gentity_t *ent ) {
00532         if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) {
00533                 return;
00534         }
00535         if (ent->health <= 0) {
00536                 return;
00537         }
00538 
00539         if ((g_gametype.integer == GT_DUEL || g_gametype.integer == GT_POWERDUEL) &&
00540                 level.numPlayingClients > 1 && !level.warmupTime)
00541         {
00542                 if (!g_allowDuelSuicide.integer)
00543                 {
00544                         trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", G_GetStringEdString("MP_SVGAME", "ATTEMPTDUELKILL")) );
00545                         return;
00546                 }
00547         }
00548 
00549         ent->flags &= ~FL_GODMODE;
00550         ent->client->ps.stats[STAT_HEALTH] = ent->health = -999;
00551         player_die (ent, ent, ent, 100000, MOD_SUICIDE);
00552 }
00553 
00554 gentity_t *G_GetDuelWinner(gclient_t *client)
00555 {
00556         gclient_t *wCl;
00557         int i;
00558 
00559         for ( i = 0 ; i < level.maxclients ; i++ ) {
00560                 wCl = &level.clients[i];
00561                 
00562                 if (wCl && wCl != client && /*wCl->ps.clientNum != client->ps.clientNum &&*/
00563                         wCl->pers.connected == CON_CONNECTED && wCl->sess.sessionTeam != TEAM_SPECTATOR)
00564                 {
00565                         return &g_entities[wCl->ps.clientNum];
00566                 }
00567         }
00568 
00569         return NULL;
00570 }
00571 
00572 /*
00573 =================
00574 BroadCastTeamChange
00575 
00576 Let everyone know about a team change
00577 =================
00578 */
00579 void BroadcastTeamChange( gclient_t *client, int oldTeam )
00580 {
00581         client->ps.fd.forceDoInit = 1; //every time we change teams make sure our force powers are set right
00582 
00583         if (g_gametype.integer == GT_SIEGE)
00584         { //don't announce these things in siege
00585                 return;
00586         }
00587 
00588         if ( client->sess.sessionTeam == TEAM_RED ) {
00589                 trap_SendServerCommand( -1, va("cp \"%s" S_COLOR_WHITE " %s\n\"",
00590                         client->pers.netname, G_GetStringEdString("MP_SVGAME", "JOINEDTHEREDTEAM")) );
00591         } else if ( client->sess.sessionTeam == TEAM_BLUE ) {
00592                 trap_SendServerCommand( -1, va("cp \"%s" S_COLOR_WHITE " %s\n\"",
00593                 client->pers.netname, G_GetStringEdString("MP_SVGAME", "JOINEDTHEBLUETEAM")));
00594         } else if ( client->sess.sessionTeam == TEAM_SPECTATOR && oldTeam != TEAM_SPECTATOR ) {
00595                 trap_SendServerCommand( -1, va("cp \"%s" S_COLOR_WHITE " %s\n\"",
00596                 client->pers.netname, G_GetStringEdString("MP_SVGAME", "JOINEDTHESPECTATORS")));
00597         } else if ( client->sess.sessionTeam == TEAM_FREE ) {
00598                 if (g_gametype.integer == GT_DUEL || g_gametype.integer == GT_POWERDUEL)
00599                 {
00600                         /*
00601                         gentity_t *currentWinner = G_GetDuelWinner(client);
00602 
00603                         if (currentWinner && currentWinner->client)
00604                         {
00605                                 trap_SendServerCommand( -1, va("cp \"%s" S_COLOR_WHITE " %s %s\n\"",
00606                                 currentWinner->client->pers.netname, G_GetStringEdString("MP_SVGAME", "VERSUS"), client->pers.netname));
00607                         }
00608                         else
00609                         {
00610                                 trap_SendServerCommand( -1, va("cp \"%s" S_COLOR_WHITE " %s\n\"",
00611                                 client->pers.netname, G_GetStringEdString("MP_SVGAME", "JOINEDTHEBATTLE")));
00612                         }
00613                         */
00614                         //NOTE: Just doing a vs. once it counts two players up
00615                 }
00616                 else
00617                 {
00618                         trap_SendServerCommand( -1, va("cp \"%s" S_COLOR_WHITE " %s\n\"",
00619                         client->pers.netname, G_GetStringEdString("MP_SVGAME", "JOINEDTHEBATTLE")));
00620                 }
00621         }
00622 
00623         G_LogPrintf ( "setteam:  %i %s %s\n",
00624                                   client - &level.clients[0],
00625                                   TeamName ( oldTeam ),
00626                                   TeamName ( client->sess.sessionTeam ) );
00627 }
00628 
00629 qboolean G_PowerDuelCheckFail(gentity_t *ent)
00630 {
00631         int                     loners = 0;
00632         int                     doubles = 0;
00633 
00634         if (!ent->client || ent->client->sess.duelTeam == DUELTEAM_FREE)
00635         {
00636                 return qtrue;
00637         }
00638 
00639         G_PowerDuelCount(&loners, &doubles, qfalse);
00640 
00641         if (ent->client->sess.duelTeam == DUELTEAM_LONE && loners >= 1)
00642         {
00643                 return qtrue;
00644         }
00645 
00646         if (ent->client->sess.duelTeam == DUELTEAM_DOUBLE && doubles >= 2)
00647         {
00648                 return qtrue;
00649         }
00650 
00651         return qfalse;
00652 }
00653 
00654 /*
00655 =================
00656 SetTeam
00657 =================
00658 */
00659 qboolean g_dontPenalizeTeam = qfalse;
00660 qboolean g_preventTeamBegin = qfalse;
00661 void SetTeam( gentity_t *ent, char *s ) {
00662         int                                     team, oldTeam;
00663         gclient_t                       *client;
00664         int                                     clientNum;
00665         spectatorState_t        specState;
00666         int                                     specClient;
00667         int                                     teamLeader;
00668 
00669         //
00670         // see what change is requested
00671         //
00672         client = ent->client;
00673 
00674         clientNum = client - level.clients;
00675         specClient = 0;
00676         specState = SPECTATOR_NOT;
00677         if ( !Q_stricmp( s, "scoreboard" ) || !Q_stricmp( s, "score" )  ) {
00678                 team = TEAM_SPECTATOR;
00679                 specState = SPECTATOR_SCOREBOARD;
00680         } else if ( !Q_stricmp( s, "follow1" ) ) {
00681                 team = TEAM_SPECTATOR;
00682                 specState = SPECTATOR_FOLLOW;
00683                 specClient = -1;
00684         } else if ( !Q_stricmp( s, "follow2" ) ) {
00685                 team = TEAM_SPECTATOR;
00686                 specState = SPECTATOR_FOLLOW;
00687                 specClient = -2;
00688         } else if ( !Q_stricmp( s, "spectator" ) || !Q_stricmp( s, "s" ) ) {
00689                 team = TEAM_SPECTATOR;
00690                 specState = SPECTATOR_FREE;
00691         } else if ( g_gametype.integer >= GT_TEAM ) {
00692                 // if running a team game, assign player to one of the teams
00693                 specState = SPECTATOR_NOT;
00694                 if ( !Q_stricmp( s, "red" ) || !Q_stricmp( s, "r" ) ) {
00695                         team = TEAM_RED;
00696                 } else if ( !Q_stricmp( s, "blue" ) || !Q_stricmp( s, "b" ) ) {
00697                         team = TEAM_BLUE;
00698                 } else {
00699                         // pick the team with the least number of players
00700                         //For now, don't do this. The legalize function will set powers properly now.
00701                         /*
00702                         if (g_forceBasedTeams.integer)
00703                         {
00704                                 if (ent->client->ps.fd.forceSide == FORCE_LIGHTSIDE)
00705                                 {
00706                                         team = TEAM_BLUE;
00707                                 }
00708                                 else
00709                                 {
00710                                         team = TEAM_RED;
00711                                 }
00712                         }
00713                         else
00714                         {
00715                         */
00716                                 team = PickTeam( clientNum );
00717                         //}
00718                 }
00719 
00720                 if ( g_teamForceBalance.integer && !g_trueJedi.integer ) {
00721                         int             counts[TEAM_NUM_TEAMS];
00722 
00723                         counts[TEAM_BLUE] = TeamCount( ent->client->ps.clientNum, TEAM_BLUE );
00724                         counts[TEAM_RED] = TeamCount( ent->client->ps.clientNum, TEAM_RED );
00725 
00726                         // We allow a spread of two
00727                         if ( team == TEAM_RED && counts[TEAM_RED] - counts[TEAM_BLUE] > 1 ) {
00728                                 //For now, don't do this. The legalize function will set powers properly now.
00729                                 /*
00730                                 if (g_forceBasedTeams.integer && ent->client->ps.fd.forceSide == FORCE_DARKSIDE)
00731                                 {
00732                                         trap_SendServerCommand( ent->client->ps.clientNum, 
00733                                                 va("print \"%s\n\"", G_GetStringEdString("MP_SVGAME", "TOOMANYRED_SWITCH")) );
00734                                 }
00735                                 else
00736                                 */
00737                                 {
00738                                         trap_SendServerCommand( ent->client->ps.clientNum, 
00739                                                 va("print \"%s\n\"", G_GetStringEdString("MP_SVGAME", "TOOMANYRED")) );
00740                                 }
00741                                 return; // ignore the request
00742                         }
00743                         if ( team == TEAM_BLUE && counts[TEAM_BLUE] - counts[TEAM_RED] > 1 ) {
00744                                 //For now, don't do this. The legalize function will set powers properly now.
00745                                 /*
00746                                 if (g_forceBasedTeams.integer && ent->client->ps.fd.forceSide == FORCE_LIGHTSIDE)
00747                                 {
00748                                         trap_SendServerCommand( ent->client->ps.clientNum, 
00749                                                 va("print \"%s\n\"", G_GetStringEdString("MP_SVGAME", "TOOMANYBLUE_SWITCH")) );
00750                                 }
00751                                 else
00752                                 */
00753                                 {
00754                                         trap_SendServerCommand( ent->client->ps.clientNum, 
00755                                                 va("print \"%s\n\"", G_GetStringEdString("MP_SVGAME", "TOOMANYBLUE")) );
00756                                 }
00757                                 return; // ignore the request
00758                         }
00759 
00760                         // It's ok, the team we are switching to has less or same number of players
00761                 }
00762 
00763                 //For now, don't do this. The legalize function will set powers properly now.
00764                 /*
00765                 if (g_forceBasedTeams.integer)
00766                 {
00767                         if (team == TEAM_BLUE && ent->client->ps.fd.forceSide != FORCE_LIGHTSIDE)
00768                         {
00769                                 trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", G_GetStringEdString("MP_SVGAME", "MUSTBELIGHT")) );
00770                                 return;
00771                         }
00772                         if (team == TEAM_RED && ent->client->ps.fd.forceSide != FORCE_DARKSIDE)
00773                         {
00774                                 trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", G_GetStringEdString("MP_SVGAME", "MUSTBEDARK")) );
00775                                 return;
00776                         }
00777                 }
00778                 */
00779 
00780         } else {
00781                 // force them to spectators if there aren't any spots free
00782                 team = TEAM_FREE;
00783         }
00784 
00785         if (g_gametype.integer == GT_SIEGE)
00786         {
00787                 if (client->tempSpectate >= level.time &&
00788                         team == TEAM_SPECTATOR)
00789                 { //sorry, can't do that.
00790                         return;
00791                 }
00792 
00793                 client->sess.siegeDesiredTeam = team;
00794                 //oh well, just let them go.
00795                 /*
00796                 if (team != TEAM_SPECTATOR)
00797                 { //can't switch to anything in siege unless you want to switch to being a fulltime spectator
00798                         //fill them in on their objectives for this team now
00799                         trap_SendServerCommand(ent-g_entities, va("sb %i", client->sess.siegeDesiredTeam));
00800 
00801                         trap_SendServerCommand( ent-g_entities, va("print \"You will be on the selected team the next time the round begins.\n\"") );
00802                         return;
00803                 }
00804                 */
00805                 if (client->sess.sessionTeam != TEAM_SPECTATOR &&
00806                         team != TEAM_SPECTATOR)
00807                 { //not a spectator now, and not switching to spec, so you have to wait til you die.
00808                         //trap_SendServerCommand( ent-g_entities, va("print \"You will be on the selected team the next time you respawn.\n\"") );
00809                         qboolean doBegin;
00810                         if (ent->client->tempSpectate >= level.time)
00811                         {
00812                                 doBegin = qfalse;
00813                         }
00814                         else
00815                         {
00816                                 doBegin = qtrue;
00817                         }
00818 
00819                         if (doBegin)
00820                         {
00821                                 // Kill them so they automatically respawn in the team they wanted.
00822                                 if (ent->health > 0)
00823                                 {
00824                                         ent->flags &= ~FL_GODMODE;
00825                                         ent->client->ps.stats[STAT_HEALTH] = ent->health = 0;
00826                                         player_die( ent, ent, ent, 100000, MOD_TEAM_CHANGE ); 
00827                                 }
00828                         }
00829 
00830                         if (ent->client->sess.sessionTeam != ent->client->sess.siegeDesiredTeam)
00831                         {
00832                                 SetTeamQuick(ent, ent->client->sess.siegeDesiredTeam, qfalse);
00833                         }
00834 
00835                         return;
00836                 }
00837         }
00838 
00839         // override decision if limiting the players
00840         if ( (g_gametype.integer == GT_DUEL)
00841                 && level.numNonSpectatorClients >= 2 )
00842         {
00843                 team = TEAM_SPECTATOR;
00844         }
00845         else if ( (g_gametype.integer == GT_POWERDUEL)
00846                 && (level.numPlayingClients >= 3 || G_PowerDuelCheckFail(ent)) )
00847         {
00848                 team = TEAM_SPECTATOR;
00849         }
00850         else if ( g_maxGameClients.integer > 0 && 
00851                 level.numNonSpectatorClients >= g_maxGameClients.integer )
00852         {
00853                 team = TEAM_SPECTATOR;
00854         }
00855 
00856         //
00857         // decide if we will allow the change
00858         //
00859         oldTeam = client->sess.sessionTeam;
00860         if ( team == oldTeam && team != TEAM_SPECTATOR ) {
00861                 return;
00862         }
00863 
00864         //
00865         // execute the team change
00866         //
00867 
00868         //If it's siege then show the mission briefing for the team you just joined.
00869 //      if (g_gametype.integer == GT_SIEGE && team != TEAM_SPECTATOR)
00870 //      {
00871 //              trap_SendServerCommand(clientNum, va("sb %i", team));
00872 //      }
00873 
00874         // if the player was dead leave the body
00875         if ( client->ps.stats[STAT_HEALTH] <= 0 && client->sess.sessionTeam != TEAM_SPECTATOR ) {
00876                 MaintainBodyQueue(ent);
00877         }
00878 
00879         // he starts at 'base'
00880         client->pers.teamState.state = TEAM_BEGIN;
00881         if ( oldTeam != TEAM_SPECTATOR ) {
00882                 // Kill him (makes sure he loses flags, etc)
00883                 ent->flags &= ~FL_GODMODE;
00884                 ent->client->ps.stats[STAT_HEALTH] = ent->health = 0;
00885                 g_dontPenalizeTeam = qtrue;
00886                 player_die (ent, ent, ent, 100000, MOD_SUICIDE);
00887                 g_dontPenalizeTeam = qfalse;
00888 
00889         }
00890         // they go to the end of the line for tournements
00891         if ( team == TEAM_SPECTATOR ) {
00892                 if ( (g_gametype.integer != GT_DUEL) || (oldTeam != TEAM_SPECTATOR) )   {//so you don't get dropped to the bottom of the queue for changing skins, etc.
00893                         client->sess.spectatorTime = level.time;
00894                 }
00895         }
00896 
00897         client->sess.sessionTeam = team;
00898         client->sess.spectatorState = specState;
00899         client->sess.spectatorClient = specClient;
00900 
00901         client->sess.teamLeader = qfalse;
00902         if ( team == TEAM_RED || team == TEAM_BLUE ) {
00903                 teamLeader = TeamLeader( team );
00904                 // if there is no team leader or the team leader is a bot and this client is not a bot
00905                 if ( teamLeader == -1 || ( !(g_entities[clientNum].r.svFlags & SVF_BOT) && (g_entities[teamLeader].r.svFlags & SVF_BOT) ) ) {
00906                         //SetLeader( team, clientNum );
00907                 }
00908         }
00909         // make sure there is a team leader on the team the player came from
00910         if ( oldTeam == TEAM_RED || oldTeam == TEAM_BLUE ) {
00911                 CheckTeamLeader( oldTeam );
00912         }
00913 
00914         BroadcastTeamChange( client, oldTeam );
00915 
00916         //make a disappearing effect where they were before teleporting them to the appropriate spawn point,
00917         //if we were not on the spec team
00918         if (oldTeam != TEAM_SPECTATOR)
00919         {
00920                 gentity_t *tent = G_TempEntity( client->ps.origin, EV_PLAYER_TELEPORT_OUT );
00921                 tent->s.clientNum = clientNum;
00922         }
00923 
00924         // get and distribute relevent paramters
00925         ClientUserinfoChanged( clientNum );
00926 
00927         if (!g_preventTeamBegin)
00928         {
00929                 ClientBegin( clientNum, qfalse );
00930         }
00931 }
00932 
00933 /*
00934 =================
00935 StopFollowing
00936 
00937 If the client being followed leaves the game, or you just want to drop
00938 to free floating spectator mode
00939 =================
00940 */
00941 void StopFollowing( gentity_t *ent ) {
00942         ent->client->ps.persistant[ PERS_TEAM ] = TEAM_SPECTATOR;       
00943         ent->client->sess.sessionTeam = TEAM_SPECTATOR; 
00944         ent->client->sess.spectatorState = SPECTATOR_FREE;
00945         ent->client->ps.pm_flags &= ~PMF_FOLLOW;
00946         ent->r.svFlags &= ~SVF_BOT;
00947         ent->client->ps.clientNum = ent - g_entities;
00948         ent->client->ps.weapon = WP_NONE;
00949         ent->client->ps.m_iVehicleNum = 0;
00950         ent->client->ps.viewangles[ROLL] = 0.0f;
00951         ent->client->ps.forceHandExtend = HANDEXTEND_NONE;
00952         ent->client->ps.forceHandExtendTime = 0;
00953         ent->client->ps.zoomMode = 0;
00954         ent->client->ps.zoomLocked = 0;
00955         ent->client->ps.zoomLockTime = 0;
00956         ent->client->ps.legsAnim = 0;
00957         ent->client->ps.legsTimer = 0;
00958         ent->client->ps.torsoAnim = 0;
00959         ent->client->ps.torsoTimer = 0;
00960 }
00961 
00962 /*
00963 =================
00964 Cmd_Team_f
00965 =================
00966 */
00967 void Cmd_Team_f( gentity_t *ent ) {
00968         int                     oldTeam;
00969         char            s[MAX_TOKEN_CHARS];
00970 
00971         if ( trap_Argc() != 2 ) {
00972                 oldTeam = ent->client->sess.sessionTeam;
00973                 switch ( oldTeam ) {
00974                 case TEAM_BLUE:
00975                         trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", G_GetStringEdString("MP_SVGAME", "PRINTBLUETEAM")) );
00976                         break;
00977                 case TEAM_RED:
00978                         trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", G_GetStringEdString("MP_SVGAME", "PRINTREDTEAM")) );
00979                         break;
00980                 case TEAM_FREE:
00981                         trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", G_GetStringEdString("MP_SVGAME", "PRINTFREETEAM")) );
00982                         break;
00983                 case TEAM_SPECTATOR:
00984                         trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", G_GetStringEdString("MP_SVGAME", "PRINTSPECTEAM")) );
00985                         break;
00986                 }
00987                 return;
00988         }
00989 
00990         if ( ent->client->switchTeamTime > level.time ) {
00991                 trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", G_GetStringEdString("MP_SVGAME", "NOSWITCH")) );
00992                 return;
00993         }
00994 
00995         if (gEscaping)
00996         {
00997                 return;
00998         }
00999 
01000         // if they are playing a tournement game, count as a loss
01001         if ( g_gametype.integer == GT_DUEL
01002                 && ent->client->sess.sessionTeam == TEAM_FREE ) {//in a tournament game
01003                 //disallow changing teams
01004                 trap_SendServerCommand( ent-g_entities, "print \"Cannot switch teams in Duel\n\"" );
01005                 return;
01006                 //FIXME: why should this be a loss???
01007                 //ent->client->sess.losses++;
01008         }
01009 
01010         if (g_gametype.integer == GT_POWERDUEL)
01011         { //don't let clients change teams manually at all in powerduel, it will be taken care of through automated stuff
01012                 trap_SendServerCommand( ent-g_entities, "print \"Cannot switch teams in Power Duel\n\"" );
01013                 return;
01014         }
01015 
01016         trap_Argv( 1, s, sizeof( s ) );
01017 
01018         SetTeam( ent, s );
01019 
01020         ent->client->switchTeamTime = level.time + 5000;
01021 }
01022 
01023 /*
01024 =================
01025 Cmd_DuelTeam_f
01026 =================
01027 */
01028 void Cmd_DuelTeam_f(gentity_t *ent)
01029 {
01030         int                     oldTeam;
01031         char            s[MAX_TOKEN_CHARS];
01032 
01033         if (g_gametype.integer != GT_POWERDUEL)
01034         { //don't bother doing anything if this is not power duel
01035                 return;
01036         }
01037 
01038         /*
01039         if (ent->client->sess.sessionTeam != TEAM_SPECTATOR)
01040         {
01041                 trap_SendServerCommand( ent-g_entities, va("print \"You cannot change your duel team unless you are a spectator.\n\""));
01042                 return;
01043         }
01044         */
01045 
01046         if ( trap_Argc() != 2 )
01047         { //No arg so tell what team we're currently on.
01048                 oldTeam = ent->client->sess.duelTeam;
01049                 switch ( oldTeam )
01050                 {
01051                 case DUELTEAM_FREE:
01052                         trap_SendServerCommand( ent-g_entities, va("print \"None\n\"") );
01053                         break;
01054                 case DUELTEAM_LONE:
01055                         trap_SendServerCommand( ent-g_entities, va("print \"Single\n\"") );
01056                         break;
01057                 case DUELTEAM_DOUBLE:
01058                         trap_SendServerCommand( ent-g_entities, va("print \"Double\n\"") );
01059                         break;
01060                 default:
01061                         break;
01062                 }
01063                 return;
01064         }
01065 
01066         if ( ent->client->switchDuelTeamTime > level.time )
01067         { //debounce for changing
01068                 trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", G_GetStringEdString("MP_SVGAME", "NOSWITCH")) );
01069                 return;
01070         }
01071 
01072         trap_Argv( 1, s, sizeof( s ) );
01073 
01074         oldTeam = ent->client->sess.duelTeam;
01075 
01076         if (!Q_stricmp(s, "free"))
01077         {
01078                 ent->client->sess.duelTeam = DUELTEAM_FREE;
01079         }
01080         else if (!Q_stricmp(s, "single"))
01081         {
01082                 ent->client->sess.duelTeam = DUELTEAM_LONE;
01083         }
01084         else if (!Q_stricmp(s, "double"))
01085         {
01086                 ent->client->sess.duelTeam = DUELTEAM_DOUBLE;
01087         }
01088         else
01089         {
01090                 trap_SendServerCommand( ent-g_entities, va("print \"'%s' not a valid duel team.\n\"", s) );
01091         }
01092 
01093         if (oldTeam == ent->client->sess.duelTeam)
01094         { //didn't actually change, so don't care.
01095                 return;
01096         }
01097 
01098         if (ent->client->sess.sessionTeam != TEAM_SPECTATOR)
01099         { //ok..die
01100                 int curTeam = ent->client->sess.duelTeam;
01101                 ent->client->sess.duelTeam = oldTeam;
01102                 G_Damage(ent, ent, ent, NULL, ent->client->ps.origin, 99999, DAMAGE_NO_PROTECTION, MOD_SUICIDE);
01103                 ent->client->sess.duelTeam = curTeam;
01104         }
01105         //reset wins and losses
01106         ent->client->sess.wins = 0;
01107         ent->client->sess.losses = 0;
01108 
01109         //get and distribute relevent paramters
01110         ClientUserinfoChanged( ent->s.number );
01111 
01112         ent->client->switchDuelTeamTime = level.time + 5000;
01113 }
01114 
01115 int G_TeamForSiegeClass(const char *clName)
01116 {
01117         int i = 0;
01118         int team = SIEGETEAM_TEAM1;
01119         siegeTeam_t *stm = BG_SiegeFindThemeForTeam(team);
01120         siegeClass_t *scl;
01121 
01122         if (!stm)
01123         {
01124                 return 0;
01125         }
01126 
01127         while (team <= SIEGETEAM_TEAM2)
01128         {
01129                 scl = stm->classes[i];
01130 
01131                 if (scl && scl->name[0])
01132                 {
01133                         if (!Q_stricmp(clName, scl->name))
01134                         {
01135                                 return team;
01136                         }
01137                 }
01138 
01139                 i++;
01140                 if (i >= MAX_SIEGE_CLASSES || i >= stm->numClasses)
01141                 {
01142                         if (team == SIEGETEAM_TEAM2)
01143                         {
01144                                 break;
01145                         }
01146                         team = SIEGETEAM_TEAM2;
01147                         stm = BG_SiegeFindThemeForTeam(team);
01148                         i = 0;
01149                 }
01150         }
01151 
01152         return 0;
01153 }
01154 
01155 /*
01156 =================
01157 Cmd_SiegeClass_f
01158 =================
01159 */
01160 void Cmd_SiegeClass_f( gentity_t *ent )
01161 {
01162         char className[64];
01163         int team = 0;
01164         int preScore;
01165         qboolean startedAsSpec = qfalse;
01166 
01167         if (g_gametype.integer != GT_SIEGE)
01168         { //classes are only valid for this gametype
01169                 return;
01170         }
01171 
01172         if (!ent->client)
01173         {
01174                 return;
01175         }
01176 
01177         if (trap_Argc() < 1)
01178         {
01179                 return;
01180         }
01181 
01182         if ( ent->client->switchClassTime > level.time )
01183         {
01184                 trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", G_GetStringEdString("MP_SVGAME", "NOCLASSSWITCH")) );
01185                 return;
01186         }
01187 
01188         if (ent->client->sess.sessionTeam == TEAM_SPECTATOR)
01189         {
01190                 startedAsSpec = qtrue;
01191         }
01192 
01193         trap_Argv( 1, className, sizeof( className ) );
01194 
01195         team = G_TeamForSiegeClass(className);
01196 
01197         if (!team)
01198         { //not a valid class name
01199                 return;
01200         }
01201 
01202         if (ent->client->sess.sessionTeam != team)
01203         { //try changing it then
01204                 g_preventTeamBegin = qtrue;
01205                 if (team == TEAM_RED)
01206                 {
01207                         SetTeam(ent, "red");
01208                 }
01209                 else if (team == TEAM_BLUE)
01210                 {
01211                         SetTeam(ent, "blue");
01212                 }
01213                 g_preventTeamBegin = qfalse;
01214 
01215                 if (ent->client->sess.sessionTeam != team)
01216                 { //failed, oh well
01217                         if (ent->client->sess.sessionTeam != TEAM_SPECTATOR ||
01218                                 ent->client->sess.siegeDesiredTeam != team)
01219                         {
01220                                 trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", G_GetStringEdString("MP_SVGAME", "NOCLASSTEAM")) );
01221                                 return;
01222                         }
01223                 }
01224         }
01225 
01226         //preserve 'is score
01227         preScore = ent->client->ps.persistant[PERS_SCORE];
01228 
01229         //Make sure the class is valid for the team
01230         BG_SiegeCheckClassLegality(team, className);
01231 
01232         //Set the session data
01233         strcpy(ent->client->sess.siegeClass, className);
01234 
01235         // get and distribute relevent paramters
01236         ClientUserinfoChanged( ent->s.number );
01237 
01238         if (ent->client->tempSpectate < level.time)
01239         {
01240                 // Kill him (makes sure he loses flags, etc)
01241                 if (ent->health > 0 && !startedAsSpec)
01242                 {
01243                         ent->flags &= ~FL_GODMODE;
01244                         ent->client->ps.stats[STAT_HEALTH] = ent->health = 0;
01245                         player_die (ent, ent, ent, 100000, MOD_SUICIDE);
01246                 }
01247 
01248                 if (ent->client->sess.sessionTeam == TEAM_SPECTATOR || startedAsSpec)
01249                 { //respawn them instantly.
01250                         ClientBegin( ent->s.number, qfalse );
01251                 }
01252         }
01253         //set it back after we do all the stuff
01254         ent->client->ps.persistant[