Player.cpp 258 KB

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