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[PERS_SCORE] = preScore;
01255 
01256         ent->client->switchClassTime = level.time + 5000;
01257 }
01258 
01259 /*
01260 =================
01261 Cmd_ForceChanged_f
01262 =================
01263 */
01264 void Cmd_ForceChanged_f( gentity_t *ent )
01265 {
01266         char fpChStr[1024];
01267         const char *buf;
01268 //      Cmd_Kill_f(ent);
01269         if (ent->client->sess.sessionTeam == TEAM_SPECTATOR)
01270         { //if it's a spec, just make the changes now
01271                 //trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", G_GetStringEdString("MP_SVGAME", "FORCEAPPLIED")) );
01272                 //No longer print it, as the UI calls this a lot.
01273                 WP_InitForcePowers( ent );
01274                 goto argCheck;
01275         }
01276 
01277         buf = G_GetStringEdString("MP_SVGAME", "FORCEPOWERCHANGED");
01278 
01279         strcpy(fpChStr, buf);
01280 
01281         trap_SendServerCommand( ent-g_entities, va("print \"%s%s\n\n\"", S_COLOR_GREEN, fpChStr) );
01282 
01283         ent->client->ps.fd.forceDoInit = 1;
01284 argCheck:
01285         if (g_gametype.integer == GT_DUEL || g_gametype.integer == GT_POWERDUEL)
01286         { //If this is duel, don't even bother changing team in relation to this.
01287                 return;
01288         }
01289 
01290         if (trap_Argc() > 1)
01291         {
01292                 char    arg[MAX_TOKEN_CHARS];
01293 
01294                 trap_Argv( 1, arg, sizeof( arg ) );
01295 
01296                 if (arg && arg[0])
01297                 { //if there's an arg, assume it's a combo team command from the UI.
01298                         Cmd_Team_f(ent);
01299                 }
01300         }
01301 }
01302 
01303 extern qboolean WP_SaberStyleValidForSaber( saberInfo_t *saber1, saberInfo_t *saber2, int saberHolstered, int saberAnimLevel );
01304 extern qboolean WP_UseFirstValidSaberStyle( saberInfo_t *saber1, saberInfo_t *saber2, int saberHolstered, int *saberAnimLevel );
01305 qboolean G_SetSaber(gentity_t *ent, int saberNum, char *saberName, qboolean siegeOverride)
01306 {
01307         char truncSaberName[64];
01308         int i = 0;
01309 
01310         if (!siegeOverride &&
01311                 g_gametype.integer == GT_SIEGE &&
01312                 ent->client->siegeClass != -1 &&
01313                 (
01314                  bgSiegeClasses[ent->client->siegeClass].saberStance ||
01315                  bgSiegeClasses[ent->client->siegeClass].saber1[0] ||
01316                  bgSiegeClasses[ent->client->siegeClass].saber2[0]
01317                 ))
01318         { //don't let it be changed if the siege class has forced any saber-related things
01319         return qfalse;
01320         }
01321 
01322         while (saberName[i] && i < 64-1)
01323         {
01324         truncSaberName[i] = saberName[i];
01325                 i++;
01326         }
01327         truncSaberName[i] = 0;
01328 
01329         if ( saberNum == 0 && (Q_stricmp( "none", truncSaberName ) == 0 || Q_stricmp( "remove", truncSaberName ) == 0) )
01330         { //can't remove saber 0 like this
01331         strcpy(truncSaberName, "Kyle");
01332         }
01333 
01334         //Set the saber with the arg given. If the arg is
01335         //not a valid sabername defaults will be used.
01336         WP_SetSaber(ent->s.number, ent->client->saber, saberNum, truncSaberName);
01337 
01338         if (!ent->client->saber[0].model[0])
01339         {
01340                 assert(0); //should never happen!
01341                 strcpy(ent->client->sess.saberType, "none");
01342         }
01343         else
01344         {
01345                 strcpy(ent->client->sess.saberType, ent->client->saber[0].name);
01346         }
01347 
01348         if (!ent->client->saber[1].model[0])
01349         {
01350                 strcpy(ent->client->sess.saber2Type, "none");
01351         }
01352         else
01353         {
01354                 strcpy(ent->client->sess.saber2Type, ent->client->saber[1].name);
01355         }
01356 
01357         if ( !WP_SaberStyleValidForSaber( &ent->client->saber[0], &ent->client->saber[1], ent->client->ps.saberHolstered, ent->client->ps.fd.saberAnimLevel ) )
01358         {
01359                 WP_UseFirstValidSaberStyle( &ent->client->saber[0], &ent->client->saber[1], ent->client->ps.saberHolstered, &ent->client->ps.fd.saberAnimLevel );
01360                 ent->client->ps.fd.saberAnimLevelBase = ent->client->saberCycleQueue = ent->client->ps.fd.saberAnimLevel;
01361         }
01362 
01363         return qtrue;
01364 }
01365 
01366 /*
01367 =================
01368 Cmd_Follow_f
01369 =================
01370 */
01371 void Cmd_Follow_f( gentity_t *ent ) {
01372         int             i;
01373         char    arg[MAX_TOKEN_CHARS];
01374 
01375         if ( trap_Argc() != 2 ) {
01376                 if ( ent->client->sess.spectatorState == SPECTATOR_FOLLOW ) {
01377                         StopFollowing( ent );
01378                 }
01379                 return;
01380         }
01381 
01382         trap_Argv( 1, arg, sizeof( arg ) );
01383         i = ClientNumberFromString( ent, arg );
01384         if ( i == -1 ) {
01385                 return;
01386         }
01387 
01388         // can't follow self
01389         if ( &level.clients[ i ] == ent->client ) {
01390                 return;
01391         }
01392 
01393         // can't follow another spectator
01394         if ( level.clients[ i ].sess.sessionTeam == TEAM_SPECTATOR ) {
01395                 return;
01396         }
01397 
01398         // if they are playing a tournement game, count as a loss
01399         if ( (g_gametype.integer == GT_DUEL || g_gametype.integer == GT_POWERDUEL)
01400                 && ent->client->sess.sessionTeam == TEAM_FREE ) {
01401                 //WTF???
01402                 ent->client->sess.losses++;
01403         }
01404 
01405         // first set them to spectator
01406         if ( ent->client->sess.sessionTeam != TEAM_SPECTATOR ) {
01407                 SetTeam( ent, "spectator" );
01408         }
01409 
01410         ent->client->sess.spectatorState = SPECTATOR_FOLLOW;
01411         ent->client->sess.spectatorClient = i;
01412 }
01413 
01414 /*
01415 =================
01416 Cmd_FollowCycle_f
01417 =================
01418 */
01419 void Cmd_FollowCycle_f( gentity_t *ent, int dir ) {
01420         int             clientnum;
01421         int             original;
01422 
01423         // if they are playing a tournement game, count as a loss
01424         if ( (g_gametype.integer == GT_DUEL || g_gametype.integer == GT_POWERDUEL)
01425                 && ent->client->sess.sessionTeam == TEAM_FREE ) {\
01426                 //WTF???
01427                 ent->client->sess.losses++;
01428         }
01429         // first set them to spectator
01430         if ( ent->client->sess.spectatorState == SPECTATOR_NOT ) {
01431                 SetTeam( ent, "spectator" );
01432         }
01433 
01434         if ( dir != 1 && dir != -1 ) {
01435                 G_Error( "Cmd_FollowCycle_f: bad dir %i", dir );
01436         }
01437 
01438         clientnum = ent->client->sess.spectatorClient;
01439         original = clientnum;
01440         do {
01441                 clientnum += dir;
01442                 if ( clientnum >= level.maxclients ) {
01443                         clientnum = 0;
01444                 }
01445                 if ( clientnum < 0 ) {
01446                         clientnum = level.maxclients - 1;
01447                 }
01448 
01449                 // can only follow connected clients
01450                 if ( level.clients[ clientnum ].pers.connected != CON_CONNECTED ) {
01451                         continue;
01452                 }
01453 
01454                 // can't follow another spectator
01455                 if ( level.clients[ clientnum ].sess.sessionTeam == TEAM_SPECTATOR ) {
01456                         continue;
01457                 }
01458 
01459                 // this is good, we can use it
01460                 ent->client->sess.spectatorClient = clientnum;
01461                 ent->client->sess.spectatorState = SPECTATOR_FOLLOW;
01462                 return;
01463         } while ( clientnum != original );
01464 
01465         // leave it where it was
01466 }
01467 
01468 
01469 /*
01470 ==================
01471 G_Say
01472 ==================
01473 */
01474 
01475 static void G_SayTo( gentity_t *ent, gentity_t *other, int mode, int color, const char *name, const char *message, char *locMsg )
01476 {
01477         if (!other) {
01478                 return;
01479         }
01480         if (!other->inuse) {
01481                 return;
01482         }
01483         if (!other->client) {
01484                 return;
01485         }
01486         if ( other->client->pers.connected != CON_CONNECTED ) {
01487                 return;
01488         }
01489         if ( mode == SAY_TEAM  && !OnSameTeam(ent, other) ) {
01490                 return;
01491         }
01492         /*
01493         // no chatting to players in tournements
01494         if ( (g_gametype.integer == GT_DUEL || g_gametype.integer == GT_POWERDUEL)
01495                 && other->client->sess.sessionTeam == TEAM_FREE
01496                 && ent->client->sess.sessionTeam != TEAM_FREE ) {
01497                 //Hmm, maybe some option to do so if allowed?  Or at least in developer mode...
01498                 return;
01499         }
01500         */
01501         //They've requested I take this out.
01502 
01503         if (g_gametype.integer == GT_SIEGE &&
01504                 ent->client && (ent->client->tempSpectate >= level.time || ent->client->sess.sessionTeam == TEAM_SPECTATOR) &&
01505                 other->client->sess.sessionTeam != TEAM_SPECTATOR &&
01506                 other->client->tempSpectate < level.time)
01507         { //siege temp spectators should not communicate to ingame players
01508                 return;
01509         }
01510 
01511         if (locMsg)
01512         {
01513                 trap_SendServerCommand( other-g_entities, va("%s \"%s\" \"%s\" \"%c\" \"%s\"", 
01514                         mode == SAY_TEAM ? "ltchat" : "lchat",
01515                         name, locMsg, color, message));
01516         }
01517         else
01518         {
01519                 trap_SendServerCommand( other-g_entities, va("%s \"%s%c%c%s\"", 
01520                         mode == SAY_TEAM ? "tchat" : "chat",
01521                         name, Q_COLOR_ESCAPE, color, message));
01522         }
01523 }
01524 
01525 #define EC              "\x19"
01526 
01527 void G_Say( gentity_t *ent, gentity_t *target, int mode, const char *chatText ) {
01528         int                     j;
01529         gentity_t       *other;
01530         int                     color;
01531         char            name[64];
01532         // don't let text be too long for malicious reasons
01533         char            text[MAX_SAY_TEXT];
01534         char            location[64];
01535         char            *locMsg = NULL;
01536 
01537         if ( g_gametype.integer < GT_TEAM && mode == SAY_TEAM ) {
01538                 mode = SAY_ALL;
01539         }
01540 
01541         switch ( mode ) {
01542         default:
01543         case SAY_ALL:
01544                 G_LogPrintf( "say: %s: %s\n", ent->client->pers.netname, chatText );
01545                 Com_sprintf (name, sizeof(name), "%s%c%c"EC": ", ent->client->pers.netname, Q_COLOR_ESCAPE, COLOR_WHITE );
01546                 color = COLOR_GREEN;
01547                 break;
01548         case SAY_TEAM:
01549                 G_LogPrintf( "sayteam: %s: %s\n", ent->client->pers.netname, chatText );
01550                 if (Team_GetLocationMsg(ent, location, sizeof(location)))
01551                 {
01552                         Com_sprintf (name, sizeof(name), EC"(%s%c%c"EC")"EC": ", 
01553                                 ent->client->pers.netname, Q_COLOR_ESCAPE, COLOR_WHITE );
01554                         locMsg = location;
01555                 }
01556                 else
01557                 {
01558                         Com_sprintf (name, sizeof(name), EC"(%s%c%c"EC")"EC": ", 
01559                                 ent->client->pers.netname, Q_COLOR_ESCAPE, COLOR_WHITE );
01560                 }
01561                 color = COLOR_CYAN;
01562                 break;
01563         case SAY_TELL:
01564                 if (target && g_gametype.integer >= GT_TEAM &&
01565                         target->client->sess.sessionTeam == ent->client->sess.sessionTeam &&
01566                         Team_GetLocationMsg(ent, location, sizeof(location)))
01567                 {
01568                         Com_sprintf (name, sizeof(name), EC"[%s%c%c"EC"]"EC": ", ent->client->pers.netname, Q_COLOR_ESCAPE, COLOR_WHITE );
01569                         locMsg = location;
01570                 }
01571                 else
01572                 {
01573                         Com_sprintf (name, sizeof(name), EC"[%s%c%c"EC"]"EC": ", ent->client->pers.netname, Q_COLOR_ESCAPE, COLOR_WHITE );
01574                 }
01575                 color = COLOR_MAGENTA;
01576                 break;
01577         }
01578 
01579         Q_strncpyz( text, chatText, sizeof(text) );
01580 
01581         if ( target ) {
01582                 G_SayTo( ent, target, mode, color, name, text, locMsg );
01583                 return;
01584         }
01585 
01586         // echo the text to the console
01587         if ( g_dedicated.integer ) {
01588                 G_Printf( "%s%s\n", name, text);
01589         }
01590 
01591         // send it to all the apropriate clients
01592         for (j = 0; j < level.maxclients; j++) {
01593                 other = &g_entities[j];
01594                 G_SayTo( ent, other, mode, color, name, text, locMsg );
01595         }
01596 }
01597 
01598 
01599 /*
01600 ==================
01601 Cmd_Say_f
01602 ==================
01603 */
01604 static void Cmd_Say_f( gentity_t *ent, int mode, qboolean arg0 ) {
01605         char            *p;
01606 
01607         if ( trap_Argc () < 2 && !arg0 ) {
01608                 return;
01609         }
01610 
01611         if (arg0)
01612         {
01613                 p = ConcatArgs( 0 );
01614         }
01615         else
01616         {
01617                 p = ConcatArgs( 1 );
01618         }
01619 
01620         G_Say( ent, NULL, mode, p );
01621 }
01622 
01623 /*
01624 ==================
01625 Cmd_Tell_f
01626 ==================
01627 */
01628 static void Cmd_Tell_f( gentity_t *ent ) {
01629         int                     targetNum;
01630         gentity_t       *target;
01631         char            *p;
01632         char            arg[MAX_TOKEN_CHARS];
01633 
01634         if ( trap_Argc () < 2 ) {
01635                 return;
01636         }
01637 
01638         trap_Argv( 1, arg, sizeof( arg ) );
01639         targetNum = atoi( arg );
01640         if ( targetNum < 0 || targetNum >= level.maxclients ) {
01641                 return;
01642         }
01643 
01644         target = &g_entities[targetNum];
01645         if ( !target || !target->inuse || !target->client ) {
01646                 return;
01647         }
01648 
01649         p = ConcatArgs( 2 );
01650 
01651         G_LogPrintf( "tell: %s to %s: %s\n", ent->client->pers.netname, target->client->pers.netname, p );
01652         G_Say( ent, target, SAY_TELL, p );
01653         // don't tell to the player self if it was already directed to this player
01654         // also don't send the chat back to a bot
01655         if ( ent != target && !(ent->r.svFlags & SVF_BOT)) {
01656                 G_Say( ent, ent, SAY_TELL, p );
01657         }
01658 }
01659 
01660 //siege voice command
01661 static void Cmd_VoiceCommand_f(gentity_t *ent)
01662 {
01663         gentity_t *te;
01664         char arg[MAX_TOKEN_CHARS];
01665         char *s;
01666         int i = 0;
01667 
01668         if (g_gametype.integer < GT_TEAM)
01669         {
01670                 return;
01671         }
01672 
01673         if (trap_Argc() < 2)
01674         {
01675                 return;
01676         }
01677 
01678         if (ent->client->sess.sessionTeam == TEAM_SPECTATOR ||
01679                 ent->client->tempSpectate >= level.time)
01680         {
01681                 trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", G_GetStringEdString("MP_SVGAME", "NOVOICECHATASSPEC")) );
01682                 return;
01683         }
01684 
01685         trap_Argv(1, arg, sizeof(arg));
01686 
01687         if (arg[0] == '*')
01688         { //hmm.. don't expect a * to be prepended already. maybe someone is trying to be sneaky.
01689                 return;
01690         }
01691 
01692         s = va("*%s", arg);
01693 
01694         //now, make sure it's a valid sound to be playing like this.. so people can't go around
01695         //screaming out death sounds or whatever.
01696         while (i < MAX_CUSTOM_SIEGE_SOUNDS)
01697         {
01698                 if (!bg_customSiegeSoundNames[i])
01699                 {
01700                         break;
01701                 }
01702                 if (!Q_stricmp(bg_customSiegeSoundNames[i], s))
01703                 { //it matches this one, so it's ok
01704                         break;
01705                 }
01706                 i++;
01707         }
01708 
01709         if (i == MAX_CUSTOM_SIEGE_SOUNDS || !bg_customSiegeSoundNames[i])
01710         { //didn't find it in the list
01711                 return;
01712         }
01713 
01714         te = G_TempEntity(vec3_origin, EV_VOICECMD_SOUND);
01715         te->s.groundEntityNum = ent->s.number;
01716         te->s.eventParm = G_SoundIndex((char *)bg_customSiegeSoundNames[i]);
01717         te->r.svFlags |= SVF_BROADCAST;
01718 }
01719 
01720 
01721 static char     *gc_orders[] = {
01722         "hold your position",
01723         "hold this position",
01724         "come here",
01725         "cover me",
01726         "guard location",
01727         "search and destroy",
01728         "report"
01729 };
01730 
01731 void Cmd_GameCommand_f( gentity_t *ent ) {
01732         int             player;
01733         int             order;
01734         char    str[MAX_TOKEN_CHARS];
01735 
01736         trap_Argv( 1, str, sizeof( str ) );
01737         player = atoi( str );
01738         trap_Argv( 2, str, sizeof( str ) );
01739         order = atoi( str );
01740 
01741         if ( player < 0 || player >= MAX_CLIENTS ) {
01742                 return;
01743         }
01744         if ( order < 0 || order > sizeof(gc_orders)/sizeof(char *) ) {
01745                 return;
01746         }
01747         G_Say( ent, &g_entities[player], SAY_TELL, gc_orders[order] );
01748         G_Say( ent, ent, SAY_TELL, gc_orders[order] );
01749 }
01750 
01751 /*
01752 ==================
01753 Cmd_Where_f
01754 ==================
01755 */
01756 void Cmd_Where_f( gentity_t *ent ) {
01757         trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", vtos( ent->s.origin ) ) );
01758 }
01759 
01760 static const char *gameNames[] = {
01761         "Free For All",
01762         "Holocron FFA",
01763         "Jedi Master",
01764         "Duel",
01765         "Power Duel",
01766         "Single Player",
01767         "Team FFA",
01768         "Siege",
01769         "Capture the Flag",
01770         "Capture the Ysalamiri"
01771 };
01772 
01773 /*
01774 ==================
01775 G_ClientNumberFromName
01776 
01777 Finds the client number of the client with the given name
01778 ==================
01779 */
01780 int G_ClientNumberFromName ( const char* name )
01781 {
01782         char            s2[MAX_STRING_CHARS];
01783         char            n2[MAX_STRING_CHARS];
01784         int                     i;
01785         gclient_t*      cl;
01786 
01787         // check for a name match
01788         SanitizeString( (char*)name, s2 );
01789         for ( i=0, cl=level.clients ; i < level.numConnectedClients ; i++, cl++ ) 
01790         {
01791                 SanitizeString( cl->pers.netname, n2 );
01792                 if ( !strcmp( n2, s2 ) ) 
01793                 {
01794                         return i;
01795                 }
01796         }
01797 
01798         return -1;
01799 }
01800 
01801 /*
01802 ==================
01803 SanitizeString2
01804 
01805 Rich's revised version of SanitizeString
01806 ==================
01807 */
01808 void SanitizeString2( char *in, char *out )
01809 {
01810         int i = 0;
01811         int r = 0;
01812 
01813         while (in[i])
01814         {
01815                 if (i >= MAX_NAME_LENGTH-1)
01816                 { //the ui truncates the name here..
01817                         break;
01818                 }
01819 
01820                 if (in[i] == '^')
01821                 {
01822                         if (in[i+1] >= 48 && //'0'
01823                                 in[i+1] <= 57) //'9'
01824                         { //only skip it if there's a number after it for the color
01825                                 i += 2;
01826                                 continue;
01827                         }
01828                         else
01829                         { //just skip the ^
01830                                 i++;
01831                                 continue;
01832                         }
01833                 }
01834 
01835                 if (in[i] < 32)
01836                 {
01837                         i++;
01838                         continue;
01839                 }
01840 
01841                 out[r] = in[i];
01842                 r++;
01843                 i++;
01844         }
01845         out[r] = 0;
01846 }
01847 
01848 /*
01849 ==================
01850 G_ClientNumberFromStrippedName
01851 
01852 Same as above, but strips special characters out of the names before comparing.
01853 ==================
01854 */
01855 int G_ClientNumberFromStrippedName ( const char* name )
01856 {
01857         char            s2[MAX_STRING_CHARS];
01858         char            n2[MAX_STRING_CHARS];
01859         int                     i;
01860         gclient_t*      cl;
01861 
01862         // check for a name match
01863         SanitizeString2( (char*)name, s2 );
01864         for ( i=0, cl=level.clients ; i < level.numConnectedClients ; i++, cl++ ) 
01865         {
01866                 SanitizeString2( cl->pers.netname, n2 );
01867                 if ( !strcmp( n2, s2 ) ) 
01868                 {
01869                         return i;
01870                 }
01871         }
01872 
01873         return -1;
01874 }
01875 
01876 /*
01877 ==================
01878 Cmd_CallVote_f
01879 ==================
01880 */
01881 extern void SiegeClearSwitchData(void); //g_saga.c
01882 const char *G_GetArenaInfoByMap( const char *map );
01883 void Cmd_CallVote_f( gentity_t *ent ) {
01884         int             i;
01885         char    arg1[MAX_STRING_TOKENS];
01886         char    arg2[MAX_STRING_TOKENS];
01887 //      int             n = 0;
01888 //      char*   type = NULL;
01889         char*           mapName = 0;
01890         const char*     arenaInfo;
01891 
01892         if ( !g_allowVote.integer ) {
01893                 trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", G_GetStringEdString("MP_SVGAME", "NOVOTE")) );
01894                 return;
01895         }
01896 
01897         if ( level.voteTime || level.voteExecuteTime >= level.time ) {
01898                 trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", G_GetStringEdString("MP_SVGAME", "VOTEINPROGRESS")) );
01899                 return;
01900         }
01901         if ( ent->client->pers.voteCount >= MAX_VOTE_COUNT ) {
01902                 trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", G_GetStringEdString("MP_SVGAME", "MAXVOTES")) );
01903                 return;
01904         }
01905 
01906         if (g_gametype.integer != GT_DUEL &&
01907                 g_gametype.integer != GT_POWERDUEL)
01908         {
01909                 if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) {
01910                         trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", G_GetStringEdString("MP_SVGAME", "NOSPECVOTE")) );
01911                         return;
01912                 }
01913         }
01914 
01915         // make sure it is a valid command to vote on
01916         trap_Argv( 1, arg1, sizeof( arg1 ) );
01917         trap_Argv( 2, arg2, sizeof( arg2 ) );
01918 
01919         if( strchr( arg1, ';' ) || strchr( arg2, ';' ) ) {
01920                 trap_SendServerCommand( ent-g_entities, "print \"Invalid vote string.\n\"" );
01921                 return;
01922         }
01923 
01924         if ( !Q_stricmp( arg1, "map_restart" ) ) {
01925         } else if ( !Q_stricmp( arg1, "nextmap" ) ) {
01926         } else if ( !Q_stricmp( arg1, "map" ) ) {
01927         } else if ( !Q_stricmp( arg1, "g_gametype" ) ) {
01928         } else if ( !Q_stricmp( arg1, "kick" ) ) {
01929         } else if ( !Q_stricmp( arg1, "clientkick" ) ) {
01930         } else if ( !Q_stricmp( arg1, "g_doWarmup" ) ) {
01931         } else if ( !Q_stricmp( arg1, "timelimit" ) ) {
01932         } else if ( !Q_stricmp( arg1, "fraglimit" ) ) {
01933         } else {
01934                 trap_SendServerCommand( ent-g_entities, "print \"Invalid vote string.\n\"" );
01935                 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\"" );
01936                 return;
01937         }
01938 
01939         // if there is still a vote to be executed
01940         if ( level.voteExecuteTime ) {
01941                 level.voteExecuteTime = 0;
01942                 trap_SendConsoleCommand( EXEC_APPEND, va("%s\n", level.voteString ) );
01943         }
01944 
01945         // special case for g_gametype, check for bad values
01946         if ( !Q_stricmp( arg1, "g_gametype" ) )
01947         {
01948                 i = atoi( arg2 );
01949                 if( i == GT_SINGLE_PLAYER || i < GT_FFA || i >= GT_MAX_GAME_TYPE) {
01950                         trap_SendServerCommand( ent-g_entities, "print \"Invalid gametype.\n\"" );
01951                         return;
01952                 }
01953 
01954                 level.votingGametype = qtrue;
01955                 level.votingGametypeTo = i;
01956 
01957                 Com_sprintf( level.voteString, sizeof( level.voteString ), "%s %d", arg1, i );
01958                 Com_sprintf( level.voteDisplayString, sizeof( level.voteDisplayString ), "%s %s", arg1, gameNames[i] );
01959         }
01960         else if ( !Q_stricmp( arg1, "map" ) ) 
01961         {
01962                 // special case for map changes, we want to reset the nextmap setting
01963                 // this allows a player to change maps, but not upset the map rotation
01964                 char    s[MAX_STRING_CHARS];
01965 
01966                 if (!G_DoesMapSupportGametype(arg2, trap_Cvar_VariableIntegerValue("g_gametype")))
01967                 {
01968                         //trap_SendServerCommand( ent-g_entities, "print \"You can't vote for this map, it isn't supported by the current gametype.\n\"" );
01969                         trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", G_GetStringEdString("MP_SVGAME", "NOVOTE_MAPNOTSUPPORTEDBYGAME")) );
01970                         return;
01971                 }
01972 
01973                 trap_Cvar_VariableStringBuffer( "nextmap", s, sizeof(s) );
01974                 if (*s) {
01975                         Com_sprintf( level.voteString, sizeof( level.voteString ), "%s %s; set nextmap \"%s\"", arg1, arg2, s );
01976                 } else {
01977                         Com_sprintf( level.voteString, sizeof( level.voteString ), "%s %s", arg1, arg2 );
01978                 }
01979                 
01980                 arenaInfo       = G_GetArenaInfoByMap(arg2);
01981                 if (arenaInfo)
01982                 {
01983                         mapName = Info_ValueForKey(arenaInfo, "longname");
01984                 }
01985 
01986                 if (!mapName || !mapName[0])
01987                 {
01988                         mapName = "ERROR";
01989                 }
01990 
01991                 Com_sprintf( level.voteDisplayString, sizeof( level.voteDisplayString ), "map %s", mapName);
01992         }
01993         else if ( !Q_stricmp ( arg1, "clientkick" ) )
01994         {
01995                 int n = atoi ( arg2 );
01996 
01997                 if ( n < 0 || n >= MAX_CLIENTS )
01998                 {
01999                         trap_SendServerCommand( ent-g_entities, va("print \"invalid client number %d.\n\"", n ) );
02000                         return;
02001                 }
02002 
02003                 if ( g_entities[n].client->pers.connected == CON_DISCONNECTED )
02004                 {
02005                         trap_SendServerCommand( ent-g_entities, va("print \"there is no client with the client number %d.\n\"", n ) );
02006                         return;
02007                 }
02008                         
02009                 Com_sprintf ( level.voteString, sizeof(level.voteString ), "%s %s", arg1, arg2 );
02010                 Com_sprintf ( level.voteDisplayString, sizeof(level.voteDisplayString), "kick %s", g_entities[n].client->pers.netname );
02011         }
02012         else if ( !Q_stricmp ( arg1, "kick" ) )
02013         {
02014                 int clientid = G_ClientNumberFromName ( arg2 );
02015 
02016                 if ( clientid == -1 )
02017                 {
02018                         clientid = G_ClientNumberFromStrippedName(arg2);
02019 
02020                         if (clientid == -1)
02021                         {
02022                                 trap_SendServerCommand( ent-g_entities, va("print \"there is no client named '%s' currently on the server.\n\"", arg2 ) );
02023                                 return;
02024                         }
02025                 }
02026 
02027                 Com_sprintf ( level.voteString, sizeof(level.voteString ), "clientkick %d", clientid );
02028                 Com_sprintf ( level.voteDisplayString, sizeof(level.voteDisplayString), "kick %s", g_entities[clientid].client->pers.netname );
02029         }
02030         else if ( !Q_stricmp( arg1, "nextmap" ) ) 
02031         {
02032                 char    s[MAX_STRING_CHARS];
02033 
02034                 trap_Cvar_VariableStringBuffer( "nextmap", s, sizeof(s) );
02035                 if (!*s) {
02036                         trap_SendServerCommand( ent-g_entities, "print \"nextmap not set.\n\"" );
02037                         return;
02038                 }
02039                 SiegeClearSwitchData();
02040                 Com_sprintf( level.voteString, sizeof( level.voteString ), "vstr nextmap");
02041                 Com_sprintf( level.voteDisplayString, sizeof( level.voteDisplayString ), "%s", level.voteString );
02042         } 
02043         else
02044         {
02045                 Com_sprintf( level.voteString, sizeof( level.voteString ), "%s \"%s\"", arg1, arg2 );
02046                 Com_sprintf( level.voteDisplayString, sizeof( level.voteDisplayString ), "%s", level.voteString );
02047         }
02048 
02049         trap_SendServerCommand( -1, va("print \"%s^7 %s\n\"", ent->client->pers.netname, G_GetStringEdString("MP_SVGAME", "PLCALLEDVOTE") ) );
02050 
02051         // start the voting, the caller autoamtically votes yes
02052         level.voteTime = level.time;
02053         level.voteYes = 1;
02054         level.voteNo = 0;
02055 
02056         for ( i = 0 ; i < level.maxclients ; i++ ) {
02057                 level.clients[i].mGameFlags &= ~PSG_VOTED;
02058         }
02059         ent->client->mGameFlags |= PSG_VOTED;
02060 
02061         trap_SetConfigstring( CS_VOTE_TIME, va("%i", level.voteTime ) );
02062         trap_SetConfigstring( CS_VOTE_STRING, level.voteDisplayString );        
02063         trap_SetConfigstring( CS_VOTE_YES, va("%i", level.voteYes ) );
02064         trap_SetConfigstring( CS_VOTE_NO, va("%i", level.voteNo ) );    
02065 }
02066 
02067 /*
02068 ==================
02069 Cmd_Vote_f
02070 ==================
02071 */
02072 void Cmd_Vote_f( gentity_t *ent ) {
02073         char            msg[64];
02074 
02075         if ( !level.voteTime ) {
02076                 trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", G_GetStringEdString("MP_SVGAME", "NOVOTEINPROG")) );
02077                 return;
02078         }
02079         if ( ent->client->mGameFlags & PSG_VOTED ) {
02080                 trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", G_GetStringEdString("MP_SVGAME", "VOTEALREADY")) );
02081                 return;
02082         }
02083         if (g_gametype.integer != GT_DUEL &&
02084                 g_gametype.integer != GT_POWERDUEL)
02085         {
02086                 if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) {
02087                         trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", G_GetStringEdString("MP_SVGAME", "NOVOTEASSPEC")) );
02088                         return;
02089                 }
02090         }
02091 
02092         trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", G_GetStringEdString("MP_SVGAME", "PLVOTECAST")) );
02093 
02094         ent->client->mGameFlags |= PSG_VOTED;
02095 
02096         trap_Argv( 1, msg, sizeof( msg ) );
02097 
02098         if ( msg[0] == 'y' || msg[1] == 'Y' || msg[1] == '1' ) {
02099                 level.voteYes++;
02100                 trap_SetConfigstring( CS_VOTE_YES, va("%i", level.voteYes ) );
02101         } else {
02102                 level.voteNo++;
02103                 trap_SetConfigstring( CS_VOTE_NO, va("%i", level.voteNo ) );    
02104         }
02105 
02106         // a majority will be determined in CheckVote, which will also account
02107         // for players entering or leaving
02108 }
02109 
02110 /*
02111 ==================
02112 Cmd_CallTeamVote_f
02113 ==================
02114 */
02115 void Cmd_CallTeamVote_f( gentity_t *ent ) {
02116         int             i, team, cs_offset;
02117         char    arg1[MAX_STRING_TOKENS];
02118         char    arg2[MAX_STRING_TOKENS];
02119 
02120         team = ent->client->sess.sessionTeam;
02121         if ( team == TEAM_RED )
02122                 cs_offset = 0;
02123         else if ( team == TEAM_BLUE )
02124                 cs_offset = 1;
02125         else
02126                 return;
02127 
02128         if ( !g_allowVote.integer ) {
02129                 trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", G_GetStringEdString("MP_SVGAME", "NOVOTE")) );
02130                 return;
02131         }
02132 
02133         if ( level.teamVoteTime[cs_offset] ) {
02134                 trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", G_GetStringEdString("MP_SVGAME", "TEAMVOTEALREADY")) );
02135                 return;
02136         }
02137         if ( ent->client->pers.teamVoteCount >= MAX_VOTE_COUNT ) {
02138                 trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", G_GetStringEdString("MP_SVGAME", "MAXTEAMVOTES")) );
02139                 return;
02140         }
02141         if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) {
02142                 trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", G_GetStringEdString("MP_SVGAME", "NOSPECVOTE")) );
02143                 return;
02144         }
02145 
02146         // make sure it is a valid command to vote on
02147         trap_Argv( 1, arg1, sizeof( arg1 ) );
02148         arg2[0] = '\0';
02149         for ( i = 2; i < trap_Argc(); i++ ) {
02150                 if (i > 2)
02151                         strcat(arg2, " ");
02152                 trap_Argv( i, &arg2[strlen(arg2)], sizeof( arg2 ) - strlen(arg2) );
02153         }
02154 
02155         if( strchr( arg1, ';' ) || strchr( arg2, ';' ) ) {
02156                 trap_SendServerCommand( ent-g_entities, "print \"Invalid vote string.\n\"" );
02157                 return;
02158         }
02159 
02160         if ( !Q_stricmp( arg1, "leader" ) ) {
02161                 char netname[MAX_NETNAME], leader[MAX_NETNAME];
02162 
02163                 if ( !arg2[0] ) {
02164                         i = ent->client->ps.clientNum;
02165                 }
02166                 else {
02167                         // numeric values are just slot numbers
02168                         for (i = 0; i < 3; i++) {
02169                                 if ( !arg2[i] || arg2[i] < '0' || arg2[i] > '9' )
02170                                         break;
02171                         }
02172                         if ( i >= 3 || !arg2[i]) {
02173                                 i = atoi( arg2 );
02174                                 if ( i < 0 || i >= level.maxclients ) {
02175                                         trap_SendServerCommand( ent-g_entities, va("print \"Bad client slot: %i\n\"", i) );
02176                                         return;
02177                                 }
02178 
02179                                 if ( !g_entities[i].inuse ) {
02180                                         trap_SendServerCommand( ent-g_entities, va("print \"Client %i is not active\n\"", i) );
02181                                         return;
02182                                 }
02183                         }
02184                         else {
02185                                 Q_strncpyz(leader, arg2, sizeof(leader));
02186                                 Q_CleanStr(leader);
02187                                 for ( i = 0 ; i < level.maxclients ; i++ ) {
02188                                         if ( level.clients[i].pers.connected == CON_DISCONNECTED )
02189                                                 continue;
02190                                         if (level.clients[i].sess.sessionTeam != team)
02191                                                 continue;
02192                                         Q_strncpyz(netname, level.clients[i].pers.netname, sizeof(netname));
02193                                         Q_CleanStr(netname);
02194                                         if ( !Q_stricmp(netname, leader) ) {
02195                                                 break;
02196                                         }
02197                                 }
02198                                 if ( i >= level.maxclients ) {
02199                                         trap_SendServerCommand( ent-g_entities, va("print \"%s is not a valid player on your team.\n\"", arg2) );
02200                                         return;
02201                                 }
02202                         }
02203                 }
02204                 Com_sprintf(arg2, sizeof(arg2), "%d", i);
02205         } else {
02206                 trap_SendServerCommand( ent-g_entities, "print \"Invalid vote string.\n\"" );
02207                 trap_SendServerCommand( ent-g_entities, "print \"Team vote commands are: leader <player>.\n\"" );
02208                 return;
02209         }
02210 
02211         Com_sprintf( level.teamVoteString[cs_offset], sizeof( level.teamVoteString[cs_offset] ), "%s %s", arg1, arg2 );
02212 
02213         for ( i = 0 ; i < level.maxclients ; i++ ) {
02214                 if ( level.clients[i].pers.connected == CON_DISCONNECTED )
02215                         continue;
02216                 if (level.clients[i].sess.sessionTeam == team)
02217                         trap_SendServerCommand( i, va("print \"%s called a team vote.\n\"", ent->client->pers.netname ) );
02218         }
02219 
02220         // start the voting, the caller autoamtically votes yes
02221         level.teamVoteTime[cs_offset] = level.time;
02222         level.teamVoteYes[cs_offset] = 1;
02223         level.teamVoteNo[cs_offset] = 0;
02224 
02225         for ( i = 0 ; i < level.maxclients ; i++ ) {
02226                 if (level.clients[i].sess.sessionTeam == team)
02227                         level.clients[i].mGameFlags &= ~PSG_TEAMVOTED;
02228         }
02229         ent->client->mGameFlags |= PSG_TEAMVOTED;
02230 
02231         trap_SetConfigstring( CS_TEAMVOTE_TIME + cs_offset, va("%i", level.teamVoteTime[cs_offset] ) );
02232         trap_SetConfigstring( CS_TEAMVOTE_STRING + cs_offset, level.teamVoteString[cs_offset] );
02233         trap_SetConfigstring( CS_TEAMVOTE_YES + cs_offset, va("%i", level.teamVoteYes[cs_offset] ) );
02234         trap_SetConfigstring( CS_TEAMVOTE_NO + cs_offset, va("%i", level.teamVoteNo[cs_offset] ) );
02235 }
02236 
02237 /*
02238 ==================
02239 Cmd_TeamVote_f
02240 ==================
02241 */
02242 void Cmd_TeamVote_f( gentity_t *ent ) {
02243         int                     team, cs_offset;
02244         char            msg[64];
02245 
02246         team = ent->client->sess.sessionTeam;
02247         if ( team == TEAM_RED )
02248                 cs_offset = 0;
02249         else if ( team == TEAM_BLUE )
02250                 cs_offset = 1;
02251         else
02252                 return;
02253 
02254         if ( !level.teamVoteTime[cs_offset] ) {
02255                 trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", G_GetStringEdString("MP_SVGAME", "NOTEAMVOTEINPROG")) );
02256                 return;
02257         }
02258         if ( ent->client->mGameFlags & PSG_TEAMVOTED ) {
02259                 trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", G_GetStringEdString("MP_SVGAME", "TEAMVOTEALREADYCAST")) );
02260                 return;
02261         }
02262         if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) {
02263                 trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", G_GetStringEdString("MP_SVGAME", "NOVOTEASSPEC")) );
02264                 return;
02265         }
02266 
02267         trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", G_GetStringEdString("MP_SVGAME", "PLTEAMVOTECAST")) );
02268 
02269         ent->client->mGameFlags |= PSG_TEAMVOTED;
02270 
02271         trap_Argv( 1, msg, sizeof( msg ) );
02272 
02273         if ( msg[0] == 'y' || msg[1] == 'Y' || msg[1] == '1' ) {
02274                 level.teamVoteYes[cs_offset]++;
02275                 trap_SetConfigstring( CS_TEAMVOTE_YES + cs_offset, va("%i", level.teamVoteYes[cs_offset] ) );
02276         } else {
02277                 level.teamVoteNo[cs_offset]++;
02278                 trap_SetConfigstring( CS_TEAMVOTE_NO + cs_offset, va("%i", level.teamVoteNo[cs_offset] ) );     
02279         }
02280 
02281         // a majority will be determined in TeamCheckVote, which will also account
02282         // for players entering or leaving
02283 }
02284 
02285 
02286 /*
02287 =================
02288 Cmd_SetViewpos_f
02289 =================
02290 */
02291 void Cmd_SetViewpos_f( gentity_t *ent ) {
02292         vec3_t          origin, angles;
02293         char            buffer[MAX_TOKEN_CHARS];
02294         int                     i;
02295 
02296         if ( !g_cheats.integer ) {
02297                 trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", G_GetStringEdString("MP_SVGAME", "NOCHEATS")));
02298                 return;
02299         }
02300         if ( trap_Argc() != 5 ) {
02301                 trap_SendServerCommand( ent-g_entities, va("print \"usage: setviewpos x y z yaw\n\""));
02302                 return;
02303         }
02304 
02305         VectorClear( angles );
02306         for ( i = 0 ; i < 3 ; i++ ) {
02307                 trap_Argv( i + 1, buffer, sizeof( buffer ) );
02308                 origin[i] = atof( buffer );
02309         }
02310 
02311         trap_Argv( 4, buffer, sizeof( buffer ) );
02312         angles[YAW] = atof( buffer );
02313 
02314         TeleportPlayer( ent, origin, angles );
02315 }
02316 
02317 
02318 
02319 /*
02320 =================
02321 Cmd_Stats_f
02322 =================
02323 */
02324 void Cmd_Stats_f( gentity_t *ent ) {
02325 /*
02326         int max, n, i;
02327 
02328         max = trap_AAS_PointReachabilityAreaIndex( NULL );
02329 
02330         n = 0;
02331         for ( i = 0; i < max; i++ ) {
02332                 if ( ent->client->areabits[i >> 3] & (1 << (i & 7)) )
02333                         n++;
02334         }
02335 
02336         //trap_SendServerCommand( ent-g_entities, va("print \"visited %d of %d areas\n\"", n, max));
02337         trap_SendServerCommand( ent-g_entities, va("print \"%d%% level coverage\n\"", n * 100 / max));
02338 */
02339 }
02340 
02341 int G_ItemUsable(playerState_t *ps, int forcedUse)
02342 {
02343         vec3_t fwd, fwdorg, dest, pos;
02344         vec3_t yawonly;
02345         vec3_t mins, maxs;
02346         vec3_t trtest;
02347         trace_t tr;
02348 
02349         if (ps->m_iVehicleNum)
02350         {
02351                 return 0;
02352         }
02353         
02354         if (ps->pm_flags & PMF_USE_ITEM_HELD)
02355         { //force to let go first
02356                 return 0;
02357         }
02358 
02359         if (!forcedUse)
02360         {
02361                 forcedUse = bg_itemlist[ps->stats[STAT_HOLDABLE_ITEM]].giTag;
02362         }
02363 
02364         if (!BG_IsItemSelectable(ps, forcedUse))
02365         {
02366                 return 0;
02367         }
02368 
02369         switch (forcedUse)
02370         {
02371         case HI_MEDPAC:
02372         case HI_MEDPAC_BIG:
02373                 if (ps->stats[STAT_HEALTH] >= ps->stats[STAT_MAX_HEALTH])
02374                 {
02375                         return 0;
02376                 }
02377 
02378                 if (ps->stats[STAT_HEALTH] <= 0)
02379                 {
02380                         return 0;
02381                 }
02382 
02383                 return 1;
02384         case HI_SEEKER:
02385                 if (ps->eFlags & EF_SEEKERDRONE)
02386                 {
02387                         G_AddEvent(&g_entities[ps->clientNum], EV_ITEMUSEFAIL, SEEKER_ALREADYDEPLOYED);
02388                         return 0;
02389                 }
02390 
02391                 return 1;
02392         case HI_SENTRY_GUN:
02393                 if (ps->fd.sentryDeployed)
02394                 {
02395                         G_AddEvent(&g_entities[ps->clientNum], EV_ITEMUSEFAIL, SENTRY_ALREADYPLACED);
02396                         return 0;
02397                 }
02398 
02399                 yawonly[ROLL] = 0;
02400                 yawonly[PITCH] = 0;
02401                 yawonly[YAW] = ps->viewangles[YAW];
02402 
02403                 VectorSet( mins, -8, -8, 0 );
02404                 VectorSet( maxs, 8, 8, 24 );
02405 
02406                 AngleVectors(yawonly, fwd, NULL, NULL);
02407 
02408                 fwdorg[0] = ps->origin[0] + fwd[0]*64;
02409                 fwdorg[1] = ps->origin[1] + fwd[1]*64;
02410                 fwdorg[2] = ps->origin[2] + fwd[2]*64;
02411 
02412                 trtest[0] = fwdorg[0] + fwd[0]*16;
02413                 trtest[1] = fwdorg[1] + fwd[1]*16;
02414                 trtest[2] = fwdorg[2] + fwd[2]*16;
02415 
02416                 trap_Trace(&tr, ps->origin, mins, maxs, trtest, ps->clientNum, MASK_PLAYERSOLID);
02417 
02418                 if ((tr.fraction != 1 && tr.entityNum != ps->clientNum) || tr.startsolid || tr.allsolid)
02419                 {
02420                         G_AddEvent(&g_entities[ps->clientNum], EV_ITEMUSEFAIL, SENTRY_NOROOM);
02421                         return 0;
02422                 }
02423 
02424                 return 1;
02425         case HI_SHIELD:
02426                 mins[0] = -8;
02427                 mins[1] = -8;
02428                 mins[2] = 0;
02429 
02430                 maxs[0] = 8;
02431                 maxs[1] = 8;
02432                 maxs[2] = 8;
02433 
02434                 AngleVectors (ps->viewangles, fwd, NULL, NULL);
02435                 fwd[2] = 0;
02436                 VectorMA(ps->origin, 64, fwd, dest);
02437                 trap_Trace(&tr, ps->origin, mins, maxs, dest, ps->clientNum, MASK_SHOT );
02438                 if (tr.fraction > 0.9 && !tr.startsolid && !tr.allsolid)
02439                 {
02440                         VectorCopy(tr.endpos, pos);
02441                         VectorSet( dest, pos[0], pos[1], pos[2] - 4096 );
02442                         trap_Trace( &tr, pos, mins, maxs, dest, ps->clientNum, MASK_SOLID );
02443                         if ( !tr.startsolid && !tr.allsolid )
02444                         {
02445                                 return 1;
02446                         }
02447                 }
02448                 G_AddEvent(&g_entities[ps->clientNum], EV_ITEMUSEFAIL, SHIELD_NOROOM);
02449                 return 0;
02450         case HI_JETPACK: //do something?
02451                 return 1;
02452         case HI_HEALTHDISP:
02453                 return 1;
02454         case HI_AMMODISP:
02455                 return 1;
02456         case HI_EWEB:
02457                 return 1;
02458         case HI_CLOAK:
02459                 return 1;
02460         default:
02461                 return 1;
02462         }
02463 }
02464 
02465 void saberKnockDown(gentity_t *saberent, gentity_t *saberOwner, gentity_t *other);
02466 
02467 void Cmd_ToggleSaber_f(gentity_t *ent)
02468 {
02469         if (ent->client->ps.fd.forceGripCripple)
02470         { //if they are being gripped, don't let them unholster their saber
02471                 if (ent->client->ps.saberHolstered)
02472                 {
02473                         return;
02474                 }
02475         }
02476 
02477         if (ent->client->ps.saberInFlight)
02478         {
02479                 if (ent->client->ps.saberEntityNum)
02480                 { //turn it off in midair
02481                         saberKnockDown(&g_entities[ent->client->ps.saberEntityNum], ent, ent);
02482                 }
02483                 return;
02484         }
02485 
02486         if (ent->client->ps.forceHandExtend != HANDEXTEND_NONE)
02487         {
02488                 return;
02489         }
02490 
02491         if (ent->client->ps.weapon != WP_SABER)
02492         {
02493                 return;
02494         }
02495 
02496 //      if (ent->client->ps.duelInProgress && !ent->client->ps.saberHolstered)
02497 //      {
02498 //              return;
02499 //      }
02500 
02501         if (ent->client->ps.duelTime >= level.time)
02502         {
02503                 return;
02504         }
02505 
02506         if (ent->client->ps.saberLockTime >= level.time)
02507         {
02508                 return;
02509         }
02510 
02511         if (ent->client && ent->client->ps.weaponTime < 1)
02512         {
02513                 if (ent->client->ps.saberHolstered == 2)
02514                 {
02515                         ent->client->ps.saberHolstered = 0;
02516 
02517                         if (ent->client->saber[0].soundOn)
02518                         {
02519                                 G_Sound(ent, CHAN_AUTO, ent->client->saber[0].soundOn);
02520                         }
02521                         if (ent->client->saber[1].soundOn)
02522                         {
02523                                 G_Sound(ent, CHAN_AUTO, ent->client->saber[1].soundOn);
02524                         }
02525                 }
02526                 else
02527                 {
02528                         ent->client->ps.saberHolstered = 2;
02529                         if (ent->client->saber[0].soundOff)
02530                         {
02531                                 G_Sound(ent, CHAN_AUTO, ent->client->saber[0].soundOff);
02532                         }
02533                         if (ent->client->saber[1].soundOff &&
02534                                 ent->client->saber[1].model[0])
02535                         {
02536                                 G_Sound(ent, CHAN_AUTO, ent->client->saber[1].soundOff);
02537                         }
02538                         //prevent anything from being done for 400ms after holster
02539                         ent->client->ps.weaponTime = 400;
02540                 }
02541         }
02542 }
02543 
02544 extern vmCvar_t         d_saberStanceDebug;
02545 
02546 extern qboolean WP_SaberCanTurnOffSomeBlades( saberInfo_t *saber );
02547 void Cmd_SaberAttackCycle_f(gentity_t *ent)
02548 {
02549         int selectLevel = 0;
02550         qboolean usingSiegeStyle = qfalse;
02551         
02552         if ( !ent || !ent->client )
02553         {
02554                 return;
02555         }
02556         /*
02557         if (ent->client->ps.weaponTime > 0)
02558         { //no switching attack level when busy
02559                 return;
02560         }
02561         */
02562 
02563         if (ent->client->saber[0].model[0] && ent->client->saber[1].model[0])
02564         { //no cycling for akimbo
02565                 if ( WP_SaberCanTurnOffSomeBlades( &ent->client->saber[1] ) )
02566                 {//can turn second saber off 
02567                         if ( ent->client->ps.saberHolstered == 1 )
02568                         {//have one holstered
02569                                 //unholster it
02570                                 G_Sound(ent, CHAN_AUTO, ent->client->saber[1].soundOn);
02571                                 ent->client->ps.saberHolstered = 0;
02572                                 //g_active should take care of this, but...
02573                                 ent->client->ps.fd.saberAnimLevel = SS_DUAL;
02574                         }
02575                         else if ( ent->client->ps.saberHolstered == 0 )
02576                         {//have none holstered
02577                                 if ( (ent->client->saber[1].saberFlags2&SFL2_NO_MANUAL_DEACTIVATE) )
02578                                 {//can't turn it off manually
02579                                 }
02580                                 else if ( ent->client->saber[1].bladeStyle2Start > 0
02581                                         && (ent->client->saber[1].saberFlags2&SFL2_NO_MANUAL_DEACTIVATE2) )
02582                                 {//can't turn it off manually
02583                                 }
02584                                 else
02585                                 {
02586                                         //turn it off
02587                                         G_Sound(ent, CHAN_AUTO, ent->client->saber[1].soundOff);
02588                                         ent->client->ps.saberHolstered = 1;
02589                                         //g_active should take care of this, but...
02590                                         ent->client->ps.fd.saberAnimLevel = SS_FAST;
02591                                 }
02592                         }
02593 
02594                         if (d_saberStanceDebug.integer)
02595                         {
02596                                 trap_SendServerCommand( ent-g_entities, va("print \"SABERSTANCEDEBUG: Attempted to toggle dual saber blade.\n\"") );
02597                         }
02598                         return;
02599                 }
02600         }
02601         else if (ent->client->saber[0].numBlades > 1
02602                 && WP_SaberCanTurnOffSomeBlades( &ent->client->saber[0] ) )
02603         { //use staff stance then.
02604                 if ( ent->client->ps.saberHolstered == 1 )
02605                 {//second blade off
02606                         if ( ent->client->ps.saberInFlight )
02607                         {//can't turn second blade back on if it's in the air, you naughty boy!
02608                                 if (d_saberStanceDebug.integer)
02609                                 {
02610                                         trap_SendServerCommand( ent-g_entities, va("print \"SABERSTANCEDEBUG: Attempted to toggle staff blade in air.\n\"") );
02611                                 }
02612                                 return;
02613                         }
02614                         //turn it on
02615                         G_Sound(ent, CHAN_AUTO, ent->client->saber[0].soundOn);
02616                         ent->client->ps.saberHolstered = 0;
02617                         //g_active should take care of this, but...
02618                         if ( ent->client->saber[0].stylesForbidden )
02619                         {//have a style we have to use
02620                                 WP_UseFirstValidSaberStyle( &ent->client->saber[0], &ent->client->saber[1], ent->client->ps.saberHolstered, &selectLevel );
02621                                 if ( ent->client->ps.weaponTime <= 0 )
02622                                 { //not busy, set it now
02623                                         ent->client->ps.fd.saberAnimLevel = selectLevel;
02624                                 }
02625                                 else
02626                                 { //can't set it now or we might cause unexpected chaining, so queue it
02627                                         ent->client->saberCycleQueue = selectLevel;
02628                                 }
02629                         }
02630                 }
02631                 else if ( ent->client->ps.saberHolstered == 0 )
02632                 {//both blades on
02633                         if ( (ent->client->saber[0].saberFlags2&SFL2_NO_MANUAL_DEACTIVATE) )
02634                         {//can't turn it off manually
02635                         }
02636                         else if ( ent->client->saber[0].bladeStyle2Start > 0
02637                                 && (ent->client->saber[0].saberFlags2&SFL2_NO_MANUAL_DEACTIVATE2) )
02638                         {//can't turn it off manually
02639                         }
02640                         else
02641                         {
02642                                 //turn second one off
02643                                 G_Sound(ent, CHAN_AUTO, ent->client->saber[0].soundOff);
02644                                 ent->client->ps.saberHolstered = 1;
02645                                 //g_active should take care of this, but...
02646                                 if ( ent->client->saber[0].singleBladeStyle != SS_NONE )
02647                                 {
02648                                         if ( ent->client->ps.weaponTime <= 0 )
02649                                         { //not busy, set it now
02650                                                 ent->client->ps.fd.saberAnimLevel = ent->client->saber[0].singleBladeStyle;
02651                                         }
02652                                         else
02653                                         { //can't set it now or we might cause unexpected chaining, so queue it
02654                                                 ent->client->saberCycleQueue = ent->client->saber[0].singleBladeStyle;
02655                                         }
02656                                 }
02657                         }
02658                 }
02659                 if (d_saberStanceDebug.integer)
02660                 {
02661                         trap_SendServerCommand( ent-g_entities, va("print \"SABERSTANCEDEBUG: Attempted to toggle staff blade.\n\"") );
02662                 }
02663                 return;
02664         }
02665 
02666         if (ent->client->saberCycleQueue)
02667         { //resume off of the queue if we haven't gotten a chance to update it yet
02668                 selectLevel = ent->client->saberCycleQueue;
02669         }
02670         else
02671         {
02672                 selectLevel = ent->client->ps.fd.saberAnimLevel;
02673         }
02674 
02675         if (g_gametype.integer == GT_SIEGE &&
02676                 ent->client->siegeClass != -1 &&
02677                 bgSiegeClasses[ent->client->siegeClass].saberStance)
02678         { //we have a flag of useable stances so cycle through it instead
02679                 int i = selectLevel+1;
02680 
02681                 usingSiegeStyle = qtrue;
02682 
02683                 while (i != selectLevel)
02684                 { //cycle around upward til we hit the next style or end up back on this one
02685                         if (i >= SS_NUM_SABER_STYLES)
02686                         { //loop back around to the first valid
02687                                 i = SS_FAST;
02688                         }
02689 
02690                         if (bgSiegeClasses[ent->client->siegeClass].saberStance & (1 << i))
02691                         { //we can use this one, select it and break out.
02692                                 selectLevel = i;
02693                                 break;
02694                         }
02695                         i++;
02696                 }
02697 
02698                 if (d_saberStanceDebug.integer)
02699                 {
02700                         trap_SendServerCommand( ent-g_entities, va("print \"SABERSTANCEDEBUG: Attempted to cycle given class stance.\n\"") );
02701                 }
02702         }
02703         else
02704         {
02705                 selectLevel++;
02706                 if ( selectLevel > ent->client->ps.fd.forcePowerLevel[FP_SABER_OFFENSE] )
02707                 {
02708                         selectLevel = FORCE_LEVEL_1;
02709                 }
02710                 if (d_saberStanceDebug.integer)
02711                 {
02712                         trap_SendServerCommand( ent-g_entities, va("print \"SABERSTANCEDEBUG: Attempted to cycle stance normally.\n\"") );
02713                 }
02714         }
02715 /*
02716 #ifndef FINAL_BUILD
02717         switch ( selectLevel )
02718         {
02719         case FORCE_LEVEL_1:
02720                 trap_SendServerCommand( ent-g_entities, va("print \"Lightsaber Combat Style: %sfast\n\"", S_COLOR_BLUE) );
02721                 break;
02722         case FORCE_LEVEL_2:
02723                 trap_SendServerCommand( ent-g_entities, va("print \"Lightsaber Combat Style: %smedium\n\"", S_COLOR_YELLOW) );
02724                 break;
02725         case FORCE_LEVEL_3:
02726                 trap_SendServerCommand( ent-g_entities, va("print \"Lightsaber Combat Style: %sstrong\n\"", S_COLOR_RED) );
02727                 break;
02728         }
02729 #endif
02730 */
02731         if ( !usingSiegeStyle )
02732         {
02733                 //make sure it's valid, change it if not
02734                 WP_UseFirstValidSaberStyle( &ent->client->saber[0], &ent->client->saber[1], ent->client->ps.saberHolstered, &selectLevel );
02735         }
02736 
02737         if (ent->client->ps.weaponTime <= 0)
02738         { //not busy, set it now
02739                 ent->client->ps.fd.saberAnimLevelBase = ent->client->ps.fd.saberAnimLevel = selectLevel;
02740         }
02741         else
02742         { //can't set it now or we might cause unexpected chaining, so queue it
02743                 ent->client->ps.fd.saberAnimLevelBase = ent->client->saberCycleQueue = selectLevel;
02744         }
02745 }
02746 
02747 qboolean G_OtherPlayersDueling(void)
02748 {
02749         int i = 0;
02750         gentity_t *ent;
02751 
02752         while (i < MAX_CLIENTS)
02753         {
02754                 ent = &g_entities[i];
02755 
02756                 if (ent && ent->inuse && ent->client && ent->client->ps.duelInProgress)
02757                 {
02758                         return qtrue;
02759                 }
02760                 i++;
02761         }
02762 
02763         return qfalse;
02764 }
02765 
02766 void Cmd_EngageDuel_f(gentity_t *ent)
02767 {
02768         trace_t tr;
02769         vec3_t forward, fwdOrg;
02770 
02771         if (!g_privateDuel.integer)
02772         {
02773                 return;
02774         }
02775 
02776         if (g_gametype.integer == GT_DUEL || g_gametype.integer == GT_POWERDUEL)
02777         { //rather pointless in this mode..
02778                 trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", G_GetStringEdString("MP_SVGAME", "NODUEL_GAMETYPE")) );
02779                 return;
02780         }
02781 
02782         //if (g_gametype.integer >= GT_TEAM && g_gametype.integer != GT_SIEGE)
02783         if (g_gametype.integer >= GT_TEAM)
02784         { //no private dueling in team modes
02785                 trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", G_GetStringEdString("MP_SVGAME", "NODUEL_GAMETYPE")) );
02786                 return;
02787         }
02788 
02789         if (ent->client->ps.duelTime >= level.time)
02790         {
02791                 return;
02792         }
02793 
02794         if (ent->client->ps.weapon != WP_SABER)
02795         {
02796                 return;
02797         }
02798 
02799         /*
02800         if (!ent->client->ps.saberHolstered)
02801         { //must have saber holstered at the start of the duel
02802                 return;
02803         }
02804         */
02805         //NOTE: No longer doing this..
02806 
02807         if (ent->client->ps.saberInFlight)
02808         {
02809                 return;
02810         }
02811 
02812         if (ent->client->ps.duelInProgress)
02813         {
02814                 return;
02815         }
02816 
02817         //New: Don't let a player duel if he just did and hasn't waited 10 seconds yet (note: If someone challenges him, his duel timer will reset so he can accept)
02818         if (ent->client->ps.fd.privateDuelTime > level.time)
02819         {
02820                 trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", G_GetStringEdString("MP_SVGAME", "CANTDUEL_JUSTDID")) );
02821                 return;
02822         }
02823 
02824         if (G_OtherPlayersDueling())
02825         {
02826                 trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", G_GetStringEdString("MP_SVGAME", "CANTDUEL_BUSY")) );
02827                 return;
02828         }
02829 
02830         AngleVectors( ent->client->ps.viewangles, forward, NULL, NULL );
02831 
02832         fwdOrg[0] = ent->client->ps.origin[0] + forward[0]*256;
02833         fwdOrg[1] = ent->client->ps.origin[1] + forward[1]*256;
02834         fwdOrg[2] = (ent->client->ps.origin[2]+ent->client->ps.viewheight) + forward[2]*256;
02835 
02836         trap_Trace(&tr, ent->client->ps.origin, NULL, NULL, fwdOrg, ent->s.number, MASK_PLAYERSOLID);
02837 
02838         if (tr.fraction != 1 && tr.entityNum < MAX_CLIENTS)
02839         {
02840                 gentity_t *challenged = &g_entities[tr.entityNum];
02841 
02842                 if (!challenged || !challenged->client || !challenged->inuse ||
02843                         challenged->health < 1 || challenged->client->ps.stats[STAT_HEALTH] < 1 ||
02844                         challenged->client->ps.weapon != WP_SABER || challenged->client->ps.duelInProgress ||
02845                         challenged->client->ps.saberInFlight)
02846                 {
02847                         return;
02848                 }
02849 
02850                 if (g_gametype.integer >= GT_TEAM && OnSameTeam(ent, challenged))
02851                 {
02852                         return;
02853                 }
02854 
02855                 if (challenged->client->ps.duelIndex == ent->s.number && challenged->client->ps.duelTime >= level.time)
02856                 {
02857                         trap_SendServerCommand( /*challenged-g_entities*/-1, va("print \"%s %s %s!\n\"", challenged->client->pers.netname, G_GetStringEdString("MP_SVGAME", "PLDUELACCEPT"), ent->client->pers.netname) );
02858 
02859                         ent->client->ps.duelInProgress = qtrue;
02860                         challenged->client->ps.duelInProgress = qtrue;
02861 
02862                         ent->client->ps.duelTime = level.time + 2000;
02863                         challenged->client->ps.duelTime = level.time + 2000;
02864 
02865                         G_AddEvent(ent, EV_PRIVATE_DUEL, 1);
02866                         G_AddEvent(challenged, EV_PRIVATE_DUEL, 1);
02867 
02868                         //Holster their sabers now, until the duel starts (then they'll get auto-turned on to look cool)
02869 
02870                         if (!ent->client->ps.saberHolstered)
02871                         {
02872                                 if (ent->client->saber[0].soundOff)
02873                                 {
02874                                         G_Sound(ent, CHAN_AUTO, ent->client->saber[0].soundOff);
02875                                 }
02876                                 if (ent->client->saber[1].soundOff &&
02877                                         ent->client->saber[1].model[0])
02878                                 {
02879                                         G_Sound(ent, CHAN_AUTO, ent->client->saber[1].soundOff);
02880                                 }
02881                                 ent->client->ps.weaponTime = 400;
02882                                 ent->client->ps.saberHolstered = 2;
02883                         }
02884                         if (!challenged->client->ps.saberHolstered)
02885                         {
02886                                 if (challenged->client->saber[0].soundOff)
02887                                 {
02888                                         G_Sound(challenged, CHAN_AUTO, challenged->client->saber[0].soundOff);
02889                                 }
02890                                 if (challenged->client->saber[1].soundOff &&
02891                                         challenged->client->saber[1].model[0])
02892                                 {
02893                                         G_Sound(challenged, CHAN_AUTO, challenged->client->saber[1].soundOff);
02894                                 }
02895                                 challenged->client->ps.weaponTime = 400;
02896                                 challenged->client->ps.saberHolstered = 2;
02897                         }
02898                 }
02899                 else
02900                 {
02901                         //Print the message that a player has been challenged in private, only announce the actual duel initiation in private
02902                         trap_SendServerCommand( challenged-g_entities, va("cp \"%s %s\n\"", ent->client->pers.netname, G_GetStringEdString("MP_SVGAME", "PLDUELCHALLENGE")) );
02903                         trap_SendServerCommand( ent-g_entities, va("cp \"%s %s\n\"", G_GetStringEdString("MP_SVGAME", "PLDUELCHALLENGED"), challenged->client->pers.netname) );
02904                 }
02905 
02906                 challenged->client->ps.fd.privateDuelTime = 0; //reset the timer in case this player just got out of a duel. He should still be able to accept the challenge.
02907 
02908                 ent->client->ps.forceHandExtend = HANDEXTEND_DUELCHALLENGE;
02909                 ent->client->ps.forceHandExtendTime = level.time + 1000;
02910 
02911                 ent->client->ps.duelIndex = challenged->s.number;
02912                 ent->client->ps.duelTime = level.time + 5000;
02913         }
02914 }
02915 
02916 #ifndef FINAL_BUILD
02917 extern stringID_table_t animTable[MAX_ANIMATIONS+1];
02918 
02919 void Cmd_DebugSetSaberMove_f(gentity_t *self)
02920 {
02921         int argNum = trap_Argc();
02922         char arg[MAX_STRING_CHARS];
02923 
02924         if (argNum < 2)
02925         {
02926                 return;
02927         }
02928 
02929         trap_Argv( 1, arg, sizeof( arg ) );
02930 
02931         if (!arg[0])
02932         {
02933                 return;
02934         }
02935 
02936         self->client->ps.saberMove = atoi(arg);
02937         self->client->ps.saberBlocked = BLOCKED_BOUNCE_MOVE;
02938 
02939         if (self->client->ps.saberMove >= LS_MOVE_MAX)
02940         {
02941                 self->client->ps.saberMove = LS_MOVE_MAX-1;
02942         }
02943 
02944         Com_Printf("Anim for move: %s\n", animTable[saberMoveData[self->client->ps.saberMove].animToUse].name);
02945 }
02946 
02947 void Cmd_DebugSetBodyAnim_f(gentity_t *self, int flags)
02948 {
02949         int argNum = trap_Argc();
02950         char arg[MAX_STRING_CHARS];
02951         int i = 0;
02952 
02953         if (argNum < 2)
02954         {
02955                 return;
02956         }
02957 
02958         trap_Argv( 1, arg, sizeof( arg ) );
02959 
02960         if (!arg[0])
02961         {
02962                 return;
02963         }
02964 
02965         while (i < MAX_ANIMATIONS)
02966         {
02967                 if (!Q_stricmp(arg, animTable[i].name))
02968                 {
02969                         break;
02970                 }
02971                 i++;
02972         }
02973 
02974         if (i == MAX_ANIMATIONS)
02975         {
02976                 Com_Printf("Animation '%s' does not exist\n", arg);
02977                 return;
02978         }
02979 
02980         G_SetAnim(self, NULL, SETANIM_BOTH, i, flags, 0);
02981 
02982         Com_Printf("Set body anim to %s\n", arg);
02983 }
02984 #endif
02985 
02986 void StandardSetBodyAnim(gentity_t *self, int anim, int flags)
02987 {
02988         G_SetAnim(self, NULL, SETANIM_BOTH, anim, flags, 0);
02989 }
02990 
02991 void DismembermentTest(gentity_t *self);
02992 
02993 void Bot_SetForcedMovement(int bot, int forward, int right, int up);
02994 
02995 #ifndef FINAL_BUILD
02996 extern void DismembermentByNum(gentity_t *self, int num);
02997 extern void G_SetVehDamageFlags( gentity_t *veh, int shipSurf, int damageLevel );
02998 #endif
02999 
03000 static int G_ClientNumFromNetname(char *name)
03001 {
03002         int i = 0;
03003         gentity_t *ent;
03004 
03005         while (i < MAX_CLIENTS)
03006         {
03007                 ent = &g_entities[i];
03008 
03009                 if (ent->inuse && ent->client &&
03010                         !Q_stricmp(ent->client->pers.netname, name))
03011                 {
03012                         return ent->s.number;
03013                 }
03014                 i++;
03015         }
03016 
03017         return -1;
03018 }
03019 
03020 qboolean TryGrapple(gentity_t *ent)
03021 {
03022         if (ent->client->ps.weaponTime > 0)
03023         { //weapon busy
03024                 return qfalse;
03025         }
03026         if (ent->client->ps.forceHandExtend != HANDEXTEND_NONE)
03027         { //force power or knockdown or something
03028                 return qfalse;
03029         }
03030         if (ent->client->grappleState)
03031         { //already grappling? but weapontime should be > 0 then..
03032                 return qfalse;
03033         }
03034 
03035         if (ent->client->ps.weapon != WP_SABER && ent->client->ps.weapon != WP_MELEE)
03036         {
03037                 return qfalse;
03038         }
03039 
03040         if (ent->client->ps.weapon == WP_SABER && !ent->client->ps.saberHolstered)
03041         {
03042                 Cmd_ToggleSaber_f(ent);
03043                 if (!ent->client->ps.saberHolstered)
03044                 { //must have saber holstered
03045                         return qfalse;
03046                 }
03047         }
03048 
03049         //G_SetAnim(ent, &ent->client->pers.cmd, SETANIM_BOTH, BOTH_KYLE_PA_1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 0);
03050         G_SetAnim(ent, &ent->client->pers.cmd, SETANIM_BOTH, BOTH_KYLE_GRAB, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 0);
03051         if (ent->client->ps.torsoAnim == BOTH_KYLE_GRAB)
03052         { //providing the anim set succeeded..
03053                 ent->client->ps.torsoTimer += 500; //make the hand stick out a little longer than it normally would
03054                 if (ent->client->ps.legsAnim == ent->client->ps.torsoAnim)
03055                 {
03056                         ent->client->ps.legsTimer = ent->client->ps.torsoTimer;
03057                 }
03058                 ent->client->ps.weaponTime = ent->client->ps.torsoTimer;
03059                 return qtrue;
03060         }
03061 
03062         return qfalse;
03063 }
03064 
03065 #ifndef FINAL_BUILD
03066 qboolean saberKnockOutOfHand(gentity_t *saberent, gentity_t *saberOwner, vec3_t velocity);
03067 #endif
03068 
03069 /*
03070 =================
03071 ClientCommand
03072 =================
03073 */
03074 void ClientCommand( int clientNum ) {
03075         gentity_t *ent;
03076         char    cmd[MAX_TOKEN_CHARS];
03077 
03078         ent = g_entities + clientNum;
03079         if ( !ent->client ) {
03080                 return;         // not fully in game yet
03081         }
03082 
03083 
03084         trap_Argv( 0, cmd, sizeof( cmd ) );
03085 
03086         //rww - redirect bot commands
03087         if (strstr(cmd, "bot_") && AcceptBotCommand(cmd, ent))
03088         {
03089                 return;
03090         }
03091         //end rww
03092 
03093         if (Q_stricmp (cmd, "say") == 0) {
03094                 Cmd_Say_f (ent, SAY_ALL, qfalse);
03095                 return;
03096         }
03097         if (Q_stricmp (cmd, "say_team") == 0) {
03098                 if (g_gametype.integer < GT_TEAM)
03099                 { //not a team game, just refer to regular say.
03100                         Cmd_Say_f (ent, SAY_ALL, qfalse);
03101                 }
03102                 else
03103                 {
03104                         Cmd_Say_f (ent, SAY_TEAM, qfalse);
03105                 }
03106                 return;
03107         }
03108         if (Q_stricmp (cmd, "tell") == 0) {
03109                 Cmd_Tell_f ( ent );
03110                 return;
03111         }
03112 
03113         if (Q_stricmp(cmd, "voice_cmd") == 0)
03114         {
03115                 Cmd_VoiceCommand_f(ent);
03116                 return;
03117         }
03118 
03119         if (Q_stricmp (cmd, "score") == 0) {
03120                 Cmd_Score_f (ent);
03121                 return;
03122         }
03123 
03124         // ignore all other commands when at intermission
03125         if (level.intermissiontime)
03126         {
03127                 qboolean giveError = qfalse;
03128                 //rwwFIXMEFIXME: This is terrible, write it differently
03129 
03130                 if (!Q_stricmp(cmd, "give"))
03131                 {
03132                         giveError = qtrue;
03133                 }
03134                 else if (!Q_stricmp(cmd, "giveother"))
03135                 {
03136                         giveError = qtrue;
03137                 }
03138                 else if (!Q_stricmp(cmd, "god"))
03139                 {
03140                         giveError = qtrue;
03141                 }
03142                 else if (!Q_stricmp(cmd, "notarget"))
03143                 {
03144                         giveError = qtrue;
03145                 }
03146                 else if (!Q_stricmp(cmd, "noclip"))
03147                 {
03148                         giveError = qtrue;
03149                 }
03150                 else if (!Q_stricmp(cmd, "kill"))
03151                 {
03152                         giveError = qtrue;
03153                 }
03154                 else if (!Q_stricmp(cmd, "teamtask"))
03155                 {
03156                         giveError = qtrue;
03157                 }
03158                 else if (!Q_stricmp(cmd, "levelshot"))
03159                 {
03160                         giveError = qtrue;
03161                 }
03162                 else if (!Q_stricmp(cmd, "follow"))
03163                 {
03164                         giveError = qtrue;
03165                 }
03166                 else if (!Q_stricmp(cmd, "follownext"))
03167                 {
03168                         giveError = qtrue;
03169                 }
03170                 else if (!Q_stricmp(cmd, "followprev"))
03171                 {
03172                         giveError = qtrue;
03173                 }
03174                 else if (!Q_stricmp(cmd, "team"))
03175                 {
03176                         giveError = qtrue;
03177                 }
03178                 else if (!Q_stricmp(cmd, "duelteam"))
03179                 {
03180                         giveError = qtrue;
03181                 }
03182                 else if (!Q_stricmp(cmd, "siegeclass"))
03183                 {
03184                         giveError = qtrue;
03185                 }
03186                 else if (!Q_stricmp(cmd, "forcechanged"))
03187                 { //special case: still update force change
03188                         Cmd_ForceChanged_f (ent);
03189                         return;
03190                 }
03191                 else if (!Q_stricmp(cmd, "where"))
03192                 {
03193                         giveError = qtrue;
03194                 }
03195                 else if (!Q_stricmp(cmd, "callvote"))
03196                 {
03197                         giveError = qtrue;
03198                 }
03199                 else if (!Q_stricmp(cmd, "vote"))
03200                 {
03201                         giveError = qtrue;
03202                 }
03203                 else if (!Q_stricmp(cmd, "callteamvote"))
03204                 {
03205                         giveError = qtrue;
03206                 }
03207                 else if (!Q_stricmp(cmd, "teamvote"))
03208                 {
03209                         giveError = qtrue;
03210                 }
03211                 else if (!Q_stricmp(cmd, "gc"))
03212                 {
03213                         giveError = qtrue;
03214                 }
03215                 else if (!Q_stricmp(cmd, "setviewpos"))
03216                 {
03217                         giveError = qtrue;
03218                 }
03219                 else if (!Q_stricmp(cmd, "stats"))
03220                 {
03221                         giveError = qtrue;
03222                 }
03223 
03224                 if (giveError)
03225                 {
03226                         trap_SendServerCommand( clientNum, va("print \"%s (%s) \n\"", G_GetStringEdString("MP_SVGAME", "CANNOT_TASK_INTERMISSION"), cmd ) );
03227                 }
03228                 else
03229                 {
03230                         Cmd_Say_f (ent, qfalse, qtrue);
03231                 }
03232                 return;
03233         }
03234 
03235         if (Q_stricmp (cmd, "give") == 0)
03236         {
03237                 Cmd_Give_f (ent, 0);
03238         }
03239         else if (Q_stricmp (cmd, "giveother") == 0)
03240         { //for debugging pretty much
03241                 Cmd_Give_f (ent, 1);
03242         }
03243         else if (Q_stricmp (cmd, "t_use") == 0 && CheatsOk(ent))
03244         { //debug use map object
03245                 if (trap_Argc() > 1)
03246                 {
03247                         char sArg[MAX_STRING_CHARS];
03248                         gentity_t *targ;
03249 
03250                         trap_Argv( 1, sArg, sizeof( sArg ) );
03251                         targ = G_Find( NULL, FOFS(targetname), sArg );
03252 
03253                         while (targ)
03254                         {
03255                                 if (targ->use)
03256                                 {
03257                                         targ->use(targ, ent, ent);
03258                                 }
03259                                 targ = G_Find( targ, FOFS(targetname), sArg );
03260                         }
03261                 }
03262         }
03263         else if (Q_stricmp (cmd, "god") == 0)
03264                 Cmd_God_f (ent);
03265         else if (Q_stricmp (cmd, "notarget") == 0)
03266                 Cmd_Notarget_f (ent);
03267         else if (Q_stricmp (cmd, "noclip") == 0)
03268                 Cmd_Noclip_f (ent);
03269         else if ( Q_stricmp( cmd, "NPC" ) == 0 && CheatsOk(ent) )
03270         {
03271                 Cmd_NPC_f( ent );
03272         }
03273         else if (Q_stricmp (cmd, "kill") == 0)
03274                 Cmd_Kill_f (ent);
03275         else if (Q_stricmp (cmd, "teamtask") == 0)
03276                 Cmd_TeamTask_f (ent);
03277         else if (Q_stricmp (cmd, "levelshot") == 0)
03278                 Cmd_LevelShot_f (ent);
03279         else if (Q_stricmp (cmd, "follow") == 0)
03280                 Cmd_Follow_f (ent);
03281         else if (Q_stricmp (cmd, "follownext") == 0)
03282                 Cmd_FollowCycle_f (ent, 1);
03283         else if (Q_stricmp (cmd, "followprev") == 0)
03284                 Cmd_FollowCycle_f (ent, -1);
03285         else if (Q_stricmp (cmd, "team") == 0)
03286                 Cmd_Team_f (ent);
03287         else if (Q_stricmp (cmd, "duelteam") == 0)
03288                 Cmd_DuelTeam_f (ent);
03289         else if (Q_stricmp (cmd, "siegeclass") == 0)
03290                 Cmd_SiegeClass_f (ent);
03291         else if (Q_stricmp (cmd, "forcechanged") == 0)
03292                 Cmd_ForceChanged_f (ent);
03293         else if (Q_stricmp (cmd, "where") == 0)
03294                 Cmd_Where_f (ent);
03295         else if (Q_stricmp (cmd, "callvote") == 0)
03296                 Cmd_CallVote_f (ent);
03297         else if (Q_stricmp (cmd, "vote") == 0)
03298                 Cmd_Vote_f (ent);
03299         else if (Q_stricmp (cmd, "callteamvote") == 0)
03300                 Cmd_CallTeamVote_f (ent);
03301         else if (Q_stricmp (cmd, "teamvote") == 0)
03302                 Cmd_TeamVote_f (ent);
03303         else if (Q_stricmp (cmd, "gc") == 0)
03304                 Cmd_GameCommand_f( ent );
03305         else if (Q_stricmp (cmd, "setviewpos") == 0)
03306                 Cmd_SetViewpos_f( ent );
03307         else if (Q_stricmp (cmd, "stats") == 0)
03308                 Cmd_Stats_f( ent );
03309         /*
03310         else if (Q_stricmp (cmd, "kylesmash") == 0)
03311         {
03312                 TryGrapple(ent);
03313         }
03314         */
03315         //for convenient powerduel testing in release
03316         else if (Q_stricmp(cmd, "killother") == 0 && CheatsOk( ent ))
03317         {
03318                 if (trap_Argc() > 1)
03319                 {
03320                         char sArg[MAX_STRING_CHARS];
03321                         int entNum = 0;
03322 
03323                         trap_Argv( 1, sArg, sizeof( sArg ) );
03324 
03325                         entNum = G_ClientNumFromNetname(sArg);
03326 
03327                         if (entNum >= 0 && entNum < MAX_GENTITIES)
03328                         {
03329                                 gentity_t *kEnt = &g_entities[entNum];
03330 
03331                                 if (kEnt->inuse && kEnt->client)
03332                                 {
03333                                         kEnt->flags &= ~FL_GODMODE;
03334                                         kEnt->client->ps.stats[STAT_HEALTH] = kEnt->health = -999;
03335                                         player_die (kEnt, kEnt, kEnt, 100000, MOD_SUICIDE);
03336                                 }
03337                         }
03338                 }
03339         }
03340 #ifdef _DEBUG
03341         else if (Q_stricmp(cmd, "relax") == 0 && CheatsOk( ent ))
03342         {
03343                 if (ent->client->ps.eFlags & EF_RAG)
03344                 {
03345                         ent->client->ps.eFlags &= ~EF_RAG;
03346                 }
03347                 else
03348                 {
03349                         ent->client->ps.eFlags |= EF_RAG;
03350                 }
03351         }
03352         else if (Q_stricmp(cmd, "holdme") == 0 && CheatsOk( ent ))
03353         {
03354                 if (trap_Argc() > 1)
03355                 {
03356                         char sArg[MAX_STRING_CHARS];
03357                         int entNum = 0;
03358 
03359                         trap_Argv( 1, sArg, sizeof( sArg ) );
03360 
03361                         entNum = atoi(sArg);
03362 
03363                         if (entNum >= 0 &&
03364                                 entNum < MAX_GENTITIES)
03365                         {
03366                                 gentity_t *grabber = &g_entities[entNum];
03367 
03368                                 if (grabber->inuse && grabber->client && grabber->ghoul2)
03369                                 {
03370                                         if (!grabber->s.number)
03371                                         { //switch cl 0 and entitynum_none, so we can operate on the "if non-0" concept
03372                                                 ent->client->ps.ragAttach = ENTITYNUM_NONE;
03373                                         }
03374                                         else
03375                                         {
03376                                                 ent->client->ps.ragAttach = grabber->s.number;
03377                                         }
03378                                 }
03379                         }
03380                 }
03381                 else
03382                 {
03383                         ent->client->ps.ragAttach = 0;
03384                 }
03385         }
03386         else if (Q_stricmp(cmd, "limb_break") == 0 && CheatsOk( ent ))
03387         {
03388                 if (trap_Argc() > 1)
03389                 {
03390                         char sArg[MAX_STRING_CHARS];
03391                         int breakLimb = 0;
03392 
03393                         trap_Argv( 1, sArg, sizeof( sArg ) );
03394                         if (!Q_stricmp(sArg, "right"))
03395                         {
03396                                 breakLimb = BROKENLIMB_RARM;
03397                         }
03398                         else if (!Q_stricmp(sArg, "left"))
03399                         {
03400                                 breakLimb = BROKENLIMB_LARM;
03401                         }
03402 
03403                         G_BreakArm(ent, breakLimb);
03404                 }
03405         }
03406         else if (Q_stricmp(cmd, "headexplodey") == 0 && CheatsOk( ent ))
03407         {
03408                 Cmd_Kill_f (ent);
03409                 if (ent->health < 1)
03410                 {
03411                         DismembermentTest(ent);
03412                 }
03413         }
03414         else if (Q_stricmp(cmd, "debugstupidthing") == 0 && CheatsOk( ent ))
03415         {
03416                 int i = 0;
03417                 gentity_t *blah;
03418                 while (i < MAX_GENTITIES)
03419                 {
03420                         blah = &g_entities[i];
03421                         if (blah->inuse && blah->classname && blah->classname[0] && !Q_stricmp(blah->classname, "NPC_Vehicle"))
03422                         {
03423                                 Com_Printf("Found it.\n");
03424                         }
03425                         i++;
03426                 }
03427         }
03428         else if (Q_stricmp(cmd, "arbitraryprint") == 0 && CheatsOk( ent ))
03429         {
03430                 trap_SendServerCommand( -1, va("cp \"Blah blah blah\n\""));
03431         }
03432         else if (Q_stricmp(cmd, "handcut") == 0 && CheatsOk( ent ))
03433         {
03434                 int bCl = 0;
03435                 char sarg[MAX_STRING_CHARS];
03436 
03437                 if (trap_Argc() > 1)
03438                 {
03439                         trap_Argv( 1, sarg, sizeof( sarg ) );
03440 
03441                         if (sarg[0])
03442                         {
03443                                 bCl = atoi(sarg);
03444 
03445                                 if (bCl >= 0 && bCl < MAX_GENTITIES)
03446                                 {
03447                                         gentity_t *hEnt = &g_entities[bCl];
03448 
03449                                         if (hEnt->client)
03450                                         {
03451                                                 if (hEnt->health > 0)
03452                                                 {
03453                                                         gGAvoidDismember = 1;
03454                                                         hEnt->flags &= ~FL_GODMODE;
03455                                                         hEnt->client->ps.stats[STAT_HEALTH] = hEnt->health = -999;
03456                                                         player_die (hEnt, hEnt, hEnt, 100000, MOD_SUICIDE);
03457                                                 }
03458                                                 gGAvoidDismember = 2;
03459                                                 G_CheckForDismemberment(hEnt, ent, hEnt->client->ps.origin, 999, hEnt->client->ps.legsAnim, qfalse);
03460                                                 gGAvoidDismember = 0;
03461                                         }
03462                                 }
03463                         }
03464                 }
03465         }
03466         else if (Q_stricmp(cmd, "loveandpeace") == 0 && CheatsOk( ent ))
03467         {
03468                 trace_t tr;
03469                 vec3_t fPos;
03470 
03471                 AngleVectors(ent->client->ps.viewangles, fPos, 0, 0);
03472 
03473                 fPos[0] = ent->client->ps.origin[0] + fPos[0]*40;
03474                 fPos[1] = ent->client->ps.origin[1] + fPos[1]*40;
03475                 fPos[2] = ent->client->ps.origin[2] + fPos[2]*40;
03476 
03477                 trap_Trace(&tr, ent->client->ps.origin, 0, 0, fPos, ent->s.number, ent->clipmask);
03478 
03479                 if (tr.entityNum < MAX_CLIENTS && tr.entityNum != ent->s.number)
03480                 {
03481                         gentity_t *other = &g_entities[tr.entityNum];
03482 
03483                         if (other && other->inuse && other->client)
03484                         {
03485                                 vec3_t entDir;
03486                                 vec3_t otherDir;
03487                                 vec3_t entAngles;
03488                                 vec3_t otherAngles;
03489 
03490                                 if (ent->client->ps.weapon == WP_SABER && !ent->client->ps.saberHolstered)
03491                                 {
03492                                         Cmd_ToggleSaber_f(ent);
03493                                 }
03494 
03495                                 if (other->client->ps.weapon == WP_SABER && !other->client->ps.saberHolstered)
03496                                 {
03497                                         Cmd_ToggleSaber_f(other);
03498                                 }
03499 
03500                                 if ((ent->client->ps.weapon != WP_SABER || ent->client->ps.saberHolstered) &&
03501                                         (other->client->ps.weapon != WP_SABER || other->client->ps.saberHolstered))
03502                                 {
03503                                         VectorSubtract( other->client->ps.origin, ent->client->ps.origin, otherDir );
03504                                         VectorCopy( ent->client->ps.viewangles, entAngles );
03505                                         entAngles[YAW] = vectoyaw( otherDir );
03506                                         SetClientViewAngle( ent, entAngles );
03507 
03508                                         StandardSetBodyAnim(ent, /*BOTH_KISSER1LOOP*/BOTH_STAND1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD|SETANIM_FLAG_HOLDLESS);
03509                                         ent->client->ps.saberMove = LS_NONE;
03510                                         ent->client->ps.saberBlocked = 0;
03511                                         ent->client->ps.saberBlocking = 0;
03512 
03513                                         VectorSubtract( ent->client->ps.origin, other->client->ps.origin, entDir );
03514                                         VectorCopy( other->client->ps.viewangles, otherAngles );
03515                                         otherAngles[YAW] = vectoyaw( entDir );
03516                                         SetClientViewAngle( other, otherAngles );
03517 
03518                                         StandardSetBodyAnim(other, /*BOTH_KISSEE1LOOP*/BOTH_STAND1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD|SETANIM_FLAG_HOLDLESS);
03519                                         other->client->ps.saberMove = LS_NONE;
03520                                         other->client->ps.saberBlocked = 0;
03521                                         other->client->ps.saberBlocking = 0;
03522                                 }
03523                         }
03524                 }
03525         }
03526 #endif
03527         else if (Q_stricmp(cmd, "thedestroyer") == 0 && CheatsOk( ent ) && ent && ent->client && ent->client->ps.saberHolstered && ent->client->ps.weapon == WP_SABER)
03528         {
03529                 Cmd_ToggleSaber_f(ent);
03530 
03531                 if (!ent->client->ps.saberHolstered)
03532                 {
03533                 }
03534         }
03535         //begin bot debug cmds
03536         else if (Q_stricmp(cmd, "debugBMove_Forward") == 0 && CheatsOk(ent))
03537         {
03538                 int arg = 4000;
03539                 int bCl = 0;
03540                 char sarg[MAX_STRING_CHARS];
03541 
03542                 assert(trap_Argc() > 1);
03543                 trap_Argv( 1, sarg, sizeof( sarg ) );
03544 
03545                 assert(sarg[0]);
03546                 bCl = atoi(sarg);
03547                 Bot_SetForcedMovement(bCl, arg, -1, -1);
03548         }
03549         else if (Q_stricmp(cmd, "debugBMove_Back") == 0 && CheatsOk(ent))
03550         {
03551                 int arg = -4000;
03552                 int bCl = 0;
03553                 char sarg[MAX_STRING_CHARS];
03554 
03555                 assert(trap_Argc() > 1);
03556                 trap_Argv( 1, sarg, sizeof( sarg ) );
03557 
03558                 assert(sarg[0]);
03559                 bCl = atoi(sarg);
03560                 Bot_SetForcedMovement(bCl, arg, -1, -1);
03561         }
03562         else if (Q_stricmp(cmd, "debugBMove_Right") == 0 && CheatsOk(ent))
03563         {
03564                 int arg = 4000;
03565                 int bCl = 0;
03566                 char sarg[MAX_STRING_CHARS];
03567 
03568                 assert(trap_Argc() > 1);
03569                 trap_Argv( 1, sarg, sizeof( sarg ) );
03570 
03571                 assert(sarg[0]);
03572                 bCl = atoi(sarg);
03573                 Bot_SetForcedMovement(bCl, -1, arg, -1);
03574         }
03575         else if (Q_stricmp(cmd, "debugBMove_Left") == 0 && CheatsOk(ent))
03576         {
03577                 int arg = -4000;
03578                 int bCl = 0;
03579                 char sarg[MAX_STRING_CHARS];
03580 
03581                 assert(trap_Argc() > 1);
03582                 trap_Argv( 1, sarg, sizeof( sarg ) );
03583 
03584                 assert(sarg[0]);
03585                 bCl = atoi(sarg);
03586                 Bot_SetForcedMovement(bCl, -1, arg, -1);
03587         }
03588         else if (Q_stricmp(cmd, "debugBMove_Up") == 0 && CheatsOk(ent))
03589         {
03590                 int arg = 4000;
03591                 int bCl = 0;
03592                 char sarg[MAX_STRING_CHARS];
03593 
03594                 assert(trap_Argc() > 1);
03595                 trap_Argv( 1, sarg, sizeof( sarg ) );
03596 
03597                 assert(sarg[0]);
03598                 bCl = atoi(sarg);
03599                 Bot_SetForcedMovement(bCl, -1, -1, arg);
03600         }
03601         //end bot debug cmds
03602 #ifndef FINAL_BUILD
03603         else if (Q_stricmp(cmd, "debugSetSaberMove") == 0)
03604         {
03605                 Cmd_DebugSetSaberMove_f(ent);
03606         }
03607         else if (Q_stricmp(cmd, "debugSetBodyAnim") == 0)
03608         {
03609                 Cmd_DebugSetBodyAnim_f(ent, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);
03610         }
03611         else if (Q_stricmp(cmd, "debugDismemberment") == 0)
03612         {
03613                 Cmd_Kill_f (ent);
03614                 if (ent->health < 1)
03615                 {
03616                         char    arg[MAX_STRING_CHARS];
03617                         int             iArg = 0;
03618 
03619                         if (trap_Argc() > 1)
03620                         {
03621                                 trap_Argv( 1, arg, sizeof( arg ) );
03622 
03623                                 if (arg[0])
03624                                 {
03625                                         iArg = atoi(arg);
03626                                 }
03627                         }
03628 
03629                         DismembermentByNum(ent, iArg);
03630                 }
03631         }
03632         else if (Q_stricmp(cmd, "debugDropSaber") == 0)
03633         {
03634                 if (ent->client->ps.weapon == WP_SABER &&
03635                         ent->client->ps.saberEntityNum &&
03636                         !ent->client->ps.saberInFlight)
03637                 {
03638                         saberKnockOutOfHand(&g_entities[ent->client->ps.saberEntityNum], ent, vec3_origin);
03639                 }
03640         }
03641         else if (Q_stricmp(cmd, "debugKnockMeDown") == 0)
03642         {
03643                 if (BG_KnockDownable(&ent->client->ps))
03644                 {
03645                         ent->client->ps.forceHandExtend = HANDEXTEND_KNOCKDOWN;
03646                         ent->client->ps.forceDodgeAnim = 0;
03647                         if (trap_Argc() > 1)
03648                         {
03649                                 ent->client->ps.forceHandExtendTime = level.time + 1100;
03650                                 ent->client->ps.quickerGetup = qfalse;
03651                         }
03652                         else
03653                         {
03654                                 ent->client->ps.forceHandExtendTime = level.time + 700;
03655                                 ent->client->ps.quickerGetup = qtrue;
03656                         }
03657                 }
03658         }
03659         else if (Q_stricmp(cmd, "debugSaberSwitch") == 0)
03660         {
03661                 gentity_t *targ = NULL;
03662 
03663                 if (trap_Argc() > 1)
03664                 {
03665                         char    arg[MAX_STRING_CHARS];
03666 
03667                         trap_Argv( 1, arg, sizeof( arg ) );
03668 
03669                         if (arg[0])
03670                         {
03671                                 int x = atoi(arg);
03672                                 
03673                                 if (x >= 0 && x < MAX_CLIENTS)
03674                                 {
03675                                         targ = &g_entities[x];
03676                                 }
03677                         }
03678                 }
03679 
03680                 if (targ && targ->inuse && targ->client)
03681                 {
03682                         Cmd_ToggleSaber_f(targ);
03683                 }
03684         }
03685         else if (Q_stricmp(cmd, "debugIKGrab") == 0)
03686         {
03687                 gentity_t *targ = NULL;
03688 
03689                 if (trap_Argc() > 1)
03690                 {
03691                         char    arg[MAX_STRING_CHARS];
03692 
03693                         trap_Argv( 1, arg, sizeof( arg ) );
03694 
03695                         if (arg[0])
03696                         {
03697                                 int x = atoi(arg);
03698                                 
03699                                 if (x >= 0 && x < MAX_CLIENTS)
03700                                 {
03701                                         targ = &g_entities[x];
03702                                 }
03703                         }
03704                 }
03705 
03706                 if (targ && targ->inuse && targ->client && ent->s.number != targ->s.number)
03707                 {
03708                         targ->client->ps.heldByClient = ent->s.number+1;
03709                 }
03710         }
03711         else if (Q_stricmp(cmd, "debugIKBeGrabbedBy") == 0)
03712         {
03713                 gentity_t *targ = NULL;
03714 
03715                 if (trap_Argc() > 1)
03716                 {
03717                         char    arg[MAX_STRING_CHARS];
03718 
03719                         trap_Argv( 1, arg, sizeof( arg ) );
03720 
03721                         if (arg[0])
03722                         {
03723                                 int x = atoi(arg);
03724                                 
03725                                 if (x >= 0 && x < MAX_CLIENTS)
03726                                 {
03727                                         targ = &g_entities[x];
03728                                 }
03729                         }
03730                 }
03731 
03732                 if (targ && targ->inuse && targ->client && ent->s.number != targ->s.number)
03733                 {
03734                         ent->client->ps.heldByClient = targ->s.number+1;
03735                 }
03736         }
03737         else if (Q_stricmp(cmd, "debugIKRelease") == 0)
03738         {
03739                 gentity_t *targ = NULL;
03740 
03741                 if (trap_Argc() > 1)
03742                 {
03743                         char    arg[MAX_STRING_CHARS];
03744 
03745                         trap_Argv( 1, arg, sizeof( arg ) );
03746 
03747                         if (arg[0])
03748                         {
03749                                 int x = atoi(arg);
03750                                 
03751                                 if (x >= 0 && x < MAX_CLIENTS)
03752                                 {
03753                                         targ = &g_entities[x];
03754                                 }
03755                         }
03756                 }
03757 
03758                 if (targ && targ->inuse && targ->client)
03759                 {
03760                         targ->client->ps.heldByClient = 0;
03761                 }
03762         }
03763         else if (Q_stricmp(cmd, "debugThrow") == 0)
03764         {
03765                 trace_t tr;
03766                 vec3_t tTo, fwd;
03767 
03768                 if (ent->client->ps.weaponTime > 0 || ent->client->ps.forceHandExtend != HANDEXTEND_NONE ||
03769                         ent->client->ps.groundEntityNum == ENTITYNUM_NONE || ent->health < 1)
03770                 {
03771                         return;
03772                 }
03773 
03774                 AngleVectors(ent->client->ps.viewangles, fwd, 0, 0);
03775                 tTo[0] = ent->client->ps.origin[0] + fwd[0]*32;
03776                 tTo[1] = ent->client->ps.origin[1] + fwd[1]*32;
03777                 tTo[2] = ent->client->ps.origin[2] + fwd[2]*32;
03778 
03779                 trap_Trace(&tr, ent->client->ps.origin, 0, 0, tTo, ent->s.number, MASK_PLAYERSOLID);
03780 
03781                 if (tr.fraction != 1)
03782                 {
03783                         gentity_t *other = &g_entities[tr.entityNum];
03784 
03785                         if (other->inuse && other->client && other->client->ps.forceHandExtend == HANDEXTEND_NONE &&
03786                                 other->client->ps.groundEntityNum != ENTITYNUM_NONE && other->health > 0 &&
03787                                 (int)ent->client->ps.origin[2] == (int)other->client->ps.origin[2])
03788                         {
03789                                 float pDif = 40.0f;
03790                                 vec3_t entAngles, entDir;
03791                                 vec3_t otherAngles, otherDir;
03792                                 vec3_t intendedOrigin;
03793                                 vec3_t boltOrg, pBoltOrg;
03794                                 vec3_t tAngles, vDif;
03795                                 vec3_t fwd, right;
03796                                 trace_t tr;
03797                                 trace_t tr2;
03798 
03799                                 VectorSubtract( other->client->ps.origin, ent->client->ps.origin, otherDir );
03800                                 VectorCopy( ent->client->ps.viewangles, entAngles );
03801                                 entAngles[YAW] = vectoyaw( otherDir );
03802                                 SetClientViewAngle( ent, entAngles );
03803 
03804                                 ent->client->ps.forceHandExtend = HANDEXTEND_PRETHROW;
03805                                 ent->client->ps.forceHandExtendTime = level.time + 5000;
03806 
03807                                 ent->client->throwingIndex = other->s.number;
03808                                 ent->client->doingThrow = level.time + 5000;
03809                                 ent->client->beingThrown = 0;
03810 
03811                                 VectorSubtract( ent->client->ps.origin, other->client->ps.origin, entDir );
03812                                 VectorCopy( other->client->ps.viewangles, otherAngles );
03813                                 otherAngles[YAW] = vectoyaw( entDir );
03814                                 SetClientViewAngle( other, otherAngles );
03815 
03816                                 other->client->ps.forceHandExtend = HANDEXTEND_PRETHROWN;
03817                                 other->client->ps.forceHandExtendTime = level.time + 5000;
03818 
03819                                 other->client->throwingIndex = ent->s.number;
03820                                 other->client->beingThrown = level.time + 5000;
03821                                 other->client->doingThrow = 0;
03822 
03823                                 //Doing this now at a stage in the throw, isntead of initially.
03824                                 //other->client->ps.heldByClient = ent->s.number+1;
03825 
03826                                 G_EntitySound( other, CHAN_VOICE, G_SoundIndex("*pain100.wav") );
03827                                 G_EntitySound( ent, CHAN_VOICE, G_SoundIndex("*jump1.wav") );
03828                                 G_Sound(other, CHAN_AUTO, G_SoundIndex( "sound/movers/objects/objectHit.wav" ));
03829 
03830                                 //see if we can move to be next to the hand.. if it's not clear, break the throw.
03831                                 VectorClear(tAngles);
03832                                 tAngles[YAW] = ent->client->ps.viewangles[YAW];
03833                                 VectorCopy(ent->client->ps.origin, pBoltOrg);
03834                                 AngleVectors(tAngles, fwd, right, 0);
03835                                 boltOrg[0] = pBoltOrg[0] + fwd[0]*8 + right[0]*pDif;
03836                                 boltOrg[1] = pBoltOrg[1] + fwd[1]*8 + right[1]*pDif;
03837                                 boltOrg[2] = pBoltOrg[2];
03838 
03839                                 VectorSubtract(boltOrg, pBoltOrg, vDif);
03840                                 VectorNormalize(vDif);
03841 
03842                                 VectorClear(other->client->ps.velocity);
03843                                 intendedOrigin[0] = pBoltOrg[0] + vDif[0]*pDif;
03844                                 intendedOrigin[1] = pBoltOrg[1] + vDif[1]*pDif;
03845                                 intendedOrigin[2] = other->client->ps.origin[2];
03846 
03847                                 trap_Trace(&tr, intendedOrigin, other->r.mins, other->r.maxs, intendedOrigin, other->s.number, other->clipmask);
03848                                 trap_Trace(&tr2, ent->client->ps.origin, ent->r.mins, ent->r.maxs, intendedOrigin, ent->s.number, CONTENTS_SOLID);
03849 
03850                                 if (tr.fraction == 1.0 && !tr.startsolid && tr2.fraction == 1.0 && !tr2.startsolid)
03851                                 {
03852                                         VectorCopy(intendedOrigin, other->client->ps.origin);
03853                                 }
03854                                 else
03855                                 { //if the guy can't be put here then it's time to break the throw off.
03856                                         vec3_t oppDir;
03857                                         int strength = 4;
03858 
03859                                         other->client->ps.heldByClient = 0;
03860                                         other->client->beingThrown = 0;
03861                                         ent->client->doingThrow = 0;
03862 
03863                                         ent->client->ps.forceHandExtend = HANDEXTEND_NONE;
03864                                         G_EntitySound( ent, CHAN_VOICE, G_SoundIndex("*pain25.wav") );
03865 
03866                                         other->client->ps.forceHandExtend = HANDEXTEND_NONE;
03867                                         VectorSubtract(other->client->ps.origin, ent->client->ps.origin, oppDir);
03868                                         VectorNormalize(oppDir);
03869                                         other->client->ps.velocity[0] = oppDir[0]*(strength*40);
03870                                         other->client->ps.velocity[1] = oppDir[1]*(strength*40);
03871                                         other->client->ps.velocity[2] = 150;
03872 
03873                                         VectorSubtract(ent->client->ps.origin, other->client->ps.origin, oppDir);
03874                                         VectorNormalize(oppDir);
03875                                         ent->client->ps.velocity[0] = oppDir[0]*(strength*40);
03876                                         ent->client->ps.velocity[1] = oppDir[1]*(strength*40);
03877                                         ent->client->ps.velocity[2] = 150;
03878                                 }
03879                         }
03880                 }
03881         }
03882 #endif
03883 #ifdef VM_MEMALLOC_DEBUG
03884         else if (Q_stricmp(cmd, "debugTestAlloc") == 0)
03885         { //rww - small routine to stress the malloc trap stuff and make sure nothing bad is happening.
03886                 char *blah;
03887                 int i = 1;
03888                 int x;
03889 
03890                 //stress it. Yes, this will take a while. If it doesn't explode miserably in the process.
03891                 while (i < 32768)
03892                 {
03893                         x = 0;
03894 
03895                         trap_TrueMalloc((void **)&blah, i);
03896                         if (!blah)
03897                         { //pointer is returned null if allocation failed
03898                                 trap_SendServerCommand( -1, va("print \"Failed to alloc at %i!\n\"", i));
03899                                 break;
03900                         }
03901                         while (x < i)
03902                         { //fill the allocated memory up to the edge
03903                                 if (x+1 == i)
03904                                 {
03905                                         blah[x] = 0;
03906                                 }
03907                                 else
03908                                 {
03909                                         blah[x] = 'A';
03910                                 }
03911                                 x++;
03912                         }
03913                         trap_TrueFree((void **)&blah);
03914                         if (blah)
03915                         { //should be nullified in the engine after being freed
03916                                 trap_SendServerCommand( -1, va("print \"Failed to free at %i!\n\"", i));
03917                                 break;
03918                         }
03919 
03920                         i++;
03921                 }
03922 
03923                 trap_SendServerCommand( -1, "print \"Finished allocation test\n\"");
03924         }
03925 #endif
03926 #ifndef FINAL_BUILD
03927         else if (Q_stricmp(cmd, "debugShipDamage") == 0)
03928         {
03929                 char    arg[MAX_STRING_CHARS];
03930                 char    arg2[MAX_STRING_CHARS];
03931                 int             shipSurf, damageLevel;
03932 
03933                 trap_Argv( 1, arg, sizeof( arg ) );
03934                 trap_Argv( 2, arg2, sizeof( arg2 ) );
03935                 shipSurf = SHIPSURF_FRONT+atoi(arg);
03936                 damageLevel = atoi(arg2);
03937 
03938                 G_SetVehDamageFlags( &g_entities[ent->s.m_iVehicleNum], shipSurf, damageLevel );
03939         }
03940 #endif
03941         else
03942         {
03943                 if (Q_stricmp(cmd, "addbot") == 0)
03944                 { //because addbot isn't a recognized command unless you're the server, but it is in the menus regardless
03945 //                      trap_SendServerCommand( clientNum, va("print \"You can only add bots as the server.\n\"" ) );
03946                         trap_SendServerCommand( clientNum, va("print \"%s.\n\"", G_GetStringEdString("MP_SVGAME", "ONLY_ADD_BOTS_AS_SERVER")));
03947                 }
03948                 else
03949                 {
03950                         trap_SendServerCommand( clientNum, va("print \"unknown cmd %s\n\"", cmd ) );
03951                 }
03952         }
03953 }