Game_local.cpp 123 KB

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