123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189 |
- /*
- ===========================================================================
- Doom 3 BFG Edition GPL Source Code
- Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
- This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
- Doom 3 BFG Edition 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 BFG Edition 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 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
- In addition, the Doom 3 BFG Edition 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 BFG Edition 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 "../idlib/precompiled.h"
- #pragma hdrstop
- #include "Game_local.h"
- // could be a problem if players manage to go down sudden deaths till this .. oh well
- #define LASTMAN_NOLIVES -20
- extern idCVar ui_skinIndex;
- const char * idMultiplayerGame::teamNames[] = { "#str_02499", "#str_02500" };
- const char * idMultiplayerGame::skinNames[] = {
- "skins/characters/player/marine_mp",
- "skins/characters/player/marine_mp_red",
- "skins/characters/player/marine_mp_blue",
- "skins/characters/player/marine_mp_green",
- "skins/characters/player/marine_mp_yellow",
- "skins/characters/player/marine_mp_purple",
- "skins/characters/player/marine_mp_grey",
- "skins/characters/player/marine_mp_orange"
- };
- const idVec3 idMultiplayerGame::skinColors[] = {
- idVec3( 0.25f, 0.25f, 0.25f ), // light grey
- idVec3( 1.00f, 0.00f, 0.00f ), // red
- idVec3( 0.20f, 0.50f, 0.80f ), // blue
- idVec3( 0.00f, 0.80f, 0.10f ), // green
- idVec3( 1.00f, 0.80f, 0.10f ), // yellow
- idVec3( 0.39f, 0.199f, 0.3f ), // purple
- idVec3( 0.425f, 0.484f, 0.445f ), // dark grey
- idVec3( 0.484f, 0.312f, 0.074f) // orange
- };
- const int idMultiplayerGame::numSkins = sizeof( idMultiplayerGame::skinNames ) / sizeof( idMultiplayerGame::skinNames[0] );
- const char * idMultiplayerGame::GetTeamName( int team ) const { return teamNames[idMath::ClampInt( 0, 1, team )]; }
- const char * idMultiplayerGame::GetSkinName( int skin ) const { return skinNames[idMath::ClampInt( 0, numSkins-1, skin )]; }
- const idVec3 & idMultiplayerGame::GetSkinColor( int skin ) const { return skinColors[idMath::ClampInt( 0, numSkins-1, skin )]; }
- // make CTF not included in game modes for consoles
- const char * gameTypeNames_WithCTF[] = { "Deathmatch", "Tourney", "Team DM", "Last Man", "CTF", "" };
- const char * gameTypeDisplayNames_WithCTF[] = { "#str_04260", "#str_04261", "#str_04262", "#str_04263", "#str_swf_mode_ctf", "" };
- const char * gameTypeNames_WithoutCTF[] = { "Deathmatch", "Tourney", "Team DM", "Last Man", "" };
- const char * gameTypeDisplayNames_WithoutCTF[] = { "#str_04260", "#str_04261", "#str_04262", "#str_04263", "" };
- compile_time_assert( GAME_DM == 0 );
- compile_time_assert( GAME_TOURNEY == 1 );
- compile_time_assert( GAME_TDM == 2 );
- compile_time_assert( GAME_LASTMAN == 3 );
- compile_time_assert( GAME_CTF == 4 );
- 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/vo/feedback/voc_youwin.wav",
- "sound/vo/feedback/voc_youlose.wav",
- "sound/vo/feedback/fight.wav",
- "sound/vo/feedback/three.wav",
- "sound/vo/feedback/two.wav",
- "sound/vo/feedback/one.wav",
- "sound/vo/feedback/sudden_death.wav",
- "sound/vo/ctf/flag_capped_yours.wav",
- "sound/vo/ctf/flag_capped_theirs.wav",
- "sound/vo/ctf/flag_return.wav",
- "sound/vo/ctf/flag_taken_yours.wav",
- "sound/vo/ctf/flag_taken_theirs.wav",
- "sound/vo/ctf/flag_dropped_yours.wav",
- "sound/vo/ctf/flag_dropped_theirs.wav"
- };
- // handy verbose
- const char *idMultiplayerGame::GameStateStrings[] = {
- "INACTIVE",
- "WARMUP",
- "COUNTDOWN",
- "GAMEON",
- "SUDDENDEATH",
- "GAMEREVIEW",
- "NEXTGAME"
- };
- /*
- ================
- idMultiplayerGame::idMultiplayerGame
- ================
- */
- idMultiplayerGame::idMultiplayerGame() {
- teamFlags[0] = NULL;
- teamFlags[1] = NULL;
- teamPoints[0] = 0;
- teamPoints[1] = 0;
- flagMsgOn = true;
- player_blue_flag = -1;
- player_red_flag = -1;
-
- Clear();
- scoreboardManager = NULL;
- }
- /*
- ================
- idMultiplayerGame::Shutdown
- ================
- */
- void idMultiplayerGame::Shutdown() {
- Clear();
- }
- /*
- ================
- idMultiplayerGame::Reset
- ================
- */
- void idMultiplayerGame::Reset() {
- Clear();
- ClearChatData();
- if ( common->IsMultiplayer() ) {
- scoreboardManager = new idMenuHandler_Scoreboard();
- if ( scoreboardManager != NULL ) {
- scoreboardManager->Initialize( "scoreboard", common->SW() );
- }
- }
- ClearChatData();
- warmupEndTime = 0;
- lastChatLineTime = 0;
- }
- /*
- ================
- idMultiplayerGame::ServerClientConnect
- ================
- */
- void idMultiplayerGame::ServerClientConnect( int clientNum ) {
- memset( &playerState[ clientNum ], 0, sizeof( playerState[ clientNum ] ) );
- }
- /*
- ================
- idMultiplayerGame::SpawnPlayer
- ================
- */
- void idMultiplayerGame::SpawnPlayer( int clientNum ) {
- idLobbyBase & lobby = session->GetActingGameStateLobbyBase();
- lobbyUserID_t & lobbyUserID = gameLocal.lobbyUserIDs[clientNum];
- AddChatLine( idLocalization::GetString( "#str_07177" ), lobby.GetLobbyUserName( lobbyUserID ) );
- memset( &playerState[ clientNum ], 0, sizeof( playerState[ clientNum ] ) );
- if ( !common->IsClient() ) {
- if ( gameLocal.gameType == GAME_LASTMAN ) {
- // Players spawn with no lives to prevent them from getting the clean slate achievement if
- // they didn't earn it.
- playerState[ clientNum ].fragCount = LASTMAN_NOLIVES;
- }
- idPlayer *p = static_cast< idPlayer * >( gameLocal.entities[ clientNum ] );
- p->spawnedTime = gameLocal.serverTime;
- p->team = 0;
- p->tourneyRank = 0;
- if ( gameLocal.gameType == GAME_TOURNEY && gameState == GAMEON ) {
- p->tourneyRank++;
- }
- }
- }
- /*
- ================
- idMultiplayerGame::Clear
- ================
- */
- void idMultiplayerGame::Clear() {
- int i;
- gameState = INACTIVE;
- nextState = INACTIVE;
- nextStateSwitch = 0;
- matchStartedTime = 0;
- currentTourneyPlayer[ 0 ] = -1;
- currentTourneyPlayer[ 1 ] = -1;
- one = two = three = false;
- memset( &playerState, 0 , sizeof( playerState ) );
- lastWinner = -1;
- pureReady = false;
-
- CleanupScoreboard();
- fragLimitTimeout = 0;
- voiceChatThrottle = 0;
- for ( i = 0; i < NUM_CHAT_NOTIFY; i++ ) {
- chatHistory[ i ].line.Clear();
- }
- startFragLimit = -1;
- }
- /*
- ================
- idMultiplayerGame::Clear
- ================
- */
- void idMultiplayerGame::CleanupScoreboard() {
- if ( scoreboardManager != NULL ) {
- delete scoreboardManager;
- scoreboardManager = NULL;
- }
- }
- /*
- ================
- 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 ];
- }
- /*
- ================
- 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::IsScoreboardActive
- ================
- */
- bool idMultiplayerGame::IsScoreboardActive() {
- if ( scoreboardManager != NULL ) {
- return scoreboardManager->IsActive();
- }
- return false;
- }
- /*
- ================
- idMultiplayerGame::HandleGuiEvent
- ================
- */
- bool idMultiplayerGame::HandleGuiEvent( const sysEvent_t * sev ) {
- if ( scoreboardManager != NULL && scoreboardManager->IsActive() ) {
- scoreboardManager->HandleGuiEvent( sev );
- return true;
- }
- return false;
- }
- /*
- ================
- idMultiplayerGame::UpdateScoreboard
- ================
- */
- void idMultiplayerGame::UpdateScoreboard( idMenuHandler_Scoreboard * scoreboard, idPlayer *owner ) {
- if ( owner == NULL ) {
- return;
- }
- int redScore = 0;
- int blueScore = 0;
- idLobbyBase & lobby = session->GetActingGameStateLobbyBase();
- idList< mpScoreboardInfo > scoreboardInfo;
- for ( int i = 0; i < MAX_CLIENTS; ++i ) {
- if ( i < gameLocal.numClients ) {
- idEntity *ent = gameLocal.entities[ i ];
- if ( !ent || !ent->IsType( idPlayer::Type ) ) {
- continue;
- }
- idPlayer *player = static_cast<idPlayer *>(ent);
- if ( !player ) {
- continue;
- }
- idStr spectateData;
- idStr playerName;
- int score = 0;
- int wins = 0;
- int ping = 0;
- int playerNum = player->entityNumber;
- int team = - 1;
- lobbyUserID_t & lobbyUserID = gameLocal.lobbyUserIDs[ playerNum ];
- if ( IsGametypeTeamBased() ) {
- team = player->team;
- if ( team == 0 ) {
- redScore = playerState[ playerNum ].teamFragCount;
- } else if ( team == 1 ) {
- blueScore = playerState[ playerNum ].teamFragCount;
- }
- }
- score = idMath::ClampInt( MP_PLAYER_MINFRAGS, MP_PLAYER_MAXFRAGS, playerState[ playerNum ].fragCount );
- // HACK -
- if( gameLocal.gameType == GAME_LASTMAN && score == LASTMAN_NOLIVES ) {
- score = 0;
- }
- wins = idMath::ClampInt( 0, MP_PLAYER_MAXWINS, playerState[ playerNum ].wins );
- ping = playerState[ playerNum ].ping;
- if ( gameState == WARMUP ) {
- if ( player->spectating ) {
- spectateData = idLocalization::GetString( "#str_04246" );
- } else {
- spectateData = idLocalization::GetString( "#str_04247" );
- }
- } else {
- if ( gameLocal.gameType == GAME_LASTMAN && playerState[ playerNum ].fragCount == LASTMAN_NOLIVES ) {
- spectateData = idLocalization::GetString( "#str_06736" );
- } else if ( player->spectating ) {
- spectateData = idLocalization::GetString( "#str_04246" );
- }
- }
- if ( playerNum == owner->entityNumber ) {
- playerName = "^3";
- playerName.Append( lobby.GetLobbyUserName( lobbyUserID ) );
- playerName.Append( "^0" );
- } else {
- playerName = lobby.GetLobbyUserName( lobbyUserID );
- }
- mpScoreboardInfo info;
- info.voiceState = session->GetDisplayStateFromVoiceState( session->GetLobbyUserVoiceState( lobbyUserID ) );
- info.name = playerName;
- info.team = team;
- info.score = score;
- info.wins = wins;
- info.ping = ping;
- info.playerNum = playerNum;
- info.spectateData = spectateData;
- bool added = false;
- for ( int i = 0; i < scoreboardInfo.Num(); ++i ) {
- if ( info.team == scoreboardInfo[i].team ) {
- if ( info.score > scoreboardInfo[i].score ) {
- scoreboardInfo.Insert( info, i );
- added = true;
- break;
- }
- } else if ( info.team < scoreboardInfo[i].team ) {
- scoreboardInfo.Insert( info, i );
- added = true;
- break;
- }
- }
- if ( !added ) {
- scoreboardInfo.Append( info );
- }
- }
- }
- idStr gameInfo;
- if ( gameState == GAMEREVIEW ) {
- int timeRemaining = nextStateSwitch - gameLocal.serverTime;
- int ms = (int) ceilf( timeRemaining / 1000.0f );
- if ( ms == 1 ) {
- gameInfo = idLocalization::GetString( "#str_online_game_starts_in_second" );
- gameInfo.Replace( "<DNT_VAL>", idStr( ms ) );
- } else if ( ms > 0 && ms < 30 ) {
- gameInfo = idLocalization::GetString( "#str_online_game_starts_in_seconds" );
- gameInfo.Replace( "<DNT_VAL>", idStr( ms ) );
- }
- } else {
- if ( gameLocal.gameType == GAME_LASTMAN ) {
- if ( gameState == GAMEON || gameState == SUDDENDEATH ) {
- gameInfo = va( "%s: %i", idLocalization::GetString( "#str_04264" ), startFragLimit );
- } else {
- gameInfo = va( "%s: %i", idLocalization::GetString( "#str_04264" ), gameLocal.serverInfo.GetInt( "si_fragLimit" ) );
- }
- } else if ( gameLocal.gameType == GAME_CTF ) {
- int captureLimit = gameLocal.serverInfo.GetInt( "si_fragLimit" );
- gameInfo = va( idLocalization::GetString( "#str_11108" ), captureLimit );
- } else {
- gameInfo = va( "%s: %i", idLocalization::GetString( "#str_01982" ), gameLocal.serverInfo.GetInt( "si_fragLimit" ) );
- }
- if ( gameLocal.serverInfo.GetInt( "si_timeLimit" ) > 0 ) {
- gameInfo.Append( va( " %s: %i", idLocalization::GetString( "#str_01983" ), gameLocal.serverInfo.GetInt( "si_timeLimit" ) ) );
- }
- }
- if ( scoreboardManager ) {
- if ( IsGametypeFlagBased() ) {
- scoreboardManager->SetTeamScores( GetFlagPoints( 0 ), GetFlagPoints( 1 ) );
- } else if ( IsGametypeTeamBased() ) {
- scoreboardManager->SetTeamScores( redScore, blueScore );
- }
- scoreboardManager->UpdateScoreboard( scoreboardInfo, gameInfo );
- scoreboardManager->UpdateScoreboardSelection();
- scoreboardManager->Update();
- }
- }
- /*
- ================
- idMultiplayerGame::GameTime
- ================
- */
- const char *idMultiplayerGame::GameTime() {
- static char buff[16];
- int m, s, t, ms;
- if ( gameState == COUNTDOWN ) {
- ms = warmupEndTime - gameLocal.serverTime;
- // we never want to show double dashes.
- if( ms <= 0 ) {
- // Try to setup time again.
- warmupEndTime = gameLocal.serverTime + 1000*cvarSystem->GetCVarInteger( "g_countDown" );
- ms = warmupEndTime - gameLocal.serverTime;
- }
- 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.serverTime - matchStartedTime );
- } else {
- ms = gameLocal.serverTime - 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 teamCount[ 2 ];
- int clients = NumActualClients( false, teamCount );
- if ( IsGametypeTeamBased() ) { /* CTF */
- return clients >= 2 && teamCount[ 0 ] && teamCount[ 1 ];
- } else {
- return clients >= 2;
- }
- }
- /*
- ================
- 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;
- if ( IsGametypeFlagBased() ) /* CTF */
- return NULL;
- 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.serverTime >= matchStartedTime + timeLimit * 60000 ) {
- return true;
- }
- }
- return false;
- }
- /*
- ================
- idMultiplayerGame::WinningTeam
- return winning team
- -1 if tied or no players
- ================
- */
- int idMultiplayerGame::WinningTeam() {
- if ( teamPoints[0] > teamPoints[1] )
- return 0;
- if ( teamPoints[0] < teamPoints[1] )
- return 1;
- return -1;
- }
- /*
- ================
- idMultiplayerGame::PointLimitHit
- ================
- */
- bool idMultiplayerGame::PointLimitHit() {
- 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;
- }
- /*
- ================
- idMultiplayerGame::FragLeader
- return the current winner ( or a player from the winning team )
- NULL if even
- ================
- */
- idPlayer *idMultiplayerGame::FragLeader() {
- 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( i, SND_YOUWIN );
- } else {
- PlayGlobalSound( i, SND_YOULOSE );
- }
- } else if ( gameLocal.gameType == GAME_LASTMAN ) {
- if ( player == winner ) {
- playerState[ i ].wins++;
- PlayGlobalSound( i, SND_YOUWIN );
- } else if ( !player->wantSpectate ) {
- PlayGlobalSound( i, SND_YOULOSE );
- }
- } else if ( gameLocal.gameType == GAME_TOURNEY ) {
- if ( player == winner ) {
- playerState[ i ].wins++;
- PlayGlobalSound( i, SND_YOUWIN );
- } else if ( i == currentTourneyPlayer[ 0 ] || i == currentTourneyPlayer[ 1 ] ) {
- PlayGlobalSound( i, SND_YOULOSE );
- }
- } else {
- if ( player == winner ) {
- playerState[i].wins++;
- PlayGlobalSound( i, SND_YOUWIN );
- } else if ( !player->wantSpectate ) {
- PlayGlobalSound( i, SND_YOULOSE );
- }
- }
- }
- } 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( i, SND_YOUWIN );
- playerState[ i ].wins++;
- } else {
- PlayGlobalSound( i, SND_YOULOSE );
- }
- }
- }
-
- if ( winner ) {
- lastWinner = winner->entityNumber;
- } else {
- lastWinner = -1;
- }
- }
- /*
- ================
- idMultiplayerGame::TeamScoreCTF
- ================
- */
- void idMultiplayerGame::TeamScoreCTF( int team, int delta ) {
- if ( team < 0 || team > 1 )
- return;
- teamPoints[team] += delta;
- if ( gameState == GAMEON || gameState == SUDDENDEATH )
- PrintMessageEvent( 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;
- }
- /*
- ================
- 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( !common->IsClient() );
- if( gameState == COUNTDOWN || gameState == WARMUP ) {
- // No Kill scores are gained during warmup.
- return;
- }
- if( dead == NULL ) {
- idLib::Warning( "idMultiplayerGame::PlayerDeath dead ptr == NULL, kill will not count" );
- return;
- }
- playerState[ dead->entityNumber ].deaths++;
- 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( MSG_SUICIDE, dead->entityNumber );
- } else if ( killer ) {
- if ( telefrag ) {
- killer->GetAchievementManager().EventCompletesAchievement( ACHIEVEMENT_MP_KILL_PLAYER_VIA_TELEPORT );
- PrintMessageEvent( MSG_TELEFRAGGED, dead->entityNumber, killer->entityNumber );
- } else if ( IsGametypeTeamBased() && dead->team == killer->team ) { /* CTF */
- PrintMessageEvent( MSG_KILLEDTEAM, dead->entityNumber, killer->entityNumber );
- } else {
- if ( killer->PowerUpActive( INVISIBILITY ) ) {
- killer->GetAchievementManager().EventCompletesAchievement( ACHIEVEMENT_MP_KILL_5_PLAYERS_USING_INVIS );
- }
- if ( killer->PowerUpActive( BERSERK ) ) {
- killer->GetAchievementManager().EventCompletesAchievement( ACHIEVEMENT_MP_USE_BERSERK_TO_KILL_PLAYER );
- }
- PrintMessageEvent( MSG_KILLED, dead->entityNumber, killer->entityNumber );
- }
- } else {
- PrintMessageEvent( 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=%ld tks=%ld", team, playerState[ clientNum ].fragCount, playerState[ clientNum ].teamFragCount );
- return;
- }
- /*
- ================
- idMultiplayerGame::DumpTourneyLine
- ================
- */
- void idMultiplayerGame::DumpTourneyLine() {
- 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( !common->IsClient() );
- assert( news < STATE_COUNT );
- gameLocal.DPrintf( "%s -> %s\n", GameStateStrings[ gameState ], GameStateStrings[ news ] );
- switch( news ) {
- case GAMEON: {
- gameLocal.LocalMapRestart();
- outMsg.InitWrite( msgBuf, sizeof( msgBuf ) );
- outMsg.WriteBits( 0, 1 );
- session->GetActingGameStateLobbyBase().SendReliable( GAME_RELIABLE_MESSAGE_RESTART, outMsg, false );
- teamPoints[0] = 0;
- teamPoints[1] = 0;
-
- PlayGlobalSound( -1, SND_FIGHT );
- matchStartedTime = gameLocal.serverTime;
- idBitMsg matchStartedTimeMsg;
- byte matchStartedTimeMsgBuf[ sizeof( matchStartedTime ) ];
- matchStartedTimeMsg.InitWrite( matchStartedTimeMsgBuf, sizeof( matchStartedTimeMsgBuf ) );
- matchStartedTimeMsg.WriteLong( matchStartedTime );
- session->GetActingGameStateLobbyBase().SendReliable( GAME_RELIABLE_MESSAGE_MATCH_STARTED_TIME, matchStartedTimeMsg, false );
- 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->wantSpectate = false; // Make sure everyone is in the game.
- 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 );
- idLib::Printf( "TOURNEY NewState GAMEON :> Player %d Benched \n", p->entityNumber );
- 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 );
- idLib::Printf( "TOURNEY NewState :> Player %d On Deck \n", ent->entityNumber );
- if ( gameLocal.gameType == GAME_TOURNEY ) {
- p->tourneyRank = 0;
- }
- }
- }
- if ( CanPlay( p ) ) {
- p->lastManPresent = true;
- } else {
- p->lastManPresent = false;
- }
- }
- NewState_GameOn_ServerAndClient();
- break;
- }
- case GAMEREVIEW: {
- SetFlagMsg( false );
- nextState = INACTIVE; // used to abort a game. cancel out any upcoming state change
- // set all players spectating
- for( i = 0; i < gameLocal.numClients; i++ ) {
- idEntity *ent = gameLocal.entities[ i ];
- if ( !ent || !ent->IsType( idPlayer::Type ) ) {
- continue;
- }
- static_cast<idPlayer *>(ent)->ServerSpectate( true );
- idLib::Printf( "TOURNEY NewState GAMEREVIEW :> Player %d Benched \n", ent->entityNumber );
- }
- UpdateWinsLosses( player );
- SetFlagMsg( true );
- NewState_GameReview_ServerAndClient();
- break;
- }
- case SUDDENDEATH: {
- PrintMessageEvent( MSG_SUDDENDEATH );
- PlayGlobalSound( -1, SND_SUDDENDEATH );
- break;
- }
- case COUNTDOWN: {
- idBitMsg outMsg;
- byte msgBuf[ 128 ];
- warmupEndTime = gameLocal.serverTime + 1000*cvarSystem->GetCVarInteger( "g_countDown" );
- outMsg.InitWrite( msgBuf, sizeof( msgBuf ) );
- outMsg.WriteLong( warmupEndTime );
- session->GetActingGameStateLobbyBase().SendReliable( GAME_RELIABLE_MESSAGE_WARMUPTIME, outMsg, false );
- // Reset all the scores.
- for( i = 0; i < gameLocal.numClients; i++ ) {
- int fragLimit = gameLocal.serverInfo.GetInt( "si_fragLimit" );
- int startingCount = ( gameLocal.gameType == GAME_LASTMAN ) ? fragLimit : 0;
- playerState[ i ].fragCount = startingCount;
- playerState[ i ].teamFragCount = startingCount;
- playerState[ i ].deaths = 0;
- }
- NewState_Countdown_ServerAndClient();
- break;
- }
- 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;
- }
- }
- NewState_Warmup_ServerAndClient();
- break;
- }
- default:
- break;
- }
- gameState = news;
- }
- /*
- ================
- idMultiplayerGame::NewState_Warmup_ServerAndClient
- Called on both servers and clients once when the game state changes to WARMUP.
- ================
- */
- void idMultiplayerGame::NewState_Warmup_ServerAndClient() {
- SetScoreboardActive( false );
- }
- /*
- ================
- idMultiplayerGame::NewState_Countdown_ServerAndClient
- Called on both servers and clients once when the game state changes to COUNTDOWN.
- ================
- */
- void idMultiplayerGame::NewState_Countdown_ServerAndClient() {
- SetScoreboardActive( false );
- }
- /*
- ================
- idMultiplayerGame::NewState_GameOn_ServerAndClient
- Called on both servers and clients once when the game state changes to GAMEON.
- ================
- */
- void idMultiplayerGame::NewState_GameOn_ServerAndClient() {
- startFragLimit = gameLocal.serverInfo.GetInt( "si_fragLimit" );
- SetScoreboardActive( false );
- }
- /*
- ================
- idMultiplayerGame::NewState_GameReview_ServerAndClient
- Called on both servers and clients once when the game state changes to GAMEREVIEW.
- ================
- */
- void idMultiplayerGame::NewState_GameReview_ServerAndClient() {
- SetScoreboardActive( true );
- }
- /*
- ================
- 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;
- idLib::Printf( "TOURNEY :> Executing FillTourneySlots \n" );
- // 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 ) {
- idLib::Printf( "FillTourneySlots: Skipping Player %d ( Wants Spectate )\n", p->entityNumber );
- 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
- }
- idLib::Printf( "TOURNEY :> Player 1: %d Player 2: %d \n", currentTourneyPlayer[ 0 ], currentTourneyPlayer[ 1 ] );
-
- }
- /*
- ================
- 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() {
- assert( !common->IsClient() );
- if ( gameLocal.gameType != GAME_TOURNEY ) {
- return;
- }
- int globalmax = -1;
- for ( int j = 1; j <= gameLocal.numClients; j++ ) {
- int max = -1;
- int imax = -1;
- for ( int i = 0; i < gameLocal.numClients; i++ ) {
- if ( currentTourneyPlayer[ 0 ] == i || currentTourneyPlayer[ 1 ] == i ) {
- continue;
- }
- idPlayer * p = static_cast< idPlayer * >( gameLocal.entities[ i ] );
- if ( !p || p->wantSpectate ) {
- continue;
- }
- if ( p->tourneyRank > max && ( globalmax == -1 || p->tourneyRank < globalmax ) ) {
- max = p->tourneyRank;
- imax = i;
- }
- }
- if ( imax == -1 ) {
- break;
- }
- idBitMsg outMsg;
- byte msgBuf[1024];
- outMsg.InitWrite( msgBuf, sizeof( msgBuf ) );
- outMsg.WriteByte( j );
- session->GetActingGameStateLobbyBase().SendReliableToLobbyUser( gameLocal.lobbyUserIDs[imax], GAME_RELIABLE_MESSAGE_TOURNEYLINE, 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 );
- idLib::Printf( "TOURNEY CycleTourneyPlayers:> Player %d On Deck \n", player->entityNumber );
- } else {
- ent = gameLocal.entities[ i ];
- if ( ent && ent->IsType( idPlayer::Type ) ) {
- player = static_cast<idPlayer *>( gameLocal.entities[ i ] );
- player->ServerSpectate( true );
- idLib::Printf( "TOURNEY CycleTourneyPlayers:> Player %d Benched \n", player->entityNumber );
- }
- }
- }
- UpdateTourneyLine();
- }
- /*
- ================
- idMultiplayerGame::Warmup
- ================
- */
- bool idMultiplayerGame::Warmup() {
- return ( gameState == WARMUP );
- }
- void idMultiplayerGame::GameHasBeenWon() {
- // Only allow leaderboard submissions within public games.
- const idMatchParameters & matchParameters = session->GetActingGameStateLobbyBase().GetMatchParms();
- if( matchParameters.matchFlags & MATCH_RANKED ) {
- // Upload all player's scores to the leaderboard.
- for( int playerIdx = 0; playerIdx < gameLocal.numClients; playerIdx++ ) {
- leaderboardStats_t stats = { playerState[ playerIdx ].fragCount, playerState[ playerIdx ].wins, playerState[ playerIdx ].teamFragCount, playerState[ playerIdx].deaths };
- LeaderboardLocal_Upload( gameLocal.lobbyUserIDs[ playerIdx ], gameLocal.gameType, stats );
- }
- // Flush all the collectively queued leaderboards all at once. ( Otherwise you get a busy signal on the second "flush" )
- session->LeaderboardFlush();
- }
- // Award Any players that have not died. An Achievement
- for( int i = 0; i < gameLocal.numClients; i++ ) {
- idPlayer * player = static_cast< idPlayer* >( gameLocal.entities[ i ] );
- if ( player == NULL ) {
- continue;
- }
- // Only validate players that were in the tourney.
- if ( gameLocal.gameType == GAME_TOURNEY ) {
- if ( i != currentTourneyPlayer[ 0 ] && i != currentTourneyPlayer[ 1 ] ) {
- continue;
- }
- }
- // In LMS, players that join mid-game will not have ever died
- if ( gameLocal.gameType == GAME_LASTMAN ) {
- if ( playerState[i].fragCount == LASTMAN_NOLIVES ) {
- continue;
- }
- }
- if ( playerState[ i ].deaths == 0 ) {
- player->GetAchievementManager().EventCompletesAchievement( ACHIEVEMENT_MP_COMPLETE_MATCH_WITHOUT_DYING );
- }
- }
- }
- /*
- ================
- idMultiplayerGame::Run
- ================
- */
- void idMultiplayerGame::Run() {
- int i, timeLeft;
- idPlayer *player;
- int gameReviewPause;
- assert( common->IsMultiplayer() );
- assert( !common->IsClient() );
- pureReady = true;
- if ( gameState == INACTIVE ) {
- NewState( WARMUP );
- }
- CheckRespawns();
- if ( nextState != INACTIVE && gameLocal.serverTime > nextStateSwitch ) {
- NewState( nextState );
- nextState = INACTIVE;
- }
- idLobbyBase & lobby = session->GetActingGameStateLobbyBase();
- for ( i = 0; i < gameLocal.numClients; i++ ) {
- idPlayer * player = static_cast<idPlayer *>( gameLocal.entities[i] );
- if ( player != NULL ) {
- playerState[i].ping = lobby.GetLobbyUserQoS( gameLocal.lobbyUserIDs[i] );
- }
- }
-
- switch( gameState ) {
- case GAMEREVIEW: {
- if ( nextState == INACTIVE ) {
- gameReviewPause = cvarSystem->GetCVarInteger( "g_gameReviewPause" );
- nextState = NEXTGAME;
- nextStateSwitch = gameLocal.serverTime + 1000 * gameReviewPause;
- }
- break;
- }
- case NEXTGAME: {
- if ( nextState == INACTIVE ) {
- // make sure flags are returned
- if ( IsGametypeFlagBased() ) {
- idItemTeam * flag;
- flag = GetTeamFlag( 0 );
- if ( flag ) {
- flag->Return();
- }
- flag = GetTeamFlag( 1 );
- if ( flag ) {
- flag->Return();
- }
- }
- 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 ( EnoughClientsToPlay() ) {
- NewState( COUNTDOWN );
- nextState = GAMEON;
- nextStateSwitch = gameLocal.serverTime + 1000 * cvarSystem->GetCVarInteger( "g_countDown" );
- }
- one = two = three = false;
- break;
- }
- case COUNTDOWN: {
- timeLeft = ( nextStateSwitch - gameLocal.serverTime ) / 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;
- }
- break;
- }
- case GAMEON: {
- if ( IsGametypeFlagBased() ) { /* CTF */
- // totally different logic branch for CTF
- if ( PointLimitHit() ) {
- int team = WinningTeam();
- assert( team != -1 );
- NewState( GAMEREVIEW, NULL );
- PrintMessageEvent( MSG_POINTLIMIT, team );
- GameHasBeenWon();
- } else if ( TimeLimitHit() ) {
- int team = WinningTeam();
- if ( EnoughClientsToPlay() && team == -1 ) {
- NewState( SUDDENDEATH );
- } else {
- NewState( GAMEREVIEW, NULL );
- PrintMessageEvent( MSG_TIMELIMIT );
- GameHasBeenWon();
- }
- }
- break;
- }
-
- 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.serverTime + FRAGLIMIT_DELAY;
- }
- if ( gameLocal.serverTime > fragLimitTimeout ) {
- NewState( GAMEREVIEW, player );
- PrintMessageEvent( MSG_FRAGLIMIT, IsGametypeTeamBased() ? player->team : player->entityNumber );
- GameHasBeenWon();
- }
- } 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( MSG_HOLYSHIT );
- fragLimitTimeout = 0;
- NewState( SUDDENDEATH );
- } else if ( TimeLimitHit() ) {
- player = FragLeader();
- if ( !player ) {
- NewState( SUDDENDEATH );
- } else {
- NewState( GAMEREVIEW, player );
- PrintMessageEvent( MSG_TIMELIMIT );
- GameHasBeenWon();
- }
- } else {
- BalanceTeams();
- }
- }
- break;
- }
- case SUDDENDEATH: {
- if ( IsGametypeFlagBased() ) { /* CTF */
- int team = WinningTeam();
- if ( team != -1 ) {
- // TODO : implement pointLimitTimeout
- NewState( GAMEREVIEW, NULL );
- PrintMessageEvent( MSG_POINTLIMIT, team );
- GameHasBeenWon();
- }
- break;
- }
-
- player = FragLeader();
- if ( player ) {
- if ( !fragLimitTimeout ) {
- common->DPrintf( "enter sudden death FragLeader timeout, player %d is leader\n", player->entityNumber );
- fragLimitTimeout = gameLocal.serverTime + FRAGLIMIT_DELAY;
- }
- if ( gameLocal.serverTime > fragLimitTimeout ) {
- NewState( GAMEREVIEW, player );
- PrintMessageEvent( MSG_FRAGLIMIT, IsGametypeTeamBased() ? player->team : player->entityNumber );
- GameHasBeenWon();
- }
- } else if ( fragLimitTimeout ) {
- SuddenRespawn();
- PrintMessageEvent( MSG_HOLYSHIT );
- fragLimitTimeout = 0;
- }
- break;
- }
- }
- }
- /*
- ================
- 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;
- }
- // if the player you are viewing is holding a flag, hide it.
- idEntity* flag = GetTeamFlag( viewPlayer->team ? 0 : 1 );
- if( flag ) {
- if( viewPlayer->carryingFlag ) {
- flag->Hide();
- } else {
- flag->Show();
- }
- }
- }
- UpdatePlayerRanks();
- UpdateHud( viewPlayer, player->hudManager );
- // use the hud of the local player
- viewPlayer->playerView.RenderPlayerView( player->hudManager );
- idStr spectatetext[ 2 ];
- GetSpectateText( player, spectatetext, true );
- if ( scoreboardManager != NULL ) {
- scoreboardManager->UpdateSpectating( spectatetext[0].c_str(), spectatetext[1].c_str() );
- }
- DrawChat( player );
- DrawScoreBoard( player );
- return true;
- }
- /*
- ================
- idMultiplayerGame::GetSpectateText
- ================
- */
- void idMultiplayerGame::GetSpectateText( idPlayer * player, idStr spectatetext[ 2 ], bool scoreboard ) {
- if ( !player->spectating ) {
- return;
- }
- if ( player->wantSpectate && !scoreboard ) {
- if ( gameLocal.gameType == GAME_LASTMAN && gameState == GAMEON ) {
- // If we're in GAMEON in lastman, you can't actully spawn, or you'll have an unfair
- // advantage with more lives than everyone else.
- spectatetext[ 0 ] = idLocalization::GetString( "#str_04246" );
- spectatetext[ 0 ] += idLocalization::GetString( "#str_07003" );
- } else {
- if ( gameLocal.gameType == GAME_TOURNEY &&
- ( currentTourneyPlayer[0] != -1 && currentTourneyPlayer[1] != -1 ) &&
- ( currentTourneyPlayer[0] != player->entityNumber && currentTourneyPlayer[1] != player->entityNumber ) ) {
- spectatetext[ 0 ] = idLocalization::GetString( "#str_04246" );
- switch ( player->tourneyLine ) {
- case 0:
- spectatetext[ 0 ] += idLocalization::GetString( "#str_07003" );
- break;
- case 1:
- spectatetext[ 0 ] += idLocalization::GetString( "#str_07004" );
- break;
- case 2:
- spectatetext[ 0 ] += idLocalization::GetString( "#str_07005" );
- break;
- default:
- spectatetext[ 0 ] += va( idLocalization::GetString( "#str_07006" ), player->tourneyLine );
- break;
- }
- } else {
- spectatetext[ 0 ] = idLocalization::GetString( "#str_respawn_message" );
- }
- }
- } else {
- if ( gameLocal.gameType == GAME_TOURNEY ) {
- spectatetext[ 0 ] = idLocalization::GetString( "#str_04246" );
- switch ( player->tourneyLine ) {
- case 0:
- spectatetext[ 0 ] += idLocalization::GetString( "#str_07003" );
- break;
- case 1:
- spectatetext[ 0 ] += idLocalization::GetString( "#str_07004" );
- break;
- case 2:
- spectatetext[ 0 ] += idLocalization::GetString( "#str_07005" );
- break;
- default:
- spectatetext[ 0 ] += va( idLocalization::GetString( "#str_07006" ), player->tourneyLine );
- break;
- }
- } else if ( gameLocal.gameType == GAME_LASTMAN ) {
- spectatetext[ 0 ] = idLocalization::GetString( "#str_07007" );
- } else {
- spectatetext[ 0 ] = idLocalization::GetString( "#str_04246" );
- }
- }
- if ( player->spectator != player->entityNumber ) {
- idLobbyBase & lobby = session->GetActingGameStateLobbyBase();
- spectatetext[ 1 ] = va( idLocalization::GetString( "#str_07008" ), lobby.GetLobbyUserName( gameLocal.lobbyUserIDs[ player->spectator ] ) );
- }
- }
- /*
- ================
- idMultiplayerGame::UpdateHud
- ================
- */
- void idMultiplayerGame::UpdateHud( idPlayer *player, idMenuHandler_HUD * hudManager ) {
- if ( hudManager && hudManager->GetHud() ) {
- idMenuScreen_HUD * hud = hudManager->GetHud();
- if ( Warmup() ) {
- hud->UpdateMessage( true, "#str_04251" );
- if ( IsGametypeTeamBased() ) {
- hud->SetTeamScore( 0, 0 );
- hud->SetTeamScore( 1, 0 );
- }
- } else if ( gameState == SUDDENDEATH ) {
- hud->UpdateMessage( true, "#str_04252" );
- } else {
- hud->UpdateGameTime( GameTime() );
- }
- if ( IsGametypeTeamBased() || IsGametypeFlagBased() ) {
- hud->ToggleMPInfo( true, true, IsGametypeFlagBased() );
- } else {
- hud->ToggleMPInfo( true, false );
- }
- if ( gameState == GAMEON || gameState == COUNTDOWN || gameState == WARMUP ) {
- if ( IsGametypeTeamBased() && !IsGametypeFlagBased() ) {
- for ( int i = 0; i < gameLocal.numClients; ++i ) {
- idEntity * ent = gameLocal.entities[ i ];
- if ( ent == NULL || !ent->IsType( idPlayer::Type ) ) {
- continue;
- }
- idPlayer * player = static_cast< idPlayer * >( ent );
- hud->SetTeamScore( player->team, playerState[ player->entityNumber ].teamFragCount );
- }
- }
- }
- if ( IsGametypeFlagBased() || IsGametypeTeamBased() ) {
- hud->SetTeam( player->team );
- } else {
- hud->SetTeam( -1 );
- }
- }
- }
- /*
- ================
- idMultiplayerGame::SetScoreboardActive
- ================
- */
- void idMultiplayerGame::SetScoreboardActive( bool active ) {
- if( scoreboardManager != NULL ) {
- if( active ) {
- if ( IsGametypeTeamBased() || IsGametypeFlagBased() ) {
- scoreboardManager->SetActivationScreen( SCOREBOARD_AREA_TEAM, MENU_TRANSITION_SIMPLE );
- } else {
- scoreboardManager->SetActivationScreen( SCOREBOARD_AREA_DEFAULT, MENU_TRANSITION_SIMPLE );
- }
- scoreboardManager->ActivateMenu( true );
- } else {
- scoreboardManager->ActivateMenu( false );
- }
- }
- }
- /*
- ================
- idMultiplayerGame::DrawScoreBoard
- ================
- */
- void idMultiplayerGame::DrawScoreBoard( idPlayer *player ) {
- if ( scoreboardManager && scoreboardManager->IsActive() == true ) {
- UpdateScoreboard( scoreboardManager, player );
- }
- }
- /*
- ===============
- 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 = Sys_Milliseconds();
- }
- /*
- ===============
- idMultiplayerGame::DrawChat
- ===============
- */
- void idMultiplayerGame::DrawChat( idPlayer * player ) {
- int i, j;
- if ( player ) {
- if ( Sys_Milliseconds() - 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 = Sys_Milliseconds();
- }
- if ( chatDataUpdated ) {
- j = 0;
- i = chatHistoryIndex - chatHistorySize;
- while ( i < chatHistoryIndex ) {
- player->AddChatMessage( j, Min( 4, (int)chatHistory[ i % NUM_CHAT_NOTIFY ].fade ), chatHistory[ i % NUM_CHAT_NOTIFY ].line );
- j++; i++;
- }
- while ( j < NUM_CHAT_NOTIFY ) {
- player->ClearChatMessage( j );
- j++;
- }
- chatDataUpdated = false;
- }
- }
- }
- //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
- 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( idBitMsg &msg ) const {
- int i;
- int value;
- // This is a hack - I need a place to read the lobby ids before the player entities are
- // read (SpawnPlayer requires a valid lobby id for the player).
- for ( int i = 0; i < gameLocal.lobbyUserIDs.Num(); ++i ) {
- gameLocal.lobbyUserIDs[i].WriteToMsg( msg );
- }
- msg.WriteByte( gameState );
- msg.WriteLong( nextStateSwitch );
- 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.WriteShort( teamPoints[0] );
- msg.WriteShort( teamPoints[1] );
- msg.WriteShort( player_red_flag );
- msg.WriteShort( player_blue_flag );
- }
- /*
- ================
- idMultiplayerGame::ReadFromSnapshot
- ================
- */
- void idMultiplayerGame::ReadFromSnapshot( const idBitMsg &msg ) {
- int i;
- gameState_t newState;
- // This is a hack - I need a place to read the lobby ids before the player entities are
- // read (SpawnPlayer requires a valid lobby id for the player).
- for ( int i = 0; i < gameLocal.lobbyUserIDs.Num(); ++i ) {
- gameLocal.lobbyUserIDs[i].ReadFromMsg( msg );
- }
- newState = (idMultiplayerGame::gameState_t)msg.ReadByte();
- nextStateSwitch = msg.ReadLong();
- if ( newState != gameState && newState < STATE_COUNT ) {
- gameLocal.DPrintf( "%s -> %s\n", GameStateStrings[ gameState ], GameStateStrings[ newState ] );
- gameState = newState;
-
- switch( gameState ) {
- case GAMEON: {
- NewState_GameOn_ServerAndClient();
- break;
- }
- case GAMEREVIEW: {
- NewState_GameReview_ServerAndClient();
- break;
- }
- case WARMUP: {
- NewState_Warmup_ServerAndClient();
- break;
- }
- case COUNTDOWN: {
- NewState_Countdown_ServerAndClient();
- break;
- }
- }
- }
- 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 );
- }
- teamPoints[0] = msg.ReadShort();
- teamPoints[1] = msg.ReadShort();
- player_red_flag = msg.ReadShort();
- player_blue_flag = msg.ReadShort();
- }
- /*
- ================
- idMultiplayerGame::PlayGlobalSound
- ================
- */
- void idMultiplayerGame::PlayGlobalSound( int toPlayerNum, snd_evt_t evt, const char *shader ) {
- if ( toPlayerNum < 0 || toPlayerNum == gameLocal.GetLocalClientNum() ) {
- if ( shader ) {
- if ( gameSoundWorld ) {
- gameSoundWorld->PlayShaderDirectly( shader );
- }
- } else {
- if ( gameSoundWorld ) {
- gameSoundWorld->PlayShaderDirectly( GlobalSoundStrings[ evt ] );
- }
- }
- }
- if ( !common->IsClient() && toPlayerNum != gameLocal.GetLocalClientNum() ) {
- idBitMsg outMsg;
- byte msgBuf[1024];
- outMsg.InitWrite( msgBuf, sizeof( msgBuf ) );
- int type = 0;
- if ( shader ) {
- const idSoundShader * shaderDecl = declManager->FindSound( shader );
- if ( !shaderDecl ) {
- return;
- }
- outMsg.WriteLong( gameLocal.ServerRemapDecl( -1, DECL_SOUND, shaderDecl->Index() ) );
- type = GAME_RELIABLE_MESSAGE_SOUND_INDEX;
- } else {
- outMsg.WriteByte( evt );
- type = GAME_RELIABLE_MESSAGE_SOUND_EVENT;
- }
- if ( toPlayerNum >= 0 ) {
- session->GetActingGameStateLobbyBase().SendReliableToLobbyUser( gameLocal.lobbyUserIDs[toPlayerNum], type, outMsg );
- } else {
- session->GetActingGameStateLobbyBase().SendReliable( type, outMsg, false );
- }
- }
- }
- /*
- ================
- 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 );
- }
- }
- /*
- ================
- idMultiplayerGame::PrintMessageEvent
- ================
- */
- void idMultiplayerGame::PrintMessageEvent( msg_evt_t evt, int parm1, int parm2 ) {
- idLobbyBase & lobby = session->GetActingGameStateLobbyBase();
- switch ( evt ) {
- case MSG_LEFTGAME:
- assert( parm1 >= 0 );
- AddChatLine( idLocalization::GetString( "#str_11604" ), lobby.GetLobbyUserName( gameLocal.lobbyUserIDs[parm1] ) );
- break;
- case MSG_SUICIDE:
- assert( parm1 >= 0 );
- AddChatLine( idLocalization::GetString( "#str_04293" ), lobby.GetLobbyUserName( gameLocal.lobbyUserIDs[parm1] ) );
- break;
- case MSG_KILLED:
- assert( parm1 >= 0 && parm2 >= 0 );
- AddChatLine( idLocalization::GetString( "#str_04292" ), lobby.GetLobbyUserName( gameLocal.lobbyUserIDs[parm1] ), lobby.GetLobbyUserName( gameLocal.lobbyUserIDs[parm2] ) );
- break;
- case MSG_KILLEDTEAM:
- assert( parm1 >= 0 && parm2 >= 0 );
- AddChatLine( idLocalization::GetString( "#str_04291" ), lobby.GetLobbyUserName( gameLocal.lobbyUserIDs[parm1] ), lobby.GetLobbyUserName( gameLocal.lobbyUserIDs[parm2] ) );
- break;
- case MSG_TELEFRAGGED:
- assert( parm1 >= 0 && parm2 >= 0 );
- AddChatLine( idLocalization::GetString( "#str_04290" ), lobby.GetLobbyUserName( gameLocal.lobbyUserIDs[parm1] ), lobby.GetLobbyUserName( gameLocal.lobbyUserIDs[parm2] ) );
- break;
- case MSG_DIED:
- assert( parm1 >= 0 );
- AddChatLine( idLocalization::GetString( "#str_04289" ), lobby.GetLobbyUserName( gameLocal.lobbyUserIDs[parm1] ) );
- break;
- case MSG_SUDDENDEATH:
- AddChatLine( idLocalization::GetString( "#str_04287" ) );
- break;
- case MSG_JOINEDSPEC:
- AddChatLine( idLocalization::GetString( "#str_04285" ), lobby.GetLobbyUserName( gameLocal.lobbyUserIDs[parm1] ) );
- break;
- case MSG_TIMELIMIT:
- AddChatLine( idLocalization::GetString( "#str_04284" ) );
- break;
- case MSG_FRAGLIMIT:
- if ( gameLocal.gameType == GAME_LASTMAN ) {
- AddChatLine( idLocalization::GetString( "#str_04283" ), lobby.GetLobbyUserName( gameLocal.lobbyUserIDs[parm1] ) );
- } else if ( IsGametypeTeamBased() ) { /* CTF */
- AddChatLine( idLocalization::GetString( "#str_04282" ), idLocalization::GetString( teamNames[ parm1 ] ) );
- } else {
- AddChatLine( idLocalization::GetString( "#str_04281" ), lobby.GetLobbyUserName( gameLocal.lobbyUserIDs[parm1] ) );
- }
- break;
- case MSG_JOINTEAM:
- AddChatLine( idLocalization::GetString( "#str_04280" ), lobby.GetLobbyUserName( gameLocal.lobbyUserIDs[parm1] ), parm2 ? idLocalization::GetString( "#str_02500" ) : idLocalization::GetString( "#str_02499" ) );
- break;
- case MSG_HOLYSHIT:
- AddChatLine( idLocalization::GetString( "#str_06732" ) );
- break;
- case MSG_POINTLIMIT:
- AddChatLine( idLocalization::GetString( "#str_11100" ), parm1 ? idLocalization::GetString( "#str_11110" ) : idLocalization::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( idLocalization::GetString( "#str_11101" ), lobby.GetLobbyUserName( gameLocal.lobbyUserIDs[parm2] ) ); // your team
- } else {
- AddChatLine( idLocalization::GetString( "#str_11102" ), lobby.GetLobbyUserName( gameLocal.lobbyUserIDs[parm2] ) ); // enemy
- }
- break;
- case MSG_FLAGDROP :
- if ( gameLocal.GetLocalPlayer() == NULL )
- break;
- if ( gameLocal.GetLocalPlayer()->team != parm1 ) {
- AddChatLine( idLocalization::GetString( "#str_11103" ) ); // your team
- } else {
- AddChatLine( idLocalization::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( idLocalization::GetString( "#str_11120" ), lobby.GetLobbyUserName( gameLocal.lobbyUserIDs[parm2] ) ); // your team
- } else {
- AddChatLine( idLocalization::GetString( "#str_11121" ), lobby.GetLobbyUserName( gameLocal.lobbyUserIDs[parm2] ) ); // enemy
- }
- } else {
- AddChatLine( idLocalization::GetString( "#str_11105" ), parm1 ? idLocalization::GetString( "#str_11110" ) : idLocalization::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( idLocalization::GetString( "#str_11122" ), lobby.GetLobbyUserName( gameLocal.lobbyUserIDs[parm2] ) ); // your team
- } else {
- AddChatLine( idLocalization::GetString( "#str_11123" ), lobby.GetLobbyUserName( gameLocal.lobbyUserIDs[parm2] ) ); // enemy
- }
-
- // AddChatLine( idLocalization::GetString( "#str_11106" ), parm1 ? idLocalization::GetString( "#str_11110" ) : idLocalization::GetString( "#str_11111" ) );
- break;
- case MSG_SCOREUPDATE:
- AddChatLine( idLocalization::GetString( "#str_11107" ), parm1, parm2 );
- break;
- default:
- gameLocal.DPrintf( "PrintMessageEvent: unknown message type %d\n", evt );
- return;
- }
- if ( !common->IsClient() ) {
- idBitMsg outMsg;
- byte msgBuf[1024];
- outMsg.InitWrite( msgBuf, sizeof( msgBuf ) );
- outMsg.WriteByte( evt );
- outMsg.WriteByte( parm1 );
- outMsg.WriteByte( parm2 );
- session->GetActingGameStateLobbyBase().SendReliable( GAME_RELIABLE_MESSAGE_DB, outMsg, false );
- }
- }
- /*
- ================
- 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() {
- 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 );
- idLib::Printf( "TOURNEY CheckRespawns :> Player %d On Deck \n", p->entityNumber );
- }
- } else if ( gameState == WARMUP ) {
- // make sure empty tourney slots get filled first
- FillTourneySlots( );
- if ( i == currentTourneyPlayer[ 0 ] || i == currentTourneyPlayer[ 1 ] ) {
- p->ServerSpectate( false );
- idLib::Printf( "TOURNEY CheckRespawns WARMUP :> Player %d On Deck \n", p->entityNumber );
- }
- }
- } else if ( gameLocal.gameType == GAME_LASTMAN ) {
- if ( gameState == WARMUP || gameState == COUNTDOWN ) {
- // Player has spawned in game, give him lives.
- playerState[ i ].fragCount = gameLocal.serverInfo.GetInt( "si_fragLimit" );
- 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 );
- idLib::Printf( "TOURNEY CheckRespawns :> Player %d Wants Spectate \n", p->entityNumber );
- UpdateTourneyLine();
- CheckAbortGame();
- }
- }
- }
- /*
- ================
- idMultiplayerGame::DropWeapon
- ================
- */
- void idMultiplayerGame::DropWeapon( int clientNum ) {
- assert( !common->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 ( !common->IsMultiplayer() ) {
- common->Printf( "clientDropWeapon: only valid in multiplayer\n" );
- return;
- }
- idBitMsg outMsg;
- session->GetActingGameStateLobbyBase().SendReliableToHost( GAME_RELIABLE_MESSAGE_DROPWEAPON, outMsg );
- }
- /*
- ================
- idMultiplayerGame::MessageMode_f
- ================
- */
- void idMultiplayerGame::MessageMode_f( const idCmdArgs &args ) {
- if ( !common->IsMultiplayer() ) {
- return;
- }
- gameLocal.mpGame.MessageMode( args );
- }
- /*
- ================
- idMultiplayerGame::MessageMode
- ================
- */
- void idMultiplayerGame::MessageMode( const idCmdArgs &args ) {
- idEntity *ent = gameLocal.entities[ gameLocal.GetLocalClientNum() ];
- if ( !ent || !ent->IsType( idPlayer::Type ) ) {
- return;
- }
- idPlayer * player = static_cast< idPlayer* >( ent );
- if ( player && !player->spectating ) {
- if ( args.Argc() != 2 ) {
- player->isChatting = 1;
- } else {
- player->isChatting = 2;
- }
- }
- }
- /*
- ================
- idMultiplayerGame::DisconnectClient
- ================
- */
- void idMultiplayerGame::DisconnectClient( int clientNum ) {
- if ( lastWinner == clientNum ) {
- lastWinner = -1;
- }
- // Show that the user has left the game.
- PrintMessageEvent( MSG_LEFTGAME, clientNum, -1 );
- UpdatePlayerRanks();
- CheckAbortGame();
- }
- /*
- ================
- idMultiplayerGame::CheckAbortGame
- ================
- */
- void idMultiplayerGame::CheckAbortGame() {
- 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++ ) {
- idPlayer * player = static_cast< idPlayer * >( gameLocal.entities[ currentTourneyPlayer[ i ] ] );
- if ( !gameLocal.entities[ currentTourneyPlayer[ i ] ] || player->spectating ) {
- NewState( GAMEREVIEW );
- return;
- }
- }
- break;
- default:
- if ( !EnoughClientsToPlay() ) {
- NewState( GAMEREVIEW );
- }
- break;
- }
- }
- /*
- ================
- idMultiplayerGame::MapRestart
- ================
- */
- void idMultiplayerGame::MapRestart() {
- assert( !common->IsClient() );
- if ( gameState != WARMUP ) {
- NewState( WARMUP );
- nextState = INACTIVE;
- nextStateSwitch = 0;
- }
- teamPoints[0] = 0;
- teamPoints[1] = 0;
- BalanceTeams();
- }
- /*
- ================
- idMultiplayerGame::BalanceTeams
- ================
- */
- void idMultiplayerGame::BalanceTeams() {
- if ( !IsGametypeTeamBased() ) {
- return;
- }
- int teamCount[ 2 ] = { 0, 0 };
- NumActualClients( false, teamCount );
- int outOfBalance = abs( teamCount[0] - teamCount[1] );
- if ( outOfBalance <= 1 ) {
- return;
- }
- // Teams are out of balance
- // Move N players from the large team to the small team
- int numPlayersToMove = outOfBalance / 2;
- int smallTeam = MinIndex( teamCount[0], teamCount[1] );
- int largeTeam = 1 - smallTeam;
- idLobbyBase & lobby = session->GetActingGameStateLobbyBase();
- // First move players from the large team that match a party token on the small team
- for ( int a = 0; a < gameLocal.numClients; a++ ) {
- idPlayer * playerA = gameLocal.GetClientByNum( a );
- if ( playerA->team == largeTeam && CanPlay( playerA ) ) {
- for ( int b = 0; b < gameLocal.numClients; b++ ) {
- if ( a == b ) {
- continue;
- }
- idPlayer * playerB = gameLocal.GetClientByNum( b );
- if ( playerB->team == smallTeam && CanPlay( playerB ) ) {
- if ( lobby.GetLobbyUserPartyToken( gameLocal.lobbyUserIDs[ a ] ) == lobby.GetLobbyUserPartyToken( gameLocal.lobbyUserIDs[ b ] ) ) {
- SwitchToTeam( a, largeTeam, smallTeam );
- numPlayersToMove--;
- if ( numPlayersToMove == 0 ) {
- return;
- }
- break;
- }
- }
- }
- }
- }
- // Then move players from the large team that DON'T match party tokens on the large team
- for ( int a = 0; a < gameLocal.numClients; a++ ) {
- idPlayer * playerA = gameLocal.GetClientByNum( a );
- if ( playerA->team == largeTeam && CanPlay( playerA ) ) {
- bool match = false;
- for ( int b = 0; b < gameLocal.numClients; b++ ) {
- if ( a == b ) {
- continue;
- }
- idPlayer * playerB = gameLocal.GetClientByNum( b );
- if ( playerB->team == largeTeam && CanPlay( playerB ) ) {
- if ( lobby.GetLobbyUserPartyToken( gameLocal.lobbyUserIDs[ a ] ) == lobby.GetLobbyUserPartyToken( gameLocal.lobbyUserIDs[ b ] ) ) {
- match = true;
- break;
- }
- }
- }
- if ( !match ) {
- SwitchToTeam( a, largeTeam, smallTeam );
- numPlayersToMove--;
- if ( numPlayersToMove == 0 ) {
- return;
- }
- }
- }
- }
- // Then move any players from the large team to the small team
- for ( int a = 0; a < gameLocal.numClients; a++ ) {
- idPlayer * playerA = gameLocal.GetClientByNum( a );
- if ( playerA->team == largeTeam && CanPlay( playerA ) ) {
- SwitchToTeam( a, largeTeam, smallTeam );
- numPlayersToMove--;
- if ( numPlayersToMove == 0 ) {
- return;
- }
- }
- }
- }
- /*
- ================
- idMultiplayerGame::SwitchToTeam
- ================
- */
- void idMultiplayerGame::SwitchToTeam( int clientNum, int oldteam, int newteam ) {
- idPlayer * p = static_cast<idPlayer *>( gameLocal.entities[ clientNum ] );
- p->team = newteam;
- session->GetActingGameStateLobbyBase().SetLobbyUserTeam( gameLocal.lobbyUserIDs[ clientNum ], newteam );
- session->SetVoiceGroupsToTeams();
- assert( IsGametypeTeamBased() ); /* CTF */
- assert( oldteam != newteam );
- assert( !common->IsClient() );
- if ( !common->IsClient() && newteam >= 0 ) {
- PrintMessageEvent( MSG_JOINTEAM, clientNum, newteam );
- }
- // assign the right teamFragCount
- int i;
- for( i = 0; i < gameLocal.numClients; i++ ) {
- if ( i == clientNum ) {
- continue;
- }
- idEntity * 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;
- }
- if ( ( gameState == GAMEON || ( IsGametypeFlagBased() && gameState == SUDDENDEATH ) ) && oldteam != -1 ) {
- // when changing teams during game, kill and respawn
- if ( p->IsInTeleport() ) {
- p->ServerSendEvent( idPlayer::EVENT_ABORT_TELEPORTER, NULL, false );
- p->SetPrivateCameraView( NULL );
- }
- p->Kill( true, true );
- if ( IsGametypeFlagBased() )
- p->DropFlag();
- CheckAbortGame();
- } else if ( IsGametypeFlagBased() && oldteam != -1 ) {
- p->DropFlag();
- }
- }
- /*
- ================
- 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 *pfrom;
- idStr prefixed_name;
- assert( !common->IsClient() );
- if ( clientNum >= 0 ) {
- pfrom = static_cast< idPlayer * >( gameLocal.entities[ clientNum ] );
- if ( !( pfrom && pfrom->IsType( idPlayer::Type ) ) ) {
- return;
- }
- if ( pfrom->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 {
- pfrom = NULL;
- send_to = 0;
- }
- // put the message together
- outMsg.InitWrite( msgBuf, sizeof( msgBuf ) );
- 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 );
- session->GetActingGameStateLobbyBase().SendReliable( GAME_RELIABLE_MESSAGE_CHAT, outMsg, false );
- 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;
- }
- idPlayer * pent = static_cast< idPlayer * >( ent );
- if ( send_to == 1 && pent->spectating ) {
- if ( sound ) {
- PlayGlobalSound( i, SND_COUNT, sound );
- }
- if ( i == gameLocal.GetLocalClientNum() ) {
- AddChatLine( "%s^0: %s\n", prefixed_name.c_str(), text );
- } else {
- session->GetActingGameStateLobbyBase().SendReliableToLobbyUser( gameLocal.lobbyUserIDs[i], GAME_RELIABLE_MESSAGE_CHAT, outMsg );
- }
- } else if ( send_to == 2 && pent->team == pfrom->team ) {
- if ( sound ) {
- PlayGlobalSound( i, SND_COUNT, sound );
- }
- if ( i == gameLocal.GetLocalClientNum() ) {
- AddChatLine( "%s^0: %s\n", prefixed_name.c_str(), text );
- } else {
- session->GetActingGameStateLobbyBase().SendReliableToLobbyUser( gameLocal.lobbyUserIDs[i], GAME_RELIABLE_MESSAGE_CHAT, outMsg );
- }
- }
- }
- }
- }
- /*
- ================
- idMultiplayerGame::Precache
- ================
- */
- void idMultiplayerGame::Precache() {
- if ( !common->IsMultiplayer() ) {
- return;
- }
- // player
- declManager->FindType( DECL_ENTITYDEF, gameLocal.GetMPPlayerDefName(), false );
- // skins
- for ( int i = 0; i < numSkins; i++ ) {
- idStr baseSkinName = skinNames[ i ];
- declManager->FindSkin( baseSkinName, false );
- declManager->FindSkin( baseSkinName + "_berserk", false );
- declManager->FindSkin( baseSkinName + "_invuln", false );
- }
- // MP game sounds
- for ( int i = 0; i < SND_COUNT; i++ ) {
- declManager->FindSound( GlobalSoundStrings[ i ] );
- }
- }
- /*
- ================
- idMultiplayerGame::ToggleSpectate
- ================
- */
- void idMultiplayerGame::ToggleSpectate() {
- assert( common->IsClient() || gameLocal.GetLocalClientNum() == 0 );
- idPlayer * player = (idPlayer *)gameLocal.entities[gameLocal.GetLocalClientNum()];
- bool spectating = player->spectating;
- // only allow toggling to spectate if spectators are enabled.
- if ( !spectating && !gameLocal.serverInfo.GetBool( "si_spectators" ) ) {
- gameLocal.mpGame.AddChatLine( idLocalization::GetString( "#str_06747" ) );
- return;
- }
- byte msgBuf[ 256 ];
- idBitMsg outMsg;
- outMsg.InitWrite( msgBuf, sizeof( msgBuf ) );
- outMsg.WriteBool( !spectating );
- outMsg.WriteByteAlign();
- session->GetActingGameStateLobbyBase().SendReliableToHost( GAME_RELIABLE_MESSAGE_SPECTATE, outMsg );
- }
- /*
- ================
- idMultiplayerGame::CanPlay
- ================
- */
- bool idMultiplayerGame::CanPlay( idPlayer *p ) {
- return !p->wantSpectate;
- }
- /*
- ================
- idMultiplayerGame::WantRespawn
- ================
- */
- bool idMultiplayerGame::WantRespawn( idPlayer *p ) {
- return p->forceRespawn && !p->wantSpectate;
- }
- /*
- ================
- 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 ( !common->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;
- }
- if ( gameLocal.GetLocalPlayer() == NULL ) {
- return;
- }
- voc = args.Argv( 1 );
-
- spawnArgs = &gameLocal.GetLocalPlayer()->spawnArgs;
- 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.InitWrite( msgBuf, sizeof( msgBuf ) );
- outMsg.WriteLong( index );
- outMsg.WriteBits( team ? 1 : 0, 1 );
- session->GetActingGameStateLobbyBase().SendReliableToHost( GAME_RELIABLE_MESSAGE_VCHAT, 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 = &p->spawnArgs;
- 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 = session->GetActingGameStateLobbyBase().GetLobbyUserName( gameLocal.lobbyUserIDs[ clientNum ] );
- 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, lobbyUserID_t lobbyUserID ) {
- idBitMsg outMsg;
- byte msgBuf[ MAX_GAME_MESSAGE_SIZE ];
- outMsg.InitWrite( msgBuf, sizeof( msgBuf ) );
- outMsg.BeginWriting();
- // 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( int i = 0; i < gameLocal.numClients; i++ ) {
- idEntity * ent = gameLocal.entities[ i ];
- if ( i != clientNum && ent && ent->IsType( idPlayer::Type ) ) {
- outMsg.WriteByte( i );
- outMsg.WriteBits( static_cast< idPlayer * >( ent )->inventory.powerups, 15 );
- outMsg.WriteBits( static_cast< idPlayer * >( ent )->spectating, 1 );
- }
- }
- outMsg.WriteByte( MAX_CLIENTS );
- session->GetActingGameStateLobbyBase().SendReliableToLobbyUser( lobbyUserID, GAME_RELIABLE_MESSAGE_STARTSTATE, outMsg );
- // warmup time
- if ( gameState == COUNTDOWN ) {
- outMsg.BeginWriting();
- outMsg.WriteByte( GAME_RELIABLE_MESSAGE_WARMUPTIME );
- outMsg.WriteLong( warmupEndTime );
- session->GetActingGameStateLobbyBase().SendReliableToLobbyUser( lobbyUserID, GAME_RELIABLE_MESSAGE_WARMUPTIME, outMsg );
- }
- }
- /*
- ================
- idMultiplayerGame::ClientReadStartState
- ================
- */
- void idMultiplayerGame::ClientReadStartState( const idBitMsg &msg ) {
- // read the state in preparation for reading snapshot updates
- gameState = (idMultiplayerGame::gameState_t)msg.ReadByte();
- matchStartedTime = msg.ReadLong( );
- startFragLimit = msg.ReadShort( );
- int client;
- while ( ( client = msg.ReadByte() ) != MAX_CLIENTS ) {
- // Do not process players that are not here.
- if( gameLocal.entities[ client ] == NULL ) {
- continue;
- }
- assert( gameLocal.entities[ client ] && gameLocal.entities[ client ]->IsType( idPlayer::Type ) );
- int powerup = msg.ReadBits( 15 );
- for ( int i = 0; i < MAX_POWERUPS; i++ ) {
- if ( powerup & ( 1 << i ) ) {
- static_cast< idPlayer * >( gameLocal.entities[ client ] )->GivePowerUp( i, 0, ITEM_GIVE_FEEDBACK | ITEM_GIVE_UPDATE_STATE );
- }
- }
- 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();
- }
- /*
- ================
- idMultiplayerGame::ClientReadWarmupTime
- ================
- */
- void idMultiplayerGame::ClientReadMatchStartedTime( const idBitMsg & msg ) {
- matchStartedTime = msg.ReadLong();
- }
- /*
- ================
- idMultiplayerGame::ClientReadAchievementUnlock
- ================
- */
- void idMultiplayerGame::ClientReadAchievementUnlock( const idBitMsg & msg ) {
- int playerid = msg.ReadByte();
- achievement_t achieve = ( achievement_t )msg.ReadByte();
- idPlayer * player = static_cast<idPlayer *>( gameLocal.entities[ playerid ] );
- if( player != NULL ) {
- idLib::Printf( "Client Receiving Achievement\n");
- player->GetAchievementManager().EventCompletesAchievement( achieve );
- }
- }
- /*
- ================
- idMultiplayerGame::IsGametypeTeamBased
- ================
- */
- bool idMultiplayerGame::IsGametypeTeamBased() /* CTF */
- {
- switch ( gameLocal.gameType )
- {
- case GAME_SP:
- case GAME_DM:
- case GAME_TOURNEY:
- case GAME_LASTMAN:
- return false;
- case GAME_CTF:
- case GAME_TDM:
- return true;
- default:
- assert( !"Add support for your new gametype here." );
- }
- return false;
- }
- /*
- ================
- idMultiplayerGame::IsGametypeFlagBased
- ================
- */
- bool idMultiplayerGame::IsGametypeFlagBased() {
- switch ( gameLocal.gameType )
- {
- case GAME_SP:
- case GAME_DM:
- case GAME_TOURNEY:
- case GAME_LASTMAN:
- case GAME_TDM:
- return false;
- case GAME_CTF:
- return true;
- default:
- assert( !"Add support for your new gametype here." );
- }
- return false;
-
- }
- /*
- ================
- 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() {
- 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() {
- return ( GetGameState() == WARMUP || GetGameState() == GAMEON || GetGameState() == SUDDENDEATH ) && flagMsgOn;
- }
- /*
- ================
- idMultiplayerGame::ReloadScoreboard
- ================
- */
- void idMultiplayerGame::ReloadScoreboard() {
-
- if ( scoreboardManager != NULL ) {
- scoreboardManager->Initialize( "scoreboard", common->SW() );
- }
-
- Precache();
- }
- /*
- ================
- idMultiplayerGame::GetGameModes
- ================
- */
- int idMultiplayerGame::GetGameModes( const char *** gameModes, const char *** gameModesDisplay ) {
- bool defaultValue = true;
- if( session->GetTitleStorageBool("CTF_Enabled", defaultValue ) ) {
- *gameModes = gameTypeNames_WithCTF;
- *gameModesDisplay = gameTypeDisplayNames_WithCTF;
- return GAME_COUNT;
- } else {
- *gameModes = gameTypeNames_WithoutCTF;
- *gameModesDisplay = gameTypeDisplayNames_WithoutCTF;
- return GAME_COUNT - 1;
- }
- }
|