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

  1. //----------------------------------------------------------------
  2. // GameState.cpp
  3. //
  4. // Copyright 2002-2005 Raven Software
  5. //----------------------------------------------------------------
  6.  
  7. #include "../../idlib/precompiled.h"
  8. #pragma hdrstop
  9.  
  10. #include "GameState.h"
  11.  
  12. /*
  13. ===============================================================================
  14.  
  15. rvGameState
  16.  
  17. Game state info for deathmatch, team deathmatch
  18.  
  19. ===============================================================================
  20. */
  21.  
  22. /*
  23. ================
  24. rvGameState::rvGameState
  25. ================
  26. */
  27. rvGameState::rvGameState( bool allocPrevious ) {
  28.     Clear();
  29.  
  30.     if( allocPrevious ) {
  31.         previousGameState = new rvGameState( false );
  32.     } else {
  33.         previousGameState = NULL;
  34.     }
  35.     
  36.     trackPrevious = allocPrevious;
  37.  
  38.     type = GS_BASE;
  39. }
  40.  
  41. /*
  42. ================
  43. rvGameState::~rvGameState
  44. ================
  45. */
  46. rvGameState::~rvGameState( void ) {
  47.     Clear();
  48.     delete previousGameState;
  49.     previousGameState = NULL;
  50. }
  51.  
  52. /*
  53. ================
  54. rvGameState::Clear
  55. ================
  56. */
  57. void rvGameState::Clear( void ) {
  58.     currentState = INACTIVE;
  59.     nextState = INACTIVE;
  60.     nextStateTime = 0;
  61.     fragLimitTimeout = 0;
  62. #ifdef _XENON
  63.     lastStatUpdate = 0;
  64. #endif
  65. }
  66.  
  67. /*
  68. ================
  69. rvGameState::SendState
  70. ================
  71. */
  72. void rvGameState::SendState( int clientNum ) {
  73.     idBitMsg    outMsg;
  74.     byte        msgBuf[MAX_GAME_MESSAGE_SIZE];
  75.  
  76.     assert( gameLocal.isServer && trackPrevious );
  77.  
  78.     if( clientNum == -1 && (*this) == (*previousGameState) ) {
  79.         return;
  80.     }
  81.  
  82.     outMsg.Init( msgBuf, sizeof( msgBuf ) );
  83.     outMsg.WriteByte( GAME_RELIABLE_MESSAGE_GAMESTATE );
  84.  
  85.     WriteState( outMsg );
  86.  
  87.     networkSystem->ServerSendReliableMessage( clientNum, outMsg );
  88.     
  89.     // don't update the state if we are working for a single client
  90.     if ( clientNum == -1 ) {
  91.         outMsg.ReadByte(); // pop off the msg ID
  92.         ReceiveState( outMsg );
  93.     }
  94. }
  95.  
  96. /*
  97. ===============
  98. rvGameState::WriteState
  99. ===============
  100. */
  101. void rvGameState::WriteState( idBitMsg &msg ) {
  102.     PackState( msg );
  103. }
  104.  
  105. /*
  106. ================
  107. rvGameState::SendInitialState
  108. ================
  109. */
  110. void rvGameState::SendInitialState( int clientNum ) {
  111.     rvGameState* previousState = previousGameState;
  112.  
  113.     rvGameState invalidState;
  114.  
  115.     previousGameState = &invalidState;
  116.  
  117.     SendState( clientNum );
  118.  
  119.     previousGameState = previousState;
  120. }
  121.  
  122. /*
  123. ================
  124. rvGameState::ReceiveState
  125. ================
  126. */
  127. void rvGameState::ReceiveState( const idBitMsg& msg ) {
  128.     UnpackState( msg );
  129.  
  130.     if ( gameLocal.localClientNum >= 0 ) {
  131.         GameStateChanged();
  132.     }
  133.  
  134.     (*previousGameState) = (*this);
  135. }
  136.  
  137. /*
  138. ================
  139. rvGameState::PackState
  140. ================
  141. */
  142. void rvGameState::PackState( idBitMsg& outMsg ) {
  143.     // for now, we only transmit 3 bytes.  If we need to sync more data, we should
  144.     // only transmit the diff
  145.     outMsg.WriteByte( currentState );
  146.     outMsg.WriteByte( nextState );
  147.     outMsg.WriteLong( nextStateTime );
  148. }
  149.  
  150. /*
  151. ================
  152. rvGameState::UnpackState
  153. ================
  154. */
  155. void rvGameState::UnpackState( const idBitMsg& inMsg ) {
  156.     currentState = (mpGameState_t)inMsg.ReadByte();
  157.     nextState = (mpGameState_t)inMsg.ReadByte();
  158.     nextStateTime = inMsg.ReadLong();
  159. }
  160.  
  161. /*
  162. ================
  163. rvGameState::GameStateChanged
  164. ================
  165. */
  166. void rvGameState::GameStateChanged( void ) {
  167.     idPlayer* player = gameLocal.GetLocalPlayer();
  168.  
  169.     if( player == NULL ) {
  170.         gameLocal.Warning( "rvGameState::GameStateChanged() - NULL local player\n" ) ;
  171.         return;
  172.     }
  173.  
  174.     // Check for a currentState change
  175.     if( currentState != previousGameState->currentState ) {
  176.         if( currentState == WARMUP ) {
  177.             if( gameLocal.gameType != GAME_TOURNEY ) {
  178.                 player->GUIMainNotice( common->GetLocalizedString( "#str_107706" ), true );        
  179.             }
  180. #ifdef _XENON
  181.             // on Xenon Hide the hud until the warmup period ends.
  182.             player->disableHud = true;
  183. #endif
  184.             soundSystem->SetActiveSoundWorld(true);
  185.             
  186.             // reset stats on the client-side
  187.             if( gameLocal.isClient ) {
  188.                 statManager->Init();
  189.             }
  190.         } else if( currentState == COUNTDOWN ) {
  191.             if( gameLocal.gameType != GAME_TOURNEY ) {
  192.                 player->GUIMainNotice( common->GetLocalizedString( "#str_107706" ), true );        
  193.             }
  194. #ifdef _XENON
  195.             // on Xenon remove the pre game lobby when the countdown starts
  196.             session->SetGUI(NULL, NULL);
  197.             player->disableHud = false;
  198.             gameLocal.mpGame.ClearMenu();
  199. #endif
  200.             soundSystem->SetActiveSoundWorld(true);
  201.             if( gameLocal.gameType != GAME_TOURNEY && previousGameState->currentState != INACTIVE ) {
  202.                 gameLocal.mpGame.ScheduleAnnouncerSound( AS_GENERAL_PREPARE_TO_FIGHT, gameLocal.time );
  203.                 gameLocal.mpGame.ScheduleAnnouncerSound( AS_GENERAL_THREE, nextStateTime - 3000 );
  204.                 gameLocal.mpGame.ScheduleAnnouncerSound( AS_GENERAL_TWO, nextStateTime - 2000 );
  205.                 gameLocal.mpGame.ScheduleAnnouncerSound( AS_GENERAL_ONE, nextStateTime - 1000 );
  206.             }
  207.         } else if( currentState == GAMEON ) {
  208.             if ( !player->vsMsgState ) {
  209.                 player->GUIMainNotice( "" );
  210.                 player->GUIFragNotice( "" );
  211.             } else {
  212.                 player->vsMsgState = false;
  213.             }
  214.             if( gameLocal.gameType != GAME_TOURNEY ) {
  215.                 gameLocal.mpGame.ScheduleAnnouncerSound( AS_GENERAL_FIGHT, gameLocal.time );
  216.             }
  217.             cvarSystem->SetCVarString( "ui_ready", "Not Ready" );
  218. #ifdef _XENON
  219.             // Kill the loading gui if it's up
  220.             session->KillLoadingGUI();
  221. #endif
  222.             gameLocal.mpGame.ScheduleTimeAnnouncements( );
  223.  
  224.             // clear stats on client
  225.             if( gameLocal.isClient ) {
  226.                 statManager->Init();
  227.             }
  228.          } else if( currentState == SUDDENDEATH ) {
  229.             gameLocal.mpGame.ScheduleAnnouncerSound( AS_GENERAL_SUDDEN_DEATH, gameLocal.time );
  230.             gameLocal.GetLocalPlayer()->GUIMainNotice( common->GetLocalizedString( "#str_104287" ) );
  231.         } else if( currentState == GAMEREVIEW ) {
  232.             // the game goes into (GAMEREVIEW->INACTIVE) before going into (GAMEREVIEW->WARMUP), so
  233.             // check nextState to make sure this only gets called once
  234.             //if( nextState == INACTIVE ) {
  235. #ifdef _XENON
  236.                 Live()->PlayedRound();
  237.                 if(Live()->RoundsPlayed() < cvarSystem->GetCVarInteger("si_matchRounds"))
  238.                 {
  239.                     gameLocal.mpGame.ShowStatSummary();
  240.                 }
  241. #else
  242.                 gameLocal.mpGame.ShowStatSummary();
  243. #endif
  244.                 if( gameLocal.IsTeamGame() ) {
  245.                     int winningTeam = gameLocal.mpGame.GetScoreForTeam( TEAM_MARINE ) > gameLocal.mpGame.GetScoreForTeam( TEAM_STROGG ) ? TEAM_MARINE : TEAM_STROGG;
  246.                     if( player->team == winningTeam ) {
  247.                         gameLocal.mpGame.ScheduleAnnouncerSound( AS_GENERAL_YOU_WIN, gameLocal.time );
  248.                     } else {
  249.                         gameLocal.mpGame.ScheduleAnnouncerSound( AS_GENERAL_YOU_LOSE, gameLocal.time );
  250.                     }
  251.                 } else if( gameLocal.gameType != GAME_TOURNEY ) {
  252.                     if( player->GetRank() == 0 ) {
  253.                         gameLocal.mpGame.ScheduleAnnouncerSound( AS_GENERAL_YOU_WIN, gameLocal.time );
  254.                     } else {
  255.                         gameLocal.mpGame.ScheduleAnnouncerSound( AS_GENERAL_YOU_LOSE, gameLocal.time );
  256.                     }
  257.                 }
  258.  
  259.             //}
  260.         }
  261.     }
  262. }
  263.  
  264. /*
  265. ================
  266. rvGameState::Run
  267. ================
  268. */
  269. void rvGameState::Run( void ) {
  270.     if ( currentState == INACTIVE ) {
  271.  
  272. #ifdef _XENON
  273.         if(Live()->RoundsPlayed() < cvarSystem->GetCVarInteger("si_matchRounds"))
  274. #endif
  275.         {
  276.             NewState( WARMUP );
  277.         }
  278.     }
  279.  
  280.     if ( nextState != INACTIVE && gameLocal.time > nextStateTime ) {
  281.         NewState( nextState );
  282.         nextState = INACTIVE;
  283.     }
  284.  
  285.     switch( currentState ) {
  286.         case INACTIVE:
  287. #ifdef _XENON
  288.             if(Live()->RoundsPlayed() >= cvarSystem->GetCVarInteger("si_matchRounds") && gameLocal.time > nextStateTime)
  289.             {    
  290.                 Live()->EndMatch();
  291.                 cmdSystem->BufferCommandText( CMD_EXEC_NOW, "disconnect\n" );
  292.             }
  293. #endif
  294.             break;
  295.  
  296.         case GAMEREVIEW: {
  297.             if ( nextState == INACTIVE ) {
  298.                 // asalmon: Xenon only plays one match and then exists
  299.                 statManager->SendAllStats();
  300. #ifndef _XENON
  301.                 nextState = NEXTGAME;
  302.                 // allow a little extra time in tourney since we have to display end brackets
  303.                 if( gameLocal.gameType == GAME_TOURNEY ) {
  304.                     nextStateTime = gameLocal.time + 5000 + (1000 * cvarSystem->GetCVarInteger( "g_gameReviewPause" ));
  305.                 } else {
  306.                     nextStateTime = gameLocal.time + (1000 * cvarSystem->GetCVarInteger( "g_gameReviewPause" ));
  307.                 }
  308.                 
  309. #else
  310.                 //get in one last stat update before shutdown
  311.                 //statManager->SendAllStats();
  312.                 if(Live()->RoundsPlayed() >= cvarSystem->GetCVarInteger("si_matchRounds"))
  313.                 {
  314.  
  315.                     NewState(INACTIVE);
  316.                     Live()->WriteSessionStats();
  317.                     nextStateTime = gameLocal.time + 1000;
  318.                     //cmdSystem->BufferCommandText( CMD_EXEC_NOW, "disconnect\n" );
  319.                     
  320.                 }
  321.                 else
  322.                 {
  323.                     nextState = NEXTGAME;
  324.                     nextStateTime = gameLocal.time + 1000 * cvarSystem->GetCVarInteger( "g_gameReviewPause" );
  325.                     Live()->PickMap();
  326.                 }
  327. #endif
  328.             }
  329.             break;
  330.         }
  331.         case NEXTGAME: {
  332.             if ( nextState == INACTIVE ) {
  333.                 // game rotation, new map, gametype etc.
  334.                 // only cycle in tourney if tourneylimit is higher than the specified value
  335.                 if( gameLocal.gameType != GAME_TOURNEY || ((rvTourneyGameState*)this)->GetTourneyCount() >= gameLocal.serverInfo.GetInt( "si_tourneyLimit" ) ) {
  336.                     // whether we switch to the next map or not, reset the tourney count
  337.                     if( gameLocal.gameType == GAME_TOURNEY ) {
  338.                         ((rvTourneyGameState*)this)->SetTourneyCount( 0 );
  339.                     }
  340.  
  341.                     if ( gameLocal.NextMap() ) {
  342.                         cmdSystem->BufferCommandText( CMD_EXEC_NOW, "serverMapRestart\n" );
  343.                         return;
  344.                     }
  345.                 }
  346.  
  347.                 NewState( WARMUP );
  348.  
  349.                 // put everyone back in from endgame spectate
  350.                 for ( int i = 0; i < gameLocal.numClients; i++ ) {
  351.                     idEntity *ent = gameLocal.entities[ i ];
  352.                     if ( ent && ent->IsType( idPlayer::GetClassType() ) ) {
  353.                         if ( !static_cast< idPlayer * >( ent )->wantSpectate ) {
  354.                             gameLocal.mpGame.CheckRespawns( static_cast<idPlayer *>( ent ) );
  355.                         }
  356.                     }
  357.                 }
  358.             }
  359.             break;
  360.         }
  361.         case WARMUP: {
  362.             // check to see if we actually want to do a warmup, or if we fall through
  363.  
  364. //RAVEN BEGIN
  365. //asalmon: Live has its own rules for ending warm up
  366. #ifdef _XENON
  367.             if(!Live()->RollCall())
  368.             {
  369.                 break;
  370.             }
  371.             session->KillLoadingGUI();
  372. #endif
  373. //RAVEN END
  374.             if( !gameLocal.serverInfo.GetBool( "si_warmup" ) && gameLocal.gameType != GAME_TOURNEY ) {
  375.                 // tourney always needs a warmup, to ensure that at least 2 players get seeded for the tournament.
  376.                 NewState( GAMEON );
  377.             } else if ( gameLocal.mpGame.AllPlayersReady() ) {            
  378.                 NewState( COUNTDOWN );
  379.                 nextState = GAMEON;
  380.                 nextStateTime = gameLocal.time + 1000 * gameLocal.serverInfo.GetInt( "si_countDown" );
  381.             }
  382.             break;
  383.         }
  384.         case COUNTDOWN: {
  385.             break;
  386.         }
  387.     }
  388.  
  389.     //RAVEN BEGIN
  390.     //asalmon: Xenon must send periodic updates of all multiplayer stats.
  391. #ifdef _XENON
  392.     if((Sys_Milliseconds() - lastStatUpdate) > XENON_STAT_UPDATE_INTERVAL)
  393.     {
  394.         statManager->SendAllStats();    
  395.         lastStatUpdate = Sys_Milliseconds();
  396.     }
  397. #endif
  398.     //RAVEN END
  399. }
  400.  
  401. /*
  402. ================
  403. rvGameState::NewState
  404. ================
  405. */
  406. void rvGameState::NewState( mpGameState_t newState ) {
  407.     idBitMsg    outMsg;
  408.     byte        msgBuf[MAX_GAME_MESSAGE_SIZE];
  409.     int            i;
  410.  
  411.     assert( (newState != currentState) && gameLocal.isServer );
  412.     
  413.     switch( newState ) {
  414.         case WARMUP: {
  415.             //    asalmon: start the stat manager as soon as the game starts
  416.             statManager->Init();
  417.             statManager->BeginGame();
  418.  
  419.             // if shuffle is on, shuffle the teams around
  420.             if( gameLocal.IsTeamGame() && gameLocal.serverInfo.GetBool( "si_shuffle" ) ) {
  421.                 gameLocal.mpGame.ShuffleTeams();
  422.             }
  423.  
  424.             // allow damage in warmup
  425.             //gameLocal.mpGame.EnableDamage( false );
  426.  
  427.             //asalmon: clear out lingering team scores.
  428.             gameLocal.mpGame.ClearTeamScores();
  429.  
  430.             if( gameLocal.gameType != GAME_TOURNEY ) {
  431.                 for( i = 0; i < gameLocal.numClients; i++ ) {
  432.                     idEntity *ent = gameLocal.entities[ i ];
  433.                     if ( !ent || !ent->IsType( idPlayer::GetClassType() ) ) {
  434.                         continue;
  435.                     }
  436.                     ((idPlayer*)ent)->JoinInstance( 0 );
  437.                 }
  438.             }
  439.  
  440.             break;
  441.         }
  442.         case GAMEON: {
  443.             // allow damage in warmup
  444.             //gameLocal.mpGame.EnableDamage( true );
  445.             gameLocal.LocalMapRestart();
  446.  
  447.             // asalmon: reset the stats when the warmup period is over
  448.             statManager->Init();
  449.             statManager->BeginGame();
  450.  
  451.             outMsg.Init( msgBuf, sizeof( msgBuf ) );
  452.             outMsg.WriteByte( GAME_RELIABLE_MESSAGE_RESTART );
  453.             outMsg.WriteBits( 0, 1 );
  454.             networkSystem->ServerSendReliableMessage( -1, outMsg );
  455.  
  456.             gameLocal.mpGame.SetMatchStartedTime( gameLocal.time );
  457.  
  458.             fragLimitTimeout = 0;
  459.  
  460.             // write server initial reliable messages to give everyone new base
  461.             for( i = 0; i < MAX_CLIENTS; i++ ) {
  462.                 // dont send this to server - we have all the data already and this will
  463.                 // just trigger extra gamestate detection
  464.                 if( gameLocal.entities[ i ] && i != gameLocal.localClientNum ) {
  465.                     gameLocal.mpGame.ServerWriteInitialReliableMessages( i );
  466.                 }
  467.             }
  468.             
  469.  
  470.             for( i = 0; i < gameLocal.numClients; i++ ) {
  471.                 idEntity *ent = gameLocal.entities[ i ];
  472.                 if ( !ent || !ent->IsType( idPlayer::GetClassType() ) ) {
  473.                     continue;
  474.                 }
  475.                 idPlayer *p = static_cast<idPlayer *>( ent );
  476.                 p->SetLeader( false ); // don't carry the flag from previous games    
  477.                 gameLocal.mpGame.SetPlayerScore( p, 0 );
  478.                 gameLocal.mpGame.SetPlayerTeamScore( p, 0 );
  479.  
  480.                 // in normal gameplay modes, spawn the player in.  For tourney, the tourney manager handles spawning
  481.                 if( gameLocal.gameType != GAME_TOURNEY ) {
  482.                     p->JoinInstance( 0 );
  483.                     p->ServerSpectate( static_cast<idPlayer *>(ent)->wantSpectate );
  484.                 }
  485.             }
  486.  
  487.             gameLocal.mpGame.ClearTeamScores();
  488.  
  489.             cvarSystem->SetCVarString( "ui_ready", "Not Ready" );
  490.             gameLocal.mpGame.switchThrottle[ 1 ] = 0;    // passby the throttle
  491.             break;
  492.         }
  493.         case GAMEREVIEW: {
  494.             statManager->EndGame();
  495.  
  496.             //statManager->DebugPrint();
  497.             nextState = INACTIVE;    // used to abort a game. cancel out any upcoming state change
  498.  
  499.             for( i = 0; i < gameLocal.numClients; i++ ) {
  500.                 idEntity *ent = gameLocal.entities[ i ];
  501.                 // RAVEN BEGIN
  502.                 // jnewquist: Use accessor for static class type 
  503.                 if ( !ent || !ent->IsType( idPlayer::GetClassType() ) ) {
  504.                     // RAVEN END
  505.                     continue;
  506.                 }
  507.                 static_cast< idPlayer *>( ent )->forcedReady = false;
  508.                 static_cast<idPlayer *>(ent)->ServerSpectate( true );
  509.             }
  510.             break;
  511.         }
  512.         case SUDDENDEATH: {
  513.             gameLocal.mpGame.PrintMessageEvent( -1, MSG_SUDDENDEATH );
  514.             //unmark all leaders, so we make sure we only let the proper people respawn
  515.             for( i = 0; i < gameLocal.numClients; i++ ) {
  516.                 idEntity *ent = gameLocal.entities[ i ];
  517.                 if ( !ent || !ent->IsType( idPlayer::GetClassType() ) ) {
  518.                     continue;
  519.                 }
  520.                 idPlayer *p = static_cast<idPlayer *>( ent );
  521.                 p->SetLeader( false ); // don't carry the flag from previous games    
  522.             }
  523.  
  524.             gameLocal.LocalMapRestart();
  525.             // Mark everyone tied for the lead as leaders
  526.             i = 0;
  527.             idPlayer* leader = gameLocal.mpGame.GetRankedPlayer( i );
  528.             // rjohnson: 9920
  529.             if ( leader ) {
  530.                 int highScore = gameLocal.mpGame.GetScore( leader );
  531.                 while( leader ) {
  532.                     if( gameLocal.mpGame.GetScore( leader ) < highScore ) {
  533.                         break;
  534.                     }
  535.                     leader->SetLeader( true );
  536.                     leader = gameLocal.mpGame.GetRankedPlayer( ++i );
  537.                 }
  538.             }
  539.  
  540.             if ( gameLocal.gameType == GAME_DM ) {
  541.                 //send all the non-leaders to spectatormode?
  542.                 for( i = 0; i < gameLocal.numClients; i++ ) {
  543.                     idEntity *ent = gameLocal.entities[ i ];
  544.                     // RAVEN BEGIN
  545.                     // jnewquist: Use accessor for static class type 
  546.                     if ( !ent || !ent->IsType( idPlayer::GetClassType() ) ) {
  547.                         // RAVEN END
  548.                         continue;
  549.                     }
  550.                     if ( static_cast< idPlayer *>( ent )->IsLeader() ) {
  551.                          static_cast<idPlayer *>(ent)->ServerSpectate( false );
  552.                         continue;
  553.                     }
  554.                     static_cast< idPlayer *>( ent )->forcedReady = false;
  555.                     static_cast<idPlayer *>(ent)->ServerSpectate( true );
  556.                 }
  557.             }
  558.             
  559.             break;
  560.         }
  561.         default: {
  562.             break;
  563.         }
  564.     }
  565.  
  566.     currentState = newState;
  567. }
  568.  
  569. /*
  570. ================
  571. rvGameState::ClientDisconnect
  572. ================
  573. */
  574. void rvGameState::ClientDisconnect( idPlayer* player ) {
  575.     return;
  576. }
  577.  
  578. /*
  579. ================
  580. rvGameState::Spectate
  581. ================
  582. */
  583. void rvGameState::Spectate( idPlayer* player ) {
  584.     if( player->spectating && player->wantSpectate ) {
  585.         gameLocal.mpGame.ClearFrags( player->entityNumber );
  586.     }
  587.     return;
  588. }
  589.  
  590. /*
  591. ================
  592. rvGameState::operator==
  593. ================
  594. */
  595. bool rvGameState::operator==( const rvGameState& rhs ) const {
  596.     return    (
  597.         ( currentState == rhs.currentState ) &&
  598.         ( nextState    == rhs.nextState ) &&
  599.         ( nextStateTime == rhs.nextStateTime ) 
  600.         );
  601. }
  602.  
  603. /*
  604. ================
  605. rvGameState::operator!=
  606. ================
  607. */
  608. bool rvGameState::operator!=( const rvGameState& rhs ) const {
  609.     return    (
  610.         ( currentState != rhs.currentState ) ||
  611.         ( nextState    != rhs.nextState ) ||
  612.         ( nextStateTime != rhs.nextStateTime ) 
  613.         );
  614. }
  615.  
  616. /*
  617. ================
  618. rvGameState::operator=
  619. ================
  620. */
  621. rvGameState& rvGameState::operator=( const rvGameState& rhs ) {
  622.     currentState = rhs.currentState;
  623.     nextState = rhs.nextState;
  624.     nextStateTime = rhs.nextStateTime;
  625.     return (*this);
  626. }
  627.  
  628. /*
  629. ===============
  630. rvGameState::WriteClientNetworkInfo
  631. ===============
  632. */
  633. void rvGameState::WriteClientNetworkInfo( idFile *file, int clientNum ) {
  634.     idBitMsg    msg;
  635.     byte        msgBuf[ MAX_GAME_MESSAGE_SIZE ];
  636.     
  637.     msg.Init( msgBuf, sizeof( msgBuf ) );
  638.     msg.BeginWriting();
  639.     WriteState( msg );
  640.     file->WriteInt( msg.GetSize() );
  641.     file->Write( msg.GetData(), msg.GetSize() );
  642. }
  643.  
  644. /*
  645. ===============
  646. rvGameState::ReadClientNetworkInfo
  647. ===============
  648. */
  649. void rvGameState::ReadClientNetworkInfo( idFile *file, int clientNum ) {
  650.     idBitMsg    msg;
  651.     byte        msgBuf[ MAX_GAME_MESSAGE_SIZE ];
  652.     int            size;
  653.  
  654.     file->ReadInt( size );
  655.     msg.Init( msgBuf, size );
  656.     msg.SetSize( size );
  657.     file->Read( msg.GetData(), size );
  658.     ReceiveState( msg );
  659. }
  660.  
  661. /*
  662. ===============================================================================
  663.  
  664. rvDMGameState
  665.  
  666. Game state info for DM
  667.  
  668. ===============================================================================
  669. */
  670. rvDMGameState::rvDMGameState( bool allocPrevious ) : rvGameState( allocPrevious ) {
  671.     trackPrevious = allocPrevious;
  672.  
  673.     type = GS_DM;
  674. }
  675.  
  676. void rvDMGameState::Run( void ) {
  677.     idPlayer* player = NULL;
  678.  
  679.     rvGameState::Run();
  680.     
  681.     switch( currentState ) {
  682.         case GAMEON: {
  683.             player = gameLocal.mpGame.FragLimitHit();
  684.  
  685.             bool tiedForFirst = false;
  686.             idPlayer* first = gameLocal.mpGame.GetRankedPlayer( 0 );
  687.             idPlayer* second = gameLocal.mpGame.GetRankedPlayer( 1 );
  688.  
  689.             if( player == NULL ) {
  690.                 if( first && second && gameLocal.mpGame.GetScore( first ) == gameLocal.mpGame.GetScore( second ) ) {
  691.                     tiedForFirst = true;
  692.                 }
  693.             }
  694.  
  695.             if ( player ) {
  696.                 // delay between detecting frag limit and ending game. let the death anims play
  697.                 if ( !fragLimitTimeout ) {
  698.                     common->DPrintf( "enter FragLimit timeout, player %d is leader\n", player->entityNumber );
  699.                     fragLimitTimeout = gameLocal.time + FRAGLIMIT_DELAY;
  700.                 }
  701.                 if ( gameLocal.time > fragLimitTimeout ) {
  702.                     NewState( GAMEREVIEW );
  703.  
  704.                     gameLocal.mpGame.PrintMessageEvent( -1, MSG_FRAGLIMIT, player->entityNumber );
  705.  
  706.                 }
  707.             } else if ( fragLimitTimeout ) {
  708.                 // frag limit was hit and cancelled. means the two teams got even during FRAGLIMIT_DELAY
  709.                 // enter sudden death, the next frag leader will win
  710.                 //
  711.                 // jshepard: OR it means that the winner killed himself during the fraglimit delay, and the
  712.                 // game needs to roll on.
  713.                 if( first && second && (gameLocal.mpGame.GetScore( first ) == gameLocal.mpGame.GetScore( second )) )    {
  714.                     //this is a tie...
  715.                     if( gameLocal.mpGame.GetScore( first ) >= gameLocal.serverInfo.GetInt( "si_fragLimit" ) )    {
  716.                          //and it must be tied at fraglimit, so sudden death.
  717.                         NewState( SUDDENDEATH ); 
  718.                     }
  719.                 }
  720.                 //otherwise, just keep playing as normal.
  721.                 fragLimitTimeout = 0;
  722.                 
  723.             } else if ( gameLocal.mpGame.TimeLimitHit() ) {
  724.                 gameLocal.mpGame.PrintMessageEvent( -1, MSG_TIMELIMIT );
  725.                 if( tiedForFirst ) {
  726.                     // if tied at timelimit hit, goto sudden death
  727.                     fragLimitTimeout = 0;
  728.                     NewState( SUDDENDEATH );
  729.                 } else {
  730.                     // or just end the game
  731.                     NewState( GAMEREVIEW );
  732.                 }
  733.             } else if( tiedForFirst && gameLocal.serverInfo.GetInt( "si_fragLimit" ) > 0 && gameLocal.mpGame.GetScore( first ) >= gameLocal.serverInfo.GetInt( "si_fragLimit" ) ) {
  734.                 // check for the rare case that two players both hit the fraglimit the same frame
  735.                 // two people tied at fraglimit, advance to sudden death after a delay
  736.                 fragLimitTimeout = gameLocal.time + FRAGLIMIT_DELAY;
  737.             }
  738.     
  739.             break;             
  740.         }
  741.  
  742.         case SUDDENDEATH: {
  743.             player = gameLocal.mpGame.FragLeader();
  744.  
  745.             if ( player ) {
  746.                 if ( !fragLimitTimeout ) {
  747.                     common->DPrintf( "enter sudden death FragLeader timeout, player %d is leader\n", player->entityNumber );
  748.                     fragLimitTimeout = gameLocal.time + FRAGLIMIT_DELAY;
  749.                 }
  750.                 if ( gameLocal.time > fragLimitTimeout ) {
  751.                     NewState( GAMEREVIEW );
  752.                     gameLocal.mpGame.PrintMessageEvent( -1, MSG_FRAGLIMIT, player->entityNumber );
  753.                 }
  754.             } else if ( fragLimitTimeout ) {
  755.                 gameLocal.mpGame.PrintMessageEvent( -1, MSG_HOLYSHIT );
  756.                 fragLimitTimeout = 0;
  757.             }
  758.             break;
  759.         }
  760.  
  761.     }
  762. }
  763.  
  764. /*
  765. ===============================================================================
  766.  
  767. rvTeamDMGameState
  768.  
  769. Game state info for Team DM
  770.  
  771. ===============================================================================
  772. */
  773. rvTeamDMGameState::rvTeamDMGameState( bool allocPrevious ) : rvGameState( allocPrevious ) {
  774.     trackPrevious = allocPrevious;
  775.  
  776.     type = GS_TEAMDM;
  777. }
  778.  
  779. void rvTeamDMGameState::Run( void ) {
  780.     rvGameState::Run();
  781.  
  782.     switch( currentState ) {
  783.         case GAMEON: {
  784.             int team = ( ( gameLocal.mpGame.GetScoreForTeam( TEAM_MARINE ) >= gameLocal.serverInfo.GetInt( "si_fragLimit" ) ) ? TEAM_MARINE : ( ( gameLocal.mpGame.GetScoreForTeam( TEAM_STROGG ) >= gameLocal.serverInfo.GetInt( "si_fragLimit" ) ) ? TEAM_STROGG : -1 ) );
  785.             if( gameLocal.serverInfo.GetInt( "si_fragLimit" ) <= 0 ) {
  786.                 // no fraglimit
  787.                 team = -1;
  788.             }
  789.             bool tiedForFirst = gameLocal.mpGame.GetScoreForTeam( TEAM_MARINE ) == gameLocal.mpGame.GetScoreForTeam( TEAM_STROGG );
  790.  
  791.             // rjohnson: 9920
  792.             int        numPlayers = 0;
  793.             for( int i = 0; i < gameLocal.numClients; i++ ) {
  794.                 idEntity *ent = gameLocal.entities[ i ];
  795.                 if ( ent && ent->IsType( idPlayer::GetClassType() ) ) {
  796.                     numPlayers++;
  797.                 }
  798.             }
  799.  
  800.             if ( team >= 0 && !tiedForFirst ) {
  801.                 if ( !fragLimitTimeout ) {
  802.                     common->DPrintf( "enter FragLimit timeout, team %d is leader\n", team );
  803.                     fragLimitTimeout = gameLocal.time + FRAGLIMIT_DELAY;
  804.                 }
  805.                 if ( gameLocal.time > fragLimitTimeout ) {
  806.                     NewState( GAMEREVIEW );
  807.  
  808.                     gameLocal.mpGame.PrintMessageEvent( -1, MSG_FRAGLIMIT, team );
  809.  
  810.                 }
  811.             } else if ( fragLimitTimeout ) {
  812.                 // frag limit was hit and cancelled. means the two teams got even during FRAGLIMIT_DELAY
  813.                 // enter sudden death, the next frag leader will win
  814.                 //
  815.                 // jshepard: OR it means that the winner killed himself during the fraglimit delay, and the
  816.                 // game needs to roll on.
  817.                 if( tiedForFirst )    {
  818.                     //this is a tie
  819.                     if( gameLocal.mpGame.GetScoreForTeam( TEAM_MARINE ) >= gameLocal.serverInfo.GetInt( "si_fragLimit" ) )    {
  820.                         //and it's tied at the fraglimit.
  821.                         NewState( SUDDENDEATH ); 
  822.                     }
  823.                     //not a tie, game on.
  824.                     fragLimitTimeout = 0;
  825.                 }
  826.             } else if ( gameLocal.mpGame.TimeLimitHit() ) {
  827.                 gameLocal.mpGame.PrintMessageEvent( -1, MSG_TIMELIMIT );
  828.                 // rjohnson: 9920
  829.                 if( tiedForFirst && numPlayers ) {
  830.                     // if tied at timelimit hit, goto sudden death
  831.                     fragLimitTimeout = 0;
  832.                     NewState( SUDDENDEATH );
  833.                 } else {
  834.                     // or just end the game
  835.                     NewState( GAMEREVIEW );
  836.                 }
  837.             } else if( tiedForFirst && team >= 0 ) {
  838.                 // check for the rare case that two teams both hit the fraglimit the same frame
  839.                 // two people tied at fraglimit, advance to sudden death after a delay
  840.                 fragLimitTimeout = gameLocal.time + FRAGLIMIT_DELAY;
  841.             }
  842.             break;
  843.         }
  844.  
  845.         case SUDDENDEATH: {
  846.             int team = gameLocal.mpGame.GetScoreForTeam( TEAM_MARINE ) > gameLocal.mpGame.GetScoreForTeam( TEAM_STROGG ) ? TEAM_MARINE : TEAM_STROGG;
  847.             bool tiedForFirst = false;
  848.             if( gameLocal.mpGame.GetScoreForTeam( TEAM_MARINE ) == gameLocal.mpGame.GetScoreForTeam( TEAM_STROGG ) ) {
  849.                 team = -1;
  850.                 tiedForFirst = true;
  851.             }
  852.  
  853.             if ( team >= 0 && !tiedForFirst ) {
  854.                 if ( !fragLimitTimeout ) {
  855.                     common->DPrintf( "enter sudden death FragLeader timeout, team %d is leader\n", team );
  856.                     fragLimitTimeout = gameLocal.time + FRAGLIMIT_DELAY;
  857.                 }
  858.                 if ( gameLocal.time > fragLimitTimeout ) {
  859.                     NewState( GAMEREVIEW );
  860.                     gameLocal.mpGame.PrintMessageEvent( -1, MSG_FRAGLIMIT, team );
  861.                 }
  862.             } else if ( fragLimitTimeout ) {
  863.                 gameLocal.mpGame.PrintMessageEvent( -1, MSG_HOLYSHIT );
  864.                 fragLimitTimeout = 0;
  865.             } 
  866.  
  867.             break;
  868.         }
  869.     }
  870. }
  871.  
  872. /*
  873. ===============================================================================
  874.  
  875. rvCTFGameState
  876.  
  877. Game state info for CTF
  878.  
  879. ===============================================================================
  880. */
  881.  
  882. /*
  883. ================
  884. rvCTFGameState::rvCTFGameState
  885. ================
  886. */
  887. rvCTFGameState::rvCTFGameState( bool allocPrevious ) : rvGameState( false ) {
  888.     Clear();
  889.  
  890.     if( allocPrevious ) {
  891.         previousGameState = new rvCTFGameState( false );
  892.     } else {
  893.         previousGameState = NULL;
  894.     }
  895.  
  896.     trackPrevious = allocPrevious;
  897.  
  898.     type = GS_CTF;
  899. }
  900.  
  901. /*
  902. ================
  903. rvCTFGameState::Clear
  904. ================
  905. */
  906. void rvCTFGameState::Clear( void ) {
  907.     rvGameState::Clear();
  908.  
  909.     // mekberg: clear previous game state.
  910.     if ( previousGameState ) {
  911.         previousGameState->Clear( );
  912.     }        
  913.  
  914.     for( int i = 0; i < TEAM_MAX; i++ ) {
  915.         flagStatus[ i ].state = FS_AT_BASE;
  916.         flagStatus[ i ].clientNum = -1;
  917.     }
  918.  
  919.     for( int i = 0; i < MAX_AP; i++ ) {
  920.         apState[ i ] = AS_NEUTRAL;
  921.     }
  922. }
  923.  
  924. /*
  925. ================
  926. rvCTFGameState::SendState
  927. ================
  928. */
  929. void rvCTFGameState::SendState( int clientNum ) {
  930.     idBitMsg    outMsg;
  931.     byte        msgBuf[MAX_GAME_MESSAGE_SIZE];
  932.  
  933.     assert( gameLocal.isServer && trackPrevious && type == GS_CTF );
  934.  
  935.     if( clientNum == -1 && (rvCTFGameState&)(*this) == (rvCTFGameState&)(*previousGameState) ) {
  936.         return;
  937.     }
  938.  
  939.     outMsg.Init( msgBuf, sizeof( msgBuf ) );
  940.     outMsg.WriteByte( GAME_RELIABLE_MESSAGE_GAMESTATE );
  941.  
  942.     WriteState( outMsg );
  943.  
  944.     networkSystem->ServerSendReliableMessage( clientNum, outMsg );
  945.  
  946.     // don't update the state if we are working for a single client
  947.     if ( clientNum == -1 ) {
  948.         outMsg.ReadByte(); // pop off the msg ID
  949.         ReceiveState( outMsg );
  950.     }
  951. }
  952.  
  953. /*
  954. ===============
  955. rvCTFGameState::WriteState
  956. ===============
  957. */
  958. void rvCTFGameState::WriteState( idBitMsg &msg ) {
  959.     // send off base info
  960.     rvGameState::PackState( msg );
  961.     // add CTF info
  962.     PackState( msg );
  963. }
  964.  
  965. /*
  966. ================
  967. rvCTFGameState::ReceiveState
  968. ================
  969. */
  970. void rvCTFGameState::ReceiveState( const idBitMsg& msg ) {
  971.     assert( type == GS_CTF );
  972.  
  973.     rvGameState::UnpackState( msg );
  974.     UnpackState( msg );
  975.  
  976.     if ( gameLocal.localClientNum >= 0 ) {
  977.         GameStateChanged();
  978.     }
  979.  
  980.     (rvCTFGameState&)(*previousGameState) = (rvCTFGameState&)(*this);
  981. }
  982.  
  983. /*
  984. ================
  985. rvCTFGameState::PackState
  986. ================
  987. */
  988. void rvCTFGameState::PackState( idBitMsg& outMsg ) {
  989.     // use indexing to pack in info
  990.     int index = 0;
  991.  
  992.     for( int i = 0; i < TEAM_MAX; i++ ) {
  993.         if( flagStatus[ i ] != ((rvCTFGameState*)previousGameState)->flagStatus[ i ] ) {
  994.             outMsg.WriteByte( index );
  995.             outMsg.WriteByte( flagStatus[ i ].state );
  996.             outMsg.WriteByte( flagStatus[ i ].clientNum );
  997.         }
  998.         index++;
  999.     }
  1000.  
  1001.     for( int i = 0; i < MAX_AP; i++ ) {
  1002.         if( apState[ i ] != ((rvCTFGameState*)previousGameState)->apState[ i ] ) {
  1003.             outMsg.WriteByte( index );
  1004.             outMsg.WriteByte( apState[ i ] );
  1005.         }
  1006.         index++;
  1007.     }
  1008. }
  1009.  
  1010. /*
  1011. ================
  1012. rvCTFGameState::UnpackState
  1013. ================
  1014. */
  1015. void rvCTFGameState::UnpackState( const idBitMsg& inMsg ) {
  1016.     while( inMsg.GetRemainingData() ) {
  1017.         int index = inMsg.ReadByte();
  1018.  
  1019.         if( index >= 0 && index < TEAM_MAX ) {
  1020.             flagStatus[ index ].state = (flagState_t)inMsg.ReadByte();
  1021.             flagStatus[ index ].clientNum = inMsg.ReadByte();
  1022.         } else if( index >= TEAM_MAX && index < ( TEAM_MAX + MAX_AP ) ) {
  1023.             apState[ index - TEAM_MAX ] = (apState_t)inMsg.ReadByte();
  1024.         } else {
  1025.             gameLocal.Error( "rvCTFGameState::UnpackState() - Unknown data identifier '%d'\n", index );
  1026.         }
  1027.     }
  1028. }
  1029.  
  1030. /*
  1031. ================
  1032. rvCTFGameState::SendInitialState
  1033. ================
  1034. */
  1035. void rvCTFGameState::SendInitialState( int clientNum ) {
  1036.     assert( type == GS_CTF );
  1037.  
  1038.     rvCTFGameState* previousState = (rvCTFGameState*)previousGameState;
  1039.  
  1040.     rvCTFGameState invalidState;
  1041.  
  1042.     previousGameState = &invalidState;
  1043.  
  1044.     SendState( clientNum );
  1045.  
  1046.     previousGameState = previousState;
  1047. }
  1048.  
  1049. /*
  1050. ================
  1051. rvCTFGameState::GameStateChanged
  1052. ================
  1053. */
  1054. void rvCTFGameState::GameStateChanged( void ) {
  1055.     // detect any base state changes
  1056.     rvGameState::GameStateChanged();
  1057.  
  1058.     // CTF specific stuff
  1059.     idPlayer* player = gameLocal.GetLocalPlayer();
  1060.     bool noSounds = false;
  1061.  
  1062.     if( player == NULL ) {
  1063.         gameLocal.Error( "rvCTFGameState::GameStateChanged() - NULL local player\n" );
  1064.         return;
  1065.     }
  1066.  
  1067.     for( int i = 0; i < TEAM_MAX; i++ ) {
  1068.         if( flagStatus[ i ] == ((rvCTFGameState*)previousGameState)->flagStatus[ i ] ) {
  1069.             continue;
  1070.         }
  1071.  
  1072.         // don't play flag messages when flag state changes as a result of the gamestate changing
  1073.         if( currentState != ((rvCTFGameState*)previousGameState)->currentState && ((rvCTFGameState*)previousGameState)->currentState != INACTIVE ) {
  1074.             continue;
  1075.         }
  1076.  
  1077.         if ( ((rvCTFGameState*)previousGameState)->currentState == INACTIVE ) {
  1078.             noSounds = true;
  1079.         }
  1080.  
  1081.         // flagTeam - used to tell the HUD which flag to update
  1082.         int flagTeam = i;
  1083.  
  1084.         // in one flag CTF flagTeam is set to whichever team has the flag
  1085.         if( gameLocal.gameType == GAME_1F_CTF || gameLocal.gameType == GAME_ARENA_1F_CTF ) {
  1086.             if( flagStatus[ i ].state == FS_TAKEN_MARINE ) {
  1087.                 flagTeam = TEAM_MARINE;
  1088.             } else if( flagStatus[ i ].state == FS_TAKEN_STROGG ) {
  1089.                 flagTeam = TEAM_STROGG;
  1090.             }
  1091.         }
  1092.  
  1093.         if( flagStatus[ i ].state == FS_DROPPED && !noSounds ) {
  1094.             if( flagTeam == player->team ) {
  1095.                 // our flag was dropped, so the enemy dropped it
  1096.                 gameLocal.mpGame.ScheduleAnnouncerSound ( AS_CTF_ENEMY_DROPS_FLAG, gameLocal.time, -1, true );
  1097.             } else {
  1098.                 gameLocal.mpGame.ScheduleAnnouncerSound ( AS_CTF_YOUR_TEAM_DROPS_FLAG, gameLocal.time, -1, true );
  1099.             }
  1100.  
  1101.         if( player->mphud ) {
  1102.                 player->mphud->SetStateInt( "team", flagTeam );
  1103.                 player->mphud->HandleNamedEvent( "flagDrop" );
  1104.  
  1105.                 if ( flagTeam == player->team ) {
  1106.                     player->mphud->SetStateString( "main_notice_text", common->GetLocalizedString( "#str_107723" ) );
  1107.                 } else {
  1108.                     player->mphud->SetStateString( "main_notice_text", common->GetLocalizedString( "#str_104420" ) );
  1109.                 }
  1110.  
  1111.                 player->mphud->HandleNamedEvent( "main_notice" );
  1112.             }
  1113.         } else if( flagStatus[ i ].state == FS_AT_BASE ) {
  1114.             if( ((rvCTFGameState*)previousGameState)->flagStatus[ i ].state == FS_TAKEN && !noSounds ) {
  1115.                 // team scores
  1116.                 if( flagTeam == player->team ) {
  1117.                     if( gameLocal.mpGame.CanCapture( gameLocal.mpGame.OpposingTeam( flagTeam ) ) ) {
  1118.                         gameLocal.mpGame.ScheduleAnnouncerSound ( AS_TEAM_ENEMY_SCORES, gameLocal.time );
  1119.                     }
  1120.                 } else {
  1121.                     if( gameLocal.mpGame.CanCapture( gameLocal.mpGame.OpposingTeam( flagTeam ) ) ) {
  1122.                         gameLocal.mpGame.ScheduleAnnouncerSound ( AS_TEAM_YOU_SCORE, gameLocal.time );
  1123.                     }
  1124.                 }
  1125.             } else if( ((rvCTFGameState*)previousGameState)->flagStatus[ i ].state == FS_DROPPED && !noSounds ) {
  1126.                 // flag returned
  1127.                 if( flagTeam == player->team ) {
  1128.                     gameLocal.mpGame.ScheduleAnnouncerSound ( AS_CTF_YOUR_FLAG_RETURNED, gameLocal.time, -1, true );
  1129.                 } else {
  1130.                     gameLocal.mpGame.ScheduleAnnouncerSound ( AS_CTF_ENEMY_RETURNS_FLAG, gameLocal.time, -1, true );
  1131.                 }
  1132.             }
  1133.  
  1134.             if( player->mphud ) {
  1135.                 player->mphud->SetStateInt( "team", flagTeam );
  1136.                 player->mphud->HandleNamedEvent( "flagReturn" );
  1137.             }
  1138.         } else if( flagStatus[ i ].state == FS_TAKEN || flagStatus[ i ].state == FS_TAKEN_STROGG || flagStatus[ i ].state == FS_TAKEN_MARINE ) {
  1139.             // flag taken
  1140.             if( flagTeam == player->team ) {
  1141.                 if ( !noSounds ) {
  1142.                     gameLocal.mpGame.ScheduleAnnouncerSound ( AS_CTF_ENEMY_HAS_FLAG, gameLocal.time, -1, true );
  1143.                 }
  1144.  
  1145.                 if ( player->mphud ) {
  1146.                     player->mphud->SetStateString( "main_notice_text", common->GetLocalizedString( "#str_107722" ) );
  1147.                     player->mphud->HandleNamedEvent( "main_notice" );
  1148.                 }
  1149.             } else {
  1150.                 if( flagStatus[ i ].clientNum == gameLocal.localClientNum ) {
  1151.                     if ( !noSounds ) {
  1152.                         gameLocal.mpGame.ScheduleAnnouncerSound ( AS_CTF_YOU_HAVE_FLAG, gameLocal.time, -1, true );
  1153.                     }                    
  1154.  
  1155.                     // shouchard:  inform the GUI that you've taken the flag
  1156.                     player->mphud->SetStateString( "main_notice_text", common->GetLocalizedString( "#str_104419" ) );
  1157.                     player->mphud->HandleNamedEvent( "main_notice" );
  1158.                 } else if ( !noSounds ) {
  1159.                     gameLocal.mpGame.ScheduleAnnouncerSound ( AS_CTF_YOUR_TEAM_HAS_FLAG, gameLocal.time, -1, true );
  1160.                 }
  1161.             }
  1162.  
  1163.             if( player->mphud ) {
  1164.                 player->mphud->SetStateInt( "team", flagTeam );
  1165.                 player->mphud->HandleNamedEvent ( "flagTaken" );
  1166.             }
  1167.         }
  1168.     }
  1169. }
  1170.  
  1171. /*
  1172. ================
  1173. rvCTFGameState::Run
  1174. ================
  1175. */
  1176. void rvCTFGameState::Run( void ) {
  1177.     // run common stuff    
  1178.     rvGameState::Run();
  1179.  
  1180.     switch( currentState ) {
  1181.         case GAMEON: {
  1182.             int team = ( ( gameLocal.mpGame.GetScoreForTeam( TEAM_MARINE ) >= gameLocal.serverInfo.GetInt( "si_captureLimit" ) ) ? TEAM_MARINE : ( ( gameLocal.mpGame.GetScoreForTeam( TEAM_STROGG ) >= gameLocal.serverInfo.GetInt( "si_captureLimit" ) ) ? TEAM_STROGG : -1 ) );
  1183.             if( gameLocal.serverInfo.GetInt( "si_captureLimit" ) <= 0 ) {
  1184.                 // no capture limit games
  1185.                 team = -1;
  1186.             }
  1187.             bool    tiedForFirst = gameLocal.mpGame.GetScoreForTeam( TEAM_MARINE ) == gameLocal.mpGame.GetScoreForTeam( TEAM_STROGG );
  1188.  
  1189.             // rjohnson: 9920
  1190.             int        numPlayers = 0;
  1191.             for( int i = 0; i < gameLocal.numClients; i++ ) {
  1192.                 idEntity *ent = gameLocal.entities[ i ];
  1193.                 if ( ent && ent->IsType( idPlayer::GetClassType() ) ) {
  1194.                     numPlayers++;
  1195.                 }
  1196.             }
  1197.  
  1198.             if ( team >= 0 && !tiedForFirst ) {
  1199.                 if ( !fragLimitTimeout ) {
  1200.                     common->DPrintf( "enter capture limit timeout, team %d is leader\n", team );
  1201.                     fragLimitTimeout = gameLocal.time + CAPTURELIMIT_DELAY;
  1202.                 }
  1203.                 if ( gameLocal.time > fragLimitTimeout ) {
  1204.                     NewState( GAMEREVIEW );
  1205.  
  1206.                     gameLocal.mpGame.PrintMessageEvent( -1, MSG_CAPTURELIMIT, team );
  1207.  
  1208.                 }
  1209.             } else if ( fragLimitTimeout ) {
  1210.                 // frag limit was hit and cancelled. means the two teams got even during FRAGLIMIT_DELAY
  1211.                 // enter sudden death, the next frag leader will win
  1212.                 // OR the winner lost a point in the frag delay, and there's no tie, so no one wins, game on.
  1213.                 if( tiedForFirst && ( gameLocal.mpGame.GetScoreForTeam( TEAM_MARINE ) >= gameLocal.serverInfo.GetInt( "si_captureLimit" ) ))    {
  1214.                     NewState( SUDDENDEATH );
  1215.                 }
  1216.                 fragLimitTimeout = 0; 
  1217.             } else if ( gameLocal.mpGame.TimeLimitHit() ) {
  1218.                 gameLocal.mpGame.PrintMessageEvent( -1, MSG_TIMELIMIT );
  1219.  
  1220.                 // rjohnson: 9920
  1221.                 if( tiedForFirst && numPlayers ) {
  1222.                     // if tied at timelimit hit, goto sudden death
  1223.                     fragLimitTimeout = 0;
  1224.                     NewState( SUDDENDEATH );
  1225.                 } else {
  1226.                     // or just end the game
  1227.                     NewState( GAMEREVIEW );
  1228.                 }
  1229.             } else if( tiedForFirst && team >= 0 ) {
  1230.                 // check for the rare case that two teams both hit the fraglimit the same frame
  1231.                 // two people tied at fraglimit, advance to sudden death after a delay
  1232.                 fragLimitTimeout = gameLocal.time + CAPTURELIMIT_DELAY;
  1233.             }
  1234.             break;
  1235.         }
  1236.  
  1237.         case SUDDENDEATH: {
  1238.             int team = gameLocal.mpGame.GetScoreForTeam( TEAM_MARINE ) > gameLocal.mpGame.GetScoreForTeam( TEAM_STROGG ) ? TEAM_MARINE : TEAM_STROGG;
  1239.             bool tiedForFirst = false;
  1240.             if( gameLocal.mpGame.GetScoreForTeam( TEAM_MARINE ) == gameLocal.mpGame.GetScoreForTeam( TEAM_STROGG ) ) {
  1241.                 team = -1;
  1242.                 tiedForFirst = true;
  1243.             }
  1244.  
  1245.             if ( team >= 0 && !tiedForFirst ) {
  1246.                 if ( !fragLimitTimeout ) {
  1247.                     common->DPrintf( "enter sudden death FragLeader timeout, team %d is leader\n", team );
  1248.                     fragLimitTimeout = gameLocal.time + CAPTURELIMIT_DELAY;
  1249.                 }
  1250.                 if ( gameLocal.time > fragLimitTimeout ) {
  1251.                     NewState( GAMEREVIEW );
  1252.                     gameLocal.mpGame.PrintMessageEvent( -1, MSG_CAPTURELIMIT, team );
  1253.                 }
  1254.             } else if ( fragLimitTimeout ) {
  1255.                 gameLocal.mpGame.PrintMessageEvent( -1, MSG_HOLYSHIT );
  1256.                 fragLimitTimeout = 0;
  1257.             } 
  1258.  
  1259.             break;
  1260.         }
  1261.     }
  1262. }
  1263.  
  1264. /*
  1265. ================
  1266. rvCTFGameState::SetFlagState
  1267. ================
  1268. */
  1269. void rvCTFGameState::SetFlagState( int flag, flagState_t newState ) {
  1270.     if ( !gameLocal.isServer ) {
  1271.         return;
  1272.     }
  1273.  
  1274.     assert( gameLocal.isServer && ( flag >= 0 && flag < TEAM_MAX ) && type == GS_CTF );
  1275.  
  1276.     flagStatus[ flag ].state = newState;
  1277. }
  1278.  
  1279. /*
  1280. ================
  1281. rvCTFGameState::SetFlagCarrier
  1282. ================
  1283. */
  1284. void rvCTFGameState::SetFlagCarrier( int flag, int clientNum ) {
  1285.     assert( gameLocal.isServer && ( flag >= 0 && flag < TEAM_MAX ) && (clientNum >= 0 && clientNum < MAX_CLIENTS) && type == GS_CTF );
  1286.  
  1287.     flagStatus[ flag ].clientNum = clientNum;
  1288. }
  1289.  
  1290. /*
  1291. ================
  1292. rvCTFGameState::operator==
  1293. ================
  1294. */
  1295. bool rvCTFGameState::operator==( const rvCTFGameState& rhs ) const {
  1296.     if( (rvGameState&)(*this) != (rvGameState&)rhs ) {
  1297.         return false;
  1298.     }
  1299.  
  1300.     for( int i = 0; i < TEAM_MAX; i++ ) {
  1301.         if( flagStatus[ i ] != rhs.flagStatus[ i ] ) {
  1302.             return false;
  1303.         }
  1304.     }
  1305.  
  1306.     for( int i = 0; i < MAX_AP; i++ ) {
  1307.         if( apState[ i ] != rhs.apState[ i ] ) {
  1308.             return false;
  1309.         }
  1310.     }
  1311.  
  1312.     return true;
  1313. }
  1314.  
  1315. /*
  1316. ================
  1317. rvCTFGameState::operator=
  1318. ================
  1319. */
  1320. rvCTFGameState& rvCTFGameState::operator=( const rvCTFGameState& rhs ) {
  1321.     (rvGameState&)(*this) = (rvGameState&)rhs;
  1322.  
  1323.     for( int i = 0; i < TEAM_MAX; i++ ) {
  1324.         flagStatus[ i ] = rhs.flagStatus[ i ];
  1325.     }
  1326.  
  1327.     for( int i = 0; i < MAX_AP; i++ ) {
  1328.         apState[ i ] = rhs.apState[ i ];
  1329.     }
  1330.  
  1331.      return (*this);
  1332. }
  1333.  
  1334. /*
  1335. ===============================================================================
  1336.  
  1337. rvTourneyGameState
  1338.  
  1339. Game state info for tourney
  1340.  
  1341. ===============================================================================
  1342. */
  1343.  
  1344. /*
  1345. ================
  1346. rvTourneyGameState::rvTourneyGameState
  1347. ================
  1348. */
  1349. rvTourneyGameState::rvTourneyGameState( bool allocPrevious ) : rvGameState( false ) {
  1350.     type = GS_TOURNEY;
  1351.  
  1352.     Clear();
  1353.  
  1354.     if( allocPrevious ) {
  1355.         previousGameState = new rvTourneyGameState( false );
  1356.     } else {
  1357.         previousGameState = NULL;
  1358.     }
  1359.  
  1360.     packTourneyHistory = false;
  1361.  
  1362.     trackPrevious = allocPrevious;
  1363. }
  1364.  
  1365. /*
  1366. ================
  1367. rvTourneyGameState::Clear
  1368. ================
  1369. */
  1370. void rvTourneyGameState::Clear( void ) {
  1371.     assert( type == GS_TOURNEY );
  1372.  
  1373.     // mekberg: clear previous game state.
  1374.     if ( previousGameState ) {
  1375.         previousGameState->Clear();
  1376.     }        
  1377.  
  1378.     rvGameState::Clear();
  1379.  
  1380.     tourneyState = TS_INVALID;
  1381.  
  1382.     for( int i = 0; i < MAX_ARENAS; i++ ) {
  1383.         arenas[ i ].Clear( false );
  1384.         arenas[ i ].SetArenaID( i );
  1385.     }
  1386.  
  1387.     for( int i = 0; i < MAX_ROUNDS; i++ ) {
  1388.         for( int j = 0; j < MAX_ARENAS; j++ ) {
  1389.             tourneyHistory[ i ][ j ].playerOne[ 0 ] = '\0';
  1390.             tourneyHistory[ i ][ j ].playerTwo[ 0 ] = '\0';
  1391.             tourneyHistory[ i ][ j ].playerOneNum = -1;
  1392.             tourneyHistory[ i ][ j ].playerTwoNum = -1;
  1393.             tourneyHistory[ i ][ j ].playerOneScore = -1;
  1394.             tourneyHistory[ i ][ j ].playerTwoScore = -1;
  1395.         }
  1396.     }
  1397.  
  1398.     startingRound = 0;
  1399.     maxRound = 0;
  1400.     round = -1;
  1401.     byePlayer = NULL;
  1402.     tourneyCount = 0;
  1403.     roundTimeout = 0;
  1404. }
  1405.  
  1406. /*
  1407. ================
  1408. rvTourneyGameState::Reset
  1409. ================
  1410. */
  1411. void rvTourneyGameState::Reset( void ) {
  1412.     for( int i = 0; i < MAX_ARENAS; i++ ) {
  1413.         arenas[ i ].Clear( false );
  1414.         arenas[ i ].SetArenaID( i );
  1415.     }
  1416.  
  1417.     for( int i = 0; i < MAX_ROUNDS; i++ ) {
  1418.         for( int j = 0; j < MAX_ARENAS; j++ ) {
  1419.             tourneyHistory[ i ][ j ].playerOne[ 0 ] = '\0';
  1420.             tourneyHistory[ i ][ j ].playerTwo[ 0 ] = '\0';
  1421.             tourneyHistory[ i ][ j ].playerOneNum = -1;
  1422.             tourneyHistory[ i ][ j ].playerTwoNum = -1;
  1423.             tourneyHistory[ i ][ j ].playerOneScore = -1;
  1424.             tourneyHistory[ i ][ j ].playerTwoScore = -1;
  1425.         }
  1426.     }
  1427.  
  1428.     startingRound = 0;
  1429.     maxRound = 0;
  1430.     round = -1;
  1431.     byePlayer = NULL;
  1432.     roundTimeout = 0;
  1433. }
  1434.  
  1435. /*
  1436. ================
  1437. rvTourneyGameState::Run
  1438. ================
  1439. */
  1440. void rvTourneyGameState::Run( void ) {
  1441.     assert( type == GS_TOURNEY );
  1442.  
  1443.     rvGameState::Run();
  1444.  
  1445.     switch( currentState ) {
  1446.         case GAMEON: {
  1447.             // check to see if we need to advance to the next round
  1448.             if( nextState == GAMEREVIEW ) {
  1449.                 return;
  1450.             }
  1451.  
  1452.             bool roundDone = true;
  1453.  
  1454.             for( int i = 0; i < MAX_ARENAS; i++ ) {
  1455.                 if( arenas[ i ].GetState() == AS_INACTIVE ) {
  1456.                     continue;
  1457.                 }
  1458.  
  1459.                 arenas[ i ].UpdateState();
  1460.  
  1461.                 if( arenas[ i ].GetState() != AS_DONE ) {
  1462.                     // check to see if we're done
  1463.                     roundDone = false;
  1464.                 }
  1465.             }
  1466.             if( roundDone && !roundTimeout ) {
  1467.                 roundTimeout = gameLocal.time + FRAGLIMIT_DELAY;
  1468.             }
  1469.  
  1470.             if( roundDone && gameLocal.time > roundTimeout ) {
  1471.                 int historyOffset = round - 1; //rounds are 1 indexed
  1472.                 idList<rvPair<int, int> > scoreDeltas; //track who won by the largest margin
  1473.                 idPlayer* formerByePlayer = byePlayer;
  1474.  
  1475.                 round++;
  1476.                 roundTimeout = 0;
  1477.  
  1478.                 assert( round >= 2 );
  1479.  
  1480.                 // add the bye player to the tourney history first, since byePlayer gets overwritten
  1481.                 int i;
  1482.  
  1483.                 if( byePlayer ) {
  1484.                     for( i = 0; i < MAX_ARENAS; i++ ) {
  1485.                         if( arenas[ i ].GetState() == AS_INACTIVE ) {
  1486.                             break;
  1487.                         }
  1488.                     }
  1489.  
  1490.                     if( i < MAX_ARENAS && historyOffset >= 0 && historyOffset < MAX_ROUNDS ) {
  1491.                         //strncpy( tourneyHistory[ historyOffset ][ i ].playerOne, byePlayer->GetUserInfo()->GetString( "ui_name" ), MAX_TOURNEY_HISTORY_NAME_LEN ); 
  1492.                         //tourneyHistory[ historyOffset ][ i ].playerTwo[ 0 ] = '\0';
  1493.                         tourneyHistory[ historyOffset ][ i ].playerOneNum = byePlayer->entityNumber;
  1494.                         tourneyHistory[ historyOffset ][ i ].playerTwoNum = -1;
  1495.                         tourneyHistory[ historyOffset ][ i ].playerOneScore = 0;
  1496.                         tourneyHistory[ historyOffset ][ i ].playerTwoScore = 0;
  1497.                     }
  1498.                 }
  1499.  
  1500.                 // setup the next round - even though sometimes we process 2 arenas at once, we have to run through all of them, incase
  1501.                 // the setup changes mid-round because of people disconnecting
  1502.                 for( i = 0; i < MAX_ARENAS; i++ ) {
  1503.                     // only consider active arenas
  1504.                     if( arenas[ i ].GetState() == AS_INACTIVE ) {
  1505.                         continue;
  1506.                     }
  1507.  
  1508.                     int j = i + 1;
  1509.  
  1510.                     idPlayer* advanceOne = arenas[ i ].GetWinner();
  1511.                     idPlayer* advanceTwo = j < MAX_ARENAS ? arenas[ j ].GetWinner() : NULL;
  1512.  
  1513.                     assert( advanceOne );
  1514.                     assert( arenas[ i ].GetLoser() );
  1515.     
  1516.                     assert( arenas[ i ].GetPlayers()[ 0 ] && arenas[ i ].GetPlayers()[ 1 ] );
  1517.  
  1518.                     if( historyOffset >= 0 && historyOffset < MAX_ROUNDS ) {
  1519.                         tourneyHistory[ historyOffset ][ i ].playerOneNum = arenas[ i ].GetPlayers()[ 0 ]->entityNumber;
  1520.                         tourneyHistory[ historyOffset ][ i ].playerTwoNum = arenas[ i ].GetPlayers()[ 1 ]->entityNumber;
  1521.                         tourneyHistory[ historyOffset ][ i ].playerOne[ MAX_TOURNEY_HISTORY_NAME_LEN - 1 ] = '\0';
  1522.                         tourneyHistory[ historyOffset ][ i ].playerTwo[ MAX_TOURNEY_HISTORY_NAME_LEN - 1 ] = '\0';
  1523.  
  1524.                         tourneyHistory[ historyOffset ][ i ].playerOneScore = arenas[ i ].GetPlayerScore( 0 );
  1525.                         tourneyHistory[ historyOffset ][ i ].playerTwoScore = arenas[ i ].GetPlayerScore( 1 );
  1526.                     }
  1527.                     
  1528.                     scoreDeltas.Append( rvPair<int, int>( arenas[ i ].GetWinner()->entityNumber, gameLocal.mpGame.GetTeamScore( arenas[ i ].GetWinner() ) - gameLocal.mpGame.GetTeamScore( arenas[ i ].GetLoser() ) ) );
  1529.  
  1530.                     // if this arena is active & done, but the next one is inactive we need to check for a bye
  1531.                     if( j >= MAX_ARENAS || arenas[ j ].GetState() == AS_INACTIVE ) {
  1532.                         // if we're the last arena and there are no byes and we're done, the tourney is done
  1533.                         if( i == 0 && byePlayer == NULL ) {
  1534.                             gameLocal.Printf( "Round %d: %d (%s) has won the tourney!\n", round - 1, advanceOne->entityNumber, gameLocal.userInfo[ advanceOne->entityNumber ].GetString( "ui_name" ) );
  1535.                             // award 5 wins for winning the entire tournament
  1536.                             gameLocal.mpGame.AddPlayerWin( advanceOne, 5 );
  1537. //asalmon: For Xbox 360 do not delay game review
  1538. #ifdef _XENON
  1539.                             NewState( GAMEREVIEW );
  1540. #else
  1541.                             nextState = GAMEREVIEW;
  1542.                             nextStateTime = gameLocal.time + 5000;
  1543. #endif
  1544.                             break;
  1545.                         }
  1546.  
  1547.                         arenas[ i ].Clear();
  1548.  
  1549.                         // if no player has a bye, we become the byePlayer
  1550.                         if( byePlayer == NULL ) {
  1551.                             byePlayer = advanceOne;
  1552.                             continue;
  1553.                         } else {
  1554.                             // otherwise, we play the current byePlayer
  1555.                             advanceTwo = byePlayer;
  1556.                             byePlayer = NULL;
  1557.                         }
  1558.                     } else {
  1559.                         // arena i and arena i + 1 can fight eachother
  1560.  
  1561.                         // GetWinner() might return NULL if there's a tie, but if 
  1562.                         // there is a tie round state should not get to AS_DONE
  1563.                         assert( advanceTwo );
  1564.  
  1565.                         assert( arenas[ j ].GetPlayers()[ 0 ] && arenas[ j ].GetPlayers()[ 1 ] );
  1566.  
  1567.                         if( historyOffset >= 0 && historyOffset < MAX_ROUNDS ) {
  1568.                             tourneyHistory[ historyOffset ][ j ].playerOneNum = arenas[ j ].GetPlayers()[ 0 ]->entityNumber;
  1569.                             tourneyHistory[ historyOffset ][ j ].playerTwoNum = arenas[ j ].GetPlayers()[ 1 ]->entityNumber;
  1570.  
  1571.                             tourneyHistory[ historyOffset ][ j ].playerOne[ MAX_TOURNEY_HISTORY_NAME_LEN - 1 ] = '\0';
  1572.                             tourneyHistory[ historyOffset ][ j ].playerTwo[ MAX_TOURNEY_HISTORY_NAME_LEN - 1 ] = '\0';
  1573.  
  1574.                             tourneyHistory[ historyOffset ][ j ].playerOneScore = arenas[ j ].GetPlayerScore( 0 );
  1575.                             tourneyHistory[ historyOffset ][ j ].playerTwoScore = arenas[ j ].GetPlayerScore( 1 );
  1576.                         }
  1577.  
  1578.                         scoreDeltas.Append( rvPair<int, int>( arenas[ j ].GetWinner()->entityNumber, gameLocal.mpGame.GetTeamScore( arenas[ j ].GetWinner() ) - gameLocal.mpGame.GetTeamScore( arenas[ j ].GetLoser() ) ) );
  1579.  
  1580.                         // clear arenas
  1581.                         arenas[ i ].Clear();
  1582.                         arenas[ j ].Clear();
  1583.                     }
  1584.  
  1585.                     // winners of arenas X and X+1 compete in arena 
  1586.                     // X / 2 in the next round.  This arena should have been
  1587.                     // cleared in a previous loop iteration
  1588.                     arenas[ (i / 2) ].AddPlayers( advanceOne, advanceTwo );
  1589.                     
  1590.                     // award bracket advancement
  1591.                     gameLocal.mpGame.AddPlayerWin( advanceOne, 1 );
  1592.                     gameLocal.mpGame.AddPlayerWin( advanceTwo, 1 );
  1593.  
  1594.                     advanceOne->ServerSpectate( false );
  1595.                     advanceTwo->ServerSpectate( false );
  1596.  
  1597.                     gameLocal.Printf( "Round %d: Arena %d is starting play with players %d (%s) and %d (%s)\n", round, i / 2, advanceOne->entityNumber, advanceOne->GetUserInfo()->GetString( "ui_name" ), advanceTwo->entityNumber, advanceTwo->GetUserInfo()->GetString( "ui_name" ) );
  1598.  
  1599.                     advanceOne->JoinInstance( (i / 2) );
  1600.                     advanceTwo->JoinInstance( (i / 2) );
  1601.  
  1602.                     arenas[ (i / 2) ].Ready();
  1603.                 }
  1604.  
  1605.                 // in certain situations, a player will get bye'd to the finals and needs to win
  1606.                 if ( byePlayer && // rj 9538
  1607.                     ( ( round >= maxRound && GetNumArenas() == 0 ) ||
  1608.                     ( byePlayer == formerByePlayer && scoreDeltas.Num() == 0 ) ) ) {  // 
  1609.                     gameLocal.Printf( "Round %d: %d (%s) has won the tourney!\n", round, byePlayer->entityNumber, gameLocal.userInfo[ byePlayer->entityNumber ].GetString( "ui_name" ) );
  1610.                     // award 5 wins for winning the entire tournament
  1611.                     gameLocal.mpGame.AddPlayerWin( byePlayer, 5 );
  1612. //asalmon: For Xbox 360 do not delay game review
  1613. #ifdef _XENON
  1614.                             NewState( GAMEREVIEW );
  1615. #else
  1616.                             nextState = GAMEREVIEW;
  1617.                             nextStateTime = gameLocal.time + 5000;
  1618. #endif
  1619.                     break;
  1620.                 }
  1621.  
  1622.                 // if the bye player is the same as before we setup the new round, swap the byeplayer
  1623.                 // with someone else - so one player doesn't get a bye all the way to the finals.
  1624.                 if( byePlayer && byePlayer == formerByePlayer ) {
  1625.                     assert( scoreDeltas.Num() ); // something is very wrong....
  1626.  
  1627.                     // Pick t
  1628.                     scoreDeltas.Sort( rvPair<int, int>::rvPairSecondCompare );
  1629.                     int i;
  1630.                     for( i = 1; i < scoreDeltas.Num(); i++ ) {
  1631.                         if( scoreDeltas[ i ].Second() != scoreDeltas[ i - 1 ].Second() ) {
  1632.                             break;
  1633.                         } 
  1634.                     }
  1635.                     int arena = gameLocal.random.RandomInt( i ); // this is an index to the previous round's arenas
  1636.                     int swapPlayer = scoreDeltas[ arena ].First();
  1637.                     rvTourneyArena* swapArena = NULL;
  1638.                     int playerIndex = -1;
  1639.                     for( int i = 0; i < MAX_ARENAS; i++ ) {
  1640.                         if( arenas[ i ].GetState() == AS_INACTIVE ) {
  1641.                             continue;
  1642.                         }
  1643.  
  1644.                         if( arenas[ i ].GetPlayers()[ 0 ] && arenas[ i ].GetPlayers()[ 0 ]->entityNumber == swapPlayer ) {
  1645.                             playerIndex = 0;
  1646.                             swapArena = &arenas[ i ];
  1647.                         } else if( arenas[ i ].GetPlayers()[ 1 ] && arenas[ i ].GetPlayers()[ 1 ]->entityNumber == swapPlayer ) {
  1648.                             playerIndex = 1;
  1649.                             swapArena = &arenas[ i ];
  1650.                         }
  1651.  
  1652.                     }
  1653.  
  1654.                     assert( swapArena );
  1655.  
  1656.                     if( swapArena ) {
  1657.                         idPlayer* t = byePlayer;
  1658.                         if( playerIndex == 0 ) {
  1659.                             // the highest delta was the first players
  1660.                             byePlayer = swapArena->GetPlayers()[ 0 ];
  1661.                             swapArena->AddPlayers( t, swapArena->GetPlayers()[ 1 ] );
  1662.                         } else if( playerIndex == 1 ) {
  1663.                             // the highest delta was the second player
  1664.                             byePlayer = swapArena->GetPlayers()[ 1 ];
  1665.                             swapArena->AddPlayers( swapArena->GetPlayers()[ 0 ], t );                    
  1666.                         } else {
  1667.                             assert( 0 );
  1668.                         }
  1669.  
  1670.                         swapArena->GetPlayers()[ 0 ]->JoinInstance( swapArena->GetArenaID() );
  1671.                         swapArena->GetPlayers()[ 1 ]->JoinInstance( swapArena->GetArenaID() );
  1672.                         swapArena->GetPlayers()[ 0 ]->ServerSpectate( false );
  1673.                         swapArena->GetPlayers()[ 1 ]->ServerSpectate( false );
  1674.                         byePlayer->ServerSpectate( true );
  1675.                         swapArena->Ready();
  1676.                         gameLocal.Printf( "Round %d: Arena %d is re-starting play (bye-swap) with players %d (%s) and %d (%s)\n", round, swapArena->GetArenaID(), swapArena->GetPlayers()[ 0 ]->entityNumber, swapArena->GetPlayers()[ 0 ]->GetUserInfo()->GetString( "ui_name" ), swapArena->GetPlayers()[ 1 ]->entityNumber, swapArena->GetPlayers()[ 1 ]->GetUserInfo()->GetString( "ui_name" ) );
  1677.                     }
  1678.                 }    
  1679.  
  1680.                 // once the new round is setup, go through and make sure all the spectators are in valid arena
  1681.                 for( int i = 0; i < gameLocal.numClients; i++ ) {
  1682.                     idPlayer* p = (idPlayer*)gameLocal.entities[ i ];
  1683.  
  1684.                     // re-select our arena since round changed
  1685.                     if( p && p->spectating ) {
  1686.                         rvTourneyArena& thisArena = arenas[ p->GetArena() ];
  1687.                         if( thisArena.GetState() != AS_WARMUP ) {
  1688.                             p->JoinInstance( GetNextActiveArena( p->GetArena() ) );
  1689.                         }
  1690.                     }
  1691.                 }
  1692.             }
  1693.  
  1694.             break;
  1695.         }
  1696.     }
  1697. }
  1698.  
  1699. /*
  1700. ================
  1701. rvTourneyGameState::NewState
  1702. ================
  1703. */
  1704. void rvTourneyGameState::NewState( mpGameState_t newState ) {
  1705.     assert( (newState != currentState) && gameLocal.isServer );
  1706.  
  1707.     switch( newState ) {
  1708.         case WARMUP: {
  1709.             Reset();
  1710.             break;
  1711.         }
  1712.         case GAMEON: {
  1713.             tourneyCount++;
  1714.             SetupInitialBrackets();
  1715.             break;
  1716.         }
  1717.     }
  1718.  
  1719.     rvGameState::NewState( newState );
  1720. }
  1721.  
  1722. /*
  1723. ================
  1724. rvTourneyGameState::GameStateChanged
  1725. ================
  1726. */
  1727. void rvTourneyGameState::GameStateChanged( void ) {
  1728.     assert( type == GS_TOURNEY );
  1729.     rvGameState::GameStateChanged();
  1730.     idPlayer* localPlayer = gameLocal.GetLocalPlayer();
  1731.  
  1732.     int oldRound = ((rvTourneyGameState*)previousGameState)->round;
  1733.  
  1734.     if( round != oldRound ) {
  1735.         if ( round > maxRound && gameLocal.GetLocalPlayer() ) {
  1736.             gameLocal.GetLocalPlayer()->ForceScoreboard( true, gameLocal.time + 5000 );
  1737.             gameLocal.mpGame.ScheduleAnnouncerSound( AS_TOURNEY_DONE, gameLocal.time );
  1738.         }
  1739.  
  1740.  
  1741.         // we're starting a new round
  1742.         gameLocal.mpGame.tourneyGUI.RoundStart();
  1743.             
  1744.         // play the sound a bit after round restart to let spawn sounds settle
  1745.         gameLocal.mpGame.ScheduleAnnouncerSound( (announcerSound_t)(AS_TOURNEY_PRELIMS + idMath::ClampInt( 1, maxRound, round - 1 ) ), gameLocal.time + 1500);
  1746.     }
  1747.  
  1748.     int byeArena = -1;
  1749.  
  1750.     for( int i = 0; i < MAX_ARENAS; i++ ) {
  1751.         rvTourneyArena& thisArena = arenas[ i ];
  1752.         rvTourneyArena& previousArena = ((rvTourneyGameState*)previousGameState)->arenas[ i ];
  1753.         
  1754.         if( thisArena.GetState() == AS_INACTIVE && byeArena == -1 ) {
  1755.             byeArena = i;
  1756.         }
  1757.  
  1758.         if( (thisArena.GetPlayers()[ 0 ] != previousArena.GetPlayers()[ 0 ]) ||
  1759.             (thisArena.GetPlayers()[ 1 ] != previousArena.GetPlayers()[ 1 ]) ||
  1760.             (round != ((rvTourneyGameState*)previousGameState)->round ) ) {
  1761.             gameLocal.mpGame.tourneyGUI.ArenaStart( i );
  1762.             if( localPlayer && thisArena.GetPlayers()[ 0 ] == localPlayer ) {
  1763.                 gameLocal.mpGame.tourneyGUI.ArenaSelect( i, TGH_PLAYER_ONE );
  1764.             }
  1765.             if( localPlayer && thisArena.GetPlayers()[ 1 ] == localPlayer ) {
  1766.                 gameLocal.mpGame.tourneyGUI.ArenaSelect( i, TGH_PLAYER_TWO );
  1767.             }
  1768.  
  1769.             // on the client, add this arena to our tourneyHistory
  1770.             if( gameLocal.isClient && thisArena.GetPlayers()[ 0 ] && thisArena.GetPlayers()[ 1 ] && (round - 1 ) >= 0 && (round - 1) < MAX_ROUNDS )  {
  1771.                 tourneyHistory[ round - 1 ][ i ].playerOneNum = thisArena.GetPlayers()[ 0 ]->entityNumber;
  1772.                 tourneyHistory[ round - 1 ][ i ].playerTwoNum = thisArena.GetPlayers()[ 1 ]->entityNumber;
  1773.             }
  1774.         }
  1775.  
  1776.         if( thisArena.GetState() == AS_SUDDEN_DEATH && thisArena.GetState() != previousArena.GetState() ) {
  1777.             gameLocal.mpGame.ScheduleAnnouncerSound( AS_GENERAL_SUDDEN_DEATH, gameLocal.time, thisArena.GetArenaID() );
  1778.             gameLocal.GetLocalPlayer()->GUIMainNotice( common->GetLocalizedString( "#str_104287" ) );
  1779.         }
  1780.  
  1781.         if( thisArena.GetState() == AS_DONE && thisArena.GetState() != previousArena.GetState() ) {
  1782.             if( thisArena.GetWinner() != previousArena.GetWinner() && thisArena.GetWinner() == gameLocal.GetLocalPlayer() ) {
  1783.                 if( round >= maxRound ) {
  1784.                     gameLocal.mpGame.ScheduleAnnouncerSound( AS_TOURNEY_WON, gameLocal.time );
  1785.                 } else {
  1786.                      gameLocal.mpGame.ScheduleAnnouncerSound( AS_TOURNEY_ADVANCE, gameLocal.time );
  1787.                 }
  1788.             } else if( thisArena.GetLoser() != previousArena.GetLoser() && thisArena.GetLoser() == gameLocal.GetLocalPlayer() ) {
  1789.                 gameLocal.mpGame.ScheduleAnnouncerSound( AS_TOURNEY_ELIMINATED, gameLocal.time );
  1790.             }
  1791.  
  1792.             if( previousArena.GetWinner() == NULL && thisArena.GetWinner() != NULL ) {
  1793.                 gameLocal.mpGame.tourneyGUI.ArenaDone( i );
  1794.             }
  1795.  
  1796.             // on the client, add this result to our tourneyHistory
  1797.             if( gameLocal.isClient && thisArena.GetPlayers()[ 0 ] && thisArena.GetPlayers()[ 1 ] && (round - 1 ) >= 0 && (round - 1) < MAX_ROUNDS ) {
  1798.                 tourneyHistory[ round - 1 ][ i ].playerOneScore = gameLocal.mpGame.GetTeamScore( thisArena.GetPlayers()[ 0 ] );
  1799.                 tourneyHistory[ round - 1 ][ i ].playerTwoScore = gameLocal.mpGame.GetTeamScore( thisArena.GetPlayers()[ 1 ] );
  1800.             }
  1801.         }
  1802.  
  1803.         if( localPlayer && (thisArena.GetState() == AS_WARMUP) && (thisArena.GetState() != previousArena.GetState()) && localPlayer->GetArena() == thisArena.GetArenaID() ) {
  1804.             int warmupEndTime = gameLocal.time + ( gameLocal.serverInfo.GetInt( "si_countdown" ) * 1000 ) + 5000; 
  1805.             gameLocal.mpGame.ScheduleAnnouncerSound( AS_GENERAL_PREPARE_TO_FIGHT, gameLocal.time + 5000 );
  1806.             gameLocal.mpGame.ScheduleAnnouncerSound( AS_GENERAL_THREE, warmupEndTime - 3000 );
  1807.             gameLocal.mpGame.ScheduleAnnouncerSound( AS_GENERAL_TWO, warmupEndTime - 2000 );
  1808.             gameLocal.mpGame.ScheduleAnnouncerSound( AS_GENERAL_ONE, warmupEndTime - 1000 );
  1809.  
  1810.             localPlayer->vsMsgState = true;
  1811.             localPlayer->GUIMainNotice( va( "%s^0 vs. %s^0", thisArena.GetPlayerName( 0 ), thisArena.GetPlayerName( 1 ) ), true );
  1812.         }
  1813.  
  1814.         if( (thisArena.GetState() == AS_ROUND) && (thisArena.GetState() != previousArena.GetState()) ) {
  1815.               if( localPlayer && localPlayer->GetArena() == thisArena.GetArenaID() ) {
  1816.                   gameLocal.mpGame.ScheduleAnnouncerSound( AS_GENERAL_FIGHT, gameLocal.time );
  1817.               }
  1818.         }
  1819.     }
  1820.  
  1821.     if( ((rvTourneyGameState*)previousGameState)->byePlayer != byePlayer && byeArena >= 0 && byeArena < MAX_ARENAS ) {
  1822.         if( !byePlayer ) {
  1823.             tourneyHistory[ round - 1 ][ byeArena ].playerOneNum = -1;
  1824.         } else {
  1825.             tourneyHistory[ round - 1 ][ byeArena ].playerOneNum = byePlayer->entityNumber;
  1826.         }
  1827.     
  1828.         tourneyHistory[ round - 1 ][ byeArena ].playerTwoNum = -1;
  1829.         tourneyHistory[ round - 1 ][ byeArena ].playerOneScore = 0;
  1830.         tourneyHistory[ round - 1 ][ byeArena ].playerTwoScore = 0;
  1831.  
  1832.     }
  1833.  
  1834.     if( ((rvTourneyGameState*)previousGameState)->currentState != currentState ) {
  1835.         if( currentState == WARMUP ) {
  1836.             gameLocal.mpGame.tourneyGUI.PreTourney();
  1837.         } else if( currentState == COUNTDOWN ) {    
  1838.             if( currentState == COUNTDOWN && ((rvTourneyGameState*)previousGameState)->currentState != INACTIVE ) {
  1839.                 gameLocal.mpGame.ScheduleAnnouncerSound( AS_TOURNEY_START, gameLocal.time);
  1840.             }
  1841.         } else if( currentState == GAMEON ) {
  1842.             gameLocal.mpGame.tourneyGUI.TourneyStart();
  1843.         }
  1844.     }
  1845.  
  1846.     if( byePlayer != ((rvTourneyGameState*)previousGameState)->byePlayer ) {
  1847.         gameLocal.mpGame.tourneyGUI.UpdateByePlayer();
  1848.     }
  1849.  
  1850.     if( packTourneyHistory ) {
  1851.         // apply history
  1852.         gameLocal.mpGame.tourneyGUI.SetupTourneyHistory( startingRound, round - 1, tourneyHistory );
  1853.         packTourneyHistory = false;
  1854.     }
  1855.  
  1856.     if( localPlayer && localPlayer->spectating ) {
  1857.  
  1858.         rvTourneyArena& thisArena = arenas[ localPlayer->GetArena() ];
  1859.         if( thisArena.GetPlayers()[ 0 ] == NULL || thisArena.GetPlayers()[ 1 ] == NULL || (localPlayer->spectating && localPlayer->spectator != thisArena.GetPlayers()[ 0 ]->entityNumber && localPlayer->spectator != thisArena.GetPlayers()[ 1 ]->entityNumber) ) {
  1860.             gameLocal.mpGame.tourneyGUI.ArenaSelect( localPlayer->GetArena(), TGH_BRACKET );
  1861.         } else if ( thisArena.GetPlayers()[ 0 ] == localPlayer || (localPlayer->spectating && thisArena.GetPlayers()[ 0 ]->entityNumber == localPlayer->spectator) ) {
  1862.             gameLocal.mpGame.tourneyGUI.ArenaSelect( localPlayer->GetArena(), TGH_PLAYER_ONE );
  1863.         } else if ( thisArena.GetPlayers()[ 1 ] == localPlayer || (localPlayer->spectating && thisArena.GetPlayers()[ 1 ]->entityNumber == localPlayer->spectator) ) {
  1864.             gameLocal.mpGame.tourneyGUI.ArenaSelect( localPlayer->GetArena(), TGH_PLAYER_TWO );
  1865.         }
  1866.     }
  1867.  
  1868. }
  1869.  
  1870. /*
  1871. ================
  1872. msgTourney_t
  1873. Identifiers for transmitted state
  1874. ================
  1875. */
  1876. typedef enum {
  1877.     MSG_TOURNEY_TOURNEYSTATE,
  1878.     MSG_TOURNEY_STARTINGROUND,
  1879.     MSG_TOURNEY_ROUND,
  1880.     MSG_TOURNEY_MAXROUND,
  1881.     MSG_TOURNEY_BYEPLAYER,
  1882.     MSG_TOURNEY_HISTORY,
  1883.     MSG_TOURNEY_TOURNEYCOUNT,
  1884.     MSG_TOURNEY_ARENAINFO
  1885. } msgTourney_t;
  1886.  
  1887. /*
  1888. ================
  1889. rvTourneyGameState::PackState
  1890. ================
  1891. */
  1892. void rvTourneyGameState::PackState( idBitMsg& outMsg ) {
  1893.     assert( type == GS_TOURNEY );
  1894.     
  1895.     if( tourneyState != ((rvTourneyGameState*)previousGameState)->tourneyState ) {
  1896.         outMsg.WriteByte( MSG_TOURNEY_TOURNEYSTATE );
  1897.         outMsg.WriteByte( tourneyState );
  1898.     }
  1899.  
  1900.     if( startingRound != ((rvTourneyGameState*)previousGameState)->startingRound ) {
  1901.         outMsg.WriteByte( MSG_TOURNEY_STARTINGROUND );
  1902.         outMsg.WriteByte( startingRound );
  1903.     }
  1904.  
  1905.     if( round != ((rvTourneyGameState*)previousGameState)->round ) {
  1906.         outMsg.WriteByte( MSG_TOURNEY_ROUND );
  1907.         outMsg.WriteByte( round );
  1908.     }
  1909.  
  1910.     if( maxRound != ((rvTourneyGameState*)previousGameState)->maxRound ) {
  1911.         outMsg.WriteByte( MSG_TOURNEY_MAXROUND );
  1912.         outMsg.WriteByte( maxRound );
  1913.     }
  1914.     
  1915.     if( byePlayer != ((rvTourneyGameState*)previousGameState)->byePlayer ) {
  1916.         outMsg.WriteByte( MSG_TOURNEY_BYEPLAYER );
  1917.         if( byePlayer ) {
  1918.             outMsg.WriteByte( byePlayer->entityNumber );
  1919.         } else {
  1920.             outMsg.WriteByte( 255 );
  1921.         }
  1922.     }
  1923.  
  1924.     for( int i = 0; i < MAX_ARENAS; i++ ) {
  1925.         if( arenas[ i ] != ((rvTourneyGameState*)previousGameState)->arenas[ i ] ) {
  1926.             outMsg.WriteByte( MSG_TOURNEY_ARENAINFO + i );
  1927.             arenas[ i ].PackState( outMsg );
  1928.         }
  1929.     }
  1930.  
  1931.     if( packTourneyHistory ) {
  1932.         // don't write out uninitialized tourney history
  1933.         if( startingRound > 0 && round > 0 ) {
  1934.             outMsg.WriteByte( MSG_TOURNEY_HISTORY );
  1935.  
  1936.             // client might not yet have startingRound or round
  1937.             outMsg.WriteByte( startingRound ); 
  1938.             outMsg.WriteByte( round ); 
  1939.  
  1940.             for( int i = startingRound - 1; i < round - 1; i++ ) {
  1941.                 for( int j = 0; j < MAX_ARENAS / (i + 1); j++ ) {
  1942.                     outMsg.WriteString( tourneyHistory[ i ][ j ].playerOne, MAX_TOURNEY_HISTORY_NAME_LEN );
  1943.                     outMsg.WriteByte( tourneyHistory[ i ][ j ].playerOneScore );
  1944.                     outMsg.WriteString( tourneyHistory[ i ][ j ].playerTwo, MAX_TOURNEY_HISTORY_NAME_LEN );
  1945.                     outMsg.WriteByte( tourneyHistory[ i ][ j ].playerTwoScore );
  1946.                     outMsg.WriteByte( tourneyHistory[ i ][ j ].playerOneNum );
  1947.                     outMsg.WriteByte( tourneyHistory[ i ][ j ].playerTwoNum );
  1948.                 }
  1949.             }
  1950.             packTourneyHistory = false;
  1951.         }
  1952.     }
  1953.  
  1954.     if( tourneyCount != ((rvTourneyGameState*)previousGameState)->tourneyCount ) {
  1955.         outMsg.WriteByte( MSG_TOURNEY_TOURNEYCOUNT );
  1956.         outMsg.WriteByte( tourneyCount );
  1957.     }
  1958. }
  1959.  
  1960. /*
  1961. ================
  1962. rvTourneyGameState::UnpackState
  1963. ================
  1964. */
  1965. void rvTourneyGameState::UnpackState( const idBitMsg& inMsg ) {
  1966.     assert( type == GS_TOURNEY );
  1967.  
  1968.     while( inMsg.GetRemainingData() ) {
  1969.         int index = inMsg.ReadByte();
  1970.         
  1971.         switch( index ) {
  1972.             case MSG_TOURNEY_TOURNEYSTATE: {
  1973.                 tourneyState = (tourneyState_t)inMsg.ReadByte();
  1974.                 break;
  1975.             }
  1976.             case MSG_TOURNEY_STARTINGROUND: {
  1977.                 startingRound = inMsg.ReadByte();
  1978.                 break;
  1979.             }
  1980.             case MSG_TOURNEY_ROUND: {
  1981.                 round = inMsg.ReadByte();
  1982.                 // not a great way of doing this, should clamp the values
  1983.                 if( round == 255 ) {
  1984.                     round = -1;
  1985.                 }
  1986.                 break;
  1987.             }
  1988.             case MSG_TOURNEY_MAXROUND: {
  1989.                 maxRound = inMsg.ReadByte();
  1990.                 if( maxRound == 255 ) {
  1991.                     maxRound = -1;
  1992.                 }
  1993.                 break;
  1994.             }
  1995.             case MSG_TOURNEY_BYEPLAYER: {
  1996.                 int clientNum = inMsg.ReadByte();
  1997.                 if( clientNum >= 0 && clientNum < MAX_CLIENTS ) {
  1998.                     byePlayer = (idPlayer*)gameLocal.entities[ clientNum ];
  1999.                 } else {
  2000.                     byePlayer = NULL;
  2001.                 }
  2002.                 
  2003.                 break;
  2004.             }
  2005.             case MSG_TOURNEY_HISTORY: {
  2006.                 int startRound = inMsg.ReadByte();
  2007.                 int round = inMsg.ReadByte();
  2008.  
  2009.                 assert( round >= 1 ); // something is uninitialized
  2010.  
  2011.                 for( int i = startRound - 1; i < round - 1; i++ ) {
  2012.                     for( int j = 0; j < MAX_ARENAS / (i + 1); j++ ) {
  2013.                         inMsg.ReadString( tourneyHistory[ i ][ j ].playerOne, MAX_TOURNEY_HISTORY_NAME_LEN );
  2014.                         tourneyHistory[ i ][ j ].playerOneScore = inMsg.ReadByte();
  2015.                         inMsg.ReadString( tourneyHistory[ i ][ j ].playerTwo, MAX_TOURNEY_HISTORY_NAME_LEN );
  2016.                         tourneyHistory[ i ][ j ].playerTwoScore = inMsg.ReadByte();
  2017.                         tourneyHistory[ i ][ j ].playerOneNum = inMsg.ReadByte();
  2018.                         tourneyHistory[ i ][ j ].playerTwoNum = inMsg.ReadByte();
  2019.  
  2020.                         if( tourneyHistory[ i ][ j ].playerOneNum < 0 || tourneyHistory[ i ][ j ].playerOneNum >= MAX_CLIENTS ) {
  2021.                             tourneyHistory[ i ][ j ].playerOneNum = -1;
  2022.                         }
  2023.  
  2024.                         if( tourneyHistory[ i ][ j ].playerTwoNum < 0 || tourneyHistory[ i ][ j ].playerTwoNum >= MAX_CLIENTS ) {
  2025.                             tourneyHistory[ i ][ j ].playerTwoNum = -1;
  2026.                         }
  2027.                     }
  2028.                 }
  2029.  
  2030.                 // client side (and listen server) no reason to check for history change all the time
  2031.                 packTourneyHistory = true;
  2032.                 break;
  2033.             }
  2034.             case MSG_TOURNEY_TOURNEYCOUNT: {
  2035.                 tourneyCount = inMsg.ReadByte();
  2036.                 break;
  2037.             }
  2038.             default: {
  2039.                 if( index >= MSG_TOURNEY_ARENAINFO && index < MSG_TOURNEY_ARENAINFO + MAX_ARENAS ) {
  2040.                     arenas[ index - MSG_TOURNEY_ARENAINFO ].UnpackState( inMsg );
  2041.                 } else {
  2042.                     gameLocal.Error( "rvTourneyGameState::UnpackState() - Unknown data identifier '%d'\n", index );
  2043.                 }
  2044.                 break;
  2045.             }
  2046.         }
  2047.  
  2048.     }
  2049. }
  2050.  
  2051. /*
  2052. ================
  2053. rvTourneyGameState::SendState
  2054. ================
  2055. */
  2056. void rvTourneyGameState::SendState( int clientNum ) {
  2057.     idBitMsg    outMsg;
  2058.     byte        msgBuf[MAX_GAME_MESSAGE_SIZE];
  2059.  
  2060.     assert( gameLocal.isServer && trackPrevious && type == GS_TOURNEY );
  2061.  
  2062.     if ( clientNum == -1 && (rvTourneyGameState&)(*this) == (rvTourneyGameState&)(*previousGameState) ) {
  2063.         return;
  2064.     }
  2065.  
  2066.     outMsg.Init( msgBuf, sizeof( msgBuf ) );
  2067.     outMsg.WriteByte( GAME_RELIABLE_MESSAGE_GAMESTATE );
  2068.  
  2069.     WriteState( outMsg );
  2070.  
  2071.     networkSystem->ServerSendReliableMessage( clientNum, outMsg );
  2072.  
  2073.     // don't update the state if we are working for a single client
  2074.     if ( clientNum == -1 ) {
  2075.         outMsg.ReadByte(); // pop off the msg ID
  2076.         ReceiveState( outMsg );
  2077.     }
  2078. }
  2079.  
  2080. /*
  2081. ===============
  2082. rvTourneyGameState::WriteState
  2083. ===============
  2084. */
  2085. void rvTourneyGameState::WriteState( idBitMsg &msg ) {
  2086.     // send off base info
  2087.     rvGameState::PackState( msg );
  2088.     // add Tourney info
  2089.     PackState( msg );
  2090. }
  2091.  
  2092. /*
  2093. ================
  2094. rvTourneyGameState::ReceiveState
  2095. ================
  2096. */
  2097. void rvTourneyGameState::ReceiveState( const idBitMsg& msg ) {
  2098.     assert( type == GS_TOURNEY );
  2099.  
  2100.     rvGameState::UnpackState( msg );
  2101.     UnpackState( msg );
  2102.  
  2103.     if ( gameLocal.localClientNum >= 0 ) {
  2104.         GameStateChanged();
  2105.     }
  2106.  
  2107.     (rvTourneyGameState&)(*previousGameState) = (rvTourneyGameState&)(*this);
  2108. }
  2109.  
  2110. /*
  2111. ================
  2112. rvTourneyGameState::SendInitialState
  2113. ================
  2114. */
  2115. void rvTourneyGameState::SendInitialState( int clientNum ) {
  2116.     assert( type == GS_TOURNEY );
  2117.  
  2118.     rvTourneyGameState* previousState = (rvTourneyGameState*)previousGameState;
  2119.  
  2120.     rvTourneyGameState invalidState;
  2121.  
  2122.     previousGameState = &invalidState;
  2123.  
  2124.     
  2125.     // if the tourney has been going on, transmit the tourney history
  2126.     if( round > 0 ) {
  2127.         // comparing the tourney history for all state changes is wasteful when we really just want to send it to new clients
  2128.         packTourneyHistory = true;
  2129.     }
  2130.     
  2131.     SendState( clientNum );
  2132.  
  2133.     previousGameState = previousState;
  2134. }
  2135.  
  2136.  
  2137. /*
  2138. ================
  2139. rvTourneyGameState::operator==
  2140. ================
  2141. */
  2142. bool rvTourneyGameState::operator==( const rvTourneyGameState& rhs ) const {
  2143.     assert( type == GS_TOURNEY && rhs.type == GS_TOURNEY );
  2144.  
  2145.     if( (rvGameState&)(*this) != (rvGameState&)rhs ) {
  2146.         return false;
  2147.     }
  2148.  
  2149.     if( round != rhs.round || startingRound != rhs.startingRound || maxRound != rhs.maxRound || tourneyState != rhs.tourneyState || byePlayer != rhs.byePlayer ) {
  2150.         return false;
  2151.     }
  2152.  
  2153.     for( int i = 0; i < MAX_ARENAS; i++ ) {
  2154.         if( arenas[ i ] != rhs.arenas[ i ] ) {
  2155.             return false;
  2156.         }
  2157.     }
  2158.  
  2159.     return true;
  2160. }
  2161.  
  2162. /*
  2163. ================
  2164. rvTourneyGameState::operator=
  2165. ================
  2166. */
  2167. rvTourneyGameState& rvTourneyGameState::operator=( const rvTourneyGameState& rhs ) {
  2168.     assert( type == GS_TOURNEY && rhs.type == GS_TOURNEY );
  2169.  
  2170.     (rvGameState&)(*this) = (rvGameState&)rhs;
  2171.  
  2172.     round = rhs.round;
  2173.     startingRound = rhs.startingRound;
  2174.     maxRound = rhs.maxRound;
  2175.     tourneyState = rhs.tourneyState;
  2176.  
  2177.     byePlayer = rhs.byePlayer;
  2178.  
  2179.     for( int i = 0; i < MAX_ARENAS; i++ ) {
  2180.         arenas[ i ] = rhs.arenas[ i ];
  2181.     }
  2182.     
  2183.     return (*this);
  2184. }
  2185.  
  2186. /*
  2187. ================
  2188. rvTourneyGameState::GetNumArenas
  2189. Returns number of active arenas
  2190. ================
  2191. */
  2192. int rvTourneyGameState::GetNumArenas( void ) const {
  2193.     assert( type == GS_TOURNEY );
  2194.  
  2195.     int num = 0;
  2196.     
  2197.     for( int i = 0; i < MAX_ARENAS; i++ ) {
  2198.         if( arenas[ i ].GetState() != AS_INACTIVE && arenas[ i ].GetState() != AS_DONE ) {
  2199.             num++;
  2200.         }
  2201.     }
  2202.  
  2203.     return num;
  2204. }
  2205. /*
  2206. ================
  2207. rvTourneyGameState::SetupInitialBrackets
  2208.  
  2209. Sets up the brackets for a new match.  fragCount in playerstate is the player's
  2210. persistant frag count over an entire level.  In teamFragCount we store this
  2211. rounds score.
  2212. ================
  2213. */
  2214. void rvTourneyGameState::SetupInitialBrackets( void ) {
  2215.     // re-rank players based on frags for bracket calculations
  2216.     gameLocal.mpGame.UpdatePlayerRanks( PRM_SCORE );
  2217.  
  2218.     // setup pairwise brackets (best versus worst)
  2219.     int numRankedPlayers = gameLocal.mpGame.GetNumRankedPlayers();
  2220.  
  2221.     // all this crazy math does is figure out: 
  2222.     //    8 arenas to 4 rounds ( round 1: 8 arenas, round 2: 4 arenas, round 3: 2 arenas, round 4: 1 arena )
  2223.     //    16 arenas to 5 rounds ( round 1: 16 arenas, round 2: 8 arenas, round 3: 4 arenas, round 4: 2 arenas, round 5: 1 arena )
  2224.     //  etc
  2225.     int newMaxRound = idMath::ILog2( Max( idMath::CeilPowerOfTwo( MAX_ARENAS * 2 ), 2 ) );
  2226.  
  2227.     // we start at a round appropriate for our # of people
  2228.     // If you have 1-2 players, start in maxRound,  if you have 3-4 players start in maxRound - 1, if you have 5-8 players
  2229.     // start in maxRound - 2, if you have 9 - 16 players start in maxRound - 3, etc.
  2230.     int newRound = newMaxRound - idMath::ILog2( Max( idMath::CeilPowerOfTwo( gameLocal.mpGame.GetNumRankedPlayers() ), 2 ) ) + 1;
  2231.  
  2232.     round = newRound;
  2233.     maxRound = newMaxRound;
  2234.     startingRound = round;
  2235.  
  2236.     for( int i = 0, j = numRankedPlayers - 1; i < (numRankedPlayers / 2); i++, j-- ) {    
  2237.         if( i >= MAX_ARENAS ) {
  2238.             // the rest of players will have to spectate
  2239.             break;
  2240.         }
  2241.         idPlayer* playerOne = gameLocal.mpGame.GetRankedPlayer( i );
  2242.         idPlayer* playerTwo = gameLocal.mpGame.GetRankedPlayer( j );
  2243.  
  2244.         playerOne->SetTourneyStatus( PTS_PLAYING );
  2245.         playerTwo->SetTourneyStatus( PTS_PLAYING );
  2246.  
  2247.         rvTourneyArena& arena = arenas[ i ];
  2248.  
  2249.         arena.Clear();
  2250.         arena.AddPlayers( playerOne, playerTwo );
  2251.  
  2252.         playerOne->ServerSpectate( false );
  2253.         playerTwo->ServerSpectate( false );
  2254.  
  2255.         gameLocal.mpGame.SetPlayerTeamScore( playerOne, 0 );
  2256.         gameLocal.mpGame.SetPlayerTeamScore( playerTwo, 0 );
  2257.  
  2258.         // place the players in the correct instance
  2259.         playerOne->JoinInstance( i );
  2260.         playerTwo->JoinInstance( i );
  2261.  
  2262.         gameLocal.Printf( "rvTourneyGameState::SetupInitialBrackets() - %s will face %s in arena %d\n", playerOne->GetUserInfo()->GetString( "ui_name" ), playerTwo->GetUserInfo()->GetString( "ui_name" ), i );
  2263.  
  2264.         // this arena is ready to play
  2265.         arena.Ready();
  2266.     }
  2267.  
  2268.     if( (numRankedPlayers % 2) ) {
  2269.         // the mid player gets a bye
  2270.         byePlayer = gameLocal.mpGame.GetRankedPlayer( numRankedPlayers / 2 );
  2271.         byePlayer->SetTourneyStatus( PTS_UNKNOWN );
  2272.         gameLocal.mpGame.GetRankedPlayer( numRankedPlayers / 2 )->ServerSpectate( true );
  2273.     } else {
  2274.         byePlayer = NULL;
  2275.     }
  2276. }
  2277.  
  2278. /*
  2279. ================
  2280. rvTourneyGameState::ClientDisconnect
  2281. A player has disconnected from the server
  2282. ================
  2283. */
  2284. void rvTourneyGameState::ClientDisconnect( idPlayer* player ) {
  2285.     if( gameLocal.isClient || gameLocal.GameState() == GAMESTATE_SHUTDOWN ) {  // rj 9557
  2286.         return;
  2287.     }
  2288.     
  2289.     // go through the tourney history and copy over the disconnecting player's name
  2290.     if( startingRound > 0 && round <= MAX_ROUNDS ) {
  2291.         for( int i = startingRound - 1; i < round - 1; i++ ) {
  2292.             for( int j = 0; j < MAX_ARENAS / (i + 1); j++ ) {
  2293.                 if( tourneyHistory[ i ][ j ].playerOneNum == player->entityNumber ) {
  2294.                     idStr::Copynz( tourneyHistory[ i ][ j ].playerOne, player->GetUserInfo()->GetString( "ui_name" ), MAX_TOURNEY_HISTORY_NAME_LEN );
  2295.                     tourneyHistory[ i ][ j ].playerOneNum = -1;
  2296.                 } else if( tourneyHistory[ i ][ j ].playerTwoNum == player->entityNumber ) {
  2297.                     idStr::Copynz( tourneyHistory[ i ][ j ].playerTwo, player->GetUserInfo()->GetString( "ui_name" ), MAX_TOURNEY_HISTORY_NAME_LEN );
  2298.                     tourneyHistory[ i ][ j ].playerTwoNum = -1;
  2299.                 }
  2300.             }
  2301.         }
  2302.  
  2303.         // retransmit tourney history to everyone
  2304.         packTourneyHistory = true;
  2305.         for( int i = 0; i < gameLocal.numClients; i++ ) {
  2306.             if( i == player->entityNumber ) {
  2307.                 continue;
  2308.             }
  2309.  
  2310.             if( gameLocal.entities[ i ] ) {
  2311.                 SendState( i );
  2312.             }
  2313.         }
  2314.     }
  2315.  
  2316.     RemovePlayer( player, true );
  2317. }
  2318.  
  2319. /*
  2320. ================
  2321. rvTourneyGameState::Spectate
  2322. ================
  2323. */
  2324. void rvTourneyGameState::Spectate( idPlayer* player ) {
  2325.     assert( gameLocal.isServer );
  2326.  
  2327.     if( player->spectating && player->wantSpectate ) {
  2328.         RemovePlayer( player, false );
  2329.     }
  2330. }
  2331.  
  2332. /*
  2333. ================
  2334. rvTourneyGameState::RemovePlayer
  2335. Removes the specified player from the arena
  2336. ================
  2337. */
  2338. void rvTourneyGameState::RemovePlayer( idPlayer* player, bool disconnecting ) {
  2339.     // if we were the byeplayer and we dropped, just set the byeplayer to null
  2340.     if( player == byePlayer ) {
  2341.         byePlayer = NULL;
  2342.         return;
  2343.     }
  2344.  
  2345.     for( int i = 0; i < MAX_ARENAS; i++ ) {
  2346.         idPlayer** players = GetArenaPlayers( i );
  2347.  
  2348.         if( players[ 0 ] == player || players[ 1 ] == player ) {
  2349.             rvTourneyArena& arena = arenas[ i ];
  2350.  
  2351.             idPlayer* remainingPlayer = players[ 0 ] == player ? players[ 1 ] : players[ 0 ];
  2352.  
  2353.             assert( remainingPlayer );
  2354.  
  2355.             arena.RemovePlayer( player, disconnecting );
  2356.  
  2357.             // reassign remaining player
  2358.             if( byePlayer ) {
  2359.                 arena.AddPlayers( remainingPlayer, byePlayer );
  2360.  
  2361.                 remainingPlayer->ServerSpectate( false );
  2362.                 byePlayer->ServerSpectate( false );
  2363.  
  2364.                 gameLocal.mpGame.SetPlayerTeamScore( remainingPlayer, 0 );
  2365.                 gameLocal.mpGame.SetPlayerTeamScore( byePlayer, 0 );
  2366.  
  2367.                 // place the players in the correct instance
  2368.                 remainingPlayer->JoinInstance( i );
  2369.                 byePlayer->JoinInstance( i );
  2370.  
  2371.                 gameLocal.Printf( "rvTourneyManager::RemovePlayer() - %s will face %s in arena %d\n", remainingPlayer->GetUserInfo()->GetString( "ui_name" ), byePlayer->GetUserInfo()->GetString( "ui_name" ), i );
  2372.  
  2373.                 byePlayer = NULL;
  2374.  
  2375.                 // this arena is ready to play
  2376.                 arena.Ready();
  2377.             } else {
  2378.                 // set bye player and move them to the next available arena
  2379.                 byePlayer = remainingPlayer;
  2380.                 byePlayer->JoinInstance( GetNextActiveArena( arena.GetArenaID() ) );
  2381.             }
  2382.  
  2383.             return;
  2384.         }
  2385.     }
  2386. }
  2387.  
  2388. int rvTourneyGameState::GetNextActiveArena( int arena ) {
  2389.     assert( type == GS_TOURNEY );
  2390.  
  2391.     for( int i = arena + 1; i < MAX_ARENAS; i++ ) {
  2392.         if( arenas[ i ].GetState() != AS_INACTIVE && arenas[ i ].GetState() != AS_DONE ) {
  2393.             return i;
  2394.         }
  2395.     }
  2396.  
  2397.     for( int i = 0; i < arena; i++ ) {
  2398.         if( arenas[ i ].GetState() != AS_INACTIVE && arenas[ i ].GetState() != AS_DONE ) {
  2399.             return i;
  2400.         }
  2401.     }
  2402.  
  2403.     return arena;
  2404. }
  2405.  
  2406. int rvTourneyGameState::GetPrevActiveArena( int arena ) {
  2407.     assert( type == GS_TOURNEY );
  2408.  
  2409.     for( int i = arena - 1; i >= 0; i-- ) {
  2410.         if( arenas[ i ].GetState() != AS_INACTIVE && arenas[ i ].GetState() != AS_DONE ) {
  2411.             return i;
  2412.         }
  2413.     }
  2414.  
  2415.     for( int i = MAX_ARENAS - 1; i > arena; i-- ) {
  2416.         if( arenas[ i ].GetState() != AS_INACTIVE && arenas[ i ].GetState() != AS_DONE ) {
  2417.             return i;
  2418.         }
  2419.     }
  2420.  
  2421.     return arena;
  2422. }
  2423.  
  2424. void rvTourneyGameState::SpectateCycleNext( idPlayer* player ) {
  2425.     assert( gameLocal.isServer );
  2426.  
  2427.     rvTourneyArena& spectatingArena = arenas[ player->GetArena() ];
  2428.     
  2429.     idPlayer** players = spectatingArena.GetPlayers();
  2430.  
  2431.     if( !players[ 0 ] || !players[ 1 ] || players[ 0 ]->spectating || players[ 1 ]->spectating ) {
  2432.         // setting the spectated client to ourselves will unlock us
  2433.         player->spectator = player->entityNumber;
  2434.         return;
  2435.     }
  2436.  
  2437.     if( player->spectator != players[ 0 ]->entityNumber && player->spectator != players[ 1 ]->entityNumber ) {
  2438.         player->spectator = players[ 0 ]->entityNumber;
  2439.     } else if( player->spectator == players[ 0 ]->entityNumber ) {
  2440.         player->spectator = players[ 1 ]->entityNumber;
  2441.     } else if( player->spectator == players[ 1 ]->entityNumber ) {
  2442.         if( gameLocal.time > player->lastArenaChange ) {
  2443.             if ( GetNumArenas() <= 0 ) {
  2444.                 player->JoinInstance( 0 );
  2445.             } else {
  2446.                 player->JoinInstance( GetNextActiveArena( player->GetArena() ) );
  2447.             }
  2448.             player->lastArenaChange = gameLocal.time + 2000;
  2449.             player->spectator = player->entityNumber;
  2450.         }
  2451.     }
  2452.  
  2453.     // this is where the listen server updates it gui spectating elements
  2454.     if( gameLocal.GetLocalPlayer() == player ) {
  2455.         rvTourneyArena& arena = arenas[ player->GetArena() ];
  2456.  
  2457.         if( arena.GetPlayers()[ 0 ] == NULL || arena.GetPlayers()[ 1 ] == NULL || (player->spectating && player->spectator != arena.GetPlayers()[ 0 ]->entityNumber && player->spectator != arena.GetPlayers()[ 1 ]->entityNumber) ) {
  2458.             gameLocal.mpGame.tourneyGUI.ArenaSelect( player->GetArena(), TGH_BRACKET );
  2459.         } else if( arena.GetPlayers()[ 0 ] == player || player->spectator == arena.GetPlayers()[ 0 ]->entityNumber ) {
  2460.             gameLocal.mpGame.tourneyGUI.ArenaSelect( player->GetArena(), TGH_PLAYER_ONE );
  2461.         } else if( arena.GetPlayers()[ 1 ] == player || player->spectator == arena.GetPlayers()[ 1 ]->entityNumber ) {
  2462.             gameLocal.mpGame.tourneyGUI.ArenaSelect( player->GetArena(), TGH_PLAYER_TWO );
  2463.         }
  2464.  
  2465.         gameLocal.mpGame.tourneyGUI.UpdateScores();
  2466.     }
  2467. }
  2468.  
  2469. void rvTourneyGameState::SpectateCyclePrev( idPlayer* player ) {
  2470.     assert( gameLocal.isServer );
  2471.  
  2472.     rvTourneyArena& spectatingArena = arenas[ player->GetArena() ];
  2473.  
  2474.     idPlayer** players = spectatingArena.GetPlayers();
  2475.  
  2476.     if( !players[ 0 ] || !players[ 1 ] || players[ 0 ]->spectating || players[ 1 ]->spectating ) {
  2477.         // setting the spectated client to ourselves will unlock us
  2478.         player->spectator = player->entityNumber;
  2479.         return;
  2480.     }
  2481.  
  2482.     if( player->spectator != players[ 0 ]->entityNumber && player->spectator != players[ 1 ]->entityNumber ) {
  2483.         if( gameLocal.time > player->lastArenaChange ) {
  2484.             if ( GetNumArenas() <= 0 ) {
  2485.                 player->JoinInstance( 0 );
  2486.             } else {
  2487.                 player->JoinInstance( GetPrevActiveArena( player->GetArena() ) );
  2488.             }
  2489.             player->lastArenaChange = gameLocal.time + 2000;
  2490.             
  2491.             rvTourneyArena& newSpectatingArena = arenas[ player->GetArena() ];
  2492.         
  2493.             idPlayer** newPlayers = newSpectatingArena.GetPlayers();
  2494.  
  2495.             if( !newPlayers[ 0 ] || !newPlayers[ 1 ] || newPlayers[ 0 ]->spectating || newPlayers[ 1 ]->spectating ) {
  2496.                 // setting the spectated client to ourselves will unlock us
  2497.                 player->spectator = player->entityNumber;
  2498.                 return;
  2499.             }
  2500.  
  2501.             player->spectator = newPlayers[ 1 ]->entityNumber;
  2502.         } 
  2503.     } else if( player->spectator == players[ 0 ]->entityNumber ) {
  2504.         player->spectator = player->entityNumber;
  2505.     } else if( player->spectator == players[ 1 ]->entityNumber ) {
  2506.         player->spectator = players[ 0 ]->entityNumber;
  2507.     }
  2508.  
  2509.     // this is where the listen server updates it gui spectating elements
  2510.     if( gameLocal.GetLocalPlayer() == player ) {
  2511.         rvTourneyArena& arena = arenas[ player->GetArena() ];
  2512.  
  2513.         if( arena.GetPlayers()[ 0 ] == NULL || arena.GetPlayers()[ 1 ] == NULL || (player->spectating && player->spectator != arena.GetPlayers()[ 0 ]->entityNumber && player->spectator != arena.GetPlayers()[ 1 ]->entityNumber) ) {
  2514.             gameLocal.mpGame.tourneyGUI.ArenaSelect( player->GetArena(), TGH_BRACKET );
  2515.         } else if( arena.GetPlayers()[ 0 ] == player || player->spectator == arena.GetPlayers()[ 0 ]->entityNumber ) {
  2516.             gameLocal.mpGame.tourneyGUI.ArenaSelect( player->GetArena(), TGH_PLAYER_ONE );
  2517.         } else if( arena.GetPlayers()[ 1 ] == player || player->spectator == arena.GetPlayers()[ 1 ]->entityNumber ) {
  2518.             gameLocal.mpGame.tourneyGUI.ArenaSelect( player->GetArena(), TGH_PLAYER_TWO );
  2519.         }
  2520.  
  2521.         gameLocal.mpGame.tourneyGUI.UpdateScores();
  2522.     }
  2523. }
  2524.  
  2525. void rvTourneyGameState::UpdateTourneyBrackets( void ) {
  2526.     gameLocal.mpGame.tourneyGUI.SetupTourneyHistory( startingRound, round - 1, tourneyHistory );
  2527. }
  2528.