]> icculus.org git repositories - icculus/iodoom3.git/blob - neo/d3xp/MultiplayerGame.cpp
hello world
[icculus/iodoom3.git] / neo / d3xp / MultiplayerGame.cpp
1 /*
2 ===========================================================================
3
4 Doom 3 GPL Source Code
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company. 
6
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).  
8
9 Doom 3 Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13
14 Doom 3 Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with Doom 3 Source Code.  If not, see <http://www.gnu.org/licenses/>.
21
22 In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code.  If not, please request a copy in writing from id Software at the address below.
23
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
25
26 ===========================================================================
27 */
28
29 #include "../idlib/precompiled.h"
30 #pragma hdrstop
31
32 #include "Game_local.h"
33
34 // could be a problem if players manage to go down sudden deaths till this .. oh well
35 #define LASTMAN_NOLIVES -20
36
37 idCVar g_spectatorChat( "g_spectatorChat", "0", CVAR_GAME | CVAR_ARCHIVE | CVAR_BOOL, "let spectators talk to everyone during game" );
38
39 // global sounds transmitted by index - 0 .. SND_COUNT
40 // sounds in this list get precached on MP start
41 const char *idMultiplayerGame::GlobalSoundStrings[] = {
42         "sound/feedback/voc_youwin.wav",
43         "sound/feedback/voc_youlose.wav",
44         "sound/feedback/fight.wav",
45         "sound/feedback/vote_now.wav",
46         "sound/feedback/vote_passed.wav",
47         "sound/feedback/vote_failed.wav",
48         "sound/feedback/three.wav",
49         "sound/feedback/two.wav",
50         "sound/feedback/one.wav",
51         "sound/feedback/sudden_death.wav",
52 #ifdef CTF
53         "sound/ctf/flag_capped_yours.wav",
54         "sound/ctf/flag_capped_theirs.wav",
55         "sound/ctf/flag_return.wav",
56         "sound/ctf/flag_taken_yours.wav",
57         "sound/ctf/flag_taken_theirs.wav",
58         "sound/ctf/flag_dropped_yours.wav",
59         "sound/ctf/flag_dropped_theirs.wav"
60 #endif
61 };
62
63 // handy verbose
64 const char *idMultiplayerGame::GameStateStrings[] = {
65         "INACTIVE",
66         "WARMUP",
67         "COUNTDOWN",
68         "GAMEON",
69         "SUDDENDEATH",
70         "GAMEREVIEW",
71         "NEXTGAME"
72 };
73
74 const char *idMultiplayerGame::MPGuis[] = {
75         "guis/mphud.gui",
76         "guis/mpmain.gui",
77         "guis/mpmsgmode.gui",
78         "guis/netmenu.gui",
79         NULL
80 };
81
82 const char *idMultiplayerGame::ThrottleVars[] = {
83         "ui_spectate",
84         "ui_ready",
85         "ui_team",
86         NULL
87 };
88
89 const char *idMultiplayerGame::ThrottleVarsInEnglish[] = {
90         "#str_06738",
91         "#str_06737",
92         "#str_01991",
93         NULL
94 };
95
96 const int idMultiplayerGame::ThrottleDelay[] = {
97         8,
98         5,
99         5
100 };
101
102 /*
103 ================
104 idMultiplayerGame::idMultiplayerGame
105 ================
106 */
107 idMultiplayerGame::idMultiplayerGame() {
108         scoreBoard = NULL;
109         spectateGui = NULL;
110         guiChat = NULL;
111         mainGui = NULL;
112         mapList = NULL;
113         msgmodeGui = NULL;
114         lastGameType = GAME_SP;
115
116 #ifdef CTF
117         teamFlags[0] = NULL;
118         teamFlags[1] = NULL;
119
120         teamPoints[0] = 0;
121         teamPoints[1] = 0;
122
123         flagMsgOn = true;
124
125     player_blue_flag = -1;
126     player_red_flag = -1;
127 #endif
128     
129         Clear();
130 }
131
132 /*
133 ================
134 idMultiplayerGame::Shutdown
135 ================
136 */
137 void idMultiplayerGame::Shutdown( void ) {
138         Clear();
139 }
140
141 /*
142 ================
143 idMultiplayerGame::SetMenuSkin
144 ================
145 */
146 void idMultiplayerGame::SetMenuSkin( void ) {
147         // skins
148         idStr str = cvarSystem->GetCVarString( "mod_validSkins" );
149         idStr uiSkin = cvarSystem->GetCVarString( "ui_skin" );
150         idStr skin;
151         int skinId = 1;
152         int count = 1;
153         while ( str.Length() ) {
154                 int n = str.Find( ";" );
155                 if ( n >= 0 ) {
156                         skin = str.Left( n );
157                         str = str.Right( str.Length() - n - 1 );
158                 } else {
159                         skin = str;
160                         str = "";
161                 }
162                 if ( skin.Icmp( uiSkin ) == 0 ) {
163                         skinId = count;
164                 }
165                 count++;
166         }
167
168         for ( int i = 0; i < count; i++ ) {
169                 mainGui->SetStateInt( va( "skin%i", i+1 ), 0 );
170         }
171         mainGui->SetStateInt( va( "skin%i", skinId ), 1 );
172 }
173
174 /*
175 ================
176 idMultiplayerGame::Reset
177 ================
178 */
179 void idMultiplayerGame::Reset() {
180         Clear();
181         assert( !scoreBoard && !spectateGui && !guiChat && !mainGui && !mapList );
182
183 #ifdef CTF
184     // CTF uses its own scoreboard
185     if ( IsGametypeFlagBased() )
186         scoreBoard = uiManager->FindGui( "guis/ctfscoreboard.gui", true, false, true );
187     else
188 #endif
189         scoreBoard = uiManager->FindGui( "guis/scoreboard.gui", true, false, true );
190     
191         spectateGui = uiManager->FindGui( "guis/spectate.gui", true, false, true );
192         guiChat = uiManager->FindGui( "guis/chat.gui", true, false, true );
193         mainGui = uiManager->FindGui( "guis/mpmain.gui", true, false, true );
194         mapList = uiManager->AllocListGUI( );
195         mapList->Config( mainGui, "mapList" );
196         // set this GUI so that our Draw function is still called when it becomes the active/fullscreen GUI
197         mainGui->SetStateBool( "gameDraw", true );
198         mainGui->SetKeyBindingNames();
199         mainGui->SetStateInt( "com_machineSpec", cvarSystem->GetCVarInteger( "com_machineSpec" ) );
200         SetMenuSkin();
201         msgmodeGui = uiManager->FindGui( "guis/mpmsgmode.gui", true, false, true );
202         msgmodeGui->SetStateBool( "gameDraw", true );
203         ClearGuis();
204         ClearChatData();
205         warmupEndTime = 0;
206 }
207
208 /*
209 ================
210 idMultiplayerGame::ServerClientConnect
211 ================
212 */
213 void idMultiplayerGame::ServerClientConnect( int clientNum ) {
214         memset( &playerState[ clientNum ], 0, sizeof( playerState[ clientNum ] ) );
215 }
216
217 /*
218 ================
219 idMultiplayerGame::SpawnPlayer
220 ================
221 */
222 void idMultiplayerGame::SpawnPlayer( int clientNum ) {
223
224         bool ingame = playerState[ clientNum ].ingame;
225
226         memset( &playerState[ clientNum ], 0, sizeof( playerState[ clientNum ] ) );
227         if ( !gameLocal.isClient ) {
228                 idPlayer *p = static_cast< idPlayer * >( gameLocal.entities[ clientNum ] );
229                 p->spawnedTime = gameLocal.time;
230
231                 if ( IsGametypeTeamBased() ) {  /* CTF */ 
232                         SwitchToTeam( clientNum, -1, p->team );
233                 }
234                 p->tourneyRank = 0;
235                 if ( gameLocal.gameType == GAME_TOURNEY && gameState == GAMEON ) {
236                         p->tourneyRank++;
237                 }
238                 playerState[ clientNum ].ingame = ingame;
239         }
240 }
241
242 /*
243 ================
244 idMultiplayerGame::Clear
245 ================
246 */
247 void idMultiplayerGame::Clear() {
248         int i;
249
250         gameState = INACTIVE;
251         nextState = INACTIVE;
252         pingUpdateTime = 0;
253         vote = VOTE_NONE;
254         voteTimeOut = 0;
255         voteExecTime = 0;
256         nextStateSwitch = 0;
257         matchStartedTime = 0;
258         currentTourneyPlayer[ 0 ] = -1;
259         currentTourneyPlayer[ 1 ] = -1;
260         one = two = three = false;
261         memset( &playerState, 0 , sizeof( playerState ) );
262         lastWinner = -1;
263         currentMenu = 0;
264         bCurrentMenuMsg = false;
265         nextMenu = 0;
266         pureReady = false;
267         scoreBoard = NULL;
268         spectateGui = NULL;
269         guiChat = NULL;
270         mainGui = NULL;
271         msgmodeGui = NULL;
272         if ( mapList ) {
273                 uiManager->FreeListGUI( mapList );
274                 mapList = NULL;
275         }
276         fragLimitTimeout = 0;
277         memset( &switchThrottle, 0, sizeof( switchThrottle ) );
278         voiceChatThrottle = 0;
279         for ( i = 0; i < NUM_CHAT_NOTIFY; i++ ) {
280                 chatHistory[ i ].line.Clear();
281         }
282         warmupText.Clear();
283         voteValue.Clear();
284         voteString.Clear();
285         startFragLimit = -1;
286 }
287
288 /*
289 ================
290 idMultiplayerGame::ClearGuis
291 ================
292 */
293 void idMultiplayerGame::ClearGuis() {
294         int i;
295         
296         for ( i = 0; i < MAX_CLIENTS; i++ ) {
297                 scoreBoard->SetStateString( va( "player%i",i+1 ), "" );
298                 scoreBoard->SetStateString( va( "player%i_score", i+1 ), "" );
299                 scoreBoard->SetStateString( va( "player%i_tdm_tscore", i+1 ), "" );
300                 scoreBoard->SetStateString( va( "player%i_tdm_score", i+1 ), "" );
301                 scoreBoard->SetStateString( va( "player%i_wins", i+1 ), "" );
302                 scoreBoard->SetStateString( va( "player%i_status", i+1 ), "" );
303                 scoreBoard->SetStateInt( va( "rank%i", i+1 ), 0 );
304                 scoreBoard->SetStateInt( "rank_self", 0 );
305
306                 idPlayer *player = static_cast<idPlayer *>( gameLocal.entities[ i ] );
307                 if ( !player || !player->hud ) {
308                         continue;
309                 }
310                 player->hud->SetStateString( va( "player%i",i+1 ), "" );
311                 player->hud->SetStateString( va( "player%i_score", i+1 ), "" );
312                 player->hud->SetStateString( va( "player%i_ready", i+1 ), "" );
313                 scoreBoard->SetStateInt( va( "rank%i", i+1 ), 0 );
314                 player->hud->SetStateInt( "rank_self", 0 );
315
316         }       
317     
318 #ifdef CTF
319     ClearHUDStatus();
320 #endif  
321 }
322
323 #ifdef CTF
324 /*
325 ================
326 idMultiplayerGame::ClearHUDStatus
327 ================
328 */
329 void idMultiplayerGame::ClearHUDStatus( void ) {
330     int i;
331
332     for ( i = 0; i < MAX_CLIENTS; i++ ) {
333         
334                 idPlayer *player = static_cast<idPlayer *>( gameLocal.entities[ i ] );
335                 if ( !player || !player->hud ) {
336                         continue;
337                 }
338         
339         player->hud->SetStateInt( "red_flagstatus", 0 );
340         player->hud->SetStateInt( "blue_flagstatus", 0 );
341         if ( IsGametypeFlagBased())
342             player->hud->SetStateInt( "self_team", player->team );
343         else
344             player->hud->SetStateInt( "self_team", -1 ); // Invisible.
345     }
346     
347 }
348
349 /*
350 ================
351 idMultiplayerGame::GetFlagPoints
352
353 Gets number of captures in CTF game.
354
355 0 = red team
356 1 = blue team
357 ================
358 */
359 int idMultiplayerGame::GetFlagPoints( int team )
360 {
361     assert( team <= 1 );
362     
363     return teamPoints[ team ];
364 }
365 #endif
366
367 /*
368 ================
369 idMultiplayerGame::UpdatePlayerRanks
370 ================
371 */
372 void idMultiplayerGame::UpdatePlayerRanks() {
373         int i, j, k;
374         idPlayer *players[MAX_CLIENTS];
375         idEntity *ent;
376         idPlayer *player;
377
378         memset( players, 0, sizeof( players ) );
379         numRankedPlayers = 0;
380
381         for ( i = 0; i < gameLocal.numClients; i++ ) {
382                 ent = gameLocal.entities[ i ];
383                 if ( !ent || !ent->IsType( idPlayer::Type ) ) {
384                         continue;
385                 }
386                 player = static_cast< idPlayer * >( ent );
387                 if ( !CanPlay( player ) ) {
388                         continue;
389                 }
390                 if ( gameLocal.gameType == GAME_TOURNEY ) {
391                         if ( i != currentTourneyPlayer[ 0 ] && i != currentTourneyPlayer[ 1 ] ) {
392                                 continue;
393                         }
394                 }
395                 if ( gameLocal.gameType == GAME_LASTMAN && playerState[ i ].fragCount == LASTMAN_NOLIVES ) {
396                         continue;
397                 }
398                 for ( j = 0; j < numRankedPlayers; j++ ) {
399                         bool insert = false;
400
401                         if ( IsGametypeTeamBased() ) { /* CTF */
402                                 if ( player->team != players[ j ]->team ) {
403                                         if ( playerState[ i ].teamFragCount > playerState[ players[ j ]->entityNumber ].teamFragCount ) {
404                                                 // team scores
405                                                 insert = true;
406                                         } else if ( playerState[ i ].teamFragCount == playerState[ players[ j ]->entityNumber ].teamFragCount && player->team < players[ j ]->team ) {
407                                                 // at equal scores, sort by team number
408                                                 insert = true;
409                                         }
410                                 } else if ( playerState[ i ].fragCount > playerState[ players[ j ]->entityNumber ].fragCount ) {
411                                         // in the same team, sort by frag count
412                                         insert = true;
413                                 }
414                         } else {
415                                 insert = ( playerState[ i ].fragCount > playerState[ players[ j ]->entityNumber ].fragCount );
416                         }
417                         if ( insert ) {
418                                 for ( k = numRankedPlayers; k > j; k-- ) {
419                                         players[ k ] = players[ k-1 ];
420                                 }
421                                 players[ j ] = player;
422                                 break;
423                         }
424                 }
425                 if ( j == numRankedPlayers ) {
426                         players[ numRankedPlayers ] = player;
427                 }
428                 numRankedPlayers++;
429         }
430
431         memcpy( rankedPlayers, players, sizeof( players ) );
432 }
433
434
435 /*
436 ================
437 idMultiplayerGame::UpdateRankColor
438 ================
439 */
440 void idMultiplayerGame::UpdateRankColor( idUserInterface *gui, const char *mask, int i, const idVec3 &vec ) {
441         for ( int j = 1; j < 4; j++ ) {
442                 gui->SetStateFloat( va( mask, i, j ), vec[ j - 1 ] );
443         }
444 }
445
446 /*
447 ================
448 idMultiplayerGame::UpdateScoreboard
449 ================
450 */
451 void idMultiplayerGame::UpdateScoreboard( idUserInterface *scoreBoard, idPlayer *player ) {
452         int i, j, iline, k;
453         idStr gameinfo;
454 #ifdef _D3XP
455         idStr livesinfo;
456         idStr timeinfo;
457 #endif
458
459         idEntity *ent;
460         idPlayer *p;
461         int value;
462
463         scoreBoard->SetStateString( "scoretext", gameLocal.gameType == GAME_LASTMAN ? common->GetLanguageDict()->GetString( "#str_04242" ) : common->GetLanguageDict()->GetString( "#str_04243" ) );
464
465         iline = 0; // the display lines
466         if ( gameState != WARMUP ) {
467                 for ( i = 0; i < numRankedPlayers; i++ ) {
468                         // ranked player
469                         iline++;
470                         scoreBoard->SetStateString( va( "player%i", iline ), rankedPlayers[ i ]->GetUserInfo()->GetString( "ui_name" ) );
471                         if ( IsGametypeTeamBased() ) { /* CTF */
472                                 value = idMath::ClampInt( MP_PLAYER_MINFRAGS, MP_PLAYER_MAXFRAGS, playerState[ rankedPlayers[ i ]->entityNumber ].fragCount );
473                                 scoreBoard->SetStateInt( va( "player%i_tdm_score", iline ), value );
474                                 value = idMath::ClampInt( MP_PLAYER_MINFRAGS, MP_PLAYER_MAXFRAGS, playerState[ rankedPlayers[ i ]->entityNumber ].teamFragCount );
475                                 scoreBoard->SetStateString( va( "player%i_tdm_tscore", iline ), va( "/ %i", value ) );
476                                 scoreBoard->SetStateString( va( "player%i_score", iline ), "" );
477                         } else {
478                                 value = idMath::ClampInt( MP_PLAYER_MINFRAGS, MP_PLAYER_MAXFRAGS, playerState[ rankedPlayers[ i ]->entityNumber ].fragCount );
479                                 scoreBoard->SetStateInt( va( "player%i_score", iline ), value );
480                                 scoreBoard->SetStateString( va( "player%i_tdm_tscore", iline ), "" );
481                                 scoreBoard->SetStateString( va( "player%i_tdm_score", iline ), "" );
482                         }
483
484                         value = idMath::ClampInt( 0, MP_PLAYER_MAXWINS, playerState[ rankedPlayers[ i ]->entityNumber ].wins );
485                         scoreBoard->SetStateInt( va( "player%i_wins", iline ), value );
486                         scoreBoard->SetStateInt( va( "player%i_ping", iline ), playerState[ rankedPlayers[ i ]->entityNumber ].ping );
487                         // set the color band
488                         scoreBoard->SetStateInt( va( "rank%i", iline ), 1 );
489                         UpdateRankColor( scoreBoard, "rank%i_color%i", iline, rankedPlayers[ i ]->colorBar );
490                         if ( rankedPlayers[ i ] == player ) {
491                                 // highlight who we are
492                                 scoreBoard->SetStateInt( "rank_self", iline );
493                         }
494                 }
495         }
496
497         // if warmup, this draws everyone, otherwise it goes over spectators only
498         // when doing warmup we loop twice to draw ready/not ready first *then* spectators
499         // NOTE: in tourney, shows spectators according to their playing rank order?
500         for ( k = 0; k < ( gameState == WARMUP ? 2 : 1 ); k++ ) {
501                 for ( i = 0; i < MAX_CLIENTS; i++ ) {
502                         ent = gameLocal.entities[ i ];
503                         if ( !ent || !ent->IsType( idPlayer::Type ) ) {
504                                 continue;
505                         }
506                         if ( gameState != WARMUP ) {
507                                 // check he's not covered by ranks already
508                                 for ( j = 0; j < numRankedPlayers; j++ ) {
509                                         if ( ent == rankedPlayers[ j ] ) {
510                                                 break;
511                                         }
512                                 }
513                                 if ( j != numRankedPlayers ) {
514                                         continue;
515                                 }
516                         }
517                         p = static_cast< idPlayer * >( ent );
518                         if ( gameState == WARMUP ) {
519                                 if ( k == 0 && p->spectating ) {
520                                         continue;
521                                 }
522                                 if ( k == 1 && !p->spectating ) {
523                                         continue;
524                                 }
525                         }
526         
527                         iline++;
528                         if ( !playerState[ i ].ingame ) {
529                                 scoreBoard->SetStateString( va( "player%i", iline ), common->GetLanguageDict()->GetString( "#str_04244" ) );
530                                 scoreBoard->SetStateString( va( "player%i_score", iline ), common->GetLanguageDict()->GetString( "#str_04245" ) );
531                                 // no color band
532                                 scoreBoard->SetStateInt( va( "rank%i", iline ), 0 );
533                         } else {
534                                 scoreBoard->SetStateString( va( "player%i", iline ), gameLocal.userInfo[ i ].GetString( "ui_name" ) );
535                                 if ( gameState == WARMUP ) {
536                                         if ( p->spectating ) {
537                                                 scoreBoard->SetStateString( va( "player%i_score", iline ), common->GetLanguageDict()->GetString( "#str_04246" ) );
538                                                 // no color band
539                                                 scoreBoard->SetStateInt( va( "rank%i", iline ), 0 );
540                                         } else {
541                                                 scoreBoard->SetStateString( va( "player%i_score", iline ), p->IsReady() ? common->GetLanguageDict()->GetString( "#str_04247" ) : common->GetLanguageDict()->GetString( "#str_04248" ) );
542                                                 // set the color band
543                                                 scoreBoard->SetStateInt( va( "rank%i", iline ), 1 );
544                                                 UpdateRankColor( scoreBoard, "rank%i_color%i", iline, p->colorBar );
545                                         }
546                                 } else {
547                                         if ( gameLocal.gameType == GAME_LASTMAN && playerState[ i ].fragCount == LASTMAN_NOLIVES ) {
548                                                 scoreBoard->SetStateString( va( "player%i_score", iline ), common->GetLanguageDict()->GetString( "#str_06736" ) );
549                                                 // set the color band
550                                                 scoreBoard->SetStateInt( va( "rank%i", iline ), 1 );
551                                                 UpdateRankColor( scoreBoard, "rank%i_color%i", iline, p->colorBar );
552                                         } else {
553                                                 scoreBoard->SetStateString( va( "player%i_score", iline ), common->GetLanguageDict()->GetString( "#str_04246" ) );
554                                                 // no color band
555                                                 scoreBoard->SetStateInt( va( "rank%i", iline ), 0 );
556                                         }
557                                 }
558                         }
559                         scoreBoard->SetStateString( va( "player%i_tdm_tscore", iline ), "" );
560                         scoreBoard->SetStateString( va( "player%i_tdm_score", iline ), "" );
561                         scoreBoard->SetStateString( va( "player%i_wins", iline ), "" );
562                         scoreBoard->SetStateInt( va( "player%i_ping", iline ), playerState[ i ].ping );
563                         if ( i == player->entityNumber ) {
564                                 // highlight who we are
565                                 scoreBoard->SetStateInt( "rank_self", iline );
566                         }
567                 }
568         }
569
570         // clear remaining lines (empty slots)
571         iline++;
572 #ifdef _D3XP
573         while ( iline < MAX_CLIENTS ) { //Max players is now 8
574 #else
575         while ( iline < 5 ) {
576 #endif
577                 scoreBoard->SetStateString( va( "player%i", iline ), "" );
578                 scoreBoard->SetStateString( va( "player%i_score", iline ), "" );
579                 scoreBoard->SetStateString( va( "player%i_tdm_tscore", iline ), "" );
580                 scoreBoard->SetStateString( va( "player%i_tdm_score", iline ), "" );
581                 scoreBoard->SetStateString( va( "player%i_wins", iline ), "" );
582                 scoreBoard->SetStateString( va( "player%i_ping", iline ), "" );
583                 scoreBoard->SetStateInt( va( "rank%i", iline ), 0 );
584                 iline++;
585         }
586
587         gameinfo = va( "%s: %s", common->GetLanguageDict()->GetString( "#str_02376" ), gameLocal.serverInfo.GetString( "si_gameType" ) );
588         if ( gameLocal.gameType == GAME_LASTMAN ) {
589                 if ( gameState == GAMEON || gameState == SUDDENDEATH ) {
590                         livesinfo = va( "%s: %i", common->GetLanguageDict()->GetString( "#str_04264" ), startFragLimit );
591                 } else {
592                         livesinfo = va( "%s: %i", common->GetLanguageDict()->GetString( "#str_04264" ), gameLocal.serverInfo.GetInt( "si_fragLimit" ) );
593                 }
594 #ifdef CTF
595         } else if ( gameLocal.gameType != GAME_CTF ) {
596 #else
597         } else {
598 #endif 
599                 livesinfo = va( "%s: %i", common->GetLanguageDict()->GetString( "#str_01982" ), gameLocal.serverInfo.GetInt( "si_fragLimit" ) );
600         } 
601         if ( gameLocal.serverInfo.GetInt( "si_timeLimit" ) > 0 ) {
602                 timeinfo = va( "%s: %i", common->GetLanguageDict()->GetString( "#str_01983" ), gameLocal.serverInfo.GetInt( "si_timeLimit" ) );
603         } else {
604                 timeinfo = va("%s", common->GetLanguageDict()->GetString( "#str_07209" ));
605         }
606         scoreBoard->SetStateString( "gameinfo", gameinfo );
607         scoreBoard->SetStateString( "livesinfo", livesinfo );
608         scoreBoard->SetStateString( "timeinfo", timeinfo );
609
610         scoreBoard->Redraw( gameLocal.time );
611 }
612
613 #ifdef CTF 
614 /*
615 ================
616 idMultiplayerGame::UpdateCTFScoreboard
617 ================
618 */
619 void idMultiplayerGame::UpdateCTFScoreboard( idUserInterface *scoreBoard, idPlayer *player ) {
620         int i, j;
621         idStr gameinfo;
622         idEntity *ent;
623         int value;
624
625     // The display lines
626     int ilines[2] = {0,0};
627
628     // The team strings
629     char redTeam[] = "red";
630     char blueTeam[] = "blue";
631     char *curTeam = NULL;
632
633         /* Word "frags" */
634         scoreBoard->SetStateString( "scoretext", gameLocal.gameType == GAME_LASTMAN ? common->GetLanguageDict()->GetString( "#str_04242" ) : common->GetLanguageDict()->GetString( "#str_04243" ) );
635
636     // Blank the flag carrier on the scoreboard.  We update these in the loop below if necessary.
637     if ( this->player_blue_flag == -1 )
638                 scoreBoard->SetStateInt( "player_blue_flag", 0 );
639
640     if ( this->player_red_flag == -1 )
641         scoreBoard->SetStateInt( "player_red_flag", 0 );
642     
643         if ( gameState != WARMUP ) {
644                 for ( i = 0; i < numRankedPlayers; i++ ) {
645
646             idPlayer *player = rankedPlayers[ i ];
647             assert( player );
648
649             if ( player->team == 0 )
650                 curTeam = redTeam;
651             else
652                 curTeam = blueTeam;
653             
654             // Increase the appropriate iline
655             assert( player->team <= 1 );
656             ilines[ player->team ]++;
657
658
659                         // Update the flag status
660                         if ( this->player_blue_flag == player->entityNumber )
661                                 scoreBoard->SetStateInt( "player_blue_flag", ilines[ player->team ] );
662        
663                         if ( player->team == 1 && this->player_red_flag == player->entityNumber )       
664                                 scoreBoard->SetStateInt( "player_red_flag", ilines[ player->team ] );
665             
666
667
668             /* Player Name */
669             scoreBoard->SetStateString( va( "player%i_%s", ilines[ player->team ], curTeam ), player->GetUserInfo()->GetString( "ui_name" ) );
670             
671                         if ( IsGametypeTeamBased() ) { 
672                 
673                                 value = idMath::ClampInt( MP_PLAYER_MINFRAGS, MP_PLAYER_MAXFRAGS, playerState[ rankedPlayers[ i ]->entityNumber ].fragCount );
674                                 scoreBoard->SetStateInt( va( "player%i_%s_score", ilines[ player->team ], curTeam ), value );
675
676                 /* Team score and score, blanked */
677                 scoreBoard->SetStateString( va( "player%i_%s_tscore", ilines[ player->team ], curTeam ), "" );
678                 //scoreBoard->SetStateString( va( "player%i_%s_score",  ilines[ player->team ], curTeam ), "" );
679                         } 
680
681             /* Wins */
682                         value = idMath::ClampInt( 0, MP_PLAYER_MAXWINS, playerState[ rankedPlayers[ i ]->entityNumber ].wins );
683             scoreBoard->SetStateInt( va( "player%i_%s_wins", ilines[ player->team ], curTeam ), value );
684
685             /* Ping */
686             scoreBoard->SetStateInt( va( "player%i_%s_ping", ilines[ player->team ], curTeam ), playerState[ rankedPlayers[ i ]->entityNumber ].ping );
687                 }
688         }
689     
690     for ( i = 0; i < MAX_CLIENTS; i++ ) {
691             
692         ent = gameLocal.entities[ i ];
693         if ( !ent || !ent->IsType( idPlayer::Type ) ) {
694             continue;
695         }
696             
697         if ( gameState != WARMUP ) {    
698             // check he's not covered by ranks already
699             for ( j = 0; j < numRankedPlayers; j++ ) {
700                 if ( ent == rankedPlayers[ j ] ) {
701                     break;
702                 }
703             }
704                         
705             if ( j != numRankedPlayers ) {
706                 continue;
707             }
708                         
709         }
710         player = static_cast< idPlayer * >( ent );
711         
712         if ( player->spectating )
713             continue;
714
715         if ( player->team == 0 )
716             curTeam = redTeam;
717         else
718             curTeam = blueTeam;
719         
720         ilines[ player->team ]++;
721
722
723         
724    
725
726         if ( !playerState[ i ].ingame ) {
727
728             /* "New Player" on player's name location */
729             scoreBoard->SetStateString( va( "player%i_%s", ilines[ player->team ], curTeam ), common->GetLanguageDict()->GetString( "#str_04244" ) );
730
731             /* "Connecting" on player's score location */
732             scoreBoard->SetStateString( va( "player%i_%s_score", ilines[ player->team ], curTeam ), common->GetLanguageDict()->GetString( "#str_04245" ) );
733
734                 
735         } else {
736
737             /* Player's name in player's name location */
738             if ( !player->spectating )
739                 scoreBoard->SetStateString( va( "player%i_%s", ilines[ player->team ], curTeam ), gameLocal.userInfo[ i ].GetString( "ui_name" ) );
740                 
741             if ( gameState == WARMUP ) {
742
743                 if ( player->spectating ) {
744
745                     /* "Spectating" on player's score location */
746                     scoreBoard->SetStateString( va( "player%i_%s_score", ilines[ player->team ], curTeam ), common->GetLanguageDict()->GetString( "#str_04246" ) );
747
748                 } else {
749
750                     /* Display "ready" in player's score location if they're ready.  Display nothing if not.  No room for 'not ready'.  */
751                     scoreBoard->SetStateString( va( "player%i_%s_score", ilines[ player->team ], curTeam ), player->IsReady() ? common->GetLanguageDict()->GetString( "#str_04247" ) : "" );
752
753                 }
754             } 
755         }
756
757     }
758
759     // Clear remaining slots
760     for ( i = 0; i < 2; i++ )
761     {
762         if ( i )
763             curTeam = blueTeam;
764         else
765             curTeam = redTeam;
766         
767         for ( j = ilines[ i ]+1; j <= 8; j++ )
768         {
769             scoreBoard->SetStateString( va( "player%i_%s", j, curTeam ), "" );
770             scoreBoard->SetStateString( va( "player%i_%s_score", j, curTeam ), "" );
771             scoreBoard->SetStateString( va( "player%i_%s_wins", j, curTeam ), "" );
772             scoreBoard->SetStateString( va( "player%i_%s_ping", j, curTeam ), "" );
773             scoreBoard->SetStateInt( "rank_self", 0 );            
774         }
775     }
776
777
778     // Don't display "CTF" -- if this scoreboard comes up, it should be apparent.
779     
780         if ( gameLocal.gameType == GAME_CTF ) {
781
782         int captureLimit = gameLocal.serverInfo.GetInt( "si_fragLimit" );
783                 
784                 if ( captureLimit > MP_CTF_MAXPOINTS  )
785                         captureLimit = MP_CTF_MAXPOINTS;
786
787         int timeLimit    = gameLocal.serverInfo.GetInt( "si_timeLimit" );
788
789         /* Prints "Capture Limit: %i" at the bottom of the scoreboard, left */
790         if ( captureLimit )
791             scoreBoard->SetStateString( "gameinfo_red", va( common->GetLanguageDict()->GetString( "#str_11108" ), captureLimit) );
792         else
793             scoreBoard->SetStateString( "gameinfo_red", "" );
794
795         /* Prints "Time Limit: %i" at the bottom of the scoreboard, right */
796         if ( timeLimit )
797             scoreBoard->SetStateString( "gameinfo_blue", va( common->GetLanguageDict()->GetString( "#str_11109" ), timeLimit) );
798         else
799             scoreBoard->SetStateString( "gameinfo_blue", "" );            
800         }
801     
802
803
804     // Set team scores
805     scoreBoard->SetStateInt( "red_team_score", GetFlagPoints( 0 ) );
806     scoreBoard->SetStateInt( "blue_team_score", GetFlagPoints( 1 ) );
807
808         // Handle flag status changed event
809         scoreBoard->HandleNamedEvent( "BlueFlagStatusChange" );
810     scoreBoard->HandleNamedEvent( "RedFlagStatusChange" );
811         
812         scoreBoard->Redraw( gameLocal.time );
813
814         
815         
816
817 }
818 #endif
819
820 /*
821 ================
822 idMultiplayerGame::GameTime
823 ================
824 */
825 const char *idMultiplayerGame::GameTime() {
826         static char buff[16];
827         int m, s, t, ms;
828
829         if ( gameState == COUNTDOWN ) {
830                 ms = warmupEndTime - gameLocal.realClientTime;
831                 s = ms / 1000 + 1;
832                 if ( ms <= 0 ) {
833                         strcpy( buff, "WMP --" );
834                 } else {
835                         sprintf( buff, "WMP %i", s );
836                 }
837         } else {
838                 int timeLimit = gameLocal.serverInfo.GetInt( "si_timeLimit" );
839                 if ( timeLimit ) {
840                         ms = ( timeLimit * 60000 ) - ( gameLocal.time - matchStartedTime );
841                 } else {
842                         ms = gameLocal.time - matchStartedTime;
843                 }
844                 if ( ms < 0 ) {
845                         ms = 0;
846                 }
847         
848                 s = ms / 1000;
849                 m = s / 60;
850                 s -= m * 60;
851                 t = s / 10;
852                 s -= t * 10;
853
854                 sprintf( buff, "%i:%i%i", m, t, s );
855         }
856         return &buff[0];
857 }
858
859 /*
860 ================
861 idMultiplayerGame::NumActualClients
862 ================
863 */
864 int idMultiplayerGame::NumActualClients( bool countSpectators, int *teamcounts ) {
865         idPlayer *p;
866         int c = 0;
867
868         if ( teamcounts ) {
869                 teamcounts[ 0 ] = teamcounts[ 1 ] = 0;
870         }
871         for( int i = 0 ; i < gameLocal.numClients ; i++ ) {
872                 idEntity *ent = gameLocal.entities[ i ];
873                 if ( !ent || !ent->IsType( idPlayer::Type ) ) {
874                         continue;
875                 }
876                 p = static_cast< idPlayer * >( ent );
877                 if ( countSpectators || CanPlay( p ) ) {
878                         c++;
879                 }
880                 if ( teamcounts && CanPlay( p ) ) {
881                         teamcounts[ p->team ]++;
882                 }
883         }
884         return c;
885 }
886
887 /*
888 ================
889 idMultiplayerGame::EnoughClientsToPlay
890 ================
891 */
892 bool idMultiplayerGame::EnoughClientsToPlay() {
893         int team[ 2 ];
894         int clients = NumActualClients( false, &team[ 0 ] );
895         if ( IsGametypeTeamBased() ) { /* CTF */
896                 return clients >= 2 && team[ 0 ] && team[ 1 ];
897         } else {
898                 return clients >= 2;
899         }
900 }
901
902 /*
903 ================
904 idMultiplayerGame::AllPlayersReady
905 ================
906 */
907 bool idMultiplayerGame::AllPlayersReady() {
908         int                     i;
909         idEntity        *ent;
910         idPlayer        *p;
911         int                     team[ 2 ];
912
913         if ( NumActualClients( false, &team[ 0 ] ) <= 1 ) {
914                 return false;
915         }
916
917         if ( IsGametypeTeamBased() ) { /* CTF */
918                 if ( !team[ 0 ] || !team[ 1 ] ) {
919                         return false;
920                 }
921         }
922
923         if ( !gameLocal.serverInfo.GetBool( "si_warmup" ) ) {
924                 return true;
925         }
926
927         for( i = 0; i < gameLocal.numClients; i++ ) {
928                 if ( gameLocal.gameType == GAME_TOURNEY && i != currentTourneyPlayer[ 0 ] && i != currentTourneyPlayer[ 1 ] ) {
929                         continue;
930                 }
931                 ent = gameLocal.entities[ i ];
932                 if ( !ent || !ent->IsType( idPlayer::Type ) ) {
933                         continue;
934                 }
935                 p = static_cast< idPlayer * >( ent );
936                 if ( CanPlay( p ) && !p->IsReady() ) {
937                         return false;
938                 }
939                 team[ p->team ]++;
940         }
941
942         return true;
943 }
944
945 /*
946 ================
947 idMultiplayerGame::FragLimitHit
948 return the winning player (team player)
949 if there is no FragLeader(), the game is tied and we return NULL
950 ================
951 */
952 idPlayer *idMultiplayerGame::FragLimitHit() {
953         int i;
954         int fragLimit           = gameLocal.serverInfo.GetInt( "si_fragLimit" );
955         idPlayer *leader;
956
957 #ifdef CTF
958         if ( IsGametypeFlagBased() ) /* CTF */
959                 return NULL;
960 #endif
961
962         leader = FragLeader();
963         if ( !leader ) {
964                 return NULL;
965         }
966
967         if ( fragLimit <= 0 ) {
968                 fragLimit = MP_PLAYER_MAXFRAGS;
969         }
970
971         if ( gameLocal.gameType == GAME_LASTMAN ) {
972                 // we have a leader, check if any other players have frags left
973                 assert( !static_cast< idPlayer * >( leader )->lastManOver );
974                 for( i = 0 ; i < gameLocal.numClients ; i++ ) {
975                         idEntity *ent = gameLocal.entities[ i ];
976                         if ( !ent || !ent->IsType( idPlayer::Type ) ) {
977                                 continue;
978                         }
979                         if ( !CanPlay( static_cast< idPlayer * >( ent ) ) ) {
980                                 continue;
981                         }
982                         if ( ent == leader ) {
983                                 continue;
984                         }
985                         if ( playerState[ ent->entityNumber ].fragCount > 0 ) {
986                                 return NULL;
987                         }
988                 }
989                 // there is a leader, his score may even be negative, but no one else has frags left or is !lastManOver
990                 return leader;
991         } else if ( IsGametypeTeamBased() ) { /* CTF */
992                 if ( playerState[ leader->entityNumber ].teamFragCount >= fragLimit ) {
993                         return leader;
994                 }
995         } else {
996                 if ( playerState[ leader->entityNumber ].fragCount >= fragLimit ) {
997                         return leader;
998                 }
999         }
1000
1001         return NULL;
1002 }
1003
1004 /*
1005 ================
1006 idMultiplayerGame::TimeLimitHit
1007 ================
1008 */
1009 bool idMultiplayerGame::TimeLimitHit() {        
1010         int timeLimit = gameLocal.serverInfo.GetInt( "si_timeLimit" );
1011         if ( timeLimit ) {
1012                 if ( gameLocal.time >= matchStartedTime + timeLimit * 60000 ) {
1013                         return true;
1014                 }
1015         }
1016         return false;
1017 }
1018
1019 #ifdef CTF
1020
1021 /*
1022 ================
1023 idMultiplayerGame::WinningTeam
1024 return winning team
1025 -1 if tied or no players
1026 ================
1027 */
1028 int idMultiplayerGame::WinningTeam( void ) {
1029         if ( teamPoints[0] > teamPoints[1] )
1030                 return 0;
1031         if ( teamPoints[0] < teamPoints[1] )
1032                 return 1;
1033         return -1;
1034 }
1035
1036 /*
1037 ================
1038 idMultiplayerGame::PointLimitHit
1039 ================
1040 */
1041 bool idMultiplayerGame::PointLimitHit( void ) {
1042         int pointLimit = gameLocal.serverInfo.GetInt( "si_fragLimit" );
1043
1044         // default to MP_CTF_MAXPOINTS if needed
1045         if ( pointLimit > MP_CTF_MAXPOINTS )
1046                 pointLimit = MP_CTF_MAXPOINTS;
1047         else if ( pointLimit <= 0 )
1048                 pointLimit = MP_CTF_MAXPOINTS;
1049
1050         if ( teamPoints[0] == teamPoints[1] )
1051                 return false;
1052
1053         if ( teamPoints[0] >= pointLimit ||
1054                  teamPoints[1] >= pointLimit )
1055                  return true;
1056
1057         return false;
1058 }
1059 #endif
1060
1061 /*
1062 ================
1063 idMultiplayerGame::FragLeader
1064 return the current winner ( or a player from the winning team )
1065 NULL if even
1066 ================
1067 */
1068 idPlayer *idMultiplayerGame::FragLeader( void ) {
1069         int i;
1070         int frags[ MAX_CLIENTS ];
1071         idPlayer *leader = NULL;
1072         idEntity *ent;
1073         idPlayer *p;
1074         int high = -9999;
1075         int count = 0;
1076         bool teamLead[ 2 ] = { false, false };
1077
1078         for ( i = 0 ; i < gameLocal.numClients ; i++ ) {
1079                 ent = gameLocal.entities[ i ];
1080                 if ( !ent || !ent->IsType( idPlayer::Type ) ) {
1081                         continue;
1082                 }
1083                 if ( !CanPlay( static_cast< idPlayer * >( ent ) ) ) {
1084                         continue;
1085                 }
1086                 if ( gameLocal.gameType == GAME_TOURNEY && ent->entityNumber != currentTourneyPlayer[ 0 ] && ent->entityNumber != currentTourneyPlayer[ 1 ] ) {
1087                         continue;
1088                 }
1089                 if ( static_cast< idPlayer * >( ent )->lastManOver ) {
1090                         continue;
1091                 }
1092
1093                 int fragc = ( IsGametypeTeamBased() ) ? playerState[i].teamFragCount : playerState[i].fragCount; /* CTF */
1094                 if ( fragc > high ) {
1095                         high = fragc;
1096                 }
1097
1098                 frags[ i ] = fragc;
1099         }
1100
1101         for ( i = 0; i < gameLocal.numClients; i++ ) {
1102                 ent = gameLocal.entities[ i ];
1103                 if ( !ent || !ent->IsType( idPlayer::Type ) ) {
1104                         continue;
1105                 }
1106                 p = static_cast< idPlayer * >( ent );
1107                 p->SetLeader( false );
1108
1109                 if ( !CanPlay( p ) ) {
1110                         continue;
1111                 }
1112                 if ( gameLocal.gameType == GAME_TOURNEY && ent->entityNumber != currentTourneyPlayer[ 0 ] && ent->entityNumber != currentTourneyPlayer[ 1 ] ) {
1113                         continue;
1114                 }
1115                 if ( p->lastManOver ) {
1116                         continue;
1117                 }
1118                 if ( p->spectating ) {
1119                         continue;
1120                 }
1121
1122                 if ( frags[ i ] >= high ) {
1123                         leader = p;
1124                         count++;
1125                         p->SetLeader( true );
1126                         if ( IsGametypeTeamBased() ) { /* CTF */
1127                                 teamLead[ p->team ] = true;
1128                         }
1129                 }
1130         }
1131
1132         if ( !IsGametypeTeamBased() ) { /* CTF */
1133                 // more than one player at the highest frags
1134                 if ( count > 1 ) {
1135                         return NULL;
1136                 } else {
1137                         return leader;
1138                 }
1139         } else {
1140                 if ( teamLead[ 0 ] && teamLead[ 1 ] ) {
1141                         // even game in team play
1142                         return NULL;
1143                 }
1144                 return leader;
1145         }
1146 }
1147
1148 /*
1149 ================
1150 idGameLocal::UpdateWinsLosses
1151 ================
1152 */
1153 void idMultiplayerGame::UpdateWinsLosses( idPlayer *winner ) {
1154         if ( winner ) {
1155                 // run back through and update win/loss count
1156                 for( int i = 0; i < gameLocal.numClients; i++ ) {
1157                         idEntity *ent = gameLocal.entities[ i ];
1158                         if ( !ent || !ent->IsType( idPlayer::Type ) ) {
1159                                 continue;
1160                         }
1161                         idPlayer *player = static_cast<idPlayer *>(ent);
1162                         if ( IsGametypeTeamBased() ) { /* CTF */
1163                                 if ( player == winner || ( player != winner && player->team == winner->team ) ) {
1164                                         playerState[ i ].wins++;
1165                                         PlayGlobalSound( player->entityNumber, SND_YOUWIN );
1166                                 } else {
1167                                         PlayGlobalSound( player->entityNumber, SND_YOULOSE );
1168                                 }
1169                         } else if ( gameLocal.gameType == GAME_LASTMAN ) {
1170                                 if ( player == winner ) {
1171                                         playerState[ i ].wins++;
1172                                         PlayGlobalSound( player->entityNumber, SND_YOUWIN );
1173                                 } else if ( !player->wantSpectate ) {
1174                                         PlayGlobalSound( player->entityNumber, SND_YOULOSE );
1175                                 }
1176                         } else if ( gameLocal.gameType == GAME_TOURNEY ) {
1177                                 if ( player == winner ) { 
1178                                         playerState[ i ].wins++;
1179                                         PlayGlobalSound( player->entityNumber, SND_YOUWIN );
1180                                 } else if ( i == currentTourneyPlayer[ 0 ] || i == currentTourneyPlayer[ 1 ] ) {
1181                                         PlayGlobalSound( player->entityNumber, SND_YOULOSE );
1182                                 }
1183                         } else {
1184                                 if ( player == winner ) {
1185                                         playerState[i].wins++;
1186                                         PlayGlobalSound( player->entityNumber, SND_YOUWIN );
1187                                 } else if ( !player->wantSpectate ) {
1188                                         PlayGlobalSound( player->entityNumber, SND_YOULOSE );
1189                                 }
1190                         }
1191                 }
1192         }
1193 #ifdef CTF
1194         else if ( IsGametypeFlagBased() ) { /* CTF */
1195                 int winteam = WinningTeam();
1196
1197                 if ( winteam != -1 )    // TODO : print a message telling it why the hell the game ended with no winning team?
1198                 for( int i = 0; i < gameLocal.numClients; i++ ) {
1199                         idEntity *ent = gameLocal.entities[ i ];
1200                         if ( !ent || !ent->IsType( idPlayer::Type ) ) {
1201                                 continue;
1202                         }
1203                         idPlayer *player = static_cast<idPlayer *>(ent);
1204
1205                         if ( player->team == winteam ) {
1206                                 PlayGlobalSound( player->entityNumber, SND_YOUWIN );
1207                         } else {
1208                                 PlayGlobalSound( player->entityNumber, SND_YOULOSE );
1209                         }
1210                 }
1211         }
1212 #endif
1213     
1214         if ( winner ) {
1215                 lastWinner = winner->entityNumber;
1216         } else {
1217                 lastWinner = -1;
1218         }
1219 }
1220
1221 #ifdef CTF
1222 /*
1223 ================
1224 idMultiplayerGame::TeamScoreCTF
1225 ================
1226 */
1227 void idMultiplayerGame::TeamScoreCTF( int team, int delta ) {
1228         if ( team < 0 || team > 1 )
1229                 return;
1230
1231         teamPoints[team] += delta;
1232
1233         if ( gameState == GAMEON || gameState == SUDDENDEATH )
1234         PrintMessageEvent( -1, MSG_SCOREUPDATE, teamPoints[0], teamPoints[1] );
1235 }
1236
1237 /*
1238 ================
1239 idMultiplayerGame::PlayerScoreCTF
1240 ================
1241 */
1242 void idMultiplayerGame::PlayerScoreCTF( int playerIdx, int delta ) {
1243         if ( playerIdx < 0 || playerIdx >= MAX_CLIENTS )
1244                 return;
1245
1246         playerState[ playerIdx ].fragCount += delta;
1247 }
1248
1249 /*
1250 ================
1251 idMultiplayerGame::GetFlagCarrier
1252 ================
1253 */
1254 int     idMultiplayerGame::GetFlagCarrier( int team ) {
1255         int iFlagCarrier = -1;
1256
1257         for ( int i = 0; i < gameLocal.numClients; i++ ) {
1258                 idEntity * ent = gameLocal.entities[ i ];
1259                 if ( !ent || !ent->IsType( idPlayer::Type ) ) {
1260                         continue;
1261                 }
1262
1263                 idPlayer * player = static_cast<idPlayer *>( ent );
1264                 if ( player->team != team )
1265                         continue;
1266
1267                 if ( player->carryingFlag ) {
1268                         if ( iFlagCarrier != -1 )
1269                                 gameLocal.Warning( "BUG: more than one flag carrier on %s team", team == 0 ? "red" : "blue" );
1270                         iFlagCarrier = i;
1271                 }
1272         }
1273
1274         return iFlagCarrier;
1275 }
1276
1277
1278
1279 #endif
1280
1281 /*
1282 ================
1283 idMultiplayerGame::TeamScore
1284 ================
1285 */
1286 void idMultiplayerGame::TeamScore( int entityNumber, int team, int delta ) {
1287         playerState[ entityNumber ].fragCount += delta;
1288         for( int i = 0 ; i < gameLocal.numClients ; i++ ) {
1289                 idEntity *ent = gameLocal.entities[ i ];
1290                 if ( !ent || !ent->IsType( idPlayer::Type ) ) {
1291                         continue;
1292                 }
1293                 idPlayer *player = static_cast<idPlayer *>(ent);
1294                 if ( player->team == team ) {
1295                         playerState[ player->entityNumber ].teamFragCount += delta;
1296                 }
1297         }
1298 }
1299
1300 /*
1301 ================
1302 idMultiplayerGame::PlayerDeath
1303 ================
1304 */
1305 void idMultiplayerGame::PlayerDeath( idPlayer *dead, idPlayer *killer, bool telefrag ) {
1306
1307         // don't do PrintMessageEvent and shit
1308         assert( !gameLocal.isClient );
1309
1310         if ( killer ) {
1311                 if ( gameLocal.gameType == GAME_LASTMAN ) {
1312                         playerState[ dead->entityNumber ].fragCount--;
1313             
1314                 } else if ( IsGametypeTeamBased() ) { /* CTF */
1315                         if ( killer == dead || killer->team == dead->team ) {
1316                                 // suicide or teamkill
1317                                 TeamScore( killer->entityNumber, killer->team, -1 );
1318                         } else {
1319                                 TeamScore( killer->entityNumber, killer->team, +1 );
1320                         }
1321                 } else {
1322                         playerState[ killer->entityNumber ].fragCount += ( killer == dead ) ? -1 : 1;
1323                 }
1324         }
1325
1326         if ( killer && killer == dead ) {
1327                 PrintMessageEvent( -1, MSG_SUICIDE, dead->entityNumber );
1328         } else if ( killer ) {
1329                 if ( telefrag ) {
1330                         PrintMessageEvent( -1, MSG_TELEFRAGGED, dead->entityNumber, killer->entityNumber );
1331                 } else if ( IsGametypeTeamBased() && dead->team == killer->team ) { /* CTF */
1332                         PrintMessageEvent( -1, MSG_KILLEDTEAM, dead->entityNumber, killer->entityNumber );
1333                 } else {
1334                         PrintMessageEvent( -1, MSG_KILLED, dead->entityNumber, killer->entityNumber );
1335                 }
1336         } else {
1337                 PrintMessageEvent( -1, MSG_DIED, dead->entityNumber );
1338                 playerState[ dead->entityNumber ].fragCount--;
1339         }
1340 }
1341
1342 /*
1343 ================
1344 idMultiplayerGame::PlayerStats
1345 ================
1346 */
1347 void idMultiplayerGame::PlayerStats( int clientNum, char *data, const int len ) {
1348
1349         idEntity *ent;
1350         int team;
1351
1352         *data = 0;
1353
1354         // make sure we don't exceed the client list
1355         if ( clientNum < 0 || clientNum > gameLocal.numClients ) {
1356                 return;
1357         }
1358
1359         // find which team this player is on
1360         ent = gameLocal.entities[ clientNum ]; 
1361         if ( ent && ent->IsType( idPlayer::Type ) ) {
1362                 team = static_cast< idPlayer * >(ent)->team;
1363         } else {
1364                 return;
1365         }
1366
1367         idStr::snPrintf( data, len, "team=%d score=%ld tks=%ld", team, playerState[ clientNum ].fragCount, playerState[ clientNum ].teamFragCount );
1368
1369         return;
1370
1371 }
1372
1373 /*
1374 ================
1375 idMultiplayerGame::PlayerVote
1376 ================
1377 */
1378 void idMultiplayerGame::PlayerVote( int clientNum, playerVote_t vote ) {
1379         playerState[ clientNum ].vote = vote;
1380 }
1381
1382 /*
1383 ================
1384 idMultiplayerGame::DumpTourneyLine
1385 ================
1386 */
1387 void idMultiplayerGame::DumpTourneyLine( void ) {
1388         int i;
1389         for ( i = 0; i < gameLocal.numClients; i++ ) {
1390                 if ( gameLocal.entities[ i ] && gameLocal.entities[ i ]->IsType( idPlayer::Type ) ) {
1391                         common->Printf( "client %d: rank %d\n", i, static_cast< idPlayer * >( gameLocal.entities[ i ] )->tourneyRank );
1392                 }
1393         }
1394 }
1395
1396 /*
1397 ================
1398 idMultiplayerGame::NewState
1399 ================
1400 */
1401 void idMultiplayerGame::NewState( gameState_t news, idPlayer *player ) {
1402         idBitMsg        outMsg;
1403         byte            msgBuf[MAX_GAME_MESSAGE_SIZE];
1404         int                     i;
1405
1406         assert( news != gameState );
1407         assert( !gameLocal.isClient );
1408         gameLocal.DPrintf( "%s -> %s\n", GameStateStrings[ gameState ], GameStateStrings[ news ] );
1409         switch( news ) {
1410                 case GAMEON: {
1411                         gameLocal.LocalMapRestart();
1412                         outMsg.Init( msgBuf, sizeof( msgBuf ) );
1413                         outMsg.WriteByte( GAME_RELIABLE_MESSAGE_RESTART );
1414                         outMsg.WriteBits( 0, 1 );
1415                         networkSystem->ServerSendReliableMessage( -1, outMsg );
1416
1417 #ifdef CTF
1418                         teamPoints[0] = 0;
1419                         teamPoints[1] = 0;
1420
1421             ClearHUDStatus();
1422 #endif
1423
1424                         PlayGlobalSound( -1, SND_FIGHT );
1425                         matchStartedTime = gameLocal.time;
1426                         fragLimitTimeout = 0;
1427                         for( i = 0; i < gameLocal.numClients; i++ ) {
1428                                 idEntity *ent = gameLocal.entities[ i ];
1429                                 if ( !ent || !ent->IsType( idPlayer::Type ) ) {
1430                                         continue;
1431                                 }
1432                                 idPlayer *p = static_cast<idPlayer *>( ent );
1433                                 p->SetLeader( false ); // don't carry the flag from previous games
1434                                 if ( gameLocal.gameType == GAME_TOURNEY && currentTourneyPlayer[ 0 ] != i && currentTourneyPlayer[ 1 ] != i ) {
1435                                         p->ServerSpectate( true );
1436                                         p->tourneyRank++;
1437                                 } else {
1438                                         int fragLimit = gameLocal.serverInfo.GetInt( "si_fragLimit" );
1439                                         int startingCount = ( gameLocal.gameType == GAME_LASTMAN ) ? fragLimit : 0;
1440                                         playerState[ i ].fragCount = startingCount;
1441                                         playerState[ i ].teamFragCount = startingCount;
1442                                         if ( !static_cast<idPlayer *>(ent)->wantSpectate ) {
1443                                                 static_cast<idPlayer *>(ent)->ServerSpectate( false );
1444                                                 if ( gameLocal.gameType == GAME_TOURNEY ) {
1445                                                         p->tourneyRank = 0;
1446                                                 }
1447                                         }
1448                                 }
1449                                 if ( CanPlay( p ) ) {
1450                                         p->lastManPresent = true;
1451                                 } else {
1452                                         p->lastManPresent = false;
1453                                 }
1454                         }
1455                         cvarSystem->SetCVarString( "ui_ready", "Not Ready" );
1456                         switchThrottle[ 1 ] = 0;        // passby the throttle
1457                         startFragLimit = gameLocal.serverInfo.GetInt( "si_fragLimit" );
1458                         break;
1459                 }
1460                 case GAMEREVIEW: {
1461 #ifdef CTF
1462                         SetFlagMsg( false );
1463 #endif
1464                         nextState = INACTIVE;   // used to abort a game. cancel out any upcoming state change
1465                         // set all players not ready and spectating
1466                         for( i = 0; i < gameLocal.numClients; i++ ) {
1467                                 idEntity *ent = gameLocal.entities[ i ];
1468                                 if ( !ent || !ent->IsType( idPlayer::Type ) ) {
1469                                         continue;
1470                                 }
1471                                 static_cast< idPlayer *>( ent )->forcedReady = false;
1472                                 static_cast<idPlayer *>(ent)->ServerSpectate( true );
1473                         }
1474                         UpdateWinsLosses( player );
1475 #ifdef CTF
1476                         SetFlagMsg( true );
1477 #endif
1478                         break;
1479                 }
1480                 case SUDDENDEATH: {
1481                         PrintMessageEvent( -1, MSG_SUDDENDEATH );
1482                         PlayGlobalSound( -1, SND_SUDDENDEATH );
1483                         break;
1484                 }
1485                 case COUNTDOWN: {
1486                         idBitMsg        outMsg;
1487                         byte            msgBuf[ 128 ];
1488
1489                         warmupEndTime = gameLocal.time + 1000*cvarSystem->GetCVarInteger( "g_countDown" );
1490
1491                         outMsg.Init( msgBuf, sizeof( msgBuf ) );
1492                         outMsg.WriteByte( GAME_RELIABLE_MESSAGE_WARMUPTIME );
1493                         outMsg.WriteLong( warmupEndTime );
1494                         networkSystem->ServerSendReliableMessage( -1, outMsg );
1495
1496                         break;
1497                 }
1498 #ifdef CTF
1499                 case WARMUP: {
1500                         teamPoints[0] = 0;
1501                         teamPoints[1] = 0;
1502
1503                         if ( IsGametypeFlagBased() ) {
1504                                 // reset player scores to zero, only required for CTF
1505                                 for( i = 0; i < gameLocal.numClients; i++ ) {
1506                                         idEntity *ent = gameLocal.entities[ i ];
1507                                         if ( !ent || !ent->IsType( idPlayer::Type ) ) {
1508                                                 continue;
1509                                         }
1510                                         playerState[ i ].fragCount = 0;
1511                                 }
1512                         }
1513                 }
1514 #endif
1515                 default:
1516                         break;
1517         }
1518
1519         gameState = news;
1520 }
1521
1522 /*
1523 ================
1524 idMultiplayerGame::FillTourneySlots
1525 NOTE: called each frame during warmup to keep the tourney slots filled
1526 ================
1527 */
1528 void idMultiplayerGame::FillTourneySlots( ) {
1529         int i, j, rankmax, rankmaxindex;
1530         idEntity *ent;
1531         idPlayer *p;
1532
1533         // fill up the slots based on tourney ranks
1534         for ( i = 0; i < 2; i++ ) {
1535                 if ( currentTourneyPlayer[ i ] != -1 ) {
1536                         continue;
1537                 }
1538                 rankmax = -1;
1539                 rankmaxindex = -1;
1540                 for ( j = 0; j < gameLocal.numClients; j++ ) {
1541                         ent = gameLocal.entities[ j ];
1542                         if ( !ent || !ent->IsType( idPlayer::Type ) ) {
1543                                 continue;
1544                         }
1545                         if ( currentTourneyPlayer[ 0 ] == j || currentTourneyPlayer[ 1 ] == j ) {
1546                                 continue;
1547                         }
1548                         p = static_cast< idPlayer * >( ent );
1549                         if ( p->wantSpectate ) {
1550                                 continue;
1551                         }
1552                         if ( p->tourneyRank >= rankmax ) {
1553                                 // when ranks are equal, use time in game
1554                                 if ( p->tourneyRank == rankmax ) {
1555                                         assert( rankmaxindex >= 0 );
1556                                         if ( p->spawnedTime > static_cast< idPlayer * >( gameLocal.entities[ rankmaxindex ] )->spawnedTime ) {
1557                                                 continue;
1558                                         }
1559                                 }
1560                                 rankmax = static_cast< idPlayer * >( ent )->tourneyRank;
1561                                 rankmaxindex = j;
1562                         }
1563                 }
1564                 currentTourneyPlayer[ i ] = rankmaxindex; // may be -1 if we found nothing
1565         }
1566 }
1567
1568 /*
1569 ================
1570 idMultiplayerGame::UpdateTourneyLine
1571 we manipulate tourneyRank on player entities for internal ranking. it's easier to deal with.
1572 but we need a real wait list to be synced down to clients for GUI
1573 ignore current players, ignore wantSpectate
1574 ================
1575 */
1576 void idMultiplayerGame::UpdateTourneyLine( void ) {
1577         int i, j, imax, max, globalmax = -1;
1578         idPlayer *p;
1579
1580         assert( !gameLocal.isClient );
1581         if ( gameLocal.gameType != GAME_TOURNEY ) {
1582                 return;
1583         }
1584
1585         for ( j = 1; j <= gameLocal.numClients; j++ ) {
1586                 max = -1; imax = -1;
1587                 for ( i = 0; i < gameLocal.numClients; i++ ) {
1588                         if ( currentTourneyPlayer[ 0 ] == i || currentTourneyPlayer[ 1 ] == i ) {
1589                                 continue;
1590                         }
1591                         p = static_cast< idPlayer * >( gameLocal.entities[ i ] );
1592                         if ( !p || p->wantSpectate ) {
1593                                 continue;
1594                         }
1595                         if ( p->tourneyRank > max && ( globalmax == -1 || p->tourneyRank < globalmax ) ) {
1596                                 imax = i;
1597                                 max = p->tourneyRank;
1598                         }
1599                 }
1600                 if ( imax == -1 ) {
1601                         break;
1602                 }
1603
1604                 idBitMsg outMsg;
1605                 byte msgBuf[1024];
1606                 outMsg.Init( msgBuf, sizeof( msgBuf ) );
1607                 outMsg.WriteByte( GAME_RELIABLE_MESSAGE_TOURNEYLINE );
1608                 outMsg.WriteByte( j );
1609                 networkSystem->ServerSendReliableMessage( imax, outMsg );
1610
1611                 globalmax = max;
1612         }
1613 }
1614
1615 /*
1616 ================
1617 idMultiplayerGame::CycleTourneyPlayers
1618 ================
1619 */
1620 void idMultiplayerGame::CycleTourneyPlayers( ) {
1621         int i;
1622         idEntity *ent;
1623         idPlayer *player;
1624
1625         currentTourneyPlayer[ 0 ] = -1;
1626         currentTourneyPlayer[ 1 ] = -1;
1627         // if any, winner from last round will play again
1628         if ( lastWinner != -1 ) {
1629                 idEntity *ent = gameLocal.entities[ lastWinner ];
1630                 if ( ent && ent->IsType( idPlayer::Type ) ) {
1631                         currentTourneyPlayer[ 0 ] = lastWinner;         
1632                 }
1633         }
1634         FillTourneySlots( );
1635         // force selected players in/out of the game and update the ranks
1636         for ( i = 0 ; i < gameLocal.numClients ; i++ ) {
1637                 if ( currentTourneyPlayer[ 0 ] == i || currentTourneyPlayer[ 1 ] == i ) {
1638                         player = static_cast<idPlayer *>( gameLocal.entities[ i ] );
1639                         player->ServerSpectate( false );
1640                 } else {
1641                         ent = gameLocal.entities[ i ];
1642                         if ( ent && ent->IsType( idPlayer::Type ) ) {
1643                                 player = static_cast<idPlayer *>( gameLocal.entities[ i ] );
1644                                 player->ServerSpectate( true );
1645                         }
1646                 }
1647         }
1648         UpdateTourneyLine();
1649 }
1650
1651 /*
1652 ================
1653 idMultiplayerGame::ExecuteVote
1654 the votes are checked for validity/relevance before they are started
1655 we assume that they are still legit when reaching here
1656 ================
1657 */
1658 void idMultiplayerGame::ExecuteVote( void ) {
1659         bool needRestart;
1660         switch ( vote ) {
1661                 case VOTE_RESTART:
1662                         gameLocal.MapRestart();
1663                         break;
1664                 case VOTE_TIMELIMIT:
1665                         si_timeLimit.SetInteger( atoi( voteValue ) );
1666 #ifdef _D3XP
1667                         needRestart = gameLocal.NeedRestart();
1668                         cmdSystem->BufferCommandText( CMD_EXEC_NOW, "rescanSI" );
1669                         if ( needRestart ) {
1670                                 cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "nextMap" );
1671                         }
1672 #endif                  
1673                         break;
1674                 case VOTE_FRAGLIMIT:
1675                         si_fragLimit.SetInteger( atoi( voteValue ) );
1676 #ifdef _D3XP
1677                         needRestart = gameLocal.NeedRestart();
1678                         cmdSystem->BufferCommandText( CMD_EXEC_NOW, "rescanSI" );
1679                         if ( needRestart ) {
1680                                 cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "nextMap" );
1681                         }
1682 #endif
1683                         break;
1684                 case VOTE_GAMETYPE:
1685                         si_gameType.SetString( voteValue );
1686                         gameLocal.MapRestart();
1687                         break;
1688                 case VOTE_KICK:
1689                         cmdSystem->BufferCommandText( CMD_EXEC_NOW, va( "kick %s", voteValue.c_str() ) );
1690                         break;
1691                 case VOTE_MAP:
1692                         si_map.SetString( voteValue );
1693                         gameLocal.MapRestart();
1694                         break;
1695                 case VOTE_SPECTATORS:
1696                         si_spectators.SetBool( !si_spectators.GetBool() );
1697 #ifdef _D3XP
1698                         needRestart = gameLocal.NeedRestart();
1699                         cmdSystem->BufferCommandText( CMD_EXEC_NOW, "rescanSI" );
1700                         if ( needRestart ) {
1701                                 cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "nextMap" );
1702                         }
1703 #endif
1704                         break;
1705                 case VOTE_NEXTMAP:
1706                         cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "serverNextMap\n" );
1707                         break;
1708         }
1709 }
1710
1711 /*
1712 ================
1713 idMultiplayerGame::CheckVote
1714 ================
1715 */
1716 void idMultiplayerGame::CheckVote( void ) {
1717         int numVoters, i;
1718
1719         if ( vote == VOTE_NONE ) {
1720                 return;
1721         }
1722
1723         if ( voteExecTime ) {
1724                 if ( gameLocal.time > voteExecTime ) {
1725                         voteExecTime = 0;
1726                         ClientUpdateVote( VOTE_RESET, 0, 0 );
1727                         ExecuteVote();
1728                         vote = VOTE_NONE;
1729                 }
1730                 return;
1731         }
1732
1733         // count voting players
1734         numVoters = 0;
1735         for ( i = 0; i < gameLocal.numClients; i++ ) {
1736                 idEntity *ent = gameLocal.entities[ i ];
1737                 if ( !ent || !ent->IsType( idPlayer::Type ) ) {
1738                         continue;
1739                 }
1740                 if ( playerState[ i ].vote != PLAYER_VOTE_NONE ) {
1741                         numVoters++;
1742                 }
1743         }
1744         if ( !numVoters ) {
1745                 // abort
1746                 vote = VOTE_NONE;
1747                 ClientUpdateVote( VOTE_ABORTED, yesVotes, noVotes );
1748                 return;
1749         }
1750         if ( yesVotes / numVoters > 0.5f ) {
1751                 ClientUpdateVote( VOTE_PASSED, yesVotes, noVotes );
1752                 voteExecTime = gameLocal.time + 2000;
1753                 return;
1754         }
1755         if ( gameLocal.time > voteTimeOut || noVotes / numVoters >= 0.5f ) {
1756                 ClientUpdateVote( VOTE_FAILED, yesVotes, noVotes );
1757                 vote = VOTE_NONE;
1758                 return;
1759         }
1760 }
1761
1762 /*
1763 ================
1764 idMultiplayerGame::Warmup
1765 ================
1766 */
1767 bool idMultiplayerGame::Warmup() {
1768         return ( gameState == WARMUP );
1769 }
1770
1771 /*
1772 ================
1773 idMultiplayerGame::Run
1774 ================
1775 */
1776 void idMultiplayerGame::Run() {
1777         int i, timeLeft;
1778         idPlayer *player;
1779         int gameReviewPause;
1780
1781         assert( gameLocal.isMultiplayer );
1782         assert( !gameLocal.isClient );
1783
1784         pureReady = true;
1785
1786         if ( gameState == INACTIVE ) {
1787                 lastGameType = gameLocal.gameType;
1788                 NewState( WARMUP );
1789         }
1790
1791         CheckVote();
1792
1793         CheckRespawns();
1794
1795         if ( nextState != INACTIVE && gameLocal.time > nextStateSwitch ) {
1796                 NewState( nextState );
1797                 nextState = INACTIVE;
1798         }
1799
1800         // don't update the ping every frame to save bandwidth
1801         if ( gameLocal.time > pingUpdateTime ) {
1802                 for ( i = 0; i < gameLocal.numClients; i++ ) {
1803                         playerState[i].ping = networkSystem->ServerGetClientPing( i );
1804                 }
1805                 pingUpdateTime = gameLocal.time + 1000;
1806         }
1807
1808         warmupText = "";
1809
1810         switch( gameState ) {
1811                 case GAMEREVIEW: {
1812                         if ( nextState == INACTIVE ) {
1813                                 gameReviewPause = cvarSystem->GetCVarInteger( "g_gameReviewPause" );
1814                                 nextState = NEXTGAME;
1815                                 nextStateSwitch = gameLocal.time + 1000 * gameReviewPause;
1816                         }
1817                         break;
1818                 }
1819                 case NEXTGAME: {
1820                         if ( nextState == INACTIVE ) {
1821                                 // game rotation, new map, gametype etc.
1822                                 if ( gameLocal.NextMap() ) {
1823                                         cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "serverMapRestart\n" );
1824                                         return;
1825                                 }
1826 #ifdef CTF
1827                                 // make sure flags are returned
1828                                 if ( IsGametypeFlagBased() ) {
1829                                         idItemTeam * flag;
1830                                         flag = GetTeamFlag( 0 );
1831                                         if ( flag ) {
1832                                                 flag->Return();
1833                                         }
1834                                         flag = GetTeamFlag( 1 );
1835                                         if ( flag ) {
1836                                                 flag->Return();
1837                                         }
1838                                 }
1839 #endif
1840                                 NewState( WARMUP );
1841                                 if ( gameLocal.gameType == GAME_TOURNEY ) {
1842                                         CycleTourneyPlayers();
1843                                 }
1844                                 // put everyone back in from endgame spectate
1845                                 for ( i = 0; i < gameLocal.numClients; i++ ) {
1846                                         idEntity *ent = gameLocal.entities[ i ];
1847                                         if ( ent && ent->IsType( idPlayer::Type ) ) {
1848                                                 if ( !static_cast< idPlayer * >( ent )->wantSpectate ) {
1849                                                         CheckRespawns( static_cast<idPlayer *>( ent ) );
1850                                                 }
1851                                         }
1852                                 }
1853                         }
1854                         break;
1855                 }
1856                 case WARMUP: {
1857                         if ( AllPlayersReady() ) {
1858                                 NewState( COUNTDOWN );
1859                                 nextState = GAMEON;
1860                                 nextStateSwitch = gameLocal.time + 1000 * cvarSystem->GetCVarInteger( "g_countDown" );
1861                         }
1862                         warmupText = "Warming up.. waiting for players to get ready";
1863                         one = two = three = false;
1864                         break;
1865                 }
1866                 case COUNTDOWN: {
1867                         timeLeft = ( nextStateSwitch - gameLocal.time ) / 1000 + 1;
1868                         if ( timeLeft == 3 && !three ) {
1869                                 PlayGlobalSound( -1, SND_THREE );
1870                                 three = true;
1871                         } else if ( timeLeft == 2 && !two ) {
1872                                 PlayGlobalSound( -1, SND_TWO );
1873                                 two = true;
1874                         } else if ( timeLeft == 1 && !one ) {
1875                                 PlayGlobalSound( -1, SND_ONE );
1876                                 one = true;
1877                         }
1878                         warmupText = va( "Match starts in %i", timeLeft );
1879                         break;
1880                 }
1881                 case GAMEON: {
1882 #ifdef CTF
1883                         if ( IsGametypeFlagBased() ) { /* CTF */
1884                                 // totally different logic branch for CTF
1885                                 if ( PointLimitHit() ) {
1886                                         int team = WinningTeam();
1887                                         assert( team != -1 );
1888
1889                                         NewState( GAMEREVIEW, NULL );
1890                                         PrintMessageEvent( -1, MSG_POINTLIMIT, team );
1891                                 } else if ( TimeLimitHit() ) {
1892                                         int team = WinningTeam();
1893                                         if ( EnoughClientsToPlay() && team == -1 ) {
1894                                                 NewState( SUDDENDEATH );
1895                                         } else {
1896                                                 NewState( GAMEREVIEW, NULL  );
1897                                                 PrintMessageEvent( -1, MSG_TIMELIMIT );
1898                                         }
1899                                 }
1900                                 break;
1901                         }
1902 #endif
1903             
1904                         player = FragLimitHit();
1905                         if ( player ) {
1906                                 // delay between detecting frag limit and ending game. let the death anims play
1907                                 if ( !fragLimitTimeout ) {
1908                                         common->DPrintf( "enter FragLimit timeout, player %d is leader\n", player->entityNumber );
1909                                         fragLimitTimeout = gameLocal.time + FRAGLIMIT_DELAY;
1910                                 }
1911                                 if ( gameLocal.time > fragLimitTimeout ) {
1912                                         NewState( GAMEREVIEW, player );
1913                                         PrintMessageEvent( -1, MSG_FRAGLIMIT, player->entityNumber );
1914                                 }
1915                         } else {
1916                                 if ( fragLimitTimeout ) {
1917                                         // frag limit was hit and cancelled. means the two teams got even during FRAGLIMIT_DELAY
1918                                         // enter sudden death, the next frag leader will win
1919                                         SuddenRespawn();
1920                                         PrintMessageEvent( -1, MSG_HOLYSHIT );
1921                                         fragLimitTimeout = 0;
1922                                         NewState( SUDDENDEATH );
1923                                 } else if ( TimeLimitHit() ) {
1924                                         player = FragLeader();
1925                                         if ( !player ) {
1926                                                 NewState( SUDDENDEATH );
1927                                         } else {
1928                                                 NewState( GAMEREVIEW, player );
1929                                                 PrintMessageEvent( -1, MSG_TIMELIMIT );
1930                                         }
1931                                 }
1932                         }
1933                         break;
1934                 }
1935                 case SUDDENDEATH: {
1936 #ifdef CTF
1937                         if ( IsGametypeFlagBased() ) { /* CTF */
1938                                 int team = WinningTeam();
1939                                 if ( team != -1 ) {
1940                                         // TODO : implement pointLimitTimeout
1941                                         NewState( GAMEREVIEW, NULL );
1942                                         PrintMessageEvent( -1, MSG_POINTLIMIT, team );
1943                                 }
1944                                 break;
1945                         }
1946 #endif
1947             
1948                         player = FragLeader();
1949                         if ( player ) {
1950                                 if ( !fragLimitTimeout ) {
1951                                         common->DPrintf( "enter sudden death FragLeader timeout, player %d is leader\n", player->entityNumber );
1952                                         fragLimitTimeout = gameLocal.time + FRAGLIMIT_DELAY;
1953                                 }
1954                                 if ( gameLocal.time > fragLimitTimeout ) {
1955                                         NewState( GAMEREVIEW, player );
1956                                         PrintMessageEvent( -1, MSG_FRAGLIMIT, player->entityNumber );
1957                                 }
1958                         } else if ( fragLimitTimeout ) {
1959                                 SuddenRespawn();
1960                                 PrintMessageEvent( -1, MSG_HOLYSHIT );
1961                                 fragLimitTimeout = 0;
1962                         }
1963                         break;
1964                 }
1965         }
1966 }
1967
1968 /*
1969 ================
1970 idMultiplayerGame::UpdateMainGui
1971 ================
1972 */
1973 void idMultiplayerGame::UpdateMainGui( void ) {
1974         int i;
1975         mainGui->SetStateInt( "readyon", gameState == WARMUP ? 1 : 0 );
1976         mainGui->SetStateInt( "readyoff", gameState != WARMUP ? 1 : 0 );
1977         idStr strReady = cvarSystem->GetCVarString( "ui_ready" );
1978         if ( strReady.Icmp( "ready") == 0 ){
1979                 strReady = common->GetLanguageDict()->GetString( "#str_04248" );
1980         } else {
1981                 strReady = common->GetLanguageDict()->GetString( "#str_04247" );
1982         }
1983         mainGui->SetStateString( "ui_ready", strReady );
1984         mainGui->SetStateInt( "teamon", IsGametypeTeamBased() ? 1 : 0 ); /* CTF */
1985         mainGui->SetStateInt( "teamoff", (!IsGametypeTeamBased()) ? 1 : 0 ); /* CTF */
1986         if ( IsGametypeTeamBased() ) {
1987                 idPlayer *p = gameLocal.GetClientByNum( gameLocal.localClientNum );
1988                 if ( p ) {
1989                         mainGui->SetStateInt( "team", p->team );
1990                 }
1991                 else {
1992                         mainGui->SetStateInt( "team", 0 );
1993                 }
1994         }
1995         // setup vote
1996         mainGui->SetStateInt( "voteon", ( vote != VOTE_NONE && !voted ) ? 1 : 0 );
1997         mainGui->SetStateInt( "voteoff", ( vote != VOTE_NONE && !voted ) ? 0 : 1 );
1998         // last man hack
1999         mainGui->SetStateInt( "isLastMan", gameLocal.gameType == GAME_LASTMAN ? 1 : 0 );
2000         // send the current serverinfo values
2001         for ( i = 0; i < gameLocal.serverInfo.GetNumKeyVals(); i++ ) {
2002                 const idKeyValue *keyval = gameLocal.serverInfo.GetKeyVal( i );
2003                 mainGui->SetStateString( keyval->GetKey(), keyval->GetValue() );
2004         }
2005         mainGui->StateChanged( gameLocal.time );
2006 #if defined( __linux__ )
2007         // replacing the oh-so-useful s_reverse with sound backend prompt
2008         mainGui->SetStateString( "driver_prompt", "1" );
2009 #else
2010         mainGui->SetStateString( "driver_prompt", "0" );
2011 #endif
2012 }
2013
2014 /*
2015 ================
2016 idMultiplayerGame::StartMenu
2017 ================
2018 */
2019 idUserInterface* idMultiplayerGame::StartMenu( void ) {
2020
2021         if ( mainGui == NULL ) {
2022                 return NULL;
2023         }
2024
2025         int i, j;
2026         if ( currentMenu ) {
2027                 currentMenu = 0;
2028                 cvarSystem->SetCVarBool( "ui_chat", false );
2029         } else {
2030                 if ( nextMenu >= 2 ) {
2031                         currentMenu = nextMenu;
2032                 } else {
2033                         // for default and explicit
2034                         currentMenu = 1;
2035                 }
2036                 cvarSystem->SetCVarBool( "ui_chat", true );
2037         }
2038         nextMenu = 0;
2039         gameLocal.sessionCommand = "";  // in case we used "game_startMenu" to trigger the menu
2040         if ( currentMenu == 1 ) {
2041                 UpdateMainGui();
2042
2043                 // UpdateMainGui sets most things, but it doesn't set these because
2044                 // it'd be pointless and/or harmful to set them every frame (for various reasons)
2045                 // Currenty the gui doesn't update properly if they change anyway, so we'll leave it like this.
2046
2047                 // setup callvote
2048                 if ( vote == VOTE_NONE ) {
2049                         bool callvote_ok = false;
2050                         for ( i = 0; i < VOTE_COUNT; i++ ) {
2051                                 // flag on means vote is denied, so default value 0 means all votes and -1 disables
2052                                 mainGui->SetStateInt( va( "vote%d", i ), g_voteFlags.GetInteger() & ( 1 << i ) ? 0 : 1 );
2053                                 if ( !( g_voteFlags.GetInteger() & ( 1 << i ) ) ) {
2054                                         callvote_ok = true;
2055                                 }
2056                         }
2057                         mainGui->SetStateInt( "callvote", callvote_ok );
2058                 } else {
2059                         mainGui->SetStateInt( "callvote", 2 );
2060                 }
2061
2062                 // player kick data
2063                 idStr kickList;
2064                 j = 0;
2065                 for ( i = 0; i < gameLocal.numClients; i++ ) {
2066                         if ( gameLocal.entities[ i ] && gameLocal.entities[ i ]->IsType( idPlayer::Type ) ) {
2067                                 if ( kickList.Length() ) {
2068                                         kickList += ";";
2069                                 }
2070                                 kickList += va( "\"%d - %s\"", i, gameLocal.userInfo[ i ].GetString( "ui_name" ) );
2071                                 kickVoteMap[ j ] = i;
2072                                 j++;
2073                         }
2074                 }
2075                 mainGui->SetStateString( "kickChoices", kickList );
2076
2077 #ifdef CTF
2078                 const char *gametype = gameLocal.serverInfo.GetString( "si_gameType" );
2079                 const char *map = gameLocal.serverInfo.GetString( "si_map" );                   // what if server changes this strings while user in UI?
2080                 int num = declManager->GetNumDecls( DECL_MAPDEF );
2081
2082                 for ( i = 0; i < num; i++ ) {
2083                         const idDeclEntityDef *mapDef = static_cast<const idDeclEntityDef *>( declManager->DeclByIndex( DECL_MAPDEF, i ) );
2084
2085                         if ( mapDef && idStr::Icmp( mapDef->GetName(), map ) == 0 && mapDef->dict.GetBool( gametype ) ) {
2086                                 int k = 0;
2087
2088                                 idStr gametypeList;
2089
2090                                 for ( j = 0; si_gameTypeArgs[ j ]; j++ ) {
2091                                         if ( mapDef->dict.GetBool( si_gameTypeArgs[ j ] ) ) {
2092                                                 if ( gametypeList.Length() ) {
2093                                                         gametypeList += ";";
2094                                                 }
2095                                                 gametypeList += va( "%s", si_gameTypeArgs[ j ] );
2096                                                 gameTypeVoteMap[ k ] = si_gameTypeArgs[ j ];
2097                                                 k++;
2098                                         }
2099                                 }
2100
2101                                 mainGui->SetStateString( "gametypeChoices", gametypeList );
2102
2103                                 break;
2104                         }
2105                 }
2106 #endif
2107
2108                 mainGui->SetStateString( "chattext", "" );
2109                 mainGui->Activate( true, gameLocal.time );
2110                 return mainGui;
2111         } else if ( currentMenu == 2 ) {
2112                 // the setup is done in MessageMode
2113                 msgmodeGui->Activate( true, gameLocal.time );
2114                 cvarSystem->SetCVarBool( "ui_chat", true );
2115                 return msgmodeGui;
2116         }
2117         return NULL;
2118 }
2119
2120 /*
2121 ================
2122 idMultiplayerGame::DisableMenu
2123 ================
2124 */
2125 void idMultiplayerGame::DisableMenu( void ) {
2126         gameLocal.sessionCommand = "";  // in case we used "game_startMenu" to trigger the menu
2127         if ( currentMenu == 1 ) {
2128                 mainGui->Activate( false, gameLocal.time );
2129         } else if ( currentMenu == 2 ) {
2130                 msgmodeGui->Activate( false, gameLocal.time );
2131         }
2132         currentMenu = 0;
2133         nextMenu = 0;
2134         cvarSystem->SetCVarBool( "ui_chat", false );
2135 }
2136
2137 /*
2138 ================
2139 idMultiplayerGame::SetMapShot
2140 ================
2141 */
2142 void idMultiplayerGame::SetMapShot( void ) {
2143         char screenshot[ MAX_STRING_CHARS ];
2144         int mapNum = mapList->GetSelection( NULL, 0 );
2145         const idDict *dict = NULL;
2146         if ( mapNum >= 0 ) {
2147                 dict = fileSystem->GetMapDecl( mapNum );
2148         }
2149         fileSystem->FindMapScreenshot( dict ? dict->GetString( "path" ) : "", screenshot, MAX_STRING_CHARS );
2150         mainGui->SetStateString( "current_levelshot", screenshot );
2151 }
2152
2153 /*
2154 ================
2155 idMultiplayerGame::HandleGuiCommands
2156 ================
2157 */
2158 const char* idMultiplayerGame::HandleGuiCommands( const char *_menuCommand ) {
2159         idUserInterface *currentGui;
2160         const char              *voteValue;
2161         int                             vote_clientNum;
2162         int                             icmd;
2163         idCmdArgs               args;
2164
2165         if ( !_menuCommand[ 0 ] ) {
2166                 common->Printf( "idMultiplayerGame::HandleGuiCommands: empty command\n" );
2167                 return "continue";
2168         }
2169         assert( currentMenu );
2170         if ( currentMenu == 1 ) {
2171                 currentGui = mainGui;
2172         } else {
2173                 currentGui = msgmodeGui;
2174         }
2175
2176         args.TokenizeString( _menuCommand, false );
2177
2178         for( icmd = 0; icmd < args.Argc(); ) {
2179                 const char *cmd = args.Argv( icmd++ );
2180
2181                 if ( !idStr::Icmp( cmd, ";"     ) )     {
2182                         continue;
2183                 } else if (     !idStr::Icmp( cmd, "video" ) ) {
2184                         idStr vcmd;
2185                         if ( args.Argc() - icmd >= 1 ) {
2186                                 vcmd = args.Argv( icmd++ );
2187                         }
2188
2189                         int     oldSpec = cvarSystem->GetCVarInteger( "com_machineSpec" );
2190
2191                         if ( idStr::Icmp( vcmd, "low" ) == 0 ) {
2192                                 cvarSystem->SetCVarInteger(     "com_machineSpec", 0 );
2193                         } else if (     idStr::Icmp( vcmd, "medium"     ) == 0 ) {
2194                                 cvarSystem->SetCVarInteger(     "com_machineSpec", 1 );
2195                         } else  if ( idStr::Icmp( vcmd, "high" ) ==     0 )     {
2196                                 cvarSystem->SetCVarInteger(     "com_machineSpec", 2 );
2197                         } else  if ( idStr::Icmp( vcmd, "ultra" ) == 0 ) {
2198                                 cvarSystem->SetCVarInteger(     "com_machineSpec", 3 );
2199                         } else if (     idStr::Icmp( vcmd, "recommended" ) == 0 ) {
2200                                 cmdSystem->BufferCommandText( CMD_EXEC_NOW,     "setMachineSpec\n" );
2201                         }
2202
2203                         if ( oldSpec != cvarSystem->GetCVarInteger(     "com_machineSpec" )     ) {
2204                                 currentGui->SetStateInt( "com_machineSpec",     cvarSystem->GetCVarInteger(     "com_machineSpec" )     );
2205                                 currentGui->StateChanged( gameLocal.realClientTime );
2206                                 cmdSystem->BufferCommandText( CMD_EXEC_NOW,     "execMachineSpec\n"     );
2207                         }
2208
2209                         if ( idStr::Icmp( vcmd, "restart" )      ==     0) {
2210                                 cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "vid_restart\n" );
2211                         }
2212
2213                         continue;
2214                 } else if (     !idStr::Icmp( cmd, "play" )     ) {
2215                         if ( args.Argc() - icmd >= 1 ) {
2216                                 idStr snd =     args.Argv( icmd++ );
2217                                 int     channel = 1;
2218                                 if ( snd.Length() == 1 ) {
2219                                         channel = atoi( snd     );
2220                                         snd     = args.Argv( icmd++     );
2221                                 }
2222                                 gameSoundWorld->PlayShaderDirectly(     snd, channel );
2223                         }
2224                         continue;
2225                 } else if (     !idStr::Icmp( cmd, "mpSkin"     ) )     {
2226                         idStr skin;
2227                         if ( args.Argc() - icmd >= 1 ) {
2228                                 skin = args.Argv( icmd++ );
2229                                 cvarSystem->SetCVarString( "ui_skin", skin );
2230                         }
2231                         SetMenuSkin();
2232                         continue;
2233                 } else if (     !idStr::Icmp( cmd, "quit" )     ) {
2234                         cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "quit\n" );
2235                         return NULL;
2236                 } else if (     !idStr::Icmp( cmd, "disconnect" ) )     {
2237                         cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "disconnect\n" );
2238                         return NULL;
2239                 } else if (     !idStr::Icmp( cmd, "close" ) ) {
2240                         DisableMenu( );
2241                         return NULL;
2242                 } else if (     !idStr::Icmp( cmd, "spectate" ) ) {
2243                         ToggleSpectate();
2244                         DisableMenu( );
2245                         return NULL;
2246                 } else if (     !idStr::Icmp( cmd, "chatmessage" ) ) {
2247                         int     mode = currentGui->State().GetInt( "messagemode" );
2248                         if ( mode )     {
2249                                 cmdSystem->BufferCommandText( CMD_EXEC_NOW,     va(     "sayTeam \"%s\"", currentGui->State().GetString( "chattext"     ) )     );
2250                         } else {
2251                                 cmdSystem->BufferCommandText( CMD_EXEC_NOW,     va(     "say \"%s\"", currentGui->State().GetString( "chattext" ) )     );
2252                         }
2253                         currentGui->SetStateString(     "chattext",     "" );
2254                         if ( currentMenu ==     1 )     {
2255                                 return "continue";
2256                         } else {
2257                                 DisableMenu();
2258                                 return NULL;
2259                         }
2260                 } else if (     !idStr::Icmp( cmd, "readytoggle" ) ) {
2261                         ToggleReady( );
2262                         DisableMenu( );
2263                         return NULL;
2264                 } else if (     !idStr::Icmp( cmd, "teamtoggle" ) )     {
2265                         ToggleTeam(     );
2266                         DisableMenu( );
2267                         return NULL;
2268                 } else if (     !idStr::Icmp( cmd, "callVote" ) ) {
2269                         vote_flags_t voteIndex = (vote_flags_t)mainGui->State().GetInt( "voteIndex"     );
2270                         if ( voteIndex == VOTE_MAP ) {
2271                                 int mapNum = mapList->GetSelection( NULL, 0 );
2272                                 if ( mapNum >= 0 ) {
2273                                         const idDict *dict = fileSystem->GetMapDecl( mapNum );
2274                                         if ( dict ) {
2275                                                 ClientCallVote( VOTE_MAP, dict->GetString( "path" ) );
2276                                         }
2277                                 }
2278                         } else {
2279                                 voteValue =     mainGui->State().GetString(     "str_voteValue" );
2280                                 if ( voteIndex == VOTE_KICK     ) {
2281                                         vote_clientNum = kickVoteMap[ atoi(     voteValue )     ];
2282                                         ClientCallVote( voteIndex, va( "%d", vote_clientNum     ) );
2283 #ifdef CTF
2284                                 } else if ( voteIndex == VOTE_GAMETYPE ) {
2285                                         // send the actual gametype index, not an index in the choice list
2286                                         int i;
2287                                         for ( i = 0; si_gameTypeArgs[i]; i++ ) {
2288                                                 if ( !idStr::Icmp( gameTypeVoteMap[ atoi( voteValue ) ], si_gameTypeArgs[i] ) ) {
2289                                                         ClientCallVote( voteIndex, va( "%d", i ) );
2290                                                         break;
2291                                                 }
2292                                         }
2293 #endif
2294                                 } else {
2295                                         ClientCallVote( voteIndex, voteValue );
2296                                 }
2297                         }
2298                         DisableMenu();
2299                         return NULL;
2300                 } else if (     !idStr::Icmp( cmd, "voteyes" ) ) {
2301                         CastVote( gameLocal.localClientNum,     true );
2302                         DisableMenu();
2303                         return NULL;
2304                 } else if (     !idStr::Icmp( cmd, "voteno"     ) )     {
2305                         CastVote( gameLocal.localClientNum,     false );
2306                         DisableMenu();
2307                         return NULL;
2308                 } else if ( !idStr::Icmp( cmd, "bind" ) ) {
2309                         if ( args.Argc() - icmd >= 2 ) {
2310                                 idStr key = args.Argv( icmd++ );
2311                                 idStr bind = args.Argv( icmd++ );
2312                                 cmdSystem->BufferCommandText( CMD_EXEC_NOW, va( "bindunbindtwo \"%s\" \"%s\"", key.c_str(), bind.c_str() ) );
2313                                 mainGui->SetKeyBindingNames();
2314                         }
2315                         continue;
2316                 } else if ( !idStr::Icmp( cmd, "clearbind" ) ) {
2317                         if ( args.Argc() - icmd >= 1 ) {
2318                                 idStr bind = args.Argv( icmd++ );
2319                                 cmdSystem->BufferCommandText( CMD_EXEC_NOW, va( "unbind \"%s\"", bind.c_str() ) );
2320                                 mainGui->SetKeyBindingNames();
2321                         }
2322                         continue;
2323                 } else if (     !idStr::Icmp( cmd, "MAPScan" ) ) {
2324                         const char *gametype = gameLocal.serverInfo.GetString( "si_gameType" );
2325                         if ( gametype == NULL || *gametype == 0 || idStr::Icmp( gametype, "singleplayer" ) == 0 ) {
2326                                 gametype = "Deathmatch";
2327                         }
2328
2329                         int i, num;
2330                         idStr si_map = gameLocal.serverInfo.GetString("si_map");
2331                         const idDict *dict;
2332
2333                         mapList->Clear();
2334                         mapList->SetSelection( -1 );
2335                         num = fileSystem->GetNumMaps();
2336                         for ( i = 0; i < num; i++ ) {
2337                                 dict = fileSystem->GetMapDecl( i );
2338                                 if ( dict ) {
2339                                         // any MP gametype supported
2340                                         bool isMP = false;
2341                                         int igt = GAME_SP + 1;
2342                                         while ( si_gameTypeArgs[ igt ] ) {
2343                                                 if ( dict->GetBool( si_gameTypeArgs[ igt ] ) ) {
2344                                                         isMP = true;
2345                                                         break;
2346                                                 }
2347                                                 igt++;
2348                                         }
2349                                         if ( isMP ) {
2350                                                 const char *mapName = dict->GetString( "name" );
2351                                                 if ( mapName[0] == '\0' ) {
2352                                                         mapName = dict->GetString( "path" );
2353                                                 }
2354                                                 mapName = common->GetLanguageDict()->GetString( mapName );
2355                                                 mapList->Add( i, mapName );
2356                                                 if ( !si_map.Icmp( dict->GetString( "path" ) ) ) {
2357                                                         mapList->SetSelection( mapList->Num() - 1 );
2358                                                 }
2359                                         }
2360                                 }
2361                         }
2362                         // set the current level shot
2363                         SetMapShot(     );
2364                         return "continue";
2365                 } else if (     !idStr::Icmp( cmd, "click_maplist" ) ) {
2366                         SetMapShot(     );
2367                         return "continue";
2368                 } else if ( strstr( cmd, "sound" ) == cmd ) {
2369                         // pass that back to the core, will know what to do with it
2370                         return _menuCommand;
2371                 }
2372                 common->Printf( "idMultiplayerGame::HandleGuiCommands: '%s'     unknown\n",     cmd     );
2373
2374         }
2375         return "continue";
2376 }
2377
2378 /*
2379 ================
2380 idMultiplayerGame::Draw
2381 ================
2382 */
2383 bool idMultiplayerGame::Draw( int clientNum ) {
2384         idPlayer *player, *viewPlayer;
2385
2386         // clear the render entities for any players that don't need
2387         // icons and which might not be thinking because they weren't in
2388         // the last snapshot.
2389         for ( int i = 0; i < gameLocal.numClients; i++ ) {
2390                 player = static_cast<idPlayer *>( gameLocal.entities[ i ] );
2391                 if ( player && !player->NeedsIcon() ) {
2392                         player->HidePlayerIcons();
2393                 }
2394         }
2395
2396         player = viewPlayer = static_cast<idPlayer *>( gameLocal.entities[ clientNum ] );
2397
2398         if ( player == NULL ) {
2399                 return false;
2400         }
2401
2402         if ( player->spectating ) {
2403                 viewPlayer = static_cast<idPlayer *>( gameLocal.entities[ player->spectator ] );
2404                 if ( viewPlayer == NULL ) {
2405                         return false;
2406                 }
2407         }
2408
2409         UpdatePlayerRanks();
2410         UpdateHud( viewPlayer, player->hud );
2411         // use the hud of the local player
2412         viewPlayer->playerView.RenderPlayerView( player->hud );
2413
2414         if ( currentMenu ) {
2415 #if 0
2416                 // uncomment this if you want to track when players are in a menu
2417                 if ( !bCurrentMenuMsg ) {
2418                         idBitMsg        outMsg;
2419                         byte            msgBuf[ 128 ];
2420
2421                         outMsg.Init( msgBuf, sizeof( msgBuf ) );
2422                         outMsg.WriteByte( GAME_RELIABLE_MESSAGE_MENU );
2423                         outMsg.WriteBits( 1, 1 );
2424                         networkSystem->ClientSendReliableMessage( outMsg );
2425
2426                         bCurrentMenuMsg = true;
2427                 }
2428 #endif
2429                 if ( player->wantSpectate ) {
2430                         mainGui->SetStateString( "spectext", common->GetLanguageDict()->GetString( "#str_04249" ) );
2431                 } else {
2432                         mainGui->SetStateString( "spectext", common->GetLanguageDict()->GetString( "#str_04250" ) );
2433                 }
2434                 DrawChat();
2435                 if ( currentMenu == 1 ) {
2436                         UpdateMainGui();
2437                         mainGui->Redraw( gameLocal.time );
2438                 } else {
2439                         msgmodeGui->Redraw( gameLocal.time );
2440                 }
2441         } else {
2442 #if 0
2443                 // uncomment this if you want to track when players are in a menu
2444                 if ( bCurrentMenuMsg ) {
2445                         idBitMsg        outMsg;
2446                         byte            msgBuf[ 128 ];
2447
2448                         outMsg.Init( msgBuf, sizeof( msgBuf ) );
2449                         outMsg.WriteByte( GAME_RELIABLE_MESSAGE_MENU );
2450                         outMsg.WriteBits( 0, 1 );
2451                         networkSystem->ClientSendReliableMessage( outMsg );
2452
2453                         bCurrentMenuMsg = false;
2454                 }
2455 #endif
2456                 if ( player->spectating ) {
2457                         idStr spectatetext[ 2 ];
2458                         int ispecline = 0;
2459                         if ( gameLocal.gameType == GAME_TOURNEY ) {
2460                                 if ( !player->wantSpectate ) {
2461                                         spectatetext[ 0 ] = common->GetLanguageDict()->GetString( "#str_04246" );
2462                                         switch ( player->tourneyLine ) {
2463                                                 case 0:
2464                                                         spectatetext[ 0 ] += common->GetLanguageDict()->GetString( "#str_07003" );
2465                                                         break;
2466                                                 case 1:
2467                                                         spectatetext[ 0 ] += common->GetLanguageDict()->GetString( "#str_07004" );
2468                                                         break;
2469                                                 case 2:
2470                                                         spectatetext[ 0 ] += common->GetLanguageDict()->GetString( "#str_07005" );
2471                                                         break;
2472                                                 default:
2473                                                         spectatetext[ 0 ] += va( common->GetLanguageDict()->GetString( "#str_07006" ), player->tourneyLine );
2474                                                         break;
2475                                         }
2476                                         ispecline++;
2477                                 }
2478                         } else if ( gameLocal.gameType == GAME_LASTMAN ) {
2479                                 if ( !player->wantSpectate ) {
2480                                         spectatetext[ 0 ] = common->GetLanguageDict()->GetString( "#str_07007" );
2481                                         ispecline++;
2482                                 }
2483                         }
2484                         if ( player->spectator != player->entityNumber ) {
2485                                 spectatetext[ ispecline ] = va( common->GetLanguageDict()->GetString( "#str_07008" ), viewPlayer->GetUserInfo()->GetString( "ui_name" ) );
2486                         } else if ( !ispecline ) {
2487                                 spectatetext[ 0 ] = common->GetLanguageDict()->GetString( "#str_04246" );
2488                         }
2489                         spectateGui->SetStateString( "spectatetext0", spectatetext[0].c_str() );
2490                         spectateGui->SetStateString( "spectatetext1", spectatetext[1].c_str() );
2491                         if ( vote != VOTE_NONE ) {
2492                                 spectateGui->SetStateString( "vote", va( "%s (y: %d n: %d)", voteString.c_str(), (int)yesVotes, (int)noVotes ) );
2493                         } else {
2494                                 spectateGui->SetStateString( "vote", "" );
2495                         }
2496                         spectateGui->Redraw( gameLocal.time );
2497                 }
2498                 DrawChat();
2499                 DrawScoreBoard( player );
2500         }
2501
2502         return true;
2503 }
2504
2505 /*
2506 ================
2507 idMultiplayerGame::UpdateHud
2508 ================
2509 */
2510 void idMultiplayerGame::UpdateHud( idPlayer *player, idUserInterface *hud ) {
2511         int i;
2512
2513         if ( !hud ) {
2514                 return;
2515         }
2516
2517         hud->SetStateBool( "warmup", Warmup() );
2518
2519         if ( gameState == WARMUP ) {
2520                 if ( player->IsReady() ) {
2521                         hud->SetStateString( "warmuptext", common->GetLanguageDict()->GetString( "#str_04251" ) );
2522                 } else {
2523                         hud->SetStateString( "warmuptext", common->GetLanguageDict()->GetString( "#str_07002" ) );
2524                 }
2525         }
2526
2527         hud->SetStateString( "timer", ( Warmup() ) ? common->GetLanguageDict()->GetString( "#str_04251" ) : ( gameState == SUDDENDEATH ) ? common->GetLanguageDict()->GetString( "#str_04252" ) : GameTime() );
2528         if ( vote != VOTE_NONE ) {
2529                 hud->SetStateString( "vote", va( "%s (y: %d n: %d)", voteString.c_str(), (int)yesVotes, (int)noVotes ) );
2530         } else {
2531                 hud->SetStateString( "vote", "" );
2532         }
2533
2534         hud->SetStateInt( "rank_self", 0 );
2535         if ( gameState == GAMEON ) {
2536                 for ( i = 0; i < numRankedPlayers; i++ ) {
2537                         if (  IsGametypeTeamBased() ) { /* CTF */
2538                                 hud->SetStateInt( va( "player%i_score", i+1 ), playerState[ rankedPlayers[ i ]->entityNumber ].teamFragCount );
2539                         } else {
2540                                 hud->SetStateInt( va( "player%i_score", i+1 ), playerState[ rankedPlayers[ i ]->entityNumber ].fragCount );
2541                         }
2542                         hud->SetStateInt( va( "rank%i", i+1 ), 1 );
2543                         UpdateRankColor( hud, "rank%i_color%i", i+1, rankedPlayers[ i ]->colorBar );
2544                         if ( rankedPlayers[ i ] == player ) {
2545                                 hud->SetStateInt( "rank_self", i+1 );
2546                         }
2547                 }
2548         }
2549 #ifdef _D3XP
2550         for ( i = ( gameState == GAMEON ? numRankedPlayers : 0 ) ; i < MAX_CLIENTS; i++ ) {
2551 #else
2552         for ( i = ( gameState == GAMEON ? numRankedPlayers : 0 ) ; i < 5; i++ ) {
2553 #endif
2554                 hud->SetStateString( va( "player%i", i+1 ), "" );
2555                 hud->SetStateString( va( "player%i_score", i+1 ), "" );
2556                 hud->SetStateInt( va( "rank%i", i+1 ), 0 );
2557         }
2558
2559 #ifdef CTF
2560     if ( IsGametypeFlagBased() )
2561         hud->SetStateInt( "self_team", player->team );
2562     else
2563         hud->SetStateInt( "self_team", -1 ); /* Disable */
2564 #endif
2565
2566 }
2567
2568 /*
2569 ================
2570 idMultiplayerGame::DrawScoreBoard
2571 ================
2572 */
2573 void idMultiplayerGame::DrawScoreBoard( idPlayer *player ) {
2574         if ( player->scoreBoardOpen || gameState == GAMEREVIEW ) {
2575                 if ( !playerState[ player->entityNumber ].scoreBoardUp ) {
2576                         scoreBoard->Activate( true, gameLocal.time );
2577                         playerState[ player->entityNumber ].scoreBoardUp = true;
2578                 }
2579         
2580 #ifdef CTF
2581         if ( IsGametypeFlagBased() )
2582             UpdateCTFScoreboard( scoreBoard, player );
2583         else
2584 #endif
2585                 UpdateScoreboard( scoreBoard, player );
2586         
2587         } else {
2588                 if ( playerState[ player->entityNumber ].scoreBoardUp ) {
2589                         scoreBoard->Activate( false, gameLocal.time );
2590                         playerState[ player->entityNumber ].scoreBoardUp = false;
2591                 }
2592         }
2593 }
2594
2595 /*
2596 ===============
2597 idMultiplayerGame::ClearChatData
2598 ===============
2599 */
2600 void idMultiplayerGame::ClearChatData() {
2601         chatHistoryIndex        = 0;
2602         chatHistorySize         = 0;
2603         chatDataUpdated         = true;
2604 }
2605
2606 /*
2607 ===============
2608 idMultiplayerGame::AddChatLine
2609 ===============
2610 */
2611 void idMultiplayerGame::AddChatLine( const char *fmt, ... ) {
2612         idStr temp;
2613         va_list argptr;
2614         
2615         va_start( argptr, fmt );
2616         vsprintf( temp, fmt, argptr );
2617         va_end( argptr );
2618         
2619         gameLocal.Printf( "%s\n", temp.c_str() );
2620
2621         chatHistory[ chatHistoryIndex % NUM_CHAT_NOTIFY ].line = temp;
2622         chatHistory[ chatHistoryIndex % NUM_CHAT_NOTIFY ].fade = 6;
2623
2624         chatHistoryIndex++;
2625         if ( chatHistorySize < NUM_CHAT_NOTIFY ) {
2626                 chatHistorySize++;
2627         }
2628         chatDataUpdated = true;
2629         lastChatLineTime = gameLocal.time;
2630 }
2631
2632 /*
2633 ===============
2634 idMultiplayerGame::DrawChat
2635 ===============
2636 */
2637 void idMultiplayerGame::DrawChat() {
2638         int i, j;
2639         if ( guiChat ) {
2640                 if ( gameLocal.time - lastChatLineTime > CHAT_FADE_TIME ) {
2641                         if ( chatHistorySize > 0 ) {
2642                                 for ( i = chatHistoryIndex - chatHistorySize; i < chatHistoryIndex; i++ ) {
2643                                         chatHistory[ i % NUM_CHAT_NOTIFY ].fade--;
2644                                         if ( chatHistory[ i % NUM_CHAT_NOTIFY ].fade < 0 ) {
2645                                                 chatHistorySize--; // this assumes the removals are always at the beginning
2646                                         }
2647                                 }
2648                                 chatDataUpdated = true;
2649                         }
2650                         lastChatLineTime = gameLocal.time;
2651                 }
2652                 if ( chatDataUpdated ) {
2653                         j = 0;
2654                         i = chatHistoryIndex - chatHistorySize;
2655                         while ( i < chatHistoryIndex ) {
2656                                 guiChat->SetStateString( va( "chat%i", j ), chatHistory[ i % NUM_CHAT_NOTIFY ].line );
2657                                 // don't set alpha above 4, the gui only knows that
2658                                 guiChat->SetStateInt( va( "alpha%i", j ), Min( 4, (int)chatHistory[ i % NUM_CHAT_NOTIFY ].fade ) );
2659                                 j++; i++;
2660                         }
2661                         while ( j < NUM_CHAT_NOTIFY ) {
2662                                 guiChat->SetStateString( va( "chat%i", j ), "" );
2663                                 j++;
2664                         }
2665                         guiChat->Activate( true, gameLocal.time );
2666                         chatDataUpdated = false;
2667                 }
2668                 guiChat->Redraw( gameLocal.time );
2669         }
2670 }
2671
2672 #ifdef _D3XP
2673 //D3XP: Adding one to frag count to allow for the negative flag in numbers greater than 255
2674 const int ASYNC_PLAYER_FRAG_BITS = -(idMath::BitsForInteger( MP_PLAYER_MAXFRAGS - MP_PLAYER_MINFRAGS )+1);      // player can have negative frags
2675 #else
2676 const int ASYNC_PLAYER_FRAG_BITS = -idMath::BitsForInteger( MP_PLAYER_MAXFRAGS - MP_PLAYER_MINFRAGS );  // player can have negative frags
2677 #endif
2678 const int ASYNC_PLAYER_WINS_BITS = idMath::BitsForInteger( MP_PLAYER_MAXWINS );
2679 const int ASYNC_PLAYER_PING_BITS = idMath::BitsForInteger( MP_PLAYER_MAXPING );
2680
2681 /*
2682 ================
2683 idMultiplayerGame::WriteToSnapshot
2684 ================
2685 */
2686 void idMultiplayerGame::WriteToSnapshot( idBitMsgDelta &msg ) const {
2687         int i;
2688         int value;
2689
2690         msg.WriteByte( gameState );
2691         msg.WriteShort( currentTourneyPlayer[ 0 ] );
2692         msg.WriteShort( currentTourneyPlayer[ 1 ] );
2693         for ( i = 0; i < MAX_CLIENTS; i++ ) {
2694                 // clamp all values to min/max possible value that we can send over
2695                 value = idMath::ClampInt( MP_PLAYER_MINFRAGS, MP_PLAYER_MAXFRAGS, playerState[i].fragCount );
2696                 msg.WriteBits( value, ASYNC_PLAYER_FRAG_BITS );
2697                 value = idMath::ClampInt( MP_PLAYER_MINFRAGS, MP_PLAYER_MAXFRAGS, playerState[i].teamFragCount );
2698                 msg.WriteBits( value, ASYNC_PLAYER_FRAG_BITS );
2699                 value = idMath::ClampInt( 0, MP_PLAYER_MAXWINS, playerState[i].wins );
2700                 msg.WriteBits( value, ASYNC_PLAYER_WINS_BITS );
2701                 value = idMath::ClampInt( 0, MP_PLAYER_MAXPING, playerState[i].ping );
2702                 msg.WriteBits( value, ASYNC_PLAYER_PING_BITS );
2703                 msg.WriteBits( playerState[i].ingame, 1 );
2704         }
2705
2706 #ifdef CTF
2707     msg.WriteShort( teamPoints[0] );
2708     msg.WriteShort( teamPoints[1] );
2709     msg.WriteShort( player_red_flag );
2710     msg.WriteShort( player_blue_flag );
2711 #endif
2712 }
2713
2714 /*
2715 ================
2716 idMultiplayerGame::ReadFromSnapshot
2717 ================
2718 */
2719 void idMultiplayerGame::ReadFromSnapshot( const idBitMsgDelta &msg ) {
2720         int i;
2721         gameState_t newState;
2722
2723         newState = (idMultiplayerGame::gameState_t)msg.ReadByte();
2724         if ( newState != gameState ) {
2725                 gameLocal.DPrintf( "%s -> %s\n", GameStateStrings[ gameState ], GameStateStrings[ newState ] );
2726                 gameState = newState;
2727                 // these could be gathered in a BGNewState() kind of thing, as we have to do them in NewState as well
2728                 if ( gameState == GAMEON ) {
2729                         matchStartedTime = gameLocal.time;
2730                         cvarSystem->SetCVarString( "ui_ready", "Not Ready" );
2731                         switchThrottle[ 1 ] = 0;        // passby the throttle
2732                         startFragLimit = gameLocal.serverInfo.GetInt( "si_fragLimit" );
2733                 }
2734         }
2735         currentTourneyPlayer[ 0 ] = msg.ReadShort();
2736         currentTourneyPlayer[ 1 ] = msg.ReadShort();
2737         for ( i = 0; i < MAX_CLIENTS; i++ ) {
2738                 playerState[i].fragCount = msg.ReadBits( ASYNC_PLAYER_FRAG_BITS );
2739                 playerState[i].teamFragCount = msg.ReadBits( ASYNC_PLAYER_FRAG_BITS );
2740                 playerState[i].wins = msg.ReadBits( ASYNC_PLAYER_WINS_BITS );
2741                 playerState[i].ping = msg.ReadBits( ASYNC_PLAYER_PING_BITS );
2742                 playerState[i].ingame = msg.ReadBits( 1 ) != 0;
2743         }
2744
2745 #ifdef CTF    
2746     teamPoints[0] = msg.ReadShort();
2747     teamPoints[1] = msg.ReadShort();
2748
2749     player_red_flag = msg.ReadShort();
2750     player_blue_flag = msg.ReadShort();
2751
2752 #endif    
2753
2754 }
2755
2756 /*
2757 ================
2758 idMultiplayerGame::PlayGlobalSound
2759 ================
2760 */
2761 void idMultiplayerGame::PlayGlobalSound( int to, snd_evt_t evt, const char *shader ) {
2762         const idSoundShader *shaderDecl;
2763
2764         if ( to == -1 || to == gameLocal.localClientNum ) {
2765                 if ( shader ) {
2766                         if ( gameSoundWorld ) {
2767                                 gameSoundWorld->PlayShaderDirectly( shader );
2768                         }
2769                 } else {
2770                         if ( gameSoundWorld ) {
2771                                 gameSoundWorld->PlayShaderDirectly( GlobalSoundStrings[ evt ] );
2772                         }
2773                 }
2774         }
2775
2776         if ( !gameLocal.isClient ) {
2777                 idBitMsg outMsg;
2778                 byte msgBuf[1024];
2779                 outMsg.Init( msgBuf, sizeof( msgBuf ) );
2780
2781                 if ( shader ) {
2782                         shaderDecl = declManager->FindSound( shader );
2783                         if ( !shaderDecl ) {
2784                                 return;
2785                         }
2786                         outMsg.WriteByte( GAME_RELIABLE_MESSAGE_SOUND_INDEX );
2787                         outMsg.WriteLong( gameLocal.ServerRemapDecl( to, DECL_SOUND, shaderDecl->Index() ) );
2788                 } else {
2789                         outMsg.WriteByte( GAME_RELIABLE_MESSAGE_SOUND_EVENT );
2790                         outMsg.WriteByte( evt );
2791                 }
2792
2793                 networkSystem->ServerSendReliableMessage( to, outMsg );
2794         }
2795 }
2796
2797 #ifdef CTF
2798 /*
2799 ================
2800 idMultiplayerGame::PlayTeamSound
2801 ================
2802 */
2803 void idMultiplayerGame::PlayTeamSound( int toTeam, snd_evt_t evt, const char *shader ) {
2804         for( int i = 0; i < gameLocal.numClients; i++ ) {
2805                 idEntity *ent = gameLocal.entities[ i ];
2806                 if ( !ent || !ent->IsType( idPlayer::Type ) ) {
2807                         continue;
2808                 }
2809                 idPlayer * player = static_cast<idPlayer*>(ent);
2810                 if ( player->team != toTeam )
2811                         continue;
2812                 PlayGlobalSound( i, evt, shader );
2813         }
2814 }
2815 #endif
2816
2817 /*
2818 ================
2819 idMultiplayerGame::PrintMessageEvent
2820 ================
2821 */
2822 void idMultiplayerGame::PrintMessageEvent( int to, msg_evt_t evt, int parm1, int parm2 ) {
2823         switch ( evt ) {
2824                 case MSG_SUICIDE:
2825                         assert( parm1 >= 0 );
2826                         AddChatLine( common->GetLanguageDict()->GetString( "#str_04293" ), gameLocal.userInfo[ parm1 ].GetString( "ui_name" ) );
2827                         break;
2828                 case MSG_KILLED:
2829                         assert( parm1 >= 0 && parm2 >= 0 );
2830                         AddChatLine( common->GetLanguageDict()->GetString( "#str_04292" ), gameLocal.userInfo[ parm1 ].GetString( "ui_name" ), gameLocal.userInfo[ parm2 ].GetString( "ui_name" ) );
2831                         break;
2832                 case MSG_KILLEDTEAM:
2833                         assert( parm1 >= 0 && parm2 >= 0 );
2834                         AddChatLine( common->GetLanguageDict()->GetString( "#str_04291" ), gameLocal.userInfo[ parm1 ].GetString( "ui_name" ), gameLocal.userInfo[ parm2 ].GetString( "ui_name" ) );
2835                         break;
2836                 case MSG_TELEFRAGGED:
2837                         assert( parm1 >= 0 && parm2 >= 0 );
2838                         AddChatLine( common->GetLanguageDict()->GetString( "#str_04290" ), gameLocal.userInfo[ parm1 ].GetString( "ui_name" ), gameLocal.userInfo[ parm2 ].GetString( "ui_name" ) );
2839                         break;
2840                 case MSG_DIED:
2841                         assert( parm1 >= 0 );
2842                         AddChatLine( common->GetLanguageDict()->GetString( "#str_04289" ), gameLocal.userInfo[ parm1 ].GetString( "ui_name" ) );
2843                         break;
2844                 case MSG_VOTE:
2845                         AddChatLine( common->GetLanguageDict()->GetString( "#str_04288" ) );
2846                         break;
2847                 case MSG_SUDDENDEATH:
2848                         AddChatLine( common->GetLanguageDict()->GetString( "#str_04287" ) );
2849                         break;
2850                 case MSG_FORCEREADY:
2851                         AddChatLine( common->GetLanguageDict()->GetString( "#str_04286" ), gameLocal.userInfo[ parm1 ].GetString( "ui_name" ) );
2852                         if ( gameLocal.entities[ parm1 ] && gameLocal.entities[ parm1 ]->IsType( idPlayer::Type ) ) {
2853                                 static_cast< idPlayer * >( gameLocal.entities[ parm1 ] )->forcedReady = true;
2854                         }
2855                         break;
2856                 case MSG_JOINEDSPEC:
2857                         AddChatLine( common->GetLanguageDict()->GetString( "#str_04285" ), gameLocal.userInfo[ parm1 ].GetString( "ui_name" ) );
2858                         break;
2859                 case MSG_TIMELIMIT:
2860                         AddChatLine( common->GetLanguageDict()->GetString( "#str_04284" ) );
2861                         break;
2862                 case MSG_FRAGLIMIT:
2863                         if ( gameLocal.gameType == GAME_LASTMAN ) {
2864                                 AddChatLine( common->GetLanguageDict()->GetString( "#str_04283" ), gameLocal.userInfo[ parm1 ].GetString( "ui_name" ) );
2865                         } else if ( IsGametypeTeamBased() ) { /* CTF */
2866                                 AddChatLine( common->GetLanguageDict()->GetString( "#str_04282" ), gameLocal.userInfo[ parm1 ].GetString( "ui_team" ) );
2867                         } else {
2868                                 AddChatLine( common->GetLanguageDict()->GetString( "#str_04281" ), gameLocal.userInfo[ parm1 ].GetString( "ui_name" ) );
2869                         }
2870                         break;
2871                 case MSG_JOINTEAM:
2872                         AddChatLine( common->GetLanguageDict()->GetString( "#str_04280" ), gameLocal.userInfo[ parm1 ].GetString( "ui_name" ), parm2 ? common->GetLanguageDict()->GetString( "#str_02500" ) : common->GetLanguageDict()->GetString( "#str_02499" ) );
2873                         break;
2874                 case MSG_HOLYSHIT:
2875                         AddChatLine( common->GetLanguageDict()->GetString( "#str_06732" ) );
2876                         break;
2877 #ifdef CTF
2878                 case MSG_POINTLIMIT:
2879                         AddChatLine( common->GetLanguageDict()->GetString( "#str_11100" ), parm1 ? common->GetLanguageDict()->GetString( "#str_11110" ) : common->GetLanguageDict()->GetString( "#str_11111"  ) );
2880                         break;
2881
2882                 case MSG_FLAGTAKEN :
2883             if ( gameLocal.GetLocalPlayer() == NULL )
2884                 break;
2885
2886                         if ( parm2 < 0 || parm2 >= MAX_CLIENTS )
2887                                 break;
2888
2889                         if ( gameLocal.GetLocalPlayer()->team != parm1 ) {
2890                                 AddChatLine( common->GetLanguageDict()->GetString( "#str_11101" ), gameLocal.userInfo[ parm2 ].GetString( "ui_name" ) );        // your team
2891                         } else {
2892                                 AddChatLine( common->GetLanguageDict()->GetString( "#str_11102" ), gameLocal.userInfo[ parm2 ].GetString( "ui_name" ) );        // enemy
2893                         }
2894                         break;
2895
2896                 case MSG_FLAGDROP :
2897             if ( gameLocal.GetLocalPlayer() == NULL )
2898                 break;
2899
2900                         if ( gameLocal.GetLocalPlayer()->team != parm1 ) {
2901                                 AddChatLine( common->GetLanguageDict()->GetString( "#str_11103" ) );    // your team
2902                         } else {
2903                                 AddChatLine( common->GetLanguageDict()->GetString( "#str_11104" ) );    // enemy
2904                         }
2905                         break;
2906
2907                 case MSG_FLAGRETURN :
2908             if ( gameLocal.GetLocalPlayer() == NULL )
2909                 break;
2910
2911                         if ( parm2 >= 0 && parm2 < MAX_CLIENTS ) {
2912                                 if ( gameLocal.GetLocalPlayer()->team != parm1 ) {
2913                                         AddChatLine( common->GetLanguageDict()->GetString( "#str_11120" ), gameLocal.userInfo[ parm2 ].GetString( "ui_name" ) );        // your team
2914                                 } else {
2915                                         AddChatLine( common->GetLanguageDict()->GetString( "#str_11121" ), gameLocal.userInfo[ parm2 ].GetString( "ui_name" ) );        // enemy
2916                                 }
2917                         } else {
2918                                 AddChatLine( common->GetLanguageDict()->GetString( "#str_11105" ), parm1 ? common->GetLanguageDict()->GetString( "#str_11110" ) : common->GetLanguageDict()->GetString( "#str_11111" ) );                       
2919                         }
2920                         break;
2921
2922                 case MSG_FLAGCAPTURE :
2923             if ( gameLocal.GetLocalPlayer() == NULL )
2924                 break;
2925
2926                         if ( parm2 < 0 || parm2 >= MAX_CLIENTS )
2927                                 break;
2928
2929                         if ( gameLocal.GetLocalPlayer()->team != parm1 ) {
2930                                 AddChatLine( common->GetLanguageDict()->GetString( "#str_11122" ), gameLocal.userInfo[ parm2 ].GetString( "ui_name" ) );        // your team
2931                         } else {
2932                                 AddChatLine( common->GetLanguageDict()->GetString( "#str_11123" ), gameLocal.userInfo[ parm2 ].GetString( "ui_name" ) );        // enemy
2933                         }       
2934                         
2935 //                      AddChatLine( common->GetLanguageDict()->GetString( "#str_11106" ), parm1 ? common->GetLanguageDict()->GetString( "#str_11110" ) : common->GetLanguageDict()->GetString( "#str_11111" ) );
2936                         break;
2937
2938                 case MSG_SCOREUPDATE:
2939                         AddChatLine( common->GetLanguageDict()->GetString( "#str_11107" ), parm1, parm2 );
2940                         break;
2941 #endif
2942                 default:
2943                         gameLocal.DPrintf( "PrintMessageEvent: unknown message type %d\n", evt );
2944                         return;
2945         }
2946         if ( !gameLocal.isClient ) {
2947                 idBitMsg outMsg;
2948                 byte msgBuf[1024];
2949                 outMsg.Init( msgBuf, sizeof( msgBuf ) );
2950                 outMsg.WriteByte( GAME_RELIABLE_MESSAGE_DB );
2951                 outMsg.WriteByte( evt );
2952                 outMsg.WriteByte( parm1 );
2953                 outMsg.WriteByte( parm2 );
2954                 networkSystem->ServerSendReliableMessage( to, outMsg );
2955         }
2956 }
2957
2958 /*
2959 ================
2960 idMultiplayerGame::SuddenRespawns
2961 solely for LMN if an end game ( fragLimitTimeout ) was entered and aborted before expiration
2962 LMN players which still have lives left need to be respawned without being marked lastManOver
2963 ================
2964 */
2965 void idMultiplayerGame::SuddenRespawn( void ) {
2966         int i;
2967
2968         if ( gameLocal.gameType != GAME_LASTMAN ) {
2969                 return;
2970         }
2971
2972         for ( i = 0; i < gameLocal.numClients; i++ ) {
2973                 if ( !gameLocal.entities[ i ] || !gameLocal.entities[ i ]->IsType( idPlayer::Type ) ) {
2974                         continue;
2975                 }
2976                 if ( !CanPlay( static_cast< idPlayer * >( gameLocal.entities[ i ] ) ) ) {
2977                         continue;
2978                 }
2979                 if ( static_cast< idPlayer * >( gameLocal.entities[ i ] )->lastManOver ) {
2980                         continue;
2981                 }
2982                 static_cast< idPlayer * >( gameLocal.entities[ i ] )->lastManPlayAgain = true;
2983         }
2984 }
2985
2986 /*
2987 ================
2988 idMultiplayerGame::CheckSpawns
2989 ================
2990 */
2991 void idMultiplayerGame::CheckRespawns( idPlayer *spectator ) {
2992         for( int i = 0 ; i < gameLocal.numClients ; i++ ) {
2993                 idEntity *ent = gameLocal.entities[ i ];
2994                 if ( !ent || !ent->IsType( idPlayer::Type ) ) {
2995                         continue;
2996                 }
2997                 idPlayer *p = static_cast<idPlayer *>(ent);
2998                 // once we hit sudden death, nobody respawns till game has ended
2999                 if ( WantRespawn( p ) || p == spectator ) {
3000                         if ( gameState == SUDDENDEATH && gameLocal.gameType != GAME_LASTMAN ) {
3001                                 // respawn rules while sudden death are different
3002                                 // sudden death may trigger while a player is dead, so there are still cases where we need to respawn
3003                                 // don't do any respawns while we are in end game delay though
3004                                 if ( !fragLimitTimeout ) {
3005                                         if ( IsGametypeTeamBased() || p->IsLeader() ) {                     /* CTF */
3006 #ifdef _DEBUG
3007                                                 if ( gameLocal.gameType == GAME_TOURNEY ) {
3008                                                         assert( p->entityNumber == currentTourneyPlayer[ 0 ] || p->entityNumber == currentTourneyPlayer[ 1 ] );
3009                                                 }
3010 #endif
3011                                                 p->ServerSpectate( false );
3012                                         } else if ( !p->IsLeader() ) {
3013                                                 // sudden death is rolling, this player is not a leader, have him spectate
3014                                                 p->ServerSpectate( true );
3015                                                 CheckAbortGame();
3016                                         }
3017                                 }
3018                         } else {
3019                                 if ( gameLocal.gameType == GAME_DM ||           // CTF : 3wave sboily, was DM really included before?
3020                      IsGametypeTeamBased() )
3021                 {
3022                                         if ( gameState == WARMUP || gameState == COUNTDOWN || gameState == GAMEON ) {
3023                                                 p->ServerSpectate( false );
3024                                         }                               
3025                                 } else if ( gameLocal.gameType == GAME_TOURNEY ) {
3026                                         if ( i == currentTourneyPlayer[ 0 ] || i == currentTourneyPlayer[ 1 ] ) {
3027                                                 if ( gameState == WARMUP || gameState == COUNTDOWN || gameState == GAMEON ) {
3028                                                         p->ServerSpectate( false );
3029                                                 }
3030                                         } else if ( gameState == WARMUP ) {
3031                                                 // make sure empty tourney slots get filled first
3032                                                 FillTourneySlots( );
3033                                                 if ( i == currentTourneyPlayer[ 0 ] || i == currentTourneyPlayer[ 1 ] ) {
3034                                                         p->ServerSpectate( false );
3035                                                 }
3036                                         }
3037                                 } else if ( gameLocal.gameType == GAME_LASTMAN ) {
3038                                         if ( gameState == WARMUP || gameState == COUNTDOWN ) {
3039                                                 p->ServerSpectate( false );
3040                                         } else if ( gameState == GAMEON || gameState == SUDDENDEATH ) {
3041                                                 if ( gameState == GAMEON && playerState[ i ].fragCount > 0 && p->lastManPresent ) {
3042                                                         assert( !p->lastManOver );
3043                                                         p->ServerSpectate( false );
3044                                                 } else if ( p->lastManPlayAgain && p->lastManPresent ) {
3045                                                         assert( gameState == SUDDENDEATH );
3046                                                         p->ServerSpectate( false );
3047                                                 } else {
3048                                                         // if a fragLimitTimeout was engaged, do NOT mark lastManOver as that could mean
3049                                                         // everyone ends up spectator and game is stalled with no end
3050                                                         // if the frag limit delay is engaged and cancels out before expiring, LMN players are
3051                                                         // respawned to play the tie again ( through SuddenRespawn and lastManPlayAgain )
3052                                                         if ( !fragLimitTimeout && !p->lastManOver ) {
3053                                                                 common->DPrintf( "client %d has lost all last man lives\n", i );
3054                                                                 // end of the game for this guy, send him to spectators
3055                                                                 p->lastManOver = true;
3056                                                                 // clients don't have access to lastManOver
3057                                                                 // so set the fragCount to something silly ( used in scoreboard and player ranking )
3058                                                                 playerState[ i ].fragCount = LASTMAN_NOLIVES;
3059                                                                 p->ServerSpectate( true );
3060
3061                                                                 //Check for a situation where the last two player dies at the same time and don't
3062                                                                 //try to respawn manually...This was causing all players to go into spectate mode
3063                                                                 //and the server got stuck
3064                                                                 {
3065                                                                         int j;
3066                                                                         for ( j = 0; j < gameLocal.numClients; j++ ) {
3067                                                                                 if ( !gameLocal.entities[ j ] ) {
3068                                                                                         continue;
3069                                                                                 }
3070                                                                                 if ( !CanPlay( static_cast< idPlayer * >( gameLocal.entities[ j ] ) ) ) {
3071                                                                                         continue;
3072                                                                                 }
3073                                                                                 if ( !static_cast< idPlayer * >( gameLocal.entities[ j ] )->lastManOver ) {
3074                                                                                         break;
3075                                                                                 }
3076                                                                         }
3077                                                                         if( j == gameLocal.numClients) {
3078                                                                                 //Everyone is dead so don't allow this player to spectate
3079                                                                                 //so the match will end
3080                                                                                 p->ServerSpectate( false );
3081                                                                         }
3082                                                                 }
3083                                                         }
3084                                                 }
3085                                         }
3086                                 }
3087                         }
3088                 } else if ( p->wantSpectate && !p->spectating ) {
3089                         playerState[ i ].fragCount = 0; // whenever you willingly go spectate during game, your score resets
3090                         p->ServerSpectate( true );
3091                         UpdateTourneyLine();
3092                         CheckAbortGame();
3093                 }
3094         }
3095 }
3096
3097 /*
3098 ================
3099 idMultiplayerGame::ForceReady
3100 ================
3101 */
3102 void idMultiplayerGame::ForceReady( ) {
3103
3104         for( int i = 0 ; i < gameLocal.numClients ; i++ ) {
3105                 idEntity *ent = gameLocal.entities[ i ];
3106                 if ( !ent || !ent->IsType( idPlayer::Type ) ) {
3107                         continue;
3108                 }
3109                 idPlayer *p = static_cast<idPlayer *>( ent );
3110                 if ( !p->IsReady() ) {
3111                         PrintMessageEvent( -1, MSG_FORCEREADY, i );
3112                         p->forcedReady = true;
3113                 }
3114         }
3115 }
3116
3117 /*
3118 ================
3119 idMultiplayerGame::ForceReady_f
3120 ================
3121 */
3122 void idMultiplayerGame::ForceReady_f( const idCmdArgs &args ) {
3123         if ( !gameLocal.isMultiplayer || gameLocal.isClient ) {
3124                 common->Printf( "forceReady: multiplayer server only\n" );
3125                 return;
3126         }
3127         gameLocal.mpGame.ForceReady();
3128 }
3129
3130 /*
3131 ================
3132 idMultiplayerGame::DropWeapon
3133 ================
3134 */
3135 void idMultiplayerGame::DropWeapon( int clientNum ) {
3136         assert( !gameLocal.isClient );
3137         idEntity *ent = gameLocal.entities[ clientNum ];
3138         if ( !ent || !ent->IsType( idPlayer::Type ) ) {
3139                 return;
3140         }
3141         static_cast< idPlayer* >( ent )->DropWeapon( false );
3142 }
3143
3144 /*
3145 ================
3146 idMultiplayerGame::DropWeapon_f
3147 ================
3148 */
3149 void idMultiplayerGame::DropWeapon_f( const idCmdArgs &args ) {
3150         if ( !gameLocal.isMultiplayer ) {
3151                 common->Printf( "clientDropWeapon: only valid in multiplayer\n" );
3152                 return;
3153         }
3154         idBitMsg        outMsg;
3155         byte            msgBuf[128];
3156         outMsg.Init( msgBuf, sizeof( msgBuf ) );
3157         outMsg.WriteByte( GAME_RELIABLE_MESSAGE_DROPWEAPON );
3158         networkSystem->ClientSendReliableMessage( outMsg );
3159 }
3160
3161 /*
3162 ================
3163 idMultiplayerGame::MessageMode_f
3164 ================
3165 */
3166 void idMultiplayerGame::MessageMode_f( const idCmdArgs &args ) {
3167         gameLocal.mpGame.MessageMode( args );
3168 }
3169
3170 /*
3171 ================
3172 idMultiplayerGame::MessageMode
3173 ================
3174 */
3175 void idMultiplayerGame::MessageMode( const idCmdArgs &args ) {
3176         const char *mode;
3177         int imode;
3178
3179         if ( !gameLocal.isMultiplayer ) {
3180                 common->Printf( "clientMessageMode: only valid in multiplayer\n" );
3181                 return;
3182         }
3183         if ( !mainGui ) {
3184                 common->Printf( "no local client\n" );
3185                 return;
3186         }
3187         mode = args.Argv( 1 );
3188         if ( !mode[ 0 ] ) {
3189                 imode = 0;
3190         } else {
3191                 imode = atoi( mode );
3192         }
3193         msgmodeGui->SetStateString( "messagemode", imode ? "1" : "0" );
3194         msgmodeGui->SetStateString( "chattext", "" );
3195         nextMenu = 2;
3196         // let the session know that we want our ingame main menu opened
3197         gameLocal.sessionCommand = "game_startmenu";
3198 }
3199
3200 /*
3201 ================
3202 idMultiplayerGame::Vote_f
3203 FIXME: voting from console
3204 ================
3205 */
3206 void idMultiplayerGame::Vote_f( const idCmdArgs &args ) { }
3207
3208 /*
3209 ================
3210 idMultiplayerGame::CallVote_f
3211 FIXME: voting from console
3212 ================
3213 */
3214 void idMultiplayerGame::CallVote_f( const idCmdArgs &args ) { }
3215
3216 /*
3217 ================
3218 idMultiplayerGame::ServerStartVote
3219 ================
3220 */
3221 void idMultiplayerGame::ServerStartVote( int clientNum, vote_flags_t voteIndex, const char *value ) {
3222         int i;
3223
3224         assert( vote == VOTE_NONE );
3225
3226         // setup
3227         yesVotes = 1;
3228         noVotes = 0;
3229         vote = voteIndex;
3230         voteValue = value;
3231         voteTimeOut = gameLocal.time + 20000;
3232         // mark players allowed to vote - only current ingame players, players joining during vote will be ignored
3233         for ( i = 0; i < gameLocal.numClients; i++ ) {
3234                 if ( gameLocal.entities[ i ] && gameLocal.entities[ i ]->IsType( idPlayer::Type ) ) {
3235                         playerState[ i ].vote = ( i == clientNum ) ? PLAYER_VOTE_YES : PLAYER_VOTE_WAIT;
3236                 } else {
3237                         playerState[i].vote = PLAYER_VOTE_NONE;
3238                 }
3239         }
3240 }
3241
3242 /*
3243 ================
3244 idMultiplayerGame::ClientStartVote
3245 ================
3246 */
3247 void idMultiplayerGame::ClientStartVote( int clientNum, const char *_voteString ) {
3248         idBitMsg        outMsg;
3249         byte            msgBuf[ MAX_GAME_MESSAGE_SIZE ];
3250
3251         if ( !gameLocal.isClient ) {
3252                 outMsg.Init( msgBuf, sizeof( msgBuf ) );
3253                 outMsg.WriteByte( GAME_RELIABLE_MESSAGE_STARTVOTE );
3254                 outMsg.WriteByte( clientNum );
3255                 outMsg.WriteString( _voteString );
3256                 networkSystem->ServerSendReliableMessage( -1, outMsg );
3257         }
3258
3259         voteString = _voteString;
3260         AddChatLine( va( common->GetLanguageDict()->GetString( "#str_04279" ), gameLocal.userInfo[ clientNum ].GetString( "ui_name" ) ) );
3261         gameSoundWorld->PlayShaderDirectly( GlobalSoundStrings[ SND_VOTE ] );
3262         if ( clientNum == gameLocal.localClientNum ) {
3263                 voted = true;
3264         } else {
3265                 voted = false;
3266         }
3267         if ( gameLocal.isClient ) {
3268                 // the the vote value to something so the vote line is displayed
3269                 vote = VOTE_RESTART;
3270                 yesVotes = 1;
3271                 noVotes = 0;
3272         }
3273 }
3274
3275 /*
3276 ================
3277 idMultiplayerGame::ClientUpdateVote
3278 ================
3279 */
3280 void idMultiplayerGame::ClientUpdateVote( vote_result_t status, int yesCount, int noCount ) {
3281         idBitMsg        outMsg;
3282         byte            msgBuf[ MAX_GAME_MESSAGE_SIZE ];
3283
3284         if ( !gameLocal.isClient ) {
3285                 outMsg.Init( msgBuf, sizeof( msgBuf ) );
3286                 outMsg.WriteByte( GAME_RELIABLE_MESSAGE_UPDATEVOTE );
3287                 outMsg.WriteByte( status );
3288                 outMsg.WriteByte( yesCount );
3289                 outMsg.WriteByte( noCount );
3290                 networkSystem->ServerSendReliableMessage( -1, outMsg );
3291         }
3292
3293         if ( vote == VOTE_NONE ) {
3294                 // clients coming in late don't get the vote start and are not allowed to vote
3295                 return;
3296         }
3297
3298         switch ( status ) {
3299                 case VOTE_FAILED:
3300                         AddChatLine( common->GetLanguageDict()->GetString( "#str_04278" ) );
3301                         gameSoundWorld->PlayShaderDirectly( GlobalSoundStrings[ SND_VOTE_FAILED ] );
3302                         if ( gameLocal.isClient ) {
3303                                 vote = VOTE_NONE;
3304                         }
3305                         break;
3306                 case VOTE_PASSED:
3307                         AddChatLine( common->GetLanguageDict()->GetString( "#str_04277" ) );
3308                         gameSoundWorld->PlayShaderDirectly( GlobalSoundStrings[ SND_VOTE_PASSED ] );
3309                         break;
3310                 case VOTE_RESET:
3311                         if ( gameLocal.isClient ) {
3312                                 vote = VOTE_NONE;
3313                         }
3314                         break;
3315                 case VOTE_ABORTED:
3316                         AddChatLine( common->GetLanguageDict()->GetString( "#str_04276" ) );
3317                         if ( gameLocal.isClient ) {
3318                                 vote = VOTE_NONE;
3319                         }
3320                         break;
3321                 default:
3322                         break;
3323         }
3324         if ( gameLocal.isClient ) {
3325                 yesVotes = yesCount;
3326                 noVotes = noCount;
3327         }
3328 }
3329
3330 /*
3331 ================
3332 idMultiplayerGame::ClientCallVote
3333 ================
3334 */
3335 void idMultiplayerGame::ClientCallVote( vote_flags_t voteIndex, const char *voteValue ) {
3336         idBitMsg        outMsg;
3337         byte            msgBuf[ MAX_GAME_MESSAGE_SIZE ];
3338
3339         // send 
3340         outMsg.Init( msgBuf, sizeof( msgBuf ) );
3341         outMsg.WriteByte( GAME_RELIABLE_MESSAGE_CALLVOTE );
3342         outMsg.WriteByte( voteIndex );
3343         outMsg.WriteString( voteValue );
3344         networkSystem->ClientSendReliableMessage( outMsg );
3345 }
3346
3347 /*
3348 ================
3349 idMultiplayerGame::CastVote
3350 ================
3351 */
3352 void idMultiplayerGame::CastVote( int clientNum, bool castVote ) {
3353         idBitMsg        outMsg;
3354         byte            msgBuf[ 128 ];
3355
3356         if ( clientNum == gameLocal.localClientNum ) {
3357                 voted = true;
3358         }
3359
3360         if ( gameLocal.isClient ) {
3361                 outMsg.Init( msgBuf, sizeof( msgBuf ) );
3362                 outMsg.WriteByte( GAME_RELIABLE_MESSAGE_CASTVOTE );
3363                 outMsg.WriteByte( castVote );
3364                 networkSystem->ClientSendReliableMessage( outMsg );
3365                 return;
3366         }
3367
3368         // sanity
3369         if ( vote == VOTE_NONE ) {
3370                 gameLocal.ServerSendChatMessage( clientNum, "server", common->GetLanguageDict()->GetString( "#str_04275" ) );
3371                 common->DPrintf( "client %d: cast vote while no vote in progress\n", clientNum );
3372                 return;
3373         }
3374         if ( playerState[ clientNum ].vote != PLAYER_VOTE_WAIT ) {
3375                 gameLocal.ServerSendChatMessage( clientNum, "server", common->GetLanguageDict()->GetString( "#str_04274" ) );
3376                 common->DPrintf( "client %d: cast vote - vote %d != PLAYER_VOTE_WAIT\n", clientNum, playerState[ clientNum ].vote );
3377                 return;
3378         }
3379
3380         if ( castVote ) {
3381                 playerState[ clientNum ].vote = PLAYER_VOTE_YES;
3382                 yesVotes++;
3383         } else {
3384                 playerState[ clientNum ].vote = PLAYER_VOTE_NO;
3385                 noVotes++;
3386         }
3387
3388         ClientUpdateVote( VOTE_UPDATE, yesVotes, noVotes );
3389 }
3390
3391 /*
3392 ================
3393 idMultiplayerGame::ServerCallVote
3394 ================
3395 */
3396 void idMultiplayerGame::ServerCallVote( int clientNum, const idBitMsg &msg ) {
3397         vote_flags_t    voteIndex;
3398         int                             vote_timeLimit, vote_fragLimit, vote_clientNum, vote_gameTypeIndex; //, vote_kickIndex;
3399         char                    value[ MAX_STRING_CHARS ];
3400
3401         assert( clientNum != -1 );
3402         assert( !gameLocal.isClient );
3403
3404         voteIndex = (vote_flags_t)msg.ReadByte( );
3405         msg.ReadString( value, sizeof( value ) );
3406
3407         // sanity checks - setup the vote
3408         if ( vote != VOTE_NONE ) {
3409                 gameLocal.ServerSendChatMessage( clientNum, "server", common->GetLanguageDict()->GetString( "#str_04273" ) );
3410                 common->DPrintf( "client %d: called vote while voting already in progress - ignored\n", clientNum );
3411                 return;
3412         }
3413         switch ( voteIndex ) {
3414                 case VOTE_RESTART:
3415                         ServerStartVote( clientNum, voteIndex, "" );
3416                         ClientStartVote( clientNum, common->GetLanguageDict()->GetString( "#str_04271" ) );
3417                         break;
3418                 case VOTE_NEXTMAP:
3419                         ServerStartVote( clientNum, voteIndex, "" );
3420                         ClientStartVote( clientNum, common->GetLanguageDict()->GetString( "#str_04272" ) );
3421                         break;
3422                 case VOTE_TIMELIMIT:
3423                         vote_timeLimit = strtol( value, NULL, 10 );
3424                         if ( vote_timeLimit == gameLocal.serverInfo.GetInt( "si_timeLimit" ) ) {
3425                                 gameLocal.ServerSendChatMessage( clientNum, "server", common->GetLanguageDict()->GetString( "#str_04270" ) );
3426                                 common->DPrintf( "client %d: already at the voted Time Limit\n", clientNum );
3427                                 return;                                 
3428                         }
3429                         if ( vote_timeLimit < si_timeLimit.GetMinValue() || vote_timeLimit > si_timeLimit.GetMaxValue() ) {
3430                                 gameLocal.ServerSendChatMessage( clientNum, "server", common->GetLanguageDict()->GetString( "#str_04269" ) );
3431                                 common->DPrintf( "client %d: timelimit value out of range for vote: %s\n", clientNum, value );
3432                                 return;
3433                         }
3434                         ServerStartVote( clientNum, voteIndex, value );
3435                         ClientStartVote( clientNum, va( common->GetLanguageDict()->GetString( "#str_04268" ), vote_timeLimit ) );
3436                         break;
3437                 case VOTE_FRAGLIMIT:
3438                         vote_fragLimit = strtol( value, NULL, 10 );
3439                         if ( vote_fragLimit == gameLocal.serverInfo.GetInt( "si_fragLimit" ) ) {
3440                                 gameLocal.ServerSendChatMessage( clientNum, "server", common->GetLanguageDict()->GetString( "#str_04267" ) );
3441                                 common->DPrintf( "client %d: already at the voted Frag Limit\n", clientNum );
3442                                 return;                                 
3443                         }
3444                         if ( vote_fragLimit < si_fragLimit.GetMinValue() || vote_fragLimit > si_fragLimit.GetMaxValue() ) {
3445                                 gameLocal.ServerSendChatMessage( clientNum, "server", common->GetLanguageDict()->GetString( "#str_04266" ) );
3446                                 common->DPrintf( "client %d: fraglimit value out of range for vote: %s\n", clientNum, value );
3447                                 return;
3448                         }
3449                         ServerStartVote( clientNum, voteIndex, value );
3450                         ClientStartVote( clientNum, va( common->GetLanguageDict()->GetString( "#str_04303" ), gameLocal.gameType == GAME_LASTMAN ? common->GetLanguageDict()->GetString( "#str_04264" ) : common->GetLanguageDict()->GetString( "#str_04265" ), vote_fragLimit ) );
3451                         break;
3452                 case VOTE_GAMETYPE:
3453                         vote_gameTypeIndex = strtol( value, NULL, 10 );
3454 #ifdef CTF
3455                         assert( vote_gameTypeIndex > 0 && vote_gameTypeIndex < GAME_COUNT );
3456                         strcpy( value, si_gameTypeArgs[ vote_gameTypeIndex ] );
3457 #endif
3458
3459 /*#ifdef CTF
3460                         assert( vote_gameTypeIndex >= 0 && vote_gameTypeIndex <= 4 );
3461 #else
3462                         assert( vote_gameTypeIndex >= 0 && vote_gameTypeIndex <= 3 );
3463 #endif
3464                         switch ( vote_gameTypeIndex ) {
3465                                 case 0:
3466                                         strcpy( value, "Deathmatch" );
3467                                         break;
3468                                 case 1:
3469                                         strcpy( value, "Tourney" );
3470                                         break;
3471                                 case 2:
3472                                         strcpy( value, "Team DM" );
3473                                         break;
3474                                 case 3:
3475                                         strcpy( value, "Last Man" );
3476                                         break;
3477 #ifdef CTF
3478                                 case 4:
3479                                         strcpy( value, "CTF" );
3480                                         break;
3481 #endif
3482                         }*/
3483                         if ( !idStr::Icmp( value, gameLocal.serverInfo.GetString( "si_gameType" ) ) ) {
3484                                 gameLocal.ServerSendChatMessage( clientNum, "server", common->GetLanguageDict()->GetString( "#str_04259" ) );
3485                                 common->DPrintf( "client %d: already at the voted Game Type\n", clientNum );
3486                                 return;
3487                         }
3488                         ServerStartVote( clientNum, voteIndex, value );
3489                         ClientStartVote( clientNum, va( common->GetLanguageDict()->GetString( "#str_04258" ), value ) );
3490                         break;
3491                 case VOTE_KICK:
3492                         vote_clientNum = strtol( value, NULL, 10 );
3493                         if ( vote_clientNum == gameLocal.localClientNum ) {
3494                                 gameLocal.ServerSendChatMessage( clientNum, "server", common->GetLanguageDict()->GetString( "#str_04257" ) );
3495                                 common->DPrintf( "client %d: called kick for the server host\n", clientNum );
3496                                 return;
3497                         }
3498                         ServerStartVote( clientNum, voteIndex, va( "%d", vote_clientNum ) );
3499                         ClientStartVote( clientNum, va( common->GetLanguageDict()->GetString( "#str_04302" ), vote_clientNum, gameLocal.userInfo[ vote_clientNum ].GetString( "ui_name" ) ) );
3500                         break;
3501                 case VOTE_MAP: {
3502                         if ( idStr::FindText( gameLocal.serverInfo.GetString( "si_map" ), value ) != -1 ) {
3503                                 gameLocal.ServerSendChatMessage( clientNum, "server", va( common->GetLanguageDict()->GetString( "#str_04295" ), value ) );
3504                                 common->DPrintf( "client %d: already running the voted map: %s\n", clientNum, value );
3505                                 return;
3506                         }
3507                         int                             num = fileSystem->GetNumMaps();
3508                         int                             i;
3509                         const idDict    *dict;
3510                         bool                    haveMap = false;
3511                         for ( i = 0; i < num; i++ ) {
3512                                 dict = fileSystem->GetMapDecl( i );
3513                                 if ( dict && !idStr::Icmp( dict->GetString( "path" ), value ) ) {
3514                                         haveMap = true;
3515                                         break;
3516                                 }
3517                         }
3518                         if ( !haveMap ) {
3519                                 gameLocal.ServerSendChatMessage( clientNum, "server", va( common->GetLanguageDict()->GetString( "#str_04296" ), value ) );
3520                                 common->Printf( "client %d: map not found: %s\n", clientNum, value );
3521                                 return;
3522                         }
3523                         ServerStartVote( clientNum, voteIndex, value );
3524                         ClientStartVote( clientNum, va( common->GetLanguageDict()->GetString( "#str_04256" ), common->GetLanguageDict()->GetString( dict ? dict->GetString( "name" ) : value ) ) );
3525                         break;
3526                 }
3527                 case VOTE_SPECTATORS:
3528                         if ( gameLocal.serverInfo.GetBool( "si_spectators" ) ) {
3529                                 ServerStartVote( clientNum, voteIndex, "" );
3530                                 ClientStartVote( clientNum, common->GetLanguageDict()->GetString( "#str_04255" ) );
3531                         } else {
3532                                 ServerStartVote( clientNum, voteIndex, "" );
3533                                 ClientStartVote( clientNum, common->GetLanguageDict()->GetString( "#str_04254" ) );
3534                         }
3535                         break;
3536                 default:
3537                         gameLocal.ServerSendChatMessage( clientNum, "server", va( common->GetLanguageDict()->GetString( "#str_04297" ), (int)voteIndex ) );
3538                         common->DPrintf( "client %d: unknown vote index %d\n", clientNum, voteIndex );
3539         }
3540 }
3541
3542 /*
3543 ================
3544 idMultiplayerGame::DisconnectClient
3545 ================
3546 */
3547 void idMultiplayerGame::DisconnectClient( int clientNum ) {
3548         if ( lastWinner == clientNum ) {
3549                 lastWinner = -1;
3550         }
3551         UpdatePlayerRanks();
3552         CheckAbortGame();
3553 }
3554
3555 /*
3556 ================
3557 idMultiplayerGame::CheckAbortGame
3558 ================
3559 */
3560 void idMultiplayerGame::CheckAbortGame( void ) {
3561         int i;
3562         if ( gameLocal.gameType == GAME_TOURNEY && gameState == WARMUP ) {
3563                 // if a tourney player joined spectators, let someone else have his spot
3564                 for ( i = 0; i < 2; i++ ) {
3565                         if ( !gameLocal.entities[ currentTourneyPlayer[ i ] ] || static_cast< idPlayer * >( gameLocal.entities[ currentTourneyPlayer[ i ] ] )->spectating ) {
3566                                 currentTourneyPlayer[ i ] = -1;
3567                         }
3568                 }
3569         }
3570         // only checks for aborts -> game review below
3571         if ( gameState != COUNTDOWN && gameState != GAMEON && gameState != SUDDENDEATH ) {
3572                 return;
3573         }
3574         switch ( gameLocal.gameType ) {
3575                 case GAME_TOURNEY:
3576                         for ( i = 0; i < 2; i++ ) {
3577                                 if ( !gameLocal.entities[ currentTourneyPlayer[ i ] ] || static_cast< idPlayer * >( gameLocal.entities[ currentTourneyPlayer[ i ] ] )->spectating ) {
3578                                         NewState( GAMEREVIEW );
3579                                         return;
3580                                 }
3581                         }
3582                         break;
3583                 default:
3584                         if ( !EnoughClientsToPlay() ) {
3585                                 NewState( GAMEREVIEW );
3586                         }
3587                         break;
3588         }
3589 }
3590
3591 /*
3592 ================
3593 idMultiplayerGame::WantKilled
3594 ================
3595 */
3596 void idMultiplayerGame::WantKilled( int clientNum ) {
3597         idEntity *ent = gameLocal.entities[ clientNum ];
3598         if ( ent && ent->IsType( idPlayer::Type ) ) {
3599                 static_cast<idPlayer *>( ent )->Kill( false, false );
3600         }
3601 }
3602
3603 /*
3604 ================
3605 idMultiplayerGame::MapRestart
3606 ================
3607 */
3608 void idMultiplayerGame::MapRestart( void ) {
3609         int clientNum;
3610
3611         assert( !gameLocal.isClient );
3612         if ( gameState != WARMUP ) {
3613                 NewState( WARMUP );
3614                 nextState = INACTIVE;
3615                 nextStateSwitch = 0;
3616         }
3617
3618 #ifdef CTF
3619         teamPoints[0] = 0;
3620         teamPoints[1] = 0;
3621
3622     ClearHUDStatus();    
3623 #endif
3624
3625 #ifdef CTF
3626         // still balance teams in CTF
3627         if ( g_balanceTDM.GetBool() && lastGameType != GAME_TDM && lastGameType != GAME_CTF && gameLocal.mpGame.IsGametypeTeamBased() ) {
3628 #else
3629         if ( g_balanceTDM.GetBool() && lastGameType != GAME_TDM && gameLocal.gameType == GAME_TDM ) {
3630 #endif  
3631                 for ( clientNum = 0; clientNum < gameLocal.numClients; clientNum++ ) {
3632                         if ( gameLocal.entities[ clientNum ] && gameLocal.entities[ clientNum ]->IsType( idPlayer::Type ) ) {
3633                                 if ( static_cast< idPlayer* >( gameLocal.entities[ clientNum ] )->BalanceTDM() ) {
3634                                         // core is in charge of syncing down userinfo changes
3635                                         // it will also call back game through SetUserInfo with the current info for update
3636                                         cmdSystem->BufferCommandText( CMD_EXEC_NOW, va( "updateUI %d\n", clientNum ) );
3637                                 }
3638                         }
3639                 }
3640         }
3641         lastGameType = gameLocal.gameType;
3642 }
3643
3644 /*
3645 ================
3646 idMultiplayerGame::SwitchToTeam
3647 ================
3648 */
3649 void idMultiplayerGame::SwitchToTeam( int clientNum, int oldteam, int newteam ) {
3650         idEntity *ent;
3651         int i;
3652
3653         assert( IsGametypeTeamBased() ); /* CTF */
3654         assert( oldteam != newteam );
3655         assert( !gameLocal.isClient );
3656
3657         if ( !gameLocal.isClient && newteam >= 0 && IsInGame( clientNum ) ) {
3658                 PrintMessageEvent( -1, MSG_JOINTEAM, clientNum, newteam );
3659         }
3660         // assign the right teamFragCount
3661         for( i = 0; i < gameLocal.numClients; i++ ) {
3662                 if ( i == clientNum ) {
3663                         continue;
3664                 }
3665                 ent = gameLocal.entities[ i ]; 
3666                 if ( ent && ent->IsType( idPlayer::Type ) && static_cast< idPlayer * >(ent)->team == newteam ) {
3667                         playerState[ clientNum ].teamFragCount = playerState[ i ].teamFragCount;
3668                         break;
3669                 }       
3670         }
3671         if ( i == gameLocal.numClients ) {
3672                 // alone on this team
3673                 playerState[ clientNum ].teamFragCount = 0;
3674         
3675         }
3676 #ifdef CTF
3677         if ( ( gameState == GAMEON || ( IsGametypeFlagBased() && gameState == SUDDENDEATH ) ) && oldteam != -1 ) {
3678 #else
3679         if ( gameState == GAMEON && oldteam != -1 ) {
3680 #endif
3681                 // when changing teams during game, kill and respawn
3682                 idPlayer *p = static_cast<idPlayer *>( gameLocal.entities[ clientNum ] );
3683                 if ( p->IsInTeleport() ) {
3684                         p->ServerSendEvent( idPlayer::EVENT_ABORT_TELEPORTER, NULL, false, -1 );
3685                         p->SetPrivateCameraView( NULL );
3686                 }
3687                 p->Kill( true, true );
3688 #ifdef CTF
3689         if ( IsGametypeFlagBased() )
3690             p->DropFlag();
3691 #endif        
3692                 CheckAbortGame();
3693         }
3694 #ifdef CTF
3695         else if ( IsGametypeFlagBased() && oldteam != -1 ) {
3696                 idPlayer *p = static_cast<idPlayer *>( gameLocal.entities[ clientNum ] );
3697                 p->DropFlag();
3698         }
3699 #endif
3700 }
3701
3702 /*
3703 ================
3704 idMultiplayerGame::ProcessChatMessage
3705 ================
3706 */
3707 void idMultiplayerGame::ProcessChatMessage( int clientNum, bool team, const char *name, const char *text, const char *sound ) {
3708         idBitMsg        outMsg;
3709         byte            msgBuf[ 256 ];
3710         const char *prefix = NULL;
3711         int                     send_to; // 0 - all, 1 - specs, 2 - team
3712         int                     i;
3713         idEntity        *ent;
3714         idPlayer        *p;
3715         idStr           prefixed_name;
3716
3717         assert( !gameLocal.isClient );
3718
3719         if ( clientNum >= 0 ) {
3720                 p = static_cast< idPlayer * >( gameLocal.entities[ clientNum ] );
3721                 if ( !( p && p->IsType( idPlayer::Type ) ) ) {
3722                         return;
3723                 }
3724
3725                 if ( p->spectating ) {
3726                         prefix = "spectating";
3727                         if ( team || ( !g_spectatorChat.GetBool() && ( gameState == GAMEON || gameState == SUDDENDEATH ) ) ) {
3728                                 // to specs
3729                                 send_to = 1;
3730                         } else {
3731                                 // to all
3732                                 send_to = 0;
3733                         }
3734                 } else if ( team ) {
3735                         prefix = "team";
3736                         // to team
3737                         send_to = 2;
3738                 } else {
3739                         // to all
3740                         send_to = 0;
3741                 }
3742         } else {
3743                 p = NULL;
3744                 send_to = 0;
3745         }
3746         // put the message together
3747         outMsg.Init( msgBuf, sizeof( msgBuf ) );
3748         outMsg.WriteByte( GAME_RELIABLE_MESSAGE_CHAT );
3749         if ( prefix ) {
3750                 prefixed_name = va( "(%s) %s", prefix, name );
3751         } else {
3752                 prefixed_name = name;
3753         }
3754         outMsg.WriteString( prefixed_name );
3755         outMsg.WriteString( text, -1, false );
3756         if ( !send_to ) {
3757                 AddChatLine( "%s^0: %s\n", prefixed_name.c_str(), text );
3758                 networkSystem->ServerSendReliableMessage( -1, outMsg );
3759                 if ( sound ) {
3760                         PlayGlobalSound( -1, SND_COUNT, sound );
3761                 }
3762         } else {
3763                 for ( i = 0; i < gameLocal.numClients; i++ ) {
3764                         ent = gameLocal.entities[ i ]; 
3765                         if ( !ent || !ent->IsType( idPlayer::Type ) ) {
3766                                 continue;
3767                         }
3768                         if ( send_to == 1 && static_cast< idPlayer * >( ent )->spectating ) {
3769                                 if ( sound ) {
3770                                         PlayGlobalSound( i, SND_COUNT, sound );
3771                                 }
3772                                 if ( i == gameLocal.localClientNum ) {
3773                                         AddChatLine( "%s^0: %s\n", prefixed_name.c_str(), text );
3774                                 } else {
3775                                         networkSystem->ServerSendReliableMessage( i, outMsg );
3776                                 }
3777                         } else if ( send_to == 2 && static_cast< idPlayer * >( ent )->team == p->team ) {
3778                                 if ( sound ) {
3779                                         PlayGlobalSound( i, SND_COUNT, sound );
3780                                 }
3781                                 if ( i == gameLocal.localClientNum ) {
3782                                         AddChatLine( "%s^0: %s\n", prefixed_name.c_str(), text );
3783                                 } else {
3784                                         networkSystem->ServerSendReliableMessage( i, outMsg );
3785                                 }
3786                         }
3787                 }
3788         }
3789 }
3790
3791 /*
3792 ================
3793 idMultiplayerGame::Precache
3794 ================
3795 */
3796 void idMultiplayerGame::Precache( void ) {
3797         int                     i;
3798         idFile          *f;
3799
3800         if ( !gameLocal.isMultiplayer ) {
3801                 return;
3802         }
3803         gameLocal.FindEntityDefDict( "player_doommarine", false );;
3804         
3805         // skins
3806         idStr str = cvarSystem->GetCVarString( "mod_validSkins" );
3807         idStr skin;
3808         while ( str.Length() ) {
3809                 int n = str.Find( ";" );
3810                 if ( n >= 0 ) {
3811                         skin = str.Left( n );
3812                         str = str.Right( str.Length() - n - 1 );
3813                 } else {
3814                         skin = str;
3815                         str = "";
3816                 }
3817                 declManager->FindSkin( skin, false );
3818         }
3819
3820         for ( i = 0; ui_skinArgs[ i ]; i++ ) {
3821                 declManager->FindSkin( ui_skinArgs[ i ], false );
3822         }
3823         // MP game sounds
3824         for ( i = 0; i < SND_COUNT; i++ ) {
3825                 f = fileSystem->OpenFileRead( GlobalSoundStrings[ i ] );
3826                 fileSystem->CloseFile( f );
3827         }
3828         // MP guis. just make sure we hit all of them
3829         i = 0;
3830         while ( MPGuis[ i ] ) {
3831                 uiManager->FindGui( MPGuis[ i ], true );
3832                 i++;
3833         }
3834 }
3835
3836 /*
3837 ================
3838 idMultiplayerGame::ToggleSpectate
3839 ================
3840 */
3841 void idMultiplayerGame::ToggleSpectate( void ) {
3842         bool spectating;
3843         assert( gameLocal.isClient || gameLocal.localClientNum == 0 );
3844
3845         spectating = ( idStr::Icmp( cvarSystem->GetCVarString( "ui_spectate" ), "Spectate" ) == 0 );
3846         if ( spectating ) {
3847                 // always allow toggling to play
3848                 cvarSystem->SetCVarString( "ui_spectate", "Play" );
3849         } else {
3850                 // only allow toggling to spectate if spectators are enabled.
3851                 if ( gameLocal.serverInfo.GetBool( "si_spectators" ) ) {
3852                         cvarSystem->SetCVarString( "ui_spectate", "Spectate" );
3853                 } else {
3854                         gameLocal.mpGame.AddChatLine( common->GetLanguageDict()->GetString( "#str_06747" ) );
3855                 }
3856         }
3857 }
3858
3859 /*
3860 ================
3861 idMultiplayerGame::ToggleReady
3862 ================
3863 */
3864 void idMultiplayerGame::ToggleReady( void ) {
3865         bool ready;
3866         assert( gameLocal.isClient || gameLocal.localClientNum == 0 );
3867
3868         ready = ( idStr::Icmp( cvarSystem->GetCVarString( "ui_ready" ), "Ready" ) == 0 );
3869         if ( ready ) {
3870                 cvarSystem->SetCVarString( "ui_ready", "Not Ready" );
3871         } else {
3872                 cvarSystem->SetCVarString( "ui_ready", "Ready" );
3873         }
3874 }
3875
3876 /*
3877 ================
3878 idMultiplayerGame::ToggleTeam
3879 ================
3880 */
3881 void idMultiplayerGame::ToggleTeam( void ) {
3882         bool team;
3883         assert( gameLocal.isClient || gameLocal.localClientNum == 0 );
3884         
3885         team = ( idStr::Icmp( cvarSystem->GetCVarString( "ui_team" ), "Red" ) == 0 );
3886         if ( team ) {
3887                 cvarSystem->SetCVarString( "ui_team", "Blue" );
3888         } else {
3889                 cvarSystem->SetCVarString( "ui_team", "Red" );
3890         }
3891 }
3892
3893 /*
3894 ================
3895 idMultiplayerGame::ToggleUserInfo
3896 ================
3897 */
3898 void idMultiplayerGame::ThrottleUserInfo( void ) {
3899         int i;
3900
3901         assert( gameLocal.localClientNum >= 0 );
3902
3903         i = 0;
3904         while ( ThrottleVars[ i ] ) {
3905                 if ( idStr::Icmp( gameLocal.userInfo[ gameLocal.localClientNum ].GetString( ThrottleVars[ i ] ),
3906                         cvarSystem->GetCVarString( ThrottleVars[ i ] ) ) ) {
3907                         if ( gameLocal.realClientTime < switchThrottle[ i ] ) {
3908                                 AddChatLine( common->GetLanguageDict()->GetString( "#str_04299" ), common->GetLanguageDict()->GetString( ThrottleVarsInEnglish[ i ] ), ( switchThrottle[ i ] - gameLocal.time ) / 1000 + 1 );
3909                                 cvarSystem->SetCVarString( ThrottleVars[ i ], gameLocal.userInfo[ gameLocal.localClientNum ].GetString( ThrottleVars[ i ] ) );
3910                         } else {
3911                                 switchThrottle[ i ] = gameLocal.time + ThrottleDelay[ i ] * 1000;
3912                         }
3913                 }
3914                 i++;
3915         }
3916 }
3917
3918 /*
3919 ================
3920 idMultiplayerGame::CanPlay
3921 ================
3922 */
3923 bool idMultiplayerGame::CanPlay( idPlayer *p ) {
3924         return !p->wantSpectate && playerState[ p->entityNumber ].ingame;
3925 }
3926
3927 /*
3928 ================
3929 idMultiplayerGame::EnterGame
3930 ================
3931 */
3932 void idMultiplayerGame::EnterGame( int clientNum ) {
3933         assert( !gameLocal.isClient );
3934
3935         if ( !playerState[ clientNum ].ingame ) {
3936                 playerState[ clientNum ].ingame = true;
3937                 if ( gameLocal.isMultiplayer ) {
3938                         // can't use PrintMessageEvent as clients don't know the nickname yet
3939                         gameLocal.ServerSendChatMessage( -1, common->GetLanguageDict()->GetString( "#str_02047" ), va( common->GetLanguageDict()->GetString( "#str_07177" ), gameLocal.userInfo[ clientNum ].GetString( "ui_name" ) ) );
3940                 }
3941         }
3942 }
3943
3944 /*
3945 ================
3946 idMultiplayerGame::WantRespawn
3947 ================
3948 */
3949 bool idMultiplayerGame::WantRespawn( idPlayer *p ) {
3950         return p->forceRespawn && !p->wantSpectate && playerState[ p->entityNumber ].ingame;
3951 }
3952
3953 /*
3954 ================
3955 idMultiplayerGame::VoiceChat
3956 ================
3957 */
3958 void idMultiplayerGame::VoiceChat_f( const idCmdArgs &args ) {
3959         gameLocal.mpGame.VoiceChat( args, false );
3960 }
3961
3962 /*
3963 ================
3964 idMultiplayerGame::VoiceChatTeam
3965 ================
3966 */
3967 void idMultiplayerGame::VoiceChatTeam_f( const idCmdArgs &args ) {
3968         gameLocal.mpGame.VoiceChat( args, true );
3969 }
3970
3971 /*
3972 ================
3973 idMultiplayerGame::VoiceChat
3974 ================
3975 */
3976 void idMultiplayerGame::VoiceChat( const idCmdArgs &args, bool team ) {
3977         idBitMsg                        outMsg;
3978         byte                            msgBuf[128];
3979         const char                      *voc;
3980         const idDict            *spawnArgs;
3981         const idKeyValue        *keyval;
3982         int                                     index;
3983
3984         if ( !gameLocal.isMultiplayer ) {
3985                 common->Printf( "clientVoiceChat: only valid in multiplayer\n" );
3986                 return;
3987         }
3988         if ( args.Argc() != 2 ) {
3989                 common->Printf( "clientVoiceChat: bad args\n" );
3990                 return;
3991         }
3992         // throttle
3993         if ( gameLocal.realClientTime < voiceChatThrottle ) {
3994                 return;
3995         }
3996
3997         voc = args.Argv( 1 );
3998         spawnArgs = gameLocal.FindEntityDefDict( "player_doommarine", false );
3999         keyval = spawnArgs->MatchPrefix( "snd_voc_", NULL );
4000         index = 0;
4001         while ( keyval ) {
4002                 if ( !keyval->GetValue().Icmp( voc ) ) {
4003                         break;
4004                 }
4005                 keyval = spawnArgs->MatchPrefix( "snd_voc_", keyval );
4006                 index++;
4007         }
4008         if ( !keyval ) {
4009                 common->Printf( "Voice command not found: %s\n", voc );
4010                 return;
4011         }
4012         voiceChatThrottle = gameLocal.realClientTime + 1000;
4013
4014         outMsg.Init( msgBuf, sizeof( msgBuf ) );
4015         outMsg.WriteByte( GAME_RELIABLE_MESSAGE_VCHAT );
4016         outMsg.WriteLong( index );
4017         outMsg.WriteBits( team ? 1 : 0, 1 );
4018         networkSystem->ClientSendReliableMessage( outMsg );
4019 }
4020
4021 /*
4022 ================
4023 idMultiplayerGame::ProcessVoiceChat
4024 ================
4025 */
4026 void idMultiplayerGame::ProcessVoiceChat( int clientNum, bool team, int index ) {
4027         const idDict            *spawnArgs;
4028         const idKeyValue        *keyval;
4029         idStr                           name;
4030         idStr                           snd_key;
4031         idStr                           text_key;
4032         idPlayer                        *p;
4033
4034         p = static_cast< idPlayer * >( gameLocal.entities[ clientNum ] );
4035         if ( !( p && p->IsType( idPlayer::Type ) ) ) {
4036                 return;
4037         }
4038
4039         if ( p->spectating ) {
4040                 return;
4041         }
4042
4043         // lookup the sound def
4044         spawnArgs = gameLocal.FindEntityDefDict( "player_doommarine", false );
4045         keyval = spawnArgs->MatchPrefix( "snd_voc_", NULL );
4046         while ( index > 0 && keyval ) {
4047                 keyval = spawnArgs->MatchPrefix( "snd_voc_", keyval );
4048                 index--;
4049         }
4050         if ( !keyval ) {
4051                 common->DPrintf( "ProcessVoiceChat: unknown chat index %d\n", index );
4052                 return;
4053         }
4054         snd_key = keyval->GetKey();
4055         name = gameLocal.userInfo[ clientNum ].GetString( "ui_name" );
4056         sprintf( text_key, "txt_%s", snd_key.Right( snd_key.Length() - 4 ).c_str() );
4057         if ( team || gameState == COUNTDOWN || gameState == GAMEREVIEW ) {
4058                 ProcessChatMessage( clientNum, team, name, spawnArgs->GetString( text_key ), spawnArgs->GetString( snd_key ) );
4059         } else {
4060                 p->StartSound( snd_key, SND_CHANNEL_ANY, 0, true, NULL );
4061                 ProcessChatMessage( clientNum, team, name, spawnArgs->GetString( text_key ), NULL );
4062         }
4063 }
4064
4065 /*
4066 ================
4067 idMultiplayerGame::ServerWriteInitialReliableMessages
4068 ================
4069 */
4070 void idMultiplayerGame::ServerWriteInitialReliableMessages( int clientNum ) {
4071         idBitMsg        outMsg;
4072         byte            msgBuf[ MAX_GAME_MESSAGE_SIZE ];
4073         int                     i;
4074         idEntity        *ent;
4075
4076         outMsg.Init( msgBuf, sizeof( msgBuf ) );
4077         outMsg.BeginWriting();
4078         outMsg.WriteByte( GAME_RELIABLE_MESSAGE_STARTSTATE );
4079         // send the game state and start time
4080         outMsg.WriteByte( gameState );
4081         outMsg.WriteLong( matchStartedTime );
4082         outMsg.WriteShort( startFragLimit );
4083         // send the powerup states and the spectate states
4084         for( i = 0; i < gameLocal.numClients; i++ ) {
4085                 ent = gameLocal.entities[ i ]; 
4086                 if ( i != clientNum && ent && ent->IsType( idPlayer::Type ) ) {
4087                         outMsg.WriteShort( i );
4088                         outMsg.WriteShort( static_cast< idPlayer * >( ent )->inventory.powerups );
4089                         outMsg.WriteBits( static_cast< idPlayer * >( ent )->spectating, 1 );
4090                 }
4091         }
4092         outMsg.WriteShort( MAX_CLIENTS );
4093         networkSystem->ServerSendReliableMessage( clientNum, outMsg );
4094
4095         // we send SI in connectResponse messages, but it may have been modified already
4096         outMsg.BeginWriting( );
4097         outMsg.WriteByte( GAME_RELIABLE_MESSAGE_SERVERINFO );
4098         outMsg.WriteDeltaDict( gameLocal.serverInfo, NULL );
4099         networkSystem->ServerSendReliableMessage( clientNum, outMsg );
4100
4101         // warmup time
4102         if ( gameState == COUNTDOWN ) {
4103                 outMsg.BeginWriting();
4104                 outMsg.WriteByte( GAME_RELIABLE_MESSAGE_WARMUPTIME );
4105                 outMsg.WriteLong( warmupEndTime );
4106                 networkSystem->ServerSendReliableMessage( clientNum, outMsg );
4107         }
4108 }
4109
4110 /*
4111 ================
4112 idMultiplayerGame::ClientReadStartState
4113 ================
4114 */
4115 void idMultiplayerGame::ClientReadStartState( const idBitMsg &msg ) {
4116         int i, client, powerup;
4117
4118         // read the state in preparation for reading snapshot updates
4119         gameState = (idMultiplayerGame::gameState_t)msg.ReadByte();
4120         matchStartedTime = msg.ReadLong( );
4121         startFragLimit = msg.ReadShort( );
4122         while ( ( client = msg.ReadShort() ) != MAX_CLIENTS ) {
4123                 assert( gameLocal.entities[ client ] && gameLocal.entities[ client ]->IsType( idPlayer::Type ) );
4124                 powerup = msg.ReadShort();
4125                 for ( i = 0; i < MAX_POWERUPS; i++ ) {
4126                         if ( powerup & ( 1 << i ) ) {
4127                                 static_cast< idPlayer * >( gameLocal.entities[ client ] )->GivePowerUp( i, 0 );
4128                         }
4129                 }
4130                 bool spectate = ( msg.ReadBits( 1 ) != 0 );
4131                 static_cast< idPlayer * >( gameLocal.entities[ client ] )->Spectate( spectate );
4132         }
4133 }
4134
4135 /*
4136 ================
4137 idMultiplayerGame::ClientReadWarmupTime
4138 ================
4139 */
4140 void idMultiplayerGame::ClientReadWarmupTime( const idBitMsg &msg ) {
4141         warmupEndTime = msg.ReadLong();
4142 }
4143
4144 /*
4145 #ifdef CTF
4146
4147     Threewave note:
4148         The below IsGametype...() functions were implemented for CTF,
4149         but we did not #ifdef CTF them, because doing so would clutter
4150         the codebase substantially.  Please consider them part of the merged
4151         CTF code.
4152 */
4153
4154 /*
4155 ================
4156 idMultiplayerGame::IsGametypeTeamBased
4157 ================
4158 */
4159 bool idMultiplayerGame::IsGametypeTeamBased( void ) /* CTF */
4160 {
4161     switch ( gameLocal.gameType )
4162     {
4163     case GAME_SP:
4164     case GAME_DM:
4165     case GAME_TOURNEY:
4166     case GAME_LASTMAN:
4167         return false;
4168 #ifdef CTF
4169     case GAME_CTF:
4170 #endif
4171     case GAME_TDM:
4172         return true;
4173
4174     default:
4175         assert( !"Add support for your new gametype here." );
4176     }
4177
4178     return false;
4179 }
4180
4181 /*
4182 ================
4183 idMultiplayerGame::IsGametypeFlagBased 
4184 ================
4185 */
4186 bool idMultiplayerGame::IsGametypeFlagBased( void )  {
4187     switch ( gameLocal.gameType )
4188     {
4189     case GAME_SP:
4190     case GAME_DM:
4191     case GAME_TOURNEY:
4192     case GAME_LASTMAN:
4193     case GAME_TDM:        
4194         return false;
4195
4196 #ifdef CTF
4197     case GAME_CTF:
4198         return true;
4199 #endif
4200
4201     default:
4202         assert( !"Add support for your new gametype here." );
4203     }
4204
4205     return false;
4206     
4207 }
4208 #ifdef CTF
4209
4210 /*
4211 ================
4212 idMultiplayerGame::GetTeamFlag
4213 ================
4214 */
4215 idItemTeam * idMultiplayerGame::GetTeamFlag( int team ) {
4216     assert( team == 0 || team == 1 );
4217     
4218         if ( !IsGametypeFlagBased() || ( team != 0 && team != 1 ) ) /* CTF */
4219                 return NULL;
4220
4221         // TODO : just call on map start
4222         FindTeamFlags();
4223
4224         return teamFlags[team];
4225 }
4226
4227 /*
4228 ================
4229 idMultiplayerGame::GetTeamFlag
4230 ================
4231 */
4232 void idMultiplayerGame::FindTeamFlags( void ) {
4233         char * flagDefs[2] =
4234         {
4235                 "team_CTF_redflag",
4236                 "team_CTF_blueflag"
4237         };
4238
4239         for ( int i = 0; i < 2; i++)
4240         {
4241                 idEntity * entity = gameLocal.FindEntityUsingDef( NULL, flagDefs[i] );
4242                 do
4243                 {
4244                         if ( entity == NULL )
4245                                 return;
4246
4247                         idItemTeam * flag = static_cast<idItemTeam *>(entity);
4248
4249                         if ( flag->team == i )
4250                         {
4251                                 teamFlags[i] = flag;
4252                                 break;
4253                         }
4254
4255                         entity = gameLocal.FindEntityUsingDef( entity, flagDefs[i] );
4256                 } while( entity );
4257         }
4258 }
4259
4260 /*
4261 ================
4262 idMultiplayerGame::GetFlagStatus
4263 ================
4264 */
4265 flagStatus_t idMultiplayerGame::GetFlagStatus( int team ) {
4266     //assert( IsGametypeFlagBased() );
4267
4268     idItemTeam *teamFlag = GetTeamFlag( team );
4269     //assert( teamFlag != NULL );
4270
4271         if ( teamFlag != NULL ) {
4272                 if ( teamFlag->carried == false && teamFlag->dropped == false )
4273                 return FLAGSTATUS_INBASE;
4274
4275             if ( teamFlag->carried == true )
4276                         return FLAGSTATUS_TAKEN;
4277
4278                 if ( teamFlag->carried == false && teamFlag->dropped == true )
4279                 return FLAGSTATUS_STRAY;
4280         }
4281
4282     //assert( !"Invalid flag state." );
4283     return FLAGSTATUS_NONE;
4284 }
4285
4286 /*
4287 ================
4288 idMultiplayerGame::SetFlagMsgs
4289 ================
4290 */
4291 void idMultiplayerGame::SetFlagMsg( bool b ) {
4292         flagMsgOn = b;
4293 }
4294
4295 /*
4296 ================
4297 idMultiplayerGame::IsFlagMsgOn
4298 ================
4299 */
4300 bool idMultiplayerGame::IsFlagMsgOn( void ) {
4301         return ( GetGameState() == WARMUP || GetGameState() == GAMEON || GetGameState() == SUDDENDEATH ) && flagMsgOn;
4302 }
4303
4304
4305 /*
4306 ================
4307 idMultiplayerGame::SetBestGametype
4308 ================
4309 */
4310 void idMultiplayerGame::SetBestGametype( const char * map ) {
4311         const char *gametype = gameLocal.serverInfo.GetString( "si_gameType" );
4312         //      const char *map = gameLocal.serverInfo.GetString( "si_map" );
4313         int num = declManager->GetNumDecls( DECL_MAPDEF );
4314         int i, j;
4315
4316         for ( i = 0; i < num; i++ ) {
4317                 const idDeclEntityDef *mapDef = static_cast<const idDeclEntityDef *>( declManager->DeclByIndex( DECL_MAPDEF, i ) );
4318
4319                 if ( mapDef && idStr::Icmp( mapDef->GetName(), map ) == 0 ) {
4320                         if ( mapDef->dict.GetBool( gametype ) ) {
4321                                 // dont change gametype
4322                                 return;
4323                         }
4324
4325                         for ( j = 1; si_gameTypeArgs[ j ]; j++ ) {
4326                                 if ( mapDef->dict.GetBool( si_gameTypeArgs[ j ] ) ) {
4327                                         si_gameType.SetString( si_gameTypeArgs[ j ] );
4328                                         return;
4329                                 }
4330                         }
4331
4332                         // error out, no valid gametype
4333                         return;
4334                 }
4335         }
4336 }
4337
4338 /*
4339 ================
4340 idMultiplayerGame::ReloadScoreboard
4341 ================
4342 */
4343 void idMultiplayerGame::ReloadScoreboard() {
4344         // CTF uses its own scoreboard
4345         if ( IsGametypeFlagBased() )
4346                 scoreBoard = uiManager->FindGui( "guis/ctfscoreboard.gui", true, false, true );
4347         else
4348                 scoreBoard = uiManager->FindGui( "guis/scoreboard.gui", true, false, true );
4349
4350         Precache();
4351 }
4352
4353
4354 #endif
4355
4356 #ifdef _D3XP
4357 idStr idMultiplayerGame::GetBestGametype( const char* map, const char* gametype ) {
4358         
4359         int num = declManager->GetNumDecls( DECL_MAPDEF );
4360         int i, j;
4361
4362         for ( i = 0; i < num; i++ ) {
4363                 const idDeclEntityDef *mapDef = static_cast<const idDeclEntityDef *>( declManager->DeclByIndex( DECL_MAPDEF, i ) );
4364
4365                 if ( mapDef && idStr::Icmp( mapDef->GetName(), map ) == 0 ) {
4366                         if ( mapDef->dict.GetBool( gametype ) ) {
4367                                 // dont change gametype
4368                                 return gametype;
4369                         }
4370
4371                         for ( j = 1; si_gameTypeArgs[ j ]; j++ ) {
4372                                 if ( mapDef->dict.GetBool( si_gameTypeArgs[ j ] ) ) {
4373                                         return si_gameTypeArgs[ j ];
4374                                 }
4375                         }
4376
4377                         // error out, no valid gametype
4378                         return "deathmatch";
4379                 }
4380         }
4381
4382         //For testing a new map let it play any gametpye
4383         return gametype;
4384 }
4385 #endif