3134 lines
92 KiB
C++
3134 lines
92 KiB
C++
|
//----------------------------------------------------------------
|
||
|
// GameState.cpp
|
||
|
//
|
||
|
// Copyright 2002-2005 Raven Software
|
||
|
//----------------------------------------------------------------
|
||
|
|
||
|
#include "../../idlib/precompiled.h"
|
||
|
#pragma hdrstop
|
||
|
|
||
|
#include "GameState.h"
|
||
|
|
||
|
/*
|
||
|
===============================================================================
|
||
|
|
||
|
rvGameState
|
||
|
|
||
|
Game state info for deathmatch, team deathmatch
|
||
|
|
||
|
===============================================================================
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
rvGameState::rvGameState
|
||
|
================
|
||
|
*/
|
||
|
rvGameState::rvGameState( bool allocPrevious ) {
|
||
|
Clear();
|
||
|
|
||
|
if( allocPrevious ) {
|
||
|
previousGameState = new rvGameState( false );
|
||
|
} else {
|
||
|
previousGameState = NULL;
|
||
|
}
|
||
|
|
||
|
trackPrevious = allocPrevious;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
rvGameState::~rvGameState
|
||
|
================
|
||
|
*/
|
||
|
rvGameState::~rvGameState( void ) {
|
||
|
Clear();
|
||
|
delete previousGameState;
|
||
|
previousGameState = NULL;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
rvGameState::Clear
|
||
|
================
|
||
|
*/
|
||
|
void rvGameState::Clear( void ) {
|
||
|
currentState = INACTIVE;
|
||
|
nextState = INACTIVE;
|
||
|
nextStateTime = 0;
|
||
|
fragLimitTimeout = 0;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
rvGameState::SendState
|
||
|
================
|
||
|
*/
|
||
|
void rvGameState::SendState( const idMessageSender &sender, int clientNum ) {
|
||
|
idBitMsg outMsg;
|
||
|
byte msgBuf[MAX_GAME_MESSAGE_SIZE];
|
||
|
|
||
|
assert( (gameLocal.isServer || gameLocal.isRepeater) && trackPrevious );
|
||
|
|
||
|
if( clientNum == -1 && (*this) == (*previousGameState) ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
outMsg.Init( msgBuf, sizeof( msgBuf ) );
|
||
|
outMsg.WriteByte( GAME_RELIABLE_MESSAGE_GAMESTATE );
|
||
|
|
||
|
WriteState( outMsg );
|
||
|
|
||
|
sender.Send( outMsg );
|
||
|
|
||
|
// don't update the state if we are working for a single client
|
||
|
if ( clientNum == -1 ) {
|
||
|
outMsg.ReadByte(); // pop off the msg ID
|
||
|
ReceiveState( outMsg );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===============
|
||
|
rvGameState::WriteState
|
||
|
===============
|
||
|
*/
|
||
|
void rvGameState::WriteState( idBitMsg &msg ) {
|
||
|
PackState( msg );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
rvGameState::SendInitialState
|
||
|
================
|
||
|
*/
|
||
|
void rvGameState::SendInitialState( const idMessageSender &sender, int clientNum ) {
|
||
|
rvGameState* previousState = previousGameState;
|
||
|
|
||
|
rvGameState invalidState;
|
||
|
|
||
|
previousGameState = &invalidState;
|
||
|
|
||
|
SendState( sender, clientNum );
|
||
|
|
||
|
previousGameState = previousState;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
rvGameState::ReceiveState
|
||
|
================
|
||
|
*/
|
||
|
void rvGameState::ReceiveState( const idBitMsg& msg ) {
|
||
|
if ( !BaseUnpackState( msg ) ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if ( gameLocal.localClientNum >= 0 ) {
|
||
|
GameStateChanged();
|
||
|
}
|
||
|
|
||
|
(*previousGameState) = (*this);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
rvGameState::PackState
|
||
|
================
|
||
|
*/
|
||
|
void rvGameState::PackState( idBitMsg& outMsg ) {
|
||
|
// server and client changing their rvGameState subclass by guesswork
|
||
|
// you can't rely on it being in sync at all times, so read and verify the type first
|
||
|
outMsg.WriteByte( gameLocal.gameType );
|
||
|
|
||
|
// for now, we only transmit 3 bytes. If we need to sync more data, we should
|
||
|
// only transmit the diff
|
||
|
outMsg.WriteByte( currentState );
|
||
|
outMsg.WriteByte( nextState );
|
||
|
outMsg.WriteLong( nextStateTime );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
rvGameState::BaseUnpackState
|
||
|
================
|
||
|
*/
|
||
|
bool rvGameState::BaseUnpackState( const idBitMsg& inMsg ) {
|
||
|
gameType_t t = (gameType_t)inMsg.ReadByte();
|
||
|
if ( t != gameLocal.gameType ) {
|
||
|
common->Warning( "rvGameState::UnpackState: client gametype (%d) is out of sync with server (%d). Ignoring", gameLocal.gameType, t );
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
currentState = (mpGameState_t)inMsg.ReadByte();
|
||
|
nextState = (mpGameState_t)inMsg.ReadByte();
|
||
|
nextStateTime = inMsg.ReadLong();
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
rvGameState::GameStateChanged
|
||
|
================
|
||
|
*/
|
||
|
void rvGameState::GameStateChanged( void ) {
|
||
|
idPlayer* player = gameLocal.GetLocalPlayer();
|
||
|
if ( !player ) {
|
||
|
if ( gameLocal.IsServerDemoPlaying() && gameLocal.GetDemoFollowClient() >= 0 ) {
|
||
|
player = static_cast< idPlayer * >( gameLocal.entities[ gameLocal.GetDemoFollowClient() ] );
|
||
|
}
|
||
|
}
|
||
|
if ( !player ) {
|
||
|
gameLocal.Warning( "rvGameState::GameStateChanged() - NULL local player\n" ) ;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Check for a currentState change
|
||
|
if( currentState != previousGameState->currentState ) {
|
||
|
if( currentState == WARMUP ) {
|
||
|
if( gameLocal.gameType != GAME_TOURNEY ) {
|
||
|
player->GUIMainNotice( common->GetLocalizedString( "#str_107706" ), true );
|
||
|
}
|
||
|
soundSystem->SetActiveSoundWorld( true );
|
||
|
|
||
|
// reset stats on the client-side
|
||
|
if( gameLocal.isClient ) {
|
||
|
statManager->Init();
|
||
|
}
|
||
|
} else if( currentState == COUNTDOWN ) {
|
||
|
if( gameLocal.gameType != GAME_TOURNEY ) {
|
||
|
player->GUIMainNotice( common->GetLocalizedString( "#str_107706" ), true );
|
||
|
}
|
||
|
soundSystem->SetActiveSoundWorld(true);
|
||
|
if( gameLocal.gameType != GAME_TOURNEY && previousGameState->currentState != INACTIVE ) {
|
||
|
gameLocal.mpGame.RemoveAnnouncerSound( AS_GENERAL_PREPARE_TO_FIGHT );
|
||
|
gameLocal.mpGame.RemoveAnnouncerSound( AS_GENERAL_THREE );
|
||
|
gameLocal.mpGame.RemoveAnnouncerSound( AS_GENERAL_TWO );
|
||
|
gameLocal.mpGame.RemoveAnnouncerSound( AS_GENERAL_ONE );
|
||
|
|
||
|
gameLocal.mpGame.ScheduleAnnouncerSound( AS_GENERAL_PREPARE_TO_FIGHT, gameLocal.time );
|
||
|
gameLocal.mpGame.ScheduleAnnouncerSound( AS_GENERAL_THREE, nextStateTime - 3000 );
|
||
|
gameLocal.mpGame.ScheduleAnnouncerSound( AS_GENERAL_TWO, nextStateTime - 2000 );
|
||
|
gameLocal.mpGame.ScheduleAnnouncerSound( AS_GENERAL_ONE, nextStateTime - 1000 );
|
||
|
}
|
||
|
} else if( currentState == GAMEON ) {
|
||
|
if ( !player->vsMsgState ) {
|
||
|
player->GUIMainNotice( "" );
|
||
|
player->GUIFragNotice( "" );
|
||
|
} else {
|
||
|
player->vsMsgState = false;
|
||
|
}
|
||
|
if( gameLocal.gameType != GAME_TOURNEY ) {
|
||
|
gameLocal.mpGame.ScheduleAnnouncerSound( AS_GENERAL_FIGHT, gameLocal.time );
|
||
|
}
|
||
|
//if ( gameLocal.gameType == GAME_DEADZONE ) {
|
||
|
// if ( player->team == TEAM_MARINE )
|
||
|
// gameLocal.mpGame.ScheduleAnnouncerSound( AS_TEAM_JOIN_MARINE, gameLocal.time );
|
||
|
// else
|
||
|
// gameLocal.mpGame.ScheduleAnnouncerSound( AS_TEAM_JOIN_STROGG, gameLocal.time );
|
||
|
//}
|
||
|
cvarSystem->SetCVarString( "ui_ready", "Not Ready" );
|
||
|
soundSystem->SetActiveSoundWorld( true );
|
||
|
|
||
|
// clear stats on client
|
||
|
if( gameLocal.isClient ) {
|
||
|
statManager->Init();
|
||
|
}
|
||
|
} else if( currentState == SUDDENDEATH ) {
|
||
|
soundSystem->SetActiveSoundWorld( true );
|
||
|
gameLocal.mpGame.ScheduleAnnouncerSound( AS_GENERAL_SUDDEN_DEATH, gameLocal.time );
|
||
|
gameLocal.GetLocalPlayer()->GUIMainNotice( common->GetLocalizedString( "#str_104287" ) );
|
||
|
} else if( currentState == GAMEREVIEW ) {
|
||
|
// RITUAL BEGIN
|
||
|
gameLocal.mpGame.isBuyingAllowedRightNow = false;
|
||
|
// RITUAL END
|
||
|
gameLocal.mpGame.ShowStatSummary();
|
||
|
}
|
||
|
|
||
|
gameLocal.mpGame.ScheduleTimeAnnouncements( );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
rvGameState::SpawnDeadZonePowerup
|
||
|
================
|
||
|
*/
|
||
|
void rvGameState::SpawnDeadZonePowerup( void ) {
|
||
|
idEntity *ent;
|
||
|
riDeadZonePowerup* spawnSpot = 0;
|
||
|
int count = 0;
|
||
|
for ( ent = gameLocal.spawnedEntities.Next(); ent != NULL; ent = ent->spawnNode.Next() ) {
|
||
|
|
||
|
// If its not a DeadZone powerup then skip it
|
||
|
if ( !ent->IsType( riDeadZonePowerup::GetClassType() ) ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// Make sure its the right type first
|
||
|
riDeadZonePowerup* flag;
|
||
|
flag = static_cast<riDeadZonePowerup*>(ent);
|
||
|
if ( flag->powerup != POWERUP_DEADZONE || flag->IsVisible() ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if ( flag->spawnArgs.GetBool("dropped", "0") && !flag->IsVisible() ) {
|
||
|
flag->PostEventMS( &EV_Remove, 0 );
|
||
|
|
||
|
} else {
|
||
|
count++;
|
||
|
if ( !(rand()%(count)) ) {
|
||
|
spawnSpot = flag;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if ( spawnSpot ) {
|
||
|
spawnSpot->PostEventMS( &EV_RespawnItem, 0 );
|
||
|
spawnSpot->srvReady = 1; // Go ahead and set this, so the loop works properly.
|
||
|
} else {
|
||
|
gameLocal.Error("Couldn't find enough dead zone spawn spots for the number of dead zone artifacts specified in the map def!");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
rvGameState::Run
|
||
|
================
|
||
|
*/
|
||
|
void rvGameState::Run( void ) {
|
||
|
if ( currentState == INACTIVE ) {
|
||
|
|
||
|
#ifdef _XENON
|
||
|
if(Live()->RoundsPlayed() < cvarSystem->GetCVarInteger("si_matchRounds"))
|
||
|
#endif
|
||
|
{
|
||
|
NewState( WARMUP );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( nextState != INACTIVE && gameLocal.time > nextStateTime ) {
|
||
|
NewState( nextState );
|
||
|
nextState = INACTIVE;
|
||
|
}
|
||
|
|
||
|
switch( currentState ) {
|
||
|
case INACTIVE:
|
||
|
break;
|
||
|
|
||
|
case GAMEREVIEW: {
|
||
|
if ( nextState == INACTIVE ) {
|
||
|
|
||
|
statManager->SendAllStats();
|
||
|
|
||
|
nextState = NEXTGAME;
|
||
|
|
||
|
// allow a little extra time in tourney since we have to display end brackets
|
||
|
if( gameLocal.gameType == GAME_TOURNEY ) {
|
||
|
nextStateTime = gameLocal.time + 5000 + (1000 * cvarSystem->GetCVarInteger( "g_gameReviewPause" ));
|
||
|
} else {
|
||
|
nextStateTime = gameLocal.time + (1000 * cvarSystem->GetCVarInteger( "g_gameReviewPause" ));
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
case NEXTGAME: {
|
||
|
|
||
|
// the core will force a restart at 12 hours max
|
||
|
// but it's nicer if we can wait for a game transition to perform the restart so the game is not interrupted
|
||
|
// perform a restart once we are past 8 hours
|
||
|
if ( networkSystem->ServerGetServerTime() > 8*60*60*1000 ) {
|
||
|
gameLocal.sessionCommand = "nextMap";
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if ( nextState == INACTIVE ) {
|
||
|
// game rotation, new map, gametype etc.
|
||
|
// only cycle in tourney if tourneylimit is higher than the specified value
|
||
|
if( gameLocal.gameType != GAME_TOURNEY || ((rvTourneyGameState*)this)->GetTourneyCount() >= gameLocal.serverInfo.GetInt( "si_tourneyLimit" ) ) {
|
||
|
// whether we switch to the next map or not, reset the tourney count
|
||
|
if( gameLocal.gameType == GAME_TOURNEY ) {
|
||
|
((rvTourneyGameState*)this)->SetTourneyCount( 0 );
|
||
|
}
|
||
|
|
||
|
|
||
|
if ( gameLocal.NextMap() ) {
|
||
|
cmdSystem->BufferCommandText( CMD_EXEC_NOW, "serverMapRestart\n" );
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
NewState( WARMUP );
|
||
|
|
||
|
// put everyone back in from endgame spectate
|
||
|
for ( int i = 0; i < gameLocal.numClients; i++ ) {
|
||
|
idEntity *ent = gameLocal.entities[ i ];
|
||
|
if ( ent && ent->IsType( idPlayer::GetClassType() ) ) {
|
||
|
if ( !static_cast< idPlayer * >( ent )->wantSpectate ) {
|
||
|
gameLocal.mpGame.CheckRespawns( static_cast<idPlayer *>( ent ) );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
case WARMUP: {
|
||
|
// check to see if we actually want to do a warmup, or if we fall through
|
||
|
|
||
|
//RAVEN BEGIN
|
||
|
//asalmon: Live has its own rules for ending warm up
|
||
|
#ifdef _XENON
|
||
|
if(!Live()->RollCall())
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
#endif
|
||
|
//RAVEN END
|
||
|
if( !gameLocal.serverInfo.GetBool( "si_warmup" ) && gameLocal.gameType != GAME_TOURNEY ) {
|
||
|
// tourney always needs a warmup, to ensure that at least 2 players get seeded for the tournament.
|
||
|
NewState( GAMEON );
|
||
|
} else if ( gameLocal.mpGame.AllPlayersReady() ) {
|
||
|
NewState( COUNTDOWN );
|
||
|
nextState = GAMEON;
|
||
|
nextStateTime = gameLocal.time + 1000 * gameLocal.serverInfo.GetInt( "si_countDown" );
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
case COUNTDOWN: {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
rvGameState::NewState
|
||
|
================
|
||
|
*/
|
||
|
void rvGameState::NewState( mpGameState_t newState ) {
|
||
|
idBitMsg outMsg;
|
||
|
byte msgBuf[MAX_GAME_MESSAGE_SIZE];
|
||
|
int i;
|
||
|
|
||
|
assert( (newState != currentState) && gameLocal.isServer );
|
||
|
|
||
|
switch( newState ) {
|
||
|
case WARMUP: {
|
||
|
// asalmon: start the stat manager as soon as the game starts
|
||
|
statManager->Init();
|
||
|
statManager->BeginGame();
|
||
|
|
||
|
// if shuffle is on, shuffle the teams around
|
||
|
if( gameLocal.IsTeamGame() && gameLocal.serverInfo.GetBool( "si_shuffle" ) ) {
|
||
|
gameLocal.mpGame.ShuffleTeams();
|
||
|
}
|
||
|
|
||
|
// allow damage in warmup
|
||
|
//gameLocal.mpGame.EnableDamage( false );
|
||
|
|
||
|
//asalmon: clear out lingering team scores.
|
||
|
gameLocal.mpGame.ClearTeamScores();
|
||
|
|
||
|
if( gameLocal.gameType != GAME_TOURNEY ) {
|
||
|
for( i = 0; i < gameLocal.numClients; i++ ) {
|
||
|
idEntity *ent = gameLocal.entities[ i ];
|
||
|
if ( !ent || !ent->IsType( idPlayer::GetClassType() ) ) {
|
||
|
continue;
|
||
|
}
|
||
|
((idPlayer*)ent)->JoinInstance( 0 );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
case GAMEON: {
|
||
|
// allow damage in warmup
|
||
|
//gameLocal.mpGame.EnableDamage( true );
|
||
|
gameLocal.LocalMapRestart();
|
||
|
// RITUAL BEGIN
|
||
|
// squirrel: Buying & Deadzone
|
||
|
for( i = 0; i < gameLocal.numClients; i++ ) {
|
||
|
idEntity *ent = gameLocal.entities[ i ];
|
||
|
if ( !ent || !ent->IsType( idPlayer::GetClassType() ) ) {
|
||
|
continue;
|
||
|
}
|
||
|
idPlayer* player = static_cast< idPlayer* >(ent);
|
||
|
player->inventory.carryOverWeapons = 0;
|
||
|
player->ResetCash();
|
||
|
// If the buy menu is up during a server restart,
|
||
|
// make sure to refresh it.
|
||
|
gameLocal.mpGame.RedrawLocalBuyMenu();
|
||
|
}
|
||
|
|
||
|
if ( gameLocal.mpGame.IsBuyingAllowedInTheCurrentGameMode() )
|
||
|
{
|
||
|
gameLocal.mpGame.isBuyingAllowedRightNow = true;
|
||
|
|
||
|
// Give all the clients full ammo since this is the start of the round.
|
||
|
for( int i = 0; i < gameLocal.numClients; i++ ) {
|
||
|
idPlayer* p = (idPlayer*)gameLocal.entities[ i ];
|
||
|
if( p == NULL || !p->IsType( idPlayer::GetClassType() ) )
|
||
|
continue;
|
||
|
|
||
|
GiveStuffToPlayer(p, "ammo", "");
|
||
|
p->inventory.weapons |= p->inventory.carryOverWeapons & CARRYOVER_WEAPONS_MASK;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( gameLocal.gameType == GAME_DEADZONE ) {
|
||
|
// Spawn the powerups!
|
||
|
const char *mapName = gameLocal.serverInfo.GetString( "si_map" );
|
||
|
const idDict *mapDict = fileSystem->GetMapDecl( mapName );
|
||
|
if ( mapDict )
|
||
|
gameLocal.mpGame.deadZonePowerupCount = mapDict->GetInt("deadZonePowerupCount", "3");
|
||
|
else
|
||
|
gameLocal.mpGame.deadZonePowerupCount = 3;
|
||
|
|
||
|
int pcount = gameLocal.mpGame.deadZonePowerupCount;
|
||
|
if ( pcount == -1 )
|
||
|
pcount = 3; // Good default.
|
||
|
|
||
|
pcount = idMath::ClampInt(1, 12, pcount);
|
||
|
for ( int i = 0; i<pcount; i++ ) {
|
||
|
SpawnDeadZonePowerup();
|
||
|
}
|
||
|
}
|
||
|
// RITUAL END
|
||
|
|
||
|
// asalmon: reset the stats when the warmup period is over
|
||
|
statManager->Init();
|
||
|
statManager->BeginGame();
|
||
|
|
||
|
outMsg.Init( msgBuf, sizeof( msgBuf ) );
|
||
|
outMsg.WriteByte( GAME_RELIABLE_MESSAGE_RESTART );
|
||
|
outMsg.WriteBits( 0, 1 );
|
||
|
networkSystem->ServerSendReliableMessage( -1, outMsg );
|
||
|
|
||
|
gameLocal.mpGame.SetMatchStartedTime( gameLocal.time );
|
||
|
|
||
|
fragLimitTimeout = 0;
|
||
|
|
||
|
// write server initial reliable messages to give everyone new base
|
||
|
for( i = 0; i < MAX_CLIENTS; i++ ) {
|
||
|
// dont send this to server - we have all the data already and this will
|
||
|
// just trigger extra gamestate detection
|
||
|
if ( gameLocal.entities[ i ] && i != gameLocal.localClientNum ) {
|
||
|
gameLocal.mpGame.ServerWriteInitialReliableMessages( serverReliableSender.To( i, true ), i );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( gameLocal.isRepeater ) {
|
||
|
gameLocal.mpGame.ServerWriteInitialReliableMessages( repeaterReliableSender.To( -1 ), ENTITYNUM_NONE );
|
||
|
}
|
||
|
|
||
|
for( i = 0; i < gameLocal.numClients; i++ ) {
|
||
|
idEntity *ent = gameLocal.entities[ i ];
|
||
|
if ( !ent || !ent->IsType( idPlayer::GetClassType() ) ) {
|
||
|
continue;
|
||
|
}
|
||
|
idPlayer *p = static_cast<idPlayer *>( ent );
|
||
|
p->SetLeader( false ); // don't carry the flag from previous games
|
||
|
gameLocal.mpGame.SetPlayerScore( p, 0 );
|
||
|
gameLocal.mpGame.SetPlayerTeamScore( p, 0 );
|
||
|
|
||
|
// in normal gameplay modes, spawn the player in. For tourney, the tourney manager handles spawning
|
||
|
if( gameLocal.gameType != GAME_TOURNEY ) {
|
||
|
p->JoinInstance( 0 );
|
||
|
// in team games, let UserInfoChanged() spawn people on the right teams
|
||
|
if( !gameLocal.IsTeamGame() || p->team != -1 ) {
|
||
|
p->ServerSpectate( static_cast<idPlayer *>(ent)->wantSpectate );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
gameLocal.mpGame.ClearTeamScores();
|
||
|
|
||
|
cvarSystem->SetCVarString( "ui_ready", "Not Ready" );
|
||
|
gameLocal.mpGame.switchThrottle[ 1 ] = 0; // passby the throttle
|
||
|
break;
|
||
|
}
|
||
|
case GAMEREVIEW: {
|
||
|
statManager->EndGame();
|
||
|
|
||
|
//statManager->DebugPrint();
|
||
|
nextState = INACTIVE; // used to abort a game. cancel out any upcoming state change
|
||
|
|
||
|
for( i = 0; i < gameLocal.numClients; i++ ) {
|
||
|
idEntity *ent = gameLocal.entities[ i ];
|
||
|
// RAVEN BEGIN
|
||
|
// jnewquist: Use accessor for static class type
|
||
|
if ( !ent || !ent->IsType( idPlayer::GetClassType() ) ) {
|
||
|
// RAVEN END
|
||
|
continue;
|
||
|
}
|
||
|
// RITUAL BEGIN
|
||
|
// squirrel: support for Buying in multiplayer
|
||
|
idPlayer* player = static_cast< idPlayer* >(ent);
|
||
|
player->inventory.carryOverWeapons = 0;
|
||
|
player->ResetCash();
|
||
|
player->forcedReady = false;
|
||
|
player->ServerSpectate( true );
|
||
|
static_cast< idPlayer *>( ent )->forcedReady = false;
|
||
|
static_cast<idPlayer *>(ent)->ServerSpectate( true );
|
||
|
// RITUAL END
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
case SUDDENDEATH: {
|
||
|
gameLocal.mpGame.PrintMessageEvent( -1, MSG_SUDDENDEATH );
|
||
|
//unmark all leaders, so we make sure we only let the proper people respawn
|
||
|
for( i = 0; i < gameLocal.numClients; i++ ) {
|
||
|
idEntity *ent = gameLocal.entities[ i ];
|
||
|
if ( !ent || !ent->IsType( idPlayer::GetClassType() ) ) {
|
||
|
continue;
|
||
|
}
|
||
|
idPlayer *p = static_cast<idPlayer *>( ent );
|
||
|
p->SetLeader( false ); // don't carry the flag from previous games
|
||
|
}
|
||
|
|
||
|
// only restart in team games if si_suddenDeathRestart is set.
|
||
|
if( !gameLocal.IsTeamGame() || gameLocal.serverInfo.GetBool( "si_suddenDeathRestart" ) ) {
|
||
|
gameLocal.LocalMapRestart();
|
||
|
}
|
||
|
|
||
|
// Mark everyone tied for the lead as leaders
|
||
|
i = 0;
|
||
|
idPlayer* leader = gameLocal.mpGame.GetRankedPlayer( i );
|
||
|
if( leader ) {
|
||
|
int highScore = gameLocal.mpGame.GetScore( leader );
|
||
|
while( leader ) {
|
||
|
if( gameLocal.mpGame.GetScore( leader ) < highScore ) {
|
||
|
break;
|
||
|
}
|
||
|
leader->SetLeader( true );
|
||
|
leader = gameLocal.mpGame.GetRankedPlayer( ++i );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// RITUAL BEGIN
|
||
|
// squirrel: Buying & Deadzone
|
||
|
/// Reset players' cash and inventory if si_suddenDeathRestart is set
|
||
|
if( !gameLocal.IsTeamGame() || gameLocal.serverInfo.GetBool( "si_suddenDeathRestart" ) )
|
||
|
{
|
||
|
for( i = 0; i < gameLocal.numClients; i++ ) {
|
||
|
idEntity *ent = gameLocal.entities[ i ];
|
||
|
if ( !ent || !ent->IsType( idPlayer::GetClassType() ) ) {
|
||
|
continue;
|
||
|
}
|
||
|
idPlayer* player = static_cast< idPlayer* >(ent);
|
||
|
player->inventory.carryOverWeapons = 0;
|
||
|
player->ResetCash();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( gameLocal.mpGame.IsBuyingAllowedInTheCurrentGameMode() )
|
||
|
{
|
||
|
gameLocal.mpGame.isBuyingAllowedRightNow = true;
|
||
|
|
||
|
// Give all the clients full ammo since this is the start of the round.
|
||
|
for( int i = 0; i < gameLocal.numClients; i++ ) {
|
||
|
idPlayer* p = (idPlayer*)gameLocal.entities[ i ];
|
||
|
if( p == NULL || !p->IsType( idPlayer::GetClassType() ) )
|
||
|
continue;
|
||
|
|
||
|
GiveStuffToPlayer(p, "ammo", "");
|
||
|
p->inventory.weapons |= p->inventory.carryOverWeapons & CARRYOVER_WEAPONS_MASK;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( gameLocal.gameType == GAME_DEADZONE ) {
|
||
|
// Spawn the powerups!
|
||
|
const char *mapName = gameLocal.serverInfo.GetString( "si_map" );
|
||
|
const idDict *mapDict = fileSystem->GetMapDecl( mapName );
|
||
|
if ( mapDict )
|
||
|
gameLocal.mpGame.deadZonePowerupCount = mapDict->GetInt("deadZonePowerupCount", "3");
|
||
|
else
|
||
|
gameLocal.mpGame.deadZonePowerupCount = 3;
|
||
|
|
||
|
int pcount = gameLocal.mpGame.deadZonePowerupCount;
|
||
|
if ( pcount == -1 )
|
||
|
pcount = 3; // Good default.
|
||
|
|
||
|
pcount = idMath::ClampInt(1, 12, pcount);
|
||
|
for ( int i = 0; i<pcount; i++ ) {
|
||
|
SpawnDeadZonePowerup();
|
||
|
}
|
||
|
}
|
||
|
// RITUAL END
|
||
|
|
||
|
if ( gameLocal.gameType == GAME_DM ) {
|
||
|
//send all the non-leaders to spectatormode?
|
||
|
for( i = 0; i < gameLocal.numClients; i++ ) {
|
||
|
idEntity *ent = gameLocal.entities[ i ];
|
||
|
// RAVEN BEGIN
|
||
|
// jnewquist: Use accessor for static class type
|
||
|
if ( !ent || !ent->IsType( idPlayer::GetClassType() ) ) {
|
||
|
// RAVEN END
|
||
|
continue;
|
||
|
}
|
||
|
if ( static_cast< idPlayer *>( ent )->IsLeader() ) {
|
||
|
static_cast<idPlayer *>(ent)->ServerSpectate( false );
|
||
|
continue;
|
||
|
}
|
||
|
static_cast< idPlayer *>( ent )->forcedReady = false;
|
||
|
static_cast<idPlayer *>(ent)->ServerSpectate( true );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
default: {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
currentState = newState;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
rvGameState::ClientDisconnect
|
||
|
================
|
||
|
*/
|
||
|
void rvGameState::ClientDisconnect( idPlayer* player ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
rvGameState::Spectate
|
||
|
================
|
||
|
*/
|
||
|
void rvGameState::Spectate( idPlayer* player ) {
|
||
|
if( player->spectating && player->wantSpectate ) {
|
||
|
gameLocal.mpGame.ClearFrags( player->entityNumber );
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
rvGameState::operator==
|
||
|
================
|
||
|
*/
|
||
|
bool rvGameState::operator==( const rvGameState& rhs ) const {
|
||
|
return (
|
||
|
( currentState == rhs.currentState ) &&
|
||
|
( nextState == rhs.nextState ) &&
|
||
|
( nextStateTime == rhs.nextStateTime )
|
||
|
);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
rvGameState::operator!=
|
||
|
================
|
||
|
*/
|
||
|
bool rvGameState::operator!=( const rvGameState& rhs ) const {
|
||
|
return (
|
||
|
( currentState != rhs.currentState ) ||
|
||
|
( nextState != rhs.nextState ) ||
|
||
|
( nextStateTime != rhs.nextStateTime )
|
||
|
);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
rvGameState::operator=
|
||
|
================
|
||
|
*/
|
||
|
rvGameState& rvGameState::operator=( const rvGameState& rhs ) {
|
||
|
currentState = rhs.currentState;
|
||
|
nextState = rhs.nextState;
|
||
|
nextStateTime = rhs.nextStateTime;
|
||
|
return (*this);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===============
|
||
|
rvGameState::WriteNetworkInfo
|
||
|
===============
|
||
|
*/
|
||
|
void rvGameState::WriteNetworkInfo( idFile *file, int clientNum ) {
|
||
|
idBitMsg msg;
|
||
|
byte msgBuf[ MAX_GAME_MESSAGE_SIZE ];
|
||
|
|
||
|
msg.Init( msgBuf, sizeof( msgBuf ) );
|
||
|
msg.BeginWriting();
|
||
|
WriteState( msg );
|
||
|
file->WriteInt( msg.GetSize() );
|
||
|
file->Write( msg.GetData(), msg.GetSize() );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===============
|
||
|
rvGameState::ReadNetworkInfo
|
||
|
===============
|
||
|
*/
|
||
|
void rvGameState::ReadNetworkInfo( idFile *file, int clientNum ) {
|
||
|
idBitMsg msg;
|
||
|
byte msgBuf[ MAX_GAME_MESSAGE_SIZE ];
|
||
|
int size;
|
||
|
|
||
|
file->ReadInt( size );
|
||
|
msg.Init( msgBuf, size );
|
||
|
msg.SetSize( size );
|
||
|
file->Read( msg.GetData(), size );
|
||
|
ReceiveState( msg );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===============================================================================
|
||
|
|
||
|
rvDMGameState
|
||
|
|
||
|
Game state info for DM
|
||
|
|
||
|
===============================================================================
|
||
|
*/
|
||
|
rvDMGameState::rvDMGameState( bool allocPrevious ) : rvGameState( allocPrevious ) {
|
||
|
trackPrevious = allocPrevious;
|
||
|
}
|
||
|
|
||
|
void rvDMGameState::Run( void ) {
|
||
|
idPlayer* player = NULL;
|
||
|
|
||
|
rvGameState::Run();
|
||
|
|
||
|
switch( currentState ) {
|
||
|
case GAMEON: {
|
||
|
player = gameLocal.mpGame.FragLimitHit();
|
||
|
|
||
|
bool tiedForFirst = false;
|
||
|
idPlayer* first = gameLocal.mpGame.GetRankedPlayer( 0 );
|
||
|
idPlayer* second = gameLocal.mpGame.GetRankedPlayer( 1 );
|
||
|
|
||
|
if( player == NULL ) {
|
||
|
if( first && second && gameLocal.mpGame.GetScore( first ) == gameLocal.mpGame.GetScore( second ) ) {
|
||
|
tiedForFirst = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( player ) {
|
||
|
// delay between detecting frag limit and ending game. let the death anims play
|
||
|
if ( !fragLimitTimeout ) {
|
||
|
common->DPrintf( "enter FragLimit timeout, player %d is leader\n", player->entityNumber );
|
||
|
fragLimitTimeout = gameLocal.time + FRAGLIMIT_DELAY;
|
||
|
}
|
||
|
if ( gameLocal.time > fragLimitTimeout ) {
|
||
|
NewState( GAMEREVIEW );
|
||
|
|
||
|
gameLocal.mpGame.PrintMessageEvent( -1, MSG_FRAGLIMIT, player->entityNumber );
|
||
|
|
||
|
}
|
||
|
} else if ( fragLimitTimeout ) {
|
||
|
// frag limit was hit and cancelled. means the two teams got even during FRAGLIMIT_DELAY
|
||
|
// enter sudden death, the next frag leader will win
|
||
|
//
|
||
|
// jshepard: OR it means that the winner killed himself during the fraglimit delay, and the
|
||
|
// game needs to roll on.
|
||
|
if( first && second && (gameLocal.mpGame.GetScore( first ) == gameLocal.mpGame.GetScore( second )) ) {
|
||
|
//this is a tie...
|
||
|
if( gameLocal.mpGame.GetScore( first ) >= gameLocal.serverInfo.GetInt( "si_fragLimit" ) ) {
|
||
|
//and it must be tied at fraglimit, so sudden death.
|
||
|
NewState( SUDDENDEATH );
|
||
|
}
|
||
|
}
|
||
|
//otherwise, just keep playing as normal.
|
||
|
fragLimitTimeout = 0;
|
||
|
|
||
|
} else if ( gameLocal.mpGame.TimeLimitHit() ) {
|
||
|
gameLocal.mpGame.PrintMessageEvent( -1, MSG_TIMELIMIT );
|
||
|
if( tiedForFirst ) {
|
||
|
// if tied at timelimit hit, goto sudden death
|
||
|
fragLimitTimeout = 0;
|
||
|
NewState( SUDDENDEATH );
|
||
|
} else {
|
||
|
// or just end the game
|
||
|
NewState( GAMEREVIEW );
|
||
|
}
|
||
|
} else if( tiedForFirst && gameLocal.serverInfo.GetInt( "si_fragLimit" ) > 0 && gameLocal.mpGame.GetScore( first ) >= gameLocal.serverInfo.GetInt( "si_fragLimit" ) ) {
|
||
|
// check for the rare case that two players both hit the fraglimit the same frame
|
||
|
// two people tied at fraglimit, advance to sudden death after a delay
|
||
|
fragLimitTimeout = gameLocal.time + FRAGLIMIT_DELAY;
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case SUDDENDEATH: {
|
||
|
player = gameLocal.mpGame.FragLeader();
|
||
|
|
||
|
if ( player ) {
|
||
|
if ( !fragLimitTimeout ) {
|
||
|
common->DPrintf( "enter sudden death FragLeader timeout, player %d is leader\n", player->entityNumber );
|
||
|
fragLimitTimeout = gameLocal.time + FRAGLIMIT_DELAY;
|
||
|
}
|
||
|
if ( gameLocal.time > fragLimitTimeout ) {
|
||
|
NewState( GAMEREVIEW );
|
||
|
gameLocal.mpGame.PrintMessageEvent( -1, MSG_FRAGLIMIT, player->entityNumber );
|
||
|
}
|
||
|
} else if ( fragLimitTimeout ) {
|
||
|
gameLocal.mpGame.PrintMessageEvent( -1, MSG_HOLYSHIT );
|
||
|
fragLimitTimeout = 0;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===============================================================================
|
||
|
|
||
|
rvTeamDMGameState
|
||
|
|
||
|
Game state info for Team DM
|
||
|
|
||
|
===============================================================================
|
||
|
*/
|
||
|
rvTeamDMGameState::rvTeamDMGameState( bool allocPrevious ) : rvGameState( allocPrevious ) {
|
||
|
trackPrevious = allocPrevious;
|
||
|
}
|
||
|
|
||
|
void rvTeamDMGameState::Run( void ) {
|
||
|
rvGameState::Run();
|
||
|
|
||
|
switch( currentState ) {
|
||
|
case GAMEON: {
|
||
|
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 ) );
|
||
|
if( gameLocal.serverInfo.GetInt( "si_fragLimit" ) <= 0 ) {
|
||
|
// no fraglimit
|
||
|
team = -1;
|
||
|
}
|
||
|
bool tiedForFirst = gameLocal.mpGame.GetScoreForTeam( TEAM_MARINE ) == gameLocal.mpGame.GetScoreForTeam( TEAM_STROGG );
|
||
|
if ( team >= 0 && !tiedForFirst ) {
|
||
|
if ( !fragLimitTimeout ) {
|
||
|
common->DPrintf( "enter FragLimit timeout, team %d is leader\n", team );
|
||
|
fragLimitTimeout = gameLocal.time + FRAGLIMIT_DELAY;
|
||
|
}
|
||
|
if ( gameLocal.time > fragLimitTimeout ) {
|
||
|
NewState( GAMEREVIEW );
|
||
|
|
||
|
gameLocal.mpGame.PrintMessageEvent( -1, MSG_FRAGLIMIT, team );
|
||
|
|
||
|
}
|
||
|
} else if ( fragLimitTimeout ) {
|
||
|
// frag limit was hit and cancelled. means the two teams got even during FRAGLIMIT_DELAY
|
||
|
// enter sudden death, the next frag leader will win
|
||
|
//
|
||
|
// jshepard: OR it means that the winner killed himself during the fraglimit delay, and the
|
||
|
// game needs to roll on.
|
||
|
if( tiedForFirst ) {
|
||
|
//this is a tie
|
||
|
if( gameLocal.mpGame.GetScoreForTeam( TEAM_MARINE ) >= gameLocal.serverInfo.GetInt( "si_fragLimit" ) ) {
|
||
|
//and it's tied at the fraglimit.
|
||
|
NewState( SUDDENDEATH );
|
||
|
}
|
||
|
//not a tie, game on.
|
||
|
fragLimitTimeout = 0;
|
||
|
}
|
||
|
} else if ( gameLocal.mpGame.TimeLimitHit() ) {
|
||
|
gameLocal.mpGame.PrintMessageEvent( -1, MSG_TIMELIMIT );
|
||
|
if( tiedForFirst ) {
|
||
|
// if tied at timelimit hit, goto sudden death
|
||
|
fragLimitTimeout = 0;
|
||
|
NewState( SUDDENDEATH );
|
||
|
} else {
|
||
|
// or just end the game
|
||
|
NewState( GAMEREVIEW );
|
||
|
}
|
||
|
} else if( tiedForFirst && team >= 0 ) {
|
||
|
// check for the rare case that two teams both hit the fraglimit the same frame
|
||
|
// two people tied at fraglimit, advance to sudden death after a delay
|
||
|
fragLimitTimeout = gameLocal.time + FRAGLIMIT_DELAY;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case SUDDENDEATH: {
|
||
|
int team = gameLocal.mpGame.GetScoreForTeam( TEAM_MARINE ) > gameLocal.mpGame.GetScoreForTeam( TEAM_STROGG ) ? TEAM_MARINE : TEAM_STROGG;
|
||
|
bool tiedForFirst = false;
|
||
|
if( gameLocal.mpGame.GetScoreForTeam( TEAM_MARINE ) == gameLocal.mpGame.GetScoreForTeam( TEAM_STROGG ) ) {
|
||
|
team = -1;
|
||
|
tiedForFirst = true;
|
||
|
}
|
||
|
|
||
|
if ( team >= 0 && !tiedForFirst ) {
|
||
|
if ( !fragLimitTimeout ) {
|
||
|
common->DPrintf( "enter sudden death FragLeader timeout, team %d is leader\n", team );
|
||
|
fragLimitTimeout = gameLocal.time + FRAGLIMIT_DELAY;
|
||
|
}
|
||
|
if ( gameLocal.time > fragLimitTimeout ) {
|
||
|
NewState( GAMEREVIEW );
|
||
|
gameLocal.mpGame.PrintMessageEvent( -1, MSG_FRAGLIMIT, team );
|
||
|
}
|
||
|
} else if ( fragLimitTimeout ) {
|
||
|
gameLocal.mpGame.PrintMessageEvent( -1, MSG_HOLYSHIT );
|
||
|
fragLimitTimeout = 0;
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===============================================================================
|
||
|
|
||
|
rvCTFGameState
|
||
|
|
||
|
Game state info for CTF
|
||
|
|
||
|
===============================================================================
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
rvCTFGameState::rvCTFGameState
|
||
|
================
|
||
|
*/
|
||
|
rvCTFGameState::rvCTFGameState( bool allocPrevious ) : rvGameState( false ) {
|
||
|
Clear();
|
||
|
|
||
|
if( allocPrevious ) {
|
||
|
previousGameState = new rvCTFGameState( false );
|
||
|
} else {
|
||
|
previousGameState = NULL;
|
||
|
}
|
||
|
|
||
|
trackPrevious = allocPrevious;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
rvCTFGameState::Clear
|
||
|
================
|
||
|
*/
|
||
|
void rvCTFGameState::Clear( void ) {
|
||
|
rvGameState::Clear();
|
||
|
|
||
|
// mekberg: clear previous game state.
|
||
|
if ( previousGameState ) {
|
||
|
previousGameState->Clear( );
|
||
|
}
|
||
|
|
||
|
for( int i = 0; i < TEAM_MAX; i++ ) {
|
||
|
flagStatus[ i ].state = FS_AT_BASE;
|
||
|
flagStatus[ i ].clientNum = -1;
|
||
|
}
|
||
|
|
||
|
for( int i = 0; i < MAX_AP; i++ ) {
|
||
|
apState[ i ] = AS_NEUTRAL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
rvCTFGameState::SendState
|
||
|
================
|
||
|
*/
|
||
|
void rvCTFGameState::SendState( const idMessageSender &sender, int clientNum ) {
|
||
|
idBitMsg outMsg;
|
||
|
byte msgBuf[MAX_GAME_MESSAGE_SIZE];
|
||
|
|
||
|
assert( (gameLocal.isServer || gameLocal.isRepeater) && trackPrevious && IsType( rvCTFGameState::GetClassType() ) );
|
||
|
|
||
|
if( clientNum == -1 && (rvCTFGameState&)(*this) == (rvCTFGameState&)(*previousGameState) ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
outMsg.Init( msgBuf, sizeof( msgBuf ) );
|
||
|
outMsg.WriteByte( GAME_RELIABLE_MESSAGE_GAMESTATE );
|
||
|
|
||
|
WriteState( outMsg );
|
||
|
|
||
|
sender.Send( outMsg );
|
||
|
|
||
|
// don't update the state if we are working for a single client
|
||
|
if ( clientNum == -1 ) {
|
||
|
outMsg.ReadByte(); // pop off the msg ID
|
||
|
ReceiveState( outMsg );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===============
|
||
|
rvCTFGameState::WriteState
|
||
|
===============
|
||
|
*/
|
||
|
void rvCTFGameState::WriteState( idBitMsg &msg ) {
|
||
|
// send off base info
|
||
|
rvGameState::PackState( msg );
|
||
|
// add CTF info
|
||
|
PackState( msg );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
rvCTFGameState::ReceiveState
|
||
|
================
|
||
|
*/
|
||
|
void rvCTFGameState::ReceiveState( const idBitMsg& msg ) {
|
||
|
assert( IsType( rvCTFGameState::GetClassType() ) );
|
||
|
|
||
|
if ( !rvGameState::BaseUnpackState( msg ) ) {
|
||
|
return;
|
||
|
}
|
||
|
UnpackState( msg );
|
||
|
|
||
|
if ( gameLocal.localClientNum >= 0 ) {
|
||
|
GameStateChanged();
|
||
|
}
|
||
|
|
||
|
(rvCTFGameState&)(*previousGameState) = (rvCTFGameState&)(*this);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
rvCTFGameState::PackState
|
||
|
================
|
||
|
*/
|
||
|
void rvCTFGameState::PackState( idBitMsg& outMsg ) {
|
||
|
// use indexing to pack in info
|
||
|
int index = 0;
|
||
|
|
||
|
for( int i = 0; i < TEAM_MAX; i++ ) {
|
||
|
if( flagStatus[ i ] != ((rvCTFGameState*)previousGameState)->flagStatus[ i ] ) {
|
||
|
outMsg.WriteByte( index );
|
||
|
outMsg.WriteByte( flagStatus[ i ].state );
|
||
|
outMsg.WriteByte( flagStatus[ i ].clientNum );
|
||
|
}
|
||
|
index++;
|
||
|
}
|
||
|
|
||
|
for( int i = 0; i < MAX_AP; i++ ) {
|
||
|
if( apState[ i ] != ((rvCTFGameState*)previousGameState)->apState[ i ] ) {
|
||
|
outMsg.WriteByte( index );
|
||
|
outMsg.WriteByte( apState[ i ] );
|
||
|
}
|
||
|
index++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
rvCTFGameState::UnpackState
|
||
|
================
|
||
|
*/
|
||
|
void rvCTFGameState::UnpackState( const idBitMsg& inMsg ) {
|
||
|
while( inMsg.GetRemainingData() ) {
|
||
|
int index = inMsg.ReadByte();
|
||
|
|
||
|
if( index >= 0 && index < TEAM_MAX ) {
|
||
|
flagStatus[ index ].state = (flagState_t)inMsg.ReadByte();
|
||
|
flagStatus[ index ].clientNum = inMsg.ReadByte();
|
||
|
} else if( index >= TEAM_MAX && index < ( TEAM_MAX + MAX_AP ) ) {
|
||
|
apState[ index - TEAM_MAX ] = (apState_t)inMsg.ReadByte();
|
||
|
} else {
|
||
|
gameLocal.Error( "rvCTFGameState::UnpackState() - Unknown data identifier '%d'\n", index );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
rvCTFGameState::SendInitialState
|
||
|
================
|
||
|
*/
|
||
|
void rvCTFGameState::SendInitialState( const idMessageSender &sender, int clientNum ) {
|
||
|
assert( IsType( rvCTFGameState::GetClassType() ) );
|
||
|
|
||
|
rvCTFGameState* previousState = (rvCTFGameState*)previousGameState;
|
||
|
|
||
|
rvCTFGameState invalidState;
|
||
|
|
||
|
previousGameState = &invalidState;
|
||
|
|
||
|
SendState( sender, clientNum );
|
||
|
|
||
|
previousGameState = previousState;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
rvCTFGameState::GameStateChanged
|
||
|
================
|
||
|
*/
|
||
|
void rvCTFGameState::GameStateChanged( void ) {
|
||
|
// detect any base state changes
|
||
|
rvGameState::GameStateChanged();
|
||
|
|
||
|
// CTF specific stuff
|
||
|
idPlayer* player = gameLocal.GetLocalPlayer();
|
||
|
if ( !player ) {
|
||
|
if ( gameLocal.IsServerDemoPlaying() && gameLocal.GetDemoFollowClient() >= 0 ) {
|
||
|
player = static_cast< idPlayer * >( gameLocal.entities[ gameLocal.GetDemoFollowClient() ] );
|
||
|
}
|
||
|
}
|
||
|
if ( !player ) {
|
||
|
gameLocal.Warning( "rvCTFGameState::GameStateChanged() - NULL local player\n" ) ;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
bool noSounds = false;
|
||
|
|
||
|
for( int i = 0; i < TEAM_MAX; i++ ) {
|
||
|
if( flagStatus[ i ] == ((rvCTFGameState*)previousGameState)->flagStatus[ i ] ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// don't play flag messages when flag state changes as a result of the gamestate changing
|
||
|
if( currentState != ((rvCTFGameState*)previousGameState)->currentState && ((rvCTFGameState*)previousGameState)->currentState != INACTIVE ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if ( ((rvCTFGameState*)previousGameState)->currentState == INACTIVE ) {
|
||
|
noSounds = true;
|
||
|
}
|
||
|
|
||
|
// flagTeam - used to tell the HUD which flag to update
|
||
|
int flagTeam = i;
|
||
|
|
||
|
// in one flag CTF flagTeam is set to whichever team has the flag
|
||
|
if( gameLocal.gameType == GAME_1F_CTF || gameLocal.gameType == GAME_ARENA_1F_CTF ) {
|
||
|
if( flagStatus[ i ].state == FS_TAKEN_MARINE ) {
|
||
|
flagTeam = TEAM_MARINE;
|
||
|
} else if( flagStatus[ i ].state == FS_TAKEN_STROGG ) {
|
||
|
flagTeam = TEAM_STROGG;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if( flagStatus[ i ].state == FS_DROPPED && !noSounds ) {
|
||
|
if ( !player->spectating ) {
|
||
|
if( flagTeam == player->team ) {
|
||
|
// our flag was dropped, so the enemy dropped it
|
||
|
gameLocal.mpGame.ScheduleAnnouncerSound ( AS_CTF_ENEMY_DROPS_FLAG, gameLocal.time, -1, true );
|
||
|
} else {
|
||
|
gameLocal.mpGame.ScheduleAnnouncerSound ( AS_CTF_YOUR_TEAM_DROPS_FLAG, gameLocal.time, -1, true );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if( player->mphud ) {
|
||
|
player->mphud->SetStateInt( "team", flagTeam );
|
||
|
player->mphud->HandleNamedEvent( "flagDrop" );
|
||
|
|
||
|
if ( !player->spectating ) {
|
||
|
if ( flagTeam == player->team ) {
|
||
|
player->mphud->SetStateString( "main_notice_text", common->GetLocalizedString( "#str_107723" ) );
|
||
|
} else {
|
||
|
player->mphud->SetStateString( "main_notice_text", common->GetLocalizedString( "#str_104420" ) );
|
||
|
}
|
||
|
|
||
|
player->mphud->HandleNamedEvent( "main_notice" );
|
||
|
}
|
||
|
}
|
||
|
} else if( flagStatus[ i ].state == FS_AT_BASE ) {
|
||
|
if( ((rvCTFGameState*)previousGameState)->flagStatus[ i ].state == FS_TAKEN && !noSounds ) {
|
||
|
// team scores
|
||
|
if ( !player->spectating ) {
|
||
|
if( flagTeam == player->team ) {
|
||
|
if( gameLocal.mpGame.CanCapture( gameLocal.mpGame.OpposingTeam( flagTeam ) ) ) {
|
||
|
gameLocal.mpGame.ScheduleAnnouncerSound ( AS_TEAM_ENEMY_SCORES, gameLocal.time );
|
||
|
}
|
||
|
} else {
|
||
|
if( gameLocal.mpGame.CanCapture( gameLocal.mpGame.OpposingTeam( flagTeam ) ) ) {
|
||
|
gameLocal.mpGame.ScheduleAnnouncerSound ( AS_TEAM_YOU_SCORE, gameLocal.time );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
} else if( ((rvCTFGameState*)previousGameState)->flagStatus[ i ].state == FS_DROPPED && !noSounds ) {
|
||
|
// flag returned
|
||
|
if ( !player->spectating ) {
|
||
|
if( flagTeam == player->team ) {
|
||
|
gameLocal.mpGame.ScheduleAnnouncerSound ( AS_CTF_YOUR_FLAG_RETURNED, gameLocal.time, -1, true );
|
||
|
} else {
|
||
|
gameLocal.mpGame.ScheduleAnnouncerSound ( AS_CTF_ENEMY_RETURNS_FLAG, gameLocal.time, -1, true );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if( player->mphud ) {
|
||
|
player->mphud->SetStateInt( "team", flagTeam );
|
||
|
player->mphud->HandleNamedEvent( "flagReturn" );
|
||
|
}
|
||
|
} else if( flagStatus[ i ].state == FS_TAKEN || flagStatus[ i ].state == FS_TAKEN_STROGG || flagStatus[ i ].state == FS_TAKEN_MARINE ) {
|
||
|
// flag taken
|
||
|
if( flagTeam == player->team ) {
|
||
|
if ( !player->spectating ) {
|
||
|
if ( !noSounds ) {
|
||
|
gameLocal.mpGame.ScheduleAnnouncerSound ( AS_CTF_ENEMY_HAS_FLAG, gameLocal.time, -1, true );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( !player->spectating ) {
|
||
|
if ( player->mphud ) {
|
||
|
player->mphud->SetStateString( "main_notice_text", common->GetLocalizedString( "#str_107722" ) );
|
||
|
player->mphud->HandleNamedEvent( "main_notice" );
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
if ( flagStatus[ i ].clientNum == gameLocal.localClientNum ) {
|
||
|
if ( !noSounds ) {
|
||
|
gameLocal.mpGame.ScheduleAnnouncerSound ( AS_CTF_YOU_HAVE_FLAG, gameLocal.time, -1, true );
|
||
|
}
|
||
|
|
||
|
if ( !player->spectating ) {
|
||
|
// shouchard: inform the GUI that you've taken the flag
|
||
|
player->mphud->SetStateString( "main_notice_text", common->GetLocalizedString( "#str_104419" ) );
|
||
|
player->mphud->HandleNamedEvent( "main_notice" );
|
||
|
}
|
||
|
} else if ( !noSounds ) {
|
||
|
if ( !player->spectating ) {
|
||
|
gameLocal.mpGame.ScheduleAnnouncerSound ( AS_CTF_YOUR_TEAM_HAS_FLAG, gameLocal.time, -1, true );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if( player->mphud ) {
|
||
|
player->mphud->SetStateInt( "team", flagTeam );
|
||
|
player->mphud->HandleNamedEvent ( "flagTaken" );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
rvCTFGameState::Run
|
||
|
================
|
||
|
*/
|
||
|
void rvCTFGameState::Run( void ) {
|
||
|
// run common stuff
|
||
|
rvGameState::Run();
|
||
|
|
||
|
switch( currentState ) {
|
||
|
case GAMEON: {
|
||
|
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 ) );
|
||
|
if( gameLocal.serverInfo.GetInt( "si_captureLimit" ) <= 0 ) {
|
||
|
// no capture limit games
|
||
|
team = -1;
|
||
|
}
|
||
|
bool tiedForFirst = gameLocal.mpGame.GetScoreForTeam( TEAM_MARINE ) == gameLocal.mpGame.GetScoreForTeam( TEAM_STROGG );
|
||
|
if ( team >= 0 && !tiedForFirst ) {
|
||
|
if ( !fragLimitTimeout ) {
|
||
|
common->DPrintf( "enter capture limit timeout, team %d is leader\n", team );
|
||
|
fragLimitTimeout = gameLocal.time + CAPTURELIMIT_DELAY;
|
||
|
}
|
||
|
if ( gameLocal.time > fragLimitTimeout ) {
|
||
|
NewState( GAMEREVIEW );
|
||
|
|
||
|
gameLocal.mpGame.PrintMessageEvent( -1, MSG_CAPTURELIMIT, team );
|
||
|
|
||
|
}
|
||
|
} else if ( fragLimitTimeout ) {
|
||
|
// frag limit was hit and cancelled. means the two teams got even during FRAGLIMIT_DELAY
|
||
|
// enter sudden death, the next frag leader will win
|
||
|
// OR the winner lost a point in the frag delay, and there's no tie, so no one wins, game on.
|
||
|
if( tiedForFirst && ( gameLocal.mpGame.GetScoreForTeam( TEAM_MARINE ) >= gameLocal.serverInfo.GetInt( "si_captureLimit" ) )) {
|
||
|
NewState( SUDDENDEATH );
|
||
|
}
|
||
|
fragLimitTimeout = 0;
|
||
|
} else if ( gameLocal.mpGame.TimeLimitHit() ) {
|
||
|
gameLocal.mpGame.PrintMessageEvent( -1, MSG_TIMELIMIT );
|
||
|
if( tiedForFirst ) {
|
||
|
// if tied at timelimit hit, goto sudden death
|
||
|
fragLimitTimeout = 0;
|
||
|
NewState( SUDDENDEATH );
|
||
|
} else {
|
||
|
// or just end the game
|
||
|
NewState( GAMEREVIEW );
|
||
|
}
|
||
|
} else if( tiedForFirst && team >= 0 ) {
|
||
|
// check for the rare case that two teams both hit the fraglimit the same frame
|
||
|
// two people tied at fraglimit, advance to sudden death after a delay
|
||
|
fragLimitTimeout = gameLocal.time + CAPTURELIMIT_DELAY;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case SUDDENDEATH: {
|
||
|
int team = gameLocal.mpGame.GetScoreForTeam( TEAM_MARINE ) > gameLocal.mpGame.GetScoreForTeam( TEAM_STROGG ) ? TEAM_MARINE : TEAM_STROGG;
|
||
|
bool tiedForFirst = false;
|
||
|
if( gameLocal.mpGame.GetScoreForTeam( TEAM_MARINE ) == gameLocal.mpGame.GetScoreForTeam( TEAM_STROGG ) ) {
|
||
|
team = -1;
|
||
|
tiedForFirst = true;
|
||
|
}
|
||
|
|
||
|
if ( team >= 0 && !tiedForFirst ) {
|
||
|
if ( !fragLimitTimeout ) {
|
||
|
common->DPrintf( "enter sudden death FragLeader timeout, team %d is leader\n", team );
|
||
|
fragLimitTimeout = gameLocal.time + CAPTURELIMIT_DELAY;
|
||
|
}
|
||
|
if ( gameLocal.time > fragLimitTimeout ) {
|
||
|
NewState( GAMEREVIEW );
|
||
|
gameLocal.mpGame.PrintMessageEvent( -1, MSG_CAPTURELIMIT, team );
|
||
|
}
|
||
|
} else if ( fragLimitTimeout ) {
|
||
|
gameLocal.mpGame.PrintMessageEvent( -1, MSG_HOLYSHIT );
|
||
|
fragLimitTimeout = 0;
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
rvCTFGameState::SetFlagState
|
||
|
================
|
||
|
*/
|
||
|
void rvCTFGameState::SetFlagState( int flag, flagState_t newState ) {
|
||
|
if ( !gameLocal.isServer ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
assert( gameLocal.isServer && ( flag >= 0 && flag < TEAM_MAX ) && IsType( rvCTFGameState::GetClassType() ) );
|
||
|
|
||
|
flagStatus[ flag ].state = newState;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
rvCTFGameState::SetFlagCarrier
|
||
|
================
|
||
|
*/
|
||
|
void rvCTFGameState::SetFlagCarrier( int flag, int clientNum ) {
|
||
|
assert( gameLocal.isServer && ( flag >= 0 && flag < TEAM_MAX ) && (clientNum >= 0 && clientNum < MAX_CLIENTS) && IsType( rvCTFGameState::GetClassType() ) );
|
||
|
|
||
|
flagStatus[ flag ].clientNum = clientNum;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
rvCTFGameState::operator==
|
||
|
================
|
||
|
*/
|
||
|
bool rvCTFGameState::operator==( const rvCTFGameState& rhs ) const {
|
||
|
if( (rvGameState&)(*this) != (rvGameState&)rhs ) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
for( int i = 0; i < TEAM_MAX; i++ ) {
|
||
|
if( flagStatus[ i ] != rhs.flagStatus[ i ] ) {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for( int i = 0; i < MAX_AP; i++ ) {
|
||
|
if( apState[ i ] != rhs.apState[ i ] ) {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
rvCTFGameState::operator=
|
||
|
================
|
||
|
*/
|
||
|
rvCTFGameState& rvCTFGameState::operator=( const rvCTFGameState& rhs ) {
|
||
|
(rvGameState&)(*this) = (rvGameState&)rhs;
|
||
|
|
||
|
for( int i = 0; i < TEAM_MAX; i++ ) {
|
||
|
flagStatus[ i ] = rhs.flagStatus[ i ];
|
||
|
}
|
||
|
|
||
|
for( int i = 0; i < MAX_AP; i++ ) {
|
||
|
apState[ i ] = rhs.apState[ i ];
|
||
|
}
|
||
|
|
||
|
return (*this);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===============================================================================
|
||
|
|
||
|
rvTourneyGameState
|
||
|
|
||
|
Game state info for tourney
|
||
|
|
||
|
===============================================================================
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
rvTourneyGameState::rvTourneyGameState
|
||
|
================
|
||
|
*/
|
||
|
rvTourneyGameState::rvTourneyGameState( bool allocPrevious ) : rvGameState( false ) {
|
||
|
Clear();
|
||
|
|
||
|
if( allocPrevious ) {
|
||
|
previousGameState = new rvTourneyGameState( false );
|
||
|
} else {
|
||
|
previousGameState = NULL;
|
||
|
}
|
||
|
|
||
|
packTourneyHistory = false;
|
||
|
|
||
|
trackPrevious = allocPrevious;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
rvTourneyGameState::Clear
|
||
|
================
|
||
|
*/
|
||
|
void rvTourneyGameState::Clear( void ) {
|
||
|
assert( IsType( rvTourneyGameState::GetClassType() ) );
|
||
|
|
||
|
// mekberg: clear previous game state.
|
||
|
if ( previousGameState ) {
|
||
|
previousGameState->Clear();
|
||
|
}
|
||
|
|
||
|
rvGameState::Clear();
|
||
|
|
||
|
tourneyState = TS_INVALID;
|
||
|
|
||
|
for( int i = 0; i < MAX_ARENAS; i++ ) {
|
||
|
arenas[ i ].Clear( false );
|
||
|
arenas[ i ].SetArenaID( i );
|
||
|
}
|
||
|
|
||
|
for( int i = 0; i < MAX_ROUNDS; i++ ) {
|
||
|
for( int j = 0; j < MAX_ARENAS; j++ ) {
|
||
|
tourneyHistory[ i ][ j ].playerOne[ 0 ] = '\0';
|
||
|
tourneyHistory[ i ][ j ].playerTwo[ 0 ] = '\0';
|
||
|
tourneyHistory[ i ][ j ].playerOneNum = -1;
|
||
|
tourneyHistory[ i ][ j ].playerTwoNum = -1;
|
||
|
tourneyHistory[ i ][ j ].playerOneScore = -1;
|
||
|
tourneyHistory[ i ][ j ].playerTwoScore = -1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
startingRound = 0;
|
||
|
maxRound = 0;
|
||
|
round = -1;
|
||
|
byeArena = -1;
|
||
|
tourneyCount = 0;
|
||
|
roundTimeout = 0;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
rvTourneyGameState::Reset
|
||
|
================
|
||
|
*/
|
||
|
void rvTourneyGameState::Reset( void ) {
|
||
|
for( int i = 0; i < MAX_ARENAS; i++ ) {
|
||
|
arenas[ i ].Clear( false );
|
||
|
arenas[ i ].SetArenaID( i );
|
||
|
}
|
||
|
|
||
|
for( int i = 0; i < MAX_ROUNDS; i++ ) {
|
||
|
for( int j = 0; j < MAX_ARENAS; j++ ) {
|
||
|
tourneyHistory[ i ][ j ].playerOne[ 0 ] = '\0';
|
||
|
tourneyHistory[ i ][ j ].playerTwo[ 0 ] = '\0';
|
||
|
tourneyHistory[ i ][ j ].playerOneNum = -1;
|
||
|
tourneyHistory[ i ][ j ].playerTwoNum = -1;
|
||
|
tourneyHistory[ i ][ j ].playerOneScore = -1;
|
||
|
tourneyHistory[ i ][ j ].playerTwoScore = -1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
startingRound = 0;
|
||
|
maxRound = 0;
|
||
|
round = -1;
|
||
|
byeArena = -1;
|
||
|
roundTimeout = 0;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
rvTourneyGameState::Run
|
||
|
================
|
||
|
*/
|
||
|
void rvTourneyGameState::Run( void ) {
|
||
|
assert( IsType( rvTourneyGameState::GetClassType() ) );
|
||
|
|
||
|
rvGameState::Run();
|
||
|
|
||
|
switch( currentState ) {
|
||
|
case GAMEON: {
|
||
|
// check to see if we need to advance to the next round
|
||
|
if( nextState == GAMEREVIEW ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
bool roundDone = true;
|
||
|
|
||
|
for( int i = 0; i < MAX_ARENAS; i++ ) {
|
||
|
if( arenas[ i ].GetState() == AS_INACTIVE ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
arenas[ i ].UpdateState();
|
||
|
|
||
|
if( arenas[ i ].GetState() != AS_DONE ) {
|
||
|
// check to see if we're done
|
||
|
roundDone = false;
|
||
|
}
|
||
|
}
|
||
|
if( roundDone && !roundTimeout ) {
|
||
|
roundTimeout = gameLocal.time + FRAGLIMIT_DELAY;
|
||
|
}
|
||
|
|
||
|
if( roundDone && gameLocal.time > roundTimeout ) {
|
||
|
int pastRound = round - 1; //rounds are 1 indexed
|
||
|
idList<rvPair<idPlayer*, int> > advancingPlayers;
|
||
|
|
||
|
round++;
|
||
|
roundTimeout = 0;
|
||
|
|
||
|
assert( round >= 2 );
|
||
|
|
||
|
// copy over tourney history for the previous round
|
||
|
UpdateTourneyHistory( pastRound );
|
||
|
|
||
|
// transmit history
|
||
|
forceTourneyHistory = true;
|
||
|
|
||
|
// add who will advance to the next round
|
||
|
for( int i = 0; i < MAX_ARENAS; i++ ) {
|
||
|
if( arenas[ i ].GetState() == AS_DONE && arenas[ i ].GetWinner() ) {
|
||
|
advancingPlayers.Append( rvPair<idPlayer*, int>( arenas[ i ].GetWinner(), i ) );
|
||
|
gameLocal.mpGame.AddPlayerWin( arenas[ i ].GetWinner(), 1 );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// when only one player advances, that player has won the tournament
|
||
|
if( advancingPlayers.Num() == 1 ) {
|
||
|
idPlayer* winner = advancingPlayers[ 0 ].First();
|
||
|
assert( winner );
|
||
|
|
||
|
for( int i = 0; i < MAX_ARENAS; i++ ) {
|
||
|
arenas[ i ].Clear();
|
||
|
}
|
||
|
|
||
|
gameLocal.Printf( "Round %d: player %d (%s) has won the tourney!\n", round - 1, winner->entityNumber, gameLocal.userInfo[ winner->entityNumber ].GetString( "ui_name" ) );
|
||
|
// award 5 wins for winning the entire tournament
|
||
|
gameLocal.mpGame.AddPlayerWin( winner, 5 );
|
||
|
|
||
|
nextState = GAMEREVIEW;
|
||
|
nextStateTime = gameLocal.time + 5000;
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// see if we have to swap a player from the byeArena this round
|
||
|
bool swapBye = false;
|
||
|
int thisByeArena = -1;
|
||
|
|
||
|
if( byeArena >= 0 ) {
|
||
|
// look at the bye arena from the round that just ended
|
||
|
thisByeArena = byeArena / (round - 1);
|
||
|
|
||
|
// if the bye arena is playing in the next round, there's no need to switch players
|
||
|
if( !(thisByeArena % 2) ) {
|
||
|
if( arenas[ thisByeArena ].GetState() == AS_DONE && arenas[ thisByeArena + 1 ].GetState() == AS_DONE ) {
|
||
|
assert( arenas[ thisByeArena ].GetWinner() && arenas[ thisByeArena + 1 ].GetWinner() );
|
||
|
swapBye = false;
|
||
|
} else {
|
||
|
swapBye = true;
|
||
|
}
|
||
|
} else {
|
||
|
if( arenas[ thisByeArena ].GetState() == AS_DONE && arenas[ thisByeArena - 1 ].GetState() == AS_DONE ) {
|
||
|
assert( arenas[ thisByeArena ].GetWinner() && arenas[ thisByeArena - 1 ].GetWinner() );
|
||
|
swapBye = false;
|
||
|
} else {
|
||
|
swapBye = true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
idPlayer* oldByePlayer = NULL;
|
||
|
idPlayer* newByePlayer = NULL;
|
||
|
|
||
|
if( swapBye ) {
|
||
|
oldByePlayer = arenas[ thisByeArena ].GetWinner();
|
||
|
|
||
|
// pick a new bye player only from players who will be fighting next round
|
||
|
// i.e., don't swap the bye player with a player who will have a disconnect bye this upcoming round
|
||
|
idList<rvPair<idPlayer*, int> > nextRoundPlayers;
|
||
|
for ( int i = 0; i < MAX_ARENAS; i += 2 ) {
|
||
|
if( arenas[ i ].GetState() == AS_DONE && arenas[ i ].GetWinner() && arenas[ i + 1 ].GetState() == AS_DONE && arenas[ i + 1 ].GetWinner() ) {
|
||
|
nextRoundPlayers.Append( rvPair<idPlayer*, int>( arenas[ i ].GetWinner(), i ) );
|
||
|
nextRoundPlayers.Append( rvPair<idPlayer*, int>( arenas[ i + 1 ].GetWinner(), i + 1 ) );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( nextRoundPlayers.Num() ) {
|
||
|
newByePlayer = nextRoundPlayers[ gameLocal.random.RandomInt( nextRoundPlayers.Num() ) ].First();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// assign arenas for the next round
|
||
|
for ( int i = 0; i < MAX_ARENAS; i += 2 ) {
|
||
|
idPlayer* advanceOne = arenas[ i ].GetWinner();
|
||
|
idPlayer* advanceTwo = arenas[ i + 1 ].GetWinner();
|
||
|
|
||
|
// #13266 - bug: bystander from prev round is spectator in his match arena after round change
|
||
|
// we call rvTourneyInstance::Clear when setting up rounds (below), which sets it's players as spectator
|
||
|
// if the former bystander is placed in one of the new arenas and is still referenced in the bye arena then he'll get set spectating
|
||
|
// so just clear him up once identified
|
||
|
// #13631 - we used to call RemovePlayer before entering that loop, but that was messing up the selection of the next bystander
|
||
|
// so have to do it inside the loop now, just before it happens
|
||
|
if ( ( i == thisByeArena || i + 1 == thisByeArena ) && arenas[ thisByeArena ].HasPlayer( oldByePlayer ) ) {
|
||
|
arenas[ thisByeArena ].RemovePlayer( oldByePlayer );
|
||
|
}
|
||
|
|
||
|
arenas[ i ].Clear();
|
||
|
arenas[ i + 1 ].Clear();
|
||
|
|
||
|
// assign new arenas, swapping oldByePlayer with newByePlayer
|
||
|
if ( newByePlayer && oldByePlayer ) {
|
||
|
if ( advanceOne == newByePlayer ) {
|
||
|
advanceOne = oldByePlayer;
|
||
|
} else if ( advanceTwo == newByePlayer ) {
|
||
|
advanceTwo = oldByePlayer;
|
||
|
} else if ( advanceOne == oldByePlayer ) {
|
||
|
advanceOne = newByePlayer;
|
||
|
} else if ( advanceTwo == oldByePlayer ) {
|
||
|
advanceTwo = newByePlayer;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( advanceOne || advanceTwo ) {
|
||
|
arenas[ (i / 2) ].AddPlayers( advanceOne, advanceTwo );
|
||
|
|
||
|
if ( advanceOne ) {
|
||
|
advanceOne->JoinInstance( (i / 2) );
|
||
|
advanceOne->ServerSpectate( false );
|
||
|
}
|
||
|
|
||
|
if ( advanceTwo ) {
|
||
|
advanceTwo->JoinInstance( (i / 2) );
|
||
|
advanceTwo->ServerSpectate( false );
|
||
|
}
|
||
|
|
||
|
gameLocal.Printf( "Round %d: Arena %d is starting play with players %d (%s) and %d (%s)\n", round, (i / 2), advanceOne ? advanceOne->entityNumber : -1, advanceOne ? advanceOne->GetUserInfo()->GetString( "ui_name" ) : "NULL", advanceTwo ? advanceTwo->entityNumber : -1, advanceTwo ? advanceTwo->GetUserInfo()->GetString( "ui_name" ) : "NULL" );
|
||
|
|
||
|
arenas[ (i / 2) ].Ready();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// once the new round is setup, go through and make sure all the spectators are in valid arena
|
||
|
for( int i = 0; i < gameLocal.numClients; i++ ) {
|
||
|
idPlayer* p = (idPlayer*)gameLocal.entities[ i ];
|
||
|
|
||
|
// re-select our arena since round changed
|
||
|
if( p && p->spectating ) {
|
||
|
rvTourneyArena& thisArena = arenas[ p->GetArena() ];
|
||
|
if( thisArena.GetState() != AS_WARMUP ) {
|
||
|
p->JoinInstance( GetNextActiveArena( p->GetArena() ) );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
rvTourneyGameState::NewState
|
||
|
================
|
||
|
*/
|
||
|
void rvTourneyGameState::NewState( mpGameState_t newState ) {
|
||
|
assert( (newState != currentState) && gameLocal.isServer );
|
||
|
|
||
|
switch( newState ) {
|
||
|
case WARMUP: {
|
||
|
Reset();
|
||
|
// force everyone to spectate
|
||
|
for( int i = 0; i < MAX_CLIENTS; i++ ) {
|
||
|
if( gameLocal.entities[ i ] ) {
|
||
|
((idPlayer*)gameLocal.entities[ i ])->ServerSpectate( true );
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
case GAMEON: {
|
||
|
tourneyCount++;
|
||
|
SetupInitialBrackets();
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
rvGameState::NewState( newState );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
rvTourneyGameState::GameStateChanged
|
||
|
================
|
||
|
*/
|
||
|
void rvTourneyGameState::GameStateChanged( void ) {
|
||
|
assert( IsType( rvTourneyGameState::GetClassType() ) );
|
||
|
rvGameState::GameStateChanged();
|
||
|
idPlayer* localPlayer = gameLocal.GetLocalPlayer();
|
||
|
|
||
|
int oldRound = ((rvTourneyGameState*)previousGameState)->round;
|
||
|
|
||
|
if( round != oldRound ) {
|
||
|
if ( round > maxRound && gameLocal.GetLocalPlayer() ) {
|
||
|
gameLocal.GetLocalPlayer()->ForceScoreboard( true, gameLocal.time + 5000 );
|
||
|
gameLocal.mpGame.ScheduleAnnouncerSound( AS_TOURNEY_DONE, gameLocal.time );
|
||
|
}
|
||
|
|
||
|
// we're starting a new round
|
||
|
gameLocal.mpGame.tourneyGUI.RoundStart();
|
||
|
|
||
|
// skip announce if the round number doesn't make sense
|
||
|
if ( round >= 1 && round <= MAX_ROUNDS ) {
|
||
|
// play the sound a bit after round restart to let spawn sounds settle
|
||
|
gameLocal.mpGame.ScheduleAnnouncerSound( (announcerSound_t)( AS_TOURNEY_PRELIMS + round - 1 ), gameLocal.time + 1500);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for( int i = 0; i < MAX_ARENAS; i++ ) {
|
||
|
rvTourneyArena& thisArena = arenas[ i ];
|
||
|
rvTourneyArena& previousArena = ((rvTourneyGameState*)previousGameState)->arenas[ i ];
|
||
|
|
||
|
if( ((thisArena.GetPlayers()[ 0 ] != previousArena.GetPlayers()[ 0 ]) ||
|
||
|
(thisArena.GetPlayers()[ 1 ] != previousArena.GetPlayers()[ 1 ]) ||
|
||
|
(round != ((rvTourneyGameState*)previousGameState)->round )) /*&&
|
||
|
!((thisArena.GetPlayers()[ 0 ] == NULL && thisArena.GetPlayers()[ 1 ] != NULL) ||
|
||
|
(thisArena.GetPlayers()[ 0 ] != NULL && thisArena.GetPlayers()[ 1 ] == NULL) ) */) {
|
||
|
|
||
|
// don't re-init arenas that have changed and been done for a while
|
||
|
if( thisArena.GetState() != AS_DONE || previousArena.GetState() != AS_DONE ) {
|
||
|
gameLocal.mpGame.tourneyGUI.ArenaStart( i );
|
||
|
}
|
||
|
|
||
|
if( localPlayer && thisArena.GetPlayers()[ 0 ] == localPlayer ) {
|
||
|
gameLocal.mpGame.tourneyGUI.ArenaSelect( i, TGH_PLAYER_ONE );
|
||
|
}
|
||
|
if( localPlayer && thisArena.GetPlayers()[ 1 ] == localPlayer ) {
|
||
|
gameLocal.mpGame.tourneyGUI.ArenaSelect( i, TGH_PLAYER_TWO );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if( localPlayer->GetArena() == thisArena.GetArenaID() && thisArena.GetState() == AS_SUDDEN_DEATH && thisArena.GetState() != previousArena.GetState() ) {
|
||
|
gameLocal.mpGame.ScheduleAnnouncerSound( AS_GENERAL_SUDDEN_DEATH, gameLocal.time, thisArena.GetArenaID() );
|
||
|
gameLocal.GetLocalPlayer()->GUIMainNotice( common->GetLocalizedString( "#str_104287" ) );
|
||
|
}
|
||
|
|
||
|
if( thisArena.GetState() == AS_DONE && thisArena.GetState() != previousArena.GetState() ) {
|
||
|
if( thisArena.GetWinner() != previousArena.GetWinner() && thisArena.GetWinner() == gameLocal.GetLocalPlayer() ) {
|
||
|
if( round >= maxRound ) {
|
||
|
gameLocal.mpGame.ScheduleAnnouncerSound( AS_TOURNEY_WON, gameLocal.time );
|
||
|
} else {
|
||
|
gameLocal.mpGame.ScheduleAnnouncerSound( AS_TOURNEY_ADVANCE, gameLocal.time );
|
||
|
}
|
||
|
} else if( thisArena.GetLoser() != previousArena.GetLoser() && thisArena.GetLoser() == gameLocal.GetLocalPlayer() ) {
|
||
|
gameLocal.mpGame.ScheduleAnnouncerSound( AS_TOURNEY_ELIMINATED, gameLocal.time );
|
||
|
}
|
||
|
|
||
|
if( previousArena.GetWinner() == NULL && thisArena.GetWinner() != NULL ) {
|
||
|
gameLocal.mpGame.tourneyGUI.ArenaDone( i );
|
||
|
}
|
||
|
|
||
|
// on the client, add this result to our tourneyHistory
|
||
|
//if( gameLocal.isClient && thisArena.GetPlayers()[ 0 ] && thisArena.GetPlayers()[ 1 ] && (round - 1 ) >= 0 && (round - 1) < MAX_ROUNDS ) {
|
||
|
// tourneyHistory[ round - 1 ][ i ].playerOneScore = gameLocal.mpGame.GetTeamScore( thisArena.GetPlayers()[ 0 ] );
|
||
|
// tourneyHistory[ round - 1 ][ i ].playerTwoScore = gameLocal.mpGame.GetTeamScore( thisArena.GetPlayers()[ 1 ] );
|
||
|
//}
|
||
|
}
|
||
|
|
||
|
if( localPlayer && (thisArena.GetState() == AS_WARMUP) && (thisArena.GetState() != previousArena.GetState()) && localPlayer->GetArena() == thisArena.GetArenaID() ) {
|
||
|
gameLocal.mpGame.RemoveAnnouncerSound( AS_GENERAL_PREPARE_TO_FIGHT );
|
||
|
gameLocal.mpGame.RemoveAnnouncerSound( AS_GENERAL_THREE );
|
||
|
gameLocal.mpGame.RemoveAnnouncerSound( AS_GENERAL_TWO );
|
||
|
gameLocal.mpGame.RemoveAnnouncerSound( AS_GENERAL_ONE );
|
||
|
|
||
|
int warmupEndTime = gameLocal.time + ( gameLocal.serverInfo.GetInt( "si_countdown" ) * 1000 ) + 5000;
|
||
|
gameLocal.mpGame.ScheduleAnnouncerSound( AS_GENERAL_PREPARE_TO_FIGHT, gameLocal.time + 5000 );
|
||
|
gameLocal.mpGame.ScheduleAnnouncerSound( AS_GENERAL_THREE, warmupEndTime - 3000 );
|
||
|
gameLocal.mpGame.ScheduleAnnouncerSound( AS_GENERAL_TWO, warmupEndTime - 2000 );
|
||
|
gameLocal.mpGame.ScheduleAnnouncerSound( AS_GENERAL_ONE, warmupEndTime - 1000 );
|
||
|
|
||
|
localPlayer->vsMsgState = true;
|
||
|
localPlayer->GUIMainNotice( va( "%s^0 vs. %s^0", thisArena.GetPlayerName( 0 ), thisArena.GetPlayerName( 1 ) ), true );
|
||
|
}
|
||
|
|
||
|
if( (thisArena.GetState() == AS_ROUND) && (thisArena.GetState() != previousArena.GetState()) ) {
|
||
|
if( localPlayer && localPlayer->GetArena() == thisArena.GetArenaID() ) {
|
||
|
gameLocal.mpGame.ScheduleAnnouncerSound( AS_GENERAL_FIGHT, gameLocal.time );
|
||
|
gameLocal.mpGame.ScheduleTimeAnnouncements();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if( ((rvTourneyGameState*)previousGameState)->currentState != currentState ) {
|
||
|
if( currentState == WARMUP ) {
|
||
|
gameLocal.mpGame.tourneyGUI.PreTourney();
|
||
|
} else if( currentState == COUNTDOWN ) {
|
||
|
if( currentState == COUNTDOWN && ((rvTourneyGameState*)previousGameState)->currentState != INACTIVE ) {
|
||
|
gameLocal.mpGame.ScheduleAnnouncerSound( AS_TOURNEY_START, gameLocal.time);
|
||
|
}
|
||
|
} else if( currentState == GAMEON ) {
|
||
|
gameLocal.mpGame.tourneyGUI.TourneyStart();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if( packTourneyHistory ) {
|
||
|
// apply history
|
||
|
gameLocal.mpGame.tourneyGUI.SetupTourneyHistory( startingRound, round - 1, tourneyHistory );
|
||
|
packTourneyHistory = false;
|
||
|
}
|
||
|
|
||
|
if( localPlayer && localPlayer->spectating ) {
|
||
|
|
||
|
rvTourneyArena& thisArena = arenas[ localPlayer->GetArena() ];
|
||
|
if( thisArena.GetPlayers()[ 0 ] == NULL || thisArena.GetPlayers()[ 1 ] == NULL || (localPlayer->spectating && localPlayer->spectator != thisArena.GetPlayers()[ 0 ]->entityNumber && localPlayer->spectator != thisArena.GetPlayers()[ 1 ]->entityNumber) ) {
|
||
|
gameLocal.mpGame.tourneyGUI.ArenaSelect( localPlayer->GetArena(), TGH_BRACKET );
|
||
|
} else if ( thisArena.GetPlayers()[ 0 ] == localPlayer || (localPlayer->spectating && thisArena.GetPlayers()[ 0 ]->entityNumber == localPlayer->spectator) ) {
|
||
|
gameLocal.mpGame.tourneyGUI.ArenaSelect( localPlayer->GetArena(), TGH_PLAYER_ONE );
|
||
|
} else if ( thisArena.GetPlayers()[ 1 ] == localPlayer || (localPlayer->spectating && thisArena.GetPlayers()[ 1 ]->entityNumber == localPlayer->spectator) ) {
|
||
|
gameLocal.mpGame.tourneyGUI.ArenaSelect( localPlayer->GetArena(), TGH_PLAYER_TWO );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
msgTourney_t
|
||
|
Identifiers for transmitted state
|
||
|
================
|
||
|
*/
|
||
|
typedef enum {
|
||
|
MSG_TOURNEY_TOURNEYSTATE,
|
||
|
MSG_TOURNEY_STARTINGROUND,
|
||
|
MSG_TOURNEY_ROUND,
|
||
|
MSG_TOURNEY_MAXROUND,
|
||
|
MSG_TOURNEY_HISTORY,
|
||
|
MSG_TOURNEY_TOURNEYCOUNT,
|
||
|
MSG_TOURNEY_ARENAINFO
|
||
|
} msgTourney_t;
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
rvTourneyGameState::PackState
|
||
|
================
|
||
|
*/
|
||
|
void rvTourneyGameState::PackState( idBitMsg& outMsg ) {
|
||
|
assert( IsType( rvTourneyGameState::GetClassType() ) );
|
||
|
|
||
|
if ( tourneyState != ((rvTourneyGameState*)previousGameState)->tourneyState ) {
|
||
|
outMsg.WriteByte( MSG_TOURNEY_TOURNEYSTATE );
|
||
|
outMsg.WriteByte( tourneyState );
|
||
|
}
|
||
|
|
||
|
if ( startingRound != ((rvTourneyGameState*)previousGameState)->startingRound ) {
|
||
|
outMsg.WriteByte( MSG_TOURNEY_STARTINGROUND );
|
||
|
outMsg.WriteByte( startingRound );
|
||
|
}
|
||
|
|
||
|
if( round != ((rvTourneyGameState*)previousGameState)->round ) {
|
||
|
outMsg.WriteByte( MSG_TOURNEY_ROUND );
|
||
|
outMsg.WriteByte( round );
|
||
|
}
|
||
|
|
||
|
if( maxRound != ((rvTourneyGameState*)previousGameState)->maxRound ) {
|
||
|
outMsg.WriteByte( MSG_TOURNEY_MAXROUND );
|
||
|
outMsg.WriteByte( maxRound );
|
||
|
}
|
||
|
|
||
|
for ( int i = 0; i < MAX_ARENAS; i++ ) {
|
||
|
if ( arenas[ i ] != ((rvTourneyGameState*)previousGameState)->arenas[ i ] ) {
|
||
|
outMsg.WriteByte( MSG_TOURNEY_ARENAINFO + i );
|
||
|
arenas[ i ].PackState( outMsg );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( packTourneyHistory || forceTourneyHistory ) {
|
||
|
if ( forceTourneyHistory ) {
|
||
|
common->Warning( "forcing down tourney history\n" );
|
||
|
}
|
||
|
// don't write out uninitialized tourney history
|
||
|
if ( startingRound > 0 && round > 1 ) {
|
||
|
outMsg.WriteByte( MSG_TOURNEY_HISTORY );
|
||
|
|
||
|
// client might not yet have startingRound or round
|
||
|
outMsg.WriteByte( startingRound );
|
||
|
outMsg.WriteChar( round );
|
||
|
outMsg.WriteByte( maxRound );
|
||
|
|
||
|
for( int i = startingRound - 1; i <= Min( (round - 1), (maxRound - 1) ); i++ ) {
|
||
|
for( int j = 0; j < MAX_ARENAS / (i + 1); j++ ) {
|
||
|
outMsg.WriteString( tourneyHistory[ i ][ j ].playerOne, MAX_TOURNEY_HISTORY_NAME_LEN );
|
||
|
outMsg.WriteChar( idMath::ClampChar( tourneyHistory[ i ][ j ].playerOneScore ) );
|
||
|
outMsg.WriteString( tourneyHistory[ i ][ j ].playerTwo, MAX_TOURNEY_HISTORY_NAME_LEN );
|
||
|
outMsg.WriteChar( idMath::ClampChar( tourneyHistory[ i ][ j ].playerTwoScore ) );
|
||
|
outMsg.WriteChar( tourneyHistory[ i ][ j ].playerOneNum );
|
||
|
outMsg.WriteChar( tourneyHistory[ i ][ j ].playerTwoNum );
|
||
|
}
|
||
|
}
|
||
|
packTourneyHistory = false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( tourneyCount != ((rvTourneyGameState*)previousGameState)->tourneyCount ) {
|
||
|
outMsg.WriteByte( MSG_TOURNEY_TOURNEYCOUNT );
|
||
|
outMsg.WriteByte( tourneyCount );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
rvTourneyGameState::UnpackState
|
||
|
================
|
||
|
*/
|
||
|
void rvTourneyGameState::UnpackState( const idBitMsg& inMsg ) {
|
||
|
assert( IsType( rvTourneyGameState::GetClassType() ) );
|
||
|
|
||
|
while( inMsg.GetRemainingData() ) {
|
||
|
int index = inMsg.ReadByte();
|
||
|
|
||
|
switch( index ) {
|
||
|
case MSG_TOURNEY_TOURNEYSTATE: {
|
||
|
tourneyState = (tourneyState_t)inMsg.ReadByte();
|
||
|
break;
|
||
|
}
|
||
|
case MSG_TOURNEY_STARTINGROUND: {
|
||
|
startingRound = inMsg.ReadByte();
|
||
|
break;
|
||
|
}
|
||
|
case MSG_TOURNEY_ROUND: {
|
||
|
round = inMsg.ReadByte();
|
||
|
// not a great way of doing this, should clamp the values
|
||
|
if ( round == 255 ) {
|
||
|
round = -1;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
case MSG_TOURNEY_MAXROUND: {
|
||
|
maxRound = inMsg.ReadByte();
|
||
|
if ( maxRound == 255 ) {
|
||
|
maxRound = -1;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
case MSG_TOURNEY_HISTORY: {
|
||
|
int startRound = inMsg.ReadByte();
|
||
|
int rnd = inMsg.ReadChar();
|
||
|
int maxr = inMsg.ReadByte();
|
||
|
|
||
|
assert( rnd >= 1 ); // something is uninitialized
|
||
|
|
||
|
for( int i = startRound - 1; i <= Min( (rnd - 1), (maxr - 1) ); i++ ) {
|
||
|
for( int j = 0; j < MAX_ARENAS / (i + 1); j++ ) {
|
||
|
inMsg.ReadString( tourneyHistory[ i ][ j ].playerOne, MAX_TOURNEY_HISTORY_NAME_LEN );
|
||
|
tourneyHistory[ i ][ j ].playerOneScore = inMsg.ReadChar();
|
||
|
inMsg.ReadString( tourneyHistory[ i ][ j ].playerTwo, MAX_TOURNEY_HISTORY_NAME_LEN );
|
||
|
tourneyHistory[ i ][ j ].playerTwoScore = inMsg.ReadChar();
|
||
|
tourneyHistory[ i ][ j ].playerOneNum = inMsg.ReadChar();
|
||
|
tourneyHistory[ i ][ j ].playerTwoNum = inMsg.ReadChar();
|
||
|
|
||
|
if( tourneyHistory[ i ][ j ].playerOneNum < 0 || tourneyHistory[ i ][ j ].playerOneNum >= MAX_CLIENTS ) {
|
||
|
tourneyHistory[ i ][ j ].playerOneNum = -1;
|
||
|
}
|
||
|
|
||
|
if( tourneyHistory[ i ][ j ].playerTwoNum < 0 || tourneyHistory[ i ][ j ].playerTwoNum >= MAX_CLIENTS ) {
|
||
|
tourneyHistory[ i ][ j ].playerTwoNum = -1;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// client side (and listen server) no reason to check for history change all the time
|
||
|
packTourneyHistory = true;
|
||
|
break;
|
||
|
}
|
||
|
case MSG_TOURNEY_TOURNEYCOUNT: {
|
||
|
tourneyCount = inMsg.ReadByte();
|
||
|
break;
|
||
|
}
|
||
|
default: {
|
||
|
if ( index >= MSG_TOURNEY_ARENAINFO && index < MSG_TOURNEY_ARENAINFO + MAX_ARENAS ) {
|
||
|
arenas[ index - MSG_TOURNEY_ARENAINFO ].UnpackState( inMsg );
|
||
|
} else {
|
||
|
gameLocal.Error( "rvTourneyGameState::UnpackState() - Unknown data identifier '%d'\n", index );
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
rvTourneyGameState::SendState
|
||
|
================
|
||
|
*/
|
||
|
void rvTourneyGameState::SendState( const idMessageSender &sender, int clientNum ) {
|
||
|
idBitMsg outMsg;
|
||
|
byte msgBuf[MAX_GAME_MESSAGE_SIZE];
|
||
|
|
||
|
assert( (gameLocal.isServer || gameLocal.isRepeater) && trackPrevious && IsType( rvTourneyGameState::GetClassType() ) );
|
||
|
|
||
|
if ( clientNum == -1 && (rvTourneyGameState&)(*this) == (rvTourneyGameState&)(*previousGameState) ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
outMsg.Init( msgBuf, sizeof( msgBuf ) );
|
||
|
outMsg.WriteByte( GAME_RELIABLE_MESSAGE_GAMESTATE );
|
||
|
|
||
|
WriteState( outMsg );
|
||
|
|
||
|
if( clientNum == -1 && forceTourneyHistory ) {
|
||
|
forceTourneyHistory = false;
|
||
|
}
|
||
|
|
||
|
sender.Send( outMsg );
|
||
|
|
||
|
// don't update the state if we are working for a single client
|
||
|
if ( clientNum == -1 ) {
|
||
|
outMsg.ReadByte(); // pop off the msg ID
|
||
|
ReceiveState( outMsg );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===============
|
||
|
rvTourneyGameState::WriteState
|
||
|
===============
|
||
|
*/
|
||
|
void rvTourneyGameState::WriteState( idBitMsg &msg ) {
|
||
|
// send off base info
|
||
|
rvGameState::PackState( msg );
|
||
|
// add Tourney info
|
||
|
PackState( msg );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
rvTourneyGameState::ReceiveState
|
||
|
================
|
||
|
*/
|
||
|
void rvTourneyGameState::ReceiveState( const idBitMsg& msg ) {
|
||
|
assert( IsType( rvTourneyGameState::GetClassType() ) );
|
||
|
|
||
|
if ( !rvGameState::BaseUnpackState( msg ) ) {
|
||
|
return;
|
||
|
}
|
||
|
UnpackState( msg );
|
||
|
|
||
|
if ( gameLocal.localClientNum >= 0 ) {
|
||
|
GameStateChanged();
|
||
|
}
|
||
|
|
||
|
(rvTourneyGameState&)(*previousGameState) = (rvTourneyGameState&)(*this);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
rvTourneyGameState::SendInitialState
|
||
|
================
|
||
|
*/
|
||
|
void rvTourneyGameState::SendInitialState( const idMessageSender &sender, int clientNum ) {
|
||
|
assert( IsType( rvTourneyGameState::GetClassType() ) );
|
||
|
|
||
|
rvTourneyGameState* previousState = (rvTourneyGameState*)previousGameState;
|
||
|
|
||
|
rvTourneyGameState invalidState;
|
||
|
|
||
|
previousGameState = &invalidState;
|
||
|
|
||
|
|
||
|
// if the tourney has been going on, transmit the tourney history
|
||
|
if( round > 0 ) {
|
||
|
// comparing the tourney history for all state changes is wasteful when we really just want to send it to new clients
|
||
|
packTourneyHistory = true;
|
||
|
}
|
||
|
|
||
|
SendState( sender, clientNum );
|
||
|
|
||
|
previousGameState = previousState;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
rvTourneyGameState::operator==
|
||
|
================
|
||
|
*/
|
||
|
bool rvTourneyGameState::operator==( const rvTourneyGameState& rhs ) const {
|
||
|
assert( IsType( rvTourneyGameState::GetClassType() ) && rhs.IsType( rvTourneyGameState::GetClassType() ) );
|
||
|
|
||
|
if( (rvGameState&)(*this) != (rvGameState&)rhs ) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if( round != rhs.round || startingRound != rhs.startingRound || maxRound != rhs.maxRound || tourneyState != rhs.tourneyState ) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
for( int i = 0; i < MAX_ARENAS; i++ ) {
|
||
|
if( arenas[ i ] != rhs.arenas[ i ] ) {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
rvTourneyGameState::operator=
|
||
|
================
|
||
|
*/
|
||
|
rvTourneyGameState& rvTourneyGameState::operator=( const rvTourneyGameState& rhs ) {
|
||
|
assert( IsType( rvTourneyGameState::GetClassType() ) && rhs.IsType( rvTourneyGameState::GetClassType() ) );
|
||
|
|
||
|
(rvGameState&)(*this) = (rvGameState&)rhs;
|
||
|
|
||
|
round = rhs.round;
|
||
|
startingRound = rhs.startingRound;
|
||
|
maxRound = rhs.maxRound;
|
||
|
tourneyState = rhs.tourneyState;
|
||
|
|
||
|
for( int i = 0; i < MAX_ARENAS; i++ ) {
|
||
|
arenas[ i ] = rhs.arenas[ i ];
|
||
|
}
|
||
|
|
||
|
return (*this);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
rvTourneyGameState::GetNumArenas
|
||
|
Returns number of active arenas
|
||
|
================
|
||
|
*/
|
||
|
int rvTourneyGameState::GetNumArenas( void ) const {
|
||
|
assert( IsType( rvTourneyGameState::GetClassType() ) );
|
||
|
|
||
|
int num = 0;
|
||
|
|
||
|
for( int i = 0; i < MAX_ARENAS; i++ ) {
|
||
|
if( arenas[ i ].GetState() != AS_INACTIVE && arenas[ i ].GetState() != AS_DONE ) {
|
||
|
num++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return num;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
rvTourneyGameState::SetupInitialBrackets
|
||
|
|
||
|
Sets up the brackets for a new match. fragCount in playerstate is the player's
|
||
|
persistant frag count over an entire level. In teamFragCount we store this
|
||
|
rounds score.
|
||
|
|
||
|
Initial bracket seeds are random
|
||
|
================
|
||
|
*/
|
||
|
void rvTourneyGameState::SetupInitialBrackets( void ) {
|
||
|
assert( IsType( rvTourneyGameState::GetClassType() ) );
|
||
|
idList<idPlayer*> unseededPlayers;
|
||
|
int numRankedPlayers = gameLocal.mpGame.GetNumRankedPlayers();
|
||
|
|
||
|
// all this crazy math does is figure out:
|
||
|
// 8 arenas to 4 rounds ( round 1: 8 arenas, round 2: 4 arenas, round 3: 2 arenas, round 4: 1 arena )
|
||
|
// 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 )
|
||
|
// etc
|
||
|
int newMaxRound = idMath::ILog2( Max( idMath::CeilPowerOfTwo( MAX_ARENAS * 2 ), 2 ) );
|
||
|
|
||
|
// we start at a round appropriate for our # of people
|
||
|
// 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
|
||
|
// start in maxRound - 2, if you have 9 - 16 players start in maxRound - 3, etc.
|
||
|
int newRound = newMaxRound - idMath::ILog2( Max( idMath::CeilPowerOfTwo( gameLocal.mpGame.GetNumRankedPlayers() ), 2 ) ) + 1;
|
||
|
|
||
|
round = newRound;
|
||
|
maxRound = newMaxRound;
|
||
|
startingRound = round;
|
||
|
|
||
|
for( int i = 0; i < numRankedPlayers; i++ ) {
|
||
|
unseededPlayers.Append( gameLocal.mpGame.GetRankedPlayer( i ) );
|
||
|
}
|
||
|
|
||
|
int numArenas = 0;
|
||
|
|
||
|
while ( unseededPlayers.Num() > 1 ) {
|
||
|
idPlayer* playerOne = unseededPlayers[ gameLocal.random.RandomInt( unseededPlayers.Num() ) ];
|
||
|
unseededPlayers.Remove( playerOne );
|
||
|
|
||
|
idPlayer* playerTwo = unseededPlayers[ gameLocal.random.RandomInt( unseededPlayers.Num() ) ];
|
||
|
unseededPlayers.Remove( playerTwo );
|
||
|
|
||
|
rvTourneyArena& arena = arenas[ numArenas ];
|
||
|
|
||
|
arena.Clear();
|
||
|
arena.AddPlayers( playerOne, playerTwo );
|
||
|
|
||
|
// place the players in the correct instance
|
||
|
playerOne->JoinInstance( numArenas );
|
||
|
playerTwo->JoinInstance( numArenas );
|
||
|
|
||
|
playerOne->ServerSpectate( false );
|
||
|
playerTwo->ServerSpectate( false );
|
||
|
|
||
|
gameLocal.mpGame.SetPlayerTeamScore( playerOne, 0 );
|
||
|
gameLocal.mpGame.SetPlayerTeamScore( playerTwo, 0 );
|
||
|
|
||
|
gameLocal.Printf( "rvTourneyGameState::SetupInitialBrackets() - %s will face %s in arena %d\n", playerOne->GetUserInfo()->GetString( "ui_name" ), playerTwo->GetUserInfo()->GetString( "ui_name" ), numArenas );
|
||
|
|
||
|
// this arena is ready to play
|
||
|
arena.Ready();
|
||
|
|
||
|
numArenas++;
|
||
|
}
|
||
|
|
||
|
assert( unseededPlayers.Num() == 0 || unseededPlayers.Num() == 1 );
|
||
|
|
||
|
if( unseededPlayers.Num() ) {
|
||
|
assert( unseededPlayers[ 0 ] );
|
||
|
// the mid player gets a bye
|
||
|
unseededPlayers[ 0 ]->SetTourneyStatus( PTS_UNKNOWN );
|
||
|
unseededPlayers[ 0 ]->ServerSpectate( true );
|
||
|
|
||
|
arenas[ numArenas ].AddPlayers( unseededPlayers[ 0 ], NULL );
|
||
|
arenas[ numArenas ].Ready();
|
||
|
|
||
|
// mark the ancestor arena of the bye player
|
||
|
byeArena = numArenas * startingRound;
|
||
|
} else {
|
||
|
byeArena = -1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
rvTourneyGameState::ClientDisconnect
|
||
|
A player has disconnected from the server
|
||
|
================
|
||
|
*/
|
||
|
void rvTourneyGameState::ClientDisconnect( idPlayer* player ) {
|
||
|
assert( IsType( rvTourneyGameState::GetClassType() ) );
|
||
|
if( gameLocal.isClient ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// go through the tourney history and copy over the disconnecting player's name
|
||
|
if( startingRound > 0 && round <= MAX_ROUNDS ) {
|
||
|
for( int i = startingRound - 1; i < round - 1; i++ ) {
|
||
|
for( int j = 0; j < MAX_ARENAS / (i + 1); j++ ) {
|
||
|
if( tourneyHistory[ i ][ j ].playerOneNum == player->entityNumber ) {
|
||
|
idStr::Copynz( tourneyHistory[ i ][ j ].playerOne, player->GetUserInfo()->GetString( "ui_name" ), MAX_TOURNEY_HISTORY_NAME_LEN );
|
||
|
tourneyHistory[ i ][ j ].playerOneNum = -1;
|
||
|
} else if( tourneyHistory[ i ][ j ].playerTwoNum == player->entityNumber ) {
|
||
|
idStr::Copynz( tourneyHistory[ i ][ j ].playerTwo, player->GetUserInfo()->GetString( "ui_name" ), MAX_TOURNEY_HISTORY_NAME_LEN );
|
||
|
tourneyHistory[ i ][ j ].playerTwoNum = -1;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// copy over the current rounds info if the player is playing
|
||
|
for( int i = 0; i < MAX_ARENAS; i++ ) {
|
||
|
if( arenas[ i ].GetPlayers()[ 0 ] == player ) {
|
||
|
tourneyHistory[ round - 1 ][ i ].playerOneNum = -1;
|
||
|
idStr::Copynz( tourneyHistory[ round - 1 ][ i ].playerOne, player->GetUserInfo()->GetString( "ui_name" ), MAX_TOURNEY_HISTORY_NAME_LEN );
|
||
|
tourneyHistory[ round - 1 ][ i ].playerOneScore = gameLocal.mpGame.GetTeamScore( player );
|
||
|
} else if( arenas[ i ].GetPlayers()[ 1 ] == player ) {
|
||
|
tourneyHistory[ round - 1 ][ i ].playerTwoNum = -1;
|
||
|
idStr::Copynz( tourneyHistory[ round - 1 ][ i ].playerTwo, player->GetUserInfo()->GetString( "ui_name" ), MAX_TOURNEY_HISTORY_NAME_LEN );
|
||
|
tourneyHistory[ round - 1 ][ i ].playerTwoScore = gameLocal.mpGame.GetTeamScore( player );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// retransmit tourney history to everyone
|
||
|
packTourneyHistory = true;
|
||
|
}
|
||
|
|
||
|
RemovePlayer( player );
|
||
|
|
||
|
// give RemovePlayer() a chance to update tourney history if needed
|
||
|
if( packTourneyHistory ) {
|
||
|
for( int i = 0; i < gameLocal.numClients; i++ ) {
|
||
|
if( i == player->entityNumber ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if( gameLocal.entities[ i ] ) {
|
||
|
packTourneyHistory = true;
|
||
|
SendState( serverReliableSender.To( i, true ), i );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( gameLocal.isRepeater ) {
|
||
|
packTourneyHistory = true;
|
||
|
SendState( repeaterReliableSender.To( -1 ), ENTITYNUM_NONE );
|
||
|
}
|
||
|
packTourneyHistory = false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
rvTourneyGameState::Spectate
|
||
|
================
|
||
|
*/
|
||
|
void rvTourneyGameState::Spectate( idPlayer* player ) {
|
||
|
assert( IsType( rvTourneyGameState::GetClassType() ) );
|
||
|
assert( gameLocal.isServer || player->IsFakeClient() );
|
||
|
|
||
|
if( player->spectating && player->wantSpectate ) {
|
||
|
RemovePlayer( player );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
rvTourneyGameState::RemovePlayer
|
||
|
Removes the specified player from the arena
|
||
|
================
|
||
|
*/
|
||
|
void rvTourneyGameState::RemovePlayer( idPlayer* player ) {
|
||
|
assert( IsType( rvTourneyGameState::GetClassType() ) );
|
||
|
|
||
|
if( round < 1 || round > maxRound ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
for( int i = 0; i < MAX_ARENAS; i++ ) {
|
||
|
idPlayer** players = GetArenaPlayers( i );
|
||
|
|
||
|
if( players[ 0 ] == player || players[ 1 ] == player ) {
|
||
|
rvTourneyArena& arena = arenas[ i ];
|
||
|
|
||
|
idPlayer* remainingPlayer = players[ 0 ] == player ? players[ 1 ] : players[ 0 ];
|
||
|
|
||
|
bool arenaInProgress = arena.GetState() == AS_ROUND || arena.GetState() == AS_WARMUP || arena.GetState() == AS_SUDDEN_DEATH;
|
||
|
bool remainingIsWinner = (remainingPlayer == arena.GetWinner());
|
||
|
int remainingIndex = (remainingPlayer == arena.GetPlayers()[ 0 ]) ? 0 : 1;
|
||
|
|
||
|
arena.RemovePlayer( player );
|
||
|
|
||
|
// both players have disconnected from this arena, or the winner has disconnected, just return
|
||
|
if( (!arena.GetPlayers()[ 0 ] && !arena.GetPlayers()[ 1 ]) || (!arenaInProgress && remainingPlayer && !remainingIsWinner) ) {
|
||
|
arena.Clear();
|
||
|
tourneyHistory[ round - 1 ][ i ].playerOneNum = -1;
|
||
|
tourneyHistory[ round - 1 ][ i ].playerOne[ 0 ] = '\0';
|
||
|
tourneyHistory[ round - 1 ][ i ].playerTwoNum = -1;
|
||
|
tourneyHistory[ round - 1 ][ i ].playerTwo[ 0 ] = '\0';
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
assert( remainingPlayer );
|
||
|
|
||
|
// if this arena is in progress, try restarting
|
||
|
// ATVI DevTrack #13196 - do not put the bye player into a game with the abandonned player anymore
|
||
|
// if ( arenaInProgress && byeArena >= 0 && arenas[ byeArena / round ].GetWinner() ) {
|
||
|
if ( 0 ) {
|
||
|
idPlayer* byePlayer = arenas[ byeArena / round ].GetWinner();
|
||
|
|
||
|
// reset history for this new round
|
||
|
tourneyHistory[ round - 1 ][ i ].playerOneNum = -1;
|
||
|
tourneyHistory[ round - 1 ][ i ].playerOne[ 0 ] = '\0';
|
||
|
tourneyHistory[ round - 1 ][ i ].playerTwoNum = -1;
|
||
|
tourneyHistory[ round - 1 ][ i ].playerTwo[ 0 ] = '\0';
|
||
|
|
||
|
arena.Clear();
|
||
|
arenas[ byeArena / round ].Clear();
|
||
|
|
||
|
arena.AddPlayers( remainingPlayer, byePlayer );
|
||
|
|
||
|
// place the players in the correct instance
|
||
|
remainingPlayer->JoinInstance( i );
|
||
|
byePlayer->JoinInstance( i );
|
||
|
|
||
|
remainingPlayer->ServerSpectate( false );
|
||
|
byePlayer->ServerSpectate( false );
|
||
|
|
||
|
gameLocal.mpGame.SetPlayerTeamScore( remainingPlayer, 0 );
|
||
|
gameLocal.mpGame.SetPlayerTeamScore( byePlayer, 0 );
|
||
|
|
||
|
gameLocal.Printf( "rvTourneyManager::RemovePlayer() - %s will face %s in arena %d\n", remainingPlayer->GetUserInfo()->GetString( "ui_name" ), byePlayer->GetUserInfo()->GetString( "ui_name" ), i );
|
||
|
|
||
|
byeArena = -1;
|
||
|
// this arena is ready to play
|
||
|
arena.Ready();
|
||
|
} else {
|
||
|
// if the arena was in progress and didn't get a bye player to step in, this becomes a bye
|
||
|
// arena - clear the tourney history
|
||
|
if( arenaInProgress ) {
|
||
|
tourneyHistory[ round - 1 ][ i ].playerOneNum = -1;
|
||
|
tourneyHistory[ round - 1 ][ i ].playerOne[ 0 ] = '\0';
|
||
|
tourneyHistory[ round - 1 ][ i ].playerOneScore = -1;
|
||
|
|
||
|
tourneyHistory[ round - 1 ][ i ].playerTwoNum = -1;
|
||
|
tourneyHistory[ round - 1 ][ i ].playerTwo[ 0 ] = '\0';
|
||
|
tourneyHistory[ round - 1 ][ i ].playerTwoScore = -1;
|
||
|
} else {
|
||
|
// since the player is disconnecting, write the history for this round
|
||
|
if( remainingIndex == 0 ) {
|
||
|
tourneyHistory[ round - 1 ][ i ].playerOneNum = remainingPlayer->entityNumber;
|
||
|
tourneyHistory[ round - 1 ][ i ].playerOne[ 0 ] = '\0';
|
||
|
tourneyHistory[ round - 1 ][ i ].playerOneScore = gameLocal.mpGame.GetTeamScore( remainingPlayer );
|
||
|
} else {
|
||
|
tourneyHistory[ round - 1 ][ i ].playerTwoNum = remainingPlayer->entityNumber;
|
||
|
tourneyHistory[ round - 1 ][ i ].playerTwo[ 0 ] = '\0';
|
||
|
tourneyHistory[ round - 1 ][ i ].playerTwoScore = gameLocal.mpGame.GetTeamScore( remainingPlayer );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int rvTourneyGameState::GetNextActiveArena( int arena ) {
|
||
|
assert( IsType( rvTourneyGameState::GetClassType() ) );
|
||
|
|
||
|
for( int i = arena + 1; i < MAX_ARENAS; i++ ) {
|
||
|
if( arenas[ i ].GetState() != AS_INACTIVE && arenas[ i ].GetState() != AS_DONE ) {
|
||
|
return i;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for( int i = 0; i < arena; i++ ) {
|
||
|
if( arenas[ i ].GetState() != AS_INACTIVE && arenas[ i ].GetState() != AS_DONE ) {
|
||
|
return i;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return arena;
|
||
|
}
|
||
|
|
||
|
int rvTourneyGameState::GetPrevActiveArena( int arena ) {
|
||
|
assert( IsType( rvTourneyGameState::GetClassType() ) );
|
||
|
|
||
|
for( int i = arena - 1; i >= 0; i-- ) {
|
||
|
if( arenas[ i ].GetState() != AS_INACTIVE && arenas[ i ].GetState() != AS_DONE ) {
|
||
|
return i;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for( int i = MAX_ARENAS - 1; i > arena; i-- ) {
|
||
|
if( arenas[ i ].GetState() != AS_INACTIVE && arenas[ i ].GetState() != AS_DONE ) {
|
||
|
return i;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return arena;
|
||
|
}
|
||
|
|
||
|
void rvTourneyGameState::SpectateCycleNext( idPlayer* player ) {
|
||
|
assert( IsType( rvTourneyGameState::GetClassType() ) );
|
||
|
assert( gameLocal.isServer || player->IsFakeClient() );
|
||
|
|
||
|
rvTourneyArena& spectatingArena = arenas[ player->GetArena() ];
|
||
|
|
||
|
idPlayer** players = spectatingArena.GetPlayers();
|
||
|
|
||
|
if( !players[ 0 ] || !players[ 1 ] || players[ 0 ]->spectating || players[ 1 ]->spectating ) {
|
||
|
// setting the spectated client to ourselves will unlock us
|
||
|
player->spectator = player->entityNumber;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if( player->spectator != players[ 0 ]->entityNumber && player->spectator != players[ 1 ]->entityNumber ) {
|
||
|
player->spectator = players[ 0 ]->entityNumber;
|
||
|
} else if( player->spectator == players[ 0 ]->entityNumber ) {
|
||
|
player->spectator = players[ 1 ]->entityNumber;
|
||
|
} else if( player->spectator == players[ 1 ]->entityNumber ) {
|
||
|
if( gameLocal.time > player->lastArenaChange ) {
|
||
|
if ( GetNumArenas() <= 0 ) {
|
||
|
player->JoinInstance( 0 );
|
||
|
} else {
|
||
|
player->JoinInstance( GetNextActiveArena( player->GetArena() ) );
|
||
|
}
|
||
|
player->lastArenaChange = gameLocal.time + 2000;
|
||
|
player->spectator = player->entityNumber;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// this is where the listen server updates it's gui spectating elements
|
||
|
if ( gameLocal.GetLocalPlayer() == player ) {
|
||
|
rvTourneyArena& arena = arenas[ player->GetArena() ];
|
||
|
|
||
|
if( arena.GetPlayers()[ 0 ] == NULL || arena.GetPlayers()[ 1 ] == NULL || (player->spectating && player->spectator != arena.GetPlayers()[ 0 ]->entityNumber && player->spectator != arena.GetPlayers()[ 1 ]->entityNumber) ) {
|
||
|
gameLocal.mpGame.tourneyGUI.ArenaSelect( player->GetArena(), TGH_BRACKET );
|
||
|
} else if( arena.GetPlayers()[ 0 ] == player || player->spectator == arena.GetPlayers()[ 0 ]->entityNumber ) {
|
||
|
gameLocal.mpGame.tourneyGUI.ArenaSelect( player->GetArena(), TGH_PLAYER_ONE );
|
||
|
} else if( arena.GetPlayers()[ 1 ] == player || player->spectator == arena.GetPlayers()[ 1 ]->entityNumber ) {
|
||
|
gameLocal.mpGame.tourneyGUI.ArenaSelect( player->GetArena(), TGH_PLAYER_TWO );
|
||
|
}
|
||
|
|
||
|
gameLocal.mpGame.tourneyGUI.UpdateScores();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void rvTourneyGameState::SpectateCyclePrev( idPlayer* player ) {
|
||
|
assert( IsType( rvTourneyGameState::GetClassType() ) );
|
||
|
assert( gameLocal.isServer || player->IsFakeClient() );
|
||
|
|
||
|
rvTourneyArena& spectatingArena = arenas[ player->GetArena() ];
|
||
|
|
||
|
idPlayer** players = spectatingArena.GetPlayers();
|
||
|
|
||
|
if( !players[ 0 ] || !players[ 1 ] || players[ 0 ]->spectating || players[ 1 ]->spectating ) {
|
||
|
// setting the spectated client to ourselves will unlock us
|
||
|
player->spectator = player->entityNumber;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if( player->spectator != players[ 0 ]->entityNumber && player->spectator != players[ 1 ]->entityNumber ) {
|
||
|
if( gameLocal.time > player->lastArenaChange ) {
|
||
|
if ( GetNumArenas() <= 0 ) {
|
||
|
player->JoinInstance( 0 );
|
||
|
} else {
|
||
|
player->JoinInstance( GetPrevActiveArena( player->GetArena() ) );
|
||
|
}
|
||
|
player->lastArenaChange = gameLocal.time + 2000;
|
||
|
|
||
|
rvTourneyArena& newSpectatingArena = arenas[ player->GetArena() ];
|
||
|
|
||
|
idPlayer** newPlayers = newSpectatingArena.GetPlayers();
|
||
|
|
||
|
if( !newPlayers[ 0 ] || !newPlayers[ 1 ] || newPlayers[ 0 ]->spectating || newPlayers[ 1 ]->spectating ) {
|
||
|
// setting the spectated client to ourselves will unlock us
|
||
|
player->spectator = player->entityNumber;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
player->spectator = newPlayers[ 1 ]->entityNumber;
|
||
|
}
|
||
|
} else if( player->spectator == players[ 0 ]->entityNumber ) {
|
||
|
player->spectator = player->entityNumber;
|
||
|
} else if( player->spectator == players[ 1 ]->entityNumber ) {
|
||
|
player->spectator = players[ 0 ]->entityNumber;
|
||
|
}
|
||
|
|
||
|
// this is where the listen server updates it gui spectating elements
|
||
|
if( gameLocal.GetLocalPlayer() == player ) {
|
||
|
rvTourneyArena& arena = arenas[ player->GetArena() ];
|
||
|
|
||
|
if( arena.GetPlayers()[ 0 ] == NULL || arena.GetPlayers()[ 1 ] == NULL || (player->spectating && player->spectator != arena.GetPlayers()[ 0 ]->entityNumber && player->spectator != arena.GetPlayers()[ 1 ]->entityNumber) ) {
|
||
|
gameLocal.mpGame.tourneyGUI.ArenaSelect( player->GetArena(), TGH_BRACKET );
|
||
|
} else if( arena.GetPlayers()[ 0 ] == player || player->spectator == arena.GetPlayers()[ 0 ]->entityNumber ) {
|
||
|
gameLocal.mpGame.tourneyGUI.ArenaSelect( player->GetArena(), TGH_PLAYER_ONE );
|
||
|
} else if( arena.GetPlayers()[ 1 ] == player || player->spectator == arena.GetPlayers()[ 1 ]->entityNumber ) {
|
||
|
gameLocal.mpGame.tourneyGUI.ArenaSelect( player->GetArena(), TGH_PLAYER_TWO );
|
||
|
}
|
||
|
|
||
|
gameLocal.mpGame.tourneyGUI.UpdateScores();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void rvTourneyGameState::UpdateTourneyBrackets( void ) {
|
||
|
assert( IsType( rvTourneyGameState::GetClassType() ) );
|
||
|
|
||
|
gameLocal.mpGame.tourneyGUI.SetupTourneyHistory( startingRound, round - 1, tourneyHistory );
|
||
|
}
|
||
|
|
||
|
void rvTourneyGameState::UpdateTourneyHistory( int round ) {
|
||
|
assert( IsType( rvTourneyGameState::GetClassType() ) );
|
||
|
|
||
|
if ( round >= MAX_ROUNDS ) {
|
||
|
assert( false );
|
||
|
gameLocal.Error( "rvTourneyGameState::UpdateTourneyHistory: MAX_ROUNDS exceeded" );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
for( int i = 0; i < MAX_ARENAS; i++ ) {
|
||
|
// sometimes tourney history might have been updated for the current
|
||
|
// round, so don't clobber any data
|
||
|
|
||
|
if( (tourneyHistory[ round ][ i ].playerOne[ 0 ] != '\0' || tourneyHistory[ round ][ i ].playerOneNum != -1) ||
|
||
|
(tourneyHistory[ round ][ i ].playerTwo[ 0 ] != '\0' || tourneyHistory[ round ][ i ].playerTwoNum != -1) ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
tourneyHistory[ round ][ i ].playerOne[ 0 ] = '\0';
|
||
|
tourneyHistory[ round ][ i ].playerOneNum = -1;
|
||
|
tourneyHistory[ round ][ i ].playerOneScore = 0;
|
||
|
|
||
|
tourneyHistory[ round ][ i ].playerTwo[ 0 ] = '\0';
|
||
|
tourneyHistory[ round ][ i ].playerTwoNum = -1;
|
||
|
tourneyHistory[ round ][ i ].playerTwoScore = 0;
|
||
|
|
||
|
if( arenas[ i ].GetState() == AS_DONE ) {
|
||
|
idPlayer* playerOne = arenas[ i ].GetPlayers()[ 0 ];
|
||
|
idPlayer* playerTwo = arenas[ i ].GetPlayers()[ 1 ];
|
||
|
|
||
|
if( playerOne ) {
|
||
|
tourneyHistory[ round ][ i ].playerOneNum = playerOne->entityNumber;
|
||
|
tourneyHistory[ round ][ i ].playerOne[ MAX_TOURNEY_HISTORY_NAME_LEN - 1 ] = '\0';
|
||
|
tourneyHistory[ round ][ i ].playerOneScore = gameLocal.mpGame.GetTeamScore( playerOne );
|
||
|
}
|
||
|
|
||
|
if( playerTwo ) {
|
||
|
tourneyHistory[ round ][ i ].playerTwoNum = playerTwo->entityNumber;
|
||
|
tourneyHistory[ round ][ i ].playerTwo[ MAX_TOURNEY_HISTORY_NAME_LEN - 1 ] = '\0';
|
||
|
tourneyHistory[ round ][ i ].playerTwoScore = gameLocal.mpGame.GetTeamScore( playerTwo );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
rvTourneyGameState::FirstAvailableArena
|
||
|
Returns the first non-WARMUP arena available for use in the next round
|
||
|
================
|
||
|
*/
|
||
|
int rvTourneyGameState::FirstAvailableArena( void ) {
|
||
|
assert( IsType( rvTourneyGameState::GetClassType() ) );
|
||
|
|
||
|
for( int i = 0; i < MAX_ARENAS; i++ ) {
|
||
|
if( arenas[ i ].GetState() != AS_WARMUP ) {
|
||
|
return i;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// no available arenas
|
||
|
assert( 0 );
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
arenaOutcome_t* rvTourneyGameState::GetArenaOutcome( int arena ) {
|
||
|
assert( IsType( rvTourneyGameState::GetClassType() ) );
|
||
|
|
||
|
if( arenas[ arena ].GetState() != AS_DONE || (arenas[ arena ].GetPlayers()[ 0 ] && arenas[ arena ].GetPlayers()[ 1 ]) ) {
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
return &(tourneyHistory[ round - 1 ][ arena ]);
|
||
|
}
|
||
|
|
||
|
bool rvTourneyGameState::HasBye( idPlayer* player ) {
|
||
|
assert( IsType( rvTourneyGameState::GetClassType() ) );
|
||
|
|
||
|
if( player == NULL || player->GetArena() < 0 || player->GetArena() >= MAX_ARENAS ) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// if we're one of the players in the arena we're in, and the other player is NULL, we have a bye
|
||
|
if( (arenas[ player->GetArena() ].GetPlayers()[ 0 ] == player || arenas[ player->GetArena() ].GetPlayers()[ 1 ] == player) &&
|
||
|
(arenas[ player->GetArena() ].GetPlayers()[ 0 ] == NULL || arenas[ player->GetArena() ].GetPlayers()[ 1 ] == NULL) ) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// simple RTTI
|
||
|
gameStateType_t rvGameState::type = GS_BASE;
|
||
|
gameStateType_t rvDMGameState::type = GS_DM;
|
||
|
gameStateType_t rvTeamDMGameState::type = GS_TEAMDM;
|
||
|
gameStateType_t rvCTFGameState::type = GS_CTF;
|
||
|
gameStateType_t rvTourneyGameState::type = GS_TOURNEY;
|
||
|
|
||
|
bool rvGameState::IsType( gameStateType_t type ) const {
|
||
|
return ( type == rvGameState::type );
|
||
|
}
|
||
|
|
||
|
bool rvDMGameState::IsType( gameStateType_t type ) const {
|
||
|
return ( type == rvDMGameState::type );
|
||
|
}
|
||
|
|
||
|
bool rvTeamDMGameState::IsType( gameStateType_t type ) const {
|
||
|
return ( type == rvTeamDMGameState::type );
|
||
|
}
|
||
|
|
||
|
bool rvCTFGameState::IsType( gameStateType_t type ) const {
|
||
|
return ( type == rvCTFGameState::type );
|
||
|
}
|
||
|
|
||
|
bool rvTourneyGameState::IsType( gameStateType_t type ) const {
|
||
|
return ( type == rvTourneyGameState::type );
|
||
|
}
|
||
|
|
||
|
gameStateType_t rvGameState::GetClassType( void ) {
|
||
|
return rvGameState::type;
|
||
|
}
|
||
|
|
||
|
gameStateType_t rvDMGameState::GetClassType( void ) {
|
||
|
return rvDMGameState::type;
|
||
|
}
|
||
|
|
||
|
gameStateType_t rvTeamDMGameState::GetClassType( void ) {
|
||
|
return rvTeamDMGameState::type;
|
||
|
}
|
||
|
|
||
|
gameStateType_t rvCTFGameState::GetClassType( void ) {
|
||
|
return rvCTFGameState::type;
|
||
|
}
|
||
|
|
||
|
gameStateType_t rvTourneyGameState::GetClassType( void ) {
|
||
|
return rvTourneyGameState::type;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===============================================================================
|
||
|
|
||
|
riDZGameState
|
||
|
|
||
|
Game state info for Dead Zone
|
||
|
|
||
|
===============================================================================
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
riDZGameState::riDZGameState
|
||
|
================
|
||
|
*/
|
||
|
riDZGameState::riDZGameState( bool allocPrevious ) : rvGameState( false ) {
|
||
|
Clear();
|
||
|
|
||
|
if( allocPrevious ) {
|
||
|
previousGameState = new riDZGameState( false );
|
||
|
} else {
|
||
|
previousGameState = NULL;
|
||
|
}
|
||
|
|
||
|
trackPrevious = allocPrevious;
|
||
|
|
||
|
type = GS_DZ;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
riDZGameState::~riDZGameState
|
||
|
================
|
||
|
*/
|
||
|
riDZGameState::~riDZGameState( void ) {
|
||
|
Clear();
|
||
|
delete previousGameState;
|
||
|
previousGameState = NULL;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
riDZGameState::Clear
|
||
|
================
|
||
|
*/
|
||
|
void riDZGameState::Clear( void ) {
|
||
|
rvGameState::Clear();
|
||
|
|
||
|
if ( previousGameState ) {
|
||
|
riDZGameState* prevState = (riDZGameState*)previousGameState;
|
||
|
prevState->Clear( );
|
||
|
}
|
||
|
|
||
|
for( int i = 0; i < TEAM_MAX; i++ ) {
|
||
|
dzStatus[ i ].state = DZ_NONE;
|
||
|
dzStatus[ i ].clientNum = -1;
|
||
|
}
|
||
|
|
||
|
dzTriggerEnt = -1;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
riDZGameState::SendState
|
||
|
================
|
||
|
*/
|
||
|
void riDZGameState::SendState( const idMessageSender &sender, int clientNum ) {
|
||
|
idBitMsg outMsg;
|
||
|
byte msgBuf[MAX_GAME_MESSAGE_SIZE];
|
||
|
|
||
|
assert( (gameLocal.isServer || gameLocal.isRepeater) && trackPrevious && type == GS_DZ );
|
||
|
|
||
|
if( clientNum == -1 && (riDZGameState&)(*this) == (riDZGameState&)(*previousGameState) ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
outMsg.Init( msgBuf, sizeof( msgBuf ) );
|
||
|
outMsg.WriteByte( GAME_RELIABLE_MESSAGE_GAMESTATE );
|
||
|
|
||
|
WriteState( outMsg );
|
||
|
|
||
|
sender.Send( outMsg );
|
||
|
|
||
|
// don't update the state if we are working for a single client
|
||
|
if ( clientNum == -1 ) {
|
||
|
outMsg.ReadByte(); // pop off the msg ID
|
||
|
ReceiveState( outMsg );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
===============
|
||
|
riDZGameState::WriteState
|
||
|
===============
|
||
|
*/
|
||
|
void riDZGameState::WriteState( idBitMsg &msg ) {
|
||
|
// send off base info
|
||
|
rvGameState::PackState( msg );
|
||
|
// add DZ info
|
||
|
PackState( msg );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
riDZGameState::ReceiveState
|
||
|
================
|
||
|
*/
|
||
|
void riDZGameState::ReceiveState( const idBitMsg& msg ) {
|
||
|
assert( type == GS_DZ );
|
||
|
|
||
|
if ( !rvGameState::BaseUnpackState( msg ) ) {
|
||
|
return;
|
||
|
}
|
||
|
UnpackState( msg );
|
||
|
|
||
|
if ( gameLocal.localClientNum >= 0 ) {
|
||
|
GameStateChanged();
|
||
|
}
|
||
|
|
||
|
(riDZGameState&)(*previousGameState) = (riDZGameState&)(*this);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
riDZGameState::PackState
|
||
|
================
|
||
|
*/
|
||
|
void riDZGameState::PackState( idBitMsg& outMsg ) {
|
||
|
int i;
|
||
|
for( i = 0; i < TEAM_MAX; i++ ) {
|
||
|
outMsg.WriteByte( dzStatus[ i ].state );
|
||
|
outMsg.WriteBits( dzStatus[ i ].clientNum, -( idMath::BitsForInteger( MAX_CLIENTS ) + 1 ) );
|
||
|
}
|
||
|
|
||
|
outMsg.WriteLong(dzTriggerEnt);
|
||
|
outMsg.WriteLong(dzShaderParm);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
riDZGameState::UnpackState
|
||
|
================
|
||
|
*/
|
||
|
void riDZGameState::UnpackState( const idBitMsg& inMsg ) {
|
||
|
int i;
|
||
|
for( i = 0; i < TEAM_MAX; i++ ) {
|
||
|
dzStatus[ i ].state = (dzState_t)inMsg.ReadByte();
|
||
|
dzStatus[ i ].clientNum = inMsg.ReadBits( -( idMath::BitsForInteger( MAX_CLIENTS ) + 1 ) );
|
||
|
}
|
||
|
dzTriggerEnt = inMsg.ReadLong();
|
||
|
dzShaderParm = inMsg.ReadLong();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
riDZGameState::SendInitialState
|
||
|
================
|
||
|
*/
|
||
|
void riDZGameState::SendInitialState( const idMessageSender &sender, int clientNum ) {
|
||
|
assert( type == GS_DZ );
|
||
|
|
||
|
riDZGameState* previousState = (riDZGameState*)previousGameState;
|
||
|
|
||
|
riDZGameState invalidState;
|
||
|
|
||
|
previousGameState = &invalidState;
|
||
|
|
||
|
SendState( sender, clientNum );
|
||
|
|
||
|
previousGameState = previousState;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
riDZGameState::ControlZoneStateChanged
|
||
|
================
|
||
|
*/
|
||
|
void riDZGameState::ControlZoneStateChanged( int team ) {
|
||
|
if( !gameLocal.isClient && !gameLocal.isListenServer ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
idPlayer* player = gameLocal.GetLocalPlayer();
|
||
|
|
||
|
if( player == NULL ) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if ( dzTriggerEnt < 0 )
|
||
|
return;
|
||
|
|
||
|
idEntity* ent = gameLocal.FindEntity(dzTriggerEnt);
|
||
|
if ( ent ) {
|
||
|
ent->SetShaderParm(7, dzShaderParm);
|
||
|
}
|
||
|
|
||
|
dzTriggerEnt = -1;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
riDZGameState::GameStateChanged
|
||
|
================
|
||
|
*/
|
||
|
void riDZGameState::GameStateChanged( void ) {
|
||
|
// detect any base state changes
|
||
|
rvGameState::GameStateChanged();
|
||
|
|
||
|
// DZ specific stuff
|
||
|
idPlayer* player = gameLocal.GetLocalPlayer();
|
||
|
|
||
|
if( player == NULL ) {
|
||
|
//gameLocal.Error( "riDZGameState::GameStateChanged() - NULL local player\n" );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if( currentState == GAMEREVIEW )
|
||
|
{
|
||
|
// Need to clear the deadzone state at this point.
|
||
|
((riDZGameState*)previousGameState)->Clear();
|
||
|
for( int i = 0; i < TEAM_MAX; i++ ) {
|
||
|
dzStatus[ i ].state = DZ_NONE;
|
||
|
dzStatus[ i ].clientNum = -1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for( int i = 0; i < TEAM_MAX; i++ ) {
|
||
|
if( dzStatus[ i ] == ((riDZGameState*)previousGameState)->dzStatus[ i ] ) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
ControlZoneStateChanged(i);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
riDZGameState::Run
|
||
|
================
|
||
|
*/
|
||
|
void riDZGameState::Run( void ) {
|
||
|
// run common stuff
|
||
|
rvGameState::Run();
|
||
|
|
||
|
switch( currentState ) {
|
||
|
case GAMEON:
|
||
|
{
|
||
|
/// Check if we're frozen (in buy mode, etc.)
|
||
|
if( gameLocal.GetIsFrozen() )
|
||
|
{
|
||
|
/// Check if it's time for freeze to wear off
|
||
|
int unFreezeTime = gameLocal.GetUnFreezeTime();
|
||
|
if( gameLocal.time >= unFreezeTime )
|
||
|
{
|
||
|
gameLocal.SetIsFrozen( false );
|
||
|
// FIXME: say "fight"
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
/// Check if time limit has been reached
|
||
|
if ( gameLocal.mpGame.TimeLimitHit() )
|
||
|
{
|
||
|
int marineTeamScore = gameLocal.mpGame.GetScoreForTeam( TEAM_MARINE );
|
||
|
int stroggTeamScore = gameLocal.mpGame.GetScoreForTeam( TEAM_STROGG );
|
||
|
|
||
|
gameLocal.mpGame.PrintMessageEvent( -1, MSG_TIMELIMIT );
|
||
|
if( marineTeamScore > stroggTeamScore )
|
||
|
{
|
||
|
/// Marines win!
|
||
|
gameLocal.mpGame.OnDeadZoneTeamVictory( TEAM_MARINE );
|
||
|
}
|
||
|
else if( marineTeamScore < stroggTeamScore )
|
||
|
{
|
||
|
/// Strogg win!
|
||
|
gameLocal.mpGame.OnDeadZoneTeamVictory( TEAM_STROGG );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
/// Teams are tied and time limit was hit - go to sudden death!
|
||
|
fragLimitTimeout = 0;
|
||
|
NewState( SUDDENDEATH );
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case SUDDENDEATH:
|
||
|
{
|
||
|
int marineTeamScore = gameLocal.mpGame.GetScoreForTeam( TEAM_MARINE );
|
||
|
int stroggTeamScore = gameLocal.mpGame.GetScoreForTeam( TEAM_STROGG );
|
||
|
|
||
|
if( marineTeamScore > stroggTeamScore )
|
||
|
{
|
||
|
/// Marines win!
|
||
|
gameLocal.mpGame.OnDeadZoneTeamVictory( TEAM_MARINE );
|
||
|
}
|
||
|
else if( marineTeamScore < stroggTeamScore )
|
||
|
{
|
||
|
/// Strogg win!
|
||
|
gameLocal.mpGame.OnDeadZoneTeamVictory( TEAM_STROGG );
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
riDZGameState::SetDZState
|
||
|
================
|
||
|
*/
|
||
|
void riDZGameState::SetDZState( int dz, dzState_t newState ) {
|
||
|
assert( gameLocal.isServer && ( dz >= 0 && dz < TEAM_MAX ) && type == GS_DZ );
|
||
|
|
||
|
dzStatus[ dz ].state = newState;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
riDZGameState::operator==
|
||
|
================
|
||
|
*/
|
||
|
bool riDZGameState::operator==( const riDZGameState& rhs ) const {
|
||
|
if( (rvGameState&)(*this) != (rvGameState&)rhs ) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
for( int i = 0; i < TEAM_MAX; i++ ) {
|
||
|
if( dzStatus[ i ] != rhs.dzStatus[ i ] ) {
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
================
|
||
|
riDZGameState::operator=
|
||
|
================
|
||
|
*/
|
||
|
riDZGameState& riDZGameState::operator=( const riDZGameState& rhs ) {
|
||
|
(rvGameState&)(*this) = (rvGameState&)rhs;
|
||
|
|
||
|
for( int i = 0; i < TEAM_MAX; i++ ) {
|
||
|
dzStatus[ i ] = rhs.dzStatus[ i ];
|
||
|
}
|
||
|
|
||
|
return (*this);
|
||
|
}
|
||
|
|