Game_local.cpp 108 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369
  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. #ifdef GAME_DLL
  24. idSys * sys = NULL;
  25. idCommon * common = NULL;
  26. idCmdSystem * cmdSystem = NULL;
  27. idCVarSystem * cvarSystem = NULL;
  28. idFileSystem * fileSystem = NULL;
  29. idNetworkSystem * networkSystem = NULL;
  30. idRenderSystem * renderSystem = NULL;
  31. idSoundSystem * soundSystem = NULL;
  32. idRenderModelManager * renderModelManager = NULL;
  33. idUserInterfaceManager * uiManager = NULL;
  34. idDeclManager * declManager = NULL;
  35. idAASFileManager * AASFileManager = NULL;
  36. idCollisionModelManager * collisionModelManager = NULL;
  37. idCVar * idCVar::staticVars = NULL;
  38. idCVar com_forceGenericSIMD( "com_forceGenericSIMD", "0", CVAR_BOOL|CVAR_SYSTEM, "force generic platform independent SIMD" );
  39. #endif
  40. idRenderWorld * gameRenderWorld = NULL; // all drawing is done to this world
  41. idSoundWorld * gameSoundWorld = NULL; // all audio goes to this world
  42. static gameExport_t gameExport;
  43. // global animation lib
  44. idAnimManager animationLib;
  45. // the rest of the engine will only reference the "game" variable, while all local aspects stay hidden
  46. idGameLocal gameLocal;
  47. idGame * game = &gameLocal; // statically pointed at an idGameLocal
  48. const char *idGameLocal::sufaceTypeNames[ MAX_SURFACE_TYPES ] = {
  49. "none", "metal", "stone", "flesh", "wood", "cardboard", "liquid", "glass", "plastic",
  50. "ricochet", "surftype10", "surftype11", "surftype12", "surftype13", "surftype14", "surftype15"
  51. };
  52. /*
  53. ===========
  54. GetGameAPI
  55. ============
  56. */
  57. #if __MWERKS__
  58. #pragma export on
  59. #endif
  60. #if __GNUC__ >= 4
  61. #pragma GCC visibility push(default)
  62. #endif
  63. extern "C" gameExport_t *GetGameAPI( gameImport_t *import ) {
  64. #if __MWERKS__
  65. #pragma export off
  66. #endif
  67. if ( import->version == GAME_API_VERSION ) {
  68. // set interface pointers used by the game
  69. sys = import->sys;
  70. common = import->common;
  71. cmdSystem = import->cmdSystem;
  72. cvarSystem = import->cvarSystem;
  73. fileSystem = import->fileSystem;
  74. networkSystem = import->networkSystem;
  75. renderSystem = import->renderSystem;
  76. soundSystem = import->soundSystem;
  77. renderModelManager = import->renderModelManager;
  78. uiManager = import->uiManager;
  79. declManager = import->declManager;
  80. AASFileManager = import->AASFileManager;
  81. collisionModelManager = import->collisionModelManager;
  82. }
  83. // set interface pointers used by idLib
  84. idLib::sys = sys;
  85. idLib::common = common;
  86. idLib::cvarSystem = cvarSystem;
  87. idLib::fileSystem = fileSystem;
  88. // setup export interface
  89. gameExport.version = GAME_API_VERSION;
  90. gameExport.game = game;
  91. gameExport.gameEdit = gameEdit;
  92. return &gameExport;
  93. }
  94. #if __GNUC__ >= 4
  95. #pragma GCC visibility pop
  96. #endif
  97. /*
  98. ===========
  99. TestGameAPI
  100. ============
  101. */
  102. void TestGameAPI( void ) {
  103. gameImport_t testImport;
  104. gameExport_t testExport;
  105. testImport.sys = ::sys;
  106. testImport.common = ::common;
  107. testImport.cmdSystem = ::cmdSystem;
  108. testImport.cvarSystem = ::cvarSystem;
  109. testImport.fileSystem = ::fileSystem;
  110. testImport.networkSystem = ::networkSystem;
  111. testImport.renderSystem = ::renderSystem;
  112. testImport.soundSystem = ::soundSystem;
  113. testImport.renderModelManager = ::renderModelManager;
  114. testImport.uiManager = ::uiManager;
  115. testImport.declManager = ::declManager;
  116. testImport.AASFileManager = ::AASFileManager;
  117. testImport.collisionModelManager = ::collisionModelManager;
  118. testExport = *GetGameAPI( &testImport );
  119. }
  120. /*
  121. ===========
  122. idGameLocal::idGameLocal
  123. ============
  124. */
  125. idGameLocal::idGameLocal() {
  126. Clear();
  127. }
  128. /*
  129. ===========
  130. idGameLocal::Clear
  131. ============
  132. */
  133. void idGameLocal::Clear( void ) {
  134. int i;
  135. serverInfo.Clear();
  136. numClients = 0;
  137. for ( i = 0; i < MAX_CLIENTS; i++ ) {
  138. userInfo[i].Clear();
  139. persistentPlayerInfo[i].Clear();
  140. }
  141. memset( usercmds, 0, sizeof( usercmds ) );
  142. memset( entities, 0, sizeof( entities ) );
  143. memset( spawnIds, -1, sizeof( spawnIds ) );
  144. firstFreeIndex = 0;
  145. num_entities = 0;
  146. spawnedEntities.Clear();
  147. activeEntities.Clear();
  148. numEntitiesToDeactivate = 0;
  149. sortPushers = false;
  150. sortTeamMasters = false;
  151. persistentLevelInfo.Clear();
  152. memset( globalShaderParms, 0, sizeof( globalShaderParms ) );
  153. random.SetSeed( 0 );
  154. world = NULL;
  155. frameCommandThread = NULL;
  156. testmodel = NULL;
  157. testFx = NULL;
  158. clip.Shutdown();
  159. pvs.Shutdown();
  160. sessionCommand.Clear();
  161. locationEntities = NULL;
  162. smokeParticles = NULL;
  163. editEntities = NULL;
  164. entityHash.Clear( 1024, MAX_GENTITIES );
  165. inCinematic = false;
  166. cinematicSkipTime = 0;
  167. cinematicStopTime = 0;
  168. cinematicMaxSkipTime = 0;
  169. framenum = 0;
  170. previousTime = 0;
  171. time = 0;
  172. vacuumAreaNum = 0;
  173. mapFileName.Clear();
  174. mapFile = NULL;
  175. spawnCount = INITIAL_SPAWN_COUNT;
  176. mapSpawnCount = 0;
  177. camera = NULL;
  178. aasList.Clear();
  179. aasNames.Clear();
  180. lastAIAlertEntity = NULL;
  181. lastAIAlertTime = 0;
  182. spawnArgs.Clear();
  183. gravity.Set( 0, 0, -1 );
  184. playerPVS.h = (unsigned int)-1;
  185. playerConnectedAreas.h = (unsigned int)-1;
  186. gamestate = GAMESTATE_UNINITIALIZED;
  187. skipCinematic = false;
  188. influenceActive = false;
  189. localClientNum = 0;
  190. isMultiplayer = false;
  191. isServer = false;
  192. isClient = false;
  193. realClientTime = 0;
  194. isNewFrame = true;
  195. clientSmoothing = 0.1f;
  196. entityDefBits = 0;
  197. nextGibTime = 0;
  198. globalMaterial = NULL;
  199. newInfo.Clear();
  200. lastGUIEnt = NULL;
  201. lastGUI = 0;
  202. memset( clientEntityStates, 0, sizeof( clientEntityStates ) );
  203. memset( clientPVS, 0, sizeof( clientPVS ) );
  204. memset( clientSnapshots, 0, sizeof( clientSnapshots ) );
  205. eventQueue.Init();
  206. savedEventQueue.Init();
  207. memset( lagometer, 0, sizeof( lagometer ) );
  208. }
  209. /*
  210. ===========
  211. idGameLocal::Init
  212. initialize the game object, only happens once at startup, not each level load
  213. ============
  214. */
  215. void idGameLocal::Init( void ) {
  216. const idDict *dict;
  217. idAAS *aas;
  218. #ifndef GAME_DLL
  219. TestGameAPI();
  220. #else
  221. // initialize idLib
  222. idLib::Init();
  223. // register static cvars declared in the game
  224. idCVar::RegisterStaticVars();
  225. // initialize processor specific SIMD
  226. idSIMD::InitProcessor( "game", com_forceGenericSIMD.GetBool() );
  227. #endif
  228. Printf( "--------- Initializing Game ----------\n" );
  229. Printf( "gamename: %s\n", GAME_VERSION );
  230. Printf( "gamedate: %s\n", __DATE__ );
  231. // register game specific decl types
  232. declManager->RegisterDeclType( "model", DECL_MODELDEF, idDeclAllocator<idDeclModelDef> );
  233. declManager->RegisterDeclType( "export", DECL_MODELEXPORT, idDeclAllocator<idDecl> );
  234. // register game specific decl folders
  235. declManager->RegisterDeclFolder( "def", ".def", DECL_ENTITYDEF );
  236. declManager->RegisterDeclFolder( "fx", ".fx", DECL_FX );
  237. declManager->RegisterDeclFolder( "particles", ".prt", DECL_PARTICLE );
  238. declManager->RegisterDeclFolder( "af", ".af", DECL_AF );
  239. declManager->RegisterDeclFolder( "newpdas", ".pda", DECL_PDA );
  240. cmdSystem->AddCommand( "listModelDefs", idListDecls_f<DECL_MODELDEF>, CMD_FL_SYSTEM|CMD_FL_GAME, "lists model defs" );
  241. cmdSystem->AddCommand( "printModelDefs", idPrintDecls_f<DECL_MODELDEF>, CMD_FL_SYSTEM|CMD_FL_GAME, "prints a model def", idCmdSystem::ArgCompletion_Decl<DECL_MODELDEF> );
  242. Clear();
  243. idEvent::Init();
  244. idClass::Init();
  245. InitConsoleCommands();
  246. // load default scripts
  247. program.Startup( SCRIPT_DEFAULT );
  248. smokeParticles = new idSmokeParticles;
  249. // set up the aas
  250. dict = FindEntityDefDict( "aas_types" );
  251. if ( !dict ) {
  252. Error( "Unable to find entityDef for 'aas_types'" );
  253. }
  254. // allocate space for the aas
  255. const idKeyValue *kv = dict->MatchPrefix( "type" );
  256. while( kv != NULL ) {
  257. aas = idAAS::Alloc();
  258. aasList.Append( aas );
  259. aasNames.Append( kv->GetValue() );
  260. kv = dict->MatchPrefix( "type", kv );
  261. }
  262. gamestate = GAMESTATE_NOMAP;
  263. Printf( "...%d aas types\n", aasList.Num() );
  264. Printf( "game initialized.\n" );
  265. Printf( "--------------------------------------\n" );
  266. }
  267. /*
  268. ===========
  269. idGameLocal::Shutdown
  270. shut down the entire game
  271. ============
  272. */
  273. void idGameLocal::Shutdown( void ) {
  274. if ( !common ) {
  275. return;
  276. }
  277. Printf( "------------ Game Shutdown -----------\n" );
  278. mpGame.Shutdown();
  279. MapShutdown();
  280. aasList.DeleteContents( true );
  281. aasNames.Clear();
  282. idAI::FreeObstacleAvoidanceNodes();
  283. // shutdown the model exporter
  284. idModelExport::Shutdown();
  285. idEvent::Shutdown();
  286. delete[] locationEntities;
  287. locationEntities = NULL;
  288. delete smokeParticles;
  289. smokeParticles = NULL;
  290. idClass::Shutdown();
  291. // clear list with forces
  292. idForce::ClearForceList();
  293. // free the program data
  294. program.FreeData();
  295. // delete the .map file
  296. delete mapFile;
  297. mapFile = NULL;
  298. // free the collision map
  299. collisionModelManager->FreeMap();
  300. ShutdownConsoleCommands();
  301. // free memory allocated by class objects
  302. Clear();
  303. // shut down the animation manager
  304. animationLib.Shutdown();
  305. Printf( "--------------------------------------\n" );
  306. #ifdef GAME_DLL
  307. // remove auto-completion function pointers pointing into this DLL
  308. cvarSystem->RemoveFlaggedAutoCompletion( CVAR_GAME );
  309. // enable leak test
  310. Mem_EnableLeakTest( "game" );
  311. // shutdown idLib
  312. idLib::ShutDown();
  313. #endif
  314. }
  315. /*
  316. ===========
  317. idGameLocal::SaveGame
  318. save the current player state, level name, and level state
  319. the session may have written some data to the file already
  320. ============
  321. */
  322. void idGameLocal::SaveGame( idFile *f ) {
  323. int i;
  324. idEntity *ent;
  325. idEntity *link;
  326. idSaveGame savegame( f );
  327. if (g_flushSave.GetBool( ) == true ) {
  328. // force flushing with each write... for tracking down
  329. // save game bugs.
  330. f->ForceFlush();
  331. }
  332. savegame.WriteBuildNumber( BUILD_NUMBER );
  333. // go through all entities and threads and add them to the object list
  334. for( i = 0; i < MAX_GENTITIES; i++ ) {
  335. ent = entities[i];
  336. if ( ent ) {
  337. if ( ent->GetTeamMaster() && ent->GetTeamMaster() != ent ) {
  338. continue;
  339. }
  340. for ( link = ent; link != NULL; link = link->GetNextTeamEntity() ) {
  341. savegame.AddObject( link );
  342. }
  343. }
  344. }
  345. idList<idThread *> threads;
  346. threads = idThread::GetThreads();
  347. for( i = 0; i < threads.Num(); i++ ) {
  348. savegame.AddObject( threads[i] );
  349. }
  350. // write out complete object list
  351. savegame.WriteObjectList();
  352. program.Save( &savegame );
  353. savegame.WriteInt( g_skill.GetInteger() );
  354. savegame.WriteDict( &serverInfo );
  355. savegame.WriteInt( numClients );
  356. for( i = 0; i < numClients; i++ ) {
  357. savegame.WriteDict( &userInfo[ i ] );
  358. savegame.WriteUsercmd( usercmds[ i ] );
  359. savegame.WriteDict( &persistentPlayerInfo[ i ] );
  360. }
  361. for( i = 0; i < MAX_GENTITIES; i++ ) {
  362. savegame.WriteObject( entities[ i ] );
  363. savegame.WriteInt( spawnIds[ i ] );
  364. }
  365. savegame.WriteInt( firstFreeIndex );
  366. savegame.WriteInt( num_entities );
  367. // enityHash is restored by idEntity::Restore setting the entity name.
  368. savegame.WriteObject( world );
  369. savegame.WriteInt( spawnedEntities.Num() );
  370. for( ent = spawnedEntities.Next(); ent != NULL; ent = ent->spawnNode.Next() ) {
  371. savegame.WriteObject( ent );
  372. }
  373. savegame.WriteInt( activeEntities.Num() );
  374. for( ent = activeEntities.Next(); ent != NULL; ent = ent->activeNode.Next() ) {
  375. savegame.WriteObject( ent );
  376. }
  377. savegame.WriteInt( numEntitiesToDeactivate );
  378. savegame.WriteBool( sortPushers );
  379. savegame.WriteBool( sortTeamMasters );
  380. savegame.WriteDict( &persistentLevelInfo );
  381. for( i = 0; i < MAX_GLOBAL_SHADER_PARMS; i++ ) {
  382. savegame.WriteFloat( globalShaderParms[ i ] );
  383. }
  384. savegame.WriteInt( random.GetSeed() );
  385. savegame.WriteObject( frameCommandThread );
  386. // clip
  387. // push
  388. // pvs
  389. testmodel = NULL;
  390. testFx = NULL;
  391. savegame.WriteString( sessionCommand );
  392. // FIXME: save smoke particles
  393. savegame.WriteInt( cinematicSkipTime );
  394. savegame.WriteInt( cinematicStopTime );
  395. savegame.WriteInt( cinematicMaxSkipTime );
  396. savegame.WriteBool( inCinematic );
  397. savegame.WriteBool( skipCinematic );
  398. savegame.WriteBool( isMultiplayer );
  399. savegame.WriteInt( gameType );
  400. savegame.WriteInt( framenum );
  401. savegame.WriteInt( previousTime );
  402. savegame.WriteInt( time );
  403. savegame.WriteInt( vacuumAreaNum );
  404. savegame.WriteInt( entityDefBits );
  405. savegame.WriteBool( isServer );
  406. savegame.WriteBool( isClient );
  407. savegame.WriteInt( localClientNum );
  408. // snapshotEntities is used for multiplayer only
  409. savegame.WriteInt( realClientTime );
  410. savegame.WriteBool( isNewFrame );
  411. savegame.WriteFloat( clientSmoothing );
  412. savegame.WriteBool( mapCycleLoaded );
  413. savegame.WriteInt( spawnCount );
  414. if ( !locationEntities ) {
  415. savegame.WriteInt( 0 );
  416. } else {
  417. savegame.WriteInt( gameRenderWorld->NumAreas() );
  418. for( i = 0; i < gameRenderWorld->NumAreas(); i++ ) {
  419. savegame.WriteObject( locationEntities[ i ] );
  420. }
  421. }
  422. savegame.WriteObject( camera );
  423. savegame.WriteMaterial( globalMaterial );
  424. lastAIAlertEntity.Save( &savegame );
  425. savegame.WriteInt( lastAIAlertTime );
  426. savegame.WriteDict( &spawnArgs );
  427. savegame.WriteInt( playerPVS.i );
  428. savegame.WriteInt( playerPVS.h );
  429. savegame.WriteInt( playerConnectedAreas.i );
  430. savegame.WriteInt( playerConnectedAreas.h );
  431. savegame.WriteVec3( gravity );
  432. // gamestate
  433. savegame.WriteBool( influenceActive );
  434. savegame.WriteInt( nextGibTime );
  435. // spawnSpots
  436. // initialSpots
  437. // currentInitialSpot
  438. // newInfo
  439. // makingBuild
  440. // shakeSounds
  441. // write out pending events
  442. idEvent::Save( &savegame );
  443. savegame.Close();
  444. }
  445. /*
  446. ===========
  447. idGameLocal::GetPersistentPlayerInfo
  448. ============
  449. */
  450. const idDict &idGameLocal::GetPersistentPlayerInfo( int clientNum ) {
  451. idEntity *ent;
  452. persistentPlayerInfo[ clientNum ].Clear();
  453. ent = entities[ clientNum ];
  454. if ( ent && ent->IsType( idPlayer::Type ) ) {
  455. static_cast<idPlayer *>(ent)->SavePersistantInfo();
  456. }
  457. return persistentPlayerInfo[ clientNum ];
  458. }
  459. /*
  460. ===========
  461. idGameLocal::SetPersistentPlayerInfo
  462. ============
  463. */
  464. void idGameLocal::SetPersistentPlayerInfo( int clientNum, const idDict &playerInfo ) {
  465. persistentPlayerInfo[ clientNum ] = playerInfo;
  466. }
  467. /*
  468. ============
  469. idGameLocal::Printf
  470. ============
  471. */
  472. void idGameLocal::Printf( const char *fmt, ... ) const {
  473. va_list argptr;
  474. char text[MAX_STRING_CHARS];
  475. va_start( argptr, fmt );
  476. idStr::vsnPrintf( text, sizeof( text ), fmt, argptr );
  477. va_end( argptr );
  478. common->Printf( "%s", text );
  479. }
  480. /*
  481. ============
  482. idGameLocal::DPrintf
  483. ============
  484. */
  485. void idGameLocal::DPrintf( const char *fmt, ... ) const {
  486. va_list argptr;
  487. char text[MAX_STRING_CHARS];
  488. if ( !developer.GetBool() ) {
  489. return;
  490. }
  491. va_start( argptr, fmt );
  492. idStr::vsnPrintf( text, sizeof( text ), fmt, argptr );
  493. va_end( argptr );
  494. common->Printf( "%s", text );
  495. }
  496. /*
  497. ============
  498. idGameLocal::Warning
  499. ============
  500. */
  501. void idGameLocal::Warning( const char *fmt, ... ) const {
  502. va_list argptr;
  503. char text[MAX_STRING_CHARS];
  504. idThread * thread;
  505. va_start( argptr, fmt );
  506. idStr::vsnPrintf( text, sizeof( text ), fmt, argptr );
  507. va_end( argptr );
  508. thread = idThread::CurrentThread();
  509. if ( thread ) {
  510. thread->Warning( "%s", text );
  511. } else {
  512. common->Warning( "%s", text );
  513. }
  514. }
  515. /*
  516. ============
  517. idGameLocal::DWarning
  518. ============
  519. */
  520. void idGameLocal::DWarning( const char *fmt, ... ) const {
  521. va_list argptr;
  522. char text[MAX_STRING_CHARS];
  523. idThread * thread;
  524. if ( !developer.GetBool() ) {
  525. return;
  526. }
  527. va_start( argptr, fmt );
  528. idStr::vsnPrintf( text, sizeof( text ), fmt, argptr );
  529. va_end( argptr );
  530. thread = idThread::CurrentThread();
  531. if ( thread ) {
  532. thread->Warning( "%s", text );
  533. } else {
  534. common->DWarning( "%s", text );
  535. }
  536. }
  537. /*
  538. ============
  539. idGameLocal::Error
  540. ============
  541. */
  542. void idGameLocal::Error( const char *fmt, ... ) const {
  543. va_list argptr;
  544. char text[MAX_STRING_CHARS];
  545. idThread * thread;
  546. va_start( argptr, fmt );
  547. idStr::vsnPrintf( text, sizeof( text ), fmt, argptr );
  548. va_end( argptr );
  549. thread = idThread::CurrentThread();
  550. if ( thread ) {
  551. thread->Error( "%s", text );
  552. } else {
  553. common->Error( "%s", text );
  554. }
  555. }
  556. /*
  557. ===============
  558. gameError
  559. ===============
  560. */
  561. void gameError( const char *fmt, ... ) {
  562. va_list argptr;
  563. char text[MAX_STRING_CHARS];
  564. va_start( argptr, fmt );
  565. idStr::vsnPrintf( text, sizeof( text ), fmt, argptr );
  566. va_end( argptr );
  567. gameLocal.Error( "%s", text );
  568. }
  569. /*
  570. ===========
  571. idGameLocal::SetLocalClient
  572. ============
  573. */
  574. void idGameLocal::SetLocalClient( int clientNum ) {
  575. localClientNum = clientNum;
  576. }
  577. /*
  578. ===========
  579. idGameLocal::SetUserInfo
  580. ============
  581. */
  582. const idDict* idGameLocal::SetUserInfo( int clientNum, const idDict &userInfo, bool isClient, bool canModify ) {
  583. int i;
  584. bool modifiedInfo = false;
  585. this->isClient = isClient;
  586. if ( clientNum >= 0 && clientNum < MAX_CLIENTS ) {
  587. idGameLocal::userInfo[ clientNum ] = userInfo;
  588. // server sanity
  589. if ( canModify ) {
  590. // don't let numeric nicknames, it can be exploited to go around kick and ban commands from the server
  591. if ( idStr::IsNumeric( this->userInfo[ clientNum ].GetString( "ui_name" ) ) ) {
  592. idGameLocal::userInfo[ clientNum ].Set( "ui_name", va( "%s_", idGameLocal::userInfo[ clientNum ].GetString( "ui_name" ) ) );
  593. modifiedInfo = true;
  594. }
  595. // don't allow dupe nicknames
  596. for ( i = 0; i < numClients; i++ ) {
  597. if ( i == clientNum ) {
  598. continue;
  599. }
  600. if ( entities[ i ] && entities[ i ]->IsType( idPlayer::Type ) ) {
  601. if ( !idStr::Icmp( idGameLocal::userInfo[ clientNum ].GetString( "ui_name" ), idGameLocal::userInfo[ i ].GetString( "ui_name" ) ) ) {
  602. idGameLocal::userInfo[ clientNum ].Set( "ui_name", va( "%s_", idGameLocal::userInfo[ clientNum ].GetString( "ui_name" ) ) );
  603. modifiedInfo = true;
  604. i = -1; // rescan
  605. continue;
  606. }
  607. }
  608. }
  609. }
  610. if ( entities[ clientNum ] && entities[ clientNum ]->IsType( idPlayer::Type ) ) {
  611. modifiedInfo |= static_cast<idPlayer *>( entities[ clientNum ] )->UserInfoChanged( canModify );
  612. }
  613. if ( !isClient ) {
  614. // now mark this client in game
  615. mpGame.EnterGame( clientNum );
  616. }
  617. }
  618. if ( modifiedInfo ) {
  619. assert( canModify );
  620. newInfo = idGameLocal::userInfo[ clientNum ];
  621. return &newInfo;
  622. }
  623. return NULL;
  624. }
  625. /*
  626. ===========
  627. idGameLocal::GetUserInfo
  628. ============
  629. */
  630. const idDict* idGameLocal::GetUserInfo( int clientNum ) {
  631. if ( entities[ clientNum ] && entities[ clientNum ]->IsType( idPlayer::Type ) ) {
  632. return &userInfo[ clientNum ];
  633. }
  634. return NULL;
  635. }
  636. /*
  637. ===========
  638. idGameLocal::SetServerInfo
  639. ============
  640. */
  641. void idGameLocal::SetServerInfo( const idDict &_serverInfo ) {
  642. idBitMsg outMsg;
  643. byte msgBuf[MAX_GAME_MESSAGE_SIZE];
  644. serverInfo = _serverInfo;
  645. UpdateServerInfoFlags();
  646. if ( !isClient ) {
  647. // Let our clients know the server info changed
  648. outMsg.Init( msgBuf, sizeof( msgBuf ) );
  649. outMsg.WriteByte( GAME_RELIABLE_MESSAGE_SERVERINFO );
  650. outMsg.WriteDeltaDict( gameLocal.serverInfo, NULL );
  651. networkSystem->ServerSendReliableMessage( -1, outMsg );
  652. }
  653. }
  654. /*
  655. ===================
  656. idGameLocal::LoadMap
  657. Initializes all map variables common to both save games and spawned games.
  658. ===================
  659. */
  660. void idGameLocal::LoadMap( const char *mapName, int randseed ) {
  661. int i;
  662. bool sameMap = (mapFile && idStr::Icmp(mapFileName, mapName) == 0);
  663. // clear the sound system
  664. gameSoundWorld->ClearAllSoundEmitters();
  665. InitAsyncNetwork();
  666. if ( !sameMap || ( mapFile && mapFile->NeedsReload() ) ) {
  667. // load the .map file
  668. if ( mapFile ) {
  669. delete mapFile;
  670. }
  671. mapFile = new idMapFile;
  672. if ( !mapFile->Parse( idStr( mapName ) + ".map" ) ) {
  673. delete mapFile;
  674. mapFile = NULL;
  675. Error( "Couldn't load %s", mapName );
  676. }
  677. }
  678. mapFileName = mapFile->GetName();
  679. // load the collision map
  680. collisionModelManager->LoadMap( mapFile );
  681. numClients = 0;
  682. // initialize all entities for this game
  683. memset( entities, 0, sizeof( entities ) );
  684. memset( usercmds, 0, sizeof( usercmds ) );
  685. memset( spawnIds, -1, sizeof( spawnIds ) );
  686. spawnCount = INITIAL_SPAWN_COUNT;
  687. spawnedEntities.Clear();
  688. activeEntities.Clear();
  689. numEntitiesToDeactivate = 0;
  690. sortTeamMasters = false;
  691. sortPushers = false;
  692. lastGUIEnt = NULL;
  693. lastGUI = 0;
  694. globalMaterial = NULL;
  695. memset( globalShaderParms, 0, sizeof( globalShaderParms ) );
  696. // always leave room for the max number of clients,
  697. // even if they aren't all used, so numbers inside that
  698. // range are NEVER anything but clients
  699. num_entities = MAX_CLIENTS;
  700. firstFreeIndex = MAX_CLIENTS;
  701. // reset the random number generator.
  702. random.SetSeed( isMultiplayer ? randseed : 0 );
  703. camera = NULL;
  704. world = NULL;
  705. testmodel = NULL;
  706. testFx = NULL;
  707. lastAIAlertEntity = NULL;
  708. lastAIAlertTime = 0;
  709. previousTime = 0;
  710. time = 0;
  711. framenum = 0;
  712. sessionCommand = "";
  713. nextGibTime = 0;
  714. vacuumAreaNum = -1; // if an info_vacuum is spawned, it will set this
  715. if ( !editEntities ) {
  716. editEntities = new idEditEntities;
  717. }
  718. gravity.Set( 0, 0, -g_gravity.GetFloat() );
  719. spawnArgs.Clear();
  720. skipCinematic = false;
  721. inCinematic = false;
  722. cinematicSkipTime = 0;
  723. cinematicStopTime = 0;
  724. cinematicMaxSkipTime = 0;
  725. clip.Init();
  726. pvs.Init();
  727. playerPVS.i = -1;
  728. playerConnectedAreas.i = -1;
  729. // load navigation system for all the different monster sizes
  730. for( i = 0; i < aasNames.Num(); i++ ) {
  731. aasList[ i ]->Init( idStr( mapFileName ).SetFileExtension( aasNames[ i ] ).c_str(), mapFile->GetGeometryCRC() );
  732. }
  733. // clear the smoke particle free list
  734. smokeParticles->Init();
  735. // cache miscellanious media references
  736. FindEntityDef( "preCacheExtras", false );
  737. if ( !sameMap ) {
  738. mapFile->RemovePrimitiveData();
  739. }
  740. }
  741. /*
  742. ===================
  743. idGameLocal::LocalMapRestart
  744. ===================
  745. */
  746. void idGameLocal::LocalMapRestart( ) {
  747. int i, latchSpawnCount;
  748. Printf( "----------- Game Map Restart ------------\n" );
  749. gamestate = GAMESTATE_SHUTDOWN;
  750. for ( i = 0; i < MAX_CLIENTS; i++ ) {
  751. if ( entities[ i ] && entities[ i ]->IsType( idPlayer::Type ) ) {
  752. static_cast< idPlayer * >( entities[ i ] )->PrepareForRestart();
  753. }
  754. }
  755. eventQueue.Shutdown();
  756. savedEventQueue.Shutdown();
  757. MapClear( false );
  758. // clear the smoke particle free list
  759. smokeParticles->Init();
  760. // clear the sound system
  761. if ( gameSoundWorld ) {
  762. gameSoundWorld->ClearAllSoundEmitters();
  763. }
  764. // the spawnCount is reset to zero temporarily to spawn the map entities with the same spawnId
  765. // if we don't do that, network clients are confused and don't show any map entities
  766. latchSpawnCount = spawnCount;
  767. spawnCount = INITIAL_SPAWN_COUNT;
  768. gamestate = GAMESTATE_STARTUP;
  769. program.Restart();
  770. InitScriptForMap();
  771. MapPopulate();
  772. // once the map is populated, set the spawnCount back to where it was so we don't risk any collision
  773. // (note that if there are no players in the game, we could just leave it at it's current value)
  774. spawnCount = latchSpawnCount;
  775. // setup the client entities again
  776. for ( i = 0; i < MAX_CLIENTS; i++ ) {
  777. if ( entities[ i ] && entities[ i ]->IsType( idPlayer::Type ) ) {
  778. static_cast< idPlayer * >( entities[ i ] )->Restart();
  779. }
  780. }
  781. gamestate = GAMESTATE_ACTIVE;
  782. Printf( "--------------------------------------\n" );
  783. }
  784. /*
  785. ===================
  786. idGameLocal::MapRestart
  787. ===================
  788. */
  789. void idGameLocal::MapRestart( ) {
  790. idBitMsg outMsg;
  791. byte msgBuf[MAX_GAME_MESSAGE_SIZE];
  792. idDict newInfo;
  793. int i;
  794. const idKeyValue *keyval, *keyval2;
  795. if ( isClient ) {
  796. LocalMapRestart();
  797. } else {
  798. newInfo = *cvarSystem->MoveCVarsToDict( CVAR_SERVERINFO );
  799. for ( i = 0; i < newInfo.GetNumKeyVals(); i++ ) {
  800. keyval = newInfo.GetKeyVal( i );
  801. keyval2 = serverInfo.FindKey( keyval->GetKey() );
  802. if ( !keyval2 ) {
  803. break;
  804. }
  805. // a select set of si_ changes will cause a full restart of the server
  806. if ( keyval->GetValue().Cmp( keyval2->GetValue() ) &&
  807. ( !keyval->GetKey().Cmp( "si_pure" ) || !keyval->GetKey().Cmp( "si_map" ) ) ) {
  808. break;
  809. }
  810. }
  811. cmdSystem->BufferCommandText( CMD_EXEC_NOW, "rescanSI" );
  812. if ( i != newInfo.GetNumKeyVals() ) {
  813. cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "nextMap" );
  814. } else {
  815. outMsg.Init( msgBuf, sizeof( msgBuf ) );
  816. outMsg.WriteByte( GAME_RELIABLE_MESSAGE_RESTART );
  817. outMsg.WriteBits( 1, 1 );
  818. outMsg.WriteDeltaDict( serverInfo, NULL );
  819. networkSystem->ServerSendReliableMessage( -1, outMsg );
  820. LocalMapRestart();
  821. mpGame.MapRestart();
  822. }
  823. }
  824. }
  825. /*
  826. ===================
  827. idGameLocal::MapRestart_f
  828. ===================
  829. */
  830. void idGameLocal::MapRestart_f( const idCmdArgs &args ) {
  831. if ( !gameLocal.isMultiplayer || gameLocal.isClient ) {
  832. common->Printf( "server is not running - use spawnServer\n" );
  833. cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "spawnServer\n" );
  834. return;
  835. }
  836. gameLocal.MapRestart( );
  837. }
  838. /*
  839. ===================
  840. idGameLocal::NextMap
  841. ===================
  842. */
  843. bool idGameLocal::NextMap( void ) {
  844. const function_t *func;
  845. idThread *thread;
  846. idDict newInfo;
  847. const idKeyValue *keyval, *keyval2;
  848. int i;
  849. if ( !g_mapCycle.GetString()[0] ) {
  850. Printf( common->GetLanguageDict()->GetString( "#str_04294" ) );
  851. return false;
  852. }
  853. if ( fileSystem->ReadFile( g_mapCycle.GetString(), NULL, NULL ) < 0 ) {
  854. if ( fileSystem->ReadFile( va( "%s.scriptcfg", g_mapCycle.GetString() ), NULL, NULL ) < 0 ) {
  855. Printf( "map cycle script '%s': not found\n", g_mapCycle.GetString() );
  856. return false;
  857. } else {
  858. g_mapCycle.SetString( va( "%s.scriptcfg", g_mapCycle.GetString() ) );
  859. }
  860. }
  861. Printf( "map cycle script: '%s'\n", g_mapCycle.GetString() );
  862. func = program.FindFunction( "mapcycle::cycle" );
  863. if ( !func ) {
  864. program.CompileFile( g_mapCycle.GetString() );
  865. func = program.FindFunction( "mapcycle::cycle" );
  866. }
  867. if ( !func ) {
  868. Printf( "Couldn't find mapcycle::cycle\n" );
  869. return false;
  870. }
  871. thread = new idThread( func );
  872. thread->Start();
  873. delete thread;
  874. newInfo = *cvarSystem->MoveCVarsToDict( CVAR_SERVERINFO );
  875. for ( i = 0; i < newInfo.GetNumKeyVals(); i++ ) {
  876. keyval = newInfo.GetKeyVal( i );
  877. keyval2 = serverInfo.FindKey( keyval->GetKey() );
  878. if ( !keyval2 || keyval->GetValue().Cmp( keyval2->GetValue() ) ) {
  879. break;
  880. }
  881. }
  882. return ( i != newInfo.GetNumKeyVals() );
  883. }
  884. /*
  885. ===================
  886. idGameLocal::NextMap_f
  887. ===================
  888. */
  889. void idGameLocal::NextMap_f( const idCmdArgs &args ) {
  890. if ( !gameLocal.isMultiplayer || gameLocal.isClient ) {
  891. common->Printf( "server is not running\n" );
  892. return;
  893. }
  894. gameLocal.NextMap( );
  895. // next map was either voted for or triggered by a server command - always restart
  896. gameLocal.MapRestart( );
  897. }
  898. /*
  899. ===================
  900. idGameLocal::MapPopulate
  901. ===================
  902. */
  903. void idGameLocal::MapPopulate( void ) {
  904. if ( isMultiplayer ) {
  905. cvarSystem->SetCVarBool( "r_skipSpecular", false );
  906. }
  907. // parse the key/value pairs and spawn entities
  908. SpawnMapEntities();
  909. // mark location entities in all connected areas
  910. SpreadLocations();
  911. // prepare the list of randomized initial spawn spots
  912. RandomizeInitialSpawns();
  913. // spawnCount - 1 is the number of entities spawned into the map, their indexes started at MAX_CLIENTS (included)
  914. // mapSpawnCount is used as the max index of map entities, it's the first index of non-map entities
  915. mapSpawnCount = MAX_CLIENTS + spawnCount - 1;
  916. // execute pending events before the very first game frame
  917. // this makes sure the map script main() function is called
  918. // before the physics are run so entities can bind correctly
  919. Printf( "==== Processing events ====\n" );
  920. idEvent::ServiceEvents();
  921. }
  922. /*
  923. ===================
  924. idGameLocal::InitFromNewMap
  925. ===================
  926. */
  927. void idGameLocal::InitFromNewMap( const char *mapName, idRenderWorld *renderWorld, idSoundWorld *soundWorld, bool isServer, bool isClient, int randseed ) {
  928. this->isServer = isServer;
  929. this->isClient = isClient;
  930. this->isMultiplayer = isServer || isClient;
  931. if ( mapFileName.Length() ) {
  932. MapShutdown();
  933. }
  934. Printf( "----------- Game Map Init ------------\n" );
  935. gamestate = GAMESTATE_STARTUP;
  936. gameRenderWorld = renderWorld;
  937. gameSoundWorld = soundWorld;
  938. LoadMap( mapName, randseed );
  939. InitScriptForMap();
  940. MapPopulate();
  941. mpGame.Reset();
  942. mpGame.Precache();
  943. // free up any unused animations
  944. animationLib.FlushUnusedAnims();
  945. gamestate = GAMESTATE_ACTIVE;
  946. Printf( "--------------------------------------\n" );
  947. }
  948. /*
  949. =================
  950. idGameLocal::InitFromSaveGame
  951. =================
  952. */
  953. bool idGameLocal::InitFromSaveGame( const char *mapName, idRenderWorld *renderWorld, idSoundWorld *soundWorld, idFile *saveGameFile ) {
  954. int i;
  955. int num;
  956. idEntity *ent;
  957. idDict si;
  958. if ( mapFileName.Length() ) {
  959. MapShutdown();
  960. }
  961. Printf( "------- Game Map Init SaveGame -------\n" );
  962. gamestate = GAMESTATE_STARTUP;
  963. gameRenderWorld = renderWorld;
  964. gameSoundWorld = soundWorld;
  965. idRestoreGame savegame( saveGameFile );
  966. savegame.ReadBuildNumber();
  967. // Create the list of all objects in the game
  968. savegame.CreateObjects();
  969. // Load the idProgram, also checking to make sure scripting hasn't changed since the savegame
  970. if ( program.Restore( &savegame ) == false ) {
  971. // Abort the load process, and let the session know so that it can restart the level
  972. // with the player persistent data.
  973. savegame.DeleteObjects();
  974. program.Restart();
  975. return false;
  976. }
  977. // load the map needed for this savegame
  978. LoadMap( mapName, 0 );
  979. savegame.ReadInt( i );
  980. g_skill.SetInteger( i );
  981. // precache the player
  982. FindEntityDef( "player_doommarine", false );
  983. // precache any media specified in the map
  984. for ( i = 0; i < mapFile->GetNumEntities(); i++ ) {
  985. idMapEntity *mapEnt = mapFile->GetEntity( i );
  986. if ( !InhibitEntitySpawn( mapEnt->epairs ) ) {
  987. CacheDictionaryMedia( &mapEnt->epairs );
  988. const char *classname = mapEnt->epairs.GetString( "classname" );
  989. if ( classname != '\0' ) {
  990. FindEntityDef( classname, false );
  991. }
  992. }
  993. }
  994. savegame.ReadDict( &si );
  995. SetServerInfo( si );
  996. savegame.ReadInt( numClients );
  997. for( i = 0; i < numClients; i++ ) {
  998. savegame.ReadDict( &userInfo[ i ] );
  999. savegame.ReadUsercmd( usercmds[ i ] );
  1000. savegame.ReadDict( &persistentPlayerInfo[ i ] );
  1001. }
  1002. for( i = 0; i < MAX_GENTITIES; i++ ) {
  1003. savegame.ReadObject( reinterpret_cast<idClass *&>( entities[ i ] ) );
  1004. savegame.ReadInt( spawnIds[ i ] );
  1005. // restore the entityNumber
  1006. if ( entities[ i ] != NULL ) {
  1007. entities[ i ]->entityNumber = i;
  1008. }
  1009. }
  1010. savegame.ReadInt( firstFreeIndex );
  1011. savegame.ReadInt( num_entities );
  1012. // enityHash is restored by idEntity::Restore setting the entity name.
  1013. savegame.ReadObject( reinterpret_cast<idClass *&>( world ) );
  1014. savegame.ReadInt( num );
  1015. for( i = 0; i < num; i++ ) {
  1016. savegame.ReadObject( reinterpret_cast<idClass *&>( ent ) );
  1017. assert( ent );
  1018. if ( ent ) {
  1019. ent->spawnNode.AddToEnd( spawnedEntities );
  1020. }
  1021. }
  1022. savegame.ReadInt( num );
  1023. for( i = 0; i < num; i++ ) {
  1024. savegame.ReadObject( reinterpret_cast<idClass *&>( ent ) );
  1025. assert( ent );
  1026. if ( ent ) {
  1027. ent->activeNode.AddToEnd( activeEntities );
  1028. }
  1029. }
  1030. savegame.ReadInt( numEntitiesToDeactivate );
  1031. savegame.ReadBool( sortPushers );
  1032. savegame.ReadBool( sortTeamMasters );
  1033. savegame.ReadDict( &persistentLevelInfo );
  1034. for( i = 0; i < MAX_GLOBAL_SHADER_PARMS; i++ ) {
  1035. savegame.ReadFloat( globalShaderParms[ i ] );
  1036. }
  1037. savegame.ReadInt( i );
  1038. random.SetSeed( i );
  1039. savegame.ReadObject( reinterpret_cast<idClass *&>( frameCommandThread ) );
  1040. // clip
  1041. // push
  1042. // pvs
  1043. // testmodel = "<NULL>"
  1044. // testFx = "<NULL>"
  1045. savegame.ReadString( sessionCommand );
  1046. // FIXME: save smoke particles
  1047. savegame.ReadInt( cinematicSkipTime );
  1048. savegame.ReadInt( cinematicStopTime );
  1049. savegame.ReadInt( cinematicMaxSkipTime );
  1050. savegame.ReadBool( inCinematic );
  1051. savegame.ReadBool( skipCinematic );
  1052. savegame.ReadBool( isMultiplayer );
  1053. savegame.ReadInt( (int &)gameType );
  1054. savegame.ReadInt( framenum );
  1055. savegame.ReadInt( previousTime );
  1056. savegame.ReadInt( time );
  1057. savegame.ReadInt( vacuumAreaNum );
  1058. savegame.ReadInt( entityDefBits );
  1059. savegame.ReadBool( isServer );
  1060. savegame.ReadBool( isClient );
  1061. savegame.ReadInt( localClientNum );
  1062. // snapshotEntities is used for multiplayer only
  1063. savegame.ReadInt( realClientTime );
  1064. savegame.ReadBool( isNewFrame );
  1065. savegame.ReadFloat( clientSmoothing );
  1066. savegame.ReadBool( mapCycleLoaded );
  1067. savegame.ReadInt( spawnCount );
  1068. savegame.ReadInt( num );
  1069. if ( num ) {
  1070. if ( num != gameRenderWorld->NumAreas() ) {
  1071. savegame.Error( "idGameLocal::InitFromSaveGame: number of areas in map differs from save game." );
  1072. }
  1073. locationEntities = new idLocationEntity *[ num ];
  1074. for( i = 0; i < num; i++ ) {
  1075. savegame.ReadObject( reinterpret_cast<idClass *&>( locationEntities[ i ] ) );
  1076. }
  1077. }
  1078. savegame.ReadObject( reinterpret_cast<idClass *&>( camera ) );
  1079. savegame.ReadMaterial( globalMaterial );
  1080. lastAIAlertEntity.Restore( &savegame );
  1081. savegame.ReadInt( lastAIAlertTime );
  1082. savegame.ReadDict( &spawnArgs );
  1083. savegame.ReadInt( playerPVS.i );
  1084. savegame.ReadInt( (int &)playerPVS.h );
  1085. savegame.ReadInt( playerConnectedAreas.i );
  1086. savegame.ReadInt( (int &)playerConnectedAreas.h );
  1087. savegame.ReadVec3( gravity );
  1088. // gamestate is restored after restoring everything else
  1089. savegame.ReadBool( influenceActive );
  1090. savegame.ReadInt( nextGibTime );
  1091. // spawnSpots
  1092. // initialSpots
  1093. // currentInitialSpot
  1094. // newInfo
  1095. // makingBuild
  1096. // shakeSounds
  1097. // Read out pending events
  1098. idEvent::Restore( &savegame );
  1099. savegame.RestoreObjects();
  1100. mpGame.Reset();
  1101. mpGame.Precache();
  1102. // free up any unused animations
  1103. animationLib.FlushUnusedAnims();
  1104. gamestate = GAMESTATE_ACTIVE;
  1105. Printf( "--------------------------------------\n" );
  1106. return true;
  1107. }
  1108. /*
  1109. ===========
  1110. idGameLocal::MapClear
  1111. ===========
  1112. */
  1113. void idGameLocal::MapClear( bool clearClients ) {
  1114. int i;
  1115. for( i = ( clearClients ? 0 : MAX_CLIENTS ); i < MAX_GENTITIES; i++ ) {
  1116. delete entities[ i ];
  1117. // ~idEntity is in charge of setting the pointer to NULL
  1118. // it will also clear pending events for this entity
  1119. assert( !entities[ i ] );
  1120. spawnIds[ i ] = -1;
  1121. }
  1122. entityHash.Clear( 1024, MAX_GENTITIES );
  1123. if ( !clearClients ) {
  1124. // add back the hashes of the clients
  1125. for ( i = 0; i < MAX_CLIENTS; i++ ) {
  1126. if ( !entities[ i ] ) {
  1127. continue;
  1128. }
  1129. entityHash.Add( entityHash.GenerateKey( entities[ i ]->name.c_str(), true ), i );
  1130. }
  1131. }
  1132. delete frameCommandThread;
  1133. frameCommandThread = NULL;
  1134. if ( editEntities ) {
  1135. delete editEntities;
  1136. editEntities = NULL;
  1137. }
  1138. delete[] locationEntities;
  1139. locationEntities = NULL;
  1140. }
  1141. /*
  1142. ===========
  1143. idGameLocal::MapShutdown
  1144. ============
  1145. */
  1146. void idGameLocal::MapShutdown( void ) {
  1147. Printf( "--------- Game Map Shutdown ----------\n" );
  1148. gamestate = GAMESTATE_SHUTDOWN;
  1149. if ( gameRenderWorld ) {
  1150. // clear any debug lines, text, and polygons
  1151. gameRenderWorld->DebugClearLines( 0 );
  1152. gameRenderWorld->DebugClearPolygons( 0 );
  1153. }
  1154. // clear out camera if we're in a cinematic
  1155. if ( inCinematic ) {
  1156. camera = NULL;
  1157. inCinematic = false;
  1158. }
  1159. MapClear( true );
  1160. // reset the script to the state it was before the map was started
  1161. program.Restart();
  1162. if ( smokeParticles ) {
  1163. smokeParticles->Shutdown();
  1164. }
  1165. pvs.Shutdown();
  1166. clip.Shutdown();
  1167. idClipModel::ClearTraceModelCache();
  1168. ShutdownAsyncNetwork();
  1169. mapFileName.Clear();
  1170. gameRenderWorld = NULL;
  1171. gameSoundWorld = NULL;
  1172. gamestate = GAMESTATE_NOMAP;
  1173. Printf( "--------------------------------------\n" );
  1174. }
  1175. /*
  1176. ===================
  1177. idGameLocal::DumpOggSounds
  1178. ===================
  1179. */
  1180. void idGameLocal::DumpOggSounds( void ) {
  1181. int i, j, k, size, totalSize;
  1182. idFile *file;
  1183. idStrList oggSounds, weaponSounds;
  1184. const idSoundShader *soundShader;
  1185. const soundShaderParms_t *parms;
  1186. idStr soundName;
  1187. for ( i = 0; i < declManager->GetNumDecls( DECL_SOUND ); i++ ) {
  1188. soundShader = static_cast<const idSoundShader *>(declManager->DeclByIndex( DECL_SOUND, i, false ));
  1189. parms = soundShader->GetParms();
  1190. if ( soundShader->EverReferenced() && soundShader->GetState() != DS_DEFAULTED ) {
  1191. const_cast<idSoundShader *>(soundShader)->EnsureNotPurged();
  1192. for ( j = 0; j < soundShader->GetNumSounds(); j++ ) {
  1193. soundName = soundShader->GetSound( j );
  1194. soundName.BackSlashesToSlashes();
  1195. // don't OGG sounds that cause a shake because that would
  1196. // cause continuous seeking on the OGG file which is expensive
  1197. if ( parms->shakes != 0.0f ) {
  1198. shakeSounds.AddUnique( soundName );
  1199. continue;
  1200. }
  1201. // if not voice over or combat chatter
  1202. if ( soundName.Find( "/vo/", false ) == -1 &&
  1203. soundName.Find( "/combat_chatter/", false ) == -1 &&
  1204. soundName.Find( "/bfgcarnage/", false ) == -1 &&
  1205. soundName.Find( "/enpro/", false ) == - 1 &&
  1206. soundName.Find( "/soulcube/energize_01.wav", false ) == -1 ) {
  1207. // don't OGG weapon sounds
  1208. if ( soundName.Find( "weapon", false ) != -1 ||
  1209. soundName.Find( "gun", false ) != -1 ||
  1210. soundName.Find( "bullet", false ) != -1 ||
  1211. soundName.Find( "bfg", false ) != -1 ||
  1212. soundName.Find( "plasma", false ) != -1 ) {
  1213. weaponSounds.AddUnique( soundName );
  1214. continue;
  1215. }
  1216. }
  1217. for ( k = 0; k < shakeSounds.Num(); k++ ) {
  1218. if ( shakeSounds[k].IcmpPath( soundName ) == 0 ) {
  1219. break;
  1220. }
  1221. }
  1222. if ( k < shakeSounds.Num() ) {
  1223. continue;
  1224. }
  1225. oggSounds.AddUnique( soundName );
  1226. }
  1227. }
  1228. }
  1229. file = fileSystem->OpenFileWrite( "makeogg.bat", "fs_savepath" );
  1230. if ( file == NULL ) {
  1231. common->Warning( "Couldn't open makeogg.bat" );
  1232. return;
  1233. }
  1234. // list all the shake sounds
  1235. totalSize = 0;
  1236. for ( i = 0; i < shakeSounds.Num(); i++ ) {
  1237. size = fileSystem->ReadFile( shakeSounds[i], NULL, NULL );
  1238. totalSize += size;
  1239. shakeSounds[i].Replace( "/", "\\" );
  1240. file->Printf( "echo \"%s\" (%d kB)\n", shakeSounds[i].c_str(), size >> 10 );
  1241. }
  1242. file->Printf( "echo %d kB in shake sounds\n\n\n", totalSize >> 10 );
  1243. // list all the weapon sounds
  1244. totalSize = 0;
  1245. for ( i = 0; i < weaponSounds.Num(); i++ ) {
  1246. size = fileSystem->ReadFile( weaponSounds[i], NULL, NULL );
  1247. totalSize += size;
  1248. weaponSounds[i].Replace( "/", "\\" );
  1249. file->Printf( "echo \"%s\" (%d kB)\n", weaponSounds[i].c_str(), size >> 10 );
  1250. }
  1251. file->Printf( "echo %d kB in weapon sounds\n\n\n", totalSize >> 10 );
  1252. // list commands to convert all other sounds to ogg
  1253. totalSize = 0;
  1254. for ( i = 0; i < oggSounds.Num(); i++ ) {
  1255. size = fileSystem->ReadFile( oggSounds[i], NULL, NULL );
  1256. totalSize += size;
  1257. oggSounds[i].Replace( "/", "\\" );
  1258. file->Printf( "w:\\doom\\ogg\\oggenc -q 0 \"c:\\doom\\base\\%s\"\n", oggSounds[i].c_str() );
  1259. file->Printf( "del \"c:\\doom\\base\\%s\"\n", oggSounds[i].c_str() );
  1260. }
  1261. file->Printf( "\n\necho %d kB in OGG sounds\n\n\n", totalSize >> 10 );
  1262. fileSystem->CloseFile( file );
  1263. shakeSounds.Clear();
  1264. }
  1265. /*
  1266. ===================
  1267. idGameLocal::GetShakeSounds
  1268. ===================
  1269. */
  1270. void idGameLocal::GetShakeSounds( const idDict *dict ) {
  1271. const idSoundShader *soundShader;
  1272. const char *soundShaderName;
  1273. idStr soundName;
  1274. soundShaderName = dict->GetString( "s_shader" );
  1275. if ( soundShaderName != '\0' && dict->GetFloat( "s_shakes" ) != 0.0f ) {
  1276. soundShader = declManager->FindSound( soundShaderName );
  1277. for ( int i = 0; i < soundShader->GetNumSounds(); i++ ) {
  1278. soundName = soundShader->GetSound( i );
  1279. soundName.BackSlashesToSlashes();
  1280. shakeSounds.AddUnique( soundName );
  1281. }
  1282. }
  1283. }
  1284. /*
  1285. ===================
  1286. idGameLocal::CacheDictionaryMedia
  1287. This is called after parsing an EntityDef and for each entity spawnArgs before
  1288. merging the entitydef. It could be done post-merge, but that would
  1289. avoid the fast pre-cache check associated with each entityDef
  1290. ===================
  1291. */
  1292. void idGameLocal::CacheDictionaryMedia( const idDict *dict ) {
  1293. const idKeyValue *kv;
  1294. if ( dict == NULL ) {
  1295. if ( cvarSystem->GetCVarBool( "com_makingBuild") ) {
  1296. DumpOggSounds();
  1297. }
  1298. return;
  1299. }
  1300. if ( cvarSystem->GetCVarBool( "com_makingBuild" ) ) {
  1301. GetShakeSounds( dict );
  1302. }
  1303. kv = dict->MatchPrefix( "model" );
  1304. while( kv ) {
  1305. if ( kv->GetValue().Length() ) {
  1306. declManager->MediaPrint( "Precaching model %s\n", kv->GetValue().c_str() );
  1307. // precache model/animations
  1308. if ( declManager->FindType( DECL_MODELDEF, kv->GetValue(), false ) == NULL ) {
  1309. // precache the render model
  1310. renderModelManager->FindModel( kv->GetValue() );
  1311. // precache .cm files only
  1312. collisionModelManager->LoadModel( kv->GetValue(), true );
  1313. }
  1314. }
  1315. kv = dict->MatchPrefix( "model", kv );
  1316. }
  1317. kv = dict->FindKey( "s_shader" );
  1318. if ( kv && kv->GetValue().Length() ) {
  1319. declManager->FindType( DECL_SOUND, kv->GetValue() );
  1320. }
  1321. kv = dict->MatchPrefix( "snd", NULL );
  1322. while( kv ) {
  1323. if ( kv->GetValue().Length() ) {
  1324. declManager->FindType( DECL_SOUND, kv->GetValue() );
  1325. }
  1326. kv = dict->MatchPrefix( "snd", kv );
  1327. }
  1328. kv = dict->MatchPrefix( "gui", NULL );
  1329. while( kv ) {
  1330. if ( kv->GetValue().Length() ) {
  1331. if ( !idStr::Icmp( kv->GetKey(), "gui_noninteractive" )
  1332. || !idStr::Icmpn( kv->GetKey(), "gui_parm", 8 )
  1333. || !idStr::Icmp( kv->GetKey(), "gui_inventory" ) ) {
  1334. // unfortunate flag names, they aren't actually a gui
  1335. } else {
  1336. declManager->MediaPrint( "Precaching gui %s\n", kv->GetValue().c_str() );
  1337. idUserInterface *gui = uiManager->Alloc();
  1338. if ( gui ) {
  1339. gui->InitFromFile( kv->GetValue() );
  1340. uiManager->DeAlloc( gui );
  1341. }
  1342. }
  1343. }
  1344. kv = dict->MatchPrefix( "gui", kv );
  1345. }
  1346. kv = dict->FindKey( "texture" );
  1347. if ( kv && kv->GetValue().Length() ) {
  1348. declManager->FindType( DECL_MATERIAL, kv->GetValue() );
  1349. }
  1350. kv = dict->MatchPrefix( "mtr", NULL );
  1351. while( kv ) {
  1352. if ( kv->GetValue().Length() ) {
  1353. declManager->FindType( DECL_MATERIAL, kv->GetValue() );
  1354. }
  1355. kv = dict->MatchPrefix( "mtr", kv );
  1356. }
  1357. // handles hud icons
  1358. kv = dict->MatchPrefix( "inv_icon", NULL );
  1359. while ( kv ) {
  1360. if ( kv->GetValue().Length() ) {
  1361. declManager->FindType( DECL_MATERIAL, kv->GetValue() );
  1362. }
  1363. kv = dict->MatchPrefix( "inv_icon", kv );
  1364. }
  1365. // handles teleport fx.. this is not ideal but the actual decision on which fx to use
  1366. // is handled by script code based on the teleport number
  1367. kv = dict->MatchPrefix( "teleport", NULL );
  1368. if ( kv && kv->GetValue().Length() ) {
  1369. int teleportType = atoi( kv->GetValue() );
  1370. const char *p = ( teleportType ) ? va( "fx/teleporter%i.fx", teleportType ) : "fx/teleporter.fx";
  1371. declManager->FindType( DECL_FX, p );
  1372. }
  1373. kv = dict->MatchPrefix( "fx", NULL );
  1374. while( kv ) {
  1375. if ( kv->GetValue().Length() ) {
  1376. declManager->MediaPrint( "Precaching fx %s\n", kv->GetValue().c_str() );
  1377. declManager->FindType( DECL_FX, kv->GetValue() );
  1378. }
  1379. kv = dict->MatchPrefix( "fx", kv );
  1380. }
  1381. kv = dict->MatchPrefix( "smoke", NULL );
  1382. while( kv ) {
  1383. if ( kv->GetValue().Length() ) {
  1384. idStr prtName = kv->GetValue();
  1385. int dash = prtName.Find('-');
  1386. if ( dash > 0 ) {
  1387. prtName = prtName.Left( dash );
  1388. }
  1389. declManager->FindType( DECL_PARTICLE, prtName );
  1390. }
  1391. kv = dict->MatchPrefix( "smoke", kv );
  1392. }
  1393. kv = dict->MatchPrefix( "skin", NULL );
  1394. while( kv ) {
  1395. if ( kv->GetValue().Length() ) {
  1396. declManager->MediaPrint( "Precaching skin %s\n", kv->GetValue().c_str() );
  1397. declManager->FindType( DECL_SKIN, kv->GetValue() );
  1398. }
  1399. kv = dict->MatchPrefix( "skin", kv );
  1400. }
  1401. kv = dict->MatchPrefix( "def", NULL );
  1402. while( kv ) {
  1403. if ( kv->GetValue().Length() ) {
  1404. FindEntityDef( kv->GetValue().c_str(), false );
  1405. }
  1406. kv = dict->MatchPrefix( "def", kv );
  1407. }
  1408. kv = dict->MatchPrefix( "pda_name", NULL );
  1409. while( kv ) {
  1410. if ( kv->GetValue().Length() ) {
  1411. declManager->FindType( DECL_PDA, kv->GetValue().c_str(), false );
  1412. }
  1413. kv = dict->MatchPrefix( "pda_name", kv );
  1414. }
  1415. kv = dict->MatchPrefix( "video", NULL );
  1416. while( kv ) {
  1417. if ( kv->GetValue().Length() ) {
  1418. declManager->FindType( DECL_VIDEO, kv->GetValue().c_str(), false );
  1419. }
  1420. kv = dict->MatchPrefix( "video", kv );
  1421. }
  1422. kv = dict->MatchPrefix( "audio", NULL );
  1423. while( kv ) {
  1424. if ( kv->GetValue().Length() ) {
  1425. declManager->FindType( DECL_AUDIO, kv->GetValue().c_str(), false );
  1426. }
  1427. kv = dict->MatchPrefix( "audio", kv );
  1428. }
  1429. }
  1430. /*
  1431. ===========
  1432. idGameLocal::InitScriptForMap
  1433. ============
  1434. */
  1435. void idGameLocal::InitScriptForMap( void ) {
  1436. // create a thread to run frame commands on
  1437. frameCommandThread = new idThread();
  1438. frameCommandThread->ManualDelete();
  1439. frameCommandThread->SetThreadName( "frameCommands" );
  1440. // run the main game script function (not the level specific main)
  1441. const function_t *func = program.FindFunction( SCRIPT_DEFAULTFUNC );
  1442. if ( func != NULL ) {
  1443. idThread *thread = new idThread( func );
  1444. if ( thread->Start() ) {
  1445. // thread has finished executing, so delete it
  1446. delete thread;
  1447. }
  1448. }
  1449. }
  1450. /*
  1451. ===========
  1452. idGameLocal::SpawnPlayer
  1453. ============
  1454. */
  1455. void idGameLocal::SpawnPlayer( int clientNum ) {
  1456. idEntity *ent;
  1457. idDict args;
  1458. // they can connect
  1459. Printf( "SpawnPlayer: %i\n", clientNum );
  1460. args.SetInt( "spawn_entnum", clientNum );
  1461. args.Set( "name", va( "player%d", clientNum + 1 ) );
  1462. args.Set( "classname", isMultiplayer ? "player_doommarine_mp" : "player_doommarine" );
  1463. if ( !SpawnEntityDef( args, &ent ) || !entities[ clientNum ] ) {
  1464. Error( "Failed to spawn player as '%s'", args.GetString( "classname" ) );
  1465. }
  1466. // make sure it's a compatible class
  1467. if ( !ent->IsType( idPlayer::Type ) ) {
  1468. Error( "'%s' spawned the player as a '%s'. Player spawnclass must be a subclass of idPlayer.", args.GetString( "classname" ), ent->GetClassname() );
  1469. }
  1470. if ( clientNum >= numClients ) {
  1471. numClients = clientNum + 1;
  1472. }
  1473. mpGame.SpawnPlayer( clientNum );
  1474. }
  1475. /*
  1476. ================
  1477. idGameLocal::GetClientByNum
  1478. ================
  1479. */
  1480. idPlayer *idGameLocal::GetClientByNum( int current ) const {
  1481. if ( current < 0 || current >= numClients ) {
  1482. current = 0;
  1483. }
  1484. if ( entities[current] ) {
  1485. return static_cast<idPlayer *>( entities[ current ] );
  1486. }
  1487. return NULL;
  1488. }
  1489. /*
  1490. ================
  1491. idGameLocal::GetClientByName
  1492. ================
  1493. */
  1494. idPlayer *idGameLocal::GetClientByName( const char *name ) const {
  1495. int i;
  1496. idEntity *ent;
  1497. for ( i = 0 ; i < numClients ; i++ ) {
  1498. ent = entities[ i ];
  1499. if ( ent && ent->IsType( idPlayer::Type ) ) {
  1500. if ( idStr::IcmpNoColor( name, userInfo[ i ].GetString( "ui_name" ) ) == 0 ) {
  1501. return static_cast<idPlayer *>( ent );
  1502. }
  1503. }
  1504. }
  1505. return NULL;
  1506. }
  1507. /*
  1508. ================
  1509. idGameLocal::GetClientByCmdArgs
  1510. ================
  1511. */
  1512. idPlayer *idGameLocal::GetClientByCmdArgs( const idCmdArgs &args ) const {
  1513. idPlayer *player;
  1514. idStr client = args.Argv( 1 );
  1515. if ( !client.Length() ) {
  1516. return NULL;
  1517. }
  1518. // we don't allow numeric ui_name so this can't go wrong
  1519. if ( client.IsNumeric() ) {
  1520. player = GetClientByNum( atoi( client.c_str() ) );
  1521. } else {
  1522. player = GetClientByName( client.c_str() );
  1523. }
  1524. if ( !player ) {
  1525. common->Printf( "Player '%s' not found\n", client.c_str() );
  1526. }
  1527. return player;
  1528. }
  1529. /*
  1530. ================
  1531. idGameLocal::GetNextClientNum
  1532. ================
  1533. */
  1534. int idGameLocal::GetNextClientNum( int _current ) const {
  1535. int i, current;
  1536. current = 0;
  1537. for ( i = 0; i < numClients; i++) {
  1538. current = ( _current + i + 1 ) % numClients;
  1539. if ( entities[ current ] && entities[ current ]->IsType( idPlayer::Type ) ) {
  1540. return current;
  1541. }
  1542. }
  1543. return current;
  1544. }
  1545. /*
  1546. ================
  1547. idGameLocal::GetLocalPlayer
  1548. Nothing in the game tic should EVER make a decision based on what the
  1549. local client number is, it shouldn't even be aware that there is a
  1550. draw phase even happening. This just returns client 0, which will
  1551. be correct for single player.
  1552. ================
  1553. */
  1554. idPlayer *idGameLocal::GetLocalPlayer() const {
  1555. if ( localClientNum < 0 ) {
  1556. return NULL;
  1557. }
  1558. if ( !entities[ localClientNum ] || !entities[ localClientNum ]->IsType( idPlayer::Type ) ) {
  1559. // not fully in game yet
  1560. return NULL;
  1561. }
  1562. return static_cast<idPlayer *>( entities[ localClientNum ] );
  1563. }
  1564. /*
  1565. ================
  1566. idGameLocal::SetupClientPVS
  1567. ================
  1568. */
  1569. pvsHandle_t idGameLocal::GetClientPVS( idPlayer *player, pvsType_t type ) {
  1570. if ( player->GetPrivateCameraView() ) {
  1571. return pvs.SetupCurrentPVS( player->GetPrivateCameraView()->GetPVSAreas(), player->GetPrivateCameraView()->GetNumPVSAreas() );
  1572. } else if ( camera ) {
  1573. return pvs.SetupCurrentPVS( camera->GetPVSAreas(), camera->GetNumPVSAreas() );
  1574. } else {
  1575. return pvs.SetupCurrentPVS( player->GetPVSAreas(), player->GetNumPVSAreas() );
  1576. }
  1577. }
  1578. /*
  1579. ================
  1580. idGameLocal::SetupPlayerPVS
  1581. ================
  1582. */
  1583. void idGameLocal::SetupPlayerPVS( void ) {
  1584. int i;
  1585. idEntity * ent;
  1586. idPlayer * player;
  1587. pvsHandle_t otherPVS, newPVS;
  1588. playerPVS.i = -1;
  1589. for ( i = 0; i < numClients; i++ ) {
  1590. ent = entities[i];
  1591. if ( !ent || !ent->IsType( idPlayer::Type ) ) {
  1592. continue;
  1593. }
  1594. player = static_cast<idPlayer *>(ent);
  1595. if ( playerPVS.i == -1 ) {
  1596. playerPVS = GetClientPVS( player, PVS_NORMAL );
  1597. } else {
  1598. otherPVS = GetClientPVS( player, PVS_NORMAL );
  1599. newPVS = pvs.MergeCurrentPVS( playerPVS, otherPVS );
  1600. pvs.FreeCurrentPVS( playerPVS );
  1601. pvs.FreeCurrentPVS( otherPVS );
  1602. playerPVS = newPVS;
  1603. }
  1604. if ( playerConnectedAreas.i == -1 ) {
  1605. playerConnectedAreas = GetClientPVS( player, PVS_CONNECTED_AREAS );
  1606. } else {
  1607. otherPVS = GetClientPVS( player, PVS_CONNECTED_AREAS );
  1608. newPVS = pvs.MergeCurrentPVS( playerConnectedAreas, otherPVS );
  1609. pvs.FreeCurrentPVS( playerConnectedAreas );
  1610. pvs.FreeCurrentPVS( otherPVS );
  1611. playerConnectedAreas = newPVS;
  1612. }
  1613. }
  1614. }
  1615. /*
  1616. ================
  1617. idGameLocal::FreePlayerPVS
  1618. ================
  1619. */
  1620. void idGameLocal::FreePlayerPVS( void ) {
  1621. if ( playerPVS.i != -1 ) {
  1622. pvs.FreeCurrentPVS( playerPVS );
  1623. playerPVS.i = -1;
  1624. }
  1625. if ( playerConnectedAreas.i != -1 ) {
  1626. pvs.FreeCurrentPVS( playerConnectedAreas );
  1627. playerConnectedAreas.i = -1;
  1628. }
  1629. }
  1630. /*
  1631. ================
  1632. idGameLocal::InPlayerPVS
  1633. should only be called during entity thinking and event handling
  1634. ================
  1635. */
  1636. bool idGameLocal::InPlayerPVS( idEntity *ent ) const {
  1637. if ( playerPVS.i == -1 ) {
  1638. return false;
  1639. }
  1640. return pvs.InCurrentPVS( playerPVS, ent->GetPVSAreas(), ent->GetNumPVSAreas() );
  1641. }
  1642. /*
  1643. ================
  1644. idGameLocal::InPlayerConnectedArea
  1645. should only be called during entity thinking and event handling
  1646. ================
  1647. */
  1648. bool idGameLocal::InPlayerConnectedArea( idEntity *ent ) const {
  1649. if ( playerConnectedAreas.i == -1 ) {
  1650. return false;
  1651. }
  1652. return pvs.InCurrentPVS( playerConnectedAreas, ent->GetPVSAreas(), ent->GetNumPVSAreas() );
  1653. }
  1654. /*
  1655. ================
  1656. idGameLocal::UpdateGravity
  1657. ================
  1658. */
  1659. void idGameLocal::UpdateGravity( void ) {
  1660. idEntity *ent;
  1661. if ( g_gravity.IsModified() ) {
  1662. if ( g_gravity.GetFloat() == 0.0f ) {
  1663. g_gravity.SetFloat( 1.0f );
  1664. }
  1665. gravity.Set( 0, 0, -g_gravity.GetFloat() );
  1666. // update all physics objects
  1667. for( ent = spawnedEntities.Next(); ent != NULL; ent = ent->spawnNode.Next() ) {
  1668. if ( ent->IsType( idAFEntity_Generic::Type ) ) {
  1669. idPhysics *phys = ent->GetPhysics();
  1670. if ( phys ) {
  1671. phys->SetGravity( gravity );
  1672. }
  1673. }
  1674. }
  1675. g_gravity.ClearModified();
  1676. }
  1677. }
  1678. /*
  1679. ================
  1680. idGameLocal::GetGravity
  1681. ================
  1682. */
  1683. const idVec3 &idGameLocal::GetGravity( void ) const {
  1684. return gravity;
  1685. }
  1686. /*
  1687. ================
  1688. idGameLocal::SortActiveEntityList
  1689. Sorts the active entity list such that pushing entities come first,
  1690. actors come next and physics team slaves appear after their master.
  1691. ================
  1692. */
  1693. void idGameLocal::SortActiveEntityList( void ) {
  1694. idEntity *ent, *next_ent, *master, *part;
  1695. // if the active entity list needs to be reordered to place physics team masters at the front
  1696. if ( sortTeamMasters ) {
  1697. for ( ent = activeEntities.Next(); ent != NULL; ent = next_ent ) {
  1698. next_ent = ent->activeNode.Next();
  1699. master = ent->GetTeamMaster();
  1700. if ( master && master == ent ) {
  1701. ent->activeNode.Remove();
  1702. ent->activeNode.AddToFront( activeEntities );
  1703. }
  1704. }
  1705. }
  1706. // if the active entity list needs to be reordered to place pushers at the front
  1707. if ( sortPushers ) {
  1708. for ( ent = activeEntities.Next(); ent != NULL; ent = next_ent ) {
  1709. next_ent = ent->activeNode.Next();
  1710. master = ent->GetTeamMaster();
  1711. if ( !master || master == ent ) {
  1712. // check if there is an actor on the team
  1713. for ( part = ent; part != NULL; part = part->GetNextTeamEntity() ) {
  1714. if ( part->GetPhysics()->IsType( idPhysics_Actor::Type ) ) {
  1715. break;
  1716. }
  1717. }
  1718. // if there is an actor on the team
  1719. if ( part ) {
  1720. ent->activeNode.Remove();
  1721. ent->activeNode.AddToFront( activeEntities );
  1722. }
  1723. }
  1724. }
  1725. for ( ent = activeEntities.Next(); ent != NULL; ent = next_ent ) {
  1726. next_ent = ent->activeNode.Next();
  1727. master = ent->GetTeamMaster();
  1728. if ( !master || master == ent ) {
  1729. // check if there is an entity on the team using parametric physics
  1730. for ( part = ent; part != NULL; part = part->GetNextTeamEntity() ) {
  1731. if ( part->GetPhysics()->IsType( idPhysics_Parametric::Type ) ) {
  1732. break;
  1733. }
  1734. }
  1735. // if there is an entity on the team using parametric physics
  1736. if ( part ) {
  1737. ent->activeNode.Remove();
  1738. ent->activeNode.AddToFront( activeEntities );
  1739. }
  1740. }
  1741. }
  1742. }
  1743. sortTeamMasters = false;
  1744. sortPushers = false;
  1745. }
  1746. /*
  1747. ================
  1748. idGameLocal::RunFrame
  1749. ================
  1750. */
  1751. gameReturn_t idGameLocal::RunFrame( const usercmd_t *clientCmds ) {
  1752. idEntity * ent;
  1753. int num;
  1754. float ms;
  1755. idTimer timer_think, timer_events, timer_singlethink;
  1756. gameReturn_t ret;
  1757. idPlayer *player;
  1758. const renderView_t *view;
  1759. #ifdef _DEBUG
  1760. if ( isMultiplayer ) {
  1761. assert( !isClient );
  1762. }
  1763. #endif
  1764. player = GetLocalPlayer();
  1765. if ( !isMultiplayer && g_stopTime.GetBool() ) {
  1766. // clear any debug lines from a previous frame
  1767. gameRenderWorld->DebugClearLines( time + 1 );
  1768. // set the user commands for this frame
  1769. memcpy( usercmds, clientCmds, numClients * sizeof( usercmds[ 0 ] ) );
  1770. if ( player ) {
  1771. player->Think();
  1772. }
  1773. } else do {
  1774. // update the game time
  1775. framenum++;
  1776. previousTime = time;
  1777. time += msec;
  1778. realClientTime = time;
  1779. #ifdef GAME_DLL
  1780. // allow changing SIMD usage on the fly
  1781. if ( com_forceGenericSIMD.IsModified() ) {
  1782. idSIMD::InitProcessor( "game", com_forceGenericSIMD.GetBool() );
  1783. }
  1784. #endif
  1785. // make sure the random number counter is used each frame so random events
  1786. // are influenced by the player's actions
  1787. random.RandomInt();
  1788. if ( player ) {
  1789. // update the renderview so that any gui videos play from the right frame
  1790. view = player->GetRenderView();
  1791. if ( view ) {
  1792. gameRenderWorld->SetRenderView( view );
  1793. }
  1794. }
  1795. // clear any debug lines from a previous frame
  1796. gameRenderWorld->DebugClearLines( time );
  1797. // clear any debug polygons from a previous frame
  1798. gameRenderWorld->DebugClearPolygons( time );
  1799. // set the user commands for this frame
  1800. memcpy( usercmds, clientCmds, numClients * sizeof( usercmds[ 0 ] ) );
  1801. // free old smoke particles
  1802. smokeParticles->FreeSmokes();
  1803. // process events on the server
  1804. ServerProcessEntityNetworkEventQueue();
  1805. // update our gravity vector if needed.
  1806. UpdateGravity();
  1807. // create a merged pvs for all players
  1808. SetupPlayerPVS();
  1809. // sort the active entity list
  1810. SortActiveEntityList();
  1811. timer_think.Clear();
  1812. timer_think.Start();
  1813. // let entities think
  1814. if ( g_timeentities.GetFloat() ) {
  1815. num = 0;
  1816. for( ent = activeEntities.Next(); ent != NULL; ent = ent->activeNode.Next() ) {
  1817. if ( g_cinematic.GetBool() && inCinematic && !ent->cinematic ) {
  1818. ent->GetPhysics()->UpdateTime( time );
  1819. continue;
  1820. }
  1821. timer_singlethink.Clear();
  1822. timer_singlethink.Start();
  1823. ent->Think();
  1824. timer_singlethink.Stop();
  1825. ms = timer_singlethink.Milliseconds();
  1826. if ( ms >= g_timeentities.GetFloat() ) {
  1827. Printf( "%d: entity '%s': %.1f ms\n", time, ent->name.c_str(), ms );
  1828. }
  1829. num++;
  1830. }
  1831. } else {
  1832. if ( inCinematic ) {
  1833. num = 0;
  1834. for( ent = activeEntities.Next(); ent != NULL; ent = ent->activeNode.Next() ) {
  1835. if ( g_cinematic.GetBool() && !ent->cinematic ) {
  1836. ent->GetPhysics()->UpdateTime( time );
  1837. continue;
  1838. }
  1839. ent->Think();
  1840. num++;
  1841. }
  1842. } else {
  1843. num = 0;
  1844. for( ent = activeEntities.Next(); ent != NULL; ent = ent->activeNode.Next() ) {
  1845. ent->Think();
  1846. num++;
  1847. }
  1848. }
  1849. }
  1850. // remove any entities that have stopped thinking
  1851. if ( numEntitiesToDeactivate ) {
  1852. idEntity *next_ent;
  1853. int c = 0;
  1854. for( ent = activeEntities.Next(); ent != NULL; ent = next_ent ) {
  1855. next_ent = ent->activeNode.Next();
  1856. if ( !ent->thinkFlags ) {
  1857. ent->activeNode.Remove();
  1858. c++;
  1859. }
  1860. }
  1861. //assert( numEntitiesToDeactivate == c );
  1862. numEntitiesToDeactivate = 0;
  1863. }
  1864. timer_think.Stop();
  1865. timer_events.Clear();
  1866. timer_events.Start();
  1867. // service any pending events
  1868. idEvent::ServiceEvents();
  1869. timer_events.Stop();
  1870. // free the player pvs
  1871. FreePlayerPVS();
  1872. // do multiplayer related stuff
  1873. if ( isMultiplayer ) {
  1874. mpGame.Run();
  1875. }
  1876. // display how long it took to calculate the current game frame
  1877. if ( g_frametime.GetBool() ) {
  1878. Printf( "game %d: all:%.1f th:%.1f ev:%.1f %d ents \n",
  1879. time, timer_think.Milliseconds() + timer_events.Milliseconds(),
  1880. timer_think.Milliseconds(), timer_events.Milliseconds(), num );
  1881. }
  1882. // build the return value
  1883. ret.consistencyHash = 0;
  1884. ret.sessionCommand[0] = 0;
  1885. if ( !isMultiplayer && player ) {
  1886. ret.health = player->health;
  1887. ret.heartRate = player->heartRate;
  1888. ret.stamina = idMath::FtoiFast( player->stamina );
  1889. // combat is a 0-100 value based on lastHitTime and lastDmgTime
  1890. // each make up 50% of the time spread over 10 seconds
  1891. ret.combat = 0;
  1892. if ( player->lastDmgTime > 0 && time < player->lastDmgTime + 10000 ) {
  1893. ret.combat += 50.0f * (float) ( time - player->lastDmgTime ) / 10000;
  1894. }
  1895. if ( player->lastHitTime > 0 && time < player->lastHitTime + 10000 ) {
  1896. ret.combat += 50.0f * (float) ( time - player->lastHitTime ) / 10000;
  1897. }
  1898. }
  1899. // see if a target_sessionCommand has forced a changelevel
  1900. if ( sessionCommand.Length() ) {
  1901. strncpy( ret.sessionCommand, sessionCommand, sizeof( ret.sessionCommand ) );
  1902. break;
  1903. }
  1904. // make sure we don't loop forever when skipping a cinematic
  1905. if ( skipCinematic && ( time > cinematicMaxSkipTime ) ) {
  1906. Warning( "Exceeded maximum cinematic skip length. Cinematic may be looping infinitely." );
  1907. skipCinematic = false;
  1908. break;
  1909. }
  1910. } while( ( inCinematic || ( time < cinematicStopTime ) ) && skipCinematic );
  1911. ret.syncNextGameFrame = skipCinematic;
  1912. if ( skipCinematic ) {
  1913. soundSystem->SetMute( false );
  1914. skipCinematic = false;
  1915. }
  1916. // show any debug info for this frame
  1917. RunDebugInfo();
  1918. D_DrawDebugLines();
  1919. return ret;
  1920. }
  1921. /*
  1922. ======================================================================
  1923. Game view drawing
  1924. ======================================================================
  1925. */
  1926. /*
  1927. ====================
  1928. idGameLocal::CalcFov
  1929. Calculates the horizontal and vertical field of view based on a horizontal field of view and custom aspect ratio
  1930. ====================
  1931. */
  1932. void idGameLocal::CalcFov( float base_fov, float &fov_x, float &fov_y ) const {
  1933. float x;
  1934. float y;
  1935. float ratio_x;
  1936. float ratio_y;
  1937. if ( !sys->FPU_StackIsEmpty() ) {
  1938. Printf( sys->FPU_GetState() );
  1939. Error( "idGameLocal::CalcFov: FPU stack not empty" );
  1940. }
  1941. // first, calculate the vertical fov based on a 640x480 view
  1942. x = 640.0f / tan( base_fov / 360.0f * idMath::PI );
  1943. y = atan2( 480.0f, x );
  1944. fov_y = y * 360.0f / idMath::PI;
  1945. // FIXME: somehow, this is happening occasionally
  1946. assert( fov_y > 0 );
  1947. if ( fov_y <= 0 ) {
  1948. Printf( sys->FPU_GetState() );
  1949. Error( "idGameLocal::CalcFov: bad result" );
  1950. }
  1951. switch( r_aspectRatio.GetInteger() ) {
  1952. default :
  1953. case 0 :
  1954. // 4:3
  1955. fov_x = base_fov;
  1956. return;
  1957. break;
  1958. case 1 :
  1959. // 16:9
  1960. ratio_x = 16.0f;
  1961. ratio_y = 9.0f;
  1962. break;
  1963. case 2 :
  1964. // 16:10
  1965. ratio_x = 16.0f;
  1966. ratio_y = 10.0f;
  1967. break;
  1968. }
  1969. y = ratio_y / tan( fov_y / 360.0f * idMath::PI );
  1970. fov_x = atan2( ratio_x, y ) * 360.0f / idMath::PI;
  1971. if ( fov_x < base_fov ) {
  1972. fov_x = base_fov;
  1973. x = ratio_x / tan( fov_x / 360.0f * idMath::PI );
  1974. fov_y = atan2( ratio_y, x ) * 360.0f / idMath::PI;
  1975. }
  1976. // FIXME: somehow, this is happening occasionally
  1977. assert( ( fov_x > 0 ) && ( fov_y > 0 ) );
  1978. if ( ( fov_y <= 0 ) || ( fov_x <= 0 ) ) {
  1979. Printf( sys->FPU_GetState() );
  1980. Error( "idGameLocal::CalcFov: bad result" );
  1981. }
  1982. }
  1983. /*
  1984. ================
  1985. idGameLocal::Draw
  1986. makes rendering and sound system calls
  1987. ================
  1988. */
  1989. bool idGameLocal::Draw( int clientNum ) {
  1990. if ( isMultiplayer ) {
  1991. return mpGame.Draw( clientNum );
  1992. }
  1993. idPlayer *player = static_cast<idPlayer *>(entities[ clientNum ]);
  1994. if ( !player ) {
  1995. return false;
  1996. }
  1997. // render the scene
  1998. player->playerView.RenderPlayerView( player->hud );
  1999. return true;
  2000. }
  2001. /*
  2002. ================
  2003. idGameLocal::HandleESC
  2004. ================
  2005. */
  2006. escReply_t idGameLocal::HandleESC( idUserInterface **gui ) {
  2007. if ( isMultiplayer ) {
  2008. *gui = StartMenu();
  2009. // we may set the gui back to NULL to hide it
  2010. return ESC_GUI;
  2011. }
  2012. idPlayer *player = GetLocalPlayer();
  2013. if ( player ) {
  2014. if ( player->HandleESC() ) {
  2015. return ESC_IGNORE;
  2016. } else {
  2017. return ESC_MAIN;
  2018. }
  2019. }
  2020. return ESC_MAIN;
  2021. }
  2022. /*
  2023. ================
  2024. idGameLocal::StartMenu
  2025. ================
  2026. */
  2027. idUserInterface* idGameLocal::StartMenu( void ) {
  2028. if ( !isMultiplayer ) {
  2029. return NULL;
  2030. }
  2031. return mpGame.StartMenu();
  2032. }
  2033. /*
  2034. ================
  2035. idGameLocal::HandleGuiCommands
  2036. ================
  2037. */
  2038. const char* idGameLocal::HandleGuiCommands( const char *menuCommand ) {
  2039. if ( !isMultiplayer ) {
  2040. return NULL;
  2041. }
  2042. return mpGame.HandleGuiCommands( menuCommand );
  2043. }
  2044. /*
  2045. ================
  2046. idGameLocal::HandleMainMenuCommands
  2047. ================
  2048. */
  2049. void idGameLocal::HandleMainMenuCommands( const char *menuCommand, idUserInterface *gui ) { }
  2050. /*
  2051. ================
  2052. idGameLocal::GetLevelMap
  2053. should only be used for in-game level editing
  2054. ================
  2055. */
  2056. idMapFile *idGameLocal::GetLevelMap( void ) {
  2057. if ( mapFile && mapFile->HasPrimitiveData()) {
  2058. return mapFile;
  2059. }
  2060. if ( !mapFileName.Length() ) {
  2061. return NULL;
  2062. }
  2063. if ( mapFile ) {
  2064. delete mapFile;
  2065. }
  2066. mapFile = new idMapFile;
  2067. if ( !mapFile->Parse( mapFileName ) ) {
  2068. delete mapFile;
  2069. mapFile = NULL;
  2070. }
  2071. return mapFile;
  2072. }
  2073. /*
  2074. ================
  2075. idGameLocal::GetMapName
  2076. ================
  2077. */
  2078. const char *idGameLocal::GetMapName( void ) const {
  2079. return mapFileName.c_str();
  2080. }
  2081. /*
  2082. ================
  2083. idGameLocal::CallFrameCommand
  2084. ================
  2085. */
  2086. void idGameLocal::CallFrameCommand( idEntity *ent, const function_t *frameCommand ) {
  2087. frameCommandThread->CallFunction( ent, frameCommand, true );
  2088. frameCommandThread->Execute();
  2089. }
  2090. /*
  2091. ================
  2092. idGameLocal::CallObjectFrameCommand
  2093. ================
  2094. */
  2095. void idGameLocal::CallObjectFrameCommand( idEntity *ent, const char *frameCommand ) {
  2096. const function_t *func;
  2097. func = ent->scriptObject.GetFunction( frameCommand );
  2098. if ( !func ) {
  2099. if ( !ent->IsType( idTestModel::Type ) ) {
  2100. Error( "Unknown function '%s' called for frame command on entity '%s'", frameCommand, ent->name.c_str() );
  2101. }
  2102. } else {
  2103. frameCommandThread->CallFunction( ent, func, true );
  2104. frameCommandThread->Execute();
  2105. }
  2106. }
  2107. /*
  2108. ================
  2109. idGameLocal::ShowTargets
  2110. ================
  2111. */
  2112. void idGameLocal::ShowTargets( void ) {
  2113. idMat3 axis = GetLocalPlayer()->viewAngles.ToMat3();
  2114. idVec3 up = axis[ 2 ] * 5.0f;
  2115. const idVec3 &viewPos = GetLocalPlayer()->GetPhysics()->GetOrigin();
  2116. idBounds viewTextBounds( viewPos );
  2117. idBounds viewBounds( viewPos );
  2118. idBounds box( idVec3( -4.0f, -4.0f, -4.0f ), idVec3( 4.0f, 4.0f, 4.0f ) );
  2119. idEntity *ent;
  2120. idEntity *target;
  2121. int i;
  2122. idBounds totalBounds;
  2123. viewTextBounds.ExpandSelf( 128.0f );
  2124. viewBounds.ExpandSelf( 512.0f );
  2125. for( ent = spawnedEntities.Next(); ent != NULL; ent = ent->spawnNode.Next() ) {
  2126. totalBounds = ent->GetPhysics()->GetAbsBounds();
  2127. for( i = 0; i < ent->targets.Num(); i++ ) {
  2128. target = ent->targets[ i ].GetEntity();
  2129. if ( target ) {
  2130. totalBounds.AddBounds( target->GetPhysics()->GetAbsBounds() );
  2131. }
  2132. }
  2133. if ( !viewBounds.IntersectsBounds( totalBounds ) ) {
  2134. continue;
  2135. }
  2136. float dist;
  2137. idVec3 dir = totalBounds.GetCenter() - viewPos;
  2138. dir.NormalizeFast();
  2139. totalBounds.RayIntersection( viewPos, dir, dist );
  2140. float frac = ( 512.0f - dist ) / 512.0f;
  2141. if ( frac < 0.0f ) {
  2142. continue;
  2143. }
  2144. gameRenderWorld->DebugBounds( ( ent->IsHidden() ? colorLtGrey : colorOrange ) * frac, ent->GetPhysics()->GetAbsBounds() );
  2145. if ( viewTextBounds.IntersectsBounds( ent->GetPhysics()->GetAbsBounds() ) ) {
  2146. idVec3 center = ent->GetPhysics()->GetAbsBounds().GetCenter();
  2147. gameRenderWorld->DrawText( ent->name.c_str(), center - up, 0.1f, colorWhite * frac, axis, 1 );
  2148. gameRenderWorld->DrawText( ent->GetEntityDefName(), center, 0.1f, colorWhite * frac, axis, 1 );
  2149. gameRenderWorld->DrawText( va( "#%d", ent->entityNumber ), center + up, 0.1f, colorWhite * frac, axis, 1 );
  2150. }
  2151. for( i = 0; i < ent->targets.Num(); i++ ) {
  2152. target = ent->targets[ i ].GetEntity();
  2153. if ( target ) {
  2154. gameRenderWorld->DebugArrow( colorYellow * frac, ent->GetPhysics()->GetAbsBounds().GetCenter(), target->GetPhysics()->GetOrigin(), 10, 0 );
  2155. gameRenderWorld->DebugBounds( colorGreen * frac, box, target->GetPhysics()->GetOrigin() );
  2156. }
  2157. }
  2158. }
  2159. }
  2160. /*
  2161. ================
  2162. idGameLocal::RunDebugInfo
  2163. ================
  2164. */
  2165. void idGameLocal::RunDebugInfo( void ) {
  2166. idEntity *ent;
  2167. idPlayer *player;
  2168. player = GetLocalPlayer();
  2169. if ( !player ) {
  2170. return;
  2171. }
  2172. const idVec3 &origin = player->GetPhysics()->GetOrigin();
  2173. if ( g_showEntityInfo.GetBool() ) {
  2174. idMat3 axis = player->viewAngles.ToMat3();
  2175. idVec3 up = axis[ 2 ] * 5.0f;
  2176. idBounds viewTextBounds( origin );
  2177. idBounds viewBounds( origin );
  2178. viewTextBounds.ExpandSelf( 128.0f );
  2179. viewBounds.ExpandSelf( 512.0f );
  2180. for( ent = spawnedEntities.Next(); ent != NULL; ent = ent->spawnNode.Next() ) {
  2181. // don't draw the worldspawn
  2182. if ( ent == world ) {
  2183. continue;
  2184. }
  2185. // skip if the entity is very far away
  2186. if ( !viewBounds.IntersectsBounds( ent->GetPhysics()->GetAbsBounds() ) ) {
  2187. continue;
  2188. }
  2189. const idBounds &entBounds = ent->GetPhysics()->GetAbsBounds();
  2190. int contents = ent->GetPhysics()->GetContents();
  2191. if ( contents & CONTENTS_BODY ) {
  2192. gameRenderWorld->DebugBounds( colorCyan, entBounds );
  2193. } else if ( contents & CONTENTS_TRIGGER ) {
  2194. gameRenderWorld->DebugBounds( colorOrange, entBounds );
  2195. } else if ( contents & CONTENTS_SOLID ) {
  2196. gameRenderWorld->DebugBounds( colorGreen, entBounds );
  2197. } else {
  2198. if ( !entBounds.GetVolume() ) {
  2199. gameRenderWorld->DebugBounds( colorMdGrey, entBounds.Expand( 8.0f ) );
  2200. } else {
  2201. gameRenderWorld->DebugBounds( colorMdGrey, entBounds );
  2202. }
  2203. }
  2204. if ( viewTextBounds.IntersectsBounds( entBounds ) ) {
  2205. gameRenderWorld->DrawText( ent->name.c_str(), entBounds.GetCenter(), 0.1f, colorWhite, axis, 1 );
  2206. gameRenderWorld->DrawText( va( "#%d", ent->entityNumber ), entBounds.GetCenter() + up, 0.1f, colorWhite, axis, 1 );
  2207. }
  2208. }
  2209. }
  2210. // debug tool to draw bounding boxes around active entities
  2211. if ( g_showActiveEntities.GetBool() ) {
  2212. for( ent = activeEntities.Next(); ent != NULL; ent = ent->activeNode.Next() ) {
  2213. idBounds b = ent->GetPhysics()->GetBounds();
  2214. if ( b.GetVolume() <= 0 ) {
  2215. b[0][0] = b[0][1] = b[0][2] = -8;
  2216. b[1][0] = b[1][1] = b[1][2] = 8;
  2217. }
  2218. if ( ent->fl.isDormant ) {
  2219. gameRenderWorld->DebugBounds( colorYellow, b, ent->GetPhysics()->GetOrigin() );
  2220. } else {
  2221. gameRenderWorld->DebugBounds( colorGreen, b, ent->GetPhysics()->GetOrigin() );
  2222. }
  2223. }
  2224. }
  2225. if ( g_showTargets.GetBool() ) {
  2226. ShowTargets();
  2227. }
  2228. if ( g_showTriggers.GetBool() ) {
  2229. idTrigger::DrawDebugInfo();
  2230. }
  2231. if ( ai_showCombatNodes.GetBool() ) {
  2232. idCombatNode::DrawDebugInfo();
  2233. }
  2234. if ( ai_showPaths.GetBool() ) {
  2235. idPathCorner::DrawDebugInfo();
  2236. }
  2237. if ( g_editEntityMode.GetBool() ) {
  2238. editEntities->DisplayEntities();
  2239. }
  2240. if ( g_showCollisionWorld.GetBool() ) {
  2241. collisionModelManager->DrawModel( 0, vec3_origin, mat3_identity, origin, 128.0f );
  2242. }
  2243. if ( g_showCollisionModels.GetBool() ) {
  2244. clip.DrawClipModels( player->GetEyePosition(), g_maxShowDistance.GetFloat(), pm_thirdPerson.GetBool() ? NULL : player );
  2245. }
  2246. if ( g_showCollisionTraces.GetBool() ) {
  2247. clip.PrintStatistics();
  2248. }
  2249. if ( g_showPVS.GetInteger() ) {
  2250. pvs.DrawPVS( origin, ( g_showPVS.GetInteger() == 2 ) ? PVS_ALL_PORTALS_OPEN : PVS_NORMAL );
  2251. }
  2252. if ( aas_test.GetInteger() >= 0 ) {
  2253. idAAS *aas = GetAAS( aas_test.GetInteger() );
  2254. if ( aas ) {
  2255. aas->Test( origin );
  2256. if ( ai_testPredictPath.GetBool() ) {
  2257. idVec3 velocity;
  2258. predictedPath_t path;
  2259. velocity.x = cos( DEG2RAD( player->viewAngles.yaw ) ) * 100.0f;
  2260. velocity.y = sin( DEG2RAD( player->viewAngles.yaw ) ) * 100.0f;
  2261. velocity.z = 0.0f;
  2262. idAI::PredictPath( player, aas, origin, velocity, 1000, 100, SE_ENTER_OBSTACLE | SE_BLOCKED | SE_ENTER_LEDGE_AREA, path );
  2263. }
  2264. }
  2265. }
  2266. if ( ai_showObstacleAvoidance.GetInteger() == 2 ) {
  2267. idAAS *aas = GetAAS( 0 );
  2268. if ( aas ) {
  2269. idVec3 seekPos;
  2270. obstaclePath_t path;
  2271. seekPos = player->GetPhysics()->GetOrigin() + player->viewAxis[0] * 200.0f;
  2272. idAI::FindPathAroundObstacles( player->GetPhysics(), aas, NULL, player->GetPhysics()->GetOrigin(), seekPos, path );
  2273. }
  2274. }
  2275. // collision map debug output
  2276. collisionModelManager->DebugOutput( player->GetEyePosition() );
  2277. }
  2278. /*
  2279. ==================
  2280. idGameLocal::NumAAS
  2281. ==================
  2282. */
  2283. int idGameLocal::NumAAS( void ) const {
  2284. return aasList.Num();
  2285. }
  2286. /*
  2287. ==================
  2288. idGameLocal::GetAAS
  2289. ==================
  2290. */
  2291. idAAS *idGameLocal::GetAAS( int num ) const {
  2292. if ( ( num >= 0 ) && ( num < aasList.Num() ) ) {
  2293. if ( aasList[ num ] && aasList[ num ]->GetSettings() ) {
  2294. return aasList[ num ];
  2295. }
  2296. }
  2297. return NULL;
  2298. }
  2299. /*
  2300. ==================
  2301. idGameLocal::GetAAS
  2302. ==================
  2303. */
  2304. idAAS *idGameLocal::GetAAS( const char *name ) const {
  2305. int i;
  2306. for ( i = 0; i < aasNames.Num(); i++ ) {
  2307. if ( aasNames[ i ] == name ) {
  2308. if ( !aasList[ i ]->GetSettings() ) {
  2309. return NULL;
  2310. } else {
  2311. return aasList[ i ];
  2312. }
  2313. }
  2314. }
  2315. return NULL;
  2316. }
  2317. /*
  2318. ==================
  2319. idGameLocal::SetAASAreaState
  2320. ==================
  2321. */
  2322. void idGameLocal::SetAASAreaState( const idBounds &bounds, const int areaContents, bool closed ) {
  2323. int i;
  2324. for( i = 0; i < aasList.Num(); i++ ) {
  2325. aasList[ i ]->SetAreaState( bounds, areaContents, closed );
  2326. }
  2327. }
  2328. /*
  2329. ==================
  2330. idGameLocal::AddAASObstacle
  2331. ==================
  2332. */
  2333. aasHandle_t idGameLocal::AddAASObstacle( const idBounds &bounds ) {
  2334. int i;
  2335. aasHandle_t obstacle;
  2336. aasHandle_t check;
  2337. if ( !aasList.Num() ) {
  2338. return -1;
  2339. }
  2340. obstacle = aasList[ 0 ]->AddObstacle( bounds );
  2341. for( i = 1; i < aasList.Num(); i++ ) {
  2342. check = aasList[ i ]->AddObstacle( bounds );
  2343. assert( check == obstacle );
  2344. }
  2345. return obstacle;
  2346. }
  2347. /*
  2348. ==================
  2349. idGameLocal::RemoveAASObstacle
  2350. ==================
  2351. */
  2352. void idGameLocal::RemoveAASObstacle( const aasHandle_t handle ) {
  2353. int i;
  2354. for( i = 0; i < aasList.Num(); i++ ) {
  2355. aasList[ i ]->RemoveObstacle( handle );
  2356. }
  2357. }
  2358. /*
  2359. ==================
  2360. idGameLocal::RemoveAllAASObstacles
  2361. ==================
  2362. */
  2363. void idGameLocal::RemoveAllAASObstacles( void ) {
  2364. int i;
  2365. for( i = 0; i < aasList.Num(); i++ ) {
  2366. aasList[ i ]->RemoveAllObstacles();
  2367. }
  2368. }
  2369. /*
  2370. ==================
  2371. idGameLocal::CheatsOk
  2372. ==================
  2373. */
  2374. bool idGameLocal::CheatsOk( bool requirePlayer ) {
  2375. idPlayer *player;
  2376. if ( isMultiplayer && !cvarSystem->GetCVarBool( "net_allowCheats" ) ) {
  2377. Printf( "Not allowed in multiplayer.\n" );
  2378. return false;
  2379. }
  2380. if ( developer.GetBool() ) {
  2381. return true;
  2382. }
  2383. player = GetLocalPlayer();
  2384. if ( !requirePlayer || ( player && ( player->health > 0 ) ) ) {
  2385. return true;
  2386. }
  2387. Printf( "You must be alive to use this command.\n" );
  2388. return false;
  2389. }
  2390. /*
  2391. ===================
  2392. idGameLocal::RegisterEntity
  2393. ===================
  2394. */
  2395. void idGameLocal::RegisterEntity( idEntity *ent ) {
  2396. int spawn_entnum;
  2397. if ( spawnCount >= ( 1 << ( 32 - GENTITYNUM_BITS ) ) ) {
  2398. Error( "idGameLocal::RegisterEntity: spawn count overflow" );
  2399. }
  2400. if ( !spawnArgs.GetInt( "spawn_entnum", "0", spawn_entnum ) ) {
  2401. while( entities[firstFreeIndex] && firstFreeIndex < ENTITYNUM_MAX_NORMAL ) {
  2402. firstFreeIndex++;
  2403. }
  2404. if ( firstFreeIndex >= ENTITYNUM_MAX_NORMAL ) {
  2405. Error( "no free entities" );
  2406. }
  2407. spawn_entnum = firstFreeIndex++;
  2408. }
  2409. entities[ spawn_entnum ] = ent;
  2410. spawnIds[ spawn_entnum ] = spawnCount++;
  2411. ent->entityNumber = spawn_entnum;
  2412. ent->spawnNode.AddToEnd( spawnedEntities );
  2413. ent->spawnArgs.TransferKeyValues( spawnArgs );
  2414. if ( spawn_entnum >= num_entities ) {
  2415. num_entities++;
  2416. }
  2417. }
  2418. /*
  2419. ===================
  2420. idGameLocal::UnregisterEntity
  2421. ===================
  2422. */
  2423. void idGameLocal::UnregisterEntity( idEntity *ent ) {
  2424. assert( ent );
  2425. if ( editEntities ) {
  2426. editEntities->RemoveSelectedEntity( ent );
  2427. }
  2428. if ( ( ent->entityNumber != ENTITYNUM_NONE ) && ( entities[ ent->entityNumber ] == ent ) ) {
  2429. ent->spawnNode.Remove();
  2430. entities[ ent->entityNumber ] = NULL;
  2431. spawnIds[ ent->entityNumber ] = -1;
  2432. if ( ent->entityNumber >= MAX_CLIENTS && ent->entityNumber < firstFreeIndex ) {
  2433. firstFreeIndex = ent->entityNumber;
  2434. }
  2435. ent->entityNumber = ENTITYNUM_NONE;
  2436. }
  2437. }
  2438. /*
  2439. ================
  2440. idGameLocal::SpawnEntityType
  2441. ================
  2442. */
  2443. idEntity *idGameLocal::SpawnEntityType( const idTypeInfo &classdef, const idDict *args, bool bIsClientReadSnapshot ) {
  2444. idClass *obj;
  2445. #if _DEBUG
  2446. if ( isClient ) {
  2447. assert( bIsClientReadSnapshot );
  2448. }
  2449. #endif
  2450. if ( !classdef.IsType( idEntity::Type ) ) {
  2451. Error( "Attempted to spawn non-entity class '%s'", classdef.classname );
  2452. }
  2453. try {
  2454. if ( args ) {
  2455. spawnArgs = *args;
  2456. } else {
  2457. spawnArgs.Clear();
  2458. }
  2459. obj = classdef.CreateInstance();
  2460. obj->CallSpawn();
  2461. }
  2462. catch( idAllocError & ) {
  2463. obj = NULL;
  2464. }
  2465. spawnArgs.Clear();
  2466. return static_cast<idEntity *>(obj);
  2467. }
  2468. /*
  2469. ===================
  2470. idGameLocal::SpawnEntityDef
  2471. Finds the spawn function for the entity and calls it,
  2472. returning false if not found
  2473. ===================
  2474. */
  2475. bool idGameLocal::SpawnEntityDef( const idDict &args, idEntity **ent, bool setDefaults ) {
  2476. const char *classname;
  2477. const char *spawn;
  2478. idTypeInfo *cls;
  2479. idClass *obj;
  2480. idStr error;
  2481. const char *name;
  2482. if ( ent ) {
  2483. *ent = NULL;
  2484. }
  2485. spawnArgs = args;
  2486. if ( spawnArgs.GetString( "name", "", &name ) ) {
  2487. sprintf( error, " on '%s'", name);
  2488. }
  2489. spawnArgs.GetString( "classname", NULL, &classname );
  2490. const idDeclEntityDef *def = FindEntityDef( classname, false );
  2491. if ( !def ) {
  2492. Warning( "Unknown classname '%s'%s.", classname, error.c_str() );
  2493. return false;
  2494. }
  2495. spawnArgs.SetDefaults( &def->dict );
  2496. // check if we should spawn a class object
  2497. spawnArgs.GetString( "spawnclass", NULL, &spawn );
  2498. if ( spawn ) {
  2499. cls = idClass::GetClass( spawn );
  2500. if ( !cls ) {
  2501. Warning( "Could not spawn '%s'. Class '%s' not found%s.", classname, spawn, error.c_str() );
  2502. return false;
  2503. }
  2504. obj = cls->CreateInstance();
  2505. if ( !obj ) {
  2506. Warning( "Could not spawn '%s'. Instance could not be created%s.", classname, error.c_str() );
  2507. return false;
  2508. }
  2509. obj->CallSpawn();
  2510. if ( ent && obj->IsType( idEntity::Type ) ) {
  2511. *ent = static_cast<idEntity *>(obj);
  2512. }
  2513. return true;
  2514. }
  2515. // check if we should call a script function to spawn
  2516. spawnArgs.GetString( "spawnfunc", NULL, &spawn );
  2517. if ( spawn ) {
  2518. const function_t *func = program.FindFunction( spawn );
  2519. if ( !func ) {
  2520. Warning( "Could not spawn '%s'. Script function '%s' not found%s.", classname, spawn, error.c_str() );
  2521. return false;
  2522. }
  2523. idThread *thread = new idThread( func );
  2524. thread->DelayedStart( 0 );
  2525. return true;
  2526. }
  2527. Warning( "%s doesn't include a spawnfunc or spawnclass%s.", classname, error.c_str() );
  2528. return false;
  2529. }
  2530. /*
  2531. ================
  2532. idGameLocal::FindEntityDef
  2533. ================
  2534. */
  2535. const idDeclEntityDef *idGameLocal::FindEntityDef( const char *name, bool makeDefault ) const {
  2536. const idDecl *decl = NULL;
  2537. if ( isMultiplayer ) {
  2538. decl = declManager->FindType( DECL_ENTITYDEF, va( "%s_mp", name ), false );
  2539. }
  2540. if ( !decl ) {
  2541. decl = declManager->FindType( DECL_ENTITYDEF, name, makeDefault );
  2542. }
  2543. return static_cast<const idDeclEntityDef *>( decl );
  2544. }
  2545. /*
  2546. ================
  2547. idGameLocal::FindEntityDefDict
  2548. ================
  2549. */
  2550. const idDict *idGameLocal::FindEntityDefDict( const char *name, bool makeDefault ) const {
  2551. const idDeclEntityDef *decl = FindEntityDef( name, makeDefault );
  2552. return decl ? &decl->dict : NULL;
  2553. }
  2554. /*
  2555. ================
  2556. idGameLocal::InhibitEntitySpawn
  2557. ================
  2558. */
  2559. bool idGameLocal::InhibitEntitySpawn( idDict &spawnArgs ) {
  2560. bool result = false;
  2561. if ( isMultiplayer ) {
  2562. spawnArgs.GetBool( "not_multiplayer", "0", result );
  2563. } else if ( g_skill.GetInteger() == 0 ) {
  2564. spawnArgs.GetBool( "not_easy", "0", result );
  2565. } else if ( g_skill.GetInteger() == 1 ) {
  2566. spawnArgs.GetBool( "not_medium", "0", result );
  2567. } else {
  2568. spawnArgs.GetBool( "not_hard", "0", result );
  2569. }
  2570. const char *name;
  2571. #ifndef ID_DEMO_BUILD
  2572. if ( g_skill.GetInteger() == 3 ) {
  2573. name = spawnArgs.GetString( "classname" );
  2574. if ( idStr::Icmp( name, "item_medkit" ) == 0 || idStr::Icmp( name, "item_medkit_small" ) == 0 ) {
  2575. result = true;
  2576. }
  2577. }
  2578. #endif
  2579. if ( gameLocal.isMultiplayer ) {
  2580. name = spawnArgs.GetString( "classname" );
  2581. if ( idStr::Icmp( name, "weapon_bfg" ) == 0 || idStr::Icmp( name, "weapon_soulcube" ) == 0 ) {
  2582. result = true;
  2583. }
  2584. }
  2585. return result;
  2586. }
  2587. /*
  2588. ================
  2589. idGameLocal::SetSkill
  2590. ================
  2591. */
  2592. void idGameLocal::SetSkill( int value ) {
  2593. int skill_level;
  2594. if ( value < 0 ) {
  2595. skill_level = 0;
  2596. } else if ( value > 3 ) {
  2597. skill_level = 3;
  2598. } else {
  2599. skill_level = value;
  2600. }
  2601. g_skill.SetInteger( skill_level );
  2602. }
  2603. /*
  2604. ==============
  2605. idGameLocal::GameState
  2606. Used to allow entities to know if they're being spawned during the initial spawn.
  2607. ==============
  2608. */
  2609. gameState_t idGameLocal::GameState( void ) const {
  2610. return gamestate;
  2611. }
  2612. /*
  2613. ==============
  2614. idGameLocal::SpawnMapEntities
  2615. Parses textual entity definitions out of an entstring and spawns gentities.
  2616. ==============
  2617. */
  2618. void idGameLocal::SpawnMapEntities( void ) {
  2619. int i;
  2620. int num;
  2621. int inhibit;
  2622. idMapEntity *mapEnt;
  2623. int numEntities;
  2624. idDict args;
  2625. Printf( "Spawning entities\n" );
  2626. if ( mapFile == NULL ) {
  2627. Printf("No mapfile present\n");
  2628. return;
  2629. }
  2630. SetSkill( g_skill.GetInteger() );
  2631. numEntities = mapFile->GetNumEntities();
  2632. if ( numEntities == 0 ) {
  2633. Error( "...no entities" );
  2634. }
  2635. // the worldspawn is a special that performs any global setup
  2636. // needed by a level
  2637. mapEnt = mapFile->GetEntity( 0 );
  2638. args = mapEnt->epairs;
  2639. args.SetInt( "spawn_entnum", ENTITYNUM_WORLD );
  2640. if ( !SpawnEntityDef( args ) || !entities[ ENTITYNUM_WORLD ] || !entities[ ENTITYNUM_WORLD ]->IsType( idWorldspawn::Type ) ) {
  2641. Error( "Problem spawning world entity" );
  2642. }
  2643. num = 1;
  2644. inhibit = 0;
  2645. for ( i = 1 ; i < numEntities ; i++ ) {
  2646. mapEnt = mapFile->GetEntity( i );
  2647. args = mapEnt->epairs;
  2648. if ( !InhibitEntitySpawn( args ) ) {
  2649. // precache any media specified in the map entity
  2650. CacheDictionaryMedia( &args );
  2651. SpawnEntityDef( args );
  2652. num++;
  2653. } else {
  2654. inhibit++;
  2655. }
  2656. }
  2657. Printf( "...%i entities spawned, %i inhibited\n\n", num, inhibit );
  2658. }
  2659. /*
  2660. ================
  2661. idGameLocal::AddEntityToHash
  2662. ================
  2663. */
  2664. void idGameLocal::AddEntityToHash( const char *name, idEntity *ent ) {
  2665. if ( FindEntity( name ) ) {
  2666. Error( "Multiple entities named '%s'", name );
  2667. }
  2668. entityHash.Add( entityHash.GenerateKey( name, true ), ent->entityNumber );
  2669. }
  2670. /*
  2671. ================
  2672. idGameLocal::RemoveEntityFromHash
  2673. ================
  2674. */
  2675. bool idGameLocal::RemoveEntityFromHash( const char *name, idEntity *ent ) {
  2676. int hash, i;
  2677. hash = entityHash.GenerateKey( name, true );
  2678. for ( i = entityHash.First( hash ); i != -1; i = entityHash.Next( i ) ) {
  2679. if ( entities[i] && entities[i] == ent && entities[i]->name.Icmp( name ) == 0 ) {
  2680. entityHash.Remove( hash, i );
  2681. return true;
  2682. }
  2683. }
  2684. return false;
  2685. }
  2686. /*
  2687. ================
  2688. idGameLocal::GetTargets
  2689. ================
  2690. */
  2691. int idGameLocal::GetTargets( const idDict &args, idList< idEntityPtr<idEntity> > &list, const char *ref ) const {
  2692. int i, num, refLength;
  2693. const idKeyValue *arg;
  2694. idEntity *ent;
  2695. list.Clear();
  2696. refLength = strlen( ref );
  2697. num = args.GetNumKeyVals();
  2698. for( i = 0; i < num; i++ ) {
  2699. arg = args.GetKeyVal( i );
  2700. if ( arg->GetKey().Icmpn( ref, refLength ) == 0 ) {
  2701. ent = FindEntity( arg->GetValue() );
  2702. if ( ent ) {
  2703. idEntityPtr<idEntity> &entityPtr = list.Alloc();
  2704. entityPtr = ent;
  2705. }
  2706. }
  2707. }
  2708. return list.Num();
  2709. }
  2710. /*
  2711. =============
  2712. idGameLocal::GetTraceEntity
  2713. returns the master entity of a trace. for example, if the trace entity is the player's head, it will return the player.
  2714. =============
  2715. */
  2716. idEntity *idGameLocal::GetTraceEntity( const trace_t &trace ) const {
  2717. idEntity *master;
  2718. if ( !entities[ trace.c.entityNum ] ) {
  2719. return NULL;
  2720. }
  2721. master = entities[ trace.c.entityNum ]->GetBindMaster();
  2722. if ( master ) {
  2723. return master;
  2724. }
  2725. return entities[ trace.c.entityNum ];
  2726. }
  2727. /*
  2728. =============
  2729. idGameLocal::ArgCompletion_EntityName
  2730. Argument completion for entity names
  2731. =============
  2732. */
  2733. void idGameLocal::ArgCompletion_EntityName( const idCmdArgs &args, void(*callback)( const char *s ) ) {
  2734. int i;
  2735. for( i = 0; i < gameLocal.num_entities; i++ ) {
  2736. if ( gameLocal.entities[ i ] ) {
  2737. callback( va( "%s %s", args.Argv( 0 ), gameLocal.entities[ i ]->name.c_str() ) );
  2738. }
  2739. }
  2740. }
  2741. /*
  2742. =============
  2743. idGameLocal::FindEntity
  2744. Returns the entity whose name matches the specified string.
  2745. =============
  2746. */
  2747. idEntity *idGameLocal::FindEntity( const char *name ) const {
  2748. int hash, i;
  2749. hash = entityHash.GenerateKey( name, true );
  2750. for ( i = entityHash.First( hash ); i != -1; i = entityHash.Next( i ) ) {
  2751. if ( entities[i] && entities[i]->name.Icmp( name ) == 0 ) {
  2752. return entities[i];
  2753. }
  2754. }
  2755. return NULL;
  2756. }
  2757. /*
  2758. =============
  2759. idGameLocal::FindEntityUsingDef
  2760. Searches all active entities for the next one using the specified entityDef.
  2761. Searches beginning at the entity after from, or the beginning if NULL
  2762. NULL will be returned if the end of the list is reached.
  2763. =============
  2764. */
  2765. idEntity *idGameLocal::FindEntityUsingDef( idEntity *from, const char *match ) const {
  2766. idEntity *ent;
  2767. if ( !from ) {
  2768. ent = spawnedEntities.Next();
  2769. } else {
  2770. ent = from->spawnNode.Next();
  2771. }
  2772. for ( ; ent != NULL; ent = ent->spawnNode.Next() ) {
  2773. assert( ent );
  2774. if ( idStr::Icmp( ent->GetEntityDefName(), match ) == 0 ) {
  2775. return ent;
  2776. }
  2777. }
  2778. return NULL;
  2779. }
  2780. /*
  2781. =============
  2782. idGameLocal::FindTraceEntity
  2783. Searches all active entities for the closest ( to start ) match that intersects
  2784. the line start,end
  2785. =============
  2786. */
  2787. idEntity *idGameLocal::FindTraceEntity( idVec3 start, idVec3 end, const idTypeInfo &c, const idEntity *skip ) const {
  2788. idEntity *ent;
  2789. idEntity *bestEnt;
  2790. float scale;
  2791. float bestScale;
  2792. idBounds b;
  2793. bestEnt = NULL;
  2794. bestScale = 1.0f;
  2795. for( ent = spawnedEntities.Next(); ent != NULL; ent = ent->spawnNode.Next() ) {
  2796. if ( ent->IsType( c ) && ent != skip ) {
  2797. b = ent->GetPhysics()->GetAbsBounds().Expand( 16 );
  2798. if ( b.RayIntersection( start, end-start, scale ) ) {
  2799. if ( scale >= 0.0f && scale < bestScale ) {
  2800. bestEnt = ent;
  2801. bestScale = scale;
  2802. }
  2803. }
  2804. }
  2805. }
  2806. return bestEnt;
  2807. }
  2808. /*
  2809. ================
  2810. idGameLocal::EntitiesWithinRadius
  2811. ================
  2812. */
  2813. int idGameLocal::EntitiesWithinRadius( const idVec3 org, float radius, idEntity **entityList, int maxCount ) const {
  2814. idEntity *ent;
  2815. idBounds bo( org );
  2816. int entCount = 0;
  2817. bo.ExpandSelf( radius );
  2818. for( ent = spawnedEntities.Next(); ent != NULL; ent = ent->spawnNode.Next() ) {
  2819. if ( ent->GetPhysics()->GetAbsBounds().IntersectsBounds( bo ) ) {
  2820. entityList[entCount++] = ent;
  2821. }
  2822. }
  2823. return entCount;
  2824. }
  2825. /*
  2826. =================
  2827. idGameLocal::KillBox
  2828. Kills all entities that would touch the proposed new positioning of ent. The ent itself will not being killed.
  2829. Checks if player entities are in the teleporter, and marks them to die at teleport exit instead of immediately.
  2830. If catch_teleport, this only marks teleport players for death on exit
  2831. =================
  2832. */
  2833. void idGameLocal::KillBox( idEntity *ent, bool catch_teleport ) {
  2834. int i;
  2835. int num;
  2836. idEntity * hit;
  2837. idClipModel *cm;
  2838. idClipModel *clipModels[ MAX_GENTITIES ];
  2839. idPhysics *phys;
  2840. phys = ent->GetPhysics();
  2841. if ( !phys->GetNumClipModels() ) {
  2842. return;
  2843. }
  2844. num = clip.ClipModelsTouchingBounds( phys->GetAbsBounds(), phys->GetClipMask(), clipModels, MAX_GENTITIES );
  2845. for ( i = 0; i < num; i++ ) {
  2846. cm = clipModels[ i ];
  2847. // don't check render entities
  2848. if ( cm->IsRenderModel() ) {
  2849. continue;
  2850. }
  2851. hit = cm->GetEntity();
  2852. if ( ( hit == ent ) || !hit->fl.takedamage ) {
  2853. continue;
  2854. }
  2855. if ( !phys->ClipContents( cm ) ) {
  2856. continue;
  2857. }
  2858. // nail it
  2859. if ( hit->IsType( idPlayer::Type ) && static_cast< idPlayer * >( hit )->IsInTeleport() ) {
  2860. static_cast< idPlayer * >( hit )->TeleportDeath( ent->entityNumber );
  2861. } else if ( !catch_teleport ) {
  2862. hit->Damage( ent, ent, vec3_origin, "damage_telefrag", 1.0f, INVALID_JOINT );
  2863. }
  2864. if ( !gameLocal.isMultiplayer ) {
  2865. // let the mapper know about it
  2866. Warning( "'%s' telefragged '%s'", ent->name.c_str(), hit->name.c_str() );
  2867. }
  2868. }
  2869. }
  2870. /*
  2871. ================
  2872. idGameLocal::RequirementMet
  2873. ================
  2874. */
  2875. bool idGameLocal::RequirementMet( idEntity *activator, const idStr &requires, int removeItem ) {
  2876. if ( requires.Length() ) {
  2877. if ( activator->IsType( idPlayer::Type ) ) {
  2878. idPlayer *player = static_cast<idPlayer *>(activator);
  2879. idDict *item = player->FindInventoryItem( requires );
  2880. if ( item ) {
  2881. if ( removeItem ) {
  2882. player->RemoveInventoryItem( item );
  2883. }
  2884. return true;
  2885. } else {
  2886. return false;
  2887. }
  2888. }
  2889. }
  2890. return true;
  2891. }
  2892. /*
  2893. ============
  2894. idGameLocal::AlertAI
  2895. ============
  2896. */
  2897. void idGameLocal::AlertAI( idEntity *ent ) {
  2898. if ( ent && ent->IsType( idActor::Type ) ) {
  2899. // alert them for the next frame
  2900. lastAIAlertTime = time + msec;
  2901. lastAIAlertEntity = static_cast<idActor *>( ent );
  2902. }
  2903. }
  2904. /*
  2905. ============
  2906. idGameLocal::GetAlertEntity
  2907. ============
  2908. */
  2909. idActor *idGameLocal::GetAlertEntity( void ) {
  2910. if ( lastAIAlertTime >= time ) {
  2911. return lastAIAlertEntity.GetEntity();
  2912. }
  2913. return NULL;
  2914. }
  2915. /*
  2916. ============
  2917. idGameLocal::RadiusDamage
  2918. ============
  2919. */
  2920. void idGameLocal::RadiusDamage( const idVec3 &origin, idEntity *inflictor, idEntity *attacker, idEntity *ignoreDamage, idEntity *ignorePush, const char *damageDefName, float dmgPower ) {
  2921. float dist, damageScale, attackerDamageScale, attackerPushScale;
  2922. idEntity * ent;
  2923. idEntity * entityList[ MAX_GENTITIES ];
  2924. int numListedEntities;
  2925. idBounds bounds;
  2926. idVec3 v, damagePoint, dir;
  2927. int i, e, damage, radius, push;
  2928. const idDict *damageDef = FindEntityDefDict( damageDefName, false );
  2929. if ( !damageDef ) {
  2930. Warning( "Unknown damageDef '%s'", damageDefName );
  2931. return;
  2932. }
  2933. damageDef->GetInt( "damage", "20", damage );
  2934. damageDef->GetInt( "radius", "50", radius );
  2935. damageDef->GetInt( "push", va( "%d", damage * 100 ), push );
  2936. damageDef->GetFloat( "attackerDamageScale", "0.5", attackerDamageScale );
  2937. damageDef->GetFloat( "attackerPushScale", "0", attackerPushScale );
  2938. if ( radius < 1 ) {
  2939. radius = 1;
  2940. }
  2941. bounds = idBounds( origin ).Expand( radius );
  2942. // get all entities touching the bounds
  2943. numListedEntities = clip.EntitiesTouchingBounds( bounds, -1, entityList, MAX_GENTITIES );
  2944. if ( inflictor && inflictor->IsType( idAFAttachment::Type ) ) {
  2945. inflictor = static_cast<idAFAttachment*>(inflictor)->GetBody();
  2946. }
  2947. if ( attacker && attacker->IsType( idAFAttachment::Type ) ) {
  2948. attacker = static_cast<idAFAttachment*>(attacker)->GetBody();
  2949. }
  2950. if ( ignoreDamage && ignoreDamage->IsType( idAFAttachment::Type ) ) {
  2951. ignoreDamage = static_cast<idAFAttachment*>(ignoreDamage)->GetBody();
  2952. }
  2953. // apply damage to the entities
  2954. for ( e = 0; e < numListedEntities; e++ ) {
  2955. ent = entityList[ e ];
  2956. assert( ent );
  2957. if ( !ent->fl.takedamage ) {
  2958. continue;
  2959. }
  2960. if ( ent == inflictor || ( ent->IsType( idAFAttachment::Type ) && static_cast<idAFAttachment*>(ent)->GetBody() == inflictor ) ) {
  2961. continue;
  2962. }
  2963. if ( ent == ignoreDamage || ( ent->IsType( idAFAttachment::Type ) && static_cast<idAFAttachment*>(ent)->GetBody() == ignoreDamage ) ) {
  2964. continue;
  2965. }
  2966. // don't damage a dead player
  2967. if ( isMultiplayer && ent->entityNumber < MAX_CLIENTS && ent->IsType( idPlayer::Type ) && static_cast< idPlayer * >( ent )->health < 0 ) {
  2968. continue;
  2969. }
  2970. // find the distance from the edge of the bounding box
  2971. for ( i = 0; i < 3; i++ ) {
  2972. if ( origin[ i ] < ent->GetPhysics()->GetAbsBounds()[0][ i ] ) {
  2973. v[ i ] = ent->GetPhysics()->GetAbsBounds()[0][ i ] - origin[ i ];
  2974. } else if ( origin[ i ] > ent->GetPhysics()->GetAbsBounds()[1][ i ] ) {
  2975. v[ i ] = origin[ i ] - ent->GetPhysics()->GetAbsBounds()[1][ i ];
  2976. } else {
  2977. v[ i ] = 0;
  2978. }
  2979. }
  2980. dist = v.Length();
  2981. if ( dist >= radius ) {
  2982. continue;
  2983. }
  2984. if ( ent->CanDamage( origin, damagePoint ) ) {
  2985. // push the center of mass higher than the origin so players
  2986. // get knocked into the air more
  2987. dir = ent->GetPhysics()->GetOrigin() - origin;
  2988. dir[ 2 ] += 24;
  2989. // get the damage scale
  2990. damageScale = dmgPower * ( 1.0f - dist / radius );
  2991. if ( ent == attacker || ( ent->IsType( idAFAttachment::Type ) && static_cast<idAFAttachment*>(ent)->GetBody() == attacker ) ) {
  2992. damageScale *= attackerDamageScale;
  2993. }
  2994. ent->Damage( inflictor, attacker, dir, damageDefName, damageScale, INVALID_JOINT );
  2995. }
  2996. }
  2997. // push physics objects
  2998. if ( push ) {
  2999. RadiusPush( origin, radius, push * dmgPower, attacker, ignorePush, attackerPushScale, false );
  3000. }
  3001. }
  3002. /*
  3003. ==============
  3004. idGameLocal::RadiusPush
  3005. ==============
  3006. */
  3007. void idGameLocal::RadiusPush( const idVec3 &origin, const float radius, const float push, const idEntity *inflictor, const idEntity *ignore, float inflictorScale, const bool quake ) {
  3008. int i, numListedClipModels;
  3009. idClipModel *clipModel;
  3010. idClipModel *clipModelList[ MAX_GENTITIES ];
  3011. idVec3 dir;
  3012. idBounds bounds;
  3013. modelTrace_t result;
  3014. idEntity *ent;
  3015. float scale;
  3016. dir.Set( 0.0f, 0.0f, 1.0f );
  3017. bounds = idBounds( origin ).Expand( radius );
  3018. // get all clip models touching the bounds
  3019. numListedClipModels = clip.ClipModelsTouchingBounds( bounds, -1, clipModelList, MAX_GENTITIES );
  3020. if ( inflictor && inflictor->IsType( idAFAttachment::Type ) ) {
  3021. inflictor = static_cast<const idAFAttachment*>(inflictor)->GetBody();
  3022. }
  3023. if ( ignore && ignore->IsType( idAFAttachment::Type ) ) {
  3024. ignore = static_cast<const idAFAttachment*>(ignore)->GetBody();
  3025. }
  3026. // apply impact to all the clip models through their associated physics objects
  3027. for ( i = 0; i < numListedClipModels; i++ ) {
  3028. clipModel = clipModelList[i];
  3029. // never push render models
  3030. if ( clipModel->IsRenderModel() ) {
  3031. continue;
  3032. }
  3033. ent = clipModel->GetEntity();
  3034. // never push projectiles
  3035. if ( ent->IsType( idProjectile::Type ) ) {
  3036. continue;
  3037. }
  3038. // players use "knockback" in idPlayer::Damage
  3039. if ( ent->IsType( idPlayer::Type ) && !quake ) {
  3040. continue;
  3041. }
  3042. // don't push the ignore entity
  3043. if ( ent == ignore || ( ent->IsType( idAFAttachment::Type ) && static_cast<idAFAttachment*>(ent)->GetBody() == ignore ) ) {
  3044. continue;
  3045. }
  3046. if ( gameRenderWorld->FastWorldTrace( result, origin, clipModel->GetOrigin() ) ) {
  3047. continue;
  3048. }
  3049. // scale the push for the inflictor
  3050. if ( ent == inflictor || ( ent->IsType( idAFAttachment::Type ) && static_cast<idAFAttachment*>(ent)->GetBody() == inflictor ) ) {
  3051. scale = inflictorScale;
  3052. } else {
  3053. scale = 1.0f;
  3054. }
  3055. if ( quake ) {
  3056. clipModel->GetEntity()->ApplyImpulse( world, clipModel->GetId(), clipModel->GetOrigin(), scale * push * dir );
  3057. } else {
  3058. RadiusPushClipModel( origin, scale * push, clipModel );
  3059. }
  3060. }
  3061. }
  3062. /*
  3063. ==============
  3064. idGameLocal::RadiusPushClipModel
  3065. ==============
  3066. */
  3067. void idGameLocal::RadiusPushClipModel( const idVec3 &origin, const float push, const idClipModel *clipModel ) {
  3068. int i, j;
  3069. float dot, dist, area;
  3070. const idTraceModel *trm;
  3071. const traceModelPoly_t *poly;
  3072. idFixedWinding w;
  3073. idVec3 v, localOrigin, center, impulse;
  3074. trm = clipModel->GetTraceModel();
  3075. if ( !trm || 1 ) {
  3076. impulse = clipModel->GetAbsBounds().GetCenter() - origin;
  3077. impulse.Normalize();
  3078. impulse.z += 1.0f;
  3079. clipModel->GetEntity()->ApplyImpulse( world, clipModel->GetId(), clipModel->GetOrigin(), push * impulse );
  3080. return;
  3081. }
  3082. localOrigin = ( origin - clipModel->GetOrigin() ) * clipModel->GetAxis().Transpose();
  3083. for ( i = 0; i < trm->numPolys; i++ ) {
  3084. poly = &trm->polys[i];
  3085. center.Zero();
  3086. for ( j = 0; j < poly->numEdges; j++ ) {
  3087. v = trm->verts[ trm->edges[ abs(poly->edges[j]) ].v[ INTSIGNBITSET( poly->edges[j] ) ] ];
  3088. center += v;
  3089. v -= localOrigin;
  3090. v.NormalizeFast(); // project point on a unit sphere
  3091. w.AddPoint( v );
  3092. }
  3093. center /= poly->numEdges;
  3094. v = center - localOrigin;
  3095. dist = v.NormalizeFast();
  3096. dot = v * poly->normal;
  3097. if ( dot > 0.0f ) {
  3098. continue;
  3099. }
  3100. area = w.GetArea();
  3101. // impulse in polygon normal direction
  3102. impulse = poly->normal * clipModel->GetAxis();
  3103. // always push up for nicer effect
  3104. impulse.z -= 1.0f;
  3105. // scale impulse based on visible surface area and polygon angle
  3106. impulse *= push * ( dot * area * ( 1.0f / ( 4.0f * idMath::PI ) ) );
  3107. // scale away distance for nicer effect
  3108. impulse *= ( dist * 2.0f );
  3109. // impulse is applied to the center of the polygon
  3110. center = clipModel->GetOrigin() + center * clipModel->GetAxis();
  3111. clipModel->GetEntity()->ApplyImpulse( world, clipModel->GetId(), center, impulse );
  3112. }
  3113. }
  3114. /*
  3115. ===============
  3116. idGameLocal::ProjectDecal
  3117. ===============
  3118. */
  3119. void idGameLocal::ProjectDecal( const idVec3 &origin, const idVec3 &dir, float depth, bool parallel, float size, const char *material, float angle ) {
  3120. float s, c;
  3121. idMat3 axis, axistemp;
  3122. idFixedWinding winding;
  3123. idVec3 windingOrigin, projectionOrigin;
  3124. static idVec3 decalWinding[4] = {
  3125. idVec3( 1.0f, 1.0f, 0.0f ),
  3126. idVec3( -1.0f, 1.0f, 0.0f ),
  3127. idVec3( -1.0f, -1.0f, 0.0f ),
  3128. idVec3( 1.0f, -1.0f, 0.0f )
  3129. };
  3130. if ( !g_decals.GetBool() ) {
  3131. return;
  3132. }
  3133. // randomly rotate the decal winding
  3134. idMath::SinCos16( ( angle ) ? angle : random.RandomFloat() * idMath::TWO_PI, s, c );
  3135. // winding orientation
  3136. axis[2] = dir;
  3137. axis[2].Normalize();
  3138. axis[2].NormalVectors( axistemp[0], axistemp[1] );
  3139. axis[0] = axistemp[ 0 ] * c + axistemp[ 1 ] * -s;
  3140. axis[1] = axistemp[ 0 ] * -s + axistemp[ 1 ] * -c;
  3141. windingOrigin = origin + depth * axis[2];
  3142. if ( parallel ) {
  3143. projectionOrigin = origin - depth * axis[2];
  3144. } else {
  3145. projectionOrigin = origin;
  3146. }
  3147. size *= 0.5f;
  3148. winding.Clear();
  3149. winding += idVec5( windingOrigin + ( axis * decalWinding[0] ) * size, idVec2( 1, 1 ) );
  3150. winding += idVec5( windingOrigin + ( axis * decalWinding[1] ) * size, idVec2( 0, 1 ) );
  3151. winding += idVec5( windingOrigin + ( axis * decalWinding[2] ) * size, idVec2( 0, 0 ) );
  3152. winding += idVec5( windingOrigin + ( axis * decalWinding[3] ) * size, idVec2( 1, 0 ) );
  3153. gameRenderWorld->ProjectDecalOntoWorld( winding, projectionOrigin, parallel, depth * 0.5f, declManager->FindMaterial( material ), time );
  3154. }
  3155. /*
  3156. ==============
  3157. idGameLocal::BloodSplat
  3158. ==============
  3159. */
  3160. void idGameLocal::BloodSplat( const idVec3 &origin, const idVec3 &dir, float size, const char *material ) {
  3161. float halfSize = size * 0.5f;
  3162. idVec3 verts[] = { idVec3( 0.0f, +halfSize, +halfSize ),
  3163. idVec3( 0.0f, +halfSize, -halfSize ),
  3164. idVec3( 0.0f, -halfSize, -halfSize ),
  3165. idVec3( 0.0f, -halfSize, +halfSize ) };
  3166. idTraceModel trm;
  3167. idClipModel mdl;
  3168. trace_t results;
  3169. // FIXME: get from damage def
  3170. if ( !g_bloodEffects.GetBool() ) {
  3171. return;
  3172. }
  3173. size = halfSize + random.RandomFloat() * halfSize;
  3174. trm.SetupPolygon( verts, 4 );
  3175. mdl.LoadModel( trm );
  3176. clip.Translation( results, origin, origin + dir * 64.0f, &mdl, mat3_identity, CONTENTS_SOLID, NULL );
  3177. ProjectDecal( results.endpos, dir, 2.0f * size, true, size, material );
  3178. }
  3179. /*
  3180. =============
  3181. idGameLocal::SetCamera
  3182. =============
  3183. */
  3184. void idGameLocal::SetCamera( idCamera *cam ) {
  3185. int i;
  3186. idEntity *ent;
  3187. idAI *ai;
  3188. // this should fix going into a cinematic when dead.. rare but happens
  3189. idPlayer *client = GetLocalPlayer();
  3190. if ( client->health <= 0 || client->AI_DEAD ) {
  3191. return;
  3192. }
  3193. camera = cam;
  3194. if ( camera ) {
  3195. inCinematic = true;
  3196. if ( skipCinematic && camera->spawnArgs.GetBool( "disconnect" ) ) {
  3197. camera->spawnArgs.SetBool( "disconnect", false );
  3198. cvarSystem->SetCVarFloat( "r_znear", 3.0f );
  3199. cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "disconnect\n" );
  3200. skipCinematic = false;
  3201. return;
  3202. }
  3203. if ( time > cinematicStopTime ) {
  3204. cinematicSkipTime = time + CINEMATIC_SKIP_DELAY;
  3205. }
  3206. // set r_znear so that transitioning into/out of the player's head doesn't clip through the view
  3207. cvarSystem->SetCVarFloat( "r_znear", 1.0f );
  3208. // hide all the player models
  3209. for( i = 0; i < numClients; i++ ) {
  3210. if ( entities[ i ] ) {
  3211. client = static_cast< idPlayer* >( entities[ i ] );
  3212. client->EnterCinematic();
  3213. }
  3214. }
  3215. if ( !cam->spawnArgs.GetBool( "ignore_enemies" ) ) {
  3216. // kill any active monsters that are enemies of the player
  3217. for ( ent = spawnedEntities.Next(); ent != NULL; ent = ent->spawnNode.Next() ) {
  3218. if ( ent->cinematic || ent->fl.isDormant ) {
  3219. // only kill entities that aren't needed for cinematics and aren't dormant
  3220. continue;
  3221. }
  3222. if ( ent->IsType( idAI::Type ) ) {
  3223. ai = static_cast<idAI *>( ent );
  3224. if ( !ai->GetEnemy() || !ai->IsActive() ) {
  3225. // no enemy, or inactive, so probably safe to ignore
  3226. continue;
  3227. }
  3228. } else if ( ent->IsType( idProjectile::Type ) ) {
  3229. // remove all projectiles
  3230. } else if ( ent->spawnArgs.GetBool( "cinematic_remove" ) ) {
  3231. // remove anything marked to be removed during cinematics
  3232. } else {
  3233. // ignore everything else
  3234. continue;
  3235. }
  3236. // remove it
  3237. DPrintf( "removing '%s' for cinematic\n", ent->GetName() );
  3238. ent->PostEventMS( &EV_Remove, 0 );
  3239. }
  3240. }
  3241. } else {
  3242. inCinematic = false;
  3243. cinematicStopTime = time + msec;
  3244. // restore r_znear
  3245. cvarSystem->SetCVarFloat( "r_znear", 3.0f );
  3246. // show all the player models
  3247. for( i = 0; i < numClients; i++ ) {
  3248. if ( entities[ i ] ) {
  3249. idPlayer *client = static_cast< idPlayer* >( entities[ i ] );
  3250. client->ExitCinematic();
  3251. }
  3252. }
  3253. }
  3254. }
  3255. /*
  3256. =============
  3257. idGameLocal::GetCamera
  3258. =============
  3259. */
  3260. idCamera *idGameLocal::GetCamera( void ) const {
  3261. return camera;
  3262. }
  3263. /*
  3264. =============
  3265. idGameLocal::SkipCinematic
  3266. =============
  3267. */
  3268. bool idGameLocal::SkipCinematic( void ) {
  3269. if ( camera ) {
  3270. if ( camera->spawnArgs.GetBool( "disconnect" ) ) {
  3271. camera->spawnArgs.SetBool( "disconnect", false );
  3272. cvarSystem->SetCVarFloat( "r_znear", 3.0f );
  3273. cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "disconnect\n" );
  3274. skipCinematic = false;
  3275. return false;
  3276. }
  3277. if ( camera->spawnArgs.GetBool( "instantSkip" ) ) {
  3278. camera->Stop();
  3279. return false;
  3280. }
  3281. }
  3282. soundSystem->SetMute( true );
  3283. if ( !skipCinematic ) {
  3284. skipCinematic = true;
  3285. cinematicMaxSkipTime = gameLocal.time + SEC2MS( g_cinematicMaxSkipTime.GetFloat() );
  3286. }
  3287. return true;
  3288. }
  3289. /*
  3290. ======================
  3291. idGameLocal::SpreadLocations
  3292. Now that everything has been spawned, associate areas with location entities
  3293. ======================
  3294. */
  3295. void idGameLocal::SpreadLocations() {
  3296. idEntity *ent;
  3297. // allocate the area table
  3298. int numAreas = gameRenderWorld->NumAreas();
  3299. locationEntities = new idLocationEntity *[ numAreas ];
  3300. memset( locationEntities, 0, numAreas * sizeof( *locationEntities ) );
  3301. // for each location entity, make pointers from every area it touches
  3302. for( ent = spawnedEntities.Next(); ent != NULL; ent = ent->spawnNode.Next() ) {
  3303. if ( !ent->IsType( idLocationEntity::Type ) ) {
  3304. continue;
  3305. }
  3306. idVec3 point = ent->spawnArgs.GetVector( "origin" );
  3307. int areaNum = gameRenderWorld->PointInArea( point );
  3308. if ( areaNum < 0 ) {
  3309. Printf( "SpreadLocations: location '%s' is not in a valid area\n", ent->spawnArgs.GetString( "name" ) );
  3310. continue;
  3311. }
  3312. if ( areaNum >= numAreas ) {
  3313. Error( "idGameLocal::SpreadLocations: areaNum >= gameRenderWorld->NumAreas()" );
  3314. }
  3315. if ( locationEntities[areaNum] ) {
  3316. Warning( "location entity '%s' overlaps '%s'", ent->spawnArgs.GetString( "name" ),
  3317. locationEntities[areaNum]->spawnArgs.GetString( "name" ) );
  3318. continue;
  3319. }
  3320. locationEntities[areaNum] = static_cast<idLocationEntity *>(ent);
  3321. // spread to all other connected areas
  3322. for ( int i = 0 ; i < numAreas ; i++ ) {
  3323. if ( i == areaNum ) {
  3324. continue;
  3325. }
  3326. if ( gameRenderWorld->AreasAreConnected( areaNum, i, PS_BLOCK_LOCATION ) ) {
  3327. locationEntities[i] = static_cast<idLocationEntity *>(ent);
  3328. }
  3329. }
  3330. }
  3331. }
  3332. /*
  3333. ===================
  3334. idGameLocal::LocationForPoint
  3335. The player checks the location each frame to update the HUD text display
  3336. May return NULL
  3337. ===================
  3338. */
  3339. idLocationEntity *idGameLocal::LocationForPoint( const idVec3 &point ) {
  3340. if ( !locationEntities ) {
  3341. // before SpreadLocations() has been called
  3342. return NULL;
  3343. }
  3344. int areaNum = gameRenderWorld->PointInArea( point );
  3345. if ( areaNum < 0 ) {
  3346. return NULL;
  3347. }
  3348. if ( areaNum >= gameRenderWorld->NumAreas() ) {
  3349. Error( "idGameLocal::LocationForPoint: areaNum >= gameRenderWorld->NumAreas()" );
  3350. }
  3351. return locationEntities[ areaNum ];
  3352. }
  3353. /*
  3354. ============
  3355. idGameLocal::SetPortalState
  3356. ============
  3357. */
  3358. void idGameLocal::SetPortalState( qhandle_t portal, int blockingBits ) {
  3359. idBitMsg outMsg;
  3360. byte msgBuf[ MAX_GAME_MESSAGE_SIZE ];
  3361. if ( !gameLocal.isClient ) {
  3362. outMsg.Init( msgBuf, sizeof( msgBuf ) );
  3363. outMsg.WriteByte( GAME_RELIABLE_MESSAGE_PORTAL );
  3364. outMsg.WriteLong( portal );
  3365. outMsg.WriteBits( blockingBits, NUM_RENDER_PORTAL_BITS );
  3366. networkSystem->ServerSendReliableMessage( -1, outMsg );
  3367. }
  3368. gameRenderWorld->SetPortalState( portal, blockingBits );
  3369. }
  3370. /*
  3371. ============
  3372. idGameLocal::sortSpawnPoints
  3373. ============
  3374. */
  3375. int idGameLocal::sortSpawnPoints( const void *ptr1, const void *ptr2 ) {
  3376. const spawnSpot_t *spot1 = static_cast<const spawnSpot_t *>( ptr1 );
  3377. const spawnSpot_t *spot2 = static_cast<const spawnSpot_t *>( ptr2 );
  3378. float diff;
  3379. diff = spot1->dist - spot2->dist;
  3380. if ( diff < 0.0f ) {
  3381. return 1;
  3382. } else if ( diff > 0.0f ) {
  3383. return -1;
  3384. } else {
  3385. return 0;
  3386. }
  3387. }
  3388. /*
  3389. ===========
  3390. idGameLocal::RandomizeInitialSpawns
  3391. randomize the order of the initial spawns
  3392. prepare for a sequence of initial player spawns
  3393. ============
  3394. */
  3395. void idGameLocal::RandomizeInitialSpawns( void ) {
  3396. spawnSpot_t spot;
  3397. int i, j;
  3398. idEntity *ent;
  3399. if ( !isMultiplayer || isClient ) {
  3400. return;
  3401. }
  3402. spawnSpots.Clear();
  3403. initialSpots.Clear();
  3404. spot.dist = 0;
  3405. spot.ent = FindEntityUsingDef( NULL, "info_player_deathmatch" );
  3406. while( spot.ent ) {
  3407. spawnSpots.Append( spot );
  3408. if ( spot.ent->spawnArgs.GetBool( "initial" ) ) {
  3409. initialSpots.Append( spot.ent );
  3410. }
  3411. spot.ent = FindEntityUsingDef( spot.ent, "info_player_deathmatch" );
  3412. }
  3413. if ( !spawnSpots.Num() ) {
  3414. common->Warning( "no info_player_deathmatch in map" );
  3415. return;
  3416. }
  3417. common->Printf( "%d spawns (%d initials)\n", spawnSpots.Num(), initialSpots.Num() );
  3418. // if there are no initial spots in the map, consider they can all be used as initial
  3419. if ( !initialSpots.Num() ) {
  3420. common->Warning( "no info_player_deathmatch entities marked initial in map" );
  3421. for ( i = 0; i < spawnSpots.Num(); i++ ) {
  3422. initialSpots.Append( spawnSpots[ i ].ent );
  3423. }
  3424. }
  3425. for ( i = 0; i < initialSpots.Num(); i++ ) {
  3426. j = random.RandomInt( initialSpots.Num() );
  3427. ent = initialSpots[ i ];
  3428. initialSpots[ i ] = initialSpots[ j ];
  3429. initialSpots[ j ] = ent;
  3430. }
  3431. // reset the counter
  3432. currentInitialSpot = 0;
  3433. }
  3434. /*
  3435. ===========
  3436. idGameLocal::SelectInitialSpawnPoint
  3437. spectators are spawned randomly anywhere
  3438. in-game clients are spawned based on distance to active players (randomized on the first half)
  3439. upon map restart, initial spawns are used (randomized ordered list of spawns flagged "initial")
  3440. if there are more players than initial spots, overflow to regular spawning
  3441. ============
  3442. */
  3443. idEntity *idGameLocal::SelectInitialSpawnPoint( idPlayer *player ) {
  3444. int i, j, which;
  3445. spawnSpot_t spot;
  3446. idVec3 pos;
  3447. float dist;
  3448. bool alone;
  3449. if ( !isMultiplayer || !spawnSpots.Num() ) {
  3450. spot.ent = FindEntityUsingDef( NULL, "info_player_start" );
  3451. if ( !spot.ent ) {
  3452. Error( "No info_player_start on map.\n" );
  3453. }
  3454. return spot.ent;
  3455. }
  3456. if ( player->spectating ) {
  3457. // plain random spot, don't bother
  3458. return spawnSpots[ random.RandomInt( spawnSpots.Num() ) ].ent;
  3459. } else if ( player->useInitialSpawns && currentInitialSpot < initialSpots.Num() ) {
  3460. return initialSpots[ currentInitialSpot++ ];
  3461. } else {
  3462. // check if we are alone in map
  3463. alone = true;
  3464. for ( j = 0; j < MAX_CLIENTS; j++ ) {
  3465. if ( entities[ j ] && entities[ j ] != player ) {
  3466. alone = false;
  3467. break;
  3468. }
  3469. }
  3470. if ( alone ) {
  3471. // don't do distance-based
  3472. return spawnSpots[ random.RandomInt( spawnSpots.Num() ) ].ent;
  3473. }
  3474. // find the distance to the closest active player for each spawn spot
  3475. for( i = 0; i < spawnSpots.Num(); i++ ) {
  3476. pos = spawnSpots[ i ].ent->GetPhysics()->GetOrigin();
  3477. spawnSpots[ i ].dist = 0x7fffffff;
  3478. for( j = 0; j < MAX_CLIENTS; j++ ) {
  3479. if ( !entities[ j ] || !entities[ j ]->IsType( idPlayer::Type )
  3480. || entities[ j ] == player
  3481. || static_cast< idPlayer * >( entities[ j ] )->spectating ) {
  3482. continue;
  3483. }
  3484. dist = ( pos - entities[ j ]->GetPhysics()->GetOrigin() ).LengthSqr();
  3485. if ( dist < spawnSpots[ i ].dist ) {
  3486. spawnSpots[ i ].dist = dist;
  3487. }
  3488. }
  3489. }
  3490. // sort the list
  3491. qsort( ( void * )spawnSpots.Ptr(), spawnSpots.Num(), sizeof( spawnSpot_t ), ( int (*)(const void *, const void *) )sortSpawnPoints );
  3492. // choose a random one in the top half
  3493. which = random.RandomInt( spawnSpots.Num() / 2 );
  3494. spot = spawnSpots[ which ];
  3495. }
  3496. return spot.ent;
  3497. }
  3498. /*
  3499. ================
  3500. idGameLocal::UpdateServerInfoFlags
  3501. ================
  3502. */
  3503. void idGameLocal::UpdateServerInfoFlags() {
  3504. gameType = GAME_SP;
  3505. if ( ( idStr::Icmp( serverInfo.GetString( "si_gameType" ), "deathmatch" ) == 0 ) ) {
  3506. gameType = GAME_DM;
  3507. } else if ( ( idStr::Icmp( serverInfo.GetString( "si_gameType" ), "Tourney" ) == 0 ) ) {
  3508. gameType = GAME_TOURNEY;
  3509. } else if ( ( idStr::Icmp( serverInfo.GetString( "si_gameType" ), "Team DM" ) == 0 ) ) {
  3510. gameType = GAME_TDM;
  3511. } else if ( ( idStr::Icmp( serverInfo.GetString( "si_gameType" ), "Last Man" ) == 0 ) ) {
  3512. gameType = GAME_LASTMAN;
  3513. }
  3514. if ( gameType == GAME_LASTMAN ) {
  3515. if ( !serverInfo.GetInt( "si_warmup" ) ) {
  3516. common->Warning( "Last Man Standing - forcing warmup on" );
  3517. serverInfo.SetInt( "si_warmup", 1 );
  3518. }
  3519. if ( serverInfo.GetInt( "si_fraglimit" ) <= 0 ) {
  3520. common->Warning( "Last Man Standing - setting fraglimit 1" );
  3521. serverInfo.SetInt( "si_fraglimit", 1 );
  3522. }
  3523. }
  3524. }
  3525. /*
  3526. ================
  3527. idGameLocal::SetGlobalMaterial
  3528. ================
  3529. */
  3530. void idGameLocal::SetGlobalMaterial( const idMaterial *mat ) {
  3531. globalMaterial = mat;
  3532. }
  3533. /*
  3534. ================
  3535. idGameLocal::GetGlobalMaterial
  3536. ================
  3537. */
  3538. const idMaterial *idGameLocal::GetGlobalMaterial() {
  3539. return globalMaterial;
  3540. }
  3541. /*
  3542. ================
  3543. idGameLocal::GetSpawnId
  3544. ================
  3545. */
  3546. int idGameLocal::GetSpawnId( const idEntity* ent ) const {
  3547. return ( gameLocal.spawnIds[ ent->entityNumber ] << GENTITYNUM_BITS ) | ent->entityNumber;
  3548. }
  3549. /*
  3550. ================
  3551. idGameLocal::ThrottleUserInfo
  3552. ================
  3553. */
  3554. void idGameLocal::ThrottleUserInfo( void ) {
  3555. mpGame.ThrottleUserInfo();
  3556. }
  3557. /*
  3558. ===========
  3559. idGameLocal::SelectTimeGroup
  3560. ============
  3561. */
  3562. void idGameLocal::SelectTimeGroup( int timeGroup ) { }
  3563. /*
  3564. ===========
  3565. idGameLocal::GetTimeGroupTime
  3566. ============
  3567. */
  3568. int idGameLocal::GetTimeGroupTime( int timeGroup ) {
  3569. return gameLocal.time;
  3570. }
  3571. /*
  3572. ===========
  3573. idGameLocal::GetBestGameType
  3574. ============
  3575. */
  3576. void idGameLocal::GetBestGameType( const char* map, const char* gametype, char buf[ MAX_STRING_CHARS ] ) {
  3577. strncpy( buf, gametype, MAX_STRING_CHARS );
  3578. buf[ MAX_STRING_CHARS - 1 ] = '\0';
  3579. }
  3580. /*
  3581. ===========
  3582. idGameLocal::NeedRestart
  3583. ============
  3584. */
  3585. bool idGameLocal::NeedRestart() {
  3586. idDict newInfo;
  3587. const idKeyValue *keyval, *keyval2;
  3588. newInfo = *cvarSystem->MoveCVarsToDict( CVAR_SERVERINFO );
  3589. for ( int i = 0; i < newInfo.GetNumKeyVals(); i++ ) {
  3590. keyval = newInfo.GetKeyVal( i );
  3591. keyval2 = serverInfo.FindKey( keyval->GetKey() );
  3592. if ( !keyval2 ) {
  3593. return true;
  3594. }
  3595. // a select set of si_ changes will cause a full restart of the server
  3596. if ( keyval->GetValue().Cmp( keyval2->GetValue() ) && ( !keyval->GetKey().Cmp( "si_pure" ) || !keyval->GetKey().Cmp( "si_map" ) ) ) {
  3597. return true;
  3598. }
  3599. }
  3600. return false;
  3601. }
  3602. /*
  3603. ================
  3604. idGameLocal::GetClientStats
  3605. ================
  3606. */
  3607. void idGameLocal::GetClientStats( int clientNum, char *data, const int len ) {
  3608. mpGame.PlayerStats( clientNum, data, len );
  3609. }
  3610. /*
  3611. ================
  3612. idGameLocal::SwitchTeam
  3613. ================
  3614. */
  3615. void idGameLocal::SwitchTeam( int clientNum, int team ) {
  3616. idPlayer * player;
  3617. player = clientNum >= 0 ? static_cast<idPlayer *>( gameLocal.entities[ clientNum ] ) : NULL;
  3618. if ( !player )
  3619. return;
  3620. int oldTeam = player->team;
  3621. // Put in spectator mode
  3622. if ( team == -1 ) {
  3623. static_cast< idPlayer * >( entities[ clientNum ] )->Spectate( true );
  3624. }
  3625. // Switch to a team
  3626. else {
  3627. mpGame.SwitchToTeam ( clientNum, oldTeam, team );
  3628. }
  3629. }
  3630. /*
  3631. ===============
  3632. idGameLocal::GetMapLoadingGUI
  3633. ===============
  3634. */
  3635. void idGameLocal::GetMapLoadingGUI( char gui[ MAX_STRING_CHARS ] ) { }