123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372637363746375637663776378637963806381638263836384638563866387638863896390639163926393639463956396639763986399640064016402640364046405640664076408640964106411641264136414641564166417641864196420642164226423642464256426642764286429643064316432643364346435643664376438643964406441644264436444644564466447644864496450645164526453645464556456645764586459646064616462646364646465646664676468646964706471647264736474647564766477647864796480648164826483648464856486648764886489649064916492649364946495649664976498649965006501650265036504650565066507650865096510651165126513651465156516651765186519652065216522652365246525652665276528652965306531653265336534653565366537653865396540654165426543654465456546654765486549655065516552655365546555655665576558655965606561656265636564656565666567656865696570657165726573657465756576657765786579658065816582658365846585658665876588658965906591659265936594659565966597659865996600660166026603660466056606660766086609661066116612661366146615661666176618661966206621662266236624662566266627662866296630663166326633663466356636663766386639664066416642664366446645664666476648664966506651665266536654665566566657665866596660666166626663666466656666666766686669667066716672667366746675667666776678667966806681668266836684668566866687668866896690669166926693669466956696669766986699670067016702670367046705670667076708670967106711671267136714671567166717671867196720672167226723672467256726672767286729673067316732673367346735673667376738673967406741674267436744674567466747674867496750675167526753675467556756675767586759676067616762676367646765676667676768676967706771677267736774677567766777677867796780678167826783678467856786678767886789679067916792679367946795679667976798679968006801680268036804680568066807680868096810681168126813681468156816681768186819682068216822682368246825682668276828682968306831683268336834683568366837683868396840684168426843684468456846684768486849685068516852685368546855685668576858685968606861686268636864686568666867686868696870687168726873687468756876687768786879688068816882688368846885688668876888688968906891689268936894689568966897689868996900690169026903690469056906690769086909691069116912691369146915691669176918691969206921692269236924692569266927692869296930693169326933693469356936693769386939694069416942694369446945694669476948694969506951695269536954695569566957695869596960696169626963696469656966696769686969697069716972697369746975697669776978697969806981698269836984698569866987698869896990699169926993699469956996699769986999700070017002700370047005700670077008700970107011701270137014701570167017701870197020702170227023702470257026702770287029703070317032703370347035703670377038703970407041704270437044704570467047704870497050705170527053705470557056705770587059706070617062706370647065706670677068706970707071707270737074707570767077707870797080708170827083708470857086708770887089709070917092709370947095709670977098709971007101710271037104710571067107710871097110711171127113711471157116711771187119712071217122712371247125712671277128712971307131713271337134713571367137713871397140714171427143714471457146714771487149715071517152715371547155715671577158715971607161716271637164716571667167716871697170717171727173717471757176717771787179718071817182718371847185718671877188718971907191719271937194719571967197719871997200720172027203720472057206720772087209721072117212721372147215721672177218721972207221722272237224722572267227722872297230723172327233723472357236723772387239724072417242724372447245724672477248724972507251725272537254725572567257725872597260726172627263726472657266726772687269727072717272727372747275727672777278727972807281728272837284728572867287728872897290729172927293729472957296729772987299730073017302730373047305730673077308730973107311731273137314731573167317731873197320732173227323732473257326732773287329733073317332733373347335733673377338733973407341734273437344734573467347734873497350735173527353735473557356735773587359736073617362736373647365736673677368736973707371737273737374737573767377737873797380738173827383738473857386738773887389739073917392739373947395739673977398739974007401740274037404740574067407740874097410741174127413741474157416741774187419742074217422742374247425742674277428742974307431743274337434743574367437743874397440744174427443744474457446744774487449745074517452745374547455745674577458745974607461746274637464746574667467746874697470747174727473747474757476747774787479748074817482748374847485748674877488748974907491749274937494749574967497749874997500750175027503750475057506750775087509751075117512751375147515751675177518751975207521752275237524752575267527752875297530753175327533753475357536753775387539754075417542754375447545754675477548754975507551755275537554755575567557755875597560756175627563756475657566756775687569757075717572757375747575757675777578757975807581758275837584758575867587758875897590759175927593759475957596759775987599760076017602760376047605760676077608760976107611761276137614761576167617761876197620762176227623762476257626762776287629763076317632763376347635763676377638763976407641764276437644764576467647764876497650765176527653765476557656765776587659766076617662766376647665766676677668766976707671767276737674767576767677767876797680768176827683768476857686768776887689769076917692769376947695769676977698769977007701770277037704770577067707770877097710771177127713771477157716771777187719772077217722772377247725772677277728772977307731773277337734773577367737773877397740774177427743774477457746774777487749775077517752775377547755775677577758775977607761776277637764776577667767776877697770777177727773777477757776777777787779778077817782778377847785778677877788778977907791779277937794779577967797779877997800780178027803780478057806780778087809781078117812781378147815781678177818781978207821782278237824782578267827782878297830783178327833783478357836783778387839784078417842784378447845784678477848784978507851785278537854785578567857785878597860786178627863786478657866786778687869787078717872787378747875787678777878787978807881788278837884788578867887788878897890789178927893789478957896789778987899790079017902790379047905790679077908790979107911791279137914791579167917791879197920792179227923792479257926792779287929793079317932793379347935793679377938793979407941794279437944794579467947794879497950795179527953795479557956795779587959796079617962796379647965796679677968796979707971797279737974797579767977797879797980798179827983798479857986798779887989799079917992799379947995799679977998799980008001800280038004800580068007800880098010801180128013801480158016801780188019802080218022802380248025802680278028802980308031803280338034803580368037803880398040804180428043804480458046804780488049805080518052805380548055805680578058805980608061806280638064806580668067806880698070807180728073807480758076807780788079808080818082808380848085808680878088808980908091809280938094809580968097809880998100810181028103810481058106810781088109811081118112811381148115811681178118811981208121812281238124812581268127812881298130813181328133813481358136813781388139814081418142814381448145814681478148814981508151815281538154815581568157815881598160816181628163816481658166816781688169817081718172817381748175817681778178817981808181818281838184818581868187818881898190819181928193819481958196819781988199820082018202820382048205820682078208820982108211821282138214821582168217821882198220822182228223822482258226822782288229823082318232823382348235823682378238823982408241824282438244824582468247824882498250825182528253825482558256825782588259826082618262826382648265826682678268826982708271827282738274827582768277827882798280828182828283828482858286828782888289829082918292829382948295829682978298829983008301830283038304830583068307830883098310831183128313831483158316831783188319832083218322832383248325832683278328832983308331833283338334833583368337833883398340834183428343834483458346834783488349835083518352835383548355835683578358835983608361836283638364836583668367836883698370837183728373837483758376837783788379838083818382838383848385838683878388838983908391839283938394839583968397839883998400840184028403840484058406840784088409841084118412841384148415841684178418841984208421842284238424842584268427842884298430843184328433843484358436843784388439844084418442844384448445844684478448844984508451845284538454845584568457845884598460846184628463846484658466846784688469847084718472847384748475847684778478847984808481848284838484848584868487848884898490849184928493849484958496849784988499850085018502850385048505850685078508850985108511851285138514851585168517851885198520852185228523852485258526852785288529853085318532853385348535853685378538853985408541854285438544854585468547854885498550855185528553855485558556855785588559856085618562856385648565856685678568856985708571857285738574857585768577857885798580858185828583858485858586858785888589859085918592859385948595859685978598859986008601860286038604860586068607860886098610861186128613861486158616861786188619862086218622862386248625862686278628862986308631863286338634863586368637863886398640864186428643864486458646864786488649865086518652865386548655865686578658865986608661866286638664866586668667866886698670867186728673867486758676867786788679868086818682868386848685868686878688868986908691869286938694869586968697869886998700870187028703870487058706870787088709871087118712871387148715871687178718871987208721872287238724872587268727872887298730873187328733873487358736873787388739874087418742874387448745874687478748874987508751875287538754875587568757875887598760876187628763876487658766876787688769877087718772877387748775877687778778877987808781878287838784878587868787878887898790879187928793879487958796879787988799880088018802880388048805880688078808880988108811881288138814881588168817881888198820882188228823882488258826882788288829883088318832883388348835883688378838883988408841884288438844884588468847884888498850885188528853885488558856885788588859886088618862886388648865886688678868886988708871887288738874887588768877887888798880888188828883888488858886888788888889889088918892889388948895889688978898889989008901890289038904890589068907890889098910891189128913891489158916891789188919892089218922892389248925892689278928892989308931893289338934893589368937893889398940894189428943894489458946894789488949895089518952895389548955895689578958895989608961896289638964896589668967896889698970897189728973897489758976897789788979898089818982898389848985898689878988898989908991899289938994899589968997899889999000900190029003900490059006900790089009901090119012901390149015901690179018901990209021902290239024902590269027902890299030903190329033903490359036903790389039904090419042904390449045904690479048904990509051905290539054905590569057905890599060906190629063906490659066906790689069907090719072907390749075907690779078907990809081908290839084908590869087908890899090909190929093909490959096909790989099910091019102910391049105910691079108910991109111911291139114911591169117911891199120912191229123912491259126912791289129913091319132913391349135913691379138913991409141914291439144914591469147914891499150915191529153915491559156915791589159916091619162916391649165916691679168916991709171917291739174917591769177917891799180918191829183918491859186918791889189919091919192919391949195919691979198919992009201920292039204920592069207920892099210921192129213921492159216921792189219922092219222922392249225922692279228922992309231923292339234923592369237923892399240924192429243924492459246924792489249925092519252925392549255925692579258925992609261926292639264926592669267926892699270927192729273927492759276927792789279928092819282928392849285928692879288928992909291929292939294929592969297929892999300930193029303930493059306930793089309931093119312931393149315931693179318931993209321932293239324932593269327932893299330933193329333933493359336933793389339934093419342934393449345934693479348934993509351935293539354935593569357935893599360936193629363936493659366936793689369937093719372937393749375937693779378937993809381938293839384938593869387938893899390939193929393939493959396939793989399940094019402940394049405940694079408940994109411941294139414941594169417941894199420942194229423942494259426942794289429943094319432943394349435943694379438943994409441944294439444944594469447944894499450945194529453945494559456945794589459946094619462946394649465946694679468946994709471947294739474947594769477947894799480948194829483948494859486948794889489949094919492949394949495949694979498949995009501950295039504950595069507950895099510951195129513951495159516951795189519952095219522952395249525952695279528952995309531953295339534953595369537953895399540954195429543954495459546954795489549955095519552955395549555955695579558955995609561956295639564956595669567956895699570957195729573957495759576957795789579958095819582958395849585958695879588958995909591959295939594959595969597959895999600960196029603960496059606960796089609961096119612961396149615961696179618961996209621962296239624962596269627962896299630963196329633963496359636963796389639964096419642964396449645964696479648964996509651965296539654965596569657965896599660966196629663966496659666966796689669967096719672967396749675967696779678967996809681968296839684968596869687968896899690969196929693969496959696969796989699970097019702970397049705970697079708970997109711971297139714971597169717971897199720972197229723972497259726972797289729973097319732973397349735973697379738973997409741974297439744974597469747974897499750975197529753975497559756975797589759976097619762976397649765976697679768976997709771977297739774977597769777977897799780978197829783978497859786978797889789979097919792979397949795979697979798979998009801980298039804980598069807980898099810981198129813981498159816981798189819982098219822982398249825982698279828982998309831983298339834983598369837983898399840984198429843984498459846984798489849985098519852985398549855985698579858985998609861986298639864986598669867986898699870987198729873987498759876987798789879988098819882988398849885988698879888988998909891989298939894989598969897989898999900990199029903990499059906990799089909991099119912991399149915991699179918991999209921992299239924992599269927992899299930993199329933993499359936993799389939994099419942994399449945994699479948994999509951995299539954995599569957995899599960996199629963996499659966996799689969997099719972997399749975997699779978997999809981998299839984998599869987998899899990999199929993999499959996999799989999100001000110002100031000410005100061000710008100091001010011100121001310014100151001610017100181001910020100211002210023100241002510026100271002810029100301003110032100331003410035100361003710038100391004010041100421004310044100451004610047100481004910050100511005210053100541005510056100571005810059100601006110062100631006410065100661006710068100691007010071100721007310074100751007610077100781007910080100811008210083100841008510086100871008810089100901009110092100931009410095100961009710098100991010010101101021010310104101051010610107101081010910110101111011210113101141011510116101171011810119101201012110122101231012410125101261012710128101291013010131101321013310134101351013610137101381013910140101411014210143101441014510146101471014810149101501015110152101531015410155101561015710158101591016010161101621016310164101651016610167101681016910170101711017210173101741017510176101771017810179101801018110182101831018410185101861018710188101891019010191101921019310194101951019610197101981019910200102011020210203102041020510206102071020810209102101021110212102131021410215102161021710218102191022010221102221022310224102251022610227102281022910230102311023210233102341023510236102371023810239102401024110242102431024410245102461024710248102491025010251102521025310254102551025610257102581025910260102611026210263102641026510266102671026810269102701027110272102731027410275102761027710278102791028010281102821028310284102851028610287102881028910290102911029210293102941029510296102971029810299103001030110302103031030410305103061030710308103091031010311103121031310314103151031610317103181031910320103211032210323103241032510326103271032810329103301033110332103331033410335103361033710338103391034010341103421034310344103451034610347103481034910350103511035210353103541035510356103571035810359103601036110362103631036410365103661036710368103691037010371103721037310374103751037610377103781037910380103811038210383103841038510386103871038810389103901039110392103931039410395103961039710398103991040010401104021040310404104051040610407104081040910410104111041210413104141041510416104171041810419104201042110422104231042410425104261042710428104291043010431104321043310434104351043610437104381043910440104411044210443104441044510446104471044810449104501045110452104531045410455104561045710458104591046010461104621046310464104651046610467104681046910470104711047210473104741047510476104771047810479104801048110482104831048410485104861048710488104891049010491104921049310494104951049610497104981049910500105011050210503105041050510506105071050810509105101051110512105131051410515105161051710518105191052010521105221052310524105251052610527105281052910530105311053210533105341053510536105371053810539105401054110542105431054410545105461054710548105491055010551105521055310554105551055610557105581055910560105611056210563105641056510566105671056810569105701057110572105731057410575105761057710578105791058010581105821058310584105851058610587105881058910590105911059210593105941059510596105971059810599106001060110602106031060410605106061060710608106091061010611106121061310614106151061610617106181061910620106211062210623106241062510626106271062810629106301063110632106331063410635106361063710638106391064010641106421064310644106451064610647106481064910650106511065210653106541065510656106571065810659106601066110662106631066410665106661066710668106691067010671106721067310674106751067610677106781067910680106811068210683106841068510686106871068810689106901069110692106931069410695106961069710698106991070010701107021070310704107051070610707107081070910710107111071210713107141071510716107171071810719107201072110722107231072410725107261072710728107291073010731107321073310734107351073610737107381073910740107411074210743 |
- /*
- ===========================================================================
- Doom 3 BFG Edition GPL Source Code
- Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
- This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
- Doom 3 BFG Edition 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 BFG Edition 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 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
- In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below.
- 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"
- #include "../framework/Common_local.h"
- #include "PredictedValue_impl.h"
- idCVar flashlight_batteryDrainTimeMS( "flashlight_batteryDrainTimeMS", "30000", CVAR_INTEGER, "amount of time (in MS) it takes for full battery to drain (-1 == no battery drain)" );
- idCVar flashlight_batteryChargeTimeMS( "flashlight_batteryChargeTimeMS", "3000", CVAR_INTEGER, "amount of time (in MS) it takes to fully recharge battery" );
- idCVar flashlight_minActivatePercent( "flashlight_minActivatePercent", ".25", CVAR_FLOAT, "( 0.0 - 1.0 ) minimum amount of battery (%) needed to turn on flashlight" );
- idCVar flashlight_batteryFlickerPercent( "flashlight_batteryFlickerPercent", ".1", CVAR_FLOAT, "chance of flickering when battery is low" );
- // No longer userinfo, but I don't want to rename the cvar
- idCVar ui_showGun( "ui_showGun", "1", CVAR_GAME | CVAR_ARCHIVE | CVAR_BOOL, "show gun" );
- // Client-authoritative stuff
- idCVar pm_clientAuthoritative_debug( "pm_clientAuthoritative_debug", "0", CVAR_BOOL, "" );
- idCVar pm_controllerShake_damageMaxMag( "pm_controllerShake_damageMaxMag", "60.0f", CVAR_FLOAT, "" );
- idCVar pm_controllerShake_damageMaxDur( "pm_controllerShake_damageMaxDur", "60.0f", CVAR_FLOAT, "" );
- idCVar pm_clientAuthoritative_warnDist( "pm_clientAuthoritative_warnDist", "100.0f", CVAR_FLOAT, "" );
- idCVar pm_clientAuthoritative_minDistZ( "pm_clientAuthoritative_minDistZ", "1.0f", CVAR_FLOAT, "" );
- idCVar pm_clientAuthoritative_minDist( "pm_clientAuthoritative_minDist", "-1.0f", CVAR_FLOAT, "" );
- idCVar pm_clientAuthoritative_Lerp( "pm_clientAuthoritative_Lerp", "0.9f", CVAR_FLOAT, "" );
- idCVar pm_clientAuthoritative_Divergence( "pm_clientAuthoritative_Divergence", "200.0f", CVAR_FLOAT, "" );
- idCVar pm_clientInterpolation_Divergence( "pm_clientInterpolation_Divergence", "5000.0f", CVAR_FLOAT, "" );
- idCVar pm_clientAuthoritative_minSpeedSquared( "pm_clientAuthoritative_minSpeedSquared", "1000.0f", CVAR_FLOAT, "" );
- extern idCVar g_demoMode;
- /*
- ===============================================================================
- Player control of the Doom Marine.
- This object handles all player movement and world interaction.
- ===============================================================================
- */
- // distance between ladder rungs (actually is half that distance, but this sounds better)
- const int LADDER_RUNG_DISTANCE = 32;
- // amount of health per dose from the health station
- const int HEALTH_PER_DOSE = 10;
- // time before a weapon dropped to the floor disappears
- const int WEAPON_DROP_TIME = 20 * 1000;
- // time before a next or prev weapon switch happens
- const int WEAPON_SWITCH_DELAY = 150;
- // how many units to raise spectator above default view height so it's in the head of someone
- const int SPECTATE_RAISE = 25;
- const int HEALTHPULSE_TIME = 333;
- // minimum speed to bob and play run/walk animations at
- const float MIN_BOB_SPEED = 5.0f;
- // Special team used for spectators that we ONLY store on lobby. The local team property on player remains as 0 or 1.
- const float LOBBY_SPECTATE_TEAM_FOR_VOICE_CHAT = 2;
- const idEventDef EV_Player_GetButtons( "getButtons", NULL, 'd' );
- const idEventDef EV_Player_GetMove( "getMove", NULL, 'v' );
- const idEventDef EV_Player_GetViewAngles( "getViewAngles", NULL, 'v' );
- const idEventDef EV_Player_StopFxFov( "stopFxFov" );
- const idEventDef EV_Player_EnableWeapon( "enableWeapon" );
- const idEventDef EV_Player_DisableWeapon( "disableWeapon" );
- const idEventDef EV_Player_GetCurrentWeapon( "getCurrentWeapon", NULL, 's' );
- const idEventDef EV_Player_GetPreviousWeapon( "getPreviousWeapon", NULL, 's' );
- const idEventDef EV_Player_SelectWeapon( "selectWeapon", "s" );
- const idEventDef EV_Player_GetWeaponEntity( "getWeaponEntity", NULL, 'e' );
- const idEventDef EV_Player_OpenPDA( "openPDA" );
- const idEventDef EV_Player_InPDA( "inPDA", NULL, 'd' );
- const idEventDef EV_Player_ExitTeleporter( "exitTeleporter" );
- const idEventDef EV_Player_StopAudioLog( "stopAudioLog" );
- const idEventDef EV_Player_HideTip( "hideTip" );
- const idEventDef EV_Player_LevelTrigger( "levelTrigger" );
- const idEventDef EV_SpectatorTouch( "spectatorTouch", "et" );
- const idEventDef EV_Player_GiveInventoryItem( "giveInventoryItem", "s" );
- const idEventDef EV_Player_RemoveInventoryItem( "removeInventoryItem", "s" );
- const idEventDef EV_Player_GetIdealWeapon( "getIdealWeapon", NULL, 's' );
- const idEventDef EV_Player_SetPowerupTime( "setPowerupTime", "dd" );
- const idEventDef EV_Player_IsPowerupActive( "isPowerupActive", "d", 'd' );
- const idEventDef EV_Player_WeaponAvailable( "weaponAvailable", "s", 'd');
- const idEventDef EV_Player_StartWarp( "startWarp" );
- const idEventDef EV_Player_StopHelltime( "stopHelltime", "d" );
- const idEventDef EV_Player_ToggleBloom( "toggleBloom", "d" );
- const idEventDef EV_Player_SetBloomParms( "setBloomParms", "ff" );
- CLASS_DECLARATION( idActor, idPlayer )
- EVENT( EV_Player_GetButtons, idPlayer::Event_GetButtons )
- EVENT( EV_Player_GetMove, idPlayer::Event_GetMove )
- EVENT( EV_Player_GetViewAngles, idPlayer::Event_GetViewAngles )
- EVENT( EV_Player_StopFxFov, idPlayer::Event_StopFxFov )
- EVENT( EV_Player_EnableWeapon, idPlayer::Event_EnableWeapon )
- EVENT( EV_Player_DisableWeapon, idPlayer::Event_DisableWeapon )
- EVENT( EV_Player_GetCurrentWeapon, idPlayer::Event_GetCurrentWeapon )
- EVENT( EV_Player_GetPreviousWeapon, idPlayer::Event_GetPreviousWeapon )
- EVENT( EV_Player_SelectWeapon, idPlayer::Event_SelectWeapon )
- EVENT( EV_Player_GetWeaponEntity, idPlayer::Event_GetWeaponEntity )
- EVENT( EV_Player_OpenPDA, idPlayer::Event_OpenPDA )
- EVENT( EV_Player_InPDA, idPlayer::Event_InPDA )
- EVENT( EV_Player_ExitTeleporter, idPlayer::Event_ExitTeleporter )
- EVENT( EV_Player_StopAudioLog, idPlayer::Event_StopAudioLog )
- EVENT( EV_Player_HideTip, idPlayer::Event_HideTip )
- EVENT( EV_Player_LevelTrigger, idPlayer::Event_LevelTrigger )
- EVENT( EV_Gibbed, idPlayer::Event_Gibbed )
- EVENT( EV_Player_GiveInventoryItem, idPlayer::Event_GiveInventoryItem )
- EVENT( EV_Player_RemoveInventoryItem, idPlayer::Event_RemoveInventoryItem )
- EVENT( EV_Player_GetIdealWeapon, idPlayer::Event_GetIdealWeapon )
- EVENT( EV_Player_WeaponAvailable, idPlayer::Event_WeaponAvailable )
- EVENT( EV_Player_SetPowerupTime, idPlayer::Event_SetPowerupTime )
- EVENT( EV_Player_IsPowerupActive, idPlayer::Event_IsPowerupActive )
- EVENT( EV_Player_StartWarp, idPlayer::Event_StartWarp )
- EVENT( EV_Player_StopHelltime, idPlayer::Event_StopHelltime )
- EVENT( EV_Player_ToggleBloom, idPlayer::Event_ToggleBloom )
- EVENT( EV_Player_SetBloomParms, idPlayer::Event_SetBloomParms )
- END_CLASS
- const int MAX_RESPAWN_TIME = 10000;
- const int RAGDOLL_DEATH_TIME = 3000;
- const int MAX_PDAS = 64;
- const int MAX_PDA_ITEMS = 128;
- const int STEPUP_TIME = 200;
- const int MAX_INVENTORY_ITEMS = 20;
- /*
- ==============
- idInventory::Clear
- ==============
- */
- void idInventory::Clear() {
- maxHealth = 0;
- weapons = 0;
- powerups = 0;
- armor = 0;
- maxarmor = 0;
- deplete_armor = 0;
- deplete_rate = 0.0f;
- deplete_ammount = 0;
- nextArmorDepleteTime = 0;
- for ( int i = 0; i < ammo.Num(); ++i ) {
- ammo[i].Set( 0 );
- }
- ClearPowerUps();
- // set to -1 so that the gun knows to have a full clip the first time we get it and at the start of the level
- for ( int i = 0; i < clip.Num(); ++i ) {
- clip[i].Set( -1 );
- }
-
- items.DeleteContents( true );
- memset(pdasViewed, 0, 4 * sizeof( pdasViewed[0] ) );
- pdas.Clear();
- videos.Clear();
- emails.Clear();
- selVideo = 0;
- selEMail = 0;
- selPDA = 0;
- selAudio = 0;
- pdaOpened = false;
- levelTriggers.Clear();
- nextItemPickup = 0;
- nextItemNum = 1;
- onePickupTime = 0;
- pickupItemNames.Clear();
- objectiveNames.Clear();
- ammoPredictTime = 0;
- lastGiveTime = 0;
- ammoPulse = false;
- weaponPulse = false;
- armorPulse = false;
- }
- /*
- ==============
- idInventory::GivePowerUp
- ==============
- */
- void idInventory::GivePowerUp( idPlayer *player, int powerup, int msec ) {
- powerups |= 1 << powerup;
- powerupEndTime[ powerup ] = gameLocal.time + msec;
- }
- /*
- ==============
- idInventory::ClearPowerUps
- ==============
- */
- void idInventory::ClearPowerUps() {
- int i;
- for ( i = 0; i < MAX_POWERUPS; i++ ) {
- powerupEndTime[ i ] = 0;
- }
- powerups = 0;
- }
- /*
- ==============
- idInventory::GetPersistantData
- ==============
- */
- void idInventory::GetPersistantData( idDict &dict ) {
- int i;
- int num;
- idDict *item;
- idStr key;
- const idKeyValue *kv;
- const char *name;
- // armor
- dict.SetInt( "armor", armor );
- // don't bother with powerups, maxhealth, maxarmor, or the clip
- // ammo
- for( i = 0; i < AMMO_NUMTYPES; i++ ) {
- name = idWeapon::GetAmmoNameForNum( ( ammo_t )i );
- if ( name ) {
- dict.SetInt( name, ammo[ i ].Get() );
- }
- }
- //Save the clip data
- for( i = 0; i < MAX_WEAPONS; i++ ) {
- dict.SetInt( va("clip%i", i), clip[ i ].Get() );
- }
- // items
- num = 0;
- for( i = 0; i < items.Num(); i++ ) {
- item = items[ i ];
- // copy all keys with "inv_"
- kv = item->MatchPrefix( "inv_" );
- if ( kv ) {
- while( kv ) {
- sprintf( key, "item_%i %s", num, kv->GetKey().c_str() );
- dict.Set( key, kv->GetValue() );
- kv = item->MatchPrefix( "inv_", kv );
- }
- num++;
- }
- }
- dict.SetInt( "items", num );
- // pdas viewed
- for ( i = 0; i < 4; i++ ) {
- dict.SetInt( va("pdasViewed_%i", i), pdasViewed[i] );
- }
- dict.SetInt( "selPDA", selPDA );
- dict.SetInt( "selVideo", selVideo );
- dict.SetInt( "selEmail", selEMail );
- dict.SetInt( "selAudio", selAudio );
- dict.SetInt( "pdaOpened", pdaOpened );
- // pdas
- for ( i = 0; i < pdas.Num(); i++ ) {
- sprintf( key, "pda_%i", i );
- dict.Set( key, pdas[ i ]->GetName() );
- }
- dict.SetInt( "pdas", pdas.Num() );
- // video cds
- for ( i = 0; i < videos.Num(); i++ ) {
- sprintf( key, "video_%i", i );
- dict.Set( key, videos[ i ]->GetName() );
- }
- dict.SetInt( "videos", videos.Num() );
- // emails
- for ( i = 0; i < emails.Num(); i++ ) {
- sprintf( key, "email_%i", i );
- dict.Set( key, emails[ i ]->GetName() );
- }
- dict.SetInt( "emails", emails.Num() );
- // weapons
- dict.SetInt( "weapon_bits", weapons );
- dict.SetInt( "levelTriggers", levelTriggers.Num() );
- for ( i = 0; i < levelTriggers.Num(); i++ ) {
- sprintf( key, "levelTrigger_Level_%i", i );
- dict.Set( key, levelTriggers[i].levelName );
- sprintf( key, "levelTrigger_Trigger_%i", i );
- dict.Set( key, levelTriggers[i].triggerName );
- }
- }
- /*
- ==============
- idInventory::RestoreInventory
- ==============
- */
- void idInventory::RestoreInventory( idPlayer *owner, const idDict &dict ) {
- int i;
- int num;
- idDict *item;
- idStr key;
- idStr itemname;
- const idKeyValue *kv;
- const char *name;
- Clear();
- // health/armor
- maxHealth = dict.GetInt( "maxhealth", "100" );
- armor = dict.GetInt( "armor", "50" );
- maxarmor = dict.GetInt( "maxarmor", "100" );
- deplete_armor = dict.GetInt( "deplete_armor", "0" );
- deplete_rate = dict.GetFloat( "deplete_rate", "2.0" );
- deplete_ammount = dict.GetInt( "deplete_ammount", "1" );
- // the clip and powerups aren't restored
- // ammo
- for( i = 0; i < AMMO_NUMTYPES; i++ ) {
- name = idWeapon::GetAmmoNameForNum( ( ammo_t )i );
- if ( name ) {
- ammo[ i ] = dict.GetInt( name );
- }
- }
- //Restore the clip data
- for( i = 0; i < MAX_WEAPONS; i++ ) {
- clip[i] = dict.GetInt(va("clip%i", i), "-1");
- }
- // items
- num = dict.GetInt( "items" );
- items.SetNum( num );
- for( i = 0; i < num; i++ ) {
- item = new (TAG_ENTITY) idDict();
- items[ i ] = item;
- sprintf( itemname, "item_%i ", i );
- kv = dict.MatchPrefix( itemname );
- while( kv ) {
- key = kv->GetKey();
- key.Strip( itemname );
- item->Set( key, kv->GetValue() );
- kv = dict.MatchPrefix( itemname, kv );
- }
- }
- // pdas viewed
- for ( i = 0; i < 4; i++ ) {
- pdasViewed[i] = dict.GetInt(va("pdasViewed_%i", i));
- }
- selPDA = dict.GetInt( "selPDA" );
- selEMail = dict.GetInt( "selEmail" );
- selVideo = dict.GetInt( "selVideo" );
- selAudio = dict.GetInt( "selAudio" );
- pdaOpened = dict.GetBool( "pdaOpened" );
- // pdas
- num = dict.GetInt( "pdas" );
- pdas.SetNum( num );
- for ( i = 0; i < num; i++ ) {
- sprintf( itemname, "pda_%i", i );
- pdas[i] = static_cast<const idDeclPDA *>( declManager->FindType( DECL_PDA, dict.GetString( itemname, "default" ) ) );
- }
- // videos
- num = dict.GetInt( "videos" );
- videos.SetNum( num );
- for ( i = 0; i < num; i++ ) {
- sprintf( itemname, "video_%i", i );
- videos[i] = static_cast<const idDeclVideo *>( declManager->FindType( DECL_VIDEO, dict.GetString( itemname, "default" ) ) );
- }
- // emails
- num = dict.GetInt( "emails" );
- emails.SetNum( num );
- for ( i = 0; i < num; i++ ) {
- sprintf( itemname, "email_%i", i );
- emails[i] = static_cast<const idDeclEmail *>( declManager->FindType( DECL_EMAIL, dict.GetString( itemname, "default" ) ) );
- }
- // weapons are stored as a number for persistant data, but as strings in the entityDef
- weapons = dict.GetInt( "weapon_bits", "0" );
- if ( g_skill.GetInteger() >= 3 || cvarSystem->GetCVarBool( "fs_buildresources" ) ) {
- Give( owner, dict, "weapon", dict.GetString( "weapon_nightmare" ), NULL, false, ITEM_GIVE_FEEDBACK | ITEM_GIVE_UPDATE_STATE );
- } else {
- Give( owner, dict, "weapon", dict.GetString( "weapon" ), NULL, false, ITEM_GIVE_FEEDBACK | ITEM_GIVE_UPDATE_STATE );
- }
- num = dict.GetInt( "levelTriggers" );
- for ( i = 0; i < num; i++ ) {
- sprintf( itemname, "levelTrigger_Level_%i", i );
- idLevelTriggerInfo lti;
- lti.levelName = dict.GetString( itemname );
- sprintf( itemname, "levelTrigger_Trigger_%i", i );
- lti.triggerName = dict.GetString( itemname );
- levelTriggers.Append( lti );
- }
- }
- /*
- ==============
- idInventory::Save
- ==============
- */
- void idInventory::Save( idSaveGame *savefile ) const {
- int i;
- savefile->WriteInt( maxHealth );
- savefile->WriteInt( weapons );
- savefile->WriteInt( powerups );
- savefile->WriteInt( armor );
- savefile->WriteInt( maxarmor );
- savefile->WriteInt( ammoPredictTime );
- savefile->WriteInt( deplete_armor );
- savefile->WriteFloat( deplete_rate );
- savefile->WriteInt( deplete_ammount );
- savefile->WriteInt( nextArmorDepleteTime );
- for( i = 0; i < AMMO_NUMTYPES; i++ ) {
- savefile->WriteInt( ammo[ i ].Get() );
- }
- for( i = 0; i < MAX_WEAPONS; i++ ) {
- savefile->WriteInt( clip[ i ].Get() );
- }
- for( i = 0; i < MAX_POWERUPS; i++ ) {
- savefile->WriteInt( powerupEndTime[ i ] );
- }
- savefile->WriteInt( items.Num() );
- for( i = 0; i < items.Num(); i++ ) {
- savefile->WriteDict( items[ i ] );
- }
- savefile->WriteInt( pdasViewed[0] );
- savefile->WriteInt( pdasViewed[1] );
- savefile->WriteInt( pdasViewed[2] );
- savefile->WriteInt( pdasViewed[3] );
-
- savefile->WriteInt( selPDA );
- savefile->WriteInt( selVideo );
- savefile->WriteInt( selEMail );
- savefile->WriteInt( selAudio );
- savefile->WriteBool( pdaOpened );
- savefile->WriteInt( pdas.Num() );
- for( i = 0; i < pdas.Num(); i++ ) {
- savefile->WriteString( pdas[ i ]->GetName() );
- }
- savefile->WriteInt( pdaSecurity.Num() );
- for( i=0; i < pdaSecurity.Num(); i++ ) {
- savefile->WriteString( pdaSecurity[ i ] );
- }
- savefile->WriteInt( videos.Num() );
- for( i = 0; i < videos.Num(); i++ ) {
- savefile->WriteString( videos[ i ]->GetName() );
- }
- savefile->WriteInt( emails.Num() );
- for ( i = 0; i < emails.Num(); i++ ) {
- savefile->WriteString( emails[ i ]->GetName() );
- }
- savefile->WriteInt( nextItemPickup );
- savefile->WriteInt( nextItemNum );
- savefile->WriteInt( onePickupTime );
- savefile->WriteInt( pickupItemNames.Num() );
- for( i = 0; i < pickupItemNames.Num(); i++ ) {
- savefile->WriteString( pickupItemNames[i] );
- }
- savefile->WriteInt( objectiveNames.Num() );
- for( i = 0; i < objectiveNames.Num(); i++ ) {
- savefile->WriteMaterial( objectiveNames[i].screenshot );
- savefile->WriteString( objectiveNames[i].text );
- savefile->WriteString( objectiveNames[i].title );
- }
- savefile->WriteInt( levelTriggers.Num() );
- for ( i = 0; i < levelTriggers.Num(); i++ ) {
- savefile->WriteString( levelTriggers[i].levelName );
- savefile->WriteString( levelTriggers[i].triggerName );
- }
- savefile->WriteBool( ammoPulse );
- savefile->WriteBool( weaponPulse );
- savefile->WriteBool( armorPulse );
- savefile->WriteInt( lastGiveTime );
- for(i = 0; i < AMMO_NUMTYPES; i++) {
- savefile->WriteInt(rechargeAmmo[i].ammo);
- savefile->WriteInt(rechargeAmmo[i].rechargeTime);
- savefile->WriteString(rechargeAmmo[i].ammoName);
- }
- }
- /*
- ==============
- idInventory::Restore
- ==============
- */
- void idInventory::Restore( idRestoreGame *savefile ) {
- int i, num;
- savefile->ReadInt( maxHealth );
- savefile->ReadInt( weapons );
- savefile->ReadInt( powerups );
- savefile->ReadInt( armor );
- savefile->ReadInt( maxarmor );
- savefile->ReadInt( ammoPredictTime );
- savefile->ReadInt( deplete_armor );
- savefile->ReadFloat( deplete_rate );
- savefile->ReadInt( deplete_ammount );
- savefile->ReadInt( nextArmorDepleteTime );
- for( i = 0; i < AMMO_NUMTYPES; i++ ) {
- int savedAmmo = 0;
- savefile->ReadInt( savedAmmo );
- ammo[ i ].Set( savedAmmo );
- }
- for( i = 0; i < MAX_WEAPONS; i++ ) {
- int savedClip = 0;
- savefile->ReadInt( savedClip );
- clip[ i ].Set( savedClip );
- }
- for( i = 0; i < MAX_POWERUPS; i++ ) {
- savefile->ReadInt( powerupEndTime[ i ] );
- }
- savefile->ReadInt( num );
- for( i = 0; i < num; i++ ) {
- idDict *itemdict = new (TAG_ENTITY) idDict;
- savefile->ReadDict( itemdict );
- items.Append( itemdict );
- }
- // pdas
- savefile->ReadInt( pdasViewed[0] );
- savefile->ReadInt( pdasViewed[1] );
- savefile->ReadInt( pdasViewed[2] );
- savefile->ReadInt( pdasViewed[3] );
-
- savefile->ReadInt( selPDA );
- savefile->ReadInt( selVideo );
- savefile->ReadInt( selEMail );
- savefile->ReadInt( selAudio );
- savefile->ReadBool( pdaOpened );
- savefile->ReadInt( num );
- for( i = 0; i < num; i++ ) {
- idStr strPda;
- savefile->ReadString( strPda );
- pdas.Append( static_cast<const idDeclPDA *>( declManager->FindType( DECL_PDA, strPda ) ) );
- }
- // pda security clearances
- savefile->ReadInt( num );
- for ( i = 0; i < num; i++ ) {
- idStr invName;
- savefile->ReadString( invName );
- pdaSecurity.Append( invName );
- }
- // videos
- savefile->ReadInt( num );
- for( i = 0; i < num; i++ ) {
- idStr strVideo;
- savefile->ReadString( strVideo );
- videos.Append( static_cast<const idDeclVideo *>( declManager->FindType( DECL_VIDEO, strVideo ) ) );
- }
- // email
- savefile->ReadInt( num );
- for( i = 0; i < num; i++ ) {
- idStr strEmail;
- savefile->ReadString( strEmail );
- emails.Append( static_cast<const idDeclEmail *>( declManager->FindType( DECL_EMAIL, strEmail ) ) );
- }
- savefile->ReadInt( nextItemPickup );
- savefile->ReadInt( nextItemNum );
- savefile->ReadInt( onePickupTime );
- savefile->ReadInt( num );
- for( i = 0; i < num; i++ ) {
- idStr itemName;
- savefile->ReadString( itemName );
- pickupItemNames.Append( itemName );
- }
- savefile->ReadInt( num );
- for( i = 0; i < num; i++ ) {
- idObjectiveInfo obj;
- savefile->ReadMaterial( obj.screenshot );
- savefile->ReadString( obj.text );
- savefile->ReadString( obj.title );
- objectiveNames.Append( obj );
- }
- savefile->ReadInt( num );
- for ( i = 0; i < num; i++ ) {
- idLevelTriggerInfo lti;
- savefile->ReadString( lti.levelName );
- savefile->ReadString( lti.triggerName );
- levelTriggers.Append( lti );
- }
- savefile->ReadBool( ammoPulse );
- savefile->ReadBool( weaponPulse );
- savefile->ReadBool( armorPulse );
- savefile->ReadInt( lastGiveTime );
- for(i = 0; i < AMMO_NUMTYPES; i++) {
- savefile->ReadInt(rechargeAmmo[i].ammo);
- savefile->ReadInt(rechargeAmmo[i].rechargeTime);
-
- idStr name;
- savefile->ReadString(name);
- strcpy(rechargeAmmo[i].ammoName, name);
- }
- }
- /*
- ==============
- idInventory::AmmoIndexForAmmoClass
- ==============
- */
- ammo_t idInventory::AmmoIndexForAmmoClass( const char *ammo_classname ) const {
- return idWeapon::GetAmmoNumForName( ammo_classname );
- }
- /*
- ==============
- idInventory::AmmoIndexForAmmoClass
- ==============
- */
- int idInventory::MaxAmmoForAmmoClass( const idPlayer *owner, const char *ammo_classname ) const {
- return owner->spawnArgs.GetInt( va( "max_%s", ammo_classname ), "0" );
- }
- /*
- ==============
- idInventory::AmmoPickupNameForIndex
- ==============
- */
- const char *idInventory::AmmoPickupNameForIndex( ammo_t ammonum ) const {
- return idWeapon::GetAmmoPickupNameForNum( ammonum );
- }
- /*
- ==============
- idInventory::WeaponIndexForAmmoClass
- mapping could be prepared in the constructor
- ==============
- */
- int idInventory::WeaponIndexForAmmoClass( const idDict & spawnArgs, const char *ammo_classname ) const {
- int i;
- const char *weapon_classname;
- for( i = 0; i < MAX_WEAPONS; i++ ) {
- weapon_classname = spawnArgs.GetString( va( "def_weapon%d", i ) );
- if ( !weapon_classname ) {
- continue;
- }
- const idDeclEntityDef *decl = gameLocal.FindEntityDef( weapon_classname, false );
- if ( !decl ) {
- continue;
- }
- if ( !idStr::Icmp( ammo_classname, decl->dict.GetString( "ammoType" ) ) ) {
- return i;
- }
- }
- return -1;
- }
- /*
- ==============
- idInventory::AmmoIndexForWeaponClass
- ==============
- */
- ammo_t idInventory::AmmoIndexForWeaponClass( const char *weapon_classname, int *ammoRequired ) {
- const idDeclEntityDef *decl = gameLocal.FindEntityDef( weapon_classname, false );
- if ( !decl ) {
- //gameLocal.Error( "Unknown weapon in decl '%s'", weapon_classname );
- return 0;
- }
- if ( ammoRequired ) {
- *ammoRequired = decl->dict.GetInt( "ammoRequired" );
- }
- ammo_t ammo_i = AmmoIndexForAmmoClass( decl->dict.GetString( "ammoType" ) );
- return ammo_i;
- }
- /*
- ==============
- idInventory::AddPickupName
- ==============
- */
- void idInventory::AddPickupName( const char * name, idPlayer * owner ) { //_D3XP
- int num = pickupItemNames.Num();
- if ( ( num == 0 ) || ( pickupItemNames[ num - 1 ].Icmp( name ) != 0 ) ) {
- if ( idStr::Cmpn( name, STRTABLE_ID, STRTABLE_ID_LENGTH ) == 0 ) {
- pickupItemNames.Append( idLocalization::GetString( name ) );
- } else {
- pickupItemNames.Append( name );
- }
- }
- }
- /*
- ==============
- idInventory::Give
- ==============
- */
- bool idInventory::Give( idPlayer *owner, const idDict &spawnArgs, const char *statname, const char *value,
- idPredictedValue< int > * idealWeapon, bool updateHud, unsigned int giveFlags ) {
- int i;
- const char *pos;
- const char *end;
- int len;
- idStr weaponString;
- int max;
- const idDeclEntityDef *weaponDecl;
- bool tookWeapon;
- int amount;
- const char *name;
- if ( !idStr::Icmp( statname, "ammo_bloodstone" ) ) {
- i = AmmoIndexForAmmoClass( statname );
- max = MaxAmmoForAmmoClass( owner, statname );
- if(max <= 0) {
- if ( giveFlags & ITEM_GIVE_UPDATE_STATE ) {
- //No Max
- ammo[ i ] += atoi( value );
- }
- } else {
- //Already at or above the max so don't allow the give
- if(ammo[ i ].Get() >= max) {
- if ( giveFlags & ITEM_GIVE_UPDATE_STATE ) {
- ammo[ i ] = max;
- }
- return false;
- }
- if ( giveFlags & ITEM_GIVE_UPDATE_STATE ) {
- //We were below the max so accept the give but cap it at the max
- ammo[ i ] += atoi( value );
- if(ammo[ i ].Get() > max) {
- ammo[ i ] = max;
- }
- }
- }
- } else if ( !idStr::Icmpn( statname, "ammo_", 5 ) ) {
- i = AmmoIndexForAmmoClass( statname );
- max = MaxAmmoForAmmoClass( owner, statname );
- if ( ammo[ i ].Get() >= max ) {
- return false;
- }
- // Add ammo for the feedback flag because it's predicted.
- // If it is a misprediction, the client will be corrected in
- // a snapshot.
- if ( giveFlags & ITEM_GIVE_FEEDBACK ) {
- amount = atoi( value );
- if ( amount ) {
- ammo[ i ] += amount;
- if ( ( max > 0 ) && ( ammo[ i ].Get() > max ) ) {
- ammo[ i ] = max;
- }
- ammoPulse = true;
- }
- name = AmmoPickupNameForIndex( i );
- if ( idStr::Length( name ) ) {
- AddPickupName( name, owner ); //_D3XP
- }
- }
- } else if ( !idStr::Icmp( statname, "armor" ) ) {
- if ( armor >= maxarmor ) {
- return false; // can't hold any more, so leave the item
- }
- if ( giveFlags & ITEM_GIVE_UPDATE_STATE ) {
- amount = atoi( value );
- if ( amount ) {
- armor += amount;
- if ( armor > maxarmor ) {
- armor = maxarmor;
- }
- nextArmorDepleteTime = 0;
- armorPulse = true;
- }
- }
- } else if ( idStr::FindText( statname, "inclip_" ) == 0 ) {
- if ( giveFlags & ITEM_GIVE_UPDATE_STATE ) {
- idStr temp = statname;
- i = atoi(temp.Mid(7, 2));
- if ( i != -1 ) {
- // set, don't add. not going over the clip size limit.
- SetClipAmmoForWeapon( i, atoi( value ) );
- }
- }
- } else if ( !idStr::Icmp( statname, "invulnerability" ) ) {
- owner->GivePowerUp( INVULNERABILITY, SEC2MS( atof( value ) ), giveFlags );
- } else if ( !idStr::Icmp( statname, "helltime" ) ) {
- owner->GivePowerUp( HELLTIME, SEC2MS( atof( value ) ), giveFlags );
- } else if ( !idStr::Icmp( statname, "envirosuit" ) ) {
- owner->GivePowerUp( ENVIROSUIT, SEC2MS( atof( value ) ), giveFlags );
- owner->GivePowerUp( ENVIROTIME, SEC2MS( atof( value ) ), giveFlags );
- } else if ( !idStr::Icmp( statname, "berserk" ) ) {
- owner->GivePowerUp( BERSERK, SEC2MS( atof( value ) ), giveFlags );
- //} else if ( !idStr::Icmp( statname, "haste" ) ) {
- // owner->GivePowerUp( HASTE, SEC2MS( atof( value ) ) );
- } else if ( !idStr::Icmp( statname, "adrenaline" ) ) {
- if ( giveFlags & ITEM_GIVE_UPDATE_STATE ) {
- GivePowerUp( owner, ADRENALINE, SEC2MS( atof( value ) ) );
- }
- } else if ( !idStr::Icmp( statname, "mega" ) ) {
- if ( giveFlags & ITEM_GIVE_UPDATE_STATE ) {
- GivePowerUp( owner, MEGAHEALTH, SEC2MS( atof( value ) ) );
- }
- } else if ( !idStr::Icmp( statname, "weapon" ) ) {
- tookWeapon = false;
- for( pos = value; pos != NULL; pos = end ) {
- end = strchr( pos, ',' );
- if ( end ) {
- len = end - pos;
- end++;
- } else {
- len = strlen( pos );
- }
- idStr weaponName( pos, 0, len );
- // find the number of the matching weapon name
- for( i = 0; i < MAX_WEAPONS; i++ ) {
- if ( weaponName == spawnArgs.GetString( va( "def_weapon%d", i ) ) ) {
- break;
- }
- }
- if ( i >= MAX_WEAPONS ) {
- gameLocal.Warning( "Unknown weapon '%s'", weaponName.c_str() );
- continue;
- }
- // cache the media for this weapon
- weaponDecl = gameLocal.FindEntityDef( weaponName, false );
- // don't pickup "no ammo" weapon types twice
- // not for D3 SP .. there is only one case in the game where you can get a no ammo
- // weapon when you might already have it, in that case it is more conistent to pick it up
- if ( common->IsMultiplayer() && ( weapons & ( 1 << i ) ) && ( weaponDecl != NULL ) && !weaponDecl->dict.GetInt( "ammoRequired" ) ) {
- continue;
- }
- if ( !gameLocal.world->spawnArgs.GetBool( "no_Weapons" ) || ( weaponName == "weapon_fists" ) || ( weaponName == "weapon_soulcube" ) ) {
- if ( ( weapons & ( 1 << i ) ) == 0 || common->IsMultiplayer() ) {
- tookWeapon = true;
-
- // This is done during "feedback" so that clients can predict the ideal weapon.
- if ( giveFlags & ITEM_GIVE_FEEDBACK ) {
- idLobbyBase & lobby = session->GetActingGameStateLobbyBase();
- lobbyUserID_t & lobbyUserID = gameLocal.lobbyUserIDs[owner->entityNumber];
- if ( lobby.GetLobbyUserWeaponAutoSwitch( lobbyUserID ) && idealWeapon != NULL && i != owner->weapon_bloodstone_active1 && i != owner->weapon_bloodstone_active2 && i != owner->weapon_bloodstone_active3) {
- idealWeapon->Set( i );
- }
- }
- if ( giveFlags & ITEM_GIVE_UPDATE_STATE ) {
- if ( updateHud && lastGiveTime + 1000 < gameLocal.time ) {
- if ( owner->hud ) {
- owner->hud->GiveWeapon( owner, i );
- }
- lastGiveTime = gameLocal.time;
- }
- weaponPulse = true;
- weapons |= ( 1 << i );
-
- if ( weaponName != "weapon_pda" ) {
- for ( int index = 0; index < NUM_QUICK_SLOTS; ++index ) {
- if ( owner->GetQuickSlot( index ) == -1 ) {
- owner->SetQuickSlot( index, i );
- break;
- }
- }
- }
- }
- }
- }
- }
- return tookWeapon;
- } else if ( !idStr::Icmp( statname, "item" ) || !idStr::Icmp( statname, "icon" ) || !idStr::Icmp( statname, "name" ) ) {
- // ignore these as they're handled elsewhere
- return false;
- } else {
- // unknown item
- gameLocal.Warning( "Unknown stat '%s' added to player's inventory", statname );
- return false;
- }
- return true;
- }
- /*
- ===============
- idInventoy::Drop
- ===============
- */
- void idInventory::Drop( const idDict &spawnArgs, const char *weapon_classname, int weapon_index ) {
- // remove the weapon bit
- // also remove the ammo associated with the weapon as we pushed it in the item
- assert( weapon_index != -1 || weapon_classname );
- if ( weapon_index == -1 ) {
- for( weapon_index = 0; weapon_index < MAX_WEAPONS; weapon_index++ ) {
- if ( !idStr::Icmp( weapon_classname, spawnArgs.GetString( va( "def_weapon%d", weapon_index ) ) ) ) {
- break;
- }
- }
- if ( weapon_index >= MAX_WEAPONS ) {
- gameLocal.Error( "Unknown weapon '%s'", weapon_classname );
- }
- } else if ( !weapon_classname ) {
- weapon_classname = spawnArgs.GetString( va( "def_weapon%d", weapon_index ) );
- }
- weapons &= ( 0xffffffff ^ ( 1 << weapon_index ) );
- ammo_t ammo_i = AmmoIndexForWeaponClass( weapon_classname, NULL );
- if ( ammo_i && ammo_i < AMMO_NUMTYPES ) {
- clip[ weapon_index ] = -1;
- ammo[ ammo_i ] = 0;
- }
- }
- /*
- ===============
- idInventory::HasAmmo
- ===============
- */
- int idInventory::HasAmmo( ammo_t type, int amount ) {
- if ( ( type == 0 ) || !amount ) {
- // always allow weapons that don't use ammo to fire
- return -1;
- }
- // check if we have infinite ammo
- if ( ammo[ type ].Get() < 0 ) {
- return -1;
- }
- // return how many shots we can fire
- return ammo[ type ].Get() / amount;
- }
- /*
- ===============
- idInventory::HasAmmo
- ===============
- */
- int idInventory::HasAmmo( const char *weapon_classname, bool includeClip, idPlayer* owner ) { //_D3XP
- int ammoRequired;
- ammo_t ammo_i = AmmoIndexForWeaponClass( weapon_classname, &ammoRequired );
- int ammoCount = HasAmmo( ammo_i, ammoRequired );
- if(includeClip && owner) {
- ammoCount += Max( 0, clip[owner->SlotForWeapon(weapon_classname)].Get() );
- }
- return ammoCount;
- }
- /*
- ===============
- idInventory::HasEmptyClipCannotRefill
- ===============
- */
- bool idInventory::HasEmptyClipCannotRefill(const char *weapon_classname, idPlayer* owner) {
-
- int clipSize = clip[owner->SlotForWeapon(weapon_classname)].Get();
- if(clipSize) {
- return false;
- }
- const idDeclEntityDef *decl = gameLocal.FindEntityDef( weapon_classname, false );
- if ( decl == NULL ) {
- gameLocal.Error( "Unknown weapon in decl '%s'", weapon_classname );
- return false;
- }
- int minclip = decl->dict.GetInt("minclipsize");
- if(!minclip) {
- return false;
- }
- ammo_t ammo_i = AmmoIndexForAmmoClass( decl->dict.GetString( "ammoType" ) );
- int ammoRequired = decl->dict.GetInt( "ammoRequired" );
- int ammoCount = HasAmmo( ammo_i, ammoRequired );
- if(ammoCount < minclip) {
- return true;
- }
- return false;
- }
- /*
- ===============
- idInventory::UseAmmo
- ===============
- */
- bool idInventory::UseAmmo( ammo_t type, int amount ) {
- if ( g_infiniteAmmo.GetBool() ) {
- return true;
- }
- if ( !HasAmmo( type, amount ) ) {
- return false;
- }
- // take an ammo away if not infinite
- if ( ammo[ type ].Get() >= 0 ) {
- const int currentAmmo = GetInventoryAmmoForType( type );
- SetInventoryAmmoForType( type, currentAmmo - amount );
- }
- return true;
- }
- /*
- ===============
- idInventory::UpdateArmor
- ===============
- */
- void idInventory::UpdateArmor() {
- if ( deplete_armor != 0.0f && deplete_armor < armor ) {
- if ( !nextArmorDepleteTime ) {
- nextArmorDepleteTime = gameLocal.time + deplete_rate * 1000;
- } else if ( gameLocal.time > nextArmorDepleteTime ) {
- armor -= deplete_ammount;
- if ( armor < deplete_armor ) {
- armor = deplete_armor;
- }
- nextArmorDepleteTime = gameLocal.time + deplete_rate * 1000;
- }
- }
- }
- /*
- ===============
- idInventory::InitRechargeAmmo
- ===============
- * Loads any recharge ammo definitions from the ammo_types entity definitions.
- */
- void idInventory::InitRechargeAmmo(idPlayer *owner) {
- memset (rechargeAmmo, 0, sizeof(rechargeAmmo));
- const idKeyValue *kv = owner->spawnArgs.MatchPrefix( "ammorecharge_" );
- while( kv ) {
- idStr key = kv->GetKey();
- idStr ammoname = key.Right(key.Length()- strlen("ammorecharge_"));
- int ammoType = AmmoIndexForAmmoClass(ammoname);
- rechargeAmmo[ammoType].ammo = (atof(kv->GetValue().c_str())*1000);
- strcpy(rechargeAmmo[ammoType].ammoName, ammoname);
- kv = owner->spawnArgs.MatchPrefix( "ammorecharge_", kv );
- }
- }
- /*
- ===============
- idInventory::RechargeAmmo
- ===============
- * Called once per frame to update any ammo amount for ammo types that recharge.
- */
- void idInventory::RechargeAmmo(idPlayer *owner) {
- for(int i = 0; i < AMMO_NUMTYPES; i++) {
- if(rechargeAmmo[i].ammo > 0) {
- if(!rechargeAmmo[i].rechargeTime) {
- //Initialize the recharge timer.
- rechargeAmmo[i].rechargeTime = gameLocal.time;
- }
- int elapsed = gameLocal.time - rechargeAmmo[i].rechargeTime;
- if(elapsed >= rechargeAmmo[i].ammo) {
- int intervals = (gameLocal.time - rechargeAmmo[i].rechargeTime)/rechargeAmmo[i].ammo;
- ammo[i] += intervals;
- int max = MaxAmmoForAmmoClass(owner, rechargeAmmo[i].ammoName);
- if(max > 0) {
- if(ammo[i].Get() > max) {
- ammo[i] = max;
- }
- }
- rechargeAmmo[i].rechargeTime += intervals*rechargeAmmo[i].ammo;
- }
- }
- }
- }
- /*
- ===============
- idInventory::CanGive
- ===============
- */
- bool idInventory::CanGive( idPlayer *owner, const idDict &spawnArgs, const char *statname, const char *value ) {
- if ( !idStr::Icmp( statname, "ammo_bloodstone" ) ) {
- int max = MaxAmmoForAmmoClass(owner, statname);
- int i = AmmoIndexForAmmoClass(statname);
- if(max <= 0) {
- //No Max
- return true;
- } else {
- //Already at or above the max so don't allow the give
- if(ammo[ i ].Get() >= max) {
- ammo[ i ] = max;
- return false;
- }
- return true;
- }
- } else if ( !idStr::Icmp( statname, "item" ) || !idStr::Icmp( statname, "icon" ) || !idStr::Icmp( statname, "name" ) ) {
- // ignore these as they're handled elsewhere
- //These items should not be considered as succesful gives because it messes up the max ammo items
- return false;
- }
- return true;
- }
- /*
- ===============
- idInventory::SetClipAmmoForWeapon
- Ammo is predicted on clients. This function ensures the frame the prediction occurs
- is stored so the predicted value doesn't get overwritten by snapshots. Of course
- the snapshot-reading function must check this value.
- ===============
- */
- void idInventory::SetClipAmmoForWeapon( const int weapon, const int amount ) {
- clip[weapon] = amount;
- }
- /*
- ===============
- idInventory::SetInventoryAmmoForType
- Ammo is predicted on clients. This function ensures the frame the prediction occurs
- is stored so the predicted value doesn't get overwritten by snapshots. Of course
- the snapshot-reading function must check this value.
- ===============
- */
- void idInventory::SetInventoryAmmoForType( int ammoType, const int amount ) {
- ammo[ammoType] = amount;
- }
- /*
- ===============
- idInventory::GetClipAmmoForWeapon
- ===============
- */
- int idInventory::GetClipAmmoForWeapon( const int weapon ) const {
- return clip[weapon].Get();
- }
- /*
- ===============
- idInventory::GetInventoryAmmoForType
- ===============
- */
- int idInventory::GetInventoryAmmoForType( const int ammoType ) const {
- return ammo[ammoType].Get();
- }
- /*
- ===============
- idInventory::WriteAmmoToSnapshot
- ===============
- */
- void idInventory::WriteAmmoToSnapshot( idBitMsg & msg ) const {
- for( int i = 0; i < AMMO_NUMTYPES; i++ ) {
- msg.WriteBits( ammo[i].Get(), ASYNC_PLAYER_INV_AMMO_BITS );
- }
- for( int i = 0; i < MAX_WEAPONS; i++ ) {
- msg.WriteBits( clip[i].Get(), ASYNC_PLAYER_INV_CLIP_BITS );
- }
- }
- /*
- ===============
- idInventory::ReadAmmoFromSnapshot
- ===============
- */
- void idInventory::ReadAmmoFromSnapshot( const idBitMsg & msg, const int ownerEntityNumber ) {
- for( int i = 0; i < ammo.Num(); i++ ) {
- const int snapshotAmmo = msg.ReadBits( ASYNC_PLAYER_INV_AMMO_BITS );
- ammo[i].UpdateFromSnapshot( snapshotAmmo, ownerEntityNumber );
- }
- for( int i = 0; i < clip.Num(); i++ ) {
- const int snapshotClip = msg.ReadBits( ASYNC_PLAYER_INV_CLIP_BITS );
- clip[i].UpdateFromSnapshot( snapshotClip, ownerEntityNumber );
- }
- }
- /*
- ===============
- idInventory::ReadAmmoFromSnapshot
- Doesn't really matter what remote client's ammo count is, so just set it to 999.
- ===============
- */
- void idInventory::SetRemoteClientAmmo( const int ownerEntityNumber ) {
- for ( int i = 0; i < ammo.Num(); ++i ) {
- ammo[i].UpdateFromSnapshot( 999, ownerEntityNumber );
- }
- }
- /*
- ==============
- idPlayer::idPlayer
- ==============
- */
- idPlayer::idPlayer():
- previousViewQuat( 0.0f, 0.0f, 0.0f, 1.0f ),
- nextViewQuat( 0.0f, 0.0f, 0.0f, 1.0f ),
- idealWeapon( -1 ),
- serverOverridePositionTime( 0 ),
- clientFireCount( 0 ) {
- noclip = false;
- godmode = false;
- spawnAnglesSet = false;
- spawnAngles = ang_zero;
- viewAngles = ang_zero;
- cmdAngles = ang_zero;
- independentWeaponPitchAngle = 0.0f;
- oldButtons = 0;
- buttonMask = 0;
- oldImpulseSequence = 0;
- lastHitTime = 0;
- lastSndHitTime = 0;
- lastSavingThrowTime = 0;
- laserSightHandle = -1;
- memset( &laserSightRenderEntity, 0, sizeof( laserSightRenderEntity ) );
- weapon = NULL;
- primaryObjective = NULL;
- hudManager = new idMenuHandler_HUD();
- hud = NULL;
- objectiveSystemOpen = false;
- memset( quickSlot, -1, sizeof( quickSlot ) );
- pdaMenu = new (TAG_SWF) idMenuHandler_PDA();
- pdaVideoMat = NULL;
- mpMessages = NULL;
- mountedObject = NULL;
- enviroSuitLight = NULL;
- heartRate = BASE_HEARTRATE;
- heartInfo.Init( 0, 0, 0, 0 );
- lastHeartAdjust = 0;
- lastHeartBeat = 0;
- lastDmgTime = 0;
- deathClearContentsTime = 0;
- lastArmorPulse = -10000;
- stamina = 0.0f;
- healthPool = 0.0f;
- nextHealthPulse = 0;
- healthPulse = false;
- nextHealthTake = 0;
- healthTake = false;
- forceScoreBoard = false;
- forceRespawn = false;
- spectating = false;
- spectator = 0;
- wantSpectate = true;
- carryingFlag = false;
- lastHitToggle = false;
- minRespawnTime = 0;
- maxRespawnTime = 0;
- firstPersonViewOrigin = vec3_zero;
- firstPersonViewAxis = mat3_identity;
- hipJoint = INVALID_JOINT;
- chestJoint = INVALID_JOINT;
- headJoint = INVALID_JOINT;
- bobFoot = 0;
- bobFrac = 0.0f;
- bobfracsin = 0.0f;
- bobCycle = 0;
- xyspeed = 0.0f;
- stepUpTime = 0;
- stepUpDelta = 0.0f;
- idealLegsYaw = 0.0f;
- legsYaw = 0.0f;
- legsForward = true;
- oldViewYaw = 0.0f;
- viewBobAngles = ang_zero;
- viewBob = vec3_zero;
- landChange = 0;
- landTime = 0;
- currentWeapon = -1;
- previousWeapon = -1;
- weaponSwitchTime = 0;
- weaponEnabled = true;
- weapon_soulcube = -1;
- weapon_pda = -1;
- weapon_fists = -1;
- weapon_chainsaw = -1;
- weapon_bloodstone = -1;
- weapon_bloodstone_active1 = -1;
- weapon_bloodstone_active2 = -1;
- weapon_bloodstone_active3 = -1;
- harvest_lock = false;
- hudPowerup = -1;
- lastHudPowerup = -1;
- hudPowerupDuration = 0;
- skinIndex = 0;
- skin = NULL;
- powerUpSkin = NULL;
- numProjectileKills = 0;
- numProjectilesFired = 0;
- numProjectileHits = 0;
- airless = false;
- airMsec = 0;
- lastAirDamage = 0;
- gibDeath = false;
- gibsLaunched = false;
- gibsDir = vec3_zero;
- zoomFov.Init( 0, 0, 0, 0 );
- centerView.Init( 0, 0, 0, 0 );
- fxFov = false;
- influenceFov = 0;
- influenceActive = 0;
- influenceRadius = 0.0f;
- influenceEntity = NULL;
- influenceMaterial = NULL;
- influenceSkin = NULL;
- privateCameraView = NULL;
- memset( loggedViewAngles, 0, sizeof( loggedViewAngles ) );
- memset( loggedAccel, 0, sizeof( loggedAccel ) );
- currentLoggedAccel = 0;
- focusTime = 0;
- focusGUIent = NULL;
- focusUI = NULL;
- focusCharacter = NULL;
- talkCursor = 0;
- focusVehicle = NULL;
- cursor = NULL;
-
- oldMouseX = 0;
- oldMouseY = 0;
- lastDamageDef = 0;
- lastDamageDir = vec3_zero;
- lastDamageLocation = 0;
- smoothedFrame = 0;
- smoothedOriginUpdated = false;
- smoothedOrigin = vec3_zero;
- smoothedAngles = ang_zero;
- fl.networkSync = true;
- doingDeathSkin = false;
- weaponGone = false;
- useInitialSpawns = false;
- tourneyRank = 0;
- lastSpectateTeleport = 0;
- tourneyLine = 0;
- hiddenWeapon = false;
- tipUp = false;
- objectiveUp = false;
- teleportEntity = NULL;
- teleportKiller = -1;
- respawning = false;
- leader = false;
- lastSpectateChange = 0;
- lastTeleFX = -9999;
- weaponCatchup = false;
- clientFireCount = 0;
- MPAim = -1;
- lastMPAim = -1;
- lastMPAimTime = 0;
- MPAimFadeTime = 0;
- MPAimHighlight = false;
- spawnedTime = 0;
- lastManOver = false;
- lastManPlayAgain = false;
- lastManPresent = false;
- isTelefragged = false;
- isLagged = false;
- isChatting = 0;
- selfSmooth = false;
- playedTimeSecs = 0;
- playedTimeResidual = 0;
- ResetControllerShake();
- memset( pdaHasBeenRead, 0, sizeof( pdaHasBeenRead ) );
- memset( videoHasBeenViewed, 0, sizeof( videoHasBeenViewed ) );
- memset( audioHasBeenHeard, 0, sizeof( audioHasBeenHeard ) );
- }
- /*
- ==============
- idPlayer::LinkScriptVariables
- set up conditions for animation
- ==============
- */
- void idPlayer::LinkScriptVariables() {
- AI_FORWARD.LinkTo( scriptObject, "AI_FORWARD" );
- AI_BACKWARD.LinkTo( scriptObject, "AI_BACKWARD" );
- AI_STRAFE_LEFT.LinkTo( scriptObject, "AI_STRAFE_LEFT" );
- AI_STRAFE_RIGHT.LinkTo( scriptObject, "AI_STRAFE_RIGHT" );
- AI_ATTACK_HELD.LinkTo( scriptObject, "AI_ATTACK_HELD" );
- AI_WEAPON_FIRED.LinkTo( scriptObject, "AI_WEAPON_FIRED" );
- AI_JUMP.LinkTo( scriptObject, "AI_JUMP" );
- AI_DEAD.LinkTo( scriptObject, "AI_DEAD" );
- AI_CROUCH.LinkTo( scriptObject, "AI_CROUCH" );
- AI_ONGROUND.LinkTo( scriptObject, "AI_ONGROUND" );
- AI_ONLADDER.LinkTo( scriptObject, "AI_ONLADDER" );
- AI_HARDLANDING.LinkTo( scriptObject, "AI_HARDLANDING" );
- AI_SOFTLANDING.LinkTo( scriptObject, "AI_SOFTLANDING" );
- AI_RUN.LinkTo( scriptObject, "AI_RUN" );
- AI_PAIN.LinkTo( scriptObject, "AI_PAIN" );
- AI_RELOAD.LinkTo( scriptObject, "AI_RELOAD" );
- AI_TELEPORT.LinkTo( scriptObject, "AI_TELEPORT" );
- AI_TURN_LEFT.LinkTo( scriptObject, "AI_TURN_LEFT" );
- AI_TURN_RIGHT.LinkTo( scriptObject, "AI_TURN_RIGHT" );
- }
- /*
- ==============
- idPlayer::SetupWeaponEntity
- ==============
- */
- void idPlayer::SetupWeaponEntity() {
- int w;
- const char *weap;
- if ( weapon.GetEntity() ) {
- // get rid of old weapon
- weapon.GetEntity()->Clear();
- currentWeapon = -1;
- } else if ( !common->IsClient() ) {
- weapon = static_cast<idWeapon *>( gameLocal.SpawnEntityType( idWeapon::Type, NULL ) );
- weapon.GetEntity()->SetOwner( this );
- currentWeapon = -1;
- // flashlight
- flashlight = static_cast<idWeapon *>( gameLocal.SpawnEntityType( idWeapon::Type, NULL ) );
- flashlight.GetEntity()->SetFlashlightOwner( this );
- //FlashlightOff();
- }
- for( w = 0; w < MAX_WEAPONS; w++ ) {
- weap = spawnArgs.GetString( va( "def_weapon%d", w ) );
- if ( weap != NULL && *weap != NULL ) {
- idWeapon::CacheWeapon( weap );
- }
- }
- }
- /*
- ==============
- idPlayer::Init
- ==============
- */
- void idPlayer::Init() {
- const char *value;
- const idKeyValue *kv;
- noclip = false;
- godmode = false;
- oldButtons = 0;
- oldImpulseSequence = 0;
- currentWeapon = -1;
- idealWeapon = -1;
- previousWeapon = -1;
- weaponSwitchTime = 0;
- weaponEnabled = true;
- weapon_soulcube = SlotForWeapon( "weapon_soulcube" );
- weapon_pda = SlotForWeapon( "weapon_pda" );
- weapon_fists = SlotForWeapon( "weapon_fists" );
- weapon_flashlight = SlotForWeapon( "weapon_flashlight" );
- weapon_chainsaw = SlotForWeapon( "weapon_chainsaw" );
- weapon_bloodstone = SlotForWeapon( "weapon_bloodstone_passive" );
- weapon_bloodstone_active1 = SlotForWeapon( "weapon_bloodstone_active1" );
- weapon_bloodstone_active2 = SlotForWeapon( "weapon_bloodstone_active2" );
- weapon_bloodstone_active3 = SlotForWeapon( "weapon_bloodstone_active3" );
- harvest_lock = false;
- lastDmgTime = 0;
- lastArmorPulse = -10000;
- lastHeartAdjust = 0;
- lastHeartBeat = 0;
- heartInfo.Init( 0, 0, 0, 0 );
- bobCycle = 0;
- bobFrac = 0.0f;
- landChange = 0;
- landTime = 0;
- zoomFov.Init( 0, 0, 0, 0 );
- centerView.Init( 0, 0, 0, 0 );
- fxFov = false;
- influenceFov = 0;
- influenceActive = 0;
- influenceRadius = 0.0f;
- influenceEntity = NULL;
- influenceMaterial = NULL;
- influenceSkin = NULL;
- mountedObject = NULL;
- if( enviroSuitLight.IsValid() ) {
- enviroSuitLight.GetEntity()->PostEventMS( &EV_Remove, 0 );
- }
- enviroSuitLight = NULL;
- healthRecharge = false;
- lastHealthRechargeTime = 0;
- rechargeSpeed = 500;
- new_g_damageScale = 1.f;
- bloomEnabled = false;
- bloomSpeed = 1.f;
- bloomIntensity = -0.01f;
- inventory.InitRechargeAmmo(this);
- hudPowerup = -1;
- lastHudPowerup = -1;
- hudPowerupDuration = 0;
- currentLoggedAccel = 0;
- focusTime = 0;
- focusGUIent = NULL;
- focusUI = NULL;
- focusCharacter = NULL;
- talkCursor = 0;
- focusVehicle = NULL;
- // remove any damage effects
- playerView.ClearEffects();
- // damage values
- fl.takedamage = true;
- ClearPain();
- // restore persistent data
- RestorePersistantInfo();
- bobCycle = 0;
- stamina = 0.0f;
- healthPool = 0.0f;
- nextHealthPulse = 0;
- healthPulse = false;
- nextHealthTake = 0;
- healthTake = false;
- SetupWeaponEntity();
- currentWeapon = -1;
- previousWeapon = -1;
- heartRate = BASE_HEARTRATE;
- AdjustHeartRate( BASE_HEARTRATE, 0.0f, 0.0f, true );
- idealLegsYaw = 0.0f;
- legsYaw = 0.0f;
- legsForward = true;
- oldViewYaw = 0.0f;
- // set the pm_ cvars
- if ( !common->IsMultiplayer() || common->IsServer() ) {
- kv = spawnArgs.MatchPrefix( "pm_", NULL );
- while( kv ) {
- cvarSystem->SetCVarString( kv->GetKey(), kv->GetValue() );
- kv = spawnArgs.MatchPrefix( "pm_", kv );
- }
- }
- // disable stamina on hell levels
- if ( gameLocal.world && gameLocal.world->spawnArgs.GetBool( "no_stamina" ) ) {
- pm_stamina.SetFloat( 0.0f );
- }
- // stamina always initialized to maximum
- stamina = pm_stamina.GetFloat();
- // air always initialized to maximum too
- airMsec = pm_airMsec.GetFloat();
- airless = false;
- gibDeath = false;
- gibsLaunched = false;
- gibsDir.Zero();
- // set the gravity
- physicsObj.SetGravity( gameLocal.GetGravity() );
- // start out standing
- SetEyeHeight( pm_normalviewheight.GetFloat() );
- stepUpTime = 0;
- stepUpDelta = 0.0f;
- viewBobAngles.Zero();
- viewBob.Zero();
- value = spawnArgs.GetString( "model" );
- if ( value != NULL && ( *value != 0 ) ) {
- SetModel( value );
- }
- if ( hud ) {
- hud->SetCursorState( this, CURSOR_TALK, 0 );
- hud->SetCursorState( this, CURSOR_IN_COMBAT, 1 );
- hud->SetCursorState( this, CURSOR_ITEM, 0 );
- hud->SetCursorState( this, CURSOR_GRABBER, 0 );
- hud->SetCursorState( this, CURSOR_NONE, 0 );
- hud->UpdateCursorState();
- }
- if ( ( common->IsMultiplayer() || g_testDeath.GetBool() ) && skin ) {
- SetSkin( skin );
- renderEntity.shaderParms[6] = 0.0f;
- } else if ( spawnArgs.GetString( "spawn_skin", NULL, &value ) ) {
- skin = declManager->FindSkin( value );
- SetSkin( skin );
- renderEntity.shaderParms[6] = 0.0f;
- }
- value = spawnArgs.GetString( "bone_hips", "" );
- hipJoint = animator.GetJointHandle( value );
- if ( hipJoint == INVALID_JOINT ) {
- gameLocal.Error( "Joint '%s' not found for 'bone_hips' on '%s'", value, name.c_str() );
- }
- value = spawnArgs.GetString( "bone_chest", "" );
- chestJoint = animator.GetJointHandle( value );
- if ( chestJoint == INVALID_JOINT ) {
- gameLocal.Error( "Joint '%s' not found for 'bone_chest' on '%s'", value, name.c_str() );
- }
- value = spawnArgs.GetString( "bone_head", "" );
- headJoint = animator.GetJointHandle( value );
- if ( headJoint == INVALID_JOINT ) {
- gameLocal.Error( "Joint '%s' not found for 'bone_head' on '%s'", value, name.c_str() );
- }
- // initialize the script variables
- AI_FORWARD = false;
- AI_BACKWARD = false;
- AI_STRAFE_LEFT = false;
- AI_STRAFE_RIGHT = false;
- AI_ATTACK_HELD = false;
- AI_WEAPON_FIRED = false;
- AI_JUMP = false;
- AI_DEAD = false;
- AI_CROUCH = false;
- AI_ONGROUND = true;
- AI_ONLADDER = false;
- AI_HARDLANDING = false;
- AI_SOFTLANDING = false;
- AI_RUN = false;
- AI_PAIN = false;
- AI_RELOAD = false;
- AI_TELEPORT = false;
- AI_TURN_LEFT = false;
- AI_TURN_RIGHT = false;
- // reset the script object
- ConstructScriptObject();
- // execute the script so the script object's constructor takes effect immediately
- scriptThread->Execute();
-
- forceScoreBoard = false;
- privateCameraView = NULL;
- lastSpectateChange = 0;
- lastTeleFX = -9999;
- hiddenWeapon = false;
- tipUp = false;
- objectiveUp = false;
- teleportEntity = NULL;
- teleportKiller = -1;
- leader = false;
- SetPrivateCameraView( NULL );
- MPAim = -1;
- lastMPAim = -1;
- lastMPAimTime = 0;
- MPAimFadeTime = 0;
- MPAimHighlight = false;
- //isChatting = false;
- achievementManager.Init( this );
- flashlightBattery = flashlight_batteryDrainTimeMS.GetInteger(); // fully charged
- aimAssist.Init( this );
- // laser sight for 3DTV
- memset( &laserSightRenderEntity, 0, sizeof( laserSightRenderEntity ) );
- laserSightRenderEntity.hModel = renderModelManager->FindModel( "_BEAM" );
- laserSightRenderEntity.customShader = declManager->FindMaterial( "stereoRenderLaserSight" );
- }
- /*
- ==============
- idPlayer::Spawn
- Prepare any resources used by the player.
- ==============
- */
- void idPlayer::Spawn() {
- idStr temp;
- idBounds bounds;
- if ( entityNumber >= MAX_CLIENTS ) {
- gameLocal.Error( "entityNum > MAX_CLIENTS for player. Player may only be spawned with a client." );
- }
- // allow thinking during cinematics
- cinematic = true;
- if ( common->IsMultiplayer() ) {
- // always start in spectating state waiting to be spawned in
- // do this before SetClipModel to get the right bounding box
- spectating = true;
- }
- // set our collision model
- physicsObj.SetSelf( this );
- SetClipModel();
- physicsObj.SetMass( spawnArgs.GetFloat( "mass", "100" ) );
- physicsObj.SetContents( CONTENTS_BODY );
- physicsObj.SetClipMask( MASK_PLAYERSOLID );
- SetPhysics( &physicsObj );
- InitAASLocation();
- skin = renderEntity.customSkin;
- // only the local player needs guis
- if ( !common->IsMultiplayer() || IsLocallyControlled() ) {
- // load HUD
- if ( hudManager != NULL ) {
- hudManager->Initialize( "hud", common->SW() );
- hudManager->ActivateMenu( true );
- hud = hudManager->GetHud();
- }
- // load cursor
- if ( spawnArgs.GetString( "cursor", "", temp ) ) {
- cursor = uiManager->FindGui( temp, true, common->IsMultiplayer(), common->IsMultiplayer() );
- }
- if ( cursor ) {
- cursor->Activate( true, gameLocal.time );
- }
- if ( pdaMenu != NULL ) {
- pdaMenu->Initialize( "pda", common->SW() );
- }
- objectiveSystemOpen = false;
- }
- if ( common->IsMultiplayer() && mpMessages == NULL ) {
- mpMessages = new idSWF( "mp_messages", common->SW() );
- mpMessages->Activate( true );
- }
- SetLastHitTime( 0 );
- // load the armor sound feedback
- declManager->FindSound( "player_sounds_hitArmor" );
- // set up conditions for animation
- LinkScriptVariables();
- animator.RemoveOriginOffset( true );
- // create combat collision hull for exact collision detection
- SetCombatModel();
- // init the damage effects
- playerView.SetPlayerEntity( this );
- // supress model in non-player views, but allow it in mirrors and remote views
- renderEntity.suppressSurfaceInViewID = entityNumber+1;
- // don't project shadow on self or weapon
- renderEntity.noSelfShadow = true;
- idAFAttachment *headEnt = head.GetEntity();
- if ( headEnt ) {
- headEnt->GetRenderEntity()->suppressSurfaceInViewID = entityNumber+1;
- headEnt->GetRenderEntity()->noSelfShadow = true;
- }
- if ( common->IsMultiplayer() ) {
- Init();
- Hide(); // properly hidden if starting as a spectator
- if ( !common->IsClient() ) {
- // set yourself ready to spawn. idMultiplayerGame will decide when/if appropriate and call SpawnFromSpawnSpot
- SetupWeaponEntity();
- SpawnFromSpawnSpot();
- forceRespawn = true;
- wantSpectate = true;
- assert( spectating );
- }
- } else {
- SetupWeaponEntity();
- SpawnFromSpawnSpot();
- }
- // trigger playtesting item gives, if we didn't get here from a previous level
- // the devmap key will be set on the first devmap, but cleared on any level
- // transitions
- if ( !common->IsMultiplayer() && gameLocal.serverInfo.FindKey( "devmap" ) ) {
- // fire a trigger with the name "devmap"
- idEntity *ent = gameLocal.FindEntity( "devmap" );
- if ( ent ) {
- ent->ActivateTargets( this );
- }
- }
- if ( hud ) {
- if ( weapon_soulcube > 0 && ( inventory.weapons & ( 1 << weapon_soulcube ) ) ) {
- int max_souls = inventory.MaxAmmoForAmmoClass( this, "ammo_souls" );
- if ( inventory.GetInventoryAmmoForType( idWeapon::GetAmmoNumForName( "ammo_souls" ) ) >= max_souls ) {
- hud->SetShowSoulCubeOnLoad( true );
- }
- }
- }
- if ( GetPDA() ) {
- // Add any emails from the inventory
- for ( int i = 0; i < inventory.emails.Num(); i++ ) {
- GetPDA()->AddEmail( inventory.emails[i] );
- }
- GetPDA()->SetSecurity( idLocalization::GetString( "#str_00066" ) );
- }
- if ( gameLocal.world->spawnArgs.GetBool( "no_Weapons" ) ) {
- hiddenWeapon = true;
- if ( weapon.GetEntity() ) {
- weapon.GetEntity()->LowerWeapon();
- }
- idealWeapon = weapon_fists;
- } else {
- hiddenWeapon = false;
- }
-
- UpdateHudWeapon();
- tipUp = false;
- objectiveUp = false;
- if ( inventory.levelTriggers.Num() ) {
- PostEventMS( &EV_Player_LevelTrigger, 0 );
- }
- inventory.pdaOpened = false;
- inventory.selPDA = 0;
- if ( !common->IsMultiplayer() ) {
- int startingHealth = gameLocal.world->spawnArgs.GetInt( "startingHealth", health );
- if ( health > startingHealth ) {
- health = startingHealth;
- }
- if ( g_skill.GetInteger() < 2 ) {
- if ( health < 25 ) {
- health = 25;
- }
- if ( g_useDynamicProtection.GetBool() ) {
- new_g_damageScale = 1.0f;
- }
- } else {
- new_g_damageScale = 1.0f;
- g_armorProtection.SetFloat( ( g_skill.GetInteger() < 2 ) ? 0.4f : 0.2f );
- if ( g_skill.GetInteger() == 3 ) {
- nextHealthTake = gameLocal.time + g_healthTakeTime.GetInteger() * 1000;
- }
- }
- }
- //Setup the weapon toggle lists
- const idKeyValue *kv;
- kv = spawnArgs.MatchPrefix( "weapontoggle", NULL );
- while( kv ) {
- WeaponToggle_t newToggle;
- strcpy(newToggle.name, kv->GetKey().c_str());
- idStr toggleData = kv->GetValue();
- idLexer src;
- idToken token;
- src.LoadMemory(toggleData, toggleData.Length(), "toggleData");
- while(1) {
- if(!src.ReadToken(&token)) {
- break;
- }
- int index = atoi(token.c_str());
- newToggle.toggleList.Append(index);
- //Skip the ,
- src.ReadToken(&token);
- }
- newToggle.lastUsed = 0;
- weaponToggles.Set(newToggle.name, newToggle);
- kv = spawnArgs.MatchPrefix( "weapontoggle", kv );
- }
- if( g_skill.GetInteger() >= 3 || cvarSystem->GetCVarBool( "fs_buildresources" ) ) {
- if(!WeaponAvailable("weapon_bloodstone_passive")) {
- GiveInventoryItem("weapon_bloodstone_passive");
- }
- if(!WeaponAvailable("weapon_bloodstone_active1")) {
- GiveInventoryItem("weapon_bloodstone_active1");
- }
- if(!WeaponAvailable("weapon_bloodstone_active2")) {
- GiveInventoryItem("weapon_bloodstone_active2");
- }
- if(!WeaponAvailable("weapon_bloodstone_active3")) {
- GiveInventoryItem("weapon_bloodstone_active3");
- }
- }
- bloomEnabled = false;
- bloomSpeed = 1;
- bloomIntensity = -0.01f;
- if ( g_demoMode.GetBool() && weapon.GetEntity() && weapon.GetEntity()->AmmoInClip() == 0 ) {
- weapon.GetEntity()->ForceAmmoInClip();
- }
- }
- /*
- ==============
- idPlayer::~idPlayer()
- Release any resources used by the player.
- ==============
- */
- idPlayer::~idPlayer() {
- delete weapon.GetEntity();
- weapon = NULL;
- delete flashlight.GetEntity();
- flashlight = NULL;
- if ( enviroSuitLight.IsValid() ) {
- enviroSuitLight.GetEntity()->ProcessEvent( &EV_Remove );
- }
- // have to do this here, idMultiplayerGame::DisconnectClient() is too late
- if ( common->IsMultiplayer() && gameLocal.mpGame.IsGametypeFlagBased() ) {
- ReturnFlag();
- }
- delete hudManager;
- hudManager = NULL;
- delete pdaMenu;
- pdaMenu = NULL;
- delete mpMessages;
- mpMessages = NULL;
- }
- /*
- ===========
- idPlayer::Save
- ===========
- */
- void idPlayer::Save( idSaveGame *savefile ) const {
- int i;
- savefile->WriteUsercmd( usercmd );
- playerView.Save( savefile );
- savefile->WriteBool( noclip );
- savefile->WriteBool( godmode );
- // don't save spawnAnglesSet, since we'll have to reset them after loading the savegame
- savefile->WriteAngles( spawnAngles );
- savefile->WriteAngles( viewAngles );
- savefile->WriteAngles( cmdAngles );
- savefile->WriteInt( buttonMask );
- savefile->WriteInt( oldButtons );
- savefile->WriteInt( oldImpulseSequence );
- savefile->WriteInt( lastHitTime );
- savefile->WriteInt( lastSndHitTime );
- savefile->WriteInt( lastSavingThrowTime );
- // idBoolFields don't need to be saved, just re-linked in Restore
- savefile->WriteObject( primaryObjective );
- inventory.Save( savefile );
- weapon.Save( savefile );
- for ( int i = 0; i < NUM_QUICK_SLOTS; ++i ) {
- savefile->WriteInt( quickSlot[ i ] );
- }
- savefile->WriteInt( weapon_soulcube );
- savefile->WriteInt( weapon_pda );
- savefile->WriteInt( weapon_fists );
- savefile->WriteInt( weapon_flashlight );
- savefile->WriteInt( weapon_chainsaw );
- savefile->WriteInt( weapon_bloodstone );
- savefile->WriteInt( weapon_bloodstone_active1 );
- savefile->WriteInt( weapon_bloodstone_active2 );
- savefile->WriteInt( weapon_bloodstone_active3 );
- savefile->WriteBool( harvest_lock );
- savefile->WriteInt( hudPowerup );
- savefile->WriteInt( lastHudPowerup );
- savefile->WriteInt( hudPowerupDuration );
-
-
- savefile->WriteInt( heartRate );
- savefile->WriteFloat( heartInfo.GetStartTime() );
- savefile->WriteFloat( heartInfo.GetDuration() );
- savefile->WriteFloat( heartInfo.GetStartValue() );
- savefile->WriteFloat( heartInfo.GetEndValue() );
- savefile->WriteInt( lastHeartAdjust );
- savefile->WriteInt( lastHeartBeat );
- savefile->WriteInt( lastDmgTime );
- savefile->WriteInt( deathClearContentsTime );
- savefile->WriteBool( doingDeathSkin );
- savefile->WriteInt( lastArmorPulse );
- savefile->WriteFloat( stamina );
- savefile->WriteFloat( healthPool );
- savefile->WriteInt( nextHealthPulse );
- savefile->WriteBool( healthPulse );
- savefile->WriteInt( nextHealthTake );
- savefile->WriteBool( healthTake );
- savefile->WriteBool( hiddenWeapon );
- soulCubeProjectile.Save( savefile );
- savefile->WriteInt( spectator );
- savefile->WriteBool( forceScoreBoard );
- savefile->WriteBool( forceRespawn );
- savefile->WriteBool( spectating );
- savefile->WriteInt( lastSpectateTeleport );
- savefile->WriteBool( lastHitToggle );
- savefile->WriteBool( wantSpectate );
- savefile->WriteBool( weaponGone );
- savefile->WriteBool( useInitialSpawns );
- savefile->WriteInt( tourneyRank );
- savefile->WriteInt( tourneyLine );
- teleportEntity.Save( savefile );
- savefile->WriteInt( teleportKiller );
- savefile->WriteInt( minRespawnTime );
- savefile->WriteInt( maxRespawnTime );
- savefile->WriteVec3( firstPersonViewOrigin );
- savefile->WriteMat3( firstPersonViewAxis );
- // don't bother saving dragEntity since it's a dev tool
- savefile->WriteJoint( hipJoint );
- savefile->WriteJoint( chestJoint );
- savefile->WriteJoint( headJoint );
- savefile->WriteStaticObject( physicsObj );
- savefile->WriteInt( aasLocation.Num() );
- for( i = 0; i < aasLocation.Num(); i++ ) {
- savefile->WriteInt( aasLocation[ i ].areaNum );
- savefile->WriteVec3( aasLocation[ i ].pos );
- }
- savefile->WriteInt( bobFoot );
- savefile->WriteFloat( bobFrac );
- savefile->WriteFloat( bobfracsin );
- savefile->WriteInt( bobCycle );
- savefile->WriteFloat( xyspeed );
- savefile->WriteInt( stepUpTime );
- savefile->WriteFloat( stepUpDelta );
- savefile->WriteFloat( idealLegsYaw );
- savefile->WriteFloat( legsYaw );
- savefile->WriteBool( legsForward );
- savefile->WriteFloat( oldViewYaw );
- savefile->WriteAngles( viewBobAngles );
- savefile->WriteVec3( viewBob );
- savefile->WriteInt( landChange );
- savefile->WriteInt( landTime );
- savefile->WriteInt( currentWeapon );
- savefile->WriteInt( idealWeapon.Get() );
- savefile->WriteInt( previousWeapon );
- savefile->WriteInt( weaponSwitchTime );
- savefile->WriteBool( weaponEnabled );
- savefile->WriteInt( skinIndex );
- savefile->WriteSkin( skin );
- savefile->WriteSkin( powerUpSkin );
- savefile->WriteInt( numProjectilesFired );
- savefile->WriteInt( numProjectileHits );
- savefile->WriteBool( airless );
- savefile->WriteInt( airMsec );
- savefile->WriteInt( lastAirDamage );
- savefile->WriteBool( gibDeath );
- savefile->WriteBool( gibsLaunched );
- savefile->WriteVec3( gibsDir );
- savefile->WriteFloat( zoomFov.GetStartTime() );
- savefile->WriteFloat( zoomFov.GetDuration() );
- savefile->WriteFloat( zoomFov.GetStartValue() );
- savefile->WriteFloat( zoomFov.GetEndValue() );
- savefile->WriteFloat( centerView.GetStartTime() );
- savefile->WriteFloat( centerView.GetDuration() );
- savefile->WriteFloat( centerView.GetStartValue() );
- savefile->WriteFloat( centerView.GetEndValue() );
- savefile->WriteBool( fxFov );
- savefile->WriteFloat( influenceFov );
- savefile->WriteInt( influenceActive );
- savefile->WriteFloat( influenceRadius );
- savefile->WriteObject( influenceEntity );
- savefile->WriteMaterial( influenceMaterial );
- savefile->WriteSkin( influenceSkin );
- savefile->WriteObject( privateCameraView );
- for( i = 0; i < NUM_LOGGED_VIEW_ANGLES; i++ ) {
- savefile->WriteAngles( loggedViewAngles[ i ] );
- }
- for( i = 0; i < NUM_LOGGED_ACCELS; i++ ) {
- savefile->WriteInt( loggedAccel[ i ].time );
- savefile->WriteVec3( loggedAccel[ i ].dir );
- }
- savefile->WriteInt( currentLoggedAccel );
- savefile->WriteObject( focusGUIent );
- // can't save focusUI
- savefile->WriteObject( focusCharacter );
- savefile->WriteInt( talkCursor );
- savefile->WriteInt( focusTime );
- savefile->WriteObject( focusVehicle );
- savefile->WriteUserInterface( cursor, false );
- savefile->WriteInt( oldMouseX );
- savefile->WriteInt( oldMouseY );
- savefile->WriteBool( tipUp );
- savefile->WriteBool( objectiveUp );
- savefile->WriteInt( lastDamageDef );
- savefile->WriteVec3( lastDamageDir );
- savefile->WriteInt( lastDamageLocation );
- savefile->WriteInt( smoothedFrame );
- savefile->WriteBool( smoothedOriginUpdated );
- savefile->WriteVec3( smoothedOrigin );
- savefile->WriteAngles( smoothedAngles );
- savefile->WriteBool( respawning );
- savefile->WriteBool( leader );
- savefile->WriteInt( lastSpectateChange );
- savefile->WriteInt( lastTeleFX );
- savefile->WriteFloat( pm_stamina.GetFloat() );
- // TODO_SPARTY hook this up with new hud
- //if ( hud ) {
- // hud->SetStateString( "message", idLocalization::GetString( "#str_02916" ) );
- // hud->HandleNamedEvent( "Message" );
- //}
- savefile->WriteInt(weaponToggles.Num());
- for(i = 0; i < weaponToggles.Num(); i++) {
- WeaponToggle_t* weaponToggle = weaponToggles.GetIndex(i);
- savefile->WriteString(weaponToggle->name);
- savefile->WriteInt(weaponToggle->toggleList.Num());
- for(int j = 0; j < weaponToggle->toggleList.Num(); j++) {
- savefile->WriteInt(weaponToggle->toggleList[j]);
- }
- }
- savefile->WriteObject( mountedObject );
- enviroSuitLight.Save( savefile );
- savefile->WriteBool( healthRecharge );
- savefile->WriteInt( lastHealthRechargeTime );
- savefile->WriteInt( rechargeSpeed );
- savefile->WriteFloat( new_g_damageScale );
- savefile->WriteBool( bloomEnabled );
- savefile->WriteFloat( bloomSpeed );
- savefile->WriteFloat( bloomIntensity );
- savefile->WriteObject( flashlight.GetEntity() );
- savefile->WriteInt( flashlightBattery );
- achievementManager.Save( savefile );
- savefile->WriteInt( playedTimeSecs );
- savefile->WriteInt( playedTimeResidual );
- for ( int i=0; i<MAX_PLAYER_PDA; i++ ) {
- savefile->WriteBool( pdaHasBeenRead[i] );
- }
- for ( int i=0; i<MAX_PLAYER_VIDEO; i++ ) {
- savefile->WriteBool( videoHasBeenViewed[i] );
- }
- for ( int i=0; i<MAX_PLAYER_AUDIO; i++ ) {
- for ( int j=0; j<MAX_PLAYER_AUDIO_ENTRIES; j++ ) {
- savefile->WriteBool( audioHasBeenHeard[i][j] );
- }
- }
- }
- /*
- ===========
- idPlayer::Restore
- ===========
- */
- void idPlayer::Restore( idRestoreGame *savefile ) {
- int i;
- int num;
- float set;
- savefile->ReadUsercmd( usercmd );
- playerView.Restore( savefile );
- savefile->ReadBool( noclip );
- savefile->ReadBool( godmode );
- savefile->ReadAngles( spawnAngles );
- savefile->ReadAngles( viewAngles );
- savefile->ReadAngles( cmdAngles );
- memset( usercmd.angles, 0, sizeof( usercmd.angles ) );
- SetViewAngles( viewAngles );
- spawnAnglesSet = true;
- savefile->ReadInt( buttonMask );
- savefile->ReadInt( oldButtons );
- savefile->ReadInt( oldImpulseSequence );
- usercmd.impulseSequence = 0;
- oldImpulseSequence = 0;
- savefile->ReadInt( lastHitTime );
- savefile->ReadInt( lastSndHitTime );
- savefile->ReadInt( lastSavingThrowTime );
- // Re-link idBoolFields to the scriptObject, values will be restored in scriptObject's restore
- LinkScriptVariables();
- savefile->ReadObject( reinterpret_cast<idClass *&>( primaryObjective ) );
- inventory.Restore( savefile );
- weapon.Restore( savefile );
- if ( hudManager != NULL ) {
- hudManager->Initialize( "hud", common->SW() );
- hudManager->ActivateMenu( true );
- hud = hudManager->GetHud();
- }
- if ( pdaMenu != NULL ) {
- pdaMenu->Initialize( "pda", common->SW() );
- }
- for ( i = 0; i < inventory.emails.Num(); i++ ) {
- GetPDA()->AddEmail( inventory.emails[i] );
- }
- for ( int i = 0; i < NUM_QUICK_SLOTS; ++i ) {
- savefile->ReadInt( quickSlot[ i ] );
- }
- savefile->ReadInt( weapon_soulcube );
- savefile->ReadInt( weapon_pda );
- savefile->ReadInt( weapon_fists );
- savefile->ReadInt( weapon_flashlight );
- savefile->ReadInt( weapon_chainsaw );
- savefile->ReadInt( weapon_bloodstone );
- savefile->ReadInt( weapon_bloodstone_active1 );
- savefile->ReadInt( weapon_bloodstone_active2 );
- savefile->ReadInt( weapon_bloodstone_active3 );
- savefile->ReadBool( harvest_lock );
- savefile->ReadInt( hudPowerup );
- savefile->ReadInt( lastHudPowerup );
- savefile->ReadInt( hudPowerupDuration );
-
-
- savefile->ReadInt( heartRate );
- savefile->ReadFloat( set );
- heartInfo.SetStartTime( set );
- savefile->ReadFloat( set );
- heartInfo.SetDuration( set );
- savefile->ReadFloat( set );
- heartInfo.SetStartValue( set );
- savefile->ReadFloat( set );
- heartInfo.SetEndValue( set );
- savefile->ReadInt( lastHeartAdjust );
- savefile->ReadInt( lastHeartBeat );
- savefile->ReadInt( lastDmgTime );
- savefile->ReadInt( deathClearContentsTime );
- savefile->ReadBool( doingDeathSkin );
- savefile->ReadInt( lastArmorPulse );
- savefile->ReadFloat( stamina );
- savefile->ReadFloat( healthPool );
- savefile->ReadInt( nextHealthPulse );
- savefile->ReadBool( healthPulse );
- savefile->ReadInt( nextHealthTake );
- savefile->ReadBool( healthTake );
- savefile->ReadBool( hiddenWeapon );
- soulCubeProjectile.Restore( savefile );
- savefile->ReadInt( spectator );
- savefile->ReadBool( forceScoreBoard );
- savefile->ReadBool( forceRespawn );
- savefile->ReadBool( spectating );
- savefile->ReadInt( lastSpectateTeleport );
- savefile->ReadBool( lastHitToggle );
- savefile->ReadBool( wantSpectate );
- savefile->ReadBool( weaponGone );
- savefile->ReadBool( useInitialSpawns );
- savefile->ReadInt( tourneyRank );
- savefile->ReadInt( tourneyLine );
- teleportEntity.Restore( savefile );
- savefile->ReadInt( teleportKiller );
- savefile->ReadInt( minRespawnTime );
- savefile->ReadInt( maxRespawnTime );
- savefile->ReadVec3( firstPersonViewOrigin );
- savefile->ReadMat3( firstPersonViewAxis );
- // don't bother saving dragEntity since it's a dev tool
- dragEntity.Clear();
- savefile->ReadJoint( hipJoint );
- savefile->ReadJoint( chestJoint );
- savefile->ReadJoint( headJoint );
- savefile->ReadStaticObject( physicsObj );
- RestorePhysics( &physicsObj );
- savefile->ReadInt( num );
- aasLocation.SetGranularity( 1 );
- aasLocation.SetNum( num );
- for( i = 0; i < num; i++ ) {
- savefile->ReadInt( aasLocation[ i ].areaNum );
- savefile->ReadVec3( aasLocation[ i ].pos );
- }
- savefile->ReadInt( bobFoot );
- savefile->ReadFloat( bobFrac );
- savefile->ReadFloat( bobfracsin );
- savefile->ReadInt( bobCycle );
- savefile->ReadFloat( xyspeed );
- savefile->ReadInt( stepUpTime );
- savefile->ReadFloat( stepUpDelta );
- savefile->ReadFloat( idealLegsYaw );
- savefile->ReadFloat( legsYaw );
- savefile->ReadBool( legsForward );
- savefile->ReadFloat( oldViewYaw );
- savefile->ReadAngles( viewBobAngles );
- savefile->ReadVec3( viewBob );
- savefile->ReadInt( landChange );
- savefile->ReadInt( landTime );
- savefile->ReadInt( currentWeapon );
-
- int savedIdealWeapon = -1;
- savefile->ReadInt( savedIdealWeapon );
- idealWeapon.Set( savedIdealWeapon );
- savefile->ReadInt( previousWeapon );
- savefile->ReadInt( weaponSwitchTime );
- savefile->ReadBool( weaponEnabled );
- savefile->ReadInt( skinIndex );
- savefile->ReadSkin( skin );
- savefile->ReadSkin( powerUpSkin );
- savefile->ReadInt( numProjectilesFired );
- savefile->ReadInt( numProjectileHits );
- savefile->ReadBool( airless );
- savefile->ReadInt( airMsec );
- savefile->ReadInt( lastAirDamage );
- savefile->ReadBool( gibDeath );
- savefile->ReadBool( gibsLaunched );
- savefile->ReadVec3( gibsDir );
- savefile->ReadFloat( set );
- zoomFov.SetStartTime( set );
- savefile->ReadFloat( set );
- zoomFov.SetDuration( set );
- savefile->ReadFloat( set );
- zoomFov.SetStartValue( set );
- savefile->ReadFloat( set );
- zoomFov.SetEndValue( set );
- savefile->ReadFloat( set );
- centerView.SetStartTime( set );
- savefile->ReadFloat( set );
- centerView.SetDuration( set );
- savefile->ReadFloat( set );
- centerView.SetStartValue( set );
- savefile->ReadFloat( set );
- centerView.SetEndValue( set );
- savefile->ReadBool( fxFov );
- savefile->ReadFloat( influenceFov );
- savefile->ReadInt( influenceActive );
- savefile->ReadFloat( influenceRadius );
- savefile->ReadObject( reinterpret_cast<idClass *&>( influenceEntity ) );
- savefile->ReadMaterial( influenceMaterial );
- savefile->ReadSkin( influenceSkin );
- savefile->ReadObject( reinterpret_cast<idClass *&>( privateCameraView ) );
- for( i = 0; i < NUM_LOGGED_VIEW_ANGLES; i++ ) {
- savefile->ReadAngles( loggedViewAngles[ i ] );
- }
- for( i = 0; i < NUM_LOGGED_ACCELS; i++ ) {
- savefile->ReadInt( loggedAccel[ i ].time );
- savefile->ReadVec3( loggedAccel[ i ].dir );
- }
- savefile->ReadInt( currentLoggedAccel );
- savefile->ReadObject( reinterpret_cast<idClass *&>( focusGUIent ) );
- // can't save focusUI
- focusUI = NULL;
- savefile->ReadObject( reinterpret_cast<idClass *&>( focusCharacter ) );
- savefile->ReadInt( talkCursor );
- savefile->ReadInt( focusTime );
- savefile->ReadObject( reinterpret_cast<idClass *&>( focusVehicle ) );
- savefile->ReadUserInterface( cursor );
- savefile->ReadInt( oldMouseX );
- savefile->ReadInt( oldMouseY );
- savefile->ReadBool( tipUp );
- savefile->ReadBool( objectiveUp );
- savefile->ReadInt( lastDamageDef );
- savefile->ReadVec3( lastDamageDir );
- savefile->ReadInt( lastDamageLocation );
- savefile->ReadInt( smoothedFrame );
- savefile->ReadBool( smoothedOriginUpdated );
- savefile->ReadVec3( smoothedOrigin );
- savefile->ReadAngles( smoothedAngles );
- savefile->ReadBool( respawning );
- savefile->ReadBool( leader );
- savefile->ReadInt( lastSpectateChange );
- savefile->ReadInt( lastTeleFX );
- // set the pm_ cvars
- const idKeyValue *kv;
- kv = spawnArgs.MatchPrefix( "pm_", NULL );
- while( kv ) {
- cvarSystem->SetCVarString( kv->GetKey(), kv->GetValue() );
- kv = spawnArgs.MatchPrefix( "pm_", kv );
- }
- savefile->ReadFloat( set );
- pm_stamina.SetFloat( set );
- // create combat collision hull for exact collision detection
- SetCombatModel();
- int weaponToggleCount;
- savefile->ReadInt(weaponToggleCount);
- for(i = 0; i < weaponToggleCount; i++) {
- WeaponToggle_t newToggle;
- memset(&newToggle, 0, sizeof(newToggle));
- idStr name;
- savefile->ReadString(name);
- strcpy(newToggle.name, name.c_str());
- int indexCount;
- savefile->ReadInt(indexCount);
- for(int j = 0; j < indexCount; j++) {
- int temp;
- savefile->ReadInt(temp);
- newToggle.toggleList.Append(temp);
- }
- newToggle.lastUsed = 0;
- weaponToggles.Set(newToggle.name, newToggle);
- }
- savefile->ReadObject(reinterpret_cast<idClass *&>(mountedObject));
- enviroSuitLight.Restore( savefile );
- savefile->ReadBool( healthRecharge );
- savefile->ReadInt( lastHealthRechargeTime );
- savefile->ReadInt( rechargeSpeed );
- savefile->ReadFloat( new_g_damageScale );
- savefile->ReadBool( bloomEnabled );
- savefile->ReadFloat( bloomSpeed );
- savefile->ReadFloat( bloomIntensity );
-
- // flashlight
- idWeapon *tempWeapon;
- savefile->ReadObject( reinterpret_cast<idClass *&>( tempWeapon ) );
- tempWeapon->SetIsPlayerFlashlight( true );
- flashlight = tempWeapon;
- savefile->ReadInt( flashlightBattery );
- achievementManager.Restore( savefile );
- savefile->ReadInt( playedTimeSecs );
- savefile->ReadInt( playedTimeResidual );
- aimAssist.Init( this );
- laserSightHandle = -1;
- // re-init the laser model
- memset( &laserSightRenderEntity, 0, sizeof( laserSightRenderEntity ) );
- laserSightRenderEntity.hModel = renderModelManager->FindModel( "_BEAM" );
- laserSightRenderEntity.customShader = declManager->FindMaterial( "stereoRenderLaserSight" );
- for ( int i=0; i<MAX_PLAYER_PDA; i++ ) {
- savefile->ReadBool( pdaHasBeenRead[i] );
- }
- for ( int i=0; i<MAX_PLAYER_VIDEO; i++ ) {
- savefile->ReadBool( videoHasBeenViewed[i] );
- }
- for ( int i=0; i<MAX_PLAYER_AUDIO; i++ ) {
- for ( int j=0; j<MAX_PLAYER_AUDIO_ENTRIES; j++ ) {
- savefile->ReadBool( audioHasBeenHeard[i][j] );
- }
- }
- // Update the soul cube HUD indicator
- if ( hud ) {
- if ( weapon_soulcube > 0 && ( inventory.weapons & ( 1 << weapon_soulcube ) ) ) {
- int max_souls = inventory.MaxAmmoForAmmoClass( this, "ammo_souls" );
- if ( inventory.GetInventoryAmmoForType( idWeapon::GetAmmoNumForName( "ammo_souls" ) ) >= max_souls ) {
- hud->SetShowSoulCubeOnLoad( true );
- }
- }
- }
- }
- /*
- ===============
- idPlayer::PrepareForRestart
- ================
- */
- void idPlayer::PrepareForRestart() {
- ClearPowerUps();
- if( common->IsClient() == false ) {
- ServerSpectate( true );
- }
- forceRespawn = true;
-
- // Confirm reset hud states
- DropFlag();
- if ( hud ) {
- hud->SetFlagState( 0, 0 );
- hud->SetFlagState( 1, 0 );
- }
-
- // we will be restarting program, clear the client entities from program-related things first
- ShutdownThreads();
- // the sound world is going to be cleared, don't keep references to emitters
- FreeSoundEmitter( false );
- }
- /*
- ===============
- idPlayer::Restart
- ================
- */
- void idPlayer::Restart() {
- idActor::Restart();
-
- // client needs to setup the animation script object again
- if ( common->IsClient() ) {
- // Make sure the weapon spawnId gets re-linked on the next snapshot.
- // Otherwise, its owner might not be set after the map restart, which causes asserts and crashes.
- weapon = NULL;
- flashlight = NULL;
- enviroSuitLight = NULL;
- Init();
- } else {
- // choose a random spot and prepare the point of view in case player is left spectating
- assert( spectating );
- SpawnFromSpawnSpot();
- }
- useInitialSpawns = true;
- UpdateSkinSetup();
- }
- /*
- ===============
- idPlayer::ServerSpectate
- ================
- */
- void idPlayer::ServerSpectate( bool spectate ) {
- assert( !common->IsClient() );
- if ( spectating != spectate ) {
- Spectate( spectate );
- if ( !spectate ) {
- // When coming out of spectate, join the team with the least number of players
- if ( gameLocal.mpGame.IsGametypeTeamBased() ) {
- int teamCounts[2] = { 0, 0 };
- gameLocal.mpGame.NumActualClients( false, teamCounts );
- teamCounts[team]--;
- if ( teamCounts[0] < teamCounts[1] ) {
- team = 0;
- } else if ( teamCounts[1] < teamCounts[0] ) {
- team = 1;
- }
- gameLocal.mpGame.SwitchToTeam( entityNumber, -1, team );
- }
- if ( gameLocal.gameType == GAME_DM ) {
- // make sure the scores are reset so you can't exploit by spectating and entering the game back
- // other game types don't matter, as you either can't join back, or it's team scores
- gameLocal.mpGame.ClearFrags( entityNumber );
- }
- }
- }
- if ( !spectate ) {
- SpawnFromSpawnSpot();
- }
- // drop the flag if player was carrying it
- if ( spectate && common->IsMultiplayer() && gameLocal.mpGame.IsGametypeFlagBased() &&
- carryingFlag )
- {
- DropFlag();
- }
- }
- /*
- ===========
- idPlayer::SelectInitialSpawnPoint
- Try to find a spawn point marked 'initial', otherwise
- use normal spawn selection.
- ============
- */
- void idPlayer::SelectInitialSpawnPoint( idVec3 &origin, idAngles &angles ) {
- idEntity *spot;
- idStr skin;
- spot = gameLocal.SelectInitialSpawnPoint( this );
- // set the player skin from the spawn location
- if ( spot->spawnArgs.GetString( "skin", NULL, skin ) ) {
- spawnArgs.Set( "spawn_skin", skin );
- }
- // activate the spawn locations targets
- spot->PostEventMS( &EV_ActivateTargets, 0, this );
- origin = spot->GetPhysics()->GetOrigin();
- origin[2] += 4.0f + CM_BOX_EPSILON; // move up to make sure the player is at least an epsilon above the floor
- angles = spot->GetPhysics()->GetAxis().ToAngles();
- }
- /*
- ===========
- idPlayer::SpawnFromSpawnSpot
- Chooses a spawn location and spawns the player
- ============
- */
- void idPlayer::SpawnFromSpawnSpot() {
- idVec3 spawn_origin;
- idAngles spawn_angles;
-
- SelectInitialSpawnPoint( spawn_origin, spawn_angles );
- SpawnToPoint( spawn_origin, spawn_angles );
- }
- /*
- ===========
- idPlayer::SpawnToPoint
- Called every time a client is placed fresh in the world:
- after the first ClientBegin, and after each respawn
- Initializes all non-persistant parts of playerState
- when called here with spectating set to true, just place yourself and init
- ============
- */
- void idPlayer::SpawnToPoint( const idVec3 &spawn_origin, const idAngles &spawn_angles ) {
- idVec3 spec_origin;
- assert( !common->IsClient() );
- respawning = true;
- Init();
- fl.noknockback = false;
- // stop any ragdolls being used
- StopRagdoll();
- // set back the player physics
- SetPhysics( &physicsObj );
- physicsObj.SetClipModelAxis();
- physicsObj.EnableClip();
- if ( !spectating ) {
- SetCombatContents( true );
- }
- physicsObj.SetLinearVelocity( vec3_origin );
- // setup our initial view
- if ( !spectating ) {
- SetOrigin( spawn_origin );
- } else {
- spec_origin = spawn_origin;
- spec_origin[ 2 ] += pm_normalheight.GetFloat();
- spec_origin[ 2 ] += SPECTATE_RAISE;
- SetOrigin( spec_origin );
- }
- // if this is the first spawn of the map, we don't have a usercmd yet,
- // so the delta angles won't be correct. This will be fixed on the first think.
- viewAngles = ang_zero;
- SetDeltaViewAngles( ang_zero );
- SetViewAngles( spawn_angles );
- spawnAngles = spawn_angles;
- spawnAnglesSet = false;
- legsForward = true;
- legsYaw = 0.0f;
- idealLegsYaw = 0.0f;
- oldViewYaw = viewAngles.yaw;
- if ( spectating ) {
- Hide();
- } else {
- Show();
- }
- if ( common->IsMultiplayer() ) {
- if ( !spectating ) {
- // we may be called twice in a row in some situations. avoid a double fx and 'fly to the roof'
- if ( lastTeleFX < gameLocal.time - 1000 ) {
- idEntityFx::StartFx( spawnArgs.GetString( "fx_spawn" ), &spawn_origin, NULL, this, true );
- lastTeleFX = gameLocal.time;
- }
- }
- AI_TELEPORT = true;
- } else {
- AI_TELEPORT = false;
- }
- // kill anything at the new position
- if ( !spectating ) {
- physicsObj.SetClipMask( MASK_PLAYERSOLID ); // the clip mask is usually maintained in Move(), but KillBox requires it
- gameLocal.KillBox( this );
- }
- // don't allow full run speed for a bit
- physicsObj.SetKnockBack( 100 );
- // set our respawn time and buttons so that if we're killed we don't respawn immediately
- minRespawnTime = gameLocal.time;
- maxRespawnTime = gameLocal.time;
- if ( !spectating ) {
- forceRespawn = false;
- }
- Respawn_Shared();
- privateCameraView = NULL;
- BecomeActive( TH_THINK );
- // run a client frame to drop exactly to the floor,
- // initialize animations and other things
- Think();
- respawning = false;
- lastManOver = false;
- lastManPlayAgain = false;
- isTelefragged = false;
- }
- /*
- ===============
- idPlayer::Respawn_Shared
- Called on server and client players when they respawn (including on initial spawn)
- ===============
- */
- void idPlayer::Respawn_Shared() {
- respawn_netEvent.Set();
- serverOverridePositionTime = gameLocal.GetServerGameTimeMs();
- // Remove the hud respawn message.
- HideRespawnHudMessage();
- FlashlightOff();
- }
- /*
- ===============
- idPlayer::SavePersistantInfo
- Saves any inventory and player stats when changing levels.
- ===============
- */
- void idPlayer::SavePersistantInfo() {
- idDict &playerInfo = gameLocal.persistentPlayerInfo[entityNumber];
- playerInfo.Clear();
- inventory.GetPersistantData( playerInfo );
- playerInfo.SetInt( "health", health );
- playerInfo.SetInt( "current_weapon", currentWeapon );
- playerInfo.SetInt( "playedTime", playedTimeSecs );
- achievementManager.SavePersistentData( playerInfo );
- }
- /*
- ===============
- idPlayer::RestorePersistantInfo
- Restores any inventory and player stats when changing levels.
- ===============
- */
- void idPlayer::RestorePersistantInfo() {
- if ( common->IsMultiplayer() || g_demoMode.GetBool() ) {
- gameLocal.persistentPlayerInfo[entityNumber].Clear();
- }
- spawnArgs.Copy( gameLocal.persistentPlayerInfo[entityNumber] );
- inventory.RestoreInventory( this, spawnArgs );
- health = spawnArgs.GetInt( "health", "100" );
- idealWeapon = spawnArgs.GetInt( "current_weapon", "1" );
-
- playedTimeSecs = spawnArgs.GetInt( "playedTime" );
- achievementManager.RestorePersistentData( spawnArgs );
- }
- /*
- ==============
- idPlayer::UpdateSkinSetup
- ==============
- */
- void idPlayer::UpdateSkinSetup() {
- if ( !common->IsMultiplayer() ) {
- return;
- }
- if ( gameLocal.mpGame.IsGametypeTeamBased() ) { /* CTF */
- skinIndex = team + 1;
- } else {
- // Each player will now have their Skin Index Reflect their entity number ( host = 0, client 1 = 1, client 2 = 2 etc )
- skinIndex = entityNumber; // session->GetActingGameStateLobbyBase().GetLobbyUserSkinIndex( gameLocal.lobbyUserIDs[entityNumber] );
- }
- const char * baseSkinName = gameLocal.mpGame.GetSkinName( skinIndex );
- skin = declManager->FindSkin( baseSkinName, false );
- if ( PowerUpActive( BERSERK ) ) {
- idStr powerSkinName = baseSkinName;
- powerSkinName.Append( "_berserk" );
- powerUpSkin = declManager->FindSkin( powerSkinName );
- }
- else if ( PowerUpActive( INVULNERABILITY ) ) {
- idStr powerSkinName = baseSkinName;
- powerSkinName.Append( "_invuln" );
- powerUpSkin = declManager->FindSkin( powerSkinName );
- }
- else if ( PowerUpActive( INVISIBILITY ) ) {
- const char * invisibleSkin = "";
- spawnArgs.GetString( "skin_invisibility", "", &invisibleSkin );
- powerUpSkin = declManager->FindSkin( invisibleSkin );
- }
- }
- /*
- ===============
- idPlayer::UpdateHudStats
- ===============
- */
- void idPlayer::UpdateHudStats( idMenuHandler_HUD * _hudManager ) {
-
- if ( _hudManager && _hudManager->GetHud() ) {
-
- idMenuScreen_HUD * hud = _hudManager->GetHud();
- hud->UpdateHealthArmor( this );
- hud->UpdateStamina( this );
- hud->UpdateWeaponInfo( this );
- if ( inventory.weaponPulse ) {
- UpdateHudWeapon();
- inventory.weaponPulse = false;
- }
- if ( gameLocal.mpGame.IsGametypeFlagBased() )
- {
- hud->SetFlagState( 0, gameLocal.mpGame.GetFlagStatus( 0 ) );
- hud->SetFlagState( 1, gameLocal.mpGame.GetFlagStatus( 1 ) );
- hud->SetTeamScore( 0, gameLocal.mpGame.GetFlagPoints( 0 ) );
- hud->SetTeamScore( 1, gameLocal.mpGame.GetFlagPoints( 1 ) );
- hud->SetTeam( team );
- }
- }
- }
- /*
- ===============
- idPlayer::UpdateHudWeapon
- ===============
- */
- void idPlayer::UpdateHudWeapon( bool flashWeapon ) {
- idMenuScreen_HUD * curDisplay = hud;
- idPlayer *p = this;
- if ( gameLocal.GetLocalClientNum() >= 0 && gameLocal.entities[ gameLocal.GetLocalClientNum() ] && gameLocal.entities[ gameLocal.GetLocalClientNum() ]->IsType( idPlayer::Type ) ) {
- p = static_cast< idPlayer * >( gameLocal.entities[ gameLocal.GetLocalClientNum() ] );
- if ( p->spectating && p->spectator == entityNumber ) {
- assert( p->hud );
- curDisplay = p->hud;
- }
- }
- if ( !curDisplay ) {
- return;
- }
- curDisplay->UpdateWeaponStates( p, flashWeapon );
- }
- /*
- ===============
- idPlayer::UpdateHudWeapon
- ===============
- */
- void idPlayer::UpdateChattingHud() {
- idMenuScreen_HUD * curDisplay = hud;
- idPlayer *p = this;
- if ( gameLocal.GetLocalClientNum() >= 0 && gameLocal.entities[ gameLocal.GetLocalClientNum() ] && gameLocal.entities[ gameLocal.GetLocalClientNum() ]->IsType( idPlayer::Type ) ) {
- p = static_cast< idPlayer * >( gameLocal.entities[ gameLocal.GetLocalClientNum() ] );
- if ( p->spectating && p->spectator == entityNumber ) {
- assert( p->hud );
- curDisplay = p->hud;
- }
- }
- if ( !curDisplay ) {
- return;
- }
- curDisplay->UpdateChattingHud( p );
- }
- /*
- ========================
- idMenuScreen_Scoreboard::UpdateSpectating
- ========================
- */
- void idPlayer::UpdateSpectatingText() {
- idSWF * spectatorMessages = mpMessages;
- idPlayer *p = this;
- if ( gameLocal.GetLocalClientNum() >= 0 && gameLocal.entities[ gameLocal.GetLocalClientNum() ] && gameLocal.entities[ gameLocal.GetLocalClientNum() ]->IsType( idPlayer::Type ) ) {
- p = static_cast< idPlayer * >( gameLocal.entities[ gameLocal.GetLocalClientNum() ] );
- if ( p && p->spectating ) {
- spectatorMessages = p->mpMessages;
- }
- }
- if ( !spectatorMessages || !spectatorMessages->IsActive() ) {
- return;
- }
- idPlayer * viewPlayer = static_cast<idPlayer *>( gameLocal.entities[ p->spectator ] );
- if ( viewPlayer == NULL ) {
- return;
- }
- idStr spectatetext[ 2 ];
- if ( !gameLocal.mpGame.IsScoreboardActive() ) {
- gameLocal.mpGame.GetSpectateText( p, spectatetext, false );
- }
- idSWFScriptObject & root = spectatorMessages->GetRootObject();
- idSWFTextInstance * txtVal = root.GetNestedText( "txtSpectating" );
- if ( txtVal != NULL ) {
- txtVal->tooltip = true;
- txtVal->SetText( spectatetext[0] );
- txtVal->SetStrokeInfo( true, 0.75f, 1.75f );
- }
- txtVal = root.GetNestedText( "txtFollow" );
- if ( txtVal != NULL ) {
- txtVal->SetText( spectatetext[1] );
- txtVal->SetStrokeInfo( true, 0.75f, 1.75f );
- }
- }
- /*
- ===============
- idPlayer::UpdateMpMessages
- ===============
- */
- void idPlayer::AddChatMessage( int index, int alpha, const idStr & message ) {
- if ( mpMessages == NULL || !mpMessages->IsActive() ) {
- return;
- }
- idSWFScriptObject * mpChat = mpMessages->GetRootObject().GetNestedObj( "_left", "mpChat" );
- idSWFSpriteInstance * info = mpChat->GetNestedSprite( va( "info%i", index ) );
- idSWFTextInstance * txtVal = mpChat->GetNestedText( va( "info%i", index ), "txtVal" );
- if ( info ) {
- info->SetVisible( true );
- if ( alpha >= 4 ) {
- info->SetAlpha( 1.0f );
- } else if ( alpha == 3 ) {
- info->SetAlpha( 0.875f );
- } else if ( alpha == 2 ) {
- info->SetAlpha( 0.75f );
- } else if ( alpha == 1 ) {
- info->SetAlpha( 0.625f );
- } else {
- info->SetAlpha( 0.5f );
- }
- }
- if ( txtVal ) {
- txtVal->SetIgnoreColor( false );
- txtVal->SetText( message );
- txtVal->SetStrokeInfo( true, 0.9f, 1.75f );
- }
- }
- /*
- ===============
- idPlayer::UpdateMpMessages
- ===============
- */
- void idPlayer::ClearChatMessage( int index ) {
- if ( mpMessages == NULL || !mpMessages->IsActive() ) {
- return;
- }
- idSWFScriptObject * mpChat = mpMessages->GetRootObject().GetNestedObj( "_left", "mpChat" );
- idSWFSpriteInstance * info = mpChat->GetNestedSprite( va( "info%i", index ) );
- idSWFTextInstance * txtVal = mpChat->GetNestedText( va( "info%i", index ), "txtVal" );
- if ( info ) {
- info->SetVisible( false );
- }
- if ( txtVal ) {
- txtVal->SetText( "" );
- }
- }
- /*
- ===============
- idPlayer::DrawHUD
- ===============
- */
- void idPlayer::DrawHUD( idMenuHandler_HUD * _hudManager ) {
- SCOPED_PROFILE_EVENT( "idPlayer::DrawHUD" );
- if ( !weapon.GetEntity() || influenceActive != INFLUENCE_NONE || privateCameraView || gameLocal.GetCamera() || !g_showHud.GetBool() ) {
- return;
- }
-
- if ( common->IsMultiplayer() ) {
- UpdateChattingHud();
- UpdateSpectatingText();
- }
- // Always draw the local client's messages so that chat works correctly while spectating another player.
- idPlayer * localPlayer = static_cast< idPlayer * >( gameLocal.entities[ gameLocal.GetLocalClientNum() ] );
- if ( localPlayer != NULL && localPlayer->mpMessages != NULL ) {
- localPlayer->mpMessages->Render( renderSystem, Sys_Milliseconds() );
- }
- UpdateHudStats( _hudManager );
- if ( spectating ) {
- return;
- }
- if ( _hudManager ) {
- _hudManager->Update();
- }
- weapon.GetEntity()->UpdateGUI();
- // weapon targeting crosshair
- if ( !GuiActive() ) {
- // don't show the 2D crosshair in stereo rendering, use the
- // laser sight model instead
- if ( _hudManager && _hudManager->GetHud() ) {
- idMenuScreen_HUD * hud = _hudManager->GetHud();
- if ( weapon.GetEntity()->ShowCrosshair() && !IsGameStereoRendered() ) {
- if ( weapon.GetEntity()->GetGrabberState() == 1 || weapon.GetEntity()->GetGrabberState() == 2 ) {
- hud->SetCursorState( this, CURSOR_GRABBER, 1 );
- hud->SetCursorState( this, CURSOR_IN_COMBAT, 0 );
- } else {
- hud->SetCursorState( this, CURSOR_GRABBER, 0 );
- hud->SetCursorState( this, CURSOR_IN_COMBAT, 1 );
- }
- } else {
- hud->SetCursorState( this, CURSOR_NONE, 1 );
- }
- hud->UpdateCursorState();
- }
- } else if ( _hudManager && _hudManager->GetHud() ) {
- idMenuScreen_HUD * hud = _hudManager->GetHud();
- hud->SetCursorState( this, CURSOR_NONE, 1 );
- hud->UpdateCursorState();
- }
- }
- /*
- ===============
- idPlayer::EnterCinematic
- ===============
- */
- void idPlayer::EnterCinematic() {
- if ( PowerUpActive( HELLTIME ) ) {
- StopHelltime();
- }
- Hide();
- StopSound( SND_CHANNEL_PDA_AUDIO, false );
- StopSound( SND_CHANNEL_PDA_VIDEO, false );
- if ( hudManager ) {
- hudManager->SetRadioMessage( false );
- }
- physicsObj.SetLinearVelocity( vec3_origin );
-
- SetState( "EnterCinematic" );
- UpdateScript();
- if ( weaponEnabled && weapon.GetEntity() ) {
- weapon.GetEntity()->EnterCinematic();
- }
- if ( flashlight.GetEntity() ) {
- flashlight.GetEntity()->EnterCinematic();
- }
- AI_FORWARD = false;
- AI_BACKWARD = false;
- AI_STRAFE_LEFT = false;
- AI_STRAFE_RIGHT = false;
- AI_RUN = false;
- AI_ATTACK_HELD = false;
- AI_WEAPON_FIRED = false;
- AI_JUMP = false;
- AI_CROUCH = false;
- AI_ONGROUND = true;
- AI_ONLADDER = false;
- AI_DEAD = ( health <= 0 );
- AI_RUN = false;
- AI_PAIN = false;
- AI_HARDLANDING = false;
- AI_SOFTLANDING = false;
- AI_RELOAD = false;
- AI_TELEPORT = false;
- AI_TURN_LEFT = false;
- AI_TURN_RIGHT = false;
- }
- /*
- ===============
- idPlayer::ExitCinematic
- ===============
- */
- void idPlayer::ExitCinematic() {
- Show();
- if ( weaponEnabled && weapon.GetEntity() ) {
- weapon.GetEntity()->ExitCinematic();
- }
- if ( flashlight.GetEntity() ) {
- flashlight.GetEntity()->ExitCinematic();
- }
- // long cinematics would have surpassed the healthTakeTime, causing the player to take damage
- // immediately after the cinematic ends. Instead we start the healthTake cooldown again once
- // the cinematic ends.
- if ( g_skill.GetInteger() == 3 ) {
- nextHealthTake = gameLocal.time + g_healthTakeTime.GetInteger() * 1000;
- }
- SetState( "ExitCinematic" );
- UpdateScript();
- }
- /*
- =====================
- idPlayer::UpdateConditions
- =====================
- */
- void idPlayer::UpdateConditions() {
- idVec3 velocity;
- float fallspeed;
- float forwardspeed;
- float sidespeed;
- // minus the push velocity to avoid playing the walking animation and sounds when riding a mover
- velocity = physicsObj.GetLinearVelocity() - physicsObj.GetPushedLinearVelocity();
- fallspeed = velocity * physicsObj.GetGravityNormal();
- if ( influenceActive ) {
- AI_FORWARD = false;
- AI_BACKWARD = false;
- AI_STRAFE_LEFT = false;
- AI_STRAFE_RIGHT = false;
- } else if ( gameLocal.time - lastDmgTime < 500 ) {
- forwardspeed = velocity * viewAxis[ 0 ];
- sidespeed = velocity * viewAxis[ 1 ];
- AI_FORWARD = AI_ONGROUND && ( forwardspeed > 20.01f );
- AI_BACKWARD = AI_ONGROUND && ( forwardspeed < -20.01f );
- AI_STRAFE_LEFT = AI_ONGROUND && ( sidespeed > 20.01f );
- AI_STRAFE_RIGHT = AI_ONGROUND && ( sidespeed < -20.01f );
- } else if ( xyspeed > MIN_BOB_SPEED ) {
- AI_FORWARD = AI_ONGROUND && ( usercmd.forwardmove > 0 );
- AI_BACKWARD = AI_ONGROUND && ( usercmd.forwardmove < 0 );
- AI_STRAFE_LEFT = AI_ONGROUND && ( usercmd.rightmove < 0 );
- AI_STRAFE_RIGHT = AI_ONGROUND && ( usercmd.rightmove > 0 );
- } else {
- AI_FORWARD = false;
- AI_BACKWARD = false;
- AI_STRAFE_LEFT = false;
- AI_STRAFE_RIGHT = false;
- }
- AI_RUN = ( usercmd.buttons & BUTTON_RUN ) && ( ( !pm_stamina.GetFloat() ) || ( stamina > pm_staminathreshold.GetFloat() ) );
- AI_DEAD = ( health <= 0 );
- }
- /*
- ==================
- WeaponFireFeedback
- Called when a weapon fires, generates head twitches, etc
- ==================
- */
- void idPlayer::WeaponFireFeedback( const idDict *weaponDef ) {
- // force a blink
- blink_time = 0;
- // play the fire animation
- AI_WEAPON_FIRED = true;
- // update view feedback
- playerView.WeaponFireFeedback( weaponDef );
- // shake controller
- float highMagnitude = weaponDef->GetFloat( "controllerShakeHighMag" );
- int highDuration = weaponDef->GetInt( "controllerShakeHighTime" );
- float lowMagnitude = weaponDef->GetFloat( "controllerShakeLowMag" );
- int lowDuration = weaponDef->GetInt( "controllerShakeLowTime" );
- //const char *name = weaponDef->GetString( "inv_name" );
- if( IsLocallyControlled() ) {
- SetControllerShake( highMagnitude, highDuration, lowMagnitude, lowDuration );
- }
- }
- /*
- ===============
- idPlayer::StopFiring
- ===============
- */
- void idPlayer::StopFiring() {
- AI_ATTACK_HELD = false;
- AI_WEAPON_FIRED = false;
- AI_RELOAD = false;
- if ( weapon.GetEntity() ) {
- weapon.GetEntity()->EndAttack();
- }
- }
- /*
- ===============
- idPlayer::FireWeapon
- ===============
- */
- idCVar g_infiniteAmmo( "g_infiniteAmmo", "0", CVAR_GAME | CVAR_BOOL, "infinite ammo" );
- extern idCVar ui_autoSwitch;
- void idPlayer::FireWeapon() {
- idMat3 axis;
- idVec3 muzzle;
- if ( privateCameraView ) {
- return;
- }
- if ( g_editEntityMode.GetInteger() ) {
- GetViewPos( muzzle, axis );
- if ( gameLocal.editEntities->SelectEntity( muzzle, axis[0], this ) ) {
- return;
- }
- }
- if ( !hiddenWeapon && weapon.GetEntity()->IsReady() ) {
- if ( g_infiniteAmmo.GetBool() || weapon.GetEntity()->AmmoInClip() || weapon.GetEntity()->AmmoAvailable() ) {
- AI_ATTACK_HELD = true;
- weapon.GetEntity()->BeginAttack();
- if ( ( weapon_soulcube >= 0 ) && ( currentWeapon == weapon_soulcube ) ) {
- if ( hud ) {
- hud->UpdateSoulCube( false );
- }
- SelectWeapon( previousWeapon, false );
- }
- if( (weapon_bloodstone >= 0) && (currentWeapon == weapon_bloodstone) && inventory.weapons & ( 1 << weapon_bloodstone_active1 ) && weapon.GetEntity()->GetStatus() == WP_READY) {
- // tell it to switch to the previous weapon. Only do this once to prevent
- // weapon toggling messing up the previous weapon
- if(idealWeapon == weapon_bloodstone) {
- if(previousWeapon == weapon_bloodstone || previousWeapon == -1) {
- NextBestWeapon();
- } else {
- //Since this is a toggle weapon just select itself and it will toggle to the last weapon
- SelectWeapon( weapon_bloodstone, false );
- }
- }
- }
- } else {
- idLobbyBase & lobby = session->GetActingGameStateLobbyBase();
- lobbyUserID_t & lobbyUserID = gameLocal.lobbyUserIDs[ entityNumber ];
- bool autoSwitch = lobby.GetLobbyUserWeaponAutoSwitch( lobbyUserID );
- if ( !autoSwitch ) {
- return;
- }
- // update our ammo clip in our inventory
- if ( ( currentWeapon >= 0 ) && ( currentWeapon < MAX_WEAPONS ) ) {
- inventory.SetClipAmmoForWeapon( currentWeapon, weapon.GetEntity()->AmmoInClip() );
- }
- NextBestWeapon();
- }
- }
-
- if ( tipUp ) {
- HideTip();
- }
-
- if ( objectiveUp ) {
- HideObjective();
- }
- }
- /*
- ===============
- idPlayer::CacheWeapons
- ===============
- */
- void idPlayer::CacheWeapons() {
- idStr weap;
- int w;
- // check if we have any weapons
- if ( !inventory.weapons ) {
- return;
- }
-
- for( w = 0; w < MAX_WEAPONS; w++ ) {
- if ( inventory.weapons & ( 1 << w ) ) {
- weap = spawnArgs.GetString( va( "def_weapon%d", w ) );
- if ( weap != "" ) {
- idWeapon::CacheWeapon( weap );
- } else {
- inventory.weapons &= ~( 1 << w );
- }
- }
- }
- }
- /*
- ===============
- idPlayer::SetQuickSlot
- ===============
- */
- void idPlayer::SetQuickSlot( int index, int val ) {
- if ( index >= NUM_QUICK_SLOTS || index < 0 ) {
- return;
- }
- quickSlot[ index ] = val;
- }
- /*
- ===============
- idPlayer::GetQuickSlot
- ===============
- */
- int idPlayer::GetQuickSlot( int index ) {
- if ( index >= NUM_QUICK_SLOTS || index < 0 ) {
- return -1;
- }
- return quickSlot[ index ];
- }
- /*
- ===============
- idPlayer::Give
- ===============
- */
- bool idPlayer::Give( const char *statname, const char *value, unsigned int giveFlags ) {
- int amount;
- if ( AI_DEAD ) {
- return false;
- }
- if ( !idStr::Icmp( statname, "health" ) ) {
- if ( health >= inventory.maxHealth ) {
- return false;
- }
- if ( giveFlags & ITEM_GIVE_UPDATE_STATE ) {
- amount = atoi( value );
- if ( amount ) {
- health += amount;
- if ( health > inventory.maxHealth ) {
- health = inventory.maxHealth;
- }
- healthPulse = true;
- }
- }
- } else if ( !idStr::Icmp( statname, "stamina" ) ) {
- if ( stamina >= 100 ) {
- return false;
- }
- if ( giveFlags & ITEM_GIVE_UPDATE_STATE ) {
- stamina += atof( value );
- if ( stamina > 100 ) {
- stamina = 100;
- }
- }
- } else if ( !idStr::Icmp( statname, "heartRate" ) ) {
- if ( giveFlags & ITEM_GIVE_UPDATE_STATE ) {
- heartRate += atoi( value );
- if ( heartRate > MAX_HEARTRATE ) {
- heartRate = MAX_HEARTRATE;
- }
- }
- } else if ( !idStr::Icmp( statname, "air" ) ) {
- if ( airMsec >= pm_airMsec.GetInteger() ) {
- return false;
- }
- if ( giveFlags & ITEM_GIVE_UPDATE_STATE ) {
- airMsec += pm_airMsec.GetInteger() * atoi( value ) / 100;
- if ( airMsec > pm_airMsec.GetInteger() ) {
- airMsec = pm_airMsec.GetInteger();
- }
- }
- } else if ( !idStr::Icmp( statname, "enviroTime" ) ) {
- if ( ( giveFlags & ITEM_GIVE_UPDATE_STATE ) && PowerUpActive( ENVIROTIME ) ) {
- inventory.powerupEndTime[ ENVIROTIME ] += (atof(value) * 1000);
- } else {
- GivePowerUp( ENVIROTIME, atoi(value)*1000, giveFlags );
- }
- } else {
- bool ret = inventory.Give( this, spawnArgs, statname, value, &idealWeapon, true, giveFlags );
- return ret;
- }
- return true;
- }
- /*
- ===============
- idPlayer::GiveHealthPool
- adds health to the player health pool
- ===============
- */
- void idPlayer::GiveHealthPool( float amt ) {
-
- if ( AI_DEAD ) {
- return;
- }
- if ( health > 0 ) {
- healthPool += amt;
- if ( healthPool > inventory.maxHealth - health ) {
- healthPool = inventory.maxHealth - health;
- }
- nextHealthPulse = gameLocal.time;
- }
- }
- /*
- ===============
- idPlayer::GiveItem
- Returns false if the item shouldn't be picked up
- ===============
- */
- bool idPlayer::GiveItem( idItem *item, unsigned int giveFlags ) {
- int i;
- const idKeyValue *arg;
- idDict attr;
- bool gave;
- int numPickup;
- if ( common->IsMultiplayer() && spectating ) {
- return false;
- }
- if ( idStr::FindText( item->GetName(), "weapon_flashlight_new" ) > -1 ) {
- return false;
- }
- if ( idStr::FindText( item->GetName(), "weapon_flashlight" ) > -1 ) {
- // don't allow flashlight weapon unless classic mode is enabled
- return false;
- }
- item->GetAttributes( attr );
-
- gave = false;
- numPickup = inventory.pickupItemNames.Num();
- for( i = 0; i < attr.GetNumKeyVals(); i++ ) {
- arg = attr.GetKeyVal( i );
- if ( Give( arg->GetKey(), arg->GetValue(), giveFlags ) ) {
- gave = true;
- }
- }
- if ( giveFlags & ITEM_GIVE_FEEDBACK ) {
- arg = item->spawnArgs.MatchPrefix( "inv_weapon", NULL );
- if ( arg ) {
- // We need to update the weapon hud manually, but not
- // the armor/ammo/health because they are updated every
- // frame no matter what
- UpdateHudWeapon( false );
- }
- // display the pickup feedback on the hud
- if ( gave && ( numPickup == inventory.pickupItemNames.Num() ) ) {
- inventory.AddPickupName( item->spawnArgs.GetString( "inv_name" ), this ); //_D3XP
- }
- }
- return gave;
- }
- /*
- ===============
- idPlayer::PowerUpModifier
- ===============
- */
- float idPlayer::PowerUpModifier( int type ) {
- float mod = 1.0f;
- if ( PowerUpActive( BERSERK ) ) {
- switch( type ) {
- case SPEED: {
- mod *= 1.7f;
- break;
- }
- case PROJECTILE_DAMAGE: {
- mod *= 2.0f;
- break;
- }
- case MELEE_DAMAGE: {
- mod *= 30.0f;
- break;
- }
- case MELEE_DISTANCE: {
- mod *= 2.0f;
- break;
- }
- }
- }
- if ( common->IsMultiplayer() && !common->IsClient() ) {
- if ( PowerUpActive( MEGAHEALTH ) ) {
- if ( healthPool <= 0 ) {
- GiveHealthPool( 100 );
- }
- } else {
- healthPool = 0;
- }
- /*if( PowerUpActive( HASTE ) ) {
- switch( type ) {
- case SPEED: {
- mod = 1.7f;
- break;
- }
- }
- }*/
- }
- return mod;
- }
- /*
- ===============
- idPlayer::PowerUpActive
- ===============
- */
- bool idPlayer::PowerUpActive( int powerup ) const {
- return ( inventory.powerups & ( 1 << powerup ) ) != 0;
- }
- /*
- ===============
- idPlayer::GivePowerUp
- ===============
- */
- bool idPlayer::GivePowerUp( int powerup, int time, unsigned int giveFlags ) {
- const char *sound;
- if ( powerup >= 0 && powerup < MAX_POWERUPS ) {
- if ( giveFlags & ITEM_GIVE_UPDATE_STATE ) {
- if ( common->IsServer() ) {
- idBitMsg msg;
- byte msgBuf[MAX_EVENT_PARAM_SIZE];
- msg.InitWrite( msgBuf, sizeof( msgBuf ) );
- msg.WriteShort( powerup );
- msg.WriteShort( time );
- ServerSendEvent( EVENT_POWERUP, &msg, false );
- }
- if ( powerup != MEGAHEALTH ) {
- inventory.GivePowerUp( this, powerup, time );
- }
- }
- switch( powerup ) {
- case BERSERK: {
- if ( giveFlags & ITEM_GIVE_FROM_WEAPON ) {
- // Berserk is granted by the bloodstone in ROE, but we don't want any of the
- // standard behavior (sound fx, switch to fists) when you get it this way.
- } else {
- if ( giveFlags & ITEM_GIVE_FEEDBACK ) {
- inventory.AddPickupName( "#str_00100627", this );
- if ( spawnArgs.GetString( "snd_berserk_third", "", &sound ) && sound[ 0 ] != '\0' ) {
- StartSoundShader( declManager->FindSound( sound ), SND_CHANNEL_DEMONIC, 0, false, NULL );
- }
- }
- if ( giveFlags & ITEM_GIVE_UPDATE_STATE ) {
- if ( !common->IsClient() ) {
- idealWeapon = weapon_fists;
- }
- }
- }
- break;
- }
- case INVISIBILITY: {
- if ( common->IsMultiplayer() && ( giveFlags & ITEM_GIVE_FEEDBACK ) ) {
- inventory.AddPickupName("#str_00100628", this);
- }
- if ( giveFlags & ITEM_GIVE_UPDATE_STATE ) {
- // remove any decals from the model
- if ( modelDefHandle != -1 ) {
- gameRenderWorld->RemoveDecals( modelDefHandle );
- }
- if ( weapon.GetEntity() ) {
- weapon.GetEntity()->UpdateSkin();
- }
- if( flashlight.GetEntity() ) {
- flashlight.GetEntity()->UpdateSkin();
- }
- }
- /* if ( spawnArgs.GetString( "snd_invisibility", "", &sound ) ) {
- StartSoundShader( declManager->FindSound( sound ), SND_CHANNEL_ANY, 0, false, NULL );
- } */
- break;
- }
- case ADRENALINE: {
- if ( giveFlags & ITEM_GIVE_FEEDBACK ) {
- inventory.AddPickupName("#str_00100799", this);
- }
- if ( giveFlags & ITEM_GIVE_UPDATE_STATE ) {
- stamina = 100.0f;
- }
- break;
- }
- case MEGAHEALTH: {
- if ( giveFlags & ITEM_GIVE_FEEDBACK ) {
- if( common->IsMultiplayer() ) {
- inventory.AddPickupName("#str_00100629", this);
- }
- if ( spawnArgs.GetString( "snd_megahealth", "", &sound ) ) {
- StartSoundShader( declManager->FindSound( sound ), SND_CHANNEL_ANY, 0, false, NULL );
- }
- }
- if ( giveFlags & ITEM_GIVE_UPDATE_STATE ) {
- health = 200;
- }
- break;
- }
- case HELLTIME: {
- if ( spawnArgs.GetString( "snd_helltime_start", "", &sound ) ) {
- PostEventMS( &EV_StartSoundShader, 0, sound, SND_CHANNEL_ANY );
- }
- if ( spawnArgs.GetString( "snd_helltime_loop", "", &sound ) ) {
- PostEventMS( &EV_StartSoundShader, 0, sound, SND_CHANNEL_DEMONIC );
- }
- break;
- }
- case ENVIROSUIT: {
- if ( giveFlags & ITEM_GIVE_FEEDBACK ) {
- // Turn on the envirosuit sound
- if ( gameSoundWorld ) {
- gameSoundWorld->SetEnviroSuit( true );
- }
- }
- if( giveFlags & ITEM_GIVE_UPDATE_STATE ) {
- // Put the helmet and lights on the player
- idDict args;
- // Light
- const idDict *lightDef = gameLocal.FindEntityDefDict( "envirosuit_light", false );
- if ( lightDef ) {
- idEntity *temp;
- gameLocal.SpawnEntityDef( *lightDef, &temp, false );
- idLight *eLight = static_cast<idLight *>(temp);
- eLight->GetPhysics()->SetOrigin( firstPersonViewOrigin );
- eLight->UpdateVisuals();
- eLight->Present();
- enviroSuitLight = eLight;
- }
- }
- break;
- }
- case ENVIROTIME: {
- if ( giveFlags & ITEM_GIVE_UPDATE_STATE ) {
- hudPowerup = ENVIROTIME;
- // The HUD display bar is fixed at 60 seconds
- hudPowerupDuration = 60000;
- }
- break;
- }
- case INVULNERABILITY: {
- if ( common->IsMultiplayer() && ( giveFlags & ITEM_GIVE_FEEDBACK ) ) {
- inventory.AddPickupName("#str_00100630", this);
- }
- break;
- }
- }
- if ( giveFlags & ITEM_GIVE_UPDATE_STATE ) {
- UpdateSkinSetup();
- }
- return true;
- } else {
- gameLocal.Warning( "Player given power up %i\n which is out of range", powerup );
- }
- return false;
- }
- /*
- ==============
- idPlayer::ClearPowerup
- ==============
- */
- void idPlayer::ClearPowerup( int i ) {
- if ( common->IsServer() ) {
- idBitMsg msg;
- byte msgBuf[MAX_EVENT_PARAM_SIZE];
- msg.InitWrite( msgBuf, sizeof( msgBuf ) );
- msg.WriteShort( i );
- msg.WriteShort( 0 );
- ServerSendEvent( EVENT_POWERUP, &msg, false );
- }
- powerUpSkin = NULL;
- inventory.powerups &= ~( 1 << i );
- inventory.powerupEndTime[ i ] = 0;
- switch( i ) {
- case BERSERK: {
- if(common->IsMultiplayer()) {
- StopSound( SND_CHANNEL_DEMONIC, false );
- }
- if(!common->IsMultiplayer()) {
- StopHealthRecharge();
- }
- break;
- }
- case INVISIBILITY: {
- if ( weapon.GetEntity() ) {
- weapon.GetEntity()->UpdateSkin();
- }
- if ( flashlight.GetEntity() ) {
- flashlight.GetEntity()->UpdateSkin();
- }
- break;
- }
- case HELLTIME: {
- GetAchievementManager().ResetHellTimeKills();
- StopSound( SND_CHANNEL_DEMONIC, false );
- break;
- }
- case ENVIROSUIT: {
-
- hudPowerup = -1;
- // Turn off the envirosuit sound
- if ( gameSoundWorld ) {
- gameSoundWorld->SetEnviroSuit( false );
- }
- // Take off the helmet and lights
- if ( enviroSuitLight.IsValid() ) {
- enviroSuitLight.GetEntity()->PostEventMS( &EV_Remove, 0 );
- }
- enviroSuitLight = NULL;
- break;
- }
- case INVULNERABILITY: {
- if(common->IsMultiplayer()) {
- StopSound( SND_CHANNEL_DEMONIC, false );
- }
- }
- /*case HASTE: {
- if(common->IsMultiplayer()) {
- StopSound( SND_CHANNEL_DEMONIC, false );
- }
- }*/
- }
- }
- /*
- ==============
- idPlayer::UpdatePowerUps
- ==============
- */
- void idPlayer::UpdatePowerUps() {
- int i;
- if ( !common->IsClient() ) {
- for ( i = 0; i < MAX_POWERUPS; i++ ) {
- if ( ( inventory.powerups & ( 1 << i ) ) && inventory.powerupEndTime[i] > gameLocal.time ) {
- switch( i ) {
- case ENVIROSUIT: {
- if ( enviroSuitLight.IsValid() ) {
- idAngles lightAng = firstPersonViewAxis.ToAngles();
- idVec3 lightOrg = firstPersonViewOrigin;
- const idDict *lightDef = gameLocal.FindEntityDefDict( "envirosuit_light", false );
- idVec3 enviroOffset = lightDef->GetVector( "enviro_offset" );
- idVec3 enviroAngleOffset = lightDef->GetVector( "enviro_angle_offset" );
- lightOrg += (enviroOffset.x * firstPersonViewAxis[0]);
- lightOrg += (enviroOffset.y * firstPersonViewAxis[1]);
- lightOrg += (enviroOffset.z * firstPersonViewAxis[2]);
- lightAng.pitch += enviroAngleOffset.x;
- lightAng.yaw += enviroAngleOffset.y;
- lightAng.roll += enviroAngleOffset.z;
- enviroSuitLight.GetEntity()->GetPhysics()->SetOrigin( lightOrg );
- enviroSuitLight.GetEntity()->GetPhysics()->SetAxis( lightAng.ToMat3() );
- enviroSuitLight.GetEntity()->UpdateVisuals();
- enviroSuitLight.GetEntity()->Present();
- }
- break;
- }
- default: {
- break;
- }
- }
- }
- if ( PowerUpActive( i ) && inventory.powerupEndTime[i] <= gameLocal.time ) {
- ClearPowerup( i );
- }
- }
- }
- if ( health > 0 ) {
- if ( powerUpSkin ) {
- renderEntity.customSkin = powerUpSkin;
- } else {
- renderEntity.customSkin = skin;
- }
- }
- if ( healthPool && gameLocal.time > nextHealthPulse && !AI_DEAD && health > 0 ) {
- assert( !common->IsClient() ); // healthPool never be set on client
- int amt = ( healthPool > 5.0f ) ? 5 : healthPool;
- health += amt;
- if ( health > inventory.maxHealth ) {
- health = inventory.maxHealth;
- healthPool = 0;
- } else {
- healthPool -= amt;
- }
- if ( healthPool < 1.0f ) {
- healthPool = 0.0f;
- } else {
- nextHealthPulse = gameLocal.time + HEALTHPULSE_TIME;
- healthPulse = true;
- }
- }
- if ( !gameLocal.inCinematic && influenceActive == 0 && g_skill.GetInteger() == 3 && gameLocal.time > nextHealthTake && !AI_DEAD && health > g_healthTakeLimit.GetInteger() ) {
- assert( !common->IsClient() ); // healthPool never be set on client
-
- if(!PowerUpActive(INVULNERABILITY)) {
- health -= g_healthTakeAmt.GetInteger();
- if ( health < g_healthTakeLimit.GetInteger() ) {
- health = g_healthTakeLimit.GetInteger();
- }
- }
- nextHealthTake = gameLocal.time + g_healthTakeTime.GetInteger() * 1000;
- healthTake = true;
- }
- }
- /*
- ===============
- idPlayer::ClearPowerUps
- ===============
- */
- void idPlayer::ClearPowerUps() {
- int i;
- for ( i = 0; i < MAX_POWERUPS; i++ ) {
- if ( PowerUpActive( i ) ) {
- ClearPowerup( i );
- }
- }
- inventory.ClearPowerUps();
- if ( common->IsMultiplayer() ) {
- if ( enviroSuitLight.IsValid() ) {
- enviroSuitLight.GetEntity()->PostEventMS( &EV_Remove, 0 );
- }
- }
- }
- /*
- ===============
- idPlayer::GiveInventoryItem
- ===============
- */
- bool idPlayer::GiveInventoryItem( idDict * item, unsigned int giveFlags ) {
- if ( common->IsMultiplayer() && spectating ) {
- return false;
- }
-
- if ( giveFlags & ITEM_GIVE_UPDATE_STATE ) {
- inventory.items.Append( new (TAG_ENTITY) idDict( *item ) );
- }
- const char * itemName = item->GetString( "inv_name" );
- if ( giveFlags & ITEM_GIVE_FEEDBACK ) {
- if ( idStr::Cmpn( itemName, STRTABLE_ID, STRTABLE_ID_LENGTH ) == 0 ) {
- inventory.pickupItemNames.Append( idLocalization::GetString( itemName ) );
- } else {
- inventory.pickupItemNames.Append( itemName );
- }
- const char * icon = item->GetString( "inv_icon" );
- if ( hud != NULL ) {
- hud->ShowNewItem( itemName, icon );
- }
- }
-
- // D3XP added to support powercells
- if( ( giveFlags & ITEM_GIVE_UPDATE_STATE ) && item->GetInt("inv_powercell") && focusUI) {
- //Reset the powercell count
- int powerCellCount = 0;
- for ( int j = 0; j < inventory.items.Num(); j++ ) {
- idDict *item = inventory.items[ j ];
- if(item->GetInt("inv_powercell")) {
- powerCellCount++;
- }
- }
- focusUI->SetStateInt( "powercell_count", powerCellCount );
- }
- return true;
- }
- /*
- ==============
- idPlayer::GiveInventoryItem
- ==============
- */
- bool idPlayer::GiveInventoryItem( const char *name ) {
- idDict args;
- args.Set( "classname", name );
- args.Set( "owner", this->name.c_str() );
- gameLocal.SpawnEntityDef( args);
- return true;
- }
- /*
- ===============
- idPlayer::GiveObjective
- ===============
- */
- void idPlayer::GiveObjective( const char * title, const char * text, const idMaterial * screenshot ) {
- idObjectiveInfo & info = inventory.objectiveNames.Alloc();
- info.title = title;
- info.text = text;
- info.screenshot = screenshot;
- StartSound( "snd_objectiveup", SND_CHANNEL_ANY, 0, false, NULL );
- if ( hud ) {
- hud->SetupObjective( title, text, screenshot );
- hud->ShowObjective( false );
- objectiveUp = true;
- }
- }
- /*
- ===============
- idPlayer::CompleteObjective
- ===============
- */
- void idPlayer::CompleteObjective( const char *title ) {
- int c = inventory.objectiveNames.Num();
- for ( int i = 0; i < c; i++ ) {
- if ( idStr::Icmp(inventory.objectiveNames[i].title, title) == 0 ) {
- inventory.objectiveNames.RemoveIndex( i );
- break;
- }
- }
- StartSound( "snd_objectiveup", SND_CHANNEL_ANY, 0, false, NULL );
-
- if ( hud ) {
- hud->SetupObjectiveComplete( title );
- hud->ShowObjective( true );
- }
- }
- /*
- ===============
- idPlayer::GiveVideo
- ===============
- */
- void idPlayer::GiveVideo( const idDeclVideo * video, const char * itemName ) {
- if ( video == NULL ) {
- return;
- }
- int oldNumVideos = inventory.videos.Num();
- inventory.videos.AddUnique( video );
- if ( oldNumVideos < inventory.videos.Num() ) {
- GetAchievementManager().EventCompletesAchievement( ACHIEVEMENT_WATCH_ALL_VIDEOS );
- }
- if ( itemName != NULL && itemName[0] != 0 ) {
- inventory.pickupItemNames.Append( itemName );
- }
- if ( hud ) {
- hud->DownloadVideo();
- }
- }
- /*
- ===============
- idPlayer::GiveSecurity
- ===============
- */
- void idPlayer::GiveSecurity( const char *security ) {
- GetPDA()->SetSecurity( security );
- if ( hud ) {
- hud->UpdatedSecurity();
- }
- }
- /*
- ===============
- idPlayer::GiveEmail
- ===============
- */
- void idPlayer::GiveEmail( const idDeclEmail * email ) {
- if ( email == NULL ) {
- return;
- }
- inventory.emails.AddUnique( email );
- GetPDA()->AddEmail( email );
- // TODO_SPARTY: hook up new email notification in new hud
- //if ( hud ) {
- // hud->HandleNamedEvent( "emailPickup" );
- //}
- }
- /*
- ===============
- idPlayer::GivePDA
- ===============
- */
- void idPlayer::GivePDA( const idDeclPDA * pda, const char * securityItem ) {
- if ( common->IsMultiplayer() && spectating ) {
- return;
- }
- if ( securityItem != NULL && securityItem[0] != 0 ) {
- inventory.pdaSecurity.AddUnique( securityItem );
- }
- // Just to make sure they want the default player spawn defined pda.
- // Some what of a hack, so i dont have to change any map scripts that initially give
- // the player "personal" pda.
- if ( pda == NULL || idStr::Icmp( pda->GetName(), "personal" ) == 0 ) {
- pda = static_cast<const idDeclPDA *>( declManager->FindType( DECL_PDA, spawnArgs.GetString( "pda_name", "personal" ) ) );
- }
- if ( pda == NULL ) {
- return;
- }
- int oldNumPDAs = inventory.pdas.Num();
- inventory.pdas.AddUnique( pda );
- int newNumPDAs = inventory.pdas.Num();
- // Set the stat for # of PDAs...
- // Only increment the PDA stat if we've added a new one....
- if ( oldNumPDAs < newNumPDAs ) {
- switch ( GetExpansionType() ) {
- case GAME_BASE:
- GetAchievementManager().EventCompletesAchievement( ACHIEVEMENT_PDAS_BASE );
- break;
- case GAME_D3XP:
- GetAchievementManager().EventCompletesAchievement( ACHIEVEMENT_PDAS_ROE );
- break;
- case GAME_D3LE:
- GetAchievementManager().EventCompletesAchievement( ACHIEVEMENT_PDAS_LE );
- break;
- }
- }
- // Copy any videos over
- for ( int i = 0; i < pda->GetNumVideos(); i++ ) {
- const idDeclVideo * video = pda->GetVideoByIndex( i );
- if ( video != NULL ) {
- inventory.videos.AddUnique( video );
- }
- }
- // This is kind of a hack, but it works nicely
- // We don't want to display the 'you got a new pda' message during a map load
- if ( gameLocal.GetFrameNum() > 10 ) {
- const char * sec = pda->GetSecurity();
- if ( hud ) {
- hud->DownloadPDA( pda, ( sec != NULL && sec[0] != 0 ) ? true : false );
- }
- if ( inventory.pdas.Num() == 1 ) {
- GetPDA()->RemoveAddedEmailsAndVideos();
- if ( !objectiveSystemOpen ) {
- TogglePDA();
- }
- //ShowTip( spawnArgs.GetString( "text_infoTitle" ), spawnArgs.GetString( "text_firstPDA" ), true );
- }
- }
- }
- /*
- ===============
- idPlayer::FindInventoryItem
- ===============
- */
- idDict *idPlayer::FindInventoryItem( const char *name ) {
- for ( int i = 0; i < inventory.items.Num(); i++ ) {
- const char *iname = inventory.items[i]->GetString( "inv_name" );
- if ( iname != NULL && *iname != NULL ) {
- if ( idStr::Icmp( name, iname ) == 0 ) {
- return inventory.items[i];
- }
- }
- }
- return NULL;
- }
- /*
- ===============
- idPlayer::FindInventoryItem
- ===============
- */
- idDict * idPlayer::FindInventoryItem( int index ) {
- if ( index <= inventory.items.Num() ) {
- return inventory.items[ index ];
- }
- return NULL;
- }
- /*
- ===============
- idPlayer::GetNumInventoryItems
- ===============
- */
- int idPlayer::GetNumInventoryItems() {
- return inventory.items.Num();
- }
- /*
- ===============
- idPlayer::RemoveInventoryItem
- ===============
- */
- void idPlayer::RemoveInventoryItem( const char *name ) {
- //Hack for localization
- if(!idStr::Icmp(name, "Pwr Cell")) {
- name = idLocalization::GetString( "#str_00101056" );
- }
- idDict *item = FindInventoryItem(name);
- if ( item ) {
- RemoveInventoryItem( item );
- }
- }
- /*
- ===============
- idPlayer::RemoveInventoryItem
- ===============
- */
- void idPlayer::RemoveInventoryItem( idDict *item ) {
- inventory.items.Remove( item );
- if(item->GetInt("inv_powercell") && focusUI) {
- //Reset the powercell count
- int powerCellCount = 0;
- for ( int j = 0; j < inventory.items.Num(); j++ ) {
- idDict *item = inventory.items[ j ];
- if(item->GetInt("inv_powercell")) {
- powerCellCount++;
- }
- }
- focusUI->SetStateInt( "powercell_count", powerCellCount );
- }
- delete item;
- }
- /*
- ===============
- idPlayer::GiveItem
- ===============
- */
- void idPlayer::GiveItem( const char *itemname ) {
- idDict args;
- args.Set( "classname", itemname );
- args.Set( "owner", name.c_str() );
- gameLocal.SpawnEntityDef( args );
- }
- /*
- ==================
- idPlayer::SlotForWeapon
- ==================
- */
- int idPlayer::SlotForWeapon( const char *weaponName ) {
- int i;
- for( i = 0; i < MAX_WEAPONS; i++ ) {
- const char *weap = spawnArgs.GetString( va( "def_weapon%d", i ) );
- if ( !idStr::Cmp( weap, weaponName ) ) {
- return i;
- }
- }
- // not found
- return -1;
- }
- /*
- ===============
- idPlayer::Reload
- ===============
- */
- void idPlayer::Reload() {
- if ( spectating || gameLocal.inCinematic || influenceActive ) {
- return;
- }
- if ( common->IsClient() && !IsLocallyControlled() ) {
- return;
- }
- if ( weapon.GetEntity() && weapon.GetEntity()->IsLinked() ) {
- weapon.GetEntity()->Reload();
- }
- }
- /*
- ===============
- idPlayer::NextBestWeapon
- ===============
- */
- void idPlayer::NextBestWeapon() {
- const char *weap;
- int w = MAX_WEAPONS;
- if ( !weaponEnabled ) {
- return;
- }
- while ( w > 0 ) {
- w--;
- if ( w == weapon_flashlight ) {
- continue;
- }
- weap = spawnArgs.GetString( va( "def_weapon%d", w ) );
- if ( !weap[ 0 ] || ( ( inventory.weapons & ( 1 << w ) ) == 0 ) || ( !inventory.HasAmmo( weap, true, this ) ) ) {
- continue;
- }
- if ( !spawnArgs.GetBool( va( "weapon%d_best", w ) ) ) {
- continue;
- }
- //Some weapons will report having ammo but the clip is empty and
- //will not have enough to fill the clip (i.e. Double Barrel Shotgun with 1 round left)
- //We need to skip these weapons because they cannot be used
- if(inventory.HasEmptyClipCannotRefill(weap, this)) {
- continue;
- }
- break;
- }
- idealWeapon = w;
- weaponSwitchTime = gameLocal.time + WEAPON_SWITCH_DELAY;
- UpdateHudWeapon();
- }
- /*
- ===============
- idPlayer::NextWeapon
- ===============
- */
- void idPlayer::NextWeapon() {
- if ( !weaponEnabled || spectating || hiddenWeapon || gameLocal.inCinematic || gameLocal.world->spawnArgs.GetBool( "no_Weapons" ) || health < 0 ) {
- return;
- }
- // check if we have any weapons
- if ( !inventory.weapons ) {
- return;
- }
-
- int w = idealWeapon.Get();
- while( 1 ) {
- w++;
- if ( w >= MAX_WEAPONS ) {
- w = 0;
- }
- if ( w == idealWeapon ) {
- w = weapon_fists;
- break;
- }
- if ( ( inventory.weapons & ( 1 << w ) ) == 0 ) {
- continue;
- }
- const char * weap = spawnArgs.GetString( va( "def_weapon%d", w ) );
- if ( !spawnArgs.GetBool( va( "weapon%d_cycle", w ) ) ) {
- continue;
- }
- if ( !weap[ 0 ] ) {
- continue;
- }
- if ( inventory.HasAmmo( weap, true, this ) || w == weapon_bloodstone ) {
- break;
- }
- }
- if ( ( w != currentWeapon ) && ( w != idealWeapon ) ) {
- idealWeapon = w;
- weaponSwitchTime = gameLocal.time + WEAPON_SWITCH_DELAY;
- UpdateHudWeapon();
- }
- }
- /*
- ===============
- idPlayer::PrevWeapon
- ===============
- */
- void idPlayer::PrevWeapon() {
- if ( !weaponEnabled || spectating || hiddenWeapon || gameLocal.inCinematic || gameLocal.world->spawnArgs.GetBool( "no_Weapons" ) || health < 0 ) {
- return;
- }
- // check if we have any weapons
- if ( !inventory.weapons ) {
- return;
- }
- int w = idealWeapon.Get();
- while( 1 ) {
- w--;
- if ( w < 0 ) {
- w = MAX_WEAPONS - 1;
- }
- if ( w == idealWeapon ) {
- w = weapon_fists;
- break;
- }
- if ( ( inventory.weapons & ( 1 << w ) ) == 0 ) {
- continue;
- }
- const char * weap = spawnArgs.GetString( va( "def_weapon%d", w ) );
- if ( !spawnArgs.GetBool( va( "weapon%d_cycle", w ) ) ) {
- continue;
- }
- if ( !weap[ 0 ] ) {
- continue;
- }
- if ( inventory.HasAmmo( weap, true, this ) || w == weapon_bloodstone ) {
- break;
- }
- }
- if ( ( w != currentWeapon ) && ( w != idealWeapon ) ) {
- idealWeapon = w;
- weaponSwitchTime = gameLocal.time + WEAPON_SWITCH_DELAY;
- UpdateHudWeapon();
- }
- }
- /*
- ===============
- idPlayer::SelectWeapon
- ===============
- */
- void idPlayer::SelectWeapon( int num, bool force ) {
- const char *weap;
- if ( !weaponEnabled || spectating || gameLocal.inCinematic || health < 0 ) {
- return;
- }
- if ( ( num < 0 ) || ( num >= MAX_WEAPONS ) ) {
- return;
- }
- if ( num == weapon_flashlight ) {
- return;
- }
- if ( ( num != weapon_pda ) && gameLocal.world->spawnArgs.GetBool( "no_Weapons" ) ) {
- num = weapon_fists;
- hiddenWeapon ^= 1;
- if ( hiddenWeapon && weapon.GetEntity() ) {
- weapon.GetEntity()->LowerWeapon();
- } else {
- weapon.GetEntity()->RaiseWeapon();
- }
- }
- //Is the weapon a toggle weapon
- WeaponToggle_t* weaponToggle;
- if(weaponToggles.Get(va("weapontoggle%d", num), &weaponToggle)) {
- int weaponToggleIndex = 0;
- //Find the current Weapon in the list
- int currentIndex = -1;
- for(int i = 0; i < weaponToggle->toggleList.Num(); i++) {
- if(weaponToggle->toggleList[i] == idealWeapon) {
- currentIndex = i;
- break;
- }
- }
- if ( currentIndex == -1 ) {
- //Didn't find the current weapon so select the first item
- weaponToggleIndex = weaponToggle->lastUsed;
- } else {
- //Roll to the next available item in the list
- weaponToggleIndex = currentIndex;
- weaponToggleIndex++;
- if(weaponToggleIndex >= weaponToggle->toggleList.Num()) {
- weaponToggleIndex = 0;
- }
- }
- for(int i = 0; i < weaponToggle->toggleList.Num(); i++) {
- int weapNum = weaponToggle->toggleList[weaponToggleIndex];
- //Is it available
- if(inventory.weapons & ( 1 << weapNum)) {
- //Do we have ammo for it
- if ( inventory.HasAmmo( spawnArgs.GetString( va( "def_weapon%d", weapNum ) ), true, this ) || spawnArgs.GetBool( va( "weapon%d_allowempty", weapNum ) ) ) {
- break;
- }
- }
- weaponToggleIndex++;
- if(weaponToggleIndex >= weaponToggle->toggleList.Num()) {
- weaponToggleIndex = 0;
- }
- }
- weaponToggle->lastUsed = weaponToggleIndex;
- num = weaponToggle->toggleList[weaponToggleIndex];
- }
- weap = spawnArgs.GetString( va( "def_weapon%d", num ) );
- if ( !weap[ 0 ] ) {
- gameLocal.Printf( "Invalid weapon\n" );
- return;
- }
- if ( force || ( inventory.weapons & ( 1 << num ) ) ) {
- if ( !inventory.HasAmmo( weap, true, this ) && !spawnArgs.GetBool( va( "weapon%d_allowempty", num ) ) ) {
- return;
- }
- if ( ( previousWeapon >= 0 ) && ( idealWeapon == num ) && ( spawnArgs.GetBool( va( "weapon%d_toggle", num ) ) ) ) {
- weap = spawnArgs.GetString( va( "def_weapon%d", previousWeapon ) );
- if ( !inventory.HasAmmo( weap, true, this ) && !spawnArgs.GetBool( va( "weapon%d_allowempty", previousWeapon ) ) ) {
- return;
- }
- idealWeapon = previousWeapon;
- } else if ( ( weapon_pda >= 0 ) && ( num == weapon_pda ) && ( inventory.pdas.Num() == 0 ) ) {
- ShowTip( spawnArgs.GetString( "text_infoTitle" ), spawnArgs.GetString( "text_noPDA" ), true );
- return;
- } else {
- idealWeapon = num;
- }
- UpdateHudWeapon();
- }
- }
- /*
- =================
- idPlayer::DropWeapon
- =================
- */
- void idPlayer::DropWeapon( bool died ) {
- idVec3 forward, up;
- int inclip, ammoavailable;
- if( died == false ){
- return;
- }
- assert( !common->IsClient() );
-
- if ( spectating || weaponGone || weapon.GetEntity() == NULL ) {
- return;
- }
-
- if ( ( !died && !weapon.GetEntity()->IsReady() ) || weapon.GetEntity()->IsReloading() ) {
- return;
- }
- // ammoavailable is how many shots we can fire
- // inclip is which amount is in clip right now
- ammoavailable = weapon.GetEntity()->AmmoAvailable();
- inclip = weapon.GetEntity()->AmmoInClip();
-
- // don't drop a grenade if we have none left
- if ( !idStr::Icmp( idWeapon::GetAmmoNameForNum( weapon.GetEntity()->GetAmmoType() ), "ammo_grenades" ) && ( ammoavailable - inclip <= 0 ) ) {
- return;
- }
- ammoavailable += inclip;
- // expect an ammo setup that makes sense before doing any dropping
- // ammoavailable is -1 for infinite ammo, and weapons like chainsaw
- // a bad ammo config usually indicates a bad weapon state, so we should not drop
- // used to be an assertion check, but it still happens in edge cases
- if ( ( ammoavailable != -1 ) && ( ammoavailable < 0 ) ) {
- common->DPrintf( "idPlayer::DropWeapon: bad ammo setup\n" );
- return;
- }
- idEntity *item = NULL;
- if ( died ) {
- // ain't gonna throw you no weapon if I'm dead
- item = weapon.GetEntity()->DropItem( vec3_origin, 0, WEAPON_DROP_TIME, died );
- } else {
- viewAngles.ToVectors( &forward, NULL, &up );
- item = weapon.GetEntity()->DropItem( 250.0f * forward + 150.0f * up, 500, WEAPON_DROP_TIME, died );
- }
- if ( !item ) {
- return;
- }
- // set the appropriate ammo in the dropped object
- const idKeyValue * keyval = item->spawnArgs.MatchPrefix( "inv_ammo_" );
- if ( keyval ) {
- item->spawnArgs.SetInt( keyval->GetKey(), ammoavailable );
- idStr inclipKey = keyval->GetKey();
- inclipKey.Insert( "inclip_", 4 );
- inclipKey.Insert( va("%.2d", currentWeapon), 11);
- item->spawnArgs.SetInt( inclipKey, inclip );
- }
- if ( !died ) {
- // remove from our local inventory completely
- inventory.Drop( spawnArgs, item->spawnArgs.GetString( "inv_weapon" ), -1 );
- weapon.GetEntity()->ResetAmmoClip();
- NextWeapon();
- weapon.GetEntity()->WeaponStolen();
- weaponGone = true;
- }
- }
- /*
- =================
- idPlayer::StealWeapon
- steal the target player's current weapon
- =================
- */
- void idPlayer::StealWeapon( idPlayer *player ) {
- assert( !common->IsClient() );
- // make sure there's something to steal
- idWeapon *player_weapon = static_cast< idWeapon * >( player->weapon.GetEntity() );
- if ( !player_weapon || !player_weapon->CanDrop() || weaponGone ) {
- return;
- }
- // steal - we need to effectively force the other player to abandon his weapon
- int newweap = player->currentWeapon;
- if ( newweap == -1 ) {
- return;
- }
- // might be just dropped - check inventory
- if ( ! ( player->inventory.weapons & ( 1 << newweap ) ) ) {
- return;
- }
- const char *weapon_classname = spawnArgs.GetString( va( "def_weapon%d", newweap ) );
- assert( weapon_classname );
- int ammoavailable = player->weapon.GetEntity()->AmmoAvailable();
- int inclip = player->weapon.GetEntity()->AmmoInClip();
- ammoavailable += inclip;
- if ( ( ammoavailable != -1 ) && ( ammoavailable < 0 ) ) {
- // see DropWeapon
- common->DPrintf( "idPlayer::StealWeapon: bad ammo setup\n" );
- // we still steal the weapon, so let's use the default ammo levels
- inclip = -1;
- const idDeclEntityDef *decl = gameLocal.FindEntityDef( weapon_classname );
- assert( decl );
- const idKeyValue *keypair = decl->dict.MatchPrefix( "inv_ammo_" );
- assert( keypair );
- ammoavailable = atoi( keypair->GetValue() );
- }
- player->weapon.GetEntity()->WeaponStolen();
- player->inventory.Drop( player->spawnArgs, NULL, newweap );
- player->SelectWeapon( weapon_fists, false );
- // in case the robbed player is firing rounds with a continuous fire weapon like the chaingun/plasma etc.
- // this will ensure the firing actually stops
- player->weaponGone = true;
- // give weapon, setup the ammo count
- Give( "weapon", weapon_classname, ITEM_GIVE_FEEDBACK | ITEM_GIVE_UPDATE_STATE );
- ammo_t ammo_i = player->inventory.AmmoIndexForWeaponClass( weapon_classname, NULL );
- idealWeapon = newweap;
- const int currentAmmo = inventory.GetInventoryAmmoForType( ammo_i );
- inventory.SetInventoryAmmoForType( ammo_i, currentAmmo + ammoavailable );
- }
- /*
- ===============
- idPlayer::ActiveGui
- ===============
- */
- idUserInterface *idPlayer::ActiveGui() {
- if ( objectiveSystemOpen ) {
- return NULL;
- }
- return focusUI;
- }
- /*
- ===============
- idPlayer::Weapon_Combat
- ===============
- */
- void idPlayer::Weapon_Combat() {
- if ( influenceActive || !weaponEnabled || gameLocal.inCinematic || privateCameraView ) {
- return;
- }
- weapon.GetEntity()->RaiseWeapon();
- if ( weapon.GetEntity()->IsReloading() ) {
- if ( !AI_RELOAD ) {
- AI_RELOAD = true;
- SetState( "ReloadWeapon" );
- UpdateScript();
- }
- } else {
- AI_RELOAD = false;
- }
- if ( idealWeapon == weapon_soulcube && soulCubeProjectile.GetEntity() != NULL ) {
- idealWeapon = currentWeapon;
- }
- if ( idealWeapon != currentWeapon && idealWeapon.Get() < MAX_WEAPONS ) {
- if ( weaponCatchup ) {
- assert( common->IsClient() );
- currentWeapon = idealWeapon.Get();
- weaponGone = false;
- animPrefix = spawnArgs.GetString( va( "def_weapon%d", currentWeapon ) );
- weapon.GetEntity()->GetWeaponDef( animPrefix, inventory.GetClipAmmoForWeapon( currentWeapon ) );
- animPrefix.Strip( "weapon_" );
- weapon.GetEntity()->NetCatchup();
- const function_t *newstate = GetScriptFunction( "NetCatchup" );
- if ( newstate ) {
- SetState( newstate );
- UpdateScript();
- }
- weaponCatchup = false;
- } else {
- if ( weapon.GetEntity()->IsReady() ) {
- weapon.GetEntity()->PutAway();
- }
- if ( weapon.GetEntity()->IsHolstered() ) {
- assert( idealWeapon.Get() >= 0 );
- assert( idealWeapon.Get() < MAX_WEAPONS );
- if ( currentWeapon != weapon_pda && !spawnArgs.GetBool( va( "weapon%d_toggle", currentWeapon ) ) ) {
- previousWeapon = currentWeapon;
- }
- currentWeapon = idealWeapon.Get();
- weaponGone = false;
- animPrefix = spawnArgs.GetString( va( "def_weapon%d", currentWeapon ) );
- weapon.GetEntity()->GetWeaponDef( animPrefix, inventory.GetClipAmmoForWeapon( currentWeapon ) );
- animPrefix.Strip( "weapon_" );
- weapon.GetEntity()->Raise();
- }
- }
- } else {
- weaponGone = false; // if you drop and re-get weap, you may miss the = false above
- if ( weapon.GetEntity()->IsHolstered() ) {
- if ( !weapon.GetEntity()->AmmoAvailable() ) {
- // weapons can switch automatically if they have no more ammo
- NextBestWeapon();
- } else {
- weapon.GetEntity()->Raise();
- state = GetScriptFunction( "RaiseWeapon" );
- if ( state ) {
- SetState( state );
- }
- }
- }
- }
- // check for attack
- AI_WEAPON_FIRED = false;
- if ( !influenceActive ) {
- if ( ( usercmd.buttons & BUTTON_ATTACK ) && !weaponGone ) {
- FireWeapon();
- } else if ( oldButtons & BUTTON_ATTACK ) {
- AI_ATTACK_HELD = false;
- weapon.GetEntity()->EndAttack();
- }
- }
- // update our ammo clip in our inventory
- if ( ( currentWeapon >= 0 ) && ( currentWeapon < MAX_WEAPONS ) ) {
- inventory.SetClipAmmoForWeapon( currentWeapon, weapon.GetEntity()->AmmoInClip() );
- }
- }
- /*
- ===============
- idPlayer::Weapon_NPC
- ===============
- */
- void idPlayer::Weapon_NPC() {
- if ( idealWeapon != currentWeapon ) {
- Weapon_Combat();
- }
- StopFiring();
- weapon.GetEntity()->LowerWeapon();
- bool wasDown = ( oldButtons & (BUTTON_ATTACK|BUTTON_USE) ) != 0;
- bool isDown = ( usercmd.buttons & (BUTTON_ATTACK|BUTTON_USE) ) != 0;
- if ( isDown && !wasDown ) {
- buttonMask |= BUTTON_ATTACK;
- focusCharacter->TalkTo( this );
- }
- }
- /*
- ===============
- idPlayer::LowerWeapon
- ===============
- */
- void idPlayer::LowerWeapon() {
- if ( weapon.GetEntity() && !weapon.GetEntity()->IsHidden() ) {
- weapon.GetEntity()->LowerWeapon();
- }
- }
- /*
- ===============
- idPlayer::RaiseWeapon
- ===============
- */
- void idPlayer::RaiseWeapon() {
- if ( weapon.GetEntity() && weapon.GetEntity()->IsHidden() ) {
- weapon.GetEntity()->RaiseWeapon();
- }
- }
- /*
- ===============
- idPlayer::WeaponLoweringCallback
- ===============
- */
- void idPlayer::WeaponLoweringCallback() {
- SetState( "LowerWeapon" );
- UpdateScript();
- }
- /*
- ===============
- idPlayer::WeaponRisingCallback
- ===============
- */
- void idPlayer::WeaponRisingCallback() {
- SetState( "RaiseWeapon" );
- UpdateScript();
- }
- /*
- ===============
- idPlayer::Weapon_GUI
- ===============
- */
- void idPlayer::Weapon_GUI() {
- if ( !objectiveSystemOpen ) {
- if ( idealWeapon != currentWeapon ) {
- Weapon_Combat();
- }
- StopFiring();
- weapon.GetEntity()->LowerWeapon();
- }
- // disable click prediction for the GUIs. handy to check the state sync does the right thing
- if ( common->IsClient() && !net_clientPredictGUI.GetBool() ) {
- return;
- }
- bool wasDown = ( oldButtons & (BUTTON_ATTACK|BUTTON_USE) ) != 0;
- bool isDown = ( usercmd.buttons & (BUTTON_ATTACK|BUTTON_USE) ) != 0;
- if ( isDown != wasDown ) {
- const char * command = NULL;
- idUserInterface * ui = ActiveGui();
- if ( ui ) {
- bool updateVisuals = false;
- sysEvent_t ev = sys->GenerateMouseButtonEvent( 1, isDown );
- command = ui->HandleEvent( &ev, gameLocal.time, &updateVisuals );
- if ( updateVisuals && focusGUIent && ui == focusUI ) {
- focusGUIent->UpdateVisuals();
- }
- }
- if ( common->IsClient() ) {
- // we predict enough, but don't want to execute commands
- return;
- }
- // HACK - Check to see who is activating the frag chamber. Im sorry.
- if( common->IsMultiplayer() && focusGUIent ) {
- if( strcmp( focusGUIent->GetName(), "chamber_gui_console" ) == 0 && strcmp( command, " ; runScript chamber_trigger" ) == 0 ){
- gameLocal.playerActivateFragChamber = this;
- }
- }
- if ( focusGUIent ) {
- HandleGuiCommands( focusGUIent, command );
- } else {
- HandleGuiCommands( this, command );
- }
- }
- }
- /*
- ===============
- idPlayer::UpdateWeapon
- ===============
- */
- void idPlayer::UpdateWeapon() {
- if ( health <= 0 ) {
- return;
- }
- assert( !spectating );
- if ( common->IsClient() ) {
- // clients need to wait till the weapon and it's world model entity
- // are present and synchronized ( weapon.worldModel idEntityPtr to idAnimatedEntity )
- if ( !weapon.GetEntity()->IsWorldModelReady() ) {
- return;
- }
- }
- // always make sure the weapon is correctly setup before accessing it
- if ( !weapon.GetEntity()->IsLinked() ) {
- if ( idealWeapon != -1 ) {
- animPrefix = spawnArgs.GetString( va( "def_weapon%d", idealWeapon.Get() ) );
- int ammoInClip = inventory.GetClipAmmoForWeapon( idealWeapon.Get() );
- if ( common->IsMultiplayer() && respawning ) {
- // Do not load ammo into the clip here on MP respawn, as it will be done
- // elsewhere. If we take ammo out here then the player will end up losing
- // a clip of ammo for their initial weapon upon respawn.
- ammoInClip = 0;
- }
- weapon.GetEntity()->GetWeaponDef( animPrefix, ammoInClip );
- assert( weapon.GetEntity()->IsLinked() );
- } else {
- return;
- }
- }
- if ( hiddenWeapon && tipUp && usercmd.buttons & BUTTON_ATTACK ) {
- HideTip();
- }
-
- if ( g_dragEntity.GetBool() ) {
- StopFiring();
- weapon.GetEntity()->LowerWeapon();
- dragEntity.Update( this );
- } else if ( ActiveGui() ) {
- // gui handling overrides weapon use
- Weapon_GUI();
- } else if ( focusCharacter && ( focusCharacter->health > 0 ) ) {
- Weapon_NPC();
- } else {
- Weapon_Combat();
- }
-
- if ( hiddenWeapon ) {
- weapon.GetEntity()->LowerWeapon();
- }
- // update weapon state, particles, dlights, etc
- weapon.GetEntity()->PresentWeapon( CanShowWeaponViewmodel() );
- }
- /*
- ===============
- idPlayer::UpdateFlashLight
- ===============
- */
- void idPlayer::UpdateFlashlight() {
- if ( idealWeapon == weapon_flashlight ) {
- // force classic flashlight to go away
- NextWeapon();
- }
- if ( !flashlight.IsValid() ) {
- return;
- }
- if ( !flashlight.GetEntity()->GetOwner() ) {
- return;
- }
- // Don't update the flashlight if dead in MP.
- // Otherwise you can see a floating flashlight worldmodel near player's skeletons.
- if ( common->IsMultiplayer() ) {
- if ( health < 0 ) {
- return;
- }
- }
- // Flashlight has an infinite battery in multiplayer.
- if ( !common->IsMultiplayer() ) {
- if ( flashlight.GetEntity()->lightOn ) {
- if ( flashlight_batteryDrainTimeMS.GetInteger() > 0 ) {
- flashlightBattery -= ( gameLocal.time - gameLocal.previousTime );
- if ( flashlightBattery < 0 ) {
- FlashlightOff();
- flashlightBattery = 0;
- }
- }
- } else {
- if ( flashlightBattery < flashlight_batteryDrainTimeMS.GetInteger() ) {
- flashlightBattery += ( gameLocal.time - gameLocal.previousTime ) * Max( 1, ( flashlight_batteryDrainTimeMS.GetInteger() / flashlight_batteryChargeTimeMS.GetInteger() ) );
- if ( flashlightBattery > flashlight_batteryDrainTimeMS.GetInteger() ) {
- flashlightBattery = flashlight_batteryDrainTimeMS.GetInteger();
- }
- }
- }
- }
- if ( hud ) {
- hud->UpdateFlashlight( this );
- }
- if ( common->IsClient() ) {
- // clients need to wait till the weapon and it's world model entity
- // are present and synchronized ( weapon.worldModel idEntityPtr to idAnimatedEntity )
- if ( !flashlight.GetEntity()->IsWorldModelReady() ) {
- return;
- }
- }
- // always make sure the weapon is correctly setup before accessing it
- if ( !flashlight.GetEntity()->IsLinked() ) {
- flashlight.GetEntity()->GetWeaponDef( "weapon_flashlight_new", 0 );
- flashlight.GetEntity()->SetIsPlayerFlashlight( true );
- // adjust position / orientation of flashlight
- idAnimatedEntity *worldModel = flashlight.GetEntity()->GetWorldModel();
- worldModel->BindToJoint( this, "Chest", true );
- // Don't interpolate the flashlight world model in mp, let it bind like normal.
- worldModel->SetUseClientInterpolation( false );
- assert( flashlight.GetEntity()->IsLinked() );
- }
- // this positions the third person flashlight model! (as seen in the mirror)
- idAnimatedEntity *worldModel = flashlight.GetEntity()->GetWorldModel();
- static const idVec3 fl_pos = idVec3( 3.0f, 9.0f, 2.0f );
- worldModel->GetPhysics()->SetOrigin( fl_pos );
- static float fl_pitch = 0.0f;
- static float fl_yaw = 0.0f;
- static float fl_roll = 0.0f;
- static idAngles ang = ang_zero;
- ang.Set( fl_pitch, fl_yaw, fl_roll );
- worldModel->GetPhysics()->SetAxis( ang.ToMat3() );
- if ( flashlight.GetEntity()->lightOn ) {
- if ( ( flashlightBattery < flashlight_batteryChargeTimeMS.GetInteger() / 2 ) && ( gameLocal.random.RandomFloat() < flashlight_batteryFlickerPercent.GetFloat() ) ) {
- flashlight.GetEntity()->RemoveMuzzleFlashlight();
- } else {
- flashlight.GetEntity()->MuzzleFlashLight();
- }
- }
- flashlight.GetEntity()->PresentWeapon( true );
- if ( gameLocal.world->spawnArgs.GetBool( "no_Weapons" ) || gameLocal.inCinematic || spectating || fl.hidden ) {
- worldModel->Hide();
- } else {
- worldModel->Show();
- }
- }
- /*
- ===============
- idPlayer::FlashlightOn
- ===============
- */
- void idPlayer::FlashlightOn() {
- if ( !flashlight.IsValid() ) {
- return;
- }
- if ( flashlightBattery < idMath::Ftoi( flashlight_minActivatePercent.GetFloat() * flashlight_batteryDrainTimeMS.GetFloat() ) ) {
- return;
- }
- if ( gameLocal.inCinematic ) {
- return;
- }
- if ( flashlight.GetEntity()->lightOn ) {
- return;
- }
- if ( health <= 0 ) {
- return;
- }
- if ( spectating ) {
- return;
- }
- flashlight->FlashlightOn();
- }
- /*
- ===============
- idPlayer::FlashlightOff
- ===============
- */
- void idPlayer::FlashlightOff() {
- if ( !flashlight.IsValid() ) {
- return;
- }
- if ( !flashlight.GetEntity()->lightOn ) {
- return;
- }
- flashlight->FlashlightOff();
- }
- /*
- ===============
- idPlayer::SpectateFreeFly
- ===============
- */
- void idPlayer::SpectateFreeFly( bool force ) {
- idPlayer *player;
- idVec3 newOrig;
- idVec3 spawn_origin;
- idAngles spawn_angles;
- player = gameLocal.GetClientByNum( spectator );
- if ( force || gameLocal.time > lastSpectateChange ) {
- spectator = entityNumber;
- if ( player != NULL && player != this && !player->spectating && !player->IsInTeleport() ) {
- newOrig = player->GetPhysics()->GetOrigin();
- if ( player->physicsObj.IsCrouching() ) {
- newOrig[ 2 ] += pm_crouchviewheight.GetFloat();
- } else {
- newOrig[ 2 ] += pm_normalviewheight.GetFloat();
- }
- newOrig[ 2 ] += SPECTATE_RAISE;
- idBounds b = idBounds( vec3_origin ).Expand( pm_spectatebbox.GetFloat() * 0.5f );
- idVec3 start = player->GetPhysics()->GetOrigin();
- start[2] += pm_spectatebbox.GetFloat() * 0.5f;
- trace_t t;
- // assuming spectate bbox is inside stand or crouch box
- gameLocal.clip.TraceBounds( t, start, newOrig, b, MASK_PLAYERSOLID, player );
- newOrig.Lerp( start, newOrig, t.fraction );
- SetOrigin( newOrig );
- idAngles angle = player->viewAngles;
- angle[ 2 ] = 0;
- SetViewAngles( angle );
- } else {
- SelectInitialSpawnPoint( spawn_origin, spawn_angles );
- spawn_origin[ 2 ] += pm_normalviewheight.GetFloat();
- spawn_origin[ 2 ] += SPECTATE_RAISE;
- SetOrigin( spawn_origin );
- SetViewAngles( spawn_angles );
- // This may happen during GAMESTATE_STARTUP in mp, so we must set the spawnAngles too.
- spawnAngles = spawn_angles;
- if( force == false ) {
- // only do this if they hit the cycle button.
- if ( common->IsServer() ) {
- if( player != NULL ) {
- idBitMsg msg;
- byte msgBuf[MAX_EVENT_PARAM_SIZE];
- msg.InitWrite( msgBuf, sizeof( msgBuf ) );
- msg.WriteFloat( GetPhysics()->GetOrigin()[0] );
- msg.WriteFloat( GetPhysics()->GetOrigin()[1] );
- msg.WriteFloat( GetPhysics()->GetOrigin()[2] );
- msg.WriteFloat( viewAngles[0] );
- msg.WriteFloat( viewAngles[1] );
- msg.WriteFloat( viewAngles[2] );
- ServerSendEvent( idPlayer::EVENT_FORCE_ORIGIN, &msg, false );
- }
- }
- }
- }
- lastSpectateChange = gameLocal.time + 500;
- }
-
- }
- /*
- ===============
- idPlayer::SpectateCycle
- ===============
- */
- void idPlayer::SpectateCycle() {
- idPlayer *player;
- if ( gameLocal.time > lastSpectateChange ) {
- int latchedSpectator = spectator;
- spectator = gameLocal.GetNextClientNum( spectator );
- player = gameLocal.GetClientByNum( spectator );
- assert( player ); // never call here when the current spectator is wrong
- // ignore other spectators
- while ( latchedSpectator != spectator && player->spectating ) {
- spectator = gameLocal.GetNextClientNum( spectator );
- player = gameLocal.GetClientByNum( spectator );
- }
- lastSpectateChange = gameLocal.time + 500;
- }
- }
- /*
- ===============
- idPlayer::UpdateSpectating
- ===============
- */
- void idPlayer::UpdateSpectating() {
- assert( spectating );
- assert( !common->IsClient() );
- assert( IsHidden() );
- idPlayer *player;
- if ( !common->IsMultiplayer() ) {
- return;
- }
- player = gameLocal.GetClientByNum( spectator );
- if ( !player || ( player->spectating && player != this ) ) {
- SpectateFreeFly( true );
- } else if ( usercmd.buttons & BUTTON_JUMP ) {
- SpectateFreeFly( false );
- } else if ( usercmd.buttons & BUTTON_USE ) {
- SpectateCycle();
- } else if ( usercmd.buttons & BUTTON_ATTACK ) {
- wantSpectate = false;
- }
- }
- /*
- ===============
- idPlayer::HandleSingleGuiCommand
- ===============
- */
- bool idPlayer::HandleSingleGuiCommand( idEntity *entityGui, idLexer *src ) {
- idToken token;
- if ( !src->ReadToken( &token ) ) {
- return false;
- }
- if ( token == ";" ) {
- return false;
- }
- if ( token.Icmp( "addhealth" ) == 0 ) {
- if ( entityGui && health < 100 ) {
- int _health = entityGui->spawnArgs.GetInt( "gui_parm1" );
- int amt = ( _health >= HEALTH_PER_DOSE ) ? HEALTH_PER_DOSE : _health;
- _health -= amt;
- entityGui->spawnArgs.SetInt( "gui_parm1", _health );
- if ( entityGui->GetRenderEntity() && entityGui->GetRenderEntity()->gui[ 0 ] ) {
- entityGui->GetRenderEntity()->gui[ 0 ]->SetStateInt( "gui_parm1", _health );
- }
- health += amt;
- if ( health > 100 ) {
- health = 100;
- }
- }
- return true;
- }
- src->UnreadToken( &token );
- return false;
- }
- /*
- ==============
- idPlayer::PlayAudioLog
- ==============
- */
- void idPlayer::PlayAudioLog( const idSoundShader * shader ) {
- EndVideoDisk();
- if ( name.Length() > 0 ) {
- int ms;
- StartSoundShader( shader, SND_CHANNEL_PDA_AUDIO, 0, false, &ms );
- CancelEvents( &EV_Player_StopAudioLog );
- PostEventMS( &EV_Player_StopAudioLog, ms + 150 );
- }
- }
- /*
- ==============
- idPlayer::EndAudioLog
- ==============
- */
- void idPlayer::EndAudioLog() {
- StopSound( SND_CHANNEL_PDA_AUDIO, false );
- }
- /*
- ==============
- idPlayer::PlayVideoDisk
- ==============
- */
- void idPlayer::PlayVideoDisk( const idDeclVideo * decl ) {
- EndAudioLog();
- pdaVideoMat = decl->GetRoq();
- if ( pdaVideoMat ) {
- int c = pdaVideoMat->GetNumStages();
- for ( int i = 0; i < c; i++ ) {
- const shaderStage_t *stage = pdaVideoMat->GetStage( i );
- if ( stage != NULL && stage->texture.cinematic ) {
- stage->texture.cinematic->ResetTime( Sys_Milliseconds() );
- }
- }
- if ( decl->GetWave() != NULL ) {
- StartSoundShader( decl->GetWave(), SND_CHANNEL_PDA_VIDEO, 0, false, NULL );
- }
- }
- }
- /*
- ==============
- idPlayer::EndVideoDisk
- ==============
- */
- void idPlayer::EndVideoDisk() {
- pdaVideoMat = NULL;
- StopSound( SND_CHANNEL_PDA_VIDEO, false );
- }
- /*
- ==============
- idPlayer::Collide
- ==============
- */
- bool idPlayer::Collide( const trace_t &collision, const idVec3 &velocity ) {
- idEntity *other;
- if ( common->IsClient() && spectating == false ) {
- return false;
- }
- other = gameLocal.entities[ collision.c.entityNum ];
- if ( other ) {
- other->Signal( SIG_TOUCH );
- if ( !spectating ) {
- if ( other->RespondsTo( EV_Touch ) ) {
- other->ProcessEvent( &EV_Touch, this, &collision );
- }
- } else {
- if ( other->RespondsTo( EV_SpectatorTouch ) ) {
- other->ProcessEvent( &EV_SpectatorTouch, this, &collision );
- }
- }
- }
- return false;
- }
- /*
- ================
- idPlayer::UpdateLocation
- Searches nearby locations
- ================
- */
- void idPlayer::UpdateLocation() {
- if ( hud ) {
- hud->UpdateLocation( this );
- }
- }
- /*
- ================
- idPlayer::ClearFocus
- Clears the focus cursor
- ================
- */
- void idPlayer::ClearFocus() {
- focusCharacter = NULL;
- focusGUIent = NULL;
- focusUI = NULL;
- focusVehicle = NULL;
- talkCursor = 0;
- }
- /*
- ================
- idPlayer::UpdateFocus
- Searches nearby entities for interactive guis, possibly making one of them
- the focus and sending it a mouse move event
- ================
- */
- void idPlayer::UpdateFocus() {
- idClipModel *clipModelList[ MAX_GENTITIES ];
- idClipModel *clip;
- int listedClipModels;
- idEntity *oldFocus;
- idEntity *ent;
- idUserInterface *oldUI;
- idAI *oldChar;
- int oldTalkCursor;
- idAFEntity_Vehicle *oldVehicle;
- int i, j;
- idVec3 start, end;
- bool allowFocus;
- const char *command;
- trace_t trace;
- guiPoint_t pt;
- const idKeyValue *kv;
- sysEvent_t ev;
- idUserInterface *ui;
- if ( gameLocal.inCinematic ) {
- return;
- }
- // only update the focus character when attack button isn't pressed so players
- // can still chainsaw NPC's
- if ( common->IsMultiplayer() || ( !focusCharacter && ( usercmd.buttons & BUTTON_ATTACK ) ) ) {
- allowFocus = false;
- } else {
- allowFocus = true;
- }
- oldFocus = focusGUIent;
- oldUI = focusUI;
- oldChar = focusCharacter;
- oldTalkCursor = talkCursor;
- oldVehicle = focusVehicle;
- if ( focusTime <= gameLocal.time ) {
- ClearFocus();
- }
- // don't let spectators interact with GUIs
- if ( spectating ) {
- return;
- }
- start = GetEyePosition();
- end = start + firstPersonViewAxis[0] * 80.0f;
- // player identification -> names to the hud
- if ( common->IsMultiplayer() && IsLocallyControlled() ) {
- idVec3 end = start + viewAngles.ToForward() * 768.0f;
- gameLocal.clip.TracePoint( trace, start, end, MASK_SHOT_BOUNDINGBOX, this );
- int iclient = -1;
- if ( ( trace.fraction < 1.0f ) && ( trace.c.entityNum < MAX_CLIENTS ) ) {
- iclient = trace.c.entityNum;
- }
- if ( MPAim != iclient ) {
- lastMPAim = MPAim;
- MPAim = iclient;
- lastMPAimTime = gameLocal.realClientTime;
- }
- }
- idBounds bounds( start );
- bounds.AddPoint( end );
- listedClipModels = gameLocal.clip.ClipModelsTouchingBounds( bounds, -1, clipModelList, MAX_GENTITIES );
- // no pretense at sorting here, just assume that there will only be one active
- // gui within range along the trace
- for ( i = 0; i < listedClipModels; i++ ) {
- clip = clipModelList[ i ];
- ent = clip->GetEntity();
- if ( ent->IsHidden() ) {
- continue;
- }
- if ( allowFocus ) {
- if ( ent->IsType( idAFAttachment::Type ) ) {
- idEntity *body = static_cast<idAFAttachment *>( ent )->GetBody();
- if ( body != NULL && body->IsType( idAI::Type ) && ( static_cast<idAI *>( body )->GetTalkState() >= TALK_OK ) ) {
- gameLocal.clip.TracePoint( trace, start, end, MASK_SHOT_RENDERMODEL, this );
- if ( ( trace.fraction < 1.0f ) && ( trace.c.entityNum == ent->entityNumber ) ) {
- ClearFocus();
- focusCharacter = static_cast<idAI *>( body );
- talkCursor = 1;
- focusTime = gameLocal.time + FOCUS_TIME;
- break;
- }
- }
- continue;
- }
- if ( ent->IsType( idAI::Type ) ) {
- if ( static_cast<idAI *>( ent )->GetTalkState() >= TALK_OK ) {
- gameLocal.clip.TracePoint( trace, start, end, MASK_SHOT_RENDERMODEL, this );
- if ( ( trace.fraction < 1.0f ) && ( trace.c.entityNum == ent->entityNumber ) ) {
- ClearFocus();
- focusCharacter = static_cast<idAI *>( ent );
- talkCursor = 1;
- focusTime = gameLocal.time + FOCUS_TIME;
- break;
- }
- }
- continue;
- }
- if ( ent->IsType( idAFEntity_Vehicle::Type ) ) {
- gameLocal.clip.TracePoint( trace, start, end, MASK_SHOT_RENDERMODEL, this );
- if ( ( trace.fraction < 1.0f ) && ( trace.c.entityNum == ent->entityNumber ) ) {
- ClearFocus();
- focusVehicle = static_cast<idAFEntity_Vehicle *>( ent );
- focusTime = gameLocal.time + FOCUS_TIME;
- break;
- }
- continue;
- }
- }
- if ( !ent->GetRenderEntity() || !ent->GetRenderEntity()->gui[ 0 ] || !ent->GetRenderEntity()->gui[ 0 ]->IsInteractive() ) {
- continue;
- }
- if ( ent->spawnArgs.GetBool( "inv_item" ) ) {
- // don't allow guis on pickup items focus
- continue;
- }
- pt = gameRenderWorld->GuiTrace( ent->GetModelDefHandle(), start, end );
- if ( pt.x != -1 ) {
- // we have a hit
- renderEntity_t *focusGUIrenderEntity = ent->GetRenderEntity();
- if ( !focusGUIrenderEntity ) {
- continue;
- }
- if ( pt.guiId == 1 ) {
- ui = focusGUIrenderEntity->gui[ 0 ];
- } else if ( pt.guiId == 2 ) {
- ui = focusGUIrenderEntity->gui[ 1 ];
- } else {
- ui = focusGUIrenderEntity->gui[ 2 ];
- }
-
- if ( ui == NULL ) {
- continue;
- }
- ClearFocus();
- focusGUIent = ent;
- focusUI = ui;
- if ( oldFocus != ent ) {
- // new activation
- // going to see if we have anything in inventory a gui might be interested in
- // need to enumerate inventory items
- focusUI->SetStateInt( "inv_count", inventory.items.Num() );
- for ( j = 0; j < inventory.items.Num(); j++ ) {
- idDict *item = inventory.items[ j ];
- const char *iname = item->GetString( "inv_name" );
- const char *iicon = item->GetString( "inv_icon" );
- const char *itext = item->GetString( "inv_text" );
- focusUI->SetStateString( va( "inv_name_%i", j), iname );
- focusUI->SetStateString( va( "inv_icon_%i", j), iicon );
- focusUI->SetStateString( va( "inv_text_%i", j), itext );
- kv = item->MatchPrefix("inv_id", NULL);
- if ( kv ) {
- focusUI->SetStateString( va( "inv_id_%i", j ), kv->GetValue() );
- }
- focusUI->SetStateInt( iname, 1 );
- }
- for( j = 0; j < inventory.pdaSecurity.Num(); j++ ) {
- const char *p = inventory.pdaSecurity[ j ];
- if ( p && *p ) {
- focusUI->SetStateInt( p, 1 );
- }
- }
- int powerCellCount = 0;
- for ( j = 0; j < inventory.items.Num(); j++ ) {
- idDict *item = inventory.items[ j ];
- if(item->GetInt("inv_powercell")) {
- powerCellCount++;
- }
- }
- focusUI->SetStateInt( "powercell_count", powerCellCount );
- int staminapercentage = ( int )( 100.0f * stamina / pm_stamina.GetFloat() );
- focusUI->SetStateString( "player_health", va("%i", health ) );
- focusUI->SetStateString( "player_stamina", va( "%i%%", staminapercentage ) );
- focusUI->SetStateString( "player_armor", va( "%i%%", inventory.armor ) );
- kv = focusGUIent->spawnArgs.MatchPrefix( "gui_parm", NULL );
- while ( kv ) {
- focusUI->SetStateString( kv->GetKey(), kv->GetValue() );
- kv = focusGUIent->spawnArgs.MatchPrefix( "gui_parm", kv );
- }
- }
- // clamp the mouse to the corner
- ev = sys->GenerateMouseMoveEvent( -2000, -2000 );
- command = focusUI->HandleEvent( &ev, gameLocal.time );
- HandleGuiCommands( focusGUIent, command );
- // move to an absolute position
- ev = sys->GenerateMouseMoveEvent( pt.x * SCREEN_WIDTH, pt.y * SCREEN_HEIGHT );
- command = focusUI->HandleEvent( &ev, gameLocal.time );
- HandleGuiCommands( focusGUIent, command );
- focusTime = gameLocal.time + FOCUS_GUI_TIME;
- break;
- }
- }
- if ( focusGUIent && focusUI ) {
- if ( !oldFocus || oldFocus != focusGUIent ) {
- command = focusUI->Activate( true, gameLocal.time );
- HandleGuiCommands( focusGUIent, command );
- StartSound( "snd_guienter", SND_CHANNEL_ANY, 0, false, NULL );
- // HideTip();
- // HideObjective();
- }
- } else if ( oldFocus && oldUI ) {
- command = oldUI->Activate( false, gameLocal.time );
- HandleGuiCommands( oldFocus, command );
- StartSound( "snd_guiexit", SND_CHANNEL_ANY, 0, false, NULL );
- }
- if ( hud ) {
- hud->SetCursorState( this, CURSOR_TALK, talkCursor );
- }
- if ( oldChar != focusCharacter && hud ) {
- if ( focusCharacter ) {
- hud->SetCursorText( "#str_02036", focusCharacter->spawnArgs.GetString( "npc_name", "Joe" ) );
- hud->UpdateCursorState();
- } else {
- hud->SetCursorText( "", "" );
- hud->UpdateCursorState();
- }
- }
- }
- /*
- =================
- idPlayer::CrashLand
- Check for hard landings that generate sound events
- =================
- */
- void idPlayer::CrashLand( const idVec3 &oldOrigin, const idVec3 &oldVelocity ) {
- idVec3 origin, velocity;
- idVec3 gravityVector, gravityNormal;
- float delta;
- float hardDelta, fatalDelta, softDelta;
- float dist;
- float vel, acc;
- float t;
- float a, b, c, den;
- waterLevel_t waterLevel;
- bool noDamage;
- AI_SOFTLANDING = false;
- AI_HARDLANDING = false;
- // if the player is not on the ground
- if ( !physicsObj.HasGroundContacts() ) {
- return;
- }
- gravityNormal = physicsObj.GetGravityNormal();
- // if the player wasn't going down
- if ( ( oldVelocity * -gravityNormal ) >= 0.0f ) {
- return;
- }
- waterLevel = physicsObj.GetWaterLevel();
- // never take falling damage if completely underwater
- if ( waterLevel == WATERLEVEL_HEAD ) {
- return;
- }
- // no falling damage if touching a nodamage surface
- noDamage = false;
- for ( int i = 0; i < physicsObj.GetNumContacts(); i++ ) {
- const contactInfo_t &contact = physicsObj.GetContact( i );
- if ( contact.material->GetSurfaceFlags() & SURF_NODAMAGE ) {
- noDamage = true;
- StartSound( "snd_land_hard", SND_CHANNEL_ANY, 0, false, NULL );
- break;
- }
- }
- origin = GetPhysics()->GetOrigin();
- gravityVector = physicsObj.GetGravity();
- // calculate the exact velocity on landing
- dist = ( origin - oldOrigin ) * -gravityNormal;
- vel = oldVelocity * -gravityNormal;
- acc = -gravityVector.Length();
- a = acc / 2.0f;
- b = vel;
- c = -dist;
- den = b * b - 4.0f * a * c;
- if ( den < 0 ) {
- return;
- }
- t = ( -b - idMath::Sqrt( den ) ) / ( 2.0f * a );
- delta = vel + t * acc;
- delta = delta * delta * 0.0001;
- // reduce falling damage if there is standing water
- if ( waterLevel == WATERLEVEL_WAIST ) {
- delta *= 0.25f;
- }
- if ( waterLevel == WATERLEVEL_FEET ) {
- delta *= 0.5f;
- }
- if ( delta < 1.0f ) {
- return;
- }
- // allow falling a bit further for multiplayer
- if ( common->IsMultiplayer() ) {
- fatalDelta = 75.0f;
- hardDelta = 50.0f;
- softDelta = 45.0f;
- } else {
- fatalDelta = 65.0f;
- hardDelta = 45.0f;
- softDelta = 30.0f;
- }
- if ( delta > fatalDelta ) {
- AI_HARDLANDING = true;
- landChange = -32;
- landTime = gameLocal.time;
- if ( !noDamage ) {
- pain_debounce_time = gameLocal.time + pain_delay + 1; // ignore pain since we'll play our landing anim
- Damage( NULL, NULL, idVec3( 0, 0, -1 ), "damage_fatalfall", 1.0f, 0 );
- }
- } else if ( delta > hardDelta ) {
- AI_HARDLANDING = true;
- landChange = -24;
- landTime = gameLocal.time;
- if ( !noDamage ) {
- pain_debounce_time = gameLocal.time + pain_delay + 1; // ignore pain since we'll play our landing anim
- Damage( NULL, NULL, idVec3( 0, 0, -1 ), "damage_hardfall", 1.0f, 0 );
- }
- } else if ( delta > softDelta ) {
- AI_HARDLANDING = true;
- landChange = -16;
- landTime = gameLocal.time;
- if ( !noDamage ) {
- pain_debounce_time = gameLocal.time + pain_delay + 1; // ignore pain since we'll play our landing anim
- Damage( NULL, NULL, idVec3( 0, 0, -1 ), "damage_softfall", 1.0f, 0 );
- }
- } else if ( delta > 7 ) {
- AI_SOFTLANDING = true;
- landChange = -8;
- landTime = gameLocal.time;
- } else if ( delta > 3 ) {
- // just walk on
- }
- }
- /*
- ===============
- idPlayer::BobCycle
- ===============
- */
- void idPlayer::BobCycle( const idVec3 &pushVelocity ) {
- float bobmove;
- int old, deltaTime;
- idVec3 vel, gravityDir, velocity;
- idMat3 viewaxis;
- float bob;
- float delta;
- float speed;
- float f;
- //
- // calculate speed and cycle to be used for
- // all cyclic walking effects
- //
- velocity = physicsObj.GetLinearVelocity() - pushVelocity;
- gravityDir = physicsObj.GetGravityNormal();
- vel = velocity - ( velocity * gravityDir ) * gravityDir;
- xyspeed = vel.LengthFast();
- // do not evaluate the bob for other clients
- // when doing a spectate follow, don't do any weapon bobbing
- if ( common->IsClient() && !IsLocallyControlled() ) {
- viewBobAngles.Zero();
- viewBob.Zero();
- return;
- }
- if ( !physicsObj.HasGroundContacts() || influenceActive == INFLUENCE_LEVEL2 || ( common->IsMultiplayer() && spectating ) ) {
- // airborne
- bobCycle = 0;
- bobFoot = 0;
- bobfracsin = 0;
- } else if ( ( !usercmd.forwardmove && !usercmd.rightmove ) || ( xyspeed <= MIN_BOB_SPEED ) ) {
- // start at beginning of cycle again
- bobCycle = 0;
- bobFoot = 0;
- bobfracsin = 0;
- } else {
- if ( physicsObj.IsCrouching() ) {
- bobmove = pm_crouchbob.GetFloat();
- // ducked characters never play footsteps
- } else {
- // vary the bobbing based on the speed of the player
- bobmove = pm_walkbob.GetFloat() * ( 1.0f - bobFrac ) + pm_runbob.GetFloat() * bobFrac;
- }
- // check for footstep / splash sounds
- old = bobCycle;
- bobCycle = (int)( old + bobmove * ( gameLocal.time - gameLocal.previousTime ) ) & 255;
- bobFoot = ( bobCycle & 128 ) >> 7;
- bobfracsin = idMath::Fabs( sin( ( bobCycle & 127 ) / 127.0 * idMath::PI ) );
- }
- // calculate angles for view bobbing
- viewBobAngles.Zero();
- viewaxis = viewAngles.ToMat3() * physicsObj.GetGravityAxis();
- // add angles based on velocity
- delta = velocity * viewaxis[0];
- viewBobAngles.pitch += delta * pm_runpitch.GetFloat();
-
- delta = velocity * viewaxis[1];
- viewBobAngles.roll -= delta * pm_runroll.GetFloat();
- // add angles based on bob
- // make sure the bob is visible even at low speeds
- speed = xyspeed > 200 ? xyspeed : 200;
- delta = bobfracsin * pm_bobpitch.GetFloat() * speed;
- if ( physicsObj.IsCrouching() ) {
- delta *= 3; // crouching
- }
- viewBobAngles.pitch += delta;
- delta = bobfracsin * pm_bobroll.GetFloat() * speed;
- if ( physicsObj.IsCrouching() ) {
- delta *= 3; // crouching accentuates roll
- }
- if ( bobFoot & 1 ) {
- delta = -delta;
- }
- viewBobAngles.roll += delta;
- // calculate position for view bobbing
- viewBob.Zero();
- if ( physicsObj.HasSteppedUp() ) {
- // check for stepping up before a previous step is completed
- deltaTime = gameLocal.time - stepUpTime;
- if ( deltaTime < STEPUP_TIME ) {
- stepUpDelta = stepUpDelta * ( STEPUP_TIME - deltaTime ) / STEPUP_TIME + physicsObj.GetStepUp();
- } else {
- stepUpDelta = physicsObj.GetStepUp();
- }
- if ( stepUpDelta > 2.0f * pm_stepsize.GetFloat() ) {
- stepUpDelta = 2.0f * pm_stepsize.GetFloat();
- }
- stepUpTime = gameLocal.time;
- }
- idVec3 gravity = physicsObj.GetGravityNormal();
- // if the player stepped up recently
- deltaTime = gameLocal.time - stepUpTime;
- if ( deltaTime < STEPUP_TIME ) {
- viewBob += gravity * ( stepUpDelta * ( STEPUP_TIME - deltaTime ) / STEPUP_TIME );
- }
- // add bob height after any movement smoothing
- bob = bobfracsin * xyspeed * pm_bobup.GetFloat();
- if ( bob > 6 ) {
- bob = 6;
- }
- viewBob[2] += bob;
- // add fall height
- delta = gameLocal.time - landTime;
- if ( delta < LAND_DEFLECT_TIME ) {
- f = delta / LAND_DEFLECT_TIME;
- viewBob -= gravity * ( landChange * f );
- } else if ( delta < LAND_DEFLECT_TIME + LAND_RETURN_TIME ) {
- delta -= LAND_DEFLECT_TIME;
- f = 1.0 - ( delta / LAND_RETURN_TIME );
- viewBob -= gravity * ( landChange * f );
- }
- }
- /*
- ================
- idPlayer::UpdateDeltaViewAngles
- ================
- */
- void idPlayer::UpdateDeltaViewAngles( const idAngles &angles ) {
- // set the delta angle
- idAngles delta;
- for( int i = 0; i < 3; i++ ) {
- delta[ i ] = angles[ i ] - SHORT2ANGLE( usercmd.angles[ i ] );
- }
- SetDeltaViewAngles( delta );
- }
- /*
- ================
- idPlayer::SetViewAngles
- ================
- */
- void idPlayer::SetViewAngles( const idAngles &angles ) {
- UpdateDeltaViewAngles( angles );
- viewAngles = angles;
- }
- /*
- ================
- idPlayer::UpdateViewAngles
- ================
- */
- void idPlayer::UpdateViewAngles() {
- int i;
- idAngles delta;
- if ( !noclip && ( gameLocal.inCinematic || privateCameraView || gameLocal.GetCamera() || influenceActive == INFLUENCE_LEVEL2 || objectiveSystemOpen ) ) {
- // no view changes at all, but we still want to update the deltas or else when
- // we get out of this mode, our view will snap to a kind of random angle
- UpdateDeltaViewAngles( viewAngles );
- return;
- }
- // if dead
- if ( health <= 0 ) {
- if ( pm_thirdPersonDeath.GetBool() ) {
- viewAngles.roll = 0.0f;
- viewAngles.pitch = 30.0f;
- } else {
- viewAngles.roll = 40.0f;
- viewAngles.pitch = -15.0f;
- }
- return;
- }
- //
- // circularly clamp the angles with deltas
- for ( i = 0; i < 3; i++ ) {
- cmdAngles[i] = SHORT2ANGLE( usercmd.angles[i] );
- if ( influenceActive == INFLUENCE_LEVEL3 ) {
- viewAngles[i] += idMath::ClampFloat( -1.0f, 1.0f, idMath::AngleDelta( idMath::AngleNormalize180( SHORT2ANGLE( usercmd.angles[i]) + deltaViewAngles[i] ) , viewAngles[i] ) );
- } else {
- viewAngles[i] = idMath::AngleNormalize180( SHORT2ANGLE( usercmd.angles[i]) + deltaViewAngles[i] );
- }
- }
- if ( !centerView.IsDone( gameLocal.time ) ) {
- viewAngles.pitch = centerView.GetCurrentValue(gameLocal.time);
- }
- // clamp the pitch
- if ( noclip ) {
- if ( viewAngles.pitch > 89.0f ) {
- // don't let the player look down more than 89 degrees while noclipping
- viewAngles.pitch = 89.0f;
- } else if ( viewAngles.pitch < -89.0f ) {
- // don't let the player look up more than 89 degrees while noclipping
- viewAngles.pitch = -89.0f;
- }
- } else if ( mountedObject ) {
- int yaw_min, yaw_max, varc;
- mountedObject->GetAngleRestrictions( yaw_min, yaw_max, varc );
- if ( yaw_min < yaw_max ) {
- viewAngles.yaw = idMath::ClampFloat( yaw_min, yaw_max, viewAngles.yaw );
- } else {
- if ( viewAngles.yaw < 0 ) {
- viewAngles.yaw = idMath::ClampFloat( -180.f, yaw_max, viewAngles.yaw );
- } else {
- viewAngles.yaw = idMath::ClampFloat( yaw_min, 180.f, viewAngles.yaw );
- }
- }
- viewAngles.pitch = idMath::ClampFloat( -varc, varc, viewAngles.pitch );
- } else {
- // don't let the player look up or down more than 90 degrees normally
- const float restrict = 1.0f;
- viewAngles.pitch = std::min( viewAngles.pitch, pm_maxviewpitch.GetFloat() * restrict );
- viewAngles.pitch = std::max( viewAngles.pitch, pm_minviewpitch.GetFloat() * restrict );
- }
- UpdateDeltaViewAngles( viewAngles );
- // orient the model towards the direction we're looking
- SetAngles( idAngles( 0, viewAngles.yaw, 0 ) );
- // save in the log for analyzing weapon angle offsets
- loggedViewAngles[ gameLocal.framenum & (NUM_LOGGED_VIEW_ANGLES-1) ] = viewAngles;
- }
- /*
- ==============
- idPlayer::AdjustHeartRate
- Player heartrate works as follows
- DEF_HEARTRATE is resting heartrate
- Taking damage when health is above 75 adjusts heart rate by 1 beat per second
- Taking damage when health is below 75 adjusts heart rate by 5 beats per second
- Maximum heartrate from damage is MAX_HEARTRATE
- Firing a weapon adds 1 beat per second up to a maximum of COMBAT_HEARTRATE
- Being at less than 25% stamina adds 5 beats per second up to ZEROSTAMINA_HEARTRATE
- All heartrates are target rates.. the heart rate will start falling as soon as there have been no adjustments for 5 seconds
- Once it starts falling it always tries to get to DEF_HEARTRATE
- The exception to the above rule is upon death at which point the rate is set to DYING_HEARTRATE and starts falling
- immediately to zero
- Heart rate volumes go from zero ( -40 db for DEF_HEARTRATE to 5 db for MAX_HEARTRATE ) the volume is
- scaled linearly based on the actual rate
- Exception to the above rule is once the player is dead, the dying heart rate starts at either the current volume if
- it is audible or -10db and scales to 8db on the last few beats
- ==============
- */
- void idPlayer::AdjustHeartRate( int target, float timeInSecs, float delay, bool force ) {
- if ( heartInfo.GetEndValue() == target ) {
- return;
- }
- if ( AI_DEAD && !force ) {
- return;
- }
- lastHeartAdjust = gameLocal.time;
- heartInfo.Init( gameLocal.time + delay * 1000, timeInSecs * 1000, heartRate, target );
- }
- /*
- ==============
- idPlayer::GetBaseHeartRate
- ==============
- */
- int idPlayer::GetBaseHeartRate() {
- int base = idMath::Ftoi( ( BASE_HEARTRATE + LOWHEALTH_HEARTRATE_ADJ ) - ( (float)health / 100.0f ) * LOWHEALTH_HEARTRATE_ADJ );
- int rate = idMath::Ftoi( base + ( ZEROSTAMINA_HEARTRATE - base ) * ( 1.0f - stamina / pm_stamina.GetFloat() ) );
- int diff = ( lastDmgTime ) ? gameLocal.time - lastDmgTime : 99999;
- rate += ( diff < 5000 ) ? ( diff < 2500 ) ? ( diff < 1000 ) ? 15 : 10 : 5 : 0;
- return rate;
- }
- /*
- ==============
- idPlayer::SetCurrentHeartRate
- ==============
- */
- void idPlayer::SetCurrentHeartRate() {
- int base = idMath::Ftoi( ( BASE_HEARTRATE + LOWHEALTH_HEARTRATE_ADJ ) - ( (float) health / 100.0f ) * LOWHEALTH_HEARTRATE_ADJ );
- if ( PowerUpActive( ADRENALINE )) {
- heartRate = 135;
- } else {
- heartRate = idMath::Ftoi( heartInfo.GetCurrentValue( gameLocal.time ) );
- int currentRate = GetBaseHeartRate();
- if ( health >= 0 && gameLocal.time > lastHeartAdjust + 2500 ) {
- AdjustHeartRate( currentRate, 2.5f, 0.0f, false );
- }
- }
- int bps = idMath::Ftoi( 60.0f / heartRate * 1000.0f );
- if ( gameLocal.time - lastHeartBeat > bps ) {
- int dmgVol = DMG_VOLUME;
- int deathVol = DEATH_VOLUME;
- int zeroVol = ZERO_VOLUME;
- float pct = 0.0;
- if ( heartRate > BASE_HEARTRATE && health > 0 ) {
- pct = (float)(heartRate - base) / (MAX_HEARTRATE - base);
- pct *= ((float)dmgVol - (float)zeroVol);
- } else if ( health <= 0 ) {
- pct = (float)(heartRate - DYING_HEARTRATE) / (BASE_HEARTRATE - DYING_HEARTRATE);
- if ( pct > 1.0f ) {
- pct = 1.0f;
- } else if (pct < 0.0f) {
- pct = 0.0f;
- }
- pct *= ((float)deathVol - (float)zeroVol);
- }
- pct += (float)zeroVol;
- if ( pct != zeroVol ) {
- StartSound( "snd_heartbeat", SND_CHANNEL_HEART, SSF_PRIVATE_SOUND, false, NULL );
- // modify just this channel to a custom volume
- soundShaderParms_t parms;
- memset( &parms, 0, sizeof( parms ) );
- parms.volume = pct;
- refSound.referenceSound->ModifySound( SND_CHANNEL_HEART, &parms );
- }
- lastHeartBeat = gameLocal.time;
- }
- }
- /*
- ==============
- idPlayer::UpdateAir
- ==============
- */
- void idPlayer::UpdateAir() {
- if ( health <= 0 ) {
- return;
- }
- // see if the player is connected to the info_vacuum
- bool newAirless = false;
- if ( gameLocal.vacuumAreaNum != -1 ) {
- int num = GetNumPVSAreas();
- if ( num > 0 ) {
- int areaNum;
- // if the player box spans multiple areas, get the area from the origin point instead,
- // otherwise a rotating player box may poke into an outside area
- if ( num == 1 ) {
- const int *pvsAreas = GetPVSAreas();
- areaNum = pvsAreas[0];
- } else {
- areaNum = gameRenderWorld->PointInArea( this->GetPhysics()->GetOrigin() );
- }
- newAirless = gameRenderWorld->AreasAreConnected( gameLocal.vacuumAreaNum, areaNum, PS_BLOCK_AIR );
- }
- }
- if ( PowerUpActive( ENVIROTIME ) ) {
- newAirless = false;
- }
- if ( newAirless ) {
- if ( !airless ) {
- StartSound( "snd_decompress", SND_CHANNEL_ANY, SSF_GLOBAL, false, NULL );
- StartSound( "snd_noAir", SND_CHANNEL_BODY2, 0, false, NULL );
- }
- airMsec -= ( gameLocal.time - gameLocal.previousTime );
- if ( airMsec < 0 ) {
- airMsec = 0;
- // check for damage
- const idDict *damageDef = gameLocal.FindEntityDefDict( "damage_noair", false );
- int dmgTiming = 1000 * ((damageDef) ? damageDef->GetFloat( "delay", "3.0" ) : 3.0f );
- if ( gameLocal.time > lastAirDamage + dmgTiming ) {
- Damage( NULL, NULL, vec3_origin, "damage_noair", 1.0f, 0 );
- lastAirDamage = gameLocal.time;
- }
- }
-
- } else {
- if ( airless ) {
- StartSound( "snd_recompress", SND_CHANNEL_ANY, SSF_GLOBAL, false, NULL );
- StopSound( SND_CHANNEL_BODY2, false );
- }
- airMsec += ( gameLocal.time - gameLocal.previousTime ); // regain twice as fast as lose
- if ( airMsec > pm_airMsec.GetInteger() ) {
- airMsec = pm_airMsec.GetInteger();
- }
- }
- airless = newAirless;
- if ( hud ) {
- hud->UpdateOxygen( airless, 100 * airMsec / pm_airMsec.GetInteger() );
- }
- }
- void idPlayer::UpdatePowerupHud() {
-
- if ( health <= 0 ) {
- return;
- }
- if( lastHudPowerup != hudPowerup ) {
-
- if( hudPowerup == -1 ) {
- //The powerup hud should be turned off
- // TODO_SPARTY: powerups??
- //if ( hud ) {
- // hud->HandleNamedEvent( "noPowerup" );
- //}
- } else {
- //Turn the pwoerup hud on
- // TODO_SPARTY: powerups??
- //if ( hud ) {
- // hud->HandleNamedEvent( "Powerup" );
- //}
- }
- lastHudPowerup = hudPowerup;
- }
- if ( hudPowerup != -1 && hudPowerup < MAX_POWERUPS ) {
- if ( PowerUpActive( hudPowerup ) ) {
- //int remaining = inventory.powerupEndTime[ hudPowerup ] - gameLocal.time;
- //int filledbar = idMath::ClampInt( 0, hudPowerupDuration, remaining );
-
- // TODO_SPARTY: powerups??
- //if ( hud ) {
- // hud->SetStateInt( "player_powerup", 100 * filledbar / hudPowerupDuration );
- // hud->SetStateInt( "player_poweruptime", remaining / 1000 );
- //}
- }
- }
- }
- /*
- ==============
- idPlayer::GetPDA
- ==============
- */
- const idDeclPDA * idPlayer::GetPDA() const {
- if ( inventory.pdas.Num() > 0 ) {
- return inventory.pdas[ 0 ];
- } else {
- return NULL;
- }
- }
- /*
- ==============
- idPlayer::GetVideo
- ==============
- */
- const idDeclVideo *idPlayer::GetVideo( int index ) {
- if ( index >= 0 && index < inventory.videos.Num() ) {
- return inventory.videos[index];
- }
- return NULL;
- }
- /*
- ==============
- idPlayer::TogglePDA
- ==============
- */
- void idPlayer::TogglePDA() {
- if ( inventory.pdas.Num() == 0 ) {
- ShowTip( spawnArgs.GetString( "text_infoTitle" ), spawnArgs.GetString( "text_noPDA" ), true );
- return;
- }
- if ( pdaMenu != NULL ) {
- objectiveSystemOpen = !objectiveSystemOpen;
- pdaMenu->ActivateMenu( objectiveSystemOpen );
-
- if ( objectiveSystemOpen ) {
- if ( hud ) {
- hud->ClearNewPDAInfo();
- }
- }
- }
- }
- /*
- ==============
- idPlayer::Spectate
- ==============
- */
- void idPlayer::Spectate( bool spectate, bool force ) {
- spectating = spectate;
- if ( spectating ) {
- // join the spectators
- ClearPowerUps();
- spectator = this->entityNumber;
- Init();
- StopRagdoll();
- SetPhysics( &physicsObj );
- physicsObj.DisableClip();
- FlashlightOff();
- Hide();
- Event_DisableWeapon();
- // Raise me up by a little bit. if i'm the local client.
- if( IsLocallyControlled() ) {
- SetSpectateOrigin();
- }
- HideRespawnHudMessage();
- idLib::Printf( "DMP _ GENERAL :> Player %d Spectating \n", entityNumber );
- } else {
- // put everything back together again
- currentWeapon = -1; // to make sure the def will be loaded if necessary
- Show();
- Event_EnableWeapon();
- idLib::Printf( "DMP _ GENERAL :> Player %d Not Spectating \n", entityNumber );
- SetEyeHeight( pm_normalviewheight.GetFloat() );
- }
- SetClipModel();
- }
- /*
- ==============
- idPlayer::SetClipModel
- ==============
- */
- void idPlayer::SetClipModel() {
- idBounds bounds;
- if ( spectating ) {
- bounds = idBounds( vec3_origin ).Expand( pm_spectatebbox.GetFloat() * 0.5f );
- } else {
- bounds[0].Set( -pm_bboxwidth.GetFloat() * 0.5f, -pm_bboxwidth.GetFloat() * 0.5f, 0 );
- bounds[1].Set( pm_bboxwidth.GetFloat() * 0.5f, pm_bboxwidth.GetFloat() * 0.5f, pm_normalheight.GetFloat() );
- }
- // the origin of the clip model needs to be set before calling SetClipModel
- // otherwise our physics object's current origin value gets reset to 0
- idClipModel *newClip;
- if ( pm_usecylinder.GetBool() ) {
- newClip = new (TAG_PHYSICS_CLIP_ENTITY) idClipModel( idTraceModel( bounds, 8 ) );
- newClip->Translate( physicsObj.PlayerGetOrigin() );
- physicsObj.SetClipModel( newClip, 1.0f );
- } else {
- newClip = new (TAG_PHYSICS_CLIP_ENTITY) idClipModel( idTraceModel( bounds ) );
- newClip->Translate( physicsObj.PlayerGetOrigin() );
- physicsObj.SetClipModel( newClip, 1.0f );
- }
- }
- /*
- ==============
- idPlayer::UseVehicle
- ==============
- */
- void idPlayer::UseVehicle() {
- trace_t trace;
- idVec3 start, end;
- idEntity *ent;
- if ( GetBindMaster() && GetBindMaster()->IsType( idAFEntity_Vehicle::Type ) ) {
- Show();
- static_cast<idAFEntity_Vehicle*>(GetBindMaster())->Use( this );
- } else {
- start = GetEyePosition();
- end = start + viewAngles.ToForward() * 80.0f;
- gameLocal.clip.TracePoint( trace, start, end, MASK_SHOT_RENDERMODEL, this );
- if ( trace.fraction < 1.0f ) {
- ent = gameLocal.entities[ trace.c.entityNum ];
- if ( ent && ent->IsType( idAFEntity_Vehicle::Type ) ) {
- Hide();
- static_cast<idAFEntity_Vehicle*>(ent)->Use( this );
- }
- }
- }
- }
- /*
- ==============
- idPlayer::PerformImpulse
- ==============
- */
- void idPlayer::PerformImpulse( int impulse ) {
- bool isIntroMap = ( idStr::FindText( gameLocal.GetMapFileName(), "mars_city1" ) >= 0 );
- // Normal 1 - 0 Keys.
- if ( impulse >= IMPULSE_0 && impulse <= IMPULSE_12 && !isIntroMap ) {
- SelectWeapon( impulse, false );
- return;
- }
- // DPAD Weapon Quick Select
- if ( impulse >= IMPULSE_28 && impulse <= IMPULSE_31 && !isIntroMap ) {
- SelectWeapon( impulse, false );
- return;
- }
- switch( impulse ) {
- case IMPULSE_13: {
- Reload();
- break;
- }
- case IMPULSE_14: {
- if ( !isIntroMap ) {
- NextWeapon();
- }
- break;
- }
- case IMPULSE_15: {
- if ( !isIntroMap ) {
- PrevWeapon();
- }
- break;
- }
- case IMPULSE_16: {
- if( flashlight.IsValid() ) {
- if ( flashlight.GetEntity()->lightOn ) {
- FlashlightOff();
- } else if ( !spectating && weaponEnabled && !hiddenWeapon && !gameLocal.world->spawnArgs.GetBool( "no_Weapons" ) ) {
- FlashlightOn();
- }
- }
- break;
- }
- case IMPULSE_19: {
- // when we're not in single player, IMPULSE_19 is used for showScores
- // otherwise it opens the pda
- if ( !common->IsMultiplayer() ) {
- #if !defined(ID_RETAIL) && !defined(ID_RETAIL_INTERNAL)
- if ( !common->KeyState( 56 ) ) { // don't toggle PDA when LEFT ALT is down
- #endif
- if ( objectiveSystemOpen ) {
- TogglePDA();
- } else if ( weapon_pda >= 0 ) {
- SelectWeapon( weapon_pda, true );
- }
- #if !defined(ID_RETAIL) && !defined(ID_RETAIL_INTERNAL)
- }
- #endif
- } else {
- if ( IsLocallyControlled() ) {
- gameLocal.mpGame.SetScoreboardActive( true );
- }
- }
- break;
- }
- case IMPULSE_22: {
- if ( common->IsClient() || IsLocallyControlled() ) {
- gameLocal.mpGame.ToggleSpectate();
- }
- break;
- }
- case IMPULSE_25: {
- if ( common->IsServer() && gameLocal.mpGame.IsGametypeFlagBased() && (gameLocal.serverInfo.GetInt( "si_midnight" ) == 2) ) {
- if ( enviroSuitLight.IsValid() ) {
- enviroSuitLight.GetEntity()->PostEventMS( &EV_Remove, 0 );
- enviroSuitLight = NULL;
- } else {
- const idDict *lightDef = gameLocal.FindEntityDefDict( "envirosuit_light", false );
- if ( lightDef ) {
- idEntity *temp = static_cast<idEntity *>(enviroSuitLight.GetEntity());
- idAngles lightAng = firstPersonViewAxis.ToAngles();
- idVec3 lightOrg = firstPersonViewOrigin;
- idVec3 enviroOffset = lightDef->GetVector( "enviro_offset" );
- idVec3 enviroAngleOffset = lightDef->GetVector( "enviro_angle_offset" );
- gameLocal.SpawnEntityDef( *lightDef, &temp, false );
- enviroSuitLight = static_cast<idLight *>(temp);
- enviroSuitLight.GetEntity()->fl.networkSync = true;
- lightOrg += (enviroOffset.x * firstPersonViewAxis[0]);
- lightOrg += (enviroOffset.y * firstPersonViewAxis[1]);
- lightOrg += (enviroOffset.z * firstPersonViewAxis[2]);
- lightAng.pitch += enviroAngleOffset.x;
- lightAng.yaw += enviroAngleOffset.y;
- lightAng.roll += enviroAngleOffset.z;
- enviroSuitLight.GetEntity()->GetPhysics()->SetOrigin( lightOrg );
- enviroSuitLight.GetEntity()->GetPhysics()->SetAxis( lightAng.ToMat3() );
- enviroSuitLight.GetEntity()->UpdateVisuals();
- enviroSuitLight.GetEntity()->Present();
- }
- }
- }
- break;
- }
- //Hack so the chainsaw will work in MP
- case IMPULSE_27: {
- SelectWeapon(18, false);
- break;
- }
- }
- }
- /*
- ==============
- idPlayer::EvaluateControls
- ==============
- */
- void idPlayer::EvaluateControls() {
- // check for respawning
- if ( health <= 0 && !g_testDeath.GetBool() ) {
- if ( common->IsMultiplayer() ) {
- // in MP, idMultiplayerGame decides spawns
- if ( ( gameLocal.time > minRespawnTime ) && ( usercmd.buttons & BUTTON_ATTACK ) ) {
- forceRespawn = true;
- } else if ( gameLocal.time > maxRespawnTime ) {
- forceRespawn = true;
- }
- } else {
- // in single player, we let the session handle restarting the level or loading a game
- if ( gameLocal.time > minRespawnTime ) {
- gameLocal.sessionCommand = "died";
- }
- }
- }
- if ( usercmd.impulseSequence != oldImpulseSequence ) {
- PerformImpulse( usercmd.impulse );
- }
- if ( forceScoreBoard ) {
- gameLocal.mpGame.SetScoreboardActive( true );
- }
- oldImpulseSequence = usercmd.impulseSequence;
- AdjustSpeed();
- // update the viewangles
- UpdateViewAngles();
- }
- /*
- ==============
- idPlayer::AdjustSpeed
- ==============
- */
- void idPlayer::AdjustSpeed() {
- float speed;
- float rate;
- if ( spectating ) {
- speed = pm_spectatespeed.GetFloat();
- bobFrac = 0.0f;
- } else if ( noclip ) {
- speed = pm_noclipspeed.GetFloat();
- bobFrac = 0.0f;
- } else if ( !physicsObj.OnLadder() && ( usercmd.buttons & BUTTON_RUN ) && ( usercmd.forwardmove || usercmd.rightmove ) && !( usercmd.buttons & BUTTON_CROUCH ) ) {
- if ( !common->IsMultiplayer() && !physicsObj.IsCrouching() && !PowerUpActive( ADRENALINE ) ) {
- stamina -= MS2SEC( gameLocal.time - gameLocal.previousTime );
- }
- if ( stamina < 0 ) {
- stamina = 0;
- }
- if ( ( !pm_stamina.GetFloat() ) || ( stamina > pm_staminathreshold.GetFloat() ) ) {
- bobFrac = 1.0f;
- } else if ( pm_staminathreshold.GetFloat() <= 0.0001f ) {
- bobFrac = 0.0f;
- } else {
- bobFrac = stamina / pm_staminathreshold.GetFloat();
- }
- speed = pm_walkspeed.GetFloat() * ( 1.0f - bobFrac ) + pm_runspeed.GetFloat() * bobFrac;
- } else {
- rate = pm_staminarate.GetFloat();
-
- // increase 25% faster when not moving
- if ( ( usercmd.forwardmove == 0 ) && ( usercmd.rightmove == 0 ) && ( !physicsObj.OnLadder() || ( ( usercmd.buttons & (BUTTON_CROUCH|BUTTON_JUMP) ) == 0 ) ) ) {
- rate *= 1.25f;
- }
- stamina += rate * MS2SEC( gameLocal.time - gameLocal.previousTime );
- if ( stamina > pm_stamina.GetFloat() ) {
- stamina = pm_stamina.GetFloat();
- }
- speed = pm_walkspeed.GetFloat();
- bobFrac = 0.0f;
- }
- speed *= PowerUpModifier(SPEED);
- if ( influenceActive == INFLUENCE_LEVEL3 ) {
- speed *= 0.33f;
- }
- physicsObj.SetSpeed( speed, pm_crouchspeed.GetFloat() );
- }
- /*
- ==============
- idPlayer::AdjustBodyAngles
- ==============
- */
- void idPlayer::AdjustBodyAngles() {
- idMat3 lookAxis;
- idMat3 legsAxis;
- bool blend;
- float diff;
- float frac;
- float upBlend;
- float forwardBlend;
- float downBlend;
- if ( health < 0 ) {
- return;
- }
- blend = true;
- if ( !physicsObj.HasGroundContacts() ) {
- idealLegsYaw = 0.0f;
- legsForward = true;
- } else if ( usercmd.forwardmove < 0 ) {
- idealLegsYaw = idMath::AngleNormalize180( idVec3( -usercmd.forwardmove, usercmd.rightmove, 0.0f ).ToYaw() );
- legsForward = false;
- } else if ( usercmd.forwardmove > 0 ) {
- idealLegsYaw = idMath::AngleNormalize180( idVec3( usercmd.forwardmove, -usercmd.rightmove, 0.0f ).ToYaw() );
- legsForward = true;
- } else if ( ( usercmd.rightmove != 0 ) && physicsObj.IsCrouching() ) {
- if ( !legsForward ) {
- idealLegsYaw = idMath::AngleNormalize180( idVec3( idMath::Abs( usercmd.rightmove ), usercmd.rightmove, 0.0f ).ToYaw() );
- } else {
- idealLegsYaw = idMath::AngleNormalize180( idVec3( idMath::Abs( usercmd.rightmove ), -usercmd.rightmove, 0.0f ).ToYaw() );
- }
- } else if ( usercmd.rightmove != 0 ) {
- idealLegsYaw = 0.0f;
- legsForward = true;
- } else {
- legsForward = true;
- diff = idMath::Fabs( idealLegsYaw - legsYaw );
- idealLegsYaw = idealLegsYaw - idMath::AngleNormalize180( viewAngles.yaw - oldViewYaw );
- if ( diff < 0.1f ) {
- legsYaw = idealLegsYaw;
- blend = false;
- }
- }
- if ( !physicsObj.IsCrouching() ) {
- legsForward = true;
- }
- oldViewYaw = viewAngles.yaw;
- AI_TURN_LEFT = false;
- AI_TURN_RIGHT = false;
- if ( idealLegsYaw < -45.0f ) {
- idealLegsYaw = 0;
- AI_TURN_RIGHT = true;
- blend = true;
- } else if ( idealLegsYaw > 45.0f ) {
- idealLegsYaw = 0;
- AI_TURN_LEFT = true;
- blend = true;
- }
- if ( blend ) {
- legsYaw = legsYaw * 0.9f + idealLegsYaw * 0.1f;
- }
- legsAxis = idAngles( 0.0f, legsYaw, 0.0f ).ToMat3();
- animator.SetJointAxis( hipJoint, JOINTMOD_WORLD, legsAxis );
- // calculate the blending between down, straight, and up
- frac = viewAngles.pitch / 90.0f;
- if ( frac > 0.0f ) {
- downBlend = frac;
- forwardBlend = 1.0f - frac;
- upBlend = 0.0f;
- } else {
- downBlend = 0.0f;
- forwardBlend = 1.0f + frac;
- upBlend = -frac;
- }
- animator.CurrentAnim( ANIMCHANNEL_TORSO )->SetSyncedAnimWeight( 0, downBlend );
- animator.CurrentAnim( ANIMCHANNEL_TORSO )->SetSyncedAnimWeight( 1, forwardBlend );
- animator.CurrentAnim( ANIMCHANNEL_TORSO )->SetSyncedAnimWeight( 2, upBlend );
- animator.CurrentAnim( ANIMCHANNEL_LEGS )->SetSyncedAnimWeight( 0, downBlend );
- animator.CurrentAnim( ANIMCHANNEL_LEGS )->SetSyncedAnimWeight( 1, forwardBlend );
- animator.CurrentAnim( ANIMCHANNEL_LEGS )->SetSyncedAnimWeight( 2, upBlend );
- }
- /*
- ==============
- idPlayer::InitAASLocation
- ==============
- */
- void idPlayer::InitAASLocation() {
- int i;
- int num;
- idVec3 size;
- idBounds bounds;
- idAAS *aas;
- idVec3 origin;
- GetFloorPos( 64.0f, origin );
- num = gameLocal.NumAAS();
- aasLocation.SetGranularity( 1 );
- aasLocation.SetNum( num );
- for( i = 0; i < aasLocation.Num(); i++ ) {
- aasLocation[ i ].areaNum = 0;
- aasLocation[ i ].pos = origin;
- aas = gameLocal.GetAAS( i );
- if ( aas != NULL && aas->GetSettings() ) {
- size = aas->GetSettings()->boundingBoxes[0][1];
- bounds[0] = -size;
- size.z = 32.0f;
- bounds[1] = size;
- aasLocation[ i ].areaNum = aas->PointReachableAreaNum( origin, bounds, AREA_REACHABLE_WALK );
- }
- }
- }
- /*
- ==============
- idPlayer::SetAASLocation
- ==============
- */
- void idPlayer::SetAASLocation() {
- int i;
- int areaNum;
- idVec3 size;
- idBounds bounds;
- idAAS *aas;
- idVec3 origin;
- if ( !GetFloorPos( 64.0f, origin ) ) {
- return;
- }
-
- for( i = 0; i < aasLocation.Num(); i++ ) {
- aas = gameLocal.GetAAS( i );
- if ( !aas ) {
- continue;
- }
- size = aas->GetSettings()->boundingBoxes[0][1];
- bounds[0] = -size;
- size.z = 32.0f;
- bounds[1] = size;
- areaNum = aas->PointReachableAreaNum( origin, bounds, AREA_REACHABLE_WALK );
- if ( areaNum ) {
- aasLocation[ i ].pos = origin;
- aasLocation[ i ].areaNum = areaNum;
- }
- }
- }
- /*
- ==============
- idPlayer::GetAASLocation
- ==============
- */
- void idPlayer::GetAASLocation( idAAS *aas, idVec3 &pos, int &areaNum ) const {
- int i;
- if ( aas != NULL ) {
- for( i = 0; i < aasLocation.Num(); i++ ) {
- if ( aas == gameLocal.GetAAS( i ) ) {
- areaNum = aasLocation[ i ].areaNum;
- pos = aasLocation[ i ].pos;
- return;
- }
- }
- }
- areaNum = 0;
- pos = physicsObj.GetOrigin();
- }
- /*
- ==============
- idPlayer::Move_Interpolated
- ==============
- */
- void idPlayer::Move_Interpolated( float fraction ) {
- float newEyeOffset;
- idVec3 oldOrigin;
- idVec3 oldVelocity;
- idVec3 pushVelocity;
- // save old origin and velocity for crashlanding
- oldOrigin = physicsObj.GetOrigin();
- oldVelocity = physicsObj.GetLinearVelocity();
- pushVelocity = physicsObj.GetPushedLinearVelocity();
- // set physics variables
- physicsObj.SetMaxStepHeight( pm_stepsize.GetFloat() );
- physicsObj.SetMaxJumpHeight( pm_jumpheight.GetFloat() );
- if ( noclip ) {
- physicsObj.SetContents( 0 );
- physicsObj.SetMovementType( PM_NOCLIP );
- } else if ( spectating ) {
- physicsObj.SetContents( 0 );
- physicsObj.SetMovementType( PM_SPECTATOR );
- } else if ( health <= 0 ) {
- physicsObj.SetContents( CONTENTS_CORPSE | CONTENTS_MONSTERCLIP );
- physicsObj.SetMovementType( PM_DEAD );
- } else if ( gameLocal.inCinematic || gameLocal.GetCamera() || privateCameraView || ( influenceActive == INFLUENCE_LEVEL2 ) ) {
- physicsObj.SetContents( CONTENTS_BODY );
- physicsObj.SetMovementType( PM_FREEZE );
- } else if ( mountedObject ) {
- physicsObj.SetContents( 0 );
- physicsObj.SetMovementType( PM_FREEZE );
- } else {
- physicsObj.SetContents( CONTENTS_BODY );
- physicsObj.SetMovementType( PM_NORMAL );
- }
- if ( spectating ) {
- physicsObj.SetClipMask( MASK_DEADSOLID );
- } else if ( health <= 0 ) {
- physicsObj.SetClipMask( MASK_DEADSOLID );
- } else {
- physicsObj.SetClipMask( MASK_PLAYERSOLID );
- }
- physicsObj.SetDebugLevel( g_debugMove.GetBool() );
- {
- idVec3 org;
- idMat3 axis;
- GetViewPos( org, axis );
- physicsObj.SetPlayerInput( usercmd, axis[0] );
- }
- // FIXME: physics gets disabled somehow
- BecomeActive( TH_PHYSICS );
- InterpolatePhysics( fraction );
- // update our last valid AAS location for the AI
- SetAASLocation();
- if ( spectating ) {
- newEyeOffset = 0.0f;
- } else if ( health <= 0 ) {
- newEyeOffset = pm_deadviewheight.GetFloat();
- } else if ( physicsObj.IsCrouching() ) {
- newEyeOffset = pm_crouchviewheight.GetFloat();
- } else if ( GetBindMaster() && GetBindMaster()->IsType( idAFEntity_Vehicle::Type ) ) {
- newEyeOffset = 0.0f;
- } else {
- newEyeOffset = pm_normalviewheight.GetFloat();
- }
- if ( EyeHeight() != newEyeOffset ) {
- if ( spectating ) {
- SetEyeHeight( newEyeOffset );
- } else {
- // smooth out duck height changes
- SetEyeHeight( EyeHeight() * pm_crouchrate.GetFloat() + newEyeOffset * ( 1.0f - pm_crouchrate.GetFloat() ) );
- }
- }
- if ( AI_JUMP ) {
- // bounce the view weapon
- loggedAccel_t *acc = &loggedAccel[currentLoggedAccel&(NUM_LOGGED_ACCELS-1)];
- currentLoggedAccel++;
- acc->time = gameLocal.time;
- acc->dir[2] = 200;
- acc->dir[0] = acc->dir[1] = 0;
- }
- if ( AI_ONLADDER ) {
- int old_rung = oldOrigin.z / LADDER_RUNG_DISTANCE;
- int new_rung = physicsObj.GetOrigin().z / LADDER_RUNG_DISTANCE;
- if ( old_rung != new_rung ) {
- StartSound( "snd_stepladder", SND_CHANNEL_ANY, 0, false, NULL );
- }
- }
- BobCycle( pushVelocity );
- CrashLand( oldOrigin, oldVelocity );
- }
- /*
- ==============
- idPlayer::Move
- ==============
- */
- void idPlayer::Move() {
- float newEyeOffset;
- idVec3 oldOrigin;
- idVec3 oldVelocity;
- idVec3 pushVelocity;
- // save old origin and velocity for crashlanding
- oldOrigin = physicsObj.GetOrigin();
- oldVelocity = physicsObj.GetLinearVelocity();
- pushVelocity = physicsObj.GetPushedLinearVelocity();
- // set physics variables
- physicsObj.SetMaxStepHeight( pm_stepsize.GetFloat() );
- physicsObj.SetMaxJumpHeight( pm_jumpheight.GetFloat() );
- if ( noclip ) {
- physicsObj.SetContents( 0 );
- physicsObj.SetMovementType( PM_NOCLIP );
- } else if ( spectating ) {
- physicsObj.SetContents( 0 );
- physicsObj.SetMovementType( PM_SPECTATOR );
- } else if ( health <= 0 ) {
- physicsObj.SetContents( CONTENTS_CORPSE | CONTENTS_MONSTERCLIP );
- physicsObj.SetMovementType( PM_DEAD );
- } else if ( gameLocal.inCinematic || gameLocal.GetCamera() || privateCameraView || ( influenceActive == INFLUENCE_LEVEL2 ) ) {
- physicsObj.SetContents( CONTENTS_BODY );
- physicsObj.SetMovementType( PM_FREEZE );
- } else if ( mountedObject ) {
- physicsObj.SetContents( 0 );
- physicsObj.SetMovementType( PM_FREEZE );
- } else {
- physicsObj.SetContents( CONTENTS_BODY );
- physicsObj.SetMovementType( PM_NORMAL );
- }
- if ( spectating ) {
- physicsObj.SetClipMask( MASK_DEADSOLID );
- } else if ( health <= 0 ) {
- physicsObj.SetClipMask( MASK_DEADSOLID );
- } else {
- physicsObj.SetClipMask( MASK_PLAYERSOLID );
- }
- physicsObj.SetDebugLevel( g_debugMove.GetBool() );
- {
- idVec3 org;
- idMat3 axis;
- GetViewPos( org, axis );
- physicsObj.SetPlayerInput( usercmd, axis[0] );
- }
- // FIXME: physics gets disabled somehow
- BecomeActive( TH_PHYSICS );
- RunPhysics();
- // update our last valid AAS location for the AI
- SetAASLocation();
- if ( spectating ) {
- newEyeOffset = 0.0f;
- } else if ( health <= 0 ) {
- newEyeOffset = pm_deadviewheight.GetFloat();
- } else if ( physicsObj.IsCrouching() ) {
- newEyeOffset = pm_crouchviewheight.GetFloat();
- } else if ( GetBindMaster() && GetBindMaster()->IsType( idAFEntity_Vehicle::Type ) ) {
- newEyeOffset = 0.0f;
- } else {
- newEyeOffset = pm_normalviewheight.GetFloat();
- }
- if ( EyeHeight() != newEyeOffset ) {
- if ( spectating ) {
- SetEyeHeight( newEyeOffset );
- } else {
- // smooth out duck height changes
- SetEyeHeight( EyeHeight() * pm_crouchrate.GetFloat() + newEyeOffset * ( 1.0f - pm_crouchrate.GetFloat() ) );
- }
- }
- if ( noclip || gameLocal.inCinematic || ( influenceActive == INFLUENCE_LEVEL2 ) ) {
- AI_CROUCH = false;
- AI_ONGROUND = ( influenceActive == INFLUENCE_LEVEL2 );
- AI_ONLADDER = false;
- AI_JUMP = false;
- } else {
- AI_CROUCH = physicsObj.IsCrouching();
- AI_ONGROUND = physicsObj.HasGroundContacts();
- AI_ONLADDER = physicsObj.OnLadder();
- AI_JUMP = physicsObj.HasJumped();
- // check if we're standing on top of a monster and give a push if we are
- idEntity *groundEnt = physicsObj.GetGroundEntity();
- if ( groundEnt != NULL && groundEnt->IsType( idAI::Type ) ) {
- idVec3 vel = physicsObj.GetLinearVelocity();
- if ( vel.ToVec2().LengthSqr() < 0.1f ) {
- vel.ToVec2() = physicsObj.GetOrigin().ToVec2() - groundEnt->GetPhysics()->GetAbsBounds().GetCenter().ToVec2();
- vel.ToVec2().NormalizeFast();
- vel.ToVec2() *= pm_walkspeed.GetFloat();
- } else {
- // give em a push in the direction they're going
- vel *= 1.1f;
- }
- physicsObj.SetLinearVelocity( vel );
- }
- }
- if ( AI_JUMP ) {
- // bounce the view weapon
- loggedAccel_t *acc = &loggedAccel[currentLoggedAccel&(NUM_LOGGED_ACCELS-1)];
- currentLoggedAccel++;
- acc->time = gameLocal.time;
- acc->dir[2] = 200;
- acc->dir[0] = acc->dir[1] = 0;
- }
- if ( AI_ONLADDER ) {
- int old_rung = oldOrigin.z / LADDER_RUNG_DISTANCE;
- int new_rung = physicsObj.GetOrigin().z / LADDER_RUNG_DISTANCE;
- if ( old_rung != new_rung ) {
- StartSound( "snd_stepladder", SND_CHANNEL_ANY, 0, false, NULL );
- }
- }
- BobCycle( pushVelocity );
- CrashLand( oldOrigin, oldVelocity );
- }
- /*
- ========================
- idPlayer::AllowClientAuthPhysics
- ========================
- */
- bool idPlayer::AllowClientAuthPhysics() {
- // note respawn count > 1: respawn should be called twice - once for initial spawn and once for actual respawn by game mode
- // TODO: I don't think doom 3 will need to care about the respawn count.
- return ( usercmd.serverGameMilliseconds > serverOverridePositionTime && commonLocal.GetUCmdMgr().HasUserCmdForPlayer( entityNumber ) );
- }
- /*
- ========================
- idPlayer::RunPhysics_RemoteClientCorrection
- ========================
- */
- void idPlayer::RunPhysics_RemoteClientCorrection() {
- if ( !AllowClientAuthPhysics() ) {
- // We are still overriding client's position
- if ( pm_clientAuthoritative_debug.GetBool() ) {
- //clientGame->renderWorld->DebugPoint( idColor::colorRed, GetOrigin() );
- idLib::Printf("[%d]Ignoring client auth: cmd.serverTime: %d overrideTime: %d \n", entityNumber, usercmd.serverGameMilliseconds, serverOverridePositionTime );
- }
- return;
- }
- // Client is on a pusher... ignore him so he doesn't lag behind
- bool becameUnlocked = false;
- if ( physicsObj.ClientPusherLocked( becameUnlocked ) ) {
- // Check and see how far we've diverged.
- idVec3 cmdPos( usercmd.pos[0], usercmd.pos[1], usercmd.pos[2] );
- idVec3 newOrigin = physicsObj.GetOrigin();
- idVec3 divergeVec = cmdPos - newOrigin;
- idLib::Printf( "Client Divergence: %s Length: %2f\n", divergeVec.ToString( 3 ), divergeVec.Length() );
- // if the client Diverges over a certain amount, snap him back
- if( divergeVec.Length() < pm_clientAuthoritative_Divergence.GetFloat() ) {
- return;
- }
-
- }
- if ( becameUnlocked ) {
- // Client just got off of a mover, wait before listening to him
- serverOverridePositionTime = gameLocal.GetServerGameTimeMs();
- return;
- }
- // Correction
- {
- idVec3 newOrigin = physicsObj.GetOrigin();
- idVec3 cmdPos( usercmd.pos[0], usercmd.pos[1], usercmd.pos[2] );
- idVec3 desiredPos = cmdPos;
- float delta = ( desiredPos - newOrigin ).Length();
- // ignore small differences in Z: this can cause player to not have proper ground contacts which messes up
- // velocity/acceleration calculation. If this hack doesn't work out, will may need more precision for at least
- // the Z component of the client's origin.
- if ( idMath::Fabs( desiredPos.z - newOrigin.z ) < pm_clientAuthoritative_minDistZ.GetFloat() ) {
- if ( pm_clientAuthoritative_debug.GetBool() ) {
- //idLib::Printf("[%d]Remote client physics: ignore small z delta: %f\n", usercmd.clientGameFrame, ( desiredPos.z - newOrigin.z ) );
- }
- desiredPos.z = newOrigin.z;
- }
- // Origin
- if ( delta > pm_clientAuthoritative_minDist.GetFloat() ) {
- if ( pm_clientAuthoritative_Lerp.GetFloat() > 0.0f ) {
- desiredPos.x = idMath::LerpToWithScale( newOrigin.x, desiredPos.x, pm_clientAuthoritative_Lerp.GetFloat() );
- desiredPos.y = idMath::LerpToWithScale( newOrigin.y, desiredPos.y, pm_clientAuthoritative_Lerp.GetFloat() );
- }
- // Set corrected position immediately if non deferred
- physicsObj.SetOrigin( desiredPos );
-
- if ( pm_clientAuthoritative_debug.GetBool() && delta > pm_clientAuthoritative_warnDist.GetFloat() ) {
- idLib::Warning("Remote client player physics: delta movement for frame was %f units", delta );
- gameRenderWorld->DebugLine( colorRed, newOrigin, desiredPos );
- }
- }
- if ( pm_clientAuthoritative_debug.GetBool() ) {
- //idLib::Printf( "[%d]Remote client player physics delta: %.2f. forward: %d pos <%.2f, %.2f, %.2f> \n", usercmd.clientGameFrame, delta, (int)usercmd.forwardmove, desiredPos.x, desiredPos.y, desiredPos.z );
- gameRenderWorld->DebugLine( colorRed, newOrigin, desiredPos );
- //gameRenderWorld->DebugPoint( colorBlue, cmdPos );
- }
- // Set velocity if significantly different than client.
- const float serverSpeedSquared = physicsObj.GetLinearVelocity().LengthSqr();
- const float clientSpeedSquared = usercmd.speedSquared;
- if ( std::abs( serverSpeedSquared - clientSpeedSquared ) > pm_clientAuthoritative_minSpeedSquared.GetFloat() ) {
- idVec3 normalizedVelocity = physicsObj.GetLinearVelocity();
- const float VELOCITY_EPSILON = 0.001f;
- if ( normalizedVelocity.LengthSqr() > VELOCITY_EPSILON ) {
- normalizedVelocity.Normalize();
- }
- physicsObj.SetLinearVelocity( normalizedVelocity * idMath::Sqrt( clientSpeedSquared ) );
- }
- }
- }
- /*
- ========================
- idPlayer::GetPhysicsTimeStep
- Uses the time from the usercmd in case the server is running at a slower engineHz
- than the client.
- ========================
- */
- int idPlayer::GetPhysicsTimeStep() const {
- // if the ucDeltaMillisecond value looks wrong, use the game delta milliseconds
- // This can happen if the user brings up the pause menu in SP
- const int ucDeltaMilliseconds = usercmd.clientGameMilliseconds - oldCmd.clientGameMilliseconds;
- if ( ucDeltaMilliseconds < 1 || ucDeltaMilliseconds > 20 ) {
- return gameLocal.time - gameLocal.previousTime;
- } else {
- return ucDeltaMilliseconds;
- }
- }
- /*
- ==============
- idPlayer::ShowRespawnHudMessage
- Called once when the minimum respawn time has passed after a player has died
- so that we can display a message to the user.
- ==============
- */
- void idPlayer::ShowRespawnHudMessage() {
- if ( IsLocallyControlled() ) {
- hud->ShowRespawnMessage( true );
- } else {
- // Clients show the hud message through a reliable message.
- idBitMsg outMsg;
- byte dummyData[1];
- outMsg.InitWrite( dummyData, sizeof( dummyData ) );
- outMsg.BeginWriting();
- outMsg.WriteByte( 0 );
- session->GetActingGameStateLobbyBase().SendReliableToLobbyUser( gameLocal.lobbyUserIDs[entityNumber], GAME_RELIABLE_MESSAGE_RESPAWN_AVAILABLE, outMsg );
- }
- }
- /*
- ==============
- idPlayer::HideRespawnHudMessage
- Called once when we should remove the respawn message from the hud,
- for example, when a player does respawn.
- ==============
- */
- void idPlayer::HideRespawnHudMessage() {
- if ( IsLocallyControlled() ) {
- hud->ShowRespawnMessage( false );
- }
- }
- /*
- ==============
- idPlayer::UpdateHud
- ==============
- */
- void idPlayer::UpdateHud() {
- idPlayer *aimed;
- if ( !hud ) {
- return;
- }
- if ( !IsLocallyControlled() ) {
- return;
- }
- int c = inventory.pickupItemNames.Num();
- if ( c > 0 ) {
- if ( hud != NULL && hud->IsPickupListReady() ) {
- if ( inventory.nextItemPickup && gameLocal.time - inventory.nextItemPickup > 2000 ) {
- inventory.nextItemNum = 1;
- }
- int i;
- int count = 5;
- bool showNewPickups = false;
- for ( i = 0; i < count; i++ ) { //_D3XP
- if ( i < c ) {
- hud->UpdatePickupInfo( i, inventory.pickupItemNames[0] );
- inventory.nextItemNum++;
- showNewPickups = true;
- } else {
- hud->UpdatePickupInfo( i, "" );
- continue;
- }
- inventory.nextItemPickup = gameLocal.time + 2500;
- inventory.pickupItemNames.RemoveIndex( 0 );
- }
- if ( showNewPickups ) {
- hud->ShowPickups();
- }
- }
- }
- if ( gameLocal.realClientTime == lastMPAimTime ) {
- if ( MPAim != -1 && gameLocal.mpGame.IsGametypeTeamBased() /* CTF */
- && gameLocal.entities[ MPAim ] && gameLocal.entities[ MPAim ]->IsType( idPlayer::Type )
- && static_cast< idPlayer * >( gameLocal.entities[ MPAim ] )->team == team ) {
- aimed = static_cast< idPlayer * >( gameLocal.entities[ MPAim ] );
- hud->TriggerHitTarget( true, session->GetActingGameStateLobbyBase().GetLobbyUserName( gameLocal.lobbyUserIDs[ MPAim ] ), aimed->team + 1 );
- MPAimHighlight = true;
- MPAimFadeTime = 0; // no fade till loosing focus
- } else if ( MPAimHighlight ) {
- hud->TriggerHitTarget( false, "" );
- MPAimFadeTime = gameLocal.realClientTime;
- MPAimHighlight = false;
- }
- }
- if ( MPAimFadeTime ) {
- assert( !MPAimHighlight );
- if ( gameLocal.realClientTime - MPAimFadeTime > 2000 ) {
- MPAimFadeTime = 0;
- }
- }
- if ( common->IsMultiplayer() && IsLocallyControlled() ) {
- hud->ToggleLagged( isLagged );
- // TODO_SPARTY: what is this projectile stuff for
- //hud->SetStateInt( "g_showProjectilePct", g_showProjectilePct.GetInteger() );
- //if ( numProjectilesFired ) {
- // hud->SetStateString( "projectilepct", va( "Hit %% %.1f", ( (float) numProjectileHits / numProjectilesFired ) * 100 ) );
- //} else {
- // hud->SetStateString( "projectilepct", "Hit % 0.0" );
- //}
- }
- }
- /*
- ==============
- idPlayer::UpdateDeathSkin
- ==============
- */
- void idPlayer::UpdateDeathSkin( bool state_hitch ) {
- if ( !( common->IsMultiplayer() || g_testDeath.GetBool() ) ) {
- return;
- }
- if ( health <= 0 ) {
- if ( !doingDeathSkin ) {
- deathClearContentsTime = spawnArgs.GetInt( "deathSkinTime" );
- doingDeathSkin = true;
- renderEntity.noShadow = true;
- if ( state_hitch ) {
- renderEntity.shaderParms[ SHADERPARM_TIME_OF_DEATH ] = gameLocal.time * 0.001f - 2.0f;
- } else {
- renderEntity.shaderParms[ SHADERPARM_TIME_OF_DEATH ] = gameLocal.time * 0.001f;
- }
- UpdateVisuals();
- }
- // wait a bit before switching off the content
- if ( deathClearContentsTime && gameLocal.time > deathClearContentsTime ) {
- SetCombatContents( false );
- deathClearContentsTime = 0;
- }
- } else {
- renderEntity.noShadow = false;
- renderEntity.shaderParms[ SHADERPARM_TIME_OF_DEATH ] = 0.0f;
- UpdateVisuals();
- doingDeathSkin = false;
- }
- }
- /*
- ==============
- idPlayer::StartFxOnBone
- ==============
- */
- void idPlayer::StartFxOnBone( const char *fx, const char *bone ) {
- idVec3 offset;
- idMat3 axis;
- jointHandle_t jointHandle = GetAnimator()->GetJointHandle( bone );
- if ( jointHandle == INVALID_JOINT ) {
- gameLocal.Printf( "Cannot find bone %s\n", bone );
- return;
- }
- if ( GetAnimator()->GetJointTransform( jointHandle, gameLocal.time, offset, axis ) ) {
- offset = GetPhysics()->GetOrigin() + offset * GetPhysics()->GetAxis();
- axis = axis * GetPhysics()->GetAxis();
- }
- idEntityFx::StartFx( fx, &offset, &axis, this, true );
- }
- /*
- ==============
- idPlayer::HandleGuiEvents
- ==============
- */
- bool idPlayer::HandleGuiEvents( const sysEvent_t * ev ) {
- bool handled = false;
- if ( hudManager != NULL && hudManager->IsActive() ) {
- handled = hudManager->HandleGuiEvent( ev );
- }
- if ( pdaMenu != NULL && pdaMenu->IsActive() ) {
- handled = pdaMenu->HandleGuiEvent( ev );
- }
- return handled;
- }
- /*
- ==============
- idPlayer::UpdateLaserSight
- ==============
- */
- idCVar g_laserSightWidth( "g_laserSightWidth", "2.0", CVAR_FLOAT | CVAR_ARCHIVE, "laser sight beam width" );
- idCVar g_laserSightLength( "g_laserSightLength", "250", CVAR_FLOAT | CVAR_ARCHIVE, "laser sight beam length" );
- void idPlayer::UpdateLaserSight() {
- idVec3 muzzleOrigin;
- idMat3 muzzleAxis;
- // In Multiplayer, weapon might not have been spawned yet.
- if( weapon.GetEntity() == NULL ) {
- return;
- }
- if ( !IsGameStereoRendered() ||
- !weapon.GetEntity()->ShowCrosshair() ||
- AI_DEAD ||
- weapon->IsHidden() ||
- !weapon->GetMuzzlePositionWithHacks( muzzleOrigin, muzzleAxis ) ) {
- // hide it
- laserSightRenderEntity.allowSurfaceInViewID = -1;
- if ( laserSightHandle == -1 ) {
- laserSightHandle = gameRenderWorld->AddEntityDef( &laserSightRenderEntity );
- } else {
- gameRenderWorld->UpdateEntityDef( laserSightHandle, &laserSightRenderEntity );
- }
- return;
- }
- // program the beam model
- // only show in the player's view
- laserSightRenderEntity.allowSurfaceInViewID = entityNumber+1;
- laserSightRenderEntity.axis.Identity();
- laserSightRenderEntity.origin = muzzleOrigin - muzzleAxis[0] * 2.0f;
- idVec3 &target = *reinterpret_cast<idVec3 *>( &laserSightRenderEntity.shaderParms[SHADERPARM_BEAM_END_X] );
- target = muzzleOrigin + muzzleAxis[0] * g_laserSightLength.GetFloat();
- laserSightRenderEntity.shaderParms[SHADERPARM_BEAM_WIDTH] = g_laserSightWidth.GetFloat();
- if ( IsGameStereoRendered() && laserSightHandle == -1 ) {
- laserSightHandle = gameRenderWorld->AddEntityDef( &laserSightRenderEntity );
- } else {
- gameRenderWorld->UpdateEntityDef( laserSightHandle, &laserSightRenderEntity );
- }
- }
- /*
- ==============
- idPlayer::Think
- Called every tic for each player
- ==============
- */
- void idPlayer::Think() {
- playedTimeResidual += ( gameLocal.time - gameLocal.previousTime );
- playedTimeSecs += playedTimeResidual / 1000;
- playedTimeResidual = playedTimeResidual % 1000;
- aimAssist.Update();
- UpdatePlayerIcons();
- UpdateSkinSetup();
- buttonMask &= usercmd.buttons;
- usercmd.buttons &= ~buttonMask;
- // clear the ik before we do anything else so the skeleton doesn't get updated twice
- walkIK.ClearJointMods();
-
- // if this is the very first frame of the map, set the delta view angles
- // based on the usercmd angles
- if ( !spawnAnglesSet && ( gameLocal.GameState() != GAMESTATE_STARTUP ) ) {
- spawnAnglesSet = true;
- SetViewAngles( spawnAngles );
- oldImpulseSequence = usercmd.impulseSequence;
- }
- if ( mountedObject ) {
- usercmd.forwardmove = 0;
- usercmd.rightmove = 0;
- usercmd.buttons &= ~(BUTTON_JUMP|BUTTON_CROUCH);
- }
- if ( objectiveSystemOpen || gameLocal.inCinematic || influenceActive ) {
- if ( objectiveSystemOpen && AI_PAIN ) {
- TogglePDA();
- }
- usercmd.forwardmove = 0;
- usercmd.rightmove = 0;
- usercmd.buttons &= ~(BUTTON_JUMP|BUTTON_CROUCH);
- }
- // log movement changes for weapon bobbing effects
- if ( usercmd.forwardmove != oldCmd.forwardmove ) {
- loggedAccel_t *acc = &loggedAccel[currentLoggedAccel&(NUM_LOGGED_ACCELS-1)];
- currentLoggedAccel++;
- acc->time = gameLocal.time;
- acc->dir[0] = usercmd.forwardmove - oldCmd.forwardmove;
- acc->dir[1] = acc->dir[2] = 0;
- }
- if ( usercmd.rightmove != oldCmd.rightmove ) {
- loggedAccel_t *acc = &loggedAccel[currentLoggedAccel&(NUM_LOGGED_ACCELS-1)];
- currentLoggedAccel++;
- acc->time = gameLocal.time;
- acc->dir[1] = usercmd.rightmove - oldCmd.rightmove;
- acc->dir[0] = acc->dir[2] = 0;
- }
- // zooming
- if ( ( usercmd.buttons ^ oldCmd.buttons ) & BUTTON_ZOOM ) {
- if ( ( usercmd.buttons & BUTTON_ZOOM ) && weapon.GetEntity() ) {
- zoomFov.Init( gameLocal.time, 200.0f, CalcFov( false ), weapon.GetEntity()->GetZoomFov() );
- } else {
- zoomFov.Init( gameLocal.time, 200.0f, zoomFov.GetCurrentValue( gameLocal.time ), DefaultFov() );
- }
- }
- // if we have an active gui, we will unrotate the view angles as
- // we turn the mouse movements into gui events
- idUserInterface *gui = ActiveGui();
- if ( gui && gui != focusUI ) {
- RouteGuiMouse( gui );
- }
- // set the push velocity on the weapon before running the physics
- if ( weapon.GetEntity() ) {
- weapon.GetEntity()->SetPushVelocity( physicsObj.GetPushedLinearVelocity() );
- }
- EvaluateControls();
- if ( !af.IsActive() ) {
- AdjustBodyAngles();
- CopyJointsFromBodyToHead();
- }
- if ( IsLocallyControlled() ) {
- // Local player on the server. Do normal movement.
- Move();
- } else {
- // Server is processing a client. Run client's commands like normal...
- Move();
- // ...then correct if needed.
- RunPhysics_RemoteClientCorrection();
- }
- if ( !g_stopTime.GetBool() ) {
- if ( !noclip && !spectating && ( health > 0 ) && !IsHidden() ) {
- TouchTriggers();
- }
- // not done on clients for various reasons. don't do it on server and save the sound channel for other things
- if ( !common->IsMultiplayer() ) {
- SetCurrentHeartRate();
- float scale = new_g_damageScale;
- if ( g_useDynamicProtection.GetBool() && scale < 1.0f && gameLocal.time - lastDmgTime > 500 ) {
- if ( scale < 1.0f ) {
- scale += 0.05f;
- }
- if ( scale > 1.0f ) {
- scale = 1.0f;
- }
- new_g_damageScale = scale;
- }
- }
- // update GUIs, Items, and character interactions
- UpdateFocus();
-
- UpdateLocation();
- // update player script
- UpdateScript();
- // service animations
- if ( !spectating && !af.IsActive() && !gameLocal.inCinematic ) {
- UpdateConditions();
- UpdateAnimState();
- CheckBlink();
- }
- // clear out our pain flag so we can tell if we recieve any damage between now and the next time we think
- AI_PAIN = false;
- }
- // calculate the exact bobbed view position, which is used to
- // position the view weapon, among other things
- CalculateFirstPersonView();
- // this may use firstPersonView, or a thirdPeroson / camera view
- CalculateRenderView();
- inventory.UpdateArmor();
- if ( spectating ) {
- UpdateSpectating();
- } else if ( health > 0 ) {
- UpdateWeapon();
- }
- UpdateFlashlight();
- UpdateAir();
-
- UpdatePowerupHud();
-
- UpdateHud();
- UpdatePowerUps();
- UpdateDeathSkin( false );
- if ( common->IsMultiplayer() ) {
- DrawPlayerIcons();
- if ( enviroSuitLight.IsValid() ) {
- idAngles lightAng = firstPersonViewAxis.ToAngles();
- idVec3 lightOrg = firstPersonViewOrigin;
- const idDict *lightDef = gameLocal.FindEntityDefDict( "envirosuit_light", false );
- idVec3 enviroOffset = lightDef->GetVector( "enviro_offset" );
- idVec3 enviroAngleOffset = lightDef->GetVector( "enviro_angle_offset" );
- lightOrg += (enviroOffset.x * firstPersonViewAxis[0]);
- lightOrg += (enviroOffset.y * firstPersonViewAxis[1]);
- lightOrg += (enviroOffset.z * firstPersonViewAxis[2]);
- lightAng.pitch += enviroAngleOffset.x;
- lightAng.yaw += enviroAngleOffset.y;
- lightAng.roll += enviroAngleOffset.z;
- enviroSuitLight.GetEntity()->GetPhysics()->SetOrigin( lightOrg );
- enviroSuitLight.GetEntity()->GetPhysics()->SetAxis( lightAng.ToMat3() );
- enviroSuitLight.GetEntity()->UpdateVisuals();
- enviroSuitLight.GetEntity()->Present();
- }
- }
- renderEntity_t * headRenderEnt = NULL;
- if ( head.GetEntity() ) {
- headRenderEnt = head.GetEntity()->GetRenderEntity();
- }
- if ( headRenderEnt ) {
- if ( influenceSkin ) {
- headRenderEnt->customSkin = influenceSkin;
- } else {
- headRenderEnt->customSkin = NULL;
- }
- }
- if ( common->IsMultiplayer() || g_showPlayerShadow.GetBool() ) {
- renderEntity.suppressShadowInViewID = 0;
- if ( headRenderEnt ) {
- headRenderEnt->suppressShadowInViewID = 0;
- }
- } else {
- renderEntity.suppressShadowInViewID = entityNumber+1;
- if ( headRenderEnt ) {
- headRenderEnt->suppressShadowInViewID = entityNumber+1;
- }
- }
- // never cast shadows from our first-person muzzle flashes
- renderEntity.suppressShadowInLightID = LIGHTID_VIEW_MUZZLE_FLASH + entityNumber;
- if ( headRenderEnt ) {
- headRenderEnt->suppressShadowInLightID = LIGHTID_VIEW_MUZZLE_FLASH + entityNumber;
- }
- if ( !g_stopTime.GetBool() ) {
- UpdateAnimation();
- Present();
- UpdateDamageEffects();
- LinkCombat();
- playerView.CalculateShake();
- }
- if ( !( thinkFlags & TH_THINK ) ) {
- gameLocal.Printf( "player %d not thinking?\n", entityNumber );
- }
- if ( g_showEnemies.GetBool() ) {
- idActor *ent;
- int num = 0;
- for( ent = enemyList.Next(); ent != NULL; ent = ent->enemyNode.Next() ) {
- gameLocal.Printf( "enemy (%d)'%s'\n", ent->entityNumber, ent->name.c_str() );
- gameRenderWorld->DebugBounds( colorRed, ent->GetPhysics()->GetBounds().Expand( 2 ), ent->GetPhysics()->GetOrigin() );
- num++;
- }
- gameLocal.Printf( "%d: enemies\n", num );
- }
- inventory.RechargeAmmo(this);
- if(healthRecharge) {
- int elapsed = gameLocal.time - lastHealthRechargeTime;
- if(elapsed >= rechargeSpeed) {
- int intervals = (gameLocal.time - lastHealthRechargeTime)/rechargeSpeed;
- Give("health", va("%d", intervals), ITEM_GIVE_FEEDBACK | ITEM_GIVE_UPDATE_STATE );
- lastHealthRechargeTime += intervals*rechargeSpeed;
- }
- }
- // determine if portal sky is in pvs
- gameLocal.portalSkyActive = gameLocal.pvs.CheckAreasForPortalSky( gameLocal.GetPlayerPVS(), GetPhysics()->GetOrigin() );
- // stereo rendering laser sight that replaces the crosshair
- UpdateLaserSight();
- // Show the respawn hud message if necessary.
- if ( common->IsMultiplayer() && ( minRespawnTime != maxRespawnTime ) ) {
- if ( gameLocal.previousTime < minRespawnTime && minRespawnTime <= gameLocal.time ) {
- // Server will show the hud message directly.
- ShowRespawnHudMessage();
- }
- }
- // Make sure voice groups are set to the right team
- if ( common->IsMultiplayer() && session->GetState() >= idSession::INGAME && entityNumber < MAX_CLIENTS ) { // The entityNumber < MAX_CLIENTS seems to quiet the static analyzer
- // Make sure we're on the right team (at the lobby level)
- const int voiceTeam = spectating ? LOBBY_SPECTATE_TEAM_FOR_VOICE_CHAT : team;
- //idLib::Printf( "SERVER: Sending voice %i / %i\n", entityNumber, voiceTeam );
- // Update lobby team
- session->GetActingGameStateLobbyBase().SetLobbyUserTeam( gameLocal.lobbyUserIDs[ entityNumber ], voiceTeam );
-
- // Update voice groups to match in case something changed
- session->SetVoiceGroupsToTeams();
- }
- }
- /*
- =================
- idPlayer::StartHealthRecharge
- =================
- */
- void idPlayer::StartHealthRecharge(int speed) {
- lastHealthRechargeTime = gameLocal.time;
- healthRecharge = true;
- rechargeSpeed = speed;
- }
- /*
- =================
- idPlayer::StopHealthRecharge
- =================
- */
- void idPlayer::StopHealthRecharge() {
- healthRecharge = false;
- }
- /*
- =================
- idPlayer::GetCurrentWeapon
- =================
- */
- idStr idPlayer::GetCurrentWeapon() {
- const char *weapon;
- if ( currentWeapon >= 0 ) {
- weapon = spawnArgs.GetString( va( "def_weapon%d", currentWeapon ) );
- return weapon;
- } else {
- return "";
- }
- }
- /*
- =================
- idPlayer::CanGive
- =================
- */
- bool idPlayer::CanGive( const char *statname, const char *value ) {
- if ( AI_DEAD ) {
- return false;
- }
- if ( !idStr::Icmp( statname, "health" ) ) {
- if ( health >= inventory.maxHealth ) {
- return false;
- }
- return true;
- } else if ( !idStr::Icmp( statname, "stamina" ) ) {
- if ( stamina >= 100 ) {
- return false;
- }
- return true;
- } else if ( !idStr::Icmp( statname, "heartRate" ) ) {
- return true;
- } else if ( !idStr::Icmp( statname, "air" ) ) {
- if ( airMsec >= pm_airMsec.GetInteger() ) {
- return false;
- }
- return true;
- } else {
- return inventory.CanGive( this, spawnArgs, statname, value );
- }
- }
- /*
- =================
- idPlayer::StopHelltime
- provides a quick non-ramping way of stopping helltime
- =================
- */
- void idPlayer::StopHelltime( bool quick ) {
- if ( !PowerUpActive( HELLTIME ) ) {
- return;
- }
- // take away the powerups
- if ( PowerUpActive( INVULNERABILITY ) ) {
- ClearPowerup( INVULNERABILITY );
- }
- if ( PowerUpActive( BERSERK ) ) {
- ClearPowerup( BERSERK );
- }
- if ( PowerUpActive( HELLTIME ) ) {
- ClearPowerup( HELLTIME );
- }
- // stop the looping sound
- StopSound( SND_CHANNEL_DEMONIC, false );
- // reset the game vars
- if ( quick ) {
- gameLocal.QuickSlowmoReset();
- }
- }
- /*
- =================
- idPlayer::Event_ToggleBloom
- =================
- */
- void idPlayer::Event_ToggleBloom( int on ) {
- if ( on ) {
- bloomEnabled = true;
- }
- else {
- bloomEnabled = false;
- }
- }
- /*
- =================
- idPlayer::Event_SetBloomParms
- =================
- */
- void idPlayer::Event_SetBloomParms( float speed, float intensity ) {
- bloomSpeed = speed;
- bloomIntensity = intensity;
- }
- /*
- =================
- idPlayer::PlayHelltimeStopSound
- =================
- */
- void idPlayer::PlayHelltimeStopSound() {
- const char* sound;
- if ( spawnArgs.GetString( "snd_helltime_stop", "", &sound ) ) {
- PostEventMS( &EV_StartSoundShader, 0, sound, SND_CHANNEL_ANY );
- }
- }
- /*
- =================
- idPlayer::RouteGuiMouse
- =================
- */
- void idPlayer::RouteGuiMouse( idUserInterface *gui ) {
- sysEvent_t ev;
- const char *command;
- if ( usercmd.mx != oldMouseX || usercmd.my != oldMouseY ) {
- ev = sys->GenerateMouseMoveEvent( usercmd.mx - oldMouseX, usercmd.my - oldMouseY );
- command = gui->HandleEvent( &ev, gameLocal.time );
- oldMouseX = usercmd.mx;
- oldMouseY = usercmd.my;
- }
- }
- /*
- ==================
- idPlayer::LookAtKiller
- ==================
- */
- void idPlayer::LookAtKiller( idEntity *inflictor, idEntity *attacker ) {
- idVec3 dir;
-
- if ( attacker && attacker != this ) {
- dir = attacker->GetPhysics()->GetOrigin() - GetPhysics()->GetOrigin();
- } else if ( inflictor && inflictor != this ) {
- dir = inflictor->GetPhysics()->GetOrigin() - GetPhysics()->GetOrigin();
- } else {
- dir = viewAxis[ 0 ];
- }
- idAngles ang( 0, dir.ToYaw(), 0 );
- SetViewAngles( ang );
- }
- /*
- ==============
- idPlayer::Kill
- ==============
- */
- void idPlayer::Kill( bool delayRespawn, bool nodamage ) {
- if ( spectating ) {
- SpectateFreeFly( false );
- } else if ( health > 0 ) {
- godmode = false;
- if ( nodamage ) {
- ServerSpectate( true );
- idLib::Printf( "TOURNEY Kill :> Player %d On Deck \n", entityNumber );
- forceRespawn = true;
- } else {
- Damage( this, this, vec3_origin, "damage_suicide", 1.0f, INVALID_JOINT );
- if ( delayRespawn ) {
- forceRespawn = false;
- int delay = spawnArgs.GetFloat( "respawn_delay" );
- minRespawnTime = gameLocal.time + SEC2MS( delay );
- maxRespawnTime = minRespawnTime + MAX_RESPAWN_TIME;
- }
- }
- }
- }
- /*
- ==================
- idPlayer::Killed
- ==================
- */
- void idPlayer::Killed( idEntity *inflictor, idEntity *attacker, int damage, const idVec3 &dir, int location ) {
- float delay;
- assert( !common->IsClient() );
- // stop taking knockback once dead
- fl.noknockback = true;
- if ( health < -999 ) {
- health = -999;
- }
- if ( AI_DEAD ) {
- AI_PAIN = true;
- return;
- }
- heartInfo.Init( 0, 0, 0, BASE_HEARTRATE );
- AdjustHeartRate( DEAD_HEARTRATE, 10.0f, 0.0f, true );
- if ( !g_testDeath.GetBool() && !common->IsMultiplayer() ) {
- playerView.Fade( colorBlack, 3000 );
- }
- AI_DEAD = true;
- SetAnimState( ANIMCHANNEL_LEGS, "Legs_Death", 4 );
- SetAnimState( ANIMCHANNEL_TORSO, "Torso_Death", 4 );
- SetWaitState( "" );
- animator.ClearAllJoints();
- if ( StartRagdoll() ) {
- pm_modelView.SetInteger( 0 );
- minRespawnTime = gameLocal.time + RAGDOLL_DEATH_TIME;
- maxRespawnTime = minRespawnTime + MAX_RESPAWN_TIME;
- } else {
- // don't allow respawn until the death anim is done
- // g_forcerespawn may force spawning at some later time
- delay = spawnArgs.GetFloat( "respawn_delay" );
- minRespawnTime = gameLocal.time + SEC2MS( delay );
- maxRespawnTime = minRespawnTime + MAX_RESPAWN_TIME;
- }
- physicsObj.SetMovementType( PM_DEAD );
- StartSound( "snd_death", SND_CHANNEL_VOICE, 0, false, NULL );
- StopSound( SND_CHANNEL_BODY2, false );
- fl.takedamage = true; // can still be gibbed
- // get rid of weapon
- weapon.GetEntity()->OwnerDied();
-
- // In multiplayer, get rid of the flashlight, or other players
- // will see it floating after the player is dead.
- if ( common->IsMultiplayer() ) {
- FlashlightOff();
- if ( flashlight.GetEntity() ) {
- flashlight.GetEntity()->OwnerDied();
- }
- }
- // drop the weapon as an item
- DropWeapon( true );
- // drop the flag if player was carrying it
- if ( common->IsMultiplayer() && gameLocal.mpGame.IsGametypeFlagBased() && carryingFlag ) {
- DropFlag();
- }
- if ( !g_testDeath.GetBool() ) {
- LookAtKiller( inflictor, attacker );
- }
- if ( common->IsMultiplayer() || g_testDeath.GetBool() ) {
- idPlayer *killer = NULL;
- // no gibbing in MP. Event_Gib will early out in MP
- if ( attacker->IsType( idPlayer::Type ) ) {
- killer = static_cast<idPlayer*>(attacker);
- if ( health < -20 || killer->PowerUpActive( BERSERK ) ) {
- gibDeath = true;
- gibsDir = dir;
- gibsLaunched = false;
- }
- }
- gameLocal.mpGame.PlayerDeath( this, killer, isTelefragged );
- } else {
- physicsObj.SetContents( CONTENTS_CORPSE | CONTENTS_MONSTERCLIP );
- }
- ClearPowerUps();
- UpdateVisuals();
- }
- /*
- =====================
- idPlayer::GetAIAimTargets
- Returns positions for the AI to aim at.
- =====================
- */
- void idPlayer::GetAIAimTargets( const idVec3 &lastSightPos, idVec3 &headPos, idVec3 &chestPos ) {
- idVec3 offset;
- idMat3 axis;
- idVec3 origin;
-
- origin = lastSightPos - physicsObj.GetOrigin();
- GetJointWorldTransform( chestJoint, gameLocal.time, offset, axis );
- headPos = offset + origin;
- GetJointWorldTransform( headJoint, gameLocal.time, offset, axis );
- chestPos = offset + origin;
- }
- /*
- ================
- idPlayer::DamageFeedback
- callback function for when another entity received damage from this entity. damage can be adjusted and returned to the caller.
- ================
- */
- void idPlayer::DamageFeedback( idEntity *victim, idEntity *inflictor, int &damage ) {
- // Since we're predicting projectiles on the client now, we might actually get here
- // (used be an assert for clients).
- if ( common->IsClient() ) {
- return;
- }
- damage *= PowerUpModifier( BERSERK );
- if ( damage && ( victim != this ) && ( victim->IsType( idActor::Type ) || victim->IsType( idDamagable::Type ) ) ) {
- idPlayer *victimPlayer = NULL;
-
- /* No damage feedback sound for hitting friendlies in CTF */
- if ( victim->IsType( idPlayer::Type ) ) {
- victimPlayer = static_cast<idPlayer*>(victim);
- }
- if ( gameLocal.mpGame.IsGametypeFlagBased() && victimPlayer && this->team == victimPlayer->team ) {
- /* Do nothing ... */
- } else {
- SetLastHitTime( gameLocal.time );
- }
- }
- }
- /*
- =================
- idPlayer::CalcDamagePoints
- Calculates how many health and armor points will be inflicted, but
- doesn't actually do anything with them. This is used to tell when an attack
- would have killed the player, possibly allowing a "saving throw"
- =================
- */
- void idPlayer::CalcDamagePoints( idEntity *inflictor, idEntity *attacker, const idDict *damageDef,
- const float damageScale, const int location, int *health, int *armor ) {
- int damage;
- int armorSave;
- damageDef->GetInt( "damage", "20", damage );
- damage = GetDamageForLocation( damage, location );
- idPlayer *player = attacker->IsType( idPlayer::Type ) ? static_cast<idPlayer*>(attacker) : NULL;
- if ( !common->IsMultiplayer() ) {
- if ( inflictor != gameLocal.world ) {
- switch ( g_skill.GetInteger() ) {
- case 0:
- damage *= 0.50f;
- if ( damage < 1 ) {
- damage = 1;
- }
- break;
- case 2:
- damage *= 1.70f;
- break;
- case 3:
- damage *= 3.5f;
- break;
- default:
- break;
- }
- }
- }
- damage *= damageScale;
- // always give half damage if hurting self
- if ( attacker == this ) {
- if ( common->IsMultiplayer() ) {
- // only do this in mp so single player plasma and rocket splash is very dangerous in close quarters
- damage *= damageDef->GetFloat( "selfDamageScale", "0.5" );
- } else {
- damage *= damageDef->GetFloat( "selfDamageScale", "1" );
- }
- }
- // check for completely getting out of the damage
- if ( !damageDef->GetBool( "noGod" ) ) {
- // check for godmode
- if ( godmode ) {
- damage = 0;
- }
- //Invulnerability is just like god mode
- if( PowerUpActive( INVULNERABILITY ) ) {
- damage = 0;
- }
- }
- // inform the attacker that they hit someone
- attacker->DamageFeedback( this, inflictor, damage );
- // save some from armor
- if ( !damageDef->GetBool( "noArmor" ) ) {
- float armor_protection;
- armor_protection = ( common->IsMultiplayer() ) ? g_armorProtectionMP.GetFloat() : g_armorProtection.GetFloat();
- armorSave = ceil( damage * armor_protection );
- if ( armorSave >= inventory.armor ) {
- armorSave = inventory.armor;
- }
- if ( !damage ) {
- armorSave = 0;
- } else if ( armorSave >= damage ) {
- armorSave = damage - 1;
- damage = 1;
- } else {
- damage -= armorSave;
- }
- } else {
- armorSave = 0;
- }
- // check for team damage
- if ( gameLocal.mpGame.IsGametypeTeamBased() /* CTF */
- && !gameLocal.serverInfo.GetBool( "si_teamDamage" )
- && !damageDef->GetBool( "noTeam" )
- && player
- && player != this // you get self damage no matter what
- && player->team == team ) {
- damage = 0;
- }
- *health = damage;
- *armor = armorSave;
- }
- /*
- ============
- idPlayer::ControllerShakeFromDamage
- ============
- */
- void idPlayer::ControllerShakeFromDamage( int damage ) {
- // If the player is local. SHAkkkkkkeeee!
- if( common->IsMultiplayer() && IsLocallyControlled() ) {
- int maxMagScale = pm_controllerShake_damageMaxMag.GetFloat();
- int maxDurScale = pm_controllerShake_damageMaxDur.GetFloat();
- // determine rumble
- // >= 100 damage - will be 300 Mag
- float highMag = ( Max( damage, 100 ) / 100.0f ) * maxMagScale;
- int highDuration = idMath::Ftoi( ( Max( damage, 100 ) / 100.0f ) * maxDurScale );
- float lowMag = highMag * 0.75f;
- int lowDuration = idMath::Ftoi( highDuration );
- SetControllerShake( highMag, highDuration, lowMag, lowDuration );
- }
- }
- /*
- ============
- AdjustDamageAmount
- Modifies the previously calculated damage to adjust for more factors.
- ============
- */
- int idPlayer::AdjustDamageAmount( const int inputDamage ) {
- int outputDamage = inputDamage;
-
- if ( inputDamage > 0 ) {
- if ( !common->IsMultiplayer() ) {
- float scale = new_g_damageScale;
- if ( g_useDynamicProtection.GetBool() && g_skill.GetInteger() < 2 ) {
- if ( gameLocal.time > lastDmgTime + 500 && scale > 0.25f ) {
- scale -= 0.05f;
- new_g_damageScale = scale;
- }
- }
- if ( scale > 0.0f ) {
- outputDamage *= scale;
- }
- }
- if ( g_demoMode.GetBool() ) {
- outputDamage /= 2;
- }
- if ( outputDamage < 1 ) {
- outputDamage = 1;
- }
- }
- return outputDamage;
- }
- /*
- ============
- ServerDealDamage
- Only called on the server and in singleplayer. This is where
- the player's health is actually modified, but the visual and
- sound effects happen elsewhere so that clients can get instant
- feedback and hide lag.
- ============
- */
- void idPlayer::ServerDealDamage( int damage, idEntity & inflictor, idEntity & attacker, const idVec3 & dir, const char * damageDefName, const int location ) {
- assert( !common->IsClient() );
- const idDeclEntityDef *damageDef = gameLocal.FindEntityDef( damageDefName, false );
- if ( !damageDef ) {
- gameLocal.Warning( "Unknown damageDef '%s'", damageDefName );
- return;
- }
- // move the world direction vector to local coordinates
- idVec3 damage_from;
- idVec3 localDamageVector;
- damage_from = dir;
- damage_from.Normalize();
-
- viewAxis.ProjectVector( damage_from, localDamageVector );
- // add to the damage inflicted on a player this frame
- // the total will be turned into screen blends and view angle kicks
- // at the end of the frame
- if ( health > 0 ) {
- playerView.DamageImpulse( localDamageVector, &damageDef->dict );
- }
- // do the damage
- if ( damage > 0 ) {
- GetAchievementManager().SetPlayerTookDamage( true );
- int oldHealth = health;
- health -= damage;
- if ( health <= 0 ) {
- if ( health < -999 ) {
- health = -999;
- }
- // HACK - A - LICIOUS - Check to see if we are being damaged by the frag chamber.
- if ( oldHealth > 0 && strcmp( gameLocal.GetMapName(), "maps/game/mp/d3dm3.map" ) == 0 && strcmp( damageDefName, "damage_triggerhurt_1000_chamber" ) == 0 ) {
- idPlayer * fragChamberActivator = gameLocal.playerActivateFragChamber;
- if ( fragChamberActivator != NULL ) {
- fragChamberActivator->GetAchievementManager().EventCompletesAchievement( ACHIEVEMENT_MP_CATCH_ENEMY_IN_ROFC );
- }
- gameLocal.playerActivateFragChamber = NULL;
- }
- isTelefragged = damageDef->dict.GetBool( "telefrag" );
- lastDmgTime = gameLocal.time;
- Killed( &inflictor, &attacker, damage, dir, location );
- } else {
- if ( !g_testDeath.GetBool() ) {
- lastDmgTime = gameLocal.time;
- }
- }
- } else {
- // don't accumulate impulses
- if ( af.IsLoaded() ) {
- // clear impacts
- af.Rest();
- // physics is turned off by calling af.Rest()
- BecomeActive( TH_PHYSICS );
- }
- }
- }
- /*
- ============
- Damage
- this entity that is being damaged
- inflictor entity that is causing the damage
- attacker entity that caused the inflictor to damage targ
- example: this=monster, inflictor=rocket, attacker=player
- dir direction of the attack for knockback in global space
- damageDef an idDict with all the options for damage effects
- inflictor, attacker, dir, and point can be NULL for environmental effects
- ============
- */
- void idPlayer::Damage( idEntity *inflictor, idEntity *attacker, const idVec3 &dir,
- const char *damageDefName, const float damageScale, const int location ) {
- idVec3 kick;
- int damage;
- int armorSave;
- SetTimeState ts( timeGroup );
-
- if ( !fl.takedamage || noclip || spectating || gameLocal.inCinematic ) {
- return;
- }
- if ( !inflictor ) {
- inflictor = gameLocal.world;
- }
- if ( !attacker ) {
- attacker = gameLocal.world;
- }
- if ( attacker->IsType( idAI::Type ) ) {
- if ( PowerUpActive( BERSERK ) ) {
- return;
- }
- // don't take damage from monsters during influences
- if ( influenceActive != 0 ) {
- return;
- }
- }
- const idDeclEntityDef *damageDef = gameLocal.FindEntityDef( damageDefName, false );
- if ( !damageDef ) {
- gameLocal.Warning( "Unknown damageDef '%s'", damageDefName );
- return;
- }
- if ( damageDef->dict.GetBool( "ignore_player" ) ) {
- return;
- }
- // determine knockback
- int knockback = 0;
- damageDef->dict.GetInt( "knockback", "20", knockback );
- if ( knockback != 0 && !fl.noknockback ) {
- float attackerPushScale = 0.0f;
- if ( attacker == this ) {
- damageDef->dict.GetFloat( "attackerPushScale", "0", attackerPushScale );
- } else {
- attackerPushScale = 1.0f;
- }
- idVec3 kick = dir;
- kick.Normalize();
- kick *= g_knockback.GetFloat() * knockback * attackerPushScale / 200.0f;
- physicsObj.SetLinearVelocity( physicsObj.GetLinearVelocity() + kick );
- // set the timer so that the player can't cancel out the movement immediately
- physicsObj.SetKnockBack( idMath::ClampInt( 50, 200, knockback * 2 ) );
- if ( common->IsServer() ) {
- idBitMsg msg;
- byte msgBuf[MAX_EVENT_PARAM_SIZE];
- msg.InitWrite( msgBuf, sizeof( msgBuf ) );
- msg.WriteFloat( physicsObj.GetLinearVelocity()[0] );
- msg.WriteFloat( physicsObj.GetLinearVelocity()[1] );
- msg.WriteFloat( physicsObj.GetLinearVelocity()[2] );
- msg.WriteByte( idMath::ClampInt( 50, 200, knockback * 2 ) );
- ServerSendEvent( idPlayer::EVENT_KNOCKBACK, &msg, false );
- }
- }
- // If this is a locally controlled MP client, don't apply damage effects predictively here.
- // Local clients will see the damage feedback (view kick, etc) when their health changes
- // in a snapshot. This ensures that any feedback the local player sees is in sync with
- // his actual health reported by the server.
- if( common->IsMultiplayer() && common->IsClient() && IsLocallyControlled() ) {
- return;
- }
-
- CalcDamagePoints( inflictor, attacker, &damageDef->dict, damageScale, location, &damage, &armorSave );
- // give feedback on the player view and audibly when armor is helping
- if ( armorSave ) {
- inventory.armor -= armorSave;
- if ( gameLocal.time > lastArmorPulse + 200 ) {
- StartSound( "snd_hitArmor", SND_CHANNEL_ITEM, 0, false, NULL );
- }
- lastArmorPulse = gameLocal.time;
- }
-
- if ( damageDef->dict.GetBool( "burn" ) ) {
- StartSound( "snd_burn", SND_CHANNEL_BODY3, 0, false, NULL );
- } else if ( damageDef->dict.GetBool( "no_air" ) ) {
- if ( !armorSave && health > 0 ) {
- StartSound( "snd_airGasp", SND_CHANNEL_ITEM, 0, false, NULL );
- }
- }
- if ( g_debugDamage.GetInteger() ) {
- gameLocal.Printf( "client:%02d\tdamage type:%s\t\thealth:%03d\tdamage:%03d\tarmor:%03d\n", entityNumber, damageDef->GetName(), health, damage, armorSave );
- }
- if ( common->IsMultiplayer() && IsLocallyControlled() ) {
- ControllerShakeFromDamage( damage );
- }
- // The client needs to know the final damage amount for predictive pain animations.
- const int finalDamage = AdjustDamageAmount( damage );
- if ( health > 0 ) {
- // force a blink
- blink_time = 0;
- // let the anim script know we took damage
- AI_PAIN = Pain( inflictor, attacker, damage, dir, location );
- }
- // Only actually deal the damage here in singleplayer and for locally controlled servers.
- if ( !common->IsMultiplayer() || common->IsServer() ) {
- // Server will deal his damage normally
- ServerDealDamage( finalDamage, *inflictor, *attacker, dir, damageDefName, location );
- } else if ( attacker->GetEntityNumber() == gameLocal.GetLocalClientNum() ) {
- // Clients send a reliable message to the server with the parameters of the hit. The
- // server should make sure the client still has line-of-sight to its target before
- // actually applying the damage.
- byte msgBuffer[MAX_GAME_MESSAGE_SIZE];
- idBitMsg msg;
- msg.InitWrite( msgBuffer, sizeof( msgBuffer ) );
- msg.BeginWriting();
- msg.WriteShort( attacker->GetEntityNumber() );
- msg.WriteShort( GetEntityNumber() ); // victim
- msg.WriteVectorFloat( dir );
- msg.WriteLong( damageDef->Index() );
- msg.WriteFloat( damageScale );
- msg.WriteLong( location );
- idLobbyBase & lobby = session->GetActingGameStateLobbyBase();
- lobby.SendReliableToHost( GAME_RELIABLE_MESSAGE_CLIENT_HITSCAN_HIT, msg );
- }
- lastDamageDef = damageDef->Index();
- lastDamageDir = dir;
- lastDamageDir.Normalize();
- lastDamageLocation = location;
- }
- /*
- ===========
- idPlayer::Teleport
- ============
- */
- void idPlayer::Teleport( const idVec3 &origin, const idAngles &angles, idEntity *destination ) {
- idVec3 org;
- if ( weapon.GetEntity() ) {
- weapon.GetEntity()->LowerWeapon();
- }
- SetOrigin( origin + idVec3( 0, 0, CM_CLIP_EPSILON ) );
- if ( !common->IsMultiplayer() && GetFloorPos( 16.0f, org ) ) {
- SetOrigin( org );
- }
- // clear the ik heights so model doesn't appear in the wrong place
- walkIK.EnableAll();
- GetPhysics()->SetLinearVelocity( vec3_origin );
- SetViewAngles( angles );
- legsYaw = 0.0f;
- idealLegsYaw = 0.0f;
- oldViewYaw = viewAngles.yaw;
- if ( common->IsMultiplayer() ) {
- playerView.Flash( colorWhite, 140 );
- }
- UpdateVisuals();
- teleportEntity = destination;
- if ( !common->IsClient() && !noclip ) {
- if ( common->IsMultiplayer() ) {
- // kill anything at the new position or mark for kill depending on immediate or delayed teleport
- gameLocal.KillBox( this, destination != NULL );
- } else {
- // kill anything at the new position
- gameLocal.KillBox( this, true );
- }
- }
- if ( PowerUpActive( HELLTIME ) ) {
- StopHelltime();
- }
- }
- /*
- ====================
- idPlayer::SetPrivateCameraView
- ====================
- */
- void idPlayer::SetPrivateCameraView( idCamera *camView ) {
- privateCameraView = camView;
- if ( camView ) {
- StopFiring();
- Hide();
- } else {
- if ( !spectating ) {
- Show();
- }
- }
- }
- /*
- ====================
- idPlayer::DefaultFov
- Returns the base FOV
- ====================
- */
- float idPlayer::DefaultFov() const {
- float fov;
- fov = g_fov.GetFloat();
- if ( common->IsMultiplayer() ) {
- if ( fov < 80.0f ) {
- return 80.0f;
- } else if ( fov > 120.0f ) {
- return 120.0f;
- }
- }
- return fov;
- }
- /*
- ====================
- idPlayer::CalcFov
- Fixed fov at intermissions, otherwise account for fov variable and zooms.
- ====================
- */
- float idPlayer::CalcFov( bool honorZoom ) {
- float fov;
- if ( fxFov ) {
- return DefaultFov() + 10.0f + cos( ( gameLocal.time + 2000 ) * 0.01 ) * 10.0f;
- }
- if ( influenceFov ) {
- return influenceFov;
- }
- if ( zoomFov.IsDone( gameLocal.time ) ) {
- fov = ( honorZoom && usercmd.buttons & BUTTON_ZOOM ) && weapon.GetEntity() ? weapon.GetEntity()->GetZoomFov() : DefaultFov();
- } else {
- fov = zoomFov.GetCurrentValue( gameLocal.time );
- }
- // bound normal viewsize
- if ( fov < 1 ) {
- fov = 1;
- } else if ( fov > 179 ) {
- fov = 179;
- }
- return fov;
- }
- /*
- ==============
- idPlayer::GunTurningOffset
- generate a rotational offset for the gun based on the view angle
- history in loggedViewAngles
- ==============
- */
- idAngles idPlayer::GunTurningOffset() {
- idAngles a;
- a.Zero();
- if ( gameLocal.framenum < NUM_LOGGED_VIEW_ANGLES ) {
- return a;
- }
- idAngles current = loggedViewAngles[ gameLocal.framenum & (NUM_LOGGED_VIEW_ANGLES-1) ];
- idAngles av, base;
- int weaponAngleOffsetAverages;
- float weaponAngleOffsetScale, weaponAngleOffsetMax;
- weapon.GetEntity()->GetWeaponAngleOffsets( &weaponAngleOffsetAverages, &weaponAngleOffsetScale, &weaponAngleOffsetMax );
- av = current;
- // calcualte this so the wrap arounds work properly
- for ( int j = 1 ; j < weaponAngleOffsetAverages ; j++ ) {
- idAngles a2 = loggedViewAngles[ ( gameLocal.framenum - j ) & (NUM_LOGGED_VIEW_ANGLES-1) ];
- idAngles delta = a2 - current;
- if ( delta[1] > 180 ) {
- delta[1] -= 360;
- } else if ( delta[1] < -180 ) {
- delta[1] += 360;
- }
- av += delta * ( 1.0f / weaponAngleOffsetAverages );
- }
- a = ( av - current ) * weaponAngleOffsetScale;
- for ( int i = 0 ; i < 3 ; i++ ) {
- if ( a[i] < -weaponAngleOffsetMax ) {
- a[i] = -weaponAngleOffsetMax;
- } else if ( a[i] > weaponAngleOffsetMax ) {
- a[i] = weaponAngleOffsetMax;
- }
- }
- return a;
- }
- /*
- ==============
- idPlayer::GunAcceleratingOffset
- generate a positional offset for the gun based on the movement
- history in loggedAccelerations
- ==============
- */
- idVec3 idPlayer::GunAcceleratingOffset() {
- idVec3 ofs;
- float weaponOffsetTime, weaponOffsetScale;
- ofs.Zero();
- weapon.GetEntity()->GetWeaponTimeOffsets( &weaponOffsetTime, &weaponOffsetScale );
- int stop = currentLoggedAccel - NUM_LOGGED_ACCELS;
- if ( stop < 0 ) {
- stop = 0;
- }
- for ( int i = currentLoggedAccel-1 ; i > stop ; i-- ) {
- loggedAccel_t *acc = &loggedAccel[i&(NUM_LOGGED_ACCELS-1)];
- float f;
- float t = gameLocal.time - acc->time;
- if ( t >= weaponOffsetTime ) {
- break; // remainder are too old to care about
- }
- f = t / weaponOffsetTime;
- f = ( cos( f * 2.0f * idMath::PI ) - 1.0f ) * 0.5f;
- ofs += f * weaponOffsetScale * acc->dir;
- }
- return ofs;
- }
- /*
- ==============
- idPlayer::CalculateViewWeaponPos
- Calculate the bobbing position of the view weapon
- ==============
- */
- void idPlayer::CalculateViewWeaponPos( idVec3 &origin, idMat3 &axis ) {
- float scale;
- float fracsin;
- idAngles angles;
- int delta;
- // CalculateRenderView must have been called first
- const idVec3 &viewOrigin = firstPersonViewOrigin;
- const idMat3 &viewAxis = firstPersonViewAxis;
- // these cvars are just for hand tweaking before moving a value to the weapon def
- idVec3 gunpos( g_gun_x.GetFloat(), g_gun_y.GetFloat(), g_gun_z.GetFloat() );
- // as the player changes direction, the gun will take a small lag
- idVec3 gunOfs = GunAcceleratingOffset();
- origin = viewOrigin + ( gunpos + gunOfs ) * viewAxis;
- // on odd legs, invert some angles
- if ( bobCycle & 128 ) {
- scale = -xyspeed;
- } else {
- scale = xyspeed;
- }
- // gun angles from bobbing
- angles.roll = scale * bobfracsin * 0.005f;
- angles.yaw = scale * bobfracsin * 0.01f;
- angles.pitch = xyspeed * bobfracsin * 0.005f;
- // gun angles from turning
- if ( common->IsMultiplayer() ) {
- idAngles offset = GunTurningOffset();
- offset *= g_mpWeaponAngleScale.GetFloat();
- angles += offset;
- } else {
- angles += GunTurningOffset();
- }
- idVec3 gravity = physicsObj.GetGravityNormal();
- // drop the weapon when landing after a jump / fall
- delta = gameLocal.time - landTime;
- if ( delta < LAND_DEFLECT_TIME ) {
- origin -= gravity * ( landChange*0.25f * delta / LAND_DEFLECT_TIME );
- } else if ( delta < LAND_DEFLECT_TIME + LAND_RETURN_TIME ) {
- origin -= gravity * ( landChange*0.25f * (LAND_DEFLECT_TIME + LAND_RETURN_TIME - delta) / LAND_RETURN_TIME );
- }
- // speed sensitive idle drift
- scale = xyspeed + 40.0f;
- fracsin = scale * sin( MS2SEC( gameLocal.time ) ) * 0.01f;
- angles.roll += fracsin;
- angles.yaw += fracsin;
- angles.pitch += fracsin;
- // decoupled weapon aiming in head mounted displays
- angles.pitch += independentWeaponPitchAngle;
- const idMat3 anglesMat = angles.ToMat3();
- const idMat3 scaledMat = anglesMat * g_gunScale.GetFloat();
- axis = scaledMat * viewAxis;
- }
- /*
- ===============
- idPlayer::OffsetThirdPersonView
- ===============
- */
- void idPlayer::OffsetThirdPersonView( float angle, float range, float height, bool clip ) {
- idVec3 view;
- idVec3 focusAngles;
- trace_t trace;
- idVec3 focusPoint;
- float focusDist;
- float forwardScale, sideScale;
- idVec3 origin;
- idAngles angles;
- idMat3 axis;
- idBounds bounds;
- angles = viewAngles;
- GetViewPos( origin, axis );
- if ( angle ) {
- angles.pitch = 0.0f;
- }
- if ( angles.pitch > 45.0f ) {
- angles.pitch = 45.0f; // don't go too far overhead
- }
- focusPoint = origin + angles.ToForward() * THIRD_PERSON_FOCUS_DISTANCE;
- focusPoint.z += height;
- view = origin;
- view.z += 8 + height;
- angles.pitch *= 0.5f;
- renderView->viewaxis = angles.ToMat3() * physicsObj.GetGravityAxis();
- idMath::SinCos( DEG2RAD( angle ), sideScale, forwardScale );
- view -= range * forwardScale * renderView->viewaxis[ 0 ];
- view += range * sideScale * renderView->viewaxis[ 1 ];
- if ( clip ) {
- // trace a ray from the origin to the viewpoint to make sure the view isn't
- // in a solid block. Use an 8 by 8 block to prevent the view from near clipping anything
- bounds = idBounds( idVec3( -4, -4, -4 ), idVec3( 4, 4, 4 ) );
- gameLocal.clip.TraceBounds( trace, origin, view, bounds, MASK_SOLID, this );
- if ( trace.fraction != 1.0f ) {
- view = trace.endpos;
- view.z += ( 1.0f - trace.fraction ) * 32.0f;
- // try another trace to this position, because a tunnel may have the ceiling
- // close enough that this is poking out
- gameLocal.clip.TraceBounds( trace, origin, view, bounds, MASK_SOLID, this );
- view = trace.endpos;
- }
- }
- // select pitch to look at focus point from vieword
- focusPoint -= view;
- focusDist = idMath::Sqrt( focusPoint[0] * focusPoint[0] + focusPoint[1] * focusPoint[1] );
- if ( focusDist < 1.0f ) {
- focusDist = 1.0f; // should never happen
- }
- angles.pitch = - RAD2DEG( atan2( focusPoint.z, focusDist ) );
- angles.yaw -= angle;
- renderView->vieworg = view;
- renderView->viewaxis = angles.ToMat3() * physicsObj.GetGravityAxis();
- renderView->viewID = 0;
- }
- /*
- ===============
- idPlayer::GetEyePosition
- ===============
- */
- idVec3 idPlayer::GetEyePosition() const {
- idVec3 org;
-
- // use the smoothed origin if spectating another player in multiplayer
- if ( common->IsClient() && !IsLocallyControlled() ) {
- org = smoothedOrigin;
- } else {
- org = GetPhysics()->GetOrigin();
- }
- return org + ( GetPhysics()->GetGravityNormal() * -eyeOffset.z );
- }
- /*
- ===============
- idPlayer::GetViewPos
- ===============
- */
- void idPlayer::GetViewPos( idVec3 &origin, idMat3 &axis ) const {
- idAngles angles;
- // if dead, fix the angle and don't add any kick
- if ( health <= 0 ) {
- angles.yaw = viewAngles.yaw;
- angles.roll = 40;
- angles.pitch = -15;
- axis = angles.ToMat3();
- origin = GetEyePosition();
- } else {
- origin = GetEyePosition() + viewBob;
- angles = viewAngles + viewBobAngles + playerView.AngleOffset();
- axis = angles.ToMat3() * physicsObj.GetGravityAxis();
- // Move pivot point down so looking straight ahead is a no-op on the Z
- const idVec3 & gravityVector = physicsObj.GetGravityNormal();
- origin += gravityVector * g_viewNodalZ.GetFloat();
- // adjust the origin based on the camera nodal distance (eye distance from neck)
- origin += axis[0] * g_viewNodalX.GetFloat() + axis[2] * g_viewNodalZ.GetFloat();
- }
- }
- /*
- ===============
- idPlayer::CalculateFirstPersonView
- ===============
- */
- void idPlayer::CalculateFirstPersonView() {
- if ( ( pm_modelView.GetInteger() == 1 ) || ( ( pm_modelView.GetInteger() == 2 ) && ( health <= 0 ) ) ) {
- // Displays the view from the point of view of the "camera" joint in the player model
- idMat3 axis;
- idVec3 origin;
- idAngles ang;
- ang = viewBobAngles + playerView.AngleOffset();
- ang.yaw += viewAxis[ 0 ].ToYaw();
-
- jointHandle_t joint = animator.GetJointHandle( "camera" );
- animator.GetJointTransform( joint, gameLocal.time, origin, axis );
- firstPersonViewOrigin = ( origin + modelOffset ) * ( viewAxis * physicsObj.GetGravityAxis() ) + physicsObj.GetOrigin() + viewBob;
- firstPersonViewAxis = axis * ang.ToMat3() * physicsObj.GetGravityAxis();
- } else {
- // offset for local bobbing and kicks
- GetViewPos( firstPersonViewOrigin, firstPersonViewAxis );
- #if 0
- // shakefrom sound stuff only happens in first person
- firstPersonViewAxis = firstPersonViewAxis * playerView.ShakeAxis();
- #endif
- }
- }
- /*
- ==================
- idPlayer::GetRenderView
- Returns the renderView that was calculated for this tic
- ==================
- */
- renderView_t *idPlayer::GetRenderView() {
- return renderView;
- }
- /*
- ==================
- idPlayer::CalculateRenderView
- create the renderView for the current tic
- ==================
- */
- void idPlayer::CalculateRenderView() {
- int i;
- float range;
- if ( !renderView ) {
- renderView = new (TAG_ENTITY) renderView_t;
- }
- memset( renderView, 0, sizeof( *renderView ) );
- // copy global shader parms
- for( i = 0; i < MAX_GLOBAL_SHADER_PARMS; i++ ) {
- renderView->shaderParms[ i ] = gameLocal.globalShaderParms[ i ];
- }
- renderView->globalMaterial = gameLocal.GetGlobalMaterial();
- renderView->time[0] = gameLocal.slow.time;
- renderView->time[1] = gameLocal.fast.time;
- renderView->viewID = 0;
- // check if we should be drawing from a camera's POV
- if ( !noclip && (gameLocal.GetCamera() || privateCameraView) ) {
- // get origin, axis, and fov
- if ( privateCameraView ) {
- privateCameraView->GetViewParms( renderView );
- } else {
- gameLocal.GetCamera()->GetViewParms( renderView );
- }
- } else {
- if ( g_stopTime.GetBool() ) {
- renderView->vieworg = firstPersonViewOrigin;
- renderView->viewaxis = firstPersonViewAxis;
- if ( !pm_thirdPerson.GetBool() ) {
- // set the viewID to the clientNum + 1, so we can suppress the right player bodies and
- // allow the right player view weapons
- renderView->viewID = entityNumber + 1;
- }
- } else if ( pm_thirdPerson.GetBool() ) {
- OffsetThirdPersonView( pm_thirdPersonAngle.GetFloat(), pm_thirdPersonRange.GetFloat(), pm_thirdPersonHeight.GetFloat(), pm_thirdPersonClip.GetBool() );
- } else if ( pm_thirdPersonDeath.GetBool() ) {
- range = gameLocal.time < minRespawnTime ? ( gameLocal.time + RAGDOLL_DEATH_TIME - minRespawnTime ) * ( 120.0f / RAGDOLL_DEATH_TIME ) : 120.0f;
- OffsetThirdPersonView( 0.0f, 20.0f + range, 0.0f, false );
- } else {
- renderView->vieworg = firstPersonViewOrigin;
- renderView->viewaxis = firstPersonViewAxis;
- // set the viewID to the clientNum + 1, so we can suppress the right player bodies and
- // allow the right player view weapons
- renderView->viewID = entityNumber + 1;
- }
-
- gameLocal.CalcFov( CalcFov( true ), renderView->fov_x, renderView->fov_y );
- }
- if ( renderView->fov_y == 0 ) {
- common->Error( "renderView->fov_y == 0" );
- }
- if ( g_showviewpos.GetBool() ) {
- gameLocal.Printf( "%s : %s\n", renderView->vieworg.ToString(), renderView->viewaxis.ToAngles().ToString() );
- }
- }
- /*
- =============
- idPlayer::AddAIKill
- =============
- */
- void idPlayer::AddAIKill() {
- int max_souls;
- int ammo_souls;
- if ( ( weapon_soulcube < 0 ) || ( inventory.weapons & ( 1 << weapon_soulcube ) ) == 0 ) {
- return;
- }
- ammo_souls = idWeapon::GetAmmoNumForName( "ammo_souls" );
- max_souls = inventory.MaxAmmoForAmmoClass( this, "ammo_souls" );
- const int currentSoulAmmo = inventory.GetInventoryAmmoForType( ammo_souls );
- if ( currentSoulAmmo < max_souls ) {
- inventory.SetInventoryAmmoForType( ammo_souls, currentSoulAmmo + 1 );
- if ( inventory.GetInventoryAmmoForType( ammo_souls ) >= max_souls ) {
-
- if ( hud ) {
- hud->UpdateSoulCube( true );
- }
- StartSound( "snd_soulcube_ready", SND_CHANNEL_ANY, 0, false, NULL );
- }
- }
- }
- /*
- =============
- idPlayer::SetSoulCubeProjectile
- =============
- */
- void idPlayer::SetSoulCubeProjectile( idProjectile *projectile ) {
- soulCubeProjectile = projectile;
- }
- /*
- =============
- idPlayer::AddProjectilesFired
- =============
- */
- void idPlayer::AddProjectilesFired( int count ) {
- numProjectilesFired += count;
- }
- /*
- =============
- idPlayer::AddProjectileHites
- =============
- */
- void idPlayer::AddProjectileHits( int count ) {
- numProjectileHits += count;
- }
- /*
- =============
- idPlayer::SetLastHitTime
- =============
- */
- void idPlayer::SetLastHitTime( int time ) {
- idPlayer *aimed = NULL;
- if ( time && lastHitTime != time ) {
- lastHitToggle ^= 1;
- }
- lastHitTime = time;
- if ( !time ) {
- // level start and inits
- return;
- }
- if ( common->IsMultiplayer() && ( time - lastSndHitTime ) > 10 ) {
- lastSndHitTime = time;
- StartSound( "snd_hit_feedback", SND_CHANNEL_ANY, SSF_PRIVATE_SOUND, false, NULL );
- }
-
- if ( hud ) {
- hud->CombatCursorFlash();
- }
- if ( MPAim != -1 ) {
- if ( gameLocal.entities[ MPAim ] && gameLocal.entities[ MPAim ]->IsType( idPlayer::Type ) ) {
- aimed = static_cast< idPlayer * >( gameLocal.entities[ MPAim ] );
- }
- assert( aimed );
- // full highlight, no fade till loosing aim
- if ( hud ) {
- int color = 0;
- if ( aimed ) {
- color = aimed->team + 1;
- }
- hud->TriggerHitTarget( true, session->GetActingGameStateLobbyBase().GetLobbyUserName( gameLocal.lobbyUserIDs[ MPAim ] ), color );
- }
- MPAimHighlight = true;
- MPAimFadeTime = 0;
- } else if ( lastMPAim != -1 ) {
- if ( gameLocal.entities[ lastMPAim ] && gameLocal.entities[ lastMPAim ]->IsType( idPlayer::Type ) ) {
- aimed = static_cast< idPlayer * >( gameLocal.entities[ lastMPAim ] );
- }
- assert( aimed );
- // start fading right away
- if ( hud ) {
- int color = 0;
- if ( aimed ) {
- color = aimed->team + 1;
- }
- hud->TriggerHitTarget( true, session->GetActingGameStateLobbyBase().GetLobbyUserName( gameLocal.lobbyUserIDs[ lastMPAim ] ), color );
- hud->TriggerHitTarget( false, "" );
- }
- MPAimHighlight = false;
- MPAimFadeTime = gameLocal.realClientTime;
- }
- }
- /*
- =============
- idPlayer::SetInfluenceLevel
- =============
- */
- void idPlayer::SetInfluenceLevel( int level ) {
- if ( level != influenceActive ) {
- if ( level ) {
- for ( idEntity *ent = gameLocal.spawnedEntities.Next(); ent != NULL; ent = ent->spawnNode.Next() ) {
- if ( ent->IsType( idProjectile::Type ) ) {
- // remove all projectiles
- ent->PostEventMS( &EV_Remove, 0 );
- }
- }
- if ( weaponEnabled && weapon.GetEntity() ) {
- weapon.GetEntity()->EnterCinematic();
- }
- } else {
- physicsObj.SetLinearVelocity( vec3_origin );
- if ( weaponEnabled && weapon.GetEntity() ) {
- weapon.GetEntity()->ExitCinematic();
- }
- }
- influenceActive = level;
- }
- }
- /*
- =============
- idPlayer::SetInfluenceView
- =============
- */
- void idPlayer::SetInfluenceView( const char *mtr, const char *skinname, float radius, idEntity *ent ) {
- influenceMaterial = NULL;
- influenceEntity = NULL;
- influenceSkin = NULL;
- if ( mtr && *mtr ) {
- influenceMaterial = declManager->FindMaterial( mtr );
- }
- if ( skinname && *skinname ) {
- influenceSkin = declManager->FindSkin( skinname );
- if ( head.GetEntity() ) {
- head.GetEntity()->GetRenderEntity()->shaderParms[ SHADERPARM_TIMEOFFSET ] = -MS2SEC( gameLocal.time );
- }
- UpdateVisuals();
- }
- influenceRadius = radius;
- if ( radius > 0.0f ) {
- influenceEntity = ent;
- }
- }
- /*
- =============
- idPlayer::SetInfluenceFov
- =============
- */
- void idPlayer::SetInfluenceFov( float fov ) {
- influenceFov = fov;
- }
- /*
- ================
- idPlayer::OnLadder
- ================
- */
- bool idPlayer::OnLadder() const {
- return physicsObj.OnLadder();
- }
- /*
- ==================
- idPlayer::Event_GetButtons
- ==================
- */
- void idPlayer::Event_GetButtons() {
- idThread::ReturnInt( usercmd.buttons );
- }
- /*
- ==================
- idPlayer::Event_GetMove
- ==================
- */
- void idPlayer::Event_GetMove() {
- int upmove = ( ( usercmd.buttons & BUTTON_JUMP ) ? 127 : 0 ) - ( ( usercmd.buttons & BUTTON_CROUCH ) ? 127 : 0 );
- idVec3 move( usercmd.forwardmove, usercmd.rightmove, upmove );
- idThread::ReturnVector( move );
- }
- /*
- ================
- idPlayer::Event_GetViewAngles
- ================
- */
- void idPlayer::Event_GetViewAngles() {
- idThread::ReturnVector( idVec3( viewAngles[0], viewAngles[1], viewAngles[2] ) );
- }
- /*
- ==================
- idPlayer::Event_StopFxFov
- ==================
- */
- void idPlayer::Event_StopFxFov() {
- fxFov = false;
- }
- /*
- ==================
- idPlayer::StartFxFov
- ==================
- */
- void idPlayer::StartFxFov( float duration ) {
- fxFov = true;
- PostEventSec( &EV_Player_StopFxFov, duration );
- }
- /*
- ==================
- idPlayer::Event_EnableWeapon
- ==================
- */
- void idPlayer::Event_EnableWeapon() {
- hiddenWeapon = gameLocal.world->spawnArgs.GetBool( "no_Weapons" );
- weaponEnabled = true;
- if ( weapon.GetEntity() ) {
- weapon.GetEntity()->ExitCinematic();
- }
- }
- /*
- ==================
- idPlayer::Event_DisableWeapon
- ==================
- */
- void idPlayer::Event_DisableWeapon() {
- hiddenWeapon = gameLocal.world->spawnArgs.GetBool( "no_Weapons" );
- weaponEnabled = false;
- if ( weapon.GetEntity() ) {
- weapon.GetEntity()->EnterCinematic();
- }
- }
- /*
- ==================
- idPlayer::Event_GiveInventoryItem
- ==================
- */
- void idPlayer::Event_GiveInventoryItem( const char* name ) {
- GiveInventoryItem(name);
- }
- /*
- ==================
- idPlayer::Event_RemoveInventoryItem
- ==================
- */
- void idPlayer::Event_RemoveInventoryItem( const char* name ) {
- RemoveInventoryItem(name);
- }
- /*
- ==================
- idPlayer::Event_GetIdealWeapon
- ==================
- */
- void idPlayer::Event_GetIdealWeapon() {
- const char *weapon;
- if ( idealWeapon.Get() >= 0 ) {
- weapon = spawnArgs.GetString( va( "def_weapon%d", idealWeapon.Get() ) );
- idThread::ReturnString( weapon );
- } else {
- idThread::ReturnString( "" );
- }
- }
- /*
- ==================
- idPlayer::Event_SetPowerupTime
- ==================
- */
- void idPlayer::Event_SetPowerupTime( int powerup, int time ) {
- if ( time > 0 ) {
- GivePowerUp( powerup, time, ITEM_GIVE_FEEDBACK | ITEM_GIVE_UPDATE_STATE );
- } else {
- ClearPowerup( powerup );
- }
- }
- /*
- ==================
- idPlayer::Event_IsPowerupActive
- ==================
- */
- void idPlayer::Event_IsPowerupActive( int powerup ) {
- idThread::ReturnInt(this->PowerUpActive(powerup) ? 1 : 0);
- }
- /*
- ==================
- idPlayer::Event_StartWarp
- ==================
- */
- void idPlayer::Event_StartWarp() {
- playerView.AddWarp( idVec3( 0, 0, 0 ), SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, 100, 1000 );
- }
- /*
- ==================
- idPlayer::Event_StopHelltime
- ==================
- */
- void idPlayer::Event_StopHelltime( int mode ) {
- if ( mode == 1 ) {
- StopHelltime( true );
- }
- else {
- StopHelltime( false );
- }
- }
- /*
- ==================
- idPlayer::Event_WeaponAvailable
- ==================
- */
- void idPlayer::Event_WeaponAvailable( const char* name ) {
- idThread::ReturnInt( WeaponAvailable(name) ? 1 : 0 );
- }
- bool idPlayer::WeaponAvailable( const char* name ) {
- for( int i = 0; i < MAX_WEAPONS; i++ ) {
- if ( inventory.weapons & ( 1 << i ) ) {
- const char *weap = spawnArgs.GetString( va( "def_weapon%d", i ) );
- if ( !idStr::Cmp( weap, name ) ) {
- return true;
- }
- }
- }
- return false;
- }
- /*
- ==================
- idPlayer::Event_GetCurrentWeapon
- ==================
- */
- void idPlayer::Event_GetCurrentWeapon() {
- const char *weapon;
- if ( currentWeapon >= 0 ) {
- weapon = spawnArgs.GetString( va( "def_weapon%d", currentWeapon ) );
- idThread::ReturnString( weapon );
- } else {
- idThread::ReturnString( "" );
- }
- }
- /*
- ==================
- idPlayer::Event_GetPreviousWeapon
- ==================
- */
- void idPlayer::Event_GetPreviousWeapon() {
- const char *weapon;
- if ( previousWeapon >= 0 ) {
- int pw = ( gameLocal.world->spawnArgs.GetBool( "no_Weapons" ) ) ? 0 : previousWeapon;
- weapon = spawnArgs.GetString( va( "def_weapon%d", pw) );
- idThread::ReturnString( weapon );
- } else {
- idThread::ReturnString( spawnArgs.GetString( "def_weapon0" ) );
- }
- }
- /*
- ==================
- idPlayer::Event_SelectWeapon
- ==================
- */
- void idPlayer::Event_SelectWeapon( const char *weaponName ) {
- int i;
- int weaponNum;
- if ( common->IsClient() ) {
- gameLocal.Warning( "Cannot switch weapons from script in multiplayer" );
- return;
- }
- if ( hiddenWeapon && gameLocal.world->spawnArgs.GetBool( "no_Weapons" ) ) {
- idealWeapon = weapon_fists;
- weapon.GetEntity()->HideWeapon();
- return;
- }
- weaponNum = -1;
- for( i = 0; i < MAX_WEAPONS; i++ ) {
- if ( inventory.weapons & ( 1 << i ) ) {
- const char *weap = spawnArgs.GetString( va( "def_weapon%d", i ) );
- if ( !idStr::Cmp( weap, weaponName ) ) {
- weaponNum = i;
- break;
- }
- }
- }
- if ( weaponNum < 0 ) {
- gameLocal.Warning( "%s is not carrying weapon '%s'", name.c_str(), weaponName );
- return;
- }
- hiddenWeapon = false;
- idealWeapon = weaponNum;
- UpdateHudWeapon();
- }
- /*
- ==================
- idPlayer::Event_GetWeaponEntity
- ==================
- */
- void idPlayer::Event_GetWeaponEntity() {
- idThread::ReturnEntity( weapon.GetEntity() );
- }
- /*
- ==================
- idPlayer::Event_OpenPDA
- ==================
- */
- void idPlayer::Event_OpenPDA() {
- if ( !common->IsMultiplayer() ) {
- TogglePDA();
- }
- }
- /*
- ==================
- idPlayer::Event_InPDA
- ==================
- */
- void idPlayer::Event_InPDA() {
- idThread::ReturnInt( objectiveSystemOpen );
- }
- /*
- ==================
- idPlayer::TeleportDeath
- ==================
- */
- void idPlayer::TeleportDeath( int killer ) {
- teleportKiller = killer;
- }
- /*
- ==================
- idPlayer::Event_ExitTeleporter
- ==================
- */
- void idPlayer::Event_ForceOrigin( idVec3 & origin, idAngles & angles ) {
- SetOrigin( origin + idVec3( 0, 0, CM_CLIP_EPSILON ) );
- //SetViewAngles( angles );
- UpdateVisuals();
- }
- /*
- ==================
- idPlayer::Event_ExitTeleporter
- ==================
- */
- void idPlayer::Event_ExitTeleporter() {
- idEntity *exitEnt;
- float pushVel;
- // verify and setup
- exitEnt = teleportEntity.GetEntity();
- if ( !exitEnt ) {
- common->DPrintf( "Event_ExitTeleporter player %d while not being teleported\n", entityNumber );
- return;
- }
- pushVel = exitEnt->spawnArgs.GetFloat( "push", "300" );
- if ( common->IsServer() ) {
- ServerSendEvent( EVENT_EXIT_TELEPORTER, NULL, false );
- }
- SetPrivateCameraView( NULL );
- // setup origin and push according to the exit target
- SetOrigin( exitEnt->GetPhysics()->GetOrigin() + idVec3( 0, 0, CM_CLIP_EPSILON ) );
- SetViewAngles( exitEnt->GetPhysics()->GetAxis().ToAngles() );
- physicsObj.SetLinearVelocity( exitEnt->GetPhysics()->GetAxis()[ 0 ] * pushVel );
- physicsObj.ClearPushedVelocity();
- // teleport fx
- playerView.Flash( colorWhite, 120 );
- // clear the ik heights so model doesn't appear in the wrong place
- walkIK.EnableAll();
- UpdateVisuals();
- StartSound( "snd_teleport_exit", SND_CHANNEL_ANY, 0, false, NULL );
- if ( teleportKiller != -1 ) {
- // we got killed while being teleported
- Damage( gameLocal.entities[ teleportKiller ], gameLocal.entities[ teleportKiller ], vec3_origin, "damage_telefrag", 1.0f, INVALID_JOINT );
- teleportKiller = -1;
- } else {
- // kill anything that would have waited at teleport exit
- gameLocal.KillBox( this );
- }
- teleportEntity = NULL;
- }
- /*
- ================
- idPlayer::ClientThink
- ================
- */
- void idPlayer::ClientThink( const int curTime, const float fraction, const bool predict ) {
- if ( IsLocallyControlled() ) {
- aimAssist.Update();
- }
- UpdateSkinSetup();
- if ( !IsLocallyControlled() ) {
- // ignore attack button of other clients. that's no good for predictions
- usercmd.buttons &= ~BUTTON_ATTACK;
- }
- buttonMask &= usercmd.buttons;
- usercmd.buttons &= ~buttonMask;
- buttonMask &= usercmd.buttons;
- usercmd.buttons &= ~buttonMask;
- if ( mountedObject ) {
- usercmd.forwardmove = 0;
- usercmd.rightmove = 0;
- usercmd.buttons &= ~(BUTTON_JUMP|BUTTON_CROUCH);
- }
- if ( objectiveSystemOpen ) {
- usercmd.forwardmove = 0;
- usercmd.rightmove = 0;
- usercmd.buttons &= ~(BUTTON_JUMP|BUTTON_CROUCH);
- }
- if ( IsLocallyControlled() ) {
- // zooming
- if ( ( usercmd.buttons ^ oldCmd.buttons ) & BUTTON_ZOOM ) {
- if ( ( usercmd.buttons & BUTTON_ZOOM ) && weapon.GetEntity() ) {
- zoomFov.Init( gameLocal.time, 200.0f, CalcFov( false ), weapon.GetEntity()->GetZoomFov() );
- } else {
- zoomFov.Init( gameLocal.time, 200.0f, zoomFov.GetCurrentValue( gameLocal.time ), DefaultFov() );
- }
- }
- }
- // clear the ik before we do anything else so the skeleton doesn't get updated twice
- walkIK.ClearJointMods();
- if ( gameLocal.isNewFrame ) {
- if ( usercmd.impulseSequence != oldImpulseSequence ) {
- PerformImpulse( usercmd.impulse );
- }
- }
- if ( forceScoreBoard ) {
- gameLocal.mpGame.SetScoreboardActive( true );
- }
- AdjustSpeed();
- if ( IsLocallyControlled() ) {
- UpdateViewAngles();
- } else {
- idQuat interpolatedAngles = Slerp( previousViewQuat, nextViewQuat, fraction );
- viewAngles = interpolatedAngles.ToAngles();
- }
- smoothedOriginUpdated = false;
- if ( !af.IsActive() ) {
- AdjustBodyAngles();
- }
- if ( !isLagged ) {
- // don't allow client to move when lagged
- if ( IsLocallyControlled() ) {
- // Locally-controlled clients are authoritative on their positions, so they can move normally.
- Move();
- usercmd.pos = physicsObj.GetOrigin();
- } else {
- // Non-locally controlled players are interpolated.
- Move_Interpolated(fraction);
- }
- }
- if ( !g_stopTime.GetBool() ) {
- if ( !noclip && !spectating && ( health > 0 ) && !IsHidden() ) {
- TouchTriggers();
- }
- }
- // update GUIs, Items, and character interactions
- UpdateFocus();
- // service animations
- if ( !spectating && !af.IsActive() ) {
- UpdateConditions();
- UpdateAnimState();
- CheckBlink();
- }
- // clear out our pain flag so we can tell if we recieve any damage between now and the next time we think
- AI_PAIN = false;
- UpdateLocation();
- // calculate the exact bobbed view position, which is used to
- // position the view weapon, among other things
- CalculateFirstPersonView();
- // this may use firstPersonView, or a thirdPerson / camera view
- CalculateRenderView();
- if ( !gameLocal.inCinematic && weapon.GetEntity() && ( health > 0 ) && !( common->IsMultiplayer() && spectating ) ) {
- UpdateWeapon();
- }
- UpdateFlashlight();
- UpdateHud();
- if ( gameLocal.isNewFrame ) {
- UpdatePowerUps();
- }
- UpdateDeathSkin( false );
- renderEntity_t * headRenderEnt = NULL;
- if ( head.GetEntity() ) {
- headRenderEnt = head.GetEntity()->GetRenderEntity();
- }
- if ( headRenderEnt ) {
- if ( influenceSkin ) {
- headRenderEnt->customSkin = influenceSkin;
- } else {
- headRenderEnt->customSkin = NULL;
- }
- }
- if ( common->IsMultiplayer() || g_showPlayerShadow.GetBool() ) {
- renderEntity.suppressShadowInViewID = 0;
- if ( headRenderEnt ) {
- headRenderEnt->suppressShadowInViewID = 0;
- }
- } else {
- renderEntity.suppressShadowInViewID = entityNumber+1;
- if ( headRenderEnt ) {
- headRenderEnt->suppressShadowInViewID = entityNumber+1;
- }
- }
- // never cast shadows from our first-person muzzle flashes
- renderEntity.suppressShadowInLightID = LIGHTID_VIEW_MUZZLE_FLASH + entityNumber;
- if ( headRenderEnt ) {
- headRenderEnt->suppressShadowInLightID = LIGHTID_VIEW_MUZZLE_FLASH + entityNumber;
- }
- if ( !gameLocal.inCinematic ) {
- UpdateAnimation();
- }
- if ( enviroSuitLight.IsValid() ) {
- idAngles lightAng = firstPersonViewAxis.ToAngles();
- idVec3 lightOrg = firstPersonViewOrigin;
- const idDict *lightDef = gameLocal.FindEntityDefDict( "envirosuit_light", false );
- idVec3 enviroOffset = lightDef->GetVector( "enviro_offset" );
- idVec3 enviroAngleOffset = lightDef->GetVector( "enviro_angle_offset" );
- lightOrg += (enviroOffset.x * firstPersonViewAxis[0]);
- lightOrg += (enviroOffset.y * firstPersonViewAxis[1]);
- lightOrg += (enviroOffset.z * firstPersonViewAxis[2]);
- lightAng.pitch += enviroAngleOffset.x;
- lightAng.yaw += enviroAngleOffset.y;
- lightAng.roll += enviroAngleOffset.z;
- enviroSuitLight.GetEntity()->GetPhysics()->SetOrigin( lightOrg );
- enviroSuitLight.GetEntity()->GetPhysics()->SetAxis( lightAng.ToMat3() );
- enviroSuitLight.GetEntity()->UpdateVisuals();
- enviroSuitLight.GetEntity()->Present();
- }
- if ( common->IsMultiplayer() ) {
- DrawPlayerIcons();
- }
- Present();
- UpdateDamageEffects();
- LinkCombat();
- // stereo rendering laser sight that replaces the crosshair
- UpdateLaserSight();
- if ( gameLocal.isNewFrame && IsLocallyControlled() ) {
- playerView.CalculateShake();
- }
- // determine if portal sky is in pvs
- pvsHandle_t clientPVS = gameLocal.pvs.SetupCurrentPVS( GetPVSAreas(), GetNumPVSAreas() );
- gameLocal.portalSkyActive = gameLocal.pvs.CheckAreasForPortalSky( clientPVS, GetPhysics()->GetOrigin() );
- gameLocal.pvs.FreeCurrentPVS( clientPVS );
- //InterpolatePhysics( fraction );
- // Make sure voice groups are set to the right team
- if ( common->IsMultiplayer() && session->GetState() >= idSession::INGAME && entityNumber < MAX_CLIENTS ) { // The entityNumber < MAX_CLIENTS seems to quiet the static analyzer
- // Make sure we're on the right team (at the lobby level)
- const int voiceTeam = spectating ? LOBBY_SPECTATE_TEAM_FOR_VOICE_CHAT : team;
- //idLib::Printf( "CLIENT: Sending voice %i / %i\n", entityNumber, voiceTeam );
- // Update lobby team
- session->GetActingGameStateLobbyBase().SetLobbyUserTeam( gameLocal.lobbyUserIDs[ entityNumber ], voiceTeam );
-
- // Update voice groups to match in case something changed
- session->SetVoiceGroupsToTeams();
- }
- }
- /*
- ================
- idPlayer::GetPhysicsToVisualTransform
- ================
- */
- bool idPlayer::GetPhysicsToVisualTransform( idVec3 &origin, idMat3 &axis ) {
- if ( af.IsActive() ) {
- af.GetPhysicsToVisualTransform( origin, axis );
- return true;
- }
- // smoothen the rendered origin and angles of other clients
- // smooth self origin if snapshots are telling us prediction is off
- if ( common->IsClient() && gameLocal.framenum >= smoothedFrame && ( !IsLocallyControlled() || selfSmooth ) ) {
- // render origin and axis
- idMat3 renderAxis = viewAxis * GetPhysics()->GetAxis();
- idVec3 renderOrigin = GetPhysics()->GetOrigin() + modelOffset * renderAxis;
- // update the smoothed origin
- if ( !smoothedOriginUpdated ) {
- idVec2 originDiff = renderOrigin.ToVec2() - smoothedOrigin.ToVec2();
- if ( originDiff.LengthSqr() < Square( 100.0f ) ) {
- // smoothen by pushing back to the previous position
- if ( selfSmooth ) {
- assert( IsLocallyControlled() );
- renderOrigin.ToVec2() -= net_clientSelfSmoothing.GetFloat() * originDiff;
- } else {
- renderOrigin.ToVec2() -= gameLocal.clientSmoothing * originDiff;
- }
- }
- smoothedOrigin = renderOrigin;
- smoothedFrame = gameLocal.framenum;
- smoothedOriginUpdated = true;
- }
- axis = idAngles( 0.0f, viewAngles.yaw, 0.0f ).ToMat3();
- origin = ( smoothedOrigin - GetPhysics()->GetOrigin() ) * axis.Transpose();
- } else {
- axis = viewAxis;
- origin = modelOffset;
- }
- return true;
- }
- /*
- ================
- idPlayer::GetPhysicsToSoundTransform
- ================
- */
- bool idPlayer::GetPhysicsToSoundTransform( idVec3 &origin, idMat3 &axis ) {
- idCamera *camera;
- if ( privateCameraView ) {
- camera = privateCameraView;
- } else {
- camera = gameLocal.GetCamera();
- }
- if ( camera ) {
- renderView_t view;
- memset( &view, 0, sizeof( view ) );
- camera->GetViewParms( &view );
- origin = view.vieworg;
- axis = view.viewaxis;
- return true;
- } else {
- return idActor::GetPhysicsToSoundTransform( origin, axis );
- }
- }
- /*
- ================
- idPlayer::HandleUserCmds
- ================
- */
- void idPlayer::HandleUserCmds( const usercmd_t & newcmd ) {
- // latch button actions
- oldButtons = usercmd.buttons;
-
- // grab out usercmd
- oldCmd = usercmd;
- oldImpulseSequence = usercmd.impulseSequence;
- usercmd = newcmd;
- }
- /*
- ================
- idPlayer::WriteToSnapshot
- ================
- */
- void idPlayer::WriteToSnapshot( idBitMsg &msg ) const {
- physicsObj.WriteToSnapshot( msg );
- WriteBindToSnapshot( msg );
- // Only remote players will use these actual viewangles.
- idCQuat snapViewCQuat( viewAngles.ToQuat().ToCQuat() );
- msg.WriteFloat( snapViewCQuat.x );
- msg.WriteFloat( snapViewCQuat.y );
- msg.WriteFloat( snapViewCQuat.z );
- msg.WriteDeltaFloat( 0.0f, deltaViewAngles[0] );
- msg.WriteDeltaFloat( 0.0f, deltaViewAngles[1] );
- msg.WriteDeltaFloat( 0.0f, deltaViewAngles[2] );
- msg.WriteShort( health );
- msg.WriteBits( gameLocal.ServerRemapDecl( -1, DECL_ENTITYDEF, lastDamageDef ), gameLocal.entityDefBits );
- msg.WriteDir( lastDamageDir, 9 );
- msg.WriteShort( lastDamageLocation );
- msg.WriteBits( idealWeapon.Get(), idMath::BitsForInteger( MAX_WEAPONS ) );
- msg.WriteBits( inventory.weapons, MAX_WEAPONS );
- msg.WriteBits( weapon.GetSpawnId(), 32 );
- msg.WriteBits( flashlight.GetSpawnId(), 32 );
- msg.WriteBits( spectator, idMath::BitsForInteger( MAX_CLIENTS ) );
- msg.WriteBits( lastHitToggle, 1 );
- msg.WriteBits( weaponGone, 1 );
- msg.WriteBits( isLagged, 1 );
- msg.WriteShort( team );
- WriteToBitMsg( respawn_netEvent, msg );
- /* Needed for the scoreboard */
- msg.WriteBits( carryingFlag, 1 );
- msg.WriteBits( enviroSuitLight.GetSpawnId(), 32 );
- msg.WriteBits( AI_CROUCH, 1 );
- msg.WriteBits( AI_ONGROUND, 1 );
- msg.WriteBits( AI_ONLADDER, 1 );
- msg.WriteBits( AI_JUMP, 1 );
- msg.WriteBits( AI_WEAPON_FIRED, 1 );
- msg.WriteBits( AI_ATTACK_HELD, 1 );
- msg.WriteByte( usercmd.buttons );
- msg.WriteBits( usercmd.forwardmove, -8 );
- msg.WriteBits( usercmd.rightmove, -8 );
- msg.WriteBool( spectating );
- }
- /*
- ================
- idPlayer::ReadFromSnapshot
- ================
- */
- void idPlayer::ReadFromSnapshot( const idBitMsg &msg ) {
- int oldHealth, newIdealWeapon, weaponSpawnId;
- int flashlightSpawnId;
- bool newHitToggle;
- oldHealth = health;
- physicsObj.ReadFromSnapshot( msg );
- ReadBindFromSnapshot( msg );
- // The remote players get updated view angles from the snapshot.
- idCQuat snapViewCQuat;
- snapViewCQuat.x = msg.ReadFloat();
- snapViewCQuat.y = msg.ReadFloat();
- snapViewCQuat.z = msg.ReadFloat();
- idAngles tempDeltaViewAngles;
- tempDeltaViewAngles[0] = msg.ReadDeltaFloat( 0.0f );
- tempDeltaViewAngles[1] = msg.ReadDeltaFloat( 0.0f );
- tempDeltaViewAngles[2] = msg.ReadDeltaFloat( 0.0f );
- deltaViewAngles = tempDeltaViewAngles;
- health = msg.ReadShort();
- lastDamageDef = gameLocal.ClientRemapDecl( DECL_ENTITYDEF, msg.ReadBits( gameLocal.entityDefBits ) );
- lastDamageDir = msg.ReadDir( 9 );
- lastDamageLocation = msg.ReadShort();
- newIdealWeapon = msg.ReadBits( idMath::BitsForInteger( MAX_WEAPONS ) );
- inventory.weapons = msg.ReadBits( MAX_WEAPONS );
- weaponSpawnId = msg.ReadBits( 32 );
- flashlightSpawnId = msg.ReadBits( 32 );
- spectator = msg.ReadBits( idMath::BitsForInteger( MAX_CLIENTS ) );
- newHitToggle = msg.ReadBits( 1 ) != 0;
- weaponGone = msg.ReadBits( 1 ) != 0;
- isLagged = msg.ReadBits( 1 ) != 0;
- team = msg.ReadShort();
- ReadFromBitMsg( respawn_netEvent, msg );
- carryingFlag = msg.ReadBits( 1 ) != 0;
- int enviroSpawnId;
- enviroSpawnId = msg.ReadBits( 32 );
- enviroSuitLight.SetSpawnId( enviroSpawnId );
- bool snapshotCrouch = msg.ReadBool();
- bool snapshotOnGround = msg.ReadBool();
- bool snapshotOnLadder = msg.ReadBool();
- bool snapshotJump = msg.ReadBool();
- bool snapShotFired = msg.ReadBool();
- bool snapShotAttackHeld = msg.ReadBool();
- byte snapshotButtons = msg.ReadByte();
- signed char snapshotForward = msg.ReadBits( -8 );
- signed char snapshotRight = msg.ReadBits( -8 );
- const bool snapshotSpectating = msg.ReadBool();
- // no msg reading below this
- // Update remote remote player state.
- if ( !IsLocallyControlled() ) {
- previousViewQuat = nextViewQuat;
- nextViewQuat = snapViewCQuat.ToQuat();
- AI_CROUCH = snapshotCrouch;
- AI_ONGROUND = snapshotOnGround;
- AI_ONLADDER = snapshotOnLadder;
- AI_JUMP = snapshotJump;
- AI_WEAPON_FIRED = snapShotFired;
- AI_ATTACK_HELD = snapShotAttackHeld;
- oldCmd = usercmd;
-
- usercmd.buttons = snapshotButtons;
- usercmd.forwardmove = snapshotForward;
- usercmd.rightmove = snapshotRight;
- }
- if ( weapon.SetSpawnId( weaponSpawnId ) ) {
- if ( weapon.GetEntity() ) {
- // maintain ownership locally
- weapon.GetEntity()->SetOwner( this );
- }
- currentWeapon = -1;
- }
- if ( flashlight.SetSpawnId( flashlightSpawnId ) ) {
- if ( flashlight.GetEntity() ) {
- flashlight.GetEntity()->SetFlashlightOwner( this );
- }
- }
- /*
- // if not a local client
- if ( !IsLocallyControlled() ) {
- // assume the client has all ammo types
- inventory.SetRemoteClientAmmo( GetEntityNumber() );
- }
- */
- // Update spectating state
- const bool wasSpectating = spectating;
- spectating = snapshotSpectating;
- if ( spectating != wasSpectating ) {
- Spectate( spectating, false );
- }
- if ( oldHealth > 0 && health <= 0 ) {
- if ( snapshotStale ) {
- // so we just hide and don't show a death skin
- UpdateDeathSkin( true );
- }
- // die
- AI_DEAD = true;
- ClearPowerUps();
- SetAnimState( ANIMCHANNEL_LEGS, "Legs_Death", 4 );
- SetAnimState( ANIMCHANNEL_TORSO, "Torso_Death", 4 );
- SetWaitState( "" );
- animator.ClearAllJoints();
- StartRagdoll();
- physicsObj.SetMovementType( PM_DEAD );
- if ( !snapshotStale ) {
- StartSound( "snd_death", SND_CHANNEL_VOICE, 0, false, NULL );
- }
- if ( weapon.GetEntity() ) {
- weapon.GetEntity()->OwnerDied();
- }
- if ( flashlight.GetEntity() ) {
- FlashlightOff();
- flashlight.GetEntity()->OwnerDied();
- }
- if( IsLocallyControlled() ) {
- ControllerShakeFromDamage( oldHealth - health );
- }
- } else if ( health < oldHealth && health > 0 ) {
- if ( snapshotStale ) {
- lastDmgTime = gameLocal.time;
- } else {
- // damage feedback
- const idDeclEntityDef *def = static_cast<const idDeclEntityDef *>( declManager->DeclByIndex( DECL_ENTITYDEF, lastDamageDef, false ) );
- if ( def ) {
- if ( IsLocallyControlled() ) {
- playerView.DamageImpulse( lastDamageDir * viewAxis.Transpose(), &def->dict );
- AI_PAIN = Pain( NULL, NULL, oldHealth - health, lastDamageDir, lastDamageLocation );
- }
- lastDmgTime = gameLocal.time;
- } else {
- common->Warning( "NET: no damage def for damage feedback '%d'\n", lastDamageDef );
- }
- if( IsLocallyControlled() ) {
- ControllerShakeFromDamage( oldHealth - health );
- }
- }
- } else if ( health > oldHealth && PowerUpActive( MEGAHEALTH ) && !snapshotStale ) {
- // just pulse, for any health raise
- healthPulse = true;
- }
- // handle respawns
- if ( respawn_netEvent.Get() ) {
- Init();
- StopRagdoll();
- SetPhysics( &physicsObj );
- // Explicitly set the current origin, since locally-controlled clients
- // don't interpolate. Reading the physics object from the snapshot only
- // updates the next state, not the current state.
- physicsObj.SnapToNextState();
- physicsObj.EnableClip();
- SetCombatContents( true );
- if ( flashlight.GetEntity() ) {
- flashlight.GetEntity()->Show();
- }
- Respawn_Shared();
- }
- // If the player is alive, restore proper physics object
- if ( health > 0 && IsActiveAF() ) {
- StopRagdoll();
- SetPhysics( &physicsObj );
- physicsObj.EnableClip();
- SetCombatContents( true );
- }
- const int oldIdealWeapon = idealWeapon.Get();
- idealWeapon.UpdateFromSnapshot( newIdealWeapon, GetEntityNumber() );
- if ( oldIdealWeapon != idealWeapon.Get() ) {
- if ( snapshotStale ) {
- weaponCatchup = true;
- }
- UpdateHudWeapon();
- }
- if ( lastHitToggle != newHitToggle ) {
- SetLastHitTime( gameLocal.realClientTime );
- }
- if ( msg.HasChanged() ) {
- UpdateVisuals();
- }
- }
- /*
- ================
- idPlayer::WritePlayerStateToSnapshot
- ================
- */
- void idPlayer::WritePlayerStateToSnapshot( idBitMsg &msg ) const {
- msg.WriteByte( bobCycle );
- msg.WriteLong( stepUpTime );
- msg.WriteFloat( stepUpDelta );
- msg.WriteLong( inventory.weapons );
- msg.WriteByte( inventory.armor );
- inventory.WriteAmmoToSnapshot( msg );
- }
- /*
- ================
- idPlayer::ReadPlayerStateFromSnapshot
- ================
- */
- void idPlayer::ReadPlayerStateFromSnapshot( const idBitMsg &msg ) {
- int newBobCycle = 0;
- int newStepUpTime = 0;
- int newStepUpDelta = 0;
- newBobCycle = msg.ReadByte();
- newStepUpTime = msg.ReadLong();
- newStepUpDelta = msg.ReadFloat();
- inventory.weapons = msg.ReadLong();
- inventory.armor = msg.ReadByte();
- inventory.ReadAmmoFromSnapshot( msg, GetEntityNumber() );
- }
- /*
- ================
- idPlayer::ServerReceiveEvent
- ================
- */
- bool idPlayer::ServerReceiveEvent( int event, int time, const idBitMsg &msg ) {
- if ( idEntity::ServerReceiveEvent( event, time, msg ) ) {
- return true;
- }
- return false;
- }
- /*
- ================
- idPlayer::ClientReceiveEvent
- ================
- */
- bool idPlayer::ClientReceiveEvent( int event, int time, const idBitMsg &msg ) {
- switch ( event ) {
- case EVENT_EXIT_TELEPORTER:
- Event_ExitTeleporter();
- return true;
- case EVENT_ABORT_TELEPORTER:
- SetPrivateCameraView( NULL );
- return true;
- case EVENT_POWERUP: {
- int powerup = msg.ReadShort();
- int powertime = msg.ReadShort();
- if ( powertime > 0 ) {
- GivePowerUp( powerup, powertime, ITEM_GIVE_UPDATE_STATE );
- } else {
- ClearPowerup( powerup );
- }
- return true;
- }
- case EVENT_PICKUPNAME: {
- char buf[MAX_EVENT_PARAM_SIZE];
- msg.ReadString(buf, MAX_EVENT_PARAM_SIZE);
- inventory.AddPickupName(buf, this); //_D3XP
- return true;
- }
- case EVENT_SPECTATE: {
- bool spectate = ( msg.ReadBits( 1 ) != 0 );
- Spectate( spectate, true );
- return true;
- }
- case EVENT_ADD_DAMAGE_EFFECT: {
- if ( spectating ) {
- // if we're spectating, ignore
- // happens if the event and the spectate change are written on the server during the same frame (fraglimit)
- return true;
- }
- return idActor::ClientReceiveEvent( event, time, msg );
- }
- case EVENT_FORCE_ORIGIN: {
- idVec3 forceOrigin = ReadFloatArray< idVec3 >( msg );
- idAngles forceAngles;
- forceAngles[0] = msg.ReadFloat();
- forceAngles[1] = msg.ReadFloat();
- forceAngles[2] = msg.ReadFloat();
- Event_ForceOrigin( forceOrigin, forceAngles );
- return true;
- }
- case EVENT_KNOCKBACK: {
- idVec3 linearVelocity = ReadFloatArray< idVec3 >( msg );
- int knockbacktime = msg.ReadByte();
- physicsObj.SetLinearVelocity( linearVelocity );
- physicsObj.SetKnockBack( knockbacktime );
- return true;
- }
- default: {
- return idActor::ClientReceiveEvent( event, time, msg );
- }
- }
- // return false;
- }
- /*
- ================
- idPlayer::Hide
- ================
- */
- void idPlayer::Hide() {
- idWeapon *weap;
- idActor::Hide();
- weap = weapon.GetEntity();
- if ( weap ) {
- weap->HideWorldModel();
- }
- idWeapon * flash = flashlight.GetEntity();
- if( flash ) {
- flash->HideWorldModel();
- }
- }
- /*
- ================
- idPlayer::Show
- ================
- */
- void idPlayer::Show() {
- idWeapon *weap;
-
- idActor::Show();
- weap = weapon.GetEntity();
- if ( weap ) {
- weap->ShowWorldModel();
- }
- idWeapon * flash = flashlight.GetEntity();
- if( flash ) {
- flash->ShowWorldModel();
- }
- }
- /*
- ===============
- idPlayer::IsSoundChannelPlaying
- ===============
- */
- bool idPlayer::IsSoundChannelPlaying( const s_channelType channel ) {
- if ( GetSoundEmitter() != NULL ) {
- return GetSoundEmitter()->CurrentlyPlaying( channel );
- }
- return false;
- }
- /*
- ===============
- idPlayer::ShowTip
- ===============
- */
- void idPlayer::ShowTip( const char *title, const char *tip, bool autoHide ) {
- if ( tipUp ) {
- return;
- }
- if ( hudManager ) {
- hudManager->ShowTip( title, tip, autoHide );
- }
- tipUp = true;
- }
- /*
- ===============
- idPlayer::HideTip
- ===============
- */
- void idPlayer::HideTip() {
- if ( hudManager ) {
- hudManager->HideTip();
- }
- tipUp = false;
- }
- /*
- ===============
- idPlayer::Event_HideTip
- ===============
- */
- void idPlayer::Event_HideTip() {
- HideTip();
- }
- /*
- ===============
- idPlayer::HideObjective
- ===============
- */
- void idPlayer::HideObjective() {
- StartSound( "snd_objectivedown", SND_CHANNEL_ANY, 0, false, NULL );
- if ( hud ) {
- if ( objectiveUp ) {
- hud->HideObjective( false );
- objectiveUp = false;
- } else {
- hud->HideObjective( true );
- }
- }
- }
- /*
- ===============
- idPlayer::Event_StopAudioLog
- ===============
- */
- void idPlayer::Event_StopAudioLog() {
- //EndAudioLog();
- }
- /*
- ===============
- idPlayer::SetSpectateOrigin
- ===============
- */
- void idPlayer::SetSpectateOrigin() {
- idVec3 neworig;
- neworig = GetPhysics()->GetOrigin();
- neworig[ 2 ] += pm_normalviewheight.GetFloat();
- neworig[ 2 ] += SPECTATE_RAISE;
- SetOrigin( neworig );
- }
- /*
- ===============
- idPlayer::RemoveWeapon
- ===============
- */
- void idPlayer::RemoveWeapon( const char *weap ) {
- if ( weap && *weap ) {
- inventory.Drop( spawnArgs, spawnArgs.GetString( weap ), -1 );
- }
- }
- /*
- ===============
- idPlayer::RemoveAllButEssentialWeapons
- ===============
- */
- void idPlayer::RemoveAllButEssentialWeapons() {
- const idKeyValue * kv = spawnArgs.MatchPrefix( "def_weapon", NULL );
- for ( ; kv != NULL; kv = spawnArgs.MatchPrefix( "def_weapon", kv ) ) {
- // This list probably ought to be placed int the player's def
- if ( kv->GetValue() == "weapon_fists" || kv->GetValue() == "weapon_soulcube" || kv->GetValue() == "weapon_pda"
- || kv->GetValue() == "weapon_flashlight" || kv->GetValue() == "weapon_flashlight_new" ) {
- continue;
- }
- inventory.Drop( spawnArgs, kv->GetValue(), -1 );
- }
- }
- /*
- ===============
- idPlayer::CanShowWeaponViewmodel
- ===============
- */
- bool idPlayer::CanShowWeaponViewmodel() const {
- return ui_showGun.GetBool();
- }
- /*
- ===============
- idPlayer::SetLevelTrigger
- ===============
- */
- void idPlayer::SetLevelTrigger( const char *levelName, const char *triggerName ) {
- if ( levelName && *levelName && triggerName && *triggerName ) {
- idLevelTriggerInfo lti;
- lti.levelName = levelName;
- lti.triggerName = triggerName;
- inventory.levelTriggers.Append( lti );
- }
- }
- /*
- ===============
- idPlayer::Event_LevelTrigger
- ===============
- */
- void idPlayer::Event_LevelTrigger() {
- idStr mapName = gameLocal.GetMapName();
- mapName.StripPath();
- mapName.StripFileExtension();
- for ( int i = inventory.levelTriggers.Num() - 1; i >= 0; i-- ) {
- if ( idStr::Icmp( mapName, inventory.levelTriggers[i].levelName) == 0 ){
- idEntity *ent = gameLocal.FindEntity( inventory.levelTriggers[i].triggerName );
- if ( ent ) {
- ent->PostEventMS( &EV_Activate, 1, this );
- }
- }
- }
- }
- /*
- ===============
- idPlayer::Event_Gibbed
- ===============
- */
- void idPlayer::Event_Gibbed() {
- // do nothing
- }
- extern idCVar net_clientMaxPrediction;
- /*
- ===============
- idPlayer::UpdatePlayerIcons
- ===============
- */
- void idPlayer::UpdatePlayerIcons() {
- idLobbyBase & lobby = session->GetActingGameStateLobbyBase();
- int lastPacketTime = lobby.GetPeerTimeSinceLastPacket( lobby.PeerIndexFromLobbyUser( gameLocal.lobbyUserIDs[entityNumber] ) );
- isLagged = ( lastPacketTime > net_clientMaxPrediction.GetInteger() );
- //isChatting = ( ( usercmd.buttons & BUTTON_CHATTING ) && ( health > 0 ) );
- }
- /*
- ===============
- idPlayer::DrawPlayerIcons
- ===============
- */
- void idPlayer::DrawPlayerIcons() {
- if ( !NeedsIcon() ) {
- playerIcon.FreeIcon();
- return;
- }
- // Never draw icons for hidden players.
- if ( this->IsHidden() )
- return;
-
- playerIcon.Draw( this, headJoint );
- }
- /*
- ===============
- idPlayer::HidePlayerIcons
- ===============
- */
- void idPlayer::HidePlayerIcons() {
- playerIcon.FreeIcon();
- }
- /*
- ===============
- idPlayer::NeedsIcon
- ==============
- */
- bool idPlayer::NeedsIcon() {
- // local clients don't render their own icons... they're only info for other clients
- // always draw icons in CTF games
- return !IsLocallyControlled() && ( ( g_CTFArrows.GetBool() && gameLocal.mpGame.IsGametypeFlagBased() && !IsHidden() && !AI_DEAD ) || ( isLagged ) );
- }
- /*
- ===============
- idPlayer::DropFlag()
- ==============
- */
- void idPlayer::DropFlag() {
- if ( !carryingFlag || !common->IsMultiplayer() || !gameLocal.mpGame.IsGametypeFlagBased() ) /* CTF */
- return;
- idEntity * entity = gameLocal.mpGame.GetTeamFlag( 1 - team );
- if ( entity ) {
- idItemTeam * item = static_cast<idItemTeam*>(entity);
-
- if ( item->carried && !item->dropped ) {
- item->Drop( health <= 0 );
- carryingFlag = false;
- }
- }
- }
- void idPlayer::ReturnFlag() {
- if ( !carryingFlag || !common->IsMultiplayer() || !gameLocal.mpGame.IsGametypeFlagBased() ) /* CTF */
- return;
- idEntity * entity = gameLocal.mpGame.GetTeamFlag( 1 - team );
- if ( entity ) {
- idItemTeam * item = static_cast<idItemTeam*>(entity);
- if ( item->carried && !item->dropped ) {
- item->Return();
- carryingFlag = false;
- }
- }
- }
- void idPlayer::FreeModelDef() {
- idAFEntity_Base::FreeModelDef();
- if ( common->IsMultiplayer() && gameLocal.mpGame.IsGametypeFlagBased() )
- playerIcon.FreeIcon();
- }
- /*
- ========================
- idView::SetControllerShake
- ========================
- */
- void idPlayer::SetControllerShake( float highMagnitude, int highDuration, float lowMagnitude, int lowDuration ) {
- // the main purpose of having these buffer is so multiple, individual shake events can co-exist with each other,
- // for instance, a constant low rumble from the chainsaw when it's idle and a harsh rumble when it's being used.
- // find active buffer with similar magnitude values
- int activeBufferWithSimilarMags = -1;
- int inactiveBuffer = -1;
- for ( int i=0; i<MAX_SHAKE_BUFFER; i++ ) {
- if ( gameLocal.GetTime() <= controllerShakeHighTime[i] || gameLocal.GetTime() <= controllerShakeLowTime[i] ) {
- if ( idMath::Fabs( highMagnitude - controllerShakeHighMag[i] ) <= 0.1f && idMath::Fabs( lowMagnitude - controllerShakeLowMag[i] ) <= 0.1f ) {
- activeBufferWithSimilarMags = i;
- break;
- }
- } else {
- if ( inactiveBuffer == -1 ) {
- inactiveBuffer = i; // first, inactive buffer..
- }
- }
- }
- if ( activeBufferWithSimilarMags > -1 ) {
- // average the magnitudes and adjust the time
- controllerShakeHighMag[ activeBufferWithSimilarMags ] += highMagnitude;
- controllerShakeHighMag[ activeBufferWithSimilarMags ] *= 0.5f;
- controllerShakeLowMag[ activeBufferWithSimilarMags ] += lowMagnitude;
- controllerShakeLowMag[ activeBufferWithSimilarMags ] *= 0.5f;
- controllerShakeHighTime[ activeBufferWithSimilarMags ] = gameLocal.GetTime() + highDuration;
- controllerShakeLowTime[ activeBufferWithSimilarMags ] = gameLocal.GetTime() + lowDuration;
- controllerShakeTimeGroup = gameLocal.selectedGroup;
- return;
- }
- if ( inactiveBuffer == -1 ) {
- inactiveBuffer = 0; // FIXME: probably want to use the oldest buffer..
- }
- controllerShakeHighMag[ inactiveBuffer ] = highMagnitude;
- controllerShakeLowMag[ inactiveBuffer ] = lowMagnitude;
- controllerShakeHighTime[ inactiveBuffer ] = gameLocal.GetTime() + highDuration;
- controllerShakeLowTime[ inactiveBuffer ] = gameLocal.GetTime() + lowDuration;
- controllerShakeTimeGroup = gameLocal.selectedGroup;
- }
- /*
- ========================
- idView::ResetControllerShake
- ========================
- */
- void idPlayer::ResetControllerShake() {
- for ( int i=0; i<MAX_SHAKE_BUFFER; i++ ) {
- controllerShakeHighTime[i] = 0;
- }
- for ( int i=0; i<MAX_SHAKE_BUFFER; i++ ) {
- controllerShakeHighMag[i] = 0.0f;
- }
- for ( int i=0; i<MAX_SHAKE_BUFFER; i++ ) {
- controllerShakeLowTime[i] = 0;
- }
- for ( int i=0; i<MAX_SHAKE_BUFFER; i++ ) {
- controllerShakeLowMag[i] = 0.0f;
- }
- }
- /*
- ========================
- idPlayer::GetControllerShake
- ========================
- */
- void idPlayer::GetControllerShake( int & highMagnitude, int & lowMagnitude ) const {
- if ( gameLocal.inCinematic ) {
- // no controller shake during cinematics
- highMagnitude = 0;
- lowMagnitude = 0;
- return;
- }
- float lowMag = 0.0f;
- float highMag = 0.0f;
- lowMagnitude = 0;
- highMagnitude = 0;
- // use highest values from active buffers
- for ( int i=0; i<MAX_SHAKE_BUFFER; i++ ) {
- if ( gameLocal.GetTimeGroupTime( controllerShakeTimeGroup ) < controllerShakeLowTime[i] ) {
- if ( controllerShakeLowMag[i] > lowMag ) {
- lowMag = controllerShakeLowMag[i];
- }
- }
- if ( gameLocal.GetTimeGroupTime( controllerShakeTimeGroup ) < controllerShakeHighTime[i] ) {
- if ( controllerShakeHighMag[i] > highMag ) {
- highMag = controllerShakeHighMag[i];
- }
- }
- }
- lowMagnitude = idMath::Ftoi( lowMag * 65535.0f );
- highMagnitude = idMath::Ftoi( highMag * 65535.0f );
- }
- /*
- ========================
- idPlayer::GetExpansionType
- ========================
- */
- gameExpansionType_t idPlayer::GetExpansionType() const {
- const char * expansion = spawnArgs.GetString( "player_expansion", "d3" );
- if ( idStr::Icmp( expansion, "d3" ) == 0 ) {
- return GAME_BASE;
- }
- if ( idStr::Icmp( expansion, "d3xp" ) == 0 ) {
- return GAME_D3XP;
- }
- if ( idStr::Icmp( expansion, "d3le" ) == 0 ) {
- return GAME_D3LE;
- }
- return GAME_UNKNOWN;
- }
|