home *** CD-ROM | disk | FTP | other *** search
/ GameStar 2006 March / Gamestar_82_2006-03_dvd.iso / DVDStar / Editace / quake4_sdkv10.exe / source / game / MultiplayerGame.cpp < prev    next >
C/C++ Source or Header  |  2005-11-14  |  252KB  |  7,656 lines

  1. // RAVEN BEGIN
  2. // ddynerman: note that this file is no longer merged with Doom3 updates
  3. //
  4. // MERGE_DATE 09/30/2004
  5.  
  6. #include "../idlib/precompiled.h"
  7. #pragma hdrstop
  8.  
  9. #include "Game_local.h"
  10.  
  11. idCVar g_spectatorChat( "g_spectatorChat", "0", CVAR_GAME | CVAR_ARCHIVE | CVAR_BOOL, "let spectators talk to everyone during game" );
  12.  
  13. const char *idMultiplayerGame::MPGuis[] = {
  14. // RAVEN BEGIN
  15. // bdube: use regular hud for now
  16.     "guis/hud.gui",
  17. // RAVEN END
  18.     "guis/mpmain.gui",
  19.     "guis/mpmsgmode.gui",
  20.     "guis/netmenu.gui",
  21.     "guis/mphud.gui",
  22.     NULL
  23. };
  24.  
  25. const char *idMultiplayerGame::ThrottleVars[] = {
  26.     "ui_spectate",
  27.     "ui_ready",
  28.     "ui_team",
  29.     NULL
  30. };
  31.  
  32. const char *idMultiplayerGame::ThrottleVarsInEnglish[] = {
  33.     "#str_106738",
  34.     "#str_106737",
  35.     "#str_101991",
  36.     NULL
  37. };
  38.  
  39. const int idMultiplayerGame::ThrottleDelay[] = {
  40.     8,
  41.     5,
  42.     5
  43. };
  44.  
  45. const char* idMultiplayerGame::teamNames[ TEAM_MAX ] = {
  46.     "Marine",
  47.     "Strogg"
  48. };
  49.  
  50. idCVar gui_ui_name( "gui_ui_name", "", CVAR_GAME | CVAR_NOCHEAT, "copy-over cvar for ui_name" );
  51.  
  52. /*
  53. ================
  54. ComparePlayerByScore
  55. ================
  56. */
  57. int ComparePlayersByScore( const void* left, const void* right ) {
  58.     return ((const rvPair<idPlayer*, int>*)right)->Second() - 
  59.         ((const rvPair<idPlayer*, int>*)left)->Second();
  60. }
  61.  
  62. /*
  63. ================
  64. CompareTeamByScore
  65. ================
  66. */
  67. int CompareTeamsByScore( const void* left, const void* right ) {
  68.     return ((const rvPair<int, int>*)right)->Second() - 
  69.              ((const rvPair<int, int>*)left)->Second();
  70. }
  71.  
  72. /*
  73. ================
  74. idMultiplayerGame::idMultiplayerGame
  75. ================
  76. */
  77. idMultiplayerGame::idMultiplayerGame() {
  78.     scoreBoard = NULL;
  79.     statSummary = NULL;
  80.     mainGui = NULL;
  81.     mapList = NULL;
  82.     msgmodeGui = NULL;
  83.  
  84.     memset ( lights, 0, sizeof( lights ) );
  85.     memset ( lightHandles, -1, sizeof( lightHandles ) );
  86.  
  87.     Clear();
  88.  
  89.     for( int i = 0; i < TEAM_MAX; i++ ) {
  90.         teamScore[ i ] = 0;
  91.     }
  92.  
  93.     announcerSoundQueue.Clear();
  94.     announcerPlayTime = 0;
  95.  
  96.     gameState = NULL;
  97.     currentSoundOverride = false;
  98. }
  99.  
  100. /*
  101. ================
  102. idMultiplayerGame::Shutdown
  103. ================
  104. */
  105. void idMultiplayerGame::Shutdown( void ) {
  106.  
  107.     Clear();
  108.     statManager->Shutdown();
  109.  
  110.     if( gameState ) {
  111.         delete gameState;
  112.     }
  113.     gameState = NULL;
  114. }
  115.  
  116. /*
  117. ================
  118. idMultiplayerGame::Reset
  119. ================
  120. */
  121. void idMultiplayerGame::Reset() {
  122.     Clear();
  123.     assert( !scoreBoard && !mainGui && !mapList );
  124.     
  125.     PACIFIER_UPDATE;
  126.     scoreBoard = uiManager->FindGui( "guis/scoreboard.gui", true, false, true );
  127.  
  128. #ifdef _XENON
  129.     statSummary = scoreBoard;
  130. #else
  131.     statSummary = uiManager->FindGui( "guis/summary.gui", true, false, true );
  132.     statSummary->SetStateBool( "gameDraw", true );
  133. #endif
  134.  
  135.     PACIFIER_UPDATE;
  136.  
  137.     mainGui = uiManager->FindGui( "guis/mpmain.gui", true, false, true );
  138.     mapList = uiManager->AllocListGUI( );
  139.     mapList->Config( mainGui, "mapList" );
  140.     
  141.     // set this GUI so that our Draw function is still called when it becomes the active/fullscreen GUI
  142.     mainGui->SetStateBool( "gameDraw", true );
  143.     mainGui->SetKeyBindingNames();
  144.     mainGui->SetStateInt( "com_machineSpec", cvarSystem->GetCVarInteger( "com_machineSpec" ) );
  145.  
  146. //    SetMenuSkin();
  147.     
  148.     PACIFIER_UPDATE;
  149.     msgmodeGui = uiManager->FindGui( "guis/mpmsgmode.gui", true, false, true );
  150.     msgmodeGui->SetStateBool( "gameDraw", true );
  151.  
  152.     memset ( lights, 0, sizeof( lights ) );
  153.     memset ( lightHandles, -1, sizeof( lightHandles ) );
  154.  
  155.     renderLight_t    *light;
  156.     const char        *shader;
  157.  
  158.     light = &lights[ MPLIGHT_CTF_MARINE ];
  159.     shader = "lights/mpCTFLight";
  160.     if ( shader && *shader ) {
  161.         light->axis.Identity();
  162.         light->shader = declManager->FindMaterial( shader, false );
  163.         light->lightRadius[0] = light->lightRadius[1] = light->lightRadius[2] = 64.0f;
  164.         light->shaderParms[ SHADERPARM_RED ]    = 142.0f / 255.0f;
  165.         light->shaderParms[ SHADERPARM_GREEN ]    = 190.0f / 255.0f;
  166.         light->shaderParms[ SHADERPARM_BLUE ]    = 84.0f / 255.0f;
  167.         light->shaderParms[ SHADERPARM_ALPHA ]    = 1.0f;
  168.         light->detailLevel = DEFAULT_LIGHT_DETAIL_LEVEL;
  169.         light->pointLight = true;
  170.         light->noShadows = true;
  171.         light->noDynamicShadows = true;
  172.         light->lightId = -MPLIGHT_CTF_MARINE;
  173.         light->allowLightInViewID = 0;
  174.     }
  175.  
  176.     light = &lights[ MPLIGHT_CTF_STROGG ];
  177.     shader = "lights/mpCTFLight";
  178.     if ( shader && *shader ) {
  179.         light->axis.Identity();
  180.         light->shader = declManager->FindMaterial( shader, false );
  181.         light->lightRadius[0] = light->lightRadius[1] = light->lightRadius[2] = 64.0f;
  182.         light->shaderParms[ SHADERPARM_RED ]    = 255.0f / 255.0f;
  183.         light->shaderParms[ SHADERPARM_GREEN ]    = 153.0f / 255.0f;
  184.         light->shaderParms[ SHADERPARM_BLUE ]    = 0.0f / 255.0f;
  185.         light->shaderParms[ SHADERPARM_ALPHA ]    = 1.0f;
  186.         light->detailLevel = DEFAULT_LIGHT_DETAIL_LEVEL;
  187.         light->pointLight = true;
  188.         light->noShadows = true;
  189.         light->noDynamicShadows = true;
  190.         light->lightId = -MPLIGHT_CTF_STROGG;
  191.         light->allowLightInViewID = 0;
  192.     }
  193.  
  194.     light = &lights[ MPLIGHT_QUAD ];
  195.     shader = "lights/mpCTFLight";
  196.     if ( shader && *shader ) {
  197.         light->axis.Identity();
  198.         light->shader = declManager->FindMaterial( shader, false );
  199.         light->lightRadius[0] = light->lightRadius[1] = light->lightRadius[2] = 64.0f;
  200.         light->shaderParms[ SHADERPARM_RED ]    = 0.0f;
  201.         light->shaderParms[ SHADERPARM_GREEN ]    = 128.0f / 255.0f;
  202.         light->shaderParms[ SHADERPARM_BLUE ]    = 255.0f / 255.0f;
  203.         light->shaderParms[ SHADERPARM_ALPHA ]    = 1.0f;
  204.         light->detailLevel = DEFAULT_LIGHT_DETAIL_LEVEL;
  205.         light->pointLight = true;
  206.         light->noShadows = true;
  207.         light->noDynamicShadows = true;
  208.         light->lightId = -MPLIGHT_CTF_STROGG;
  209.         light->allowLightInViewID = 0;
  210.     }
  211.  
  212.     light = &lights[ MPLIGHT_HASTE ];
  213.     shader = "lights/mpCTFLight";
  214.     if ( shader && *shader ) {
  215.         light->axis.Identity();
  216.         light->shader = declManager->FindMaterial( shader, false );
  217.         light->lightRadius[0] = light->lightRadius[1] = light->lightRadius[2] = 64.0f;
  218.         light->shaderParms[ SHADERPARM_RED ]    = 225.0f / 255.0f;
  219.         light->shaderParms[ SHADERPARM_GREEN ]    = 255.0f / 255.0f;
  220.         light->shaderParms[ SHADERPARM_BLUE ]    = 0.0f;
  221.         light->shaderParms[ SHADERPARM_ALPHA ]    = 1.0f;
  222.         light->detailLevel = DEFAULT_LIGHT_DETAIL_LEVEL;
  223.         light->pointLight = true;
  224.         light->noShadows = true;
  225.         light->noDynamicShadows = true;
  226.         light->lightId = -MPLIGHT_CTF_STROGG;
  227.         light->allowLightInViewID = 0;
  228.     }
  229.  
  230.     light = &lights[ MPLIGHT_REGEN ];
  231.     shader = "lights/mpCTFLight";
  232.     if ( shader && *shader ) {
  233.         light->axis.Identity();
  234.         light->shader = declManager->FindMaterial( shader, false );
  235.         light->lightRadius[0] = light->lightRadius[1] = light->lightRadius[2] = 64.0f;
  236.         light->shaderParms[ SHADERPARM_RED ]    = 255.0f / 255.0f;
  237.         light->shaderParms[ SHADERPARM_GREEN ]    = 0.0f;
  238.         light->shaderParms[ SHADERPARM_BLUE ]    = 0.0f;
  239.         light->shaderParms[ SHADERPARM_ALPHA ]    = 1.0f;
  240.         light->detailLevel = DEFAULT_LIGHT_DETAIL_LEVEL;
  241.         light->pointLight = true;
  242.         light->noShadows = true;
  243.         light->noDynamicShadows = true;
  244.         light->lightId = -MPLIGHT_CTF_STROGG;
  245.         light->allowLightInViewID = 0;
  246.     }
  247.  
  248.     PACIFIER_UPDATE;
  249.     ClearGuis();
  250.  
  251.     // asalmon: Need to refresh stats periodically if the player is looking at stats
  252.     currentStatClient = -1;
  253.     currentStatTeam = 0;
  254.  
  255.     iconManager->Shutdown();
  256. }
  257.  
  258. /*
  259. ================
  260. idMultiplayerGame::ServerClientConnect
  261. ================
  262. */
  263. void idMultiplayerGame::ServerClientConnect( int clientNum ) {
  264.     memset( &playerState[ clientNum ], 0, sizeof( playerState[ clientNum ] ) );
  265.     statManager->ClientConnect( clientNum );
  266. }
  267.  
  268. /*
  269. ================
  270. idMultiplayerGame::SpawnPlayer
  271. ================
  272. */
  273. void idMultiplayerGame::SpawnPlayer( int clientNum ) {
  274.  
  275.     TIME_THIS_SCOPE( __FUNCLINE__);
  276.  
  277.     bool ingame = playerState[ clientNum ].ingame;
  278.      // keep ingame to true if needed, that should only happen for local player
  279.      
  280.     memset( &playerState[ clientNum ], 0, sizeof( playerState[ clientNum ] ) );
  281.     if ( !gameLocal.isClient ) {
  282.         idPlayer *p = static_cast< idPlayer * >( gameLocal.entities[ clientNum ] );
  283.         p->spawnedTime = gameLocal.time;
  284.         if ( gameLocal.IsTeamGame() ) {
  285.             SwitchToTeam( clientNum, -1, p->team );
  286.         }
  287.          playerState[ clientNum ].ingame = ingame;
  288.     }
  289.  
  290.     if ( clientNum == gameLocal.localClientNum ) {
  291.         tourneyGUI.SetupTourneyGUI( gameLocal.GetLocalPlayer()->mphud, scoreBoard );
  292.     }
  293. }
  294.  
  295. /*
  296. ================
  297. idMultiplayerGame::Clear
  298. ================
  299. */
  300. void idMultiplayerGame::Clear() {
  301.     
  302. //    if( mainGui )    {
  303. //        mainGui->SetStateInt( "password_valid", 0 );
  304. //    }
  305.  
  306.     pingUpdateTime = 0;
  307.     vote = VOTE_NONE;
  308.     voteTimeOut = 0;
  309.     voteExecTime = 0;
  310.     matchStartedTime = 0;
  311.     memset( &playerState, 0 , sizeof( playerState ) );
  312.     currentMenu = 0;
  313.     bCurrentMenuMsg = false;
  314.     nextMenu = 0;
  315.     pureReady = false;
  316.     scoreBoard = NULL;
  317.     statSummary = NULL;
  318.     mainGui = NULL;
  319.     msgmodeGui = NULL;
  320.     if ( mapList ) {
  321.          uiManager->FreeListGUI( mapList );
  322.         mapList = NULL;
  323.     }
  324.     memset( &switchThrottle, 0, sizeof( switchThrottle ) );
  325.     voiceChatThrottle = 0;
  326.  
  327.     voteValue.Clear();
  328.     voteString.Clear();
  329.  
  330.     for ( int i = 0; i < MPLIGHT_MAX; i ++ ) {
  331.         FreeLight( i );
  332.     }
  333.  
  334.     memset( rankedTeams, 0, sizeof( rvPair<int, int> ) * TEAM_MAX );
  335.  
  336.     if( gameState ) {
  337.         gameState->Clear();
  338.     }
  339.  
  340. // RAVEN BEGIN
  341. // mwhitlock: Dynamic memory consolidation
  342. #if defined(_RV_MEM_SYS_SUPPORT)
  343.     rankedPlayers.SetAllocatorHeap(rvGetSysHeap(RV_HEAP_ID_MULTIPLE_FRAME));
  344.     unrankedPlayers.SetAllocatorHeap(rvGetSysHeap(RV_HEAP_ID_MULTIPLE_FRAME));
  345.     assaultPoints.SetAllocatorHeap(rvGetSysHeap(RV_HEAP_ID_MULTIPLE_FRAME));
  346. #endif
  347. // RAVEN END
  348.  
  349.     rankedPlayers.Clear();
  350.     unrankedPlayers.Clear();
  351.     assaultPoints.Clear();
  352. }
  353.  
  354. /*
  355. ================
  356. idMultiplayerGame::ClearMap
  357. ================
  358. */
  359. void idMultiplayerGame::ClearMap ( void ) {
  360.     assaultPoints.Clear();
  361.     ClearAnnouncerSounds();
  362.     announcerPlayTime = 0;
  363. }
  364.  
  365. /*
  366. ================
  367. idMultiplayerGame::ClearGuis
  368. ================
  369. */
  370. void idMultiplayerGame::ClearGuis() {
  371.     int i;
  372.  
  373.     for ( i = 0; i < MAX_CLIENTS; i++ ) {
  374.         scoreBoard->SetStateString( va( "player%i",i+1 ), "" );
  375.         scoreBoard->SetStateString( va( "player%i_score", i+1 ), "" );
  376.         scoreBoard->SetStateString( va( "player%i_tdm_tscore", i+1 ), "" );
  377.         scoreBoard->SetStateString( va( "player%i_tdm_score", i+1 ), "" );
  378.         scoreBoard->SetStateString( va( "player%i_wins", i+1 ), "" );
  379.         scoreBoard->SetStateString( va( "player%i_status", i+1 ), "" );
  380.         scoreBoard->SetStateInt( va( "rank%i", i+1 ), 0 );
  381.         scoreBoard->SetStateInt( "rank_self", 0 );
  382.  
  383.         idPlayer *player = static_cast<idPlayer *>( gameLocal.entities[ i ] );
  384.         if ( !player || !player->hud ) {
  385.             continue;
  386.         }
  387.         player->hud->SetStateString( va( "player%i",i+1 ), "" );
  388.         player->hud->SetStateString( va( "player%i_score", i+1 ), "" );
  389.         player->hud->SetStateString( va( "player%i_ready", i+1 ), "" );
  390.         scoreBoard->SetStateInt( va( "rank%i", i+1 ), 0 );
  391.         player->hud->SetStateInt( "rank_self", 0 );
  392.  
  393.         player->hud->SetStateInt( "team", TEAM_MARINE );
  394.         player->hud->HandleNamedEvent( "flagReturn" );    
  395.         player->hud->SetStateInt( "team", TEAM_STROGG );
  396.         player->hud->HandleNamedEvent( "flagReturn" );    
  397.     }    
  398.  
  399.     ClearVote();
  400. }
  401.  
  402. /*
  403. ================
  404. idMultiplayerGame::GetPlayerRank
  405. Returns the player rank (0 best), returning the best rank in the case of a tie
  406. ================
  407. */
  408. int idMultiplayerGame::GetPlayerRank( idPlayer* player, bool& isTied ) {
  409.     int initialRank = -1;
  410.     int rank = -1;
  411.  
  412.     for( int i = 0; i < rankedPlayers.Num(); i++ ) {
  413.         if( rankedPlayers[ i ].First() == player ) {
  414.             rank = i;
  415.             initialRank = rank;
  416.         }
  417.     }
  418.     
  419.     if( rank == -1 ) {
  420.         return rank;
  421.     }
  422.  
  423.     if( rank > 0 ) {
  424.         if( rankedPlayers[ rank - 1 ].Second() == rankedPlayers[ rank ].Second() ) {
  425.             rank = rankedPlayers[ rank - 1 ].First()->GetRank();
  426.         } else {
  427.             rank = rankedPlayers[ rank - 1 ].First()->GetRank() + 1;
  428.         }
  429.     }
  430.  
  431.     // check for tie
  432.     isTied = false;
  433.  
  434.     for( int i = rank - 1; i <= rank + 1; i++ ) {
  435.         if( i < 0 || i >= rankedPlayers.Num() || rankedPlayers[ i ].First() == player ) {
  436.             continue;
  437.         }
  438.  
  439.         if( rankedPlayers[ i ].Second() == rankedPlayers[ initialRank ].Second() ) {
  440.             isTied = true;
  441.             break;
  442.         }
  443.     }
  444.  
  445.     return rank;
  446. }
  447.  
  448. /*
  449. ================
  450. idMultiplayerGame::GetPlayerRank
  451. Returns the player rank (0 best), adjusting the player's score by 'score' - used to
  452. print rank information before UpdatePlayerRanks is called
  453. ================
  454. */
  455. int idMultiplayerGame::GetAdjustedPlayerRank( idPlayer* player, int adjust, bool& isTied ) {
  456.     int rank = -1;
  457.     int score = -1;
  458.  
  459.     for( int i = 0; i < rankedPlayers.Num(); i++ ) {
  460.         if( rankedPlayers[ i ].First() == player ) {
  461.             rank = i;
  462.             score = rankedPlayers[ i ].Second() + adjust;
  463.             // see if we start at a different rank because out our adjustment
  464.             if( adjust > 0 ) {
  465.                 int j;
  466.                 for( j = i - 1; j >= 0; j-- ) {
  467.                     if( score < rankedPlayers[ j ].Second() ) {
  468.                         break;
  469.                     }
  470.                 }
  471.                 rank = j + 1;
  472.             } else if( adjust < 0 ) {
  473.                 int j;
  474.                 for( j = i + 1; j < rankedPlayers.Num(); j++ ) {
  475.                     if( score > rankedPlayers[ j ].Second() ) {
  476.                         break;
  477.                     }
  478.                 }
  479.                 rank = j - 1;
  480.             }    
  481.         }
  482.     }
  483.  
  484.     if( rank == -1 ) {
  485.         return rank;
  486.     }
  487.  
  488.     // check for ties
  489.     for( int i = rank - 1; i <= rank + 1; i++ ) {
  490.         if( i < 0 || i >= rankedPlayers.Num() || i == rank ) {
  491.             continue;
  492.  
  493.         }
  494.         if( rankedPlayers[ i ].Second() == score )  {
  495.             isTied = true;
  496.             break;
  497.         } 
  498.     }
  499.  
  500.     // if tied, always give the player the best rank
  501.     // i.e., if all players are tied for first, all should be ranked 0
  502.     if( isTied ) {
  503.         for( int i = rank - 1; i >= 0; i-- ) {
  504.             if( rankedPlayers[ i ].Second() == score ) {
  505.                 rank = i;
  506.             } else{
  507.                 break;
  508.             }
  509.         }
  510.     }
  511.  
  512.     return rank;
  513. }
  514.  
  515. /*
  516. ================
  517. idMultiplayerGame::UpdatePlayerRanks
  518. ================
  519. */
  520. void idMultiplayerGame::UpdatePlayerRanks( playerRankMode_t rankMode ) {
  521.     idEntity* ent = NULL;
  522.  
  523.     if( rankMode == PRM_AUTO ) {
  524.         if( gameLocal.IsTeamGame() ) {
  525.             rankMode = PRM_TEAM_SCORE_PLUS_SCORE;
  526.         } else if ( gameLocal.gameType == GAME_TOURNEY ) {
  527.             rankMode = PRM_WINS;
  528.         } else {
  529.             rankMode = PRM_SCORE;
  530.         }
  531.     }
  532.  
  533.     rankedPlayers.Clear();
  534.     unrankedPlayers.Clear();
  535.  
  536.     for ( int i = 0; i < gameLocal.numClients; i++ ) {
  537.         ent = gameLocal.entities[ i ];
  538.         
  539.         if ( !ent || !ent->IsType( idPlayer::GetClassType() ) ) {
  540.             continue;
  541.         }
  542.  
  543.         idPlayer* player = (idPlayer*)ent;
  544.         
  545.         if ( !CanPlay( player ) ) {
  546.             unrankedPlayers.Append( player );
  547.         } else {
  548.             int rankingValue = 0;
  549.             switch( rankMode ) {
  550.                 case PRM_SCORE: {
  551.                     rankingValue = GetScore( player );
  552.                     break;
  553.                 }
  554.                 case PRM_TEAM_SCORE: {
  555.                     rankingValue = GetTeamScore( player );
  556.                     break;
  557.                 }
  558.                 case PRM_TEAM_SCORE_PLUS_SCORE: {
  559.                     rankingValue = GetScore( player ) + GetTeamScore( player );
  560.                     break;
  561.                 }
  562.                 case PRM_WINS: {
  563.                     rankingValue = GetWins( player );
  564.                     break;
  565.                 }
  566.                 default: {
  567.                     gameLocal.Error( "idMultiplayerGame::UpdatePlayerRanks() - Bad ranking mode '%d'\n", rankMode );
  568.                 }
  569.             }
  570.             rankedPlayers.Append( rvPair<idPlayer*, int>(player, rankingValue ) );
  571.         }
  572.     }
  573.  
  574.     qsort( rankedPlayers.Ptr(), rankedPlayers.Num(), rankedPlayers.TypeSize(), ComparePlayersByScore );
  575.  
  576.     for( int i = 0; i < rankedPlayers.Num(); i++ ) {
  577.         bool tied;
  578.         rankedPlayers[ i ].First()->SetRank( GetPlayerRank( rankedPlayers[ i ].First(), tied ) );
  579.     }
  580.  
  581.     for( int i = 0; i < unrankedPlayers.Num(); i++ ) {
  582.         unrankedPlayers[ i ]->SetRank( -1 );
  583.     }
  584. }
  585.  
  586. /*
  587. ================
  588. idMultiplayerGame::UpdateTeamRanks
  589. ================
  590. */
  591. void idMultiplayerGame::UpdateTeamRanks( void ) {
  592.     for ( int i = 0; i < TEAM_MAX; i++ ) {
  593.         rankedTeams[ i ] = rvPair<int, int>( i, teamScore[ i ] );
  594.     }
  595.  
  596.     qsort( rankedTeams, TEAM_MAX, sizeof( rvPair<int, int> ), CompareTeamsByScore );
  597. }
  598.  
  599. /*
  600. ================
  601. idMultiplayerGame::UpdateRankColor
  602. ================
  603. */
  604. void idMultiplayerGame::UpdateRankColor( idUserInterface *gui, const char *mask, int i, const idVec3 &vec ) {
  605.     for ( int j = 1; j < 4; j++ ) {
  606.         gui->SetStateFloat( va( mask, i, j ), vec[ j - 1 ] );
  607.     }
  608. }
  609.  
  610. /*
  611. ================
  612. idMultiplayerGame::CanCapture
  613.  
  614. Determines if the given flag can be captured in the given gamestate
  615. ================
  616. */
  617. bool idMultiplayerGame::CanCapture ( int team ) {
  618.     // no AP's in one flag
  619.     if( gameLocal.gameType == GAME_1F_CTF || gameLocal.gameType == GAME_ARENA_1F_CTF ) {
  620.         return true;
  621.     } else if( gameLocal.gameType != GAME_CTF && gameLocal.gameType != GAME_ARENA_CTF ) {
  622.         return false; // no flag caps in none-CTF games
  623.     }
  624.  
  625.     if ( !assaultPoints.Num() ) {
  626.         return true;
  627.     }
  628.  
  629.     // since other logic ensures AP's are captured in order, we just need to check the last AP before the enemy flag
  630.     if ( team == TEAM_STROGG ) {
  631.         // AP 0 is always next to the marine flag
  632.         return ((rvCTFGameState*)gameState)->GetAPOwner( 0 ) == TEAM_STROGG;
  633.     }
  634.     if ( team == TEAM_MARINE ) {
  635.         // the last AP is always the one next to the strogg flag
  636.         return ((rvCTFGameState*)gameState)->GetAPOwner( assaultPoints.Num() - 1 ) == TEAM_MARINE;
  637.     }
  638.  
  639.     return false;
  640. }
  641.  
  642. void idMultiplayerGame::FlagCaptured ( idPlayer *player ) {
  643.     if( !gameLocal.isClient ) {
  644.         AddTeamScore( player->team, 1 );
  645.         AddPlayerTeamScore( player, 5 );
  646.         
  647.         gameLocal.ClearForwardSpawns();
  648.         
  649.         for( int i = 0; i < assaultPoints.Num(); i++ ) {
  650.             assaultPoints[ i ]->Reset();
  651.             ((rvCTFGameState*)gameState)->SetAPOwner( i, AS_NEUTRAL );
  652.         }
  653.  
  654.         statManager->FlagCaptured( player, OpposingTeam( player->team ) );
  655.         player->SetEmote( PE_CHEER );
  656.     }
  657. }
  658.  
  659. /*
  660. ================
  661. idMultiplayerGame::SendDeathMessage
  662. ================
  663. */
  664. void idMultiplayerGame::SendDeathMessage( idPlayer* attacker, idPlayer* victim, int methodOfDeath ) { 
  665.     if( !gameLocal.isClient ) {
  666.         idBitMsg outMsg;
  667.         byte msgBuf[1024];
  668.         outMsg.Init( msgBuf, sizeof( msgBuf ) );
  669.         outMsg.WriteByte( GAME_RELIABLE_MESSAGE_DEATH );
  670.         if( attacker ) {
  671.             outMsg.WriteByte( attacker->entityNumber );
  672.             outMsg.WriteBits( idMath::ClampInt( MP_PLAYER_MINFRAGS, MP_PLAYER_MAXFRAGS, playerState[ attacker->entityNumber ].fragCount ), ASYNC_PLAYER_FRAG_BITS );
  673.         } else {
  674.             outMsg.WriteByte( 255 );
  675.         }
  676.         
  677.         if( victim ) {
  678.             outMsg.WriteByte( victim->entityNumber );
  679.             outMsg.WriteBits( idMath::ClampInt( MP_PLAYER_MINFRAGS, MP_PLAYER_MAXFRAGS, playerState[ victim->entityNumber ].fragCount ), ASYNC_PLAYER_FRAG_BITS );
  680.         } else {
  681.             outMsg.WriteByte( 255 );
  682.         }
  683.         
  684.         outMsg.WriteByte( methodOfDeath );
  685.         
  686.         gameLocal.ServerSendInstanceReliableMessage( victim, -1, outMsg );    
  687.  
  688.         if( gameLocal.isListenServer && gameLocal.GetLocalPlayer() && victim && gameLocal.GetLocalPlayer()->GetInstance() == victim->GetInstance() )
  689.         {
  690.             // This is for listen servers, which won't get to ClientProcessReliableMessage
  691.             ReceiveDeathMessage( attacker, attacker ? playerState[ attacker->entityNumber ].fragCount : -1, victim, victim ? playerState[ victim->entityNumber ].fragCount : -1, methodOfDeath );
  692.         }
  693.     }
  694. }
  695.  
  696. /*
  697. ================
  698. idMultiplayerGame::ReceiveDeathMessage
  699. ================
  700. */
  701. void idMultiplayerGame::ReceiveDeathMessage( idPlayer *attacker, int attackerScore, idPlayer *victim, int victimScore, int methodOfDeath ) {
  702.     if( gameLocal.GetLocalPlayer() == NULL ) {
  703.         return;
  704.     }
  705.     
  706.     const char* icon = "";
  707.  
  708.     // if methodOfDeath is in range [0, MAX_WEAPONS - 1] it refers to a specific weapon. MAX_WEAPONS refers to
  709.     // a generic or unknown death (i.e. "Killer killed victim") and values above MAX_WEAPONS + 1 refer
  710.     // to other non-weapon deaths (i.e. telefrags)
  711.  
  712.     // setup to either use weapon icons for a weapon death, or generic death icons
  713.     if ( methodOfDeath < MAX_WEAPONS ) {
  714.         icon = va( "w%02d", methodOfDeath );
  715.     } else {
  716.         icon = va( "dm%d", methodOfDeath - MAX_WEAPONS );
  717.     }
  718.  
  719.     char* message = NULL;
  720.  
  721.     if ( gameLocal.IsTeamGame() ) {
  722.         message = va ( "%s%s ^r^i%s %s%s",    (attacker ? (attacker->team ? S_COLOR_STROGG : S_COLOR_MARINE) : ""), 
  723.                             (attacker ? gameLocal.userInfo[ attacker->entityNumber ].GetString( "ui_name" ) : ""), 
  724.                             icon,
  725.                             (victim ? (victim->team ? S_COLOR_STROGG : S_COLOR_MARINE) : ""), 
  726.                             (victim ? gameLocal.userInfo[ victim->entityNumber ].GetString( "ui_name" ) : "" ) );
  727.     } else {
  728.         message = va ( "%s ^r^i%s %s",     (attacker ? gameLocal.userInfo[ attacker->entityNumber ].GetString( "ui_name" ) : ""), 
  729.                                         icon,
  730.                                         (victim ? gameLocal.userInfo[ victim->entityNumber ].GetString( "ui_name" ) : "") );
  731.     }
  732.  
  733.     gameLocal.GetLocalPlayer()->mphud->SetStateString ( "deathinfo", message );
  734.     gameLocal.GetLocalPlayer()->mphud->HandleNamedEvent ( "addDeathLine" );
  735.  
  736.     // echo to console
  737.     gameLocal.Printf( gameLocal.GetLocalPlayer()->spawnArgs.GetString( va( "%s_text", icon ), "%s killed %s" ), 
  738.                     (victim ? gameLocal.userInfo[ victim->entityNumber ].GetString( "ui_name" ) : "world"),
  739.                     (attacker ? gameLocal.userInfo[ attacker->entityNumber ].GetString( "ui_name" ) : "world") );
  740.     gameLocal.Printf( "\n" );
  741.  
  742.     // display message on hud
  743.     if( attacker && victim && (gameLocal.GetLocalPlayer() == attacker || gameLocal.GetLocalPlayer() == victim) && attacker != victim && methodOfDeath < MAX_WEAPONS ) {
  744.         if( gameLocal.GetLocalPlayer() == attacker ) {
  745. // RAVEN BEGIN
  746. // rhummer: Added lang entries for "You fragged %s" and "You were fragged by %s"
  747.             (gameLocal.GetLocalPlayer())->GUIFragNotice( va( common->GetLocalizedString( "#str_107295" ), gameLocal.userInfo[ victim->entityNumber ].GetString( "ui_name" ) ) );
  748.         } else {
  749.             (gameLocal.GetLocalPlayer())->GUIFragNotice( va( common->GetLocalizedString( "#str_107296" ), gameLocal.userInfo[ attacker->entityNumber ].GetString( "ui_name" ) ) );
  750. // RAVEN END
  751.         }
  752.  
  753.         if( gameLocal.gameType == GAME_DM ) {
  754.             // we may not have gotten a snapshot with the current frag count, so see if there's an offset
  755.             bool tied = false;
  756.             if( gameLocal.GetLocalPlayer() == attacker ) {
  757.                 int rank = GetAdjustedPlayerRank( attacker, attackerScore - playerState[ attacker->entityNumber ].fragCount, tied );
  758.                 (gameLocal.GetLocalPlayer())->GUIMainNotice( GetPlayerRankText( rank, tied, attackerScore ) );        
  759.             } else {
  760.                 int rank = GetAdjustedPlayerRank( victim, victimScore - playerState[ victim->entityNumber ].fragCount, tied );
  761.                 (gameLocal.GetLocalPlayer())->GUIMainNotice( GetPlayerRankText( rank, tied, victimScore ) );        
  762.             }
  763.         }
  764.     }
  765. }
  766.  
  767.  
  768. // ddynerman: Gametype specific scoreboard
  769. /*
  770. ================
  771. idMultiplayerGame::UpdateScoreboard
  772. ================
  773. */
  774. void idMultiplayerGame::UpdateScoreboard( idUserInterface *scoreBoard ) {
  775.     scoreBoard->SetStateInt( "gametype", gameLocal.gameType );
  776.  
  777.     statManager->UpdateInGameHud( scoreBoard, true );
  778.  
  779. //RAVEN BEGIN
  780. //asalmon: xenon uses a different scoreboard
  781. #ifdef _XENON
  782.     UpdateXenonScoreboard(scoreBoard);
  783. #else
  784.     if( gameLocal.IsTeamGame() ) {
  785.         UpdateTeamScoreboard( scoreBoard );
  786.     } else {
  787.         UpdateDMScoreboard( scoreBoard );
  788.     }
  789. #endif
  790. //RAVEN END
  791.  
  792.     return;
  793. }
  794.  
  795. /*
  796. ================
  797. idMultiplayerGame::UpdateDMScoreboard
  798. ================
  799. */
  800. void idMultiplayerGame::UpdateDMScoreboard( idUserInterface *scoreBoard ) {
  801.     idPlayer* player = gameLocal.GetLocalPlayer();
  802.     int i;
  803.  
  804.     // bdube: mechanism for testing the scoreboard (populates it with fake names, pings, etc)
  805.     if ( g_testScoreboard.GetInteger() > 0 ) {
  806.         UpdateTestScoreboard ( scoreBoard );
  807.         return;
  808.     }
  809.  
  810.     if( !player ) {
  811.         return;
  812.     }
  813.  
  814.     scoreBoard->SetStateString( "scores_sel_0", "-1" );
  815.     scoreBoard->SetStateString( "spectator_scores_sel_0", "-1" );
  816.  
  817.     if( gameLocal.gameType == GAME_DM ) {
  818.         for ( i = 0; i < MAX_CLIENTS; i++ ) {
  819.             if( i < rankedPlayers.Num() ) {
  820.                 // ranked player
  821.                 idPlayer*    rankedPlayer    = rankedPlayers[ i ].First();
  822.                 int            rankedScore        = rankedPlayers[ i ].Second();
  823.  
  824.                 if ( rankedPlayer == gameLocal.GetLocalPlayer() ) {
  825.                     // highlight who we are
  826.                     scoreBoard->SetStateInt( "scores_sel_0", i );
  827.                 }
  828.  
  829.                 scoreBoard->SetStateString ( 
  830.                     va("scores_item_%i", i), 
  831.                     va("%s\t%s\t%s\t%s\t%i\t%i\t%i\t", 
  832.                     ( player->IsPlayerMuted( rankedPlayer ) ? I_VOICE_DISABLED : I_VOICE_ENABLED ),        // mute icon
  833.                     ( player->IsFriend( rankedPlayer ) ? I_FRIEND_ENABLED : I_FRIEND_DISABLED ),        // friend icon
  834.                     rankedPlayer->GetUserInfo()->GetString( "ui_name" ),                                // name
  835.                     rankedPlayer->GetUserInfo()->GetString( "ui_clan" ),                                // clan
  836.                     rankedScore,                                                                         // score
  837.                     GetPlayerTime( rankedPlayer ),                                                        // time
  838.                     playerState[ rankedPlayer->entityNumber ].ping ) );                                    // ping
  839.             } else {
  840.                 scoreBoard->SetStateString ( va("scores_item_%i", i), "" );
  841.                 scoreBoard->SetStateBool( va( "scores_item_%i_greyed", i ), false );
  842.             }
  843.  
  844.             if( i < unrankedPlayers.Num() ) {
  845.                 if ( unrankedPlayers[ i ] == gameLocal.GetLocalPlayer() ) {
  846.                     // highlight who we are
  847.                     scoreBoard->SetStateInt( "spectator_scores_sel_0", i );
  848.                 }
  849.  
  850.                 scoreBoard->SetStateString ( 
  851.                     va("spectator_scores_item_%i", i), 
  852.                     va("%s\t%s\t%s\t%s\t%s\t%i\t%i\t", 
  853.                     ( player->spectator && player->IsPlayerMuted( unrankedPlayers[ i ] ) ? I_VOICE_DISABLED : I_VOICE_ENABLED ), // mute icon
  854.                     ( player->IsFriend( unrankedPlayers[ i ] ) ? I_FRIEND_ENABLED : I_FRIEND_DISABLED ),    // friend icon
  855.                     unrankedPlayers[ i ]->GetUserInfo()->GetString( "ui_name" ),                            // name
  856.                     unrankedPlayers[ i ]->GetUserInfo()->GetString( "ui_clan" ),                            // clan
  857.                     "",                                                                                         // score
  858.                     GetPlayerTime( unrankedPlayers[ i ] ),                                                    // time
  859.                     playerState[ unrankedPlayers[ i ]->entityNumber ].ping ) );                                // ping
  860.             } else {
  861.                 scoreBoard->SetStateString ( va("spectator_scores_item_%i", i), "" );
  862.                 scoreBoard->SetStateBool( va( "scores_item_%i_greyed", i ), false );
  863.             }
  864.         }
  865.     } else if( gameLocal.gameType == GAME_TOURNEY ) {
  866.         // loop through twice listing players who are playing, then players who have been eliminated
  867.         int listIndex = 0;
  868.  
  869.  
  870.  
  871.         for ( i = 0; i < rankedPlayers.Num(); i++ ) {
  872.             // ranked player
  873.             idPlayer*    rankedPlayer    = rankedPlayers[ i ].First();
  874.             int            rankedScore        = rankedPlayers[ i ].Second();
  875.  
  876.             if( rankedPlayer->GetTourneyStatus() == PTS_ELIMINATED ) {
  877.                 continue;
  878.             }
  879.  
  880.             if ( rankedPlayer == gameLocal.GetLocalPlayer() ) {
  881.                 // highlight who we are
  882.                 scoreBoard->SetStateInt( "scores_sel_0", listIndex );
  883.             }
  884.  
  885.             scoreBoard->SetStateString ( 
  886.                 va("scores_item_%i", listIndex), 
  887.                 va("%s\t%s\t%s\t%s\t%i\t%i\t%s\t", 
  888.                 ( player->IsPlayerMuted( rankedPlayer ) ? I_VOICE_DISABLED : I_VOICE_ENABLED ),        // mute icon
  889.                 ( player->IsFriend( rankedPlayer ) ? I_FRIEND_ENABLED : I_FRIEND_DISABLED ),        // friend icon
  890.                 rankedPlayer->GetUserInfo()->GetString( "ui_name" ),                                // name
  891.                 rankedPlayer->GetUserInfo()->GetString( "ui_clan" ),                                // clan
  892.                 rankedScore,                                                                         // score
  893.                 playerState[ rankedPlayer->entityNumber ].ping,                                        // ping
  894.                 rankedPlayer->GetTextTourneyStatus() ) );                                            // tourney status
  895.             
  896.             scoreBoard->SetStateBool( va( "scores_item_%i_greyed", listIndex ), false );
  897.             listIndex++;
  898.         }
  899.  
  900.         for ( i = 0; i < rankedPlayers.Num(); i++ ) {
  901.             // ranked player
  902.             idPlayer*    rankedPlayer    = rankedPlayers[ i ].First();
  903.             int            rankedScore        = rankedPlayers[ i ].Second();
  904.  
  905.             if( rankedPlayer->GetTourneyStatus() != PTS_ELIMINATED ) {
  906.                 continue;
  907.             }
  908.  
  909.             if ( rankedPlayer == gameLocal.GetLocalPlayer() ) {
  910.                 // highlight who we are
  911.                 scoreBoard->SetStateInt( "scores_sel_0", listIndex );
  912.             }
  913.  
  914.             scoreBoard->SetStateString ( 
  915.                 va("scores_item_%i", listIndex), 
  916.                 va("%s\t%s\t%s\t%s\t%i\t%i\t%s\t", 
  917.                 ( player->IsPlayerMuted( rankedPlayer ) ? I_VOICE_DISABLED : I_VOICE_ENABLED ),        // mute icon
  918.                 ( player->IsFriend( rankedPlayer ) ? I_FRIEND_ENABLED : I_FRIEND_DISABLED ),        // friend icon
  919.                 rankedPlayer->GetUserInfo()->GetString( "ui_name" ),                                // name
  920.                 rankedPlayer->GetUserInfo()->GetString( "ui_clan" ),                                // clan
  921.                 rankedScore,                                                                         // score
  922.                 playerState[ rankedPlayer->entityNumber ].ping,                                        // ping
  923.                 rankedPlayer->GetTextTourneyStatus() ) );                                            // tourney status
  924.             
  925.             scoreBoard->SetStateBool( va( "scores_item_%i_greyed", listIndex ), true );
  926.             listIndex++;
  927.         }
  928.  
  929.         for( i = 0; i < MAX_CLIENTS; i++ ) {
  930.             if( i < unrankedPlayers.Num() ) {
  931.                 if ( unrankedPlayers[ i ] == gameLocal.GetLocalPlayer() ) {
  932.                     // highlight who we are
  933.                     scoreBoard->SetStateInt( "spectator_scores_sel_0", i );
  934.                 }
  935.  
  936.                 scoreBoard->SetStateString ( 
  937.                     va("spectator_scores_item_%i", i), 
  938.                     va("%s\t%s\t%s\t%s\t%s\t%i\t%s\t", 
  939.                     ( player->spectator && player->IsPlayerMuted( unrankedPlayers[ i ] ) ? I_VOICE_DISABLED : I_VOICE_ENABLED ), // mute icon
  940.                     ( player->IsFriend( unrankedPlayers[ i ] ) ? I_FRIEND_ENABLED : I_FRIEND_DISABLED ),    // friend icon
  941.                     unrankedPlayers[ i ]->GetUserInfo()->GetString( "ui_name" ),                            // name
  942.                     unrankedPlayers[ i ]->GetUserInfo()->GetString( "ui_clan" ),                            // clan
  943.                     "",                                                                                         // score
  944.                     playerState[ unrankedPlayers[ i ]->entityNumber ].ping,                                    // ping
  945.                     "" ) );    
  946.             } else {
  947.                 scoreBoard->SetStateString( va( "spectator_scores_item_%i", i ), "" );
  948.             }
  949.         }
  950.  
  951.         for( i = listIndex; i < MAX_CLIENTS; i++ ) {
  952.             scoreBoard->SetStateString( va( "scores_item_%i", i ), "" );
  953.             scoreBoard->SetStateBool( va( "scores_item_%i_greyed", i ), false );
  954.         }
  955.     }
  956.  
  957.     scoreBoard->SetStateInt ( "num_players", rankedPlayers.Num() );
  958.     scoreBoard->SetStateInt ( "num_spec_players", unrankedPlayers.Num() );
  959.  
  960.     idStr serverAddress = networkSystem->GetServerAddress();
  961.  
  962.     scoreBoard->SetStateString( "servername", gameLocal.serverInfo.GetString( "si_name" ) );
  963.  
  964.     scoreBoard->SetStateString( "position_text", GetPlayerRankText( gameLocal.GetLocalPlayer() ) );
  965.     // shouchard:  added map name
  966.     // mekberg: localized string
  967.     const char *mapName = gameLocal.serverInfo.GetString( "si_map" );
  968.     const idDeclEntityDef *mapDef = static_cast<const idDeclEntityDef *>(declManager->FindType( DECL_MAPDEF, mapName, false ));
  969.     if ( mapDef ) {
  970.         mapName = common->GetLocalizedString( mapDef->dict.GetString( "name", mapName ) );
  971.     }
  972.     scoreBoard->SetStateString( "servermap", mapName );
  973.     scoreBoard->SetStateString( "serverip",    serverAddress.c_str() );
  974.     scoreBoard->SetStateString( "servergametype", GetLongGametypeName( gameLocal.serverInfo.GetString( "si_gameType" ) ) );
  975.     scoreBoard->SetStateString( "servertimelimit", va( "%s: %d", common->GetLocalizedString( "#str_107659" ), gameLocal.serverInfo.GetInt( "si_timeLimit" ) ) );
  976.     scoreBoard->SetStateString( "serverlimit", va( "%s: %d", common->GetLocalizedString( "#str_107660" ), gameLocal.serverInfo.GetInt( "si_fragLimit" ) ) );
  977.  
  978.     int timeLimit = gameLocal.serverInfo.GetInt( "si_timeLimit" );
  979.     mpGameState_t state = gameState->GetMPGameState();
  980.  
  981.     bool inNonTimedState = (state == SUDDENDEATH) || (state == WARMUP) || (state == GAMEREVIEW);
  982.  
  983.     if( gameLocal.gameType == GAME_TOURNEY ) {
  984.         if( gameLocal.serverInfo.GetInt( "si_fragLimit" ) == 1 ) {
  985.             // stupid english plurals
  986.             scoreBoard->SetStateString( "tourney_frag_count", va( common->GetLocalizedString( "#str_107712" ), gameLocal.serverInfo.GetInt( "si_fragLimit" ) ) );
  987.         } else {
  988.             scoreBoard->SetStateString( "tourney_frag_count", va( common->GetLocalizedString( "#str_107715" ), gameLocal.serverInfo.GetInt( "si_fragLimit" ) ) );
  989.         }
  990.         
  991.         scoreBoard->SetStateString( "tourney_count", va( common->GetLocalizedString( "#str_107713" ), ((rvTourneyGameState*)gameState)->GetTourneyCount(), gameLocal.serverInfo.GetInt( "si_tourneyLimit" ) ) );
  992.         if( gameLocal.GetLocalPlayer() ) {
  993.             inNonTimedState |= ((rvTourneyGameState*)gameState)->GetArena( gameLocal.GetLocalPlayer()->GetArena() ).GetState() == AS_SUDDEN_DEATH;
  994.         }
  995.     }
  996.  
  997.     scoreBoard->SetStateString( "timeleft", GameTime() );
  998.  
  999.     scoreBoard->SetStateBool( "infinity", ( !timeLimit && state != COUNTDOWN ) || inNonTimedState );
  1000.  
  1001.     scoreBoard->StateChanged ( gameLocal.time );
  1002.     scoreBoard->Redraw( gameLocal.time );
  1003. }
  1004.  
  1005. /*
  1006. ================
  1007. idMultiplayerGame::UpdateTeamScoreboard
  1008. ================
  1009. */
  1010. void idMultiplayerGame::UpdateTeamScoreboard( idUserInterface *scoreBoard ) {
  1011.     idStr    gameinfo;
  1012.     int        numTeamEntries[ TEAM_MAX ];
  1013.     idPlayer* player = gameLocal.GetLocalPlayer();
  1014.  
  1015.     // bdube: mechanism for testing the scoreboard (populates it with fake names, pings, etc)
  1016.     if ( g_testScoreboard.GetInteger() > 0 ) {
  1017.         UpdateTestScoreboard ( scoreBoard );
  1018.         return;
  1019.     }
  1020.     
  1021.     if( !player ) {
  1022.         return;
  1023.     }
  1024.  
  1025.     SIMDProcessor->Memset( numTeamEntries, 0, sizeof( int ) * TEAM_MAX );
  1026.  
  1027.     scoreBoard->SetStateString( "team_0_scores_sel_0", "-1" );
  1028.     scoreBoard->SetStateString( "team_1_scores_sel_0", "-1" );
  1029.     scoreBoard->SetStateString( "spectator_scores_sel_0", "-1" );
  1030.  
  1031.     for ( int i = 0; i < MAX_CLIENTS; i++ ) {
  1032.         if( i < rankedPlayers.Num() ) {
  1033.             // ranked player
  1034.             idPlayer*    rankedPlayer    = rankedPlayers[ i ].First();
  1035.             int            rankedScore        = rankedPlayers[ i ].Second();
  1036.  
  1037.             if ( rankedPlayer == gameLocal.GetLocalPlayer() ) {
  1038.                 // highlight who we are
  1039.                 scoreBoard->SetStateInt( va("team_%i_scores_sel_0", rankedPlayer->team ), numTeamEntries[ rankedPlayer->team ] ); 
  1040.             }
  1041.  
  1042. // RAVEN BEGIN
  1043. // mekberg: redid this
  1044.             if ( gameLocal.gameType == GAME_TDM ) {
  1045.                 scoreBoard->SetStateString ( 
  1046.                 va("team_%i_scores_item_%i", rankedPlayer->team, numTeamEntries[ rankedPlayer->team ]), 
  1047.                 va("%s\t%s\t%s\t%s\t%i\t%i\t%i\t", 
  1048.                 ( player->team == rankedPlayer->team && player->IsPlayerMuted( rankedPlayer ) ? I_VOICE_DISABLED : I_VOICE_ENABLED ), // mute icon
  1049.                 ( player->IsFriend( rankedPlayer ) ? I_FRIEND_ENABLED : I_FRIEND_DISABLED ),    // friend icon
  1050.                 rankedPlayer->GetUserInfo()->GetString( "ui_name" ),                            // name
  1051.                 rankedPlayer->GetUserInfo()->GetString( "ui_clan" ),                            // clan
  1052.                 rankedScore,                                                                     // score                                    
  1053.                 GetPlayerTime( rankedPlayer ),                                                    // time
  1054.                 playerState[ rankedPlayer->entityNumber ].ping ) );                                // ping
  1055.                 numTeamEntries[ rankedPlayer->team ]++;
  1056.             } else {
  1057.                 // mekberg: made this check slightly more sane.
  1058.                 const char* flagString = "";
  1059.                 if ( rankedPlayer->PowerUpActive( rankedPlayer->team ? POWERUP_CTF_MARINEFLAG : POWERUP_CTF_STROGGFLAG ) ) {
  1060.                     flagString = ( rankedPlayer->team ? I_FLAG_MARINE : I_FLAG_STROGG );
  1061.                 } else if ( gameLocal.gameType == GAME_ARENA_CTF && gameLocal.GetLocalPlayer() && rankedPlayer->team == gameLocal.GetLocalPlayer()->team ) {
  1062.                     flagString = rankedPlayer->GetArenaPowerupString( );
  1063.                 }
  1064.                 scoreBoard->SetStateString ( 
  1065.                 va("team_%i_scores_item_%i", rankedPlayer->team, numTeamEntries[ rankedPlayer->team ]), 
  1066.                 va("%s\t%s\t%s\t%s\t%s\t%i\t%i\t%i\t%i\t", 
  1067.                 ( player->team == rankedPlayer->team && player->IsPlayerMuted( rankedPlayer ) ? I_VOICE_DISABLED : I_VOICE_ENABLED ), // mute icon
  1068.                 ( player->IsFriend( rankedPlayer ) ? I_FRIEND_ENABLED : I_FRIEND_DISABLED ),    // friend icon
  1069.                 flagString,                                                                        // shouchard: twhitaker: updated steve's original flag system 
  1070.                 rankedPlayer->GetUserInfo()->GetString( "ui_name" ),                            // name
  1071.                 rankedPlayer->GetUserInfo()->GetString( "ui_clan" ),                            // clan
  1072.                 rankedScore,                                                                     // score    
  1073.                 playerState[ rankedPlayer->entityNumber ].fragCount,                            // kills                    
  1074.                 GetPlayerTime( rankedPlayer ),                                                    // time
  1075.                 playerState[ rankedPlayer->entityNumber ].ping ) );                                // ping
  1076.                 numTeamEntries[ rankedPlayer->team ]++;
  1077.             }
  1078. // RAVEN END
  1079.         }
  1080.  
  1081.         if( i < unrankedPlayers.Num() ) {
  1082.             if ( unrankedPlayers[ i ] == gameLocal.GetLocalPlayer() ) {
  1083.                 // highlight who we are
  1084.                 scoreBoard->SetStateInt( "spectator_scores_sel_0", i );
  1085.             }
  1086.  
  1087. // RAVEN BEGIN
  1088. // mekberg: redid this
  1089.             scoreBoard->SetStateString ( 
  1090.             va("spectator_scores_item_%i", i), 
  1091.             va("%s\t%s\t%s\t%s\t%s\t%i\t%i\t", 
  1092.             ( player->spectating && player->IsPlayerMuted( unrankedPlayers[ i ] ) ? I_VOICE_DISABLED : I_VOICE_ENABLED ), // mute icon
  1093.             ( player->IsFriend( unrankedPlayers[ i ] ) ? I_FRIEND_ENABLED : I_FRIEND_DISABLED ),    // friend icon
  1094.             unrankedPlayers[ i ]->GetUserInfo()->GetString( "ui_name" ),                            // name
  1095.             unrankedPlayers[ i ]->GetUserInfo()->GetString( "ui_clan" ),                            // clan
  1096.             "",                                                                                        // score
  1097.             GetPlayerTime( unrankedPlayers[ i ] ),                                                    // time
  1098.             playerState[ unrankedPlayers[ i ]->entityNumber ].ping ) );                                // ping                            // ping
  1099. // RAVEN END
  1100.  
  1101.         } else {
  1102.             scoreBoard->SetStateString ( va("spectator_scores_item_%i", i), "" );
  1103.         }
  1104.     }
  1105.  
  1106.     // clear unused space
  1107.     for( int k = 0; k < TEAM_MAX; k++ ) {
  1108.         for( int i = numTeamEntries[ k ]; i < MAX_CLIENTS; i++ ) {
  1109.             scoreBoard->SetStateString ( va("team_%i_scores_item_%i", k, i), "" );
  1110.         }
  1111.     }
  1112.  
  1113.     scoreBoard->SetStateInt ( "playerteam", gameLocal.GetLocalPlayer()->team );
  1114.  
  1115.     scoreBoard->SetStateInt ( "strogg_score", teamScore[ TEAM_STROGG ] );
  1116.     scoreBoard->SetStateInt ( "marine_score", teamScore[ TEAM_MARINE ] );
  1117.     scoreBoard->SetStateInt ( "num_strogg_players", numTeamEntries[ TEAM_STROGG ] );
  1118.     scoreBoard->SetStateInt ( "num_marine_players", numTeamEntries[ TEAM_MARINE ] );
  1119.     scoreBoard->SetStateInt ( "num_players", numTeamEntries[ TEAM_STROGG ] + numTeamEntries[ TEAM_MARINE ] );
  1120.     scoreBoard->SetStateInt ( "num_spec_players", unrankedPlayers.Num() );
  1121.  
  1122.     idStr serverAddress = networkSystem->GetServerAddress();
  1123.  
  1124.     scoreBoard->SetStateString( "servername", gameLocal.serverInfo.GetString( "si_name" ) );
  1125. // RAVEN BEGIN
  1126. // shouchard:  added map name
  1127. // mekberg: get localized string.
  1128.     const char *mapName = gameLocal.serverInfo.GetString( "si_map" );
  1129.     const idDeclEntityDef *mapDef = static_cast<const idDeclEntityDef *>(declManager->FindType( DECL_MAPDEF, mapName, false ));
  1130.     if ( mapDef ) {
  1131.         mapName = common->GetLocalizedString( mapDef->dict.GetString( "name", mapName ) );
  1132.     }
  1133.     scoreBoard->SetStateString( "servermap", mapName );
  1134. // RAVEN END
  1135.     scoreBoard->SetStateString( "serverip",    serverAddress.c_str() );
  1136.     scoreBoard->SetStateString( "servergametype", GetLongGametypeName( gameLocal.serverInfo.GetString( "si_gameType" ) ) );
  1137.     scoreBoard->SetStateString( "servertimelimit", va( "%s: %d", common->GetLocalizedString( "#str_107659" ), gameLocal.serverInfo.GetInt( "si_timeLimit" ) ) );
  1138.     if ( gameLocal.IsFlagGameType() ) {
  1139.         scoreBoard->SetStateString( "serverlimit", va( "%s: %d", common->GetLocalizedString( "#str_107661" ), gameLocal.serverInfo.GetInt( "si_captureLimit" ) ) );
  1140.     } else {
  1141.         scoreBoard->SetStateString( "serverlimit", va( "%s: %d", common->GetLocalizedString( "#str_107660" ), gameLocal.serverInfo.GetInt( "si_fragLimit" ) ) );        
  1142.     }
  1143.  
  1144.     scoreBoard->SetStateString( "timeleft", GameTime() );
  1145.  
  1146.     int timeLimit = gameLocal.serverInfo.GetInt( "si_timeLimit" );
  1147.     mpGameState_t state = gameState->GetMPGameState();
  1148.     scoreBoard->SetStateBool( "infinity", ( !timeLimit && state != COUNTDOWN ) || state == WARMUP || state == GAMEREVIEW || state == SUDDENDEATH );
  1149.  
  1150.     scoreBoard->StateChanged ( gameLocal.time );
  1151.     scoreBoard->Redraw( gameLocal.time );
  1152. }
  1153.  
  1154. /*
  1155. ================
  1156. idMultiplayerGame::BuildSummaryListString
  1157. Returns a summary string for the specified player
  1158. ================
  1159. */
  1160. const char* idMultiplayerGame::BuildSummaryListString( idPlayer* player, int rankedScore ) {
  1161.     // track top 3 accuracies
  1162.     rvPlayerStat* stat = statManager->GetPlayerStat( player->entityNumber );
  1163.     idList<rvPair<int, float> > bestAccuracies;
  1164.  
  1165.     for( int j = 0; j < MAX_WEAPONS; j++ ) {
  1166.         // only consider weapons we fired more than a few shots
  1167.         if( stat->weaponShots[ j ] <= 10 ) {
  1168.             continue;
  1169.         }
  1170.  
  1171.         float accuracy = (float)stat->weaponHits[ j ] / (float)stat->weaponShots[ j ];
  1172.         bestAccuracies.Append( rvPair<int, float>( j, accuracy ) );
  1173.     }
  1174.  
  1175.     bestAccuracies.Sort( rvPair<int, float>::rvPairSecondCompareDirect );
  1176.  
  1177.     // hold upto 3 top weapons at 5 chars each
  1178.     idStr weaponString;
  1179.     for( int j = 0; j < 3; j++ ) {
  1180.         if( j >= bestAccuracies.Num() ) {
  1181.             continue;
  1182.         }
  1183.  
  1184.         weaponString += va( "^iw%02d", bestAccuracies[ j ].First() );
  1185.     }
  1186.  
  1187.     return     va("%d. %s\t%s\t%d\t%s\t", 
  1188.             player->GetRank() + 1,
  1189.             player->GetUserInfo()->GetString( "ui_name" ),                                // name
  1190.             player->GetUserInfo()->GetString( "ui_clan" ),                                // clan
  1191.             rankedScore,                                                                // score
  1192.             weaponString.c_str() );
  1193. }
  1194.  
  1195. /*
  1196. ================
  1197. idMultiplayerGame::UpdateSummaryBoard
  1198. Shows top 10 players if local player is in top 10, otherwise shows top 9 and localplayer
  1199. ================
  1200. */
  1201. void idMultiplayerGame::UpdateSummaryBoard( idUserInterface *scoreBoard ) {
  1202.     idPlayer* player = gameLocal.GetLocalPlayer();
  1203.  
  1204.     if( !player ) {
  1205.         return;
  1206.     }
  1207.  
  1208.     int playerIndex = -1;
  1209.  
  1210.     // update our ranks in case we call this the same frame it happens
  1211.     UpdatePlayerRanks();
  1212.  
  1213.     // highlight top 3 players
  1214.     idVec4 blueHighlight = idStr::ColorForIndex( C_COLOR_BLUE );
  1215.     idVec4 redHighlight = idStr::ColorForIndex( C_COLOR_RED );
  1216.     idVec4 yellowHighlight = idStr::ColorForIndex( C_COLOR_YELLOW );
  1217.     blueHighlight[ 3 ] = 0.15f;
  1218.     redHighlight[ 3 ] = 0.15f;
  1219.     yellowHighlight[ 3 ] = 0.15f;
  1220.  
  1221.     if( gameLocal.IsTeamGame() ) {
  1222.         scoreBoard->HandleNamedEvent( teamScore[ TEAM_MARINE ] > teamScore[ TEAM_STROGG ] ? "marine_wins" : "strogg_wins" );
  1223.         // summary is top 5 players on each team
  1224.         int lastHighIndices[ TEAM_MAX ];
  1225.         memset( lastHighIndices, 0, sizeof( int ) * TEAM_MAX );
  1226.  
  1227.         for( int i = 0; i < 5; i++ ) {
  1228.             scoreBoard->SetStateString ( va( "%s_item_%i", "summary_marine_names", i ), "" );
  1229.             scoreBoard->SetStateString ( va( "%s_item_%i", "summary_strogg_names", i ), "" );
  1230.         }
  1231.  
  1232.         for( int i = 0; i < TEAM_MAX; i++ ) {
  1233.             for( int j = 0; j < 5; j++ ) {
  1234.                 idPlayer*    rankedPlayer    = NULL;
  1235.                 int            rankedScore        = 0;
  1236.                 int k;
  1237.                 for( k = lastHighIndices[ i ]; k < rankedPlayers.Num(); k++ ) {
  1238.                     if( rankedPlayers[ k ].First()->team == i ) {
  1239.                         rankedPlayer = rankedPlayers[ k ].First();
  1240.                         rankedScore = rankedPlayers[ k ].Second();
  1241.                         break;    
  1242.                     }
  1243.                 }
  1244.  
  1245.                 // no more teammates
  1246.                 if( k >= rankedPlayers.Num() ) {
  1247.                     break;
  1248.                 }
  1249.                 
  1250.                 if( j == 4 && playerIndex == -1 && player->team == i ) {
  1251.                     int z;
  1252.                     for( z = 0; z < rankedPlayers.Num(); z++ ) {
  1253.                         if( rankedPlayers[ z ].First() == player ) {
  1254.                             rankedPlayer = player;
  1255.                             rankedScore = rankedPlayers[ z ].Second();
  1256.                             break;
  1257.                         }
  1258.                     }
  1259.                 }
  1260.                 
  1261.                 if ( rankedPlayer == gameLocal.GetLocalPlayer() ) {
  1262.                     // highlight who we are
  1263.                     playerIndex = i;
  1264.                 }
  1265.  
  1266.                 scoreBoard->SetStateString ( va( "%s_item_%i", i == TEAM_MARINE ? "summary_marine_names" : "summary_strogg_names", j ), BuildSummaryListString( rankedPlayer, rankedScore ) );
  1267.  
  1268.                 lastHighIndices[ i ] = k + 1;
  1269.             }
  1270.         }
  1271.  
  1272.         if( playerIndex > 0 ) {
  1273.             if( player->team == TEAM_MARINE ) {
  1274.                 scoreBoard->SetStateInt( "summary_marine_names_sel_0", playerIndex );     
  1275.                 scoreBoard->SetStateInt( "summary_strogg_names_sel_0", -1 );     
  1276.             } else {
  1277.                 scoreBoard->SetStateInt( "summary_strogg_names_sel_0", playerIndex );     
  1278.                 scoreBoard->SetStateInt( "summary_marine_names_sel_0", -1 );     
  1279.             }
  1280.         } else {
  1281.             scoreBoard->SetStateInt( "summary_marine_names_sel_0", -1 );     
  1282.             scoreBoard->SetStateInt( "summary_strogg_names_sel_0", -1 );     
  1283.         }
  1284.     } else {
  1285.         for ( int i = 0; i < 10; i++ ) {
  1286.  
  1287.             // mekberg: delete old highlights
  1288.             scoreBoard->DeleteStateVar( va( "summary_names_item_%d_highlight", i ) );
  1289.  
  1290.             if( i < rankedPlayers.Num() ) {
  1291.                 // ranked player
  1292.                 idPlayer*    rankedPlayer    = rankedPlayers[ i ].First();
  1293.                 int            rankedScore        = rankedPlayers[ i ].Second();
  1294.  
  1295.                 if( i == 9 && playerIndex == -1 ) {
  1296.                     // if the player is ranked, substitute them in
  1297.                     int i;
  1298.                     for( i = 0; i < rankedPlayers.Num(); i++ ) {
  1299.                         if( rankedPlayers[ i ].First() == player ) {
  1300.                             rankedPlayer = player;
  1301.                             rankedScore = rankedPlayers[ i ].Second();
  1302.                             break;
  1303.                         }
  1304.                     }
  1305.                 }
  1306.  
  1307.                 if ( rankedPlayer == gameLocal.GetLocalPlayer() ) {
  1308.                     // highlight who we are
  1309.                     playerIndex = i;
  1310.                 }
  1311.     
  1312.                 scoreBoard->SetStateString ( va( "%s_item_%i", "summary_names", i ), BuildSummaryListString( rankedPlayer, rankedScore ) );
  1313.  
  1314.                 if( rankedPlayer->GetRank() == 0 ) {
  1315.                     scoreBoard->SetStateVec4( va( "summary_names_item_%d_highlight", i ), blueHighlight );
  1316.                 } else if( rankedPlayer->GetRank() == 1 ) {
  1317.                     scoreBoard->SetStateVec4( va( "summary_names_item_%d_highlight", i ), redHighlight );
  1318.                 } else if( rankedPlayer->GetRank() == 2 ) {
  1319.                     scoreBoard->SetStateVec4( va( "summary_names_item_%d_highlight", i ), yellowHighlight );
  1320.                 }
  1321.             } else {
  1322.                 scoreBoard->SetStateString ( va("summary_names_item_%i", i), "" );
  1323.             }
  1324.         }
  1325.  
  1326.         // highlight who we are (only if not ranked in the top 3)
  1327.         if( player->GetRank() >= 0 && player->GetRank() < 3 ) {
  1328.             scoreBoard->SetStateInt( "summary_names_sel_0", -1 ); 
  1329.         } else {
  1330.             scoreBoard->SetStateInt( "summary_names_sel_0", playerIndex ); 
  1331.         }
  1332.     } 
  1333.  
  1334.  
  1335.     scoreBoard->StateChanged ( gameLocal.time );
  1336.     scoreBoard->Redraw( gameLocal.time );
  1337. }
  1338.  
  1339. /*
  1340. ================
  1341. idMultiplayerGame::UpdateTestScoreboard
  1342. ================
  1343. */
  1344. void idMultiplayerGame::UpdateTestScoreboard ( idUserInterface *scoreBoard ) {
  1345.     int i;
  1346.  
  1347.     gameLocal.random.SetSeed ( g_testScoreboard.GetInteger ( ) );
  1348.  
  1349.     if( gameLocal.IsTeamGame() ) {
  1350.         for ( i = 0; i < MAX_CLIENTS && i < g_testScoreboard.GetInteger ( ); i ++ ) {
  1351.             idStr name = va("Player %d", i + 1 );
  1352.             name = va("%s\t%i\t%i", name.c_str(), 
  1353.                                      gameLocal.random.RandomInt ( 50 ), 
  1354.                                      gameLocal.random.RandomInt ( 10 ));
  1355.             scoreBoard->SetStateString ( va("team_0_scores_item_%i", i), name );
  1356.         }
  1357.         while ( i < MAX_CLIENTS ) {
  1358.             scoreBoard->SetStateString ( va("team_0_scores_item_%i", i), "" );
  1359.             i++;
  1360.         }
  1361.         for ( i = 0; i < MAX_CLIENTS && i < g_testScoreboard.GetInteger ( ); i ++ ) {
  1362.             idStr name = va("Player %d", i + 1 );
  1363.             name = va("%s\t%i\t%i", name.c_str(), 
  1364.                                      gameLocal.random.RandomInt ( 50 ), 
  1365.                                      gameLocal.random.RandomInt ( 10 ));
  1366.             scoreBoard->SetStateString ( va("team_1_scores_item_%i", i), name );
  1367.         }
  1368.         while ( i < MAX_CLIENTS ) {
  1369.             scoreBoard->SetStateString ( va("team_1_scores_item_%i", i), "" );
  1370.             i++;
  1371.         }
  1372.  
  1373.         scoreBoard->SetStateInt ( "strogg_score", gameLocal.random.RandomInt ( 10 ) );
  1374.         scoreBoard->SetStateInt ( "marine_score", gameLocal.random.RandomInt ( 10 ) );
  1375.     } else {
  1376.         for ( i = 0; i < MAX_CLIENTS && i < g_testScoreboard.GetInteger ( ); i ++ ) {
  1377.             idStr name = va("Player %d", i + 1 );        
  1378.  
  1379.             scoreBoard->SetStateString ( 
  1380.                 va("scores_item_%i", i), 
  1381.                 va("%s\t%s\t%s\t%s\t%s\t%s\t%i\t%i\t%i\t", 
  1382.                 ( gameLocal.random.RandomInt() % 2 ? I_VOICE_DISABLED : I_VOICE_ENABLED ),        // mute icon
  1383.                 ( gameLocal.random.RandomInt() % 2 ? I_FRIEND_ENABLED : I_FRIEND_DISABLED ),        // friend icon
  1384.                 "",                                                                                    // shouchard:  flag
  1385.                 name.c_str(),                                // name
  1386.                 "Clan",                                // clan
  1387.                 "",                                                                                    // team score (unused in DM)
  1388.                 gameLocal.random.RandomInt ( 50 ),                                                                         // score
  1389.                 gameLocal.random.RandomInt ( 10 ),                                                        // time
  1390.                 gameLocal.random.RandomInt ( 300 ) + 20 ) );        
  1391.  
  1392.  
  1393.         }
  1394.         // clear remaining lines (empty slots)    
  1395.         while ( i < MAX_CLIENTS ) {
  1396.             scoreBoard->SetStateString ( va("scores_item_%i", i), "" );
  1397.             i++;
  1398.         }
  1399.     }
  1400.  
  1401.     scoreBoard->SetStateInt ( "num_marine_players", g_testScoreboard.GetInteger() );
  1402.     scoreBoard->SetStateInt ( "num_strogg_players", g_testScoreboard.GetInteger() );
  1403.     scoreBoard->SetStateInt ( "num_players", g_testScoreboard.GetInteger() );
  1404.  
  1405.     scoreBoard->SetStateInt( "rank_self", 2 );
  1406.     scoreBoard->SetStateInt ( "playercount", g_testScoreboard.GetInteger ( ) );
  1407.  
  1408.     scoreBoard->StateChanged ( gameLocal.time  );
  1409.     scoreBoard->SetStateString( "gameinfo", va( "Game Type:%s     Frag Limit:%i     Time Limit:%i", gameLocal.serverInfo.GetString( "si_gameType" ), gameLocal.serverInfo.GetInt( "si_fragLimit" ), gameLocal.serverInfo.GetInt( "si_timeLimit" ) ) );
  1410.     scoreBoard->Redraw( gameLocal.time );
  1411. }
  1412. // RAVEN END
  1413.  
  1414. /*
  1415. ================
  1416. idMultiplayerGame::GameTime
  1417. ================
  1418. */
  1419. const char *idMultiplayerGame::GameTime( void ) {
  1420.     static char buff[16];
  1421.     int m, s, t, ms;
  1422.  
  1423.     bool inCountdown = false;
  1424.  
  1425.     ms = 0;
  1426.     if( gameState->GetMPGameState() == COUNTDOWN ) {
  1427.         inCountdown = true;
  1428.         ms = gameState->GetNextMPGameStateTime() - gameLocal.realClientTime;
  1429.     } else if( gameLocal.GetLocalPlayer() && gameLocal.gameType == GAME_TOURNEY && ((rvTourneyGameState*)gameState)->GetArena( gameLocal.GetLocalPlayer()->GetArena() ).GetState() == AS_WARMUP ) {
  1430.         inCountdown = true;
  1431.         ms = ((rvTourneyGameState*)gameState)->GetArena( gameLocal.GetLocalPlayer()->GetArena() ).GetNextStateTime() - gameLocal.realClientTime;
  1432.     }
  1433.     if ( inCountdown ) {
  1434.         s = ms / 1000 + 1;
  1435.         if ( ms <= 0 ) {
  1436.             // in tourney mode use a different string since warmups happen before each round
  1437.             // (not really before the overall game)
  1438.             strcpy( buff, va( "%s --", (gameState->GetMPGameState() == COUNTDOWN && gameLocal.gameType == GAME_TOURNEY) ? common->GetLocalizedString( "#str_107721" ) : common->GetLocalizedString( "#str_107706" ) ) );
  1439.         } else {
  1440.             sprintf( buff, "%s %i", (gameState->GetMPGameState() == COUNTDOWN && gameLocal.gameType == GAME_TOURNEY) ? common->GetLocalizedString( "#str_107721" ) : common->GetLocalizedString( "#str_107706" ), s );
  1441.         }
  1442.     } else {
  1443.         int timeLimit = gameLocal.serverInfo.GetInt( "si_timeLimit" );
  1444.         int startTime = matchStartedTime;
  1445.         if( gameLocal.gameType == GAME_TOURNEY ) {
  1446.             if( gameLocal.GetLocalPlayer() ) {
  1447.                 startTime = ((rvTourneyGameState*)gameState)->GetArena( gameLocal.GetLocalPlayer()->GetArena() ).GetMatchStartTime();
  1448.             }
  1449.         }
  1450.         if ( timeLimit ) {
  1451.             ms = ( timeLimit * 60000 ) - ( gameLocal.time - startTime );
  1452.         } else {
  1453.             ms = gameLocal.time - startTime;
  1454.         }
  1455.         if ( ms < 0 ) {
  1456.             ms = 0;
  1457.         }
  1458.     
  1459.         s = ms / 1000;
  1460.         m = s / 60;
  1461.         s -= m * 60;
  1462.         t = s / 10;
  1463.         s -= t * 10;
  1464.  
  1465.         sprintf( buff, "%i:%i%i", m, t, s );
  1466.     }
  1467.     return &buff[0];
  1468. }
  1469.  
  1470. /*
  1471. ================
  1472. idMultiplayerGame::NumActualClients
  1473. ================
  1474. */
  1475. int idMultiplayerGame::NumActualClients( bool countSpectators, int *teamcounts ) {
  1476.     idPlayer *p;
  1477.     int c = 0;
  1478.  
  1479.     if ( teamcounts ) {
  1480.         teamcounts[ 0 ] = teamcounts[ 1 ] = 0;
  1481.     }
  1482.     for( int i = 0 ; i < gameLocal.numClients ; i++ ) {
  1483.         idEntity *ent = gameLocal.entities[ i ];
  1484. // RAVEN BEGIN
  1485. // jnewquist: Use accessor for static class type 
  1486.         if ( !ent || !ent->IsType( idPlayer::GetClassType() ) ) {
  1487. // RAVEN END
  1488.             continue;
  1489.         }
  1490.         p = static_cast< idPlayer * >( ent );
  1491.         if ( countSpectators || CanPlay( p ) ) {
  1492.             c++;
  1493.         }
  1494.         if ( teamcounts && CanPlay( p ) ) {
  1495.             teamcounts[ p->team ]++;
  1496.         }
  1497.     }
  1498.     return c;
  1499. }
  1500.  
  1501. /*
  1502. ================
  1503. idMultiplayerGame::EnoughClientsToPlay
  1504. ================
  1505. */
  1506. bool idMultiplayerGame::EnoughClientsToPlay() {
  1507.     int team[ 2 ];
  1508.     int clients = NumActualClients( false, &team[ 0 ] );
  1509.     if ( gameLocal.IsTeamGame() ) {
  1510.         return clients >= 2 && team[ 0 ] && team[ 1 ];
  1511.     } else {
  1512.         return clients >= 2;
  1513.     }
  1514. }
  1515.  
  1516. /*
  1517. ================
  1518. idMultiplayerGame::AllPlayersReady
  1519. ================
  1520. */
  1521. bool idMultiplayerGame::AllPlayersReady( idStr* reason ) {
  1522.     int            i, minClients, numClients;
  1523.     idEntity    *ent;
  1524.     idPlayer    *p;
  1525.     int            team[ 2 ];
  1526.     bool        notReady;
  1527.  
  1528.     notReady = false;
  1529.     
  1530.     minClients = Max( 2, gameLocal.serverInfo.GetInt( "si_minPlayers" ) );
  1531.     numClients = NumActualClients( false, &team[ 0 ] );
  1532.     if ( numClients < minClients ) { 
  1533.         if( reason ) {
  1534.             // stupid english plurals
  1535.             if( minClients == 2 ) {
  1536.                 *reason = common->GetLocalizedString( "#str_107674" );
  1537.             } else {
  1538.                 *reason = va( common->GetLocalizedString( "#str_107732" ), minClients - numClients );
  1539.             }
  1540.             
  1541.         }
  1542.     
  1543.         return false;
  1544.     }
  1545.  
  1546.     if ( gameLocal.IsTeamGame() ) {
  1547.         if ( !team[ 0 ] || !team[ 1 ] ) {
  1548.             if( reason ) {
  1549.                 *reason = common->GetLocalizedString( "#str_107675" );
  1550.             }
  1551.     
  1552.             return false;
  1553.         }
  1554.     }
  1555.  
  1556.     for( i = 0; i < gameLocal.numClients; i++ ) {
  1557.         ent = gameLocal.entities[ i ];
  1558.  
  1559.         if ( !ent || !ent->IsType( idPlayer::GetClassType() ) ) {
  1560.             continue;
  1561.         }
  1562.  
  1563.         p = static_cast< idPlayer * >( ent );
  1564.  
  1565.         if ( CanPlay( p ) && !p->IsReady() ) {
  1566.             notReady = true;
  1567.         }
  1568.         team[ p->team ]++;
  1569.     }
  1570.  
  1571.     if( notReady ) {
  1572.         if( reason ) {
  1573.             if( gameLocal.GetLocalPlayer() && gameLocal.GetLocalPlayer()->IsReady() ) {
  1574.                 *reason = va( common->GetLocalizedString( "#str_107711" ), common->KeysFromBinding( "_impulse17" ) );
  1575.             } else if( gameLocal.GetLocalPlayer() ) {
  1576.                 *reason = va( common->GetLocalizedString( "#str_107710" ), common->KeysFromBinding( "_impulse17" ) );
  1577.             }
  1578.         }
  1579.         return false;
  1580.     }
  1581.  
  1582.     return true;
  1583. }
  1584.  
  1585. /*
  1586. ================
  1587. idMultiplayerGame::FragLimitHit
  1588. return the winning player (team player)
  1589. if there is no FragLeader(), the game is tied and we return NULL
  1590. ================
  1591. */
  1592. idPlayer *idMultiplayerGame::FragLimitHit() {
  1593.     int fragLimit = gameLocal.serverInfo.GetInt( "si_fragLimit" );
  1594.     idPlayer *leader = NULL;
  1595.  
  1596.      if ( fragLimit <= 0 ) {
  1597.          return NULL; // fraglimit disabled
  1598.     }
  1599.  
  1600.     leader = FragLeader();
  1601.     if ( !leader ) {
  1602.         return NULL;
  1603.     }
  1604.  
  1605.     if ( playerState[ leader->entityNumber ].fragCount >= fragLimit ) {
  1606.         return leader;
  1607.     }
  1608.  
  1609.     return NULL;
  1610. }
  1611.  
  1612. /*
  1613. ================
  1614. idMultiplayerGame::TimeLimitHit
  1615. ================
  1616. */
  1617. bool idMultiplayerGame::TimeLimitHit( void ) {    
  1618.     int timeLimit = gameLocal.serverInfo.GetInt( "si_timeLimit" );
  1619.     if ( timeLimit ) {
  1620.         if ( gameLocal.time >= matchStartedTime + timeLimit * 60000 ) {
  1621.             return true;
  1622.         }
  1623.     }
  1624.     return false;
  1625. }
  1626.  
  1627. /*
  1628. ================
  1629. idMultiplayerGame::FragLeader
  1630. return the current winner
  1631. NULL if even
  1632. relies on UpdatePlayerRanks() being called earlier in frame to sort players
  1633. ================
  1634. */
  1635. idPlayer* idMultiplayerGame::FragLeader( void ) {
  1636.     if( rankedPlayers.Num() < 2 ) {
  1637.         return NULL;
  1638.     }
  1639.  
  1640.     // mark leaders
  1641.     int i;
  1642.     int high = GetScore( rankedPlayers[ 0 ].First() );
  1643.     idPlayer* p;
  1644.     for ( i = 0; i < rankedPlayers.Num(); i++ ) {
  1645.         p = rankedPlayers[ i ].First();
  1646.         if ( !p ) {
  1647.             continue;
  1648.         }
  1649.         p->SetLeader( false );
  1650.  
  1651.         if ( !CanPlay( p ) ) {
  1652.             continue;
  1653.         }
  1654.         if ( gameLocal.gameType == GAME_TOURNEY ) {
  1655.             continue;
  1656.         }
  1657.         if ( p->spectating ) {
  1658.             continue;
  1659.         }
  1660.  
  1661.         if ( GetScore( p ) >= high ) {
  1662.             p->SetLeader( true );
  1663.         }
  1664.     }
  1665.  
  1666.     if( gameLocal.IsTeamGame() ) {
  1667.         // in a team game, find the first player not on the leader's team, and make sure they aren't tied
  1668.         int i = 0;
  1669.         while( i < rankedPlayers.Num() && rankedPlayers[ i ].First()->team == rankedPlayers[ 0 ].First()->team ) {
  1670.             i++;
  1671.         }
  1672.         if( i < rankedPlayers.Num() ) {
  1673.             if( GetScore( rankedPlayers[ i ].First()->entityNumber ) == GetScore( rankedPlayers[ 0 ].First()->entityNumber ) ) {
  1674.                 return NULL;
  1675.             }
  1676.         }
  1677.     } else if( GetScore( rankedPlayers[ 0 ].First()->entityNumber ) == GetScore( rankedPlayers[ 1 ].First()->entityNumber ) ) {
  1678.         return NULL;
  1679.     }
  1680.     
  1681.     return rankedPlayers[ 0 ].First();
  1682. }
  1683.  
  1684. /*
  1685. ================
  1686. idMultiplayerGame::PlayerDeath
  1687. ================
  1688. */
  1689. void idMultiplayerGame::PlayerDeath( idPlayer *dead, idPlayer *killer, int methodOfDeath ) {
  1690.     // don't do PrintMessageEvent
  1691.     assert( !gameLocal.isClient );
  1692.  
  1693.     if ( killer ) {
  1694.         if ( gameLocal.IsTeamGame() ) {
  1695.             if ( killer == dead || killer->team == dead->team ) {
  1696.                 // suicide or teamkill
  1697.                 AddPlayerScore( killer == dead ? dead : killer, -1 );
  1698.             } else {
  1699.                 AddPlayerScore( killer, 1 );
  1700.             }
  1701.             
  1702.             // additional CTF points
  1703.             if( gameLocal.IsFlagGameType() ) {
  1704.                 if( dead->PowerUpActive( killer->team ? POWERUP_CTF_STROGGFLAG : POWERUP_CTF_MARINEFLAG ) ) {
  1705.                     AddPlayerTeamScore( killer, 2 );
  1706.                 }
  1707.             }
  1708.             if( gameLocal.gameType == GAME_TDM ) {
  1709.                 if ( killer == dead || killer->team == dead->team ) {
  1710.                     // suicide or teamkill
  1711.                     AddTeamScore( killer->team, -1 );
  1712.                 } else {
  1713.                     AddTeamScore( killer->team, 1 );
  1714.                 }            
  1715.             }
  1716.         } else {
  1717.             // in tourney mode, we don't award points while in the waiting arena
  1718.             if( gameLocal.gameType != GAME_TOURNEY || ((rvTourneyGameState*)gameState)->GetArena( killer->GetArena() ).GetState() != AS_WARMUP ) {
  1719.                 AddPlayerScore( killer, ( killer == dead ) ? -1 : 1 );
  1720.             }
  1721.  
  1722.             // in tourney mode, frags track performance over the entire level load, team score keeps track of
  1723.             // individual rounds
  1724.             if( gameLocal.gameType == GAME_TOURNEY ) {
  1725.                 AddPlayerTeamScore( killer, ( killer == dead ) ? -1 : 1 );
  1726.             }
  1727.         }
  1728.     } else {
  1729.         // e.g. an environmental death
  1730.         AddPlayerScore( dead, -1 );
  1731.         if( gameLocal.gameType == GAME_TOURNEY ) {
  1732.             AddPlayerTeamScore( dead, -1 );
  1733.         }
  1734.         // to force Tim Willits to play honorably.
  1735.         if( gameLocal.gameType == GAME_TDM ) {
  1736.             AddTeamScore( dead->team, -1);
  1737.         }
  1738.     }
  1739.     
  1740.     SendDeathMessage( killer, dead, methodOfDeath );
  1741.  
  1742.     statManager->Kill( dead, killer, methodOfDeath );
  1743.  
  1744. // RAVEN BEGIN
  1745. // shouchard:  hack for CTF drop messages for listen servers
  1746.     if ( dead == gameLocal.GetLocalPlayer() && 
  1747.         dead->PowerUpActive( dead->team ? POWERUP_CTF_MARINEFLAG : POWERUP_CTF_STROGGFLAG ) ) {
  1748.         if ( dead->mphud ) {
  1749.             dead->mphud->SetStateString( "main_notice_text", common->GetLocalizedString( "#str_104420" ) );
  1750.             dead->mphud->HandleNamedEvent( "main_notice" );
  1751.         }
  1752.     }
  1753. // RAVEN END
  1754. }
  1755.  
  1756. /*
  1757. ================
  1758. idMultiplayerGame::PlayerStats
  1759. ================
  1760. */
  1761. void idMultiplayerGame::PlayerStats( int clientNum, char *data, const int len ) {
  1762.  
  1763.     idEntity *ent;
  1764.     int team;
  1765.  
  1766.     *data = 0;
  1767.  
  1768.     // make sure we don't exceed the client list
  1769.     if ( clientNum < 0 || clientNum > gameLocal.numClients ) {
  1770.         return;
  1771.     }
  1772.  
  1773.     // find which team this player is on
  1774.     ent = gameLocal.entities[ clientNum ]; 
  1775.     if ( ent && ent->IsType( idPlayer::GetClassType() ) ) {
  1776.         team = static_cast< idPlayer * >(ent)->team;
  1777.     } else {
  1778.         return;
  1779.     }
  1780.  
  1781.     idStr::snPrintf( data, len, "team=%d score=%ld tks=%ld", team, playerState[ clientNum ].fragCount, playerState[ clientNum ].teamFragCount );
  1782. }
  1783.  
  1784. /*
  1785. ================
  1786. idMultiplayerGame::PlayerVote
  1787. ================
  1788. */
  1789. void idMultiplayerGame::PlayerVote( int clientNum, playerVote_t vote ) {
  1790.     playerState[ clientNum ].vote = vote;
  1791. }
  1792.  
  1793. /*
  1794. ================
  1795. idMultiplayerGame::ExecuteVote
  1796. the votes are checked for validity/relevance before they are started
  1797. we assume that they are still legit when reaching here
  1798. ================
  1799. */
  1800. void idMultiplayerGame::ExecuteVote( void ) {
  1801.     bool needRestart;
  1802.     ClearVote();
  1803.     switch ( vote ) {
  1804.         case VOTE_RESTART:
  1805.             cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "serverMapRestart\n");
  1806.             break;
  1807.         case VOTE_TIMELIMIT:
  1808.             si_timeLimit.SetInteger( atoi( voteValue ) );
  1809.             needRestart = gameLocal.NeedRestart();
  1810.             cmdSystem->BufferCommandText( CMD_EXEC_NOW, "rescanSI" );
  1811.             if ( needRestart ) {
  1812.                 cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "nextMap" );
  1813.             }
  1814.             break;
  1815.         case VOTE_FRAGLIMIT:
  1816.             si_fragLimit.SetInteger( atoi( voteValue ) );
  1817.             needRestart = gameLocal.NeedRestart();
  1818.             cmdSystem->BufferCommandText( CMD_EXEC_NOW, "rescanSI" );
  1819.             if ( needRestart ) {
  1820.                 cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "nextMap" );
  1821.             }
  1822.             break;
  1823.         case VOTE_GAMETYPE:
  1824.             cvarSystem->SetCVarString( "si_gametype", voteValue );
  1825.             cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "serverMapRestart\n");
  1826.             break;
  1827.         case VOTE_KICK:
  1828.             cmdSystem->BufferCommandText( CMD_EXEC_NOW, va( "kick %s", voteValue.c_str() ) );
  1829.             break;
  1830.         case VOTE_MAP:
  1831.             cvarSystem->SetCVarString( "si_map", voteValue );
  1832.             cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "serverMapRestart\n");
  1833.             break;
  1834.         case VOTE_MIN_PLAYERS:
  1835.             cvarSystem->SetCVarString( "si_minPlayers", voteValue );
  1836.             cmdSystem->BufferCommandText( CMD_EXEC_NOW, "rescanSI" );
  1837.             break;
  1838. // RAVEN BEGIN
  1839. // shouchard:  added capture limit
  1840.         case VOTE_CAPTURELIMIT:
  1841.             si_captureLimit.SetInteger( atoi( voteValue ) );
  1842.             cmdSystem->BufferCommandText( CMD_EXEC_NOW, "rescanSI" );
  1843.             cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "nextMap" );
  1844.             break;
  1845.         // todo:  round limit here (if we add it)
  1846.         case VOTE_AUTOBALANCE:
  1847.             si_autobalance.SetInteger( atoi( voteValue ) );
  1848.             cmdSystem->BufferCommandText( CMD_EXEC_NOW, "rescanSI" );
  1849.             break;
  1850.         case VOTE_MULTIFIELD:
  1851.             ExecutePackedVote();
  1852.             break;
  1853. // RAVEN END
  1854.         case VOTE_NEXTMAP:
  1855.             cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "serverNextMap\n" );
  1856.             break;
  1857.     }
  1858. }
  1859.  
  1860. /*
  1861. ================
  1862. idMultiplayerGame::CheckVote
  1863. ================
  1864. */
  1865. void idMultiplayerGame::CheckVote( void ) {
  1866.     int numVoters, i;
  1867.  
  1868.     if ( vote == VOTE_NONE ) {
  1869.         return;
  1870.     }
  1871.  
  1872.     if ( voteExecTime ) {
  1873.         if ( gameLocal.time > voteExecTime ) {
  1874.             voteExecTime = 0;
  1875.             ClientUpdateVote( VOTE_RESET, 0, 0, currentVoteData );
  1876.             ExecuteVote();
  1877.             vote = VOTE_NONE;
  1878.         }
  1879.         return;
  1880.     }
  1881.  
  1882.     // count voting players
  1883.     numVoters = 0;
  1884.  
  1885.     for ( i = 0; i < gameLocal.numClients; i++ ) {
  1886.         idEntity *ent = gameLocal.entities[ i ];
  1887. // RAVEN BEGIN
  1888. // jnewquist: Use accessor for static class type 
  1889.         if ( !ent || !ent->IsType( idPlayer::GetClassType() ) ) {
  1890. // RAVEN END
  1891.             continue;
  1892.         }
  1893.         if ( playerState[ i ].vote != PLAYER_VOTE_NONE ) {
  1894.             numVoters++;
  1895.         }
  1896.     }
  1897.     if ( !numVoters ) {
  1898.         // abort
  1899.         vote = VOTE_NONE;
  1900.         ClientUpdateVote( VOTE_ABORTED, yesVotes, noVotes, currentVoteData );
  1901.         return;
  1902.     }
  1903.     if ( yesVotes / numVoters > 0.5f ) {
  1904.         ClientUpdateVote( VOTE_PASSED, yesVotes, noVotes, currentVoteData );
  1905.         voteExecTime = gameLocal.time + 2000;
  1906.         return;
  1907.     }
  1908.     if ( gameLocal.time > voteTimeOut || noVotes / numVoters >= 0.5f ) {
  1909.         ClientUpdateVote( VOTE_FAILED, yesVotes, noVotes, currentVoteData );
  1910.         vote = VOTE_NONE;
  1911.         return;
  1912.     }
  1913. }
  1914.  
  1915. // RAVEN BEGIN
  1916. // shouchard:  multifield voting here
  1917.  
  1918. /*
  1919. ================
  1920. idMultiplayerGame::ClientCallPackedVote
  1921.  
  1922. The assumption is that the zero changes case has been handled above.
  1923. ================
  1924. */
  1925. void idMultiplayerGame::ClientCallPackedVote( const voteStruct_t &voteData ) {
  1926.     idBitMsg    outMsg;
  1927.     byte        msgBuf[ MAX_GAME_MESSAGE_SIZE ];
  1928.  
  1929.     assert( 0 != voteData.m_fieldFlags );
  1930.  
  1931.     // send 
  1932.     outMsg.Init( msgBuf, sizeof( msgBuf ) );
  1933.     outMsg.WriteByte( GAME_RELIABLE_MESSAGE_CALLPACKEDVOTE );
  1934.     outMsg.WriteShort( voteData.m_fieldFlags );
  1935.     if ( 0 != ( voteData.m_fieldFlags & VOTEFLAG_KICK ) ) {
  1936.         outMsg.WriteByte( idMath::ClampChar( voteData.m_kick ) );    
  1937.     }
  1938.     if ( 0 != ( voteData.m_fieldFlags & VOTEFLAG_MAP ) ) {
  1939.         outMsg.WriteString( voteData.m_map.c_str() );
  1940.     }
  1941.     if ( 0 != ( voteData.m_fieldFlags & VOTEFLAG_GAMETYPE ) ) {
  1942.         outMsg.WriteByte( idMath::ClampChar( voteData.m_gameType ) );
  1943.     }
  1944.     if ( 0 != ( voteData.m_fieldFlags & VOTEFLAG_TIMELIMIT ) ) {
  1945.         outMsg.WriteByte( idMath::ClampChar( voteData.m_timeLimit ) );
  1946.     }
  1947.     if ( 0 != ( voteData.m_fieldFlags & VOTEFLAG_TOURNEYLIMIT ) ) {
  1948.         outMsg.WriteShort( idMath::ClampShort( voteData.m_tourneyLimit ) );
  1949.     }
  1950.     if ( 0 != ( voteData.m_fieldFlags & VOTEFLAG_CAPTURELIMIT ) ) {
  1951.         outMsg.WriteShort( idMath::ClampShort( voteData.m_captureLimit ) );
  1952.     }
  1953.     if ( 0 != ( voteData.m_fieldFlags & VOTEFLAG_FRAGLIMIT ) ) {
  1954.         outMsg.WriteShort( idMath::ClampShort( voteData.m_fragLimit ) );
  1955.     }
  1956.     if ( 0 != ( voteData.m_fieldFlags & VOTEFLAG_MIN_PLAYERS ) ) {
  1957.         outMsg.WriteShort( idMath::ClampShort( voteData.m_minPlayers ) );
  1958.     }
  1959.     if ( 0 != ( voteData.m_fieldFlags & VOTEFLAG_TEAMBALANCE ) ) {
  1960.         outMsg.WriteByte( idMath::ClampChar( voteData.m_teamBalance ) );
  1961.     }
  1962.     networkSystem->ClientSendReliableMessage( outMsg );
  1963. }
  1964.  
  1965. /*
  1966. ================
  1967. idMultiplayerGame::ServerCallPackedVote
  1968. ================
  1969. */
  1970. void idMultiplayerGame::ServerCallPackedVote( int clientNum, const idBitMsg &msg ) {
  1971.     voteStruct_t voteData;
  1972.     memset( &voteData, 0, sizeof( voteData ) );
  1973.  
  1974.     assert( -1 != clientNum );
  1975.     
  1976.     if( !gameLocal.serverInfo.GetBool( "si_allowVoting" ) ) {
  1977.         return;
  1978.     }
  1979.  
  1980.     // this is set to false if an invalid parameter is asked for-- time limit of -1, or frag limit of "jeff" or whatever.    
  1981.     // if it's a multivote, it may still be valid, but this value is only checked if there are no vote parameters changed. 
  1982.     bool validVote = true;
  1983.  
  1984.     // sanity checks - setup the vote
  1985.     if ( vote != VOTE_NONE ) {
  1986.         gameLocal.ServerSendChatMessage( clientNum, "server", "#str_104273" );
  1987.         common->DPrintf( "client %d: called vote while voting already in progress - ignored\n", clientNum );
  1988.         return;
  1989.     }
  1990.  
  1991.     // flags (short)
  1992.     voteData.m_fieldFlags = msg.ReadShort();
  1993.  
  1994.     // kick
  1995.     if ( 0 != ( voteData.m_fieldFlags & VOTEFLAG_KICK ) ) {
  1996.         voteData.m_kick = msg.ReadByte();
  1997.         if ( voteData.m_kick == gameLocal.localClientNum ) {
  1998.             gameLocal.ServerSendChatMessage( clientNum, "server", "#str_104257" );
  1999.             common->DPrintf( "client %d: called kick for the server host\n", clientNum );
  2000.             validVote = false;
  2001.             voteData.m_fieldFlags &= ( ~VOTEFLAG_KICK );
  2002.         }
  2003.     }
  2004.  
  2005.     // map (string)
  2006.     if ( 0 != ( voteData.m_fieldFlags & VOTEFLAG_MAP ) ) {
  2007.         char buffer[128];
  2008.         msg.ReadString( buffer, sizeof( buffer ) );
  2009.         voteData.m_map = buffer;
  2010.         if ( 0 == idStr::Icmp( buffer, si_map.GetString() ) ) {
  2011.             //gameLocal.ServerSendChatMessage( clientNum, "server", "Selected map is the same as current map." );
  2012.             // mekberg: localized string
  2013.             const char* mapName = si_map.GetString();
  2014.             const idDeclEntityDef *mapDef = static_cast<const idDeclEntityDef *>(declManager->FindType( DECL_MAPDEF, mapName, false ));
  2015.             if ( mapDef ) {
  2016.                 mapName = common->GetLocalizedString( mapDef->dict.GetString( "name", mapName ) );
  2017.             }
  2018.             gameLocal.ServerSendChatMessage( clientNum, "server", va( common->GetLocalizedString( "#str_104295" ), mapName ) );
  2019.             validVote = false;
  2020.             voteData.m_fieldFlags &= ( ~VOTEFLAG_MAP );
  2021.         }
  2022.     }
  2023.  
  2024.     // gametype
  2025.     if ( 0 != ( voteData.m_fieldFlags & VOTEFLAG_GAMETYPE ) ) {
  2026.         voteData.m_gameType = msg.ReadByte();
  2027.         const char *voteString = "DM";
  2028.         switch ( voteData.m_gameType ) {
  2029.             case VOTE_GAMETYPE_TOURNEY:    
  2030.                 voteString = "Tourney";    
  2031.                 break;
  2032.             case VOTE_GAMETYPE_CTF:        
  2033.                 voteString = "CTF";        
  2034.                 break;
  2035.             case VOTE_GAMETYPE_TDM:        
  2036.                 voteString = "Team DM";    
  2037.                 break;
  2038.             case VOTE_GAMETYPE_ARENA_CTF:
  2039.                 voteString = "Arena CTF";
  2040.                 break;
  2041.             case VOTE_GAMETYPE_DM:
  2042.             default:
  2043.                 voteString = "DM";
  2044.                 break;
  2045.         }
  2046.         if ( !idStr::Icmp( voteString, gameLocal.serverInfo.GetString( "si_gameType" ) ) ) {
  2047.             gameLocal.ServerSendChatMessage( clientNum, "server", "#str_104259" );
  2048.             common->DPrintf( "client %d: already at the voted Game Type\n", clientNum );
  2049.             validVote = false;
  2050.             voteData.m_fieldFlags &= ( ~VOTEFLAG_GAMETYPE );
  2051.         }
  2052. //jshepard: if chosen gametype is CT`F and our map doesn't support CTF, change the map to a random CTF map.
  2053. //jshepard: Let's wait until the vote is resolved before we do this!
  2054. /*        if( voteData.m_gameType == VOTE_GAMETYPE_CTF || voteData.m_gameType == VOTE_GAMETYPE_ARENA_CTF )    {
  2055.             PickMap( "CTF");
  2056.             voteData.m_map = si_map.GetString();
  2057.         } */
  2058.     }
  2059.  
  2060.     // timelimit
  2061.     if ( 0 != ( voteData.m_fieldFlags & VOTEFLAG_TIMELIMIT ) ) {
  2062.         voteData.m_timeLimit = msg.ReadByte();
  2063.         if ( voteData.m_timeLimit < si_timeLimit.GetMinValue() || voteData.m_timeLimit > si_timeLimit.GetMaxValue() ) {
  2064.             gameLocal.ServerSendChatMessage( clientNum, "server", "#str_104269" );
  2065.             common->DPrintf( "client %d: timelimit value out of range for vote: %d\n", clientNum, voteData.m_timeLimit );
  2066.             validVote = false;
  2067.             voteData.m_fieldFlags &= ( ~VOTEFLAG_TIMELIMIT );
  2068.         }
  2069.         if ( voteData.m_timeLimit == si_timeLimit.GetInteger() ) {
  2070.             gameLocal.ServerSendChatMessage( clientNum, "server", "#str_104270" );
  2071.             validVote = false;
  2072.             voteData.m_fieldFlags &= ( ~VOTEFLAG_TIMELIMIT );
  2073.         }
  2074.     }
  2075.  
  2076.     // tourneylimit
  2077.     if ( 0 != ( voteData.m_fieldFlags & VOTEFLAG_TOURNEYLIMIT ) ) {
  2078.         voteData.m_tourneyLimit = msg.ReadShort();
  2079.         if ( voteData.m_tourneyLimit < si_tourneyLimit.GetMinValue() || voteData.m_tourneyLimit > si_tourneyLimit.GetMaxValue() ) {
  2080.             gameLocal.ServerSendChatMessage( clientNum, "server", "#str_104261" );
  2081.             validVote = false;
  2082.             voteData.m_fieldFlags &= ( ~VOTEFLAG_TOURNEYLIMIT );
  2083.         }
  2084.         if ( voteData.m_tourneyLimit == si_tourneyLimit.GetInteger() ) {
  2085.             gameLocal.ServerSendChatMessage( clientNum, "server", "#str_104260" );
  2086.             validVote = false;
  2087.             voteData.m_fieldFlags &= ( ~VOTEFLAG_TOURNEYLIMIT );
  2088.         }
  2089.     }
  2090.  
  2091.     // capture limit
  2092.     if ( 0 != ( voteData.m_fieldFlags & VOTEFLAG_CAPTURELIMIT ) ) {
  2093.         voteData.m_captureLimit = msg.ReadShort();
  2094.         if ( voteData.m_captureLimit < si_captureLimit.GetMinValue() || voteData.m_captureLimit > si_fragLimit.GetMaxValue() ) {
  2095.             gameLocal.ServerSendChatMessage( clientNum, "server", "#str_104402" );
  2096.             common->DPrintf( "client %d: caplimit value out of range for vote: %d\n", clientNum, voteData.m_captureLimit );
  2097.             validVote = false;
  2098.             voteData.m_fieldFlags &= ( ~VOTEFLAG_CAPTURELIMIT );
  2099.         }
  2100.         if ( voteData.m_captureLimit == si_captureLimit.GetInteger() ) {
  2101.             gameLocal.ServerSendChatMessage( clientNum, "server", "#str_104401" );
  2102.             validVote = false;
  2103.             voteData.m_fieldFlags &= ( ~VOTEFLAG_CAPTURELIMIT );
  2104.         }
  2105.     }
  2106.  
  2107.     // fraglimit
  2108.     if ( 0 != ( voteData.m_fieldFlags & VOTEFLAG_FRAGLIMIT ) ) {
  2109.         voteData.m_fragLimit = msg.ReadShort();
  2110.         if ( voteData.m_fragLimit < si_fragLimit.GetMinValue() || voteData.m_fragLimit > si_fragLimit.GetMaxValue() ) {
  2111.             gameLocal.ServerSendChatMessage( clientNum, "server", "#str_104266" );
  2112.             common->DPrintf( "client %d: fraglimit value out of range for vote: %d\n", clientNum, voteData.m_fragLimit );
  2113.             validVote = false;
  2114.             voteData.m_fieldFlags &= ( ~VOTEFLAG_FRAGLIMIT );
  2115.         }
  2116.         if ( voteData.m_fragLimit == si_fragLimit.GetInteger() ) {
  2117.             gameLocal.ServerSendChatMessage( clientNum, "server", "#str_104267" );
  2118.             validVote = false;
  2119.             voteData.m_fieldFlags &= ( ~VOTEFLAG_FRAGLIMIT );
  2120.         }
  2121.     }
  2122.  
  2123.     // spectators
  2124. /*    if ( 0 != ( voteData.m_fieldFlags & VOTEFLAG_SPECTATORS ) ) {
  2125.         voteData.m_spectators = msg.ReadByte();
  2126.         if ( voteData.m_spectators == si_spectators.GetInteger() ) {
  2127.             gameLocal.ServerSendChatMessage( clientNum, "server", "#str_104421" );
  2128.             validVote = false;
  2129.             voteData.m_fieldFlags &= ( ~VOTEFLAG_SPECTATORS );
  2130.         }
  2131.     } */
  2132.  
  2133.     // minplayers
  2134.     if ( 0 != ( voteData.m_fieldFlags & VOTEFLAG_MIN_PLAYERS ) ) {
  2135.         voteData.m_minPlayers = msg.ReadShort();
  2136.         if ( voteData.m_minPlayers == si_minPlayers.GetInteger() ) {
  2137.             gameLocal.ServerSendChatMessage( clientNum, "server", "#str_107733" );
  2138.             validVote = false;
  2139.             voteData.m_fieldFlags &= ( ~VOTEFLAG_MIN_PLAYERS );
  2140.         }
  2141.     }
  2142.  
  2143.     // autobalance teams
  2144.     if ( 0 != ( voteData.m_fieldFlags & VOTEFLAG_TEAMBALANCE ) ) {
  2145.         voteData.m_teamBalance = msg.ReadByte();
  2146.         if ( voteData.m_teamBalance == si_autobalance.GetInteger() ) {
  2147.             gameLocal.ServerSendChatMessage( clientNum, "server", "#str_104403" );
  2148.             validVote = false;
  2149.             voteData.m_fieldFlags &= ( ~VOTEFLAG_TEAMBALANCE );
  2150.         }
  2151.     }
  2152.  
  2153.     // check for no changes at all
  2154.     if ( 0 == voteData.m_fieldFlags ) {
  2155.         // If the vote was called empty, announce there were no valid changes. Otherwise, say nothing, there's already been a warning message.
  2156.         if( validVote )    {
  2157.             gameLocal.ServerSendChatMessage( clientNum, "server", "#str_104400" );
  2158.         }
  2159.         return;
  2160.     }
  2161.  
  2162.     ServerStartPackedVote( clientNum, voteData );
  2163.     ClientStartPackedVote( clientNum, voteData );
  2164. }
  2165.  
  2166. /*
  2167. ================
  2168. idMultiplayerGame::ClientStartPackedVote
  2169. ================
  2170. */
  2171. void idMultiplayerGame::ClientStartPackedVote( int clientNum, const voteStruct_t &voteData ) {
  2172.     assert( 0 != voteData.m_fieldFlags );
  2173.  
  2174.     if ( !gameLocal.isListenServer && !gameLocal.isClient ) {
  2175.         return;
  2176.     }
  2177.  
  2178.     // "%s has called a vote!"
  2179.     AddChatLine( va( common->GetLocalizedString( "#str_104279" ), gameLocal.userInfo[ clientNum ].GetString( "ui_name" ) ) );
  2180.  
  2181.     // display the vote called text on the hud and play an announcer sound
  2182.     if ( gameLocal.GetLocalPlayer() && gameLocal.GetLocalPlayer()->mphud ) {
  2183.         gameLocal.GetLocalPlayer()->mphud->SetStateInt( "voteNotice", 1 );
  2184.     }
  2185.     ScheduleAnnouncerSound( AS_GENERAL_VOTE_NOW, gameLocal.time );
  2186.  
  2187.     if ( clientNum == gameLocal.localClientNum ) {
  2188.         voted = true;
  2189.     } else {
  2190.         voted = false;
  2191.     }
  2192.  
  2193.     if ( gameLocal.isClient ) {
  2194.         // the the vote value to something so the vote line is displayed
  2195.         vote = VOTE_RESTART;
  2196.         yesVotes = 1;
  2197.         noVotes = 0;
  2198.     }
  2199.  
  2200.     currentVoteData = voteData;
  2201.     idUserInterface * mpHud =  gameLocal.GetLocalPlayer()->mphud;
  2202.  
  2203.     // push data to the interface
  2204.     if ( mpHud && mainGui ) {
  2205.         int voteLineCount = 1;
  2206.         int menuVoteLineCount = 0;
  2207.         bool kickActive = false;
  2208.         bool maxWindows = false;
  2209.         idStr yesKey = common->KeysFromBinding("_impulse28");
  2210.  
  2211.         mainGui->SetStateInt( "vote_going", 1 );
  2212.  
  2213.         //dynamic vote yes/no box
  2214.         mpHud->SetStateString( "voteNoticeText", va( common->GetLocalizedString( "#str_107242" ), yesKey.c_str(), common->KeysFromBinding("_impulse29") ));
  2215.  
  2216.         // kick should always be the highest one
  2217.         if ( 0 != ( currentVoteData.m_fieldFlags & VOTEFLAG_KICK ) ) {
  2218.             // mpGui here, not mpHud
  2219.             //mpHud->SetStateString( "vote_data0", va( common->GetLocalizedString( "#str_104422" ), player->GetName() );
  2220.             kickActive = true;
  2221.             mpHud->SetStateString( va( "voteInfo_%d", voteLineCount ), 
  2222.                 va( common->GetLocalizedString( "#str_104422" ), gameLocal.userInfo[ currentVoteData.m_kick ].GetString( "ui_name" ) ) );
  2223.  
  2224.             mainGui->SetStateString( va( "voteData_item_%d", menuVoteLineCount ), 
  2225.                 va( common->GetLocalizedString( "#str_104422" ), gameLocal.userInfo[ currentVoteData.m_kick ].GetString( "ui_name" ) ) );
  2226.  
  2227.             voteLineCount++;
  2228.             menuVoteLineCount++;
  2229.             if( voteLineCount == 7)    {
  2230.                 voteLineCount = 6;
  2231.                 maxWindows = true;
  2232.             }
  2233.         }
  2234.         if ( 0 != ( currentVoteData.m_fieldFlags & VOTEFLAG_RESTART ) ) {
  2235.             mpHud->SetStateString( va( "voteInfo_%d", voteLineCount ), 
  2236.                 common->GetLocalizedString( "#str_104423" ) );
  2237.  
  2238.             mainGui->SetStateString( va( "voteData_item_%d", menuVoteLineCount ), 
  2239.                 common->GetLocalizedString( "#str_104423" ) );
  2240.  
  2241.             voteLineCount++;
  2242.             menuVoteLineCount++;
  2243.             if( voteLineCount == 7)    {
  2244.                 voteLineCount = 6;
  2245.                 maxWindows = true;
  2246.             }
  2247.         }
  2248.         if ( 0 != ( currentVoteData.m_fieldFlags & VOTEFLAG_MIN_PLAYERS ) ) {
  2249.             mpHud->SetStateString( va( "voteInfo_%d", voteLineCount ), 
  2250.                 va( common->GetLocalizedString( "#str_104424" ), currentVoteData.m_minPlayers ));
  2251.  
  2252.             mainGui->SetStateString( va( "voteData_item_%d", menuVoteLineCount ), 
  2253.                 va( common->GetLocalizedString( "#str_104424" ), currentVoteData.m_minPlayers ));
  2254.  
  2255.             voteLineCount++;
  2256.             menuVoteLineCount++;
  2257.             if( voteLineCount == 7)    {
  2258.                 voteLineCount = 6;
  2259.                 maxWindows = true;
  2260.             }
  2261.         }
  2262.         if ( 0 != ( currentVoteData.m_fieldFlags & VOTEFLAG_TEAMBALANCE ) ) {
  2263.             mpHud->SetStateString( va( "voteInfo_%d", voteLineCount ),
  2264.                 va( common->GetLocalizedString( "#str_104427" ), currentVoteData.m_teamBalance ? common->GetLocalizedString( "#str_104341" ) : common->GetLocalizedString( "#str_104342" ) ) );
  2265.  
  2266.             mainGui->SetStateString( va( "voteData_item_%d", menuVoteLineCount ),
  2267.                 va( common->GetLocalizedString( "#str_104427" ), currentVoteData.m_teamBalance ? common->GetLocalizedString( "#str_104341" ) : common->GetLocalizedString( "#str_104342" ) ) );
  2268.  
  2269.             voteLineCount++;
  2270.             menuVoteLineCount++;
  2271.             if( voteLineCount == 7)    {
  2272.                 voteLineCount = 6;
  2273.                 maxWindows = true;
  2274.             }
  2275.         }
  2276.         if ( 0 != ( currentVoteData.m_fieldFlags & VOTEFLAG_NEXTMAP ) ) {
  2277.             mpHud->SetStateString( va( "voteInfo_%d", voteLineCount ),
  2278.                 common->GetLocalizedString( "#str_104428" ) );
  2279.  
  2280.             mainGui->SetStateString( va( "voteData_item_%d", menuVoteLineCount ),
  2281.                 common->GetLocalizedString( "#str_104428" ) );
  2282.  
  2283.             voteLineCount++;
  2284.             menuVoteLineCount++;
  2285.             if( voteLineCount == 7)    {
  2286.                 voteLineCount = 6;
  2287.                 maxWindows = true;
  2288.             }
  2289.         }
  2290.         if ( 0 != ( currentVoteData.m_fieldFlags & VOTEFLAG_MAP ) ) {
  2291.  
  2292.             const char *mapName = currentVoteData.m_map.c_str();
  2293.             const idDeclEntityDef *mapDef = static_cast<const idDeclEntityDef *>(declManager->FindType( DECL_MAPDEF, mapName, false ));
  2294.             if ( mapDef ) {
  2295.                 mapName = common->GetLocalizedString( mapDef->dict.GetString( "name", mapName ) );
  2296.             }
  2297.             mpHud->SetStateString( va( "voteInfo_%d", voteLineCount ),
  2298.                 va( "%s: %s", common->GetLocalizedString( "#str_200040" ), mapName ) );
  2299.  
  2300.             mainGui->SetStateString( va( "voteData_item_%d", menuVoteLineCount ),
  2301.                 va( "%s: %s", common->GetLocalizedString( "#str_200040" ), mapName ) );
  2302.  
  2303.             voteLineCount++;
  2304.             menuVoteLineCount++;
  2305.             if( voteLineCount == 7)    {
  2306.                 voteLineCount = 6;
  2307.                 maxWindows = true;
  2308.             }
  2309.         }
  2310.         if ( 0 != ( currentVoteData.m_fieldFlags & VOTEFLAG_GAMETYPE ) ) {
  2311.             const char *gameTypeString = "DM";
  2312.             switch( currentVoteData.m_gameType ) {
  2313.                 case VOTE_GAMETYPE_TOURNEY:
  2314.                     gameTypeString = "Tourney";
  2315.                     break;
  2316.                 case VOTE_GAMETYPE_TDM:
  2317.                     gameTypeString = "Team DM";
  2318.                     break;
  2319.                 case VOTE_GAMETYPE_CTF:
  2320.                     gameTypeString = "CTF";
  2321.                     break;
  2322.                 case VOTE_GAMETYPE_ARENA_CTF:
  2323.                     gameTypeString = "Arena CTF";
  2324.                     break;
  2325.                 case VOTE_GAMETYPE_DM:
  2326.                 default:
  2327.                     gameTypeString = "DM";
  2328.                     break;
  2329.             }
  2330.             mpHud->SetStateString( va( "voteInfo_%d", voteLineCount ),
  2331.                 va( common->GetLocalizedString( "#str_104430" ), gameTypeString ) );
  2332.  
  2333.             mainGui->SetStateString( va( "voteData_item_%d", menuVoteLineCount ),
  2334.                 va( common->GetLocalizedString( "#str_104430" ), gameTypeString ) );
  2335.  
  2336.             voteLineCount++;
  2337.             menuVoteLineCount++;
  2338.             if( voteLineCount == 7)    {
  2339.                 voteLineCount = 6;
  2340.                 maxWindows = true;
  2341.             }
  2342.         }
  2343.         if ( 0 != ( currentVoteData.m_fieldFlags & VOTEFLAG_TIMELIMIT ) ) {
  2344.             mpHud->SetStateString( va( "voteInfo_%d", voteLineCount ),
  2345.                 va( common->GetLocalizedString( "#str_104431" ), currentVoteData.m_timeLimit ) );
  2346.  
  2347.             mainGui->SetStateString( va( "voteData_item_%d", menuVoteLineCount ),
  2348.                 va( common->GetLocalizedString( "#str_104431" ), currentVoteData.m_timeLimit ) );
  2349.  
  2350.             voteLineCount++;
  2351.             menuVoteLineCount++;
  2352.             if( voteLineCount == 7)    {
  2353.                 voteLineCount = 6;
  2354.                 maxWindows = true;
  2355.             }
  2356.         }
  2357.         if ( 0 != ( currentVoteData.m_fieldFlags & VOTEFLAG_TOURNEYLIMIT ) ) {
  2358.             mpHud->SetStateString( va( "voteInfo_%d", voteLineCount ),
  2359.                 va( common->GetLocalizedString( "#str_104432" ), currentVoteData.m_tourneyLimit ) );
  2360.  
  2361.             mainGui->SetStateString( va( "voteData_item_%d", menuVoteLineCount ),
  2362.                 va( common->GetLocalizedString( "#str_104432" ), currentVoteData.m_tourneyLimit ) );
  2363.  
  2364.             voteLineCount++;
  2365.             menuVoteLineCount++;
  2366.             if( voteLineCount == 7)    {
  2367.                 voteLineCount = 6;
  2368.                 maxWindows = true;
  2369.             }
  2370.         }
  2371.         if ( 0 != ( currentVoteData.m_fieldFlags & VOTEFLAG_CAPTURELIMIT ) ) {
  2372.             mpHud->SetStateString( va( "voteInfo_%d", voteLineCount ),
  2373.                 va( common->GetLocalizedString( "#str_104433" ), currentVoteData.m_captureLimit ) );
  2374.  
  2375.             mainGui->SetStateString( va( "voteData_item_%d", menuVoteLineCount ),
  2376.                 va( common->GetLocalizedString( "#str_104433" ), currentVoteData.m_captureLimit ) );
  2377.  
  2378.             voteLineCount++;
  2379.             menuVoteLineCount++;
  2380.             if( voteLineCount == 7)    {
  2381.                 voteLineCount = 6;
  2382.                 maxWindows = true;
  2383.             }
  2384.         }
  2385.         if ( 0 != ( currentVoteData.m_fieldFlags & VOTEFLAG_FRAGLIMIT ) ) {
  2386.             mpHud->SetStateString( va( "voteInfo_%d", voteLineCount ),
  2387.                 va( common->GetLocalizedString( "#str_104434" ), currentVoteData.m_fragLimit ) );
  2388.  
  2389.             mainGui->SetStateString( va( "voteData_item_%d", menuVoteLineCount ),
  2390.                 va( common->GetLocalizedString( "#str_104434" ), currentVoteData.m_fragLimit ) );
  2391.  
  2392.             voteLineCount++;
  2393.             menuVoteLineCount++;
  2394.             if( voteLineCount == 7)    {
  2395.                 voteLineCount = 6;
  2396.                 maxWindows = true;
  2397.             }
  2398.         }
  2399.  
  2400.         //jshep: max of 7 windows and the 7th is always "..."
  2401.         if( maxWindows )    {
  2402.             mpHud->SetStateString( "voteInfo_7", "..." );
  2403.         }
  2404.  
  2405.         mainGui->DeleteStateVar( va( "voteData_item_%d", menuVoteLineCount ) );
  2406.         mainGui->SetStateInt( "vote_going", 1 );
  2407.         mainGui->SetStateString( "voteCount", va( common->GetLocalizedString( "#str_104435" ), (int)yesVotes, (int)noVotes ) );
  2408.     }
  2409.  
  2410.     ClientUpdateVote( VOTE_UPDATE, yesVotes, noVotes, currentVoteData );
  2411. }
  2412.  
  2413. /*
  2414. ================
  2415. idMultiplayerGame::ServerStartPackedVote
  2416. ================
  2417. */
  2418. void idMultiplayerGame::ServerStartPackedVote( int clientNum, const voteStruct_t &voteData ) {
  2419.     idBitMsg    outMsg;
  2420.     byte        msgBuf[ MAX_GAME_MESSAGE_SIZE ];
  2421.  
  2422.     assert( vote == VOTE_NONE );
  2423.  
  2424.     if ( !gameLocal.isServer ) {
  2425.         return;
  2426.     }
  2427.  
  2428.     // setup
  2429.     yesVotes = 1;
  2430.     noVotes = 0;
  2431.     vote = VOTE_MULTIFIELD;
  2432.     currentVoteData = voteData;
  2433.     voteTimeOut = gameLocal.time + 30000;    // 30 seconds?  might need to be longer because it requires fiddling with the GUI
  2434.     // mark players allowed to vote - only current ingame players, players joining during vote will be ignored
  2435.     for ( int i = 0; i < gameLocal.numClients; i++ ) {
  2436.         if ( gameLocal.entities[ i ] && gameLocal.entities[ i ]->IsType( idPlayer::GetClassType() ) ) {
  2437.             playerState[ i ].vote = ( i == clientNum ) ? PLAYER_VOTE_YES : PLAYER_VOTE_WAIT;
  2438.         } else {
  2439.             playerState[i].vote = PLAYER_VOTE_NONE;
  2440.         }
  2441.     }
  2442.  
  2443.     outMsg.Init( msgBuf, sizeof( msgBuf ) );
  2444.     outMsg.WriteByte( GAME_RELIABLE_MESSAGE_STARTPACKEDVOTE );
  2445.     outMsg.WriteByte( clientNum );
  2446.     outMsg.WriteShort( voteData.m_fieldFlags );
  2447.     if ( 0 != ( voteData.m_fieldFlags & VOTEFLAG_KICK ) ) {
  2448.         outMsg.WriteByte( idMath::ClampChar( voteData.m_kick ) );
  2449.     }
  2450.     if ( 0 != ( voteData.m_fieldFlags & VOTEFLAG_MAP ) ) {
  2451.         outMsg.WriteString( voteData.m_map.c_str() );
  2452.     }
  2453.     if ( 0 != ( voteData.m_fieldFlags & VOTEFLAG_GAMETYPE ) ) {
  2454.         outMsg.WriteByte( idMath::ClampChar( voteData.m_gameType ) );
  2455.     }
  2456.     if ( 0 != ( voteData.m_fieldFlags & VOTEFLAG_TIMELIMIT ) ) {
  2457.         outMsg.WriteByte( idMath::ClampChar( voteData.m_timeLimit ) );
  2458.     }
  2459.     if ( 0 != ( voteData.m_fieldFlags & VOTEFLAG_FRAGLIMIT ) ) {
  2460.         outMsg.WriteShort( idMath::ClampShort( voteData.m_fragLimit ) );
  2461.     }
  2462.     if ( 0 != ( voteData.m_fieldFlags & VOTEFLAG_TOURNEYLIMIT ) ) {
  2463.         outMsg.WriteShort( idMath::ClampShort( voteData.m_tourneyLimit ) );
  2464.     }
  2465.     if ( 0 != ( voteData.m_fieldFlags & VOTEFLAG_CAPTURELIMIT ) ) {
  2466.         outMsg.WriteShort( idMath::ClampShort( voteData.m_captureLimit ) );
  2467.     }
  2468.     if ( 0 != ( voteData.m_fieldFlags & VOTEFLAG_MIN_PLAYERS ) ) {
  2469.         outMsg.WriteShort( idMath::ClampShort( voteData.m_minPlayers ) );
  2470.     }
  2471.     if ( 0 != ( voteData.m_fieldFlags & VOTEFLAG_TEAMBALANCE ) ) {
  2472.         outMsg.WriteByte( idMath::ClampChar( voteData.m_teamBalance ) );
  2473.     }
  2474.     networkSystem->ServerSendReliableMessage( -1, outMsg );
  2475. }
  2476.  
  2477. /*
  2478. ================
  2479. idMultiplayerGame::ExecutePackedVote
  2480. ================
  2481. */
  2482. void idMultiplayerGame::ExecutePackedVote( void ) {
  2483.     assert( VOTE_MULTIFIELD == vote );
  2484.  
  2485.     if ( 0 == currentVoteData.m_fieldFlags ) {
  2486.         return;
  2487.     }
  2488.  
  2489.     bool needRestart = false;
  2490.     bool needNextMap = false;
  2491.  
  2492.     if ( 0 != ( currentVoteData.m_fieldFlags & VOTEFLAG_RESTART ) ) {
  2493.         needRestart = true;
  2494.     }
  2495.     if ( 0 != ( currentVoteData.m_fieldFlags & VOTEFLAG_MIN_PLAYERS ) ) {
  2496.         si_minPlayers.SetInteger( currentVoteData.m_minPlayers );
  2497.         cmdSystem->BufferCommandText( CMD_EXEC_NOW, "rescanSI" );
  2498.     }
  2499.     if ( 0 != ( currentVoteData.m_fieldFlags & VOTEFLAG_TEAMBALANCE ) ) {
  2500.         si_autobalance.SetInteger( currentVoteData.m_teamBalance );
  2501.         cmdSystem->BufferCommandText( CMD_EXEC_NOW, "rescanSI" );
  2502.     }
  2503.     if ( 0 != ( currentVoteData.m_fieldFlags & VOTEFLAG_NEXTMAP ) ) {
  2504.         // todo:  write this!
  2505.     }
  2506.     if ( 0 != ( currentVoteData.m_fieldFlags & VOTEFLAG_KICK ) ) {
  2507.         cmdSystem->BufferCommandText( CMD_EXEC_NOW, va( "kick %d", currentVoteData.m_kick ) );
  2508.     }
  2509.     if ( 0 != ( currentVoteData.m_fieldFlags & VOTEFLAG_MAP ) ) {
  2510.         si_map.SetString( currentVoteData.m_map.c_str() );
  2511.         needNextMap = true;
  2512.     }
  2513.     if ( 0 != ( currentVoteData.m_fieldFlags & VOTEFLAG_GAMETYPE ) ) {
  2514.         const char *gameTypeString = NULL;
  2515.         //jshepard: Currently the DM gametypes can be played on any map. The other gametypes require specially configured maps.
  2516.         //if further gametypes are added that can be played on any map, don't set the "runPickMap" flag.
  2517.         bool runPickMap = false;
  2518.         switch ( currentVoteData.m_gameType ) {
  2519.             case VOTE_GAMETYPE_TOURNEY:
  2520.                 gameTypeString = "Tourney";
  2521.                 runPickMap = true;
  2522.                 break;
  2523.             case VOTE_GAMETYPE_TDM:
  2524.                 gameTypeString = "Team DM";
  2525.                 break;
  2526.             case VOTE_GAMETYPE_CTF:
  2527.                 gameTypeString = "CTF";
  2528.                 runPickMap = true;
  2529.                 break;
  2530.             case VOTE_GAMETYPE_ARENA_CTF:
  2531.                 gameTypeString = "Arena CTF";
  2532.                 runPickMap = true;
  2533.                 break;
  2534.             case VOTE_GAMETYPE_DM:
  2535.             default:
  2536.                 gameTypeString = "DM";
  2537.                 break;
  2538.         }
  2539.         assert( gameTypeString );
  2540.         si_gameType.SetString( gameTypeString );
  2541.         //jshepard: run a pick map here in case the packed vote is trying to pick the wrong map type.
  2542.         //PickMap returns true if the map has changed (requiring a nextMap call)
  2543.         if( runPickMap )    {
  2544.             if( PickMap( gameTypeString ) )    {
  2545.                 needNextMap = true;
  2546.             } else {
  2547.                 needRestart = true;
  2548.             }
  2549.         } else    {
  2550.             needRestart = true;
  2551.         }
  2552.     }
  2553.     if ( 0 != ( currentVoteData.m_fieldFlags & VOTEFLAG_TIMELIMIT ) ) {
  2554.         si_timeLimit.SetInteger( currentVoteData.m_timeLimit );
  2555.         cmdSystem->BufferCommandText( CMD_EXEC_NOW, "rescanSI" );
  2556.     }
  2557.     if ( 0 != ( currentVoteData.m_fieldFlags & VOTEFLAG_TOURNEYLIMIT ) ) {
  2558.         si_tourneyLimit.SetInteger( currentVoteData.m_tourneyLimit );
  2559.         cmdSystem->BufferCommandText( CMD_EXEC_NOW, "rescanSI" );
  2560.     }
  2561.     if ( 0 != ( currentVoteData.m_fieldFlags & VOTEFLAG_CAPTURELIMIT ) ) {
  2562.         si_captureLimit.SetInteger( currentVoteData.m_captureLimit );
  2563.         cmdSystem->BufferCommandText( CMD_EXEC_NOW, "rescanSI" );
  2564.     }
  2565.     if ( 0 != ( currentVoteData.m_fieldFlags & VOTEFLAG_FRAGLIMIT ) ) {
  2566.         si_fragLimit.SetInteger( currentVoteData.m_fragLimit );
  2567.         cmdSystem->BufferCommandText( CMD_EXEC_NOW,"rescanSI" );
  2568.     }
  2569.  
  2570.     if ( needNextMap ) {
  2571.         cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "nextMap" );    
  2572.     }
  2573.     else if ( needRestart || gameLocal.NeedRestart() ) {
  2574.         cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "serverMapRestart" );    
  2575.     }
  2576. }
  2577.  
  2578. // RAVEN END
  2579.  
  2580. /*
  2581. ================
  2582. idMultiplayerGame::CommonRun
  2583. Called once each render frame (client)/once each game frame (server)
  2584. ================
  2585. */
  2586. void idMultiplayerGame::CommonRun( void ) {
  2587.     idPlayer* player = gameLocal.GetLocalPlayer();
  2588.  
  2589.     // twhitaker r282
  2590.     // TTimo: sure is a nasty way to do it
  2591.     if ( gameLocal.isServer && ( gameLocal.serverInfo.GetInt( "net_serverDedicated" ) != cvarSystem->GetCVarInteger( "net_serverDedicated" ) ) ) {
  2592.         cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "spawnServer\n" );
  2593.     }
  2594.  
  2595.     if( player && player->mphud ) {
  2596.         // update icons
  2597.         iconManager->UpdateIcons();
  2598.  
  2599. #ifdef _USE_VOICECHAT
  2600.         float    micLevel;
  2601.         bool    sending, testing;
  2602.  
  2603.         // jscott: enable the voice recording
  2604.         testing = !!currentMenu;                    // need something better here...
  2605.         sending = soundSystem->EnableRecording( !!( player->usercmd.buttons & BUTTON_VOICECHAT ), testing, micLevel );
  2606.  
  2607.         if( mainGui ) {
  2608.             mainGui->SetStateFloat( "s_micLevel", micLevel );
  2609.             mainGui->SetStateFloat( "s_micInputLevel", cvarSystem->GetCVarFloat( "s_micInputLevel" ) );
  2610.         }
  2611.  
  2612. // RAVEN BEGIN
  2613. // shouchard:  let the UI know about voicechat states
  2614.         if ( sending ) {
  2615.             player->mphud->HandleNamedEvent( "show_transmit_self" );
  2616.         } else {
  2617.             player->mphud->HandleNamedEvent( "hide_transmit_self" );
  2618.         }
  2619.  
  2620.         if( player->GetUserInfo()->GetBool( "s_voiceChatReceive" ) ) {
  2621.             int maxChannels = soundSystem->GetNumVoiceChannels();
  2622.             int clientNum = -1;
  2623.             for (int channels = 0; channels < maxChannels; channels++ ) {
  2624.                 clientNum = soundSystem->GetCommClientNum( channels );
  2625.                 if ( -1 != clientNum ) {
  2626.                     break;
  2627.                 }
  2628.             }
  2629.  
  2630.             // Sanity check for network errors
  2631.             assert( clientNum > -2 && clientNum < MAX_CLIENTS );
  2632.  
  2633.             if ( clientNum > -1 && clientNum < MAX_CLIENTS ) {
  2634.                 idPlayer *from = ( idPlayer * )gameLocal.entities[clientNum];
  2635.                 if( from ) {
  2636.                     player->mphud->SetStateString( "audio_name", from->GetUserInfo()->GetString( "ui_name" ) );
  2637.                 }
  2638.                 player->mphud->HandleNamedEvent( "show_transmit" );
  2639.             } else {
  2640.                 player->mphud->HandleNamedEvent( "hide_transmit" );
  2641.             }
  2642.         }
  2643.         else {
  2644.             player->mphud->HandleNamedEvent( "hide_transmit" );
  2645.         }
  2646. #endif // _USE_VOICECHAT
  2647. // RAVEN END
  2648.     }
  2649. #ifdef _USE_VOICECHAT
  2650.     // jscott: Send any new voice data
  2651.     XmitVoiceData();
  2652. #endif
  2653.  
  2654.     int oldRank = -1;
  2655.     int oldLeadingTeam = -1;
  2656.     bool wasTied = false;
  2657.     int oldHighScore = idMath::INT_MIN;
  2658.  
  2659.     if( player && rankedPlayers.Num() ) {
  2660.         if( gameLocal.gameType == GAME_DM ) {
  2661.             oldRank = GetPlayerRank( player, wasTied );
  2662.             oldHighScore = rankedPlayers[ 0 ].Second();
  2663.         } else if( gameLocal.IsTeamGame() ) {
  2664.             oldLeadingTeam = rankedTeams[ 0 ].First();
  2665.             wasTied = ( rankedTeams[ 0 ].Second() == rankedTeams[ 1 ].Second() );
  2666.             oldHighScore = rankedTeams[ 0 ].Second();
  2667.         }    
  2668.     } 
  2669.  
  2670.  
  2671.     UpdatePlayerRanks();
  2672.     if( gameLocal.IsTeamGame() ) {
  2673.         UpdateTeamRanks();
  2674.     }
  2675.  
  2676.     if( player && rankedPlayers.Num() && gameState->GetMPGameState() == GAMEON ) {
  2677.         if( gameLocal.gameType == GAME_DM ) {
  2678.             // leader message
  2679.             bool isTied = false;
  2680.             int newRank = GetPlayerRank( player, isTied );
  2681.          
  2682.             if( newRank == 0 ) {
  2683.                 if( ( oldRank != 0 || wasTied ) && !isTied ) {
  2684.                     // we've gained first place or the person we were tied with dropped out of first place        
  2685.                     ScheduleAnnouncerSound( AS_DM_YOU_HAVE_TAKEN_LEAD, gameLocal.time );
  2686.                 } else if( oldRank != 0 || (!wasTied && isTied) ) {
  2687.                     // we tied first place or we were in first and someone else tied
  2688.                     ScheduleAnnouncerSound( AS_DM_YOU_TIED_LEAD, gameLocal.time );
  2689.                 }
  2690.             } else if( oldRank == 0 ) {
  2691.                 // we lost first place
  2692.                 ScheduleAnnouncerSound( AS_DM_YOU_LOST_LEAD, gameLocal.time );            
  2693.             }
  2694.         } else if( gameLocal.IsTeamGame() ) {
  2695.             int    leadingTeam = rankedTeams[ 0 ].First();
  2696.             bool isTied = ( rankedTeams[ 0 ].Second() == rankedTeams[ 1 ].Second() );
  2697.  
  2698.             if( !wasTied && isTied ) {
  2699.                 ScheduleAnnouncerSound( AS_TEAM_TEAMS_TIED, gameLocal.time );
  2700.             } else if( (leadingTeam != oldLeadingTeam && !isTied) || ( wasTied && !isTied ) ) {
  2701.                 ScheduleAnnouncerSound( leadingTeam ? AS_TEAM_STROGG_LEAD : AS_TEAM_MARINES_LEAD, gameLocal.time );
  2702.             }
  2703.  
  2704.             if( gameLocal.gameType == GAME_TDM && oldHighScore != teamScore[ rankedTeams[ 0 ].First() ] ) {
  2705.                 if( teamScore[ rankedTeams[ 0 ].First() ] == gameLocal.serverInfo.GetInt( "si_fragLimit" ) - 3 ) {
  2706.                     ScheduleAnnouncerSound( AS_GENERAL_THREE_FRAGS, gameLocal.time );
  2707.                 } else if( teamScore[ rankedTeams[ 0 ].First() ] == gameLocal.serverInfo.GetInt( "si_fragLimit" ) - 2 ) {
  2708.                     ScheduleAnnouncerSound( AS_GENERAL_TWO_FRAGS, gameLocal.time );    
  2709.                 } else if( teamScore[ rankedTeams[ 0 ].First() ] == gameLocal.serverInfo.GetInt( "si_fragLimit" ) - 1 ) {
  2710.                     ScheduleAnnouncerSound( AS_GENERAL_ONE_FRAG, gameLocal.time );    
  2711.                 }
  2712.             }
  2713.         }
  2714.  
  2715.         if( ( gameLocal.gameType == GAME_DM ) && rankedPlayers[ 0 ].Second() != oldHighScore ) {
  2716.             // fraglimit warning
  2717.             if( rankedPlayers[ 0 ].Second() == gameLocal.serverInfo.GetInt( "si_fragLimit" ) - 3 ) {
  2718.                 ScheduleAnnouncerSound( AS_GENERAL_THREE_FRAGS, gameLocal.time );
  2719.             } else if( rankedPlayers[ 0 ].Second() == gameLocal.serverInfo.GetInt( "si_fragLimit" ) - 2 ) {
  2720.                 ScheduleAnnouncerSound( AS_GENERAL_TWO_FRAGS, gameLocal.time );
  2721.             } else if( rankedPlayers[ 0 ].Second() == gameLocal.serverInfo.GetInt( "si_fragLimit" ) - 1 ) {
  2722.                 ScheduleAnnouncerSound( AS_GENERAL_ONE_FRAG, gameLocal.time );
  2723.             }
  2724.         }
  2725.     
  2726.     }
  2727.  
  2728.     PlayAnnouncerSounds();
  2729.  
  2730. //RAVEN BEGIN
  2731. //asalmon: Need to refresh stats periodically if the player is looking at stats
  2732.     if(currentStatClient != -1)
  2733.     {
  2734.         rvPlayerStat* clientStat = statManager->GetPlayerStat( currentStatClient );
  2735.         if ( (gameLocal.time - clientStat->lastUpdateTime ) > 5000 )
  2736.         {
  2737.             statManager->SelectStatWindow(currentStatClient, currentStatTeam);
  2738.         } 
  2739.     }
  2740. //RAVEN END
  2741. }
  2742.  
  2743. /*
  2744. ================
  2745. idMultiplayerGame::ClientRun
  2746. Called once each client render frame (before any ClientPrediction frames have been run)
  2747. ================
  2748. */
  2749. void idMultiplayerGame::ClientRun( void ) {
  2750.     CommonRun();
  2751. }
  2752.  
  2753.  
  2754. /*
  2755. ================
  2756. idMultiplayerGame::Run
  2757. ================
  2758. */
  2759. void idMultiplayerGame::Run( void ) {
  2760.     pureReady = true;
  2761.  
  2762.     assert( gameLocal.isMultiplayer && gameLocal.isServer && gameState );
  2763.  
  2764.     CommonRun();
  2765.  
  2766.     CheckVote();
  2767.  
  2768.     CheckRespawns();
  2769.  
  2770.     CheckSpecialLights( );
  2771.  
  2772.     gameState->Run();
  2773.  
  2774.     gameState->SendState();
  2775.  
  2776.     // don't update the ping every frame to save bandwidth
  2777.     if ( gameLocal.time > pingUpdateTime ) {
  2778.         for ( int i = 0; i < gameLocal.numClients; i++ ) {
  2779.             playerState[i].ping = networkSystem->ServerGetClientPing( i );
  2780.         }
  2781.         pingUpdateTime = gameLocal.time + 1000;
  2782.     }
  2783.  
  2784.  
  2785. }
  2786.  
  2787. /*
  2788. ================
  2789. idMultiplayerGame::UpdateMainGui
  2790. ================
  2791. */
  2792. void idMultiplayerGame::UpdateMainGui( void ) {
  2793.     int i;
  2794.     mainGui->SetStateInt( "readyon", gameState->GetMPGameState() == WARMUP ? 1 : 0 );
  2795.     mainGui->SetStateInt( "readyoff", gameState->GetMPGameState() != WARMUP ? 1 : 0 );
  2796.     idStr strReady = cvarSystem->GetCVarString( "ui_ready" );
  2797.     if ( strReady.Icmp( "ready") == 0 ){
  2798.         strReady = common->GetLocalizedString( "#str_104248" );
  2799.     } else {
  2800.         strReady = common->GetLocalizedString( "#str_104247" );
  2801.     }
  2802.     mainGui->SetStateString( "ui_ready", strReady );
  2803.     mainGui->SetStateInt( "num_spec_players", unrankedPlayers.Num() );
  2804.     
  2805.     mainGui->SetStateInt( "gametype", gameLocal.gameType );
  2806.     mainGui->SetStateBool( "s_useOpenAL", cvarSystem->GetCVarBool( "s_useOpenAL" ) );
  2807.     mainGui->SetStateBool( "s_loadOpenALFailed", cvarSystem->GetCVarBool( "s_loadOpenALFailed" ) );
  2808.  
  2809.     idVec4    hitscanTint;
  2810.     idStr    hitScanValue = cvarSystem->GetCVarString( "ui_hitscanTint" );
  2811.     sscanf( hitScanValue.c_str(), "%f %f %f %f", &hitscanTint.x, &hitscanTint.y, &hitscanTint.z, &hitscanTint.w );
  2812.     mainGui->SetStateFloat( "ui_hitscanTint", hitscanTint.x );
  2813.  
  2814. // RAVEN BEGIN
  2815. // bdube: capture the flag
  2816.     if ( gameLocal.IsTeamGame() ) {
  2817.         idPlayer *p = gameLocal.GetClientByNum( gameLocal.localClientNum );
  2818.         if ( p ) {
  2819.             mainGui->SetStateInt( "team", p->team );
  2820.         }
  2821.         mainGui->SetStateInt( "teamon", 1 );
  2822.         mainGui->SetStateInt( "teamoff", 0 );
  2823.     } else {
  2824.         mainGui->SetStateInt( "teamon", 0 );
  2825.         mainGui->SetStateInt( "teamoff", 1 );
  2826.     }
  2827. // RAVEN END        
  2828.     mainGui->SetStateInt( "teamon", gameLocal.gameType == GAME_TDM ? 1 : 0 );
  2829.     mainGui->SetStateInt( "teamoff", gameLocal.gameType != GAME_TDM ? 1 : 0 );
  2830.     if ( gameLocal.gameType == GAME_TDM ) {
  2831.         idPlayer *p = gameLocal.GetClientByNum( gameLocal.localClientNum );
  2832.         if ( p ) {
  2833.             mainGui->SetStateInt( "team", p->team );
  2834.         }        
  2835.     }
  2836.     // setup vote
  2837.     mainGui->SetStateInt( "voteon", ( vote != VOTE_NONE && !voted ) ? 1 : 0 );
  2838.     mainGui->SetStateInt( "voteoff", ( vote != VOTE_NONE && !voted ) ? 0 : 1 );
  2839.     // send the current serverinfo values
  2840.     for ( i = 0; i < gameLocal.serverInfo.GetNumKeyVals(); i++ ) {
  2841.         const idKeyValue *keyval = gameLocal.serverInfo.GetKeyVal( i );
  2842.         mainGui->SetStateString( keyval->GetKey(), keyval->GetValue() );
  2843.     }
  2844.     mainGui->StateChanged( gameLocal.time );
  2845. #if defined( __linux__ )
  2846.     // replacing the oh-so-useful s_reverse with sound backend prompt
  2847.     mainGui->SetStateString( "driver_prompt", "1" );
  2848. #else
  2849.     mainGui->SetStateString( "driver_prompt", "0" );
  2850. #endif
  2851.  
  2852. //RAVEN BEGIN
  2853. // cnicholson: Add Custom Crosshair update
  2854.     mainGui->SetStateString( "g_crosshairCustom", cvarSystem->GetCVarBool( "g_crosshairCustom" ) ? "1" : "0" );
  2855. //RAVEN END
  2856.  
  2857. // RAVEN BEGIN
  2858. // cnicholson: We need to setup the custom crosshair so it shows up the first time the player enters the MP settings menu.
  2859. //               This block checks the current crosshair, and compares it against the list of crosshairs in player.def (mtr_crosshair*) under the 
  2860. //               player_marine_mp section. If it finds a match, it assigns the crosshair, otherwise, the first found crosshair is used.
  2861. #ifndef _XENON
  2862.     const idDeclEntityDef *defCH = static_cast<const idDeclEntityDef*>( declManager->FindType( DECL_ENTITYDEF, "player_marine_mp", false, true ) );
  2863. #else
  2864.     bool insideLevelLoad = declManager->GetInsideLoad();
  2865.     if ( !insideLevelLoad ) {
  2866.         declManager->SetInsideLoad( true );
  2867.     }
  2868.     const idDeclEntityDef *defCH = static_cast<const idDeclEntityDef*>( declManager->FindType( DECL_ENTITYDEF, "player_marine_mp_ui", false, false ) );
  2869.     declManager->SetInsideLoad( insideLevelLoad );
  2870. #endif
  2871.  
  2872. #ifndef _XENON
  2873.     idStr currentCrosshair = cvarSystem->GetCVarString("g_crosshairCustomFile");
  2874.  
  2875.     const idKeyValue* kv = defCH->dict.MatchPrefix("mtr_crosshair", NULL);
  2876.  
  2877.     while ( kv ) {                                                    // Loop through all crosshairs listed in the def
  2878.         if ( kv->GetValue() == currentCrosshair.c_str() ) {            // Until a match is found
  2879.             break;
  2880.         }
  2881.         kv = defCH->dict.MatchPrefix("mtr_crosshair", kv );
  2882.     }
  2883.  
  2884.     if ( !kv ){
  2885.         kv = defCH->dict.MatchPrefix("mtr_crosshair", NULL );            // If no natches are found, use the first one.
  2886.     }
  2887.  
  2888.     idStr newCrosshair(kv->GetValue());
  2889.  
  2890.     mainGui->SetStateString ( "crossImage", newCrosshair.c_str());
  2891.     cvarSystem->SetCVarString("g_crosshairCustomFile", newCrosshair.c_str());
  2892. #endif
  2893.  
  2894.  
  2895. //asalmon: Set up a state var for match type of Xbox 360
  2896. #ifdef _XENON
  2897.     mainGui->SetStateInt("MatchType", Live()->GetMatchtype());
  2898. #endif
  2899. // RAVEN END
  2900.  
  2901. }
  2902.  
  2903. /*
  2904. ================
  2905. idMultiplayerGame::StartMenu
  2906. ================
  2907. */
  2908. idUserInterface* idMultiplayerGame::StartMenu( void ) {
  2909.     if ( mainGui == NULL ) {
  2910.         return NULL;
  2911.     }
  2912.     //if we're the server, allow access to the admin tab right away. Otherwise, make sure we don't have it.
  2913.     if( gameLocal.isServer    )    {
  2914.         mainGui->SetStateInt( "password_valid", 1 );
  2915.     } else {
  2916.         mainGui->SetStateInt( "password_valid", 0 );
  2917.     }
  2918.  
  2919.     int i, j;
  2920.     if ( currentMenu ) {
  2921.         currentMenu = 0;
  2922.          cvarSystem->SetCVarBool( "ui_chat", false );
  2923.     } else {
  2924.         if ( nextMenu >= 2 ) {
  2925.             currentMenu = nextMenu;
  2926.         } else {
  2927.             // for default and explicit
  2928.             currentMenu = 1;
  2929.         }
  2930.          cvarSystem->SetCVarBool( "ui_chat", true );
  2931.     }
  2932.     
  2933.     if( gameLocal.GetLocalPlayer() ) {
  2934.         gameLocal.GetLocalPlayer()->disableHud = true;
  2935.     }
  2936.  
  2937.     nextMenu = 0;
  2938.     gameLocal.sessionCommand = "";    // in case we used "game_startMenu" to trigger the menu
  2939.     if ( currentMenu == 1 ) {
  2940.         UpdateMainGui();
  2941.  
  2942.         // UpdateMainGui sets most things, but it doesn't set these because
  2943.         // it'd be pointless and/or harmful to set them every frame (for various reasons)
  2944.         // Currenty the gui doesn't update properly if they change anyway, so we'll leave it like this.
  2945.  
  2946.         // setup callvote
  2947.         if ( vote == VOTE_NONE ) {
  2948.             bool callvote_ok = false;
  2949.             for ( i = 0; i < VOTE_COUNT; i++ ) {
  2950.                 // flag on means vote is denied, so default value 0 means all votes and -1 disables
  2951.                 mainGui->SetStateInt( va( "vote%d", i ), g_voteFlags.GetInteger() & ( 1 << i ) ? 0 : 1 );
  2952.                 if ( !( g_voteFlags.GetInteger() & ( 1 << i ) ) ) {
  2953.                     callvote_ok = true;
  2954.                 }
  2955.             }
  2956.             mainGui->SetStateInt( "callvote", callvote_ok );
  2957.         } else {
  2958.             mainGui->SetStateInt( "callvote", 2 );
  2959.         }
  2960.  
  2961.         // player kick data
  2962.         for ( i = 0; i < 16; i++ ) {
  2963.             kickVoteMapNames[ i ].Clear();
  2964.             kickVoteMap[ i ] = -1;
  2965.         }
  2966.  
  2967.         idStr kickList;
  2968.         j = 0;
  2969.         for ( i = 0; i < gameLocal.numClients; i++ ) {
  2970. // RAVEN BEGIN
  2971. // jnewquist: Use accessor for static class type 
  2972.             if ( gameLocal.entities[ i ] && gameLocal.entities[ i ]->IsType( idPlayer::GetClassType() ) ) {
  2973. // RAVEN END
  2974.                 if ( kickList.Length() ) {
  2975.                     kickList += ";";
  2976.                 }
  2977.                 kickList += va( "\"%d - %s\"", i, gameLocal.userInfo[ i ].GetString( "ui_name" ) );
  2978.                 kickVoteMap[ j ] = i;
  2979. // RAVEN BEGIN
  2980. // shouchard:  names for kick vote map
  2981.                 kickVoteMapNames[ j ] = gameLocal.userInfo[ i ].GetString( "ui_name" );
  2982. // RAVEN END
  2983.                 j++;
  2984.             }
  2985.         }
  2986.         mainGui->SetStateString( "kickChoices", kickList );
  2987.  
  2988.         mainGui->SetStateString( "chattext", "" );
  2989.         mainGui->Activate( true, gameLocal.time );
  2990.  
  2991.         idPlayer                *localP = gameLocal.GetLocalPlayer();
  2992. #ifndef _XENON
  2993.         const idDeclEntityDef    *def = gameLocal.FindEntityDef( "player_marine_mp", false );
  2994. #else
  2995.         const idDeclEntityDef    *def = gameLocal.FindEntityDef( "player_marine_mp_ui", false );
  2996. #endif
  2997.         if ( def ) {
  2998.             idStr    buildValues, buildNames;
  2999.             int        i;
  3000.  
  3001.             for( i=0; ; i++ )
  3002.             {
  3003.                 const char *resultValue = def->dict.GetString( va( "def_model%d", i ) );
  3004.  
  3005.                 if ( !resultValue || !resultValue[0] ) {
  3006.                     break;
  3007.                 }
  3008.  
  3009.                 const idDeclEntityDef *valueDef = gameLocal.FindEntityDef( resultValue, false );
  3010.                 if ( valueDef ) {
  3011.                     const char *team = valueDef->dict.GetString( "team" );
  3012.  
  3013.                     if ( gameLocal.IsTeamGame() ) {
  3014.                         if ( team && localP && localP->team >= 0 && localP->team < TEAM_MAX && idStr::Icmp( teamNames[ localP->team ], team ) == 0 ) {
  3015.                         } else {
  3016.                             // doesn't match, so skip
  3017.                             continue;
  3018.                         }
  3019.                     }
  3020.  
  3021.                     if ( i ) {
  3022.                         buildValues += ";";
  3023.                         buildNames += ";";
  3024.                     }
  3025.                     buildValues += resultValue;
  3026. // RAVEN BEGIN
  3027. // rhummer: Need to localize the default models we ship with, so if the name is found in the current lang dict use it, else don't.
  3028.                     const char *resultName = common->GetLocalizedString( valueDef->dict.GetString( "description" ) );
  3029. // RAVEN END
  3030.  
  3031.                     if ( !resultName || !resultName[0] ) {
  3032.                         buildNames += resultValue;
  3033.                     } else {
  3034.                         buildNames += resultName;
  3035.                     }
  3036.                 }
  3037.             }
  3038.             
  3039.             mainGui->SetStateString( "model_values", buildValues.c_str() );
  3040.             mainGui->SetStateString( "model_names", buildNames.c_str() );
  3041.             mainGui->SetStateBool( "player_model_updated", true );
  3042.         }
  3043.  
  3044.         const char *model;
  3045.         if ( localP && localP->team >= 0 && localP->team < TEAM_MAX ) {
  3046.             model = cvarSystem->GetCVarString( va( "ui_model_%s", teamNames[ localP->team ] ) );
  3047.             if( *model == '\0' ) {
  3048.                 model = def->dict.GetString( va( "def_default_model_%s", teamNames[ localP->team ] ) );
  3049.             }
  3050.         } else {
  3051.             model = cvarSystem->GetCVarString( "ui_model" );
  3052.             if( *model == '\0' ) {
  3053.                 model = def->dict.GetString( "def_default_model" );
  3054.             }
  3055.         }
  3056.         def = gameLocal.FindEntityDef( model, false );
  3057.         if ( def ) {
  3058.             mainGui->SetStateString( "player_model_name", def->dict.GetString( "model" ) );
  3059.             mainGui->SetStateString( "player_head_model_name", def->dict.GetString( "def_head" ) );
  3060.             mainGui->SetStateString( "player_skin_name", def->dict.GetString( "skin" ) );
  3061.             mainGui->SetStateBool( "need_update", true );
  3062.         }
  3063.  
  3064.         if( gameLocal.GetLocalPlayer() ) {
  3065.             cvarSystem->SetCVarString( "gui_ui_name", gameLocal.GetLocalPlayer()->GetUserInfo()->GetString( "ui_name" ) );
  3066.         }
  3067.  
  3068.         return mainGui;
  3069.     } else if ( currentMenu == 2 ) {
  3070.         // the setup is done in MessageMode
  3071.         if( gameLocal.GetLocalPlayer() ) {
  3072.             gameLocal.GetLocalPlayer()->disableHud = false;
  3073.         }
  3074.         msgmodeGui->Activate( true, gameLocal.time );
  3075.          cvarSystem->SetCVarBool( "ui_chat", true );
  3076.         return msgmodeGui;
  3077.     } else if ( currentMenu == 3 ) {
  3078.         statSummary->Activate( true, gameLocal.time );
  3079.         statManager->SetupStatWindow( statSummary );
  3080.         UpdateScoreboard( statSummary );
  3081.         UpdateSummaryBoard( statSummary );
  3082.         statSummary->SetStateFloat( "ready", 0 );
  3083.         statSummary->StateChanged( gameLocal.time );
  3084.         return statSummary;
  3085.     }
  3086.  
  3087.     return NULL;
  3088. }
  3089.  
  3090. /*
  3091. ================
  3092. idMultiplayerGame::DisableMenu
  3093. ================
  3094. */
  3095. void idMultiplayerGame::DisableMenu( void ) {
  3096.     gameLocal.sessionCommand = "";    // in case we used "game_startMenu" to trigger the menu
  3097.     if ( currentMenu == 1 ) {
  3098.         mainGui->Activate( false, gameLocal.time );
  3099.     } else if ( currentMenu == 2 ) {
  3100.         msgmodeGui->Activate( false, gameLocal.time );
  3101.     } else if( currentMenu == 3 ) {
  3102.         statSummary->Activate( false, gameLocal.time );
  3103.     }
  3104.  
  3105.     // copy over name from temp cvar
  3106.     if( currentMenu == 1 && idStr::Cmp( cvarSystem->GetCVarString( "gui_ui_name" ), cvarSystem->GetCVarString( "ui_name" ) ) ) {
  3107.         cvarSystem->SetCVarString( "ui_name", cvarSystem->GetCVarString( "gui_ui_name" ) );
  3108.     }
  3109.  
  3110.     currentMenu = 0;
  3111.     nextMenu = 0;
  3112.      cvarSystem->SetCVarBool( "ui_chat", false );
  3113.     
  3114.     if( gameLocal.GetLocalPlayer() ) {
  3115.         gameLocal.GetLocalPlayer()->disableHud = false;
  3116.         if( gameLocal.GetLocalPlayer()->mphud)    {
  3117.             gameLocal.GetLocalPlayer()->mphud->Activate( true, gameLocal.time );
  3118.         }
  3119.     }
  3120.  
  3121.     mainGui->DeleteStateVar( va( "sa_playerList_item_%d", 0 ) );
  3122.     mainGui->SetStateString( "sa_playerList_sel_0", "-1" );
  3123.  
  3124.     mainGui->DeleteStateVar( va( "sa_banList_item_%d", 0 ) );
  3125.     mainGui->SetStateString( "sa_banList_sel_0", "-1" );
  3126.  
  3127.     // asalmon: Need to refresh stats periodically if the player is looking at stats
  3128.     currentStatClient = -1;
  3129.     currentStatTeam = -1;
  3130. }
  3131.  
  3132. /*
  3133. ================
  3134. idMultiplayerGame::SetMapShot
  3135. ================
  3136. */
  3137. void idMultiplayerGame::SetMapShot( void ) {
  3138. #ifdef _XENON    
  3139.     // Should not be used
  3140.     assert( 0 );
  3141. #else
  3142.     char screenshot[ MAX_STRING_CHARS ];
  3143.     int mapNum = mapList->GetSelection( NULL, 0 );
  3144.     const idDict *dict = NULL;
  3145.     if ( mapNum >= 0 ) {
  3146.         dict = fileSystem->GetMapDecl( mapNum );
  3147.     }
  3148.     fileSystem->FindMapScreenshot( dict ? dict->GetString( "path" ) : "", screenshot, MAX_STRING_CHARS );
  3149.     mainGui->SetStateString( "current_levelshot", screenshot );
  3150. // RAVEN BEGIN
  3151. // cnicholson: Need to sort the material screenshot so it doesn't overlap other things
  3152.     const idMaterial *mat = declManager->FindMaterial( screenshot );
  3153.     mat->SetSort( SS_GUI );
  3154. // RAVEN END
  3155. #endif
  3156. }
  3157.  
  3158. /*
  3159. ================
  3160. LocalServerRedirect
  3161. Dummy local redirect for gui rcon functionality on a local server
  3162. ================
  3163. */
  3164. void LocalServerRedirect( const char* string ) {
  3165.     gameLocal.mpGame.ReceiveRemoteConsoleOutput( string );
  3166. }
  3167.  
  3168. /*
  3169. ================
  3170. idMultiplayerGame::HandleGuiCommands
  3171. ================
  3172. */
  3173. const char* idMultiplayerGame::HandleGuiCommands( const char *_menuCommand ) {
  3174.     idUserInterface    *currentGui;
  3175. // RAVEN BEGIN
  3176. // shouchard:  removed the code that deals with these variables
  3177.     //const char        *voteValue;
  3178.     //int                vote_clientNum;
  3179. // RAVEN END
  3180.     int                icmd;
  3181.     idCmdArgs        args;
  3182.  
  3183.  
  3184.  
  3185.     if ( !_menuCommand[ 0 ] ) {
  3186.         common->Printf( "idMultiplayerGame::HandleGuiCommands: empty command\n" );
  3187.         return "continue";
  3188.     }
  3189.     
  3190. #ifdef _XENON
  3191.     if ( currentMenu == 0 && (session->GetActiveGUI() != scoreBoard) ) {
  3192. #else
  3193.     if ( currentMenu == 0 ) {
  3194. #endif
  3195.         return NULL; // this will tell session to not send us events/commands anymore
  3196.     }
  3197.  
  3198.     if ( currentMenu == 1 ) {
  3199.         currentGui = mainGui;
  3200.     } 
  3201. #ifdef _XENON
  3202.     else if (session->GetActiveGUI() != scoreBoard) {
  3203.         currentGui = msgmodeGui;
  3204.     } else {
  3205.         currentGui = scoreBoard;
  3206.     }
  3207. #else
  3208.     else if( currentMenu == 2 ) {
  3209.         currentGui = msgmodeGui;
  3210.     } else if( currentMenu == 3 ) {
  3211.         currentGui = statSummary;
  3212.     } else {
  3213.         gameLocal.Warning( "idMultiplayerGame::HandleGuiCommands() - Unknown current menu '%d'\n", currentMenu );
  3214.         currentGui = mainGui;
  3215.     }
  3216. #endif
  3217.     
  3218.  
  3219.      args.TokenizeString( _menuCommand, false );
  3220.  
  3221.     for( icmd = 0; icmd < args.Argc(); ) {
  3222.         const char *cmd = args.Argv( icmd++ );
  3223.  
  3224.         if ( !idStr::Icmp( cmd,    ";"    ) )    {
  3225.             continue;
  3226.         } else if ( !idStr::Icmp( cmd, "inGameMenu" ) ) {
  3227.             if ( args.Argc() - icmd    >= 1 ) {
  3228.                 idStr igArg = args.Argv( icmd++ );
  3229.                 if( !igArg.Icmp( "init" ) ) {
  3230.                     currentGui->SetStateString( "chat", chatHistory.c_str() );
  3231.  
  3232.                     if( gameLocal.GetLocalPlayer() ) {
  3233.                         currentGui->SetStateInt( "player_team", gameLocal.GetLocalPlayer()->team );
  3234.                     }
  3235.  
  3236.                     // mekberg: added
  3237.                     UpdateMPSettingsModel ( currentGui );
  3238.  
  3239.                     if( gameLocal.gameType == GAME_TOURNEY ) {
  3240.                         if( !idStr::Icmp( cvarSystem->GetCVarString( "ui_spectate" ), "Spectate" ) ) {
  3241.                             currentGui->SetStateString( "toggleTourneyButton", common->GetLocalizedString( "#str_107699" ) );
  3242.                         } else {
  3243.                             currentGui->SetStateString( "toggleTourneyButton", common->GetLocalizedString( "#str_107700" ) );
  3244.                         }    
  3245.                     }
  3246.                     
  3247.                     currentGui->SetStateBool( "useReady", gameLocal.serverInfo.GetBool( "si_useReady", "0" ) && gameState->GetMPGameState() == WARMUP );
  3248.                     if( gameLocal.serverInfo.GetBool( "si_useReady" ) && gameLocal.GetLocalPlayer() && gameLocal.GetLocalPlayer()->IsReady() && gameState->GetMPGameState() == WARMUP ) {
  3249.                         currentGui->SetStateString( "readyStatus", common->GetLocalizedString( "#str_104247" ) );
  3250.                     } else if( gameLocal.serverInfo.GetBool( "si_useReady" ) && gameLocal.GetLocalPlayer() && !gameLocal.GetLocalPlayer()->IsReady() && gameState->GetMPGameState() == WARMUP ) {
  3251.                         currentGui->SetStateString( "readyStatus", common->GetLocalizedString( "#str_104248" ) );
  3252.                     } else {
  3253.                         currentGui->SetStateString( "readyStatus", "" );
  3254.                     }
  3255.  
  3256.                     currentGui->SetStateBool( "si_allowVoting", gameLocal.serverInfo.GetBool( "si_allowVoting" ) );
  3257.  
  3258.                 }
  3259.             }
  3260.             continue;
  3261.         } else if (    !idStr::Icmp( cmd, "video" ) ) {
  3262.             idStr vcmd;
  3263.             if ( args.Argc() - icmd    >= 1 ) {
  3264.                 vcmd = args.Argv( icmd++ );
  3265.             }
  3266.  
  3267.             if ( idStr::Icmp( vcmd,    "low" )    == 0 ) {
  3268.                 cvarSystem->SetCVarInteger(    "com_machineSpec", 0 );
  3269.             } else if (    idStr::Icmp( vcmd, "medium"    ) == 0 ) {
  3270.                 cvarSystem->SetCVarInteger(    "com_machineSpec", 1 );
  3271.             } else    if ( idStr::Icmp( vcmd,    "high" ) ==    0 )    {
  3272.                 cvarSystem->SetCVarInteger(    "com_machineSpec", 2 );
  3273.             } else    if ( idStr::Icmp( vcmd,    "ultra"    ) == 0 ) {
  3274.                 cvarSystem->SetCVarInteger(    "com_machineSpec", 3 );
  3275.             } else if (    idStr::Icmp( vcmd, "recommended" ) == 0    ) {
  3276.                 cmdSystem->BufferCommandText( CMD_EXEC_NOW,    "setMachineSpec\n" );
  3277.             }
  3278.  
  3279. // RAVEN BEGIN
  3280. // mekberg: set the r_mode.
  3281.             currentGui->SetStateInt( "com_machineSpec", cvarSystem->GetCVarInteger( "com_machineSpec" ) );
  3282.             currentGui->StateChanged( gameLocal.realClientTime );
  3283.             cvarSystem->SetCVarInteger( "r_mode", common->GetRModeForMachineSpec ( cvarSystem->GetCVarInteger( "com_machineSpec" ) ) );
  3284.             common->SetDesiredMachineSpec( cvarSystem->GetCVarInteger( "com_machineSpec" ) );
  3285. // RAVEN END
  3286.  
  3287.             if ( idStr::Icmp( vcmd,    "restart" )     ==    0) {
  3288.                 cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "vid_restart\n" );
  3289.             }
  3290.  
  3291.             continue;
  3292.         } else if (    !idStr::Icmp( cmd, "join" )    ) {
  3293.             if ( args.Argc() - icmd    >= 1 ) {
  3294.                 JoinTeam( args.Argv( icmd++ ) );
  3295.             }
  3296.             continue;
  3297.         } else if (    !idStr::Icmp( cmd, "quit" )    ) {
  3298.             cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "quit\n"    );
  3299.             return NULL;
  3300.         } else if (    !idStr::Icmp( cmd, "disconnect"    ) )    {
  3301.             cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "disconnect\n" );
  3302.             return NULL;
  3303.         } else if (    !idStr::Icmp( cmd, "close" ) ) {
  3304.             DisableMenu( );
  3305.             return NULL;
  3306.         } else if (    !idStr::Icmp( cmd, "spectate" )    ) {
  3307.             ToggleSpectate();
  3308.             DisableMenu( );
  3309.             return NULL;
  3310.         } else if ( !idStr::Icmp( cmd, "admin" ) ) {
  3311.             if ( args.Argc() - icmd    >= 1 ) {
  3312.                 idStr igArg = args.Argv( icmd++ );
  3313.                 idStr input( currentGui->State().GetString( "admin_console_input" ) );
  3314.                 input.StripTrailing( "\n" );
  3315.                 //jshepard: check to see if this is a server before using rcon!
  3316.                 if( gameLocal.isServer ) {
  3317.                     char redirectBuffer[ RCON_HISTORY_SIZE ];
  3318.                     common->BeginRedirect( (char *)redirectBuffer, sizeof( redirectBuffer ), LocalServerRedirect );
  3319.  
  3320.                     if( !igArg.Icmp( "tab" ) ) {
  3321.                         cmdSystem->BufferCommandText( CMD_EXEC_NOW, va( "tabComplete \"%s\"\n", input.c_str() ) );
  3322.                     } else if( !igArg.Icmp( "command" ) ) {
  3323.                         currentGui->SetStateString( "admin_console_input", "" );
  3324.                         ReceiveRemoteConsoleOutput( input.c_str() );
  3325.                         cmdSystem->BufferCommandText( CMD_EXEC_NOW, va( "%s\n", input.c_str() ) );
  3326.                     }
  3327.  
  3328.                     common->EndRedirect();
  3329.                 } else {
  3330.                     if( !igArg.Icmp( "tab" ) ) {
  3331.                         cmdSystem->BufferCommandText( CMD_EXEC_APPEND, va( "rcon tabComplete \"%s\"\n", input.c_str() ) );
  3332.                     } else if( !igArg.Icmp( "command" ) ) {
  3333.                         currentGui->SetStateString( "admin_console_input", "" );
  3334.                         cmdSystem->BufferCommandText( CMD_EXEC_APPEND, va( "rcon \"%s\"\n", input.c_str() ) );
  3335.                         ReceiveRemoteConsoleOutput( input.c_str() );
  3336.                     }
  3337.                 }
  3338.             }
  3339.             continue;
  3340.         } else if (    !idStr::Icmp( cmd, "chatmessage" ) ) {
  3341.             int    mode = currentGui->State().GetInt( "messagemode" );
  3342. // RAVEN BEGIN
  3343. // bdube: dont send chat message if there was no text specified
  3344.             const char* text;
  3345.             text = currentGui->State().GetString( "chattext" );
  3346.             if ( *text ) {
  3347.                 if ( mode ) {
  3348.                     cmdSystem->BufferCommandText( CMD_EXEC_NOW, va( "sayTeam \"%s\"", text ) );
  3349.                 } else {
  3350.                     cmdSystem->BufferCommandText( CMD_EXEC_NOW, va( "say \"%s\"", text ) );
  3351.                 }
  3352.             }
  3353. // RAVEN BEGIN        
  3354.             currentGui->SetStateString(    "chattext",    "" );
  3355.             if ( currentMenu ==    1 || currentMenu == 3 )    {
  3356.                 return "continue";
  3357.             } else {
  3358.                 DisableMenu();
  3359.                 return NULL;
  3360.             }
  3361.         } else if (    !idStr::Icmp( cmd, "toggleReady" ) ) {
  3362.             ToggleReady( );
  3363.             DisableMenu( );
  3364.             return NULL;
  3365.         } else if (    !idStr::Icmp( cmd, "play" )    ) {
  3366.             if ( args.Argc() - icmd    >= 1 ) {
  3367.                 idStr snd =    args.Argv( icmd++ );
  3368.                 int    channel    = 1;
  3369.                 if ( snd.Length() == 1 ) {
  3370.                     channel    = atoi(    snd    );
  3371.                     snd    = args.Argv( icmd++    );
  3372.                 }
  3373.                 soundSystem->PlayShaderDirectly( SOUNDWORLD_GAME, snd, channel );
  3374.             }
  3375.             continue;
  3376.         } else if (    !idStr::Icmp( cmd, "callVote" )    ) {
  3377. // RAVEN BEGIN
  3378. // shouchard:  new functionality to match the new interface
  3379.             voteStruct_t voteData;
  3380.             memset( &voteData, 0, sizeof( voteData ) );
  3381.             
  3382.             // kick
  3383.             int uiKickSelection = mainGui->State().GetInt( "playerList_sel_0" );
  3384.             if ( -1 != uiKickSelection ) {
  3385.                 voteData.m_kick = kickVoteMap[ uiKickSelection ];
  3386.                 voteData.m_fieldFlags |= VOTEFLAG_KICK;
  3387.             }
  3388.             // restart
  3389.             if ( 0 != mainGui->State().GetInt( "vote_val2_sel" ) ) {
  3390.                 voteData.m_fieldFlags |= VOTEFLAG_RESTART;
  3391.             }
  3392.             // map
  3393.             int uiMapSelection = mainGui->State().GetInt( "mapList_sel_0" );
  3394.             if ( -1 != uiMapSelection ) {
  3395. // rjohnson: code commented out below would get the text friendly name of the map and not the file name
  3396.                 int mapNum = mainGui->State().GetInt( va( "mapList_item_%d_id", uiMapSelection ) );
  3397.                 if ( mapNum >= 0 ) {
  3398.                     const idDict *dict = fileSystem->GetMapDecl( mapNum );
  3399.                     voteData.m_map = dict->GetString( "path" );
  3400.                     voteData.m_fieldFlags |= VOTEFLAG_MAP;
  3401.                 }
  3402. //                const char *mapName = mainGui->State().GetString( va( "mapList_item_%d", uiMapSelection ) );
  3403. //                if ( NULL != mapName && '\0' != mapName[0] ) {
  3404. //                if ( mapFileName[ 0 ] ) {
  3405. //                    voteData.m_map = va( "mp/%s", mapName );
  3406. //                    voteData.m_fieldFlags |= VOTEFLAG_MAP;
  3407. //                }
  3408.             }
  3409.             // gametype
  3410.             // todo:  need a function for switching between gametype strings and values
  3411.             int uiGameTypeInt = mainGui->GetStateInt( "currentGametype" );
  3412.             const char *currentGameTypeString = gameLocal.serverInfo.GetString( "si_gametype" );
  3413.             int serverGameTypeInt = GameTypeToVote( currentGameTypeString );
  3414.  
  3415.             if ( uiGameTypeInt != serverGameTypeInt ) {
  3416.                 voteData.m_gameType = uiGameTypeInt;
  3417.                 voteData.m_fieldFlags |= VOTEFLAG_GAMETYPE;
  3418.             }
  3419.             // time limit
  3420.             int uiTimeLimit = mainGui->GetStateInt( "timeLimit" );
  3421.             if ( uiTimeLimit != gameLocal.serverInfo.GetInt( "si_timeLimit" ) ) {
  3422.                 voteData.m_timeLimit = uiTimeLimit;
  3423.                 voteData.m_fieldFlags |= VOTEFLAG_TIMELIMIT;
  3424.             }
  3425.             // autobalance
  3426.             int uiBalanceTeams = mainGui->GetStateInt( "vote_val6_sel" );
  3427.             if ( uiBalanceTeams != gameLocal.serverInfo.GetInt( "si_autobalance" ) ) {
  3428.                 voteData.m_teamBalance = uiBalanceTeams;
  3429.                 voteData.m_fieldFlags |= VOTEFLAG_TEAMBALANCE;
  3430.             }
  3431.             // allow spectators
  3432.             /* int uiAllowSpectators = mainGui->GetStateInt( "vote_val7_sel" );
  3433.             if ( uiAllowSpectators != gameLocal.serverInfo.GetInt( "si_spectators" ) ) {
  3434.                 voteData.m_spectators = uiAllowSpectators;
  3435.                 voteData.m_fieldFlags |= VOTEFLAG_SPECTATORS;
  3436.             } */
  3437.             // minimum players 
  3438.             int uiMinPlayers = mainGui->GetStateInt( "minplayers" );
  3439.             if ( uiMinPlayers != gameLocal.serverInfo.GetInt( "si_minPlayers" ) ) {
  3440.                 voteData.m_minPlayers = uiMinPlayers;
  3441.                 voteData.m_fieldFlags |= VOTEFLAG_MIN_PLAYERS;
  3442.             } 
  3443.             // roundlimit (tourney only)
  3444.             int uiTourneyLimit = mainGui->GetStateInt( "tourneylimit" );
  3445.             if ( uiTourneyLimit != gameLocal.serverInfo.GetInt( "si_tourneyLimit" ) ) {
  3446.                 voteData.m_tourneyLimit = uiTourneyLimit;
  3447.                 voteData.m_fieldFlags |= VOTEFLAG_TOURNEYLIMIT;
  3448.             }
  3449.             // capturelimit (ctf only)
  3450.             int uiCaptureLimit = mainGui->GetStateInt( "capturelimit" );
  3451.             if ( uiCaptureLimit != gameLocal.serverInfo.GetInt( "si_captureLimit" ) ) {
  3452.                 voteData.m_captureLimit = uiCaptureLimit;
  3453.                 voteData.m_fieldFlags |= VOTEFLAG_CAPTURELIMIT;
  3454.             }
  3455.             // fraglimit (DM & TDM only)
  3456.             int uiFragLimit = mainGui->GetStateInt( "fraglimit" );
  3457.             if ( uiFragLimit != gameLocal.serverInfo.GetInt( "si_fragLimit" ) ) {
  3458.                 voteData.m_fragLimit = uiFragLimit;
  3459.                 voteData.m_fieldFlags |= VOTEFLAG_FRAGLIMIT;
  3460.             }
  3461.             DisableMenu();
  3462.             // this means we haven't changed anything
  3463.             if ( 0 == voteData.m_fieldFlags ) {
  3464.                 //AddChatLine( common->GetLocalizedString( "#str_104400" ) );
  3465.             } else {
  3466.                 ClientCallPackedVote( voteData );
  3467.             }
  3468.             /*
  3469.             // sjh:  original doom code here
  3470.             vote_flags_t voteIndex = (vote_flags_t)mainGui->State().GetInt(    "voteIndex"    );
  3471.             if ( voteIndex == VOTE_MAP ) {
  3472.                 int mapNum = mapList->GetSelection( NULL, 0 );
  3473.                 if ( mapNum >= 0 ) {
  3474.                     const idDict *dict = fileSystem->GetMapDecl( mapNum );
  3475.                     if ( dict ) {
  3476.                         ClientCallVote( VOTE_MAP, dict->GetString( "path" ) );
  3477.                     }
  3478.                 }
  3479.             } else {
  3480.                 voteValue =    mainGui->State().GetString(    "str_voteValue"    );
  3481.                 if ( voteIndex == VOTE_KICK    ) {
  3482.                     vote_clientNum = kickVoteMap[ atoi(    voteValue )    ];
  3483.                     ClientCallVote(    voteIndex, va( "%d", vote_clientNum    ) );
  3484.                 } else {
  3485.                     ClientCallVote(    voteIndex, voteValue );
  3486.                 }
  3487.             }
  3488.             */
  3489.             return NULL;
  3490.         } else if ( !idStr::Icmp( cmd, "voteYes" ) ) {
  3491.             gameLocal.mpGame.CastVote( gameLocal.localClientNum, true );
  3492.             DisableMenu();
  3493.             return NULL;
  3494.         } else if ( !idStr::Icmp( cmd, "voteNo" ) ) {
  3495.             gameLocal.mpGame.CastVote( gameLocal.localClientNum, false );
  3496.             DisableMenu();
  3497.             return NULL;
  3498.         } else if ( !idStr::Icmp( cmd, "click_playerList" ) ) {
  3499.             // push data into the name field
  3500.             int sel = mainGui->GetStateInt( "playerList_sel_0" );
  3501.             if ( -1 == sel ) {
  3502.                 mainGui->SetStateString( "playerKick", "" );
  3503.             } else { 
  3504.                 mainGui->SetStateString( "playerKick", kickVoteMapNames[ sel ] );
  3505.             }
  3506.             continue;
  3507.         } else if ( !idStr::Icmp( cmd, "click_voteMapList" ) ) {
  3508.             int sel = mainGui->GetStateInt( "mapList_sel_0" );
  3509.             if ( -1 == sel ) {
  3510.                 mainGui->SetStateString( "mapName", "" );
  3511.             } else {
  3512.                 mainGui->SetStateString( "mapName", mainGui->GetStateString( va( "mapList_item_%d", sel ) ) );
  3513.             }
  3514.             continue;
  3515.         } else if ( !idStr::Icmp( cmd, "setVoteMapList" ) ) {
  3516. #ifdef _XENON
  3517.             // Xenon should not get here
  3518.             assert( 0 );
  3519. #else
  3520.             int numMaps = fileSystem->GetNumMaps();
  3521.             const idDict *dict;
  3522.             int numMapsAdded = 0;
  3523.             int i;
  3524.             int gameTypeInt = mainGui->GetStateInt( "currentGametype" );
  3525.             const char *gameType = NULL;
  3526.             switch ( gameTypeInt ) {
  3527.                 case VOTE_GAMETYPE_DM:
  3528.                     gameType = "DM";
  3529.                     break;
  3530.                 case VOTE_GAMETYPE_TOURNEY:
  3531.                     gameType = "Tourney";
  3532.                     break;
  3533.                 case VOTE_GAMETYPE_TDM:
  3534.                     gameType = "Team DM";
  3535.                     break;
  3536.                 case VOTE_GAMETYPE_CTF:
  3537.                     gameType = "CTF";
  3538.                     break;
  3539.                 case VOTE_GAMETYPE_ARENA_CTF:
  3540.                     gameType = "Arena CTF";
  3541.                     break;
  3542.             }
  3543.             if ( NULL == gameType || 0 == *gameType || 0 == idStr::Icmp( gameType, "singleplayer" ) ) {
  3544.                 gameType = "DM";
  3545.             }
  3546.             for ( i = 0; i < numMaps; i++ ) {
  3547.                 dict = fileSystem->GetMapDecl( i );
  3548.                 bool mapOk = false;
  3549.                 //if the gametype is DM, check for any of these types...
  3550.                 if( !(strcmp( gameType, "DM")) || !(strcmp( gameType, "Team DM")) )    {
  3551.                     if ( dict && ( 
  3552.                         dict->GetBool( "DM" ) || 
  3553.                         dict->GetBool( "Team DM" ) || 
  3554.                         dict->GetBool( "CTF" ) || 
  3555.                         dict->GetBool( "Tourney" ) ||
  3556.                         dict->GetBool( "Arena CTF" ))
  3557.                         ) {
  3558.                             mapOk = true;
  3559.                         }
  3560.                         //but if not, match the gametype.
  3561.                 } else if ( dict && dict->GetBool( gameType ) ) { 
  3562.                     mapOk = true;            
  3563.                 }
  3564.                 if( mapOk )    {
  3565.                     const char *mapName = dict->GetString( "name" );
  3566.                     if ( '\0' == mapName[ 0 ] ) {
  3567.                         mapName = dict->GetString( "path" );
  3568.                     }
  3569.                     mapName = common->GetLocalizedString( mapName );
  3570.                     mainGui->SetStateString( va( "mapList_item_%d", numMapsAdded), mapName );
  3571.                     mainGui->SetStateInt( va( "mapList_item_%d_id", numMapsAdded), i );
  3572.                     numMapsAdded++;
  3573.                 }
  3574.             }
  3575.             mainGui->DeleteStateVar( va( "mapList_item_%d", numMapsAdded ) );
  3576.             mainGui->SetStateString( "mapList_sel_0", "-1" );
  3577. #endif
  3578.             continue;
  3579.         } else if ( !idStr::Icmp( cmd, "setVoteData" ) ) {
  3580. #ifdef _XENON
  3581.             // Xenon should not get here
  3582.             assert( 0 );
  3583. #else
  3584.             // push data into the vote_ cvars so the UI can start at where we currently are
  3585.             int players;
  3586.             for ( players=0; players<gameLocal.numClients; players++ ) { 
  3587.                 mainGui->SetStateString( va( "playerList_item_%d", players ), kickVoteMapNames[players] );
  3588.             }
  3589.             if ( players < MAX_CLIENTS ) {
  3590.                 mainGui->DeleteStateVar( va( "playerList_item_%d", players ) );
  3591.             }
  3592.             mainGui->SetStateString( "playerList_sel_0", "-1" );
  3593.             mainGui->SetStateString( "playerKick", "" );
  3594.             mainGui->SetStateInt( "vote_val2_sel", 0 );
  3595.  
  3596. // RAVEN BEGIN
  3597. // mekberg: get localized string.
  3598.             const char *mapName = gameLocal.serverInfo.GetString( "si_map" );
  3599.             const idDeclEntityDef *mapDef = static_cast<const idDeclEntityDef *>(declManager->FindType( DECL_MAPDEF, mapName, false ));
  3600.             if ( mapDef ) {
  3601.                 mapName = common->GetLocalizedString( mapDef->dict.GetString( "name", mapName ) );
  3602.             }
  3603.             mainGui->SetStateString( "mapName", mapName );
  3604. // RAVEN END
  3605.             int numMaps = fileSystem->GetNumMaps();
  3606.             const idDict *dict;
  3607.             int numMapsAdded = 0;
  3608.             int i;
  3609.             const char *gameType = gameLocal.serverInfo.GetString( "si_gametype" ); // this will need to change for multi-votes
  3610.             if ( NULL == gameType || 0 == *gameType || 0 == idStr::Icmp( gameType, "singleplayer" ) ) {
  3611.                 gameType = "DM";
  3612.             }
  3613. // RAVEN BEGIN
  3614. // jshepard: if gametype is DM, then we can play on DM, TeamDM, Tourney or CTF maps. So, all of them, basically.
  3615.  
  3616.             for ( i = 0; i < numMaps; i++ ) {
  3617.                 dict = fileSystem->GetMapDecl( i );
  3618.                 bool mapOk = false;
  3619.                 //if the gametype is DM, check for any of these types...
  3620.                 if( !(strcmp( gameType, "DM")) || !(strcmp( gameType, "Team DM")) )    {
  3621.                     if ( dict && ( 
  3622.                         dict->GetBool( "DM" ) || 
  3623.                         dict->GetBool( "Team DM" ) || 
  3624.                         dict->GetBool( "CTF" ) || 
  3625.                         dict->GetBool( "Tourney" ) ||
  3626.                         dict->GetBool( "Arena CTF" ))
  3627.                         ) {
  3628.                     mapOk = true;
  3629.                     }
  3630.                 //but if not, match the gametype.
  3631.                 } else if ( dict && dict->GetBool( gameType ) ) { 
  3632.                     mapOk = true;            
  3633.                 }
  3634.                 if( mapOk )    {
  3635.                     const char *mapName = dict->GetString( "name" );
  3636.                     if ( '\0' == mapName[ 0 ] ) {
  3637.                         mapName = dict->GetString( "path" );
  3638.                     }
  3639.                     mapName = common->GetLocalizedString( mapName );
  3640.                     mainGui->SetStateString( va( "mapList_item_%d", numMapsAdded), mapName );
  3641.                     numMapsAdded++;
  3642.                 }
  3643.             }
  3644.             mainGui->DeleteStateVar( va( "mapList_item_%d", numMapsAdded ) );
  3645.             mainGui->SetStateString( "mapList_sel_0", "-1" );
  3646.             const char *currentGameTypeString = gameLocal.serverInfo.GetString( "si_gameType" );
  3647.             int uiGameTypeInt = GameTypeToVote( currentGameTypeString );
  3648.             mainGui->SetStateInt( "currentGametype", uiGameTypeInt );
  3649.             mainGui->SetStateInt( "timelimit", gameLocal.serverInfo.GetInt( "si_timeLimit" ) );
  3650.             mainGui->SetStateInt( "tourneylimit", gameLocal.serverInfo.GetInt( "si_tourneyLimit" ) );
  3651.             mainGui->SetStateInt( "capturelimit", gameLocal.serverInfo.GetInt( "si_captureLimit" ) );
  3652.             mainGui->SetStateInt( "vote_val6_sel", gameLocal.serverInfo.GetInt( "si_autobalance" ) );
  3653.             mainGui->SetStateInt( "minplayers", gameLocal.serverInfo.GetInt( "si_minPlayers" ) );
  3654.             mainGui->SetStateInt( "fraglimit", gameLocal.serverInfo.GetInt( "si_fraglimit" ) );
  3655.             mainGui->StateChanged( gameLocal.time );
  3656.             mainGui->HandleNamedEvent( "gametypeChange" );
  3657. #endif
  3658.             continue;
  3659.         } else if ( !idStr::Icmp( cmd, "populateServerInfo" ) ) {
  3660. #ifdef _XENON
  3661.             // Xenon should not get here
  3662.             assert( 0 );
  3663. #else
  3664.             mainGui->SetStateString( "serverInfoList_item_0", va( "%s:\t%s", common->GetLocalizedString( "#str_107725" ), gameLocal.serverInfo.GetString( "si_name" ) ) );
  3665.             idStr serverAddress = networkSystem->GetServerAddress( );
  3666.             mainGui->SetStateString( "serverInfoList_item_1", va( "%s:\t%s", common->GetLocalizedString( "#str_107726" ), serverAddress.c_str() ) );
  3667.             mainGui->SetStateString( "serverInfoList_item_2", va( "%s:\t%s", common->GetLocalizedString( "#str_107727" ), gameLocal.serverInfo.GetString( "si_gametype" ) ) );
  3668.  
  3669.             const char *mapName = gameLocal.serverInfo.GetString( "si_map" );
  3670.             const idDeclEntityDef *mapDef = static_cast<const idDeclEntityDef *>(declManager->FindType( DECL_MAPDEF, mapName, false ));
  3671.             if ( mapDef ) {
  3672.                 mapName = common->GetLocalizedString( mapDef->dict.GetString( "name", mapName ) );
  3673.             }
  3674. // rhummer localized "map name"
  3675.             mainGui->SetStateString( "serverInfoList_item_3", va( "%s\t%s", common->GetLocalizedString( "#str_107730" ), mapName ) );
  3676.             const char *gameType = gameLocal.serverInfo.GetString( "si_gametype" );
  3677.             if ( 0 == idStr::Icmp( gameType, "CTF" ) ) {
  3678.                 mainGui->SetStateString( "serverInfoList_item_4", va( "%s:\t%s", common->GetLocalizedString( "#str_107661" ), gameLocal.serverInfo.GetString( "si_captureLimit" ) ) );
  3679.             }
  3680.             else if ( 0 == idStr::Icmp( gameType, "DM" ) || 0 == idStr::Icmp( gameType, "Team DM" ) ) {
  3681.                 mainGui->SetStateString( "serverInfoList_item_4", va( "%s:\t%s", common->GetLocalizedString( "#str_107660" ), gameLocal.serverInfo.GetString( "si_fragLimit" ) ) );
  3682.             }
  3683.             mainGui->SetStateString( "serverInfoList_item_5", va( "%s:\t%s", common->GetLocalizedString( "#str_107659" ), gameLocal.serverInfo.GetString( "si_timeLimit" ) ) );
  3684.             mainGui->SetStateString( "serverInfoList_item_6", va( "%s:\t%s", common->GetLocalizedString( "#str_107662" ), gameLocal.serverInfo.GetString( "si_pure" ) ) );
  3685.             mainGui->SetStateString( "serverInfoList_item_7", va( "%s:\t%s", common->GetLocalizedString( "#str_107663" ), gameLocal.serverInfo.GetString( "si_maxPlayers" ) ) );
  3686.             mainGui->SetStateString( "serverInfoList_item_8", va( "%s:\t%s", common->GetLocalizedString( "#str_107664" ), gameLocal.serverInfo.GetString( "si_teamDamage" ) ) );
  3687.             mainGui->SetStateString( "serverInfoList_item_9", va( "%s:\t%s", common->GetLocalizedString( "#str_104254" ), gameLocal.serverInfo.GetString( "si_spectators" ) ) );
  3688. #endif
  3689.             continue;
  3690.         // handler for the server admin tab (normal stuff)
  3691.         } else if ( !idStr::Icmp( cmd, "checkAdminPass" )) {
  3692.             //password has been added, so call the rcon verifypassword command
  3693.             cmdSystem->BufferCommandText( CMD_EXEC_NOW, "rcon verifyRconPass" );            
  3694.             continue;
  3695.     
  3696.         } else if ( !idStr::Icmp( cmd, "initServerAdmin" ) ) {
  3697.             mainGui->SetStateInt( "admin_server_val1_sel", 0 ); // restart defaults to off
  3698.             // maplist handled in initServerAdminMaplist; this needs to be called first
  3699.             // to properly set the gametype since we read it back to show an appropriate list
  3700.             const char *currentGameTypeString = gameLocal.serverInfo.GetString( "si_gameType" );
  3701.             int uiGameTypeInt = GameTypeToVote( currentGameTypeString );
  3702.             mainGui->SetStateInt( "admincurrentGametype", uiGameTypeInt );
  3703.             mainGui->SetStateInt( "sa_timelimit", gameLocal.serverInfo.GetInt( "si_timeLimit" ) );
  3704.             mainGui->SetStateInt( "sa_tourneylimit", gameLocal.serverInfo.GetInt( "si_tourneyLimit" ) );
  3705.             mainGui->SetStateInt( "sa_capturelimit", gameLocal.serverInfo.GetInt( "si_captureLimit" ) );
  3706.             mainGui->SetStateInt( "sa_autobalance", gameLocal.serverInfo.GetInt( "si_autobalance" ) );
  3707.             mainGui->SetStateInt( "sa_minPlayers", gameLocal.serverInfo.GetInt( "si_minPlayers" ) );
  3708.             mainGui->SetStateInt( "sa_fraglimit", gameLocal.serverInfo.GetInt( "si_fraglimit" ) );
  3709.  
  3710. // mekberg: get the ban list if not server
  3711.             if ( !gameLocal.isServer ) {
  3712.                 idBitMsg    outMsg;
  3713.                 byte        msgBuf[ MAX_GAME_MESSAGE_SIZE ];
  3714.  
  3715.                 outMsg.Init( msgBuf, sizeof( msgBuf ) );
  3716.                 outMsg.WriteByte( GAME_RELIABLE_MESSAGE_GETADMINBANLIST ) ;
  3717.                 networkSystem->ClientSendReliableMessage( outMsg );
  3718.             }
  3719.     
  3720.             mainGui->StateChanged( gameLocal.time );
  3721.  
  3722.             continue;
  3723.         // handler for populating the map list; called both on open and on change gametype so it'll show the right maps
  3724.         } else if ( !idStr::Icmp( cmd, "initServerAdminMapList" ) ) {
  3725. #ifdef _XENON
  3726.             // Xenon should not get here
  3727.             assert( 0 );
  3728. #else
  3729.             // RAVEN BEGIN
  3730. // mekberg: get localized string.
  3731.             const char *mapName = gameLocal.serverInfo.GetString( "si_map" );
  3732.             const idDeclEntityDef *mapDef = static_cast<const idDeclEntityDef *>(declManager->FindType( DECL_MAPDEF, mapName, false ));
  3733.             if ( mapDef ) {
  3734.                 mapName = common->GetLocalizedString( mapDef->dict.GetString( "name", mapName ) );
  3735.             }
  3736.             mainGui->SetStateString( "sa_mapName", mapName );
  3737. // RAVEN END
  3738.             int numMaps = fileSystem->GetNumMaps();
  3739.             const idDict *dict;
  3740.             int numMapsAdded = 0;
  3741.             int i;
  3742.             const char *gameType = "DM";
  3743.  
  3744.             int gameTypeInt = mainGui->GetStateInt( "adminCurrentGametype" );
  3745.  
  3746.             if ( VOTE_GAMETYPE_TOURNEY == gameTypeInt ) {
  3747.                 gameType = "Tourney";
  3748.             } else if ( VOTE_GAMETYPE_TDM == gameTypeInt ) {
  3749.                 gameType = "Team DM";
  3750.             } else if ( VOTE_GAMETYPE_CTF == gameTypeInt ) {
  3751.                 gameType = "CTF";
  3752.             } else if ( VOTE_GAMETYPE_ARENA_CTF == gameTypeInt ) {
  3753.                 gameType = "Arena CTF";
  3754.             } else {
  3755.                 gameType = "DM";
  3756.             }
  3757.             // sanity check
  3758.             if ( NULL == gameType || 0 == *gameType || 0 == idStr::Icmp( gameType, "singleplayer" ) ) {
  3759.                 gameType = "DM";
  3760.             }
  3761.             for ( i = 0; i < numMaps; i++ ) {
  3762.                 dict = fileSystem->GetMapDecl( i );
  3763.                 bool mapOk = false;
  3764.                 //if the gametype is DM, check for any of these types...
  3765.                 if( !(strcmp( gameType, "DM")) || !(strcmp( gameType, "Team DM")) )    {
  3766.                     if ( dict && ( 
  3767.                         dict->GetBool( "DM" ) || 
  3768.                         dict->GetBool( "Team DM" ) || 
  3769.                         dict->GetBool( "CTF" ) || 
  3770.                         dict->GetBool( "Tourney" ) ||
  3771.                         dict->GetBool( "Arena CTF" ))
  3772.                         ) {
  3773.                             mapOk = true;
  3774.                         }
  3775.                         //but if not, match the gametype.
  3776.                 } else if ( dict && dict->GetBool( gameType ) ) { 
  3777.                     mapOk = true;            
  3778.                 }
  3779.                 if ( mapOk ) { 
  3780.                     const char *mapName = dict->GetString( "name" );
  3781.                     if ( '\0' == mapName[ 0 ] ) {
  3782.                         mapName = dict->GetString( "path" );
  3783.                     }
  3784.                     mapName = common->GetLocalizedString( mapName );
  3785.                     mainGui->SetStateString( va( "sa_mapList_item_%d", numMapsAdded), mapName );
  3786.                     mainGui->SetStateInt( va( "sa_mapList_item_%d_id", numMapsAdded), i );
  3787.                     numMapsAdded++;
  3788.                 }
  3789.             }
  3790.             mainGui->DeleteStateVar( va( "sa_mapList_item_%d", numMapsAdded ) );
  3791.             mainGui->SetStateString( "sa_mapList_sel_0", "-1" );
  3792. #endif
  3793.             continue;
  3794.         // handler for updating the current map in the name
  3795.         } else if ( !idStr::Icmp( cmd, "serverAdminUpdateMap" ) ) {
  3796.             int mapSelection = mainGui->GetStateInt( "sa_mapList_sel_0" );
  3797.             if ( -1 == mapSelection ) {
  3798.                 mainGui->SetStateString( "sa_mapName", gameLocal.serverInfo.GetString( "si_map" ) );
  3799.             } else {
  3800.                 int mapNum = mainGui->State().GetInt( va( "sa_mapList_item_%d_id", mapSelection ) );
  3801.                 if ( mapNum >= 0 ) {
  3802.                     const idDict *dict = fileSystem->GetMapDecl( mapNum );
  3803.                     mainGui->SetStateString( "sa_mapName", common->GetLocalizedString( dict->GetString( "name" )) );
  3804.                 }
  3805.             }
  3806.             continue;
  3807.         // handler for initializing the player list on the admin player tab
  3808.         } else if ( !idStr::Icmp( cmd, "initServerAdminPlayer" ) ) {
  3809.             int players;
  3810.             for ( players=0; players<gameLocal.numClients; players++ ) { 
  3811.                 mainGui->SetStateString( va( "sa_playerList_item_%d", players ), kickVoteMapNames[players] );
  3812.             }
  3813.             if ( players < MAX_CLIENTS ) {
  3814.                 mainGui->DeleteStateVar( va( "sa_playerList_item_%d", players ) );
  3815.                 //common->Printf( "DELETING at slot %d\n", players );
  3816.             }
  3817.             mainGui->SetStateString( "sa_playerList_sel_0", "-1" );
  3818.             continue;
  3819.         // handler for actually changing something on the server admin tab
  3820.         } else if ( !idStr::Icmp( cmd, "handleServerAdmin" ) ) {
  3821.             // read in a bunch of data, pack it into the appropriate structure
  3822.             serverAdminData_t data;
  3823.             memset( &data, 0, sizeof( data ) );
  3824.             data.restartMap = 0 != mainGui->GetStateInt( "admin_server_val1_sel" );
  3825.             // map list here
  3826.             int uiMapSelection = mainGui->State().GetInt( "sa_mapList_sel_0" );
  3827.             if (-1 != uiMapSelection ) {
  3828.                 int mapNum = mainGui->State().GetInt( va( "sa_mapList_item_%d_id", uiMapSelection ) );
  3829.                 if ( mapNum >= 0 ) {
  3830.                     const idDict *dict = fileSystem->GetMapDecl( mapNum );
  3831.                     data.mapName = common->GetLocalizedString( dict->GetString( "path" ));
  3832.                 } else { 
  3833.                     data.mapName = gameLocal.serverInfo.GetString( "si_map" );
  3834.                 }        
  3835.             } else { 
  3836.                 data.mapName = gameLocal.serverInfo.GetString( "si_map" );
  3837.             }
  3838.  
  3839.             switch ( mainGui->GetStateInt( "admincurrentGametype" ) ) {
  3840.                 case VOTE_GAMETYPE_DM:
  3841.                     data.gameType = GAME_DM;
  3842.                     break;
  3843.                 case VOTE_GAMETYPE_TOURNEY:
  3844.                     data.gameType = GAME_TOURNEY;
  3845.                     break;
  3846.                 case VOTE_GAMETYPE_TDM:
  3847.                     data.gameType = GAME_TDM;
  3848.                     break;
  3849.                 case VOTE_GAMETYPE_CTF:
  3850.                     data.gameType = GAME_CTF;
  3851.                     break;
  3852.                 case VOTE_GAMETYPE_ARENA_CTF:
  3853.                     data.gameType = GAME_ARENA_CTF;
  3854.                     break;
  3855.             }
  3856.             data.captureLimit = mainGui->GetStateInt( "sa_captureLimit" );
  3857.             data.fragLimit = mainGui->GetStateInt( "sa_fragLimit" );
  3858.             data.tourneyLimit = mainGui->GetStateInt( "sa_tourneylimit" );
  3859.             data.timeLimit = mainGui->GetStateInt( "sa_timeLimit" );
  3860.             data.minPlayers = mainGui->GetStateInt( "sa_minPlayers" );
  3861.             data.autoBalance = 0 != mainGui->GetStateInt( "sa_autobalance" );
  3862.             // make the call to change the server data
  3863.             if ( gameLocal.mpGame.HandleServerAdminCommands( data ) ) {
  3864.                 DisableMenu();
  3865.                 return NULL;
  3866.             }
  3867.             continue;
  3868.         // handler for the kick button on the player tab of the server admin gui
  3869.         } else if ( !idStr::Icmp( cmd, "handleServerAdminKick" ) ) {
  3870.             int uiKickSelection = mainGui->State().GetInt( "sa_playerList_sel_0" );
  3871.             if ( -1 != uiKickSelection ) {
  3872.                 HandleServerAdminKickPlayer( kickVoteMap[ uiKickSelection ] );
  3873.                 DisableMenu();
  3874.                 return NULL;
  3875.             }
  3876.             //common->Printf( "HANDLE SERVER ADMIN KICK!\n" );
  3877.             continue;
  3878.         // handler for the ban button on the player tab of the server admin gui
  3879.         } else if ( !idStr::Icmp( cmd, "handleServerAdminBan" ) ) {
  3880.             //common->Printf( "HANDLE SERVER ADMIN BAN!\n" );
  3881.             int uiBanSelection = mainGui->State().GetInt( "sa_playerList_sel_0" );
  3882.             if ( -1 != uiBanSelection ) {
  3883.                 HandleServerAdminBanPlayer( kickVoteMap[ uiBanSelection ] );
  3884.                 DisableMenu();
  3885.                 mainGui->DeleteStateVar( va( "sa_banList_item_%d", 0 ) );
  3886.                 mainGui->SetStateString( "sa_banList_sel_0", "-1" );
  3887.                 return NULL;
  3888.             }
  3889.             continue;
  3890.         // handler for the remove ban button on the player tab of the server admin gui
  3891.         } else if ( !idStr::Icmp( cmd, "handleServerAdminRemoveBan" ) ) {
  3892.             //common->Printf( "HANDLE SERVER ADMIN REMOVE BAN!\n" );
  3893.             int uiBanSelection = mainGui->State().GetInt( "sa_banList_sel_0" );
  3894.             if ( -1 != uiBanSelection ) {
  3895.                 idStr guid = &mainGui->GetStateString( va( "sa_banList_item_%d", uiBanSelection ) )[ 4 ];
  3896.                 guid = guid.ReplaceChar( '\t', '\0' );
  3897.                 guid = &guid.c_str()[ strlen( guid.c_str() ) + 1 ];
  3898.                 HandleServerAdminRemoveBan( guid.c_str() );
  3899.                 DisableMenu();
  3900.                 return NULL;
  3901.             }
  3902.             continue;
  3903.         // handler for the switch teams button on the player tab of the server admin gui
  3904.         } else if ( !idStr::Icmp( cmd, "handleServerAdminSwitchTeams" ) ) {
  3905.             if ( gameLocal.IsTeamGame() ) {
  3906.                 int uiSwitchSelection = mainGui->State().GetInt( "sa_playerList_sel_0" );
  3907.                 if ( -1 != uiSwitchSelection ) {
  3908.                     HandleServerAdminForceTeamSwitch( kickVoteMap[ uiSwitchSelection ] );
  3909.                     DisableMenu();
  3910.                     return NULL;
  3911.                 }
  3912.             }
  3913.             continue;
  3914.         // handler for the show ban list button of the server admin gui
  3915.         } else if ( !idStr::Icmp( cmd, "populateBanList" ) ) {
  3916.             gameLocal.PopulateBanList( mainGui );
  3917.             continue;
  3918. // RAVEN END
  3919.         } else if (    !idStr::Icmp( cmd, "voteyes" ) ) {
  3920.             CastVote( gameLocal.localClientNum,    true );
  3921.             DisableMenu();
  3922.             return NULL;
  3923.         } else if (    !idStr::Icmp( cmd, "voteno"    ) )    {
  3924.             CastVote( gameLocal.localClientNum,    false );
  3925.             DisableMenu();
  3926.             return NULL;
  3927.         } else if ( !idStr::Icmp( cmd, "bind" ) ) {
  3928.             if ( args.Argc() - icmd >= 2 ) {
  3929.                 idStr key = args.Argv( icmd++ );
  3930.                 idStr bind = args.Argv( icmd++ );
  3931.                 cmdSystem->BufferCommandText( CMD_EXEC_NOW, va( "bindunbindtwo \"%s\" \"%s\"", key.c_str(), bind.c_str() ) );
  3932.                 mainGui->SetKeyBindingNames();
  3933.             }
  3934.             continue;
  3935.         } else if ( !idStr::Icmp( cmd, "clearbind" ) ) {
  3936.             if ( args.Argc() - icmd >= 1 ) {
  3937.                 idStr bind = args.Argv( icmd++ );
  3938.                 cmdSystem->BufferCommandText( CMD_EXEC_NOW, va( "unbind \"%s\"", bind.c_str() ) );
  3939.                 mainGui->SetKeyBindingNames();
  3940.             }
  3941.             continue;
  3942.         } else if (    !idStr::Icmp( cmd, "MAPScan" ) ) {
  3943. #ifdef _XENON
  3944.             // Xenon should not get here
  3945.             assert( 0 );
  3946. #else
  3947.             const char *gametype = gameLocal.serverInfo.GetString( "si_gameType" );
  3948.             if ( gametype == NULL || *gametype == 0 || idStr::Icmp( gametype, "singleplayer" ) == 0 ) {
  3949.                 gametype = "DM";
  3950.             }
  3951.  
  3952.             int i, num;
  3953.             idStr si_map = gameLocal.serverInfo.GetString("si_map");
  3954.             const idDict *dict;
  3955.  
  3956.             mapList->Clear();
  3957.             mapList->SetSelection( -1 );
  3958.             num = fileSystem->GetNumMaps();
  3959.             for ( i = 0; i < num; i++ ) {
  3960.                 dict = fileSystem->GetMapDecl( i );
  3961.                 if ( dict ) {
  3962.                     // any MP gametype supported
  3963.                     bool isMP = false;
  3964.                     int igt = GAME_SP + 1;
  3965.                     while ( si_gameTypeArgs[ igt ] ) {
  3966.                         if ( dict->GetBool( si_gameTypeArgs[ igt ] ) ) {
  3967.                             isMP = true;
  3968.                             break;
  3969.                         }
  3970.                         igt++;
  3971.                     }
  3972.                     if ( isMP ) {
  3973.                         const char *mapName = dict->GetString( "name" );
  3974.                         if ( mapName[0] == '\0' ) {
  3975.                             mapName = dict->GetString( "path" );
  3976.                         }
  3977.                         mapName = common->GetLocalizedString( mapName );
  3978.                         mapList->Add( i, mapName );
  3979.                         if ( !si_map.Icmp( dict->GetString( "path" ) ) ) {
  3980.                             mapList->SetSelection( mapList->Num() - 1 );
  3981.                         }
  3982.                     }
  3983.                 }
  3984.             }
  3985.             // set the current level shot
  3986.             SetMapShot(    );
  3987. #endif
  3988.             return "continue";
  3989.         } else if (    !idStr::Icmp( cmd, "click_maplist" ) ) {
  3990.             SetMapShot(    );
  3991.             return "continue";
  3992.         } else if ( !idStr::Icmp( cmd, "sm_select_player" ) ) {
  3993.             idStr vcmd;
  3994.             if ( args.Argc() - icmd    >= 1 ) {
  3995.                 vcmd = args.Argv( icmd++ );
  3996.             } 
  3997.  
  3998.             int index = atoi( vcmd.c_str() );
  3999.             if( index > 0 && index < MAX_CLIENTS && statSummary && currentMenu == 3 ) {
  4000.                 statManager->UpdateEndGameHud( statSummary, index - 1 );
  4001.             }
  4002.             return "continue";
  4003.         } else if ( !idStr::Icmp( cmd, "update_model" ) ) {
  4004. // RAVEN BEGIN
  4005. // mekberg: moved to function
  4006.             UpdateMPSettingsModel( currentGui );
  4007. // RAVEN END
  4008.             continue;
  4009.         } else if( !idStr::Icmp( cmd, "ingameStats" ) ) {
  4010.             if ( args.Argc() - icmd    >= 1 ) {
  4011.                 idStr igArg = args.Argv( icmd++ );
  4012.                 if( !igArg.Icmp( "init" ) ) {
  4013.                     // setup the player list
  4014.                     statManager->SetupStatWindow( currentGui );
  4015.                 } else if( !igArg.Icmp( "spectator" ) ) {
  4016.                     int currentSel = currentGui->State().GetInt( "spec_names_sel_0", "-1" );
  4017.                     currentGui->SetStateString( "dm_names_sel_0", "-1" );
  4018.                     currentGui->SetStateString( "team_1_names_sel_0", "-1" );
  4019.                     currentGui->SetStateString( "team_2_names_sel_0", "-1" );
  4020.  
  4021.                     statManager->SelectStatWindow( currentSel, TEAM_MAX );
  4022. //RAVEN BEGIN
  4023. //asalmon: Need to refresh stats periodically if the player is looking at stats
  4024.                     currentStatClient = currentSel;
  4025.                     currentStatTeam = TEAM_MAX;
  4026. //RAVEN END
  4027.                 } else if( !igArg.Icmp( "dm" ) ) {
  4028.                     int currentSel = currentGui->State().GetInt( "dm_names_sel_0", "-1" );
  4029.                     currentGui->SetStateString( "spec_names_sel_0", "-1" );
  4030.                     currentGui->SetStateString( "team_1_names_sel_0", "-1" );
  4031.                     currentGui->SetStateString( "team_2_names_sel_0", "-1" );
  4032.  
  4033.                     statManager->SelectStatWindow( currentSel, 0 );
  4034. //RAVEN BEGIN
  4035. //asalmon: Need to refresh stats periodically if the player is looking at stats
  4036.                     currentStatClient = currentSel;
  4037.                     currentStatTeam = 0;
  4038. //RAVEN END
  4039.                 } else if( !igArg.Icmp( "strogg" ) ) {
  4040.                     int currentSel = currentGui->State().GetInt( "team_2_names_sel_0", "-1" );
  4041.                     currentGui->SetStateString( "spec_names_sel_0", "-1" );
  4042.                     currentGui->SetStateString( "team_1_names_sel_0", "-1" );
  4043.                     currentGui->SetStateString( "dm_names_sel_0", "-1" );
  4044.  
  4045.                     statManager->SelectStatWindow( currentSel, TEAM_STROGG );
  4046. //RAVEN BEGIN
  4047. //asalmon: Need to refresh stats periodically if the player is looking at stats
  4048.                     currentStatClient = currentSel;
  4049.                     currentStatTeam = TEAM_STROGG;
  4050. //RAVEN END
  4051.                 } else if( !igArg.Icmp( "marine" ) ) {
  4052.                     int currentSel = currentGui->State().GetInt( "team_1_names_sel_0", "-1" );
  4053.                     currentGui->SetStateString( "spec_names_sel_0", "-1" );
  4054.                     currentGui->SetStateString( "team_2_names_sel_0", "-1" );
  4055.                     currentGui->SetStateString( "dm_names_sel_0", "-1" );
  4056.  
  4057.                     statManager->SelectStatWindow( currentSel, TEAM_MARINE );
  4058. //RAVEN BEGIN
  4059. //asalmon: Need to refresh stats periodically if the player is looking at stats
  4060.                     currentStatClient = currentSel;
  4061.                     currentStatTeam = TEAM_MARINE;
  4062. //RAVEN END
  4063.                 }
  4064.             }
  4065.             continue;
  4066.         } else if( !idStr::Icmp( cmd, "mainMenu" ) ) {
  4067.             DisableMenu();
  4068.             static idStr menuCmd;
  4069.             menuCmd.Clear();                        // cnicholson: In order to avoid repeated eventnames from screwing up the menu system, clear it.
  4070.             menuCmd.Append( "main" );
  4071.             const char* eventName = "";
  4072.             if( args.Argc() - icmd >= 1 ) {
  4073.                 eventName = args.Argv( icmd++ );
  4074.                 menuCmd.Append( " " );
  4075.                 menuCmd.Append( eventName );
  4076.             }
  4077.             return menuCmd.c_str();
  4078.         } 
  4079. // RAVEN BEGIN
  4080. // cnicholson: The menu calls this prior to entering multiplayer settings. What it does is to check the current crosshair, and compare it
  4081. //               agasint the list of crosshairs in player.def under the player_marine_mp section. If it finds a match, it assigns the 
  4082. //               crosshair to the next one in the list. If there isn't one, or if its the end of the list, the first found crosshair is used.
  4083.         else if ( !idStr::Icmp( cmd, "chooseCrosshair" ) ) {
  4084. #ifndef _XENON
  4085.  
  4086. #ifndef _XENON
  4087.             const idDeclEntityDef *def = static_cast<const idDeclEntityDef*>( declManager->FindType( DECL_ENTITYDEF, "player_marine_mp", false, true ) );
  4088. #else
  4089.             bool insideLevelLoad = declManager->GetInsideLoad();
  4090.             if ( !insideLevelLoad ) {
  4091.                 declManager->SetInsideLoad( true );
  4092.             }
  4093.             const idDeclEntityDef *def = static_cast<const idDeclEntityDef*>( declManager->FindType( DECL_ENTITYDEF, "player_marine_mp_ui", false, false ) );
  4094.             declManager->SetInsideLoad( insideLevelLoad );
  4095. #endif
  4096.  
  4097.             idStr currentCrosshair = cvarSystem->GetCVarString("g_crosshairCustomFile");
  4098.  
  4099.             const idKeyValue* kv = def->dict.MatchPrefix("mtr_crosshair", NULL);
  4100.     
  4101.             while ( kv ) {
  4102.                 if ( kv->GetValue() == currentCrosshair.c_str() ) {
  4103.                     kv = def->dict.MatchPrefix("mtr_crosshair", kv );
  4104.                     break;
  4105.                 }
  4106.                 kv = def->dict.MatchPrefix("mtr_crosshair", kv );
  4107.             }
  4108.  
  4109.             if ( !kv ){
  4110.                 kv = def->dict.MatchPrefix("mtr_crosshair", NULL );
  4111.             }
  4112.  
  4113.             idStr newCrosshair(kv->GetValue());
  4114.  
  4115.             mainGui->SetStateString ( "crossImage", newCrosshair.c_str());
  4116.             cvarSystem->SetCVarString("g_crosshairCustomFile", newCrosshair.c_str());
  4117. #endif        
  4118.         }
  4119. // RAVEN END
  4120.         else if( !idStr::Icmp( cmd, "friend" ) ) {
  4121.             // we friend/unfriend from the stat window, so use that to get selection info
  4122.             int selectionTeam = -1;
  4123.             int selectionIndex = -1;
  4124.  
  4125.             // get the selected client num, as well as the selectionIndex/Team from the stat window
  4126.             int client = statManager->GetSelectedClientNum( &selectionIndex, &selectionTeam );
  4127.  
  4128.             if( ( client < 0 || client >= MAX_CLIENTS ) || !gameLocal.GetLocalPlayer() ) {
  4129.                 continue;
  4130.             }
  4131.  
  4132.             // un-mark this client as a friend
  4133.             if( gameLocal.GetLocalPlayer()->IsFriend( client ) ) {
  4134.                 networkSystem->RemoveFriend( client );
  4135.             } else {
  4136.                 networkSystem->AddFriend( client );
  4137.             }
  4138.             
  4139.             // refresh with new info
  4140.             statManager->SetupStatWindow( currentGui );
  4141.             statManager->SelectStatWindow( selectionIndex, selectionTeam );
  4142.             continue;
  4143.         } else if( !idStr::Icmp( cmd, "mute" ) ) {
  4144.             // we mute/unmute from the stat window, so use that to get selection info
  4145.             int selectionTeam = -1;
  4146.             int selectionIndex = -1;
  4147.  
  4148.             // get the selected client num, as well as the selectionIndex/Team from the stat window
  4149.             int client = statManager->GetSelectedClientNum( &selectionIndex, &selectionTeam );
  4150.  
  4151.             
  4152.             ClientVoiceMute( client, !gameLocal.GetLocalPlayer()->IsPlayerMuted( client ) );
  4153.  
  4154.             // refresh with new info
  4155.             statManager->SetupStatWindow( currentGui );
  4156.             statManager->SelectStatWindow( selectionIndex, selectionTeam );
  4157.  
  4158.             continue;
  4159.         }
  4160. //RAVEN BEGIN
  4161. //asalmon: pass through some commands that need to be handled in the main menu handle function
  4162.         else if ((strstr( cmd, "FilterMPMapList" ) == cmd) 
  4163.             || (strstr( cmd, "AddMapLive" ) == cmd) 
  4164.             || (strstr( cmd, "RemoveMapLive" ) == cmd) 
  4165.         ) {
  4166.             static idStr menuCmd;
  4167.             menuCmd.Clear();                        
  4168.             menuCmd.Append( cmd );
  4169.             return menuCmd.c_str();
  4170.         } else if( !idStr::Icmp( cmd, "toggleTourney" ) ) {
  4171.             if( gameLocal.gameType == GAME_TOURNEY ) {
  4172.                 ToggleSpectate();
  4173.                 DisableMenu( );
  4174.                 return NULL;
  4175.             }
  4176.             continue;
  4177.         }
  4178. //RAVEN END
  4179.         common->Printf(    "idMultiplayerGame::HandleGuiCommands: '%s'    unknown\n",    cmd    );
  4180.  
  4181.     }
  4182.     return "continue";
  4183. }
  4184.  
  4185. /*
  4186. ================
  4187. idMultiplayerGame::Draw
  4188. ================
  4189. */
  4190. bool idMultiplayerGame::Draw( int clientNum ) {
  4191.     idPlayer *player, *viewPlayer;
  4192.  
  4193.     player = viewPlayer = static_cast<idPlayer *>( gameLocal.entities[ clientNum ] );
  4194.  
  4195.     if ( player == NULL ) {
  4196.         return false;
  4197.     }
  4198.  
  4199.     if ( player->spectating ) {
  4200.         viewPlayer = static_cast<idPlayer *>( gameLocal.entities[ player->spectator ] );
  4201.         if ( viewPlayer == NULL || !viewPlayer->GetRenderView() ) {
  4202.             return false;
  4203.         }
  4204.     }
  4205.  
  4206.     if(gameLocal.IsFlagGameType())
  4207.     {
  4208.         renderView_t* view = viewPlayer->GetRenderView();
  4209.         if ( view ) {
  4210.             view->shaderParms[ 1 ] = ( ((rvCTFGameState*)gameLocal.mpGame.GetGameState())->GetFlagState( TEAM_MARINE ) != FS_AT_BASE );
  4211.             view->shaderParms[ 2 ] = ( ((rvCTFGameState*)gameLocal.mpGame.GetGameState())->GetFlagState( TEAM_STROGG ) != FS_AT_BASE );
  4212.  
  4213.             // RAVEN BEGIN
  4214.             // mwhitlock: Xenon texture streaming.
  4215.             #if defined(_XENON)
  4216.                 view->streamingPrecache=player->streamingPrecache;
  4217.             #endif
  4218.             // RAVEN END
  4219.         }
  4220.     }
  4221.     // use the hud of the local player
  4222.     viewPlayer->playerView.RenderPlayerView( player->hud );
  4223.  
  4224. // RAVEN BEGIN
  4225. // mwhitlock: Xenon texture streaming
  4226. #if defined(_XENON)
  4227.     assert(gameRenderWorld!=0);
  4228.     if(gameRenderWorld!=0)
  4229.     {
  4230.         gameRenderWorld->StreamAreaTextures(!player->streamingPrecache);
  4231.     }
  4232. #endif
  4233. // RAVEN END
  4234.  
  4235. // RAVEN BEGIN
  4236. // mwhitlock: Xenon texture streaming.
  4237. #if defined(_XENON)
  4238.     player->streamingPrecache=false;
  4239. #endif
  4240. // RAVEN END
  4241.  
  4242.     // allow force scoreboard to overwrite a fullscreen menu
  4243.     if ( currentMenu ) { 
  4244. #if 0
  4245.         // uncomment this if you want to track when players are in a menu
  4246.         if ( !bCurrentMenuMsg ) {
  4247.             idBitMsg    outMsg;
  4248.             byte        msgBuf[ 128 ];
  4249.  
  4250.             outMsg.Init( msgBuf, sizeof( msgBuf ) );
  4251.             outMsg.WriteByte( GAME_RELIABLE_MESSAGE_MENU );
  4252.             outMsg.WriteBits( 1, 1 );
  4253.             networkSystem->ClientSendReliableMessage( outMsg );
  4254.  
  4255.             bCurrentMenuMsg = true;
  4256.         }
  4257. #endif
  4258.         if ( player->wantSpectate ) {
  4259.             mainGui->SetStateString( "spectext", common->GetLocalizedString( "#str_104249" ) );
  4260.         } else {
  4261.             mainGui->SetStateString( "spectext", common->GetLocalizedString( "#str_104250" ) );
  4262.         }
  4263.         // if we died, isChatting is cleared, so re-set our chatting cvar
  4264.         if( gameLocal.GetLocalPlayer() && !gameLocal.GetLocalPlayer()->isChatting && !gameLocal.GetLocalPlayer()->pfl.dead ) {
  4265.             cvarSystem->SetCVarBool( "ui_chat", true );
  4266.             cvarSystem->SetModifiedFlags( CVAR_USERINFO ); // force update
  4267.         }
  4268.         if ( currentMenu == 1 ) {
  4269.             UpdateMainGui();
  4270.             mainGui->Redraw( gameLocal.time );
  4271.         } else if( currentMenu == 2 ) {
  4272.             msgmodeGui->Redraw( gameLocal.time );
  4273.         } else if( currentMenu == 3 ) {
  4274.             DrawStatSummary();
  4275.         }
  4276.     } else {
  4277. #if 0
  4278.         // uncomment this if you want to track when players are in a menu
  4279.         if ( bCurrentMenuMsg ) {
  4280.             idBitMsg    outMsg;
  4281.             byte        msgBuf[ 128 ];
  4282.  
  4283.             outMsg.Init( msgBuf, sizeof( msgBuf ) );
  4284.             outMsg.WriteByte( GAME_RELIABLE_MESSAGE_MENU );
  4285.             outMsg.WriteBits( 0, 1 );
  4286.             networkSystem->ClientSendReliableMessage( outMsg );
  4287.  
  4288.             bCurrentMenuMsg = false;
  4289.         }
  4290. #endif
  4291.         DrawScoreBoard( player );
  4292.     }
  4293.  
  4294. // RAVEN BEGIN
  4295. // bdube: debugging HUD
  4296.     gameDebug.DrawHud ( );
  4297. // RAVEN END
  4298.     return true;
  4299. }
  4300.  
  4301. /*
  4302. ================
  4303. idMultiplayerGame::UpdateHud
  4304. ================
  4305. */
  4306. void idMultiplayerGame::UpdateHud( idUserInterface* _mphud ) {
  4307.     if ( !_mphud ) {
  4308.         return;
  4309.     }
  4310.  
  4311.     assert( gameLocal.GetLocalPlayer() );
  4312.  
  4313.  
  4314.  
  4315. //RAVEN BEGIN
  4316. //asalmon: Turn on/off the lag icon so that clients know that they are losing connection
  4317.     if (  networkSystem->ClientGetTimeSinceLastPacket() > 0 && ( networkSystem->ClientGetTimeSinceLastPacket() > cvarSystem->GetCVarInteger("net_clientServerTimeout")*500 ) ) {
  4318.         _mphud->SetStateBool("IsLagged", true);
  4319.     }
  4320.     else{
  4321.         _mphud->SetStateBool("IsLagged", false);
  4322.     }
  4323. //RAVEN END
  4324.  
  4325.     _mphud->SetStateInt( "marine_score", teamScore[ TEAM_MARINE ] );
  4326.     _mphud->SetStateInt( "strogg_score", teamScore[ TEAM_STROGG ] );
  4327.  
  4328.     int timeLimit = gameLocal.serverInfo.GetInt( "si_timeLimit" );
  4329.     
  4330.     // Always show GameTime() for WARMUP and COUNTDOWN.
  4331.     mpGameState_t state = gameState->GetMPGameState();
  4332.     _mphud->SetStateString( "timeleft", GameTime() );
  4333.  
  4334.     bool inNonTimedState = (state == SUDDENDEATH) || (state == WARMUP) || (state == GAMEREVIEW);
  4335.     bool inCountdownState = (state == COUNTDOWN);
  4336.     if( gameLocal.gameType == GAME_TOURNEY && gameLocal.GetLocalPlayer() ) {
  4337.         inNonTimedState |= (((rvTourneyGameState*)gameState)->GetArena( gameLocal.GetLocalPlayer()->GetArena() ).GetState() == AS_SUDDEN_DEATH);
  4338.         inCountdownState |= (((rvTourneyGameState*)gameState)->GetArena( gameLocal.GetLocalPlayer()->GetArena() ).GetState() == AS_WARMUP);
  4339.     }
  4340.     _mphud->SetStateBool( "infinity", ( !timeLimit && !inCountdownState ) || inNonTimedState );
  4341.  
  4342.     if( gameLocal.gameType == GAME_DM ) {
  4343.         if( rankedPlayers.Num() ) {
  4344.             _mphud->SetStateString( "player1_name", rankedPlayers[ 0 ].First()->GetUserInfo()->GetString( "ui_name" ) );
  4345.             _mphud->SetStateString( "player1_score", va( "%d", GetScore( rankedPlayers[ 0 ].First() ) ) );
  4346.             _mphud->SetStateString( "player1_rank", "1." );
  4347.  
  4348.             // if we're in the lead or spectating, show the person in 2nd
  4349.             if( ( (rankedPlayers[ 0 ].First() == gameLocal.GetLocalPlayer()) || (gameLocal.GetLocalPlayer()->spectating) ) && rankedPlayers.Num() > 1 ) {
  4350.                 _mphud->SetStateString( "player2_name", rankedPlayers[ 1 ].First()->GetUserInfo()->GetString( "ui_name" ) );
  4351.                 _mphud->SetStateString( "player2_score", va( "%d", GetScore( rankedPlayers[ 1 ].First() ) ) );
  4352.                 _mphud->SetStateString( "player2_rank", va( "%d.", rankedPlayers[ 1 ].First()->GetRank() + 1 ) );
  4353.             } else if( rankedPlayers[ 0 ].First() != gameLocal.GetLocalPlayer() && !gameLocal.GetLocalPlayer()->spectating ) {
  4354.                 // otherwise, show our score
  4355.                 _mphud->SetStateString( "player2_name", gameLocal.GetLocalPlayer()->GetUserInfo()->GetString( "ui_name" ) );
  4356.                 _mphud->SetStateString( "player2_score", va( "%d", GetScore( gameLocal.GetLocalPlayer() ) ) );
  4357.                 _mphud->SetStateString( "player2_rank", va( "%d.", gameLocal.GetLocalPlayer()->GetRank() + 1 ) );
  4358.             } else {
  4359.                 // no person to place in 2nd
  4360.                 _mphud->SetStateString( "player2_name", "" );
  4361.                 _mphud->SetStateString( "player2_score", "" );
  4362.                 _mphud->SetStateString( "player2_rank", "" );
  4363.             }
  4364.         } else {
  4365.             _mphud->SetStateString( "player1_name", "" );
  4366.             _mphud->SetStateString( "player1_score", "" );
  4367.             _mphud->SetStateString( "player1_rank", "" );
  4368.  
  4369.             _mphud->SetStateString( "player2_name", "" );
  4370.             _mphud->SetStateString( "player2_score", "" );
  4371.             _mphud->SetStateString( "player2_rank", "" );
  4372.         }
  4373.     } 
  4374.  
  4375.     if( gameLocal.gameType == GAME_TOURNEY && gameLocal.GetLocalPlayer()->GetArena() == MAX_ARENAS ) {
  4376.         int numWaitingArenaPlayers = 0;
  4377.         for( int i = 0; i < rankedPlayers.Num(); i++ ) {
  4378.             if( rankedPlayers[ i ].First() && rankedPlayers[ i ].First()->GetArena() == MAX_ARENAS ) {
  4379.                 _mphud->SetStateString( va( "waitRoom_item_%d", numWaitingArenaPlayers++ ), rankedPlayers[ i ].First()->GetUserInfo()->GetString( "ui_name" ) );
  4380.             }
  4381.         }
  4382.         _mphud->SetStateString( va( "waitRoom_item_%d", numWaitingArenaPlayers ), "" );
  4383.         _mphud->SetStateBool( "waitroom", true );
  4384.         _mphud->SetStateInt( "num_waitroom_players", numWaitingArenaPlayers );
  4385.     } else {
  4386.         _mphud->SetStateBool( "waitroom", false );
  4387.     }
  4388.  
  4389.     idStr spectateText0;
  4390.     idStr spectateText1;
  4391.     idStr spectateText2;
  4392.  
  4393.     if( gameLocal.gameType == GAME_TOURNEY ) {
  4394.         // line 1 - why we aren't playing
  4395.         if( gameLocal.GetLocalPlayer()->wantSpectate ) {
  4396.             if( gameLocal.GetLocalPlayer()->spectator != gameLocal.GetLocalPlayer()->entityNumber ) {
  4397.                 spectateText0 = va( common->GetLocalizedString( "#str_107672" ), gameLocal.GetClientByNum( gameLocal.GetLocalPlayer()->spectator )->GetUserInfo()->GetString( "ui_name" ) );
  4398.             } else if( gameLocal.GetLocalPlayer()->spectating ) {
  4399.                 spectateText0 = common->GetLocalizedString( "#str_107673" );
  4400.             }
  4401.         } else {
  4402.             rvTourneyArena& currentArena = ((rvTourneyGameState*)gameState)->GetArena( gameLocal.GetLocalPlayer()->GetArena() );
  4403.             if( gameState->GetMPGameState() == WARMUP ) {
  4404.                 // grab the reason we aren't playing yet
  4405.                 AllPlayersReady( &spectateText0 );
  4406.             } else if( gameState->GetMPGameState() == COUNTDOWN ) {
  4407.                 spectateText0 = va( common->GetLocalizedString( "#str_107671" ), Max( ((gameState->GetNextMPGameStateTime() - gameLocal.time) / 1000) + 1, 0 ) );
  4408.             } else if( gameState->GetMPGameState() != GAMEREVIEW && gameLocal.GetLocalPlayer()->GetTourneyStatus() == PTS_ELIMINATED ) { 
  4409.                 spectateText0 = common->GetLocalizedString( "#str_107687" );
  4410.             } else if( gameState->GetMPGameState() != GAMEREVIEW && gameLocal.GetLocalPlayer()->GetTourneyStatus() == PTS_ADVANCED ) {
  4411.                 spectateText0 = common->GetLocalizedString( "#str_107688" );
  4412.             } else if( ((rvTourneyGameState*)gameState)->GetByePlayer() == gameLocal.GetLocalPlayer() ) {
  4413.                 spectateText0 = common->GetLocalizedString( "#str_107709" );
  4414.             } else if( currentArena.IsPlaying( gameLocal.GetLocalPlayer() ) ) {
  4415.                 spectateText0 = va( "%s %d; %s", common->GetLocalizedString( "#str_107716" ), gameLocal.GetLocalPlayer()->GetArena() + 1, ((rvTourneyGameState*)gameState)->GetRoundDescription() );
  4416.             } else if( gameLocal.GetLocalPlayer()->spectating ) {
  4417.                 // this should only happen if the player was spectating at start of round, but then decides
  4418.                 // to join the tourney
  4419.                 spectateText0 = common->GetLocalizedString( "#str_107684" );
  4420.             }
  4421.         }
  4422.         
  4423.         // line 2 - will or wont be seeded, how to cycle
  4424.         // line 3 - how to enter waiting room
  4425.         if( gameState->GetMPGameState() == WARMUP || gameState->GetMPGameState() == COUNTDOWN ) {
  4426.             if( gameLocal.GetLocalPlayer()->wantSpectate ) {
  4427.                 spectateText1 = common->GetLocalizedString( "#str_107685" );
  4428.                 spectateText2 = common->GetLocalizedString( "#str_107695" );
  4429.             } else {
  4430.                 spectateText1 = common->GetLocalizedString( "#str_107684" );
  4431.                 spectateText2 = common->GetLocalizedString( "#str_107694" );
  4432.             }
  4433.         } else if( gameLocal.GetLocalPlayer()->spectating ) {
  4434.             if( gameLocal.GetLocalPlayer()->GetArena() == MAX_ARENAS ) {
  4435.                 spectateText1 = common->GetLocalizedString( "#str_107686" );
  4436.             } else {
  4437.                 spectateText1 = va( common->GetLocalizedString( "#str_107670" ), common->KeysFromBinding( "_impulse14" ), common->KeysFromBinding( "_impulse15" ) );
  4438.             }
  4439.         }
  4440.     } else {
  4441.         // non-tourney spectate text
  4442.         if( gameLocal.GetLocalPlayer()->spectating ) {
  4443.             if( gameLocal.GetLocalPlayer()->spectator != gameLocal.GetLocalPlayer()->entityNumber ) {
  4444.                 spectateText0 = va( common->GetLocalizedString( "#str_107672" ), gameLocal.GetClientByNum( gameLocal.GetLocalPlayer()->spectator )->GetUserInfo()->GetString( "ui_name" ) );
  4445.             } else if( gameLocal.GetLocalPlayer()->spectating ) {
  4446.                 spectateText0 = common->GetLocalizedString( "#str_107673" );
  4447.             }
  4448.  
  4449.             // spectating instructions
  4450.             if( gameLocal.GetLocalPlayer()->spectator != gameLocal.GetLocalPlayer()->entityNumber ) {
  4451.                 //cycle & exit follow
  4452.                 spectateText1 = va( common->GetLocalizedString( "#str_107698" ), common->KeysFromBinding( "_attack" ), common->KeysFromBinding( "_moveup" )  );
  4453.             } else {
  4454.                 //start follow
  4455.                 spectateText1 = va( common->GetLocalizedString( "#str_108024" ), common->KeysFromBinding( "_attack" )  );
  4456.             }
  4457.             
  4458.         }
  4459.  
  4460.         if( gameState->GetMPGameState() == WARMUP ) {
  4461.             AllPlayersReady( &spectateText1 );
  4462.         } else if( gameState->GetMPGameState() == COUNTDOWN ) {
  4463.             spectateText1 = va( common->GetLocalizedString( "#str_107671" ), Max( ((gameState->GetNextMPGameStateTime() - gameLocal.time) / 1000) + 1, 0 ) );
  4464.         }
  4465.     }
  4466.  
  4467.     _mphud->SetStateString( "spectatetext0", spectateText0 );
  4468.     _mphud->SetStateString( "spectatetext1", spectateText1 );
  4469.     _mphud->SetStateString( "spectatetext2", spectateText2 );
  4470.  
  4471.     if( gameLocal.gameType == GAME_TOURNEY ) {
  4472.         gameLocal.mpGame.tourneyGUI.UpdateScores();
  4473.     }
  4474.  
  4475.     _mphud->StateChanged( gameLocal.time );
  4476.  
  4477.     statManager->UpdateInGameHud( _mphud, !!( gameLocal.GetLocalPlayer()->usercmd.buttons & BUTTON_INGAMESTATS ) );
  4478.  
  4479.     //update awards
  4480.     if(gameLocal.isClient || gameLocal.isListenServer)
  4481.     {
  4482.         statManager->CheckAwardQueue();
  4483.     }
  4484. }
  4485.  
  4486. /*
  4487. ================
  4488. idMultiplayerGame::DrawScoreBoard
  4489. ================
  4490. */
  4491. void idMultiplayerGame::DrawScoreBoard( idPlayer *player ) {
  4492.  
  4493. //asalmon: I think Nathan added this originally but I fixed it so the player HUD is restored when
  4494. //the gui is dismissed
  4495. #ifdef _XENON
  4496.     if ( player->scoreBoardOpen ) {
  4497.         scoreBoard->Activate( true, gameLocal.time );    
  4498.         session->SetGUI(scoreBoard, NULL);
  4499.         player->scoreBoardOpen = false;
  4500.         player->disableHud = true;
  4501.     }
  4502.     
  4503.     if ( session->GetActiveGUI() == scoreBoard ) {
  4504.         UpdateScoreboard( scoreBoard );
  4505.     }
  4506.     else
  4507.     {
  4508.         scoreBoard->Activate( false, gameLocal.time );
  4509.         playerState[ player->entityNumber ].scoreBoardUp = false;
  4510.         player->disableHud = false;
  4511.     }
  4512.         
  4513. #else
  4514.  
  4515.     if ( player->scoreBoardOpen ) {
  4516.         if ( !playerState[ player->entityNumber ].scoreBoardUp ) {
  4517.             scoreBoard->Activate( true, gameLocal.time );
  4518.             playerState[ player->entityNumber ].scoreBoardUp = true;
  4519.             player->disableHud = true;
  4520.         }
  4521.         if( gameLocal.gameType == GAME_TOURNEY ) {
  4522.             ((rvTourneyGameState*)gameState)->UpdateTourneyBrackets();
  4523.         }
  4524.         UpdateScoreboard( scoreBoard );
  4525.     } else {
  4526.         if ( playerState[ player->entityNumber ].scoreBoardUp ) {
  4527.             scoreBoard->Activate( false, gameLocal.time );
  4528.             playerState[ player->entityNumber ].scoreBoardUp = false;
  4529.             player->disableHud = false;
  4530.         }
  4531.     }
  4532. #endif
  4533. }
  4534.  
  4535. /*
  4536. ===============
  4537. idMultiplayerGame::AddChatLine
  4538. ===============
  4539. */
  4540. void idMultiplayerGame::AddChatLine( const char *fmt, ... ) {
  4541.     idStr temp;
  4542.     va_list argptr;
  4543.  
  4544. // mekberg: chat changes.
  4545.     wrapInfo_t wrapInfo;
  4546.     idStr wrap1;
  4547.     idStr wrap2;
  4548.     
  4549.     va_start( argptr, fmt );
  4550.     vsprintf( temp, fmt, argptr );
  4551.     va_end( argptr );
  4552.     
  4553.     temp.StripTrailingOnce("\n");
  4554.     
  4555.     // this isn't a good way to color informational lines....
  4556.     if( temp.Find( ":" ) > 0 && temp.Find( ":" ) < temp.Length() - 1 ) {
  4557.         gameLocal.Printf( "%s^0^2%s\n", temp.Left( temp.Find( ":" ) + 1 ).c_str(), temp.Right( temp.Length() - temp.Find( ":" ) - 1).c_str() );
  4558.     } else {
  4559.         gameLocal.Printf( "%s\n", temp.c_str() );
  4560.     }
  4561.     
  4562.     // bdube: new chat interraction with hud
  4563.     if ( gameLocal.GetLocalPlayer() != NULL && gameLocal.GetLocalPlayer()->mphud ) {
  4564.             wrap1 = temp;
  4565.             wrap2 = temp;
  4566.         do {
  4567.             memset( &wrapInfo, -1, sizeof ( wrapInfo_t ) );
  4568.             gameLocal.GetLocalPlayer( )->mphud->GetMaxTextIndex( "history1", wrap1.c_str( ), wrapInfo );
  4569.  
  4570.             // If we have a whitespace near the end. Otherwise the user could enter a giant word.
  4571.             if ( wrapInfo.lastWhitespace != -1 &&  float( wrapInfo.lastWhitespace ) / float( wrapInfo.maxIndex ) > .75 ) {
  4572.                 wrap2 = wrap1.Left( wrapInfo.lastWhitespace++ );
  4573.  
  4574.             // Just text wrap, no word wrap.
  4575.             } else if ( wrapInfo.maxIndex != -1 ) {                    
  4576.                 wrap2 = wrap1.Left( wrapInfo.maxIndex );
  4577.  
  4578.             // We fit in less than a line.
  4579.             } else {
  4580.                 wrap2 = wrap1;
  4581.             }
  4582.  
  4583.             // Recalc the base string.
  4584.             wrap1 = wrap1.Right( wrap1.Length( ) - wrap2.Length( ) );
  4585.  
  4586.             // Push to gui.
  4587.             gameLocal.GetLocalPlayer( )->mphud->SetStateString( "chattext", wrap2.c_str( ) );
  4588.             gameLocal.GetLocalPlayer( )->mphud->HandleNamedEvent( "addchatline" );
  4589.         } while ( wrapInfo.maxIndex != -1 );
  4590.     }
  4591.  
  4592.     if( chatHistory.Length() + temp.Length() > CHAT_HISTORY_SIZE ) {
  4593.         int removeLength = chatHistory.Find( '\n' );
  4594.         if( removeLength == -1 ) {
  4595.             // nuke the whole string
  4596.             chatHistory.Empty();
  4597.         } else {
  4598.             while( (chatHistory.Length() - removeLength) + temp.Length() > CHAT_HISTORY_SIZE ) {
  4599.                 removeLength = chatHistory.Find( '\n', removeLength + 1 );
  4600.                  if( removeLength == -1 ) {
  4601.                     chatHistory.Empty();
  4602.                     break;
  4603.                 }
  4604.             }
  4605.         }
  4606.         chatHistory = chatHistory.Right( chatHistory.Length() - removeLength );
  4607.     }
  4608.  
  4609.     chatHistory.Append( temp );
  4610.     chatHistory.Append( '\n' );
  4611.  
  4612.     if( mainGui ) {
  4613.         mainGui->SetStateString( "chat", chatHistory.c_str() );
  4614.     }
  4615.     if( statSummary ) {
  4616.         statSummary->SetStateString( "chat", chatHistory.c_str() );
  4617.     }
  4618.     
  4619.     // play chat sound
  4620.     char* chatSound = "snd_chat";
  4621.     if( gameLocal.GetLocalPlayer() ) {
  4622.         // not a great way to detect teams
  4623.         if( gameLocal.IsTeamGame() ) {
  4624.             int i = temp.Find( gameLocal.GetLocalPlayer()->team ? "Strogg" : "Marine", false );
  4625.             int firstColon = temp.Find( ":" );
  4626.             if( firstColon >= 0 && i < firstColon && i >= 1 && temp[ i - 6 ] == '(' ) {
  4627.                 chatSound = "snd_teamchat";
  4628.             }
  4629.         }
  4630.  
  4631.         gameLocal.GetLocalPlayer()->StartSound( chatSound, SND_CHANNEL_ANY, 0, false, NULL );
  4632.     }
  4633.     
  4634. }
  4635.  
  4636. void idMultiplayerGame::DrawStatSummary( void ) {
  4637.     if ( !statSummary->GetStateFloat( "ready" ) ) {
  4638.         statSummary->SetStateFloat( "ready", 1 );
  4639.         statSummary->HandleNamedEvent( "chatFocus" );
  4640.         statSummary->StateChanged( gameLocal.time );
  4641.     }
  4642.     statSummary->Redraw( gameLocal.time );
  4643. }
  4644.  
  4645. void idMultiplayerGame::ShowStatSummary( void ) {
  4646.     assert( gameLocal.GetLocalPlayer() );
  4647.     DisableMenu( );
  4648.     nextMenu = 3;
  4649.     gameLocal.sessionCommand = "game_startmenu";
  4650.     gameLocal.GetLocalPlayer()->GUIMainNotice( "" );
  4651.     gameLocal.GetLocalPlayer()->GUIFragNotice( "" );
  4652. }
  4653.  
  4654. /*
  4655. ================
  4656. idMultiplayerGame::WriteToSnapshot
  4657. ================
  4658. */
  4659. void idMultiplayerGame::WriteToSnapshot( idBitMsgDelta &msg ) const {
  4660.     int         i;
  4661.      int         value;
  4662.     byte        ingame[ MAX_CLIENTS / 8 ];
  4663.     idEntity*    ent;
  4664.  
  4665.     assert( MAX_CLIENTS % 8 == 0 );
  4666.  
  4667. // RAVEN BEGIN
  4668. // ddynerman: CTF scoring
  4669. // FIXME - not in the snapshot
  4670.     for( i = 0; i < TEAM_MAX; i++ ) {
  4671.         msg.WriteShort( teamScore[i] );
  4672.     }
  4673. // RAVEN END
  4674.  
  4675.     // write ingame bits first, then we only sync down for ingame clients
  4676.     // do a single write, this doesn't change often it's best to deltify in a single shot
  4677.     for ( i = 0; i < MAX_CLIENTS; i++ ) {
  4678.         if ( playerState[i].ingame ) {
  4679.             ingame[ i / 8 ] |= 1 << ( i % 8 );
  4680.         } else {
  4681.             ingame[ i / 8 ] &= ~( 1 << ( i % 8 ) );
  4682.         }
  4683.     }
  4684.     msg.WriteData( ingame, MAX_CLIENTS / 8 );
  4685.  
  4686.     // those rarely change as well and will deltify away nicely
  4687.     for ( i = 0; i < MAX_CLIENTS; i++ ) {
  4688.         if ( playerState[i].ingame ) {
  4689.             ent = gameLocal.entities[ i ];
  4690.             // clamp all values to min/max possible value that we can send over
  4691.             value = idMath::ClampInt( MP_PLAYER_MINFRAGS, MP_PLAYER_MAXFRAGS, playerState[i].fragCount );
  4692.             msg.WriteBits( value, ASYNC_PLAYER_FRAG_BITS );
  4693.             value = idMath::ClampInt( MP_PLAYER_MINFRAGS, MP_PLAYER_MAXFRAGS, playerState[i].teamFragCount );
  4694.             msg.WriteBits( value, ASYNC_PLAYER_FRAG_BITS );
  4695.             value = idMath::ClampInt( 0, MP_PLAYER_MAXWINS, playerState[i].wins );
  4696.             msg.WriteBits( value, ASYNC_PLAYER_WINS_BITS );
  4697.             // only transmit instance info in tourney
  4698.             if( gameLocal.gameType == GAME_TOURNEY ) {
  4699.                 if( !ent ) {
  4700.                     msg.WriteBits( 0, 1 );
  4701.                 } else {
  4702.                     msg.WriteBits( 1, 1 );
  4703.                     value = idMath::ClampInt( 0, MAX_INSTANCES, ent->GetInstance() );
  4704.                     msg.WriteBits( value, ASYNC_PLAYER_INSTANCE_BITS );
  4705.                     msg.WriteBits( ((idPlayer*)ent)->GetTourneyStatus(), ASYNC_PLAYER_TOURNEY_STATUS_BITS );
  4706.                 }
  4707.             }
  4708.         }
  4709.     }
  4710.  
  4711.     // those change all the time, keep them in a single pack
  4712.     for ( i = 0; i < MAX_CLIENTS; i++ ) {
  4713.         if ( playerState[i].ingame ) {
  4714.             value = idMath::ClampInt( 0, MP_PLAYER_MAXPING, playerState[i].ping );
  4715.             msg.WriteBits( value, ASYNC_PLAYER_PING_BITS );
  4716.         }
  4717.     }
  4718. }
  4719.  
  4720. /*
  4721. ================
  4722. idMultiplayerGame::ReadFromSnapshot
  4723. ================
  4724. */
  4725. void idMultiplayerGame::ReadFromSnapshot( const idBitMsgDelta &msg ) {
  4726.     int         i, newInstance;
  4727.     byte        ingame[ MAX_CLIENTS / 8 ];
  4728.     idEntity*    ent;
  4729.  
  4730.     // CTF/TDM scoring
  4731.     for( i = 0; i < TEAM_MAX; i++ ) {
  4732.         teamScore[ i ] = msg.ReadShort( );
  4733.     }
  4734.  
  4735.     msg.ReadData( ingame, MAX_CLIENTS / 8 );
  4736.     for ( i = 0; i < MAX_CLIENTS; i++ ) {
  4737.         if ( ingame[ i / 8 ] & ( 1 << ( i % 8 ) ) ) {
  4738.             playerState[i].ingame = true;
  4739.         } else {
  4740.             playerState[i].ingame = false;
  4741.         }
  4742.     }
  4743.  
  4744.     for ( i = 0; i < MAX_CLIENTS; i++ ) {
  4745.         if ( playerState[i].ingame ) {
  4746.             ent = gameLocal.entities[ i ];
  4747.             playerState[ i ].fragCount = msg.ReadBits( ASYNC_PLAYER_FRAG_BITS );
  4748.             playerState[ i ].teamFragCount = msg.ReadBits( ASYNC_PLAYER_FRAG_BITS );
  4749.             playerState[ i ].wins = msg.ReadBits( ASYNC_PLAYER_WINS_BITS );
  4750.             if( gameLocal.gameType == GAME_TOURNEY ) {
  4751.                 if( msg.ReadBits( 1 ) ) {
  4752.                     newInstance = msg.ReadBits( ASYNC_PLAYER_INSTANCE_BITS );
  4753.                     if( newInstance != ent->GetInstance() ) {
  4754.                         ent->SetInstance( newInstance );
  4755.                         if( gameLocal.GetLocalPlayer() && i != gameLocal.localClientNum ) {
  4756.                             if( ent->GetInstance() == gameLocal.GetLocalPlayer()->GetInstance() ) {
  4757.                                 ((idPlayer*)ent)->ClientInstanceJoin();
  4758.                             } else {
  4759.                                 ((idPlayer*)ent)->ClientInstanceLeave();
  4760.                             }
  4761.                         }
  4762.                     }
  4763.                     ((idPlayer*)ent)->SetTourneyStatus( (playerTourneyStatus_t)msg.ReadBits( ASYNC_PLAYER_TOURNEY_STATUS_BITS ) );
  4764.                 }
  4765.             }
  4766.         }
  4767.     }
  4768.  
  4769.     for ( i = 0; i < MAX_CLIENTS; i++ ) {
  4770.         if ( playerState[i].ingame ) {
  4771.             playerState[ i ].ping = msg.ReadBits( ASYNC_PLAYER_PING_BITS );
  4772.         }
  4773.     }
  4774. }
  4775.  
  4776. // RAVEN BEGIN
  4777. // bdube: global item sounds
  4778. /*
  4779. ================
  4780. idMultiplayerGame::PlayGlobalItemAcquireSound
  4781. ================
  4782. */
  4783. void idMultiplayerGame::PlayGlobalItemAcquireSound( int defIndex ) {
  4784.     const idDeclEntityDef*  def;
  4785.     def = static_cast<const idDeclEntityDef*>( declManager->DeclByIndex( DECL_ENTITYDEF, defIndex, false ) );
  4786.     if ( !def ) {
  4787.         gameLocal.Warning ( "NET: invalid entity def index (%d) for global item acquire sound", defIndex );
  4788.         return;
  4789.     }
  4790.  
  4791.     if( !gameLocal.GetLocalPlayer() || !gameLocal.currentThinkingEntity || gameLocal.GetLocalPlayer()->GetInstance() == gameLocal.currentThinkingEntity->GetInstance() ) {
  4792.         soundSystem->PlayShaderDirectly ( SOUNDWORLD_GAME, def->dict.GetString ( "snd_acquire" ) );        
  4793.     }
  4794.  
  4795.     if ( gameLocal.isServer ) {
  4796.         idBitMsg outMsg;
  4797.         byte msgBuf[1024];
  4798.         outMsg.Init( msgBuf, sizeof( msgBuf ) );
  4799.         outMsg.WriteByte( GAME_RELIABLE_MESSAGE_ITEMACQUIRESOUND );
  4800.         outMsg.WriteBits ( defIndex, gameLocal.entityDefBits );
  4801.         gameLocal.ServerSendInstanceReliableMessage( gameLocal.currentThinkingEntity, -1, outMsg );
  4802.     }    
  4803. }
  4804. // RAVEN END
  4805.  
  4806. /*
  4807. ================
  4808. idMultiplayerGame::PrintMessageEvent
  4809. ================
  4810. */
  4811. void idMultiplayerGame::PrintMessageEvent( int to, msg_evt_t evt, int parm1, int parm2 ) {
  4812.     switch ( evt ) {
  4813.         case MSG_SUICIDE:
  4814.             assert( parm1 >= 0 );
  4815.             AddChatLine( common->GetLocalizedString( "#str_104293" ), gameLocal.userInfo[ parm1 ].GetString( "ui_name" ) );
  4816.             break;
  4817.         case MSG_KILLED:
  4818.             assert( parm1 >= 0 && parm2 >= 0 );
  4819.             AddChatLine( common->GetLocalizedString( "#str_104292" ), gameLocal.userInfo[ parm1 ].GetString( "ui_name" ), gameLocal.userInfo[ parm2 ].GetString( "ui_name" ) );
  4820.             break;
  4821.         case MSG_KILLEDTEAM:
  4822.             assert( parm1 >= 0 && parm2 >= 0 );
  4823.             AddChatLine( common->GetLocalizedString( "#str_104291" ), gameLocal.userInfo[ parm1 ].GetString( "ui_name" ), gameLocal.userInfo[ parm2 ].GetString( "ui_name" ) );
  4824.             break;
  4825.         case MSG_TELEFRAGGED:
  4826.             assert( parm1 >= 0 && parm2 >= 0 );
  4827.             AddChatLine( common->GetLocalizedString( "#str_104290" ), gameLocal.userInfo[ parm1 ].GetString( "ui_name" ), gameLocal.userInfo[ parm2 ].GetString( "ui_name" ) );
  4828.             break;
  4829.         case MSG_DIED:
  4830.             assert( parm1 >= 0 );
  4831.             AddChatLine( common->GetLocalizedString( "#str_104289" ), gameLocal.userInfo[ parm1 ].GetString( "ui_name" ) );
  4832.             break;
  4833.         case MSG_VOTE:
  4834.             AddChatLine( common->GetLocalizedString( "#str_104288" ) );
  4835.             break;
  4836.         case MSG_SUDDENDEATH:
  4837.             AddChatLine( common->GetLocalizedString( "#str_104287" ) );
  4838.             break;
  4839.         case MSG_FORCEREADY:
  4840.             AddChatLine( common->GetLocalizedString( "#str_104286" ), gameLocal.userInfo[ parm1 ].GetString( "ui_name" ) );
  4841. // RAVEN BEGIN
  4842. // jnewquist: Use accessor for static class type 
  4843.             if ( gameLocal.entities[ parm1 ] && gameLocal.entities[ parm1 ]->IsType( idPlayer::GetClassType() ) ) {
  4844. // RAVEN END
  4845.                 static_cast< idPlayer * >( gameLocal.entities[ parm1 ] )->forcedReady = true;
  4846.             }
  4847.             break;
  4848.         case MSG_JOINEDSPEC:
  4849.             AddChatLine( common->GetLocalizedString( "#str_104285" ), gameLocal.userInfo[ parm1 ].GetString( "ui_name" ) );
  4850.             break;
  4851.         case MSG_TIMELIMIT:
  4852.             AddChatLine( common->GetLocalizedString( "#str_104284" ) );
  4853.             break;
  4854.         case MSG_FRAGLIMIT:
  4855.              if ( gameLocal.gameType == GAME_TDM ) {
  4856.                  AddChatLine( common->GetLocalizedString( "#str_107665" ), parm1 ? "Strogg" : "Marine" );
  4857.             } else {
  4858.                 AddChatLine( common->GetLocalizedString( "#str_104281" ), gameLocal.userInfo[ parm1 ].GetString( "ui_name" ) );
  4859.             }
  4860.             break;
  4861.         case MSG_CAPTURELIMIT:
  4862.             AddChatLine( "%s team hit the capture limit.", parm1 ? "Strogg" : "Marine" );
  4863.             break;
  4864.         case MSG_JOINTEAM:
  4865.             AddChatLine( common->GetLocalizedString( "#str_104280" ), gameLocal.userInfo[ parm1 ].GetString( "ui_name" ), parm2 ? "Strogg" : "Marine" );
  4866.             if( parm1 == gameLocal.localClientNum ) {
  4867.                 if( parm2 == TEAM_STROGG ) {
  4868.                     ScheduleAnnouncerSound( AS_TEAM_JOIN_STROGG, gameLocal.time );
  4869.                 } else if( parm2 == TEAM_MARINE ) {
  4870.                     ScheduleAnnouncerSound( AS_TEAM_JOIN_MARINE, gameLocal.time );
  4871.                 }
  4872.             }
  4873.             break;
  4874.         case MSG_HOLYSHIT:
  4875.             AddChatLine( common->GetLocalizedString( "#str_106732" ) );
  4876.             break;
  4877.         default:
  4878.             gameLocal.DPrintf( "PrintMessageEvent: unknown message type %d\n", evt );
  4879.             return;
  4880.     }
  4881.     if ( !gameLocal.isClient ) {
  4882.         idBitMsg outMsg;
  4883.         byte msgBuf[1024];
  4884.         outMsg.Init( msgBuf, sizeof( msgBuf ) );
  4885.         outMsg.WriteByte( GAME_RELIABLE_MESSAGE_DB );
  4886.         outMsg.WriteByte( evt );
  4887.         outMsg.WriteByte( parm1 );
  4888.         outMsg.WriteByte( parm2 );
  4889.         networkSystem->ServerSendReliableMessage( to, outMsg );
  4890.     }
  4891. }
  4892.  
  4893. /*
  4894. ================
  4895. idMultiplayerGame::CheckSpawns
  4896. ================
  4897. */
  4898. void idMultiplayerGame::CheckRespawns( idPlayer *spectator ) {
  4899.     for( int i = 0 ; i < gameLocal.numClients ; i++ ) {
  4900.         idEntity *ent = gameLocal.entities[ i ];
  4901.         
  4902.         if ( !ent || !ent->IsType( idPlayer::GetClassType() ) ) {
  4903.             continue;
  4904.         }
  4905.  
  4906.         idPlayer *p = static_cast<idPlayer *>(ent);
  4907.  
  4908.         // once we hit sudden death, nobody respawns till game has ended
  4909.         // no respawns in tourney mode, the tourney manager manually handles spawns
  4910.         if ( (WantRespawn( p ) || p == spectator) ) {
  4911.             if ( gameState->GetMPGameState() == SUDDENDEATH && gameLocal.gameType != GAME_TOURNEY ) {
  4912.                 // respawn rules while sudden death are different
  4913.                 // sudden death may trigger while a player is dead, so there are still cases where we need to respawn
  4914.                 // don't do any respawns while we are in end game delay though
  4915.                 if ( gameLocal.IsTeamGame() || p->IsLeader() ) {
  4916.                     //everyone respawns in team games, only fragleaders respawn in DM
  4917.                     p->ServerSpectate( false );
  4918.                 } else {//if ( !p->IsLeader() ) {
  4919.                     // sudden death is rolling, this player is not a leader, have him spectate
  4920.                     p->ServerSpectate( true );
  4921.                     CheckAbortGame();
  4922.                 }
  4923.             } else {
  4924.                 if ( gameState->GetMPGameState() == WARMUP || gameState->GetMPGameState() == COUNTDOWN || gameState->GetMPGameState() == GAMEON ) {
  4925.                     if ( gameLocal.gameType != GAME_TOURNEY ) {
  4926.                         p->ServerSpectate( false );
  4927.                     } else {
  4928.                         if( p->GetArena() >= 0 && p->GetArena() < MAX_ARENAS ) {
  4929.                             rvTourneyArena& arena = ((rvTourneyGameState*)gameState)->GetArena( p->GetArena() );
  4930.                             if( ( arena.GetState() != AS_DONE && arena.GetState() != AS_INACTIVE ) && ( p == arena.GetPlayers()[ 0 ] || p == arena.GetPlayers()[ 1 ] ) ) {
  4931.                                 // only allow respawn if the arena we're in is active 
  4932.                                 // and we're one of the assigned players (we're not just spectating it)
  4933.                                 p->ServerSpectate( false );
  4934.                             }
  4935.                         } else {
  4936.                             // always allow respawn in the waiting room
  4937.                             assert( p->GetArena() == MAX_ARENAS );
  4938.                             p->ServerSpectate( false );
  4939.                         }
  4940.                     }
  4941.                 }                
  4942.             }
  4943.         } else if ( p->wantSpectate && !p->spectating ) {
  4944.             playerState[ i ].fragCount = 0; // whenever you willingly go spectate during game, your score resets
  4945.             p->ServerSpectate( true );
  4946.             CheckAbortGame();
  4947.         }
  4948.     }
  4949. }
  4950.  
  4951. void idMultiplayerGame::FreeLight ( int lightID ) {
  4952.     if ( lightHandles[lightID] != -1 && gameRenderWorld ) {
  4953.         gameRenderWorld->FreeLightDef( lightHandles[lightID] );
  4954.         lightHandles[lightID] = -1;
  4955.     }
  4956. }
  4957.  
  4958. void idMultiplayerGame::UpdateLight ( int lightID, idPlayer *player ) {
  4959.     lights[ lightID ].origin = player->GetPhysics()->GetOrigin() + idVec3( 0, 0, 20 );
  4960.     
  4961.     if ( lightHandles[ lightID ] == -1 ) {
  4962.         lightHandles[ lightID ] = gameRenderWorld->AddLightDef ( &lights[ lightID ] );
  4963.     } else {
  4964.         gameRenderWorld->UpdateLightDef( lightHandles[ lightID ], &lights[ lightID ] );
  4965.     }
  4966. }
  4967.  
  4968. void idMultiplayerGame::CheckSpecialLights( void ) {
  4969.     if ( !gameLocal.isLastPredictFrame ) {
  4970.         return;
  4971.     }
  4972.  
  4973.     idPlayer *marineFlagCarrier = NULL;
  4974.     idPlayer *stroggFlagCarrier = NULL;
  4975.     idPlayer *quadDamageCarrier = NULL;
  4976.     idPlayer *regenerationCarrier = NULL;
  4977.     idPlayer *hasteCarrier = NULL;
  4978.  
  4979.     for( int i = 0 ; i < gameLocal.numClients ; i++ ) {
  4980.         idEntity *ent = gameLocal.entities[ i ];
  4981.         if ( !ent || !ent->IsType( idPlayer::GetClassType() ) ) {
  4982.             continue;
  4983.         }
  4984.  
  4985.         idPlayer *p = static_cast<idPlayer *>( ent );
  4986.  
  4987.         if( gameLocal.GetLocalPlayer() && p->GetInstance() != gameLocal.GetLocalPlayer()->GetInstance() ) {
  4988.             continue;
  4989.         }
  4990.  
  4991.         if ( p->PowerUpActive( POWERUP_CTF_MARINEFLAG ) ) {
  4992.             marineFlagCarrier = p;
  4993.         }
  4994.         else if ( p->PowerUpActive( POWERUP_CTF_STROGGFLAG ) ) {
  4995.             stroggFlagCarrier = p;
  4996.         }
  4997.         else if( p->PowerUpActive( POWERUP_QUADDAMAGE ) ) {
  4998.             quadDamageCarrier = p;
  4999.         }
  5000.         else if( p->PowerUpActive( POWERUP_REGENERATION ) ) {
  5001.             regenerationCarrier = p;
  5002.         }
  5003.         else if( p->PowerUpActive( POWERUP_HASTE ) ) {
  5004.             hasteCarrier = p;
  5005.         }
  5006.     }
  5007.  
  5008.     if ( marineFlagCarrier ) {
  5009.         UpdateLight ( MPLIGHT_CTF_MARINE, marineFlagCarrier );
  5010.     } else {
  5011.         FreeLight( MPLIGHT_CTF_MARINE );
  5012.     }
  5013.  
  5014.     if ( stroggFlagCarrier ) {
  5015.         UpdateLight ( MPLIGHT_CTF_STROGG, stroggFlagCarrier );
  5016.     } else {
  5017.         FreeLight( MPLIGHT_CTF_STROGG );
  5018.     }
  5019.  
  5020.     if ( quadDamageCarrier ) {
  5021.         UpdateLight ( MPLIGHT_QUAD, quadDamageCarrier );
  5022.     } else {
  5023.         FreeLight( MPLIGHT_QUAD );
  5024.     }
  5025.  
  5026.     if ( regenerationCarrier ) {
  5027.         UpdateLight ( MPLIGHT_REGEN, regenerationCarrier );
  5028.     } else {
  5029.         FreeLight( MPLIGHT_REGEN );
  5030.     }
  5031.  
  5032.     if ( hasteCarrier ) {
  5033.         UpdateLight ( MPLIGHT_HASTE, hasteCarrier );
  5034.     } else {
  5035.         FreeLight( MPLIGHT_HASTE );
  5036.     }
  5037. }
  5038.  
  5039. /*
  5040. ================
  5041. idMultiplayerGame::ForceReady
  5042. ================
  5043. */
  5044. void idMultiplayerGame::ForceReady( ) {
  5045.  
  5046.     for( int i = 0 ; i < gameLocal.numClients ; i++ ) {
  5047.         idEntity *ent = gameLocal.entities[ i ];
  5048. // RAVEN BEGIN
  5049. // jnewquist: Use accessor for static class type 
  5050.         if ( !ent || !ent->IsType( idPlayer::GetClassType() ) ) {
  5051. // RAVEN END
  5052.             continue;
  5053.         }
  5054.         idPlayer *p = static_cast<idPlayer *>( ent );
  5055.         if ( !p->IsReady() ) {
  5056.             PrintMessageEvent( -1, MSG_FORCEREADY, i );
  5057.             p->forcedReady = true;
  5058.         }
  5059.     }
  5060. }
  5061.  
  5062. /*
  5063. ================
  5064. idMultiplayerGame::ForceReady_f
  5065. ================
  5066. */
  5067. void idMultiplayerGame::ForceReady_f( const idCmdArgs &args ) {
  5068.     if ( !gameLocal.isMultiplayer || gameLocal.isClient ) {
  5069.         gameLocal.Printf( "forceReady: multiplayer server only\n" );
  5070.         return;
  5071.     }
  5072.     gameLocal.mpGame.ForceReady();
  5073. }
  5074.  
  5075. /*
  5076. ================
  5077. idMultiplayerGame::DropWeapon
  5078. ================
  5079. */
  5080. void idMultiplayerGame::DropWeapon( int clientNum ) {
  5081.     assert( !gameLocal.isClient );
  5082.     idEntity *ent = gameLocal.entities[ clientNum ];
  5083. // RAVEN BEGIN
  5084. // jnewquist: Use accessor for static class type 
  5085.     if ( !ent || !ent->IsType( idPlayer::GetClassType() ) ) {
  5086. // RAVEN END
  5087.         return;
  5088.     }
  5089. // RAVEN BEGIN
  5090. // bdube: removed parameter
  5091.     static_cast< idPlayer* >( ent )->DropWeapon( );
  5092. // RAVEN END
  5093. }
  5094.  
  5095. /*
  5096. ================
  5097. idMultiplayerGame::DropWeapon_f
  5098. ================
  5099. */
  5100. void idMultiplayerGame::DropWeapon_f( const idCmdArgs &args ) {
  5101.     if ( !gameLocal.isMultiplayer ) {
  5102.         gameLocal.Printf( "clientDropWeapon: only valid in multiplayer\n" );
  5103.         return;
  5104.     }
  5105.     idBitMsg    outMsg;
  5106.     byte        msgBuf[128];
  5107.     outMsg.Init( msgBuf, sizeof( msgBuf ) );
  5108.     outMsg.WriteByte( GAME_RELIABLE_MESSAGE_DROPWEAPON );
  5109.     networkSystem->ClientSendReliableMessage( outMsg );
  5110. }
  5111.  
  5112. /*
  5113. ================
  5114. idMultiplayerGame::MessageMode_f
  5115. ================
  5116. */
  5117. void idMultiplayerGame::MessageMode_f( const idCmdArgs &args ) {
  5118.     gameLocal.mpGame.MessageMode( args );
  5119. }
  5120.  
  5121. /*
  5122. ================
  5123. idMultiplayerGame::MessageMode
  5124. ================
  5125. */
  5126. void idMultiplayerGame::MessageMode( const idCmdArgs &args ) {
  5127.     const char *mode;
  5128.     int imode;
  5129.  
  5130.     if ( !gameLocal.isMultiplayer ) {
  5131.         common->Printf( "clientMessageMode: only valid in multiplayer\n" );
  5132.         return;
  5133.     }
  5134.     if ( !mainGui ) {
  5135.         common->Printf( "no local client\n" );
  5136.         return;
  5137.     }
  5138.     mode = args.Argv( 1 );
  5139.     if ( !mode[ 0 ] || !gameLocal.IsTeamGame() ) {
  5140.         imode = 0;
  5141.     } else {
  5142.         imode = atoi( mode );
  5143.     }
  5144.     msgmodeGui->SetStateString( "messagemode", imode ? "1" : "0" );
  5145.     msgmodeGui->SetStateString( "chattext", "" );
  5146.     nextMenu = 2;
  5147.     // let the session know that we want our ingame main menu opened
  5148.     gameLocal.sessionCommand = "game_startmenu";
  5149. }
  5150.  
  5151. /*
  5152. ================
  5153. idMultiplayerGame::Vote_f
  5154. ================
  5155. */
  5156. void idMultiplayerGame::Vote_f( const idCmdArgs &args ) { 
  5157. // RAVEN BEGIN
  5158. // shouchard:  implemented for testing    
  5159.     if ( args.Argc() < 2 ) {
  5160.         common->Printf( common->GetLocalizedString( "#str_104418" ) );
  5161.         return;
  5162.     }
  5163.  
  5164.     const char *szArg1 = args.Argv(1);
  5165.     bool voteValue = false;
  5166.     if ( 0 == idStr::Icmp( szArg1, "yes" ) ) {
  5167.         voteValue = true;
  5168.     }
  5169.     
  5170.     gameLocal.mpGame.CastVote( gameLocal.localClientNum, voteValue );
  5171. // RAVEN END
  5172. }
  5173.  
  5174. /*
  5175. ================
  5176. idMultiplayerGame::CallVote_f
  5177. ================
  5178. */
  5179. void idMultiplayerGame::CallVote_f( const idCmdArgs &args ) { 
  5180. // RAVEN BEGIN
  5181. // shouchard:  implemented for testing
  5182.     const char *szArg1 = args.Argv(1);
  5183.     const char *szArg2 = args.Argv(2);
  5184.     if ( '\0' == *szArg1 ) {
  5185.         common->Printf( common->GetLocalizedString( "#str_104404" ) );
  5186.         common->Printf( common->GetLocalizedString( "#str_104405" ) );
  5187.         return;
  5188.     }
  5189.  
  5190.     vote_flags_t voteFlags = VOTE_NONE;
  5191.     if ( 0 == idStr::Icmp( szArg1, "restart" ) ) {
  5192.         voteFlags = VOTE_RESTART;
  5193.     } else if ( 0 == idStr::Icmp( szArg1, "timelimit" ) ) {
  5194.         if ( '\0' == *szArg2 ) {
  5195.             common->Printf( common->GetLocalizedString( "#str_104406" ) );
  5196.             return;
  5197.         }
  5198.         voteFlags = VOTE_TIMELIMIT;
  5199.     } else if ( 0 == idStr::Icmp( szArg1, "fraglimit" ) ) {
  5200.         if ( '\0' == *szArg2 ) {
  5201.             common->Printf( common->GetLocalizedString( "#str_104407" ) );
  5202.             return;
  5203.         }
  5204.         voteFlags = VOTE_FRAGLIMIT;
  5205.     } else if ( 0 == idStr::Icmp( szArg1, "gametype" ) ) {
  5206.         if ( '\0' == *szArg2 ) {
  5207.             common->Printf( common->GetLocalizedString( "#str_104408" ) );
  5208.             common->Printf( common->GetLocalizedString( "#str_104409" ) );
  5209.             return;
  5210.         }
  5211.         voteFlags = VOTE_GAMETYPE;
  5212.         // the use of magic numbers here and relying on the return of a 
  5213.         // static buffer creeps the hell outta me--just in case you wondered.
  5214.         if ( 0 == idStr::Icmp( szArg2, "DM" ) ) {
  5215.             szArg2 = va("%d", VOTE_GAMETYPE_DM);
  5216.         } else if ( 0 == idStr::Icmp( szArg2, "Tourney" ) ) {
  5217.             szArg2 = va("%d", VOTE_GAMETYPE_TOURNEY);
  5218.         } else if ( 0 == idStr::Icmp( szArg2, "Team DM" ) ) {
  5219.             szArg2 = va("%d", VOTE_GAMETYPE_TDM);
  5220.         } else if ( 0 == idStr::Icmp( szArg2, "CTF" ) ) {
  5221.             szArg2 = va( "%d", VOTE_GAMETYPE_CTF );
  5222.         } else if ( 0 == idStr::Icmp( szArg2, "Arena CTF" ) ) {
  5223.             szArg2 = va( "%d", VOTE_GAMETYPE_ARENA_CTF );
  5224.         } else {
  5225.             common->Printf( common->GetLocalizedString( "#str_104410" ) );
  5226.             common->Printf( common->GetLocalizedString( "#str_104409" ) );
  5227.             return;
  5228.         }
  5229.     }
  5230.     else if ( 0 == idStr::Icmp( szArg1, "kick" ) ) {
  5231.         if ( '\0' == *szArg2 ) {
  5232.             common->Printf( common->GetLocalizedString( "#str_104412" ) );
  5233.             return;
  5234.         }
  5235.         voteFlags = VOTE_KICK;
  5236.     } else if ( 0 == idStr::Icmp( szArg1, "map" ) ) {
  5237.         if ( '\0' == *szArg2 ) {
  5238.             common->Printf( common->GetLocalizedString( "#str_104413" ) );
  5239.             return;
  5240.         }
  5241.         voteFlags = VOTE_MAP;
  5242.     } else if ( 0 == idStr::Icmp( szArg1, "minplayers" ) ) {
  5243.         if ( '\0' == *szArg2 ) {
  5244.             common->Printf( common->GetLocalizedString( "#str_104414" ) );
  5245.             return;
  5246.         }
  5247.         voteFlags = VOTE_MIN_PLAYERS;
  5248.     } else if ( 0 == idStr::Icmp( szArg1, "nextmap" ) ) {
  5249.         voteFlags = VOTE_NEXTMAP;
  5250.     } else if ( 0 == idStr::Icmp( szArg1, "capturelimit" ) ) {
  5251.         if ( '\0' == *szArg2 ) {
  5252.             common->Printf( common->GetLocalizedString( "#str_104415" ) );
  5253.             return;
  5254.         }
  5255.         voteFlags = VOTE_CAPTURELIMIT;
  5256.     //} else if ( 0 == idStr::Icmp( szArg1, "roundlimit")) {
  5257.     } else if ( 0 == idStr::Icmp( szArg1, "autobalance" ) ) {
  5258.         if ( '\0' == *szArg2 ) {
  5259.             common->Printf( common->GetLocalizedString( "#str_104416" ) );
  5260.         }
  5261.         voteFlags = VOTE_AUTOBALANCE;
  5262.     } else {
  5263.         common->Printf( common->GetLocalizedString( "#str_104404" ) );
  5264.         common->Printf( common->GetLocalizedString( "#str_104405" ) );
  5265.         return;
  5266.     }
  5267.  
  5268.     gameLocal.mpGame.ClientCallVote( voteFlags, szArg2 );
  5269. // RAVEN END
  5270. }
  5271.  
  5272. // RAVEN BEGIN
  5273. // shouchard: added voice mute and unmute console commands; sans XBOX to not step on their live voice stuff
  5274. #ifndef _XBOX
  5275. /*
  5276. ================
  5277. idMultiplayerGame::VoiceMute_f
  5278. ================
  5279. */
  5280. void idMultiplayerGame::VoiceMute_f( const idCmdArgs &args ) {
  5281.     if ( args.Argc() < 2 ) {
  5282.         common->Printf( "USAGE: clientvoicemute <player>\n" );
  5283.         return;
  5284.     }
  5285.     gameLocal.mpGame.ClientVoiceMute( gameLocal.mpGame.GetClientNumFromPlayerName( args.Argv( 1 ) ), true );
  5286. }
  5287.  
  5288. /*
  5289. ================
  5290. idMultiplayerGame::VoiceUnmute_f
  5291. ================
  5292. */
  5293. void idMultiplayerGame::VoiceUnmute_f( const idCmdArgs &args ) {
  5294.     if ( args.Argc() < 2 ) {
  5295.         common->Printf( "USAGE: clientvoiceunmute <player>\n" );
  5296.         return;
  5297.     }
  5298.     gameLocal.mpGame.ClientVoiceMute( gameLocal.mpGame.GetClientNumFromPlayerName( args.Argv( 1 ) ), false );
  5299. }
  5300.  
  5301. // RAVEN END
  5302. #endif // _XBOX
  5303.  
  5304. // RAVEN BEGIN
  5305. /*
  5306. ================
  5307. idMultiplayerGame::ForceTeamChange_f
  5308. ================
  5309. */
  5310. void idMultiplayerGame::ForceTeamChange_f( const idCmdArgs &args)    {
  5311.  
  5312.     if( !gameLocal.isMultiplayer )    {
  5313.         common->Printf( "[MP ONLY] Forces player to change teams. Usage: ForceTeamChange <client number>\n" );
  5314.         return;
  5315.     }
  5316.  
  5317.     idStr clientId;
  5318.     int clientNum;
  5319.  
  5320.     clientId = args.Argv( 1 );
  5321.     if ( !clientId.IsNumeric() ) {
  5322.         common->Printf( "usage: ForceTeamChange <client number>\n" );
  5323.         return;
  5324.     }
  5325.  
  5326.     clientNum = atoi( clientId );
  5327.     
  5328.     if ( gameLocal.entities[ clientNum ] && gameLocal.entities[ clientNum ]->IsType( idPlayer::GetClassType() ) )
  5329.     {
  5330.         idPlayer *player = static_cast< idPlayer *>( gameLocal.entities[ clientNum ] );
  5331.         player->GetUserInfo()->Set( "ui_team", player->team ? "Marine" : "Strogg" );
  5332.         cmdSystem->BufferCommandText( CMD_EXEC_NOW, va( "updateUI %d\n", clientNum ) );
  5333.     }
  5334.  
  5335. }
  5336.  
  5337.  
  5338. /*
  5339. ================
  5340. idMultiplayerGame::RemoveClientFromBanList_f
  5341. ================
  5342. */
  5343. void idMultiplayerGame::RemoveClientFromBanList_f( const idCmdArgs& args )    {
  5344.  
  5345.     if( !gameLocal.isMultiplayer )    {
  5346.         common->Printf( "[MP ONLY] Remove player from banlist. Usage: RemoveClientFromBanList <client number>\n" );
  5347.         return;
  5348.     }
  5349.     
  5350.     idStr clientId;
  5351.     clientId = args.Argv( 1 );
  5352.     int clientNum;
  5353.  
  5354.     if ( !clientId.IsNumeric() ) {
  5355.         common->Printf( "Usage: RemoveClientFromBanList <client number>\n" );
  5356.         return;
  5357.     }
  5358.  
  5359.     clientNum = atoi( clientId );
  5360.  
  5361.     const char *clientGuid = networkSystem->GetClientGUID( clientNum ); //  gameLocal.GetGuidByClientNum( clientNum );
  5362.  
  5363.     if ( NULL == clientGuid || !clientGuid[ 0 ]) {
  5364.         common->DPrintf( "idMultiplayerGame::HandleServerAdminRemoveBan:  bad guid!\n" );
  5365.         return;
  5366.     }
  5367.  
  5368.     if ( gameLocal.isServer || gameLocal.isListenServer ) {
  5369.         // remove from the ban list
  5370.         gameLocal.RemoveGuidFromBanList( clientGuid );
  5371.     }
  5372.  
  5373. }
  5374.  
  5375. /*
  5376. ================
  5377. idMultiplayerGame::ProcessRconReturn
  5378. ================
  5379. */
  5380. void idMultiplayerGame::ProcessRconReturn( bool success )    {
  5381.  
  5382.     if( success )    {
  5383.         mainGui->HandleNamedEvent("adminPasswordSuccess");
  5384.     } else {
  5385.         mainGui->HandleNamedEvent("adminPasswordFail");
  5386.     }
  5387.  
  5388.  
  5389. }
  5390.  
  5391.  
  5392. // RAVEN END
  5393.  
  5394. /*
  5395. ================
  5396. idMultiplayerGame::ServerStartVote
  5397. ================
  5398. */
  5399. void idMultiplayerGame::ServerStartVote( int clientNum, vote_flags_t voteIndex, const char *value ) {
  5400.     int i;
  5401.  
  5402.     assert( vote == VOTE_NONE );
  5403.  
  5404.     // setup
  5405.     yesVotes = 1;
  5406.     noVotes = 0;
  5407.     vote = voteIndex;
  5408.     voteValue = value;
  5409.     voteTimeOut = gameLocal.time + 20000;
  5410.     // mark players allowed to vote - only current ingame players, players joining during vote will be ignored
  5411.     for ( i = 0; i < gameLocal.numClients; i++ ) {
  5412. // RAVEN BEGIN
  5413. // jnewquist: Use accessor for static class type 
  5414.         if ( gameLocal.entities[ i ] && gameLocal.entities[ i ]->IsType( idPlayer::GetClassType() ) ) {
  5415. // RAVEN END
  5416.             playerState[ i ].vote = ( i == clientNum ) ? PLAYER_VOTE_YES : PLAYER_VOTE_WAIT;
  5417.         } else {
  5418.             playerState[i].vote = PLAYER_VOTE_NONE;
  5419.         }
  5420.     }
  5421. }
  5422.  
  5423. /*
  5424. ================
  5425. idMultiplayerGame::ClientStartVote
  5426. ================
  5427. */
  5428. void idMultiplayerGame::ClientStartVote( int clientNum, const char *_voteString ) {
  5429.     idBitMsg    outMsg;
  5430.     byte        msgBuf[ MAX_GAME_MESSAGE_SIZE ];
  5431.  
  5432.     if ( !gameLocal.isClient ) {
  5433.         outMsg.Init( msgBuf, sizeof( msgBuf ) );
  5434.         outMsg.WriteByte( GAME_RELIABLE_MESSAGE_STARTVOTE );
  5435.         outMsg.WriteByte( clientNum );
  5436.         outMsg.WriteString( _voteString );
  5437.         networkSystem->ServerSendReliableMessage( -1, outMsg );
  5438.     }
  5439.  
  5440.     voteString = _voteString;
  5441.     AddChatLine( va( common->GetLocalizedString( "#str_104279" ), gameLocal.userInfo[ clientNum ].GetString( "ui_name" ) ) );
  5442. // RAVEN BEGIN
  5443. // shouchard:  better info when a vote called in the chat buffer
  5444.     AddChatLine( voteString ); // TODO:  will push this into a UI field later
  5445. // shouchard:  display the vote called text on the hud
  5446.     if ( gameLocal.GetLocalPlayer() && gameLocal.GetLocalPlayer()->mphud ) {
  5447.         gameLocal.GetLocalPlayer()->mphud->SetStateInt( "voteNotice", 1 );
  5448.     }
  5449. // RAVEN END
  5450.     ScheduleAnnouncerSound( AS_GENERAL_VOTE_NOW, gameLocal.time );
  5451.     
  5452.     if ( clientNum == gameLocal.localClientNum ) {
  5453.         voted = true;
  5454.     } else {
  5455.         voted = false;
  5456.     }
  5457.     if ( gameLocal.isClient ) {
  5458.         // the the vote value to something so the vote line is displayed
  5459.         vote = VOTE_RESTART;
  5460.         yesVotes = 1;
  5461.         noVotes = 0;
  5462.     }
  5463.  
  5464.     ClientUpdateVote( VOTE_UPDATE, yesVotes, noVotes, currentVoteData );
  5465. }
  5466.  
  5467. /*
  5468. ================
  5469. idMultiplayerGame::ClientUpdateVote
  5470. ================
  5471. */
  5472. void idMultiplayerGame::ClientUpdateVote( vote_result_t status, int yesCount, int noCount, const voteStruct_t &voteData ) {
  5473.     idBitMsg    outMsg;
  5474.     byte        msgBuf[ MAX_GAME_MESSAGE_SIZE ];
  5475.     const char * localizedString = 0;
  5476.     idPlayer* player = gameLocal.GetLocalPlayer( );
  5477.  
  5478.     if ( !gameLocal.isClient ) {
  5479.         outMsg.Init( msgBuf, sizeof( msgBuf ) );
  5480.         outMsg.WriteByte( GAME_RELIABLE_MESSAGE_UPDATEVOTE );
  5481.         outMsg.WriteByte( status );
  5482.         outMsg.WriteByte( yesCount );
  5483.         outMsg.WriteByte( noCount );
  5484. // RAVEN BEGIN
  5485. // shouchard:  multifield vote support
  5486.         if ( VOTE_MULTIFIELD != vote ) {
  5487.             outMsg.WriteByte( 0 );
  5488.         } else {
  5489.             outMsg.WriteByte( 1 );
  5490.             outMsg.WriteShort( voteData.m_fieldFlags );
  5491.             outMsg.WriteByte( idMath::ClampChar( voteData.m_kick ) );
  5492.             outMsg.WriteString( voteData.m_map.c_str() );
  5493.             outMsg.WriteByte( idMath::ClampChar( voteData.m_gameType ) );
  5494.             outMsg.WriteByte( idMath::ClampChar( voteData.m_timeLimit ) );
  5495.             outMsg.WriteShort( idMath::ClampShort( voteData.m_fragLimit ) );
  5496.             outMsg.WriteShort( idMath::ClampShort( voteData.m_tourneyLimit ) );
  5497.             outMsg.WriteShort( idMath::ClampShort( voteData.m_captureLimit ) );
  5498.             outMsg.WriteShort( idMath::ClampShort( voteData.m_minPlayers ) );
  5499.             outMsg.WriteByte( idMath::ClampChar( voteData.m_teamBalance ) );
  5500.         }
  5501.         networkSystem->ServerSendReliableMessage( -1, outMsg );
  5502.     } else {
  5503.         currentVoteData = voteData;
  5504.     }
  5505. // RAVEN END
  5506.  
  5507.     if ( vote == VOTE_NONE ) {
  5508.         // clients coming in late don't get the vote start and are not allowed to vote
  5509.         if ( mainGui ) {
  5510.             mainGui->SetStateInt( "vote_going", 0 );
  5511.         }
  5512.         return;
  5513.     }
  5514.  
  5515.     switch ( status ) {
  5516.         case VOTE_FAILED:
  5517.             localizedString = common->GetLocalizedString( "#str_104278" );
  5518.             AddChatLine( localizedString );
  5519.             ScheduleAnnouncerSound( AS_GENERAL_VOTE_FAILED, gameLocal.time );
  5520.             if ( gameLocal.isClient ) {
  5521.                 vote = VOTE_NONE;
  5522.             }
  5523.             break;
  5524.         case VOTE_PASSED:
  5525.             localizedString = common->GetLocalizedString( "#str_104277" );
  5526.             AddChatLine( localizedString );
  5527.             ScheduleAnnouncerSound( AS_GENERAL_VOTE_PASSED, gameLocal.time );
  5528.             break;
  5529.         case VOTE_RESET:
  5530.             if ( gameLocal.isClient ) {
  5531.                 vote = VOTE_NONE;
  5532.             }
  5533.             break;
  5534.         case VOTE_ABORTED:
  5535.             localizedString = common->GetLocalizedString( "#str_104276" );
  5536.             AddChatLine( localizedString );
  5537.             if ( gameLocal.isClient ) {
  5538.                 vote = VOTE_NONE;
  5539.             }
  5540.             break;
  5541.         case VOTE_UPDATE:
  5542.             if ( player && player->mphud && voted ) {
  5543.                 player->mphud->SetStateString( "voteNoticeText", va("^:%s\n%s: %d %s: %d", 
  5544.                     common->GetLocalizedString( "#str_107724" ),
  5545.                     common->GetLocalizedString( "#str_107703" ),
  5546.                     yesCount,
  5547.                     common->GetLocalizedString( "#str_107704" ),
  5548.                     noCount ) );
  5549.             }
  5550.  
  5551.             if ( mainGui ) {
  5552.                 mainGui->SetStateInt( "playerVoted", voted );
  5553.             }
  5554.             break;
  5555.         default:
  5556.             break;
  5557.     }
  5558.  
  5559.     if ( gameLocal.isClient ) {
  5560.         yesVotes = yesCount;
  5561.         noVotes = noCount;
  5562.     }
  5563.  
  5564. // RAVEN BEGIN
  5565. // shouchard:  remove vote notification
  5566.     if ( VOTE_FAILED == status || VOTE_PASSED == status || VOTE_RESET == status ) {
  5567.         ClearVote();
  5568.     }
  5569.  
  5570.     if ( mainGui ) {
  5571.         mainGui->SetStateString( "voteCount", va( common->GetLocalizedString( "#str_104435" ), (int)yesVotes, (int)noVotes ) );
  5572.     }
  5573. // RAVEN END
  5574. }
  5575.  
  5576. /*
  5577. ================
  5578. idMultiplayerGame::ClientCallVote
  5579. ================
  5580. */
  5581. void idMultiplayerGame::ClientCallVote( vote_flags_t voteIndex, const char *voteValue ) {
  5582.     idBitMsg    outMsg;
  5583.     byte        msgBuf[ MAX_GAME_MESSAGE_SIZE ];
  5584.  
  5585.     // send 
  5586.     outMsg.Init( msgBuf, sizeof( msgBuf ) );
  5587.     outMsg.WriteByte( GAME_RELIABLE_MESSAGE_CALLVOTE );
  5588.     outMsg.WriteByte( voteIndex );
  5589.     outMsg.WriteString( voteValue );
  5590.     networkSystem->ClientSendReliableMessage( outMsg );
  5591. }
  5592.  
  5593. /*
  5594. ================
  5595. idMultiplayerGame::CastVote
  5596. ================
  5597. */
  5598. void idMultiplayerGame::CastVote( int clientNum, bool castVote ) {
  5599.     idBitMsg    outMsg;
  5600.     byte        msgBuf[ 128 ];
  5601.  
  5602.     if ( clientNum == gameLocal.localClientNum ) {
  5603.         voted = true;
  5604.     }
  5605.  
  5606.     if ( gameLocal.isClient ) {
  5607.         outMsg.Init( msgBuf, sizeof( msgBuf ) );
  5608.         outMsg.WriteByte( GAME_RELIABLE_MESSAGE_CASTVOTE );
  5609.         outMsg.WriteByte( castVote );
  5610.         networkSystem->ClientSendReliableMessage( outMsg );
  5611.         return;
  5612.     }
  5613.  
  5614.     // sanity
  5615.     if ( vote == VOTE_NONE ) {
  5616.         gameLocal.ServerSendChatMessage( clientNum, "server", "#str_104275" );
  5617.         common->DPrintf( "client %d: cast vote while no vote in progress\n", clientNum );
  5618.         return;
  5619.     }
  5620.     if ( playerState[ clientNum ].vote != PLAYER_VOTE_WAIT ) {
  5621.         gameLocal.ServerSendChatMessage( clientNum, "server", "#str_104274" );
  5622.         common->DPrintf( "client %d: cast vote - vote %d != PLAYER_VOTE_WAIT\n", clientNum, playerState[ clientNum ].vote );
  5623.         return;
  5624.     }
  5625.  
  5626.     if ( castVote ) {
  5627.         playerState[ clientNum ].vote = PLAYER_VOTE_YES;
  5628.         yesVotes++;
  5629.     } else {
  5630.         playerState[ clientNum ].vote = PLAYER_VOTE_NO;
  5631.         noVotes++;
  5632.     }
  5633.  
  5634.     ClientUpdateVote( VOTE_UPDATE, yesVotes, noVotes, currentVoteData );
  5635. }
  5636.  
  5637. /*
  5638. ================
  5639. idMultiplayerGame::ServerCallVote
  5640. ================
  5641. */
  5642. void idMultiplayerGame::ServerCallVote( int clientNum, const idBitMsg &msg ) {
  5643.     vote_flags_t    voteIndex;
  5644.     int                vote_timeLimit, vote_fragLimit, vote_clientNum, vote_gameTypeIndex, vote_minPlayers; //, vote_kickIndex;
  5645. // RAVEN BEGIN
  5646. // shouchard:  added capture limit and autobalance
  5647.     int                vote_captureLimit;
  5648.     bool            vote_autobalance;
  5649. // RAVEN END
  5650.     char            value[ MAX_STRING_CHARS ];
  5651.  
  5652.     assert( clientNum != -1 );
  5653.     assert( !gameLocal.isClient );
  5654.  
  5655.     if( !gameLocal.serverInfo.GetBool( "si_allowVoting" ) ) {
  5656.         return;
  5657.     }
  5658.  
  5659.     voteIndex = (vote_flags_t)msg.ReadByte( );
  5660.     msg.ReadString( value, sizeof( value ) );
  5661.  
  5662.     // sanity checks - setup the vote
  5663.     if ( vote != VOTE_NONE ) {
  5664.         gameLocal.ServerSendChatMessage( clientNum, "server", "#str_104273" );
  5665.         common->DPrintf( "client %d: called vote while voting already in progress - ignored\n", clientNum );
  5666.         return;
  5667.     }
  5668.     switch ( voteIndex ) {
  5669.         case VOTE_RESTART: {
  5670.             ServerStartVote( clientNum, voteIndex, "" );
  5671.             ClientStartVote( clientNum, common->GetLocalizedString( "#str_104271" ) );
  5672.             break;
  5673.         }
  5674.         case VOTE_NEXTMAP: {
  5675.             ServerStartVote( clientNum, voteIndex, "" );
  5676.             ClientStartVote( clientNum, common->GetLocalizedString( "#str_104272" ) );
  5677.             break;
  5678.         }
  5679.         case VOTE_TIMELIMIT: {
  5680.             vote_timeLimit = strtol( value, NULL, 10 );
  5681.             if ( vote_timeLimit == gameLocal.serverInfo.GetInt( "si_timeLimit" ) ) {
  5682.                 gameLocal.ServerSendChatMessage( clientNum, "server", "#str_104270" );
  5683.                 common->DPrintf( "client %d: already at the voted Time Limit\n", clientNum );
  5684.                 return;                    
  5685.             }
  5686.             if ( vote_timeLimit < si_timeLimit.GetMinValue() || vote_timeLimit > si_timeLimit.GetMaxValue() ) {
  5687.                 gameLocal.ServerSendChatMessage( clientNum, "server", "#str_104269" );
  5688.                 common->DPrintf( "client %d: timelimit value out of range for vote: %s\n", clientNum, value );
  5689.                 return;
  5690.             }
  5691.             ServerStartVote( clientNum, voteIndex, value );
  5692.             ClientStartVote( clientNum, va( common->GetLocalizedString( "#str_104268" ), vote_timeLimit ) );
  5693.             break;
  5694.         }
  5695.         case VOTE_FRAGLIMIT: {
  5696.             vote_fragLimit = strtol( value, NULL, 10 );
  5697.             if ( vote_fragLimit == gameLocal.serverInfo.GetInt( "si_fragLimit" ) ) {
  5698.                 gameLocal.ServerSendChatMessage( clientNum, "server", "#str_104267" );
  5699.                 common->DPrintf( "client %d: already at the voted Frag Limit\n", clientNum );
  5700.                 return;
  5701.             }
  5702.             if ( vote_fragLimit < si_fragLimit.GetMinValue() || vote_fragLimit > si_fragLimit.GetMaxValue() ) {
  5703.                 gameLocal.ServerSendChatMessage( clientNum, "server", "#str_104266" );
  5704.                 common->DPrintf( "client %d: fraglimit value out of range for vote: %s\n", clientNum, value );
  5705.                 return;
  5706.             }
  5707.             ServerStartVote( clientNum, voteIndex, value );
  5708.             ClientStartVote( clientNum, va( common->GetLocalizedString( "#str_104303" ), common->GetLocalizedString( "#str_104265" ), vote_fragLimit ) );
  5709.             break;
  5710.         }
  5711.         case VOTE_GAMETYPE: {
  5712. // RAVEN BEGIN
  5713. // shouchard:  removed magic numbers & added CTF type
  5714.             vote_gameTypeIndex = strtol( value, NULL, 10 );
  5715.             assert( vote_gameTypeIndex >= 0 && vote_gameTypeIndex < VOTE_GAMETYPE_COUNT );
  5716.             switch ( vote_gameTypeIndex ) {
  5717.                 case VOTE_GAMETYPE_DM:
  5718.                     strcpy( value, "DM" );
  5719.                     break;
  5720.                 case VOTE_GAMETYPE_TOURNEY:
  5721.                     strcpy( value, "Tourney" );
  5722.                     break;
  5723.                 case VOTE_GAMETYPE_TDM:
  5724.                     strcpy( value, "Team DM" );
  5725.                     break;
  5726.                 case VOTE_GAMETYPE_CTF:
  5727.                     strcpy( value, "CTF" );
  5728.                     break;
  5729.                 case VOTE_GAMETYPE_ARENA_CTF:
  5730.                     strcpy( value, "Arena CTF" );
  5731.                     break;
  5732. // RAVEN END
  5733.             }
  5734.             if ( !idStr::Icmp( value, gameLocal.serverInfo.GetString( "si_gameType" ) ) ) {
  5735.                 gameLocal.ServerSendChatMessage( clientNum, "server", "#str_104259" );
  5736.                 common->DPrintf( "client %d: already at the voted Game Type\n", clientNum );
  5737.                 return;
  5738.             }
  5739.             ServerStartVote( clientNum, voteIndex, value );
  5740.             ClientStartVote( clientNum, va( common->GetLocalizedString( "#str_104258" ), value ) );
  5741.             break;
  5742.         }
  5743.         case VOTE_KICK: {
  5744.             vote_clientNum = strtol( value, NULL, 10 );
  5745.             if ( vote_clientNum == gameLocal.localClientNum ) {
  5746.                 gameLocal.ServerSendChatMessage( clientNum, "server", "#str_104257" );
  5747.                 common->DPrintf( "client %d: called kick for the server host\n", clientNum );
  5748.                 return;
  5749.             }
  5750.             ServerStartVote( clientNum, voteIndex, va( "%d", vote_clientNum ) );
  5751.             ClientStartVote( clientNum, va( common->GetLocalizedString( "#str_104302" ), vote_clientNum, gameLocal.userInfo[ vote_clientNum ].GetString( "ui_name" ) ) );
  5752.             break;
  5753.         }
  5754.         case VOTE_MAP: {
  5755. #ifdef _XENON
  5756.             // Xenon should not get here
  5757.             assert( 0 );
  5758. #else
  5759.             if ( idStr::FindText( gameLocal.serverInfo.GetString( "si_map" ), value ) != -1 ) {
  5760.  
  5761.                 // mekberg: localized string
  5762.                 const char* mapName = si_map.GetString();
  5763.                 const idDeclEntityDef *mapDef = static_cast<const idDeclEntityDef *>(declManager->FindType( DECL_MAPDEF, mapName, false ));
  5764.                 if ( mapDef ) {
  5765.                     mapName = common->GetLocalizedString( mapDef->dict.GetString( "name", mapName ) );
  5766.                 }
  5767.                 gameLocal.ServerSendChatMessage( clientNum, "server", va( common->GetLocalizedString( "#str_104295" ), mapName ) );
  5768.                 common->DPrintf( "client %d: already running the voted map: %s\n", clientNum, value );
  5769.                 return;
  5770.             }
  5771.             int                num = fileSystem->GetNumMaps();
  5772.             int                i;
  5773.             const idDict    *dict = NULL;
  5774.             bool            haveMap = false;
  5775.             for ( i = 0; i < num; i++ ) {
  5776.                 dict = fileSystem->GetMapDecl( i );
  5777.                 if( !dict ) {
  5778.                     gameLocal.Warning( "idMultiplayerGame::ServerCallVote() - bad map decl index on vote\n"    );
  5779.                     break;
  5780.                 }
  5781.                 if ( dict && !idStr::Icmp( dict->GetString( "path" ), value ) ) {
  5782.                     haveMap = true;
  5783.                     break;
  5784.                 }
  5785.             }
  5786.             if ( !haveMap ) {
  5787.                 gameLocal.ServerSendChatMessage( clientNum, "server", "#str_104296", value );
  5788.                 common->Printf( "client %d: map not found: %s\n", clientNum, value );
  5789.                 return;
  5790.             }
  5791.             ServerStartVote( clientNum, voteIndex, value );
  5792.             ClientStartVote( clientNum, va( common->GetLocalizedString( "#str_104256" ), dict ? dict->GetString( "name" ) : value ) );
  5793. #endif
  5794.             break;
  5795.         }
  5796.         case VOTE_MIN_PLAYERS: {
  5797.             vote_minPlayers = strtol( value, NULL, 10 );
  5798.             if ( vote_minPlayers == gameLocal.serverInfo.GetInt( "si_minPlayers" ) ) {
  5799.                 gameLocal.ServerSendChatMessage( clientNum, "server", "#str_107733" );
  5800.                 common->DPrintf( "client %d: already at the voted Min Players\n", clientNum );
  5801.                 return;                    
  5802.             }
  5803.             if ( vote_minPlayers < si_minPlayers.GetMinValue() || vote_minPlayers > si_minPlayers.GetMaxValue() ) {
  5804.                 gameLocal.ServerSendChatMessage( clientNum, "server", "#str_107734" );
  5805.                 common->DPrintf( "client %d: min players value out of range for vote: %s\n", clientNum, value );
  5806.                 return;
  5807.             }
  5808.             ServerStartVote( clientNum, voteIndex, value );
  5809.             ClientStartVote( clientNum, va( common->GetLocalizedString( "#str_107735" ), vote_minPlayers ) );
  5810.             break;
  5811.         }
  5812. // RAVEN BEGIN
  5813. // shouchard:  added capture limit, round limit, and autobalance
  5814.         case VOTE_CAPTURELIMIT: {
  5815.             vote_captureLimit = strtol( value, NULL, 10 );
  5816.             if ( vote_captureLimit == gameLocal.serverInfo.GetInt( "si_captureLimit" ) ) {
  5817.                 gameLocal.ServerSendChatMessage( clientNum, "server", "#str_104401" );
  5818.                 common->DPrintf( "client %d: already at the voted Capture Limit\n", clientNum );
  5819.                 return;                    
  5820.             }
  5821.             if ( vote_captureLimit < si_captureLimit.GetMinValue() || vote_captureLimit > si_fragLimit.GetMaxValue() ) {
  5822.                 gameLocal.ServerSendChatMessage( clientNum, "server", "#str_104402" );
  5823.                 common->DPrintf( "client %d: fraglimit value out of range for vote: %s\n", clientNum, value );
  5824.                 return;
  5825.             }
  5826.  
  5827.             ServerStartVote( clientNum, voteIndex, value );
  5828.             ClientStartVote( clientNum, "si_captureLimit" );
  5829.             break;
  5830.         }
  5831.         // round limit is for tourneys
  5832.         case VOTE_ROUNDLIMIT: {
  5833.             // need a CVar or something to change here
  5834.             break;
  5835.         }
  5836.         case VOTE_AUTOBALANCE: {
  5837.             vote_autobalance = (0 != strtol( value, NULL, 10 ) );
  5838.             if ( vote_autobalance == gameLocal.serverInfo.GetBool( "si_autobalance" ) ) {
  5839.                 gameLocal.ServerSendChatMessage( clientNum, "server", "#str_104403" );
  5840.                 common->DPrintf( "client %d: already at the voted balance teams\n", clientNum );
  5841.                 return;                    
  5842.             }
  5843.  
  5844.             ServerStartVote( clientNum, voteIndex, value );
  5845.             ClientStartVote( clientNum, "si_autobalance" );
  5846.             break;
  5847.         }
  5848. // RAVEN END
  5849.         default: {
  5850.             gameLocal.ServerSendChatMessage( clientNum, "server", "#str_104297", va( "%d", ( int )voteIndex ) );
  5851.             common->DPrintf( "client %d: unknown vote index %d\n", clientNum, voteIndex );
  5852.         }
  5853.     }
  5854. }
  5855.  
  5856.  
  5857. /*
  5858. ================
  5859. idMultiplayerGame::DisconnectClient
  5860. ================
  5861. */
  5862. void idMultiplayerGame::DisconnectClient( int clientNum ) {
  5863.     // gameLocal.entities[ clientNum ] could be null if server is shutting down
  5864.     if( gameLocal.entities[ clientNum ] ) {
  5865.         // only kill non-spectators
  5866.         if( !((idPlayer*)gameLocal.entities[ clientNum ])->spectating ) {
  5867.             static_cast<idPlayer *>( gameLocal.entities[ clientNum ] )->Kill( true, true );
  5868.         }
  5869.         statManager->ClientDisconnect( clientNum );
  5870.     }
  5871.  
  5872.     delete gameLocal.entities[ clientNum ];
  5873.  
  5874.     UpdatePlayerRanks();
  5875.     CheckAbortGame();
  5876. }
  5877.  
  5878. /*
  5879. ================
  5880. idMultiplayerGame::CheckAbortGame
  5881. ================
  5882. */
  5883. void idMultiplayerGame::CheckAbortGame( void ) {
  5884.     // only checks for aborts -> game review below
  5885.     if ( gameState->GetMPGameState() != COUNTDOWN && gameState->GetMPGameState() != GAMEON && gameState->GetMPGameState() != SUDDENDEATH ) {
  5886.         return;
  5887.     }
  5888.  
  5889.     // in tourney, if we don't have enough clients to play we need to cycle back to 
  5890.     // warmup to re-seed
  5891.     if( gameLocal.gameType == GAME_TOURNEY ) {
  5892.         if ( !EnoughClientsToPlay() ) {
  5893.             gameState->NewState( WARMUP );
  5894.         }
  5895.     } else {
  5896.         if ( !EnoughClientsToPlay() && TimeLimitHit() ) {
  5897.             gameState->NewState( GAMEREVIEW );
  5898.         }
  5899.     }
  5900. }
  5901.  
  5902. /*
  5903. ================
  5904. idMultiplayerGame::WantKilled
  5905. ================
  5906. */
  5907. void idMultiplayerGame::WantKilled( int clientNum ) {
  5908.     idEntity *ent = gameLocal.entities[ clientNum ];
  5909. // RAVEN BEGIN
  5910. // jnewquist: Use accessor for static class type 
  5911.     if ( ent && ent->IsType( idPlayer::GetClassType() ) ) {
  5912. // RAVEN END
  5913.         static_cast<idPlayer *>( ent )->Kill( false, false );
  5914.     }
  5915. }
  5916.  
  5917. /*
  5918. ================
  5919. idMultiplayerGame::ClearVote
  5920. ================
  5921. */
  5922. void idMultiplayerGame::ClearVote( int clientNum ) {
  5923.     int start = 0;
  5924.     int end = MAX_CLIENTS;
  5925.     
  5926.     if( clientNum != -1 ) {
  5927.         start = clientNum;
  5928.         end = clientNum + 1;
  5929.     }
  5930.     
  5931.     for ( int i = start; i < end; i++ ) {
  5932.         idPlayer *player = static_cast<idPlayer *>( gameLocal.entities[ i ] );
  5933.         if ( !player || !player->mphud ) {
  5934.             continue;
  5935.         }
  5936.     
  5937.         player->mphud->SetStateInt( "voteNotice", 0 );
  5938.         player->mphud->SetStateString( "voteInfo_1", "" );
  5939.         player->mphud->SetStateString( "voteInfo_2", "" );
  5940.         player->mphud->SetStateString( "voteInfo_3", "" );
  5941.         player->mphud->SetStateString( "voteInfo_4", "" );
  5942.         player->mphud->SetStateString( "voteInfo_5", "" );
  5943.         player->mphud->SetStateString( "voteInfo_6", "" );
  5944.         player->mphud->SetStateString( "voteInfo_7", "" );        
  5945.         player->mphud->StateChanged( gameLocal.time );
  5946.     }
  5947.     if ( mainGui ) {
  5948.         mainGui->SetStateInt( "vote_going", 0 );
  5949.         mainGui->StateChanged( gameLocal.time );
  5950.     }
  5951. }
  5952. /*
  5953. ================
  5954. idMultiplayerGame::MapRestart
  5955. ================
  5956. */
  5957. void idMultiplayerGame::MapRestart( void ) {
  5958.     int clientNum;
  5959.     // jshepard: clean up votes
  5960.     ClearVote();
  5961.  
  5962.     assert( !gameLocal.isClient );
  5963.     if ( gameState->GetMPGameState() != WARMUP ) {
  5964.         gameState->NewState( WARMUP );
  5965.         // force an immediate state detection/update, otherwise if we update our state this
  5966.         // same frame we'll miss transitions
  5967.         gameState->SendState();
  5968.  
  5969.         gameState->SetNextMPGameState( INACTIVE );
  5970.         gameState->SetNextMPGameStateTime( 0 );
  5971.         
  5972.     }
  5973.  
  5974.     // mekberg: moved this before the updateUI just in case these values weren't reset.
  5975.     for ( int i = 0; i < TEAM_MAX; i++ ) {
  5976.         teamScore[ i ] = 0;
  5977.     }
  5978.  
  5979.     // mekberg: Re-wrote this loop to always updateUI. Previously the player would be
  5980.     //            on a team but the UI wouldn't know about it
  5981.     // shouchard:  balance teams extended to CTF    
  5982.     for ( clientNum = 0; clientNum < gameLocal.numClients; clientNum++ ) {
  5983.         // jnewquist: Use accessor for static class type 
  5984.         if ( gameLocal.entities[ clientNum ] && gameLocal.entities[ clientNum ]->IsType( idPlayer::GetClassType() ) ) {
  5985.             // mekberg: clear wins only on map restart
  5986.             idPlayer *player = static_cast<idPlayer *>( gameLocal.entities[ clientNum ] );
  5987.             SetPlayerWin( player, 0 );
  5988.             
  5989.             // shouchard:  BalanceTDM->BalanceTeam
  5990.             /*if ( gameLocal.serverInfo.GetBool( "si_autoBalance" ) && gameLocal.IsTeamGame() )  {
  5991.                 player->BalanceTeam();
  5992.             }
  5993.  
  5994.             // core is in charge of syncing down userinfo changes
  5995.             // it will also call back game through SetUserInfo with the current info for update
  5996.             cmdSystem->BufferCommandText( CMD_EXEC_NOW, va( "updateUI %d\n", clientNum ) );*/
  5997.         }
  5998.     }
  5999. }
  6000.  
  6001. /*
  6002. ================
  6003. idMultiplayerGame::SwitchToTeam
  6004. ================
  6005. */
  6006. void idMultiplayerGame::SwitchToTeam( int clientNum, int oldteam, int newteam ) {
  6007.     assert( gameLocal.IsTeamGame() );
  6008.  
  6009.     assert( oldteam != newteam );
  6010.     assert( !gameLocal.isClient );
  6011.  
  6012.     if ( !gameLocal.isClient && newteam >= 0 && IsInGame( clientNum ) ) {
  6013.         PrintMessageEvent( -1, MSG_JOINTEAM, clientNum, newteam );
  6014.     }
  6015.     
  6016.     if ( oldteam != -1 ) {
  6017.         // kill and respawn
  6018.         idPlayer *p = static_cast<idPlayer *>( gameLocal.entities[ clientNum ] );
  6019.         if ( p->IsInTeleport() ) {
  6020.              p->ServerSendInstanceEvent( idPlayer::EVENT_ABORT_TELEPORTER, NULL, false, -1 );
  6021.             p->SetPrivateCameraView( NULL );
  6022.         }
  6023.         p->Kill( true, true );
  6024.         CheckAbortGame();
  6025.     }
  6026. }
  6027.  
  6028. /*
  6029. ================
  6030. idMultiplayerGame::JoinTeam
  6031. ================
  6032. */
  6033. void idMultiplayerGame::JoinTeam( const char* team ) {
  6034.     if( !idStr::Icmp( team, "auto" ) ) {
  6035.         int            teamCount[ TEAM_MAX ];
  6036.         idEntity    *ent;
  6037.         
  6038.         memset( teamCount, 0, sizeof( int ) * TEAM_MAX );
  6039.  
  6040.         for( int i = 0; i < gameLocal.numClients; i++ ) {
  6041.             ent = gameLocal.entities[ i ];
  6042.             if ( ent && ent->IsType( idPlayer::GetClassType() ) ) {
  6043.                 teamCount[ ((idPlayer*)ent)->team ]++;
  6044.             }
  6045.         }
  6046.  
  6047.         int minCount = idMath::INT_MAX;
  6048.         int minCountTeam = -1;
  6049.         for( int i = 0; i < TEAM_MAX; i++ ) {
  6050.             if( teamCount[ i ] < minCount ) {
  6051.                 minCount = teamCount[ i ];
  6052.                 minCountTeam = i;
  6053.             }
  6054.         }
  6055.  
  6056.         if( minCountTeam >= 0 && minCountTeam < TEAM_MAX ) {
  6057.             cvarSystem->SetCVarString( "ui_spectate", "Play" );
  6058.             cvarSystem->SetCVarString( "ui_team", teamNames[ minCountTeam ] );
  6059.         } else {
  6060.             cvarSystem->SetCVarString( "ui_spectate", "Play" );
  6061.             cvarSystem->SetCVarString( "ui_team", teamNames[ gameLocal.random.RandomInt( TEAM_MAX - 1 ) ] );
  6062.         }
  6063.     } else if( !idStr::Icmp( team, "spectator" ) ) {
  6064.         cvarSystem->SetCVarString( "ui_spectate", "Spectate" );
  6065.     } else {
  6066.         int i;
  6067.         for( i = 0; i < TEAM_MAX; i++ ) {
  6068.             if( !idStr::Icmp( team, teamNames[ i ] ) ) {
  6069.                 cvarSystem->SetCVarString( "ui_spectate", "Play" );
  6070.                 cvarSystem->SetCVarString( "ui_team", teamNames[ i ] );
  6071.                 break;
  6072.             }
  6073.         }
  6074.         if( i >= TEAM_MAX ) {
  6075.             gameLocal.Warning( "idMultiplayerGame::JoinTeam() - unknown team '%s'\n", team );
  6076.         }
  6077.     }
  6078. }
  6079.  
  6080. /*
  6081. ================
  6082. idMultiplayerGame::ProcessChatMessage
  6083. ================
  6084. */
  6085. void idMultiplayerGame::ProcessChatMessage( int clientNum, bool team, const char *name, const char *text, const char *sound ) {
  6086.     idBitMsg    outMsg;
  6087.     byte        msgBuf[ 256 ];
  6088.     const char *suffix = NULL;
  6089.     int            send_to; // 0 - all, 1 - specs, 2 - team
  6090.     int            i;
  6091.     idEntity     *ent;
  6092.     idPlayer    *p;
  6093.     idStr        suffixed_name;
  6094.     idStr        prefixed_text;
  6095.  
  6096.     assert( !gameLocal.isClient );
  6097.  
  6098.     if ( clientNum >= 0 ) {
  6099.         p = static_cast< idPlayer * >( gameLocal.entities[ clientNum ] );
  6100. // RAVEN BEGIN
  6101. // jnewquist: Use accessor for static class type 
  6102.         if ( !( p && p->IsType( idPlayer::GetClassType() ) ) ) {
  6103. // RAVEN END
  6104.             return;
  6105.         }
  6106.  
  6107.         if ( p->spectating && ( p->wantSpectate || gameLocal.gameType == GAME_TOURNEY ) ) {
  6108.             suffix = "spectating";
  6109.             if ( team || ( !g_spectatorChat.GetBool() && ( gameState->GetMPGameState() == GAMEON || gameState->GetMPGameState() == SUDDENDEATH ) ) ) {
  6110.                 // to specs
  6111.                 send_to = 1;
  6112.             } else {
  6113.                 // to all
  6114.                 send_to = 0;
  6115.             }
  6116.         } else if ( team ) {
  6117.             suffix = va( "%s%s", p->team ? S_COLOR_STROGG : S_COLOR_MARINE, p->team ? "Strogg^0" : "Marine^0" ); 
  6118.             // to team
  6119.             send_to = 2;
  6120.         } else {
  6121.             if( gameLocal.gameType == GAME_TOURNEY ) {
  6122.                 suffix = va( "Arena %d", (p->GetArena() + 1) );
  6123.             }
  6124.             // to all
  6125.             send_to = 0;
  6126.         }
  6127.     } else {
  6128.         p = NULL;
  6129.         send_to = 0;
  6130.     }
  6131.     // put the message together
  6132.     outMsg.Init( msgBuf, sizeof( msgBuf ) );
  6133.     outMsg.WriteByte( GAME_RELIABLE_MESSAGE_CHAT );
  6134.  
  6135.     if ( suffix ) {
  6136.         suffixed_name = va( "^0%s^0 (%s)", name, suffix );
  6137.     } else {
  6138.         suffixed_name = va( "^0%s^0", name );
  6139.     }
  6140.     if( p && send_to == 2 ) {
  6141.         prefixed_text = va( "%s%s", p->team ? S_COLOR_STROGG : S_COLOR_MARINE, common->GetLocalizedString( text ) );
  6142.     } else {
  6143.         prefixed_text = common->GetLocalizedString( text );
  6144.     }
  6145.  
  6146.     if( suffixed_name.Length() + prefixed_text.Length() >= 240 ) {
  6147.         gameLocal.Warning( "idMultiplayerGame::ProcessChatMessage() - Chat line too long\n" );
  6148.         return;
  6149.     }
  6150.  
  6151.     outMsg.WriteString( suffixed_name );
  6152.      outMsg.WriteString( prefixed_text );
  6153.      outMsg.WriteString( "" );
  6154.  
  6155.     for ( i = 0; i < gameLocal.numClients; i++ ) {
  6156.         ent = gameLocal.entities[ i ]; 
  6157.         if ( !ent || !ent->IsType( idPlayer::GetClassType() ) ) {
  6158.             continue;
  6159.         }
  6160.         idPlayer *to = static_cast< idPlayer * >( ent );
  6161.         switch( send_to ) {
  6162.             case 0:
  6163.                 if ( !p || !to->IsPlayerMuted( p ) ) {
  6164.                     if ( i == gameLocal.localClientNum ) {
  6165.                         AddChatLine( "%s^0: %s\n", suffixed_name.c_str(), prefixed_text.c_str() );
  6166.                     } else {
  6167.                         networkSystem->ServerSendReliableMessage( i, outMsg );
  6168.                     }
  6169.                 }
  6170.                 break;
  6171.  
  6172.             case 1:
  6173.                 if ( !p || ( to->spectating && !to->IsPlayerMuted( p ) ) ) {
  6174.                     if ( i == gameLocal.localClientNum ) {
  6175.                         AddChatLine( "%s^0: %s\n", suffixed_name.c_str(), prefixed_text.c_str() );
  6176.                     } else {
  6177.                         networkSystem->ServerSendReliableMessage( i, outMsg );
  6178.                     }
  6179.                 }
  6180.                 break;
  6181.  
  6182.             case 2:
  6183.                 if ( !p || ( to->team == p->team && !to->IsPlayerMuted( p ) ) ) {
  6184.                     if ( i == gameLocal.localClientNum ) {
  6185.                         AddChatLine( "%s^0: %s\n", suffixed_name.c_str(), prefixed_text.c_str() );
  6186.                     } else {
  6187.                         networkSystem->ServerSendReliableMessage( i, outMsg );
  6188.                     }
  6189.                 }
  6190.                 break;
  6191.         }
  6192.     }
  6193. }
  6194.  
  6195. /*
  6196. ================
  6197. idMultiplayerGame::Precache
  6198. ================
  6199. */
  6200. void idMultiplayerGame::Precache( void ) {
  6201.     int            i;
  6202.  
  6203.     if ( !gameLocal.isMultiplayer ) {
  6204.         return;
  6205.     }
  6206.     gameLocal.FindEntityDefDict( "player_marine", false );
  6207.     
  6208.     // MP game sounds
  6209.     for ( i = 0; i < AS_NUM_SOUNDS; i++ ) {
  6210.         declManager->FindSound( announcerSoundDefs[ i ], false );
  6211.     }
  6212.  
  6213.     // MP guis. just make sure we hit all of them
  6214.     i = 0;
  6215.     while ( MPGuis[ i ] ) {
  6216.         uiManager->FindGui( MPGuis[ i ], true );
  6217.         i++;
  6218.     }
  6219. }
  6220.  
  6221. /*
  6222. ================
  6223. idMultiplayerGame::ToggleSpectate
  6224. ================
  6225. */
  6226. void idMultiplayerGame::ToggleSpectate( void ) {
  6227.      bool spectating;
  6228.     assert( gameLocal.isClient || gameLocal.localClientNum == 0 );
  6229.  
  6230.      spectating = ( idStr::Icmp( cvarSystem->GetCVarString( "ui_spectate" ), "Spectate" ) == 0 );
  6231.      if ( spectating ) {
  6232.          // always allow toggling to play
  6233.          cvarSystem->SetCVarString( "ui_spectate", "Play" );
  6234.      } else {
  6235.          // only allow toggling to spectate if spectators are enabled.
  6236.          if ( gameLocal.serverInfo.GetBool( "si_spectators" ) ) {
  6237.              cvarSystem->SetCVarString( "ui_spectate", "Spectate" );
  6238.            } else {
  6239.              gameLocal.mpGame.AddChatLine( common->GetLocalizedString( "#str_106747" ) );
  6240.            }
  6241.        }
  6242. }
  6243.  
  6244. /*
  6245. ================
  6246. idMultiplayerGame::ToggleReady
  6247. ================
  6248. */
  6249. void idMultiplayerGame::ToggleReady( void ) {
  6250.     bool ready;
  6251.     assert( gameLocal.isClient || gameLocal.localClientNum == 0 );
  6252.  
  6253.     ready = ( idStr::Icmp( cvarSystem->GetCVarString( "ui_ready" ), "Ready" ) == 0 );
  6254.     if ( ready ) {
  6255.         cvarSystem->SetCVarString( "ui_ready", "Not Ready" );
  6256.     } else {
  6257.         cvarSystem->SetCVarString( "ui_ready", "Ready" );
  6258.     }
  6259. }
  6260.  
  6261. /*
  6262. ================
  6263. idMultiplayerGame::ToggleTeam
  6264. ================
  6265. */
  6266. void idMultiplayerGame::ToggleTeam( void ) {
  6267.     bool team;
  6268.     assert( gameLocal.isClient || gameLocal.localClientNum == 0 );
  6269.     
  6270.     // RAVEN BEGIN
  6271.     // ddynerman: new multiplayer teams
  6272.     team = ( idStr::Icmp( cvarSystem->GetCVarString( "ui_team" ), "Marine" ) == 0 );
  6273.     if ( team ) {
  6274.         cvarSystem->SetCVarString( "ui_team", "Strogg" );
  6275.     } else {
  6276.         cvarSystem->SetCVarString( "ui_team", "Marine" );
  6277.     }
  6278.     // RAVEN END
  6279. }
  6280.  
  6281. /*
  6282. ================
  6283. idMultiplayerGame::ToggleUserInfo
  6284. ================
  6285. */
  6286. void idMultiplayerGame::ThrottleUserInfo( void ) {
  6287.     int i;
  6288.  
  6289.     assert( gameLocal.localClientNum >= 0 );
  6290.  
  6291.     i = 0;
  6292.     while ( ThrottleVars[ i ] ) {
  6293.         if ( idStr::Icmp( gameLocal.userInfo[ gameLocal.localClientNum ].GetString( ThrottleVars[ i ] ),
  6294.             cvarSystem->GetCVarString( ThrottleVars[ i ] ) ) ) {
  6295.             if ( gameLocal.realClientTime < switchThrottle[ i ] ) {
  6296.                 AddChatLine( common->GetLocalizedString( "#str_104299" ), common->GetLocalizedString( ThrottleVarsInEnglish[ i ] ), ( switchThrottle[ i ] - gameLocal.time ) / 1000 + 1 );
  6297.                 cvarSystem->SetCVarString( ThrottleVars[ i ], gameLocal.userInfo[ gameLocal.localClientNum ].GetString( ThrottleVars[ i ] ) );
  6298.             } else {
  6299.                 switchThrottle[ i ] = gameLocal.time + ThrottleDelay[ i ] * 1000;
  6300.             }
  6301.         }
  6302.         i++;
  6303.     }
  6304. }
  6305.  
  6306. /*
  6307. ================
  6308. idMultiplayerGame::CanPlay
  6309. ================
  6310. */
  6311. bool idMultiplayerGame::CanPlay( idPlayer *p ) {
  6312.     return !p->wantSpectate && playerState[ p->entityNumber ].ingame;
  6313. }
  6314.  
  6315. /*
  6316. ================
  6317. idMultiplayerGame::EnterGame
  6318. ================
  6319. */
  6320. void idMultiplayerGame::EnterGame( int clientNum ) {
  6321.      assert( !gameLocal.isClient );
  6322.  
  6323.      if ( !playerState[ clientNum ].ingame ) {
  6324.          playerState[ clientNum ].ingame = true;
  6325.          if ( gameLocal.isMultiplayer ) {
  6326.              // can't use PrintMessageEvent as clients don't know the nickname yet
  6327.              //gameLocal.ServerSendChatMessage( -1, common->GetLocalizedString( "#str_102047" ), va( common->GetLocalizedString( "#str_107177" ), gameLocal.userInfo[ clientNum ].GetString( "ui_name" ) ) );
  6328.          }
  6329.      }
  6330. }
  6331.  
  6332. /*
  6333. ================
  6334. idMultiplayerGame::WantRespawn
  6335. ================
  6336. */
  6337. bool idMultiplayerGame::WantRespawn( idPlayer *p ) {
  6338.     return p->forceRespawn && !p->wantSpectate && playerState[ p->entityNumber ].ingame;
  6339. }
  6340.  
  6341. /*
  6342. ================
  6343. idMultiplayerGame::VoiceChat
  6344. ================
  6345. */
  6346. void idMultiplayerGame::VoiceChat_f( const idCmdArgs &args ) {
  6347.     gameLocal.mpGame.VoiceChat( args, false );
  6348. }
  6349.  
  6350. // RAVEN BEGIN
  6351. // mekberg: added
  6352. /*
  6353. ================
  6354. idMultiplayerGame::UpdateMPSettingsModel
  6355. ================
  6356. */
  6357. void idMultiplayerGame::UpdateMPSettingsModel( idUserInterface* currentGui ) {
  6358.     if ( !currentGui ) {
  6359.         return;
  6360.     }
  6361.  
  6362.     const char *model;
  6363.     idPlayer    *localP = gameLocal.GetLocalPlayer();
  6364.     if ( gameLocal.IsTeamGame() && localP && localP->team >= 0 && localP->team < TEAM_MAX ) {
  6365.         model = cvarSystem->GetCVarString( va( "ui_model_%s", teamNames[ localP->team ] ) );
  6366.     } else {
  6367.         model = cvarSystem->GetCVarString( "ui_model" );
  6368.     }
  6369.     const idDeclEntityDef *def = gameLocal.FindEntityDef( model, false );
  6370.     if ( def ) {
  6371.         currentGui->SetStateString( "player_model_name", def->dict.GetString( "model" ) );
  6372.         currentGui->SetStateString( "player_head_model_name", def->dict.GetString( "def_head" ) );
  6373.         currentGui->SetStateString( "player_skin_name", def->dict.GetString( "skin" ) );
  6374.         currentGui->SetStateBool( "need_update", true );
  6375.     }
  6376. }
  6377. // RAVEN END
  6378.  
  6379. /*
  6380. ================
  6381. idMultiplayerGame::VoiceChatTeam
  6382. ================
  6383. */
  6384. void idMultiplayerGame::VoiceChatTeam_f( const idCmdArgs &args ) {
  6385.     gameLocal.mpGame.VoiceChat( args, true );
  6386. }
  6387.  
  6388. /*
  6389. ================
  6390. idMultiplayerGame::VoiceChat
  6391. ================
  6392. */
  6393. void idMultiplayerGame::VoiceChat( const idCmdArgs &args, bool team ) {
  6394.     idBitMsg            outMsg;
  6395.     byte                msgBuf[128];
  6396.     const char            *voc;
  6397.     const idDict        *spawnArgs;
  6398.     const idKeyValue    *keyval;
  6399.     int                    index;
  6400.  
  6401.     if ( !gameLocal.isMultiplayer ) {
  6402.         common->Printf( "clientVoiceChat: only valid in multiplayer\n" );
  6403.         return;
  6404.     }
  6405.     if ( args.Argc() != 2 ) {
  6406.         common->Printf( "clientVoiceChat: bad args\n" );
  6407.         return;
  6408.     }
  6409.     // throttle
  6410.     if ( gameLocal.realClientTime < voiceChatThrottle ) {
  6411.         return;
  6412.     }
  6413.  
  6414.     voc = args.Argv( 1 );
  6415.     spawnArgs = gameLocal.FindEntityDefDict( "player_marine", false );
  6416.     keyval = spawnArgs->MatchPrefix( "snd_voc_", NULL );
  6417.     index = 0;
  6418.     while ( keyval ) {
  6419.         if ( !keyval->GetValue().Icmp( voc ) ) {
  6420.             break;
  6421.         }
  6422.         keyval = spawnArgs->MatchPrefix( "snd_voc_", keyval );
  6423.         index++;
  6424.     }
  6425.     if ( !keyval ) {
  6426.         common->Printf( "Voice command not found: %s\n", voc );
  6427.         return;
  6428.     }
  6429.     voiceChatThrottle = gameLocal.realClientTime + 1000;
  6430.  
  6431.     outMsg.Init( msgBuf, sizeof( msgBuf ) );
  6432.     outMsg.WriteByte( GAME_RELIABLE_MESSAGE_VCHAT );
  6433.     outMsg.WriteLong( index );
  6434.     outMsg.WriteBits( team ? 1 : 0, 1 );
  6435.     networkSystem->ClientSendReliableMessage( outMsg );
  6436. }
  6437.  
  6438. /*
  6439. ================
  6440. idMultiplayerGame::ProcessVoiceChat
  6441. ================
  6442. */
  6443. void idMultiplayerGame::ProcessVoiceChat( int clientNum, bool team, int index ) {
  6444.     const idDict        *spawnArgs;
  6445.     const idKeyValue    *keyval;
  6446.     idStr                name;
  6447.     idStr                snd_key;
  6448.     idStr                text_key;
  6449.     idPlayer            *p;
  6450.  
  6451.     p = static_cast< idPlayer * >( gameLocal.entities[ clientNum ] );
  6452. // RAVEN BEGIN
  6453. // jnewquist: Use accessor for static class type 
  6454.     if ( !( p && p->IsType( idPlayer::GetClassType() ) ) ) {
  6455. // RAVEN END
  6456.         return;
  6457.     }
  6458.  
  6459.     if ( p->spectating ) {
  6460.         return;
  6461.     }
  6462.  
  6463.     // lookup the sound def
  6464.     spawnArgs = gameLocal.FindEntityDefDict( "player_marine", false );
  6465.     keyval = spawnArgs->MatchPrefix( "snd_voc_", NULL );
  6466.     while ( index > 0 && keyval ) {
  6467.         keyval = spawnArgs->MatchPrefix( "snd_voc_", keyval );
  6468.         index--;
  6469.     }
  6470.     if ( !keyval ) {
  6471.         common->DPrintf( "ProcessVoiceChat: unknown chat index %d\n", index );
  6472.         return;
  6473.     }
  6474.     snd_key = keyval->GetKey();
  6475.     name = gameLocal.userInfo[ clientNum ].GetString( "ui_name" );
  6476.     sprintf( text_key, "txt_%s", snd_key.Right( snd_key.Length() - 4 ).c_str() );
  6477.     if ( team || gameState->GetMPGameState() == COUNTDOWN || gameState->GetMPGameState() == GAMEREVIEW ) {
  6478.         ProcessChatMessage( clientNum, team, name, spawnArgs->GetString( text_key ), spawnArgs->GetString( snd_key ) );
  6479.     } else {
  6480.         p->StartSound( snd_key, SND_CHANNEL_ANY, 0, true, NULL );
  6481.         ProcessChatMessage( clientNum, team, name, spawnArgs->GetString( text_key ), NULL );
  6482.     }
  6483. }
  6484.  
  6485. // RAVEN BEGIN
  6486. // shouchard:  added commands to mute/unmute voice chat
  6487. /*
  6488. ================
  6489. idMultiplayerGame::ClientVoiceMute
  6490. ================
  6491. */
  6492. void idMultiplayerGame::ClientVoiceMute( int muteClient, bool mute ) {
  6493.     // clients/listen server only
  6494.     assert( gameLocal.isListenServer || gameLocal.isClient );
  6495.  
  6496.     if ( NULL == gameLocal.GetLocalPlayer() ) {
  6497.         return;
  6498.     }
  6499.  
  6500.     if( !gameLocal.mpGame.IsInGame( muteClient ) ) {
  6501.         gameLocal.Warning( "idMultiplayerGame::ClientVoiceMute() - Invalid client '%d' specified\n", muteClient );
  6502.     }
  6503.  
  6504.     // do the mute/unmute
  6505.     gameLocal.GetLocalPlayer()->MutePlayer( muteClient, mute );
  6506.  
  6507.     // tell the server
  6508.     if( gameLocal.isClient ) {
  6509.         idBitMsg outMsg;
  6510.         byte msgBuf[128];
  6511.         outMsg.Init( msgBuf, sizeof( msgBuf ) );
  6512.         outMsg.WriteByte( GAME_RELIABLE_MESSAGE_VOICECHAT_MUTING );
  6513.         outMsg.WriteByte( muteClient );
  6514.         outMsg.WriteByte( mute ? 1 : 0 ); // 1 for mute, 0 for unmute
  6515.         networkSystem->ClientSendReliableMessage( outMsg );
  6516.     }
  6517.  
  6518.     // display some niceties
  6519.     common->Printf( "Player %s's has been %s.\n", gameLocal.GetUserInfo( muteClient )->GetString( "ui_name" ), mute ? "muted" : "unmuted" );
  6520. }
  6521.  
  6522. /*
  6523. ================
  6524. idMultiplayerGame::GetClientNumFromPlayerName
  6525. ================
  6526. */
  6527. int idMultiplayerGame::GetClientNumFromPlayerName( const char *playerName ) {
  6528.     if ( NULL == playerName || '\0' == *playerName ) {
  6529.         return -1;
  6530.     }
  6531.  
  6532.     int clientNum = -1;
  6533.  
  6534.     for ( int i = 0; i < gameLocal.numClients; i++ ) {
  6535.         if ( gameLocal.entities[ i ] && gameLocal.entities[ i ]->IsType( idPlayer::GetClassType() ) ) {
  6536.             if ( 0 == idStr::Icmp( gameLocal.userInfo[ i ].GetString( "ui_name" ), playerName ) ) {
  6537.                 clientNum = i;
  6538.                 break;
  6539.             }
  6540.         }
  6541.     }
  6542.  
  6543.     if ( -1 == clientNum ) {
  6544.         common->Warning( "idMultiplayerGame::GetClientNumFromPlayerName():  unknown player '%s'\n", playerName );
  6545.     }
  6546.  
  6547.     return clientNum;
  6548. }
  6549.  
  6550. /*
  6551. ================
  6552. idMultiplayerGame::ServerHandleVoiceMuting
  6553. ================
  6554. */
  6555. void idMultiplayerGame::ServerHandleVoiceMuting( int clientSrc, int clientDest, bool mute ) {
  6556.     assert( !gameLocal.isClient );
  6557.  
  6558.     idPlayer *playerSrc = gameLocal.GetClientByNum( clientSrc );
  6559.     idPlayer *playerDest = gameLocal.GetClientByNum( clientDest );
  6560.  
  6561.     if ( NULL == playerSrc ) {
  6562.         common->DPrintf( "idMultiplayerGame::ServerHandleVoiceMuting:  couldn't map client %d to a player\n", clientSrc );
  6563.         return;
  6564.     }
  6565.  
  6566.     if ( NULL == playerDest ) {
  6567.         common->DPrintf( "idMultiplayerGame::ServerHandleVoiceMuting:  couldn't map client %d to a player\n", clientDest );
  6568.         return;
  6569.     }
  6570.  
  6571.     if ( mute ) {
  6572.         playerSrc->MutePlayer( playerDest, true );
  6573.         common->DPrintf( "DEBUG:  client %s muted to client %s\n", 
  6574.             gameLocal.userInfo[ clientDest ].GetString( "ui_name" ),
  6575.             gameLocal.userInfo[ clientSrc ].GetString( "ui_name" ) );
  6576.     } else {
  6577.         playerSrc->MutePlayer( playerDest, false );
  6578.         common->DPrintf( "DEBUG:  client %s unmuted to client %s\n", 
  6579.             gameLocal.userInfo[ clientDest ].GetString( "ui_name" ),
  6580.             gameLocal.userInfo[ clientSrc ].GetString( "ui_name" ) );
  6581.     }
  6582. }
  6583.  
  6584.  
  6585. /*
  6586. ================
  6587. idMultiplayerGame::ClearAnnouncerSounds
  6588.  
  6589. This method deletes unplayed announcer sounds at the end of a game round.  
  6590. This fixes a bug where the round time warnings were being played from 
  6591. previous rounds.
  6592. ================
  6593. */
  6594. void idMultiplayerGame::ClearAnnouncerSounds() {
  6595.     announcerSoundNode_t* snd = NULL;    
  6596.     announcerSoundNode_t* nextSnd = NULL;    
  6597.     
  6598.     for ( snd = announcerSoundQueue.Next(); snd != NULL; snd = nextSnd ) {
  6599.         nextSnd = snd->announcerSoundNode.Next();
  6600.         snd->announcerSoundNode.Remove ( );
  6601.         delete snd;
  6602.     }
  6603. }
  6604.  
  6605. /*
  6606. ================
  6607. idMultiplayerGame::HandleServerAdminBanPlayer
  6608. ================
  6609. */    
  6610. void idMultiplayerGame::HandleServerAdminBanPlayer( int clientNum ) {
  6611.     if ( clientNum < 0 || clientNum >= gameLocal.numClients ) {
  6612.         common->DPrintf( "idMultiplayerGame::HandleServerAdminBanPlayer:  bad client num %d\n", clientNum );
  6613.         return;
  6614.     }
  6615.  
  6616.     if ( gameLocal.isServer    || gameLocal.isListenServer ) {
  6617.         if ( gameLocal.isListenServer && clientNum == gameLocal.localClientNum ) {
  6618.             common->DPrintf( "idMultiplayerGame::HandleServerAdminBanPlayer: Cannot ban the host!\n" );
  6619.             return;
  6620.         }
  6621.         cmdSystem->BufferCommandText( CMD_EXEC_NOW, va( "kick %i ban", clientNum ) );
  6622.     } else {
  6623.         if ( clientNum == gameLocal.localClientNum ) {
  6624.             common->DPrintf( "idMultiplayerGame::HandleServerAdminBanPlayer: Cannot ban yourserlf!\n" );
  6625.             return;
  6626.         }
  6627.         cmdSystem->BufferCommandText( CMD_EXEC_NOW, va( "rcon kick %i ban", clientNum ) );        
  6628.     }
  6629. }
  6630.  
  6631. /*
  6632. ================
  6633. idMultiplayerGame::HandleServerAdminRemoveBan
  6634. ================
  6635. */
  6636. void idMultiplayerGame::HandleServerAdminRemoveBan( const char * clientGuid ) {
  6637.     if ( NULL == clientGuid || !clientGuid[ 0 ]) {
  6638.         common->DPrintf( "idMultiplayerGame::HandleServerAdminRemoveBan:  bad guid!\n" );
  6639.         return;
  6640.     }
  6641.  
  6642.     if ( gameLocal.isServer || gameLocal.isListenServer ) {
  6643.         gameLocal.RemoveGuidFromBanList( clientGuid );
  6644.     } else {
  6645.         int clientNum = gameLocal.GetClientNumByGuid( clientGuid );
  6646.         cmdSystem->BufferCommandText( CMD_EXEC_NOW, va( "rcon removeClientFromBanList %d", clientNum ) ); 
  6647.     }
  6648. }
  6649.  
  6650. /*
  6651. ================
  6652. idMultiplayerGame::HandleServerAdminKickPlayer
  6653. ================
  6654. */
  6655. void idMultiplayerGame::HandleServerAdminKickPlayer( int clientNum ) {
  6656.     if ( clientNum < 0 || clientNum >= gameLocal.numClients ) {
  6657.         common->DPrintf( "idMultiplayerGame::HandleServerAdminKickPlayer:  bad client num %d\n", clientNum );
  6658.         return;
  6659.     }
  6660.  
  6661.     if ( gameLocal.isServer || gameLocal.isListenServer ) {
  6662.         cmdSystem->BufferCommandText( CMD_EXEC_NOW, va( "kick %i", clientNum ) );
  6663.     } else { 
  6664.         cmdSystem->BufferCommandText( CMD_EXEC_NOW, va( "rcon kick %i", clientNum ) );
  6665.     }
  6666. }
  6667.  
  6668. /*
  6669. ================
  6670. idMultiplayerGame::HandleServerAdminForceTeamSwitch
  6671. ================
  6672. */
  6673. void idMultiplayerGame::HandleServerAdminForceTeamSwitch( int clientNum ) {
  6674.     if ( !gameLocal.IsTeamGame() ) {
  6675.         return;
  6676.     }
  6677.  
  6678.     if ( clientNum < 0 || clientNum >= gameLocal.numClients ) {
  6679.         common->DPrintf( "idMultiplayerGame::HandleServerAdminForceTeamSwitch:  bad client num %d\n", clientNum );
  6680.         return;
  6681.     }
  6682.  
  6683.     if ( gameLocal.isServer || gameLocal.isListenServer ) {
  6684.         cmdSystem->BufferCommandText( CMD_EXEC_NOW, va( "forceTeamChange %d\n", clientNum));
  6685.  
  6686. /*        if ( gameLocal.entities[ clientNum ] && gameLocal.entities[ clientNum ]->IsType( idPlayer::GetClassType() ) )
  6687.         {
  6688.             idPlayer *player = static_cast< idPlayer *>( gameLocal.entities[ clientNum ] );
  6689.             player->GetUserInfo()->Set( "ui_team", player->team ? "Marine" : "Strogg" );
  6690.             cmdSystem->BufferCommandText( CMD_EXEC_NOW, va( "updateUI %d\n", clientNum ) );
  6691.         }*/
  6692.     } else {
  6693. /*        idBitMsg outMsg;
  6694.         byte msgBuf[ MAX_GAME_MESSAGE_SIZE ];
  6695.         outMsg.Init( msgBuf, sizeof( msgBuf ) );
  6696.         outMsg.WriteByte( GAME_RELIABLE_MESSAGE_SERVER_ADMIN );
  6697.         outMsg.WriteByte( SERVER_ADMIN_FORCE_SWITCH );
  6698.         outMsg.WriteByte( clientNum );
  6699.         networkSystem->ClientSendReliableMessage( outMsg ); */
  6700.  
  6701.         //jshepard: need to be able to do this via rcon
  6702.         cmdSystem->BufferCommandText( CMD_EXEC_NOW, va( "rcon forceTeamChange %d\n", clientNum));
  6703.  
  6704.     }
  6705. }
  6706.  
  6707. /*
  6708. ================
  6709. idMultiplayerGame::HandleServerAdminCommands
  6710. ================
  6711. */
  6712. bool idMultiplayerGame::HandleServerAdminCommands( serverAdminData_t &data ) {
  6713.     bool restartNeeded = false;
  6714.     bool nextMapNeeded = false;
  6715.     bool anyChanges = false;
  6716.     bool runPickMap = false;
  6717.     int nGameType = 0;
  6718.     idStr currentMap = si_map.GetString( );
  6719.  
  6720.     const char *szGameType = gameLocal.serverInfo.GetString( "si_gametype" );
  6721.     if ( 0 == idStr::Icmp( szGameType, "DM" ) ) {
  6722.         nGameType = GAME_DM;
  6723.     } else if ( 0 == idStr::Icmp( szGameType, "Team DM" ) ) {
  6724.         nGameType = GAME_TDM;
  6725.     } else if ( 0 == idStr::Icmp( szGameType, "CTF" ) ) {
  6726.         nGameType = GAME_CTF;
  6727.     } else if ( 0 == idStr::Icmp( szGameType, "Tourney" ) ) {
  6728.         nGameType = GAME_TOURNEY;
  6729.     } else if ( 0 == idStr::Icmp( szGameType, "Arena CTF" ) ) {
  6730.         nGameType = GAME_ARENA_CTF;
  6731.     } else {
  6732.         nGameType = GAME_SP;
  6733.     }
  6734.     if ( nGameType != data.gameType ) {
  6735.         
  6736.         switch ( data.gameType ) {
  6737.             case GAME_TDM:            szGameType = "Team DM";        break;
  6738.             case GAME_TOURNEY:        szGameType = "Tourney";        break;
  6739.             case GAME_CTF:            szGameType = "CTF";            runPickMap = true; break;
  6740.             case GAME_ARENA_CTF:    szGameType = "Arena CTF";    runPickMap = true; break;
  6741.  
  6742.             // mekberg: hack, if we had 1f ctf the gui index wouldn't be off =(
  6743.             case GAME_1F_CTF:        szGameType = "Arena CTF";    runPickMap = true; break;
  6744.             default:
  6745.             case GAME_DM:            szGameType = "DM";            break;
  6746.         }
  6747.  
  6748.         //we're going to reset the map here, so make sure to kill the active vote.
  6749.         ClientUpdateVote( VOTE_RESET, 0, 0, currentVoteData );
  6750.         vote = VOTE_NONE;
  6751.         restartNeeded = true;
  6752.         anyChanges = true;
  6753.  
  6754.         si_gameType.SetString( szGameType );
  6755.         if( runPickMap && gameLocal.isServer )    {
  6756.             //set the selected map to the admin data value, then make sure it can run the selected gametype.
  6757.             si_map.SetString( data.mapName.c_str() );
  6758.             if( PickMap( szGameType ) || idStr::Icmp( si_map.GetString( ), currentMap.c_str( ) ) )    {
  6759.                 nextMapNeeded = true;
  6760.                 restartNeeded = false;
  6761.                 data.mapName = idStr( si_map.GetString() );
  6762.                 data.restartMap = true;
  6763.             }
  6764.         }
  6765.     } 
  6766.  
  6767.     // Rcon these cvars if this isn't the server. We can trust the input from the gui that the
  6768.     // gametype and map always match.
  6769.     if ( !gameLocal.isServer ) {
  6770.         cmdSystem->BufferCommandText( CMD_EXEC_NOW, va( "rcon si_autoBalance %d",    data.autoBalance ) );
  6771.         cmdSystem->BufferCommandText( CMD_EXEC_NOW, va( "rcon si_captureLimit %d",    data.captureLimit ) );
  6772.         cmdSystem->BufferCommandText( CMD_EXEC_NOW, va( "rcon si_fragLimit %d",        data.fragLimit ) );
  6773.         cmdSystem->BufferCommandText( CMD_EXEC_NOW, va( "rcon si_gameType %s",        szGameType ) );
  6774.         cmdSystem->BufferCommandText( CMD_EXEC_NOW, va( "rcon si_map %s",            data.mapName.c_str() ) );
  6775.         cmdSystem->BufferCommandText( CMD_EXEC_NOW, va( "rcon si_tourneyLimit %d",    data.tourneyLimit ) );
  6776.         cmdSystem->BufferCommandText( CMD_EXEC_NOW, va( "rcon si_minPlayers %d",    data.minPlayers ) );
  6777.         cmdSystem->BufferCommandText( CMD_EXEC_NOW, va( "rcon si_timeLimit %d",        data.timeLimit ) );
  6778.         cmdSystem->BufferCommandText( CMD_EXEC_NOW, va( "rcon verifyServerSettings" ) );
  6779.         
  6780.         if( restartNeeded )    {
  6781.             cmdSystem->BufferCommandText( CMD_EXEC_NOW, "rcon serverMapRestart" );
  6782.         } else if( data.restartMap || nextMapNeeded || idStr::Icmp( gameLocal.serverInfo.GetString( "si_map" ), data.mapName.c_str() ) ) {
  6783.             cmdSystem->BufferCommandText( CMD_EXEC_NOW, "rcon nextMap" );
  6784.         }
  6785.         else
  6786.         {
  6787.             cmdSystem->BufferCommandText( CMD_EXEC_NOW, "rcon rescanSI" );
  6788.         }
  6789.         
  6790.         return true;
  6791.     }
  6792.  
  6793.     if ( data.restartMap ) {
  6794.         ClientUpdateVote( VOTE_RESET, 0, 0, currentVoteData );
  6795.         vote = VOTE_NONE;
  6796.         restartNeeded = true;
  6797.         anyChanges = true;
  6798.     }
  6799.  
  6800.     //this section won't be encountered if the gametype was changed. But that's ok.
  6801.     if ( data.mapName.c_str() && idStr::Icmp( data.mapName.c_str(), si_map.GetString() ) ) {
  6802.         ClientUpdateVote( VOTE_RESET, 0, 0, currentVoteData );
  6803.         vote = VOTE_NONE;
  6804.         si_map.SetString(data.mapName.c_str());
  6805.         nextMapNeeded = true;
  6806.         anyChanges = true;
  6807.     }
  6808.  
  6809.     if ( data.captureLimit != gameLocal.serverInfo.GetInt( "si_captureLimit" ) ) {
  6810.         si_captureLimit.SetInteger( data.captureLimit );
  6811.         anyChanges = true;
  6812.     }
  6813.     if ( data.fragLimit !=  gameLocal.serverInfo.GetInt( "si_fragLimit" ) ) {
  6814.         si_fragLimit.SetInteger( data.fragLimit );
  6815.         anyChanges = true;
  6816.     }
  6817.     if ( data.tourneyLimit != gameLocal.serverInfo.GetInt( "si_tourneyLimit" ) ) {
  6818.         anyChanges = true;
  6819.     }
  6820.     if ( data.timeLimit != gameLocal.serverInfo.GetInt( "si_timeLimit" ) ) {
  6821.         si_timeLimit.SetInteger( data.timeLimit );
  6822.         anyChanges = true;
  6823.     }
  6824.     if ( data.minPlayers != gameLocal.serverInfo.GetInt( "si_minPlayers" ) ) {
  6825.         si_minPlayers.SetInteger( data.minPlayers );
  6826.         anyChanges = true;
  6827.     }
  6828.     if ( data.autoBalance != gameLocal.serverInfo.GetBool( "si_autobalance" ) ) {
  6829.         si_autobalance.SetBool( data.autoBalance );
  6830.         anyChanges = true;
  6831.     }
  6832.  
  6833.     
  6834.     if ( nextMapNeeded )    {
  6835.         ClientUpdateVote( VOTE_RESET, 0, 0, currentVoteData );
  6836.         vote = VOTE_NONE;
  6837.         cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "nextMap" );
  6838.         return anyChanges;
  6839.     }
  6840.     else if ( gameLocal.NeedRestart() || restartNeeded ) {
  6841.         ClientUpdateVote( VOTE_RESET, 0, 0, currentVoteData );
  6842.         vote = VOTE_NONE;
  6843.         cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "serverMapRestart" );
  6844.     }
  6845.     else
  6846.     {
  6847.         cmdSystem->BufferCommandText( CMD_EXEC_NOW, "rescanSI" );
  6848.     }
  6849.     
  6850.     return anyChanges;
  6851. }
  6852.  
  6853.  
  6854. // RAVEN END
  6855.  
  6856. /*
  6857. ===============
  6858. idMultiplayerGame::ServerWriteStartState
  6859. ===============
  6860. */
  6861.  void idMultiplayerGame::ServerWriteStartState( int clientNum, idBitMsg &msg, bool withLocalClient ) {
  6862.     int            i;
  6863.     idEntity    *ent;
  6864.  
  6865.     // send the start time
  6866.     msg.WriteLong( matchStartedTime );
  6867.     // send the powerup states and the spectate states
  6868.     for( i = 0; i < gameLocal.numClients; i++ ) {
  6869.         ent = gameLocal.entities[ i ]; 
  6870. // RAVEN BEGIN
  6871. // jnewquist: Use accessor for static class type 
  6872.         if ( ( withLocalClient || i != clientNum ) && ent && ent->IsType( idPlayer::GetClassType() ) ) {
  6873. // RAVEN END
  6874.             msg.WriteShort( i );
  6875.             msg.WriteShort( static_cast< idPlayer * >( ent )->inventory.powerups );
  6876.             msg.WriteBits( ent->GetInstance(), ASYNC_PLAYER_INSTANCE_BITS );
  6877.             msg.WriteBits( static_cast< idPlayer * >( ent )->spectating, 1 );
  6878.         }
  6879.     }
  6880.     msg.WriteShort( MAX_CLIENTS );    
  6881. }
  6882.  
  6883. /*
  6884. ================
  6885. idMultiplayerGame::ServerWriteInitialReliableMessages
  6886. ================
  6887. */
  6888. void idMultiplayerGame::ServerWriteInitialReliableMessages( int clientNum ) {
  6889.     idBitMsg    outMsg;
  6890.     byte        msgBuf[ MAX_GAME_MESSAGE_SIZE ];
  6891.  
  6892.     outMsg.Init( msgBuf, sizeof( msgBuf ) );
  6893.     outMsg.BeginWriting();
  6894.     outMsg.WriteByte( GAME_RELIABLE_MESSAGE_STARTSTATE );
  6895.     ServerWriteStartState( clientNum, outMsg, false );
  6896.     networkSystem->ServerSendReliableMessage( clientNum, outMsg );
  6897.  
  6898.     // we send SI in connectResponse messages, but it may have been modified already
  6899.     outMsg.BeginWriting( );
  6900.      outMsg.WriteByte( GAME_RELIABLE_MESSAGE_SERVERINFO );
  6901.     outMsg.WriteDeltaDict( gameLocal.serverInfo, NULL );
  6902.     networkSystem->ServerSendReliableMessage( clientNum, outMsg );
  6903.  
  6904.     gameState->SendInitialState( clientNum );
  6905. }
  6906.  
  6907. /*
  6908. ================
  6909. idMultiplayerGame::ClientReadStartState
  6910. ================
  6911. */
  6912. void idMultiplayerGame::ClientReadStartState( const idBitMsg &msg ) {
  6913.     int i, client, powerup;
  6914.  
  6915.     assert( gameLocal.isClient );
  6916.  
  6917.     // read the state in preparation for reading snapshot updates
  6918.     matchStartedTime = msg.ReadLong( );
  6919.     while ( ( client = msg.ReadShort() ) != MAX_CLIENTS ) {
  6920. // RAVEN BEGIN
  6921. // jnewquist: Use accessor for static class type 
  6922.         assert( gameLocal.entities[ client ] && gameLocal.entities[ client ]->IsType( idPlayer::GetClassType() ) );
  6923. // RAVEN END
  6924.         powerup = msg.ReadShort();
  6925.  
  6926.         int instance = ( msg.ReadBits( ASYNC_PLAYER_INSTANCE_BITS ) );
  6927.         static_cast< idPlayer * >( gameLocal.entities[ client ] )->SetInstance( instance );
  6928.         bool spectate = ( msg.ReadBits( 1 ) != 0 );
  6929.         static_cast< idPlayer * >( gameLocal.entities[ client ] )->Spectate( spectate );
  6930.  
  6931.         // set powerups after we get instance information for this client
  6932.         for ( i = 0; i < POWERUP_MAX; i++ ) {
  6933.             if ( powerup & ( 1 << i ) ) {
  6934.                 static_cast< idPlayer * >( gameLocal.entities[ client ] )->GivePowerUp( i, 0 );
  6935.             }
  6936.         }
  6937.     }
  6938. }
  6939.  
  6940. const char* idMultiplayerGame::announcerSoundDefs[ AS_NUM_SOUNDS ] = {
  6941.     // General announcements
  6942.     "announce_general_one",                    // AS_GENERAL_ONE
  6943.     "announce_general_two",                    // AS_GENERAL_TWO
  6944.     "announce_general_three",                // AS_GENERAL_THREE
  6945.     "announce_general_you_win",                // AS_GENERAL_YOU_WIN
  6946.     "announce_general_you_lose",            // AS_GENERAL_YOU_LOSE
  6947.     "announce_general_fight",                // AS_GENERAL_FIGHT
  6948.     "announce_general_sudden_death",        // AS_GENERAL_SUDDEN_DEATH
  6949.     "announce_general_vote_failed",            // AS_GENERAL_VOTE_FAILED
  6950.     "announce_general_vote_passed",            // AS_GENERAL_VOTE_PASSED
  6951.     "announce_general_vote_now",            // AS_GENERAL_VOTE_NOW
  6952.     "announce_general_one_frag",            // AS_GENERAL_ONE_FRAG
  6953.     "announce_general_two_frags",            // AS_GENERAL_TWO_FRAGS
  6954.     "announce_general_three_frags",            // AS_GENERAL_THREE_FRAGS
  6955.     "announce_general_one_minute",            // AS_GENERAL_ONE_MINUTE
  6956.     "announce_general_five_minute",            // AS_GENERAL_FIVE_MINUTE
  6957.     "announce_general_prepare_to_fight",    // AS_GENERAL_PREPARE_TO_FIGHT
  6958.     "announce_general_quad_damage",            // AS_GENERAL_QUAD_DAMAGE
  6959.     "announce_general_regeneration",        // AS_GENERAL_REGENERATION
  6960.     "announce_general_haste",                // AS_GENERAL_HASTE
  6961.     "announce_general_invisibility",        // AS_GENERAL_INVISIBILITY
  6962.     // DM announcements
  6963.     "announce_dm_you_tied_lead",            // AS_DM_YOU_TIED_LEAD
  6964.     "announce_dm_you_have_taken_lead",        // AS_DM_YOU_HAVE_TAKEN_LEAD
  6965.     "announce_dm_you_lost_lead",            // AS_DM_YOU_LOST_LEAD
  6966.     // Team announcements
  6967.     "announce_team_enemy_score",            // AS_TEAM_ENEMY_SCORES
  6968.     "announce_team_you_score",                // AS_TEAM_YOU_SCORE
  6969.     "announce_team_teams_tied",                // AS_TEAM_TEAMS_TIED
  6970.     "announce_team_strogg_lead",            // AS_TEAM_STROGG_LEAD
  6971.     "announce_team_marines_lead",            // AS_TEAM_MARINES_LEAD
  6972.     "announce_team_join_marine",            // AS_TEAM_JOIN_MARINE
  6973.     "announce_team_join_strogg",            // AS_TEAM_JOIN_STROGG
  6974.     // CTF announcements
  6975.     "announce_ctf_you_have_flag",            // AS_CTF_YOU_HAVE_FLAG
  6976.     "announce_ctf_your_team_has_flag",        // AS_CTF_YOUR_TEAM_HAS_FLAG
  6977.     "announce_ctf_enemy_has_flag",            // AS_CTF_ENEMY_HAS_FLAG
  6978.     "announce_ctf_your_team_drops_flag",    // AS_CTF_YOUR_TEAM_DROPS_FLAG
  6979.     "announce_ctf_enemy_drops_flag",        // AS_CTF_ENEMY_DROPS_FLAG
  6980.     "announce_ctf_your_flag_returned",        // AS_CTF_YOUR_FLAG_RETURNED
  6981.     "announce_ctf_enemy_returns_flag",        // AS_CTF_ENEMY_RETURNS_FLAG
  6982.     // Tourney announcements
  6983.     "announce_tourney_advance",                // AS_TOURNEY_ADVANCE
  6984.     "announce_tourney_join_arena_one",        // AS_TOURNEY_JOIN_ARENA_ONE
  6985.     "announce_tourney_join_arena_two",        // AS_TOURNEY_JOIN_ARENA_TWO
  6986.     "announce_tourney_join_arena_three",    // AS_TOURNEY_JOIN_ARENA_THREE
  6987.     "announce_tourney_join_arena_four",        // AS_TOURNEY_JOIN_ARENA_FOUR
  6988.     "announce_tourney_join_arena_five",        // AS_TOURNEY_JOIN_ARENA_FIVE
  6989.     "announce_tourney_join_arena_six",        // AS_TOURNEY_JOIN_ARENA_SIX
  6990.     "announce_tourney_join_arena_seven",    // AS_TOURNEY_JOIN_ARENA_SEVEN
  6991.     "announce_tourney_join_arena_eight",    // AS_TOURNEY_JOIN_ARENA_EIGHT
  6992.     "announce_tourney_join_arena_waiting",    // AS_TOURNEY_JOIN_ARENA_WAITING
  6993.     "announce_tourney_done",                // AS_TOURNEY_DONE
  6994.     "announce_tourney_start",                // AS_TOURNEY_START
  6995.     "announce_tourney_eliminated",            // AS_TOURNEY_ELIMINATED
  6996.     "announce_tourney_won",                    // AS_TOURNEY_WON
  6997.     "announce_tourney_prelims",                // AS_TOURNEY_PRELIMS
  6998.     "announce_tourney_quarter_finals",        // AS_TOURNEY_QUARTER_FINALS
  6999.     "announce_tourney_semi_finals",            // AS_TOURNEY_SEMI_FINALS
  7000.     "announce_tourney_final_match"            // AS_TOURNEY_FINAL_MATCH
  7001. };
  7002.  
  7003. void idMultiplayerGame::ScheduleAnnouncerSound ( announcerSound_t sound, float time, int arena, bool allowOverride ) {
  7004.     if( !gameLocal.GetLocalPlayer() ) {
  7005.         return;
  7006.     }
  7007.  
  7008.     if ( time < gameLocal.time ) {
  7009.         return;
  7010.     }
  7011.  
  7012.     announcerSoundNode_t* newSound = new announcerSoundNode_t;
  7013.     newSound->soundShader = sound;
  7014.     newSound->time = time;
  7015.     newSound->announcerSoundNode.SetOwner ( newSound );
  7016.     newSound->arena = arena;
  7017.     newSound->allowOverride = allowOverride;
  7018.  
  7019.     announcerSoundNode_t* snd = NULL;
  7020.     for ( snd = announcerSoundQueue.Next(); snd != NULL; snd = snd->announcerSoundNode.Next() ) {
  7021.         if ( snd->time > newSound->time ) {
  7022.             newSound->announcerSoundNode.InsertBefore ( snd->announcerSoundNode );
  7023.             break;
  7024.         }
  7025.     }
  7026.     if ( snd == NULL ) {
  7027.          newSound->announcerSoundNode.AddToEnd ( announcerSoundQueue );
  7028.     }
  7029. }
  7030.  
  7031. void idMultiplayerGame::ScheduleTimeAnnouncements( void ) {
  7032.     if( gameLocal.gameType != GAME_TOURNEY ) {
  7033.         int timeLimit = gameLocal.serverInfo.GetInt( "si_timeLimit" );
  7034.         int endGameTime = matchStartedTime + ( timeLimit * 60000 );
  7035.  
  7036.         if( timeLimit > 5 ) {
  7037.             ScheduleAnnouncerSound( AS_GENERAL_FIVE_MINUTE, endGameTime - (5 * 60000) );
  7038.         }
  7039.         if( timeLimit > 1 ) {
  7040.             ScheduleAnnouncerSound( AS_GENERAL_ONE_MINUTE, endGameTime - (60000) );
  7041.         }
  7042.     }
  7043. }
  7044.  
  7045. void idMultiplayerGame::PlayAnnouncerSounds( void ) {
  7046.     announcerSoundNode_t* snd = NULL;    
  7047.     announcerSoundNode_t* nextSnd = NULL;    
  7048.  
  7049.     if( !gameLocal.GetLocalPlayer() ) {
  7050.         return;
  7051.     }
  7052.  
  7053.     // if we're done playing the last sound reset override status
  7054.     if( announcerPlayTime <= gameLocal.time ) {
  7055.         currentSoundOverride = false;
  7056.     }
  7057.  
  7058.     if ( announcerPlayTime > gameLocal.time && !currentSoundOverride ) {
  7059.         return;
  7060.     } 
  7061.  
  7062.     // in tourney only play sounds scheduled for your current arena
  7063.     if ( gameLocal.gameType == GAME_TOURNEY ) {
  7064.         // go through and find the first sound to play in our arena, delete any sounds
  7065.         // for other arenas we see along the way.
  7066.         for ( snd = announcerSoundQueue.Next(); snd != NULL; snd = nextSnd ) {
  7067.             nextSnd = snd->announcerSoundNode.Next();
  7068.  
  7069.             if( snd->time > gameLocal.time ) {
  7070.                 return;
  7071.             }
  7072.  
  7073.             if( snd->arena == -1 ) {
  7074.                 // all-arena sound
  7075.                 break;
  7076.             }
  7077.  
  7078.             if( snd->arena == gameLocal.GetLocalPlayer()->GetArena() ) {
  7079.                 if( snd->allowOverride && nextSnd && nextSnd->time <= gameLocal.time ) {
  7080.                     // this sound is OK with being over-ridden, 
  7081.                     // and the next sound is ready to play, so go ahead and look at the next sound
  7082.                     snd->announcerSoundNode.Remove ( );
  7083.                     delete snd;
  7084.  
  7085.                     continue;
  7086.                 } else {
  7087.                     break;
  7088.                 }
  7089.             }
  7090.  
  7091.             snd->announcerSoundNode.Remove ( );
  7092.             delete snd;
  7093.         }
  7094.     } else {
  7095.         snd = announcerSoundQueue.Next();
  7096.         if( snd && snd->time > gameLocal.time ) {
  7097.             return;
  7098.         }
  7099.     }
  7100.  
  7101.     // play the sound locally
  7102.     if ( snd && snd->soundShader < AS_NUM_SOUNDS ) {
  7103.         int length = 0;
  7104.  
  7105.         //don't play timelimit countdown announcements if game is already over
  7106.         mpGameState_t state = gameState->GetMPGameState();
  7107.         if ( state == GAMEREVIEW //game is over, in scoreboard
  7108.             && ( snd->soundShader == AS_GENERAL_ONE_MINUTE
  7109.                 || snd->soundShader == AS_GENERAL_FIVE_MINUTE ) ) {
  7110.             //ignore scheduled time limit warnings that haven't executed yet
  7111.             snd->announcerSoundNode.Remove();
  7112.             delete snd;
  7113.         } else {
  7114.             snd->announcerSoundNode.Remove();
  7115.  
  7116.             gameLocal.GetLocalPlayer()->StartSoundShader( declManager->FindSound( announcerSoundDefs[ snd->soundShader ], false ), SND_CHANNEL_MP_ANNOUNCER, 0, false, &length );
  7117.             currentSoundOverride = snd->allowOverride;
  7118.  
  7119.             delete snd;
  7120.         }
  7121.  
  7122.         // if sounds remain to be played, check again    
  7123.         announcerPlayTime = gameLocal.time + length;
  7124.     } 
  7125. }
  7126.  
  7127. void idMultiplayerGame::ClearTeamScores ( void ) {
  7128.     for ( int i = 0; i < TEAM_MAX; i++ ) {
  7129.         teamScore[ i ] = 0;
  7130.     }
  7131. }
  7132.  
  7133. void idMultiplayerGame::AddTeamScore ( int team, int amount ) {
  7134.     if ( team < 0 || team >= TEAM_MAX ) {
  7135.         return;
  7136.     }
  7137.  
  7138.     teamScore[ team ] += amount;
  7139. }
  7140.  
  7141. void idMultiplayerGame::AddPlayerScore( idPlayer* player, int amount ) {
  7142.     if( player == NULL ) {
  7143.         gameLocal.Warning( "idMultiplayerGame::AddPlayerScore() - NULL player specified" );
  7144.         return;
  7145.     }
  7146.  
  7147.     if( player->entityNumber < 0 || player->entityNumber >= MAX_CLIENTS ) {
  7148.         gameLocal.Warning( "idMultiplayerGame::AddPlayerScore() - Bad player entityNumber '%d'\n", player->entityNumber );
  7149.         return;
  7150.     }
  7151.  
  7152.     playerState[ player->entityNumber ].fragCount += amount;
  7153. }
  7154.  
  7155. void idMultiplayerGame::AddPlayerTeamScore( idPlayer* player, int amount ) {
  7156.     if( player == NULL ) {
  7157.         gameLocal.Warning( "idMultiplayerGame::AddPlayerTeamScore() - NULL player specified" );
  7158.         return;
  7159.     }
  7160.  
  7161.     if( player->entityNumber < 0 || player->entityNumber >= MAX_CLIENTS ) {
  7162.         gameLocal.Warning( "idMultiplayerGame::AddPlayerTeamScore() - Bad player entityNumber '%d'\n", player->entityNumber );
  7163.         return;
  7164.     }
  7165.  
  7166.     playerState[ player->entityNumber ].teamFragCount += amount;
  7167. }
  7168.  
  7169. void idMultiplayerGame::AddPlayerWin( idPlayer* player, int amount ) {
  7170.     if( player == NULL ) {
  7171.         gameLocal.Warning( "idMultiplayerGame::AddPlayerWin() - NULL player specified" );
  7172.         return;
  7173.     }
  7174.  
  7175.     if( player->entityNumber < 0 || player->entityNumber >= MAX_CLIENTS ) {
  7176.         gameLocal.Warning( "idMultiplayerGame::AddPlayerWin() - Bad player entityNumber '%d'\n", player->entityNumber );
  7177.         return;
  7178.     }
  7179.  
  7180.     playerState[ player->entityNumber ].wins += amount;
  7181. }
  7182.  
  7183. void idMultiplayerGame::SetPlayerScore( idPlayer* player, int value ) {
  7184.     if( player == NULL ) {
  7185.         gameLocal.Warning( "idMultiplayerGame::SetPlayerScore() - NULL player specified" );
  7186.         return;
  7187.     }
  7188.  
  7189.     if( player->entityNumber < 0 || player->entityNumber >= MAX_CLIENTS ) {
  7190.         gameLocal.Warning( "idMultiplayerGame::SetPlayerScore() - Bad player entityNumber '%d'\n", player->entityNumber );
  7191.         return;
  7192.     }
  7193.  
  7194.     playerState[ player->entityNumber ].fragCount = value;
  7195. }
  7196.  
  7197. void idMultiplayerGame::SetPlayerTeamScore( idPlayer* player, int value ) {
  7198.     if( player == NULL ) {
  7199.         gameLocal.Warning( "idMultiplayerGame::SetPlayerTeamScore() - NULL player specified" );
  7200.         return;
  7201.     }
  7202.  
  7203.     if( player->entityNumber < 0 || player->entityNumber >= MAX_CLIENTS ) {
  7204.         gameLocal.Warning( "idMultiplayerGame::SetPlayerTeamScore() - Bad player entityNumber '%d'\n", player->entityNumber );
  7205.         return;
  7206.     }
  7207.  
  7208.     playerState[ player->entityNumber ].teamFragCount = value;
  7209. }
  7210.  
  7211. void idMultiplayerGame::SetPlayerWin( idPlayer* player, int value ) {
  7212.     if( player == NULL ) {
  7213.         gameLocal.Warning( "idMultiplayerGame::SetPlayerWin() - NULL player specified" );
  7214.         return;
  7215.     }
  7216.  
  7217.     if( player->entityNumber < 0 || player->entityNumber >= MAX_CLIENTS ) {
  7218.         gameLocal.Warning( "idMultiplayerGame::SetPlayerWin() - Bad player entityNumber '%d'\n", player->entityNumber );
  7219.         return;
  7220.     }
  7221.  
  7222.     playerState[ player->entityNumber ].wins = value;
  7223. }
  7224.  
  7225. rvCTF_AssaultPoint* idMultiplayerGame::NextAP( int team ) {
  7226.     for( int i = 0; i < assaultPoints.Num(); i++ ) {
  7227.         if( assaultPoints[ (team ? (assaultPoints.Num() - 1 - i) : i) ]->GetOwner() == team ) {
  7228.             continue;
  7229.         }
  7230.         return assaultPoints[ (team ? (assaultPoints.Num() - 1 - i) : i) ];
  7231.     }
  7232.     return NULL;
  7233. }
  7234.  
  7235. void idMultiplayerGame::ClientSetInstance( const idBitMsg& msg ) {
  7236.     assert( gameLocal.GetLocalPlayer() );
  7237.     
  7238.     idPlayer* player = gameLocal.GetLocalPlayer();
  7239.  
  7240.     int instance = msg.ReadByte();
  7241.  
  7242.     gameLocal.GetInstance( 0 )->SetSpawnInstanceID( instance );
  7243.     // on the client, we delete all entities, 
  7244.     // the server will send over new ones
  7245.     gameLocal.InstanceClear( instance );
  7246.  
  7247.     player->SetArena( instance );
  7248.     player->SetInstance( instance );
  7249.  
  7250.     // spawn the instance entities
  7251.     gameLocal.GetInstance( 0 )->PopulateFromMessage( msg );
  7252.  
  7253.     // players in other instances might have been hidden, update them
  7254.     for( int i = 0; i < MAX_CLIENTS; i++ ) {
  7255.         idPlayer* p = (idPlayer*)gameLocal.entities[ i ];
  7256.         if( p ) {
  7257.             if( p->GetInstance() == instance ) {
  7258.                 p->ClientInstanceJoin();
  7259.             } else {
  7260.                 p->ClientInstanceLeave();
  7261.             }
  7262.         }
  7263.     }
  7264. }
  7265.  
  7266. void idMultiplayerGame::ServerSetInstance( int instance ) {
  7267.     for( int i = MAX_CLIENTS; i < MAX_GENTITIES; i++ ) {
  7268.         idEntity* ent = gameLocal.entities[ i ];
  7269.         if( ent ) {
  7270.             if( ent->GetInstance() != instance ) {
  7271.                 ent->InstanceLeave();
  7272.             } else {
  7273.                 ent->InstanceJoin();
  7274.             }
  7275.         }
  7276.     }
  7277. }
  7278.  
  7279. const char* idMultiplayerGame::GetLongGametypeName( const char* gametype ) {
  7280.     if( !idStr::Icmp( gametype, "Tourney" ) ) {
  7281.         return common->GetLocalizedString( "#str_107676" );
  7282.     } else if( !idStr::Icmp( gametype, "Team DM" ) ) {
  7283.         return common->GetLocalizedString( "#str_107677" );
  7284.     } else if( !idStr::Icmp( gametype, "CTF" ) ) {
  7285.         return common->GetLocalizedString( "#str_107678" );
  7286.     } else if( !idStr::Icmp( gametype, "DM" ) ) {
  7287.         return common->GetLocalizedString( "#str_107679" );
  7288.     } else if( !idStr::Icmp( gametype, "One Flag CTF" ) ) {
  7289.         return common->GetLocalizedString( "#str_107680" );
  7290.     } else if( !idStr::Icmp( gametype, "Arena CTF" ) ) {
  7291.         return common->GetLocalizedString( "#str_107681" );
  7292.     } else if( !idStr::Icmp( gametype, "Arena One Flag CTF" ) ) {
  7293.         return common->GetLocalizedString( "#str_107682" );
  7294.     }
  7295.  
  7296.     return "";
  7297. }
  7298.  
  7299. int    idMultiplayerGame::GameTypeToVote( const char *gameType ) {
  7300.     if ( 0 == idStr::Icmp( gameType, "DM" ) ) { 
  7301.         return VOTE_GAMETYPE_DM;
  7302.     } else if ( 0 == idStr::Icmp( gameType, "Tourney" ) ) {
  7303.         return VOTE_GAMETYPE_TOURNEY;
  7304.     } else if ( 0 == idStr::Icmp( gameType, "Team DM" ) ) {
  7305.         return VOTE_GAMETYPE_TDM;
  7306.     } else if ( 0 == idStr::Icmp( gameType, "CTF" ) ) {
  7307.         return VOTE_GAMETYPE_CTF;
  7308.     } else if ( 0 == idStr::Icmp( gameType, "Arena CTF" ) ) {
  7309.         return VOTE_GAMETYPE_ARENA_CTF;
  7310.     }
  7311.  
  7312.     return VOTE_GAMETYPE_DM;
  7313. }
  7314.  
  7315. int idMultiplayerGame::GetPlayerTime( idPlayer* player ) {
  7316.     return ( gameLocal.time - player->GetConnectTime() ) / 60000;
  7317. }
  7318.  
  7319. int idMultiplayerGame::GetTeamScore( idPlayer* player ) {
  7320.     return GetTeamScore( player->entityNumber );
  7321. }
  7322.  
  7323. int idMultiplayerGame::GetScore( idPlayer* player ) {
  7324.     return GetScore( player->entityNumber );
  7325. }
  7326.  
  7327. int idMultiplayerGame::GetWins( idPlayer* player ) {
  7328.     return GetWins( player->entityNumber );
  7329. }
  7330.  
  7331. void idMultiplayerGame::EnableDamage( bool enable ) {
  7332.     for( int i = 0; i < gameLocal.numClients; i++ ) {
  7333.         idPlayer* player = (idPlayer*)gameLocal.entities[ i ];
  7334.  
  7335.         if( player == NULL ) {
  7336.             continue;
  7337.         }
  7338.  
  7339.         player->fl.takedamage = enable;
  7340.     }
  7341. }
  7342.  
  7343. void idMultiplayerGame::ReceiveRemoteConsoleOutput( const char* output ) {
  7344.     if( mainGui ) {
  7345.         idStr newOutput( output );
  7346.  
  7347.         if( rconHistory.Length() + newOutput.Length() > RCON_HISTORY_SIZE ) {
  7348.             int removeLength = rconHistory.Find( '\n' );
  7349.             if( removeLength == -1 ) {
  7350.                 // nuke the whole string
  7351.                 rconHistory.Empty();
  7352.             } else {
  7353.                 while( (rconHistory.Length() - removeLength) + newOutput.Length() > RCON_HISTORY_SIZE ) {
  7354.                     removeLength = rconHistory.Find( '\n', removeLength + 1 );
  7355.                     if( removeLength == -1 ) {
  7356.                         rconHistory.Empty();
  7357.                         break;
  7358.                     }
  7359.                 }
  7360.             }
  7361.             rconHistory = rconHistory.Right( rconHistory.Length() - removeLength );
  7362.         }
  7363.  
  7364.  
  7365.         int consoleInputStart = newOutput.Find( "Console Input: " );
  7366.         if( consoleInputStart != -1 ) {
  7367.             idStr consoleInput = newOutput.Right( newOutput.Length() - consoleInputStart - 15 );
  7368.             newOutput = newOutput.Left( consoleInputStart );
  7369.             newOutput.StripTrailing( "\n" );
  7370.             consoleInput.StripTrailing( "\n" );
  7371.             mainGui->SetStateString( "admin_console_input", consoleInput.c_str() );
  7372.         } 
  7373.  
  7374.         if( newOutput.Length() ) {
  7375.             rconHistory.Append( newOutput );
  7376.             rconHistory.Append( '\n' );
  7377.         }
  7378.  
  7379.         mainGui->SetStateString( "admin_console_history", rconHistory.c_str() );
  7380.     }
  7381. }
  7382. void idMultiplayerGame::ShuffleTeams( void ) {
  7383.     int loosingTeam = teamScore[ TEAM_MARINE ] < teamScore[ TEAM_STROGG ] ? TEAM_MARINE : TEAM_STROGG;
  7384.     for( int i = 0; i < rankedPlayers.Num(); i++ ) {
  7385.         if( rankedPlayers[ i ].First()->team != loosingTeam ) {
  7386.             rankedPlayers[ i ].First()->GetUserInfo()->Set( "ui_team", teamNames[ loosingTeam ] );
  7387.             cmdSystem->BufferCommandText( CMD_EXEC_NOW, va( "updateUI %d\n", rankedPlayers[ i ].First()->entityNumber ) );
  7388.         }
  7389.     }
  7390.     loosingTeam = (loosingTeam == TEAM_MARINE ? TEAM_STROGG : TEAM_MARINE );
  7391. }
  7392.  
  7393.  
  7394. rvGameState* idMultiplayerGame::GetGameState( void ) { 
  7395.     return gameState; 
  7396. }
  7397.  
  7398. void idMultiplayerGame::SetGameType( void ) {
  7399.     if( gameState ) {
  7400.         delete gameState;
  7401.     }
  7402.     gameState = NULL;
  7403.  
  7404.     if ( ( idStr::Icmp( gameLocal.serverInfo.GetString( "si_gameType" ), "DM" ) == 0 ) ) {
  7405.         gameLocal.gameType = GAME_DM;
  7406.         gameState = new rvDMGameState();
  7407.     } else if ( ( idStr::Icmp( gameLocal.serverInfo.GetString( "si_gameType" ), "Tourney" ) == 0 ) ) {
  7408.         gameLocal.gameType = GAME_TOURNEY;
  7409.         gameState = new rvTourneyGameState();
  7410.     } else if ( ( idStr::Icmp( gameLocal.serverInfo.GetString( "si_gameType" ), "Team DM" ) == 0 ) ) {
  7411.         gameLocal.gameType = GAME_TDM;
  7412.         gameState = new rvTeamDMGameState();
  7413.     } else if ( ( idStr::Icmp( gameLocal.serverInfo.GetString( "si_gameType" ), "CTF" ) == 0 ) ) {
  7414.         gameLocal.gameType = GAME_CTF;
  7415.         gameState = new rvCTFGameState();
  7416.     } else if ( ( idStr::Icmp( gameLocal.serverInfo.GetString( "si_gameType" ), "One Flag CTF" ) == 0 ) ) {
  7417.         gameLocal.gameType = GAME_1F_CTF;
  7418.         gameState = new rvCTFGameState();
  7419.     } else if ( ( idStr::Icmp( gameLocal.serverInfo.GetString( "si_gameType" ), "Arena CTF" ) == 0 ) ) {
  7420.         gameLocal.gameType = GAME_ARENA_CTF;
  7421.         gameState = new rvCTFGameState();
  7422.     } else if ( ( idStr::Icmp( gameLocal.serverInfo.GetString( "si_gameType" ), "Arena One Flag CTF" ) == 0 ) ) {
  7423.         gameLocal.gameType = GAME_ARENA_1F_CTF;
  7424.         gameState = new rvCTFGameState();
  7425.     } else {
  7426.         gameLocal.Error( "idMultiplayerGame::SetGameType() - Unknown gametype '%s'\n", gameLocal.serverInfo.GetString( "si_gameType" ) );
  7427.     }
  7428.  
  7429.     // force entity filter to gametype name in multiplayer
  7430.     if( gameLocal.gameType != GAME_SP ) {
  7431.         gameLocal.serverInfo.Set( "si_entityFilter", gameLocal.serverInfo.GetString( "si_gameType" ) );
  7432.         // also set as a CVar for when serverinfo is rescanned
  7433.         cvarSystem->SetCVarString( "si_entityFilter", gameLocal.serverInfo.GetString( "si_gameType" ) );
  7434.     }
  7435. }
  7436.  
  7437. //asalmon: need to access total frags for a team and total score for a team
  7438. int idMultiplayerGame::GetTeamsTotalFrags( int i ) {
  7439.     if( i < 0 || i > TEAM_MAX ) {
  7440.         return 0;
  7441.     }
  7442.     int total = 0;
  7443.     for(int j=0; j <  GetNumRankedPlayers(); j++)
  7444.     {
  7445.         if(rankedPlayers[ j ].First()->team == i)
  7446.         {
  7447.             total += GetScore(rankedPlayers[ j ].First()->entityNumber);
  7448.         }
  7449.     }
  7450.  
  7451.     return total;
  7452.  
  7453. }
  7454.  
  7455. int idMultiplayerGame::GetTeamsTotalScore( int i ) {
  7456.     if( i < 0 || i > TEAM_MAX ) {
  7457.         return 0;
  7458.     }
  7459.     int total = 0; 
  7460.     for(int j=0; j <  GetNumRankedPlayers(); j++)
  7461.     {
  7462.         idPlayer foo;
  7463.         
  7464.         if(rankedPlayers[ j ].First()->team == i)
  7465.         {
  7466.             total += GetTeamScore(rankedPlayers[ j ].First()->entityNumber);
  7467.         }
  7468.     }
  7469.  
  7470.     return total;
  7471.  
  7472. }
  7473.  
  7474. /*
  7475. ===============
  7476. idMultiplayerGame::PickMap
  7477. ===============
  7478. */
  7479. bool idMultiplayerGame::PickMap( idStr gameType ) {
  7480.     
  7481.     idStrList maps;
  7482.     int miss = 0;
  7483.     const idDecl *mapDecl;
  7484.     const idDeclEntityDef *mapDef;
  7485.     int index = 0;
  7486.     int btype;
  7487.     const char* mapName;
  7488.  
  7489.     mapName = si_map.GetString();
  7490.  
  7491.     //if we didn't set up a gametype, grab the current game type.
  7492.     if( gameType.IsEmpty() )    {
  7493.         gameType = si_gameType.GetString();
  7494.     }
  7495.  
  7496.     //if we're playing a map of this gametype, don't change.
  7497.     mapDecl = declManager->FindType( DECL_MAPDEF, mapName, false );
  7498.     mapDef = static_cast<const idDeclEntityDef *>( mapDecl );
  7499.     if ( mapDef ) {
  7500.         btype = mapDef->dict.GetInt( gameType );
  7501.         if(btype)
  7502.         {                    
  7503.             cvarSystem->SetCVarString("si_map",mapName);
  7504.             si_map.SetString( mapName );
  7505.             return false;
  7506.             
  7507.         }
  7508.     }
  7509.  
  7510.  
  7511.     int i;
  7512.     idFileList *files;
  7513.     idStrList fileList;
  7514.     
  7515.     int count = 0;
  7516.  
  7517.     files = fileSystem->ListFiles( "maps/mp", ".map" );
  7518.     for ( i = 0; i < files->GetList().Num(); i++, count++ ) {
  7519.         fileList.AddUnique( va( "mp/%s", files->GetList()[i].c_str() ) );
  7520.     }
  7521.     fileSystem->FreeFileList( files );
  7522.  
  7523.     files = fileSystem->ListFiles( "maps/mp", ".mapc" );
  7524.     for ( i = 0; i < files->GetList().Num(); i++, count++ ) {
  7525.         idStr fixedExtension(files->GetList()[i]);
  7526.         fixedExtension.SetFileExtension("map");
  7527.         fileList.AddUnique( va( "mp/%s", fixedExtension.c_str() ) );
  7528.     }
  7529.  
  7530.     fileList.Sort();
  7531.  
  7532.     idStr name;
  7533.     idStr cycle;
  7534.  
  7535.     //Populate the map list
  7536.     for ( i = 0; i < fileList.Num(); i++) {
  7537.         //Add only MP maps.
  7538.         if(!idStr::FindText(fileList[i].c_str(), "mp/"))
  7539.         {
  7540.             maps.AddUnique(fileList[i].c_str());
  7541.         }
  7542.     }
  7543.     maps.Sort();
  7544.  
  7545.     if(maps.Num() > 0)
  7546.     {
  7547.         while(miss < 100)
  7548.         {
  7549.             index = gameLocal.random.RandomInt(maps.Num()-1);
  7550.             mapName = maps[index].c_str();
  7551.             
  7552.             mapDecl = declManager->FindType( DECL_MAPDEF, mapName, false );
  7553.             mapDef = static_cast<const idDeclEntityDef *>( mapDecl );
  7554.             if ( mapDef ) {
  7555.                 btype = mapDef->dict.GetInt( gameType );
  7556.                 if(btype)
  7557.                 {                    
  7558.                     cvarSystem->SetCVarString("si_map",mapName);
  7559.                     si_map.SetString( mapName );
  7560.                     return true;
  7561.                     
  7562.                 }
  7563.             }
  7564.             miss++;
  7565.         
  7566.         }
  7567.     
  7568.     }
  7569.  
  7570.     //something is wrong and there are no maps for this game type.  This should never happen.
  7571.     gameLocal.Warning( "No maps found for game type: %s.\n", gameType.c_str() );
  7572.     return false;
  7573. }
  7574.  
  7575. /*
  7576. ===============
  7577. idMultiplayerGame::GetPlayerRankText
  7578. ===============
  7579. */
  7580. char* idMultiplayerGame::GetPlayerRankText( int rank, bool tied, int score ) {
  7581.     char* placeString;
  7582.  
  7583.     if( rank == 0 ) {
  7584.         //"1st^0 place with"
  7585.         placeString = va( "%s%s %d", S_COLOR_BLUE, common->GetLocalizedString( "#str_107689" ), score );
  7586.     } else if( rank == 1 ) {
  7587.         //"2nd^0 place with"
  7588.         placeString = va( "%s%s %d", S_COLOR_RED, common->GetLocalizedString(  "#str_107690" ), score );
  7589.     } else if( rank == 2 ) {
  7590.         //"3rd^0 place with"
  7591.         placeString = va( "%s%s %d", S_COLOR_YELLOW, common->GetLocalizedString( "#str_107691" ), score );
  7592.     } else {
  7593.         //"th^0 place with"
  7594.         placeString = va( "%d%s %d", rank + 1, common->GetLocalizedString( "#str_107692" ), score );
  7595.     }
  7596.  
  7597.     if( tied ) {
  7598.         //Tied for
  7599.         return va( "%s %s", common->GetLocalizedString( "#str_107693" ), placeString );
  7600.     } else {
  7601.         return placeString;
  7602.     }
  7603. }
  7604.  
  7605. /*
  7606. ===============
  7607. idMultiplayerGame::GetPlayerRankText
  7608. ===============
  7609. */
  7610. char* idMultiplayerGame::GetPlayerRankText( idPlayer* player ) {
  7611.     if( player == NULL ) {
  7612.         return "";
  7613.     }
  7614.  
  7615.     bool tied = false;
  7616.     int rank = GetPlayerRank( player, tied );
  7617.     return GetPlayerRankText( rank, tied, GetScore( player ) );
  7618. }
  7619.  
  7620. /*
  7621. ===============
  7622. idMultiplayerGame::WriteClientNetworkInfo
  7623. ===============
  7624. */
  7625. void idMultiplayerGame::WriteClientNetworkInfo( idFile *file, int clientNum ) {
  7626.     idBitMsg    msg;
  7627.     byte        msgBuf[ MAX_GAME_MESSAGE_SIZE ];
  7628.     
  7629.     msg.Init( msgBuf, sizeof( msgBuf ) );
  7630.     msg.BeginWriting();
  7631.     ServerWriteStartState( clientNum, msg, true );
  7632.     file->WriteInt( msg.GetSize() );
  7633.     file->Write( msg.GetData(), msg.GetSize() );
  7634.  
  7635.     gameState->WriteClientNetworkInfo( file, clientNum );
  7636. }
  7637.  
  7638. /*
  7639. ===============
  7640. idMultiplayerGame::ReadClientNetworkInfo
  7641. ===============
  7642. */
  7643. void idMultiplayerGame::ReadClientNetworkInfo( idFile* file, int clientNum ) {
  7644.     idBitMsg    msg;
  7645.     byte        msgBuf[ MAX_GAME_MESSAGE_SIZE ];
  7646.     int            size;
  7647.  
  7648.     file->ReadInt( size );
  7649.     msg.Init( msgBuf, sizeof( msgBuf ) );
  7650.     msg.SetSize( size );
  7651.     file->Read( msg.GetData(), size );
  7652.     ClientReadStartState( msg );
  7653.  
  7654.     gameState->ReadClientNetworkInfo( file, clientNum );
  7655. }
  7656.