dhewm3-sdk/d3xp/MultiplayerGame.cpp
dhewg afebd7e1e5 Untangle the epic precompiled.h mess
Don't include the lazy precompiled.h everywhere, only what's
required for the compilation unit.
platform.h needs to be included instead to provide all essential
defines and types.
All includes use the relative path to the neo or the game
specific root.
Move all idlib related includes from idlib/Lib.h to precompiled.h.
precompiled.h still exists for the MFC stuff in tools/.
Add some missing header guards.
2018-08-20 01:46:28 +02:00

4390 lines
123 KiB
C++

/*
===========================================================================
Doom 3 GPL Source Code
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code").
Doom 3 Source Code is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Doom 3 Source Code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
===========================================================================
*/
#include "sys/platform.h"
#include "idlib/BitMsg.h"
#include "idlib/Str.h"
#include "idlib/LangDict.h"
#include "framework/async/NetworkSystem.h"
#include "framework/FileSystem.h"
#include "framework/DeclEntityDef.h"
#include "ui/UserInterface.h"
#include "gamesys/SysCvar.h"
#include "Player.h"
#include "Game_local.h"
#include "MultiplayerGame.h"
// could be a problem if players manage to go down sudden deaths till this .. oh well
#define LASTMAN_NOLIVES -20
idCVar g_spectatorChat( "g_spectatorChat", "0", CVAR_GAME | CVAR_ARCHIVE | CVAR_BOOL, "let spectators talk to everyone during game" );
// global sounds transmitted by index - 0 .. SND_COUNT
// sounds in this list get precached on MP start
const char *idMultiplayerGame::GlobalSoundStrings[] = {
"sound/feedback/voc_youwin.wav",
"sound/feedback/voc_youlose.wav",
"sound/feedback/fight.wav",
"sound/feedback/vote_now.wav",
"sound/feedback/vote_passed.wav",
"sound/feedback/vote_failed.wav",
"sound/feedback/three.wav",
"sound/feedback/two.wav",
"sound/feedback/one.wav",
"sound/feedback/sudden_death.wav",
#ifdef CTF
"sound/ctf/flag_capped_yours.wav",
"sound/ctf/flag_capped_theirs.wav",
"sound/ctf/flag_return.wav",
"sound/ctf/flag_taken_yours.wav",
"sound/ctf/flag_taken_theirs.wav",
"sound/ctf/flag_dropped_yours.wav",
"sound/ctf/flag_dropped_theirs.wav"
#endif
};
// handy verbose
const char *idMultiplayerGame::GameStateStrings[] = {
"INACTIVE",
"WARMUP",
"COUNTDOWN",
"GAMEON",
"SUDDENDEATH",
"GAMEREVIEW",
"NEXTGAME"
};
const char *idMultiplayerGame::MPGuis[] = {
"guis/mphud.gui",
"guis/mpmain.gui",
"guis/mpmsgmode.gui",
"guis/netmenu.gui",
NULL
};
const char *idMultiplayerGame::ThrottleVars[] = {
"ui_spectate",
"ui_ready",
"ui_team",
NULL
};
const char *idMultiplayerGame::ThrottleVarsInEnglish[] = {
"#str_06738",
"#str_06737",
"#str_01991",
NULL
};
const int idMultiplayerGame::ThrottleDelay[] = {
8,
5,
5
};
/*
================
idMultiplayerGame::idMultiplayerGame
================
*/
idMultiplayerGame::idMultiplayerGame() {
scoreBoard = NULL;
spectateGui = NULL;
guiChat = NULL;
mainGui = NULL;
mapList = NULL;
msgmodeGui = NULL;
lastGameType = GAME_SP;
#ifdef CTF
teamFlags[0] = NULL;
teamFlags[1] = NULL;
teamPoints[0] = 0;
teamPoints[1] = 0;
flagMsgOn = true;
player_blue_flag = -1;
player_red_flag = -1;
#endif
Clear();
}
/*
================
idMultiplayerGame::Shutdown
================
*/
void idMultiplayerGame::Shutdown( void ) {
Clear();
}
/*
================
idMultiplayerGame::SetMenuSkin
================
*/
void idMultiplayerGame::SetMenuSkin( void ) {
// skins
idStr str = cvarSystem->GetCVarString( "mod_validSkins" );
idStr uiSkin = cvarSystem->GetCVarString( "ui_skin" );
idStr skin;
int skinId = 1;
int count = 1;
while ( str.Length() ) {
int n = str.Find( ";" );
if ( n >= 0 ) {
skin = str.Left( n );
str = str.Right( str.Length() - n - 1 );
} else {
skin = str;
str = "";
}
if ( skin.Icmp( uiSkin ) == 0 ) {
skinId = count;
}
count++;
}
for ( int i = 0; i < count; i++ ) {
mainGui->SetStateInt( va( "skin%i", i+1 ), 0 );
}
mainGui->SetStateInt( va( "skin%i", skinId ), 1 );
}
/*
================
idMultiplayerGame::Reset
================
*/
void idMultiplayerGame::Reset() {
Clear();
assert( !scoreBoard && !spectateGui && !guiChat && !mainGui && !mapList );
#ifdef CTF
// CTF uses its own scoreboard
if ( IsGametypeFlagBased() )
scoreBoard = uiManager->FindGui( "guis/ctfscoreboard.gui", true, false, true );
else
#endif
scoreBoard = uiManager->FindGui( "guis/scoreboard.gui", true, false, true );
spectateGui = uiManager->FindGui( "guis/spectate.gui", true, false, true );
guiChat = uiManager->FindGui( "guis/chat.gui", true, false, true );
mainGui = uiManager->FindGui( "guis/mpmain.gui", true, false, true );
mapList = uiManager->AllocListGUI( );
mapList->Config( mainGui, "mapList" );
// set this GUI so that our Draw function is still called when it becomes the active/fullscreen GUI
mainGui->SetStateBool( "gameDraw", true );
mainGui->SetKeyBindingNames();
mainGui->SetStateInt( "com_machineSpec", cvarSystem->GetCVarInteger( "com_machineSpec" ) );
SetMenuSkin();
msgmodeGui = uiManager->FindGui( "guis/mpmsgmode.gui", true, false, true );
msgmodeGui->SetStateBool( "gameDraw", true );
ClearGuis();
ClearChatData();
warmupEndTime = 0;
}
/*
================
idMultiplayerGame::ServerClientConnect
================
*/
void idMultiplayerGame::ServerClientConnect( int clientNum ) {
memset( &playerState[ clientNum ], 0, sizeof( playerState[ clientNum ] ) );
}
/*
================
idMultiplayerGame::SpawnPlayer
================
*/
void idMultiplayerGame::SpawnPlayer( int clientNum ) {
bool ingame = playerState[ clientNum ].ingame;
memset( &playerState[ clientNum ], 0, sizeof( playerState[ clientNum ] ) );
if ( !gameLocal.isClient ) {
idPlayer *p = static_cast< idPlayer * >( gameLocal.entities[ clientNum ] );
p->spawnedTime = gameLocal.time;
if ( IsGametypeTeamBased() ) { /* CTF */
SwitchToTeam( clientNum, -1, p->team );
}
p->tourneyRank = 0;
if ( gameLocal.gameType == GAME_TOURNEY && gameState == GAMEON ) {
p->tourneyRank++;
}
playerState[ clientNum ].ingame = ingame;
}
}
/*
================
idMultiplayerGame::Clear
================
*/
void idMultiplayerGame::Clear() {
int i;
gameState = INACTIVE;
nextState = INACTIVE;
pingUpdateTime = 0;
vote = VOTE_NONE;
voteTimeOut = 0;
voteExecTime = 0;
nextStateSwitch = 0;
matchStartedTime = 0;
currentTourneyPlayer[ 0 ] = -1;
currentTourneyPlayer[ 1 ] = -1;
one = two = three = false;
memset( &playerState, 0 , sizeof( playerState ) );
lastWinner = -1;
currentMenu = 0;
bCurrentMenuMsg = false;
nextMenu = 0;
pureReady = false;
scoreBoard = NULL;
spectateGui = NULL;
guiChat = NULL;
mainGui = NULL;
msgmodeGui = NULL;
if ( mapList ) {
uiManager->FreeListGUI( mapList );
mapList = NULL;
}
fragLimitTimeout = 0;
memset( &switchThrottle, 0, sizeof( switchThrottle ) );
voiceChatThrottle = 0;
for ( i = 0; i < NUM_CHAT_NOTIFY; i++ ) {
chatHistory[ i ].line.Clear();
}
warmupText.Clear();
voteValue.Clear();
voteString.Clear();
startFragLimit = -1;
}
/*
================
idMultiplayerGame::ClearGuis
================
*/
void idMultiplayerGame::ClearGuis() {
int i;
for ( i = 0; i < MAX_CLIENTS; i++ ) {
scoreBoard->SetStateString( va( "player%i",i+1 ), "" );
scoreBoard->SetStateString( va( "player%i_score", i+1 ), "" );
scoreBoard->SetStateString( va( "player%i_tdm_tscore", i+1 ), "" );
scoreBoard->SetStateString( va( "player%i_tdm_score", i+1 ), "" );
scoreBoard->SetStateString( va( "player%i_wins", i+1 ), "" );
scoreBoard->SetStateString( va( "player%i_status", i+1 ), "" );
scoreBoard->SetStateInt( va( "rank%i", i+1 ), 0 );
scoreBoard->SetStateInt( "rank_self", 0 );
idPlayer *player = static_cast<idPlayer *>( gameLocal.entities[ i ] );
if ( !player || !player->hud ) {
continue;
}
player->hud->SetStateString( va( "player%i",i+1 ), "" );
player->hud->SetStateString( va( "player%i_score", i+1 ), "" );
player->hud->SetStateString( va( "player%i_ready", i+1 ), "" );
scoreBoard->SetStateInt( va( "rank%i", i+1 ), 0 );
player->hud->SetStateInt( "rank_self", 0 );
}
#ifdef CTF
ClearHUDStatus();
#endif
}
#ifdef CTF
/*
================
idMultiplayerGame::ClearHUDStatus
================
*/
void idMultiplayerGame::ClearHUDStatus( void ) {
int i;
for ( i = 0; i < MAX_CLIENTS; i++ ) {
idPlayer *player = static_cast<idPlayer *>( gameLocal.entities[ i ] );
if ( !player || !player->hud ) {
continue;
}
player->hud->SetStateInt( "red_flagstatus", 0 );
player->hud->SetStateInt( "blue_flagstatus", 0 );
if ( IsGametypeFlagBased())
player->hud->SetStateInt( "self_team", player->team );
else
player->hud->SetStateInt( "self_team", -1 ); // Invisible.
}
}
/*
================
idMultiplayerGame::GetFlagPoints
Gets number of captures in CTF game.
0 = red team
1 = blue team
================
*/
int idMultiplayerGame::GetFlagPoints( int team )
{
assert( team <= 1 );
return teamPoints[ team ];
}
#endif
/*
================
idMultiplayerGame::UpdatePlayerRanks
================
*/
void idMultiplayerGame::UpdatePlayerRanks() {
int i, j, k;
idPlayer *players[MAX_CLIENTS];
idEntity *ent;
idPlayer *player;
memset( players, 0, sizeof( players ) );
numRankedPlayers = 0;
for ( i = 0; i < gameLocal.numClients; i++ ) {
ent = gameLocal.entities[ i ];
if ( !ent || !ent->IsType( idPlayer::Type ) ) {
continue;
}
player = static_cast< idPlayer * >( ent );
if ( !CanPlay( player ) ) {
continue;
}
if ( gameLocal.gameType == GAME_TOURNEY ) {
if ( i != currentTourneyPlayer[ 0 ] && i != currentTourneyPlayer[ 1 ] ) {
continue;
}
}
if ( gameLocal.gameType == GAME_LASTMAN && playerState[ i ].fragCount == LASTMAN_NOLIVES ) {
continue;
}
for ( j = 0; j < numRankedPlayers; j++ ) {
bool insert = false;
if ( IsGametypeTeamBased() ) { /* CTF */
if ( player->team != players[ j ]->team ) {
if ( playerState[ i ].teamFragCount > playerState[ players[ j ]->entityNumber ].teamFragCount ) {
// team scores
insert = true;
} else if ( playerState[ i ].teamFragCount == playerState[ players[ j ]->entityNumber ].teamFragCount && player->team < players[ j ]->team ) {
// at equal scores, sort by team number
insert = true;
}
} else if ( playerState[ i ].fragCount > playerState[ players[ j ]->entityNumber ].fragCount ) {
// in the same team, sort by frag count
insert = true;
}
} else {
insert = ( playerState[ i ].fragCount > playerState[ players[ j ]->entityNumber ].fragCount );
}
if ( insert ) {
for ( k = numRankedPlayers; k > j; k-- ) {
players[ k ] = players[ k-1 ];
}
players[ j ] = player;
break;
}
}
if ( j == numRankedPlayers ) {
players[ numRankedPlayers ] = player;
}
numRankedPlayers++;
}
memcpy( rankedPlayers, players, sizeof( players ) );
}
/*
================
idMultiplayerGame::UpdateRankColor
================
*/
void idMultiplayerGame::UpdateRankColor( idUserInterface *gui, const char *mask, int i, const idVec3 &vec ) {
for ( int j = 1; j < 4; j++ ) {
gui->SetStateFloat( va( mask, i, j ), vec[ j - 1 ] );
}
}
/*
================
idMultiplayerGame::UpdateScoreboard
================
*/
void idMultiplayerGame::UpdateScoreboard( idUserInterface *scoreBoard, idPlayer *player ) {
int i, j, iline, k;
idStr gameinfo;
#ifdef _D3XP
idStr livesinfo;
idStr timeinfo;
#endif
idEntity *ent;
idPlayer *p;
int value;
scoreBoard->SetStateString( "scoretext", gameLocal.gameType == GAME_LASTMAN ? common->GetLanguageDict()->GetString( "#str_04242" ) : common->GetLanguageDict()->GetString( "#str_04243" ) );
iline = 0; // the display lines
if ( gameState != WARMUP ) {
for ( i = 0; i < numRankedPlayers; i++ ) {
// ranked player
iline++;
scoreBoard->SetStateString( va( "player%i", iline ), rankedPlayers[ i ]->GetUserInfo()->GetString( "ui_name" ) );
if ( IsGametypeTeamBased() ) { /* CTF */
value = idMath::ClampInt( MP_PLAYER_MINFRAGS, MP_PLAYER_MAXFRAGS, playerState[ rankedPlayers[ i ]->entityNumber ].fragCount );
scoreBoard->SetStateInt( va( "player%i_tdm_score", iline ), value );
value = idMath::ClampInt( MP_PLAYER_MINFRAGS, MP_PLAYER_MAXFRAGS, playerState[ rankedPlayers[ i ]->entityNumber ].teamFragCount );
scoreBoard->SetStateString( va( "player%i_tdm_tscore", iline ), va( "/ %i", value ) );
scoreBoard->SetStateString( va( "player%i_score", iline ), "" );
} else {
value = idMath::ClampInt( MP_PLAYER_MINFRAGS, MP_PLAYER_MAXFRAGS, playerState[ rankedPlayers[ i ]->entityNumber ].fragCount );
scoreBoard->SetStateInt( va( "player%i_score", iline ), value );
scoreBoard->SetStateString( va( "player%i_tdm_tscore", iline ), "" );
scoreBoard->SetStateString( va( "player%i_tdm_score", iline ), "" );
}
value = idMath::ClampInt( 0, MP_PLAYER_MAXWINS, playerState[ rankedPlayers[ i ]->entityNumber ].wins );
scoreBoard->SetStateInt( va( "player%i_wins", iline ), value );
scoreBoard->SetStateInt( va( "player%i_ping", iline ), playerState[ rankedPlayers[ i ]->entityNumber ].ping );
// set the color band
scoreBoard->SetStateInt( va( "rank%i", iline ), 1 );
UpdateRankColor( scoreBoard, "rank%i_color%i", iline, rankedPlayers[ i ]->colorBar );
if ( rankedPlayers[ i ] == player ) {
// highlight who we are
scoreBoard->SetStateInt( "rank_self", iline );
}
}
}
// if warmup, this draws everyone, otherwise it goes over spectators only
// when doing warmup we loop twice to draw ready/not ready first *then* spectators
// NOTE: in tourney, shows spectators according to their playing rank order?
for ( k = 0; k < ( gameState == WARMUP ? 2 : 1 ); k++ ) {
for ( i = 0; i < MAX_CLIENTS; i++ ) {
ent = gameLocal.entities[ i ];
if ( !ent || !ent->IsType( idPlayer::Type ) ) {
continue;
}
if ( gameState != WARMUP ) {
// check he's not covered by ranks already
for ( j = 0; j < numRankedPlayers; j++ ) {
if ( ent == rankedPlayers[ j ] ) {
break;
}
}
if ( j != numRankedPlayers ) {
continue;
}
}
p = static_cast< idPlayer * >( ent );
if ( gameState == WARMUP ) {
if ( k == 0 && p->spectating ) {
continue;
}
if ( k == 1 && !p->spectating ) {
continue;
}
}
iline++;
if ( !playerState[ i ].ingame ) {
scoreBoard->SetStateString( va( "player%i", iline ), common->GetLanguageDict()->GetString( "#str_04244" ) );
scoreBoard->SetStateString( va( "player%i_score", iline ), common->GetLanguageDict()->GetString( "#str_04245" ) );
// no color band
scoreBoard->SetStateInt( va( "rank%i", iline ), 0 );
} else {
scoreBoard->SetStateString( va( "player%i", iline ), gameLocal.userInfo[ i ].GetString( "ui_name" ) );
if ( gameState == WARMUP ) {
if ( p->spectating ) {
scoreBoard->SetStateString( va( "player%i_score", iline ), common->GetLanguageDict()->GetString( "#str_04246" ) );
// no color band
scoreBoard->SetStateInt( va( "rank%i", iline ), 0 );
} else {
scoreBoard->SetStateString( va( "player%i_score", iline ), p->IsReady() ? common->GetLanguageDict()->GetString( "#str_04247" ) : common->GetLanguageDict()->GetString( "#str_04248" ) );
// set the color band
scoreBoard->SetStateInt( va( "rank%i", iline ), 1 );
UpdateRankColor( scoreBoard, "rank%i_color%i", iline, p->colorBar );
}
} else {
if ( gameLocal.gameType == GAME_LASTMAN && playerState[ i ].fragCount == LASTMAN_NOLIVES ) {
scoreBoard->SetStateString( va( "player%i_score", iline ), common->GetLanguageDict()->GetString( "#str_06736" ) );
// set the color band
scoreBoard->SetStateInt( va( "rank%i", iline ), 1 );
UpdateRankColor( scoreBoard, "rank%i_color%i", iline, p->colorBar );
} else {
scoreBoard->SetStateString( va( "player%i_score", iline ), common->GetLanguageDict()->GetString( "#str_04246" ) );
// no color band
scoreBoard->SetStateInt( va( "rank%i", iline ), 0 );
}
}
}
scoreBoard->SetStateString( va( "player%i_tdm_tscore", iline ), "" );
scoreBoard->SetStateString( va( "player%i_tdm_score", iline ), "" );
scoreBoard->SetStateString( va( "player%i_wins", iline ), "" );
scoreBoard->SetStateInt( va( "player%i_ping", iline ), playerState[ i ].ping );
if ( i == player->entityNumber ) {
// highlight who we are
scoreBoard->SetStateInt( "rank_self", iline );
}
}
}
// clear remaining lines (empty slots)
iline++;
#ifdef _D3XP
while ( iline < MAX_CLIENTS ) { //Max players is now 8
#else
while ( iline < 5 ) {
#endif
scoreBoard->SetStateString( va( "player%i", iline ), "" );
scoreBoard->SetStateString( va( "player%i_score", iline ), "" );
scoreBoard->SetStateString( va( "player%i_tdm_tscore", iline ), "" );
scoreBoard->SetStateString( va( "player%i_tdm_score", iline ), "" );
scoreBoard->SetStateString( va( "player%i_wins", iline ), "" );
scoreBoard->SetStateString( va( "player%i_ping", iline ), "" );
scoreBoard->SetStateInt( va( "rank%i", iline ), 0 );
iline++;
}
gameinfo = va( "%s: %s", common->GetLanguageDict()->GetString( "#str_02376" ), gameLocal.serverInfo.GetString( "si_gameType" ) );
if ( gameLocal.gameType == GAME_LASTMAN ) {
if ( gameState == GAMEON || gameState == SUDDENDEATH ) {
livesinfo = va( "%s: %i", common->GetLanguageDict()->GetString( "#str_04264" ), startFragLimit );
} else {
livesinfo = va( "%s: %i", common->GetLanguageDict()->GetString( "#str_04264" ), gameLocal.serverInfo.GetInt( "si_fragLimit" ) );
}
#ifdef CTF
} else if ( gameLocal.gameType != GAME_CTF ) {
#else
} else {
#endif
livesinfo = va( "%s: %i", common->GetLanguageDict()->GetString( "#str_01982" ), gameLocal.serverInfo.GetInt( "si_fragLimit" ) );
}
if ( gameLocal.serverInfo.GetInt( "si_timeLimit" ) > 0 ) {
timeinfo = va( "%s: %i", common->GetLanguageDict()->GetString( "#str_01983" ), gameLocal.serverInfo.GetInt( "si_timeLimit" ) );
} else {
timeinfo = va("%s", common->GetLanguageDict()->GetString( "#str_07209" ));
}
scoreBoard->SetStateString( "gameinfo", gameinfo );
scoreBoard->SetStateString( "livesinfo", livesinfo );
scoreBoard->SetStateString( "timeinfo", timeinfo );
scoreBoard->Redraw( gameLocal.time );
}
#ifdef CTF
/*
================
idMultiplayerGame::UpdateCTFScoreboard
================
*/
void idMultiplayerGame::UpdateCTFScoreboard( idUserInterface *scoreBoard, idPlayer *player ) {
int i, j;
idStr gameinfo;
idEntity *ent;
int value;
// The display lines
int ilines[2] = {0,0};
// The team strings
char redTeam[] = "red";
char blueTeam[] = "blue";
char *curTeam = NULL;
/* Word "frags" */
scoreBoard->SetStateString( "scoretext", gameLocal.gameType == GAME_LASTMAN ? common->GetLanguageDict()->GetString( "#str_04242" ) : common->GetLanguageDict()->GetString( "#str_04243" ) );
// Blank the flag carrier on the scoreboard. We update these in the loop below if necessary.
if ( this->player_blue_flag == -1 )
scoreBoard->SetStateInt( "player_blue_flag", 0 );
if ( this->player_red_flag == -1 )
scoreBoard->SetStateInt( "player_red_flag", 0 );
if ( gameState != WARMUP ) {
for ( i = 0; i < numRankedPlayers; i++ ) {
idPlayer *player = rankedPlayers[ i ];
assert( player );
if ( player->team == 0 )
curTeam = redTeam;
else
curTeam = blueTeam;
// Increase the appropriate iline
assert( player->team <= 1 );
ilines[ player->team ]++;
// Update the flag status
if ( this->player_blue_flag == player->entityNumber )
scoreBoard->SetStateInt( "player_blue_flag", ilines[ player->team ] );
if ( player->team == 1 && this->player_red_flag == player->entityNumber )
scoreBoard->SetStateInt( "player_red_flag", ilines[ player->team ] );
/* Player Name */
scoreBoard->SetStateString( va( "player%i_%s", ilines[ player->team ], curTeam ), player->GetUserInfo()->GetString( "ui_name" ) );
if ( IsGametypeTeamBased() ) {
value = idMath::ClampInt( MP_PLAYER_MINFRAGS, MP_PLAYER_MAXFRAGS, playerState[ rankedPlayers[ i ]->entityNumber ].fragCount );
scoreBoard->SetStateInt( va( "player%i_%s_score", ilines[ player->team ], curTeam ), value );
/* Team score and score, blanked */
scoreBoard->SetStateString( va( "player%i_%s_tscore", ilines[ player->team ], curTeam ), "" );
//scoreBoard->SetStateString( va( "player%i_%s_score", ilines[ player->team ], curTeam ), "" );
}
/* Wins */
value = idMath::ClampInt( 0, MP_PLAYER_MAXWINS, playerState[ rankedPlayers[ i ]->entityNumber ].wins );
scoreBoard->SetStateInt( va( "player%i_%s_wins", ilines[ player->team ], curTeam ), value );
/* Ping */
scoreBoard->SetStateInt( va( "player%i_%s_ping", ilines[ player->team ], curTeam ), playerState[ rankedPlayers[ i ]->entityNumber ].ping );
}
}
for ( i = 0; i < MAX_CLIENTS; i++ ) {
ent = gameLocal.entities[ i ];
if ( !ent || !ent->IsType( idPlayer::Type ) ) {
continue;
}
if ( gameState != WARMUP ) {
// check he's not covered by ranks already
for ( j = 0; j < numRankedPlayers; j++ ) {
if ( ent == rankedPlayers[ j ] ) {
break;
}
}
if ( j != numRankedPlayers ) {
continue;
}
}
player = static_cast< idPlayer * >( ent );
if ( player->spectating )
continue;
if ( player->team == 0 )
curTeam = redTeam;
else
curTeam = blueTeam;
ilines[ player->team ]++;
if ( !playerState[ i ].ingame ) {
/* "New Player" on player's name location */
scoreBoard->SetStateString( va( "player%i_%s", ilines[ player->team ], curTeam ), common->GetLanguageDict()->GetString( "#str_04244" ) );
/* "Connecting" on player's score location */
scoreBoard->SetStateString( va( "player%i_%s_score", ilines[ player->team ], curTeam ), common->GetLanguageDict()->GetString( "#str_04245" ) );
} else {
/* Player's name in player's name location */
if ( !player->spectating )
scoreBoard->SetStateString( va( "player%i_%s", ilines[ player->team ], curTeam ), gameLocal.userInfo[ i ].GetString( "ui_name" ) );
if ( gameState == WARMUP ) {
if ( player->spectating ) {
/* "Spectating" on player's score location */
scoreBoard->SetStateString( va( "player%i_%s_score", ilines[ player->team ], curTeam ), common->GetLanguageDict()->GetString( "#str_04246" ) );
} else {
/* Display "ready" in player's score location if they're ready. Display nothing if not. No room for 'not ready'. */
scoreBoard->SetStateString( va( "player%i_%s_score", ilines[ player->team ], curTeam ), player->IsReady() ? common->GetLanguageDict()->GetString( "#str_04247" ) : "" );
}
}
}
}
// Clear remaining slots
for ( i = 0; i < 2; i++ )
{
if ( i )
curTeam = blueTeam;
else
curTeam = redTeam;
for ( j = ilines[ i ]+1; j <= 8; j++ )
{
scoreBoard->SetStateString( va( "player%i_%s", j, curTeam ), "" );
scoreBoard->SetStateString( va( "player%i_%s_score", j, curTeam ), "" );
scoreBoard->SetStateString( va( "player%i_%s_wins", j, curTeam ), "" );
scoreBoard->SetStateString( va( "player%i_%s_ping", j, curTeam ), "" );
scoreBoard->SetStateInt( "rank_self", 0 );
}
}
// Don't display "CTF" -- if this scoreboard comes up, it should be apparent.
if ( gameLocal.gameType == GAME_CTF ) {
int captureLimit = gameLocal.serverInfo.GetInt( "si_fragLimit" );
if ( captureLimit > MP_CTF_MAXPOINTS )
captureLimit = MP_CTF_MAXPOINTS;
int timeLimit = gameLocal.serverInfo.GetInt( "si_timeLimit" );
/* Prints "Capture Limit: %i" at the bottom of the scoreboard, left */
if ( captureLimit )
scoreBoard->SetStateString( "gameinfo_red", va( common->GetLanguageDict()->GetString( "#str_11108" ), captureLimit) );
else
scoreBoard->SetStateString( "gameinfo_red", "" );
/* Prints "Time Limit: %i" at the bottom of the scoreboard, right */
if ( timeLimit )
scoreBoard->SetStateString( "gameinfo_blue", va( common->GetLanguageDict()->GetString( "#str_11109" ), timeLimit) );
else
scoreBoard->SetStateString( "gameinfo_blue", "" );
}
// Set team scores
scoreBoard->SetStateInt( "red_team_score", GetFlagPoints( 0 ) );
scoreBoard->SetStateInt( "blue_team_score", GetFlagPoints( 1 ) );
// Handle flag status changed event
scoreBoard->HandleNamedEvent( "BlueFlagStatusChange" );
scoreBoard->HandleNamedEvent( "RedFlagStatusChange" );
scoreBoard->Redraw( gameLocal.time );
}
#endif
/*
================
idMultiplayerGame::GameTime
================
*/
const char *idMultiplayerGame::GameTime() {
static char buff[16];
int m, s, t, ms;
if ( gameState == COUNTDOWN ) {
ms = warmupEndTime - gameLocal.realClientTime;
s = ms / 1000 + 1;
if ( ms <= 0 ) {
strcpy( buff, "WMP --" );
} else {
sprintf( buff, "WMP %i", s );
}
} else {
int timeLimit = gameLocal.serverInfo.GetInt( "si_timeLimit" );
if ( timeLimit ) {
ms = ( timeLimit * 60000 ) - ( gameLocal.time - matchStartedTime );
} else {
ms = gameLocal.time - matchStartedTime;
}
if ( ms < 0 ) {
ms = 0;
}
s = ms / 1000;
m = s / 60;
s -= m * 60;
t = s / 10;
s -= t * 10;
sprintf( buff, "%i:%i%i", m, t, s );
}
return &buff[0];
}
/*
================
idMultiplayerGame::NumActualClients
================
*/
int idMultiplayerGame::NumActualClients( bool countSpectators, int *teamcounts ) {
idPlayer *p;
int c = 0;
if ( teamcounts ) {
teamcounts[ 0 ] = teamcounts[ 1 ] = 0;
}
for( int i = 0 ; i < gameLocal.numClients ; i++ ) {
idEntity *ent = gameLocal.entities[ i ];
if ( !ent || !ent->IsType( idPlayer::Type ) ) {
continue;
}
p = static_cast< idPlayer * >( ent );
if ( countSpectators || CanPlay( p ) ) {
c++;
}
if ( teamcounts && CanPlay( p ) ) {
teamcounts[ p->team ]++;
}
}
return c;
}
/*
================
idMultiplayerGame::EnoughClientsToPlay
================
*/
bool idMultiplayerGame::EnoughClientsToPlay() {
int team[ 2 ];
int clients = NumActualClients( false, &team[ 0 ] );
if ( IsGametypeTeamBased() ) { /* CTF */
return clients >= 2 && team[ 0 ] && team[ 1 ];
} else {
return clients >= 2;
}
}
/*
================
idMultiplayerGame::AllPlayersReady
================
*/
bool idMultiplayerGame::AllPlayersReady() {
int i;
idEntity *ent;
idPlayer *p;
int team[ 2 ];
if ( NumActualClients( false, &team[ 0 ] ) <= 1 ) {
return false;
}
if ( IsGametypeTeamBased() ) { /* CTF */
if ( !team[ 0 ] || !team[ 1 ] ) {
return false;
}
}
if ( !gameLocal.serverInfo.GetBool( "si_warmup" ) ) {
return true;
}
for( i = 0; i < gameLocal.numClients; i++ ) {
if ( gameLocal.gameType == GAME_TOURNEY && i != currentTourneyPlayer[ 0 ] && i != currentTourneyPlayer[ 1 ] ) {
continue;
}
ent = gameLocal.entities[ i ];
if ( !ent || !ent->IsType( idPlayer::Type ) ) {
continue;
}
p = static_cast< idPlayer * >( ent );
if ( CanPlay( p ) && !p->IsReady() ) {
return false;
}
team[ p->team ]++;
}
return true;
}
/*
================
idMultiplayerGame::FragLimitHit
return the winning player (team player)
if there is no FragLeader(), the game is tied and we return NULL
================
*/
idPlayer *idMultiplayerGame::FragLimitHit() {
int i;
int fragLimit = gameLocal.serverInfo.GetInt( "si_fragLimit" );
idPlayer *leader;
#ifdef CTF
if ( IsGametypeFlagBased() ) /* CTF */
return NULL;
#endif
leader = FragLeader();
if ( !leader ) {
return NULL;
}
if ( fragLimit <= 0 ) {
fragLimit = MP_PLAYER_MAXFRAGS;
}
if ( gameLocal.gameType == GAME_LASTMAN ) {
// we have a leader, check if any other players have frags left
assert( !static_cast< idPlayer * >( leader )->lastManOver );
for( i = 0 ; i < gameLocal.numClients ; i++ ) {
idEntity *ent = gameLocal.entities[ i ];
if ( !ent || !ent->IsType( idPlayer::Type ) ) {
continue;
}
if ( !CanPlay( static_cast< idPlayer * >( ent ) ) ) {
continue;
}
if ( ent == leader ) {
continue;
}
if ( playerState[ ent->entityNumber ].fragCount > 0 ) {
return NULL;
}
}
// there is a leader, his score may even be negative, but no one else has frags left or is !lastManOver
return leader;
} else if ( IsGametypeTeamBased() ) { /* CTF */
if ( playerState[ leader->entityNumber ].teamFragCount >= fragLimit ) {
return leader;
}
} else {
if ( playerState[ leader->entityNumber ].fragCount >= fragLimit ) {
return leader;
}
}
return NULL;
}
/*
================
idMultiplayerGame::TimeLimitHit
================
*/
bool idMultiplayerGame::TimeLimitHit() {
int timeLimit = gameLocal.serverInfo.GetInt( "si_timeLimit" );
if ( timeLimit ) {
if ( gameLocal.time >= matchStartedTime + timeLimit * 60000 ) {
return true;
}
}
return false;
}
#ifdef CTF
/*
================
idMultiplayerGame::WinningTeam
return winning team
-1 if tied or no players
================
*/
int idMultiplayerGame::WinningTeam( void ) {
if ( teamPoints[0] > teamPoints[1] )
return 0;
if ( teamPoints[0] < teamPoints[1] )
return 1;
return -1;
}
/*
================
idMultiplayerGame::PointLimitHit
================
*/
bool idMultiplayerGame::PointLimitHit( void ) {
int pointLimit = gameLocal.serverInfo.GetInt( "si_fragLimit" );
// default to MP_CTF_MAXPOINTS if needed
if ( pointLimit > MP_CTF_MAXPOINTS )
pointLimit = MP_CTF_MAXPOINTS;
else if ( pointLimit <= 0 )
pointLimit = MP_CTF_MAXPOINTS;
if ( teamPoints[0] == teamPoints[1] )
return false;
if ( teamPoints[0] >= pointLimit ||
teamPoints[1] >= pointLimit )
return true;
return false;
}
#endif
/*
================
idMultiplayerGame::FragLeader
return the current winner ( or a player from the winning team )
NULL if even
================
*/
idPlayer *idMultiplayerGame::FragLeader( void ) {
int i;
int frags[ MAX_CLIENTS ];
idPlayer *leader = NULL;
idEntity *ent;
idPlayer *p;
int high = -9999;
int count = 0;
bool teamLead[ 2 ] = { false, false };
for ( i = 0 ; i < gameLocal.numClients ; i++ ) {
ent = gameLocal.entities[ i ];
if ( !ent || !ent->IsType( idPlayer::Type ) ) {
continue;
}
if ( !CanPlay( static_cast< idPlayer * >( ent ) ) ) {
continue;
}
if ( gameLocal.gameType == GAME_TOURNEY && ent->entityNumber != currentTourneyPlayer[ 0 ] && ent->entityNumber != currentTourneyPlayer[ 1 ] ) {
continue;
}
if ( static_cast< idPlayer * >( ent )->lastManOver ) {
continue;
}
int fragc = ( IsGametypeTeamBased() ) ? playerState[i].teamFragCount : playerState[i].fragCount; /* CTF */
if ( fragc > high ) {
high = fragc;
}
frags[ i ] = fragc;
}
for ( i = 0; i < gameLocal.numClients; i++ ) {
ent = gameLocal.entities[ i ];
if ( !ent || !ent->IsType( idPlayer::Type ) ) {
continue;
}
p = static_cast< idPlayer * >( ent );
p->SetLeader( false );
if ( !CanPlay( p ) ) {
continue;
}
if ( gameLocal.gameType == GAME_TOURNEY && ent->entityNumber != currentTourneyPlayer[ 0 ] && ent->entityNumber != currentTourneyPlayer[ 1 ] ) {
continue;
}
if ( p->lastManOver ) {
continue;
}
if ( p->spectating ) {
continue;
}
if ( frags[ i ] >= high ) {
leader = p;
count++;
p->SetLeader( true );
if ( IsGametypeTeamBased() ) { /* CTF */
teamLead[ p->team ] = true;
}
}
}
if ( !IsGametypeTeamBased() ) { /* CTF */
// more than one player at the highest frags
if ( count > 1 ) {
return NULL;
} else {
return leader;
}
} else {
if ( teamLead[ 0 ] && teamLead[ 1 ] ) {
// even game in team play
return NULL;
}
return leader;
}
}
/*
================
idGameLocal::UpdateWinsLosses
================
*/
void idMultiplayerGame::UpdateWinsLosses( idPlayer *winner ) {
if ( winner ) {
// run back through and update win/loss count
for( int i = 0; i < gameLocal.numClients; i++ ) {
idEntity *ent = gameLocal.entities[ i ];
if ( !ent || !ent->IsType( idPlayer::Type ) ) {
continue;
}
idPlayer *player = static_cast<idPlayer *>(ent);
if ( IsGametypeTeamBased() ) { /* CTF */
if ( player == winner || ( player != winner && player->team == winner->team ) ) {
playerState[ i ].wins++;
PlayGlobalSound( player->entityNumber, SND_YOUWIN );
} else {
PlayGlobalSound( player->entityNumber, SND_YOULOSE );
}
} else if ( gameLocal.gameType == GAME_LASTMAN ) {
if ( player == winner ) {
playerState[ i ].wins++;
PlayGlobalSound( player->entityNumber, SND_YOUWIN );
} else if ( !player->wantSpectate ) {
PlayGlobalSound( player->entityNumber, SND_YOULOSE );
}
} else if ( gameLocal.gameType == GAME_TOURNEY ) {
if ( player == winner ) {
playerState[ i ].wins++;
PlayGlobalSound( player->entityNumber, SND_YOUWIN );
} else if ( i == currentTourneyPlayer[ 0 ] || i == currentTourneyPlayer[ 1 ] ) {
PlayGlobalSound( player->entityNumber, SND_YOULOSE );
}
} else {
if ( player == winner ) {
playerState[i].wins++;
PlayGlobalSound( player->entityNumber, SND_YOUWIN );
} else if ( !player->wantSpectate ) {
PlayGlobalSound( player->entityNumber, SND_YOULOSE );
}
}
}
}
#ifdef CTF
else if ( IsGametypeFlagBased() ) { /* CTF */
int winteam = WinningTeam();
if ( winteam != -1 ) // TODO : print a message telling it why the hell the game ended with no winning team?
for( int i = 0; i < gameLocal.numClients; i++ ) {
idEntity *ent = gameLocal.entities[ i ];
if ( !ent || !ent->IsType( idPlayer::Type ) ) {
continue;
}
idPlayer *player = static_cast<idPlayer *>(ent);
if ( player->team == winteam ) {
PlayGlobalSound( player->entityNumber, SND_YOUWIN );
} else {
PlayGlobalSound( player->entityNumber, SND_YOULOSE );
}
}
}
#endif
if ( winner ) {
lastWinner = winner->entityNumber;
} else {
lastWinner = -1;
}
}
#ifdef CTF
/*
================
idMultiplayerGame::TeamScoreCTF
================
*/
void idMultiplayerGame::TeamScoreCTF( int team, int delta ) {
if ( team < 0 || team > 1 )
return;
teamPoints[team] += delta;
if ( gameState == GAMEON || gameState == SUDDENDEATH )
PrintMessageEvent( -1, MSG_SCOREUPDATE, teamPoints[0], teamPoints[1] );
}
/*
================
idMultiplayerGame::PlayerScoreCTF
================
*/
void idMultiplayerGame::PlayerScoreCTF( int playerIdx, int delta ) {
if ( playerIdx < 0 || playerIdx >= MAX_CLIENTS )
return;
playerState[ playerIdx ].fragCount += delta;
}
/*
================
idMultiplayerGame::GetFlagCarrier
================
*/
int idMultiplayerGame::GetFlagCarrier( int team ) {
int iFlagCarrier = -1;
for ( int i = 0; i < gameLocal.numClients; i++ ) {
idEntity * ent = gameLocal.entities[ i ];
if ( !ent || !ent->IsType( idPlayer::Type ) ) {
continue;
}
idPlayer * player = static_cast<idPlayer *>( ent );
if ( player->team != team )
continue;
if ( player->carryingFlag ) {
if ( iFlagCarrier != -1 )
gameLocal.Warning( "BUG: more than one flag carrier on %s team", team == 0 ? "red" : "blue" );
iFlagCarrier = i;
}
}
return iFlagCarrier;
}
#endif
/*
================
idMultiplayerGame::TeamScore
================
*/
void idMultiplayerGame::TeamScore( int entityNumber, int team, int delta ) {
playerState[ entityNumber ].fragCount += delta;
for( int i = 0 ; i < gameLocal.numClients ; i++ ) {
idEntity *ent = gameLocal.entities[ i ];
if ( !ent || !ent->IsType( idPlayer::Type ) ) {
continue;
}
idPlayer *player = static_cast<idPlayer *>(ent);
if ( player->team == team ) {
playerState[ player->entityNumber ].teamFragCount += delta;
}
}
}
/*
================
idMultiplayerGame::PlayerDeath
================
*/
void idMultiplayerGame::PlayerDeath( idPlayer *dead, idPlayer *killer, bool telefrag ) {
// don't do PrintMessageEvent and shit
assert( !gameLocal.isClient );
if ( killer ) {
if ( gameLocal.gameType == GAME_LASTMAN ) {
playerState[ dead->entityNumber ].fragCount--;
} else if ( IsGametypeTeamBased() ) { /* CTF */
if ( killer == dead || killer->team == dead->team ) {
// suicide or teamkill
TeamScore( killer->entityNumber, killer->team, -1 );
} else {
TeamScore( killer->entityNumber, killer->team, +1 );
}
} else {
playerState[ killer->entityNumber ].fragCount += ( killer == dead ) ? -1 : 1;
}
}
if ( killer && killer == dead ) {
PrintMessageEvent( -1, MSG_SUICIDE, dead->entityNumber );
} else if ( killer ) {
if ( telefrag ) {
PrintMessageEvent( -1, MSG_TELEFRAGGED, dead->entityNumber, killer->entityNumber );
} else if ( IsGametypeTeamBased() && dead->team == killer->team ) { /* CTF */
PrintMessageEvent( -1, MSG_KILLEDTEAM, dead->entityNumber, killer->entityNumber );
} else {
PrintMessageEvent( -1, MSG_KILLED, dead->entityNumber, killer->entityNumber );
}
} else {
PrintMessageEvent( -1, MSG_DIED, dead->entityNumber );
playerState[ dead->entityNumber ].fragCount--;
}
}
/*
================
idMultiplayerGame::PlayerStats
================
*/
void idMultiplayerGame::PlayerStats( int clientNum, char *data, const int len ) {
idEntity *ent;
int team;
*data = 0;
// make sure we don't exceed the client list
if ( clientNum < 0 || clientNum > gameLocal.numClients ) {
return;
}
// find which team this player is on
ent = gameLocal.entities[ clientNum ];
if ( ent && ent->IsType( idPlayer::Type ) ) {
team = static_cast< idPlayer * >(ent)->team;
} else {
return;
}
idStr::snPrintf( data, len, "team=%d score=%d tks=%d", team, playerState[ clientNum ].fragCount, playerState[ clientNum ].teamFragCount );
return;
}
/*
================
idMultiplayerGame::PlayerVote
================
*/
void idMultiplayerGame::PlayerVote( int clientNum, playerVote_t vote ) {
playerState[ clientNum ].vote = vote;
}
/*
================
idMultiplayerGame::DumpTourneyLine
================
*/
void idMultiplayerGame::DumpTourneyLine( void ) {
int i;
for ( i = 0; i < gameLocal.numClients; i++ ) {
if ( gameLocal.entities[ i ] && gameLocal.entities[ i ]->IsType( idPlayer::Type ) ) {
common->Printf( "client %d: rank %d\n", i, static_cast< idPlayer * >( gameLocal.entities[ i ] )->tourneyRank );
}
}
}
/*
================
idMultiplayerGame::NewState
================
*/
void idMultiplayerGame::NewState( gameState_t news, idPlayer *player ) {
idBitMsg outMsg;
byte msgBuf[MAX_GAME_MESSAGE_SIZE];
int i;
assert( news != gameState );
assert( !gameLocal.isClient );
gameLocal.DPrintf( "%s -> %s\n", GameStateStrings[ gameState ], GameStateStrings[ news ] );
switch( news ) {
case GAMEON: {
gameLocal.LocalMapRestart();
outMsg.Init( msgBuf, sizeof( msgBuf ) );
outMsg.WriteByte( GAME_RELIABLE_MESSAGE_RESTART );
outMsg.WriteBits( 0, 1 );
networkSystem->ServerSendReliableMessage( -1, outMsg );
#ifdef CTF
teamPoints[0] = 0;
teamPoints[1] = 0;
ClearHUDStatus();
#endif
PlayGlobalSound( -1, SND_FIGHT );
matchStartedTime = gameLocal.time;
fragLimitTimeout = 0;
for( i = 0; i < gameLocal.numClients; i++ ) {
idEntity *ent = gameLocal.entities[ i ];
if ( !ent || !ent->IsType( idPlayer::Type ) ) {
continue;
}
idPlayer *p = static_cast<idPlayer *>( ent );
p->SetLeader( false ); // don't carry the flag from previous games
if ( gameLocal.gameType == GAME_TOURNEY && currentTourneyPlayer[ 0 ] != i && currentTourneyPlayer[ 1 ] != i ) {
p->ServerSpectate( true );
p->tourneyRank++;
} else {
int fragLimit = gameLocal.serverInfo.GetInt( "si_fragLimit" );
int startingCount = ( gameLocal.gameType == GAME_LASTMAN ) ? fragLimit : 0;
playerState[ i ].fragCount = startingCount;
playerState[ i ].teamFragCount = startingCount;
if ( !static_cast<idPlayer *>(ent)->wantSpectate ) {
static_cast<idPlayer *>(ent)->ServerSpectate( false );
if ( gameLocal.gameType == GAME_TOURNEY ) {
p->tourneyRank = 0;
}
}
}
if ( CanPlay( p ) ) {
p->lastManPresent = true;
} else {
p->lastManPresent = false;
}
}
cvarSystem->SetCVarString( "ui_ready", "Not Ready" );
switchThrottle[ 1 ] = 0; // passby the throttle
startFragLimit = gameLocal.serverInfo.GetInt( "si_fragLimit" );
break;
}
case GAMEREVIEW: {
#ifdef CTF
SetFlagMsg( false );
#endif
nextState = INACTIVE; // used to abort a game. cancel out any upcoming state change
// set all players not ready and spectating
for( i = 0; i < gameLocal.numClients; i++ ) {
idEntity *ent = gameLocal.entities[ i ];
if ( !ent || !ent->IsType( idPlayer::Type ) ) {
continue;
}
static_cast< idPlayer *>( ent )->forcedReady = false;
static_cast<idPlayer *>(ent)->ServerSpectate( true );
}
UpdateWinsLosses( player );
#ifdef CTF
SetFlagMsg( true );
#endif
break;
}
case SUDDENDEATH: {
PrintMessageEvent( -1, MSG_SUDDENDEATH );
PlayGlobalSound( -1, SND_SUDDENDEATH );
break;
}
case COUNTDOWN: {
idBitMsg outMsg;
byte msgBuf[ 128 ];
warmupEndTime = gameLocal.time + 1000*cvarSystem->GetCVarInteger( "g_countDown" );
outMsg.Init( msgBuf, sizeof( msgBuf ) );
outMsg.WriteByte( GAME_RELIABLE_MESSAGE_WARMUPTIME );
outMsg.WriteLong( warmupEndTime );
networkSystem->ServerSendReliableMessage( -1, outMsg );
break;
}
#ifdef CTF
case WARMUP: {
teamPoints[0] = 0;
teamPoints[1] = 0;
if ( IsGametypeFlagBased() ) {
// reset player scores to zero, only required for CTF
for( i = 0; i < gameLocal.numClients; i++ ) {
idEntity *ent = gameLocal.entities[ i ];
if ( !ent || !ent->IsType( idPlayer::Type ) ) {
continue;
}
playerState[ i ].fragCount = 0;
}
}
}
#endif
default:
break;
}
gameState = news;
}
/*
================
idMultiplayerGame::FillTourneySlots
NOTE: called each frame during warmup to keep the tourney slots filled
================
*/
void idMultiplayerGame::FillTourneySlots( ) {
int i, j, rankmax, rankmaxindex;
idEntity *ent;
idPlayer *p;
// fill up the slots based on tourney ranks
for ( i = 0; i < 2; i++ ) {
if ( currentTourneyPlayer[ i ] != -1 ) {
continue;
}
rankmax = -1;
rankmaxindex = -1;
for ( j = 0; j < gameLocal.numClients; j++ ) {
ent = gameLocal.entities[ j ];
if ( !ent || !ent->IsType( idPlayer::Type ) ) {
continue;
}
if ( currentTourneyPlayer[ 0 ] == j || currentTourneyPlayer[ 1 ] == j ) {
continue;
}
p = static_cast< idPlayer * >( ent );
if ( p->wantSpectate ) {
continue;
}
if ( p->tourneyRank >= rankmax ) {
// when ranks are equal, use time in game
if ( p->tourneyRank == rankmax ) {
assert( rankmaxindex >= 0 );
if ( p->spawnedTime > static_cast< idPlayer * >( gameLocal.entities[ rankmaxindex ] )->spawnedTime ) {
continue;
}
}
rankmax = static_cast< idPlayer * >( ent )->tourneyRank;
rankmaxindex = j;
}
}
currentTourneyPlayer[ i ] = rankmaxindex; // may be -1 if we found nothing
}
}
/*
================
idMultiplayerGame::UpdateTourneyLine
we manipulate tourneyRank on player entities for internal ranking. it's easier to deal with.
but we need a real wait list to be synced down to clients for GUI
ignore current players, ignore wantSpectate
================
*/
void idMultiplayerGame::UpdateTourneyLine( void ) {
int i, j, imax, max, globalmax = -1;
idPlayer *p;
assert( !gameLocal.isClient );
if ( gameLocal.gameType != GAME_TOURNEY ) {
return;
}
for ( j = 1; j <= gameLocal.numClients; j++ ) {
max = -1; imax = -1;
for ( i = 0; i < gameLocal.numClients; i++ ) {
if ( currentTourneyPlayer[ 0 ] == i || currentTourneyPlayer[ 1 ] == i ) {
continue;
}
p = static_cast< idPlayer * >( gameLocal.entities[ i ] );
if ( !p || p->wantSpectate ) {
continue;
}
if ( p->tourneyRank > max && ( globalmax == -1 || p->tourneyRank < globalmax ) ) {
imax = i;
max = p->tourneyRank;
}
}
if ( imax == -1 ) {
break;
}
idBitMsg outMsg;
byte msgBuf[1024];
outMsg.Init( msgBuf, sizeof( msgBuf ) );
outMsg.WriteByte( GAME_RELIABLE_MESSAGE_TOURNEYLINE );
outMsg.WriteByte( j );
networkSystem->ServerSendReliableMessage( imax, outMsg );
globalmax = max;
}
}
/*
================
idMultiplayerGame::CycleTourneyPlayers
================
*/
void idMultiplayerGame::CycleTourneyPlayers( ) {
int i;
idEntity *ent;
idPlayer *player;
currentTourneyPlayer[ 0 ] = -1;
currentTourneyPlayer[ 1 ] = -1;
// if any, winner from last round will play again
if ( lastWinner != -1 ) {
idEntity *ent = gameLocal.entities[ lastWinner ];
if ( ent && ent->IsType( idPlayer::Type ) ) {
currentTourneyPlayer[ 0 ] = lastWinner;
}
}
FillTourneySlots( );
// force selected players in/out of the game and update the ranks
for ( i = 0 ; i < gameLocal.numClients ; i++ ) {
if ( currentTourneyPlayer[ 0 ] == i || currentTourneyPlayer[ 1 ] == i ) {
player = static_cast<idPlayer *>( gameLocal.entities[ i ] );
player->ServerSpectate( false );
} else {
ent = gameLocal.entities[ i ];
if ( ent && ent->IsType( idPlayer::Type ) ) {
player = static_cast<idPlayer *>( gameLocal.entities[ i ] );
player->ServerSpectate( true );
}
}
}
UpdateTourneyLine();
}
/*
================
idMultiplayerGame::ExecuteVote
the votes are checked for validity/relevance before they are started
we assume that they are still legit when reaching here
================
*/
void idMultiplayerGame::ExecuteVote( void ) {
bool needRestart;
switch ( vote ) {
case VOTE_RESTART:
gameLocal.MapRestart();
break;
case VOTE_TIMELIMIT:
si_timeLimit.SetInteger( atoi( voteValue ) );
#ifdef _D3XP
needRestart = gameLocal.NeedRestart();
cmdSystem->BufferCommandText( CMD_EXEC_NOW, "rescanSI" );
if ( needRestart ) {
cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "nextMap" );
}
#endif
break;
case VOTE_FRAGLIMIT:
si_fragLimit.SetInteger( atoi( voteValue ) );
#ifdef _D3XP
needRestart = gameLocal.NeedRestart();
cmdSystem->BufferCommandText( CMD_EXEC_NOW, "rescanSI" );
if ( needRestart ) {
cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "nextMap" );
}
#endif
break;
case VOTE_GAMETYPE:
si_gameType.SetString( voteValue );
gameLocal.MapRestart();
break;
case VOTE_KICK:
cmdSystem->BufferCommandText( CMD_EXEC_NOW, va( "kick %s", voteValue.c_str() ) );
break;
case VOTE_MAP:
si_map.SetString( voteValue );
gameLocal.MapRestart();
break;
case VOTE_SPECTATORS:
si_spectators.SetBool( !si_spectators.GetBool() );
#ifdef _D3XP
needRestart = gameLocal.NeedRestart();
cmdSystem->BufferCommandText( CMD_EXEC_NOW, "rescanSI" );
if ( needRestart ) {
cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "nextMap" );
}
#endif
break;
case VOTE_NEXTMAP:
cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "serverNextMap\n" );
break;
}
}
/*
================
idMultiplayerGame::CheckVote
================
*/
void idMultiplayerGame::CheckVote( void ) {
int numVoters, i;
if ( vote == VOTE_NONE ) {
return;
}
if ( voteExecTime ) {
if ( gameLocal.time > voteExecTime ) {
voteExecTime = 0;
ClientUpdateVote( VOTE_RESET, 0, 0 );
ExecuteVote();
vote = VOTE_NONE;
}
return;
}
// count voting players
numVoters = 0;
for ( i = 0; i < gameLocal.numClients; i++ ) {
idEntity *ent = gameLocal.entities[ i ];
if ( !ent || !ent->IsType( idPlayer::Type ) ) {
continue;
}
if ( playerState[ i ].vote != PLAYER_VOTE_NONE ) {
numVoters++;
}
}
if ( !numVoters ) {
// abort
vote = VOTE_NONE;
ClientUpdateVote( VOTE_ABORTED, yesVotes, noVotes );
return;
}
if ( yesVotes / numVoters > 0.5f ) {
ClientUpdateVote( VOTE_PASSED, yesVotes, noVotes );
voteExecTime = gameLocal.time + 2000;
return;
}
if ( gameLocal.time > voteTimeOut || noVotes / numVoters >= 0.5f ) {
ClientUpdateVote( VOTE_FAILED, yesVotes, noVotes );
vote = VOTE_NONE;
return;
}
}
/*
================
idMultiplayerGame::Warmup
================
*/
bool idMultiplayerGame::Warmup() {
return ( gameState == WARMUP );
}
/*
================
idMultiplayerGame::Run
================
*/
void idMultiplayerGame::Run() {
int i, timeLeft;
idPlayer *player;
int gameReviewPause;
assert( gameLocal.isMultiplayer );
assert( !gameLocal.isClient );
pureReady = true;
if ( gameState == INACTIVE ) {
lastGameType = gameLocal.gameType;
NewState( WARMUP );
}
CheckVote();
CheckRespawns();
if ( nextState != INACTIVE && gameLocal.time > nextStateSwitch ) {
NewState( nextState );
nextState = INACTIVE;
}
// don't update the ping every frame to save bandwidth
if ( gameLocal.time > pingUpdateTime ) {
for ( i = 0; i < gameLocal.numClients; i++ ) {
playerState[i].ping = networkSystem->ServerGetClientPing( i );
}
pingUpdateTime = gameLocal.time + 1000;
}
warmupText = "";
switch( gameState ) {
case GAMEREVIEW: {
if ( nextState == INACTIVE ) {
gameReviewPause = cvarSystem->GetCVarInteger( "g_gameReviewPause" );
nextState = NEXTGAME;
nextStateSwitch = gameLocal.time + 1000 * gameReviewPause;
}
break;
}
case NEXTGAME: {
if ( nextState == INACTIVE ) {
// game rotation, new map, gametype etc.
if ( gameLocal.NextMap() ) {
cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "serverMapRestart\n" );
return;
}
#ifdef CTF
// make sure flags are returned
if ( IsGametypeFlagBased() ) {
idItemTeam * flag;
flag = GetTeamFlag( 0 );
if ( flag ) {
flag->Return();
}
flag = GetTeamFlag( 1 );
if ( flag ) {
flag->Return();
}
}
#endif
NewState( WARMUP );
if ( gameLocal.gameType == GAME_TOURNEY ) {
CycleTourneyPlayers();
}
// put everyone back in from endgame spectate
for ( i = 0; i < gameLocal.numClients; i++ ) {
idEntity *ent = gameLocal.entities[ i ];
if ( ent && ent->IsType( idPlayer::Type ) ) {
if ( !static_cast< idPlayer * >( ent )->wantSpectate ) {
CheckRespawns( static_cast<idPlayer *>( ent ) );
}
}
}
}
break;
}
case WARMUP: {
if ( AllPlayersReady() ) {
NewState( COUNTDOWN );
nextState = GAMEON;
nextStateSwitch = gameLocal.time + 1000 * cvarSystem->GetCVarInteger( "g_countDown" );
}
warmupText = "Warming up.. waiting for players to get ready";
one = two = three = false;
break;
}
case COUNTDOWN: {
timeLeft = ( nextStateSwitch - gameLocal.time ) / 1000 + 1;
if ( timeLeft == 3 && !three ) {
PlayGlobalSound( -1, SND_THREE );
three = true;
} else if ( timeLeft == 2 && !two ) {
PlayGlobalSound( -1, SND_TWO );
two = true;
} else if ( timeLeft == 1 && !one ) {
PlayGlobalSound( -1, SND_ONE );
one = true;
}
warmupText = va( "Match starts in %i", timeLeft );
break;
}
case GAMEON: {
#ifdef CTF
if ( IsGametypeFlagBased() ) { /* CTF */
// totally different logic branch for CTF
if ( PointLimitHit() ) {
int team = WinningTeam();
assert( team != -1 );
NewState( GAMEREVIEW, NULL );
PrintMessageEvent( -1, MSG_POINTLIMIT, team );
} else if ( TimeLimitHit() ) {
int team = WinningTeam();
if ( EnoughClientsToPlay() && team == -1 ) {
NewState( SUDDENDEATH );
} else {
NewState( GAMEREVIEW, NULL );
PrintMessageEvent( -1, MSG_TIMELIMIT );
}
}
break;
}
#endif
player = FragLimitHit();
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, player );
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
SuddenRespawn();
PrintMessageEvent( -1, MSG_HOLYSHIT );
fragLimitTimeout = 0;
NewState( SUDDENDEATH );
} else if ( TimeLimitHit() ) {
player = FragLeader();
if ( !player ) {
NewState( SUDDENDEATH );
} else {
NewState( GAMEREVIEW, player );
PrintMessageEvent( -1, MSG_TIMELIMIT );
}
}
}
break;
}
case SUDDENDEATH: {
#ifdef CTF
if ( IsGametypeFlagBased() ) { /* CTF */
int team = WinningTeam();
if ( team != -1 ) {
// TODO : implement pointLimitTimeout
NewState( GAMEREVIEW, NULL );
PrintMessageEvent( -1, MSG_POINTLIMIT, team );
}
break;
}
#endif
player = 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, player );
PrintMessageEvent( -1, MSG_FRAGLIMIT, player->entityNumber );
}
} else if ( fragLimitTimeout ) {
SuddenRespawn();
PrintMessageEvent( -1, MSG_HOLYSHIT );
fragLimitTimeout = 0;
}
break;
}
}
}
/*
================
idMultiplayerGame::UpdateMainGui
================
*/
void idMultiplayerGame::UpdateMainGui( void ) {
int i;
mainGui->SetStateInt( "readyon", gameState == WARMUP ? 1 : 0 );
mainGui->SetStateInt( "readyoff", gameState != WARMUP ? 1 : 0 );
idStr strReady = cvarSystem->GetCVarString( "ui_ready" );
if ( strReady.Icmp( "ready") == 0 ){
strReady = common->GetLanguageDict()->GetString( "#str_04248" );
} else {
strReady = common->GetLanguageDict()->GetString( "#str_04247" );
}
mainGui->SetStateString( "ui_ready", strReady );
mainGui->SetStateInt( "teamon", IsGametypeTeamBased() ? 1 : 0 ); /* CTF */
mainGui->SetStateInt( "teamoff", (!IsGametypeTeamBased()) ? 1 : 0 ); /* CTF */
if ( IsGametypeTeamBased() ) {
idPlayer *p = gameLocal.GetClientByNum( gameLocal.localClientNum );
if ( p ) {
mainGui->SetStateInt( "team", p->team );
}
else {
mainGui->SetStateInt( "team", 0 );
}
}
// setup vote
mainGui->SetStateInt( "voteon", ( vote != VOTE_NONE && !voted ) ? 1 : 0 );
mainGui->SetStateInt( "voteoff", ( vote != VOTE_NONE && !voted ) ? 0 : 1 );
// last man hack
mainGui->SetStateInt( "isLastMan", gameLocal.gameType == GAME_LASTMAN ? 1 : 0 );
// send the current serverinfo values
for ( i = 0; i < gameLocal.serverInfo.GetNumKeyVals(); i++ ) {
const idKeyValue *keyval = gameLocal.serverInfo.GetKeyVal( i );
mainGui->SetStateString( keyval->GetKey(), keyval->GetValue() );
}
mainGui->StateChanged( gameLocal.time );
mainGui->SetStateString( "driver_prompt", "0" );
}
/*
================
idMultiplayerGame::StartMenu
================
*/
idUserInterface* idMultiplayerGame::StartMenu( void ) {
if ( mainGui == NULL ) {
return NULL;
}
int i, j;
if ( currentMenu ) {
currentMenu = 0;
cvarSystem->SetCVarBool( "ui_chat", false );
} else {
if ( nextMenu >= 2 ) {
currentMenu = nextMenu;
} else {
// for default and explicit
currentMenu = 1;
}
cvarSystem->SetCVarBool( "ui_chat", true );
}
nextMenu = 0;
gameLocal.sessionCommand = ""; // in case we used "game_startMenu" to trigger the menu
if ( currentMenu == 1 ) {
UpdateMainGui();
// UpdateMainGui sets most things, but it doesn't set these because
// it'd be pointless and/or harmful to set them every frame (for various reasons)
// Currenty the gui doesn't update properly if they change anyway, so we'll leave it like this.
// setup callvote
if ( vote == VOTE_NONE ) {
bool callvote_ok = false;
for ( i = 0; i < VOTE_COUNT; i++ ) {
// flag on means vote is denied, so default value 0 means all votes and -1 disables
mainGui->SetStateInt( va( "vote%d", i ), g_voteFlags.GetInteger() & ( 1 << i ) ? 0 : 1 );
if ( !( g_voteFlags.GetInteger() & ( 1 << i ) ) ) {
callvote_ok = true;
}
}
mainGui->SetStateInt( "callvote", callvote_ok );
} else {
mainGui->SetStateInt( "callvote", 2 );
}
// player kick data
idStr kickList;
j = 0;
for ( i = 0; i < gameLocal.numClients; i++ ) {
if ( gameLocal.entities[ i ] && gameLocal.entities[ i ]->IsType( idPlayer::Type ) ) {
if ( kickList.Length() ) {
kickList += ";";
}
kickList += va( "\"%d - %s\"", i, gameLocal.userInfo[ i ].GetString( "ui_name" ) );
kickVoteMap[ j ] = i;
j++;
}
}
mainGui->SetStateString( "kickChoices", kickList );
#ifdef CTF
const char *gametype = gameLocal.serverInfo.GetString( "si_gameType" );
const char *map = gameLocal.serverInfo.GetString( "si_map" ); // what if server changes this strings while user in UI?
int num = declManager->GetNumDecls( DECL_MAPDEF );
for ( i = 0; i < num; i++ ) {
const idDeclEntityDef *mapDef = static_cast<const idDeclEntityDef *>( declManager->DeclByIndex( DECL_MAPDEF, i ) );
if ( mapDef && idStr::Icmp( mapDef->GetName(), map ) == 0 && mapDef->dict.GetBool( gametype ) ) {
int k = 0;
idStr gametypeList;
for ( j = 0; si_gameTypeArgs[ j ]; j++ ) {
if ( mapDef->dict.GetBool( si_gameTypeArgs[ j ] ) ) {
if ( gametypeList.Length() ) {
gametypeList += ";";
}
gametypeList += va( "%s", si_gameTypeArgs[ j ] );
gameTypeVoteMap[ k ] = si_gameTypeArgs[ j ];
k++;
}
}
mainGui->SetStateString( "gametypeChoices", gametypeList );
break;
}
}
#endif
mainGui->SetStateString( "chattext", "" );
mainGui->Activate( true, gameLocal.time );
return mainGui;
} else if ( currentMenu == 2 ) {
// the setup is done in MessageMode
msgmodeGui->Activate( true, gameLocal.time );
cvarSystem->SetCVarBool( "ui_chat", true );
return msgmodeGui;
}
return NULL;
}
/*
================
idMultiplayerGame::DisableMenu
================
*/
void idMultiplayerGame::DisableMenu( void ) {
gameLocal.sessionCommand = ""; // in case we used "game_startMenu" to trigger the menu
if ( currentMenu == 1 ) {
mainGui->Activate( false, gameLocal.time );
} else if ( currentMenu == 2 ) {
msgmodeGui->Activate( false, gameLocal.time );
}
currentMenu = 0;
nextMenu = 0;
cvarSystem->SetCVarBool( "ui_chat", false );
}
/*
================
idMultiplayerGame::SetMapShot
================
*/
void idMultiplayerGame::SetMapShot( void ) {
char screenshot[ MAX_STRING_CHARS ];
int mapNum = mapList->GetSelection( NULL, 0 );
const idDict *dict = NULL;
if ( mapNum >= 0 ) {
dict = fileSystem->GetMapDecl( mapNum );
}
fileSystem->FindMapScreenshot( dict ? dict->GetString( "path" ) : "", screenshot, MAX_STRING_CHARS );
mainGui->SetStateString( "current_levelshot", screenshot );
}
/*
================
idMultiplayerGame::HandleGuiCommands
================
*/
const char* idMultiplayerGame::HandleGuiCommands( const char *_menuCommand ) {
idUserInterface *currentGui;
const char *voteValue;
int vote_clientNum;
int icmd;
idCmdArgs args;
if ( !_menuCommand[ 0 ] ) {
common->Printf( "idMultiplayerGame::HandleGuiCommands: empty command\n" );
return "continue";
}
assert( currentMenu );
if ( currentMenu == 1 ) {
currentGui = mainGui;
} else {
currentGui = msgmodeGui;
}
args.TokenizeString( _menuCommand, false );
for( icmd = 0; icmd < args.Argc(); ) {
const char *cmd = args.Argv( icmd++ );
if ( !idStr::Icmp( cmd, ";" ) ) {
continue;
} else if ( !idStr::Icmp( cmd, "video" ) ) {
idStr vcmd;
if ( args.Argc() - icmd >= 1 ) {
vcmd = args.Argv( icmd++ );
}
int oldSpec = cvarSystem->GetCVarInteger( "com_machineSpec" );
if ( idStr::Icmp( vcmd, "low" ) == 0 ) {
cvarSystem->SetCVarInteger( "com_machineSpec", 0 );
} else if ( idStr::Icmp( vcmd, "medium" ) == 0 ) {
cvarSystem->SetCVarInteger( "com_machineSpec", 1 );
} else if ( idStr::Icmp( vcmd, "high" ) == 0 ) {
cvarSystem->SetCVarInteger( "com_machineSpec", 2 );
} else if ( idStr::Icmp( vcmd, "ultra" ) == 0 ) {
cvarSystem->SetCVarInteger( "com_machineSpec", 3 );
} else if ( idStr::Icmp( vcmd, "recommended" ) == 0 ) {
cmdSystem->BufferCommandText( CMD_EXEC_NOW, "setMachineSpec\n" );
}
if ( oldSpec != cvarSystem->GetCVarInteger( "com_machineSpec" ) ) {
currentGui->SetStateInt( "com_machineSpec", cvarSystem->GetCVarInteger( "com_machineSpec" ) );
currentGui->StateChanged( gameLocal.realClientTime );
cmdSystem->BufferCommandText( CMD_EXEC_NOW, "execMachineSpec\n" );
}
if ( idStr::Icmp( vcmd, "restart" ) == 0) {
cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "vid_restart\n" );
}
continue;
} else if ( !idStr::Icmp( cmd, "play" ) ) {
if ( args.Argc() - icmd >= 1 ) {
idStr snd = args.Argv( icmd++ );
int channel = 1;
if ( snd.Length() == 1 ) {
channel = atoi( snd );
snd = args.Argv( icmd++ );
}
gameSoundWorld->PlayShaderDirectly( snd, channel );
}
continue;
} else if ( !idStr::Icmp( cmd, "mpSkin" ) ) {
idStr skin;
if ( args.Argc() - icmd >= 1 ) {
skin = args.Argv( icmd++ );
cvarSystem->SetCVarString( "ui_skin", skin );
}
SetMenuSkin();
continue;
} else if ( !idStr::Icmp( cmd, "quit" ) ) {
cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "quit\n" );
return NULL;
} else if ( !idStr::Icmp( cmd, "disconnect" ) ) {
cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "disconnect\n" );
return NULL;
} else if ( !idStr::Icmp( cmd, "close" ) ) {
DisableMenu( );
return NULL;
} else if ( !idStr::Icmp( cmd, "spectate" ) ) {
ToggleSpectate();
DisableMenu( );
return NULL;
} else if ( !idStr::Icmp( cmd, "chatmessage" ) ) {
int mode = currentGui->State().GetInt( "messagemode" );
if ( mode ) {
cmdSystem->BufferCommandText( CMD_EXEC_NOW, va( "sayTeam \"%s\"", currentGui->State().GetString( "chattext" ) ) );
} else {
cmdSystem->BufferCommandText( CMD_EXEC_NOW, va( "say \"%s\"", currentGui->State().GetString( "chattext" ) ) );
}
currentGui->SetStateString( "chattext", "" );
if ( currentMenu == 1 ) {
return "continue";
} else {
DisableMenu();
return NULL;
}
} else if ( !idStr::Icmp( cmd, "readytoggle" ) ) {
ToggleReady( );
DisableMenu( );
return NULL;
} else if ( !idStr::Icmp( cmd, "teamtoggle" ) ) {
ToggleTeam( );
DisableMenu( );
return NULL;
} else if ( !idStr::Icmp( cmd, "callVote" ) ) {
vote_flags_t voteIndex = (vote_flags_t)mainGui->State().GetInt( "voteIndex" );
if ( voteIndex == VOTE_MAP ) {
int mapNum = mapList->GetSelection( NULL, 0 );
if ( mapNum >= 0 ) {
const idDict *dict = fileSystem->GetMapDecl( mapNum );
if ( dict ) {
ClientCallVote( VOTE_MAP, dict->GetString( "path" ) );
}
}
} else {
voteValue = mainGui->State().GetString( "str_voteValue" );
if ( voteIndex == VOTE_KICK ) {
vote_clientNum = kickVoteMap[ atoi( voteValue ) ];
ClientCallVote( voteIndex, va( "%d", vote_clientNum ) );
#ifdef CTF
} else if ( voteIndex == VOTE_GAMETYPE ) {
// send the actual gametype index, not an index in the choice list
int i;
for ( i = 0; si_gameTypeArgs[i]; i++ ) {
if ( !idStr::Icmp( gameTypeVoteMap[ atoi( voteValue ) ], si_gameTypeArgs[i] ) ) {
ClientCallVote( voteIndex, va( "%d", i ) );
break;
}
}
#endif
} else {
ClientCallVote( voteIndex, voteValue );
}
}
DisableMenu();
return NULL;
} else if ( !idStr::Icmp( cmd, "voteyes" ) ) {
CastVote( gameLocal.localClientNum, true );
DisableMenu();
return NULL;
} else if ( !idStr::Icmp( cmd, "voteno" ) ) {
CastVote( gameLocal.localClientNum, false );
DisableMenu();
return NULL;
} else if ( !idStr::Icmp( cmd, "bind" ) ) {
if ( args.Argc() - icmd >= 2 ) {
idStr key = args.Argv( icmd++ );
idStr bind = args.Argv( icmd++ );
cmdSystem->BufferCommandText( CMD_EXEC_NOW, va( "bindunbindtwo \"%s\" \"%s\"", key.c_str(), bind.c_str() ) );
mainGui->SetKeyBindingNames();
}
continue;
} else if ( !idStr::Icmp( cmd, "clearbind" ) ) {
if ( args.Argc() - icmd >= 1 ) {
idStr bind = args.Argv( icmd++ );
cmdSystem->BufferCommandText( CMD_EXEC_NOW, va( "unbind \"%s\"", bind.c_str() ) );
mainGui->SetKeyBindingNames();
}
continue;
} else if ( !idStr::Icmp( cmd, "MAPScan" ) ) {
const char *gametype = gameLocal.serverInfo.GetString( "si_gameType" );
if ( gametype == NULL || *gametype == 0 || idStr::Icmp( gametype, "singleplayer" ) == 0 ) {
gametype = "Deathmatch";
}
int i, num;
idStr si_map = gameLocal.serverInfo.GetString("si_map");
const idDict *dict;
mapList->Clear();
mapList->SetSelection( -1 );
num = fileSystem->GetNumMaps();
for ( i = 0; i < num; i++ ) {
dict = fileSystem->GetMapDecl( i );
if ( dict ) {
// any MP gametype supported
bool isMP = false;
int igt = GAME_SP + 1;
while ( si_gameTypeArgs[ igt ] ) {
if ( dict->GetBool( si_gameTypeArgs[ igt ] ) ) {
isMP = true;
break;
}
igt++;
}
if ( isMP ) {
const char *mapName = dict->GetString( "name" );
if ( mapName[0] == '\0' ) {
mapName = dict->GetString( "path" );
}
mapName = common->GetLanguageDict()->GetString( mapName );
mapList->Add( i, mapName );
if ( !si_map.Icmp( dict->GetString( "path" ) ) ) {
mapList->SetSelection( mapList->Num() - 1 );
}
}
}
}
// set the current level shot
SetMapShot( );
return "continue";
} else if ( !idStr::Icmp( cmd, "click_maplist" ) ) {
SetMapShot( );
return "continue";
} else if ( strstr( cmd, "sound" ) == cmd ) {
// pass that back to the core, will know what to do with it
return _menuCommand;
}
common->Printf( "idMultiplayerGame::HandleGuiCommands: '%s' unknown\n", cmd );
}
return "continue";
}
/*
================
idMultiplayerGame::Draw
================
*/
bool idMultiplayerGame::Draw( int clientNum ) {
idPlayer *player, *viewPlayer;
// clear the render entities for any players that don't need
// icons and which might not be thinking because they weren't in
// the last snapshot.
for ( int i = 0; i < gameLocal.numClients; i++ ) {
player = static_cast<idPlayer *>( gameLocal.entities[ i ] );
if ( player && !player->NeedsIcon() ) {
player->HidePlayerIcons();
}
}
player = viewPlayer = static_cast<idPlayer *>( gameLocal.entities[ clientNum ] );
if ( player == NULL ) {
return false;
}
if ( player->spectating ) {
viewPlayer = static_cast<idPlayer *>( gameLocal.entities[ player->spectator ] );
if ( viewPlayer == NULL ) {
return false;
}
}
UpdatePlayerRanks();
UpdateHud( viewPlayer, player->hud );
// use the hud of the local player
viewPlayer->playerView.RenderPlayerView( player->hud );
if ( currentMenu ) {
#if 0
// uncomment this if you want to track when players are in a menu
if ( !bCurrentMenuMsg ) {
idBitMsg outMsg;
byte msgBuf[ 128 ];
outMsg.Init( msgBuf, sizeof( msgBuf ) );
outMsg.WriteByte( GAME_RELIABLE_MESSAGE_MENU );
outMsg.WriteBits( 1, 1 );
networkSystem->ClientSendReliableMessage( outMsg );
bCurrentMenuMsg = true;
}
#endif
if ( player->wantSpectate ) {
mainGui->SetStateString( "spectext", common->GetLanguageDict()->GetString( "#str_04249" ) );
} else {
mainGui->SetStateString( "spectext", common->GetLanguageDict()->GetString( "#str_04250" ) );
}
DrawChat();
if ( currentMenu == 1 ) {
UpdateMainGui();
mainGui->Redraw( gameLocal.time );
} else {
msgmodeGui->Redraw( gameLocal.time );
}
} else {
#if 0
// uncomment this if you want to track when players are in a menu
if ( bCurrentMenuMsg ) {
idBitMsg outMsg;
byte msgBuf[ 128 ];
outMsg.Init( msgBuf, sizeof( msgBuf ) );
outMsg.WriteByte( GAME_RELIABLE_MESSAGE_MENU );
outMsg.WriteBits( 0, 1 );
networkSystem->ClientSendReliableMessage( outMsg );
bCurrentMenuMsg = false;
}
#endif
if ( player->spectating ) {
idStr spectatetext[ 2 ];
int ispecline = 0;
if ( gameLocal.gameType == GAME_TOURNEY ) {
if ( !player->wantSpectate ) {
spectatetext[ 0 ] = common->GetLanguageDict()->GetString( "#str_04246" );
switch ( player->tourneyLine ) {
case 0:
spectatetext[ 0 ] += common->GetLanguageDict()->GetString( "#str_07003" );
break;
case 1:
spectatetext[ 0 ] += common->GetLanguageDict()->GetString( "#str_07004" );
break;
case 2:
spectatetext[ 0 ] += common->GetLanguageDict()->GetString( "#str_07005" );
break;
default:
spectatetext[ 0 ] += va( common->GetLanguageDict()->GetString( "#str_07006" ), player->tourneyLine );
break;
}
ispecline++;
}
} else if ( gameLocal.gameType == GAME_LASTMAN ) {
if ( !player->wantSpectate ) {
spectatetext[ 0 ] = common->GetLanguageDict()->GetString( "#str_07007" );
ispecline++;
}
}
if ( player->spectator != player->entityNumber ) {
spectatetext[ ispecline ] = va( common->GetLanguageDict()->GetString( "#str_07008" ), viewPlayer->GetUserInfo()->GetString( "ui_name" ) );
} else if ( !ispecline ) {
spectatetext[ 0 ] = common->GetLanguageDict()->GetString( "#str_04246" );
}
spectateGui->SetStateString( "spectatetext0", spectatetext[0].c_str() );
spectateGui->SetStateString( "spectatetext1", spectatetext[1].c_str() );
if ( vote != VOTE_NONE ) {
spectateGui->SetStateString( "vote", va( "%s (y: %d n: %d)", voteString.c_str(), (int)yesVotes, (int)noVotes ) );
} else {
spectateGui->SetStateString( "vote", "" );
}
spectateGui->Redraw( gameLocal.time );
}
DrawChat();
DrawScoreBoard( player );
}
return true;
}
/*
================
idMultiplayerGame::UpdateHud
================
*/
void idMultiplayerGame::UpdateHud( idPlayer *player, idUserInterface *hud ) {
int i;
if ( !hud ) {
return;
}
hud->SetStateBool( "warmup", Warmup() );
if ( gameState == WARMUP ) {
if ( player->IsReady() ) {
hud->SetStateString( "warmuptext", common->GetLanguageDict()->GetString( "#str_04251" ) );
} else {
hud->SetStateString( "warmuptext", common->GetLanguageDict()->GetString( "#str_07002" ) );
}
}
hud->SetStateString( "timer", ( Warmup() ) ? common->GetLanguageDict()->GetString( "#str_04251" ) : ( gameState == SUDDENDEATH ) ? common->GetLanguageDict()->GetString( "#str_04252" ) : GameTime() );
if ( vote != VOTE_NONE ) {
hud->SetStateString( "vote", va( "%s (y: %d n: %d)", voteString.c_str(), (int)yesVotes, (int)noVotes ) );
} else {
hud->SetStateString( "vote", "" );
}
hud->SetStateInt( "rank_self", 0 );
if ( gameState == GAMEON ) {
for ( i = 0; i < numRankedPlayers; i++ ) {
if ( IsGametypeTeamBased() ) { /* CTF */
hud->SetStateInt( va( "player%i_score", i+1 ), playerState[ rankedPlayers[ i ]->entityNumber ].teamFragCount );
} else {
hud->SetStateInt( va( "player%i_score", i+1 ), playerState[ rankedPlayers[ i ]->entityNumber ].fragCount );
}
hud->SetStateInt( va( "rank%i", i+1 ), 1 );
UpdateRankColor( hud, "rank%i_color%i", i+1, rankedPlayers[ i ]->colorBar );
if ( rankedPlayers[ i ] == player ) {
hud->SetStateInt( "rank_self", i+1 );
}
}
}
#ifdef _D3XP
for ( i = ( gameState == GAMEON ? numRankedPlayers : 0 ) ; i < MAX_CLIENTS; i++ ) {
#else
for ( i = ( gameState == GAMEON ? numRankedPlayers : 0 ) ; i < 5; i++ ) {
#endif
hud->SetStateString( va( "player%i", i+1 ), "" );
hud->SetStateString( va( "player%i_score", i+1 ), "" );
hud->SetStateInt( va( "rank%i", i+1 ), 0 );
}
#ifdef CTF
if ( IsGametypeFlagBased() )
hud->SetStateInt( "self_team", player->team );
else
hud->SetStateInt( "self_team", -1 ); /* Disable */
#endif
}
/*
================
idMultiplayerGame::DrawScoreBoard
================
*/
void idMultiplayerGame::DrawScoreBoard( idPlayer *player ) {
if ( player->scoreBoardOpen || gameState == GAMEREVIEW ) {
if ( !playerState[ player->entityNumber ].scoreBoardUp ) {
scoreBoard->Activate( true, gameLocal.time );
playerState[ player->entityNumber ].scoreBoardUp = true;
}
#ifdef CTF
if ( IsGametypeFlagBased() )
UpdateCTFScoreboard( scoreBoard, player );
else
#endif
UpdateScoreboard( scoreBoard, player );
} else {
if ( playerState[ player->entityNumber ].scoreBoardUp ) {
scoreBoard->Activate( false, gameLocal.time );
playerState[ player->entityNumber ].scoreBoardUp = false;
}
}
}
/*
===============
idMultiplayerGame::ClearChatData
===============
*/
void idMultiplayerGame::ClearChatData() {
chatHistoryIndex = 0;
chatHistorySize = 0;
chatDataUpdated = true;
}
/*
===============
idMultiplayerGame::AddChatLine
===============
*/
void idMultiplayerGame::AddChatLine( const char *fmt, ... ) {
idStr temp;
va_list argptr;
va_start( argptr, fmt );
vsprintf( temp, fmt, argptr );
va_end( argptr );
gameLocal.Printf( "%s\n", temp.c_str() );
chatHistory[ chatHistoryIndex % NUM_CHAT_NOTIFY ].line = temp;
chatHistory[ chatHistoryIndex % NUM_CHAT_NOTIFY ].fade = 6;
chatHistoryIndex++;
if ( chatHistorySize < NUM_CHAT_NOTIFY ) {
chatHistorySize++;
}
chatDataUpdated = true;
lastChatLineTime = gameLocal.time;
}
/*
===============
idMultiplayerGame::DrawChat
===============
*/
void idMultiplayerGame::DrawChat() {
int i, j;
if ( guiChat ) {
if ( gameLocal.time - lastChatLineTime > CHAT_FADE_TIME ) {
if ( chatHistorySize > 0 ) {
for ( i = chatHistoryIndex - chatHistorySize; i < chatHistoryIndex; i++ ) {
chatHistory[ i % NUM_CHAT_NOTIFY ].fade--;
if ( chatHistory[ i % NUM_CHAT_NOTIFY ].fade < 0 ) {
chatHistorySize--; // this assumes the removals are always at the beginning
}
}
chatDataUpdated = true;
}
lastChatLineTime = gameLocal.time;
}
if ( chatDataUpdated ) {
j = 0;
i = chatHistoryIndex - chatHistorySize;
while ( i < chatHistoryIndex ) {
guiChat->SetStateString( va( "chat%i", j ), chatHistory[ i % NUM_CHAT_NOTIFY ].line );
// don't set alpha above 4, the gui only knows that
guiChat->SetStateInt( va( "alpha%i", j ), Min( 4, (int)chatHistory[ i % NUM_CHAT_NOTIFY ].fade ) );
j++; i++;
}
while ( j < NUM_CHAT_NOTIFY ) {
guiChat->SetStateString( va( "chat%i", j ), "" );
j++;
}
guiChat->Activate( true, gameLocal.time );
chatDataUpdated = false;
}
guiChat->Redraw( gameLocal.time );
}
}
#ifdef _D3XP
//D3XP: Adding one to frag count to allow for the negative flag in numbers greater than 255
const int ASYNC_PLAYER_FRAG_BITS = -(idMath::BitsForInteger( MP_PLAYER_MAXFRAGS - MP_PLAYER_MINFRAGS )+1); // player can have negative frags
#else
const int ASYNC_PLAYER_FRAG_BITS = -idMath::BitsForInteger( MP_PLAYER_MAXFRAGS - MP_PLAYER_MINFRAGS ); // player can have negative frags
#endif
const int ASYNC_PLAYER_WINS_BITS = idMath::BitsForInteger( MP_PLAYER_MAXWINS );
const int ASYNC_PLAYER_PING_BITS = idMath::BitsForInteger( MP_PLAYER_MAXPING );
/*
================
idMultiplayerGame::WriteToSnapshot
================
*/
void idMultiplayerGame::WriteToSnapshot( idBitMsgDelta &msg ) const {
int i;
int value;
msg.WriteByte( gameState );
msg.WriteShort( currentTourneyPlayer[ 0 ] );
msg.WriteShort( currentTourneyPlayer[ 1 ] );
for ( i = 0; i < MAX_CLIENTS; i++ ) {
// clamp all values to min/max possible value that we can send over
value = idMath::ClampInt( MP_PLAYER_MINFRAGS, MP_PLAYER_MAXFRAGS, playerState[i].fragCount );
msg.WriteBits( value, ASYNC_PLAYER_FRAG_BITS );
value = idMath::ClampInt( MP_PLAYER_MINFRAGS, MP_PLAYER_MAXFRAGS, playerState[i].teamFragCount );
msg.WriteBits( value, ASYNC_PLAYER_FRAG_BITS );
value = idMath::ClampInt( 0, MP_PLAYER_MAXWINS, playerState[i].wins );
msg.WriteBits( value, ASYNC_PLAYER_WINS_BITS );
value = idMath::ClampInt( 0, MP_PLAYER_MAXPING, playerState[i].ping );
msg.WriteBits( value, ASYNC_PLAYER_PING_BITS );
msg.WriteBits( playerState[i].ingame, 1 );
}
#ifdef CTF
msg.WriteShort( teamPoints[0] );
msg.WriteShort( teamPoints[1] );
msg.WriteShort( player_red_flag );
msg.WriteShort( player_blue_flag );
#endif
}
/*
================
idMultiplayerGame::ReadFromSnapshot
================
*/
void idMultiplayerGame::ReadFromSnapshot( const idBitMsgDelta &msg ) {
int i;
gameState_t newState;
newState = (idMultiplayerGame::gameState_t)msg.ReadByte();
if ( newState != gameState ) {
gameLocal.DPrintf( "%s -> %s\n", GameStateStrings[ gameState ], GameStateStrings[ newState ] );
gameState = newState;
// these could be gathered in a BGNewState() kind of thing, as we have to do them in NewState as well
if ( gameState == GAMEON ) {
matchStartedTime = gameLocal.time;
cvarSystem->SetCVarString( "ui_ready", "Not Ready" );
switchThrottle[ 1 ] = 0; // passby the throttle
startFragLimit = gameLocal.serverInfo.GetInt( "si_fragLimit" );
}
}
currentTourneyPlayer[ 0 ] = msg.ReadShort();
currentTourneyPlayer[ 1 ] = msg.ReadShort();
for ( i = 0; i < MAX_CLIENTS; i++ ) {
playerState[i].fragCount = msg.ReadBits( ASYNC_PLAYER_FRAG_BITS );
playerState[i].teamFragCount = msg.ReadBits( ASYNC_PLAYER_FRAG_BITS );
playerState[i].wins = msg.ReadBits( ASYNC_PLAYER_WINS_BITS );
playerState[i].ping = msg.ReadBits( ASYNC_PLAYER_PING_BITS );
playerState[i].ingame = msg.ReadBits( 1 ) != 0;
}
#ifdef CTF
teamPoints[0] = msg.ReadShort();
teamPoints[1] = msg.ReadShort();
player_red_flag = msg.ReadShort();
player_blue_flag = msg.ReadShort();
#endif
}
/*
================
idMultiplayerGame::PlayGlobalSound
================
*/
void idMultiplayerGame::PlayGlobalSound( int to, snd_evt_t evt, const char *shader ) {
const idSoundShader *shaderDecl;
if ( to == -1 || to == gameLocal.localClientNum ) {
if ( shader ) {
if ( gameSoundWorld ) {
gameSoundWorld->PlayShaderDirectly( shader );
}
} else {
if ( gameSoundWorld ) {
gameSoundWorld->PlayShaderDirectly( GlobalSoundStrings[ evt ] );
}
}
}
if ( !gameLocal.isClient ) {
idBitMsg outMsg;
byte msgBuf[1024];
outMsg.Init( msgBuf, sizeof( msgBuf ) );
if ( shader ) {
shaderDecl = declManager->FindSound( shader );
if ( !shaderDecl ) {
return;
}
outMsg.WriteByte( GAME_RELIABLE_MESSAGE_SOUND_INDEX );
outMsg.WriteLong( gameLocal.ServerRemapDecl( to, DECL_SOUND, shaderDecl->Index() ) );
} else {
outMsg.WriteByte( GAME_RELIABLE_MESSAGE_SOUND_EVENT );
outMsg.WriteByte( evt );
}
networkSystem->ServerSendReliableMessage( to, outMsg );
}
}
#ifdef CTF
/*
================
idMultiplayerGame::PlayTeamSound
================
*/
void idMultiplayerGame::PlayTeamSound( int toTeam, snd_evt_t evt, const char *shader ) {
for( int i = 0; i < gameLocal.numClients; i++ ) {
idEntity *ent = gameLocal.entities[ i ];
if ( !ent || !ent->IsType( idPlayer::Type ) ) {
continue;
}
idPlayer * player = static_cast<idPlayer*>(ent);
if ( player->team != toTeam )
continue;
PlayGlobalSound( i, evt, shader );
}
}
#endif
/*
================
idMultiplayerGame::PrintMessageEvent
================
*/
void idMultiplayerGame::PrintMessageEvent( int to, msg_evt_t evt, int parm1, int parm2 ) {
switch ( evt ) {
case MSG_SUICIDE:
assert( parm1 >= 0 );
AddChatLine( common->GetLanguageDict()->GetString( "#str_04293" ), gameLocal.userInfo[ parm1 ].GetString( "ui_name" ) );
break;
case MSG_KILLED:
assert( parm1 >= 0 && parm2 >= 0 );
AddChatLine( common->GetLanguageDict()->GetString( "#str_04292" ), gameLocal.userInfo[ parm1 ].GetString( "ui_name" ), gameLocal.userInfo[ parm2 ].GetString( "ui_name" ) );
break;
case MSG_KILLEDTEAM:
assert( parm1 >= 0 && parm2 >= 0 );
AddChatLine( common->GetLanguageDict()->GetString( "#str_04291" ), gameLocal.userInfo[ parm1 ].GetString( "ui_name" ), gameLocal.userInfo[ parm2 ].GetString( "ui_name" ) );
break;
case MSG_TELEFRAGGED:
assert( parm1 >= 0 && parm2 >= 0 );
AddChatLine( common->GetLanguageDict()->GetString( "#str_04290" ), gameLocal.userInfo[ parm1 ].GetString( "ui_name" ), gameLocal.userInfo[ parm2 ].GetString( "ui_name" ) );
break;
case MSG_DIED:
assert( parm1 >= 0 );
AddChatLine( common->GetLanguageDict()->GetString( "#str_04289" ), gameLocal.userInfo[ parm1 ].GetString( "ui_name" ) );
break;
case MSG_VOTE:
AddChatLine( common->GetLanguageDict()->GetString( "#str_04288" ) );
break;
case MSG_SUDDENDEATH:
AddChatLine( common->GetLanguageDict()->GetString( "#str_04287" ) );
break;
case MSG_FORCEREADY:
AddChatLine( common->GetLanguageDict()->GetString( "#str_04286" ), gameLocal.userInfo[ parm1 ].GetString( "ui_name" ) );
if ( gameLocal.entities[ parm1 ] && gameLocal.entities[ parm1 ]->IsType( idPlayer::Type ) ) {
static_cast< idPlayer * >( gameLocal.entities[ parm1 ] )->forcedReady = true;
}
break;
case MSG_JOINEDSPEC:
AddChatLine( common->GetLanguageDict()->GetString( "#str_04285" ), gameLocal.userInfo[ parm1 ].GetString( "ui_name" ) );
break;
case MSG_TIMELIMIT:
AddChatLine( common->GetLanguageDict()->GetString( "#str_04284" ) );
break;
case MSG_FRAGLIMIT:
if ( gameLocal.gameType == GAME_LASTMAN ) {
AddChatLine( common->GetLanguageDict()->GetString( "#str_04283" ), gameLocal.userInfo[ parm1 ].GetString( "ui_name" ) );
} else if ( IsGametypeTeamBased() ) { /* CTF */
AddChatLine( common->GetLanguageDict()->GetString( "#str_04282" ), gameLocal.userInfo[ parm1 ].GetString( "ui_team" ) );
} else {
AddChatLine( common->GetLanguageDict()->GetString( "#str_04281" ), gameLocal.userInfo[ parm1 ].GetString( "ui_name" ) );
}
break;
case MSG_JOINTEAM:
AddChatLine( common->GetLanguageDict()->GetString( "#str_04280" ), gameLocal.userInfo[ parm1 ].GetString( "ui_name" ), parm2 ? common->GetLanguageDict()->GetString( "#str_02500" ) : common->GetLanguageDict()->GetString( "#str_02499" ) );
break;
case MSG_HOLYSHIT:
AddChatLine( common->GetLanguageDict()->GetString( "#str_06732" ) );
break;
#ifdef CTF
case MSG_POINTLIMIT:
AddChatLine( common->GetLanguageDict()->GetString( "#str_11100" ), parm1 ? common->GetLanguageDict()->GetString( "#str_11110" ) : common->GetLanguageDict()->GetString( "#str_11111" ) );
break;
case MSG_FLAGTAKEN :
if ( gameLocal.GetLocalPlayer() == NULL )
break;
if ( parm2 < 0 || parm2 >= MAX_CLIENTS )
break;
if ( gameLocal.GetLocalPlayer()->team != parm1 ) {
AddChatLine( common->GetLanguageDict()->GetString( "#str_11101" ), gameLocal.userInfo[ parm2 ].GetString( "ui_name" ) ); // your team
} else {
AddChatLine( common->GetLanguageDict()->GetString( "#str_11102" ), gameLocal.userInfo[ parm2 ].GetString( "ui_name" ) ); // enemy
}
break;
case MSG_FLAGDROP :
if ( gameLocal.GetLocalPlayer() == NULL )
break;
if ( gameLocal.GetLocalPlayer()->team != parm1 ) {
AddChatLine( common->GetLanguageDict()->GetString( "#str_11103" ) ); // your team
} else {
AddChatLine( common->GetLanguageDict()->GetString( "#str_11104" ) ); // enemy
}
break;
case MSG_FLAGRETURN :
if ( gameLocal.GetLocalPlayer() == NULL )
break;
if ( parm2 >= 0 && parm2 < MAX_CLIENTS ) {
if ( gameLocal.GetLocalPlayer()->team != parm1 ) {
AddChatLine( common->GetLanguageDict()->GetString( "#str_11120" ), gameLocal.userInfo[ parm2 ].GetString( "ui_name" ) ); // your team
} else {
AddChatLine( common->GetLanguageDict()->GetString( "#str_11121" ), gameLocal.userInfo[ parm2 ].GetString( "ui_name" ) ); // enemy
}
} else {
AddChatLine( common->GetLanguageDict()->GetString( "#str_11105" ), parm1 ? common->GetLanguageDict()->GetString( "#str_11110" ) : common->GetLanguageDict()->GetString( "#str_11111" ) );
}
break;
case MSG_FLAGCAPTURE :
if ( gameLocal.GetLocalPlayer() == NULL )
break;
if ( parm2 < 0 || parm2 >= MAX_CLIENTS )
break;
if ( gameLocal.GetLocalPlayer()->team != parm1 ) {
AddChatLine( common->GetLanguageDict()->GetString( "#str_11122" ), gameLocal.userInfo[ parm2 ].GetString( "ui_name" ) ); // your team
} else {
AddChatLine( common->GetLanguageDict()->GetString( "#str_11123" ), gameLocal.userInfo[ parm2 ].GetString( "ui_name" ) ); // enemy
}
// AddChatLine( common->GetLanguageDict()->GetString( "#str_11106" ), parm1 ? common->GetLanguageDict()->GetString( "#str_11110" ) : common->GetLanguageDict()->GetString( "#str_11111" ) );
break;
case MSG_SCOREUPDATE:
AddChatLine( common->GetLanguageDict()->GetString( "#str_11107" ), parm1, parm2 );
break;
#endif
default:
gameLocal.DPrintf( "PrintMessageEvent: unknown message type %d\n", evt );
return;
}
if ( !gameLocal.isClient ) {
idBitMsg outMsg;
byte msgBuf[1024];
outMsg.Init( msgBuf, sizeof( msgBuf ) );
outMsg.WriteByte( GAME_RELIABLE_MESSAGE_DB );
outMsg.WriteByte( evt );
outMsg.WriteByte( parm1 );
outMsg.WriteByte( parm2 );
networkSystem->ServerSendReliableMessage( to, outMsg );
}
}
/*
================
idMultiplayerGame::SuddenRespawns
solely for LMN if an end game ( fragLimitTimeout ) was entered and aborted before expiration
LMN players which still have lives left need to be respawned without being marked lastManOver
================
*/
void idMultiplayerGame::SuddenRespawn( void ) {
int i;
if ( gameLocal.gameType != GAME_LASTMAN ) {
return;
}
for ( i = 0; i < gameLocal.numClients; i++ ) {
if ( !gameLocal.entities[ i ] || !gameLocal.entities[ i ]->IsType( idPlayer::Type ) ) {
continue;
}
if ( !CanPlay( static_cast< idPlayer * >( gameLocal.entities[ i ] ) ) ) {
continue;
}
if ( static_cast< idPlayer * >( gameLocal.entities[ i ] )->lastManOver ) {
continue;
}
static_cast< idPlayer * >( gameLocal.entities[ i ] )->lastManPlayAgain = true;
}
}
/*
================
idMultiplayerGame::CheckSpawns
================
*/
void idMultiplayerGame::CheckRespawns( idPlayer *spectator ) {
for( int i = 0 ; i < gameLocal.numClients ; i++ ) {
idEntity *ent = gameLocal.entities[ i ];
if ( !ent || !ent->IsType( idPlayer::Type ) ) {
continue;
}
idPlayer *p = static_cast<idPlayer *>(ent);
// once we hit sudden death, nobody respawns till game has ended
if ( WantRespawn( p ) || p == spectator ) {
if ( gameState == SUDDENDEATH && gameLocal.gameType != GAME_LASTMAN ) {
// respawn rules while sudden death are different
// sudden death may trigger while a player is dead, so there are still cases where we need to respawn
// don't do any respawns while we are in end game delay though
if ( !fragLimitTimeout ) {
if ( IsGametypeTeamBased() || p->IsLeader() ) { /* CTF */
#ifdef _DEBUG
if ( gameLocal.gameType == GAME_TOURNEY ) {
assert( p->entityNumber == currentTourneyPlayer[ 0 ] || p->entityNumber == currentTourneyPlayer[ 1 ] );
}
#endif
p->ServerSpectate( false );
} else if ( !p->IsLeader() ) {
// sudden death is rolling, this player is not a leader, have him spectate
p->ServerSpectate( true );
CheckAbortGame();
}
}
} else {
if ( gameLocal.gameType == GAME_DM || // CTF : 3wave sboily, was DM really included before?
IsGametypeTeamBased() )
{
if ( gameState == WARMUP || gameState == COUNTDOWN || gameState == GAMEON ) {
p->ServerSpectate( false );
}
} else if ( gameLocal.gameType == GAME_TOURNEY ) {
if ( i == currentTourneyPlayer[ 0 ] || i == currentTourneyPlayer[ 1 ] ) {
if ( gameState == WARMUP || gameState == COUNTDOWN || gameState == GAMEON ) {
p->ServerSpectate( false );
}
} else if ( gameState == WARMUP ) {
// make sure empty tourney slots get filled first
FillTourneySlots( );
if ( i == currentTourneyPlayer[ 0 ] || i == currentTourneyPlayer[ 1 ] ) {
p->ServerSpectate( false );
}
}
} else if ( gameLocal.gameType == GAME_LASTMAN ) {
if ( gameState == WARMUP || gameState == COUNTDOWN ) {
p->ServerSpectate( false );
} else if ( gameState == GAMEON || gameState == SUDDENDEATH ) {
if ( gameState == GAMEON && playerState[ i ].fragCount > 0 && p->lastManPresent ) {
assert( !p->lastManOver );
p->ServerSpectate( false );
} else if ( p->lastManPlayAgain && p->lastManPresent ) {
assert( gameState == SUDDENDEATH );
p->ServerSpectate( false );
} else {
// if a fragLimitTimeout was engaged, do NOT mark lastManOver as that could mean
// everyone ends up spectator and game is stalled with no end
// if the frag limit delay is engaged and cancels out before expiring, LMN players are
// respawned to play the tie again ( through SuddenRespawn and lastManPlayAgain )
if ( !fragLimitTimeout && !p->lastManOver ) {
common->DPrintf( "client %d has lost all last man lives\n", i );
// end of the game for this guy, send him to spectators
p->lastManOver = true;
// clients don't have access to lastManOver
// so set the fragCount to something silly ( used in scoreboard and player ranking )
playerState[ i ].fragCount = LASTMAN_NOLIVES;
p->ServerSpectate( true );
//Check for a situation where the last two player dies at the same time and don't
//try to respawn manually...This was causing all players to go into spectate mode
//and the server got stuck
{
int j;
for ( j = 0; j < gameLocal.numClients; j++ ) {
if ( !gameLocal.entities[ j ] ) {
continue;
}
if ( !CanPlay( static_cast< idPlayer * >( gameLocal.entities[ j ] ) ) ) {
continue;
}
if ( !static_cast< idPlayer * >( gameLocal.entities[ j ] )->lastManOver ) {
break;
}
}
if( j == gameLocal.numClients) {
//Everyone is dead so don't allow this player to spectate
//so the match will end
p->ServerSpectate( false );
}
}
}
}
}
}
}
} else if ( p->wantSpectate && !p->spectating ) {
playerState[ i ].fragCount = 0; // whenever you willingly go spectate during game, your score resets
p->ServerSpectate( true );
UpdateTourneyLine();
CheckAbortGame();
}
}
}
/*
================
idMultiplayerGame::ForceReady
================
*/
void idMultiplayerGame::ForceReady( ) {
for( int i = 0 ; i < gameLocal.numClients ; i++ ) {
idEntity *ent = gameLocal.entities[ i ];
if ( !ent || !ent->IsType( idPlayer::Type ) ) {
continue;
}
idPlayer *p = static_cast<idPlayer *>( ent );
if ( !p->IsReady() ) {
PrintMessageEvent( -1, MSG_FORCEREADY, i );
p->forcedReady = true;
}
}
}
/*
================
idMultiplayerGame::ForceReady_f
================
*/
void idMultiplayerGame::ForceReady_f( const idCmdArgs &args ) {
if ( !gameLocal.isMultiplayer || gameLocal.isClient ) {
common->Printf( "forceReady: multiplayer server only\n" );
return;
}
gameLocal.mpGame.ForceReady();
}
/*
================
idMultiplayerGame::DropWeapon
================
*/
void idMultiplayerGame::DropWeapon( int clientNum ) {
assert( !gameLocal.isClient );
idEntity *ent = gameLocal.entities[ clientNum ];
if ( !ent || !ent->IsType( idPlayer::Type ) ) {
return;
}
static_cast< idPlayer* >( ent )->DropWeapon( false );
}
/*
================
idMultiplayerGame::DropWeapon_f
================
*/
void idMultiplayerGame::DropWeapon_f( const idCmdArgs &args ) {
if ( !gameLocal.isMultiplayer ) {
common->Printf( "clientDropWeapon: only valid in multiplayer\n" );
return;
}
idBitMsg outMsg;
byte msgBuf[128];
outMsg.Init( msgBuf, sizeof( msgBuf ) );
outMsg.WriteByte( GAME_RELIABLE_MESSAGE_DROPWEAPON );
networkSystem->ClientSendReliableMessage( outMsg );
}
/*
================
idMultiplayerGame::MessageMode_f
================
*/
void idMultiplayerGame::MessageMode_f( const idCmdArgs &args ) {
gameLocal.mpGame.MessageMode( args );
}
/*
================
idMultiplayerGame::MessageMode
================
*/
void idMultiplayerGame::MessageMode( const idCmdArgs &args ) {
const char *mode;
int imode;
if ( !gameLocal.isMultiplayer ) {
common->Printf( "clientMessageMode: only valid in multiplayer\n" );
return;
}
if ( !mainGui ) {
common->Printf( "no local client\n" );
return;
}
mode = args.Argv( 1 );
if ( !mode[ 0 ] ) {
imode = 0;
} else {
imode = atoi( mode );
}
msgmodeGui->SetStateString( "messagemode", imode ? "1" : "0" );
msgmodeGui->SetStateString( "chattext", "" );
nextMenu = 2;
// let the session know that we want our ingame main menu opened
gameLocal.sessionCommand = "game_startmenu";
}
/*
================
idMultiplayerGame::Vote_f
FIXME: voting from console
================
*/
void idMultiplayerGame::Vote_f( const idCmdArgs &args ) { }
/*
================
idMultiplayerGame::CallVote_f
FIXME: voting from console
================
*/
void idMultiplayerGame::CallVote_f( const idCmdArgs &args ) { }
/*
================
idMultiplayerGame::ServerStartVote
================
*/
void idMultiplayerGame::ServerStartVote( int clientNum, vote_flags_t voteIndex, const char *value ) {
int i;
assert( vote == VOTE_NONE );
// setup
yesVotes = 1;
noVotes = 0;
vote = voteIndex;
voteValue = value;
voteTimeOut = gameLocal.time + 20000;
// mark players allowed to vote - only current ingame players, players joining during vote will be ignored
for ( i = 0; i < gameLocal.numClients; i++ ) {
if ( gameLocal.entities[ i ] && gameLocal.entities[ i ]->IsType( idPlayer::Type ) ) {
playerState[ i ].vote = ( i == clientNum ) ? PLAYER_VOTE_YES : PLAYER_VOTE_WAIT;
} else {
playerState[i].vote = PLAYER_VOTE_NONE;
}
}
}
/*
================
idMultiplayerGame::ClientStartVote
================
*/
void idMultiplayerGame::ClientStartVote( int clientNum, const char *_voteString ) {
idBitMsg outMsg;
byte msgBuf[ MAX_GAME_MESSAGE_SIZE ];
if ( !gameLocal.isClient ) {
outMsg.Init( msgBuf, sizeof( msgBuf ) );
outMsg.WriteByte( GAME_RELIABLE_MESSAGE_STARTVOTE );
outMsg.WriteByte( clientNum );
outMsg.WriteString( _voteString );
networkSystem->ServerSendReliableMessage( -1, outMsg );
}
voteString = _voteString;
AddChatLine( va( common->GetLanguageDict()->GetString( "#str_04279" ), gameLocal.userInfo[ clientNum ].GetString( "ui_name" ) ) );
gameSoundWorld->PlayShaderDirectly( GlobalSoundStrings[ SND_VOTE ] );
if ( clientNum == gameLocal.localClientNum ) {
voted = true;
} else {
voted = false;
}
if ( gameLocal.isClient ) {
// the the vote value to something so the vote line is displayed
vote = VOTE_RESTART;
yesVotes = 1;
noVotes = 0;
}
}
/*
================
idMultiplayerGame::ClientUpdateVote
================
*/
void idMultiplayerGame::ClientUpdateVote( vote_result_t status, int yesCount, int noCount ) {
idBitMsg outMsg;
byte msgBuf[ MAX_GAME_MESSAGE_SIZE ];
if ( !gameLocal.isClient ) {
outMsg.Init( msgBuf, sizeof( msgBuf ) );
outMsg.WriteByte( GAME_RELIABLE_MESSAGE_UPDATEVOTE );
outMsg.WriteByte( status );
outMsg.WriteByte( yesCount );
outMsg.WriteByte( noCount );
networkSystem->ServerSendReliableMessage( -1, outMsg );
}
if ( vote == VOTE_NONE ) {
// clients coming in late don't get the vote start and are not allowed to vote
return;
}
switch ( status ) {
case VOTE_FAILED:
AddChatLine( common->GetLanguageDict()->GetString( "#str_04278" ) );
gameSoundWorld->PlayShaderDirectly( GlobalSoundStrings[ SND_VOTE_FAILED ] );
if ( gameLocal.isClient ) {
vote = VOTE_NONE;
}
break;
case VOTE_PASSED:
AddChatLine( common->GetLanguageDict()->GetString( "#str_04277" ) );
gameSoundWorld->PlayShaderDirectly( GlobalSoundStrings[ SND_VOTE_PASSED ] );
break;
case VOTE_RESET:
if ( gameLocal.isClient ) {
vote = VOTE_NONE;
}
break;
case VOTE_ABORTED:
AddChatLine( common->GetLanguageDict()->GetString( "#str_04276" ) );
if ( gameLocal.isClient ) {
vote = VOTE_NONE;
}
break;
default:
break;
}
if ( gameLocal.isClient ) {
yesVotes = yesCount;
noVotes = noCount;
}
}
/*
================
idMultiplayerGame::ClientCallVote
================
*/
void idMultiplayerGame::ClientCallVote( vote_flags_t voteIndex, const char *voteValue ) {
idBitMsg outMsg;
byte msgBuf[ MAX_GAME_MESSAGE_SIZE ];
// send
outMsg.Init( msgBuf, sizeof( msgBuf ) );
outMsg.WriteByte( GAME_RELIABLE_MESSAGE_CALLVOTE );
outMsg.WriteByte( voteIndex );
outMsg.WriteString( voteValue );
networkSystem->ClientSendReliableMessage( outMsg );
}
/*
================
idMultiplayerGame::CastVote
================
*/
void idMultiplayerGame::CastVote( int clientNum, bool castVote ) {
idBitMsg outMsg;
byte msgBuf[ 128 ];
if ( clientNum == gameLocal.localClientNum ) {
voted = true;
}
if ( gameLocal.isClient ) {
outMsg.Init( msgBuf, sizeof( msgBuf ) );
outMsg.WriteByte( GAME_RELIABLE_MESSAGE_CASTVOTE );
outMsg.WriteByte( castVote );
networkSystem->ClientSendReliableMessage( outMsg );
return;
}
// sanity
if ( vote == VOTE_NONE ) {
gameLocal.ServerSendChatMessage( clientNum, "server", common->GetLanguageDict()->GetString( "#str_04275" ) );
common->DPrintf( "client %d: cast vote while no vote in progress\n", clientNum );
return;
}
if ( playerState[ clientNum ].vote != PLAYER_VOTE_WAIT ) {
gameLocal.ServerSendChatMessage( clientNum, "server", common->GetLanguageDict()->GetString( "#str_04274" ) );
common->DPrintf( "client %d: cast vote - vote %d != PLAYER_VOTE_WAIT\n", clientNum, playerState[ clientNum ].vote );
return;
}
if ( castVote ) {
playerState[ clientNum ].vote = PLAYER_VOTE_YES;
yesVotes++;
} else {
playerState[ clientNum ].vote = PLAYER_VOTE_NO;
noVotes++;
}
ClientUpdateVote( VOTE_UPDATE, yesVotes, noVotes );
}
/*
================
idMultiplayerGame::ServerCallVote
================
*/
void idMultiplayerGame::ServerCallVote( int clientNum, const idBitMsg &msg ) {
vote_flags_t voteIndex;
int vote_timeLimit, vote_fragLimit, vote_clientNum, vote_gameTypeIndex; //, vote_kickIndex;
char value[ MAX_STRING_CHARS ];
assert( clientNum != -1 );
assert( !gameLocal.isClient );
voteIndex = (vote_flags_t)msg.ReadByte( );
msg.ReadString( value, sizeof( value ) );
// sanity checks - setup the vote
if ( vote != VOTE_NONE ) {
gameLocal.ServerSendChatMessage( clientNum, "server", common->GetLanguageDict()->GetString( "#str_04273" ) );
common->DPrintf( "client %d: called vote while voting already in progress - ignored\n", clientNum );
return;
}
switch ( voteIndex ) {
case VOTE_RESTART:
ServerStartVote( clientNum, voteIndex, "" );
ClientStartVote( clientNum, common->GetLanguageDict()->GetString( "#str_04271" ) );
break;
case VOTE_NEXTMAP:
ServerStartVote( clientNum, voteIndex, "" );
ClientStartVote( clientNum, common->GetLanguageDict()->GetString( "#str_04272" ) );
break;
case VOTE_TIMELIMIT:
vote_timeLimit = strtol( value, NULL, 10 );
if ( vote_timeLimit == gameLocal.serverInfo.GetInt( "si_timeLimit" ) ) {
gameLocal.ServerSendChatMessage( clientNum, "server", common->GetLanguageDict()->GetString( "#str_04270" ) );
common->DPrintf( "client %d: already at the voted Time Limit\n", clientNum );
return;
}
if ( vote_timeLimit < si_timeLimit.GetMinValue() || vote_timeLimit > si_timeLimit.GetMaxValue() ) {
gameLocal.ServerSendChatMessage( clientNum, "server", common->GetLanguageDict()->GetString( "#str_04269" ) );
common->DPrintf( "client %d: timelimit value out of range for vote: %s\n", clientNum, value );
return;
}
ServerStartVote( clientNum, voteIndex, value );
ClientStartVote( clientNum, va( common->GetLanguageDict()->GetString( "#str_04268" ), vote_timeLimit ) );
break;
case VOTE_FRAGLIMIT:
vote_fragLimit = strtol( value, NULL, 10 );
if ( vote_fragLimit == gameLocal.serverInfo.GetInt( "si_fragLimit" ) ) {
gameLocal.ServerSendChatMessage( clientNum, "server", common->GetLanguageDict()->GetString( "#str_04267" ) );
common->DPrintf( "client %d: already at the voted Frag Limit\n", clientNum );
return;
}
if ( vote_fragLimit < si_fragLimit.GetMinValue() || vote_fragLimit > si_fragLimit.GetMaxValue() ) {
gameLocal.ServerSendChatMessage( clientNum, "server", common->GetLanguageDict()->GetString( "#str_04266" ) );
common->DPrintf( "client %d: fraglimit value out of range for vote: %s\n", clientNum, value );
return;
}
ServerStartVote( clientNum, voteIndex, value );
ClientStartVote( clientNum, va( common->GetLanguageDict()->GetString( "#str_04303" ), gameLocal.gameType == GAME_LASTMAN ? common->GetLanguageDict()->GetString( "#str_04264" ) : common->GetLanguageDict()->GetString( "#str_04265" ), vote_fragLimit ) );
break;
case VOTE_GAMETYPE:
vote_gameTypeIndex = strtol( value, NULL, 10 );
#ifdef CTF
assert( vote_gameTypeIndex > 0 && vote_gameTypeIndex < GAME_COUNT );
strcpy( value, si_gameTypeArgs[ vote_gameTypeIndex ] );
#endif
/*#ifdef CTF
assert( vote_gameTypeIndex >= 0 && vote_gameTypeIndex <= 4 );
#else
assert( vote_gameTypeIndex >= 0 && vote_gameTypeIndex <= 3 );
#endif
switch ( vote_gameTypeIndex ) {
case 0:
strcpy( value, "Deathmatch" );
break;
case 1:
strcpy( value, "Tourney" );
break;
case 2:
strcpy( value, "Team DM" );
break;
case 3:
strcpy( value, "Last Man" );
break;
#ifdef CTF
case 4:
strcpy( value, "CTF" );
break;
#endif
}*/
if ( !idStr::Icmp( value, gameLocal.serverInfo.GetString( "si_gameType" ) ) ) {
gameLocal.ServerSendChatMessage( clientNum, "server", common->GetLanguageDict()->GetString( "#str_04259" ) );
common->DPrintf( "client %d: already at the voted Game Type\n", clientNum );
return;
}
ServerStartVote( clientNum, voteIndex, value );
ClientStartVote( clientNum, va( common->GetLanguageDict()->GetString( "#str_04258" ), value ) );
break;
case VOTE_KICK:
vote_clientNum = strtol( value, NULL, 10 );
if ( vote_clientNum == gameLocal.localClientNum ) {
gameLocal.ServerSendChatMessage( clientNum, "server", common->GetLanguageDict()->GetString( "#str_04257" ) );
common->DPrintf( "client %d: called kick for the server host\n", clientNum );
return;
}
ServerStartVote( clientNum, voteIndex, va( "%d", vote_clientNum ) );
ClientStartVote( clientNum, va( common->GetLanguageDict()->GetString( "#str_04302" ), vote_clientNum, gameLocal.userInfo[ vote_clientNum ].GetString( "ui_name" ) ) );
break;
case VOTE_MAP: {
if ( idStr::FindText( gameLocal.serverInfo.GetString( "si_map" ), value ) != -1 ) {
gameLocal.ServerSendChatMessage( clientNum, "server", va( common->GetLanguageDict()->GetString( "#str_04295" ), value ) );
common->DPrintf( "client %d: already running the voted map: %s\n", clientNum, value );
return;
}
int num = fileSystem->GetNumMaps();
int i;
const idDict *dict;
bool haveMap = false;
for ( i = 0; i < num; i++ ) {
dict = fileSystem->GetMapDecl( i );
if ( dict && !idStr::Icmp( dict->GetString( "path" ), value ) ) {
haveMap = true;
break;
}
}
if ( !haveMap ) {
gameLocal.ServerSendChatMessage( clientNum, "server", va( common->GetLanguageDict()->GetString( "#str_04296" ), value ) );
common->Printf( "client %d: map not found: %s\n", clientNum, value );
return;
}
ServerStartVote( clientNum, voteIndex, value );
ClientStartVote( clientNum, va( common->GetLanguageDict()->GetString( "#str_04256" ), common->GetLanguageDict()->GetString( dict ? dict->GetString( "name" ) : value ) ) );
break;
}
case VOTE_SPECTATORS:
if ( gameLocal.serverInfo.GetBool( "si_spectators" ) ) {
ServerStartVote( clientNum, voteIndex, "" );
ClientStartVote( clientNum, common->GetLanguageDict()->GetString( "#str_04255" ) );
} else {
ServerStartVote( clientNum, voteIndex, "" );
ClientStartVote( clientNum, common->GetLanguageDict()->GetString( "#str_04254" ) );
}
break;
default:
gameLocal.ServerSendChatMessage( clientNum, "server", va( common->GetLanguageDict()->GetString( "#str_04297" ), (int)voteIndex ) );
common->DPrintf( "client %d: unknown vote index %d\n", clientNum, voteIndex );
}
}
/*
================
idMultiplayerGame::DisconnectClient
================
*/
void idMultiplayerGame::DisconnectClient( int clientNum ) {
if ( lastWinner == clientNum ) {
lastWinner = -1;
}
UpdatePlayerRanks();
CheckAbortGame();
}
/*
================
idMultiplayerGame::CheckAbortGame
================
*/
void idMultiplayerGame::CheckAbortGame( void ) {
int i;
if ( gameLocal.gameType == GAME_TOURNEY && gameState == WARMUP ) {
// if a tourney player joined spectators, let someone else have his spot
for ( i = 0; i < 2; i++ ) {
if ( !gameLocal.entities[ currentTourneyPlayer[ i ] ] || static_cast< idPlayer * >( gameLocal.entities[ currentTourneyPlayer[ i ] ] )->spectating ) {
currentTourneyPlayer[ i ] = -1;
}
}
}
// only checks for aborts -> game review below
if ( gameState != COUNTDOWN && gameState != GAMEON && gameState != SUDDENDEATH ) {
return;
}
switch ( gameLocal.gameType ) {
case GAME_TOURNEY:
for ( i = 0; i < 2; i++ ) {
if ( !gameLocal.entities[ currentTourneyPlayer[ i ] ] || static_cast< idPlayer * >( gameLocal.entities[ currentTourneyPlayer[ i ] ] )->spectating ) {
NewState( GAMEREVIEW );
return;
}
}
break;
default:
if ( !EnoughClientsToPlay() ) {
NewState( GAMEREVIEW );
}
break;
}
}
/*
================
idMultiplayerGame::WantKilled
================
*/
void idMultiplayerGame::WantKilled( int clientNum ) {
idEntity *ent = gameLocal.entities[ clientNum ];
if ( ent && ent->IsType( idPlayer::Type ) ) {
static_cast<idPlayer *>( ent )->Kill( false, false );
}
}
/*
================
idMultiplayerGame::MapRestart
================
*/
void idMultiplayerGame::MapRestart( void ) {
int clientNum;
assert( !gameLocal.isClient );
if ( gameState != WARMUP ) {
NewState( WARMUP );
nextState = INACTIVE;
nextStateSwitch = 0;
}
#ifdef CTF
teamPoints[0] = 0;
teamPoints[1] = 0;
ClearHUDStatus();
#endif
#ifdef CTF
// still balance teams in CTF
if ( g_balanceTDM.GetBool() && lastGameType != GAME_TDM && lastGameType != GAME_CTF && gameLocal.mpGame.IsGametypeTeamBased() ) {
#else
if ( g_balanceTDM.GetBool() && lastGameType != GAME_TDM && gameLocal.gameType == GAME_TDM ) {
#endif
for ( clientNum = 0; clientNum < gameLocal.numClients; clientNum++ ) {
if ( gameLocal.entities[ clientNum ] && gameLocal.entities[ clientNum ]->IsType( idPlayer::Type ) ) {
if ( static_cast< idPlayer* >( gameLocal.entities[ clientNum ] )->BalanceTDM() ) {
// core is in charge of syncing down userinfo changes
// it will also call back game through SetUserInfo with the current info for update
cmdSystem->BufferCommandText( CMD_EXEC_NOW, va( "updateUI %d\n", clientNum ) );
}
}
}
}
lastGameType = gameLocal.gameType;
}
/*
================
idMultiplayerGame::SwitchToTeam
================
*/
void idMultiplayerGame::SwitchToTeam( int clientNum, int oldteam, int newteam ) {
idEntity *ent;
int i;
assert( IsGametypeTeamBased() ); /* CTF */
assert( oldteam != newteam );
assert( !gameLocal.isClient );
if ( !gameLocal.isClient && newteam >= 0 && IsInGame( clientNum ) ) {
PrintMessageEvent( -1, MSG_JOINTEAM, clientNum, newteam );
}
// assign the right teamFragCount
for( i = 0; i < gameLocal.numClients; i++ ) {
if ( i == clientNum ) {
continue;
}
ent = gameLocal.entities[ i ];
if ( ent && ent->IsType( idPlayer::Type ) && static_cast< idPlayer * >(ent)->team == newteam ) {
playerState[ clientNum ].teamFragCount = playerState[ i ].teamFragCount;
break;
}
}
if ( i == gameLocal.numClients ) {
// alone on this team
playerState[ clientNum ].teamFragCount = 0;
}
#ifdef CTF
if ( ( gameState == GAMEON || ( IsGametypeFlagBased() && gameState == SUDDENDEATH ) ) && oldteam != -1 ) {
#else
if ( gameState == GAMEON && oldteam != -1 ) {
#endif
// when changing teams during game, kill and respawn
idPlayer *p = static_cast<idPlayer *>( gameLocal.entities[ clientNum ] );
if ( p->IsInTeleport() ) {
p->ServerSendEvent( idPlayer::EVENT_ABORT_TELEPORTER, NULL, false, -1 );
p->SetPrivateCameraView( NULL );
}
p->Kill( true, true );
#ifdef CTF
if ( IsGametypeFlagBased() )
p->DropFlag();
#endif
CheckAbortGame();
}
#ifdef CTF
else if ( IsGametypeFlagBased() && oldteam != -1 ) {
idPlayer *p = static_cast<idPlayer *>( gameLocal.entities[ clientNum ] );
p->DropFlag();
}
#endif
}
/*
================
idMultiplayerGame::ProcessChatMessage
================
*/
void idMultiplayerGame::ProcessChatMessage( int clientNum, bool team, const char *name, const char *text, const char *sound ) {
idBitMsg outMsg;
byte msgBuf[ 256 ];
const char *prefix = NULL;
int send_to; // 0 - all, 1 - specs, 2 - team
int i;
idEntity *ent;
idPlayer *p;
idStr prefixed_name;
assert( !gameLocal.isClient );
if ( clientNum >= 0 ) {
p = static_cast< idPlayer * >( gameLocal.entities[ clientNum ] );
if ( !( p && p->IsType( idPlayer::Type ) ) ) {
return;
}
if ( p->spectating ) {
prefix = "spectating";
if ( team || ( !g_spectatorChat.GetBool() && ( gameState == GAMEON || gameState == SUDDENDEATH ) ) ) {
// to specs
send_to = 1;
} else {
// to all
send_to = 0;
}
} else if ( team ) {
prefix = "team";
// to team
send_to = 2;
} else {
// to all
send_to = 0;
}
} else {
p = NULL;
send_to = 0;
}
// put the message together
outMsg.Init( msgBuf, sizeof( msgBuf ) );
outMsg.WriteByte( GAME_RELIABLE_MESSAGE_CHAT );
if ( prefix ) {
prefixed_name = va( "(%s) %s", prefix, name );
} else {
prefixed_name = name;
}
outMsg.WriteString( prefixed_name );
outMsg.WriteString( text, -1, false );
if ( !send_to ) {
AddChatLine( "%s^0: %s\n", prefixed_name.c_str(), text );
networkSystem->ServerSendReliableMessage( -1, outMsg );
if ( sound ) {
PlayGlobalSound( -1, SND_COUNT, sound );
}
} else {
for ( i = 0; i < gameLocal.numClients; i++ ) {
ent = gameLocal.entities[ i ];
if ( !ent || !ent->IsType( idPlayer::Type ) ) {
continue;
}
if ( send_to == 1 && static_cast< idPlayer * >( ent )->spectating ) {
if ( sound ) {
PlayGlobalSound( i, SND_COUNT, sound );
}
if ( i == gameLocal.localClientNum ) {
AddChatLine( "%s^0: %s\n", prefixed_name.c_str(), text );
} else {
networkSystem->ServerSendReliableMessage( i, outMsg );
}
} else if ( send_to == 2 && static_cast< idPlayer * >( ent )->team == p->team ) {
if ( sound ) {
PlayGlobalSound( i, SND_COUNT, sound );
}
if ( i == gameLocal.localClientNum ) {
AddChatLine( "%s^0: %s\n", prefixed_name.c_str(), text );
} else {
networkSystem->ServerSendReliableMessage( i, outMsg );
}
}
}
}
}
/*
================
idMultiplayerGame::Precache
================
*/
void idMultiplayerGame::Precache( void ) {
int i;
idFile *f;
if ( !gameLocal.isMultiplayer ) {
return;
}
gameLocal.FindEntityDefDict( "player_doommarine", false );;
// skins
idStr str = cvarSystem->GetCVarString( "mod_validSkins" );
idStr skin;
while ( str.Length() ) {
int n = str.Find( ";" );
if ( n >= 0 ) {
skin = str.Left( n );
str = str.Right( str.Length() - n - 1 );
} else {
skin = str;
str = "";
}
declManager->FindSkin( skin, false );
}
for ( i = 0; ui_skinArgs[ i ]; i++ ) {
declManager->FindSkin( ui_skinArgs[ i ], false );
}
// MP game sounds
for ( i = 0; i < SND_COUNT; i++ ) {
f = fileSystem->OpenFileRead( GlobalSoundStrings[ i ] );
fileSystem->CloseFile( f );
}
// MP guis. just make sure we hit all of them
i = 0;
while ( MPGuis[ i ] ) {
uiManager->FindGui( MPGuis[ i ], true );
i++;
}
}
/*
================
idMultiplayerGame::ToggleSpectate
================
*/
void idMultiplayerGame::ToggleSpectate( void ) {
bool spectating;
assert( gameLocal.isClient || gameLocal.localClientNum == 0 );
spectating = ( idStr::Icmp( cvarSystem->GetCVarString( "ui_spectate" ), "Spectate" ) == 0 );
if ( spectating ) {
// always allow toggling to play
cvarSystem->SetCVarString( "ui_spectate", "Play" );
} else {
// only allow toggling to spectate if spectators are enabled.
if ( gameLocal.serverInfo.GetBool( "si_spectators" ) ) {
cvarSystem->SetCVarString( "ui_spectate", "Spectate" );
} else {
gameLocal.mpGame.AddChatLine( common->GetLanguageDict()->GetString( "#str_06747" ) );
}
}
}
/*
================
idMultiplayerGame::ToggleReady
================
*/
void idMultiplayerGame::ToggleReady( void ) {
bool ready;
assert( gameLocal.isClient || gameLocal.localClientNum == 0 );
ready = ( idStr::Icmp( cvarSystem->GetCVarString( "ui_ready" ), "Ready" ) == 0 );
if ( ready ) {
cvarSystem->SetCVarString( "ui_ready", "Not Ready" );
} else {
cvarSystem->SetCVarString( "ui_ready", "Ready" );
}
}
/*
================
idMultiplayerGame::ToggleTeam
================
*/
void idMultiplayerGame::ToggleTeam( void ) {
bool team;
assert( gameLocal.isClient || gameLocal.localClientNum == 0 );
team = ( idStr::Icmp( cvarSystem->GetCVarString( "ui_team" ), "Red" ) == 0 );
if ( team ) {
cvarSystem->SetCVarString( "ui_team", "Blue" );
} else {
cvarSystem->SetCVarString( "ui_team", "Red" );
}
}
/*
================
idMultiplayerGame::ToggleUserInfo
================
*/
void idMultiplayerGame::ThrottleUserInfo( void ) {
int i;
assert( gameLocal.localClientNum >= 0 );
i = 0;
while ( ThrottleVars[ i ] ) {
if ( idStr::Icmp( gameLocal.userInfo[ gameLocal.localClientNum ].GetString( ThrottleVars[ i ] ),
cvarSystem->GetCVarString( ThrottleVars[ i ] ) ) ) {
if ( gameLocal.realClientTime < switchThrottle[ i ] ) {
AddChatLine( common->GetLanguageDict()->GetString( "#str_04299" ), common->GetLanguageDict()->GetString( ThrottleVarsInEnglish[ i ] ), ( switchThrottle[ i ] - gameLocal.time ) / 1000 + 1 );
cvarSystem->SetCVarString( ThrottleVars[ i ], gameLocal.userInfo[ gameLocal.localClientNum ].GetString( ThrottleVars[ i ] ) );
} else {
switchThrottle[ i ] = gameLocal.time + ThrottleDelay[ i ] * 1000;
}
}
i++;
}
}
/*
================
idMultiplayerGame::CanPlay
================
*/
bool idMultiplayerGame::CanPlay( idPlayer *p ) {
return !p->wantSpectate && playerState[ p->entityNumber ].ingame;
}
/*
================
idMultiplayerGame::EnterGame
================
*/
void idMultiplayerGame::EnterGame( int clientNum ) {
assert( !gameLocal.isClient );
if ( !playerState[ clientNum ].ingame ) {
playerState[ clientNum ].ingame = true;
if ( gameLocal.isMultiplayer ) {
// can't use PrintMessageEvent as clients don't know the nickname yet
gameLocal.ServerSendChatMessage( -1, common->GetLanguageDict()->GetString( "#str_02047" ), va( common->GetLanguageDict()->GetString( "#str_07177" ), gameLocal.userInfo[ clientNum ].GetString( "ui_name" ) ) );
}
}
}
/*
================
idMultiplayerGame::WantRespawn
================
*/
bool idMultiplayerGame::WantRespawn( idPlayer *p ) {
return p->forceRespawn && !p->wantSpectate && playerState[ p->entityNumber ].ingame;
}
/*
================
idMultiplayerGame::VoiceChat
================
*/
void idMultiplayerGame::VoiceChat_f( const idCmdArgs &args ) {
gameLocal.mpGame.VoiceChat( args, false );
}
/*
================
idMultiplayerGame::VoiceChatTeam
================
*/
void idMultiplayerGame::VoiceChatTeam_f( const idCmdArgs &args ) {
gameLocal.mpGame.VoiceChat( args, true );
}
/*
================
idMultiplayerGame::VoiceChat
================
*/
void idMultiplayerGame::VoiceChat( const idCmdArgs &args, bool team ) {
idBitMsg outMsg;
byte msgBuf[128];
const char *voc;
const idDict *spawnArgs;
const idKeyValue *keyval;
int index;
if ( !gameLocal.isMultiplayer ) {
common->Printf( "clientVoiceChat: only valid in multiplayer\n" );
return;
}
if ( args.Argc() != 2 ) {
common->Printf( "clientVoiceChat: bad args\n" );
return;
}
// throttle
if ( gameLocal.realClientTime < voiceChatThrottle ) {
return;
}
voc = args.Argv( 1 );
spawnArgs = gameLocal.FindEntityDefDict( "player_doommarine", false );
keyval = spawnArgs->MatchPrefix( "snd_voc_", NULL );
index = 0;
while ( keyval ) {
if ( !keyval->GetValue().Icmp( voc ) ) {
break;
}
keyval = spawnArgs->MatchPrefix( "snd_voc_", keyval );
index++;
}
if ( !keyval ) {
common->Printf( "Voice command not found: %s\n", voc );
return;
}
voiceChatThrottle = gameLocal.realClientTime + 1000;
outMsg.Init( msgBuf, sizeof( msgBuf ) );
outMsg.WriteByte( GAME_RELIABLE_MESSAGE_VCHAT );
outMsg.WriteLong( index );
outMsg.WriteBits( team ? 1 : 0, 1 );
networkSystem->ClientSendReliableMessage( outMsg );
}
/*
================
idMultiplayerGame::ProcessVoiceChat
================
*/
void idMultiplayerGame::ProcessVoiceChat( int clientNum, bool team, int index ) {
const idDict *spawnArgs;
const idKeyValue *keyval;
idStr name;
idStr snd_key;
idStr text_key;
idPlayer *p;
p = static_cast< idPlayer * >( gameLocal.entities[ clientNum ] );
if ( !( p && p->IsType( idPlayer::Type ) ) ) {
return;
}
if ( p->spectating ) {
return;
}
// lookup the sound def
spawnArgs = gameLocal.FindEntityDefDict( "player_doommarine", false );
keyval = spawnArgs->MatchPrefix( "snd_voc_", NULL );
while ( index > 0 && keyval ) {
keyval = spawnArgs->MatchPrefix( "snd_voc_", keyval );
index--;
}
if ( !keyval ) {
common->DPrintf( "ProcessVoiceChat: unknown chat index %d\n", index );
return;
}
snd_key = keyval->GetKey();
name = gameLocal.userInfo[ clientNum ].GetString( "ui_name" );
sprintf( text_key, "txt_%s", snd_key.Right( snd_key.Length() - 4 ).c_str() );
if ( team || gameState == COUNTDOWN || gameState == GAMEREVIEW ) {
ProcessChatMessage( clientNum, team, name, spawnArgs->GetString( text_key ), spawnArgs->GetString( snd_key ) );
} else {
p->StartSound( snd_key, SND_CHANNEL_ANY, 0, true, NULL );
ProcessChatMessage( clientNum, team, name, spawnArgs->GetString( text_key ), NULL );
}
}
/*
================
idMultiplayerGame::ServerWriteInitialReliableMessages
================
*/
void idMultiplayerGame::ServerWriteInitialReliableMessages( int clientNum ) {
idBitMsg outMsg;
byte msgBuf[ MAX_GAME_MESSAGE_SIZE ];
int i;
idEntity *ent;
outMsg.Init( msgBuf, sizeof( msgBuf ) );
outMsg.BeginWriting();
outMsg.WriteByte( GAME_RELIABLE_MESSAGE_STARTSTATE );
// send the game state and start time
outMsg.WriteByte( gameState );
outMsg.WriteLong( matchStartedTime );
outMsg.WriteShort( startFragLimit );
// send the powerup states and the spectate states
for( i = 0; i < gameLocal.numClients; i++ ) {
ent = gameLocal.entities[ i ];
if ( i != clientNum && ent && ent->IsType( idPlayer::Type ) ) {
outMsg.WriteShort( i );
outMsg.WriteShort( static_cast< idPlayer * >( ent )->inventory.powerups );
outMsg.WriteBits( static_cast< idPlayer * >( ent )->spectating, 1 );
}
}
outMsg.WriteShort( MAX_CLIENTS );
networkSystem->ServerSendReliableMessage( clientNum, outMsg );
// we send SI in connectResponse messages, but it may have been modified already
outMsg.BeginWriting( );
outMsg.WriteByte( GAME_RELIABLE_MESSAGE_SERVERINFO );
outMsg.WriteDeltaDict( gameLocal.serverInfo, NULL );
networkSystem->ServerSendReliableMessage( clientNum, outMsg );
// warmup time
if ( gameState == COUNTDOWN ) {
outMsg.BeginWriting();
outMsg.WriteByte( GAME_RELIABLE_MESSAGE_WARMUPTIME );
outMsg.WriteLong( warmupEndTime );
networkSystem->ServerSendReliableMessage( clientNum, outMsg );
}
}
/*
================
idMultiplayerGame::ClientReadStartState
================
*/
void idMultiplayerGame::ClientReadStartState( const idBitMsg &msg ) {
int i, client, powerup;
// read the state in preparation for reading snapshot updates
gameState = (idMultiplayerGame::gameState_t)msg.ReadByte();
matchStartedTime = msg.ReadLong( );
startFragLimit = msg.ReadShort( );
while ( ( client = msg.ReadShort() ) != MAX_CLIENTS ) {
assert( gameLocal.entities[ client ] && gameLocal.entities[ client ]->IsType( idPlayer::Type ) );
powerup = msg.ReadShort();
for ( i = 0; i < MAX_POWERUPS; i++ ) {
if ( powerup & ( 1 << i ) ) {
static_cast< idPlayer * >( gameLocal.entities[ client ] )->GivePowerUp( i, 0 );
}
}
bool spectate = ( msg.ReadBits( 1 ) != 0 );
static_cast< idPlayer * >( gameLocal.entities[ client ] )->Spectate( spectate );
}
}
/*
================
idMultiplayerGame::ClientReadWarmupTime
================
*/
void idMultiplayerGame::ClientReadWarmupTime( const idBitMsg &msg ) {
warmupEndTime = msg.ReadLong();
}
/*
#ifdef CTF
Threewave note:
The below IsGametype...() functions were implemented for CTF,
but we did not #ifdef CTF them, because doing so would clutter
the codebase substantially. Please consider them part of the merged
CTF code.
*/
/*
================
idMultiplayerGame::IsGametypeTeamBased
================
*/
bool idMultiplayerGame::IsGametypeTeamBased( void ) /* CTF */
{
switch ( gameLocal.gameType )
{
case GAME_SP:
case GAME_DM:
case GAME_TOURNEY:
case GAME_LASTMAN:
return false;
#ifdef CTF
case GAME_CTF:
#endif
case GAME_TDM:
return true;
default:
assert( !"Add support for your new gametype here." );
}
return false;
}
/*
================
idMultiplayerGame::IsGametypeFlagBased
================
*/
bool idMultiplayerGame::IsGametypeFlagBased( void ) {
switch ( gameLocal.gameType )
{
case GAME_SP:
case GAME_DM:
case GAME_TOURNEY:
case GAME_LASTMAN:
case GAME_TDM:
return false;
#ifdef CTF
case GAME_CTF:
return true;
#endif
default:
assert( !"Add support for your new gametype here." );
}
return false;
}
#ifdef CTF
/*
================
idMultiplayerGame::GetTeamFlag
================
*/
idItemTeam * idMultiplayerGame::GetTeamFlag( int team ) {
assert( team == 0 || team == 1 );
if ( !IsGametypeFlagBased() || ( team != 0 && team != 1 ) ) /* CTF */
return NULL;
// TODO : just call on map start
FindTeamFlags();
return teamFlags[team];
}
/*
================
idMultiplayerGame::GetTeamFlag
================
*/
void idMultiplayerGame::FindTeamFlags( void ) {
const char * flagDefs[2] =
{
"team_CTF_redflag",
"team_CTF_blueflag"
};
for ( int i = 0; i < 2; i++)
{
idEntity * entity = gameLocal.FindEntityUsingDef( NULL, flagDefs[i] );
do
{
if ( entity == NULL )
return;
idItemTeam * flag = static_cast<idItemTeam *>(entity);
if ( flag->team == i )
{
teamFlags[i] = flag;
break;
}
entity = gameLocal.FindEntityUsingDef( entity, flagDefs[i] );
} while( entity );
}
}
/*
================
idMultiplayerGame::GetFlagStatus
================
*/
flagStatus_t idMultiplayerGame::GetFlagStatus( int team ) {
//assert( IsGametypeFlagBased() );
idItemTeam *teamFlag = GetTeamFlag( team );
//assert( teamFlag != NULL );
if ( teamFlag != NULL ) {
if ( teamFlag->carried == false && teamFlag->dropped == false )
return FLAGSTATUS_INBASE;
if ( teamFlag->carried == true )
return FLAGSTATUS_TAKEN;
if ( teamFlag->carried == false && teamFlag->dropped == true )
return FLAGSTATUS_STRAY;
}
//assert( !"Invalid flag state." );
return FLAGSTATUS_NONE;
}
/*
================
idMultiplayerGame::SetFlagMsgs
================
*/
void idMultiplayerGame::SetFlagMsg( bool b ) {
flagMsgOn = b;
}
/*
================
idMultiplayerGame::IsFlagMsgOn
================
*/
bool idMultiplayerGame::IsFlagMsgOn( void ) {
return ( GetGameState() == WARMUP || GetGameState() == GAMEON || GetGameState() == SUDDENDEATH ) && flagMsgOn;
}
/*
================
idMultiplayerGame::SetBestGametype
================
*/
void idMultiplayerGame::SetBestGametype( const char * map ) {
const char *gametype = gameLocal.serverInfo.GetString( "si_gameType" );
// const char *map = gameLocal.serverInfo.GetString( "si_map" );
int num = declManager->GetNumDecls( DECL_MAPDEF );
int i, j;
for ( i = 0; i < num; i++ ) {
const idDeclEntityDef *mapDef = static_cast<const idDeclEntityDef *>( declManager->DeclByIndex( DECL_MAPDEF, i ) );
if ( mapDef && idStr::Icmp( mapDef->GetName(), map ) == 0 ) {
if ( mapDef->dict.GetBool( gametype ) ) {
// dont change gametype
return;
}
for ( j = 1; si_gameTypeArgs[ j ]; j++ ) {
if ( mapDef->dict.GetBool( si_gameTypeArgs[ j ] ) ) {
si_gameType.SetString( si_gameTypeArgs[ j ] );
return;
}
}
// error out, no valid gametype
return;
}
}
}
/*
================
idMultiplayerGame::ReloadScoreboard
================
*/
void idMultiplayerGame::ReloadScoreboard() {
// CTF uses its own scoreboard
if ( IsGametypeFlagBased() )
scoreBoard = uiManager->FindGui( "guis/ctfscoreboard.gui", true, false, true );
else
scoreBoard = uiManager->FindGui( "guis/scoreboard.gui", true, false, true );
Precache();
}
#endif
#ifdef _D3XP
idStr idMultiplayerGame::GetBestGametype( const char* map, const char* gametype ) {
int num = declManager->GetNumDecls( DECL_MAPDEF );
int i, j;
for ( i = 0; i < num; i++ ) {
const idDeclEntityDef *mapDef = static_cast<const idDeclEntityDef *>( declManager->DeclByIndex( DECL_MAPDEF, i ) );
if ( mapDef && idStr::Icmp( mapDef->GetName(), map ) == 0 ) {
if ( mapDef->dict.GetBool( gametype ) ) {
// dont change gametype
return gametype;
}
for ( j = 1; si_gameTypeArgs[ j ]; j++ ) {
if ( mapDef->dict.GetBool( si_gameTypeArgs[ j ] ) ) {
return si_gameTypeArgs[ j ];
}
}
// error out, no valid gametype
return "deathmatch";
}
}
//For testing a new map let it play any gametpye
return gametype;
}
#endif