123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805 |
- // leave this line at the top for all g_xxxx.cpp files...
- #include "g_headers.h"
- #include "g_local.h"
- #include "g_functions.h"
- #include "..\cgame\cg_local.h"
- #include "Q3_Interface.h"
- #include "wp_saber.h"
- #include "g_vehicles.h"
- #ifdef _DEBUG
- #include <float.h>
- #endif //_DEBUG
- #define SLOWDOWN_DIST 128.0f
- #define MIN_NPC_SPEED 16.0f
- #ifdef _XBOX
- int g_lastFireTime = 0;
- #endif
- extern void VehicleExplosionDelay( gentity_t *self );
- extern qboolean Q3_TaskIDPending( gentity_t *ent, taskID_t taskType );
- extern void G_MaintainFormations(gentity_t *self);
- extern void BG_CalculateOffsetAngles( gentity_t *ent, usercmd_t *ucmd );//in bg_pangles.cpp
- extern void TryUse( gentity_t *ent );
- extern void ChangeWeapon( gentity_t *ent, int newWeapon );
- extern void ScoreBoardReset(void);
- extern void WP_SaberReflectCheck( gentity_t *self, usercmd_t *ucmd );
- extern void WP_SaberUpdate( gentity_t *self, usercmd_t *ucmd );
- extern void WP_SaberStartMissileBlockCheck( gentity_t *self, usercmd_t *ucmd );
- extern void WP_ForcePowersUpdate( gentity_t *self, usercmd_t *ucmd );
- extern gentity_t *SeekerAcquiresTarget ( gentity_t *ent, vec3_t pos );
- extern void FireSeeker( gentity_t *owner, gentity_t *target, vec3_t origin, vec3_t dir );
- extern qboolean InFront( vec3_t spot, vec3_t from, vec3_t fromAngles, float threshHold = 0.0f );
- extern float DotToSpot( vec3_t spot, vec3_t from, vec3_t fromAngles );
- extern void NPC_SetLookTarget( gentity_t *self, int entNum, int clearTime );
- extern qboolean PM_LockAngles( gentity_t *ent, usercmd_t *ucmd );
- extern qboolean PM_AdjustAnglesToGripper( gentity_t *gent, usercmd_t *cmd );
- extern qboolean PM_AdjustAnglesToPuller( gentity_t *ent, gentity_t *puller, usercmd_t *ucmd, qboolean faceAway );
- extern qboolean PM_AdjustAngleForWallRun( gentity_t *ent, usercmd_t *ucmd, qboolean doMove );
- extern qboolean PM_AdjustAngleForWallRunUp( gentity_t *ent, usercmd_t *ucmd, qboolean doMove );
- extern qboolean PM_AdjustAnglesForSpinningFlip( gentity_t *ent, usercmd_t *ucmd, qboolean anglesOnly );
- extern qboolean PM_AdjustAnglesForBackAttack( gentity_t *ent, usercmd_t *ucmd );
- extern qboolean PM_AdjustAnglesForSaberLock( gentity_t *ent, usercmd_t *ucmd );
- extern qboolean PM_AdjustAnglesForKnockdown( gentity_t *ent, usercmd_t *ucmd, qboolean angleClampOnly );
- extern qboolean PM_AdjustAnglesForDualJumpAttack( gentity_t *ent, usercmd_t *ucmd );
- extern qboolean PM_AdjustAnglesForLongJump( gentity_t *ent, usercmd_t *ucmd );
- extern qboolean PM_AdjustAnglesForGrapple( gentity_t *ent, usercmd_t *ucmd );
- extern qboolean PM_AdjustAngleForWallJump( gentity_t *ent, usercmd_t *ucmd, qboolean doMove );
- extern qboolean PM_AdjustAnglesForBFKick( gentity_t *ent, usercmd_t *ucmd, vec3_t fwdAngs, qboolean aimFront );
- extern qboolean PM_AdjustAnglesForStabDown( gentity_t *ent, usercmd_t *ucmd );
- extern qboolean PM_AdjustAnglesForSpinProtect( gentity_t *ent, usercmd_t *ucmd );
- extern qboolean PM_AdjustAnglesForWallRunUpFlipAlt( gentity_t *ent, usercmd_t *ucmd );
- extern qboolean PM_AdjustAnglesForHeldByMonster( gentity_t *ent, gentity_t *monster, usercmd_t *ucmd );
- extern qboolean PM_HasAnimation( gentity_t *ent, int animation );
- extern qboolean PM_LeapingSaberAnim( int anim );
- extern qboolean PM_SpinningSaberAnim( int anim );
- extern qboolean PM_SaberInAttack( int move );
- extern qboolean PM_KickingAnim( int anim );
- extern int PM_AnimLength( int index, animNumber_t anim );
- extern qboolean PM_InKnockDown( playerState_t *ps );
- extern qboolean PM_InGetUp( playerState_t *ps );
- extern qboolean PM_InRoll( playerState_t *ps );
- extern void PM_CmdForRoll( playerState_t *ps, usercmd_t *pCmd );
- extern qboolean PM_InAttackRoll( int anim );
- extern qboolean PM_CrouchAnim( int anim );
- extern qboolean PM_FlippingAnim( int anim );
- extern qboolean PM_InCartwheel( int anim );
- extern qboolean PM_StandingAnim( int anim );
- extern qboolean PM_InForceGetUp( playerState_t *ps );
- extern qboolean PM_GetupAnimNoMove( int legsAnim );
- extern qboolean PM_SuperBreakLoseAnim( int anim );
- extern qboolean PM_SuperBreakWinAnim( int anim );
- extern qboolean PM_CanRollFromSoulCal( playerState_t *ps );
- extern qboolean BG_FullBodyTauntAnim( int anim );
- extern qboolean FlyingCreature( gentity_t *ent );
- extern Vehicle_t *G_IsRidingVehicle( gentity_t *ent );
- extern void G_AttachToVehicle( gentity_t *ent, usercmd_t **ucmd );
- extern void G_GetBoltPosition( gentity_t *self, int boltIndex, vec3_t pos, int modelIndex = 0 );
- extern void G_UpdateEmplacedWeaponData( gentity_t *ent );
- extern void RunEmplacedWeapon( gentity_t *ent, usercmd_t **ucmd );
- extern qboolean G_PointInBounds( const vec3_t point, const vec3_t mins, const vec3_t maxs );
- extern void NPC_SetPainEvent( gentity_t *self );
- extern qboolean G_HasKnockdownAnims( gentity_t *ent );
- extern int G_GetEntsNearBolt( gentity_t *self, gentity_t **radiusEnts, float radius, int boltIndex, vec3_t boltOrg );
- extern qboolean PM_InOnGroundAnim ( playerState_t *ps );
- extern qboolean PM_LockedAnim( int anim );
- extern qboolean WP_SabersCheckLock2( gentity_t *attacker, gentity_t *defender, sabersLockMode_t lockMode );
- extern qboolean G_JediInNormalAI( gentity_t *ent );
- extern bool in_camera;
- extern qboolean player_locked;
- extern qboolean stop_icarus;
- extern qboolean MatrixMode;
- extern cvar_t *g_spskill;
- extern cvar_t *g_timescale;
- extern cvar_t *g_saberMoveSpeed;
- extern cvar_t *g_saberAutoBlocking;
- extern cvar_t *g_speederControlScheme;
- extern cvar_t *d_slowmodeath;
- extern cvar_t *g_debugMelee;
- extern vmCvar_t cg_thirdPersonAlpha;
- extern vmCvar_t cg_thirdPersonAutoAlpha;
- void ClientEndPowerUps( gentity_t *ent );
- int G_FindLookItem( gentity_t *self )
- {
- //FIXME: should be a more intelligent way of doing this, like auto aim?
- //closest, most in front... did damage to... took damage from? How do we know who the player is focusing on?
- gentity_t *ent;
- int bestEntNum = ENTITYNUM_NONE;
- gentity_t *entityList[MAX_GENTITIES];
- int numListedEntities;
- vec3_t center, mins, maxs, fwdangles, forward, dir;
- int i, e;
- float radius = 256;
- float rating, bestRating = 0.0f;
- //FIXME: no need to do this in 1st person?
- fwdangles[1] = self->client->ps.viewangles[1];
- AngleVectors( fwdangles, forward, NULL, NULL );
- VectorCopy( self->currentOrigin, center );
- for ( i = 0 ; i < 3 ; i++ )
- {
- mins[i] = center[i] - radius;
- maxs[i] = center[i] + radius;
- }
- numListedEntities = gi.EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES );
-
- if ( !numListedEntities )
- {
- return ENTITYNUM_NONE;
- }
- for ( e = 0 ; e < numListedEntities ; e++ )
- {
- ent = entityList[ e ];
- if ( !ent->item )
- {
- continue;
- }
- if ( ent->s.eFlags&EF_NODRAW )
- {
- continue;
- }
- if ( (ent->spawnflags&4/*ITMSF_MONSTER*/) )
- {//NPCs only
- continue;
- }
- if ( !BG_CanItemBeGrabbed( &ent->s, &self->client->ps ) )
- {//don't need it
- continue;
- }
- if ( !gi.inPVS( self->currentOrigin, ent->currentOrigin ) )
- {//not even potentially visible
- continue;
- }
- if ( !G_ClearLOS( self, self->client->renderInfo.eyePoint, ent ) )
- {//can't see him
- continue;
- }
- //rate him based on how close & how in front he is
- VectorSubtract( ent->currentOrigin, center, dir );
- rating = (1.0f-(VectorNormalize( dir )/radius));
- rating *= DotProduct( forward, dir );
- if ( ent->item->giType == IT_HOLDABLE && ent->item->giTag == INV_SECURITY_KEY )
- {//security keys are of the highest importance
- rating *= 2.0f;
- }
- if ( rating > bestRating )
- {
- bestEntNum = ent->s.number;
- bestRating = rating;
- }
- }
- return bestEntNum;
- }
- extern void CG_SetClientViewAngles( vec3_t angles, qboolean overrideViewEnt );
- qboolean G_ClearViewEntity( gentity_t *ent )
- {
- if ( !ent->client->ps.viewEntity )
- return qfalse;
- if ( ent->client->ps.viewEntity > 0 && ent->client->ps.viewEntity < ENTITYNUM_NONE )
- {
- if ( &g_entities[ent->client->ps.viewEntity] )
- {
- g_entities[ent->client->ps.viewEntity].svFlags &= ~SVF_BROADCAST;
- if ( g_entities[ent->client->ps.viewEntity].NPC )
- {
- g_entities[ent->client->ps.viewEntity].NPC->controlledTime = 0;
- SetClientViewAngle( &g_entities[ent->client->ps.viewEntity], g_entities[ent->client->ps.viewEntity].currentAngles );
- G_SetAngles( &g_entities[ent->client->ps.viewEntity], g_entities[ent->client->ps.viewEntity].currentAngles );
- VectorCopy( g_entities[ent->client->ps.viewEntity].currentAngles, g_entities[ent->client->ps.viewEntity].NPC->lastPathAngles );
- g_entities[ent->client->ps.viewEntity].NPC->desiredYaw = g_entities[ent->client->ps.viewEntity].currentAngles[YAW];
- }
- }
- CG_SetClientViewAngles( ent->pos4, qtrue );
- SetClientViewAngle( ent, ent->pos4 );
- }
- ent->client->ps.viewEntity = 0;
- return qtrue;
- }
- void G_SetViewEntity( gentity_t *self, gentity_t *viewEntity )
- {
- if ( !self || !self->client || !viewEntity )
- {
- return;
- }
- if ( self->s.number == 0 && cg.zoomMode )
- {
- // yeah, it should really toggle them so it plays the end sound....
- cg.zoomMode = 0;
- }
- if ( viewEntity->s.number == self->client->ps.viewEntity )
- {
- return;
- }
- //clear old one first
- G_ClearViewEntity( self );
- //set new one
- self->client->ps.viewEntity = viewEntity->s.number;
- viewEntity->svFlags |= SVF_BROADCAST;
- //remember current angles
- VectorCopy( self->client->ps.viewangles, self->pos4 );
- if ( viewEntity->client )
- {
- //vec3_t clear = {0,0,0};
- CG_SetClientViewAngles( viewEntity->client->ps.viewangles, qtrue );
- //SetClientViewAngle( self, viewEntity->client->ps.viewangles );
- //SetClientViewAngle( viewEntity, clear );
- /*
- VectorCopy( viewEntity->client->ps.viewangles, self->client->ps.viewangles );
- for ( int i = 0; i < 3; i++ )
- {
- self->client->ps.delta_angles[i] = viewEntity->client->ps.delta_angles[i];
- }
- */
- }
- if ( !self->s.number )
- {
- CG_CenterPrint( "@SP_INGAME_EXIT_VIEW", SCREEN_HEIGHT * 0.87 );
- }
- }
- qboolean G_ControlledByPlayer( gentity_t *self )
- {
- if ( self && self->NPC && self->NPC->controlledTime > level.time )
- {//being controlled
- gentity_t *controller = &g_entities[0];
- if ( controller->client && controller->client->ps.viewEntity == self->s.number )
- {//we're the player's viewEntity
- return qtrue;
- }
- }
- return qfalse;
- }
- qboolean G_ValidateLookEnemy( gentity_t *self, gentity_t *enemy )
- {
- if ( !enemy )
- {
- return qfalse;
- }
- if ( enemy->flags&FL_NOTARGET )
- {
- return qfalse;
- }
- if ( (enemy->s.eFlags&EF_NODRAW) )
- {
- return qfalse;
- }
- if ( !enemy || enemy == self || !enemy->inuse )
- {
- return qfalse;
- }
- if ( !enemy->client || !enemy->NPC )
- {//not valid
- if ( (enemy->svFlags&SVF_NONNPC_ENEMY)
- && enemy->s.weapon == WP_TURRET
- && enemy->noDamageTeam != self->client->playerTeam
- && enemy->health > 0 )
- {//a turret
- //return qtrue;
- }
- else
- {
- return qfalse;
- }
- }
- else
- {
- if ( self->client->playerTeam != TEAM_FREE//evil player hates everybody
- && enemy->client->playerTeam == self->client->playerTeam )
- {//on same team
- return qfalse;
- }
- Vehicle_t *pVeh = G_IsRidingVehicle( self );
- if ( pVeh && pVeh == enemy->m_pVehicle )
- {
- return qfalse;
- }
- if ( enemy->health <= 0 && ((level.time-enemy->s.time) > 3000||!InFront(enemy->currentOrigin,self->currentOrigin,self->client->ps.viewangles,0.2f)||DistanceHorizontal(enemy->currentOrigin,self->currentOrigin)>16384))//>128
- {//corpse, been dead too long or too out of sight to be interesting
- if ( !enemy->message )
- {
- return qfalse;
- }
- }
- }
- if ( (!InFront( enemy->currentOrigin, self->currentOrigin, self->client->ps.viewangles, 0.0f) || !G_ClearLOS( self, self->client->renderInfo.eyePoint, enemy ) )
- && ( DistanceHorizontalSquared( enemy->currentOrigin, self->currentOrigin ) > 65536 || fabs(enemy->currentOrigin[2]-self->currentOrigin[2]) > 384 ) )
- {//(not in front or not clear LOS) & greater than 256 away
- return qfalse;
- }
- //LOS?
- return qtrue;
- }
- void G_ChooseLookEnemy( gentity_t *self, usercmd_t *ucmd )
- {
- //FIXME: should be a more intelligent way of doing this, like auto aim?
- //closest, most in front... did damage to... took damage from? How do we know who the player is focusing on?
- gentity_t *ent, *bestEnt = NULL;
- gentity_t *entityList[MAX_GENTITIES];
- int numListedEntities;
- vec3_t center, mins, maxs, fwdangles, forward, dir;
- int i, e;
- float radius = 256;
- float rating, bestRating = 0.0f;
- //FIXME: no need to do this in 1st person?
- fwdangles[0] = 0; //Must initialize data!
- fwdangles[1] = self->client->ps.viewangles[1];
- fwdangles[2] = 0;
- AngleVectors( fwdangles, forward, NULL, NULL );
- VectorCopy( self->currentOrigin, center );
- for ( i = 0 ; i < 3 ; i++ )
- {
- mins[i] = center[i] - radius;
- maxs[i] = center[i] + radius;
- }
- numListedEntities = gi.EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES );
-
- if ( !numListedEntities )
- {//should we clear the enemy?
- return;
- }
- for ( e = 0 ; e < numListedEntities ; e++ )
- {
- ent = entityList[ e ];
- if ( !gi.inPVS( self->currentOrigin, ent->currentOrigin ) )
- {//not even potentially visible
- continue;
- }
- if ( !G_ValidateLookEnemy( self, ent ) )
- {//doesn't meet criteria of valid look enemy (don't check current since we would have done that before this func's call
- continue;
- }
- if ( !G_ClearLOS( self, self->client->renderInfo.eyePoint, ent ) )
- {//can't see him
- continue;
- }
- //rate him based on how close & how in front he is
- VectorSubtract( ent->currentOrigin, center, dir );
- rating = (1.0f-(VectorNormalize( dir )/radius));
- rating *= DotProduct( forward, dir )+1.0f;
- if ( ent->health <= 0 )
- {
- if ( (ucmd->buttons&BUTTON_ATTACK)
- || (ucmd->buttons&BUTTON_ALT_ATTACK)
- || (ucmd->buttons&BUTTON_FORCE_FOCUS) )
- {//if attacking, don't consider dead enemies
- continue;
- }
- if ( ent->message )
- {//keyholder
- rating *= 0.5f;
- }
- else
- {
- rating *= 0.1f;
- }
- }
- if ( ent->s.weapon == WP_SABER )
- {
- rating *= 2.0f;
- }
- if ( ent->enemy == self )
- {//he's mad at me, he's more important
- rating *= 2.0f;
- }
- else if ( ent->NPC && ent->NPC->blockedSpeechDebounceTime > level.time - 6000 )
- {//he's detected me, he's more important
- if ( ent->NPC->blockedSpeechDebounceTime > level.time + 4000 )
- {
- rating *= 1.5f;
- }
- else
- {//from 1.0f to 1.5f
- rating += rating * ((float)(ent->NPC->blockedSpeechDebounceTime-level.time) + 6000.0f)/20000.0f;
- }
- }
- /*
- if ( g_crosshairEntNum == ent && !self->enemy )
- {//we don't have an enemy and we are aiming at this guy
- rading *= 2.0f;
- }
- */
- if ( rating > bestRating )
- {
- bestEnt = ent;
- bestRating = rating;
- }
- }
- if ( bestEnt )
- {
- self->enemy = bestEnt;
- }
- }
- /*
- ===============
- G_DamageFeedback
- Called just before a snapshot is sent to the given player.
- Totals up all damage and generates both the player_state_t
- damage values to that client for pain blends and kicks, and
- global pain sound events for all clients.
- ===============
- */
- void P_DamageFeedback( gentity_t *player ) {
- gclient_t *client;
- float count;
- vec3_t angles;
- client = player->client;
- if ( client->ps.pm_type == PM_DEAD ) {
- return;
- }
- // total points of damage shot at the player this frame
- count = client->damage_blood + client->damage_armor;
- if ( count == 0 ) {
- return; // didn't take any damage
- }
- if ( count > 255 ) {
- count = 255;
- }
- // send the information to the client
- // world damage (falling, slime, etc) uses a special code
- // to make the blend blob centered instead of positional
- if ( client->damage_fromWorld )
- {
- client->ps.damagePitch = 255;
- client->ps.damageYaw = 255;
- client->damage_fromWorld = false;
- }
- else
- {
- vectoangles( client->damage_from, angles );
- client->ps.damagePitch = angles[PITCH]/360.0 * 256;
- client->ps.damageYaw = angles[YAW]/360.0 * 256;
- }
- client->ps.damageCount = count;
- //
- // clear totals
- //
- client->damage_blood = 0;
- client->damage_armor = 0;
- }
- /*
- =============
- P_WorldEffects
- Check for lava / slime contents and drowning
- =============
- */
- extern void WP_ForcePowerStart( gentity_t *self, forcePowers_t forcePower, int overrideAmt );
- void P_WorldEffects( gentity_t *ent ) {
- int mouthContents = 0;
- if ( ent->client->noclip )
- {
- ent->client->airOutTime = level.time + 12000; // don't need air
- return;
- }
- if ( !in_camera )
- {
- #ifndef _XBOX
- if (gi.totalMapContents() & (CONTENTS_WATER|CONTENTS_SLIME))
- {
- mouthContents = gi.pointcontents( ent->client->renderInfo.eyePoint, ent->s.number );
- }
- #endif // _XBOX
- }
- //
- // check for drowning
- //
- #ifdef _XBOX
- // using waterlevel 3 should be good enough
- // this saves us from doing an expensive trace
- if ( ent->waterlevel == 3 )
- #else
- if ( (mouthContents&(CONTENTS_WATER|CONTENTS_SLIME)) )
- #endif // _XBOX
- {
- if ( ent->client->NPC_class == CLASS_SWAMPTROOPER )
- {//they have air tanks
- ent->client->airOutTime = level.time + 12000; // don't need air
- ent->damage = 2;
- }
- else if ( ent->client->airOutTime < level.time)
- {// if out of air, start drowning
- // drown!
- ent->client->airOutTime += 1000;
- if ( ent->health > 0 ) {
- // take more damage the longer underwater
- ent->damage += 2;
- if (ent->damage > 15)
- ent->damage = 15;
- // play a gurp sound instead of a normal pain sound
- if (ent->health <= ent->damage)
- {
- G_AddEvent( ent, EV_WATER_DROWN, 0 );
- }
- else
- {
- G_AddEvent( ent, Q_irand(EV_WATER_GURP1, EV_WATER_GURP2), 0 );
- }
- // don't play a normal pain sound
- ent->painDebounceTime = level.time + 200;
- G_Damage (ent, NULL, NULL, NULL, NULL,
- ent->damage, DAMAGE_NO_ARMOR, MOD_WATER);
- }
- }
- }
- else
- {
- ent->client->airOutTime = level.time + 12000;
- ent->damage = 2;
- }
- //
- // check for sizzle damage (move to pmove?)
- //
- if (ent->waterlevel &&
- (ent->watertype&(CONTENTS_LAVA|CONTENTS_SLIME)) ) {
- if (ent->health > 0
- && ent->painDebounceTime < level.time ) {
- if (ent->watertype & CONTENTS_LAVA) {
- G_Damage (ent, NULL, NULL, NULL, NULL,
- 15*ent->waterlevel, 0, MOD_LAVA);
- }
- if (ent->watertype & CONTENTS_SLIME) {
- G_Damage (ent, NULL, NULL, NULL, NULL,
- 1, 0, MOD_SLIME);
- }
- }
- }
- if ((ent->health > 0) &&
- (ent->painDebounceTime < level.time) &&
- gi.WE_IsOutsideCausingPain(ent->currentOrigin) &&
- TIMER_Done(ent, "AcidPainDebounce"))
- {
- if (ent->NPC && ent->client && (ent->client->ps.forcePowersKnown&(1<< FP_PROTECT)))
- {
- if (!(ent->client->ps.forcePowersActive & (1<<FP_PROTECT)))
- {
- WP_ForcePowerStart( ent, FP_PROTECT, 0 );
- }
- }
- else
- {
- G_Damage (ent, NULL, NULL, NULL, NULL, 1, 0, MOD_SLIME);
- }
- }
- // Poisoned?
- if ((ent->client->poisonDamage) && (ent->client->poisonTime < level.time))
- {
- ent->client->poisonDamage -= 2;
- ent->client->poisonTime = level.time + 1000;
- G_Damage( ent, NULL, NULL, 0, 0, 2, DAMAGE_NO_KNOCKBACK|DAMAGE_NO_ARMOR, MOD_UNKNOWN );//FIXME: MOD_POISON?
- if (ent->client->poisonDamage<0)
- {
- ent->client->poisonDamage = 0;
- }
- }
- //in space?
- if (ent->client->inSpaceIndex && ent->client->inSpaceIndex != ENTITYNUM_NONE)
- { //we're in space, check for suffocating and for exiting
- gentity_t *spacetrigger = &g_entities[ent->client->inSpaceIndex];
- if (!spacetrigger->inuse ||
- !G_PointInBounds(ent->client->ps.origin, spacetrigger->absmin, spacetrigger->absmax))
- { //no longer in space then I suppose
- ent->client->inSpaceIndex = 0;
- }
- else
- { //check for suffocation
- if (ent->client->inSpaceSuffocation < level.time)
- { //suffocate!
- if (ent->health > 0)
- { //if they're still alive..
- G_Damage(ent, spacetrigger, spacetrigger, NULL, ent->client->ps.origin, Q_irand(20, 40), DAMAGE_NO_ARMOR, MOD_SUICIDE);
- if (ent->health > 0)
- { //did that last one kill them?
- //play the choking sound
- G_SoundOnEnt( ent, CHAN_VOICE, va( "*choke%d.wav", Q_irand( 1, 3 ) ) );
- int anim = BOTH_CHOKE3; //left-handed choke
- if ( ent->client->ps.weapon == WP_NONE || ent->client->ps.weapon == WP_MELEE )
- {
- anim = BOTH_CHOKE1; //two-handed choke
- }
- //make them grasp their throat
- NPC_SetAnim( ent, SETANIM_BOTH, BOTH_CHOKE1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
- }
- }
- ent->client->inSpaceSuffocation = level.time + Q_irand(1000, 2000);
- }
- }
- }
- }
- /*
- ===============
- G_SetClientSound
- ===============
- */
- void G_SetClientSound( gentity_t *ent ) {
- // if (ent->waterlevel && (ent->watertype&(CONTENTS_LAVA|CONTENTS_SLIME)) )
- // ent->s.loopSound = G_SoundIndex("sound/weapons/stasis/electricloop.wav");
- // else
- // ent->s.loopSound = 0;
- }
- //==============================================================
- extern void G_Knockdown( gentity_t *self, gentity_t *attacker, const vec3_t pushDir, float strength, qboolean breakSaberLock );
- extern void G_StartMatrixEffect( gentity_t *ent, int meFlags = 0, int length = 1000, float timeScale = 0.0f, int spinTime = 0 );
- void G_GetMassAndVelocityForEnt( gentity_t *ent, float *mass, vec3_t velocity )
- {
- if( ent->client )
- {
- VectorCopy( ent->client->ps.velocity, velocity );
- *mass = ent->mass;
- }
- else
- {
- VectorCopy( ent->s.pos.trDelta, velocity );
- if ( ent->s.pos.trType == TR_GRAVITY )
- {
- velocity[2] -= 0.25f * g_gravity->value;
- }
- if( !ent->mass )
- {
- *mass = 1;
- }
- else if ( ent->mass <= 10 )
- {
- *mass = 10;
- }
- else
- {
- *mass = ent->mass;///10;
- }
- }
- }
- void DoImpact( gentity_t *self, gentity_t *other, qboolean damageSelf, trace_t *trace )
- {
- float magnitude, my_mass;
- bool thrown = false;
- vec3_t velocity;
- Vehicle_t *pSelfVeh = NULL;
- Vehicle_t *pOtherVeh = NULL;
- // See if either of these guys are vehicles, if so, keep a pointer to the vehicle npc.
- if ( self->client && self->client->NPC_class == CLASS_VEHICLE )
- {
- pSelfVeh = self->m_pVehicle;
- }
- if ( other->client && other->client->NPC_class == CLASS_VEHICLE )
- {
- pOtherVeh = other->m_pVehicle;
- }
- G_GetMassAndVelocityForEnt( self, &my_mass, velocity );
- if ( pSelfVeh )
- {
- magnitude = VectorLength( velocity ) * pSelfVeh->m_pVehicleInfo->mass / 50.0f;
- }
- else
- {
- magnitude = VectorLength( velocity ) * my_mass / 50;
- }
- //if vehicle hit another vehicle, factor in their data, too
- // TODO: Bring this back in later on, it's not critical right now...
- /* if ( self->client && self->client->NPC_class == CLASS_VEHICLE )
- {//we're in a vehicle
- if ( other->client && other->client->ps.vehicleIndex != VEHICLE_NONE )
- {//they're in a vehicle
- float o_mass;
- vec3_t o_velocity;
- G_GetMassAndVelocityForEnt( other, &o_mass, o_velocity );
- //now combine
- if ( DotProduct( o_velocity, velocity ) < 0 )
- {//were heading towards each other, this is going to be a STRONG impact...
- vec3_t velocityMod;
- //incorportate mass into each velocity to get directional force
- VectorScale( velocity, my_mass/50, velocityMod );
- VectorScale( o_velocity, o_mass/50, o_velocity );
- //figure out the overall magnitude of those 2 directed forces impacting
- magnitude = (DotProduct( o_velocity, velocityMod ) * -1.0f)/500.0f;
- }
- }
- }*/
- // Check For Vehicle On Vehicle Impact (Ramming)
- //-----------------------------------------------
- if ( pSelfVeh &&
- pSelfVeh->m_pVehicleInfo->type!=VH_ANIMAL &&
- pOtherVeh &&
- pSelfVeh->m_pVehicleInfo==pOtherVeh->m_pVehicleInfo
- )
- {
- gentity_t* attacker = self;
- Vehicle_t* attackerVeh = pSelfVeh;
- gentity_t* victim = other;
- Vehicle_t* victimVeh = pOtherVeh;
- // Is The Attacker Actually Not Attacking?
- //-----------------------------------------
- if (!(attackerVeh->m_ulFlags&VEH_STRAFERAM))
- {
- // Ok, So Is The Victim Actually Attacking?
- //------------------------------------------
- if (victimVeh->m_ulFlags&VEH_STRAFERAM)
- {
- // Ah, Ok. Swap Who Is The Attacker Then
- //----------------------------------------
- attacker = other;
- attackerVeh = pOtherVeh;
- victim = self;
- victimVeh = pSelfVeh;
- }
- else
- {
- // No Attackers, So Stop
- //-----------------------
- attacker = victim = 0;
- }
- }
- if (attacker && victim)
- {
- // float maxMoveSpeed = pSelfVeh->m_pVehicleInfo->speedMax;
- // float minLockingSpeed = maxMoveSpeed * 0.75;
- vec3_t attackerMoveDir;
- float attackerMoveSpeed;
- vec3_t victimMoveDir;
- float victimMoveSpeed;
- vec3_t victimTowardAttacker;
- float victimTowardAttackerDistance;
- vec3_t victimRight;
- float victimRightAccuracy;
- VectorCopy(attacker->client->ps.velocity, attackerMoveDir);
- VectorCopy(victim->client->ps.velocity, victimMoveDir);
- attackerMoveSpeed = VectorNormalize(attackerMoveDir);
- victimMoveSpeed = VectorNormalize(victimMoveDir);
- AngleVectors(victim->currentAngles, 0, victimRight, 0);
- VectorSubtract(victim->currentOrigin, attacker->currentOrigin, victimTowardAttacker);
- victimTowardAttackerDistance = VectorNormalize(victimTowardAttacker);
- victimRightAccuracy = DotProduct(victimTowardAttacker, victimRight);
- if (
- fabsf(victimRightAccuracy)>0.25 // Must Be Exactly Right Or Left
- // && victimTowardAttackerDistance<100.0f // Must Be Close Enough
- // && attackerMoveSpeed>minLockingSpeed // Must be moving fast enough
- // && fabsf(attackerMoveSpeed - victimMoveSpeed)<100 // Both must be going about the same speed
- )
- {
- thrown = true;
- vec3_t victimRight;
- vec3_t victimAngles;
- VectorCopy(victim->currentAngles, victimAngles);
- victimAngles[2] = 0;
- AngleVectors(victimAngles, 0, victimRight, 0);
- if (attackerVeh->m_fStrafeTime<0)
- {
- VectorScale(victimRight, -1.0f, victimRight);
- }
- if ( !(victim->flags&FL_NO_KNOCKBACK) )
- {
- G_Throw(victim, victimRight, 250);
- }
- // if (false)
- // {
- // VectorMA(victim->currentOrigin, 250.0f, victimRight, victimRight);
- // CG_DrawEdge(victim->currentOrigin, victimRight, EDGE_IMPACT_POSSIBLE);
- // }
- if (victimVeh->m_pVehicleInfo->iImpactFX)
- {
- G_PlayEffect(victimVeh->m_pVehicleInfo->iImpactFX, victim->currentOrigin, trace->plane.normal );
- }
- }
- }
- }
- if ( !self->client || self->client->ps.lastOnGround+300<level.time || ( self->client->ps.lastOnGround+100 < level.time ) )
- {
- vec3_t dir1, dir2;
- float force = 0, dot;
- qboolean vehicleHitOwner = qfalse;
- if ( other->material == MAT_GLASS || other->material == MAT_GLASS_METAL || other->material == MAT_GRATE1 || ((other->svFlags&SVF_BBRUSH)&&(other->spawnflags&8/*THIN*/)) )//(other->absmax[0]-other->absmin[0]<=32||other->absmax[1]-other->absmin[1]<=32||other->absmax[2]-other->absmin[2]<=32)) )
- {//glass and thin breakable brushes (axially aligned only, unfortunately) take more impact damage
- magnitude *= 2;
- }
-
- // See if the vehicle has crashed into the ground.
- if ( pSelfVeh && pSelfVeh->m_pVehicleInfo->type!=VH_ANIMAL)
- {
- if ((magnitude >= 80) && (self->painDebounceTime < level.time))
- {
- // Setup Some Variables
- //----------------------
- vec3_t vehFwd;
- VectorCopy(velocity, vehFwd);
- float vehSpeed = VectorNormalize(vehFwd);
- float vehToughnessAgainstOther = pSelfVeh->m_pVehicleInfo->toughness;
- float vehHitPercent = fabsf(DotProduct(vehFwd, trace->plane.normal));
- int vehDFlags = DAMAGE_NO_ARMOR;
- bool vehPilotedByPlayer = (pSelfVeh->m_pPilot && pSelfVeh->m_pPilot->s.number<MAX_CLIENTS);
- bool vehInTurbo = (pSelfVeh->m_iTurboTime>level.time);
- self->painDebounceTime = level.time + 200;
- // Modify Magnitude By Hit Percent And Toughness Against Other
- //-------------------------------------------------------------
- if (pSelfVeh->m_ulFlags & VEH_OUTOFCONTROL)
- {
- vehToughnessAgainstOther *= 0.01f; // If Out Of Control, No Damage Resistance
- }
- else
- {
- if (vehPilotedByPlayer)
- {
- vehToughnessAgainstOther *= 1.5f;
- }
- if (other && other->client)
- {
- vehToughnessAgainstOther *= 15.0f; // Very Tough against other clients (NPCS, Player, etc)
- }
- }
- if (vehToughnessAgainstOther>0.0f)
- {
- magnitude *= (vehHitPercent / vehToughnessAgainstOther);
- }
- else
- {
- magnitude *= vehHitPercent;
- }
- // If We Hit Architecture
- //------------------------
- if (!other || !other->client)
- {
- // Turbo Hurts
- //-------------
- if (vehInTurbo)
- {
- magnitude *= 5.0f;
- }
- else if (trace->plane.normal[2]>0.75f && vehHitPercent<0.2f)
- {
- magnitude /= 10.0f;
- }
- // If No Pilot, Blow This Thing Now
- //----------------------------------
- if (vehHitPercent>0.9f && !pSelfVeh->m_pPilot && vehSpeed>1000.0f)
- {
- vehDFlags |= DAMAGE_IMPACT_DIE;
- }
- // If Out Of Control, And We Hit A Wall Or Landed Or Head On
- //------------------------------------------------------------
- if ((pSelfVeh->m_ulFlags&VEH_OUTOFCONTROL) && (vehHitPercent>0.5f || trace->plane.normal[2]<0.5f || velocity[2]<-50.0f))
- {
- vehDFlags |= DAMAGE_IMPACT_DIE;
- }
- // If This Is A Direct Impact (Debounced By 4 Seconds)
- //-----------------------------------------------------
- if (vehHitPercent>0.9f && (level.time - self->lastImpact)>2000 && vehSpeed>300.0f)
- {
- self->lastImpact = level.time;
- // The Player Has Harder Requirements to Explode
- //-----------------------------------------------
- if (vehPilotedByPlayer)
- {
- if ((vehHitPercent>0.99f && vehSpeed>1000.0f && !Q_irand(0,30)) ||
- (vehHitPercent>0.999f && vehInTurbo))
- {
- vehDFlags |= DAMAGE_IMPACT_DIE;
- }
- }
- else if (player && G_IsRidingVehicle(player) &&
- (Distance(self->currentOrigin, player->currentOrigin)<800.0f) &&
- (vehInTurbo || !Q_irand(0,1) || vehHitPercent>0.999f))
- {
- vehDFlags |= DAMAGE_IMPACT_DIE;
- }
- }
- // Make Sure He Dies This Time. I will accept no excuses.
- //---------------------------------------------------------
- if (vehDFlags&DAMAGE_IMPACT_DIE)
- {
- // If close enough To The PLayer
- if (player &&
- G_IsRidingVehicle(player) &&
- self->owner &&
- Distance(self->currentOrigin, player->currentOrigin)<500.0f)
- {
- player->lastEnemy = self->owner;
- G_StartMatrixEffect(player, MEF_LOOK_AT_ENEMY|MEF_NO_RANGEVAR|MEF_NO_VERTBOB|MEF_NO_SPIN, 1000);
- }
- magnitude = 100000.0f;
- }
- }
- if (magnitude>10.0f)
- {
- // Play The Impact Effect
- //------------------------
- if (pSelfVeh->m_pVehicleInfo->iImpactFX && vehSpeed>100.0f)
- {
- G_PlayEffect( pSelfVeh->m_pVehicleInfo->iImpactFX, self->currentOrigin, trace->plane.normal );
- }
- // Set The Crashing Flag And Pain Debounce Time
- //----------------------------------------------
- pSelfVeh->m_ulFlags |= VEH_CRASHING;
- }
- G_Damage( self, player, player, NULL, self->currentOrigin, magnitude, vehDFlags, MOD_FALLING );//FIXME: MOD_IMPACT
- }
- if ( self->owner == other || self->activator == other )
- {//hit owner/activator
- if ( self->m_pVehicle && !self->m_pVehicle->m_pVehicleInfo->Inhabited( self->m_pVehicle ) )
- {//empty swoop
- if ( self->client->respawnTime - level.time < 1000 )
- {//just spawned in a second ago
- //don't actually damage or throw him...
- vehicleHitOwner = qtrue;
- }
- }
- }
- //if 2 vehicles on same side hit each other, tone it down
- //NOTE: we do this here because we still want the impact effect
- if ( pOtherVeh )
- {
- if ( self->client->playerTeam == other->client->playerTeam )
- {
- magnitude /= 25;
- }
- }
- }
- else if ( self->client
- && (PM_InKnockDown( &self->client->ps )||(self->client->ps.eFlags&EF_FORCE_GRIPPED))
- && magnitude >= 120 )
- {//FORCE-SMACKED into something
- if ( TIMER_Done( self, "impactEffect" ) )
- {
- G_PlayEffect( G_EffectIndex( "env/impact_dustonly" ), trace->endpos, trace->plane.normal );
- G_Sound( self, G_SoundIndex( va( "sound/weapons/melee/punch%d", Q_irand( 1, 4 ) ) ) );
- TIMER_Set( self, "impactEffect", 1000 );
- }
- }
- //damage them
- if ( magnitude >= 100 && other->s.number < ENTITYNUM_WORLD )
- {
- VectorCopy( velocity, dir1 );
- VectorNormalize( dir1 );
- if( VectorCompare( other->currentOrigin, vec3_origin ) )
- {//a brush with no origin
- VectorCopy ( dir1, dir2 );
- }
- else
- {
- VectorSubtract( other->currentOrigin, self->currentOrigin, dir2 );
- VectorNormalize( dir2 );
- }
- dot = DotProduct( dir1, dir2 );
- if ( dot >= 0.2 )
- {
- force = dot;
- }
- else
- {
- force = 0;
- }
- force *= (magnitude/50);
- int cont = gi.pointcontents( other->absmax, other->s.number );
- if( (cont&CONTENTS_WATER) )
- {//water absorbs 2/3 velocity
- force *= 0.33333f;
- }
- if ( self->NPC && other->s.number == ENTITYNUM_WORLD )
- {//NPCs take less damage
- force *= 0.5f;
- }
- if ( self->s.number >= MAX_CLIENTS && self->client && (PM_InKnockDown( &self->client->ps )||self->client->ps.eFlags&EF_FORCE_GRIPPED) )
- {//NPC: I was knocked down or being gripped, impact should be harder
- //FIXME: what if I was just thrown - force pushed/pulled or thrown from a grip?
- force *= 10;
- }
- //FIXME: certain NPCs/entities should be TOUGH - like Ion Cannons, AT-STs, Mark1 droids, etc.
- if ( pOtherVeh )
- {//if hit another vehicle, take their toughness into account, too
- force /= pOtherVeh->m_pVehicleInfo->toughness;
- }
- if( ( (force >= 1 || pSelfVeh) && other->s.number>=MAX_CLIENTS ) || force >= 10)
- {
- /*
- dprint("Damage other (");
- dprint(loser.classname);
- dprint("): ");
- dprint(ftos(force));
- dprint("\n");
- */
- if ( other->svFlags & SVF_GLASS_BRUSH )
- {
- other->splashRadius = (float)(self->maxs[0] - self->mins[0])/4.0f;
- }
- if ( pSelfVeh )
- {//if in a vehicle when land on someone, always knockdown, throw, damage
- if ( !vehicleHitOwner )
- {//didn't hit owner
- // If the player was hit don't make the damage so bad...
- if ( other && other->s.number<MAX_CLIENTS )
- {
- force *= 0.5f;
- }
- //Hmm, maybe knockdown?
- if ( !(other->flags&FL_NO_KNOCKBACK) )
- {
- G_Throw( other, dir2, force );
- }
- G_Knockdown( other, self, dir2, force, qtrue );
- G_Damage( other, self, self, velocity, self->currentOrigin, force, DAMAGE_NO_ARMOR|DAMAGE_EXTRA_KNOCKBACK, MOD_IMPACT );
- }
- }
- else if ( self->forcePushTime > level.time - 1000//was force pushed/pulled in the last 1600 milliseconds
- && self->forcePuller == other->s.number>=MAX_CLIENTS )//hit the person who pushed/pulled me
- {//ignore the impact
- }
- else if ( other->takedamage )
- {
- if ( !self->client || other->s.number<MAX_CLIENTS || !other->client )
- {//aw, fuck it, clients no longer take impact damage from other clients, unless you're the player
- if ( other->client //he's a client
- && self->client //I'm a client
- && other->client->ps.forceGripEntityNum == self->s.number )//he's force-gripping me
- {//don't damage the other guy if he's gripping me
- }
- else
- {
- G_Damage( other, self, self, velocity, self->currentOrigin, floor(force), DAMAGE_NO_ARMOR, MOD_IMPACT );
- }
- }
- else
- {
- GEntity_PainFunc( other, self, self, self->currentOrigin, force, MOD_IMPACT );
- //Hmm, maybe knockdown?
- if (!thrown)
- {
- if ( !(other->flags&FL_NO_KNOCKBACK) )
- {
- G_Throw( other, dir2, force );
- }
- }
- }
- if ( other->health > 0 )
- {//still alive?
- //TODO: if someone was thrown through the air (in a knockdown or being gripped)
- // and they hit me hard enough, knock me down
- if ( other->client )
- {
- if ( self->client )
- {
- if ( PM_InKnockDown( &self->client->ps ) || (self->client->ps.eFlags&EF_FORCE_GRIPPED) )
- {
- G_Knockdown( other, self, dir2, Q_irand( 200, 400 ), qtrue );
- }
- }
- else if ( self->forcePuller != ENTITYNUM_NONE
- && g_entities[self->forcePuller].client
- && self->mass > Q_irand( 50, 100 ) )
- {
- G_Knockdown( other, &g_entities[self->forcePuller], dir2, Q_irand( 200, 400 ), qtrue );
- }
- }
- }
- }
- else
- {
- //Hmm, maybe knockdown?
- if (!thrown)
- {
- if ( !(other->flags&FL_NO_KNOCKBACK) )
- {
- G_Throw( other, dir2, force );
- }
- }
- }
- }
- }
- if ( damageSelf && self->takedamage && !(self->flags&FL_NO_IMPACT_DMG))
- {
- //Now damage me
- //FIXME: more lenient falling damage, especially for when driving a vehicle
- if ( pSelfVeh && self->client->ps.forceJumpZStart )
- {//we were force-jumping
- if ( self->currentOrigin[2] >= self->client->ps.forceJumpZStart )
- {//we landed at same height or higher than we landed
- magnitude = 0;
- }
- else
- {//FIXME: take off some of it, at least?
- magnitude = (self->client->ps.forceJumpZStart-self->currentOrigin[2])/3;
- }
- }
- if( ( magnitude >= 100 + self->health
- && self->s.number >= MAX_CLIENTS
- && self->s.weapon != WP_SABER )
- || self->client->NPC_class == CLASS_VEHICLE
- || ( magnitude >= 700 ) )//health here is used to simulate structural integrity
- {
- if ( (self->s.weapon == WP_SABER || self->s.number<MAX_CLIENTS || (self->client&&(self->client->NPC_class==CLASS_BOBAFETT||self->client->NPC_class==CLASS_ROCKETTROOPER))) && self->client && self->client->ps.groundEntityNum < ENTITYNUM_NONE && magnitude < 1000 )
- {//players and jedi take less impact damage
- //allow for some lenience on high falls
- magnitude /= 2;
- }
- //drop it some (magic number... sigh)
- magnitude /= 40;
- //If damage other, subtract half of that damage off of own injury
- if ( other->bmodel && other->material != MAT_GLASS )
- {//take off only a little because we broke architecture (not including glass), that should hurt
- magnitude = magnitude - force/8;
- }
- else
- {//take off half damage we did to it
- magnitude = magnitude - force/2;
- }
- if ( pSelfVeh )
- {
- //FIXME: if hit another vehicle, take their toughness into
- // account, too? Or should their toughness only matter
- // when they hit me?
- magnitude /= pSelfVeh->m_pVehicleInfo->toughness * 1000.0f;
- if ( other->bmodel && other->material != MAT_GLASS )
- {//broke through some architecture, take a good amount of damage
- }
- else if ( pOtherVeh )
- {//they're tougher
- //magnitude /= 4.0f;//FIXME: get the toughness of other from vehicles.cfg
- }
- else
- {//take some off because of give
- //NOTE: this covers all other entities and impact with world...
- //FIXME: certain NPCs/entities should be TOUGH - like Ion Cannons, AT-STs, Mark1 droids, etc.
- magnitude /= 10.0f;
- }
- if ( magnitude < 1.0f )
- {
- magnitude = 0;
- }
- }
- if ( magnitude >= 1 )
- {
- //FIXME: Put in a thingtype impact sound function
- /*
- dprint("Damage self (");
- dprint(self.classname);
- dprint("): ");
- dprint(ftos(magnitude));
- dprint("\n");
- */
- if ( self->NPC && self->s.weapon == WP_SABER )
- {//FIXME: for now Jedi take no falling damage, but really they should if pushed off?
- magnitude = 0;
- }
- G_Damage( self, NULL, NULL, NULL, self->currentOrigin, magnitude/2, DAMAGE_NO_ARMOR, MOD_FALLING );//FIXME: MOD_IMPACT
- }
- }
- }
- //FIXME: slow my velocity some?
-
- /*
- if(self.flags&FL_ONGROUND)
- self.last_onground=time;
- */
- }
- }
- /*
- ==============
- ClientImpacts
- ==============
- */
- void ClientImpacts( gentity_t *ent, pmove_t *pm ) {
- int i, j;
- trace_t trace;
- gentity_t *other;
- memset( &trace, 0, sizeof( trace ) );
- for (i=0 ; i<pm->numtouch ; i++) {
- for (j=0 ; j<i ; j++) {
- if (pm->touchents[j] == pm->touchents[i] ) {
- break;
- }
- }
- if (j != i) {
- continue; // duplicated
- }
- other = &g_entities[ pm->touchents[i] ];
- if ( ( ent->NPC != NULL ) && ( ent->e_TouchFunc != touchF_NULL ) ) { // last check unneccessary
- GEntity_TouchFunc( ent, other, &trace );
- }
- if ( other->e_TouchFunc == touchF_NULL ) { // not needed, but I'll leave it I guess (cache-hit issues)
- continue;
- }
- GEntity_TouchFunc( other, ent, &trace );
- }
- }
- /*
- ============
- G_TouchTriggersLerped
- Find all trigger entities that ent's current position touches.
- Spectators will only interact with teleporters.
- This version checks at 6 unit steps between last and current origins
- ============
- */
- void G_TouchTriggersLerped( gentity_t *ent ) {
- int i, num;
- float dist, curDist = 0;
- gentity_t *touch[MAX_GENTITIES], *hit;
- trace_t trace;
- vec3_t end, mins, maxs, diff;
- const vec3_t range = { 40, 40, 52 };
- qboolean touched[MAX_GENTITIES];
- qboolean done = qfalse;
- if ( !ent->client ) {
- return;
- }
- // dead NPCs don't activate triggers!
- if ( ent->client->ps.stats[STAT_HEALTH] <= 0 )
- {
- if ( ent->s.number>=MAX_CLIENTS )
- {
- return;
- }
- }
- #ifdef _DEBUG
- for ( int j = 0; j < 3; j++ )
- {
- assert( !_isnan(ent->currentOrigin[j]));
- assert( !_isnan(ent->lastOrigin[j]));
- }
- #endif// _DEBUG
- VectorSubtract( ent->currentOrigin, ent->lastOrigin, diff );
- dist = VectorNormalize( diff );
- #ifdef _DEBUG
- assert( (dist<1024) && "insane distance in G_TouchTriggersLerped!" );
- #endif// _DEBUG
- if ( dist > 1024 )
- {
- return;
- }
- memset (touched, qfalse, sizeof(touched) );
- for ( curDist = 0; !done && ent->maxs[1]>0; curDist += (float)ent->maxs[1]/2.0f )
- {
- if ( curDist >= dist )
- {
- VectorCopy( ent->currentOrigin, end );
- done = qtrue;
- }
- else
- {
- VectorMA( ent->lastOrigin, curDist, diff, end );
- }
- VectorSubtract( end, range, mins );
- VectorAdd( end, range, maxs );
- num = gi.EntitiesInBox( mins, maxs, touch, MAX_GENTITIES );
- // can't use ent->absmin, because that has a one unit pad
- VectorAdd( end, ent->mins, mins );
- VectorAdd( end, ent->maxs, maxs );
- for ( i=0 ; i<num ; i++ ) {
- hit = touch[i];
- if ( (hit->e_TouchFunc == touchF_NULL) && (ent->e_TouchFunc == touchF_NULL) ) {
- continue;
- }
- if ( !( hit->contents & CONTENTS_TRIGGER ) ) {
- continue;
- }
- if ( touched[i] == qtrue ) {
- continue;//already touched this move
- }
- if ( ent->client->ps.stats[STAT_HEALTH] <= 0 )
- {
- if ( Q_stricmp( "trigger_teleport", hit->classname ) || !(hit->spawnflags&16/*TTSF_DEAD_OK*/) )
- {//dead clients can only touch tiogger_teleports that are marked as touchable
- continue;
- }
- }
- // use seperate code for determining if an item is picked up
- // so you don't have to actually contact its bounding box
- /*
- if ( hit->s.eType == ET_ITEM ) {
- if ( !BG_PlayerTouchesItem( &ent->client->ps, &hit->s, level.time ) ) {
- continue;
- }
- } else */
- {
- if ( !gi.EntityContact( mins, maxs, hit ) ) {
- continue;
- }
- }
- touched[i] = qtrue;
- memset( &trace, 0, sizeof(trace) );
- if ( hit->e_TouchFunc != touchF_NULL ) {
- GEntity_TouchFunc(hit, ent, &trace);
- }
- //WTF? Why would a trigger ever fire off the NPC's touch func??!!!
- /*
- if ( ( ent->NPC != NULL ) && ( ent->e_TouchFunc != touchF_NULL ) ) {
- GEntity_TouchFunc( ent, hit, &trace );
- }
- */
- }
- }
- }
- /*
- ============
- G_TouchTriggers
- Find all trigger entities that ent's current position touches.
- Spectators will only interact with teleporters.
- ============
- */
- void G_TouchTriggers( gentity_t *ent ) {
- int i, num;
- gentity_t *touch[MAX_GENTITIES], *hit;
- trace_t trace;
- vec3_t mins, maxs;
- const vec3_t range = { 40, 40, 52 };
- if ( !ent->client ) {
- return;
- }
- // dead clients don't activate triggers!
- if ( ent->client->ps.stats[STAT_HEALTH] <= 0 ) {
- return;
- }
- VectorSubtract( ent->client->ps.origin, range, mins );
- VectorAdd( ent->client->ps.origin, range, maxs );
- num = gi.EntitiesInBox( mins, maxs, touch, MAX_GENTITIES );
- // can't use ent->absmin, because that has a one unit pad
- VectorAdd( ent->client->ps.origin, ent->mins, mins );
- VectorAdd( ent->client->ps.origin, ent->maxs, maxs );
- for ( i=0 ; i<num ; i++ ) {
- hit = touch[i];
- if ( (hit->e_TouchFunc == touchF_NULL) && (ent->e_TouchFunc == touchF_NULL) ) {
- continue;
- }
- if ( !( hit->contents & CONTENTS_TRIGGER ) ) {
- continue;
- }
- // use seperate code for determining if an item is picked up
- // so you don't have to actually contact its bounding box
- /*
- if ( hit->s.eType == ET_ITEM ) {
- if ( !BG_PlayerTouchesItem( &ent->client->ps, &hit->s, level.time ) ) {
- continue;
- }
- } else */
- {
- if ( !gi.EntityContact( mins, maxs, hit ) ) {
- continue;
- }
- }
- memset( &trace, 0, sizeof(trace) );
- if ( hit->e_TouchFunc != touchF_NULL ) {
- GEntity_TouchFunc(hit, ent, &trace);
- }
- if ( ( ent->NPC != NULL ) && ( ent->e_TouchFunc != touchF_NULL ) ) {
- GEntity_TouchFunc( ent, hit, &trace );
- }
- }
- }
- /*
- ============
- G_MoverTouchTriggers
- Find all trigger entities that ent's current position touches.
- Spectators will only interact with teleporters.
- ============
- */
- void G_MoverTouchPushTriggers( gentity_t *ent, vec3_t oldOrg )
- {
- int i, num;
- float step, stepSize, dist;
- gentity_t *touch[MAX_GENTITIES], *hit;
- trace_t trace;
- vec3_t mins, maxs, dir, size, checkSpot;
- const vec3_t range = { 40, 40, 52 };
- // non-moving movers don't hit triggers!
- if ( !VectorLengthSquared( ent->s.pos.trDelta ) )
- {
- return;
- }
- VectorSubtract( ent->mins, ent->maxs, size );
- stepSize = VectorLength( size );
- if ( stepSize < 1 )
- {
- stepSize = 1;
- }
- VectorSubtract( ent->currentOrigin, oldOrg, dir );
- dist = VectorNormalize( dir );
- for ( step = 0; step <= dist; step += stepSize )
- {
- VectorMA( ent->currentOrigin, step, dir, checkSpot );
- VectorSubtract( checkSpot, range, mins );
- VectorAdd( checkSpot, range, maxs );
- num = gi.EntitiesInBox( mins, maxs, touch, MAX_GENTITIES );
- // can't use ent->absmin, because that has a one unit pad
- VectorAdd( checkSpot, ent->mins, mins );
- VectorAdd( checkSpot, ent->maxs, maxs );
- for ( i=0 ; i<num ; i++ )
- {
- hit = touch[i];
- if ( hit->s.eType != ET_PUSH_TRIGGER )
- {
- continue;
- }
- if ( hit->e_TouchFunc == touchF_NULL )
- {
- continue;
- }
- if ( !( hit->contents & CONTENTS_TRIGGER ) )
- {
- continue;
- }
- if ( !gi.EntityContact( mins, maxs, hit ) )
- {
- continue;
- }
- memset( &trace, 0, sizeof(trace) );
- if ( hit->e_TouchFunc != touchF_NULL )
- {
- GEntity_TouchFunc(hit, ent, &trace);
- }
- }
- }
- }
- void G_MatchPlayerWeapon( gentity_t *ent )
- {
- if ( g_entities[0].inuse && g_entities[0].client )
- {//player is around
- int newWeap;
- if ( g_entities[0].client->ps.weapon > WP_CONCUSSION )
- {
- newWeap = WP_BLASTER_PISTOL;
- }
- else
- {
- newWeap = g_entities[0].client->ps.weapon;
- }
- if ( newWeap != WP_NONE && ent->client->ps.weapon != newWeap )
- {
- G_RemoveWeaponModels( ent );
- ent->client->ps.stats[STAT_WEAPONS] = ( 1 << newWeap );
- ent->client->ps.ammo[weaponData[newWeap].ammoIndex] = 999;
- ChangeWeapon( ent, newWeap );
- ent->client->ps.weapon = newWeap;
- ent->client->ps.weaponstate = WEAPON_READY;
- if ( newWeap == WP_SABER )
- {
- //FIXME: AddSound/Sight Event
- int numSabers = WP_SaberInitBladeData( ent );
- WP_SaberAddG2SaberModels( ent );
- for ( int saberNum = 0; saberNum < numSabers; saberNum++ )
- {
- //G_CreateG2AttachedWeaponModel( ent, ent->client->ps.saber[saberNum].model, ent->handRBolt, 0 );
- ent->client->ps.saber[saberNum].type = g_entities[0].client->ps.saber[saberNum].type;
- for ( int bladeNum = 0; bladeNum < ent->client->ps.saber[saberNum].numBlades; bladeNum++ )
- {
- ent->client->ps.saber[saberNum].blade[0].active = g_entities[0].client->ps.saber[saberNum].blade[bladeNum].active;
- ent->client->ps.saber[saberNum].blade[0].length = g_entities[0].client->ps.saber[saberNum].blade[bladeNum].length;
- }
- }
- ent->client->ps.saberAnimLevel = g_entities[0].client->ps.saberAnimLevel;
- ent->client->ps.saberStylesKnown = g_entities[0].client->ps.saberStylesKnown;
- }
- else
- {
- G_CreateG2AttachedWeaponModel( ent, weaponData[newWeap].weaponMdl, ent->handRBolt, 0 );
- }
- }
- }
- }
- void G_NPCMunroMatchPlayerWeapon( gentity_t *ent )
- {
- //special uber hack for cinematic players to match player's weapon
- if ( !in_camera )
- {
- if ( ent && ent->client && ent->NPC && (ent->NPC->aiFlags&NPCAI_MATCHPLAYERWEAPON) )
- {//we're a Player NPC
- G_MatchPlayerWeapon( ent );
- }
- }
- }
- /*
- ==================
- ClientTimerActions
- Actions that happen once a second
- ==================
- */
- void ClientTimerActions( gentity_t *ent, int msec ) {
- gclient_t *client;
- client = ent->client;
- client->timeResidual += msec;
- while ( client->timeResidual >= 1000 )
- {
- client->timeResidual -= 1000;
- if ( ent->s.weapon != WP_NONE )
- {
- ent->client->sess.missionStats.weaponUsed[ent->s.weapon]++;
- }
- // if we've got the seeker powerup, see if we can shoot it at someone
- /* if ( ent->client->ps.powerups[PW_SEEKER] > level.time )
- {
- vec3_t seekerPos, dir;
- gentity_t *enemy = SeekerAcquiresTarget( ent, seekerPos );
- if ( enemy != NULL ) // set the client's enemy to a valid target
- {
- FireSeeker( ent, enemy, seekerPos, dir );
- gentity_t *tent;
- tent = G_TempEntity( seekerPos, EV_POWERUP_SEEKER_FIRE );
- VectorCopy( dir, tent->pos1 );
- tent->s.eventParm = ent->s.number;
- }
- }*/
- if ( (ent->flags&FL_OVERCHARGED_HEALTH) )
- {//need to gradually reduce health back to max
- if ( ent->health > ent->client->ps.stats[STAT_MAX_HEALTH] )
- {//decrement it
- ent->health--;
- ent->client->ps.stats[STAT_HEALTH] = ent->health;
- }
- else
- {//done
- ent->flags &= ~FL_OVERCHARGED_HEALTH;
- }
- }
- }
- }
- /*
- ====================
- ClientIntermissionThink
- ====================
- */
- static qboolean ClientCinematicThink( gclient_t *client ) {
- client->ps.eFlags &= ~EF_FIRING;
- // swap button actions
- client->oldbuttons = client->buttons;
- client->buttons = client->usercmd.buttons;
- if ( client->buttons & ( BUTTON_USE ) & ( client->oldbuttons ^ client->buttons ) ) {
- return( qtrue );
- }
- return( qfalse );
- }
- /*
- ================
- ClientEvents
- Events will be passed on to the clients for presentation,
- but any server game effects are handled here
- ================
- */
- extern void WP_SabersDamageTrace( gentity_t *ent, qboolean noEffects = qfalse );
- extern void WP_SaberUpdateOldBladeData( gentity_t *ent );
- void ClientEvents( gentity_t *ent, int oldEventSequence ) {
- int i;
- int event;
- gclient_t *client;
- //int damage;
- qboolean fired;
- client = ent->client;
- fired = qfalse;
- for ( i = oldEventSequence ; i < client->ps.eventSequence ; i++ ) {
- event = client->ps.events[ i & (MAX_PS_EVENTS-1) ];
- switch ( event ) {
- case EV_FALL_MEDIUM:
- case EV_FALL_FAR://these come from bg_pmove, PM_CrashLand
- if ( ent->s.eType != ET_PLAYER ) {
- break; // not in the player model
- }
- /*
- //FIXME: isn't there a more accurate way to calculate damage from falls?
- if ( event == EV_FALL_FAR )
- {
- damage = 50;
- }
- else
- {
- damage = 25;
- }
- ent->painDebounceTime = level.time + 200; // no normal pain sound
- G_Damage (ent, NULL, NULL, NULL, NULL, damage, 0, MOD_FALLING);
- */
- break;
- case EV_FIRE_WEAPON:
- #ifndef FINAL_BUILD
- if ( fired ) {
- gi.Printf( "DOUBLE EV_FIRE_WEAPON AND-OR EV_ALT_FIRE!!\n" );
- }
- #endif
- fired = qtrue;
- FireWeapon( ent, qfalse );
- #ifdef _XBOX
- extern int Sys_Milliseconds();
- if (ent->s.clientNum == 0)
- g_lastFireTime = Sys_Milliseconds();
- #endif
- break;
- case EV_ALT_FIRE:
- #ifndef FINAL_BUILD
- if ( fired ) {
- gi.Printf( "DOUBLE EV_FIRE_WEAPON AND-OR EV_ALT_FIRE!!\n" );
- }
- #endif
- fired = qtrue;
- FireWeapon( ent, qtrue );
- #ifdef _XBOX
- if (ent->s.clientNum == 0)
- g_lastFireTime = Sys_Milliseconds();
- #endif
- break;
- default:
- break;
- }
- }
- //by the way, if you have your saber in hand and it's on, do the damage trace
- if ( client->ps.weapon == WP_SABER )
- {
- if ( g_timescale->value >= 1.0f || !(client->ps.forcePowersActive&(1<<FP_SPEED)) )
- {
- int wait = FRAMETIME/2;
- //sanity check
- if ( client->ps.saberDamageDebounceTime - level.time > wait )
- {//when you unpause the game with force speed on, the time gets *really* wiggy...
- client->ps.saberDamageDebounceTime = level.time + wait;
- }
- if ( client->ps.saberDamageDebounceTime <= level.time )
- {
- WP_SabersDamageTrace( ent );
- WP_SaberUpdateOldBladeData( ent );
- /*
- if ( g_timescale->value&&client->ps.clientNum==0&&!player_locked&&!MatrixMode&&client->ps.forcePowersActive&(1<<FP_SPEED) )
- {
- wait = floor( (float)wait*g_timescale->value );
- }
- */
- client->ps.saberDamageDebounceTime = level.time + wait;
- }
- }
- }
- }
- void G_ThrownDeathAnimForDeathAnim( gentity_t *hitEnt, vec3_t impactPoint )
- {
- int anim = -1;
- if ( !hitEnt || !hitEnt->client )
- {
- return;
- }
- switch ( hitEnt->client->ps.legsAnim )
- {
- case BOTH_DEATH9://fall to knees, fall over
- case BOTH_DEATH10://fall to knees, fall over
- case BOTH_DEATH11://fall to knees, fall over
- case BOTH_DEATH13://stumble back, fall over
- case BOTH_DEATH17://jerky fall to knees, fall over
- case BOTH_DEATH18://grab gut, fall to knees, fall over
- case BOTH_DEATH19://grab gut, fall to knees, fall over
- case BOTH_DEATH20://grab shoulder, fall forward
- case BOTH_DEATH21://grab shoulder, fall forward
- case BOTH_DEATH3://knee collapse, twist & fall forward
- case BOTH_DEATH7://knee collapse, twist & fall forward
- {
- vec3_t dir2Impact, fwdAngles, facing;
- VectorSubtract( impactPoint, hitEnt->currentOrigin, dir2Impact );
- dir2Impact[2] = 0;
- VectorNormalize( dir2Impact );
- VectorSet( fwdAngles, 0, hitEnt->client->ps.viewangles[YAW], 0 );
- AngleVectors( fwdAngles, facing, NULL, NULL );
- float dot = DotProduct( facing, dir2Impact );//-1 = hit in front, 0 = hit on side, 1 = hit in back
- if ( dot > 0.5f )
- {//kicked in chest, fly backward
- switch ( Q_irand( 0, 4 ) )
- {//FIXME: don't start at beginning of anim?
- case 0:
- anim = BOTH_DEATH1;//thrown backwards
- break;
- case 1:
- anim = BOTH_DEATH2;//fall backwards
- break;
- case 2:
- anim = BOTH_DEATH15;//roll over backwards
- break;
- case 3:
- anim = BOTH_DEATH22;//fast fall back
- break;
- case 4:
- anim = BOTH_DEATH23;//fast fall back
- break;
- }
- }
- else if ( dot < -0.5f )
- {//kicked in back, fly forward
- switch ( Q_irand( 0, 5 ) )
- {//FIXME: don't start at beginning of anim?
- case 0:
- anim = BOTH_DEATH14;
- break;
- case 1:
- anim = BOTH_DEATH24;
- break;
- case 2:
- anim = BOTH_DEATH25;
- break;
- case 3:
- anim = BOTH_DEATH4;//thrown forwards
- break;
- case 4:
- anim = BOTH_DEATH5;//thrown forwards
- break;
- case 5:
- anim = BOTH_DEATH16;//thrown forwards
- break;
- }
- }
- else
- {//hit on side, spin
- switch ( Q_irand( 0, 2 ) )
- {//FIXME: don't start at beginning of anim?
- case 0:
- anim = BOTH_DEATH12;
- break;
- case 1:
- anim = BOTH_DEATH14;
- break;
- case 2:
- anim = BOTH_DEATH15;
- break;
- case 3:
- anim = BOTH_DEATH6;
- break;
- case 4:
- anim = BOTH_DEATH8;
- break;
- }
- }
- }
- break;
- }
- if ( anim != -1 )
- {
- NPC_SetAnim( hitEnt, SETANIM_BOTH, anim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
- }
- }
- gentity_t *G_KickTrace( gentity_t *ent, vec3_t kickDir, float kickDist, vec3_t kickEnd, int kickDamage, float kickPush, qboolean doSoundOnWalls )
- {
- vec3_t traceOrg, traceEnd, kickMins={-2,-2,-2}, kickMaxs={2,2,2};
- trace_t trace;
- gentity_t *hitEnt = NULL;
- //FIXME: variable kick height?
- if ( kickEnd && !VectorCompare( kickEnd, vec3_origin ) )
- {//they passed us the end point of the trace, just use that
- //this makes the trace flat
- VectorSet( traceOrg, ent->currentOrigin[0], ent->currentOrigin[1], kickEnd[2] );
- VectorCopy( kickEnd, traceEnd );
- }
- else
- {//extrude
- VectorSet( traceOrg, ent->currentOrigin[0], ent->currentOrigin[1], ent->currentOrigin[2]+ent->maxs[2]*0.5f );
- VectorMA( traceOrg, kickDist, kickDir, traceEnd );
- }
- gi.trace( &trace, traceOrg, kickMins, kickMaxs, traceEnd, ent->s.number, MASK_SHOT );//clipmask ok?
- if ( trace.fraction < 1.0f && !trace.startsolid && !trace.allsolid && trace.entityNum < ENTITYNUM_NONE )
- {
- hitEnt = &g_entities[trace.entityNum];
- if ( ent->client->ps.lastKickedEntNum != trace.entityNum )
- {
- TIMER_Remove( ent, "kickSoundDebounce" );
- ent->client->ps.lastKickedEntNum = trace.entityNum;
- }
- if ( hitEnt )
- {//we hit an entity
- if ( hitEnt->client )
- {
- if ( !(hitEnt->client->ps.pm_flags&PMF_TIME_KNOCKBACK)
- && TIMER_Done( hitEnt, "kickedDebounce" ) )//not already flying through air? Intended to stop multiple hits, but...
- {//FIXME: this should not always work
- if ( PM_InKnockDown( &hitEnt->client->ps )
- && !PM_InGetUp( &hitEnt->client->ps ) )
- {//don't hit people who are knocked down or being knocked down (okay to hit people getting up, though)
- return NULL;
- }
- if ( PM_InRoll( &hitEnt->client->ps ) )
- {//can't hit people who are rolling
- return NULL;
- }
- //don't hit same ent more than once per kick
- if ( hitEnt->takedamage )
- {//hurt it
- G_Damage( hitEnt, ent, ent, kickDir, trace.endpos, kickDamage, DAMAGE_NO_KNOCKBACK|DAMAGE_NO_KILL, MOD_MELEE );
- }
- //do kick hit sound and impact effect
- if ( TIMER_Done( ent, "kickSoundDebounce" ) )
- {
- if ( ent->client->ps.torsoAnim == BOTH_A7_HILT )
- {
- G_Sound( ent, G_SoundIndex( "sound/movers/objects/saber_slam" ) );
- }
- else
- {
- vec3_t fxOrg, fxDir;
- VectorCopy( kickDir, fxDir );
- VectorMA( trace.endpos, Q_flrand( 5.0f, 10.0f ), fxDir, fxOrg );
- VectorScale( fxDir, -1, fxDir );
- G_PlayEffect( G_EffectIndex( "melee/kick_impact" ), fxOrg, fxDir );
- //G_Sound( ent, G_SoundIndex( va( "sound/weapons/melee/punch%d", Q_irand( 1, 4 ) ) ) );
- }
- TIMER_Set( ent, "kickSoundDebounce", 2000 );
- }
- TIMER_Set( hitEnt, "kickedDebounce", 1000 );
- if ( ent->client->ps.torsoAnim == BOTH_A7_HILT )
- {//hit in head
- if ( hitEnt->health > 0 )
- {//knock down
- if ( kickPush >= 150.0f/*75.0f*/ && !Q_irand( 0, 1 ) )
- {//knock them down
- if ( !(hitEnt->flags&FL_NO_KNOCKBACK) )
- {
- G_Throw( hitEnt, kickDir, kickPush/3.0f );
- }
- G_Knockdown( hitEnt, ent, kickDir, 300, qtrue );
- }
- else
- {//force them to play a pain anim
- if ( hitEnt->s.number < MAX_CLIENTS )
- {
- NPC_SetPainEvent( hitEnt );
- }
- else
- {
- GEntity_PainFunc( hitEnt, ent, ent, hitEnt->currentOrigin, 0, MOD_MELEE );
- }
- }
- //just so we don't hit him again...
- hitEnt->client->ps.pm_flags |= PMF_TIME_KNOCKBACK;
- hitEnt->client->ps.pm_time = 100;
- }
- else
- {
- if ( !(hitEnt->flags&FL_NO_KNOCKBACK) )
- {
- G_Throw( hitEnt, kickDir, kickPush );
- }
- //see if we should play a better looking death on them
- G_ThrownDeathAnimForDeathAnim( hitEnt, trace.endpos );
- }
- }
- else if ( ent->client->ps.legsAnim == BOTH_GETUP_BROLL_B
- || ent->client->ps.legsAnim == BOTH_GETUP_BROLL_F
- || ent->client->ps.legsAnim == BOTH_GETUP_FROLL_B
- || ent->client->ps.legsAnim == BOTH_GETUP_FROLL_F )
- {
- if ( hitEnt->health > 0 )
- {//knock down
- if ( hitEnt->client->ps.groundEntityNum == ENTITYNUM_NONE )
- {//he's in the air? Send him flying back
- if ( !(hitEnt->flags&FL_NO_KNOCKBACK) )
- {
- G_Throw( hitEnt, kickDir, kickPush );
- }
- }
- else
- {
- //just so we don't hit him again...
- hitEnt->client->ps.pm_flags |= PMF_TIME_KNOCKBACK;
- hitEnt->client->ps.pm_time = 100;
- }
- //knock them down
- G_Knockdown( hitEnt, ent, kickDir, 300, qtrue );
- }
- else
- {
- if ( !(hitEnt->flags&FL_NO_KNOCKBACK) )
- {
- G_Throw( hitEnt, kickDir, kickPush );
- }
- //see if we should play a better looking death on them
- G_ThrownDeathAnimForDeathAnim( hitEnt, trace.endpos );
- }
- }
- else if ( hitEnt->health <= 0 )
- {//we kicked a dead guy
- //throw harder - FIXME: no matter how hard I push them, they don't go anywhere... corpses use less physics???
- if ( !(hitEnt->flags&FL_NO_KNOCKBACK) )
- {
- G_Throw( hitEnt, kickDir, kickPush*4 );
- }
- //see if we should play a better looking death on them
- G_ThrownDeathAnimForDeathAnim( hitEnt, trace.endpos );
- }
- else
- {
- if ( !(hitEnt->flags&FL_NO_KNOCKBACK) )
- {
- G_Throw( hitEnt, kickDir, kickPush );
- }
- if ( kickPush >= 150.0f/*75.0f*/ && !Q_irand( 0, 2 ) )
- {
- G_Knockdown( hitEnt, ent, kickDir, 300, qtrue );
- }
- else
- {
- G_Knockdown( hitEnt, ent, kickDir, kickPush, qtrue );
- }
- }
- }
- }
- else
- {//FIXME: don't do this in repeated frames... only allow 1 frame in kick to hit wall? The most extended one? Pass in a bool on that frame.
- if ( doSoundOnWalls )
- {//do kick hit sound and impact effect
- if ( TIMER_Done( ent, "kickSoundDebounce" ) )
- {
- if ( ent->client->ps.torsoAnim == BOTH_A7_HILT )
- {
- G_Sound( ent, G_SoundIndex( "sound/movers/objects/saber_slam" ) );
- }
- else
- {
- G_PlayEffect( G_EffectIndex( "melee/kick_impact" ), trace.endpos, trace.plane.normal );
- //G_Sound( ent, G_SoundIndex( va( "sound/weapons/melee/punch%d", Q_irand( 1, 4 ) ) ) );
- }
- TIMER_Set( ent, "kickSoundDebounce", 2000 );
- }
- }
- }
- }
- }
- return (hitEnt);
- }
- qboolean G_CheckRollSafety( gentity_t *self, int anim, float testDist )
- {
- vec3_t forward, right, testPos, angles;
- trace_t trace;
- int contents = (CONTENTS_SOLID|CONTENTS_BOTCLIP);
- if ( !self || !self->client )
- {
- return qfalse;
- }
- if ( self->s.number < MAX_CLIENTS )
- {//player
- contents |= CONTENTS_PLAYERCLIP;
- }
- else
- {//NPC
- contents |= CONTENTS_MONSTERCLIP;
- }
- if ( PM_InAttackRoll( self->client->ps.legsAnim ) )
- {//we don't care if people are in the way, we'll knock them down!
- contents &= ~CONTENTS_BODY;
- }
- angles[PITCH] = angles[ROLL] = 0;
- angles[YAW] = self->client->ps.viewangles[YAW];//Add ucmd.angles[YAW]?
- AngleVectors( angles, forward, right, NULL );
- switch ( anim )
- {
- case BOTH_GETUP_BROLL_R:
- case BOTH_GETUP_FROLL_R:
- VectorMA( self->currentOrigin, testDist, right, testPos );
- break;
- case BOTH_GETUP_BROLL_L:
- case BOTH_GETUP_FROLL_L:
- VectorMA( self->currentOrigin, -testDist, right, testPos );
- break;
- case BOTH_GETUP_BROLL_F:
- case BOTH_GETUP_FROLL_F:
- VectorMA( self->currentOrigin, testDist, forward, testPos );
- break;
- case BOTH_GETUP_BROLL_B:
- case BOTH_GETUP_FROLL_B:
- VectorMA( self->currentOrigin, -testDist, forward, testPos );
- break;
- default://FIXME: add normal rolls? Make generic for any forced-movement anim?
- return qtrue;
- break;
- }
- gi.trace( &trace, self->currentOrigin, self->mins, self->maxs, testPos, self->s.number, contents );
- if ( trace.fraction < 1.0f
- || trace.allsolid
- || trace.startsolid )
- {//inside something or will hit something
- return qfalse;
- }
- return qtrue;
- }
- void G_CamPullBackForLegsAnim( gentity_t *ent, qboolean useTorso = qfalse )
- {
- if ( (ent->s.number < MAX_CLIENTS||G_ControlledByPlayer(ent)) )
- {
- float animLength = PM_AnimLength( ent->client->clientInfo.animFileIndex, (useTorso?(animNumber_t)ent->client->ps.torsoAnim:(animNumber_t)ent->client->ps.legsAnim) );
- float elapsedTime = (float)(animLength-(useTorso?ent->client->ps.torsoAnimTimer:ent->client->ps.legsAnimTimer));
- float backDist = 0;
- if ( elapsedTime < animLength/2.0f )
- {//starting anim
- backDist = (elapsedTime/animLength)*120.0f;
- }
- else
- {//ending anim
- backDist = ((animLength-elapsedTime)/animLength)*120.0f;
- }
- cg.overrides.active |= CG_OVERRIDE_3RD_PERSON_RNG;
- cg.overrides.thirdPersonRange = cg_thirdPersonRange.value+backDist;
- }
- }
- void G_CamCircleForLegsAnim( gentity_t *ent )
- {
- if ( (ent->s.number < MAX_CLIENTS||G_ControlledByPlayer(ent)) )
- {
- float animLength = PM_AnimLength( ent->client->clientInfo.animFileIndex, (animNumber_t)ent->client->ps.legsAnim );
- float elapsedTime = (float)(animLength-ent->client->ps.legsAnimTimer);
- float angle = 0;
- angle = (elapsedTime/animLength)*360.0f;
- cg.overrides.active |= CG_OVERRIDE_3RD_PERSON_ANG;
- cg.overrides.thirdPersonAngle = cg_thirdPersonAngle.value+angle;
- }
- }
- qboolean G_GrabClient( gentity_t *ent, usercmd_t *ucmd )
- {
- gentity_t *bestEnt = NULL, *radiusEnts[ 128 ];
- int numEnts;
- const float radius = 100.0f;
- const float radiusSquared = (radius*radius);
- float bestDistSq = (radiusSquared+1.0f), distSq;
- int i;
- vec3_t boltOrg;
- numEnts = G_GetEntsNearBolt( ent, radiusEnts, radius, ent->handRBolt, boltOrg );
- for ( i = 0; i < numEnts; i++ )
- {
- if ( !radiusEnts[i]->inuse )
- {
- continue;
- }
-
- if ( radiusEnts[i] == ent )
- {//Skip the rancor ent
- continue;
- }
- if ( !radiusEnts[i]->inuse || radiusEnts[i]->health <= 0 )
- {//must be alive
- continue;
- }
-
- if ( radiusEnts[i]->client == NULL )
- {//must be a client
- continue;
- }
- if ( (radiusEnts[i]->client->ps.eFlags&EF_HELD_BY_RANCOR)
- ||(radiusEnts[i]->client->ps.eFlags&EF_HELD_BY_WAMPA)
- ||(radiusEnts[i]->client->ps.eFlags&EF_HELD_BY_SAND_CREATURE) )
- {//can't be one being held
- continue;
- }
-
- if ( PM_LockedAnim( radiusEnts[i]->client->ps.torsoAnim )
- || PM_LockedAnim( radiusEnts[i]->client->ps.legsAnim ) )
- {//don't interrupt
- continue;
- }
- if ( radiusEnts[i]->client->ps.groundEntityNum == ENTITYNUM_NONE )
- {//must be on ground
- continue;
- }
-
- if ( PM_InOnGroundAnim( &radiusEnts[i]->client->ps ) )
- {//must be standing up
- continue;
- }
- if ( fabs(radiusEnts[i]->currentOrigin[2]-ent->currentOrigin[2])>8.0f )
- {//have to be close in Z
- continue;
- }
- if ( !PM_HasAnimation( radiusEnts[i], BOTH_PLAYER_PA_1 ) )
- {//doesn't have matching anims
- continue;
- }
- distSq = DistanceSquared( radiusEnts[i]->currentOrigin, boltOrg );
- if ( distSq < bestDistSq )
- {
- bestDistSq = distSq;
- bestEnt = radiusEnts[i];
- }
- }
- if ( bestEnt != NULL )
- {
- int lockType = LOCK_KYLE_GRAB1;
- if ( ucmd->forwardmove > 0 )
- {
- lockType = LOCK_KYLE_GRAB3;
- }
- else if ( ucmd->forwardmove < 0 )
- {
- lockType = LOCK_KYLE_GRAB2;
- }
- WP_SabersCheckLock2( ent, bestEnt, (sabersLockMode_t)lockType );
- return qtrue;
- }
- return qfalse;
- }
- qboolean G_PullAttack( gentity_t *ent, usercmd_t *ucmd )
- {
- qboolean overridAngles = qfalse;
- if ( ent->client->ps.torsoAnim == BOTH_PULL_IMPALE_STAB
- || ent->client->ps.torsoAnim == BOTH_PULL_IMPALE_SWING )
- {//pulling
- if ( ent->NPC )
- {
- VectorClear( ent->client->ps.moveDir );
- }
- overridAngles = (PM_LockAngles( ent, ucmd )?qtrue:overridAngles);
- ucmd->forwardmove = ucmd->rightmove = ucmd->upmove = 0;
- }
- else if ( ent->client->ps.torsoAnim == BOTH_PULLED_INAIR_B
- || ent->client->ps.torsoAnim == BOTH_PULLED_INAIR_F )
- {//being pulled
- gentity_t *puller = &g_entities[ent->client->ps.pullAttackEntNum];
- if ( puller
- && puller->inuse
- && puller->client
- && ( puller->client->ps.torsoAnim == BOTH_PULL_IMPALE_STAB
- || puller->client->ps.torsoAnim == BOTH_PULL_IMPALE_SWING ) )
- {
- vec3_t pullDir;
- vec3_t pullPos;
- //calc where to pull me to
- /*
- VectorCopy( puller->client->ps.saber[0].blade[0].muzzlePoint, pullPos );
- VectorMA( pullPos, puller->client->ps.saber[0].blade[0].length*0.5f, puller->client->ps.saber[0].blade[0].muzzleDir, pullPos );
- */
- vec3_t pullerFwd;
- AngleVectors( puller->client->ps.viewangles, pullerFwd, NULL, NULL );
- VectorMA( puller->currentOrigin, (puller->maxs[0]*1.5f)+16.0f, pullerFwd, pullPos );
- //pull me towards that pos
- VectorSubtract( pullPos, ent->currentOrigin, pullDir );
- float pullDist = VectorNormalize( pullDir );
- int sweetSpotTime = (puller->client->ps.torsoAnim == BOTH_PULL_IMPALE_STAB)?1250:1350;
- float pullLength = PM_AnimLength( puller->client->clientInfo.animFileIndex, (animNumber_t)puller->client->ps.torsoAnim )-sweetSpotTime;
- if ( pullLength <= 0.25f )
- {
- pullLength = 0.25f;
- }
- //float pullTimeRemaining = ent->client->ps.pullAttackTime - level.time;
-
- //float pullSpeed = pullDist * (pullTimeRemaining/pullLength);
- float pullSpeed = (pullDist*1000.0f)/pullLength;
-
- VectorScale( pullDir, pullSpeed, ent->client->ps.velocity );
- //slide, if necessary
- ent->client->ps.pm_flags |= PMF_TIME_NOFRICTION;
- ent->client->ps.pm_time = 100;
- //make it so I don't actually hurt them when pulled at them...
- ent->forcePuller = puller->s.number;
- ent->forcePushTime = level.time + 100; // let the push effect last for 100 more ms
- //look at him
- PM_AdjustAnglesToPuller( ent, puller, ucmd, qboolean(ent->client->ps.legsAnim==BOTH_PULLED_INAIR_B) );
- overridAngles = qtrue;
- //don't move
- if ( ent->NPC )
- {
- VectorClear( ent->client->ps.moveDir );
- }
- ucmd->forwardmove = ucmd->rightmove = ucmd->upmove = 0;
- }
- }
- return overridAngles;
- }
- void G_FixMins( gentity_t *ent )
- {
- //do a trace to make sure it's okay
- float downdist = (DEFAULT_MINS_2-ent->mins[2]);
- vec3_t end={ent->currentOrigin[0],ent->currentOrigin[1],ent->currentOrigin[2]+downdist};
- trace_t trace;
- gi.trace( &trace, ent->currentOrigin, ent->mins, ent->maxs, end, ent->s.number, ent->clipmask );
- if ( !trace.allsolid && !trace.startsolid )
- {
- if ( trace.fraction >= 1.0f )
- {//all clear
- //drop the bottom of my bbox back down
- ent->mins[2] = DEFAULT_MINS_2;
- if ( ent->client )
- {
- ent->client->ps.pm_flags &= ~PMF_FIX_MINS;
- }
- }
- else
- {//move me up so the bottom of my bbox will be where the trace ended, at least
- //need to trace up, too
- float updist = ((1.0f-trace.fraction) * -downdist);
- end[2] = ent->currentOrigin[2]+updist;
- gi.trace( &trace, ent->currentOrigin, ent->mins, ent->maxs, end, ent->s.number, ent->clipmask );
- if ( !trace.allsolid && !trace.startsolid )
- {
- if ( trace.fraction >= 1.0f )
- {//all clear
- //move me up
- ent->currentOrigin[2] += updist;
- //drop the bottom of my bbox back down
- ent->mins[2] = DEFAULT_MINS_2;
- G_SetOrigin( ent, ent->currentOrigin );
- gi.linkentity( ent );
- if ( ent->client )
- {
- ent->client->ps.pm_flags &= ~PMF_FIX_MINS;
- }
- }
- else
- {//crap, no room to expand!
- if ( ent->client->ps.legsAnimTimer <= 200 )
- {//at the end of the anim, and we can't leave ourselves like this
- //so drop the maxs, put the mins back and move us up
- ent->maxs[2] += downdist;
- ent->currentOrigin[2] -= downdist;
- ent->mins[2] = DEFAULT_MINS_2;
- G_SetOrigin( ent, ent->currentOrigin );
- gi.linkentity( ent );
- //this way we'll be in a crouch when we're done
- ent->client->ps.legsAnimTimer = ent->client->ps.torsoAnimTimer = 0;
- ent->client->ps.pm_flags |= PMF_DUCKED;
- //FIXME: do we need to set a crouch anim here?
- if ( ent->client )
- {
- ent->client->ps.pm_flags &= ~PMF_FIX_MINS;
- }
- }
- }
- }//crap, stuck
- }
- }//crap, stuck!
- }
- qboolean G_CheckClampUcmd( gentity_t *ent, usercmd_t *ucmd )
- {
- qboolean overridAngles = qfalse;
- if ( ent->client->NPC_class == CLASS_PROTOCOL
- || ent->client->NPC_class == CLASS_R2D2
- || ent->client->NPC_class == CLASS_R5D2
- || ent->client->NPC_class == CLASS_GONK
- || ent->client->NPC_class == CLASS_MOUSE )
- {//these droids *cannot* strafe (looks bad)
- if ( ucmd->rightmove )
- {
- //clear the strafe
- ucmd->rightmove = 0;
- //movedir is invalid now, but PM_WalkMove will rebuild it from the ucmds, with the appropriate speed
- VectorClear( ent->client->ps.moveDir );
- }
- }
- if ( ent->client->ps.pullAttackEntNum < ENTITYNUM_WORLD
- && ent->client->ps.pullAttackTime > level.time )
- {
- return G_PullAttack( ent, ucmd );
- }
- if ( (ent->s.number < MAX_CLIENTS||G_ControlledByPlayer(ent))
- && ent->s.weapon == WP_MELEE
- && ent->client->ps.torsoAnim == BOTH_KYLE_GRAB )
- {//see if we grabbed enemy
- if ( ent->client->ps.torsoAnimTimer <= 200 )
- {//close to end of anim
- if ( G_GrabClient( ent, ucmd ) )
- {//grabbed someone!
- }
- else
- {//missed
- NPC_SetAnim( ent, SETANIM_BOTH, BOTH_KYLE_MISS, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
- ent->client->ps.weaponTime = ent->client->ps.torsoAnimTimer;
- }
- }
- ucmd->rightmove = ucmd->forwardmove = ucmd->upmove = 0;
- }
- if ( (!ent->s.number&&ent->aimDebounceTime>level.time)
- || (ent->client->ps.pm_time && (ent->client->ps.pm_flags&PMF_TIME_KNOCKBACK))
- || ent->forcePushTime > level.time )
- {//being knocked back, can't do anything!
- ucmd->buttons = 0;
- ucmd->forwardmove = 0;
- ucmd->rightmove = 0;
- ucmd->upmove = 0;
- if ( ent->NPC )
- {
- VectorClear( ent->client->ps.moveDir );
- }
- }
- overridAngles = (PM_AdjustAnglesForKnockdown( ent, ucmd, qfalse )?qtrue:overridAngles);
- if ( PM_GetupAnimNoMove( ent->client->ps.legsAnim ) )
- {
- ucmd->forwardmove = ucmd->rightmove = 0;//ucmd->upmove = ?
- }
- //check saberlock
- if ( ent->client->ps.saberLockTime > level.time )
- {//in saberlock
- ucmd->forwardmove = ucmd->rightmove = ucmd->upmove = 0;
- if ( ent->client->ps.saberLockTime - level.time > SABER_LOCK_DELAYED_TIME )
- {//2 second delay before either can push
- //FIXME: base on difficulty
- ucmd->buttons = 0;
- }
- else
- {
- ucmd->buttons &= ~(ucmd->buttons&~BUTTON_ATTACK);
- }
- overridAngles = (PM_AdjustAnglesForSaberLock( ent, ucmd )?qtrue:overridAngles);
- if ( ent->NPC )
- {
- VectorClear( ent->client->ps.moveDir );
- }
- }
- //check force drain
- if ( (ent->client->ps.forcePowersActive&(1<<FP_DRAIN)) )
- {//draining
- ucmd->forwardmove = ucmd->rightmove = ucmd->upmove = 0;
- ucmd->buttons &= ~(BUTTON_ATTACK|BUTTON_ALT_ATTACK|BUTTON_FORCE_FOCUS);
- if ( ent->NPC )
- {
- VectorClear( ent->client->ps.moveDir );
- }
- }
- if ( ent->client->ps.saberMove == LS_A_LUNGE )
- {//can't move during lunge
- ucmd->rightmove = ucmd->upmove = 0;
- if ( ent->client->ps.legsAnimTimer > 500 && (ent->s.number || !player_locked) )
- {
- ucmd->forwardmove = 127;
- }
- else
- {
- ucmd->forwardmove = 0;
- }
- if ( ent->NPC )
- {//invalid now
- VectorClear( ent->client->ps.moveDir );
- }
- }
- if ( ent->client->ps.legsAnim == BOTH_FORCEWALLRUNFLIP_ALT
- && ent->client->ps.legsAnimTimer )
- {
- vec3_t vFwd, fwdAng = {0,ent->currentAngles[YAW],0};
- AngleVectors( fwdAng, vFwd, NULL, NULL );
- if ( ent->client->ps.groundEntityNum == ENTITYNUM_NONE )
- {
- float savZ = ent->client->ps.velocity[2];
- VectorScale( vFwd, 100, ent->client->ps.velocity );
- ent->client->ps.velocity[2] = savZ;
- }
- ucmd->forwardmove = ucmd->rightmove = ucmd->upmove = 0;
- overridAngles = (PM_AdjustAnglesForWallRunUpFlipAlt( ent, ucmd )?qtrue:overridAngles);
- }
- if ( ent->client->ps.saberMove == LS_A_JUMP_T__B_ )
- {//can't move during leap
- if ( ent->client->ps.groundEntityNum != ENTITYNUM_NONE || (!ent->s.number && player_locked) )
- {//hit the ground
- ucmd->forwardmove = 0;
- }
- ucmd->rightmove = ucmd->upmove = 0;
- if ( ent->NPC )
- {//invalid now
- VectorClear( ent->client->ps.moveDir );
- }
- }
- if ( ent->client->ps.saberMove == LS_A_BACKFLIP_ATK
- && ent->client->ps.legsAnim == BOTH_JUMPATTACK7 )
- {//backflip attack
- if ( ent->client->ps.legsAnimTimer > 800 //not at end
- && PM_AnimLength( ent->client->clientInfo.animFileIndex, BOTH_JUMPATTACK7 ) - ent->client->ps.legsAnimTimer >= 400 )//not in beginning
- {//middle of anim
- if ( ent->client->ps.groundEntityNum != ENTITYNUM_NONE )
- {//still on ground?
- vec3_t yawAngles, backDir;
- //push backwards some?
- VectorSet( yawAngles, 0, ent->client->ps.viewangles[YAW]+180, 0 );
- AngleVectors( yawAngles, backDir, 0, 0 );
- VectorScale( backDir, 100, ent->client->ps.velocity );
- //jump!
- ent->client->ps.velocity[2] = 180;
- ent->client->ps.forceJumpZStart = ent->client->ps.origin[2];//so we don't take damage if we land at same height
- ent->client->ps.pm_flags |= PMF_JUMPING|PMF_SLOW_MO_FALL;
- //FIXME: NPCs yell?
- G_AddEvent( ent, EV_JUMP, 0 );
- G_SoundOnEnt( ent, CHAN_BODY, "sound/weapons/force/jump.wav" );
- ucmd->upmove = 0;//clear any actual jump command
- }
- }
- ucmd->forwardmove = ucmd->rightmove = ucmd->upmove = 0;
- if ( ent->NPC )
- {//invalid now
- VectorClear( ent->client->ps.moveDir );
- }
- }
- if ( ent->client->ps.legsAnim == BOTH_JUMPATTACK6
- && ent->client->ps.legsAnimTimer > 0 )
- {
- ucmd->forwardmove = ucmd->rightmove = ucmd->upmove = 0;
- //FIXME: don't slide off people/obstacles?
- if ( ent->client->ps.legsAnimTimer >= 100 //not at end
- && PM_AnimLength( ent->client->clientInfo.animFileIndex, BOTH_JUMPATTACK6 ) - ent->client->ps.legsAnimTimer >= 250 )//not in beginning
- {//middle of anim
- //push forward
- ucmd->forwardmove = 127;
- }
-
- if ( (ent->client->ps.legsAnimTimer >= 900 //not at end
- && PM_AnimLength( ent->client->clientInfo.animFileIndex, BOTH_JUMPATTACK6 ) - ent->client->ps.legsAnimTimer >= 950 ) //not in beginning
- || ( ent->client->ps.legsAnimTimer >= 1600
- && PM_AnimLength( ent->client->clientInfo.animFileIndex, BOTH_JUMPATTACK6 ) - ent->client->ps.legsAnimTimer >= 400 ) )//not in beginning
- {//one of the two jumps
- if ( ent->client->ps.groundEntityNum != ENTITYNUM_NONE )
- {//still on ground?
- //jump!
- ent->client->ps.velocity[2] = 250;
- ent->client->ps.forceJumpZStart = ent->client->ps.origin[2];//so we don't take damage if we land at same height
- ent->client->ps.pm_flags |= PMF_JUMPING;//|PMF_SLOW_MO_FALL;
- //FIXME: NPCs yell?
- G_AddEvent( ent, EV_JUMP, 0 );
- G_SoundOnEnt( ent, CHAN_BODY, "sound/weapons/force/jump.wav" );
- }
- else
- {//FIXME: if this is the second jump, maybe we should just stop the anim?
- }
- }
- //else
- {//disallow turning unless in the middle frame when you're on the ground
- //overridAngles = (PM_AdjustAnglesForDualJumpAttack( ent, ucmd )?qtrue:overridAngles);
- }
- //dynamically reduce bounding box to let character sail over heads of enemies
- if ( ( ent->client->ps.legsAnimTimer >= 1450
- && PM_AnimLength( ent->client->clientInfo.animFileIndex, BOTH_JUMPATTACK6 ) - ent->client->ps.legsAnimTimer >= 400 )
- ||(ent->client->ps.legsAnimTimer >= 400
- && PM_AnimLength( ent->client->clientInfo.animFileIndex, BOTH_JUMPATTACK6 ) - ent->client->ps.legsAnimTimer >= 1100 ) )
- {//in a part of the anim that we're pretty much sideways in, raise up the mins
- ent->mins[2] = 0;
- ent->client->ps.pm_flags |= PMF_FIX_MINS;
- }
- else if ( (ent->client->ps.pm_flags&PMF_FIX_MINS) )
- {//drop the mins back down
- G_FixMins( ent );
- }
- if ( ent->NPC )
- {//invalid now
- VectorClear( ent->client->ps.moveDir );
- }
- }
- else if ( (ent->client->ps.pm_flags&PMF_FIX_MINS) )
- {
- G_FixMins( ent );
- }
-
- if ( ( ent->client->ps.legsAnim == BOTH_BUTTERFLY_FL1
- && ent->client->ps.saberMove == LS_JUMPATTACK_STAFF_LEFT )
- || ( ent->client->ps.legsAnim == BOTH_BUTTERFLY_FR1
- && ent->client->ps.saberMove == LS_JUMPATTACK_STAFF_RIGHT )
- || ( ent->client->ps.legsAnim == BOTH_BUTTERFLY_RIGHT
- && ent->client->ps.saberMove == LS_BUTTERFLY_RIGHT )
- || ( ent->client->ps.legsAnim == BOTH_BUTTERFLY_LEFT
- && ent->client->ps.saberMove == LS_BUTTERFLY_LEFT ) )
- {//forward flip/spin attack
- ucmd->forwardmove = ucmd->rightmove = ucmd->upmove = 0;
- /*if ( ent->client->ps.legsAnim == BOTH_BUTTERFLY_FL1
- || ent->client->ps.legsAnim == BOTH_BUTTERFLY_FR1
- || ent->client->ps.legsAnim == BOTH_BUTTERFLY_LEFT
- || ent->client->ps.legsAnim == BOTH_BUTTERFLY RIGHT )*/
- {
- //FIXME: don't slide off people/obstacles?
- if ( ent->client->ps.legsAnim != BOTH_BUTTERFLY_LEFT
- && ent->client->ps.legsAnim != BOTH_BUTTERFLY_RIGHT )
- {
- if ( ent->client->ps.legsAnimTimer >= 100 //not at end
- && PM_AnimLength( ent->client->clientInfo.animFileIndex, (animNumber_t)ent->client->ps.legsAnim ) - ent->client->ps.legsAnimTimer >= 250 )//not in beginning
- {//middle of anim
- //push forward
- ucmd->forwardmove = 127;
- }
- }
-
- if ( ent->client->ps.legsAnimTimer >= 1700 && ent->client->ps.legsAnimTimer < 1800 )
- {//one of the two jumps
- if ( ent->client->ps.groundEntityNum != ENTITYNUM_NONE )
- {//still on ground?
- //jump!
- ent->client->ps.velocity[2] = 250;
- ent->client->ps.forceJumpZStart = ent->client->ps.origin[2];//so we don't take damage if we land at same height
- ent->client->ps.pm_flags |= PMF_JUMPING;//|PMF_SLOW_MO_FALL;
- //FIXME: NPCs yell?
- G_AddEvent( ent, EV_JUMP, 0 );
- G_SoundOnEnt( ent, CHAN_BODY, "sound/weapons/force/jump.wav" );
- }
- else
- {//FIXME: if this is the second jump, maybe we should just stop the anim?
- }
- }
- //disallow turning unless in the middle frame when you're on the ground
- //overridAngles = (PM_AdjustAnglesForDualJumpAttack( ent, ucmd )?qtrue:overridAngles);
- }
- if ( ent->NPC )
- {//invalid now
- VectorClear( ent->client->ps.moveDir );
- }
- }
-
- if ( ent->client->ps.legsAnim == BOTH_A7_SOULCAL
- && ent->client->ps.saberMove == LS_STAFF_SOULCAL )
- {//forward spinning staff attack
- ucmd->upmove = 0;
- if ( PM_CanRollFromSoulCal( &ent->client->ps ) )
- {
- ucmd->upmove = -127;
- }
- else
- {
- ucmd->rightmove = 0;
- //FIXME: don't slide off people/obstacles?
- if ( ent->client->ps.legsAnimTimer >= 2750 )
- {//not at end
- //push forward
- ucmd->forwardmove = 64;
- }
- else
- {
- ucmd->forwardmove = 0;
- }
- }
- if ( ent->client->ps.legsAnimTimer >= 2650
- && ent->client->ps.legsAnimTimer < 2850 )
- {//the jump
- if ( ent->client->ps.groundEntityNum != ENTITYNUM_NONE )
- {//still on ground?
- //jump!
- ent->client->ps.velocity[2] = 250;
- ent->client->ps.forceJumpZStart = ent->client->ps.origin[2];//so we don't take damage if we land at same height
- ent->client->ps.pm_flags |= PMF_JUMPING;//|PMF_SLOW_MO_FALL;
- //FIXME: NPCs yell?
- G_AddEvent( ent, EV_JUMP, 0 );
- G_SoundOnEnt( ent, CHAN_BODY, "sound/weapons/force/jump.wav" );
- }
- }
- if ( ent->NPC )
- {//invalid now
- VectorClear( ent->client->ps.moveDir );
- }
- }
- if ( ent->client->ps.torsoAnim == BOTH_LK_DL_S_T_SB_1_W )
- {
- G_CamPullBackForLegsAnim( ent, qtrue );
- }
- if ( ent->client->ps.torsoAnim == BOTH_A6_FB
- || ent->client->ps.torsoAnim == BOTH_A6_LR )
- {//can't turn or move during dual attack
- if ( ent->NPC )
- {
- VectorClear( ent->client->ps.moveDir );
- }
- overridAngles = (PM_LockAngles( ent, ucmd )?qtrue:overridAngles);
- ucmd->forwardmove = ucmd->rightmove = ucmd->upmove = 0;
- }
- else if ( ent->client->ps.legsAnim == BOTH_ROLL_STAB )
- {
- if ( ent->NPC )
- {
- VectorClear( ent->client->ps.moveDir );
- }
- overridAngles = (PM_LockAngles( ent, ucmd )?qtrue:overridAngles);
- ucmd->forwardmove = ucmd->rightmove = ucmd->upmove = 0;
- if ( ent->client->ps.legsAnimTimer )
- {
- ucmd->upmove = -127;
- }
- if ( ent->client->ps.dualSabers && ent->client->ps.saber[1].Active() )
- {
- G_CamPullBackForLegsAnim( ent );
- }
- }
- else if ( PM_SuperBreakLoseAnim( ent->client->ps.torsoAnim ) )
- {//can't turn during Kyle's grappling
- if ( ent->NPC )
- {
- VectorClear( ent->client->ps.moveDir );
- }
- overridAngles = (PM_LockAngles( ent, ucmd )?qtrue:overridAngles);
- ucmd->forwardmove = ucmd->rightmove = ucmd->upmove = 0;
- if ( ent->client->ps.legsAnim == BOTH_LK_DL_ST_T_SB_1_L )
- {
- PM_CmdForRoll( &ent->client->ps, ucmd );
- if ( ent->NPC )
- {//invalid now
- VectorClear( ent->client->ps.moveDir );
- }
- ent->client->ps.speed = 400;
- }
- }
- else if ( ent->client->ps.torsoAnim==BOTH_FORCE_DRAIN_GRAB_START
- || ent->client->ps.torsoAnim==BOTH_FORCE_DRAIN_GRAB_HOLD
- || ent->client->ps.torsoAnim==BOTH_FORCE_DRAIN_GRAB_END
- || ent->client->ps.legsAnim==BOTH_FORCE_DRAIN_GRABBED )
- {//can't turn or move
- if ( ent->s.number < MAX_CLIENTS
- || G_ControlledByPlayer(ent) )
- {//player
- float forceDrainAngle = 90.0f;
- if ( ent->client->ps.torsoAnim == BOTH_FORCE_DRAIN_GRAB_START )
- {//starting drain
- float animLength = PM_AnimLength( ent->client->clientInfo.animFileIndex, (animNumber_t)ent->client->ps.torsoAnim );
- float elapsedTime = (float)(animLength-ent->client->ps.legsAnimTimer);
- float angle = (elapsedTime/animLength)*forceDrainAngle;
- cg.overrides.active |= CG_OVERRIDE_3RD_PERSON_ANG;
- cg.overrides.thirdPersonAngle = cg_thirdPersonAngle.value+angle;
- }
- else if ( ent->client->ps.torsoAnim == BOTH_FORCE_DRAIN_GRAB_HOLD )
- {//draining
- cg.overrides.active |= CG_OVERRIDE_3RD_PERSON_ANG;
- cg.overrides.thirdPersonAngle = cg_thirdPersonAngle.value+forceDrainAngle;
- }
- else if ( ent->client->ps.torsoAnim == BOTH_FORCE_DRAIN_GRAB_END )
- {//ending drain
- float animLength = PM_AnimLength( ent->client->clientInfo.animFileIndex, (animNumber_t)ent->client->ps.torsoAnim );
- float elapsedTime = (float)(animLength-ent->client->ps.legsAnimTimer);
- float angle = forceDrainAngle-((elapsedTime/animLength)*forceDrainAngle);
- cg.overrides.active |= CG_OVERRIDE_3RD_PERSON_ANG;
- cg.overrides.thirdPersonAngle = cg_thirdPersonAngle.value+angle;
- }
- }
- if ( ent->NPC )
- {
- VectorClear( ent->client->ps.moveDir );
- }
- ucmd->forwardmove = ucmd->rightmove = ucmd->upmove = 0;
- overridAngles = PM_LockAngles( ent, ucmd )?qtrue:overridAngles;
- }
- else if ( ent->client->ps.legsAnim==BOTH_PLAYER_PA_1
- || ent->client->ps.legsAnim==BOTH_PLAYER_PA_2
- || ent->client->ps.legsAnim==BOTH_PLAYER_PA_3
- || ent->client->ps.legsAnim==BOTH_PLAYER_PA_3_FLY
- || ent->client->ps.legsAnim==BOTH_KYLE_PA_1
- || ent->client->ps.legsAnim==BOTH_KYLE_PA_2
- || ent->client->ps.legsAnim==BOTH_KYLE_PA_3 )
- {//can't turn during Kyle's grappling
- if ( ent->NPC )
- {
- VectorClear( ent->client->ps.moveDir );
- }
- overridAngles = PM_AdjustAnglesForGrapple( ent, ucmd )?qtrue:overridAngles;
- //if ( g_debugMelee->integer )
- {//actually do some damage during sequence
- int damage = 0;
- int dflags = (DAMAGE_NO_KNOCKBACK|DAMAGE_NO_ARMOR|DAMAGE_NO_KILL);
- if ( TIMER_Done( ent, "grappleDamageDebounce" ) )
- {
- switch ( ent->client->ps.legsAnim )
- {
- case BOTH_PLAYER_PA_1:
- if ( ent->client->ps.legsAnimTimer <= 3150
- && ent->client->ps.legsAnimTimer > 3050 )
- {
- TIMER_Set( ent, "grappleDamageDebounce", 150 );
- if ( ent->s.number < MAX_CLIENTS )
- {//special case
- damage = Q_irand( 1, 3 );
- }
- else
- {
- damage = Q_irand( 3, 5 );
- }
- }
- if ( ent->client->ps.legsAnimTimer <= 1150
- && ent->client->ps.legsAnimTimer > 10500 )
- {
- TIMER_Set( ent, "grappleDamageDebounce", 150 );
- if ( ent->s.number < MAX_CLIENTS )
- {//special case
- damage = Q_irand( 3, 5 );
- }
- else
- {
- damage = Q_irand( 5, 8 );
- }
- }
- if ( ent->client->ps.legsAnimTimer <= 100 )
- {
- TIMER_Set( ent, "grappleDamageDebounce", 150 );
- if ( ent->s.number < MAX_CLIENTS )
- {//special case
- damage = Q_irand( 5, 8 );
- }
- else
- {
- damage = Q_irand( 10, 20 );
- }
- dflags &= ~DAMAGE_NO_KILL;
- }
- break;
- case BOTH_PLAYER_PA_2:
- if ( ent->client->ps.legsAnimTimer <= 5700
- && ent->client->ps.legsAnimTimer > 5600 )
- {
- TIMER_Set( ent, "grappleDamageDebounce", 150 );
- if ( ent->s.number < MAX_CLIENTS )
- {//special case
- damage = Q_irand( 3, 6 );
- }
- else
- {
- damage = Q_irand( 7, 10 );
- }
- }
- if ( ent->client->ps.legsAnimTimer <= 5200
- && ent->client->ps.legsAnimTimer > 5100 )
- {
- TIMER_Set( ent, "grappleDamageDebounce", 150 );
- if ( ent->s.number < MAX_CLIENTS )
- {//special case
- damage = Q_irand( 1, 3 );
- }
- else
- {
- damage = Q_irand( 3, 5 );
- }
- }
- if ( ent->client->ps.legsAnimTimer <= 4550
- && ent->client->ps.legsAnimTimer > 4450 )
- {
- TIMER_Set( ent, "grappleDamageDebounce", 150 );
- if ( ent->s.number < MAX_CLIENTS )
- {//special case
- damage = Q_irand( 3, 6 );
- }
- else
- {
- damage = Q_irand( 10, 15 );
- }
- }
- if ( ent->client->ps.legsAnimTimer <= 3550
- && ent->client->ps.legsAnimTimer > 3450 )
- {
- TIMER_Set( ent, "grappleDamageDebounce", 150 );
- if ( ent->s.number < MAX_CLIENTS )
- {//special case
- damage = Q_irand( 3, 6 );
- }
- else
- {
- damage = Q_irand( 10, 20 );
- }
- dflags &= ~DAMAGE_NO_KILL;
- }
- break;
- case BOTH_PLAYER_PA_3:
- if ( ent->client->ps.legsAnimTimer <= 3800
- && ent->client->ps.legsAnimTimer > 3700 )
- {
- TIMER_Set( ent, "grappleDamageDebounce", 150 );
- if ( ent->s.number < MAX_CLIENTS )
- {//special case
- damage = Q_irand( 2, 5 );
- }
- else
- {
- damage = Q_irand( 5, 8 );
- }
- }
- if ( ent->client->ps.legsAnimTimer <= 3100
- && ent->client->ps.legsAnimTimer > 3000 )
- {
- TIMER_Set( ent, "grappleDamageDebounce", 150 );
- if ( ent->s.number < MAX_CLIENTS )
- {//special case
- damage = Q_irand( 2, 5 );
- }
- else
- {
- damage = Q_irand( 5, 8 );
- }
- }
- if ( ent->client->ps.legsAnimTimer <= 1650
- && ent->client->ps.legsAnimTimer > 1550 )
- {
- TIMER_Set( ent, "grappleDamageDebounce", 150 );
- if ( ent->s.number < MAX_CLIENTS )
- {//special case
- damage = Q_irand( 3, 6 );
- }
- else
- {
- damage = Q_irand( 7, 12 );
- }
- }
- break;
- case BOTH_PLAYER_PA_3_FLY:
- /*
- if ( ent->s.number < MAX_CLIENTS )
- {//special case
- if ( ent->client->ps.legsAnimTimer > PLAYER_KNOCKDOWN_HOLD_EXTRA_TIME
- && ent->client->ps.legsAnimTimer <= PLAYER_KNOCKDOWN_HOLD_EXTRA_TIME + 100 )
- {
- TIMER_Set( ent, "grappleDamageDebounce", 150 );
- damage = Q_irand( 4, 8 );
- dflags &= ~DAMAGE_NO_KILL;
- }
- }
- else*/ if ( ent->client->ps.legsAnimTimer <= 100 )
- {
- TIMER_Set( ent, "grappleDamageDebounce", 150 );
- damage = Q_irand( 10, 20 );
- dflags &= ~DAMAGE_NO_KILL;
- }
- break;
- }
- if ( damage )
- {
- G_Damage( ent, ent->enemy, ent->enemy, NULL, ent->currentOrigin, damage, dflags, MOD_MELEE );//MOD_IMPACT?
- }
- }
- }
- qboolean clearMove = qtrue;
- int endOfAnimTime = 100;
- if ( ent->s.number < MAX_CLIENTS
- && ent->client->ps.legsAnim == BOTH_PLAYER_PA_3_FLY )
- {//player holds extra long so you have more time to decide to do the quick getup
- //endOfAnimTime += PLAYER_KNOCKDOWN_HOLD_EXTRA_TIME;
- if ( ent->client->ps.legsAnimTimer <= endOfAnimTime )//PLAYER_KNOCKDOWN_HOLD_EXTRA_TIME )
- {
- clearMove = qfalse;
- }
- }
- if ( clearMove )
- {
- ucmd->forwardmove = ucmd->rightmove = ucmd->upmove = 0;
- }
- if ( ent->client->ps.legsAnimTimer <= endOfAnimTime )
- {//pretty much done with the anim, so get up now
- if ( ent->client->ps.legsAnim==BOTH_PLAYER_PA_3 )
- {
- vec3_t ang = {10,ent->currentAngles[YAW],0};
- vec3_t throwDir;
- AngleVectors( ang, throwDir, NULL, NULL );
- if ( !(ent->flags&FL_NO_KNOCKBACK) )
- {
- G_Throw( ent, throwDir, -100 );
- }
- ent->client->ps.legsAnimTimer = ent->client->ps.torsoAnimTimer = 0;
- NPC_SetAnim( ent, SETANIM_BOTH, BOTH_PLAYER_PA_3_FLY, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
- ent->client->ps.weaponTime = ent->client->ps.legsAnimTimer;
- /*
- if ( ent->s.number < MAX_CLIENTS )
- {//player holds extra long so you have more time to decide to do the quick getup
- ent->client->ps.legsAnimTimer += PLAYER_KNOCKDOWN_HOLD_EXTRA_TIME;
- ent->client->ps.torsoAnimTimer += PLAYER_KNOCKDOWN_HOLD_EXTRA_TIME;
- }
- */
- //force-thrown - FIXME: start earlier?
- ent->forcePushTime = level.time + 500;
- }
- else if ( ent->client->ps.legsAnim==BOTH_PLAYER_PA_1 )
- {
- vec3_t ang = {10,ent->currentAngles[YAW],0};
- vec3_t throwDir;
- AngleVectors( ang, throwDir, NULL, NULL );
- if ( !(ent->flags&FL_NO_KNOCKBACK) )
- {
- G_Throw( ent, throwDir, -100 );
- }
- ent->client->ps.legsAnimTimer = ent->client->ps.torsoAnimTimer = 0;
- NPC_SetAnim( ent, SETANIM_BOTH, BOTH_KNOCKDOWN2, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
- ent->client->ps.weaponTime = ent->client->ps.legsAnimTimer;
- if ( ent->s.number < MAX_CLIENTS )
- {//player holds extra long so you have more time to decide to do the quick getup
- ent->client->ps.legsAnimTimer += PLAYER_KNOCKDOWN_HOLD_EXTRA_TIME;
- ent->client->ps.torsoAnimTimer += PLAYER_KNOCKDOWN_HOLD_EXTRA_TIME;
- }
- }
- //FIXME: should end so you can do a getup
- /*
- else if ( ent->client->ps.legsAnim==BOTH_PLAYER_PA_2 )
- {
- ent->client->ps.legsAnimTimer = ent->client->ps.torsoAnimTimer = 0;
- NPC_SetAnim( ent, SETANIM_BOTH, BOTH_GETUP1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
- ent->client->ps.weaponTime = ent->client->ps.legsAnimTimer;
- }
- */
- }
- if ( d_slowmodeath->integer <= 3
- && ent->s.number < MAX_CLIENTS )
- {//no matrix effect, so slide the camera back and to the side
- G_CamPullBackForLegsAnim( ent );
- G_CamCircleForLegsAnim( ent );
- }
- }
- else if ( ent->client->ps.legsAnim == BOTH_FORCELONGLEAP_START
- || ent->client->ps.legsAnim == BOTH_FORCELONGLEAP_ATTACK
- || ent->client->ps.legsAnim == BOTH_FORCELONGLEAP_LAND )
- {//can't turn during force leap
- if ( ent->NPC )
- {
- VectorClear( ent->client->ps.moveDir );
- }
- overridAngles = PM_AdjustAnglesForLongJump( ent, ucmd )?qtrue:overridAngles;
- }
- else if ( PM_KickingAnim( ent->client->ps.legsAnim )
- || ent->client->ps.legsAnim == BOTH_ARIAL_F1
- || ent->client->ps.legsAnim == BOTH_ARIAL_LEFT
- || ent->client->ps.legsAnim == BOTH_ARIAL_RIGHT
- || ent->client->ps.legsAnim == BOTH_CARTWHEEL_LEFT
- || ent->client->ps.legsAnim == BOTH_CARTWHEEL_RIGHT
- || ent->client->ps.legsAnim == BOTH_JUMPATTACK7
- || ent->client->ps.legsAnim == BOTH_BUTTERFLY_FL1
- || ent->client->ps.legsAnim == BOTH_BUTTERFLY_FR1
- || ent->client->ps.legsAnim == BOTH_BUTTERFLY_RIGHT
- || ent->client->ps.legsAnim == BOTH_BUTTERFLY_LEFT
- || ent->client->ps.legsAnim == BOTH_A7_SOULCAL )
- {//can't move, check for damage frame
- if ( PM_KickingAnim( ent->client->ps.legsAnim ) )
- {
- ucmd->forwardmove = ucmd->rightmove = 0;
- if ( ent->NPC )
- {
- VectorClear( ent->client->ps.moveDir );
- }
- }
- vec3_t kickDir={0,0,0}, kickDir2={0,0,0}, kickEnd={0,0,0}, kickEnd2={0,0,0}, fwdAngs = {0,ent->client->ps.viewangles[YAW],0};
- float animLength = PM_AnimLength( ent->client->clientInfo.animFileIndex, (animNumber_t)ent->client->ps.legsAnim );
- float elapsedTime = (float)(animLength-ent->client->ps.legsAnimTimer);
- float remainingTime = (animLength-elapsedTime);
- float kickDist = (ent->maxs[0]*1.5f)+STAFF_KICK_RANGE+8.0f;//fudge factor of 8
- float kickDist2 = kickDist;
- int kickDamage = Q_irand( 3, 8 );
- int kickDamage2 = Q_irand( 3, 8 );
- int kickPush = Q_flrand( 100.0f, 200.0f );//Q_flrand( 50.0f, 100.0f );
- int kickPush2 = Q_flrand( 100.0f, 200.0f );//Q_flrand( 50.0f, 100.0f );
- qboolean doKick = qfalse;
- qboolean doKick2 = qfalse;
- qboolean kickSoundOnWalls = qfalse;
- //HMM... or maybe trace from origin to footRBolt/footLBolt? Which one? G2 trace? Will do hitLoc, if so...
- if ( ent->client->ps.torsoAnim == BOTH_A7_HILT )
- {
- if ( elapsedTime >= 250 && remainingTime >= 250 )
- {//front
- doKick = qtrue;
- if ( ent->handRBolt != -1 )
- {//actually trace to a bolt
- G_GetBoltPosition( ent, ent->handRBolt, kickEnd );
- VectorSubtract( kickEnd, ent->currentOrigin, kickDir );
- kickDir[2] = 0;//ah, flatten it, I guess...
- VectorNormalize( kickDir );
- }
- else
- {//guess
- AngleVectors( fwdAngs, kickDir, NULL, NULL );
- }
- }
- }
- else
- {
- switch ( ent->client->ps.legsAnim )
- {
- case BOTH_A7_SOULCAL:
- kickPush = Q_flrand( 150.0f, 250.0f );//Q_flrand( 75.0f, 125.0f );
- if ( elapsedTime >= 1400 && elapsedTime <= 1500 )
- {//right leg
- doKick = qtrue;
- if ( ent->footRBolt != -1 )
- {//actually trace to a bolt
- G_GetBoltPosition( ent, ent->footRBolt, kickEnd );
- VectorSubtract( kickEnd, ent->currentOrigin, kickDir );
- kickDir[2] = 0;//ah, flatten it, I guess...
- VectorNormalize( kickDir );
- }
- else
- {//guess
- AngleVectors( fwdAngs, kickDir, NULL, NULL );
- }
- }
- break;
- case BOTH_ARIAL_F1:
- if ( elapsedTime >= 550 && elapsedTime <= 1000 )
- {//right leg
- doKick = qtrue;
- if ( ent->footRBolt != -1 )
- {//actually trace to a bolt
- G_GetBoltPosition( ent, ent->footRBolt, kickEnd );
- VectorSubtract( kickEnd, ent->currentOrigin, kickDir );
- kickDir[2] = 0;//ah, flatten it, I guess...
- VectorNormalize( kickDir );
- }
- else
- {//guess
- AngleVectors( fwdAngs, kickDir, NULL, NULL );
- }
- }
- if ( elapsedTime >= 800 && elapsedTime <= 1200 )
- {//left leg
- doKick2 = qtrue;
- if ( ent->footLBolt != -1 )
- {//actually trace to a bolt
- G_GetBoltPosition( ent, ent->footLBolt, kickEnd2 );
- VectorSubtract( kickEnd2, ent->currentOrigin, kickDir2 );
- kickDir2[2] = 0;//ah, flatten it, I guess...
- VectorNormalize( kickDir2 );
- }
- else
- {//guess
- AngleVectors( fwdAngs, kickDir2, NULL, NULL );
- }
- }
- break;
- case BOTH_ARIAL_LEFT:
- case BOTH_ARIAL_RIGHT:
- if ( elapsedTime >= 200 && elapsedTime <= 600 )
- {//lead leg
- int footBolt = (ent->client->ps.legsAnim==BOTH_ARIAL_LEFT)?ent->footRBolt:ent->footLBolt;//mirrored anims
- doKick = qtrue;
- if ( footBolt != -1 )
- {//actually trace to a bolt
- G_GetBoltPosition( ent, footBolt, kickEnd );
- VectorSubtract( kickEnd, ent->currentOrigin, kickDir );
- kickDir[2] = 0;//ah, flatten it, I guess...
- VectorNormalize( kickDir );
- }
- else
- {//guess
- AngleVectors( fwdAngs, kickDir, NULL, NULL );
- }
- }
- if ( elapsedTime >= 400 && elapsedTime <= 850 )
- {//trailing leg
- int footBolt = (ent->client->ps.legsAnim==BOTH_ARIAL_LEFT)?ent->footLBolt:ent->footRBolt;//mirrored anims
- doKick2 = qtrue;
- if ( footBolt != -1 )
- {//actually trace to a bolt
- G_GetBoltPosition( ent, footBolt, kickEnd2 );
- VectorSubtract( kickEnd2, ent->currentOrigin, kickDir2 );
- kickDir2[2] = 0;//ah, flatten it, I guess...
- VectorNormalize( kickDir2 );
- }
- else
- {//guess
- AngleVectors( fwdAngs, kickDir2, NULL, NULL );
- }
- }
- break;
- case BOTH_CARTWHEEL_LEFT:
- case BOTH_CARTWHEEL_RIGHT:
- if ( elapsedTime >= 200 && elapsedTime <= 600 )
- {//lead leg
- int footBolt = (ent->client->ps.legsAnim==BOTH_CARTWHEEL_LEFT)?ent->footRBolt:ent->footLBolt;//mirrored anims
- doKick = qtrue;
- if ( footBolt != -1 )
- {//actually trace to a bolt
- G_GetBoltPosition( ent, footBolt, kickEnd );
- VectorSubtract( kickEnd, ent->currentOrigin, kickDir );
- kickDir[2] = 0;//ah, flatten it, I guess...
- VectorNormalize( kickDir );
- }
- else
- {//guess
- AngleVectors( fwdAngs, kickDir, NULL, NULL );
- }
- }
- if ( elapsedTime >= 350 && elapsedTime <= 650 )
- {//trailing leg
- int footBolt = (ent->client->ps.legsAnim==BOTH_CARTWHEEL_LEFT)?ent->footLBolt:ent->footRBolt;//mirrored anims
- doKick2 = qtrue;
- if ( footBolt != -1 )
- {//actually trace to a bolt
- G_GetBoltPosition( ent, footBolt, kickEnd2 );
- VectorSubtract( kickEnd2, ent->currentOrigin, kickDir2 );
- kickDir2[2] = 0;//ah, flatten it, I guess...
- VectorNormalize( kickDir2 );
- }
- else
- {//guess
- AngleVectors( fwdAngs, kickDir2, NULL, NULL );
- }
- }
- break;
- case BOTH_JUMPATTACK7:
- if ( elapsedTime >= 300 && elapsedTime <= 900 )
- {//right knee
- doKick = qtrue;
- if ( ent->kneeRBolt != -1 )
- {//actually trace to a bolt
- G_GetBoltPosition( ent, ent->kneeRBolt, kickEnd );
- VectorSubtract( kickEnd, ent->currentOrigin, kickDir );
- kickDir[2] = 0;//ah, flatten it, I guess...
- VectorNormalize( kickDir );
- }
- else
- {//guess
- AngleVectors( fwdAngs, kickDir, NULL, NULL );
- }
- }
- if ( elapsedTime >= 600 && elapsedTime <= 900 )
- {//left leg
- doKick2 = qtrue;
- if ( ent->footLBolt != -1 )
- {//actually trace to a bolt
- G_GetBoltPosition( ent, ent->footLBolt, kickEnd2 );
- VectorSubtract( kickEnd2, ent->currentOrigin, kickDir2 );
- kickDir2[2] = 0;//ah, flatten it, I guess...
- VectorNormalize( kickDir2 );
- }
- else
- {//guess
- AngleVectors( fwdAngs, kickDir2, NULL, NULL );
- }
- }
- break;
- case BOTH_BUTTERFLY_FL1:
- case BOTH_BUTTERFLY_FR1:
- if ( elapsedTime >= 950 && elapsedTime <= 1300 )
- {//lead leg
- int footBolt = (ent->client->ps.legsAnim==BOTH_BUTTERFLY_FL1)?ent->footRBolt:ent->footLBolt;//mirrored anims
- doKick = qtrue;
- if ( footBolt != -1 )
- {//actually trace to a bolt
- G_GetBoltPosition( ent, footBolt, kickEnd );
- VectorSubtract( kickEnd, ent->currentOrigin, kickDir );
- kickDir[2] = 0;//ah, flatten it, I guess...
- VectorNormalize( kickDir );
- }
- else
- {//guess
- AngleVectors( fwdAngs, kickDir, NULL, NULL );
- }
- }
- if ( elapsedTime >= 1150 && elapsedTime <= 1600 )
- {//trailing leg
- int footBolt = (ent->client->ps.legsAnim==BOTH_BUTTERFLY_FL1)?ent->footLBolt:ent->footRBolt;//mirrored anims
- doKick2 = qtrue;
- if ( footBolt != -1 )
- {//actually trace to a bolt
- G_GetBoltPosition( ent, footBolt, kickEnd2 );
- VectorSubtract( kickEnd2, ent->currentOrigin, kickDir2 );
- kickDir2[2] = 0;//ah, flatten it, I guess...
- VectorNormalize( kickDir2 );
- }
- else
- {//guess
- AngleVectors( fwdAngs, kickDir2, NULL, NULL );
- }
- }
- break;
- case BOTH_BUTTERFLY_LEFT:
- case BOTH_BUTTERFLY_RIGHT:
- if ( (elapsedTime >= 100 && elapsedTime <= 450)
- || (elapsedTime >= 1100 && elapsedTime <= 1350) )
- {//lead leg
- int footBolt = (ent->client->ps.legsAnim==BOTH_BUTTERFLY_LEFT)?ent->footLBolt:ent->footRBolt;//mirrored anims
- doKick = qtrue;
- if ( footBolt != -1 )
- {//actually trace to a bolt
- G_GetBoltPosition( ent, footBolt, kickEnd );
- VectorSubtract( kickEnd, ent->currentOrigin, kickDir );
- kickDir[2] = 0;//ah, flatten it, I guess...
- VectorNormalize( kickDir );
- }
- else
- {//guess
- AngleVectors( fwdAngs, kickDir, NULL, NULL );
- }
- }
- if ( elapsedTime >= 900 && elapsedTime <= 1600 )
- {//trailing leg
- int footBolt = (ent->client->ps.legsAnim==BOTH_BUTTERFLY_LEFT)?ent->footRBolt:ent->footLBolt;//mirrored anims
- doKick2 = qtrue;
- if ( footBolt != -1 )
- {//actually trace to a bolt
- G_GetBoltPosition( ent, footBolt, kickEnd2 );
- VectorSubtract( kickEnd2, ent->currentOrigin, kickDir2 );
- kickDir2[2] = 0;//ah, flatten it, I guess...
- VectorNormalize( kickDir2 );
- }
- else
- {//guess
- AngleVectors( fwdAngs, kickDir2, NULL, NULL );
- }
- }
- break;
- case BOTH_GETUP_BROLL_B:
- case BOTH_GETUP_BROLL_F:
- case BOTH_GETUP_FROLL_B:
- case BOTH_GETUP_FROLL_F:
- kickPush = Q_flrand( 150.0f, 250.0f );//Q_flrand( 75.0f, 125.0f );
- if ( elapsedTime >= 250 && remainingTime >= 250 )
- {//front
- doKick = qtrue;
- if ( ent->footRBolt != -1 )
- {//actually trace to a bolt
- G_GetBoltPosition( ent, ent->footRBolt, kickEnd );
- VectorSubtract( kickEnd, ent->currentOrigin, kickDir );
- kickDir[2] = 0;//ah, flatten it, I guess...
- VectorNormalize( kickDir );
- }
- else
- {//guess
- AngleVectors( fwdAngs, kickDir, NULL, NULL );
- }
- }
- if ( ent->client->ps.legsAnim == BOTH_GETUP_BROLL_B
- || ent->client->ps.legsAnim == BOTH_GETUP_FROLL_B )
- {//rolling back, pull back the view
- G_CamPullBackForLegsAnim( ent );
- }
- break;
- case BOTH_A7_KICK_F_AIR:
- kickPush = Q_flrand( 150.0f, 250.0f );//Q_flrand( 75.0f, 125.0f );
- kickSoundOnWalls = qtrue;
- if ( elapsedTime >= 100 && remainingTime >= 500 )
- {//front
- doKick = qtrue;
- if ( ent->footRBolt != -1 )
- {//actually trace to a bolt
- G_GetBoltPosition( ent, ent->footRBolt, kickEnd );
- VectorSubtract( kickEnd, ent->currentOrigin, kickDir );
- kickDir[2] = 0;//ah, flatten it, I guess...
- VectorNormalize( kickDir );
- }
- else
- {//guess
- AngleVectors( fwdAngs, kickDir, NULL, NULL );
- }
- }
- break;
- case BOTH_A7_KICK_F:
- kickSoundOnWalls = qtrue;
- //FIXME: push forward?
- if ( elapsedTime >= 250 && remainingTime >= 250 )
- {//front
- doKick = qtrue;
- if ( ent->footRBolt != -1 )
- {//actually trace to a bolt
- G_GetBoltPosition( ent, ent->footRBolt, kickEnd );
- VectorSubtract( kickEnd, ent->currentOrigin, kickDir );
- kickDir[2] = 0;//ah, flatten it, I guess...
- VectorNormalize( kickDir );
- }
- else
- {//guess
- AngleVectors( fwdAngs, kickDir, NULL, NULL );
- }
- }
- break;
- case BOTH_A7_KICK_B_AIR:
- kickPush = Q_flrand( 150.0f, 250.0f );//Q_flrand( 75.0f, 125.0f );
- kickSoundOnWalls = qtrue;
- if ( elapsedTime >= 100 && remainingTime >= 400 )
- {//back
- doKick = qtrue;
- if ( ent->footRBolt != -1 )
- {//actually trace to a bolt
- G_GetBoltPosition( ent, ent->footRBolt, kickEnd );
- VectorSubtract( kickEnd, ent->currentOrigin, kickDir );
- kickDir[2] = 0;//ah, flatten it, I guess...
- VectorNormalize( kickDir );
- }
- else
- {//guess
- AngleVectors( fwdAngs, kickDir, NULL, NULL );
- VectorScale( kickDir, -1, kickDir );
- }
- }
- break;
- case BOTH_A7_KICK_B:
- kickSoundOnWalls = qtrue;
- if ( elapsedTime >= 250 && remainingTime >= 250 )
- {//back
- doKick = qtrue;
- if ( ent->footRBolt != -1 )
- {//actually trace to a bolt
- G_GetBoltPosition( ent, ent->footRBolt, kickEnd );
- VectorSubtract( kickEnd, ent->currentOrigin, kickDir );
- kickDir[2] = 0;//ah, flatten it, I guess...
- VectorNormalize( kickDir );
- }
- else
- {//guess
- AngleVectors( fwdAngs, kickDir, NULL, NULL );
- VectorScale( kickDir, -1, kickDir );
- }
- }
- break;
- case BOTH_A7_KICK_R_AIR:
- kickPush = Q_flrand( 150.0f, 250.0f );//Q_flrand( 75.0f, 125.0f );
- kickSoundOnWalls = qtrue;
- if ( elapsedTime >= 150 && remainingTime >= 300 )
- {//left
- doKick = qtrue;
- if ( ent->footLBolt != -1 )
- {//actually trace to a bolt
- G_GetBoltPosition( ent, ent->footLBolt, kickEnd );
- VectorSubtract( kickEnd, ent->currentOrigin, kickDir );
- kickDir[2] = 0;//ah, flatten it, I guess...
- VectorNormalize( kickDir );
- }
- else
- {//guess
- AngleVectors( fwdAngs, NULL, kickDir, NULL );
- VectorScale( kickDir, -1, kickDir );
- }
- }
- break;
- case BOTH_A7_KICK_R:
- kickSoundOnWalls = qtrue;
- //FIXME: push right?
- if ( elapsedTime >= 250 && remainingTime >= 250 )
- {//right
- doKick = qtrue;
- if ( ent->footRBolt != -1 )
- {//actually trace to a bolt
- G_GetBoltPosition( ent, ent->footRBolt, kickEnd );
- VectorSubtract( kickEnd, ent->currentOrigin, kickDir );
- kickDir[2] = 0;//ah, flatten it, I guess...
- VectorNormalize( kickDir );
- }
- else
- {//guess
- AngleVectors( fwdAngs, NULL, kickDir, NULL );
- }
- }
- break;
- case BOTH_A7_KICK_L_AIR:
- kickPush = Q_flrand( 150.0f, 250.0f );//Q_flrand( 75.0f, 125.0f );
- kickSoundOnWalls = qtrue;
- if ( elapsedTime >= 150 && remainingTime >= 300 )
- {//left
- doKick = qtrue;
- if ( ent->footLBolt != -1 )
- {//actually trace to a bolt
- G_GetBoltPosition( ent, ent->footLBolt, kickEnd );
- VectorSubtract( kickEnd, ent->currentOrigin, kickDir );
- kickDir[2] = 0;//ah, flatten it, I guess...
- VectorNormalize( kickDir );
- }
- else
- {//guess
- AngleVectors( fwdAngs, NULL, kickDir, NULL );
- VectorScale( kickDir, -1, kickDir );
- }
- }
- break;
- case BOTH_A7_KICK_L:
- kickSoundOnWalls = qtrue;
- //FIXME: push left?
- if ( elapsedTime >= 250 && remainingTime >= 250 )
- {//left
- doKick = qtrue;
- if ( ent->footLBolt != -1 )
- {//actually trace to a bolt
- G_GetBoltPosition( ent, ent->footLBolt, kickEnd );
- VectorSubtract( kickEnd, ent->currentOrigin, kickDir );
- kickDir[2] = 0;//ah, flatten it, I guess...
- VectorNormalize( kickDir );
- }
- else
- {//guess
- AngleVectors( fwdAngs, NULL, kickDir, NULL );
- VectorScale( kickDir, -1, kickDir );
- }
- }
- break;
- case BOTH_A7_KICK_S:
- kickPush = Q_flrand( 150.0f, 250.0f );//Q_flrand( 75.0f, 125.0f );
- if ( ent->footRBolt != -1 )
- {//actually trace to a bolt
- if ( elapsedTime >= 550
- && elapsedTime <= 1050 )
- {
- doKick = qtrue;
- G_GetBoltPosition( ent, ent->footRBolt, kickEnd );
- VectorSubtract( kickEnd, ent->currentOrigin, kickDir );
- kickDir[2] = 0;//ah, flatten it, I guess...
- VectorNormalize( kickDir );
- //NOTE: have to fudge this a little because it's not getting enough range with the anim as-is
- VectorMA( kickEnd, 8.0f, kickDir, kickEnd );
- }
- }
- else
- {//guess
- if ( elapsedTime >= 400 && elapsedTime < 500 )
- {//front
- doKick = qtrue;
- AngleVectors( fwdAngs, kickDir, NULL, NULL );
- }
- else if ( elapsedTime >= 500 && elapsedTime < 600 )
- {//front-right?
- doKick = qtrue;
- fwdAngs[YAW] += 45;
- AngleVectors( fwdAngs, kickDir, NULL, NULL );
- }
- else if ( elapsedTime >= 600 && elapsedTime < 700 )
- {//right
- doKick = qtrue;
- AngleVectors( fwdAngs, NULL, kickDir, NULL );
- }
- else if ( elapsedTime >= 700 && elapsedTime < 800 )
- {//back-right?
- doKick = qtrue;
- fwdAngs[YAW] += 45;
- AngleVectors( fwdAngs, NULL, kickDir, NULL );
- }
- else if ( elapsedTime >= 800 && elapsedTime < 900 )
- {//back
- doKick = qtrue;
- AngleVectors( fwdAngs, kickDir, NULL, NULL );
- VectorScale( kickDir, -1, kickDir );
- }
- else if ( elapsedTime >= 900 && elapsedTime < 1000 )
- {//back-left?
- doKick = qtrue;
- fwdAngs[YAW] += 45;
- AngleVectors( fwdAngs, kickDir, NULL, NULL );
- }
- else if ( elapsedTime >= 1000 && elapsedTime < 1100 )
- {//left
- doKick = qtrue;
- AngleVectors( fwdAngs, NULL, kickDir, NULL );
- VectorScale( kickDir, -1, kickDir );
- }
- else if ( elapsedTime >= 1100 && elapsedTime < 1200 )
- {//front-left?
- doKick = qtrue;
- fwdAngs[YAW] += 45;
- AngleVectors( fwdAngs, NULL, kickDir, NULL );
- VectorScale( kickDir, -1, kickDir );
- }
- }
- break;
- case BOTH_A7_KICK_BF:
- kickPush = Q_flrand( 150.0f, 250.0f );//Q_flrand( 75.0f, 125.0f );
- if ( elapsedTime < 1500 )
- {//auto-aim!
- overridAngles = PM_AdjustAnglesForBFKick( ent, ucmd, fwdAngs, qboolean(elapsedTime<850) )?qtrue:overridAngles;
- //FIXME: if we haven't done the back kick yet and there's no-one there to
- // kick anymore, go into some anim that returns us to our base stance
- }
- if ( ent->footRBolt != -1 )
- {//actually trace to a bolt
- if ( ( elapsedTime >= 750 && elapsedTime < 850 )
- || ( elapsedTime >= 1400 && elapsedTime < 1500 ) )
- {//right, though either would do
- doKick = qtrue;
- G_GetBoltPosition( ent, ent->footRBolt, kickEnd );
- VectorSubtract( kickEnd, ent->currentOrigin, kickDir );
- kickDir[2] = 0;//ah, flatten it, I guess...
- VectorNormalize( kickDir );
- //NOTE: have to fudge this a little because it's not getting enough range with the anim as-is
- VectorMA( kickEnd, 8, kickDir, kickEnd );
- }
- }
- else
- {//guess
- if ( elapsedTime >= 250 && elapsedTime < 350 )
- {//front
- doKick = qtrue;
- AngleVectors( fwdAngs, kickDir, NULL, NULL );
- }
- else if ( elapsedTime >= 350 && elapsedTime < 450 )
- {//back
- doKick = qtrue;
- AngleVectors( fwdAngs, kickDir, NULL, NULL );
- VectorScale( kickDir, -1, kickDir );
- }
- }
- break;
- case BOTH_A7_KICK_RL:
- kickSoundOnWalls = qtrue;
- kickPush = Q_flrand( 150.0f, 250.0f );//Q_flrand( 75.0f, 125.0f );
- //FIXME: auto aim at enemies on the side of us?
- //overridAngles = PM_AdjustAnglesForRLKick( ent, ucmd, fwdAngs, qboolean(elapsedTime<850) )?qtrue:overridAngles;
- if ( elapsedTime >= 250 && elapsedTime < 350 )
- {//right
- doKick = qtrue;
- if ( ent->footRBolt != -1 )
- {//actually trace to a bolt
- G_GetBoltPosition( ent, ent->footRBolt, kickEnd );
- VectorSubtract( kickEnd, ent->currentOrigin, kickDir );
- kickDir[2] = 0;//ah, flatten it, I guess...
- VectorNormalize( kickDir );
- //NOTE: have to fudge this a little because it's not getting enough range with the anim as-is
- VectorMA( kickEnd, 8, kickDir, kickEnd );
- }
- else
- {//guess
- AngleVectors( fwdAngs, NULL, kickDir, NULL );
- }
- }
- else if ( elapsedTime >= 350 && elapsedTime < 450 )
- {//left
- doKick = qtrue;
- if ( ent->footLBolt != -1 )
- {//actually trace to a bolt
- G_GetBoltPosition( ent, ent->footLBolt, kickEnd );
- VectorSubtract( kickEnd, ent->currentOrigin, kickDir );
- kickDir[2] = 0;//ah, flatten it, I guess...
- VectorNormalize( kickDir );
- //NOTE: have to fudge this a little because it's not getting enough range with the anim as-is
- VectorMA( kickEnd, 8, kickDir, kickEnd );
- }
- else
- {//guess
- AngleVectors( fwdAngs, NULL, kickDir, NULL );
- VectorScale( kickDir, -1, kickDir );
- }
- }
- break;
- }
- }
- if ( doKick )
- {
- G_KickTrace( ent, kickDir, kickDist, kickEnd, kickDamage, kickPush, kickSoundOnWalls );
- }
- if ( doKick2 )
- {
- G_KickTrace( ent, kickDir2, kickDist2, kickEnd2, kickDamage2, kickPush2, kickSoundOnWalls );
- }
- }
- else if ( ent->client->ps.saberMove == LS_DUAL_FB )
- {
- //pull back the view
- G_CamPullBackForLegsAnim( ent );
- }
- else if ( ent->client->ps.saberMove == LS_A_BACK || ent->client->ps.saberMove == LS_A_BACK_CR
- || ent->client->ps.saberMove == LS_A_BACKSTAB )
- {//can't move or turn during back attacks
- ucmd->forwardmove = ucmd->rightmove = 0;
- if ( ent->NPC )
- {
- VectorClear( ent->client->ps.moveDir );
- }
- if ( (overridAngles = (PM_AdjustAnglesForBackAttack( ent, ucmd )?qtrue:overridAngles)) == qtrue )
- {
- //pull back the view
- G_CamPullBackForLegsAnim( ent );
- }
- }
- else if ( ent->client->ps.torsoAnim == BOTH_WALL_FLIP_BACK1
- || ent->client->ps.torsoAnim == BOTH_WALL_FLIP_BACK2
- || ent->client->ps.legsAnim == BOTH_FORCEWALLRUNFLIP_END
- || ent->client->ps.legsAnim == BOTH_FORCEWALLREBOUND_BACK )
- {
- //pull back the view
- G_CamPullBackForLegsAnim( ent );
- }
- else if ( ent->client->ps.torsoAnim == BOTH_A6_SABERPROTECT )
- {
- ucmd->forwardmove = ucmd->rightmove = ucmd->upmove = 0;
- if ( ent->NPC )
- {
- VectorClear( ent->client->ps.moveDir );
- ent->client->ps.forceJumpCharge = 0;
- }
- if ( !ent->s.number )
- {
- float animLength = PM_AnimLength( ent->client->clientInfo.animFileIndex, (animNumber_t)ent->client->ps.torsoAnim );
- float elapsedTime = (float)(animLength-ent->client->ps.torsoAnimTimer);
- float backDist = 0;
- if ( elapsedTime <= 300.0f )
- {//starting anim
- backDist = (elapsedTime/300.0f)*90.0f;
- }
- else if ( ent->client->ps.torsoAnimTimer <= 300.0f )
- {//ending anim
- backDist = (ent->client->ps.torsoAnimTimer/300.0f)*90.0f;
- }
- else
- {//in middle of anim
- backDist = 90.0f;
- }
- //back off and look down
- cg.overrides.active |= (CG_OVERRIDE_3RD_PERSON_RNG|CG_OVERRIDE_3RD_PERSON_POF);
- cg.overrides.thirdPersonRange = cg_thirdPersonRange.value+backDist;
- cg.overrides.thirdPersonPitchOffset = cg_thirdPersonPitchOffset.value+(backDist/2.0f);
- }
- overridAngles = (PM_AdjustAnglesForSpinProtect( ent, ucmd )?qtrue:overridAngles);
- }
- else if ( ent->client->ps.legsAnim == BOTH_A3_SPECIAL )
- {//push forward
- float animLength = PM_AnimLength( ent->client->clientInfo.animFileIndex, (animNumber_t)ent->client->ps.torsoAnim );
- float elapsedTime = (float)(animLength-ent->client->ps.torsoAnimTimer);
- ucmd->upmove = ucmd->rightmove = 0;
- ucmd->forwardmove = 0;
- if ( elapsedTime >= 350 && elapsedTime < 1500 )
- {//push forward
- ucmd->forwardmove = 64;
- ent->client->ps.speed = 200.0f;
- }
- //FIXME: pull back camera?
- }
- else if ( ent->client->ps.legsAnim == BOTH_A2_SPECIAL )
- {//push forward
- ucmd->upmove = ucmd->rightmove = 0;
- ucmd->forwardmove = 0;
- if ( ent->client->ps.legsAnimTimer > 200.0f )
- {
- float animLength = PM_AnimLength( ent->client->clientInfo.animFileIndex, (animNumber_t)ent->client->ps.torsoAnim );
- float elapsedTime = (float)(animLength-ent->client->ps.torsoAnimTimer);
- if ( elapsedTime < 750
- || (elapsedTime >= 1650 && elapsedTime < 2400) )
- {//push forward
- ucmd->forwardmove = 64;
- ent->client->ps.speed = 200.0f;
- }
- }
- //FIXME: pull back camera?
- }//FIXME: fast special?
- else if ( ent->client->ps.legsAnim == BOTH_A1_SPECIAL
- && (ucmd->forwardmove || ucmd->rightmove || (VectorCompare( ent->client->ps.moveDir, vec3_origin )&&ent->client->ps.speed>0)) )
- {//moving during full-body fast special
- ent->client->ps.legsAnimTimer = 0;//don't hold this legsAnim, allow them to run
- //FIXME: just add this to the list of overridable special moves in PM_Footsteps?
- }
- else if ( ent->client->ps.legsAnim == BOTH_FLIP_LAND )
- {//moving during full-body fast special
- float animLength = PM_AnimLength( ent->client->clientInfo.animFileIndex, (animNumber_t)ent->client->ps.torsoAnim );
- float elapsedTime = (float)(animLength-ent->client->ps.torsoAnimTimer);
- ucmd->upmove = ucmd->rightmove = ucmd->forwardmove = 0;
- if ( elapsedTime > 600 && elapsedTime < 800
- && ent->client->ps.groundEntityNum != ENTITYNUM_NONE )
- {//jump - FIXME: how do we stop double-jumps?
- ent->client->ps.pm_flags |= PMF_JUMP_HELD;
- ent->client->ps.groundEntityNum = ENTITYNUM_NONE;
- ent->client->ps.jumpZStart = ent->currentOrigin[2];
- ent->client->ps.velocity[2] = JUMP_VELOCITY;
- G_AddEvent( ent, EV_JUMP, 0 );
- }
- }
- else if ( ( PM_SuperBreakWinAnim( ent->client->ps.torsoAnim )
- || PM_SuperBreakLoseAnim( ent->client->ps.torsoAnim ) )
- && ent->client->ps.torsoAnimTimer )
- {//can't move or turn
- ucmd->forwardmove = ucmd->rightmove = ucmd->upmove = 0;
- if ( ent->NPC )
- {
- VectorClear( ent->client->ps.moveDir );
- ent->client->ps.forceJumpCharge = 0;
- }
- overridAngles = (PM_LockAngles( ent, ucmd )?qtrue:overridAngles);
- }
- else if ( BG_FullBodyTauntAnim( ent->client->ps.legsAnim )
- && BG_FullBodyTauntAnim( ent->client->ps.torsoAnim ) )
- {
- if ( (ucmd->buttons&BUTTON_ATTACK)
- || (ucmd->buttons&BUTTON_ALT_ATTACK)
- || (ucmd->buttons&BUTTON_USE_FORCE)
- || (ucmd->buttons&BUTTON_FORCEGRIP)
- || (ucmd->buttons&BUTTON_FORCE_LIGHTNING)
- || (ucmd->buttons&BUTTON_FORCE_DRAIN)
- || ucmd->upmove )
- {//stop the anim
- if ( ent->client->ps.legsAnim == BOTH_MEDITATE
- && ent->client->ps.torsoAnim == BOTH_MEDITATE )
- {
- NPC_SetAnim( ent, SETANIM_BOTH, BOTH_MEDITATE_END, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 0 );
- }
- else
- {
- ent->client->ps.legsAnimTimer = ent->client->ps.torsoAnimTimer = 0;
- }
- }
- else
- {
- if ( ent->client->ps.legsAnim == BOTH_MEDITATE )
- {
- if ( ent->client->ps.legsAnimTimer < 100 )
- {
- ent->client->ps.legsAnimTimer = 100;
- }
- }
- if ( ent->client->ps.torsoAnim == BOTH_MEDITATE )
- {
- if ( ent->client->ps.torsoAnimTimer < 100 )
- {
- ent->client->ps.legsAnimTimer = 100;
- }
- }
- if ( ent->client->ps.legsAnimTimer > 0 || ent->client->ps.torsoAnimTimer > 0 )
- {
- ucmd->rightmove = 0;
- ucmd->upmove = 0;
- ucmd->forwardmove = 0;
- ucmd->buttons = 0;
- if ( ent->NPC )
- {
- VectorClear( ent->client->ps.moveDir );
- ent->client->ps.forceJumpCharge = 0;
- }
- overridAngles = (PM_LockAngles( ent, ucmd )?qtrue:overridAngles);
- }
- }
- }
- else if ( ent->client->ps.legsAnim == BOTH_MEDITATE_END
- && ent->client->ps.legsAnimTimer > 0 )
- {
- ucmd->rightmove = 0;
- ucmd->upmove = 0;
- ucmd->forwardmove = 0;
- ucmd->buttons = 0;
- if ( ent->NPC )
- {
- VectorClear( ent->client->ps.moveDir );
- ent->client->ps.forceJumpCharge = 0;
- }
- overridAngles = (PM_LockAngles( ent, ucmd )?qtrue:overridAngles);
- }
- else if ( !ent->s.number )
- {
- if ( ent->client->NPC_class != CLASS_ATST )
- {
- // Not in a vehicle.
- if ( ent->s.m_iVehicleNum == 0 )
- {
- if ( !MatrixMode )
- {
- cg.overrides.active &= ~(CG_OVERRIDE_3RD_PERSON_RNG|CG_OVERRIDE_3RD_PERSON_POF|CG_OVERRIDE_3RD_PERSON_ANG);
- cg.overrides.thirdPersonRange = 0;
- }
- }
- }
- }
- if ( PM_InRoll( &ent->client->ps ) )
- {
- if ( ent->s.number >= MAX_CLIENTS || !player_locked )
- {
- //FIXME: NPCs should try to face us during this roll, so they roll around us...?
- PM_CmdForRoll( &ent->client->ps, ucmd );
- if ( ent->s.number >= MAX_CLIENTS )
- {//make sure it doesn't roll me off a ledge
- if ( !G_CheckRollSafety( ent, ent->client->ps.legsAnim, 24 ) )
- {//crap! I guess all we can do is stop... UGH
- ucmd->rightmove = ucmd->forwardmove = 0;
- }
- }
- }
- if ( ent->NPC )
- {//invalid now
- VectorClear( ent->client->ps.moveDir );
- }
- ent->client->ps.speed = 400;
- }
- if ( PM_InCartwheel( ent->client->ps.legsAnim ) )
- {//can't keep moving in cartwheel
- if ( ent->client->ps.legsAnimTimer > 100 )
- {//still have time left in the anim
- ucmd->forwardmove = ucmd->rightmove = ucmd->upmove = 0;
- if ( ent->NPC )
- {//invalid now
- VectorClear( ent->client->ps.moveDir );
- }
- if ( ent->s.number || !player_locked )
- {
- switch ( ent->client->ps.legsAnim )
- {
- case BOTH_ARIAL_LEFT:
- case BOTH_CARTWHEEL_LEFT:
- ucmd->rightmove = -127;
- break;
- case BOTH_ARIAL_RIGHT:
- case BOTH_CARTWHEEL_RIGHT:
- ucmd->rightmove = 127;
- break;
- case BOTH_ARIAL_F1:
- ucmd->forwardmove = 127;
- break;
- default:
- break;
- }
- }
- }
- }
- if ( ent->client->ps.legsAnim == BOTH_BUTTERFLY_LEFT
- || ent->client->ps.legsAnim == BOTH_BUTTERFLY_RIGHT )
- {
- if ( ent->client->ps.legsAnimTimer > 100 )
- {//still have time left in the anim
- ucmd->forwardmove = ucmd->rightmove = ucmd->upmove = 0;
- if ( ent->NPC )
- {//invalid now
- VectorClear( ent->client->ps.moveDir );
- }
- if ( ent->s.number || !player_locked )
- {
- if ( ent->client->ps.legsAnimTimer > 450 )
- {
- switch ( ent->client->ps.legsAnim )
- {
- case BOTH_BUTTERFLY_LEFT:
- ucmd->rightmove = -127;
- break;
- case BOTH_BUTTERFLY_RIGHT:
- ucmd->rightmove = 127;
- break;
- default:
- break;
- }
- }
- }
- }
- }
- overridAngles = (PM_AdjustAnglesForStabDown( ent, ucmd )?qtrue:overridAngles);
- overridAngles = (PM_AdjustAngleForWallJump( ent, ucmd, qtrue )?qtrue:overridAngles);
- overridAngles = (PM_AdjustAngleForWallRunUp( ent, ucmd, qtrue )?qtrue:overridAngles);
- overridAngles = (PM_AdjustAngleForWallRun( ent, ucmd, qtrue )?qtrue:overridAngles);
- return overridAngles;
- }
- void BG_AddPushVecToUcmd( gentity_t *self, usercmd_t *ucmd )
- {
- vec3_t forward, right, moveDir;
- float pushSpeed, fMove, rMove;
- if ( !self->client )
- {
- return;
- }
- pushSpeed = VectorLengthSquared(self->client->pushVec);
- if(!pushSpeed)
- {//not being pushed
- return;
- }
- AngleVectors(self->client->ps.viewangles, forward, right, NULL);
- VectorScale(forward, ucmd->forwardmove/127.0f * self->client->ps.speed, moveDir);
- VectorMA(moveDir, ucmd->rightmove/127.0f * self->client->ps.speed, right, moveDir);
- //moveDir is now our intended move velocity
- VectorAdd(moveDir, self->client->pushVec, moveDir);
- self->client->ps.speed = VectorNormalize(moveDir);
- //moveDir is now our intended move velocity plus our push Vector
- fMove = 127.0 * DotProduct(forward, moveDir);
- rMove = 127.0 * DotProduct(right, moveDir);
- ucmd->forwardmove = floor(fMove);//If in the same dir , will be positive
- ucmd->rightmove = floor(rMove);//If in the same dir , will be positive
- if ( self->client->pushVecTime < level.time )
- {
- VectorClear( self->client->pushVec );
- }
- }
- void NPC_Accelerate( gentity_t *ent, qboolean fullWalkAcc, qboolean fullRunAcc )
- {
- if ( !ent->client || !ent->NPC )
- {
- return;
- }
- if ( !ent->NPC->stats.acceleration )
- {//No acceleration means just start and stop
- ent->NPC->currentSpeed = ent->NPC->desiredSpeed;
- }
- //FIXME: in cinematics always accel/decel?
- else if ( ent->NPC->desiredSpeed <= ent->NPC->stats.walkSpeed )
- {//Only accelerate if at walkSpeeds
- if ( ent->NPC->desiredSpeed > ent->NPC->currentSpeed + ent->NPC->stats.acceleration )
- {
- //ent->client->ps.friction = 0;
- ent->NPC->currentSpeed += ent->NPC->stats.acceleration;
- }
- else if ( ent->NPC->desiredSpeed > ent->NPC->currentSpeed )
- {
- //ent->client->ps.friction = 0;
- ent->NPC->currentSpeed = ent->NPC->desiredSpeed;
- }
- else if ( fullWalkAcc && ent->NPC->desiredSpeed < ent->NPC->currentSpeed - ent->NPC->stats.acceleration )
- {//decelerate even when walking
- ent->NPC->currentSpeed -= ent->NPC->stats.acceleration;
- }
- else if ( ent->NPC->desiredSpeed < ent->NPC->currentSpeed )
- {//stop on a dime
- ent->NPC->currentSpeed = ent->NPC->desiredSpeed;
- }
- }
- else// if ( ent->NPC->desiredSpeed > ent->NPC->stats.walkSpeed )
- {//Only decelerate if at runSpeeds
- if ( fullRunAcc && ent->NPC->desiredSpeed > ent->NPC->currentSpeed + ent->NPC->stats.acceleration )
- {//Accelerate to runspeed
- //ent->client->ps.friction = 0;
- ent->NPC->currentSpeed += ent->NPC->stats.acceleration;
- }
- else if ( ent->NPC->desiredSpeed > ent->NPC->currentSpeed )
- {//accelerate instantly
- //ent->client->ps.friction = 0;
- ent->NPC->currentSpeed = ent->NPC->desiredSpeed;
- }
- else if ( fullRunAcc && ent->NPC->desiredSpeed < ent->NPC->currentSpeed - ent->NPC->stats.acceleration )
- {
- ent->NPC->currentSpeed -= ent->NPC->stats.acceleration;
- }
- else if ( ent->NPC->desiredSpeed < ent->NPC->currentSpeed )
- {
- ent->NPC->currentSpeed = ent->NPC->desiredSpeed;
- }
- }
- }
- /*
- -------------------------
- NPC_GetWalkSpeed
- -------------------------
- */
- static int NPC_GetWalkSpeed( gentity_t *ent )
- {
- int walkSpeed = 0;
- if ( ( ent->client == NULL ) || ( ent->NPC == NULL ) )
- return 0;
- switch ( ent->client->playerTeam )
- {
- case TEAM_PLAYER: //To shutup compiler, will add entries later (this is stub code)
- default:
- walkSpeed = ent->NPC->stats.walkSpeed;
- break;
- }
- return walkSpeed;
- }
- /*
- -------------------------
- NPC_GetRunSpeed
- -------------------------
- */
- #define BORG_RUN_INCR 25
- #define SPECIES_RUN_INCR 25
- #define STASIS_RUN_INCR 20
- #define WARBOT_RUN_INCR 20
- static int NPC_GetRunSpeed( gentity_t *ent )
- {
- int runSpeed = 0;
- if ( ( ent->client == NULL ) || ( ent->NPC == NULL ) )
- return 0;
- /*
- switch ( ent->client->playerTeam )
- {
- case TEAM_BORG:
- runSpeed = ent->NPC->stats.runSpeed;
- runSpeed += BORG_RUN_INCR * (g_spskill->integer%3);
- break;
- case TEAM_8472:
- runSpeed = ent->NPC->stats.runSpeed;
- runSpeed += SPECIES_RUN_INCR * (g_spskill->integer%3);
- break;
- case TEAM_STASIS:
- runSpeed = ent->NPC->stats.runSpeed;
- runSpeed += STASIS_RUN_INCR * (g_spskill->integer%3);
- break;
- case TEAM_BOTS:
- runSpeed = ent->NPC->stats.runSpeed;
- break;
- default:
- runSpeed = ent->NPC->stats.runSpeed;
- break;
- }
- */
- // team no longer indicates species/race. Use NPC_class to adjust speed for specific npc types
- switch( ent->client->NPC_class)
- {
- case CLASS_PROBE: // droid cases here to shut-up compiler
- case CLASS_GONK:
- case CLASS_R2D2:
- case CLASS_R5D2:
- case CLASS_MARK1:
- case CLASS_MARK2:
- case CLASS_PROTOCOL:
- case CLASS_ATST: // hmm, not really your average droid
- case CLASS_MOUSE:
- case CLASS_SEEKER:
- case CLASS_REMOTE:
- runSpeed = ent->NPC->stats.runSpeed;
- break;
- default:
- runSpeed = ent->NPC->stats.runSpeed;
- break;
- }
- return runSpeed;
- }
- void G_HeldByMonster( gentity_t *ent, usercmd_t **ucmd )
- {
- if ( ent && ent->activator && ent->activator->inuse && ent->activator->health > 0 )
- {
- gentity_t *monster = ent->activator;
- //take the monster's waypoint as your own
- ent->waypoint = monster->waypoint;
- //update the actual origin of the victim
- mdxaBone_t boltMatrix;
- // Getting the bolt here
- int boltIndex = monster->gutBolt;//default to being held in his mouth
- if ( monster->count == 1 )
- {//being held in hand rather than the mouth, so use *that* bolt
- boltIndex = monster->handRBolt;
- }
- vec3_t monAngles = {0};
- monAngles[YAW] = monster->currentAngles[YAW];//only use YAW when passing angles to G2
- gi.G2API_GetBoltMatrix( monster->ghoul2, monster->playerModel, boltIndex,
- &boltMatrix, monAngles, monster->currentOrigin, (cg.time?cg.time:level.time),
- NULL, monster->s.modelScale );
- // Storing ent position, bolt position, and bolt axis
- gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, ent->client->ps.origin );
- gi.linkentity( ent );
- //lock view angles
- PM_AdjustAnglesForHeldByMonster( ent, monster, *ucmd );
- if ( monster->client && monster->client->NPC_class == CLASS_WAMPA )
- {//can only hit attack button
- (*ucmd)->buttons &= ~((*ucmd)->buttons&~BUTTON_ATTACK);
- }
- }
- else if (ent)
- {//doh, my captor died!
- ent->activator = NULL;
- if (ent->client)
- {
- ent->client->ps.eFlags &= ~(EF_HELD_BY_WAMPA|EF_HELD_BY_RANCOR);
- }
- }
- // don't allow movement, weapon switching, and most kinds of button presses
- (*ucmd)->forwardmove = 0;
- (*ucmd)->rightmove = 0;
- (*ucmd)->upmove = 0;
- }
- // yes... so stop skipping...
- void G_StopCinematicSkip( void )
- {
- gi.cvar_set("skippingCinematic", "0");
- gi.cvar_set("timescale", "1");
- }
- void G_StartCinematicSkip( void )
- {
-
- if (cinematicSkipScript[0])
- {
- Quake3Game()->RunScript( &g_entities[0], cinematicSkipScript );
- cinematicSkipScript[0] = 0;
- gi.cvar_set("skippingCinematic", "1");
- gi.cvar_set("timescale", "100");
- }
- else
- {
- // no... so start skipping...
- gi.cvar_set("skippingCinematic", "1");
- gi.cvar_set("timescale", "100");
- }
- }
- void G_CheckClientIdle( gentity_t *ent, usercmd_t *ucmd )
- {
- if ( !ent || !ent->client || ent->health <= 0 )
- {
- return;
- }
- if ( !ent->s.number && ( !cg.renderingThirdPerson || cg.zoomMode ) )
- {
- if ( ent->client->idleTime < level.time )
- {
- ent->client->idleTime = level.time;
- }
- return;
- }
- if ( !VectorCompare( vec3_origin, ent->client->ps.velocity )
- || ucmd->buttons || ucmd->forwardmove || ucmd->rightmove || ucmd->upmove
- || !PM_StandingAnim( ent->client->ps.legsAnim )
- || ent->enemy
- || ent->client->ps.legsAnimTimer
- || ent->client->ps.torsoAnimTimer )
- {//FIXME: also check for turning?
- if ( !VectorCompare( vec3_origin, ent->client->ps.velocity )
- || ucmd->buttons || ucmd->forwardmove || ucmd->rightmove || ucmd->upmove
- || ent->enemy )
- {
- //if in an idle, break out
- switch ( ent->client->ps.legsAnim )
- {
- case BOTH_STAND1IDLE1:
- case BOTH_STAND2IDLE1:
- case BOTH_STAND2IDLE2:
- case BOTH_STAND3IDLE1:
- case BOTH_STAND5IDLE1:
- ent->client->ps.legsAnimTimer = 0;
- break;
- }
- switch ( ent->client->ps.torsoAnim )
- {
- case BOTH_STAND1IDLE1:
- case BOTH_STAND2IDLE1:
- case BOTH_STAND2IDLE2:
- case BOTH_STAND3IDLE1:
- case BOTH_STAND5IDLE1:
- ent->client->ps.torsoAnimTimer = 0;
- break;
- }
- }
- //
- if ( ent->client->idleTime < level.time )
- {
- ent->client->idleTime = level.time;
- }
- }
- else if ( level.time - ent->client->idleTime > 5000 )
- {//been idle for 5 seconds
- int idleAnim = -1;
- switch ( ent->client->ps.legsAnim )
- {
- case BOTH_STAND1:
- idleAnim = BOTH_STAND1IDLE1;
- break;
- case BOTH_STAND2:
- idleAnim = Q_irand(BOTH_STAND2IDLE1,BOTH_STAND2IDLE2);
- break;
- case BOTH_STAND3:
- idleAnim = BOTH_STAND3IDLE1;
- break;
- case BOTH_STAND5:
- idleAnim = BOTH_STAND5IDLE1;
- break;
- }
- if ( idleAnim != -1 && PM_HasAnimation( ent, idleAnim ) )
- {
- NPC_SetAnim( ent, SETANIM_BOTH, idleAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
- //don't idle again after this anim for a while
- ent->client->idleTime = level.time + PM_AnimLength( ent->client->clientInfo.animFileIndex, (animNumber_t)idleAnim ) + Q_irand( 0, 2000 );
- }
- }
- }
- void G_CheckMovingLoopingSounds( gentity_t *ent, usercmd_t *ucmd )
- {
- if ( ent->client )
- {
- if ( (ent->NPC&&!VectorCompare( vec3_origin, ent->client->ps.moveDir ))//moving using moveDir
- || ucmd->forwardmove || ucmd->rightmove//moving using ucmds
- || (ucmd->upmove&&FlyingCreature( ent ))//flier using ucmds to move
- || (FlyingCreature( ent )&&!VectorCompare( vec3_origin, ent->client->ps.velocity )&&ent->health>0))//flier using velocity to move
- {
- switch( ent->client->NPC_class )
- {
- case CLASS_R2D2:
- ent->s.loopSound = G_SoundIndex( "sound/chars/r2d2/misc/r2_move_lp.wav" );
- break;
- case CLASS_R5D2:
- ent->s.loopSound = G_SoundIndex( "sound/chars/r2d2/misc/r2_move_lp2.wav" );
- break;
- case CLASS_MARK2:
- ent->s.loopSound = G_SoundIndex( "sound/chars/mark2/misc/mark2_move_lp" );
- break;
- case CLASS_MOUSE:
- ent->s.loopSound = G_SoundIndex( "sound/chars/mouse/misc/mouse_lp" );
- break;
- case CLASS_PROBE:
- ent->s.loopSound = G_SoundIndex( "sound/chars/probe/misc/probedroidloop" );
- }
- }
- else
- {//not moving under your own control, stop loopSound
- if ( ent->client->NPC_class == CLASS_R2D2 || ent->client->NPC_class == CLASS_R5D2
- || ent->client->NPC_class == CLASS_MARK2 || ent->client->NPC_class == CLASS_MOUSE
- || ent->client->NPC_class == CLASS_PROBE )
- {
- ent->s.loopSound = 0;
- }
- }
- }
- }
- /*
- ==============
- ClientAlterSpeed
- This function is called ONLY from ClientThinkReal, and is responsible for setting client ps.speed
- ==============
- */
- void ClientAlterSpeed(gentity_t *ent, usercmd_t *ucmd, qboolean controlledByPlayer, float vehicleFrameTimeModifier)
- {
- gclient_t *client = ent->client;
- Vehicle_t *pVeh = NULL;
- if ( ent->client && ent->client->NPC_class == CLASS_VEHICLE )
- {
- pVeh = ent->m_pVehicle;
- }
- // set speed
- // This may be wrong: If we're an npc and we are in a vehicle???
- if ( ent->NPC != NULL && ent->client && ( ent->s.m_iVehicleNum != 0 )/*&& ent->client->NPC_class == CLASS_VEHICLE*/ )
- {//we don't actually scale the ucmd, we use actual speeds
- //FIXME: swoop should keep turning (and moving forward?) for a little bit?
- if ( ent->NPC->combatMove == qfalse )
- {
- if ( !(ucmd->buttons & BUTTON_USE) )
- {//Not leaning
- qboolean Flying = (ucmd->upmove && ent->client->moveType == MT_FLYSWIM);
- qboolean Climbing = (ucmd->upmove && ent->watertype&CONTENTS_LADDER );
- client->ps.friction = 6;
- if ( ucmd->forwardmove || ucmd->rightmove || Flying )
- {
- //if ( ent->NPC->behaviorState != BS_FORMATION )
- {//In - Formation NPCs set thier desiredSpeed themselves
- if ( ucmd->buttons & BUTTON_WALKING )
- {
- ent->NPC->desiredSpeed = NPC_GetWalkSpeed( ent );//ent->NPC->stats.walkSpeed;
- }
- else//running
- {
- ent->NPC->desiredSpeed = NPC_GetRunSpeed( ent );//ent->NPC->stats.runSpeed;
- }
- if ( ent->NPC->currentSpeed >= 80 && !controlledByPlayer )
- {//At higher speeds, need to slow down close to stuff
- //Slow down as you approach your goal
- // if ( ent->NPC->distToGoal < SLOWDOWN_DIST && client->race != RACE_BORG && !(ent->NPC->aiFlags&NPCAI_NO_SLOWDOWN) )//128
- if ( ent->NPC->distToGoal < SLOWDOWN_DIST && !(ent->NPC->aiFlags&NPCAI_NO_SLOWDOWN) )//128
- {
- if ( ent->NPC->desiredSpeed > MIN_NPC_SPEED )
- {
- float slowdownSpeed = ((float)ent->NPC->desiredSpeed) * ent->NPC->distToGoal / SLOWDOWN_DIST;
- ent->NPC->desiredSpeed = ceil(slowdownSpeed);
- if ( ent->NPC->desiredSpeed < MIN_NPC_SPEED )
- {//don't slow down too much
- ent->NPC->desiredSpeed = MIN_NPC_SPEED;
- }
- }
- }
- }
- }
- }
- else if ( Climbing )
- {
- ent->NPC->desiredSpeed = ent->NPC->stats.walkSpeed;
- }
- else
- {//We want to stop
- ent->NPC->desiredSpeed = 0;
- }
- NPC_Accelerate( ent, qfalse, qfalse );
- if ( ent->NPC->currentSpeed <= 24 && ent->NPC->desiredSpeed < ent->NPC->currentSpeed )
- {//No-one walks this slow
- client->ps.speed = ent->NPC->currentSpeed = 0;//Full stop
- ucmd->forwardmove = 0;
- ucmd->rightmove = 0;
- }
- else
- {
- if ( ent->NPC->currentSpeed <= ent->NPC->stats.walkSpeed )
- {//Play the walkanim
- ucmd->buttons |= BUTTON_WALKING;
- }
- else
- {
- ucmd->buttons &= ~BUTTON_WALKING;
- }
- if ( ent->NPC->currentSpeed > 0 )
- {//We should be moving
- if ( Climbing || Flying )
- {
- if ( !ucmd->upmove )
- {//We need to force them to take a couple more steps until stopped
- ucmd->upmove = ent->NPC->last_ucmd.upmove;//was last_upmove;
- }
- }
- else if ( !ucmd->forwardmove && !ucmd->rightmove )
- {//We need to force them to take a couple more steps until stopped
- ucmd->forwardmove = ent->NPC->last_ucmd.forwardmove;//was last_forwardmove;
- ucmd->rightmove = ent->NPC->last_ucmd.rightmove;//was last_rightmove;
- }
- }
- client->ps.speed = ent->NPC->currentSpeed;
- if ( player && player->client && player->client->ps.viewEntity == ent->s.number )
- {
- }
- else
- {
- //Slow down on turns - don't orbit!!!
- float turndelta = 0;
- // if the NPC is locked into a Yaw, we want to check the lockedDesiredYaw...otherwise the NPC can't walk backwards, because it always thinks it trying to turn according to desiredYaw
- if( client->renderInfo.renderFlags & RF_LOCKEDANGLE ) // yeah I know the RF_ flag is a pretty ugly hack...
- {
- turndelta = (180 - fabs( AngleDelta( ent->currentAngles[YAW], ent->NPC->lockedDesiredYaw ) ))/180;
- }
- else
- {
- turndelta = (180 - fabs( AngleDelta( ent->currentAngles[YAW], ent->NPC->desiredYaw ) ))/180;
- }
-
- if ( turndelta < 0.75f )
- {
- client->ps.speed = 0;
- }
- else if ( ent->NPC->distToGoal < 100 && turndelta < 1.0 )
- {//Turn is greater than 45 degrees or closer than 100 to goal
- client->ps.speed = floor(((float)(client->ps.speed))*turndelta);
- }
- }
- }
- }
- }
- else
- {
- ent->NPC->desiredSpeed = ( ucmd->buttons & BUTTON_WALKING ) ? NPC_GetWalkSpeed( ent ) : NPC_GetRunSpeed( ent );
- client->ps.speed = ent->NPC->desiredSpeed;
- }
- }
- else
- {//Client sets ucmds and such for speed alterations
- {
- client->ps.speed = g_speed->value;//default is 320
- /*if ( !ent->s.number && ent->painDebounceTime>level.time )
- {
- client->ps.speed *= 0.25f;
- }
- else */if (ent->client->ps.heldClient < ENTITYNUM_WORLD)
- {
- client->ps.speed *= 0.3f;
- }
- else if ( PM_SaberInAttack( ent->client->ps.saberMove ) && ucmd->forwardmove < 0 )
- {//if running backwards while attacking, don't run as fast.
- switch( client->ps.saberAnimLevel )
- {
- case SS_FAST:
- client->ps.speed *= 0.75f;
- break;
- case SS_MEDIUM:
- case SS_DUAL:
- case SS_STAFF:
- client->ps.speed *= 0.60f;
- break;
- case SS_STRONG:
- client->ps.speed *= 0.45f;
- break;
- }
- if ( g_saberMoveSpeed->value != 1.0f )
- {
- client->ps.speed *= g_saberMoveSpeed->value;
- }
- }
- else if ( PM_LeapingSaberAnim( client->ps.legsAnim ) )
- {//no mod on speed when leaping
- //FIXME: maybe jump?
- }
- else if ( PM_SpinningSaberAnim( client->ps.legsAnim ) )
- {
- client->ps.speed *= 0.5f;
- if ( g_saberMoveSpeed->value != 1.0f )
- {
- client->ps.speed *= g_saberMoveSpeed->value;
- }
- }
- else if ( client->ps.weapon == WP_SABER && ( ucmd->buttons & BUTTON_ATTACK ) )
- {//if attacking with saber while running, drop your speed
- //FIXME: should be weaponTime? Or in certain anims?
- switch( client->ps.saberAnimLevel )
- {
- case SS_MEDIUM:
- case SS_DUAL:
- case SS_STAFF:
- client->ps.speed *= 0.85f;
- break;
- case SS_STRONG:
- client->ps.speed *= 0.70f;
- break;
- }
- if ( g_saberMoveSpeed->value != 1.0f )
- {
- client->ps.speed *= g_saberMoveSpeed->value;
- }
- }
- }
- }
- if ( client->NPC_class == CLASS_ATST && client->ps.legsAnim == BOTH_RUN1START )
- {//HACK: when starting to move as atst, ramp up speed
- //float animLength = PM_AnimLength( client->clientInfo.animFileIndex, (animNumber_t)client->ps.legsAnim);
- //client->ps.speed *= ( animLength - client->ps.legsAnimTimer)/animLength;
- if ( client->ps.legsAnimTimer > 100 )
- {
- client->ps.speed = 0;
- }
- }
- //Apply forced movement
- if ( client->forced_forwardmove )
- {
- ucmd->forwardmove = client->forced_forwardmove;
- if ( !client->ps.speed )
- {
- if ( ent->NPC != NULL )
- {
- client->ps.speed = ent->NPC->stats.runSpeed;
- }
- else
- {
- client->ps.speed = g_speed->value;//default is 320
- }
- }
- }
- if ( client->forced_rightmove )
- {
- ucmd->rightmove = client->forced_rightmove;
- if ( !client->ps.speed )
- {
- if ( ent->NPC != NULL )
- {
- client->ps.speed = ent->NPC->stats.runSpeed;
- }
- else
- {
- client->ps.speed = g_speed->value;//default is 320
- }
- }
- }
- if ( !pVeh )
- {
- if ( ucmd->forwardmove < 0 && !(ucmd->buttons&BUTTON_WALKING) && client->ps.groundEntityNum != ENTITYNUM_NONE )
- {//running backwards is slower than running forwards
- client->ps.speed *= 0.75;
- }
- if (client->ps.forceRageRecoveryTime > level.time )
- {
- client->ps.speed *= 0.75;
- }
- }
- }
- extern qboolean ForceDrain2(gentity_t *ent);
- extern void ForceGrip(gentity_t *ent);
- extern void ForceLightning(gentity_t *ent);
- extern void ForceProtect(gentity_t *ent);
- extern void ForceRage(gentity_t *ent);
- extern void ForceSeeing(gentity_t *ent);
- extern void ForceTelepathy(gentity_t *ent);
- extern void ForceAbsorb(gentity_t *ent);
- extern void ForceHeal(gentity_t *ent);
- static void ProcessGenericCmd(gentity_t *ent, byte cmd)
- {
- switch(cmd) {
- case 0:
- break;
- case GENCMD_FORCE_DRAIN:
- ForceDrain2(ent);
- break;
- case GENCMD_FORCE_THROW:
- ForceThrow(ent, qfalse);
- break;
- case GENCMD_FORCE_SPEED:
- ForceSpeed(ent);
- break;
- case GENCMD_FORCE_PULL:
- ForceThrow(ent, qtrue);
- break;
- case GENCMD_FORCE_DISTRACT:
- ForceTelepathy(ent);
- break;
- case GENCMD_FORCE_GRIP:
- ForceGrip(ent);
- break;
- case GENCMD_FORCE_LIGHTNING:
- ForceLightning(ent);
- break;
- case GENCMD_FORCE_RAGE:
- ForceRage(ent);
- break;
- case GENCMD_FORCE_PROTECT:
- ForceProtect(ent);
- break;
- case GENCMD_FORCE_ABSORB:
- ForceAbsorb(ent);
- break;
- case GENCMD_FORCE_SEEING:
- ForceSeeing(ent);
- break;
- case GENCMD_FORCE_HEAL:
- ForceHeal(ent);
- break;
- }
- }
- /*
- ==============
- ClientThink
- This will be called once for each client frame, which will
- usually be a couple times for each server frame on fast clients.
- ==============
- */
- extern int G_FindLocalInterestPoint( gentity_t *self );
- extern float G_CanJumpToEnemyVeh(Vehicle_t *pVeh, const usercmd_t *pUmcd );
- extern void Cbuf_ExecuteText( int exec_when, const char *text );
- void ClientThink_real( gentity_t *ent, usercmd_t *ucmd )
- {
- gclient_t *client;
- pmove_t pm;
- vec3_t oldOrigin;
- int oldEventSequence;
- int msec;
- qboolean inSpinFlipAttack = PM_AdjustAnglesForSpinningFlip( ent, ucmd, qfalse );
- qboolean controlledByPlayer = qfalse;
- Vehicle_t *pVeh = NULL;
- if ( ent->client && ent->client->NPC_class == CLASS_VEHICLE )
- {
- pVeh = ent->m_pVehicle;
- }
- //Don't let the player do anything if in a camera
- if ( (ent->s.eFlags&EF_HELD_BY_RANCOR)
- || (ent->s.eFlags&EF_HELD_BY_WAMPA) )
- {
- G_HeldByMonster( ent, &ucmd );
- }
- if ( ent->s.number == 0 )
- {
- extern cvar_t *g_skippingcin;
- if ( ent->s.eFlags & EF_LOCKED_TO_WEAPON )
- {
- G_UpdateEmplacedWeaponData( ent );
- RunEmplacedWeapon( ent, &ucmd );
- }
- if ( ent->client->ps.saberLockTime > level.time && ent->client->ps.saberLockEnemy != ENTITYNUM_NONE )
- {
- NPC_SetLookTarget( ent, ent->client->ps.saberLockEnemy, level.time+1000 );
- }
- if ( ent->client->renderInfo.lookTargetClearTime < level.time //NOTE: here this is used as a debounce, not an actual timer
- && ent->health > 0 //must be alive
- && (!ent->enemy || ent->client->ps.saberMove != LS_A_BACKSTAB) )//don't update if in backstab unless don't currently have an enemy
- {//NOTE: doesn't keep updating to nearest enemy once you're dead
- int newLookTarget;
- if ( !G_ValidateLookEnemy( ent, ent->enemy ) )
- {
- ent->enemy = NULL;
- }
- //FIXME: make this a little prescient?
- G_ChooseLookEnemy( ent, ucmd );
- if ( ent->enemy )
- {//target
- newLookTarget = ent->enemy->s.number;
- //so we don't change our minds in the next 1 second
- ent->client->renderInfo.lookTargetClearTime = level.time+1000;
- ent->client->renderInfo.lookMode = LM_ENT;
- }
- else
- {//no target
- //FIXME: what about sightalerts and missiles?
- newLookTarget = ENTITYNUM_NONE;
- newLookTarget = G_FindLocalInterestPoint( ent );
- if ( newLookTarget != ENTITYNUM_NONE )
- {//found something of interest
- ent->client->renderInfo.lookMode = LM_INTEREST;
- }
- else
- {//okay, no interesting things and no enemies, so look for items
- newLookTarget = G_FindLookItem( ent );
- ent->client->renderInfo.lookMode = LM_ENT;
- }
- }
- if ( ent->client->renderInfo.lookTarget != newLookTarget )
- {//transitioning
- NPC_SetLookTarget( ent, newLookTarget, level.time+1000 );
- }
- }
- if ( in_camera )
- {
- // watch the code here, you MUST "return" within this IF(), *unless* you're stopping the cinematic skip.
- //
- if ( ClientCinematicThink(ent->client) )
- {
- if (g_skippingcin->integer) // already doing cinematic skip?
- {// yes... so stop skipping...
- G_StopCinematicSkip();
- }
- else
- {// no... so start skipping...
- G_StartCinematicSkip();
- return;
- }
- }
- else
- {
- return;
- }
- }
- // If he's riding the vehicle...
- else if ( ent->s.m_iVehicleNum != 0 && ent->health > 0 )
- {
- }
- else
- {
- if ( g_skippingcin->integer )
- {//We're skipping the cinematic and it's over now
- gi.cvar_set("timescale", "1");
- gi.cvar_set("skippingCinematic", "0");
- }
- if ( ent->client->ps.pm_type == PM_DEAD && cg.missionStatusDeadTime < level.time )
- {//mission status screen is up because player is dead, stop all scripts
- if (Q_stricmpn(level.mapname,"_holo",5)) {
- stop_icarus = qtrue;
- }
- }
- }
- // // Don't allow the player to adjust the pitch when they are in third person overhead cam.
- //extern vmCvar_t cg_thirdPerson;
- // if ( cg_thirdPerson.integer == 2 )
- // {
- // ucmd->angles[PITCH] = 0;
- // }
- if ( cg.zoomMode == 2 )
- {
- // Any kind of movement when the player is NOT ducked when the disruptor gun is zoomed will cause us to auto-magically un-zoom
- if ( ( (ucmd->forwardmove||ucmd->rightmove)
- && ucmd->upmove >= 0 //crouching-moving is ok
- && !(ucmd->buttons&BUTTON_USE)/*leaning is ok*/
- )
- || ucmd->upmove > 0 //jumping not allowed
- )
- {
- // already zooming, so must be wanting to turn it off
- G_Sound( ent, G_SoundIndex( "sound/weapons/disruptor/zoomend.wav" ));
- #ifdef _IMMERSION
- G_Force( ent, G_ForceIndex( "fffx/weapons/disruptor/zoomend", FF_CHANNEL_WEAPON ) );
- #endif // _IMMERSION
- cg.zoomMode = 0;
- cg.zoomTime = cg.time;
- cg.zoomLocked = qfalse;
- }
- }
- if ( (player_locked
- || (ent->client->ps.eFlags&EF_FORCE_GRIPPED)
- || (ent->client->ps.eFlags&EF_FORCE_DRAINED)
- || (ent->client->ps.legsAnim==BOTH_PLAYER_PA_1)
- || (ent->client->ps.legsAnim==BOTH_PLAYER_PA_2)
- || (ent->client->ps.legsAnim==BOTH_PLAYER_PA_3))
- && ent->client->ps.pm_type < PM_DEAD ) // unless dead
- {//lock out player control
- if ( !player_locked )
- {
- VectorClear( ucmd->angles );
- }
- ucmd->forwardmove = 0;
- ucmd->rightmove = 0;
- ucmd->buttons = 0;
- ucmd->upmove = 0;
- PM_AdjustAnglesToGripper( ent, ucmd );
- }
- if ( ent->client->ps.leanofs )
- {//no shooting while leaning
- ucmd->buttons &= ~BUTTON_ATTACK;
- if ( ent->client->ps.weapon != WP_DISRUPTOR )
- {//can still zoom around corners
- ucmd->buttons &= ~BUTTON_ALT_ATTACK;
- }
- }
- }
- else
- {
- if ( ent->s.eFlags & EF_LOCKED_TO_WEAPON )
- {
- G_UpdateEmplacedWeaponData( ent );
- }
- if ( player && player->client && player->client->ps.viewEntity == ent->s.number )
- {
- controlledByPlayer = qtrue;
- int sav_weapon = ucmd->weapon;
- memcpy( ucmd, &player->client->usercmd, sizeof( usercmd_t ) );
- ucmd->weapon = sav_weapon;
- ent->client->usercmd = *ucmd;
- }
- // Transfer over our driver's commands to us (the vehicle).
- if ( ent->owner && ent->client && ent->client->NPC_class == CLASS_VEHICLE )
- {
- memcpy( ucmd, &ent->owner->client->usercmd, sizeof( usercmd_t ) );
- ucmd->buttons &= ~BUTTON_USE;//Vehicles NEVER try to use ANYTHING!!!
- //ucmd->weapon = ent->client->ps.weapon; // but keep our weapon.
- ent->client->usercmd = *ucmd;
- }
- G_NPCMunroMatchPlayerWeapon( ent );
- }
- // If we are a vehicle, update ourself.
- if ( pVeh
- && (pVeh->m_pVehicleInfo->Inhabited(pVeh)
- || pVeh->m_iBoarding!=0
- || pVeh->m_pVehicleInfo->type!=VH_ANIMAL) )
- {
- pVeh->m_pVehicleInfo->Update( pVeh, ucmd );
- }
- else if ( ent->client )
- {//this is any client that is not a vehicle (OR: is a vehicle and it not being ridden, is not being boarded, or is a TaunTaun...!
- if ( ent->client->NPC_class == CLASS_GONK ||
- ent->client->NPC_class == CLASS_MOUSE ||
- ent->client->NPC_class == CLASS_R2D2 ||
- ent->client->NPC_class == CLASS_R5D2 )
- {//no jumping or strafing in these guys
- ucmd->upmove = ucmd->rightmove = 0;
- }
- else if ( ent->client->NPC_class == CLASS_ATST || ent->client->NPC_class == CLASS_RANCOR )
- {//no jumping in atst
- if (ent->client->ps.pm_type != PM_NOCLIP)
- {
- ucmd->upmove = 0;
- }
- if ( ent->client->ps.groundEntityNum != ENTITYNUM_NONE )
- {//ATST crushes anything underneath it
- gentity_t *under = &g_entities[ent->client->ps.groundEntityNum];
- if ( under && under->health && under->takedamage )
- {
- vec3_t down = {0,0,-1};
- //FIXME: we'll be doing traces down from each foot, so we'll have a real impact origin
- G_Damage( under, ent, ent, down, under->currentOrigin, 100, 0, MOD_CRUSH );
- }
- //so they know to run like hell when I get close
- //FIXME: project this in the direction I'm moving?
- if ( ent->client->NPC_class != CLASS_RANCOR && !Q_irand( 0, 10 ) )
- {//not so often...
- AddSoundEvent( ent, ent->currentOrigin, ent->maxs[1]*5, AEL_DANGER, qfalse, qtrue );
- AddSightEvent( ent, ent->currentOrigin, ent->maxs[1]*5, AEL_DANGER, 100 );
- }
- }
- }
- else if ( ent->client->ps.groundEntityNum < ENTITYNUM_WORLD && !ent->client->ps.forceJumpCharge )
- {//standing on an entity and not currently force jumping
- gentity_t *groundEnt = &g_entities[ent->client->ps.groundEntityNum];
- if ( groundEnt && groundEnt->client )
- {
- // If you landed on a speeder or animal vehicle...
- if ( groundEnt->client && groundEnt->client->NPC_class == CLASS_VEHICLE )
- {
- if ( ent->client->NPC_class != CLASS_VEHICLE )
- {//um... vehicles shouldn't ride other vehicles, mmkay?
- if ( (groundEnt->m_pVehicle->m_pVehicleInfo->type == VH_ANIMAL && PM_HasAnimation( ent, BOTH_VT_IDLE ))
- || (groundEnt->m_pVehicle->m_pVehicleInfo->type == VH_SPEEDER && PM_HasAnimation( ent, BOTH_VS_IDLE )) )
- {
- //groundEnt->m_pVehicle->m_iBoarding = -3; // Land From Behind
- groundEnt->m_pVehicle->m_pVehicleInfo->Board( groundEnt->m_pVehicle, ent );
- }
- }
- }
- else if ( groundEnt->client && groundEnt->client->NPC_class == CLASS_SAND_CREATURE
- && G_HasKnockdownAnims( ent ) )
- {//step on a sand creature = *you* fall down
- G_Knockdown( ent, groundEnt, vec3_origin, 300, qtrue );
- }
- else if ( groundEnt->client && groundEnt->client->NPC_class == CLASS_RANCOR )
- {//step on a Rancor, it bucks & throws you off
- if ( groundEnt->client->ps.legsAnim != BOTH_ATTACK3
- && groundEnt->client->ps.legsAnim != BOTH_ATTACK4
- && groundEnt->client->ps.legsAnim != BOTH_BUCK_RIDER )
- {//don't interrupt special anims
- vec3_t throwDir, right;
- AngleVectors( groundEnt->currentAngles, throwDir, right, NULL );
- VectorScale(throwDir,-1,throwDir);
- VectorMA( throwDir, Q_flrand( -0.5f, 0.5f), right, throwDir );
- throwDir[2] = 0.2f;
- VectorNormalize( throwDir );
- if ( !(ent->flags&FL_NO_KNOCKBACK) )
- {
- G_Throw( ent, throwDir, Q_flrand( 50, 200 ) );
- }
- if ( G_HasKnockdownAnims( ent ) )
- {
- G_Knockdown( ent, groundEnt, vec3_origin, 300, qtrue );
- }
- NPC_SetAnim( groundEnt, SETANIM_BOTH, BOTH_BUCK_RIDER, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);
- }
- }
- else if ( groundEnt->client->ps.groundEntityNum != ENTITYNUM_NONE &&
- groundEnt->health > 0 && !PM_InRoll( &groundEnt->client->ps )
- && !(groundEnt->client->ps.eFlags&EF_LOCKED_TO_WEAPON)
- && !(groundEnt->client->ps.eFlags&EF_HELD_BY_RANCOR)
- && !(groundEnt->client->ps.eFlags&EF_HELD_BY_WAMPA)
- && !(groundEnt->client->ps.eFlags&EF_HELD_BY_SAND_CREATURE)
- && !inSpinFlipAttack )
- {//landed on a live client who is on the ground, jump off them and knock them down
- qboolean forceKnockdown = qfalse;
-
- // If in a vehicle when land on someone, always knockdown.
- if ( pVeh )
- {
- forceKnockdown = qtrue;
- }
- else if ( ent->s.number
- && ent->NPC
- && ent->client->ps.forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_0 )//ent->s.weapon == WP_SABER )
- {//force-jumper landed on someone
- //don't jump off, too many ledges, plus looks weird
- if ( groundEnt->client->playerTeam != ent->client->playerTeam )
- {//don't knock down own guys
- forceKnockdown = (qboolean)(Q_irand( 0, RANK_CAPTAIN+4 )<ent->NPC->rank);
- }
- //now what... push the groundEnt out of the way?
- if ( !ent->client->ps.velocity[0]
- && !ent->client->ps.velocity[1] )
- {//not moving, shove us a little
- vec3_t slideFwd;
- AngleVectors( ent->client->ps.viewangles, slideFwd, NULL, NULL );
- slideFwd[2] = 0.0f;
- VectorNormalize( slideFwd );
- ent->client->ps.velocity[0] = slideFwd[0]*10.0f;
- ent->client->ps.velocity[1] = slideFwd[1]*10.0f;
- }
- //slide for a little
- ent->client->ps.pm_flags |= PMF_TIME_NOFRICTION;
- ent->client->ps.pm_time = 100;
- }
- else if ( ent->health > 0 )
- {
- if ( !PM_InRoll( &ent->client->ps )
- && !PM_FlippingAnim( ent->client->ps.legsAnim ) )
- {
- if ( ent->s.number && ent->s.weapon == WP_SABER )
- {
- ent->client->ps.forceJumpCharge = 320;//FIXME: calc this intelligently?
- }
- else if ( !ucmd->upmove )
- {//if not ducking (which should cause a roll), then jump
- ucmd->upmove = 127;
- }
- if ( !ucmd->forwardmove && !ucmd->rightmove )
- {// If not moving, don't want to jump straight up
- //FIXME: trace for clear di?
- if ( !Q_irand( 0, 3 ) )
- {
- ucmd->forwardmove = 127;
- }
- else if ( !Q_irand( 0, 3 ) )
- {
- ucmd->forwardmove = -127;
- }
- else if ( !Q_irand( 0, 1 ) )
- {
- ucmd->rightmove = 127;
- }
- else
- {
- ucmd->rightmove = -127;
- }
- }
- if ( !ent->s.number && ucmd->upmove < 0 )
- {//player who should roll- force it
- int rollAnim = BOTH_ROLL_F;
- if ( ucmd->forwardmove >= 0 )
- {
- rollAnim = BOTH_ROLL_F;
- }
- else if ( ucmd->forwardmove < 0 )
- {
- rollAnim = BOTH_ROLL_B;
- }
- else if ( ucmd->rightmove > 0 )
- {
- rollAnim = BOTH_ROLL_R;
- }
- else if ( ucmd->rightmove < 0 )
- {
- rollAnim = BOTH_ROLL_L;
- }
- NPC_SetAnim(ent,SETANIM_BOTH,rollAnim,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);
- G_AddEvent( ent, EV_ROLL, 0 );
- ent->client->ps.saberMove = LS_NONE;
- }
- }
- }
- else
- {//a corpse? Shit
- //Hmm, corpses should probably *always* knockdown...
- forceKnockdown = qtrue;
- ent->clipmask &= ~CONTENTS_BODY;
- }
- //FIXME: need impact sound event
- GEntity_PainFunc( groundEnt, ent, ent, groundEnt->currentOrigin, 0, MOD_CRUSH );
- if ( !forceKnockdown
- && groundEnt->client->NPC_class == CLASS_DESANN
- && ent->client->NPC_class != CLASS_LUKE )
- {//can't knock down desann unless you're luke
- //FIXME: should he smack you away like Galak Mech?
- }
- else if ( forceKnockdown //forced
- || ent->client->NPC_class == CLASS_DESANN //desann always knocks people down
- || ( ( (groundEnt->s.number&&(groundEnt->s.weapon!=WP_SABER||!groundEnt->NPC||groundEnt->NPC->rank<Q_irand(RANK_CIVILIAN,RANK_CAPTAIN+1))) //an NPC who is either not a saber user or passed the rank-based probability test
- || ((!ent->s.number||G_ControlledByPlayer(groundEnt)) && !Q_irand( 0, 3 )&&cg.renderingThirdPerson&&!cg.zoomMode) )//or a player in third person, 25% of the time
- && groundEnt->client->playerTeam != ent->client->playerTeam//and not on the same team
- && ent->client->ps.legsAnim != BOTH_JUMPATTACK6 ) )//not in the sideways-spinning jump attack
- {
- int knockAnim = BOTH_KNOCKDOWN1;
- if ( PM_CrouchAnim( groundEnt->client->ps.legsAnim ) )
- {//knockdown from crouch
- knockAnim = BOTH_KNOCKDOWN4;
- }
- else
- {
- vec3_t gEFwd, gEAngles = {0,groundEnt->client->ps.viewangles[YAW],0};
- AngleVectors( gEAngles, gEFwd, NULL, NULL );
- if ( DotProduct( ent->client->ps.velocity, gEFwd ) > 50 )
- {//pushing him forward
- knockAnim = BOTH_KNOCKDOWN3;
- }
- }
- NPC_SetAnim( groundEnt, SETANIM_BOTH, knockAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
- }
- }
- }
- }
- }
- client = ent->client;
- // mark the time, so the connection sprite can be removed
- client->lastCmdTime = level.time;
- client->pers.lastCommand = *ucmd;
- // sanity check the command time to prevent speedup cheating
- if ( ucmd->serverTime > level.time + 200 )
- {
- ucmd->serverTime = level.time + 200;
- }
- if ( ucmd->serverTime < level.time - 1000 )
- {
- ucmd->serverTime = level.time - 1000;
- }
- msec = ucmd->serverTime - client->ps.commandTime;
- if ( msec < 1 )
- {
- msec = 1;
- }
- if ( msec > 200 )
- {
- msec = 200;
- }
- if ( client->noclip )
- {
- client->ps.pm_type = PM_NOCLIP;
- }
- else if ( client->ps.stats[STAT_HEALTH] <= 0 )
- {
- client->ps.pm_type = PM_DEAD;
- }
- else
- {
- client->ps.pm_type = PM_NORMAL;
- }
- //FIXME: if global gravity changes this should update everyone's personal gravity...
- if ( !(ent->svFlags & SVF_CUSTOM_GRAVITY) )
- {
- if (ent->client->inSpaceIndex)
- { //in space, so no gravity...
- client->ps.gravity = 0.0f;
- }
- else
- {
- client->ps.gravity = g_gravity->value;
- }
- }
- if (!USENEWNAVSYSTEM || ent->s.number==0)
- {
- ClientAlterSpeed(ent, ucmd, controlledByPlayer, 0);
- }
- //FIXME: need to do this before check to avoid walls and cliffs (or just cliffs?)
- BG_AddPushVecToUcmd( ent, ucmd );
- G_CheckClampUcmd( ent, ucmd );
- WP_ForcePowersUpdate( ent, ucmd );
- //if we have the saber in hand, check for starting a block to reflect shots
- if ( ent->s.number < MAX_CLIENTS//player
- || ( ent->NPC && G_JediInNormalAI( ent ) ) )//NPC jedi not in a special AI mode
- {
- WP_SaberStartMissileBlockCheck( ent, ucmd );
- }
- // Update the position of the saber, and check to see if we're throwing it
- if ( client->ps.saberEntityNum != ENTITYNUM_NONE )
- {
- int updates = 1;
- if ( ent->NPC )
- {
- updates = 3;//simulate player update rate?
- }
- for ( int update = 0; update < updates; update++ )
- {
- WP_SaberUpdate( ent, ucmd );
- }
- }
- //NEED to do this every frame, since these overrides do not go into the save/load data
- if ( ent->client && ent->s.m_iVehicleNum != 0 && !ent->s.number && !MatrixMode)
- {//FIXME: extern and read from g_vehicleInfo?
- Vehicle_t *pPlayerVeh = ent->owner->m_pVehicle;
- if ( pPlayerVeh && pPlayerVeh->m_pVehicleInfo->cameraOverride )
- {
- // Vehicle Camera Overrides
- //--------------------------
- cg.overrides.active |= ( CG_OVERRIDE_3RD_PERSON_RNG | CG_OVERRIDE_FOV | CG_OVERRIDE_3RD_PERSON_VOF | CG_OVERRIDE_3RD_PERSON_POF );
- cg.overrides.thirdPersonRange = pPlayerVeh->m_pVehicleInfo->cameraRange;
- cg.overrides.fov = pPlayerVeh->m_pVehicleInfo->cameraFOV;
- cg.overrides.thirdPersonVertOffset = pPlayerVeh->m_pVehicleInfo->cameraVertOffset;
- cg.overrides.thirdPersonPitchOffset = pPlayerVeh->m_pVehicleInfo->cameraPitchOffset;
- if ( pPlayerVeh->m_pVehicleInfo->cameraAlpha )
- {
- cg.overrides.active |= CG_OVERRIDE_3RD_PERSON_APH;
- }
- // If In A Speeder (NOT DURING TURBO)
- //------------------------------------
- if ((level.time>pPlayerVeh->m_iTurboTime) && pPlayerVeh->m_pVehicleInfo->type==VH_SPEEDER)
- {
- // If Using Strafe And Use Keys
- //------------------------------
- if ((pPlayerVeh->m_ucmd.rightmove!=0) &&
- // (pPlayerVeh->m_pParentEntity->client->ps.speed>=0) &&
- (pPlayerVeh->m_ucmd.buttons&BUTTON_USE)
- )
- {
- cg.overrides.active |= CG_OVERRIDE_3RD_PERSON_ANG; // Turn On Angle Offset
- cg.overrides.thirdPersonRange *= -2; // Camera In Front Of Player
- cg.overrides.thirdPersonAngle = (pPlayerVeh->m_ucmd.rightmove>0)?(20):(-20);
- }
- // Auto Pullback Of Camera To Show Enemy
- //---------------------------------------
- else
- {
- cg.overrides.active &= ~CG_OVERRIDE_3RD_PERSON_ANG; // Turn Off Angle Offset
- if (ent->enemy)
- {
- vec3_t actorDirection;
- vec3_t enemyDirection;
- AngleVectors(ent->currentAngles, actorDirection, 0, 0);
- VectorSubtract(ent->enemy->currentOrigin, ent->currentOrigin, enemyDirection);
- float enemyDistance = VectorNormalize(enemyDirection);
- if (enemyDistance>cg.overrides.thirdPersonRange && enemyDistance<400 && DotProduct(actorDirection, enemyDirection)<-0.5f)
- {
- cg.overrides.thirdPersonRange = enemyDistance;
- }
- }
- }
- }
- }
- }
- else if ( client->ps.eFlags&EF_IN_ATST )
- {
- cg.overrides.active |= (CG_OVERRIDE_3RD_PERSON_RNG|CG_OVERRIDE_3RD_PERSON_POF|CG_OVERRIDE_3RD_PERSON_VOF);
- cg.overrides.thirdPersonRange = 240;
- if ( cg_thirdPersonAutoAlpha.integer )
- {
- if ( ent->health > 0 && ent->client->ps.viewangles[PITCH] < 15 && ent->client->ps.viewangles[PITCH] > 0 )
- {
- cg.overrides.active |= CG_OVERRIDE_3RD_PERSON_APH;
- if ( cg.overrides.thirdPersonAlpha > 0.525f )
- {
- cg.overrides.thirdPersonAlpha -= 0.025f;
- }
- else if ( cg.overrides.thirdPersonAlpha > 0.5f )
- {
- cg.overrides.thirdPersonAlpha = 0.5f;
- }
- }
- else if ( cg.overrides.active&CG_OVERRIDE_3RD_PERSON_APH )
- {
- if ( cg.overrides.thirdPersonAlpha > cg_thirdPersonAlpha.value )
- {
- cg.overrides.active &= ~CG_OVERRIDE_3RD_PERSON_APH;
- }
- else if ( cg.overrides.thirdPersonAlpha < cg_thirdPersonAlpha.value-0.1f )
- {
- cg.overrides.thirdPersonAlpha += 0.1f;
- }
- else if ( cg.overrides.thirdPersonAlpha < cg_thirdPersonAlpha.value )
- {
- cg.overrides.thirdPersonAlpha = cg_thirdPersonAlpha.value;
- cg.overrides.active &= ~CG_OVERRIDE_3RD_PERSON_APH;
- }
- }
- }
- if ( ent->client->ps.viewangles[PITCH] > 0 )
- {
- cg.overrides.thirdPersonPitchOffset = ent->client->ps.viewangles[PITCH]*-0.75;
- cg.overrides.thirdPersonVertOffset = 300+ent->client->ps.viewangles[PITCH]*-10;
- if ( cg.overrides.thirdPersonVertOffset < 0 )
- {
- cg.overrides.thirdPersonVertOffset = 0;
- }
- }
- else if ( ent->client->ps.viewangles[PITCH] < 0 )
- {
- cg.overrides.thirdPersonPitchOffset = ent->client->ps.viewangles[PITCH]*-0.75;
- cg.overrides.thirdPersonVertOffset = 300+ent->client->ps.viewangles[PITCH]*-5;
- if ( cg.overrides.thirdPersonVertOffset > 300 )
- {
- cg.overrides.thirdPersonVertOffset = 300;
- }
- }
- else
- {
- cg.overrides.thirdPersonPitchOffset = 0;
- cg.overrides.thirdPersonVertOffset = 200;
- }
- }
- //play/stop any looping sounds tied to controlled movement
- G_CheckMovingLoopingSounds( ent, ucmd );
- //remember your last angles
- VectorCopy ( ent->client->ps.viewangles, ent->lastAngles );
- // set up for pmove
- oldEventSequence = client->ps.eventSequence;
- memset( &pm, 0, sizeof(pm) );
- pm.gent = ent;
- pm.ps = &client->ps;
- pm.cmd = *ucmd;
- // pm.tracemask = MASK_PLAYERSOLID; // used differently for navgen
- pm.tracemask = ent->clipmask;
- pm.trace = gi.trace;
- pm.pointcontents = gi.pointcontents;
- pm.debugLevel = g_debugMove->integer;
- pm.noFootsteps = 0;//( g_dmflags->integer & DF_NO_FOOTSTEPS ) > 0;
- if ( ent->client && ent->NPC )
- {
- pm.cmd.weapon = ent->client->ps.weapon;
- }
- VectorCopy( client->ps.origin, oldOrigin );
- #ifdef _XBOX
- // if we're an npc then set the waterlevel
- // based on the entity structure
- // otherwise, zero it
- if(ent->s.number != 0)
- {
- pm.waterlevel = ent->waterlevel;
- pm.watertype = ent->watertype;
- }
- else
- {
- pm.waterlevel = 0;
- pm.watertype = 0;
- }
- #endif
- // perform a pmove
- Pmove( &pm );
- pm.gent = 0;
- ProcessGenericCmd(ent, pm.cmd.generic_cmd);
- // save results of pmove
- if ( ent->client->ps.eventSequence != oldEventSequence )
- {
- ent->eventTime = level.time;
- {
- int seq;
- seq = (ent->client->ps.eventSequence-1) & (MAX_PS_EVENTS-1);
- ent->s.event = ent->client->ps.events[ seq ] | ( ( ent->client->ps.eventSequence & 3 ) << 8 );
- ent->s.eventParm = ent->client->ps.eventParms[ seq ];
- }
- }
- PlayerStateToEntityState( &ent->client->ps, &ent->s );
- VectorCopy ( ent->currentOrigin, ent->lastOrigin );
- #if 1
- // use the precise origin for linking
- VectorCopy( ent->client->ps.origin, ent->currentOrigin );
- #else
- //We don't use prediction anymore, so screw this
- // use the snapped origin for linking so it matches client predicted versions
- VectorCopy( ent->s.pos.trBase, ent->currentOrigin );
- #endif
- //Had to leave this in, some legacy code must still be using s.angles
- //Shouldn't interfere with interpolation of angles, should it?
- VectorCopy(ent->client->ps.viewangles , ent->currentAngles );
- // if (pVeh)
- // {
- // gi.Printf("%d\n", ucmd->angles[2]);
- // }
- VectorCopy( pm.mins, ent->mins );
- VectorCopy( pm.maxs, ent->maxs );
- #ifdef _XBOX
- // if this is the player then set the ent water level
- // npcs are updated elsewhere
- if(ent->s.number == 0)
- {
- ent->waterlevel = pm.waterlevel;
- ent->watertype = pm.watertype;
- }
- #else
- ent->waterlevel = pm.waterlevel;
- ent->watertype = pm.watertype;
- #endif
- _VectorCopy( ucmd->angles, client->pers.cmd_angles );
- // execute client events
- ClientEvents( ent, oldEventSequence );
- if ( pm.useEvent )
- {
- //TODO: Use
- TryUse( ent );
- }
- // link entity now, after any personal teleporters have been used
- gi.linkentity( ent );
- ent->client->hiddenDist = 0;
- if ( !ent->client->noclip )
- {
- G_TouchTriggersLerped( ent );
- }
- // touch other objects
- ClientImpacts( ent, &pm );
- // swap and latch button actions
- client->oldbuttons = client->buttons;
- client->buttons = ucmd->buttons;
- client->latched_buttons |= client->buttons & ~client->oldbuttons;
- // check for respawning
- if ( client->ps.stats[STAT_HEALTH] <= 0 )
- {
- if( client->ps.clientNum == 0 )
- {
- Cbuf_ExecuteText(EXEC_NOW, "-moveup");
- Cbuf_ExecuteText(EXEC_NOW, "-movedown");
- Cbuf_ExecuteText(EXEC_NOW, "-hotswap1");
- Cbuf_ExecuteText(EXEC_NOW, "-hotswap2");
- Cbuf_ExecuteText(EXEC_NOW, "-hotswap3");
- Cbuf_ExecuteText(EXEC_NOW, "-attack");
- Cbuf_ExecuteText(EXEC_NOW, "-altattack");
- Cbuf_ExecuteText(EXEC_NOW, "-use");
- Cbuf_ExecuteText(EXEC_NOW, "-useforce");
- }
- // wait for the attack button to be pressed
- if ( ent->NPC == NULL && level.time > client->respawnTime )
- {
- // don't allow respawn if they are still flying through the
- // air, unless 10 extra seconds have passed, meaning something
- // strange is going on, like the corpse is caught in a wind tunnel
- /*
- if ( level.time < client->respawnTime + 10000 )
- {
- if ( client->ps.groundEntityNum == ENTITYNUM_NONE )
- {
- return;
- }
- }
- */
- // pressing attack or use is the normal respawn method
- //if ( ucmd->buttons & ( BUTTON_ATTACK ) )
- //{
-
- // respawn( ent );
- //}
- }
- if ( ent
- && !ent->s.number
- && ent->enemy
- && ent->enemy != ent
- && ent->enemy->s.number < ENTITYNUM_WORLD
- && ent->enemy->inuse
- && !(cg.overrides.active&CG_OVERRIDE_3RD_PERSON_ANG) )
- {//keep facing enemy
- vec3_t deadDir;
- float deadYaw;
- VectorSubtract( ent->enemy->currentOrigin, ent->currentOrigin, deadDir );
- deadYaw = AngleNormalize180( vectoyaw ( deadDir ) );
- if ( deadYaw > ent->client->ps.stats[STAT_DEAD_YAW] + 1 )
- {
- ent->client->ps.stats[STAT_DEAD_YAW]++;
- }
- else if ( deadYaw < ent->client->ps.stats[STAT_DEAD_YAW] - 1 )
- {
- ent->client->ps.stats[STAT_DEAD_YAW]--;
- }
- else
- {
- ent->client->ps.stats[STAT_DEAD_YAW] = deadYaw;
- }
- }
- return;
- }
- // perform once-a-second actions
- ClientTimerActions( ent, msec );
- ClientEndPowerUps( ent );
- //try some idle anims on ent if getting no input and not moving for some time
- G_CheckClientIdle( ent, ucmd );
- extern void FF_XboxSaberRumble( void );
- if(ent->s.number == 0) // player
- {
- if(ent->client->ps.saberLockTime > level.time)
- {
- FF_XboxSaberRumble();
- }
- }
- }
- /*
- ==================
- ClientThink
- A new command has arrived from the client
- ==================
- */
- extern void PM_CheckForceUseButton( gentity_t *ent, usercmd_t *ucmd );
- extern qboolean PM_GentCantJump( gentity_t *gent );
- extern qboolean PM_WeaponOkOnVehicle( int weapon );
- void ClientThink( int clientNum, usercmd_t *ucmd ) {
- gentity_t *ent;
- qboolean restore_ucmd = qfalse;
- usercmd_t sav_ucmd = {0};
- ent = g_entities + clientNum;
- if ( ent->s.number<MAX_CLIENTS )
- {
- if ( ent->client->ps.viewEntity > 0 && ent->client->ps.viewEntity < ENTITYNUM_WORLD )
- {//you're controlling another NPC
- gentity_t *controlled = &g_entities[ent->client->ps.viewEntity];
- qboolean freed = qfalse;
- if ( controlled->NPC
- && controlled->NPC->controlledTime
- && ent->client->ps.forcePowerLevel[FP_TELEPATHY] > FORCE_LEVEL_3 )
- {//An NPC I'm controlling with mind trick
- if ( controlled->NPC->controlledTime < level.time )
- {//time's up!
- G_ClearViewEntity( ent );
- freed = qtrue;
- }
- }
- else if ( controlled->client //an NPC
- && PM_GentCantJump( controlled ) //that cannot jump
- && controlled->client->moveType != MT_FLYSWIM ) //and does not use upmove to fly
- {//these types use jump to get out
- if ( ucmd->upmove > 0 )
- {//jumping gets you out of it FIXME: check some other button instead... like ESCAPE... so you could even have total control over an NPC?
- G_ClearViewEntity( ent );
- ucmd->upmove = 0;//ucmd->buttons = 0;
- //stop player from doing anything for a half second after
- ent->aimDebounceTime = level.time + 500;
- freed = qtrue;
- }
- }
- if ( !freed )
- {//still controlling, save off my ucmd and clear it for my actual run through pmove
- restore_ucmd = qtrue;
- memcpy( &sav_ucmd, ucmd, sizeof( usercmd_t ) );
- memset( ucmd, 0, sizeof( usercmd_t ) );
- //to keep pointing in same dir, need to set ucmd->angles
- ucmd->angles[PITCH] = ANGLE2SHORT( ent->client->ps.viewangles[PITCH] ) - ent->client->ps.delta_angles[PITCH];
- ucmd->angles[YAW] = ANGLE2SHORT( ent->client->ps.viewangles[YAW] ) - ent->client->ps.delta_angles[YAW];
- ucmd->angles[ROLL] = 0;
- if ( controlled->NPC )
- {
- VectorClear( controlled->client->ps.moveDir );
- controlled->client->ps.speed = (sav_ucmd.buttons&BUTTON_WALKING)?controlled->NPC->stats.walkSpeed:controlled->NPC->stats.runSpeed;
- }
- }
- else
- {
- ucmd->angles[PITCH] = ANGLE2SHORT( ent->client->ps.viewangles[PITCH] ) - ent->client->ps.delta_angles[PITCH];
- ucmd->angles[YAW] = ANGLE2SHORT( ent->client->ps.viewangles[YAW] ) - ent->client->ps.delta_angles[YAW];
- ucmd->angles[ROLL] = 0;
- }
- }
- else if ( ent->client->NPC_class == CLASS_ATST )
- {
- if ( ucmd->upmove > 0 )
- {//get out of ATST
- GEntity_UseFunc( ent->activator, ent, ent );
- ucmd->upmove = 0;//ucmd->buttons = 0;
- }
- }
- PM_CheckForceUseButton( ent, ucmd );
- }
- Vehicle_t *pVeh = NULL;
- // Rider logic.
- // NOTE: Maybe this should be extracted into a RiderUpdate() within the vehicle.
- if ( ( pVeh = G_IsRidingVehicle( ent ) ) != 0 )
- {
- // If we're still in the vehicle...
- if ( pVeh->m_pVehicleInfo->UpdateRider( pVeh, ent, ucmd ) )
- {
- restore_ucmd = qtrue;
- memcpy( &sav_ucmd, ucmd, sizeof( usercmd_t ) );
- memset( ucmd, 0, sizeof( usercmd_t ) );
- //to keep pointing in same dir, need to set ucmd->angles
- //ucmd->angles[PITCH] = ANGLE2SHORT( ent->client->ps.viewangles[PITCH] ) - ent->client->ps.delta_angles[PITCH];
- //ucmd->angles[YAW] = ANGLE2SHORT( ent->client->ps.viewangles[YAW] ) - ent->client->ps.delta_angles[YAW];
- //ucmd->angles[ROLL] = 0;
- ucmd->angles[PITCH] = sav_ucmd.angles[PITCH];
- ucmd->angles[YAW] = sav_ucmd.angles[YAW];
- ucmd->angles[ROLL] = sav_ucmd.angles[ROLL];
- //if ( sav_ucmd.weapon != ent->client->ps.weapon && PM_WeaponOkOnVehicle( sav_ucmd.weapon ) )
- {//trying to change weapons to a valid weapon for this vehicle, to preserve this weapon change command
- ucmd->weapon = sav_ucmd.weapon;
- }
- //else
- {//keep our current weapon
- // ucmd->weapon = ent->client->ps.weapon;
- // if ( ent->client->ps.weapon != WP_NONE )
- {//not changing weapons and we are using one of our weapons, not using vehicle weapon
- //so we actually want to do our fire weapon on us, not the vehicle
- ucmd->buttons = (sav_ucmd.buttons&(BUTTON_ATTACK|BUTTON_ALT_ATTACK));
- // sav_ucmd.buttons &= ~ucmd->buttons;
- }
- }
- }
- }
- ent->client->usercmd = *ucmd;
- // if ( !g_syncronousClients->integer )
- {
- ClientThink_real( ent, ucmd );
- }
- // If a vehicle, make sure to attach our driver and passengers here (after we pmove, which is done in Think_Real))
- if ( ent->client && ent->client->NPC_class == CLASS_VEHICLE )
- {
- pVeh = ent->m_pVehicle;
- pVeh->m_pVehicleInfo->AttachRiders( pVeh );
- }
- // ClientThink_real can end up freeing this ent, need to check
- if ( restore_ucmd && ent->client )
- {//restore ucmd for later so NPC you're controlling can refer to them
- memcpy( &ent->client->usercmd, &sav_ucmd, sizeof( usercmd_t ) );
- }
- if ( ent->s.number )
- {//NPCs drown, burn from lava, etc, also
- P_WorldEffects( ent );
- }
- }
- void ClientEndPowerUps( gentity_t *ent )
- {
- int i;
- if ( ent == NULL || ent->client == NULL )
- {
- return;
- }
- // turn off any expired powerups
- for ( i = 0 ; i < MAX_POWERUPS ; i++ )
- {
- if ( ent->client->ps.powerups[ i ] < level.time )
- {
- ent->client->ps.powerups[ i ] = 0;
- }
- }
- }
- /*
- ==============
- ClientEndFrame
- Called at the end of each server frame for each connected client
- A fast client will have multiple ClientThink for each ClientEdFrame,
- while a slow client may have multiple ClientEndFrame between ClientThink.
- ==============
- */
- void ClientEndFrame( gentity_t *ent )
- {
- //
- // If the end of unit layout is displayed, don't give
- // the player any normal movement attributes
- //
- // burn from lava, etc
- P_WorldEffects (ent);
- // apply all the damage taken this frame
- P_DamageFeedback (ent);
- // add the EF_CONNECTION flag if we haven't gotten commands recently
- /*
- if ( level.time - ent->client->lastCmdTime > 1000 ) {
- ent->s.eFlags |= EF_CONNECTION;
- } else {
- ent->s.eFlags &= ~EF_CONNECTION;
- }
- */
- ent->client->ps.stats[STAT_HEALTH] = ent->health; // FIXME: get rid of ent->health...
- // G_SetClientSound (ent);
- }
|