MultiplayerGame.cpp 88 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189
  1. /*
  2. ===========================================================================
  3. Doom 3 BFG Edition GPL Source Code
  4. Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
  5. This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
  6. Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation, either version 3 of the License, or
  9. (at your option) any later version.
  10. Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. GNU General Public License for more details.
  14. You should have received a copy of the GNU General Public License
  15. along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
  16. 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.
  17. 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.
  18. ===========================================================================
  19. */
  20. #include "../idlib/precompiled.h"
  21. #pragma hdrstop
  22. #include "Game_local.h"
  23. // could be a problem if players manage to go down sudden deaths till this .. oh well
  24. #define LASTMAN_NOLIVES -20
  25. extern idCVar ui_skinIndex;
  26. const char * idMultiplayerGame::teamNames[] = { "#str_02499", "#str_02500" };
  27. const char * idMultiplayerGame::skinNames[] = {
  28. "skins/characters/player/marine_mp",
  29. "skins/characters/player/marine_mp_red",
  30. "skins/characters/player/marine_mp_blue",
  31. "skins/characters/player/marine_mp_green",
  32. "skins/characters/player/marine_mp_yellow",
  33. "skins/characters/player/marine_mp_purple",
  34. "skins/characters/player/marine_mp_grey",
  35. "skins/characters/player/marine_mp_orange"
  36. };
  37. const idVec3 idMultiplayerGame::skinColors[] = {
  38. idVec3( 0.25f, 0.25f, 0.25f ), // light grey
  39. idVec3( 1.00f, 0.00f, 0.00f ), // red
  40. idVec3( 0.20f, 0.50f, 0.80f ), // blue
  41. idVec3( 0.00f, 0.80f, 0.10f ), // green
  42. idVec3( 1.00f, 0.80f, 0.10f ), // yellow
  43. idVec3( 0.39f, 0.199f, 0.3f ), // purple
  44. idVec3( 0.425f, 0.484f, 0.445f ), // dark grey
  45. idVec3( 0.484f, 0.312f, 0.074f) // orange
  46. };
  47. const int idMultiplayerGame::numSkins = sizeof( idMultiplayerGame::skinNames ) / sizeof( idMultiplayerGame::skinNames[0] );
  48. const char * idMultiplayerGame::GetTeamName( int team ) const { return teamNames[idMath::ClampInt( 0, 1, team )]; }
  49. const char * idMultiplayerGame::GetSkinName( int skin ) const { return skinNames[idMath::ClampInt( 0, numSkins-1, skin )]; }
  50. const idVec3 & idMultiplayerGame::GetSkinColor( int skin ) const { return skinColors[idMath::ClampInt( 0, numSkins-1, skin )]; }
  51. // make CTF not included in game modes for consoles
  52. const char * gameTypeNames_WithCTF[] = { "Deathmatch", "Tourney", "Team DM", "Last Man", "CTF", "" };
  53. const char * gameTypeDisplayNames_WithCTF[] = { "#str_04260", "#str_04261", "#str_04262", "#str_04263", "#str_swf_mode_ctf", "" };
  54. const char * gameTypeNames_WithoutCTF[] = { "Deathmatch", "Tourney", "Team DM", "Last Man", "" };
  55. const char * gameTypeDisplayNames_WithoutCTF[] = { "#str_04260", "#str_04261", "#str_04262", "#str_04263", "" };
  56. compile_time_assert( GAME_DM == 0 );
  57. compile_time_assert( GAME_TOURNEY == 1 );
  58. compile_time_assert( GAME_TDM == 2 );
  59. compile_time_assert( GAME_LASTMAN == 3 );
  60. compile_time_assert( GAME_CTF == 4 );
  61. idCVar g_spectatorChat( "g_spectatorChat", "0", CVAR_GAME | CVAR_ARCHIVE | CVAR_BOOL, "let spectators talk to everyone during game" );
  62. // global sounds transmitted by index - 0 .. SND_COUNT
  63. // sounds in this list get precached on MP start
  64. const char *idMultiplayerGame::GlobalSoundStrings[] = {
  65. "sound/vo/feedback/voc_youwin.wav",
  66. "sound/vo/feedback/voc_youlose.wav",
  67. "sound/vo/feedback/fight.wav",
  68. "sound/vo/feedback/three.wav",
  69. "sound/vo/feedback/two.wav",
  70. "sound/vo/feedback/one.wav",
  71. "sound/vo/feedback/sudden_death.wav",
  72. "sound/vo/ctf/flag_capped_yours.wav",
  73. "sound/vo/ctf/flag_capped_theirs.wav",
  74. "sound/vo/ctf/flag_return.wav",
  75. "sound/vo/ctf/flag_taken_yours.wav",
  76. "sound/vo/ctf/flag_taken_theirs.wav",
  77. "sound/vo/ctf/flag_dropped_yours.wav",
  78. "sound/vo/ctf/flag_dropped_theirs.wav"
  79. };
  80. // handy verbose
  81. const char *idMultiplayerGame::GameStateStrings[] = {
  82. "INACTIVE",
  83. "WARMUP",
  84. "COUNTDOWN",
  85. "GAMEON",
  86. "SUDDENDEATH",
  87. "GAMEREVIEW",
  88. "NEXTGAME"
  89. };
  90. /*
  91. ================
  92. idMultiplayerGame::idMultiplayerGame
  93. ================
  94. */
  95. idMultiplayerGame::idMultiplayerGame() {
  96. teamFlags[0] = NULL;
  97. teamFlags[1] = NULL;
  98. teamPoints[0] = 0;
  99. teamPoints[1] = 0;
  100. flagMsgOn = true;
  101. player_blue_flag = -1;
  102. player_red_flag = -1;
  103. Clear();
  104. scoreboardManager = NULL;
  105. }
  106. /*
  107. ================
  108. idMultiplayerGame::Shutdown
  109. ================
  110. */
  111. void idMultiplayerGame::Shutdown() {
  112. Clear();
  113. }
  114. /*
  115. ================
  116. idMultiplayerGame::Reset
  117. ================
  118. */
  119. void idMultiplayerGame::Reset() {
  120. Clear();
  121. ClearChatData();
  122. if ( common->IsMultiplayer() ) {
  123. scoreboardManager = new idMenuHandler_Scoreboard();
  124. if ( scoreboardManager != NULL ) {
  125. scoreboardManager->Initialize( "scoreboard", common->SW() );
  126. }
  127. }
  128. ClearChatData();
  129. warmupEndTime = 0;
  130. lastChatLineTime = 0;
  131. }
  132. /*
  133. ================
  134. idMultiplayerGame::ServerClientConnect
  135. ================
  136. */
  137. void idMultiplayerGame::ServerClientConnect( int clientNum ) {
  138. memset( &playerState[ clientNum ], 0, sizeof( playerState[ clientNum ] ) );
  139. }
  140. /*
  141. ================
  142. idMultiplayerGame::SpawnPlayer
  143. ================
  144. */
  145. void idMultiplayerGame::SpawnPlayer( int clientNum ) {
  146. idLobbyBase & lobby = session->GetActingGameStateLobbyBase();
  147. lobbyUserID_t & lobbyUserID = gameLocal.lobbyUserIDs[clientNum];
  148. AddChatLine( idLocalization::GetString( "#str_07177" ), lobby.GetLobbyUserName( lobbyUserID ) );
  149. memset( &playerState[ clientNum ], 0, sizeof( playerState[ clientNum ] ) );
  150. if ( !common->IsClient() ) {
  151. if ( gameLocal.gameType == GAME_LASTMAN ) {
  152. // Players spawn with no lives to prevent them from getting the clean slate achievement if
  153. // they didn't earn it.
  154. playerState[ clientNum ].fragCount = LASTMAN_NOLIVES;
  155. }
  156. idPlayer *p = static_cast< idPlayer * >( gameLocal.entities[ clientNum ] );
  157. p->spawnedTime = gameLocal.serverTime;
  158. p->team = 0;
  159. p->tourneyRank = 0;
  160. if ( gameLocal.gameType == GAME_TOURNEY && gameState == GAMEON ) {
  161. p->tourneyRank++;
  162. }
  163. }
  164. }
  165. /*
  166. ================
  167. idMultiplayerGame::Clear
  168. ================
  169. */
  170. void idMultiplayerGame::Clear() {
  171. int i;
  172. gameState = INACTIVE;
  173. nextState = INACTIVE;
  174. nextStateSwitch = 0;
  175. matchStartedTime = 0;
  176. currentTourneyPlayer[ 0 ] = -1;
  177. currentTourneyPlayer[ 1 ] = -1;
  178. one = two = three = false;
  179. memset( &playerState, 0 , sizeof( playerState ) );
  180. lastWinner = -1;
  181. pureReady = false;
  182. CleanupScoreboard();
  183. fragLimitTimeout = 0;
  184. voiceChatThrottle = 0;
  185. for ( i = 0; i < NUM_CHAT_NOTIFY; i++ ) {
  186. chatHistory[ i ].line.Clear();
  187. }
  188. startFragLimit = -1;
  189. }
  190. /*
  191. ================
  192. idMultiplayerGame::Clear
  193. ================
  194. */
  195. void idMultiplayerGame::CleanupScoreboard() {
  196. if ( scoreboardManager != NULL ) {
  197. delete scoreboardManager;
  198. scoreboardManager = NULL;
  199. }
  200. }
  201. /*
  202. ================
  203. idMultiplayerGame::GetFlagPoints
  204. Gets number of captures in CTF game.
  205. 0 = red team
  206. 1 = blue team
  207. ================
  208. */
  209. int idMultiplayerGame::GetFlagPoints( int team )
  210. {
  211. assert( team <= 1 );
  212. return teamPoints[ team ];
  213. }
  214. /*
  215. ================
  216. idMultiplayerGame::UpdatePlayerRanks
  217. ================
  218. */
  219. void idMultiplayerGame::UpdatePlayerRanks() {
  220. int i, j, k;
  221. idPlayer *players[MAX_CLIENTS];
  222. idEntity *ent;
  223. idPlayer *player;
  224. memset( players, 0, sizeof( players ) );
  225. numRankedPlayers = 0;
  226. for ( i = 0; i < gameLocal.numClients; i++ ) {
  227. ent = gameLocal.entities[ i ];
  228. if ( !ent || !ent->IsType( idPlayer::Type ) ) {
  229. continue;
  230. }
  231. player = static_cast< idPlayer * >( ent );
  232. if ( !CanPlay( player ) ) {
  233. continue;
  234. }
  235. if ( gameLocal.gameType == GAME_TOURNEY ) {
  236. if ( i != currentTourneyPlayer[ 0 ] && i != currentTourneyPlayer[ 1 ] ) {
  237. continue;
  238. }
  239. }
  240. if ( gameLocal.gameType == GAME_LASTMAN && playerState[ i ].fragCount == LASTMAN_NOLIVES ) {
  241. continue;
  242. }
  243. for ( j = 0; j < numRankedPlayers; j++ ) {
  244. bool insert = false;
  245. if ( IsGametypeTeamBased() ) { /* CTF */
  246. if ( player->team != players[ j ]->team ) {
  247. if ( playerState[ i ].teamFragCount > playerState[ players[ j ]->entityNumber ].teamFragCount ) {
  248. // team scores
  249. insert = true;
  250. } else if ( playerState[ i ].teamFragCount == playerState[ players[ j ]->entityNumber ].teamFragCount && player->team < players[ j ]->team ) {
  251. // at equal scores, sort by team number
  252. insert = true;
  253. }
  254. } else if ( playerState[ i ].fragCount > playerState[ players[ j ]->entityNumber ].fragCount ) {
  255. // in the same team, sort by frag count
  256. insert = true;
  257. }
  258. } else {
  259. insert = ( playerState[ i ].fragCount > playerState[ players[ j ]->entityNumber ].fragCount );
  260. }
  261. if ( insert ) {
  262. for ( k = numRankedPlayers; k > j; k-- ) {
  263. players[ k ] = players[ k-1 ];
  264. }
  265. players[ j ] = player;
  266. break;
  267. }
  268. }
  269. if ( j == numRankedPlayers ) {
  270. players[ numRankedPlayers ] = player;
  271. }
  272. numRankedPlayers++;
  273. }
  274. memcpy( rankedPlayers, players, sizeof( players ) );
  275. }
  276. /*
  277. ================
  278. idMultiplayerGame::UpdateRankColor
  279. ================
  280. */
  281. void idMultiplayerGame::UpdateRankColor( idUserInterface *gui, const char *mask, int i, const idVec3 &vec ) {
  282. for ( int j = 1; j < 4; j++ ) {
  283. gui->SetStateFloat( va( mask, i, j ), vec[ j - 1 ] );
  284. }
  285. }
  286. /*
  287. ================
  288. idMultiplayerGame::IsScoreboardActive
  289. ================
  290. */
  291. bool idMultiplayerGame::IsScoreboardActive() {
  292. if ( scoreboardManager != NULL ) {
  293. return scoreboardManager->IsActive();
  294. }
  295. return false;
  296. }
  297. /*
  298. ================
  299. idMultiplayerGame::HandleGuiEvent
  300. ================
  301. */
  302. bool idMultiplayerGame::HandleGuiEvent( const sysEvent_t * sev ) {
  303. if ( scoreboardManager != NULL && scoreboardManager->IsActive() ) {
  304. scoreboardManager->HandleGuiEvent( sev );
  305. return true;
  306. }
  307. return false;
  308. }
  309. /*
  310. ================
  311. idMultiplayerGame::UpdateScoreboard
  312. ================
  313. */
  314. void idMultiplayerGame::UpdateScoreboard( idMenuHandler_Scoreboard * scoreboard, idPlayer *owner ) {
  315. if ( owner == NULL ) {
  316. return;
  317. }
  318. int redScore = 0;
  319. int blueScore = 0;
  320. idLobbyBase & lobby = session->GetActingGameStateLobbyBase();
  321. idList< mpScoreboardInfo > scoreboardInfo;
  322. for ( int i = 0; i < MAX_CLIENTS; ++i ) {
  323. if ( i < gameLocal.numClients ) {
  324. idEntity *ent = gameLocal.entities[ i ];
  325. if ( !ent || !ent->IsType( idPlayer::Type ) ) {
  326. continue;
  327. }
  328. idPlayer *player = static_cast<idPlayer *>(ent);
  329. if ( !player ) {
  330. continue;
  331. }
  332. idStr spectateData;
  333. idStr playerName;
  334. int score = 0;
  335. int wins = 0;
  336. int ping = 0;
  337. int playerNum = player->entityNumber;
  338. int team = - 1;
  339. lobbyUserID_t & lobbyUserID = gameLocal.lobbyUserIDs[ playerNum ];
  340. if ( IsGametypeTeamBased() ) {
  341. team = player->team;
  342. if ( team == 0 ) {
  343. redScore = playerState[ playerNum ].teamFragCount;
  344. } else if ( team == 1 ) {
  345. blueScore = playerState[ playerNum ].teamFragCount;
  346. }
  347. }
  348. score = idMath::ClampInt( MP_PLAYER_MINFRAGS, MP_PLAYER_MAXFRAGS, playerState[ playerNum ].fragCount );
  349. // HACK -
  350. if( gameLocal.gameType == GAME_LASTMAN && score == LASTMAN_NOLIVES ) {
  351. score = 0;
  352. }
  353. wins = idMath::ClampInt( 0, MP_PLAYER_MAXWINS, playerState[ playerNum ].wins );
  354. ping = playerState[ playerNum ].ping;
  355. if ( gameState == WARMUP ) {
  356. if ( player->spectating ) {
  357. spectateData = idLocalization::GetString( "#str_04246" );
  358. } else {
  359. spectateData = idLocalization::GetString( "#str_04247" );
  360. }
  361. } else {
  362. if ( gameLocal.gameType == GAME_LASTMAN && playerState[ playerNum ].fragCount == LASTMAN_NOLIVES ) {
  363. spectateData = idLocalization::GetString( "#str_06736" );
  364. } else if ( player->spectating ) {
  365. spectateData = idLocalization::GetString( "#str_04246" );
  366. }
  367. }
  368. if ( playerNum == owner->entityNumber ) {
  369. playerName = "^3";
  370. playerName.Append( lobby.GetLobbyUserName( lobbyUserID ) );
  371. playerName.Append( "^0" );
  372. } else {
  373. playerName = lobby.GetLobbyUserName( lobbyUserID );
  374. }
  375. mpScoreboardInfo info;
  376. info.voiceState = session->GetDisplayStateFromVoiceState( session->GetLobbyUserVoiceState( lobbyUserID ) );
  377. info.name = playerName;
  378. info.team = team;
  379. info.score = score;
  380. info.wins = wins;
  381. info.ping = ping;
  382. info.playerNum = playerNum;
  383. info.spectateData = spectateData;
  384. bool added = false;
  385. for ( int i = 0; i < scoreboardInfo.Num(); ++i ) {
  386. if ( info.team == scoreboardInfo[i].team ) {
  387. if ( info.score > scoreboardInfo[i].score ) {
  388. scoreboardInfo.Insert( info, i );
  389. added = true;
  390. break;
  391. }
  392. } else if ( info.team < scoreboardInfo[i].team ) {
  393. scoreboardInfo.Insert( info, i );
  394. added = true;
  395. break;
  396. }
  397. }
  398. if ( !added ) {
  399. scoreboardInfo.Append( info );
  400. }
  401. }
  402. }
  403. idStr gameInfo;
  404. if ( gameState == GAMEREVIEW ) {
  405. int timeRemaining = nextStateSwitch - gameLocal.serverTime;
  406. int ms = (int) ceilf( timeRemaining / 1000.0f );
  407. if ( ms == 1 ) {
  408. gameInfo = idLocalization::GetString( "#str_online_game_starts_in_second" );
  409. gameInfo.Replace( "<DNT_VAL>", idStr( ms ) );
  410. } else if ( ms > 0 && ms < 30 ) {
  411. gameInfo = idLocalization::GetString( "#str_online_game_starts_in_seconds" );
  412. gameInfo.Replace( "<DNT_VAL>", idStr( ms ) );
  413. }
  414. } else {
  415. if ( gameLocal.gameType == GAME_LASTMAN ) {
  416. if ( gameState == GAMEON || gameState == SUDDENDEATH ) {
  417. gameInfo = va( "%s: %i", idLocalization::GetString( "#str_04264" ), startFragLimit );
  418. } else {
  419. gameInfo = va( "%s: %i", idLocalization::GetString( "#str_04264" ), gameLocal.serverInfo.GetInt( "si_fragLimit" ) );
  420. }
  421. } else if ( gameLocal.gameType == GAME_CTF ) {
  422. int captureLimit = gameLocal.serverInfo.GetInt( "si_fragLimit" );
  423. gameInfo = va( idLocalization::GetString( "#str_11108" ), captureLimit );
  424. } else {
  425. gameInfo = va( "%s: %i", idLocalization::GetString( "#str_01982" ), gameLocal.serverInfo.GetInt( "si_fragLimit" ) );
  426. }
  427. if ( gameLocal.serverInfo.GetInt( "si_timeLimit" ) > 0 ) {
  428. gameInfo.Append( va( " %s: %i", idLocalization::GetString( "#str_01983" ), gameLocal.serverInfo.GetInt( "si_timeLimit" ) ) );
  429. }
  430. }
  431. if ( scoreboardManager ) {
  432. if ( IsGametypeFlagBased() ) {
  433. scoreboardManager->SetTeamScores( GetFlagPoints( 0 ), GetFlagPoints( 1 ) );
  434. } else if ( IsGametypeTeamBased() ) {
  435. scoreboardManager->SetTeamScores( redScore, blueScore );
  436. }
  437. scoreboardManager->UpdateScoreboard( scoreboardInfo, gameInfo );
  438. scoreboardManager->UpdateScoreboardSelection();
  439. scoreboardManager->Update();
  440. }
  441. }
  442. /*
  443. ================
  444. idMultiplayerGame::GameTime
  445. ================
  446. */
  447. const char *idMultiplayerGame::GameTime() {
  448. static char buff[16];
  449. int m, s, t, ms;
  450. if ( gameState == COUNTDOWN ) {
  451. ms = warmupEndTime - gameLocal.serverTime;
  452. // we never want to show double dashes.
  453. if( ms <= 0 ) {
  454. // Try to setup time again.
  455. warmupEndTime = gameLocal.serverTime + 1000*cvarSystem->GetCVarInteger( "g_countDown" );
  456. ms = warmupEndTime - gameLocal.serverTime;
  457. }
  458. s = ms / 1000 + 1;
  459. if ( ms <= 0 ) {
  460. strcpy( buff, "WMP --" );
  461. } else {
  462. sprintf( buff, "WMP %i", s );
  463. }
  464. } else {
  465. int timeLimit = gameLocal.serverInfo.GetInt( "si_timeLimit" );
  466. if ( timeLimit ) {
  467. ms = ( timeLimit * 60000 ) - ( gameLocal.serverTime - matchStartedTime );
  468. } else {
  469. ms = gameLocal.serverTime - matchStartedTime;
  470. }
  471. if ( ms < 0 ) {
  472. ms = 0;
  473. }
  474. s = ms / 1000;
  475. m = s / 60;
  476. s -= m * 60;
  477. t = s / 10;
  478. s -= t * 10;
  479. sprintf( buff, "%i:%i%i", m, t, s );
  480. }
  481. return &buff[0];
  482. }
  483. /*
  484. ================
  485. idMultiplayerGame::NumActualClients
  486. ================
  487. */
  488. int idMultiplayerGame::NumActualClients( bool countSpectators, int *teamcounts ) {
  489. idPlayer *p;
  490. int c = 0;
  491. if ( teamcounts ) {
  492. teamcounts[ 0 ] = teamcounts[ 1 ] = 0;
  493. }
  494. for( int i = 0 ; i < gameLocal.numClients ; i++ ) {
  495. idEntity *ent = gameLocal.entities[ i ];
  496. if ( !ent || !ent->IsType( idPlayer::Type ) ) {
  497. continue;
  498. }
  499. p = static_cast< idPlayer * >( ent );
  500. if ( countSpectators || CanPlay( p ) ) {
  501. c++;
  502. }
  503. if ( teamcounts && CanPlay( p ) ) {
  504. teamcounts[ p->team ]++;
  505. }
  506. }
  507. return c;
  508. }
  509. /*
  510. ================
  511. idMultiplayerGame::EnoughClientsToPlay
  512. ================
  513. */
  514. bool idMultiplayerGame::EnoughClientsToPlay() {
  515. int teamCount[ 2 ];
  516. int clients = NumActualClients( false, teamCount );
  517. if ( IsGametypeTeamBased() ) { /* CTF */
  518. return clients >= 2 && teamCount[ 0 ] && teamCount[ 1 ];
  519. } else {
  520. return clients >= 2;
  521. }
  522. }
  523. /*
  524. ================
  525. idMultiplayerGame::FragLimitHit
  526. return the winning player (team player)
  527. if there is no FragLeader(), the game is tied and we return NULL
  528. ================
  529. */
  530. idPlayer *idMultiplayerGame::FragLimitHit() {
  531. int i;
  532. int fragLimit = gameLocal.serverInfo.GetInt( "si_fragLimit" );
  533. idPlayer *leader;
  534. if ( IsGametypeFlagBased() ) /* CTF */
  535. return NULL;
  536. leader = FragLeader();
  537. if ( !leader ) {
  538. return NULL;
  539. }
  540. if ( fragLimit <= 0 ) {
  541. fragLimit = MP_PLAYER_MAXFRAGS;
  542. }
  543. if ( gameLocal.gameType == GAME_LASTMAN ) {
  544. // we have a leader, check if any other players have frags left
  545. assert( !static_cast< idPlayer * >( leader )->lastManOver );
  546. for( i = 0 ; i < gameLocal.numClients ; i++ ) {
  547. idEntity *ent = gameLocal.entities[ i ];
  548. if ( !ent || !ent->IsType( idPlayer::Type ) ) {
  549. continue;
  550. }
  551. if ( !CanPlay( static_cast< idPlayer * >( ent ) ) ) {
  552. continue;
  553. }
  554. if ( ent == leader ) {
  555. continue;
  556. }
  557. if ( playerState[ ent->entityNumber ].fragCount > 0 ) {
  558. return NULL;
  559. }
  560. }
  561. // there is a leader, his score may even be negative, but no one else has frags left or is !lastManOver
  562. return leader;
  563. } else if ( IsGametypeTeamBased() ) { /* CTF */
  564. if ( playerState[ leader->entityNumber ].teamFragCount >= fragLimit ) {
  565. return leader;
  566. }
  567. } else {
  568. if ( playerState[ leader->entityNumber ].fragCount >= fragLimit ) {
  569. return leader;
  570. }
  571. }
  572. return NULL;
  573. }
  574. /*
  575. ================
  576. idMultiplayerGame::TimeLimitHit
  577. ================
  578. */
  579. bool idMultiplayerGame::TimeLimitHit() {
  580. int timeLimit = gameLocal.serverInfo.GetInt( "si_timeLimit" );
  581. if ( timeLimit ) {
  582. if ( gameLocal.serverTime >= matchStartedTime + timeLimit * 60000 ) {
  583. return true;
  584. }
  585. }
  586. return false;
  587. }
  588. /*
  589. ================
  590. idMultiplayerGame::WinningTeam
  591. return winning team
  592. -1 if tied or no players
  593. ================
  594. */
  595. int idMultiplayerGame::WinningTeam() {
  596. if ( teamPoints[0] > teamPoints[1] )
  597. return 0;
  598. if ( teamPoints[0] < teamPoints[1] )
  599. return 1;
  600. return -1;
  601. }
  602. /*
  603. ================
  604. idMultiplayerGame::PointLimitHit
  605. ================
  606. */
  607. bool idMultiplayerGame::PointLimitHit() {
  608. int pointLimit = gameLocal.serverInfo.GetInt( "si_fragLimit" );
  609. // default to MP_CTF_MAXPOINTS if needed
  610. if ( pointLimit > MP_CTF_MAXPOINTS )
  611. pointLimit = MP_CTF_MAXPOINTS;
  612. else if ( pointLimit <= 0 )
  613. pointLimit = MP_CTF_MAXPOINTS;
  614. if ( teamPoints[0] == teamPoints[1] )
  615. return false;
  616. if ( teamPoints[0] >= pointLimit ||
  617. teamPoints[1] >= pointLimit )
  618. return true;
  619. return false;
  620. }
  621. /*
  622. ================
  623. idMultiplayerGame::FragLeader
  624. return the current winner ( or a player from the winning team )
  625. NULL if even
  626. ================
  627. */
  628. idPlayer *idMultiplayerGame::FragLeader() {
  629. int i;
  630. int frags[ MAX_CLIENTS ];
  631. idPlayer *leader = NULL;
  632. idEntity *ent;
  633. idPlayer *p;
  634. int high = -9999;
  635. int count = 0;
  636. bool teamLead[ 2 ] = { false, false };
  637. for ( i = 0 ; i < gameLocal.numClients ; i++ ) {
  638. ent = gameLocal.entities[ i ];
  639. if ( !ent || !ent->IsType( idPlayer::Type ) ) {
  640. continue;
  641. }
  642. if ( !CanPlay( static_cast< idPlayer * >( ent ) ) ) {
  643. continue;
  644. }
  645. if ( gameLocal.gameType == GAME_TOURNEY && ent->entityNumber != currentTourneyPlayer[ 0 ] && ent->entityNumber != currentTourneyPlayer[ 1 ] ) {
  646. continue;
  647. }
  648. if ( static_cast< idPlayer * >( ent )->lastManOver ) {
  649. continue;
  650. }
  651. int fragc = ( IsGametypeTeamBased() ) ? playerState[i].teamFragCount : playerState[i].fragCount; /* CTF */
  652. if ( fragc > high ) {
  653. high = fragc;
  654. }
  655. frags[ i ] = fragc;
  656. }
  657. for ( i = 0; i < gameLocal.numClients; i++ ) {
  658. ent = gameLocal.entities[ i ];
  659. if ( !ent || !ent->IsType( idPlayer::Type ) ) {
  660. continue;
  661. }
  662. p = static_cast< idPlayer * >( ent );
  663. p->SetLeader( false );
  664. if ( !CanPlay( p ) ) {
  665. continue;
  666. }
  667. if ( gameLocal.gameType == GAME_TOURNEY && ent->entityNumber != currentTourneyPlayer[ 0 ] && ent->entityNumber != currentTourneyPlayer[ 1 ] ) {
  668. continue;
  669. }
  670. if ( p->lastManOver ) {
  671. continue;
  672. }
  673. if ( p->spectating ) {
  674. continue;
  675. }
  676. if ( frags[ i ] >= high ) {
  677. leader = p;
  678. count++;
  679. p->SetLeader( true );
  680. if ( IsGametypeTeamBased() ) { /* CTF */
  681. teamLead[ p->team ] = true;
  682. }
  683. }
  684. }
  685. if ( !IsGametypeTeamBased() ) { /* CTF */
  686. // more than one player at the highest frags
  687. if ( count > 1 ) {
  688. return NULL;
  689. } else {
  690. return leader;
  691. }
  692. } else {
  693. if ( teamLead[ 0 ] && teamLead[ 1 ] ) {
  694. // even game in team play
  695. return NULL;
  696. }
  697. return leader;
  698. }
  699. }
  700. /*
  701. ================
  702. idGameLocal::UpdateWinsLosses
  703. ================
  704. */
  705. void idMultiplayerGame::UpdateWinsLosses( idPlayer *winner ) {
  706. if ( winner ) {
  707. // run back through and update win/loss count
  708. for( int i = 0; i < gameLocal.numClients; i++ ) {
  709. idEntity *ent = gameLocal.entities[ i ];
  710. if ( !ent || !ent->IsType( idPlayer::Type ) ) {
  711. continue;
  712. }
  713. idPlayer *player = static_cast<idPlayer *>(ent);
  714. if ( IsGametypeTeamBased() ) { /* CTF */
  715. if ( player == winner || ( player != winner && player->team == winner->team ) ) {
  716. playerState[ i ].wins++;
  717. PlayGlobalSound( i, SND_YOUWIN );
  718. } else {
  719. PlayGlobalSound( i, SND_YOULOSE );
  720. }
  721. } else if ( gameLocal.gameType == GAME_LASTMAN ) {
  722. if ( player == winner ) {
  723. playerState[ i ].wins++;
  724. PlayGlobalSound( i, SND_YOUWIN );
  725. } else if ( !player->wantSpectate ) {
  726. PlayGlobalSound( i, SND_YOULOSE );
  727. }
  728. } else if ( gameLocal.gameType == GAME_TOURNEY ) {
  729. if ( player == winner ) {
  730. playerState[ i ].wins++;
  731. PlayGlobalSound( i, SND_YOUWIN );
  732. } else if ( i == currentTourneyPlayer[ 0 ] || i == currentTourneyPlayer[ 1 ] ) {
  733. PlayGlobalSound( i, SND_YOULOSE );
  734. }
  735. } else {
  736. if ( player == winner ) {
  737. playerState[i].wins++;
  738. PlayGlobalSound( i, SND_YOUWIN );
  739. } else if ( !player->wantSpectate ) {
  740. PlayGlobalSound( i, SND_YOULOSE );
  741. }
  742. }
  743. }
  744. } else if ( IsGametypeFlagBased() ) { /* CTF */
  745. int winteam = WinningTeam();
  746. if ( winteam != -1 ) // TODO : print a message telling it why the hell the game ended with no winning team?
  747. for( int i = 0; i < gameLocal.numClients; i++ ) {
  748. idEntity *ent = gameLocal.entities[ i ];
  749. if ( !ent || !ent->IsType( idPlayer::Type ) ) {
  750. continue;
  751. }
  752. idPlayer *player = static_cast<idPlayer *>(ent);
  753. if ( player->team == winteam ) {
  754. PlayGlobalSound( i, SND_YOUWIN );
  755. playerState[ i ].wins++;
  756. } else {
  757. PlayGlobalSound( i, SND_YOULOSE );
  758. }
  759. }
  760. }
  761. if ( winner ) {
  762. lastWinner = winner->entityNumber;
  763. } else {
  764. lastWinner = -1;
  765. }
  766. }
  767. /*
  768. ================
  769. idMultiplayerGame::TeamScoreCTF
  770. ================
  771. */
  772. void idMultiplayerGame::TeamScoreCTF( int team, int delta ) {
  773. if ( team < 0 || team > 1 )
  774. return;
  775. teamPoints[team] += delta;
  776. if ( gameState == GAMEON || gameState == SUDDENDEATH )
  777. PrintMessageEvent( MSG_SCOREUPDATE, teamPoints[0], teamPoints[1] );
  778. }
  779. /*
  780. ================
  781. idMultiplayerGame::PlayerScoreCTF
  782. ================
  783. */
  784. void idMultiplayerGame::PlayerScoreCTF( int playerIdx, int delta ) {
  785. if ( playerIdx < 0 || playerIdx >= MAX_CLIENTS )
  786. return;
  787. playerState[ playerIdx ].fragCount += delta;
  788. }
  789. /*
  790. ================
  791. idMultiplayerGame::GetFlagCarrier
  792. ================
  793. */
  794. int idMultiplayerGame::GetFlagCarrier( int team ) {
  795. int iFlagCarrier = -1;
  796. for ( int i = 0; i < gameLocal.numClients; i++ ) {
  797. idEntity * ent = gameLocal.entities[ i ];
  798. if ( !ent || !ent->IsType( idPlayer::Type ) ) {
  799. continue;
  800. }
  801. idPlayer * player = static_cast<idPlayer *>( ent );
  802. if ( player->team != team )
  803. continue;
  804. if ( player->carryingFlag ) {
  805. if ( iFlagCarrier != -1 )
  806. gameLocal.Warning( "BUG: more than one flag carrier on %s team", team == 0 ? "red" : "blue" );
  807. iFlagCarrier = i;
  808. }
  809. }
  810. return iFlagCarrier;
  811. }
  812. /*
  813. ================
  814. idMultiplayerGame::TeamScore
  815. ================
  816. */
  817. void idMultiplayerGame::TeamScore( int entityNumber, int team, int delta ) {
  818. playerState[ entityNumber ].fragCount += delta;
  819. for( int i = 0 ; i < gameLocal.numClients ; i++ ) {
  820. idEntity *ent = gameLocal.entities[ i ];
  821. if ( !ent || !ent->IsType( idPlayer::Type ) ) {
  822. continue;
  823. }
  824. idPlayer *player = static_cast<idPlayer *>(ent);
  825. if ( player->team == team ) {
  826. playerState[ player->entityNumber ].teamFragCount += delta;
  827. }
  828. }
  829. }
  830. /*
  831. ================
  832. idMultiplayerGame::PlayerDeath
  833. ================
  834. */
  835. void idMultiplayerGame::PlayerDeath( idPlayer *dead, idPlayer *killer, bool telefrag ) {
  836. // don't do PrintMessageEvent and shit
  837. assert( !common->IsClient() );
  838. if( gameState == COUNTDOWN || gameState == WARMUP ) {
  839. // No Kill scores are gained during warmup.
  840. return;
  841. }
  842. if( dead == NULL ) {
  843. idLib::Warning( "idMultiplayerGame::PlayerDeath dead ptr == NULL, kill will not count" );
  844. return;
  845. }
  846. playerState[ dead->entityNumber ].deaths++;
  847. if ( killer ) {
  848. if ( gameLocal.gameType == GAME_LASTMAN ) {
  849. playerState[ dead->entityNumber ].fragCount--;
  850. } else if ( IsGametypeTeamBased() ) { /* CTF */
  851. if ( killer == dead || killer->team == dead->team ) {
  852. // suicide or teamkill
  853. TeamScore( killer->entityNumber, killer->team, -1 );
  854. } else {
  855. TeamScore( killer->entityNumber, killer->team, +1 );
  856. }
  857. } else {
  858. playerState[ killer->entityNumber ].fragCount += ( killer == dead ) ? -1 : 1;
  859. }
  860. }
  861. if ( killer && killer == dead ) {
  862. PrintMessageEvent( MSG_SUICIDE, dead->entityNumber );
  863. } else if ( killer ) {
  864. if ( telefrag ) {
  865. killer->GetAchievementManager().EventCompletesAchievement( ACHIEVEMENT_MP_KILL_PLAYER_VIA_TELEPORT );
  866. PrintMessageEvent( MSG_TELEFRAGGED, dead->entityNumber, killer->entityNumber );
  867. } else if ( IsGametypeTeamBased() && dead->team == killer->team ) { /* CTF */
  868. PrintMessageEvent( MSG_KILLEDTEAM, dead->entityNumber, killer->entityNumber );
  869. } else {
  870. if ( killer->PowerUpActive( INVISIBILITY ) ) {
  871. killer->GetAchievementManager().EventCompletesAchievement( ACHIEVEMENT_MP_KILL_5_PLAYERS_USING_INVIS );
  872. }
  873. if ( killer->PowerUpActive( BERSERK ) ) {
  874. killer->GetAchievementManager().EventCompletesAchievement( ACHIEVEMENT_MP_USE_BERSERK_TO_KILL_PLAYER );
  875. }
  876. PrintMessageEvent( MSG_KILLED, dead->entityNumber, killer->entityNumber );
  877. }
  878. } else {
  879. PrintMessageEvent( MSG_DIED, dead->entityNumber );
  880. playerState[ dead->entityNumber ].fragCount--;
  881. }
  882. }
  883. /*
  884. ================
  885. idMultiplayerGame::PlayerStats
  886. ================
  887. */
  888. void idMultiplayerGame::PlayerStats( int clientNum, char *data, const int len ) {
  889. idEntity *ent;
  890. int team;
  891. *data = 0;
  892. // make sure we don't exceed the client list
  893. if ( clientNum < 0 || clientNum > gameLocal.numClients ) {
  894. return;
  895. }
  896. // find which team this player is on
  897. ent = gameLocal.entities[ clientNum ];
  898. if ( ent && ent->IsType( idPlayer::Type ) ) {
  899. team = static_cast< idPlayer * >(ent)->team;
  900. } else {
  901. return;
  902. }
  903. idStr::snPrintf( data, len, "team=%d score=%ld tks=%ld", team, playerState[ clientNum ].fragCount, playerState[ clientNum ].teamFragCount );
  904. return;
  905. }
  906. /*
  907. ================
  908. idMultiplayerGame::DumpTourneyLine
  909. ================
  910. */
  911. void idMultiplayerGame::DumpTourneyLine() {
  912. int i;
  913. for ( i = 0; i < gameLocal.numClients; i++ ) {
  914. if ( gameLocal.entities[ i ] && gameLocal.entities[ i ]->IsType( idPlayer::Type ) ) {
  915. common->Printf( "client %d: rank %d\n", i, static_cast< idPlayer * >( gameLocal.entities[ i ] )->tourneyRank );
  916. }
  917. }
  918. }
  919. /*
  920. ================
  921. idMultiplayerGame::NewState
  922. ================
  923. */
  924. void idMultiplayerGame::NewState( gameState_t news, idPlayer *player ) {
  925. idBitMsg outMsg;
  926. byte msgBuf[MAX_GAME_MESSAGE_SIZE];
  927. int i;
  928. assert( news != gameState );
  929. assert( !common->IsClient() );
  930. assert( news < STATE_COUNT );
  931. gameLocal.DPrintf( "%s -> %s\n", GameStateStrings[ gameState ], GameStateStrings[ news ] );
  932. switch( news ) {
  933. case GAMEON: {
  934. gameLocal.LocalMapRestart();
  935. outMsg.InitWrite( msgBuf, sizeof( msgBuf ) );
  936. outMsg.WriteBits( 0, 1 );
  937. session->GetActingGameStateLobbyBase().SendReliable( GAME_RELIABLE_MESSAGE_RESTART, outMsg, false );
  938. teamPoints[0] = 0;
  939. teamPoints[1] = 0;
  940. PlayGlobalSound( -1, SND_FIGHT );
  941. matchStartedTime = gameLocal.serverTime;
  942. idBitMsg matchStartedTimeMsg;
  943. byte matchStartedTimeMsgBuf[ sizeof( matchStartedTime ) ];
  944. matchStartedTimeMsg.InitWrite( matchStartedTimeMsgBuf, sizeof( matchStartedTimeMsgBuf ) );
  945. matchStartedTimeMsg.WriteLong( matchStartedTime );
  946. session->GetActingGameStateLobbyBase().SendReliable( GAME_RELIABLE_MESSAGE_MATCH_STARTED_TIME, matchStartedTimeMsg, false );
  947. fragLimitTimeout = 0;
  948. for( i = 0; i < gameLocal.numClients; i++ ) {
  949. idEntity *ent = gameLocal.entities[ i ];
  950. if ( !ent || !ent->IsType( idPlayer::Type ) ) {
  951. continue;
  952. }
  953. idPlayer *p = static_cast<idPlayer *>( ent );
  954. p->wantSpectate = false; // Make sure everyone is in the game.
  955. p->SetLeader( false ); // don't carry the flag from previous games
  956. if ( gameLocal.gameType == GAME_TOURNEY && currentTourneyPlayer[ 0 ] != i && currentTourneyPlayer[ 1 ] != i ) {
  957. p->ServerSpectate( true );
  958. idLib::Printf( "TOURNEY NewState GAMEON :> Player %d Benched \n", p->entityNumber );
  959. p->tourneyRank++;
  960. } else {
  961. int fragLimit = gameLocal.serverInfo.GetInt( "si_fragLimit" );
  962. int startingCount = ( gameLocal.gameType == GAME_LASTMAN ) ? fragLimit : 0;
  963. playerState[ i ].fragCount = startingCount;
  964. playerState[ i ].teamFragCount = startingCount;
  965. if ( !static_cast<idPlayer *>(ent)->wantSpectate ) {
  966. static_cast<idPlayer *>(ent)->ServerSpectate( false );
  967. idLib::Printf( "TOURNEY NewState :> Player %d On Deck \n", ent->entityNumber );
  968. if ( gameLocal.gameType == GAME_TOURNEY ) {
  969. p->tourneyRank = 0;
  970. }
  971. }
  972. }
  973. if ( CanPlay( p ) ) {
  974. p->lastManPresent = true;
  975. } else {
  976. p->lastManPresent = false;
  977. }
  978. }
  979. NewState_GameOn_ServerAndClient();
  980. break;
  981. }
  982. case GAMEREVIEW: {
  983. SetFlagMsg( false );
  984. nextState = INACTIVE; // used to abort a game. cancel out any upcoming state change
  985. // set all players spectating
  986. for( i = 0; i < gameLocal.numClients; i++ ) {
  987. idEntity *ent = gameLocal.entities[ i ];
  988. if ( !ent || !ent->IsType( idPlayer::Type ) ) {
  989. continue;
  990. }
  991. static_cast<idPlayer *>(ent)->ServerSpectate( true );
  992. idLib::Printf( "TOURNEY NewState GAMEREVIEW :> Player %d Benched \n", ent->entityNumber );
  993. }
  994. UpdateWinsLosses( player );
  995. SetFlagMsg( true );
  996. NewState_GameReview_ServerAndClient();
  997. break;
  998. }
  999. case SUDDENDEATH: {
  1000. PrintMessageEvent( MSG_SUDDENDEATH );
  1001. PlayGlobalSound( -1, SND_SUDDENDEATH );
  1002. break;
  1003. }
  1004. case COUNTDOWN: {
  1005. idBitMsg outMsg;
  1006. byte msgBuf[ 128 ];
  1007. warmupEndTime = gameLocal.serverTime + 1000*cvarSystem->GetCVarInteger( "g_countDown" );
  1008. outMsg.InitWrite( msgBuf, sizeof( msgBuf ) );
  1009. outMsg.WriteLong( warmupEndTime );
  1010. session->GetActingGameStateLobbyBase().SendReliable( GAME_RELIABLE_MESSAGE_WARMUPTIME, outMsg, false );
  1011. // Reset all the scores.
  1012. for( i = 0; i < gameLocal.numClients; i++ ) {
  1013. int fragLimit = gameLocal.serverInfo.GetInt( "si_fragLimit" );
  1014. int startingCount = ( gameLocal.gameType == GAME_LASTMAN ) ? fragLimit : 0;
  1015. playerState[ i ].fragCount = startingCount;
  1016. playerState[ i ].teamFragCount = startingCount;
  1017. playerState[ i ].deaths = 0;
  1018. }
  1019. NewState_Countdown_ServerAndClient();
  1020. break;
  1021. }
  1022. case WARMUP: {
  1023. teamPoints[0] = 0;
  1024. teamPoints[1] = 0;
  1025. if ( IsGametypeFlagBased() ) {
  1026. // reset player scores to zero, only required for CTF
  1027. for( i = 0; i < gameLocal.numClients; i++ ) {
  1028. idEntity *ent = gameLocal.entities[ i ];
  1029. if ( !ent || !ent->IsType( idPlayer::Type ) ) {
  1030. continue;
  1031. }
  1032. playerState[ i ].fragCount = 0;
  1033. }
  1034. }
  1035. NewState_Warmup_ServerAndClient();
  1036. break;
  1037. }
  1038. default:
  1039. break;
  1040. }
  1041. gameState = news;
  1042. }
  1043. /*
  1044. ================
  1045. idMultiplayerGame::NewState_Warmup_ServerAndClient
  1046. Called on both servers and clients once when the game state changes to WARMUP.
  1047. ================
  1048. */
  1049. void idMultiplayerGame::NewState_Warmup_ServerAndClient() {
  1050. SetScoreboardActive( false );
  1051. }
  1052. /*
  1053. ================
  1054. idMultiplayerGame::NewState_Countdown_ServerAndClient
  1055. Called on both servers and clients once when the game state changes to COUNTDOWN.
  1056. ================
  1057. */
  1058. void idMultiplayerGame::NewState_Countdown_ServerAndClient() {
  1059. SetScoreboardActive( false );
  1060. }
  1061. /*
  1062. ================
  1063. idMultiplayerGame::NewState_GameOn_ServerAndClient
  1064. Called on both servers and clients once when the game state changes to GAMEON.
  1065. ================
  1066. */
  1067. void idMultiplayerGame::NewState_GameOn_ServerAndClient() {
  1068. startFragLimit = gameLocal.serverInfo.GetInt( "si_fragLimit" );
  1069. SetScoreboardActive( false );
  1070. }
  1071. /*
  1072. ================
  1073. idMultiplayerGame::NewState_GameReview_ServerAndClient
  1074. Called on both servers and clients once when the game state changes to GAMEREVIEW.
  1075. ================
  1076. */
  1077. void idMultiplayerGame::NewState_GameReview_ServerAndClient() {
  1078. SetScoreboardActive( true );
  1079. }
  1080. /*
  1081. ================
  1082. idMultiplayerGame::FillTourneySlots
  1083. NOTE: called each frame during warmup to keep the tourney slots filled
  1084. ================
  1085. */
  1086. void idMultiplayerGame::FillTourneySlots( ) {
  1087. int i, j, rankmax, rankmaxindex;
  1088. idEntity *ent;
  1089. idPlayer *p;
  1090. idLib::Printf( "TOURNEY :> Executing FillTourneySlots \n" );
  1091. // fill up the slots based on tourney ranks
  1092. for ( i = 0; i < 2; i++ ) {
  1093. if ( currentTourneyPlayer[ i ] != -1 ) {
  1094. continue;
  1095. }
  1096. rankmax = -1;
  1097. rankmaxindex = -1;
  1098. for ( j = 0; j < gameLocal.numClients; j++ ) {
  1099. ent = gameLocal.entities[ j ];
  1100. if ( !ent || !ent->IsType( idPlayer::Type ) ) {
  1101. continue;
  1102. }
  1103. if ( currentTourneyPlayer[ 0 ] == j || currentTourneyPlayer[ 1 ] == j ) {
  1104. continue;
  1105. }
  1106. p = static_cast< idPlayer * >( ent );
  1107. if ( p->wantSpectate ) {
  1108. idLib::Printf( "FillTourneySlots: Skipping Player %d ( Wants Spectate )\n", p->entityNumber );
  1109. continue;
  1110. }
  1111. if ( p->tourneyRank >= rankmax ) {
  1112. // when ranks are equal, use time in game
  1113. if ( p->tourneyRank == rankmax ) {
  1114. assert( rankmaxindex >= 0 );
  1115. if ( p->spawnedTime > static_cast< idPlayer * >( gameLocal.entities[ rankmaxindex ] )->spawnedTime ) {
  1116. continue;
  1117. }
  1118. }
  1119. rankmax = static_cast< idPlayer * >( ent )->tourneyRank;
  1120. rankmaxindex = j;
  1121. }
  1122. }
  1123. currentTourneyPlayer[ i ] = rankmaxindex; // may be -1 if we found nothing
  1124. }
  1125. idLib::Printf( "TOURNEY :> Player 1: %d Player 2: %d \n", currentTourneyPlayer[ 0 ], currentTourneyPlayer[ 1 ] );
  1126. }
  1127. /*
  1128. ================
  1129. idMultiplayerGame::UpdateTourneyLine
  1130. we manipulate tourneyRank on player entities for internal ranking. it's easier to deal with.
  1131. but we need a real wait list to be synced down to clients for GUI
  1132. ignore current players, ignore wantSpectate
  1133. ================
  1134. */
  1135. void idMultiplayerGame::UpdateTourneyLine() {
  1136. assert( !common->IsClient() );
  1137. if ( gameLocal.gameType != GAME_TOURNEY ) {
  1138. return;
  1139. }
  1140. int globalmax = -1;
  1141. for ( int j = 1; j <= gameLocal.numClients; j++ ) {
  1142. int max = -1;
  1143. int imax = -1;
  1144. for ( int i = 0; i < gameLocal.numClients; i++ ) {
  1145. if ( currentTourneyPlayer[ 0 ] == i || currentTourneyPlayer[ 1 ] == i ) {
  1146. continue;
  1147. }
  1148. idPlayer * p = static_cast< idPlayer * >( gameLocal.entities[ i ] );
  1149. if ( !p || p->wantSpectate ) {
  1150. continue;
  1151. }
  1152. if ( p->tourneyRank > max && ( globalmax == -1 || p->tourneyRank < globalmax ) ) {
  1153. max = p->tourneyRank;
  1154. imax = i;
  1155. }
  1156. }
  1157. if ( imax == -1 ) {
  1158. break;
  1159. }
  1160. idBitMsg outMsg;
  1161. byte msgBuf[1024];
  1162. outMsg.InitWrite( msgBuf, sizeof( msgBuf ) );
  1163. outMsg.WriteByte( j );
  1164. session->GetActingGameStateLobbyBase().SendReliableToLobbyUser( gameLocal.lobbyUserIDs[imax], GAME_RELIABLE_MESSAGE_TOURNEYLINE, outMsg );
  1165. globalmax = max;
  1166. }
  1167. }
  1168. /*
  1169. ================
  1170. idMultiplayerGame::CycleTourneyPlayers
  1171. ================
  1172. */
  1173. void idMultiplayerGame::CycleTourneyPlayers( ) {
  1174. int i;
  1175. idEntity *ent;
  1176. idPlayer *player;
  1177. currentTourneyPlayer[ 0 ] = -1;
  1178. currentTourneyPlayer[ 1 ] = -1;
  1179. // if any, winner from last round will play again
  1180. if ( lastWinner != -1 ) {
  1181. idEntity *ent = gameLocal.entities[ lastWinner ];
  1182. if ( ent && ent->IsType( idPlayer::Type ) ) {
  1183. currentTourneyPlayer[ 0 ] = lastWinner;
  1184. }
  1185. }
  1186. FillTourneySlots( );
  1187. // force selected players in/out of the game and update the ranks
  1188. for ( i = 0 ; i < gameLocal.numClients ; i++ ) {
  1189. if ( currentTourneyPlayer[ 0 ] == i || currentTourneyPlayer[ 1 ] == i ) {
  1190. player = static_cast<idPlayer *>( gameLocal.entities[ i ] );
  1191. player->ServerSpectate( false );
  1192. idLib::Printf( "TOURNEY CycleTourneyPlayers:> Player %d On Deck \n", player->entityNumber );
  1193. } else {
  1194. ent = gameLocal.entities[ i ];
  1195. if ( ent && ent->IsType( idPlayer::Type ) ) {
  1196. player = static_cast<idPlayer *>( gameLocal.entities[ i ] );
  1197. player->ServerSpectate( true );
  1198. idLib::Printf( "TOURNEY CycleTourneyPlayers:> Player %d Benched \n", player->entityNumber );
  1199. }
  1200. }
  1201. }
  1202. UpdateTourneyLine();
  1203. }
  1204. /*
  1205. ================
  1206. idMultiplayerGame::Warmup
  1207. ================
  1208. */
  1209. bool idMultiplayerGame::Warmup() {
  1210. return ( gameState == WARMUP );
  1211. }
  1212. void idMultiplayerGame::GameHasBeenWon() {
  1213. // Only allow leaderboard submissions within public games.
  1214. const idMatchParameters & matchParameters = session->GetActingGameStateLobbyBase().GetMatchParms();
  1215. if( matchParameters.matchFlags & MATCH_RANKED ) {
  1216. // Upload all player's scores to the leaderboard.
  1217. for( int playerIdx = 0; playerIdx < gameLocal.numClients; playerIdx++ ) {
  1218. leaderboardStats_t stats = { playerState[ playerIdx ].fragCount, playerState[ playerIdx ].wins, playerState[ playerIdx ].teamFragCount, playerState[ playerIdx].deaths };
  1219. LeaderboardLocal_Upload( gameLocal.lobbyUserIDs[ playerIdx ], gameLocal.gameType, stats );
  1220. }
  1221. // Flush all the collectively queued leaderboards all at once. ( Otherwise you get a busy signal on the second "flush" )
  1222. session->LeaderboardFlush();
  1223. }
  1224. // Award Any players that have not died. An Achievement
  1225. for( int i = 0; i < gameLocal.numClients; i++ ) {
  1226. idPlayer * player = static_cast< idPlayer* >( gameLocal.entities[ i ] );
  1227. if ( player == NULL ) {
  1228. continue;
  1229. }
  1230. // Only validate players that were in the tourney.
  1231. if ( gameLocal.gameType == GAME_TOURNEY ) {
  1232. if ( i != currentTourneyPlayer[ 0 ] && i != currentTourneyPlayer[ 1 ] ) {
  1233. continue;
  1234. }
  1235. }
  1236. // In LMS, players that join mid-game will not have ever died
  1237. if ( gameLocal.gameType == GAME_LASTMAN ) {
  1238. if ( playerState[i].fragCount == LASTMAN_NOLIVES ) {
  1239. continue;
  1240. }
  1241. }
  1242. if ( playerState[ i ].deaths == 0 ) {
  1243. player->GetAchievementManager().EventCompletesAchievement( ACHIEVEMENT_MP_COMPLETE_MATCH_WITHOUT_DYING );
  1244. }
  1245. }
  1246. }
  1247. /*
  1248. ================
  1249. idMultiplayerGame::Run
  1250. ================
  1251. */
  1252. void idMultiplayerGame::Run() {
  1253. int i, timeLeft;
  1254. idPlayer *player;
  1255. int gameReviewPause;
  1256. assert( common->IsMultiplayer() );
  1257. assert( !common->IsClient() );
  1258. pureReady = true;
  1259. if ( gameState == INACTIVE ) {
  1260. NewState( WARMUP );
  1261. }
  1262. CheckRespawns();
  1263. if ( nextState != INACTIVE && gameLocal.serverTime > nextStateSwitch ) {
  1264. NewState( nextState );
  1265. nextState = INACTIVE;
  1266. }
  1267. idLobbyBase & lobby = session->GetActingGameStateLobbyBase();
  1268. for ( i = 0; i < gameLocal.numClients; i++ ) {
  1269. idPlayer * player = static_cast<idPlayer *>( gameLocal.entities[i] );
  1270. if ( player != NULL ) {
  1271. playerState[i].ping = lobby.GetLobbyUserQoS( gameLocal.lobbyUserIDs[i] );
  1272. }
  1273. }
  1274. switch( gameState ) {
  1275. case GAMEREVIEW: {
  1276. if ( nextState == INACTIVE ) {
  1277. gameReviewPause = cvarSystem->GetCVarInteger( "g_gameReviewPause" );
  1278. nextState = NEXTGAME;
  1279. nextStateSwitch = gameLocal.serverTime + 1000 * gameReviewPause;
  1280. }
  1281. break;
  1282. }
  1283. case NEXTGAME: {
  1284. if ( nextState == INACTIVE ) {
  1285. // make sure flags are returned
  1286. if ( IsGametypeFlagBased() ) {
  1287. idItemTeam * flag;
  1288. flag = GetTeamFlag( 0 );
  1289. if ( flag ) {
  1290. flag->Return();
  1291. }
  1292. flag = GetTeamFlag( 1 );
  1293. if ( flag ) {
  1294. flag->Return();
  1295. }
  1296. }
  1297. NewState( WARMUP );
  1298. if ( gameLocal.gameType == GAME_TOURNEY ) {
  1299. CycleTourneyPlayers();
  1300. }
  1301. // put everyone back in from endgame spectate
  1302. for ( i = 0; i < gameLocal.numClients; i++ ) {
  1303. idEntity *ent = gameLocal.entities[ i ];
  1304. if ( ent && ent->IsType( idPlayer::Type ) ) {
  1305. if ( !static_cast< idPlayer * >( ent )->wantSpectate ) {
  1306. CheckRespawns( static_cast<idPlayer *>( ent ) );
  1307. }
  1308. }
  1309. }
  1310. }
  1311. break;
  1312. }
  1313. case WARMUP: {
  1314. if ( EnoughClientsToPlay() ) {
  1315. NewState( COUNTDOWN );
  1316. nextState = GAMEON;
  1317. nextStateSwitch = gameLocal.serverTime + 1000 * cvarSystem->GetCVarInteger( "g_countDown" );
  1318. }
  1319. one = two = three = false;
  1320. break;
  1321. }
  1322. case COUNTDOWN: {
  1323. timeLeft = ( nextStateSwitch - gameLocal.serverTime ) / 1000 + 1;
  1324. if ( timeLeft == 3 && !three ) {
  1325. PlayGlobalSound( -1, SND_THREE );
  1326. three = true;
  1327. } else if ( timeLeft == 2 && !two ) {
  1328. PlayGlobalSound( -1, SND_TWO );
  1329. two = true;
  1330. } else if ( timeLeft == 1 && !one ) {
  1331. PlayGlobalSound( -1, SND_ONE );
  1332. one = true;
  1333. }
  1334. break;
  1335. }
  1336. case GAMEON: {
  1337. if ( IsGametypeFlagBased() ) { /* CTF */
  1338. // totally different logic branch for CTF
  1339. if ( PointLimitHit() ) {
  1340. int team = WinningTeam();
  1341. assert( team != -1 );
  1342. NewState( GAMEREVIEW, NULL );
  1343. PrintMessageEvent( MSG_POINTLIMIT, team );
  1344. GameHasBeenWon();
  1345. } else if ( TimeLimitHit() ) {
  1346. int team = WinningTeam();
  1347. if ( EnoughClientsToPlay() && team == -1 ) {
  1348. NewState( SUDDENDEATH );
  1349. } else {
  1350. NewState( GAMEREVIEW, NULL );
  1351. PrintMessageEvent( MSG_TIMELIMIT );
  1352. GameHasBeenWon();
  1353. }
  1354. }
  1355. break;
  1356. }
  1357. player = FragLimitHit();
  1358. if ( player ) {
  1359. // delay between detecting frag limit and ending game. let the death anims play
  1360. if ( !fragLimitTimeout ) {
  1361. common->DPrintf( "enter FragLimit timeout, player %d is leader\n", player->entityNumber );
  1362. fragLimitTimeout = gameLocal.serverTime + FRAGLIMIT_DELAY;
  1363. }
  1364. if ( gameLocal.serverTime > fragLimitTimeout ) {
  1365. NewState( GAMEREVIEW, player );
  1366. PrintMessageEvent( MSG_FRAGLIMIT, IsGametypeTeamBased() ? player->team : player->entityNumber );
  1367. GameHasBeenWon();
  1368. }
  1369. } else {
  1370. if ( fragLimitTimeout ) {
  1371. // frag limit was hit and cancelled. means the two teams got even during FRAGLIMIT_DELAY
  1372. // enter sudden death, the next frag leader will win
  1373. SuddenRespawn();
  1374. PrintMessageEvent( MSG_HOLYSHIT );
  1375. fragLimitTimeout = 0;
  1376. NewState( SUDDENDEATH );
  1377. } else if ( TimeLimitHit() ) {
  1378. player = FragLeader();
  1379. if ( !player ) {
  1380. NewState( SUDDENDEATH );
  1381. } else {
  1382. NewState( GAMEREVIEW, player );
  1383. PrintMessageEvent( MSG_TIMELIMIT );
  1384. GameHasBeenWon();
  1385. }
  1386. } else {
  1387. BalanceTeams();
  1388. }
  1389. }
  1390. break;
  1391. }
  1392. case SUDDENDEATH: {
  1393. if ( IsGametypeFlagBased() ) { /* CTF */
  1394. int team = WinningTeam();
  1395. if ( team != -1 ) {
  1396. // TODO : implement pointLimitTimeout
  1397. NewState( GAMEREVIEW, NULL );
  1398. PrintMessageEvent( MSG_POINTLIMIT, team );
  1399. GameHasBeenWon();
  1400. }
  1401. break;
  1402. }
  1403. player = FragLeader();
  1404. if ( player ) {
  1405. if ( !fragLimitTimeout ) {
  1406. common->DPrintf( "enter sudden death FragLeader timeout, player %d is leader\n", player->entityNumber );
  1407. fragLimitTimeout = gameLocal.serverTime + FRAGLIMIT_DELAY;
  1408. }
  1409. if ( gameLocal.serverTime > fragLimitTimeout ) {
  1410. NewState( GAMEREVIEW, player );
  1411. PrintMessageEvent( MSG_FRAGLIMIT, IsGametypeTeamBased() ? player->team : player->entityNumber );
  1412. GameHasBeenWon();
  1413. }
  1414. } else if ( fragLimitTimeout ) {
  1415. SuddenRespawn();
  1416. PrintMessageEvent( MSG_HOLYSHIT );
  1417. fragLimitTimeout = 0;
  1418. }
  1419. break;
  1420. }
  1421. }
  1422. }
  1423. /*
  1424. ================
  1425. idMultiplayerGame::Draw
  1426. ================
  1427. */
  1428. bool idMultiplayerGame::Draw( int clientNum ) {
  1429. idPlayer *player, *viewPlayer;
  1430. // clear the render entities for any players that don't need
  1431. // icons and which might not be thinking because they weren't in
  1432. // the last snapshot.
  1433. for ( int i = 0; i < gameLocal.numClients; i++ ) {
  1434. player = static_cast<idPlayer *>( gameLocal.entities[ i ] );
  1435. if ( player && !player->NeedsIcon() ) {
  1436. player->HidePlayerIcons();
  1437. }
  1438. }
  1439. player = viewPlayer = static_cast<idPlayer *>( gameLocal.entities[ clientNum ] );
  1440. if ( player == NULL ) {
  1441. return false;
  1442. }
  1443. if ( player->spectating ) {
  1444. viewPlayer = static_cast<idPlayer *>( gameLocal.entities[ player->spectator ] );
  1445. if ( viewPlayer == NULL ) {
  1446. return false;
  1447. }
  1448. // if the player you are viewing is holding a flag, hide it.
  1449. idEntity* flag = GetTeamFlag( viewPlayer->team ? 0 : 1 );
  1450. if( flag ) {
  1451. if( viewPlayer->carryingFlag ) {
  1452. flag->Hide();
  1453. } else {
  1454. flag->Show();
  1455. }
  1456. }
  1457. }
  1458. UpdatePlayerRanks();
  1459. UpdateHud( viewPlayer, player->hudManager );
  1460. // use the hud of the local player
  1461. viewPlayer->playerView.RenderPlayerView( player->hudManager );
  1462. idStr spectatetext[ 2 ];
  1463. GetSpectateText( player, spectatetext, true );
  1464. if ( scoreboardManager != NULL ) {
  1465. scoreboardManager->UpdateSpectating( spectatetext[0].c_str(), spectatetext[1].c_str() );
  1466. }
  1467. DrawChat( player );
  1468. DrawScoreBoard( player );
  1469. return true;
  1470. }
  1471. /*
  1472. ================
  1473. idMultiplayerGame::GetSpectateText
  1474. ================
  1475. */
  1476. void idMultiplayerGame::GetSpectateText( idPlayer * player, idStr spectatetext[ 2 ], bool scoreboard ) {
  1477. if ( !player->spectating ) {
  1478. return;
  1479. }
  1480. if ( player->wantSpectate && !scoreboard ) {
  1481. if ( gameLocal.gameType == GAME_LASTMAN && gameState == GAMEON ) {
  1482. // If we're in GAMEON in lastman, you can't actully spawn, or you'll have an unfair
  1483. // advantage with more lives than everyone else.
  1484. spectatetext[ 0 ] = idLocalization::GetString( "#str_04246" );
  1485. spectatetext[ 0 ] += idLocalization::GetString( "#str_07003" );
  1486. } else {
  1487. if ( gameLocal.gameType == GAME_TOURNEY &&
  1488. ( currentTourneyPlayer[0] != -1 && currentTourneyPlayer[1] != -1 ) &&
  1489. ( currentTourneyPlayer[0] != player->entityNumber && currentTourneyPlayer[1] != player->entityNumber ) ) {
  1490. spectatetext[ 0 ] = idLocalization::GetString( "#str_04246" );
  1491. switch ( player->tourneyLine ) {
  1492. case 0:
  1493. spectatetext[ 0 ] += idLocalization::GetString( "#str_07003" );
  1494. break;
  1495. case 1:
  1496. spectatetext[ 0 ] += idLocalization::GetString( "#str_07004" );
  1497. break;
  1498. case 2:
  1499. spectatetext[ 0 ] += idLocalization::GetString( "#str_07005" );
  1500. break;
  1501. default:
  1502. spectatetext[ 0 ] += va( idLocalization::GetString( "#str_07006" ), player->tourneyLine );
  1503. break;
  1504. }
  1505. } else {
  1506. spectatetext[ 0 ] = idLocalization::GetString( "#str_respawn_message" );
  1507. }
  1508. }
  1509. } else {
  1510. if ( gameLocal.gameType == GAME_TOURNEY ) {
  1511. spectatetext[ 0 ] = idLocalization::GetString( "#str_04246" );
  1512. switch ( player->tourneyLine ) {
  1513. case 0:
  1514. spectatetext[ 0 ] += idLocalization::GetString( "#str_07003" );
  1515. break;
  1516. case 1:
  1517. spectatetext[ 0 ] += idLocalization::GetString( "#str_07004" );
  1518. break;
  1519. case 2:
  1520. spectatetext[ 0 ] += idLocalization::GetString( "#str_07005" );
  1521. break;
  1522. default:
  1523. spectatetext[ 0 ] += va( idLocalization::GetString( "#str_07006" ), player->tourneyLine );
  1524. break;
  1525. }
  1526. } else if ( gameLocal.gameType == GAME_LASTMAN ) {
  1527. spectatetext[ 0 ] = idLocalization::GetString( "#str_07007" );
  1528. } else {
  1529. spectatetext[ 0 ] = idLocalization::GetString( "#str_04246" );
  1530. }
  1531. }
  1532. if ( player->spectator != player->entityNumber ) {
  1533. idLobbyBase & lobby = session->GetActingGameStateLobbyBase();
  1534. spectatetext[ 1 ] = va( idLocalization::GetString( "#str_07008" ), lobby.GetLobbyUserName( gameLocal.lobbyUserIDs[ player->spectator ] ) );
  1535. }
  1536. }
  1537. /*
  1538. ================
  1539. idMultiplayerGame::UpdateHud
  1540. ================
  1541. */
  1542. void idMultiplayerGame::UpdateHud( idPlayer *player, idMenuHandler_HUD * hudManager ) {
  1543. if ( hudManager && hudManager->GetHud() ) {
  1544. idMenuScreen_HUD * hud = hudManager->GetHud();
  1545. if ( Warmup() ) {
  1546. hud->UpdateMessage( true, "#str_04251" );
  1547. if ( IsGametypeTeamBased() ) {
  1548. hud->SetTeamScore( 0, 0 );
  1549. hud->SetTeamScore( 1, 0 );
  1550. }
  1551. } else if ( gameState == SUDDENDEATH ) {
  1552. hud->UpdateMessage( true, "#str_04252" );
  1553. } else {
  1554. hud->UpdateGameTime( GameTime() );
  1555. }
  1556. if ( IsGametypeTeamBased() || IsGametypeFlagBased() ) {
  1557. hud->ToggleMPInfo( true, true, IsGametypeFlagBased() );
  1558. } else {
  1559. hud->ToggleMPInfo( true, false );
  1560. }
  1561. if ( gameState == GAMEON || gameState == COUNTDOWN || gameState == WARMUP ) {
  1562. if ( IsGametypeTeamBased() && !IsGametypeFlagBased() ) {
  1563. for ( int i = 0; i < gameLocal.numClients; ++i ) {
  1564. idEntity * ent = gameLocal.entities[ i ];
  1565. if ( ent == NULL || !ent->IsType( idPlayer::Type ) ) {
  1566. continue;
  1567. }
  1568. idPlayer * player = static_cast< idPlayer * >( ent );
  1569. hud->SetTeamScore( player->team, playerState[ player->entityNumber ].teamFragCount );
  1570. }
  1571. }
  1572. }
  1573. if ( IsGametypeFlagBased() || IsGametypeTeamBased() ) {
  1574. hud->SetTeam( player->team );
  1575. } else {
  1576. hud->SetTeam( -1 );
  1577. }
  1578. }
  1579. }
  1580. /*
  1581. ================
  1582. idMultiplayerGame::SetScoreboardActive
  1583. ================
  1584. */
  1585. void idMultiplayerGame::SetScoreboardActive( bool active ) {
  1586. if( scoreboardManager != NULL ) {
  1587. if( active ) {
  1588. if ( IsGametypeTeamBased() || IsGametypeFlagBased() ) {
  1589. scoreboardManager->SetActivationScreen( SCOREBOARD_AREA_TEAM, MENU_TRANSITION_SIMPLE );
  1590. } else {
  1591. scoreboardManager->SetActivationScreen( SCOREBOARD_AREA_DEFAULT, MENU_TRANSITION_SIMPLE );
  1592. }
  1593. scoreboardManager->ActivateMenu( true );
  1594. } else {
  1595. scoreboardManager->ActivateMenu( false );
  1596. }
  1597. }
  1598. }
  1599. /*
  1600. ================
  1601. idMultiplayerGame::DrawScoreBoard
  1602. ================
  1603. */
  1604. void idMultiplayerGame::DrawScoreBoard( idPlayer *player ) {
  1605. if ( scoreboardManager && scoreboardManager->IsActive() == true ) {
  1606. UpdateScoreboard( scoreboardManager, player );
  1607. }
  1608. }
  1609. /*
  1610. ===============
  1611. idMultiplayerGame::ClearChatData
  1612. ===============
  1613. */
  1614. void idMultiplayerGame::ClearChatData() {
  1615. chatHistoryIndex = 0;
  1616. chatHistorySize = 0;
  1617. chatDataUpdated = true;
  1618. }
  1619. /*
  1620. ===============
  1621. idMultiplayerGame::AddChatLine
  1622. ===============
  1623. */
  1624. void idMultiplayerGame::AddChatLine( const char *fmt, ... ) {
  1625. idStr temp;
  1626. va_list argptr;
  1627. va_start( argptr, fmt );
  1628. vsprintf( temp, fmt, argptr );
  1629. va_end( argptr );
  1630. gameLocal.Printf( "%s\n", temp.c_str() );
  1631. chatHistory[ chatHistoryIndex % NUM_CHAT_NOTIFY ].line = temp;
  1632. chatHistory[ chatHistoryIndex % NUM_CHAT_NOTIFY ].fade = 6;
  1633. chatHistoryIndex++;
  1634. if ( chatHistorySize < NUM_CHAT_NOTIFY ) {
  1635. chatHistorySize++;
  1636. }
  1637. chatDataUpdated = true;
  1638. lastChatLineTime = Sys_Milliseconds();
  1639. }
  1640. /*
  1641. ===============
  1642. idMultiplayerGame::DrawChat
  1643. ===============
  1644. */
  1645. void idMultiplayerGame::DrawChat( idPlayer * player ) {
  1646. int i, j;
  1647. if ( player ) {
  1648. if ( Sys_Milliseconds() - lastChatLineTime > CHAT_FADE_TIME ) {
  1649. if ( chatHistorySize > 0 ) {
  1650. for ( i = chatHistoryIndex - chatHistorySize; i < chatHistoryIndex; i++ ) {
  1651. chatHistory[ i % NUM_CHAT_NOTIFY ].fade--;
  1652. if ( chatHistory[ i % NUM_CHAT_NOTIFY ].fade < 0 ) {
  1653. chatHistorySize--; // this assumes the removals are always at the beginning
  1654. }
  1655. }
  1656. chatDataUpdated = true;
  1657. }
  1658. lastChatLineTime = Sys_Milliseconds();
  1659. }
  1660. if ( chatDataUpdated ) {
  1661. j = 0;
  1662. i = chatHistoryIndex - chatHistorySize;
  1663. while ( i < chatHistoryIndex ) {
  1664. player->AddChatMessage( j, Min( 4, (int)chatHistory[ i % NUM_CHAT_NOTIFY ].fade ), chatHistory[ i % NUM_CHAT_NOTIFY ].line );
  1665. j++; i++;
  1666. }
  1667. while ( j < NUM_CHAT_NOTIFY ) {
  1668. player->ClearChatMessage( j );
  1669. j++;
  1670. }
  1671. chatDataUpdated = false;
  1672. }
  1673. }
  1674. }
  1675. //D3XP: Adding one to frag count to allow for the negative flag in numbers greater than 255
  1676. const int ASYNC_PLAYER_FRAG_BITS = -(idMath::BitsForInteger( MP_PLAYER_MAXFRAGS - MP_PLAYER_MINFRAGS )+1); // player can have negative frags
  1677. const int ASYNC_PLAYER_WINS_BITS = idMath::BitsForInteger( MP_PLAYER_MAXWINS );
  1678. const int ASYNC_PLAYER_PING_BITS = idMath::BitsForInteger( MP_PLAYER_MAXPING );
  1679. /*
  1680. ================
  1681. idMultiplayerGame::WriteToSnapshot
  1682. ================
  1683. */
  1684. void idMultiplayerGame::WriteToSnapshot( idBitMsg &msg ) const {
  1685. int i;
  1686. int value;
  1687. // This is a hack - I need a place to read the lobby ids before the player entities are
  1688. // read (SpawnPlayer requires a valid lobby id for the player).
  1689. for ( int i = 0; i < gameLocal.lobbyUserIDs.Num(); ++i ) {
  1690. gameLocal.lobbyUserIDs[i].WriteToMsg( msg );
  1691. }
  1692. msg.WriteByte( gameState );
  1693. msg.WriteLong( nextStateSwitch );
  1694. msg.WriteShort( currentTourneyPlayer[ 0 ] );
  1695. msg.WriteShort( currentTourneyPlayer[ 1 ] );
  1696. for ( i = 0; i < MAX_CLIENTS; i++ ) {
  1697. // clamp all values to min/max possible value that we can send over
  1698. value = idMath::ClampInt( MP_PLAYER_MINFRAGS, MP_PLAYER_MAXFRAGS, playerState[i].fragCount );
  1699. msg.WriteBits( value, ASYNC_PLAYER_FRAG_BITS );
  1700. value = idMath::ClampInt( MP_PLAYER_MINFRAGS, MP_PLAYER_MAXFRAGS, playerState[i].teamFragCount );
  1701. msg.WriteBits( value, ASYNC_PLAYER_FRAG_BITS );
  1702. value = idMath::ClampInt( 0, MP_PLAYER_MAXWINS, playerState[i].wins );
  1703. msg.WriteBits( value, ASYNC_PLAYER_WINS_BITS );
  1704. value = idMath::ClampInt( 0, MP_PLAYER_MAXPING, playerState[i].ping );
  1705. msg.WriteBits( value, ASYNC_PLAYER_PING_BITS );
  1706. }
  1707. msg.WriteShort( teamPoints[0] );
  1708. msg.WriteShort( teamPoints[1] );
  1709. msg.WriteShort( player_red_flag );
  1710. msg.WriteShort( player_blue_flag );
  1711. }
  1712. /*
  1713. ================
  1714. idMultiplayerGame::ReadFromSnapshot
  1715. ================
  1716. */
  1717. void idMultiplayerGame::ReadFromSnapshot( const idBitMsg &msg ) {
  1718. int i;
  1719. gameState_t newState;
  1720. // This is a hack - I need a place to read the lobby ids before the player entities are
  1721. // read (SpawnPlayer requires a valid lobby id for the player).
  1722. for ( int i = 0; i < gameLocal.lobbyUserIDs.Num(); ++i ) {
  1723. gameLocal.lobbyUserIDs[i].ReadFromMsg( msg );
  1724. }
  1725. newState = (idMultiplayerGame::gameState_t)msg.ReadByte();
  1726. nextStateSwitch = msg.ReadLong();
  1727. if ( newState != gameState && newState < STATE_COUNT ) {
  1728. gameLocal.DPrintf( "%s -> %s\n", GameStateStrings[ gameState ], GameStateStrings[ newState ] );
  1729. gameState = newState;
  1730. switch( gameState ) {
  1731. case GAMEON: {
  1732. NewState_GameOn_ServerAndClient();
  1733. break;
  1734. }
  1735. case GAMEREVIEW: {
  1736. NewState_GameReview_ServerAndClient();
  1737. break;
  1738. }
  1739. case WARMUP: {
  1740. NewState_Warmup_ServerAndClient();
  1741. break;
  1742. }
  1743. case COUNTDOWN: {
  1744. NewState_Countdown_ServerAndClient();
  1745. break;
  1746. }
  1747. }
  1748. }
  1749. currentTourneyPlayer[ 0 ] = msg.ReadShort();
  1750. currentTourneyPlayer[ 1 ] = msg.ReadShort();
  1751. for ( i = 0; i < MAX_CLIENTS; i++ ) {
  1752. playerState[i].fragCount = msg.ReadBits( ASYNC_PLAYER_FRAG_BITS );
  1753. playerState[i].teamFragCount = msg.ReadBits( ASYNC_PLAYER_FRAG_BITS );
  1754. playerState[i].wins = msg.ReadBits( ASYNC_PLAYER_WINS_BITS );
  1755. playerState[i].ping = msg.ReadBits( ASYNC_PLAYER_PING_BITS );
  1756. }
  1757. teamPoints[0] = msg.ReadShort();
  1758. teamPoints[1] = msg.ReadShort();
  1759. player_red_flag = msg.ReadShort();
  1760. player_blue_flag = msg.ReadShort();
  1761. }
  1762. /*
  1763. ================
  1764. idMultiplayerGame::PlayGlobalSound
  1765. ================
  1766. */
  1767. void idMultiplayerGame::PlayGlobalSound( int toPlayerNum, snd_evt_t evt, const char *shader ) {
  1768. if ( toPlayerNum < 0 || toPlayerNum == gameLocal.GetLocalClientNum() ) {
  1769. if ( shader ) {
  1770. if ( gameSoundWorld ) {
  1771. gameSoundWorld->PlayShaderDirectly( shader );
  1772. }
  1773. } else {
  1774. if ( gameSoundWorld ) {
  1775. gameSoundWorld->PlayShaderDirectly( GlobalSoundStrings[ evt ] );
  1776. }
  1777. }
  1778. }
  1779. if ( !common->IsClient() && toPlayerNum != gameLocal.GetLocalClientNum() ) {
  1780. idBitMsg outMsg;
  1781. byte msgBuf[1024];
  1782. outMsg.InitWrite( msgBuf, sizeof( msgBuf ) );
  1783. int type = 0;
  1784. if ( shader ) {
  1785. const idSoundShader * shaderDecl = declManager->FindSound( shader );
  1786. if ( !shaderDecl ) {
  1787. return;
  1788. }
  1789. outMsg.WriteLong( gameLocal.ServerRemapDecl( -1, DECL_SOUND, shaderDecl->Index() ) );
  1790. type = GAME_RELIABLE_MESSAGE_SOUND_INDEX;
  1791. } else {
  1792. outMsg.WriteByte( evt );
  1793. type = GAME_RELIABLE_MESSAGE_SOUND_EVENT;
  1794. }
  1795. if ( toPlayerNum >= 0 ) {
  1796. session->GetActingGameStateLobbyBase().SendReliableToLobbyUser( gameLocal.lobbyUserIDs[toPlayerNum], type, outMsg );
  1797. } else {
  1798. session->GetActingGameStateLobbyBase().SendReliable( type, outMsg, false );
  1799. }
  1800. }
  1801. }
  1802. /*
  1803. ================
  1804. idMultiplayerGame::PlayTeamSound
  1805. ================
  1806. */
  1807. void idMultiplayerGame::PlayTeamSound( int toTeam, snd_evt_t evt, const char *shader ) {
  1808. for( int i = 0; i < gameLocal.numClients; i++ ) {
  1809. idEntity *ent = gameLocal.entities[ i ];
  1810. if ( !ent || !ent->IsType( idPlayer::Type ) ) {
  1811. continue;
  1812. }
  1813. idPlayer * player = static_cast<idPlayer*>(ent);
  1814. if ( player->team != toTeam )
  1815. continue;
  1816. PlayGlobalSound( i, evt, shader );
  1817. }
  1818. }
  1819. /*
  1820. ================
  1821. idMultiplayerGame::PrintMessageEvent
  1822. ================
  1823. */
  1824. void idMultiplayerGame::PrintMessageEvent( msg_evt_t evt, int parm1, int parm2 ) {
  1825. idLobbyBase & lobby = session->GetActingGameStateLobbyBase();
  1826. switch ( evt ) {
  1827. case MSG_LEFTGAME:
  1828. assert( parm1 >= 0 );
  1829. AddChatLine( idLocalization::GetString( "#str_11604" ), lobby.GetLobbyUserName( gameLocal.lobbyUserIDs[parm1] ) );
  1830. break;
  1831. case MSG_SUICIDE:
  1832. assert( parm1 >= 0 );
  1833. AddChatLine( idLocalization::GetString( "#str_04293" ), lobby.GetLobbyUserName( gameLocal.lobbyUserIDs[parm1] ) );
  1834. break;
  1835. case MSG_KILLED:
  1836. assert( parm1 >= 0 && parm2 >= 0 );
  1837. AddChatLine( idLocalization::GetString( "#str_04292" ), lobby.GetLobbyUserName( gameLocal.lobbyUserIDs[parm1] ), lobby.GetLobbyUserName( gameLocal.lobbyUserIDs[parm2] ) );
  1838. break;
  1839. case MSG_KILLEDTEAM:
  1840. assert( parm1 >= 0 && parm2 >= 0 );
  1841. AddChatLine( idLocalization::GetString( "#str_04291" ), lobby.GetLobbyUserName( gameLocal.lobbyUserIDs[parm1] ), lobby.GetLobbyUserName( gameLocal.lobbyUserIDs[parm2] ) );
  1842. break;
  1843. case MSG_TELEFRAGGED:
  1844. assert( parm1 >= 0 && parm2 >= 0 );
  1845. AddChatLine( idLocalization::GetString( "#str_04290" ), lobby.GetLobbyUserName( gameLocal.lobbyUserIDs[parm1] ), lobby.GetLobbyUserName( gameLocal.lobbyUserIDs[parm2] ) );
  1846. break;
  1847. case MSG_DIED:
  1848. assert( parm1 >= 0 );
  1849. AddChatLine( idLocalization::GetString( "#str_04289" ), lobby.GetLobbyUserName( gameLocal.lobbyUserIDs[parm1] ) );
  1850. break;
  1851. case MSG_SUDDENDEATH:
  1852. AddChatLine( idLocalization::GetString( "#str_04287" ) );
  1853. break;
  1854. case MSG_JOINEDSPEC:
  1855. AddChatLine( idLocalization::GetString( "#str_04285" ), lobby.GetLobbyUserName( gameLocal.lobbyUserIDs[parm1] ) );
  1856. break;
  1857. case MSG_TIMELIMIT:
  1858. AddChatLine( idLocalization::GetString( "#str_04284" ) );
  1859. break;
  1860. case MSG_FRAGLIMIT:
  1861. if ( gameLocal.gameType == GAME_LASTMAN ) {
  1862. AddChatLine( idLocalization::GetString( "#str_04283" ), lobby.GetLobbyUserName( gameLocal.lobbyUserIDs[parm1] ) );
  1863. } else if ( IsGametypeTeamBased() ) { /* CTF */
  1864. AddChatLine( idLocalization::GetString( "#str_04282" ), idLocalization::GetString( teamNames[ parm1 ] ) );
  1865. } else {
  1866. AddChatLine( idLocalization::GetString( "#str_04281" ), lobby.GetLobbyUserName( gameLocal.lobbyUserIDs[parm1] ) );
  1867. }
  1868. break;
  1869. case MSG_JOINTEAM:
  1870. AddChatLine( idLocalization::GetString( "#str_04280" ), lobby.GetLobbyUserName( gameLocal.lobbyUserIDs[parm1] ), parm2 ? idLocalization::GetString( "#str_02500" ) : idLocalization::GetString( "#str_02499" ) );
  1871. break;
  1872. case MSG_HOLYSHIT:
  1873. AddChatLine( idLocalization::GetString( "#str_06732" ) );
  1874. break;
  1875. case MSG_POINTLIMIT:
  1876. AddChatLine( idLocalization::GetString( "#str_11100" ), parm1 ? idLocalization::GetString( "#str_11110" ) : idLocalization::GetString( "#str_11111" ) );
  1877. break;
  1878. case MSG_FLAGTAKEN :
  1879. if ( gameLocal.GetLocalPlayer() == NULL )
  1880. break;
  1881. if ( parm2 < 0 || parm2 >= MAX_CLIENTS )
  1882. break;
  1883. if ( gameLocal.GetLocalPlayer()->team != parm1 ) {
  1884. AddChatLine( idLocalization::GetString( "#str_11101" ), lobby.GetLobbyUserName( gameLocal.lobbyUserIDs[parm2] ) ); // your team
  1885. } else {
  1886. AddChatLine( idLocalization::GetString( "#str_11102" ), lobby.GetLobbyUserName( gameLocal.lobbyUserIDs[parm2] ) ); // enemy
  1887. }
  1888. break;
  1889. case MSG_FLAGDROP :
  1890. if ( gameLocal.GetLocalPlayer() == NULL )
  1891. break;
  1892. if ( gameLocal.GetLocalPlayer()->team != parm1 ) {
  1893. AddChatLine( idLocalization::GetString( "#str_11103" ) ); // your team
  1894. } else {
  1895. AddChatLine( idLocalization::GetString( "#str_11104" ) ); // enemy
  1896. }
  1897. break;
  1898. case MSG_FLAGRETURN :
  1899. if ( gameLocal.GetLocalPlayer() == NULL )
  1900. break;
  1901. if ( parm2 >= 0 && parm2 < MAX_CLIENTS ) {
  1902. if ( gameLocal.GetLocalPlayer()->team != parm1 ) {
  1903. AddChatLine( idLocalization::GetString( "#str_11120" ), lobby.GetLobbyUserName( gameLocal.lobbyUserIDs[parm2] ) ); // your team
  1904. } else {
  1905. AddChatLine( idLocalization::GetString( "#str_11121" ), lobby.GetLobbyUserName( gameLocal.lobbyUserIDs[parm2] ) ); // enemy
  1906. }
  1907. } else {
  1908. AddChatLine( idLocalization::GetString( "#str_11105" ), parm1 ? idLocalization::GetString( "#str_11110" ) : idLocalization::GetString( "#str_11111" ) );
  1909. }
  1910. break;
  1911. case MSG_FLAGCAPTURE :
  1912. if ( gameLocal.GetLocalPlayer() == NULL )
  1913. break;
  1914. if ( parm2 < 0 || parm2 >= MAX_CLIENTS )
  1915. break;
  1916. if ( gameLocal.GetLocalPlayer()->team != parm1 ) {
  1917. AddChatLine( idLocalization::GetString( "#str_11122" ), lobby.GetLobbyUserName( gameLocal.lobbyUserIDs[parm2] ) ); // your team
  1918. } else {
  1919. AddChatLine( idLocalization::GetString( "#str_11123" ), lobby.GetLobbyUserName( gameLocal.lobbyUserIDs[parm2] ) ); // enemy
  1920. }
  1921. // AddChatLine( idLocalization::GetString( "#str_11106" ), parm1 ? idLocalization::GetString( "#str_11110" ) : idLocalization::GetString( "#str_11111" ) );
  1922. break;
  1923. case MSG_SCOREUPDATE:
  1924. AddChatLine( idLocalization::GetString( "#str_11107" ), parm1, parm2 );
  1925. break;
  1926. default:
  1927. gameLocal.DPrintf( "PrintMessageEvent: unknown message type %d\n", evt );
  1928. return;
  1929. }
  1930. if ( !common->IsClient() ) {
  1931. idBitMsg outMsg;
  1932. byte msgBuf[1024];
  1933. outMsg.InitWrite( msgBuf, sizeof( msgBuf ) );
  1934. outMsg.WriteByte( evt );
  1935. outMsg.WriteByte( parm1 );
  1936. outMsg.WriteByte( parm2 );
  1937. session->GetActingGameStateLobbyBase().SendReliable( GAME_RELIABLE_MESSAGE_DB, outMsg, false );
  1938. }
  1939. }
  1940. /*
  1941. ================
  1942. idMultiplayerGame::SuddenRespawns
  1943. solely for LMN if an end game ( fragLimitTimeout ) was entered and aborted before expiration
  1944. LMN players which still have lives left need to be respawned without being marked lastManOver
  1945. ================
  1946. */
  1947. void idMultiplayerGame::SuddenRespawn() {
  1948. int i;
  1949. if ( gameLocal.gameType != GAME_LASTMAN ) {
  1950. return;
  1951. }
  1952. for ( i = 0; i < gameLocal.numClients; i++ ) {
  1953. if ( !gameLocal.entities[ i ] || !gameLocal.entities[ i ]->IsType( idPlayer::Type ) ) {
  1954. continue;
  1955. }
  1956. if ( !CanPlay( static_cast< idPlayer * >( gameLocal.entities[ i ] ) ) ) {
  1957. continue;
  1958. }
  1959. if ( static_cast< idPlayer * >( gameLocal.entities[ i ] )->lastManOver ) {
  1960. continue;
  1961. }
  1962. static_cast< idPlayer * >( gameLocal.entities[ i ] )->lastManPlayAgain = true;
  1963. }
  1964. }
  1965. /*
  1966. ================
  1967. idMultiplayerGame::CheckSpawns
  1968. ================
  1969. */
  1970. void idMultiplayerGame::CheckRespawns( idPlayer *spectator ) {
  1971. for( int i = 0 ; i < gameLocal.numClients ; i++ ) {
  1972. idEntity *ent = gameLocal.entities[ i ];
  1973. if ( !ent || !ent->IsType( idPlayer::Type ) ) {
  1974. continue;
  1975. }
  1976. idPlayer *p = static_cast<idPlayer *>(ent);
  1977. // once we hit sudden death, nobody respawns till game has ended
  1978. if ( WantRespawn( p ) || p == spectator ) {
  1979. if ( gameState == SUDDENDEATH && gameLocal.gameType != GAME_LASTMAN ) {
  1980. // respawn rules while sudden death are different
  1981. // sudden death may trigger while a player is dead, so there are still cases where we need to respawn
  1982. // don't do any respawns while we are in end game delay though
  1983. if ( !fragLimitTimeout ) {
  1984. if ( IsGametypeTeamBased() || p->IsLeader() ) { /* CTF */
  1985. #ifdef _DEBUG
  1986. if ( gameLocal.gameType == GAME_TOURNEY ) {
  1987. assert( p->entityNumber == currentTourneyPlayer[ 0 ] || p->entityNumber == currentTourneyPlayer[ 1 ] );
  1988. }
  1989. #endif
  1990. p->ServerSpectate( false );
  1991. } else if ( !p->IsLeader() ) {
  1992. // sudden death is rolling, this player is not a leader, have him spectate
  1993. p->ServerSpectate( true );
  1994. CheckAbortGame();
  1995. }
  1996. }
  1997. } else {
  1998. if ( gameLocal.gameType == GAME_DM || // CTF : 3wave sboily, was DM really included before?
  1999. IsGametypeTeamBased() )
  2000. {
  2001. if ( gameState == WARMUP || gameState == COUNTDOWN || gameState == GAMEON ) {
  2002. p->ServerSpectate( false );
  2003. }
  2004. } else if ( gameLocal.gameType == GAME_TOURNEY ) {
  2005. if ( i == currentTourneyPlayer[ 0 ] || i == currentTourneyPlayer[ 1 ] ) {
  2006. if ( gameState == WARMUP || gameState == COUNTDOWN || gameState == GAMEON ) {
  2007. p->ServerSpectate( false );
  2008. idLib::Printf( "TOURNEY CheckRespawns :> Player %d On Deck \n", p->entityNumber );
  2009. }
  2010. } else if ( gameState == WARMUP ) {
  2011. // make sure empty tourney slots get filled first
  2012. FillTourneySlots( );
  2013. if ( i == currentTourneyPlayer[ 0 ] || i == currentTourneyPlayer[ 1 ] ) {
  2014. p->ServerSpectate( false );
  2015. idLib::Printf( "TOURNEY CheckRespawns WARMUP :> Player %d On Deck \n", p->entityNumber );
  2016. }
  2017. }
  2018. } else if ( gameLocal.gameType == GAME_LASTMAN ) {
  2019. if ( gameState == WARMUP || gameState == COUNTDOWN ) {
  2020. // Player has spawned in game, give him lives.
  2021. playerState[ i ].fragCount = gameLocal.serverInfo.GetInt( "si_fragLimit" );
  2022. p->ServerSpectate( false );
  2023. } else if ( gameState == GAMEON || gameState == SUDDENDEATH ) {
  2024. if ( gameState == GAMEON && playerState[ i ].fragCount > 0 && p->lastManPresent ) {
  2025. assert( !p->lastManOver );
  2026. p->ServerSpectate( false );
  2027. } else if ( p->lastManPlayAgain && p->lastManPresent ) {
  2028. assert( gameState == SUDDENDEATH );
  2029. p->ServerSpectate( false );
  2030. } else {
  2031. // if a fragLimitTimeout was engaged, do NOT mark lastManOver as that could mean
  2032. // everyone ends up spectator and game is stalled with no end
  2033. // if the frag limit delay is engaged and cancels out before expiring, LMN players are
  2034. // respawned to play the tie again ( through SuddenRespawn and lastManPlayAgain )
  2035. if ( !fragLimitTimeout && !p->lastManOver ) {
  2036. common->DPrintf( "client %d has lost all last man lives\n", i );
  2037. // end of the game for this guy, send him to spectators
  2038. p->lastManOver = true;
  2039. // clients don't have access to lastManOver
  2040. // so set the fragCount to something silly ( used in scoreboard and player ranking )
  2041. playerState[ i ].fragCount = LASTMAN_NOLIVES;
  2042. p->ServerSpectate( true );
  2043. //Check for a situation where the last two player dies at the same time and don't
  2044. //try to respawn manually...This was causing all players to go into spectate mode
  2045. //and the server got stuck
  2046. {
  2047. int j;
  2048. for ( j = 0; j < gameLocal.numClients; j++ ) {
  2049. if ( !gameLocal.entities[ j ] ) {
  2050. continue;
  2051. }
  2052. if ( !CanPlay( static_cast< idPlayer * >( gameLocal.entities[ j ] ) ) ) {
  2053. continue;
  2054. }
  2055. if ( !static_cast< idPlayer * >( gameLocal.entities[ j ] )->lastManOver ) {
  2056. break;
  2057. }
  2058. }
  2059. if( j == gameLocal.numClients) {
  2060. //Everyone is dead so don't allow this player to spectate
  2061. //so the match will end
  2062. p->ServerSpectate( false );
  2063. }
  2064. }
  2065. }
  2066. }
  2067. }
  2068. }
  2069. }
  2070. } else if ( p->wantSpectate && !p->spectating ) {
  2071. playerState[ i ].fragCount = 0; // whenever you willingly go spectate during game, your score resets
  2072. p->ServerSpectate( true );
  2073. idLib::Printf( "TOURNEY CheckRespawns :> Player %d Wants Spectate \n", p->entityNumber );
  2074. UpdateTourneyLine();
  2075. CheckAbortGame();
  2076. }
  2077. }
  2078. }
  2079. /*
  2080. ================
  2081. idMultiplayerGame::DropWeapon
  2082. ================
  2083. */
  2084. void idMultiplayerGame::DropWeapon( int clientNum ) {
  2085. assert( !common->IsClient() );
  2086. idEntity *ent = gameLocal.entities[ clientNum ];
  2087. if ( !ent || !ent->IsType( idPlayer::Type ) ) {
  2088. return;
  2089. }
  2090. static_cast< idPlayer* >( ent )->DropWeapon( false );
  2091. }
  2092. /*
  2093. ================
  2094. idMultiplayerGame::DropWeapon_f
  2095. ================
  2096. */
  2097. void idMultiplayerGame::DropWeapon_f( const idCmdArgs &args ) {
  2098. if ( !common->IsMultiplayer() ) {
  2099. common->Printf( "clientDropWeapon: only valid in multiplayer\n" );
  2100. return;
  2101. }
  2102. idBitMsg outMsg;
  2103. session->GetActingGameStateLobbyBase().SendReliableToHost( GAME_RELIABLE_MESSAGE_DROPWEAPON, outMsg );
  2104. }
  2105. /*
  2106. ================
  2107. idMultiplayerGame::MessageMode_f
  2108. ================
  2109. */
  2110. void idMultiplayerGame::MessageMode_f( const idCmdArgs &args ) {
  2111. if ( !common->IsMultiplayer() ) {
  2112. return;
  2113. }
  2114. gameLocal.mpGame.MessageMode( args );
  2115. }
  2116. /*
  2117. ================
  2118. idMultiplayerGame::MessageMode
  2119. ================
  2120. */
  2121. void idMultiplayerGame::MessageMode( const idCmdArgs &args ) {
  2122. idEntity *ent = gameLocal.entities[ gameLocal.GetLocalClientNum() ];
  2123. if ( !ent || !ent->IsType( idPlayer::Type ) ) {
  2124. return;
  2125. }
  2126. idPlayer * player = static_cast< idPlayer* >( ent );
  2127. if ( player && !player->spectating ) {
  2128. if ( args.Argc() != 2 ) {
  2129. player->isChatting = 1;
  2130. } else {
  2131. player->isChatting = 2;
  2132. }
  2133. }
  2134. }
  2135. /*
  2136. ================
  2137. idMultiplayerGame::DisconnectClient
  2138. ================
  2139. */
  2140. void idMultiplayerGame::DisconnectClient( int clientNum ) {
  2141. if ( lastWinner == clientNum ) {
  2142. lastWinner = -1;
  2143. }
  2144. // Show that the user has left the game.
  2145. PrintMessageEvent( MSG_LEFTGAME, clientNum, -1 );
  2146. UpdatePlayerRanks();
  2147. CheckAbortGame();
  2148. }
  2149. /*
  2150. ================
  2151. idMultiplayerGame::CheckAbortGame
  2152. ================
  2153. */
  2154. void idMultiplayerGame::CheckAbortGame() {
  2155. int i;
  2156. if ( gameLocal.gameType == GAME_TOURNEY && gameState == WARMUP ) {
  2157. // if a tourney player joined spectators, let someone else have his spot
  2158. for ( i = 0; i < 2; i++ ) {
  2159. if ( !gameLocal.entities[ currentTourneyPlayer[ i ] ] || static_cast< idPlayer * >( gameLocal.entities[ currentTourneyPlayer[ i ] ] )->spectating ) {
  2160. currentTourneyPlayer[ i ] = -1;
  2161. }
  2162. }
  2163. }
  2164. // only checks for aborts -> game review below
  2165. if ( gameState != COUNTDOWN && gameState != GAMEON && gameState != SUDDENDEATH ) {
  2166. return;
  2167. }
  2168. switch ( gameLocal.gameType ) {
  2169. case GAME_TOURNEY:
  2170. for ( i = 0; i < 2; i++ ) {
  2171. idPlayer * player = static_cast< idPlayer * >( gameLocal.entities[ currentTourneyPlayer[ i ] ] );
  2172. if ( !gameLocal.entities[ currentTourneyPlayer[ i ] ] || player->spectating ) {
  2173. NewState( GAMEREVIEW );
  2174. return;
  2175. }
  2176. }
  2177. break;
  2178. default:
  2179. if ( !EnoughClientsToPlay() ) {
  2180. NewState( GAMEREVIEW );
  2181. }
  2182. break;
  2183. }
  2184. }
  2185. /*
  2186. ================
  2187. idMultiplayerGame::MapRestart
  2188. ================
  2189. */
  2190. void idMultiplayerGame::MapRestart() {
  2191. assert( !common->IsClient() );
  2192. if ( gameState != WARMUP ) {
  2193. NewState( WARMUP );
  2194. nextState = INACTIVE;
  2195. nextStateSwitch = 0;
  2196. }
  2197. teamPoints[0] = 0;
  2198. teamPoints[1] = 0;
  2199. BalanceTeams();
  2200. }
  2201. /*
  2202. ================
  2203. idMultiplayerGame::BalanceTeams
  2204. ================
  2205. */
  2206. void idMultiplayerGame::BalanceTeams() {
  2207. if ( !IsGametypeTeamBased() ) {
  2208. return;
  2209. }
  2210. int teamCount[ 2 ] = { 0, 0 };
  2211. NumActualClients( false, teamCount );
  2212. int outOfBalance = abs( teamCount[0] - teamCount[1] );
  2213. if ( outOfBalance <= 1 ) {
  2214. return;
  2215. }
  2216. // Teams are out of balance
  2217. // Move N players from the large team to the small team
  2218. int numPlayersToMove = outOfBalance / 2;
  2219. int smallTeam = MinIndex( teamCount[0], teamCount[1] );
  2220. int largeTeam = 1 - smallTeam;
  2221. idLobbyBase & lobby = session->GetActingGameStateLobbyBase();
  2222. // First move players from the large team that match a party token on the small team
  2223. for ( int a = 0; a < gameLocal.numClients; a++ ) {
  2224. idPlayer * playerA = gameLocal.GetClientByNum( a );
  2225. if ( playerA->team == largeTeam && CanPlay( playerA ) ) {
  2226. for ( int b = 0; b < gameLocal.numClients; b++ ) {
  2227. if ( a == b ) {
  2228. continue;
  2229. }
  2230. idPlayer * playerB = gameLocal.GetClientByNum( b );
  2231. if ( playerB->team == smallTeam && CanPlay( playerB ) ) {
  2232. if ( lobby.GetLobbyUserPartyToken( gameLocal.lobbyUserIDs[ a ] ) == lobby.GetLobbyUserPartyToken( gameLocal.lobbyUserIDs[ b ] ) ) {
  2233. SwitchToTeam( a, largeTeam, smallTeam );
  2234. numPlayersToMove--;
  2235. if ( numPlayersToMove == 0 ) {
  2236. return;
  2237. }
  2238. break;
  2239. }
  2240. }
  2241. }
  2242. }
  2243. }
  2244. // Then move players from the large team that DON'T match party tokens on the large team
  2245. for ( int a = 0; a < gameLocal.numClients; a++ ) {
  2246. idPlayer * playerA = gameLocal.GetClientByNum( a );
  2247. if ( playerA->team == largeTeam && CanPlay( playerA ) ) {
  2248. bool match = false;
  2249. for ( int b = 0; b < gameLocal.numClients; b++ ) {
  2250. if ( a == b ) {
  2251. continue;
  2252. }
  2253. idPlayer * playerB = gameLocal.GetClientByNum( b );
  2254. if ( playerB->team == largeTeam && CanPlay( playerB ) ) {
  2255. if ( lobby.GetLobbyUserPartyToken( gameLocal.lobbyUserIDs[ a ] ) == lobby.GetLobbyUserPartyToken( gameLocal.lobbyUserIDs[ b ] ) ) {
  2256. match = true;
  2257. break;
  2258. }
  2259. }
  2260. }
  2261. if ( !match ) {
  2262. SwitchToTeam( a, largeTeam, smallTeam );
  2263. numPlayersToMove--;
  2264. if ( numPlayersToMove == 0 ) {
  2265. return;
  2266. }
  2267. }
  2268. }
  2269. }
  2270. // Then move any players from the large team to the small team
  2271. for ( int a = 0; a < gameLocal.numClients; a++ ) {
  2272. idPlayer * playerA = gameLocal.GetClientByNum( a );
  2273. if ( playerA->team == largeTeam && CanPlay( playerA ) ) {
  2274. SwitchToTeam( a, largeTeam, smallTeam );
  2275. numPlayersToMove--;
  2276. if ( numPlayersToMove == 0 ) {
  2277. return;
  2278. }
  2279. }
  2280. }
  2281. }
  2282. /*
  2283. ================
  2284. idMultiplayerGame::SwitchToTeam
  2285. ================
  2286. */
  2287. void idMultiplayerGame::SwitchToTeam( int clientNum, int oldteam, int newteam ) {
  2288. idPlayer * p = static_cast<idPlayer *>( gameLocal.entities[ clientNum ] );
  2289. p->team = newteam;
  2290. session->GetActingGameStateLobbyBase().SetLobbyUserTeam( gameLocal.lobbyUserIDs[ clientNum ], newteam );
  2291. session->SetVoiceGroupsToTeams();
  2292. assert( IsGametypeTeamBased() ); /* CTF */
  2293. assert( oldteam != newteam );
  2294. assert( !common->IsClient() );
  2295. if ( !common->IsClient() && newteam >= 0 ) {
  2296. PrintMessageEvent( MSG_JOINTEAM, clientNum, newteam );
  2297. }
  2298. // assign the right teamFragCount
  2299. int i;
  2300. for( i = 0; i < gameLocal.numClients; i++ ) {
  2301. if ( i == clientNum ) {
  2302. continue;
  2303. }
  2304. idEntity * ent = gameLocal.entities[ i ];
  2305. if ( ent && ent->IsType( idPlayer::Type ) && static_cast< idPlayer * >(ent)->team == newteam ) {
  2306. playerState[ clientNum ].teamFragCount = playerState[ i ].teamFragCount;
  2307. break;
  2308. }
  2309. }
  2310. if ( i == gameLocal.numClients ) {
  2311. // alone on this team
  2312. playerState[ clientNum ].teamFragCount = 0;
  2313. }
  2314. if ( ( gameState == GAMEON || ( IsGametypeFlagBased() && gameState == SUDDENDEATH ) ) && oldteam != -1 ) {
  2315. // when changing teams during game, kill and respawn
  2316. if ( p->IsInTeleport() ) {
  2317. p->ServerSendEvent( idPlayer::EVENT_ABORT_TELEPORTER, NULL, false );
  2318. p->SetPrivateCameraView( NULL );
  2319. }
  2320. p->Kill( true, true );
  2321. if ( IsGametypeFlagBased() )
  2322. p->DropFlag();
  2323. CheckAbortGame();
  2324. } else if ( IsGametypeFlagBased() && oldteam != -1 ) {
  2325. p->DropFlag();
  2326. }
  2327. }
  2328. /*
  2329. ================
  2330. idMultiplayerGame::ProcessChatMessage
  2331. ================
  2332. */
  2333. void idMultiplayerGame::ProcessChatMessage( int clientNum, bool team, const char *name, const char *text, const char *sound ) {
  2334. idBitMsg outMsg;
  2335. byte msgBuf[ 256 ];
  2336. const char *prefix = NULL;
  2337. int send_to; // 0 - all, 1 - specs, 2 - team
  2338. int i;
  2339. idEntity *ent;
  2340. idPlayer *pfrom;
  2341. idStr prefixed_name;
  2342. assert( !common->IsClient() );
  2343. if ( clientNum >= 0 ) {
  2344. pfrom = static_cast< idPlayer * >( gameLocal.entities[ clientNum ] );
  2345. if ( !( pfrom && pfrom->IsType( idPlayer::Type ) ) ) {
  2346. return;
  2347. }
  2348. if ( pfrom->spectating ) {
  2349. prefix = "spectating";
  2350. if ( team || ( !g_spectatorChat.GetBool() && ( gameState == GAMEON || gameState == SUDDENDEATH ) ) ) {
  2351. // to specs
  2352. send_to = 1;
  2353. } else {
  2354. // to all
  2355. send_to = 0;
  2356. }
  2357. } else if ( team ) {
  2358. prefix = "team";
  2359. // to team
  2360. send_to = 2;
  2361. } else {
  2362. // to all
  2363. send_to = 0;
  2364. }
  2365. } else {
  2366. pfrom = NULL;
  2367. send_to = 0;
  2368. }
  2369. // put the message together
  2370. outMsg.InitWrite( msgBuf, sizeof( msgBuf ) );
  2371. if ( prefix ) {
  2372. prefixed_name = va( "(%s) %s", prefix, name );
  2373. } else {
  2374. prefixed_name = name;
  2375. }
  2376. outMsg.WriteString( prefixed_name );
  2377. outMsg.WriteString( text, -1, false );
  2378. if ( !send_to ) {
  2379. AddChatLine( "%s^0: %s\n", prefixed_name.c_str(), text );
  2380. session->GetActingGameStateLobbyBase().SendReliable( GAME_RELIABLE_MESSAGE_CHAT, outMsg, false );
  2381. if ( sound ) {
  2382. PlayGlobalSound( -1, SND_COUNT, sound );
  2383. }
  2384. } else {
  2385. for ( i = 0; i < gameLocal.numClients; i++ ) {
  2386. ent = gameLocal.entities[ i ];
  2387. if ( !ent || !ent->IsType( idPlayer::Type ) ) {
  2388. continue;
  2389. }
  2390. idPlayer * pent = static_cast< idPlayer * >( ent );
  2391. if ( send_to == 1 && pent->spectating ) {
  2392. if ( sound ) {
  2393. PlayGlobalSound( i, SND_COUNT, sound );
  2394. }
  2395. if ( i == gameLocal.GetLocalClientNum() ) {
  2396. AddChatLine( "%s^0: %s\n", prefixed_name.c_str(), text );
  2397. } else {
  2398. session->GetActingGameStateLobbyBase().SendReliableToLobbyUser( gameLocal.lobbyUserIDs[i], GAME_RELIABLE_MESSAGE_CHAT, outMsg );
  2399. }
  2400. } else if ( send_to == 2 && pent->team == pfrom->team ) {
  2401. if ( sound ) {
  2402. PlayGlobalSound( i, SND_COUNT, sound );
  2403. }
  2404. if ( i == gameLocal.GetLocalClientNum() ) {
  2405. AddChatLine( "%s^0: %s\n", prefixed_name.c_str(), text );
  2406. } else {
  2407. session->GetActingGameStateLobbyBase().SendReliableToLobbyUser( gameLocal.lobbyUserIDs[i], GAME_RELIABLE_MESSAGE_CHAT, outMsg );
  2408. }
  2409. }
  2410. }
  2411. }
  2412. }
  2413. /*
  2414. ================
  2415. idMultiplayerGame::Precache
  2416. ================
  2417. */
  2418. void idMultiplayerGame::Precache() {
  2419. if ( !common->IsMultiplayer() ) {
  2420. return;
  2421. }
  2422. // player
  2423. declManager->FindType( DECL_ENTITYDEF, gameLocal.GetMPPlayerDefName(), false );
  2424. // skins
  2425. for ( int i = 0; i < numSkins; i++ ) {
  2426. idStr baseSkinName = skinNames[ i ];
  2427. declManager->FindSkin( baseSkinName, false );
  2428. declManager->FindSkin( baseSkinName + "_berserk", false );
  2429. declManager->FindSkin( baseSkinName + "_invuln", false );
  2430. }
  2431. // MP game sounds
  2432. for ( int i = 0; i < SND_COUNT; i++ ) {
  2433. declManager->FindSound( GlobalSoundStrings[ i ] );
  2434. }
  2435. }
  2436. /*
  2437. ================
  2438. idMultiplayerGame::ToggleSpectate
  2439. ================
  2440. */
  2441. void idMultiplayerGame::ToggleSpectate() {
  2442. assert( common->IsClient() || gameLocal.GetLocalClientNum() == 0 );
  2443. idPlayer * player = (idPlayer *)gameLocal.entities[gameLocal.GetLocalClientNum()];
  2444. bool spectating = player->spectating;
  2445. // only allow toggling to spectate if spectators are enabled.
  2446. if ( !spectating && !gameLocal.serverInfo.GetBool( "si_spectators" ) ) {
  2447. gameLocal.mpGame.AddChatLine( idLocalization::GetString( "#str_06747" ) );
  2448. return;
  2449. }
  2450. byte msgBuf[ 256 ];
  2451. idBitMsg outMsg;
  2452. outMsg.InitWrite( msgBuf, sizeof( msgBuf ) );
  2453. outMsg.WriteBool( !spectating );
  2454. outMsg.WriteByteAlign();
  2455. session->GetActingGameStateLobbyBase().SendReliableToHost( GAME_RELIABLE_MESSAGE_SPECTATE, outMsg );
  2456. }
  2457. /*
  2458. ================
  2459. idMultiplayerGame::CanPlay
  2460. ================
  2461. */
  2462. bool idMultiplayerGame::CanPlay( idPlayer *p ) {
  2463. return !p->wantSpectate;
  2464. }
  2465. /*
  2466. ================
  2467. idMultiplayerGame::WantRespawn
  2468. ================
  2469. */
  2470. bool idMultiplayerGame::WantRespawn( idPlayer *p ) {
  2471. return p->forceRespawn && !p->wantSpectate;
  2472. }
  2473. /*
  2474. ================
  2475. idMultiplayerGame::VoiceChat
  2476. ================
  2477. */
  2478. void idMultiplayerGame::VoiceChat_f( const idCmdArgs &args ) {
  2479. gameLocal.mpGame.VoiceChat( args, false );
  2480. }
  2481. /*
  2482. ================
  2483. idMultiplayerGame::VoiceChatTeam
  2484. ================
  2485. */
  2486. void idMultiplayerGame::VoiceChatTeam_f( const idCmdArgs &args ) {
  2487. gameLocal.mpGame.VoiceChat( args, true );
  2488. }
  2489. /*
  2490. ================
  2491. idMultiplayerGame::VoiceChat
  2492. ================
  2493. */
  2494. void idMultiplayerGame::VoiceChat( const idCmdArgs &args, bool team ) {
  2495. idBitMsg outMsg;
  2496. byte msgBuf[128];
  2497. const char *voc;
  2498. const idDict *spawnArgs;
  2499. const idKeyValue *keyval;
  2500. int index;
  2501. if ( !common->IsMultiplayer() ) {
  2502. common->Printf( "clientVoiceChat: only valid in multiplayer\n" );
  2503. return;
  2504. }
  2505. if ( args.Argc() != 2 ) {
  2506. common->Printf( "clientVoiceChat: bad args\n" );
  2507. return;
  2508. }
  2509. // throttle
  2510. if ( gameLocal.realClientTime < voiceChatThrottle ) {
  2511. return;
  2512. }
  2513. if ( gameLocal.GetLocalPlayer() == NULL ) {
  2514. return;
  2515. }
  2516. voc = args.Argv( 1 );
  2517. spawnArgs = &gameLocal.GetLocalPlayer()->spawnArgs;
  2518. keyval = spawnArgs->MatchPrefix( "snd_voc_", NULL );
  2519. index = 0;
  2520. while ( keyval ) {
  2521. if ( !keyval->GetValue().Icmp( voc ) ) {
  2522. break;
  2523. }
  2524. keyval = spawnArgs->MatchPrefix( "snd_voc_", keyval );
  2525. index++;
  2526. }
  2527. if ( !keyval ) {
  2528. common->Printf( "Voice command not found: %s\n", voc );
  2529. return;
  2530. }
  2531. voiceChatThrottle = gameLocal.realClientTime + 1000;
  2532. outMsg.InitWrite( msgBuf, sizeof( msgBuf ) );
  2533. outMsg.WriteLong( index );
  2534. outMsg.WriteBits( team ? 1 : 0, 1 );
  2535. session->GetActingGameStateLobbyBase().SendReliableToHost( GAME_RELIABLE_MESSAGE_VCHAT, outMsg );
  2536. }
  2537. /*
  2538. ================
  2539. idMultiplayerGame::ProcessVoiceChat
  2540. ================
  2541. */
  2542. void idMultiplayerGame::ProcessVoiceChat( int clientNum, bool team, int index ) {
  2543. const idDict *spawnArgs;
  2544. const idKeyValue *keyval;
  2545. idStr name;
  2546. idStr snd_key;
  2547. idStr text_key;
  2548. idPlayer *p;
  2549. p = static_cast< idPlayer * >( gameLocal.entities[ clientNum ] );
  2550. if ( !( p && p->IsType( idPlayer::Type ) ) ) {
  2551. return;
  2552. }
  2553. if ( p->spectating ) {
  2554. return;
  2555. }
  2556. // lookup the sound def
  2557. spawnArgs = &p->spawnArgs;
  2558. keyval = spawnArgs->MatchPrefix( "snd_voc_", NULL );
  2559. while ( index > 0 && keyval ) {
  2560. keyval = spawnArgs->MatchPrefix( "snd_voc_", keyval );
  2561. index--;
  2562. }
  2563. if ( !keyval ) {
  2564. common->DPrintf( "ProcessVoiceChat: unknown chat index %d\n", index );
  2565. return;
  2566. }
  2567. snd_key = keyval->GetKey();
  2568. name = session->GetActingGameStateLobbyBase().GetLobbyUserName( gameLocal.lobbyUserIDs[ clientNum ] );
  2569. sprintf( text_key, "txt_%s", snd_key.Right( snd_key.Length() - 4 ).c_str() );
  2570. if ( team || gameState == COUNTDOWN || gameState == GAMEREVIEW ) {
  2571. ProcessChatMessage( clientNum, team, name, spawnArgs->GetString( text_key ), spawnArgs->GetString( snd_key ) );
  2572. } else {
  2573. p->StartSound( snd_key, SND_CHANNEL_ANY, 0, true, NULL );
  2574. ProcessChatMessage( clientNum, team, name, spawnArgs->GetString( text_key ), NULL );
  2575. }
  2576. }
  2577. /*
  2578. ================
  2579. idMultiplayerGame::ServerWriteInitialReliableMessages
  2580. ================
  2581. */
  2582. void idMultiplayerGame::ServerWriteInitialReliableMessages( int clientNum, lobbyUserID_t lobbyUserID ) {
  2583. idBitMsg outMsg;
  2584. byte msgBuf[ MAX_GAME_MESSAGE_SIZE ];
  2585. outMsg.InitWrite( msgBuf, sizeof( msgBuf ) );
  2586. outMsg.BeginWriting();
  2587. // send the game state and start time
  2588. outMsg.WriteByte( gameState );
  2589. outMsg.WriteLong( matchStartedTime );
  2590. outMsg.WriteShort( startFragLimit );
  2591. // send the powerup states and the spectate states
  2592. for( int i = 0; i < gameLocal.numClients; i++ ) {
  2593. idEntity * ent = gameLocal.entities[ i ];
  2594. if ( i != clientNum && ent && ent->IsType( idPlayer::Type ) ) {
  2595. outMsg.WriteByte( i );
  2596. outMsg.WriteBits( static_cast< idPlayer * >( ent )->inventory.powerups, 15 );
  2597. outMsg.WriteBits( static_cast< idPlayer * >( ent )->spectating, 1 );
  2598. }
  2599. }
  2600. outMsg.WriteByte( MAX_CLIENTS );
  2601. session->GetActingGameStateLobbyBase().SendReliableToLobbyUser( lobbyUserID, GAME_RELIABLE_MESSAGE_STARTSTATE, outMsg );
  2602. // warmup time
  2603. if ( gameState == COUNTDOWN ) {
  2604. outMsg.BeginWriting();
  2605. outMsg.WriteByte( GAME_RELIABLE_MESSAGE_WARMUPTIME );
  2606. outMsg.WriteLong( warmupEndTime );
  2607. session->GetActingGameStateLobbyBase().SendReliableToLobbyUser( lobbyUserID, GAME_RELIABLE_MESSAGE_WARMUPTIME, outMsg );
  2608. }
  2609. }
  2610. /*
  2611. ================
  2612. idMultiplayerGame::ClientReadStartState
  2613. ================
  2614. */
  2615. void idMultiplayerGame::ClientReadStartState( const idBitMsg &msg ) {
  2616. // read the state in preparation for reading snapshot updates
  2617. gameState = (idMultiplayerGame::gameState_t)msg.ReadByte();
  2618. matchStartedTime = msg.ReadLong( );
  2619. startFragLimit = msg.ReadShort( );
  2620. int client;
  2621. while ( ( client = msg.ReadByte() ) != MAX_CLIENTS ) {
  2622. // Do not process players that are not here.
  2623. if( gameLocal.entities[ client ] == NULL ) {
  2624. continue;
  2625. }
  2626. assert( gameLocal.entities[ client ] && gameLocal.entities[ client ]->IsType( idPlayer::Type ) );
  2627. int powerup = msg.ReadBits( 15 );
  2628. for ( int i = 0; i < MAX_POWERUPS; i++ ) {
  2629. if ( powerup & ( 1 << i ) ) {
  2630. static_cast< idPlayer * >( gameLocal.entities[ client ] )->GivePowerUp( i, 0, ITEM_GIVE_FEEDBACK | ITEM_GIVE_UPDATE_STATE );
  2631. }
  2632. }
  2633. bool spectate = ( msg.ReadBits( 1 ) != 0 );
  2634. static_cast< idPlayer * >( gameLocal.entities[ client ] )->Spectate( spectate );
  2635. }
  2636. }
  2637. /*
  2638. ================
  2639. idMultiplayerGame::ClientReadWarmupTime
  2640. ================
  2641. */
  2642. void idMultiplayerGame::ClientReadWarmupTime( const idBitMsg &msg ) {
  2643. warmupEndTime = msg.ReadLong();
  2644. }
  2645. /*
  2646. ================
  2647. idMultiplayerGame::ClientReadWarmupTime
  2648. ================
  2649. */
  2650. void idMultiplayerGame::ClientReadMatchStartedTime( const idBitMsg & msg ) {
  2651. matchStartedTime = msg.ReadLong();
  2652. }
  2653. /*
  2654. ================
  2655. idMultiplayerGame::ClientReadAchievementUnlock
  2656. ================
  2657. */
  2658. void idMultiplayerGame::ClientReadAchievementUnlock( const idBitMsg & msg ) {
  2659. int playerid = msg.ReadByte();
  2660. achievement_t achieve = ( achievement_t )msg.ReadByte();
  2661. idPlayer * player = static_cast<idPlayer *>( gameLocal.entities[ playerid ] );
  2662. if( player != NULL ) {
  2663. idLib::Printf( "Client Receiving Achievement\n");
  2664. player->GetAchievementManager().EventCompletesAchievement( achieve );
  2665. }
  2666. }
  2667. /*
  2668. ================
  2669. idMultiplayerGame::IsGametypeTeamBased
  2670. ================
  2671. */
  2672. bool idMultiplayerGame::IsGametypeTeamBased() /* CTF */
  2673. {
  2674. switch ( gameLocal.gameType )
  2675. {
  2676. case GAME_SP:
  2677. case GAME_DM:
  2678. case GAME_TOURNEY:
  2679. case GAME_LASTMAN:
  2680. return false;
  2681. case GAME_CTF:
  2682. case GAME_TDM:
  2683. return true;
  2684. default:
  2685. assert( !"Add support for your new gametype here." );
  2686. }
  2687. return false;
  2688. }
  2689. /*
  2690. ================
  2691. idMultiplayerGame::IsGametypeFlagBased
  2692. ================
  2693. */
  2694. bool idMultiplayerGame::IsGametypeFlagBased() {
  2695. switch ( gameLocal.gameType )
  2696. {
  2697. case GAME_SP:
  2698. case GAME_DM:
  2699. case GAME_TOURNEY:
  2700. case GAME_LASTMAN:
  2701. case GAME_TDM:
  2702. return false;
  2703. case GAME_CTF:
  2704. return true;
  2705. default:
  2706. assert( !"Add support for your new gametype here." );
  2707. }
  2708. return false;
  2709. }
  2710. /*
  2711. ================
  2712. idMultiplayerGame::GetTeamFlag
  2713. ================
  2714. */
  2715. idItemTeam * idMultiplayerGame::GetTeamFlag( int team ) {
  2716. assert( team == 0 || team == 1 );
  2717. if ( !IsGametypeFlagBased() || ( team != 0 && team != 1 ) ) /* CTF */
  2718. return NULL;
  2719. // TODO : just call on map start
  2720. FindTeamFlags();
  2721. return teamFlags[team];
  2722. }
  2723. /*
  2724. ================
  2725. idMultiplayerGame::GetTeamFlag
  2726. ================
  2727. */
  2728. void idMultiplayerGame::FindTeamFlags() {
  2729. char * flagDefs[2] =
  2730. {
  2731. "team_CTF_redflag",
  2732. "team_CTF_blueflag"
  2733. };
  2734. for ( int i = 0; i < 2; i++)
  2735. {
  2736. idEntity * entity = gameLocal.FindEntityUsingDef( NULL, flagDefs[i] );
  2737. do
  2738. {
  2739. if ( entity == NULL )
  2740. return;
  2741. idItemTeam * flag = static_cast<idItemTeam *>(entity);
  2742. if ( flag->team == i )
  2743. {
  2744. teamFlags[i] = flag;
  2745. break;
  2746. }
  2747. entity = gameLocal.FindEntityUsingDef( entity, flagDefs[i] );
  2748. } while( entity );
  2749. }
  2750. }
  2751. /*
  2752. ================
  2753. idMultiplayerGame::GetFlagStatus
  2754. ================
  2755. */
  2756. flagStatus_t idMultiplayerGame::GetFlagStatus( int team ) {
  2757. //assert( IsGametypeFlagBased() );
  2758. idItemTeam *teamFlag = GetTeamFlag( team );
  2759. //assert( teamFlag != NULL );
  2760. if ( teamFlag != NULL ) {
  2761. if ( teamFlag->carried == false && teamFlag->dropped == false )
  2762. return FLAGSTATUS_INBASE;
  2763. if ( teamFlag->carried == true )
  2764. return FLAGSTATUS_TAKEN;
  2765. if ( teamFlag->carried == false && teamFlag->dropped == true )
  2766. return FLAGSTATUS_STRAY;
  2767. }
  2768. //assert( !"Invalid flag state." );
  2769. return FLAGSTATUS_NONE;
  2770. }
  2771. /*
  2772. ================
  2773. idMultiplayerGame::SetFlagMsgs
  2774. ================
  2775. */
  2776. void idMultiplayerGame::SetFlagMsg( bool b ) {
  2777. flagMsgOn = b;
  2778. }
  2779. /*
  2780. ================
  2781. idMultiplayerGame::IsFlagMsgOn
  2782. ================
  2783. */
  2784. bool idMultiplayerGame::IsFlagMsgOn() {
  2785. return ( GetGameState() == WARMUP || GetGameState() == GAMEON || GetGameState() == SUDDENDEATH ) && flagMsgOn;
  2786. }
  2787. /*
  2788. ================
  2789. idMultiplayerGame::ReloadScoreboard
  2790. ================
  2791. */
  2792. void idMultiplayerGame::ReloadScoreboard() {
  2793. if ( scoreboardManager != NULL ) {
  2794. scoreboardManager->Initialize( "scoreboard", common->SW() );
  2795. }
  2796. Precache();
  2797. }
  2798. /*
  2799. ================
  2800. idMultiplayerGame::GetGameModes
  2801. ================
  2802. */
  2803. int idMultiplayerGame::GetGameModes( const char *** gameModes, const char *** gameModesDisplay ) {
  2804. bool defaultValue = true;
  2805. if( session->GetTitleStorageBool("CTF_Enabled", defaultValue ) ) {
  2806. *gameModes = gameTypeNames_WithCTF;
  2807. *gameModesDisplay = gameTypeDisplayNames_WithCTF;
  2808. return GAME_COUNT;
  2809. } else {
  2810. *gameModes = gameTypeNames_WithoutCTF;
  2811. *gameModesDisplay = gameTypeDisplayNames_WithoutCTF;
  2812. return GAME_COUNT - 1;
  2813. }
  2814. }