Player.cpp 217 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306530753085309531053115312531353145315531653175318531953205321532253235324532553265327532853295330533153325333533453355336533753385339534053415342534353445345534653475348534953505351535253535354535553565357535853595360536153625363536453655366536753685369537053715372537353745375537653775378537953805381538253835384538553865387538853895390539153925393539453955396539753985399540054015402540354045405540654075408540954105411541254135414541554165417541854195420542154225423542454255426542754285429543054315432543354345435543654375438543954405441544254435444544554465447544854495450545154525453545454555456545754585459546054615462546354645465546654675468546954705471547254735474547554765477547854795480548154825483548454855486548754885489549054915492549354945495549654975498549955005501550255035504550555065507550855095510551155125513551455155516551755185519552055215522552355245525552655275528552955305531553255335534553555365537553855395540554155425543554455455546554755485549555055515552555355545555555655575558555955605561556255635564556555665567556855695570557155725573557455755576557755785579558055815582558355845585558655875588558955905591559255935594559555965597559855995600560156025603560456055606560756085609561056115612561356145615561656175618561956205621562256235624562556265627562856295630563156325633563456355636563756385639564056415642564356445645564656475648564956505651565256535654565556565657565856595660566156625663566456655666566756685669567056715672567356745675567656775678567956805681568256835684568556865687568856895690569156925693569456955696569756985699570057015702570357045705570657075708570957105711571257135714571557165717571857195720572157225723572457255726572757285729573057315732573357345735573657375738573957405741574257435744574557465747574857495750575157525753575457555756575757585759576057615762576357645765576657675768576957705771577257735774577557765777577857795780578157825783578457855786578757885789579057915792579357945795579657975798579958005801580258035804580558065807580858095810581158125813581458155816581758185819582058215822582358245825582658275828582958305831583258335834583558365837583858395840584158425843584458455846584758485849585058515852585358545855585658575858585958605861586258635864586558665867586858695870587158725873587458755876587758785879588058815882588358845885588658875888588958905891589258935894589558965897589858995900590159025903590459055906590759085909591059115912591359145915591659175918591959205921592259235924592559265927592859295930593159325933593459355936593759385939594059415942594359445945594659475948594959505951595259535954595559565957595859595960596159625963596459655966596759685969597059715972597359745975597659775978597959805981598259835984598559865987598859895990599159925993599459955996599759985999600060016002600360046005600660076008600960106011601260136014601560166017601860196020602160226023602460256026602760286029603060316032603360346035603660376038603960406041604260436044604560466047604860496050605160526053605460556056605760586059606060616062606360646065606660676068606960706071607260736074607560766077607860796080608160826083608460856086608760886089609060916092609360946095609660976098609961006101610261036104610561066107610861096110611161126113611461156116611761186119612061216122612361246125612661276128612961306131613261336134613561366137613861396140614161426143614461456146614761486149615061516152615361546155615661576158615961606161616261636164616561666167616861696170617161726173617461756176617761786179618061816182618361846185618661876188618961906191619261936194619561966197619861996200620162026203620462056206620762086209621062116212621362146215621662176218621962206221622262236224622562266227622862296230623162326233623462356236623762386239624062416242624362446245624662476248624962506251625262536254625562566257625862596260626162626263626462656266626762686269627062716272627362746275627662776278627962806281628262836284628562866287628862896290629162926293629462956296629762986299630063016302630363046305630663076308630963106311631263136314631563166317631863196320632163226323632463256326632763286329633063316332633363346335633663376338633963406341634263436344634563466347634863496350635163526353635463556356635763586359636063616362636363646365636663676368636963706371637263736374637563766377637863796380638163826383638463856386638763886389639063916392639363946395639663976398639964006401640264036404640564066407640864096410641164126413641464156416641764186419642064216422642364246425642664276428642964306431643264336434643564366437643864396440644164426443644464456446644764486449645064516452645364546455645664576458645964606461646264636464646564666467646864696470647164726473647464756476647764786479648064816482648364846485648664876488648964906491649264936494649564966497649864996500650165026503650465056506650765086509651065116512651365146515651665176518651965206521652265236524652565266527652865296530653165326533653465356536653765386539654065416542654365446545654665476548654965506551655265536554655565566557655865596560656165626563656465656566656765686569657065716572657365746575657665776578657965806581658265836584658565866587658865896590659165926593659465956596659765986599660066016602660366046605660666076608660966106611661266136614661566166617661866196620662166226623662466256626662766286629663066316632663366346635663666376638663966406641664266436644664566466647664866496650665166526653665466556656665766586659666066616662666366646665666666676668666966706671667266736674667566766677667866796680668166826683668466856686668766886689669066916692669366946695669666976698669967006701670267036704670567066707670867096710671167126713671467156716671767186719672067216722672367246725672667276728672967306731673267336734673567366737673867396740674167426743674467456746674767486749675067516752675367546755675667576758675967606761676267636764676567666767676867696770677167726773677467756776677767786779678067816782678367846785678667876788678967906791679267936794679567966797679867996800680168026803680468056806680768086809681068116812681368146815681668176818681968206821682268236824682568266827682868296830683168326833683468356836683768386839684068416842684368446845684668476848684968506851685268536854685568566857685868596860686168626863686468656866686768686869687068716872687368746875687668776878687968806881688268836884688568866887688868896890689168926893689468956896689768986899690069016902690369046905690669076908690969106911691269136914691569166917691869196920692169226923692469256926692769286929693069316932693369346935693669376938693969406941694269436944694569466947694869496950695169526953695469556956695769586959696069616962696369646965696669676968696969706971697269736974697569766977697869796980698169826983698469856986698769886989699069916992699369946995699669976998699970007001700270037004700570067007700870097010701170127013701470157016701770187019702070217022702370247025702670277028702970307031703270337034703570367037703870397040704170427043704470457046704770487049705070517052705370547055705670577058705970607061706270637064706570667067706870697070707170727073707470757076707770787079708070817082708370847085708670877088708970907091709270937094709570967097709870997100710171027103710471057106710771087109711071117112711371147115711671177118711971207121712271237124712571267127712871297130713171327133713471357136713771387139714071417142714371447145714671477148714971507151715271537154715571567157715871597160716171627163716471657166716771687169717071717172717371747175717671777178717971807181718271837184718571867187718871897190719171927193719471957196719771987199720072017202720372047205720672077208720972107211721272137214721572167217721872197220722172227223722472257226722772287229723072317232723372347235723672377238723972407241724272437244724572467247724872497250725172527253725472557256725772587259726072617262726372647265726672677268726972707271727272737274727572767277727872797280728172827283728472857286728772887289729072917292729372947295729672977298729973007301730273037304730573067307730873097310731173127313731473157316731773187319732073217322732373247325732673277328732973307331733273337334733573367337733873397340734173427343734473457346734773487349735073517352735373547355735673577358735973607361736273637364736573667367736873697370737173727373737473757376737773787379738073817382738373847385738673877388738973907391739273937394739573967397739873997400740174027403740474057406740774087409741074117412741374147415741674177418741974207421742274237424742574267427742874297430743174327433743474357436743774387439744074417442744374447445744674477448744974507451745274537454745574567457745874597460746174627463746474657466746774687469747074717472747374747475747674777478747974807481748274837484748574867487748874897490749174927493749474957496749774987499750075017502750375047505750675077508750975107511751275137514751575167517751875197520752175227523752475257526752775287529753075317532753375347535753675377538753975407541754275437544754575467547754875497550755175527553755475557556755775587559756075617562756375647565756675677568756975707571757275737574757575767577757875797580758175827583758475857586758775887589759075917592759375947595759675977598759976007601760276037604760576067607760876097610761176127613761476157616761776187619762076217622762376247625762676277628762976307631763276337634763576367637763876397640764176427643764476457646764776487649765076517652765376547655765676577658765976607661766276637664766576667667766876697670767176727673767476757676767776787679768076817682768376847685768676877688768976907691769276937694769576967697769876997700770177027703770477057706770777087709771077117712771377147715771677177718771977207721772277237724772577267727772877297730773177327733773477357736773777387739774077417742774377447745774677477748774977507751775277537754775577567757775877597760776177627763776477657766776777687769777077717772777377747775777677777778777977807781778277837784778577867787778877897790779177927793779477957796779777987799780078017802780378047805780678077808780978107811781278137814781578167817781878197820782178227823782478257826782778287829783078317832783378347835783678377838783978407841784278437844784578467847784878497850785178527853785478557856785778587859786078617862786378647865786678677868786978707871787278737874787578767877787878797880788178827883788478857886788778887889789078917892789378947895789678977898789979007901790279037904790579067907790879097910791179127913791479157916791779187919792079217922792379247925792679277928792979307931793279337934793579367937793879397940794179427943794479457946794779487949795079517952795379547955795679577958795979607961796279637964796579667967796879697970797179727973797479757976797779787979798079817982798379847985798679877988798979907991799279937994799579967997799879998000800180028003800480058006800780088009801080118012801380148015801680178018801980208021802280238024802580268027802880298030803180328033803480358036803780388039804080418042804380448045804680478048804980508051805280538054805580568057805880598060806180628063806480658066806780688069807080718072807380748075807680778078807980808081808280838084808580868087808880898090809180928093809480958096809780988099810081018102810381048105810681078108810981108111811281138114811581168117811881198120812181228123812481258126812781288129813081318132813381348135813681378138813981408141814281438144814581468147814881498150815181528153815481558156815781588159816081618162816381648165816681678168816981708171817281738174817581768177817881798180818181828183818481858186818781888189819081918192819381948195819681978198819982008201820282038204820582068207820882098210821182128213821482158216821782188219822082218222822382248225822682278228822982308231823282338234823582368237823882398240824182428243824482458246824782488249825082518252825382548255825682578258825982608261826282638264826582668267826882698270827182728273827482758276827782788279828082818282828382848285828682878288828982908291829282938294829582968297829882998300830183028303830483058306830783088309831083118312831383148315831683178318831983208321832283238324832583268327832883298330833183328333833483358336833783388339834083418342834383448345834683478348834983508351835283538354835583568357835883598360836183628363836483658366836783688369837083718372837383748375837683778378837983808381838283838384838583868387838883898390839183928393839483958396839783988399840084018402840384048405840684078408840984108411841284138414841584168417841884198420842184228423842484258426842784288429843084318432843384348435843684378438843984408441844284438444844584468447844884498450845184528453845484558456845784588459846084618462846384648465846684678468846984708471847284738474847584768477847884798480848184828483848484858486848784888489849084918492849384948495849684978498849985008501850285038504850585068507850885098510851185128513851485158516851785188519852085218522
  1. /*
  2. ===========================================================================
  3. Doom 3 GPL Source Code
  4. Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
  5. This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
  6. Doom 3 Source Code is free software: you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation, either version 3 of the License, or
  9. (at your option) any later version.
  10. Doom 3 Source Code is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. GNU General Public License for more details.
  14. You should have received a copy of the GNU General Public License
  15. along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
  16. In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
  17. If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
  18. ===========================================================================
  19. */
  20. #include "../idlib/precompiled.h"
  21. #pragma hdrstop
  22. #include "Game_local.h"
  23. /*
  24. ===============================================================================
  25. Player control of the Doom Marine.
  26. This object handles all player movement and world interaction.
  27. ===============================================================================
  28. */
  29. // distance between ladder rungs (actually is half that distance, but this sounds better)
  30. const int LADDER_RUNG_DISTANCE = 32;
  31. // amount of health per dose from the health station
  32. const int HEALTH_PER_DOSE = 10;
  33. // time before a weapon dropped to the floor disappears
  34. const int WEAPON_DROP_TIME = 20 * 1000;
  35. // time before a next or prev weapon switch happens
  36. const int WEAPON_SWITCH_DELAY = 150;
  37. // how many units to raise spectator above default view height so it's in the head of someone
  38. const int SPECTATE_RAISE = 25;
  39. const int HEALTHPULSE_TIME = 333;
  40. // minimum speed to bob and play run/walk animations at
  41. const float MIN_BOB_SPEED = 5.0f;
  42. const idEventDef EV_Player_GetButtons( "getButtons", NULL, 'd' );
  43. const idEventDef EV_Player_GetMove( "getMove", NULL, 'v' );
  44. const idEventDef EV_Player_GetViewAngles( "getViewAngles", NULL, 'v' );
  45. const idEventDef EV_Player_StopFxFov( "stopFxFov" );
  46. const idEventDef EV_Player_EnableWeapon( "enableWeapon" );
  47. const idEventDef EV_Player_DisableWeapon( "disableWeapon" );
  48. const idEventDef EV_Player_GetCurrentWeapon( "getCurrentWeapon", NULL, 's' );
  49. const idEventDef EV_Player_GetPreviousWeapon( "getPreviousWeapon", NULL, 's' );
  50. const idEventDef EV_Player_SelectWeapon( "selectWeapon", "s" );
  51. const idEventDef EV_Player_GetWeaponEntity( "getWeaponEntity", NULL, 'e' );
  52. const idEventDef EV_Player_OpenPDA( "openPDA" );
  53. const idEventDef EV_Player_InPDA( "inPDA", NULL, 'd' );
  54. const idEventDef EV_Player_ExitTeleporter( "exitTeleporter" );
  55. const idEventDef EV_Player_StopAudioLog( "stopAudioLog" );
  56. const idEventDef EV_Player_HideTip( "hideTip" );
  57. const idEventDef EV_Player_LevelTrigger( "levelTrigger" );
  58. const idEventDef EV_SpectatorTouch( "spectatorTouch", "et" );
  59. const idEventDef EV_Player_GetIdealWeapon( "getIdealWeapon", NULL, 's' );
  60. CLASS_DECLARATION( idActor, idPlayer )
  61. EVENT( EV_Player_GetButtons, idPlayer::Event_GetButtons )
  62. EVENT( EV_Player_GetMove, idPlayer::Event_GetMove )
  63. EVENT( EV_Player_GetViewAngles, idPlayer::Event_GetViewAngles )
  64. EVENT( EV_Player_StopFxFov, idPlayer::Event_StopFxFov )
  65. EVENT( EV_Player_EnableWeapon, idPlayer::Event_EnableWeapon )
  66. EVENT( EV_Player_DisableWeapon, idPlayer::Event_DisableWeapon )
  67. EVENT( EV_Player_GetCurrentWeapon, idPlayer::Event_GetCurrentWeapon )
  68. EVENT( EV_Player_GetPreviousWeapon, idPlayer::Event_GetPreviousWeapon )
  69. EVENT( EV_Player_SelectWeapon, idPlayer::Event_SelectWeapon )
  70. EVENT( EV_Player_GetWeaponEntity, idPlayer::Event_GetWeaponEntity )
  71. EVENT( EV_Player_OpenPDA, idPlayer::Event_OpenPDA )
  72. EVENT( EV_Player_InPDA, idPlayer::Event_InPDA )
  73. EVENT( EV_Player_ExitTeleporter, idPlayer::Event_ExitTeleporter )
  74. EVENT( EV_Player_StopAudioLog, idPlayer::Event_StopAudioLog )
  75. EVENT( EV_Player_HideTip, idPlayer::Event_HideTip )
  76. EVENT( EV_Player_LevelTrigger, idPlayer::Event_LevelTrigger )
  77. EVENT( EV_Gibbed, idPlayer::Event_Gibbed )
  78. EVENT( EV_Player_GetIdealWeapon, idPlayer::Event_GetIdealWeapon )
  79. END_CLASS
  80. const int MAX_RESPAWN_TIME = 10000;
  81. const int RAGDOLL_DEATH_TIME = 3000;
  82. const int MAX_PDAS = 64;
  83. const int MAX_PDA_ITEMS = 128;
  84. const int STEPUP_TIME = 200;
  85. const int MAX_INVENTORY_ITEMS = 20;
  86. idVec3 idPlayer::colorBarTable[ 5 ] = {
  87. idVec3( 0.25f, 0.25f, 0.25f ),
  88. idVec3( 1.00f, 0.00f, 0.00f ),
  89. idVec3( 0.00f, 0.80f, 0.10f ),
  90. idVec3( 0.20f, 0.50f, 0.80f ),
  91. idVec3( 1.00f, 0.80f, 0.10f )
  92. };
  93. /*
  94. ==============
  95. idInventory::Clear
  96. ==============
  97. */
  98. void idInventory::Clear( void ) {
  99. maxHealth = 0;
  100. weapons = 0;
  101. powerups = 0;
  102. armor = 0;
  103. maxarmor = 0;
  104. deplete_armor = 0;
  105. deplete_rate = 0.0f;
  106. deplete_ammount = 0;
  107. nextArmorDepleteTime = 0;
  108. memset( ammo, 0, sizeof( ammo ) );
  109. ClearPowerUps();
  110. // 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
  111. memset( clip, -1, sizeof( clip ) );
  112. items.DeleteContents( true );
  113. memset(pdasViewed, 0, 4 * sizeof( pdasViewed[0] ) );
  114. pdas.Clear();
  115. videos.Clear();
  116. emails.Clear();
  117. selVideo = 0;
  118. selEMail = 0;
  119. selPDA = 0;
  120. selAudio = 0;
  121. pdaOpened = false;
  122. turkeyScore = false;
  123. levelTriggers.Clear();
  124. nextItemPickup = 0;
  125. nextItemNum = 1;
  126. onePickupTime = 0;
  127. pickupItemNames.Clear();
  128. objectiveNames.Clear();
  129. ammoPredictTime = 0;
  130. lastGiveTime = 0;
  131. ammoPulse = false;
  132. weaponPulse = false;
  133. armorPulse = false;
  134. }
  135. /*
  136. ==============
  137. idInventory::GivePowerUp
  138. ==============
  139. */
  140. void idInventory::GivePowerUp( idPlayer *player, int powerup, int msec ) {
  141. if ( !msec ) {
  142. // get the duration from the .def files
  143. const idDeclEntityDef *def = NULL;
  144. switch ( powerup ) {
  145. case BERSERK:
  146. def = gameLocal.FindEntityDef( "powerup_berserk", false );
  147. break;
  148. case INVISIBILITY:
  149. def = gameLocal.FindEntityDef( "powerup_invisibility", false );
  150. break;
  151. case MEGAHEALTH:
  152. def = gameLocal.FindEntityDef( "powerup_megahealth", false );
  153. break;
  154. case ADRENALINE:
  155. def = gameLocal.FindEntityDef( "powerup_adrenaline", false );
  156. break;
  157. }
  158. assert( def );
  159. msec = def->dict.GetInt( "time" ) * 1000;
  160. }
  161. powerups |= 1 << powerup;
  162. powerupEndTime[ powerup ] = gameLocal.time + msec;
  163. }
  164. /*
  165. ==============
  166. idInventory::ClearPowerUps
  167. ==============
  168. */
  169. void idInventory::ClearPowerUps( void ) {
  170. int i;
  171. for ( i = 0; i < MAX_POWERUPS; i++ ) {
  172. powerupEndTime[ i ] = 0;
  173. }
  174. powerups = 0;
  175. }
  176. /*
  177. ==============
  178. idInventory::GetPersistantData
  179. ==============
  180. */
  181. void idInventory::GetPersistantData( idDict &dict ) {
  182. int i;
  183. int num;
  184. idDict *item;
  185. idStr key;
  186. const idKeyValue *kv;
  187. const char *name;
  188. // armor
  189. dict.SetInt( "armor", armor );
  190. // don't bother with powerups, maxhealth, maxarmor, or the clip
  191. // ammo
  192. for( i = 0; i < AMMO_NUMTYPES; i++ ) {
  193. name = idWeapon::GetAmmoNameForNum( ( ammo_t )i );
  194. if ( name ) {
  195. dict.SetInt( name, ammo[ i ] );
  196. }
  197. }
  198. // items
  199. num = 0;
  200. for( i = 0; i < items.Num(); i++ ) {
  201. item = items[ i ];
  202. // copy all keys with "inv_"
  203. kv = item->MatchPrefix( "inv_" );
  204. if ( kv ) {
  205. while( kv ) {
  206. sprintf( key, "item_%i %s", num, kv->GetKey().c_str() );
  207. dict.Set( key, kv->GetValue() );
  208. kv = item->MatchPrefix( "inv_", kv );
  209. }
  210. num++;
  211. }
  212. }
  213. dict.SetInt( "items", num );
  214. // pdas viewed
  215. for ( i = 0; i < 4; i++ ) {
  216. dict.SetInt( va("pdasViewed_%i", i), pdasViewed[i] );
  217. }
  218. dict.SetInt( "selPDA", selPDA );
  219. dict.SetInt( "selVideo", selVideo );
  220. dict.SetInt( "selEmail", selEMail );
  221. dict.SetInt( "selAudio", selAudio );
  222. dict.SetInt( "pdaOpened", pdaOpened );
  223. dict.SetInt( "turkeyScore", turkeyScore );
  224. // pdas
  225. for ( i = 0; i < pdas.Num(); i++ ) {
  226. sprintf( key, "pda_%i", i );
  227. dict.Set( key, pdas[ i ] );
  228. }
  229. dict.SetInt( "pdas", pdas.Num() );
  230. // video cds
  231. for ( i = 0; i < videos.Num(); i++ ) {
  232. sprintf( key, "video_%i", i );
  233. dict.Set( key, videos[ i ].c_str() );
  234. }
  235. dict.SetInt( "videos", videos.Num() );
  236. // emails
  237. for ( i = 0; i < emails.Num(); i++ ) {
  238. sprintf( key, "email_%i", i );
  239. dict.Set( key, emails[ i ].c_str() );
  240. }
  241. dict.SetInt( "emails", emails.Num() );
  242. // weapons
  243. dict.SetInt( "weapon_bits", weapons );
  244. dict.SetInt( "levelTriggers", levelTriggers.Num() );
  245. for ( i = 0; i < levelTriggers.Num(); i++ ) {
  246. sprintf( key, "levelTrigger_Level_%i", i );
  247. dict.Set( key, levelTriggers[i].levelName );
  248. sprintf( key, "levelTrigger_Trigger_%i", i );
  249. dict.Set( key, levelTriggers[i].triggerName );
  250. }
  251. }
  252. /*
  253. ==============
  254. idInventory::RestoreInventory
  255. ==============
  256. */
  257. void idInventory::RestoreInventory( idPlayer *owner, const idDict &dict ) {
  258. int i;
  259. int num;
  260. idDict *item;
  261. idStr key;
  262. idStr itemname;
  263. const idKeyValue *kv;
  264. const char *name;
  265. Clear();
  266. // health/armor
  267. maxHealth = dict.GetInt( "maxhealth", "100" );
  268. armor = dict.GetInt( "armor", "50" );
  269. maxarmor = dict.GetInt( "maxarmor", "100" );
  270. deplete_armor = dict.GetInt( "deplete_armor", "0" );
  271. deplete_rate = dict.GetFloat( "deplete_rate", "2.0" );
  272. deplete_ammount = dict.GetInt( "deplete_ammount", "1" );
  273. // the clip and powerups aren't restored
  274. // ammo
  275. for( i = 0; i < AMMO_NUMTYPES; i++ ) {
  276. name = idWeapon::GetAmmoNameForNum( ( ammo_t )i );
  277. if ( name ) {
  278. ammo[ i ] = dict.GetInt( name );
  279. }
  280. }
  281. // items
  282. num = dict.GetInt( "items" );
  283. items.SetNum( num );
  284. for( i = 0; i < num; i++ ) {
  285. item = new idDict();
  286. items[ i ] = item;
  287. sprintf( itemname, "item_%i ", i );
  288. kv = dict.MatchPrefix( itemname );
  289. while( kv ) {
  290. key = kv->GetKey();
  291. key.Strip( itemname );
  292. item->Set( key, kv->GetValue() );
  293. kv = dict.MatchPrefix( itemname, kv );
  294. }
  295. }
  296. // pdas viewed
  297. for ( i = 0; i < 4; i++ ) {
  298. pdasViewed[i] = dict.GetInt(va("pdasViewed_%i", i));
  299. }
  300. selPDA = dict.GetInt( "selPDA" );
  301. selEMail = dict.GetInt( "selEmail" );
  302. selVideo = dict.GetInt( "selVideo" );
  303. selAudio = dict.GetInt( "selAudio" );
  304. pdaOpened = dict.GetBool( "pdaOpened" );
  305. turkeyScore = dict.GetBool( "turkeyScore" );
  306. // pdas
  307. num = dict.GetInt( "pdas" );
  308. pdas.SetNum( num );
  309. for ( i = 0; i < num; i++ ) {
  310. sprintf( itemname, "pda_%i", i );
  311. pdas[i] = dict.GetString( itemname, "default" );
  312. }
  313. // videos
  314. num = dict.GetInt( "videos" );
  315. videos.SetNum( num );
  316. for ( i = 0; i < num; i++ ) {
  317. sprintf( itemname, "video_%i", i );
  318. videos[i] = dict.GetString( itemname, "default" );
  319. }
  320. // emails
  321. num = dict.GetInt( "emails" );
  322. emails.SetNum( num );
  323. for ( i = 0; i < num; i++ ) {
  324. sprintf( itemname, "email_%i", i );
  325. emails[i] = dict.GetString( itemname, "default" );
  326. }
  327. // weapons are stored as a number for persistant data, but as strings in the entityDef
  328. weapons = dict.GetInt( "weapon_bits", "0" );
  329. #ifdef ID_DEMO_BUILD
  330. Give( owner, dict, "weapon", dict.GetString( "weapon" ), NULL, false );
  331. #else
  332. if ( g_skill.GetInteger() >= 3 ) {
  333. Give( owner, dict, "weapon", dict.GetString( "weapon_nightmare" ), NULL, false );
  334. } else {
  335. Give( owner, dict, "weapon", dict.GetString( "weapon" ), NULL, false );
  336. }
  337. #endif
  338. num = dict.GetInt( "levelTriggers" );
  339. for ( i = 0; i < num; i++ ) {
  340. sprintf( itemname, "levelTrigger_Level_%i", i );
  341. idLevelTriggerInfo lti;
  342. lti.levelName = dict.GetString( itemname );
  343. sprintf( itemname, "levelTrigger_Trigger_%i", i );
  344. lti.triggerName = dict.GetString( itemname );
  345. levelTriggers.Append( lti );
  346. }
  347. }
  348. /*
  349. ==============
  350. idInventory::Save
  351. ==============
  352. */
  353. void idInventory::Save( idSaveGame *savefile ) const {
  354. int i;
  355. savefile->WriteInt( maxHealth );
  356. savefile->WriteInt( weapons );
  357. savefile->WriteInt( powerups );
  358. savefile->WriteInt( armor );
  359. savefile->WriteInt( maxarmor );
  360. savefile->WriteInt( ammoPredictTime );
  361. savefile->WriteInt( deplete_armor );
  362. savefile->WriteFloat( deplete_rate );
  363. savefile->WriteInt( deplete_ammount );
  364. savefile->WriteInt( nextArmorDepleteTime );
  365. for( i = 0; i < AMMO_NUMTYPES; i++ ) {
  366. savefile->WriteInt( ammo[ i ] );
  367. }
  368. for( i = 0; i < MAX_WEAPONS; i++ ) {
  369. savefile->WriteInt( clip[ i ] );
  370. }
  371. for( i = 0; i < MAX_POWERUPS; i++ ) {
  372. savefile->WriteInt( powerupEndTime[ i ] );
  373. }
  374. savefile->WriteInt( items.Num() );
  375. for( i = 0; i < items.Num(); i++ ) {
  376. savefile->WriteDict( items[ i ] );
  377. }
  378. savefile->WriteInt( pdasViewed[0] );
  379. savefile->WriteInt( pdasViewed[1] );
  380. savefile->WriteInt( pdasViewed[2] );
  381. savefile->WriteInt( pdasViewed[3] );
  382. savefile->WriteInt( selPDA );
  383. savefile->WriteInt( selVideo );
  384. savefile->WriteInt( selEMail );
  385. savefile->WriteInt( selAudio );
  386. savefile->WriteBool( pdaOpened );
  387. savefile->WriteBool( turkeyScore );
  388. savefile->WriteInt( pdas.Num() );
  389. for( i = 0; i < pdas.Num(); i++ ) {
  390. savefile->WriteString( pdas[ i ] );
  391. }
  392. savefile->WriteInt( pdaSecurity.Num() );
  393. for( i=0; i < pdaSecurity.Num(); i++ ) {
  394. savefile->WriteString( pdaSecurity[ i ] );
  395. }
  396. savefile->WriteInt( videos.Num() );
  397. for( i = 0; i < videos.Num(); i++ ) {
  398. savefile->WriteString( videos[ i ] );
  399. }
  400. savefile->WriteInt( emails.Num() );
  401. for ( i = 0; i < emails.Num(); i++ ) {
  402. savefile->WriteString( emails[ i ] );
  403. }
  404. savefile->WriteInt( nextItemPickup );
  405. savefile->WriteInt( nextItemNum );
  406. savefile->WriteInt( onePickupTime );
  407. savefile->WriteInt( pickupItemNames.Num() );
  408. for( i = 0; i < pickupItemNames.Num(); i++ ) {
  409. savefile->WriteString( pickupItemNames[i].icon );
  410. savefile->WriteString( pickupItemNames[i].name );
  411. }
  412. savefile->WriteInt( objectiveNames.Num() );
  413. for( i = 0; i < objectiveNames.Num(); i++ ) {
  414. savefile->WriteString( objectiveNames[i].screenshot );
  415. savefile->WriteString( objectiveNames[i].text );
  416. savefile->WriteString( objectiveNames[i].title );
  417. }
  418. savefile->WriteInt( levelTriggers.Num() );
  419. for ( i = 0; i < levelTriggers.Num(); i++ ) {
  420. savefile->WriteString( levelTriggers[i].levelName );
  421. savefile->WriteString( levelTriggers[i].triggerName );
  422. }
  423. savefile->WriteBool( ammoPulse );
  424. savefile->WriteBool( weaponPulse );
  425. savefile->WriteBool( armorPulse );
  426. savefile->WriteInt( lastGiveTime );
  427. }
  428. /*
  429. ==============
  430. idInventory::Restore
  431. ==============
  432. */
  433. void idInventory::Restore( idRestoreGame *savefile ) {
  434. int i, num;
  435. savefile->ReadInt( maxHealth );
  436. savefile->ReadInt( weapons );
  437. savefile->ReadInt( powerups );
  438. savefile->ReadInt( armor );
  439. savefile->ReadInt( maxarmor );
  440. savefile->ReadInt( ammoPredictTime );
  441. savefile->ReadInt( deplete_armor );
  442. savefile->ReadFloat( deplete_rate );
  443. savefile->ReadInt( deplete_ammount );
  444. savefile->ReadInt( nextArmorDepleteTime );
  445. for( i = 0; i < AMMO_NUMTYPES; i++ ) {
  446. savefile->ReadInt( ammo[ i ] );
  447. }
  448. for( i = 0; i < MAX_WEAPONS; i++ ) {
  449. savefile->ReadInt( clip[ i ] );
  450. }
  451. for( i = 0; i < MAX_POWERUPS; i++ ) {
  452. savefile->ReadInt( powerupEndTime[ i ] );
  453. }
  454. savefile->ReadInt( num );
  455. for( i = 0; i < num; i++ ) {
  456. idDict *itemdict = new idDict;
  457. savefile->ReadDict( itemdict );
  458. items.Append( itemdict );
  459. }
  460. // pdas
  461. savefile->ReadInt( pdasViewed[0] );
  462. savefile->ReadInt( pdasViewed[1] );
  463. savefile->ReadInt( pdasViewed[2] );
  464. savefile->ReadInt( pdasViewed[3] );
  465. savefile->ReadInt( selPDA );
  466. savefile->ReadInt( selVideo );
  467. savefile->ReadInt( selEMail );
  468. savefile->ReadInt( selAudio );
  469. savefile->ReadBool( pdaOpened );
  470. savefile->ReadBool( turkeyScore );
  471. savefile->ReadInt( num );
  472. for( i = 0; i < num; i++ ) {
  473. idStr strPda;
  474. savefile->ReadString( strPda );
  475. pdas.Append( strPda );
  476. }
  477. // pda security clearances
  478. savefile->ReadInt( num );
  479. for ( i = 0; i < num; i++ ) {
  480. idStr invName;
  481. savefile->ReadString( invName );
  482. pdaSecurity.Append( invName );
  483. }
  484. // videos
  485. savefile->ReadInt( num );
  486. for( i = 0; i < num; i++ ) {
  487. idStr strVideo;
  488. savefile->ReadString( strVideo );
  489. videos.Append( strVideo );
  490. }
  491. // email
  492. savefile->ReadInt( num );
  493. for( i = 0; i < num; i++ ) {
  494. idStr strEmail;
  495. savefile->ReadString( strEmail );
  496. emails.Append( strEmail );
  497. }
  498. savefile->ReadInt( nextItemPickup );
  499. savefile->ReadInt( nextItemNum );
  500. savefile->ReadInt( onePickupTime );
  501. savefile->ReadInt( num );
  502. for( i = 0; i < num; i++ ) {
  503. idItemInfo info;
  504. savefile->ReadString( info.icon );
  505. savefile->ReadString( info.name );
  506. pickupItemNames.Append( info );
  507. }
  508. savefile->ReadInt( num );
  509. for( i = 0; i < num; i++ ) {
  510. idObjectiveInfo obj;
  511. savefile->ReadString( obj.screenshot );
  512. savefile->ReadString( obj.text );
  513. savefile->ReadString( obj.title );
  514. objectiveNames.Append( obj );
  515. }
  516. savefile->ReadInt( num );
  517. for ( i = 0; i < num; i++ ) {
  518. idLevelTriggerInfo lti;
  519. savefile->ReadString( lti.levelName );
  520. savefile->ReadString( lti.triggerName );
  521. levelTriggers.Append( lti );
  522. }
  523. savefile->ReadBool( ammoPulse );
  524. savefile->ReadBool( weaponPulse );
  525. savefile->ReadBool( armorPulse );
  526. savefile->ReadInt( lastGiveTime );
  527. }
  528. /*
  529. ==============
  530. idInventory::AmmoIndexForAmmoClass
  531. ==============
  532. */
  533. ammo_t idInventory::AmmoIndexForAmmoClass( const char *ammo_classname ) const {
  534. return idWeapon::GetAmmoNumForName( ammo_classname );
  535. }
  536. /*
  537. ==============
  538. idInventory::AmmoIndexForAmmoClass
  539. ==============
  540. */
  541. int idInventory::MaxAmmoForAmmoClass( idPlayer *owner, const char *ammo_classname ) const {
  542. return owner->spawnArgs.GetInt( va( "max_%s", ammo_classname ), "0" );
  543. }
  544. /*
  545. ==============
  546. idInventory::AmmoPickupNameForIndex
  547. ==============
  548. */
  549. const char *idInventory::AmmoPickupNameForIndex( ammo_t ammonum ) const {
  550. return idWeapon::GetAmmoPickupNameForNum( ammonum );
  551. }
  552. /*
  553. ==============
  554. idInventory::WeaponIndexForAmmoClass
  555. mapping could be prepared in the constructor
  556. ==============
  557. */
  558. int idInventory::WeaponIndexForAmmoClass( const idDict & spawnArgs, const char *ammo_classname ) const {
  559. int i;
  560. const char *weapon_classname;
  561. for( i = 0; i < MAX_WEAPONS; i++ ) {
  562. weapon_classname = spawnArgs.GetString( va( "def_weapon%d", i ) );
  563. if ( !weapon_classname ) {
  564. continue;
  565. }
  566. const idDeclEntityDef *decl = gameLocal.FindEntityDef( weapon_classname, false );
  567. if ( !decl ) {
  568. continue;
  569. }
  570. if ( !idStr::Icmp( ammo_classname, decl->dict.GetString( "ammoType" ) ) ) {
  571. return i;
  572. }
  573. }
  574. return -1;
  575. }
  576. /*
  577. ==============
  578. idInventory::AmmoIndexForWeaponClass
  579. ==============
  580. */
  581. ammo_t idInventory::AmmoIndexForWeaponClass( const char *weapon_classname, int *ammoRequired ) {
  582. const idDeclEntityDef *decl = gameLocal.FindEntityDef( weapon_classname, false );
  583. if ( !decl ) {
  584. gameLocal.Error( "Unknown weapon in decl '%s'", weapon_classname );
  585. }
  586. if ( ammoRequired ) {
  587. *ammoRequired = decl->dict.GetInt( "ammoRequired" );
  588. }
  589. ammo_t ammo_i = AmmoIndexForAmmoClass( decl->dict.GetString( "ammoType" ) );
  590. return ammo_i;
  591. }
  592. /*
  593. ==============
  594. idInventory::AddPickupName
  595. ==============
  596. */
  597. void idInventory::AddPickupName( const char *name, const char *icon ) {
  598. int num;
  599. num = pickupItemNames.Num();
  600. if ( ( num == 0 ) || ( pickupItemNames[ num - 1 ].name.Icmp( name ) != 0 ) ) {
  601. idItemInfo &info = pickupItemNames.Alloc();
  602. if ( idStr::Cmpn( name, STRTABLE_ID, STRTABLE_ID_LENGTH ) == 0 ) {
  603. info.name = common->GetLanguageDict()->GetString( name );
  604. } else {
  605. info.name = name;
  606. }
  607. info.icon = icon;
  608. }
  609. }
  610. /*
  611. ==============
  612. idInventory::Give
  613. ==============
  614. */
  615. bool idInventory::Give( idPlayer *owner, const idDict &spawnArgs, const char *statname, const char *value, int *idealWeapon, bool updateHud ) {
  616. int i;
  617. const char *pos;
  618. const char *end;
  619. int len;
  620. idStr weaponString;
  621. int max;
  622. const idDeclEntityDef *weaponDecl;
  623. bool tookWeapon;
  624. int amount;
  625. idItemInfo info;
  626. const char *name;
  627. if ( !idStr::Icmpn( statname, "ammo_", 5 ) ) {
  628. i = AmmoIndexForAmmoClass( statname );
  629. max = MaxAmmoForAmmoClass( owner, statname );
  630. if ( ammo[ i ] >= max ) {
  631. return false;
  632. }
  633. amount = atoi( value );
  634. if ( amount ) {
  635. ammo[ i ] += amount;
  636. if ( ( max > 0 ) && ( ammo[ i ] > max ) ) {
  637. ammo[ i ] = max;
  638. }
  639. ammoPulse = true;
  640. name = AmmoPickupNameForIndex( i );
  641. if ( idStr::Length( name ) ) {
  642. AddPickupName( name, "" );
  643. }
  644. }
  645. } else if ( !idStr::Icmp( statname, "armor" ) ) {
  646. if ( armor >= maxarmor ) {
  647. return false; // can't hold any more, so leave the item
  648. }
  649. amount = atoi( value );
  650. if ( amount ) {
  651. armor += amount;
  652. if ( armor > maxarmor ) {
  653. armor = maxarmor;
  654. }
  655. nextArmorDepleteTime = 0;
  656. armorPulse = true;
  657. }
  658. } else if ( idStr::FindText( statname, "inclip_" ) == 0 ) {
  659. i = WeaponIndexForAmmoClass( spawnArgs, statname + 7 );
  660. if ( i != -1 ) {
  661. // set, don't add. not going over the clip size limit.
  662. clip[ i ] = atoi( value );
  663. }
  664. } else if ( !idStr::Icmp( statname, "berserk" ) ) {
  665. GivePowerUp( owner, BERSERK, SEC2MS( atof( value ) ) );
  666. } else if ( !idStr::Icmp( statname, "mega" ) ) {
  667. GivePowerUp( owner, MEGAHEALTH, SEC2MS( atof( value ) ) );
  668. } else if ( !idStr::Icmp( statname, "weapon" ) ) {
  669. tookWeapon = false;
  670. for( pos = value; pos != NULL; pos = end ) {
  671. end = strchr( pos, ',' );
  672. if ( end ) {
  673. len = end - pos;
  674. end++;
  675. } else {
  676. len = strlen( pos );
  677. }
  678. idStr weaponName( pos, 0, len );
  679. // find the number of the matching weapon name
  680. for( i = 0; i < MAX_WEAPONS; i++ ) {
  681. if ( weaponName == spawnArgs.GetString( va( "def_weapon%d", i ) ) ) {
  682. break;
  683. }
  684. }
  685. if ( i >= MAX_WEAPONS ) {
  686. gameLocal.Error( "Unknown weapon '%s'", weaponName.c_str() );
  687. }
  688. // cache the media for this weapon
  689. weaponDecl = gameLocal.FindEntityDef( weaponName, false );
  690. // don't pickup "no ammo" weapon types twice
  691. // not for D3 SP .. there is only one case in the game where you can get a no ammo
  692. // weapon when you might already have it, in that case it is more conistent to pick it up
  693. if ( gameLocal.isMultiplayer && weaponDecl && ( weapons & ( 1 << i ) ) && !weaponDecl->dict.GetInt( "ammoRequired" ) ) {
  694. continue;
  695. }
  696. if ( !gameLocal.world->spawnArgs.GetBool( "no_Weapons" ) || ( weaponName == "weapon_fists" ) || ( weaponName == "weapon_soulcube" ) ) {
  697. if ( ( weapons & ( 1 << i ) ) == 0 || gameLocal.isMultiplayer ) {
  698. if ( owner->GetUserInfo()->GetBool( "ui_autoSwitch" ) && idealWeapon ) {
  699. assert( !gameLocal.isClient );
  700. *idealWeapon = i;
  701. }
  702. if ( owner->hud && updateHud && lastGiveTime + 1000 < gameLocal.time ) {
  703. owner->hud->SetStateInt( "newWeapon", i );
  704. owner->hud->HandleNamedEvent( "newWeapon" );
  705. lastGiveTime = gameLocal.time;
  706. }
  707. weaponPulse = true;
  708. weapons |= ( 1 << i );
  709. tookWeapon = true;
  710. }
  711. }
  712. }
  713. return tookWeapon;
  714. } else if ( !idStr::Icmp( statname, "item" ) || !idStr::Icmp( statname, "icon" ) || !idStr::Icmp( statname, "name" ) ) {
  715. // ignore these as they're handled elsewhere
  716. return false;
  717. } else {
  718. // unknown item
  719. gameLocal.Warning( "Unknown stat '%s' added to player's inventory", statname );
  720. return false;
  721. }
  722. return true;
  723. }
  724. /*
  725. ===============
  726. idInventoy::Drop
  727. ===============
  728. */
  729. void idInventory::Drop( const idDict &spawnArgs, const char *weapon_classname, int weapon_index ) {
  730. // remove the weapon bit
  731. // also remove the ammo associated with the weapon as we pushed it in the item
  732. assert( weapon_index != -1 || weapon_classname );
  733. if ( weapon_index == -1 ) {
  734. for( weapon_index = 0; weapon_index < MAX_WEAPONS; weapon_index++ ) {
  735. if ( !idStr::Icmp( weapon_classname, spawnArgs.GetString( va( "def_weapon%d", weapon_index ) ) ) ) {
  736. break;
  737. }
  738. }
  739. if ( weapon_index >= MAX_WEAPONS ) {
  740. gameLocal.Error( "Unknown weapon '%s'", weapon_classname );
  741. }
  742. } else if ( !weapon_classname ) {
  743. weapon_classname = spawnArgs.GetString( va( "def_weapon%d", weapon_index ) );
  744. }
  745. weapons &= ( 0xffffffff ^ ( 1 << weapon_index ) );
  746. ammo_t ammo_i = AmmoIndexForWeaponClass( weapon_classname, NULL );
  747. if ( ammo_i ) {
  748. clip[ weapon_index ] = -1;
  749. ammo[ ammo_i ] = 0;
  750. }
  751. }
  752. /*
  753. ===============
  754. idInventory::HasAmmo
  755. ===============
  756. */
  757. int idInventory::HasAmmo( ammo_t type, int amount ) {
  758. if ( ( type == 0 ) || !amount ) {
  759. // always allow weapons that don't use ammo to fire
  760. return -1;
  761. }
  762. // check if we have infinite ammo
  763. if ( ammo[ type ] < 0 ) {
  764. return -1;
  765. }
  766. // return how many shots we can fire
  767. return ammo[ type ] / amount;
  768. }
  769. /*
  770. ===============
  771. idInventory::HasAmmo
  772. ===============
  773. */
  774. int idInventory::HasAmmo( const char *weapon_classname ) {
  775. int ammoRequired;
  776. ammo_t ammo_i = AmmoIndexForWeaponClass( weapon_classname, &ammoRequired );
  777. return HasAmmo( ammo_i, ammoRequired );
  778. }
  779. /*
  780. ===============
  781. idInventory::UseAmmo
  782. ===============
  783. */
  784. bool idInventory::UseAmmo( ammo_t type, int amount ) {
  785. if ( !HasAmmo( type, amount ) ) {
  786. return false;
  787. }
  788. // take an ammo away if not infinite
  789. if ( ammo[ type ] >= 0 ) {
  790. ammo[ type ] -= amount;
  791. ammoPredictTime = gameLocal.time; // mp client: we predict this. mark time so we're not confused by snapshots
  792. }
  793. return true;
  794. }
  795. /*
  796. ===============
  797. idInventory::UpdateArmor
  798. ===============
  799. */
  800. void idInventory::UpdateArmor( void ) {
  801. if ( deplete_armor != 0.0f && deplete_armor < armor ) {
  802. if ( !nextArmorDepleteTime ) {
  803. nextArmorDepleteTime = gameLocal.time + deplete_rate * 1000;
  804. } else if ( gameLocal.time > nextArmorDepleteTime ) {
  805. armor -= deplete_ammount;
  806. if ( armor < deplete_armor ) {
  807. armor = deplete_armor;
  808. }
  809. nextArmorDepleteTime = gameLocal.time + deplete_rate * 1000;
  810. }
  811. }
  812. }
  813. /*
  814. ==============
  815. idPlayer::idPlayer
  816. ==============
  817. */
  818. idPlayer::idPlayer() {
  819. memset( &usercmd, 0, sizeof( usercmd ) );
  820. noclip = false;
  821. godmode = false;
  822. spawnAnglesSet = false;
  823. spawnAngles = ang_zero;
  824. viewAngles = ang_zero;
  825. cmdAngles = ang_zero;
  826. oldButtons = 0;
  827. buttonMask = 0;
  828. oldFlags = 0;
  829. lastHitTime = 0;
  830. lastSndHitTime = 0;
  831. lastSavingThrowTime = 0;
  832. weapon = NULL;
  833. hud = NULL;
  834. objectiveSystem = NULL;
  835. objectiveSystemOpen = false;
  836. heartRate = BASE_HEARTRATE;
  837. heartInfo.Init( 0, 0, 0, 0 );
  838. lastHeartAdjust = 0;
  839. lastHeartBeat = 0;
  840. lastDmgTime = 0;
  841. deathClearContentsTime = 0;
  842. lastArmorPulse = -10000;
  843. stamina = 0.0f;
  844. healthPool = 0.0f;
  845. nextHealthPulse = 0;
  846. healthPulse = false;
  847. nextHealthTake = 0;
  848. healthTake = false;
  849. scoreBoardOpen = false;
  850. forceScoreBoard = false;
  851. forceRespawn = false;
  852. spectating = false;
  853. spectator = 0;
  854. colorBar = vec3_zero;
  855. colorBarIndex = 0;
  856. forcedReady = false;
  857. wantSpectate = false;
  858. lastHitToggle = false;
  859. minRespawnTime = 0;
  860. maxRespawnTime = 0;
  861. firstPersonViewOrigin = vec3_zero;
  862. firstPersonViewAxis = mat3_identity;
  863. hipJoint = INVALID_JOINT;
  864. chestJoint = INVALID_JOINT;
  865. headJoint = INVALID_JOINT;
  866. bobFoot = 0;
  867. bobFrac = 0.0f;
  868. bobfracsin = 0.0f;
  869. bobCycle = 0;
  870. xyspeed = 0.0f;
  871. stepUpTime = 0;
  872. stepUpDelta = 0.0f;
  873. idealLegsYaw = 0.0f;
  874. legsYaw = 0.0f;
  875. legsForward = true;
  876. oldViewYaw = 0.0f;
  877. viewBobAngles = ang_zero;
  878. viewBob = vec3_zero;
  879. landChange = 0;
  880. landTime = 0;
  881. currentWeapon = -1;
  882. idealWeapon = -1;
  883. previousWeapon = -1;
  884. weaponSwitchTime = 0;
  885. weaponEnabled = true;
  886. weapon_soulcube = -1;
  887. weapon_pda = -1;
  888. weapon_fists = -1;
  889. showWeaponViewModel = true;
  890. skin = NULL;
  891. powerUpSkin = NULL;
  892. baseSkinName = "";
  893. numProjectilesFired = 0;
  894. numProjectileHits = 0;
  895. airless = false;
  896. airTics = 0;
  897. lastAirDamage = 0;
  898. gibDeath = false;
  899. gibsLaunched = false;
  900. gibsDir = vec3_zero;
  901. zoomFov.Init( 0, 0, 0, 0 );
  902. centerView.Init( 0, 0, 0, 0 );
  903. fxFov = false;
  904. influenceFov = 0;
  905. influenceActive = 0;
  906. influenceRadius = 0.0f;
  907. influenceEntity = NULL;
  908. influenceMaterial = NULL;
  909. influenceSkin = NULL;
  910. privateCameraView = NULL;
  911. memset( loggedViewAngles, 0, sizeof( loggedViewAngles ) );
  912. memset( loggedAccel, 0, sizeof( loggedAccel ) );
  913. currentLoggedAccel = 0;
  914. focusTime = 0;
  915. focusGUIent = NULL;
  916. focusUI = NULL;
  917. focusCharacter = NULL;
  918. talkCursor = 0;
  919. focusVehicle = NULL;
  920. cursor = NULL;
  921. oldMouseX = 0;
  922. oldMouseY = 0;
  923. pdaAudio = "";
  924. pdaVideo = "";
  925. pdaVideoWave = "";
  926. lastDamageDef = 0;
  927. lastDamageDir = vec3_zero;
  928. lastDamageLocation = 0;
  929. smoothedFrame = 0;
  930. smoothedOriginUpdated = false;
  931. smoothedOrigin = vec3_zero;
  932. smoothedAngles = ang_zero;
  933. fl.networkSync = true;
  934. latchedTeam = -1;
  935. doingDeathSkin = false;
  936. weaponGone = false;
  937. useInitialSpawns = false;
  938. tourneyRank = 0;
  939. lastSpectateTeleport = 0;
  940. tourneyLine = 0;
  941. hiddenWeapon = false;
  942. tipUp = false;
  943. objectiveUp = false;
  944. teleportEntity = NULL;
  945. teleportKiller = -1;
  946. respawning = false;
  947. ready = false;
  948. leader = false;
  949. lastSpectateChange = 0;
  950. lastTeleFX = -9999;
  951. weaponCatchup = false;
  952. lastSnapshotSequence = 0;
  953. MPAim = -1;
  954. lastMPAim = -1;
  955. lastMPAimTime = 0;
  956. MPAimFadeTime = 0;
  957. MPAimHighlight = false;
  958. spawnedTime = 0;
  959. lastManOver = false;
  960. lastManPlayAgain = false;
  961. lastManPresent = false;
  962. isTelefragged = false;
  963. isLagged = false;
  964. isChatting = false;
  965. selfSmooth = false;
  966. }
  967. /*
  968. ==============
  969. idPlayer::LinkScriptVariables
  970. set up conditions for animation
  971. ==============
  972. */
  973. void idPlayer::LinkScriptVariables( void ) {
  974. AI_FORWARD.LinkTo( scriptObject, "AI_FORWARD" );
  975. AI_BACKWARD.LinkTo( scriptObject, "AI_BACKWARD" );
  976. AI_STRAFE_LEFT.LinkTo( scriptObject, "AI_STRAFE_LEFT" );
  977. AI_STRAFE_RIGHT.LinkTo( scriptObject, "AI_STRAFE_RIGHT" );
  978. AI_ATTACK_HELD.LinkTo( scriptObject, "AI_ATTACK_HELD" );
  979. AI_WEAPON_FIRED.LinkTo( scriptObject, "AI_WEAPON_FIRED" );
  980. AI_JUMP.LinkTo( scriptObject, "AI_JUMP" );
  981. AI_DEAD.LinkTo( scriptObject, "AI_DEAD" );
  982. AI_CROUCH.LinkTo( scriptObject, "AI_CROUCH" );
  983. AI_ONGROUND.LinkTo( scriptObject, "AI_ONGROUND" );
  984. AI_ONLADDER.LinkTo( scriptObject, "AI_ONLADDER" );
  985. AI_HARDLANDING.LinkTo( scriptObject, "AI_HARDLANDING" );
  986. AI_SOFTLANDING.LinkTo( scriptObject, "AI_SOFTLANDING" );
  987. AI_RUN.LinkTo( scriptObject, "AI_RUN" );
  988. AI_PAIN.LinkTo( scriptObject, "AI_PAIN" );
  989. AI_RELOAD.LinkTo( scriptObject, "AI_RELOAD" );
  990. AI_TELEPORT.LinkTo( scriptObject, "AI_TELEPORT" );
  991. AI_TURN_LEFT.LinkTo( scriptObject, "AI_TURN_LEFT" );
  992. AI_TURN_RIGHT.LinkTo( scriptObject, "AI_TURN_RIGHT" );
  993. }
  994. /*
  995. ==============
  996. idPlayer::SetupWeaponEntity
  997. ==============
  998. */
  999. void idPlayer::SetupWeaponEntity( void ) {
  1000. int w;
  1001. const char *weap;
  1002. if ( weapon.GetEntity() ) {
  1003. // get rid of old weapon
  1004. weapon.GetEntity()->Clear();
  1005. currentWeapon = -1;
  1006. } else if ( !gameLocal.isClient ) {
  1007. weapon = static_cast<idWeapon *>( gameLocal.SpawnEntityType( idWeapon::Type, NULL ) );
  1008. weapon.GetEntity()->SetOwner( this );
  1009. currentWeapon = -1;
  1010. }
  1011. for( w = 0; w < MAX_WEAPONS; w++ ) {
  1012. weap = spawnArgs.GetString( va( "def_weapon%d", w ) );
  1013. if ( weap && *weap ) {
  1014. idWeapon::CacheWeapon( weap );
  1015. }
  1016. }
  1017. }
  1018. /*
  1019. ==============
  1020. idPlayer::Init
  1021. ==============
  1022. */
  1023. void idPlayer::Init( void ) {
  1024. const char *value;
  1025. const idKeyValue *kv;
  1026. noclip = false;
  1027. godmode = false;
  1028. oldButtons = 0;
  1029. oldFlags = 0;
  1030. currentWeapon = -1;
  1031. idealWeapon = -1;
  1032. previousWeapon = -1;
  1033. weaponSwitchTime = 0;
  1034. weaponEnabled = true;
  1035. weapon_soulcube = SlotForWeapon( "weapon_soulcube" );
  1036. weapon_pda = SlotForWeapon( "weapon_pda" );
  1037. weapon_fists = SlotForWeapon( "weapon_fists" );
  1038. showWeaponViewModel = GetUserInfo()->GetBool( "ui_showGun" );
  1039. lastDmgTime = 0;
  1040. lastArmorPulse = -10000;
  1041. lastHeartAdjust = 0;
  1042. lastHeartBeat = 0;
  1043. heartInfo.Init( 0, 0, 0, 0 );
  1044. bobCycle = 0;
  1045. bobFrac = 0.0f;
  1046. landChange = 0;
  1047. landTime = 0;
  1048. zoomFov.Init( 0, 0, 0, 0 );
  1049. centerView.Init( 0, 0, 0, 0 );
  1050. fxFov = false;
  1051. influenceFov = 0;
  1052. influenceActive = 0;
  1053. influenceRadius = 0.0f;
  1054. influenceEntity = NULL;
  1055. influenceMaterial = NULL;
  1056. influenceSkin = NULL;
  1057. currentLoggedAccel = 0;
  1058. focusTime = 0;
  1059. focusGUIent = NULL;
  1060. focusUI = NULL;
  1061. focusCharacter = NULL;
  1062. talkCursor = 0;
  1063. focusVehicle = NULL;
  1064. // remove any damage effects
  1065. playerView.ClearEffects();
  1066. // damage values
  1067. fl.takedamage = true;
  1068. ClearPain();
  1069. // restore persistent data
  1070. RestorePersistantInfo();
  1071. bobCycle = 0;
  1072. stamina = 0.0f;
  1073. healthPool = 0.0f;
  1074. nextHealthPulse = 0;
  1075. healthPulse = false;
  1076. nextHealthTake = 0;
  1077. healthTake = false;
  1078. SetupWeaponEntity();
  1079. currentWeapon = -1;
  1080. previousWeapon = -1;
  1081. heartRate = BASE_HEARTRATE;
  1082. AdjustHeartRate( BASE_HEARTRATE, 0.0f, 0.0f, true );
  1083. idealLegsYaw = 0.0f;
  1084. legsYaw = 0.0f;
  1085. legsForward = true;
  1086. oldViewYaw = 0.0f;
  1087. // set the pm_ cvars
  1088. if ( !gameLocal.isMultiplayer || gameLocal.isServer ) {
  1089. kv = spawnArgs.MatchPrefix( "pm_", NULL );
  1090. while( kv ) {
  1091. cvarSystem->SetCVarString( kv->GetKey(), kv->GetValue() );
  1092. kv = spawnArgs.MatchPrefix( "pm_", kv );
  1093. }
  1094. }
  1095. // disable stamina on hell levels
  1096. if ( gameLocal.world && gameLocal.world->spawnArgs.GetBool( "no_stamina" ) ) {
  1097. pm_stamina.SetFloat( 0.0f );
  1098. }
  1099. // stamina always initialized to maximum
  1100. stamina = pm_stamina.GetFloat();
  1101. // air always initialized to maximum too
  1102. airTics = pm_airTics.GetFloat();
  1103. airless = false;
  1104. gibDeath = false;
  1105. gibsLaunched = false;
  1106. gibsDir.Zero();
  1107. // set the gravity
  1108. physicsObj.SetGravity( gameLocal.GetGravity() );
  1109. // start out standing
  1110. SetEyeHeight( pm_normalviewheight.GetFloat() );
  1111. stepUpTime = 0;
  1112. stepUpDelta = 0.0f;
  1113. viewBobAngles.Zero();
  1114. viewBob.Zero();
  1115. value = spawnArgs.GetString( "model" );
  1116. if ( value && ( *value != 0 ) ) {
  1117. SetModel( value );
  1118. }
  1119. if ( cursor ) {
  1120. cursor->SetStateInt( "talkcursor", 0 );
  1121. cursor->SetStateString( "combatcursor", "1" );
  1122. cursor->SetStateString( "itemcursor", "0" );
  1123. cursor->SetStateString( "guicursor", "0" );
  1124. }
  1125. if ( ( gameLocal.isMultiplayer || g_testDeath.GetBool() ) && skin ) {
  1126. SetSkin( skin );
  1127. renderEntity.shaderParms[6] = 0.0f;
  1128. } else if ( spawnArgs.GetString( "spawn_skin", NULL, &value ) ) {
  1129. skin = declManager->FindSkin( value );
  1130. SetSkin( skin );
  1131. renderEntity.shaderParms[6] = 0.0f;
  1132. }
  1133. value = spawnArgs.GetString( "bone_hips", "" );
  1134. hipJoint = animator.GetJointHandle( value );
  1135. if ( hipJoint == INVALID_JOINT ) {
  1136. gameLocal.Error( "Joint '%s' not found for 'bone_hips' on '%s'", value, name.c_str() );
  1137. }
  1138. value = spawnArgs.GetString( "bone_chest", "" );
  1139. chestJoint = animator.GetJointHandle( value );
  1140. if ( chestJoint == INVALID_JOINT ) {
  1141. gameLocal.Error( "Joint '%s' not found for 'bone_chest' on '%s'", value, name.c_str() );
  1142. }
  1143. value = spawnArgs.GetString( "bone_head", "" );
  1144. headJoint = animator.GetJointHandle( value );
  1145. if ( headJoint == INVALID_JOINT ) {
  1146. gameLocal.Error( "Joint '%s' not found for 'bone_head' on '%s'", value, name.c_str() );
  1147. }
  1148. // initialize the script variables
  1149. AI_FORWARD = false;
  1150. AI_BACKWARD = false;
  1151. AI_STRAFE_LEFT = false;
  1152. AI_STRAFE_RIGHT = false;
  1153. AI_ATTACK_HELD = false;
  1154. AI_WEAPON_FIRED = false;
  1155. AI_JUMP = false;
  1156. AI_DEAD = false;
  1157. AI_CROUCH = false;
  1158. AI_ONGROUND = true;
  1159. AI_ONLADDER = false;
  1160. AI_HARDLANDING = false;
  1161. AI_SOFTLANDING = false;
  1162. AI_RUN = false;
  1163. AI_PAIN = false;
  1164. AI_RELOAD = false;
  1165. AI_TELEPORT = false;
  1166. AI_TURN_LEFT = false;
  1167. AI_TURN_RIGHT = false;
  1168. // reset the script object
  1169. ConstructScriptObject();
  1170. // execute the script so the script object's constructor takes effect immediately
  1171. scriptThread->Execute();
  1172. forceScoreBoard = false;
  1173. forcedReady = false;
  1174. privateCameraView = NULL;
  1175. lastSpectateChange = 0;
  1176. lastTeleFX = -9999;
  1177. hiddenWeapon = false;
  1178. tipUp = false;
  1179. objectiveUp = false;
  1180. teleportEntity = NULL;
  1181. teleportKiller = -1;
  1182. leader = false;
  1183. SetPrivateCameraView( NULL );
  1184. lastSnapshotSequence = 0;
  1185. MPAim = -1;
  1186. lastMPAim = -1;
  1187. lastMPAimTime = 0;
  1188. MPAimFadeTime = 0;
  1189. MPAimHighlight = false;
  1190. if ( hud ) {
  1191. hud->HandleNamedEvent( "aim_clear" );
  1192. }
  1193. cvarSystem->SetCVarBool( "ui_chat", false );
  1194. }
  1195. /*
  1196. ==============
  1197. idPlayer::Spawn
  1198. Prepare any resources used by the player.
  1199. ==============
  1200. */
  1201. void idPlayer::Spawn( void ) {
  1202. idStr temp;
  1203. idBounds bounds;
  1204. if ( entityNumber >= MAX_CLIENTS ) {
  1205. gameLocal.Error( "entityNum > MAX_CLIENTS for player. Player may only be spawned with a client." );
  1206. }
  1207. // allow thinking during cinematics
  1208. cinematic = true;
  1209. if ( gameLocal.isMultiplayer ) {
  1210. // always start in spectating state waiting to be spawned in
  1211. // do this before SetClipModel to get the right bounding box
  1212. spectating = true;
  1213. }
  1214. // set our collision model
  1215. physicsObj.SetSelf( this );
  1216. SetClipModel();
  1217. physicsObj.SetMass( spawnArgs.GetFloat( "mass", "100" ) );
  1218. physicsObj.SetContents( CONTENTS_BODY );
  1219. physicsObj.SetClipMask( MASK_PLAYERSOLID );
  1220. SetPhysics( &physicsObj );
  1221. InitAASLocation();
  1222. skin = renderEntity.customSkin;
  1223. // only the local player needs guis
  1224. if ( !gameLocal.isMultiplayer || entityNumber == gameLocal.localClientNum ) {
  1225. // load HUD
  1226. if ( gameLocal.isMultiplayer ) {
  1227. hud = uiManager->FindGui( "guis/mphud.gui", true, false, true );
  1228. } else if ( spawnArgs.GetString( "hud", "", temp ) ) {
  1229. hud = uiManager->FindGui( temp, true, false, true );
  1230. }
  1231. if ( hud ) {
  1232. hud->Activate( true, gameLocal.time );
  1233. }
  1234. // load cursor
  1235. if ( spawnArgs.GetString( "cursor", "", temp ) ) {
  1236. cursor = uiManager->FindGui( temp, true, gameLocal.isMultiplayer, gameLocal.isMultiplayer );
  1237. }
  1238. if ( cursor ) {
  1239. cursor->Activate( true, gameLocal.time );
  1240. }
  1241. objectiveSystem = uiManager->FindGui( "guis/pda.gui", true, false, true );
  1242. objectiveSystemOpen = false;
  1243. }
  1244. SetLastHitTime( 0 );
  1245. // load the armor sound feedback
  1246. declManager->FindSound( "player_sounds_hitArmor" );
  1247. // set up conditions for animation
  1248. LinkScriptVariables();
  1249. animator.RemoveOriginOffset( true );
  1250. // initialize user info related settings
  1251. // on server, we wait for the userinfo broadcast, as this controls when the player is initially spawned in game
  1252. if ( gameLocal.isClient || entityNumber == gameLocal.localClientNum ) {
  1253. UserInfoChanged( false );
  1254. }
  1255. // create combat collision hull for exact collision detection
  1256. SetCombatModel();
  1257. // init the damage effects
  1258. playerView.SetPlayerEntity( this );
  1259. // supress model in non-player views, but allow it in mirrors and remote views
  1260. renderEntity.suppressSurfaceInViewID = entityNumber+1;
  1261. // don't project shadow on self or weapon
  1262. renderEntity.noSelfShadow = true;
  1263. idAFAttachment *headEnt = head.GetEntity();
  1264. if ( headEnt ) {
  1265. headEnt->GetRenderEntity()->suppressSurfaceInViewID = entityNumber+1;
  1266. headEnt->GetRenderEntity()->noSelfShadow = true;
  1267. }
  1268. if ( gameLocal.isMultiplayer ) {
  1269. Init();
  1270. Hide(); // properly hidden if starting as a spectator
  1271. if ( !gameLocal.isClient ) {
  1272. // set yourself ready to spawn. idMultiplayerGame will decide when/if appropriate and call SpawnFromSpawnSpot
  1273. SetupWeaponEntity();
  1274. SpawnFromSpawnSpot();
  1275. forceRespawn = true;
  1276. assert( spectating );
  1277. }
  1278. } else {
  1279. SetupWeaponEntity();
  1280. SpawnFromSpawnSpot();
  1281. }
  1282. // trigger playtesting item gives, if we didn't get here from a previous level
  1283. // the devmap key will be set on the first devmap, but cleared on any level
  1284. // transitions
  1285. if ( !gameLocal.isMultiplayer && gameLocal.serverInfo.FindKey( "devmap" ) ) {
  1286. // fire a trigger with the name "devmap"
  1287. idEntity *ent = gameLocal.FindEntity( "devmap" );
  1288. if ( ent ) {
  1289. ent->ActivateTargets( this );
  1290. }
  1291. }
  1292. if ( hud ) {
  1293. // We can spawn with a full soul cube, so we need to make sure the hud knows this
  1294. if ( weapon_soulcube > 0 && ( inventory.weapons & ( 1 << weapon_soulcube ) ) ) {
  1295. int max_souls = inventory.MaxAmmoForAmmoClass( this, "ammo_souls" );
  1296. if ( inventory.ammo[ idWeapon::GetAmmoNumForName( "ammo_souls" ) ] >= max_souls ) {
  1297. hud->HandleNamedEvent( "soulCubeReady" );
  1298. }
  1299. }
  1300. hud->HandleNamedEvent( "itemPickup" );
  1301. }
  1302. if ( GetPDA() ) {
  1303. // Add any emails from the inventory
  1304. for ( int i = 0; i < inventory.emails.Num(); i++ ) {
  1305. GetPDA()->AddEmail( inventory.emails[i] );
  1306. }
  1307. GetPDA()->SetSecurity( common->GetLanguageDict()->GetString( "#str_00066" ) );
  1308. }
  1309. if ( gameLocal.world->spawnArgs.GetBool( "no_Weapons" ) ) {
  1310. hiddenWeapon = true;
  1311. if ( weapon.GetEntity() ) {
  1312. weapon.GetEntity()->LowerWeapon();
  1313. }
  1314. idealWeapon = 0;
  1315. } else {
  1316. hiddenWeapon = false;
  1317. }
  1318. if ( hud ) {
  1319. UpdateHudWeapon();
  1320. hud->StateChanged( gameLocal.time );
  1321. }
  1322. tipUp = false;
  1323. objectiveUp = false;
  1324. if ( inventory.levelTriggers.Num() ) {
  1325. PostEventMS( &EV_Player_LevelTrigger, 0 );
  1326. }
  1327. inventory.pdaOpened = false;
  1328. inventory.selPDA = 0;
  1329. if ( !gameLocal.isMultiplayer ) {
  1330. if ( g_skill.GetInteger() < 2 ) {
  1331. if ( health < 25 ) {
  1332. health = 25;
  1333. }
  1334. if ( g_useDynamicProtection.GetBool() ) {
  1335. g_damageScale.SetFloat( 1.0f );
  1336. }
  1337. } else {
  1338. g_damageScale.SetFloat( 1.0f );
  1339. g_armorProtection.SetFloat( ( g_skill.GetInteger() < 2 ) ? 0.4f : 0.2f );
  1340. #ifndef ID_DEMO_BUILD
  1341. if ( g_skill.GetInteger() == 3 ) {
  1342. healthTake = true;
  1343. nextHealthTake = gameLocal.time + g_healthTakeTime.GetInteger() * 1000;
  1344. }
  1345. #endif
  1346. }
  1347. }
  1348. }
  1349. /*
  1350. ==============
  1351. idPlayer::~idPlayer()
  1352. Release any resources used by the player.
  1353. ==============
  1354. */
  1355. idPlayer::~idPlayer() {
  1356. delete weapon.GetEntity();
  1357. weapon = NULL;
  1358. }
  1359. /*
  1360. ===========
  1361. idPlayer::Save
  1362. ===========
  1363. */
  1364. void idPlayer::Save( idSaveGame *savefile ) const {
  1365. int i;
  1366. savefile->WriteUsercmd( usercmd );
  1367. playerView.Save( savefile );
  1368. savefile->WriteBool( noclip );
  1369. savefile->WriteBool( godmode );
  1370. // don't save spawnAnglesSet, since we'll have to reset them after loading the savegame
  1371. savefile->WriteAngles( spawnAngles );
  1372. savefile->WriteAngles( viewAngles );
  1373. savefile->WriteAngles( cmdAngles );
  1374. savefile->WriteInt( buttonMask );
  1375. savefile->WriteInt( oldButtons );
  1376. savefile->WriteInt( oldFlags );
  1377. savefile->WriteInt( lastHitTime );
  1378. savefile->WriteInt( lastSndHitTime );
  1379. savefile->WriteInt( lastSavingThrowTime );
  1380. // idBoolFields don't need to be saved, just re-linked in Restore
  1381. inventory.Save( savefile );
  1382. weapon.Save( savefile );
  1383. savefile->WriteUserInterface( hud, false );
  1384. savefile->WriteUserInterface( objectiveSystem, false );
  1385. savefile->WriteBool( objectiveSystemOpen );
  1386. savefile->WriteInt( weapon_soulcube );
  1387. savefile->WriteInt( weapon_pda );
  1388. savefile->WriteInt( weapon_fists );
  1389. savefile->WriteInt( heartRate );
  1390. savefile->WriteFloat( heartInfo.GetStartTime() );
  1391. savefile->WriteFloat( heartInfo.GetDuration() );
  1392. savefile->WriteFloat( heartInfo.GetStartValue() );
  1393. savefile->WriteFloat( heartInfo.GetEndValue() );
  1394. savefile->WriteInt( lastHeartAdjust );
  1395. savefile->WriteInt( lastHeartBeat );
  1396. savefile->WriteInt( lastDmgTime );
  1397. savefile->WriteInt( deathClearContentsTime );
  1398. savefile->WriteBool( doingDeathSkin );
  1399. savefile->WriteInt( lastArmorPulse );
  1400. savefile->WriteFloat( stamina );
  1401. savefile->WriteFloat( healthPool );
  1402. savefile->WriteInt( nextHealthPulse );
  1403. savefile->WriteBool( healthPulse );
  1404. savefile->WriteInt( nextHealthTake );
  1405. savefile->WriteBool( healthTake );
  1406. savefile->WriteBool( hiddenWeapon );
  1407. soulCubeProjectile.Save( savefile );
  1408. savefile->WriteInt( spectator );
  1409. savefile->WriteVec3( colorBar );
  1410. savefile->WriteInt( colorBarIndex );
  1411. savefile->WriteBool( scoreBoardOpen );
  1412. savefile->WriteBool( forceScoreBoard );
  1413. savefile->WriteBool( forceRespawn );
  1414. savefile->WriteBool( spectating );
  1415. savefile->WriteInt( lastSpectateTeleport );
  1416. savefile->WriteBool( lastHitToggle );
  1417. savefile->WriteBool( forcedReady );
  1418. savefile->WriteBool( wantSpectate );
  1419. savefile->WriteBool( weaponGone );
  1420. savefile->WriteBool( useInitialSpawns );
  1421. savefile->WriteInt( latchedTeam );
  1422. savefile->WriteInt( tourneyRank );
  1423. savefile->WriteInt( tourneyLine );
  1424. teleportEntity.Save( savefile );
  1425. savefile->WriteInt( teleportKiller );
  1426. savefile->WriteInt( minRespawnTime );
  1427. savefile->WriteInt( maxRespawnTime );
  1428. savefile->WriteVec3( firstPersonViewOrigin );
  1429. savefile->WriteMat3( firstPersonViewAxis );
  1430. // don't bother saving dragEntity since it's a dev tool
  1431. savefile->WriteJoint( hipJoint );
  1432. savefile->WriteJoint( chestJoint );
  1433. savefile->WriteJoint( headJoint );
  1434. savefile->WriteStaticObject( physicsObj );
  1435. savefile->WriteInt( aasLocation.Num() );
  1436. for( i = 0; i < aasLocation.Num(); i++ ) {
  1437. savefile->WriteInt( aasLocation[ i ].areaNum );
  1438. savefile->WriteVec3( aasLocation[ i ].pos );
  1439. }
  1440. savefile->WriteInt( bobFoot );
  1441. savefile->WriteFloat( bobFrac );
  1442. savefile->WriteFloat( bobfracsin );
  1443. savefile->WriteInt( bobCycle );
  1444. savefile->WriteFloat( xyspeed );
  1445. savefile->WriteInt( stepUpTime );
  1446. savefile->WriteFloat( stepUpDelta );
  1447. savefile->WriteFloat( idealLegsYaw );
  1448. savefile->WriteFloat( legsYaw );
  1449. savefile->WriteBool( legsForward );
  1450. savefile->WriteFloat( oldViewYaw );
  1451. savefile->WriteAngles( viewBobAngles );
  1452. savefile->WriteVec3( viewBob );
  1453. savefile->WriteInt( landChange );
  1454. savefile->WriteInt( landTime );
  1455. savefile->WriteInt( currentWeapon );
  1456. savefile->WriteInt( idealWeapon );
  1457. savefile->WriteInt( previousWeapon );
  1458. savefile->WriteInt( weaponSwitchTime );
  1459. savefile->WriteBool( weaponEnabled );
  1460. savefile->WriteBool( showWeaponViewModel );
  1461. savefile->WriteSkin( skin );
  1462. savefile->WriteSkin( powerUpSkin );
  1463. savefile->WriteString( baseSkinName );
  1464. savefile->WriteInt( numProjectilesFired );
  1465. savefile->WriteInt( numProjectileHits );
  1466. savefile->WriteBool( airless );
  1467. savefile->WriteInt( airTics );
  1468. savefile->WriteInt( lastAirDamage );
  1469. savefile->WriteBool( gibDeath );
  1470. savefile->WriteBool( gibsLaunched );
  1471. savefile->WriteVec3( gibsDir );
  1472. savefile->WriteFloat( zoomFov.GetStartTime() );
  1473. savefile->WriteFloat( zoomFov.GetDuration() );
  1474. savefile->WriteFloat( zoomFov.GetStartValue() );
  1475. savefile->WriteFloat( zoomFov.GetEndValue() );
  1476. savefile->WriteFloat( centerView.GetStartTime() );
  1477. savefile->WriteFloat( centerView.GetDuration() );
  1478. savefile->WriteFloat( centerView.GetStartValue() );
  1479. savefile->WriteFloat( centerView.GetEndValue() );
  1480. savefile->WriteBool( fxFov );
  1481. savefile->WriteFloat( influenceFov );
  1482. savefile->WriteInt( influenceActive );
  1483. savefile->WriteFloat( influenceRadius );
  1484. savefile->WriteObject( influenceEntity );
  1485. savefile->WriteMaterial( influenceMaterial );
  1486. savefile->WriteSkin( influenceSkin );
  1487. savefile->WriteObject( privateCameraView );
  1488. for( i = 0; i < NUM_LOGGED_VIEW_ANGLES; i++ ) {
  1489. savefile->WriteAngles( loggedViewAngles[ i ] );
  1490. }
  1491. for( i = 0; i < NUM_LOGGED_ACCELS; i++ ) {
  1492. savefile->WriteInt( loggedAccel[ i ].time );
  1493. savefile->WriteVec3( loggedAccel[ i ].dir );
  1494. }
  1495. savefile->WriteInt( currentLoggedAccel );
  1496. savefile->WriteObject( focusGUIent );
  1497. // can't save focusUI
  1498. savefile->WriteObject( focusCharacter );
  1499. savefile->WriteInt( talkCursor );
  1500. savefile->WriteInt( focusTime );
  1501. savefile->WriteObject( focusVehicle );
  1502. savefile->WriteUserInterface( cursor, false );
  1503. savefile->WriteInt( oldMouseX );
  1504. savefile->WriteInt( oldMouseY );
  1505. savefile->WriteString( pdaAudio );
  1506. savefile->WriteString( pdaVideo );
  1507. savefile->WriteString( pdaVideoWave );
  1508. savefile->WriteBool( tipUp );
  1509. savefile->WriteBool( objectiveUp );
  1510. savefile->WriteInt( lastDamageDef );
  1511. savefile->WriteVec3( lastDamageDir );
  1512. savefile->WriteInt( lastDamageLocation );
  1513. savefile->WriteInt( smoothedFrame );
  1514. savefile->WriteBool( smoothedOriginUpdated );
  1515. savefile->WriteVec3( smoothedOrigin );
  1516. savefile->WriteAngles( smoothedAngles );
  1517. savefile->WriteBool( ready );
  1518. savefile->WriteBool( respawning );
  1519. savefile->WriteBool( leader );
  1520. savefile->WriteInt( lastSpectateChange );
  1521. savefile->WriteInt( lastTeleFX );
  1522. savefile->WriteFloat( pm_stamina.GetFloat() );
  1523. if ( hud ) {
  1524. hud->SetStateString( "message", common->GetLanguageDict()->GetString( "#str_02916" ) );
  1525. hud->HandleNamedEvent( "Message" );
  1526. }
  1527. }
  1528. /*
  1529. ===========
  1530. idPlayer::Restore
  1531. ===========
  1532. */
  1533. void idPlayer::Restore( idRestoreGame *savefile ) {
  1534. int i;
  1535. int num;
  1536. float set;
  1537. savefile->ReadUsercmd( usercmd );
  1538. playerView.Restore( savefile );
  1539. savefile->ReadBool( noclip );
  1540. savefile->ReadBool( godmode );
  1541. savefile->ReadAngles( spawnAngles );
  1542. savefile->ReadAngles( viewAngles );
  1543. savefile->ReadAngles( cmdAngles );
  1544. memset( usercmd.angles, 0, sizeof( usercmd.angles ) );
  1545. SetViewAngles( viewAngles );
  1546. spawnAnglesSet = true;
  1547. savefile->ReadInt( buttonMask );
  1548. savefile->ReadInt( oldButtons );
  1549. savefile->ReadInt( oldFlags );
  1550. usercmd.flags = 0;
  1551. oldFlags = 0;
  1552. savefile->ReadInt( lastHitTime );
  1553. savefile->ReadInt( lastSndHitTime );
  1554. savefile->ReadInt( lastSavingThrowTime );
  1555. // Re-link idBoolFields to the scriptObject, values will be restored in scriptObject's restore
  1556. LinkScriptVariables();
  1557. inventory.Restore( savefile );
  1558. weapon.Restore( savefile );
  1559. for ( i = 0; i < inventory.emails.Num(); i++ ) {
  1560. GetPDA()->AddEmail( inventory.emails[i] );
  1561. }
  1562. savefile->ReadUserInterface( hud );
  1563. savefile->ReadUserInterface( objectiveSystem );
  1564. savefile->ReadBool( objectiveSystemOpen );
  1565. savefile->ReadInt( weapon_soulcube );
  1566. savefile->ReadInt( weapon_pda );
  1567. savefile->ReadInt( weapon_fists );
  1568. savefile->ReadInt( heartRate );
  1569. savefile->ReadFloat( set );
  1570. heartInfo.SetStartTime( set );
  1571. savefile->ReadFloat( set );
  1572. heartInfo.SetDuration( set );
  1573. savefile->ReadFloat( set );
  1574. heartInfo.SetStartValue( set );
  1575. savefile->ReadFloat( set );
  1576. heartInfo.SetEndValue( set );
  1577. savefile->ReadInt( lastHeartAdjust );
  1578. savefile->ReadInt( lastHeartBeat );
  1579. savefile->ReadInt( lastDmgTime );
  1580. savefile->ReadInt( deathClearContentsTime );
  1581. savefile->ReadBool( doingDeathSkin );
  1582. savefile->ReadInt( lastArmorPulse );
  1583. savefile->ReadFloat( stamina );
  1584. savefile->ReadFloat( healthPool );
  1585. savefile->ReadInt( nextHealthPulse );
  1586. savefile->ReadBool( healthPulse );
  1587. savefile->ReadInt( nextHealthTake );
  1588. savefile->ReadBool( healthTake );
  1589. savefile->ReadBool( hiddenWeapon );
  1590. soulCubeProjectile.Restore( savefile );
  1591. savefile->ReadInt( spectator );
  1592. savefile->ReadVec3( colorBar );
  1593. savefile->ReadInt( colorBarIndex );
  1594. savefile->ReadBool( scoreBoardOpen );
  1595. savefile->ReadBool( forceScoreBoard );
  1596. savefile->ReadBool( forceRespawn );
  1597. savefile->ReadBool( spectating );
  1598. savefile->ReadInt( lastSpectateTeleport );
  1599. savefile->ReadBool( lastHitToggle );
  1600. savefile->ReadBool( forcedReady );
  1601. savefile->ReadBool( wantSpectate );
  1602. savefile->ReadBool( weaponGone );
  1603. savefile->ReadBool( useInitialSpawns );
  1604. savefile->ReadInt( latchedTeam );
  1605. savefile->ReadInt( tourneyRank );
  1606. savefile->ReadInt( tourneyLine );
  1607. teleportEntity.Restore( savefile );
  1608. savefile->ReadInt( teleportKiller );
  1609. savefile->ReadInt( minRespawnTime );
  1610. savefile->ReadInt( maxRespawnTime );
  1611. savefile->ReadVec3( firstPersonViewOrigin );
  1612. savefile->ReadMat3( firstPersonViewAxis );
  1613. // don't bother saving dragEntity since it's a dev tool
  1614. dragEntity.Clear();
  1615. savefile->ReadJoint( hipJoint );
  1616. savefile->ReadJoint( chestJoint );
  1617. savefile->ReadJoint( headJoint );
  1618. savefile->ReadStaticObject( physicsObj );
  1619. RestorePhysics( &physicsObj );
  1620. savefile->ReadInt( num );
  1621. aasLocation.SetGranularity( 1 );
  1622. aasLocation.SetNum( num );
  1623. for( i = 0; i < num; i++ ) {
  1624. savefile->ReadInt( aasLocation[ i ].areaNum );
  1625. savefile->ReadVec3( aasLocation[ i ].pos );
  1626. }
  1627. savefile->ReadInt( bobFoot );
  1628. savefile->ReadFloat( bobFrac );
  1629. savefile->ReadFloat( bobfracsin );
  1630. savefile->ReadInt( bobCycle );
  1631. savefile->ReadFloat( xyspeed );
  1632. savefile->ReadInt( stepUpTime );
  1633. savefile->ReadFloat( stepUpDelta );
  1634. savefile->ReadFloat( idealLegsYaw );
  1635. savefile->ReadFloat( legsYaw );
  1636. savefile->ReadBool( legsForward );
  1637. savefile->ReadFloat( oldViewYaw );
  1638. savefile->ReadAngles( viewBobAngles );
  1639. savefile->ReadVec3( viewBob );
  1640. savefile->ReadInt( landChange );
  1641. savefile->ReadInt( landTime );
  1642. savefile->ReadInt( currentWeapon );
  1643. savefile->ReadInt( idealWeapon );
  1644. savefile->ReadInt( previousWeapon );
  1645. savefile->ReadInt( weaponSwitchTime );
  1646. savefile->ReadBool( weaponEnabled );
  1647. savefile->ReadBool( showWeaponViewModel );
  1648. savefile->ReadSkin( skin );
  1649. savefile->ReadSkin( powerUpSkin );
  1650. savefile->ReadString( baseSkinName );
  1651. savefile->ReadInt( numProjectilesFired );
  1652. savefile->ReadInt( numProjectileHits );
  1653. savefile->ReadBool( airless );
  1654. savefile->ReadInt( airTics );
  1655. savefile->ReadInt( lastAirDamage );
  1656. savefile->ReadBool( gibDeath );
  1657. savefile->ReadBool( gibsLaunched );
  1658. savefile->ReadVec3( gibsDir );
  1659. savefile->ReadFloat( set );
  1660. zoomFov.SetStartTime( set );
  1661. savefile->ReadFloat( set );
  1662. zoomFov.SetDuration( set );
  1663. savefile->ReadFloat( set );
  1664. zoomFov.SetStartValue( set );
  1665. savefile->ReadFloat( set );
  1666. zoomFov.SetEndValue( set );
  1667. savefile->ReadFloat( set );
  1668. centerView.SetStartTime( set );
  1669. savefile->ReadFloat( set );
  1670. centerView.SetDuration( set );
  1671. savefile->ReadFloat( set );
  1672. centerView.SetStartValue( set );
  1673. savefile->ReadFloat( set );
  1674. centerView.SetEndValue( set );
  1675. savefile->ReadBool( fxFov );
  1676. savefile->ReadFloat( influenceFov );
  1677. savefile->ReadInt( influenceActive );
  1678. savefile->ReadFloat( influenceRadius );
  1679. savefile->ReadObject( reinterpret_cast<idClass *&>( influenceEntity ) );
  1680. savefile->ReadMaterial( influenceMaterial );
  1681. savefile->ReadSkin( influenceSkin );
  1682. savefile->ReadObject( reinterpret_cast<idClass *&>( privateCameraView ) );
  1683. for( i = 0; i < NUM_LOGGED_VIEW_ANGLES; i++ ) {
  1684. savefile->ReadAngles( loggedViewAngles[ i ] );
  1685. }
  1686. for( i = 0; i < NUM_LOGGED_ACCELS; i++ ) {
  1687. savefile->ReadInt( loggedAccel[ i ].time );
  1688. savefile->ReadVec3( loggedAccel[ i ].dir );
  1689. }
  1690. savefile->ReadInt( currentLoggedAccel );
  1691. savefile->ReadObject( reinterpret_cast<idClass *&>( focusGUIent ) );
  1692. // can't save focusUI
  1693. focusUI = NULL;
  1694. savefile->ReadObject( reinterpret_cast<idClass *&>( focusCharacter ) );
  1695. savefile->ReadInt( talkCursor );
  1696. savefile->ReadInt( focusTime );
  1697. savefile->ReadObject( reinterpret_cast<idClass *&>( focusVehicle ) );
  1698. savefile->ReadUserInterface( cursor );
  1699. savefile->ReadInt( oldMouseX );
  1700. savefile->ReadInt( oldMouseY );
  1701. savefile->ReadString( pdaAudio );
  1702. savefile->ReadString( pdaVideo );
  1703. savefile->ReadString( pdaVideoWave );
  1704. savefile->ReadBool( tipUp );
  1705. savefile->ReadBool( objectiveUp );
  1706. savefile->ReadInt( lastDamageDef );
  1707. savefile->ReadVec3( lastDamageDir );
  1708. savefile->ReadInt( lastDamageLocation );
  1709. savefile->ReadInt( smoothedFrame );
  1710. savefile->ReadBool( smoothedOriginUpdated );
  1711. savefile->ReadVec3( smoothedOrigin );
  1712. savefile->ReadAngles( smoothedAngles );
  1713. savefile->ReadBool( ready );
  1714. savefile->ReadBool( respawning );
  1715. savefile->ReadBool( leader );
  1716. savefile->ReadInt( lastSpectateChange );
  1717. savefile->ReadInt( lastTeleFX );
  1718. // set the pm_ cvars
  1719. const idKeyValue *kv;
  1720. kv = spawnArgs.MatchPrefix( "pm_", NULL );
  1721. while( kv ) {
  1722. cvarSystem->SetCVarString( kv->GetKey(), kv->GetValue() );
  1723. kv = spawnArgs.MatchPrefix( "pm_", kv );
  1724. }
  1725. savefile->ReadFloat( set );
  1726. pm_stamina.SetFloat( set );
  1727. // create combat collision hull for exact collision detection
  1728. SetCombatModel();
  1729. }
  1730. /*
  1731. ===============
  1732. idPlayer::PrepareForRestart
  1733. ================
  1734. */
  1735. void idPlayer::PrepareForRestart( void ) {
  1736. ClearPowerUps();
  1737. Spectate( true );
  1738. forceRespawn = true;
  1739. // we will be restarting program, clear the client entities from program-related things first
  1740. ShutdownThreads();
  1741. // the sound world is going to be cleared, don't keep references to emitters
  1742. FreeSoundEmitter( false );
  1743. }
  1744. /*
  1745. ===============
  1746. idPlayer::Restart
  1747. ================
  1748. */
  1749. void idPlayer::Restart( void ) {
  1750. idActor::Restart();
  1751. // client needs to setup the animation script object again
  1752. if ( gameLocal.isClient ) {
  1753. Init();
  1754. } else {
  1755. // choose a random spot and prepare the point of view in case player is left spectating
  1756. assert( spectating );
  1757. SpawnFromSpawnSpot();
  1758. }
  1759. useInitialSpawns = true;
  1760. UpdateSkinSetup( true );
  1761. }
  1762. /*
  1763. ===============
  1764. idPlayer::ServerSpectate
  1765. ================
  1766. */
  1767. void idPlayer::ServerSpectate( bool spectate ) {
  1768. assert( !gameLocal.isClient );
  1769. if ( spectating != spectate ) {
  1770. Spectate( spectate );
  1771. if ( spectate ) {
  1772. SetSpectateOrigin();
  1773. } else {
  1774. if ( gameLocal.gameType == GAME_DM ) {
  1775. // make sure the scores are reset so you can't exploit by spectating and entering the game back
  1776. // other game types don't matter, as you either can't join back, or it's team scores
  1777. gameLocal.mpGame.ClearFrags( entityNumber );
  1778. }
  1779. }
  1780. }
  1781. if ( !spectate ) {
  1782. SpawnFromSpawnSpot();
  1783. }
  1784. }
  1785. /*
  1786. ===========
  1787. idPlayer::SelectInitialSpawnPoint
  1788. Try to find a spawn point marked 'initial', otherwise
  1789. use normal spawn selection.
  1790. ============
  1791. */
  1792. void idPlayer::SelectInitialSpawnPoint( idVec3 &origin, idAngles &angles ) {
  1793. idEntity *spot;
  1794. idStr skin;
  1795. spot = gameLocal.SelectInitialSpawnPoint( this );
  1796. // set the player skin from the spawn location
  1797. if ( spot->spawnArgs.GetString( "skin", NULL, skin ) ) {
  1798. spawnArgs.Set( "spawn_skin", skin );
  1799. }
  1800. // activate the spawn locations targets
  1801. spot->PostEventMS( &EV_ActivateTargets, 0, this );
  1802. origin = spot->GetPhysics()->GetOrigin();
  1803. origin[2] += 4.0f + CM_BOX_EPSILON; // move up to make sure the player is at least an epsilon above the floor
  1804. angles = spot->GetPhysics()->GetAxis().ToAngles();
  1805. }
  1806. /*
  1807. ===========
  1808. idPlayer::SpawnFromSpawnSpot
  1809. Chooses a spawn location and spawns the player
  1810. ============
  1811. */
  1812. void idPlayer::SpawnFromSpawnSpot( void ) {
  1813. idVec3 spawn_origin;
  1814. idAngles spawn_angles;
  1815. SelectInitialSpawnPoint( spawn_origin, spawn_angles );
  1816. SpawnToPoint( spawn_origin, spawn_angles );
  1817. }
  1818. /*
  1819. ===========
  1820. idPlayer::SpawnToPoint
  1821. Called every time a client is placed fresh in the world:
  1822. after the first ClientBegin, and after each respawn
  1823. Initializes all non-persistant parts of playerState
  1824. when called here with spectating set to true, just place yourself and init
  1825. ============
  1826. */
  1827. void idPlayer::SpawnToPoint( const idVec3 &spawn_origin, const idAngles &spawn_angles ) {
  1828. idVec3 spec_origin;
  1829. assert( !gameLocal.isClient );
  1830. respawning = true;
  1831. Init();
  1832. fl.noknockback = false;
  1833. // stop any ragdolls being used
  1834. StopRagdoll();
  1835. // set back the player physics
  1836. SetPhysics( &physicsObj );
  1837. physicsObj.SetClipModelAxis();
  1838. physicsObj.EnableClip();
  1839. if ( !spectating ) {
  1840. SetCombatContents( true );
  1841. }
  1842. physicsObj.SetLinearVelocity( vec3_origin );
  1843. // setup our initial view
  1844. if ( !spectating ) {
  1845. SetOrigin( spawn_origin );
  1846. } else {
  1847. spec_origin = spawn_origin;
  1848. spec_origin[ 2 ] += pm_normalheight.GetFloat();
  1849. spec_origin[ 2 ] += SPECTATE_RAISE;
  1850. SetOrigin( spec_origin );
  1851. }
  1852. // if this is the first spawn of the map, we don't have a usercmd yet,
  1853. // so the delta angles won't be correct. This will be fixed on the first think.
  1854. viewAngles = ang_zero;
  1855. SetDeltaViewAngles( ang_zero );
  1856. SetViewAngles( spawn_angles );
  1857. spawnAngles = spawn_angles;
  1858. spawnAnglesSet = false;
  1859. legsForward = true;
  1860. legsYaw = 0.0f;
  1861. idealLegsYaw = 0.0f;
  1862. oldViewYaw = viewAngles.yaw;
  1863. if ( spectating ) {
  1864. Hide();
  1865. } else {
  1866. Show();
  1867. }
  1868. if ( gameLocal.isMultiplayer ) {
  1869. if ( !spectating ) {
  1870. // we may be called twice in a row in some situations. avoid a double fx and 'fly to the roof'
  1871. if ( lastTeleFX < gameLocal.time - 1000 ) {
  1872. idEntityFx::StartFx( spawnArgs.GetString( "fx_spawn" ), &spawn_origin, NULL, this, true );
  1873. lastTeleFX = gameLocal.time;
  1874. }
  1875. }
  1876. AI_TELEPORT = true;
  1877. } else {
  1878. AI_TELEPORT = false;
  1879. }
  1880. // kill anything at the new position
  1881. if ( !spectating ) {
  1882. physicsObj.SetClipMask( MASK_PLAYERSOLID ); // the clip mask is usually maintained in Move(), but KillBox requires it
  1883. gameLocal.KillBox( this );
  1884. }
  1885. // don't allow full run speed for a bit
  1886. physicsObj.SetKnockBack( 100 );
  1887. // set our respawn time and buttons so that if we're killed we don't respawn immediately
  1888. minRespawnTime = gameLocal.time;
  1889. maxRespawnTime = gameLocal.time;
  1890. if ( !spectating ) {
  1891. forceRespawn = false;
  1892. }
  1893. privateCameraView = NULL;
  1894. BecomeActive( TH_THINK );
  1895. // run a client frame to drop exactly to the floor,
  1896. // initialize animations and other things
  1897. Think();
  1898. respawning = false;
  1899. lastManOver = false;
  1900. lastManPlayAgain = false;
  1901. isTelefragged = false;
  1902. }
  1903. /*
  1904. ===============
  1905. idPlayer::SavePersistantInfo
  1906. Saves any inventory and player stats when changing levels.
  1907. ===============
  1908. */
  1909. void idPlayer::SavePersistantInfo( void ) {
  1910. idDict &playerInfo = gameLocal.persistentPlayerInfo[entityNumber];
  1911. playerInfo.Clear();
  1912. inventory.GetPersistantData( playerInfo );
  1913. playerInfo.SetInt( "health", health );
  1914. playerInfo.SetInt( "current_weapon", currentWeapon );
  1915. }
  1916. /*
  1917. ===============
  1918. idPlayer::RestorePersistantInfo
  1919. Restores any inventory and player stats when changing levels.
  1920. ===============
  1921. */
  1922. void idPlayer::RestorePersistantInfo( void ) {
  1923. if ( gameLocal.isMultiplayer ) {
  1924. gameLocal.persistentPlayerInfo[entityNumber].Clear();
  1925. }
  1926. spawnArgs.Copy( gameLocal.persistentPlayerInfo[entityNumber] );
  1927. inventory.RestoreInventory( this, spawnArgs );
  1928. health = spawnArgs.GetInt( "health", "100" );
  1929. if ( !gameLocal.isClient ) {
  1930. idealWeapon = spawnArgs.GetInt( "current_weapon", "1" );
  1931. }
  1932. }
  1933. /*
  1934. ================
  1935. idPlayer::GetUserInfo
  1936. ================
  1937. */
  1938. idDict *idPlayer::GetUserInfo( void ) {
  1939. return &gameLocal.userInfo[ entityNumber ];
  1940. }
  1941. /*
  1942. ==============
  1943. idPlayer::UpdateSkinSetup
  1944. ==============
  1945. */
  1946. void idPlayer::UpdateSkinSetup( bool restart ) {
  1947. if ( restart ) {
  1948. team = ( idStr::Icmp( GetUserInfo()->GetString( "ui_team" ), "Blue" ) == 0 );
  1949. }
  1950. if ( gameLocal.gameType == GAME_TDM ) {
  1951. if ( team ) {
  1952. baseSkinName = "skins/characters/player/marine_mp_blue";
  1953. } else {
  1954. baseSkinName = "skins/characters/player/marine_mp_red";
  1955. }
  1956. if ( !gameLocal.isClient && team != latchedTeam ) {
  1957. gameLocal.mpGame.SwitchToTeam( entityNumber, latchedTeam, team );
  1958. }
  1959. latchedTeam = team;
  1960. } else {
  1961. baseSkinName = GetUserInfo()->GetString( "ui_skin" );
  1962. }
  1963. if ( !baseSkinName.Length() ) {
  1964. baseSkinName = "skins/characters/player/marine_mp";
  1965. }
  1966. skin = declManager->FindSkin( baseSkinName, false );
  1967. assert( skin );
  1968. // match the skin to a color band for scoreboard
  1969. if ( baseSkinName.Find( "red" ) != -1 ) {
  1970. colorBarIndex = 1;
  1971. } else if ( baseSkinName.Find( "green" ) != -1 ) {
  1972. colorBarIndex = 2;
  1973. } else if ( baseSkinName.Find( "blue" ) != -1 ) {
  1974. colorBarIndex = 3;
  1975. } else if ( baseSkinName.Find( "yellow" ) != -1 ) {
  1976. colorBarIndex = 4;
  1977. } else {
  1978. colorBarIndex = 0;
  1979. }
  1980. colorBar = colorBarTable[ colorBarIndex ];
  1981. if ( PowerUpActive( BERSERK ) ) {
  1982. powerUpSkin = declManager->FindSkin( baseSkinName + "_berserk" );
  1983. }
  1984. }
  1985. /*
  1986. ==============
  1987. idPlayer::BalanceTDM
  1988. ==============
  1989. */
  1990. bool idPlayer::BalanceTDM( void ) {
  1991. int i, balanceTeam, teamCount[2];
  1992. idEntity *ent;
  1993. teamCount[ 0 ] = teamCount[ 1 ] = 0;
  1994. for( i = 0; i < gameLocal.numClients; i++ ) {
  1995. ent = gameLocal.entities[ i ];
  1996. if ( ent && ent->IsType( idPlayer::Type ) ) {
  1997. teamCount[ static_cast< idPlayer * >( ent )->team ]++;
  1998. }
  1999. }
  2000. balanceTeam = -1;
  2001. if ( teamCount[ 0 ] < teamCount[ 1 ] ) {
  2002. balanceTeam = 0;
  2003. } else if ( teamCount[ 0 ] > teamCount[ 1 ] ) {
  2004. balanceTeam = 1;
  2005. }
  2006. if ( balanceTeam != -1 && team != balanceTeam ) {
  2007. common->DPrintf( "team balance: forcing player %d to %s team\n", entityNumber, balanceTeam ? "blue" : "red" );
  2008. team = balanceTeam;
  2009. GetUserInfo()->Set( "ui_team", team ? "Blue" : "Red" );
  2010. return true;
  2011. }
  2012. return false;
  2013. }
  2014. /*
  2015. ==============
  2016. idPlayer::UserInfoChanged
  2017. ==============
  2018. */
  2019. bool idPlayer::UserInfoChanged( bool canModify ) {
  2020. idDict *userInfo;
  2021. bool modifiedInfo;
  2022. bool spec;
  2023. bool newready;
  2024. userInfo = GetUserInfo();
  2025. showWeaponViewModel = userInfo->GetBool( "ui_showGun" );
  2026. if ( !gameLocal.isMultiplayer ) {
  2027. return false;
  2028. }
  2029. modifiedInfo = false;
  2030. spec = ( idStr::Icmp( userInfo->GetString( "ui_spectate" ), "Spectate" ) == 0 );
  2031. if ( gameLocal.serverInfo.GetBool( "si_spectators" ) ) {
  2032. // never let spectators go back to game while sudden death is on
  2033. if ( canModify && gameLocal.mpGame.GetGameState() == idMultiplayerGame::SUDDENDEATH && !spec && wantSpectate == true ) {
  2034. userInfo->Set( "ui_spectate", "Spectate" );
  2035. modifiedInfo |= true;
  2036. } else {
  2037. if ( spec != wantSpectate && !spec ) {
  2038. // returning from spectate, set forceRespawn so we don't get stuck in spectate forever
  2039. forceRespawn = true;
  2040. }
  2041. wantSpectate = spec;
  2042. }
  2043. } else {
  2044. if ( canModify && spec ) {
  2045. userInfo->Set( "ui_spectate", "Play" );
  2046. modifiedInfo |= true;
  2047. } else if ( spectating ) {
  2048. // allow player to leaving spectator mode if they were in it when si_spectators got turned off
  2049. forceRespawn = true;
  2050. }
  2051. wantSpectate = false;
  2052. }
  2053. newready = ( idStr::Icmp( userInfo->GetString( "ui_ready" ), "Ready" ) == 0 );
  2054. if ( ready != newready && gameLocal.mpGame.GetGameState() == idMultiplayerGame::WARMUP && !wantSpectate ) {
  2055. gameLocal.mpGame.AddChatLine( common->GetLanguageDict()->GetString( "#str_07180" ), userInfo->GetString( "ui_name" ), newready ? common->GetLanguageDict()->GetString( "#str_04300" ) : common->GetLanguageDict()->GetString( "#str_04301" ) );
  2056. }
  2057. ready = newready;
  2058. team = ( idStr::Icmp( userInfo->GetString( "ui_team" ), "Blue" ) == 0 );
  2059. // server maintains TDM balance
  2060. if ( canModify && gameLocal.gameType == GAME_TDM && !gameLocal.mpGame.IsInGame( entityNumber ) && g_balanceTDM.GetBool() ) {
  2061. modifiedInfo |= BalanceTDM( );
  2062. }
  2063. UpdateSkinSetup( false );
  2064. isChatting = userInfo->GetBool( "ui_chat", "0" );
  2065. if ( canModify && isChatting && AI_DEAD ) {
  2066. // if dead, always force chat icon off.
  2067. isChatting = false;
  2068. userInfo->SetBool( "ui_chat", false );
  2069. modifiedInfo |= true;
  2070. }
  2071. return modifiedInfo;
  2072. }
  2073. /*
  2074. ===============
  2075. idPlayer::UpdateHudAmmo
  2076. ===============
  2077. */
  2078. void idPlayer::UpdateHudAmmo( idUserInterface *_hud ) {
  2079. int inclip;
  2080. int ammoamount;
  2081. assert( weapon.GetEntity() );
  2082. assert( _hud );
  2083. inclip = weapon.GetEntity()->AmmoInClip();
  2084. ammoamount = weapon.GetEntity()->AmmoAvailable();
  2085. if ( ammoamount < 0 || !weapon.GetEntity()->IsReady() ) {
  2086. // show infinite ammo
  2087. _hud->SetStateString( "player_ammo", "" );
  2088. _hud->SetStateString( "player_totalammo", "" );
  2089. } else {
  2090. // show remaining ammo
  2091. _hud->SetStateString( "player_totalammo", va( "%i", ammoamount - inclip ) );
  2092. _hud->SetStateString( "player_ammo", weapon.GetEntity()->ClipSize() ? va( "%i", inclip ) : "--" ); // how much in the current clip
  2093. _hud->SetStateString( "player_clips", weapon.GetEntity()->ClipSize() ? va( "%i", ammoamount / weapon.GetEntity()->ClipSize() ) : "--" );
  2094. _hud->SetStateString( "player_allammo", va( "%i/%i", inclip, ammoamount - inclip ) );
  2095. }
  2096. _hud->SetStateBool( "player_ammo_empty", ( ammoamount == 0 ) );
  2097. _hud->SetStateBool( "player_clip_empty", ( weapon.GetEntity()->ClipSize() ? inclip == 0 : false ) );
  2098. _hud->SetStateBool( "player_clip_low", ( weapon.GetEntity()->ClipSize() ? inclip <= weapon.GetEntity()->LowAmmo() : false ) );
  2099. _hud->HandleNamedEvent( "updateAmmo" );
  2100. }
  2101. /*
  2102. ===============
  2103. idPlayer::UpdateHudStats
  2104. ===============
  2105. */
  2106. void idPlayer::UpdateHudStats( idUserInterface *_hud ) {
  2107. int staminapercentage;
  2108. float max_stamina;
  2109. assert( _hud );
  2110. max_stamina = pm_stamina.GetFloat();
  2111. if ( !max_stamina ) {
  2112. // stamina disabled, so show full stamina bar
  2113. staminapercentage = 100.0f;
  2114. } else {
  2115. staminapercentage = idMath::FtoiFast( 100.0f * stamina / max_stamina );
  2116. }
  2117. _hud->SetStateInt( "player_health", health );
  2118. _hud->SetStateInt( "player_stamina", staminapercentage );
  2119. _hud->SetStateInt( "player_armor", inventory.armor );
  2120. _hud->SetStateInt( "player_hr", heartRate );
  2121. _hud->SetStateInt( "player_nostamina", ( max_stamina == 0 ) ? 1 : 0 );
  2122. _hud->HandleNamedEvent( "updateArmorHealthAir" );
  2123. if ( healthPulse ) {
  2124. _hud->HandleNamedEvent( "healthPulse" );
  2125. StartSound( "snd_healthpulse", SND_CHANNEL_ITEM, 0, false, NULL );
  2126. healthPulse = false;
  2127. }
  2128. if ( healthTake ) {
  2129. _hud->HandleNamedEvent( "healthPulse" );
  2130. StartSound( "snd_healthtake", SND_CHANNEL_ITEM, 0, false, NULL );
  2131. healthTake = false;
  2132. }
  2133. if ( inventory.ammoPulse ) {
  2134. _hud->HandleNamedEvent( "ammoPulse" );
  2135. inventory.ammoPulse = false;
  2136. }
  2137. if ( inventory.weaponPulse ) {
  2138. // We need to update the weapon hud manually, but not
  2139. // the armor/ammo/health because they are updated every
  2140. // frame no matter what
  2141. UpdateHudWeapon();
  2142. _hud->HandleNamedEvent( "weaponPulse" );
  2143. inventory.weaponPulse = false;
  2144. }
  2145. if ( inventory.armorPulse ) {
  2146. _hud->HandleNamedEvent( "armorPulse" );
  2147. inventory.armorPulse = false;
  2148. }
  2149. UpdateHudAmmo( _hud );
  2150. }
  2151. /*
  2152. ===============
  2153. idPlayer::UpdateHudWeapon
  2154. ===============
  2155. */
  2156. void idPlayer::UpdateHudWeapon( bool flashWeapon ) {
  2157. idUserInterface *hud = idPlayer::hud;
  2158. // if updating the hud of a followed client
  2159. if ( gameLocal.localClientNum >= 0 && gameLocal.entities[ gameLocal.localClientNum ] && gameLocal.entities[ gameLocal.localClientNum ]->IsType( idPlayer::Type ) ) {
  2160. idPlayer *p = static_cast< idPlayer * >( gameLocal.entities[ gameLocal.localClientNum ] );
  2161. if ( p->spectating && p->spectator == entityNumber ) {
  2162. assert( p->hud );
  2163. hud = p->hud;
  2164. }
  2165. }
  2166. if ( !hud ) {
  2167. return;
  2168. }
  2169. for ( int i = 0; i < MAX_WEAPONS; i++ ) {
  2170. const char *weapnum = va( "def_weapon%d", i );
  2171. const char *hudWeap = va( "weapon%d", i );
  2172. int weapstate = 0;
  2173. if ( inventory.weapons & ( 1 << i ) ) {
  2174. const char *weap = spawnArgs.GetString( weapnum );
  2175. if ( weap && *weap ) {
  2176. weapstate++;
  2177. }
  2178. if ( idealWeapon == i ) {
  2179. weapstate++;
  2180. }
  2181. }
  2182. hud->SetStateInt( hudWeap, weapstate );
  2183. }
  2184. if ( flashWeapon ) {
  2185. hud->HandleNamedEvent( "weaponChange" );
  2186. }
  2187. }
  2188. /*
  2189. ===============
  2190. idPlayer::DrawHUD
  2191. ===============
  2192. */
  2193. void idPlayer::DrawHUD( idUserInterface *_hud ) {
  2194. if ( !weapon.GetEntity() || influenceActive != INFLUENCE_NONE || privateCameraView || gameLocal.GetCamera() || !_hud || !g_showHud.GetBool() ) {
  2195. return;
  2196. }
  2197. UpdateHudStats( _hud );
  2198. _hud->SetStateString( "weapicon", weapon.GetEntity()->Icon() );
  2199. // FIXME: this is temp to allow the sound meter to show up in the hud
  2200. // it should be commented out before shipping but the code can remain
  2201. // for mod developers to enable for the same functionality
  2202. _hud->SetStateInt( "s_debug", cvarSystem->GetCVarInteger( "s_showLevelMeter" ) );
  2203. weapon.GetEntity()->UpdateGUI();
  2204. _hud->Redraw( gameLocal.realClientTime );
  2205. // weapon targeting crosshair
  2206. if ( !GuiActive() ) {
  2207. if ( cursor && weapon.GetEntity()->ShowCrosshair() ) {
  2208. cursor->Redraw( gameLocal.realClientTime );
  2209. }
  2210. }
  2211. }
  2212. /*
  2213. ===============
  2214. idPlayer::EnterCinematic
  2215. ===============
  2216. */
  2217. void idPlayer::EnterCinematic( void ) {
  2218. Hide();
  2219. StopAudioLog();
  2220. StopSound( SND_CHANNEL_PDA, false );
  2221. if ( hud ) {
  2222. hud->HandleNamedEvent( "radioChatterDown" );
  2223. }
  2224. physicsObj.SetLinearVelocity( vec3_origin );
  2225. SetState( "EnterCinematic" );
  2226. UpdateScript();
  2227. if ( weaponEnabled && weapon.GetEntity() ) {
  2228. weapon.GetEntity()->EnterCinematic();
  2229. }
  2230. AI_FORWARD = false;
  2231. AI_BACKWARD = false;
  2232. AI_STRAFE_LEFT = false;
  2233. AI_STRAFE_RIGHT = false;
  2234. AI_RUN = false;
  2235. AI_ATTACK_HELD = false;
  2236. AI_WEAPON_FIRED = false;
  2237. AI_JUMP = false;
  2238. AI_CROUCH = false;
  2239. AI_ONGROUND = true;
  2240. AI_ONLADDER = false;
  2241. AI_DEAD = ( health <= 0 );
  2242. AI_RUN = false;
  2243. AI_PAIN = false;
  2244. AI_HARDLANDING = false;
  2245. AI_SOFTLANDING = false;
  2246. AI_RELOAD = false;
  2247. AI_TELEPORT = false;
  2248. AI_TURN_LEFT = false;
  2249. AI_TURN_RIGHT = false;
  2250. }
  2251. /*
  2252. ===============
  2253. idPlayer::ExitCinematic
  2254. ===============
  2255. */
  2256. void idPlayer::ExitCinematic( void ) {
  2257. Show();
  2258. if ( weaponEnabled && weapon.GetEntity() ) {
  2259. weapon.GetEntity()->ExitCinematic();
  2260. }
  2261. SetState( "ExitCinematic" );
  2262. UpdateScript();
  2263. }
  2264. /*
  2265. =====================
  2266. idPlayer::UpdateConditions
  2267. =====================
  2268. */
  2269. void idPlayer::UpdateConditions( void ) {
  2270. idVec3 velocity;
  2271. float fallspeed;
  2272. float forwardspeed;
  2273. float sidespeed;
  2274. // minus the push velocity to avoid playing the walking animation and sounds when riding a mover
  2275. velocity = physicsObj.GetLinearVelocity() - physicsObj.GetPushedLinearVelocity();
  2276. fallspeed = velocity * physicsObj.GetGravityNormal();
  2277. if ( influenceActive ) {
  2278. AI_FORWARD = false;
  2279. AI_BACKWARD = false;
  2280. AI_STRAFE_LEFT = false;
  2281. AI_STRAFE_RIGHT = false;
  2282. } else if ( gameLocal.time - lastDmgTime < 500 ) {
  2283. forwardspeed = velocity * viewAxis[ 0 ];
  2284. sidespeed = velocity * viewAxis[ 1 ];
  2285. AI_FORWARD = AI_ONGROUND && ( forwardspeed > 20.01f );
  2286. AI_BACKWARD = AI_ONGROUND && ( forwardspeed < -20.01f );
  2287. AI_STRAFE_LEFT = AI_ONGROUND && ( sidespeed > 20.01f );
  2288. AI_STRAFE_RIGHT = AI_ONGROUND && ( sidespeed < -20.01f );
  2289. } else if ( xyspeed > MIN_BOB_SPEED ) {
  2290. AI_FORWARD = AI_ONGROUND && ( usercmd.forwardmove > 0 );
  2291. AI_BACKWARD = AI_ONGROUND && ( usercmd.forwardmove < 0 );
  2292. AI_STRAFE_LEFT = AI_ONGROUND && ( usercmd.rightmove < 0 );
  2293. AI_STRAFE_RIGHT = AI_ONGROUND && ( usercmd.rightmove > 0 );
  2294. } else {
  2295. AI_FORWARD = false;
  2296. AI_BACKWARD = false;
  2297. AI_STRAFE_LEFT = false;
  2298. AI_STRAFE_RIGHT = false;
  2299. }
  2300. AI_RUN = ( usercmd.buttons & BUTTON_RUN ) && ( ( !pm_stamina.GetFloat() ) || ( stamina > pm_staminathreshold.GetFloat() ) );
  2301. AI_DEAD = ( health <= 0 );
  2302. }
  2303. /*
  2304. ==================
  2305. WeaponFireFeedback
  2306. Called when a weapon fires, generates head twitches, etc
  2307. ==================
  2308. */
  2309. void idPlayer::WeaponFireFeedback( const idDict *weaponDef ) {
  2310. // force a blink
  2311. blink_time = 0;
  2312. // play the fire animation
  2313. AI_WEAPON_FIRED = true;
  2314. // update view feedback
  2315. playerView.WeaponFireFeedback( weaponDef );
  2316. }
  2317. /*
  2318. ===============
  2319. idPlayer::StopFiring
  2320. ===============
  2321. */
  2322. void idPlayer::StopFiring( void ) {
  2323. AI_ATTACK_HELD = false;
  2324. AI_WEAPON_FIRED = false;
  2325. AI_RELOAD = false;
  2326. if ( weapon.GetEntity() ) {
  2327. weapon.GetEntity()->EndAttack();
  2328. }
  2329. }
  2330. /*
  2331. ===============
  2332. idPlayer::FireWeapon
  2333. ===============
  2334. */
  2335. void idPlayer::FireWeapon( void ) {
  2336. idMat3 axis;
  2337. idVec3 muzzle;
  2338. if ( privateCameraView ) {
  2339. return;
  2340. }
  2341. if ( g_editEntityMode.GetInteger() ) {
  2342. GetViewPos( muzzle, axis );
  2343. if ( gameLocal.editEntities->SelectEntity( muzzle, axis[0], this ) ) {
  2344. return;
  2345. }
  2346. }
  2347. if ( !hiddenWeapon && weapon.GetEntity()->IsReady() ) {
  2348. if ( weapon.GetEntity()->AmmoInClip() || weapon.GetEntity()->AmmoAvailable() ) {
  2349. AI_ATTACK_HELD = true;
  2350. weapon.GetEntity()->BeginAttack();
  2351. if ( ( weapon_soulcube >= 0 ) && ( currentWeapon == weapon_soulcube ) ) {
  2352. if ( hud ) {
  2353. hud->HandleNamedEvent( "soulCubeNotReady" );
  2354. }
  2355. SelectWeapon( previousWeapon, false );
  2356. }
  2357. } else {
  2358. NextBestWeapon();
  2359. }
  2360. }
  2361. if ( hud ) {
  2362. if ( tipUp ) {
  2363. HideTip();
  2364. }
  2365. // may want to track with with a bool as well
  2366. // keep from looking up named events so often
  2367. if ( objectiveUp ) {
  2368. HideObjective();
  2369. }
  2370. }
  2371. }
  2372. /*
  2373. ===============
  2374. idPlayer::CacheWeapons
  2375. ===============
  2376. */
  2377. void idPlayer::CacheWeapons( void ) {
  2378. idStr weap;
  2379. int w;
  2380. // check if we have any weapons
  2381. if ( !inventory.weapons ) {
  2382. return;
  2383. }
  2384. for( w = 0; w < MAX_WEAPONS; w++ ) {
  2385. if ( inventory.weapons & ( 1 << w ) ) {
  2386. weap = spawnArgs.GetString( va( "def_weapon%d", w ) );
  2387. if ( weap != "" ) {
  2388. idWeapon::CacheWeapon( weap );
  2389. } else {
  2390. inventory.weapons &= ~( 1 << w );
  2391. }
  2392. }
  2393. }
  2394. }
  2395. /*
  2396. ===============
  2397. idPlayer::Give
  2398. ===============
  2399. */
  2400. bool idPlayer::Give( const char *statname, const char *value ) {
  2401. int amount;
  2402. if ( AI_DEAD ) {
  2403. return false;
  2404. }
  2405. if ( !idStr::Icmp( statname, "health" ) ) {
  2406. if ( health >= inventory.maxHealth ) {
  2407. return false;
  2408. }
  2409. amount = atoi( value );
  2410. if ( amount ) {
  2411. health += amount;
  2412. if ( health > inventory.maxHealth ) {
  2413. health = inventory.maxHealth;
  2414. }
  2415. if ( hud ) {
  2416. hud->HandleNamedEvent( "healthPulse" );
  2417. }
  2418. }
  2419. } else if ( !idStr::Icmp( statname, "stamina" ) ) {
  2420. if ( stamina >= 100 ) {
  2421. return false;
  2422. }
  2423. stamina += atof( value );
  2424. if ( stamina > 100 ) {
  2425. stamina = 100;
  2426. }
  2427. } else if ( !idStr::Icmp( statname, "heartRate" ) ) {
  2428. heartRate += atoi( value );
  2429. if ( heartRate > MAX_HEARTRATE ) {
  2430. heartRate = MAX_HEARTRATE;
  2431. }
  2432. } else if ( !idStr::Icmp( statname, "air" ) ) {
  2433. if ( airTics >= pm_airTics.GetInteger() ) {
  2434. return false;
  2435. }
  2436. airTics += atoi( value ) / 100.0 * pm_airTics.GetInteger();
  2437. if ( airTics > pm_airTics.GetInteger() ) {
  2438. airTics = pm_airTics.GetInteger();
  2439. }
  2440. } else {
  2441. return inventory.Give( this, spawnArgs, statname, value, &idealWeapon, true );
  2442. }
  2443. return true;
  2444. }
  2445. /*
  2446. ===============
  2447. idPlayer::GiveHealthPool
  2448. adds health to the player health pool
  2449. ===============
  2450. */
  2451. void idPlayer::GiveHealthPool( float amt ) {
  2452. if ( AI_DEAD ) {
  2453. return;
  2454. }
  2455. if ( health > 0 ) {
  2456. healthPool += amt;
  2457. if ( healthPool > inventory.maxHealth - health ) {
  2458. healthPool = inventory.maxHealth - health;
  2459. }
  2460. nextHealthPulse = gameLocal.time;
  2461. }
  2462. }
  2463. /*
  2464. ===============
  2465. idPlayer::GiveItem
  2466. Returns false if the item shouldn't be picked up
  2467. ===============
  2468. */
  2469. bool idPlayer::GiveItem( idItem *item ) {
  2470. int i;
  2471. const idKeyValue *arg;
  2472. idDict attr;
  2473. bool gave;
  2474. int numPickup;
  2475. if ( gameLocal.isMultiplayer && spectating ) {
  2476. return false;
  2477. }
  2478. item->GetAttributes( attr );
  2479. gave = false;
  2480. numPickup = inventory.pickupItemNames.Num();
  2481. for( i = 0; i < attr.GetNumKeyVals(); i++ ) {
  2482. arg = attr.GetKeyVal( i );
  2483. if ( Give( arg->GetKey(), arg->GetValue() ) ) {
  2484. gave = true;
  2485. }
  2486. }
  2487. arg = item->spawnArgs.MatchPrefix( "inv_weapon", NULL );
  2488. if ( arg && hud ) {
  2489. // We need to update the weapon hud manually, but not
  2490. // the armor/ammo/health because they are updated every
  2491. // frame no matter what
  2492. UpdateHudWeapon( false );
  2493. hud->HandleNamedEvent( "weaponPulse" );
  2494. }
  2495. // display the pickup feedback on the hud
  2496. if ( gave && ( numPickup == inventory.pickupItemNames.Num() ) ) {
  2497. inventory.AddPickupName( item->spawnArgs.GetString( "inv_name" ), item->spawnArgs.GetString( "inv_icon" ) );
  2498. }
  2499. return gave;
  2500. }
  2501. /*
  2502. ===============
  2503. idPlayer::PowerUpModifier
  2504. ===============
  2505. */
  2506. float idPlayer::PowerUpModifier( int type ) {
  2507. float mod = 1.0f;
  2508. if ( PowerUpActive( BERSERK ) ) {
  2509. switch( type ) {
  2510. case SPEED: {
  2511. mod *= 1.7f;
  2512. break;
  2513. }
  2514. case PROJECTILE_DAMAGE: {
  2515. mod *= 2.0f;
  2516. break;
  2517. }
  2518. case MELEE_DAMAGE: {
  2519. mod *= 30.0f;
  2520. break;
  2521. }
  2522. case MELEE_DISTANCE: {
  2523. mod *= 2.0f;
  2524. break;
  2525. }
  2526. }
  2527. }
  2528. if ( gameLocal.isMultiplayer && !gameLocal.isClient ) {
  2529. if ( PowerUpActive( MEGAHEALTH ) ) {
  2530. if ( healthPool <= 0 ) {
  2531. GiveHealthPool( 100 );
  2532. }
  2533. } else {
  2534. healthPool = 0;
  2535. }
  2536. }
  2537. return mod;
  2538. }
  2539. /*
  2540. ===============
  2541. idPlayer::PowerUpActive
  2542. ===============
  2543. */
  2544. bool idPlayer::PowerUpActive( int powerup ) const {
  2545. return ( inventory.powerups & ( 1 << powerup ) ) != 0;
  2546. }
  2547. /*
  2548. ===============
  2549. idPlayer::GivePowerUp
  2550. ===============
  2551. */
  2552. bool idPlayer::GivePowerUp( int powerup, int time ) {
  2553. const char *sound;
  2554. const char *skin;
  2555. if ( powerup >= 0 && powerup < MAX_POWERUPS ) {
  2556. if ( gameLocal.isServer ) {
  2557. idBitMsg msg;
  2558. byte msgBuf[MAX_EVENT_PARAM_SIZE];
  2559. msg.Init( msgBuf, sizeof( msgBuf ) );
  2560. msg.WriteShort( powerup );
  2561. msg.WriteBits( 1, 1 );
  2562. ServerSendEvent( EVENT_POWERUP, &msg, false, -1 );
  2563. }
  2564. if ( powerup != MEGAHEALTH ) {
  2565. inventory.GivePowerUp( this, powerup, time );
  2566. }
  2567. const idDeclEntityDef *def = NULL;
  2568. switch( powerup ) {
  2569. case BERSERK: {
  2570. if ( spawnArgs.GetString( "snd_berserk_third", "", &sound ) ) {
  2571. StartSoundShader( declManager->FindSound( sound ), SND_CHANNEL_DEMONIC, 0, false, NULL );
  2572. }
  2573. if ( baseSkinName.Length() ) {
  2574. powerUpSkin = declManager->FindSkin( baseSkinName + "_berserk" );
  2575. }
  2576. if ( !gameLocal.isClient ) {
  2577. idealWeapon = 0;
  2578. }
  2579. break;
  2580. }
  2581. case INVISIBILITY: {
  2582. spawnArgs.GetString( "skin_invisibility", "", &skin );
  2583. powerUpSkin = declManager->FindSkin( skin );
  2584. // remove any decals from the model
  2585. if ( modelDefHandle != -1 ) {
  2586. gameRenderWorld->RemoveDecals( modelDefHandle );
  2587. }
  2588. if ( weapon.GetEntity() ) {
  2589. weapon.GetEntity()->UpdateSkin();
  2590. }
  2591. if ( spawnArgs.GetString( "snd_invisibility", "", &sound ) ) {
  2592. StartSoundShader( declManager->FindSound( sound ), SND_CHANNEL_ANY, 0, false, NULL );
  2593. }
  2594. break;
  2595. }
  2596. case ADRENALINE: {
  2597. stamina = 100.0f;
  2598. break;
  2599. }
  2600. case MEGAHEALTH: {
  2601. if ( spawnArgs.GetString( "snd_megahealth", "", &sound ) ) {
  2602. StartSoundShader( declManager->FindSound( sound ), SND_CHANNEL_ANY, 0, false, NULL );
  2603. }
  2604. def = gameLocal.FindEntityDef( "powerup_megahealth", false );
  2605. if ( def ) {
  2606. health = def->dict.GetInt( "inv_health" );
  2607. }
  2608. break;
  2609. }
  2610. }
  2611. if ( hud ) {
  2612. hud->HandleNamedEvent( "itemPickup" );
  2613. }
  2614. return true;
  2615. } else {
  2616. gameLocal.Warning( "Player given power up %i\n which is out of range", powerup );
  2617. }
  2618. return false;
  2619. }
  2620. /*
  2621. ==============
  2622. idPlayer::ClearPowerup
  2623. ==============
  2624. */
  2625. void idPlayer::ClearPowerup( int i ) {
  2626. if ( gameLocal.isServer ) {
  2627. idBitMsg msg;
  2628. byte msgBuf[MAX_EVENT_PARAM_SIZE];
  2629. msg.Init( msgBuf, sizeof( msgBuf ) );
  2630. msg.WriteShort( i );
  2631. msg.WriteBits( 0, 1 );
  2632. ServerSendEvent( EVENT_POWERUP, &msg, false, -1 );
  2633. }
  2634. powerUpSkin = NULL;
  2635. inventory.powerups &= ~( 1 << i );
  2636. inventory.powerupEndTime[ i ] = 0;
  2637. switch( i ) {
  2638. case BERSERK: {
  2639. StopSound( SND_CHANNEL_DEMONIC, false );
  2640. break;
  2641. }
  2642. case INVISIBILITY: {
  2643. if ( weapon.GetEntity() ) {
  2644. weapon.GetEntity()->UpdateSkin();
  2645. }
  2646. break;
  2647. }
  2648. }
  2649. }
  2650. /*
  2651. ==============
  2652. idPlayer::UpdatePowerUps
  2653. ==============
  2654. */
  2655. void idPlayer::UpdatePowerUps( void ) {
  2656. int i;
  2657. if ( !gameLocal.isClient ) {
  2658. for ( i = 0; i < MAX_POWERUPS; i++ ) {
  2659. if ( PowerUpActive( i ) && inventory.powerupEndTime[i] <= gameLocal.time ) {
  2660. ClearPowerup( i );
  2661. }
  2662. }
  2663. }
  2664. if ( health > 0 ) {
  2665. if ( powerUpSkin ) {
  2666. renderEntity.customSkin = powerUpSkin;
  2667. } else {
  2668. renderEntity.customSkin = skin;
  2669. }
  2670. }
  2671. if ( healthPool && gameLocal.time > nextHealthPulse && !AI_DEAD && health > 0 ) {
  2672. assert( !gameLocal.isClient ); // healthPool never be set on client
  2673. int amt = ( healthPool > 5 ) ? 5 : healthPool;
  2674. health += amt;
  2675. if ( health > inventory.maxHealth ) {
  2676. health = inventory.maxHealth;
  2677. healthPool = 0;
  2678. } else {
  2679. healthPool -= amt;
  2680. }
  2681. nextHealthPulse = gameLocal.time + HEALTHPULSE_TIME;
  2682. healthPulse = true;
  2683. }
  2684. #ifndef ID_DEMO_BUILD
  2685. if ( !gameLocal.inCinematic && influenceActive == 0 && g_skill.GetInteger() == 3 && gameLocal.time > nextHealthTake && !AI_DEAD && health > g_healthTakeLimit.GetInteger() ) {
  2686. assert( !gameLocal.isClient ); // healthPool never be set on client
  2687. health -= g_healthTakeAmt.GetInteger();
  2688. if ( health < g_healthTakeLimit.GetInteger() ) {
  2689. health = g_healthTakeLimit.GetInteger();
  2690. }
  2691. nextHealthTake = gameLocal.time + g_healthTakeTime.GetInteger() * 1000;
  2692. healthTake = true;
  2693. }
  2694. #endif
  2695. }
  2696. /*
  2697. ===============
  2698. idPlayer::ClearPowerUps
  2699. ===============
  2700. */
  2701. void idPlayer::ClearPowerUps( void ) {
  2702. int i;
  2703. for ( i = 0; i < MAX_POWERUPS; i++ ) {
  2704. if ( PowerUpActive( i ) ) {
  2705. ClearPowerup( i );
  2706. }
  2707. }
  2708. inventory.ClearPowerUps();
  2709. }
  2710. /*
  2711. ===============
  2712. idPlayer::GiveInventoryItem
  2713. ===============
  2714. */
  2715. bool idPlayer::GiveInventoryItem( idDict *item ) {
  2716. if ( gameLocal.isMultiplayer && spectating ) {
  2717. return false;
  2718. }
  2719. inventory.items.Append( new idDict( *item ) );
  2720. idItemInfo info;
  2721. const char* itemName = item->GetString( "inv_name" );
  2722. if ( idStr::Cmpn( itemName, STRTABLE_ID, STRTABLE_ID_LENGTH ) == 0 ) {
  2723. info.name = common->GetLanguageDict()->GetString( itemName );
  2724. } else {
  2725. info.name = itemName;
  2726. }
  2727. info.icon = item->GetString( "inv_icon" );
  2728. inventory.pickupItemNames.Append( info );
  2729. if ( hud ) {
  2730. hud->SetStateString( "itemicon", info.icon );
  2731. hud->HandleNamedEvent( "invPickup" );
  2732. }
  2733. return true;
  2734. }
  2735. /*
  2736. ==============
  2737. idPlayer::UpdateObjectiveInfo
  2738. ==============
  2739. */
  2740. void idPlayer::UpdateObjectiveInfo( void ) {
  2741. if ( objectiveSystem == NULL ) {
  2742. return;
  2743. }
  2744. objectiveSystem->SetStateString( "objective1", "" );
  2745. objectiveSystem->SetStateString( "objective2", "" );
  2746. objectiveSystem->SetStateString( "objective3", "" );
  2747. for ( int i = 0; i < inventory.objectiveNames.Num(); i++ ) {
  2748. objectiveSystem->SetStateString( va( "objective%i", i+1 ), "1" );
  2749. objectiveSystem->SetStateString( va( "objectivetitle%i", i+1 ), inventory.objectiveNames[i].title.c_str() );
  2750. objectiveSystem->SetStateString( va( "objectivetext%i", i+1 ), inventory.objectiveNames[i].text.c_str() );
  2751. objectiveSystem->SetStateString( va( "objectiveshot%i", i+1 ), inventory.objectiveNames[i].screenshot.c_str() );
  2752. }
  2753. objectiveSystem->StateChanged( gameLocal.time );
  2754. }
  2755. /*
  2756. ===============
  2757. idPlayer::GiveObjective
  2758. ===============
  2759. */
  2760. void idPlayer::GiveObjective( const char *title, const char *text, const char *screenshot ) {
  2761. idObjectiveInfo info;
  2762. info.title = title;
  2763. info.text = text;
  2764. info.screenshot = screenshot;
  2765. inventory.objectiveNames.Append( info );
  2766. ShowObjective( "newObjective" );
  2767. if ( hud ) {
  2768. hud->HandleNamedEvent( "newObjective" );
  2769. }
  2770. }
  2771. /*
  2772. ===============
  2773. idPlayer::CompleteObjective
  2774. ===============
  2775. */
  2776. void idPlayer::CompleteObjective( const char *title ) {
  2777. int c = inventory.objectiveNames.Num();
  2778. for ( int i = 0; i < c; i++ ) {
  2779. if ( idStr::Icmp(inventory.objectiveNames[i].title, title) == 0 ) {
  2780. inventory.objectiveNames.RemoveIndex( i );
  2781. break;
  2782. }
  2783. }
  2784. ShowObjective( "newObjectiveComplete" );
  2785. if ( hud ) {
  2786. hud->HandleNamedEvent( "newObjectiveComplete" );
  2787. }
  2788. }
  2789. /*
  2790. ===============
  2791. idPlayer::GiveVideo
  2792. ===============
  2793. */
  2794. void idPlayer::GiveVideo( const char *videoName, idDict *item ) {
  2795. if ( videoName == NULL || *videoName == NULL ) {
  2796. return;
  2797. }
  2798. inventory.videos.AddUnique( videoName );
  2799. if ( item ) {
  2800. idItemInfo info;
  2801. info.name = item->GetString( "inv_name" );
  2802. info.icon = item->GetString( "inv_icon" );
  2803. inventory.pickupItemNames.Append( info );
  2804. }
  2805. if ( hud ) {
  2806. hud->HandleNamedEvent( "videoPickup" );
  2807. }
  2808. }
  2809. /*
  2810. ===============
  2811. idPlayer::GiveSecurity
  2812. ===============
  2813. */
  2814. void idPlayer::GiveSecurity( const char *security ) {
  2815. GetPDA()->SetSecurity( security );
  2816. if ( hud ) {
  2817. hud->SetStateString( "pda_security", "1" );
  2818. hud->HandleNamedEvent( "securityPickup" );
  2819. }
  2820. }
  2821. /*
  2822. ===============
  2823. idPlayer::GiveEmail
  2824. ===============
  2825. */
  2826. void idPlayer::GiveEmail( const char *emailName ) {
  2827. if ( emailName == NULL || *emailName == NULL ) {
  2828. return;
  2829. }
  2830. inventory.emails.AddUnique( emailName );
  2831. GetPDA()->AddEmail( emailName );
  2832. if ( hud ) {
  2833. hud->HandleNamedEvent( "emailPickup" );
  2834. }
  2835. }
  2836. /*
  2837. ===============
  2838. idPlayer::GivePDA
  2839. ===============
  2840. */
  2841. void idPlayer::GivePDA( const char *pdaName, idDict *item )
  2842. {
  2843. if ( gameLocal.isMultiplayer && spectating ) {
  2844. return;
  2845. }
  2846. if ( item ) {
  2847. inventory.pdaSecurity.AddUnique( item->GetString( "inv_name" ) );
  2848. }
  2849. if ( pdaName == NULL || *pdaName == NULL ) {
  2850. pdaName = "personal";
  2851. }
  2852. const idDeclPDA *pda = static_cast< const idDeclPDA* >( declManager->FindType( DECL_PDA, pdaName ) );
  2853. inventory.pdas.AddUnique( pdaName );
  2854. // Copy any videos over
  2855. for ( int i = 0; i < pda->GetNumVideos(); i++ ) {
  2856. const idDeclVideo *video = pda->GetVideoByIndex( i );
  2857. if ( video ) {
  2858. inventory.videos.AddUnique( video->GetName() );
  2859. }
  2860. }
  2861. // This is kind of a hack, but it works nicely
  2862. // We don't want to display the 'you got a new pda' message during a map load
  2863. if ( gameLocal.GetFrameNum() > 10 ) {
  2864. if ( pda && hud ) {
  2865. idStr pdaName = pda->GetPdaName();
  2866. pdaName.RemoveColors();
  2867. hud->SetStateString( "pda", "1" );
  2868. hud->SetStateString( "pda_text", pdaName );
  2869. const char *sec = pda->GetSecurity();
  2870. hud->SetStateString( "pda_security", ( sec && *sec ) ? "1" : "0" );
  2871. hud->HandleNamedEvent( "pdaPickup" );
  2872. }
  2873. if ( inventory.pdas.Num() == 1 ) {
  2874. GetPDA()->RemoveAddedEmailsAndVideos();
  2875. if ( !objectiveSystemOpen ) {
  2876. TogglePDA();
  2877. }
  2878. objectiveSystem->HandleNamedEvent( "showPDATip" );
  2879. //ShowTip( spawnArgs.GetString( "text_infoTitle" ), spawnArgs.GetString( "text_firstPDA" ), true );
  2880. }
  2881. if ( inventory.pdas.Num() > 1 && pda->GetNumVideos() > 0 && hud ) {
  2882. hud->HandleNamedEvent( "videoPickup" );
  2883. }
  2884. }
  2885. }
  2886. /*
  2887. ===============
  2888. idPlayer::FindInventoryItem
  2889. ===============
  2890. */
  2891. idDict *idPlayer::FindInventoryItem( const char *name ) {
  2892. for ( int i = 0; i < inventory.items.Num(); i++ ) {
  2893. const char *iname = inventory.items[i]->GetString( "inv_name" );
  2894. if ( iname && *iname ) {
  2895. if ( idStr::Icmp( name, iname ) == 0 ) {
  2896. return inventory.items[i];
  2897. }
  2898. }
  2899. }
  2900. return NULL;
  2901. }
  2902. /*
  2903. ===============
  2904. idPlayer::RemoveInventoryItem
  2905. ===============
  2906. */
  2907. void idPlayer::RemoveInventoryItem( const char *name ) {
  2908. idDict *item = FindInventoryItem(name);
  2909. if ( item ) {
  2910. RemoveInventoryItem( item );
  2911. }
  2912. }
  2913. /*
  2914. ===============
  2915. idPlayer::RemoveInventoryItem
  2916. ===============
  2917. */
  2918. void idPlayer::RemoveInventoryItem( idDict *item ) {
  2919. inventory.items.Remove( item );
  2920. delete item;
  2921. }
  2922. /*
  2923. ===============
  2924. idPlayer::GiveItem
  2925. ===============
  2926. */
  2927. void idPlayer::GiveItem( const char *itemname ) {
  2928. idDict args;
  2929. args.Set( "classname", itemname );
  2930. args.Set( "owner", name.c_str() );
  2931. gameLocal.SpawnEntityDef( args );
  2932. if ( hud ) {
  2933. hud->HandleNamedEvent( "itemPickup" );
  2934. }
  2935. }
  2936. /*
  2937. ==================
  2938. idPlayer::SlotForWeapon
  2939. ==================
  2940. */
  2941. int idPlayer::SlotForWeapon( const char *weaponName ) {
  2942. int i;
  2943. for( i = 0; i < MAX_WEAPONS; i++ ) {
  2944. const char *weap = spawnArgs.GetString( va( "def_weapon%d", i ) );
  2945. if ( !idStr::Cmp( weap, weaponName ) ) {
  2946. return i;
  2947. }
  2948. }
  2949. // not found
  2950. return -1;
  2951. }
  2952. /*
  2953. ===============
  2954. idPlayer::Reload
  2955. ===============
  2956. */
  2957. void idPlayer::Reload( void ) {
  2958. if ( gameLocal.isClient ) {
  2959. return;
  2960. }
  2961. if ( spectating || gameLocal.inCinematic || influenceActive ) {
  2962. return;
  2963. }
  2964. if ( weapon.GetEntity() && weapon.GetEntity()->IsLinked() ) {
  2965. weapon.GetEntity()->Reload();
  2966. }
  2967. }
  2968. /*
  2969. ===============
  2970. idPlayer::NextBestWeapon
  2971. ===============
  2972. */
  2973. void idPlayer::NextBestWeapon( void ) {
  2974. const char *weap;
  2975. int w = MAX_WEAPONS;
  2976. if ( gameLocal.isClient || !weaponEnabled ) {
  2977. return;
  2978. }
  2979. while ( w > 0 ) {
  2980. w--;
  2981. weap = spawnArgs.GetString( va( "def_weapon%d", w ) );
  2982. if ( !weap[ 0 ] || ( ( inventory.weapons & ( 1 << w ) ) == 0 ) || ( !inventory.HasAmmo( weap ) ) ) {
  2983. continue;
  2984. }
  2985. if ( !spawnArgs.GetBool( va( "weapon%d_best", w ) ) ) {
  2986. continue;
  2987. }
  2988. break;
  2989. }
  2990. idealWeapon = w;
  2991. weaponSwitchTime = gameLocal.time + WEAPON_SWITCH_DELAY;
  2992. UpdateHudWeapon();
  2993. }
  2994. /*
  2995. ===============
  2996. idPlayer::NextWeapon
  2997. ===============
  2998. */
  2999. void idPlayer::NextWeapon( void ) {
  3000. const char *weap;
  3001. int w;
  3002. if ( !weaponEnabled || spectating || hiddenWeapon || gameLocal.inCinematic || gameLocal.world->spawnArgs.GetBool( "no_Weapons" ) || health < 0 ) {
  3003. return;
  3004. }
  3005. if ( gameLocal.isClient ) {
  3006. return;
  3007. }
  3008. // check if we have any weapons
  3009. if ( !inventory.weapons ) {
  3010. return;
  3011. }
  3012. w = idealWeapon;
  3013. while( 1 ) {
  3014. w++;
  3015. if ( w >= MAX_WEAPONS ) {
  3016. w = 0;
  3017. }
  3018. weap = spawnArgs.GetString( va( "def_weapon%d", w ) );
  3019. if ( !spawnArgs.GetBool( va( "weapon%d_cycle", w ) ) ) {
  3020. continue;
  3021. }
  3022. if ( !weap[ 0 ] ) {
  3023. continue;
  3024. }
  3025. if ( ( inventory.weapons & ( 1 << w ) ) == 0 ) {
  3026. continue;
  3027. }
  3028. if ( inventory.HasAmmo( weap ) ) {
  3029. break;
  3030. }
  3031. }
  3032. if ( ( w != currentWeapon ) && ( w != idealWeapon ) ) {
  3033. idealWeapon = w;
  3034. weaponSwitchTime = gameLocal.time + WEAPON_SWITCH_DELAY;
  3035. UpdateHudWeapon();
  3036. }
  3037. }
  3038. /*
  3039. ===============
  3040. idPlayer::PrevWeapon
  3041. ===============
  3042. */
  3043. void idPlayer::PrevWeapon( void ) {
  3044. const char *weap;
  3045. int w;
  3046. if ( !weaponEnabled || spectating || hiddenWeapon || gameLocal.inCinematic || gameLocal.world->spawnArgs.GetBool( "no_Weapons" ) || health < 0 ) {
  3047. return;
  3048. }
  3049. if ( gameLocal.isClient ) {
  3050. return;
  3051. }
  3052. // check if we have any weapons
  3053. if ( !inventory.weapons ) {
  3054. return;
  3055. }
  3056. w = idealWeapon;
  3057. while( 1 ) {
  3058. w--;
  3059. if ( w < 0 ) {
  3060. w = MAX_WEAPONS - 1;
  3061. }
  3062. weap = spawnArgs.GetString( va( "def_weapon%d", w ) );
  3063. if ( !spawnArgs.GetBool( va( "weapon%d_cycle", w ) ) ) {
  3064. continue;
  3065. }
  3066. if ( !weap[ 0 ] ) {
  3067. continue;
  3068. }
  3069. if ( ( inventory.weapons & ( 1 << w ) ) == 0 ) {
  3070. continue;
  3071. }
  3072. if ( inventory.HasAmmo( weap ) ) {
  3073. break;
  3074. }
  3075. }
  3076. if ( ( w != currentWeapon ) && ( w != idealWeapon ) ) {
  3077. idealWeapon = w;
  3078. weaponSwitchTime = gameLocal.time + WEAPON_SWITCH_DELAY;
  3079. UpdateHudWeapon();
  3080. }
  3081. }
  3082. /*
  3083. ===============
  3084. idPlayer::SelectWeapon
  3085. ===============
  3086. */
  3087. void idPlayer::SelectWeapon( int num, bool force ) {
  3088. const char *weap;
  3089. if ( !weaponEnabled || spectating || gameLocal.inCinematic || health < 0 ) {
  3090. return;
  3091. }
  3092. if ( ( num < 0 ) || ( num >= MAX_WEAPONS ) ) {
  3093. return;
  3094. }
  3095. if ( gameLocal.isClient ) {
  3096. return;
  3097. }
  3098. if ( ( num != weapon_pda ) && gameLocal.world->spawnArgs.GetBool( "no_Weapons" ) ) {
  3099. num = weapon_fists;
  3100. hiddenWeapon ^= 1;
  3101. if ( hiddenWeapon && weapon.GetEntity() ) {
  3102. weapon.GetEntity()->LowerWeapon();
  3103. } else {
  3104. weapon.GetEntity()->RaiseWeapon();
  3105. }
  3106. }
  3107. weap = spawnArgs.GetString( va( "def_weapon%d", num ) );
  3108. if ( !weap[ 0 ] ) {
  3109. gameLocal.Printf( "Invalid weapon\n" );
  3110. return;
  3111. }
  3112. if ( force || ( inventory.weapons & ( 1 << num ) ) ) {
  3113. if ( !inventory.HasAmmo( weap ) && !spawnArgs.GetBool( va( "weapon%d_allowempty", num ) ) ) {
  3114. return;
  3115. }
  3116. if ( ( previousWeapon >= 0 ) && ( idealWeapon == num ) && ( spawnArgs.GetBool( va( "weapon%d_toggle", num ) ) ) ) {
  3117. weap = spawnArgs.GetString( va( "def_weapon%d", previousWeapon ) );
  3118. if ( !inventory.HasAmmo( weap ) && !spawnArgs.GetBool( va( "weapon%d_allowempty", previousWeapon ) ) ) {
  3119. return;
  3120. }
  3121. idealWeapon = previousWeapon;
  3122. } else if ( ( weapon_pda >= 0 ) && ( num == weapon_pda ) && ( inventory.pdas.Num() == 0 ) ) {
  3123. ShowTip( spawnArgs.GetString( "text_infoTitle" ), spawnArgs.GetString( "text_noPDA" ), true );
  3124. return;
  3125. } else {
  3126. idealWeapon = num;
  3127. }
  3128. UpdateHudWeapon();
  3129. }
  3130. }
  3131. /*
  3132. =================
  3133. idPlayer::DropWeapon
  3134. =================
  3135. */
  3136. void idPlayer::DropWeapon( bool died ) {
  3137. idVec3 forward, up;
  3138. int inclip, ammoavailable;
  3139. assert( !gameLocal.isClient );
  3140. if ( spectating || weaponGone || weapon.GetEntity() == NULL ) {
  3141. return;
  3142. }
  3143. if ( ( !died && !weapon.GetEntity()->IsReady() ) || weapon.GetEntity()->IsReloading() ) {
  3144. return;
  3145. }
  3146. // ammoavailable is how many shots we can fire
  3147. // inclip is which amount is in clip right now
  3148. ammoavailable = weapon.GetEntity()->AmmoAvailable();
  3149. inclip = weapon.GetEntity()->AmmoInClip();
  3150. // don't drop a grenade if we have none left
  3151. if ( !idStr::Icmp( idWeapon::GetAmmoNameForNum( weapon.GetEntity()->GetAmmoType() ), "ammo_grenades" ) && ( ammoavailable - inclip <= 0 ) ) {
  3152. return;
  3153. }
  3154. // expect an ammo setup that makes sense before doing any dropping
  3155. // ammoavailable is -1 for infinite ammo, and weapons like chainsaw
  3156. // a bad ammo config usually indicates a bad weapon state, so we should not drop
  3157. // used to be an assertion check, but it still happens in edge cases
  3158. if ( ( ammoavailable != -1 ) && ( ammoavailable - inclip < 0 ) ) {
  3159. common->DPrintf( "idPlayer::DropWeapon: bad ammo setup\n" );
  3160. return;
  3161. }
  3162. idEntity *item = NULL;
  3163. if ( died ) {
  3164. // ain't gonna throw you no weapon if I'm dead
  3165. item = weapon.GetEntity()->DropItem( vec3_origin, 0, WEAPON_DROP_TIME, died );
  3166. } else {
  3167. viewAngles.ToVectors( &forward, NULL, &up );
  3168. item = weapon.GetEntity()->DropItem( 250.0f * forward + 150.0f * up, 500, WEAPON_DROP_TIME, died );
  3169. }
  3170. if ( !item ) {
  3171. return;
  3172. }
  3173. // set the appropriate ammo in the dropped object
  3174. const idKeyValue * keyval = item->spawnArgs.MatchPrefix( "inv_ammo_" );
  3175. if ( keyval ) {
  3176. item->spawnArgs.SetInt( keyval->GetKey(), ammoavailable );
  3177. idStr inclipKey = keyval->GetKey();
  3178. inclipKey.Insert( "inclip_", 4 );
  3179. item->spawnArgs.SetInt( inclipKey, inclip );
  3180. }
  3181. if ( !died ) {
  3182. // remove from our local inventory completely
  3183. inventory.Drop( spawnArgs, item->spawnArgs.GetString( "inv_weapon" ), -1 );
  3184. weapon.GetEntity()->ResetAmmoClip();
  3185. NextWeapon();
  3186. weapon.GetEntity()->WeaponStolen();
  3187. weaponGone = true;
  3188. }
  3189. }
  3190. /*
  3191. =================
  3192. idPlayer::StealWeapon
  3193. steal the target player's current weapon
  3194. =================
  3195. */
  3196. void idPlayer::StealWeapon( idPlayer *player ) {
  3197. assert( !gameLocal.isClient );
  3198. // make sure there's something to steal
  3199. idWeapon *player_weapon = static_cast< idWeapon * >( player->weapon.GetEntity() );
  3200. if ( !player_weapon || !player_weapon->CanDrop() || weaponGone ) {
  3201. return;
  3202. }
  3203. // steal - we need to effectively force the other player to abandon his weapon
  3204. int newweap = player->currentWeapon;
  3205. if ( newweap == -1 ) {
  3206. return;
  3207. }
  3208. // might be just dropped - check inventory
  3209. if ( ! ( player->inventory.weapons & ( 1 << newweap ) ) ) {
  3210. return;
  3211. }
  3212. const char *weapon_classname = spawnArgs.GetString( va( "def_weapon%d", newweap ) );
  3213. assert( weapon_classname );
  3214. int ammoavailable = player->weapon.GetEntity()->AmmoAvailable();
  3215. int inclip = player->weapon.GetEntity()->AmmoInClip();
  3216. if ( ( ammoavailable != -1 ) && ( ammoavailable - inclip < 0 ) ) {
  3217. // see DropWeapon
  3218. common->DPrintf( "idPlayer::StealWeapon: bad ammo setup\n" );
  3219. // we still steal the weapon, so let's use the default ammo levels
  3220. inclip = -1;
  3221. const idDeclEntityDef *decl = gameLocal.FindEntityDef( weapon_classname );
  3222. assert( decl );
  3223. const idKeyValue *keypair = decl->dict.MatchPrefix( "inv_ammo_" );
  3224. assert( keypair );
  3225. ammoavailable = atoi( keypair->GetValue() );
  3226. }
  3227. player->weapon.GetEntity()->WeaponStolen();
  3228. player->inventory.Drop( player->spawnArgs, NULL, newweap );
  3229. player->SelectWeapon( weapon_fists, false );
  3230. // in case the robbed player is firing rounds with a continuous fire weapon like the chaingun/plasma etc.
  3231. // this will ensure the firing actually stops
  3232. player->weaponGone = true;
  3233. // give weapon, setup the ammo count
  3234. Give( "weapon", weapon_classname );
  3235. ammo_t ammo_i = player->inventory.AmmoIndexForWeaponClass( weapon_classname, NULL );
  3236. idealWeapon = newweap;
  3237. inventory.ammo[ ammo_i ] += ammoavailable;
  3238. inventory.clip[ newweap ] = inclip;
  3239. }
  3240. /*
  3241. ===============
  3242. idPlayer::ActiveGui
  3243. ===============
  3244. */
  3245. idUserInterface *idPlayer::ActiveGui( void ) {
  3246. if ( objectiveSystemOpen ) {
  3247. return objectiveSystem;
  3248. }
  3249. return focusUI;
  3250. }
  3251. /*
  3252. ===============
  3253. idPlayer::Weapon_Combat
  3254. ===============
  3255. */
  3256. void idPlayer::Weapon_Combat( void ) {
  3257. if ( influenceActive || !weaponEnabled || gameLocal.inCinematic || privateCameraView ) {
  3258. return;
  3259. }
  3260. weapon.GetEntity()->RaiseWeapon();
  3261. if ( weapon.GetEntity()->IsReloading() ) {
  3262. if ( !AI_RELOAD ) {
  3263. AI_RELOAD = true;
  3264. SetState( "ReloadWeapon" );
  3265. UpdateScript();
  3266. }
  3267. } else {
  3268. AI_RELOAD = false;
  3269. }
  3270. if ( idealWeapon == weapon_soulcube && soulCubeProjectile.GetEntity() != NULL ) {
  3271. idealWeapon = currentWeapon;
  3272. }
  3273. if ( idealWeapon != currentWeapon ) {
  3274. if ( weaponCatchup ) {
  3275. assert( gameLocal.isClient );
  3276. currentWeapon = idealWeapon;
  3277. weaponGone = false;
  3278. animPrefix = spawnArgs.GetString( va( "def_weapon%d", currentWeapon ) );
  3279. weapon.GetEntity()->GetWeaponDef( animPrefix, inventory.clip[ currentWeapon ] );
  3280. animPrefix.Strip( "weapon_" );
  3281. weapon.GetEntity()->NetCatchup();
  3282. const function_t *newstate = GetScriptFunction( "NetCatchup" );
  3283. if ( newstate ) {
  3284. SetState( newstate );
  3285. UpdateScript();
  3286. }
  3287. weaponCatchup = false;
  3288. } else {
  3289. if ( weapon.GetEntity()->IsReady() ) {
  3290. weapon.GetEntity()->PutAway();
  3291. }
  3292. if ( weapon.GetEntity()->IsHolstered() ) {
  3293. assert( idealWeapon >= 0 );
  3294. assert( idealWeapon < MAX_WEAPONS );
  3295. if ( currentWeapon != weapon_pda && !spawnArgs.GetBool( va( "weapon%d_toggle", currentWeapon ) ) ) {
  3296. previousWeapon = currentWeapon;
  3297. }
  3298. currentWeapon = idealWeapon;
  3299. weaponGone = false;
  3300. animPrefix = spawnArgs.GetString( va( "def_weapon%d", currentWeapon ) );
  3301. weapon.GetEntity()->GetWeaponDef( animPrefix, inventory.clip[ currentWeapon ] );
  3302. animPrefix.Strip( "weapon_" );
  3303. weapon.GetEntity()->Raise();
  3304. }
  3305. }
  3306. } else {
  3307. weaponGone = false; // if you drop and re-get weap, you may miss the = false above
  3308. if ( weapon.GetEntity()->IsHolstered() ) {
  3309. if ( !weapon.GetEntity()->AmmoAvailable() ) {
  3310. // weapons can switch automatically if they have no more ammo
  3311. NextBestWeapon();
  3312. } else {
  3313. weapon.GetEntity()->Raise();
  3314. state = GetScriptFunction( "RaiseWeapon" );
  3315. if ( state ) {
  3316. SetState( state );
  3317. }
  3318. }
  3319. }
  3320. }
  3321. // check for attack
  3322. AI_WEAPON_FIRED = false;
  3323. if ( !influenceActive ) {
  3324. if ( ( usercmd.buttons & BUTTON_ATTACK ) && !weaponGone ) {
  3325. FireWeapon();
  3326. } else if ( oldButtons & BUTTON_ATTACK ) {
  3327. AI_ATTACK_HELD = false;
  3328. weapon.GetEntity()->EndAttack();
  3329. }
  3330. }
  3331. // update our ammo clip in our inventory
  3332. if ( ( currentWeapon >= 0 ) && ( currentWeapon < MAX_WEAPONS ) ) {
  3333. inventory.clip[ currentWeapon ] = weapon.GetEntity()->AmmoInClip();
  3334. if ( hud && ( currentWeapon == idealWeapon ) ) {
  3335. UpdateHudAmmo( hud );
  3336. }
  3337. }
  3338. }
  3339. /*
  3340. ===============
  3341. idPlayer::Weapon_NPC
  3342. ===============
  3343. */
  3344. void idPlayer::Weapon_NPC( void ) {
  3345. if ( idealWeapon != currentWeapon ) {
  3346. Weapon_Combat();
  3347. }
  3348. StopFiring();
  3349. weapon.GetEntity()->LowerWeapon();
  3350. if ( ( usercmd.buttons & BUTTON_ATTACK ) && !( oldButtons & BUTTON_ATTACK ) ) {
  3351. buttonMask |= BUTTON_ATTACK;
  3352. focusCharacter->TalkTo( this );
  3353. }
  3354. }
  3355. /*
  3356. ===============
  3357. idPlayer::LowerWeapon
  3358. ===============
  3359. */
  3360. void idPlayer::LowerWeapon( void ) {
  3361. if ( weapon.GetEntity() && !weapon.GetEntity()->IsHidden() ) {
  3362. weapon.GetEntity()->LowerWeapon();
  3363. }
  3364. }
  3365. /*
  3366. ===============
  3367. idPlayer::RaiseWeapon
  3368. ===============
  3369. */
  3370. void idPlayer::RaiseWeapon( void ) {
  3371. if ( weapon.GetEntity() && weapon.GetEntity()->IsHidden() ) {
  3372. weapon.GetEntity()->RaiseWeapon();
  3373. }
  3374. }
  3375. /*
  3376. ===============
  3377. idPlayer::WeaponLoweringCallback
  3378. ===============
  3379. */
  3380. void idPlayer::WeaponLoweringCallback( void ) {
  3381. SetState( "LowerWeapon" );
  3382. UpdateScript();
  3383. }
  3384. /*
  3385. ===============
  3386. idPlayer::WeaponRisingCallback
  3387. ===============
  3388. */
  3389. void idPlayer::WeaponRisingCallback( void ) {
  3390. SetState( "RaiseWeapon" );
  3391. UpdateScript();
  3392. }
  3393. /*
  3394. ===============
  3395. idPlayer::Weapon_GUI
  3396. ===============
  3397. */
  3398. void idPlayer::Weapon_GUI( void ) {
  3399. if ( !objectiveSystemOpen ) {
  3400. if ( idealWeapon != currentWeapon ) {
  3401. Weapon_Combat();
  3402. }
  3403. StopFiring();
  3404. weapon.GetEntity()->LowerWeapon();
  3405. }
  3406. // disable click prediction for the GUIs. handy to check the state sync does the right thing
  3407. if ( gameLocal.isClient && !net_clientPredictGUI.GetBool() ) {
  3408. return;
  3409. }
  3410. if ( ( oldButtons ^ usercmd.buttons ) & BUTTON_ATTACK ) {
  3411. sysEvent_t ev;
  3412. const char *command = NULL;
  3413. bool updateVisuals = false;
  3414. idUserInterface *ui = ActiveGui();
  3415. if ( ui ) {
  3416. ev = sys->GenerateMouseButtonEvent( 1, ( usercmd.buttons & BUTTON_ATTACK ) != 0 );
  3417. command = ui->HandleEvent( &ev, gameLocal.time, &updateVisuals );
  3418. if ( updateVisuals && focusGUIent && ui == focusUI ) {
  3419. focusGUIent->UpdateVisuals();
  3420. }
  3421. }
  3422. if ( gameLocal.isClient ) {
  3423. // we predict enough, but don't want to execute commands
  3424. return;
  3425. }
  3426. if ( focusGUIent ) {
  3427. HandleGuiCommands( focusGUIent, command );
  3428. } else {
  3429. HandleGuiCommands( this, command );
  3430. }
  3431. }
  3432. }
  3433. /*
  3434. ===============
  3435. idPlayer::UpdateWeapon
  3436. ===============
  3437. */
  3438. void idPlayer::UpdateWeapon( void ) {
  3439. if ( health <= 0 ) {
  3440. return;
  3441. }
  3442. assert( !spectating );
  3443. if ( gameLocal.isClient ) {
  3444. // clients need to wait till the weapon and it's world model entity
  3445. // are present and synchronized ( weapon.worldModel idEntityPtr to idAnimatedEntity )
  3446. if ( !weapon.GetEntity()->IsWorldModelReady() ) {
  3447. return;
  3448. }
  3449. }
  3450. // always make sure the weapon is correctly setup before accessing it
  3451. if ( !weapon.GetEntity()->IsLinked() ) {
  3452. if ( idealWeapon != -1 ) {
  3453. animPrefix = spawnArgs.GetString( va( "def_weapon%d", idealWeapon ) );
  3454. weapon.GetEntity()->GetWeaponDef( animPrefix, inventory.clip[ idealWeapon ] );
  3455. assert( weapon.GetEntity()->IsLinked() );
  3456. } else {
  3457. return;
  3458. }
  3459. }
  3460. if ( hiddenWeapon && tipUp && usercmd.buttons & BUTTON_ATTACK ) {
  3461. HideTip();
  3462. }
  3463. if ( g_dragEntity.GetBool() ) {
  3464. StopFiring();
  3465. weapon.GetEntity()->LowerWeapon();
  3466. dragEntity.Update( this );
  3467. } else if ( ActiveGui() ) {
  3468. // gui handling overrides weapon use
  3469. Weapon_GUI();
  3470. } else if ( focusCharacter && ( focusCharacter->health > 0 ) ) {
  3471. Weapon_NPC();
  3472. } else {
  3473. Weapon_Combat();
  3474. }
  3475. if ( hiddenWeapon ) {
  3476. weapon.GetEntity()->LowerWeapon();
  3477. }
  3478. // update weapon state, particles, dlights, etc
  3479. weapon.GetEntity()->PresentWeapon( showWeaponViewModel );
  3480. }
  3481. /*
  3482. ===============
  3483. idPlayer::SpectateFreeFly
  3484. ===============
  3485. */
  3486. void idPlayer::SpectateFreeFly( bool force ) {
  3487. idPlayer *player;
  3488. idVec3 newOrig;
  3489. idVec3 spawn_origin;
  3490. idAngles spawn_angles;
  3491. player = gameLocal.GetClientByNum( spectator );
  3492. if ( force || gameLocal.time > lastSpectateChange ) {
  3493. spectator = entityNumber;
  3494. if ( player && player != this && !player->spectating && !player->IsInTeleport() ) {
  3495. newOrig = player->GetPhysics()->GetOrigin();
  3496. if ( player->physicsObj.IsCrouching() ) {
  3497. newOrig[ 2 ] += pm_crouchviewheight.GetFloat();
  3498. } else {
  3499. newOrig[ 2 ] += pm_normalviewheight.GetFloat();
  3500. }
  3501. newOrig[ 2 ] += SPECTATE_RAISE;
  3502. idBounds b = idBounds( vec3_origin ).Expand( pm_spectatebbox.GetFloat() * 0.5f );
  3503. idVec3 start = player->GetPhysics()->GetOrigin();
  3504. start[2] += pm_spectatebbox.GetFloat() * 0.5f;
  3505. trace_t t;
  3506. // assuming spectate bbox is inside stand or crouch box
  3507. gameLocal.clip.TraceBounds( t, start, newOrig, b, MASK_PLAYERSOLID, player );
  3508. newOrig.Lerp( start, newOrig, t.fraction );
  3509. SetOrigin( newOrig );
  3510. idAngles angle = player->viewAngles;
  3511. angle[ 2 ] = 0;
  3512. SetViewAngles( angle );
  3513. } else {
  3514. SelectInitialSpawnPoint( spawn_origin, spawn_angles );
  3515. spawn_origin[ 2 ] += pm_normalviewheight.GetFloat();
  3516. spawn_origin[ 2 ] += SPECTATE_RAISE;
  3517. SetOrigin( spawn_origin );
  3518. SetViewAngles( spawn_angles );
  3519. }
  3520. lastSpectateChange = gameLocal.time + 500;
  3521. }
  3522. }
  3523. /*
  3524. ===============
  3525. idPlayer::SpectateCycle
  3526. ===============
  3527. */
  3528. void idPlayer::SpectateCycle( void ) {
  3529. idPlayer *player;
  3530. if ( gameLocal.time > lastSpectateChange ) {
  3531. int latchedSpectator = spectator;
  3532. spectator = gameLocal.GetNextClientNum( spectator );
  3533. player = gameLocal.GetClientByNum( spectator );
  3534. assert( player ); // never call here when the current spectator is wrong
  3535. // ignore other spectators
  3536. while ( latchedSpectator != spectator && player->spectating ) {
  3537. spectator = gameLocal.GetNextClientNum( spectator );
  3538. player = gameLocal.GetClientByNum( spectator );
  3539. }
  3540. lastSpectateChange = gameLocal.time + 500;
  3541. }
  3542. }
  3543. /*
  3544. ===============
  3545. idPlayer::UpdateSpectating
  3546. ===============
  3547. */
  3548. void idPlayer::UpdateSpectating( void ) {
  3549. assert( spectating );
  3550. assert( !gameLocal.isClient );
  3551. assert( IsHidden() );
  3552. idPlayer *player;
  3553. if ( !gameLocal.isMultiplayer ) {
  3554. return;
  3555. }
  3556. player = gameLocal.GetClientByNum( spectator );
  3557. if ( !player || ( player->spectating && player != this ) ) {
  3558. SpectateFreeFly( true );
  3559. } else if ( usercmd.upmove > 0 ) {
  3560. SpectateFreeFly( false );
  3561. } else if ( usercmd.buttons & BUTTON_ATTACK ) {
  3562. SpectateCycle();
  3563. }
  3564. }
  3565. /*
  3566. ===============
  3567. idPlayer::HandleSingleGuiCommand
  3568. ===============
  3569. */
  3570. bool idPlayer::HandleSingleGuiCommand( idEntity *entityGui, idLexer *src ) {
  3571. idToken token;
  3572. if ( !src->ReadToken( &token ) ) {
  3573. return false;
  3574. }
  3575. if ( token == ";" ) {
  3576. return false;
  3577. }
  3578. if ( token.Icmp( "addhealth" ) == 0 ) {
  3579. if ( entityGui && health < 100 ) {
  3580. int _health = entityGui->spawnArgs.GetInt( "gui_parm1" );
  3581. int amt = ( _health >= HEALTH_PER_DOSE ) ? HEALTH_PER_DOSE : _health;
  3582. _health -= amt;
  3583. entityGui->spawnArgs.SetInt( "gui_parm1", _health );
  3584. if ( entityGui->GetRenderEntity() && entityGui->GetRenderEntity()->gui[ 0 ] ) {
  3585. entityGui->GetRenderEntity()->gui[ 0 ]->SetStateInt( "gui_parm1", _health );
  3586. }
  3587. health += amt;
  3588. if ( health > 100 ) {
  3589. health = 100;
  3590. }
  3591. }
  3592. return true;
  3593. }
  3594. if ( token.Icmp( "ready" ) == 0 ) {
  3595. PerformImpulse( IMPULSE_17 );
  3596. return true;
  3597. }
  3598. if ( token.Icmp( "updatepda" ) == 0 ) {
  3599. UpdatePDAInfo( true );
  3600. return true;
  3601. }
  3602. if ( token.Icmp( "updatepda2" ) == 0 ) {
  3603. UpdatePDAInfo( false );
  3604. return true;
  3605. }
  3606. if ( token.Icmp( "stoppdavideo" ) == 0 ) {
  3607. if ( objectiveSystem && objectiveSystemOpen && pdaVideoWave.Length() > 0 ) {
  3608. StopSound( SND_CHANNEL_PDA, false );
  3609. }
  3610. return true;
  3611. }
  3612. if ( token.Icmp( "close" ) == 0 ) {
  3613. if ( objectiveSystem && objectiveSystemOpen ) {
  3614. TogglePDA();
  3615. }
  3616. }
  3617. if ( token.Icmp( "playpdavideo" ) == 0 ) {
  3618. if ( objectiveSystem && objectiveSystemOpen && pdaVideo.Length() > 0 ) {
  3619. const idMaterial *mat = declManager->FindMaterial( pdaVideo );
  3620. if ( mat ) {
  3621. int c = mat->GetNumStages();
  3622. for ( int i = 0; i < c; i++ ) {
  3623. const shaderStage_t *stage = mat->GetStage(i);
  3624. if ( stage && stage->texture.cinematic ) {
  3625. stage->texture.cinematic->ResetTime( gameLocal.time );
  3626. }
  3627. }
  3628. if ( pdaVideoWave.Length() ) {
  3629. const idSoundShader *shader = declManager->FindSound( pdaVideoWave );
  3630. StartSoundShader( shader, SND_CHANNEL_PDA, 0, false, NULL );
  3631. }
  3632. }
  3633. }
  3634. }
  3635. if ( token.Icmp( "playpdaaudio" ) == 0 ) {
  3636. if ( objectiveSystem && objectiveSystemOpen && pdaAudio.Length() > 0 ) {
  3637. const idSoundShader *shader = declManager->FindSound( pdaAudio );
  3638. int ms;
  3639. StartSoundShader( shader, SND_CHANNEL_PDA, 0, false, &ms );
  3640. StartAudioLog();
  3641. CancelEvents( &EV_Player_StopAudioLog );
  3642. PostEventMS( &EV_Player_StopAudioLog, ms + 150 );
  3643. }
  3644. return true;
  3645. }
  3646. if ( token.Icmp( "stoppdaaudio" ) == 0 ) {
  3647. if ( objectiveSystem && objectiveSystemOpen && pdaAudio.Length() > 0 ) {
  3648. // idSoundShader *shader = declManager->FindSound( pdaAudio );
  3649. StopAudioLog();
  3650. StopSound( SND_CHANNEL_PDA, false );
  3651. }
  3652. return true;
  3653. }
  3654. src->UnreadToken( &token );
  3655. return false;
  3656. }
  3657. /*
  3658. ==============
  3659. idPlayer::Collide
  3660. ==============
  3661. */
  3662. bool idPlayer::Collide( const trace_t &collision, const idVec3 &velocity ) {
  3663. idEntity *other;
  3664. if ( gameLocal.isClient ) {
  3665. return false;
  3666. }
  3667. other = gameLocal.entities[ collision.c.entityNum ];
  3668. if ( other ) {
  3669. other->Signal( SIG_TOUCH );
  3670. if ( !spectating ) {
  3671. if ( other->RespondsTo( EV_Touch ) ) {
  3672. other->ProcessEvent( &EV_Touch, this, &collision );
  3673. }
  3674. } else {
  3675. if ( other->RespondsTo( EV_SpectatorTouch ) ) {
  3676. other->ProcessEvent( &EV_SpectatorTouch, this, &collision );
  3677. }
  3678. }
  3679. }
  3680. return false;
  3681. }
  3682. /*
  3683. ================
  3684. idPlayer::UpdateLocation
  3685. Searches nearby locations
  3686. ================
  3687. */
  3688. void idPlayer::UpdateLocation( void ) {
  3689. if ( hud ) {
  3690. idLocationEntity *locationEntity = gameLocal.LocationForPoint( GetEyePosition() );
  3691. if ( locationEntity ) {
  3692. hud->SetStateString( "location", locationEntity->GetLocation() );
  3693. } else {
  3694. hud->SetStateString( "location", common->GetLanguageDict()->GetString( "#str_02911" ) );
  3695. }
  3696. }
  3697. }
  3698. /*
  3699. ================
  3700. idPlayer::ClearFocus
  3701. Clears the focus cursor
  3702. ================
  3703. */
  3704. void idPlayer::ClearFocus( void ) {
  3705. focusCharacter = NULL;
  3706. focusGUIent = NULL;
  3707. focusUI = NULL;
  3708. focusVehicle = NULL;
  3709. talkCursor = 0;
  3710. }
  3711. /*
  3712. ================
  3713. idPlayer::UpdateFocus
  3714. Searches nearby entities for interactive guis, possibly making one of them
  3715. the focus and sending it a mouse move event
  3716. ================
  3717. */
  3718. void idPlayer::UpdateFocus( void ) {
  3719. idClipModel *clipModelList[ MAX_GENTITIES ];
  3720. idClipModel *clip;
  3721. int listedClipModels;
  3722. idEntity *oldFocus;
  3723. idEntity *ent;
  3724. idUserInterface *oldUI;
  3725. idAI *oldChar;
  3726. int oldTalkCursor;
  3727. idAFEntity_Vehicle *oldVehicle;
  3728. int i, j;
  3729. idVec3 start, end;
  3730. bool allowFocus;
  3731. const char *command;
  3732. trace_t trace;
  3733. guiPoint_t pt;
  3734. const idKeyValue *kv;
  3735. sysEvent_t ev;
  3736. idUserInterface *ui;
  3737. if ( gameLocal.inCinematic ) {
  3738. return;
  3739. }
  3740. // only update the focus character when attack button isn't pressed so players
  3741. // can still chainsaw NPC's
  3742. if ( gameLocal.isMultiplayer || ( !focusCharacter && ( usercmd.buttons & BUTTON_ATTACK ) ) ) {
  3743. allowFocus = false;
  3744. } else {
  3745. allowFocus = true;
  3746. }
  3747. oldFocus = focusGUIent;
  3748. oldUI = focusUI;
  3749. oldChar = focusCharacter;
  3750. oldTalkCursor = talkCursor;
  3751. oldVehicle = focusVehicle;
  3752. if ( focusTime <= gameLocal.time ) {
  3753. ClearFocus();
  3754. }
  3755. // don't let spectators interact with GUIs
  3756. if ( spectating ) {
  3757. return;
  3758. }
  3759. start = GetEyePosition();
  3760. end = start + viewAngles.ToForward() * 80.0f;
  3761. // player identification -> names to the hud
  3762. if ( gameLocal.isMultiplayer && entityNumber == gameLocal.localClientNum ) {
  3763. idVec3 end = start + viewAngles.ToForward() * 768.0f;
  3764. gameLocal.clip.TracePoint( trace, start, end, MASK_SHOT_BOUNDINGBOX, this );
  3765. int iclient = -1;
  3766. if ( ( trace.fraction < 1.0f ) && ( trace.c.entityNum < MAX_CLIENTS ) ) {
  3767. iclient = trace.c.entityNum;
  3768. }
  3769. if ( MPAim != iclient ) {
  3770. lastMPAim = MPAim;
  3771. MPAim = iclient;
  3772. lastMPAimTime = gameLocal.realClientTime;
  3773. }
  3774. }
  3775. idBounds bounds( start );
  3776. bounds.AddPoint( end );
  3777. listedClipModels = gameLocal.clip.ClipModelsTouchingBounds( bounds, -1, clipModelList, MAX_GENTITIES );
  3778. // no pretense at sorting here, just assume that there will only be one active
  3779. // gui within range along the trace
  3780. for ( i = 0; i < listedClipModels; i++ ) {
  3781. clip = clipModelList[ i ];
  3782. ent = clip->GetEntity();
  3783. if ( ent->IsHidden() ) {
  3784. continue;
  3785. }
  3786. if ( allowFocus ) {
  3787. if ( ent->IsType( idAFAttachment::Type ) ) {
  3788. idEntity *body = static_cast<idAFAttachment *>( ent )->GetBody();
  3789. if ( body && body->IsType( idAI::Type ) && ( static_cast<idAI *>( body )->GetTalkState() >= TALK_OK ) ) {
  3790. gameLocal.clip.TracePoint( trace, start, end, MASK_SHOT_RENDERMODEL, this );
  3791. if ( ( trace.fraction < 1.0f ) && ( trace.c.entityNum == ent->entityNumber ) ) {
  3792. ClearFocus();
  3793. focusCharacter = static_cast<idAI *>( body );
  3794. talkCursor = 1;
  3795. focusTime = gameLocal.time + FOCUS_TIME;
  3796. break;
  3797. }
  3798. }
  3799. continue;
  3800. }
  3801. if ( ent->IsType( idAI::Type ) ) {
  3802. if ( static_cast<idAI *>( ent )->GetTalkState() >= TALK_OK ) {
  3803. gameLocal.clip.TracePoint( trace, start, end, MASK_SHOT_RENDERMODEL, this );
  3804. if ( ( trace.fraction < 1.0f ) && ( trace.c.entityNum == ent->entityNumber ) ) {
  3805. ClearFocus();
  3806. focusCharacter = static_cast<idAI *>( ent );
  3807. talkCursor = 1;
  3808. focusTime = gameLocal.time + FOCUS_TIME;
  3809. break;
  3810. }
  3811. }
  3812. continue;
  3813. }
  3814. if ( ent->IsType( idAFEntity_Vehicle::Type ) ) {
  3815. gameLocal.clip.TracePoint( trace, start, end, MASK_SHOT_RENDERMODEL, this );
  3816. if ( ( trace.fraction < 1.0f ) && ( trace.c.entityNum == ent->entityNumber ) ) {
  3817. ClearFocus();
  3818. focusVehicle = static_cast<idAFEntity_Vehicle *>( ent );
  3819. focusTime = gameLocal.time + FOCUS_TIME;
  3820. break;
  3821. }
  3822. continue;
  3823. }
  3824. }
  3825. if ( !ent->GetRenderEntity() || !ent->GetRenderEntity()->gui[ 0 ] || !ent->GetRenderEntity()->gui[ 0 ]->IsInteractive() ) {
  3826. continue;
  3827. }
  3828. if ( ent->spawnArgs.GetBool( "inv_item" ) ) {
  3829. // don't allow guis on pickup items focus
  3830. continue;
  3831. }
  3832. pt = gameRenderWorld->GuiTrace( ent->GetModelDefHandle(), start, end );
  3833. if ( pt.x != -1 ) {
  3834. // we have a hit
  3835. renderEntity_t *focusGUIrenderEntity = ent->GetRenderEntity();
  3836. if ( !focusGUIrenderEntity ) {
  3837. continue;
  3838. }
  3839. if ( pt.guiId == 1 ) {
  3840. ui = focusGUIrenderEntity->gui[ 0 ];
  3841. } else if ( pt.guiId == 2 ) {
  3842. ui = focusGUIrenderEntity->gui[ 1 ];
  3843. } else {
  3844. ui = focusGUIrenderEntity->gui[ 2 ];
  3845. }
  3846. if ( ui == NULL ) {
  3847. continue;
  3848. }
  3849. ClearFocus();
  3850. focusGUIent = ent;
  3851. focusUI = ui;
  3852. if ( oldFocus != ent ) {
  3853. // new activation
  3854. // going to see if we have anything in inventory a gui might be interested in
  3855. // need to enumerate inventory items
  3856. focusUI->SetStateInt( "inv_count", inventory.items.Num() );
  3857. for ( j = 0; j < inventory.items.Num(); j++ ) {
  3858. idDict *item = inventory.items[ j ];
  3859. const char *iname = item->GetString( "inv_name" );
  3860. const char *iicon = item->GetString( "inv_icon" );
  3861. const char *itext = item->GetString( "inv_text" );
  3862. focusUI->SetStateString( va( "inv_name_%i", j), iname );
  3863. focusUI->SetStateString( va( "inv_icon_%i", j), iicon );
  3864. focusUI->SetStateString( va( "inv_text_%i", j), itext );
  3865. kv = item->MatchPrefix("inv_id", NULL);
  3866. if ( kv ) {
  3867. focusUI->SetStateString( va( "inv_id_%i", j ), kv->GetValue() );
  3868. }
  3869. focusUI->SetStateInt( iname, 1 );
  3870. }
  3871. for( j = 0; j < inventory.pdaSecurity.Num(); j++ ) {
  3872. const char *p = inventory.pdaSecurity[ j ];
  3873. if ( p && *p ) {
  3874. focusUI->SetStateInt( p, 1 );
  3875. }
  3876. }
  3877. int staminapercentage = ( int )( 100.0f * stamina / pm_stamina.GetFloat() );
  3878. focusUI->SetStateString( "player_health", va("%i", health ) );
  3879. focusUI->SetStateString( "player_stamina", va( "%i%%", staminapercentage ) );
  3880. focusUI->SetStateString( "player_armor", va( "%i%%", inventory.armor ) );
  3881. kv = focusGUIent->spawnArgs.MatchPrefix( "gui_parm", NULL );
  3882. while ( kv ) {
  3883. focusUI->SetStateString( kv->GetKey(), kv->GetValue() );
  3884. kv = focusGUIent->spawnArgs.MatchPrefix( "gui_parm", kv );
  3885. }
  3886. }
  3887. // clamp the mouse to the corner
  3888. ev = sys->GenerateMouseMoveEvent( -2000, -2000 );
  3889. command = focusUI->HandleEvent( &ev, gameLocal.time );
  3890. HandleGuiCommands( focusGUIent, command );
  3891. // move to an absolute position
  3892. ev = sys->GenerateMouseMoveEvent( pt.x * SCREEN_WIDTH, pt.y * SCREEN_HEIGHT );
  3893. command = focusUI->HandleEvent( &ev, gameLocal.time );
  3894. HandleGuiCommands( focusGUIent, command );
  3895. focusTime = gameLocal.time + FOCUS_GUI_TIME;
  3896. break;
  3897. }
  3898. }
  3899. if ( focusGUIent && focusUI ) {
  3900. if ( !oldFocus || oldFocus != focusGUIent ) {
  3901. command = focusUI->Activate( true, gameLocal.time );
  3902. HandleGuiCommands( focusGUIent, command );
  3903. StartSound( "snd_guienter", SND_CHANNEL_ANY, 0, false, NULL );
  3904. // HideTip();
  3905. // HideObjective();
  3906. }
  3907. } else if ( oldFocus && oldUI ) {
  3908. command = oldUI->Activate( false, gameLocal.time );
  3909. HandleGuiCommands( oldFocus, command );
  3910. StartSound( "snd_guiexit", SND_CHANNEL_ANY, 0, false, NULL );
  3911. }
  3912. if ( cursor && ( oldTalkCursor != talkCursor ) ) {
  3913. cursor->SetStateInt( "talkcursor", talkCursor );
  3914. }
  3915. if ( oldChar != focusCharacter && hud ) {
  3916. if ( focusCharacter ) {
  3917. hud->SetStateString( "npc", focusCharacter->spawnArgs.GetString( "npc_name", "Joe" ) );
  3918. hud->HandleNamedEvent( "showNPC" );
  3919. // HideTip();
  3920. // HideObjective();
  3921. } else {
  3922. hud->SetStateString( "npc", "" );
  3923. hud->HandleNamedEvent( "hideNPC" );
  3924. }
  3925. }
  3926. }
  3927. /*
  3928. =================
  3929. idPlayer::CrashLand
  3930. Check for hard landings that generate sound events
  3931. =================
  3932. */
  3933. void idPlayer::CrashLand( const idVec3 &oldOrigin, const idVec3 &oldVelocity ) {
  3934. idVec3 origin, velocity;
  3935. idVec3 gravityVector, gravityNormal;
  3936. float delta;
  3937. float hardDelta, fatalDelta;
  3938. float dist;
  3939. float vel, acc;
  3940. float t;
  3941. float a, b, c, den;
  3942. waterLevel_t waterLevel;
  3943. bool noDamage;
  3944. AI_SOFTLANDING = false;
  3945. AI_HARDLANDING = false;
  3946. // if the player is not on the ground
  3947. if ( !physicsObj.HasGroundContacts() ) {
  3948. return;
  3949. }
  3950. gravityNormal = physicsObj.GetGravityNormal();
  3951. // if the player wasn't going down
  3952. if ( ( oldVelocity * -gravityNormal ) >= 0.0f ) {
  3953. return;
  3954. }
  3955. waterLevel = physicsObj.GetWaterLevel();
  3956. // never take falling damage if completely underwater
  3957. if ( waterLevel == WATERLEVEL_HEAD ) {
  3958. return;
  3959. }
  3960. // no falling damage if touching a nodamage surface
  3961. noDamage = false;
  3962. for ( int i = 0; i < physicsObj.GetNumContacts(); i++ ) {
  3963. const contactInfo_t &contact = physicsObj.GetContact( i );
  3964. if ( contact.material->GetSurfaceFlags() & SURF_NODAMAGE ) {
  3965. noDamage = true;
  3966. StartSound( "snd_land_hard", SND_CHANNEL_ANY, 0, false, NULL );
  3967. break;
  3968. }
  3969. }
  3970. origin = GetPhysics()->GetOrigin();
  3971. gravityVector = physicsObj.GetGravity();
  3972. // calculate the exact velocity on landing
  3973. dist = ( origin - oldOrigin ) * -gravityNormal;
  3974. vel = oldVelocity * -gravityNormal;
  3975. acc = -gravityVector.Length();
  3976. a = acc / 2.0f;
  3977. b = vel;
  3978. c = -dist;
  3979. den = b * b - 4.0f * a * c;
  3980. if ( den < 0 ) {
  3981. return;
  3982. }
  3983. t = ( -b - idMath::Sqrt( den ) ) / ( 2.0f * a );
  3984. delta = vel + t * acc;
  3985. delta = delta * delta * 0.0001;
  3986. // reduce falling damage if there is standing water
  3987. if ( waterLevel == WATERLEVEL_WAIST ) {
  3988. delta *= 0.25f;
  3989. }
  3990. if ( waterLevel == WATERLEVEL_FEET ) {
  3991. delta *= 0.5f;
  3992. }
  3993. if ( delta < 1.0f ) {
  3994. return;
  3995. }
  3996. // allow falling a bit further for multiplayer
  3997. if ( gameLocal.isMultiplayer ) {
  3998. fatalDelta = 75.0f;
  3999. hardDelta = 50.0f;
  4000. } else {
  4001. fatalDelta = 65.0f;
  4002. hardDelta = 45.0f;
  4003. }
  4004. if ( delta > fatalDelta ) {
  4005. AI_HARDLANDING = true;
  4006. landChange = -32;
  4007. landTime = gameLocal.time;
  4008. if ( !noDamage ) {
  4009. pain_debounce_time = gameLocal.time + pain_delay + 1; // ignore pain since we'll play our landing anim
  4010. Damage( NULL, NULL, idVec3( 0, 0, -1 ), "damage_fatalfall", 1.0f, 0 );
  4011. }
  4012. } else if ( delta > hardDelta ) {
  4013. AI_HARDLANDING = true;
  4014. landChange = -24;
  4015. landTime = gameLocal.time;
  4016. if ( !noDamage ) {
  4017. pain_debounce_time = gameLocal.time + pain_delay + 1; // ignore pain since we'll play our landing anim
  4018. Damage( NULL, NULL, idVec3( 0, 0, -1 ), "damage_hardfall", 1.0f, 0 );
  4019. }
  4020. } else if ( delta > 30 ) {
  4021. AI_HARDLANDING = true;
  4022. landChange = -16;
  4023. landTime = gameLocal.time;
  4024. if ( !noDamage ) {
  4025. pain_debounce_time = gameLocal.time + pain_delay + 1; // ignore pain since we'll play our landing anim
  4026. Damage( NULL, NULL, idVec3( 0, 0, -1 ), "damage_softfall", 1.0f, 0 );
  4027. }
  4028. } else if ( delta > 7 ) {
  4029. AI_SOFTLANDING = true;
  4030. landChange = -8;
  4031. landTime = gameLocal.time;
  4032. } else if ( delta > 3 ) {
  4033. // just walk on
  4034. }
  4035. }
  4036. /*
  4037. ===============
  4038. idPlayer::BobCycle
  4039. ===============
  4040. */
  4041. void idPlayer::BobCycle( const idVec3 &pushVelocity ) {
  4042. float bobmove;
  4043. int old, deltaTime;
  4044. idVec3 vel, gravityDir, velocity;
  4045. idMat3 viewaxis;
  4046. float bob;
  4047. float delta;
  4048. float speed;
  4049. float f;
  4050. //
  4051. // calculate speed and cycle to be used for
  4052. // all cyclic walking effects
  4053. //
  4054. velocity = physicsObj.GetLinearVelocity() - pushVelocity;
  4055. gravityDir = physicsObj.GetGravityNormal();
  4056. vel = velocity - ( velocity * gravityDir ) * gravityDir;
  4057. xyspeed = vel.LengthFast();
  4058. // do not evaluate the bob for other clients
  4059. // when doing a spectate follow, don't do any weapon bobbing
  4060. if ( gameLocal.isClient && entityNumber != gameLocal.localClientNum ) {
  4061. viewBobAngles.Zero();
  4062. viewBob.Zero();
  4063. return;
  4064. }
  4065. if ( !physicsObj.HasGroundContacts() || influenceActive == INFLUENCE_LEVEL2 || ( gameLocal.isMultiplayer && spectating ) ) {
  4066. // airborne
  4067. bobCycle = 0;
  4068. bobFoot = 0;
  4069. bobfracsin = 0;
  4070. } else if ( ( !usercmd.forwardmove && !usercmd.rightmove ) || ( xyspeed <= MIN_BOB_SPEED ) ) {
  4071. // start at beginning of cycle again
  4072. bobCycle = 0;
  4073. bobFoot = 0;
  4074. bobfracsin = 0;
  4075. } else {
  4076. if ( physicsObj.IsCrouching() ) {
  4077. bobmove = pm_crouchbob.GetFloat();
  4078. // ducked characters never play footsteps
  4079. } else {
  4080. // vary the bobbing based on the speed of the player
  4081. bobmove = pm_walkbob.GetFloat() * ( 1.0f - bobFrac ) + pm_runbob.GetFloat() * bobFrac;
  4082. }
  4083. // check for footstep / splash sounds
  4084. old = bobCycle;
  4085. bobCycle = (int)( old + bobmove * gameLocal.msec ) & 255;
  4086. bobFoot = ( bobCycle & 128 ) >> 7;
  4087. bobfracsin = idMath::Fabs( sin( ( bobCycle & 127 ) / 127.0 * idMath::PI ) );
  4088. }
  4089. // calculate angles for view bobbing
  4090. viewBobAngles.Zero();
  4091. viewaxis = viewAngles.ToMat3() * physicsObj.GetGravityAxis();
  4092. // add angles based on velocity
  4093. delta = velocity * viewaxis[0];
  4094. viewBobAngles.pitch += delta * pm_runpitch.GetFloat();
  4095. delta = velocity * viewaxis[1];
  4096. viewBobAngles.roll -= delta * pm_runroll.GetFloat();
  4097. // add angles based on bob
  4098. // make sure the bob is visible even at low speeds
  4099. speed = xyspeed > 200 ? xyspeed : 200;
  4100. delta = bobfracsin * pm_bobpitch.GetFloat() * speed;
  4101. if ( physicsObj.IsCrouching() ) {
  4102. delta *= 3; // crouching
  4103. }
  4104. viewBobAngles.pitch += delta;
  4105. delta = bobfracsin * pm_bobroll.GetFloat() * speed;
  4106. if ( physicsObj.IsCrouching() ) {
  4107. delta *= 3; // crouching accentuates roll
  4108. }
  4109. if ( bobFoot & 1 ) {
  4110. delta = -delta;
  4111. }
  4112. viewBobAngles.roll += delta;
  4113. // calculate position for view bobbing
  4114. viewBob.Zero();
  4115. if ( physicsObj.HasSteppedUp() ) {
  4116. // check for stepping up before a previous step is completed
  4117. deltaTime = gameLocal.time - stepUpTime;
  4118. if ( deltaTime < STEPUP_TIME ) {
  4119. stepUpDelta = stepUpDelta * ( STEPUP_TIME - deltaTime ) / STEPUP_TIME + physicsObj.GetStepUp();
  4120. } else {
  4121. stepUpDelta = physicsObj.GetStepUp();
  4122. }
  4123. if ( stepUpDelta > 2.0f * pm_stepsize.GetFloat() ) {
  4124. stepUpDelta = 2.0f * pm_stepsize.GetFloat();
  4125. }
  4126. stepUpTime = gameLocal.time;
  4127. }
  4128. idVec3 gravity = physicsObj.GetGravityNormal();
  4129. // if the player stepped up recently
  4130. deltaTime = gameLocal.time - stepUpTime;
  4131. if ( deltaTime < STEPUP_TIME ) {
  4132. viewBob += gravity * ( stepUpDelta * ( STEPUP_TIME - deltaTime ) / STEPUP_TIME );
  4133. }
  4134. // add bob height after any movement smoothing
  4135. bob = bobfracsin * xyspeed * pm_bobup.GetFloat();
  4136. if ( bob > 6 ) {
  4137. bob = 6;
  4138. }
  4139. viewBob[2] += bob;
  4140. // add fall height
  4141. delta = gameLocal.time - landTime;
  4142. if ( delta < LAND_DEFLECT_TIME ) {
  4143. f = delta / LAND_DEFLECT_TIME;
  4144. viewBob -= gravity * ( landChange * f );
  4145. } else if ( delta < LAND_DEFLECT_TIME + LAND_RETURN_TIME ) {
  4146. delta -= LAND_DEFLECT_TIME;
  4147. f = 1.0 - ( delta / LAND_RETURN_TIME );
  4148. viewBob -= gravity * ( landChange * f );
  4149. }
  4150. }
  4151. /*
  4152. ================
  4153. idPlayer::UpdateDeltaViewAngles
  4154. ================
  4155. */
  4156. void idPlayer::UpdateDeltaViewAngles( const idAngles &angles ) {
  4157. // set the delta angle
  4158. idAngles delta;
  4159. for( int i = 0; i < 3; i++ ) {
  4160. delta[ i ] = angles[ i ] - SHORT2ANGLE( usercmd.angles[ i ] );
  4161. }
  4162. SetDeltaViewAngles( delta );
  4163. }
  4164. /*
  4165. ================
  4166. idPlayer::SetViewAngles
  4167. ================
  4168. */
  4169. void idPlayer::SetViewAngles( const idAngles &angles ) {
  4170. UpdateDeltaViewAngles( angles );
  4171. viewAngles = angles;
  4172. }
  4173. /*
  4174. ================
  4175. idPlayer::UpdateViewAngles
  4176. ================
  4177. */
  4178. void idPlayer::UpdateViewAngles( void ) {
  4179. int i;
  4180. idAngles delta;
  4181. if ( !noclip && ( gameLocal.inCinematic || privateCameraView || gameLocal.GetCamera() || influenceActive == INFLUENCE_LEVEL2 || objectiveSystemOpen ) ) {
  4182. // no view changes at all, but we still want to update the deltas or else when
  4183. // we get out of this mode, our view will snap to a kind of random angle
  4184. UpdateDeltaViewAngles( viewAngles );
  4185. return;
  4186. }
  4187. // if dead
  4188. if ( health <= 0 ) {
  4189. if ( pm_thirdPersonDeath.GetBool() ) {
  4190. viewAngles.roll = 0.0f;
  4191. viewAngles.pitch = 30.0f;
  4192. } else {
  4193. viewAngles.roll = 40.0f;
  4194. viewAngles.pitch = -15.0f;
  4195. }
  4196. return;
  4197. }
  4198. // circularly clamp the angles with deltas
  4199. for ( i = 0; i < 3; i++ ) {
  4200. cmdAngles[i] = SHORT2ANGLE( usercmd.angles[i] );
  4201. if ( influenceActive == INFLUENCE_LEVEL3 ) {
  4202. viewAngles[i] += idMath::ClampFloat( -1.0f, 1.0f, idMath::AngleDelta( idMath::AngleNormalize180( SHORT2ANGLE( usercmd.angles[i]) + deltaViewAngles[i] ) , viewAngles[i] ) );
  4203. } else {
  4204. viewAngles[i] = idMath::AngleNormalize180( SHORT2ANGLE( usercmd.angles[i]) + deltaViewAngles[i] );
  4205. }
  4206. }
  4207. if ( !centerView.IsDone( gameLocal.time ) ) {
  4208. viewAngles.pitch = centerView.GetCurrentValue(gameLocal.time);
  4209. }
  4210. // clamp the pitch
  4211. if ( noclip ) {
  4212. if ( viewAngles.pitch > 89.0f ) {
  4213. // don't let the player look down more than 89 degrees while noclipping
  4214. viewAngles.pitch = 89.0f;
  4215. } else if ( viewAngles.pitch < -89.0f ) {
  4216. // don't let the player look up more than 89 degrees while noclipping
  4217. viewAngles.pitch = -89.0f;
  4218. }
  4219. } else {
  4220. if ( viewAngles.pitch > pm_maxviewpitch.GetFloat() ) {
  4221. // don't let the player look down enough to see the shadow of his (non-existant) feet
  4222. viewAngles.pitch = pm_maxviewpitch.GetFloat();
  4223. } else if ( viewAngles.pitch < pm_minviewpitch.GetFloat() ) {
  4224. // don't let the player look up more than 89 degrees
  4225. viewAngles.pitch = pm_minviewpitch.GetFloat();
  4226. }
  4227. }
  4228. UpdateDeltaViewAngles( viewAngles );
  4229. // orient the model towards the direction we're looking
  4230. SetAngles( idAngles( 0, viewAngles.yaw, 0 ) );
  4231. // save in the log for analyzing weapon angle offsets
  4232. loggedViewAngles[ gameLocal.framenum & (NUM_LOGGED_VIEW_ANGLES-1) ] = viewAngles;
  4233. }
  4234. /*
  4235. ==============
  4236. idPlayer::AdjustHeartRate
  4237. Player heartrate works as follows
  4238. DEF_HEARTRATE is resting heartrate
  4239. Taking damage when health is above 75 adjusts heart rate by 1 beat per second
  4240. Taking damage when health is below 75 adjusts heart rate by 5 beats per second
  4241. Maximum heartrate from damage is MAX_HEARTRATE
  4242. Firing a weapon adds 1 beat per second up to a maximum of COMBAT_HEARTRATE
  4243. Being at less than 25% stamina adds 5 beats per second up to ZEROSTAMINA_HEARTRATE
  4244. All heartrates are target rates.. the heart rate will start falling as soon as there have been no adjustments for 5 seconds
  4245. Once it starts falling it always tries to get to DEF_HEARTRATE
  4246. The exception to the above rule is upon death at which point the rate is set to DYING_HEARTRATE and starts falling
  4247. immediately to zero
  4248. Heart rate volumes go from zero ( -40 db for DEF_HEARTRATE to 5 db for MAX_HEARTRATE ) the volume is
  4249. scaled linearly based on the actual rate
  4250. Exception to the above rule is once the player is dead, the dying heart rate starts at either the current volume if
  4251. it is audible or -10db and scales to 8db on the last few beats
  4252. ==============
  4253. */
  4254. void idPlayer::AdjustHeartRate( int target, float timeInSecs, float delay, bool force ) {
  4255. if ( heartInfo.GetEndValue() == target ) {
  4256. return;
  4257. }
  4258. if ( AI_DEAD && !force ) {
  4259. return;
  4260. }
  4261. lastHeartAdjust = gameLocal.time;
  4262. heartInfo.Init( gameLocal.time + delay * 1000, timeInSecs * 1000, heartRate, target );
  4263. }
  4264. /*
  4265. ==============
  4266. idPlayer::GetBaseHeartRate
  4267. ==============
  4268. */
  4269. int idPlayer::GetBaseHeartRate( void ) {
  4270. int base = idMath::FtoiFast( ( BASE_HEARTRATE + LOWHEALTH_HEARTRATE_ADJ ) - ( (float)health / 100.0f ) * LOWHEALTH_HEARTRATE_ADJ );
  4271. int rate = idMath::FtoiFast( base + ( ZEROSTAMINA_HEARTRATE - base ) * ( 1.0f - stamina / pm_stamina.GetFloat() ) );
  4272. int diff = ( lastDmgTime ) ? gameLocal.time - lastDmgTime : 99999;
  4273. rate += ( diff < 5000 ) ? ( diff < 2500 ) ? ( diff < 1000 ) ? 15 : 10 : 5 : 0;
  4274. return rate;
  4275. }
  4276. /*
  4277. ==============
  4278. idPlayer::SetCurrentHeartRate
  4279. ==============
  4280. */
  4281. void idPlayer::SetCurrentHeartRate( void ) {
  4282. int base = idMath::FtoiFast( ( BASE_HEARTRATE + LOWHEALTH_HEARTRATE_ADJ ) - ( (float) health / 100.0f ) * LOWHEALTH_HEARTRATE_ADJ );
  4283. if ( PowerUpActive( ADRENALINE )) {
  4284. heartRate = 135;
  4285. } else {
  4286. heartRate = idMath::FtoiFast( heartInfo.GetCurrentValue( gameLocal.time ) );
  4287. int currentRate = GetBaseHeartRate();
  4288. if ( health >= 0 && gameLocal.time > lastHeartAdjust + 2500 ) {
  4289. AdjustHeartRate( currentRate, 2.5f, 0.0f, false );
  4290. }
  4291. }
  4292. int bps = idMath::FtoiFast( 60.0f / heartRate * 1000.0f );
  4293. if ( gameLocal.time - lastHeartBeat > bps ) {
  4294. int dmgVol = DMG_VOLUME;
  4295. int deathVol = DEATH_VOLUME;
  4296. int zeroVol = ZERO_VOLUME;
  4297. float pct = 0.0;
  4298. if ( heartRate > BASE_HEARTRATE && health > 0 ) {
  4299. pct = (float)(heartRate - base) / (MAX_HEARTRATE - base);
  4300. pct *= ((float)dmgVol - (float)zeroVol);
  4301. } else if ( health <= 0 ) {
  4302. pct = (float)(heartRate - DYING_HEARTRATE) / (BASE_HEARTRATE - DYING_HEARTRATE);
  4303. if ( pct > 1.0f ) {
  4304. pct = 1.0f;
  4305. } else if (pct < 0.0f) {
  4306. pct = 0.0f;
  4307. }
  4308. pct *= ((float)deathVol - (float)zeroVol);
  4309. }
  4310. pct += (float)zeroVol;
  4311. if ( pct != zeroVol ) {
  4312. StartSound( "snd_heartbeat", SND_CHANNEL_HEART, SSF_PRIVATE_SOUND, false, NULL );
  4313. // modify just this channel to a custom volume
  4314. soundShaderParms_t parms;
  4315. memset( &parms, 0, sizeof( parms ) );
  4316. parms.volume = pct;
  4317. refSound.referenceSound->ModifySound( SND_CHANNEL_HEART, &parms );
  4318. }
  4319. lastHeartBeat = gameLocal.time;
  4320. }
  4321. }
  4322. /*
  4323. ==============
  4324. idPlayer::UpdateAir
  4325. ==============
  4326. */
  4327. void idPlayer::UpdateAir( void ) {
  4328. if ( health <= 0 ) {
  4329. return;
  4330. }
  4331. // see if the player is connected to the info_vacuum
  4332. bool newAirless = false;
  4333. if ( gameLocal.vacuumAreaNum != -1 ) {
  4334. int num = GetNumPVSAreas();
  4335. if ( num > 0 ) {
  4336. int areaNum;
  4337. // if the player box spans multiple areas, get the area from the origin point instead,
  4338. // otherwise a rotating player box may poke into an outside area
  4339. if ( num == 1 ) {
  4340. const int *pvsAreas = GetPVSAreas();
  4341. areaNum = pvsAreas[0];
  4342. } else {
  4343. areaNum = gameRenderWorld->PointInArea( this->GetPhysics()->GetOrigin() );
  4344. }
  4345. newAirless = gameRenderWorld->AreasAreConnected( gameLocal.vacuumAreaNum, areaNum, PS_BLOCK_AIR );
  4346. }
  4347. }
  4348. if ( newAirless ) {
  4349. if ( !airless ) {
  4350. StartSound( "snd_decompress", SND_CHANNEL_ANY, SSF_GLOBAL, false, NULL );
  4351. StartSound( "snd_noAir", SND_CHANNEL_BODY2, 0, false, NULL );
  4352. if ( hud ) {
  4353. hud->HandleNamedEvent( "noAir" );
  4354. }
  4355. }
  4356. airTics--;
  4357. if ( airTics < 0 ) {
  4358. airTics = 0;
  4359. // check for damage
  4360. const idDict *damageDef = gameLocal.FindEntityDefDict( "damage_noair", false );
  4361. int dmgTiming = 1000 * ((damageDef) ? damageDef->GetFloat( "delay", "3.0" ) : 3.0f );
  4362. if ( gameLocal.time > lastAirDamage + dmgTiming ) {
  4363. Damage( NULL, NULL, vec3_origin, "damage_noair", 1.0f, 0 );
  4364. lastAirDamage = gameLocal.time;
  4365. }
  4366. }
  4367. } else {
  4368. if ( airless ) {
  4369. StartSound( "snd_recompress", SND_CHANNEL_ANY, SSF_GLOBAL, false, NULL );
  4370. StopSound( SND_CHANNEL_BODY2, false );
  4371. if ( hud ) {
  4372. hud->HandleNamedEvent( "Air" );
  4373. }
  4374. }
  4375. airTics+=2; // regain twice as fast as lose
  4376. if ( airTics > pm_airTics.GetInteger() ) {
  4377. airTics = pm_airTics.GetInteger();
  4378. }
  4379. }
  4380. airless = newAirless;
  4381. if ( hud ) {
  4382. hud->SetStateInt( "player_air", 100 * airTics / pm_airTics.GetInteger() );
  4383. }
  4384. }
  4385. /*
  4386. ==============
  4387. idPlayer::AddGuiPDAData
  4388. ==============
  4389. */
  4390. int idPlayer::AddGuiPDAData( const declType_t dataType, const char *listName, const idDeclPDA *src, idUserInterface *gui ) {
  4391. int c, i;
  4392. idStr work;
  4393. if ( dataType == DECL_EMAIL ) {
  4394. c = src->GetNumEmails();
  4395. for ( i = 0; i < c; i++ ) {
  4396. const idDeclEmail *email = src->GetEmailByIndex( i );
  4397. if ( email == NULL ) {
  4398. work = va( "-\tEmail %d not found\t-", i );
  4399. } else {
  4400. work = email->GetFrom();
  4401. work += "\t";
  4402. work += email->GetSubject();
  4403. work += "\t";
  4404. work += email->GetDate();
  4405. }
  4406. gui->SetStateString( va( "%s_item_%i", listName, i ), work );
  4407. }
  4408. return c;
  4409. } else if ( dataType == DECL_AUDIO ) {
  4410. c = src->GetNumAudios();
  4411. for ( i = 0; i < c; i++ ) {
  4412. const idDeclAudio *audio = src->GetAudioByIndex( i );
  4413. if ( audio == NULL ) {
  4414. work = va( "Audio Log %d not found", i );
  4415. } else {
  4416. work = audio->GetAudioName();
  4417. }
  4418. gui->SetStateString( va( "%s_item_%i", listName, i ), work );
  4419. }
  4420. return c;
  4421. } else if ( dataType == DECL_VIDEO ) {
  4422. c = inventory.videos.Num();
  4423. for ( i = 0; i < c; i++ ) {
  4424. const idDeclVideo *video = GetVideo( i );
  4425. if ( video == NULL ) {
  4426. work = va( "Video CD %s not found", inventory.videos[i].c_str() );
  4427. } else {
  4428. work = video->GetVideoName();
  4429. }
  4430. gui->SetStateString( va( "%s_item_%i", listName, i ), work );
  4431. }
  4432. return c;
  4433. }
  4434. return 0;
  4435. }
  4436. /*
  4437. ==============
  4438. idPlayer::GetPDA
  4439. ==============
  4440. */
  4441. const idDeclPDA *idPlayer::GetPDA( void ) const {
  4442. if ( inventory.pdas.Num() ) {
  4443. return static_cast< const idDeclPDA* >( declManager->FindType( DECL_PDA, inventory.pdas[ 0 ] ) );
  4444. } else {
  4445. return NULL;
  4446. }
  4447. }
  4448. /*
  4449. ==============
  4450. idPlayer::GetVideo
  4451. ==============
  4452. */
  4453. const idDeclVideo *idPlayer::GetVideo( int index ) {
  4454. if ( index >= 0 && index < inventory.videos.Num() ) {
  4455. return static_cast< const idDeclVideo* >( declManager->FindType( DECL_VIDEO, inventory.videos[index], false ) );
  4456. }
  4457. return NULL;
  4458. }
  4459. /*
  4460. ==============
  4461. idPlayer::UpdatePDAInfo
  4462. ==============
  4463. */
  4464. void idPlayer::UpdatePDAInfo( bool updatePDASel ) {
  4465. int j, sel;
  4466. if ( objectiveSystem == NULL ) {
  4467. return;
  4468. }
  4469. assert( hud );
  4470. int currentPDA = objectiveSystem->State().GetInt( "listPDA_sel_0", "0" );
  4471. if ( currentPDA == -1 ) {
  4472. currentPDA = 0;
  4473. }
  4474. if ( updatePDASel ) {
  4475. objectiveSystem->SetStateInt( "listPDAVideo_sel_0", 0 );
  4476. objectiveSystem->SetStateInt( "listPDAEmail_sel_0", 0 );
  4477. objectiveSystem->SetStateInt( "listPDAAudio_sel_0", 0 );
  4478. }
  4479. if ( currentPDA > 0 ) {
  4480. currentPDA = inventory.pdas.Num() - currentPDA;
  4481. }
  4482. // Mark in the bit array that this pda has been read
  4483. if ( currentPDA < 128 ) {
  4484. inventory.pdasViewed[currentPDA >> 5] |= 1 << (currentPDA & 31);
  4485. }
  4486. pdaAudio = "";
  4487. pdaVideo = "";
  4488. pdaVideoWave = "";
  4489. idStr name, data, preview, info, wave;
  4490. for ( j = 0; j < MAX_PDAS; j++ ) {
  4491. objectiveSystem->SetStateString( va( "listPDA_item_%i", j ), "" );
  4492. }
  4493. for ( j = 0; j < MAX_PDA_ITEMS; j++ ) {
  4494. objectiveSystem->SetStateString( va( "listPDAVideo_item_%i", j ), "" );
  4495. objectiveSystem->SetStateString( va( "listPDAAudio_item_%i", j ), "" );
  4496. objectiveSystem->SetStateString( va( "listPDAEmail_item_%i", j ), "" );
  4497. objectiveSystem->SetStateString( va( "listPDASecurity_item_%i", j ), "" );
  4498. }
  4499. for ( j = 0; j < inventory.pdas.Num(); j++ ) {
  4500. const idDeclPDA *pda = static_cast< const idDeclPDA* >( declManager->FindType( DECL_PDA, inventory.pdas[j], false ) );
  4501. if ( pda == NULL ) {
  4502. continue;
  4503. }
  4504. int index = inventory.pdas.Num() - j;
  4505. if ( j == 0 ) {
  4506. // Special case for the first PDA
  4507. index = 0;
  4508. }
  4509. if ( j != currentPDA && j < 128 && inventory.pdasViewed[j >> 5] & (1 << (j & 31)) ) {
  4510. // This pda has been read already, mark in gray
  4511. objectiveSystem->SetStateString( va( "listPDA_item_%i", index), va(S_COLOR_GRAY "%s", pda->GetPdaName()) );
  4512. } else {
  4513. // This pda has not been read yet
  4514. objectiveSystem->SetStateString( va( "listPDA_item_%i", index), pda->GetPdaName() );
  4515. }
  4516. const char *security = pda->GetSecurity();
  4517. if ( j == currentPDA || (currentPDA == 0 && security && *security ) ) {
  4518. if ( *security == NULL ) {
  4519. security = common->GetLanguageDict()->GetString( "#str_00066" );
  4520. }
  4521. objectiveSystem->SetStateString( "PDASecurityClearance", security );
  4522. }
  4523. if ( j == currentPDA ) {
  4524. objectiveSystem->SetStateString( "pda_icon", pda->GetIcon() );
  4525. objectiveSystem->SetStateString( "pda_id", pda->GetID() );
  4526. objectiveSystem->SetStateString( "pda_title", pda->GetTitle() );
  4527. if ( j == 0 ) {
  4528. // Selected, personal pda
  4529. // Add videos
  4530. if ( updatePDASel || !inventory.pdaOpened ) {
  4531. objectiveSystem->HandleNamedEvent( "playerPDAActive" );
  4532. objectiveSystem->SetStateString( "pda_personal", "1" );
  4533. inventory.pdaOpened = true;
  4534. }
  4535. objectiveSystem->SetStateString( "pda_location", hud->State().GetString("location") );
  4536. objectiveSystem->SetStateString( "pda_name", cvarSystem->GetCVarString( "ui_name") );
  4537. AddGuiPDAData( DECL_VIDEO, "listPDAVideo", pda, objectiveSystem );
  4538. sel = objectiveSystem->State().GetInt( "listPDAVideo_sel_0", "0" );
  4539. const idDeclVideo *vid = NULL;
  4540. if ( sel >= 0 && sel < inventory.videos.Num() ) {
  4541. vid = static_cast< const idDeclVideo * >( declManager->FindType( DECL_VIDEO, inventory.videos[ sel ], false ) );
  4542. }
  4543. if ( vid ) {
  4544. pdaVideo = vid->GetRoq();
  4545. pdaVideoWave = vid->GetWave();
  4546. objectiveSystem->SetStateString( "PDAVideoTitle", vid->GetVideoName() );
  4547. objectiveSystem->SetStateString( "PDAVideoVid", vid->GetRoq() );
  4548. objectiveSystem->SetStateString( "PDAVideoIcon", vid->GetPreview() );
  4549. objectiveSystem->SetStateString( "PDAVideoInfo", vid->GetInfo() );
  4550. } else {
  4551. //FIXME: need to precache these in the player def
  4552. objectiveSystem->SetStateString( "PDAVideoVid", "sound/vo/video/welcome.tga" );
  4553. objectiveSystem->SetStateString( "PDAVideoIcon", "sound/vo/video/welcome.tga" );
  4554. objectiveSystem->SetStateString( "PDAVideoTitle", "" );
  4555. objectiveSystem->SetStateString( "PDAVideoInfo", "" );
  4556. }
  4557. } else {
  4558. // Selected, non-personal pda
  4559. // Add audio logs
  4560. if ( updatePDASel ) {
  4561. objectiveSystem->HandleNamedEvent( "playerPDANotActive" );
  4562. objectiveSystem->SetStateString( "pda_personal", "0" );
  4563. inventory.pdaOpened = true;
  4564. }
  4565. objectiveSystem->SetStateString( "pda_location", pda->GetPost() );
  4566. objectiveSystem->SetStateString( "pda_name", pda->GetFullName() );
  4567. int audioCount = AddGuiPDAData( DECL_AUDIO, "listPDAAudio", pda, objectiveSystem );
  4568. objectiveSystem->SetStateInt( "audioLogCount", audioCount );
  4569. sel = objectiveSystem->State().GetInt( "listPDAAudio_sel_0", "0" );
  4570. const idDeclAudio *aud = NULL;
  4571. if ( sel >= 0 ) {
  4572. aud = pda->GetAudioByIndex( sel );
  4573. }
  4574. if ( aud ) {
  4575. pdaAudio = aud->GetWave();
  4576. objectiveSystem->SetStateString( "PDAAudioTitle", aud->GetAudioName() );
  4577. objectiveSystem->SetStateString( "PDAAudioIcon", aud->GetPreview() );
  4578. objectiveSystem->SetStateString( "PDAAudioInfo", aud->GetInfo() );
  4579. } else {
  4580. objectiveSystem->SetStateString( "PDAAudioIcon", "sound/vo/video/welcome.tga" );
  4581. objectiveSystem->SetStateString( "PDAAutioTitle", "" );
  4582. objectiveSystem->SetStateString( "PDAAudioInfo", "" );
  4583. }
  4584. }
  4585. // add emails
  4586. name = "";
  4587. data = "";
  4588. int numEmails = pda->GetNumEmails();
  4589. if ( numEmails > 0 ) {
  4590. AddGuiPDAData( DECL_EMAIL, "listPDAEmail", pda, objectiveSystem );
  4591. sel = objectiveSystem->State().GetInt( "listPDAEmail_sel_0", "-1" );
  4592. if ( sel >= 0 && sel < numEmails ) {
  4593. const idDeclEmail *email = pda->GetEmailByIndex( sel );
  4594. name = email->GetSubject();
  4595. data = email->GetBody();
  4596. }
  4597. }
  4598. objectiveSystem->SetStateString( "PDAEmailTitle", name );
  4599. objectiveSystem->SetStateString( "PDAEmailText", data );
  4600. }
  4601. }
  4602. if ( objectiveSystem->State().GetInt( "listPDA_sel_0", "-1" ) == -1 ) {
  4603. objectiveSystem->SetStateInt( "listPDA_sel_0", 0 );
  4604. }
  4605. objectiveSystem->StateChanged( gameLocal.time );
  4606. }
  4607. /*
  4608. ==============
  4609. idPlayer::TogglePDA
  4610. ==============
  4611. */
  4612. void idPlayer::TogglePDA( void ) {
  4613. if ( objectiveSystem == NULL ) {
  4614. return;
  4615. }
  4616. if ( inventory.pdas.Num() == 0 ) {
  4617. ShowTip( spawnArgs.GetString( "text_infoTitle" ), spawnArgs.GetString( "text_noPDA" ), true );
  4618. return;
  4619. }
  4620. assert( hud );
  4621. if ( !objectiveSystemOpen ) {
  4622. int j, c = inventory.items.Num();
  4623. objectiveSystem->SetStateInt( "inv_count", c );
  4624. for ( j = 0; j < MAX_INVENTORY_ITEMS; j++ ) {
  4625. objectiveSystem->SetStateString( va( "inv_name_%i", j ), "" );
  4626. objectiveSystem->SetStateString( va( "inv_icon_%i", j ), "" );
  4627. objectiveSystem->SetStateString( va( "inv_text_%i", j ), "" );
  4628. }
  4629. for ( j = 0; j < c; j++ ) {
  4630. idDict *item = inventory.items[j];
  4631. if ( !item->GetBool( "inv_pda" ) ) {
  4632. const char *iname = item->GetString( "inv_name" );
  4633. const char *iicon = item->GetString( "inv_icon" );
  4634. const char *itext = item->GetString( "inv_text" );
  4635. objectiveSystem->SetStateString( va( "inv_name_%i", j ), iname );
  4636. objectiveSystem->SetStateString( va( "inv_icon_%i", j ), iicon );
  4637. objectiveSystem->SetStateString( va( "inv_text_%i", j ), itext );
  4638. const idKeyValue *kv = item->MatchPrefix( "inv_id", NULL );
  4639. if ( kv ) {
  4640. objectiveSystem->SetStateString( va( "inv_id_%i", j ), kv->GetValue() );
  4641. }
  4642. }
  4643. }
  4644. for ( j = 0; j < MAX_WEAPONS; j++ ) {
  4645. const char *weapnum = va( "def_weapon%d", j );
  4646. const char *hudWeap = va( "weapon%d", j );
  4647. int weapstate = 0;
  4648. if ( inventory.weapons & ( 1 << j ) ) {
  4649. const char *weap = spawnArgs.GetString( weapnum );
  4650. if ( weap && *weap ) {
  4651. weapstate++;
  4652. }
  4653. }
  4654. objectiveSystem->SetStateInt( hudWeap, weapstate );
  4655. }
  4656. objectiveSystem->SetStateInt( "listPDA_sel_0", inventory.selPDA );
  4657. objectiveSystem->SetStateInt( "listPDAVideo_sel_0", inventory.selVideo );
  4658. objectiveSystem->SetStateInt( "listPDAAudio_sel_0", inventory.selAudio );
  4659. objectiveSystem->SetStateInt( "listPDAEmail_sel_0", inventory.selEMail );
  4660. UpdatePDAInfo( false );
  4661. UpdateObjectiveInfo();
  4662. objectiveSystem->Activate( true, gameLocal.time );
  4663. hud->HandleNamedEvent( "pdaPickupHide" );
  4664. hud->HandleNamedEvent( "videoPickupHide" );
  4665. } else {
  4666. inventory.selPDA = objectiveSystem->State().GetInt( "listPDA_sel_0" );
  4667. inventory.selVideo = objectiveSystem->State().GetInt( "listPDAVideo_sel_0" );
  4668. inventory.selAudio = objectiveSystem->State().GetInt( "listPDAAudio_sel_0" );
  4669. inventory.selEMail = objectiveSystem->State().GetInt( "listPDAEmail_sel_0" );
  4670. objectiveSystem->Activate( false, gameLocal.time );
  4671. }
  4672. objectiveSystemOpen ^= 1;
  4673. }
  4674. /*
  4675. ==============
  4676. idPlayer::ToggleScoreboard
  4677. ==============
  4678. */
  4679. void idPlayer::ToggleScoreboard( void ) {
  4680. scoreBoardOpen ^= 1;
  4681. }
  4682. /*
  4683. ==============
  4684. idPlayer::Spectate
  4685. ==============
  4686. */
  4687. void idPlayer::Spectate( bool spectate ) {
  4688. idBitMsg msg;
  4689. byte msgBuf[MAX_EVENT_PARAM_SIZE];
  4690. // track invisible player bug
  4691. // all hiding and showing should be performed through Spectate calls
  4692. // except for the private camera view, which is used for teleports
  4693. assert( ( teleportEntity.GetEntity() != NULL ) || ( IsHidden() == spectating ) );
  4694. if ( spectating == spectate ) {
  4695. return;
  4696. }
  4697. spectating = spectate;
  4698. if ( gameLocal.isServer ) {
  4699. msg.Init( msgBuf, sizeof( msgBuf ) );
  4700. msg.WriteBits( spectating, 1 );
  4701. ServerSendEvent( EVENT_SPECTATE, &msg, false, -1 );
  4702. }
  4703. if ( spectating ) {
  4704. // join the spectators
  4705. ClearPowerUps();
  4706. spectator = this->entityNumber;
  4707. Init();
  4708. StopRagdoll();
  4709. SetPhysics( &physicsObj );
  4710. physicsObj.DisableClip();
  4711. Hide();
  4712. Event_DisableWeapon();
  4713. if ( hud ) {
  4714. hud->HandleNamedEvent( "aim_clear" );
  4715. MPAimFadeTime = 0;
  4716. }
  4717. } else {
  4718. // put everything back together again
  4719. currentWeapon = -1; // to make sure the def will be loaded if necessary
  4720. Show();
  4721. Event_EnableWeapon();
  4722. }
  4723. SetClipModel();
  4724. }
  4725. /*
  4726. ==============
  4727. idPlayer::SetClipModel
  4728. ==============
  4729. */
  4730. void idPlayer::SetClipModel( void ) {
  4731. idBounds bounds;
  4732. if ( spectating ) {
  4733. bounds = idBounds( vec3_origin ).Expand( pm_spectatebbox.GetFloat() * 0.5f );
  4734. } else {
  4735. bounds[0].Set( -pm_bboxwidth.GetFloat() * 0.5f, -pm_bboxwidth.GetFloat() * 0.5f, 0 );
  4736. bounds[1].Set( pm_bboxwidth.GetFloat() * 0.5f, pm_bboxwidth.GetFloat() * 0.5f, pm_normalheight.GetFloat() );
  4737. }
  4738. // the origin of the clip model needs to be set before calling SetClipModel
  4739. // otherwise our physics object's current origin value gets reset to 0
  4740. idClipModel *newClip;
  4741. if ( pm_usecylinder.GetBool() ) {
  4742. newClip = new idClipModel( idTraceModel( bounds, 8 ) );
  4743. newClip->Translate( physicsObj.PlayerGetOrigin() );
  4744. physicsObj.SetClipModel( newClip, 1.0f );
  4745. } else {
  4746. newClip = new idClipModel( idTraceModel( bounds ) );
  4747. newClip->Translate( physicsObj.PlayerGetOrigin() );
  4748. physicsObj.SetClipModel( newClip, 1.0f );
  4749. }
  4750. }
  4751. /*
  4752. ==============
  4753. idPlayer::UseVehicle
  4754. ==============
  4755. */
  4756. void idPlayer::UseVehicle( void ) {
  4757. trace_t trace;
  4758. idVec3 start, end;
  4759. idEntity *ent;
  4760. if ( GetBindMaster() && GetBindMaster()->IsType( idAFEntity_Vehicle::Type ) ) {
  4761. Show();
  4762. static_cast<idAFEntity_Vehicle*>(GetBindMaster())->Use( this );
  4763. } else {
  4764. start = GetEyePosition();
  4765. end = start + viewAngles.ToForward() * 80.0f;
  4766. gameLocal.clip.TracePoint( trace, start, end, MASK_SHOT_RENDERMODEL, this );
  4767. if ( trace.fraction < 1.0f ) {
  4768. ent = gameLocal.entities[ trace.c.entityNum ];
  4769. if ( ent && ent->IsType( idAFEntity_Vehicle::Type ) ) {
  4770. Hide();
  4771. static_cast<idAFEntity_Vehicle*>(ent)->Use( this );
  4772. }
  4773. }
  4774. }
  4775. }
  4776. /*
  4777. ==============
  4778. idPlayer::PerformImpulse
  4779. ==============
  4780. */
  4781. void idPlayer::PerformImpulse( int impulse ) {
  4782. if ( gameLocal.isClient ) {
  4783. idBitMsg msg;
  4784. byte msgBuf[MAX_EVENT_PARAM_SIZE];
  4785. assert( entityNumber == gameLocal.localClientNum );
  4786. msg.Init( msgBuf, sizeof( msgBuf ) );
  4787. msg.BeginWriting();
  4788. msg.WriteBits( impulse, 6 );
  4789. ClientSendEvent( EVENT_IMPULSE, &msg );
  4790. }
  4791. if ( impulse >= IMPULSE_0 && impulse <= IMPULSE_12 ) {
  4792. SelectWeapon( impulse, false );
  4793. return;
  4794. }
  4795. switch( impulse ) {
  4796. case IMPULSE_13: {
  4797. Reload();
  4798. break;
  4799. }
  4800. case IMPULSE_14: {
  4801. NextWeapon();
  4802. break;
  4803. }
  4804. case IMPULSE_15: {
  4805. PrevWeapon();
  4806. break;
  4807. }
  4808. case IMPULSE_17: {
  4809. if ( gameLocal.isClient || entityNumber == gameLocal.localClientNum ) {
  4810. gameLocal.mpGame.ToggleReady();
  4811. }
  4812. break;
  4813. }
  4814. case IMPULSE_18: {
  4815. centerView.Init(gameLocal.time, 200, viewAngles.pitch, 0);
  4816. break;
  4817. }
  4818. case IMPULSE_19: {
  4819. // when we're not in single player, IMPULSE_19 is used for showScores
  4820. // otherwise it opens the pda
  4821. if ( !gameLocal.isMultiplayer ) {
  4822. if ( objectiveSystemOpen ) {
  4823. TogglePDA();
  4824. } else if ( weapon_pda >= 0 ) {
  4825. SelectWeapon( weapon_pda, true );
  4826. }
  4827. }
  4828. break;
  4829. }
  4830. case IMPULSE_20: {
  4831. if ( gameLocal.isClient || entityNumber == gameLocal.localClientNum ) {
  4832. gameLocal.mpGame.ToggleTeam();
  4833. }
  4834. break;
  4835. }
  4836. case IMPULSE_22: {
  4837. if ( gameLocal.isClient || entityNumber == gameLocal.localClientNum ) {
  4838. gameLocal.mpGame.ToggleSpectate();
  4839. }
  4840. break;
  4841. }
  4842. case IMPULSE_28: {
  4843. if ( gameLocal.isClient || entityNumber == gameLocal.localClientNum ) {
  4844. gameLocal.mpGame.CastVote( gameLocal.localClientNum, true );
  4845. }
  4846. break;
  4847. }
  4848. case IMPULSE_29: {
  4849. if ( gameLocal.isClient || entityNumber == gameLocal.localClientNum ) {
  4850. gameLocal.mpGame.CastVote( gameLocal.localClientNum, false );
  4851. }
  4852. break;
  4853. }
  4854. case IMPULSE_40: {
  4855. UseVehicle();
  4856. break;
  4857. }
  4858. }
  4859. }
  4860. bool idPlayer::HandleESC( void ) {
  4861. if ( gameLocal.inCinematic ) {
  4862. return SkipCinematic();
  4863. }
  4864. if ( objectiveSystemOpen ) {
  4865. TogglePDA();
  4866. return true;
  4867. }
  4868. return false;
  4869. }
  4870. bool idPlayer::SkipCinematic( void ) {
  4871. StartSound( "snd_skipcinematic", SND_CHANNEL_ANY, 0, false, NULL );
  4872. return gameLocal.SkipCinematic();
  4873. }
  4874. /*
  4875. ==============
  4876. idPlayer::EvaluateControls
  4877. ==============
  4878. */
  4879. void idPlayer::EvaluateControls( void ) {
  4880. // check for respawning
  4881. if ( health <= 0 ) {
  4882. if ( ( gameLocal.time > minRespawnTime ) && ( usercmd.buttons & BUTTON_ATTACK ) ) {
  4883. forceRespawn = true;
  4884. } else if ( gameLocal.time > maxRespawnTime ) {
  4885. forceRespawn = true;
  4886. }
  4887. }
  4888. // in MP, idMultiplayerGame decides spawns
  4889. if ( forceRespawn && !gameLocal.isMultiplayer && !g_testDeath.GetBool() ) {
  4890. // in single player, we let the session handle restarting the level or loading a game
  4891. gameLocal.sessionCommand = "died";
  4892. }
  4893. if ( ( usercmd.flags & UCF_IMPULSE_SEQUENCE ) != ( oldFlags & UCF_IMPULSE_SEQUENCE ) ) {
  4894. PerformImpulse( usercmd.impulse );
  4895. }
  4896. scoreBoardOpen = ( ( usercmd.buttons & BUTTON_SCORES ) != 0 || forceScoreBoard );
  4897. oldFlags = usercmd.flags;
  4898. AdjustSpeed();
  4899. // update the viewangles
  4900. UpdateViewAngles();
  4901. }
  4902. /*
  4903. ==============
  4904. idPlayer::AdjustSpeed
  4905. ==============
  4906. */
  4907. void idPlayer::AdjustSpeed( void ) {
  4908. float speed;
  4909. float rate;
  4910. if ( spectating ) {
  4911. speed = pm_spectatespeed.GetFloat();
  4912. bobFrac = 0.0f;
  4913. } else if ( noclip ) {
  4914. speed = pm_noclipspeed.GetFloat();
  4915. bobFrac = 0.0f;
  4916. } else if ( !physicsObj.OnLadder() && ( usercmd.buttons & BUTTON_RUN ) && ( usercmd.forwardmove || usercmd.rightmove ) && ( usercmd.upmove >= 0 ) ) {
  4917. if ( !gameLocal.isMultiplayer && !physicsObj.IsCrouching() && !PowerUpActive( ADRENALINE ) ) {
  4918. stamina -= MS2SEC( gameLocal.msec );
  4919. }
  4920. if ( stamina < 0 ) {
  4921. stamina = 0;
  4922. }
  4923. if ( ( !pm_stamina.GetFloat() ) || ( stamina > pm_staminathreshold.GetFloat() ) ) {
  4924. bobFrac = 1.0f;
  4925. } else if ( pm_staminathreshold.GetFloat() <= 0.0001f ) {
  4926. bobFrac = 0.0f;
  4927. } else {
  4928. bobFrac = stamina / pm_staminathreshold.GetFloat();
  4929. }
  4930. speed = pm_walkspeed.GetFloat() * ( 1.0f - bobFrac ) + pm_runspeed.GetFloat() * bobFrac;
  4931. } else {
  4932. rate = pm_staminarate.GetFloat();
  4933. // increase 25% faster when not moving
  4934. if ( ( usercmd.forwardmove == 0 ) && ( usercmd.rightmove == 0 ) && ( !physicsObj.OnLadder() || ( usercmd.upmove == 0 ) ) ) {
  4935. rate *= 1.25f;
  4936. }
  4937. stamina += rate * MS2SEC( gameLocal.msec );
  4938. if ( stamina > pm_stamina.GetFloat() ) {
  4939. stamina = pm_stamina.GetFloat();
  4940. }
  4941. speed = pm_walkspeed.GetFloat();
  4942. bobFrac = 0.0f;
  4943. }
  4944. speed *= PowerUpModifier(SPEED);
  4945. if ( influenceActive == INFLUENCE_LEVEL3 ) {
  4946. speed *= 0.33f;
  4947. }
  4948. physicsObj.SetSpeed( speed, pm_crouchspeed.GetFloat() );
  4949. }
  4950. /*
  4951. ==============
  4952. idPlayer::AdjustBodyAngles
  4953. ==============
  4954. */
  4955. void idPlayer::AdjustBodyAngles( void ) {
  4956. idMat3 lookAxis;
  4957. idMat3 legsAxis;
  4958. bool blend;
  4959. float diff;
  4960. float frac;
  4961. float upBlend;
  4962. float forwardBlend;
  4963. float downBlend;
  4964. if ( health < 0 ) {
  4965. return;
  4966. }
  4967. blend = true;
  4968. if ( !physicsObj.HasGroundContacts() ) {
  4969. idealLegsYaw = 0.0f;
  4970. legsForward = true;
  4971. } else if ( usercmd.forwardmove < 0 ) {
  4972. idealLegsYaw = idMath::AngleNormalize180( idVec3( -usercmd.forwardmove, usercmd.rightmove, 0.0f ).ToYaw() );
  4973. legsForward = false;
  4974. } else if ( usercmd.forwardmove > 0 ) {
  4975. idealLegsYaw = idMath::AngleNormalize180( idVec3( usercmd.forwardmove, -usercmd.rightmove, 0.0f ).ToYaw() );
  4976. legsForward = true;
  4977. } else if ( ( usercmd.rightmove != 0 ) && physicsObj.IsCrouching() ) {
  4978. if ( !legsForward ) {
  4979. idealLegsYaw = idMath::AngleNormalize180( idVec3( idMath::Abs( usercmd.rightmove ), usercmd.rightmove, 0.0f ).ToYaw() );
  4980. } else {
  4981. idealLegsYaw = idMath::AngleNormalize180( idVec3( idMath::Abs( usercmd.rightmove ), -usercmd.rightmove, 0.0f ).ToYaw() );
  4982. }
  4983. } else if ( usercmd.rightmove != 0 ) {
  4984. idealLegsYaw = 0.0f;
  4985. legsForward = true;
  4986. } else {
  4987. legsForward = true;
  4988. diff = idMath::Fabs( idealLegsYaw - legsYaw );
  4989. idealLegsYaw = idealLegsYaw - idMath::AngleNormalize180( viewAngles.yaw - oldViewYaw );
  4990. if ( diff < 0.1f ) {
  4991. legsYaw = idealLegsYaw;
  4992. blend = false;
  4993. }
  4994. }
  4995. if ( !physicsObj.IsCrouching() ) {
  4996. legsForward = true;
  4997. }
  4998. oldViewYaw = viewAngles.yaw;
  4999. AI_TURN_LEFT = false;
  5000. AI_TURN_RIGHT = false;
  5001. if ( idealLegsYaw < -45.0f ) {
  5002. idealLegsYaw = 0;
  5003. AI_TURN_RIGHT = true;
  5004. blend = true;
  5005. } else if ( idealLegsYaw > 45.0f ) {
  5006. idealLegsYaw = 0;
  5007. AI_TURN_LEFT = true;
  5008. blend = true;
  5009. }
  5010. if ( blend ) {
  5011. legsYaw = legsYaw * 0.9f + idealLegsYaw * 0.1f;
  5012. }
  5013. legsAxis = idAngles( 0.0f, legsYaw, 0.0f ).ToMat3();
  5014. animator.SetJointAxis( hipJoint, JOINTMOD_WORLD, legsAxis );
  5015. // calculate the blending between down, straight, and up
  5016. frac = viewAngles.pitch / 90.0f;
  5017. if ( frac > 0.0f ) {
  5018. downBlend = frac;
  5019. forwardBlend = 1.0f - frac;
  5020. upBlend = 0.0f;
  5021. } else {
  5022. downBlend = 0.0f;
  5023. forwardBlend = 1.0f + frac;
  5024. upBlend = -frac;
  5025. }
  5026. animator.CurrentAnim( ANIMCHANNEL_TORSO )->SetSyncedAnimWeight( 0, downBlend );
  5027. animator.CurrentAnim( ANIMCHANNEL_TORSO )->SetSyncedAnimWeight( 1, forwardBlend );
  5028. animator.CurrentAnim( ANIMCHANNEL_TORSO )->SetSyncedAnimWeight( 2, upBlend );
  5029. animator.CurrentAnim( ANIMCHANNEL_LEGS )->SetSyncedAnimWeight( 0, downBlend );
  5030. animator.CurrentAnim( ANIMCHANNEL_LEGS )->SetSyncedAnimWeight( 1, forwardBlend );
  5031. animator.CurrentAnim( ANIMCHANNEL_LEGS )->SetSyncedAnimWeight( 2, upBlend );
  5032. }
  5033. /*
  5034. ==============
  5035. idPlayer::InitAASLocation
  5036. ==============
  5037. */
  5038. void idPlayer::InitAASLocation( void ) {
  5039. int i;
  5040. int num;
  5041. idVec3 size;
  5042. idBounds bounds;
  5043. idAAS *aas;
  5044. idVec3 origin;
  5045. GetFloorPos( 64.0f, origin );
  5046. num = gameLocal.NumAAS();
  5047. aasLocation.SetGranularity( 1 );
  5048. aasLocation.SetNum( num );
  5049. for( i = 0; i < aasLocation.Num(); i++ ) {
  5050. aasLocation[ i ].areaNum = 0;
  5051. aasLocation[ i ].pos = origin;
  5052. aas = gameLocal.GetAAS( i );
  5053. if ( aas && aas->GetSettings() ) {
  5054. size = aas->GetSettings()->boundingBoxes[0][1];
  5055. bounds[0] = -size;
  5056. size.z = 32.0f;
  5057. bounds[1] = size;
  5058. aasLocation[ i ].areaNum = aas->PointReachableAreaNum( origin, bounds, AREA_REACHABLE_WALK );
  5059. }
  5060. }
  5061. }
  5062. /*
  5063. ==============
  5064. idPlayer::SetAASLocation
  5065. ==============
  5066. */
  5067. void idPlayer::SetAASLocation( void ) {
  5068. int i;
  5069. int areaNum;
  5070. idVec3 size;
  5071. idBounds bounds;
  5072. idAAS *aas;
  5073. idVec3 origin;
  5074. if ( !GetFloorPos( 64.0f, origin ) ) {
  5075. return;
  5076. }
  5077. for( i = 0; i < aasLocation.Num(); i++ ) {
  5078. aas = gameLocal.GetAAS( i );
  5079. if ( !aas ) {
  5080. continue;
  5081. }
  5082. size = aas->GetSettings()->boundingBoxes[0][1];
  5083. bounds[0] = -size;
  5084. size.z = 32.0f;
  5085. bounds[1] = size;
  5086. areaNum = aas->PointReachableAreaNum( origin, bounds, AREA_REACHABLE_WALK );
  5087. if ( areaNum ) {
  5088. aasLocation[ i ].pos = origin;
  5089. aasLocation[ i ].areaNum = areaNum;
  5090. }
  5091. }
  5092. }
  5093. /*
  5094. ==============
  5095. idPlayer::GetAASLocation
  5096. ==============
  5097. */
  5098. void idPlayer::GetAASLocation( idAAS *aas, idVec3 &pos, int &areaNum ) const {
  5099. int i;
  5100. if ( aas != NULL ) {
  5101. for( i = 0; i < aasLocation.Num(); i++ ) {
  5102. if ( aas == gameLocal.GetAAS( i ) ) {
  5103. areaNum = aasLocation[ i ].areaNum;
  5104. pos = aasLocation[ i ].pos;
  5105. return;
  5106. }
  5107. }
  5108. }
  5109. areaNum = 0;
  5110. pos = physicsObj.GetOrigin();
  5111. }
  5112. /*
  5113. ==============
  5114. idPlayer::Move
  5115. ==============
  5116. */
  5117. void idPlayer::Move( void ) {
  5118. float newEyeOffset;
  5119. idVec3 oldOrigin;
  5120. idVec3 oldVelocity;
  5121. idVec3 pushVelocity;
  5122. // save old origin and velocity for crashlanding
  5123. oldOrigin = physicsObj.GetOrigin();
  5124. oldVelocity = physicsObj.GetLinearVelocity();
  5125. pushVelocity = physicsObj.GetPushedLinearVelocity();
  5126. // set physics variables
  5127. physicsObj.SetMaxStepHeight( pm_stepsize.GetFloat() );
  5128. physicsObj.SetMaxJumpHeight( pm_jumpheight.GetFloat() );
  5129. if ( noclip ) {
  5130. physicsObj.SetContents( 0 );
  5131. physicsObj.SetMovementType( PM_NOCLIP );
  5132. } else if ( spectating ) {
  5133. physicsObj.SetContents( 0 );
  5134. physicsObj.SetMovementType( PM_SPECTATOR );
  5135. } else if ( health <= 0 ) {
  5136. physicsObj.SetContents( CONTENTS_CORPSE | CONTENTS_MONSTERCLIP );
  5137. physicsObj.SetMovementType( PM_DEAD );
  5138. } else if ( gameLocal.inCinematic || gameLocal.GetCamera() || privateCameraView || ( influenceActive == INFLUENCE_LEVEL2 ) ) {
  5139. physicsObj.SetContents( CONTENTS_BODY );
  5140. physicsObj.SetMovementType( PM_FREEZE );
  5141. } else {
  5142. physicsObj.SetContents( CONTENTS_BODY );
  5143. physicsObj.SetMovementType( PM_NORMAL );
  5144. }
  5145. if ( spectating ) {
  5146. physicsObj.SetClipMask( MASK_DEADSOLID );
  5147. } else if ( health <= 0 ) {
  5148. physicsObj.SetClipMask( MASK_DEADSOLID );
  5149. } else {
  5150. physicsObj.SetClipMask( MASK_PLAYERSOLID );
  5151. }
  5152. physicsObj.SetDebugLevel( g_debugMove.GetBool() );
  5153. physicsObj.SetPlayerInput( usercmd, viewAngles );
  5154. // FIXME: physics gets disabled somehow
  5155. BecomeActive( TH_PHYSICS );
  5156. RunPhysics();
  5157. // update our last valid AAS location for the AI
  5158. SetAASLocation();
  5159. if ( spectating ) {
  5160. newEyeOffset = 0.0f;
  5161. } else if ( health <= 0 ) {
  5162. newEyeOffset = pm_deadviewheight.GetFloat();
  5163. } else if ( physicsObj.IsCrouching() ) {
  5164. newEyeOffset = pm_crouchviewheight.GetFloat();
  5165. } else if ( GetBindMaster() && GetBindMaster()->IsType( idAFEntity_Vehicle::Type ) ) {
  5166. newEyeOffset = 0.0f;
  5167. } else {
  5168. newEyeOffset = pm_normalviewheight.GetFloat();
  5169. }
  5170. if ( EyeHeight() != newEyeOffset ) {
  5171. if ( spectating ) {
  5172. SetEyeHeight( newEyeOffset );
  5173. } else {
  5174. // smooth out duck height changes
  5175. SetEyeHeight( EyeHeight() * pm_crouchrate.GetFloat() + newEyeOffset * ( 1.0f - pm_crouchrate.GetFloat() ) );
  5176. }
  5177. }
  5178. if ( noclip || gameLocal.inCinematic || ( influenceActive == INFLUENCE_LEVEL2 ) ) {
  5179. AI_CROUCH = false;
  5180. AI_ONGROUND = ( influenceActive == INFLUENCE_LEVEL2 );
  5181. AI_ONLADDER = false;
  5182. AI_JUMP = false;
  5183. } else {
  5184. AI_CROUCH = physicsObj.IsCrouching();
  5185. AI_ONGROUND = physicsObj.HasGroundContacts();
  5186. AI_ONLADDER = physicsObj.OnLadder();
  5187. AI_JUMP = physicsObj.HasJumped();
  5188. // check if we're standing on top of a monster and give a push if we are
  5189. idEntity *groundEnt = physicsObj.GetGroundEntity();
  5190. if ( groundEnt && groundEnt->IsType( idAI::Type ) ) {
  5191. idVec3 vel = physicsObj.GetLinearVelocity();
  5192. if ( vel.ToVec2().LengthSqr() < 0.1f ) {
  5193. vel.ToVec2() = physicsObj.GetOrigin().ToVec2() - groundEnt->GetPhysics()->GetAbsBounds().GetCenter().ToVec2();
  5194. vel.ToVec2().NormalizeFast();
  5195. vel.ToVec2() *= pm_walkspeed.GetFloat();
  5196. } else {
  5197. // give em a push in the direction they're going
  5198. vel *= 1.1f;
  5199. }
  5200. physicsObj.SetLinearVelocity( vel );
  5201. }
  5202. }
  5203. if ( AI_JUMP ) {
  5204. // bounce the view weapon
  5205. loggedAccel_t *acc = &loggedAccel[currentLoggedAccel&(NUM_LOGGED_ACCELS-1)];
  5206. currentLoggedAccel++;
  5207. acc->time = gameLocal.time;
  5208. acc->dir[2] = 200;
  5209. acc->dir[0] = acc->dir[1] = 0;
  5210. }
  5211. if ( AI_ONLADDER ) {
  5212. int old_rung = oldOrigin.z / LADDER_RUNG_DISTANCE;
  5213. int new_rung = physicsObj.GetOrigin().z / LADDER_RUNG_DISTANCE;
  5214. if ( old_rung != new_rung ) {
  5215. StartSound( "snd_stepladder", SND_CHANNEL_ANY, 0, false, NULL );
  5216. }
  5217. }
  5218. BobCycle( pushVelocity );
  5219. CrashLand( oldOrigin, oldVelocity );
  5220. }
  5221. /*
  5222. ==============
  5223. idPlayer::UpdateHud
  5224. ==============
  5225. */
  5226. void idPlayer::UpdateHud( void ) {
  5227. idPlayer *aimed;
  5228. if ( !hud ) {
  5229. return;
  5230. }
  5231. if ( entityNumber != gameLocal.localClientNum ) {
  5232. return;
  5233. }
  5234. int c = inventory.pickupItemNames.Num();
  5235. if ( c > 0 ) {
  5236. if ( gameLocal.time > inventory.nextItemPickup ) {
  5237. if ( inventory.nextItemPickup && gameLocal.time - inventory.nextItemPickup > 2000 ) {
  5238. inventory.nextItemNum = 1;
  5239. }
  5240. int i;
  5241. for ( i = 0; i < 5, i < c; i++ ) {
  5242. hud->SetStateString( va( "itemtext%i", inventory.nextItemNum ), inventory.pickupItemNames[0].name );
  5243. hud->SetStateString( va( "itemicon%i", inventory.nextItemNum ), inventory.pickupItemNames[0].icon );
  5244. hud->HandleNamedEvent( va( "itemPickup%i", inventory.nextItemNum++ ) );
  5245. inventory.pickupItemNames.RemoveIndex( 0 );
  5246. if (inventory.nextItemNum == 1 ) {
  5247. inventory.onePickupTime = gameLocal.time;
  5248. } else if ( inventory.nextItemNum > 5 ) {
  5249. inventory.nextItemNum = 1;
  5250. inventory.nextItemPickup = inventory.onePickupTime + 2000;
  5251. } else {
  5252. inventory.nextItemPickup = gameLocal.time + 400;
  5253. }
  5254. }
  5255. }
  5256. }
  5257. if ( gameLocal.realClientTime == lastMPAimTime ) {
  5258. if ( MPAim != -1 && gameLocal.gameType == GAME_TDM
  5259. && gameLocal.entities[ MPAim ] && gameLocal.entities[ MPAim ]->IsType( idPlayer::Type )
  5260. && static_cast< idPlayer * >( gameLocal.entities[ MPAim ] )->team == team ) {
  5261. aimed = static_cast< idPlayer * >( gameLocal.entities[ MPAim ] );
  5262. hud->SetStateString( "aim_text", gameLocal.userInfo[ MPAim ].GetString( "ui_name" ) );
  5263. hud->SetStateFloat( "aim_color", aimed->colorBarIndex );
  5264. hud->HandleNamedEvent( "aim_flash" );
  5265. MPAimHighlight = true;
  5266. MPAimFadeTime = 0; // no fade till loosing focus
  5267. } else if ( MPAimHighlight ) {
  5268. hud->HandleNamedEvent( "aim_fade" );
  5269. MPAimFadeTime = gameLocal.realClientTime;
  5270. MPAimHighlight = false;
  5271. }
  5272. }
  5273. if ( MPAimFadeTime ) {
  5274. assert( !MPAimHighlight );
  5275. if ( gameLocal.realClientTime - MPAimFadeTime > 2000 ) {
  5276. MPAimFadeTime = 0;
  5277. }
  5278. }
  5279. hud->SetStateInt( "g_showProjectilePct", g_showProjectilePct.GetInteger() );
  5280. if ( numProjectilesFired ) {
  5281. hud->SetStateString( "projectilepct", va( "Hit %% %.1f", ( (float) numProjectileHits / numProjectilesFired ) * 100 ) );
  5282. } else {
  5283. hud->SetStateString( "projectilepct", "Hit % 0.0" );
  5284. }
  5285. if ( isLagged && gameLocal.isMultiplayer && gameLocal.localClientNum == entityNumber ) {
  5286. hud->SetStateString( "hudLag", "1" );
  5287. } else {
  5288. hud->SetStateString( "hudLag", "0" );
  5289. }
  5290. }
  5291. /*
  5292. ==============
  5293. idPlayer::UpdateDeathSkin
  5294. ==============
  5295. */
  5296. void idPlayer::UpdateDeathSkin( bool state_hitch ) {
  5297. if ( !( gameLocal.isMultiplayer || g_testDeath.GetBool() ) ) {
  5298. return;
  5299. }
  5300. if ( health <= 0 ) {
  5301. if ( !doingDeathSkin ) {
  5302. deathClearContentsTime = spawnArgs.GetInt( "deathSkinTime" );
  5303. doingDeathSkin = true;
  5304. renderEntity.noShadow = true;
  5305. if ( state_hitch ) {
  5306. renderEntity.shaderParms[ SHADERPARM_TIME_OF_DEATH ] = gameLocal.time * 0.001f - 2.0f;
  5307. } else {
  5308. renderEntity.shaderParms[ SHADERPARM_TIME_OF_DEATH ] = gameLocal.time * 0.001f;
  5309. }
  5310. UpdateVisuals();
  5311. }
  5312. // wait a bit before switching off the content
  5313. if ( deathClearContentsTime && gameLocal.time > deathClearContentsTime ) {
  5314. SetCombatContents( false );
  5315. deathClearContentsTime = 0;
  5316. }
  5317. } else {
  5318. renderEntity.noShadow = false;
  5319. renderEntity.shaderParms[ SHADERPARM_TIME_OF_DEATH ] = 0.0f;
  5320. UpdateVisuals();
  5321. doingDeathSkin = false;
  5322. }
  5323. }
  5324. /*
  5325. ==============
  5326. idPlayer::StartFxOnBone
  5327. ==============
  5328. */
  5329. void idPlayer::StartFxOnBone( const char *fx, const char *bone ) {
  5330. idVec3 offset;
  5331. idMat3 axis;
  5332. jointHandle_t jointHandle = GetAnimator()->GetJointHandle( bone );
  5333. if ( jointHandle == INVALID_JOINT ) {
  5334. gameLocal.Printf( "Cannot find bone %s\n", bone );
  5335. return;
  5336. }
  5337. if ( GetAnimator()->GetJointTransform( jointHandle, gameLocal.time, offset, axis ) ) {
  5338. offset = GetPhysics()->GetOrigin() + offset * GetPhysics()->GetAxis();
  5339. axis = axis * GetPhysics()->GetAxis();
  5340. }
  5341. idEntityFx::StartFx( fx, &offset, &axis, this, true );
  5342. }
  5343. /*
  5344. ==============
  5345. idPlayer::Think
  5346. Called every tic for each player
  5347. ==============
  5348. */
  5349. void idPlayer::Think( void ) {
  5350. renderEntity_t *headRenderEnt;
  5351. UpdatePlayerIcons();
  5352. // latch button actions
  5353. oldButtons = usercmd.buttons;
  5354. // grab out usercmd
  5355. usercmd_t oldCmd = usercmd;
  5356. usercmd = gameLocal.usercmds[ entityNumber ];
  5357. buttonMask &= usercmd.buttons;
  5358. usercmd.buttons &= ~buttonMask;
  5359. if ( gameLocal.inCinematic && gameLocal.skipCinematic ) {
  5360. return;
  5361. }
  5362. // clear the ik before we do anything else so the skeleton doesn't get updated twice
  5363. walkIK.ClearJointMods();
  5364. // if this is the very first frame of the map, set the delta view angles
  5365. // based on the usercmd angles
  5366. if ( !spawnAnglesSet && ( gameLocal.GameState() != GAMESTATE_STARTUP ) ) {
  5367. spawnAnglesSet = true;
  5368. SetViewAngles( spawnAngles );
  5369. oldFlags = usercmd.flags;
  5370. }
  5371. if ( objectiveSystemOpen || gameLocal.inCinematic || influenceActive ) {
  5372. if ( objectiveSystemOpen && AI_PAIN ) {
  5373. TogglePDA();
  5374. }
  5375. usercmd.forwardmove = 0;
  5376. usercmd.rightmove = 0;
  5377. usercmd.upmove = 0;
  5378. }
  5379. // log movement changes for weapon bobbing effects
  5380. if ( usercmd.forwardmove != oldCmd.forwardmove ) {
  5381. loggedAccel_t *acc = &loggedAccel[currentLoggedAccel&(NUM_LOGGED_ACCELS-1)];
  5382. currentLoggedAccel++;
  5383. acc->time = gameLocal.time;
  5384. acc->dir[0] = usercmd.forwardmove - oldCmd.forwardmove;
  5385. acc->dir[1] = acc->dir[2] = 0;
  5386. }
  5387. if ( usercmd.rightmove != oldCmd.rightmove ) {
  5388. loggedAccel_t *acc = &loggedAccel[currentLoggedAccel&(NUM_LOGGED_ACCELS-1)];
  5389. currentLoggedAccel++;
  5390. acc->time = gameLocal.time;
  5391. acc->dir[1] = usercmd.rightmove - oldCmd.rightmove;
  5392. acc->dir[0] = acc->dir[2] = 0;
  5393. }
  5394. // freelook centering
  5395. if ( ( usercmd.buttons ^ oldCmd.buttons ) & BUTTON_MLOOK ) {
  5396. centerView.Init( gameLocal.time, 200, viewAngles.pitch, 0 );
  5397. }
  5398. // zooming
  5399. if ( ( usercmd.buttons ^ oldCmd.buttons ) & BUTTON_ZOOM ) {
  5400. if ( ( usercmd.buttons & BUTTON_ZOOM ) && weapon.GetEntity() ) {
  5401. zoomFov.Init( gameLocal.time, 200.0f, CalcFov( false ), weapon.GetEntity()->GetZoomFov() );
  5402. } else {
  5403. zoomFov.Init( gameLocal.time, 200.0f, zoomFov.GetCurrentValue( gameLocal.time ), DefaultFov() );
  5404. }
  5405. }
  5406. // if we have an active gui, we will unrotate the view angles as
  5407. // we turn the mouse movements into gui events
  5408. idUserInterface *gui = ActiveGui();
  5409. if ( gui && gui != focusUI ) {
  5410. RouteGuiMouse( gui );
  5411. }
  5412. // set the push velocity on the weapon before running the physics
  5413. if ( weapon.GetEntity() ) {
  5414. weapon.GetEntity()->SetPushVelocity( physicsObj.GetPushedLinearVelocity() );
  5415. }
  5416. EvaluateControls();
  5417. if ( !af.IsActive() ) {
  5418. AdjustBodyAngles();
  5419. CopyJointsFromBodyToHead();
  5420. }
  5421. Move();
  5422. if ( !g_stopTime.GetBool() ) {
  5423. if ( !noclip && !spectating && ( health > 0 ) && !IsHidden() ) {
  5424. TouchTriggers();
  5425. }
  5426. // not done on clients for various reasons. don't do it on server and save the sound channel for other things
  5427. if ( !gameLocal.isMultiplayer ) {
  5428. SetCurrentHeartRate();
  5429. float scale = g_damageScale.GetFloat();
  5430. if ( g_useDynamicProtection.GetBool() && scale < 1.0f && gameLocal.time - lastDmgTime > 500 ) {
  5431. if ( scale < 1.0f ) {
  5432. scale += 0.05f;
  5433. }
  5434. if ( scale > 1.0f ) {
  5435. scale = 1.0f;
  5436. }
  5437. g_damageScale.SetFloat( scale );
  5438. }
  5439. }
  5440. // update GUIs, Items, and character interactions
  5441. UpdateFocus();
  5442. UpdateLocation();
  5443. // update player script
  5444. UpdateScript();
  5445. // service animations
  5446. if ( !spectating && !af.IsActive() && !gameLocal.inCinematic ) {
  5447. UpdateConditions();
  5448. UpdateAnimState();
  5449. CheckBlink();
  5450. }
  5451. // clear out our pain flag so we can tell if we recieve any damage between now and the next time we think
  5452. AI_PAIN = false;
  5453. }
  5454. // calculate the exact bobbed view position, which is used to
  5455. // position the view weapon, among other things
  5456. CalculateFirstPersonView();
  5457. // this may use firstPersonView, or a thirdPeroson / camera view
  5458. CalculateRenderView();
  5459. inventory.UpdateArmor();
  5460. if ( spectating ) {
  5461. UpdateSpectating();
  5462. } else if ( health > 0 ) {
  5463. UpdateWeapon();
  5464. }
  5465. UpdateAir();
  5466. UpdateHud();
  5467. UpdatePowerUps();
  5468. UpdateDeathSkin( false );
  5469. if ( gameLocal.isMultiplayer ) {
  5470. DrawPlayerIcons();
  5471. }
  5472. if ( head.GetEntity() ) {
  5473. headRenderEnt = head.GetEntity()->GetRenderEntity();
  5474. } else {
  5475. headRenderEnt = NULL;
  5476. }
  5477. if ( headRenderEnt ) {
  5478. if ( influenceSkin ) {
  5479. headRenderEnt->customSkin = influenceSkin;
  5480. } else {
  5481. headRenderEnt->customSkin = NULL;
  5482. }
  5483. }
  5484. if ( gameLocal.isMultiplayer || g_showPlayerShadow.GetBool() ) {
  5485. renderEntity.suppressShadowInViewID = 0;
  5486. if ( headRenderEnt ) {
  5487. headRenderEnt->suppressShadowInViewID = 0;
  5488. }
  5489. } else {
  5490. renderEntity.suppressShadowInViewID = entityNumber+1;
  5491. if ( headRenderEnt ) {
  5492. headRenderEnt->suppressShadowInViewID = entityNumber+1;
  5493. }
  5494. }
  5495. // never cast shadows from our first-person muzzle flashes
  5496. renderEntity.suppressShadowInLightID = LIGHTID_VIEW_MUZZLE_FLASH + entityNumber;
  5497. if ( headRenderEnt ) {
  5498. headRenderEnt->suppressShadowInLightID = LIGHTID_VIEW_MUZZLE_FLASH + entityNumber;
  5499. }
  5500. if ( !g_stopTime.GetBool() ) {
  5501. UpdateAnimation();
  5502. Present();
  5503. UpdateDamageEffects();
  5504. LinkCombat();
  5505. playerView.CalculateShake();
  5506. }
  5507. if ( !( thinkFlags & TH_THINK ) ) {
  5508. gameLocal.Printf( "player %d not thinking?\n", entityNumber );
  5509. }
  5510. if ( g_showEnemies.GetBool() ) {
  5511. idActor *ent;
  5512. int num = 0;
  5513. for( ent = enemyList.Next(); ent != NULL; ent = ent->enemyNode.Next() ) {
  5514. gameLocal.Printf( "enemy (%d)'%s'\n", ent->entityNumber, ent->name.c_str() );
  5515. gameRenderWorld->DebugBounds( colorRed, ent->GetPhysics()->GetBounds().Expand( 2 ), ent->GetPhysics()->GetOrigin() );
  5516. num++;
  5517. }
  5518. gameLocal.Printf( "%d: enemies\n", num );
  5519. }
  5520. }
  5521. /*
  5522. =================
  5523. idPlayer::RouteGuiMouse
  5524. =================
  5525. */
  5526. void idPlayer::RouteGuiMouse( idUserInterface *gui ) {
  5527. sysEvent_t ev;
  5528. const char *command;
  5529. if ( usercmd.mx != oldMouseX || usercmd.my != oldMouseY ) {
  5530. ev = sys->GenerateMouseMoveEvent( usercmd.mx - oldMouseX, usercmd.my - oldMouseY );
  5531. command = gui->HandleEvent( &ev, gameLocal.time );
  5532. oldMouseX = usercmd.mx;
  5533. oldMouseY = usercmd.my;
  5534. }
  5535. }
  5536. /*
  5537. ==================
  5538. idPlayer::LookAtKiller
  5539. ==================
  5540. */
  5541. void idPlayer::LookAtKiller( idEntity *inflictor, idEntity *attacker ) {
  5542. idVec3 dir;
  5543. if ( attacker && attacker != this ) {
  5544. dir = attacker->GetPhysics()->GetOrigin() - GetPhysics()->GetOrigin();
  5545. } else if ( inflictor && inflictor != this ) {
  5546. dir = inflictor->GetPhysics()->GetOrigin() - GetPhysics()->GetOrigin();
  5547. } else {
  5548. dir = viewAxis[ 0 ];
  5549. }
  5550. idAngles ang( 0, dir.ToYaw(), 0 );
  5551. SetViewAngles( ang );
  5552. }
  5553. /*
  5554. ==============
  5555. idPlayer::Kill
  5556. ==============
  5557. */
  5558. void idPlayer::Kill( bool delayRespawn, bool nodamage ) {
  5559. if ( spectating ) {
  5560. SpectateFreeFly( false );
  5561. } else if ( health > 0 ) {
  5562. godmode = false;
  5563. if ( nodamage ) {
  5564. ServerSpectate( true );
  5565. forceRespawn = true;
  5566. } else {
  5567. Damage( this, this, vec3_origin, "damage_suicide", 1.0f, INVALID_JOINT );
  5568. if ( delayRespawn ) {
  5569. forceRespawn = false;
  5570. int delay = spawnArgs.GetFloat( "respawn_delay" );
  5571. minRespawnTime = gameLocal.time + SEC2MS( delay );
  5572. maxRespawnTime = minRespawnTime + MAX_RESPAWN_TIME;
  5573. }
  5574. }
  5575. }
  5576. }
  5577. /*
  5578. ==================
  5579. idPlayer::Killed
  5580. ==================
  5581. */
  5582. void idPlayer::Killed( idEntity *inflictor, idEntity *attacker, int damage, const idVec3 &dir, int location ) {
  5583. float delay;
  5584. assert( !gameLocal.isClient );
  5585. // stop taking knockback once dead
  5586. fl.noknockback = true;
  5587. if ( health < -999 ) {
  5588. health = -999;
  5589. }
  5590. if ( AI_DEAD ) {
  5591. AI_PAIN = true;
  5592. return;
  5593. }
  5594. heartInfo.Init( 0, 0, 0, BASE_HEARTRATE );
  5595. AdjustHeartRate( DEAD_HEARTRATE, 10.0f, 0.0f, true );
  5596. if ( !g_testDeath.GetBool() ) {
  5597. playerView.Fade( colorBlack, 12000 );
  5598. }
  5599. AI_DEAD = true;
  5600. SetAnimState( ANIMCHANNEL_LEGS, "Legs_Death", 4 );
  5601. SetAnimState( ANIMCHANNEL_TORSO, "Torso_Death", 4 );
  5602. SetWaitState( "" );
  5603. animator.ClearAllJoints();
  5604. if ( StartRagdoll() ) {
  5605. pm_modelView.SetInteger( 0 );
  5606. minRespawnTime = gameLocal.time + RAGDOLL_DEATH_TIME;
  5607. maxRespawnTime = minRespawnTime + MAX_RESPAWN_TIME;
  5608. } else {
  5609. // don't allow respawn until the death anim is done
  5610. // g_forcerespawn may force spawning at some later time
  5611. delay = spawnArgs.GetFloat( "respawn_delay" );
  5612. minRespawnTime = gameLocal.time + SEC2MS( delay );
  5613. maxRespawnTime = minRespawnTime + MAX_RESPAWN_TIME;
  5614. }
  5615. physicsObj.SetMovementType( PM_DEAD );
  5616. StartSound( "snd_death", SND_CHANNEL_VOICE, 0, false, NULL );
  5617. StopSound( SND_CHANNEL_BODY2, false );
  5618. fl.takedamage = true; // can still be gibbed
  5619. // get rid of weapon
  5620. weapon.GetEntity()->OwnerDied();
  5621. // drop the weapon as an item
  5622. DropWeapon( true );
  5623. if ( !g_testDeath.GetBool() ) {
  5624. LookAtKiller( inflictor, attacker );
  5625. }
  5626. if ( gameLocal.isMultiplayer || g_testDeath.GetBool() ) {
  5627. idPlayer *killer = NULL;
  5628. // no gibbing in MP. Event_Gib will early out in MP
  5629. if ( attacker->IsType( idPlayer::Type ) ) {
  5630. killer = static_cast<idPlayer*>(attacker);
  5631. if ( health < -20 || killer->PowerUpActive( BERSERK ) ) {
  5632. gibDeath = true;
  5633. gibsDir = dir;
  5634. gibsLaunched = false;
  5635. }
  5636. }
  5637. gameLocal.mpGame.PlayerDeath( this, killer, isTelefragged );
  5638. } else {
  5639. physicsObj.SetContents( CONTENTS_CORPSE | CONTENTS_MONSTERCLIP );
  5640. }
  5641. ClearPowerUps();
  5642. UpdateVisuals();
  5643. isChatting = false;
  5644. }
  5645. /*
  5646. =====================
  5647. idPlayer::GetAIAimTargets
  5648. Returns positions for the AI to aim at.
  5649. =====================
  5650. */
  5651. void idPlayer::GetAIAimTargets( const idVec3 &lastSightPos, idVec3 &headPos, idVec3 &chestPos ) {
  5652. idVec3 offset;
  5653. idMat3 axis;
  5654. idVec3 origin;
  5655. origin = lastSightPos - physicsObj.GetOrigin();
  5656. GetJointWorldTransform( chestJoint, gameLocal.time, offset, axis );
  5657. headPos = offset + origin;
  5658. GetJointWorldTransform( headJoint, gameLocal.time, offset, axis );
  5659. chestPos = offset + origin;
  5660. }
  5661. /*
  5662. ================
  5663. idPlayer::DamageFeedback
  5664. callback function for when another entity received damage from this entity. damage can be adjusted and returned to the caller.
  5665. ================
  5666. */
  5667. void idPlayer::DamageFeedback( idEntity *victim, idEntity *inflictor, int &damage ) {
  5668. assert( !gameLocal.isClient );
  5669. damage *= PowerUpModifier( BERSERK );
  5670. if ( damage && ( victim != this ) && victim->IsType( idActor::Type ) ) {
  5671. SetLastHitTime( gameLocal.time );
  5672. }
  5673. }
  5674. /*
  5675. =================
  5676. idPlayer::CalcDamagePoints
  5677. Calculates how many health and armor points will be inflicted, but
  5678. doesn't actually do anything with them. This is used to tell when an attack
  5679. would have killed the player, possibly allowing a "saving throw"
  5680. =================
  5681. */
  5682. void idPlayer::CalcDamagePoints( idEntity *inflictor, idEntity *attacker, const idDict *damageDef,
  5683. const float damageScale, const int location, int *health, int *armor ) {
  5684. int damage;
  5685. int armorSave;
  5686. damageDef->GetInt( "damage", "20", damage );
  5687. damage = GetDamageForLocation( damage, location );
  5688. idPlayer *player = attacker->IsType( idPlayer::Type ) ? static_cast<idPlayer*>(attacker) : NULL;
  5689. if ( !gameLocal.isMultiplayer ) {
  5690. if ( inflictor != gameLocal.world ) {
  5691. switch ( g_skill.GetInteger() ) {
  5692. case 0:
  5693. damage *= 0.80f;
  5694. if ( damage < 1 ) {
  5695. damage = 1;
  5696. }
  5697. break;
  5698. case 2:
  5699. damage *= 1.70f;
  5700. break;
  5701. case 3:
  5702. damage *= 3.5f;
  5703. break;
  5704. default:
  5705. break;
  5706. }
  5707. }
  5708. }
  5709. damage *= damageScale;
  5710. // always give half damage if hurting self
  5711. if ( attacker == this ) {
  5712. if ( gameLocal.isMultiplayer ) {
  5713. // only do this in mp so single player plasma and rocket splash is very dangerous in close quarters
  5714. damage *= damageDef->GetFloat( "selfDamageScale", "0.5" );
  5715. } else {
  5716. damage *= damageDef->GetFloat( "selfDamageScale", "1" );
  5717. }
  5718. }
  5719. // check for completely getting out of the damage
  5720. if ( !damageDef->GetBool( "noGod" ) ) {
  5721. // check for godmode
  5722. if ( godmode ) {
  5723. damage = 0;
  5724. }
  5725. }
  5726. // inform the attacker that they hit someone
  5727. attacker->DamageFeedback( this, inflictor, damage );
  5728. // save some from armor
  5729. if ( !damageDef->GetBool( "noArmor" ) ) {
  5730. float armor_protection;
  5731. armor_protection = ( gameLocal.isMultiplayer ) ? g_armorProtectionMP.GetFloat() : g_armorProtection.GetFloat();
  5732. armorSave = ceil( damage * armor_protection );
  5733. if ( armorSave >= inventory.armor ) {
  5734. armorSave = inventory.armor;
  5735. }
  5736. if ( !damage ) {
  5737. armorSave = 0;
  5738. } else if ( armorSave >= damage ) {
  5739. armorSave = damage - 1;
  5740. damage = 1;
  5741. } else {
  5742. damage -= armorSave;
  5743. }
  5744. } else {
  5745. armorSave = 0;
  5746. }
  5747. // check for team damage
  5748. if ( gameLocal.gameType == GAME_TDM
  5749. && !gameLocal.serverInfo.GetBool( "si_teamDamage" )
  5750. && !damageDef->GetBool( "noTeam" )
  5751. && player
  5752. && player != this // you get self damage no matter what
  5753. && player->team == team ) {
  5754. damage = 0;
  5755. }
  5756. *health = damage;
  5757. *armor = armorSave;
  5758. }
  5759. /*
  5760. ============
  5761. Damage
  5762. this entity that is being damaged
  5763. inflictor entity that is causing the damage
  5764. attacker entity that caused the inflictor to damage targ
  5765. example: this=monster, inflictor=rocket, attacker=player
  5766. dir direction of the attack for knockback in global space
  5767. damageDef an idDict with all the options for damage effects
  5768. inflictor, attacker, dir, and point can be NULL for environmental effects
  5769. ============
  5770. */
  5771. void idPlayer::Damage( idEntity *inflictor, idEntity *attacker, const idVec3 &dir,
  5772. const char *damageDefName, const float damageScale, const int location ) {
  5773. idVec3 kick;
  5774. int damage;
  5775. int armorSave;
  5776. int knockback;
  5777. idVec3 damage_from;
  5778. idVec3 localDamageVector;
  5779. float attackerPushScale;
  5780. // damage is only processed on server
  5781. if ( gameLocal.isClient ) {
  5782. return;
  5783. }
  5784. if ( !fl.takedamage || noclip || spectating || gameLocal.inCinematic ) {
  5785. return;
  5786. }
  5787. if ( !inflictor ) {
  5788. inflictor = gameLocal.world;
  5789. }
  5790. if ( !attacker ) {
  5791. attacker = gameLocal.world;
  5792. }
  5793. if ( attacker->IsType( idAI::Type ) ) {
  5794. if ( PowerUpActive( BERSERK ) ) {
  5795. return;
  5796. }
  5797. // don't take damage from monsters during influences
  5798. if ( influenceActive != 0 ) {
  5799. return;
  5800. }
  5801. }
  5802. const idDeclEntityDef *damageDef = gameLocal.FindEntityDef( damageDefName, false );
  5803. if ( !damageDef ) {
  5804. gameLocal.Warning( "Unknown damageDef '%s'", damageDefName );
  5805. return;
  5806. }
  5807. if ( damageDef->dict.GetBool( "ignore_player" ) ) {
  5808. return;
  5809. }
  5810. CalcDamagePoints( inflictor, attacker, &damageDef->dict, damageScale, location, &damage, &armorSave );
  5811. // determine knockback
  5812. damageDef->dict.GetInt( "knockback", "20", knockback );
  5813. if ( knockback != 0 && !fl.noknockback ) {
  5814. if ( attacker == this ) {
  5815. damageDef->dict.GetFloat( "attackerPushScale", "0", attackerPushScale );
  5816. } else {
  5817. attackerPushScale = 1.0f;
  5818. }
  5819. kick = dir;
  5820. kick.Normalize();
  5821. kick *= g_knockback.GetFloat() * knockback * attackerPushScale / 200.0f;
  5822. physicsObj.SetLinearVelocity( physicsObj.GetLinearVelocity() + kick );
  5823. // set the timer so that the player can't cancel out the movement immediately
  5824. physicsObj.SetKnockBack( idMath::ClampInt( 50, 200, knockback * 2 ) );
  5825. }
  5826. // give feedback on the player view and audibly when armor is helping
  5827. if ( armorSave ) {
  5828. inventory.armor -= armorSave;
  5829. if ( gameLocal.time > lastArmorPulse + 200 ) {
  5830. StartSound( "snd_hitArmor", SND_CHANNEL_ITEM, 0, false, NULL );
  5831. }
  5832. lastArmorPulse = gameLocal.time;
  5833. }
  5834. if ( damageDef->dict.GetBool( "burn" ) ) {
  5835. StartSound( "snd_burn", SND_CHANNEL_BODY3, 0, false, NULL );
  5836. } else if ( damageDef->dict.GetBool( "no_air" ) ) {
  5837. if ( !armorSave && health > 0 ) {
  5838. StartSound( "snd_airGasp", SND_CHANNEL_ITEM, 0, false, NULL );
  5839. }
  5840. }
  5841. if ( g_debugDamage.GetInteger() ) {
  5842. gameLocal.Printf( "client:%i health:%i damage:%i armor:%i\n",
  5843. entityNumber, health, damage, armorSave );
  5844. }
  5845. // move the world direction vector to local coordinates
  5846. damage_from = dir;
  5847. damage_from.Normalize();
  5848. viewAxis.ProjectVector( damage_from, localDamageVector );
  5849. // add to the damage inflicted on a player this frame
  5850. // the total will be turned into screen blends and view angle kicks
  5851. // at the end of the frame
  5852. if ( health > 0 ) {
  5853. playerView.DamageImpulse( localDamageVector, &damageDef->dict );
  5854. }
  5855. // do the damage
  5856. if ( damage > 0 ) {
  5857. if ( !gameLocal.isMultiplayer ) {
  5858. float scale = g_damageScale.GetFloat();
  5859. if ( g_useDynamicProtection.GetBool() && g_skill.GetInteger() < 2 ) {
  5860. if ( gameLocal.time > lastDmgTime + 500 && scale > 0.25f ) {
  5861. scale -= 0.05f;
  5862. g_damageScale.SetFloat( scale );
  5863. }
  5864. }
  5865. if ( scale > 0.0f ) {
  5866. damage *= scale;
  5867. }
  5868. }
  5869. if ( damage < 1 ) {
  5870. damage = 1;
  5871. }
  5872. int oldHealth = health;
  5873. health -= damage;
  5874. if ( health <= 0 ) {
  5875. if ( health < -999 ) {
  5876. health = -999;
  5877. }
  5878. isTelefragged = damageDef->dict.GetBool( "telefrag" );
  5879. lastDmgTime = gameLocal.time;
  5880. Killed( inflictor, attacker, damage, dir, location );
  5881. } else {
  5882. // force a blink
  5883. blink_time = 0;
  5884. // let the anim script know we took damage
  5885. AI_PAIN = Pain( inflictor, attacker, damage, dir, location );
  5886. if ( !g_testDeath.GetBool() ) {
  5887. lastDmgTime = gameLocal.time;
  5888. }
  5889. }
  5890. } else {
  5891. // don't accumulate impulses
  5892. if ( af.IsLoaded() ) {
  5893. // clear impacts
  5894. af.Rest();
  5895. // physics is turned off by calling af.Rest()
  5896. BecomeActive( TH_PHYSICS );
  5897. }
  5898. }
  5899. lastDamageDef = damageDef->Index();
  5900. lastDamageDir = damage_from;
  5901. lastDamageLocation = location;
  5902. }
  5903. /*
  5904. ===========
  5905. idPlayer::Teleport
  5906. ============
  5907. */
  5908. void idPlayer::Teleport( const idVec3 &origin, const idAngles &angles, idEntity *destination ) {
  5909. idVec3 org;
  5910. if ( weapon.GetEntity() ) {
  5911. weapon.GetEntity()->LowerWeapon();
  5912. }
  5913. SetOrigin( origin + idVec3( 0, 0, CM_CLIP_EPSILON ) );
  5914. if ( !gameLocal.isMultiplayer && GetFloorPos( 16.0f, org ) ) {
  5915. SetOrigin( org );
  5916. }
  5917. // clear the ik heights so model doesn't appear in the wrong place
  5918. walkIK.EnableAll();
  5919. GetPhysics()->SetLinearVelocity( vec3_origin );
  5920. SetViewAngles( angles );
  5921. legsYaw = 0.0f;
  5922. idealLegsYaw = 0.0f;
  5923. oldViewYaw = viewAngles.yaw;
  5924. if ( gameLocal.isMultiplayer ) {
  5925. playerView.Flash( colorWhite, 140 );
  5926. }
  5927. UpdateVisuals();
  5928. teleportEntity = destination;
  5929. if ( !gameLocal.isClient && !noclip ) {
  5930. if ( gameLocal.isMultiplayer ) {
  5931. // kill anything at the new position or mark for kill depending on immediate or delayed teleport
  5932. gameLocal.KillBox( this, destination != NULL );
  5933. } else {
  5934. // kill anything at the new position
  5935. gameLocal.KillBox( this, true );
  5936. }
  5937. }
  5938. }
  5939. /*
  5940. ====================
  5941. idPlayer::SetPrivateCameraView
  5942. ====================
  5943. */
  5944. void idPlayer::SetPrivateCameraView( idCamera *camView ) {
  5945. privateCameraView = camView;
  5946. if ( camView ) {
  5947. StopFiring();
  5948. Hide();
  5949. } else {
  5950. if ( !spectating ) {
  5951. Show();
  5952. }
  5953. }
  5954. }
  5955. /*
  5956. ====================
  5957. idPlayer::DefaultFov
  5958. Returns the base FOV
  5959. ====================
  5960. */
  5961. float idPlayer::DefaultFov( void ) const {
  5962. float fov;
  5963. fov = g_fov.GetFloat();
  5964. if ( gameLocal.isMultiplayer ) {
  5965. if ( fov < 90.0f ) {
  5966. return 90.0f;
  5967. } else if ( fov > 110.0f ) {
  5968. return 110.0f;
  5969. }
  5970. }
  5971. return fov;
  5972. }
  5973. /*
  5974. ====================
  5975. idPlayer::CalcFov
  5976. Fixed fov at intermissions, otherwise account for fov variable and zooms.
  5977. ====================
  5978. */
  5979. float idPlayer::CalcFov( bool honorZoom ) {
  5980. float fov;
  5981. if ( fxFov ) {
  5982. return DefaultFov() + 10.0f + cos( ( gameLocal.time + 2000 ) * 0.01 ) * 10.0f;
  5983. }
  5984. if ( influenceFov ) {
  5985. return influenceFov;
  5986. }
  5987. if ( zoomFov.IsDone( gameLocal.time ) ) {
  5988. fov = ( honorZoom && usercmd.buttons & BUTTON_ZOOM ) && weapon.GetEntity() ? weapon.GetEntity()->GetZoomFov() : DefaultFov();
  5989. } else {
  5990. fov = zoomFov.GetCurrentValue( gameLocal.time );
  5991. }
  5992. // bound normal viewsize
  5993. if ( fov < 1 ) {
  5994. fov = 1;
  5995. } else if ( fov > 179 ) {
  5996. fov = 179;
  5997. }
  5998. return fov;
  5999. }
  6000. /*
  6001. ==============
  6002. idPlayer::GunTurningOffset
  6003. generate a rotational offset for the gun based on the view angle
  6004. history in loggedViewAngles
  6005. ==============
  6006. */
  6007. idAngles idPlayer::GunTurningOffset( void ) {
  6008. idAngles a;
  6009. a.Zero();
  6010. if ( gameLocal.framenum < NUM_LOGGED_VIEW_ANGLES ) {
  6011. return a;
  6012. }
  6013. idAngles current = loggedViewAngles[ gameLocal.framenum & (NUM_LOGGED_VIEW_ANGLES-1) ];
  6014. idAngles av, base;
  6015. int weaponAngleOffsetAverages;
  6016. float weaponAngleOffsetScale, weaponAngleOffsetMax;
  6017. weapon.GetEntity()->GetWeaponAngleOffsets( &weaponAngleOffsetAverages, &weaponAngleOffsetScale, &weaponAngleOffsetMax );
  6018. av = current;
  6019. // calcualte this so the wrap arounds work properly
  6020. for ( int j = 1 ; j < weaponAngleOffsetAverages ; j++ ) {
  6021. idAngles a2 = loggedViewAngles[ ( gameLocal.framenum - j ) & (NUM_LOGGED_VIEW_ANGLES-1) ];
  6022. idAngles delta = a2 - current;
  6023. if ( delta[1] > 180 ) {
  6024. delta[1] -= 360;
  6025. } else if ( delta[1] < -180 ) {
  6026. delta[1] += 360;
  6027. }
  6028. av += delta * ( 1.0f / weaponAngleOffsetAverages );
  6029. }
  6030. a = ( av - current ) * weaponAngleOffsetScale;
  6031. for ( int i = 0 ; i < 3 ; i++ ) {
  6032. if ( a[i] < -weaponAngleOffsetMax ) {
  6033. a[i] = -weaponAngleOffsetMax;
  6034. } else if ( a[i] > weaponAngleOffsetMax ) {
  6035. a[i] = weaponAngleOffsetMax;
  6036. }
  6037. }
  6038. return a;
  6039. }
  6040. /*
  6041. ==============
  6042. idPlayer::GunAcceleratingOffset
  6043. generate a positional offset for the gun based on the movement
  6044. history in loggedAccelerations
  6045. ==============
  6046. */
  6047. idVec3 idPlayer::GunAcceleratingOffset( void ) {
  6048. idVec3 ofs;
  6049. float weaponOffsetTime, weaponOffsetScale;
  6050. ofs.Zero();
  6051. weapon.GetEntity()->GetWeaponTimeOffsets( &weaponOffsetTime, &weaponOffsetScale );
  6052. int stop = currentLoggedAccel - NUM_LOGGED_ACCELS;
  6053. if ( stop < 0 ) {
  6054. stop = 0;
  6055. }
  6056. for ( int i = currentLoggedAccel-1 ; i > stop ; i-- ) {
  6057. loggedAccel_t *acc = &loggedAccel[i&(NUM_LOGGED_ACCELS-1)];
  6058. float f;
  6059. float t = gameLocal.time - acc->time;
  6060. if ( t >= weaponOffsetTime ) {
  6061. break; // remainder are too old to care about
  6062. }
  6063. f = t / weaponOffsetTime;
  6064. f = ( cos( f * 2.0f * idMath::PI ) - 1.0f ) * 0.5f;
  6065. ofs += f * weaponOffsetScale * acc->dir;
  6066. }
  6067. return ofs;
  6068. }
  6069. /*
  6070. ==============
  6071. idPlayer::CalculateViewWeaponPos
  6072. Calculate the bobbing position of the view weapon
  6073. ==============
  6074. */
  6075. void idPlayer::CalculateViewWeaponPos( idVec3 &origin, idMat3 &axis ) {
  6076. float scale;
  6077. float fracsin;
  6078. idAngles angles;
  6079. int delta;
  6080. // CalculateRenderView must have been called first
  6081. const idVec3 &viewOrigin = firstPersonViewOrigin;
  6082. const idMat3 &viewAxis = firstPersonViewAxis;
  6083. // these cvars are just for hand tweaking before moving a value to the weapon def
  6084. idVec3 gunpos( g_gun_x.GetFloat(), g_gun_y.GetFloat(), g_gun_z.GetFloat() );
  6085. // as the player changes direction, the gun will take a small lag
  6086. idVec3 gunOfs = GunAcceleratingOffset();
  6087. origin = viewOrigin + ( gunpos + gunOfs ) * viewAxis;
  6088. // on odd legs, invert some angles
  6089. if ( bobCycle & 128 ) {
  6090. scale = -xyspeed;
  6091. } else {
  6092. scale = xyspeed;
  6093. }
  6094. // gun angles from bobbing
  6095. angles.roll = scale * bobfracsin * 0.005f;
  6096. angles.yaw = scale * bobfracsin * 0.01f;
  6097. angles.pitch = xyspeed * bobfracsin * 0.005f;
  6098. // gun angles from turning
  6099. if ( gameLocal.isMultiplayer ) {
  6100. idAngles offset = GunTurningOffset();
  6101. offset *= g_mpWeaponAngleScale.GetFloat();
  6102. angles += offset;
  6103. } else {
  6104. angles += GunTurningOffset();
  6105. }
  6106. idVec3 gravity = physicsObj.GetGravityNormal();
  6107. // drop the weapon when landing after a jump / fall
  6108. delta = gameLocal.time - landTime;
  6109. if ( delta < LAND_DEFLECT_TIME ) {
  6110. origin -= gravity * ( landChange*0.25f * delta / LAND_DEFLECT_TIME );
  6111. } else if ( delta < LAND_DEFLECT_TIME + LAND_RETURN_TIME ) {
  6112. origin -= gravity * ( landChange*0.25f * (LAND_DEFLECT_TIME + LAND_RETURN_TIME - delta) / LAND_RETURN_TIME );
  6113. }
  6114. // speed sensitive idle drift
  6115. scale = xyspeed + 40.0f;
  6116. fracsin = scale * sin( MS2SEC( gameLocal.time ) ) * 0.01f;
  6117. angles.roll += fracsin;
  6118. angles.yaw += fracsin;
  6119. angles.pitch += fracsin;
  6120. axis = angles.ToMat3() * viewAxis;
  6121. }
  6122. /*
  6123. ===============
  6124. idPlayer::OffsetThirdPersonView
  6125. ===============
  6126. */
  6127. void idPlayer::OffsetThirdPersonView( float angle, float range, float height, bool clip ) {
  6128. idVec3 view;
  6129. idVec3 focusAngles;
  6130. trace_t trace;
  6131. idVec3 focusPoint;
  6132. float focusDist;
  6133. float forwardScale, sideScale;
  6134. idVec3 origin;
  6135. idAngles angles;
  6136. idMat3 axis;
  6137. idBounds bounds;
  6138. angles = viewAngles;
  6139. GetViewPos( origin, axis );
  6140. if ( angle ) {
  6141. angles.pitch = 0.0f;
  6142. }
  6143. if ( angles.pitch > 45.0f ) {
  6144. angles.pitch = 45.0f; // don't go too far overhead
  6145. }
  6146. focusPoint = origin + angles.ToForward() * THIRD_PERSON_FOCUS_DISTANCE;
  6147. focusPoint.z += height;
  6148. view = origin;
  6149. view.z += 8 + height;
  6150. angles.pitch *= 0.5f;
  6151. renderView->viewaxis = angles.ToMat3() * physicsObj.GetGravityAxis();
  6152. idMath::SinCos( DEG2RAD( angle ), sideScale, forwardScale );
  6153. view -= range * forwardScale * renderView->viewaxis[ 0 ];
  6154. view += range * sideScale * renderView->viewaxis[ 1 ];
  6155. if ( clip ) {
  6156. // trace a ray from the origin to the viewpoint to make sure the view isn't
  6157. // in a solid block. Use an 8 by 8 block to prevent the view from near clipping anything
  6158. bounds = idBounds( idVec3( -4, -4, -4 ), idVec3( 4, 4, 4 ) );
  6159. gameLocal.clip.TraceBounds( trace, origin, view, bounds, MASK_SOLID, this );
  6160. if ( trace.fraction != 1.0f ) {
  6161. view = trace.endpos;
  6162. view.z += ( 1.0f - trace.fraction ) * 32.0f;
  6163. // try another trace to this position, because a tunnel may have the ceiling
  6164. // close enough that this is poking out
  6165. gameLocal.clip.TraceBounds( trace, origin, view, bounds, MASK_SOLID, this );
  6166. view = trace.endpos;
  6167. }
  6168. }
  6169. // select pitch to look at focus point from vieword
  6170. focusPoint -= view;
  6171. focusDist = idMath::Sqrt( focusPoint[0] * focusPoint[0] + focusPoint[1] * focusPoint[1] );
  6172. if ( focusDist < 1.0f ) {
  6173. focusDist = 1.0f; // should never happen
  6174. }
  6175. angles.pitch = - RAD2DEG( atan2( focusPoint.z, focusDist ) );
  6176. angles.yaw -= angle;
  6177. renderView->vieworg = view;
  6178. renderView->viewaxis = angles.ToMat3() * physicsObj.GetGravityAxis();
  6179. renderView->viewID = 0;
  6180. }
  6181. /*
  6182. ===============
  6183. idPlayer::GetEyePosition
  6184. ===============
  6185. */
  6186. idVec3 idPlayer::GetEyePosition( void ) const {
  6187. idVec3 org;
  6188. // use the smoothed origin if spectating another player in multiplayer
  6189. if ( gameLocal.isClient && entityNumber != gameLocal.localClientNum ) {
  6190. org = smoothedOrigin;
  6191. } else {
  6192. org = GetPhysics()->GetOrigin();
  6193. }
  6194. return org + ( GetPhysics()->GetGravityNormal() * -eyeOffset.z );
  6195. }
  6196. /*
  6197. ===============
  6198. idPlayer::GetViewPos
  6199. ===============
  6200. */
  6201. void idPlayer::GetViewPos( idVec3 &origin, idMat3 &axis ) const {
  6202. idAngles angles;
  6203. // if dead, fix the angle and don't add any kick
  6204. if ( health <= 0 ) {
  6205. angles.yaw = viewAngles.yaw;
  6206. angles.roll = 40;
  6207. angles.pitch = -15;
  6208. axis = angles.ToMat3();
  6209. origin = GetEyePosition();
  6210. } else {
  6211. origin = GetEyePosition() + viewBob;
  6212. angles = viewAngles + viewBobAngles + playerView.AngleOffset();
  6213. axis = angles.ToMat3() * physicsObj.GetGravityAxis();
  6214. // adjust the origin based on the camera nodal distance (eye distance from neck)
  6215. origin += physicsObj.GetGravityNormal() * g_viewNodalZ.GetFloat();
  6216. origin += axis[0] * g_viewNodalX.GetFloat() + axis[2] * g_viewNodalZ.GetFloat();
  6217. }
  6218. }
  6219. /*
  6220. ===============
  6221. idPlayer::CalculateFirstPersonView
  6222. ===============
  6223. */
  6224. void idPlayer::CalculateFirstPersonView( void ) {
  6225. if ( ( pm_modelView.GetInteger() == 1 ) || ( ( pm_modelView.GetInteger() == 2 ) && ( health <= 0 ) ) ) {
  6226. // Displays the view from the point of view of the "camera" joint in the player model
  6227. idMat3 axis;
  6228. idVec3 origin;
  6229. idAngles ang;
  6230. ang = viewBobAngles + playerView.AngleOffset();
  6231. ang.yaw += viewAxis[ 0 ].ToYaw();
  6232. jointHandle_t joint = animator.GetJointHandle( "camera" );
  6233. animator.GetJointTransform( joint, gameLocal.time, origin, axis );
  6234. firstPersonViewOrigin = ( origin + modelOffset ) * ( viewAxis * physicsObj.GetGravityAxis() ) + physicsObj.GetOrigin() + viewBob;
  6235. firstPersonViewAxis = axis * ang.ToMat3() * physicsObj.GetGravityAxis();
  6236. } else {
  6237. // offset for local bobbing and kicks
  6238. GetViewPos( firstPersonViewOrigin, firstPersonViewAxis );
  6239. #if 0
  6240. // shakefrom sound stuff only happens in first person
  6241. firstPersonViewAxis = firstPersonViewAxis * playerView.ShakeAxis();
  6242. #endif
  6243. }
  6244. }
  6245. /*
  6246. ==================
  6247. idPlayer::GetRenderView
  6248. Returns the renderView that was calculated for this tic
  6249. ==================
  6250. */
  6251. renderView_t *idPlayer::GetRenderView( void ) {
  6252. return renderView;
  6253. }
  6254. /*
  6255. ==================
  6256. idPlayer::CalculateRenderView
  6257. create the renderView for the current tic
  6258. ==================
  6259. */
  6260. void idPlayer::CalculateRenderView( void ) {
  6261. int i;
  6262. float range;
  6263. if ( !renderView ) {
  6264. renderView = new renderView_t;
  6265. }
  6266. memset( renderView, 0, sizeof( *renderView ) );
  6267. // copy global shader parms
  6268. for( i = 0; i < MAX_GLOBAL_SHADER_PARMS; i++ ) {
  6269. renderView->shaderParms[ i ] = gameLocal.globalShaderParms[ i ];
  6270. }
  6271. renderView->globalMaterial = gameLocal.GetGlobalMaterial();
  6272. renderView->time = gameLocal.time;
  6273. // calculate size of 3D view
  6274. renderView->x = 0;
  6275. renderView->y = 0;
  6276. renderView->width = SCREEN_WIDTH;
  6277. renderView->height = SCREEN_HEIGHT;
  6278. renderView->viewID = 0;
  6279. // check if we should be drawing from a camera's POV
  6280. if ( !noclip && (gameLocal.GetCamera() || privateCameraView) ) {
  6281. // get origin, axis, and fov
  6282. if ( privateCameraView ) {
  6283. privateCameraView->GetViewParms( renderView );
  6284. } else {
  6285. gameLocal.GetCamera()->GetViewParms( renderView );
  6286. }
  6287. } else {
  6288. if ( g_stopTime.GetBool() ) {
  6289. renderView->vieworg = firstPersonViewOrigin;
  6290. renderView->viewaxis = firstPersonViewAxis;
  6291. if ( !pm_thirdPerson.GetBool() ) {
  6292. // set the viewID to the clientNum + 1, so we can suppress the right player bodies and
  6293. // allow the right player view weapons
  6294. renderView->viewID = entityNumber + 1;
  6295. }
  6296. } else if ( pm_thirdPerson.GetBool() ) {
  6297. OffsetThirdPersonView( pm_thirdPersonAngle.GetFloat(), pm_thirdPersonRange.GetFloat(), pm_thirdPersonHeight.GetFloat(), pm_thirdPersonClip.GetBool() );
  6298. } else if ( pm_thirdPersonDeath.GetBool() ) {
  6299. range = gameLocal.time < minRespawnTime ? ( gameLocal.time + RAGDOLL_DEATH_TIME - minRespawnTime ) * ( 120.0f / RAGDOLL_DEATH_TIME ) : 120.0f;
  6300. OffsetThirdPersonView( 0.0f, 20.0f + range, 0.0f, false );
  6301. } else {
  6302. renderView->vieworg = firstPersonViewOrigin;
  6303. renderView->viewaxis = firstPersonViewAxis;
  6304. // set the viewID to the clientNum + 1, so we can suppress the right player bodies and
  6305. // allow the right player view weapons
  6306. renderView->viewID = entityNumber + 1;
  6307. }
  6308. // field of view
  6309. gameLocal.CalcFov( CalcFov( true ), renderView->fov_x, renderView->fov_y );
  6310. }
  6311. if ( renderView->fov_y == 0 ) {
  6312. common->Error( "renderView->fov_y == 0" );
  6313. }
  6314. if ( g_showviewpos.GetBool() ) {
  6315. gameLocal.Printf( "%s : %s\n", renderView->vieworg.ToString(), renderView->viewaxis.ToAngles().ToString() );
  6316. }
  6317. }
  6318. /*
  6319. =============
  6320. idPlayer::AddAIKill
  6321. =============
  6322. */
  6323. void idPlayer::AddAIKill( void ) {
  6324. int max_souls;
  6325. int ammo_souls;
  6326. if ( ( weapon_soulcube < 0 ) || ( inventory.weapons & ( 1 << weapon_soulcube ) ) == 0 ) {
  6327. return;
  6328. }
  6329. assert( hud );
  6330. ammo_souls = idWeapon::GetAmmoNumForName( "ammo_souls" );
  6331. max_souls = inventory.MaxAmmoForAmmoClass( this, "ammo_souls" );
  6332. if ( inventory.ammo[ ammo_souls ] < max_souls ) {
  6333. inventory.ammo[ ammo_souls ]++;
  6334. if ( inventory.ammo[ ammo_souls ] >= max_souls ) {
  6335. hud->HandleNamedEvent( "soulCubeReady" );
  6336. StartSound( "snd_soulcube_ready", SND_CHANNEL_ANY, 0, false, NULL );
  6337. }
  6338. }
  6339. }
  6340. /*
  6341. =============
  6342. idPlayer::SetSoulCubeProjectile
  6343. =============
  6344. */
  6345. void idPlayer::SetSoulCubeProjectile( idProjectile *projectile ) {
  6346. soulCubeProjectile = projectile;
  6347. }
  6348. /*
  6349. =============
  6350. idPlayer::AddProjectilesFired
  6351. =============
  6352. */
  6353. void idPlayer::AddProjectilesFired( int count ) {
  6354. numProjectilesFired += count;
  6355. }
  6356. /*
  6357. =============
  6358. idPlayer::AddProjectileHites
  6359. =============
  6360. */
  6361. void idPlayer::AddProjectileHits( int count ) {
  6362. numProjectileHits += count;
  6363. }
  6364. /*
  6365. =============
  6366. idPlayer::SetLastHitTime
  6367. =============
  6368. */
  6369. void idPlayer::SetLastHitTime( int time ) {
  6370. idPlayer *aimed = NULL;
  6371. if ( time && lastHitTime != time ) {
  6372. lastHitToggle ^= 1;
  6373. }
  6374. lastHitTime = time;
  6375. if ( !time ) {
  6376. // level start and inits
  6377. return;
  6378. }
  6379. if ( gameLocal.isMultiplayer && ( time - lastSndHitTime ) > 10 ) {
  6380. lastSndHitTime = time;
  6381. StartSound( "snd_hit_feedback", SND_CHANNEL_ANY, SSF_PRIVATE_SOUND, false, NULL );
  6382. }
  6383. if ( cursor ) {
  6384. cursor->HandleNamedEvent( "hitTime" );
  6385. }
  6386. if ( hud ) {
  6387. if ( MPAim != -1 ) {
  6388. if ( gameLocal.entities[ MPAim ] && gameLocal.entities[ MPAim ]->IsType( idPlayer::Type ) ) {
  6389. aimed = static_cast< idPlayer * >( gameLocal.entities[ MPAim ] );
  6390. }
  6391. assert( aimed );
  6392. // full highlight, no fade till loosing aim
  6393. hud->SetStateString( "aim_text", gameLocal.userInfo[ MPAim ].GetString( "ui_name" ) );
  6394. if ( aimed ) {
  6395. hud->SetStateFloat( "aim_color", aimed->colorBarIndex );
  6396. }
  6397. hud->HandleNamedEvent( "aim_flash" );
  6398. MPAimHighlight = true;
  6399. MPAimFadeTime = 0;
  6400. } else if ( lastMPAim != -1 ) {
  6401. if ( gameLocal.entities[ lastMPAim ] && gameLocal.entities[ lastMPAim ]->IsType( idPlayer::Type ) ) {
  6402. aimed = static_cast< idPlayer * >( gameLocal.entities[ lastMPAim ] );
  6403. }
  6404. assert( aimed );
  6405. // start fading right away
  6406. hud->SetStateString( "aim_text", gameLocal.userInfo[ lastMPAim ].GetString( "ui_name" ) );
  6407. if ( aimed ) {
  6408. hud->SetStateFloat( "aim_color", aimed->colorBarIndex );
  6409. }
  6410. hud->HandleNamedEvent( "aim_flash" );
  6411. hud->HandleNamedEvent( "aim_fade" );
  6412. MPAimHighlight = false;
  6413. MPAimFadeTime = gameLocal.realClientTime;
  6414. }
  6415. }
  6416. }
  6417. /*
  6418. =============
  6419. idPlayer::SetInfluenceLevel
  6420. =============
  6421. */
  6422. void idPlayer::SetInfluenceLevel( int level ) {
  6423. if ( level != influenceActive ) {
  6424. if ( level ) {
  6425. for ( idEntity *ent = gameLocal.spawnedEntities.Next(); ent != NULL; ent = ent->spawnNode.Next() ) {
  6426. if ( ent->IsType( idProjectile::Type ) ) {
  6427. // remove all projectiles
  6428. ent->PostEventMS( &EV_Remove, 0 );
  6429. }
  6430. }
  6431. if ( weaponEnabled && weapon.GetEntity() ) {
  6432. weapon.GetEntity()->EnterCinematic();
  6433. }
  6434. } else {
  6435. physicsObj.SetLinearVelocity( vec3_origin );
  6436. if ( weaponEnabled && weapon.GetEntity() ) {
  6437. weapon.GetEntity()->ExitCinematic();
  6438. }
  6439. }
  6440. influenceActive = level;
  6441. }
  6442. }
  6443. /*
  6444. =============
  6445. idPlayer::SetInfluenceView
  6446. =============
  6447. */
  6448. void idPlayer::SetInfluenceView( const char *mtr, const char *skinname, float radius, idEntity *ent ) {
  6449. influenceMaterial = NULL;
  6450. influenceEntity = NULL;
  6451. influenceSkin = NULL;
  6452. if ( mtr && *mtr ) {
  6453. influenceMaterial = declManager->FindMaterial( mtr );
  6454. }
  6455. if ( skinname && *skinname ) {
  6456. influenceSkin = declManager->FindSkin( skinname );
  6457. if ( head.GetEntity() ) {
  6458. head.GetEntity()->GetRenderEntity()->shaderParms[ SHADERPARM_TIMEOFFSET ] = -MS2SEC( gameLocal.time );
  6459. }
  6460. UpdateVisuals();
  6461. }
  6462. influenceRadius = radius;
  6463. if ( radius > 0.0f ) {
  6464. influenceEntity = ent;
  6465. }
  6466. }
  6467. /*
  6468. =============
  6469. idPlayer::SetInfluenceFov
  6470. =============
  6471. */
  6472. void idPlayer::SetInfluenceFov( float fov ) {
  6473. influenceFov = fov;
  6474. }
  6475. /*
  6476. ================
  6477. idPlayer::OnLadder
  6478. ================
  6479. */
  6480. bool idPlayer::OnLadder( void ) const {
  6481. return physicsObj.OnLadder();
  6482. }
  6483. /*
  6484. ==================
  6485. idPlayer::Event_GetButtons
  6486. ==================
  6487. */
  6488. void idPlayer::Event_GetButtons( void ) {
  6489. idThread::ReturnInt( usercmd.buttons );
  6490. }
  6491. /*
  6492. ==================
  6493. idPlayer::Event_GetMove
  6494. ==================
  6495. */
  6496. void idPlayer::Event_GetMove( void ) {
  6497. idVec3 move( usercmd.forwardmove, usercmd.rightmove, usercmd.upmove );
  6498. idThread::ReturnVector( move );
  6499. }
  6500. /*
  6501. ================
  6502. idPlayer::Event_GetViewAngles
  6503. ================
  6504. */
  6505. void idPlayer::Event_GetViewAngles( void ) {
  6506. idThread::ReturnVector( idVec3( viewAngles[0], viewAngles[1], viewAngles[2] ) );
  6507. }
  6508. /*
  6509. ==================
  6510. idPlayer::Event_StopFxFov
  6511. ==================
  6512. */
  6513. void idPlayer::Event_StopFxFov( void ) {
  6514. fxFov = false;
  6515. }
  6516. /*
  6517. ==================
  6518. idPlayer::StartFxFov
  6519. ==================
  6520. */
  6521. void idPlayer::StartFxFov( float duration ) {
  6522. fxFov = true;
  6523. PostEventSec( &EV_Player_StopFxFov, duration );
  6524. }
  6525. /*
  6526. ==================
  6527. idPlayer::Event_EnableWeapon
  6528. ==================
  6529. */
  6530. void idPlayer::Event_EnableWeapon( void ) {
  6531. hiddenWeapon = gameLocal.world->spawnArgs.GetBool( "no_Weapons" );
  6532. weaponEnabled = true;
  6533. if ( weapon.GetEntity() ) {
  6534. weapon.GetEntity()->ExitCinematic();
  6535. }
  6536. }
  6537. /*
  6538. ==================
  6539. idPlayer::Event_DisableWeapon
  6540. ==================
  6541. */
  6542. void idPlayer::Event_DisableWeapon( void ) {
  6543. hiddenWeapon = gameLocal.world->spawnArgs.GetBool( "no_Weapons" );
  6544. weaponEnabled = false;
  6545. if ( weapon.GetEntity() ) {
  6546. weapon.GetEntity()->EnterCinematic();
  6547. }
  6548. }
  6549. /*
  6550. ==================
  6551. idPlayer::Event_GetCurrentWeapon
  6552. ==================
  6553. */
  6554. void idPlayer::Event_GetCurrentWeapon( void ) {
  6555. const char *weapon;
  6556. if ( currentWeapon >= 0 ) {
  6557. weapon = spawnArgs.GetString( va( "def_weapon%d", currentWeapon ) );
  6558. idThread::ReturnString( weapon );
  6559. } else {
  6560. idThread::ReturnString( "" );
  6561. }
  6562. }
  6563. /*
  6564. ==================
  6565. idPlayer::Event_GetPreviousWeapon
  6566. ==================
  6567. */
  6568. void idPlayer::Event_GetPreviousWeapon( void ) {
  6569. const char *weapon;
  6570. if ( previousWeapon >= 0 ) {
  6571. int pw = ( gameLocal.world->spawnArgs.GetBool( "no_Weapons" ) ) ? 0 : previousWeapon;
  6572. weapon = spawnArgs.GetString( va( "def_weapon%d", pw) );
  6573. idThread::ReturnString( weapon );
  6574. } else {
  6575. idThread::ReturnString( spawnArgs.GetString( "def_weapon0" ) );
  6576. }
  6577. }
  6578. /*
  6579. ==================
  6580. idPlayer::Event_SelectWeapon
  6581. ==================
  6582. */
  6583. void idPlayer::Event_SelectWeapon( const char *weaponName ) {
  6584. int i;
  6585. int weaponNum;
  6586. if ( gameLocal.isClient ) {
  6587. gameLocal.Warning( "Cannot switch weapons from script in multiplayer" );
  6588. return;
  6589. }
  6590. if ( hiddenWeapon && gameLocal.world->spawnArgs.GetBool( "no_Weapons" ) ) {
  6591. idealWeapon = weapon_fists;
  6592. weapon.GetEntity()->HideWeapon();
  6593. return;
  6594. }
  6595. weaponNum = -1;
  6596. for( i = 0; i < MAX_WEAPONS; i++ ) {
  6597. if ( inventory.weapons & ( 1 << i ) ) {
  6598. const char *weap = spawnArgs.GetString( va( "def_weapon%d", i ) );
  6599. if ( !idStr::Cmp( weap, weaponName ) ) {
  6600. weaponNum = i;
  6601. break;
  6602. }
  6603. }
  6604. }
  6605. if ( weaponNum < 0 ) {
  6606. gameLocal.Warning( "%s is not carrying weapon '%s'", name.c_str(), weaponName );
  6607. return;
  6608. }
  6609. hiddenWeapon = false;
  6610. idealWeapon = weaponNum;
  6611. UpdateHudWeapon();
  6612. }
  6613. /*
  6614. ==================
  6615. idPlayer::Event_GetWeaponEntity
  6616. ==================
  6617. */
  6618. void idPlayer::Event_GetWeaponEntity( void ) {
  6619. idThread::ReturnEntity( weapon.GetEntity() );
  6620. }
  6621. /*
  6622. ==================
  6623. idPlayer::Event_OpenPDA
  6624. ==================
  6625. */
  6626. void idPlayer::Event_OpenPDA( void ) {
  6627. if ( !gameLocal.isMultiplayer ) {
  6628. TogglePDA();
  6629. }
  6630. }
  6631. /*
  6632. ==================
  6633. idPlayer::Event_InPDA
  6634. ==================
  6635. */
  6636. void idPlayer::Event_InPDA( void ) {
  6637. idThread::ReturnInt( objectiveSystemOpen );
  6638. }
  6639. /*
  6640. ==================
  6641. idPlayer::TeleportDeath
  6642. ==================
  6643. */
  6644. void idPlayer::TeleportDeath( int killer ) {
  6645. teleportKiller = killer;
  6646. }
  6647. /*
  6648. ==================
  6649. idPlayer::Event_ExitTeleporter
  6650. ==================
  6651. */
  6652. void idPlayer::Event_ExitTeleporter( void ) {
  6653. idEntity *exitEnt;
  6654. float pushVel;
  6655. // verify and setup
  6656. exitEnt = teleportEntity.GetEntity();
  6657. if ( !exitEnt ) {
  6658. common->DPrintf( "Event_ExitTeleporter player %d while not being teleported\n", entityNumber );
  6659. return;
  6660. }
  6661. pushVel = exitEnt->spawnArgs.GetFloat( "push", "300" );
  6662. if ( gameLocal.isServer ) {
  6663. ServerSendEvent( EVENT_EXIT_TELEPORTER, NULL, false, -1 );
  6664. }
  6665. SetPrivateCameraView( NULL );
  6666. // setup origin and push according to the exit target
  6667. SetOrigin( exitEnt->GetPhysics()->GetOrigin() + idVec3( 0, 0, CM_CLIP_EPSILON ) );
  6668. SetViewAngles( exitEnt->GetPhysics()->GetAxis().ToAngles() );
  6669. physicsObj.SetLinearVelocity( exitEnt->GetPhysics()->GetAxis()[ 0 ] * pushVel );
  6670. physicsObj.ClearPushedVelocity();
  6671. // teleport fx
  6672. playerView.Flash( colorWhite, 120 );
  6673. // clear the ik heights so model doesn't appear in the wrong place
  6674. walkIK.EnableAll();
  6675. UpdateVisuals();
  6676. StartSound( "snd_teleport_exit", SND_CHANNEL_ANY, 0, false, NULL );
  6677. if ( teleportKiller != -1 ) {
  6678. // we got killed while being teleported
  6679. Damage( gameLocal.entities[ teleportKiller ], gameLocal.entities[ teleportKiller ], vec3_origin, "damage_telefrag", 1.0f, INVALID_JOINT );
  6680. teleportKiller = -1;
  6681. } else {
  6682. // kill anything that would have waited at teleport exit
  6683. gameLocal.KillBox( this );
  6684. }
  6685. teleportEntity = NULL;
  6686. }
  6687. /*
  6688. ================
  6689. idPlayer::ClientPredictionThink
  6690. ================
  6691. */
  6692. void idPlayer::ClientPredictionThink( void ) {
  6693. renderEntity_t *headRenderEnt;
  6694. oldFlags = usercmd.flags;
  6695. oldButtons = usercmd.buttons;
  6696. usercmd = gameLocal.usercmds[ entityNumber ];
  6697. if ( entityNumber != gameLocal.localClientNum ) {
  6698. // ignore attack button of other clients. that's no good for predictions
  6699. usercmd.buttons &= ~BUTTON_ATTACK;
  6700. }
  6701. buttonMask &= usercmd.buttons;
  6702. usercmd.buttons &= ~buttonMask;
  6703. if ( objectiveSystemOpen ) {
  6704. usercmd.forwardmove = 0;
  6705. usercmd.rightmove = 0;
  6706. usercmd.upmove = 0;
  6707. }
  6708. // clear the ik before we do anything else so the skeleton doesn't get updated twice
  6709. walkIK.ClearJointMods();
  6710. if ( gameLocal.isNewFrame ) {
  6711. if ( ( usercmd.flags & UCF_IMPULSE_SEQUENCE ) != ( oldFlags & UCF_IMPULSE_SEQUENCE ) ) {
  6712. PerformImpulse( usercmd.impulse );
  6713. }
  6714. }
  6715. scoreBoardOpen = ( ( usercmd.buttons & BUTTON_SCORES ) != 0 || forceScoreBoard );
  6716. AdjustSpeed();
  6717. UpdateViewAngles();
  6718. // update the smoothed view angles
  6719. if ( gameLocal.framenum >= smoothedFrame && entityNumber != gameLocal.localClientNum ) {
  6720. idAngles anglesDiff = viewAngles - smoothedAngles;
  6721. anglesDiff.Normalize180();
  6722. if ( idMath::Fabs( anglesDiff.yaw ) < 90.0f && idMath::Fabs( anglesDiff.pitch ) < 90.0f ) {
  6723. // smoothen by pushing back to the previous angles
  6724. viewAngles -= gameLocal.clientSmoothing * anglesDiff;
  6725. viewAngles.Normalize180();
  6726. }
  6727. smoothedAngles = viewAngles;
  6728. }
  6729. smoothedOriginUpdated = false;
  6730. if ( !af.IsActive() ) {
  6731. AdjustBodyAngles();
  6732. }
  6733. if ( !isLagged ) {
  6734. // don't allow client to move when lagged
  6735. Move();
  6736. }
  6737. // update GUIs, Items, and character interactions
  6738. UpdateFocus();
  6739. // service animations
  6740. if ( !spectating && !af.IsActive() ) {
  6741. UpdateConditions();
  6742. UpdateAnimState();
  6743. CheckBlink();
  6744. }
  6745. // clear out our pain flag so we can tell if we recieve any damage between now and the next time we think
  6746. AI_PAIN = false;
  6747. // calculate the exact bobbed view position, which is used to
  6748. // position the view weapon, among other things
  6749. CalculateFirstPersonView();
  6750. // this may use firstPersonView, or a thirdPerson / camera view
  6751. CalculateRenderView();
  6752. if ( !gameLocal.inCinematic && weapon.GetEntity() && ( health > 0 ) && !( gameLocal.isMultiplayer && spectating ) ) {
  6753. UpdateWeapon();
  6754. }
  6755. UpdateHud();
  6756. if ( gameLocal.isNewFrame ) {
  6757. UpdatePowerUps();
  6758. }
  6759. UpdateDeathSkin( false );
  6760. if ( head.GetEntity() ) {
  6761. headRenderEnt = head.GetEntity()->GetRenderEntity();
  6762. } else {
  6763. headRenderEnt = NULL;
  6764. }
  6765. if ( headRenderEnt ) {
  6766. if ( influenceSkin ) {
  6767. headRenderEnt->customSkin = influenceSkin;
  6768. } else {
  6769. headRenderEnt->customSkin = NULL;
  6770. }
  6771. }
  6772. if ( gameLocal.isMultiplayer || g_showPlayerShadow.GetBool() ) {
  6773. renderEntity.suppressShadowInViewID = 0;
  6774. if ( headRenderEnt ) {
  6775. headRenderEnt->suppressShadowInViewID = 0;
  6776. }
  6777. } else {
  6778. renderEntity.suppressShadowInViewID = entityNumber+1;
  6779. if ( headRenderEnt ) {
  6780. headRenderEnt->suppressShadowInViewID = entityNumber+1;
  6781. }
  6782. }
  6783. // never cast shadows from our first-person muzzle flashes
  6784. renderEntity.suppressShadowInLightID = LIGHTID_VIEW_MUZZLE_FLASH + entityNumber;
  6785. if ( headRenderEnt ) {
  6786. headRenderEnt->suppressShadowInLightID = LIGHTID_VIEW_MUZZLE_FLASH + entityNumber;
  6787. }
  6788. if ( !gameLocal.inCinematic ) {
  6789. UpdateAnimation();
  6790. }
  6791. if ( gameLocal.isMultiplayer ) {
  6792. DrawPlayerIcons();
  6793. }
  6794. Present();
  6795. UpdateDamageEffects();
  6796. LinkCombat();
  6797. if ( gameLocal.isNewFrame && entityNumber == gameLocal.localClientNum ) {
  6798. playerView.CalculateShake();
  6799. }
  6800. }
  6801. /*
  6802. ================
  6803. idPlayer::GetPhysicsToVisualTransform
  6804. ================
  6805. */
  6806. bool idPlayer::GetPhysicsToVisualTransform( idVec3 &origin, idMat3 &axis ) {
  6807. if ( af.IsActive() ) {
  6808. af.GetPhysicsToVisualTransform( origin, axis );
  6809. return true;
  6810. }
  6811. // smoothen the rendered origin and angles of other clients
  6812. // smooth self origin if snapshots are telling us prediction is off
  6813. if ( gameLocal.isClient && gameLocal.framenum >= smoothedFrame && ( entityNumber != gameLocal.localClientNum || selfSmooth ) ) {
  6814. // render origin and axis
  6815. idMat3 renderAxis = viewAxis * GetPhysics()->GetAxis();
  6816. idVec3 renderOrigin = GetPhysics()->GetOrigin() + modelOffset * renderAxis;
  6817. // update the smoothed origin
  6818. if ( !smoothedOriginUpdated ) {
  6819. idVec2 originDiff = renderOrigin.ToVec2() - smoothedOrigin.ToVec2();
  6820. if ( originDiff.LengthSqr() < Square( 100.0f ) ) {
  6821. // smoothen by pushing back to the previous position
  6822. if ( selfSmooth ) {
  6823. assert( entityNumber == gameLocal.localClientNum );
  6824. renderOrigin.ToVec2() -= net_clientSelfSmoothing.GetFloat() * originDiff;
  6825. } else {
  6826. renderOrigin.ToVec2() -= gameLocal.clientSmoothing * originDiff;
  6827. }
  6828. }
  6829. smoothedOrigin = renderOrigin;
  6830. smoothedFrame = gameLocal.framenum;
  6831. smoothedOriginUpdated = true;
  6832. }
  6833. axis = idAngles( 0.0f, smoothedAngles.yaw, 0.0f ).ToMat3();
  6834. origin = ( smoothedOrigin - GetPhysics()->GetOrigin() ) * axis.Transpose();
  6835. } else {
  6836. axis = viewAxis;
  6837. origin = modelOffset;
  6838. }
  6839. return true;
  6840. }
  6841. /*
  6842. ================
  6843. idPlayer::GetPhysicsToSoundTransform
  6844. ================
  6845. */
  6846. bool idPlayer::GetPhysicsToSoundTransform( idVec3 &origin, idMat3 &axis ) {
  6847. idCamera *camera;
  6848. if ( privateCameraView ) {
  6849. camera = privateCameraView;
  6850. } else {
  6851. camera = gameLocal.GetCamera();
  6852. }
  6853. if ( camera ) {
  6854. renderView_t view;
  6855. memset( &view, 0, sizeof( view ) );
  6856. camera->GetViewParms( &view );
  6857. origin = view.vieworg;
  6858. axis = view.viewaxis;
  6859. return true;
  6860. } else {
  6861. return idActor::GetPhysicsToSoundTransform( origin, axis );
  6862. }
  6863. }
  6864. /*
  6865. ================
  6866. idPlayer::WriteToSnapshot
  6867. ================
  6868. */
  6869. void idPlayer::WriteToSnapshot( idBitMsgDelta &msg ) const {
  6870. physicsObj.WriteToSnapshot( msg );
  6871. WriteBindToSnapshot( msg );
  6872. msg.WriteDeltaFloat( 0.0f, deltaViewAngles[0] );
  6873. msg.WriteDeltaFloat( 0.0f, deltaViewAngles[1] );
  6874. msg.WriteDeltaFloat( 0.0f, deltaViewAngles[2] );
  6875. msg.WriteShort( health );
  6876. msg.WriteBits( gameLocal.ServerRemapDecl( -1, DECL_ENTITYDEF, lastDamageDef ), gameLocal.entityDefBits );
  6877. msg.WriteDir( lastDamageDir, 9 );
  6878. msg.WriteShort( lastDamageLocation );
  6879. msg.WriteBits( idealWeapon, idMath::BitsForInteger( MAX_WEAPONS ) );
  6880. msg.WriteBits( inventory.weapons, MAX_WEAPONS );
  6881. msg.WriteBits( weapon.GetSpawnId(), 32 );
  6882. msg.WriteBits( spectator, idMath::BitsForInteger( MAX_CLIENTS ) );
  6883. msg.WriteBits( lastHitToggle, 1 );
  6884. msg.WriteBits( weaponGone, 1 );
  6885. msg.WriteBits( isLagged, 1 );
  6886. msg.WriteBits( isChatting, 1 );
  6887. }
  6888. /*
  6889. ================
  6890. idPlayer::ReadFromSnapshot
  6891. ================
  6892. */
  6893. void idPlayer::ReadFromSnapshot( const idBitMsgDelta &msg ) {
  6894. int i, oldHealth, newIdealWeapon, weaponSpawnId;
  6895. bool newHitToggle, stateHitch;
  6896. if ( snapshotSequence - lastSnapshotSequence > 1 ) {
  6897. stateHitch = true;
  6898. } else {
  6899. stateHitch = false;
  6900. }
  6901. lastSnapshotSequence = snapshotSequence;
  6902. oldHealth = health;
  6903. physicsObj.ReadFromSnapshot( msg );
  6904. ReadBindFromSnapshot( msg );
  6905. deltaViewAngles[0] = msg.ReadDeltaFloat( 0.0f );
  6906. deltaViewAngles[1] = msg.ReadDeltaFloat( 0.0f );
  6907. deltaViewAngles[2] = msg.ReadDeltaFloat( 0.0f );
  6908. health = msg.ReadShort();
  6909. lastDamageDef = gameLocal.ClientRemapDecl( DECL_ENTITYDEF, msg.ReadBits( gameLocal.entityDefBits ) );
  6910. lastDamageDir = msg.ReadDir( 9 );
  6911. lastDamageLocation = msg.ReadShort();
  6912. newIdealWeapon = msg.ReadBits( idMath::BitsForInteger( MAX_WEAPONS ) );
  6913. inventory.weapons = msg.ReadBits( MAX_WEAPONS );
  6914. weaponSpawnId = msg.ReadBits( 32 );
  6915. spectator = msg.ReadBits( idMath::BitsForInteger( MAX_CLIENTS ) );
  6916. newHitToggle = msg.ReadBits( 1 ) != 0;
  6917. weaponGone = msg.ReadBits( 1 ) != 0;
  6918. isLagged = msg.ReadBits( 1 ) != 0;
  6919. isChatting = msg.ReadBits( 1 ) != 0;
  6920. // no msg reading below this
  6921. if ( weapon.SetSpawnId( weaponSpawnId ) ) {
  6922. if ( weapon.GetEntity() ) {
  6923. // maintain ownership locally
  6924. weapon.GetEntity()->SetOwner( this );
  6925. }
  6926. currentWeapon = -1;
  6927. }
  6928. // if not a local client assume the client has all ammo types
  6929. if ( entityNumber != gameLocal.localClientNum ) {
  6930. for( i = 0; i < AMMO_NUMTYPES; i++ ) {
  6931. inventory.ammo[ i ] = 999;
  6932. }
  6933. }
  6934. if ( oldHealth > 0 && health <= 0 ) {
  6935. if ( stateHitch ) {
  6936. // so we just hide and don't show a death skin
  6937. UpdateDeathSkin( true );
  6938. }
  6939. // die
  6940. AI_DEAD = true;
  6941. ClearPowerUps();
  6942. SetAnimState( ANIMCHANNEL_LEGS, "Legs_Death", 4 );
  6943. SetAnimState( ANIMCHANNEL_TORSO, "Torso_Death", 4 );
  6944. SetWaitState( "" );
  6945. animator.ClearAllJoints();
  6946. if ( entityNumber == gameLocal.localClientNum ) {
  6947. playerView.Fade( colorBlack, 12000 );
  6948. }
  6949. StartRagdoll();
  6950. physicsObj.SetMovementType( PM_DEAD );
  6951. if ( !stateHitch ) {
  6952. StartSound( "snd_death", SND_CHANNEL_VOICE, 0, false, NULL );
  6953. }
  6954. if ( weapon.GetEntity() ) {
  6955. weapon.GetEntity()->OwnerDied();
  6956. }
  6957. } else if ( oldHealth <= 0 && health > 0 ) {
  6958. // respawn
  6959. Init();
  6960. StopRagdoll();
  6961. SetPhysics( &physicsObj );
  6962. physicsObj.EnableClip();
  6963. SetCombatContents( true );
  6964. } else if ( health < oldHealth && health > 0 ) {
  6965. if ( stateHitch ) {
  6966. lastDmgTime = gameLocal.time;
  6967. } else {
  6968. // damage feedback
  6969. const idDeclEntityDef *def = static_cast<const idDeclEntityDef *>( declManager->DeclByIndex( DECL_ENTITYDEF, lastDamageDef, false ) );
  6970. if ( def ) {
  6971. playerView.DamageImpulse( lastDamageDir * viewAxis.Transpose(), &def->dict );
  6972. AI_PAIN = Pain( NULL, NULL, oldHealth - health, lastDamageDir, lastDamageLocation );
  6973. lastDmgTime = gameLocal.time;
  6974. } else {
  6975. common->Warning( "NET: no damage def for damage feedback '%d'\n", lastDamageDef );
  6976. }
  6977. }
  6978. } else if ( health > oldHealth && PowerUpActive( MEGAHEALTH ) && !stateHitch ) {
  6979. // just pulse, for any health raise
  6980. healthPulse = true;
  6981. }
  6982. // If the player is alive, restore proper physics object
  6983. if ( health > 0 && IsActiveAF() ) {
  6984. StopRagdoll();
  6985. SetPhysics( &physicsObj );
  6986. physicsObj.EnableClip();
  6987. SetCombatContents( true );
  6988. }
  6989. if ( idealWeapon != newIdealWeapon ) {
  6990. if ( stateHitch ) {
  6991. weaponCatchup = true;
  6992. }
  6993. idealWeapon = newIdealWeapon;
  6994. UpdateHudWeapon();
  6995. }
  6996. if ( lastHitToggle != newHitToggle ) {
  6997. SetLastHitTime( gameLocal.realClientTime );
  6998. }
  6999. if ( msg.HasChanged() ) {
  7000. UpdateVisuals();
  7001. }
  7002. }
  7003. /*
  7004. ================
  7005. idPlayer::WritePlayerStateToSnapshot
  7006. ================
  7007. */
  7008. void idPlayer::WritePlayerStateToSnapshot( idBitMsgDelta &msg ) const {
  7009. int i;
  7010. msg.WriteByte( bobCycle );
  7011. msg.WriteLong( stepUpTime );
  7012. msg.WriteFloat( stepUpDelta );
  7013. msg.WriteShort( inventory.weapons );
  7014. msg.WriteByte( inventory.armor );
  7015. for( i = 0; i < AMMO_NUMTYPES; i++ ) {
  7016. msg.WriteBits( inventory.ammo[i], ASYNC_PLAYER_INV_AMMO_BITS );
  7017. }
  7018. for( i = 0; i < MAX_WEAPONS; i++ ) {
  7019. msg.WriteBits( inventory.clip[i], ASYNC_PLAYER_INV_CLIP_BITS );
  7020. }
  7021. }
  7022. /*
  7023. ================
  7024. idPlayer::ReadPlayerStateFromSnapshot
  7025. ================
  7026. */
  7027. void idPlayer::ReadPlayerStateFromSnapshot( const idBitMsgDelta &msg ) {
  7028. int i, ammo;
  7029. bobCycle = msg.ReadByte();
  7030. stepUpTime = msg.ReadLong();
  7031. stepUpDelta = msg.ReadFloat();
  7032. inventory.weapons = msg.ReadShort();
  7033. inventory.armor = msg.ReadByte();
  7034. for( i = 0; i < AMMO_NUMTYPES; i++ ) {
  7035. ammo = msg.ReadBits( ASYNC_PLAYER_INV_AMMO_BITS );
  7036. if ( gameLocal.time >= inventory.ammoPredictTime ) {
  7037. inventory.ammo[ i ] = ammo;
  7038. }
  7039. }
  7040. for( i = 0; i < MAX_WEAPONS; i++ ) {
  7041. inventory.clip[i] = msg.ReadBits( ASYNC_PLAYER_INV_CLIP_BITS );
  7042. }
  7043. }
  7044. /*
  7045. ================
  7046. idPlayer::ServerReceiveEvent
  7047. ================
  7048. */
  7049. bool idPlayer::ServerReceiveEvent( int event, int time, const idBitMsg &msg ) {
  7050. if ( idEntity::ServerReceiveEvent( event, time, msg ) ) {
  7051. return true;
  7052. }
  7053. // client->server events
  7054. switch( event ) {
  7055. case EVENT_IMPULSE: {
  7056. PerformImpulse( msg.ReadBits( 6 ) );
  7057. return true;
  7058. }
  7059. default: {
  7060. return false;
  7061. }
  7062. }
  7063. }
  7064. /*
  7065. ================
  7066. idPlayer::ClientReceiveEvent
  7067. ================
  7068. */
  7069. bool idPlayer::ClientReceiveEvent( int event, int time, const idBitMsg &msg ) {
  7070. int powerup;
  7071. bool start;
  7072. switch ( event ) {
  7073. case EVENT_EXIT_TELEPORTER:
  7074. Event_ExitTeleporter();
  7075. return true;
  7076. case EVENT_ABORT_TELEPORTER:
  7077. SetPrivateCameraView( NULL );
  7078. return true;
  7079. case EVENT_POWERUP: {
  7080. powerup = msg.ReadShort();
  7081. start = msg.ReadBits( 1 ) != 0;
  7082. if ( start ) {
  7083. GivePowerUp( powerup, 0 );
  7084. } else {
  7085. ClearPowerup( powerup );
  7086. }
  7087. return true;
  7088. }
  7089. case EVENT_SPECTATE: {
  7090. bool spectate = ( msg.ReadBits( 1 ) != 0 );
  7091. Spectate( spectate );
  7092. return true;
  7093. }
  7094. case EVENT_ADD_DAMAGE_EFFECT: {
  7095. if ( spectating ) {
  7096. // if we're spectating, ignore
  7097. // happens if the event and the spectate change are written on the server during the same frame (fraglimit)
  7098. return true;
  7099. }
  7100. return idActor::ClientReceiveEvent( event, time, msg );
  7101. }
  7102. default: {
  7103. return idActor::ClientReceiveEvent( event, time, msg );
  7104. }
  7105. }
  7106. return false;
  7107. }
  7108. /*
  7109. ================
  7110. idPlayer::Hide
  7111. ================
  7112. */
  7113. void idPlayer::Hide( void ) {
  7114. idWeapon *weap;
  7115. idActor::Hide();
  7116. weap = weapon.GetEntity();
  7117. if ( weap ) {
  7118. weap->HideWorldModel();
  7119. }
  7120. }
  7121. /*
  7122. ================
  7123. idPlayer::Show
  7124. ================
  7125. */
  7126. void idPlayer::Show( void ) {
  7127. idWeapon *weap;
  7128. idActor::Show();
  7129. weap = weapon.GetEntity();
  7130. if ( weap ) {
  7131. weap->ShowWorldModel();
  7132. }
  7133. }
  7134. /*
  7135. ===============
  7136. idPlayer::StartAudioLog
  7137. ===============
  7138. */
  7139. void idPlayer::StartAudioLog( void ) {
  7140. if ( hud ) {
  7141. hud->HandleNamedEvent( "audioLogUp" );
  7142. }
  7143. }
  7144. /*
  7145. ===============
  7146. idPlayer::StopAudioLog
  7147. ===============
  7148. */
  7149. void idPlayer::StopAudioLog( void ) {
  7150. if ( hud ) {
  7151. hud->HandleNamedEvent( "audioLogDown" );
  7152. }
  7153. }
  7154. /*
  7155. ===============
  7156. idPlayer::ShowTip
  7157. ===============
  7158. */
  7159. void idPlayer::ShowTip( const char *title, const char *tip, bool autoHide ) {
  7160. if ( tipUp ) {
  7161. return;
  7162. }
  7163. hud->SetStateString( "tip", tip );
  7164. hud->SetStateString( "tiptitle", title );
  7165. hud->HandleNamedEvent( "tipWindowUp" );
  7166. if ( autoHide ) {
  7167. PostEventSec( &EV_Player_HideTip, 5.0f );
  7168. }
  7169. tipUp = true;
  7170. }
  7171. /*
  7172. ===============
  7173. idPlayer::HideTip
  7174. ===============
  7175. */
  7176. void idPlayer::HideTip( void ) {
  7177. hud->HandleNamedEvent( "tipWindowDown" );
  7178. tipUp = false;
  7179. }
  7180. /*
  7181. ===============
  7182. idPlayer::Event_HideTip
  7183. ===============
  7184. */
  7185. void idPlayer::Event_HideTip( void ) {
  7186. HideTip();
  7187. }
  7188. /*
  7189. ===============
  7190. idPlayer::ShowObjective
  7191. ===============
  7192. */
  7193. void idPlayer::ShowObjective( const char *obj ) {
  7194. hud->HandleNamedEvent( obj );
  7195. objectiveUp = true;
  7196. }
  7197. /*
  7198. ===============
  7199. idPlayer::HideObjective
  7200. ===============
  7201. */
  7202. void idPlayer::HideObjective( void ) {
  7203. hud->HandleNamedEvent( "closeObjective" );
  7204. objectiveUp = false;
  7205. }
  7206. /*
  7207. ===============
  7208. idPlayer::Event_StopAudioLog
  7209. ===============
  7210. */
  7211. void idPlayer::Event_StopAudioLog( void ) {
  7212. StopAudioLog();
  7213. }
  7214. /*
  7215. ===============
  7216. idPlayer::SetSpectateOrigin
  7217. ===============
  7218. */
  7219. void idPlayer::SetSpectateOrigin( void ) {
  7220. idVec3 neworig;
  7221. neworig = GetPhysics()->GetOrigin();
  7222. neworig[ 2 ] += EyeHeight();
  7223. neworig[ 2 ] += 25;
  7224. SetOrigin( neworig );
  7225. }
  7226. /*
  7227. ===============
  7228. idPlayer::RemoveWeapon
  7229. ===============
  7230. */
  7231. void idPlayer::RemoveWeapon( const char *weap ) {
  7232. if ( weap && *weap ) {
  7233. inventory.Drop( spawnArgs, spawnArgs.GetString( weap ), -1 );
  7234. }
  7235. }
  7236. /*
  7237. ===============
  7238. idPlayer::CanShowWeaponViewmodel
  7239. ===============
  7240. */
  7241. bool idPlayer::CanShowWeaponViewmodel( void ) const {
  7242. return showWeaponViewModel;
  7243. }
  7244. /*
  7245. ===============
  7246. idPlayer::SetLevelTrigger
  7247. ===============
  7248. */
  7249. void idPlayer::SetLevelTrigger( const char *levelName, const char *triggerName ) {
  7250. if ( levelName && *levelName && triggerName && *triggerName ) {
  7251. idLevelTriggerInfo lti;
  7252. lti.levelName = levelName;
  7253. lti.triggerName = triggerName;
  7254. inventory.levelTriggers.Append( lti );
  7255. }
  7256. }
  7257. /*
  7258. ===============
  7259. idPlayer::Event_LevelTrigger
  7260. ===============
  7261. */
  7262. void idPlayer::Event_LevelTrigger( void ) {
  7263. idStr mapName = gameLocal.GetMapName();
  7264. mapName.StripPath();
  7265. mapName.StripFileExtension();
  7266. for ( int i = inventory.levelTriggers.Num() - 1; i >= 0; i-- ) {
  7267. if ( idStr::Icmp( mapName, inventory.levelTriggers[i].levelName) == 0 ){
  7268. idEntity *ent = gameLocal.FindEntity( inventory.levelTriggers[i].triggerName );
  7269. if ( ent ) {
  7270. ent->PostEventMS( &EV_Activate, 1, this );
  7271. }
  7272. }
  7273. }
  7274. }
  7275. /*
  7276. ===============
  7277. idPlayer::Event_Gibbed
  7278. ===============
  7279. */
  7280. void idPlayer::Event_Gibbed( void ) {
  7281. }
  7282. /*
  7283. ==================
  7284. idPlayer::Event_GetIdealWeapon
  7285. ==================
  7286. */
  7287. void idPlayer::Event_GetIdealWeapon( void ) {
  7288. const char *weapon;
  7289. if ( idealWeapon >= 0 ) {
  7290. weapon = spawnArgs.GetString( va( "def_weapon%d", idealWeapon ) );
  7291. idThread::ReturnString( weapon );
  7292. } else {
  7293. idThread::ReturnString( "" );
  7294. }
  7295. }
  7296. /*
  7297. ===============
  7298. idPlayer::UpdatePlayerIcons
  7299. ===============
  7300. */
  7301. void idPlayer::UpdatePlayerIcons( void ) {
  7302. int time = networkSystem->ServerGetClientTimeSinceLastPacket( entityNumber );
  7303. if ( time > cvarSystem->GetCVarInteger( "net_clientMaxPrediction" ) ) {
  7304. isLagged = true;
  7305. } else {
  7306. isLagged = false;
  7307. }
  7308. }
  7309. /*
  7310. ===============
  7311. idPlayer::DrawPlayerIcons
  7312. ===============
  7313. */
  7314. void idPlayer::DrawPlayerIcons( void ) {
  7315. if ( !NeedsIcon() ) {
  7316. playerIcon.FreeIcon();
  7317. return;
  7318. }
  7319. playerIcon.Draw( this, headJoint );
  7320. }
  7321. /*
  7322. ===============
  7323. idPlayer::HidePlayerIcons
  7324. ===============
  7325. */
  7326. void idPlayer::HidePlayerIcons( void ) {
  7327. playerIcon.FreeIcon();
  7328. }
  7329. /*
  7330. ===============
  7331. idPlayer::NeedsIcon
  7332. ==============
  7333. */
  7334. bool idPlayer::NeedsIcon( void ) {
  7335. // local clients don't render their own icons... they're only info for other clients
  7336. return entityNumber != gameLocal.localClientNum && ( isLagged || isChatting );
  7337. }