Player.cpp 280 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372637363746375637663776378637963806381638263836384638563866387638863896390639163926393639463956396639763986399640064016402640364046405640664076408640964106411641264136414641564166417641864196420642164226423642464256426642764286429643064316432643364346435643664376438643964406441644264436444644564466447644864496450645164526453645464556456645764586459646064616462646364646465646664676468646964706471647264736474647564766477647864796480648164826483648464856486648764886489649064916492649364946495649664976498649965006501650265036504650565066507650865096510651165126513651465156516651765186519652065216522652365246525652665276528652965306531653265336534653565366537653865396540654165426543654465456546654765486549655065516552655365546555655665576558655965606561656265636564656565666567656865696570657165726573657465756576657765786579658065816582658365846585658665876588658965906591659265936594659565966597659865996600660166026603660466056606660766086609661066116612661366146615661666176618661966206621662266236624662566266627662866296630663166326633663466356636663766386639664066416642664366446645664666476648664966506651665266536654665566566657665866596660666166626663666466656666666766686669667066716672667366746675667666776678667966806681668266836684668566866687668866896690669166926693669466956696669766986699670067016702670367046705670667076708670967106711671267136714671567166717671867196720672167226723672467256726672767286729673067316732673367346735673667376738673967406741674267436744674567466747674867496750675167526753675467556756675767586759676067616762676367646765676667676768676967706771677267736774677567766777677867796780678167826783678467856786678767886789679067916792679367946795679667976798679968006801680268036804680568066807680868096810681168126813681468156816681768186819682068216822682368246825682668276828682968306831683268336834683568366837683868396840684168426843684468456846684768486849685068516852685368546855685668576858685968606861686268636864686568666867686868696870687168726873687468756876687768786879688068816882688368846885688668876888688968906891689268936894689568966897689868996900690169026903690469056906690769086909691069116912691369146915691669176918691969206921692269236924692569266927692869296930693169326933693469356936693769386939694069416942694369446945694669476948694969506951695269536954695569566957695869596960696169626963696469656966696769686969697069716972697369746975697669776978697969806981698269836984698569866987698869896990699169926993699469956996699769986999700070017002700370047005700670077008700970107011701270137014701570167017701870197020702170227023702470257026702770287029703070317032703370347035703670377038703970407041704270437044704570467047704870497050705170527053705470557056705770587059706070617062706370647065706670677068706970707071707270737074707570767077707870797080708170827083708470857086708770887089709070917092709370947095709670977098709971007101710271037104710571067107710871097110711171127113711471157116711771187119712071217122712371247125712671277128712971307131713271337134713571367137713871397140714171427143714471457146714771487149715071517152715371547155715671577158715971607161716271637164716571667167716871697170717171727173717471757176717771787179718071817182718371847185718671877188718971907191719271937194719571967197719871997200720172027203720472057206720772087209721072117212721372147215721672177218721972207221722272237224722572267227722872297230723172327233723472357236723772387239724072417242724372447245724672477248724972507251725272537254725572567257725872597260726172627263726472657266726772687269727072717272727372747275727672777278727972807281728272837284728572867287728872897290729172927293729472957296729772987299730073017302730373047305730673077308730973107311731273137314731573167317731873197320732173227323732473257326732773287329733073317332733373347335733673377338733973407341734273437344734573467347734873497350735173527353735473557356735773587359736073617362736373647365736673677368736973707371737273737374737573767377737873797380738173827383738473857386738773887389739073917392739373947395739673977398739974007401740274037404740574067407740874097410741174127413741474157416741774187419742074217422742374247425742674277428742974307431743274337434743574367437743874397440744174427443744474457446744774487449745074517452745374547455745674577458745974607461746274637464746574667467746874697470747174727473747474757476747774787479748074817482748374847485748674877488748974907491749274937494749574967497749874997500750175027503750475057506750775087509751075117512751375147515751675177518751975207521752275237524752575267527752875297530753175327533753475357536753775387539754075417542754375447545754675477548754975507551755275537554755575567557755875597560756175627563756475657566756775687569757075717572757375747575757675777578757975807581758275837584758575867587758875897590759175927593759475957596759775987599760076017602760376047605760676077608760976107611761276137614761576167617761876197620762176227623762476257626762776287629763076317632763376347635763676377638763976407641764276437644764576467647764876497650765176527653765476557656765776587659766076617662766376647665766676677668766976707671767276737674767576767677767876797680768176827683768476857686768776887689769076917692769376947695769676977698769977007701770277037704770577067707770877097710771177127713771477157716771777187719772077217722772377247725772677277728772977307731773277337734773577367737773877397740774177427743774477457746774777487749775077517752775377547755775677577758775977607761776277637764776577667767776877697770777177727773777477757776777777787779778077817782778377847785778677877788778977907791779277937794779577967797779877997800780178027803780478057806780778087809781078117812781378147815781678177818781978207821782278237824782578267827782878297830783178327833783478357836783778387839784078417842784378447845784678477848784978507851785278537854785578567857785878597860786178627863786478657866786778687869787078717872787378747875787678777878787978807881788278837884788578867887788878897890789178927893789478957896789778987899790079017902790379047905790679077908790979107911791279137914791579167917791879197920792179227923792479257926792779287929793079317932793379347935793679377938793979407941794279437944794579467947794879497950795179527953795479557956795779587959796079617962796379647965796679677968796979707971797279737974797579767977797879797980798179827983798479857986798779887989799079917992799379947995799679977998799980008001800280038004800580068007800880098010801180128013801480158016801780188019802080218022802380248025802680278028802980308031803280338034803580368037803880398040804180428043804480458046804780488049805080518052805380548055805680578058805980608061806280638064806580668067806880698070807180728073807480758076807780788079808080818082808380848085808680878088808980908091809280938094809580968097809880998100810181028103810481058106810781088109811081118112811381148115811681178118811981208121812281238124812581268127812881298130813181328133813481358136813781388139814081418142814381448145814681478148814981508151815281538154815581568157815881598160816181628163816481658166816781688169817081718172817381748175817681778178817981808181818281838184818581868187818881898190819181928193819481958196819781988199820082018202820382048205820682078208820982108211821282138214821582168217821882198220822182228223822482258226822782288229823082318232823382348235823682378238823982408241824282438244824582468247824882498250825182528253825482558256825782588259826082618262826382648265826682678268826982708271827282738274827582768277827882798280828182828283828482858286828782888289829082918292829382948295829682978298829983008301830283038304830583068307830883098310831183128313831483158316831783188319832083218322832383248325832683278328832983308331833283338334833583368337833883398340834183428343834483458346834783488349835083518352835383548355835683578358835983608361836283638364836583668367836883698370837183728373837483758376837783788379838083818382838383848385838683878388838983908391839283938394839583968397839883998400840184028403840484058406840784088409841084118412841384148415841684178418841984208421842284238424842584268427842884298430843184328433843484358436843784388439844084418442844384448445844684478448844984508451845284538454845584568457845884598460846184628463846484658466846784688469847084718472847384748475847684778478847984808481848284838484848584868487848884898490849184928493849484958496849784988499850085018502850385048505850685078508850985108511851285138514851585168517851885198520852185228523852485258526852785288529853085318532853385348535853685378538853985408541854285438544854585468547854885498550855185528553855485558556855785588559856085618562856385648565856685678568856985708571857285738574857585768577857885798580858185828583858485858586858785888589859085918592859385948595859685978598859986008601860286038604860586068607860886098610861186128613861486158616861786188619862086218622862386248625862686278628862986308631863286338634863586368637863886398640864186428643864486458646864786488649865086518652865386548655865686578658865986608661866286638664866586668667866886698670867186728673867486758676867786788679868086818682868386848685868686878688868986908691869286938694869586968697869886998700870187028703870487058706870787088709871087118712871387148715871687178718871987208721872287238724872587268727872887298730873187328733873487358736873787388739874087418742874387448745874687478748874987508751875287538754875587568757875887598760876187628763876487658766876787688769877087718772877387748775877687778778877987808781878287838784878587868787878887898790879187928793879487958796879787988799880088018802880388048805880688078808880988108811881288138814881588168817881888198820882188228823882488258826882788288829883088318832883388348835883688378838883988408841884288438844884588468847884888498850885188528853885488558856885788588859886088618862886388648865886688678868886988708871887288738874887588768877887888798880888188828883888488858886888788888889889088918892889388948895889688978898889989008901890289038904890589068907890889098910891189128913891489158916891789188919892089218922892389248925892689278928892989308931893289338934893589368937893889398940894189428943894489458946894789488949895089518952895389548955895689578958895989608961896289638964896589668967896889698970897189728973897489758976897789788979898089818982898389848985898689878988898989908991899289938994899589968997899889999000900190029003900490059006900790089009901090119012901390149015901690179018901990209021902290239024902590269027902890299030903190329033903490359036903790389039904090419042904390449045904690479048904990509051905290539054905590569057905890599060906190629063906490659066906790689069907090719072907390749075907690779078907990809081908290839084908590869087908890899090909190929093909490959096909790989099910091019102910391049105910691079108910991109111911291139114911591169117911891199120912191229123912491259126912791289129913091319132913391349135913691379138913991409141914291439144914591469147914891499150915191529153915491559156915791589159916091619162916391649165916691679168916991709171917291739174917591769177917891799180918191829183918491859186918791889189919091919192919391949195919691979198919992009201920292039204920592069207920892099210921192129213921492159216921792189219922092219222922392249225922692279228922992309231923292339234923592369237923892399240924192429243924492459246924792489249925092519252925392549255925692579258925992609261926292639264926592669267926892699270927192729273927492759276927792789279928092819282928392849285928692879288928992909291929292939294929592969297929892999300930193029303930493059306930793089309931093119312931393149315931693179318931993209321932293239324932593269327932893299330933193329333933493359336933793389339934093419342934393449345934693479348934993509351935293539354935593569357935893599360936193629363936493659366936793689369937093719372937393749375937693779378937993809381938293839384938593869387938893899390939193929393939493959396939793989399940094019402940394049405940694079408940994109411941294139414941594169417941894199420942194229423942494259426942794289429943094319432943394349435943694379438943994409441944294439444944594469447944894499450945194529453945494559456945794589459946094619462946394649465946694679468946994709471947294739474947594769477947894799480948194829483948494859486948794889489949094919492949394949495949694979498949995009501950295039504950595069507950895099510951195129513951495159516951795189519952095219522952395249525952695279528952995309531953295339534953595369537953895399540954195429543954495459546954795489549955095519552955395549555955695579558955995609561956295639564956595669567956895699570957195729573957495759576957795789579958095819582958395849585958695879588958995909591959295939594959595969597959895999600960196029603960496059606960796089609961096119612961396149615961696179618961996209621962296239624962596269627962896299630963196329633963496359636963796389639964096419642964396449645964696479648964996509651965296539654965596569657965896599660966196629663966496659666966796689669967096719672967396749675967696779678967996809681968296839684968596869687968896899690969196929693969496959696969796989699970097019702970397049705970697079708970997109711971297139714971597169717971897199720972197229723972497259726972797289729973097319732973397349735973697379738973997409741974297439744974597469747974897499750975197529753975497559756975797589759976097619762976397649765976697679768976997709771977297739774977597769777977897799780978197829783978497859786978797889789979097919792979397949795979697979798979998009801980298039804980598069807980898099810981198129813981498159816981798189819982098219822982398249825982698279828982998309831983298339834983598369837983898399840984198429843984498459846984798489849985098519852985398549855985698579858985998609861986298639864986598669867986898699870987198729873987498759876987798789879988098819882988398849885988698879888988998909891989298939894989598969897989898999900990199029903990499059906990799089909991099119912991399149915991699179918991999209921992299239924992599269927992899299930993199329933993499359936993799389939994099419942994399449945994699479948994999509951995299539954995599569957995899599960996199629963996499659966996799689969997099719972997399749975997699779978997999809981998299839984998599869987998899899990999199929993999499959996999799989999100001000110002100031000410005100061000710008100091001010011100121001310014100151001610017100181001910020100211002210023100241002510026100271002810029100301003110032100331003410035100361003710038100391004010041100421004310044100451004610047100481004910050100511005210053100541005510056100571005810059100601006110062100631006410065100661006710068100691007010071100721007310074100751007610077100781007910080100811008210083100841008510086100871008810089100901009110092100931009410095100961009710098100991010010101101021010310104101051010610107101081010910110101111011210113101141011510116101171011810119101201012110122101231012410125101261012710128101291013010131101321013310134101351013610137101381013910140101411014210143101441014510146101471014810149101501015110152101531015410155101561015710158101591016010161101621016310164101651016610167101681016910170101711017210173101741017510176101771017810179101801018110182101831018410185101861018710188101891019010191101921019310194101951019610197101981019910200102011020210203102041020510206102071020810209102101021110212102131021410215102161021710218102191022010221102221022310224102251022610227102281022910230102311023210233102341023510236102371023810239102401024110242102431024410245102461024710248102491025010251102521025310254102551025610257102581025910260102611026210263102641026510266102671026810269102701027110272102731027410275102761027710278102791028010281102821028310284102851028610287102881028910290102911029210293102941029510296102971029810299103001030110302103031030410305103061030710308103091031010311103121031310314103151031610317103181031910320103211032210323103241032510326103271032810329103301033110332103331033410335103361033710338103391034010341103421034310344103451034610347103481034910350103511035210353103541035510356103571035810359103601036110362103631036410365103661036710368103691037010371103721037310374103751037610377103781037910380103811038210383103841038510386103871038810389103901039110392103931039410395103961039710398103991040010401104021040310404104051040610407104081040910410104111041210413104141041510416104171041810419104201042110422104231042410425104261042710428104291043010431104321043310434104351043610437104381043910440104411044210443104441044510446104471044810449104501045110452104531045410455104561045710458104591046010461104621046310464104651046610467104681046910470104711047210473104741047510476104771047810479104801048110482104831048410485104861048710488104891049010491104921049310494104951049610497104981049910500105011050210503105041050510506105071050810509105101051110512105131051410515105161051710518105191052010521105221052310524105251052610527105281052910530105311053210533105341053510536105371053810539105401054110542105431054410545105461054710548105491055010551105521055310554105551055610557105581055910560105611056210563105641056510566105671056810569105701057110572105731057410575105761057710578105791058010581105821058310584105851058610587105881058910590105911059210593105941059510596105971059810599106001060110602106031060410605106061060710608106091061010611106121061310614106151061610617106181061910620106211062210623106241062510626106271062810629106301063110632106331063410635106361063710638106391064010641106421064310644106451064610647106481064910650106511065210653106541065510656106571065810659106601066110662106631066410665106661066710668106691067010671106721067310674106751067610677106781067910680106811068210683106841068510686106871068810689106901069110692106931069410695106961069710698106991070010701107021070310704107051070610707107081070910710107111071210713107141071510716107171071810719107201072110722107231072410725107261072710728107291073010731107321073310734107351073610737107381073910740107411074210743
  1. /*
  2. ===========================================================================
  3. Doom 3 BFG Edition GPL Source Code
  4. Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
  5. This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
  6. Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation, either version 3 of the License, or
  9. (at your option) any later version.
  10. Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. GNU General Public License for more details.
  14. You should have received a copy of the GNU General Public License
  15. along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
  16. In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below.
  17. If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
  18. ===========================================================================
  19. */
  20. #include "../idlib/precompiled.h"
  21. #pragma hdrstop
  22. #include "Game_local.h"
  23. #include "../framework/Common_local.h"
  24. #include "PredictedValue_impl.h"
  25. idCVar flashlight_batteryDrainTimeMS( "flashlight_batteryDrainTimeMS", "30000", CVAR_INTEGER, "amount of time (in MS) it takes for full battery to drain (-1 == no battery drain)" );
  26. idCVar flashlight_batteryChargeTimeMS( "flashlight_batteryChargeTimeMS", "3000", CVAR_INTEGER, "amount of time (in MS) it takes to fully recharge battery" );
  27. idCVar flashlight_minActivatePercent( "flashlight_minActivatePercent", ".25", CVAR_FLOAT, "( 0.0 - 1.0 ) minimum amount of battery (%) needed to turn on flashlight" );
  28. idCVar flashlight_batteryFlickerPercent( "flashlight_batteryFlickerPercent", ".1", CVAR_FLOAT, "chance of flickering when battery is low" );
  29. // No longer userinfo, but I don't want to rename the cvar
  30. idCVar ui_showGun( "ui_showGun", "1", CVAR_GAME | CVAR_ARCHIVE | CVAR_BOOL, "show gun" );
  31. // Client-authoritative stuff
  32. idCVar pm_clientAuthoritative_debug( "pm_clientAuthoritative_debug", "0", CVAR_BOOL, "" );
  33. idCVar pm_controllerShake_damageMaxMag( "pm_controllerShake_damageMaxMag", "60.0f", CVAR_FLOAT, "" );
  34. idCVar pm_controllerShake_damageMaxDur( "pm_controllerShake_damageMaxDur", "60.0f", CVAR_FLOAT, "" );
  35. idCVar pm_clientAuthoritative_warnDist( "pm_clientAuthoritative_warnDist", "100.0f", CVAR_FLOAT, "" );
  36. idCVar pm_clientAuthoritative_minDistZ( "pm_clientAuthoritative_minDistZ", "1.0f", CVAR_FLOAT, "" );
  37. idCVar pm_clientAuthoritative_minDist( "pm_clientAuthoritative_minDist", "-1.0f", CVAR_FLOAT, "" );
  38. idCVar pm_clientAuthoritative_Lerp( "pm_clientAuthoritative_Lerp", "0.9f", CVAR_FLOAT, "" );
  39. idCVar pm_clientAuthoritative_Divergence( "pm_clientAuthoritative_Divergence", "200.0f", CVAR_FLOAT, "" );
  40. idCVar pm_clientInterpolation_Divergence( "pm_clientInterpolation_Divergence", "5000.0f", CVAR_FLOAT, "" );
  41. idCVar pm_clientAuthoritative_minSpeedSquared( "pm_clientAuthoritative_minSpeedSquared", "1000.0f", CVAR_FLOAT, "" );
  42. extern idCVar g_demoMode;
  43. /*
  44. ===============================================================================
  45. Player control of the Doom Marine.
  46. This object handles all player movement and world interaction.
  47. ===============================================================================
  48. */
  49. // distance between ladder rungs (actually is half that distance, but this sounds better)
  50. const int LADDER_RUNG_DISTANCE = 32;
  51. // amount of health per dose from the health station
  52. const int HEALTH_PER_DOSE = 10;
  53. // time before a weapon dropped to the floor disappears
  54. const int WEAPON_DROP_TIME = 20 * 1000;
  55. // time before a next or prev weapon switch happens
  56. const int WEAPON_SWITCH_DELAY = 150;
  57. // how many units to raise spectator above default view height so it's in the head of someone
  58. const int SPECTATE_RAISE = 25;
  59. const int HEALTHPULSE_TIME = 333;
  60. // minimum speed to bob and play run/walk animations at
  61. const float MIN_BOB_SPEED = 5.0f;
  62. // Special team used for spectators that we ONLY store on lobby. The local team property on player remains as 0 or 1.
  63. const float LOBBY_SPECTATE_TEAM_FOR_VOICE_CHAT = 2;
  64. const idEventDef EV_Player_GetButtons( "getButtons", NULL, 'd' );
  65. const idEventDef EV_Player_GetMove( "getMove", NULL, 'v' );
  66. const idEventDef EV_Player_GetViewAngles( "getViewAngles", NULL, 'v' );
  67. const idEventDef EV_Player_StopFxFov( "stopFxFov" );
  68. const idEventDef EV_Player_EnableWeapon( "enableWeapon" );
  69. const idEventDef EV_Player_DisableWeapon( "disableWeapon" );
  70. const idEventDef EV_Player_GetCurrentWeapon( "getCurrentWeapon", NULL, 's' );
  71. const idEventDef EV_Player_GetPreviousWeapon( "getPreviousWeapon", NULL, 's' );
  72. const idEventDef EV_Player_SelectWeapon( "selectWeapon", "s" );
  73. const idEventDef EV_Player_GetWeaponEntity( "getWeaponEntity", NULL, 'e' );
  74. const idEventDef EV_Player_OpenPDA( "openPDA" );
  75. const idEventDef EV_Player_InPDA( "inPDA", NULL, 'd' );
  76. const idEventDef EV_Player_ExitTeleporter( "exitTeleporter" );
  77. const idEventDef EV_Player_StopAudioLog( "stopAudioLog" );
  78. const idEventDef EV_Player_HideTip( "hideTip" );
  79. const idEventDef EV_Player_LevelTrigger( "levelTrigger" );
  80. const idEventDef EV_SpectatorTouch( "spectatorTouch", "et" );
  81. const idEventDef EV_Player_GiveInventoryItem( "giveInventoryItem", "s" );
  82. const idEventDef EV_Player_RemoveInventoryItem( "removeInventoryItem", "s" );
  83. const idEventDef EV_Player_GetIdealWeapon( "getIdealWeapon", NULL, 's' );
  84. const idEventDef EV_Player_SetPowerupTime( "setPowerupTime", "dd" );
  85. const idEventDef EV_Player_IsPowerupActive( "isPowerupActive", "d", 'd' );
  86. const idEventDef EV_Player_WeaponAvailable( "weaponAvailable", "s", 'd');
  87. const idEventDef EV_Player_StartWarp( "startWarp" );
  88. const idEventDef EV_Player_StopHelltime( "stopHelltime", "d" );
  89. const idEventDef EV_Player_ToggleBloom( "toggleBloom", "d" );
  90. const idEventDef EV_Player_SetBloomParms( "setBloomParms", "ff" );
  91. CLASS_DECLARATION( idActor, idPlayer )
  92. EVENT( EV_Player_GetButtons, idPlayer::Event_GetButtons )
  93. EVENT( EV_Player_GetMove, idPlayer::Event_GetMove )
  94. EVENT( EV_Player_GetViewAngles, idPlayer::Event_GetViewAngles )
  95. EVENT( EV_Player_StopFxFov, idPlayer::Event_StopFxFov )
  96. EVENT( EV_Player_EnableWeapon, idPlayer::Event_EnableWeapon )
  97. EVENT( EV_Player_DisableWeapon, idPlayer::Event_DisableWeapon )
  98. EVENT( EV_Player_GetCurrentWeapon, idPlayer::Event_GetCurrentWeapon )
  99. EVENT( EV_Player_GetPreviousWeapon, idPlayer::Event_GetPreviousWeapon )
  100. EVENT( EV_Player_SelectWeapon, idPlayer::Event_SelectWeapon )
  101. EVENT( EV_Player_GetWeaponEntity, idPlayer::Event_GetWeaponEntity )
  102. EVENT( EV_Player_OpenPDA, idPlayer::Event_OpenPDA )
  103. EVENT( EV_Player_InPDA, idPlayer::Event_InPDA )
  104. EVENT( EV_Player_ExitTeleporter, idPlayer::Event_ExitTeleporter )
  105. EVENT( EV_Player_StopAudioLog, idPlayer::Event_StopAudioLog )
  106. EVENT( EV_Player_HideTip, idPlayer::Event_HideTip )
  107. EVENT( EV_Player_LevelTrigger, idPlayer::Event_LevelTrigger )
  108. EVENT( EV_Gibbed, idPlayer::Event_Gibbed )
  109. EVENT( EV_Player_GiveInventoryItem, idPlayer::Event_GiveInventoryItem )
  110. EVENT( EV_Player_RemoveInventoryItem, idPlayer::Event_RemoveInventoryItem )
  111. EVENT( EV_Player_GetIdealWeapon, idPlayer::Event_GetIdealWeapon )
  112. EVENT( EV_Player_WeaponAvailable, idPlayer::Event_WeaponAvailable )
  113. EVENT( EV_Player_SetPowerupTime, idPlayer::Event_SetPowerupTime )
  114. EVENT( EV_Player_IsPowerupActive, idPlayer::Event_IsPowerupActive )
  115. EVENT( EV_Player_StartWarp, idPlayer::Event_StartWarp )
  116. EVENT( EV_Player_StopHelltime, idPlayer::Event_StopHelltime )
  117. EVENT( EV_Player_ToggleBloom, idPlayer::Event_ToggleBloom )
  118. EVENT( EV_Player_SetBloomParms, idPlayer::Event_SetBloomParms )
  119. END_CLASS
  120. const int MAX_RESPAWN_TIME = 10000;
  121. const int RAGDOLL_DEATH_TIME = 3000;
  122. const int MAX_PDAS = 64;
  123. const int MAX_PDA_ITEMS = 128;
  124. const int STEPUP_TIME = 200;
  125. const int MAX_INVENTORY_ITEMS = 20;
  126. /*
  127. ==============
  128. idInventory::Clear
  129. ==============
  130. */
  131. void idInventory::Clear() {
  132. maxHealth = 0;
  133. weapons = 0;
  134. powerups = 0;
  135. armor = 0;
  136. maxarmor = 0;
  137. deplete_armor = 0;
  138. deplete_rate = 0.0f;
  139. deplete_ammount = 0;
  140. nextArmorDepleteTime = 0;
  141. for ( int i = 0; i < ammo.Num(); ++i ) {
  142. ammo[i].Set( 0 );
  143. }
  144. ClearPowerUps();
  145. // 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
  146. for ( int i = 0; i < clip.Num(); ++i ) {
  147. clip[i].Set( -1 );
  148. }
  149. items.DeleteContents( true );
  150. memset(pdasViewed, 0, 4 * sizeof( pdasViewed[0] ) );
  151. pdas.Clear();
  152. videos.Clear();
  153. emails.Clear();
  154. selVideo = 0;
  155. selEMail = 0;
  156. selPDA = 0;
  157. selAudio = 0;
  158. pdaOpened = false;
  159. levelTriggers.Clear();
  160. nextItemPickup = 0;
  161. nextItemNum = 1;
  162. onePickupTime = 0;
  163. pickupItemNames.Clear();
  164. objectiveNames.Clear();
  165. ammoPredictTime = 0;
  166. lastGiveTime = 0;
  167. ammoPulse = false;
  168. weaponPulse = false;
  169. armorPulse = false;
  170. }
  171. /*
  172. ==============
  173. idInventory::GivePowerUp
  174. ==============
  175. */
  176. void idInventory::GivePowerUp( idPlayer *player, int powerup, int msec ) {
  177. powerups |= 1 << powerup;
  178. powerupEndTime[ powerup ] = gameLocal.time + msec;
  179. }
  180. /*
  181. ==============
  182. idInventory::ClearPowerUps
  183. ==============
  184. */
  185. void idInventory::ClearPowerUps() {
  186. int i;
  187. for ( i = 0; i < MAX_POWERUPS; i++ ) {
  188. powerupEndTime[ i ] = 0;
  189. }
  190. powerups = 0;
  191. }
  192. /*
  193. ==============
  194. idInventory::GetPersistantData
  195. ==============
  196. */
  197. void idInventory::GetPersistantData( idDict &dict ) {
  198. int i;
  199. int num;
  200. idDict *item;
  201. idStr key;
  202. const idKeyValue *kv;
  203. const char *name;
  204. // armor
  205. dict.SetInt( "armor", armor );
  206. // don't bother with powerups, maxhealth, maxarmor, or the clip
  207. // ammo
  208. for( i = 0; i < AMMO_NUMTYPES; i++ ) {
  209. name = idWeapon::GetAmmoNameForNum( ( ammo_t )i );
  210. if ( name ) {
  211. dict.SetInt( name, ammo[ i ].Get() );
  212. }
  213. }
  214. //Save the clip data
  215. for( i = 0; i < MAX_WEAPONS; i++ ) {
  216. dict.SetInt( va("clip%i", i), clip[ i ].Get() );
  217. }
  218. // items
  219. num = 0;
  220. for( i = 0; i < items.Num(); i++ ) {
  221. item = items[ i ];
  222. // copy all keys with "inv_"
  223. kv = item->MatchPrefix( "inv_" );
  224. if ( kv ) {
  225. while( kv ) {
  226. sprintf( key, "item_%i %s", num, kv->GetKey().c_str() );
  227. dict.Set( key, kv->GetValue() );
  228. kv = item->MatchPrefix( "inv_", kv );
  229. }
  230. num++;
  231. }
  232. }
  233. dict.SetInt( "items", num );
  234. // pdas viewed
  235. for ( i = 0; i < 4; i++ ) {
  236. dict.SetInt( va("pdasViewed_%i", i), pdasViewed[i] );
  237. }
  238. dict.SetInt( "selPDA", selPDA );
  239. dict.SetInt( "selVideo", selVideo );
  240. dict.SetInt( "selEmail", selEMail );
  241. dict.SetInt( "selAudio", selAudio );
  242. dict.SetInt( "pdaOpened", pdaOpened );
  243. // pdas
  244. for ( i = 0; i < pdas.Num(); i++ ) {
  245. sprintf( key, "pda_%i", i );
  246. dict.Set( key, pdas[ i ]->GetName() );
  247. }
  248. dict.SetInt( "pdas", pdas.Num() );
  249. // video cds
  250. for ( i = 0; i < videos.Num(); i++ ) {
  251. sprintf( key, "video_%i", i );
  252. dict.Set( key, videos[ i ]->GetName() );
  253. }
  254. dict.SetInt( "videos", videos.Num() );
  255. // emails
  256. for ( i = 0; i < emails.Num(); i++ ) {
  257. sprintf( key, "email_%i", i );
  258. dict.Set( key, emails[ i ]->GetName() );
  259. }
  260. dict.SetInt( "emails", emails.Num() );
  261. // weapons
  262. dict.SetInt( "weapon_bits", weapons );
  263. dict.SetInt( "levelTriggers", levelTriggers.Num() );
  264. for ( i = 0; i < levelTriggers.Num(); i++ ) {
  265. sprintf( key, "levelTrigger_Level_%i", i );
  266. dict.Set( key, levelTriggers[i].levelName );
  267. sprintf( key, "levelTrigger_Trigger_%i", i );
  268. dict.Set( key, levelTriggers[i].triggerName );
  269. }
  270. }
  271. /*
  272. ==============
  273. idInventory::RestoreInventory
  274. ==============
  275. */
  276. void idInventory::RestoreInventory( idPlayer *owner, const idDict &dict ) {
  277. int i;
  278. int num;
  279. idDict *item;
  280. idStr key;
  281. idStr itemname;
  282. const idKeyValue *kv;
  283. const char *name;
  284. Clear();
  285. // health/armor
  286. maxHealth = dict.GetInt( "maxhealth", "100" );
  287. armor = dict.GetInt( "armor", "50" );
  288. maxarmor = dict.GetInt( "maxarmor", "100" );
  289. deplete_armor = dict.GetInt( "deplete_armor", "0" );
  290. deplete_rate = dict.GetFloat( "deplete_rate", "2.0" );
  291. deplete_ammount = dict.GetInt( "deplete_ammount", "1" );
  292. // the clip and powerups aren't restored
  293. // ammo
  294. for( i = 0; i < AMMO_NUMTYPES; i++ ) {
  295. name = idWeapon::GetAmmoNameForNum( ( ammo_t )i );
  296. if ( name ) {
  297. ammo[ i ] = dict.GetInt( name );
  298. }
  299. }
  300. //Restore the clip data
  301. for( i = 0; i < MAX_WEAPONS; i++ ) {
  302. clip[i] = dict.GetInt(va("clip%i", i), "-1");
  303. }
  304. // items
  305. num = dict.GetInt( "items" );
  306. items.SetNum( num );
  307. for( i = 0; i < num; i++ ) {
  308. item = new (TAG_ENTITY) idDict();
  309. items[ i ] = item;
  310. sprintf( itemname, "item_%i ", i );
  311. kv = dict.MatchPrefix( itemname );
  312. while( kv ) {
  313. key = kv->GetKey();
  314. key.Strip( itemname );
  315. item->Set( key, kv->GetValue() );
  316. kv = dict.MatchPrefix( itemname, kv );
  317. }
  318. }
  319. // pdas viewed
  320. for ( i = 0; i < 4; i++ ) {
  321. pdasViewed[i] = dict.GetInt(va("pdasViewed_%i", i));
  322. }
  323. selPDA = dict.GetInt( "selPDA" );
  324. selEMail = dict.GetInt( "selEmail" );
  325. selVideo = dict.GetInt( "selVideo" );
  326. selAudio = dict.GetInt( "selAudio" );
  327. pdaOpened = dict.GetBool( "pdaOpened" );
  328. // pdas
  329. num = dict.GetInt( "pdas" );
  330. pdas.SetNum( num );
  331. for ( i = 0; i < num; i++ ) {
  332. sprintf( itemname, "pda_%i", i );
  333. pdas[i] = static_cast<const idDeclPDA *>( declManager->FindType( DECL_PDA, dict.GetString( itemname, "default" ) ) );
  334. }
  335. // videos
  336. num = dict.GetInt( "videos" );
  337. videos.SetNum( num );
  338. for ( i = 0; i < num; i++ ) {
  339. sprintf( itemname, "video_%i", i );
  340. videos[i] = static_cast<const idDeclVideo *>( declManager->FindType( DECL_VIDEO, dict.GetString( itemname, "default" ) ) );
  341. }
  342. // emails
  343. num = dict.GetInt( "emails" );
  344. emails.SetNum( num );
  345. for ( i = 0; i < num; i++ ) {
  346. sprintf( itemname, "email_%i", i );
  347. emails[i] = static_cast<const idDeclEmail *>( declManager->FindType( DECL_EMAIL, dict.GetString( itemname, "default" ) ) );
  348. }
  349. // weapons are stored as a number for persistant data, but as strings in the entityDef
  350. weapons = dict.GetInt( "weapon_bits", "0" );
  351. if ( g_skill.GetInteger() >= 3 || cvarSystem->GetCVarBool( "fs_buildresources" ) ) {
  352. Give( owner, dict, "weapon", dict.GetString( "weapon_nightmare" ), NULL, false, ITEM_GIVE_FEEDBACK | ITEM_GIVE_UPDATE_STATE );
  353. } else {
  354. Give( owner, dict, "weapon", dict.GetString( "weapon" ), NULL, false, ITEM_GIVE_FEEDBACK | ITEM_GIVE_UPDATE_STATE );
  355. }
  356. num = dict.GetInt( "levelTriggers" );
  357. for ( i = 0; i < num; i++ ) {
  358. sprintf( itemname, "levelTrigger_Level_%i", i );
  359. idLevelTriggerInfo lti;
  360. lti.levelName = dict.GetString( itemname );
  361. sprintf( itemname, "levelTrigger_Trigger_%i", i );
  362. lti.triggerName = dict.GetString( itemname );
  363. levelTriggers.Append( lti );
  364. }
  365. }
  366. /*
  367. ==============
  368. idInventory::Save
  369. ==============
  370. */
  371. void idInventory::Save( idSaveGame *savefile ) const {
  372. int i;
  373. savefile->WriteInt( maxHealth );
  374. savefile->WriteInt( weapons );
  375. savefile->WriteInt( powerups );
  376. savefile->WriteInt( armor );
  377. savefile->WriteInt( maxarmor );
  378. savefile->WriteInt( ammoPredictTime );
  379. savefile->WriteInt( deplete_armor );
  380. savefile->WriteFloat( deplete_rate );
  381. savefile->WriteInt( deplete_ammount );
  382. savefile->WriteInt( nextArmorDepleteTime );
  383. for( i = 0; i < AMMO_NUMTYPES; i++ ) {
  384. savefile->WriteInt( ammo[ i ].Get() );
  385. }
  386. for( i = 0; i < MAX_WEAPONS; i++ ) {
  387. savefile->WriteInt( clip[ i ].Get() );
  388. }
  389. for( i = 0; i < MAX_POWERUPS; i++ ) {
  390. savefile->WriteInt( powerupEndTime[ i ] );
  391. }
  392. savefile->WriteInt( items.Num() );
  393. for( i = 0; i < items.Num(); i++ ) {
  394. savefile->WriteDict( items[ i ] );
  395. }
  396. savefile->WriteInt( pdasViewed[0] );
  397. savefile->WriteInt( pdasViewed[1] );
  398. savefile->WriteInt( pdasViewed[2] );
  399. savefile->WriteInt( pdasViewed[3] );
  400. savefile->WriteInt( selPDA );
  401. savefile->WriteInt( selVideo );
  402. savefile->WriteInt( selEMail );
  403. savefile->WriteInt( selAudio );
  404. savefile->WriteBool( pdaOpened );
  405. savefile->WriteInt( pdas.Num() );
  406. for( i = 0; i < pdas.Num(); i++ ) {
  407. savefile->WriteString( pdas[ i ]->GetName() );
  408. }
  409. savefile->WriteInt( pdaSecurity.Num() );
  410. for( i=0; i < pdaSecurity.Num(); i++ ) {
  411. savefile->WriteString( pdaSecurity[ i ] );
  412. }
  413. savefile->WriteInt( videos.Num() );
  414. for( i = 0; i < videos.Num(); i++ ) {
  415. savefile->WriteString( videos[ i ]->GetName() );
  416. }
  417. savefile->WriteInt( emails.Num() );
  418. for ( i = 0; i < emails.Num(); i++ ) {
  419. savefile->WriteString( emails[ i ]->GetName() );
  420. }
  421. savefile->WriteInt( nextItemPickup );
  422. savefile->WriteInt( nextItemNum );
  423. savefile->WriteInt( onePickupTime );
  424. savefile->WriteInt( pickupItemNames.Num() );
  425. for( i = 0; i < pickupItemNames.Num(); i++ ) {
  426. savefile->WriteString( pickupItemNames[i] );
  427. }
  428. savefile->WriteInt( objectiveNames.Num() );
  429. for( i = 0; i < objectiveNames.Num(); i++ ) {
  430. savefile->WriteMaterial( objectiveNames[i].screenshot );
  431. savefile->WriteString( objectiveNames[i].text );
  432. savefile->WriteString( objectiveNames[i].title );
  433. }
  434. savefile->WriteInt( levelTriggers.Num() );
  435. for ( i = 0; i < levelTriggers.Num(); i++ ) {
  436. savefile->WriteString( levelTriggers[i].levelName );
  437. savefile->WriteString( levelTriggers[i].triggerName );
  438. }
  439. savefile->WriteBool( ammoPulse );
  440. savefile->WriteBool( weaponPulse );
  441. savefile->WriteBool( armorPulse );
  442. savefile->WriteInt( lastGiveTime );
  443. for(i = 0; i < AMMO_NUMTYPES; i++) {
  444. savefile->WriteInt(rechargeAmmo[i].ammo);
  445. savefile->WriteInt(rechargeAmmo[i].rechargeTime);
  446. savefile->WriteString(rechargeAmmo[i].ammoName);
  447. }
  448. }
  449. /*
  450. ==============
  451. idInventory::Restore
  452. ==============
  453. */
  454. void idInventory::Restore( idRestoreGame *savefile ) {
  455. int i, num;
  456. savefile->ReadInt( maxHealth );
  457. savefile->ReadInt( weapons );
  458. savefile->ReadInt( powerups );
  459. savefile->ReadInt( armor );
  460. savefile->ReadInt( maxarmor );
  461. savefile->ReadInt( ammoPredictTime );
  462. savefile->ReadInt( deplete_armor );
  463. savefile->ReadFloat( deplete_rate );
  464. savefile->ReadInt( deplete_ammount );
  465. savefile->ReadInt( nextArmorDepleteTime );
  466. for( i = 0; i < AMMO_NUMTYPES; i++ ) {
  467. int savedAmmo = 0;
  468. savefile->ReadInt( savedAmmo );
  469. ammo[ i ].Set( savedAmmo );
  470. }
  471. for( i = 0; i < MAX_WEAPONS; i++ ) {
  472. int savedClip = 0;
  473. savefile->ReadInt( savedClip );
  474. clip[ i ].Set( savedClip );
  475. }
  476. for( i = 0; i < MAX_POWERUPS; i++ ) {
  477. savefile->ReadInt( powerupEndTime[ i ] );
  478. }
  479. savefile->ReadInt( num );
  480. for( i = 0; i < num; i++ ) {
  481. idDict *itemdict = new (TAG_ENTITY) idDict;
  482. savefile->ReadDict( itemdict );
  483. items.Append( itemdict );
  484. }
  485. // pdas
  486. savefile->ReadInt( pdasViewed[0] );
  487. savefile->ReadInt( pdasViewed[1] );
  488. savefile->ReadInt( pdasViewed[2] );
  489. savefile->ReadInt( pdasViewed[3] );
  490. savefile->ReadInt( selPDA );
  491. savefile->ReadInt( selVideo );
  492. savefile->ReadInt( selEMail );
  493. savefile->ReadInt( selAudio );
  494. savefile->ReadBool( pdaOpened );
  495. savefile->ReadInt( num );
  496. for( i = 0; i < num; i++ ) {
  497. idStr strPda;
  498. savefile->ReadString( strPda );
  499. pdas.Append( static_cast<const idDeclPDA *>( declManager->FindType( DECL_PDA, strPda ) ) );
  500. }
  501. // pda security clearances
  502. savefile->ReadInt( num );
  503. for ( i = 0; i < num; i++ ) {
  504. idStr invName;
  505. savefile->ReadString( invName );
  506. pdaSecurity.Append( invName );
  507. }
  508. // videos
  509. savefile->ReadInt( num );
  510. for( i = 0; i < num; i++ ) {
  511. idStr strVideo;
  512. savefile->ReadString( strVideo );
  513. videos.Append( static_cast<const idDeclVideo *>( declManager->FindType( DECL_VIDEO, strVideo ) ) );
  514. }
  515. // email
  516. savefile->ReadInt( num );
  517. for( i = 0; i < num; i++ ) {
  518. idStr strEmail;
  519. savefile->ReadString( strEmail );
  520. emails.Append( static_cast<const idDeclEmail *>( declManager->FindType( DECL_EMAIL, strEmail ) ) );
  521. }
  522. savefile->ReadInt( nextItemPickup );
  523. savefile->ReadInt( nextItemNum );
  524. savefile->ReadInt( onePickupTime );
  525. savefile->ReadInt( num );
  526. for( i = 0; i < num; i++ ) {
  527. idStr itemName;
  528. savefile->ReadString( itemName );
  529. pickupItemNames.Append( itemName );
  530. }
  531. savefile->ReadInt( num );
  532. for( i = 0; i < num; i++ ) {
  533. idObjectiveInfo obj;
  534. savefile->ReadMaterial( obj.screenshot );
  535. savefile->ReadString( obj.text );
  536. savefile->ReadString( obj.title );
  537. objectiveNames.Append( obj );
  538. }
  539. savefile->ReadInt( num );
  540. for ( i = 0; i < num; i++ ) {
  541. idLevelTriggerInfo lti;
  542. savefile->ReadString( lti.levelName );
  543. savefile->ReadString( lti.triggerName );
  544. levelTriggers.Append( lti );
  545. }
  546. savefile->ReadBool( ammoPulse );
  547. savefile->ReadBool( weaponPulse );
  548. savefile->ReadBool( armorPulse );
  549. savefile->ReadInt( lastGiveTime );
  550. for(i = 0; i < AMMO_NUMTYPES; i++) {
  551. savefile->ReadInt(rechargeAmmo[i].ammo);
  552. savefile->ReadInt(rechargeAmmo[i].rechargeTime);
  553. idStr name;
  554. savefile->ReadString(name);
  555. strcpy(rechargeAmmo[i].ammoName, name);
  556. }
  557. }
  558. /*
  559. ==============
  560. idInventory::AmmoIndexForAmmoClass
  561. ==============
  562. */
  563. ammo_t idInventory::AmmoIndexForAmmoClass( const char *ammo_classname ) const {
  564. return idWeapon::GetAmmoNumForName( ammo_classname );
  565. }
  566. /*
  567. ==============
  568. idInventory::AmmoIndexForAmmoClass
  569. ==============
  570. */
  571. int idInventory::MaxAmmoForAmmoClass( const idPlayer *owner, const char *ammo_classname ) const {
  572. return owner->spawnArgs.GetInt( va( "max_%s", ammo_classname ), "0" );
  573. }
  574. /*
  575. ==============
  576. idInventory::AmmoPickupNameForIndex
  577. ==============
  578. */
  579. const char *idInventory::AmmoPickupNameForIndex( ammo_t ammonum ) const {
  580. return idWeapon::GetAmmoPickupNameForNum( ammonum );
  581. }
  582. /*
  583. ==============
  584. idInventory::WeaponIndexForAmmoClass
  585. mapping could be prepared in the constructor
  586. ==============
  587. */
  588. int idInventory::WeaponIndexForAmmoClass( const idDict & spawnArgs, const char *ammo_classname ) const {
  589. int i;
  590. const char *weapon_classname;
  591. for( i = 0; i < MAX_WEAPONS; i++ ) {
  592. weapon_classname = spawnArgs.GetString( va( "def_weapon%d", i ) );
  593. if ( !weapon_classname ) {
  594. continue;
  595. }
  596. const idDeclEntityDef *decl = gameLocal.FindEntityDef( weapon_classname, false );
  597. if ( !decl ) {
  598. continue;
  599. }
  600. if ( !idStr::Icmp( ammo_classname, decl->dict.GetString( "ammoType" ) ) ) {
  601. return i;
  602. }
  603. }
  604. return -1;
  605. }
  606. /*
  607. ==============
  608. idInventory::AmmoIndexForWeaponClass
  609. ==============
  610. */
  611. ammo_t idInventory::AmmoIndexForWeaponClass( const char *weapon_classname, int *ammoRequired ) {
  612. const idDeclEntityDef *decl = gameLocal.FindEntityDef( weapon_classname, false );
  613. if ( !decl ) {
  614. //gameLocal.Error( "Unknown weapon in decl '%s'", weapon_classname );
  615. return 0;
  616. }
  617. if ( ammoRequired ) {
  618. *ammoRequired = decl->dict.GetInt( "ammoRequired" );
  619. }
  620. ammo_t ammo_i = AmmoIndexForAmmoClass( decl->dict.GetString( "ammoType" ) );
  621. return ammo_i;
  622. }
  623. /*
  624. ==============
  625. idInventory::AddPickupName
  626. ==============
  627. */
  628. void idInventory::AddPickupName( const char * name, idPlayer * owner ) { //_D3XP
  629. int num = pickupItemNames.Num();
  630. if ( ( num == 0 ) || ( pickupItemNames[ num - 1 ].Icmp( name ) != 0 ) ) {
  631. if ( idStr::Cmpn( name, STRTABLE_ID, STRTABLE_ID_LENGTH ) == 0 ) {
  632. pickupItemNames.Append( idLocalization::GetString( name ) );
  633. } else {
  634. pickupItemNames.Append( name );
  635. }
  636. }
  637. }
  638. /*
  639. ==============
  640. idInventory::Give
  641. ==============
  642. */
  643. bool idInventory::Give( idPlayer *owner, const idDict &spawnArgs, const char *statname, const char *value,
  644. idPredictedValue< int > * idealWeapon, bool updateHud, unsigned int giveFlags ) {
  645. int i;
  646. const char *pos;
  647. const char *end;
  648. int len;
  649. idStr weaponString;
  650. int max;
  651. const idDeclEntityDef *weaponDecl;
  652. bool tookWeapon;
  653. int amount;
  654. const char *name;
  655. if ( !idStr::Icmp( statname, "ammo_bloodstone" ) ) {
  656. i = AmmoIndexForAmmoClass( statname );
  657. max = MaxAmmoForAmmoClass( owner, statname );
  658. if(max <= 0) {
  659. if ( giveFlags & ITEM_GIVE_UPDATE_STATE ) {
  660. //No Max
  661. ammo[ i ] += atoi( value );
  662. }
  663. } else {
  664. //Already at or above the max so don't allow the give
  665. if(ammo[ i ].Get() >= max) {
  666. if ( giveFlags & ITEM_GIVE_UPDATE_STATE ) {
  667. ammo[ i ] = max;
  668. }
  669. return false;
  670. }
  671. if ( giveFlags & ITEM_GIVE_UPDATE_STATE ) {
  672. //We were below the max so accept the give but cap it at the max
  673. ammo[ i ] += atoi( value );
  674. if(ammo[ i ].Get() > max) {
  675. ammo[ i ] = max;
  676. }
  677. }
  678. }
  679. } else if ( !idStr::Icmpn( statname, "ammo_", 5 ) ) {
  680. i = AmmoIndexForAmmoClass( statname );
  681. max = MaxAmmoForAmmoClass( owner, statname );
  682. if ( ammo[ i ].Get() >= max ) {
  683. return false;
  684. }
  685. // Add ammo for the feedback flag because it's predicted.
  686. // If it is a misprediction, the client will be corrected in
  687. // a snapshot.
  688. if ( giveFlags & ITEM_GIVE_FEEDBACK ) {
  689. amount = atoi( value );
  690. if ( amount ) {
  691. ammo[ i ] += amount;
  692. if ( ( max > 0 ) && ( ammo[ i ].Get() > max ) ) {
  693. ammo[ i ] = max;
  694. }
  695. ammoPulse = true;
  696. }
  697. name = AmmoPickupNameForIndex( i );
  698. if ( idStr::Length( name ) ) {
  699. AddPickupName( name, owner ); //_D3XP
  700. }
  701. }
  702. } else if ( !idStr::Icmp( statname, "armor" ) ) {
  703. if ( armor >= maxarmor ) {
  704. return false; // can't hold any more, so leave the item
  705. }
  706. if ( giveFlags & ITEM_GIVE_UPDATE_STATE ) {
  707. amount = atoi( value );
  708. if ( amount ) {
  709. armor += amount;
  710. if ( armor > maxarmor ) {
  711. armor = maxarmor;
  712. }
  713. nextArmorDepleteTime = 0;
  714. armorPulse = true;
  715. }
  716. }
  717. } else if ( idStr::FindText( statname, "inclip_" ) == 0 ) {
  718. if ( giveFlags & ITEM_GIVE_UPDATE_STATE ) {
  719. idStr temp = statname;
  720. i = atoi(temp.Mid(7, 2));
  721. if ( i != -1 ) {
  722. // set, don't add. not going over the clip size limit.
  723. SetClipAmmoForWeapon( i, atoi( value ) );
  724. }
  725. }
  726. } else if ( !idStr::Icmp( statname, "invulnerability" ) ) {
  727. owner->GivePowerUp( INVULNERABILITY, SEC2MS( atof( value ) ), giveFlags );
  728. } else if ( !idStr::Icmp( statname, "helltime" ) ) {
  729. owner->GivePowerUp( HELLTIME, SEC2MS( atof( value ) ), giveFlags );
  730. } else if ( !idStr::Icmp( statname, "envirosuit" ) ) {
  731. owner->GivePowerUp( ENVIROSUIT, SEC2MS( atof( value ) ), giveFlags );
  732. owner->GivePowerUp( ENVIROTIME, SEC2MS( atof( value ) ), giveFlags );
  733. } else if ( !idStr::Icmp( statname, "berserk" ) ) {
  734. owner->GivePowerUp( BERSERK, SEC2MS( atof( value ) ), giveFlags );
  735. //} else if ( !idStr::Icmp( statname, "haste" ) ) {
  736. // owner->GivePowerUp( HASTE, SEC2MS( atof( value ) ) );
  737. } else if ( !idStr::Icmp( statname, "adrenaline" ) ) {
  738. if ( giveFlags & ITEM_GIVE_UPDATE_STATE ) {
  739. GivePowerUp( owner, ADRENALINE, SEC2MS( atof( value ) ) );
  740. }
  741. } else if ( !idStr::Icmp( statname, "mega" ) ) {
  742. if ( giveFlags & ITEM_GIVE_UPDATE_STATE ) {
  743. GivePowerUp( owner, MEGAHEALTH, SEC2MS( atof( value ) ) );
  744. }
  745. } else if ( !idStr::Icmp( statname, "weapon" ) ) {
  746. tookWeapon = false;
  747. for( pos = value; pos != NULL; pos = end ) {
  748. end = strchr( pos, ',' );
  749. if ( end ) {
  750. len = end - pos;
  751. end++;
  752. } else {
  753. len = strlen( pos );
  754. }
  755. idStr weaponName( pos, 0, len );
  756. // find the number of the matching weapon name
  757. for( i = 0; i < MAX_WEAPONS; i++ ) {
  758. if ( weaponName == spawnArgs.GetString( va( "def_weapon%d", i ) ) ) {
  759. break;
  760. }
  761. }
  762. if ( i >= MAX_WEAPONS ) {
  763. gameLocal.Warning( "Unknown weapon '%s'", weaponName.c_str() );
  764. continue;
  765. }
  766. // cache the media for this weapon
  767. weaponDecl = gameLocal.FindEntityDef( weaponName, false );
  768. // don't pickup "no ammo" weapon types twice
  769. // not for D3 SP .. there is only one case in the game where you can get a no ammo
  770. // weapon when you might already have it, in that case it is more conistent to pick it up
  771. if ( common->IsMultiplayer() && ( weapons & ( 1 << i ) ) && ( weaponDecl != NULL ) && !weaponDecl->dict.GetInt( "ammoRequired" ) ) {
  772. continue;
  773. }
  774. if ( !gameLocal.world->spawnArgs.GetBool( "no_Weapons" ) || ( weaponName == "weapon_fists" ) || ( weaponName == "weapon_soulcube" ) ) {
  775. if ( ( weapons & ( 1 << i ) ) == 0 || common->IsMultiplayer() ) {
  776. tookWeapon = true;
  777. // This is done during "feedback" so that clients can predict the ideal weapon.
  778. if ( giveFlags & ITEM_GIVE_FEEDBACK ) {
  779. idLobbyBase & lobby = session->GetActingGameStateLobbyBase();
  780. lobbyUserID_t & lobbyUserID = gameLocal.lobbyUserIDs[owner->entityNumber];
  781. if ( lobby.GetLobbyUserWeaponAutoSwitch( lobbyUserID ) && idealWeapon != NULL && i != owner->weapon_bloodstone_active1 && i != owner->weapon_bloodstone_active2 && i != owner->weapon_bloodstone_active3) {
  782. idealWeapon->Set( i );
  783. }
  784. }
  785. if ( giveFlags & ITEM_GIVE_UPDATE_STATE ) {
  786. if ( updateHud && lastGiveTime + 1000 < gameLocal.time ) {
  787. if ( owner->hud ) {
  788. owner->hud->GiveWeapon( owner, i );
  789. }
  790. lastGiveTime = gameLocal.time;
  791. }
  792. weaponPulse = true;
  793. weapons |= ( 1 << i );
  794. if ( weaponName != "weapon_pda" ) {
  795. for ( int index = 0; index < NUM_QUICK_SLOTS; ++index ) {
  796. if ( owner->GetQuickSlot( index ) == -1 ) {
  797. owner->SetQuickSlot( index, i );
  798. break;
  799. }
  800. }
  801. }
  802. }
  803. }
  804. }
  805. }
  806. return tookWeapon;
  807. } else if ( !idStr::Icmp( statname, "item" ) || !idStr::Icmp( statname, "icon" ) || !idStr::Icmp( statname, "name" ) ) {
  808. // ignore these as they're handled elsewhere
  809. return false;
  810. } else {
  811. // unknown item
  812. gameLocal.Warning( "Unknown stat '%s' added to player's inventory", statname );
  813. return false;
  814. }
  815. return true;
  816. }
  817. /*
  818. ===============
  819. idInventoy::Drop
  820. ===============
  821. */
  822. void idInventory::Drop( const idDict &spawnArgs, const char *weapon_classname, int weapon_index ) {
  823. // remove the weapon bit
  824. // also remove the ammo associated with the weapon as we pushed it in the item
  825. assert( weapon_index != -1 || weapon_classname );
  826. if ( weapon_index == -1 ) {
  827. for( weapon_index = 0; weapon_index < MAX_WEAPONS; weapon_index++ ) {
  828. if ( !idStr::Icmp( weapon_classname, spawnArgs.GetString( va( "def_weapon%d", weapon_index ) ) ) ) {
  829. break;
  830. }
  831. }
  832. if ( weapon_index >= MAX_WEAPONS ) {
  833. gameLocal.Error( "Unknown weapon '%s'", weapon_classname );
  834. }
  835. } else if ( !weapon_classname ) {
  836. weapon_classname = spawnArgs.GetString( va( "def_weapon%d", weapon_index ) );
  837. }
  838. weapons &= ( 0xffffffff ^ ( 1 << weapon_index ) );
  839. ammo_t ammo_i = AmmoIndexForWeaponClass( weapon_classname, NULL );
  840. if ( ammo_i && ammo_i < AMMO_NUMTYPES ) {
  841. clip[ weapon_index ] = -1;
  842. ammo[ ammo_i ] = 0;
  843. }
  844. }
  845. /*
  846. ===============
  847. idInventory::HasAmmo
  848. ===============
  849. */
  850. int idInventory::HasAmmo( ammo_t type, int amount ) {
  851. if ( ( type == 0 ) || !amount ) {
  852. // always allow weapons that don't use ammo to fire
  853. return -1;
  854. }
  855. // check if we have infinite ammo
  856. if ( ammo[ type ].Get() < 0 ) {
  857. return -1;
  858. }
  859. // return how many shots we can fire
  860. return ammo[ type ].Get() / amount;
  861. }
  862. /*
  863. ===============
  864. idInventory::HasAmmo
  865. ===============
  866. */
  867. int idInventory::HasAmmo( const char *weapon_classname, bool includeClip, idPlayer* owner ) { //_D3XP
  868. int ammoRequired;
  869. ammo_t ammo_i = AmmoIndexForWeaponClass( weapon_classname, &ammoRequired );
  870. int ammoCount = HasAmmo( ammo_i, ammoRequired );
  871. if(includeClip && owner) {
  872. ammoCount += Max( 0, clip[owner->SlotForWeapon(weapon_classname)].Get() );
  873. }
  874. return ammoCount;
  875. }
  876. /*
  877. ===============
  878. idInventory::HasEmptyClipCannotRefill
  879. ===============
  880. */
  881. bool idInventory::HasEmptyClipCannotRefill(const char *weapon_classname, idPlayer* owner) {
  882. int clipSize = clip[owner->SlotForWeapon(weapon_classname)].Get();
  883. if(clipSize) {
  884. return false;
  885. }
  886. const idDeclEntityDef *decl = gameLocal.FindEntityDef( weapon_classname, false );
  887. if ( decl == NULL ) {
  888. gameLocal.Error( "Unknown weapon in decl '%s'", weapon_classname );
  889. return false;
  890. }
  891. int minclip = decl->dict.GetInt("minclipsize");
  892. if(!minclip) {
  893. return false;
  894. }
  895. ammo_t ammo_i = AmmoIndexForAmmoClass( decl->dict.GetString( "ammoType" ) );
  896. int ammoRequired = decl->dict.GetInt( "ammoRequired" );
  897. int ammoCount = HasAmmo( ammo_i, ammoRequired );
  898. if(ammoCount < minclip) {
  899. return true;
  900. }
  901. return false;
  902. }
  903. /*
  904. ===============
  905. idInventory::UseAmmo
  906. ===============
  907. */
  908. bool idInventory::UseAmmo( ammo_t type, int amount ) {
  909. if ( g_infiniteAmmo.GetBool() ) {
  910. return true;
  911. }
  912. if ( !HasAmmo( type, amount ) ) {
  913. return false;
  914. }
  915. // take an ammo away if not infinite
  916. if ( ammo[ type ].Get() >= 0 ) {
  917. const int currentAmmo = GetInventoryAmmoForType( type );
  918. SetInventoryAmmoForType( type, currentAmmo - amount );
  919. }
  920. return true;
  921. }
  922. /*
  923. ===============
  924. idInventory::UpdateArmor
  925. ===============
  926. */
  927. void idInventory::UpdateArmor() {
  928. if ( deplete_armor != 0.0f && deplete_armor < armor ) {
  929. if ( !nextArmorDepleteTime ) {
  930. nextArmorDepleteTime = gameLocal.time + deplete_rate * 1000;
  931. } else if ( gameLocal.time > nextArmorDepleteTime ) {
  932. armor -= deplete_ammount;
  933. if ( armor < deplete_armor ) {
  934. armor = deplete_armor;
  935. }
  936. nextArmorDepleteTime = gameLocal.time + deplete_rate * 1000;
  937. }
  938. }
  939. }
  940. /*
  941. ===============
  942. idInventory::InitRechargeAmmo
  943. ===============
  944. * Loads any recharge ammo definitions from the ammo_types entity definitions.
  945. */
  946. void idInventory::InitRechargeAmmo(idPlayer *owner) {
  947. memset (rechargeAmmo, 0, sizeof(rechargeAmmo));
  948. const idKeyValue *kv = owner->spawnArgs.MatchPrefix( "ammorecharge_" );
  949. while( kv ) {
  950. idStr key = kv->GetKey();
  951. idStr ammoname = key.Right(key.Length()- strlen("ammorecharge_"));
  952. int ammoType = AmmoIndexForAmmoClass(ammoname);
  953. rechargeAmmo[ammoType].ammo = (atof(kv->GetValue().c_str())*1000);
  954. strcpy(rechargeAmmo[ammoType].ammoName, ammoname);
  955. kv = owner->spawnArgs.MatchPrefix( "ammorecharge_", kv );
  956. }
  957. }
  958. /*
  959. ===============
  960. idInventory::RechargeAmmo
  961. ===============
  962. * Called once per frame to update any ammo amount for ammo types that recharge.
  963. */
  964. void idInventory::RechargeAmmo(idPlayer *owner) {
  965. for(int i = 0; i < AMMO_NUMTYPES; i++) {
  966. if(rechargeAmmo[i].ammo > 0) {
  967. if(!rechargeAmmo[i].rechargeTime) {
  968. //Initialize the recharge timer.
  969. rechargeAmmo[i].rechargeTime = gameLocal.time;
  970. }
  971. int elapsed = gameLocal.time - rechargeAmmo[i].rechargeTime;
  972. if(elapsed >= rechargeAmmo[i].ammo) {
  973. int intervals = (gameLocal.time - rechargeAmmo[i].rechargeTime)/rechargeAmmo[i].ammo;
  974. ammo[i] += intervals;
  975. int max = MaxAmmoForAmmoClass(owner, rechargeAmmo[i].ammoName);
  976. if(max > 0) {
  977. if(ammo[i].Get() > max) {
  978. ammo[i] = max;
  979. }
  980. }
  981. rechargeAmmo[i].rechargeTime += intervals*rechargeAmmo[i].ammo;
  982. }
  983. }
  984. }
  985. }
  986. /*
  987. ===============
  988. idInventory::CanGive
  989. ===============
  990. */
  991. bool idInventory::CanGive( idPlayer *owner, const idDict &spawnArgs, const char *statname, const char *value ) {
  992. if ( !idStr::Icmp( statname, "ammo_bloodstone" ) ) {
  993. int max = MaxAmmoForAmmoClass(owner, statname);
  994. int i = AmmoIndexForAmmoClass(statname);
  995. if(max <= 0) {
  996. //No Max
  997. return true;
  998. } else {
  999. //Already at or above the max so don't allow the give
  1000. if(ammo[ i ].Get() >= max) {
  1001. ammo[ i ] = max;
  1002. return false;
  1003. }
  1004. return true;
  1005. }
  1006. } else if ( !idStr::Icmp( statname, "item" ) || !idStr::Icmp( statname, "icon" ) || !idStr::Icmp( statname, "name" ) ) {
  1007. // ignore these as they're handled elsewhere
  1008. //These items should not be considered as succesful gives because it messes up the max ammo items
  1009. return false;
  1010. }
  1011. return true;
  1012. }
  1013. /*
  1014. ===============
  1015. idInventory::SetClipAmmoForWeapon
  1016. Ammo is predicted on clients. This function ensures the frame the prediction occurs
  1017. is stored so the predicted value doesn't get overwritten by snapshots. Of course
  1018. the snapshot-reading function must check this value.
  1019. ===============
  1020. */
  1021. void idInventory::SetClipAmmoForWeapon( const int weapon, const int amount ) {
  1022. clip[weapon] = amount;
  1023. }
  1024. /*
  1025. ===============
  1026. idInventory::SetInventoryAmmoForType
  1027. Ammo is predicted on clients. This function ensures the frame the prediction occurs
  1028. is stored so the predicted value doesn't get overwritten by snapshots. Of course
  1029. the snapshot-reading function must check this value.
  1030. ===============
  1031. */
  1032. void idInventory::SetInventoryAmmoForType( int ammoType, const int amount ) {
  1033. ammo[ammoType] = amount;
  1034. }
  1035. /*
  1036. ===============
  1037. idInventory::GetClipAmmoForWeapon
  1038. ===============
  1039. */
  1040. int idInventory::GetClipAmmoForWeapon( const int weapon ) const {
  1041. return clip[weapon].Get();
  1042. }
  1043. /*
  1044. ===============
  1045. idInventory::GetInventoryAmmoForType
  1046. ===============
  1047. */
  1048. int idInventory::GetInventoryAmmoForType( const int ammoType ) const {
  1049. return ammo[ammoType].Get();
  1050. }
  1051. /*
  1052. ===============
  1053. idInventory::WriteAmmoToSnapshot
  1054. ===============
  1055. */
  1056. void idInventory::WriteAmmoToSnapshot( idBitMsg & msg ) const {
  1057. for( int i = 0; i < AMMO_NUMTYPES; i++ ) {
  1058. msg.WriteBits( ammo[i].Get(), ASYNC_PLAYER_INV_AMMO_BITS );
  1059. }
  1060. for( int i = 0; i < MAX_WEAPONS; i++ ) {
  1061. msg.WriteBits( clip[i].Get(), ASYNC_PLAYER_INV_CLIP_BITS );
  1062. }
  1063. }
  1064. /*
  1065. ===============
  1066. idInventory::ReadAmmoFromSnapshot
  1067. ===============
  1068. */
  1069. void idInventory::ReadAmmoFromSnapshot( const idBitMsg & msg, const int ownerEntityNumber ) {
  1070. for( int i = 0; i < ammo.Num(); i++ ) {
  1071. const int snapshotAmmo = msg.ReadBits( ASYNC_PLAYER_INV_AMMO_BITS );
  1072. ammo[i].UpdateFromSnapshot( snapshotAmmo, ownerEntityNumber );
  1073. }
  1074. for( int i = 0; i < clip.Num(); i++ ) {
  1075. const int snapshotClip = msg.ReadBits( ASYNC_PLAYER_INV_CLIP_BITS );
  1076. clip[i].UpdateFromSnapshot( snapshotClip, ownerEntityNumber );
  1077. }
  1078. }
  1079. /*
  1080. ===============
  1081. idInventory::ReadAmmoFromSnapshot
  1082. Doesn't really matter what remote client's ammo count is, so just set it to 999.
  1083. ===============
  1084. */
  1085. void idInventory::SetRemoteClientAmmo( const int ownerEntityNumber ) {
  1086. for ( int i = 0; i < ammo.Num(); ++i ) {
  1087. ammo[i].UpdateFromSnapshot( 999, ownerEntityNumber );
  1088. }
  1089. }
  1090. /*
  1091. ==============
  1092. idPlayer::idPlayer
  1093. ==============
  1094. */
  1095. idPlayer::idPlayer():
  1096. previousViewQuat( 0.0f, 0.0f, 0.0f, 1.0f ),
  1097. nextViewQuat( 0.0f, 0.0f, 0.0f, 1.0f ),
  1098. idealWeapon( -1 ),
  1099. serverOverridePositionTime( 0 ),
  1100. clientFireCount( 0 ) {
  1101. noclip = false;
  1102. godmode = false;
  1103. spawnAnglesSet = false;
  1104. spawnAngles = ang_zero;
  1105. viewAngles = ang_zero;
  1106. cmdAngles = ang_zero;
  1107. independentWeaponPitchAngle = 0.0f;
  1108. oldButtons = 0;
  1109. buttonMask = 0;
  1110. oldImpulseSequence = 0;
  1111. lastHitTime = 0;
  1112. lastSndHitTime = 0;
  1113. lastSavingThrowTime = 0;
  1114. laserSightHandle = -1;
  1115. memset( &laserSightRenderEntity, 0, sizeof( laserSightRenderEntity ) );
  1116. weapon = NULL;
  1117. primaryObjective = NULL;
  1118. hudManager = new idMenuHandler_HUD();
  1119. hud = NULL;
  1120. objectiveSystemOpen = false;
  1121. memset( quickSlot, -1, sizeof( quickSlot ) );
  1122. pdaMenu = new (TAG_SWF) idMenuHandler_PDA();
  1123. pdaVideoMat = NULL;
  1124. mpMessages = NULL;
  1125. mountedObject = NULL;
  1126. enviroSuitLight = NULL;
  1127. heartRate = BASE_HEARTRATE;
  1128. heartInfo.Init( 0, 0, 0, 0 );
  1129. lastHeartAdjust = 0;
  1130. lastHeartBeat = 0;
  1131. lastDmgTime = 0;
  1132. deathClearContentsTime = 0;
  1133. lastArmorPulse = -10000;
  1134. stamina = 0.0f;
  1135. healthPool = 0.0f;
  1136. nextHealthPulse = 0;
  1137. healthPulse = false;
  1138. nextHealthTake = 0;
  1139. healthTake = false;
  1140. forceScoreBoard = false;
  1141. forceRespawn = false;
  1142. spectating = false;
  1143. spectator = 0;
  1144. wantSpectate = true;
  1145. carryingFlag = false;
  1146. lastHitToggle = false;
  1147. minRespawnTime = 0;
  1148. maxRespawnTime = 0;
  1149. firstPersonViewOrigin = vec3_zero;
  1150. firstPersonViewAxis = mat3_identity;
  1151. hipJoint = INVALID_JOINT;
  1152. chestJoint = INVALID_JOINT;
  1153. headJoint = INVALID_JOINT;
  1154. bobFoot = 0;
  1155. bobFrac = 0.0f;
  1156. bobfracsin = 0.0f;
  1157. bobCycle = 0;
  1158. xyspeed = 0.0f;
  1159. stepUpTime = 0;
  1160. stepUpDelta = 0.0f;
  1161. idealLegsYaw = 0.0f;
  1162. legsYaw = 0.0f;
  1163. legsForward = true;
  1164. oldViewYaw = 0.0f;
  1165. viewBobAngles = ang_zero;
  1166. viewBob = vec3_zero;
  1167. landChange = 0;
  1168. landTime = 0;
  1169. currentWeapon = -1;
  1170. previousWeapon = -1;
  1171. weaponSwitchTime = 0;
  1172. weaponEnabled = true;
  1173. weapon_soulcube = -1;
  1174. weapon_pda = -1;
  1175. weapon_fists = -1;
  1176. weapon_chainsaw = -1;
  1177. weapon_bloodstone = -1;
  1178. weapon_bloodstone_active1 = -1;
  1179. weapon_bloodstone_active2 = -1;
  1180. weapon_bloodstone_active3 = -1;
  1181. harvest_lock = false;
  1182. hudPowerup = -1;
  1183. lastHudPowerup = -1;
  1184. hudPowerupDuration = 0;
  1185. skinIndex = 0;
  1186. skin = NULL;
  1187. powerUpSkin = NULL;
  1188. numProjectileKills = 0;
  1189. numProjectilesFired = 0;
  1190. numProjectileHits = 0;
  1191. airless = false;
  1192. airMsec = 0;
  1193. lastAirDamage = 0;
  1194. gibDeath = false;
  1195. gibsLaunched = false;
  1196. gibsDir = vec3_zero;
  1197. zoomFov.Init( 0, 0, 0, 0 );
  1198. centerView.Init( 0, 0, 0, 0 );
  1199. fxFov = false;
  1200. influenceFov = 0;
  1201. influenceActive = 0;
  1202. influenceRadius = 0.0f;
  1203. influenceEntity = NULL;
  1204. influenceMaterial = NULL;
  1205. influenceSkin = NULL;
  1206. privateCameraView = NULL;
  1207. memset( loggedViewAngles, 0, sizeof( loggedViewAngles ) );
  1208. memset( loggedAccel, 0, sizeof( loggedAccel ) );
  1209. currentLoggedAccel = 0;
  1210. focusTime = 0;
  1211. focusGUIent = NULL;
  1212. focusUI = NULL;
  1213. focusCharacter = NULL;
  1214. talkCursor = 0;
  1215. focusVehicle = NULL;
  1216. cursor = NULL;
  1217. oldMouseX = 0;
  1218. oldMouseY = 0;
  1219. lastDamageDef = 0;
  1220. lastDamageDir = vec3_zero;
  1221. lastDamageLocation = 0;
  1222. smoothedFrame = 0;
  1223. smoothedOriginUpdated = false;
  1224. smoothedOrigin = vec3_zero;
  1225. smoothedAngles = ang_zero;
  1226. fl.networkSync = true;
  1227. doingDeathSkin = false;
  1228. weaponGone = false;
  1229. useInitialSpawns = false;
  1230. tourneyRank = 0;
  1231. lastSpectateTeleport = 0;
  1232. tourneyLine = 0;
  1233. hiddenWeapon = false;
  1234. tipUp = false;
  1235. objectiveUp = false;
  1236. teleportEntity = NULL;
  1237. teleportKiller = -1;
  1238. respawning = false;
  1239. leader = false;
  1240. lastSpectateChange = 0;
  1241. lastTeleFX = -9999;
  1242. weaponCatchup = false;
  1243. clientFireCount = 0;
  1244. MPAim = -1;
  1245. lastMPAim = -1;
  1246. lastMPAimTime = 0;
  1247. MPAimFadeTime = 0;
  1248. MPAimHighlight = false;
  1249. spawnedTime = 0;
  1250. lastManOver = false;
  1251. lastManPlayAgain = false;
  1252. lastManPresent = false;
  1253. isTelefragged = false;
  1254. isLagged = false;
  1255. isChatting = 0;
  1256. selfSmooth = false;
  1257. playedTimeSecs = 0;
  1258. playedTimeResidual = 0;
  1259. ResetControllerShake();
  1260. memset( pdaHasBeenRead, 0, sizeof( pdaHasBeenRead ) );
  1261. memset( videoHasBeenViewed, 0, sizeof( videoHasBeenViewed ) );
  1262. memset( audioHasBeenHeard, 0, sizeof( audioHasBeenHeard ) );
  1263. }
  1264. /*
  1265. ==============
  1266. idPlayer::LinkScriptVariables
  1267. set up conditions for animation
  1268. ==============
  1269. */
  1270. void idPlayer::LinkScriptVariables() {
  1271. AI_FORWARD.LinkTo( scriptObject, "AI_FORWARD" );
  1272. AI_BACKWARD.LinkTo( scriptObject, "AI_BACKWARD" );
  1273. AI_STRAFE_LEFT.LinkTo( scriptObject, "AI_STRAFE_LEFT" );
  1274. AI_STRAFE_RIGHT.LinkTo( scriptObject, "AI_STRAFE_RIGHT" );
  1275. AI_ATTACK_HELD.LinkTo( scriptObject, "AI_ATTACK_HELD" );
  1276. AI_WEAPON_FIRED.LinkTo( scriptObject, "AI_WEAPON_FIRED" );
  1277. AI_JUMP.LinkTo( scriptObject, "AI_JUMP" );
  1278. AI_DEAD.LinkTo( scriptObject, "AI_DEAD" );
  1279. AI_CROUCH.LinkTo( scriptObject, "AI_CROUCH" );
  1280. AI_ONGROUND.LinkTo( scriptObject, "AI_ONGROUND" );
  1281. AI_ONLADDER.LinkTo( scriptObject, "AI_ONLADDER" );
  1282. AI_HARDLANDING.LinkTo( scriptObject, "AI_HARDLANDING" );
  1283. AI_SOFTLANDING.LinkTo( scriptObject, "AI_SOFTLANDING" );
  1284. AI_RUN.LinkTo( scriptObject, "AI_RUN" );
  1285. AI_PAIN.LinkTo( scriptObject, "AI_PAIN" );
  1286. AI_RELOAD.LinkTo( scriptObject, "AI_RELOAD" );
  1287. AI_TELEPORT.LinkTo( scriptObject, "AI_TELEPORT" );
  1288. AI_TURN_LEFT.LinkTo( scriptObject, "AI_TURN_LEFT" );
  1289. AI_TURN_RIGHT.LinkTo( scriptObject, "AI_TURN_RIGHT" );
  1290. }
  1291. /*
  1292. ==============
  1293. idPlayer::SetupWeaponEntity
  1294. ==============
  1295. */
  1296. void idPlayer::SetupWeaponEntity() {
  1297. int w;
  1298. const char *weap;
  1299. if ( weapon.GetEntity() ) {
  1300. // get rid of old weapon
  1301. weapon.GetEntity()->Clear();
  1302. currentWeapon = -1;
  1303. } else if ( !common->IsClient() ) {
  1304. weapon = static_cast<idWeapon *>( gameLocal.SpawnEntityType( idWeapon::Type, NULL ) );
  1305. weapon.GetEntity()->SetOwner( this );
  1306. currentWeapon = -1;
  1307. // flashlight
  1308. flashlight = static_cast<idWeapon *>( gameLocal.SpawnEntityType( idWeapon::Type, NULL ) );
  1309. flashlight.GetEntity()->SetFlashlightOwner( this );
  1310. //FlashlightOff();
  1311. }
  1312. for( w = 0; w < MAX_WEAPONS; w++ ) {
  1313. weap = spawnArgs.GetString( va( "def_weapon%d", w ) );
  1314. if ( weap != NULL && *weap != NULL ) {
  1315. idWeapon::CacheWeapon( weap );
  1316. }
  1317. }
  1318. }
  1319. /*
  1320. ==============
  1321. idPlayer::Init
  1322. ==============
  1323. */
  1324. void idPlayer::Init() {
  1325. const char *value;
  1326. const idKeyValue *kv;
  1327. noclip = false;
  1328. godmode = false;
  1329. oldButtons = 0;
  1330. oldImpulseSequence = 0;
  1331. currentWeapon = -1;
  1332. idealWeapon = -1;
  1333. previousWeapon = -1;
  1334. weaponSwitchTime = 0;
  1335. weaponEnabled = true;
  1336. weapon_soulcube = SlotForWeapon( "weapon_soulcube" );
  1337. weapon_pda = SlotForWeapon( "weapon_pda" );
  1338. weapon_fists = SlotForWeapon( "weapon_fists" );
  1339. weapon_flashlight = SlotForWeapon( "weapon_flashlight" );
  1340. weapon_chainsaw = SlotForWeapon( "weapon_chainsaw" );
  1341. weapon_bloodstone = SlotForWeapon( "weapon_bloodstone_passive" );
  1342. weapon_bloodstone_active1 = SlotForWeapon( "weapon_bloodstone_active1" );
  1343. weapon_bloodstone_active2 = SlotForWeapon( "weapon_bloodstone_active2" );
  1344. weapon_bloodstone_active3 = SlotForWeapon( "weapon_bloodstone_active3" );
  1345. harvest_lock = false;
  1346. lastDmgTime = 0;
  1347. lastArmorPulse = -10000;
  1348. lastHeartAdjust = 0;
  1349. lastHeartBeat = 0;
  1350. heartInfo.Init( 0, 0, 0, 0 );
  1351. bobCycle = 0;
  1352. bobFrac = 0.0f;
  1353. landChange = 0;
  1354. landTime = 0;
  1355. zoomFov.Init( 0, 0, 0, 0 );
  1356. centerView.Init( 0, 0, 0, 0 );
  1357. fxFov = false;
  1358. influenceFov = 0;
  1359. influenceActive = 0;
  1360. influenceRadius = 0.0f;
  1361. influenceEntity = NULL;
  1362. influenceMaterial = NULL;
  1363. influenceSkin = NULL;
  1364. mountedObject = NULL;
  1365. if( enviroSuitLight.IsValid() ) {
  1366. enviroSuitLight.GetEntity()->PostEventMS( &EV_Remove, 0 );
  1367. }
  1368. enviroSuitLight = NULL;
  1369. healthRecharge = false;
  1370. lastHealthRechargeTime = 0;
  1371. rechargeSpeed = 500;
  1372. new_g_damageScale = 1.f;
  1373. bloomEnabled = false;
  1374. bloomSpeed = 1.f;
  1375. bloomIntensity = -0.01f;
  1376. inventory.InitRechargeAmmo(this);
  1377. hudPowerup = -1;
  1378. lastHudPowerup = -1;
  1379. hudPowerupDuration = 0;
  1380. currentLoggedAccel = 0;
  1381. focusTime = 0;
  1382. focusGUIent = NULL;
  1383. focusUI = NULL;
  1384. focusCharacter = NULL;
  1385. talkCursor = 0;
  1386. focusVehicle = NULL;
  1387. // remove any damage effects
  1388. playerView.ClearEffects();
  1389. // damage values
  1390. fl.takedamage = true;
  1391. ClearPain();
  1392. // restore persistent data
  1393. RestorePersistantInfo();
  1394. bobCycle = 0;
  1395. stamina = 0.0f;
  1396. healthPool = 0.0f;
  1397. nextHealthPulse = 0;
  1398. healthPulse = false;
  1399. nextHealthTake = 0;
  1400. healthTake = false;
  1401. SetupWeaponEntity();
  1402. currentWeapon = -1;
  1403. previousWeapon = -1;
  1404. heartRate = BASE_HEARTRATE;
  1405. AdjustHeartRate( BASE_HEARTRATE, 0.0f, 0.0f, true );
  1406. idealLegsYaw = 0.0f;
  1407. legsYaw = 0.0f;
  1408. legsForward = true;
  1409. oldViewYaw = 0.0f;
  1410. // set the pm_ cvars
  1411. if ( !common->IsMultiplayer() || common->IsServer() ) {
  1412. kv = spawnArgs.MatchPrefix( "pm_", NULL );
  1413. while( kv ) {
  1414. cvarSystem->SetCVarString( kv->GetKey(), kv->GetValue() );
  1415. kv = spawnArgs.MatchPrefix( "pm_", kv );
  1416. }
  1417. }
  1418. // disable stamina on hell levels
  1419. if ( gameLocal.world && gameLocal.world->spawnArgs.GetBool( "no_stamina" ) ) {
  1420. pm_stamina.SetFloat( 0.0f );
  1421. }
  1422. // stamina always initialized to maximum
  1423. stamina = pm_stamina.GetFloat();
  1424. // air always initialized to maximum too
  1425. airMsec = pm_airMsec.GetFloat();
  1426. airless = false;
  1427. gibDeath = false;
  1428. gibsLaunched = false;
  1429. gibsDir.Zero();
  1430. // set the gravity
  1431. physicsObj.SetGravity( gameLocal.GetGravity() );
  1432. // start out standing
  1433. SetEyeHeight( pm_normalviewheight.GetFloat() );
  1434. stepUpTime = 0;
  1435. stepUpDelta = 0.0f;
  1436. viewBobAngles.Zero();
  1437. viewBob.Zero();
  1438. value = spawnArgs.GetString( "model" );
  1439. if ( value != NULL && ( *value != 0 ) ) {
  1440. SetModel( value );
  1441. }
  1442. if ( hud ) {
  1443. hud->SetCursorState( this, CURSOR_TALK, 0 );
  1444. hud->SetCursorState( this, CURSOR_IN_COMBAT, 1 );
  1445. hud->SetCursorState( this, CURSOR_ITEM, 0 );
  1446. hud->SetCursorState( this, CURSOR_GRABBER, 0 );
  1447. hud->SetCursorState( this, CURSOR_NONE, 0 );
  1448. hud->UpdateCursorState();
  1449. }
  1450. if ( ( common->IsMultiplayer() || g_testDeath.GetBool() ) && skin ) {
  1451. SetSkin( skin );
  1452. renderEntity.shaderParms[6] = 0.0f;
  1453. } else if ( spawnArgs.GetString( "spawn_skin", NULL, &value ) ) {
  1454. skin = declManager->FindSkin( value );
  1455. SetSkin( skin );
  1456. renderEntity.shaderParms[6] = 0.0f;
  1457. }
  1458. value = spawnArgs.GetString( "bone_hips", "" );
  1459. hipJoint = animator.GetJointHandle( value );
  1460. if ( hipJoint == INVALID_JOINT ) {
  1461. gameLocal.Error( "Joint '%s' not found for 'bone_hips' on '%s'", value, name.c_str() );
  1462. }
  1463. value = spawnArgs.GetString( "bone_chest", "" );
  1464. chestJoint = animator.GetJointHandle( value );
  1465. if ( chestJoint == INVALID_JOINT ) {
  1466. gameLocal.Error( "Joint '%s' not found for 'bone_chest' on '%s'", value, name.c_str() );
  1467. }
  1468. value = spawnArgs.GetString( "bone_head", "" );
  1469. headJoint = animator.GetJointHandle( value );
  1470. if ( headJoint == INVALID_JOINT ) {
  1471. gameLocal.Error( "Joint '%s' not found for 'bone_head' on '%s'", value, name.c_str() );
  1472. }
  1473. // initialize the script variables
  1474. AI_FORWARD = false;
  1475. AI_BACKWARD = false;
  1476. AI_STRAFE_LEFT = false;
  1477. AI_STRAFE_RIGHT = false;
  1478. AI_ATTACK_HELD = false;
  1479. AI_WEAPON_FIRED = false;
  1480. AI_JUMP = false;
  1481. AI_DEAD = false;
  1482. AI_CROUCH = false;
  1483. AI_ONGROUND = true;
  1484. AI_ONLADDER = false;
  1485. AI_HARDLANDING = false;
  1486. AI_SOFTLANDING = false;
  1487. AI_RUN = false;
  1488. AI_PAIN = false;
  1489. AI_RELOAD = false;
  1490. AI_TELEPORT = false;
  1491. AI_TURN_LEFT = false;
  1492. AI_TURN_RIGHT = false;
  1493. // reset the script object
  1494. ConstructScriptObject();
  1495. // execute the script so the script object's constructor takes effect immediately
  1496. scriptThread->Execute();
  1497. forceScoreBoard = false;
  1498. privateCameraView = NULL;
  1499. lastSpectateChange = 0;
  1500. lastTeleFX = -9999;
  1501. hiddenWeapon = false;
  1502. tipUp = false;
  1503. objectiveUp = false;
  1504. teleportEntity = NULL;
  1505. teleportKiller = -1;
  1506. leader = false;
  1507. SetPrivateCameraView( NULL );
  1508. MPAim = -1;
  1509. lastMPAim = -1;
  1510. lastMPAimTime = 0;
  1511. MPAimFadeTime = 0;
  1512. MPAimHighlight = false;
  1513. //isChatting = false;
  1514. achievementManager.Init( this );
  1515. flashlightBattery = flashlight_batteryDrainTimeMS.GetInteger(); // fully charged
  1516. aimAssist.Init( this );
  1517. // laser sight for 3DTV
  1518. memset( &laserSightRenderEntity, 0, sizeof( laserSightRenderEntity ) );
  1519. laserSightRenderEntity.hModel = renderModelManager->FindModel( "_BEAM" );
  1520. laserSightRenderEntity.customShader = declManager->FindMaterial( "stereoRenderLaserSight" );
  1521. }
  1522. /*
  1523. ==============
  1524. idPlayer::Spawn
  1525. Prepare any resources used by the player.
  1526. ==============
  1527. */
  1528. void idPlayer::Spawn() {
  1529. idStr temp;
  1530. idBounds bounds;
  1531. if ( entityNumber >= MAX_CLIENTS ) {
  1532. gameLocal.Error( "entityNum > MAX_CLIENTS for player. Player may only be spawned with a client." );
  1533. }
  1534. // allow thinking during cinematics
  1535. cinematic = true;
  1536. if ( common->IsMultiplayer() ) {
  1537. // always start in spectating state waiting to be spawned in
  1538. // do this before SetClipModel to get the right bounding box
  1539. spectating = true;
  1540. }
  1541. // set our collision model
  1542. physicsObj.SetSelf( this );
  1543. SetClipModel();
  1544. physicsObj.SetMass( spawnArgs.GetFloat( "mass", "100" ) );
  1545. physicsObj.SetContents( CONTENTS_BODY );
  1546. physicsObj.SetClipMask( MASK_PLAYERSOLID );
  1547. SetPhysics( &physicsObj );
  1548. InitAASLocation();
  1549. skin = renderEntity.customSkin;
  1550. // only the local player needs guis
  1551. if ( !common->IsMultiplayer() || IsLocallyControlled() ) {
  1552. // load HUD
  1553. if ( hudManager != NULL ) {
  1554. hudManager->Initialize( "hud", common->SW() );
  1555. hudManager->ActivateMenu( true );
  1556. hud = hudManager->GetHud();
  1557. }
  1558. // load cursor
  1559. if ( spawnArgs.GetString( "cursor", "", temp ) ) {
  1560. cursor = uiManager->FindGui( temp, true, common->IsMultiplayer(), common->IsMultiplayer() );
  1561. }
  1562. if ( cursor ) {
  1563. cursor->Activate( true, gameLocal.time );
  1564. }
  1565. if ( pdaMenu != NULL ) {
  1566. pdaMenu->Initialize( "pda", common->SW() );
  1567. }
  1568. objectiveSystemOpen = false;
  1569. }
  1570. if ( common->IsMultiplayer() && mpMessages == NULL ) {
  1571. mpMessages = new idSWF( "mp_messages", common->SW() );
  1572. mpMessages->Activate( true );
  1573. }
  1574. SetLastHitTime( 0 );
  1575. // load the armor sound feedback
  1576. declManager->FindSound( "player_sounds_hitArmor" );
  1577. // set up conditions for animation
  1578. LinkScriptVariables();
  1579. animator.RemoveOriginOffset( true );
  1580. // create combat collision hull for exact collision detection
  1581. SetCombatModel();
  1582. // init the damage effects
  1583. playerView.SetPlayerEntity( this );
  1584. // supress model in non-player views, but allow it in mirrors and remote views
  1585. renderEntity.suppressSurfaceInViewID = entityNumber+1;
  1586. // don't project shadow on self or weapon
  1587. renderEntity.noSelfShadow = true;
  1588. idAFAttachment *headEnt = head.GetEntity();
  1589. if ( headEnt ) {
  1590. headEnt->GetRenderEntity()->suppressSurfaceInViewID = entityNumber+1;
  1591. headEnt->GetRenderEntity()->noSelfShadow = true;
  1592. }
  1593. if ( common->IsMultiplayer() ) {
  1594. Init();
  1595. Hide(); // properly hidden if starting as a spectator
  1596. if ( !common->IsClient() ) {
  1597. // set yourself ready to spawn. idMultiplayerGame will decide when/if appropriate and call SpawnFromSpawnSpot
  1598. SetupWeaponEntity();
  1599. SpawnFromSpawnSpot();
  1600. forceRespawn = true;
  1601. wantSpectate = true;
  1602. assert( spectating );
  1603. }
  1604. } else {
  1605. SetupWeaponEntity();
  1606. SpawnFromSpawnSpot();
  1607. }
  1608. // trigger playtesting item gives, if we didn't get here from a previous level
  1609. // the devmap key will be set on the first devmap, but cleared on any level
  1610. // transitions
  1611. if ( !common->IsMultiplayer() && gameLocal.serverInfo.FindKey( "devmap" ) ) {
  1612. // fire a trigger with the name "devmap"
  1613. idEntity *ent = gameLocal.FindEntity( "devmap" );
  1614. if ( ent ) {
  1615. ent->ActivateTargets( this );
  1616. }
  1617. }
  1618. if ( hud ) {
  1619. if ( weapon_soulcube > 0 && ( inventory.weapons & ( 1 << weapon_soulcube ) ) ) {
  1620. int max_souls = inventory.MaxAmmoForAmmoClass( this, "ammo_souls" );
  1621. if ( inventory.GetInventoryAmmoForType( idWeapon::GetAmmoNumForName( "ammo_souls" ) ) >= max_souls ) {
  1622. hud->SetShowSoulCubeOnLoad( true );
  1623. }
  1624. }
  1625. }
  1626. if ( GetPDA() ) {
  1627. // Add any emails from the inventory
  1628. for ( int i = 0; i < inventory.emails.Num(); i++ ) {
  1629. GetPDA()->AddEmail( inventory.emails[i] );
  1630. }
  1631. GetPDA()->SetSecurity( idLocalization::GetString( "#str_00066" ) );
  1632. }
  1633. if ( gameLocal.world->spawnArgs.GetBool( "no_Weapons" ) ) {
  1634. hiddenWeapon = true;
  1635. if ( weapon.GetEntity() ) {
  1636. weapon.GetEntity()->LowerWeapon();
  1637. }
  1638. idealWeapon = weapon_fists;
  1639. } else {
  1640. hiddenWeapon = false;
  1641. }
  1642. UpdateHudWeapon();
  1643. tipUp = false;
  1644. objectiveUp = false;
  1645. if ( inventory.levelTriggers.Num() ) {
  1646. PostEventMS( &EV_Player_LevelTrigger, 0 );
  1647. }
  1648. inventory.pdaOpened = false;
  1649. inventory.selPDA = 0;
  1650. if ( !common->IsMultiplayer() ) {
  1651. int startingHealth = gameLocal.world->spawnArgs.GetInt( "startingHealth", health );
  1652. if ( health > startingHealth ) {
  1653. health = startingHealth;
  1654. }
  1655. if ( g_skill.GetInteger() < 2 ) {
  1656. if ( health < 25 ) {
  1657. health = 25;
  1658. }
  1659. if ( g_useDynamicProtection.GetBool() ) {
  1660. new_g_damageScale = 1.0f;
  1661. }
  1662. } else {
  1663. new_g_damageScale = 1.0f;
  1664. g_armorProtection.SetFloat( ( g_skill.GetInteger() < 2 ) ? 0.4f : 0.2f );
  1665. if ( g_skill.GetInteger() == 3 ) {
  1666. nextHealthTake = gameLocal.time + g_healthTakeTime.GetInteger() * 1000;
  1667. }
  1668. }
  1669. }
  1670. //Setup the weapon toggle lists
  1671. const idKeyValue *kv;
  1672. kv = spawnArgs.MatchPrefix( "weapontoggle", NULL );
  1673. while( kv ) {
  1674. WeaponToggle_t newToggle;
  1675. strcpy(newToggle.name, kv->GetKey().c_str());
  1676. idStr toggleData = kv->GetValue();
  1677. idLexer src;
  1678. idToken token;
  1679. src.LoadMemory(toggleData, toggleData.Length(), "toggleData");
  1680. while(1) {
  1681. if(!src.ReadToken(&token)) {
  1682. break;
  1683. }
  1684. int index = atoi(token.c_str());
  1685. newToggle.toggleList.Append(index);
  1686. //Skip the ,
  1687. src.ReadToken(&token);
  1688. }
  1689. newToggle.lastUsed = 0;
  1690. weaponToggles.Set(newToggle.name, newToggle);
  1691. kv = spawnArgs.MatchPrefix( "weapontoggle", kv );
  1692. }
  1693. if( g_skill.GetInteger() >= 3 || cvarSystem->GetCVarBool( "fs_buildresources" ) ) {
  1694. if(!WeaponAvailable("weapon_bloodstone_passive")) {
  1695. GiveInventoryItem("weapon_bloodstone_passive");
  1696. }
  1697. if(!WeaponAvailable("weapon_bloodstone_active1")) {
  1698. GiveInventoryItem("weapon_bloodstone_active1");
  1699. }
  1700. if(!WeaponAvailable("weapon_bloodstone_active2")) {
  1701. GiveInventoryItem("weapon_bloodstone_active2");
  1702. }
  1703. if(!WeaponAvailable("weapon_bloodstone_active3")) {
  1704. GiveInventoryItem("weapon_bloodstone_active3");
  1705. }
  1706. }
  1707. bloomEnabled = false;
  1708. bloomSpeed = 1;
  1709. bloomIntensity = -0.01f;
  1710. if ( g_demoMode.GetBool() && weapon.GetEntity() && weapon.GetEntity()->AmmoInClip() == 0 ) {
  1711. weapon.GetEntity()->ForceAmmoInClip();
  1712. }
  1713. }
  1714. /*
  1715. ==============
  1716. idPlayer::~idPlayer()
  1717. Release any resources used by the player.
  1718. ==============
  1719. */
  1720. idPlayer::~idPlayer() {
  1721. delete weapon.GetEntity();
  1722. weapon = NULL;
  1723. delete flashlight.GetEntity();
  1724. flashlight = NULL;
  1725. if ( enviroSuitLight.IsValid() ) {
  1726. enviroSuitLight.GetEntity()->ProcessEvent( &EV_Remove );
  1727. }
  1728. // have to do this here, idMultiplayerGame::DisconnectClient() is too late
  1729. if ( common->IsMultiplayer() && gameLocal.mpGame.IsGametypeFlagBased() ) {
  1730. ReturnFlag();
  1731. }
  1732. delete hudManager;
  1733. hudManager = NULL;
  1734. delete pdaMenu;
  1735. pdaMenu = NULL;
  1736. delete mpMessages;
  1737. mpMessages = NULL;
  1738. }
  1739. /*
  1740. ===========
  1741. idPlayer::Save
  1742. ===========
  1743. */
  1744. void idPlayer::Save( idSaveGame *savefile ) const {
  1745. int i;
  1746. savefile->WriteUsercmd( usercmd );
  1747. playerView.Save( savefile );
  1748. savefile->WriteBool( noclip );
  1749. savefile->WriteBool( godmode );
  1750. // don't save spawnAnglesSet, since we'll have to reset them after loading the savegame
  1751. savefile->WriteAngles( spawnAngles );
  1752. savefile->WriteAngles( viewAngles );
  1753. savefile->WriteAngles( cmdAngles );
  1754. savefile->WriteInt( buttonMask );
  1755. savefile->WriteInt( oldButtons );
  1756. savefile->WriteInt( oldImpulseSequence );
  1757. savefile->WriteInt( lastHitTime );
  1758. savefile->WriteInt( lastSndHitTime );
  1759. savefile->WriteInt( lastSavingThrowTime );
  1760. // idBoolFields don't need to be saved, just re-linked in Restore
  1761. savefile->WriteObject( primaryObjective );
  1762. inventory.Save( savefile );
  1763. weapon.Save( savefile );
  1764. for ( int i = 0; i < NUM_QUICK_SLOTS; ++i ) {
  1765. savefile->WriteInt( quickSlot[ i ] );
  1766. }
  1767. savefile->WriteInt( weapon_soulcube );
  1768. savefile->WriteInt( weapon_pda );
  1769. savefile->WriteInt( weapon_fists );
  1770. savefile->WriteInt( weapon_flashlight );
  1771. savefile->WriteInt( weapon_chainsaw );
  1772. savefile->WriteInt( weapon_bloodstone );
  1773. savefile->WriteInt( weapon_bloodstone_active1 );
  1774. savefile->WriteInt( weapon_bloodstone_active2 );
  1775. savefile->WriteInt( weapon_bloodstone_active3 );
  1776. savefile->WriteBool( harvest_lock );
  1777. savefile->WriteInt( hudPowerup );
  1778. savefile->WriteInt( lastHudPowerup );
  1779. savefile->WriteInt( hudPowerupDuration );
  1780. savefile->WriteInt( heartRate );
  1781. savefile->WriteFloat( heartInfo.GetStartTime() );
  1782. savefile->WriteFloat( heartInfo.GetDuration() );
  1783. savefile->WriteFloat( heartInfo.GetStartValue() );
  1784. savefile->WriteFloat( heartInfo.GetEndValue() );
  1785. savefile->WriteInt( lastHeartAdjust );
  1786. savefile->WriteInt( lastHeartBeat );
  1787. savefile->WriteInt( lastDmgTime );
  1788. savefile->WriteInt( deathClearContentsTime );
  1789. savefile->WriteBool( doingDeathSkin );
  1790. savefile->WriteInt( lastArmorPulse );
  1791. savefile->WriteFloat( stamina );
  1792. savefile->WriteFloat( healthPool );
  1793. savefile->WriteInt( nextHealthPulse );
  1794. savefile->WriteBool( healthPulse );
  1795. savefile->WriteInt( nextHealthTake );
  1796. savefile->WriteBool( healthTake );
  1797. savefile->WriteBool( hiddenWeapon );
  1798. soulCubeProjectile.Save( savefile );
  1799. savefile->WriteInt( spectator );
  1800. savefile->WriteBool( forceScoreBoard );
  1801. savefile->WriteBool( forceRespawn );
  1802. savefile->WriteBool( spectating );
  1803. savefile->WriteInt( lastSpectateTeleport );
  1804. savefile->WriteBool( lastHitToggle );
  1805. savefile->WriteBool( wantSpectate );
  1806. savefile->WriteBool( weaponGone );
  1807. savefile->WriteBool( useInitialSpawns );
  1808. savefile->WriteInt( tourneyRank );
  1809. savefile->WriteInt( tourneyLine );
  1810. teleportEntity.Save( savefile );
  1811. savefile->WriteInt( teleportKiller );
  1812. savefile->WriteInt( minRespawnTime );
  1813. savefile->WriteInt( maxRespawnTime );
  1814. savefile->WriteVec3( firstPersonViewOrigin );
  1815. savefile->WriteMat3( firstPersonViewAxis );
  1816. // don't bother saving dragEntity since it's a dev tool
  1817. savefile->WriteJoint( hipJoint );
  1818. savefile->WriteJoint( chestJoint );
  1819. savefile->WriteJoint( headJoint );
  1820. savefile->WriteStaticObject( physicsObj );
  1821. savefile->WriteInt( aasLocation.Num() );
  1822. for( i = 0; i < aasLocation.Num(); i++ ) {
  1823. savefile->WriteInt( aasLocation[ i ].areaNum );
  1824. savefile->WriteVec3( aasLocation[ i ].pos );
  1825. }
  1826. savefile->WriteInt( bobFoot );
  1827. savefile->WriteFloat( bobFrac );
  1828. savefile->WriteFloat( bobfracsin );
  1829. savefile->WriteInt( bobCycle );
  1830. savefile->WriteFloat( xyspeed );
  1831. savefile->WriteInt( stepUpTime );
  1832. savefile->WriteFloat( stepUpDelta );
  1833. savefile->WriteFloat( idealLegsYaw );
  1834. savefile->WriteFloat( legsYaw );
  1835. savefile->WriteBool( legsForward );
  1836. savefile->WriteFloat( oldViewYaw );
  1837. savefile->WriteAngles( viewBobAngles );
  1838. savefile->WriteVec3( viewBob );
  1839. savefile->WriteInt( landChange );
  1840. savefile->WriteInt( landTime );
  1841. savefile->WriteInt( currentWeapon );
  1842. savefile->WriteInt( idealWeapon.Get() );
  1843. savefile->WriteInt( previousWeapon );
  1844. savefile->WriteInt( weaponSwitchTime );
  1845. savefile->WriteBool( weaponEnabled );
  1846. savefile->WriteInt( skinIndex );
  1847. savefile->WriteSkin( skin );
  1848. savefile->WriteSkin( powerUpSkin );
  1849. savefile->WriteInt( numProjectilesFired );
  1850. savefile->WriteInt( numProjectileHits );
  1851. savefile->WriteBool( airless );
  1852. savefile->WriteInt( airMsec );
  1853. savefile->WriteInt( lastAirDamage );
  1854. savefile->WriteBool( gibDeath );
  1855. savefile->WriteBool( gibsLaunched );
  1856. savefile->WriteVec3( gibsDir );
  1857. savefile->WriteFloat( zoomFov.GetStartTime() );
  1858. savefile->WriteFloat( zoomFov.GetDuration() );
  1859. savefile->WriteFloat( zoomFov.GetStartValue() );
  1860. savefile->WriteFloat( zoomFov.GetEndValue() );
  1861. savefile->WriteFloat( centerView.GetStartTime() );
  1862. savefile->WriteFloat( centerView.GetDuration() );
  1863. savefile->WriteFloat( centerView.GetStartValue() );
  1864. savefile->WriteFloat( centerView.GetEndValue() );
  1865. savefile->WriteBool( fxFov );
  1866. savefile->WriteFloat( influenceFov );
  1867. savefile->WriteInt( influenceActive );
  1868. savefile->WriteFloat( influenceRadius );
  1869. savefile->WriteObject( influenceEntity );
  1870. savefile->WriteMaterial( influenceMaterial );
  1871. savefile->WriteSkin( influenceSkin );
  1872. savefile->WriteObject( privateCameraView );
  1873. for( i = 0; i < NUM_LOGGED_VIEW_ANGLES; i++ ) {
  1874. savefile->WriteAngles( loggedViewAngles[ i ] );
  1875. }
  1876. for( i = 0; i < NUM_LOGGED_ACCELS; i++ ) {
  1877. savefile->WriteInt( loggedAccel[ i ].time );
  1878. savefile->WriteVec3( loggedAccel[ i ].dir );
  1879. }
  1880. savefile->WriteInt( currentLoggedAccel );
  1881. savefile->WriteObject( focusGUIent );
  1882. // can't save focusUI
  1883. savefile->WriteObject( focusCharacter );
  1884. savefile->WriteInt( talkCursor );
  1885. savefile->WriteInt( focusTime );
  1886. savefile->WriteObject( focusVehicle );
  1887. savefile->WriteUserInterface( cursor, false );
  1888. savefile->WriteInt( oldMouseX );
  1889. savefile->WriteInt( oldMouseY );
  1890. savefile->WriteBool( tipUp );
  1891. savefile->WriteBool( objectiveUp );
  1892. savefile->WriteInt( lastDamageDef );
  1893. savefile->WriteVec3( lastDamageDir );
  1894. savefile->WriteInt( lastDamageLocation );
  1895. savefile->WriteInt( smoothedFrame );
  1896. savefile->WriteBool( smoothedOriginUpdated );
  1897. savefile->WriteVec3( smoothedOrigin );
  1898. savefile->WriteAngles( smoothedAngles );
  1899. savefile->WriteBool( respawning );
  1900. savefile->WriteBool( leader );
  1901. savefile->WriteInt( lastSpectateChange );
  1902. savefile->WriteInt( lastTeleFX );
  1903. savefile->WriteFloat( pm_stamina.GetFloat() );
  1904. // TODO_SPARTY hook this up with new hud
  1905. //if ( hud ) {
  1906. // hud->SetStateString( "message", idLocalization::GetString( "#str_02916" ) );
  1907. // hud->HandleNamedEvent( "Message" );
  1908. //}
  1909. savefile->WriteInt(weaponToggles.Num());
  1910. for(i = 0; i < weaponToggles.Num(); i++) {
  1911. WeaponToggle_t* weaponToggle = weaponToggles.GetIndex(i);
  1912. savefile->WriteString(weaponToggle->name);
  1913. savefile->WriteInt(weaponToggle->toggleList.Num());
  1914. for(int j = 0; j < weaponToggle->toggleList.Num(); j++) {
  1915. savefile->WriteInt(weaponToggle->toggleList[j]);
  1916. }
  1917. }
  1918. savefile->WriteObject( mountedObject );
  1919. enviroSuitLight.Save( savefile );
  1920. savefile->WriteBool( healthRecharge );
  1921. savefile->WriteInt( lastHealthRechargeTime );
  1922. savefile->WriteInt( rechargeSpeed );
  1923. savefile->WriteFloat( new_g_damageScale );
  1924. savefile->WriteBool( bloomEnabled );
  1925. savefile->WriteFloat( bloomSpeed );
  1926. savefile->WriteFloat( bloomIntensity );
  1927. savefile->WriteObject( flashlight.GetEntity() );
  1928. savefile->WriteInt( flashlightBattery );
  1929. achievementManager.Save( savefile );
  1930. savefile->WriteInt( playedTimeSecs );
  1931. savefile->WriteInt( playedTimeResidual );
  1932. for ( int i=0; i<MAX_PLAYER_PDA; i++ ) {
  1933. savefile->WriteBool( pdaHasBeenRead[i] );
  1934. }
  1935. for ( int i=0; i<MAX_PLAYER_VIDEO; i++ ) {
  1936. savefile->WriteBool( videoHasBeenViewed[i] );
  1937. }
  1938. for ( int i=0; i<MAX_PLAYER_AUDIO; i++ ) {
  1939. for ( int j=0; j<MAX_PLAYER_AUDIO_ENTRIES; j++ ) {
  1940. savefile->WriteBool( audioHasBeenHeard[i][j] );
  1941. }
  1942. }
  1943. }
  1944. /*
  1945. ===========
  1946. idPlayer::Restore
  1947. ===========
  1948. */
  1949. void idPlayer::Restore( idRestoreGame *savefile ) {
  1950. int i;
  1951. int num;
  1952. float set;
  1953. savefile->ReadUsercmd( usercmd );
  1954. playerView.Restore( savefile );
  1955. savefile->ReadBool( noclip );
  1956. savefile->ReadBool( godmode );
  1957. savefile->ReadAngles( spawnAngles );
  1958. savefile->ReadAngles( viewAngles );
  1959. savefile->ReadAngles( cmdAngles );
  1960. memset( usercmd.angles, 0, sizeof( usercmd.angles ) );
  1961. SetViewAngles( viewAngles );
  1962. spawnAnglesSet = true;
  1963. savefile->ReadInt( buttonMask );
  1964. savefile->ReadInt( oldButtons );
  1965. savefile->ReadInt( oldImpulseSequence );
  1966. usercmd.impulseSequence = 0;
  1967. oldImpulseSequence = 0;
  1968. savefile->ReadInt( lastHitTime );
  1969. savefile->ReadInt( lastSndHitTime );
  1970. savefile->ReadInt( lastSavingThrowTime );
  1971. // Re-link idBoolFields to the scriptObject, values will be restored in scriptObject's restore
  1972. LinkScriptVariables();
  1973. savefile->ReadObject( reinterpret_cast<idClass *&>( primaryObjective ) );
  1974. inventory.Restore( savefile );
  1975. weapon.Restore( savefile );
  1976. if ( hudManager != NULL ) {
  1977. hudManager->Initialize( "hud", common->SW() );
  1978. hudManager->ActivateMenu( true );
  1979. hud = hudManager->GetHud();
  1980. }
  1981. if ( pdaMenu != NULL ) {
  1982. pdaMenu->Initialize( "pda", common->SW() );
  1983. }
  1984. for ( i = 0; i < inventory.emails.Num(); i++ ) {
  1985. GetPDA()->AddEmail( inventory.emails[i] );
  1986. }
  1987. for ( int i = 0; i < NUM_QUICK_SLOTS; ++i ) {
  1988. savefile->ReadInt( quickSlot[ i ] );
  1989. }
  1990. savefile->ReadInt( weapon_soulcube );
  1991. savefile->ReadInt( weapon_pda );
  1992. savefile->ReadInt( weapon_fists );
  1993. savefile->ReadInt( weapon_flashlight );
  1994. savefile->ReadInt( weapon_chainsaw );
  1995. savefile->ReadInt( weapon_bloodstone );
  1996. savefile->ReadInt( weapon_bloodstone_active1 );
  1997. savefile->ReadInt( weapon_bloodstone_active2 );
  1998. savefile->ReadInt( weapon_bloodstone_active3 );
  1999. savefile->ReadBool( harvest_lock );
  2000. savefile->ReadInt( hudPowerup );
  2001. savefile->ReadInt( lastHudPowerup );
  2002. savefile->ReadInt( hudPowerupDuration );
  2003. savefile->ReadInt( heartRate );
  2004. savefile->ReadFloat( set );
  2005. heartInfo.SetStartTime( set );
  2006. savefile->ReadFloat( set );
  2007. heartInfo.SetDuration( set );
  2008. savefile->ReadFloat( set );
  2009. heartInfo.SetStartValue( set );
  2010. savefile->ReadFloat( set );
  2011. heartInfo.SetEndValue( set );
  2012. savefile->ReadInt( lastHeartAdjust );
  2013. savefile->ReadInt( lastHeartBeat );
  2014. savefile->ReadInt( lastDmgTime );
  2015. savefile->ReadInt( deathClearContentsTime );
  2016. savefile->ReadBool( doingDeathSkin );
  2017. savefile->ReadInt( lastArmorPulse );
  2018. savefile->ReadFloat( stamina );
  2019. savefile->ReadFloat( healthPool );
  2020. savefile->ReadInt( nextHealthPulse );
  2021. savefile->ReadBool( healthPulse );
  2022. savefile->ReadInt( nextHealthTake );
  2023. savefile->ReadBool( healthTake );
  2024. savefile->ReadBool( hiddenWeapon );
  2025. soulCubeProjectile.Restore( savefile );
  2026. savefile->ReadInt( spectator );
  2027. savefile->ReadBool( forceScoreBoard );
  2028. savefile->ReadBool( forceRespawn );
  2029. savefile->ReadBool( spectating );
  2030. savefile->ReadInt( lastSpectateTeleport );
  2031. savefile->ReadBool( lastHitToggle );
  2032. savefile->ReadBool( wantSpectate );
  2033. savefile->ReadBool( weaponGone );
  2034. savefile->ReadBool( useInitialSpawns );
  2035. savefile->ReadInt( tourneyRank );
  2036. savefile->ReadInt( tourneyLine );
  2037. teleportEntity.Restore( savefile );
  2038. savefile->ReadInt( teleportKiller );
  2039. savefile->ReadInt( minRespawnTime );
  2040. savefile->ReadInt( maxRespawnTime );
  2041. savefile->ReadVec3( firstPersonViewOrigin );
  2042. savefile->ReadMat3( firstPersonViewAxis );
  2043. // don't bother saving dragEntity since it's a dev tool
  2044. dragEntity.Clear();
  2045. savefile->ReadJoint( hipJoint );
  2046. savefile->ReadJoint( chestJoint );
  2047. savefile->ReadJoint( headJoint );
  2048. savefile->ReadStaticObject( physicsObj );
  2049. RestorePhysics( &physicsObj );
  2050. savefile->ReadInt( num );
  2051. aasLocation.SetGranularity( 1 );
  2052. aasLocation.SetNum( num );
  2053. for( i = 0; i < num; i++ ) {
  2054. savefile->ReadInt( aasLocation[ i ].areaNum );
  2055. savefile->ReadVec3( aasLocation[ i ].pos );
  2056. }
  2057. savefile->ReadInt( bobFoot );
  2058. savefile->ReadFloat( bobFrac );
  2059. savefile->ReadFloat( bobfracsin );
  2060. savefile->ReadInt( bobCycle );
  2061. savefile->ReadFloat( xyspeed );
  2062. savefile->ReadInt( stepUpTime );
  2063. savefile->ReadFloat( stepUpDelta );
  2064. savefile->ReadFloat( idealLegsYaw );
  2065. savefile->ReadFloat( legsYaw );
  2066. savefile->ReadBool( legsForward );
  2067. savefile->ReadFloat( oldViewYaw );
  2068. savefile->ReadAngles( viewBobAngles );
  2069. savefile->ReadVec3( viewBob );
  2070. savefile->ReadInt( landChange );
  2071. savefile->ReadInt( landTime );
  2072. savefile->ReadInt( currentWeapon );
  2073. int savedIdealWeapon = -1;
  2074. savefile->ReadInt( savedIdealWeapon );
  2075. idealWeapon.Set( savedIdealWeapon );
  2076. savefile->ReadInt( previousWeapon );
  2077. savefile->ReadInt( weaponSwitchTime );
  2078. savefile->ReadBool( weaponEnabled );
  2079. savefile->ReadInt( skinIndex );
  2080. savefile->ReadSkin( skin );
  2081. savefile->ReadSkin( powerUpSkin );
  2082. savefile->ReadInt( numProjectilesFired );
  2083. savefile->ReadInt( numProjectileHits );
  2084. savefile->ReadBool( airless );
  2085. savefile->ReadInt( airMsec );
  2086. savefile->ReadInt( lastAirDamage );
  2087. savefile->ReadBool( gibDeath );
  2088. savefile->ReadBool( gibsLaunched );
  2089. savefile->ReadVec3( gibsDir );
  2090. savefile->ReadFloat( set );
  2091. zoomFov.SetStartTime( set );
  2092. savefile->ReadFloat( set );
  2093. zoomFov.SetDuration( set );
  2094. savefile->ReadFloat( set );
  2095. zoomFov.SetStartValue( set );
  2096. savefile->ReadFloat( set );
  2097. zoomFov.SetEndValue( set );
  2098. savefile->ReadFloat( set );
  2099. centerView.SetStartTime( set );
  2100. savefile->ReadFloat( set );
  2101. centerView.SetDuration( set );
  2102. savefile->ReadFloat( set );
  2103. centerView.SetStartValue( set );
  2104. savefile->ReadFloat( set );
  2105. centerView.SetEndValue( set );
  2106. savefile->ReadBool( fxFov );
  2107. savefile->ReadFloat( influenceFov );
  2108. savefile->ReadInt( influenceActive );
  2109. savefile->ReadFloat( influenceRadius );
  2110. savefile->ReadObject( reinterpret_cast<idClass *&>( influenceEntity ) );
  2111. savefile->ReadMaterial( influenceMaterial );
  2112. savefile->ReadSkin( influenceSkin );
  2113. savefile->ReadObject( reinterpret_cast<idClass *&>( privateCameraView ) );
  2114. for( i = 0; i < NUM_LOGGED_VIEW_ANGLES; i++ ) {
  2115. savefile->ReadAngles( loggedViewAngles[ i ] );
  2116. }
  2117. for( i = 0; i < NUM_LOGGED_ACCELS; i++ ) {
  2118. savefile->ReadInt( loggedAccel[ i ].time );
  2119. savefile->ReadVec3( loggedAccel[ i ].dir );
  2120. }
  2121. savefile->ReadInt( currentLoggedAccel );
  2122. savefile->ReadObject( reinterpret_cast<idClass *&>( focusGUIent ) );
  2123. // can't save focusUI
  2124. focusUI = NULL;
  2125. savefile->ReadObject( reinterpret_cast<idClass *&>( focusCharacter ) );
  2126. savefile->ReadInt( talkCursor );
  2127. savefile->ReadInt( focusTime );
  2128. savefile->ReadObject( reinterpret_cast<idClass *&>( focusVehicle ) );
  2129. savefile->ReadUserInterface( cursor );
  2130. savefile->ReadInt( oldMouseX );
  2131. savefile->ReadInt( oldMouseY );
  2132. savefile->ReadBool( tipUp );
  2133. savefile->ReadBool( objectiveUp );
  2134. savefile->ReadInt( lastDamageDef );
  2135. savefile->ReadVec3( lastDamageDir );
  2136. savefile->ReadInt( lastDamageLocation );
  2137. savefile->ReadInt( smoothedFrame );
  2138. savefile->ReadBool( smoothedOriginUpdated );
  2139. savefile->ReadVec3( smoothedOrigin );
  2140. savefile->ReadAngles( smoothedAngles );
  2141. savefile->ReadBool( respawning );
  2142. savefile->ReadBool( leader );
  2143. savefile->ReadInt( lastSpectateChange );
  2144. savefile->ReadInt( lastTeleFX );
  2145. // set the pm_ cvars
  2146. const idKeyValue *kv;
  2147. kv = spawnArgs.MatchPrefix( "pm_", NULL );
  2148. while( kv ) {
  2149. cvarSystem->SetCVarString( kv->GetKey(), kv->GetValue() );
  2150. kv = spawnArgs.MatchPrefix( "pm_", kv );
  2151. }
  2152. savefile->ReadFloat( set );
  2153. pm_stamina.SetFloat( set );
  2154. // create combat collision hull for exact collision detection
  2155. SetCombatModel();
  2156. int weaponToggleCount;
  2157. savefile->ReadInt(weaponToggleCount);
  2158. for(i = 0; i < weaponToggleCount; i++) {
  2159. WeaponToggle_t newToggle;
  2160. memset(&newToggle, 0, sizeof(newToggle));
  2161. idStr name;
  2162. savefile->ReadString(name);
  2163. strcpy(newToggle.name, name.c_str());
  2164. int indexCount;
  2165. savefile->ReadInt(indexCount);
  2166. for(int j = 0; j < indexCount; j++) {
  2167. int temp;
  2168. savefile->ReadInt(temp);
  2169. newToggle.toggleList.Append(temp);
  2170. }
  2171. newToggle.lastUsed = 0;
  2172. weaponToggles.Set(newToggle.name, newToggle);
  2173. }
  2174. savefile->ReadObject(reinterpret_cast<idClass *&>(mountedObject));
  2175. enviroSuitLight.Restore( savefile );
  2176. savefile->ReadBool( healthRecharge );
  2177. savefile->ReadInt( lastHealthRechargeTime );
  2178. savefile->ReadInt( rechargeSpeed );
  2179. savefile->ReadFloat( new_g_damageScale );
  2180. savefile->ReadBool( bloomEnabled );
  2181. savefile->ReadFloat( bloomSpeed );
  2182. savefile->ReadFloat( bloomIntensity );
  2183. // flashlight
  2184. idWeapon *tempWeapon;
  2185. savefile->ReadObject( reinterpret_cast<idClass *&>( tempWeapon ) );
  2186. tempWeapon->SetIsPlayerFlashlight( true );
  2187. flashlight = tempWeapon;
  2188. savefile->ReadInt( flashlightBattery );
  2189. achievementManager.Restore( savefile );
  2190. savefile->ReadInt( playedTimeSecs );
  2191. savefile->ReadInt( playedTimeResidual );
  2192. aimAssist.Init( this );
  2193. laserSightHandle = -1;
  2194. // re-init the laser model
  2195. memset( &laserSightRenderEntity, 0, sizeof( laserSightRenderEntity ) );
  2196. laserSightRenderEntity.hModel = renderModelManager->FindModel( "_BEAM" );
  2197. laserSightRenderEntity.customShader = declManager->FindMaterial( "stereoRenderLaserSight" );
  2198. for ( int i=0; i<MAX_PLAYER_PDA; i++ ) {
  2199. savefile->ReadBool( pdaHasBeenRead[i] );
  2200. }
  2201. for ( int i=0; i<MAX_PLAYER_VIDEO; i++ ) {
  2202. savefile->ReadBool( videoHasBeenViewed[i] );
  2203. }
  2204. for ( int i=0; i<MAX_PLAYER_AUDIO; i++ ) {
  2205. for ( int j=0; j<MAX_PLAYER_AUDIO_ENTRIES; j++ ) {
  2206. savefile->ReadBool( audioHasBeenHeard[i][j] );
  2207. }
  2208. }
  2209. // Update the soul cube HUD indicator
  2210. if ( hud ) {
  2211. if ( weapon_soulcube > 0 && ( inventory.weapons & ( 1 << weapon_soulcube ) ) ) {
  2212. int max_souls = inventory.MaxAmmoForAmmoClass( this, "ammo_souls" );
  2213. if ( inventory.GetInventoryAmmoForType( idWeapon::GetAmmoNumForName( "ammo_souls" ) ) >= max_souls ) {
  2214. hud->SetShowSoulCubeOnLoad( true );
  2215. }
  2216. }
  2217. }
  2218. }
  2219. /*
  2220. ===============
  2221. idPlayer::PrepareForRestart
  2222. ================
  2223. */
  2224. void idPlayer::PrepareForRestart() {
  2225. ClearPowerUps();
  2226. if( common->IsClient() == false ) {
  2227. ServerSpectate( true );
  2228. }
  2229. forceRespawn = true;
  2230. // Confirm reset hud states
  2231. DropFlag();
  2232. if ( hud ) {
  2233. hud->SetFlagState( 0, 0 );
  2234. hud->SetFlagState( 1, 0 );
  2235. }
  2236. // we will be restarting program, clear the client entities from program-related things first
  2237. ShutdownThreads();
  2238. // the sound world is going to be cleared, don't keep references to emitters
  2239. FreeSoundEmitter( false );
  2240. }
  2241. /*
  2242. ===============
  2243. idPlayer::Restart
  2244. ================
  2245. */
  2246. void idPlayer::Restart() {
  2247. idActor::Restart();
  2248. // client needs to setup the animation script object again
  2249. if ( common->IsClient() ) {
  2250. // Make sure the weapon spawnId gets re-linked on the next snapshot.
  2251. // Otherwise, its owner might not be set after the map restart, which causes asserts and crashes.
  2252. weapon = NULL;
  2253. flashlight = NULL;
  2254. enviroSuitLight = NULL;
  2255. Init();
  2256. } else {
  2257. // choose a random spot and prepare the point of view in case player is left spectating
  2258. assert( spectating );
  2259. SpawnFromSpawnSpot();
  2260. }
  2261. useInitialSpawns = true;
  2262. UpdateSkinSetup();
  2263. }
  2264. /*
  2265. ===============
  2266. idPlayer::ServerSpectate
  2267. ================
  2268. */
  2269. void idPlayer::ServerSpectate( bool spectate ) {
  2270. assert( !common->IsClient() );
  2271. if ( spectating != spectate ) {
  2272. Spectate( spectate );
  2273. if ( !spectate ) {
  2274. // When coming out of spectate, join the team with the least number of players
  2275. if ( gameLocal.mpGame.IsGametypeTeamBased() ) {
  2276. int teamCounts[2] = { 0, 0 };
  2277. gameLocal.mpGame.NumActualClients( false, teamCounts );
  2278. teamCounts[team]--;
  2279. if ( teamCounts[0] < teamCounts[1] ) {
  2280. team = 0;
  2281. } else if ( teamCounts[1] < teamCounts[0] ) {
  2282. team = 1;
  2283. }
  2284. gameLocal.mpGame.SwitchToTeam( entityNumber, -1, team );
  2285. }
  2286. if ( gameLocal.gameType == GAME_DM ) {
  2287. // make sure the scores are reset so you can't exploit by spectating and entering the game back
  2288. // other game types don't matter, as you either can't join back, or it's team scores
  2289. gameLocal.mpGame.ClearFrags( entityNumber );
  2290. }
  2291. }
  2292. }
  2293. if ( !spectate ) {
  2294. SpawnFromSpawnSpot();
  2295. }
  2296. // drop the flag if player was carrying it
  2297. if ( spectate && common->IsMultiplayer() && gameLocal.mpGame.IsGametypeFlagBased() &&
  2298. carryingFlag )
  2299. {
  2300. DropFlag();
  2301. }
  2302. }
  2303. /*
  2304. ===========
  2305. idPlayer::SelectInitialSpawnPoint
  2306. Try to find a spawn point marked 'initial', otherwise
  2307. use normal spawn selection.
  2308. ============
  2309. */
  2310. void idPlayer::SelectInitialSpawnPoint( idVec3 &origin, idAngles &angles ) {
  2311. idEntity *spot;
  2312. idStr skin;
  2313. spot = gameLocal.SelectInitialSpawnPoint( this );
  2314. // set the player skin from the spawn location
  2315. if ( spot->spawnArgs.GetString( "skin", NULL, skin ) ) {
  2316. spawnArgs.Set( "spawn_skin", skin );
  2317. }
  2318. // activate the spawn locations targets
  2319. spot->PostEventMS( &EV_ActivateTargets, 0, this );
  2320. origin = spot->GetPhysics()->GetOrigin();
  2321. origin[2] += 4.0f + CM_BOX_EPSILON; // move up to make sure the player is at least an epsilon above the floor
  2322. angles = spot->GetPhysics()->GetAxis().ToAngles();
  2323. }
  2324. /*
  2325. ===========
  2326. idPlayer::SpawnFromSpawnSpot
  2327. Chooses a spawn location and spawns the player
  2328. ============
  2329. */
  2330. void idPlayer::SpawnFromSpawnSpot() {
  2331. idVec3 spawn_origin;
  2332. idAngles spawn_angles;
  2333. SelectInitialSpawnPoint( spawn_origin, spawn_angles );
  2334. SpawnToPoint( spawn_origin, spawn_angles );
  2335. }
  2336. /*
  2337. ===========
  2338. idPlayer::SpawnToPoint
  2339. Called every time a client is placed fresh in the world:
  2340. after the first ClientBegin, and after each respawn
  2341. Initializes all non-persistant parts of playerState
  2342. when called here with spectating set to true, just place yourself and init
  2343. ============
  2344. */
  2345. void idPlayer::SpawnToPoint( const idVec3 &spawn_origin, const idAngles &spawn_angles ) {
  2346. idVec3 spec_origin;
  2347. assert( !common->IsClient() );
  2348. respawning = true;
  2349. Init();
  2350. fl.noknockback = false;
  2351. // stop any ragdolls being used
  2352. StopRagdoll();
  2353. // set back the player physics
  2354. SetPhysics( &physicsObj );
  2355. physicsObj.SetClipModelAxis();
  2356. physicsObj.EnableClip();
  2357. if ( !spectating ) {
  2358. SetCombatContents( true );
  2359. }
  2360. physicsObj.SetLinearVelocity( vec3_origin );
  2361. // setup our initial view
  2362. if ( !spectating ) {
  2363. SetOrigin( spawn_origin );
  2364. } else {
  2365. spec_origin = spawn_origin;
  2366. spec_origin[ 2 ] += pm_normalheight.GetFloat();
  2367. spec_origin[ 2 ] += SPECTATE_RAISE;
  2368. SetOrigin( spec_origin );
  2369. }
  2370. // if this is the first spawn of the map, we don't have a usercmd yet,
  2371. // so the delta angles won't be correct. This will be fixed on the first think.
  2372. viewAngles = ang_zero;
  2373. SetDeltaViewAngles( ang_zero );
  2374. SetViewAngles( spawn_angles );
  2375. spawnAngles = spawn_angles;
  2376. spawnAnglesSet = false;
  2377. legsForward = true;
  2378. legsYaw = 0.0f;
  2379. idealLegsYaw = 0.0f;
  2380. oldViewYaw = viewAngles.yaw;
  2381. if ( spectating ) {
  2382. Hide();
  2383. } else {
  2384. Show();
  2385. }
  2386. if ( common->IsMultiplayer() ) {
  2387. if ( !spectating ) {
  2388. // we may be called twice in a row in some situations. avoid a double fx and 'fly to the roof'
  2389. if ( lastTeleFX < gameLocal.time - 1000 ) {
  2390. idEntityFx::StartFx( spawnArgs.GetString( "fx_spawn" ), &spawn_origin, NULL, this, true );
  2391. lastTeleFX = gameLocal.time;
  2392. }
  2393. }
  2394. AI_TELEPORT = true;
  2395. } else {
  2396. AI_TELEPORT = false;
  2397. }
  2398. // kill anything at the new position
  2399. if ( !spectating ) {
  2400. physicsObj.SetClipMask( MASK_PLAYERSOLID ); // the clip mask is usually maintained in Move(), but KillBox requires it
  2401. gameLocal.KillBox( this );
  2402. }
  2403. // don't allow full run speed for a bit
  2404. physicsObj.SetKnockBack( 100 );
  2405. // set our respawn time and buttons so that if we're killed we don't respawn immediately
  2406. minRespawnTime = gameLocal.time;
  2407. maxRespawnTime = gameLocal.time;
  2408. if ( !spectating ) {
  2409. forceRespawn = false;
  2410. }
  2411. Respawn_Shared();
  2412. privateCameraView = NULL;
  2413. BecomeActive( TH_THINK );
  2414. // run a client frame to drop exactly to the floor,
  2415. // initialize animations and other things
  2416. Think();
  2417. respawning = false;
  2418. lastManOver = false;
  2419. lastManPlayAgain = false;
  2420. isTelefragged = false;
  2421. }
  2422. /*
  2423. ===============
  2424. idPlayer::Respawn_Shared
  2425. Called on server and client players when they respawn (including on initial spawn)
  2426. ===============
  2427. */
  2428. void idPlayer::Respawn_Shared() {
  2429. respawn_netEvent.Set();
  2430. serverOverridePositionTime = gameLocal.GetServerGameTimeMs();
  2431. // Remove the hud respawn message.
  2432. HideRespawnHudMessage();
  2433. FlashlightOff();
  2434. }
  2435. /*
  2436. ===============
  2437. idPlayer::SavePersistantInfo
  2438. Saves any inventory and player stats when changing levels.
  2439. ===============
  2440. */
  2441. void idPlayer::SavePersistantInfo() {
  2442. idDict &playerInfo = gameLocal.persistentPlayerInfo[entityNumber];
  2443. playerInfo.Clear();
  2444. inventory.GetPersistantData( playerInfo );
  2445. playerInfo.SetInt( "health", health );
  2446. playerInfo.SetInt( "current_weapon", currentWeapon );
  2447. playerInfo.SetInt( "playedTime", playedTimeSecs );
  2448. achievementManager.SavePersistentData( playerInfo );
  2449. }
  2450. /*
  2451. ===============
  2452. idPlayer::RestorePersistantInfo
  2453. Restores any inventory and player stats when changing levels.
  2454. ===============
  2455. */
  2456. void idPlayer::RestorePersistantInfo() {
  2457. if ( common->IsMultiplayer() || g_demoMode.GetBool() ) {
  2458. gameLocal.persistentPlayerInfo[entityNumber].Clear();
  2459. }
  2460. spawnArgs.Copy( gameLocal.persistentPlayerInfo[entityNumber] );
  2461. inventory.RestoreInventory( this, spawnArgs );
  2462. health = spawnArgs.GetInt( "health", "100" );
  2463. idealWeapon = spawnArgs.GetInt( "current_weapon", "1" );
  2464. playedTimeSecs = spawnArgs.GetInt( "playedTime" );
  2465. achievementManager.RestorePersistentData( spawnArgs );
  2466. }
  2467. /*
  2468. ==============
  2469. idPlayer::UpdateSkinSetup
  2470. ==============
  2471. */
  2472. void idPlayer::UpdateSkinSetup() {
  2473. if ( !common->IsMultiplayer() ) {
  2474. return;
  2475. }
  2476. if ( gameLocal.mpGame.IsGametypeTeamBased() ) { /* CTF */
  2477. skinIndex = team + 1;
  2478. } else {
  2479. // Each player will now have their Skin Index Reflect their entity number ( host = 0, client 1 = 1, client 2 = 2 etc )
  2480. skinIndex = entityNumber; // session->GetActingGameStateLobbyBase().GetLobbyUserSkinIndex( gameLocal.lobbyUserIDs[entityNumber] );
  2481. }
  2482. const char * baseSkinName = gameLocal.mpGame.GetSkinName( skinIndex );
  2483. skin = declManager->FindSkin( baseSkinName, false );
  2484. if ( PowerUpActive( BERSERK ) ) {
  2485. idStr powerSkinName = baseSkinName;
  2486. powerSkinName.Append( "_berserk" );
  2487. powerUpSkin = declManager->FindSkin( powerSkinName );
  2488. }
  2489. else if ( PowerUpActive( INVULNERABILITY ) ) {
  2490. idStr powerSkinName = baseSkinName;
  2491. powerSkinName.Append( "_invuln" );
  2492. powerUpSkin = declManager->FindSkin( powerSkinName );
  2493. }
  2494. else if ( PowerUpActive( INVISIBILITY ) ) {
  2495. const char * invisibleSkin = "";
  2496. spawnArgs.GetString( "skin_invisibility", "", &invisibleSkin );
  2497. powerUpSkin = declManager->FindSkin( invisibleSkin );
  2498. }
  2499. }
  2500. /*
  2501. ===============
  2502. idPlayer::UpdateHudStats
  2503. ===============
  2504. */
  2505. void idPlayer::UpdateHudStats( idMenuHandler_HUD * _hudManager ) {
  2506. if ( _hudManager && _hudManager->GetHud() ) {
  2507. idMenuScreen_HUD * hud = _hudManager->GetHud();
  2508. hud->UpdateHealthArmor( this );
  2509. hud->UpdateStamina( this );
  2510. hud->UpdateWeaponInfo( this );
  2511. if ( inventory.weaponPulse ) {
  2512. UpdateHudWeapon();
  2513. inventory.weaponPulse = false;
  2514. }
  2515. if ( gameLocal.mpGame.IsGametypeFlagBased() )
  2516. {
  2517. hud->SetFlagState( 0, gameLocal.mpGame.GetFlagStatus( 0 ) );
  2518. hud->SetFlagState( 1, gameLocal.mpGame.GetFlagStatus( 1 ) );
  2519. hud->SetTeamScore( 0, gameLocal.mpGame.GetFlagPoints( 0 ) );
  2520. hud->SetTeamScore( 1, gameLocal.mpGame.GetFlagPoints( 1 ) );
  2521. hud->SetTeam( team );
  2522. }
  2523. }
  2524. }
  2525. /*
  2526. ===============
  2527. idPlayer::UpdateHudWeapon
  2528. ===============
  2529. */
  2530. void idPlayer::UpdateHudWeapon( bool flashWeapon ) {
  2531. idMenuScreen_HUD * curDisplay = hud;
  2532. idPlayer *p = this;
  2533. if ( gameLocal.GetLocalClientNum() >= 0 && gameLocal.entities[ gameLocal.GetLocalClientNum() ] && gameLocal.entities[ gameLocal.GetLocalClientNum() ]->IsType( idPlayer::Type ) ) {
  2534. p = static_cast< idPlayer * >( gameLocal.entities[ gameLocal.GetLocalClientNum() ] );
  2535. if ( p->spectating && p->spectator == entityNumber ) {
  2536. assert( p->hud );
  2537. curDisplay = p->hud;
  2538. }
  2539. }
  2540. if ( !curDisplay ) {
  2541. return;
  2542. }
  2543. curDisplay->UpdateWeaponStates( p, flashWeapon );
  2544. }
  2545. /*
  2546. ===============
  2547. idPlayer::UpdateHudWeapon
  2548. ===============
  2549. */
  2550. void idPlayer::UpdateChattingHud() {
  2551. idMenuScreen_HUD * curDisplay = hud;
  2552. idPlayer *p = this;
  2553. if ( gameLocal.GetLocalClientNum() >= 0 && gameLocal.entities[ gameLocal.GetLocalClientNum() ] && gameLocal.entities[ gameLocal.GetLocalClientNum() ]->IsType( idPlayer::Type ) ) {
  2554. p = static_cast< idPlayer * >( gameLocal.entities[ gameLocal.GetLocalClientNum() ] );
  2555. if ( p->spectating && p->spectator == entityNumber ) {
  2556. assert( p->hud );
  2557. curDisplay = p->hud;
  2558. }
  2559. }
  2560. if ( !curDisplay ) {
  2561. return;
  2562. }
  2563. curDisplay->UpdateChattingHud( p );
  2564. }
  2565. /*
  2566. ========================
  2567. idMenuScreen_Scoreboard::UpdateSpectating
  2568. ========================
  2569. */
  2570. void idPlayer::UpdateSpectatingText() {
  2571. idSWF * spectatorMessages = mpMessages;
  2572. idPlayer *p = this;
  2573. if ( gameLocal.GetLocalClientNum() >= 0 && gameLocal.entities[ gameLocal.GetLocalClientNum() ] && gameLocal.entities[ gameLocal.GetLocalClientNum() ]->IsType( idPlayer::Type ) ) {
  2574. p = static_cast< idPlayer * >( gameLocal.entities[ gameLocal.GetLocalClientNum() ] );
  2575. if ( p && p->spectating ) {
  2576. spectatorMessages = p->mpMessages;
  2577. }
  2578. }
  2579. if ( !spectatorMessages || !spectatorMessages->IsActive() ) {
  2580. return;
  2581. }
  2582. idPlayer * viewPlayer = static_cast<idPlayer *>( gameLocal.entities[ p->spectator ] );
  2583. if ( viewPlayer == NULL ) {
  2584. return;
  2585. }
  2586. idStr spectatetext[ 2 ];
  2587. if ( !gameLocal.mpGame.IsScoreboardActive() ) {
  2588. gameLocal.mpGame.GetSpectateText( p, spectatetext, false );
  2589. }
  2590. idSWFScriptObject & root = spectatorMessages->GetRootObject();
  2591. idSWFTextInstance * txtVal = root.GetNestedText( "txtSpectating" );
  2592. if ( txtVal != NULL ) {
  2593. txtVal->tooltip = true;
  2594. txtVal->SetText( spectatetext[0] );
  2595. txtVal->SetStrokeInfo( true, 0.75f, 1.75f );
  2596. }
  2597. txtVal = root.GetNestedText( "txtFollow" );
  2598. if ( txtVal != NULL ) {
  2599. txtVal->SetText( spectatetext[1] );
  2600. txtVal->SetStrokeInfo( true, 0.75f, 1.75f );
  2601. }
  2602. }
  2603. /*
  2604. ===============
  2605. idPlayer::UpdateMpMessages
  2606. ===============
  2607. */
  2608. void idPlayer::AddChatMessage( int index, int alpha, const idStr & message ) {
  2609. if ( mpMessages == NULL || !mpMessages->IsActive() ) {
  2610. return;
  2611. }
  2612. idSWFScriptObject * mpChat = mpMessages->GetRootObject().GetNestedObj( "_left", "mpChat" );
  2613. idSWFSpriteInstance * info = mpChat->GetNestedSprite( va( "info%i", index ) );
  2614. idSWFTextInstance * txtVal = mpChat->GetNestedText( va( "info%i", index ), "txtVal" );
  2615. if ( info ) {
  2616. info->SetVisible( true );
  2617. if ( alpha >= 4 ) {
  2618. info->SetAlpha( 1.0f );
  2619. } else if ( alpha == 3 ) {
  2620. info->SetAlpha( 0.875f );
  2621. } else if ( alpha == 2 ) {
  2622. info->SetAlpha( 0.75f );
  2623. } else if ( alpha == 1 ) {
  2624. info->SetAlpha( 0.625f );
  2625. } else {
  2626. info->SetAlpha( 0.5f );
  2627. }
  2628. }
  2629. if ( txtVal ) {
  2630. txtVal->SetIgnoreColor( false );
  2631. txtVal->SetText( message );
  2632. txtVal->SetStrokeInfo( true, 0.9f, 1.75f );
  2633. }
  2634. }
  2635. /*
  2636. ===============
  2637. idPlayer::UpdateMpMessages
  2638. ===============
  2639. */
  2640. void idPlayer::ClearChatMessage( int index ) {
  2641. if ( mpMessages == NULL || !mpMessages->IsActive() ) {
  2642. return;
  2643. }
  2644. idSWFScriptObject * mpChat = mpMessages->GetRootObject().GetNestedObj( "_left", "mpChat" );
  2645. idSWFSpriteInstance * info = mpChat->GetNestedSprite( va( "info%i", index ) );
  2646. idSWFTextInstance * txtVal = mpChat->GetNestedText( va( "info%i", index ), "txtVal" );
  2647. if ( info ) {
  2648. info->SetVisible( false );
  2649. }
  2650. if ( txtVal ) {
  2651. txtVal->SetText( "" );
  2652. }
  2653. }
  2654. /*
  2655. ===============
  2656. idPlayer::DrawHUD
  2657. ===============
  2658. */
  2659. void idPlayer::DrawHUD( idMenuHandler_HUD * _hudManager ) {
  2660. SCOPED_PROFILE_EVENT( "idPlayer::DrawHUD" );
  2661. if ( !weapon.GetEntity() || influenceActive != INFLUENCE_NONE || privateCameraView || gameLocal.GetCamera() || !g_showHud.GetBool() ) {
  2662. return;
  2663. }
  2664. if ( common->IsMultiplayer() ) {
  2665. UpdateChattingHud();
  2666. UpdateSpectatingText();
  2667. }
  2668. // Always draw the local client's messages so that chat works correctly while spectating another player.
  2669. idPlayer * localPlayer = static_cast< idPlayer * >( gameLocal.entities[ gameLocal.GetLocalClientNum() ] );
  2670. if ( localPlayer != NULL && localPlayer->mpMessages != NULL ) {
  2671. localPlayer->mpMessages->Render( renderSystem, Sys_Milliseconds() );
  2672. }
  2673. UpdateHudStats( _hudManager );
  2674. if ( spectating ) {
  2675. return;
  2676. }
  2677. if ( _hudManager ) {
  2678. _hudManager->Update();
  2679. }
  2680. weapon.GetEntity()->UpdateGUI();
  2681. // weapon targeting crosshair
  2682. if ( !GuiActive() ) {
  2683. // don't show the 2D crosshair in stereo rendering, use the
  2684. // laser sight model instead
  2685. if ( _hudManager && _hudManager->GetHud() ) {
  2686. idMenuScreen_HUD * hud = _hudManager->GetHud();
  2687. if ( weapon.GetEntity()->ShowCrosshair() && !IsGameStereoRendered() ) {
  2688. if ( weapon.GetEntity()->GetGrabberState() == 1 || weapon.GetEntity()->GetGrabberState() == 2 ) {
  2689. hud->SetCursorState( this, CURSOR_GRABBER, 1 );
  2690. hud->SetCursorState( this, CURSOR_IN_COMBAT, 0 );
  2691. } else {
  2692. hud->SetCursorState( this, CURSOR_GRABBER, 0 );
  2693. hud->SetCursorState( this, CURSOR_IN_COMBAT, 1 );
  2694. }
  2695. } else {
  2696. hud->SetCursorState( this, CURSOR_NONE, 1 );
  2697. }
  2698. hud->UpdateCursorState();
  2699. }
  2700. } else if ( _hudManager && _hudManager->GetHud() ) {
  2701. idMenuScreen_HUD * hud = _hudManager->GetHud();
  2702. hud->SetCursorState( this, CURSOR_NONE, 1 );
  2703. hud->UpdateCursorState();
  2704. }
  2705. }
  2706. /*
  2707. ===============
  2708. idPlayer::EnterCinematic
  2709. ===============
  2710. */
  2711. void idPlayer::EnterCinematic() {
  2712. if ( PowerUpActive( HELLTIME ) ) {
  2713. StopHelltime();
  2714. }
  2715. Hide();
  2716. StopSound( SND_CHANNEL_PDA_AUDIO, false );
  2717. StopSound( SND_CHANNEL_PDA_VIDEO, false );
  2718. if ( hudManager ) {
  2719. hudManager->SetRadioMessage( false );
  2720. }
  2721. physicsObj.SetLinearVelocity( vec3_origin );
  2722. SetState( "EnterCinematic" );
  2723. UpdateScript();
  2724. if ( weaponEnabled && weapon.GetEntity() ) {
  2725. weapon.GetEntity()->EnterCinematic();
  2726. }
  2727. if ( flashlight.GetEntity() ) {
  2728. flashlight.GetEntity()->EnterCinematic();
  2729. }
  2730. AI_FORWARD = false;
  2731. AI_BACKWARD = false;
  2732. AI_STRAFE_LEFT = false;
  2733. AI_STRAFE_RIGHT = false;
  2734. AI_RUN = false;
  2735. AI_ATTACK_HELD = false;
  2736. AI_WEAPON_FIRED = false;
  2737. AI_JUMP = false;
  2738. AI_CROUCH = false;
  2739. AI_ONGROUND = true;
  2740. AI_ONLADDER = false;
  2741. AI_DEAD = ( health <= 0 );
  2742. AI_RUN = false;
  2743. AI_PAIN = false;
  2744. AI_HARDLANDING = false;
  2745. AI_SOFTLANDING = false;
  2746. AI_RELOAD = false;
  2747. AI_TELEPORT = false;
  2748. AI_TURN_LEFT = false;
  2749. AI_TURN_RIGHT = false;
  2750. }
  2751. /*
  2752. ===============
  2753. idPlayer::ExitCinematic
  2754. ===============
  2755. */
  2756. void idPlayer::ExitCinematic() {
  2757. Show();
  2758. if ( weaponEnabled && weapon.GetEntity() ) {
  2759. weapon.GetEntity()->ExitCinematic();
  2760. }
  2761. if ( flashlight.GetEntity() ) {
  2762. flashlight.GetEntity()->ExitCinematic();
  2763. }
  2764. // long cinematics would have surpassed the healthTakeTime, causing the player to take damage
  2765. // immediately after the cinematic ends. Instead we start the healthTake cooldown again once
  2766. // the cinematic ends.
  2767. if ( g_skill.GetInteger() == 3 ) {
  2768. nextHealthTake = gameLocal.time + g_healthTakeTime.GetInteger() * 1000;
  2769. }
  2770. SetState( "ExitCinematic" );
  2771. UpdateScript();
  2772. }
  2773. /*
  2774. =====================
  2775. idPlayer::UpdateConditions
  2776. =====================
  2777. */
  2778. void idPlayer::UpdateConditions() {
  2779. idVec3 velocity;
  2780. float fallspeed;
  2781. float forwardspeed;
  2782. float sidespeed;
  2783. // minus the push velocity to avoid playing the walking animation and sounds when riding a mover
  2784. velocity = physicsObj.GetLinearVelocity() - physicsObj.GetPushedLinearVelocity();
  2785. fallspeed = velocity * physicsObj.GetGravityNormal();
  2786. if ( influenceActive ) {
  2787. AI_FORWARD = false;
  2788. AI_BACKWARD = false;
  2789. AI_STRAFE_LEFT = false;
  2790. AI_STRAFE_RIGHT = false;
  2791. } else if ( gameLocal.time - lastDmgTime < 500 ) {
  2792. forwardspeed = velocity * viewAxis[ 0 ];
  2793. sidespeed = velocity * viewAxis[ 1 ];
  2794. AI_FORWARD = AI_ONGROUND && ( forwardspeed > 20.01f );
  2795. AI_BACKWARD = AI_ONGROUND && ( forwardspeed < -20.01f );
  2796. AI_STRAFE_LEFT = AI_ONGROUND && ( sidespeed > 20.01f );
  2797. AI_STRAFE_RIGHT = AI_ONGROUND && ( sidespeed < -20.01f );
  2798. } else if ( xyspeed > MIN_BOB_SPEED ) {
  2799. AI_FORWARD = AI_ONGROUND && ( usercmd.forwardmove > 0 );
  2800. AI_BACKWARD = AI_ONGROUND && ( usercmd.forwardmove < 0 );
  2801. AI_STRAFE_LEFT = AI_ONGROUND && ( usercmd.rightmove < 0 );
  2802. AI_STRAFE_RIGHT = AI_ONGROUND && ( usercmd.rightmove > 0 );
  2803. } else {
  2804. AI_FORWARD = false;
  2805. AI_BACKWARD = false;
  2806. AI_STRAFE_LEFT = false;
  2807. AI_STRAFE_RIGHT = false;
  2808. }
  2809. AI_RUN = ( usercmd.buttons & BUTTON_RUN ) && ( ( !pm_stamina.GetFloat() ) || ( stamina > pm_staminathreshold.GetFloat() ) );
  2810. AI_DEAD = ( health <= 0 );
  2811. }
  2812. /*
  2813. ==================
  2814. WeaponFireFeedback
  2815. Called when a weapon fires, generates head twitches, etc
  2816. ==================
  2817. */
  2818. void idPlayer::WeaponFireFeedback( const idDict *weaponDef ) {
  2819. // force a blink
  2820. blink_time = 0;
  2821. // play the fire animation
  2822. AI_WEAPON_FIRED = true;
  2823. // update view feedback
  2824. playerView.WeaponFireFeedback( weaponDef );
  2825. // shake controller
  2826. float highMagnitude = weaponDef->GetFloat( "controllerShakeHighMag" );
  2827. int highDuration = weaponDef->GetInt( "controllerShakeHighTime" );
  2828. float lowMagnitude = weaponDef->GetFloat( "controllerShakeLowMag" );
  2829. int lowDuration = weaponDef->GetInt( "controllerShakeLowTime" );
  2830. //const char *name = weaponDef->GetString( "inv_name" );
  2831. if( IsLocallyControlled() ) {
  2832. SetControllerShake( highMagnitude, highDuration, lowMagnitude, lowDuration );
  2833. }
  2834. }
  2835. /*
  2836. ===============
  2837. idPlayer::StopFiring
  2838. ===============
  2839. */
  2840. void idPlayer::StopFiring() {
  2841. AI_ATTACK_HELD = false;
  2842. AI_WEAPON_FIRED = false;
  2843. AI_RELOAD = false;
  2844. if ( weapon.GetEntity() ) {
  2845. weapon.GetEntity()->EndAttack();
  2846. }
  2847. }
  2848. /*
  2849. ===============
  2850. idPlayer::FireWeapon
  2851. ===============
  2852. */
  2853. idCVar g_infiniteAmmo( "g_infiniteAmmo", "0", CVAR_GAME | CVAR_BOOL, "infinite ammo" );
  2854. extern idCVar ui_autoSwitch;
  2855. void idPlayer::FireWeapon() {
  2856. idMat3 axis;
  2857. idVec3 muzzle;
  2858. if ( privateCameraView ) {
  2859. return;
  2860. }
  2861. if ( g_editEntityMode.GetInteger() ) {
  2862. GetViewPos( muzzle, axis );
  2863. if ( gameLocal.editEntities->SelectEntity( muzzle, axis[0], this ) ) {
  2864. return;
  2865. }
  2866. }
  2867. if ( !hiddenWeapon && weapon.GetEntity()->IsReady() ) {
  2868. if ( g_infiniteAmmo.GetBool() || weapon.GetEntity()->AmmoInClip() || weapon.GetEntity()->AmmoAvailable() ) {
  2869. AI_ATTACK_HELD = true;
  2870. weapon.GetEntity()->BeginAttack();
  2871. if ( ( weapon_soulcube >= 0 ) && ( currentWeapon == weapon_soulcube ) ) {
  2872. if ( hud ) {
  2873. hud->UpdateSoulCube( false );
  2874. }
  2875. SelectWeapon( previousWeapon, false );
  2876. }
  2877. if( (weapon_bloodstone >= 0) && (currentWeapon == weapon_bloodstone) && inventory.weapons & ( 1 << weapon_bloodstone_active1 ) && weapon.GetEntity()->GetStatus() == WP_READY) {
  2878. // tell it to switch to the previous weapon. Only do this once to prevent
  2879. // weapon toggling messing up the previous weapon
  2880. if(idealWeapon == weapon_bloodstone) {
  2881. if(previousWeapon == weapon_bloodstone || previousWeapon == -1) {
  2882. NextBestWeapon();
  2883. } else {
  2884. //Since this is a toggle weapon just select itself and it will toggle to the last weapon
  2885. SelectWeapon( weapon_bloodstone, false );
  2886. }
  2887. }
  2888. }
  2889. } else {
  2890. idLobbyBase & lobby = session->GetActingGameStateLobbyBase();
  2891. lobbyUserID_t & lobbyUserID = gameLocal.lobbyUserIDs[ entityNumber ];
  2892. bool autoSwitch = lobby.GetLobbyUserWeaponAutoSwitch( lobbyUserID );
  2893. if ( !autoSwitch ) {
  2894. return;
  2895. }
  2896. // update our ammo clip in our inventory
  2897. if ( ( currentWeapon >= 0 ) && ( currentWeapon < MAX_WEAPONS ) ) {
  2898. inventory.SetClipAmmoForWeapon( currentWeapon, weapon.GetEntity()->AmmoInClip() );
  2899. }
  2900. NextBestWeapon();
  2901. }
  2902. }
  2903. if ( tipUp ) {
  2904. HideTip();
  2905. }
  2906. if ( objectiveUp ) {
  2907. HideObjective();
  2908. }
  2909. }
  2910. /*
  2911. ===============
  2912. idPlayer::CacheWeapons
  2913. ===============
  2914. */
  2915. void idPlayer::CacheWeapons() {
  2916. idStr weap;
  2917. int w;
  2918. // check if we have any weapons
  2919. if ( !inventory.weapons ) {
  2920. return;
  2921. }
  2922. for( w = 0; w < MAX_WEAPONS; w++ ) {
  2923. if ( inventory.weapons & ( 1 << w ) ) {
  2924. weap = spawnArgs.GetString( va( "def_weapon%d", w ) );
  2925. if ( weap != "" ) {
  2926. idWeapon::CacheWeapon( weap );
  2927. } else {
  2928. inventory.weapons &= ~( 1 << w );
  2929. }
  2930. }
  2931. }
  2932. }
  2933. /*
  2934. ===============
  2935. idPlayer::SetQuickSlot
  2936. ===============
  2937. */
  2938. void idPlayer::SetQuickSlot( int index, int val ) {
  2939. if ( index >= NUM_QUICK_SLOTS || index < 0 ) {
  2940. return;
  2941. }
  2942. quickSlot[ index ] = val;
  2943. }
  2944. /*
  2945. ===============
  2946. idPlayer::GetQuickSlot
  2947. ===============
  2948. */
  2949. int idPlayer::GetQuickSlot( int index ) {
  2950. if ( index >= NUM_QUICK_SLOTS || index < 0 ) {
  2951. return -1;
  2952. }
  2953. return quickSlot[ index ];
  2954. }
  2955. /*
  2956. ===============
  2957. idPlayer::Give
  2958. ===============
  2959. */
  2960. bool idPlayer::Give( const char *statname, const char *value, unsigned int giveFlags ) {
  2961. int amount;
  2962. if ( AI_DEAD ) {
  2963. return false;
  2964. }
  2965. if ( !idStr::Icmp( statname, "health" ) ) {
  2966. if ( health >= inventory.maxHealth ) {
  2967. return false;
  2968. }
  2969. if ( giveFlags & ITEM_GIVE_UPDATE_STATE ) {
  2970. amount = atoi( value );
  2971. if ( amount ) {
  2972. health += amount;
  2973. if ( health > inventory.maxHealth ) {
  2974. health = inventory.maxHealth;
  2975. }
  2976. healthPulse = true;
  2977. }
  2978. }
  2979. } else if ( !idStr::Icmp( statname, "stamina" ) ) {
  2980. if ( stamina >= 100 ) {
  2981. return false;
  2982. }
  2983. if ( giveFlags & ITEM_GIVE_UPDATE_STATE ) {
  2984. stamina += atof( value );
  2985. if ( stamina > 100 ) {
  2986. stamina = 100;
  2987. }
  2988. }
  2989. } else if ( !idStr::Icmp( statname, "heartRate" ) ) {
  2990. if ( giveFlags & ITEM_GIVE_UPDATE_STATE ) {
  2991. heartRate += atoi( value );
  2992. if ( heartRate > MAX_HEARTRATE ) {
  2993. heartRate = MAX_HEARTRATE;
  2994. }
  2995. }
  2996. } else if ( !idStr::Icmp( statname, "air" ) ) {
  2997. if ( airMsec >= pm_airMsec.GetInteger() ) {
  2998. return false;
  2999. }
  3000. if ( giveFlags & ITEM_GIVE_UPDATE_STATE ) {
  3001. airMsec += pm_airMsec.GetInteger() * atoi( value ) / 100;
  3002. if ( airMsec > pm_airMsec.GetInteger() ) {
  3003. airMsec = pm_airMsec.GetInteger();
  3004. }
  3005. }
  3006. } else if ( !idStr::Icmp( statname, "enviroTime" ) ) {
  3007. if ( ( giveFlags & ITEM_GIVE_UPDATE_STATE ) && PowerUpActive( ENVIROTIME ) ) {
  3008. inventory.powerupEndTime[ ENVIROTIME ] += (atof(value) * 1000);
  3009. } else {
  3010. GivePowerUp( ENVIROTIME, atoi(value)*1000, giveFlags );
  3011. }
  3012. } else {
  3013. bool ret = inventory.Give( this, spawnArgs, statname, value, &idealWeapon, true, giveFlags );
  3014. return ret;
  3015. }
  3016. return true;
  3017. }
  3018. /*
  3019. ===============
  3020. idPlayer::GiveHealthPool
  3021. adds health to the player health pool
  3022. ===============
  3023. */
  3024. void idPlayer::GiveHealthPool( float amt ) {
  3025. if ( AI_DEAD ) {
  3026. return;
  3027. }
  3028. if ( health > 0 ) {
  3029. healthPool += amt;
  3030. if ( healthPool > inventory.maxHealth - health ) {
  3031. healthPool = inventory.maxHealth - health;
  3032. }
  3033. nextHealthPulse = gameLocal.time;
  3034. }
  3035. }
  3036. /*
  3037. ===============
  3038. idPlayer::GiveItem
  3039. Returns false if the item shouldn't be picked up
  3040. ===============
  3041. */
  3042. bool idPlayer::GiveItem( idItem *item, unsigned int giveFlags ) {
  3043. int i;
  3044. const idKeyValue *arg;
  3045. idDict attr;
  3046. bool gave;
  3047. int numPickup;
  3048. if ( common->IsMultiplayer() && spectating ) {
  3049. return false;
  3050. }
  3051. if ( idStr::FindText( item->GetName(), "weapon_flashlight_new" ) > -1 ) {
  3052. return false;
  3053. }
  3054. if ( idStr::FindText( item->GetName(), "weapon_flashlight" ) > -1 ) {
  3055. // don't allow flashlight weapon unless classic mode is enabled
  3056. return false;
  3057. }
  3058. item->GetAttributes( attr );
  3059. gave = false;
  3060. numPickup = inventory.pickupItemNames.Num();
  3061. for( i = 0; i < attr.GetNumKeyVals(); i++ ) {
  3062. arg = attr.GetKeyVal( i );
  3063. if ( Give( arg->GetKey(), arg->GetValue(), giveFlags ) ) {
  3064. gave = true;
  3065. }
  3066. }
  3067. if ( giveFlags & ITEM_GIVE_FEEDBACK ) {
  3068. arg = item->spawnArgs.MatchPrefix( "inv_weapon", NULL );
  3069. if ( arg ) {
  3070. // We need to update the weapon hud manually, but not
  3071. // the armor/ammo/health because they are updated every
  3072. // frame no matter what
  3073. UpdateHudWeapon( false );
  3074. }
  3075. // display the pickup feedback on the hud
  3076. if ( gave && ( numPickup == inventory.pickupItemNames.Num() ) ) {
  3077. inventory.AddPickupName( item->spawnArgs.GetString( "inv_name" ), this ); //_D3XP
  3078. }
  3079. }
  3080. return gave;
  3081. }
  3082. /*
  3083. ===============
  3084. idPlayer::PowerUpModifier
  3085. ===============
  3086. */
  3087. float idPlayer::PowerUpModifier( int type ) {
  3088. float mod = 1.0f;
  3089. if ( PowerUpActive( BERSERK ) ) {
  3090. switch( type ) {
  3091. case SPEED: {
  3092. mod *= 1.7f;
  3093. break;
  3094. }
  3095. case PROJECTILE_DAMAGE: {
  3096. mod *= 2.0f;
  3097. break;
  3098. }
  3099. case MELEE_DAMAGE: {
  3100. mod *= 30.0f;
  3101. break;
  3102. }
  3103. case MELEE_DISTANCE: {
  3104. mod *= 2.0f;
  3105. break;
  3106. }
  3107. }
  3108. }
  3109. if ( common->IsMultiplayer() && !common->IsClient() ) {
  3110. if ( PowerUpActive( MEGAHEALTH ) ) {
  3111. if ( healthPool <= 0 ) {
  3112. GiveHealthPool( 100 );
  3113. }
  3114. } else {
  3115. healthPool = 0;
  3116. }
  3117. /*if( PowerUpActive( HASTE ) ) {
  3118. switch( type ) {
  3119. case SPEED: {
  3120. mod = 1.7f;
  3121. break;
  3122. }
  3123. }
  3124. }*/
  3125. }
  3126. return mod;
  3127. }
  3128. /*
  3129. ===============
  3130. idPlayer::PowerUpActive
  3131. ===============
  3132. */
  3133. bool idPlayer::PowerUpActive( int powerup ) const {
  3134. return ( inventory.powerups & ( 1 << powerup ) ) != 0;
  3135. }
  3136. /*
  3137. ===============
  3138. idPlayer::GivePowerUp
  3139. ===============
  3140. */
  3141. bool idPlayer::GivePowerUp( int powerup, int time, unsigned int giveFlags ) {
  3142. const char *sound;
  3143. if ( powerup >= 0 && powerup < MAX_POWERUPS ) {
  3144. if ( giveFlags & ITEM_GIVE_UPDATE_STATE ) {
  3145. if ( common->IsServer() ) {
  3146. idBitMsg msg;
  3147. byte msgBuf[MAX_EVENT_PARAM_SIZE];
  3148. msg.InitWrite( msgBuf, sizeof( msgBuf ) );
  3149. msg.WriteShort( powerup );
  3150. msg.WriteShort( time );
  3151. ServerSendEvent( EVENT_POWERUP, &msg, false );
  3152. }
  3153. if ( powerup != MEGAHEALTH ) {
  3154. inventory.GivePowerUp( this, powerup, time );
  3155. }
  3156. }
  3157. switch( powerup ) {
  3158. case BERSERK: {
  3159. if ( giveFlags & ITEM_GIVE_FROM_WEAPON ) {
  3160. // Berserk is granted by the bloodstone in ROE, but we don't want any of the
  3161. // standard behavior (sound fx, switch to fists) when you get it this way.
  3162. } else {
  3163. if ( giveFlags & ITEM_GIVE_FEEDBACK ) {
  3164. inventory.AddPickupName( "#str_00100627", this );
  3165. if ( spawnArgs.GetString( "snd_berserk_third", "", &sound ) && sound[ 0 ] != '\0' ) {
  3166. StartSoundShader( declManager->FindSound( sound ), SND_CHANNEL_DEMONIC, 0, false, NULL );
  3167. }
  3168. }
  3169. if ( giveFlags & ITEM_GIVE_UPDATE_STATE ) {
  3170. if ( !common->IsClient() ) {
  3171. idealWeapon = weapon_fists;
  3172. }
  3173. }
  3174. }
  3175. break;
  3176. }
  3177. case INVISIBILITY: {
  3178. if ( common->IsMultiplayer() && ( giveFlags & ITEM_GIVE_FEEDBACK ) ) {
  3179. inventory.AddPickupName("#str_00100628", this);
  3180. }
  3181. if ( giveFlags & ITEM_GIVE_UPDATE_STATE ) {
  3182. // remove any decals from the model
  3183. if ( modelDefHandle != -1 ) {
  3184. gameRenderWorld->RemoveDecals( modelDefHandle );
  3185. }
  3186. if ( weapon.GetEntity() ) {
  3187. weapon.GetEntity()->UpdateSkin();
  3188. }
  3189. if( flashlight.GetEntity() ) {
  3190. flashlight.GetEntity()->UpdateSkin();
  3191. }
  3192. }
  3193. /* if ( spawnArgs.GetString( "snd_invisibility", "", &sound ) ) {
  3194. StartSoundShader( declManager->FindSound( sound ), SND_CHANNEL_ANY, 0, false, NULL );
  3195. } */
  3196. break;
  3197. }
  3198. case ADRENALINE: {
  3199. if ( giveFlags & ITEM_GIVE_FEEDBACK ) {
  3200. inventory.AddPickupName("#str_00100799", this);
  3201. }
  3202. if ( giveFlags & ITEM_GIVE_UPDATE_STATE ) {
  3203. stamina = 100.0f;
  3204. }
  3205. break;
  3206. }
  3207. case MEGAHEALTH: {
  3208. if ( giveFlags & ITEM_GIVE_FEEDBACK ) {
  3209. if( common->IsMultiplayer() ) {
  3210. inventory.AddPickupName("#str_00100629", this);
  3211. }
  3212. if ( spawnArgs.GetString( "snd_megahealth", "", &sound ) ) {
  3213. StartSoundShader( declManager->FindSound( sound ), SND_CHANNEL_ANY, 0, false, NULL );
  3214. }
  3215. }
  3216. if ( giveFlags & ITEM_GIVE_UPDATE_STATE ) {
  3217. health = 200;
  3218. }
  3219. break;
  3220. }
  3221. case HELLTIME: {
  3222. if ( spawnArgs.GetString( "snd_helltime_start", "", &sound ) ) {
  3223. PostEventMS( &EV_StartSoundShader, 0, sound, SND_CHANNEL_ANY );
  3224. }
  3225. if ( spawnArgs.GetString( "snd_helltime_loop", "", &sound ) ) {
  3226. PostEventMS( &EV_StartSoundShader, 0, sound, SND_CHANNEL_DEMONIC );
  3227. }
  3228. break;
  3229. }
  3230. case ENVIROSUIT: {
  3231. if ( giveFlags & ITEM_GIVE_FEEDBACK ) {
  3232. // Turn on the envirosuit sound
  3233. if ( gameSoundWorld ) {
  3234. gameSoundWorld->SetEnviroSuit( true );
  3235. }
  3236. }
  3237. if( giveFlags & ITEM_GIVE_UPDATE_STATE ) {
  3238. // Put the helmet and lights on the player
  3239. idDict args;
  3240. // Light
  3241. const idDict *lightDef = gameLocal.FindEntityDefDict( "envirosuit_light", false );
  3242. if ( lightDef ) {
  3243. idEntity *temp;
  3244. gameLocal.SpawnEntityDef( *lightDef, &temp, false );
  3245. idLight *eLight = static_cast<idLight *>(temp);
  3246. eLight->GetPhysics()->SetOrigin( firstPersonViewOrigin );
  3247. eLight->UpdateVisuals();
  3248. eLight->Present();
  3249. enviroSuitLight = eLight;
  3250. }
  3251. }
  3252. break;
  3253. }
  3254. case ENVIROTIME: {
  3255. if ( giveFlags & ITEM_GIVE_UPDATE_STATE ) {
  3256. hudPowerup = ENVIROTIME;
  3257. // The HUD display bar is fixed at 60 seconds
  3258. hudPowerupDuration = 60000;
  3259. }
  3260. break;
  3261. }
  3262. case INVULNERABILITY: {
  3263. if ( common->IsMultiplayer() && ( giveFlags & ITEM_GIVE_FEEDBACK ) ) {
  3264. inventory.AddPickupName("#str_00100630", this);
  3265. }
  3266. break;
  3267. }
  3268. }
  3269. if ( giveFlags & ITEM_GIVE_UPDATE_STATE ) {
  3270. UpdateSkinSetup();
  3271. }
  3272. return true;
  3273. } else {
  3274. gameLocal.Warning( "Player given power up %i\n which is out of range", powerup );
  3275. }
  3276. return false;
  3277. }
  3278. /*
  3279. ==============
  3280. idPlayer::ClearPowerup
  3281. ==============
  3282. */
  3283. void idPlayer::ClearPowerup( int i ) {
  3284. if ( common->IsServer() ) {
  3285. idBitMsg msg;
  3286. byte msgBuf[MAX_EVENT_PARAM_SIZE];
  3287. msg.InitWrite( msgBuf, sizeof( msgBuf ) );
  3288. msg.WriteShort( i );
  3289. msg.WriteShort( 0 );
  3290. ServerSendEvent( EVENT_POWERUP, &msg, false );
  3291. }
  3292. powerUpSkin = NULL;
  3293. inventory.powerups &= ~( 1 << i );
  3294. inventory.powerupEndTime[ i ] = 0;
  3295. switch( i ) {
  3296. case BERSERK: {
  3297. if(common->IsMultiplayer()) {
  3298. StopSound( SND_CHANNEL_DEMONIC, false );
  3299. }
  3300. if(!common->IsMultiplayer()) {
  3301. StopHealthRecharge();
  3302. }
  3303. break;
  3304. }
  3305. case INVISIBILITY: {
  3306. if ( weapon.GetEntity() ) {
  3307. weapon.GetEntity()->UpdateSkin();
  3308. }
  3309. if ( flashlight.GetEntity() ) {
  3310. flashlight.GetEntity()->UpdateSkin();
  3311. }
  3312. break;
  3313. }
  3314. case HELLTIME: {
  3315. GetAchievementManager().ResetHellTimeKills();
  3316. StopSound( SND_CHANNEL_DEMONIC, false );
  3317. break;
  3318. }
  3319. case ENVIROSUIT: {
  3320. hudPowerup = -1;
  3321. // Turn off the envirosuit sound
  3322. if ( gameSoundWorld ) {
  3323. gameSoundWorld->SetEnviroSuit( false );
  3324. }
  3325. // Take off the helmet and lights
  3326. if ( enviroSuitLight.IsValid() ) {
  3327. enviroSuitLight.GetEntity()->PostEventMS( &EV_Remove, 0 );
  3328. }
  3329. enviroSuitLight = NULL;
  3330. break;
  3331. }
  3332. case INVULNERABILITY: {
  3333. if(common->IsMultiplayer()) {
  3334. StopSound( SND_CHANNEL_DEMONIC, false );
  3335. }
  3336. }
  3337. /*case HASTE: {
  3338. if(common->IsMultiplayer()) {
  3339. StopSound( SND_CHANNEL_DEMONIC, false );
  3340. }
  3341. }*/
  3342. }
  3343. }
  3344. /*
  3345. ==============
  3346. idPlayer::UpdatePowerUps
  3347. ==============
  3348. */
  3349. void idPlayer::UpdatePowerUps() {
  3350. int i;
  3351. if ( !common->IsClient() ) {
  3352. for ( i = 0; i < MAX_POWERUPS; i++ ) {
  3353. if ( ( inventory.powerups & ( 1 << i ) ) && inventory.powerupEndTime[i] > gameLocal.time ) {
  3354. switch( i ) {
  3355. case ENVIROSUIT: {
  3356. if ( enviroSuitLight.IsValid() ) {
  3357. idAngles lightAng = firstPersonViewAxis.ToAngles();
  3358. idVec3 lightOrg = firstPersonViewOrigin;
  3359. const idDict *lightDef = gameLocal.FindEntityDefDict( "envirosuit_light", false );
  3360. idVec3 enviroOffset = lightDef->GetVector( "enviro_offset" );
  3361. idVec3 enviroAngleOffset = lightDef->GetVector( "enviro_angle_offset" );
  3362. lightOrg += (enviroOffset.x * firstPersonViewAxis[0]);
  3363. lightOrg += (enviroOffset.y * firstPersonViewAxis[1]);
  3364. lightOrg += (enviroOffset.z * firstPersonViewAxis[2]);
  3365. lightAng.pitch += enviroAngleOffset.x;
  3366. lightAng.yaw += enviroAngleOffset.y;
  3367. lightAng.roll += enviroAngleOffset.z;
  3368. enviroSuitLight.GetEntity()->GetPhysics()->SetOrigin( lightOrg );
  3369. enviroSuitLight.GetEntity()->GetPhysics()->SetAxis( lightAng.ToMat3() );
  3370. enviroSuitLight.GetEntity()->UpdateVisuals();
  3371. enviroSuitLight.GetEntity()->Present();
  3372. }
  3373. break;
  3374. }
  3375. default: {
  3376. break;
  3377. }
  3378. }
  3379. }
  3380. if ( PowerUpActive( i ) && inventory.powerupEndTime[i] <= gameLocal.time ) {
  3381. ClearPowerup( i );
  3382. }
  3383. }
  3384. }
  3385. if ( health > 0 ) {
  3386. if ( powerUpSkin ) {
  3387. renderEntity.customSkin = powerUpSkin;
  3388. } else {
  3389. renderEntity.customSkin = skin;
  3390. }
  3391. }
  3392. if ( healthPool && gameLocal.time > nextHealthPulse && !AI_DEAD && health > 0 ) {
  3393. assert( !common->IsClient() ); // healthPool never be set on client
  3394. int amt = ( healthPool > 5.0f ) ? 5 : healthPool;
  3395. health += amt;
  3396. if ( health > inventory.maxHealth ) {
  3397. health = inventory.maxHealth;
  3398. healthPool = 0;
  3399. } else {
  3400. healthPool -= amt;
  3401. }
  3402. if ( healthPool < 1.0f ) {
  3403. healthPool = 0.0f;
  3404. } else {
  3405. nextHealthPulse = gameLocal.time + HEALTHPULSE_TIME;
  3406. healthPulse = true;
  3407. }
  3408. }
  3409. if ( !gameLocal.inCinematic && influenceActive == 0 && g_skill.GetInteger() == 3 && gameLocal.time > nextHealthTake && !AI_DEAD && health > g_healthTakeLimit.GetInteger() ) {
  3410. assert( !common->IsClient() ); // healthPool never be set on client
  3411. if(!PowerUpActive(INVULNERABILITY)) {
  3412. health -= g_healthTakeAmt.GetInteger();
  3413. if ( health < g_healthTakeLimit.GetInteger() ) {
  3414. health = g_healthTakeLimit.GetInteger();
  3415. }
  3416. }
  3417. nextHealthTake = gameLocal.time + g_healthTakeTime.GetInteger() * 1000;
  3418. healthTake = true;
  3419. }
  3420. }
  3421. /*
  3422. ===============
  3423. idPlayer::ClearPowerUps
  3424. ===============
  3425. */
  3426. void idPlayer::ClearPowerUps() {
  3427. int i;
  3428. for ( i = 0; i < MAX_POWERUPS; i++ ) {
  3429. if ( PowerUpActive( i ) ) {
  3430. ClearPowerup( i );
  3431. }
  3432. }
  3433. inventory.ClearPowerUps();
  3434. if ( common->IsMultiplayer() ) {
  3435. if ( enviroSuitLight.IsValid() ) {
  3436. enviroSuitLight.GetEntity()->PostEventMS( &EV_Remove, 0 );
  3437. }
  3438. }
  3439. }
  3440. /*
  3441. ===============
  3442. idPlayer::GiveInventoryItem
  3443. ===============
  3444. */
  3445. bool idPlayer::GiveInventoryItem( idDict * item, unsigned int giveFlags ) {
  3446. if ( common->IsMultiplayer() && spectating ) {
  3447. return false;
  3448. }
  3449. if ( giveFlags & ITEM_GIVE_UPDATE_STATE ) {
  3450. inventory.items.Append( new (TAG_ENTITY) idDict( *item ) );
  3451. }
  3452. const char * itemName = item->GetString( "inv_name" );
  3453. if ( giveFlags & ITEM_GIVE_FEEDBACK ) {
  3454. if ( idStr::Cmpn( itemName, STRTABLE_ID, STRTABLE_ID_LENGTH ) == 0 ) {
  3455. inventory.pickupItemNames.Append( idLocalization::GetString( itemName ) );
  3456. } else {
  3457. inventory.pickupItemNames.Append( itemName );
  3458. }
  3459. const char * icon = item->GetString( "inv_icon" );
  3460. if ( hud != NULL ) {
  3461. hud->ShowNewItem( itemName, icon );
  3462. }
  3463. }
  3464. // D3XP added to support powercells
  3465. if( ( giveFlags & ITEM_GIVE_UPDATE_STATE ) && item->GetInt("inv_powercell") && focusUI) {
  3466. //Reset the powercell count
  3467. int powerCellCount = 0;
  3468. for ( int j = 0; j < inventory.items.Num(); j++ ) {
  3469. idDict *item = inventory.items[ j ];
  3470. if(item->GetInt("inv_powercell")) {
  3471. powerCellCount++;
  3472. }
  3473. }
  3474. focusUI->SetStateInt( "powercell_count", powerCellCount );
  3475. }
  3476. return true;
  3477. }
  3478. /*
  3479. ==============
  3480. idPlayer::GiveInventoryItem
  3481. ==============
  3482. */
  3483. bool idPlayer::GiveInventoryItem( const char *name ) {
  3484. idDict args;
  3485. args.Set( "classname", name );
  3486. args.Set( "owner", this->name.c_str() );
  3487. gameLocal.SpawnEntityDef( args);
  3488. return true;
  3489. }
  3490. /*
  3491. ===============
  3492. idPlayer::GiveObjective
  3493. ===============
  3494. */
  3495. void idPlayer::GiveObjective( const char * title, const char * text, const idMaterial * screenshot ) {
  3496. idObjectiveInfo & info = inventory.objectiveNames.Alloc();
  3497. info.title = title;
  3498. info.text = text;
  3499. info.screenshot = screenshot;
  3500. StartSound( "snd_objectiveup", SND_CHANNEL_ANY, 0, false, NULL );
  3501. if ( hud ) {
  3502. hud->SetupObjective( title, text, screenshot );
  3503. hud->ShowObjective( false );
  3504. objectiveUp = true;
  3505. }
  3506. }
  3507. /*
  3508. ===============
  3509. idPlayer::CompleteObjective
  3510. ===============
  3511. */
  3512. void idPlayer::CompleteObjective( const char *title ) {
  3513. int c = inventory.objectiveNames.Num();
  3514. for ( int i = 0; i < c; i++ ) {
  3515. if ( idStr::Icmp(inventory.objectiveNames[i].title, title) == 0 ) {
  3516. inventory.objectiveNames.RemoveIndex( i );
  3517. break;
  3518. }
  3519. }
  3520. StartSound( "snd_objectiveup", SND_CHANNEL_ANY, 0, false, NULL );
  3521. if ( hud ) {
  3522. hud->SetupObjectiveComplete( title );
  3523. hud->ShowObjective( true );
  3524. }
  3525. }
  3526. /*
  3527. ===============
  3528. idPlayer::GiveVideo
  3529. ===============
  3530. */
  3531. void idPlayer::GiveVideo( const idDeclVideo * video, const char * itemName ) {
  3532. if ( video == NULL ) {
  3533. return;
  3534. }
  3535. int oldNumVideos = inventory.videos.Num();
  3536. inventory.videos.AddUnique( video );
  3537. if ( oldNumVideos < inventory.videos.Num() ) {
  3538. GetAchievementManager().EventCompletesAchievement( ACHIEVEMENT_WATCH_ALL_VIDEOS );
  3539. }
  3540. if ( itemName != NULL && itemName[0] != 0 ) {
  3541. inventory.pickupItemNames.Append( itemName );
  3542. }
  3543. if ( hud ) {
  3544. hud->DownloadVideo();
  3545. }
  3546. }
  3547. /*
  3548. ===============
  3549. idPlayer::GiveSecurity
  3550. ===============
  3551. */
  3552. void idPlayer::GiveSecurity( const char *security ) {
  3553. GetPDA()->SetSecurity( security );
  3554. if ( hud ) {
  3555. hud->UpdatedSecurity();
  3556. }
  3557. }
  3558. /*
  3559. ===============
  3560. idPlayer::GiveEmail
  3561. ===============
  3562. */
  3563. void idPlayer::GiveEmail( const idDeclEmail * email ) {
  3564. if ( email == NULL ) {
  3565. return;
  3566. }
  3567. inventory.emails.AddUnique( email );
  3568. GetPDA()->AddEmail( email );
  3569. // TODO_SPARTY: hook up new email notification in new hud
  3570. //if ( hud ) {
  3571. // hud->HandleNamedEvent( "emailPickup" );
  3572. //}
  3573. }
  3574. /*
  3575. ===============
  3576. idPlayer::GivePDA
  3577. ===============
  3578. */
  3579. void idPlayer::GivePDA( const idDeclPDA * pda, const char * securityItem ) {
  3580. if ( common->IsMultiplayer() && spectating ) {
  3581. return;
  3582. }
  3583. if ( securityItem != NULL && securityItem[0] != 0 ) {
  3584. inventory.pdaSecurity.AddUnique( securityItem );
  3585. }
  3586. // Just to make sure they want the default player spawn defined pda.
  3587. // Some what of a hack, so i dont have to change any map scripts that initially give
  3588. // the player "personal" pda.
  3589. if ( pda == NULL || idStr::Icmp( pda->GetName(), "personal" ) == 0 ) {
  3590. pda = static_cast<const idDeclPDA *>( declManager->FindType( DECL_PDA, spawnArgs.GetString( "pda_name", "personal" ) ) );
  3591. }
  3592. if ( pda == NULL ) {
  3593. return;
  3594. }
  3595. int oldNumPDAs = inventory.pdas.Num();
  3596. inventory.pdas.AddUnique( pda );
  3597. int newNumPDAs = inventory.pdas.Num();
  3598. // Set the stat for # of PDAs...
  3599. // Only increment the PDA stat if we've added a new one....
  3600. if ( oldNumPDAs < newNumPDAs ) {
  3601. switch ( GetExpansionType() ) {
  3602. case GAME_BASE:
  3603. GetAchievementManager().EventCompletesAchievement( ACHIEVEMENT_PDAS_BASE );
  3604. break;
  3605. case GAME_D3XP:
  3606. GetAchievementManager().EventCompletesAchievement( ACHIEVEMENT_PDAS_ROE );
  3607. break;
  3608. case GAME_D3LE:
  3609. GetAchievementManager().EventCompletesAchievement( ACHIEVEMENT_PDAS_LE );
  3610. break;
  3611. }
  3612. }
  3613. // Copy any videos over
  3614. for ( int i = 0; i < pda->GetNumVideos(); i++ ) {
  3615. const idDeclVideo * video = pda->GetVideoByIndex( i );
  3616. if ( video != NULL ) {
  3617. inventory.videos.AddUnique( video );
  3618. }
  3619. }
  3620. // This is kind of a hack, but it works nicely
  3621. // We don't want to display the 'you got a new pda' message during a map load
  3622. if ( gameLocal.GetFrameNum() > 10 ) {
  3623. const char * sec = pda->GetSecurity();
  3624. if ( hud ) {
  3625. hud->DownloadPDA( pda, ( sec != NULL && sec[0] != 0 ) ? true : false );
  3626. }
  3627. if ( inventory.pdas.Num() == 1 ) {
  3628. GetPDA()->RemoveAddedEmailsAndVideos();
  3629. if ( !objectiveSystemOpen ) {
  3630. TogglePDA();
  3631. }
  3632. //ShowTip( spawnArgs.GetString( "text_infoTitle" ), spawnArgs.GetString( "text_firstPDA" ), true );
  3633. }
  3634. }
  3635. }
  3636. /*
  3637. ===============
  3638. idPlayer::FindInventoryItem
  3639. ===============
  3640. */
  3641. idDict *idPlayer::FindInventoryItem( const char *name ) {
  3642. for ( int i = 0; i < inventory.items.Num(); i++ ) {
  3643. const char *iname = inventory.items[i]->GetString( "inv_name" );
  3644. if ( iname != NULL && *iname != NULL ) {
  3645. if ( idStr::Icmp( name, iname ) == 0 ) {
  3646. return inventory.items[i];
  3647. }
  3648. }
  3649. }
  3650. return NULL;
  3651. }
  3652. /*
  3653. ===============
  3654. idPlayer::FindInventoryItem
  3655. ===============
  3656. */
  3657. idDict * idPlayer::FindInventoryItem( int index ) {
  3658. if ( index <= inventory.items.Num() ) {
  3659. return inventory.items[ index ];
  3660. }
  3661. return NULL;
  3662. }
  3663. /*
  3664. ===============
  3665. idPlayer::GetNumInventoryItems
  3666. ===============
  3667. */
  3668. int idPlayer::GetNumInventoryItems() {
  3669. return inventory.items.Num();
  3670. }
  3671. /*
  3672. ===============
  3673. idPlayer::RemoveInventoryItem
  3674. ===============
  3675. */
  3676. void idPlayer::RemoveInventoryItem( const char *name ) {
  3677. //Hack for localization
  3678. if(!idStr::Icmp(name, "Pwr Cell")) {
  3679. name = idLocalization::GetString( "#str_00101056" );
  3680. }
  3681. idDict *item = FindInventoryItem(name);
  3682. if ( item ) {
  3683. RemoveInventoryItem( item );
  3684. }
  3685. }
  3686. /*
  3687. ===============
  3688. idPlayer::RemoveInventoryItem
  3689. ===============
  3690. */
  3691. void idPlayer::RemoveInventoryItem( idDict *item ) {
  3692. inventory.items.Remove( item );
  3693. if(item->GetInt("inv_powercell") && focusUI) {
  3694. //Reset the powercell count
  3695. int powerCellCount = 0;
  3696. for ( int j = 0; j < inventory.items.Num(); j++ ) {
  3697. idDict *item = inventory.items[ j ];
  3698. if(item->GetInt("inv_powercell")) {
  3699. powerCellCount++;
  3700. }
  3701. }
  3702. focusUI->SetStateInt( "powercell_count", powerCellCount );
  3703. }
  3704. delete item;
  3705. }
  3706. /*
  3707. ===============
  3708. idPlayer::GiveItem
  3709. ===============
  3710. */
  3711. void idPlayer::GiveItem( const char *itemname ) {
  3712. idDict args;
  3713. args.Set( "classname", itemname );
  3714. args.Set( "owner", name.c_str() );
  3715. gameLocal.SpawnEntityDef( args );
  3716. }
  3717. /*
  3718. ==================
  3719. idPlayer::SlotForWeapon
  3720. ==================
  3721. */
  3722. int idPlayer::SlotForWeapon( const char *weaponName ) {
  3723. int i;
  3724. for( i = 0; i < MAX_WEAPONS; i++ ) {
  3725. const char *weap = spawnArgs.GetString( va( "def_weapon%d", i ) );
  3726. if ( !idStr::Cmp( weap, weaponName ) ) {
  3727. return i;
  3728. }
  3729. }
  3730. // not found
  3731. return -1;
  3732. }
  3733. /*
  3734. ===============
  3735. idPlayer::Reload
  3736. ===============
  3737. */
  3738. void idPlayer::Reload() {
  3739. if ( spectating || gameLocal.inCinematic || influenceActive ) {
  3740. return;
  3741. }
  3742. if ( common->IsClient() && !IsLocallyControlled() ) {
  3743. return;
  3744. }
  3745. if ( weapon.GetEntity() && weapon.GetEntity()->IsLinked() ) {
  3746. weapon.GetEntity()->Reload();
  3747. }
  3748. }
  3749. /*
  3750. ===============
  3751. idPlayer::NextBestWeapon
  3752. ===============
  3753. */
  3754. void idPlayer::NextBestWeapon() {
  3755. const char *weap;
  3756. int w = MAX_WEAPONS;
  3757. if ( !weaponEnabled ) {
  3758. return;
  3759. }
  3760. while ( w > 0 ) {
  3761. w--;
  3762. if ( w == weapon_flashlight ) {
  3763. continue;
  3764. }
  3765. weap = spawnArgs.GetString( va( "def_weapon%d", w ) );
  3766. if ( !weap[ 0 ] || ( ( inventory.weapons & ( 1 << w ) ) == 0 ) || ( !inventory.HasAmmo( weap, true, this ) ) ) {
  3767. continue;
  3768. }
  3769. if ( !spawnArgs.GetBool( va( "weapon%d_best", w ) ) ) {
  3770. continue;
  3771. }
  3772. //Some weapons will report having ammo but the clip is empty and
  3773. //will not have enough to fill the clip (i.e. Double Barrel Shotgun with 1 round left)
  3774. //We need to skip these weapons because they cannot be used
  3775. if(inventory.HasEmptyClipCannotRefill(weap, this)) {
  3776. continue;
  3777. }
  3778. break;
  3779. }
  3780. idealWeapon = w;
  3781. weaponSwitchTime = gameLocal.time + WEAPON_SWITCH_DELAY;
  3782. UpdateHudWeapon();
  3783. }
  3784. /*
  3785. ===============
  3786. idPlayer::NextWeapon
  3787. ===============
  3788. */
  3789. void idPlayer::NextWeapon() {
  3790. if ( !weaponEnabled || spectating || hiddenWeapon || gameLocal.inCinematic || gameLocal.world->spawnArgs.GetBool( "no_Weapons" ) || health < 0 ) {
  3791. return;
  3792. }
  3793. // check if we have any weapons
  3794. if ( !inventory.weapons ) {
  3795. return;
  3796. }
  3797. int w = idealWeapon.Get();
  3798. while( 1 ) {
  3799. w++;
  3800. if ( w >= MAX_WEAPONS ) {
  3801. w = 0;
  3802. }
  3803. if ( w == idealWeapon ) {
  3804. w = weapon_fists;
  3805. break;
  3806. }
  3807. if ( ( inventory.weapons & ( 1 << w ) ) == 0 ) {
  3808. continue;
  3809. }
  3810. const char * weap = spawnArgs.GetString( va( "def_weapon%d", w ) );
  3811. if ( !spawnArgs.GetBool( va( "weapon%d_cycle", w ) ) ) {
  3812. continue;
  3813. }
  3814. if ( !weap[ 0 ] ) {
  3815. continue;
  3816. }
  3817. if ( inventory.HasAmmo( weap, true, this ) || w == weapon_bloodstone ) {
  3818. break;
  3819. }
  3820. }
  3821. if ( ( w != currentWeapon ) && ( w != idealWeapon ) ) {
  3822. idealWeapon = w;
  3823. weaponSwitchTime = gameLocal.time + WEAPON_SWITCH_DELAY;
  3824. UpdateHudWeapon();
  3825. }
  3826. }
  3827. /*
  3828. ===============
  3829. idPlayer::PrevWeapon
  3830. ===============
  3831. */
  3832. void idPlayer::PrevWeapon() {
  3833. if ( !weaponEnabled || spectating || hiddenWeapon || gameLocal.inCinematic || gameLocal.world->spawnArgs.GetBool( "no_Weapons" ) || health < 0 ) {
  3834. return;
  3835. }
  3836. // check if we have any weapons
  3837. if ( !inventory.weapons ) {
  3838. return;
  3839. }
  3840. int w = idealWeapon.Get();
  3841. while( 1 ) {
  3842. w--;
  3843. if ( w < 0 ) {
  3844. w = MAX_WEAPONS - 1;
  3845. }
  3846. if ( w == idealWeapon ) {
  3847. w = weapon_fists;
  3848. break;
  3849. }
  3850. if ( ( inventory.weapons & ( 1 << w ) ) == 0 ) {
  3851. continue;
  3852. }
  3853. const char * weap = spawnArgs.GetString( va( "def_weapon%d", w ) );
  3854. if ( !spawnArgs.GetBool( va( "weapon%d_cycle", w ) ) ) {
  3855. continue;
  3856. }
  3857. if ( !weap[ 0 ] ) {
  3858. continue;
  3859. }
  3860. if ( inventory.HasAmmo( weap, true, this ) || w == weapon_bloodstone ) {
  3861. break;
  3862. }
  3863. }
  3864. if ( ( w != currentWeapon ) && ( w != idealWeapon ) ) {
  3865. idealWeapon = w;
  3866. weaponSwitchTime = gameLocal.time + WEAPON_SWITCH_DELAY;
  3867. UpdateHudWeapon();
  3868. }
  3869. }
  3870. /*
  3871. ===============
  3872. idPlayer::SelectWeapon
  3873. ===============
  3874. */
  3875. void idPlayer::SelectWeapon( int num, bool force ) {
  3876. const char *weap;
  3877. if ( !weaponEnabled || spectating || gameLocal.inCinematic || health < 0 ) {
  3878. return;
  3879. }
  3880. if ( ( num < 0 ) || ( num >= MAX_WEAPONS ) ) {
  3881. return;
  3882. }
  3883. if ( num == weapon_flashlight ) {
  3884. return;
  3885. }
  3886. if ( ( num != weapon_pda ) && gameLocal.world->spawnArgs.GetBool( "no_Weapons" ) ) {
  3887. num = weapon_fists;
  3888. hiddenWeapon ^= 1;
  3889. if ( hiddenWeapon && weapon.GetEntity() ) {
  3890. weapon.GetEntity()->LowerWeapon();
  3891. } else {
  3892. weapon.GetEntity()->RaiseWeapon();
  3893. }
  3894. }
  3895. //Is the weapon a toggle weapon
  3896. WeaponToggle_t* weaponToggle;
  3897. if(weaponToggles.Get(va("weapontoggle%d", num), &weaponToggle)) {
  3898. int weaponToggleIndex = 0;
  3899. //Find the current Weapon in the list
  3900. int currentIndex = -1;
  3901. for(int i = 0; i < weaponToggle->toggleList.Num(); i++) {
  3902. if(weaponToggle->toggleList[i] == idealWeapon) {
  3903. currentIndex = i;
  3904. break;
  3905. }
  3906. }
  3907. if ( currentIndex == -1 ) {
  3908. //Didn't find the current weapon so select the first item
  3909. weaponToggleIndex = weaponToggle->lastUsed;
  3910. } else {
  3911. //Roll to the next available item in the list
  3912. weaponToggleIndex = currentIndex;
  3913. weaponToggleIndex++;
  3914. if(weaponToggleIndex >= weaponToggle->toggleList.Num()) {
  3915. weaponToggleIndex = 0;
  3916. }
  3917. }
  3918. for(int i = 0; i < weaponToggle->toggleList.Num(); i++) {
  3919. int weapNum = weaponToggle->toggleList[weaponToggleIndex];
  3920. //Is it available
  3921. if(inventory.weapons & ( 1 << weapNum)) {
  3922. //Do we have ammo for it
  3923. if ( inventory.HasAmmo( spawnArgs.GetString( va( "def_weapon%d", weapNum ) ), true, this ) || spawnArgs.GetBool( va( "weapon%d_allowempty", weapNum ) ) ) {
  3924. break;
  3925. }
  3926. }
  3927. weaponToggleIndex++;
  3928. if(weaponToggleIndex >= weaponToggle->toggleList.Num()) {
  3929. weaponToggleIndex = 0;
  3930. }
  3931. }
  3932. weaponToggle->lastUsed = weaponToggleIndex;
  3933. num = weaponToggle->toggleList[weaponToggleIndex];
  3934. }
  3935. weap = spawnArgs.GetString( va( "def_weapon%d", num ) );
  3936. if ( !weap[ 0 ] ) {
  3937. gameLocal.Printf( "Invalid weapon\n" );
  3938. return;
  3939. }
  3940. if ( force || ( inventory.weapons & ( 1 << num ) ) ) {
  3941. if ( !inventory.HasAmmo( weap, true, this ) && !spawnArgs.GetBool( va( "weapon%d_allowempty", num ) ) ) {
  3942. return;
  3943. }
  3944. if ( ( previousWeapon >= 0 ) && ( idealWeapon == num ) && ( spawnArgs.GetBool( va( "weapon%d_toggle", num ) ) ) ) {
  3945. weap = spawnArgs.GetString( va( "def_weapon%d", previousWeapon ) );
  3946. if ( !inventory.HasAmmo( weap, true, this ) && !spawnArgs.GetBool( va( "weapon%d_allowempty", previousWeapon ) ) ) {
  3947. return;
  3948. }
  3949. idealWeapon = previousWeapon;
  3950. } else if ( ( weapon_pda >= 0 ) && ( num == weapon_pda ) && ( inventory.pdas.Num() == 0 ) ) {
  3951. ShowTip( spawnArgs.GetString( "text_infoTitle" ), spawnArgs.GetString( "text_noPDA" ), true );
  3952. return;
  3953. } else {
  3954. idealWeapon = num;
  3955. }
  3956. UpdateHudWeapon();
  3957. }
  3958. }
  3959. /*
  3960. =================
  3961. idPlayer::DropWeapon
  3962. =================
  3963. */
  3964. void idPlayer::DropWeapon( bool died ) {
  3965. idVec3 forward, up;
  3966. int inclip, ammoavailable;
  3967. if( died == false ){
  3968. return;
  3969. }
  3970. assert( !common->IsClient() );
  3971. if ( spectating || weaponGone || weapon.GetEntity() == NULL ) {
  3972. return;
  3973. }
  3974. if ( ( !died && !weapon.GetEntity()->IsReady() ) || weapon.GetEntity()->IsReloading() ) {
  3975. return;
  3976. }
  3977. // ammoavailable is how many shots we can fire
  3978. // inclip is which amount is in clip right now
  3979. ammoavailable = weapon.GetEntity()->AmmoAvailable();
  3980. inclip = weapon.GetEntity()->AmmoInClip();
  3981. // don't drop a grenade if we have none left
  3982. if ( !idStr::Icmp( idWeapon::GetAmmoNameForNum( weapon.GetEntity()->GetAmmoType() ), "ammo_grenades" ) && ( ammoavailable - inclip <= 0 ) ) {
  3983. return;
  3984. }
  3985. ammoavailable += inclip;
  3986. // expect an ammo setup that makes sense before doing any dropping
  3987. // ammoavailable is -1 for infinite ammo, and weapons like chainsaw
  3988. // a bad ammo config usually indicates a bad weapon state, so we should not drop
  3989. // used to be an assertion check, but it still happens in edge cases
  3990. if ( ( ammoavailable != -1 ) && ( ammoavailable < 0 ) ) {
  3991. common->DPrintf( "idPlayer::DropWeapon: bad ammo setup\n" );
  3992. return;
  3993. }
  3994. idEntity *item = NULL;
  3995. if ( died ) {
  3996. // ain't gonna throw you no weapon if I'm dead
  3997. item = weapon.GetEntity()->DropItem( vec3_origin, 0, WEAPON_DROP_TIME, died );
  3998. } else {
  3999. viewAngles.ToVectors( &forward, NULL, &up );
  4000. item = weapon.GetEntity()->DropItem( 250.0f * forward + 150.0f * up, 500, WEAPON_DROP_TIME, died );
  4001. }
  4002. if ( !item ) {
  4003. return;
  4004. }
  4005. // set the appropriate ammo in the dropped object
  4006. const idKeyValue * keyval = item->spawnArgs.MatchPrefix( "inv_ammo_" );
  4007. if ( keyval ) {
  4008. item->spawnArgs.SetInt( keyval->GetKey(), ammoavailable );
  4009. idStr inclipKey = keyval->GetKey();
  4010. inclipKey.Insert( "inclip_", 4 );
  4011. inclipKey.Insert( va("%.2d", currentWeapon), 11);
  4012. item->spawnArgs.SetInt( inclipKey, inclip );
  4013. }
  4014. if ( !died ) {
  4015. // remove from our local inventory completely
  4016. inventory.Drop( spawnArgs, item->spawnArgs.GetString( "inv_weapon" ), -1 );
  4017. weapon.GetEntity()->ResetAmmoClip();
  4018. NextWeapon();
  4019. weapon.GetEntity()->WeaponStolen();
  4020. weaponGone = true;
  4021. }
  4022. }
  4023. /*
  4024. =================
  4025. idPlayer::StealWeapon
  4026. steal the target player's current weapon
  4027. =================
  4028. */
  4029. void idPlayer::StealWeapon( idPlayer *player ) {
  4030. assert( !common->IsClient() );
  4031. // make sure there's something to steal
  4032. idWeapon *player_weapon = static_cast< idWeapon * >( player->weapon.GetEntity() );
  4033. if ( !player_weapon || !player_weapon->CanDrop() || weaponGone ) {
  4034. return;
  4035. }
  4036. // steal - we need to effectively force the other player to abandon his weapon
  4037. int newweap = player->currentWeapon;
  4038. if ( newweap == -1 ) {
  4039. return;
  4040. }
  4041. // might be just dropped - check inventory
  4042. if ( ! ( player->inventory.weapons & ( 1 << newweap ) ) ) {
  4043. return;
  4044. }
  4045. const char *weapon_classname = spawnArgs.GetString( va( "def_weapon%d", newweap ) );
  4046. assert( weapon_classname );
  4047. int ammoavailable = player->weapon.GetEntity()->AmmoAvailable();
  4048. int inclip = player->weapon.GetEntity()->AmmoInClip();
  4049. ammoavailable += inclip;
  4050. if ( ( ammoavailable != -1 ) && ( ammoavailable < 0 ) ) {
  4051. // see DropWeapon
  4052. common->DPrintf( "idPlayer::StealWeapon: bad ammo setup\n" );
  4053. // we still steal the weapon, so let's use the default ammo levels
  4054. inclip = -1;
  4055. const idDeclEntityDef *decl = gameLocal.FindEntityDef( weapon_classname );
  4056. assert( decl );
  4057. const idKeyValue *keypair = decl->dict.MatchPrefix( "inv_ammo_" );
  4058. assert( keypair );
  4059. ammoavailable = atoi( keypair->GetValue() );
  4060. }
  4061. player->weapon.GetEntity()->WeaponStolen();
  4062. player->inventory.Drop( player->spawnArgs, NULL, newweap );
  4063. player->SelectWeapon( weapon_fists, false );
  4064. // in case the robbed player is firing rounds with a continuous fire weapon like the chaingun/plasma etc.
  4065. // this will ensure the firing actually stops
  4066. player->weaponGone = true;
  4067. // give weapon, setup the ammo count
  4068. Give( "weapon", weapon_classname, ITEM_GIVE_FEEDBACK | ITEM_GIVE_UPDATE_STATE );
  4069. ammo_t ammo_i = player->inventory.AmmoIndexForWeaponClass( weapon_classname, NULL );
  4070. idealWeapon = newweap;
  4071. const int currentAmmo = inventory.GetInventoryAmmoForType( ammo_i );
  4072. inventory.SetInventoryAmmoForType( ammo_i, currentAmmo + ammoavailable );
  4073. }
  4074. /*
  4075. ===============
  4076. idPlayer::ActiveGui
  4077. ===============
  4078. */
  4079. idUserInterface *idPlayer::ActiveGui() {
  4080. if ( objectiveSystemOpen ) {
  4081. return NULL;
  4082. }
  4083. return focusUI;
  4084. }
  4085. /*
  4086. ===============
  4087. idPlayer::Weapon_Combat
  4088. ===============
  4089. */
  4090. void idPlayer::Weapon_Combat() {
  4091. if ( influenceActive || !weaponEnabled || gameLocal.inCinematic || privateCameraView ) {
  4092. return;
  4093. }
  4094. weapon.GetEntity()->RaiseWeapon();
  4095. if ( weapon.GetEntity()->IsReloading() ) {
  4096. if ( !AI_RELOAD ) {
  4097. AI_RELOAD = true;
  4098. SetState( "ReloadWeapon" );
  4099. UpdateScript();
  4100. }
  4101. } else {
  4102. AI_RELOAD = false;
  4103. }
  4104. if ( idealWeapon == weapon_soulcube && soulCubeProjectile.GetEntity() != NULL ) {
  4105. idealWeapon = currentWeapon;
  4106. }
  4107. if ( idealWeapon != currentWeapon && idealWeapon.Get() < MAX_WEAPONS ) {
  4108. if ( weaponCatchup ) {
  4109. assert( common->IsClient() );
  4110. currentWeapon = idealWeapon.Get();
  4111. weaponGone = false;
  4112. animPrefix = spawnArgs.GetString( va( "def_weapon%d", currentWeapon ) );
  4113. weapon.GetEntity()->GetWeaponDef( animPrefix, inventory.GetClipAmmoForWeapon( currentWeapon ) );
  4114. animPrefix.Strip( "weapon_" );
  4115. weapon.GetEntity()->NetCatchup();
  4116. const function_t *newstate = GetScriptFunction( "NetCatchup" );
  4117. if ( newstate ) {
  4118. SetState( newstate );
  4119. UpdateScript();
  4120. }
  4121. weaponCatchup = false;
  4122. } else {
  4123. if ( weapon.GetEntity()->IsReady() ) {
  4124. weapon.GetEntity()->PutAway();
  4125. }
  4126. if ( weapon.GetEntity()->IsHolstered() ) {
  4127. assert( idealWeapon.Get() >= 0 );
  4128. assert( idealWeapon.Get() < MAX_WEAPONS );
  4129. if ( currentWeapon != weapon_pda && !spawnArgs.GetBool( va( "weapon%d_toggle", currentWeapon ) ) ) {
  4130. previousWeapon = currentWeapon;
  4131. }
  4132. currentWeapon = idealWeapon.Get();
  4133. weaponGone = false;
  4134. animPrefix = spawnArgs.GetString( va( "def_weapon%d", currentWeapon ) );
  4135. weapon.GetEntity()->GetWeaponDef( animPrefix, inventory.GetClipAmmoForWeapon( currentWeapon ) );
  4136. animPrefix.Strip( "weapon_" );
  4137. weapon.GetEntity()->Raise();
  4138. }
  4139. }
  4140. } else {
  4141. weaponGone = false; // if you drop and re-get weap, you may miss the = false above
  4142. if ( weapon.GetEntity()->IsHolstered() ) {
  4143. if ( !weapon.GetEntity()->AmmoAvailable() ) {
  4144. // weapons can switch automatically if they have no more ammo
  4145. NextBestWeapon();
  4146. } else {
  4147. weapon.GetEntity()->Raise();
  4148. state = GetScriptFunction( "RaiseWeapon" );
  4149. if ( state ) {
  4150. SetState( state );
  4151. }
  4152. }
  4153. }
  4154. }
  4155. // check for attack
  4156. AI_WEAPON_FIRED = false;
  4157. if ( !influenceActive ) {
  4158. if ( ( usercmd.buttons & BUTTON_ATTACK ) && !weaponGone ) {
  4159. FireWeapon();
  4160. } else if ( oldButtons & BUTTON_ATTACK ) {
  4161. AI_ATTACK_HELD = false;
  4162. weapon.GetEntity()->EndAttack();
  4163. }
  4164. }
  4165. // update our ammo clip in our inventory
  4166. if ( ( currentWeapon >= 0 ) && ( currentWeapon < MAX_WEAPONS ) ) {
  4167. inventory.SetClipAmmoForWeapon( currentWeapon, weapon.GetEntity()->AmmoInClip() );
  4168. }
  4169. }
  4170. /*
  4171. ===============
  4172. idPlayer::Weapon_NPC
  4173. ===============
  4174. */
  4175. void idPlayer::Weapon_NPC() {
  4176. if ( idealWeapon != currentWeapon ) {
  4177. Weapon_Combat();
  4178. }
  4179. StopFiring();
  4180. weapon.GetEntity()->LowerWeapon();
  4181. bool wasDown = ( oldButtons & (BUTTON_ATTACK|BUTTON_USE) ) != 0;
  4182. bool isDown = ( usercmd.buttons & (BUTTON_ATTACK|BUTTON_USE) ) != 0;
  4183. if ( isDown && !wasDown ) {
  4184. buttonMask |= BUTTON_ATTACK;
  4185. focusCharacter->TalkTo( this );
  4186. }
  4187. }
  4188. /*
  4189. ===============
  4190. idPlayer::LowerWeapon
  4191. ===============
  4192. */
  4193. void idPlayer::LowerWeapon() {
  4194. if ( weapon.GetEntity() && !weapon.GetEntity()->IsHidden() ) {
  4195. weapon.GetEntity()->LowerWeapon();
  4196. }
  4197. }
  4198. /*
  4199. ===============
  4200. idPlayer::RaiseWeapon
  4201. ===============
  4202. */
  4203. void idPlayer::RaiseWeapon() {
  4204. if ( weapon.GetEntity() && weapon.GetEntity()->IsHidden() ) {
  4205. weapon.GetEntity()->RaiseWeapon();
  4206. }
  4207. }
  4208. /*
  4209. ===============
  4210. idPlayer::WeaponLoweringCallback
  4211. ===============
  4212. */
  4213. void idPlayer::WeaponLoweringCallback() {
  4214. SetState( "LowerWeapon" );
  4215. UpdateScript();
  4216. }
  4217. /*
  4218. ===============
  4219. idPlayer::WeaponRisingCallback
  4220. ===============
  4221. */
  4222. void idPlayer::WeaponRisingCallback() {
  4223. SetState( "RaiseWeapon" );
  4224. UpdateScript();
  4225. }
  4226. /*
  4227. ===============
  4228. idPlayer::Weapon_GUI
  4229. ===============
  4230. */
  4231. void idPlayer::Weapon_GUI() {
  4232. if ( !objectiveSystemOpen ) {
  4233. if ( idealWeapon != currentWeapon ) {
  4234. Weapon_Combat();
  4235. }
  4236. StopFiring();
  4237. weapon.GetEntity()->LowerWeapon();
  4238. }
  4239. // disable click prediction for the GUIs. handy to check the state sync does the right thing
  4240. if ( common->IsClient() && !net_clientPredictGUI.GetBool() ) {
  4241. return;
  4242. }
  4243. bool wasDown = ( oldButtons & (BUTTON_ATTACK|BUTTON_USE) ) != 0;
  4244. bool isDown = ( usercmd.buttons & (BUTTON_ATTACK|BUTTON_USE) ) != 0;
  4245. if ( isDown != wasDown ) {
  4246. const char * command = NULL;
  4247. idUserInterface * ui = ActiveGui();
  4248. if ( ui ) {
  4249. bool updateVisuals = false;
  4250. sysEvent_t ev = sys->GenerateMouseButtonEvent( 1, isDown );
  4251. command = ui->HandleEvent( &ev, gameLocal.time, &updateVisuals );
  4252. if ( updateVisuals && focusGUIent && ui == focusUI ) {
  4253. focusGUIent->UpdateVisuals();
  4254. }
  4255. }
  4256. if ( common->IsClient() ) {
  4257. // we predict enough, but don't want to execute commands
  4258. return;
  4259. }
  4260. // HACK - Check to see who is activating the frag chamber. Im sorry.
  4261. if( common->IsMultiplayer() && focusGUIent ) {
  4262. if( strcmp( focusGUIent->GetName(), "chamber_gui_console" ) == 0 && strcmp( command, " ; runScript chamber_trigger" ) == 0 ){
  4263. gameLocal.playerActivateFragChamber = this;
  4264. }
  4265. }
  4266. if ( focusGUIent ) {
  4267. HandleGuiCommands( focusGUIent, command );
  4268. } else {
  4269. HandleGuiCommands( this, command );
  4270. }
  4271. }
  4272. }
  4273. /*
  4274. ===============
  4275. idPlayer::UpdateWeapon
  4276. ===============
  4277. */
  4278. void idPlayer::UpdateWeapon() {
  4279. if ( health <= 0 ) {
  4280. return;
  4281. }
  4282. assert( !spectating );
  4283. if ( common->IsClient() ) {
  4284. // clients need to wait till the weapon and it's world model entity
  4285. // are present and synchronized ( weapon.worldModel idEntityPtr to idAnimatedEntity )
  4286. if ( !weapon.GetEntity()->IsWorldModelReady() ) {
  4287. return;
  4288. }
  4289. }
  4290. // always make sure the weapon is correctly setup before accessing it
  4291. if ( !weapon.GetEntity()->IsLinked() ) {
  4292. if ( idealWeapon != -1 ) {
  4293. animPrefix = spawnArgs.GetString( va( "def_weapon%d", idealWeapon.Get() ) );
  4294. int ammoInClip = inventory.GetClipAmmoForWeapon( idealWeapon.Get() );
  4295. if ( common->IsMultiplayer() && respawning ) {
  4296. // Do not load ammo into the clip here on MP respawn, as it will be done
  4297. // elsewhere. If we take ammo out here then the player will end up losing
  4298. // a clip of ammo for their initial weapon upon respawn.
  4299. ammoInClip = 0;
  4300. }
  4301. weapon.GetEntity()->GetWeaponDef( animPrefix, ammoInClip );
  4302. assert( weapon.GetEntity()->IsLinked() );
  4303. } else {
  4304. return;
  4305. }
  4306. }
  4307. if ( hiddenWeapon && tipUp && usercmd.buttons & BUTTON_ATTACK ) {
  4308. HideTip();
  4309. }
  4310. if ( g_dragEntity.GetBool() ) {
  4311. StopFiring();
  4312. weapon.GetEntity()->LowerWeapon();
  4313. dragEntity.Update( this );
  4314. } else if ( ActiveGui() ) {
  4315. // gui handling overrides weapon use
  4316. Weapon_GUI();
  4317. } else if ( focusCharacter && ( focusCharacter->health > 0 ) ) {
  4318. Weapon_NPC();
  4319. } else {
  4320. Weapon_Combat();
  4321. }
  4322. if ( hiddenWeapon ) {
  4323. weapon.GetEntity()->LowerWeapon();
  4324. }
  4325. // update weapon state, particles, dlights, etc
  4326. weapon.GetEntity()->PresentWeapon( CanShowWeaponViewmodel() );
  4327. }
  4328. /*
  4329. ===============
  4330. idPlayer::UpdateFlashLight
  4331. ===============
  4332. */
  4333. void idPlayer::UpdateFlashlight() {
  4334. if ( idealWeapon == weapon_flashlight ) {
  4335. // force classic flashlight to go away
  4336. NextWeapon();
  4337. }
  4338. if ( !flashlight.IsValid() ) {
  4339. return;
  4340. }
  4341. if ( !flashlight.GetEntity()->GetOwner() ) {
  4342. return;
  4343. }
  4344. // Don't update the flashlight if dead in MP.
  4345. // Otherwise you can see a floating flashlight worldmodel near player's skeletons.
  4346. if ( common->IsMultiplayer() ) {
  4347. if ( health < 0 ) {
  4348. return;
  4349. }
  4350. }
  4351. // Flashlight has an infinite battery in multiplayer.
  4352. if ( !common->IsMultiplayer() ) {
  4353. if ( flashlight.GetEntity()->lightOn ) {
  4354. if ( flashlight_batteryDrainTimeMS.GetInteger() > 0 ) {
  4355. flashlightBattery -= ( gameLocal.time - gameLocal.previousTime );
  4356. if ( flashlightBattery < 0 ) {
  4357. FlashlightOff();
  4358. flashlightBattery = 0;
  4359. }
  4360. }
  4361. } else {
  4362. if ( flashlightBattery < flashlight_batteryDrainTimeMS.GetInteger() ) {
  4363. flashlightBattery += ( gameLocal.time - gameLocal.previousTime ) * Max( 1, ( flashlight_batteryDrainTimeMS.GetInteger() / flashlight_batteryChargeTimeMS.GetInteger() ) );
  4364. if ( flashlightBattery > flashlight_batteryDrainTimeMS.GetInteger() ) {
  4365. flashlightBattery = flashlight_batteryDrainTimeMS.GetInteger();
  4366. }
  4367. }
  4368. }
  4369. }
  4370. if ( hud ) {
  4371. hud->UpdateFlashlight( this );
  4372. }
  4373. if ( common->IsClient() ) {
  4374. // clients need to wait till the weapon and it's world model entity
  4375. // are present and synchronized ( weapon.worldModel idEntityPtr to idAnimatedEntity )
  4376. if ( !flashlight.GetEntity()->IsWorldModelReady() ) {
  4377. return;
  4378. }
  4379. }
  4380. // always make sure the weapon is correctly setup before accessing it
  4381. if ( !flashlight.GetEntity()->IsLinked() ) {
  4382. flashlight.GetEntity()->GetWeaponDef( "weapon_flashlight_new", 0 );
  4383. flashlight.GetEntity()->SetIsPlayerFlashlight( true );
  4384. // adjust position / orientation of flashlight
  4385. idAnimatedEntity *worldModel = flashlight.GetEntity()->GetWorldModel();
  4386. worldModel->BindToJoint( this, "Chest", true );
  4387. // Don't interpolate the flashlight world model in mp, let it bind like normal.
  4388. worldModel->SetUseClientInterpolation( false );
  4389. assert( flashlight.GetEntity()->IsLinked() );
  4390. }
  4391. // this positions the third person flashlight model! (as seen in the mirror)
  4392. idAnimatedEntity *worldModel = flashlight.GetEntity()->GetWorldModel();
  4393. static const idVec3 fl_pos = idVec3( 3.0f, 9.0f, 2.0f );
  4394. worldModel->GetPhysics()->SetOrigin( fl_pos );
  4395. static float fl_pitch = 0.0f;
  4396. static float fl_yaw = 0.0f;
  4397. static float fl_roll = 0.0f;
  4398. static idAngles ang = ang_zero;
  4399. ang.Set( fl_pitch, fl_yaw, fl_roll );
  4400. worldModel->GetPhysics()->SetAxis( ang.ToMat3() );
  4401. if ( flashlight.GetEntity()->lightOn ) {
  4402. if ( ( flashlightBattery < flashlight_batteryChargeTimeMS.GetInteger() / 2 ) && ( gameLocal.random.RandomFloat() < flashlight_batteryFlickerPercent.GetFloat() ) ) {
  4403. flashlight.GetEntity()->RemoveMuzzleFlashlight();
  4404. } else {
  4405. flashlight.GetEntity()->MuzzleFlashLight();
  4406. }
  4407. }
  4408. flashlight.GetEntity()->PresentWeapon( true );
  4409. if ( gameLocal.world->spawnArgs.GetBool( "no_Weapons" ) || gameLocal.inCinematic || spectating || fl.hidden ) {
  4410. worldModel->Hide();
  4411. } else {
  4412. worldModel->Show();
  4413. }
  4414. }
  4415. /*
  4416. ===============
  4417. idPlayer::FlashlightOn
  4418. ===============
  4419. */
  4420. void idPlayer::FlashlightOn() {
  4421. if ( !flashlight.IsValid() ) {
  4422. return;
  4423. }
  4424. if ( flashlightBattery < idMath::Ftoi( flashlight_minActivatePercent.GetFloat() * flashlight_batteryDrainTimeMS.GetFloat() ) ) {
  4425. return;
  4426. }
  4427. if ( gameLocal.inCinematic ) {
  4428. return;
  4429. }
  4430. if ( flashlight.GetEntity()->lightOn ) {
  4431. return;
  4432. }
  4433. if ( health <= 0 ) {
  4434. return;
  4435. }
  4436. if ( spectating ) {
  4437. return;
  4438. }
  4439. flashlight->FlashlightOn();
  4440. }
  4441. /*
  4442. ===============
  4443. idPlayer::FlashlightOff
  4444. ===============
  4445. */
  4446. void idPlayer::FlashlightOff() {
  4447. if ( !flashlight.IsValid() ) {
  4448. return;
  4449. }
  4450. if ( !flashlight.GetEntity()->lightOn ) {
  4451. return;
  4452. }
  4453. flashlight->FlashlightOff();
  4454. }
  4455. /*
  4456. ===============
  4457. idPlayer::SpectateFreeFly
  4458. ===============
  4459. */
  4460. void idPlayer::SpectateFreeFly( bool force ) {
  4461. idPlayer *player;
  4462. idVec3 newOrig;
  4463. idVec3 spawn_origin;
  4464. idAngles spawn_angles;
  4465. player = gameLocal.GetClientByNum( spectator );
  4466. if ( force || gameLocal.time > lastSpectateChange ) {
  4467. spectator = entityNumber;
  4468. if ( player != NULL && player != this && !player->spectating && !player->IsInTeleport() ) {
  4469. newOrig = player->GetPhysics()->GetOrigin();
  4470. if ( player->physicsObj.IsCrouching() ) {
  4471. newOrig[ 2 ] += pm_crouchviewheight.GetFloat();
  4472. } else {
  4473. newOrig[ 2 ] += pm_normalviewheight.GetFloat();
  4474. }
  4475. newOrig[ 2 ] += SPECTATE_RAISE;
  4476. idBounds b = idBounds( vec3_origin ).Expand( pm_spectatebbox.GetFloat() * 0.5f );
  4477. idVec3 start = player->GetPhysics()->GetOrigin();
  4478. start[2] += pm_spectatebbox.GetFloat() * 0.5f;
  4479. trace_t t;
  4480. // assuming spectate bbox is inside stand or crouch box
  4481. gameLocal.clip.TraceBounds( t, start, newOrig, b, MASK_PLAYERSOLID, player );
  4482. newOrig.Lerp( start, newOrig, t.fraction );
  4483. SetOrigin( newOrig );
  4484. idAngles angle = player->viewAngles;
  4485. angle[ 2 ] = 0;
  4486. SetViewAngles( angle );
  4487. } else {
  4488. SelectInitialSpawnPoint( spawn_origin, spawn_angles );
  4489. spawn_origin[ 2 ] += pm_normalviewheight.GetFloat();
  4490. spawn_origin[ 2 ] += SPECTATE_RAISE;
  4491. SetOrigin( spawn_origin );
  4492. SetViewAngles( spawn_angles );
  4493. // This may happen during GAMESTATE_STARTUP in mp, so we must set the spawnAngles too.
  4494. spawnAngles = spawn_angles;
  4495. if( force == false ) {
  4496. // only do this if they hit the cycle button.
  4497. if ( common->IsServer() ) {
  4498. if( player != NULL ) {
  4499. idBitMsg msg;
  4500. byte msgBuf[MAX_EVENT_PARAM_SIZE];
  4501. msg.InitWrite( msgBuf, sizeof( msgBuf ) );
  4502. msg.WriteFloat( GetPhysics()->GetOrigin()[0] );
  4503. msg.WriteFloat( GetPhysics()->GetOrigin()[1] );
  4504. msg.WriteFloat( GetPhysics()->GetOrigin()[2] );
  4505. msg.WriteFloat( viewAngles[0] );
  4506. msg.WriteFloat( viewAngles[1] );
  4507. msg.WriteFloat( viewAngles[2] );
  4508. ServerSendEvent( idPlayer::EVENT_FORCE_ORIGIN, &msg, false );
  4509. }
  4510. }
  4511. }
  4512. }
  4513. lastSpectateChange = gameLocal.time + 500;
  4514. }
  4515. }
  4516. /*
  4517. ===============
  4518. idPlayer::SpectateCycle
  4519. ===============
  4520. */
  4521. void idPlayer::SpectateCycle() {
  4522. idPlayer *player;
  4523. if ( gameLocal.time > lastSpectateChange ) {
  4524. int latchedSpectator = spectator;
  4525. spectator = gameLocal.GetNextClientNum( spectator );
  4526. player = gameLocal.GetClientByNum( spectator );
  4527. assert( player ); // never call here when the current spectator is wrong
  4528. // ignore other spectators
  4529. while ( latchedSpectator != spectator && player->spectating ) {
  4530. spectator = gameLocal.GetNextClientNum( spectator );
  4531. player = gameLocal.GetClientByNum( spectator );
  4532. }
  4533. lastSpectateChange = gameLocal.time + 500;
  4534. }
  4535. }
  4536. /*
  4537. ===============
  4538. idPlayer::UpdateSpectating
  4539. ===============
  4540. */
  4541. void idPlayer::UpdateSpectating() {
  4542. assert( spectating );
  4543. assert( !common->IsClient() );
  4544. assert( IsHidden() );
  4545. idPlayer *player;
  4546. if ( !common->IsMultiplayer() ) {
  4547. return;
  4548. }
  4549. player = gameLocal.GetClientByNum( spectator );
  4550. if ( !player || ( player->spectating && player != this ) ) {
  4551. SpectateFreeFly( true );
  4552. } else if ( usercmd.buttons & BUTTON_JUMP ) {
  4553. SpectateFreeFly( false );
  4554. } else if ( usercmd.buttons & BUTTON_USE ) {
  4555. SpectateCycle();
  4556. } else if ( usercmd.buttons & BUTTON_ATTACK ) {
  4557. wantSpectate = false;
  4558. }
  4559. }
  4560. /*
  4561. ===============
  4562. idPlayer::HandleSingleGuiCommand
  4563. ===============
  4564. */
  4565. bool idPlayer::HandleSingleGuiCommand( idEntity *entityGui, idLexer *src ) {
  4566. idToken token;
  4567. if ( !src->ReadToken( &token ) ) {
  4568. return false;
  4569. }
  4570. if ( token == ";" ) {
  4571. return false;
  4572. }
  4573. if ( token.Icmp( "addhealth" ) == 0 ) {
  4574. if ( entityGui && health < 100 ) {
  4575. int _health = entityGui->spawnArgs.GetInt( "gui_parm1" );
  4576. int amt = ( _health >= HEALTH_PER_DOSE ) ? HEALTH_PER_DOSE : _health;
  4577. _health -= amt;
  4578. entityGui->spawnArgs.SetInt( "gui_parm1", _health );
  4579. if ( entityGui->GetRenderEntity() && entityGui->GetRenderEntity()->gui[ 0 ] ) {
  4580. entityGui->GetRenderEntity()->gui[ 0 ]->SetStateInt( "gui_parm1", _health );
  4581. }
  4582. health += amt;
  4583. if ( health > 100 ) {
  4584. health = 100;
  4585. }
  4586. }
  4587. return true;
  4588. }
  4589. src->UnreadToken( &token );
  4590. return false;
  4591. }
  4592. /*
  4593. ==============
  4594. idPlayer::PlayAudioLog
  4595. ==============
  4596. */
  4597. void idPlayer::PlayAudioLog( const idSoundShader * shader ) {
  4598. EndVideoDisk();
  4599. if ( name.Length() > 0 ) {
  4600. int ms;
  4601. StartSoundShader( shader, SND_CHANNEL_PDA_AUDIO, 0, false, &ms );
  4602. CancelEvents( &EV_Player_StopAudioLog );
  4603. PostEventMS( &EV_Player_StopAudioLog, ms + 150 );
  4604. }
  4605. }
  4606. /*
  4607. ==============
  4608. idPlayer::EndAudioLog
  4609. ==============
  4610. */
  4611. void idPlayer::EndAudioLog() {
  4612. StopSound( SND_CHANNEL_PDA_AUDIO, false );
  4613. }
  4614. /*
  4615. ==============
  4616. idPlayer::PlayVideoDisk
  4617. ==============
  4618. */
  4619. void idPlayer::PlayVideoDisk( const idDeclVideo * decl ) {
  4620. EndAudioLog();
  4621. pdaVideoMat = decl->GetRoq();
  4622. if ( pdaVideoMat ) {
  4623. int c = pdaVideoMat->GetNumStages();
  4624. for ( int i = 0; i < c; i++ ) {
  4625. const shaderStage_t *stage = pdaVideoMat->GetStage( i );
  4626. if ( stage != NULL && stage->texture.cinematic ) {
  4627. stage->texture.cinematic->ResetTime( Sys_Milliseconds() );
  4628. }
  4629. }
  4630. if ( decl->GetWave() != NULL ) {
  4631. StartSoundShader( decl->GetWave(), SND_CHANNEL_PDA_VIDEO, 0, false, NULL );
  4632. }
  4633. }
  4634. }
  4635. /*
  4636. ==============
  4637. idPlayer::EndVideoDisk
  4638. ==============
  4639. */
  4640. void idPlayer::EndVideoDisk() {
  4641. pdaVideoMat = NULL;
  4642. StopSound( SND_CHANNEL_PDA_VIDEO, false );
  4643. }
  4644. /*
  4645. ==============
  4646. idPlayer::Collide
  4647. ==============
  4648. */
  4649. bool idPlayer::Collide( const trace_t &collision, const idVec3 &velocity ) {
  4650. idEntity *other;
  4651. if ( common->IsClient() && spectating == false ) {
  4652. return false;
  4653. }
  4654. other = gameLocal.entities[ collision.c.entityNum ];
  4655. if ( other ) {
  4656. other->Signal( SIG_TOUCH );
  4657. if ( !spectating ) {
  4658. if ( other->RespondsTo( EV_Touch ) ) {
  4659. other->ProcessEvent( &EV_Touch, this, &collision );
  4660. }
  4661. } else {
  4662. if ( other->RespondsTo( EV_SpectatorTouch ) ) {
  4663. other->ProcessEvent( &EV_SpectatorTouch, this, &collision );
  4664. }
  4665. }
  4666. }
  4667. return false;
  4668. }
  4669. /*
  4670. ================
  4671. idPlayer::UpdateLocation
  4672. Searches nearby locations
  4673. ================
  4674. */
  4675. void idPlayer::UpdateLocation() {
  4676. if ( hud ) {
  4677. hud->UpdateLocation( this );
  4678. }
  4679. }
  4680. /*
  4681. ================
  4682. idPlayer::ClearFocus
  4683. Clears the focus cursor
  4684. ================
  4685. */
  4686. void idPlayer::ClearFocus() {
  4687. focusCharacter = NULL;
  4688. focusGUIent = NULL;
  4689. focusUI = NULL;
  4690. focusVehicle = NULL;
  4691. talkCursor = 0;
  4692. }
  4693. /*
  4694. ================
  4695. idPlayer::UpdateFocus
  4696. Searches nearby entities for interactive guis, possibly making one of them
  4697. the focus and sending it a mouse move event
  4698. ================
  4699. */
  4700. void idPlayer::UpdateFocus() {
  4701. idClipModel *clipModelList[ MAX_GENTITIES ];
  4702. idClipModel *clip;
  4703. int listedClipModels;
  4704. idEntity *oldFocus;
  4705. idEntity *ent;
  4706. idUserInterface *oldUI;
  4707. idAI *oldChar;
  4708. int oldTalkCursor;
  4709. idAFEntity_Vehicle *oldVehicle;
  4710. int i, j;
  4711. idVec3 start, end;
  4712. bool allowFocus;
  4713. const char *command;
  4714. trace_t trace;
  4715. guiPoint_t pt;
  4716. const idKeyValue *kv;
  4717. sysEvent_t ev;
  4718. idUserInterface *ui;
  4719. if ( gameLocal.inCinematic ) {
  4720. return;
  4721. }
  4722. // only update the focus character when attack button isn't pressed so players
  4723. // can still chainsaw NPC's
  4724. if ( common->IsMultiplayer() || ( !focusCharacter && ( usercmd.buttons & BUTTON_ATTACK ) ) ) {
  4725. allowFocus = false;
  4726. } else {
  4727. allowFocus = true;
  4728. }
  4729. oldFocus = focusGUIent;
  4730. oldUI = focusUI;
  4731. oldChar = focusCharacter;
  4732. oldTalkCursor = talkCursor;
  4733. oldVehicle = focusVehicle;
  4734. if ( focusTime <= gameLocal.time ) {
  4735. ClearFocus();
  4736. }
  4737. // don't let spectators interact with GUIs
  4738. if ( spectating ) {
  4739. return;
  4740. }
  4741. start = GetEyePosition();
  4742. end = start + firstPersonViewAxis[0] * 80.0f;
  4743. // player identification -> names to the hud
  4744. if ( common->IsMultiplayer() && IsLocallyControlled() ) {
  4745. idVec3 end = start + viewAngles.ToForward() * 768.0f;
  4746. gameLocal.clip.TracePoint( trace, start, end, MASK_SHOT_BOUNDINGBOX, this );
  4747. int iclient = -1;
  4748. if ( ( trace.fraction < 1.0f ) && ( trace.c.entityNum < MAX_CLIENTS ) ) {
  4749. iclient = trace.c.entityNum;
  4750. }
  4751. if ( MPAim != iclient ) {
  4752. lastMPAim = MPAim;
  4753. MPAim = iclient;
  4754. lastMPAimTime = gameLocal.realClientTime;
  4755. }
  4756. }
  4757. idBounds bounds( start );
  4758. bounds.AddPoint( end );
  4759. listedClipModels = gameLocal.clip.ClipModelsTouchingBounds( bounds, -1, clipModelList, MAX_GENTITIES );
  4760. // no pretense at sorting here, just assume that there will only be one active
  4761. // gui within range along the trace
  4762. for ( i = 0; i < listedClipModels; i++ ) {
  4763. clip = clipModelList[ i ];
  4764. ent = clip->GetEntity();
  4765. if ( ent->IsHidden() ) {
  4766. continue;
  4767. }
  4768. if ( allowFocus ) {
  4769. if ( ent->IsType( idAFAttachment::Type ) ) {
  4770. idEntity *body = static_cast<idAFAttachment *>( ent )->GetBody();
  4771. if ( body != NULL && body->IsType( idAI::Type ) && ( static_cast<idAI *>( body )->GetTalkState() >= TALK_OK ) ) {
  4772. gameLocal.clip.TracePoint( trace, start, end, MASK_SHOT_RENDERMODEL, this );
  4773. if ( ( trace.fraction < 1.0f ) && ( trace.c.entityNum == ent->entityNumber ) ) {
  4774. ClearFocus();
  4775. focusCharacter = static_cast<idAI *>( body );
  4776. talkCursor = 1;
  4777. focusTime = gameLocal.time + FOCUS_TIME;
  4778. break;
  4779. }
  4780. }
  4781. continue;
  4782. }
  4783. if ( ent->IsType( idAI::Type ) ) {
  4784. if ( static_cast<idAI *>( ent )->GetTalkState() >= TALK_OK ) {
  4785. gameLocal.clip.TracePoint( trace, start, end, MASK_SHOT_RENDERMODEL, this );
  4786. if ( ( trace.fraction < 1.0f ) && ( trace.c.entityNum == ent->entityNumber ) ) {
  4787. ClearFocus();
  4788. focusCharacter = static_cast<idAI *>( ent );
  4789. talkCursor = 1;
  4790. focusTime = gameLocal.time + FOCUS_TIME;
  4791. break;
  4792. }
  4793. }
  4794. continue;
  4795. }
  4796. if ( ent->IsType( idAFEntity_Vehicle::Type ) ) {
  4797. gameLocal.clip.TracePoint( trace, start, end, MASK_SHOT_RENDERMODEL, this );
  4798. if ( ( trace.fraction < 1.0f ) && ( trace.c.entityNum == ent->entityNumber ) ) {
  4799. ClearFocus();
  4800. focusVehicle = static_cast<idAFEntity_Vehicle *>( ent );
  4801. focusTime = gameLocal.time + FOCUS_TIME;
  4802. break;
  4803. }
  4804. continue;
  4805. }
  4806. }
  4807. if ( !ent->GetRenderEntity() || !ent->GetRenderEntity()->gui[ 0 ] || !ent->GetRenderEntity()->gui[ 0 ]->IsInteractive() ) {
  4808. continue;
  4809. }
  4810. if ( ent->spawnArgs.GetBool( "inv_item" ) ) {
  4811. // don't allow guis on pickup items focus
  4812. continue;
  4813. }
  4814. pt = gameRenderWorld->GuiTrace( ent->GetModelDefHandle(), start, end );
  4815. if ( pt.x != -1 ) {
  4816. // we have a hit
  4817. renderEntity_t *focusGUIrenderEntity = ent->GetRenderEntity();
  4818. if ( !focusGUIrenderEntity ) {
  4819. continue;
  4820. }
  4821. if ( pt.guiId == 1 ) {
  4822. ui = focusGUIrenderEntity->gui[ 0 ];
  4823. } else if ( pt.guiId == 2 ) {
  4824. ui = focusGUIrenderEntity->gui[ 1 ];
  4825. } else {
  4826. ui = focusGUIrenderEntity->gui[ 2 ];
  4827. }
  4828. if ( ui == NULL ) {
  4829. continue;
  4830. }
  4831. ClearFocus();
  4832. focusGUIent = ent;
  4833. focusUI = ui;
  4834. if ( oldFocus != ent ) {
  4835. // new activation
  4836. // going to see if we have anything in inventory a gui might be interested in
  4837. // need to enumerate inventory items
  4838. focusUI->SetStateInt( "inv_count", inventory.items.Num() );
  4839. for ( j = 0; j < inventory.items.Num(); j++ ) {
  4840. idDict *item = inventory.items[ j ];
  4841. const char *iname = item->GetString( "inv_name" );
  4842. const char *iicon = item->GetString( "inv_icon" );
  4843. const char *itext = item->GetString( "inv_text" );
  4844. focusUI->SetStateString( va( "inv_name_%i", j), iname );
  4845. focusUI->SetStateString( va( "inv_icon_%i", j), iicon );
  4846. focusUI->SetStateString( va( "inv_text_%i", j), itext );
  4847. kv = item->MatchPrefix("inv_id", NULL);
  4848. if ( kv ) {
  4849. focusUI->SetStateString( va( "inv_id_%i", j ), kv->GetValue() );
  4850. }
  4851. focusUI->SetStateInt( iname, 1 );
  4852. }
  4853. for( j = 0; j < inventory.pdaSecurity.Num(); j++ ) {
  4854. const char *p = inventory.pdaSecurity[ j ];
  4855. if ( p && *p ) {
  4856. focusUI->SetStateInt( p, 1 );
  4857. }
  4858. }
  4859. int powerCellCount = 0;
  4860. for ( j = 0; j < inventory.items.Num(); j++ ) {
  4861. idDict *item = inventory.items[ j ];
  4862. if(item->GetInt("inv_powercell")) {
  4863. powerCellCount++;
  4864. }
  4865. }
  4866. focusUI->SetStateInt( "powercell_count", powerCellCount );
  4867. int staminapercentage = ( int )( 100.0f * stamina / pm_stamina.GetFloat() );
  4868. focusUI->SetStateString( "player_health", va("%i", health ) );
  4869. focusUI->SetStateString( "player_stamina", va( "%i%%", staminapercentage ) );
  4870. focusUI->SetStateString( "player_armor", va( "%i%%", inventory.armor ) );
  4871. kv = focusGUIent->spawnArgs.MatchPrefix( "gui_parm", NULL );
  4872. while ( kv ) {
  4873. focusUI->SetStateString( kv->GetKey(), kv->GetValue() );
  4874. kv = focusGUIent->spawnArgs.MatchPrefix( "gui_parm", kv );
  4875. }
  4876. }
  4877. // clamp the mouse to the corner
  4878. ev = sys->GenerateMouseMoveEvent( -2000, -2000 );
  4879. command = focusUI->HandleEvent( &ev, gameLocal.time );
  4880. HandleGuiCommands( focusGUIent, command );
  4881. // move to an absolute position
  4882. ev = sys->GenerateMouseMoveEvent( pt.x * SCREEN_WIDTH, pt.y * SCREEN_HEIGHT );
  4883. command = focusUI->HandleEvent( &ev, gameLocal.time );
  4884. HandleGuiCommands( focusGUIent, command );
  4885. focusTime = gameLocal.time + FOCUS_GUI_TIME;
  4886. break;
  4887. }
  4888. }
  4889. if ( focusGUIent && focusUI ) {
  4890. if ( !oldFocus || oldFocus != focusGUIent ) {
  4891. command = focusUI->Activate( true, gameLocal.time );
  4892. HandleGuiCommands( focusGUIent, command );
  4893. StartSound( "snd_guienter", SND_CHANNEL_ANY, 0, false, NULL );
  4894. // HideTip();
  4895. // HideObjective();
  4896. }
  4897. } else if ( oldFocus && oldUI ) {
  4898. command = oldUI->Activate( false, gameLocal.time );
  4899. HandleGuiCommands( oldFocus, command );
  4900. StartSound( "snd_guiexit", SND_CHANNEL_ANY, 0, false, NULL );
  4901. }
  4902. if ( hud ) {
  4903. hud->SetCursorState( this, CURSOR_TALK, talkCursor );
  4904. }
  4905. if ( oldChar != focusCharacter && hud ) {
  4906. if ( focusCharacter ) {
  4907. hud->SetCursorText( "#str_02036", focusCharacter->spawnArgs.GetString( "npc_name", "Joe" ) );
  4908. hud->UpdateCursorState();
  4909. } else {
  4910. hud->SetCursorText( "", "" );
  4911. hud->UpdateCursorState();
  4912. }
  4913. }
  4914. }
  4915. /*
  4916. =================
  4917. idPlayer::CrashLand
  4918. Check for hard landings that generate sound events
  4919. =================
  4920. */
  4921. void idPlayer::CrashLand( const idVec3 &oldOrigin, const idVec3 &oldVelocity ) {
  4922. idVec3 origin, velocity;
  4923. idVec3 gravityVector, gravityNormal;
  4924. float delta;
  4925. float hardDelta, fatalDelta, softDelta;
  4926. float dist;
  4927. float vel, acc;
  4928. float t;
  4929. float a, b, c, den;
  4930. waterLevel_t waterLevel;
  4931. bool noDamage;
  4932. AI_SOFTLANDING = false;
  4933. AI_HARDLANDING = false;
  4934. // if the player is not on the ground
  4935. if ( !physicsObj.HasGroundContacts() ) {
  4936. return;
  4937. }
  4938. gravityNormal = physicsObj.GetGravityNormal();
  4939. // if the player wasn't going down
  4940. if ( ( oldVelocity * -gravityNormal ) >= 0.0f ) {
  4941. return;
  4942. }
  4943. waterLevel = physicsObj.GetWaterLevel();
  4944. // never take falling damage if completely underwater
  4945. if ( waterLevel == WATERLEVEL_HEAD ) {
  4946. return;
  4947. }
  4948. // no falling damage if touching a nodamage surface
  4949. noDamage = false;
  4950. for ( int i = 0; i < physicsObj.GetNumContacts(); i++ ) {
  4951. const contactInfo_t &contact = physicsObj.GetContact( i );
  4952. if ( contact.material->GetSurfaceFlags() & SURF_NODAMAGE ) {
  4953. noDamage = true;
  4954. StartSound( "snd_land_hard", SND_CHANNEL_ANY, 0, false, NULL );
  4955. break;
  4956. }
  4957. }
  4958. origin = GetPhysics()->GetOrigin();
  4959. gravityVector = physicsObj.GetGravity();
  4960. // calculate the exact velocity on landing
  4961. dist = ( origin - oldOrigin ) * -gravityNormal;
  4962. vel = oldVelocity * -gravityNormal;
  4963. acc = -gravityVector.Length();
  4964. a = acc / 2.0f;
  4965. b = vel;
  4966. c = -dist;
  4967. den = b * b - 4.0f * a * c;
  4968. if ( den < 0 ) {
  4969. return;
  4970. }
  4971. t = ( -b - idMath::Sqrt( den ) ) / ( 2.0f * a );
  4972. delta = vel + t * acc;
  4973. delta = delta * delta * 0.0001;
  4974. // reduce falling damage if there is standing water
  4975. if ( waterLevel == WATERLEVEL_WAIST ) {
  4976. delta *= 0.25f;
  4977. }
  4978. if ( waterLevel == WATERLEVEL_FEET ) {
  4979. delta *= 0.5f;
  4980. }
  4981. if ( delta < 1.0f ) {
  4982. return;
  4983. }
  4984. // allow falling a bit further for multiplayer
  4985. if ( common->IsMultiplayer() ) {
  4986. fatalDelta = 75.0f;
  4987. hardDelta = 50.0f;
  4988. softDelta = 45.0f;
  4989. } else {
  4990. fatalDelta = 65.0f;
  4991. hardDelta = 45.0f;
  4992. softDelta = 30.0f;
  4993. }
  4994. if ( delta > fatalDelta ) {
  4995. AI_HARDLANDING = true;
  4996. landChange = -32;
  4997. landTime = gameLocal.time;
  4998. if ( !noDamage ) {
  4999. pain_debounce_time = gameLocal.time + pain_delay + 1; // ignore pain since we'll play our landing anim
  5000. Damage( NULL, NULL, idVec3( 0, 0, -1 ), "damage_fatalfall", 1.0f, 0 );
  5001. }
  5002. } else if ( delta > hardDelta ) {
  5003. AI_HARDLANDING = true;
  5004. landChange = -24;
  5005. landTime = gameLocal.time;
  5006. if ( !noDamage ) {
  5007. pain_debounce_time = gameLocal.time + pain_delay + 1; // ignore pain since we'll play our landing anim
  5008. Damage( NULL, NULL, idVec3( 0, 0, -1 ), "damage_hardfall", 1.0f, 0 );
  5009. }
  5010. } else if ( delta > softDelta ) {
  5011. AI_HARDLANDING = true;
  5012. landChange = -16;
  5013. landTime = gameLocal.time;
  5014. if ( !noDamage ) {
  5015. pain_debounce_time = gameLocal.time + pain_delay + 1; // ignore pain since we'll play our landing anim
  5016. Damage( NULL, NULL, idVec3( 0, 0, -1 ), "damage_softfall", 1.0f, 0 );
  5017. }
  5018. } else if ( delta > 7 ) {
  5019. AI_SOFTLANDING = true;
  5020. landChange = -8;
  5021. landTime = gameLocal.time;
  5022. } else if ( delta > 3 ) {
  5023. // just walk on
  5024. }
  5025. }
  5026. /*
  5027. ===============
  5028. idPlayer::BobCycle
  5029. ===============
  5030. */
  5031. void idPlayer::BobCycle( const idVec3 &pushVelocity ) {
  5032. float bobmove;
  5033. int old, deltaTime;
  5034. idVec3 vel, gravityDir, velocity;
  5035. idMat3 viewaxis;
  5036. float bob;
  5037. float delta;
  5038. float speed;
  5039. float f;
  5040. //
  5041. // calculate speed and cycle to be used for
  5042. // all cyclic walking effects
  5043. //
  5044. velocity = physicsObj.GetLinearVelocity() - pushVelocity;
  5045. gravityDir = physicsObj.GetGravityNormal();
  5046. vel = velocity - ( velocity * gravityDir ) * gravityDir;
  5047. xyspeed = vel.LengthFast();
  5048. // do not evaluate the bob for other clients
  5049. // when doing a spectate follow, don't do any weapon bobbing
  5050. if ( common->IsClient() && !IsLocallyControlled() ) {
  5051. viewBobAngles.Zero();
  5052. viewBob.Zero();
  5053. return;
  5054. }
  5055. if ( !physicsObj.HasGroundContacts() || influenceActive == INFLUENCE_LEVEL2 || ( common->IsMultiplayer() && spectating ) ) {
  5056. // airborne
  5057. bobCycle = 0;
  5058. bobFoot = 0;
  5059. bobfracsin = 0;
  5060. } else if ( ( !usercmd.forwardmove && !usercmd.rightmove ) || ( xyspeed <= MIN_BOB_SPEED ) ) {
  5061. // start at beginning of cycle again
  5062. bobCycle = 0;
  5063. bobFoot = 0;
  5064. bobfracsin = 0;
  5065. } else {
  5066. if ( physicsObj.IsCrouching() ) {
  5067. bobmove = pm_crouchbob.GetFloat();
  5068. // ducked characters never play footsteps
  5069. } else {
  5070. // vary the bobbing based on the speed of the player
  5071. bobmove = pm_walkbob.GetFloat() * ( 1.0f - bobFrac ) + pm_runbob.GetFloat() * bobFrac;
  5072. }
  5073. // check for footstep / splash sounds
  5074. old = bobCycle;
  5075. bobCycle = (int)( old + bobmove * ( gameLocal.time - gameLocal.previousTime ) ) & 255;
  5076. bobFoot = ( bobCycle & 128 ) >> 7;
  5077. bobfracsin = idMath::Fabs( sin( ( bobCycle & 127 ) / 127.0 * idMath::PI ) );
  5078. }
  5079. // calculate angles for view bobbing
  5080. viewBobAngles.Zero();
  5081. viewaxis = viewAngles.ToMat3() * physicsObj.GetGravityAxis();
  5082. // add angles based on velocity
  5083. delta = velocity * viewaxis[0];
  5084. viewBobAngles.pitch += delta * pm_runpitch.GetFloat();
  5085. delta = velocity * viewaxis[1];
  5086. viewBobAngles.roll -= delta * pm_runroll.GetFloat();
  5087. // add angles based on bob
  5088. // make sure the bob is visible even at low speeds
  5089. speed = xyspeed > 200 ? xyspeed : 200;
  5090. delta = bobfracsin * pm_bobpitch.GetFloat() * speed;
  5091. if ( physicsObj.IsCrouching() ) {
  5092. delta *= 3; // crouching
  5093. }
  5094. viewBobAngles.pitch += delta;
  5095. delta = bobfracsin * pm_bobroll.GetFloat() * speed;
  5096. if ( physicsObj.IsCrouching() ) {
  5097. delta *= 3; // crouching accentuates roll
  5098. }
  5099. if ( bobFoot & 1 ) {
  5100. delta = -delta;
  5101. }
  5102. viewBobAngles.roll += delta;
  5103. // calculate position for view bobbing
  5104. viewBob.Zero();
  5105. if ( physicsObj.HasSteppedUp() ) {
  5106. // check for stepping up before a previous step is completed
  5107. deltaTime = gameLocal.time - stepUpTime;
  5108. if ( deltaTime < STEPUP_TIME ) {
  5109. stepUpDelta = stepUpDelta * ( STEPUP_TIME - deltaTime ) / STEPUP_TIME + physicsObj.GetStepUp();
  5110. } else {
  5111. stepUpDelta = physicsObj.GetStepUp();
  5112. }
  5113. if ( stepUpDelta > 2.0f * pm_stepsize.GetFloat() ) {
  5114. stepUpDelta = 2.0f * pm_stepsize.GetFloat();
  5115. }
  5116. stepUpTime = gameLocal.time;
  5117. }
  5118. idVec3 gravity = physicsObj.GetGravityNormal();
  5119. // if the player stepped up recently
  5120. deltaTime = gameLocal.time - stepUpTime;
  5121. if ( deltaTime < STEPUP_TIME ) {
  5122. viewBob += gravity * ( stepUpDelta * ( STEPUP_TIME - deltaTime ) / STEPUP_TIME );
  5123. }
  5124. // add bob height after any movement smoothing
  5125. bob = bobfracsin * xyspeed * pm_bobup.GetFloat();
  5126. if ( bob > 6 ) {
  5127. bob = 6;
  5128. }
  5129. viewBob[2] += bob;
  5130. // add fall height
  5131. delta = gameLocal.time - landTime;
  5132. if ( delta < LAND_DEFLECT_TIME ) {
  5133. f = delta / LAND_DEFLECT_TIME;
  5134. viewBob -= gravity * ( landChange * f );
  5135. } else if ( delta < LAND_DEFLECT_TIME + LAND_RETURN_TIME ) {
  5136. delta -= LAND_DEFLECT_TIME;
  5137. f = 1.0 - ( delta / LAND_RETURN_TIME );
  5138. viewBob -= gravity * ( landChange * f );
  5139. }
  5140. }
  5141. /*
  5142. ================
  5143. idPlayer::UpdateDeltaViewAngles
  5144. ================
  5145. */
  5146. void idPlayer::UpdateDeltaViewAngles( const idAngles &angles ) {
  5147. // set the delta angle
  5148. idAngles delta;
  5149. for( int i = 0; i < 3; i++ ) {
  5150. delta[ i ] = angles[ i ] - SHORT2ANGLE( usercmd.angles[ i ] );
  5151. }
  5152. SetDeltaViewAngles( delta );
  5153. }
  5154. /*
  5155. ================
  5156. idPlayer::SetViewAngles
  5157. ================
  5158. */
  5159. void idPlayer::SetViewAngles( const idAngles &angles ) {
  5160. UpdateDeltaViewAngles( angles );
  5161. viewAngles = angles;
  5162. }
  5163. /*
  5164. ================
  5165. idPlayer::UpdateViewAngles
  5166. ================
  5167. */
  5168. void idPlayer::UpdateViewAngles() {
  5169. int i;
  5170. idAngles delta;
  5171. if ( !noclip && ( gameLocal.inCinematic || privateCameraView || gameLocal.GetCamera() || influenceActive == INFLUENCE_LEVEL2 || objectiveSystemOpen ) ) {
  5172. // no view changes at all, but we still want to update the deltas or else when
  5173. // we get out of this mode, our view will snap to a kind of random angle
  5174. UpdateDeltaViewAngles( viewAngles );
  5175. return;
  5176. }
  5177. // if dead
  5178. if ( health <= 0 ) {
  5179. if ( pm_thirdPersonDeath.GetBool() ) {
  5180. viewAngles.roll = 0.0f;
  5181. viewAngles.pitch = 30.0f;
  5182. } else {
  5183. viewAngles.roll = 40.0f;
  5184. viewAngles.pitch = -15.0f;
  5185. }
  5186. return;
  5187. }
  5188. //
  5189. // circularly clamp the angles with deltas
  5190. for ( i = 0; i < 3; i++ ) {
  5191. cmdAngles[i] = SHORT2ANGLE( usercmd.angles[i] );
  5192. if ( influenceActive == INFLUENCE_LEVEL3 ) {
  5193. viewAngles[i] += idMath::ClampFloat( -1.0f, 1.0f, idMath::AngleDelta( idMath::AngleNormalize180( SHORT2ANGLE( usercmd.angles[i]) + deltaViewAngles[i] ) , viewAngles[i] ) );
  5194. } else {
  5195. viewAngles[i] = idMath::AngleNormalize180( SHORT2ANGLE( usercmd.angles[i]) + deltaViewAngles[i] );
  5196. }
  5197. }
  5198. if ( !centerView.IsDone( gameLocal.time ) ) {
  5199. viewAngles.pitch = centerView.GetCurrentValue(gameLocal.time);
  5200. }
  5201. // clamp the pitch
  5202. if ( noclip ) {
  5203. if ( viewAngles.pitch > 89.0f ) {
  5204. // don't let the player look down more than 89 degrees while noclipping
  5205. viewAngles.pitch = 89.0f;
  5206. } else if ( viewAngles.pitch < -89.0f ) {
  5207. // don't let the player look up more than 89 degrees while noclipping
  5208. viewAngles.pitch = -89.0f;
  5209. }
  5210. } else if ( mountedObject ) {
  5211. int yaw_min, yaw_max, varc;
  5212. mountedObject->GetAngleRestrictions( yaw_min, yaw_max, varc );
  5213. if ( yaw_min < yaw_max ) {
  5214. viewAngles.yaw = idMath::ClampFloat( yaw_min, yaw_max, viewAngles.yaw );
  5215. } else {
  5216. if ( viewAngles.yaw < 0 ) {
  5217. viewAngles.yaw = idMath::ClampFloat( -180.f, yaw_max, viewAngles.yaw );
  5218. } else {
  5219. viewAngles.yaw = idMath::ClampFloat( yaw_min, 180.f, viewAngles.yaw );
  5220. }
  5221. }
  5222. viewAngles.pitch = idMath::ClampFloat( -varc, varc, viewAngles.pitch );
  5223. } else {
  5224. // don't let the player look up or down more than 90 degrees normally
  5225. const float restrict = 1.0f;
  5226. viewAngles.pitch = std::min( viewAngles.pitch, pm_maxviewpitch.GetFloat() * restrict );
  5227. viewAngles.pitch = std::max( viewAngles.pitch, pm_minviewpitch.GetFloat() * restrict );
  5228. }
  5229. UpdateDeltaViewAngles( viewAngles );
  5230. // orient the model towards the direction we're looking
  5231. SetAngles( idAngles( 0, viewAngles.yaw, 0 ) );
  5232. // save in the log for analyzing weapon angle offsets
  5233. loggedViewAngles[ gameLocal.framenum & (NUM_LOGGED_VIEW_ANGLES-1) ] = viewAngles;
  5234. }
  5235. /*
  5236. ==============
  5237. idPlayer::AdjustHeartRate
  5238. Player heartrate works as follows
  5239. DEF_HEARTRATE is resting heartrate
  5240. Taking damage when health is above 75 adjusts heart rate by 1 beat per second
  5241. Taking damage when health is below 75 adjusts heart rate by 5 beats per second
  5242. Maximum heartrate from damage is MAX_HEARTRATE
  5243. Firing a weapon adds 1 beat per second up to a maximum of COMBAT_HEARTRATE
  5244. Being at less than 25% stamina adds 5 beats per second up to ZEROSTAMINA_HEARTRATE
  5245. All heartrates are target rates.. the heart rate will start falling as soon as there have been no adjustments for 5 seconds
  5246. Once it starts falling it always tries to get to DEF_HEARTRATE
  5247. The exception to the above rule is upon death at which point the rate is set to DYING_HEARTRATE and starts falling
  5248. immediately to zero
  5249. Heart rate volumes go from zero ( -40 db for DEF_HEARTRATE to 5 db for MAX_HEARTRATE ) the volume is
  5250. scaled linearly based on the actual rate
  5251. Exception to the above rule is once the player is dead, the dying heart rate starts at either the current volume if
  5252. it is audible or -10db and scales to 8db on the last few beats
  5253. ==============
  5254. */
  5255. void idPlayer::AdjustHeartRate( int target, float timeInSecs, float delay, bool force ) {
  5256. if ( heartInfo.GetEndValue() == target ) {
  5257. return;
  5258. }
  5259. if ( AI_DEAD && !force ) {
  5260. return;
  5261. }
  5262. lastHeartAdjust = gameLocal.time;
  5263. heartInfo.Init( gameLocal.time + delay * 1000, timeInSecs * 1000, heartRate, target );
  5264. }
  5265. /*
  5266. ==============
  5267. idPlayer::GetBaseHeartRate
  5268. ==============
  5269. */
  5270. int idPlayer::GetBaseHeartRate() {
  5271. int base = idMath::Ftoi( ( BASE_HEARTRATE + LOWHEALTH_HEARTRATE_ADJ ) - ( (float)health / 100.0f ) * LOWHEALTH_HEARTRATE_ADJ );
  5272. int rate = idMath::Ftoi( base + ( ZEROSTAMINA_HEARTRATE - base ) * ( 1.0f - stamina / pm_stamina.GetFloat() ) );
  5273. int diff = ( lastDmgTime ) ? gameLocal.time - lastDmgTime : 99999;
  5274. rate += ( diff < 5000 ) ? ( diff < 2500 ) ? ( diff < 1000 ) ? 15 : 10 : 5 : 0;
  5275. return rate;
  5276. }
  5277. /*
  5278. ==============
  5279. idPlayer::SetCurrentHeartRate
  5280. ==============
  5281. */
  5282. void idPlayer::SetCurrentHeartRate() {
  5283. int base = idMath::Ftoi( ( BASE_HEARTRATE + LOWHEALTH_HEARTRATE_ADJ ) - ( (float) health / 100.0f ) * LOWHEALTH_HEARTRATE_ADJ );
  5284. if ( PowerUpActive( ADRENALINE )) {
  5285. heartRate = 135;
  5286. } else {
  5287. heartRate = idMath::Ftoi( heartInfo.GetCurrentValue( gameLocal.time ) );
  5288. int currentRate = GetBaseHeartRate();
  5289. if ( health >= 0 && gameLocal.time > lastHeartAdjust + 2500 ) {
  5290. AdjustHeartRate( currentRate, 2.5f, 0.0f, false );
  5291. }
  5292. }
  5293. int bps = idMath::Ftoi( 60.0f / heartRate * 1000.0f );
  5294. if ( gameLocal.time - lastHeartBeat > bps ) {
  5295. int dmgVol = DMG_VOLUME;
  5296. int deathVol = DEATH_VOLUME;
  5297. int zeroVol = ZERO_VOLUME;
  5298. float pct = 0.0;
  5299. if ( heartRate > BASE_HEARTRATE && health > 0 ) {
  5300. pct = (float)(heartRate - base) / (MAX_HEARTRATE - base);
  5301. pct *= ((float)dmgVol - (float)zeroVol);
  5302. } else if ( health <= 0 ) {
  5303. pct = (float)(heartRate - DYING_HEARTRATE) / (BASE_HEARTRATE - DYING_HEARTRATE);
  5304. if ( pct > 1.0f ) {
  5305. pct = 1.0f;
  5306. } else if (pct < 0.0f) {
  5307. pct = 0.0f;
  5308. }
  5309. pct *= ((float)deathVol - (float)zeroVol);
  5310. }
  5311. pct += (float)zeroVol;
  5312. if ( pct != zeroVol ) {
  5313. StartSound( "snd_heartbeat", SND_CHANNEL_HEART, SSF_PRIVATE_SOUND, false, NULL );
  5314. // modify just this channel to a custom volume
  5315. soundShaderParms_t parms;
  5316. memset( &parms, 0, sizeof( parms ) );
  5317. parms.volume = pct;
  5318. refSound.referenceSound->ModifySound( SND_CHANNEL_HEART, &parms );
  5319. }
  5320. lastHeartBeat = gameLocal.time;
  5321. }
  5322. }
  5323. /*
  5324. ==============
  5325. idPlayer::UpdateAir
  5326. ==============
  5327. */
  5328. void idPlayer::UpdateAir() {
  5329. if ( health <= 0 ) {
  5330. return;
  5331. }
  5332. // see if the player is connected to the info_vacuum
  5333. bool newAirless = false;
  5334. if ( gameLocal.vacuumAreaNum != -1 ) {
  5335. int num = GetNumPVSAreas();
  5336. if ( num > 0 ) {
  5337. int areaNum;
  5338. // if the player box spans multiple areas, get the area from the origin point instead,
  5339. // otherwise a rotating player box may poke into an outside area
  5340. if ( num == 1 ) {
  5341. const int *pvsAreas = GetPVSAreas();
  5342. areaNum = pvsAreas[0];
  5343. } else {
  5344. areaNum = gameRenderWorld->PointInArea( this->GetPhysics()->GetOrigin() );
  5345. }
  5346. newAirless = gameRenderWorld->AreasAreConnected( gameLocal.vacuumAreaNum, areaNum, PS_BLOCK_AIR );
  5347. }
  5348. }
  5349. if ( PowerUpActive( ENVIROTIME ) ) {
  5350. newAirless = false;
  5351. }
  5352. if ( newAirless ) {
  5353. if ( !airless ) {
  5354. StartSound( "snd_decompress", SND_CHANNEL_ANY, SSF_GLOBAL, false, NULL );
  5355. StartSound( "snd_noAir", SND_CHANNEL_BODY2, 0, false, NULL );
  5356. }
  5357. airMsec -= ( gameLocal.time - gameLocal.previousTime );
  5358. if ( airMsec < 0 ) {
  5359. airMsec = 0;
  5360. // check for damage
  5361. const idDict *damageDef = gameLocal.FindEntityDefDict( "damage_noair", false );
  5362. int dmgTiming = 1000 * ((damageDef) ? damageDef->GetFloat( "delay", "3.0" ) : 3.0f );
  5363. if ( gameLocal.time > lastAirDamage + dmgTiming ) {
  5364. Damage( NULL, NULL, vec3_origin, "damage_noair", 1.0f, 0 );
  5365. lastAirDamage = gameLocal.time;
  5366. }
  5367. }
  5368. } else {
  5369. if ( airless ) {
  5370. StartSound( "snd_recompress", SND_CHANNEL_ANY, SSF_GLOBAL, false, NULL );
  5371. StopSound( SND_CHANNEL_BODY2, false );
  5372. }
  5373. airMsec += ( gameLocal.time - gameLocal.previousTime ); // regain twice as fast as lose
  5374. if ( airMsec > pm_airMsec.GetInteger() ) {
  5375. airMsec = pm_airMsec.GetInteger();
  5376. }
  5377. }
  5378. airless = newAirless;
  5379. if ( hud ) {
  5380. hud->UpdateOxygen( airless, 100 * airMsec / pm_airMsec.GetInteger() );
  5381. }
  5382. }
  5383. void idPlayer::UpdatePowerupHud() {
  5384. if ( health <= 0 ) {
  5385. return;
  5386. }
  5387. if( lastHudPowerup != hudPowerup ) {
  5388. if( hudPowerup == -1 ) {
  5389. //The powerup hud should be turned off
  5390. // TODO_SPARTY: powerups??
  5391. //if ( hud ) {
  5392. // hud->HandleNamedEvent( "noPowerup" );
  5393. //}
  5394. } else {
  5395. //Turn the pwoerup hud on
  5396. // TODO_SPARTY: powerups??
  5397. //if ( hud ) {
  5398. // hud->HandleNamedEvent( "Powerup" );
  5399. //}
  5400. }
  5401. lastHudPowerup = hudPowerup;
  5402. }
  5403. if ( hudPowerup != -1 && hudPowerup < MAX_POWERUPS ) {
  5404. if ( PowerUpActive( hudPowerup ) ) {
  5405. //int remaining = inventory.powerupEndTime[ hudPowerup ] - gameLocal.time;
  5406. //int filledbar = idMath::ClampInt( 0, hudPowerupDuration, remaining );
  5407. // TODO_SPARTY: powerups??
  5408. //if ( hud ) {
  5409. // hud->SetStateInt( "player_powerup", 100 * filledbar / hudPowerupDuration );
  5410. // hud->SetStateInt( "player_poweruptime", remaining / 1000 );
  5411. //}
  5412. }
  5413. }
  5414. }
  5415. /*
  5416. ==============
  5417. idPlayer::GetPDA
  5418. ==============
  5419. */
  5420. const idDeclPDA * idPlayer::GetPDA() const {
  5421. if ( inventory.pdas.Num() > 0 ) {
  5422. return inventory.pdas[ 0 ];
  5423. } else {
  5424. return NULL;
  5425. }
  5426. }
  5427. /*
  5428. ==============
  5429. idPlayer::GetVideo
  5430. ==============
  5431. */
  5432. const idDeclVideo *idPlayer::GetVideo( int index ) {
  5433. if ( index >= 0 && index < inventory.videos.Num() ) {
  5434. return inventory.videos[index];
  5435. }
  5436. return NULL;
  5437. }
  5438. /*
  5439. ==============
  5440. idPlayer::TogglePDA
  5441. ==============
  5442. */
  5443. void idPlayer::TogglePDA() {
  5444. if ( inventory.pdas.Num() == 0 ) {
  5445. ShowTip( spawnArgs.GetString( "text_infoTitle" ), spawnArgs.GetString( "text_noPDA" ), true );
  5446. return;
  5447. }
  5448. if ( pdaMenu != NULL ) {
  5449. objectiveSystemOpen = !objectiveSystemOpen;
  5450. pdaMenu->ActivateMenu( objectiveSystemOpen );
  5451. if ( objectiveSystemOpen ) {
  5452. if ( hud ) {
  5453. hud->ClearNewPDAInfo();
  5454. }
  5455. }
  5456. }
  5457. }
  5458. /*
  5459. ==============
  5460. idPlayer::Spectate
  5461. ==============
  5462. */
  5463. void idPlayer::Spectate( bool spectate, bool force ) {
  5464. spectating = spectate;
  5465. if ( spectating ) {
  5466. // join the spectators
  5467. ClearPowerUps();
  5468. spectator = this->entityNumber;
  5469. Init();
  5470. StopRagdoll();
  5471. SetPhysics( &physicsObj );
  5472. physicsObj.DisableClip();
  5473. FlashlightOff();
  5474. Hide();
  5475. Event_DisableWeapon();
  5476. // Raise me up by a little bit. if i'm the local client.
  5477. if( IsLocallyControlled() ) {
  5478. SetSpectateOrigin();
  5479. }
  5480. HideRespawnHudMessage();
  5481. idLib::Printf( "DMP _ GENERAL :> Player %d Spectating \n", entityNumber );
  5482. } else {
  5483. // put everything back together again
  5484. currentWeapon = -1; // to make sure the def will be loaded if necessary
  5485. Show();
  5486. Event_EnableWeapon();
  5487. idLib::Printf( "DMP _ GENERAL :> Player %d Not Spectating \n", entityNumber );
  5488. SetEyeHeight( pm_normalviewheight.GetFloat() );
  5489. }
  5490. SetClipModel();
  5491. }
  5492. /*
  5493. ==============
  5494. idPlayer::SetClipModel
  5495. ==============
  5496. */
  5497. void idPlayer::SetClipModel() {
  5498. idBounds bounds;
  5499. if ( spectating ) {
  5500. bounds = idBounds( vec3_origin ).Expand( pm_spectatebbox.GetFloat() * 0.5f );
  5501. } else {
  5502. bounds[0].Set( -pm_bboxwidth.GetFloat() * 0.5f, -pm_bboxwidth.GetFloat() * 0.5f, 0 );
  5503. bounds[1].Set( pm_bboxwidth.GetFloat() * 0.5f, pm_bboxwidth.GetFloat() * 0.5f, pm_normalheight.GetFloat() );
  5504. }
  5505. // the origin of the clip model needs to be set before calling SetClipModel
  5506. // otherwise our physics object's current origin value gets reset to 0
  5507. idClipModel *newClip;
  5508. if ( pm_usecylinder.GetBool() ) {
  5509. newClip = new (TAG_PHYSICS_CLIP_ENTITY) idClipModel( idTraceModel( bounds, 8 ) );
  5510. newClip->Translate( physicsObj.PlayerGetOrigin() );
  5511. physicsObj.SetClipModel( newClip, 1.0f );
  5512. } else {
  5513. newClip = new (TAG_PHYSICS_CLIP_ENTITY) idClipModel( idTraceModel( bounds ) );
  5514. newClip->Translate( physicsObj.PlayerGetOrigin() );
  5515. physicsObj.SetClipModel( newClip, 1.0f );
  5516. }
  5517. }
  5518. /*
  5519. ==============
  5520. idPlayer::UseVehicle
  5521. ==============
  5522. */
  5523. void idPlayer::UseVehicle() {
  5524. trace_t trace;
  5525. idVec3 start, end;
  5526. idEntity *ent;
  5527. if ( GetBindMaster() && GetBindMaster()->IsType( idAFEntity_Vehicle::Type ) ) {
  5528. Show();
  5529. static_cast<idAFEntity_Vehicle*>(GetBindMaster())->Use( this );
  5530. } else {
  5531. start = GetEyePosition();
  5532. end = start + viewAngles.ToForward() * 80.0f;
  5533. gameLocal.clip.TracePoint( trace, start, end, MASK_SHOT_RENDERMODEL, this );
  5534. if ( trace.fraction < 1.0f ) {
  5535. ent = gameLocal.entities[ trace.c.entityNum ];
  5536. if ( ent && ent->IsType( idAFEntity_Vehicle::Type ) ) {
  5537. Hide();
  5538. static_cast<idAFEntity_Vehicle*>(ent)->Use( this );
  5539. }
  5540. }
  5541. }
  5542. }
  5543. /*
  5544. ==============
  5545. idPlayer::PerformImpulse
  5546. ==============
  5547. */
  5548. void idPlayer::PerformImpulse( int impulse ) {
  5549. bool isIntroMap = ( idStr::FindText( gameLocal.GetMapFileName(), "mars_city1" ) >= 0 );
  5550. // Normal 1 - 0 Keys.
  5551. if ( impulse >= IMPULSE_0 && impulse <= IMPULSE_12 && !isIntroMap ) {
  5552. SelectWeapon( impulse, false );
  5553. return;
  5554. }
  5555. // DPAD Weapon Quick Select
  5556. if ( impulse >= IMPULSE_28 && impulse <= IMPULSE_31 && !isIntroMap ) {
  5557. SelectWeapon( impulse, false );
  5558. return;
  5559. }
  5560. switch( impulse ) {
  5561. case IMPULSE_13: {
  5562. Reload();
  5563. break;
  5564. }
  5565. case IMPULSE_14: {
  5566. if ( !isIntroMap ) {
  5567. NextWeapon();
  5568. }
  5569. break;
  5570. }
  5571. case IMPULSE_15: {
  5572. if ( !isIntroMap ) {
  5573. PrevWeapon();
  5574. }
  5575. break;
  5576. }
  5577. case IMPULSE_16: {
  5578. if( flashlight.IsValid() ) {
  5579. if ( flashlight.GetEntity()->lightOn ) {
  5580. FlashlightOff();
  5581. } else if ( !spectating && weaponEnabled && !hiddenWeapon && !gameLocal.world->spawnArgs.GetBool( "no_Weapons" ) ) {
  5582. FlashlightOn();
  5583. }
  5584. }
  5585. break;
  5586. }
  5587. case IMPULSE_19: {
  5588. // when we're not in single player, IMPULSE_19 is used for showScores
  5589. // otherwise it opens the pda
  5590. if ( !common->IsMultiplayer() ) {
  5591. #if !defined(ID_RETAIL) && !defined(ID_RETAIL_INTERNAL)
  5592. if ( !common->KeyState( 56 ) ) { // don't toggle PDA when LEFT ALT is down
  5593. #endif
  5594. if ( objectiveSystemOpen ) {
  5595. TogglePDA();
  5596. } else if ( weapon_pda >= 0 ) {
  5597. SelectWeapon( weapon_pda, true );
  5598. }
  5599. #if !defined(ID_RETAIL) && !defined(ID_RETAIL_INTERNAL)
  5600. }
  5601. #endif
  5602. } else {
  5603. if ( IsLocallyControlled() ) {
  5604. gameLocal.mpGame.SetScoreboardActive( true );
  5605. }
  5606. }
  5607. break;
  5608. }
  5609. case IMPULSE_22: {
  5610. if ( common->IsClient() || IsLocallyControlled() ) {
  5611. gameLocal.mpGame.ToggleSpectate();
  5612. }
  5613. break;
  5614. }
  5615. case IMPULSE_25: {
  5616. if ( common->IsServer() && gameLocal.mpGame.IsGametypeFlagBased() && (gameLocal.serverInfo.GetInt( "si_midnight" ) == 2) ) {
  5617. if ( enviroSuitLight.IsValid() ) {
  5618. enviroSuitLight.GetEntity()->PostEventMS( &EV_Remove, 0 );
  5619. enviroSuitLight = NULL;
  5620. } else {
  5621. const idDict *lightDef = gameLocal.FindEntityDefDict( "envirosuit_light", false );
  5622. if ( lightDef ) {
  5623. idEntity *temp = static_cast<idEntity *>(enviroSuitLight.GetEntity());
  5624. idAngles lightAng = firstPersonViewAxis.ToAngles();
  5625. idVec3 lightOrg = firstPersonViewOrigin;
  5626. idVec3 enviroOffset = lightDef->GetVector( "enviro_offset" );
  5627. idVec3 enviroAngleOffset = lightDef->GetVector( "enviro_angle_offset" );
  5628. gameLocal.SpawnEntityDef( *lightDef, &temp, false );
  5629. enviroSuitLight = static_cast<idLight *>(temp);
  5630. enviroSuitLight.GetEntity()->fl.networkSync = true;
  5631. lightOrg += (enviroOffset.x * firstPersonViewAxis[0]);
  5632. lightOrg += (enviroOffset.y * firstPersonViewAxis[1]);
  5633. lightOrg += (enviroOffset.z * firstPersonViewAxis[2]);
  5634. lightAng.pitch += enviroAngleOffset.x;
  5635. lightAng.yaw += enviroAngleOffset.y;
  5636. lightAng.roll += enviroAngleOffset.z;
  5637. enviroSuitLight.GetEntity()->GetPhysics()->SetOrigin( lightOrg );
  5638. enviroSuitLight.GetEntity()->GetPhysics()->SetAxis( lightAng.ToMat3() );
  5639. enviroSuitLight.GetEntity()->UpdateVisuals();
  5640. enviroSuitLight.GetEntity()->Present();
  5641. }
  5642. }
  5643. }
  5644. break;
  5645. }
  5646. //Hack so the chainsaw will work in MP
  5647. case IMPULSE_27: {
  5648. SelectWeapon(18, false);
  5649. break;
  5650. }
  5651. }
  5652. }
  5653. /*
  5654. ==============
  5655. idPlayer::EvaluateControls
  5656. ==============
  5657. */
  5658. void idPlayer::EvaluateControls() {
  5659. // check for respawning
  5660. if ( health <= 0 && !g_testDeath.GetBool() ) {
  5661. if ( common->IsMultiplayer() ) {
  5662. // in MP, idMultiplayerGame decides spawns
  5663. if ( ( gameLocal.time > minRespawnTime ) && ( usercmd.buttons & BUTTON_ATTACK ) ) {
  5664. forceRespawn = true;
  5665. } else if ( gameLocal.time > maxRespawnTime ) {
  5666. forceRespawn = true;
  5667. }
  5668. } else {
  5669. // in single player, we let the session handle restarting the level or loading a game
  5670. if ( gameLocal.time > minRespawnTime ) {
  5671. gameLocal.sessionCommand = "died";
  5672. }
  5673. }
  5674. }
  5675. if ( usercmd.impulseSequence != oldImpulseSequence ) {
  5676. PerformImpulse( usercmd.impulse );
  5677. }
  5678. if ( forceScoreBoard ) {
  5679. gameLocal.mpGame.SetScoreboardActive( true );
  5680. }
  5681. oldImpulseSequence = usercmd.impulseSequence;
  5682. AdjustSpeed();
  5683. // update the viewangles
  5684. UpdateViewAngles();
  5685. }
  5686. /*
  5687. ==============
  5688. idPlayer::AdjustSpeed
  5689. ==============
  5690. */
  5691. void idPlayer::AdjustSpeed() {
  5692. float speed;
  5693. float rate;
  5694. if ( spectating ) {
  5695. speed = pm_spectatespeed.GetFloat();
  5696. bobFrac = 0.0f;
  5697. } else if ( noclip ) {
  5698. speed = pm_noclipspeed.GetFloat();
  5699. bobFrac = 0.0f;
  5700. } else if ( !physicsObj.OnLadder() && ( usercmd.buttons & BUTTON_RUN ) && ( usercmd.forwardmove || usercmd.rightmove ) && !( usercmd.buttons & BUTTON_CROUCH ) ) {
  5701. if ( !common->IsMultiplayer() && !physicsObj.IsCrouching() && !PowerUpActive( ADRENALINE ) ) {
  5702. stamina -= MS2SEC( gameLocal.time - gameLocal.previousTime );
  5703. }
  5704. if ( stamina < 0 ) {
  5705. stamina = 0;
  5706. }
  5707. if ( ( !pm_stamina.GetFloat() ) || ( stamina > pm_staminathreshold.GetFloat() ) ) {
  5708. bobFrac = 1.0f;
  5709. } else if ( pm_staminathreshold.GetFloat() <= 0.0001f ) {
  5710. bobFrac = 0.0f;
  5711. } else {
  5712. bobFrac = stamina / pm_staminathreshold.GetFloat();
  5713. }
  5714. speed = pm_walkspeed.GetFloat() * ( 1.0f - bobFrac ) + pm_runspeed.GetFloat() * bobFrac;
  5715. } else {
  5716. rate = pm_staminarate.GetFloat();
  5717. // increase 25% faster when not moving
  5718. if ( ( usercmd.forwardmove == 0 ) && ( usercmd.rightmove == 0 ) && ( !physicsObj.OnLadder() || ( ( usercmd.buttons & (BUTTON_CROUCH|BUTTON_JUMP) ) == 0 ) ) ) {
  5719. rate *= 1.25f;
  5720. }
  5721. stamina += rate * MS2SEC( gameLocal.time - gameLocal.previousTime );
  5722. if ( stamina > pm_stamina.GetFloat() ) {
  5723. stamina = pm_stamina.GetFloat();
  5724. }
  5725. speed = pm_walkspeed.GetFloat();
  5726. bobFrac = 0.0f;
  5727. }
  5728. speed *= PowerUpModifier(SPEED);
  5729. if ( influenceActive == INFLUENCE_LEVEL3 ) {
  5730. speed *= 0.33f;
  5731. }
  5732. physicsObj.SetSpeed( speed, pm_crouchspeed.GetFloat() );
  5733. }
  5734. /*
  5735. ==============
  5736. idPlayer::AdjustBodyAngles
  5737. ==============
  5738. */
  5739. void idPlayer::AdjustBodyAngles() {
  5740. idMat3 lookAxis;
  5741. idMat3 legsAxis;
  5742. bool blend;
  5743. float diff;
  5744. float frac;
  5745. float upBlend;
  5746. float forwardBlend;
  5747. float downBlend;
  5748. if ( health < 0 ) {
  5749. return;
  5750. }
  5751. blend = true;
  5752. if ( !physicsObj.HasGroundContacts() ) {
  5753. idealLegsYaw = 0.0f;
  5754. legsForward = true;
  5755. } else if ( usercmd.forwardmove < 0 ) {
  5756. idealLegsYaw = idMath::AngleNormalize180( idVec3( -usercmd.forwardmove, usercmd.rightmove, 0.0f ).ToYaw() );
  5757. legsForward = false;
  5758. } else if ( usercmd.forwardmove > 0 ) {
  5759. idealLegsYaw = idMath::AngleNormalize180( idVec3( usercmd.forwardmove, -usercmd.rightmove, 0.0f ).ToYaw() );
  5760. legsForward = true;
  5761. } else if ( ( usercmd.rightmove != 0 ) && physicsObj.IsCrouching() ) {
  5762. if ( !legsForward ) {
  5763. idealLegsYaw = idMath::AngleNormalize180( idVec3( idMath::Abs( usercmd.rightmove ), usercmd.rightmove, 0.0f ).ToYaw() );
  5764. } else {
  5765. idealLegsYaw = idMath::AngleNormalize180( idVec3( idMath::Abs( usercmd.rightmove ), -usercmd.rightmove, 0.0f ).ToYaw() );
  5766. }
  5767. } else if ( usercmd.rightmove != 0 ) {
  5768. idealLegsYaw = 0.0f;
  5769. legsForward = true;
  5770. } else {
  5771. legsForward = true;
  5772. diff = idMath::Fabs( idealLegsYaw - legsYaw );
  5773. idealLegsYaw = idealLegsYaw - idMath::AngleNormalize180( viewAngles.yaw - oldViewYaw );
  5774. if ( diff < 0.1f ) {
  5775. legsYaw = idealLegsYaw;
  5776. blend = false;
  5777. }
  5778. }
  5779. if ( !physicsObj.IsCrouching() ) {
  5780. legsForward = true;
  5781. }
  5782. oldViewYaw = viewAngles.yaw;
  5783. AI_TURN_LEFT = false;
  5784. AI_TURN_RIGHT = false;
  5785. if ( idealLegsYaw < -45.0f ) {
  5786. idealLegsYaw = 0;
  5787. AI_TURN_RIGHT = true;
  5788. blend = true;
  5789. } else if ( idealLegsYaw > 45.0f ) {
  5790. idealLegsYaw = 0;
  5791. AI_TURN_LEFT = true;
  5792. blend = true;
  5793. }
  5794. if ( blend ) {
  5795. legsYaw = legsYaw * 0.9f + idealLegsYaw * 0.1f;
  5796. }
  5797. legsAxis = idAngles( 0.0f, legsYaw, 0.0f ).ToMat3();
  5798. animator.SetJointAxis( hipJoint, JOINTMOD_WORLD, legsAxis );
  5799. // calculate the blending between down, straight, and up
  5800. frac = viewAngles.pitch / 90.0f;
  5801. if ( frac > 0.0f ) {
  5802. downBlend = frac;
  5803. forwardBlend = 1.0f - frac;
  5804. upBlend = 0.0f;
  5805. } else {
  5806. downBlend = 0.0f;
  5807. forwardBlend = 1.0f + frac;
  5808. upBlend = -frac;
  5809. }
  5810. animator.CurrentAnim( ANIMCHANNEL_TORSO )->SetSyncedAnimWeight( 0, downBlend );
  5811. animator.CurrentAnim( ANIMCHANNEL_TORSO )->SetSyncedAnimWeight( 1, forwardBlend );
  5812. animator.CurrentAnim( ANIMCHANNEL_TORSO )->SetSyncedAnimWeight( 2, upBlend );
  5813. animator.CurrentAnim( ANIMCHANNEL_LEGS )->SetSyncedAnimWeight( 0, downBlend );
  5814. animator.CurrentAnim( ANIMCHANNEL_LEGS )->SetSyncedAnimWeight( 1, forwardBlend );
  5815. animator.CurrentAnim( ANIMCHANNEL_LEGS )->SetSyncedAnimWeight( 2, upBlend );
  5816. }
  5817. /*
  5818. ==============
  5819. idPlayer::InitAASLocation
  5820. ==============
  5821. */
  5822. void idPlayer::InitAASLocation() {
  5823. int i;
  5824. int num;
  5825. idVec3 size;
  5826. idBounds bounds;
  5827. idAAS *aas;
  5828. idVec3 origin;
  5829. GetFloorPos( 64.0f, origin );
  5830. num = gameLocal.NumAAS();
  5831. aasLocation.SetGranularity( 1 );
  5832. aasLocation.SetNum( num );
  5833. for( i = 0; i < aasLocation.Num(); i++ ) {
  5834. aasLocation[ i ].areaNum = 0;
  5835. aasLocation[ i ].pos = origin;
  5836. aas = gameLocal.GetAAS( i );
  5837. if ( aas != NULL && aas->GetSettings() ) {
  5838. size = aas->GetSettings()->boundingBoxes[0][1];
  5839. bounds[0] = -size;
  5840. size.z = 32.0f;
  5841. bounds[1] = size;
  5842. aasLocation[ i ].areaNum = aas->PointReachableAreaNum( origin, bounds, AREA_REACHABLE_WALK );
  5843. }
  5844. }
  5845. }
  5846. /*
  5847. ==============
  5848. idPlayer::SetAASLocation
  5849. ==============
  5850. */
  5851. void idPlayer::SetAASLocation() {
  5852. int i;
  5853. int areaNum;
  5854. idVec3 size;
  5855. idBounds bounds;
  5856. idAAS *aas;
  5857. idVec3 origin;
  5858. if ( !GetFloorPos( 64.0f, origin ) ) {
  5859. return;
  5860. }
  5861. for( i = 0; i < aasLocation.Num(); i++ ) {
  5862. aas = gameLocal.GetAAS( i );
  5863. if ( !aas ) {
  5864. continue;
  5865. }
  5866. size = aas->GetSettings()->boundingBoxes[0][1];
  5867. bounds[0] = -size;
  5868. size.z = 32.0f;
  5869. bounds[1] = size;
  5870. areaNum = aas->PointReachableAreaNum( origin, bounds, AREA_REACHABLE_WALK );
  5871. if ( areaNum ) {
  5872. aasLocation[ i ].pos = origin;
  5873. aasLocation[ i ].areaNum = areaNum;
  5874. }
  5875. }
  5876. }
  5877. /*
  5878. ==============
  5879. idPlayer::GetAASLocation
  5880. ==============
  5881. */
  5882. void idPlayer::GetAASLocation( idAAS *aas, idVec3 &pos, int &areaNum ) const {
  5883. int i;
  5884. if ( aas != NULL ) {
  5885. for( i = 0; i < aasLocation.Num(); i++ ) {
  5886. if ( aas == gameLocal.GetAAS( i ) ) {
  5887. areaNum = aasLocation[ i ].areaNum;
  5888. pos = aasLocation[ i ].pos;
  5889. return;
  5890. }
  5891. }
  5892. }
  5893. areaNum = 0;
  5894. pos = physicsObj.GetOrigin();
  5895. }
  5896. /*
  5897. ==============
  5898. idPlayer::Move_Interpolated
  5899. ==============
  5900. */
  5901. void idPlayer::Move_Interpolated( float fraction ) {
  5902. float newEyeOffset;
  5903. idVec3 oldOrigin;
  5904. idVec3 oldVelocity;
  5905. idVec3 pushVelocity;
  5906. // save old origin and velocity for crashlanding
  5907. oldOrigin = physicsObj.GetOrigin();
  5908. oldVelocity = physicsObj.GetLinearVelocity();
  5909. pushVelocity = physicsObj.GetPushedLinearVelocity();
  5910. // set physics variables
  5911. physicsObj.SetMaxStepHeight( pm_stepsize.GetFloat() );
  5912. physicsObj.SetMaxJumpHeight( pm_jumpheight.GetFloat() );
  5913. if ( noclip ) {
  5914. physicsObj.SetContents( 0 );
  5915. physicsObj.SetMovementType( PM_NOCLIP );
  5916. } else if ( spectating ) {
  5917. physicsObj.SetContents( 0 );
  5918. physicsObj.SetMovementType( PM_SPECTATOR );
  5919. } else if ( health <= 0 ) {
  5920. physicsObj.SetContents( CONTENTS_CORPSE | CONTENTS_MONSTERCLIP );
  5921. physicsObj.SetMovementType( PM_DEAD );
  5922. } else if ( gameLocal.inCinematic || gameLocal.GetCamera() || privateCameraView || ( influenceActive == INFLUENCE_LEVEL2 ) ) {
  5923. physicsObj.SetContents( CONTENTS_BODY );
  5924. physicsObj.SetMovementType( PM_FREEZE );
  5925. } else if ( mountedObject ) {
  5926. physicsObj.SetContents( 0 );
  5927. physicsObj.SetMovementType( PM_FREEZE );
  5928. } else {
  5929. physicsObj.SetContents( CONTENTS_BODY );
  5930. physicsObj.SetMovementType( PM_NORMAL );
  5931. }
  5932. if ( spectating ) {
  5933. physicsObj.SetClipMask( MASK_DEADSOLID );
  5934. } else if ( health <= 0 ) {
  5935. physicsObj.SetClipMask( MASK_DEADSOLID );
  5936. } else {
  5937. physicsObj.SetClipMask( MASK_PLAYERSOLID );
  5938. }
  5939. physicsObj.SetDebugLevel( g_debugMove.GetBool() );
  5940. {
  5941. idVec3 org;
  5942. idMat3 axis;
  5943. GetViewPos( org, axis );
  5944. physicsObj.SetPlayerInput( usercmd, axis[0] );
  5945. }
  5946. // FIXME: physics gets disabled somehow
  5947. BecomeActive( TH_PHYSICS );
  5948. InterpolatePhysics( fraction );
  5949. // update our last valid AAS location for the AI
  5950. SetAASLocation();
  5951. if ( spectating ) {
  5952. newEyeOffset = 0.0f;
  5953. } else if ( health <= 0 ) {
  5954. newEyeOffset = pm_deadviewheight.GetFloat();
  5955. } else if ( physicsObj.IsCrouching() ) {
  5956. newEyeOffset = pm_crouchviewheight.GetFloat();
  5957. } else if ( GetBindMaster() && GetBindMaster()->IsType( idAFEntity_Vehicle::Type ) ) {
  5958. newEyeOffset = 0.0f;
  5959. } else {
  5960. newEyeOffset = pm_normalviewheight.GetFloat();
  5961. }
  5962. if ( EyeHeight() != newEyeOffset ) {
  5963. if ( spectating ) {
  5964. SetEyeHeight( newEyeOffset );
  5965. } else {
  5966. // smooth out duck height changes
  5967. SetEyeHeight( EyeHeight() * pm_crouchrate.GetFloat() + newEyeOffset * ( 1.0f - pm_crouchrate.GetFloat() ) );
  5968. }
  5969. }
  5970. if ( AI_JUMP ) {
  5971. // bounce the view weapon
  5972. loggedAccel_t *acc = &loggedAccel[currentLoggedAccel&(NUM_LOGGED_ACCELS-1)];
  5973. currentLoggedAccel++;
  5974. acc->time = gameLocal.time;
  5975. acc->dir[2] = 200;
  5976. acc->dir[0] = acc->dir[1] = 0;
  5977. }
  5978. if ( AI_ONLADDER ) {
  5979. int old_rung = oldOrigin.z / LADDER_RUNG_DISTANCE;
  5980. int new_rung = physicsObj.GetOrigin().z / LADDER_RUNG_DISTANCE;
  5981. if ( old_rung != new_rung ) {
  5982. StartSound( "snd_stepladder", SND_CHANNEL_ANY, 0, false, NULL );
  5983. }
  5984. }
  5985. BobCycle( pushVelocity );
  5986. CrashLand( oldOrigin, oldVelocity );
  5987. }
  5988. /*
  5989. ==============
  5990. idPlayer::Move
  5991. ==============
  5992. */
  5993. void idPlayer::Move() {
  5994. float newEyeOffset;
  5995. idVec3 oldOrigin;
  5996. idVec3 oldVelocity;
  5997. idVec3 pushVelocity;
  5998. // save old origin and velocity for crashlanding
  5999. oldOrigin = physicsObj.GetOrigin();
  6000. oldVelocity = physicsObj.GetLinearVelocity();
  6001. pushVelocity = physicsObj.GetPushedLinearVelocity();
  6002. // set physics variables
  6003. physicsObj.SetMaxStepHeight( pm_stepsize.GetFloat() );
  6004. physicsObj.SetMaxJumpHeight( pm_jumpheight.GetFloat() );
  6005. if ( noclip ) {
  6006. physicsObj.SetContents( 0 );
  6007. physicsObj.SetMovementType( PM_NOCLIP );
  6008. } else if ( spectating ) {
  6009. physicsObj.SetContents( 0 );
  6010. physicsObj.SetMovementType( PM_SPECTATOR );
  6011. } else if ( health <= 0 ) {
  6012. physicsObj.SetContents( CONTENTS_CORPSE | CONTENTS_MONSTERCLIP );
  6013. physicsObj.SetMovementType( PM_DEAD );
  6014. } else if ( gameLocal.inCinematic || gameLocal.GetCamera() || privateCameraView || ( influenceActive == INFLUENCE_LEVEL2 ) ) {
  6015. physicsObj.SetContents( CONTENTS_BODY );
  6016. physicsObj.SetMovementType( PM_FREEZE );
  6017. } else if ( mountedObject ) {
  6018. physicsObj.SetContents( 0 );
  6019. physicsObj.SetMovementType( PM_FREEZE );
  6020. } else {
  6021. physicsObj.SetContents( CONTENTS_BODY );
  6022. physicsObj.SetMovementType( PM_NORMAL );
  6023. }
  6024. if ( spectating ) {
  6025. physicsObj.SetClipMask( MASK_DEADSOLID );
  6026. } else if ( health <= 0 ) {
  6027. physicsObj.SetClipMask( MASK_DEADSOLID );
  6028. } else {
  6029. physicsObj.SetClipMask( MASK_PLAYERSOLID );
  6030. }
  6031. physicsObj.SetDebugLevel( g_debugMove.GetBool() );
  6032. {
  6033. idVec3 org;
  6034. idMat3 axis;
  6035. GetViewPos( org, axis );
  6036. physicsObj.SetPlayerInput( usercmd, axis[0] );
  6037. }
  6038. // FIXME: physics gets disabled somehow
  6039. BecomeActive( TH_PHYSICS );
  6040. RunPhysics();
  6041. // update our last valid AAS location for the AI
  6042. SetAASLocation();
  6043. if ( spectating ) {
  6044. newEyeOffset = 0.0f;
  6045. } else if ( health <= 0 ) {
  6046. newEyeOffset = pm_deadviewheight.GetFloat();
  6047. } else if ( physicsObj.IsCrouching() ) {
  6048. newEyeOffset = pm_crouchviewheight.GetFloat();
  6049. } else if ( GetBindMaster() && GetBindMaster()->IsType( idAFEntity_Vehicle::Type ) ) {
  6050. newEyeOffset = 0.0f;
  6051. } else {
  6052. newEyeOffset = pm_normalviewheight.GetFloat();
  6053. }
  6054. if ( EyeHeight() != newEyeOffset ) {
  6055. if ( spectating ) {
  6056. SetEyeHeight( newEyeOffset );
  6057. } else {
  6058. // smooth out duck height changes
  6059. SetEyeHeight( EyeHeight() * pm_crouchrate.GetFloat() + newEyeOffset * ( 1.0f - pm_crouchrate.GetFloat() ) );
  6060. }
  6061. }
  6062. if ( noclip || gameLocal.inCinematic || ( influenceActive == INFLUENCE_LEVEL2 ) ) {
  6063. AI_CROUCH = false;
  6064. AI_ONGROUND = ( influenceActive == INFLUENCE_LEVEL2 );
  6065. AI_ONLADDER = false;
  6066. AI_JUMP = false;
  6067. } else {
  6068. AI_CROUCH = physicsObj.IsCrouching();
  6069. AI_ONGROUND = physicsObj.HasGroundContacts();
  6070. AI_ONLADDER = physicsObj.OnLadder();
  6071. AI_JUMP = physicsObj.HasJumped();
  6072. // check if we're standing on top of a monster and give a push if we are
  6073. idEntity *groundEnt = physicsObj.GetGroundEntity();
  6074. if ( groundEnt != NULL && groundEnt->IsType( idAI::Type ) ) {
  6075. idVec3 vel = physicsObj.GetLinearVelocity();
  6076. if ( vel.ToVec2().LengthSqr() < 0.1f ) {
  6077. vel.ToVec2() = physicsObj.GetOrigin().ToVec2() - groundEnt->GetPhysics()->GetAbsBounds().GetCenter().ToVec2();
  6078. vel.ToVec2().NormalizeFast();
  6079. vel.ToVec2() *= pm_walkspeed.GetFloat();
  6080. } else {
  6081. // give em a push in the direction they're going
  6082. vel *= 1.1f;
  6083. }
  6084. physicsObj.SetLinearVelocity( vel );
  6085. }
  6086. }
  6087. if ( AI_JUMP ) {
  6088. // bounce the view weapon
  6089. loggedAccel_t *acc = &loggedAccel[currentLoggedAccel&(NUM_LOGGED_ACCELS-1)];
  6090. currentLoggedAccel++;
  6091. acc->time = gameLocal.time;
  6092. acc->dir[2] = 200;
  6093. acc->dir[0] = acc->dir[1] = 0;
  6094. }
  6095. if ( AI_ONLADDER ) {
  6096. int old_rung = oldOrigin.z / LADDER_RUNG_DISTANCE;
  6097. int new_rung = physicsObj.GetOrigin().z / LADDER_RUNG_DISTANCE;
  6098. if ( old_rung != new_rung ) {
  6099. StartSound( "snd_stepladder", SND_CHANNEL_ANY, 0, false, NULL );
  6100. }
  6101. }
  6102. BobCycle( pushVelocity );
  6103. CrashLand( oldOrigin, oldVelocity );
  6104. }
  6105. /*
  6106. ========================
  6107. idPlayer::AllowClientAuthPhysics
  6108. ========================
  6109. */
  6110. bool idPlayer::AllowClientAuthPhysics() {
  6111. // note respawn count > 1: respawn should be called twice - once for initial spawn and once for actual respawn by game mode
  6112. // TODO: I don't think doom 3 will need to care about the respawn count.
  6113. return ( usercmd.serverGameMilliseconds > serverOverridePositionTime && commonLocal.GetUCmdMgr().HasUserCmdForPlayer( entityNumber ) );
  6114. }
  6115. /*
  6116. ========================
  6117. idPlayer::RunPhysics_RemoteClientCorrection
  6118. ========================
  6119. */
  6120. void idPlayer::RunPhysics_RemoteClientCorrection() {
  6121. if ( !AllowClientAuthPhysics() ) {
  6122. // We are still overriding client's position
  6123. if ( pm_clientAuthoritative_debug.GetBool() ) {
  6124. //clientGame->renderWorld->DebugPoint( idColor::colorRed, GetOrigin() );
  6125. idLib::Printf("[%d]Ignoring client auth: cmd.serverTime: %d overrideTime: %d \n", entityNumber, usercmd.serverGameMilliseconds, serverOverridePositionTime );
  6126. }
  6127. return;
  6128. }
  6129. // Client is on a pusher... ignore him so he doesn't lag behind
  6130. bool becameUnlocked = false;
  6131. if ( physicsObj.ClientPusherLocked( becameUnlocked ) ) {
  6132. // Check and see how far we've diverged.
  6133. idVec3 cmdPos( usercmd.pos[0], usercmd.pos[1], usercmd.pos[2] );
  6134. idVec3 newOrigin = physicsObj.GetOrigin();
  6135. idVec3 divergeVec = cmdPos - newOrigin;
  6136. idLib::Printf( "Client Divergence: %s Length: %2f\n", divergeVec.ToString( 3 ), divergeVec.Length() );
  6137. // if the client Diverges over a certain amount, snap him back
  6138. if( divergeVec.Length() < pm_clientAuthoritative_Divergence.GetFloat() ) {
  6139. return;
  6140. }
  6141. }
  6142. if ( becameUnlocked ) {
  6143. // Client just got off of a mover, wait before listening to him
  6144. serverOverridePositionTime = gameLocal.GetServerGameTimeMs();
  6145. return;
  6146. }
  6147. // Correction
  6148. {
  6149. idVec3 newOrigin = physicsObj.GetOrigin();
  6150. idVec3 cmdPos( usercmd.pos[0], usercmd.pos[1], usercmd.pos[2] );
  6151. idVec3 desiredPos = cmdPos;
  6152. float delta = ( desiredPos - newOrigin ).Length();
  6153. // ignore small differences in Z: this can cause player to not have proper ground contacts which messes up
  6154. // velocity/acceleration calculation. If this hack doesn't work out, will may need more precision for at least
  6155. // the Z component of the client's origin.
  6156. if ( idMath::Fabs( desiredPos.z - newOrigin.z ) < pm_clientAuthoritative_minDistZ.GetFloat() ) {
  6157. if ( pm_clientAuthoritative_debug.GetBool() ) {
  6158. //idLib::Printf("[%d]Remote client physics: ignore small z delta: %f\n", usercmd.clientGameFrame, ( desiredPos.z - newOrigin.z ) );
  6159. }
  6160. desiredPos.z = newOrigin.z;
  6161. }
  6162. // Origin
  6163. if ( delta > pm_clientAuthoritative_minDist.GetFloat() ) {
  6164. if ( pm_clientAuthoritative_Lerp.GetFloat() > 0.0f ) {
  6165. desiredPos.x = idMath::LerpToWithScale( newOrigin.x, desiredPos.x, pm_clientAuthoritative_Lerp.GetFloat() );
  6166. desiredPos.y = idMath::LerpToWithScale( newOrigin.y, desiredPos.y, pm_clientAuthoritative_Lerp.GetFloat() );
  6167. }
  6168. // Set corrected position immediately if non deferred
  6169. physicsObj.SetOrigin( desiredPos );
  6170. if ( pm_clientAuthoritative_debug.GetBool() && delta > pm_clientAuthoritative_warnDist.GetFloat() ) {
  6171. idLib::Warning("Remote client player physics: delta movement for frame was %f units", delta );
  6172. gameRenderWorld->DebugLine( colorRed, newOrigin, desiredPos );
  6173. }
  6174. }
  6175. if ( pm_clientAuthoritative_debug.GetBool() ) {
  6176. //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 );
  6177. gameRenderWorld->DebugLine( colorRed, newOrigin, desiredPos );
  6178. //gameRenderWorld->DebugPoint( colorBlue, cmdPos );
  6179. }
  6180. // Set velocity if significantly different than client.
  6181. const float serverSpeedSquared = physicsObj.GetLinearVelocity().LengthSqr();
  6182. const float clientSpeedSquared = usercmd.speedSquared;
  6183. if ( std::abs( serverSpeedSquared - clientSpeedSquared ) > pm_clientAuthoritative_minSpeedSquared.GetFloat() ) {
  6184. idVec3 normalizedVelocity = physicsObj.GetLinearVelocity();
  6185. const float VELOCITY_EPSILON = 0.001f;
  6186. if ( normalizedVelocity.LengthSqr() > VELOCITY_EPSILON ) {
  6187. normalizedVelocity.Normalize();
  6188. }
  6189. physicsObj.SetLinearVelocity( normalizedVelocity * idMath::Sqrt( clientSpeedSquared ) );
  6190. }
  6191. }
  6192. }
  6193. /*
  6194. ========================
  6195. idPlayer::GetPhysicsTimeStep
  6196. Uses the time from the usercmd in case the server is running at a slower engineHz
  6197. than the client.
  6198. ========================
  6199. */
  6200. int idPlayer::GetPhysicsTimeStep() const {
  6201. // if the ucDeltaMillisecond value looks wrong, use the game delta milliseconds
  6202. // This can happen if the user brings up the pause menu in SP
  6203. const int ucDeltaMilliseconds = usercmd.clientGameMilliseconds - oldCmd.clientGameMilliseconds;
  6204. if ( ucDeltaMilliseconds < 1 || ucDeltaMilliseconds > 20 ) {
  6205. return gameLocal.time - gameLocal.previousTime;
  6206. } else {
  6207. return ucDeltaMilliseconds;
  6208. }
  6209. }
  6210. /*
  6211. ==============
  6212. idPlayer::ShowRespawnHudMessage
  6213. Called once when the minimum respawn time has passed after a player has died
  6214. so that we can display a message to the user.
  6215. ==============
  6216. */
  6217. void idPlayer::ShowRespawnHudMessage() {
  6218. if ( IsLocallyControlled() ) {
  6219. hud->ShowRespawnMessage( true );
  6220. } else {
  6221. // Clients show the hud message through a reliable message.
  6222. idBitMsg outMsg;
  6223. byte dummyData[1];
  6224. outMsg.InitWrite( dummyData, sizeof( dummyData ) );
  6225. outMsg.BeginWriting();
  6226. outMsg.WriteByte( 0 );
  6227. session->GetActingGameStateLobbyBase().SendReliableToLobbyUser( gameLocal.lobbyUserIDs[entityNumber], GAME_RELIABLE_MESSAGE_RESPAWN_AVAILABLE, outMsg );
  6228. }
  6229. }
  6230. /*
  6231. ==============
  6232. idPlayer::HideRespawnHudMessage
  6233. Called once when we should remove the respawn message from the hud,
  6234. for example, when a player does respawn.
  6235. ==============
  6236. */
  6237. void idPlayer::HideRespawnHudMessage() {
  6238. if ( IsLocallyControlled() ) {
  6239. hud->ShowRespawnMessage( false );
  6240. }
  6241. }
  6242. /*
  6243. ==============
  6244. idPlayer::UpdateHud
  6245. ==============
  6246. */
  6247. void idPlayer::UpdateHud() {
  6248. idPlayer *aimed;
  6249. if ( !hud ) {
  6250. return;
  6251. }
  6252. if ( !IsLocallyControlled() ) {
  6253. return;
  6254. }
  6255. int c = inventory.pickupItemNames.Num();
  6256. if ( c > 0 ) {
  6257. if ( hud != NULL && hud->IsPickupListReady() ) {
  6258. if ( inventory.nextItemPickup && gameLocal.time - inventory.nextItemPickup > 2000 ) {
  6259. inventory.nextItemNum = 1;
  6260. }
  6261. int i;
  6262. int count = 5;
  6263. bool showNewPickups = false;
  6264. for ( i = 0; i < count; i++ ) { //_D3XP
  6265. if ( i < c ) {
  6266. hud->UpdatePickupInfo( i, inventory.pickupItemNames[0] );
  6267. inventory.nextItemNum++;
  6268. showNewPickups = true;
  6269. } else {
  6270. hud->UpdatePickupInfo( i, "" );
  6271. continue;
  6272. }
  6273. inventory.nextItemPickup = gameLocal.time + 2500;
  6274. inventory.pickupItemNames.RemoveIndex( 0 );
  6275. }
  6276. if ( showNewPickups ) {
  6277. hud->ShowPickups();
  6278. }
  6279. }
  6280. }
  6281. if ( gameLocal.realClientTime == lastMPAimTime ) {
  6282. if ( MPAim != -1 && gameLocal.mpGame.IsGametypeTeamBased() /* CTF */
  6283. && gameLocal.entities[ MPAim ] && gameLocal.entities[ MPAim ]->IsType( idPlayer::Type )
  6284. && static_cast< idPlayer * >( gameLocal.entities[ MPAim ] )->team == team ) {
  6285. aimed = static_cast< idPlayer * >( gameLocal.entities[ MPAim ] );
  6286. hud->TriggerHitTarget( true, session->GetActingGameStateLobbyBase().GetLobbyUserName( gameLocal.lobbyUserIDs[ MPAim ] ), aimed->team + 1 );
  6287. MPAimHighlight = true;
  6288. MPAimFadeTime = 0; // no fade till loosing focus
  6289. } else if ( MPAimHighlight ) {
  6290. hud->TriggerHitTarget( false, "" );
  6291. MPAimFadeTime = gameLocal.realClientTime;
  6292. MPAimHighlight = false;
  6293. }
  6294. }
  6295. if ( MPAimFadeTime ) {
  6296. assert( !MPAimHighlight );
  6297. if ( gameLocal.realClientTime - MPAimFadeTime > 2000 ) {
  6298. MPAimFadeTime = 0;
  6299. }
  6300. }
  6301. if ( common->IsMultiplayer() && IsLocallyControlled() ) {
  6302. hud->ToggleLagged( isLagged );
  6303. // TODO_SPARTY: what is this projectile stuff for
  6304. //hud->SetStateInt( "g_showProjectilePct", g_showProjectilePct.GetInteger() );
  6305. //if ( numProjectilesFired ) {
  6306. // hud->SetStateString( "projectilepct", va( "Hit %% %.1f", ( (float) numProjectileHits / numProjectilesFired ) * 100 ) );
  6307. //} else {
  6308. // hud->SetStateString( "projectilepct", "Hit % 0.0" );
  6309. //}
  6310. }
  6311. }
  6312. /*
  6313. ==============
  6314. idPlayer::UpdateDeathSkin
  6315. ==============
  6316. */
  6317. void idPlayer::UpdateDeathSkin( bool state_hitch ) {
  6318. if ( !( common->IsMultiplayer() || g_testDeath.GetBool() ) ) {
  6319. return;
  6320. }
  6321. if ( health <= 0 ) {
  6322. if ( !doingDeathSkin ) {
  6323. deathClearContentsTime = spawnArgs.GetInt( "deathSkinTime" );
  6324. doingDeathSkin = true;
  6325. renderEntity.noShadow = true;
  6326. if ( state_hitch ) {
  6327. renderEntity.shaderParms[ SHADERPARM_TIME_OF_DEATH ] = gameLocal.time * 0.001f - 2.0f;
  6328. } else {
  6329. renderEntity.shaderParms[ SHADERPARM_TIME_OF_DEATH ] = gameLocal.time * 0.001f;
  6330. }
  6331. UpdateVisuals();
  6332. }
  6333. // wait a bit before switching off the content
  6334. if ( deathClearContentsTime && gameLocal.time > deathClearContentsTime ) {
  6335. SetCombatContents( false );
  6336. deathClearContentsTime = 0;
  6337. }
  6338. } else {
  6339. renderEntity.noShadow = false;
  6340. renderEntity.shaderParms[ SHADERPARM_TIME_OF_DEATH ] = 0.0f;
  6341. UpdateVisuals();
  6342. doingDeathSkin = false;
  6343. }
  6344. }
  6345. /*
  6346. ==============
  6347. idPlayer::StartFxOnBone
  6348. ==============
  6349. */
  6350. void idPlayer::StartFxOnBone( const char *fx, const char *bone ) {
  6351. idVec3 offset;
  6352. idMat3 axis;
  6353. jointHandle_t jointHandle = GetAnimator()->GetJointHandle( bone );
  6354. if ( jointHandle == INVALID_JOINT ) {
  6355. gameLocal.Printf( "Cannot find bone %s\n", bone );
  6356. return;
  6357. }
  6358. if ( GetAnimator()->GetJointTransform( jointHandle, gameLocal.time, offset, axis ) ) {
  6359. offset = GetPhysics()->GetOrigin() + offset * GetPhysics()->GetAxis();
  6360. axis = axis * GetPhysics()->GetAxis();
  6361. }
  6362. idEntityFx::StartFx( fx, &offset, &axis, this, true );
  6363. }
  6364. /*
  6365. ==============
  6366. idPlayer::HandleGuiEvents
  6367. ==============
  6368. */
  6369. bool idPlayer::HandleGuiEvents( const sysEvent_t * ev ) {
  6370. bool handled = false;
  6371. if ( hudManager != NULL && hudManager->IsActive() ) {
  6372. handled = hudManager->HandleGuiEvent( ev );
  6373. }
  6374. if ( pdaMenu != NULL && pdaMenu->IsActive() ) {
  6375. handled = pdaMenu->HandleGuiEvent( ev );
  6376. }
  6377. return handled;
  6378. }
  6379. /*
  6380. ==============
  6381. idPlayer::UpdateLaserSight
  6382. ==============
  6383. */
  6384. idCVar g_laserSightWidth( "g_laserSightWidth", "2.0", CVAR_FLOAT | CVAR_ARCHIVE, "laser sight beam width" );
  6385. idCVar g_laserSightLength( "g_laserSightLength", "250", CVAR_FLOAT | CVAR_ARCHIVE, "laser sight beam length" );
  6386. void idPlayer::UpdateLaserSight() {
  6387. idVec3 muzzleOrigin;
  6388. idMat3 muzzleAxis;
  6389. // In Multiplayer, weapon might not have been spawned yet.
  6390. if( weapon.GetEntity() == NULL ) {
  6391. return;
  6392. }
  6393. if ( !IsGameStereoRendered() ||
  6394. !weapon.GetEntity()->ShowCrosshair() ||
  6395. AI_DEAD ||
  6396. weapon->IsHidden() ||
  6397. !weapon->GetMuzzlePositionWithHacks( muzzleOrigin, muzzleAxis ) ) {
  6398. // hide it
  6399. laserSightRenderEntity.allowSurfaceInViewID = -1;
  6400. if ( laserSightHandle == -1 ) {
  6401. laserSightHandle = gameRenderWorld->AddEntityDef( &laserSightRenderEntity );
  6402. } else {
  6403. gameRenderWorld->UpdateEntityDef( laserSightHandle, &laserSightRenderEntity );
  6404. }
  6405. return;
  6406. }
  6407. // program the beam model
  6408. // only show in the player's view
  6409. laserSightRenderEntity.allowSurfaceInViewID = entityNumber+1;
  6410. laserSightRenderEntity.axis.Identity();
  6411. laserSightRenderEntity.origin = muzzleOrigin - muzzleAxis[0] * 2.0f;
  6412. idVec3 &target = *reinterpret_cast<idVec3 *>( &laserSightRenderEntity.shaderParms[SHADERPARM_BEAM_END_X] );
  6413. target = muzzleOrigin + muzzleAxis[0] * g_laserSightLength.GetFloat();
  6414. laserSightRenderEntity.shaderParms[SHADERPARM_BEAM_WIDTH] = g_laserSightWidth.GetFloat();
  6415. if ( IsGameStereoRendered() && laserSightHandle == -1 ) {
  6416. laserSightHandle = gameRenderWorld->AddEntityDef( &laserSightRenderEntity );
  6417. } else {
  6418. gameRenderWorld->UpdateEntityDef( laserSightHandle, &laserSightRenderEntity );
  6419. }
  6420. }
  6421. /*
  6422. ==============
  6423. idPlayer::Think
  6424. Called every tic for each player
  6425. ==============
  6426. */
  6427. void idPlayer::Think() {
  6428. playedTimeResidual += ( gameLocal.time - gameLocal.previousTime );
  6429. playedTimeSecs += playedTimeResidual / 1000;
  6430. playedTimeResidual = playedTimeResidual % 1000;
  6431. aimAssist.Update();
  6432. UpdatePlayerIcons();
  6433. UpdateSkinSetup();
  6434. buttonMask &= usercmd.buttons;
  6435. usercmd.buttons &= ~buttonMask;
  6436. // clear the ik before we do anything else so the skeleton doesn't get updated twice
  6437. walkIK.ClearJointMods();
  6438. // if this is the very first frame of the map, set the delta view angles
  6439. // based on the usercmd angles
  6440. if ( !spawnAnglesSet && ( gameLocal.GameState() != GAMESTATE_STARTUP ) ) {
  6441. spawnAnglesSet = true;
  6442. SetViewAngles( spawnAngles );
  6443. oldImpulseSequence = usercmd.impulseSequence;
  6444. }
  6445. if ( mountedObject ) {
  6446. usercmd.forwardmove = 0;
  6447. usercmd.rightmove = 0;
  6448. usercmd.buttons &= ~(BUTTON_JUMP|BUTTON_CROUCH);
  6449. }
  6450. if ( objectiveSystemOpen || gameLocal.inCinematic || influenceActive ) {
  6451. if ( objectiveSystemOpen && AI_PAIN ) {
  6452. TogglePDA();
  6453. }
  6454. usercmd.forwardmove = 0;
  6455. usercmd.rightmove = 0;
  6456. usercmd.buttons &= ~(BUTTON_JUMP|BUTTON_CROUCH);
  6457. }
  6458. // log movement changes for weapon bobbing effects
  6459. if ( usercmd.forwardmove != oldCmd.forwardmove ) {
  6460. loggedAccel_t *acc = &loggedAccel[currentLoggedAccel&(NUM_LOGGED_ACCELS-1)];
  6461. currentLoggedAccel++;
  6462. acc->time = gameLocal.time;
  6463. acc->dir[0] = usercmd.forwardmove - oldCmd.forwardmove;
  6464. acc->dir[1] = acc->dir[2] = 0;
  6465. }
  6466. if ( usercmd.rightmove != oldCmd.rightmove ) {
  6467. loggedAccel_t *acc = &loggedAccel[currentLoggedAccel&(NUM_LOGGED_ACCELS-1)];
  6468. currentLoggedAccel++;
  6469. acc->time = gameLocal.time;
  6470. acc->dir[1] = usercmd.rightmove - oldCmd.rightmove;
  6471. acc->dir[0] = acc->dir[2] = 0;
  6472. }
  6473. // zooming
  6474. if ( ( usercmd.buttons ^ oldCmd.buttons ) & BUTTON_ZOOM ) {
  6475. if ( ( usercmd.buttons & BUTTON_ZOOM ) && weapon.GetEntity() ) {
  6476. zoomFov.Init( gameLocal.time, 200.0f, CalcFov( false ), weapon.GetEntity()->GetZoomFov() );
  6477. } else {
  6478. zoomFov.Init( gameLocal.time, 200.0f, zoomFov.GetCurrentValue( gameLocal.time ), DefaultFov() );
  6479. }
  6480. }
  6481. // if we have an active gui, we will unrotate the view angles as
  6482. // we turn the mouse movements into gui events
  6483. idUserInterface *gui = ActiveGui();
  6484. if ( gui && gui != focusUI ) {
  6485. RouteGuiMouse( gui );
  6486. }
  6487. // set the push velocity on the weapon before running the physics
  6488. if ( weapon.GetEntity() ) {
  6489. weapon.GetEntity()->SetPushVelocity( physicsObj.GetPushedLinearVelocity() );
  6490. }
  6491. EvaluateControls();
  6492. if ( !af.IsActive() ) {
  6493. AdjustBodyAngles();
  6494. CopyJointsFromBodyToHead();
  6495. }
  6496. if ( IsLocallyControlled() ) {
  6497. // Local player on the server. Do normal movement.
  6498. Move();
  6499. } else {
  6500. // Server is processing a client. Run client's commands like normal...
  6501. Move();
  6502. // ...then correct if needed.
  6503. RunPhysics_RemoteClientCorrection();
  6504. }
  6505. if ( !g_stopTime.GetBool() ) {
  6506. if ( !noclip && !spectating && ( health > 0 ) && !IsHidden() ) {
  6507. TouchTriggers();
  6508. }
  6509. // not done on clients for various reasons. don't do it on server and save the sound channel for other things
  6510. if ( !common->IsMultiplayer() ) {
  6511. SetCurrentHeartRate();
  6512. float scale = new_g_damageScale;
  6513. if ( g_useDynamicProtection.GetBool() && scale < 1.0f && gameLocal.time - lastDmgTime > 500 ) {
  6514. if ( scale < 1.0f ) {
  6515. scale += 0.05f;
  6516. }
  6517. if ( scale > 1.0f ) {
  6518. scale = 1.0f;
  6519. }
  6520. new_g_damageScale = scale;
  6521. }
  6522. }
  6523. // update GUIs, Items, and character interactions
  6524. UpdateFocus();
  6525. UpdateLocation();
  6526. // update player script
  6527. UpdateScript();
  6528. // service animations
  6529. if ( !spectating && !af.IsActive() && !gameLocal.inCinematic ) {
  6530. UpdateConditions();
  6531. UpdateAnimState();
  6532. CheckBlink();
  6533. }
  6534. // clear out our pain flag so we can tell if we recieve any damage between now and the next time we think
  6535. AI_PAIN = false;
  6536. }
  6537. // calculate the exact bobbed view position, which is used to
  6538. // position the view weapon, among other things
  6539. CalculateFirstPersonView();
  6540. // this may use firstPersonView, or a thirdPeroson / camera view
  6541. CalculateRenderView();
  6542. inventory.UpdateArmor();
  6543. if ( spectating ) {
  6544. UpdateSpectating();
  6545. } else if ( health > 0 ) {
  6546. UpdateWeapon();
  6547. }
  6548. UpdateFlashlight();
  6549. UpdateAir();
  6550. UpdatePowerupHud();
  6551. UpdateHud();
  6552. UpdatePowerUps();
  6553. UpdateDeathSkin( false );
  6554. if ( common->IsMultiplayer() ) {
  6555. DrawPlayerIcons();
  6556. if ( enviroSuitLight.IsValid() ) {
  6557. idAngles lightAng = firstPersonViewAxis.ToAngles();
  6558. idVec3 lightOrg = firstPersonViewOrigin;
  6559. const idDict *lightDef = gameLocal.FindEntityDefDict( "envirosuit_light", false );
  6560. idVec3 enviroOffset = lightDef->GetVector( "enviro_offset" );
  6561. idVec3 enviroAngleOffset = lightDef->GetVector( "enviro_angle_offset" );
  6562. lightOrg += (enviroOffset.x * firstPersonViewAxis[0]);
  6563. lightOrg += (enviroOffset.y * firstPersonViewAxis[1]);
  6564. lightOrg += (enviroOffset.z * firstPersonViewAxis[2]);
  6565. lightAng.pitch += enviroAngleOffset.x;
  6566. lightAng.yaw += enviroAngleOffset.y;
  6567. lightAng.roll += enviroAngleOffset.z;
  6568. enviroSuitLight.GetEntity()->GetPhysics()->SetOrigin( lightOrg );
  6569. enviroSuitLight.GetEntity()->GetPhysics()->SetAxis( lightAng.ToMat3() );
  6570. enviroSuitLight.GetEntity()->UpdateVisuals();
  6571. enviroSuitLight.GetEntity()->Present();
  6572. }
  6573. }
  6574. renderEntity_t * headRenderEnt = NULL;
  6575. if ( head.GetEntity() ) {
  6576. headRenderEnt = head.GetEntity()->GetRenderEntity();
  6577. }
  6578. if ( headRenderEnt ) {
  6579. if ( influenceSkin ) {
  6580. headRenderEnt->customSkin = influenceSkin;
  6581. } else {
  6582. headRenderEnt->customSkin = NULL;
  6583. }
  6584. }
  6585. if ( common->IsMultiplayer() || g_showPlayerShadow.GetBool() ) {
  6586. renderEntity.suppressShadowInViewID = 0;
  6587. if ( headRenderEnt ) {
  6588. headRenderEnt->suppressShadowInViewID = 0;
  6589. }
  6590. } else {
  6591. renderEntity.suppressShadowInViewID = entityNumber+1;
  6592. if ( headRenderEnt ) {
  6593. headRenderEnt->suppressShadowInViewID = entityNumber+1;
  6594. }
  6595. }
  6596. // never cast shadows from our first-person muzzle flashes
  6597. renderEntity.suppressShadowInLightID = LIGHTID_VIEW_MUZZLE_FLASH + entityNumber;
  6598. if ( headRenderEnt ) {
  6599. headRenderEnt->suppressShadowInLightID = LIGHTID_VIEW_MUZZLE_FLASH + entityNumber;
  6600. }
  6601. if ( !g_stopTime.GetBool() ) {
  6602. UpdateAnimation();
  6603. Present();
  6604. UpdateDamageEffects();
  6605. LinkCombat();
  6606. playerView.CalculateShake();
  6607. }
  6608. if ( !( thinkFlags & TH_THINK ) ) {
  6609. gameLocal.Printf( "player %d not thinking?\n", entityNumber );
  6610. }
  6611. if ( g_showEnemies.GetBool() ) {
  6612. idActor *ent;
  6613. int num = 0;
  6614. for( ent = enemyList.Next(); ent != NULL; ent = ent->enemyNode.Next() ) {
  6615. gameLocal.Printf( "enemy (%d)'%s'\n", ent->entityNumber, ent->name.c_str() );
  6616. gameRenderWorld->DebugBounds( colorRed, ent->GetPhysics()->GetBounds().Expand( 2 ), ent->GetPhysics()->GetOrigin() );
  6617. num++;
  6618. }
  6619. gameLocal.Printf( "%d: enemies\n", num );
  6620. }
  6621. inventory.RechargeAmmo(this);
  6622. if(healthRecharge) {
  6623. int elapsed = gameLocal.time - lastHealthRechargeTime;
  6624. if(elapsed >= rechargeSpeed) {
  6625. int intervals = (gameLocal.time - lastHealthRechargeTime)/rechargeSpeed;
  6626. Give("health", va("%d", intervals), ITEM_GIVE_FEEDBACK | ITEM_GIVE_UPDATE_STATE );
  6627. lastHealthRechargeTime += intervals*rechargeSpeed;
  6628. }
  6629. }
  6630. // determine if portal sky is in pvs
  6631. gameLocal.portalSkyActive = gameLocal.pvs.CheckAreasForPortalSky( gameLocal.GetPlayerPVS(), GetPhysics()->GetOrigin() );
  6632. // stereo rendering laser sight that replaces the crosshair
  6633. UpdateLaserSight();
  6634. // Show the respawn hud message if necessary.
  6635. if ( common->IsMultiplayer() && ( minRespawnTime != maxRespawnTime ) ) {
  6636. if ( gameLocal.previousTime < minRespawnTime && minRespawnTime <= gameLocal.time ) {
  6637. // Server will show the hud message directly.
  6638. ShowRespawnHudMessage();
  6639. }
  6640. }
  6641. // Make sure voice groups are set to the right team
  6642. if ( common->IsMultiplayer() && session->GetState() >= idSession::INGAME && entityNumber < MAX_CLIENTS ) { // The entityNumber < MAX_CLIENTS seems to quiet the static analyzer
  6643. // Make sure we're on the right team (at the lobby level)
  6644. const int voiceTeam = spectating ? LOBBY_SPECTATE_TEAM_FOR_VOICE_CHAT : team;
  6645. //idLib::Printf( "SERVER: Sending voice %i / %i\n", entityNumber, voiceTeam );
  6646. // Update lobby team
  6647. session->GetActingGameStateLobbyBase().SetLobbyUserTeam( gameLocal.lobbyUserIDs[ entityNumber ], voiceTeam );
  6648. // Update voice groups to match in case something changed
  6649. session->SetVoiceGroupsToTeams();
  6650. }
  6651. }
  6652. /*
  6653. =================
  6654. idPlayer::StartHealthRecharge
  6655. =================
  6656. */
  6657. void idPlayer::StartHealthRecharge(int speed) {
  6658. lastHealthRechargeTime = gameLocal.time;
  6659. healthRecharge = true;
  6660. rechargeSpeed = speed;
  6661. }
  6662. /*
  6663. =================
  6664. idPlayer::StopHealthRecharge
  6665. =================
  6666. */
  6667. void idPlayer::StopHealthRecharge() {
  6668. healthRecharge = false;
  6669. }
  6670. /*
  6671. =================
  6672. idPlayer::GetCurrentWeapon
  6673. =================
  6674. */
  6675. idStr idPlayer::GetCurrentWeapon() {
  6676. const char *weapon;
  6677. if ( currentWeapon >= 0 ) {
  6678. weapon = spawnArgs.GetString( va( "def_weapon%d", currentWeapon ) );
  6679. return weapon;
  6680. } else {
  6681. return "";
  6682. }
  6683. }
  6684. /*
  6685. =================
  6686. idPlayer::CanGive
  6687. =================
  6688. */
  6689. bool idPlayer::CanGive( const char *statname, const char *value ) {
  6690. if ( AI_DEAD ) {
  6691. return false;
  6692. }
  6693. if ( !idStr::Icmp( statname, "health" ) ) {
  6694. if ( health >= inventory.maxHealth ) {
  6695. return false;
  6696. }
  6697. return true;
  6698. } else if ( !idStr::Icmp( statname, "stamina" ) ) {
  6699. if ( stamina >= 100 ) {
  6700. return false;
  6701. }
  6702. return true;
  6703. } else if ( !idStr::Icmp( statname, "heartRate" ) ) {
  6704. return true;
  6705. } else if ( !idStr::Icmp( statname, "air" ) ) {
  6706. if ( airMsec >= pm_airMsec.GetInteger() ) {
  6707. return false;
  6708. }
  6709. return true;
  6710. } else {
  6711. return inventory.CanGive( this, spawnArgs, statname, value );
  6712. }
  6713. }
  6714. /*
  6715. =================
  6716. idPlayer::StopHelltime
  6717. provides a quick non-ramping way of stopping helltime
  6718. =================
  6719. */
  6720. void idPlayer::StopHelltime( bool quick ) {
  6721. if ( !PowerUpActive( HELLTIME ) ) {
  6722. return;
  6723. }
  6724. // take away the powerups
  6725. if ( PowerUpActive( INVULNERABILITY ) ) {
  6726. ClearPowerup( INVULNERABILITY );
  6727. }
  6728. if ( PowerUpActive( BERSERK ) ) {
  6729. ClearPowerup( BERSERK );
  6730. }
  6731. if ( PowerUpActive( HELLTIME ) ) {
  6732. ClearPowerup( HELLTIME );
  6733. }
  6734. // stop the looping sound
  6735. StopSound( SND_CHANNEL_DEMONIC, false );
  6736. // reset the game vars
  6737. if ( quick ) {
  6738. gameLocal.QuickSlowmoReset();
  6739. }
  6740. }
  6741. /*
  6742. =================
  6743. idPlayer::Event_ToggleBloom
  6744. =================
  6745. */
  6746. void idPlayer::Event_ToggleBloom( int on ) {
  6747. if ( on ) {
  6748. bloomEnabled = true;
  6749. }
  6750. else {
  6751. bloomEnabled = false;
  6752. }
  6753. }
  6754. /*
  6755. =================
  6756. idPlayer::Event_SetBloomParms
  6757. =================
  6758. */
  6759. void idPlayer::Event_SetBloomParms( float speed, float intensity ) {
  6760. bloomSpeed = speed;
  6761. bloomIntensity = intensity;
  6762. }
  6763. /*
  6764. =================
  6765. idPlayer::PlayHelltimeStopSound
  6766. =================
  6767. */
  6768. void idPlayer::PlayHelltimeStopSound() {
  6769. const char* sound;
  6770. if ( spawnArgs.GetString( "snd_helltime_stop", "", &sound ) ) {
  6771. PostEventMS( &EV_StartSoundShader, 0, sound, SND_CHANNEL_ANY );
  6772. }
  6773. }
  6774. /*
  6775. =================
  6776. idPlayer::RouteGuiMouse
  6777. =================
  6778. */
  6779. void idPlayer::RouteGuiMouse( idUserInterface *gui ) {
  6780. sysEvent_t ev;
  6781. const char *command;
  6782. if ( usercmd.mx != oldMouseX || usercmd.my != oldMouseY ) {
  6783. ev = sys->GenerateMouseMoveEvent( usercmd.mx - oldMouseX, usercmd.my - oldMouseY );
  6784. command = gui->HandleEvent( &ev, gameLocal.time );
  6785. oldMouseX = usercmd.mx;
  6786. oldMouseY = usercmd.my;
  6787. }
  6788. }
  6789. /*
  6790. ==================
  6791. idPlayer::LookAtKiller
  6792. ==================
  6793. */
  6794. void idPlayer::LookAtKiller( idEntity *inflictor, idEntity *attacker ) {
  6795. idVec3 dir;
  6796. if ( attacker && attacker != this ) {
  6797. dir = attacker->GetPhysics()->GetOrigin() - GetPhysics()->GetOrigin();
  6798. } else if ( inflictor && inflictor != this ) {
  6799. dir = inflictor->GetPhysics()->GetOrigin() - GetPhysics()->GetOrigin();
  6800. } else {
  6801. dir = viewAxis[ 0 ];
  6802. }
  6803. idAngles ang( 0, dir.ToYaw(), 0 );
  6804. SetViewAngles( ang );
  6805. }
  6806. /*
  6807. ==============
  6808. idPlayer::Kill
  6809. ==============
  6810. */
  6811. void idPlayer::Kill( bool delayRespawn, bool nodamage ) {
  6812. if ( spectating ) {
  6813. SpectateFreeFly( false );
  6814. } else if ( health > 0 ) {
  6815. godmode = false;
  6816. if ( nodamage ) {
  6817. ServerSpectate( true );
  6818. idLib::Printf( "TOURNEY Kill :> Player %d On Deck \n", entityNumber );
  6819. forceRespawn = true;
  6820. } else {
  6821. Damage( this, this, vec3_origin, "damage_suicide", 1.0f, INVALID_JOINT );
  6822. if ( delayRespawn ) {
  6823. forceRespawn = false;
  6824. int delay = spawnArgs.GetFloat( "respawn_delay" );
  6825. minRespawnTime = gameLocal.time + SEC2MS( delay );
  6826. maxRespawnTime = minRespawnTime + MAX_RESPAWN_TIME;
  6827. }
  6828. }
  6829. }
  6830. }
  6831. /*
  6832. ==================
  6833. idPlayer::Killed
  6834. ==================
  6835. */
  6836. void idPlayer::Killed( idEntity *inflictor, idEntity *attacker, int damage, const idVec3 &dir, int location ) {
  6837. float delay;
  6838. assert( !common->IsClient() );
  6839. // stop taking knockback once dead
  6840. fl.noknockback = true;
  6841. if ( health < -999 ) {
  6842. health = -999;
  6843. }
  6844. if ( AI_DEAD ) {
  6845. AI_PAIN = true;
  6846. return;
  6847. }
  6848. heartInfo.Init( 0, 0, 0, BASE_HEARTRATE );
  6849. AdjustHeartRate( DEAD_HEARTRATE, 10.0f, 0.0f, true );
  6850. if ( !g_testDeath.GetBool() && !common->IsMultiplayer() ) {
  6851. playerView.Fade( colorBlack, 3000 );
  6852. }
  6853. AI_DEAD = true;
  6854. SetAnimState( ANIMCHANNEL_LEGS, "Legs_Death", 4 );
  6855. SetAnimState( ANIMCHANNEL_TORSO, "Torso_Death", 4 );
  6856. SetWaitState( "" );
  6857. animator.ClearAllJoints();
  6858. if ( StartRagdoll() ) {
  6859. pm_modelView.SetInteger( 0 );
  6860. minRespawnTime = gameLocal.time + RAGDOLL_DEATH_TIME;
  6861. maxRespawnTime = minRespawnTime + MAX_RESPAWN_TIME;
  6862. } else {
  6863. // don't allow respawn until the death anim is done
  6864. // g_forcerespawn may force spawning at some later time
  6865. delay = spawnArgs.GetFloat( "respawn_delay" );
  6866. minRespawnTime = gameLocal.time + SEC2MS( delay );
  6867. maxRespawnTime = minRespawnTime + MAX_RESPAWN_TIME;
  6868. }
  6869. physicsObj.SetMovementType( PM_DEAD );
  6870. StartSound( "snd_death", SND_CHANNEL_VOICE, 0, false, NULL );
  6871. StopSound( SND_CHANNEL_BODY2, false );
  6872. fl.takedamage = true; // can still be gibbed
  6873. // get rid of weapon
  6874. weapon.GetEntity()->OwnerDied();
  6875. // In multiplayer, get rid of the flashlight, or other players
  6876. // will see it floating after the player is dead.
  6877. if ( common->IsMultiplayer() ) {
  6878. FlashlightOff();
  6879. if ( flashlight.GetEntity() ) {
  6880. flashlight.GetEntity()->OwnerDied();
  6881. }
  6882. }
  6883. // drop the weapon as an item
  6884. DropWeapon( true );
  6885. // drop the flag if player was carrying it
  6886. if ( common->IsMultiplayer() && gameLocal.mpGame.IsGametypeFlagBased() && carryingFlag ) {
  6887. DropFlag();
  6888. }
  6889. if ( !g_testDeath.GetBool() ) {
  6890. LookAtKiller( inflictor, attacker );
  6891. }
  6892. if ( common->IsMultiplayer() || g_testDeath.GetBool() ) {
  6893. idPlayer *killer = NULL;
  6894. // no gibbing in MP. Event_Gib will early out in MP
  6895. if ( attacker->IsType( idPlayer::Type ) ) {
  6896. killer = static_cast<idPlayer*>(attacker);
  6897. if ( health < -20 || killer->PowerUpActive( BERSERK ) ) {
  6898. gibDeath = true;
  6899. gibsDir = dir;
  6900. gibsLaunched = false;
  6901. }
  6902. }
  6903. gameLocal.mpGame.PlayerDeath( this, killer, isTelefragged );
  6904. } else {
  6905. physicsObj.SetContents( CONTENTS_CORPSE | CONTENTS_MONSTERCLIP );
  6906. }
  6907. ClearPowerUps();
  6908. UpdateVisuals();
  6909. }
  6910. /*
  6911. =====================
  6912. idPlayer::GetAIAimTargets
  6913. Returns positions for the AI to aim at.
  6914. =====================
  6915. */
  6916. void idPlayer::GetAIAimTargets( const idVec3 &lastSightPos, idVec3 &headPos, idVec3 &chestPos ) {
  6917. idVec3 offset;
  6918. idMat3 axis;
  6919. idVec3 origin;
  6920. origin = lastSightPos - physicsObj.GetOrigin();
  6921. GetJointWorldTransform( chestJoint, gameLocal.time, offset, axis );
  6922. headPos = offset + origin;
  6923. GetJointWorldTransform( headJoint, gameLocal.time, offset, axis );
  6924. chestPos = offset + origin;
  6925. }
  6926. /*
  6927. ================
  6928. idPlayer::DamageFeedback
  6929. callback function for when another entity received damage from this entity. damage can be adjusted and returned to the caller.
  6930. ================
  6931. */
  6932. void idPlayer::DamageFeedback( idEntity *victim, idEntity *inflictor, int &damage ) {
  6933. // Since we're predicting projectiles on the client now, we might actually get here
  6934. // (used be an assert for clients).
  6935. if ( common->IsClient() ) {
  6936. return;
  6937. }
  6938. damage *= PowerUpModifier( BERSERK );
  6939. if ( damage && ( victim != this ) && ( victim->IsType( idActor::Type ) || victim->IsType( idDamagable::Type ) ) ) {
  6940. idPlayer *victimPlayer = NULL;
  6941. /* No damage feedback sound for hitting friendlies in CTF */
  6942. if ( victim->IsType( idPlayer::Type ) ) {
  6943. victimPlayer = static_cast<idPlayer*>(victim);
  6944. }
  6945. if ( gameLocal.mpGame.IsGametypeFlagBased() && victimPlayer && this->team == victimPlayer->team ) {
  6946. /* Do nothing ... */
  6947. } else {
  6948. SetLastHitTime( gameLocal.time );
  6949. }
  6950. }
  6951. }
  6952. /*
  6953. =================
  6954. idPlayer::CalcDamagePoints
  6955. Calculates how many health and armor points will be inflicted, but
  6956. doesn't actually do anything with them. This is used to tell when an attack
  6957. would have killed the player, possibly allowing a "saving throw"
  6958. =================
  6959. */
  6960. void idPlayer::CalcDamagePoints( idEntity *inflictor, idEntity *attacker, const idDict *damageDef,
  6961. const float damageScale, const int location, int *health, int *armor ) {
  6962. int damage;
  6963. int armorSave;
  6964. damageDef->GetInt( "damage", "20", damage );
  6965. damage = GetDamageForLocation( damage, location );
  6966. idPlayer *player = attacker->IsType( idPlayer::Type ) ? static_cast<idPlayer*>(attacker) : NULL;
  6967. if ( !common->IsMultiplayer() ) {
  6968. if ( inflictor != gameLocal.world ) {
  6969. switch ( g_skill.GetInteger() ) {
  6970. case 0:
  6971. damage *= 0.50f;
  6972. if ( damage < 1 ) {
  6973. damage = 1;
  6974. }
  6975. break;
  6976. case 2:
  6977. damage *= 1.70f;
  6978. break;
  6979. case 3:
  6980. damage *= 3.5f;
  6981. break;
  6982. default:
  6983. break;
  6984. }
  6985. }
  6986. }
  6987. damage *= damageScale;
  6988. // always give half damage if hurting self
  6989. if ( attacker == this ) {
  6990. if ( common->IsMultiplayer() ) {
  6991. // only do this in mp so single player plasma and rocket splash is very dangerous in close quarters
  6992. damage *= damageDef->GetFloat( "selfDamageScale", "0.5" );
  6993. } else {
  6994. damage *= damageDef->GetFloat( "selfDamageScale", "1" );
  6995. }
  6996. }
  6997. // check for completely getting out of the damage
  6998. if ( !damageDef->GetBool( "noGod" ) ) {
  6999. // check for godmode
  7000. if ( godmode ) {
  7001. damage = 0;
  7002. }
  7003. //Invulnerability is just like god mode
  7004. if( PowerUpActive( INVULNERABILITY ) ) {
  7005. damage = 0;
  7006. }
  7007. }
  7008. // inform the attacker that they hit someone
  7009. attacker->DamageFeedback( this, inflictor, damage );
  7010. // save some from armor
  7011. if ( !damageDef->GetBool( "noArmor" ) ) {
  7012. float armor_protection;
  7013. armor_protection = ( common->IsMultiplayer() ) ? g_armorProtectionMP.GetFloat() : g_armorProtection.GetFloat();
  7014. armorSave = ceil( damage * armor_protection );
  7015. if ( armorSave >= inventory.armor ) {
  7016. armorSave = inventory.armor;
  7017. }
  7018. if ( !damage ) {
  7019. armorSave = 0;
  7020. } else if ( armorSave >= damage ) {
  7021. armorSave = damage - 1;
  7022. damage = 1;
  7023. } else {
  7024. damage -= armorSave;
  7025. }
  7026. } else {
  7027. armorSave = 0;
  7028. }
  7029. // check for team damage
  7030. if ( gameLocal.mpGame.IsGametypeTeamBased() /* CTF */
  7031. && !gameLocal.serverInfo.GetBool( "si_teamDamage" )
  7032. && !damageDef->GetBool( "noTeam" )
  7033. && player
  7034. && player != this // you get self damage no matter what
  7035. && player->team == team ) {
  7036. damage = 0;
  7037. }
  7038. *health = damage;
  7039. *armor = armorSave;
  7040. }
  7041. /*
  7042. ============
  7043. idPlayer::ControllerShakeFromDamage
  7044. ============
  7045. */
  7046. void idPlayer::ControllerShakeFromDamage( int damage ) {
  7047. // If the player is local. SHAkkkkkkeeee!
  7048. if( common->IsMultiplayer() && IsLocallyControlled() ) {
  7049. int maxMagScale = pm_controllerShake_damageMaxMag.GetFloat();
  7050. int maxDurScale = pm_controllerShake_damageMaxDur.GetFloat();
  7051. // determine rumble
  7052. // >= 100 damage - will be 300 Mag
  7053. float highMag = ( Max( damage, 100 ) / 100.0f ) * maxMagScale;
  7054. int highDuration = idMath::Ftoi( ( Max( damage, 100 ) / 100.0f ) * maxDurScale );
  7055. float lowMag = highMag * 0.75f;
  7056. int lowDuration = idMath::Ftoi( highDuration );
  7057. SetControllerShake( highMag, highDuration, lowMag, lowDuration );
  7058. }
  7059. }
  7060. /*
  7061. ============
  7062. AdjustDamageAmount
  7063. Modifies the previously calculated damage to adjust for more factors.
  7064. ============
  7065. */
  7066. int idPlayer::AdjustDamageAmount( const int inputDamage ) {
  7067. int outputDamage = inputDamage;
  7068. if ( inputDamage > 0 ) {
  7069. if ( !common->IsMultiplayer() ) {
  7070. float scale = new_g_damageScale;
  7071. if ( g_useDynamicProtection.GetBool() && g_skill.GetInteger() < 2 ) {
  7072. if ( gameLocal.time > lastDmgTime + 500 && scale > 0.25f ) {
  7073. scale -= 0.05f;
  7074. new_g_damageScale = scale;
  7075. }
  7076. }
  7077. if ( scale > 0.0f ) {
  7078. outputDamage *= scale;
  7079. }
  7080. }
  7081. if ( g_demoMode.GetBool() ) {
  7082. outputDamage /= 2;
  7083. }
  7084. if ( outputDamage < 1 ) {
  7085. outputDamage = 1;
  7086. }
  7087. }
  7088. return outputDamage;
  7089. }
  7090. /*
  7091. ============
  7092. ServerDealDamage
  7093. Only called on the server and in singleplayer. This is where
  7094. the player's health is actually modified, but the visual and
  7095. sound effects happen elsewhere so that clients can get instant
  7096. feedback and hide lag.
  7097. ============
  7098. */
  7099. void idPlayer::ServerDealDamage( int damage, idEntity & inflictor, idEntity & attacker, const idVec3 & dir, const char * damageDefName, const int location ) {
  7100. assert( !common->IsClient() );
  7101. const idDeclEntityDef *damageDef = gameLocal.FindEntityDef( damageDefName, false );
  7102. if ( !damageDef ) {
  7103. gameLocal.Warning( "Unknown damageDef '%s'", damageDefName );
  7104. return;
  7105. }
  7106. // move the world direction vector to local coordinates
  7107. idVec3 damage_from;
  7108. idVec3 localDamageVector;
  7109. damage_from = dir;
  7110. damage_from.Normalize();
  7111. viewAxis.ProjectVector( damage_from, localDamageVector );
  7112. // add to the damage inflicted on a player this frame
  7113. // the total will be turned into screen blends and view angle kicks
  7114. // at the end of the frame
  7115. if ( health > 0 ) {
  7116. playerView.DamageImpulse( localDamageVector, &damageDef->dict );
  7117. }
  7118. // do the damage
  7119. if ( damage > 0 ) {
  7120. GetAchievementManager().SetPlayerTookDamage( true );
  7121. int oldHealth = health;
  7122. health -= damage;
  7123. if ( health <= 0 ) {
  7124. if ( health < -999 ) {
  7125. health = -999;
  7126. }
  7127. // HACK - A - LICIOUS - Check to see if we are being damaged by the frag chamber.
  7128. if ( oldHealth > 0 && strcmp( gameLocal.GetMapName(), "maps/game/mp/d3dm3.map" ) == 0 && strcmp( damageDefName, "damage_triggerhurt_1000_chamber" ) == 0 ) {
  7129. idPlayer * fragChamberActivator = gameLocal.playerActivateFragChamber;
  7130. if ( fragChamberActivator != NULL ) {
  7131. fragChamberActivator->GetAchievementManager().EventCompletesAchievement( ACHIEVEMENT_MP_CATCH_ENEMY_IN_ROFC );
  7132. }
  7133. gameLocal.playerActivateFragChamber = NULL;
  7134. }
  7135. isTelefragged = damageDef->dict.GetBool( "telefrag" );
  7136. lastDmgTime = gameLocal.time;
  7137. Killed( &inflictor, &attacker, damage, dir, location );
  7138. } else {
  7139. if ( !g_testDeath.GetBool() ) {
  7140. lastDmgTime = gameLocal.time;
  7141. }
  7142. }
  7143. } else {
  7144. // don't accumulate impulses
  7145. if ( af.IsLoaded() ) {
  7146. // clear impacts
  7147. af.Rest();
  7148. // physics is turned off by calling af.Rest()
  7149. BecomeActive( TH_PHYSICS );
  7150. }
  7151. }
  7152. }
  7153. /*
  7154. ============
  7155. Damage
  7156. this entity that is being damaged
  7157. inflictor entity that is causing the damage
  7158. attacker entity that caused the inflictor to damage targ
  7159. example: this=monster, inflictor=rocket, attacker=player
  7160. dir direction of the attack for knockback in global space
  7161. damageDef an idDict with all the options for damage effects
  7162. inflictor, attacker, dir, and point can be NULL for environmental effects
  7163. ============
  7164. */
  7165. void idPlayer::Damage( idEntity *inflictor, idEntity *attacker, const idVec3 &dir,
  7166. const char *damageDefName, const float damageScale, const int location ) {
  7167. idVec3 kick;
  7168. int damage;
  7169. int armorSave;
  7170. SetTimeState ts( timeGroup );
  7171. if ( !fl.takedamage || noclip || spectating || gameLocal.inCinematic ) {
  7172. return;
  7173. }
  7174. if ( !inflictor ) {
  7175. inflictor = gameLocal.world;
  7176. }
  7177. if ( !attacker ) {
  7178. attacker = gameLocal.world;
  7179. }
  7180. if ( attacker->IsType( idAI::Type ) ) {
  7181. if ( PowerUpActive( BERSERK ) ) {
  7182. return;
  7183. }
  7184. // don't take damage from monsters during influences
  7185. if ( influenceActive != 0 ) {
  7186. return;
  7187. }
  7188. }
  7189. const idDeclEntityDef *damageDef = gameLocal.FindEntityDef( damageDefName, false );
  7190. if ( !damageDef ) {
  7191. gameLocal.Warning( "Unknown damageDef '%s'", damageDefName );
  7192. return;
  7193. }
  7194. if ( damageDef->dict.GetBool( "ignore_player" ) ) {
  7195. return;
  7196. }
  7197. // determine knockback
  7198. int knockback = 0;
  7199. damageDef->dict.GetInt( "knockback", "20", knockback );
  7200. if ( knockback != 0 && !fl.noknockback ) {
  7201. float attackerPushScale = 0.0f;
  7202. if ( attacker == this ) {
  7203. damageDef->dict.GetFloat( "attackerPushScale", "0", attackerPushScale );
  7204. } else {
  7205. attackerPushScale = 1.0f;
  7206. }
  7207. idVec3 kick = dir;
  7208. kick.Normalize();
  7209. kick *= g_knockback.GetFloat() * knockback * attackerPushScale / 200.0f;
  7210. physicsObj.SetLinearVelocity( physicsObj.GetLinearVelocity() + kick );
  7211. // set the timer so that the player can't cancel out the movement immediately
  7212. physicsObj.SetKnockBack( idMath::ClampInt( 50, 200, knockback * 2 ) );
  7213. if ( common->IsServer() ) {
  7214. idBitMsg msg;
  7215. byte msgBuf[MAX_EVENT_PARAM_SIZE];
  7216. msg.InitWrite( msgBuf, sizeof( msgBuf ) );
  7217. msg.WriteFloat( physicsObj.GetLinearVelocity()[0] );
  7218. msg.WriteFloat( physicsObj.GetLinearVelocity()[1] );
  7219. msg.WriteFloat( physicsObj.GetLinearVelocity()[2] );
  7220. msg.WriteByte( idMath::ClampInt( 50, 200, knockback * 2 ) );
  7221. ServerSendEvent( idPlayer::EVENT_KNOCKBACK, &msg, false );
  7222. }
  7223. }
  7224. // If this is a locally controlled MP client, don't apply damage effects predictively here.
  7225. // Local clients will see the damage feedback (view kick, etc) when their health changes
  7226. // in a snapshot. This ensures that any feedback the local player sees is in sync with
  7227. // his actual health reported by the server.
  7228. if( common->IsMultiplayer() && common->IsClient() && IsLocallyControlled() ) {
  7229. return;
  7230. }
  7231. CalcDamagePoints( inflictor, attacker, &damageDef->dict, damageScale, location, &damage, &armorSave );
  7232. // give feedback on the player view and audibly when armor is helping
  7233. if ( armorSave ) {
  7234. inventory.armor -= armorSave;
  7235. if ( gameLocal.time > lastArmorPulse + 200 ) {
  7236. StartSound( "snd_hitArmor", SND_CHANNEL_ITEM, 0, false, NULL );
  7237. }
  7238. lastArmorPulse = gameLocal.time;
  7239. }
  7240. if ( damageDef->dict.GetBool( "burn" ) ) {
  7241. StartSound( "snd_burn", SND_CHANNEL_BODY3, 0, false, NULL );
  7242. } else if ( damageDef->dict.GetBool( "no_air" ) ) {
  7243. if ( !armorSave && health > 0 ) {
  7244. StartSound( "snd_airGasp", SND_CHANNEL_ITEM, 0, false, NULL );
  7245. }
  7246. }
  7247. if ( g_debugDamage.GetInteger() ) {
  7248. gameLocal.Printf( "client:%02d\tdamage type:%s\t\thealth:%03d\tdamage:%03d\tarmor:%03d\n", entityNumber, damageDef->GetName(), health, damage, armorSave );
  7249. }
  7250. if ( common->IsMultiplayer() && IsLocallyControlled() ) {
  7251. ControllerShakeFromDamage( damage );
  7252. }
  7253. // The client needs to know the final damage amount for predictive pain animations.
  7254. const int finalDamage = AdjustDamageAmount( damage );
  7255. if ( health > 0 ) {
  7256. // force a blink
  7257. blink_time = 0;
  7258. // let the anim script know we took damage
  7259. AI_PAIN = Pain( inflictor, attacker, damage, dir, location );
  7260. }
  7261. // Only actually deal the damage here in singleplayer and for locally controlled servers.
  7262. if ( !common->IsMultiplayer() || common->IsServer() ) {
  7263. // Server will deal his damage normally
  7264. ServerDealDamage( finalDamage, *inflictor, *attacker, dir, damageDefName, location );
  7265. } else if ( attacker->GetEntityNumber() == gameLocal.GetLocalClientNum() ) {
  7266. // Clients send a reliable message to the server with the parameters of the hit. The
  7267. // server should make sure the client still has line-of-sight to its target before
  7268. // actually applying the damage.
  7269. byte msgBuffer[MAX_GAME_MESSAGE_SIZE];
  7270. idBitMsg msg;
  7271. msg.InitWrite( msgBuffer, sizeof( msgBuffer ) );
  7272. msg.BeginWriting();
  7273. msg.WriteShort( attacker->GetEntityNumber() );
  7274. msg.WriteShort( GetEntityNumber() ); // victim
  7275. msg.WriteVectorFloat( dir );
  7276. msg.WriteLong( damageDef->Index() );
  7277. msg.WriteFloat( damageScale );
  7278. msg.WriteLong( location );
  7279. idLobbyBase & lobby = session->GetActingGameStateLobbyBase();
  7280. lobby.SendReliableToHost( GAME_RELIABLE_MESSAGE_CLIENT_HITSCAN_HIT, msg );
  7281. }
  7282. lastDamageDef = damageDef->Index();
  7283. lastDamageDir = dir;
  7284. lastDamageDir.Normalize();
  7285. lastDamageLocation = location;
  7286. }
  7287. /*
  7288. ===========
  7289. idPlayer::Teleport
  7290. ============
  7291. */
  7292. void idPlayer::Teleport( const idVec3 &origin, const idAngles &angles, idEntity *destination ) {
  7293. idVec3 org;
  7294. if ( weapon.GetEntity() ) {
  7295. weapon.GetEntity()->LowerWeapon();
  7296. }
  7297. SetOrigin( origin + idVec3( 0, 0, CM_CLIP_EPSILON ) );
  7298. if ( !common->IsMultiplayer() && GetFloorPos( 16.0f, org ) ) {
  7299. SetOrigin( org );
  7300. }
  7301. // clear the ik heights so model doesn't appear in the wrong place
  7302. walkIK.EnableAll();
  7303. GetPhysics()->SetLinearVelocity( vec3_origin );
  7304. SetViewAngles( angles );
  7305. legsYaw = 0.0f;
  7306. idealLegsYaw = 0.0f;
  7307. oldViewYaw = viewAngles.yaw;
  7308. if ( common->IsMultiplayer() ) {
  7309. playerView.Flash( colorWhite, 140 );
  7310. }
  7311. UpdateVisuals();
  7312. teleportEntity = destination;
  7313. if ( !common->IsClient() && !noclip ) {
  7314. if ( common->IsMultiplayer() ) {
  7315. // kill anything at the new position or mark for kill depending on immediate or delayed teleport
  7316. gameLocal.KillBox( this, destination != NULL );
  7317. } else {
  7318. // kill anything at the new position
  7319. gameLocal.KillBox( this, true );
  7320. }
  7321. }
  7322. if ( PowerUpActive( HELLTIME ) ) {
  7323. StopHelltime();
  7324. }
  7325. }
  7326. /*
  7327. ====================
  7328. idPlayer::SetPrivateCameraView
  7329. ====================
  7330. */
  7331. void idPlayer::SetPrivateCameraView( idCamera *camView ) {
  7332. privateCameraView = camView;
  7333. if ( camView ) {
  7334. StopFiring();
  7335. Hide();
  7336. } else {
  7337. if ( !spectating ) {
  7338. Show();
  7339. }
  7340. }
  7341. }
  7342. /*
  7343. ====================
  7344. idPlayer::DefaultFov
  7345. Returns the base FOV
  7346. ====================
  7347. */
  7348. float idPlayer::DefaultFov() const {
  7349. float fov;
  7350. fov = g_fov.GetFloat();
  7351. if ( common->IsMultiplayer() ) {
  7352. if ( fov < 80.0f ) {
  7353. return 80.0f;
  7354. } else if ( fov > 120.0f ) {
  7355. return 120.0f;
  7356. }
  7357. }
  7358. return fov;
  7359. }
  7360. /*
  7361. ====================
  7362. idPlayer::CalcFov
  7363. Fixed fov at intermissions, otherwise account for fov variable and zooms.
  7364. ====================
  7365. */
  7366. float idPlayer::CalcFov( bool honorZoom ) {
  7367. float fov;
  7368. if ( fxFov ) {
  7369. return DefaultFov() + 10.0f + cos( ( gameLocal.time + 2000 ) * 0.01 ) * 10.0f;
  7370. }
  7371. if ( influenceFov ) {
  7372. return influenceFov;
  7373. }
  7374. if ( zoomFov.IsDone( gameLocal.time ) ) {
  7375. fov = ( honorZoom && usercmd.buttons & BUTTON_ZOOM ) && weapon.GetEntity() ? weapon.GetEntity()->GetZoomFov() : DefaultFov();
  7376. } else {
  7377. fov = zoomFov.GetCurrentValue( gameLocal.time );
  7378. }
  7379. // bound normal viewsize
  7380. if ( fov < 1 ) {
  7381. fov = 1;
  7382. } else if ( fov > 179 ) {
  7383. fov = 179;
  7384. }
  7385. return fov;
  7386. }
  7387. /*
  7388. ==============
  7389. idPlayer::GunTurningOffset
  7390. generate a rotational offset for the gun based on the view angle
  7391. history in loggedViewAngles
  7392. ==============
  7393. */
  7394. idAngles idPlayer::GunTurningOffset() {
  7395. idAngles a;
  7396. a.Zero();
  7397. if ( gameLocal.framenum < NUM_LOGGED_VIEW_ANGLES ) {
  7398. return a;
  7399. }
  7400. idAngles current = loggedViewAngles[ gameLocal.framenum & (NUM_LOGGED_VIEW_ANGLES-1) ];
  7401. idAngles av, base;
  7402. int weaponAngleOffsetAverages;
  7403. float weaponAngleOffsetScale, weaponAngleOffsetMax;
  7404. weapon.GetEntity()->GetWeaponAngleOffsets( &weaponAngleOffsetAverages, &weaponAngleOffsetScale, &weaponAngleOffsetMax );
  7405. av = current;
  7406. // calcualte this so the wrap arounds work properly
  7407. for ( int j = 1 ; j < weaponAngleOffsetAverages ; j++ ) {
  7408. idAngles a2 = loggedViewAngles[ ( gameLocal.framenum - j ) & (NUM_LOGGED_VIEW_ANGLES-1) ];
  7409. idAngles delta = a2 - current;
  7410. if ( delta[1] > 180 ) {
  7411. delta[1] -= 360;
  7412. } else if ( delta[1] < -180 ) {
  7413. delta[1] += 360;
  7414. }
  7415. av += delta * ( 1.0f / weaponAngleOffsetAverages );
  7416. }
  7417. a = ( av - current ) * weaponAngleOffsetScale;
  7418. for ( int i = 0 ; i < 3 ; i++ ) {
  7419. if ( a[i] < -weaponAngleOffsetMax ) {
  7420. a[i] = -weaponAngleOffsetMax;
  7421. } else if ( a[i] > weaponAngleOffsetMax ) {
  7422. a[i] = weaponAngleOffsetMax;
  7423. }
  7424. }
  7425. return a;
  7426. }
  7427. /*
  7428. ==============
  7429. idPlayer::GunAcceleratingOffset
  7430. generate a positional offset for the gun based on the movement
  7431. history in loggedAccelerations
  7432. ==============
  7433. */
  7434. idVec3 idPlayer::GunAcceleratingOffset() {
  7435. idVec3 ofs;
  7436. float weaponOffsetTime, weaponOffsetScale;
  7437. ofs.Zero();
  7438. weapon.GetEntity()->GetWeaponTimeOffsets( &weaponOffsetTime, &weaponOffsetScale );
  7439. int stop = currentLoggedAccel - NUM_LOGGED_ACCELS;
  7440. if ( stop < 0 ) {
  7441. stop = 0;
  7442. }
  7443. for ( int i = currentLoggedAccel-1 ; i > stop ; i-- ) {
  7444. loggedAccel_t *acc = &loggedAccel[i&(NUM_LOGGED_ACCELS-1)];
  7445. float f;
  7446. float t = gameLocal.time - acc->time;
  7447. if ( t >= weaponOffsetTime ) {
  7448. break; // remainder are too old to care about
  7449. }
  7450. f = t / weaponOffsetTime;
  7451. f = ( cos( f * 2.0f * idMath::PI ) - 1.0f ) * 0.5f;
  7452. ofs += f * weaponOffsetScale * acc->dir;
  7453. }
  7454. return ofs;
  7455. }
  7456. /*
  7457. ==============
  7458. idPlayer::CalculateViewWeaponPos
  7459. Calculate the bobbing position of the view weapon
  7460. ==============
  7461. */
  7462. void idPlayer::CalculateViewWeaponPos( idVec3 &origin, idMat3 &axis ) {
  7463. float scale;
  7464. float fracsin;
  7465. idAngles angles;
  7466. int delta;
  7467. // CalculateRenderView must have been called first
  7468. const idVec3 &viewOrigin = firstPersonViewOrigin;
  7469. const idMat3 &viewAxis = firstPersonViewAxis;
  7470. // these cvars are just for hand tweaking before moving a value to the weapon def
  7471. idVec3 gunpos( g_gun_x.GetFloat(), g_gun_y.GetFloat(), g_gun_z.GetFloat() );
  7472. // as the player changes direction, the gun will take a small lag
  7473. idVec3 gunOfs = GunAcceleratingOffset();
  7474. origin = viewOrigin + ( gunpos + gunOfs ) * viewAxis;
  7475. // on odd legs, invert some angles
  7476. if ( bobCycle & 128 ) {
  7477. scale = -xyspeed;
  7478. } else {
  7479. scale = xyspeed;
  7480. }
  7481. // gun angles from bobbing
  7482. angles.roll = scale * bobfracsin * 0.005f;
  7483. angles.yaw = scale * bobfracsin * 0.01f;
  7484. angles.pitch = xyspeed * bobfracsin * 0.005f;
  7485. // gun angles from turning
  7486. if ( common->IsMultiplayer() ) {
  7487. idAngles offset = GunTurningOffset();
  7488. offset *= g_mpWeaponAngleScale.GetFloat();
  7489. angles += offset;
  7490. } else {
  7491. angles += GunTurningOffset();
  7492. }
  7493. idVec3 gravity = physicsObj.GetGravityNormal();
  7494. // drop the weapon when landing after a jump / fall
  7495. delta = gameLocal.time - landTime;
  7496. if ( delta < LAND_DEFLECT_TIME ) {
  7497. origin -= gravity * ( landChange*0.25f * delta / LAND_DEFLECT_TIME );
  7498. } else if ( delta < LAND_DEFLECT_TIME + LAND_RETURN_TIME ) {
  7499. origin -= gravity * ( landChange*0.25f * (LAND_DEFLECT_TIME + LAND_RETURN_TIME - delta) / LAND_RETURN_TIME );
  7500. }
  7501. // speed sensitive idle drift
  7502. scale = xyspeed + 40.0f;
  7503. fracsin = scale * sin( MS2SEC( gameLocal.time ) ) * 0.01f;
  7504. angles.roll += fracsin;
  7505. angles.yaw += fracsin;
  7506. angles.pitch += fracsin;
  7507. // decoupled weapon aiming in head mounted displays
  7508. angles.pitch += independentWeaponPitchAngle;
  7509. const idMat3 anglesMat = angles.ToMat3();
  7510. const idMat3 scaledMat = anglesMat * g_gunScale.GetFloat();
  7511. axis = scaledMat * viewAxis;
  7512. }
  7513. /*
  7514. ===============
  7515. idPlayer::OffsetThirdPersonView
  7516. ===============
  7517. */
  7518. void idPlayer::OffsetThirdPersonView( float angle, float range, float height, bool clip ) {
  7519. idVec3 view;
  7520. idVec3 focusAngles;
  7521. trace_t trace;
  7522. idVec3 focusPoint;
  7523. float focusDist;
  7524. float forwardScale, sideScale;
  7525. idVec3 origin;
  7526. idAngles angles;
  7527. idMat3 axis;
  7528. idBounds bounds;
  7529. angles = viewAngles;
  7530. GetViewPos( origin, axis );
  7531. if ( angle ) {
  7532. angles.pitch = 0.0f;
  7533. }
  7534. if ( angles.pitch > 45.0f ) {
  7535. angles.pitch = 45.0f; // don't go too far overhead
  7536. }
  7537. focusPoint = origin + angles.ToForward() * THIRD_PERSON_FOCUS_DISTANCE;
  7538. focusPoint.z += height;
  7539. view = origin;
  7540. view.z += 8 + height;
  7541. angles.pitch *= 0.5f;
  7542. renderView->viewaxis = angles.ToMat3() * physicsObj.GetGravityAxis();
  7543. idMath::SinCos( DEG2RAD( angle ), sideScale, forwardScale );
  7544. view -= range * forwardScale * renderView->viewaxis[ 0 ];
  7545. view += range * sideScale * renderView->viewaxis[ 1 ];
  7546. if ( clip ) {
  7547. // trace a ray from the origin to the viewpoint to make sure the view isn't
  7548. // in a solid block. Use an 8 by 8 block to prevent the view from near clipping anything
  7549. bounds = idBounds( idVec3( -4, -4, -4 ), idVec3( 4, 4, 4 ) );
  7550. gameLocal.clip.TraceBounds( trace, origin, view, bounds, MASK_SOLID, this );
  7551. if ( trace.fraction != 1.0f ) {
  7552. view = trace.endpos;
  7553. view.z += ( 1.0f - trace.fraction ) * 32.0f;
  7554. // try another trace to this position, because a tunnel may have the ceiling
  7555. // close enough that this is poking out
  7556. gameLocal.clip.TraceBounds( trace, origin, view, bounds, MASK_SOLID, this );
  7557. view = trace.endpos;
  7558. }
  7559. }
  7560. // select pitch to look at focus point from vieword
  7561. focusPoint -= view;
  7562. focusDist = idMath::Sqrt( focusPoint[0] * focusPoint[0] + focusPoint[1] * focusPoint[1] );
  7563. if ( focusDist < 1.0f ) {
  7564. focusDist = 1.0f; // should never happen
  7565. }
  7566. angles.pitch = - RAD2DEG( atan2( focusPoint.z, focusDist ) );
  7567. angles.yaw -= angle;
  7568. renderView->vieworg = view;
  7569. renderView->viewaxis = angles.ToMat3() * physicsObj.GetGravityAxis();
  7570. renderView->viewID = 0;
  7571. }
  7572. /*
  7573. ===============
  7574. idPlayer::GetEyePosition
  7575. ===============
  7576. */
  7577. idVec3 idPlayer::GetEyePosition() const {
  7578. idVec3 org;
  7579. // use the smoothed origin if spectating another player in multiplayer
  7580. if ( common->IsClient() && !IsLocallyControlled() ) {
  7581. org = smoothedOrigin;
  7582. } else {
  7583. org = GetPhysics()->GetOrigin();
  7584. }
  7585. return org + ( GetPhysics()->GetGravityNormal() * -eyeOffset.z );
  7586. }
  7587. /*
  7588. ===============
  7589. idPlayer::GetViewPos
  7590. ===============
  7591. */
  7592. void idPlayer::GetViewPos( idVec3 &origin, idMat3 &axis ) const {
  7593. idAngles angles;
  7594. // if dead, fix the angle and don't add any kick
  7595. if ( health <= 0 ) {
  7596. angles.yaw = viewAngles.yaw;
  7597. angles.roll = 40;
  7598. angles.pitch = -15;
  7599. axis = angles.ToMat3();
  7600. origin = GetEyePosition();
  7601. } else {
  7602. origin = GetEyePosition() + viewBob;
  7603. angles = viewAngles + viewBobAngles + playerView.AngleOffset();
  7604. axis = angles.ToMat3() * physicsObj.GetGravityAxis();
  7605. // Move pivot point down so looking straight ahead is a no-op on the Z
  7606. const idVec3 & gravityVector = physicsObj.GetGravityNormal();
  7607. origin += gravityVector * g_viewNodalZ.GetFloat();
  7608. // adjust the origin based on the camera nodal distance (eye distance from neck)
  7609. origin += axis[0] * g_viewNodalX.GetFloat() + axis[2] * g_viewNodalZ.GetFloat();
  7610. }
  7611. }
  7612. /*
  7613. ===============
  7614. idPlayer::CalculateFirstPersonView
  7615. ===============
  7616. */
  7617. void idPlayer::CalculateFirstPersonView() {
  7618. if ( ( pm_modelView.GetInteger() == 1 ) || ( ( pm_modelView.GetInteger() == 2 ) && ( health <= 0 ) ) ) {
  7619. // Displays the view from the point of view of the "camera" joint in the player model
  7620. idMat3 axis;
  7621. idVec3 origin;
  7622. idAngles ang;
  7623. ang = viewBobAngles + playerView.AngleOffset();
  7624. ang.yaw += viewAxis[ 0 ].ToYaw();
  7625. jointHandle_t joint = animator.GetJointHandle( "camera" );
  7626. animator.GetJointTransform( joint, gameLocal.time, origin, axis );
  7627. firstPersonViewOrigin = ( origin + modelOffset ) * ( viewAxis * physicsObj.GetGravityAxis() ) + physicsObj.GetOrigin() + viewBob;
  7628. firstPersonViewAxis = axis * ang.ToMat3() * physicsObj.GetGravityAxis();
  7629. } else {
  7630. // offset for local bobbing and kicks
  7631. GetViewPos( firstPersonViewOrigin, firstPersonViewAxis );
  7632. #if 0
  7633. // shakefrom sound stuff only happens in first person
  7634. firstPersonViewAxis = firstPersonViewAxis * playerView.ShakeAxis();
  7635. #endif
  7636. }
  7637. }
  7638. /*
  7639. ==================
  7640. idPlayer::GetRenderView
  7641. Returns the renderView that was calculated for this tic
  7642. ==================
  7643. */
  7644. renderView_t *idPlayer::GetRenderView() {
  7645. return renderView;
  7646. }
  7647. /*
  7648. ==================
  7649. idPlayer::CalculateRenderView
  7650. create the renderView for the current tic
  7651. ==================
  7652. */
  7653. void idPlayer::CalculateRenderView() {
  7654. int i;
  7655. float range;
  7656. if ( !renderView ) {
  7657. renderView = new (TAG_ENTITY) renderView_t;
  7658. }
  7659. memset( renderView, 0, sizeof( *renderView ) );
  7660. // copy global shader parms
  7661. for( i = 0; i < MAX_GLOBAL_SHADER_PARMS; i++ ) {
  7662. renderView->shaderParms[ i ] = gameLocal.globalShaderParms[ i ];
  7663. }
  7664. renderView->globalMaterial = gameLocal.GetGlobalMaterial();
  7665. renderView->time[0] = gameLocal.slow.time;
  7666. renderView->time[1] = gameLocal.fast.time;
  7667. renderView->viewID = 0;
  7668. // check if we should be drawing from a camera's POV
  7669. if ( !noclip && (gameLocal.GetCamera() || privateCameraView) ) {
  7670. // get origin, axis, and fov
  7671. if ( privateCameraView ) {
  7672. privateCameraView->GetViewParms( renderView );
  7673. } else {
  7674. gameLocal.GetCamera()->GetViewParms( renderView );
  7675. }
  7676. } else {
  7677. if ( g_stopTime.GetBool() ) {
  7678. renderView->vieworg = firstPersonViewOrigin;
  7679. renderView->viewaxis = firstPersonViewAxis;
  7680. if ( !pm_thirdPerson.GetBool() ) {
  7681. // set the viewID to the clientNum + 1, so we can suppress the right player bodies and
  7682. // allow the right player view weapons
  7683. renderView->viewID = entityNumber + 1;
  7684. }
  7685. } else if ( pm_thirdPerson.GetBool() ) {
  7686. OffsetThirdPersonView( pm_thirdPersonAngle.GetFloat(), pm_thirdPersonRange.GetFloat(), pm_thirdPersonHeight.GetFloat(), pm_thirdPersonClip.GetBool() );
  7687. } else if ( pm_thirdPersonDeath.GetBool() ) {
  7688. range = gameLocal.time < minRespawnTime ? ( gameLocal.time + RAGDOLL_DEATH_TIME - minRespawnTime ) * ( 120.0f / RAGDOLL_DEATH_TIME ) : 120.0f;
  7689. OffsetThirdPersonView( 0.0f, 20.0f + range, 0.0f, false );
  7690. } else {
  7691. renderView->vieworg = firstPersonViewOrigin;
  7692. renderView->viewaxis = firstPersonViewAxis;
  7693. // set the viewID to the clientNum + 1, so we can suppress the right player bodies and
  7694. // allow the right player view weapons
  7695. renderView->viewID = entityNumber + 1;
  7696. }
  7697. gameLocal.CalcFov( CalcFov( true ), renderView->fov_x, renderView->fov_y );
  7698. }
  7699. if ( renderView->fov_y == 0 ) {
  7700. common->Error( "renderView->fov_y == 0" );
  7701. }
  7702. if ( g_showviewpos.GetBool() ) {
  7703. gameLocal.Printf( "%s : %s\n", renderView->vieworg.ToString(), renderView->viewaxis.ToAngles().ToString() );
  7704. }
  7705. }
  7706. /*
  7707. =============
  7708. idPlayer::AddAIKill
  7709. =============
  7710. */
  7711. void idPlayer::AddAIKill() {
  7712. int max_souls;
  7713. int ammo_souls;
  7714. if ( ( weapon_soulcube < 0 ) || ( inventory.weapons & ( 1 << weapon_soulcube ) ) == 0 ) {
  7715. return;
  7716. }
  7717. ammo_souls = idWeapon::GetAmmoNumForName( "ammo_souls" );
  7718. max_souls = inventory.MaxAmmoForAmmoClass( this, "ammo_souls" );
  7719. const int currentSoulAmmo = inventory.GetInventoryAmmoForType( ammo_souls );
  7720. if ( currentSoulAmmo < max_souls ) {
  7721. inventory.SetInventoryAmmoForType( ammo_souls, currentSoulAmmo + 1 );
  7722. if ( inventory.GetInventoryAmmoForType( ammo_souls ) >= max_souls ) {
  7723. if ( hud ) {
  7724. hud->UpdateSoulCube( true );
  7725. }
  7726. StartSound( "snd_soulcube_ready", SND_CHANNEL_ANY, 0, false, NULL );
  7727. }
  7728. }
  7729. }
  7730. /*
  7731. =============
  7732. idPlayer::SetSoulCubeProjectile
  7733. =============
  7734. */
  7735. void idPlayer::SetSoulCubeProjectile( idProjectile *projectile ) {
  7736. soulCubeProjectile = projectile;
  7737. }
  7738. /*
  7739. =============
  7740. idPlayer::AddProjectilesFired
  7741. =============
  7742. */
  7743. void idPlayer::AddProjectilesFired( int count ) {
  7744. numProjectilesFired += count;
  7745. }
  7746. /*
  7747. =============
  7748. idPlayer::AddProjectileHites
  7749. =============
  7750. */
  7751. void idPlayer::AddProjectileHits( int count ) {
  7752. numProjectileHits += count;
  7753. }
  7754. /*
  7755. =============
  7756. idPlayer::SetLastHitTime
  7757. =============
  7758. */
  7759. void idPlayer::SetLastHitTime( int time ) {
  7760. idPlayer *aimed = NULL;
  7761. if ( time && lastHitTime != time ) {
  7762. lastHitToggle ^= 1;
  7763. }
  7764. lastHitTime = time;
  7765. if ( !time ) {
  7766. // level start and inits
  7767. return;
  7768. }
  7769. if ( common->IsMultiplayer() && ( time - lastSndHitTime ) > 10 ) {
  7770. lastSndHitTime = time;
  7771. StartSound( "snd_hit_feedback", SND_CHANNEL_ANY, SSF_PRIVATE_SOUND, false, NULL );
  7772. }
  7773. if ( hud ) {
  7774. hud->CombatCursorFlash();
  7775. }
  7776. if ( MPAim != -1 ) {
  7777. if ( gameLocal.entities[ MPAim ] && gameLocal.entities[ MPAim ]->IsType( idPlayer::Type ) ) {
  7778. aimed = static_cast< idPlayer * >( gameLocal.entities[ MPAim ] );
  7779. }
  7780. assert( aimed );
  7781. // full highlight, no fade till loosing aim
  7782. if ( hud ) {
  7783. int color = 0;
  7784. if ( aimed ) {
  7785. color = aimed->team + 1;
  7786. }
  7787. hud->TriggerHitTarget( true, session->GetActingGameStateLobbyBase().GetLobbyUserName( gameLocal.lobbyUserIDs[ MPAim ] ), color );
  7788. }
  7789. MPAimHighlight = true;
  7790. MPAimFadeTime = 0;
  7791. } else if ( lastMPAim != -1 ) {
  7792. if ( gameLocal.entities[ lastMPAim ] && gameLocal.entities[ lastMPAim ]->IsType( idPlayer::Type ) ) {
  7793. aimed = static_cast< idPlayer * >( gameLocal.entities[ lastMPAim ] );
  7794. }
  7795. assert( aimed );
  7796. // start fading right away
  7797. if ( hud ) {
  7798. int color = 0;
  7799. if ( aimed ) {
  7800. color = aimed->team + 1;
  7801. }
  7802. hud->TriggerHitTarget( true, session->GetActingGameStateLobbyBase().GetLobbyUserName( gameLocal.lobbyUserIDs[ lastMPAim ] ), color );
  7803. hud->TriggerHitTarget( false, "" );
  7804. }
  7805. MPAimHighlight = false;
  7806. MPAimFadeTime = gameLocal.realClientTime;
  7807. }
  7808. }
  7809. /*
  7810. =============
  7811. idPlayer::SetInfluenceLevel
  7812. =============
  7813. */
  7814. void idPlayer::SetInfluenceLevel( int level ) {
  7815. if ( level != influenceActive ) {
  7816. if ( level ) {
  7817. for ( idEntity *ent = gameLocal.spawnedEntities.Next(); ent != NULL; ent = ent->spawnNode.Next() ) {
  7818. if ( ent->IsType( idProjectile::Type ) ) {
  7819. // remove all projectiles
  7820. ent->PostEventMS( &EV_Remove, 0 );
  7821. }
  7822. }
  7823. if ( weaponEnabled && weapon.GetEntity() ) {
  7824. weapon.GetEntity()->EnterCinematic();
  7825. }
  7826. } else {
  7827. physicsObj.SetLinearVelocity( vec3_origin );
  7828. if ( weaponEnabled && weapon.GetEntity() ) {
  7829. weapon.GetEntity()->ExitCinematic();
  7830. }
  7831. }
  7832. influenceActive = level;
  7833. }
  7834. }
  7835. /*
  7836. =============
  7837. idPlayer::SetInfluenceView
  7838. =============
  7839. */
  7840. void idPlayer::SetInfluenceView( const char *mtr, const char *skinname, float radius, idEntity *ent ) {
  7841. influenceMaterial = NULL;
  7842. influenceEntity = NULL;
  7843. influenceSkin = NULL;
  7844. if ( mtr && *mtr ) {
  7845. influenceMaterial = declManager->FindMaterial( mtr );
  7846. }
  7847. if ( skinname && *skinname ) {
  7848. influenceSkin = declManager->FindSkin( skinname );
  7849. if ( head.GetEntity() ) {
  7850. head.GetEntity()->GetRenderEntity()->shaderParms[ SHADERPARM_TIMEOFFSET ] = -MS2SEC( gameLocal.time );
  7851. }
  7852. UpdateVisuals();
  7853. }
  7854. influenceRadius = radius;
  7855. if ( radius > 0.0f ) {
  7856. influenceEntity = ent;
  7857. }
  7858. }
  7859. /*
  7860. =============
  7861. idPlayer::SetInfluenceFov
  7862. =============
  7863. */
  7864. void idPlayer::SetInfluenceFov( float fov ) {
  7865. influenceFov = fov;
  7866. }
  7867. /*
  7868. ================
  7869. idPlayer::OnLadder
  7870. ================
  7871. */
  7872. bool idPlayer::OnLadder() const {
  7873. return physicsObj.OnLadder();
  7874. }
  7875. /*
  7876. ==================
  7877. idPlayer::Event_GetButtons
  7878. ==================
  7879. */
  7880. void idPlayer::Event_GetButtons() {
  7881. idThread::ReturnInt( usercmd.buttons );
  7882. }
  7883. /*
  7884. ==================
  7885. idPlayer::Event_GetMove
  7886. ==================
  7887. */
  7888. void idPlayer::Event_GetMove() {
  7889. int upmove = ( ( usercmd.buttons & BUTTON_JUMP ) ? 127 : 0 ) - ( ( usercmd.buttons & BUTTON_CROUCH ) ? 127 : 0 );
  7890. idVec3 move( usercmd.forwardmove, usercmd.rightmove, upmove );
  7891. idThread::ReturnVector( move );
  7892. }
  7893. /*
  7894. ================
  7895. idPlayer::Event_GetViewAngles
  7896. ================
  7897. */
  7898. void idPlayer::Event_GetViewAngles() {
  7899. idThread::ReturnVector( idVec3( viewAngles[0], viewAngles[1], viewAngles[2] ) );
  7900. }
  7901. /*
  7902. ==================
  7903. idPlayer::Event_StopFxFov
  7904. ==================
  7905. */
  7906. void idPlayer::Event_StopFxFov() {
  7907. fxFov = false;
  7908. }
  7909. /*
  7910. ==================
  7911. idPlayer::StartFxFov
  7912. ==================
  7913. */
  7914. void idPlayer::StartFxFov( float duration ) {
  7915. fxFov = true;
  7916. PostEventSec( &EV_Player_StopFxFov, duration );
  7917. }
  7918. /*
  7919. ==================
  7920. idPlayer::Event_EnableWeapon
  7921. ==================
  7922. */
  7923. void idPlayer::Event_EnableWeapon() {
  7924. hiddenWeapon = gameLocal.world->spawnArgs.GetBool( "no_Weapons" );
  7925. weaponEnabled = true;
  7926. if ( weapon.GetEntity() ) {
  7927. weapon.GetEntity()->ExitCinematic();
  7928. }
  7929. }
  7930. /*
  7931. ==================
  7932. idPlayer::Event_DisableWeapon
  7933. ==================
  7934. */
  7935. void idPlayer::Event_DisableWeapon() {
  7936. hiddenWeapon = gameLocal.world->spawnArgs.GetBool( "no_Weapons" );
  7937. weaponEnabled = false;
  7938. if ( weapon.GetEntity() ) {
  7939. weapon.GetEntity()->EnterCinematic();
  7940. }
  7941. }
  7942. /*
  7943. ==================
  7944. idPlayer::Event_GiveInventoryItem
  7945. ==================
  7946. */
  7947. void idPlayer::Event_GiveInventoryItem( const char* name ) {
  7948. GiveInventoryItem(name);
  7949. }
  7950. /*
  7951. ==================
  7952. idPlayer::Event_RemoveInventoryItem
  7953. ==================
  7954. */
  7955. void idPlayer::Event_RemoveInventoryItem( const char* name ) {
  7956. RemoveInventoryItem(name);
  7957. }
  7958. /*
  7959. ==================
  7960. idPlayer::Event_GetIdealWeapon
  7961. ==================
  7962. */
  7963. void idPlayer::Event_GetIdealWeapon() {
  7964. const char *weapon;
  7965. if ( idealWeapon.Get() >= 0 ) {
  7966. weapon = spawnArgs.GetString( va( "def_weapon%d", idealWeapon.Get() ) );
  7967. idThread::ReturnString( weapon );
  7968. } else {
  7969. idThread::ReturnString( "" );
  7970. }
  7971. }
  7972. /*
  7973. ==================
  7974. idPlayer::Event_SetPowerupTime
  7975. ==================
  7976. */
  7977. void idPlayer::Event_SetPowerupTime( int powerup, int time ) {
  7978. if ( time > 0 ) {
  7979. GivePowerUp( powerup, time, ITEM_GIVE_FEEDBACK | ITEM_GIVE_UPDATE_STATE );
  7980. } else {
  7981. ClearPowerup( powerup );
  7982. }
  7983. }
  7984. /*
  7985. ==================
  7986. idPlayer::Event_IsPowerupActive
  7987. ==================
  7988. */
  7989. void idPlayer::Event_IsPowerupActive( int powerup ) {
  7990. idThread::ReturnInt(this->PowerUpActive(powerup) ? 1 : 0);
  7991. }
  7992. /*
  7993. ==================
  7994. idPlayer::Event_StartWarp
  7995. ==================
  7996. */
  7997. void idPlayer::Event_StartWarp() {
  7998. playerView.AddWarp( idVec3( 0, 0, 0 ), SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, 100, 1000 );
  7999. }
  8000. /*
  8001. ==================
  8002. idPlayer::Event_StopHelltime
  8003. ==================
  8004. */
  8005. void idPlayer::Event_StopHelltime( int mode ) {
  8006. if ( mode == 1 ) {
  8007. StopHelltime( true );
  8008. }
  8009. else {
  8010. StopHelltime( false );
  8011. }
  8012. }
  8013. /*
  8014. ==================
  8015. idPlayer::Event_WeaponAvailable
  8016. ==================
  8017. */
  8018. void idPlayer::Event_WeaponAvailable( const char* name ) {
  8019. idThread::ReturnInt( WeaponAvailable(name) ? 1 : 0 );
  8020. }
  8021. bool idPlayer::WeaponAvailable( const char* name ) {
  8022. for( int i = 0; i < MAX_WEAPONS; i++ ) {
  8023. if ( inventory.weapons & ( 1 << i ) ) {
  8024. const char *weap = spawnArgs.GetString( va( "def_weapon%d", i ) );
  8025. if ( !idStr::Cmp( weap, name ) ) {
  8026. return true;
  8027. }
  8028. }
  8029. }
  8030. return false;
  8031. }
  8032. /*
  8033. ==================
  8034. idPlayer::Event_GetCurrentWeapon
  8035. ==================
  8036. */
  8037. void idPlayer::Event_GetCurrentWeapon() {
  8038. const char *weapon;
  8039. if ( currentWeapon >= 0 ) {
  8040. weapon = spawnArgs.GetString( va( "def_weapon%d", currentWeapon ) );
  8041. idThread::ReturnString( weapon );
  8042. } else {
  8043. idThread::ReturnString( "" );
  8044. }
  8045. }
  8046. /*
  8047. ==================
  8048. idPlayer::Event_GetPreviousWeapon
  8049. ==================
  8050. */
  8051. void idPlayer::Event_GetPreviousWeapon() {
  8052. const char *weapon;
  8053. if ( previousWeapon >= 0 ) {
  8054. int pw = ( gameLocal.world->spawnArgs.GetBool( "no_Weapons" ) ) ? 0 : previousWeapon;
  8055. weapon = spawnArgs.GetString( va( "def_weapon%d", pw) );
  8056. idThread::ReturnString( weapon );
  8057. } else {
  8058. idThread::ReturnString( spawnArgs.GetString( "def_weapon0" ) );
  8059. }
  8060. }
  8061. /*
  8062. ==================
  8063. idPlayer::Event_SelectWeapon
  8064. ==================
  8065. */
  8066. void idPlayer::Event_SelectWeapon( const char *weaponName ) {
  8067. int i;
  8068. int weaponNum;
  8069. if ( common->IsClient() ) {
  8070. gameLocal.Warning( "Cannot switch weapons from script in multiplayer" );
  8071. return;
  8072. }
  8073. if ( hiddenWeapon && gameLocal.world->spawnArgs.GetBool( "no_Weapons" ) ) {
  8074. idealWeapon = weapon_fists;
  8075. weapon.GetEntity()->HideWeapon();
  8076. return;
  8077. }
  8078. weaponNum = -1;
  8079. for( i = 0; i < MAX_WEAPONS; i++ ) {
  8080. if ( inventory.weapons & ( 1 << i ) ) {
  8081. const char *weap = spawnArgs.GetString( va( "def_weapon%d", i ) );
  8082. if ( !idStr::Cmp( weap, weaponName ) ) {
  8083. weaponNum = i;
  8084. break;
  8085. }
  8086. }
  8087. }
  8088. if ( weaponNum < 0 ) {
  8089. gameLocal.Warning( "%s is not carrying weapon '%s'", name.c_str(), weaponName );
  8090. return;
  8091. }
  8092. hiddenWeapon = false;
  8093. idealWeapon = weaponNum;
  8094. UpdateHudWeapon();
  8095. }
  8096. /*
  8097. ==================
  8098. idPlayer::Event_GetWeaponEntity
  8099. ==================
  8100. */
  8101. void idPlayer::Event_GetWeaponEntity() {
  8102. idThread::ReturnEntity( weapon.GetEntity() );
  8103. }
  8104. /*
  8105. ==================
  8106. idPlayer::Event_OpenPDA
  8107. ==================
  8108. */
  8109. void idPlayer::Event_OpenPDA() {
  8110. if ( !common->IsMultiplayer() ) {
  8111. TogglePDA();
  8112. }
  8113. }
  8114. /*
  8115. ==================
  8116. idPlayer::Event_InPDA
  8117. ==================
  8118. */
  8119. void idPlayer::Event_InPDA() {
  8120. idThread::ReturnInt( objectiveSystemOpen );
  8121. }
  8122. /*
  8123. ==================
  8124. idPlayer::TeleportDeath
  8125. ==================
  8126. */
  8127. void idPlayer::TeleportDeath( int killer ) {
  8128. teleportKiller = killer;
  8129. }
  8130. /*
  8131. ==================
  8132. idPlayer::Event_ExitTeleporter
  8133. ==================
  8134. */
  8135. void idPlayer::Event_ForceOrigin( idVec3 & origin, idAngles & angles ) {
  8136. SetOrigin( origin + idVec3( 0, 0, CM_CLIP_EPSILON ) );
  8137. //SetViewAngles( angles );
  8138. UpdateVisuals();
  8139. }
  8140. /*
  8141. ==================
  8142. idPlayer::Event_ExitTeleporter
  8143. ==================
  8144. */
  8145. void idPlayer::Event_ExitTeleporter() {
  8146. idEntity *exitEnt;
  8147. float pushVel;
  8148. // verify and setup
  8149. exitEnt = teleportEntity.GetEntity();
  8150. if ( !exitEnt ) {
  8151. common->DPrintf( "Event_ExitTeleporter player %d while not being teleported\n", entityNumber );
  8152. return;
  8153. }
  8154. pushVel = exitEnt->spawnArgs.GetFloat( "push", "300" );
  8155. if ( common->IsServer() ) {
  8156. ServerSendEvent( EVENT_EXIT_TELEPORTER, NULL, false );
  8157. }
  8158. SetPrivateCameraView( NULL );
  8159. // setup origin and push according to the exit target
  8160. SetOrigin( exitEnt->GetPhysics()->GetOrigin() + idVec3( 0, 0, CM_CLIP_EPSILON ) );
  8161. SetViewAngles( exitEnt->GetPhysics()->GetAxis().ToAngles() );
  8162. physicsObj.SetLinearVelocity( exitEnt->GetPhysics()->GetAxis()[ 0 ] * pushVel );
  8163. physicsObj.ClearPushedVelocity();
  8164. // teleport fx
  8165. playerView.Flash( colorWhite, 120 );
  8166. // clear the ik heights so model doesn't appear in the wrong place
  8167. walkIK.EnableAll();
  8168. UpdateVisuals();
  8169. StartSound( "snd_teleport_exit", SND_CHANNEL_ANY, 0, false, NULL );
  8170. if ( teleportKiller != -1 ) {
  8171. // we got killed while being teleported
  8172. Damage( gameLocal.entities[ teleportKiller ], gameLocal.entities[ teleportKiller ], vec3_origin, "damage_telefrag", 1.0f, INVALID_JOINT );
  8173. teleportKiller = -1;
  8174. } else {
  8175. // kill anything that would have waited at teleport exit
  8176. gameLocal.KillBox( this );
  8177. }
  8178. teleportEntity = NULL;
  8179. }
  8180. /*
  8181. ================
  8182. idPlayer::ClientThink
  8183. ================
  8184. */
  8185. void idPlayer::ClientThink( const int curTime, const float fraction, const bool predict ) {
  8186. if ( IsLocallyControlled() ) {
  8187. aimAssist.Update();
  8188. }
  8189. UpdateSkinSetup();
  8190. if ( !IsLocallyControlled() ) {
  8191. // ignore attack button of other clients. that's no good for predictions
  8192. usercmd.buttons &= ~BUTTON_ATTACK;
  8193. }
  8194. buttonMask &= usercmd.buttons;
  8195. usercmd.buttons &= ~buttonMask;
  8196. buttonMask &= usercmd.buttons;
  8197. usercmd.buttons &= ~buttonMask;
  8198. if ( mountedObject ) {
  8199. usercmd.forwardmove = 0;
  8200. usercmd.rightmove = 0;
  8201. usercmd.buttons &= ~(BUTTON_JUMP|BUTTON_CROUCH);
  8202. }
  8203. if ( objectiveSystemOpen ) {
  8204. usercmd.forwardmove = 0;
  8205. usercmd.rightmove = 0;
  8206. usercmd.buttons &= ~(BUTTON_JUMP|BUTTON_CROUCH);
  8207. }
  8208. if ( IsLocallyControlled() ) {
  8209. // zooming
  8210. if ( ( usercmd.buttons ^ oldCmd.buttons ) & BUTTON_ZOOM ) {
  8211. if ( ( usercmd.buttons & BUTTON_ZOOM ) && weapon.GetEntity() ) {
  8212. zoomFov.Init( gameLocal.time, 200.0f, CalcFov( false ), weapon.GetEntity()->GetZoomFov() );
  8213. } else {
  8214. zoomFov.Init( gameLocal.time, 200.0f, zoomFov.GetCurrentValue( gameLocal.time ), DefaultFov() );
  8215. }
  8216. }
  8217. }
  8218. // clear the ik before we do anything else so the skeleton doesn't get updated twice
  8219. walkIK.ClearJointMods();
  8220. if ( gameLocal.isNewFrame ) {
  8221. if ( usercmd.impulseSequence != oldImpulseSequence ) {
  8222. PerformImpulse( usercmd.impulse );
  8223. }
  8224. }
  8225. if ( forceScoreBoard ) {
  8226. gameLocal.mpGame.SetScoreboardActive( true );
  8227. }
  8228. AdjustSpeed();
  8229. if ( IsLocallyControlled() ) {
  8230. UpdateViewAngles();
  8231. } else {
  8232. idQuat interpolatedAngles = Slerp( previousViewQuat, nextViewQuat, fraction );
  8233. viewAngles = interpolatedAngles.ToAngles();
  8234. }
  8235. smoothedOriginUpdated = false;
  8236. if ( !af.IsActive() ) {
  8237. AdjustBodyAngles();
  8238. }
  8239. if ( !isLagged ) {
  8240. // don't allow client to move when lagged
  8241. if ( IsLocallyControlled() ) {
  8242. // Locally-controlled clients are authoritative on their positions, so they can move normally.
  8243. Move();
  8244. usercmd.pos = physicsObj.GetOrigin();
  8245. } else {
  8246. // Non-locally controlled players are interpolated.
  8247. Move_Interpolated(fraction);
  8248. }
  8249. }
  8250. if ( !g_stopTime.GetBool() ) {
  8251. if ( !noclip && !spectating && ( health > 0 ) && !IsHidden() ) {
  8252. TouchTriggers();
  8253. }
  8254. }
  8255. // update GUIs, Items, and character interactions
  8256. UpdateFocus();
  8257. // service animations
  8258. if ( !spectating && !af.IsActive() ) {
  8259. UpdateConditions();
  8260. UpdateAnimState();
  8261. CheckBlink();
  8262. }
  8263. // clear out our pain flag so we can tell if we recieve any damage between now and the next time we think
  8264. AI_PAIN = false;
  8265. UpdateLocation();
  8266. // calculate the exact bobbed view position, which is used to
  8267. // position the view weapon, among other things
  8268. CalculateFirstPersonView();
  8269. // this may use firstPersonView, or a thirdPerson / camera view
  8270. CalculateRenderView();
  8271. if ( !gameLocal.inCinematic && weapon.GetEntity() && ( health > 0 ) && !( common->IsMultiplayer() && spectating ) ) {
  8272. UpdateWeapon();
  8273. }
  8274. UpdateFlashlight();
  8275. UpdateHud();
  8276. if ( gameLocal.isNewFrame ) {
  8277. UpdatePowerUps();
  8278. }
  8279. UpdateDeathSkin( false );
  8280. renderEntity_t * headRenderEnt = NULL;
  8281. if ( head.GetEntity() ) {
  8282. headRenderEnt = head.GetEntity()->GetRenderEntity();
  8283. }
  8284. if ( headRenderEnt ) {
  8285. if ( influenceSkin ) {
  8286. headRenderEnt->customSkin = influenceSkin;
  8287. } else {
  8288. headRenderEnt->customSkin = NULL;
  8289. }
  8290. }
  8291. if ( common->IsMultiplayer() || g_showPlayerShadow.GetBool() ) {
  8292. renderEntity.suppressShadowInViewID = 0;
  8293. if ( headRenderEnt ) {
  8294. headRenderEnt->suppressShadowInViewID = 0;
  8295. }
  8296. } else {
  8297. renderEntity.suppressShadowInViewID = entityNumber+1;
  8298. if ( headRenderEnt ) {
  8299. headRenderEnt->suppressShadowInViewID = entityNumber+1;
  8300. }
  8301. }
  8302. // never cast shadows from our first-person muzzle flashes
  8303. renderEntity.suppressShadowInLightID = LIGHTID_VIEW_MUZZLE_FLASH + entityNumber;
  8304. if ( headRenderEnt ) {
  8305. headRenderEnt->suppressShadowInLightID = LIGHTID_VIEW_MUZZLE_FLASH + entityNumber;
  8306. }
  8307. if ( !gameLocal.inCinematic ) {
  8308. UpdateAnimation();
  8309. }
  8310. if ( enviroSuitLight.IsValid() ) {
  8311. idAngles lightAng = firstPersonViewAxis.ToAngles();
  8312. idVec3 lightOrg = firstPersonViewOrigin;
  8313. const idDict *lightDef = gameLocal.FindEntityDefDict( "envirosuit_light", false );
  8314. idVec3 enviroOffset = lightDef->GetVector( "enviro_offset" );
  8315. idVec3 enviroAngleOffset = lightDef->GetVector( "enviro_angle_offset" );
  8316. lightOrg += (enviroOffset.x * firstPersonViewAxis[0]);
  8317. lightOrg += (enviroOffset.y * firstPersonViewAxis[1]);
  8318. lightOrg += (enviroOffset.z * firstPersonViewAxis[2]);
  8319. lightAng.pitch += enviroAngleOffset.x;
  8320. lightAng.yaw += enviroAngleOffset.y;
  8321. lightAng.roll += enviroAngleOffset.z;
  8322. enviroSuitLight.GetEntity()->GetPhysics()->SetOrigin( lightOrg );
  8323. enviroSuitLight.GetEntity()->GetPhysics()->SetAxis( lightAng.ToMat3() );
  8324. enviroSuitLight.GetEntity()->UpdateVisuals();
  8325. enviroSuitLight.GetEntity()->Present();
  8326. }
  8327. if ( common->IsMultiplayer() ) {
  8328. DrawPlayerIcons();
  8329. }
  8330. Present();
  8331. UpdateDamageEffects();
  8332. LinkCombat();
  8333. // stereo rendering laser sight that replaces the crosshair
  8334. UpdateLaserSight();
  8335. if ( gameLocal.isNewFrame && IsLocallyControlled() ) {
  8336. playerView.CalculateShake();
  8337. }
  8338. // determine if portal sky is in pvs
  8339. pvsHandle_t clientPVS = gameLocal.pvs.SetupCurrentPVS( GetPVSAreas(), GetNumPVSAreas() );
  8340. gameLocal.portalSkyActive = gameLocal.pvs.CheckAreasForPortalSky( clientPVS, GetPhysics()->GetOrigin() );
  8341. gameLocal.pvs.FreeCurrentPVS( clientPVS );
  8342. //InterpolatePhysics( fraction );
  8343. // Make sure voice groups are set to the right team
  8344. if ( common->IsMultiplayer() && session->GetState() >= idSession::INGAME && entityNumber < MAX_CLIENTS ) { // The entityNumber < MAX_CLIENTS seems to quiet the static analyzer
  8345. // Make sure we're on the right team (at the lobby level)
  8346. const int voiceTeam = spectating ? LOBBY_SPECTATE_TEAM_FOR_VOICE_CHAT : team;
  8347. //idLib::Printf( "CLIENT: Sending voice %i / %i\n", entityNumber, voiceTeam );
  8348. // Update lobby team
  8349. session->GetActingGameStateLobbyBase().SetLobbyUserTeam( gameLocal.lobbyUserIDs[ entityNumber ], voiceTeam );
  8350. // Update voice groups to match in case something changed
  8351. session->SetVoiceGroupsToTeams();
  8352. }
  8353. }
  8354. /*
  8355. ================
  8356. idPlayer::GetPhysicsToVisualTransform
  8357. ================
  8358. */
  8359. bool idPlayer::GetPhysicsToVisualTransform( idVec3 &origin, idMat3 &axis ) {
  8360. if ( af.IsActive() ) {
  8361. af.GetPhysicsToVisualTransform( origin, axis );
  8362. return true;
  8363. }
  8364. // smoothen the rendered origin and angles of other clients
  8365. // smooth self origin if snapshots are telling us prediction is off
  8366. if ( common->IsClient() && gameLocal.framenum >= smoothedFrame && ( !IsLocallyControlled() || selfSmooth ) ) {
  8367. // render origin and axis
  8368. idMat3 renderAxis = viewAxis * GetPhysics()->GetAxis();
  8369. idVec3 renderOrigin = GetPhysics()->GetOrigin() + modelOffset * renderAxis;
  8370. // update the smoothed origin
  8371. if ( !smoothedOriginUpdated ) {
  8372. idVec2 originDiff = renderOrigin.ToVec2() - smoothedOrigin.ToVec2();
  8373. if ( originDiff.LengthSqr() < Square( 100.0f ) ) {
  8374. // smoothen by pushing back to the previous position
  8375. if ( selfSmooth ) {
  8376. assert( IsLocallyControlled() );
  8377. renderOrigin.ToVec2() -= net_clientSelfSmoothing.GetFloat() * originDiff;
  8378. } else {
  8379. renderOrigin.ToVec2() -= gameLocal.clientSmoothing * originDiff;
  8380. }
  8381. }
  8382. smoothedOrigin = renderOrigin;
  8383. smoothedFrame = gameLocal.framenum;
  8384. smoothedOriginUpdated = true;
  8385. }
  8386. axis = idAngles( 0.0f, viewAngles.yaw, 0.0f ).ToMat3();
  8387. origin = ( smoothedOrigin - GetPhysics()->GetOrigin() ) * axis.Transpose();
  8388. } else {
  8389. axis = viewAxis;
  8390. origin = modelOffset;
  8391. }
  8392. return true;
  8393. }
  8394. /*
  8395. ================
  8396. idPlayer::GetPhysicsToSoundTransform
  8397. ================
  8398. */
  8399. bool idPlayer::GetPhysicsToSoundTransform( idVec3 &origin, idMat3 &axis ) {
  8400. idCamera *camera;
  8401. if ( privateCameraView ) {
  8402. camera = privateCameraView;
  8403. } else {
  8404. camera = gameLocal.GetCamera();
  8405. }
  8406. if ( camera ) {
  8407. renderView_t view;
  8408. memset( &view, 0, sizeof( view ) );
  8409. camera->GetViewParms( &view );
  8410. origin = view.vieworg;
  8411. axis = view.viewaxis;
  8412. return true;
  8413. } else {
  8414. return idActor::GetPhysicsToSoundTransform( origin, axis );
  8415. }
  8416. }
  8417. /*
  8418. ================
  8419. idPlayer::HandleUserCmds
  8420. ================
  8421. */
  8422. void idPlayer::HandleUserCmds( const usercmd_t & newcmd ) {
  8423. // latch button actions
  8424. oldButtons = usercmd.buttons;
  8425. // grab out usercmd
  8426. oldCmd = usercmd;
  8427. oldImpulseSequence = usercmd.impulseSequence;
  8428. usercmd = newcmd;
  8429. }
  8430. /*
  8431. ================
  8432. idPlayer::WriteToSnapshot
  8433. ================
  8434. */
  8435. void idPlayer::WriteToSnapshot( idBitMsg &msg ) const {
  8436. physicsObj.WriteToSnapshot( msg );
  8437. WriteBindToSnapshot( msg );
  8438. // Only remote players will use these actual viewangles.
  8439. idCQuat snapViewCQuat( viewAngles.ToQuat().ToCQuat() );
  8440. msg.WriteFloat( snapViewCQuat.x );
  8441. msg.WriteFloat( snapViewCQuat.y );
  8442. msg.WriteFloat( snapViewCQuat.z );
  8443. msg.WriteDeltaFloat( 0.0f, deltaViewAngles[0] );
  8444. msg.WriteDeltaFloat( 0.0f, deltaViewAngles[1] );
  8445. msg.WriteDeltaFloat( 0.0f, deltaViewAngles[2] );
  8446. msg.WriteShort( health );
  8447. msg.WriteBits( gameLocal.ServerRemapDecl( -1, DECL_ENTITYDEF, lastDamageDef ), gameLocal.entityDefBits );
  8448. msg.WriteDir( lastDamageDir, 9 );
  8449. msg.WriteShort( lastDamageLocation );
  8450. msg.WriteBits( idealWeapon.Get(), idMath::BitsForInteger( MAX_WEAPONS ) );
  8451. msg.WriteBits( inventory.weapons, MAX_WEAPONS );
  8452. msg.WriteBits( weapon.GetSpawnId(), 32 );
  8453. msg.WriteBits( flashlight.GetSpawnId(), 32 );
  8454. msg.WriteBits( spectator, idMath::BitsForInteger( MAX_CLIENTS ) );
  8455. msg.WriteBits( lastHitToggle, 1 );
  8456. msg.WriteBits( weaponGone, 1 );
  8457. msg.WriteBits( isLagged, 1 );
  8458. msg.WriteShort( team );
  8459. WriteToBitMsg( respawn_netEvent, msg );
  8460. /* Needed for the scoreboard */
  8461. msg.WriteBits( carryingFlag, 1 );
  8462. msg.WriteBits( enviroSuitLight.GetSpawnId(), 32 );
  8463. msg.WriteBits( AI_CROUCH, 1 );
  8464. msg.WriteBits( AI_ONGROUND, 1 );
  8465. msg.WriteBits( AI_ONLADDER, 1 );
  8466. msg.WriteBits( AI_JUMP, 1 );
  8467. msg.WriteBits( AI_WEAPON_FIRED, 1 );
  8468. msg.WriteBits( AI_ATTACK_HELD, 1 );
  8469. msg.WriteByte( usercmd.buttons );
  8470. msg.WriteBits( usercmd.forwardmove, -8 );
  8471. msg.WriteBits( usercmd.rightmove, -8 );
  8472. msg.WriteBool( spectating );
  8473. }
  8474. /*
  8475. ================
  8476. idPlayer::ReadFromSnapshot
  8477. ================
  8478. */
  8479. void idPlayer::ReadFromSnapshot( const idBitMsg &msg ) {
  8480. int oldHealth, newIdealWeapon, weaponSpawnId;
  8481. int flashlightSpawnId;
  8482. bool newHitToggle;
  8483. oldHealth = health;
  8484. physicsObj.ReadFromSnapshot( msg );
  8485. ReadBindFromSnapshot( msg );
  8486. // The remote players get updated view angles from the snapshot.
  8487. idCQuat snapViewCQuat;
  8488. snapViewCQuat.x = msg.ReadFloat();
  8489. snapViewCQuat.y = msg.ReadFloat();
  8490. snapViewCQuat.z = msg.ReadFloat();
  8491. idAngles tempDeltaViewAngles;
  8492. tempDeltaViewAngles[0] = msg.ReadDeltaFloat( 0.0f );
  8493. tempDeltaViewAngles[1] = msg.ReadDeltaFloat( 0.0f );
  8494. tempDeltaViewAngles[2] = msg.ReadDeltaFloat( 0.0f );
  8495. deltaViewAngles = tempDeltaViewAngles;
  8496. health = msg.ReadShort();
  8497. lastDamageDef = gameLocal.ClientRemapDecl( DECL_ENTITYDEF, msg.ReadBits( gameLocal.entityDefBits ) );
  8498. lastDamageDir = msg.ReadDir( 9 );
  8499. lastDamageLocation = msg.ReadShort();
  8500. newIdealWeapon = msg.ReadBits( idMath::BitsForInteger( MAX_WEAPONS ) );
  8501. inventory.weapons = msg.ReadBits( MAX_WEAPONS );
  8502. weaponSpawnId = msg.ReadBits( 32 );
  8503. flashlightSpawnId = msg.ReadBits( 32 );
  8504. spectator = msg.ReadBits( idMath::BitsForInteger( MAX_CLIENTS ) );
  8505. newHitToggle = msg.ReadBits( 1 ) != 0;
  8506. weaponGone = msg.ReadBits( 1 ) != 0;
  8507. isLagged = msg.ReadBits( 1 ) != 0;
  8508. team = msg.ReadShort();
  8509. ReadFromBitMsg( respawn_netEvent, msg );
  8510. carryingFlag = msg.ReadBits( 1 ) != 0;
  8511. int enviroSpawnId;
  8512. enviroSpawnId = msg.ReadBits( 32 );
  8513. enviroSuitLight.SetSpawnId( enviroSpawnId );
  8514. bool snapshotCrouch = msg.ReadBool();
  8515. bool snapshotOnGround = msg.ReadBool();
  8516. bool snapshotOnLadder = msg.ReadBool();
  8517. bool snapshotJump = msg.ReadBool();
  8518. bool snapShotFired = msg.ReadBool();
  8519. bool snapShotAttackHeld = msg.ReadBool();
  8520. byte snapshotButtons = msg.ReadByte();
  8521. signed char snapshotForward = msg.ReadBits( -8 );
  8522. signed char snapshotRight = msg.ReadBits( -8 );
  8523. const bool snapshotSpectating = msg.ReadBool();
  8524. // no msg reading below this
  8525. // Update remote remote player state.
  8526. if ( !IsLocallyControlled() ) {
  8527. previousViewQuat = nextViewQuat;
  8528. nextViewQuat = snapViewCQuat.ToQuat();
  8529. AI_CROUCH = snapshotCrouch;
  8530. AI_ONGROUND = snapshotOnGround;
  8531. AI_ONLADDER = snapshotOnLadder;
  8532. AI_JUMP = snapshotJump;
  8533. AI_WEAPON_FIRED = snapShotFired;
  8534. AI_ATTACK_HELD = snapShotAttackHeld;
  8535. oldCmd = usercmd;
  8536. usercmd.buttons = snapshotButtons;
  8537. usercmd.forwardmove = snapshotForward;
  8538. usercmd.rightmove = snapshotRight;
  8539. }
  8540. if ( weapon.SetSpawnId( weaponSpawnId ) ) {
  8541. if ( weapon.GetEntity() ) {
  8542. // maintain ownership locally
  8543. weapon.GetEntity()->SetOwner( this );
  8544. }
  8545. currentWeapon = -1;
  8546. }
  8547. if ( flashlight.SetSpawnId( flashlightSpawnId ) ) {
  8548. if ( flashlight.GetEntity() ) {
  8549. flashlight.GetEntity()->SetFlashlightOwner( this );
  8550. }
  8551. }
  8552. /*
  8553. // if not a local client
  8554. if ( !IsLocallyControlled() ) {
  8555. // assume the client has all ammo types
  8556. inventory.SetRemoteClientAmmo( GetEntityNumber() );
  8557. }
  8558. */
  8559. // Update spectating state
  8560. const bool wasSpectating = spectating;
  8561. spectating = snapshotSpectating;
  8562. if ( spectating != wasSpectating ) {
  8563. Spectate( spectating, false );
  8564. }
  8565. if ( oldHealth > 0 && health <= 0 ) {
  8566. if ( snapshotStale ) {
  8567. // so we just hide and don't show a death skin
  8568. UpdateDeathSkin( true );
  8569. }
  8570. // die
  8571. AI_DEAD = true;
  8572. ClearPowerUps();
  8573. SetAnimState( ANIMCHANNEL_LEGS, "Legs_Death", 4 );
  8574. SetAnimState( ANIMCHANNEL_TORSO, "Torso_Death", 4 );
  8575. SetWaitState( "" );
  8576. animator.ClearAllJoints();
  8577. StartRagdoll();
  8578. physicsObj.SetMovementType( PM_DEAD );
  8579. if ( !snapshotStale ) {
  8580. StartSound( "snd_death", SND_CHANNEL_VOICE, 0, false, NULL );
  8581. }
  8582. if ( weapon.GetEntity() ) {
  8583. weapon.GetEntity()->OwnerDied();
  8584. }
  8585. if ( flashlight.GetEntity() ) {
  8586. FlashlightOff();
  8587. flashlight.GetEntity()->OwnerDied();
  8588. }
  8589. if( IsLocallyControlled() ) {
  8590. ControllerShakeFromDamage( oldHealth - health );
  8591. }
  8592. } else if ( health < oldHealth && health > 0 ) {
  8593. if ( snapshotStale ) {
  8594. lastDmgTime = gameLocal.time;
  8595. } else {
  8596. // damage feedback
  8597. const idDeclEntityDef *def = static_cast<const idDeclEntityDef *>( declManager->DeclByIndex( DECL_ENTITYDEF, lastDamageDef, false ) );
  8598. if ( def ) {
  8599. if ( IsLocallyControlled() ) {
  8600. playerView.DamageImpulse( lastDamageDir * viewAxis.Transpose(), &def->dict );
  8601. AI_PAIN = Pain( NULL, NULL, oldHealth - health, lastDamageDir, lastDamageLocation );
  8602. }
  8603. lastDmgTime = gameLocal.time;
  8604. } else {
  8605. common->Warning( "NET: no damage def for damage feedback '%d'\n", lastDamageDef );
  8606. }
  8607. if( IsLocallyControlled() ) {
  8608. ControllerShakeFromDamage( oldHealth - health );
  8609. }
  8610. }
  8611. } else if ( health > oldHealth && PowerUpActive( MEGAHEALTH ) && !snapshotStale ) {
  8612. // just pulse, for any health raise
  8613. healthPulse = true;
  8614. }
  8615. // handle respawns
  8616. if ( respawn_netEvent.Get() ) {
  8617. Init();
  8618. StopRagdoll();
  8619. SetPhysics( &physicsObj );
  8620. // Explicitly set the current origin, since locally-controlled clients
  8621. // don't interpolate. Reading the physics object from the snapshot only
  8622. // updates the next state, not the current state.
  8623. physicsObj.SnapToNextState();
  8624. physicsObj.EnableClip();
  8625. SetCombatContents( true );
  8626. if ( flashlight.GetEntity() ) {
  8627. flashlight.GetEntity()->Show();
  8628. }
  8629. Respawn_Shared();
  8630. }
  8631. // If the player is alive, restore proper physics object
  8632. if ( health > 0 && IsActiveAF() ) {
  8633. StopRagdoll();
  8634. SetPhysics( &physicsObj );
  8635. physicsObj.EnableClip();
  8636. SetCombatContents( true );
  8637. }
  8638. const int oldIdealWeapon = idealWeapon.Get();
  8639. idealWeapon.UpdateFromSnapshot( newIdealWeapon, GetEntityNumber() );
  8640. if ( oldIdealWeapon != idealWeapon.Get() ) {
  8641. if ( snapshotStale ) {
  8642. weaponCatchup = true;
  8643. }
  8644. UpdateHudWeapon();
  8645. }
  8646. if ( lastHitToggle != newHitToggle ) {
  8647. SetLastHitTime( gameLocal.realClientTime );
  8648. }
  8649. if ( msg.HasChanged() ) {
  8650. UpdateVisuals();
  8651. }
  8652. }
  8653. /*
  8654. ================
  8655. idPlayer::WritePlayerStateToSnapshot
  8656. ================
  8657. */
  8658. void idPlayer::WritePlayerStateToSnapshot( idBitMsg &msg ) const {
  8659. msg.WriteByte( bobCycle );
  8660. msg.WriteLong( stepUpTime );
  8661. msg.WriteFloat( stepUpDelta );
  8662. msg.WriteLong( inventory.weapons );
  8663. msg.WriteByte( inventory.armor );
  8664. inventory.WriteAmmoToSnapshot( msg );
  8665. }
  8666. /*
  8667. ================
  8668. idPlayer::ReadPlayerStateFromSnapshot
  8669. ================
  8670. */
  8671. void idPlayer::ReadPlayerStateFromSnapshot( const idBitMsg &msg ) {
  8672. int newBobCycle = 0;
  8673. int newStepUpTime = 0;
  8674. int newStepUpDelta = 0;
  8675. newBobCycle = msg.ReadByte();
  8676. newStepUpTime = msg.ReadLong();
  8677. newStepUpDelta = msg.ReadFloat();
  8678. inventory.weapons = msg.ReadLong();
  8679. inventory.armor = msg.ReadByte();
  8680. inventory.ReadAmmoFromSnapshot( msg, GetEntityNumber() );
  8681. }
  8682. /*
  8683. ================
  8684. idPlayer::ServerReceiveEvent
  8685. ================
  8686. */
  8687. bool idPlayer::ServerReceiveEvent( int event, int time, const idBitMsg &msg ) {
  8688. if ( idEntity::ServerReceiveEvent( event, time, msg ) ) {
  8689. return true;
  8690. }
  8691. return false;
  8692. }
  8693. /*
  8694. ================
  8695. idPlayer::ClientReceiveEvent
  8696. ================
  8697. */
  8698. bool idPlayer::ClientReceiveEvent( int event, int time, const idBitMsg &msg ) {
  8699. switch ( event ) {
  8700. case EVENT_EXIT_TELEPORTER:
  8701. Event_ExitTeleporter();
  8702. return true;
  8703. case EVENT_ABORT_TELEPORTER:
  8704. SetPrivateCameraView( NULL );
  8705. return true;
  8706. case EVENT_POWERUP: {
  8707. int powerup = msg.ReadShort();
  8708. int powertime = msg.ReadShort();
  8709. if ( powertime > 0 ) {
  8710. GivePowerUp( powerup, powertime, ITEM_GIVE_UPDATE_STATE );
  8711. } else {
  8712. ClearPowerup( powerup );
  8713. }
  8714. return true;
  8715. }
  8716. case EVENT_PICKUPNAME: {
  8717. char buf[MAX_EVENT_PARAM_SIZE];
  8718. msg.ReadString(buf, MAX_EVENT_PARAM_SIZE);
  8719. inventory.AddPickupName(buf, this); //_D3XP
  8720. return true;
  8721. }
  8722. case EVENT_SPECTATE: {
  8723. bool spectate = ( msg.ReadBits( 1 ) != 0 );
  8724. Spectate( spectate, true );
  8725. return true;
  8726. }
  8727. case EVENT_ADD_DAMAGE_EFFECT: {
  8728. if ( spectating ) {
  8729. // if we're spectating, ignore
  8730. // happens if the event and the spectate change are written on the server during the same frame (fraglimit)
  8731. return true;
  8732. }
  8733. return idActor::ClientReceiveEvent( event, time, msg );
  8734. }
  8735. case EVENT_FORCE_ORIGIN: {
  8736. idVec3 forceOrigin = ReadFloatArray< idVec3 >( msg );
  8737. idAngles forceAngles;
  8738. forceAngles[0] = msg.ReadFloat();
  8739. forceAngles[1] = msg.ReadFloat();
  8740. forceAngles[2] = msg.ReadFloat();
  8741. Event_ForceOrigin( forceOrigin, forceAngles );
  8742. return true;
  8743. }
  8744. case EVENT_KNOCKBACK: {
  8745. idVec3 linearVelocity = ReadFloatArray< idVec3 >( msg );
  8746. int knockbacktime = msg.ReadByte();
  8747. physicsObj.SetLinearVelocity( linearVelocity );
  8748. physicsObj.SetKnockBack( knockbacktime );
  8749. return true;
  8750. }
  8751. default: {
  8752. return idActor::ClientReceiveEvent( event, time, msg );
  8753. }
  8754. }
  8755. // return false;
  8756. }
  8757. /*
  8758. ================
  8759. idPlayer::Hide
  8760. ================
  8761. */
  8762. void idPlayer::Hide() {
  8763. idWeapon *weap;
  8764. idActor::Hide();
  8765. weap = weapon.GetEntity();
  8766. if ( weap ) {
  8767. weap->HideWorldModel();
  8768. }
  8769. idWeapon * flash = flashlight.GetEntity();
  8770. if( flash ) {
  8771. flash->HideWorldModel();
  8772. }
  8773. }
  8774. /*
  8775. ================
  8776. idPlayer::Show
  8777. ================
  8778. */
  8779. void idPlayer::Show() {
  8780. idWeapon *weap;
  8781. idActor::Show();
  8782. weap = weapon.GetEntity();
  8783. if ( weap ) {
  8784. weap->ShowWorldModel();
  8785. }
  8786. idWeapon * flash = flashlight.GetEntity();
  8787. if( flash ) {
  8788. flash->ShowWorldModel();
  8789. }
  8790. }
  8791. /*
  8792. ===============
  8793. idPlayer::IsSoundChannelPlaying
  8794. ===============
  8795. */
  8796. bool idPlayer::IsSoundChannelPlaying( const s_channelType channel ) {
  8797. if ( GetSoundEmitter() != NULL ) {
  8798. return GetSoundEmitter()->CurrentlyPlaying( channel );
  8799. }
  8800. return false;
  8801. }
  8802. /*
  8803. ===============
  8804. idPlayer::ShowTip
  8805. ===============
  8806. */
  8807. void idPlayer::ShowTip( const char *title, const char *tip, bool autoHide ) {
  8808. if ( tipUp ) {
  8809. return;
  8810. }
  8811. if ( hudManager ) {
  8812. hudManager->ShowTip( title, tip, autoHide );
  8813. }
  8814. tipUp = true;
  8815. }
  8816. /*
  8817. ===============
  8818. idPlayer::HideTip
  8819. ===============
  8820. */
  8821. void idPlayer::HideTip() {
  8822. if ( hudManager ) {
  8823. hudManager->HideTip();
  8824. }
  8825. tipUp = false;
  8826. }
  8827. /*
  8828. ===============
  8829. idPlayer::Event_HideTip
  8830. ===============
  8831. */
  8832. void idPlayer::Event_HideTip() {
  8833. HideTip();
  8834. }
  8835. /*
  8836. ===============
  8837. idPlayer::HideObjective
  8838. ===============
  8839. */
  8840. void idPlayer::HideObjective() {
  8841. StartSound( "snd_objectivedown", SND_CHANNEL_ANY, 0, false, NULL );
  8842. if ( hud ) {
  8843. if ( objectiveUp ) {
  8844. hud->HideObjective( false );
  8845. objectiveUp = false;
  8846. } else {
  8847. hud->HideObjective( true );
  8848. }
  8849. }
  8850. }
  8851. /*
  8852. ===============
  8853. idPlayer::Event_StopAudioLog
  8854. ===============
  8855. */
  8856. void idPlayer::Event_StopAudioLog() {
  8857. //EndAudioLog();
  8858. }
  8859. /*
  8860. ===============
  8861. idPlayer::SetSpectateOrigin
  8862. ===============
  8863. */
  8864. void idPlayer::SetSpectateOrigin() {
  8865. idVec3 neworig;
  8866. neworig = GetPhysics()->GetOrigin();
  8867. neworig[ 2 ] += pm_normalviewheight.GetFloat();
  8868. neworig[ 2 ] += SPECTATE_RAISE;
  8869. SetOrigin( neworig );
  8870. }
  8871. /*
  8872. ===============
  8873. idPlayer::RemoveWeapon
  8874. ===============
  8875. */
  8876. void idPlayer::RemoveWeapon( const char *weap ) {
  8877. if ( weap && *weap ) {
  8878. inventory.Drop( spawnArgs, spawnArgs.GetString( weap ), -1 );
  8879. }
  8880. }
  8881. /*
  8882. ===============
  8883. idPlayer::RemoveAllButEssentialWeapons
  8884. ===============
  8885. */
  8886. void idPlayer::RemoveAllButEssentialWeapons() {
  8887. const idKeyValue * kv = spawnArgs.MatchPrefix( "def_weapon", NULL );
  8888. for ( ; kv != NULL; kv = spawnArgs.MatchPrefix( "def_weapon", kv ) ) {
  8889. // This list probably ought to be placed int the player's def
  8890. if ( kv->GetValue() == "weapon_fists" || kv->GetValue() == "weapon_soulcube" || kv->GetValue() == "weapon_pda"
  8891. || kv->GetValue() == "weapon_flashlight" || kv->GetValue() == "weapon_flashlight_new" ) {
  8892. continue;
  8893. }
  8894. inventory.Drop( spawnArgs, kv->GetValue(), -1 );
  8895. }
  8896. }
  8897. /*
  8898. ===============
  8899. idPlayer::CanShowWeaponViewmodel
  8900. ===============
  8901. */
  8902. bool idPlayer::CanShowWeaponViewmodel() const {
  8903. return ui_showGun.GetBool();
  8904. }
  8905. /*
  8906. ===============
  8907. idPlayer::SetLevelTrigger
  8908. ===============
  8909. */
  8910. void idPlayer::SetLevelTrigger( const char *levelName, const char *triggerName ) {
  8911. if ( levelName && *levelName && triggerName && *triggerName ) {
  8912. idLevelTriggerInfo lti;
  8913. lti.levelName = levelName;
  8914. lti.triggerName = triggerName;
  8915. inventory.levelTriggers.Append( lti );
  8916. }
  8917. }
  8918. /*
  8919. ===============
  8920. idPlayer::Event_LevelTrigger
  8921. ===============
  8922. */
  8923. void idPlayer::Event_LevelTrigger() {
  8924. idStr mapName = gameLocal.GetMapName();
  8925. mapName.StripPath();
  8926. mapName.StripFileExtension();
  8927. for ( int i = inventory.levelTriggers.Num() - 1; i >= 0; i-- ) {
  8928. if ( idStr::Icmp( mapName, inventory.levelTriggers[i].levelName) == 0 ){
  8929. idEntity *ent = gameLocal.FindEntity( inventory.levelTriggers[i].triggerName );
  8930. if ( ent ) {
  8931. ent->PostEventMS( &EV_Activate, 1, this );
  8932. }
  8933. }
  8934. }
  8935. }
  8936. /*
  8937. ===============
  8938. idPlayer::Event_Gibbed
  8939. ===============
  8940. */
  8941. void idPlayer::Event_Gibbed() {
  8942. // do nothing
  8943. }
  8944. extern idCVar net_clientMaxPrediction;
  8945. /*
  8946. ===============
  8947. idPlayer::UpdatePlayerIcons
  8948. ===============
  8949. */
  8950. void idPlayer::UpdatePlayerIcons() {
  8951. idLobbyBase & lobby = session->GetActingGameStateLobbyBase();
  8952. int lastPacketTime = lobby.GetPeerTimeSinceLastPacket( lobby.PeerIndexFromLobbyUser( gameLocal.lobbyUserIDs[entityNumber] ) );
  8953. isLagged = ( lastPacketTime > net_clientMaxPrediction.GetInteger() );
  8954. //isChatting = ( ( usercmd.buttons & BUTTON_CHATTING ) && ( health > 0 ) );
  8955. }
  8956. /*
  8957. ===============
  8958. idPlayer::DrawPlayerIcons
  8959. ===============
  8960. */
  8961. void idPlayer::DrawPlayerIcons() {
  8962. if ( !NeedsIcon() ) {
  8963. playerIcon.FreeIcon();
  8964. return;
  8965. }
  8966. // Never draw icons for hidden players.
  8967. if ( this->IsHidden() )
  8968. return;
  8969. playerIcon.Draw( this, headJoint );
  8970. }
  8971. /*
  8972. ===============
  8973. idPlayer::HidePlayerIcons
  8974. ===============
  8975. */
  8976. void idPlayer::HidePlayerIcons() {
  8977. playerIcon.FreeIcon();
  8978. }
  8979. /*
  8980. ===============
  8981. idPlayer::NeedsIcon
  8982. ==============
  8983. */
  8984. bool idPlayer::NeedsIcon() {
  8985. // local clients don't render their own icons... they're only info for other clients
  8986. // always draw icons in CTF games
  8987. return !IsLocallyControlled() && ( ( g_CTFArrows.GetBool() && gameLocal.mpGame.IsGametypeFlagBased() && !IsHidden() && !AI_DEAD ) || ( isLagged ) );
  8988. }
  8989. /*
  8990. ===============
  8991. idPlayer::DropFlag()
  8992. ==============
  8993. */
  8994. void idPlayer::DropFlag() {
  8995. if ( !carryingFlag || !common->IsMultiplayer() || !gameLocal.mpGame.IsGametypeFlagBased() ) /* CTF */
  8996. return;
  8997. idEntity * entity = gameLocal.mpGame.GetTeamFlag( 1 - team );
  8998. if ( entity ) {
  8999. idItemTeam * item = static_cast<idItemTeam*>(entity);
  9000. if ( item->carried && !item->dropped ) {
  9001. item->Drop( health <= 0 );
  9002. carryingFlag = false;
  9003. }
  9004. }
  9005. }
  9006. void idPlayer::ReturnFlag() {
  9007. if ( !carryingFlag || !common->IsMultiplayer() || !gameLocal.mpGame.IsGametypeFlagBased() ) /* CTF */
  9008. return;
  9009. idEntity * entity = gameLocal.mpGame.GetTeamFlag( 1 - team );
  9010. if ( entity ) {
  9011. idItemTeam * item = static_cast<idItemTeam*>(entity);
  9012. if ( item->carried && !item->dropped ) {
  9013. item->Return();
  9014. carryingFlag = false;
  9015. }
  9016. }
  9017. }
  9018. void idPlayer::FreeModelDef() {
  9019. idAFEntity_Base::FreeModelDef();
  9020. if ( common->IsMultiplayer() && gameLocal.mpGame.IsGametypeFlagBased() )
  9021. playerIcon.FreeIcon();
  9022. }
  9023. /*
  9024. ========================
  9025. idView::SetControllerShake
  9026. ========================
  9027. */
  9028. void idPlayer::SetControllerShake( float highMagnitude, int highDuration, float lowMagnitude, int lowDuration ) {
  9029. // the main purpose of having these buffer is so multiple, individual shake events can co-exist with each other,
  9030. // for instance, a constant low rumble from the chainsaw when it's idle and a harsh rumble when it's being used.
  9031. // find active buffer with similar magnitude values
  9032. int activeBufferWithSimilarMags = -1;
  9033. int inactiveBuffer = -1;
  9034. for ( int i=0; i<MAX_SHAKE_BUFFER; i++ ) {
  9035. if ( gameLocal.GetTime() <= controllerShakeHighTime[i] || gameLocal.GetTime() <= controllerShakeLowTime[i] ) {
  9036. if ( idMath::Fabs( highMagnitude - controllerShakeHighMag[i] ) <= 0.1f && idMath::Fabs( lowMagnitude - controllerShakeLowMag[i] ) <= 0.1f ) {
  9037. activeBufferWithSimilarMags = i;
  9038. break;
  9039. }
  9040. } else {
  9041. if ( inactiveBuffer == -1 ) {
  9042. inactiveBuffer = i; // first, inactive buffer..
  9043. }
  9044. }
  9045. }
  9046. if ( activeBufferWithSimilarMags > -1 ) {
  9047. // average the magnitudes and adjust the time
  9048. controllerShakeHighMag[ activeBufferWithSimilarMags ] += highMagnitude;
  9049. controllerShakeHighMag[ activeBufferWithSimilarMags ] *= 0.5f;
  9050. controllerShakeLowMag[ activeBufferWithSimilarMags ] += lowMagnitude;
  9051. controllerShakeLowMag[ activeBufferWithSimilarMags ] *= 0.5f;
  9052. controllerShakeHighTime[ activeBufferWithSimilarMags ] = gameLocal.GetTime() + highDuration;
  9053. controllerShakeLowTime[ activeBufferWithSimilarMags ] = gameLocal.GetTime() + lowDuration;
  9054. controllerShakeTimeGroup = gameLocal.selectedGroup;
  9055. return;
  9056. }
  9057. if ( inactiveBuffer == -1 ) {
  9058. inactiveBuffer = 0; // FIXME: probably want to use the oldest buffer..
  9059. }
  9060. controllerShakeHighMag[ inactiveBuffer ] = highMagnitude;
  9061. controllerShakeLowMag[ inactiveBuffer ] = lowMagnitude;
  9062. controllerShakeHighTime[ inactiveBuffer ] = gameLocal.GetTime() + highDuration;
  9063. controllerShakeLowTime[ inactiveBuffer ] = gameLocal.GetTime() + lowDuration;
  9064. controllerShakeTimeGroup = gameLocal.selectedGroup;
  9065. }
  9066. /*
  9067. ========================
  9068. idView::ResetControllerShake
  9069. ========================
  9070. */
  9071. void idPlayer::ResetControllerShake() {
  9072. for ( int i=0; i<MAX_SHAKE_BUFFER; i++ ) {
  9073. controllerShakeHighTime[i] = 0;
  9074. }
  9075. for ( int i=0; i<MAX_SHAKE_BUFFER; i++ ) {
  9076. controllerShakeHighMag[i] = 0.0f;
  9077. }
  9078. for ( int i=0; i<MAX_SHAKE_BUFFER; i++ ) {
  9079. controllerShakeLowTime[i] = 0;
  9080. }
  9081. for ( int i=0; i<MAX_SHAKE_BUFFER; i++ ) {
  9082. controllerShakeLowMag[i] = 0.0f;
  9083. }
  9084. }
  9085. /*
  9086. ========================
  9087. idPlayer::GetControllerShake
  9088. ========================
  9089. */
  9090. void idPlayer::GetControllerShake( int & highMagnitude, int & lowMagnitude ) const {
  9091. if ( gameLocal.inCinematic ) {
  9092. // no controller shake during cinematics
  9093. highMagnitude = 0;
  9094. lowMagnitude = 0;
  9095. return;
  9096. }
  9097. float lowMag = 0.0f;
  9098. float highMag = 0.0f;
  9099. lowMagnitude = 0;
  9100. highMagnitude = 0;
  9101. // use highest values from active buffers
  9102. for ( int i=0; i<MAX_SHAKE_BUFFER; i++ ) {
  9103. if ( gameLocal.GetTimeGroupTime( controllerShakeTimeGroup ) < controllerShakeLowTime[i] ) {
  9104. if ( controllerShakeLowMag[i] > lowMag ) {
  9105. lowMag = controllerShakeLowMag[i];
  9106. }
  9107. }
  9108. if ( gameLocal.GetTimeGroupTime( controllerShakeTimeGroup ) < controllerShakeHighTime[i] ) {
  9109. if ( controllerShakeHighMag[i] > highMag ) {
  9110. highMag = controllerShakeHighMag[i];
  9111. }
  9112. }
  9113. }
  9114. lowMagnitude = idMath::Ftoi( lowMag * 65535.0f );
  9115. highMagnitude = idMath::Ftoi( highMag * 65535.0f );
  9116. }
  9117. /*
  9118. ========================
  9119. idPlayer::GetExpansionType
  9120. ========================
  9121. */
  9122. gameExpansionType_t idPlayer::GetExpansionType() const {
  9123. const char * expansion = spawnArgs.GetString( "player_expansion", "d3" );
  9124. if ( idStr::Icmp( expansion, "d3" ) == 0 ) {
  9125. return GAME_BASE;
  9126. }
  9127. if ( idStr::Icmp( expansion, "d3xp" ) == 0 ) {
  9128. return GAME_D3XP;
  9129. }
  9130. if ( idStr::Icmp( expansion, "d3le" ) == 0 ) {
  9131. return GAME_D3LE;
  9132. }
  9133. return GAME_UNKNOWN;
  9134. }