doom3-gpl
Doom 3 GPL source release
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
MultiplayerGame.cpp
Go to the documentation of this file.
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
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
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 
90  "#str_06738",
91  "#str_06737",
92  "#str_01991",
93  NULL
94 };
95 
97  8,
98  5,
99  5
100 };
101 
102 /*
103 ================
104 idMultiplayerGame::idMultiplayerGame
105 ================
106 */
108  scoreBoard = NULL;
109  spectateGui = NULL;
110  guiChat = NULL;
111  mainGui = NULL;
112  mapList = NULL;
113  msgmodeGui = NULL;
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 */
138  Clear();
139 }
140 
141 /*
142 ================
143 idMultiplayerGame::SetMenuSkin
144 ================
145 */
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 */
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 );
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 );
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 */
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 ] );
230 
231  if ( IsGametypeTeamBased() ) { /* CTF */
232  SwitchToTeam( clientNum, -1, p->team );
233  }
234  p->tourneyRank = 0;
236  p->tourneyRank++;
237  }
238  playerState[ clientNum ].ingame = ingame;
239  }
240 }
241 
242 /*
243 ================
244 idMultiplayerGame::Clear
245 ================
246 */
248  int i;
249 
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 ) {
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 */
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 */
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  }
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 */
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 {
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 */
826  static char buff[16];
827  int m, s, t, ms;
828 
829  if ( gameState == COUNTDOWN ) {
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 {
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 */
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 */
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 */
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 */
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 */
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  }
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  }
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 */
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++;
1166  } else {
1168  }
1169  } else if ( gameLocal.gameType == GAME_LASTMAN ) {
1170  if ( player == winner ) {
1171  playerState[ i ].wins++;
1173  } else if ( !player->wantSpectate ) {
1175  }
1176  } else if ( gameLocal.gameType == GAME_TOURNEY ) {
1177  if ( player == winner ) {
1178  playerState[ i ].wins++;
1180  } else if ( i == currentTourneyPlayer[ 0 ] || i == currentTourneyPlayer[ 1 ] ) {
1182  }
1183  } else {
1184  if ( player == winner ) {
1185  playerState[i].wins++;
1187  } else if ( !player->wantSpectate ) {
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 ) {
1207  } else {
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
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 ) {
1328  } else if ( killer ) {
1329  if ( telefrag ) {
1331  } else if ( IsGametypeTeamBased() && dead->team == killer->team ) { /* CTF */
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 */
1379  playerState[ clientNum ].vote = vote;
1380 }
1381 
1382 /*
1383 ================
1384 idMultiplayerGame::DumpTourneyLine
1385 ================
1386 */
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 */
1402  idBitMsg outMsg;
1403  byte msgBuf[MAX_GAME_MESSAGE_SIZE];
1404  int i;
1405 
1406  assert( news != gameState );
1408  gameLocal.DPrintf( "%s -> %s\n", GameStateStrings[ gameState ], GameStateStrings[ news ] );
1409  switch( news ) {
1410  case GAMEON: {
1412  outMsg.Init( msgBuf, sizeof( msgBuf ) );
1414  outMsg.WriteBits( 0, 1 );
1416 
1417 #ifdef CTF
1418  teamPoints[0] = 0;
1419  teamPoints[1] = 0;
1420 
1421  ClearHUDStatus();
1422 #endif
1423 
1424  PlayGlobalSound( -1, SND_FIGHT );
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: {
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 ) );
1493  outMsg.WriteLong( warmupEndTime );
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 */
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 */
1577  int i, j, imax, max, globalmax = -1;
1578  idPlayer *p;
1579 
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 ) );
1608  outMsg.WriteByte( j );
1609  networkSystem->ServerSendReliableMessage( imax, outMsg );
1610 
1611  globalmax = max;
1612  }
1613 }
1614 
1615 /*
1616 ================
1617 idMultiplayerGame::CycleTourneyPlayers
1618 ================
1619 */
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 ) {
1630  if ( ent && ent->IsType( idPlayer::Type ) ) {
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  }
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 */
1659  bool needRestart;
1660  switch ( vote ) {
1661  case VOTE_RESTART:
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 ) {
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 ) {
1681  }
1682 #endif
1683  break;
1684  case VOTE_GAMETYPE:
1687  break;
1688  case VOTE_KICK:
1690  break;
1691  case VOTE_MAP:
1694  break;
1695  case VOTE_SPECTATORS:
1697 #ifdef _D3XP
1698  needRestart = gameLocal.NeedRestart();
1699  cmdSystem->BufferCommandText( CMD_EXEC_NOW, "rescanSI" );
1700  if ( needRestart ) {
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 */
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;
1748  return;
1749  }
1750  if ( yesVotes / numVoters > 0.5f ) {
1752  voteExecTime = gameLocal.time + 2000;
1753  return;
1754  }
1755  if ( gameLocal.time > voteTimeOut || noVotes / numVoters >= 0.5f ) {
1757  vote = VOTE_NONE;
1758  return;
1759  }
1760 }
1761 
1762 /*
1763 ================
1764 idMultiplayerGame::Warmup
1765 ================
1766 */
1768  return ( gameState == WARMUP );
1769 }
1770 
1771 /*
1772 ================
1773 idMultiplayerGame::Run
1774 ================
1775 */
1777  int i, timeLeft;
1778  idPlayer *player;
1779  int gameReviewPause;
1780 
1783 
1784  pureReady = true;
1785 
1786  if ( gameState == INACTIVE ) {
1788  NewState( WARMUP );
1789  }
1790 
1791  CheckVote();
1792 
1793  CheckRespawns();
1794 
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++ ) {
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 ) {
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 );
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 );
1910  }
1911  if ( gameLocal.time > fragLimitTimeout ) {
1912  NewState( GAMEREVIEW, player );
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();
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 );
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 );
1953  }
1954  if ( gameLocal.time > fragLimitTimeout ) {
1955  NewState( GAMEREVIEW, player );
1957  }
1958  } else if ( fragLimitTimeout ) {
1959  SuddenRespawn();
1961  fragLimitTimeout = 0;
1962  }
1963  break;
1964  }
1965  }
1966 }
1967 
1968 /*
1969 ================
1970 idMultiplayerGame::UpdateMainGui
1971 ================
1972 */
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() ) {
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  }
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 */
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 ) {
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?
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 */
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 */
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" ) ) {
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" ) ) {
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() ) );
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() ) );
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;
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 
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 ) );
2423  outMsg.WriteBits( 1, 1 );
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();
2438  } else {
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 ) );
2450  outMsg.WriteBits( 0, 1 );
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  }
2497  }
2498  DrawChat();
2499  DrawScoreBoard( player );
2500  }
2501 
2502  return true;
2503 }
2504 
2505 /*
2506 ================
2507 idMultiplayerGame::UpdateHud
2508 ================
2509 */
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 */
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 */
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 
2623 
2624  chatHistoryIndex++;
2625  if ( chatHistorySize < NUM_CHAT_NOTIFY ) {
2626  chatHistorySize++;
2627  }
2628  chatDataUpdated = true;
2630 }
2631 
2632 /*
2633 ===============
2634 idMultiplayerGame::DrawChat
2635 ===============
2636 */
2638  int i, j;
2639  if ( guiChat ) {
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  }
2651  }
2652  if ( chatDataUpdated ) {
2653  j = 0;
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  }
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
2680 
2681 /*
2682 ================
2683 idMultiplayerGame::WriteToSnapshot
2684 ================
2685 */
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
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 */
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 ) {
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 ) {
2768  }
2769  } else {
2770  if ( gameSoundWorld ) {
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  }
2787  outMsg.WriteLong( gameLocal.ServerRemapDecl( to, DECL_SOUND, shaderDecl->Index() ) );
2788  } else {
2790  outMsg.WriteByte( evt );
2791  }
2792 
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 ) );
2951  outMsg.WriteByte( evt );
2952  outMsg.WriteByte( parm1 );
2953  outMsg.WriteByte( parm2 );
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 */
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 */
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 ) {
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?
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 )
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 );
3092  CheckAbortGame();
3093  }
3094  }
3095 }
3096 
3097 /*
3098 ================
3099 idMultiplayerGame::ForceReady
3100 ================
3101 */
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() ) {
3112  p->forcedReady = true;
3113  }
3114  }
3115 }
3116 
3117 /*
3118 ================
3119 idMultiplayerGame::ForceReady_f
3120 ================
3121 */
3124  common->Printf( "forceReady: multiplayer server only\n" );
3125  return;
3126  }
3128 }
3129 
3130 /*
3131 ================
3132 idMultiplayerGame::DropWeapon
3133 ================
3134 */
3135 void idMultiplayerGame::DropWeapon( int clientNum ) {
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 */
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 ) );
3159 }
3160 
3161 /*
3162 ================
3163 idMultiplayerGame::MessageMode_f
3164 ================
3165 */
3167  gameLocal.mpGame.MessageMode( args );
3168 }
3169 
3170 /*
3171 ================
3172 idMultiplayerGame::MessageMode
3173 ================
3174 */
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 */
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 {
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 ) );
3254  outMsg.WriteByte( clientNum );
3255  outMsg.WriteString( _voteString );
3257  }
3258 
3259  voteString = _voteString;
3260  AddChatLine( va( common->GetLanguageDict()->GetString( "#str_04279" ), gameLocal.userInfo[ clientNum ].GetString( "ui_name" ) ) );
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 ) );
3287  outMsg.WriteByte( status );
3288  outMsg.WriteByte( yesCount );
3289  outMsg.WriteByte( noCount );
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" ) );
3302  if ( gameLocal.isClient ) {
3303  vote = VOTE_NONE;
3304  }
3305  break;
3306  case VOTE_PASSED:
3307  AddChatLine( common->GetLanguageDict()->GetString( "#str_04277" ) );
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 */
3336  idBitMsg outMsg;
3337  byte msgBuf[ MAX_GAME_MESSAGE_SIZE ];
3338 
3339  // send
3340  outMsg.Init( msgBuf, sizeof( msgBuf ) );
3342  outMsg.WriteByte( voteIndex );
3343  outMsg.WriteString( voteValue );
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 ) );
3363  outMsg.WriteByte( castVote );
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 
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 );
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 */
3548  if ( lastWinner == clientNum ) {
3549  lastWinner = -1;
3550  }
3552  CheckAbortGame();
3553 }
3554 
3555 /*
3556 ================
3557 idMultiplayerGame::CheckAbortGame
3558 ================
3559 */
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 */
3609  int clientNum;
3610 
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
3628 #else
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  }
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 );
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 ) {
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() ) {
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 
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 ) );
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 );
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 {
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 {
3785  }
3786  }
3787  }
3788  }
3789 }
3790 
3791 /*
3792 ================
3793 idMultiplayerGame::Precache
3794 ================
3795 */
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++ ) {
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 */
3842  bool spectating;
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 {
3855  }
3856  }
3857 }
3858 
3859 /*
3860 ================
3861 idMultiplayerGame::ToggleReady
3862 ================
3863 */
3865  bool ready;
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 */
3882  bool team;
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 */
3899  int i;
3900 
3902 
3903  i = 0;
3904  while ( ThrottleVars[ i ] ) {
3906  cvarSystem->GetCVarString( ThrottleVars[ i ] ) ) ) {
3907  if ( gameLocal.realClientTime < switchThrottle[ 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 */
3924  return !p->wantSpectate && playerState[ p->entityNumber ].ingame;
3925 }
3926 
3927 /*
3928 ================
3929 idMultiplayerGame::EnterGame
3930 ================
3931 */
3932 void idMultiplayerGame::EnterGame( int clientNum ) {
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 */
3950  return p->forceRespawn && !p->wantSpectate && playerState[ p->entityNumber ].ingame;
3951 }
3952 
3953 /*
3954 ================
3955 idMultiplayerGame::VoiceChat
3956 ================
3957 */
3959  gameLocal.mpGame.VoiceChat( args, false );
3960 }
3961 
3962 /*
3963 ================
3964 idMultiplayerGame::VoiceChatTeam
3965 ================
3966 */
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
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  }
4013 
4014  outMsg.Init( msgBuf, sizeof( msgBuf ) );
4016  outMsg.WriteLong( index );
4017  outMsg.WriteBits( team ? 1 : 0, 1 );
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 */
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();
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( );
4099  networkSystem->ServerSendReliableMessage( clientNum, outMsg );
4100 
4101  // warmup time
4102  if ( gameState == COUNTDOWN ) {
4103  outMsg.BeginWriting();
4105  outMsg.WriteLong( warmupEndTime );
4106  networkSystem->ServerSendReliableMessage( clientNum, outMsg );
4107  }
4108 }
4109 
4110 /*
4111 ================
4112 idMultiplayerGame::ClientReadStartState
4113 ================
4114 */
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 */
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 */
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 */
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  const 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" );
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 ] ) ) {
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
idPlayer * GetLocalPlayer() const
void WriteShort(int c)
Definition: BitMsg.h:546
void DrawScoreBoard(idPlayer *player)
void VoiceChat(const idCmdArgs &args, bool team)
void BeginWriting(void)
Definition: BitMsg.h:265
void LocalMapRestart(void)
virtual void SetCVarInteger(const char *name, const int value, int flags=0)=0
virtual idFile * OpenFileRead(const char *relativePath, bool allowCopyFiles=true, const char *gamedir=NULL)=0
static int snPrintf(char *dest, int size, const char *fmt,...) id_attribute((format(printf
Definition: Str.cpp:1465
bool IsGametypeTeamBased(void)
GLsizei const GLfloat * value
Definition: glext.h:3614
void WriteByte(int c)
Definition: BitMsg.h:283
int ReadShort(void) const
Definition: BitMsg.h:613
virtual void Add(int id, const idStr &s)=0
void ClientReadWarmupTime(const idBitMsg &msg)
int GetInt(const char *key, const char *defaultString="0") const
Definition: Dict.h:252
void ProcessVoiceChat(int clientNum, bool team, int index)
assert(prefInfo.fullscreenBtn)
virtual const idSoundShader * FindSound(const char *name, bool makeDefault=true)=0
virtual void StateChanged(int time, bool redraw=false)=0
const idDict * FindEntityDefDict(const char *name, bool makeDefault=true) const
idUserInterface * scoreBoard
idCVarSystem * cvarSystem
Definition: CVarSystem.cpp:487
idUserInterface * mainGui
const char * HandleGuiCommands(const char *menuCommand)
idNetworkSystem * networkSystem
bool forceRespawn
Definition: Player.h:339
idPlayer * FragLimitHit(void)
void PlayerStats(int clientNum, char *data, const int len)
void Printf(const char *fmt,...) const id_attribute((format(printf
Definition: Game_local.cpp:699
virtual void SetStateString(const char *varName, const char *value)=0
GLenum GLint GLuint mask
Definition: glext.h:5864
void SetLeader(bool lead)
Definition: Player.h:823
virtual void virtual void virtual const idLangDict * GetLanguageDict(void)=0
bool NeedsIcon(void)
static void MessageMode_f(const idCmdArgs &args)
const idStr & GetKey(void) const
Definition: Dict.h:52
bool IsLeader(void)
Definition: Player.h:827
const int MP_PLAYER_MINFRAGS
void UpdateScoreboard(idUserInterface *scoreBoard, idPlayer *player)
bool lastManPlayAgain
Definition: Player.h:359
void PlayGlobalSound(int to, snd_evt_t evt, const char *shader=NULL)
const int NUM_CHAT_NOTIFY
void ClientCallVote(vote_flags_t voteIndex, const char *voteValue)
void UpdateRankColor(idUserInterface *gui, const char *mask, int i, const idVec3 &vec)
void SwitchToTeam(int clientNum, int oldteam, int newteam)
int Length(void) const
Definition: Str.h:702
bool isMultiplayer
Definition: Game_local.h:325
bool IsType(const idTypeInfo &c) const
Definition: Class.h:337
void EnterGame(int clientNum)
void SpawnPlayer(int clientNum)
idStr sessionCommand
Definition: Game_local.h:303
virtual int GetNumMaps()=0
void RenderPlayerView(idUserInterface *hud)
Definition: PlayerView.cpp:684
virtual int GetCVarInteger(const char *name) const =0
virtual void PlayShaderDirectly(const char *name, int channel=-1)=0
GLenum GLsizei n
Definition: glext.h:3705
int ReadLong(void) const
Definition: BitMsg.h:375
void WantKilled(int clientNum)
const idKeyValue * MatchPrefix(const char *prefix, const idKeyValue *lastMatch=NULL) const
Definition: Dict.cpp:523
case const int
Definition: Callbacks.cpp:52
const int ASYNC_PLAYER_PING_BITS
static const char * MPGuis[]
idCVar g_spectatorChat("g_spectatorChat","0", CVAR_GAME|CVAR_ARCHIVE|CVAR_BOOL,"let spectators talk to everyone during game")
idFileSystem * fileSystem
Definition: FileSystem.cpp:500
void WriteBits(int value, int numBits)
Definition: BitMsg.cpp:648
void PrintMessageEvent(int to, msg_evt_t evt, int parm1=-1, int parm2=-1)
static const char * GameStateStrings[STATE_COUNT]
bool isClient
Definition: Game_local.h:327
gameState_t gameState
const char * GetName(void) const
Definition: DeclManager.h:140
Definition: Vector.h:316
idCVar si_gameType("si_gameType", si_gameTypeArgs[0], CVAR_GAME|CVAR_SERVERINFO|CVAR_ARCHIVE,"game type - singleplayer, deathmatch, Tourney, Team DM or Last Man", si_gameTypeArgs, idCmdSystem::ArgCompletion_String< si_gameTypeArgs >)
bool EnoughClientsToPlay(void)
void CheckRespawns(idPlayer *spectator=NULL)
virtual void HandleNamedEvent(const char *eventName)=0
virtual void FindMapScreenshot(const char *path, char *buf, int len)=0
const char * Left(int len, idStr &result) const
Definition: Str.h:892
int team
Definition: Actor.h:115
void MapRestart(void)
const int MP_PLAYER_MAXPING
virtual void SetCVarString(const char *name, const char *value, int flags=0)=0
virtual void SetKeyBindingNames(void)=0
snd_evt_t
idCmdSystem * cmdSystem
Definition: CmdSystem.cpp:116
int ReadString(char *buffer, int bufferSize) const
Definition: BitMsg.cpp:424
virtual void SetStateInt(const char *varName, const int value)=0
void WriteString(const char *s, int maxLength=-1, bool make7Bit=true)
Definition: BitMsg.cpp:174
GLdouble s
Definition: glext.h:2935
GLenum GLsizei len
Definition: glext.h:3472
int ReadBits(int numBits) const
Definition: BitMsg.cpp:709
void ClientUpdateVote(vote_result_t result, int yesCount, int noCount)
void ServerStartVote(int clientNum, vote_flags_t voteIndex, const char *voteValue)
bool forcedReady
Definition: Player.h:343
static int ClampInt(int min, int max, int value)
Definition: Math.h:883
idUserInterfaceManager * uiManager
int i
Definition: process.py:33
int ReadByte(void) const
Definition: BitMsg.h:363
GLuint GLuint num
Definition: glext.h:5390
idMultiplayerGame::gameState_t GetGameState(void) const
int Icmp(const char *text) const
Definition: Str.h:667
idSoundWorld * gameSoundWorld
Definition: Game_local.cpp:56
void PlayerDeath(idPlayer *dead, idPlayer *killer, bool telefrag)
virtual void SetStateBool(const char *varName, const bool value)=0
void Init(byte *data, int length)
Definition: BitMsg.h:155
virtual int Num(void)=0
virtual idUserInterface * FindGui(const char *qpath, bool autoLoad=false, bool needUnique=false, bool forceUnique=false)=0
virtual void HidePlayerIcons(void)
void UpdateTourneyLine(void)
virtual void SetStateFloat(const char *varName, const float value)=0
void void void Warning(const char *fmt,...) const id_attribute((format(printf
Definition: Game_local.cpp:735
Definition: File.h:50
virtual void SetSelection(int sel)=0
virtual void BufferCommandText(cmdExecution_t exec, const char *text)=0
class idPlayerView playerView
Definition: Player.h:251
int ServerRemapDecl(int clientNum, declType_t type, int index)
void NewState(gameState_t news, idPlayer *player=NULL)
void ClientReadStartState(const idBitMsg &msg)
GLuint GLuint GLsizei count
Definition: glext.h:2845
int Index(void) const
Definition: DeclManager.h:165
bool IsInTeleport(void)
Definition: Player.h:819
idPlayer * FragLeader(void)
void SetString(const char *value)
Definition: CVarSystem.h:146
static const char * ThrottleVarsInEnglish[]
mpPlayerState_t playerState[MAX_CLIENTS]
bool IsInGame(int clientNum)
GLuint index
Definition: glext.h:3476
const GLubyte * c
Definition: glext.h:4677
const char * GetString(const char *key, const char *defaultString="") const
Definition: Dict.h:240
void AddChatLine(const char *fmt,...) id_attribute((format(printf
static void CallVote_f(const idCmdArgs &args)
#define MAX_STRING_CHARS
Definition: Lib.h:95
bool StartSound(const char *soundName, const s_channelType channel, int soundShaderFlags, bool broadcast, int *length)
Definition: Entity.cpp:1622
void CastVote(int clientNum, bool vote)
const char * ui_skinArgs[]
Definition: SysCvar.cpp:58
void WriteLong(int c)
Definition: BitMsg.h:295
mpChatLine_t chatHistory[NUM_CHAT_NOTIFY]
#define MAX_CLIENTS
Definition: Game_local.h:81
idCommon * common
Definition: Common.cpp:206
bool GetBool(const char *key, const char *defaultString="0") const
Definition: Dict.h:256
Definition: Dict.h:65
virtual idListGUI * AllocListGUI(void) const =0
#define NULL
Definition: Lib.h:88
virtual const char * GetCVarString(const char *name) const =0
void void DPrintf(const char *fmt,...) const id_attribute((format(printf
Definition: Game_local.cpp:715
void ServerSendChatMessage(int to, const char *name, const char *text)
void SetInteger(const int value)
Definition: CVarSystem.h:148
GLsizei GLsizei GLenum GLenum const GLvoid * data
Definition: glext.h:2853
void void UpdateMainGui(void)
virtual void SetCVarBool(const char *name, const bool value, int flags=0)=0
int GetInteger(void) const
Definition: CVarSystem.h:143
virtual int GetSelection(char *s, int size, int sel=0) const =0
virtual void Redraw(int time)=0
void ServerSendEvent(int eventId, const idBitMsg *msg, bool saveEvent, int excludeClient) const
Definition: Entity.cpp:4897
void WriteShort(int c)
Definition: BitMsg.h:287
idCVar si_fragLimit("si_fragLimit","10", CVAR_GAME|CVAR_SERVERINFO|CVAR_ARCHIVE|CVAR_INTEGER,"frag limit", 1, MP_PLAYER_MAXFRAGS)
static int FindText(const char *str, const char *text, bool casesensitive=true, int start=0, int end=-1)
Definition: Str.cpp:207
static void VoiceChatTeam_f(const idCmdArgs &args)
GLint mode
Definition: glext.h:4165
const char * GetString(const char *str) const
Definition: LangDict.cpp:148
virtual void FreeListGUI(idListGUI *listgui)=0
bool CanPlay(idPlayer *p)
idUserInterface * StartMenu(void)
virtual const char * Activate(bool activate, int time)=0
const idStr & GetValue(void) const
Definition: Dict.h:53
idPlayer * rankedPlayers[MAX_CLIENTS]
void WriteByte(int c)
Definition: BitMsg.h:542
int Argc(void) const
Definition: CmdArgs.h:48
const char * si_gameTypeArgs[]
Definition: SysCvar.cpp:49
void Clear(void)
Definition: Str.h:724
idUserInterface * hud
Definition: Player.h:293
const char * Right(int len, idStr &result) const
Definition: Str.h:896
int Find(const char c, int start=0, int end=-1) const
Definition: Str.h:874
idGameLocal gameLocal
Definition: Game_local.cpp:64
void PlayerVote(int clientNum, playerVote_t vote)
virtual void ServerSendReliableMessage(int clientNum, const idBitMsg &msg)
idUserInterface * guiChat
void TeamScore(int entityNumber, int team, int delta)
virtual void Printf(const char *fmt,...) id_attribute((format(printf
gameType_t lastGameType
static const int ThrottleDelay[]
void ServerWriteInitialReliableMessages(int clientNum)
virtual const idDict & State() const =0
bool WantRespawn(idPlayer *p)
static int BitsForInteger(int i)
Definition: Math.h:727
void ClientStartVote(int clientNum, const char *voteString)
const int MP_PLAYER_MAXFRAGS
void MessageMode(const idCmdArgs &args)
idUserInterface * msgmodeGui
int numClients
Definition: Game_local.h:271
idCVar si_timeLimit("si_timeLimit","10", CVAR_GAME|CVAR_SERVERINFO|CVAR_ARCHIVE|CVAR_INTEGER,"time limit in minutes", 0, 60)
bool NextMap(void)
virtual void Config(idUserInterface *pGUI, const char *name)=0
idDeclManager * declManager
int spawnedTime
Definition: Player.h:350
void WriteToSnapshot(idBitMsgDelta &msg) const
GLubyte GLubyte b
Definition: glext.h:4662
idEntity * entities[MAX_GENTITIES]
Definition: Game_local.h:275
idDict userInfo[MAX_CLIENTS]
Definition: Game_local.h:272
playerVote_t
virtual int GetNumDecls(declType_t type)=0
idPlayer * GetClientByNum(int current) const
static const char * ThrottleVars[]
void TokenizeString(const char *text, bool keepAsStrings)
Definition: CmdArgs.cpp:106
int localClientNum
Definition: Game_local.h:330
virtual int ServerGetClientPing(int clientNum)
LPCSTR GetString(LPCSTR psPrompt)
Definition: GetString.cpp:87
idDict serverInfo
Definition: Game_local.h:270
const char * GameTime(void)
virtual void Clear(void)=0
bool IsReady(void)
Definition: Player.h:807
bool GetBool(void) const
Definition: CVarSystem.h:142
virtual const idDecl * DeclByIndex(declType_t type, int index, bool forceParse=true)=0
int tourneyLine
Definition: Player.h:349
void ProcessChatMessage(int clientNum, bool team, const char *name, const char *text, const char *sound)
tuple f
Definition: idal.py:89
static const char * GlobalSoundStrings[SND_COUNT]
gameState_t nextState
idUserInterface * spectateGui
bool IsGametypeFlagBased(void)
unsigned char byte
Definition: Lib.h:75
bool WriteDeltaDict(const idDict &dict, const idDict *base)
Definition: BitMsg.cpp:308
const GLcharARB * name
Definition: glext.h:3629
static void Vote_f(const idCmdArgs &args)
const int CHAT_FADE_TIME
idDict * GetUserInfo(void)
idCVar g_voteFlags("g_voteFlags","0", CVAR_GAME|CVAR_NETWORKSYNC|CVAR_INTEGER|CVAR_ARCHIVE,"vote flags. bit mask of votes not allowed on this server\n""bit 0 (+1) restart now\n""bit 1 (+2) time limit\n""bit 2 (+4) frag limit\n""bit 3 (+8) game type\n""bit 4 (+16) kick player\n""bit 5 (+32) change map\n""bit 6 (+64) spectators\n""bit 7 (+128) next map")
bool lastManOver
Definition: Player.h:358
const int MP_PLAYER_MAXWINS
Definition: Str.h:116
int ReadByte(void) const
Definition: BitMsg.h:609
static void ForceReady_f(const idCmdArgs &args)
idCVar si_map("si_map","game/mp/d3dm1", CVAR_GAME|CVAR_SERVERINFO|CVAR_ARCHIVE,"map to be played next on server", idCmdSystem::ArgCompletion_MapName)
static void DropWeapon_f(const idCmdArgs &args)
bool AllPlayersReady(void)
#define LASTMAN_NOLIVES
int vsprintf(idStr &string, const char *fmt, va_list argptr)
Definition: Str.cpp:1549
const char * c_str(void) const
Definition: Str.h:487
void DropWeapon(int clientNum)
const int MAX_GAME_MESSAGE_SIZE
Definition: Game_local.h:113
void ReadFromSnapshot(const idBitMsgDelta &msg)
void ServerSpectate(bool spectate)
const idKeyValue * GetKeyVal(int index) const
Definition: Dict.h:294
void SetBool(const bool value)
Definition: CVarSystem.h:147
bool wantSpectate
Definition: Player.h:344
const char * Argv(int arg) const
Definition: CmdArgs.h:50
idVec3 colorBar
Definition: Player.h:335
virtual const idDeclSkin * FindSkin(const char *name, bool makeDefault=true)=0
void UpdateWinsLosses(idPlayer *winner)
playerVote_t vote
int NumActualClients(bool countSpectators, int *teamcount=NULL)
int realClientTime
Definition: Game_local.h:332
GLint j
Definition: qgl.h:264
void SetPrivateCameraView(idCamera *camView)
float GetMinValue(void) const
Definition: CVarSystem.h:132
char * va(const char *fmt,...)
Definition: Str.cpp:1568
void WriteBits(int value, int numBits)
Definition: BitMsg.cpp:110
virtual void CloseFile(idFile *f)=0
virtual void DPrintf(const char *fmt,...) id_attribute((format(printf
#define max(x, y)
Definition: os.h:70
idMultiplayerGame mpGame
Definition: Game_local.h:305
virtual const idDict * GetMapDecl(int i)=0
int entityNumber
Definition: Entity.h:111
GLfloat GLfloat p
Definition: glext.h:4674
bool Draw(int clientNum)
int tourneyRank
Definition: Player.h:348
idCVar g_balanceTDM("g_balanceTDM","1", CVAR_GAME|CVAR_BOOL,"maintain even teams")
void Kill(bool delayRespawn, bool nodamage)
idCVar si_spectators("si_spectators","1", CVAR_GAME|CVAR_SERVERINFO|CVAR_ARCHIVE|CVAR_BOOL,"allow spectators or require all clients to play")
bool scoreBoardOpen
Definition: Player.h:337
int GetNumKeyVals(void) const
Definition: Dict.h:290
int spectator
Definition: Player.h:334
ID_INLINE T Min(T x, T y)
Definition: Lib.h:159
bool spectating
Definition: Player.h:340
void ServerClientConnect(int clientNum)
gameType_t gameType
Definition: Game_local.h:324
int ReadBits(int numBits) const
Definition: BitMsg.cpp:362
int sprintf(idStr &string, const char *fmt,...)
Definition: Str.cpp:1528
void ServerCallVote(int clientNum, const idBitMsg &msg)
void CycleTourneyPlayers(void)
const int FRAGLIMIT_DELAY
bool lastManPresent
Definition: Player.h:360
const int ASYNC_PLAYER_WINS_BITS
void UpdateHud(idPlayer *player, idUserInterface *hud)
virtual void ClientSendReliableMessage(const idBitMsg &msg)
void DisconnectClient(int clientNum)
static void VoiceChat_f(const idCmdArgs &args)
float GetMaxValue(void) const
Definition: CVarSystem.h:133
int kickVoteMap[MAX_CLIENTS]
int ReadShort(void) const
Definition: BitMsg.h:367
GLdouble GLdouble t
Definition: glext.h:2943
const int ASYNC_PLAYER_FRAG_BITS
idEntity * FindEntityUsingDef(idEntity *from, const char *match) const