Main Page | Class Hierarchy | Alphabetical List | Data Structures | Directories | File List | Data Fields | Globals

g_cmds.c

Go to the documentation of this file.
00001 /*
00002 ===========================================================================
00003 Copyright (C) 1999-2005 Id Software, Inc.
00004 
00005 This file is part of Quake III Arena source code.
00006 
00007 Quake III Arena source code is free software; you can redistribute it
00008 and/or modify it under the terms of the GNU General Public License as
00009 published by the Free Software Foundation; either version 2 of the License,
00010 or (at your option) any later version.
00011 
00012 Quake III Arena source code is distributed in the hope that it will be
00013 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
00014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015 GNU General Public License for more details.
00016 
00017 You should have received a copy of the GNU General Public License
00018 along with Foobar; if not, write to the Free Software
00019 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00020 ===========================================================================
00021 */
00022 //
00023 #include "g_local.h"
00024 
00025 #include "../../ui/menudef.h"           // for the voice chats
00026 
00027 /*
00028 ==================
00029 DeathmatchScoreboardMessage
00030 
00031 ==================
00032 */
00033 void DeathmatchScoreboardMessage( gentity_t *ent ) {
00034     char        entry[1024];
00035     char        string[1400];
00036     int         stringlength;
00037     int         i, j;
00038     gclient_t   *cl;
00039     int         numSorted, scoreFlags, accuracy, perfect;
00040 
00041     // send the latest information on all clients
00042     string[0] = 0;
00043     stringlength = 0;
00044     scoreFlags = 0;
00045 
00046     numSorted = level.numConnectedClients;
00047     
00048     for (i=0 ; i < numSorted ; i++) {
00049         int     ping;
00050 
00051         cl = &level.clients[level.sortedClients[i]];
00052 
00053         if ( cl->pers.connected == CON_CONNECTING ) {
00054             ping = -1;
00055         } else {
00056             ping = cl->ps.ping < 999 ? cl->ps.ping : 999;
00057         }
00058 
00059         if( cl->accuracy_shots ) {
00060             accuracy = cl->accuracy_hits * 100 / cl->accuracy_shots;
00061         }
00062         else {
00063             accuracy = 0;
00064         }
00065         perfect = ( cl->ps.persistant[PERS_RANK] == 0 && cl->ps.persistant[PERS_KILLED] == 0 ) ? 1 : 0;
00066 
00067         Com_sprintf (entry, sizeof(entry),
00068             " %i %i %i %i %i %i %i %i %i %i %i %i %i %i", level.sortedClients[i],
00069             cl->ps.persistant[PERS_SCORE], ping, (level.time - cl->pers.enterTime)/60000,
00070             scoreFlags, g_entities[level.sortedClients[i]].s.powerups, accuracy, 
00071             cl->ps.persistant[PERS_IMPRESSIVE_COUNT],
00072             cl->ps.persistant[PERS_EXCELLENT_COUNT],
00073             cl->ps.persistant[PERS_GAUNTLET_FRAG_COUNT], 
00074             cl->ps.persistant[PERS_DEFEND_COUNT], 
00075             cl->ps.persistant[PERS_ASSIST_COUNT], 
00076             perfect,
00077             cl->ps.persistant[PERS_CAPTURES]);
00078         j = strlen(entry);
00079         if (stringlength + j > 1024)
00080             break;
00081         strcpy (string + stringlength, entry);
00082         stringlength += j;
00083     }
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 \"Cheats are not enabled on this server.\n\""));
00112         return qfalse;
00113     }
00114     if ( ent->health <= 0 ) {
00115         trap_SendServerCommand( ent-g_entities, va("print \"You must be alive to use this command.\n\""));
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( *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 *ent)
00231 {
00232     char        *name;
00233     gitem_t     *it;
00234     int         i;
00235     qboolean    give_all;
00236     gentity_t       *it_ent;
00237     trace_t     trace;
00238 
00239     if ( !CheatsOk( ent ) ) {
00240         return;
00241     }
00242 
00243     name = ConcatArgs( 1 );
00244 
00245     if (Q_stricmp(name, "all") == 0)
00246         give_all = qtrue;
00247     else
00248         give_all = qfalse;
00249 
00250     if (give_all || Q_stricmp( name, "health") == 0)
00251     {
00252         ent->health = ent->client->ps.stats[STAT_MAX_HEALTH];
00253         if (!give_all)
00254             return;
00255     }
00256 
00257     if (give_all || Q_stricmp(name, "weapons") == 0)
00258     {
00259         ent->client->ps.stats[STAT_WEAPONS] = (1 << WP_NUM_WEAPONS) - 1 - 
00260             ( 1 << WP_GRAPPLING_HOOK ) - ( 1 << WP_NONE );
00261         if (!give_all)
00262             return;
00263     }
00264 
00265     if (give_all || Q_stricmp(name, "ammo") == 0)
00266     {
00267         for ( i = 0 ; i < MAX_WEAPONS ; i++ ) {
00268             ent->client->ps.ammo[i] = 999;
00269         }
00270         if (!give_all)
00271             return;
00272     }
00273 
00274     if (give_all || Q_stricmp(name, "armor") == 0)
00275     {
00276         ent->client->ps.stats[STAT_ARMOR] = 200;
00277 
00278         if (!give_all)
00279             return;
00280     }
00281 
00282     if (Q_stricmp(name, "excellent") == 0) {
00283         ent->client->ps.persistant[PERS_EXCELLENT_COUNT]++;
00284         return;
00285     }
00286     if (Q_stricmp(name, "impressive") == 0) {
00287         ent->client->ps.persistant[PERS_IMPRESSIVE_COUNT]++;
00288         return;
00289     }
00290     if (Q_stricmp(name, "gauntletaward") == 0) {
00291         ent->client->ps.persistant[PERS_GAUNTLET_FRAG_COUNT]++;
00292         return;
00293     }
00294     if (Q_stricmp(name, "defend") == 0) {
00295         ent->client->ps.persistant[PERS_DEFEND_COUNT]++;
00296         return;
00297     }
00298     if (Q_stricmp(name, "assist") == 0) {
00299         ent->client->ps.persistant[PERS_ASSIST_COUNT]++;
00300         return;
00301     }
00302 
00303     // spawn a specific item right on the player
00304     if ( !give_all ) {
00305         it = BG_FindItem (name);
00306         if (!it) {
00307             return;
00308         }
00309 
00310         it_ent = G_Spawn();
00311         VectorCopy( ent->r.currentOrigin, it_ent->s.origin );
00312         it_ent->classname = it->classname;
00313         G_SpawnItem (it_ent, it);
00314         FinishSpawningItem(it_ent );
00315         memset( &trace, 0, sizeof( trace ) );
00316         Touch_Item (it_ent, ent, &trace);
00317         if (it_ent->inuse) {
00318             G_FreeEntity( it_ent );
00319         }
00320     }
00321 }
00322 
00323 
00324 /*
00325 ==================
00326 Cmd_God_f
00327 
00328 Sets client to godmode
00329 
00330 argv(0) god
00331 ==================
00332 */
00333 void Cmd_God_f (gentity_t *ent)
00334 {
00335     char    *msg;
00336 
00337     if ( !CheatsOk( ent ) ) {
00338         return;
00339     }
00340 
00341     ent->flags ^= FL_GODMODE;
00342     if (!(ent->flags & FL_GODMODE) )
00343         msg = "godmode OFF\n";
00344     else
00345         msg = "godmode ON\n";
00346 
00347     trap_SendServerCommand( ent-g_entities, va("print \"%s\"", msg));
00348 }
00349 
00350 
00351 /*
00352 ==================
00353 Cmd_Notarget_f
00354 
00355 Sets client to notarget
00356 
00357 argv(0) notarget
00358 ==================
00359 */
00360 void Cmd_Notarget_f( gentity_t *ent ) {
00361     char    *msg;
00362 
00363     if ( !CheatsOk( ent ) ) {
00364         return;
00365     }
00366 
00367     ent->flags ^= FL_NOTARGET;
00368     if (!(ent->flags & FL_NOTARGET) )
00369         msg = "notarget OFF\n";
00370     else
00371         msg = "notarget ON\n";
00372 
00373     trap_SendServerCommand( ent-g_entities, va("print \"%s\"", msg));
00374 }
00375 
00376 
00377 /*
00378 ==================
00379 Cmd_Noclip_f
00380 
00381 argv(0) noclip
00382 ==================
00383 */
00384 void Cmd_Noclip_f( gentity_t *ent ) {
00385     char    *msg;
00386 
00387     if ( !CheatsOk( ent ) ) {
00388         return;
00389     }
00390 
00391     if ( ent->client->noclip ) {
00392         msg = "noclip OFF\n";
00393     } else {
00394         msg = "noclip ON\n";
00395     }
00396     ent->client->noclip = !ent->client->noclip;
00397 
00398     trap_SendServerCommand( ent-g_entities, va("print \"%s\"", msg));
00399 }
00400 
00401 
00402 /*
00403 ==================
00404 Cmd_LevelShot_f
00405 
00406 This is just to help generate the level pictures
00407 for the menus.  It goes to the intermission immediately
00408 and sends over a command to the client to resize the view,
00409 hide the scoreboard, and take a special screenshot
00410 ==================
00411 */
00412 void Cmd_LevelShot_f( gentity_t *ent ) {
00413     if ( !CheatsOk( ent ) ) {
00414         return;
00415     }
00416 
00417     // doesn't work in single player
00418     if ( g_gametype.integer != 0 ) {
00419         trap_SendServerCommand( ent-g_entities, 
00420             "print \"Must be in g_gametype 0 for levelshot\n\"" );
00421         return;
00422     }
00423 
00424     BeginIntermission();
00425     trap_SendServerCommand( ent-g_entities, "clientLevelShot" );
00426 }
00427 
00428 
00429 /*
00430 ==================
00431 Cmd_LevelShot_f
00432 
00433 This is just to help generate the level pictures
00434 for the menus.  It goes to the intermission immediately
00435 and sends over a command to the client to resize the view,
00436 hide the scoreboard, and take a special screenshot
00437 ==================
00438 */
00439 void Cmd_TeamTask_f( gentity_t *ent ) {
00440     char userinfo[MAX_INFO_STRING];
00441     char        arg[MAX_TOKEN_CHARS];
00442     int task;
00443     int client = ent->client - level.clients;
00444 
00445     if ( trap_Argc() != 2 ) {
00446         return;
00447     }
00448     trap_Argv( 1, arg, sizeof( arg ) );
00449     task = atoi( arg );
00450 
00451     trap_GetUserinfo(client, userinfo, sizeof(userinfo));
00452     Info_SetValueForKey(userinfo, "teamtask", va("%d", task));
00453     trap_SetUserinfo(client, userinfo);
00454     ClientUserinfoChanged(client);
00455 }
00456 
00457 
00458 
00459 /*
00460 =================
00461 Cmd_Kill_f
00462 =================
00463 */
00464 void Cmd_Kill_f( gentity_t *ent ) {
00465     if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) {
00466         return;
00467     }
00468     if (ent->health <= 0) {
00469         return;
00470     }
00471     ent->flags &= ~FL_GODMODE;
00472     ent->client->ps.stats[STAT_HEALTH] = ent->health = -999;
00473     player_die (ent, ent, ent, 100000, MOD_SUICIDE);
00474 }
00475 
00476 /*
00477 =================
00478 BroadCastTeamChange
00479 
00480 Let everyone know about a team change
00481 =================
00482 */
00483 void BroadcastTeamChange( gclient_t *client, int oldTeam )
00484 {
00485     if ( client->sess.sessionTeam == TEAM_RED ) {
00486         trap_SendServerCommand( -1, va("cp \"%s" S_COLOR_WHITE " joined the red team.\n\"",
00487             client->pers.netname) );
00488     } else if ( client->sess.sessionTeam == TEAM_BLUE ) {
00489         trap_SendServerCommand( -1, va("cp \"%s" S_COLOR_WHITE " joined the blue team.\n\"",
00490         client->pers.netname));
00491     } else if ( client->sess.sessionTeam == TEAM_SPECTATOR && oldTeam != TEAM_SPECTATOR ) {
00492         trap_SendServerCommand( -1, va("cp \"%s" S_COLOR_WHITE " joined the spectators.\n\"",
00493         client->pers.netname));
00494     } else if ( client->sess.sessionTeam == TEAM_FREE ) {
00495         trap_SendServerCommand( -1, va("cp \"%s" S_COLOR_WHITE " joined the battle.\n\"",
00496         client->pers.netname));
00497     }
00498 }
00499 
00500 /*
00501 =================
00502 SetTeam
00503 =================
00504 */
00505 void SetTeam( gentity_t *ent, char *s ) {
00506     int                 team, oldTeam;
00507     gclient_t           *client;
00508     int                 clientNum;
00509     spectatorState_t    specState;
00510     int                 specClient;
00511     int                 teamLeader;
00512 
00513     //
00514     // see what change is requested
00515     //
00516     client = ent->client;
00517 
00518     clientNum = client - level.clients;
00519     specClient = 0;
00520     specState = SPECTATOR_NOT;
00521     if ( !Q_stricmp( s, "scoreboard" ) || !Q_stricmp( s, "score" )  ) {
00522         team = TEAM_SPECTATOR;
00523         specState = SPECTATOR_SCOREBOARD;
00524     } else if ( !Q_stricmp( s, "follow1" ) ) {
00525         team = TEAM_SPECTATOR;
00526         specState = SPECTATOR_FOLLOW;
00527         specClient = -1;
00528     } else if ( !Q_stricmp( s, "follow2" ) ) {
00529         team = TEAM_SPECTATOR;
00530         specState = SPECTATOR_FOLLOW;
00531         specClient = -2;
00532     } else if ( !Q_stricmp( s, "spectator" ) || !Q_stricmp( s, "s" ) ) {
00533         team = TEAM_SPECTATOR;
00534         specState = SPECTATOR_FREE;
00535     } else if ( g_gametype.integer >= GT_TEAM ) {
00536         // if running a team game, assign player to one of the teams
00537         specState = SPECTATOR_NOT;
00538         if ( !Q_stricmp( s, "red" ) || !Q_stricmp( s, "r" ) ) {
00539             team = TEAM_RED;
00540         } else if ( !Q_stricmp( s, "blue" ) || !Q_stricmp( s, "b" ) ) {
00541             team = TEAM_BLUE;
00542         } else {
00543             // pick the team with the least number of players
00544             team = PickTeam( clientNum );
00545         }
00546 
00547         if ( g_teamForceBalance.integer  ) {
00548             int     counts[TEAM_NUM_TEAMS];
00549 
00550             counts[TEAM_BLUE] = TeamCount( ent->client->ps.clientNum, TEAM_BLUE );
00551             counts[TEAM_RED] = TeamCount( ent->client->ps.clientNum, TEAM_RED );
00552 
00553             // We allow a spread of two
00554             if ( team == TEAM_RED && counts[TEAM_RED] - counts[TEAM_BLUE] > 1 ) {
00555                 trap_SendServerCommand( ent->client->ps.clientNum, 
00556                     "cp \"Red team has too many players.\n\"" );
00557                 return; // ignore the request
00558             }
00559             if ( team == TEAM_BLUE && counts[TEAM_BLUE] - counts[TEAM_RED] > 1 ) {
00560                 trap_SendServerCommand( ent->client->ps.clientNum, 
00561                     "cp \"Blue team has too many players.\n\"" );
00562                 return; // ignore the request
00563             }
00564 
00565             // It's ok, the team we are switching to has less or same number of players
00566         }
00567 
00568     } else {
00569         // force them to spectators if there aren't any spots free
00570         team = TEAM_FREE;
00571     }
00572 
00573     // override decision if limiting the players
00574     if ( (g_gametype.integer == GT_TOURNAMENT)
00575         && level.numNonSpectatorClients >= 2 ) {
00576         team = TEAM_SPECTATOR;
00577     } else if ( g_maxGameClients.integer > 0 && 
00578         level.numNonSpectatorClients >= g_maxGameClients.integer ) {
00579         team = TEAM_SPECTATOR;
00580     }
00581 
00582     //
00583     // decide if we will allow the change
00584     //
00585     oldTeam = client->sess.sessionTeam;
00586     if ( team == oldTeam && team != TEAM_SPECTATOR ) {
00587         return;
00588     }
00589 
00590     //
00591     // execute the team change
00592     //
00593 
00594     // if the player was dead leave the body
00595     if ( client->ps.stats[STAT_HEALTH] <= 0 ) {
00596         CopyToBodyQue(ent);
00597     }
00598 
00599     // he starts at 'base'
00600     client->pers.teamState.state = TEAM_BEGIN;
00601     if ( oldTeam != TEAM_SPECTATOR ) {
00602         // Kill him (makes sure he loses flags, etc)
00603         ent->flags &= ~FL_GODMODE;
00604         ent->client->ps.stats[STAT_HEALTH] = ent->health = 0;
00605         player_die (ent, ent, ent, 100000, MOD_SUICIDE);
00606 
00607     }
00608     // they go to the end of the line for tournements
00609     if ( team == TEAM_SPECTATOR ) {
00610         client->sess.spectatorTime = level.time;
00611     }
00612 
00613     client->sess.sessionTeam = team;
00614     client->sess.spectatorState = specState;
00615     client->sess.spectatorClient = specClient;
00616 
00617     client->sess.teamLeader = qfalse;
00618     if ( team == TEAM_RED || team == TEAM_BLUE ) {
00619         teamLeader = TeamLeader( team );
00620         // if there is no team leader or the team leader is a bot and this client is not a bot
00621         if ( teamLeader == -1 || ( !(g_entities[clientNum].r.svFlags & SVF_BOT) && (g_entities[teamLeader].r.svFlags & SVF_BOT) ) ) {
00622             SetLeader( team, clientNum );
00623         }
00624     }
00625     // make sure there is a team leader on the team the player came from
00626     if ( oldTeam == TEAM_RED || oldTeam == TEAM_BLUE ) {
00627         CheckTeamLeader( oldTeam );
00628     }
00629 
00630     BroadcastTeamChange( client, oldTeam );
00631 
00632     // get and distribute relevent paramters
00633     ClientUserinfoChanged( clientNum );
00634 
00635     ClientBegin( clientNum );
00636 }
00637 
00638 /*
00639 =================
00640 StopFollowing
00641 
00642 If the client being followed leaves the game, or you just want to drop
00643 to free floating spectator mode
00644 =================
00645 */
00646 void StopFollowing( gentity_t *ent ) {
00647     ent->client->ps.persistant[ PERS_TEAM ] = TEAM_SPECTATOR;   
00648     ent->client->sess.sessionTeam = TEAM_SPECTATOR; 
00649     ent->client->sess.spectatorState = SPECTATOR_FREE;
00650     ent->client->ps.pm_flags &= ~PMF_FOLLOW;
00651     ent->r.svFlags &= ~SVF_BOT;
00652     ent->client->ps.clientNum = ent - g_entities;
00653 }
00654 
00655 /*
00656 =================
00657 Cmd_Team_f
00658 =================
00659 */
00660 void Cmd_Team_f( gentity_t *ent ) {
00661     int         oldTeam;
00662     char        s[MAX_TOKEN_CHARS];
00663 
00664     if ( trap_Argc() != 2 ) {
00665         oldTeam = ent->client->sess.sessionTeam;
00666         switch ( oldTeam ) {
00667         case TEAM_BLUE:
00668             trap_SendServerCommand( ent-g_entities, "print \"Blue team\n\"" );
00669             break;
00670         case TEAM_RED:
00671             trap_SendServerCommand( ent-g_entities, "print \"Red team\n\"" );
00672             break;
00673         case TEAM_FREE:
00674             trap_SendServerCommand( ent-g_entities, "print \"Free team\n\"" );
00675             break;
00676         case TEAM_SPECTATOR:
00677             trap_SendServerCommand( ent-g_entities, "print \"Spectator team\n\"" );
00678             break;
00679         }
00680         return;
00681     }
00682 
00683     if ( ent->client->switchTeamTime > level.time ) {
00684         trap_SendServerCommand( ent-g_entities, "print \"May not switch teams more than once per 5 seconds.\n\"" );
00685         return;
00686     }
00687 
00688     // if they are playing a tournement game, count as a loss
00689     if ( (g_gametype.integer == GT_TOURNAMENT )
00690         && ent->client->sess.sessionTeam == TEAM_FREE ) {
00691         ent->client->sess.losses++;
00692     }
00693 
00694     trap_Argv( 1, s, sizeof( s ) );
00695 
00696     SetTeam( ent, s );
00697 
00698     ent->client->switchTeamTime = level.time + 5000;
00699 }
00700 
00701 
00702 /*
00703 =================
00704 Cmd_Follow_f
00705 =================
00706 */
00707 void Cmd_Follow_f( gentity_t *ent ) {
00708     int     i;
00709     char    arg[MAX_TOKEN_CHARS];
00710 
00711     if ( trap_Argc() != 2 ) {
00712         if ( ent->client->sess.spectatorState == SPECTATOR_FOLLOW ) {
00713             StopFollowing( ent );
00714         }
00715         return;
00716     }
00717 
00718     trap_Argv( 1, arg, sizeof( arg ) );
00719     i = ClientNumberFromString( ent, arg );
00720     if ( i == -1 ) {
00721         return;
00722     }
00723 
00724     // can't follow self
00725     if ( &level.clients[ i ] == ent->client ) {
00726         return;
00727     }
00728 
00729     // can't follow another spectator
00730     if ( level.clients[ i ].sess.sessionTeam == TEAM_SPECTATOR ) {
00731         return;
00732     }
00733 
00734     // if they are playing a tournement game, count as a loss
00735     if ( (g_gametype.integer == GT_TOURNAMENT )
00736         && ent->client->sess.sessionTeam == TEAM_FREE ) {
00737         ent->client->sess.losses++;
00738     }
00739 
00740     // first set them to spectator
00741     if ( ent->client->sess.sessionTeam != TEAM_SPECTATOR ) {
00742         SetTeam( ent, "spectator" );
00743     }
00744 
00745     ent->client->sess.spectatorState = SPECTATOR_FOLLOW;
00746     ent->client->sess.spectatorClient = i;
00747 }
00748 
00749 /*
00750 =================
00751 Cmd_FollowCycle_f
00752 =================
00753 */
00754 void Cmd_FollowCycle_f( gentity_t *ent, int dir ) {
00755     int     clientnum;
00756     int     original;
00757 
00758     // if they are playing a tournement game, count as a loss
00759     if ( (g_gametype.integer == GT_TOURNAMENT )
00760         && ent->client->sess.sessionTeam == TEAM_FREE ) {
00761         ent->client->sess.losses++;
00762     }
00763     // first set them to spectator
00764     if ( ent->client->sess.spectatorState == SPECTATOR_NOT ) {
00765         SetTeam( ent, "spectator" );
00766     }
00767 
00768     if ( dir != 1 && dir != -1 ) {
00769         G_Error( "Cmd_FollowCycle_f: bad dir %i", dir );
00770     }
00771 
00772     clientnum = ent->client->sess.spectatorClient;
00773     original = clientnum;
00774     do {
00775         clientnum += dir;
00776         if ( clientnum >= level.maxclients ) {
00777             clientnum = 0;
00778         }
00779         if ( clientnum < 0 ) {
00780             clientnum = level.maxclients - 1;
00781         }
00782 
00783         // can only follow connected clients
00784         if ( level.clients[ clientnum ].pers.connected != CON_CONNECTED ) {
00785             continue;
00786         }
00787 
00788         // can't follow another spectator
00789         if ( level.clients[ clientnum ].sess.sessionTeam == TEAM_SPECTATOR ) {
00790             continue;
00791         }
00792 
00793         // this is good, we can use it
00794         ent->client->sess.spectatorClient = clientnum;
00795         ent->client->sess.spectatorState = SPECTATOR_FOLLOW;
00796         return;
00797     } while ( clientnum != original );
00798 
00799     // leave it where it was
00800 }
00801 
00802 
00803 /*
00804 ==================
00805 G_Say
00806 ==================
00807 */
00808 
00809 static void G_SayTo( gentity_t *ent, gentity_t *other, int mode, int color, const char *name, const char *message ) {
00810     if (!other) {
00811         return;
00812     }
00813     if (!other->inuse) {
00814         return;
00815     }
00816     if (!other->client) {
00817         return;
00818     }
00819     if ( other->client->pers.connected != CON_CONNECTED ) {
00820         return;
00821     }
00822     if ( mode == SAY_TEAM  && !OnSameTeam(ent, other) ) {
00823         return;
00824     }
00825     // no chatting to players in tournements
00826     if ( (g_gametype.integer == GT_TOURNAMENT )
00827         && other->client->sess.sessionTeam == TEAM_FREE
00828         && ent->client->sess.sessionTeam != TEAM_FREE ) {
00829         return;
00830     }
00831 
00832     trap_SendServerCommand( other-g_entities, va("%s \"%s%c%c%s\"", 
00833         mode == SAY_TEAM ? "tchat" : "chat",
00834         name, Q_COLOR_ESCAPE, color, message));
00835 }
00836 
00837 #define EC      "\x19"
00838 
00839 void G_Say( gentity_t *ent, gentity_t *target, int mode, const char *chatText ) {
00840     int         j;
00841     gentity_t   *other;
00842     int         color;
00843     char        name[64];
00844     // don't let text be too long for malicious reasons
00845     char        text[MAX_SAY_TEXT];
00846     char        location[64];
00847 
00848     if ( g_gametype.integer < GT_TEAM && mode == SAY_TEAM ) {
00849         mode = SAY_ALL;
00850     }
00851 
00852     switch ( mode ) {
00853     default:
00854     case SAY_ALL:
00855         G_LogPrintf( "say: %s: %s\n", ent->client->pers.netname, chatText );
00856         Com_sprintf (name, sizeof(name), "%s%c%c"EC": ", ent->client->pers.netname, Q_COLOR_ESCAPE, COLOR_WHITE );
00857         color = COLOR_GREEN;
00858         break;
00859     case SAY_TEAM:
00860         G_LogPrintf( "sayteam: %s: %s\n", ent->client->pers.netname, chatText );
00861         if (Team_GetLocationMsg(ent, location, sizeof(location)))
00862             Com_sprintf (name, sizeof(name), EC"(%s%c%c"EC") (%s)"EC": ", 
00863                 ent->client->pers.netname, Q_COLOR_ESCAPE, COLOR_WHITE, location);
00864         else
00865             Com_sprintf (name, sizeof(name), EC"(%s%c%c"EC")"EC": ", 
00866                 ent->client->pers.netname, Q_COLOR_ESCAPE, COLOR_WHITE );
00867         color = COLOR_CYAN;
00868         break;
00869     case SAY_TELL:
00870         if (target && g_gametype.integer >= GT_TEAM &&
00871             target->client->sess.sessionTeam == ent->client->sess.sessionTeam &&
00872             Team_GetLocationMsg(ent, location, sizeof(location)))
00873             Com_sprintf (name, sizeof(name), EC"[%s%c%c"EC"] (%s)"EC": ", ent->client->pers.netname, Q_COLOR_ESCAPE, COLOR_WHITE, location );
00874         else
00875             Com_sprintf (name, sizeof(name), EC"[%s%c%c"EC"]"EC": ", ent->client->pers.netname, Q_COLOR_ESCAPE, COLOR_WHITE );
00876         color = COLOR_MAGENTA;
00877         break;
00878     }
00879 
00880     Q_strncpyz( text, chatText, sizeof(text) );
00881 
00882     if ( target ) {
00883         G_SayTo( ent, target, mode, color, name, text );
00884         return;
00885     }
00886 
00887     // echo the text to the console
00888     if ( g_dedicated.integer ) {
00889         G_Printf( "%s%s\n", name, text);
00890     }
00891 
00892     // send it to all the apropriate clients
00893     for (j = 0; j < level.maxclients; j++) {
00894         other = &g_entities[j];
00895         G_SayTo( ent, other, mode, color, name, text );
00896     }
00897 }
00898 
00899 
00900 /*
00901 ==================
00902 Cmd_Say_f
00903 ==================
00904 */
00905 static void Cmd_Say_f( gentity_t *ent, int mode, qboolean arg0 ) {
00906     char        *p;
00907 
00908     if ( trap_Argc () < 2 && !arg0 ) {
00909         return;
00910     }
00911 
00912     if (arg0)
00913     {
00914         p = ConcatArgs( 0 );
00915     }
00916     else
00917     {
00918         p = ConcatArgs( 1 );
00919     }
00920 
00921     G_Say( ent, NULL, mode, p );
00922 }
00923 
00924 /*
00925 ==================
00926 Cmd_Tell_f
00927 ==================
00928 */
00929 static void Cmd_Tell_f( gentity_t *ent ) {
00930     int         targetNum;
00931     gentity_t   *target;
00932     char        *p;
00933     char        arg[MAX_TOKEN_CHARS];
00934 
00935     if ( trap_Argc () < 2 ) {
00936         return;
00937     }
00938 
00939     trap_Argv( 1, arg, sizeof( arg ) );
00940     targetNum = atoi( arg );
00941     if ( targetNum < 0 || targetNum >= level.maxclients ) {
00942         return;
00943     }
00944 
00945     target = &g_entities[targetNum];
00946     if ( !target || !target->inuse || !target->client ) {
00947         return;
00948     }
00949 
00950     p = ConcatArgs( 2 );
00951 
00952     G_LogPrintf( "tell: %s to %s: %s\n", ent->client->pers.netname, target->client->pers.netname, p );
00953     G_Say( ent, target, SAY_TELL, p );
00954     // don't tell to the player self if it was already directed to this player
00955     // also don't send the chat back to a bot
00956     if ( ent != target && !(ent->r.svFlags & SVF_BOT)) {
00957         G_Say( ent, ent, SAY_TELL, p );
00958     }
00959 }
00960 
00961 
00962 static void G_VoiceTo( gentity_t *ent, gentity_t *other, int mode, const char *id, qboolean voiceonly ) {
00963     int color;
00964     char *cmd;
00965 
00966     if (!other) {
00967         return;
00968     }
00969     if (!other->inuse) {
00970         return;
00971     }
00972     if (!other->client) {
00973         return;
00974     }
00975     if ( mode == SAY_TEAM && !OnSameTeam(ent, other) ) {
00976         return;
00977     }
00978     // no chatting to players in tournements
00979     if ( (g_gametype.integer == GT_TOURNAMENT )) {
00980         return;
00981     }
00982 
00983     if (mode == SAY_TEAM) {
00984         color = COLOR_CYAN;
00985         cmd = "vtchat";
00986     }
00987     else if (mode == SAY_TELL) {
00988         color = COLOR_MAGENTA;
00989         cmd = "vtell";
00990     }
00991     else {
00992         color = COLOR_GREEN;
00993         cmd = "vchat";
00994     }
00995 
00996     trap_SendServerCommand( other-g_entities, va("%s %d %d %d %s", cmd, voiceonly, ent->s.number, color, id));
00997 }
00998 
00999 void G_Voice( gentity_t *ent, gentity_t *target, int mode, const char *id, qboolean voiceonly ) {
01000     int         j;
01001     gentity_t   *other;
01002 
01003     if ( g_gametype.integer < GT_TEAM && mode == SAY_TEAM ) {
01004         mode = SAY_ALL;
01005     }
01006 
01007     if ( target ) {
01008         G_VoiceTo( ent, target, mode, id, voiceonly );
01009         return;
01010     }
01011 
01012     // echo the text to the console
01013     if ( g_dedicated.integer ) {
01014         G_Printf( "voice: %s %s\n", ent->client->pers.netname, id);
01015     }
01016 
01017     // send it to all the apropriate clients
01018     for (j = 0; j < level.maxclients; j++) {
01019         other = &g_entities[j];
01020         G_VoiceTo( ent, other, mode, id, voiceonly );
01021     }
01022 }
01023 
01024 /*
01025 ==================
01026 Cmd_Voice_f
01027 ==================
01028 */
01029 static void Cmd_Voice_f( gentity_t *ent, int mode, qboolean arg0, qboolean voiceonly ) {
01030     char        *p;
01031 
01032     if ( trap_Argc () < 2 && !arg0 ) {
01033         return;
01034     }
01035 
01036     if (arg0)
01037     {
01038         p = ConcatArgs( 0 );
01039     }
01040     else
01041     {
01042         p = ConcatArgs( 1 );
01043     }
01044 
01045     G_Voice( ent, NULL, mode, p, voiceonly );
01046 }
01047 
01048 /*
01049 ==================
01050 Cmd_VoiceTell_f
01051 ==================
01052 */
01053 static void Cmd_VoiceTell_f( gentity_t *ent, qboolean voiceonly ) {
01054     int         targetNum;
01055     gentity_t   *target;
01056     char        *id;
01057     char        arg[MAX_TOKEN_CHARS];
01058 
01059     if ( trap_Argc () < 2 ) {
01060         return;
01061     }
01062 
01063     trap_Argv( 1, arg, sizeof( arg ) );
01064     targetNum = atoi( arg );
01065     if ( targetNum < 0 || targetNum >= level.maxclients ) {
01066         return;
01067     }
01068 
01069     target = &g_entities[targetNum];
01070     if ( !target || !target->inuse || !target->client ) {
01071         return;
01072     }
01073 
01074     id = ConcatArgs( 2 );
01075 
01076     G_LogPrintf( "vtell: %s to %s: %s\n", ent->client->pers.netname, target->client->pers.netname, id );
01077     G_Voice( ent, target, SAY_TELL, id, voiceonly );
01078     // don't tell to the player self if it was already directed to this player
01079     // also don't send the chat back to a bot
01080     if ( ent != target && !(ent->r.svFlags & SVF_BOT)) {
01081         G_Voice( ent, ent, SAY_TELL, id, voiceonly );
01082     }
01083 }
01084 
01085 
01086 /*
01087 ==================
01088 Cmd_VoiceTaunt_f
01089 ==================
01090 */
01091 static void Cmd_VoiceTaunt_f( gentity_t *ent ) {
01092     gentity_t *who;
01093     int i;
01094 
01095     if (!ent->client) {
01096         return;
01097     }
01098 
01099     // insult someone who just killed you
01100     if (ent->enemy && ent->enemy->client && ent->enemy->client->lastkilled_client == ent->s.number) {
01101         // i am a dead corpse
01102         if (!(ent->enemy->r.svFlags & SVF_BOT)) {
01103             G_Voice( ent, ent->enemy, SAY_TELL, VOICECHAT_DEATHINSULT, qfalse );
01104         }
01105         if (!(ent->r.svFlags & SVF_BOT)) {
01106             G_Voice( ent, ent,        SAY_TELL, VOICECHAT_DEATHINSULT, qfalse );
01107         }
01108         ent->enemy = NULL;
01109         return;
01110     }
01111     // insult someone you just killed
01112     if (ent->client->lastkilled_client >= 0 && ent->client->lastkilled_client != ent->s.number) {
01113         who = g_entities + ent->client->lastkilled_client;
01114         if (who->client) {
01115             // who is the person I just killed
01116             if (who->client->lasthurt_mod == MOD_GAUNTLET) {
01117                 if (!(who->r.svFlags & SVF_BOT)) {
01118                     G_Voice( ent, who, SAY_TELL, VOICECHAT_KILLGAUNTLET, qfalse );  // and I killed them with a gauntlet
01119                 }
01120                 if (!(ent->r.svFlags & SVF_BOT)) {
01121                     G_Voice( ent, ent, SAY_TELL, VOICECHAT_KILLGAUNTLET, qfalse );
01122                 }
01123             } else {
01124                 if (!(who->r.svFlags & SVF_BOT)) {
01125                     G_Voice( ent, who, SAY_TELL, VOICECHAT_KILLINSULT, qfalse );    // and I killed them with something else
01126                 }
01127                 if (!(ent->r.svFlags & SVF_BOT)) {
01128                     G_Voice( ent, ent, SAY_TELL, VOICECHAT_KILLINSULT, qfalse );
01129                 }
01130             }
01131             ent->client->lastkilled_client = -1;
01132             return;
01133         }
01134     }
01135 
01136     if (g_gametype.integer >= GT_TEAM) {
01137         // praise a team mate who just got a reward
01138         for(i = 0; i < MAX_CLIENTS; i++) {
01139             who = g_entities + i;
01140             if (who->client && who != ent && who->client->sess.sessionTeam == ent->client->sess.sessionTeam) {
01141                 if (who->client->rewardTime > level.time) {
01142                     if (!(who->r.svFlags & SVF_BOT)) {
01143                         G_Voice( ent, who, SAY_TELL, VOICECHAT_PRAISE, qfalse );
01144                     }
01145                     if (!(ent->r.svFlags & SVF_BOT)) {
01146                         G_Voice( ent, ent, SAY_TELL, VOICECHAT_PRAISE, qfalse );
01147                     }
01148                     return;
01149                 }
01150             }
01151         }
01152     }
01153 
01154     // just say something
01155     G_Voice( ent, NULL, SAY_ALL, VOICECHAT_TAUNT, qfalse );
01156 }
01157 
01158 
01159 
01160 static char *gc_orders[] = {
01161     "hold your position",
01162     "hold this position",
01163     "come here",
01164     "cover me",
01165     "guard location",
01166     "search and destroy",
01167     "report"
01168 };
01169 
01170 void Cmd_GameCommand_f( gentity_t *ent ) {
01171     int     player;
01172     int     order;
01173     char    str[MAX_TOKEN_CHARS];
01174 
01175     trap_Argv( 1, str, sizeof( str ) );
01176     player = atoi( str );
01177     trap_Argv( 2, str, sizeof( str ) );
01178     order = atoi( str );
01179 
01180     if ( player < 0 || player >= MAX_CLIENTS ) {
01181         return;
01182     }
01183     if ( order < 0 || order > sizeof(gc_orders)/sizeof(char *) ) {
01184         return;
01185     }
01186     G_Say( ent, &g_entities[player], SAY_TELL, gc_orders[order] );
01187     G_Say( ent, ent, SAY_TELL, gc_orders[order] );
01188 }
01189 
01190 /*
01191 ==================
01192 Cmd_Where_f
01193 ==================
01194 */
01195 void Cmd_Where_f( gentity_t *ent ) {
01196     trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", vtos( ent->s.origin ) ) );
01197 }
01198 
01199 static const char *gameNames[] = {
01200     "Free For All",
01201     "Tournament",
01202     "Single Player",
01203     "Team Deathmatch",
01204     "Capture the Flag",
01205     "One Flag CTF",
01206     "Overload",
01207     "Harvester"
01208 };
01209 
01210 /*
01211 ==================
01212 Cmd_CallVote_f
01213 ==================
01214 */
01215 void Cmd_CallVote_f( gentity_t *ent ) {
01216     int     i;
01217     char    arg1[MAX_STRING_TOKENS];
01218     char    arg2[MAX_STRING_TOKENS];
01219 
01220     if ( !g_allowVote.integer ) {
01221         trap_SendServerCommand( ent-g_entities, "print \"Voting not allowed here.\n\"" );
01222         return;
01223     }
01224 
01225     if ( level.voteTime ) {
01226         trap_SendServerCommand( ent-g_entities, "print \"A vote is already in progress.\n\"" );
01227         return;
01228     }
01229     if ( ent->client->pers.voteCount >= MAX_VOTE_COUNT ) {
01230         trap_SendServerCommand( ent-g_entities, "print \"You have called the maximum number of votes.\n\"" );
01231         return;
01232     }
01233     if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) {
01234         trap_SendServerCommand( ent-g_entities, "print \"Not allowed to call a vote as spectator.\n\"" );
01235         return;
01236     }
01237 
01238     // make sure it is a valid command to vote on
01239     trap_Argv( 1, arg1, sizeof( arg1 ) );
01240     trap_Argv( 2, arg2, sizeof( arg2 ) );
01241 
01242     if( strchr( arg1, ';' ) || strchr( arg2, ';' ) ) {
01243         trap_SendServerCommand( ent-g_entities, "print \"Invalid vote string.\n\"" );
01244         return;
01245     }
01246 
01247     if ( !Q_stricmp( arg1, "map_restart" ) ) {
01248     } else if ( !Q_stricmp( arg1, "nextmap" ) ) {
01249     } else if ( !Q_stricmp( arg1, "map" ) ) {
01250     } else if ( !Q_stricmp( arg1, "g_gametype" ) ) {
01251     } else if ( !Q_stricmp( arg1, "kick" ) ) {
01252     } else if ( !Q_stricmp( arg1, "clientkick" ) ) {
01253     } else if ( !Q_stricmp( arg1, "g_doWarmup" ) ) {
01254     } else if ( !Q_stricmp( arg1, "timelimit" ) ) {
01255     } else if ( !Q_stricmp( arg1, "fraglimit" ) ) {
01256     } else {
01257         trap_SendServerCommand( ent-g_entities, "print \"Invalid vote string.\n\"" );
01258         trap_SendServerCommand( ent-g_entities, "print \"Vote commands are: map_restart, nextmap, map <mapname>, g_gametype <n>, kick <player>, clientkick <clientnum>, g_doWarmup, timelimit <time>, fraglimit <frags>.\n\"" );
01259         return;
01260     }
01261 
01262     // if there is still a vote to be executed
01263     if ( level.voteExecuteTime ) {
01264         level.voteExecuteTime = 0;
01265         trap_SendConsoleCommand( EXEC_APPEND, va("%s\n", level.voteString ) );
01266     }
01267 
01268     // special case for g_gametype, check for bad values
01269     if ( !Q_stricmp( arg1, "g_gametype" ) ) {
01270         i = atoi( arg2 );
01271         if( i == GT_SINGLE_PLAYER || i < GT_FFA || i >= GT_MAX_GAME_TYPE) {
01272             trap_SendServerCommand( ent-g_entities, "print \"Invalid gametype.\n\"" );
01273             return;
01274         }
01275 
01276         Com_sprintf( level.voteString, sizeof( level.voteString ), "%s %d", arg1, i );
01277         Com_sprintf( level.voteDisplayString, sizeof( level.voteDisplayString ), "%s %s", arg1, gameNames[i] );
01278     } else if ( !Q_stricmp( arg1, "map" ) ) {
01279         // special case for map changes, we want to reset the nextmap setting
01280         // this allows a player to change maps, but not upset the map rotation
01281         char    s[MAX_STRING_CHARS];
01282 
01283         trap_Cvar_VariableStringBuffer( "nextmap", s, sizeof(s) );
01284         if (*s) {
01285             Com_sprintf( level.voteString, sizeof( level.voteString ), "%s %s; set nextmap \"%s\"", arg1, arg2, s );
01286         } else {
01287             Com_sprintf( level.voteString, sizeof( level.voteString ), "%s %s", arg1, arg2 );
01288         }
01289         Com_sprintf( level.voteDisplayString, sizeof( level.voteDisplayString ), "%s", level.voteString );
01290     } else if ( !Q_stricmp( arg1, "nextmap" ) ) {
01291         char    s[MAX_STRING_CHARS];
01292 
01293         trap_Cvar_VariableStringBuffer( "nextmap", s, sizeof(s) );
01294         if (!*s) {
01295             trap_SendServerCommand( ent-g_entities, "print \"nextmap not set.\n\"" );
01296             return;
01297         }
01298         Com_sprintf( level.voteString, sizeof( level.voteString ), "vstr nextmap");
01299         Com_sprintf( level.voteDisplayString, sizeof( level.voteDisplayString ), "%s", level.voteString );
01300     } else {
01301         Com_sprintf( level.voteString, sizeof( level.voteString ), "%s \"%s\"", arg1, arg2 );
01302         Com_sprintf( level.voteDisplayString, sizeof( level.voteDisplayString ), "%s", level.voteString );
01303     }
01304 
01305     trap_SendServerCommand( -1, va("print \"%s called a vote.\n\"", ent->client->pers.netname ) );
01306 
01307     // start the voting, the caller autoamtically votes yes
01308     level.voteTime = level.time;
01309     level.voteYes = 1;
01310     level.voteNo = 0;
01311 
01312     for ( i = 0 ; i < level.maxclients ; i++ ) {
01313         level.clients[i].ps.eFlags &= ~EF_VOTED;
01314     }
01315     ent->client->ps.eFlags |= EF_VOTED;
01316 
01317     trap_SetConfigstring( CS_VOTE_TIME, va("%i", level.voteTime ) );
01318     trap_SetConfigstring( CS_VOTE_STRING, level.voteDisplayString );    
01319     trap_SetConfigstring( CS_VOTE_YES, va("%i", level.voteYes ) );
01320     trap_SetConfigstring( CS_VOTE_NO, va("%i", level.voteNo ) );    
01321 }
01322 
01323 /*
01324 ==================
01325 Cmd_Vote_f
01326 ==================
01327 */
01328 void Cmd_Vote_f( gentity_t *ent ) {
01329     char        msg[64];
01330 
01331     if ( !level.voteTime ) {
01332         trap_SendServerCommand( ent-g_entities, "print \"No vote in progress.\n\"" );
01333         return;
01334     }
01335     if ( ent->client->ps.eFlags & EF_VOTED ) {
01336         trap_SendServerCommand( ent-g_entities, "print \"Vote already cast.\n\"" );
01337         return;
01338     }
01339     if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) {
01340         trap_SendServerCommand( ent-g_entities, "print \"Not allowed to vote as spectator.\n\"" );
01341         return;
01342     }
01343 
01344     trap_SendServerCommand( ent-g_entities, "print \"Vote cast.\n\"" );
01345 
01346     ent->client->ps.eFlags |= EF_VOTED;
01347 
01348     trap_Argv( 1, msg, sizeof( msg ) );
01349 
01350     if ( msg[0] == 'y' || msg[1] == 'Y' || msg[1] == '1' ) {
01351         level.voteYes++;
01352         trap_SetConfigstring( CS_VOTE_YES, va("%i", level.voteYes ) );
01353     } else {
01354         level.voteNo++;
01355         trap_SetConfigstring( CS_VOTE_NO, va("%i", level.voteNo ) );    
01356     }
01357 
01358     // a majority will be determined in CheckVote, which will also account
01359     // for players entering or leaving
01360 }
01361 
01362 /*
01363 ==================
01364 Cmd_CallTeamVote_f
01365 ==================
01366 */
01367 void Cmd_CallTeamVote_f( gentity_t *ent ) {
01368     int     i, team, cs_offset;
01369     char    arg1[MAX_STRING_TOKENS];
01370     char    arg2[MAX_STRING_TOKENS];
01371 
01372     team = ent->client->sess.sessionTeam;
01373     if ( team == TEAM_RED )
01374         cs_offset = 0;
01375     else if ( team == TEAM_BLUE )
01376         cs_offset = 1;
01377     else
01378         return;
01379 
01380     if ( !g_allowVote.integer ) {
01381         trap_SendServerCommand( ent-g_entities, "print \"Voting not allowed here.\n\"" );
01382         return;
01383     }
01384 
01385     if ( level.teamVoteTime[cs_offset] ) {
01386         trap_SendServerCommand( ent-g_entities, "print \"A team vote is already in progress.\n\"" );
01387         return;
01388     }
01389     if ( ent->client->pers.teamVoteCount >= MAX_VOTE_COUNT ) {
01390         trap_SendServerCommand( ent-g_entities, "print \"You have called the maximum number of team votes.\n\"" );
01391         return;
01392     }
01393     if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) {
01394         trap_SendServerCommand( ent-g_entities, "print \"Not allowed to call a vote as spectator.\n\"" );
01395         return;
01396     }
01397 
01398     // make sure it is a valid command to vote on
01399     trap_Argv( 1, arg1, sizeof( arg1 ) );
01400     arg2[0] = '\0';
01401     for ( i = 2; i < trap_Argc(); i++ ) {
01402         if (i > 2)
01403             strcat(arg2, " ");
01404         trap_Argv( i, &arg2[strlen(arg2)], sizeof( arg2 ) - strlen(arg2) );
01405     }
01406 
01407     if( strchr( arg1, ';' ) || strchr( arg2, ';' ) ) {
01408         trap_SendServerCommand( ent-g_entities, "print \"Invalid vote string.\n\"" );
01409         return;
01410     }
01411 
01412     if ( !Q_stricmp( arg1, "leader" ) ) {
01413         char netname[MAX_NETNAME], leader[MAX_NETNAME];
01414 
01415         if ( !arg2[0] ) {
01416             i = ent->client->ps.clientNum;
01417         }
01418         else {
01419             // numeric values are just slot numbers
01420             for (i = 0; i < 3; i++) {
01421                 if ( !arg2[i] || arg2[i] < '0' || arg2[i] > '9' )
01422                     break;
01423             }
01424             if ( i >= 3 || !arg2[i]) {
01425                 i = atoi( arg2 );
01426                 if ( i < 0 || i >= level.maxclients ) {
01427                     trap_SendServerCommand( ent-g_entities, va("print \"Bad client slot: %i\n\"", i) );
01428                     return;
01429                 }
01430 
01431                 if ( !g_entities[i].inuse ) {
01432                     trap_SendServerCommand( ent-g_entities, va("print \"Client %i is not active\n\"", i) );
01433                     return;
01434                 }
01435             }
01436             else {
01437                 Q_strncpyz(leader, arg2, sizeof(leader));
01438                 Q_CleanStr(leader);
01439                 for ( i = 0 ; i < level.maxclients ; i++ ) {
01440                     if ( level.clients[i].pers.connected == CON_DISCONNECTED )
01441                         continue;
01442                     if (level.clients[i].sess.sessionTeam != team)
01443                         continue;
01444                     Q_strncpyz(netname, level.clients[i].pers.netname, sizeof(netname));
01445                     Q_CleanStr(netname);
01446                     if ( !Q_stricmp(netname, leader) ) {
01447                         break;
01448                     }
01449                 }
01450                 if ( i >= level.maxclients ) {
01451                     trap_SendServerCommand( ent-g_entities, va("print \"%s is not a valid player on your team.\n\"", arg2) );
01452                     return;
01453                 }
01454             }
01455         }
01456         Com_sprintf(arg2, sizeof(arg2), "%d", i);
01457     } else {
01458         trap_SendServerCommand( ent-g_entities, "print \"Invalid vote string.\n\"" );
01459         trap_SendServerCommand( ent-g_entities, "print \"Team vote commands are: leader <player>.\n\"" );
01460         return;
01461     }
01462 
01463     Com_sprintf( level.teamVoteString[cs_offset], sizeof( level.teamVoteString[cs_offset] ), "%s %s", arg1, arg2 );
01464 
01465     for ( i = 0 ; i < level.maxclients ; i++ ) {
01466         if ( level.clients[i].pers.connected == CON_DISCONNECTED )
01467             continue;
01468         if (level.clients[i].sess.sessionTeam == team)
01469             trap_SendServerCommand( i, va("print \"%s called a team vote.\n\"", ent->client->pers.netname ) );
01470     }
01471 
01472     // start the voting, the caller autoamtically votes yes
01473     level.teamVoteTime[cs_offset] = level.time;
01474     level.teamVoteYes[cs_offset] = 1;
01475     level.teamVoteNo[cs_offset] = 0;
01476 
01477     for ( i = 0 ; i < level.maxclients ; i++ ) {
01478         if (level.clients[i].sess.sessionTeam == team)
01479             level.clients[i].ps.eFlags &= ~EF_TEAMVOTED;
01480     }
01481     ent->client->ps.eFlags |= EF_TEAMVOTED;
01482 
01483     trap_SetConfigstring( CS_TEAMVOTE_TIME + cs_offset, va("%i", level.teamVoteTime[cs_offset] ) );
01484     trap_SetConfigstring( CS_TEAMVOTE_STRING + cs_offset, level.teamVoteString[cs_offset] );
01485     trap_SetConfigstring( CS_TEAMVOTE_YES + cs_offset, va("%i", level.teamVoteYes[cs_offset] ) );
01486     trap_SetConfigstring( CS_TEAMVOTE_NO + cs_offset, va("%i", level.teamVoteNo[cs_offset] ) );
01487 }
01488 
01489 /*
01490 ==================
01491 Cmd_TeamVote_f
01492 ==================
01493 */
01494 void Cmd_TeamVote_f( gentity_t *ent ) {
01495     int         team, cs_offset;
01496     char        msg[64];
01497 
01498     team = ent->client->sess.sessionTeam;
01499     if ( team == TEAM_RED )
01500         cs_offset = 0;
01501     else if ( team == TEAM_BLUE )
01502         cs_offset = 1;
01503     else
01504         return;
01505 
01506     if ( !level.teamVoteTime[cs_offset] ) {
01507         trap_SendServerCommand( ent-g_entities, "print \"No team vote in progress.\n\"" );
01508         return;
01509     }
01510     if ( ent->client->ps.eFlags & EF_TEAMVOTED ) {
01511         trap_SendServerCommand( ent-g_entities, "print \"Team vote already cast.\n\"" );
01512         return;
01513     }
01514     if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) {
01515         trap_SendServerCommand( ent-g_entities, "print \"Not allowed to vote as spectator.\n\"" );
01516         return;
01517     }
01518 
01519     trap_SendServerCommand( ent-g_entities, "print \"Team vote cast.\n\"" );
01520 
01521     ent->client->ps.eFlags |= EF_TEAMVOTED;
01522 
01523     trap_Argv( 1, msg, sizeof( msg ) );
01524 
01525     if ( msg[0] == 'y' || msg[1] == 'Y' || msg[1] == '1' ) {
01526         level.teamVoteYes[cs_offset]++;
01527         trap_SetConfigstring( CS_TEAMVOTE_YES + cs_offset, va("%i", level.teamVoteYes[cs_offset] ) );
01528     } else {
01529         level.teamVoteNo[cs_offset]++;
01530         trap_SetConfigstring( CS_TEAMVOTE_NO + cs_offset, va("%i", level.teamVoteNo[cs_offset] ) ); 
01531     }
01532 
01533     // a majority will be determined in TeamCheckVote, which will also account
01534     // for players entering or leaving
01535 }
01536 
01537 
01538 /*
01539 =================
01540 Cmd_SetViewpos_f
01541 =================
01542 */
01543 void Cmd_SetViewpos_f( gentity_t *ent ) {
01544     vec3_t      origin, angles;
01545     char        buffer[MAX_TOKEN_CHARS];
01546     int         i;
01547 
01548     if ( !g_cheats.integer ) {
01549         trap_SendServerCommand( ent-g_entities, va("print \"Cheats are not enabled on this server.\n\""));
01550         return;
01551     }
01552     if ( trap_Argc() != 5 ) {
01553         trap_SendServerCommand( ent-g_entities, va("print \"usage: setviewpos x y z yaw\n\""));
01554         return;
01555     }
01556 
01557     VectorClear( angles );
01558     for ( i = 0 ; i < 3 ; i++ ) {
01559         trap_Argv( i + 1, buffer, sizeof( buffer ) );
01560         origin[i] = atof( buffer );
01561     }
01562 
01563     trap_Argv( 4, buffer, sizeof( buffer ) );
01564     angles[YAW] = atof( buffer );
01565 
01566     TeleportPlayer( ent, origin, angles );
01567 }
01568 
01569 
01570 
01571 /*
01572 =================
01573 Cmd_Stats_f
01574 =================
01575 */
01576 void Cmd_Stats_f( gentity_t *ent ) {
01577 /*
01578     int max, n, i;
01579 
01580     max = trap_AAS_PointReachabilityAreaIndex( NULL );
01581 
01582     n = 0;
01583     for ( i = 0; i < max; i++ ) {
01584         if ( ent->client->areabits[i >> 3] & (1 << (i & 7)) )
01585             n++;
01586     }
01587 
01588     //trap_SendServerCommand( ent-g_entities, va("print \"visited %d of %d areas\n\"", n, max));
01589     trap_SendServerCommand( ent-g_entities, va("print \"%d%% level coverage\n\"", n * 100 / max));
01590 */
01591 }
01592 
01593 /*
01594 =================
01595 ClientCommand
01596 =================
01597 */
01598 void ClientCommand( int clientNum ) {
01599     gentity_t *ent;
01600     char    cmd[MAX_TOKEN_CHARS];
01601 
01602     ent = g_entities + clientNum;
01603     if ( !ent->client ) {
01604         return;     // not fully in game yet
01605     }
01606 
01607 
01608     trap_Argv( 0, cmd, sizeof( cmd ) );
01609 
01610     if (Q_stricmp (cmd, "say") == 0) {
01611         Cmd_Say_f (ent, SAY_ALL, qfalse);
01612         return;
01613     }
01614     if (Q_stricmp (cmd, "say_team") == 0) {
01615         Cmd_Say_f (ent, SAY_TEAM, qfalse);
01616         return;
01617     }
01618     if (Q_stricmp (cmd, "tell") == 0) {
01619         Cmd_Tell_f ( ent );
01620         return;
01621     }
01622     if (Q_stricmp (cmd, "vsay") == 0) {
01623         Cmd_Voice_f (ent, SAY_ALL, qfalse, qfalse);
01624         return;
01625     }
01626     if (Q_stricmp (cmd, "vsay_team") == 0) {
01627         Cmd_Voice_f (ent, SAY_TEAM, qfalse, qfalse);
01628         return;
01629     }
01630     if (Q_stricmp (cmd, "vtell") == 0) {
01631         Cmd_VoiceTell_f ( ent, qfalse );
01632         return;
01633     }
01634     if (Q_stricmp (cmd, "vosay") == 0) {
01635         Cmd_Voice_f (ent, SAY_ALL, qfalse, qtrue);
01636         return;
01637     }
01638     if (Q_stricmp (cmd, "vosay_team") == 0) {
01639         Cmd_Voice_f (ent, SAY_TEAM, qfalse, qtrue);
01640         return;
01641     }
01642     if (Q_stricmp (cmd, "votell") == 0) {
01643         Cmd_VoiceTell_f ( ent, qtrue );
01644         return;
01645     }
01646     if (Q_stricmp (cmd, "vtaunt") == 0) {
01647         Cmd_VoiceTaunt_f ( ent );
01648         return;
01649     }
01650     if (Q_stricmp (cmd, "score") == 0) {
01651         Cmd_Score_f (ent);
01652         return;
01653     }
01654 
01655     // ignore all other commands when at intermission
01656     if (level.intermissiontime) {
01657         Cmd_Say_f (ent, qfalse, qtrue);
01658         return;
01659     }
01660 
01661     if (Q_stricmp (cmd, "give") == 0)
01662         Cmd_Give_f (ent);
01663     else if (Q_stricmp (cmd, "god") == 0)
01664         Cmd_God_f (ent);
01665     else if (Q_stricmp (cmd, "notarget") == 0)
01666         Cmd_Notarget_f (ent);
01667     else if (Q_stricmp (cmd, "noclip") == 0)
01668         Cmd_Noclip_f (ent);
01669     else if (Q_stricmp (cmd, "kill") == 0)
01670         Cmd_Kill_f (ent);
01671     else if (Q_stricmp (cmd, "teamtask") == 0)
01672         Cmd_TeamTask_f (ent);
01673     else if (Q_stricmp (cmd, "levelshot") == 0)
01674         Cmd_LevelShot_f (ent);
01675     else if (Q_stricmp (cmd, "follow") == 0)
01676         Cmd_Follow_f (ent);
01677     else if (Q_stricmp (cmd, "follownext") == 0)
01678         Cmd_FollowCycle_f (ent, 1);
01679     else if (Q_stricmp (cmd, "followprev") == 0)
01680         Cmd_FollowCycle_f (ent, -1);
01681     else if (Q_stricmp (cmd, "team") == 0)
01682         Cmd_Team_f (ent);
01683     else if (Q_stricmp (cmd, "where") == 0)
01684         Cmd_Where_f (ent);
01685     else if (Q_stricmp (cmd, "callvote") == 0)
01686         Cmd_CallVote_f (ent);
01687     else if (Q_stricmp (cmd, "vote") == 0)
01688         Cmd_Vote_f (ent);
01689     else if (Q_stricmp (cmd, "callteamvote") == 0)
01690         Cmd_CallTeamVote_f (ent);
01691     else if (Q_stricmp (cmd, "teamvote") == 0)
01692         Cmd_TeamVote_f (ent);
01693     else if (Q_stricmp (cmd, "gc") == 0)
01694         Cmd_GameCommand_f( ent );
01695     else if (Q_stricmp (cmd, "setviewpos") == 0)
01696         Cmd_SetViewpos_f( ent );
01697     else if (Q_stricmp (cmd, "stats") == 0)
01698         Cmd_Stats_f( ent );
01699     else
01700         trap_SendServerCommand( clientNum, va("print \"unknown cmd %s\n\"", cmd ) );
01701 }

Generated on Thu Aug 25 12:37:31 2005 for Quake III Arena by  doxygen 1.3.9.1