Game_local.cpp 131 KB

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