MultiplayerGame.cpp 125 KB

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