1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369 |
- /*
- ===========================================================================
- Doom 3 GPL Source Code
- Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
- This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
- Doom 3 Source Code is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
- Doom 3 Source Code is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
- In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
- 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.
- ===========================================================================
- */
- #include "../idlib/precompiled.h"
- #pragma hdrstop
- #include "Game_local.h"
- #ifdef GAME_DLL
- idSys * sys = NULL;
- idCommon * common = NULL;
- idCmdSystem * cmdSystem = NULL;
- idCVarSystem * cvarSystem = NULL;
- idFileSystem * fileSystem = NULL;
- idNetworkSystem * networkSystem = NULL;
- idRenderSystem * renderSystem = NULL;
- idSoundSystem * soundSystem = NULL;
- idRenderModelManager * renderModelManager = NULL;
- idUserInterfaceManager * uiManager = NULL;
- idDeclManager * declManager = NULL;
- idAASFileManager * AASFileManager = NULL;
- idCollisionModelManager * collisionModelManager = NULL;
- idCVar * idCVar::staticVars = NULL;
- idCVar com_forceGenericSIMD( "com_forceGenericSIMD", "0", CVAR_BOOL|CVAR_SYSTEM, "force generic platform independent SIMD" );
- #endif
- idRenderWorld * gameRenderWorld = NULL; // all drawing is done to this world
- idSoundWorld * gameSoundWorld = NULL; // all audio goes to this world
- static gameExport_t gameExport;
- // global animation lib
- idAnimManager animationLib;
- // the rest of the engine will only reference the "game" variable, while all local aspects stay hidden
- idGameLocal gameLocal;
- idGame * game = &gameLocal; // statically pointed at an idGameLocal
- const char *idGameLocal::sufaceTypeNames[ MAX_SURFACE_TYPES ] = {
- "none", "metal", "stone", "flesh", "wood", "cardboard", "liquid", "glass", "plastic",
- "ricochet", "surftype10", "surftype11", "surftype12", "surftype13", "surftype14", "surftype15"
- };
- /*
- ===========
- GetGameAPI
- ============
- */
- #if __MWERKS__
- #pragma export on
- #endif
- #if __GNUC__ >= 4
- #pragma GCC visibility push(default)
- #endif
- extern "C" gameExport_t *GetGameAPI( gameImport_t *import ) {
- #if __MWERKS__
- #pragma export off
- #endif
- if ( import->version == GAME_API_VERSION ) {
- // set interface pointers used by the game
- sys = import->sys;
- common = import->common;
- cmdSystem = import->cmdSystem;
- cvarSystem = import->cvarSystem;
- fileSystem = import->fileSystem;
- networkSystem = import->networkSystem;
- renderSystem = import->renderSystem;
- soundSystem = import->soundSystem;
- renderModelManager = import->renderModelManager;
- uiManager = import->uiManager;
- declManager = import->declManager;
- AASFileManager = import->AASFileManager;
- collisionModelManager = import->collisionModelManager;
- }
- // set interface pointers used by idLib
- idLib::sys = sys;
- idLib::common = common;
- idLib::cvarSystem = cvarSystem;
- idLib::fileSystem = fileSystem;
- // setup export interface
- gameExport.version = GAME_API_VERSION;
- gameExport.game = game;
- gameExport.gameEdit = gameEdit;
- return &gameExport;
- }
- #if __GNUC__ >= 4
- #pragma GCC visibility pop
- #endif
- /*
- ===========
- TestGameAPI
- ============
- */
- void TestGameAPI( void ) {
- gameImport_t testImport;
- gameExport_t testExport;
- testImport.sys = ::sys;
- testImport.common = ::common;
- testImport.cmdSystem = ::cmdSystem;
- testImport.cvarSystem = ::cvarSystem;
- testImport.fileSystem = ::fileSystem;
- testImport.networkSystem = ::networkSystem;
- testImport.renderSystem = ::renderSystem;
- testImport.soundSystem = ::soundSystem;
- testImport.renderModelManager = ::renderModelManager;
- testImport.uiManager = ::uiManager;
- testImport.declManager = ::declManager;
- testImport.AASFileManager = ::AASFileManager;
- testImport.collisionModelManager = ::collisionModelManager;
- testExport = *GetGameAPI( &testImport );
- }
- /*
- ===========
- idGameLocal::idGameLocal
- ============
- */
- idGameLocal::idGameLocal() {
- Clear();
- }
- /*
- ===========
- idGameLocal::Clear
- ============
- */
- void idGameLocal::Clear( void ) {
- int i;
- serverInfo.Clear();
- numClients = 0;
- for ( i = 0; i < MAX_CLIENTS; i++ ) {
- userInfo[i].Clear();
- persistentPlayerInfo[i].Clear();
- }
- memset( usercmds, 0, sizeof( usercmds ) );
- memset( entities, 0, sizeof( entities ) );
- memset( spawnIds, -1, sizeof( spawnIds ) );
- firstFreeIndex = 0;
- num_entities = 0;
- spawnedEntities.Clear();
- activeEntities.Clear();
- numEntitiesToDeactivate = 0;
- sortPushers = false;
- sortTeamMasters = false;
- persistentLevelInfo.Clear();
- memset( globalShaderParms, 0, sizeof( globalShaderParms ) );
- random.SetSeed( 0 );
- world = NULL;
- frameCommandThread = NULL;
- testmodel = NULL;
- testFx = NULL;
- clip.Shutdown();
- pvs.Shutdown();
- sessionCommand.Clear();
- locationEntities = NULL;
- smokeParticles = NULL;
- editEntities = NULL;
- entityHash.Clear( 1024, MAX_GENTITIES );
- inCinematic = false;
- cinematicSkipTime = 0;
- cinematicStopTime = 0;
- cinematicMaxSkipTime = 0;
- framenum = 0;
- previousTime = 0;
- time = 0;
- vacuumAreaNum = 0;
- mapFileName.Clear();
- mapFile = NULL;
- spawnCount = INITIAL_SPAWN_COUNT;
- mapSpawnCount = 0;
- camera = NULL;
- aasList.Clear();
- aasNames.Clear();
- lastAIAlertEntity = NULL;
- lastAIAlertTime = 0;
- spawnArgs.Clear();
- gravity.Set( 0, 0, -1 );
- playerPVS.h = (unsigned int)-1;
- playerConnectedAreas.h = (unsigned int)-1;
- gamestate = GAMESTATE_UNINITIALIZED;
- skipCinematic = false;
- influenceActive = false;
- localClientNum = 0;
- isMultiplayer = false;
- isServer = false;
- isClient = false;
- realClientTime = 0;
- isNewFrame = true;
- clientSmoothing = 0.1f;
- entityDefBits = 0;
- nextGibTime = 0;
- globalMaterial = NULL;
- newInfo.Clear();
- lastGUIEnt = NULL;
- lastGUI = 0;
- memset( clientEntityStates, 0, sizeof( clientEntityStates ) );
- memset( clientPVS, 0, sizeof( clientPVS ) );
- memset( clientSnapshots, 0, sizeof( clientSnapshots ) );
- eventQueue.Init();
- savedEventQueue.Init();
- memset( lagometer, 0, sizeof( lagometer ) );
- }
- /*
- ===========
- idGameLocal::Init
- initialize the game object, only happens once at startup, not each level load
- ============
- */
- void idGameLocal::Init( void ) {
- const idDict *dict;
- idAAS *aas;
- #ifndef GAME_DLL
- TestGameAPI();
- #else
- // initialize idLib
- idLib::Init();
- // register static cvars declared in the game
- idCVar::RegisterStaticVars();
- // initialize processor specific SIMD
- idSIMD::InitProcessor( "game", com_forceGenericSIMD.GetBool() );
- #endif
- Printf( "--------- Initializing Game ----------\n" );
- Printf( "gamename: %s\n", GAME_VERSION );
- Printf( "gamedate: %s\n", __DATE__ );
- // register game specific decl types
- declManager->RegisterDeclType( "model", DECL_MODELDEF, idDeclAllocator<idDeclModelDef> );
- declManager->RegisterDeclType( "export", DECL_MODELEXPORT, idDeclAllocator<idDecl> );
- // register game specific decl folders
- declManager->RegisterDeclFolder( "def", ".def", DECL_ENTITYDEF );
- declManager->RegisterDeclFolder( "fx", ".fx", DECL_FX );
- declManager->RegisterDeclFolder( "particles", ".prt", DECL_PARTICLE );
- declManager->RegisterDeclFolder( "af", ".af", DECL_AF );
- declManager->RegisterDeclFolder( "newpdas", ".pda", DECL_PDA );
- cmdSystem->AddCommand( "listModelDefs", idListDecls_f<DECL_MODELDEF>, CMD_FL_SYSTEM|CMD_FL_GAME, "lists model defs" );
- cmdSystem->AddCommand( "printModelDefs", idPrintDecls_f<DECL_MODELDEF>, CMD_FL_SYSTEM|CMD_FL_GAME, "prints a model def", idCmdSystem::ArgCompletion_Decl<DECL_MODELDEF> );
- Clear();
- idEvent::Init();
- idClass::Init();
- InitConsoleCommands();
- // load default scripts
- program.Startup( SCRIPT_DEFAULT );
-
- smokeParticles = new idSmokeParticles;
- // set up the aas
- dict = FindEntityDefDict( "aas_types" );
- if ( !dict ) {
- Error( "Unable to find entityDef for 'aas_types'" );
- }
- // allocate space for the aas
- const idKeyValue *kv = dict->MatchPrefix( "type" );
- while( kv != NULL ) {
- aas = idAAS::Alloc();
- aasList.Append( aas );
- aasNames.Append( kv->GetValue() );
- kv = dict->MatchPrefix( "type", kv );
- }
- gamestate = GAMESTATE_NOMAP;
- Printf( "...%d aas types\n", aasList.Num() );
- Printf( "game initialized.\n" );
- Printf( "--------------------------------------\n" );
- }
- /*
- ===========
- idGameLocal::Shutdown
- shut down the entire game
- ============
- */
- void idGameLocal::Shutdown( void ) {
- if ( !common ) {
- return;
- }
- Printf( "------------ Game Shutdown -----------\n" );
- mpGame.Shutdown();
- MapShutdown();
- aasList.DeleteContents( true );
- aasNames.Clear();
- idAI::FreeObstacleAvoidanceNodes();
- // shutdown the model exporter
- idModelExport::Shutdown();
- idEvent::Shutdown();
- delete[] locationEntities;
- locationEntities = NULL;
- delete smokeParticles;
- smokeParticles = NULL;
- idClass::Shutdown();
- // clear list with forces
- idForce::ClearForceList();
- // free the program data
- program.FreeData();
- // delete the .map file
- delete mapFile;
- mapFile = NULL;
- // free the collision map
- collisionModelManager->FreeMap();
- ShutdownConsoleCommands();
- // free memory allocated by class objects
- Clear();
- // shut down the animation manager
- animationLib.Shutdown();
- Printf( "--------------------------------------\n" );
- #ifdef GAME_DLL
- // remove auto-completion function pointers pointing into this DLL
- cvarSystem->RemoveFlaggedAutoCompletion( CVAR_GAME );
- // enable leak test
- Mem_EnableLeakTest( "game" );
- // shutdown idLib
- idLib::ShutDown();
- #endif
- }
- /*
- ===========
- idGameLocal::SaveGame
- save the current player state, level name, and level state
- the session may have written some data to the file already
- ============
- */
- void idGameLocal::SaveGame( idFile *f ) {
- int i;
- idEntity *ent;
- idEntity *link;
- idSaveGame savegame( f );
- if (g_flushSave.GetBool( ) == true ) {
- // force flushing with each write... for tracking down
- // save game bugs.
- f->ForceFlush();
- }
- savegame.WriteBuildNumber( BUILD_NUMBER );
- // go through all entities and threads and add them to the object list
- for( i = 0; i < MAX_GENTITIES; i++ ) {
- ent = entities[i];
- if ( ent ) {
- if ( ent->GetTeamMaster() && ent->GetTeamMaster() != ent ) {
- continue;
- }
- for ( link = ent; link != NULL; link = link->GetNextTeamEntity() ) {
- savegame.AddObject( link );
- }
- }
- }
- idList<idThread *> threads;
- threads = idThread::GetThreads();
- for( i = 0; i < threads.Num(); i++ ) {
- savegame.AddObject( threads[i] );
- }
- // write out complete object list
- savegame.WriteObjectList();
- program.Save( &savegame );
- savegame.WriteInt( g_skill.GetInteger() );
- savegame.WriteDict( &serverInfo );
- savegame.WriteInt( numClients );
- for( i = 0; i < numClients; i++ ) {
- savegame.WriteDict( &userInfo[ i ] );
- savegame.WriteUsercmd( usercmds[ i ] );
- savegame.WriteDict( &persistentPlayerInfo[ i ] );
- }
- for( i = 0; i < MAX_GENTITIES; i++ ) {
- savegame.WriteObject( entities[ i ] );
- savegame.WriteInt( spawnIds[ i ] );
- }
- savegame.WriteInt( firstFreeIndex );
- savegame.WriteInt( num_entities );
- // enityHash is restored by idEntity::Restore setting the entity name.
- savegame.WriteObject( world );
- savegame.WriteInt( spawnedEntities.Num() );
- for( ent = spawnedEntities.Next(); ent != NULL; ent = ent->spawnNode.Next() ) {
- savegame.WriteObject( ent );
- }
- savegame.WriteInt( activeEntities.Num() );
- for( ent = activeEntities.Next(); ent != NULL; ent = ent->activeNode.Next() ) {
- savegame.WriteObject( ent );
- }
- savegame.WriteInt( numEntitiesToDeactivate );
- savegame.WriteBool( sortPushers );
- savegame.WriteBool( sortTeamMasters );
- savegame.WriteDict( &persistentLevelInfo );
- for( i = 0; i < MAX_GLOBAL_SHADER_PARMS; i++ ) {
- savegame.WriteFloat( globalShaderParms[ i ] );
- }
- savegame.WriteInt( random.GetSeed() );
- savegame.WriteObject( frameCommandThread );
- // clip
- // push
- // pvs
- testmodel = NULL;
- testFx = NULL;
- savegame.WriteString( sessionCommand );
- // FIXME: save smoke particles
- savegame.WriteInt( cinematicSkipTime );
- savegame.WriteInt( cinematicStopTime );
- savegame.WriteInt( cinematicMaxSkipTime );
- savegame.WriteBool( inCinematic );
- savegame.WriteBool( skipCinematic );
- savegame.WriteBool( isMultiplayer );
- savegame.WriteInt( gameType );
- savegame.WriteInt( framenum );
- savegame.WriteInt( previousTime );
- savegame.WriteInt( time );
- savegame.WriteInt( vacuumAreaNum );
- savegame.WriteInt( entityDefBits );
- savegame.WriteBool( isServer );
- savegame.WriteBool( isClient );
- savegame.WriteInt( localClientNum );
- // snapshotEntities is used for multiplayer only
- savegame.WriteInt( realClientTime );
- savegame.WriteBool( isNewFrame );
- savegame.WriteFloat( clientSmoothing );
- savegame.WriteBool( mapCycleLoaded );
- savegame.WriteInt( spawnCount );
- if ( !locationEntities ) {
- savegame.WriteInt( 0 );
- } else {
- savegame.WriteInt( gameRenderWorld->NumAreas() );
- for( i = 0; i < gameRenderWorld->NumAreas(); i++ ) {
- savegame.WriteObject( locationEntities[ i ] );
- }
- }
- savegame.WriteObject( camera );
- savegame.WriteMaterial( globalMaterial );
- lastAIAlertEntity.Save( &savegame );
- savegame.WriteInt( lastAIAlertTime );
- savegame.WriteDict( &spawnArgs );
- savegame.WriteInt( playerPVS.i );
- savegame.WriteInt( playerPVS.h );
- savegame.WriteInt( playerConnectedAreas.i );
- savegame.WriteInt( playerConnectedAreas.h );
- savegame.WriteVec3( gravity );
- // gamestate
- savegame.WriteBool( influenceActive );
- savegame.WriteInt( nextGibTime );
- // spawnSpots
- // initialSpots
- // currentInitialSpot
- // newInfo
- // makingBuild
- // shakeSounds
- // write out pending events
- idEvent::Save( &savegame );
- savegame.Close();
- }
- /*
- ===========
- idGameLocal::GetPersistentPlayerInfo
- ============
- */
- const idDict &idGameLocal::GetPersistentPlayerInfo( int clientNum ) {
- idEntity *ent;
- persistentPlayerInfo[ clientNum ].Clear();
- ent = entities[ clientNum ];
- if ( ent && ent->IsType( idPlayer::Type ) ) {
- static_cast<idPlayer *>(ent)->SavePersistantInfo();
- }
- return persistentPlayerInfo[ clientNum ];
- }
- /*
- ===========
- idGameLocal::SetPersistentPlayerInfo
- ============
- */
- void idGameLocal::SetPersistentPlayerInfo( int clientNum, const idDict &playerInfo ) {
- persistentPlayerInfo[ clientNum ] = playerInfo;
- }
- /*
- ============
- idGameLocal::Printf
- ============
- */
- void idGameLocal::Printf( const char *fmt, ... ) const {
- va_list argptr;
- char text[MAX_STRING_CHARS];
- va_start( argptr, fmt );
- idStr::vsnPrintf( text, sizeof( text ), fmt, argptr );
- va_end( argptr );
- common->Printf( "%s", text );
- }
- /*
- ============
- idGameLocal::DPrintf
- ============
- */
- void idGameLocal::DPrintf( const char *fmt, ... ) const {
- va_list argptr;
- char text[MAX_STRING_CHARS];
- if ( !developer.GetBool() ) {
- return;
- }
- va_start( argptr, fmt );
- idStr::vsnPrintf( text, sizeof( text ), fmt, argptr );
- va_end( argptr );
- common->Printf( "%s", text );
- }
- /*
- ============
- idGameLocal::Warning
- ============
- */
- void idGameLocal::Warning( const char *fmt, ... ) const {
- va_list argptr;
- char text[MAX_STRING_CHARS];
- idThread * thread;
- va_start( argptr, fmt );
- idStr::vsnPrintf( text, sizeof( text ), fmt, argptr );
- va_end( argptr );
- thread = idThread::CurrentThread();
- if ( thread ) {
- thread->Warning( "%s", text );
- } else {
- common->Warning( "%s", text );
- }
- }
- /*
- ============
- idGameLocal::DWarning
- ============
- */
- void idGameLocal::DWarning( const char *fmt, ... ) const {
- va_list argptr;
- char text[MAX_STRING_CHARS];
- idThread * thread;
- if ( !developer.GetBool() ) {
- return;
- }
- va_start( argptr, fmt );
- idStr::vsnPrintf( text, sizeof( text ), fmt, argptr );
- va_end( argptr );
- thread = idThread::CurrentThread();
- if ( thread ) {
- thread->Warning( "%s", text );
- } else {
- common->DWarning( "%s", text );
- }
- }
- /*
- ============
- idGameLocal::Error
- ============
- */
- void idGameLocal::Error( const char *fmt, ... ) const {
- va_list argptr;
- char text[MAX_STRING_CHARS];
- idThread * thread;
- va_start( argptr, fmt );
- idStr::vsnPrintf( text, sizeof( text ), fmt, argptr );
- va_end( argptr );
- thread = idThread::CurrentThread();
- if ( thread ) {
- thread->Error( "%s", text );
- } else {
- common->Error( "%s", text );
- }
- }
- /*
- ===============
- gameError
- ===============
- */
- void gameError( const char *fmt, ... ) {
- va_list argptr;
- char text[MAX_STRING_CHARS];
- va_start( argptr, fmt );
- idStr::vsnPrintf( text, sizeof( text ), fmt, argptr );
- va_end( argptr );
- gameLocal.Error( "%s", text );
- }
- /*
- ===========
- idGameLocal::SetLocalClient
- ============
- */
- void idGameLocal::SetLocalClient( int clientNum ) {
- localClientNum = clientNum;
- }
- /*
- ===========
- idGameLocal::SetUserInfo
- ============
- */
- const idDict* idGameLocal::SetUserInfo( int clientNum, const idDict &userInfo, bool isClient, bool canModify ) {
- int i;
- bool modifiedInfo = false;
- this->isClient = isClient;
- if ( clientNum >= 0 && clientNum < MAX_CLIENTS ) {
- idGameLocal::userInfo[ clientNum ] = userInfo;
- // server sanity
- if ( canModify ) {
- // don't let numeric nicknames, it can be exploited to go around kick and ban commands from the server
- if ( idStr::IsNumeric( this->userInfo[ clientNum ].GetString( "ui_name" ) ) ) {
- idGameLocal::userInfo[ clientNum ].Set( "ui_name", va( "%s_", idGameLocal::userInfo[ clientNum ].GetString( "ui_name" ) ) );
- modifiedInfo = true;
- }
-
- // don't allow dupe nicknames
- for ( i = 0; i < numClients; i++ ) {
- if ( i == clientNum ) {
- continue;
- }
- if ( entities[ i ] && entities[ i ]->IsType( idPlayer::Type ) ) {
- if ( !idStr::Icmp( idGameLocal::userInfo[ clientNum ].GetString( "ui_name" ), idGameLocal::userInfo[ i ].GetString( "ui_name" ) ) ) {
- idGameLocal::userInfo[ clientNum ].Set( "ui_name", va( "%s_", idGameLocal::userInfo[ clientNum ].GetString( "ui_name" ) ) );
- modifiedInfo = true;
- i = -1; // rescan
- continue;
- }
- }
- }
- }
- if ( entities[ clientNum ] && entities[ clientNum ]->IsType( idPlayer::Type ) ) {
- modifiedInfo |= static_cast<idPlayer *>( entities[ clientNum ] )->UserInfoChanged( canModify );
- }
- if ( !isClient ) {
- // now mark this client in game
- mpGame.EnterGame( clientNum );
- }
- }
- if ( modifiedInfo ) {
- assert( canModify );
- newInfo = idGameLocal::userInfo[ clientNum ];
- return &newInfo;
- }
- return NULL;
- }
- /*
- ===========
- idGameLocal::GetUserInfo
- ============
- */
- const idDict* idGameLocal::GetUserInfo( int clientNum ) {
- if ( entities[ clientNum ] && entities[ clientNum ]->IsType( idPlayer::Type ) ) {
- return &userInfo[ clientNum ];
- }
- return NULL;
- }
- /*
- ===========
- idGameLocal::SetServerInfo
- ============
- */
- void idGameLocal::SetServerInfo( const idDict &_serverInfo ) {
- idBitMsg outMsg;
- byte msgBuf[MAX_GAME_MESSAGE_SIZE];
- serverInfo = _serverInfo;
- UpdateServerInfoFlags();
- if ( !isClient ) {
- // Let our clients know the server info changed
- outMsg.Init( msgBuf, sizeof( msgBuf ) );
- outMsg.WriteByte( GAME_RELIABLE_MESSAGE_SERVERINFO );
- outMsg.WriteDeltaDict( gameLocal.serverInfo, NULL );
- networkSystem->ServerSendReliableMessage( -1, outMsg );
- }
- }
- /*
- ===================
- idGameLocal::LoadMap
- Initializes all map variables common to both save games and spawned games.
- ===================
- */
- void idGameLocal::LoadMap( const char *mapName, int randseed ) {
- int i;
- bool sameMap = (mapFile && idStr::Icmp(mapFileName, mapName) == 0);
- // clear the sound system
- gameSoundWorld->ClearAllSoundEmitters();
- InitAsyncNetwork();
- if ( !sameMap || ( mapFile && mapFile->NeedsReload() ) ) {
- // load the .map file
- if ( mapFile ) {
- delete mapFile;
- }
- mapFile = new idMapFile;
- if ( !mapFile->Parse( idStr( mapName ) + ".map" ) ) {
- delete mapFile;
- mapFile = NULL;
- Error( "Couldn't load %s", mapName );
- }
- }
- mapFileName = mapFile->GetName();
- // load the collision map
- collisionModelManager->LoadMap( mapFile );
- numClients = 0;
- // initialize all entities for this game
- memset( entities, 0, sizeof( entities ) );
- memset( usercmds, 0, sizeof( usercmds ) );
- memset( spawnIds, -1, sizeof( spawnIds ) );
- spawnCount = INITIAL_SPAWN_COUNT;
-
- spawnedEntities.Clear();
- activeEntities.Clear();
- numEntitiesToDeactivate = 0;
- sortTeamMasters = false;
- sortPushers = false;
- lastGUIEnt = NULL;
- lastGUI = 0;
- globalMaterial = NULL;
- memset( globalShaderParms, 0, sizeof( globalShaderParms ) );
- // always leave room for the max number of clients,
- // even if they aren't all used, so numbers inside that
- // range are NEVER anything but clients
- num_entities = MAX_CLIENTS;
- firstFreeIndex = MAX_CLIENTS;
- // reset the random number generator.
- random.SetSeed( isMultiplayer ? randseed : 0 );
- camera = NULL;
- world = NULL;
- testmodel = NULL;
- testFx = NULL;
- lastAIAlertEntity = NULL;
- lastAIAlertTime = 0;
-
- previousTime = 0;
- time = 0;
- framenum = 0;
- sessionCommand = "";
- nextGibTime = 0;
- vacuumAreaNum = -1; // if an info_vacuum is spawned, it will set this
- if ( !editEntities ) {
- editEntities = new idEditEntities;
- }
- gravity.Set( 0, 0, -g_gravity.GetFloat() );
- spawnArgs.Clear();
- skipCinematic = false;
- inCinematic = false;
- cinematicSkipTime = 0;
- cinematicStopTime = 0;
- cinematicMaxSkipTime = 0;
- clip.Init();
- pvs.Init();
- playerPVS.i = -1;
- playerConnectedAreas.i = -1;
- // load navigation system for all the different monster sizes
- for( i = 0; i < aasNames.Num(); i++ ) {
- aasList[ i ]->Init( idStr( mapFileName ).SetFileExtension( aasNames[ i ] ).c_str(), mapFile->GetGeometryCRC() );
- }
- // clear the smoke particle free list
- smokeParticles->Init();
- // cache miscellanious media references
- FindEntityDef( "preCacheExtras", false );
- if ( !sameMap ) {
- mapFile->RemovePrimitiveData();
- }
- }
- /*
- ===================
- idGameLocal::LocalMapRestart
- ===================
- */
- void idGameLocal::LocalMapRestart( ) {
- int i, latchSpawnCount;
- Printf( "----------- Game Map Restart ------------\n" );
- gamestate = GAMESTATE_SHUTDOWN;
- for ( i = 0; i < MAX_CLIENTS; i++ ) {
- if ( entities[ i ] && entities[ i ]->IsType( idPlayer::Type ) ) {
- static_cast< idPlayer * >( entities[ i ] )->PrepareForRestart();
- }
- }
- eventQueue.Shutdown();
- savedEventQueue.Shutdown();
- MapClear( false );
- // clear the smoke particle free list
- smokeParticles->Init();
- // clear the sound system
- if ( gameSoundWorld ) {
- gameSoundWorld->ClearAllSoundEmitters();
- }
- // the spawnCount is reset to zero temporarily to spawn the map entities with the same spawnId
- // if we don't do that, network clients are confused and don't show any map entities
- latchSpawnCount = spawnCount;
- spawnCount = INITIAL_SPAWN_COUNT;
- gamestate = GAMESTATE_STARTUP;
- program.Restart();
- InitScriptForMap();
- MapPopulate();
- // once the map is populated, set the spawnCount back to where it was so we don't risk any collision
- // (note that if there are no players in the game, we could just leave it at it's current value)
- spawnCount = latchSpawnCount;
- // setup the client entities again
- for ( i = 0; i < MAX_CLIENTS; i++ ) {
- if ( entities[ i ] && entities[ i ]->IsType( idPlayer::Type ) ) {
- static_cast< idPlayer * >( entities[ i ] )->Restart();
- }
- }
- gamestate = GAMESTATE_ACTIVE;
- Printf( "--------------------------------------\n" );
- }
- /*
- ===================
- idGameLocal::MapRestart
- ===================
- */
- void idGameLocal::MapRestart( ) {
- idBitMsg outMsg;
- byte msgBuf[MAX_GAME_MESSAGE_SIZE];
- idDict newInfo;
- int i;
- const idKeyValue *keyval, *keyval2;
- if ( isClient ) {
- LocalMapRestart();
- } else {
- newInfo = *cvarSystem->MoveCVarsToDict( CVAR_SERVERINFO );
- for ( i = 0; i < newInfo.GetNumKeyVals(); i++ ) {
- keyval = newInfo.GetKeyVal( i );
- keyval2 = serverInfo.FindKey( keyval->GetKey() );
- if ( !keyval2 ) {
- break;
- }
- // a select set of si_ changes will cause a full restart of the server
- if ( keyval->GetValue().Cmp( keyval2->GetValue() ) &&
- ( !keyval->GetKey().Cmp( "si_pure" ) || !keyval->GetKey().Cmp( "si_map" ) ) ) {
- break;
- }
- }
- cmdSystem->BufferCommandText( CMD_EXEC_NOW, "rescanSI" );
- if ( i != newInfo.GetNumKeyVals() ) {
- cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "nextMap" );
- } else {
- outMsg.Init( msgBuf, sizeof( msgBuf ) );
- outMsg.WriteByte( GAME_RELIABLE_MESSAGE_RESTART );
- outMsg.WriteBits( 1, 1 );
- outMsg.WriteDeltaDict( serverInfo, NULL );
- networkSystem->ServerSendReliableMessage( -1, outMsg );
- LocalMapRestart();
- mpGame.MapRestart();
- }
- }
- }
- /*
- ===================
- idGameLocal::MapRestart_f
- ===================
- */
- void idGameLocal::MapRestart_f( const idCmdArgs &args ) {
- if ( !gameLocal.isMultiplayer || gameLocal.isClient ) {
- common->Printf( "server is not running - use spawnServer\n" );
- cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "spawnServer\n" );
- return;
- }
- gameLocal.MapRestart( );
- }
- /*
- ===================
- idGameLocal::NextMap
- ===================
- */
- bool idGameLocal::NextMap( void ) {
- const function_t *func;
- idThread *thread;
- idDict newInfo;
- const idKeyValue *keyval, *keyval2;
- int i;
- if ( !g_mapCycle.GetString()[0] ) {
- Printf( common->GetLanguageDict()->GetString( "#str_04294" ) );
- return false;
- }
- if ( fileSystem->ReadFile( g_mapCycle.GetString(), NULL, NULL ) < 0 ) {
- if ( fileSystem->ReadFile( va( "%s.scriptcfg", g_mapCycle.GetString() ), NULL, NULL ) < 0 ) {
- Printf( "map cycle script '%s': not found\n", g_mapCycle.GetString() );
- return false;
- } else {
- g_mapCycle.SetString( va( "%s.scriptcfg", g_mapCycle.GetString() ) );
- }
- }
- Printf( "map cycle script: '%s'\n", g_mapCycle.GetString() );
- func = program.FindFunction( "mapcycle::cycle" );
- if ( !func ) {
- program.CompileFile( g_mapCycle.GetString() );
- func = program.FindFunction( "mapcycle::cycle" );
- }
- if ( !func ) {
- Printf( "Couldn't find mapcycle::cycle\n" );
- return false;
- }
- thread = new idThread( func );
- thread->Start();
- delete thread;
- newInfo = *cvarSystem->MoveCVarsToDict( CVAR_SERVERINFO );
- for ( i = 0; i < newInfo.GetNumKeyVals(); i++ ) {
- keyval = newInfo.GetKeyVal( i );
- keyval2 = serverInfo.FindKey( keyval->GetKey() );
- if ( !keyval2 || keyval->GetValue().Cmp( keyval2->GetValue() ) ) {
- break;
- }
- }
- return ( i != newInfo.GetNumKeyVals() );
- }
- /*
- ===================
- idGameLocal::NextMap_f
- ===================
- */
- void idGameLocal::NextMap_f( const idCmdArgs &args ) {
- if ( !gameLocal.isMultiplayer || gameLocal.isClient ) {
- common->Printf( "server is not running\n" );
- return;
- }
- gameLocal.NextMap( );
- // next map was either voted for or triggered by a server command - always restart
- gameLocal.MapRestart( );
- }
- /*
- ===================
- idGameLocal::MapPopulate
- ===================
- */
- void idGameLocal::MapPopulate( void ) {
- if ( isMultiplayer ) {
- cvarSystem->SetCVarBool( "r_skipSpecular", false );
- }
- // parse the key/value pairs and spawn entities
- SpawnMapEntities();
- // mark location entities in all connected areas
- SpreadLocations();
- // prepare the list of randomized initial spawn spots
- RandomizeInitialSpawns();
- // spawnCount - 1 is the number of entities spawned into the map, their indexes started at MAX_CLIENTS (included)
- // mapSpawnCount is used as the max index of map entities, it's the first index of non-map entities
- mapSpawnCount = MAX_CLIENTS + spawnCount - 1;
- // execute pending events before the very first game frame
- // this makes sure the map script main() function is called
- // before the physics are run so entities can bind correctly
- Printf( "==== Processing events ====\n" );
- idEvent::ServiceEvents();
- }
- /*
- ===================
- idGameLocal::InitFromNewMap
- ===================
- */
- void idGameLocal::InitFromNewMap( const char *mapName, idRenderWorld *renderWorld, idSoundWorld *soundWorld, bool isServer, bool isClient, int randseed ) {
- this->isServer = isServer;
- this->isClient = isClient;
- this->isMultiplayer = isServer || isClient;
- if ( mapFileName.Length() ) {
- MapShutdown();
- }
- Printf( "----------- Game Map Init ------------\n" );
- gamestate = GAMESTATE_STARTUP;
- gameRenderWorld = renderWorld;
- gameSoundWorld = soundWorld;
- LoadMap( mapName, randseed );
- InitScriptForMap();
- MapPopulate();
- mpGame.Reset();
- mpGame.Precache();
- // free up any unused animations
- animationLib.FlushUnusedAnims();
- gamestate = GAMESTATE_ACTIVE;
- Printf( "--------------------------------------\n" );
- }
- /*
- =================
- idGameLocal::InitFromSaveGame
- =================
- */
- bool idGameLocal::InitFromSaveGame( const char *mapName, idRenderWorld *renderWorld, idSoundWorld *soundWorld, idFile *saveGameFile ) {
- int i;
- int num;
- idEntity *ent;
- idDict si;
- if ( mapFileName.Length() ) {
- MapShutdown();
- }
- Printf( "------- Game Map Init SaveGame -------\n" );
- gamestate = GAMESTATE_STARTUP;
- gameRenderWorld = renderWorld;
- gameSoundWorld = soundWorld;
- idRestoreGame savegame( saveGameFile );
- savegame.ReadBuildNumber();
- // Create the list of all objects in the game
- savegame.CreateObjects();
- // Load the idProgram, also checking to make sure scripting hasn't changed since the savegame
- if ( program.Restore( &savegame ) == false ) {
- // Abort the load process, and let the session know so that it can restart the level
- // with the player persistent data.
- savegame.DeleteObjects();
- program.Restart();
- return false;
- }
- // load the map needed for this savegame
- LoadMap( mapName, 0 );
- savegame.ReadInt( i );
- g_skill.SetInteger( i );
- // precache the player
- FindEntityDef( "player_doommarine", false );
- // precache any media specified in the map
- for ( i = 0; i < mapFile->GetNumEntities(); i++ ) {
- idMapEntity *mapEnt = mapFile->GetEntity( i );
- if ( !InhibitEntitySpawn( mapEnt->epairs ) ) {
- CacheDictionaryMedia( &mapEnt->epairs );
- const char *classname = mapEnt->epairs.GetString( "classname" );
- if ( classname != '\0' ) {
- FindEntityDef( classname, false );
- }
- }
- }
- savegame.ReadDict( &si );
- SetServerInfo( si );
- savegame.ReadInt( numClients );
- for( i = 0; i < numClients; i++ ) {
- savegame.ReadDict( &userInfo[ i ] );
- savegame.ReadUsercmd( usercmds[ i ] );
- savegame.ReadDict( &persistentPlayerInfo[ i ] );
- }
- for( i = 0; i < MAX_GENTITIES; i++ ) {
- savegame.ReadObject( reinterpret_cast<idClass *&>( entities[ i ] ) );
- savegame.ReadInt( spawnIds[ i ] );
- // restore the entityNumber
- if ( entities[ i ] != NULL ) {
- entities[ i ]->entityNumber = i;
- }
- }
- savegame.ReadInt( firstFreeIndex );
- savegame.ReadInt( num_entities );
- // enityHash is restored by idEntity::Restore setting the entity name.
- savegame.ReadObject( reinterpret_cast<idClass *&>( world ) );
- savegame.ReadInt( num );
- for( i = 0; i < num; i++ ) {
- savegame.ReadObject( reinterpret_cast<idClass *&>( ent ) );
- assert( ent );
- if ( ent ) {
- ent->spawnNode.AddToEnd( spawnedEntities );
- }
- }
- savegame.ReadInt( num );
- for( i = 0; i < num; i++ ) {
- savegame.ReadObject( reinterpret_cast<idClass *&>( ent ) );
- assert( ent );
- if ( ent ) {
- ent->activeNode.AddToEnd( activeEntities );
- }
- }
- savegame.ReadInt( numEntitiesToDeactivate );
- savegame.ReadBool( sortPushers );
- savegame.ReadBool( sortTeamMasters );
- savegame.ReadDict( &persistentLevelInfo );
- for( i = 0; i < MAX_GLOBAL_SHADER_PARMS; i++ ) {
- savegame.ReadFloat( globalShaderParms[ i ] );
- }
- savegame.ReadInt( i );
- random.SetSeed( i );
- savegame.ReadObject( reinterpret_cast<idClass *&>( frameCommandThread ) );
- // clip
- // push
- // pvs
- // testmodel = "<NULL>"
- // testFx = "<NULL>"
- savegame.ReadString( sessionCommand );
- // FIXME: save smoke particles
- savegame.ReadInt( cinematicSkipTime );
- savegame.ReadInt( cinematicStopTime );
- savegame.ReadInt( cinematicMaxSkipTime );
- savegame.ReadBool( inCinematic );
- savegame.ReadBool( skipCinematic );
- savegame.ReadBool( isMultiplayer );
- savegame.ReadInt( (int &)gameType );
- savegame.ReadInt( framenum );
- savegame.ReadInt( previousTime );
- savegame.ReadInt( time );
- savegame.ReadInt( vacuumAreaNum );
- savegame.ReadInt( entityDefBits );
- savegame.ReadBool( isServer );
- savegame.ReadBool( isClient );
- savegame.ReadInt( localClientNum );
- // snapshotEntities is used for multiplayer only
- savegame.ReadInt( realClientTime );
- savegame.ReadBool( isNewFrame );
- savegame.ReadFloat( clientSmoothing );
- savegame.ReadBool( mapCycleLoaded );
- savegame.ReadInt( spawnCount );
- savegame.ReadInt( num );
- if ( num ) {
- if ( num != gameRenderWorld->NumAreas() ) {
- savegame.Error( "idGameLocal::InitFromSaveGame: number of areas in map differs from save game." );
- }
- locationEntities = new idLocationEntity *[ num ];
- for( i = 0; i < num; i++ ) {
- savegame.ReadObject( reinterpret_cast<idClass *&>( locationEntities[ i ] ) );
- }
- }
- savegame.ReadObject( reinterpret_cast<idClass *&>( camera ) );
- savegame.ReadMaterial( globalMaterial );
- lastAIAlertEntity.Restore( &savegame );
- savegame.ReadInt( lastAIAlertTime );
- savegame.ReadDict( &spawnArgs );
- savegame.ReadInt( playerPVS.i );
- savegame.ReadInt( (int &)playerPVS.h );
- savegame.ReadInt( playerConnectedAreas.i );
- savegame.ReadInt( (int &)playerConnectedAreas.h );
- savegame.ReadVec3( gravity );
- // gamestate is restored after restoring everything else
- savegame.ReadBool( influenceActive );
- savegame.ReadInt( nextGibTime );
- // spawnSpots
- // initialSpots
- // currentInitialSpot
- // newInfo
- // makingBuild
- // shakeSounds
- // Read out pending events
- idEvent::Restore( &savegame );
- savegame.RestoreObjects();
- mpGame.Reset();
- mpGame.Precache();
- // free up any unused animations
- animationLib.FlushUnusedAnims();
- gamestate = GAMESTATE_ACTIVE;
- Printf( "--------------------------------------\n" );
- return true;
- }
- /*
- ===========
- idGameLocal::MapClear
- ===========
- */
- void idGameLocal::MapClear( bool clearClients ) {
- int i;
- for( i = ( clearClients ? 0 : MAX_CLIENTS ); i < MAX_GENTITIES; i++ ) {
- delete entities[ i ];
- // ~idEntity is in charge of setting the pointer to NULL
- // it will also clear pending events for this entity
- assert( !entities[ i ] );
- spawnIds[ i ] = -1;
- }
- entityHash.Clear( 1024, MAX_GENTITIES );
- if ( !clearClients ) {
- // add back the hashes of the clients
- for ( i = 0; i < MAX_CLIENTS; i++ ) {
- if ( !entities[ i ] ) {
- continue;
- }
- entityHash.Add( entityHash.GenerateKey( entities[ i ]->name.c_str(), true ), i );
- }
- }
- delete frameCommandThread;
- frameCommandThread = NULL;
- if ( editEntities ) {
- delete editEntities;
- editEntities = NULL;
- }
- delete[] locationEntities;
- locationEntities = NULL;
- }
- /*
- ===========
- idGameLocal::MapShutdown
- ============
- */
- void idGameLocal::MapShutdown( void ) {
- Printf( "--------- Game Map Shutdown ----------\n" );
-
- gamestate = GAMESTATE_SHUTDOWN;
- if ( gameRenderWorld ) {
- // clear any debug lines, text, and polygons
- gameRenderWorld->DebugClearLines( 0 );
- gameRenderWorld->DebugClearPolygons( 0 );
- }
- // clear out camera if we're in a cinematic
- if ( inCinematic ) {
- camera = NULL;
- inCinematic = false;
- }
- MapClear( true );
- // reset the script to the state it was before the map was started
- program.Restart();
- if ( smokeParticles ) {
- smokeParticles->Shutdown();
- }
- pvs.Shutdown();
- clip.Shutdown();
- idClipModel::ClearTraceModelCache();
- ShutdownAsyncNetwork();
- mapFileName.Clear();
- gameRenderWorld = NULL;
- gameSoundWorld = NULL;
- gamestate = GAMESTATE_NOMAP;
- Printf( "--------------------------------------\n" );
- }
- /*
- ===================
- idGameLocal::DumpOggSounds
- ===================
- */
- void idGameLocal::DumpOggSounds( void ) {
- int i, j, k, size, totalSize;
- idFile *file;
- idStrList oggSounds, weaponSounds;
- const idSoundShader *soundShader;
- const soundShaderParms_t *parms;
- idStr soundName;
- for ( i = 0; i < declManager->GetNumDecls( DECL_SOUND ); i++ ) {
- soundShader = static_cast<const idSoundShader *>(declManager->DeclByIndex( DECL_SOUND, i, false ));
- parms = soundShader->GetParms();
- if ( soundShader->EverReferenced() && soundShader->GetState() != DS_DEFAULTED ) {
- const_cast<idSoundShader *>(soundShader)->EnsureNotPurged();
- for ( j = 0; j < soundShader->GetNumSounds(); j++ ) {
- soundName = soundShader->GetSound( j );
- soundName.BackSlashesToSlashes();
- // don't OGG sounds that cause a shake because that would
- // cause continuous seeking on the OGG file which is expensive
- if ( parms->shakes != 0.0f ) {
- shakeSounds.AddUnique( soundName );
- continue;
- }
- // if not voice over or combat chatter
- if ( soundName.Find( "/vo/", false ) == -1 &&
- soundName.Find( "/combat_chatter/", false ) == -1 &&
- soundName.Find( "/bfgcarnage/", false ) == -1 &&
- soundName.Find( "/enpro/", false ) == - 1 &&
- soundName.Find( "/soulcube/energize_01.wav", false ) == -1 ) {
- // don't OGG weapon sounds
- if ( soundName.Find( "weapon", false ) != -1 ||
- soundName.Find( "gun", false ) != -1 ||
- soundName.Find( "bullet", false ) != -1 ||
- soundName.Find( "bfg", false ) != -1 ||
- soundName.Find( "plasma", false ) != -1 ) {
- weaponSounds.AddUnique( soundName );
- continue;
- }
- }
- for ( k = 0; k < shakeSounds.Num(); k++ ) {
- if ( shakeSounds[k].IcmpPath( soundName ) == 0 ) {
- break;
- }
- }
- if ( k < shakeSounds.Num() ) {
- continue;
- }
- oggSounds.AddUnique( soundName );
- }
- }
- }
- file = fileSystem->OpenFileWrite( "makeogg.bat", "fs_savepath" );
- if ( file == NULL ) {
- common->Warning( "Couldn't open makeogg.bat" );
- return;
- }
- // list all the shake sounds
- totalSize = 0;
- for ( i = 0; i < shakeSounds.Num(); i++ ) {
- size = fileSystem->ReadFile( shakeSounds[i], NULL, NULL );
- totalSize += size;
- shakeSounds[i].Replace( "/", "\\" );
- file->Printf( "echo \"%s\" (%d kB)\n", shakeSounds[i].c_str(), size >> 10 );
- }
- file->Printf( "echo %d kB in shake sounds\n\n\n", totalSize >> 10 );
- // list all the weapon sounds
- totalSize = 0;
- for ( i = 0; i < weaponSounds.Num(); i++ ) {
- size = fileSystem->ReadFile( weaponSounds[i], NULL, NULL );
- totalSize += size;
- weaponSounds[i].Replace( "/", "\\" );
- file->Printf( "echo \"%s\" (%d kB)\n", weaponSounds[i].c_str(), size >> 10 );
- }
- file->Printf( "echo %d kB in weapon sounds\n\n\n", totalSize >> 10 );
- // list commands to convert all other sounds to ogg
- totalSize = 0;
- for ( i = 0; i < oggSounds.Num(); i++ ) {
- size = fileSystem->ReadFile( oggSounds[i], NULL, NULL );
- totalSize += size;
- oggSounds[i].Replace( "/", "\\" );
- file->Printf( "w:\\doom\\ogg\\oggenc -q 0 \"c:\\doom\\base\\%s\"\n", oggSounds[i].c_str() );
- file->Printf( "del \"c:\\doom\\base\\%s\"\n", oggSounds[i].c_str() );
- }
- file->Printf( "\n\necho %d kB in OGG sounds\n\n\n", totalSize >> 10 );
- fileSystem->CloseFile( file );
- shakeSounds.Clear();
- }
- /*
- ===================
- idGameLocal::GetShakeSounds
- ===================
- */
- void idGameLocal::GetShakeSounds( const idDict *dict ) {
- const idSoundShader *soundShader;
- const char *soundShaderName;
- idStr soundName;
- soundShaderName = dict->GetString( "s_shader" );
- if ( soundShaderName != '\0' && dict->GetFloat( "s_shakes" ) != 0.0f ) {
- soundShader = declManager->FindSound( soundShaderName );
- for ( int i = 0; i < soundShader->GetNumSounds(); i++ ) {
- soundName = soundShader->GetSound( i );
- soundName.BackSlashesToSlashes();
- shakeSounds.AddUnique( soundName );
- }
- }
- }
- /*
- ===================
- idGameLocal::CacheDictionaryMedia
- This is called after parsing an EntityDef and for each entity spawnArgs before
- merging the entitydef. It could be done post-merge, but that would
- avoid the fast pre-cache check associated with each entityDef
- ===================
- */
- void idGameLocal::CacheDictionaryMedia( const idDict *dict ) {
- const idKeyValue *kv;
- if ( dict == NULL ) {
- if ( cvarSystem->GetCVarBool( "com_makingBuild") ) {
- DumpOggSounds();
- }
- return;
- }
- if ( cvarSystem->GetCVarBool( "com_makingBuild" ) ) {
- GetShakeSounds( dict );
- }
- kv = dict->MatchPrefix( "model" );
- while( kv ) {
- if ( kv->GetValue().Length() ) {
- declManager->MediaPrint( "Precaching model %s\n", kv->GetValue().c_str() );
- // precache model/animations
- if ( declManager->FindType( DECL_MODELDEF, kv->GetValue(), false ) == NULL ) {
- // precache the render model
- renderModelManager->FindModel( kv->GetValue() );
- // precache .cm files only
- collisionModelManager->LoadModel( kv->GetValue(), true );
- }
- }
- kv = dict->MatchPrefix( "model", kv );
- }
- kv = dict->FindKey( "s_shader" );
- if ( kv && kv->GetValue().Length() ) {
- declManager->FindType( DECL_SOUND, kv->GetValue() );
- }
- kv = dict->MatchPrefix( "snd", NULL );
- while( kv ) {
- if ( kv->GetValue().Length() ) {
- declManager->FindType( DECL_SOUND, kv->GetValue() );
- }
- kv = dict->MatchPrefix( "snd", kv );
- }
- kv = dict->MatchPrefix( "gui", NULL );
- while( kv ) {
- if ( kv->GetValue().Length() ) {
- if ( !idStr::Icmp( kv->GetKey(), "gui_noninteractive" )
- || !idStr::Icmpn( kv->GetKey(), "gui_parm", 8 )
- || !idStr::Icmp( kv->GetKey(), "gui_inventory" ) ) {
- // unfortunate flag names, they aren't actually a gui
- } else {
- declManager->MediaPrint( "Precaching gui %s\n", kv->GetValue().c_str() );
- idUserInterface *gui = uiManager->Alloc();
- if ( gui ) {
- gui->InitFromFile( kv->GetValue() );
- uiManager->DeAlloc( gui );
- }
- }
- }
- kv = dict->MatchPrefix( "gui", kv );
- }
- kv = dict->FindKey( "texture" );
- if ( kv && kv->GetValue().Length() ) {
- declManager->FindType( DECL_MATERIAL, kv->GetValue() );
- }
- kv = dict->MatchPrefix( "mtr", NULL );
- while( kv ) {
- if ( kv->GetValue().Length() ) {
- declManager->FindType( DECL_MATERIAL, kv->GetValue() );
- }
- kv = dict->MatchPrefix( "mtr", kv );
- }
- // handles hud icons
- kv = dict->MatchPrefix( "inv_icon", NULL );
- while ( kv ) {
- if ( kv->GetValue().Length() ) {
- declManager->FindType( DECL_MATERIAL, kv->GetValue() );
- }
- kv = dict->MatchPrefix( "inv_icon", kv );
- }
- // handles teleport fx.. this is not ideal but the actual decision on which fx to use
- // is handled by script code based on the teleport number
- kv = dict->MatchPrefix( "teleport", NULL );
- if ( kv && kv->GetValue().Length() ) {
- int teleportType = atoi( kv->GetValue() );
- const char *p = ( teleportType ) ? va( "fx/teleporter%i.fx", teleportType ) : "fx/teleporter.fx";
- declManager->FindType( DECL_FX, p );
- }
- kv = dict->MatchPrefix( "fx", NULL );
- while( kv ) {
- if ( kv->GetValue().Length() ) {
- declManager->MediaPrint( "Precaching fx %s\n", kv->GetValue().c_str() );
- declManager->FindType( DECL_FX, kv->GetValue() );
- }
- kv = dict->MatchPrefix( "fx", kv );
- }
- kv = dict->MatchPrefix( "smoke", NULL );
- while( kv ) {
- if ( kv->GetValue().Length() ) {
- idStr prtName = kv->GetValue();
- int dash = prtName.Find('-');
- if ( dash > 0 ) {
- prtName = prtName.Left( dash );
- }
- declManager->FindType( DECL_PARTICLE, prtName );
- }
- kv = dict->MatchPrefix( "smoke", kv );
- }
- kv = dict->MatchPrefix( "skin", NULL );
- while( kv ) {
- if ( kv->GetValue().Length() ) {
- declManager->MediaPrint( "Precaching skin %s\n", kv->GetValue().c_str() );
- declManager->FindType( DECL_SKIN, kv->GetValue() );
- }
- kv = dict->MatchPrefix( "skin", kv );
- }
- kv = dict->MatchPrefix( "def", NULL );
- while( kv ) {
- if ( kv->GetValue().Length() ) {
- FindEntityDef( kv->GetValue().c_str(), false );
- }
- kv = dict->MatchPrefix( "def", kv );
- }
- kv = dict->MatchPrefix( "pda_name", NULL );
- while( kv ) {
- if ( kv->GetValue().Length() ) {
- declManager->FindType( DECL_PDA, kv->GetValue().c_str(), false );
- }
- kv = dict->MatchPrefix( "pda_name", kv );
- }
- kv = dict->MatchPrefix( "video", NULL );
- while( kv ) {
- if ( kv->GetValue().Length() ) {
- declManager->FindType( DECL_VIDEO, kv->GetValue().c_str(), false );
- }
- kv = dict->MatchPrefix( "video", kv );
- }
- kv = dict->MatchPrefix( "audio", NULL );
- while( kv ) {
- if ( kv->GetValue().Length() ) {
- declManager->FindType( DECL_AUDIO, kv->GetValue().c_str(), false );
- }
- kv = dict->MatchPrefix( "audio", kv );
- }
- }
- /*
- ===========
- idGameLocal::InitScriptForMap
- ============
- */
- void idGameLocal::InitScriptForMap( void ) {
- // create a thread to run frame commands on
- frameCommandThread = new idThread();
- frameCommandThread->ManualDelete();
- frameCommandThread->SetThreadName( "frameCommands" );
- // run the main game script function (not the level specific main)
- const function_t *func = program.FindFunction( SCRIPT_DEFAULTFUNC );
- if ( func != NULL ) {
- idThread *thread = new idThread( func );
- if ( thread->Start() ) {
- // thread has finished executing, so delete it
- delete thread;
- }
- }
- }
- /*
- ===========
- idGameLocal::SpawnPlayer
- ============
- */
- void idGameLocal::SpawnPlayer( int clientNum ) {
- idEntity *ent;
- idDict args;
- // they can connect
- Printf( "SpawnPlayer: %i\n", clientNum );
- args.SetInt( "spawn_entnum", clientNum );
- args.Set( "name", va( "player%d", clientNum + 1 ) );
- args.Set( "classname", isMultiplayer ? "player_doommarine_mp" : "player_doommarine" );
- if ( !SpawnEntityDef( args, &ent ) || !entities[ clientNum ] ) {
- Error( "Failed to spawn player as '%s'", args.GetString( "classname" ) );
- }
- // make sure it's a compatible class
- if ( !ent->IsType( idPlayer::Type ) ) {
- Error( "'%s' spawned the player as a '%s'. Player spawnclass must be a subclass of idPlayer.", args.GetString( "classname" ), ent->GetClassname() );
- }
- if ( clientNum >= numClients ) {
- numClients = clientNum + 1;
- }
- mpGame.SpawnPlayer( clientNum );
- }
- /*
- ================
- idGameLocal::GetClientByNum
- ================
- */
- idPlayer *idGameLocal::GetClientByNum( int current ) const {
- if ( current < 0 || current >= numClients ) {
- current = 0;
- }
- if ( entities[current] ) {
- return static_cast<idPlayer *>( entities[ current ] );
- }
- return NULL;
- }
- /*
- ================
- idGameLocal::GetClientByName
- ================
- */
- idPlayer *idGameLocal::GetClientByName( const char *name ) const {
- int i;
- idEntity *ent;
- for ( i = 0 ; i < numClients ; i++ ) {
- ent = entities[ i ];
- if ( ent && ent->IsType( idPlayer::Type ) ) {
- if ( idStr::IcmpNoColor( name, userInfo[ i ].GetString( "ui_name" ) ) == 0 ) {
- return static_cast<idPlayer *>( ent );
- }
- }
- }
- return NULL;
- }
- /*
- ================
- idGameLocal::GetClientByCmdArgs
- ================
- */
- idPlayer *idGameLocal::GetClientByCmdArgs( const idCmdArgs &args ) const {
- idPlayer *player;
- idStr client = args.Argv( 1 );
- if ( !client.Length() ) {
- return NULL;
- }
- // we don't allow numeric ui_name so this can't go wrong
- if ( client.IsNumeric() ) {
- player = GetClientByNum( atoi( client.c_str() ) );
- } else {
- player = GetClientByName( client.c_str() );
- }
- if ( !player ) {
- common->Printf( "Player '%s' not found\n", client.c_str() );
- }
- return player;
- }
- /*
- ================
- idGameLocal::GetNextClientNum
- ================
- */
- int idGameLocal::GetNextClientNum( int _current ) const {
- int i, current;
- current = 0;
- for ( i = 0; i < numClients; i++) {
- current = ( _current + i + 1 ) % numClients;
- if ( entities[ current ] && entities[ current ]->IsType( idPlayer::Type ) ) {
- return current;
- }
- }
- return current;
- }
- /*
- ================
- idGameLocal::GetLocalPlayer
- Nothing in the game tic should EVER make a decision based on what the
- local client number is, it shouldn't even be aware that there is a
- draw phase even happening. This just returns client 0, which will
- be correct for single player.
- ================
- */
- idPlayer *idGameLocal::GetLocalPlayer() const {
- if ( localClientNum < 0 ) {
- return NULL;
- }
- if ( !entities[ localClientNum ] || !entities[ localClientNum ]->IsType( idPlayer::Type ) ) {
- // not fully in game yet
- return NULL;
- }
- return static_cast<idPlayer *>( entities[ localClientNum ] );
- }
- /*
- ================
- idGameLocal::SetupClientPVS
- ================
- */
- pvsHandle_t idGameLocal::GetClientPVS( idPlayer *player, pvsType_t type ) {
- if ( player->GetPrivateCameraView() ) {
- return pvs.SetupCurrentPVS( player->GetPrivateCameraView()->GetPVSAreas(), player->GetPrivateCameraView()->GetNumPVSAreas() );
- } else if ( camera ) {
- return pvs.SetupCurrentPVS( camera->GetPVSAreas(), camera->GetNumPVSAreas() );
- } else {
- return pvs.SetupCurrentPVS( player->GetPVSAreas(), player->GetNumPVSAreas() );
- }
- }
- /*
- ================
- idGameLocal::SetupPlayerPVS
- ================
- */
- void idGameLocal::SetupPlayerPVS( void ) {
- int i;
- idEntity * ent;
- idPlayer * player;
- pvsHandle_t otherPVS, newPVS;
- playerPVS.i = -1;
- for ( i = 0; i < numClients; i++ ) {
- ent = entities[i];
- if ( !ent || !ent->IsType( idPlayer::Type ) ) {
- continue;
- }
- player = static_cast<idPlayer *>(ent);
- if ( playerPVS.i == -1 ) {
- playerPVS = GetClientPVS( player, PVS_NORMAL );
- } else {
- otherPVS = GetClientPVS( player, PVS_NORMAL );
- newPVS = pvs.MergeCurrentPVS( playerPVS, otherPVS );
- pvs.FreeCurrentPVS( playerPVS );
- pvs.FreeCurrentPVS( otherPVS );
- playerPVS = newPVS;
- }
- if ( playerConnectedAreas.i == -1 ) {
- playerConnectedAreas = GetClientPVS( player, PVS_CONNECTED_AREAS );
- } else {
- otherPVS = GetClientPVS( player, PVS_CONNECTED_AREAS );
- newPVS = pvs.MergeCurrentPVS( playerConnectedAreas, otherPVS );
- pvs.FreeCurrentPVS( playerConnectedAreas );
- pvs.FreeCurrentPVS( otherPVS );
- playerConnectedAreas = newPVS;
- }
- }
- }
- /*
- ================
- idGameLocal::FreePlayerPVS
- ================
- */
- void idGameLocal::FreePlayerPVS( void ) {
- if ( playerPVS.i != -1 ) {
- pvs.FreeCurrentPVS( playerPVS );
- playerPVS.i = -1;
- }
- if ( playerConnectedAreas.i != -1 ) {
- pvs.FreeCurrentPVS( playerConnectedAreas );
- playerConnectedAreas.i = -1;
- }
- }
- /*
- ================
- idGameLocal::InPlayerPVS
- should only be called during entity thinking and event handling
- ================
- */
- bool idGameLocal::InPlayerPVS( idEntity *ent ) const {
- if ( playerPVS.i == -1 ) {
- return false;
- }
- return pvs.InCurrentPVS( playerPVS, ent->GetPVSAreas(), ent->GetNumPVSAreas() );
- }
- /*
- ================
- idGameLocal::InPlayerConnectedArea
- should only be called during entity thinking and event handling
- ================
- */
- bool idGameLocal::InPlayerConnectedArea( idEntity *ent ) const {
- if ( playerConnectedAreas.i == -1 ) {
- return false;
- }
- return pvs.InCurrentPVS( playerConnectedAreas, ent->GetPVSAreas(), ent->GetNumPVSAreas() );
- }
- /*
- ================
- idGameLocal::UpdateGravity
- ================
- */
- void idGameLocal::UpdateGravity( void ) {
- idEntity *ent;
- if ( g_gravity.IsModified() ) {
- if ( g_gravity.GetFloat() == 0.0f ) {
- g_gravity.SetFloat( 1.0f );
- }
- gravity.Set( 0, 0, -g_gravity.GetFloat() );
- // update all physics objects
- for( ent = spawnedEntities.Next(); ent != NULL; ent = ent->spawnNode.Next() ) {
- if ( ent->IsType( idAFEntity_Generic::Type ) ) {
- idPhysics *phys = ent->GetPhysics();
- if ( phys ) {
- phys->SetGravity( gravity );
- }
- }
- }
- g_gravity.ClearModified();
- }
- }
- /*
- ================
- idGameLocal::GetGravity
- ================
- */
- const idVec3 &idGameLocal::GetGravity( void ) const {
- return gravity;
- }
- /*
- ================
- idGameLocal::SortActiveEntityList
- Sorts the active entity list such that pushing entities come first,
- actors come next and physics team slaves appear after their master.
- ================
- */
- void idGameLocal::SortActiveEntityList( void ) {
- idEntity *ent, *next_ent, *master, *part;
- // if the active entity list needs to be reordered to place physics team masters at the front
- if ( sortTeamMasters ) {
- for ( ent = activeEntities.Next(); ent != NULL; ent = next_ent ) {
- next_ent = ent->activeNode.Next();
- master = ent->GetTeamMaster();
- if ( master && master == ent ) {
- ent->activeNode.Remove();
- ent->activeNode.AddToFront( activeEntities );
- }
- }
- }
- // if the active entity list needs to be reordered to place pushers at the front
- if ( sortPushers ) {
- for ( ent = activeEntities.Next(); ent != NULL; ent = next_ent ) {
- next_ent = ent->activeNode.Next();
- master = ent->GetTeamMaster();
- if ( !master || master == ent ) {
- // check if there is an actor on the team
- for ( part = ent; part != NULL; part = part->GetNextTeamEntity() ) {
- if ( part->GetPhysics()->IsType( idPhysics_Actor::Type ) ) {
- break;
- }
- }
- // if there is an actor on the team
- if ( part ) {
- ent->activeNode.Remove();
- ent->activeNode.AddToFront( activeEntities );
- }
- }
- }
- for ( ent = activeEntities.Next(); ent != NULL; ent = next_ent ) {
- next_ent = ent->activeNode.Next();
- master = ent->GetTeamMaster();
- if ( !master || master == ent ) {
- // check if there is an entity on the team using parametric physics
- for ( part = ent; part != NULL; part = part->GetNextTeamEntity() ) {
- if ( part->GetPhysics()->IsType( idPhysics_Parametric::Type ) ) {
- break;
- }
- }
- // if there is an entity on the team using parametric physics
- if ( part ) {
- ent->activeNode.Remove();
- ent->activeNode.AddToFront( activeEntities );
- }
- }
- }
- }
- sortTeamMasters = false;
- sortPushers = false;
- }
- /*
- ================
- idGameLocal::RunFrame
- ================
- */
- gameReturn_t idGameLocal::RunFrame( const usercmd_t *clientCmds ) {
- idEntity * ent;
- int num;
- float ms;
- idTimer timer_think, timer_events, timer_singlethink;
- gameReturn_t ret;
- idPlayer *player;
- const renderView_t *view;
- #ifdef _DEBUG
- if ( isMultiplayer ) {
- assert( !isClient );
- }
- #endif
- player = GetLocalPlayer();
- if ( !isMultiplayer && g_stopTime.GetBool() ) {
- // clear any debug lines from a previous frame
- gameRenderWorld->DebugClearLines( time + 1 );
- // set the user commands for this frame
- memcpy( usercmds, clientCmds, numClients * sizeof( usercmds[ 0 ] ) );
- if ( player ) {
- player->Think();
- }
- } else do {
- // update the game time
- framenum++;
- previousTime = time;
- time += msec;
- realClientTime = time;
- #ifdef GAME_DLL
- // allow changing SIMD usage on the fly
- if ( com_forceGenericSIMD.IsModified() ) {
- idSIMD::InitProcessor( "game", com_forceGenericSIMD.GetBool() );
- }
- #endif
- // make sure the random number counter is used each frame so random events
- // are influenced by the player's actions
- random.RandomInt();
- if ( player ) {
- // update the renderview so that any gui videos play from the right frame
- view = player->GetRenderView();
- if ( view ) {
- gameRenderWorld->SetRenderView( view );
- }
- }
- // clear any debug lines from a previous frame
- gameRenderWorld->DebugClearLines( time );
- // clear any debug polygons from a previous frame
- gameRenderWorld->DebugClearPolygons( time );
- // set the user commands for this frame
- memcpy( usercmds, clientCmds, numClients * sizeof( usercmds[ 0 ] ) );
- // free old smoke particles
- smokeParticles->FreeSmokes();
- // process events on the server
- ServerProcessEntityNetworkEventQueue();
- // update our gravity vector if needed.
- UpdateGravity();
- // create a merged pvs for all players
- SetupPlayerPVS();
- // sort the active entity list
- SortActiveEntityList();
- timer_think.Clear();
- timer_think.Start();
- // let entities think
- if ( g_timeentities.GetFloat() ) {
- num = 0;
- for( ent = activeEntities.Next(); ent != NULL; ent = ent->activeNode.Next() ) {
- if ( g_cinematic.GetBool() && inCinematic && !ent->cinematic ) {
- ent->GetPhysics()->UpdateTime( time );
- continue;
- }
- timer_singlethink.Clear();
- timer_singlethink.Start();
- ent->Think();
- timer_singlethink.Stop();
- ms = timer_singlethink.Milliseconds();
- if ( ms >= g_timeentities.GetFloat() ) {
- Printf( "%d: entity '%s': %.1f ms\n", time, ent->name.c_str(), ms );
- }
- num++;
- }
- } else {
- if ( inCinematic ) {
- num = 0;
- for( ent = activeEntities.Next(); ent != NULL; ent = ent->activeNode.Next() ) {
- if ( g_cinematic.GetBool() && !ent->cinematic ) {
- ent->GetPhysics()->UpdateTime( time );
- continue;
- }
- ent->Think();
- num++;
- }
- } else {
- num = 0;
- for( ent = activeEntities.Next(); ent != NULL; ent = ent->activeNode.Next() ) {
- ent->Think();
- num++;
- }
- }
- }
- // remove any entities that have stopped thinking
- if ( numEntitiesToDeactivate ) {
- idEntity *next_ent;
- int c = 0;
- for( ent = activeEntities.Next(); ent != NULL; ent = next_ent ) {
- next_ent = ent->activeNode.Next();
- if ( !ent->thinkFlags ) {
- ent->activeNode.Remove();
- c++;
- }
- }
- //assert( numEntitiesToDeactivate == c );
- numEntitiesToDeactivate = 0;
- }
- timer_think.Stop();
- timer_events.Clear();
- timer_events.Start();
- // service any pending events
- idEvent::ServiceEvents();
- timer_events.Stop();
- // free the player pvs
- FreePlayerPVS();
- // do multiplayer related stuff
- if ( isMultiplayer ) {
- mpGame.Run();
- }
- // display how long it took to calculate the current game frame
- if ( g_frametime.GetBool() ) {
- Printf( "game %d: all:%.1f th:%.1f ev:%.1f %d ents \n",
- time, timer_think.Milliseconds() + timer_events.Milliseconds(),
- timer_think.Milliseconds(), timer_events.Milliseconds(), num );
- }
- // build the return value
- ret.consistencyHash = 0;
- ret.sessionCommand[0] = 0;
- if ( !isMultiplayer && player ) {
- ret.health = player->health;
- ret.heartRate = player->heartRate;
- ret.stamina = idMath::FtoiFast( player->stamina );
- // combat is a 0-100 value based on lastHitTime and lastDmgTime
- // each make up 50% of the time spread over 10 seconds
- ret.combat = 0;
- if ( player->lastDmgTime > 0 && time < player->lastDmgTime + 10000 ) {
- ret.combat += 50.0f * (float) ( time - player->lastDmgTime ) / 10000;
- }
- if ( player->lastHitTime > 0 && time < player->lastHitTime + 10000 ) {
- ret.combat += 50.0f * (float) ( time - player->lastHitTime ) / 10000;
- }
- }
- // see if a target_sessionCommand has forced a changelevel
- if ( sessionCommand.Length() ) {
- strncpy( ret.sessionCommand, sessionCommand, sizeof( ret.sessionCommand ) );
- break;
- }
- // make sure we don't loop forever when skipping a cinematic
- if ( skipCinematic && ( time > cinematicMaxSkipTime ) ) {
- Warning( "Exceeded maximum cinematic skip length. Cinematic may be looping infinitely." );
- skipCinematic = false;
- break;
- }
- } while( ( inCinematic || ( time < cinematicStopTime ) ) && skipCinematic );
- ret.syncNextGameFrame = skipCinematic;
- if ( skipCinematic ) {
- soundSystem->SetMute( false );
- skipCinematic = false;
- }
- // show any debug info for this frame
- RunDebugInfo();
- D_DrawDebugLines();
- return ret;
- }
- /*
- ======================================================================
- Game view drawing
- ======================================================================
- */
- /*
- ====================
- idGameLocal::CalcFov
- Calculates the horizontal and vertical field of view based on a horizontal field of view and custom aspect ratio
- ====================
- */
- void idGameLocal::CalcFov( float base_fov, float &fov_x, float &fov_y ) const {
- float x;
- float y;
- float ratio_x;
- float ratio_y;
-
- if ( !sys->FPU_StackIsEmpty() ) {
- Printf( sys->FPU_GetState() );
- Error( "idGameLocal::CalcFov: FPU stack not empty" );
- }
- // first, calculate the vertical fov based on a 640x480 view
- x = 640.0f / tan( base_fov / 360.0f * idMath::PI );
- y = atan2( 480.0f, x );
- fov_y = y * 360.0f / idMath::PI;
- // FIXME: somehow, this is happening occasionally
- assert( fov_y > 0 );
- if ( fov_y <= 0 ) {
- Printf( sys->FPU_GetState() );
- Error( "idGameLocal::CalcFov: bad result" );
- }
- switch( r_aspectRatio.GetInteger() ) {
- default :
- case 0 :
- // 4:3
- fov_x = base_fov;
- return;
- break;
- case 1 :
- // 16:9
- ratio_x = 16.0f;
- ratio_y = 9.0f;
- break;
- case 2 :
- // 16:10
- ratio_x = 16.0f;
- ratio_y = 10.0f;
- break;
- }
- y = ratio_y / tan( fov_y / 360.0f * idMath::PI );
- fov_x = atan2( ratio_x, y ) * 360.0f / idMath::PI;
- if ( fov_x < base_fov ) {
- fov_x = base_fov;
- x = ratio_x / tan( fov_x / 360.0f * idMath::PI );
- fov_y = atan2( ratio_y, x ) * 360.0f / idMath::PI;
- }
- // FIXME: somehow, this is happening occasionally
- assert( ( fov_x > 0 ) && ( fov_y > 0 ) );
- if ( ( fov_y <= 0 ) || ( fov_x <= 0 ) ) {
- Printf( sys->FPU_GetState() );
- Error( "idGameLocal::CalcFov: bad result" );
- }
- }
- /*
- ================
- idGameLocal::Draw
- makes rendering and sound system calls
- ================
- */
- bool idGameLocal::Draw( int clientNum ) {
- if ( isMultiplayer ) {
- return mpGame.Draw( clientNum );
- }
- idPlayer *player = static_cast<idPlayer *>(entities[ clientNum ]);
- if ( !player ) {
- return false;
- }
- // render the scene
- player->playerView.RenderPlayerView( player->hud );
- return true;
- }
- /*
- ================
- idGameLocal::HandleESC
- ================
- */
- escReply_t idGameLocal::HandleESC( idUserInterface **gui ) {
- if ( isMultiplayer ) {
- *gui = StartMenu();
- // we may set the gui back to NULL to hide it
- return ESC_GUI;
- }
- idPlayer *player = GetLocalPlayer();
- if ( player ) {
- if ( player->HandleESC() ) {
- return ESC_IGNORE;
- } else {
- return ESC_MAIN;
- }
- }
- return ESC_MAIN;
- }
- /*
- ================
- idGameLocal::StartMenu
- ================
- */
- idUserInterface* idGameLocal::StartMenu( void ) {
- if ( !isMultiplayer ) {
- return NULL;
- }
- return mpGame.StartMenu();
- }
- /*
- ================
- idGameLocal::HandleGuiCommands
- ================
- */
- const char* idGameLocal::HandleGuiCommands( const char *menuCommand ) {
- if ( !isMultiplayer ) {
- return NULL;
- }
- return mpGame.HandleGuiCommands( menuCommand );
- }
- /*
- ================
- idGameLocal::HandleMainMenuCommands
- ================
- */
- void idGameLocal::HandleMainMenuCommands( const char *menuCommand, idUserInterface *gui ) { }
- /*
- ================
- idGameLocal::GetLevelMap
- should only be used for in-game level editing
- ================
- */
- idMapFile *idGameLocal::GetLevelMap( void ) {
- if ( mapFile && mapFile->HasPrimitiveData()) {
- return mapFile;
- }
- if ( !mapFileName.Length() ) {
- return NULL;
- }
- if ( mapFile ) {
- delete mapFile;
- }
- mapFile = new idMapFile;
- if ( !mapFile->Parse( mapFileName ) ) {
- delete mapFile;
- mapFile = NULL;
- }
- return mapFile;
- }
- /*
- ================
- idGameLocal::GetMapName
- ================
- */
- const char *idGameLocal::GetMapName( void ) const {
- return mapFileName.c_str();
- }
- /*
- ================
- idGameLocal::CallFrameCommand
- ================
- */
- void idGameLocal::CallFrameCommand( idEntity *ent, const function_t *frameCommand ) {
- frameCommandThread->CallFunction( ent, frameCommand, true );
- frameCommandThread->Execute();
- }
- /*
- ================
- idGameLocal::CallObjectFrameCommand
- ================
- */
- void idGameLocal::CallObjectFrameCommand( idEntity *ent, const char *frameCommand ) {
- const function_t *func;
- func = ent->scriptObject.GetFunction( frameCommand );
- if ( !func ) {
- if ( !ent->IsType( idTestModel::Type ) ) {
- Error( "Unknown function '%s' called for frame command on entity '%s'", frameCommand, ent->name.c_str() );
- }
- } else {
- frameCommandThread->CallFunction( ent, func, true );
- frameCommandThread->Execute();
- }
- }
- /*
- ================
- idGameLocal::ShowTargets
- ================
- */
- void idGameLocal::ShowTargets( void ) {
- idMat3 axis = GetLocalPlayer()->viewAngles.ToMat3();
- idVec3 up = axis[ 2 ] * 5.0f;
- const idVec3 &viewPos = GetLocalPlayer()->GetPhysics()->GetOrigin();
- idBounds viewTextBounds( viewPos );
- idBounds viewBounds( viewPos );
- idBounds box( idVec3( -4.0f, -4.0f, -4.0f ), idVec3( 4.0f, 4.0f, 4.0f ) );
- idEntity *ent;
- idEntity *target;
- int i;
- idBounds totalBounds;
- viewTextBounds.ExpandSelf( 128.0f );
- viewBounds.ExpandSelf( 512.0f );
- for( ent = spawnedEntities.Next(); ent != NULL; ent = ent->spawnNode.Next() ) {
- totalBounds = ent->GetPhysics()->GetAbsBounds();
- for( i = 0; i < ent->targets.Num(); i++ ) {
- target = ent->targets[ i ].GetEntity();
- if ( target ) {
- totalBounds.AddBounds( target->GetPhysics()->GetAbsBounds() );
- }
- }
- if ( !viewBounds.IntersectsBounds( totalBounds ) ) {
- continue;
- }
- float dist;
- idVec3 dir = totalBounds.GetCenter() - viewPos;
- dir.NormalizeFast();
- totalBounds.RayIntersection( viewPos, dir, dist );
- float frac = ( 512.0f - dist ) / 512.0f;
- if ( frac < 0.0f ) {
- continue;
- }
- gameRenderWorld->DebugBounds( ( ent->IsHidden() ? colorLtGrey : colorOrange ) * frac, ent->GetPhysics()->GetAbsBounds() );
- if ( viewTextBounds.IntersectsBounds( ent->GetPhysics()->GetAbsBounds() ) ) {
- idVec3 center = ent->GetPhysics()->GetAbsBounds().GetCenter();
- gameRenderWorld->DrawText( ent->name.c_str(), center - up, 0.1f, colorWhite * frac, axis, 1 );
- gameRenderWorld->DrawText( ent->GetEntityDefName(), center, 0.1f, colorWhite * frac, axis, 1 );
- gameRenderWorld->DrawText( va( "#%d", ent->entityNumber ), center + up, 0.1f, colorWhite * frac, axis, 1 );
- }
- for( i = 0; i < ent->targets.Num(); i++ ) {
- target = ent->targets[ i ].GetEntity();
- if ( target ) {
- gameRenderWorld->DebugArrow( colorYellow * frac, ent->GetPhysics()->GetAbsBounds().GetCenter(), target->GetPhysics()->GetOrigin(), 10, 0 );
- gameRenderWorld->DebugBounds( colorGreen * frac, box, target->GetPhysics()->GetOrigin() );
- }
- }
- }
- }
- /*
- ================
- idGameLocal::RunDebugInfo
- ================
- */
- void idGameLocal::RunDebugInfo( void ) {
- idEntity *ent;
- idPlayer *player;
- player = GetLocalPlayer();
- if ( !player ) {
- return;
- }
- const idVec3 &origin = player->GetPhysics()->GetOrigin();
- if ( g_showEntityInfo.GetBool() ) {
- idMat3 axis = player->viewAngles.ToMat3();
- idVec3 up = axis[ 2 ] * 5.0f;
- idBounds viewTextBounds( origin );
- idBounds viewBounds( origin );
- viewTextBounds.ExpandSelf( 128.0f );
- viewBounds.ExpandSelf( 512.0f );
- for( ent = spawnedEntities.Next(); ent != NULL; ent = ent->spawnNode.Next() ) {
- // don't draw the worldspawn
- if ( ent == world ) {
- continue;
- }
- // skip if the entity is very far away
- if ( !viewBounds.IntersectsBounds( ent->GetPhysics()->GetAbsBounds() ) ) {
- continue;
- }
- const idBounds &entBounds = ent->GetPhysics()->GetAbsBounds();
- int contents = ent->GetPhysics()->GetContents();
- if ( contents & CONTENTS_BODY ) {
- gameRenderWorld->DebugBounds( colorCyan, entBounds );
- } else if ( contents & CONTENTS_TRIGGER ) {
- gameRenderWorld->DebugBounds( colorOrange, entBounds );
- } else if ( contents & CONTENTS_SOLID ) {
- gameRenderWorld->DebugBounds( colorGreen, entBounds );
- } else {
- if ( !entBounds.GetVolume() ) {
- gameRenderWorld->DebugBounds( colorMdGrey, entBounds.Expand( 8.0f ) );
- } else {
- gameRenderWorld->DebugBounds( colorMdGrey, entBounds );
- }
- }
- if ( viewTextBounds.IntersectsBounds( entBounds ) ) {
- gameRenderWorld->DrawText( ent->name.c_str(), entBounds.GetCenter(), 0.1f, colorWhite, axis, 1 );
- gameRenderWorld->DrawText( va( "#%d", ent->entityNumber ), entBounds.GetCenter() + up, 0.1f, colorWhite, axis, 1 );
- }
- }
- }
- // debug tool to draw bounding boxes around active entities
- if ( g_showActiveEntities.GetBool() ) {
- for( ent = activeEntities.Next(); ent != NULL; ent = ent->activeNode.Next() ) {
- idBounds b = ent->GetPhysics()->GetBounds();
- if ( b.GetVolume() <= 0 ) {
- b[0][0] = b[0][1] = b[0][2] = -8;
- b[1][0] = b[1][1] = b[1][2] = 8;
- }
- if ( ent->fl.isDormant ) {
- gameRenderWorld->DebugBounds( colorYellow, b, ent->GetPhysics()->GetOrigin() );
- } else {
- gameRenderWorld->DebugBounds( colorGreen, b, ent->GetPhysics()->GetOrigin() );
- }
- }
- }
- if ( g_showTargets.GetBool() ) {
- ShowTargets();
- }
- if ( g_showTriggers.GetBool() ) {
- idTrigger::DrawDebugInfo();
- }
- if ( ai_showCombatNodes.GetBool() ) {
- idCombatNode::DrawDebugInfo();
- }
- if ( ai_showPaths.GetBool() ) {
- idPathCorner::DrawDebugInfo();
- }
- if ( g_editEntityMode.GetBool() ) {
- editEntities->DisplayEntities();
- }
- if ( g_showCollisionWorld.GetBool() ) {
- collisionModelManager->DrawModel( 0, vec3_origin, mat3_identity, origin, 128.0f );
- }
- if ( g_showCollisionModels.GetBool() ) {
- clip.DrawClipModels( player->GetEyePosition(), g_maxShowDistance.GetFloat(), pm_thirdPerson.GetBool() ? NULL : player );
- }
- if ( g_showCollisionTraces.GetBool() ) {
- clip.PrintStatistics();
- }
- if ( g_showPVS.GetInteger() ) {
- pvs.DrawPVS( origin, ( g_showPVS.GetInteger() == 2 ) ? PVS_ALL_PORTALS_OPEN : PVS_NORMAL );
- }
- if ( aas_test.GetInteger() >= 0 ) {
- idAAS *aas = GetAAS( aas_test.GetInteger() );
- if ( aas ) {
- aas->Test( origin );
- if ( ai_testPredictPath.GetBool() ) {
- idVec3 velocity;
- predictedPath_t path;
- velocity.x = cos( DEG2RAD( player->viewAngles.yaw ) ) * 100.0f;
- velocity.y = sin( DEG2RAD( player->viewAngles.yaw ) ) * 100.0f;
- velocity.z = 0.0f;
- idAI::PredictPath( player, aas, origin, velocity, 1000, 100, SE_ENTER_OBSTACLE | SE_BLOCKED | SE_ENTER_LEDGE_AREA, path );
- }
- }
- }
- if ( ai_showObstacleAvoidance.GetInteger() == 2 ) {
- idAAS *aas = GetAAS( 0 );
- if ( aas ) {
- idVec3 seekPos;
- obstaclePath_t path;
- seekPos = player->GetPhysics()->GetOrigin() + player->viewAxis[0] * 200.0f;
- idAI::FindPathAroundObstacles( player->GetPhysics(), aas, NULL, player->GetPhysics()->GetOrigin(), seekPos, path );
- }
- }
- // collision map debug output
- collisionModelManager->DebugOutput( player->GetEyePosition() );
- }
- /*
- ==================
- idGameLocal::NumAAS
- ==================
- */
- int idGameLocal::NumAAS( void ) const {
- return aasList.Num();
- }
- /*
- ==================
- idGameLocal::GetAAS
- ==================
- */
- idAAS *idGameLocal::GetAAS( int num ) const {
- if ( ( num >= 0 ) && ( num < aasList.Num() ) ) {
- if ( aasList[ num ] && aasList[ num ]->GetSettings() ) {
- return aasList[ num ];
- }
- }
- return NULL;
- }
- /*
- ==================
- idGameLocal::GetAAS
- ==================
- */
- idAAS *idGameLocal::GetAAS( const char *name ) const {
- int i;
- for ( i = 0; i < aasNames.Num(); i++ ) {
- if ( aasNames[ i ] == name ) {
- if ( !aasList[ i ]->GetSettings() ) {
- return NULL;
- } else {
- return aasList[ i ];
- }
- }
- }
- return NULL;
- }
- /*
- ==================
- idGameLocal::SetAASAreaState
- ==================
- */
- void idGameLocal::SetAASAreaState( const idBounds &bounds, const int areaContents, bool closed ) {
- int i;
- for( i = 0; i < aasList.Num(); i++ ) {
- aasList[ i ]->SetAreaState( bounds, areaContents, closed );
- }
- }
- /*
- ==================
- idGameLocal::AddAASObstacle
- ==================
- */
- aasHandle_t idGameLocal::AddAASObstacle( const idBounds &bounds ) {
- int i;
- aasHandle_t obstacle;
- aasHandle_t check;
- if ( !aasList.Num() ) {
- return -1;
- }
- obstacle = aasList[ 0 ]->AddObstacle( bounds );
- for( i = 1; i < aasList.Num(); i++ ) {
- check = aasList[ i ]->AddObstacle( bounds );
- assert( check == obstacle );
- }
- return obstacle;
- }
- /*
- ==================
- idGameLocal::RemoveAASObstacle
- ==================
- */
- void idGameLocal::RemoveAASObstacle( const aasHandle_t handle ) {
- int i;
- for( i = 0; i < aasList.Num(); i++ ) {
- aasList[ i ]->RemoveObstacle( handle );
- }
- }
- /*
- ==================
- idGameLocal::RemoveAllAASObstacles
- ==================
- */
- void idGameLocal::RemoveAllAASObstacles( void ) {
- int i;
- for( i = 0; i < aasList.Num(); i++ ) {
- aasList[ i ]->RemoveAllObstacles();
- }
- }
- /*
- ==================
- idGameLocal::CheatsOk
- ==================
- */
- bool idGameLocal::CheatsOk( bool requirePlayer ) {
- idPlayer *player;
- if ( isMultiplayer && !cvarSystem->GetCVarBool( "net_allowCheats" ) ) {
- Printf( "Not allowed in multiplayer.\n" );
- return false;
- }
- if ( developer.GetBool() ) {
- return true;
- }
- player = GetLocalPlayer();
- if ( !requirePlayer || ( player && ( player->health > 0 ) ) ) {
- return true;
- }
- Printf( "You must be alive to use this command.\n" );
- return false;
- }
- /*
- ===================
- idGameLocal::RegisterEntity
- ===================
- */
- void idGameLocal::RegisterEntity( idEntity *ent ) {
- int spawn_entnum;
- if ( spawnCount >= ( 1 << ( 32 - GENTITYNUM_BITS ) ) ) {
- Error( "idGameLocal::RegisterEntity: spawn count overflow" );
- }
- if ( !spawnArgs.GetInt( "spawn_entnum", "0", spawn_entnum ) ) {
- while( entities[firstFreeIndex] && firstFreeIndex < ENTITYNUM_MAX_NORMAL ) {
- firstFreeIndex++;
- }
- if ( firstFreeIndex >= ENTITYNUM_MAX_NORMAL ) {
- Error( "no free entities" );
- }
- spawn_entnum = firstFreeIndex++;
- }
- entities[ spawn_entnum ] = ent;
- spawnIds[ spawn_entnum ] = spawnCount++;
- ent->entityNumber = spawn_entnum;
- ent->spawnNode.AddToEnd( spawnedEntities );
- ent->spawnArgs.TransferKeyValues( spawnArgs );
- if ( spawn_entnum >= num_entities ) {
- num_entities++;
- }
- }
- /*
- ===================
- idGameLocal::UnregisterEntity
- ===================
- */
- void idGameLocal::UnregisterEntity( idEntity *ent ) {
- assert( ent );
- if ( editEntities ) {
- editEntities->RemoveSelectedEntity( ent );
- }
- if ( ( ent->entityNumber != ENTITYNUM_NONE ) && ( entities[ ent->entityNumber ] == ent ) ) {
- ent->spawnNode.Remove();
- entities[ ent->entityNumber ] = NULL;
- spawnIds[ ent->entityNumber ] = -1;
- if ( ent->entityNumber >= MAX_CLIENTS && ent->entityNumber < firstFreeIndex ) {
- firstFreeIndex = ent->entityNumber;
- }
- ent->entityNumber = ENTITYNUM_NONE;
- }
- }
- /*
- ================
- idGameLocal::SpawnEntityType
- ================
- */
- idEntity *idGameLocal::SpawnEntityType( const idTypeInfo &classdef, const idDict *args, bool bIsClientReadSnapshot ) {
- idClass *obj;
- #if _DEBUG
- if ( isClient ) {
- assert( bIsClientReadSnapshot );
- }
- #endif
- if ( !classdef.IsType( idEntity::Type ) ) {
- Error( "Attempted to spawn non-entity class '%s'", classdef.classname );
- }
- try {
- if ( args ) {
- spawnArgs = *args;
- } else {
- spawnArgs.Clear();
- }
- obj = classdef.CreateInstance();
- obj->CallSpawn();
- }
-
- catch( idAllocError & ) {
- obj = NULL;
- }
- spawnArgs.Clear();
- return static_cast<idEntity *>(obj);
- }
- /*
- ===================
- idGameLocal::SpawnEntityDef
- Finds the spawn function for the entity and calls it,
- returning false if not found
- ===================
- */
- bool idGameLocal::SpawnEntityDef( const idDict &args, idEntity **ent, bool setDefaults ) {
- const char *classname;
- const char *spawn;
- idTypeInfo *cls;
- idClass *obj;
- idStr error;
- const char *name;
- if ( ent ) {
- *ent = NULL;
- }
- spawnArgs = args;
- if ( spawnArgs.GetString( "name", "", &name ) ) {
- sprintf( error, " on '%s'", name);
- }
- spawnArgs.GetString( "classname", NULL, &classname );
- const idDeclEntityDef *def = FindEntityDef( classname, false );
- if ( !def ) {
- Warning( "Unknown classname '%s'%s.", classname, error.c_str() );
- return false;
- }
- spawnArgs.SetDefaults( &def->dict );
- // check if we should spawn a class object
- spawnArgs.GetString( "spawnclass", NULL, &spawn );
- if ( spawn ) {
- cls = idClass::GetClass( spawn );
- if ( !cls ) {
- Warning( "Could not spawn '%s'. Class '%s' not found%s.", classname, spawn, error.c_str() );
- return false;
- }
- obj = cls->CreateInstance();
- if ( !obj ) {
- Warning( "Could not spawn '%s'. Instance could not be created%s.", classname, error.c_str() );
- return false;
- }
- obj->CallSpawn();
- if ( ent && obj->IsType( idEntity::Type ) ) {
- *ent = static_cast<idEntity *>(obj);
- }
- return true;
- }
- // check if we should call a script function to spawn
- spawnArgs.GetString( "spawnfunc", NULL, &spawn );
- if ( spawn ) {
- const function_t *func = program.FindFunction( spawn );
- if ( !func ) {
- Warning( "Could not spawn '%s'. Script function '%s' not found%s.", classname, spawn, error.c_str() );
- return false;
- }
- idThread *thread = new idThread( func );
- thread->DelayedStart( 0 );
- return true;
- }
- Warning( "%s doesn't include a spawnfunc or spawnclass%s.", classname, error.c_str() );
- return false;
- }
- /*
- ================
- idGameLocal::FindEntityDef
- ================
- */
- const idDeclEntityDef *idGameLocal::FindEntityDef( const char *name, bool makeDefault ) const {
- const idDecl *decl = NULL;
- if ( isMultiplayer ) {
- decl = declManager->FindType( DECL_ENTITYDEF, va( "%s_mp", name ), false );
- }
- if ( !decl ) {
- decl = declManager->FindType( DECL_ENTITYDEF, name, makeDefault );
- }
- return static_cast<const idDeclEntityDef *>( decl );
- }
- /*
- ================
- idGameLocal::FindEntityDefDict
- ================
- */
- const idDict *idGameLocal::FindEntityDefDict( const char *name, bool makeDefault ) const {
- const idDeclEntityDef *decl = FindEntityDef( name, makeDefault );
- return decl ? &decl->dict : NULL;
- }
- /*
- ================
- idGameLocal::InhibitEntitySpawn
- ================
- */
- bool idGameLocal::InhibitEntitySpawn( idDict &spawnArgs ) {
-
- bool result = false;
- if ( isMultiplayer ) {
- spawnArgs.GetBool( "not_multiplayer", "0", result );
- } else if ( g_skill.GetInteger() == 0 ) {
- spawnArgs.GetBool( "not_easy", "0", result );
- } else if ( g_skill.GetInteger() == 1 ) {
- spawnArgs.GetBool( "not_medium", "0", result );
- } else {
- spawnArgs.GetBool( "not_hard", "0", result );
- }
- const char *name;
- #ifndef ID_DEMO_BUILD
- if ( g_skill.GetInteger() == 3 ) {
- name = spawnArgs.GetString( "classname" );
- if ( idStr::Icmp( name, "item_medkit" ) == 0 || idStr::Icmp( name, "item_medkit_small" ) == 0 ) {
- result = true;
- }
- }
- #endif
- if ( gameLocal.isMultiplayer ) {
- name = spawnArgs.GetString( "classname" );
- if ( idStr::Icmp( name, "weapon_bfg" ) == 0 || idStr::Icmp( name, "weapon_soulcube" ) == 0 ) {
- result = true;
- }
- }
- return result;
- }
- /*
- ================
- idGameLocal::SetSkill
- ================
- */
- void idGameLocal::SetSkill( int value ) {
- int skill_level;
- if ( value < 0 ) {
- skill_level = 0;
- } else if ( value > 3 ) {
- skill_level = 3;
- } else {
- skill_level = value;
- }
- g_skill.SetInteger( skill_level );
- }
- /*
- ==============
- idGameLocal::GameState
- Used to allow entities to know if they're being spawned during the initial spawn.
- ==============
- */
- gameState_t idGameLocal::GameState( void ) const {
- return gamestate;
- }
- /*
- ==============
- idGameLocal::SpawnMapEntities
- Parses textual entity definitions out of an entstring and spawns gentities.
- ==============
- */
- void idGameLocal::SpawnMapEntities( void ) {
- int i;
- int num;
- int inhibit;
- idMapEntity *mapEnt;
- int numEntities;
- idDict args;
- Printf( "Spawning entities\n" );
- if ( mapFile == NULL ) {
- Printf("No mapfile present\n");
- return;
- }
- SetSkill( g_skill.GetInteger() );
- numEntities = mapFile->GetNumEntities();
- if ( numEntities == 0 ) {
- Error( "...no entities" );
- }
- // the worldspawn is a special that performs any global setup
- // needed by a level
- mapEnt = mapFile->GetEntity( 0 );
- args = mapEnt->epairs;
- args.SetInt( "spawn_entnum", ENTITYNUM_WORLD );
- if ( !SpawnEntityDef( args ) || !entities[ ENTITYNUM_WORLD ] || !entities[ ENTITYNUM_WORLD ]->IsType( idWorldspawn::Type ) ) {
- Error( "Problem spawning world entity" );
- }
- num = 1;
- inhibit = 0;
- for ( i = 1 ; i < numEntities ; i++ ) {
- mapEnt = mapFile->GetEntity( i );
- args = mapEnt->epairs;
- if ( !InhibitEntitySpawn( args ) ) {
- // precache any media specified in the map entity
- CacheDictionaryMedia( &args );
- SpawnEntityDef( args );
- num++;
- } else {
- inhibit++;
- }
- }
- Printf( "...%i entities spawned, %i inhibited\n\n", num, inhibit );
- }
- /*
- ================
- idGameLocal::AddEntityToHash
- ================
- */
- void idGameLocal::AddEntityToHash( const char *name, idEntity *ent ) {
- if ( FindEntity( name ) ) {
- Error( "Multiple entities named '%s'", name );
- }
- entityHash.Add( entityHash.GenerateKey( name, true ), ent->entityNumber );
- }
- /*
- ================
- idGameLocal::RemoveEntityFromHash
- ================
- */
- bool idGameLocal::RemoveEntityFromHash( const char *name, idEntity *ent ) {
- int hash, i;
- hash = entityHash.GenerateKey( name, true );
- for ( i = entityHash.First( hash ); i != -1; i = entityHash.Next( i ) ) {
- if ( entities[i] && entities[i] == ent && entities[i]->name.Icmp( name ) == 0 ) {
- entityHash.Remove( hash, i );
- return true;
- }
- }
- return false;
- }
- /*
- ================
- idGameLocal::GetTargets
- ================
- */
- int idGameLocal::GetTargets( const idDict &args, idList< idEntityPtr<idEntity> > &list, const char *ref ) const {
- int i, num, refLength;
- const idKeyValue *arg;
- idEntity *ent;
- list.Clear();
- refLength = strlen( ref );
- num = args.GetNumKeyVals();
- for( i = 0; i < num; i++ ) {
- arg = args.GetKeyVal( i );
- if ( arg->GetKey().Icmpn( ref, refLength ) == 0 ) {
- ent = FindEntity( arg->GetValue() );
- if ( ent ) {
- idEntityPtr<idEntity> &entityPtr = list.Alloc();
- entityPtr = ent;
- }
- }
- }
- return list.Num();
- }
- /*
- =============
- idGameLocal::GetTraceEntity
- returns the master entity of a trace. for example, if the trace entity is the player's head, it will return the player.
- =============
- */
- idEntity *idGameLocal::GetTraceEntity( const trace_t &trace ) const {
- idEntity *master;
- if ( !entities[ trace.c.entityNum ] ) {
- return NULL;
- }
- master = entities[ trace.c.entityNum ]->GetBindMaster();
- if ( master ) {
- return master;
- }
- return entities[ trace.c.entityNum ];
- }
- /*
- =============
- idGameLocal::ArgCompletion_EntityName
- Argument completion for entity names
- =============
- */
- void idGameLocal::ArgCompletion_EntityName( const idCmdArgs &args, void(*callback)( const char *s ) ) {
- int i;
- for( i = 0; i < gameLocal.num_entities; i++ ) {
- if ( gameLocal.entities[ i ] ) {
- callback( va( "%s %s", args.Argv( 0 ), gameLocal.entities[ i ]->name.c_str() ) );
- }
- }
- }
- /*
- =============
- idGameLocal::FindEntity
- Returns the entity whose name matches the specified string.
- =============
- */
- idEntity *idGameLocal::FindEntity( const char *name ) const {
- int hash, i;
- hash = entityHash.GenerateKey( name, true );
- for ( i = entityHash.First( hash ); i != -1; i = entityHash.Next( i ) ) {
- if ( entities[i] && entities[i]->name.Icmp( name ) == 0 ) {
- return entities[i];
- }
- }
- return NULL;
- }
- /*
- =============
- idGameLocal::FindEntityUsingDef
- Searches all active entities for the next one using the specified entityDef.
- Searches beginning at the entity after from, or the beginning if NULL
- NULL will be returned if the end of the list is reached.
- =============
- */
- idEntity *idGameLocal::FindEntityUsingDef( idEntity *from, const char *match ) const {
- idEntity *ent;
- if ( !from ) {
- ent = spawnedEntities.Next();
- } else {
- ent = from->spawnNode.Next();
- }
- for ( ; ent != NULL; ent = ent->spawnNode.Next() ) {
- assert( ent );
- if ( idStr::Icmp( ent->GetEntityDefName(), match ) == 0 ) {
- return ent;
- }
- }
- return NULL;
- }
- /*
- =============
- idGameLocal::FindTraceEntity
- Searches all active entities for the closest ( to start ) match that intersects
- the line start,end
- =============
- */
- idEntity *idGameLocal::FindTraceEntity( idVec3 start, idVec3 end, const idTypeInfo &c, const idEntity *skip ) const {
- idEntity *ent;
- idEntity *bestEnt;
- float scale;
- float bestScale;
- idBounds b;
- bestEnt = NULL;
- bestScale = 1.0f;
- for( ent = spawnedEntities.Next(); ent != NULL; ent = ent->spawnNode.Next() ) {
- if ( ent->IsType( c ) && ent != skip ) {
- b = ent->GetPhysics()->GetAbsBounds().Expand( 16 );
- if ( b.RayIntersection( start, end-start, scale ) ) {
- if ( scale >= 0.0f && scale < bestScale ) {
- bestEnt = ent;
- bestScale = scale;
- }
- }
- }
- }
- return bestEnt;
- }
- /*
- ================
- idGameLocal::EntitiesWithinRadius
- ================
- */
- int idGameLocal::EntitiesWithinRadius( const idVec3 org, float radius, idEntity **entityList, int maxCount ) const {
- idEntity *ent;
- idBounds bo( org );
- int entCount = 0;
- bo.ExpandSelf( radius );
- for( ent = spawnedEntities.Next(); ent != NULL; ent = ent->spawnNode.Next() ) {
- if ( ent->GetPhysics()->GetAbsBounds().IntersectsBounds( bo ) ) {
- entityList[entCount++] = ent;
- }
- }
- return entCount;
- }
- /*
- =================
- idGameLocal::KillBox
- Kills all entities that would touch the proposed new positioning of ent. The ent itself will not being killed.
- Checks if player entities are in the teleporter, and marks them to die at teleport exit instead of immediately.
- If catch_teleport, this only marks teleport players for death on exit
- =================
- */
- void idGameLocal::KillBox( idEntity *ent, bool catch_teleport ) {
- int i;
- int num;
- idEntity * hit;
- idClipModel *cm;
- idClipModel *clipModels[ MAX_GENTITIES ];
- idPhysics *phys;
- phys = ent->GetPhysics();
- if ( !phys->GetNumClipModels() ) {
- return;
- }
- num = clip.ClipModelsTouchingBounds( phys->GetAbsBounds(), phys->GetClipMask(), clipModels, MAX_GENTITIES );
- for ( i = 0; i < num; i++ ) {
- cm = clipModels[ i ];
- // don't check render entities
- if ( cm->IsRenderModel() ) {
- continue;
- }
- hit = cm->GetEntity();
- if ( ( hit == ent ) || !hit->fl.takedamage ) {
- continue;
- }
- if ( !phys->ClipContents( cm ) ) {
- continue;
- }
- // nail it
- if ( hit->IsType( idPlayer::Type ) && static_cast< idPlayer * >( hit )->IsInTeleport() ) {
- static_cast< idPlayer * >( hit )->TeleportDeath( ent->entityNumber );
- } else if ( !catch_teleport ) {
- hit->Damage( ent, ent, vec3_origin, "damage_telefrag", 1.0f, INVALID_JOINT );
- }
- if ( !gameLocal.isMultiplayer ) {
- // let the mapper know about it
- Warning( "'%s' telefragged '%s'", ent->name.c_str(), hit->name.c_str() );
- }
- }
- }
- /*
- ================
- idGameLocal::RequirementMet
- ================
- */
- bool idGameLocal::RequirementMet( idEntity *activator, const idStr &requires, int removeItem ) {
- if ( requires.Length() ) {
- if ( activator->IsType( idPlayer::Type ) ) {
- idPlayer *player = static_cast<idPlayer *>(activator);
- idDict *item = player->FindInventoryItem( requires );
- if ( item ) {
- if ( removeItem ) {
- player->RemoveInventoryItem( item );
- }
- return true;
- } else {
- return false;
- }
- }
- }
- return true;
- }
- /*
- ============
- idGameLocal::AlertAI
- ============
- */
- void idGameLocal::AlertAI( idEntity *ent ) {
- if ( ent && ent->IsType( idActor::Type ) ) {
- // alert them for the next frame
- lastAIAlertTime = time + msec;
- lastAIAlertEntity = static_cast<idActor *>( ent );
- }
- }
- /*
- ============
- idGameLocal::GetAlertEntity
- ============
- */
- idActor *idGameLocal::GetAlertEntity( void ) {
- if ( lastAIAlertTime >= time ) {
- return lastAIAlertEntity.GetEntity();
- }
- return NULL;
- }
- /*
- ============
- idGameLocal::RadiusDamage
- ============
- */
- void idGameLocal::RadiusDamage( const idVec3 &origin, idEntity *inflictor, idEntity *attacker, idEntity *ignoreDamage, idEntity *ignorePush, const char *damageDefName, float dmgPower ) {
- float dist, damageScale, attackerDamageScale, attackerPushScale;
- idEntity * ent;
- idEntity * entityList[ MAX_GENTITIES ];
- int numListedEntities;
- idBounds bounds;
- idVec3 v, damagePoint, dir;
- int i, e, damage, radius, push;
- const idDict *damageDef = FindEntityDefDict( damageDefName, false );
- if ( !damageDef ) {
- Warning( "Unknown damageDef '%s'", damageDefName );
- return;
- }
- damageDef->GetInt( "damage", "20", damage );
- damageDef->GetInt( "radius", "50", radius );
- damageDef->GetInt( "push", va( "%d", damage * 100 ), push );
- damageDef->GetFloat( "attackerDamageScale", "0.5", attackerDamageScale );
- damageDef->GetFloat( "attackerPushScale", "0", attackerPushScale );
- if ( radius < 1 ) {
- radius = 1;
- }
- bounds = idBounds( origin ).Expand( radius );
- // get all entities touching the bounds
- numListedEntities = clip.EntitiesTouchingBounds( bounds, -1, entityList, MAX_GENTITIES );
- if ( inflictor && inflictor->IsType( idAFAttachment::Type ) ) {
- inflictor = static_cast<idAFAttachment*>(inflictor)->GetBody();
- }
- if ( attacker && attacker->IsType( idAFAttachment::Type ) ) {
- attacker = static_cast<idAFAttachment*>(attacker)->GetBody();
- }
- if ( ignoreDamage && ignoreDamage->IsType( idAFAttachment::Type ) ) {
- ignoreDamage = static_cast<idAFAttachment*>(ignoreDamage)->GetBody();
- }
- // apply damage to the entities
- for ( e = 0; e < numListedEntities; e++ ) {
- ent = entityList[ e ];
- assert( ent );
- if ( !ent->fl.takedamage ) {
- continue;
- }
- if ( ent == inflictor || ( ent->IsType( idAFAttachment::Type ) && static_cast<idAFAttachment*>(ent)->GetBody() == inflictor ) ) {
- continue;
- }
- if ( ent == ignoreDamage || ( ent->IsType( idAFAttachment::Type ) && static_cast<idAFAttachment*>(ent)->GetBody() == ignoreDamage ) ) {
- continue;
- }
- // don't damage a dead player
- if ( isMultiplayer && ent->entityNumber < MAX_CLIENTS && ent->IsType( idPlayer::Type ) && static_cast< idPlayer * >( ent )->health < 0 ) {
- continue;
- }
- // find the distance from the edge of the bounding box
- for ( i = 0; i < 3; i++ ) {
- if ( origin[ i ] < ent->GetPhysics()->GetAbsBounds()[0][ i ] ) {
- v[ i ] = ent->GetPhysics()->GetAbsBounds()[0][ i ] - origin[ i ];
- } else if ( origin[ i ] > ent->GetPhysics()->GetAbsBounds()[1][ i ] ) {
- v[ i ] = origin[ i ] - ent->GetPhysics()->GetAbsBounds()[1][ i ];
- } else {
- v[ i ] = 0;
- }
- }
- dist = v.Length();
- if ( dist >= radius ) {
- continue;
- }
- if ( ent->CanDamage( origin, damagePoint ) ) {
- // push the center of mass higher than the origin so players
- // get knocked into the air more
- dir = ent->GetPhysics()->GetOrigin() - origin;
- dir[ 2 ] += 24;
- // get the damage scale
- damageScale = dmgPower * ( 1.0f - dist / radius );
- if ( ent == attacker || ( ent->IsType( idAFAttachment::Type ) && static_cast<idAFAttachment*>(ent)->GetBody() == attacker ) ) {
- damageScale *= attackerDamageScale;
- }
- ent->Damage( inflictor, attacker, dir, damageDefName, damageScale, INVALID_JOINT );
- }
- }
- // push physics objects
- if ( push ) {
- RadiusPush( origin, radius, push * dmgPower, attacker, ignorePush, attackerPushScale, false );
- }
- }
- /*
- ==============
- idGameLocal::RadiusPush
- ==============
- */
- void idGameLocal::RadiusPush( const idVec3 &origin, const float radius, const float push, const idEntity *inflictor, const idEntity *ignore, float inflictorScale, const bool quake ) {
- int i, numListedClipModels;
- idClipModel *clipModel;
- idClipModel *clipModelList[ MAX_GENTITIES ];
- idVec3 dir;
- idBounds bounds;
- modelTrace_t result;
- idEntity *ent;
- float scale;
- dir.Set( 0.0f, 0.0f, 1.0f );
- bounds = idBounds( origin ).Expand( radius );
- // get all clip models touching the bounds
- numListedClipModels = clip.ClipModelsTouchingBounds( bounds, -1, clipModelList, MAX_GENTITIES );
- if ( inflictor && inflictor->IsType( idAFAttachment::Type ) ) {
- inflictor = static_cast<const idAFAttachment*>(inflictor)->GetBody();
- }
- if ( ignore && ignore->IsType( idAFAttachment::Type ) ) {
- ignore = static_cast<const idAFAttachment*>(ignore)->GetBody();
- }
- // apply impact to all the clip models through their associated physics objects
- for ( i = 0; i < numListedClipModels; i++ ) {
- clipModel = clipModelList[i];
- // never push render models
- if ( clipModel->IsRenderModel() ) {
- continue;
- }
- ent = clipModel->GetEntity();
- // never push projectiles
- if ( ent->IsType( idProjectile::Type ) ) {
- continue;
- }
- // players use "knockback" in idPlayer::Damage
- if ( ent->IsType( idPlayer::Type ) && !quake ) {
- continue;
- }
- // don't push the ignore entity
- if ( ent == ignore || ( ent->IsType( idAFAttachment::Type ) && static_cast<idAFAttachment*>(ent)->GetBody() == ignore ) ) {
- continue;
- }
- if ( gameRenderWorld->FastWorldTrace( result, origin, clipModel->GetOrigin() ) ) {
- continue;
- }
- // scale the push for the inflictor
- if ( ent == inflictor || ( ent->IsType( idAFAttachment::Type ) && static_cast<idAFAttachment*>(ent)->GetBody() == inflictor ) ) {
- scale = inflictorScale;
- } else {
- scale = 1.0f;
- }
- if ( quake ) {
- clipModel->GetEntity()->ApplyImpulse( world, clipModel->GetId(), clipModel->GetOrigin(), scale * push * dir );
- } else {
- RadiusPushClipModel( origin, scale * push, clipModel );
- }
- }
- }
- /*
- ==============
- idGameLocal::RadiusPushClipModel
- ==============
- */
- void idGameLocal::RadiusPushClipModel( const idVec3 &origin, const float push, const idClipModel *clipModel ) {
- int i, j;
- float dot, dist, area;
- const idTraceModel *trm;
- const traceModelPoly_t *poly;
- idFixedWinding w;
- idVec3 v, localOrigin, center, impulse;
- trm = clipModel->GetTraceModel();
- if ( !trm || 1 ) {
- impulse = clipModel->GetAbsBounds().GetCenter() - origin;
- impulse.Normalize();
- impulse.z += 1.0f;
- clipModel->GetEntity()->ApplyImpulse( world, clipModel->GetId(), clipModel->GetOrigin(), push * impulse );
- return;
- }
- localOrigin = ( origin - clipModel->GetOrigin() ) * clipModel->GetAxis().Transpose();
- for ( i = 0; i < trm->numPolys; i++ ) {
- poly = &trm->polys[i];
- center.Zero();
- for ( j = 0; j < poly->numEdges; j++ ) {
- v = trm->verts[ trm->edges[ abs(poly->edges[j]) ].v[ INTSIGNBITSET( poly->edges[j] ) ] ];
- center += v;
- v -= localOrigin;
- v.NormalizeFast(); // project point on a unit sphere
- w.AddPoint( v );
- }
- center /= poly->numEdges;
- v = center - localOrigin;
- dist = v.NormalizeFast();
- dot = v * poly->normal;
- if ( dot > 0.0f ) {
- continue;
- }
- area = w.GetArea();
- // impulse in polygon normal direction
- impulse = poly->normal * clipModel->GetAxis();
- // always push up for nicer effect
- impulse.z -= 1.0f;
- // scale impulse based on visible surface area and polygon angle
- impulse *= push * ( dot * area * ( 1.0f / ( 4.0f * idMath::PI ) ) );
- // scale away distance for nicer effect
- impulse *= ( dist * 2.0f );
- // impulse is applied to the center of the polygon
- center = clipModel->GetOrigin() + center * clipModel->GetAxis();
- clipModel->GetEntity()->ApplyImpulse( world, clipModel->GetId(), center, impulse );
- }
- }
- /*
- ===============
- idGameLocal::ProjectDecal
- ===============
- */
- void idGameLocal::ProjectDecal( const idVec3 &origin, const idVec3 &dir, float depth, bool parallel, float size, const char *material, float angle ) {
- float s, c;
- idMat3 axis, axistemp;
- idFixedWinding winding;
- idVec3 windingOrigin, projectionOrigin;
- static idVec3 decalWinding[4] = {
- idVec3( 1.0f, 1.0f, 0.0f ),
- idVec3( -1.0f, 1.0f, 0.0f ),
- idVec3( -1.0f, -1.0f, 0.0f ),
- idVec3( 1.0f, -1.0f, 0.0f )
- };
- if ( !g_decals.GetBool() ) {
- return;
- }
- // randomly rotate the decal winding
- idMath::SinCos16( ( angle ) ? angle : random.RandomFloat() * idMath::TWO_PI, s, c );
- // winding orientation
- axis[2] = dir;
- axis[2].Normalize();
- axis[2].NormalVectors( axistemp[0], axistemp[1] );
- axis[0] = axistemp[ 0 ] * c + axistemp[ 1 ] * -s;
- axis[1] = axistemp[ 0 ] * -s + axistemp[ 1 ] * -c;
- windingOrigin = origin + depth * axis[2];
- if ( parallel ) {
- projectionOrigin = origin - depth * axis[2];
- } else {
- projectionOrigin = origin;
- }
- size *= 0.5f;
- winding.Clear();
- winding += idVec5( windingOrigin + ( axis * decalWinding[0] ) * size, idVec2( 1, 1 ) );
- winding += idVec5( windingOrigin + ( axis * decalWinding[1] ) * size, idVec2( 0, 1 ) );
- winding += idVec5( windingOrigin + ( axis * decalWinding[2] ) * size, idVec2( 0, 0 ) );
- winding += idVec5( windingOrigin + ( axis * decalWinding[3] ) * size, idVec2( 1, 0 ) );
- gameRenderWorld->ProjectDecalOntoWorld( winding, projectionOrigin, parallel, depth * 0.5f, declManager->FindMaterial( material ), time );
- }
- /*
- ==============
- idGameLocal::BloodSplat
- ==============
- */
- void idGameLocal::BloodSplat( const idVec3 &origin, const idVec3 &dir, float size, const char *material ) {
- float halfSize = size * 0.5f;
- idVec3 verts[] = { idVec3( 0.0f, +halfSize, +halfSize ),
- idVec3( 0.0f, +halfSize, -halfSize ),
- idVec3( 0.0f, -halfSize, -halfSize ),
- idVec3( 0.0f, -halfSize, +halfSize ) };
- idTraceModel trm;
- idClipModel mdl;
- trace_t results;
- // FIXME: get from damage def
- if ( !g_bloodEffects.GetBool() ) {
- return;
- }
- size = halfSize + random.RandomFloat() * halfSize;
- trm.SetupPolygon( verts, 4 );
- mdl.LoadModel( trm );
- clip.Translation( results, origin, origin + dir * 64.0f, &mdl, mat3_identity, CONTENTS_SOLID, NULL );
- ProjectDecal( results.endpos, dir, 2.0f * size, true, size, material );
- }
- /*
- =============
- idGameLocal::SetCamera
- =============
- */
- void idGameLocal::SetCamera( idCamera *cam ) {
- int i;
- idEntity *ent;
- idAI *ai;
- // this should fix going into a cinematic when dead.. rare but happens
- idPlayer *client = GetLocalPlayer();
- if ( client->health <= 0 || client->AI_DEAD ) {
- return;
- }
- camera = cam;
- if ( camera ) {
- inCinematic = true;
- if ( skipCinematic && camera->spawnArgs.GetBool( "disconnect" ) ) {
- camera->spawnArgs.SetBool( "disconnect", false );
- cvarSystem->SetCVarFloat( "r_znear", 3.0f );
- cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "disconnect\n" );
- skipCinematic = false;
- return;
- }
- if ( time > cinematicStopTime ) {
- cinematicSkipTime = time + CINEMATIC_SKIP_DELAY;
- }
- // set r_znear so that transitioning into/out of the player's head doesn't clip through the view
- cvarSystem->SetCVarFloat( "r_znear", 1.0f );
-
- // hide all the player models
- for( i = 0; i < numClients; i++ ) {
- if ( entities[ i ] ) {
- client = static_cast< idPlayer* >( entities[ i ] );
- client->EnterCinematic();
- }
- }
- if ( !cam->spawnArgs.GetBool( "ignore_enemies" ) ) {
- // kill any active monsters that are enemies of the player
- for ( ent = spawnedEntities.Next(); ent != NULL; ent = ent->spawnNode.Next() ) {
- if ( ent->cinematic || ent->fl.isDormant ) {
- // only kill entities that aren't needed for cinematics and aren't dormant
- continue;
- }
-
- if ( ent->IsType( idAI::Type ) ) {
- ai = static_cast<idAI *>( ent );
- if ( !ai->GetEnemy() || !ai->IsActive() ) {
- // no enemy, or inactive, so probably safe to ignore
- continue;
- }
- } else if ( ent->IsType( idProjectile::Type ) ) {
- // remove all projectiles
- } else if ( ent->spawnArgs.GetBool( "cinematic_remove" ) ) {
- // remove anything marked to be removed during cinematics
- } else {
- // ignore everything else
- continue;
- }
- // remove it
- DPrintf( "removing '%s' for cinematic\n", ent->GetName() );
- ent->PostEventMS( &EV_Remove, 0 );
- }
- }
- } else {
- inCinematic = false;
- cinematicStopTime = time + msec;
- // restore r_znear
- cvarSystem->SetCVarFloat( "r_znear", 3.0f );
- // show all the player models
- for( i = 0; i < numClients; i++ ) {
- if ( entities[ i ] ) {
- idPlayer *client = static_cast< idPlayer* >( entities[ i ] );
- client->ExitCinematic();
- }
- }
- }
- }
- /*
- =============
- idGameLocal::GetCamera
- =============
- */
- idCamera *idGameLocal::GetCamera( void ) const {
- return camera;
- }
- /*
- =============
- idGameLocal::SkipCinematic
- =============
- */
- bool idGameLocal::SkipCinematic( void ) {
- if ( camera ) {
- if ( camera->spawnArgs.GetBool( "disconnect" ) ) {
- camera->spawnArgs.SetBool( "disconnect", false );
- cvarSystem->SetCVarFloat( "r_znear", 3.0f );
- cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "disconnect\n" );
- skipCinematic = false;
- return false;
- }
- if ( camera->spawnArgs.GetBool( "instantSkip" ) ) {
- camera->Stop();
- return false;
- }
- }
- soundSystem->SetMute( true );
- if ( !skipCinematic ) {
- skipCinematic = true;
- cinematicMaxSkipTime = gameLocal.time + SEC2MS( g_cinematicMaxSkipTime.GetFloat() );
- }
- return true;
- }
- /*
- ======================
- idGameLocal::SpreadLocations
- Now that everything has been spawned, associate areas with location entities
- ======================
- */
- void idGameLocal::SpreadLocations() {
- idEntity *ent;
- // allocate the area table
- int numAreas = gameRenderWorld->NumAreas();
- locationEntities = new idLocationEntity *[ numAreas ];
- memset( locationEntities, 0, numAreas * sizeof( *locationEntities ) );
- // for each location entity, make pointers from every area it touches
- for( ent = spawnedEntities.Next(); ent != NULL; ent = ent->spawnNode.Next() ) {
- if ( !ent->IsType( idLocationEntity::Type ) ) {
- continue;
- }
- idVec3 point = ent->spawnArgs.GetVector( "origin" );
- int areaNum = gameRenderWorld->PointInArea( point );
- if ( areaNum < 0 ) {
- Printf( "SpreadLocations: location '%s' is not in a valid area\n", ent->spawnArgs.GetString( "name" ) );
- continue;
- }
- if ( areaNum >= numAreas ) {
- Error( "idGameLocal::SpreadLocations: areaNum >= gameRenderWorld->NumAreas()" );
- }
- if ( locationEntities[areaNum] ) {
- Warning( "location entity '%s' overlaps '%s'", ent->spawnArgs.GetString( "name" ),
- locationEntities[areaNum]->spawnArgs.GetString( "name" ) );
- continue;
- }
- locationEntities[areaNum] = static_cast<idLocationEntity *>(ent);
- // spread to all other connected areas
- for ( int i = 0 ; i < numAreas ; i++ ) {
- if ( i == areaNum ) {
- continue;
- }
- if ( gameRenderWorld->AreasAreConnected( areaNum, i, PS_BLOCK_LOCATION ) ) {
- locationEntities[i] = static_cast<idLocationEntity *>(ent);
- }
- }
- }
- }
- /*
- ===================
- idGameLocal::LocationForPoint
- The player checks the location each frame to update the HUD text display
- May return NULL
- ===================
- */
- idLocationEntity *idGameLocal::LocationForPoint( const idVec3 &point ) {
- if ( !locationEntities ) {
- // before SpreadLocations() has been called
- return NULL;
- }
- int areaNum = gameRenderWorld->PointInArea( point );
- if ( areaNum < 0 ) {
- return NULL;
- }
- if ( areaNum >= gameRenderWorld->NumAreas() ) {
- Error( "idGameLocal::LocationForPoint: areaNum >= gameRenderWorld->NumAreas()" );
- }
- return locationEntities[ areaNum ];
- }
- /*
- ============
- idGameLocal::SetPortalState
- ============
- */
- void idGameLocal::SetPortalState( qhandle_t portal, int blockingBits ) {
- idBitMsg outMsg;
- byte msgBuf[ MAX_GAME_MESSAGE_SIZE ];
- if ( !gameLocal.isClient ) {
- outMsg.Init( msgBuf, sizeof( msgBuf ) );
- outMsg.WriteByte( GAME_RELIABLE_MESSAGE_PORTAL );
- outMsg.WriteLong( portal );
- outMsg.WriteBits( blockingBits, NUM_RENDER_PORTAL_BITS );
- networkSystem->ServerSendReliableMessage( -1, outMsg );
- }
- gameRenderWorld->SetPortalState( portal, blockingBits );
- }
- /*
- ============
- idGameLocal::sortSpawnPoints
- ============
- */
- int idGameLocal::sortSpawnPoints( const void *ptr1, const void *ptr2 ) {
- const spawnSpot_t *spot1 = static_cast<const spawnSpot_t *>( ptr1 );
- const spawnSpot_t *spot2 = static_cast<const spawnSpot_t *>( ptr2 );
- float diff;
- diff = spot1->dist - spot2->dist;
- if ( diff < 0.0f ) {
- return 1;
- } else if ( diff > 0.0f ) {
- return -1;
- } else {
- return 0;
- }
- }
- /*
- ===========
- idGameLocal::RandomizeInitialSpawns
- randomize the order of the initial spawns
- prepare for a sequence of initial player spawns
- ============
- */
- void idGameLocal::RandomizeInitialSpawns( void ) {
- spawnSpot_t spot;
- int i, j;
- idEntity *ent;
- if ( !isMultiplayer || isClient ) {
- return;
- }
- spawnSpots.Clear();
- initialSpots.Clear();
- spot.dist = 0;
- spot.ent = FindEntityUsingDef( NULL, "info_player_deathmatch" );
- while( spot.ent ) {
- spawnSpots.Append( spot );
- if ( spot.ent->spawnArgs.GetBool( "initial" ) ) {
- initialSpots.Append( spot.ent );
- }
- spot.ent = FindEntityUsingDef( spot.ent, "info_player_deathmatch" );
- }
- if ( !spawnSpots.Num() ) {
- common->Warning( "no info_player_deathmatch in map" );
- return;
- }
- common->Printf( "%d spawns (%d initials)\n", spawnSpots.Num(), initialSpots.Num() );
- // if there are no initial spots in the map, consider they can all be used as initial
- if ( !initialSpots.Num() ) {
- common->Warning( "no info_player_deathmatch entities marked initial in map" );
- for ( i = 0; i < spawnSpots.Num(); i++ ) {
- initialSpots.Append( spawnSpots[ i ].ent );
- }
- }
- for ( i = 0; i < initialSpots.Num(); i++ ) {
- j = random.RandomInt( initialSpots.Num() );
- ent = initialSpots[ i ];
- initialSpots[ i ] = initialSpots[ j ];
- initialSpots[ j ] = ent;
- }
- // reset the counter
- currentInitialSpot = 0;
- }
- /*
- ===========
- idGameLocal::SelectInitialSpawnPoint
- spectators are spawned randomly anywhere
- in-game clients are spawned based on distance to active players (randomized on the first half)
- upon map restart, initial spawns are used (randomized ordered list of spawns flagged "initial")
- if there are more players than initial spots, overflow to regular spawning
- ============
- */
- idEntity *idGameLocal::SelectInitialSpawnPoint( idPlayer *player ) {
- int i, j, which;
- spawnSpot_t spot;
- idVec3 pos;
- float dist;
- bool alone;
- if ( !isMultiplayer || !spawnSpots.Num() ) {
- spot.ent = FindEntityUsingDef( NULL, "info_player_start" );
- if ( !spot.ent ) {
- Error( "No info_player_start on map.\n" );
- }
- return spot.ent;
- }
- if ( player->spectating ) {
- // plain random spot, don't bother
- return spawnSpots[ random.RandomInt( spawnSpots.Num() ) ].ent;
- } else if ( player->useInitialSpawns && currentInitialSpot < initialSpots.Num() ) {
- return initialSpots[ currentInitialSpot++ ];
- } else {
- // check if we are alone in map
- alone = true;
- for ( j = 0; j < MAX_CLIENTS; j++ ) {
- if ( entities[ j ] && entities[ j ] != player ) {
- alone = false;
- break;
- }
- }
- if ( alone ) {
- // don't do distance-based
- return spawnSpots[ random.RandomInt( spawnSpots.Num() ) ].ent;
- }
- // find the distance to the closest active player for each spawn spot
- for( i = 0; i < spawnSpots.Num(); i++ ) {
- pos = spawnSpots[ i ].ent->GetPhysics()->GetOrigin();
- spawnSpots[ i ].dist = 0x7fffffff;
- for( j = 0; j < MAX_CLIENTS; j++ ) {
- if ( !entities[ j ] || !entities[ j ]->IsType( idPlayer::Type )
- || entities[ j ] == player
- || static_cast< idPlayer * >( entities[ j ] )->spectating ) {
- continue;
- }
-
- dist = ( pos - entities[ j ]->GetPhysics()->GetOrigin() ).LengthSqr();
- if ( dist < spawnSpots[ i ].dist ) {
- spawnSpots[ i ].dist = dist;
- }
- }
- }
- // sort the list
- qsort( ( void * )spawnSpots.Ptr(), spawnSpots.Num(), sizeof( spawnSpot_t ), ( int (*)(const void *, const void *) )sortSpawnPoints );
- // choose a random one in the top half
- which = random.RandomInt( spawnSpots.Num() / 2 );
- spot = spawnSpots[ which ];
- }
- return spot.ent;
- }
- /*
- ================
- idGameLocal::UpdateServerInfoFlags
- ================
- */
- void idGameLocal::UpdateServerInfoFlags() {
- gameType = GAME_SP;
- if ( ( idStr::Icmp( serverInfo.GetString( "si_gameType" ), "deathmatch" ) == 0 ) ) {
- gameType = GAME_DM;
- } else if ( ( idStr::Icmp( serverInfo.GetString( "si_gameType" ), "Tourney" ) == 0 ) ) {
- gameType = GAME_TOURNEY;
- } else if ( ( idStr::Icmp( serverInfo.GetString( "si_gameType" ), "Team DM" ) == 0 ) ) {
- gameType = GAME_TDM;
- } else if ( ( idStr::Icmp( serverInfo.GetString( "si_gameType" ), "Last Man" ) == 0 ) ) {
- gameType = GAME_LASTMAN;
- }
- if ( gameType == GAME_LASTMAN ) {
- if ( !serverInfo.GetInt( "si_warmup" ) ) {
- common->Warning( "Last Man Standing - forcing warmup on" );
- serverInfo.SetInt( "si_warmup", 1 );
- }
- if ( serverInfo.GetInt( "si_fraglimit" ) <= 0 ) {
- common->Warning( "Last Man Standing - setting fraglimit 1" );
- serverInfo.SetInt( "si_fraglimit", 1 );
- }
- }
- }
- /*
- ================
- idGameLocal::SetGlobalMaterial
- ================
- */
- void idGameLocal::SetGlobalMaterial( const idMaterial *mat ) {
- globalMaterial = mat;
- }
- /*
- ================
- idGameLocal::GetGlobalMaterial
- ================
- */
- const idMaterial *idGameLocal::GetGlobalMaterial() {
- return globalMaterial;
- }
- /*
- ================
- idGameLocal::GetSpawnId
- ================
- */
- int idGameLocal::GetSpawnId( const idEntity* ent ) const {
- return ( gameLocal.spawnIds[ ent->entityNumber ] << GENTITYNUM_BITS ) | ent->entityNumber;
- }
- /*
- ================
- idGameLocal::ThrottleUserInfo
- ================
- */
- void idGameLocal::ThrottleUserInfo( void ) {
- mpGame.ThrottleUserInfo();
- }
- /*
- ===========
- idGameLocal::SelectTimeGroup
- ============
- */
- void idGameLocal::SelectTimeGroup( int timeGroup ) { }
- /*
- ===========
- idGameLocal::GetTimeGroupTime
- ============
- */
- int idGameLocal::GetTimeGroupTime( int timeGroup ) {
- return gameLocal.time;
- }
- /*
- ===========
- idGameLocal::GetBestGameType
- ============
- */
- void idGameLocal::GetBestGameType( const char* map, const char* gametype, char buf[ MAX_STRING_CHARS ] ) {
- strncpy( buf, gametype, MAX_STRING_CHARS );
- buf[ MAX_STRING_CHARS - 1 ] = '\0';
- }
- /*
- ===========
- idGameLocal::NeedRestart
- ============
- */
- bool idGameLocal::NeedRestart() {
- idDict newInfo;
- const idKeyValue *keyval, *keyval2;
- newInfo = *cvarSystem->MoveCVarsToDict( CVAR_SERVERINFO );
- for ( int i = 0; i < newInfo.GetNumKeyVals(); i++ ) {
- keyval = newInfo.GetKeyVal( i );
- keyval2 = serverInfo.FindKey( keyval->GetKey() );
- if ( !keyval2 ) {
- return true;
- }
- // a select set of si_ changes will cause a full restart of the server
- if ( keyval->GetValue().Cmp( keyval2->GetValue() ) && ( !keyval->GetKey().Cmp( "si_pure" ) || !keyval->GetKey().Cmp( "si_map" ) ) ) {
- return true;
- }
- }
- return false;
- }
- /*
- ================
- idGameLocal::GetClientStats
- ================
- */
- void idGameLocal::GetClientStats( int clientNum, char *data, const int len ) {
- mpGame.PlayerStats( clientNum, data, len );
- }
- /*
- ================
- idGameLocal::SwitchTeam
- ================
- */
- void idGameLocal::SwitchTeam( int clientNum, int team ) {
- idPlayer * player;
- player = clientNum >= 0 ? static_cast<idPlayer *>( gameLocal.entities[ clientNum ] ) : NULL;
- if ( !player )
- return;
- int oldTeam = player->team;
- // Put in spectator mode
- if ( team == -1 ) {
- static_cast< idPlayer * >( entities[ clientNum ] )->Spectate( true );
- }
- // Switch to a team
- else {
- mpGame.SwitchToTeam ( clientNum, oldTeam, team );
- }
- }
- /*
- ===============
- idGameLocal::GetMapLoadingGUI
- ===============
- */
- void idGameLocal::GetMapLoadingGUI( char gui[ MAX_STRING_CHARS ] ) { }
|