MultiplayerGame.cpp 100 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411
  1. /*
  2. ===========================================================================
  3. Doom 3 GPL Source Code
  4. Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
  5. This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
  6. Doom 3 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 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 Source Code. If not, see <http://www.gnu.org/licenses/>.
  16. In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
  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. idCVar g_spectatorChat( "g_spectatorChat", "0", CVAR_GAME | CVAR_ARCHIVE | CVAR_BOOL, "let spectators talk to everyone during game" );
  26. // global sounds transmitted by index - 0 .. SND_COUNT
  27. // sounds in this list get precached on MP start
  28. const char *idMultiplayerGame::GlobalSoundStrings[] = {
  29. "sound/feedback/voc_youwin.wav",
  30. "sound/feedback/voc_youlose.wav",
  31. "sound/feedback/fight.wav",
  32. "sound/feedback/vote_now.wav",
  33. "sound/feedback/vote_passed.wav",
  34. "sound/feedback/vote_failed.wav",
  35. "sound/feedback/three.wav",
  36. "sound/feedback/two.wav",
  37. "sound/feedback/one.wav",
  38. "sound/feedback/sudden_death.wav",
  39. };
  40. // handy verbose
  41. const char *idMultiplayerGame::GameStateStrings[] = {
  42. "INACTIVE",
  43. "WARMUP",
  44. "COUNTDOWN",
  45. "GAMEON",
  46. "SUDDENDEATH",
  47. "GAMEREVIEW",
  48. "NEXTGAME"
  49. };
  50. const char *idMultiplayerGame::MPGuis[] = {
  51. "guis/mphud.gui",
  52. "guis/mpmain.gui",
  53. "guis/mpmsgmode.gui",
  54. "guis/netmenu.gui",
  55. NULL
  56. };
  57. const char *idMultiplayerGame::ThrottleVars[] = {
  58. "ui_spectate",
  59. "ui_ready",
  60. "ui_team",
  61. NULL
  62. };
  63. const char *idMultiplayerGame::ThrottleVarsInEnglish[] = {
  64. "#str_06738",
  65. "#str_06737",
  66. "#str_01991",
  67. NULL
  68. };
  69. const int idMultiplayerGame::ThrottleDelay[] = {
  70. 8,
  71. 5,
  72. 5
  73. };
  74. /*
  75. ================
  76. idMultiplayerGame::idMultiplayerGame
  77. ================
  78. */
  79. idMultiplayerGame::idMultiplayerGame() {
  80. scoreBoard = NULL;
  81. spectateGui = NULL;
  82. guiChat = NULL;
  83. mainGui = NULL;
  84. mapList = NULL;
  85. msgmodeGui = NULL;
  86. lastGameType = GAME_SP;
  87. Clear();
  88. }
  89. /*
  90. ================
  91. idMultiplayerGame::Shutdown
  92. ================
  93. */
  94. void idMultiplayerGame::Shutdown( void ) {
  95. Clear();
  96. }
  97. /*
  98. ================
  99. idMultiplayerGame::SetMenuSkin
  100. ================
  101. */
  102. void idMultiplayerGame::SetMenuSkin( void ) {
  103. // skins
  104. idStr str = cvarSystem->GetCVarString( "mod_validSkins" );
  105. idStr uiSkin = cvarSystem->GetCVarString( "ui_skin" );
  106. idStr skin;
  107. int skinId = 1;
  108. int count = 1;
  109. while ( str.Length() ) {
  110. int n = str.Find( ";" );
  111. if ( n >= 0 ) {
  112. skin = str.Left( n );
  113. str = str.Right( str.Length() - n - 1 );
  114. } else {
  115. skin = str;
  116. str = "";
  117. }
  118. if ( skin.Icmp( uiSkin ) == 0 ) {
  119. skinId = count;
  120. }
  121. count++;
  122. }
  123. for ( int i = 0; i < count; i++ ) {
  124. mainGui->SetStateInt( va( "skin%i", i+1 ), 0 );
  125. }
  126. mainGui->SetStateInt( va( "skin%i", skinId ), 1 );
  127. }
  128. /*
  129. ================
  130. idMultiplayerGame::Reset
  131. ================
  132. */
  133. void idMultiplayerGame::Reset() {
  134. Clear();
  135. assert( !scoreBoard && !spectateGui && !guiChat && !mainGui && !mapList );
  136. scoreBoard = uiManager->FindGui( "guis/scoreboard.gui", true, false, true );
  137. spectateGui = uiManager->FindGui( "guis/spectate.gui", true, false, true );
  138. guiChat = uiManager->FindGui( "guis/chat.gui", true, false, true );
  139. mainGui = uiManager->FindGui( "guis/mpmain.gui", true, false, true );
  140. mapList = uiManager->AllocListGUI( );
  141. mapList->Config( mainGui, "mapList" );
  142. // set this GUI so that our Draw function is still called when it becomes the active/fullscreen GUI
  143. mainGui->SetStateBool( "gameDraw", true );
  144. mainGui->SetKeyBindingNames();
  145. mainGui->SetStateInt( "com_machineSpec", cvarSystem->GetCVarInteger( "com_machineSpec" ) );
  146. SetMenuSkin();
  147. msgmodeGui = uiManager->FindGui( "guis/mpmsgmode.gui", true, false, true );
  148. msgmodeGui->SetStateBool( "gameDraw", true );
  149. ClearGuis();
  150. ClearChatData();
  151. warmupEndTime = 0;
  152. }
  153. /*
  154. ================
  155. idMultiplayerGame::ServerClientConnect
  156. ================
  157. */
  158. void idMultiplayerGame::ServerClientConnect( int clientNum ) {
  159. memset( &playerState[ clientNum ], 0, sizeof( playerState[ clientNum ] ) );
  160. }
  161. /*
  162. ================
  163. idMultiplayerGame::SpawnPlayer
  164. ================
  165. */
  166. void idMultiplayerGame::SpawnPlayer( int clientNum ) {
  167. bool ingame = playerState[ clientNum ].ingame;
  168. memset( &playerState[ clientNum ], 0, sizeof( playerState[ clientNum ] ) );
  169. if ( !gameLocal.isClient ) {
  170. idPlayer *p = static_cast< idPlayer * >( gameLocal.entities[ clientNum ] );
  171. p->spawnedTime = gameLocal.time;
  172. if ( gameLocal.gameType == GAME_TDM ) {
  173. SwitchToTeam( clientNum, -1, p->team );
  174. }
  175. p->tourneyRank = 0;
  176. if ( gameLocal.gameType == GAME_TOURNEY && gameState == GAMEON ) {
  177. p->tourneyRank++;
  178. }
  179. playerState[ clientNum ].ingame = ingame;
  180. }
  181. }
  182. /*
  183. ================
  184. idMultiplayerGame::Clear
  185. ================
  186. */
  187. void idMultiplayerGame::Clear() {
  188. int i;
  189. gameState = INACTIVE;
  190. nextState = INACTIVE;
  191. pingUpdateTime = 0;
  192. vote = VOTE_NONE;
  193. voteTimeOut = 0;
  194. voteExecTime = 0;
  195. nextStateSwitch = 0;
  196. matchStartedTime = 0;
  197. currentTourneyPlayer[ 0 ] = -1;
  198. currentTourneyPlayer[ 1 ] = -1;
  199. one = two = three = false;
  200. memset( &playerState, 0 , sizeof( playerState ) );
  201. lastWinner = -1;
  202. currentMenu = 0;
  203. bCurrentMenuMsg = false;
  204. nextMenu = 0;
  205. pureReady = false;
  206. scoreBoard = NULL;
  207. spectateGui = NULL;
  208. guiChat = NULL;
  209. mainGui = NULL;
  210. msgmodeGui = NULL;
  211. if ( mapList ) {
  212. uiManager->FreeListGUI( mapList );
  213. mapList = NULL;
  214. }
  215. fragLimitTimeout = 0;
  216. memset( &switchThrottle, 0, sizeof( switchThrottle ) );
  217. voiceChatThrottle = 0;
  218. for ( i = 0; i < NUM_CHAT_NOTIFY; i++ ) {
  219. chatHistory[ i ].line.Clear();
  220. }
  221. warmupText.Clear();
  222. voteValue.Clear();
  223. voteString.Clear();
  224. startFragLimit = -1;
  225. }
  226. /*
  227. ================
  228. idMultiplayerGame::ClearGuis
  229. ================
  230. */
  231. void idMultiplayerGame::ClearGuis() {
  232. int i;
  233. for ( i = 0; i < MAX_CLIENTS; i++ ) {
  234. scoreBoard->SetStateString( va( "player%i",i+1 ), "" );
  235. scoreBoard->SetStateString( va( "player%i_score", i+1 ), "" );
  236. scoreBoard->SetStateString( va( "player%i_tdm_tscore", i+1 ), "" );
  237. scoreBoard->SetStateString( va( "player%i_tdm_score", i+1 ), "" );
  238. scoreBoard->SetStateString( va( "player%i_wins", i+1 ), "" );
  239. scoreBoard->SetStateString( va( "player%i_status", i+1 ), "" );
  240. scoreBoard->SetStateInt( va( "rank%i", i+1 ), 0 );
  241. scoreBoard->SetStateInt( "rank_self", 0 );
  242. idPlayer *player = static_cast<idPlayer *>( gameLocal.entities[ i ] );
  243. if ( !player || !player->hud ) {
  244. continue;
  245. }
  246. player->hud->SetStateString( va( "player%i",i+1 ), "" );
  247. player->hud->SetStateString( va( "player%i_score", i+1 ), "" );
  248. player->hud->SetStateString( va( "player%i_ready", i+1 ), "" );
  249. scoreBoard->SetStateInt( va( "rank%i", i+1 ), 0 );
  250. player->hud->SetStateInt( "rank_self", 0 );
  251. }
  252. }
  253. /*
  254. ================
  255. idMultiplayerGame::UpdatePlayerRanks
  256. ================
  257. */
  258. void idMultiplayerGame::UpdatePlayerRanks() {
  259. int i, j, k;
  260. idPlayer *players[MAX_CLIENTS];
  261. idEntity *ent;
  262. idPlayer *player;
  263. memset( players, 0, sizeof( players ) );
  264. numRankedPlayers = 0;
  265. for ( i = 0; i < gameLocal.numClients; i++ ) {
  266. ent = gameLocal.entities[ i ];
  267. if ( !ent || !ent->IsType( idPlayer::Type ) ) {
  268. continue;
  269. }
  270. player = static_cast< idPlayer * >( ent );
  271. if ( !CanPlay( player ) ) {
  272. continue;
  273. }
  274. if ( gameLocal.gameType == GAME_TOURNEY ) {
  275. if ( i != currentTourneyPlayer[ 0 ] && i != currentTourneyPlayer[ 1 ] ) {
  276. continue;
  277. }
  278. }
  279. if ( gameLocal.gameType == GAME_LASTMAN && playerState[ i ].fragCount == LASTMAN_NOLIVES ) {
  280. continue;
  281. }
  282. for ( j = 0; j < numRankedPlayers; j++ ) {
  283. bool insert = false;
  284. if ( gameLocal.gameType == GAME_TDM ) {
  285. if ( player->team != players[ j ]->team ) {
  286. if ( playerState[ i ].teamFragCount > playerState[ players[ j ]->entityNumber ].teamFragCount ) {
  287. // team scores
  288. insert = true;
  289. } else if ( playerState[ i ].teamFragCount == playerState[ players[ j ]->entityNumber ].teamFragCount && player->team < players[ j ]->team ) {
  290. // at equal scores, sort by team number
  291. insert = true;
  292. }
  293. } else if ( playerState[ i ].fragCount > playerState[ players[ j ]->entityNumber ].fragCount ) {
  294. // in the same team, sort by frag count
  295. insert = true;
  296. }
  297. } else {
  298. insert = ( playerState[ i ].fragCount > playerState[ players[ j ]->entityNumber ].fragCount );
  299. }
  300. if ( insert ) {
  301. for ( k = numRankedPlayers; k > j; k-- ) {
  302. players[ k ] = players[ k-1 ];
  303. }
  304. players[ j ] = player;
  305. break;
  306. }
  307. }
  308. if ( j == numRankedPlayers ) {
  309. players[ numRankedPlayers ] = player;
  310. }
  311. numRankedPlayers++;
  312. }
  313. memcpy( rankedPlayers, players, sizeof( players ) );
  314. }
  315. /*
  316. ================
  317. idMultiplayerGame::UpdateRankColor
  318. ================
  319. */
  320. void idMultiplayerGame::UpdateRankColor( idUserInterface *gui, const char *mask, int i, const idVec3 &vec ) {
  321. for ( int j = 1; j < 4; j++ ) {
  322. gui->SetStateFloat( va( mask, i, j ), vec[ j - 1 ] );
  323. }
  324. }
  325. /*
  326. ================
  327. idMultiplayerGame::UpdateScoreboard
  328. ================
  329. */
  330. void idMultiplayerGame::UpdateScoreboard( idUserInterface *scoreBoard, idPlayer *player ) {
  331. int i, j, iline, k;
  332. idStr gameinfo;
  333. idStr livesinfo;
  334. idStr timeinfo;
  335. idEntity *ent;
  336. idPlayer *p;
  337. int value;
  338. scoreBoard->SetStateString( "scoretext", gameLocal.gameType == GAME_LASTMAN ? common->GetLanguageDict()->GetString( "#str_04242" ) : common->GetLanguageDict()->GetString( "#str_04243" ) );
  339. iline = 0; // the display lines
  340. if ( gameState != WARMUP ) {
  341. for ( i = 0; i < numRankedPlayers; i++ ) {
  342. // ranked player
  343. iline++;
  344. scoreBoard->SetStateString( va( "player%i", iline ), rankedPlayers[ i ]->GetUserInfo()->GetString( "ui_name" ) );
  345. if ( gameLocal.gameType == GAME_TDM ) {
  346. value = idMath::ClampInt( MP_PLAYER_MINFRAGS, MP_PLAYER_MAXFRAGS, playerState[ rankedPlayers[ i ]->entityNumber ].fragCount );
  347. scoreBoard->SetStateInt( va( "player%i_tdm_score", iline ), value );
  348. value = idMath::ClampInt( MP_PLAYER_MINFRAGS, MP_PLAYER_MAXFRAGS, playerState[ rankedPlayers[ i ]->entityNumber ].teamFragCount );
  349. scoreBoard->SetStateString( va( "player%i_tdm_tscore", iline ), va( "/ %i", value ) );
  350. scoreBoard->SetStateString( va( "player%i_score", iline ), "" );
  351. } else {
  352. value = idMath::ClampInt( MP_PLAYER_MINFRAGS, MP_PLAYER_MAXFRAGS, playerState[ rankedPlayers[ i ]->entityNumber ].fragCount );
  353. scoreBoard->SetStateInt( va( "player%i_score", iline ), value );
  354. scoreBoard->SetStateString( va( "player%i_tdm_tscore", iline ), "" );
  355. scoreBoard->SetStateString( va( "player%i_tdm_score", iline ), "" );
  356. }
  357. value = idMath::ClampInt( 0, MP_PLAYER_MAXWINS, playerState[ rankedPlayers[ i ]->entityNumber ].wins );
  358. scoreBoard->SetStateInt( va( "player%i_wins", iline ), value );
  359. scoreBoard->SetStateInt( va( "player%i_ping", iline ), playerState[ rankedPlayers[ i ]->entityNumber ].ping );
  360. // set the color band
  361. scoreBoard->SetStateInt( va( "rank%i", iline ), 1 );
  362. UpdateRankColor( scoreBoard, "rank%i_color%i", iline, rankedPlayers[ i ]->colorBar );
  363. if ( rankedPlayers[ i ] == player ) {
  364. // highlight who we are
  365. scoreBoard->SetStateInt( "rank_self", iline );
  366. }
  367. }
  368. }
  369. // if warmup, this draws everyone, otherwise it goes over spectators only
  370. // when doing warmup we loop twice to draw ready/not ready first *then* spectators
  371. // NOTE: in tourney, shows spectators according to their playing rank order?
  372. for ( k = 0; k < ( gameState == WARMUP ? 2 : 1 ); k++ ) {
  373. for ( i = 0; i < MAX_CLIENTS; i++ ) {
  374. ent = gameLocal.entities[ i ];
  375. if ( !ent || !ent->IsType( idPlayer::Type ) ) {
  376. continue;
  377. }
  378. if ( gameState != WARMUP ) {
  379. // check he's not covered by ranks already
  380. for ( j = 0; j < numRankedPlayers; j++ ) {
  381. if ( ent == rankedPlayers[ j ] ) {
  382. break;
  383. }
  384. }
  385. if ( j != numRankedPlayers ) {
  386. continue;
  387. }
  388. }
  389. p = static_cast< idPlayer * >( ent );
  390. if ( gameState == WARMUP ) {
  391. if ( k == 0 && p->spectating ) {
  392. continue;
  393. }
  394. if ( k == 1 && !p->spectating ) {
  395. continue;
  396. }
  397. }
  398. iline++;
  399. if ( !playerState[ i ].ingame ) {
  400. scoreBoard->SetStateString( va( "player%i", iline ), common->GetLanguageDict()->GetString( "#str_04244" ) );
  401. scoreBoard->SetStateString( va( "player%i_score", iline ), common->GetLanguageDict()->GetString( "#str_04245" ) );
  402. // no color band
  403. scoreBoard->SetStateInt( va( "rank%i", iline ), 0 );
  404. } else {
  405. scoreBoard->SetStateString( va( "player%i", iline ), gameLocal.userInfo[ i ].GetString( "ui_name" ) );
  406. if ( gameState == WARMUP ) {
  407. if ( p->spectating ) {
  408. scoreBoard->SetStateString( va( "player%i_score", iline ), common->GetLanguageDict()->GetString( "#str_04246" ) );
  409. // no color band
  410. scoreBoard->SetStateInt( va( "rank%i", iline ), 0 );
  411. } else {
  412. scoreBoard->SetStateString( va( "player%i_score", iline ), p->IsReady() ? common->GetLanguageDict()->GetString( "#str_04247" ) : common->GetLanguageDict()->GetString( "#str_04248" ) );
  413. // set the color band
  414. scoreBoard->SetStateInt( va( "rank%i", iline ), 1 );
  415. UpdateRankColor( scoreBoard, "rank%i_color%i", iline, p->colorBar );
  416. }
  417. } else {
  418. if ( gameLocal.gameType == GAME_LASTMAN && playerState[ i ].fragCount == LASTMAN_NOLIVES ) {
  419. scoreBoard->SetStateString( va( "player%i_score", iline ), common->GetLanguageDict()->GetString( "#str_06736" ) );
  420. // set the color band
  421. scoreBoard->SetStateInt( va( "rank%i", iline ), 1 );
  422. UpdateRankColor( scoreBoard, "rank%i_color%i", iline, p->colorBar );
  423. } else {
  424. scoreBoard->SetStateString( va( "player%i_score", iline ), common->GetLanguageDict()->GetString( "#str_04246" ) );
  425. // no color band
  426. scoreBoard->SetStateInt( va( "rank%i", iline ), 0 );
  427. }
  428. }
  429. }
  430. scoreBoard->SetStateString( va( "player%i_tdm_tscore", iline ), "" );
  431. scoreBoard->SetStateString( va( "player%i_tdm_score", iline ), "" );
  432. scoreBoard->SetStateString( va( "player%i_wins", iline ), "" );
  433. scoreBoard->SetStateInt( va( "player%i_ping", iline ), playerState[ i ].ping );
  434. if ( i == player->entityNumber ) {
  435. // highlight who we are
  436. scoreBoard->SetStateInt( "rank_self", iline );
  437. }
  438. }
  439. }
  440. // clear remaining lines (empty slots)
  441. iline++;
  442. while ( iline < 5 ) {
  443. scoreBoard->SetStateString( va( "player%i", iline ), "" );
  444. scoreBoard->SetStateString( va( "player%i_score", iline ), "" );
  445. scoreBoard->SetStateString( va( "player%i_tdm_tscore", iline ), "" );
  446. scoreBoard->SetStateString( va( "player%i_tdm_score", iline ), "" );
  447. scoreBoard->SetStateString( va( "player%i_wins", iline ), "" );
  448. scoreBoard->SetStateString( va( "player%i_ping", iline ), "" );
  449. scoreBoard->SetStateInt( va( "rank%i", iline ), 0 );
  450. iline++;
  451. }
  452. gameinfo = va( "%s: %s", common->GetLanguageDict()->GetString( "#str_02376" ), gameLocal.serverInfo.GetString( "si_gameType" ) );
  453. if ( gameLocal.gameType == GAME_LASTMAN ) {
  454. if ( gameState == GAMEON || gameState == SUDDENDEATH ) {
  455. livesinfo = va( "%s: %i", common->GetLanguageDict()->GetString( "#str_04264" ), startFragLimit );
  456. } else {
  457. livesinfo = va( "%s: %i", common->GetLanguageDict()->GetString( "#str_04264" ), gameLocal.serverInfo.GetInt( "si_fragLimit" ) );
  458. }
  459. } else {
  460. livesinfo = va( "%s: %i", common->GetLanguageDict()->GetString( "#str_01982" ), gameLocal.serverInfo.GetInt( "si_fragLimit" ) );
  461. }
  462. if ( gameLocal.serverInfo.GetInt( "si_timeLimit" ) > 0 ) {
  463. timeinfo = va( "%s: %i", common->GetLanguageDict()->GetString( "#str_01983" ), gameLocal.serverInfo.GetInt( "si_timeLimit" ) );
  464. } else {
  465. timeinfo = va("%s", common->GetLanguageDict()->GetString( "#str_07209" ));
  466. }
  467. scoreBoard->SetStateString( "gameinfo", gameinfo );
  468. scoreBoard->SetStateString( "livesinfo", livesinfo );
  469. scoreBoard->SetStateString( "timeinfo", timeinfo );
  470. scoreBoard->Redraw( gameLocal.time );
  471. }
  472. /*
  473. ================
  474. idMultiplayerGame::GameTime
  475. ================
  476. */
  477. const char *idMultiplayerGame::GameTime() {
  478. static char buff[16];
  479. int m, s, t, ms;
  480. if ( gameState == COUNTDOWN ) {
  481. ms = warmupEndTime - gameLocal.realClientTime;
  482. s = ms / 1000 + 1;
  483. if ( ms <= 0 ) {
  484. strcpy( buff, "WMP --" );
  485. } else {
  486. sprintf( buff, "WMP %i", s );
  487. }
  488. } else {
  489. int timeLimit = gameLocal.serverInfo.GetInt( "si_timeLimit" );
  490. if ( timeLimit ) {
  491. ms = ( timeLimit * 60000 ) - ( gameLocal.time - matchStartedTime );
  492. } else {
  493. ms = gameLocal.time - matchStartedTime;
  494. }
  495. if ( ms < 0 ) {
  496. ms = 0;
  497. }
  498. s = ms / 1000;
  499. m = s / 60;
  500. s -= m * 60;
  501. t = s / 10;
  502. s -= t * 10;
  503. sprintf( buff, "%i:%i%i", m, t, s );
  504. }
  505. return &buff[0];
  506. }
  507. /*
  508. ================
  509. idMultiplayerGame::NumActualClients
  510. ================
  511. */
  512. int idMultiplayerGame::NumActualClients( bool countSpectators, int *teamcounts ) {
  513. idPlayer *p;
  514. int c = 0;
  515. if ( teamcounts ) {
  516. teamcounts[ 0 ] = teamcounts[ 1 ] = 0;
  517. }
  518. for( int i = 0 ; i < gameLocal.numClients ; i++ ) {
  519. idEntity *ent = gameLocal.entities[ i ];
  520. if ( !ent || !ent->IsType( idPlayer::Type ) ) {
  521. continue;
  522. }
  523. p = static_cast< idPlayer * >( ent );
  524. if ( countSpectators || CanPlay( p ) ) {
  525. c++;
  526. }
  527. if ( teamcounts && CanPlay( p ) ) {
  528. teamcounts[ p->team ]++;
  529. }
  530. }
  531. return c;
  532. }
  533. /*
  534. ================
  535. idMultiplayerGame::EnoughClientsToPlay
  536. ================
  537. */
  538. bool idMultiplayerGame::EnoughClientsToPlay() {
  539. int team[ 2 ];
  540. int clients = NumActualClients( false, &team[ 0 ] );
  541. if ( gameLocal.gameType == GAME_TDM ) {
  542. return clients >= 2 && team[ 0 ] && team[ 1 ];
  543. } else {
  544. return clients >= 2;
  545. }
  546. }
  547. /*
  548. ================
  549. idMultiplayerGame::AllPlayersReady
  550. ================
  551. */
  552. bool idMultiplayerGame::AllPlayersReady() {
  553. int i;
  554. idEntity *ent;
  555. idPlayer *p;
  556. int team[ 2 ];
  557. if ( NumActualClients( false, &team[ 0 ] ) <= 1 ) {
  558. return false;
  559. }
  560. if ( gameLocal.gameType == GAME_TDM ) {
  561. if ( !team[ 0 ] || !team[ 1 ] ) {
  562. return false;
  563. }
  564. }
  565. if ( !gameLocal.serverInfo.GetBool( "si_warmup" ) ) {
  566. return true;
  567. }
  568. for( i = 0; i < gameLocal.numClients; i++ ) {
  569. if ( gameLocal.gameType == GAME_TOURNEY && i != currentTourneyPlayer[ 0 ] && i != currentTourneyPlayer[ 1 ] ) {
  570. continue;
  571. }
  572. ent = gameLocal.entities[ i ];
  573. if ( !ent || !ent->IsType( idPlayer::Type ) ) {
  574. continue;
  575. }
  576. p = static_cast< idPlayer * >( ent );
  577. if ( CanPlay( p ) && !p->IsReady() ) {
  578. return false;
  579. }
  580. team[ p->team ]++;
  581. }
  582. return true;
  583. }
  584. /*
  585. ================
  586. idMultiplayerGame::FragLimitHit
  587. return the winning player (team player)
  588. if there is no FragLeader(), the game is tied and we return NULL
  589. ================
  590. */
  591. idPlayer *idMultiplayerGame::FragLimitHit() {
  592. int i;
  593. int fragLimit = gameLocal.serverInfo.GetInt( "si_fragLimit" );
  594. idPlayer *leader;
  595. leader = FragLeader();
  596. if ( !leader ) {
  597. return NULL;
  598. }
  599. if ( fragLimit <= 0 ) {
  600. fragLimit = MP_PLAYER_MAXFRAGS;
  601. }
  602. if ( gameLocal.gameType == GAME_LASTMAN ) {
  603. // we have a leader, check if any other players have frags left
  604. assert( !static_cast< idPlayer * >( leader )->lastManOver );
  605. for( i = 0 ; i < gameLocal.numClients ; i++ ) {
  606. idEntity *ent = gameLocal.entities[ i ];
  607. if ( !ent || !ent->IsType( idPlayer::Type ) ) {
  608. continue;
  609. }
  610. if ( !CanPlay( static_cast< idPlayer * >( ent ) ) ) {
  611. continue;
  612. }
  613. if ( ent == leader ) {
  614. continue;
  615. }
  616. if ( playerState[ ent->entityNumber ].fragCount > 0 ) {
  617. return NULL;
  618. }
  619. }
  620. // there is a leader, his score may even be negative, but no one else has frags left or is !lastManOver
  621. return leader;
  622. } else if ( gameLocal.gameType == GAME_TDM ) {
  623. if ( playerState[ leader->entityNumber ].teamFragCount >= fragLimit ) {
  624. return leader;
  625. }
  626. } else {
  627. if ( playerState[ leader->entityNumber ].fragCount >= fragLimit ) {
  628. return leader;
  629. }
  630. }
  631. return NULL;
  632. }
  633. /*
  634. ================
  635. idMultiplayerGame::TimeLimitHit
  636. ================
  637. */
  638. bool idMultiplayerGame::TimeLimitHit() {
  639. int timeLimit = gameLocal.serverInfo.GetInt( "si_timeLimit" );
  640. if ( timeLimit ) {
  641. if ( gameLocal.time >= matchStartedTime + timeLimit * 60000 ) {
  642. return true;
  643. }
  644. }
  645. return false;
  646. }
  647. /*
  648. ================
  649. idMultiplayerGame::FragLeader
  650. return the current winner ( or a player from the winning team )
  651. NULL if even
  652. ================
  653. */
  654. idPlayer *idMultiplayerGame::FragLeader( void ) {
  655. int i;
  656. int frags[ MAX_CLIENTS ];
  657. idPlayer *leader = NULL;
  658. idEntity *ent;
  659. idPlayer *p;
  660. int high = -9999;
  661. int count = 0;
  662. bool teamLead[ 2 ] = { false, false };
  663. for ( i = 0 ; i < gameLocal.numClients ; i++ ) {
  664. ent = gameLocal.entities[ i ];
  665. if ( !ent || !ent->IsType( idPlayer::Type ) ) {
  666. continue;
  667. }
  668. if ( !CanPlay( static_cast< idPlayer * >( ent ) ) ) {
  669. continue;
  670. }
  671. if ( gameLocal.gameType == GAME_TOURNEY && ent->entityNumber != currentTourneyPlayer[ 0 ] && ent->entityNumber != currentTourneyPlayer[ 1 ] ) {
  672. continue;
  673. }
  674. if ( static_cast< idPlayer * >( ent )->lastManOver ) {
  675. continue;
  676. }
  677. int fragc = ( gameLocal.gameType == GAME_TDM ) ? playerState[i].teamFragCount : playerState[i].fragCount;
  678. if ( fragc > high ) {
  679. high = fragc;
  680. }
  681. frags[ i ] = fragc;
  682. }
  683. for ( i = 0; i < gameLocal.numClients; i++ ) {
  684. ent = gameLocal.entities[ i ];
  685. if ( !ent || !ent->IsType( idPlayer::Type ) ) {
  686. continue;
  687. }
  688. p = static_cast< idPlayer * >( ent );
  689. p->SetLeader( false );
  690. if ( !CanPlay( p ) ) {
  691. continue;
  692. }
  693. if ( gameLocal.gameType == GAME_TOURNEY && ent->entityNumber != currentTourneyPlayer[ 0 ] && ent->entityNumber != currentTourneyPlayer[ 1 ] ) {
  694. continue;
  695. }
  696. if ( p->lastManOver ) {
  697. continue;
  698. }
  699. if ( p->spectating ) {
  700. continue;
  701. }
  702. if ( frags[ i ] >= high ) {
  703. leader = p;
  704. count++;
  705. p->SetLeader( true );
  706. if ( gameLocal.gameType == GAME_TDM ) {
  707. teamLead[ p->team ] = true;
  708. }
  709. }
  710. }
  711. if ( gameLocal.gameType != GAME_TDM ) {
  712. // more than one player at the highest frags
  713. if ( count > 1 ) {
  714. return NULL;
  715. } else {
  716. return leader;
  717. }
  718. } else {
  719. if ( teamLead[ 0 ] && teamLead[ 1 ] ) {
  720. // even game in team play
  721. return NULL;
  722. }
  723. return leader;
  724. }
  725. }
  726. /*
  727. ================
  728. idGameLocal::UpdateWinsLosses
  729. ================
  730. */
  731. void idMultiplayerGame::UpdateWinsLosses( idPlayer *winner ) {
  732. if ( winner ) {
  733. // run back through and update win/loss count
  734. for( int i = 0; i < gameLocal.numClients; i++ ) {
  735. idEntity *ent = gameLocal.entities[ i ];
  736. if ( !ent || !ent->IsType( idPlayer::Type ) ) {
  737. continue;
  738. }
  739. idPlayer *player = static_cast<idPlayer *>(ent);
  740. if ( gameLocal.gameType == GAME_TDM ) {
  741. if ( player == winner || ( player != winner && player->team == winner->team ) ) {
  742. playerState[ i ].wins++;
  743. PlayGlobalSound( player->entityNumber, SND_YOUWIN );
  744. } else {
  745. PlayGlobalSound( player->entityNumber, SND_YOULOSE );
  746. }
  747. } else if ( gameLocal.gameType == GAME_LASTMAN ) {
  748. if ( player == winner ) {
  749. playerState[ i ].wins++;
  750. PlayGlobalSound( player->entityNumber, SND_YOUWIN );
  751. } else if ( !player->wantSpectate ) {
  752. PlayGlobalSound( player->entityNumber, SND_YOULOSE );
  753. }
  754. } else if ( gameLocal.gameType == GAME_TOURNEY ) {
  755. if ( player == winner ) {
  756. playerState[ i ].wins++;
  757. PlayGlobalSound( player->entityNumber, SND_YOUWIN );
  758. } else if ( i == currentTourneyPlayer[ 0 ] || i == currentTourneyPlayer[ 1 ] ) {
  759. PlayGlobalSound( player->entityNumber, SND_YOULOSE );
  760. }
  761. } else {
  762. if ( player == winner ) {
  763. playerState[i].wins++;
  764. PlayGlobalSound( player->entityNumber, SND_YOUWIN );
  765. } else if ( !player->wantSpectate ) {
  766. PlayGlobalSound( player->entityNumber, SND_YOULOSE );
  767. }
  768. }
  769. }
  770. }
  771. if ( winner ) {
  772. lastWinner = winner->entityNumber;
  773. } else {
  774. lastWinner = -1;
  775. }
  776. }
  777. /*
  778. ================
  779. idMultiplayerGame::TeamScore
  780. ================
  781. */
  782. void idMultiplayerGame::TeamScore( int entityNumber, int team, int delta ) {
  783. playerState[ entityNumber ].fragCount += delta;
  784. for( int i = 0 ; i < gameLocal.numClients ; i++ ) {
  785. idEntity *ent = gameLocal.entities[ i ];
  786. if ( !ent || !ent->IsType( idPlayer::Type ) ) {
  787. continue;
  788. }
  789. idPlayer *player = static_cast<idPlayer *>(ent);
  790. if ( player->team == team ) {
  791. playerState[ player->entityNumber ].teamFragCount += delta;
  792. }
  793. }
  794. }
  795. /*
  796. ================
  797. idMultiplayerGame::PlayerDeath
  798. ================
  799. */
  800. void idMultiplayerGame::PlayerDeath( idPlayer *dead, idPlayer *killer, bool telefrag ) {
  801. // don't do PrintMessageEvent and shit
  802. assert( !gameLocal.isClient );
  803. if ( killer ) {
  804. if ( gameLocal.gameType == GAME_LASTMAN ) {
  805. playerState[ dead->entityNumber ].fragCount--;
  806. } else if ( gameLocal.gameType == GAME_TDM ) {
  807. if ( killer == dead || killer->team == dead->team ) {
  808. // suicide or teamkill
  809. TeamScore( killer->entityNumber, killer->team, -1 );
  810. } else {
  811. TeamScore( killer->entityNumber, killer->team, +1 );
  812. }
  813. } else {
  814. playerState[ killer->entityNumber ].fragCount += ( killer == dead ) ? -1 : 1;
  815. }
  816. }
  817. if ( killer && killer == dead ) {
  818. PrintMessageEvent( -1, MSG_SUICIDE, dead->entityNumber );
  819. } else if ( killer ) {
  820. if ( telefrag ) {
  821. PrintMessageEvent( -1, MSG_TELEFRAGGED, dead->entityNumber, killer->entityNumber );
  822. } else if ( gameLocal.gameType == GAME_TDM && dead->team == killer->team ) {
  823. PrintMessageEvent( -1, MSG_KILLEDTEAM, dead->entityNumber, killer->entityNumber );
  824. } else {
  825. PrintMessageEvent( -1, MSG_KILLED, dead->entityNumber, killer->entityNumber );
  826. }
  827. } else {
  828. PrintMessageEvent( -1, MSG_DIED, dead->entityNumber );
  829. playerState[ dead->entityNumber ].fragCount--;
  830. }
  831. }
  832. /*
  833. ================
  834. idMultiplayerGame::PlayerStats
  835. ================
  836. */
  837. void idMultiplayerGame::PlayerStats( int clientNum, char *data, const int len ) {
  838. idEntity *ent;
  839. int team;
  840. *data = 0;
  841. // make sure we don't exceed the client list
  842. if ( clientNum < 0 || clientNum > gameLocal.numClients ) {
  843. return;
  844. }
  845. // find which team this player is on
  846. ent = gameLocal.entities[ clientNum ];
  847. if ( ent && ent->IsType( idPlayer::Type ) ) {
  848. team = static_cast< idPlayer * >(ent)->team;
  849. } else {
  850. return;
  851. }
  852. idStr::snPrintf( data, len, "team=%d score=%ld tks=%ld", team, playerState[ clientNum ].fragCount, playerState[ clientNum ].teamFragCount );
  853. return;
  854. }
  855. /*
  856. ================
  857. idMultiplayerGame::PlayerVote
  858. ================
  859. */
  860. void idMultiplayerGame::PlayerVote( int clientNum, playerVote_t vote ) {
  861. playerState[ clientNum ].vote = vote;
  862. }
  863. /*
  864. ================
  865. idMultiplayerGame::DumpTourneyLine
  866. ================
  867. */
  868. void idMultiplayerGame::DumpTourneyLine( void ) {
  869. int i;
  870. for ( i = 0; i < gameLocal.numClients; i++ ) {
  871. if ( gameLocal.entities[ i ] && gameLocal.entities[ i ]->IsType( idPlayer::Type ) ) {
  872. common->Printf( "client %d: rank %d\n", i, static_cast< idPlayer * >( gameLocal.entities[ i ] )->tourneyRank );
  873. }
  874. }
  875. }
  876. /*
  877. ================
  878. idMultiplayerGame::NewState
  879. ================
  880. */
  881. void idMultiplayerGame::NewState( gameState_t news, idPlayer *player ) {
  882. idBitMsg outMsg;
  883. byte msgBuf[MAX_GAME_MESSAGE_SIZE];
  884. int i;
  885. assert( news != gameState );
  886. assert( !gameLocal.isClient );
  887. gameLocal.DPrintf( "%s -> %s\n", GameStateStrings[ gameState ], GameStateStrings[ news ] );
  888. switch( news ) {
  889. case GAMEON: {
  890. gameLocal.LocalMapRestart();
  891. outMsg.Init( msgBuf, sizeof( msgBuf ) );
  892. outMsg.WriteByte( GAME_RELIABLE_MESSAGE_RESTART );
  893. outMsg.WriteBits( 0, 1 );
  894. networkSystem->ServerSendReliableMessage( -1, outMsg );
  895. PlayGlobalSound( -1, SND_FIGHT );
  896. matchStartedTime = gameLocal.time;
  897. fragLimitTimeout = 0;
  898. for( i = 0; i < gameLocal.numClients; i++ ) {
  899. idEntity *ent = gameLocal.entities[ i ];
  900. if ( !ent || !ent->IsType( idPlayer::Type ) ) {
  901. continue;
  902. }
  903. idPlayer *p = static_cast<idPlayer *>( ent );
  904. p->SetLeader( false ); // don't carry the flag from previous games
  905. if ( gameLocal.gameType == GAME_TOURNEY && currentTourneyPlayer[ 0 ] != i && currentTourneyPlayer[ 1 ] != i ) {
  906. p->ServerSpectate( true );
  907. p->tourneyRank++;
  908. } else {
  909. int fragLimit = gameLocal.serverInfo.GetInt( "si_fragLimit" );
  910. int startingCount = ( gameLocal.gameType == GAME_LASTMAN ) ? fragLimit : 0;
  911. playerState[ i ].fragCount = startingCount;
  912. playerState[ i ].teamFragCount = startingCount;
  913. if ( !static_cast<idPlayer *>(ent)->wantSpectate ) {
  914. static_cast<idPlayer *>(ent)->ServerSpectate( false );
  915. if ( gameLocal.gameType == GAME_TOURNEY ) {
  916. p->tourneyRank = 0;
  917. }
  918. }
  919. }
  920. if ( CanPlay( p ) ) {
  921. p->lastManPresent = true;
  922. } else {
  923. p->lastManPresent = false;
  924. }
  925. }
  926. cvarSystem->SetCVarString( "ui_ready", "Not Ready" );
  927. switchThrottle[ 1 ] = 0; // passby the throttle
  928. startFragLimit = gameLocal.serverInfo.GetInt( "si_fragLimit" );
  929. break;
  930. }
  931. case GAMEREVIEW: {
  932. nextState = INACTIVE; // used to abort a game. cancel out any upcoming state change
  933. // set all players not ready and spectating
  934. for( i = 0; i < gameLocal.numClients; i++ ) {
  935. idEntity *ent = gameLocal.entities[ i ];
  936. if ( !ent || !ent->IsType( idPlayer::Type ) ) {
  937. continue;
  938. }
  939. static_cast< idPlayer *>( ent )->forcedReady = false;
  940. static_cast<idPlayer *>(ent)->ServerSpectate( true );
  941. }
  942. UpdateWinsLosses( player );
  943. break;
  944. }
  945. case SUDDENDEATH: {
  946. PrintMessageEvent( -1, MSG_SUDDENDEATH );
  947. PlayGlobalSound( -1, SND_SUDDENDEATH );
  948. break;
  949. }
  950. case COUNTDOWN: {
  951. idBitMsg outMsg;
  952. byte msgBuf[ 128 ];
  953. warmupEndTime = gameLocal.time + 1000*cvarSystem->GetCVarInteger( "g_countDown" );
  954. outMsg.Init( msgBuf, sizeof( msgBuf ) );
  955. outMsg.WriteByte( GAME_RELIABLE_MESSAGE_WARMUPTIME );
  956. outMsg.WriteLong( warmupEndTime );
  957. networkSystem->ServerSendReliableMessage( -1, outMsg );
  958. break;
  959. }
  960. default:
  961. break;
  962. }
  963. gameState = news;
  964. }
  965. /*
  966. ================
  967. idMultiplayerGame::FillTourneySlots
  968. NOTE: called each frame during warmup to keep the tourney slots filled
  969. ================
  970. */
  971. void idMultiplayerGame::FillTourneySlots( ) {
  972. int i, j, rankmax, rankmaxindex;
  973. idEntity *ent;
  974. idPlayer *p;
  975. // fill up the slots based on tourney ranks
  976. for ( i = 0; i < 2; i++ ) {
  977. if ( currentTourneyPlayer[ i ] != -1 ) {
  978. continue;
  979. }
  980. rankmax = -1;
  981. rankmaxindex = -1;
  982. for ( j = 0; j < gameLocal.numClients; j++ ) {
  983. ent = gameLocal.entities[ j ];
  984. if ( !ent || !ent->IsType( idPlayer::Type ) ) {
  985. continue;
  986. }
  987. if ( currentTourneyPlayer[ 0 ] == j || currentTourneyPlayer[ 1 ] == j ) {
  988. continue;
  989. }
  990. p = static_cast< idPlayer * >( ent );
  991. if ( p->wantSpectate ) {
  992. continue;
  993. }
  994. if ( p->tourneyRank >= rankmax ) {
  995. // when ranks are equal, use time in game
  996. if ( p->tourneyRank == rankmax ) {
  997. assert( rankmaxindex >= 0 );
  998. if ( p->spawnedTime > static_cast< idPlayer * >( gameLocal.entities[ rankmaxindex ] )->spawnedTime ) {
  999. continue;
  1000. }
  1001. }
  1002. rankmax = static_cast< idPlayer * >( ent )->tourneyRank;
  1003. rankmaxindex = j;
  1004. }
  1005. }
  1006. currentTourneyPlayer[ i ] = rankmaxindex; // may be -1 if we found nothing
  1007. }
  1008. }
  1009. /*
  1010. ================
  1011. idMultiplayerGame::UpdateTourneyLine
  1012. we manipulate tourneyRank on player entities for internal ranking. it's easier to deal with.
  1013. but we need a real wait list to be synced down to clients for GUI
  1014. ignore current players, ignore wantSpectate
  1015. ================
  1016. */
  1017. void idMultiplayerGame::UpdateTourneyLine( void ) {
  1018. int i, j, imax, max, globalmax = -1;
  1019. idPlayer *p;
  1020. assert( !gameLocal.isClient );
  1021. if ( gameLocal.gameType != GAME_TOURNEY ) {
  1022. return;
  1023. }
  1024. for ( j = 1; j <= gameLocal.numClients; j++ ) {
  1025. max = -1; imax = -1;
  1026. for ( i = 0; i < gameLocal.numClients; i++ ) {
  1027. if ( currentTourneyPlayer[ 0 ] == i || currentTourneyPlayer[ 1 ] == i ) {
  1028. continue;
  1029. }
  1030. p = static_cast< idPlayer * >( gameLocal.entities[ i ] );
  1031. if ( !p || p->wantSpectate ) {
  1032. continue;
  1033. }
  1034. if ( p->tourneyRank > max && ( globalmax == -1 || p->tourneyRank < globalmax ) ) {
  1035. imax = i;
  1036. max = p->tourneyRank;
  1037. }
  1038. }
  1039. if ( imax == -1 ) {
  1040. break;
  1041. }
  1042. idBitMsg outMsg;
  1043. byte msgBuf[1024];
  1044. outMsg.Init( msgBuf, sizeof( msgBuf ) );
  1045. outMsg.WriteByte( GAME_RELIABLE_MESSAGE_TOURNEYLINE );
  1046. outMsg.WriteByte( j );
  1047. networkSystem->ServerSendReliableMessage( imax, outMsg );
  1048. globalmax = max;
  1049. }
  1050. }
  1051. /*
  1052. ================
  1053. idMultiplayerGame::CycleTourneyPlayers
  1054. ================
  1055. */
  1056. void idMultiplayerGame::CycleTourneyPlayers( ) {
  1057. int i;
  1058. idEntity *ent;
  1059. idPlayer *player;
  1060. currentTourneyPlayer[ 0 ] = -1;
  1061. currentTourneyPlayer[ 1 ] = -1;
  1062. // if any, winner from last round will play again
  1063. if ( lastWinner != -1 ) {
  1064. idEntity *ent = gameLocal.entities[ lastWinner ];
  1065. if ( ent && ent->IsType( idPlayer::Type ) ) {
  1066. currentTourneyPlayer[ 0 ] = lastWinner;
  1067. }
  1068. }
  1069. FillTourneySlots( );
  1070. // force selected players in/out of the game and update the ranks
  1071. for ( i = 0 ; i < gameLocal.numClients ; i++ ) {
  1072. if ( currentTourneyPlayer[ 0 ] == i || currentTourneyPlayer[ 1 ] == i ) {
  1073. player = static_cast<idPlayer *>( gameLocal.entities[ i ] );
  1074. player->ServerSpectate( false );
  1075. } else {
  1076. ent = gameLocal.entities[ i ];
  1077. if ( ent && ent->IsType( idPlayer::Type ) ) {
  1078. player = static_cast<idPlayer *>( gameLocal.entities[ i ] );
  1079. player->ServerSpectate( true );
  1080. }
  1081. }
  1082. }
  1083. UpdateTourneyLine();
  1084. }
  1085. /*
  1086. ================
  1087. idMultiplayerGame::ExecuteVote
  1088. the votes are checked for validity/relevance before they are started
  1089. we assume that they are still legit when reaching here
  1090. ================
  1091. */
  1092. void idMultiplayerGame::ExecuteVote( void ) {
  1093. bool needRestart;
  1094. switch ( vote ) {
  1095. case VOTE_RESTART:
  1096. gameLocal.MapRestart();
  1097. break;
  1098. case VOTE_TIMELIMIT:
  1099. si_timeLimit.SetInteger( atoi( voteValue ) );
  1100. needRestart = gameLocal.NeedRestart();
  1101. cmdSystem->BufferCommandText( CMD_EXEC_NOW, "rescanSI" );
  1102. if ( needRestart ) {
  1103. cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "nextMap" );
  1104. }
  1105. break;
  1106. case VOTE_FRAGLIMIT:
  1107. si_fragLimit.SetInteger( atoi( voteValue ) );
  1108. needRestart = gameLocal.NeedRestart();
  1109. cmdSystem->BufferCommandText( CMD_EXEC_NOW, "rescanSI" );
  1110. if ( needRestart ) {
  1111. cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "nextMap" );
  1112. }
  1113. break;
  1114. case VOTE_GAMETYPE:
  1115. si_gameType.SetString( voteValue );
  1116. gameLocal.MapRestart();
  1117. break;
  1118. case VOTE_KICK:
  1119. cmdSystem->BufferCommandText( CMD_EXEC_NOW, va( "kick %s", voteValue.c_str() ) );
  1120. break;
  1121. case VOTE_MAP:
  1122. si_map.SetString( voteValue );
  1123. gameLocal.MapRestart();
  1124. break;
  1125. case VOTE_SPECTATORS:
  1126. si_spectators.SetBool( !si_spectators.GetBool() );
  1127. needRestart = gameLocal.NeedRestart();
  1128. cmdSystem->BufferCommandText( CMD_EXEC_NOW, "rescanSI" );
  1129. if ( needRestart ) {
  1130. cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "nextMap" );
  1131. }
  1132. break;
  1133. case VOTE_NEXTMAP:
  1134. cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "serverNextMap\n" );
  1135. break;
  1136. }
  1137. }
  1138. /*
  1139. ================
  1140. idMultiplayerGame::CheckVote
  1141. ================
  1142. */
  1143. void idMultiplayerGame::CheckVote( void ) {
  1144. int numVoters, i;
  1145. if ( vote == VOTE_NONE ) {
  1146. return;
  1147. }
  1148. if ( voteExecTime ) {
  1149. if ( gameLocal.time > voteExecTime ) {
  1150. voteExecTime = 0;
  1151. ClientUpdateVote( VOTE_RESET, 0, 0 );
  1152. ExecuteVote();
  1153. vote = VOTE_NONE;
  1154. }
  1155. return;
  1156. }
  1157. // count voting players
  1158. numVoters = 0;
  1159. for ( i = 0; i < gameLocal.numClients; i++ ) {
  1160. idEntity *ent = gameLocal.entities[ i ];
  1161. if ( !ent || !ent->IsType( idPlayer::Type ) ) {
  1162. continue;
  1163. }
  1164. if ( playerState[ i ].vote != PLAYER_VOTE_NONE ) {
  1165. numVoters++;
  1166. }
  1167. }
  1168. if ( !numVoters ) {
  1169. // abort
  1170. vote = VOTE_NONE;
  1171. ClientUpdateVote( VOTE_ABORTED, yesVotes, noVotes );
  1172. return;
  1173. }
  1174. if ( yesVotes / numVoters > 0.5f ) {
  1175. ClientUpdateVote( VOTE_PASSED, yesVotes, noVotes );
  1176. voteExecTime = gameLocal.time + 2000;
  1177. return;
  1178. }
  1179. if ( gameLocal.time > voteTimeOut || noVotes / numVoters >= 0.5f ) {
  1180. ClientUpdateVote( VOTE_FAILED, yesVotes, noVotes );
  1181. vote = VOTE_NONE;
  1182. return;
  1183. }
  1184. }
  1185. /*
  1186. ================
  1187. idMultiplayerGame::Warmup
  1188. ================
  1189. */
  1190. bool idMultiplayerGame::Warmup() {
  1191. return ( gameState == WARMUP );
  1192. }
  1193. /*
  1194. ================
  1195. idMultiplayerGame::Run
  1196. ================
  1197. */
  1198. void idMultiplayerGame::Run() {
  1199. int i, timeLeft;
  1200. idPlayer *player;
  1201. int gameReviewPause;
  1202. assert( gameLocal.isMultiplayer );
  1203. assert( !gameLocal.isClient );
  1204. pureReady = true;
  1205. if ( gameState == INACTIVE ) {
  1206. lastGameType = gameLocal.gameType;
  1207. NewState( WARMUP );
  1208. }
  1209. CheckVote();
  1210. CheckRespawns();
  1211. if ( nextState != INACTIVE && gameLocal.time > nextStateSwitch ) {
  1212. NewState( nextState );
  1213. nextState = INACTIVE;
  1214. }
  1215. // don't update the ping every frame to save bandwidth
  1216. if ( gameLocal.time > pingUpdateTime ) {
  1217. for ( i = 0; i < gameLocal.numClients; i++ ) {
  1218. playerState[i].ping = networkSystem->ServerGetClientPing( i );
  1219. }
  1220. pingUpdateTime = gameLocal.time + 1000;
  1221. }
  1222. warmupText = "";
  1223. switch( gameState ) {
  1224. case GAMEREVIEW: {
  1225. if ( nextState == INACTIVE ) {
  1226. gameReviewPause = cvarSystem->GetCVarInteger( "g_gameReviewPause" );
  1227. nextState = NEXTGAME;
  1228. nextStateSwitch = gameLocal.time + 1000 * gameReviewPause;
  1229. }
  1230. break;
  1231. }
  1232. case NEXTGAME: {
  1233. if ( nextState == INACTIVE ) {
  1234. // game rotation, new map, gametype etc.
  1235. if ( gameLocal.NextMap() ) {
  1236. cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "serverMapRestart\n" );
  1237. return;
  1238. }
  1239. NewState( WARMUP );
  1240. if ( gameLocal.gameType == GAME_TOURNEY ) {
  1241. CycleTourneyPlayers();
  1242. }
  1243. // put everyone back in from endgame spectate
  1244. for ( i = 0; i < gameLocal.numClients; i++ ) {
  1245. idEntity *ent = gameLocal.entities[ i ];
  1246. if ( ent && ent->IsType( idPlayer::Type ) ) {
  1247. if ( !static_cast< idPlayer * >( ent )->wantSpectate ) {
  1248. CheckRespawns( static_cast<idPlayer *>( ent ) );
  1249. }
  1250. }
  1251. }
  1252. }
  1253. break;
  1254. }
  1255. case WARMUP: {
  1256. if ( AllPlayersReady() ) {
  1257. NewState( COUNTDOWN );
  1258. nextState = GAMEON;
  1259. nextStateSwitch = gameLocal.time + 1000 * cvarSystem->GetCVarInteger( "g_countDown" );
  1260. }
  1261. warmupText = "Warming up.. waiting for players to get ready";
  1262. one = two = three = false;
  1263. break;
  1264. }
  1265. case COUNTDOWN: {
  1266. timeLeft = ( nextStateSwitch - gameLocal.time ) / 1000 + 1;
  1267. if ( timeLeft == 3 && !three ) {
  1268. PlayGlobalSound( -1, SND_THREE );
  1269. three = true;
  1270. } else if ( timeLeft == 2 && !two ) {
  1271. PlayGlobalSound( -1, SND_TWO );
  1272. two = true;
  1273. } else if ( timeLeft == 1 && !one ) {
  1274. PlayGlobalSound( -1, SND_ONE );
  1275. one = true;
  1276. }
  1277. warmupText = va( "Match starts in %i", timeLeft );
  1278. break;
  1279. }
  1280. case GAMEON: {
  1281. player = FragLimitHit();
  1282. if ( player ) {
  1283. // delay between detecting frag limit and ending game. let the death anims play
  1284. if ( !fragLimitTimeout ) {
  1285. common->DPrintf( "enter FragLimit timeout, player %d is leader\n", player->entityNumber );
  1286. fragLimitTimeout = gameLocal.time + FRAGLIMIT_DELAY;
  1287. }
  1288. if ( gameLocal.time > fragLimitTimeout ) {
  1289. NewState( GAMEREVIEW, player );
  1290. PrintMessageEvent( -1, MSG_FRAGLIMIT, player->entityNumber );
  1291. }
  1292. } else {
  1293. if ( fragLimitTimeout ) {
  1294. // frag limit was hit and cancelled. means the two teams got even during FRAGLIMIT_DELAY
  1295. // enter sudden death, the next frag leader will win
  1296. SuddenRespawn();
  1297. PrintMessageEvent( -1, MSG_HOLYSHIT );
  1298. fragLimitTimeout = 0;
  1299. NewState( SUDDENDEATH );
  1300. } else if ( TimeLimitHit() ) {
  1301. player = FragLeader();
  1302. if ( !player ) {
  1303. NewState( SUDDENDEATH );
  1304. } else {
  1305. NewState( GAMEREVIEW, player );
  1306. PrintMessageEvent( -1, MSG_TIMELIMIT );
  1307. }
  1308. }
  1309. }
  1310. break;
  1311. }
  1312. case SUDDENDEATH: {
  1313. player = FragLeader();
  1314. if ( player ) {
  1315. if ( !fragLimitTimeout ) {
  1316. common->DPrintf( "enter sudden death FragLeader timeout, player %d is leader\n", player->entityNumber );
  1317. fragLimitTimeout = gameLocal.time + FRAGLIMIT_DELAY;
  1318. }
  1319. if ( gameLocal.time > fragLimitTimeout ) {
  1320. NewState( GAMEREVIEW, player );
  1321. PrintMessageEvent( -1, MSG_FRAGLIMIT, player->entityNumber );
  1322. }
  1323. } else if ( fragLimitTimeout ) {
  1324. SuddenRespawn();
  1325. PrintMessageEvent( -1, MSG_HOLYSHIT );
  1326. fragLimitTimeout = 0;
  1327. }
  1328. break;
  1329. }
  1330. }
  1331. }
  1332. /*
  1333. ================
  1334. idMultiplayerGame::UpdateMainGui
  1335. ================
  1336. */
  1337. void idMultiplayerGame::UpdateMainGui( void ) {
  1338. int i;
  1339. mainGui->SetStateInt( "readyon", gameState == WARMUP ? 1 : 0 );
  1340. mainGui->SetStateInt( "readyoff", gameState != WARMUP ? 1 : 0 );
  1341. idStr strReady = cvarSystem->GetCVarString( "ui_ready" );
  1342. if ( strReady.Icmp( "ready") == 0 ){
  1343. strReady = common->GetLanguageDict()->GetString( "#str_04248" );
  1344. } else {
  1345. strReady = common->GetLanguageDict()->GetString( "#str_04247" );
  1346. }
  1347. mainGui->SetStateString( "ui_ready", strReady );
  1348. mainGui->SetStateInt( "teamon", gameLocal.gameType == GAME_TDM ? 1 : 0 );
  1349. mainGui->SetStateInt( "teamoff", gameLocal.gameType != GAME_TDM ? 1 : 0 );
  1350. if ( gameLocal.gameType == GAME_TDM ) {
  1351. idPlayer *p = gameLocal.GetClientByNum( gameLocal.localClientNum );
  1352. mainGui->SetStateInt( "team", p->team );
  1353. }
  1354. // setup vote
  1355. mainGui->SetStateInt( "voteon", ( vote != VOTE_NONE && !voted ) ? 1 : 0 );
  1356. mainGui->SetStateInt( "voteoff", ( vote != VOTE_NONE && !voted ) ? 0 : 1 );
  1357. // last man hack
  1358. mainGui->SetStateInt( "isLastMan", gameLocal.gameType == GAME_LASTMAN ? 1 : 0 );
  1359. // send the current serverinfo values
  1360. for ( i = 0; i < gameLocal.serverInfo.GetNumKeyVals(); i++ ) {
  1361. const idKeyValue *keyval = gameLocal.serverInfo.GetKeyVal( i );
  1362. mainGui->SetStateString( keyval->GetKey(), keyval->GetValue() );
  1363. }
  1364. mainGui->StateChanged( gameLocal.time );
  1365. #if defined( __linux__ )
  1366. // replacing the oh-so-useful s_reverse with sound backend prompt
  1367. mainGui->SetStateString( "driver_prompt", "1" );
  1368. #else
  1369. mainGui->SetStateString( "driver_prompt", "0" );
  1370. #endif
  1371. }
  1372. /*
  1373. ================
  1374. idMultiplayerGame::StartMenu
  1375. ================
  1376. */
  1377. idUserInterface* idMultiplayerGame::StartMenu( void ) {
  1378. if ( mainGui == NULL ) {
  1379. return NULL;
  1380. }
  1381. int i, j;
  1382. if ( currentMenu ) {
  1383. currentMenu = 0;
  1384. cvarSystem->SetCVarBool( "ui_chat", false );
  1385. } else {
  1386. if ( nextMenu >= 2 ) {
  1387. currentMenu = nextMenu;
  1388. } else {
  1389. // for default and explicit
  1390. currentMenu = 1;
  1391. }
  1392. cvarSystem->SetCVarBool( "ui_chat", true );
  1393. }
  1394. nextMenu = 0;
  1395. gameLocal.sessionCommand = ""; // in case we used "game_startMenu" to trigger the menu
  1396. if ( currentMenu == 1 ) {
  1397. UpdateMainGui();
  1398. // UpdateMainGui sets most things, but it doesn't set these because
  1399. // it'd be pointless and/or harmful to set them every frame (for various reasons)
  1400. // Currenty the gui doesn't update properly if they change anyway, so we'll leave it like this.
  1401. // setup callvote
  1402. if ( vote == VOTE_NONE ) {
  1403. bool callvote_ok = false;
  1404. for ( i = 0; i < VOTE_COUNT; i++ ) {
  1405. // flag on means vote is denied, so default value 0 means all votes and -1 disables
  1406. mainGui->SetStateInt( va( "vote%d", i ), g_voteFlags.GetInteger() & ( 1 << i ) ? 0 : 1 );
  1407. if ( !( g_voteFlags.GetInteger() & ( 1 << i ) ) ) {
  1408. callvote_ok = true;
  1409. }
  1410. }
  1411. mainGui->SetStateInt( "callvote", callvote_ok );
  1412. } else {
  1413. mainGui->SetStateInt( "callvote", 2 );
  1414. }
  1415. // player kick data
  1416. idStr kickList;
  1417. j = 0;
  1418. for ( i = 0; i < gameLocal.numClients; i++ ) {
  1419. if ( gameLocal.entities[ i ] && gameLocal.entities[ i ]->IsType( idPlayer::Type ) ) {
  1420. if ( kickList.Length() ) {
  1421. kickList += ";";
  1422. }
  1423. kickList += va( "\"%d - %s\"", i, gameLocal.userInfo[ i ].GetString( "ui_name" ) );
  1424. kickVoteMap[ j ] = i;
  1425. j++;
  1426. }
  1427. }
  1428. mainGui->SetStateString( "kickChoices", kickList );
  1429. mainGui->SetStateString( "chattext", "" );
  1430. mainGui->Activate( true, gameLocal.time );
  1431. return mainGui;
  1432. } else if ( currentMenu == 2 ) {
  1433. // the setup is done in MessageMode
  1434. msgmodeGui->Activate( true, gameLocal.time );
  1435. cvarSystem->SetCVarBool( "ui_chat", true );
  1436. return msgmodeGui;
  1437. }
  1438. return NULL;
  1439. }
  1440. /*
  1441. ================
  1442. idMultiplayerGame::DisableMenu
  1443. ================
  1444. */
  1445. void idMultiplayerGame::DisableMenu( void ) {
  1446. gameLocal.sessionCommand = ""; // in case we used "game_startMenu" to trigger the menu
  1447. if ( currentMenu == 1 ) {
  1448. mainGui->Activate( false, gameLocal.time );
  1449. } else if ( currentMenu == 2 ) {
  1450. msgmodeGui->Activate( false, gameLocal.time );
  1451. }
  1452. currentMenu = 0;
  1453. nextMenu = 0;
  1454. cvarSystem->SetCVarBool( "ui_chat", false );
  1455. }
  1456. /*
  1457. ================
  1458. idMultiplayerGame::SetMapShot
  1459. ================
  1460. */
  1461. void idMultiplayerGame::SetMapShot( void ) {
  1462. char screenshot[ MAX_STRING_CHARS ];
  1463. int mapNum = mapList->GetSelection( NULL, 0 );
  1464. const idDict *dict = NULL;
  1465. if ( mapNum >= 0 ) {
  1466. dict = fileSystem->GetMapDecl( mapNum );
  1467. }
  1468. fileSystem->FindMapScreenshot( dict ? dict->GetString( "path" ) : "", screenshot, MAX_STRING_CHARS );
  1469. mainGui->SetStateString( "current_levelshot", screenshot );
  1470. }
  1471. /*
  1472. ================
  1473. idMultiplayerGame::HandleGuiCommands
  1474. ================
  1475. */
  1476. const char* idMultiplayerGame::HandleGuiCommands( const char *_menuCommand ) {
  1477. idUserInterface *currentGui;
  1478. const char *voteValue;
  1479. int vote_clientNum;
  1480. int icmd;
  1481. idCmdArgs args;
  1482. if ( !_menuCommand[ 0 ] ) {
  1483. common->Printf( "idMultiplayerGame::HandleGuiCommands: empty command\n" );
  1484. return "continue";
  1485. }
  1486. assert( currentMenu );
  1487. if ( currentMenu == 1 ) {
  1488. currentGui = mainGui;
  1489. } else {
  1490. currentGui = msgmodeGui;
  1491. }
  1492. args.TokenizeString( _menuCommand, false );
  1493. for( icmd = 0; icmd < args.Argc(); ) {
  1494. const char *cmd = args.Argv( icmd++ );
  1495. if ( !idStr::Icmp( cmd, ";" ) ) {
  1496. continue;
  1497. } else if ( !idStr::Icmp( cmd, "video" ) ) {
  1498. idStr vcmd;
  1499. if ( args.Argc() - icmd >= 1 ) {
  1500. vcmd = args.Argv( icmd++ );
  1501. }
  1502. int oldSpec = cvarSystem->GetCVarInteger( "com_machineSpec" );
  1503. if ( idStr::Icmp( vcmd, "low" ) == 0 ) {
  1504. cvarSystem->SetCVarInteger( "com_machineSpec", 0 );
  1505. } else if ( idStr::Icmp( vcmd, "medium" ) == 0 ) {
  1506. cvarSystem->SetCVarInteger( "com_machineSpec", 1 );
  1507. } else if ( idStr::Icmp( vcmd, "high" ) == 0 ) {
  1508. cvarSystem->SetCVarInteger( "com_machineSpec", 2 );
  1509. } else if ( idStr::Icmp( vcmd, "ultra" ) == 0 ) {
  1510. cvarSystem->SetCVarInteger( "com_machineSpec", 3 );
  1511. } else if ( idStr::Icmp( vcmd, "recommended" ) == 0 ) {
  1512. cmdSystem->BufferCommandText( CMD_EXEC_NOW, "setMachineSpec\n" );
  1513. }
  1514. if ( oldSpec != cvarSystem->GetCVarInteger( "com_machineSpec" ) ) {
  1515. currentGui->SetStateInt( "com_machineSpec", cvarSystem->GetCVarInteger( "com_machineSpec" ) );
  1516. currentGui->StateChanged( gameLocal.realClientTime );
  1517. cmdSystem->BufferCommandText( CMD_EXEC_NOW, "execMachineSpec\n" );
  1518. }
  1519. if ( idStr::Icmp( vcmd, "restart" ) == 0) {
  1520. cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "vid_restart\n" );
  1521. }
  1522. continue;
  1523. } else if ( !idStr::Icmp( cmd, "play" ) ) {
  1524. if ( args.Argc() - icmd >= 1 ) {
  1525. idStr snd = args.Argv( icmd++ );
  1526. int channel = 1;
  1527. if ( snd.Length() == 1 ) {
  1528. channel = atoi( snd );
  1529. snd = args.Argv( icmd++ );
  1530. }
  1531. gameSoundWorld->PlayShaderDirectly( snd, channel );
  1532. }
  1533. continue;
  1534. } else if ( !idStr::Icmp( cmd, "mpSkin" ) ) {
  1535. idStr skin;
  1536. if ( args.Argc() - icmd >= 1 ) {
  1537. skin = args.Argv( icmd++ );
  1538. cvarSystem->SetCVarString( "ui_skin", skin );
  1539. }
  1540. SetMenuSkin();
  1541. continue;
  1542. } else if ( !idStr::Icmp( cmd, "quit" ) ) {
  1543. cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "quit\n" );
  1544. return NULL;
  1545. } else if ( !idStr::Icmp( cmd, "disconnect" ) ) {
  1546. cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "disconnect\n" );
  1547. return NULL;
  1548. } else if ( !idStr::Icmp( cmd, "close" ) ) {
  1549. DisableMenu( );
  1550. return NULL;
  1551. } else if ( !idStr::Icmp( cmd, "spectate" ) ) {
  1552. ToggleSpectate();
  1553. DisableMenu( );
  1554. return NULL;
  1555. } else if ( !idStr::Icmp( cmd, "chatmessage" ) ) {
  1556. int mode = currentGui->State().GetInt( "messagemode" );
  1557. if ( mode ) {
  1558. cmdSystem->BufferCommandText( CMD_EXEC_NOW, va( "sayTeam \"%s\"", currentGui->State().GetString( "chattext" ) ) );
  1559. } else {
  1560. cmdSystem->BufferCommandText( CMD_EXEC_NOW, va( "say \"%s\"", currentGui->State().GetString( "chattext" ) ) );
  1561. }
  1562. currentGui->SetStateString( "chattext", "" );
  1563. if ( currentMenu == 1 ) {
  1564. return "continue";
  1565. } else {
  1566. DisableMenu();
  1567. return NULL;
  1568. }
  1569. } else if ( !idStr::Icmp( cmd, "readytoggle" ) ) {
  1570. ToggleReady( );
  1571. DisableMenu( );
  1572. return NULL;
  1573. } else if ( !idStr::Icmp( cmd, "teamtoggle" ) ) {
  1574. ToggleTeam( );
  1575. DisableMenu( );
  1576. return NULL;
  1577. } else if ( !idStr::Icmp( cmd, "callVote" ) ) {
  1578. vote_flags_t voteIndex = (vote_flags_t)mainGui->State().GetInt( "voteIndex" );
  1579. if ( voteIndex == VOTE_MAP ) {
  1580. int mapNum = mapList->GetSelection( NULL, 0 );
  1581. if ( mapNum >= 0 ) {
  1582. const idDict *dict = fileSystem->GetMapDecl( mapNum );
  1583. if ( dict ) {
  1584. ClientCallVote( VOTE_MAP, dict->GetString( "path" ) );
  1585. }
  1586. }
  1587. } else {
  1588. voteValue = mainGui->State().GetString( "str_voteValue" );
  1589. if ( voteIndex == VOTE_KICK ) {
  1590. vote_clientNum = kickVoteMap[ atoi( voteValue ) ];
  1591. ClientCallVote( voteIndex, va( "%d", vote_clientNum ) );
  1592. } else {
  1593. ClientCallVote( voteIndex, voteValue );
  1594. }
  1595. }
  1596. DisableMenu();
  1597. return NULL;
  1598. } else if ( !idStr::Icmp( cmd, "voteyes" ) ) {
  1599. CastVote( gameLocal.localClientNum, true );
  1600. DisableMenu();
  1601. return NULL;
  1602. } else if ( !idStr::Icmp( cmd, "voteno" ) ) {
  1603. CastVote( gameLocal.localClientNum, false );
  1604. DisableMenu();
  1605. return NULL;
  1606. } else if ( !idStr::Icmp( cmd, "bind" ) ) {
  1607. if ( args.Argc() - icmd >= 2 ) {
  1608. idStr key = args.Argv( icmd++ );
  1609. idStr bind = args.Argv( icmd++ );
  1610. cmdSystem->BufferCommandText( CMD_EXEC_NOW, va( "bindunbindtwo \"%s\" \"%s\"", key.c_str(), bind.c_str() ) );
  1611. mainGui->SetKeyBindingNames();
  1612. }
  1613. continue;
  1614. } else if ( !idStr::Icmp( cmd, "clearbind" ) ) {
  1615. if ( args.Argc() - icmd >= 1 ) {
  1616. idStr bind = args.Argv( icmd++ );
  1617. cmdSystem->BufferCommandText( CMD_EXEC_NOW, va( "unbind \"%s\"", bind.c_str() ) );
  1618. mainGui->SetKeyBindingNames();
  1619. }
  1620. continue;
  1621. } else if ( !idStr::Icmp( cmd, "MAPScan" ) ) {
  1622. const char *gametype = gameLocal.serverInfo.GetString( "si_gameType" );
  1623. if ( gametype == NULL || *gametype == 0 || idStr::Icmp( gametype, "singleplayer" ) == 0 ) {
  1624. gametype = "Deathmatch";
  1625. }
  1626. int i, num;
  1627. idStr si_map = gameLocal.serverInfo.GetString("si_map");
  1628. const idDict *dict;
  1629. mapList->Clear();
  1630. mapList->SetSelection( -1 );
  1631. num = fileSystem->GetNumMaps();
  1632. for ( i = 0; i < num; i++ ) {
  1633. dict = fileSystem->GetMapDecl( i );
  1634. if ( dict ) {
  1635. // any MP gametype supported
  1636. bool isMP = false;
  1637. int igt = GAME_SP + 1;
  1638. while ( si_gameTypeArgs[ igt ] ) {
  1639. if ( dict->GetBool( si_gameTypeArgs[ igt ] ) ) {
  1640. isMP = true;
  1641. break;
  1642. }
  1643. igt++;
  1644. }
  1645. if ( isMP ) {
  1646. const char *mapName = dict->GetString( "name" );
  1647. if ( mapName[0] == '\0' ) {
  1648. mapName = dict->GetString( "path" );
  1649. }
  1650. mapName = common->GetLanguageDict()->GetString( mapName );
  1651. mapList->Add( i, mapName );
  1652. if ( !si_map.Icmp( dict->GetString( "path" ) ) ) {
  1653. mapList->SetSelection( mapList->Num() - 1 );
  1654. }
  1655. }
  1656. }
  1657. }
  1658. // set the current level shot
  1659. SetMapShot( );
  1660. return "continue";
  1661. } else if ( !idStr::Icmp( cmd, "click_maplist" ) ) {
  1662. SetMapShot( );
  1663. return "continue";
  1664. } else if ( strstr( cmd, "sound" ) == cmd ) {
  1665. // pass that back to the core, will know what to do with it
  1666. return _menuCommand;
  1667. }
  1668. common->Printf( "idMultiplayerGame::HandleGuiCommands: '%s' unknown\n", cmd );
  1669. }
  1670. return "continue";
  1671. }
  1672. /*
  1673. ================
  1674. idMultiplayerGame::Draw
  1675. ================
  1676. */
  1677. bool idMultiplayerGame::Draw( int clientNum ) {
  1678. idPlayer *player, *viewPlayer;
  1679. // clear the render entities for any players that don't need
  1680. // icons and which might not be thinking because they weren't in
  1681. // the last snapshot.
  1682. for ( int i = 0; i < gameLocal.numClients; i++ ) {
  1683. player = static_cast<idPlayer *>( gameLocal.entities[ i ] );
  1684. if ( player && !player->NeedsIcon() ) {
  1685. player->HidePlayerIcons();
  1686. }
  1687. }
  1688. player = viewPlayer = static_cast<idPlayer *>( gameLocal.entities[ clientNum ] );
  1689. if ( player == NULL ) {
  1690. return false;
  1691. }
  1692. if ( player->spectating ) {
  1693. viewPlayer = static_cast<idPlayer *>( gameLocal.entities[ player->spectator ] );
  1694. if ( viewPlayer == NULL ) {
  1695. return false;
  1696. }
  1697. }
  1698. UpdatePlayerRanks();
  1699. UpdateHud( viewPlayer, player->hud );
  1700. // use the hud of the local player
  1701. viewPlayer->playerView.RenderPlayerView( player->hud );
  1702. if ( currentMenu ) {
  1703. #if 0
  1704. // uncomment this if you want to track when players are in a menu
  1705. if ( !bCurrentMenuMsg ) {
  1706. idBitMsg outMsg;
  1707. byte msgBuf[ 128 ];
  1708. outMsg.Init( msgBuf, sizeof( msgBuf ) );
  1709. outMsg.WriteByte( GAME_RELIABLE_MESSAGE_MENU );
  1710. outMsg.WriteBits( 1, 1 );
  1711. networkSystem->ClientSendReliableMessage( outMsg );
  1712. bCurrentMenuMsg = true;
  1713. }
  1714. #endif
  1715. if ( player->wantSpectate ) {
  1716. mainGui->SetStateString( "spectext", common->GetLanguageDict()->GetString( "#str_04249" ) );
  1717. } else {
  1718. mainGui->SetStateString( "spectext", common->GetLanguageDict()->GetString( "#str_04250" ) );
  1719. }
  1720. DrawChat();
  1721. if ( currentMenu == 1 ) {
  1722. UpdateMainGui();
  1723. mainGui->Redraw( gameLocal.time );
  1724. } else {
  1725. msgmodeGui->Redraw( gameLocal.time );
  1726. }
  1727. } else {
  1728. #if 0
  1729. // uncomment this if you want to track when players are in a menu
  1730. if ( bCurrentMenuMsg ) {
  1731. idBitMsg outMsg;
  1732. byte msgBuf[ 128 ];
  1733. outMsg.Init( msgBuf, sizeof( msgBuf ) );
  1734. outMsg.WriteByte( GAME_RELIABLE_MESSAGE_MENU );
  1735. outMsg.WriteBits( 0, 1 );
  1736. networkSystem->ClientSendReliableMessage( outMsg );
  1737. bCurrentMenuMsg = false;
  1738. }
  1739. #endif
  1740. if ( player->spectating ) {
  1741. idStr spectatetext[ 2 ];
  1742. int ispecline = 0;
  1743. if ( gameLocal.gameType == GAME_TOURNEY ) {
  1744. if ( !player->wantSpectate ) {
  1745. spectatetext[ 0 ] = common->GetLanguageDict()->GetString( "#str_04246" );
  1746. switch ( player->tourneyLine ) {
  1747. case 0:
  1748. spectatetext[ 0 ] += common->GetLanguageDict()->GetString( "#str_07003" );
  1749. break;
  1750. case 1:
  1751. spectatetext[ 0 ] += common->GetLanguageDict()->GetString( "#str_07004" );
  1752. break;
  1753. case 2:
  1754. spectatetext[ 0 ] += common->GetLanguageDict()->GetString( "#str_07005" );
  1755. break;
  1756. default:
  1757. spectatetext[ 0 ] += va( common->GetLanguageDict()->GetString( "#str_07006" ), player->tourneyLine );
  1758. break;
  1759. }
  1760. ispecline++;
  1761. }
  1762. } else if ( gameLocal.gameType == GAME_LASTMAN ) {
  1763. if ( !player->wantSpectate ) {
  1764. spectatetext[ 0 ] = common->GetLanguageDict()->GetString( "#str_07007" );
  1765. ispecline++;
  1766. }
  1767. }
  1768. if ( player->spectator != player->entityNumber ) {
  1769. spectatetext[ ispecline ] = va( common->GetLanguageDict()->GetString( "#str_07008" ), viewPlayer->GetUserInfo()->GetString( "ui_name" ) );
  1770. } else if ( !ispecline ) {
  1771. spectatetext[ 0 ] = common->GetLanguageDict()->GetString( "#str_04246" );
  1772. }
  1773. spectateGui->SetStateString( "spectatetext0", spectatetext[0].c_str() );
  1774. spectateGui->SetStateString( "spectatetext1", spectatetext[1].c_str() );
  1775. if ( vote != VOTE_NONE ) {
  1776. spectateGui->SetStateString( "vote", va( "%s (y: %d n: %d)", voteString.c_str(), (int)yesVotes, (int)noVotes ) );
  1777. } else {
  1778. spectateGui->SetStateString( "vote", "" );
  1779. }
  1780. spectateGui->Redraw( gameLocal.time );
  1781. }
  1782. DrawChat();
  1783. DrawScoreBoard( player );
  1784. }
  1785. return true;
  1786. }
  1787. /*
  1788. ================
  1789. idMultiplayerGame::UpdateHud
  1790. ================
  1791. */
  1792. void idMultiplayerGame::UpdateHud( idPlayer *player, idUserInterface *hud ) {
  1793. int i;
  1794. if ( !hud ) {
  1795. return;
  1796. }
  1797. hud->SetStateBool( "warmup", Warmup() );
  1798. if ( gameState == WARMUP ) {
  1799. if ( player->IsReady() ) {
  1800. hud->SetStateString( "warmuptext", common->GetLanguageDict()->GetString( "#str_04251" ) );
  1801. } else {
  1802. hud->SetStateString( "warmuptext", common->GetLanguageDict()->GetString( "#str_07002" ) );
  1803. }
  1804. }
  1805. hud->SetStateString( "timer", ( Warmup() ) ? common->GetLanguageDict()->GetString( "#str_04251" ) : ( gameState == SUDDENDEATH ) ? common->GetLanguageDict()->GetString( "#str_04252" ) : GameTime() );
  1806. if ( vote != VOTE_NONE ) {
  1807. hud->SetStateString( "vote", va( "%s (y: %d n: %d)", voteString.c_str(), (int)yesVotes, (int)noVotes ) );
  1808. } else {
  1809. hud->SetStateString( "vote", "" );
  1810. }
  1811. hud->SetStateInt( "rank_self", 0 );
  1812. if ( gameState == GAMEON ) {
  1813. for ( i = 0; i < numRankedPlayers; i++ ) {
  1814. if ( gameLocal.gameType == GAME_TDM ) {
  1815. hud->SetStateInt( va( "player%i_score", i+1 ), playerState[ rankedPlayers[ i ]->entityNumber ].teamFragCount );
  1816. } else {
  1817. hud->SetStateInt( va( "player%i_score", i+1 ), playerState[ rankedPlayers[ i ]->entityNumber ].fragCount );
  1818. }
  1819. hud->SetStateInt( va( "rank%i", i+1 ), 1 );
  1820. UpdateRankColor( hud, "rank%i_color%i", i+1, rankedPlayers[ i ]->colorBar );
  1821. if ( rankedPlayers[ i ] == player ) {
  1822. hud->SetStateInt( "rank_self", i+1 );
  1823. }
  1824. }
  1825. }
  1826. for ( i = ( gameState == GAMEON ? numRankedPlayers : 0 ) ; i < 5; i++ ) {
  1827. hud->SetStateString( va( "player%i", i+1 ), "" );
  1828. hud->SetStateString( va( "player%i_score", i+1 ), "" );
  1829. hud->SetStateInt( va( "rank%i", i+1 ), 0 );
  1830. }
  1831. }
  1832. /*
  1833. ================
  1834. idMultiplayerGame::DrawScoreBoard
  1835. ================
  1836. */
  1837. void idMultiplayerGame::DrawScoreBoard( idPlayer *player ) {
  1838. if ( player->scoreBoardOpen || gameState == GAMEREVIEW ) {
  1839. if ( !playerState[ player->entityNumber ].scoreBoardUp ) {
  1840. scoreBoard->Activate( true, gameLocal.time );
  1841. playerState[ player->entityNumber ].scoreBoardUp = true;
  1842. }
  1843. UpdateScoreboard( scoreBoard, player );
  1844. } else {
  1845. if ( playerState[ player->entityNumber ].scoreBoardUp ) {
  1846. scoreBoard->Activate( false, gameLocal.time );
  1847. playerState[ player->entityNumber ].scoreBoardUp = false;
  1848. }
  1849. }
  1850. }
  1851. /*
  1852. ===============
  1853. idMultiplayerGame::ClearChatData
  1854. ===============
  1855. */
  1856. void idMultiplayerGame::ClearChatData() {
  1857. chatHistoryIndex = 0;
  1858. chatHistorySize = 0;
  1859. chatDataUpdated = true;
  1860. }
  1861. /*
  1862. ===============
  1863. idMultiplayerGame::AddChatLine
  1864. ===============
  1865. */
  1866. void idMultiplayerGame::AddChatLine( const char *fmt, ... ) {
  1867. idStr temp;
  1868. va_list argptr;
  1869. va_start( argptr, fmt );
  1870. vsprintf( temp, fmt, argptr );
  1871. va_end( argptr );
  1872. gameLocal.Printf( "%s\n", temp.c_str() );
  1873. chatHistory[ chatHistoryIndex % NUM_CHAT_NOTIFY ].line = temp;
  1874. chatHistory[ chatHistoryIndex % NUM_CHAT_NOTIFY ].fade = 6;
  1875. chatHistoryIndex++;
  1876. if ( chatHistorySize < NUM_CHAT_NOTIFY ) {
  1877. chatHistorySize++;
  1878. }
  1879. chatDataUpdated = true;
  1880. lastChatLineTime = gameLocal.time;
  1881. }
  1882. /*
  1883. ===============
  1884. idMultiplayerGame::DrawChat
  1885. ===============
  1886. */
  1887. void idMultiplayerGame::DrawChat() {
  1888. int i, j;
  1889. if ( guiChat ) {
  1890. if ( gameLocal.time - lastChatLineTime > CHAT_FADE_TIME ) {
  1891. if ( chatHistorySize > 0 ) {
  1892. for ( i = chatHistoryIndex - chatHistorySize; i < chatHistoryIndex; i++ ) {
  1893. chatHistory[ i % NUM_CHAT_NOTIFY ].fade--;
  1894. if ( chatHistory[ i % NUM_CHAT_NOTIFY ].fade < 0 ) {
  1895. chatHistorySize--; // this assumes the removals are always at the beginning
  1896. }
  1897. }
  1898. chatDataUpdated = true;
  1899. }
  1900. lastChatLineTime = gameLocal.time;
  1901. }
  1902. if ( chatDataUpdated ) {
  1903. j = 0;
  1904. i = chatHistoryIndex - chatHistorySize;
  1905. while ( i < chatHistoryIndex ) {
  1906. guiChat->SetStateString( va( "chat%i", j ), chatHistory[ i % NUM_CHAT_NOTIFY ].line );
  1907. // don't set alpha above 4, the gui only knows that
  1908. guiChat->SetStateInt( va( "alpha%i", j ), Min( 4, (int)chatHistory[ i % NUM_CHAT_NOTIFY ].fade ) );
  1909. j++; i++;
  1910. }
  1911. while ( j < NUM_CHAT_NOTIFY ) {
  1912. guiChat->SetStateString( va( "chat%i", j ), "" );
  1913. j++;
  1914. }
  1915. guiChat->Activate( true, gameLocal.time );
  1916. chatDataUpdated = false;
  1917. }
  1918. guiChat->Redraw( gameLocal.time );
  1919. }
  1920. }
  1921. const int ASYNC_PLAYER_FRAG_BITS = -idMath::BitsForInteger( MP_PLAYER_MAXFRAGS - MP_PLAYER_MINFRAGS ); // player can have negative frags
  1922. const int ASYNC_PLAYER_WINS_BITS = idMath::BitsForInteger( MP_PLAYER_MAXWINS );
  1923. const int ASYNC_PLAYER_PING_BITS = idMath::BitsForInteger( MP_PLAYER_MAXPING );
  1924. /*
  1925. ================
  1926. idMultiplayerGame::WriteToSnapshot
  1927. ================
  1928. */
  1929. void idMultiplayerGame::WriteToSnapshot( idBitMsgDelta &msg ) const {
  1930. int i;
  1931. int value;
  1932. msg.WriteByte( gameState );
  1933. msg.WriteShort( currentTourneyPlayer[ 0 ] );
  1934. msg.WriteShort( currentTourneyPlayer[ 1 ] );
  1935. for ( i = 0; i < MAX_CLIENTS; i++ ) {
  1936. // clamp all values to min/max possible value that we can send over
  1937. value = idMath::ClampInt( MP_PLAYER_MINFRAGS, MP_PLAYER_MAXFRAGS, playerState[i].fragCount );
  1938. msg.WriteBits( value, ASYNC_PLAYER_FRAG_BITS );
  1939. value = idMath::ClampInt( MP_PLAYER_MINFRAGS, MP_PLAYER_MAXFRAGS, playerState[i].teamFragCount );
  1940. msg.WriteBits( value, ASYNC_PLAYER_FRAG_BITS );
  1941. value = idMath::ClampInt( 0, MP_PLAYER_MAXWINS, playerState[i].wins );
  1942. msg.WriteBits( value, ASYNC_PLAYER_WINS_BITS );
  1943. value = idMath::ClampInt( 0, MP_PLAYER_MAXPING, playerState[i].ping );
  1944. msg.WriteBits( value, ASYNC_PLAYER_PING_BITS );
  1945. msg.WriteBits( playerState[i].ingame, 1 );
  1946. }
  1947. }
  1948. /*
  1949. ================
  1950. idMultiplayerGame::ReadFromSnapshot
  1951. ================
  1952. */
  1953. void idMultiplayerGame::ReadFromSnapshot( const idBitMsgDelta &msg ) {
  1954. int i;
  1955. gameState_t newState;
  1956. newState = (idMultiplayerGame::gameState_t)msg.ReadByte();
  1957. if ( newState != gameState ) {
  1958. gameLocal.DPrintf( "%s -> %s\n", GameStateStrings[ gameState ], GameStateStrings[ newState ] );
  1959. gameState = newState;
  1960. // these could be gathered in a BGNewState() kind of thing, as we have to do them in NewState as well
  1961. if ( gameState == GAMEON ) {
  1962. matchStartedTime = gameLocal.time;
  1963. cvarSystem->SetCVarString( "ui_ready", "Not Ready" );
  1964. switchThrottle[ 1 ] = 0; // passby the throttle
  1965. startFragLimit = gameLocal.serverInfo.GetInt( "si_fragLimit" );
  1966. }
  1967. }
  1968. currentTourneyPlayer[ 0 ] = msg.ReadShort();
  1969. currentTourneyPlayer[ 1 ] = msg.ReadShort();
  1970. for ( i = 0; i < MAX_CLIENTS; i++ ) {
  1971. playerState[i].fragCount = msg.ReadBits( ASYNC_PLAYER_FRAG_BITS );
  1972. playerState[i].teamFragCount = msg.ReadBits( ASYNC_PLAYER_FRAG_BITS );
  1973. playerState[i].wins = msg.ReadBits( ASYNC_PLAYER_WINS_BITS );
  1974. playerState[i].ping = msg.ReadBits( ASYNC_PLAYER_PING_BITS );
  1975. playerState[i].ingame = msg.ReadBits( 1 ) != 0;
  1976. }
  1977. }
  1978. /*
  1979. ================
  1980. idMultiplayerGame::PlayGlobalSound
  1981. ================
  1982. */
  1983. void idMultiplayerGame::PlayGlobalSound( int to, snd_evt_t evt, const char *shader ) {
  1984. const idSoundShader *shaderDecl;
  1985. if ( to == -1 || to == gameLocal.localClientNum ) {
  1986. if ( shader ) {
  1987. gameSoundWorld->PlayShaderDirectly( shader );
  1988. } else {
  1989. gameSoundWorld->PlayShaderDirectly( GlobalSoundStrings[ evt ] );
  1990. }
  1991. }
  1992. if ( !gameLocal.isClient ) {
  1993. idBitMsg outMsg;
  1994. byte msgBuf[1024];
  1995. outMsg.Init( msgBuf, sizeof( msgBuf ) );
  1996. if ( shader ) {
  1997. shaderDecl = declManager->FindSound( shader );
  1998. if ( !shaderDecl ) {
  1999. return;
  2000. }
  2001. outMsg.WriteByte( GAME_RELIABLE_MESSAGE_SOUND_INDEX );
  2002. outMsg.WriteLong( gameLocal.ServerRemapDecl( to, DECL_SOUND, shaderDecl->Index() ) );
  2003. } else {
  2004. outMsg.WriteByte( GAME_RELIABLE_MESSAGE_SOUND_EVENT );
  2005. outMsg.WriteByte( evt );
  2006. }
  2007. networkSystem->ServerSendReliableMessage( to, outMsg );
  2008. }
  2009. }
  2010. /*
  2011. ================
  2012. idMultiplayerGame::PrintMessageEvent
  2013. ================
  2014. */
  2015. void idMultiplayerGame::PrintMessageEvent( int to, msg_evt_t evt, int parm1, int parm2 ) {
  2016. switch ( evt ) {
  2017. case MSG_SUICIDE:
  2018. assert( parm1 >= 0 );
  2019. AddChatLine( common->GetLanguageDict()->GetString( "#str_04293" ), gameLocal.userInfo[ parm1 ].GetString( "ui_name" ) );
  2020. break;
  2021. case MSG_KILLED:
  2022. assert( parm1 >= 0 && parm2 >= 0 );
  2023. AddChatLine( common->GetLanguageDict()->GetString( "#str_04292" ), gameLocal.userInfo[ parm1 ].GetString( "ui_name" ), gameLocal.userInfo[ parm2 ].GetString( "ui_name" ) );
  2024. break;
  2025. case MSG_KILLEDTEAM:
  2026. assert( parm1 >= 0 && parm2 >= 0 );
  2027. AddChatLine( common->GetLanguageDict()->GetString( "#str_04291" ), gameLocal.userInfo[ parm1 ].GetString( "ui_name" ), gameLocal.userInfo[ parm2 ].GetString( "ui_name" ) );
  2028. break;
  2029. case MSG_TELEFRAGGED:
  2030. assert( parm1 >= 0 && parm2 >= 0 );
  2031. AddChatLine( common->GetLanguageDict()->GetString( "#str_04290" ), gameLocal.userInfo[ parm1 ].GetString( "ui_name" ), gameLocal.userInfo[ parm2 ].GetString( "ui_name" ) );
  2032. break;
  2033. case MSG_DIED:
  2034. assert( parm1 >= 0 );
  2035. AddChatLine( common->GetLanguageDict()->GetString( "#str_04289" ), gameLocal.userInfo[ parm1 ].GetString( "ui_name" ) );
  2036. break;
  2037. case MSG_VOTE:
  2038. AddChatLine( common->GetLanguageDict()->GetString( "#str_04288" ) );
  2039. break;
  2040. case MSG_SUDDENDEATH:
  2041. AddChatLine( common->GetLanguageDict()->GetString( "#str_04287" ) );
  2042. break;
  2043. case MSG_FORCEREADY:
  2044. AddChatLine( common->GetLanguageDict()->GetString( "#str_04286" ), gameLocal.userInfo[ parm1 ].GetString( "ui_name" ) );
  2045. if ( gameLocal.entities[ parm1 ] && gameLocal.entities[ parm1 ]->IsType( idPlayer::Type ) ) {
  2046. static_cast< idPlayer * >( gameLocal.entities[ parm1 ] )->forcedReady = true;
  2047. }
  2048. break;
  2049. case MSG_JOINEDSPEC:
  2050. AddChatLine( common->GetLanguageDict()->GetString( "#str_04285" ), gameLocal.userInfo[ parm1 ].GetString( "ui_name" ) );
  2051. break;
  2052. case MSG_TIMELIMIT:
  2053. AddChatLine( common->GetLanguageDict()->GetString( "#str_04284" ) );
  2054. break;
  2055. case MSG_FRAGLIMIT:
  2056. if ( gameLocal.gameType == GAME_LASTMAN ) {
  2057. AddChatLine( common->GetLanguageDict()->GetString( "#str_04283" ), gameLocal.userInfo[ parm1 ].GetString( "ui_name" ) );
  2058. } else if ( gameLocal.gameType == GAME_TDM ) {
  2059. AddChatLine( common->GetLanguageDict()->GetString( "#str_04282" ), gameLocal.userInfo[ parm1 ].GetString( "ui_team" ) );
  2060. } else {
  2061. AddChatLine( common->GetLanguageDict()->GetString( "#str_04281" ), gameLocal.userInfo[ parm1 ].GetString( "ui_name" ) );
  2062. }
  2063. break;
  2064. case MSG_JOINTEAM:
  2065. AddChatLine( common->GetLanguageDict()->GetString( "#str_04280" ), gameLocal.userInfo[ parm1 ].GetString( "ui_name" ), parm2 ? common->GetLanguageDict()->GetString( "#str_02500" ) : common->GetLanguageDict()->GetString( "#str_02499" ) );
  2066. break;
  2067. case MSG_HOLYSHIT:
  2068. AddChatLine( common->GetLanguageDict()->GetString( "#str_06732" ) );
  2069. break;
  2070. default:
  2071. gameLocal.DPrintf( "PrintMessageEvent: unknown message type %d\n", evt );
  2072. return;
  2073. }
  2074. if ( !gameLocal.isClient ) {
  2075. idBitMsg outMsg;
  2076. byte msgBuf[1024];
  2077. outMsg.Init( msgBuf, sizeof( msgBuf ) );
  2078. outMsg.WriteByte( GAME_RELIABLE_MESSAGE_DB );
  2079. outMsg.WriteByte( evt );
  2080. outMsg.WriteByte( parm1 );
  2081. outMsg.WriteByte( parm2 );
  2082. networkSystem->ServerSendReliableMessage( to, outMsg );
  2083. }
  2084. }
  2085. /*
  2086. ================
  2087. idMultiplayerGame::SuddenRespawns
  2088. solely for LMN if an end game ( fragLimitTimeout ) was entered and aborted before expiration
  2089. LMN players which still have lives left need to be respawned without being marked lastManOver
  2090. ================
  2091. */
  2092. void idMultiplayerGame::SuddenRespawn( void ) {
  2093. int i;
  2094. if ( gameLocal.gameType != GAME_LASTMAN ) {
  2095. return;
  2096. }
  2097. for ( i = 0; i < gameLocal.numClients; i++ ) {
  2098. if ( !gameLocal.entities[ i ] || !gameLocal.entities[ i ]->IsType( idPlayer::Type ) ) {
  2099. continue;
  2100. }
  2101. if ( !CanPlay( static_cast< idPlayer * >( gameLocal.entities[ i ] ) ) ) {
  2102. continue;
  2103. }
  2104. if ( static_cast< idPlayer * >( gameLocal.entities[ i ] )->lastManOver ) {
  2105. continue;
  2106. }
  2107. static_cast< idPlayer * >( gameLocal.entities[ i ] )->lastManPlayAgain = true;
  2108. }
  2109. }
  2110. /*
  2111. ================
  2112. idMultiplayerGame::CheckSpawns
  2113. ================
  2114. */
  2115. void idMultiplayerGame::CheckRespawns( idPlayer *spectator ) {
  2116. for( int i = 0 ; i < gameLocal.numClients ; i++ ) {
  2117. idEntity *ent = gameLocal.entities[ i ];
  2118. if ( !ent || !ent->IsType( idPlayer::Type ) ) {
  2119. continue;
  2120. }
  2121. idPlayer *p = static_cast<idPlayer *>(ent);
  2122. // once we hit sudden death, nobody respawns till game has ended
  2123. if ( WantRespawn( p ) || p == spectator ) {
  2124. if ( gameState == SUDDENDEATH && gameLocal.gameType != GAME_LASTMAN ) {
  2125. // respawn rules while sudden death are different
  2126. // sudden death may trigger while a player is dead, so there are still cases where we need to respawn
  2127. // don't do any respawns while we are in end game delay though
  2128. if ( !fragLimitTimeout ) {
  2129. if ( gameLocal.gameType == GAME_TDM || p->IsLeader() ) {
  2130. #ifdef _DEBUG
  2131. if ( gameLocal.gameType == GAME_TOURNEY ) {
  2132. assert( p->entityNumber == currentTourneyPlayer[ 0 ] || p->entityNumber == currentTourneyPlayer[ 1 ] );
  2133. }
  2134. #endif
  2135. p->ServerSpectate( false );
  2136. } else if ( !p->IsLeader() ) {
  2137. // sudden death is rolling, this player is not a leader, have him spectate
  2138. p->ServerSpectate( true );
  2139. CheckAbortGame();
  2140. }
  2141. }
  2142. } else {
  2143. if ( gameLocal.gameType == GAME_DM ||
  2144. gameLocal.gameType == GAME_TDM ) {
  2145. if ( gameState == WARMUP || gameState == COUNTDOWN || gameState == GAMEON ) {
  2146. p->ServerSpectate( false );
  2147. }
  2148. } else if ( gameLocal.gameType == GAME_TOURNEY ) {
  2149. if ( i == currentTourneyPlayer[ 0 ] || i == currentTourneyPlayer[ 1 ] ) {
  2150. if ( gameState == WARMUP || gameState == COUNTDOWN || gameState == GAMEON ) {
  2151. p->ServerSpectate( false );
  2152. }
  2153. } else if ( gameState == WARMUP ) {
  2154. // make sure empty tourney slots get filled first
  2155. FillTourneySlots( );
  2156. if ( i == currentTourneyPlayer[ 0 ] || i == currentTourneyPlayer[ 1 ] ) {
  2157. p->ServerSpectate( false );
  2158. }
  2159. }
  2160. } else if ( gameLocal.gameType == GAME_LASTMAN ) {
  2161. if ( gameState == WARMUP || gameState == COUNTDOWN ) {
  2162. p->ServerSpectate( false );
  2163. } else if ( gameState == GAMEON || gameState == SUDDENDEATH ) {
  2164. if ( gameState == GAMEON && playerState[ i ].fragCount > 0 && p->lastManPresent ) {
  2165. assert( !p->lastManOver );
  2166. p->ServerSpectate( false );
  2167. } else if ( p->lastManPlayAgain && p->lastManPresent ) {
  2168. assert( gameState == SUDDENDEATH );
  2169. p->ServerSpectate( false );
  2170. } else {
  2171. // if a fragLimitTimeout was engaged, do NOT mark lastManOver as that could mean
  2172. // everyone ends up spectator and game is stalled with no end
  2173. // if the frag limit delay is engaged and cancels out before expiring, LMN players are
  2174. // respawned to play the tie again ( through SuddenRespawn and lastManPlayAgain )
  2175. if ( !fragLimitTimeout && !p->lastManOver ) {
  2176. common->DPrintf( "client %d has lost all last man lives\n", i );
  2177. // end of the game for this guy, send him to spectators
  2178. p->lastManOver = true;
  2179. // clients don't have access to lastManOver
  2180. // so set the fragCount to something silly ( used in scoreboard and player ranking )
  2181. playerState[ i ].fragCount = LASTMAN_NOLIVES;
  2182. p->ServerSpectate( true );
  2183. //Check for a situation where the last two player dies at the same time and don't
  2184. //try to respawn manually...This was causing all players to go into spectate mode
  2185. //and the server got stuck
  2186. {
  2187. int j;
  2188. for ( j = 0; j < gameLocal.numClients; j++ ) {
  2189. if ( !gameLocal.entities[ j ] ) {
  2190. continue;
  2191. }
  2192. if ( !CanPlay( static_cast< idPlayer * >( gameLocal.entities[ j ] ) ) ) {
  2193. continue;
  2194. }
  2195. if ( !static_cast< idPlayer * >( gameLocal.entities[ j ] )->lastManOver ) {
  2196. break;
  2197. }
  2198. }
  2199. if( j == gameLocal.numClients) {
  2200. //Everyone is dead so don't allow this player to spectate
  2201. //so the match will end
  2202. p->ServerSpectate( false );
  2203. }
  2204. }
  2205. }
  2206. }
  2207. }
  2208. }
  2209. }
  2210. } else if ( p->wantSpectate && !p->spectating ) {
  2211. playerState[ i ].fragCount = 0; // whenever you willingly go spectate during game, your score resets
  2212. p->ServerSpectate( true );
  2213. UpdateTourneyLine();
  2214. CheckAbortGame();
  2215. }
  2216. }
  2217. }
  2218. /*
  2219. ================
  2220. idMultiplayerGame::ForceReady
  2221. ================
  2222. */
  2223. void idMultiplayerGame::ForceReady( ) {
  2224. for( int i = 0 ; i < gameLocal.numClients ; i++ ) {
  2225. idEntity *ent = gameLocal.entities[ i ];
  2226. if ( !ent || !ent->IsType( idPlayer::Type ) ) {
  2227. continue;
  2228. }
  2229. idPlayer *p = static_cast<idPlayer *>( ent );
  2230. if ( !p->IsReady() ) {
  2231. PrintMessageEvent( -1, MSG_FORCEREADY, i );
  2232. p->forcedReady = true;
  2233. }
  2234. }
  2235. }
  2236. /*
  2237. ================
  2238. idMultiplayerGame::ForceReady_f
  2239. ================
  2240. */
  2241. void idMultiplayerGame::ForceReady_f( const idCmdArgs &args ) {
  2242. if ( !gameLocal.isMultiplayer || gameLocal.isClient ) {
  2243. common->Printf( "forceReady: multiplayer server only\n" );
  2244. return;
  2245. }
  2246. gameLocal.mpGame.ForceReady();
  2247. }
  2248. /*
  2249. ================
  2250. idMultiplayerGame::DropWeapon
  2251. ================
  2252. */
  2253. void idMultiplayerGame::DropWeapon( int clientNum ) {
  2254. assert( !gameLocal.isClient );
  2255. idEntity *ent = gameLocal.entities[ clientNum ];
  2256. if ( !ent || !ent->IsType( idPlayer::Type ) ) {
  2257. return;
  2258. }
  2259. static_cast< idPlayer* >( ent )->DropWeapon( false );
  2260. }
  2261. /*
  2262. ================
  2263. idMultiplayerGame::DropWeapon_f
  2264. ================
  2265. */
  2266. void idMultiplayerGame::DropWeapon_f( const idCmdArgs &args ) {
  2267. if ( !gameLocal.isMultiplayer ) {
  2268. common->Printf( "clientDropWeapon: only valid in multiplayer\n" );
  2269. return;
  2270. }
  2271. idBitMsg outMsg;
  2272. byte msgBuf[128];
  2273. outMsg.Init( msgBuf, sizeof( msgBuf ) );
  2274. outMsg.WriteByte( GAME_RELIABLE_MESSAGE_DROPWEAPON );
  2275. networkSystem->ClientSendReliableMessage( outMsg );
  2276. }
  2277. /*
  2278. ================
  2279. idMultiplayerGame::MessageMode_f
  2280. ================
  2281. */
  2282. void idMultiplayerGame::MessageMode_f( const idCmdArgs &args ) {
  2283. gameLocal.mpGame.MessageMode( args );
  2284. }
  2285. /*
  2286. ================
  2287. idMultiplayerGame::MessageMode
  2288. ================
  2289. */
  2290. void idMultiplayerGame::MessageMode( const idCmdArgs &args ) {
  2291. const char *mode;
  2292. int imode;
  2293. if ( !gameLocal.isMultiplayer ) {
  2294. common->Printf( "clientMessageMode: only valid in multiplayer\n" );
  2295. return;
  2296. }
  2297. if ( !mainGui ) {
  2298. common->Printf( "no local client\n" );
  2299. return;
  2300. }
  2301. mode = args.Argv( 1 );
  2302. if ( !mode[ 0 ] ) {
  2303. imode = 0;
  2304. } else {
  2305. imode = atoi( mode );
  2306. }
  2307. msgmodeGui->SetStateString( "messagemode", imode ? "1" : "0" );
  2308. msgmodeGui->SetStateString( "chattext", "" );
  2309. nextMenu = 2;
  2310. // let the session know that we want our ingame main menu opened
  2311. gameLocal.sessionCommand = "game_startmenu";
  2312. }
  2313. /*
  2314. ================
  2315. idMultiplayerGame::Vote_f
  2316. FIXME: voting from console
  2317. ================
  2318. */
  2319. void idMultiplayerGame::Vote_f( const idCmdArgs &args ) { }
  2320. /*
  2321. ================
  2322. idMultiplayerGame::CallVote_f
  2323. FIXME: voting from console
  2324. ================
  2325. */
  2326. void idMultiplayerGame::CallVote_f( const idCmdArgs &args ) { }
  2327. /*
  2328. ================
  2329. idMultiplayerGame::ServerStartVote
  2330. ================
  2331. */
  2332. void idMultiplayerGame::ServerStartVote( int clientNum, vote_flags_t voteIndex, const char *value ) {
  2333. int i;
  2334. assert( vote == VOTE_NONE );
  2335. // setup
  2336. yesVotes = 1;
  2337. noVotes = 0;
  2338. vote = voteIndex;
  2339. voteValue = value;
  2340. voteTimeOut = gameLocal.time + 20000;
  2341. // mark players allowed to vote - only current ingame players, players joining during vote will be ignored
  2342. for ( i = 0; i < gameLocal.numClients; i++ ) {
  2343. if ( gameLocal.entities[ i ] && gameLocal.entities[ i ]->IsType( idPlayer::Type ) ) {
  2344. playerState[ i ].vote = ( i == clientNum ) ? PLAYER_VOTE_YES : PLAYER_VOTE_WAIT;
  2345. } else {
  2346. playerState[i].vote = PLAYER_VOTE_NONE;
  2347. }
  2348. }
  2349. }
  2350. /*
  2351. ================
  2352. idMultiplayerGame::ClientStartVote
  2353. ================
  2354. */
  2355. void idMultiplayerGame::ClientStartVote( int clientNum, const char *_voteString ) {
  2356. idBitMsg outMsg;
  2357. byte msgBuf[ MAX_GAME_MESSAGE_SIZE ];
  2358. if ( !gameLocal.isClient ) {
  2359. outMsg.Init( msgBuf, sizeof( msgBuf ) );
  2360. outMsg.WriteByte( GAME_RELIABLE_MESSAGE_STARTVOTE );
  2361. outMsg.WriteByte( clientNum );
  2362. outMsg.WriteString( _voteString );
  2363. networkSystem->ServerSendReliableMessage( -1, outMsg );
  2364. }
  2365. voteString = _voteString;
  2366. AddChatLine( va( common->GetLanguageDict()->GetString( "#str_04279" ), gameLocal.userInfo[ clientNum ].GetString( "ui_name" ) ) );
  2367. gameSoundWorld->PlayShaderDirectly( GlobalSoundStrings[ SND_VOTE ] );
  2368. if ( clientNum == gameLocal.localClientNum ) {
  2369. voted = true;
  2370. } else {
  2371. voted = false;
  2372. }
  2373. if ( gameLocal.isClient ) {
  2374. // the the vote value to something so the vote line is displayed
  2375. vote = VOTE_RESTART;
  2376. yesVotes = 1;
  2377. noVotes = 0;
  2378. }
  2379. }
  2380. /*
  2381. ================
  2382. idMultiplayerGame::ClientUpdateVote
  2383. ================
  2384. */
  2385. void idMultiplayerGame::ClientUpdateVote( vote_result_t status, int yesCount, int noCount ) {
  2386. idBitMsg outMsg;
  2387. byte msgBuf[ MAX_GAME_MESSAGE_SIZE ];
  2388. if ( !gameLocal.isClient ) {
  2389. outMsg.Init( msgBuf, sizeof( msgBuf ) );
  2390. outMsg.WriteByte( GAME_RELIABLE_MESSAGE_UPDATEVOTE );
  2391. outMsg.WriteByte( status );
  2392. outMsg.WriteByte( yesCount );
  2393. outMsg.WriteByte( noCount );
  2394. networkSystem->ServerSendReliableMessage( -1, outMsg );
  2395. }
  2396. if ( vote == VOTE_NONE ) {
  2397. // clients coming in late don't get the vote start and are not allowed to vote
  2398. return;
  2399. }
  2400. switch ( status ) {
  2401. case VOTE_FAILED:
  2402. AddChatLine( common->GetLanguageDict()->GetString( "#str_04278" ) );
  2403. gameSoundWorld->PlayShaderDirectly( GlobalSoundStrings[ SND_VOTE_FAILED ] );
  2404. if ( gameLocal.isClient ) {
  2405. vote = VOTE_NONE;
  2406. }
  2407. break;
  2408. case VOTE_PASSED:
  2409. AddChatLine( common->GetLanguageDict()->GetString( "#str_04277" ) );
  2410. gameSoundWorld->PlayShaderDirectly( GlobalSoundStrings[ SND_VOTE_PASSED ] );
  2411. break;
  2412. case VOTE_RESET:
  2413. if ( gameLocal.isClient ) {
  2414. vote = VOTE_NONE;
  2415. }
  2416. break;
  2417. case VOTE_ABORTED:
  2418. AddChatLine( common->GetLanguageDict()->GetString( "#str_04276" ) );
  2419. if ( gameLocal.isClient ) {
  2420. vote = VOTE_NONE;
  2421. }
  2422. break;
  2423. default:
  2424. break;
  2425. }
  2426. if ( gameLocal.isClient ) {
  2427. yesVotes = yesCount;
  2428. noVotes = noCount;
  2429. }
  2430. }
  2431. /*
  2432. ================
  2433. idMultiplayerGame::ClientCallVote
  2434. ================
  2435. */
  2436. void idMultiplayerGame::ClientCallVote( vote_flags_t voteIndex, const char *voteValue ) {
  2437. idBitMsg outMsg;
  2438. byte msgBuf[ MAX_GAME_MESSAGE_SIZE ];
  2439. // send
  2440. outMsg.Init( msgBuf, sizeof( msgBuf ) );
  2441. outMsg.WriteByte( GAME_RELIABLE_MESSAGE_CALLVOTE );
  2442. outMsg.WriteByte( voteIndex );
  2443. outMsg.WriteString( voteValue );
  2444. networkSystem->ClientSendReliableMessage( outMsg );
  2445. }
  2446. /*
  2447. ================
  2448. idMultiplayerGame::CastVote
  2449. ================
  2450. */
  2451. void idMultiplayerGame::CastVote( int clientNum, bool castVote ) {
  2452. idBitMsg outMsg;
  2453. byte msgBuf[ 128 ];
  2454. if ( clientNum == gameLocal.localClientNum ) {
  2455. voted = true;
  2456. }
  2457. if ( gameLocal.isClient ) {
  2458. outMsg.Init( msgBuf, sizeof( msgBuf ) );
  2459. outMsg.WriteByte( GAME_RELIABLE_MESSAGE_CASTVOTE );
  2460. outMsg.WriteByte( castVote );
  2461. networkSystem->ClientSendReliableMessage( outMsg );
  2462. return;
  2463. }
  2464. // sanity
  2465. if ( vote == VOTE_NONE ) {
  2466. gameLocal.ServerSendChatMessage( clientNum, "server", common->GetLanguageDict()->GetString( "#str_04275" ) );
  2467. common->DPrintf( "client %d: cast vote while no vote in progress\n", clientNum );
  2468. return;
  2469. }
  2470. if ( playerState[ clientNum ].vote != PLAYER_VOTE_WAIT ) {
  2471. gameLocal.ServerSendChatMessage( clientNum, "server", common->GetLanguageDict()->GetString( "#str_04274" ) );
  2472. common->DPrintf( "client %d: cast vote - vote %d != PLAYER_VOTE_WAIT\n", clientNum, playerState[ clientNum ].vote );
  2473. return;
  2474. }
  2475. if ( castVote ) {
  2476. playerState[ clientNum ].vote = PLAYER_VOTE_YES;
  2477. yesVotes++;
  2478. } else {
  2479. playerState[ clientNum ].vote = PLAYER_VOTE_NO;
  2480. noVotes++;
  2481. }
  2482. ClientUpdateVote( VOTE_UPDATE, yesVotes, noVotes );
  2483. }
  2484. /*
  2485. ================
  2486. idMultiplayerGame::ServerCallVote
  2487. ================
  2488. */
  2489. void idMultiplayerGame::ServerCallVote( int clientNum, const idBitMsg &msg ) {
  2490. vote_flags_t voteIndex;
  2491. int vote_timeLimit, vote_fragLimit, vote_clientNum, vote_gameTypeIndex; //, vote_kickIndex;
  2492. char value[ MAX_STRING_CHARS ];
  2493. assert( clientNum != -1 );
  2494. assert( !gameLocal.isClient );
  2495. voteIndex = (vote_flags_t)msg.ReadByte( );
  2496. msg.ReadString( value, sizeof( value ) );
  2497. // sanity checks - setup the vote
  2498. if ( vote != VOTE_NONE ) {
  2499. gameLocal.ServerSendChatMessage( clientNum, "server", common->GetLanguageDict()->GetString( "#str_04273" ) );
  2500. common->DPrintf( "client %d: called vote while voting already in progress - ignored\n", clientNum );
  2501. return;
  2502. }
  2503. switch ( voteIndex ) {
  2504. case VOTE_RESTART:
  2505. ServerStartVote( clientNum, voteIndex, "" );
  2506. ClientStartVote( clientNum, common->GetLanguageDict()->GetString( "#str_04271" ) );
  2507. break;
  2508. case VOTE_NEXTMAP:
  2509. ServerStartVote( clientNum, voteIndex, "" );
  2510. ClientStartVote( clientNum, common->GetLanguageDict()->GetString( "#str_04272" ) );
  2511. break;
  2512. case VOTE_TIMELIMIT:
  2513. vote_timeLimit = strtol( value, NULL, 10 );
  2514. if ( vote_timeLimit == gameLocal.serverInfo.GetInt( "si_timeLimit" ) ) {
  2515. gameLocal.ServerSendChatMessage( clientNum, "server", common->GetLanguageDict()->GetString( "#str_04270" ) );
  2516. common->DPrintf( "client %d: already at the voted Time Limit\n", clientNum );
  2517. return;
  2518. }
  2519. if ( vote_timeLimit < si_timeLimit.GetMinValue() || vote_timeLimit > si_timeLimit.GetMaxValue() ) {
  2520. gameLocal.ServerSendChatMessage( clientNum, "server", common->GetLanguageDict()->GetString( "#str_04269" ) );
  2521. common->DPrintf( "client %d: timelimit value out of range for vote: %s\n", clientNum, value );
  2522. return;
  2523. }
  2524. ServerStartVote( clientNum, voteIndex, value );
  2525. ClientStartVote( clientNum, va( common->GetLanguageDict()->GetString( "#str_04268" ), vote_timeLimit ) );
  2526. break;
  2527. case VOTE_FRAGLIMIT:
  2528. vote_fragLimit = strtol( value, NULL, 10 );
  2529. if ( vote_fragLimit == gameLocal.serverInfo.GetInt( "si_fragLimit" ) ) {
  2530. gameLocal.ServerSendChatMessage( clientNum, "server", common->GetLanguageDict()->GetString( "#str_04267" ) );
  2531. common->DPrintf( "client %d: already at the voted Frag Limit\n", clientNum );
  2532. return;
  2533. }
  2534. if ( vote_fragLimit < si_fragLimit.GetMinValue() || vote_fragLimit > si_fragLimit.GetMaxValue() ) {
  2535. gameLocal.ServerSendChatMessage( clientNum, "server", common->GetLanguageDict()->GetString( "#str_04266" ) );
  2536. common->DPrintf( "client %d: fraglimit value out of range for vote: %s\n", clientNum, value );
  2537. return;
  2538. }
  2539. ServerStartVote( clientNum, voteIndex, value );
  2540. ClientStartVote( clientNum, va( common->GetLanguageDict()->GetString( "#str_04303" ), gameLocal.gameType == GAME_LASTMAN ? common->GetLanguageDict()->GetString( "#str_04264" ) : common->GetLanguageDict()->GetString( "#str_04265" ), vote_fragLimit ) );
  2541. break;
  2542. case VOTE_GAMETYPE:
  2543. vote_gameTypeIndex = strtol( value, NULL, 10 );
  2544. assert( vote_gameTypeIndex >= 0 && vote_gameTypeIndex <= 3 );
  2545. switch ( vote_gameTypeIndex ) {
  2546. case 0:
  2547. strcpy( value, "Deathmatch" );
  2548. break;
  2549. case 1:
  2550. strcpy( value, "Tourney" );
  2551. break;
  2552. case 2:
  2553. strcpy( value, "Team DM" );
  2554. break;
  2555. case 3:
  2556. strcpy( value, "Last Man" );
  2557. break;
  2558. }
  2559. if ( !idStr::Icmp( value, gameLocal.serverInfo.GetString( "si_gameType" ) ) ) {
  2560. gameLocal.ServerSendChatMessage( clientNum, "server", common->GetLanguageDict()->GetString( "#str_04259" ) );
  2561. common->DPrintf( "client %d: already at the voted Game Type\n", clientNum );
  2562. return;
  2563. }
  2564. ServerStartVote( clientNum, voteIndex, value );
  2565. ClientStartVote( clientNum, va( common->GetLanguageDict()->GetString( "#str_04258" ), value ) );
  2566. break;
  2567. case VOTE_KICK:
  2568. vote_clientNum = strtol( value, NULL, 10 );
  2569. if ( vote_clientNum == gameLocal.localClientNum ) {
  2570. gameLocal.ServerSendChatMessage( clientNum, "server", common->GetLanguageDict()->GetString( "#str_04257" ) );
  2571. common->DPrintf( "client %d: called kick for the server host\n", clientNum );
  2572. return;
  2573. }
  2574. ServerStartVote( clientNum, voteIndex, va( "%d", vote_clientNum ) );
  2575. ClientStartVote( clientNum, va( common->GetLanguageDict()->GetString( "#str_04302" ), vote_clientNum, gameLocal.userInfo[ vote_clientNum ].GetString( "ui_name" ) ) );
  2576. break;
  2577. case VOTE_MAP: {
  2578. if ( idStr::FindText( gameLocal.serverInfo.GetString( "si_map" ), value ) != -1 ) {
  2579. gameLocal.ServerSendChatMessage( clientNum, "server", va( common->GetLanguageDict()->GetString( "#str_04295" ), value ) );
  2580. common->DPrintf( "client %d: already running the voted map: %s\n", clientNum, value );
  2581. return;
  2582. }
  2583. int num = fileSystem->GetNumMaps();
  2584. int i;
  2585. const idDict *dict;
  2586. bool haveMap = false;
  2587. for ( i = 0; i < num; i++ ) {
  2588. dict = fileSystem->GetMapDecl( i );
  2589. if ( dict && !idStr::Icmp( dict->GetString( "path" ), value ) ) {
  2590. haveMap = true;
  2591. break;
  2592. }
  2593. }
  2594. if ( !haveMap ) {
  2595. gameLocal.ServerSendChatMessage( clientNum, "server", va( common->GetLanguageDict()->GetString( "#str_04296" ), value ) );
  2596. common->Printf( "client %d: map not found: %s\n", clientNum, value );
  2597. return;
  2598. }
  2599. ServerStartVote( clientNum, voteIndex, value );
  2600. ClientStartVote( clientNum, va( common->GetLanguageDict()->GetString( "#str_04256" ), common->GetLanguageDict()->GetString( dict ? dict->GetString( "name" ) : value ) ) );
  2601. break;
  2602. }
  2603. case VOTE_SPECTATORS:
  2604. if ( gameLocal.serverInfo.GetBool( "si_spectators" ) ) {
  2605. ServerStartVote( clientNum, voteIndex, "" );
  2606. ClientStartVote( clientNum, common->GetLanguageDict()->GetString( "#str_04255" ) );
  2607. } else {
  2608. ServerStartVote( clientNum, voteIndex, "" );
  2609. ClientStartVote( clientNum, common->GetLanguageDict()->GetString( "#str_04254" ) );
  2610. }
  2611. break;
  2612. default:
  2613. gameLocal.ServerSendChatMessage( clientNum, "server", va( common->GetLanguageDict()->GetString( "#str_04297" ), (int)voteIndex ) );
  2614. common->DPrintf( "client %d: unknown vote index %d\n", clientNum, voteIndex );
  2615. }
  2616. }
  2617. /*
  2618. ================
  2619. idMultiplayerGame::DisconnectClient
  2620. ================
  2621. */
  2622. void idMultiplayerGame::DisconnectClient( int clientNum ) {
  2623. if ( lastWinner == clientNum ) {
  2624. lastWinner = -1;
  2625. }
  2626. UpdatePlayerRanks();
  2627. CheckAbortGame();
  2628. }
  2629. /*
  2630. ================
  2631. idMultiplayerGame::CheckAbortGame
  2632. ================
  2633. */
  2634. void idMultiplayerGame::CheckAbortGame( void ) {
  2635. int i;
  2636. if ( gameLocal.gameType == GAME_TOURNEY && gameState == WARMUP ) {
  2637. // if a tourney player joined spectators, let someone else have his spot
  2638. for ( i = 0; i < 2; i++ ) {
  2639. if ( !gameLocal.entities[ currentTourneyPlayer[ i ] ] || static_cast< idPlayer * >( gameLocal.entities[ currentTourneyPlayer[ i ] ] )->spectating ) {
  2640. currentTourneyPlayer[ i ] = -1;
  2641. }
  2642. }
  2643. }
  2644. // only checks for aborts -> game review below
  2645. if ( gameState != COUNTDOWN && gameState != GAMEON && gameState != SUDDENDEATH ) {
  2646. return;
  2647. }
  2648. switch ( gameLocal.gameType ) {
  2649. case GAME_TOURNEY:
  2650. for ( i = 0; i < 2; i++ ) {
  2651. if ( !gameLocal.entities[ currentTourneyPlayer[ i ] ] || static_cast< idPlayer * >( gameLocal.entities[ currentTourneyPlayer[ i ] ] )->spectating ) {
  2652. NewState( GAMEREVIEW );
  2653. return;
  2654. }
  2655. }
  2656. break;
  2657. default:
  2658. if ( !EnoughClientsToPlay() ) {
  2659. NewState( GAMEREVIEW );
  2660. }
  2661. break;
  2662. }
  2663. }
  2664. /*
  2665. ================
  2666. idMultiplayerGame::WantKilled
  2667. ================
  2668. */
  2669. void idMultiplayerGame::WantKilled( int clientNum ) {
  2670. idEntity *ent = gameLocal.entities[ clientNum ];
  2671. if ( ent && ent->IsType( idPlayer::Type ) ) {
  2672. static_cast<idPlayer *>( ent )->Kill( false, false );
  2673. }
  2674. }
  2675. /*
  2676. ================
  2677. idMultiplayerGame::MapRestart
  2678. ================
  2679. */
  2680. void idMultiplayerGame::MapRestart( void ) {
  2681. int clientNum;
  2682. assert( !gameLocal.isClient );
  2683. if ( gameState != WARMUP ) {
  2684. NewState( WARMUP );
  2685. nextState = INACTIVE;
  2686. nextStateSwitch = 0;
  2687. }
  2688. if ( g_balanceTDM.GetBool() && lastGameType != GAME_TDM && gameLocal.gameType == GAME_TDM ) {
  2689. for ( clientNum = 0; clientNum < gameLocal.numClients; clientNum++ ) {
  2690. if ( gameLocal.entities[ clientNum ] && gameLocal.entities[ clientNum ]->IsType( idPlayer::Type ) ) {
  2691. if ( static_cast< idPlayer* >( gameLocal.entities[ clientNum ] )->BalanceTDM() ) {
  2692. // core is in charge of syncing down userinfo changes
  2693. // it will also call back game through SetUserInfo with the current info for update
  2694. cmdSystem->BufferCommandText( CMD_EXEC_NOW, va( "updateUI %d\n", clientNum ) );
  2695. }
  2696. }
  2697. }
  2698. }
  2699. lastGameType = gameLocal.gameType;
  2700. }
  2701. /*
  2702. ================
  2703. idMultiplayerGame::SwitchToTeam
  2704. ================
  2705. */
  2706. void idMultiplayerGame::SwitchToTeam( int clientNum, int oldteam, int newteam ) {
  2707. idEntity *ent;
  2708. int i;
  2709. assert( gameLocal.gameType == GAME_TDM );
  2710. assert( oldteam != newteam );
  2711. assert( !gameLocal.isClient );
  2712. if ( !gameLocal.isClient && newteam >= 0 && IsInGame( clientNum ) ) {
  2713. PrintMessageEvent( -1, MSG_JOINTEAM, clientNum, newteam );
  2714. }
  2715. // assign the right teamFragCount
  2716. for( i = 0; i < gameLocal.numClients; i++ ) {
  2717. if ( i == clientNum ) {
  2718. continue;
  2719. }
  2720. ent = gameLocal.entities[ i ];
  2721. if ( ent && ent->IsType( idPlayer::Type ) && static_cast< idPlayer * >(ent)->team == newteam ) {
  2722. playerState[ clientNum ].teamFragCount = playerState[ i ].teamFragCount;
  2723. break;
  2724. }
  2725. }
  2726. if ( i == gameLocal.numClients ) {
  2727. // alone on this team
  2728. playerState[ clientNum ].teamFragCount = 0;
  2729. }
  2730. if ( gameState == GAMEON && oldteam != -1 ) {
  2731. // when changing teams during game, kill and respawn
  2732. idPlayer *p = static_cast<idPlayer *>( gameLocal.entities[ clientNum ] );
  2733. if ( p->IsInTeleport() ) {
  2734. p->ServerSendEvent( idPlayer::EVENT_ABORT_TELEPORTER, NULL, false, -1 );
  2735. p->SetPrivateCameraView( NULL );
  2736. }
  2737. p->Kill( true, true );
  2738. CheckAbortGame();
  2739. }
  2740. }
  2741. /*
  2742. ================
  2743. idMultiplayerGame::ProcessChatMessage
  2744. ================
  2745. */
  2746. void idMultiplayerGame::ProcessChatMessage( int clientNum, bool team, const char *name, const char *text, const char *sound ) {
  2747. idBitMsg outMsg;
  2748. byte msgBuf[ 256 ];
  2749. const char *prefix = NULL;
  2750. int send_to; // 0 - all, 1 - specs, 2 - team
  2751. int i;
  2752. idEntity *ent;
  2753. idPlayer *p;
  2754. idStr prefixed_name;
  2755. assert( !gameLocal.isClient );
  2756. if ( clientNum >= 0 ) {
  2757. p = static_cast< idPlayer * >( gameLocal.entities[ clientNum ] );
  2758. if ( !( p && p->IsType( idPlayer::Type ) ) ) {
  2759. return;
  2760. }
  2761. if ( p->spectating ) {
  2762. prefix = "spectating";
  2763. if ( team || ( !g_spectatorChat.GetBool() && ( gameState == GAMEON || gameState == SUDDENDEATH ) ) ) {
  2764. // to specs
  2765. send_to = 1;
  2766. } else {
  2767. // to all
  2768. send_to = 0;
  2769. }
  2770. } else if ( team ) {
  2771. prefix = "team";
  2772. // to team
  2773. send_to = 2;
  2774. } else {
  2775. // to all
  2776. send_to = 0;
  2777. }
  2778. } else {
  2779. p = NULL;
  2780. send_to = 0;
  2781. }
  2782. // put the message together
  2783. outMsg.Init( msgBuf, sizeof( msgBuf ) );
  2784. outMsg.WriteByte( GAME_RELIABLE_MESSAGE_CHAT );
  2785. if ( prefix ) {
  2786. prefixed_name = va( "(%s) %s", prefix, name );
  2787. } else {
  2788. prefixed_name = name;
  2789. }
  2790. outMsg.WriteString( prefixed_name );
  2791. outMsg.WriteString( text, -1, false );
  2792. if ( !send_to ) {
  2793. AddChatLine( "%s^0: %s\n", prefixed_name.c_str(), text );
  2794. networkSystem->ServerSendReliableMessage( -1, outMsg );
  2795. if ( sound ) {
  2796. PlayGlobalSound( -1, SND_COUNT, sound );
  2797. }
  2798. } else {
  2799. for ( i = 0; i < gameLocal.numClients; i++ ) {
  2800. ent = gameLocal.entities[ i ];
  2801. if ( !ent || !ent->IsType( idPlayer::Type ) ) {
  2802. continue;
  2803. }
  2804. if ( send_to == 1 && static_cast< idPlayer * >( ent )->spectating ) {
  2805. if ( sound ) {
  2806. PlayGlobalSound( i, SND_COUNT, sound );
  2807. }
  2808. if ( i == gameLocal.localClientNum ) {
  2809. AddChatLine( "%s^0: %s\n", prefixed_name.c_str(), text );
  2810. } else {
  2811. networkSystem->ServerSendReliableMessage( i, outMsg );
  2812. }
  2813. } else if ( send_to == 2 && static_cast< idPlayer * >( ent )->team == p->team ) {
  2814. if ( sound ) {
  2815. PlayGlobalSound( i, SND_COUNT, sound );
  2816. }
  2817. if ( i == gameLocal.localClientNum ) {
  2818. AddChatLine( "%s^0: %s\n", prefixed_name.c_str(), text );
  2819. } else {
  2820. networkSystem->ServerSendReliableMessage( i, outMsg );
  2821. }
  2822. }
  2823. }
  2824. }
  2825. }
  2826. /*
  2827. ================
  2828. idMultiplayerGame::Precache
  2829. ================
  2830. */
  2831. void idMultiplayerGame::Precache( void ) {
  2832. int i;
  2833. idFile *f;
  2834. if ( !gameLocal.isMultiplayer ) {
  2835. return;
  2836. }
  2837. gameLocal.FindEntityDefDict( "player_doommarine", false );;
  2838. // skins
  2839. idStr str = cvarSystem->GetCVarString( "mod_validSkins" );
  2840. idStr skin;
  2841. while ( str.Length() ) {
  2842. int n = str.Find( ";" );
  2843. if ( n >= 0 ) {
  2844. skin = str.Left( n );
  2845. str = str.Right( str.Length() - n - 1 );
  2846. } else {
  2847. skin = str;
  2848. str = "";
  2849. }
  2850. declManager->FindSkin( skin, false );
  2851. }
  2852. for ( i = 0; ui_skinArgs[ i ]; i++ ) {
  2853. declManager->FindSkin( ui_skinArgs[ i ], false );
  2854. }
  2855. // MP game sounds
  2856. for ( i = 0; i < SND_COUNT; i++ ) {
  2857. f = fileSystem->OpenFileRead( GlobalSoundStrings[ i ] );
  2858. fileSystem->CloseFile( f );
  2859. }
  2860. // MP guis. just make sure we hit all of them
  2861. i = 0;
  2862. while ( MPGuis[ i ] ) {
  2863. uiManager->FindGui( MPGuis[ i ], true );
  2864. i++;
  2865. }
  2866. }
  2867. /*
  2868. ================
  2869. idMultiplayerGame::ToggleSpectate
  2870. ================
  2871. */
  2872. void idMultiplayerGame::ToggleSpectate( void ) {
  2873. bool spectating;
  2874. assert( gameLocal.isClient || gameLocal.localClientNum == 0 );
  2875. spectating = ( idStr::Icmp( cvarSystem->GetCVarString( "ui_spectate" ), "Spectate" ) == 0 );
  2876. if ( spectating ) {
  2877. // always allow toggling to play
  2878. cvarSystem->SetCVarString( "ui_spectate", "Play" );
  2879. } else {
  2880. // only allow toggling to spectate if spectators are enabled.
  2881. if ( gameLocal.serverInfo.GetBool( "si_spectators" ) ) {
  2882. cvarSystem->SetCVarString( "ui_spectate", "Spectate" );
  2883. } else {
  2884. gameLocal.mpGame.AddChatLine( common->GetLanguageDict()->GetString( "#str_06747" ) );
  2885. }
  2886. }
  2887. }
  2888. /*
  2889. ================
  2890. idMultiplayerGame::ToggleReady
  2891. ================
  2892. */
  2893. void idMultiplayerGame::ToggleReady( void ) {
  2894. bool ready;
  2895. assert( gameLocal.isClient || gameLocal.localClientNum == 0 );
  2896. ready = ( idStr::Icmp( cvarSystem->GetCVarString( "ui_ready" ), "Ready" ) == 0 );
  2897. if ( ready ) {
  2898. cvarSystem->SetCVarString( "ui_ready", "Not Ready" );
  2899. } else {
  2900. cvarSystem->SetCVarString( "ui_ready", "Ready" );
  2901. }
  2902. }
  2903. /*
  2904. ================
  2905. idMultiplayerGame::ToggleTeam
  2906. ================
  2907. */
  2908. void idMultiplayerGame::ToggleTeam( void ) {
  2909. bool team;
  2910. assert( gameLocal.isClient || gameLocal.localClientNum == 0 );
  2911. team = ( idStr::Icmp( cvarSystem->GetCVarString( "ui_team" ), "Red" ) == 0 );
  2912. if ( team ) {
  2913. cvarSystem->SetCVarString( "ui_team", "Blue" );
  2914. } else {
  2915. cvarSystem->SetCVarString( "ui_team", "Red" );
  2916. }
  2917. }
  2918. /*
  2919. ================
  2920. idMultiplayerGame::ToggleUserInfo
  2921. ================
  2922. */
  2923. void idMultiplayerGame::ThrottleUserInfo( void ) {
  2924. int i;
  2925. assert( gameLocal.localClientNum >= 0 );
  2926. i = 0;
  2927. while ( ThrottleVars[ i ] ) {
  2928. if ( idStr::Icmp( gameLocal.userInfo[ gameLocal.localClientNum ].GetString( ThrottleVars[ i ] ),
  2929. cvarSystem->GetCVarString( ThrottleVars[ i ] ) ) ) {
  2930. if ( gameLocal.realClientTime < switchThrottle[ i ] ) {
  2931. AddChatLine( common->GetLanguageDict()->GetString( "#str_04299" ), common->GetLanguageDict()->GetString( ThrottleVarsInEnglish[ i ] ), ( switchThrottle[ i ] - gameLocal.time ) / 1000 + 1 );
  2932. cvarSystem->SetCVarString( ThrottleVars[ i ], gameLocal.userInfo[ gameLocal.localClientNum ].GetString( ThrottleVars[ i ] ) );
  2933. } else {
  2934. switchThrottle[ i ] = gameLocal.time + ThrottleDelay[ i ] * 1000;
  2935. }
  2936. }
  2937. i++;
  2938. }
  2939. }
  2940. /*
  2941. ================
  2942. idMultiplayerGame::CanPlay
  2943. ================
  2944. */
  2945. bool idMultiplayerGame::CanPlay( idPlayer *p ) {
  2946. return !p->wantSpectate && playerState[ p->entityNumber ].ingame;
  2947. }
  2948. /*
  2949. ================
  2950. idMultiplayerGame::EnterGame
  2951. ================
  2952. */
  2953. void idMultiplayerGame::EnterGame( int clientNum ) {
  2954. assert( !gameLocal.isClient );
  2955. if ( !playerState[ clientNum ].ingame ) {
  2956. playerState[ clientNum ].ingame = true;
  2957. if ( gameLocal.isMultiplayer ) {
  2958. // can't use PrintMessageEvent as clients don't know the nickname yet
  2959. gameLocal.ServerSendChatMessage( -1, common->GetLanguageDict()->GetString( "#str_02047" ), va( common->GetLanguageDict()->GetString( "#str_07177" ), gameLocal.userInfo[ clientNum ].GetString( "ui_name" ) ) );
  2960. }
  2961. }
  2962. }
  2963. /*
  2964. ================
  2965. idMultiplayerGame::WantRespawn
  2966. ================
  2967. */
  2968. bool idMultiplayerGame::WantRespawn( idPlayer *p ) {
  2969. return p->forceRespawn && !p->wantSpectate && playerState[ p->entityNumber ].ingame;
  2970. }
  2971. /*
  2972. ================
  2973. idMultiplayerGame::VoiceChat
  2974. ================
  2975. */
  2976. void idMultiplayerGame::VoiceChat_f( const idCmdArgs &args ) {
  2977. gameLocal.mpGame.VoiceChat( args, false );
  2978. }
  2979. /*
  2980. ================
  2981. idMultiplayerGame::VoiceChatTeam
  2982. ================
  2983. */
  2984. void idMultiplayerGame::VoiceChatTeam_f( const idCmdArgs &args ) {
  2985. gameLocal.mpGame.VoiceChat( args, true );
  2986. }
  2987. /*
  2988. ================
  2989. idMultiplayerGame::VoiceChat
  2990. ================
  2991. */
  2992. void idMultiplayerGame::VoiceChat( const idCmdArgs &args, bool team ) {
  2993. idBitMsg outMsg;
  2994. byte msgBuf[128];
  2995. const char *voc;
  2996. const idDict *spawnArgs;
  2997. const idKeyValue *keyval;
  2998. int index;
  2999. if ( !gameLocal.isMultiplayer ) {
  3000. common->Printf( "clientVoiceChat: only valid in multiplayer\n" );
  3001. return;
  3002. }
  3003. if ( args.Argc() != 2 ) {
  3004. common->Printf( "clientVoiceChat: bad args\n" );
  3005. return;
  3006. }
  3007. // throttle
  3008. if ( gameLocal.realClientTime < voiceChatThrottle ) {
  3009. return;
  3010. }
  3011. voc = args.Argv( 1 );
  3012. spawnArgs = gameLocal.FindEntityDefDict( "player_doommarine", false );
  3013. keyval = spawnArgs->MatchPrefix( "snd_voc_", NULL );
  3014. index = 0;
  3015. while ( keyval ) {
  3016. if ( !keyval->GetValue().Icmp( voc ) ) {
  3017. break;
  3018. }
  3019. keyval = spawnArgs->MatchPrefix( "snd_voc_", keyval );
  3020. index++;
  3021. }
  3022. if ( !keyval ) {
  3023. common->Printf( "Voice command not found: %s\n", voc );
  3024. return;
  3025. }
  3026. voiceChatThrottle = gameLocal.realClientTime + 1000;
  3027. outMsg.Init( msgBuf, sizeof( msgBuf ) );
  3028. outMsg.WriteByte( GAME_RELIABLE_MESSAGE_VCHAT );
  3029. outMsg.WriteLong( index );
  3030. outMsg.WriteBits( team ? 1 : 0, 1 );
  3031. networkSystem->ClientSendReliableMessage( outMsg );
  3032. }
  3033. /*
  3034. ================
  3035. idMultiplayerGame::ProcessVoiceChat
  3036. ================
  3037. */
  3038. void idMultiplayerGame::ProcessVoiceChat( int clientNum, bool team, int index ) {
  3039. const idDict *spawnArgs;
  3040. const idKeyValue *keyval;
  3041. idStr name;
  3042. idStr snd_key;
  3043. idStr text_key;
  3044. idPlayer *p;
  3045. p = static_cast< idPlayer * >( gameLocal.entities[ clientNum ] );
  3046. if ( !( p && p->IsType( idPlayer::Type ) ) ) {
  3047. return;
  3048. }
  3049. if ( p->spectating ) {
  3050. return;
  3051. }
  3052. // lookup the sound def
  3053. spawnArgs = gameLocal.FindEntityDefDict( "player_doommarine", false );
  3054. keyval = spawnArgs->MatchPrefix( "snd_voc_", NULL );
  3055. while ( index > 0 && keyval ) {
  3056. keyval = spawnArgs->MatchPrefix( "snd_voc_", keyval );
  3057. index--;
  3058. }
  3059. if ( !keyval ) {
  3060. common->DPrintf( "ProcessVoiceChat: unknown chat index %d\n", index );
  3061. return;
  3062. }
  3063. snd_key = keyval->GetKey();
  3064. name = gameLocal.userInfo[ clientNum ].GetString( "ui_name" );
  3065. sprintf( text_key, "txt_%s", snd_key.Right( snd_key.Length() - 4 ).c_str() );
  3066. if ( team || gameState == COUNTDOWN || gameState == GAMEREVIEW ) {
  3067. ProcessChatMessage( clientNum, team, name, spawnArgs->GetString( text_key ), spawnArgs->GetString( snd_key ) );
  3068. } else {
  3069. p->StartSound( snd_key, SND_CHANNEL_ANY, 0, true, NULL );
  3070. ProcessChatMessage( clientNum, team, name, spawnArgs->GetString( text_key ), NULL );
  3071. }
  3072. }
  3073. /*
  3074. ================
  3075. idMultiplayerGame::ServerWriteInitialReliableMessages
  3076. ================
  3077. */
  3078. void idMultiplayerGame::ServerWriteInitialReliableMessages( int clientNum ) {
  3079. idBitMsg outMsg;
  3080. byte msgBuf[ MAX_GAME_MESSAGE_SIZE ];
  3081. int i;
  3082. idEntity *ent;
  3083. outMsg.Init( msgBuf, sizeof( msgBuf ) );
  3084. outMsg.BeginWriting();
  3085. outMsg.WriteByte( GAME_RELIABLE_MESSAGE_STARTSTATE );
  3086. // send the game state and start time
  3087. outMsg.WriteByte( gameState );
  3088. outMsg.WriteLong( matchStartedTime );
  3089. outMsg.WriteShort( startFragLimit );
  3090. // send the powerup states and the spectate states
  3091. for( i = 0; i < gameLocal.numClients; i++ ) {
  3092. ent = gameLocal.entities[ i ];
  3093. if ( i != clientNum && ent && ent->IsType( idPlayer::Type ) ) {
  3094. outMsg.WriteShort( i );
  3095. outMsg.WriteShort( static_cast< idPlayer * >( ent )->inventory.powerups );
  3096. outMsg.WriteBits( static_cast< idPlayer * >( ent )->spectating, 1 );
  3097. }
  3098. }
  3099. outMsg.WriteShort( MAX_CLIENTS );
  3100. networkSystem->ServerSendReliableMessage( clientNum, outMsg );
  3101. // we send SI in connectResponse messages, but it may have been modified already
  3102. outMsg.BeginWriting( );
  3103. outMsg.WriteByte( GAME_RELIABLE_MESSAGE_SERVERINFO );
  3104. outMsg.WriteDeltaDict( gameLocal.serverInfo, NULL );
  3105. networkSystem->ServerSendReliableMessage( clientNum, outMsg );
  3106. // warmup time
  3107. if ( gameState == COUNTDOWN ) {
  3108. outMsg.BeginWriting();
  3109. outMsg.WriteByte( GAME_RELIABLE_MESSAGE_WARMUPTIME );
  3110. outMsg.WriteLong( warmupEndTime );
  3111. networkSystem->ServerSendReliableMessage( clientNum, outMsg );
  3112. }
  3113. }
  3114. /*
  3115. ================
  3116. idMultiplayerGame::ClientReadStartState
  3117. ================
  3118. */
  3119. void idMultiplayerGame::ClientReadStartState( const idBitMsg &msg ) {
  3120. int i, client, powerup;
  3121. // read the state in preparation for reading snapshot updates
  3122. gameState = (idMultiplayerGame::gameState_t)msg.ReadByte();
  3123. matchStartedTime = msg.ReadLong( );
  3124. startFragLimit = msg.ReadShort( );
  3125. while ( ( client = msg.ReadShort() ) != MAX_CLIENTS ) {
  3126. assert( gameLocal.entities[ client ] && gameLocal.entities[ client ]->IsType( idPlayer::Type ) );
  3127. powerup = msg.ReadShort();
  3128. for ( i = 0; i < MAX_POWERUPS; i++ ) {
  3129. if ( powerup & ( 1 << i ) ) {
  3130. static_cast< idPlayer * >( gameLocal.entities[ client ] )->GivePowerUp( i, 0 );
  3131. }
  3132. }
  3133. bool spectate = ( msg.ReadBits( 1 ) != 0 );
  3134. static_cast< idPlayer * >( gameLocal.entities[ client ] )->Spectate( spectate );
  3135. }
  3136. }
  3137. /*
  3138. ================
  3139. idMultiplayerGame::ClientReadWarmupTime
  3140. ================
  3141. */
  3142. void idMultiplayerGame::ClientReadWarmupTime( const idBitMsg &msg ) {
  3143. warmupEndTime = msg.ReadLong();
  3144. }