g_weapon.cpp 156 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306530753085309531053115312531353145315531653175318531953205321532253235324532553265327532853295330533153325333533453355336533753385339534053415342534353445345534653475348534953505351535253535354535553565357535853595360536153625363536453655366536753685369537053715372537353745375537653775378
  1. // g_weapon.c
  2. // perform the server side effects of a weapon firing
  3. // leave this line at the top for all g_xxxx.cpp files...
  4. #include "g_headers.h"
  5. #include "g_local.h"
  6. #include "g_functions.h"
  7. #include "anims.h"
  8. #include "b_local.h"
  9. #include "wp_saber.h"
  10. #include "g_vehicles.h"
  11. static vec3_t forward, vright, up;
  12. static vec3_t muzzle;
  13. void drop_charge(gentity_t *ent, vec3_t start, vec3_t dir);
  14. void ViewHeightFix( const gentity_t * const ent );
  15. qboolean LogAccuracyHit( gentity_t *target, gentity_t *attacker );
  16. extern qboolean G_BoxInBounds( const vec3_t point, const vec3_t mins, const vec3_t maxs, const vec3_t boundsMins, const vec3_t boundsMaxs );
  17. extern qboolean Jedi_DodgeEvasion( gentity_t *self, gentity_t *shooter, trace_t *tr, int hitLoc );
  18. extern qboolean PM_DroidMelee( int npc_class );
  19. extern void G_Knockdown( gentity_t *self, gentity_t *attacker, const vec3_t pushDir, float strength, qboolean breakSaberLock );
  20. extern qboolean G_HasKnockdownAnims( gentity_t *ent );
  21. static gentity_t *ent_list[MAX_GENTITIES];
  22. extern cvar_t *g_debugMelee;
  23. // Bryar Pistol
  24. //--------
  25. #define BRYAR_PISTOL_VEL 1800
  26. #define BRYAR_PISTOL_DAMAGE 14
  27. #define BRYAR_CHARGE_UNIT 200.0f // bryar charging gives us one more unit every 200ms--if you change this, you'll have to do the same in bg_pmove
  28. // E11 Blaster
  29. //---------
  30. #define BLASTER_MAIN_SPREAD 0.5f
  31. #define BLASTER_ALT_SPREAD 1.5f
  32. #define BLASTER_NPC_SPREAD 0.5f
  33. #define BLASTER_VELOCITY 2300
  34. #define BLASTER_NPC_VEL_CUT 0.5f
  35. #define BLASTER_NPC_HARD_VEL_CUT 0.7f
  36. #define BLASTER_DAMAGE 20
  37. #define BLASTER_NPC_DAMAGE_EASY 6
  38. #define BLASTER_NPC_DAMAGE_NORMAL 12 // 14
  39. #define BLASTER_NPC_DAMAGE_HARD 16 // 18
  40. // Tenloss Disruptor
  41. //----------
  42. #define DISRUPTOR_MAIN_DAMAGE 14
  43. #define DISRUPTOR_NPC_MAIN_DAMAGE_EASY 5
  44. #define DISRUPTOR_NPC_MAIN_DAMAGE_MEDIUM 10
  45. #define DISRUPTOR_NPC_MAIN_DAMAGE_HARD 15
  46. #define DISRUPTOR_ALT_DAMAGE 12
  47. #define DISRUPTOR_NPC_ALT_DAMAGE_EASY 15
  48. #define DISRUPTOR_NPC_ALT_DAMAGE_MEDIUM 25
  49. #define DISRUPTOR_NPC_ALT_DAMAGE_HARD 30
  50. #define DISRUPTOR_ALT_TRACES 3 // can go through a max of 3 entities
  51. #define DISRUPTOR_CHARGE_UNIT 150.0f // distruptor charging gives us one more unit every 150ms--if you change this, you'll have to do the same in bg_pmove
  52. // Wookie Bowcaster
  53. //----------
  54. #define BOWCASTER_DAMAGE 45
  55. #define BOWCASTER_VELOCITY 1300
  56. #define BOWCASTER_NPC_DAMAGE_EASY 12
  57. #define BOWCASTER_NPC_DAMAGE_NORMAL 24
  58. #define BOWCASTER_NPC_DAMAGE_HARD 36
  59. #define BOWCASTER_SPLASH_DAMAGE 0
  60. #define BOWCASTER_SPLASH_RADIUS 0
  61. #define BOWCASTER_SIZE 2
  62. #define BOWCASTER_ALT_SPREAD 5.0f
  63. #define BOWCASTER_VEL_RANGE 0.3f
  64. #define BOWCASTER_CHARGE_UNIT 200.0f // bowcaster charging gives us one more unit every 200ms--if you change this, you'll have to do the same in bg_pmove
  65. // Heavy Repeater
  66. //----------
  67. #define REPEATER_SPREAD 1.4f
  68. #define REPEATER_NPC_SPREAD 0.7f
  69. #define REPEATER_DAMAGE 8
  70. #define REPEATER_VELOCITY 1600
  71. #define REPEATER_NPC_DAMAGE_EASY 2
  72. #define REPEATER_NPC_DAMAGE_NORMAL 4
  73. #define REPEATER_NPC_DAMAGE_HARD 6
  74. #define REPEATER_ALT_SIZE 3 // half of bbox size
  75. #define REPEATER_ALT_DAMAGE 60
  76. #define REPEATER_ALT_SPLASH_DAMAGE 60
  77. #define REPEATER_ALT_SPLASH_RADIUS 128
  78. #define REPEATER_ALT_VELOCITY 1100
  79. #define REPEATER_ALT_NPC_DAMAGE_EASY 15
  80. #define REPEATER_ALT_NPC_DAMAGE_NORMAL 30
  81. #define REPEATER_ALT_NPC_DAMAGE_HARD 45
  82. // DEMP2
  83. //----------
  84. #define DEMP2_DAMAGE 15
  85. #define DEMP2_VELOCITY 1800
  86. #define DEMP2_NPC_DAMAGE_EASY 6
  87. #define DEMP2_NPC_DAMAGE_NORMAL 12
  88. #define DEMP2_NPC_DAMAGE_HARD 18
  89. #define DEMP2_SIZE 2 // half of bbox size
  90. #define DEMP2_ALT_DAMAGE 15
  91. #define DEMP2_CHARGE_UNIT 500.0f // demp2 charging gives us one more unit every 500ms--if you change this, you'll have to do the same in bg_pmove
  92. #define DEMP2_ALT_RANGE 4096
  93. #define DEMP2_ALT_SPLASHRADIUS 256
  94. // Golan Arms Flechette
  95. //---------
  96. #define FLECHETTE_SHOTS 6
  97. #define FLECHETTE_SPREAD 4.0f
  98. #define FLECHETTE_DAMAGE 15
  99. #define FLECHETTE_VEL 3500
  100. #define FLECHETTE_SIZE 1
  101. #define FLECHETTE_ALT_DAMAGE 20
  102. #define FLECHETTE_ALT_SPLASH_DAM 20
  103. #define FLECHETTE_ALT_SPLASH_RAD 128
  104. // NOT CURRENTLY USED
  105. #define FLECHETTE_MINE_RADIUS_CHECK 200
  106. #define FLECHETTE_MINE_VEL 1000
  107. #define FLECHETTE_MINE_DAMAGE 100
  108. #define FLECHETTE_MINE_SPLASH_DAMAGE 200
  109. #define FLECHETTE_MINE_SPLASH_RADIUS 200
  110. // Personal Rocket Launcher
  111. //---------
  112. #define ROCKET_VELOCITY 900
  113. #define ROCKET_DAMAGE 100
  114. #define ROCKET_SPLASH_DAMAGE 100
  115. #define ROCKET_SPLASH_RADIUS 160
  116. #define ROCKET_NPC_DAMAGE_EASY 20
  117. #define ROCKET_NPC_DAMAGE_NORMAL 40
  118. #define ROCKET_NPC_DAMAGE_HARD 60
  119. #define ROCKET_SIZE 3
  120. #define ROCKET_ALT_VELOCITY (ROCKET_VELOCITY*0.5)
  121. #define ROCKET_ALT_THINK_TIME 100
  122. // some naughty little things that are used cg side
  123. int g_rocketLockEntNum = ENTITYNUM_NONE;
  124. int g_rocketLockTime = 0;
  125. int g_rocketSlackTime = 0;
  126. // Concussion Rifle
  127. //---------
  128. //primary
  129. #define CONC_VELOCITY 3000
  130. #define CONC_DAMAGE 150
  131. #define CONC_NPC_SPREAD 0.7f
  132. #define CONC_NPC_DAMAGE_EASY 15
  133. #define CONC_NPC_DAMAGE_NORMAL 30
  134. #define CONC_NPC_DAMAGE_HARD 50
  135. #define CONC_SPLASH_DAMAGE 50
  136. #define CONC_SPLASH_RADIUS 300
  137. //alt
  138. #define CONC_ALT_DAMAGE 225//100
  139. #define CONC_ALT_NPC_DAMAGE_EASY 10
  140. #define CONC_ALT_NPC_DAMAGE_MEDIUM 20
  141. #define CONC_ALT_NPC_DAMAGE_HARD 30
  142. // Emplaced Gun
  143. //--------------
  144. #define EMPLACED_VEL 6000 // very fast
  145. #define EMPLACED_DAMAGE 150 // and very damaging
  146. #define EMPLACED_SIZE 5 // make it easier to hit things
  147. // ATST Main Gun
  148. //--------------
  149. #define ATST_MAIN_VEL 4000 //
  150. #define ATST_MAIN_DAMAGE 25 //
  151. #define ATST_MAIN_SIZE 3 // make it easier to hit things
  152. // ATST Side Gun
  153. //---------------
  154. #define ATST_SIDE_MAIN_DAMAGE 75
  155. #define ATST_SIDE_MAIN_VELOCITY 1300
  156. #define ATST_SIDE_MAIN_NPC_DAMAGE_EASY 30
  157. #define ATST_SIDE_MAIN_NPC_DAMAGE_NORMAL 40
  158. #define ATST_SIDE_MAIN_NPC_DAMAGE_HARD 50
  159. #define ATST_SIDE_MAIN_SIZE 4
  160. #define ATST_SIDE_MAIN_SPLASH_DAMAGE 10 // yeah, pretty small, either zero out or make it worth having?
  161. #define ATST_SIDE_MAIN_SPLASH_RADIUS 16 // yeah, pretty small, either zero out or make it worth having?
  162. #define ATST_SIDE_ALT_VELOCITY 1100
  163. #define ATST_SIDE_ALT_NPC_VELOCITY 600
  164. #define ATST_SIDE_ALT_DAMAGE 130
  165. #define ATST_SIDE_ROCKET_NPC_DAMAGE_EASY 30
  166. #define ATST_SIDE_ROCKET_NPC_DAMAGE_NORMAL 50
  167. #define ATST_SIDE_ROCKET_NPC_DAMAGE_HARD 90
  168. #define ATST_SIDE_ALT_SPLASH_DAMAGE 130
  169. #define ATST_SIDE_ALT_SPLASH_RADIUS 200
  170. #define ATST_SIDE_ALT_ROCKET_SIZE 5
  171. #define ATST_SIDE_ALT_ROCKET_SPLASH_SCALE 0.5f // scales splash for NPC's
  172. // Stun Baton
  173. //--------------
  174. #define STUN_BATON_DAMAGE 22
  175. #define STUN_BATON_ALT_DAMAGE 22
  176. #define STUN_BATON_RANGE 25
  177. // Laser Trip Mine
  178. //--------------
  179. #define LT_DAMAGE 150
  180. #define LT_SPLASH_RAD 256.0f
  181. #define LT_SPLASH_DAM 90
  182. #define LT_VELOCITY 250.0f
  183. #define LT_ALT_VELOCITY 1000.0f
  184. #define PROX_MINE_RADIUS_CHECK 190
  185. #define LT_SIZE 3.0f
  186. #define LT_ALT_TIME 2000
  187. #define LT_ACTIVATION_DELAY 1000
  188. #define LT_DELAY_TIME 50
  189. // Thermal Detonator
  190. //--------------
  191. #define TD_DAMAGE 100
  192. #define TD_NPC_DAMAGE_CUT 0.6f // NPC thrown dets deliver only 60% of the damage that a player thrown one does
  193. #define TD_SPLASH_RAD 128
  194. #define TD_SPLASH_DAM 90
  195. #define TD_VELOCITY 900
  196. #define TD_MIN_CHARGE 0.15f
  197. #define TD_TIME 4000
  198. #define TD_THINK_TIME 300 // don't think too often?
  199. #define TD_TEST_RAD (TD_SPLASH_RAD * 0.8f) // no sense in auto-blowing up if exactly on the radius edge--it would hardly do any damage
  200. #define TD_ALT_TIME 3000
  201. #define TD_ALT_DAMAGE 100
  202. #define TD_ALT_SPLASH_RAD 128
  203. #define TD_ALT_SPLASH_DAM 90
  204. #define TD_ALT_VELOCITY 600
  205. #define TD_ALT_MIN_CHARGE 0.15f
  206. #define TD_ALT_TIME 3000
  207. // Tusken Rifle Shot
  208. //--------------
  209. #define TUSKEN_RIFLE_VEL 3000 // fast
  210. #define TUSKEN_RIFLE_DAMAGE_EASY 20 // damaging
  211. #define TUSKEN_RIFLE_DAMAGE_MEDIUM 30 // very damaging
  212. #define TUSKEN_RIFLE_DAMAGE_HARD 50 // extremely damaging
  213. // Weapon Helper Functions
  214. float weaponSpeed[WP_NUM_WEAPONS][2] =
  215. {
  216. 0,0,//WP_NONE,
  217. 0,0,//WP_SABER, // NOTE: lots of code assumes this is the first weapon (... which is crap) so be careful -Ste.
  218. BRYAR_PISTOL_VEL,BRYAR_PISTOL_VEL,//WP_BLASTER_PISTOL,
  219. BLASTER_VELOCITY,BLASTER_VELOCITY,//WP_BLASTER,
  220. Q3_INFINITE,Q3_INFINITE,//WP_DISRUPTOR,
  221. BOWCASTER_VELOCITY,BOWCASTER_VELOCITY,//WP_BOWCASTER,
  222. REPEATER_VELOCITY,REPEATER_ALT_VELOCITY,//WP_REPEATER,
  223. DEMP2_VELOCITY,DEMP2_ALT_RANGE,//WP_DEMP2,
  224. FLECHETTE_VEL,FLECHETTE_MINE_VEL,//WP_FLECHETTE,
  225. ROCKET_VELOCITY,ROCKET_ALT_VELOCITY,//WP_ROCKET_LAUNCHER,
  226. TD_VELOCITY,TD_ALT_VELOCITY,//WP_THERMAL,
  227. 0,0,//WP_TRIP_MINE,
  228. 0,0,//WP_DET_PACK,
  229. CONC_VELOCITY,Q3_INFINITE,//WP_CONCUSSION,
  230. 0,0,//WP_MELEE, // Any ol' melee attack
  231. 0,0,//WP_STUN_BATON,
  232. BRYAR_PISTOL_VEL,BRYAR_PISTOL_VEL,//WP_BRYAR_PISTOL,
  233. EMPLACED_VEL,EMPLACED_VEL,//WP_EMPLACED_GUN,
  234. BRYAR_PISTOL_VEL,BRYAR_PISTOL_VEL,//WP_BOT_LASER, // Probe droid - Laser blast
  235. 0,0,//WP_TURRET, // turret guns
  236. ATST_MAIN_VEL,ATST_MAIN_VEL,//WP_ATST_MAIN,
  237. ATST_SIDE_MAIN_VELOCITY,ATST_SIDE_ALT_NPC_VELOCITY,//WP_ATST_SIDE,
  238. EMPLACED_VEL,EMPLACED_VEL,//WP_TIE_FIGHTER,
  239. EMPLACED_VEL,REPEATER_ALT_VELOCITY,//WP_RAPID_FIRE_CONC,
  240. 0,0,//WP_JAWA,
  241. TUSKEN_RIFLE_VEL,TUSKEN_RIFLE_VEL,//WP_TUSKEN_RIFLE,
  242. 0,0,//WP_TUSKEN_STAFF,
  243. 0,0,//WP_SCEPTER,
  244. 0,0,//WP_NOGHRI_STICK,
  245. };
  246. float WP_SpeedOfMissileForWeapon( int wp, qboolean alt_fire )
  247. {
  248. if ( alt_fire )
  249. {
  250. return weaponSpeed[wp][1];
  251. }
  252. return weaponSpeed[wp][0];
  253. }
  254. //-----------------------------------------------------------------------------
  255. static void WP_TraceSetStart( const gentity_t *ent, vec3_t start, const vec3_t mins, const vec3_t maxs )
  256. //-----------------------------------------------------------------------------
  257. {
  258. //make sure our start point isn't on the other side of a wall
  259. trace_t tr;
  260. vec3_t entMins, newstart;
  261. vec3_t entMaxs;
  262. VectorSet( entMaxs, 5, 5, 5 );
  263. VectorScale( entMaxs, -1, entMins );
  264. if ( !ent->client )
  265. {
  266. return;
  267. }
  268. VectorCopy( ent->currentOrigin, newstart );
  269. newstart[2] = start[2]; // force newstart to be on the same plane as the muzzle ( start )
  270. gi.trace( &tr, newstart, entMins, entMaxs, start, ent->s.number, MASK_SOLID|CONTENTS_SHOTCLIP );
  271. if ( tr.startsolid || tr.allsolid )
  272. {
  273. // there is a problem here..
  274. return;
  275. }
  276. if ( tr.fraction < 1.0f )
  277. {
  278. VectorCopy( tr.endpos, start );
  279. }
  280. }
  281. extern Vehicle_t *G_IsRidingVehicle( gentity_t *ent );
  282. //-----------------------------------------------------------------------------
  283. gentity_t *CreateMissile( vec3_t org, vec3_t dir, float vel, int life, gentity_t *owner, qboolean altFire = qfalse )
  284. //-----------------------------------------------------------------------------
  285. {
  286. gentity_t *missile;
  287. missile = G_Spawn();
  288. missile->nextthink = level.time + life;
  289. missile->e_ThinkFunc = thinkF_G_FreeEntity;
  290. missile->s.eType = ET_MISSILE;
  291. missile->owner = owner;
  292. Vehicle_t* pVeh = G_IsRidingVehicle(owner);
  293. missile->alt_fire = altFire;
  294. missile->s.pos.trType = TR_LINEAR;
  295. missile->s.pos.trTime = level.time;// - 10; // move a bit on the very first frame
  296. VectorCopy( org, missile->s.pos.trBase );
  297. VectorScale( dir, vel, missile->s.pos.trDelta );
  298. if (pVeh)
  299. {
  300. missile->s.eFlags |= EF_USE_ANGLEDELTA;
  301. vectoangles(missile->s.pos.trDelta, missile->s.angles);
  302. VectorMA(missile->s.pos.trDelta, 2.0f, pVeh->m_pParentEntity->client->ps.velocity, missile->s.pos.trDelta);
  303. }
  304. VectorCopy( org, missile->currentOrigin);
  305. gi.linkentity( missile );
  306. return missile;
  307. }
  308. //-----------------------------------------------------------------------------
  309. static void WP_Stick( gentity_t *missile, trace_t *trace, float fudge_distance = 0.0f )
  310. //-----------------------------------------------------------------------------
  311. {
  312. vec3_t org, ang;
  313. // not moving or rotating
  314. missile->s.pos.trType = TR_STATIONARY;
  315. VectorClear( missile->s.pos.trDelta );
  316. VectorClear( missile->s.apos.trDelta );
  317. // so we don't stick into the wall
  318. VectorMA( trace->endpos, fudge_distance, trace->plane.normal, org );
  319. G_SetOrigin( missile, org );
  320. vectoangles( trace->plane.normal, ang );
  321. G_SetAngles( missile, ang );
  322. // I guess explode death wants me as the normal?
  323. // VectorCopy( trace->plane.normal, missile->pos1 );
  324. gi.linkentity( missile );
  325. }
  326. // This version shares is in the thinkFunc format
  327. //-----------------------------------------------------------------------------
  328. void WP_Explode( gentity_t *self )
  329. //-----------------------------------------------------------------------------
  330. {
  331. gentity_t *attacker = self;
  332. vec3_t forward={0,0,1};
  333. // stop chain reaction runaway loops
  334. self->takedamage = qfalse;
  335. self->s.loopSound = 0;
  336. // VectorCopy( self->currentOrigin, self->s.pos.trBase );
  337. if ( !self->client )
  338. {
  339. AngleVectors( self->s.angles, forward, NULL, NULL );
  340. }
  341. if ( self->fxID > 0 )
  342. {
  343. G_PlayEffect( self->fxID, self->currentOrigin, forward );
  344. }
  345. if ( self->owner )
  346. {
  347. attacker = self->owner;
  348. }
  349. else if ( self->activator )
  350. {
  351. attacker = self->activator;
  352. }
  353. if ( self->splashDamage > 0 && self->splashRadius > 0 )
  354. {
  355. G_RadiusDamage( self->currentOrigin, attacker, self->splashDamage, self->splashRadius, 0/*don't ignore attacker*/, MOD_EXPLOSIVE_SPLASH );
  356. }
  357. if ( self->target )
  358. {
  359. G_UseTargets( self, attacker );
  360. }
  361. G_SetOrigin( self, self->currentOrigin );
  362. self->nextthink = level.time + 50;
  363. self->e_ThinkFunc = thinkF_G_FreeEntity;
  364. }
  365. // We need to have a dieFunc, otherwise G_Damage won't actually make us die. I could modify G_Damage, but that entails too many changes
  366. //-----------------------------------------------------------------------------
  367. void WP_ExplosiveDie( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int meansOfDeath,int dFlags,int hitLoc )
  368. //-----------------------------------------------------------------------------
  369. {
  370. self->enemy = attacker;
  371. if ( attacker && !attacker->s.number )
  372. {
  373. // less damage when shot by player
  374. self->splashDamage /= 3;
  375. self->splashRadius /= 3;
  376. }
  377. self->s.eFlags &= ~EF_FIRING; // don't draw beam if we are dead
  378. WP_Explode( self );
  379. }
  380. /*
  381. ----------------------------------------------
  382. PLAYER ITEMS
  383. ----------------------------------------------
  384. */
  385. /*
  386. #define SEEKER_RADIUS 500
  387. gentity_t *SeekerAcquiresTarget ( gentity_t *ent, vec3_t pos )
  388. {
  389. vec3_t seekerPos;
  390. float angle;
  391. gentity_t *entityList[MAX_GENTITIES]; // targets within inital radius
  392. gentity_t *visibleTargets[MAX_GENTITIES]; // final filtered target list
  393. int numListedEntities;
  394. int i, e;
  395. gentity_t *target;
  396. vec3_t mins, maxs;
  397. angle = cg.time * 0.004f;
  398. // must match cg_effects ( CG_Seeker ) & g_weapon ( SeekerAcquiresTarget ) & cg_weapons ( CG_FireSeeker )
  399. seekerPos[0] = ent->currentOrigin[0] + 18 * cos(angle);
  400. seekerPos[1] = ent->currentOrigin[1] + 18 * sin(angle);
  401. seekerPos[2] = ent->currentOrigin[2] + ent->client->ps.viewheight + 8 + (3*cos(level.time*0.001f));
  402. for ( i = 0 ; i < 3 ; i++ )
  403. {
  404. mins[i] = seekerPos[i] - SEEKER_RADIUS;
  405. maxs[i] = seekerPos[i] + SEEKER_RADIUS;
  406. }
  407. // get potential targets within radius
  408. numListedEntities = gi.EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES );
  409. i = 0; // reset counter
  410. for ( e = 0 ; e < numListedEntities ; e++ )
  411. {
  412. target = entityList[e];
  413. // seeker owner not a valid target
  414. if ( target == ent )
  415. {
  416. continue;
  417. }
  418. // only players are valid targets
  419. if ( !target->client )
  420. {
  421. continue;
  422. }
  423. // teammates not valid targets
  424. if ( OnSameTeam( ent, target ) )
  425. {
  426. continue;
  427. }
  428. // don't shoot at dead things
  429. if ( target->health <= 0 )
  430. {
  431. continue;
  432. }
  433. if( CanDamage( target, seekerPos ) ) // visible target, so add it to the list
  434. {
  435. visibleTargets[i++] = entityList[e];
  436. }
  437. }
  438. if ( i )
  439. {
  440. // ok, now we know there are i visible targets. Pick one as the seeker's target
  441. target = visibleTargets[Q_irand(0,i-1)];
  442. VectorCopy( seekerPos, pos );
  443. return target;
  444. }
  445. return NULL;
  446. }
  447. static void WP_FireBlasterMissile( gentity_t *ent, vec3_t start, vec3_t dir, qboolean altFire );
  448. void FireSeeker( gentity_t *owner, gentity_t *target, vec3_t origin, vec3_t dir )
  449. {
  450. VectorSubtract( target->currentOrigin, origin, dir );
  451. VectorNormalize( dir );
  452. // for now I'm just using the scavenger bullet.
  453. WP_FireBlasterMissile( owner, origin, dir, qfalse );
  454. }
  455. */
  456. #ifdef _XBOX // Auto-aim
  457. static float VectorDistanceSquared(vec3_t p1, vec3_t p2)
  458. {
  459. vec3_t dir;
  460. VectorSubtract(p2, p1, dir);
  461. return VectorLengthSquared(dir);
  462. }
  463. int WP_FindClosestBodyPart(gentity_t *ent, gentity_t *other, vec3_t point, vec3_t out, int c = 0)
  464. {
  465. int shortestLen = 509999;
  466. char where = -1;
  467. int len;
  468. renderInfo_t *ri = NULL;
  469. ri = &ent->client->renderInfo;
  470. if (ent->client)
  471. {
  472. if (c > 0)
  473. {
  474. where = c - 1; // Fail safe, set to torso
  475. }
  476. else
  477. {
  478. len = VectorDistanceSquared(point, ri->eyePoint);
  479. if (len < shortestLen) {
  480. shortestLen = len; where = 0;
  481. }
  482. len = VectorDistanceSquared(point, ri->headPoint);
  483. if (len < shortestLen) {
  484. shortestLen = len; where = 1;
  485. }
  486. len = VectorDistanceSquared(point, ri->handRPoint);
  487. if (len < shortestLen) {
  488. shortestLen = len; where = 2;
  489. }
  490. len = VectorDistanceSquared(point, ri->handLPoint);
  491. if (len < shortestLen) {
  492. shortestLen = len; where = 3;
  493. }
  494. len = VectorDistanceSquared(point, ri->crotchPoint);
  495. if (len < shortestLen) {
  496. shortestLen = len; where = 4;
  497. }
  498. len = VectorDistanceSquared(point, ri->footRPoint);
  499. if (len < shortestLen) {
  500. shortestLen = len; where = 5;
  501. }
  502. len = VectorDistanceSquared(point, ri->footLPoint);
  503. if (len < shortestLen) {
  504. shortestLen = len; where = 6;
  505. }
  506. len = VectorDistanceSquared(point, ri->torsoPoint);
  507. if (len < shortestLen) {
  508. shortestLen = len; where = 7;
  509. }
  510. }
  511. if (where < 2 && c == 0)
  512. {
  513. if (random() < .75f) // 25% chance to actualy hit the head or eye
  514. where = 7;
  515. }
  516. switch (where)
  517. {
  518. case 0:
  519. VectorCopy(ri->eyePoint, out);
  520. break;
  521. case 1:
  522. VectorCopy(ri->headPoint, out);
  523. break;
  524. case 2:
  525. VectorCopy(ri->handRPoint, out);
  526. break;
  527. case 3:
  528. VectorCopy(ri->handLPoint, out);
  529. break;
  530. case 4:
  531. VectorCopy(ri->crotchPoint, out);
  532. break;
  533. case 5:
  534. VectorCopy(ri->footRPoint, out);
  535. break;
  536. case 6:
  537. VectorCopy(ri->footLPoint, out);
  538. break;
  539. case 7:
  540. VectorCopy(ri->torsoPoint, out);
  541. break;
  542. }
  543. }
  544. else
  545. {
  546. VectorCopy(ent->s.pos.trBase, out);
  547. // Really bad hack
  548. if (ent->classname && (strcmp(ent->classname, "misc_turret") == 0))
  549. {
  550. out[2] = point[2];
  551. }
  552. }
  553. if (ent && ent->client && ent->client->NPC_class == CLASS_MINEMONSTER)
  554. {
  555. out[2] -= 24; // not a clue???
  556. return shortestLen; // mine critters are too small to randomize
  557. }
  558. if (ent->NPC_type && !Q_stricmp(ent->NPC_type, "atst"))
  559. {
  560. // Dont randomize those atst's they have some pretty small legs
  561. return shortestLen;
  562. }
  563. if (c == 0)
  564. {
  565. // Add a bit of chance to the actual location
  566. float r = random() * 8.0f - 4.0f;
  567. float r2 = random() * 8.0f - 4.0f;
  568. float r3 = random() * 10.0f - 5.0f;
  569. out[0] += r;
  570. out[1] += r2;
  571. out[2] += r3;
  572. }
  573. return shortestLen;
  574. }
  575. #endif // Auto-aim
  576. //extern cvar_t *cv_autoAim;
  577. #ifdef _XBOX // Auto-aim
  578. static bool cv_autoAim = qtrue;
  579. #endif // Auto-aim
  580. bool WP_MissileTargetHint(gentity_t* shooter, vec3_t start, vec3_t out)
  581. {
  582. #ifdef _XBOX
  583. extern short cg_crossHairStatus;
  584. extern int g_crosshairEntNum;
  585. // int allow = 0;
  586. // allow = Cvar_VariableIntegerValue("cv_autoAim");
  587. // if ((!cg.snap) || !allow ) return false;
  588. if ((!cg.snap) || !cv_autoAim ) return false;
  589. if (shooter->s.clientNum != 0) return false; // assuming shooter must be client, using 0 for cg_entities[0] a few lines down if you change this
  590. // if (cg_crossHairStatus != 1 || cg_crosshairEntNum < 0 || cg_crosshairEntNum >= ENTITYNUM_WORLD) return false;
  591. if (cg_crossHairStatus != 1 || g_crosshairEntNum < 0 || g_crosshairEntNum >= ENTITYNUM_WORLD) return false;
  592. gentity_t* traceEnt = &g_entities[g_crosshairEntNum];
  593. vec3_t d_f, d_rt, d_up;
  594. vec3_t end;
  595. trace_t trace;
  596. // Calculate the end point
  597. AngleVectors( cg_entities[0].lerpAngles, d_f, d_rt, d_up );
  598. VectorMA( start, 8192, d_f, end );//4028 is max for mind trick
  599. // This will get a detailed trace
  600. gi.trace( &trace, start, vec3_origin, vec3_origin, end, cg.snap->ps.clientNum, MASK_OPAQUE|CONTENTS_SHOTCLIP|CONTENTS_BODY|CONTENTS_ITEM, G2_COLLIDE, 10);
  601. // If the trace came up with a different entity then our crosshair, then you are not actualy over the enemy
  602. if (trace.entityNum != g_crosshairEntNum)
  603. {
  604. // Must trace again to find out where the crosshair will end up
  605. gi.trace( &trace, start, vec3_origin, vec3_origin, end, cg.snap->ps.clientNum, MASK_OPAQUE|CONTENTS_SHOTCLIP|CONTENTS_BODY|CONTENTS_ITEM, G2_NOCOLLIDE, 10 );
  606. // Find the closest body part to the trace
  607. WP_FindClosestBodyPart(traceEnt, shooter, trace.endpos, out);
  608. // Compute the direction vector between the shooter and the guy being shot
  609. VectorSubtract(out, start, out);
  610. VectorNormalize(out);
  611. for (int i = 1; i < 8; i++) /// do this 7 times to make sure we get it
  612. {
  613. /// Where will this direction end up?
  614. VectorMA( start, 8192, out, end );//4028 is max for mind trick
  615. // Try it one more time, ??? are we trying to shoot through solid space??
  616. gi.trace( &trace, start, vec3_origin, vec3_origin, end, cg.snap->ps.clientNum, MASK_OPAQUE|CONTENTS_SHOTCLIP|CONTENTS_BODY|CONTENTS_ITEM, G2_COLLIDE, 10);
  617. if (trace.entityNum != g_crosshairEntNum)
  618. {
  619. // Find the closest body part to the trace
  620. WP_FindClosestBodyPart(traceEnt, shooter, trace.endpos, out, i);
  621. // Computer the direction vector between the shooter and the guy being shot
  622. VectorSubtract(out, start, out);
  623. VectorNormalize(out);
  624. }
  625. else
  626. {
  627. break; /// a hit wahoo
  628. }
  629. }
  630. }
  631. return true;
  632. #else // Auto-aim
  633. return false;
  634. #endif
  635. }
  636. /*
  637. ----------------------------------------------
  638. PLAYER WEAPONS
  639. ----------------------------------------------
  640. */
  641. //---------------
  642. // Bryar Pistol
  643. //---------------
  644. //---------------------------------------------------------
  645. static void WP_FireBryarPistol( gentity_t *ent, qboolean alt_fire )
  646. //---------------------------------------------------------
  647. {
  648. vec3_t start;
  649. int damage = BRYAR_PISTOL_DAMAGE;
  650. VectorCopy( muzzle, start );
  651. WP_TraceSetStart( ent, start, vec3_origin, vec3_origin );//make sure our start point isn't on the other side of a wall
  652. if ( !(ent->client->ps.forcePowersActive&(1<<FP_SEE))
  653. || ent->client->ps.forcePowerLevel[FP_SEE] < FORCE_LEVEL_2 )
  654. {//force sight 2+ gives perfect aim
  655. //FIXME: maybe force sight level 3 autoaims some?
  656. if ( ent->NPC && ent->NPC->currentAim < 5 )
  657. {
  658. vec3_t angs;
  659. vectoangles( forward, angs );
  660. if ( ent->client->NPC_class == CLASS_IMPWORKER )
  661. {//*sigh*, hack to make impworkers less accurate without affecteing imperial officer accuracy
  662. angs[PITCH] += ( crandom() * (BLASTER_NPC_SPREAD+(6-ent->NPC->currentAim)*0.25f));//was 0.5f
  663. angs[YAW] += ( crandom() * (BLASTER_NPC_SPREAD+(6-ent->NPC->currentAim)*0.25f));//was 0.5f
  664. }
  665. else
  666. {
  667. angs[PITCH] += ( crandom() * ((5-ent->NPC->currentAim)*0.25f) );
  668. angs[YAW] += ( crandom() * ((5-ent->NPC->currentAim)*0.25f) );
  669. }
  670. AngleVectors( angs, forward, NULL, NULL );
  671. }
  672. }
  673. WP_MissileTargetHint(ent, start, forward);
  674. gentity_t *missile = CreateMissile( start, forward, BRYAR_PISTOL_VEL, 10000, ent, alt_fire );
  675. missile->classname = "bryar_proj";
  676. if ( ent->s.weapon == WP_BLASTER_PISTOL
  677. || ent->s.weapon == WP_JAWA )
  678. {//*SIGH*... I hate our weapon system...
  679. missile->s.weapon = ent->s.weapon;
  680. }
  681. else
  682. {
  683. missile->s.weapon = WP_BRYAR_PISTOL;
  684. }
  685. if ( alt_fire )
  686. {
  687. int count = ( level.time - ent->client->ps.weaponChargeTime ) / BRYAR_CHARGE_UNIT;
  688. if ( count < 1 )
  689. {
  690. count = 1;
  691. }
  692. else if ( count > 5 )
  693. {
  694. count = 5;
  695. }
  696. damage *= count;
  697. missile->count = count; // this will get used in the projectile rendering code to make a beefier effect
  698. }
  699. // if ( ent->client && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > 0 && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > cg.time )
  700. // {
  701. // // in overcharge mode, so doing double damage
  702. // missile->flags |= FL_OVERCHARGED;
  703. // damage *= 2;
  704. // }
  705. missile->damage = damage;
  706. missile->dflags = DAMAGE_DEATH_KNOCKBACK;
  707. if ( alt_fire )
  708. {
  709. missile->methodOfDeath = MOD_BRYAR_ALT;
  710. }
  711. else
  712. {
  713. missile->methodOfDeath = MOD_BRYAR;
  714. }
  715. missile->clipmask = MASK_SHOT | CONTENTS_LIGHTSABER;
  716. // we don't want it to bounce forever
  717. missile->bounceCount = 8;
  718. if ( ent->weaponModel[1] > 0 )
  719. {//dual pistols, toggle the muzzle point back and forth between the two pistols each time he fires
  720. ent->count = (ent->count)?0:1;
  721. }
  722. }
  723. //---------------
  724. // Blaster
  725. //---------------
  726. //---------------------------------------------------------
  727. static void WP_FireBlasterMissile( gentity_t *ent, vec3_t start, vec3_t dir, qboolean altFire )
  728. //---------------------------------------------------------
  729. {
  730. int velocity = BLASTER_VELOCITY;
  731. int damage = BLASTER_DAMAGE;
  732. if ( ent && ent->client && ent->client->NPC_class == CLASS_VEHICLE )
  733. {
  734. damage *= 3;
  735. velocity = ATST_MAIN_VEL + ent->client->ps.speed;
  736. }
  737. else
  738. {
  739. // If an enemy is shooting at us, lower the velocity so you have a chance to evade
  740. if ( ent->client && ent->client->ps.clientNum != 0 && ent->client->NPC_class != CLASS_BOBAFETT )
  741. {
  742. if ( g_spskill->integer < 2 )
  743. {
  744. velocity *= BLASTER_NPC_VEL_CUT;
  745. }
  746. else
  747. {
  748. velocity *= BLASTER_NPC_HARD_VEL_CUT;
  749. }
  750. }
  751. }
  752. WP_TraceSetStart( ent, start, vec3_origin, vec3_origin );//make sure our start point isn't on the other side of a wall
  753. WP_MissileTargetHint(ent, start, dir);
  754. gentity_t *missile = CreateMissile( start, dir, velocity, 10000, ent, altFire );
  755. missile->classname = "blaster_proj";
  756. missile->s.weapon = WP_BLASTER;
  757. // Do the damages
  758. if ( ent->s.number != 0 && ent->client->NPC_class != CLASS_BOBAFETT )
  759. {
  760. if ( g_spskill->integer == 0 )
  761. {
  762. damage = BLASTER_NPC_DAMAGE_EASY;
  763. }
  764. else if ( g_spskill->integer == 1 )
  765. {
  766. damage = BLASTER_NPC_DAMAGE_NORMAL;
  767. }
  768. else
  769. {
  770. damage = BLASTER_NPC_DAMAGE_HARD;
  771. }
  772. }
  773. // if ( ent->client )
  774. // {
  775. // if ( ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > 0 && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > cg.time )
  776. // {
  777. // // in overcharge mode, so doing double damage
  778. // missile->flags |= FL_OVERCHARGED;
  779. // damage *= 2;
  780. // }
  781. // }
  782. missile->damage = damage;
  783. missile->dflags = DAMAGE_DEATH_KNOCKBACK;
  784. if ( altFire )
  785. {
  786. missile->methodOfDeath = MOD_BLASTER_ALT;
  787. }
  788. else
  789. {
  790. missile->methodOfDeath = MOD_BLASTER;
  791. }
  792. missile->clipmask = MASK_SHOT | CONTENTS_LIGHTSABER;
  793. // we don't want it to bounce forever
  794. missile->bounceCount = 8;
  795. }
  796. //---------------------------------------------------------
  797. void WP_FireTurboLaserMissile( gentity_t *ent, vec3_t start, vec3_t dir )
  798. //---------------------------------------------------------
  799. {
  800. int velocity = ent->mass; //FIXME: externalize
  801. gentity_t *missile;
  802. missile = CreateMissile( start, dir, velocity, 10000, ent, qfalse );
  803. //use a custom shot effect
  804. //missile->s.otherEntityNum2 = G_EffectIndex( "turret/turb_shot" );
  805. //use a custom impact effect
  806. //missile->s.emplacedOwner = G_EffectIndex( "turret/turb_impact" );
  807. missile->classname = "turbo_proj";
  808. missile->s.weapon = WP_TIE_FIGHTER;
  809. missile->damage = ent->damage; //FIXME: externalize
  810. missile->splashDamage = ent->splashDamage; //FIXME: externalize
  811. missile->splashRadius = ent->splashRadius; //FIXME: externalize
  812. missile->dflags = DAMAGE_DEATH_KNOCKBACK;
  813. missile->methodOfDeath = MOD_EMPLACED; //MOD_TURBLAST; //count as a heavy weap
  814. missile->splashMethodOfDeath = MOD_EMPLACED; //MOD_TURBLAST;// ?SPLASH;
  815. missile->clipmask = MASK_SHOT;
  816. // we don't want it to bounce forever
  817. missile->bounceCount = 8;
  818. //set veh as cgame side owner for purpose of fx overrides
  819. //missile->s.owner = ent->s.number;
  820. //don't let them last forever
  821. missile->e_ThinkFunc = thinkF_G_FreeEntity;
  822. missile->nextthink = level.time + 10000;//at 20000 speed, that should be more than enough
  823. }
  824. //---------------------------------------------------------
  825. static void WP_FireBlaster( gentity_t *ent, qboolean alt_fire )
  826. //---------------------------------------------------------
  827. {
  828. vec3_t dir, angs;
  829. vectoangles( forward, angs );
  830. if ( ent->client && ent->client->NPC_class == CLASS_VEHICLE )
  831. {//no inherent aim screw up
  832. }
  833. else if ( !(ent->client->ps.forcePowersActive&(1<<FP_SEE))
  834. || ent->client->ps.forcePowerLevel[FP_SEE] < FORCE_LEVEL_2 )
  835. {//force sight 2+ gives perfect aim
  836. //FIXME: maybe force sight level 3 autoaims some?
  837. if ( alt_fire )
  838. {
  839. // add some slop to the alt-fire direction
  840. angs[PITCH] += crandom() * BLASTER_ALT_SPREAD;
  841. angs[YAW] += crandom() * BLASTER_ALT_SPREAD;
  842. }
  843. else
  844. {
  845. // Troopers use their aim values as well as the gun's inherent inaccuracy
  846. // so check for all classes of stormtroopers and anyone else that has aim error
  847. if ( ent->client && ent->NPC &&
  848. ( ent->client->NPC_class == CLASS_STORMTROOPER ||
  849. ent->client->NPC_class == CLASS_SWAMPTROOPER ) )
  850. {
  851. angs[PITCH] += ( crandom() * (BLASTER_NPC_SPREAD+(6-ent->NPC->currentAim)*0.25f));//was 0.5f
  852. angs[YAW] += ( crandom() * (BLASTER_NPC_SPREAD+(6-ent->NPC->currentAim)*0.25f));//was 0.5f
  853. }
  854. else
  855. {
  856. // add some slop to the main-fire direction
  857. angs[PITCH] += crandom() * BLASTER_MAIN_SPREAD;
  858. angs[YAW] += crandom() * BLASTER_MAIN_SPREAD;
  859. }
  860. }
  861. }
  862. AngleVectors( angs, dir, NULL, NULL );
  863. // FIXME: if temp_org does not have clear trace to inside the bbox, don't shoot!
  864. WP_FireBlasterMissile( ent, muzzle, dir, alt_fire );
  865. }
  866. //---------------------
  867. // Tenloss Disruptor
  868. //---------------------
  869. int G_GetHitLocFromTrace( trace_t *trace, int mod )
  870. {
  871. int hitLoc = HL_NONE;
  872. for (int i=0; i < MAX_G2_COLLISIONS; i++)
  873. {
  874. if ( trace->G2CollisionMap[i].mEntityNum == -1 )
  875. {
  876. break;
  877. }
  878. CCollisionRecord &coll = trace->G2CollisionMap[i];
  879. if ( (coll.mFlags & G2_FRONTFACE) )
  880. {
  881. G_GetHitLocFromSurfName( &g_entities[coll.mEntityNum], gi.G2API_GetSurfaceName( &g_entities[coll.mEntityNum].ghoul2[coll.mModelIndex], coll.mSurfaceIndex ), &hitLoc, coll.mCollisionPosition, NULL, NULL, mod );
  882. //we only want the first "entrance wound", so break
  883. break;
  884. }
  885. }
  886. return hitLoc;
  887. }
  888. //---------------------------------------------------------
  889. static void WP_DisruptorMainFire( gentity_t *ent )
  890. //---------------------------------------------------------
  891. {
  892. int damage = DISRUPTOR_MAIN_DAMAGE;
  893. qboolean render_impact = qtrue;
  894. vec3_t start, end, spot;
  895. trace_t tr;
  896. gentity_t *traceEnt = NULL, *tent;
  897. float dist, shotDist, shotRange = 8192;
  898. if ( ent->NPC )
  899. {
  900. switch ( g_spskill->integer )
  901. {
  902. case 0:
  903. damage = DISRUPTOR_NPC_MAIN_DAMAGE_EASY;
  904. break;
  905. case 1:
  906. damage = DISRUPTOR_NPC_MAIN_DAMAGE_MEDIUM;
  907. break;
  908. case 2:
  909. default:
  910. damage = DISRUPTOR_NPC_MAIN_DAMAGE_HARD;
  911. break;
  912. }
  913. }
  914. VectorCopy( muzzle, start );
  915. WP_TraceSetStart( ent, start, vec3_origin, vec3_origin );
  916. // if ( ent->client && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > 0 && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > cg.time )
  917. // {
  918. // // in overcharge mode, so doing double damage
  919. // damage *= 2;
  920. // }
  921. WP_MissileTargetHint(ent, start, forward);
  922. VectorMA( start, shotRange, forward, end );
  923. int ignore = ent->s.number;
  924. int traces = 0;
  925. while ( traces < 10 )
  926. {//need to loop this in case we hit a Jedi who dodges the shot
  927. gi.trace( &tr, start, NULL, NULL, end, ignore, MASK_SHOT, G2_RETURNONHIT, 0 );
  928. traceEnt = &g_entities[tr.entityNum];
  929. if ( traceEnt
  930. && ( traceEnt->s.weapon == WP_SABER || (traceEnt->client && (traceEnt->client->NPC_class == CLASS_BOBAFETT||traceEnt->client->NPC_class == CLASS_REBORN) ) ) )
  931. {//FIXME: need a more reliable way to know we hit a jedi?
  932. if ( Jedi_DodgeEvasion( traceEnt, ent, &tr, HL_NONE ) )
  933. {//act like we didn't even hit him
  934. VectorCopy( tr.endpos, start );
  935. ignore = tr.entityNum;
  936. traces++;
  937. continue;
  938. }
  939. }
  940. //a Jedi is not dodging this shot
  941. break;
  942. }
  943. if ( tr.surfaceFlags & SURF_NOIMPACT )
  944. {
  945. render_impact = qfalse;
  946. }
  947. // always render a shot beam, doing this the old way because I don't much feel like overriding the effect.
  948. tent = G_TempEntity( tr.endpos, EV_DISRUPTOR_MAIN_SHOT );
  949. tent->svFlags |= SVF_BROADCAST;
  950. VectorCopy( muzzle, tent->s.origin2 );
  951. if ( render_impact )
  952. {
  953. if ( tr.entityNum < ENTITYNUM_WORLD && traceEnt->takedamage )
  954. {
  955. // Create a simple impact type mark that doesn't last long in the world
  956. G_PlayEffect( G_EffectIndex( "disruptor/flesh_impact" ), tr.endpos, tr.plane.normal );
  957. if ( traceEnt->client && LogAccuracyHit( traceEnt, ent ))
  958. {
  959. ent->client->ps.persistant[PERS_ACCURACY_HITS]++;
  960. }
  961. int hitLoc = G_GetHitLocFromTrace( &tr, MOD_DISRUPTOR );
  962. if ( traceEnt && traceEnt->client && traceEnt->client->NPC_class == CLASS_GALAKMECH )
  963. {//hehe
  964. G_Damage( traceEnt, ent, ent, forward, tr.endpos, 3, DAMAGE_DEATH_KNOCKBACK, MOD_DISRUPTOR, hitLoc );
  965. }
  966. else
  967. {
  968. G_Damage( traceEnt, ent, ent, forward, tr.endpos, damage, DAMAGE_DEATH_KNOCKBACK, MOD_DISRUPTOR, hitLoc );
  969. }
  970. }
  971. else
  972. {
  973. G_PlayEffect( G_EffectIndex( "disruptor/wall_impact" ), tr.endpos, tr.plane.normal );
  974. }
  975. }
  976. shotDist = shotRange * tr.fraction;
  977. for ( dist = 0; dist < shotDist; dist += 64 )
  978. {
  979. //FIXME: on a really long shot, this could make a LOT of alerts in one frame...
  980. VectorMA( start, dist, forward, spot );
  981. AddSightEvent( ent, spot, 256, AEL_DISCOVERED, 50 );
  982. }
  983. VectorMA( start, shotDist-4, forward, spot );
  984. AddSightEvent( ent, spot, 256, AEL_DISCOVERED, 50 );
  985. }
  986. //---------------------------------------------------------
  987. void WP_DisruptorAltFire( gentity_t *ent )
  988. //---------------------------------------------------------
  989. {
  990. int damage = DISRUPTOR_ALT_DAMAGE, skip, traces = DISRUPTOR_ALT_TRACES;
  991. qboolean render_impact = qtrue;
  992. vec3_t start, end;
  993. vec3_t muzzle2, spot, dir;
  994. trace_t tr;
  995. gentity_t *traceEnt, *tent;
  996. float dist, shotDist, shotRange = 8192;
  997. qboolean hitDodged = qfalse, fullCharge = qfalse;
  998. VectorCopy( muzzle, muzzle2 ); // making a backup copy
  999. // The trace start will originate at the eye so we can ensure that it hits the crosshair.
  1000. if ( ent->NPC )
  1001. {
  1002. switch ( g_spskill->integer )
  1003. {
  1004. case 0:
  1005. damage = DISRUPTOR_NPC_ALT_DAMAGE_EASY;
  1006. break;
  1007. case 1:
  1008. damage = DISRUPTOR_NPC_ALT_DAMAGE_MEDIUM;
  1009. break;
  1010. case 2:
  1011. default:
  1012. damage = DISRUPTOR_NPC_ALT_DAMAGE_HARD;
  1013. break;
  1014. }
  1015. VectorCopy( muzzle, start );
  1016. fullCharge = qtrue;
  1017. }
  1018. else
  1019. {
  1020. VectorCopy( ent->client->renderInfo.eyePoint, start );
  1021. AngleVectors( ent->client->renderInfo.eyeAngles, forward, NULL, NULL );
  1022. // don't let NPC's do charging
  1023. int count = ( level.time - ent->client->ps.weaponChargeTime - 50 ) / DISRUPTOR_CHARGE_UNIT;
  1024. if ( count < 1 )
  1025. {
  1026. count = 1;
  1027. }
  1028. else if ( count >= 10 )
  1029. {
  1030. count = 10;
  1031. fullCharge = qtrue;
  1032. }
  1033. // more powerful charges go through more things
  1034. if ( count < 3 )
  1035. {
  1036. traces = 1;
  1037. }
  1038. else if ( count < 6 )
  1039. {
  1040. traces = 2;
  1041. }
  1042. //else do full traces
  1043. damage = damage * count + DISRUPTOR_MAIN_DAMAGE * 0.5f; // give a boost to low charge shots
  1044. }
  1045. skip = ent->s.number;
  1046. // if ( ent->client && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > 0 && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > cg.time )
  1047. // {
  1048. // // in overcharge mode, so doing double damage
  1049. // damage *= 2;
  1050. // }
  1051. for ( int i = 0; i < traces; i++ )
  1052. {
  1053. VectorMA( start, shotRange, forward, end );
  1054. //NOTE: if you want to be able to hit guys in emplaced guns, use "G2_COLLIDE, 10" instead of "G2_RETURNONHIT, 0"
  1055. //alternately, if you end up hitting an emplaced_gun that has a sitter, just redo this one trace with the "G2_COLLIDE, 10" to see if we it the sitter
  1056. gi.trace( &tr, start, NULL, NULL, end, skip, MASK_SHOT, G2_COLLIDE, 10 );//G2_RETURNONHIT, 0 );
  1057. if ( tr.surfaceFlags & SURF_NOIMPACT )
  1058. {
  1059. render_impact = qfalse;
  1060. }
  1061. if ( tr.entityNum == ent->s.number )
  1062. {
  1063. // should never happen, but basically we don't want to consider a hit to ourselves?
  1064. // Get ready for an attempt to trace through another person
  1065. VectorCopy( tr.endpos, muzzle2 );
  1066. VectorCopy( tr.endpos, start );
  1067. skip = tr.entityNum;
  1068. #ifdef _DEBUG
  1069. gi.Printf( "BAD! Disruptor gun shot somehow traced back and hit the owner!\n" );
  1070. #endif
  1071. continue;
  1072. }
  1073. // always render a shot beam, doing this the old way because I don't much feel like overriding the effect.
  1074. //NOTE: let's just draw one beam, at the end
  1075. //tent = G_TempEntity( tr.endpos, EV_DISRUPTOR_SNIPER_SHOT );
  1076. //tent->svFlags |= SVF_BROADCAST;
  1077. //tent->alt_fire = fullCharge; // mark us so we can alter the effect
  1078. //VectorCopy( muzzle2, tent->s.origin2 );
  1079. if ( tr.fraction >= 1.0f )
  1080. {
  1081. // draw the beam but don't do anything else
  1082. break;
  1083. }
  1084. traceEnt = &g_entities[tr.entityNum];
  1085. if ( traceEnt //&& traceEnt->NPC
  1086. && ( traceEnt->s.weapon == WP_SABER || (traceEnt->client && (traceEnt->client->NPC_class == CLASS_BOBAFETT||traceEnt->client->NPC_class == CLASS_REBORN) ) ) )
  1087. {//FIXME: need a more reliable way to know we hit a jedi?
  1088. hitDodged = Jedi_DodgeEvasion( traceEnt, ent, &tr, HL_NONE );
  1089. //acts like we didn't even hit him
  1090. }
  1091. if ( !hitDodged )
  1092. {
  1093. if ( render_impact )
  1094. {
  1095. if (( tr.entityNum < ENTITYNUM_WORLD && traceEnt->takedamage )
  1096. || !Q_stricmp( traceEnt->classname, "misc_model_breakable" )
  1097. || traceEnt->s.eType == ET_MOVER )
  1098. {
  1099. // Create a simple impact type mark that doesn't last long in the world
  1100. G_PlayEffect( G_EffectIndex( "disruptor/alt_hit" ), tr.endpos, tr.plane.normal );
  1101. if ( traceEnt->client && LogAccuracyHit( traceEnt, ent ))
  1102. {//NOTE: hitting multiple ents can still get you over 100% accuracy
  1103. ent->client->ps.persistant[PERS_ACCURACY_HITS]++;
  1104. }
  1105. int hitLoc = G_GetHitLocFromTrace( &tr, MOD_DISRUPTOR );
  1106. if ( traceEnt && traceEnt->client && traceEnt->client->NPC_class == CLASS_GALAKMECH )
  1107. {//hehe
  1108. G_Damage( traceEnt, ent, ent, forward, tr.endpos, 10, DAMAGE_NO_KNOCKBACK|DAMAGE_NO_HIT_LOC, fullCharge ? MOD_SNIPER : MOD_DISRUPTOR, hitLoc );
  1109. break;
  1110. }
  1111. G_Damage( traceEnt, ent, ent, forward, tr.endpos, damage, DAMAGE_NO_KNOCKBACK|DAMAGE_NO_HIT_LOC, fullCharge ? MOD_SNIPER : MOD_DISRUPTOR, hitLoc );
  1112. if ( traceEnt->s.eType == ET_MOVER )
  1113. {//stop the traces on any mover
  1114. break;
  1115. }
  1116. }
  1117. else
  1118. {
  1119. // we only make this mark on things that can't break or move
  1120. tent = G_TempEntity( tr.endpos, EV_DISRUPTOR_SNIPER_MISS );
  1121. tent->svFlags |= SVF_BROADCAST;
  1122. VectorCopy( tr.plane.normal, tent->pos1 );
  1123. break; // hit solid, but doesn't take damage, so stop the shot...we _could_ allow it to shoot through walls, might be cool?
  1124. }
  1125. }
  1126. else // not rendering impact, must be a skybox or other similar thing?
  1127. {
  1128. break; // don't try anymore traces
  1129. }
  1130. }
  1131. // Get ready for an attempt to trace through another person
  1132. VectorCopy( tr.endpos, muzzle2 );
  1133. VectorCopy( tr.endpos, start );
  1134. skip = tr.entityNum;
  1135. hitDodged = qfalse;
  1136. }
  1137. //just draw one solid beam all the way to the end...
  1138. tent = G_TempEntity( tr.endpos, EV_DISRUPTOR_SNIPER_SHOT );
  1139. tent->svFlags |= SVF_BROADCAST;
  1140. tent->alt_fire = fullCharge; // mark us so we can alter the effect
  1141. VectorCopy( muzzle, tent->s.origin2 );
  1142. // now go along the trail and make sight events
  1143. VectorSubtract( tr.endpos, muzzle, dir );
  1144. shotDist = VectorNormalize( dir );
  1145. //FIXME: if shoot *really* close to someone, the alert could be way out of their FOV
  1146. for ( dist = 0; dist < shotDist; dist += 64 )
  1147. {
  1148. //FIXME: on a really long shot, this could make a LOT of alerts in one frame...
  1149. VectorMA( muzzle, dist, dir, spot );
  1150. AddSightEvent( ent, spot, 256, AEL_DISCOVERED, 50 );
  1151. }
  1152. //FIXME: spawn a temp ent that continuously spawns sight alerts here? And 1 sound alert to draw their attention?
  1153. VectorMA( start, shotDist-4, forward, spot );
  1154. AddSightEvent( ent, spot, 256, AEL_DISCOVERED, 50 );
  1155. }
  1156. //---------------------------------------------------------
  1157. static void WP_FireDisruptor( gentity_t *ent, qboolean alt_fire )
  1158. //---------------------------------------------------------
  1159. {
  1160. if ( alt_fire )
  1161. {
  1162. WP_DisruptorAltFire( ent );
  1163. }
  1164. else
  1165. {
  1166. WP_DisruptorMainFire( ent );
  1167. }
  1168. G_PlayEffect( G_EffectIndex( "disruptor/line_cap" ), muzzle, forward );
  1169. }
  1170. //-------------------
  1171. // Wookiee Bowcaster
  1172. //-------------------
  1173. //---------------------------------------------------------
  1174. static void WP_BowcasterMainFire( gentity_t *ent )
  1175. //---------------------------------------------------------
  1176. {
  1177. int damage = BOWCASTER_DAMAGE, count;
  1178. float vel;
  1179. vec3_t angs, dir, start;
  1180. gentity_t *missile;
  1181. VectorCopy( muzzle, start );
  1182. WP_TraceSetStart( ent, start, vec3_origin, vec3_origin );//make sure our start point isn't on the other side of a wall
  1183. // Do the damages
  1184. if ( ent->s.number != 0 )
  1185. {
  1186. if ( g_spskill->integer == 0 )
  1187. {
  1188. damage = BOWCASTER_NPC_DAMAGE_EASY;
  1189. }
  1190. else if ( g_spskill->integer == 1 )
  1191. {
  1192. damage = BOWCASTER_NPC_DAMAGE_NORMAL;
  1193. }
  1194. else
  1195. {
  1196. damage = BOWCASTER_NPC_DAMAGE_HARD;
  1197. }
  1198. }
  1199. count = ( level.time - ent->client->ps.weaponChargeTime ) / BOWCASTER_CHARGE_UNIT;
  1200. if ( count < 1 )
  1201. {
  1202. count = 1;
  1203. }
  1204. else if ( count > 5 )
  1205. {
  1206. count = 5;
  1207. }
  1208. if ( !(count & 1 ))
  1209. {
  1210. // if we aren't odd, knock us down a level
  1211. count--;
  1212. }
  1213. // if ( ent->client && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > 0 && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > cg.time )
  1214. // {
  1215. // // in overcharge mode, so doing double damage
  1216. // damage *= 2;
  1217. // }
  1218. WP_MissileTargetHint(ent, start, forward);
  1219. for ( int i = 0; i < count; i++ )
  1220. {
  1221. // create a range of different velocities
  1222. vel = BOWCASTER_VELOCITY * ( crandom() * BOWCASTER_VEL_RANGE + 1.0f );
  1223. vectoangles( forward, angs );
  1224. if ( !(ent->client->ps.forcePowersActive&(1<<FP_SEE))
  1225. || ent->client->ps.forcePowerLevel[FP_SEE] < FORCE_LEVEL_2 )
  1226. {//force sight 2+ gives perfect aim
  1227. //FIXME: maybe force sight level 3 autoaims some?
  1228. // add some slop to the fire direction
  1229. angs[PITCH] += crandom() * BOWCASTER_ALT_SPREAD * 0.2f;
  1230. angs[YAW] += ((i+0.5f) * BOWCASTER_ALT_SPREAD - count * 0.5f * BOWCASTER_ALT_SPREAD );
  1231. if ( ent->NPC )
  1232. {
  1233. angs[PITCH] += ( crandom() * (BLASTER_NPC_SPREAD+(6-ent->NPC->currentAim)*0.25f) );
  1234. angs[YAW] += ( crandom() * (BLASTER_NPC_SPREAD+(6-ent->NPC->currentAim)*0.25f) );
  1235. }
  1236. }
  1237. AngleVectors( angs, dir, NULL, NULL );
  1238. missile = CreateMissile( start, dir, vel, 10000, ent );
  1239. missile->classname = "bowcaster_proj";
  1240. missile->s.weapon = WP_BOWCASTER;
  1241. VectorSet( missile->maxs, BOWCASTER_SIZE, BOWCASTER_SIZE, BOWCASTER_SIZE );
  1242. VectorScale( missile->maxs, -1, missile->mins );
  1243. // if ( ent->client && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > 0 && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > cg.time )
  1244. // {
  1245. // missile->flags |= FL_OVERCHARGED;
  1246. // }
  1247. missile->damage = damage;
  1248. missile->dflags = DAMAGE_DEATH_KNOCKBACK;
  1249. missile->methodOfDeath = MOD_BOWCASTER;
  1250. missile->clipmask = MASK_SHOT | CONTENTS_LIGHTSABER;
  1251. missile->splashDamage = BOWCASTER_SPLASH_DAMAGE;
  1252. missile->splashRadius = BOWCASTER_SPLASH_RADIUS;
  1253. // we don't want it to bounce
  1254. missile->bounceCount = 0;
  1255. ent->client->sess.missionStats.shotsFired++;
  1256. }
  1257. }
  1258. //---------------------------------------------------------
  1259. static void WP_BowcasterAltFire( gentity_t *ent )
  1260. //---------------------------------------------------------
  1261. {
  1262. vec3_t start;
  1263. int damage = BOWCASTER_DAMAGE;
  1264. VectorCopy( muzzle, start );
  1265. WP_TraceSetStart( ent, start, vec3_origin, vec3_origin );//make sure our start point isn't on the other side of a wall
  1266. WP_MissileTargetHint(ent, start, forward);
  1267. gentity_t *missile = CreateMissile( start, forward, BOWCASTER_VELOCITY, 10000, ent, qtrue );
  1268. missile->classname = "bowcaster_alt_proj";
  1269. missile->s.weapon = WP_BOWCASTER;
  1270. // Do the damages
  1271. if ( ent->s.number != 0 )
  1272. {
  1273. if ( g_spskill->integer == 0 )
  1274. {
  1275. damage = BOWCASTER_NPC_DAMAGE_EASY;
  1276. }
  1277. else if ( g_spskill->integer == 1 )
  1278. {
  1279. damage = BOWCASTER_NPC_DAMAGE_NORMAL;
  1280. }
  1281. else
  1282. {
  1283. damage = BOWCASTER_NPC_DAMAGE_HARD;
  1284. }
  1285. }
  1286. VectorSet( missile->maxs, BOWCASTER_SIZE, BOWCASTER_SIZE, BOWCASTER_SIZE );
  1287. VectorScale( missile->maxs, -1, missile->mins );
  1288. // if ( ent->client && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > 0 && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > cg.time )
  1289. // {
  1290. // // in overcharge mode, so doing double damage
  1291. // missile->flags |= FL_OVERCHARGED;
  1292. // damage *= 2;
  1293. // }
  1294. missile->s.eFlags |= EF_BOUNCE;
  1295. missile->bounceCount = 3;
  1296. missile->damage = damage;
  1297. missile->dflags = DAMAGE_DEATH_KNOCKBACK;
  1298. missile->methodOfDeath = MOD_BOWCASTER_ALT;
  1299. missile->clipmask = MASK_SHOT | CONTENTS_LIGHTSABER;
  1300. missile->splashDamage = BOWCASTER_SPLASH_DAMAGE;
  1301. missile->splashRadius = BOWCASTER_SPLASH_RADIUS;
  1302. }
  1303. //---------------------------------------------------------
  1304. static void WP_FireBowcaster( gentity_t *ent, qboolean alt_fire )
  1305. //---------------------------------------------------------
  1306. {
  1307. if ( alt_fire )
  1308. {
  1309. WP_BowcasterAltFire( ent );
  1310. }
  1311. else
  1312. {
  1313. WP_BowcasterMainFire( ent );
  1314. }
  1315. }
  1316. //-------------------
  1317. // Heavy Repeater
  1318. //-------------------
  1319. //---------------------------------------------------------
  1320. static void WP_RepeaterMainFire( gentity_t *ent, vec3_t dir )
  1321. //---------------------------------------------------------
  1322. {
  1323. vec3_t start;
  1324. int damage = REPEATER_DAMAGE;
  1325. VectorCopy( muzzle, start );
  1326. WP_TraceSetStart( ent, start, vec3_origin, vec3_origin );//make sure our start point isn't on the other side of a wall
  1327. WP_MissileTargetHint(ent, start, dir);
  1328. gentity_t *missile = CreateMissile( start, dir, REPEATER_VELOCITY, 10000, ent );
  1329. missile->classname = "repeater_proj";
  1330. missile->s.weapon = WP_REPEATER;
  1331. // Do the damages
  1332. if ( ent->s.number != 0 )
  1333. {
  1334. if ( g_spskill->integer == 0 )
  1335. {
  1336. damage = REPEATER_NPC_DAMAGE_EASY;
  1337. }
  1338. else if ( g_spskill->integer == 1 )
  1339. {
  1340. damage = REPEATER_NPC_DAMAGE_NORMAL;
  1341. }
  1342. else
  1343. {
  1344. damage = REPEATER_NPC_DAMAGE_HARD;
  1345. }
  1346. }
  1347. // if ( ent->client && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > 0 && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > cg.time )
  1348. // {
  1349. // // in overcharge mode, so doing double damage
  1350. // missile->flags |= FL_OVERCHARGED;
  1351. // damage *= 2;
  1352. // }
  1353. missile->damage = damage;
  1354. missile->dflags = DAMAGE_DEATH_KNOCKBACK;
  1355. missile->methodOfDeath = MOD_REPEATER;
  1356. missile->clipmask = MASK_SHOT | CONTENTS_LIGHTSABER;
  1357. // we don't want it to bounce forever
  1358. missile->bounceCount = 8;
  1359. }
  1360. //---------------------------------------------------------
  1361. static void WP_RepeaterAltFire( gentity_t *ent )
  1362. //---------------------------------------------------------
  1363. {
  1364. vec3_t start;
  1365. int damage = REPEATER_ALT_DAMAGE;
  1366. gentity_t *missile = NULL;
  1367. VectorCopy( muzzle, start );
  1368. WP_TraceSetStart( ent, start, vec3_origin, vec3_origin );//make sure our start point isn't on the other side of a wall
  1369. if ( ent->client && ent->client->NPC_class == CLASS_GALAKMECH )
  1370. {
  1371. missile = CreateMissile( start, ent->client->hiddenDir, ent->client->hiddenDist, 10000, ent, qtrue );
  1372. }
  1373. else
  1374. {
  1375. WP_MissileTargetHint(ent, start, forward);
  1376. missile = CreateMissile( start, forward, REPEATER_ALT_VELOCITY, 10000, ent, qtrue );
  1377. }
  1378. missile->classname = "repeater_alt_proj";
  1379. missile->s.weapon = WP_REPEATER;
  1380. missile->mass = 10;
  1381. // Do the damages
  1382. if ( ent->s.number != 0 )
  1383. {
  1384. if ( g_spskill->integer == 0 )
  1385. {
  1386. damage = REPEATER_ALT_NPC_DAMAGE_EASY;
  1387. }
  1388. else if ( g_spskill->integer == 1 )
  1389. {
  1390. damage = REPEATER_ALT_NPC_DAMAGE_NORMAL;
  1391. }
  1392. else
  1393. {
  1394. damage = REPEATER_ALT_NPC_DAMAGE_HARD;
  1395. }
  1396. }
  1397. VectorSet( missile->maxs, REPEATER_ALT_SIZE, REPEATER_ALT_SIZE, REPEATER_ALT_SIZE );
  1398. VectorScale( missile->maxs, -1, missile->mins );
  1399. missile->s.pos.trType = TR_GRAVITY;
  1400. missile->s.pos.trDelta[2] += 40.0f; //give a slight boost in the upward direction
  1401. // if ( ent->client && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > 0 && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > cg.time )
  1402. // {
  1403. // // in overcharge mode, so doing double damage
  1404. // missile->flags |= FL_OVERCHARGED;
  1405. // damage *= 2;
  1406. // }
  1407. missile->damage = damage;
  1408. missile->dflags = DAMAGE_DEATH_KNOCKBACK;
  1409. missile->methodOfDeath = MOD_REPEATER_ALT;
  1410. missile->splashMethodOfDeath = MOD_REPEATER_ALT;
  1411. missile->clipmask = MASK_SHOT | CONTENTS_LIGHTSABER;
  1412. missile->splashDamage = REPEATER_ALT_SPLASH_DAMAGE;
  1413. missile->splashRadius = REPEATER_ALT_SPLASH_RADIUS;
  1414. // we don't want it to bounce forever
  1415. missile->bounceCount = 8;
  1416. }
  1417. //---------------------------------------------------------
  1418. static void WP_FireRepeater( gentity_t *ent, qboolean alt_fire )
  1419. //---------------------------------------------------------
  1420. {
  1421. vec3_t dir, angs;
  1422. vectoangles( forward, angs );
  1423. if ( alt_fire )
  1424. {
  1425. WP_RepeaterAltFire( ent );
  1426. }
  1427. else
  1428. {
  1429. if ( !(ent->client->ps.forcePowersActive&(1<<FP_SEE))
  1430. || ent->client->ps.forcePowerLevel[FP_SEE] < FORCE_LEVEL_2 )
  1431. {//force sight 2+ gives perfect aim
  1432. //FIXME: maybe force sight level 3 autoaims some?
  1433. // Troopers use their aim values as well as the gun's inherent inaccuracy
  1434. // so check for all classes of stormtroopers and anyone else that has aim error
  1435. if ( ent->client && ent->NPC &&
  1436. ( ent->client->NPC_class == CLASS_STORMTROOPER ||
  1437. ent->client->NPC_class == CLASS_SWAMPTROOPER ||
  1438. ent->client->NPC_class == CLASS_SHADOWTROOPER ) )
  1439. {
  1440. angs[PITCH] += ( crandom() * (REPEATER_NPC_SPREAD+(6-ent->NPC->currentAim)*0.25f) );
  1441. angs[YAW] += ( crandom() * (REPEATER_NPC_SPREAD+(6-ent->NPC->currentAim)*0.25f) );
  1442. }
  1443. else
  1444. {
  1445. // add some slop to the alt-fire direction
  1446. angs[PITCH] += crandom() * REPEATER_SPREAD;
  1447. angs[YAW] += crandom() * REPEATER_SPREAD;
  1448. }
  1449. }
  1450. AngleVectors( angs, dir, NULL, NULL );
  1451. // FIXME: if temp_org does not have clear trace to inside the bbox, don't shoot!
  1452. WP_RepeaterMainFire( ent, dir );
  1453. }
  1454. }
  1455. //-------------------
  1456. // DEMP2
  1457. //-------------------
  1458. //---------------------------------------------------------
  1459. static void WP_DEMP2_MainFire( gentity_t *ent )
  1460. //---------------------------------------------------------
  1461. {
  1462. vec3_t start;
  1463. int damage = DEMP2_DAMAGE;
  1464. VectorCopy( muzzle, start );
  1465. WP_TraceSetStart( ent, start, vec3_origin, vec3_origin );//make sure our start point isn't on the other side of a wall
  1466. WP_MissileTargetHint(ent, start, forward);
  1467. gentity_t *missile = CreateMissile( start, forward, DEMP2_VELOCITY, 10000, ent );
  1468. missile->classname = "demp2_proj";
  1469. missile->s.weapon = WP_DEMP2;
  1470. // Do the damages
  1471. if ( ent->s.number != 0 )
  1472. {
  1473. if ( g_spskill->integer == 0 )
  1474. {
  1475. damage = DEMP2_NPC_DAMAGE_EASY;
  1476. }
  1477. else if ( g_spskill->integer == 1 )
  1478. {
  1479. damage = DEMP2_NPC_DAMAGE_NORMAL;
  1480. }
  1481. else
  1482. {
  1483. damage = DEMP2_NPC_DAMAGE_HARD;
  1484. }
  1485. }
  1486. VectorSet( missile->maxs, DEMP2_SIZE, DEMP2_SIZE, DEMP2_SIZE );
  1487. VectorScale( missile->maxs, -1, missile->mins );
  1488. // if ( ent->client && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > 0 && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > cg.time )
  1489. // {
  1490. // // in overcharge mode, so doing double damage
  1491. // missile->flags |= FL_OVERCHARGED;
  1492. // damage *= 2;
  1493. // }
  1494. missile->damage = damage;
  1495. missile->dflags = DAMAGE_DEATH_KNOCKBACK;
  1496. missile->methodOfDeath = MOD_DEMP2;
  1497. missile->clipmask = MASK_SHOT | CONTENTS_LIGHTSABER;
  1498. // we don't want it to ever bounce
  1499. missile->bounceCount = 0;
  1500. }
  1501. // NOTE: this is 100% for the demp2 alt-fire effect, so changes to the visual effect will affect game side demp2 code
  1502. //--------------------------------------------------
  1503. void DEMP2_AltRadiusDamage( gentity_t *ent )
  1504. {
  1505. float frac = ( level.time - ent->fx_time ) / 1300.0f; // synchronize with demp2 effect
  1506. float dist, radius;
  1507. gentity_t *gent;
  1508. gentity_t *entityList[MAX_GENTITIES];
  1509. int numListedEntities, i, e;
  1510. vec3_t mins, maxs;
  1511. vec3_t v, dir;
  1512. frac *= frac * frac; // yes, this is completely ridiculous...but it causes the shell to grow slowly then "explode" at the end
  1513. radius = frac * 200.0f; // 200 is max radius...the model is aprox. 100 units tall...the fx draw code mults. this by 2.
  1514. for ( i = 0 ; i < 3 ; i++ )
  1515. {
  1516. mins[i] = ent->currentOrigin[i] - radius;
  1517. maxs[i] = ent->currentOrigin[i] + radius;
  1518. }
  1519. numListedEntities = gi.EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES );
  1520. for ( e = 0 ; e < numListedEntities ; e++ )
  1521. {
  1522. gent = entityList[ e ];
  1523. if ( !gent->takedamage || !gent->contents )
  1524. {
  1525. continue;
  1526. }
  1527. // find the distance from the edge of the bounding box
  1528. for ( i = 0 ; i < 3 ; i++ )
  1529. {
  1530. if ( ent->currentOrigin[i] < gent->absmin[i] )
  1531. {
  1532. v[i] = gent->absmin[i] - ent->currentOrigin[i];
  1533. }
  1534. else if ( ent->currentOrigin[i] > gent->absmax[i] )
  1535. {
  1536. v[i] = ent->currentOrigin[i] - gent->absmax[i];
  1537. }
  1538. else
  1539. {
  1540. v[i] = 0;
  1541. }
  1542. }
  1543. // shape is an ellipsoid, so cut vertical distance in half`
  1544. v[2] *= 0.5f;
  1545. dist = VectorLength( v );
  1546. if ( dist >= radius )
  1547. {
  1548. // shockwave hasn't hit them yet
  1549. continue;
  1550. }
  1551. if ( dist < ent->radius )
  1552. {
  1553. // shockwave has already hit this thing...
  1554. continue;
  1555. }
  1556. VectorCopy( gent->currentOrigin, v );
  1557. VectorSubtract( v, ent->currentOrigin, dir);
  1558. // push the center of mass higher than the origin so players get knocked into the air more
  1559. dir[2] += 12;
  1560. G_Damage( gent, ent, ent->owner, dir, ent->currentOrigin, DEMP2_ALT_DAMAGE, DAMAGE_DEATH_KNOCKBACK, ent->splashMethodOfDeath );
  1561. if ( gent->takedamage && gent->client )
  1562. {
  1563. gent->s.powerups |= ( 1 << PW_SHOCKED );
  1564. gent->client->ps.powerups[PW_SHOCKED] = level.time + 2000;
  1565. Saboteur_Decloak( gent, Q_irand( 3000, 10000 ) );
  1566. }
  1567. }
  1568. // store the last fraction so that next time around we can test against those things that fall between that last point and where the current shockwave edge is
  1569. ent->radius = radius;
  1570. if ( frac < 1.0f )
  1571. {
  1572. // shock is still happening so continue letting it expand
  1573. ent->nextthink = level.time + 50;
  1574. }
  1575. }
  1576. //---------------------------------------------------------
  1577. void DEMP2_AltDetonate( gentity_t *ent )
  1578. //---------------------------------------------------------
  1579. {
  1580. G_SetOrigin( ent, ent->currentOrigin );
  1581. // start the effects, unfortunately, I wanted to do some custom things that I couldn't easily do with the fx system, so part of it uses an event and localEntities
  1582. G_PlayEffect( "demp2/altDetonate", ent->currentOrigin, ent->pos1 );
  1583. G_AddEvent( ent, EV_DEMP2_ALT_IMPACT, ent->count * 2 );
  1584. ent->fx_time = level.time;
  1585. ent->radius = 0;
  1586. ent->nextthink = level.time + 50;
  1587. ent->e_ThinkFunc = thinkF_DEMP2_AltRadiusDamage;
  1588. ent->s.eType = ET_GENERAL; // make us a missile no longer
  1589. }
  1590. //---------------------------------------------------------
  1591. static void WP_DEMP2_AltFire( gentity_t *ent )
  1592. //---------------------------------------------------------
  1593. {
  1594. int damage = DEMP2_ALT_DAMAGE;
  1595. int count;
  1596. vec3_t start;
  1597. trace_t tr;
  1598. VectorCopy( muzzle, start );
  1599. WP_TraceSetStart( ent, start, vec3_origin, vec3_origin );//make sure our start point isn't on the other side of a wall
  1600. count = ( level.time - ent->client->ps.weaponChargeTime ) / DEMP2_CHARGE_UNIT;
  1601. if ( count < 1 )
  1602. {
  1603. count = 1;
  1604. }
  1605. else if ( count > 3 )
  1606. {
  1607. count = 3;
  1608. }
  1609. damage *= ( 1 + ( count * ( count - 1 )));// yields damage of 12,36,84...gives a higher bonus for longer charge
  1610. // the shot can travel a whopping 4096 units in 1 second. Note that the shot will auto-detonate at 4096 units...we'll see if this looks cool or not
  1611. WP_MissileTargetHint(ent, start, forward);
  1612. gentity_t *missile = CreateMissile( start, forward, DEMP2_ALT_RANGE, 1000, ent, qtrue );
  1613. // letting it know what the charge size is.
  1614. missile->count = count;
  1615. // missile->speed = missile->nextthink;
  1616. VectorCopy( tr.plane.normal, missile->pos1 );
  1617. missile->classname = "demp2_alt_proj";
  1618. missile->s.weapon = WP_DEMP2;
  1619. missile->e_ThinkFunc = thinkF_DEMP2_AltDetonate;
  1620. missile->splashDamage = missile->damage = damage;
  1621. missile->splashMethodOfDeath = missile->methodOfDeath = MOD_DEMP2_ALT;
  1622. missile->splashRadius = DEMP2_ALT_SPLASHRADIUS;
  1623. missile->dflags = DAMAGE_DEATH_KNOCKBACK;
  1624. missile->clipmask = MASK_SHOT | CONTENTS_LIGHTSABER;
  1625. // we don't want it to ever bounce
  1626. missile->bounceCount = 0;
  1627. }
  1628. //---------------------------------------------------------
  1629. static void WP_FireDEMP2( gentity_t *ent, qboolean alt_fire )
  1630. //---------------------------------------------------------
  1631. {
  1632. if ( alt_fire )
  1633. {
  1634. WP_DEMP2_AltFire( ent );
  1635. }
  1636. else
  1637. {
  1638. WP_DEMP2_MainFire( ent );
  1639. }
  1640. }
  1641. //-----------------------
  1642. // Golan Arms Flechette
  1643. //-----------------------
  1644. //---------------------------------------------------------
  1645. static void WP_FlechetteMainFire( gentity_t *ent )
  1646. //---------------------------------------------------------
  1647. {
  1648. vec3_t fwd, angs, start;
  1649. gentity_t *missile;
  1650. float damage = FLECHETTE_DAMAGE, vel = FLECHETTE_VEL;
  1651. VectorCopy( muzzle, start );
  1652. WP_TraceSetStart( ent, start, vec3_origin, vec3_origin );//make sure our start point isn't on the other side of a wall
  1653. // If we aren't the player, we will cut the velocity and damage of the shots
  1654. if ( ent->s.number )
  1655. {
  1656. damage *= 0.75f;
  1657. vel *= 0.5f;
  1658. }
  1659. // if ( ent->client && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > 0 && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > cg.time )
  1660. // {
  1661. // // in overcharge mode, so doing double damage
  1662. // damage *= 2;
  1663. // }
  1664. for ( int i = 0; i < FLECHETTE_SHOTS; i++ )
  1665. {
  1666. vectoangles( forward, angs );
  1667. if ( i == 0 && ent->s.number == 0 )
  1668. {
  1669. // do nothing on the first shot for the player, this one will hit the crosshairs
  1670. }
  1671. else
  1672. {
  1673. angs[PITCH] += crandom() * FLECHETTE_SPREAD;
  1674. angs[YAW] += crandom() * FLECHETTE_SPREAD;
  1675. }
  1676. AngleVectors( angs, fwd, NULL, NULL );
  1677. WP_MissileTargetHint(ent, start, fwd);
  1678. missile = CreateMissile( start, fwd, vel, 10000, ent );
  1679. missile->classname = "flech_proj";
  1680. missile->s.weapon = WP_FLECHETTE;
  1681. VectorSet( missile->maxs, FLECHETTE_SIZE, FLECHETTE_SIZE, FLECHETTE_SIZE );
  1682. VectorScale( missile->maxs, -1, missile->mins );
  1683. missile->damage = damage;
  1684. // if ( ent->client && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > 0 && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > cg.time )
  1685. // {
  1686. // missile->flags |= FL_OVERCHARGED;
  1687. // }
  1688. missile->dflags = (DAMAGE_DEATH_KNOCKBACK|DAMAGE_EXTRA_KNOCKBACK);
  1689. missile->methodOfDeath = MOD_FLECHETTE;
  1690. missile->clipmask = MASK_SHOT | CONTENTS_LIGHTSABER;
  1691. // we don't want it to bounce forever
  1692. missile->bounceCount = Q_irand(1,2);
  1693. missile->s.eFlags |= EF_BOUNCE_SHRAPNEL;
  1694. ent->client->sess.missionStats.shotsFired++;
  1695. }
  1696. }
  1697. //---------------------------------------------------------
  1698. void prox_mine_think( gentity_t *ent )
  1699. //---------------------------------------------------------
  1700. {
  1701. int count;
  1702. qboolean blow = qfalse;
  1703. // if it isn't time to auto-explode, do a small proximity check
  1704. if ( ent->delay > level.time )
  1705. {
  1706. count = G_RadiusList( ent->currentOrigin, FLECHETTE_MINE_RADIUS_CHECK, ent, qtrue, ent_list );
  1707. for ( int i = 0; i < count; i++ )
  1708. {
  1709. if ( ent_list[i]->client && ent_list[i]->health > 0 && ent->activator && ent_list[i]->s.number != ent->activator->s.number )
  1710. {
  1711. blow = qtrue;
  1712. break;
  1713. }
  1714. }
  1715. }
  1716. else
  1717. {
  1718. // well, we must die now
  1719. blow = qtrue;
  1720. }
  1721. if ( blow )
  1722. {
  1723. // G_Sound( ent, G_SoundIndex( "sound/weapons/flechette/warning.wav" ));
  1724. ent->e_ThinkFunc = thinkF_WP_Explode;
  1725. ent->nextthink = level.time + 200;
  1726. }
  1727. else
  1728. {
  1729. // we probably don't need to do this thinking logic very often...maybe this is fast enough?
  1730. ent->nextthink = level.time + 500;
  1731. }
  1732. }
  1733. //---------------------------------------------------------
  1734. void prox_mine_stick( gentity_t *self, gentity_t *other, trace_t *trace )
  1735. //---------------------------------------------------------
  1736. {
  1737. // turn us into a generic entity so we aren't running missile code
  1738. self->s.eType = ET_GENERAL;
  1739. self->s.modelindex = G_ModelIndex("models/weapons2/golan_arms/prox_mine.md3");
  1740. self->e_TouchFunc = touchF_NULL;
  1741. self->contents = CONTENTS_SOLID;
  1742. self->takedamage = qtrue;
  1743. self->health = 5;
  1744. self->e_DieFunc = dieF_WP_ExplosiveDie;
  1745. VectorSet( self->maxs, 5, 5, 5 );
  1746. VectorScale( self->maxs, -1, self->mins );
  1747. self->activator = self->owner;
  1748. self->owner = NULL;
  1749. WP_Stick( self, trace );
  1750. self->e_ThinkFunc = thinkF_prox_mine_think;
  1751. self->nextthink = level.time + 450;
  1752. // sticks for twenty seconds, then auto blows.
  1753. self->delay = level.time + 20000;
  1754. gi.linkentity( self );
  1755. }
  1756. /* Old Flechette alt-fire code....
  1757. //---------------------------------------------------------
  1758. static void WP_FlechetteProxMine( gentity_t *ent )
  1759. //---------------------------------------------------------
  1760. {
  1761. gentity_t *missile = CreateMissile( muzzle, forward, FLECHETTE_MINE_VEL, 10000, ent, qtrue );
  1762. missile->fxID = G_EffectIndex( "flechette/explosion" );
  1763. missile->classname = "proxMine";
  1764. missile->s.weapon = WP_FLECHETTE;
  1765. missile->s.pos.trType = TR_GRAVITY;
  1766. missile->s.eFlags |= EF_MISSILE_STICK;
  1767. missile->e_TouchFunc = touchF_prox_mine_stick;
  1768. missile->damage = FLECHETTE_MINE_DAMAGE;
  1769. missile->methodOfDeath = MOD_EXPLOSIVE;
  1770. missile->splashDamage = FLECHETTE_MINE_SPLASH_DAMAGE;
  1771. missile->splashRadius = FLECHETTE_MINE_SPLASH_RADIUS;
  1772. missile->splashMethodOfDeath = MOD_EXPLOSIVE_SPLASH;
  1773. missile->clipmask = MASK_SHOT;
  1774. // we don't want it to bounce forever
  1775. missile->bounceCount = 0;
  1776. }
  1777. */
  1778. //----------------------------------------------
  1779. void WP_flechette_alt_blow( gentity_t *ent )
  1780. //----------------------------------------------
  1781. {
  1782. EvaluateTrajectory( &ent->s.pos, level.time, ent->currentOrigin ); // Not sure if this is even necessary, but correct origins are cool?
  1783. G_RadiusDamage( ent->currentOrigin, ent->owner, ent->splashDamage, ent->splashRadius, NULL, MOD_EXPLOSIVE_SPLASH );
  1784. G_PlayEffect( "flechette/alt_blow", ent->currentOrigin );
  1785. G_FreeEntity( ent );
  1786. }
  1787. //------------------------------------------------------------------------------
  1788. static void WP_CreateFlechetteBouncyThing( vec3_t start, vec3_t fwd, gentity_t *self )
  1789. //------------------------------------------------------------------------------
  1790. {
  1791. gentity_t *missile = CreateMissile( start, fwd, 950 + random() * 700, 1500 + random() * 2000, self, qtrue );
  1792. missile->e_ThinkFunc = thinkF_WP_flechette_alt_blow;
  1793. missile->s.weapon = WP_FLECHETTE;
  1794. missile->classname = "flech_alt";
  1795. missile->mass = 4;
  1796. // How 'bout we give this thing a size...
  1797. VectorSet( missile->mins, -3.0f, -3.0f, -3.0f );
  1798. VectorSet( missile->maxs, 3.0f, 3.0f, 3.0f );
  1799. missile->clipmask = MASK_SHOT;
  1800. missile->clipmask &= ~CONTENTS_CORPSE;
  1801. // normal ones bounce, alt ones explode on impact
  1802. missile->s.pos.trType = TR_GRAVITY;
  1803. missile->s.eFlags |= EF_BOUNCE_HALF;
  1804. missile->damage = FLECHETTE_ALT_DAMAGE;
  1805. missile->dflags = 0;
  1806. missile->splashDamage = FLECHETTE_ALT_SPLASH_DAM;
  1807. missile->splashRadius = FLECHETTE_ALT_SPLASH_RAD;
  1808. missile->svFlags = SVF_USE_CURRENT_ORIGIN;
  1809. missile->methodOfDeath = MOD_FLECHETTE_ALT;
  1810. missile->splashMethodOfDeath = MOD_FLECHETTE_ALT;
  1811. VectorCopy( start, missile->pos2 );
  1812. }
  1813. //---------------------------------------------------------
  1814. static void WP_FlechetteAltFire( gentity_t *self )
  1815. //---------------------------------------------------------
  1816. {
  1817. vec3_t dir, fwd, start, angs;
  1818. vectoangles( forward, angs );
  1819. VectorCopy( muzzle, start );
  1820. WP_TraceSetStart( self, start, vec3_origin, vec3_origin );//make sure our start point isn't on the other side of a wall
  1821. for ( int i = 0; i < 2; i++ )
  1822. {
  1823. VectorCopy( angs, dir );
  1824. dir[PITCH] -= random() * 4 + 8; // make it fly upwards
  1825. dir[YAW] += crandom() * 2;
  1826. AngleVectors( dir, fwd, NULL, NULL );
  1827. WP_CreateFlechetteBouncyThing( start, fwd, self );
  1828. self->client->sess.missionStats.shotsFired++;
  1829. }
  1830. }
  1831. //---------------------------------------------------------
  1832. static void WP_FireFlechette( gentity_t *ent, qboolean alt_fire )
  1833. //---------------------------------------------------------
  1834. {
  1835. if ( alt_fire )
  1836. {
  1837. WP_FlechetteAltFire( ent );
  1838. }
  1839. else
  1840. {
  1841. WP_FlechetteMainFire( ent );
  1842. }
  1843. }
  1844. //-----------------------
  1845. // Rocket Launcher
  1846. //-----------------------
  1847. //---------------------------------------------------------
  1848. void rocketThink( gentity_t *ent )
  1849. //---------------------------------------------------------
  1850. {
  1851. vec3_t newdir, targetdir,
  1852. up={0,0,1}, right;
  1853. vec3_t org;
  1854. float dot, dot2;
  1855. if ( ent->disconnectDebounceTime && ent->disconnectDebounceTime < level.time )
  1856. {//time's up, we're done, remove us
  1857. if ( ent->lockCount )
  1858. {//explode when die
  1859. WP_ExplosiveDie( ent, ent->owner, ent->owner, 0, MOD_UNKNOWN, 0, HL_NONE );
  1860. }
  1861. else
  1862. {//just remove when die
  1863. G_FreeEntity( ent );
  1864. }
  1865. return;
  1866. }
  1867. if ( ent->enemy && ent->enemy->inuse )
  1868. {
  1869. float vel = (ent->spawnflags&1)?ent->speed:ROCKET_VELOCITY;
  1870. float newDirMult = ent->angle?ent->angle*2.0f:1.0f;
  1871. float oldDirMult = ent->angle?(1.0f-ent->angle)*2.0f:1.0f;
  1872. if ( (ent->spawnflags&1) )
  1873. {//vehicle rocket
  1874. if ( ent->enemy->client && ent->enemy->client->NPC_class == CLASS_VEHICLE )
  1875. {//tracking another vehicle
  1876. if ( ent->enemy->client->ps.speed+ent->speed > vel )
  1877. {
  1878. vel = ent->enemy->client->ps.speed+ent->speed;
  1879. }
  1880. }
  1881. }
  1882. VectorCopy( ent->enemy->currentOrigin, org );
  1883. org[2] += (ent->enemy->mins[2] + ent->enemy->maxs[2]) * 0.5f;
  1884. if ( ent->enemy->client )
  1885. {
  1886. switch( ent->enemy->client->NPC_class )
  1887. {
  1888. case CLASS_ATST:
  1889. org[2] += 80;
  1890. break;
  1891. case CLASS_MARK1:
  1892. org[2] += 40;
  1893. break;
  1894. case CLASS_PROBE:
  1895. org[2] += 60;
  1896. break;
  1897. }
  1898. if ( !TIMER_Done( ent->enemy, "flee" ) )
  1899. {
  1900. TIMER_Set( ent->enemy, "rocketChasing", 500 );
  1901. }
  1902. }
  1903. VectorSubtract( org, ent->currentOrigin, targetdir );
  1904. VectorNormalize( targetdir );
  1905. // Now the rocket can't do a 180 in space, so we'll limit the turn to about 45 degrees.
  1906. dot = DotProduct( targetdir, ent->movedir );
  1907. // a dot of 1.0 means right-on-target.
  1908. if ( dot < 0.0f )
  1909. {
  1910. // Go in the direction opposite, start a 180.
  1911. CrossProduct( ent->movedir, up, right );
  1912. dot2 = DotProduct( targetdir, right );
  1913. if ( dot2 > 0 )
  1914. {
  1915. // Turn 45 degrees right.
  1916. VectorMA( ent->movedir, 0.3f*newDirMult, right, newdir );
  1917. }
  1918. else
  1919. {
  1920. // Turn 45 degrees left.
  1921. VectorMA(ent->movedir, -0.3f*newDirMult, right, newdir);
  1922. }
  1923. // Yeah we've adjusted horizontally, but let's split the difference vertically, so we kinda try to move towards it.
  1924. newdir[2] = ( (targetdir[2]*newDirMult) + (ent->movedir[2]*oldDirMult) ) * 0.5;
  1925. // slowing down coupled with fairly tight turns can lead us to orbit an enemy..looks bad so don't do it!
  1926. // vel *= 0.5f;
  1927. }
  1928. else if ( dot < 0.70f )
  1929. {
  1930. // Still a bit off, so we turn a bit softer
  1931. VectorMA( ent->movedir, 0.5f*newDirMult, targetdir, newdir );
  1932. }
  1933. else
  1934. {
  1935. // getting close, so turn a bit harder
  1936. VectorMA( ent->movedir, 0.9f*newDirMult, targetdir, newdir );
  1937. }
  1938. // add crazy drunkenness
  1939. for ( int i = 0; i < 3; i++ )
  1940. {
  1941. newdir[i] += crandom() * ent->random * 0.25f;
  1942. }
  1943. // decay the randomness
  1944. ent->random *= 0.9f;
  1945. if ( ent->enemy->client
  1946. && ent->enemy->client->ps.groundEntityNum != ENTITYNUM_NONE )
  1947. {//tracking a client who's on the ground, aim at the floor...?
  1948. // Try to crash into the ground if we get close enough to do splash damage
  1949. float dis = Distance( ent->currentOrigin, org );
  1950. if ( dis < 128 )
  1951. {
  1952. // the closer we get, the more we push the rocket down, heh heh.
  1953. newdir[2] -= (1.0f - (dis / 128.0f)) * 0.6f;
  1954. }
  1955. }
  1956. VectorNormalize( newdir );
  1957. VectorScale( newdir, vel * 0.5f, ent->s.pos.trDelta );
  1958. VectorCopy( newdir, ent->movedir );
  1959. SnapVector( ent->s.pos.trDelta ); // save net bandwidth
  1960. VectorCopy( ent->currentOrigin, ent->s.pos.trBase );
  1961. ent->s.pos.trTime = level.time;
  1962. }
  1963. ent->nextthink = level.time + ROCKET_ALT_THINK_TIME; // Nothing at all spectacular happened, continue.
  1964. return;
  1965. }
  1966. //---------------------------------------------------------
  1967. static void WP_FireRocket( gentity_t *ent, qboolean alt_fire )
  1968. //---------------------------------------------------------
  1969. {
  1970. vec3_t start;
  1971. int damage = ROCKET_DAMAGE;
  1972. float vel = ROCKET_VELOCITY;
  1973. if ( alt_fire )
  1974. {
  1975. vel *= 0.5f;
  1976. }
  1977. VectorCopy( muzzle, start );
  1978. WP_TraceSetStart( ent, start, vec3_origin, vec3_origin );//make sure our start point isn't on the other side of a wall
  1979. gentity_t *missile = CreateMissile( start, forward, vel, 10000, ent, alt_fire );
  1980. missile->classname = "rocket_proj";
  1981. missile->s.weapon = WP_ROCKET_LAUNCHER;
  1982. missile->mass = 10;
  1983. // Do the damages
  1984. if ( ent->s.number != 0 )
  1985. {
  1986. if ( g_spskill->integer == 0 )
  1987. {
  1988. damage = ROCKET_NPC_DAMAGE_EASY;
  1989. }
  1990. else if ( g_spskill->integer == 1 )
  1991. {
  1992. damage = ROCKET_NPC_DAMAGE_NORMAL;
  1993. }
  1994. else
  1995. {
  1996. damage = ROCKET_NPC_DAMAGE_HARD;
  1997. }
  1998. if (ent->client && ent->client->NPC_class==CLASS_BOBAFETT)
  1999. {
  2000. damage = damage/2;
  2001. }
  2002. }
  2003. if ( alt_fire )
  2004. {
  2005. int lockEntNum, lockTime;
  2006. if ( ent->NPC && ent->enemy )
  2007. {
  2008. lockEntNum = ent->enemy->s.number;
  2009. lockTime = Q_irand( 600, 1200 );
  2010. }
  2011. else
  2012. {
  2013. lockEntNum = g_rocketLockEntNum;
  2014. lockTime = g_rocketLockTime;
  2015. }
  2016. // we'll consider attempting to lock this little poochie onto some baddie.
  2017. if ( (lockEntNum > 0||ent->NPC&&lockEntNum>=0) && lockEntNum < ENTITYNUM_WORLD && lockTime > 0 )
  2018. {
  2019. // take our current lock time and divide that by 8 wedge slices to get the current lock amount
  2020. int dif = ( level.time - lockTime ) / ( 1200.0f / 8.0f );
  2021. if ( dif < 0 )
  2022. {
  2023. dif = 0;
  2024. }
  2025. else if ( dif > 8 )
  2026. {
  2027. dif = 8;
  2028. }
  2029. // if we are fully locked, always take on the enemy.
  2030. // Also give a slight advantage to higher, but not quite full charges.
  2031. // Finally, just give any amount of charge a very slight random chance of locking.
  2032. if ( dif == 8 || random() * dif > 2 || random() > 0.97f )
  2033. {
  2034. missile->enemy = &g_entities[lockEntNum];
  2035. if ( missile->enemy
  2036. && missile->enemy->inuse )//&& DistanceSquared( missile->currentOrigin, missile->enemy->currentOrigin ) < 262144 && InFOV( missile->currentOrigin, missile->enemy->currentOrigin, missile->enemy->client->ps.viewangles, 45, 45 ) )
  2037. {
  2038. if ( missile->enemy->client
  2039. && (missile->enemy->client->ps.forcePowersKnown&(1<<FP_PUSH))
  2040. && missile->enemy->client->ps.forcePowerLevel[FP_PUSH] > FORCE_LEVEL_0 )
  2041. {//have force push, don't flee from homing rockets
  2042. }
  2043. else
  2044. {
  2045. vec3_t dir, dir2;
  2046. AngleVectors( missile->enemy->currentAngles, dir, NULL, NULL );
  2047. AngleVectors( ent->client->renderInfo.eyeAngles, dir2, NULL, NULL );
  2048. if ( DotProduct( dir, dir2 ) < 0.0f )
  2049. {
  2050. G_StartFlee( missile->enemy, ent, missile->enemy->currentOrigin, AEL_DANGER_GREAT, 3000, 5000 );
  2051. if ( !TIMER_Done( missile->enemy, "flee" ) )
  2052. {
  2053. TIMER_Set( missile->enemy, "rocketChasing", 500 );
  2054. }
  2055. }
  2056. }
  2057. }
  2058. }
  2059. }
  2060. VectorCopy( forward, missile->movedir );
  2061. missile->e_ThinkFunc = thinkF_rocketThink;
  2062. missile->random = 1.0f;
  2063. missile->nextthink = level.time + ROCKET_ALT_THINK_TIME;
  2064. }
  2065. // Make it easier to hit things
  2066. VectorSet( missile->maxs, ROCKET_SIZE, ROCKET_SIZE, ROCKET_SIZE );
  2067. VectorScale( missile->maxs, -1, missile->mins );
  2068. missile->damage = damage;
  2069. missile->dflags = DAMAGE_DEATH_KNOCKBACK;
  2070. if ( alt_fire )
  2071. {
  2072. missile->methodOfDeath = MOD_ROCKET_ALT;
  2073. missile->splashMethodOfDeath = MOD_ROCKET_ALT;// ?SPLASH;
  2074. }
  2075. else
  2076. {
  2077. missile->methodOfDeath = MOD_ROCKET;
  2078. missile->splashMethodOfDeath = MOD_ROCKET;// ?SPLASH;
  2079. }
  2080. missile->clipmask = MASK_SHOT | CONTENTS_LIGHTSABER;
  2081. missile->splashDamage = ROCKET_SPLASH_DAMAGE;
  2082. missile->splashRadius = ROCKET_SPLASH_RADIUS;
  2083. // we don't want it to ever bounce
  2084. missile->bounceCount = 0;
  2085. }
  2086. static void WP_FireConcussionAlt( gentity_t *ent )
  2087. {//a rail-gun-like beam
  2088. int damage = CONC_ALT_DAMAGE, skip, traces = DISRUPTOR_ALT_TRACES;
  2089. qboolean render_impact = qtrue;
  2090. vec3_t start, end;
  2091. vec3_t muzzle2, spot, dir;
  2092. trace_t tr;
  2093. gentity_t *traceEnt, *tent;
  2094. float dist, shotDist, shotRange = 8192;
  2095. qboolean hitDodged = qfalse;
  2096. if (ent->s.number >= MAX_CLIENTS)
  2097. {
  2098. vec3_t angles;
  2099. vectoangles(forward, angles);
  2100. angles[PITCH] += ( crandom() * (CONC_NPC_SPREAD+(6-ent->NPC->currentAim)*0.25f));//was 0.5f
  2101. angles[YAW] += ( crandom() * (CONC_NPC_SPREAD+(6-ent->NPC->currentAim)*0.25f));//was 0.5f
  2102. AngleVectors(angles, forward, vright, up);
  2103. }
  2104. //Shove us backwards for half a second
  2105. VectorMA( ent->client->ps.velocity, -200, forward, ent->client->ps.velocity );
  2106. ent->client->ps.groundEntityNum = ENTITYNUM_NONE;
  2107. if ( (ent->client->ps.pm_flags&PMF_DUCKED) )
  2108. {//hunkered down
  2109. ent->client->ps.pm_time = 100;
  2110. }
  2111. else
  2112. {
  2113. ent->client->ps.pm_time = 250;
  2114. }
  2115. ent->client->ps.pm_flags |= PMF_TIME_KNOCKBACK|PMF_TIME_NOFRICTION;
  2116. //FIXME: only if on ground? So no "rocket jump"? Or: (see next FIXME)
  2117. //FIXME: instead, set a forced ucmd backmove instead of this sliding
  2118. VectorCopy( muzzle, muzzle2 ); // making a backup copy
  2119. // The trace start will originate at the eye so we can ensure that it hits the crosshair.
  2120. if ( ent->NPC )
  2121. {
  2122. switch ( g_spskill->integer )
  2123. {
  2124. case 0:
  2125. damage = CONC_ALT_NPC_DAMAGE_EASY;
  2126. break;
  2127. case 1:
  2128. damage = CONC_ALT_NPC_DAMAGE_MEDIUM;
  2129. break;
  2130. case 2:
  2131. default:
  2132. damage = CONC_ALT_NPC_DAMAGE_HARD;
  2133. break;
  2134. }
  2135. }
  2136. VectorCopy( muzzle, start );
  2137. WP_TraceSetStart( ent, start, vec3_origin, vec3_origin );
  2138. skip = ent->s.number;
  2139. // if ( ent->client && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > 0 && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > cg.time )
  2140. // {
  2141. // // in overcharge mode, so doing double damage
  2142. // damage *= 2;
  2143. // }
  2144. //Make it a little easier to hit guys at long range
  2145. vec3_t shot_mins, shot_maxs;
  2146. VectorSet( shot_mins, -1, -1, -1 );
  2147. VectorSet( shot_maxs, 1, 1, 1 );
  2148. for ( int i = 0; i < traces; i++ )
  2149. {
  2150. VectorMA( start, shotRange, forward, end );
  2151. //NOTE: if you want to be able to hit guys in emplaced guns, use "G2_COLLIDE, 10" instead of "G2_RETURNONHIT, 0"
  2152. //alternately, if you end up hitting an emplaced_gun that has a sitter, just redo this one trace with the "G2_COLLIDE, 10" to see if we it the sitter
  2153. //gi.trace( &tr, start, NULL, NULL, end, skip, MASK_SHOT, G2_COLLIDE, 10 );//G2_RETURNONHIT, 0 );
  2154. gi.trace( &tr, start, shot_mins, shot_maxs, end, skip, MASK_SHOT, G2_COLLIDE, 10 );//G2_RETURNONHIT, 0 );
  2155. if ( tr.surfaceFlags & SURF_NOIMPACT )
  2156. {
  2157. render_impact = qfalse;
  2158. }
  2159. if ( tr.entityNum == ent->s.number )
  2160. {
  2161. // should never happen, but basically we don't want to consider a hit to ourselves?
  2162. // Get ready for an attempt to trace through another person
  2163. VectorCopy( tr.endpos, muzzle2 );
  2164. VectorCopy( tr.endpos, start );
  2165. skip = tr.entityNum;
  2166. #ifdef _DEBUG
  2167. gi.Printf( "BAD! Concussion gun shot somehow traced back and hit the owner!\n" );
  2168. #endif
  2169. continue;
  2170. }
  2171. // always render a shot beam, doing this the old way because I don't much feel like overriding the effect.
  2172. //NOTE: let's just draw one beam at the end
  2173. //tent = G_TempEntity( tr.endpos, EV_CONC_ALT_SHOT );
  2174. //tent->svFlags |= SVF_BROADCAST;
  2175. //VectorCopy( muzzle2, tent->s.origin2 );
  2176. if ( tr.fraction >= 1.0f )
  2177. {
  2178. // draw the beam but don't do anything else
  2179. break;
  2180. }
  2181. traceEnt = &g_entities[tr.entityNum];
  2182. if ( traceEnt //&& traceEnt->NPC
  2183. && ( traceEnt->s.weapon == WP_SABER || (traceEnt->client && (traceEnt->client->NPC_class == CLASS_BOBAFETT||traceEnt->client->NPC_class == CLASS_REBORN) ) ) )
  2184. {//FIXME: need a more reliable way to know we hit a jedi?
  2185. hitDodged = Jedi_DodgeEvasion( traceEnt, ent, &tr, HL_NONE );
  2186. //acts like we didn't even hit him
  2187. }
  2188. if ( !hitDodged )
  2189. {
  2190. if ( render_impact )
  2191. {
  2192. if (( tr.entityNum < ENTITYNUM_WORLD && traceEnt->takedamage )
  2193. || !Q_stricmp( traceEnt->classname, "misc_model_breakable" )
  2194. || traceEnt->s.eType == ET_MOVER )
  2195. {
  2196. // Create a simple impact type mark that doesn't last long in the world
  2197. G_PlayEffect( G_EffectIndex( "concussion/alt_hit" ), tr.endpos, tr.plane.normal );
  2198. if ( traceEnt->client && LogAccuracyHit( traceEnt, ent ))
  2199. {//NOTE: hitting multiple ents can still get you over 100% accuracy
  2200. ent->client->ps.persistant[PERS_ACCURACY_HITS]++;
  2201. }
  2202. int hitLoc = G_GetHitLocFromTrace( &tr, MOD_CONC_ALT );
  2203. qboolean noKnockBack = (traceEnt->flags&FL_NO_KNOCKBACK);//will be set if they die, I want to know if it was on *before* they died
  2204. if ( traceEnt && traceEnt->client && traceEnt->client->NPC_class == CLASS_GALAKMECH )
  2205. {//hehe
  2206. G_Damage( traceEnt, ent, ent, forward, tr.endpos, 10, DAMAGE_NO_KNOCKBACK|DAMAGE_NO_HIT_LOC, MOD_CONC_ALT, hitLoc );
  2207. break;
  2208. }
  2209. G_Damage( traceEnt, ent, ent, forward, tr.endpos, damage, DAMAGE_NO_KNOCKBACK|DAMAGE_NO_HIT_LOC, MOD_CONC_ALT, hitLoc );
  2210. //do knockback and knockdown manually
  2211. if ( traceEnt->client )
  2212. {//only if we hit a client
  2213. vec3_t pushDir;
  2214. VectorCopy( forward, pushDir );
  2215. if ( pushDir[2] < 0.2f )
  2216. {
  2217. pushDir[2] = 0.2f;
  2218. }//hmm, re-normalize? nah...
  2219. //if ( traceEnt->NPC || Q_irand(0,g_spskill->integer+1) )
  2220. {
  2221. if ( !noKnockBack )
  2222. {//knock-backable
  2223. G_Throw( traceEnt, pushDir, 200 );
  2224. if ( traceEnt->client->NPC_class == CLASS_ROCKETTROOPER )
  2225. {
  2226. traceEnt->client->ps.pm_time = Q_irand( 1500, 3000 );
  2227. }
  2228. }
  2229. if ( traceEnt->health > 0 )
  2230. {//alive
  2231. if ( G_HasKnockdownAnims( traceEnt ) )
  2232. {//knock-downable
  2233. G_Knockdown( traceEnt, ent, pushDir, 400, qtrue );
  2234. }
  2235. }
  2236. }
  2237. }
  2238. if ( traceEnt->s.eType == ET_MOVER )
  2239. {//stop the traces on any mover
  2240. break;
  2241. }
  2242. }
  2243. else
  2244. {
  2245. // we only make this mark on things that can't break or move
  2246. tent = G_TempEntity( tr.endpos, EV_CONC_ALT_MISS );
  2247. tent->svFlags |= SVF_BROADCAST;
  2248. VectorCopy( tr.plane.normal, tent->pos1 );
  2249. break; // hit solid, but doesn't take damage, so stop the shot...we _could_ allow it to shoot through walls, might be cool?
  2250. }
  2251. }
  2252. else // not rendering impact, must be a skybox or other similar thing?
  2253. {
  2254. break; // don't try anymore traces
  2255. }
  2256. }
  2257. // Get ready for an attempt to trace through another person
  2258. VectorCopy( tr.endpos, muzzle2 );
  2259. VectorCopy( tr.endpos, start );
  2260. skip = tr.entityNum;
  2261. hitDodged = qfalse;
  2262. }
  2263. //just draw one beam all the way to the end
  2264. tent = G_TempEntity( tr.endpos, EV_CONC_ALT_SHOT );
  2265. tent->svFlags |= SVF_BROADCAST;
  2266. VectorCopy( muzzle, tent->s.origin2 );
  2267. // now go along the trail and make sight events
  2268. VectorSubtract( tr.endpos, muzzle, dir );
  2269. shotDist = VectorNormalize( dir );
  2270. //FIXME: if shoot *really* close to someone, the alert could be way out of their FOV
  2271. for ( dist = 0; dist < shotDist; dist += 64 )
  2272. {
  2273. //FIXME: on a really long shot, this could make a LOT of alerts in one frame...
  2274. VectorMA( muzzle, dist, dir, spot );
  2275. AddSightEvent( ent, spot, 256, AEL_DISCOVERED, 50 );
  2276. //FIXME: creates *way* too many effects, make it one effect somehow?
  2277. G_PlayEffect( G_EffectIndex( "concussion/alt_ring" ), spot, forward );
  2278. }
  2279. //FIXME: spawn a temp ent that continuously spawns sight alerts here? And 1 sound alert to draw their attention?
  2280. VectorMA( start, shotDist-4, forward, spot );
  2281. AddSightEvent( ent, spot, 256, AEL_DISCOVERED, 50 );
  2282. G_PlayEffect( G_EffectIndex( "concussion/altmuzzle_flash" ), muzzle, forward );
  2283. }
  2284. static void WP_FireConcussion( gentity_t *ent )
  2285. {//a fast rocket-like projectile
  2286. vec3_t start;
  2287. int damage = CONC_DAMAGE;
  2288. float vel = CONC_VELOCITY;
  2289. if (ent->s.number >= MAX_CLIENTS)
  2290. {
  2291. vec3_t angles;
  2292. vectoangles(forward, angles);
  2293. angles[PITCH] += ( crandom() * (CONC_NPC_SPREAD+(6-ent->NPC->currentAim)*0.25f));//was 0.5f
  2294. angles[YAW] += ( crandom() * (CONC_NPC_SPREAD+(6-ent->NPC->currentAim)*0.25f));//was 0.5f
  2295. AngleVectors(angles, forward, vright, up);
  2296. }
  2297. //hold us still for a bit
  2298. ent->client->ps.pm_time = 300;
  2299. ent->client->ps.pm_flags |= PMF_TIME_KNOCKBACK;
  2300. //add viewkick
  2301. if ( ent->s.number < MAX_CLIENTS//player only
  2302. && !cg.renderingThirdPerson )//gives an advantage to being in 3rd person, but would look silly otherwise
  2303. {//kick the view back
  2304. cg.kick_angles[PITCH] = Q_flrand( -10, -15 );
  2305. cg.kick_time = level.time;
  2306. }
  2307. VectorCopy( muzzle, start );
  2308. WP_TraceSetStart( ent, start, vec3_origin, vec3_origin );//make sure our start point isn't on the other side of a wall
  2309. gentity_t *missile = CreateMissile( start, forward, vel, 10000, ent, qfalse );
  2310. missile->classname = "conc_proj";
  2311. missile->s.weapon = WP_CONCUSSION;
  2312. missile->mass = 10;
  2313. // Do the damages
  2314. if ( ent->s.number != 0 )
  2315. {
  2316. if ( g_spskill->integer == 0 )
  2317. {
  2318. damage = CONC_NPC_DAMAGE_EASY;
  2319. }
  2320. else if ( g_spskill->integer == 1 )
  2321. {
  2322. damage = CONC_NPC_DAMAGE_NORMAL;
  2323. }
  2324. else
  2325. {
  2326. damage = CONC_NPC_DAMAGE_HARD;
  2327. }
  2328. }
  2329. // Make it easier to hit things
  2330. VectorSet( missile->maxs, ROCKET_SIZE, ROCKET_SIZE, ROCKET_SIZE );
  2331. VectorScale( missile->maxs, -1, missile->mins );
  2332. missile->damage = damage;
  2333. missile->dflags = DAMAGE_EXTRA_KNOCKBACK;
  2334. missile->methodOfDeath = MOD_CONC;
  2335. missile->splashMethodOfDeath = MOD_CONC;
  2336. missile->clipmask = MASK_SHOT | CONTENTS_LIGHTSABER;
  2337. missile->splashDamage = CONC_SPLASH_DAMAGE;
  2338. missile->splashRadius = CONC_SPLASH_RADIUS;
  2339. // we don't want it to ever bounce
  2340. missile->bounceCount = 0;
  2341. }
  2342. //-----------------------
  2343. // Det Pack
  2344. //-----------------------
  2345. //---------------------------------------------------------
  2346. void charge_stick( gentity_t *self, gentity_t *other, trace_t *trace )
  2347. //---------------------------------------------------------
  2348. {
  2349. self->s.eType = ET_GENERAL;
  2350. // make us so we can take damage
  2351. self->clipmask = MASK_SHOT;
  2352. self->contents = CONTENTS_SHOTCLIP;
  2353. self->takedamage = qtrue;
  2354. self->health = 25;
  2355. self->e_DieFunc = dieF_WP_ExplosiveDie;
  2356. VectorSet( self->maxs, 10, 10, 10 );
  2357. VectorScale( self->maxs, -1, self->mins );
  2358. self->activator = self->owner;
  2359. self->owner = NULL;
  2360. self->e_TouchFunc = touchF_NULL;
  2361. self->e_ThinkFunc = thinkF_NULL;
  2362. self->nextthink = -1;
  2363. WP_Stick( self, trace, 1.0f );
  2364. }
  2365. //---------------------------------------------------------
  2366. static void WP_DropDetPack( gentity_t *self, vec3_t start, vec3_t dir )
  2367. //---------------------------------------------------------
  2368. {
  2369. // Chucking a new one
  2370. AngleVectors( self->client->ps.viewangles, forward, vright, up );
  2371. CalcMuzzlePoint( self, forward, vright, up, muzzle, 0 );
  2372. VectorNormalize( forward );
  2373. VectorMA( muzzle, -4, forward, muzzle );
  2374. VectorCopy( muzzle, start );
  2375. WP_TraceSetStart( self, start, vec3_origin, vec3_origin );//make sure our start point isn't on the other side of a wall
  2376. gentity_t *missile = CreateMissile( start, forward, 300, 10000, self, qfalse );
  2377. missile->fxID = G_EffectIndex( "detpack/explosion" ); // if we set an explosion effect, explode death can use that instead
  2378. missile->classname = "detpack";
  2379. missile->s.weapon = WP_DET_PACK;
  2380. missile->s.pos.trType = TR_GRAVITY;
  2381. missile->s.eFlags |= EF_MISSILE_STICK;
  2382. missile->e_TouchFunc = touchF_charge_stick;
  2383. missile->damage = FLECHETTE_MINE_DAMAGE;
  2384. missile->methodOfDeath = MOD_DETPACK;
  2385. missile->splashDamage = FLECHETTE_MINE_SPLASH_DAMAGE;
  2386. missile->splashRadius = FLECHETTE_MINE_SPLASH_RADIUS;
  2387. missile->splashMethodOfDeath = MOD_DETPACK;// ?SPLASH;
  2388. missile->clipmask = (CONTENTS_SOLID|CONTENTS_BODY|CONTENTS_SHOTCLIP);//MASK_SHOT;
  2389. // we don't want it to ever bounce
  2390. missile->bounceCount = 0;
  2391. //Hack! Need to track spawn times.
  2392. missile->useDebounceTime = level.time;
  2393. missile->s.radius = 30;
  2394. VectorSet( missile->s.modelScale, 1.0f, 1.0f, 1.0f );
  2395. gi.G2API_InitGhoul2Model( missile->ghoul2, weaponData[WP_DET_PACK].missileMdl, G_ModelIndex( weaponData[WP_DET_PACK].missileMdl ));
  2396. AddSoundEvent( NULL, missile->currentOrigin, 128, AEL_MINOR, qtrue );
  2397. AddSightEvent( NULL, missile->currentOrigin, 128, AEL_SUSPICIOUS, 10 );
  2398. }
  2399. //---------------------------------------------------------
  2400. static void WP_FireDetPack( gentity_t *ent, qboolean alt_fire )
  2401. //---------------------------------------------------------
  2402. {
  2403. if ( !ent || !ent->client )
  2404. {
  2405. return;
  2406. }
  2407. if ( alt_fire )
  2408. {
  2409. if ( ent->client->ps.eFlags & EF_PLANTED_CHARGE )
  2410. {
  2411. gentity_t *found = NULL;
  2412. // loop through all ents and blow the crap out of them!
  2413. while (( found = G_Find( found, FOFS( classname ), "detpack" )) != NULL )
  2414. {
  2415. if ( found->activator == ent )
  2416. {
  2417. VectorCopy( found->currentOrigin, found->s.origin );
  2418. found->e_ThinkFunc = thinkF_WP_Explode;
  2419. found->nextthink = level.time + 100 + random() * 100;
  2420. G_Sound( found, G_SoundIndex( "sound/weapons/detpack/warning.wav" ));
  2421. // would be nice if this actually worked?
  2422. AddSoundEvent( NULL, found->currentOrigin, found->splashRadius*2, AEL_DANGER, qfalse, qtrue );//FIXME: are we on ground or not?
  2423. AddSightEvent( NULL, found->currentOrigin, found->splashRadius*2, AEL_DISCOVERED, 100 );
  2424. }
  2425. }
  2426. ent->client->ps.eFlags &= ~EF_PLANTED_CHARGE;
  2427. }
  2428. }
  2429. else
  2430. {
  2431. //Can't have more than 5 active at once.
  2432. int count = 0;
  2433. gentity_t *found = NULL;
  2434. gentity_t *oldest = NULL;
  2435. while((found = G_Find( found, FOFS( classname ), "detpack" )) != NULL) {
  2436. count++;
  2437. if(!oldest || found->useDebounceTime < oldest->useDebounceTime) {
  2438. oldest = found;
  2439. }
  2440. }
  2441. if(count == 5 && oldest) {
  2442. void ObjectDie(gentity_t*, gentity_t*, gentity_t*, int, int);
  2443. ObjectDie(oldest, NULL, NULL, 0, 0);
  2444. }
  2445. WP_DropDetPack( ent, muzzle, forward );
  2446. ent->client->ps.eFlags |= EF_PLANTED_CHARGE;
  2447. }
  2448. }
  2449. //-----------------------
  2450. // Laser Trip Mine
  2451. //-----------------------
  2452. #define PROXIMITY_STYLE 1
  2453. #define TRIPWIRE_STYLE 2
  2454. //---------------------------------------------------------
  2455. void touchLaserTrap( gentity_t *ent, gentity_t *other, trace_t *trace )
  2456. //---------------------------------------------------------
  2457. {
  2458. ent->s.eType = ET_GENERAL;
  2459. // a tripwire so add draw line flag
  2460. VectorCopy( trace->plane.normal, ent->movedir );
  2461. // make it shootable
  2462. VectorSet( ent->mins, -4, -4, -4 );
  2463. VectorSet( ent->maxs, 4, 4, 4 );
  2464. ent->clipmask = MASK_SHOT;
  2465. ent->contents = CONTENTS_SHOTCLIP;
  2466. ent->takedamage = qtrue;
  2467. ent->health = 15;
  2468. ent->e_DieFunc = dieF_WP_ExplosiveDie;
  2469. ent->e_TouchFunc = touchF_NULL;
  2470. // so we can trip it too
  2471. ent->activator = ent->owner;
  2472. ent->owner = NULL;
  2473. WP_Stick( ent, trace );
  2474. if ( ent->count == TRIPWIRE_STYLE )
  2475. {
  2476. vec3_t mins = {-4,-4,-4}, maxs = {4,4,4};//FIXME: global these
  2477. trace_t tr;
  2478. VectorMA( ent->currentOrigin, 32, ent->movedir, ent->s.origin2 );
  2479. gi.trace( &tr, ent->s.origin2, mins, maxs, ent->currentOrigin, ent->s.number, MASK_SHOT, G2_RETURNONHIT, 0 );
  2480. VectorCopy( tr.endpos, ent->s.origin2 );
  2481. ent->e_ThinkFunc = thinkF_laserTrapThink;
  2482. }
  2483. else
  2484. {
  2485. ent->e_ThinkFunc = thinkF_WP_prox_mine_think;
  2486. }
  2487. ent->nextthink = level.time + LT_ACTIVATION_DELAY;
  2488. }
  2489. // copied from flechette prox above...which will not be used if this gets approved
  2490. //---------------------------------------------------------
  2491. void WP_prox_mine_think( gentity_t *ent )
  2492. //---------------------------------------------------------
  2493. {
  2494. int count;
  2495. qboolean blow = qfalse;
  2496. // first time through?
  2497. if ( ent->count )
  2498. {
  2499. // play activated warning
  2500. ent->count = 0;
  2501. ent->s.eFlags |= EF_PROX_TRIP;
  2502. G_Sound( ent, G_SoundIndex( "sound/weapons/laser_trap/warning.wav" ));
  2503. }
  2504. // if it isn't time to auto-explode, do a small proximity check
  2505. if ( ent->delay > level.time )
  2506. {
  2507. count = G_RadiusList( ent->currentOrigin, PROX_MINE_RADIUS_CHECK, ent, qtrue, ent_list );
  2508. for ( int i = 0; i < count; i++ )
  2509. {
  2510. if ( ent_list[i]->client && ent_list[i]->health > 0 && ent->activator && ent_list[i]->s.number != ent->activator->s.number )
  2511. {
  2512. blow = qtrue;
  2513. break;
  2514. }
  2515. }
  2516. }
  2517. else
  2518. {
  2519. // well, we must die now
  2520. blow = qtrue;
  2521. }
  2522. if ( blow )
  2523. {
  2524. // G_Sound( ent, G_SoundIndex( "sound/weapons/flechette/warning.wav" ));
  2525. ent->e_ThinkFunc = thinkF_WP_Explode;
  2526. ent->nextthink = level.time + 200;
  2527. }
  2528. else
  2529. {
  2530. // we probably don't need to do this thinking logic very often...maybe this is fast enough?
  2531. ent->nextthink = level.time + 500;
  2532. }
  2533. }
  2534. //---------------------------------------------------------
  2535. void laserTrapThink( gentity_t *ent )
  2536. //---------------------------------------------------------
  2537. {
  2538. gentity_t *traceEnt;
  2539. vec3_t end, mins = {-4,-4,-4}, maxs = {4,4,4};
  2540. trace_t tr;
  2541. // turn on the beam effect
  2542. if ( !(ent->s.eFlags & EF_FIRING ))
  2543. {
  2544. // arm me
  2545. G_Sound( ent, G_SoundIndex( "sound/weapons/laser_trap/warning.wav" ));
  2546. ent->s.loopSound = G_SoundIndex( "sound/weapons/laser_trap/hum_loop.wav" );
  2547. ent->s.eFlags |= EF_FIRING;
  2548. }
  2549. ent->e_ThinkFunc = thinkF_laserTrapThink;
  2550. ent->nextthink = level.time + FRAMETIME;
  2551. // Find the main impact point
  2552. VectorMA( ent->s.pos.trBase, 2048, ent->movedir, end );
  2553. gi.trace( &tr, ent->s.origin2, mins, maxs, end, ent->s.number, MASK_SHOT, G2_RETURNONHIT, 0 );
  2554. traceEnt = &g_entities[ tr.entityNum ];
  2555. // Adjust this so that the effect has a relatively fresh endpoint
  2556. VectorCopy( tr.endpos, ent->pos4 );
  2557. if ( traceEnt->client || tr.startsolid )
  2558. {
  2559. // go boom
  2560. WP_Explode( ent );
  2561. ent->s.eFlags &= ~EF_FIRING; // don't draw beam if we are dead
  2562. }
  2563. else
  2564. {
  2565. /*
  2566. // FIXME: they need to avoid the beam!
  2567. AddSoundEvent( ent->owner, ent->currentOrigin, ent->splashRadius*2, AEL_DANGER );
  2568. AddSightEvent( ent->owner, ent->currentOrigin, ent->splashRadius*2, AEL_DANGER, 50 );
  2569. */
  2570. }
  2571. }
  2572. //---------------------------------------------------------
  2573. void CreateLaserTrap( gentity_t *laserTrap, vec3_t start, gentity_t *owner )
  2574. //---------------------------------------------------------
  2575. {
  2576. if ( !VALIDSTRING( laserTrap->classname ) ||
  2577. !Q_stricmp(laserTrap->classname, "noclass"))
  2578. {
  2579. // since we may be coming from a map placed trip mine, we don't want to override that class name....
  2580. // That would be bad because the player drop code tries to limit number of placed items...so it would have removed map placed ones as well.
  2581. laserTrap->classname = "tripmine";
  2582. }
  2583. laserTrap->splashDamage = LT_SPLASH_DAM;
  2584. laserTrap->splashRadius = LT_SPLASH_RAD;
  2585. laserTrap->damage = LT_DAMAGE;
  2586. laserTrap->methodOfDeath = MOD_LASERTRIP;
  2587. laserTrap->splashMethodOfDeath = MOD_LASERTRIP;//? SPLASH;
  2588. laserTrap->s.eType = ET_MISSILE;
  2589. laserTrap->svFlags = SVF_USE_CURRENT_ORIGIN;
  2590. laserTrap->s.weapon = WP_TRIP_MINE;
  2591. laserTrap->owner = owner;
  2592. // VectorSet( laserTrap->mins, -LT_SIZE, -LT_SIZE, -LT_SIZE );
  2593. // VectorSet( laserTrap->maxs, LT_SIZE, LT_SIZE, LT_SIZE );
  2594. laserTrap->clipmask = (CONTENTS_SOLID|CONTENTS_BODY|CONTENTS_SHOTCLIP);//MASK_SHOT;
  2595. laserTrap->s.pos.trTime = level.time; // move a bit on the very first frame
  2596. VectorCopy( start, laserTrap->s.pos.trBase );
  2597. VectorCopy( start, laserTrap->currentOrigin);
  2598. VectorCopy( start, laserTrap->pos2 ); // ?? wtf ?
  2599. laserTrap->fxID = G_EffectIndex( "tripMine/explosion" );
  2600. laserTrap->e_TouchFunc = touchF_touchLaserTrap;
  2601. laserTrap->s.radius = 60;
  2602. VectorSet( laserTrap->s.modelScale, 1.0f, 1.0f, 1.0f );
  2603. gi.G2API_InitGhoul2Model( laserTrap->ghoul2, weaponData[WP_TRIP_MINE].missileMdl, G_ModelIndex( weaponData[WP_TRIP_MINE].missileMdl ));
  2604. }
  2605. //---------------------------------------------------------
  2606. static void WP_RemoveOldTraps( gentity_t *ent )
  2607. //---------------------------------------------------------
  2608. {
  2609. gentity_t *found = NULL;
  2610. int trapcount = 0, i;
  2611. int foundLaserTraps[MAX_GENTITIES] = {ENTITYNUM_NONE};
  2612. int trapcount_org, lowestTimeStamp;
  2613. int removeMe;
  2614. // see how many there are now
  2615. while (( found = G_Find( found, FOFS(classname), "tripmine" )) != NULL )
  2616. {
  2617. if ( found->activator != ent ) // activator is really the owner?
  2618. {
  2619. continue;
  2620. }
  2621. foundLaserTraps[trapcount++] = found->s.number;
  2622. }
  2623. // now remove first ones we find until there are only 9 left
  2624. found = NULL;
  2625. trapcount_org = trapcount;
  2626. lowestTimeStamp = level.time;
  2627. while ( trapcount > 4 )
  2628. {
  2629. removeMe = -1;
  2630. for ( i = 0; i < trapcount_org; i++ )
  2631. {
  2632. if ( foundLaserTraps[i] == ENTITYNUM_NONE )
  2633. {
  2634. continue;
  2635. }
  2636. found = &g_entities[foundLaserTraps[i]];
  2637. if ( found->setTime < lowestTimeStamp )
  2638. {
  2639. removeMe = i;
  2640. lowestTimeStamp = found->setTime;
  2641. }
  2642. }
  2643. if ( removeMe != -1 )
  2644. {
  2645. //remove it... or blow it?
  2646. if ( &g_entities[foundLaserTraps[removeMe]] == NULL )
  2647. {
  2648. break;
  2649. }
  2650. else
  2651. {
  2652. G_FreeEntity( &g_entities[foundLaserTraps[removeMe]] );
  2653. }
  2654. foundLaserTraps[removeMe] = ENTITYNUM_NONE;
  2655. trapcount--;
  2656. }
  2657. else
  2658. {
  2659. break;
  2660. }
  2661. }
  2662. }
  2663. //---------------------------------------------------------
  2664. void WP_PlaceLaserTrap( gentity_t *ent, qboolean alt_fire )
  2665. //---------------------------------------------------------
  2666. {
  2667. vec3_t start;
  2668. gentity_t *laserTrap;
  2669. // limit to 5 placed at any one time
  2670. WP_RemoveOldTraps( ent );
  2671. //FIXME: surface must be within 64
  2672. laserTrap = G_Spawn();
  2673. if ( laserTrap )
  2674. {
  2675. // now make the new one
  2676. VectorCopy( muzzle, start );
  2677. WP_TraceSetStart( ent, start, vec3_origin, vec3_origin );//make sure our start point isn't on the other side of a wall
  2678. CreateLaserTrap( laserTrap, start, ent );
  2679. // set player-created-specific fields
  2680. laserTrap->setTime = level.time;//remember when we placed it
  2681. laserTrap->s.eFlags |= EF_MISSILE_STICK;
  2682. laserTrap->s.pos.trType = TR_GRAVITY;
  2683. VectorScale( forward, LT_VELOCITY, laserTrap->s.pos.trDelta );
  2684. if ( alt_fire )
  2685. {
  2686. laserTrap->count = PROXIMITY_STYLE;
  2687. laserTrap->delay = level.time + 40000; // will auto-blow in 40 seconds.
  2688. laserTrap->methodOfDeath = MOD_LASERTRIP_ALT;
  2689. laserTrap->splashMethodOfDeath = MOD_LASERTRIP_ALT;//? SPLASH;
  2690. }
  2691. else
  2692. {
  2693. laserTrap->count = TRIPWIRE_STYLE;
  2694. }
  2695. }
  2696. }
  2697. //---------------------
  2698. // Thermal Detonator
  2699. //---------------------
  2700. //---------------------------------------------------------
  2701. void thermalDetonatorExplode( gentity_t *ent )
  2702. //---------------------------------------------------------
  2703. {
  2704. if ( (ent->s.eFlags&EF_HELD_BY_SAND_CREATURE) )
  2705. {
  2706. ent->takedamage = qfalse; // don't allow double deaths!
  2707. G_Damage( ent->activator, ent, ent->owner, vec3_origin, ent->currentOrigin, TD_ALT_DAMAGE, 0, MOD_EXPLOSIVE );
  2708. G_PlayEffect( "thermal/explosion", ent->currentOrigin );
  2709. G_PlayEffect( "thermal/shockwave", ent->currentOrigin );
  2710. G_FreeEntity( ent );
  2711. }
  2712. else if ( !ent->count )
  2713. {
  2714. G_Sound( ent, G_SoundIndex( "sound/weapons/thermal/warning.wav" ) );
  2715. ent->count = 1;
  2716. ent->nextthink = level.time + 800;
  2717. ent->svFlags |= SVF_BROADCAST;//so everyone hears/sees the explosion?
  2718. }
  2719. else
  2720. {
  2721. vec3_t pos;
  2722. VectorSet( pos, ent->currentOrigin[0], ent->currentOrigin[1], ent->currentOrigin[2] + 8 );
  2723. ent->takedamage = qfalse; // don't allow double deaths!
  2724. G_RadiusDamage( ent->currentOrigin, ent->owner, TD_SPLASH_DAM, TD_SPLASH_RAD, NULL, MOD_EXPLOSIVE_SPLASH );
  2725. G_PlayEffect( "thermal/explosion", ent->currentOrigin );
  2726. G_PlayEffect( "thermal/shockwave", ent->currentOrigin );
  2727. G_FreeEntity( ent );
  2728. }
  2729. }
  2730. //-------------------------------------------------------------------------------------------------------------
  2731. void thermal_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod, int dFlags, int hitLoc )
  2732. //-------------------------------------------------------------------------------------------------------------
  2733. {
  2734. thermalDetonatorExplode( self );
  2735. }
  2736. //---------------------------------------------------------
  2737. qboolean WP_LobFire( gentity_t *self, vec3_t start, vec3_t target, vec3_t mins, vec3_t maxs, int clipmask,
  2738. vec3_t velocity, qboolean tracePath, int ignoreEntNum, int enemyNum,
  2739. float minSpeed = 0, float maxSpeed = 0, float idealSpeed = 0, qboolean mustHit = qfalse )
  2740. //---------------------------------------------------------
  2741. {
  2742. float targetDist, shotSpeed, speedInc = 100, travelTime, impactDist, bestImpactDist = Q3_INFINITE;//fireSpeed,
  2743. vec3_t targetDir, shotVel, failCase;
  2744. trace_t trace;
  2745. trajectory_t tr;
  2746. qboolean blocked;
  2747. int elapsedTime, skipNum, timeStep = 500, hitCount = 0, maxHits = 7;
  2748. vec3_t lastPos, testPos;
  2749. gentity_t *traceEnt;
  2750. if ( !idealSpeed )
  2751. {
  2752. idealSpeed = 300;
  2753. }
  2754. else if ( idealSpeed < speedInc )
  2755. {
  2756. idealSpeed = speedInc;
  2757. }
  2758. shotSpeed = idealSpeed;
  2759. skipNum = (idealSpeed-speedInc)/speedInc;
  2760. if ( !minSpeed )
  2761. {
  2762. minSpeed = 100;
  2763. }
  2764. if ( !maxSpeed )
  2765. {
  2766. maxSpeed = 900;
  2767. }
  2768. while ( hitCount < maxHits )
  2769. {
  2770. VectorSubtract( target, start, targetDir );
  2771. targetDist = VectorNormalize( targetDir );
  2772. VectorScale( targetDir, shotSpeed, shotVel );
  2773. travelTime = targetDist/shotSpeed;
  2774. shotVel[2] += travelTime * 0.5 * g_gravity->value;
  2775. if ( !hitCount )
  2776. {//save the first (ideal) one as the failCase (fallback value)
  2777. if ( !mustHit )
  2778. {//default is fine as a return value
  2779. VectorCopy( shotVel, failCase );
  2780. }
  2781. }
  2782. if ( tracePath )
  2783. {//do a rough trace of the path
  2784. blocked = qfalse;
  2785. VectorCopy( start, tr.trBase );
  2786. VectorCopy( shotVel, tr.trDelta );
  2787. tr.trType = TR_GRAVITY;
  2788. tr.trTime = level.time;
  2789. travelTime *= 1000.0f;
  2790. VectorCopy( start, lastPos );
  2791. //This may be kind of wasteful, especially on long throws... use larger steps? Divide the travelTime into a certain hard number of slices? Trace just to apex and down?
  2792. for ( elapsedTime = timeStep; elapsedTime < floor(travelTime)+timeStep; elapsedTime += timeStep )
  2793. {
  2794. if ( (float)elapsedTime > travelTime )
  2795. {//cap it
  2796. elapsedTime = floor( travelTime );
  2797. }
  2798. EvaluateTrajectory( &tr, level.time + elapsedTime, testPos );
  2799. gi.trace( &trace, lastPos, mins, maxs, testPos, ignoreEntNum, clipmask );
  2800. if ( trace.allsolid || trace.startsolid )
  2801. {
  2802. blocked = qtrue;
  2803. break;
  2804. }
  2805. if ( trace.fraction < 1.0f )
  2806. {//hit something
  2807. if ( trace.entityNum == enemyNum )
  2808. {//hit the enemy, that's perfect!
  2809. break;
  2810. }
  2811. else if ( trace.plane.normal[2] > 0.7 && DistanceSquared( trace.endpos, target ) < 4096 )//hit within 64 of desired location, should be okay
  2812. {//close enough!
  2813. break;
  2814. }
  2815. else
  2816. {//FIXME: maybe find the extents of this brush and go above or below it on next try somehow?
  2817. impactDist = DistanceSquared( trace.endpos, target );
  2818. if ( impactDist < bestImpactDist )
  2819. {
  2820. bestImpactDist = impactDist;
  2821. VectorCopy( shotVel, failCase );
  2822. }
  2823. blocked = qtrue;
  2824. //see if we should store this as the failCase
  2825. if ( trace.entityNum < ENTITYNUM_WORLD )
  2826. {//hit an ent
  2827. traceEnt = &g_entities[trace.entityNum];
  2828. if ( traceEnt && traceEnt->takedamage && !OnSameTeam( self, traceEnt ) )
  2829. {//hit something breakable, so that's okay
  2830. //we haven't found a clear shot yet so use this as the failcase
  2831. VectorCopy( shotVel, failCase );
  2832. }
  2833. }
  2834. break;
  2835. }
  2836. }
  2837. if ( elapsedTime == floor( travelTime ) )
  2838. {//reached end, all clear
  2839. break;
  2840. }
  2841. else
  2842. {
  2843. //all clear, try next slice
  2844. VectorCopy( testPos, lastPos );
  2845. }
  2846. }
  2847. if ( blocked )
  2848. {//hit something, adjust speed (which will change arc)
  2849. hitCount++;
  2850. shotSpeed = idealSpeed + ((hitCount-skipNum) * speedInc);//from min to max (skipping ideal)
  2851. if ( hitCount >= skipNum )
  2852. {//skip ideal since that was the first value we tested
  2853. shotSpeed += speedInc;
  2854. }
  2855. }
  2856. else
  2857. {//made it!
  2858. break;
  2859. }
  2860. }
  2861. else
  2862. {//no need to check the path, go with first calc
  2863. break;
  2864. }
  2865. }
  2866. if ( hitCount >= maxHits )
  2867. {//NOTE: worst case scenario, use the one that impacted closest to the target (or just use the first try...?)
  2868. VectorCopy( failCase, velocity );
  2869. return qfalse;
  2870. }
  2871. VectorCopy( shotVel, velocity );
  2872. return qtrue;
  2873. }
  2874. //---------------------------------------------------------
  2875. void WP_ThermalThink( gentity_t *ent )
  2876. //---------------------------------------------------------
  2877. {
  2878. int count;
  2879. qboolean blow = qfalse;
  2880. // Thermal detonators for the player do occasional radius checks and blow up if there are entities in the blast radius
  2881. // This is done so that the main fire is actually useful as an attack. We explode anyway after delay expires.
  2882. if ( (ent->s.eFlags&EF_HELD_BY_SAND_CREATURE) )
  2883. {//blow once creature is underground (done with anim)
  2884. //FIXME: chance of being spit out? Especially if lots of delay left...
  2885. ent->e_TouchFunc = NULL;//don't impact on anything
  2886. if ( !ent->activator
  2887. || !ent->activator->client
  2888. || !ent->activator->client->ps.legsAnimTimer )
  2889. {//either something happened to the sand creature or it's done with it's attack anim
  2890. //blow!
  2891. ent->e_ThinkFunc = thinkF_thermalDetonatorExplode;
  2892. ent->nextthink = level.time + Q_irand( 50, 2000 );
  2893. }
  2894. else
  2895. {//keep checking
  2896. ent->nextthink = level.time + TD_THINK_TIME;
  2897. }
  2898. return;
  2899. }
  2900. else if ( ent->delay > level.time )
  2901. {
  2902. // Finally, we force it to bounce at least once before doing the special checks, otherwise it's just too easy for the player?
  2903. if ( ent->has_bounced )
  2904. {
  2905. count = G_RadiusList( ent->currentOrigin, TD_TEST_RAD, ent, qtrue, ent_list );
  2906. for ( int i = 0; i < count; i++ )
  2907. {
  2908. if ( ent_list[i]->s.number == 0 )
  2909. {
  2910. // avoid deliberately blowing up next to the player, no matter how close any enemy is..
  2911. // ...if the delay time expires though, there is no saving the player...muwhaaa haa ha
  2912. blow = qfalse;
  2913. break;
  2914. }
  2915. else if ( ent_list[i]->client
  2916. && ent_list[i]->client->NPC_class != CLASS_SAND_CREATURE//ignore sand creatures
  2917. && ent_list[i]->health > 0 )
  2918. {
  2919. //FIXME! sometimes the ent_list order changes, so we should make sure that the player isn't anywhere in this list
  2920. blow = qtrue;
  2921. }
  2922. }
  2923. }
  2924. }
  2925. else
  2926. {
  2927. // our death time has arrived, even if nothing is near us
  2928. blow = qtrue;
  2929. }
  2930. if ( blow )
  2931. {
  2932. ent->e_ThinkFunc = thinkF_thermalDetonatorExplode;
  2933. ent->nextthink = level.time + 50;
  2934. }
  2935. else
  2936. {
  2937. // we probably don't need to do this thinking logic very often...maybe this is fast enough?
  2938. ent->nextthink = level.time + TD_THINK_TIME;
  2939. }
  2940. }
  2941. //---------------------------------------------------------
  2942. gentity_t *WP_FireThermalDetonator( gentity_t *ent, qboolean alt_fire )
  2943. //---------------------------------------------------------
  2944. {
  2945. gentity_t *bolt;
  2946. vec3_t dir, start;
  2947. float damageScale = 1.0f;
  2948. VectorCopy( forward, dir );
  2949. VectorCopy( muzzle, start );
  2950. bolt = G_Spawn();
  2951. bolt->classname = "thermal_detonator";
  2952. if ( ent->s.number != 0 )
  2953. {
  2954. // If not the player, cut the damage a bit so we don't get pounded on so much
  2955. damageScale = TD_NPC_DAMAGE_CUT;
  2956. }
  2957. if ( !alt_fire && ent->s.number == 0 )
  2958. {
  2959. // Main fires for the players do a little bit of extra thinking
  2960. bolt->e_ThinkFunc = thinkF_WP_ThermalThink;
  2961. bolt->nextthink = level.time + TD_THINK_TIME;
  2962. bolt->delay = level.time + TD_TIME; // How long 'til she blows
  2963. }
  2964. else
  2965. {
  2966. bolt->e_ThinkFunc = thinkF_thermalDetonatorExplode;
  2967. bolt->nextthink = level.time + TD_TIME; // How long 'til she blows
  2968. }
  2969. bolt->mass = 10;
  2970. // How 'bout we give this thing a size...
  2971. VectorSet( bolt->mins, -4.0f, -4.0f, -4.0f );
  2972. VectorSet( bolt->maxs, 4.0f, 4.0f, 4.0f );
  2973. bolt->clipmask = MASK_SHOT;
  2974. bolt->clipmask &= ~CONTENTS_CORPSE;
  2975. bolt->contents = CONTENTS_SHOTCLIP;
  2976. bolt->takedamage = qtrue;
  2977. bolt->health = 15;
  2978. bolt->e_DieFunc = dieF_thermal_die;
  2979. WP_TraceSetStart( ent, start, bolt->mins, bolt->maxs );//make sure our start point isn't on the other side of a wall
  2980. float chargeAmount = 1.0f; // default of full charge
  2981. if ( ent->client )
  2982. {
  2983. chargeAmount = level.time - ent->client->ps.weaponChargeTime;
  2984. }
  2985. // get charge amount
  2986. chargeAmount = chargeAmount / (float)TD_VELOCITY;
  2987. if ( chargeAmount > 1.0f )
  2988. {
  2989. chargeAmount = 1.0f;
  2990. }
  2991. else if ( chargeAmount < TD_MIN_CHARGE )
  2992. {
  2993. chargeAmount = TD_MIN_CHARGE;
  2994. }
  2995. float thrownSpeed = TD_VELOCITY;
  2996. const qboolean thisIsAShooter = !Q_stricmp( "misc_weapon_shooter", ent->classname);
  2997. if (thisIsAShooter)
  2998. {
  2999. if (ent->delay != 0)
  3000. {
  3001. thrownSpeed = ent->delay;
  3002. }
  3003. }
  3004. // normal ones bounce, alt ones explode on impact
  3005. bolt->s.pos.trType = TR_GRAVITY;
  3006. bolt->owner = ent;
  3007. VectorScale( dir, thrownSpeed * chargeAmount, bolt->s.pos.trDelta );
  3008. if ( ent->health > 0 )
  3009. {
  3010. bolt->s.pos.trDelta[2] += 120;
  3011. if ( (ent->NPC || (ent->s.number && thisIsAShooter) )
  3012. && ent->enemy )
  3013. {//NPC or misc_weapon_shooter
  3014. //FIXME: we're assuming he's actually facing this direction...
  3015. vec3_t target;
  3016. VectorCopy( ent->enemy->currentOrigin, target );
  3017. if ( target[2] <= start[2] )
  3018. {
  3019. vec3_t vec;
  3020. VectorSubtract( target, start, vec );
  3021. VectorNormalize( vec );
  3022. VectorMA( target, Q_flrand( 0, -32 ), vec, target );//throw a little short
  3023. }
  3024. target[0] += Q_flrand( -5, 5 )+(crandom()*(6-ent->NPC->currentAim)*2);
  3025. target[1] += Q_flrand( -5, 5 )+(crandom()*(6-ent->NPC->currentAim)*2);
  3026. target[2] += Q_flrand( -5, 5 )+(crandom()*(6-ent->NPC->currentAim)*2);
  3027. WP_LobFire( ent, start, target, bolt->mins, bolt->maxs, bolt->clipmask, bolt->s.pos.trDelta, qtrue, ent->s.number, ent->enemy->s.number );
  3028. }
  3029. else if ( thisIsAShooter && ent->target && !VectorCompare( ent->pos1, vec3_origin ) )
  3030. {//misc_weapon_shooter firing at a position
  3031. WP_LobFire( ent, start, ent->pos1, bolt->mins, bolt->maxs, bolt->clipmask, bolt->s.pos.trDelta, qtrue, ent->s.number, ent->enemy->s.number );
  3032. }
  3033. }
  3034. if ( alt_fire )
  3035. {
  3036. bolt->alt_fire = qtrue;
  3037. }
  3038. else
  3039. {
  3040. bolt->s.eFlags |= EF_BOUNCE_HALF;
  3041. }
  3042. bolt->s.loopSound = G_SoundIndex( "sound/weapons/thermal/thermloop.wav" );
  3043. bolt->damage = TD_DAMAGE * damageScale;
  3044. bolt->dflags = 0;
  3045. bolt->splashDamage = TD_SPLASH_DAM * damageScale;
  3046. bolt->splashRadius = TD_SPLASH_RAD;
  3047. bolt->s.eType = ET_MISSILE;
  3048. bolt->svFlags = SVF_USE_CURRENT_ORIGIN;
  3049. bolt->s.weapon = WP_THERMAL;
  3050. if ( alt_fire )
  3051. {
  3052. bolt->methodOfDeath = MOD_THERMAL_ALT;
  3053. bolt->splashMethodOfDeath = MOD_THERMAL_ALT;//? SPLASH;
  3054. }
  3055. else
  3056. {
  3057. bolt->methodOfDeath = MOD_THERMAL;
  3058. bolt->splashMethodOfDeath = MOD_THERMAL;//? SPLASH;
  3059. }
  3060. bolt->s.pos.trTime = level.time; // move a bit on the very first frame
  3061. VectorCopy( start, bolt->s.pos.trBase );
  3062. SnapVector( bolt->s.pos.trDelta ); // save net bandwidth
  3063. VectorCopy (start, bolt->currentOrigin);
  3064. VectorCopy( start, bolt->pos2 );
  3065. return bolt;
  3066. }
  3067. //---------------------------------------------------------
  3068. gentity_t *WP_DropThermal( gentity_t *ent )
  3069. //---------------------------------------------------------
  3070. {
  3071. AngleVectors( ent->client->ps.viewangles, forward, vright, up );
  3072. CalcEntitySpot( ent, SPOT_WEAPON, muzzle );
  3073. return (WP_FireThermalDetonator( ent, qfalse ));
  3074. }
  3075. // Bot Laser
  3076. //---------------------------------------------------------
  3077. void WP_BotLaser( gentity_t *ent )
  3078. //---------------------------------------------------------
  3079. {
  3080. gentity_t *missile = CreateMissile( muzzle, forward, BRYAR_PISTOL_VEL, 10000, ent );
  3081. missile->classname = "bryar_proj";
  3082. missile->s.weapon = WP_BRYAR_PISTOL;
  3083. missile->damage = BRYAR_PISTOL_DAMAGE;
  3084. missile->dflags = DAMAGE_DEATH_KNOCKBACK;
  3085. missile->methodOfDeath = MOD_ENERGY;
  3086. missile->clipmask = MASK_SHOT | CONTENTS_LIGHTSABER;
  3087. }
  3088. // Emplaced Gun
  3089. //---------------------------------------------------------
  3090. void WP_EmplacedFire( gentity_t *ent )
  3091. //---------------------------------------------------------
  3092. {
  3093. float damage = EMPLACED_DAMAGE * ( ent->NPC ? 0.1f : 1.0f );
  3094. float vel = EMPLACED_VEL * ( ent->NPC ? 0.4f : 1.0f );
  3095. WP_MissileTargetHint(ent, muzzle, forward);
  3096. gentity_t *missile = CreateMissile( muzzle, forward, vel, 10000, ent );
  3097. missile->classname = "emplaced_proj";
  3098. missile->s.weapon = WP_EMPLACED_GUN;
  3099. missile->damage = damage;
  3100. missile->dflags = DAMAGE_DEATH_KNOCKBACK | DAMAGE_HEAVY_WEAP_CLASS;
  3101. missile->methodOfDeath = MOD_EMPLACED;
  3102. missile->clipmask = MASK_SHOT | CONTENTS_LIGHTSABER;
  3103. // do some weird switchery on who the real owner is, we do this so the projectiles don't hit the gun object
  3104. if ( ent && ent->client && !(ent->client->ps.eFlags&EF_LOCKED_TO_WEAPON) )
  3105. {
  3106. missile->owner = ent;
  3107. }
  3108. else
  3109. {
  3110. missile->owner = ent->owner;
  3111. }
  3112. if ( missile->owner->e_UseFunc == useF_eweb_use )
  3113. {
  3114. missile->alt_fire = qtrue;
  3115. }
  3116. VectorSet( missile->maxs, EMPLACED_SIZE, EMPLACED_SIZE, EMPLACED_SIZE );
  3117. VectorScale( missile->maxs, -1, missile->mins );
  3118. // alternate muzzles
  3119. ent->fxID = !ent->fxID;
  3120. }
  3121. // ATST Main
  3122. //---------------------------------------------------------
  3123. void WP_ATSTMainFire( gentity_t *ent )
  3124. //---------------------------------------------------------
  3125. {
  3126. float vel = ATST_MAIN_VEL;
  3127. // if ( ent->client && (ent->client->ps.eFlags & EF_IN_ATST ))
  3128. // {
  3129. // vel = 4500.0f;
  3130. // }
  3131. if ( !ent->s.number )
  3132. {
  3133. // player shoots faster
  3134. vel *= 1.6f;
  3135. }
  3136. WP_MissileTargetHint(ent, muzzle, forward);
  3137. gentity_t *missile = CreateMissile( muzzle, forward, vel, 10000, ent );
  3138. missile->classname = "atst_main_proj";
  3139. missile->s.weapon = WP_ATST_MAIN;
  3140. missile->damage = ATST_MAIN_DAMAGE;
  3141. missile->dflags = DAMAGE_DEATH_KNOCKBACK|DAMAGE_HEAVY_WEAP_CLASS;
  3142. missile->methodOfDeath = MOD_ENERGY;
  3143. missile->clipmask = MASK_SHOT | CONTENTS_LIGHTSABER;
  3144. missile->owner = ent;
  3145. VectorSet( missile->maxs, ATST_MAIN_SIZE, ATST_MAIN_SIZE, ATST_MAIN_SIZE );
  3146. VectorScale( missile->maxs, -1, missile->mins );
  3147. }
  3148. // ATST Alt Side
  3149. //---------------------------------------------------------
  3150. void WP_ATSTSideAltFire( gentity_t *ent )
  3151. //---------------------------------------------------------
  3152. {
  3153. int damage = ATST_SIDE_ALT_DAMAGE;
  3154. float vel = ATST_SIDE_ALT_NPC_VELOCITY;
  3155. if ( ent->client && (ent->client->ps.eFlags & EF_IN_ATST ))
  3156. {
  3157. vel = ATST_SIDE_ALT_VELOCITY;
  3158. }
  3159. gentity_t *missile = CreateMissile( muzzle, forward, vel, 10000, ent, qtrue );
  3160. missile->classname = "atst_rocket";
  3161. missile->s.weapon = WP_ATST_SIDE;
  3162. missile->mass = 10;
  3163. // Do the damages
  3164. if ( ent->s.number != 0 )
  3165. {
  3166. if ( g_spskill->integer == 0 )
  3167. {
  3168. damage = ATST_SIDE_ROCKET_NPC_DAMAGE_EASY;
  3169. }
  3170. else if ( g_spskill->integer == 1 )
  3171. {
  3172. damage = ATST_SIDE_ROCKET_NPC_DAMAGE_NORMAL;
  3173. }
  3174. else
  3175. {
  3176. damage = ATST_SIDE_ROCKET_NPC_DAMAGE_HARD;
  3177. }
  3178. }
  3179. VectorCopy( forward, missile->movedir );
  3180. // Make it easier to hit things
  3181. VectorSet( missile->maxs, ATST_SIDE_ALT_ROCKET_SIZE, ATST_SIDE_ALT_ROCKET_SIZE, ATST_SIDE_ALT_ROCKET_SIZE );
  3182. VectorScale( missile->maxs, -1, missile->mins );
  3183. missile->damage = damage;
  3184. missile->dflags = DAMAGE_DEATH_KNOCKBACK | DAMAGE_HEAVY_WEAP_CLASS;
  3185. missile->methodOfDeath = MOD_EXPLOSIVE;
  3186. missile->splashMethodOfDeath = MOD_EXPLOSIVE_SPLASH;
  3187. missile->clipmask = MASK_SHOT | CONTENTS_LIGHTSABER;
  3188. // Scale damage down a bit if it is coming from an NPC
  3189. missile->splashDamage = ATST_SIDE_ALT_SPLASH_DAMAGE * ( ent->s.number == 0 ? 1.0f : ATST_SIDE_ALT_ROCKET_SPLASH_SCALE );
  3190. missile->splashRadius = ATST_SIDE_ALT_SPLASH_RADIUS;
  3191. // we don't want it to ever bounce
  3192. missile->bounceCount = 0;
  3193. }
  3194. // ATST Side
  3195. //---------------------------------------------------------
  3196. void WP_ATSTSideFire( gentity_t *ent )
  3197. //---------------------------------------------------------
  3198. {
  3199. int damage = ATST_SIDE_MAIN_DAMAGE;
  3200. gentity_t *missile = CreateMissile( muzzle, forward, ATST_SIDE_MAIN_VELOCITY, 10000, ent, qfalse );
  3201. missile->classname = "atst_side_proj";
  3202. missile->s.weapon = WP_ATST_SIDE;
  3203. // Do the damages
  3204. if ( ent->s.number != 0 )
  3205. {
  3206. if ( g_spskill->integer == 0 )
  3207. {
  3208. damage = ATST_SIDE_MAIN_NPC_DAMAGE_EASY;
  3209. }
  3210. else if ( g_spskill->integer == 1 )
  3211. {
  3212. damage = ATST_SIDE_MAIN_NPC_DAMAGE_NORMAL;
  3213. }
  3214. else
  3215. {
  3216. damage = ATST_SIDE_MAIN_NPC_DAMAGE_HARD;
  3217. }
  3218. }
  3219. VectorSet( missile->maxs, ATST_SIDE_MAIN_SIZE, ATST_SIDE_MAIN_SIZE, ATST_SIDE_MAIN_SIZE );
  3220. VectorScale( missile->maxs, -1, missile->mins );
  3221. missile->damage = damage;
  3222. missile->dflags = DAMAGE_DEATH_KNOCKBACK|DAMAGE_HEAVY_WEAP_CLASS;
  3223. missile->methodOfDeath = MOD_ENERGY;
  3224. missile->clipmask = MASK_SHOT | CONTENTS_LIGHTSABER;
  3225. missile->splashDamage = ATST_SIDE_MAIN_SPLASH_DAMAGE * ( ent->s.number == 0 ? 1.0f : 0.6f );
  3226. missile->splashRadius = ATST_SIDE_MAIN_SPLASH_RADIUS;
  3227. // we don't want it to bounce
  3228. missile->bounceCount = 0;
  3229. }
  3230. //---------------------------------------------------------
  3231. void WP_FireStunBaton( gentity_t *ent, qboolean alt_fire )
  3232. {
  3233. gentity_t *tr_ent;
  3234. trace_t tr;
  3235. vec3_t mins, maxs, end, start;
  3236. G_Sound( ent, G_SoundIndex( "sound/weapons/baton/fire" ));
  3237. #ifdef _IMMERSION
  3238. G_Force( ent, G_ForceIndex( "fffx/weapons/baton/fire", FF_CHANNEL_WEAPON ) );
  3239. #endif // _IMMERSION
  3240. VectorCopy( muzzle, start );
  3241. WP_TraceSetStart( ent, start, vec3_origin, vec3_origin );
  3242. VectorMA( start, STUN_BATON_RANGE, forward, end );
  3243. VectorSet( maxs, 5, 5, 5 );
  3244. VectorScale( maxs, -1, mins );
  3245. gi.trace ( &tr, start, mins, maxs, end, ent->s.number, CONTENTS_SOLID|CONTENTS_BODY|CONTENTS_SHOTCLIP );
  3246. if ( tr.entityNum >= ENTITYNUM_WORLD || tr.entityNum < 0 )
  3247. {
  3248. return;
  3249. }
  3250. tr_ent = &g_entities[tr.entityNum];
  3251. if ( tr_ent && tr_ent->takedamage && tr_ent->client )
  3252. {
  3253. G_PlayEffect( "stunBaton/flesh_impact", tr.endpos, tr.plane.normal );
  3254. // TEMP!
  3255. // G_Sound( tr_ent, G_SoundIndex( va("sound/weapons/melee/punch%d", Q_irand(1, 4)) ) );
  3256. tr_ent->client->ps.powerups[PW_SHOCKED] = level.time + 1500;
  3257. G_Damage( tr_ent, ent, ent, forward, tr.endpos, STUN_BATON_DAMAGE, DAMAGE_NO_KNOCKBACK, MOD_MELEE );
  3258. }
  3259. else if ( tr_ent->svFlags & SVF_GLASS_BRUSH || ( tr_ent->svFlags & SVF_BBRUSH && tr_ent->material == 12 )) // material grate...we are breaking a grate!
  3260. {
  3261. G_Damage( tr_ent, ent, ent, forward, tr.endpos, 999, DAMAGE_NO_KNOCKBACK, MOD_MELEE ); // smash that puppy
  3262. }
  3263. }
  3264. void WP_Melee( gentity_t *ent )
  3265. //---------------------------------------------------------
  3266. {
  3267. gentity_t *tr_ent;
  3268. trace_t tr;
  3269. vec3_t mins, maxs, end;
  3270. int damage = ent->s.number ? (g_spskill->integer*2)+1 : 3;
  3271. float range = ent->s.number ? 64 : 32;
  3272. VectorMA( muzzle, range, forward, end );
  3273. VectorSet( maxs, 6, 6, 6 );
  3274. VectorScale( maxs, -1, mins );
  3275. gi.trace ( &tr, muzzle, mins, maxs, end, ent->s.number, MASK_SHOT );
  3276. if ( tr.entityNum >= ENTITYNUM_WORLD )
  3277. {
  3278. return;
  3279. }
  3280. tr_ent = &g_entities[tr.entityNum];
  3281. if ( ent->client && !PM_DroidMelee( ent->client->NPC_class ) )
  3282. {
  3283. if ( ent->s.number || ent->alt_fire )
  3284. {
  3285. damage *= Q_irand( 2, 3 );
  3286. }
  3287. else
  3288. {
  3289. damage *= Q_irand( 1, 2 );
  3290. }
  3291. }
  3292. if ( tr_ent && tr_ent->takedamage )
  3293. {
  3294. int dflags = DAMAGE_NO_KNOCKBACK;
  3295. G_PlayEffect( G_EffectIndex( "melee/punch_impact" ), tr.endpos, forward );
  3296. //G_Sound( tr_ent, G_SoundIndex( va("sound/weapons/melee/punch%d", Q_irand(1, 4)) ) );
  3297. if ( ent->NPC && (ent->NPC->aiFlags&NPCAI_HEAVY_MELEE) )
  3298. { //4x damage for heavy melee class
  3299. damage *= 4;
  3300. dflags &= ~DAMAGE_NO_KNOCKBACK;
  3301. dflags |= DAMAGE_DISMEMBER;
  3302. }
  3303. G_Damage( tr_ent, ent, ent, forward, tr.endpos, damage, dflags, MOD_MELEE );
  3304. }
  3305. }
  3306. //---------------------------------------------------------
  3307. static void WP_FireTuskenRifle( gentity_t *ent )
  3308. //---------------------------------------------------------
  3309. {
  3310. vec3_t start;
  3311. VectorCopy( muzzle, start );
  3312. WP_TraceSetStart( ent, start, vec3_origin, vec3_origin );//make sure our start point isn't on the other side of a wall
  3313. if ( !(ent->client->ps.forcePowersActive&(1<<FP_SEE))
  3314. || ent->client->ps.forcePowerLevel[FP_SEE] < FORCE_LEVEL_2 )
  3315. {//force sight 2+ gives perfect aim
  3316. //FIXME: maybe force sight level 3 autoaims some?
  3317. if ( ent->NPC && ent->NPC->currentAim < 5 )
  3318. {
  3319. vec3_t angs;
  3320. vectoangles( forward, angs );
  3321. if ( ent->client->NPC_class == CLASS_IMPWORKER )
  3322. {//*sigh*, hack to make impworkers less accurate without affecteing imperial officer accuracy
  3323. angs[PITCH] += ( crandom() * (BLASTER_NPC_SPREAD+(6-ent->NPC->currentAim)*0.25f));//was 0.5f
  3324. angs[YAW] += ( crandom() * (BLASTER_NPC_SPREAD+(6-ent->NPC->currentAim)*0.25f));//was 0.5f
  3325. }
  3326. else
  3327. {
  3328. angs[PITCH] += ( crandom() * ((5-ent->NPC->currentAim)*0.25f) );
  3329. angs[YAW] += ( crandom() * ((5-ent->NPC->currentAim)*0.25f) );
  3330. }
  3331. AngleVectors( angs, forward, NULL, NULL );
  3332. }
  3333. }
  3334. WP_MissileTargetHint(ent, start, forward);
  3335. gentity_t *missile = CreateMissile( start, forward, TUSKEN_RIFLE_VEL, 10000, ent, qfalse );
  3336. missile->classname = "trifle_proj";
  3337. missile->s.weapon = WP_TUSKEN_RIFLE;
  3338. if ( ent->s.number < MAX_CLIENTS || g_spskill->integer >= 2 )
  3339. {
  3340. missile->damage = TUSKEN_RIFLE_DAMAGE_HARD;
  3341. }
  3342. else if ( g_spskill->integer > 0 )
  3343. {
  3344. missile->damage = TUSKEN_RIFLE_DAMAGE_MEDIUM;
  3345. }
  3346. else
  3347. {
  3348. missile->damage = TUSKEN_RIFLE_DAMAGE_EASY;
  3349. }
  3350. missile->dflags = DAMAGE_DEATH_KNOCKBACK;
  3351. missile->methodOfDeath = MOD_BRYAR;//???
  3352. missile->clipmask = MASK_SHOT | CONTENTS_LIGHTSABER;
  3353. // we don't want it to bounce forever
  3354. missile->bounceCount = 8;
  3355. }
  3356. //---------------------------------------------------------
  3357. static void WP_FireNoghriStick( gentity_t *ent )
  3358. //---------------------------------------------------------
  3359. {
  3360. vec3_t dir, angs;
  3361. vectoangles( forward, angs );
  3362. if ( !(ent->client->ps.forcePowersActive&(1<<FP_SEE))
  3363. || ent->client->ps.forcePowerLevel[FP_SEE] < FORCE_LEVEL_2 )
  3364. {//force sight 2+ gives perfect aim
  3365. //FIXME: maybe force sight level 3 autoaims some?
  3366. // add some slop to the main-fire direction
  3367. angs[PITCH] += ( crandom() * (BLASTER_NPC_SPREAD+(6-ent->NPC->currentAim)*0.25f));//was 0.5f
  3368. angs[YAW] += ( crandom() * (BLASTER_NPC_SPREAD+(6-ent->NPC->currentAim)*0.25f));//was 0.5f
  3369. }
  3370. AngleVectors( angs, dir, NULL, NULL );
  3371. // FIXME: if temp_org does not have clear trace to inside the bbox, don't shoot!
  3372. int velocity = 1200;
  3373. WP_TraceSetStart( ent, muzzle, vec3_origin, vec3_origin );//make sure our start point isn't on the other side of a wall
  3374. WP_MissileTargetHint(ent, muzzle, dir);
  3375. gentity_t *missile = CreateMissile( muzzle, dir, velocity, 10000, ent, qfalse );
  3376. missile->classname = "noghri_proj";
  3377. missile->s.weapon = WP_NOGHRI_STICK;
  3378. // Do the damages
  3379. if ( ent->s.number != 0 )
  3380. {
  3381. if ( g_spskill->integer == 0 )
  3382. {
  3383. missile->damage = 1;
  3384. }
  3385. else if ( g_spskill->integer == 1 )
  3386. {
  3387. missile->damage = 5;
  3388. }
  3389. else
  3390. {
  3391. missile->damage = 10;
  3392. }
  3393. }
  3394. // if ( ent->client )
  3395. // {
  3396. // if ( ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > 0 && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > cg.time )
  3397. // {
  3398. // // in overcharge mode, so doing double damage
  3399. // missile->flags |= FL_OVERCHARGED;
  3400. // damage *= 2;
  3401. // }
  3402. // }
  3403. missile->dflags = DAMAGE_NO_KNOCKBACK;
  3404. missile->methodOfDeath = MOD_BLASTER;
  3405. missile->clipmask = MASK_SHOT | CONTENTS_LIGHTSABER;
  3406. missile->splashDamage = 0;
  3407. missile->splashRadius = 100;
  3408. missile->splashMethodOfDeath = MOD_GAS;
  3409. //Hmm: maybe spew gas on impact?
  3410. }
  3411. //---------------------------------------------------------
  3412. void AddLeanOfs(const gentity_t *const ent, vec3_t point)
  3413. //---------------------------------------------------------
  3414. {
  3415. if(ent->client)
  3416. {
  3417. if(ent->client->ps.leanofs)
  3418. {
  3419. vec3_t right;
  3420. //add leaning offset
  3421. AngleVectors(ent->client->ps.viewangles, NULL, right, NULL);
  3422. VectorMA(point, (float)ent->client->ps.leanofs, right, point);
  3423. }
  3424. }
  3425. }
  3426. //---------------------------------------------------------
  3427. void SubtractLeanOfs(const gentity_t *const ent, vec3_t point)
  3428. //---------------------------------------------------------
  3429. {
  3430. if(ent->client)
  3431. {
  3432. if(ent->client->ps.leanofs)
  3433. {
  3434. vec3_t right;
  3435. //add leaning offset
  3436. AngleVectors( ent->client->ps.viewangles, NULL, right, NULL );
  3437. VectorMA( point, ent->client->ps.leanofs*-1, right, point );
  3438. }
  3439. }
  3440. }
  3441. //---------------------------------------------------------
  3442. void ViewHeightFix(const gentity_t *const ent)
  3443. //---------------------------------------------------------
  3444. {
  3445. //FIXME: this is hacky and doesn't need to be here. Was only put here to make up
  3446. //for the times a crouch anim would be used but not actually crouching.
  3447. //When we start calcing eyepos (SPOT_HEAD) from the tag_eyes, we won't need
  3448. //this (or viewheight at all?)
  3449. if ( !ent )
  3450. return;
  3451. if ( !ent->client || !ent->NPC )
  3452. return;
  3453. if ( ent->client->ps.stats[STAT_HEALTH] <= 0 )
  3454. return;//dead
  3455. if ( ent->client->ps.legsAnim == BOTH_CROUCH1IDLE || ent->client->ps.legsAnim == BOTH_CROUCH1 || ent->client->ps.legsAnim == BOTH_CROUCH1WALK )
  3456. {
  3457. if ( ent->client->ps.viewheight!=ent->client->crouchheight + STANDARD_VIEWHEIGHT_OFFSET )
  3458. ent->client->ps.viewheight = ent->client->crouchheight + STANDARD_VIEWHEIGHT_OFFSET;
  3459. }
  3460. else
  3461. {
  3462. if ( ent->client->ps.viewheight!=ent->client->standheight + STANDARD_VIEWHEIGHT_OFFSET )
  3463. ent->client->ps.viewheight = ent->client->standheight + STANDARD_VIEWHEIGHT_OFFSET;
  3464. }
  3465. }
  3466. qboolean W_AccuracyLoggableWeapon( int weapon, qboolean alt_fire, int mod )
  3467. {
  3468. if ( mod != MOD_UNKNOWN )
  3469. {
  3470. switch( mod )
  3471. {
  3472. //standard weapons
  3473. case MOD_BRYAR:
  3474. case MOD_BRYAR_ALT:
  3475. case MOD_BLASTER:
  3476. case MOD_BLASTER_ALT:
  3477. case MOD_DISRUPTOR:
  3478. case MOD_SNIPER:
  3479. case MOD_BOWCASTER:
  3480. case MOD_BOWCASTER_ALT:
  3481. case MOD_ROCKET:
  3482. case MOD_ROCKET_ALT:
  3483. case MOD_CONC:
  3484. case MOD_CONC_ALT:
  3485. return qtrue;
  3486. break;
  3487. //non-alt standard
  3488. case MOD_REPEATER:
  3489. case MOD_DEMP2:
  3490. case MOD_FLECHETTE:
  3491. return qtrue;
  3492. break;
  3493. //emplaced gun
  3494. case MOD_EMPLACED:
  3495. return qtrue;
  3496. break;
  3497. //atst
  3498. case MOD_ENERGY:
  3499. case MOD_EXPLOSIVE:
  3500. if ( weapon == WP_ATST_MAIN || weapon == WP_ATST_SIDE )
  3501. {
  3502. return qtrue;
  3503. }
  3504. break;
  3505. }
  3506. }
  3507. else if ( weapon != WP_NONE )
  3508. {
  3509. switch( weapon )
  3510. {
  3511. case WP_BRYAR_PISTOL:
  3512. case WP_BLASTER_PISTOL:
  3513. case WP_BLASTER:
  3514. case WP_DISRUPTOR:
  3515. case WP_BOWCASTER:
  3516. case WP_ROCKET_LAUNCHER:
  3517. case WP_CONCUSSION:
  3518. return qtrue;
  3519. break;
  3520. //non-alt standard
  3521. case WP_REPEATER:
  3522. case WP_DEMP2:
  3523. case WP_FLECHETTE:
  3524. if ( !alt_fire )
  3525. {
  3526. return qtrue;
  3527. }
  3528. break;
  3529. //emplaced gun
  3530. case WP_EMPLACED_GUN:
  3531. return qtrue;
  3532. break;
  3533. //atst
  3534. case WP_ATST_MAIN:
  3535. case WP_ATST_SIDE:
  3536. return qtrue;
  3537. break;
  3538. }
  3539. }
  3540. return qfalse;
  3541. }
  3542. /*
  3543. ===============
  3544. LogAccuracyHit
  3545. ===============
  3546. */
  3547. qboolean LogAccuracyHit( gentity_t *target, gentity_t *attacker ) {
  3548. if( !target->takedamage ) {
  3549. return qfalse;
  3550. }
  3551. if ( target == attacker ) {
  3552. return qfalse;
  3553. }
  3554. if( !target->client ) {
  3555. return qfalse;
  3556. }
  3557. if( !attacker->client ) {
  3558. return qfalse;
  3559. }
  3560. if( target->client->ps.stats[STAT_HEALTH] <= 0 ) {
  3561. return qfalse;
  3562. }
  3563. if ( OnSameTeam( target, attacker ) ) {
  3564. return qfalse;
  3565. }
  3566. return qtrue;
  3567. }
  3568. //---------------------------------------------------------
  3569. void CalcMuzzlePoint( gentity_t *const ent, vec3_t forward, vec3_t right, vec3_t up, vec3_t muzzlePoint, float lead_in )
  3570. //---------------------------------------------------------
  3571. {
  3572. vec3_t org;
  3573. mdxaBone_t boltMatrix;
  3574. if( !lead_in ) //&& ent->s.number != 0
  3575. {//Not players or melee
  3576. if( ent->client )
  3577. {
  3578. if ( ent->client->renderInfo.mPCalcTime >= level.time - FRAMETIME*2 )
  3579. {//Our muzz point was calced no more than 2 frames ago
  3580. VectorCopy(ent->client->renderInfo.muzzlePoint, muzzlePoint);
  3581. return;
  3582. }
  3583. }
  3584. }
  3585. VectorCopy( ent->currentOrigin, muzzlePoint );
  3586. switch( ent->s.weapon )
  3587. {
  3588. case WP_BRYAR_PISTOL:
  3589. case WP_BLASTER_PISTOL:
  3590. ViewHeightFix(ent);
  3591. muzzlePoint[2] += ent->client->ps.viewheight;//By eyes
  3592. muzzlePoint[2] -= 16;
  3593. VectorMA( muzzlePoint, 28, forward, muzzlePoint );
  3594. VectorMA( muzzlePoint, 6, vright, muzzlePoint );
  3595. break;
  3596. case WP_ROCKET_LAUNCHER:
  3597. case WP_CONCUSSION:
  3598. case WP_THERMAL:
  3599. ViewHeightFix(ent);
  3600. muzzlePoint[2] += ent->client->ps.viewheight;//By eyes
  3601. muzzlePoint[2] -= 2;
  3602. break;
  3603. case WP_BLASTER:
  3604. ViewHeightFix(ent);
  3605. muzzlePoint[2] += ent->client->ps.viewheight;//By eyes
  3606. muzzlePoint[2] -= 1;
  3607. if ( ent->s.number == 0 )
  3608. VectorMA( muzzlePoint, 12, forward, muzzlePoint ); // player, don't set this any lower otherwise the projectile will impact immediately when your back is to a wall
  3609. else
  3610. VectorMA( muzzlePoint, 2, forward, muzzlePoint ); // NPC, don't set too far forward otherwise the projectile can go through doors
  3611. VectorMA( muzzlePoint, 1, vright, muzzlePoint );
  3612. break;
  3613. case WP_SABER:
  3614. if(ent->NPC!=NULL &&
  3615. (ent->client->ps.torsoAnim == TORSO_WEAPONREADY2 ||
  3616. ent->client->ps.torsoAnim == BOTH_ATTACK2))//Sniper pose
  3617. {
  3618. ViewHeightFix(ent);
  3619. muzzle[2] += ent->client->ps.viewheight;//By eyes
  3620. }
  3621. else
  3622. {
  3623. muzzlePoint[2] += 16;
  3624. }
  3625. VectorMA( muzzlePoint, 8, forward, muzzlePoint );
  3626. VectorMA( muzzlePoint, 16, vright, muzzlePoint );
  3627. break;
  3628. case WP_BOT_LASER:
  3629. muzzlePoint[2] -= 16; //
  3630. break;
  3631. case WP_ATST_MAIN:
  3632. if (ent->count > 0)
  3633. {
  3634. ent->count = 0;
  3635. gi.G2API_GetBoltMatrix( ent->ghoul2, ent->playerModel,
  3636. ent->handLBolt,
  3637. &boltMatrix, ent->s.angles, ent->s.origin, (cg.time?cg.time:level.time),
  3638. NULL, ent->s.modelScale );
  3639. }
  3640. else
  3641. {
  3642. ent->count = 1;
  3643. gi.G2API_GetBoltMatrix( ent->ghoul2, ent->playerModel,
  3644. ent->handRBolt,
  3645. &boltMatrix, ent->s.angles, ent->s.origin, (cg.time?cg.time:level.time),
  3646. NULL, ent->s.modelScale );
  3647. }
  3648. gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, org );
  3649. VectorCopy(org,muzzlePoint);
  3650. break;
  3651. }
  3652. AddLeanOfs(ent, muzzlePoint);
  3653. }
  3654. // Muzzle point table...
  3655. vec3_t WP_MuzzlePoint[WP_NUM_WEAPONS] =
  3656. {// Fwd, right, up.
  3657. {0, 0, 0 }, // WP_NONE,
  3658. {8 , 16, 0 }, // WP_SABER,
  3659. {12, 6, -6 }, // WP_BLASTER_PISTOL,
  3660. {12, 6, -6 }, // WP_BLASTER,
  3661. {12, 6, -6 }, // WP_DISRUPTOR,
  3662. {12, 2, -6 }, // WP_BOWCASTER,
  3663. {12, 4.5, -6 }, // WP_REPEATER,
  3664. {12, 6, -6 }, // WP_DEMP2,
  3665. {12, 6, -6 }, // WP_FLECHETTE,
  3666. {12, 8, -4 }, // WP_ROCKET_LAUNCHER,
  3667. {12, 0, -4 }, // WP_THERMAL,
  3668. {12, 0, -10 }, // WP_TRIP_MINE,
  3669. {12, 0, -4 }, // WP_DET_PACK,
  3670. {12, 8, -4 }, // WP_CONCUSSION,
  3671. {0 , 8, 0 }, // WP_MELEE,
  3672. {0, 0, 0 }, // WP_ATST_MAIN,
  3673. {0, 0, 0 }, // WP_ATST_SIDE,
  3674. {0 , 8, 0 }, // WP_STUN_BATON,
  3675. {12, 6, -6 }, // WP_BRYAR_PISTOL,
  3676. };
  3677. void WP_RocketLock( gentity_t *ent, float lockDist )
  3678. {
  3679. // Not really a charge weapon, but we still want to delay fire until the button comes up so that we can
  3680. // implement our alt-fire locking stuff
  3681. vec3_t ang;
  3682. trace_t tr;
  3683. vec3_t muzzleOffPoint, muzzlePoint, forward, right, up;
  3684. AngleVectors( ent->client->ps.viewangles, forward, right, up );
  3685. AngleVectors(ent->client->ps.viewangles, ang, NULL, NULL);
  3686. VectorCopy( ent->client->ps.origin, muzzlePoint );
  3687. VectorCopy(WP_MuzzlePoint[WP_ROCKET_LAUNCHER], muzzleOffPoint);
  3688. VectorMA(muzzlePoint, muzzleOffPoint[0], forward, muzzlePoint);
  3689. VectorMA(muzzlePoint, muzzleOffPoint[1], right, muzzlePoint);
  3690. muzzlePoint[2] += ent->client->ps.viewheight + muzzleOffPoint[2];
  3691. ang[0] = muzzlePoint[0] + ang[0]*lockDist;
  3692. ang[1] = muzzlePoint[1] + ang[1]*lockDist;
  3693. ang[2] = muzzlePoint[2] + ang[2]*lockDist;
  3694. gi.trace(&tr, muzzlePoint, NULL, NULL, ang, ent->client->ps.clientNum, MASK_PLAYERSOLID);
  3695. if (tr.fraction != 1 && tr.entityNum < ENTITYNUM_NONE && tr.entityNum != ent->client->ps.clientNum)
  3696. {
  3697. gentity_t *bgEnt = &g_entities[tr.entityNum];
  3698. if ( bgEnt && (bgEnt->s.powerups&PW_CLOAKED) )
  3699. {
  3700. ent->client->rocketLockIndex = ENTITYNUM_NONE;
  3701. ent->client->rocketLockTime = 0;
  3702. }
  3703. else if (bgEnt && bgEnt->s.eType == ET_PLAYER )
  3704. {
  3705. if (ent->client->rocketLockIndex == ENTITYNUM_NONE)
  3706. {
  3707. ent->client->rocketLockIndex = tr.entityNum;
  3708. ent->client->rocketLockTime = level.time;
  3709. }
  3710. else if (ent->client->rocketLockIndex != tr.entityNum && ent->client->rocketTargetTime < level.time)
  3711. {
  3712. ent->client->rocketLockIndex = tr.entityNum;
  3713. ent->client->rocketLockTime = level.time;
  3714. }
  3715. else if (ent->client->rocketLockIndex == tr.entityNum)
  3716. {
  3717. if (ent->client->rocketLockTime == -1)
  3718. {
  3719. ent->client->rocketLockTime = ent->client->rocketLastValidTime;
  3720. }
  3721. }
  3722. if (ent->client->rocketLockIndex == tr.entityNum)
  3723. {
  3724. ent->client->rocketTargetTime = level.time + 500;
  3725. }
  3726. }
  3727. }
  3728. else if (ent->client->rocketTargetTime < level.time)
  3729. {
  3730. ent->client->rocketLockIndex = ENTITYNUM_NONE;
  3731. ent->client->rocketLockTime = 0;
  3732. }
  3733. else
  3734. {
  3735. if (ent->client->rocketLockTime != -1)
  3736. {
  3737. ent->client->rocketLastValidTime = ent->client->rocketLockTime;
  3738. }
  3739. ent->client->rocketLockTime = -1;
  3740. }
  3741. }
  3742. #define VEH_HOMING_MISSILE_THINK_TIME 100
  3743. void WP_FireVehicleWeapon( gentity_t *ent, vec3_t start, vec3_t dir, vehWeaponInfo_t *vehWeapon )
  3744. {
  3745. if ( !vehWeapon )
  3746. {//invalid vehicle weapon
  3747. return;
  3748. }
  3749. else if ( vehWeapon->bIsProjectile )
  3750. {//projectile entity
  3751. gentity_t *missile;
  3752. vec3_t mins, maxs;
  3753. VectorSet( maxs, vehWeapon->fWidth/2.0f,vehWeapon->fWidth/2.0f,vehWeapon->fHeight/2.0f );
  3754. VectorScale( maxs, -1, mins );
  3755. //make sure our start point isn't on the other side of a wall
  3756. WP_TraceSetStart( ent, start, mins, maxs );
  3757. //QUERY: alt_fire true or not? Does it matter?
  3758. missile = CreateMissile( start, dir, vehWeapon->fSpeed, 10000, ent, qfalse );
  3759. if ( vehWeapon->bHasGravity )
  3760. {//TESTME: is this all we need to do?
  3761. missile->s.pos.trType = TR_GRAVITY;
  3762. }
  3763. missile->classname = "vehicle_proj";
  3764. missile->damage = vehWeapon->iDamage;
  3765. missile->splashDamage = vehWeapon->iSplashDamage;
  3766. missile->splashRadius = vehWeapon->fSplashRadius;
  3767. // HUGE HORRIBLE HACK
  3768. if (ent->owner && ent->owner->s.number==0)
  3769. {
  3770. missile->damage *= 20.0f;
  3771. missile->splashDamage *= 20.0f;
  3772. missile->splashRadius *= 20.0f;
  3773. }
  3774. //FIXME: externalize some of these properties?
  3775. missile->dflags = DAMAGE_DEATH_KNOCKBACK;
  3776. missile->clipmask = MASK_SHOT;
  3777. //Maybe by checking flags...?
  3778. if ( vehWeapon->bSaberBlockable )
  3779. {
  3780. missile->clipmask |= CONTENTS_LIGHTSABER;
  3781. }
  3782. /*
  3783. if ( (vehWeapon->iFlags&VWF_KNOCKBACK) )
  3784. {
  3785. missile->dflags &= ~DAMAGE_DEATH_KNOCKBACK;
  3786. }
  3787. if ( (vehWeapon->iFlags&VWF_DISTORTION_TRAIL) )
  3788. {
  3789. missile->s.eFlags |= EF_DISTORTION_TRAIL;
  3790. }
  3791. if ( (vehWeapon->iFlags&VWF_RADAR) )
  3792. {
  3793. missile->s.eFlags |= EF_RADAROBJECT;
  3794. }
  3795. */
  3796. missile->s.weapon = WP_BLASTER;//does this really matter?
  3797. // Make it easier to hit things
  3798. VectorCopy( mins, missile->mins );
  3799. VectorCopy( maxs, missile->maxs );
  3800. //some slightly different stuff for things with bboxes
  3801. if ( vehWeapon->fWidth || vehWeapon->fHeight )
  3802. {//we assume it's a rocket-like thing
  3803. missile->methodOfDeath = MOD_ROCKET;
  3804. missile->splashMethodOfDeath = MOD_ROCKET;// ?SPLASH;
  3805. // we don't want it to ever bounce
  3806. missile->bounceCount = 0;
  3807. missile->mass = 10;
  3808. }
  3809. else
  3810. {//a blaster-laser-like thing
  3811. missile->s.weapon = WP_BLASTER;//does this really matter?
  3812. missile->methodOfDeath = MOD_EMPLACED;//MOD_TURBLAST; //count as a heavy weap
  3813. missile->splashMethodOfDeath = MOD_EMPLACED;//MOD_TURBLAST;// ?SPLASH;
  3814. // we don't want it to bounce forever
  3815. missile->bounceCount = 8;
  3816. }
  3817. if ( vehWeapon->iHealth )
  3818. {//the missile can take damage
  3819. missile->health = vehWeapon->iHealth;
  3820. missile->takedamage = qtrue;
  3821. missile->contents = MASK_SHOT;
  3822. missile->e_DieFunc = dieF_WP_ExplosiveDie;//dieF_RocketDie;
  3823. }
  3824. //set veh as cgame side owner for purpose of fx overrides
  3825. if (ent->m_pVehicle && ent->m_pVehicle->m_pPilot)
  3826. {
  3827. missile->owner = ent->m_pVehicle->m_pPilot;
  3828. }
  3829. else
  3830. {
  3831. missile->owner = ent;
  3832. }
  3833. missile->s.otherEntityNum = ent->s.number;
  3834. if ( vehWeapon->iLifeTime )
  3835. {//expire after a time
  3836. if ( vehWeapon->bExplodeOnExpire )
  3837. {//blow up when your lifetime is up
  3838. missile->e_ThinkFunc = thinkF_WP_Explode;//FIXME: custom func?
  3839. }
  3840. else
  3841. {//just remove yourself
  3842. missile->e_ThinkFunc = thinkF_G_FreeEntity;//FIXME: custom func?
  3843. }
  3844. missile->nextthink = level.time + vehWeapon->iLifeTime;
  3845. }
  3846. if ( vehWeapon->fHoming )
  3847. {//homing missile
  3848. //crap, we need to set up the homing stuff like it is in MP...
  3849. WP_RocketLock( ent, 16384 );
  3850. if ( ent->client && ent->client->rocketLockIndex != ENTITYNUM_NONE )
  3851. {
  3852. int dif = 0;
  3853. float rTime;
  3854. rTime = ent->client->rocketLockTime;
  3855. if (rTime == -1)
  3856. {
  3857. rTime = ent->client->rocketLastValidTime;
  3858. }
  3859. if ( !vehWeapon->iLockOnTime )
  3860. {//no minimum lock-on time
  3861. dif = 10;//guaranteed lock-on
  3862. }
  3863. else
  3864. {
  3865. float lockTimeInterval = vehWeapon->iLockOnTime/16.0f;
  3866. dif = ( level.time - rTime ) / lockTimeInterval;
  3867. }
  3868. if (dif < 0)
  3869. {
  3870. dif = 0;
  3871. }
  3872. //It's 10 even though it locks client-side at 8, because we want them to have a sturdy lock first, and because there's a slight difference in time between server and client
  3873. if ( dif >= 10 && rTime != -1 )
  3874. {
  3875. missile->enemy = &g_entities[ent->client->rocketLockIndex];
  3876. if (missile->enemy && missile->enemy->client && missile->enemy->health > 0 && !OnSameTeam(ent, missile->enemy))
  3877. { //if enemy became invalid, died, or is on the same team, then don't seek it
  3878. missile->spawnflags |= 1;//just to let it know it should be faster... FIXME: EXTERNALIZE
  3879. missile->speed = vehWeapon->fSpeed;
  3880. missile->angle = vehWeapon->fHoming;
  3881. if ( vehWeapon->iLifeTime )
  3882. {//expire after a time
  3883. missile->disconnectDebounceTime = level.time + vehWeapon->iLifeTime;
  3884. missile->lockCount = (int)(vehWeapon->bExplodeOnExpire);
  3885. }
  3886. missile->e_ThinkFunc = thinkF_rocketThink;
  3887. missile->nextthink = level.time + VEH_HOMING_MISSILE_THINK_TIME;
  3888. //FIXME: implement radar in SP?
  3889. //missile->s.eFlags |= EF_RADAROBJECT;
  3890. }
  3891. }
  3892. ent->client->rocketLockIndex = ENTITYNUM_NONE;
  3893. ent->client->rocketLockTime = 0;
  3894. ent->client->rocketTargetTime = 0;
  3895. VectorCopy( dir, missile->movedir );
  3896. missile->random = 1.0f;//FIXME: externalize?
  3897. }
  3898. }
  3899. }
  3900. else
  3901. {//traceline
  3902. //FIXME: implement
  3903. }
  3904. }
  3905. //---------------------------------------------------------
  3906. void FireVehicleWeapon( gentity_t *ent, qboolean alt_fire )
  3907. //---------------------------------------------------------
  3908. {
  3909. Vehicle_t *pVeh = ent->m_pVehicle;
  3910. if ( !pVeh )
  3911. {
  3912. return;
  3913. }
  3914. if (pVeh->m_iRemovedSurfaces)
  3915. { //can't fire when the thing is breaking apart
  3916. return;
  3917. }
  3918. if (ent->owner && ent->owner->client && ent->owner->client->ps.weapon!=WP_NONE)
  3919. {
  3920. return;
  3921. }
  3922. // TODO?: If possible (probably not enough time), it would be nice if secondary fire was actually a mode switch/toggle
  3923. // so that, for instance, an x-wing can have 4-gun fire, or individual muzzle fire. If you wanted a different weapon, you
  3924. // would actually have to press the 2 key or something like that (I doubt I'd get a graphic for it anyways though). -AReis
  3925. // If this is not the alternate fire, fire a normal blaster shot...
  3926. if ( true )//(pVeh->m_ulFlags & VEH_FLYING) || (pVeh->m_ulFlags & VEH_WINGSOPEN) ) // NOTE: Wings open also denotes that it has already launched.
  3927. {
  3928. int weaponNum = 0, vehWeaponIndex = VEH_WEAPON_NONE;
  3929. int delay = 1000;
  3930. qboolean aimCorrect = qfalse;
  3931. qboolean linkedFiring = qfalse;
  3932. if ( !alt_fire )
  3933. {
  3934. weaponNum = 0;
  3935. }
  3936. else
  3937. {
  3938. weaponNum = 1;
  3939. }
  3940. vehWeaponIndex = pVeh->m_pVehicleInfo->weapon[weaponNum].ID;
  3941. delay = pVeh->m_pVehicleInfo->weapon[weaponNum].delay;
  3942. aimCorrect = pVeh->m_pVehicleInfo->weapon[weaponNum].aimCorrect;
  3943. if ( pVeh->m_pVehicleInfo->weapon[weaponNum].linkable == 1//optionally linkable
  3944. && pVeh->weaponStatus[weaponNum].linked )//linked
  3945. {//we're linking the primary or alternate weapons, so we'll do *all* the muzzles
  3946. linkedFiring = qtrue;
  3947. }
  3948. if ( vehWeaponIndex <= VEH_WEAPON_BASE || vehWeaponIndex >= MAX_VEH_WEAPONS )
  3949. {//invalid vehicle weapon
  3950. return;
  3951. }
  3952. else
  3953. {
  3954. vehWeaponInfo_t *vehWeapon = &g_vehWeaponInfo[vehWeaponIndex];
  3955. int i, cumulativeDelay = 0;
  3956. for ( i = 0; i < MAX_VEHICLE_MUZZLES; i++ )
  3957. {
  3958. if ( pVeh->m_pVehicleInfo->weapMuzzle[i] != vehWeaponIndex )
  3959. {//this muzzle doesn't match the weapon we're trying to use
  3960. continue;
  3961. }
  3962. // Fire this muzzle.
  3963. if ( pVeh->m_iMuzzleTag[i] != -1 && pVeh->m_Muzzles[i].m_iMuzzleWait < level.time )
  3964. {
  3965. vec3_t start, dir;
  3966. // Prepare weapon delay.
  3967. if ( linkedFiring )
  3968. {//linked firing, add the delay up for each muzzle, then apply to all of them at the end
  3969. cumulativeDelay += delay;
  3970. }
  3971. else
  3972. {//normal delay - NOTE: always-linked muzzles use normal delay, not cumulative
  3973. pVeh->m_Muzzles[i].m_iMuzzleWait = level.time + delay;
  3974. }
  3975. if ( pVeh->weaponStatus[weaponNum].ammo < vehWeapon->iAmmoPerShot )
  3976. {//out of ammo!
  3977. }
  3978. else
  3979. {//have enough ammo to shoot
  3980. //take the ammo
  3981. pVeh->weaponStatus[weaponNum].ammo -= vehWeapon->iAmmoPerShot;
  3982. //do the firing
  3983. //FIXME: do we still need to calc the muzzle here in SP?
  3984. //WP_CalcVehMuzzle(ent, i);
  3985. VectorCopy( pVeh->m_Muzzles[i].m_vMuzzlePos, start );
  3986. VectorCopy( pVeh->m_Muzzles[i].m_vMuzzleDir, dir );
  3987. if ( aimCorrect )
  3988. {//auto-aim the missile at the crosshair
  3989. trace_t trace;
  3990. vec3_t end;
  3991. AngleVectors( pVeh->m_vOrientation, dir, NULL, NULL );
  3992. //VectorMA( ent->currentOrigin, 32768, dir, end );
  3993. VectorMA( ent->currentOrigin, 8192, dir, end );
  3994. gi.trace( &trace, ent->currentOrigin, vec3_origin, vec3_origin, end, ent->s.number, MASK_SHOT );
  3995. //if ( trace.fraction < 1.0f && !trace.allsolid && !trace.startsolid )
  3996. //bah, always point at end of trace
  3997. {
  3998. VectorSubtract( trace.endpos, start, dir );
  3999. VectorNormalize( dir );
  4000. }
  4001. // Mounted lazer cannon auto aiming at enemy
  4002. //-------------------------------------------
  4003. if (ent->enemy)
  4004. {
  4005. Vehicle_t* enemyVeh = G_IsRidingVehicle(ent->enemy);
  4006. // Don't Auto Aim At A Person Who Is Slide Breaking
  4007. if (!enemyVeh || !(enemyVeh->m_ulFlags&VEH_SLIDEBREAKING))
  4008. {
  4009. vec3_t dir2;
  4010. VectorSubtract( ent->enemy->currentOrigin, start, dir2 );
  4011. VectorNormalize( dir2 );
  4012. if (DotProduct(dir, dir2)>0.95f)
  4013. {
  4014. VectorCopy(dir2, dir);
  4015. }
  4016. }
  4017. }
  4018. }
  4019. //play the weapon's muzzle effect if we have one
  4020. if ( vehWeapon->iMuzzleFX )
  4021. {
  4022. G_PlayEffect( vehWeapon->iMuzzleFX, pVeh->m_Muzzles[i].m_vMuzzlePos, pVeh->m_Muzzles[i].m_vMuzzleDir );
  4023. }
  4024. WP_FireVehicleWeapon( ent, start, dir, vehWeapon );
  4025. }
  4026. if ( pVeh->weaponStatus[weaponNum].linked )//NOTE: we don't check linkedFiring here because that does *not* get set if the cannons are *always* linked
  4027. {//we're linking the weapon, so continue on and fire all appropriate muzzles
  4028. continue;
  4029. }
  4030. //ok, just break, we'll get in here again next frame and try the next muzzle...
  4031. break;
  4032. }
  4033. }
  4034. if ( cumulativeDelay )
  4035. {//we linked muzzles so we need to apply the cumulative delay now, to each of the linked muzzles
  4036. for ( i = 0; i < MAX_VEHICLE_MUZZLES; i++ )
  4037. {
  4038. if ( pVeh->m_pVehicleInfo->weapMuzzle[i] != vehWeaponIndex )
  4039. {//this muzzle doesn't match the weapon we're trying to use
  4040. continue;
  4041. }
  4042. //apply the cumulative delay
  4043. pVeh->m_Muzzles[i].m_iMuzzleWait = level.time + cumulativeDelay;
  4044. }
  4045. }
  4046. }
  4047. }
  4048. }
  4049. void WP_FireScepter( gentity_t *ent, qboolean alt_fire )
  4050. {//just a straight beam
  4051. int damage = 1;
  4052. vec3_t start, end;
  4053. trace_t tr;
  4054. gentity_t *traceEnt = NULL, *tent;
  4055. float shotRange = 8192;
  4056. qboolean render_impact = qtrue;
  4057. VectorCopy( muzzle, start );
  4058. WP_TraceSetStart( ent, start, vec3_origin, vec3_origin );
  4059. WP_MissileTargetHint(ent, start, forward);
  4060. VectorMA( start, shotRange, forward, end );
  4061. gi.trace( &tr, start, NULL, NULL, end, ent->s.number, MASK_SHOT, G2_RETURNONHIT, 10 );
  4062. traceEnt = &g_entities[tr.entityNum];
  4063. if ( tr.surfaceFlags & SURF_NOIMPACT )
  4064. {
  4065. render_impact = qfalse;
  4066. }
  4067. // always render a shot beam, doing this the old way because I don't much feel like overriding the effect.
  4068. tent = G_TempEntity( tr.endpos, EV_DISRUPTOR_MAIN_SHOT );
  4069. tent->svFlags |= SVF_BROADCAST;
  4070. VectorCopy( muzzle, tent->s.origin2 );
  4071. if ( render_impact )
  4072. {
  4073. if ( tr.entityNum < ENTITYNUM_WORLD && traceEnt->takedamage )
  4074. {
  4075. // Create a simple impact type mark that doesn't last long in the world
  4076. G_PlayEffect( G_EffectIndex( "disruptor/flesh_impact" ), tr.endpos, tr.plane.normal );
  4077. int hitLoc = G_GetHitLocFromTrace( &tr, MOD_DISRUPTOR );
  4078. G_Damage( traceEnt, ent, ent, forward, tr.endpos, damage, DAMAGE_EXTRA_KNOCKBACK, MOD_DISRUPTOR, hitLoc );
  4079. }
  4080. else
  4081. {
  4082. G_PlayEffect( G_EffectIndex( "disruptor/wall_impact" ), tr.endpos, tr.plane.normal );
  4083. }
  4084. }
  4085. /*
  4086. shotDist = shotRange * tr.fraction;
  4087. for ( dist = 0; dist < shotDist; dist += 64 )
  4088. {
  4089. //FIXME: on a really long shot, this could make a LOT of alerts in one frame...
  4090. VectorMA( start, dist, forward, spot );
  4091. AddSightEvent( ent, spot, 256, AEL_DISCOVERED, 50 );
  4092. }
  4093. VectorMA( start, shotDist-4, forward, spot );
  4094. AddSightEvent( ent, spot, 256, AEL_DISCOVERED, 50 );
  4095. */
  4096. }
  4097. extern Vehicle_t *G_IsRidingVehicle( gentity_t *ent );
  4098. //---------------------------------------------------------
  4099. void FireWeapon( gentity_t *ent, qboolean alt_fire )
  4100. //---------------------------------------------------------
  4101. {
  4102. float alert = 256;
  4103. Vehicle_t *pVeh = NULL;
  4104. // track shots taken for accuracy tracking.
  4105. ent->client->ps.persistant[PERS_ACCURACY_SHOTS]++;
  4106. // If this is a vehicle, fire it's weapon and we're done.
  4107. if ( ent && ent->client && ent->client->NPC_class == CLASS_VEHICLE )
  4108. {
  4109. FireVehicleWeapon( ent, alt_fire );
  4110. return;
  4111. }
  4112. // set aiming directions
  4113. if ( ent->s.weapon == WP_DISRUPTOR && alt_fire )
  4114. {
  4115. if ( ent->NPC )
  4116. {
  4117. //snipers must use the angles they actually did their shot trace with
  4118. AngleVectors( ent->lastAngles, forward, vright, up );
  4119. }
  4120. }
  4121. else if ( ent->s.weapon == WP_ATST_SIDE || ent->s.weapon == WP_ATST_MAIN )
  4122. {
  4123. vec3_t delta1, enemy_org1, muzzle1;
  4124. vec3_t angleToEnemy1;
  4125. VectorCopy( ent->client->renderInfo.muzzlePoint, muzzle1 );
  4126. if ( !ent->s.number )
  4127. {//player driving an AT-ST
  4128. //SIGH... because we can't anticipate alt-fire, must calc muzzle here and now
  4129. mdxaBone_t boltMatrix;
  4130. int bolt;
  4131. if ( ent->client->ps.weapon == WP_ATST_MAIN )
  4132. {//FIXME: alt_fire should fire both barrels, but slower?
  4133. if ( ent->alt_fire )
  4134. {
  4135. bolt = ent->handRBolt;
  4136. }
  4137. else
  4138. {
  4139. bolt = ent->handLBolt;
  4140. }
  4141. }
  4142. else
  4143. {// ATST SIDE weapons
  4144. if ( ent->alt_fire )
  4145. {
  4146. if ( gi.G2API_GetSurfaceRenderStatus( &ent->ghoul2[ent->playerModel], "head_light_blaster_cann" ) )
  4147. {//don't have it!
  4148. return;
  4149. }
  4150. bolt = ent->genericBolt2;
  4151. }
  4152. else
  4153. {
  4154. if ( gi.G2API_GetSurfaceRenderStatus( &ent->ghoul2[ent->playerModel], "head_concussion_charger" ) )
  4155. {//don't have it!
  4156. return;
  4157. }
  4158. bolt = ent->genericBolt1;
  4159. }
  4160. }
  4161. vec3_t yawOnlyAngles = {0, ent->currentAngles[YAW], 0};
  4162. if ( ent->currentAngles[YAW] != ent->client->ps.legsYaw )
  4163. {
  4164. yawOnlyAngles[YAW] = ent->client->ps.legsYaw;
  4165. }
  4166. gi.G2API_GetBoltMatrix( ent->ghoul2, ent->playerModel, bolt, &boltMatrix, yawOnlyAngles, ent->currentOrigin, (cg.time?cg.time:level.time), NULL, ent->s.modelScale );
  4167. // work the matrix axis stuff into the original axis and origins used.
  4168. gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, ent->client->renderInfo.muzzlePoint );
  4169. gi.G2API_GiveMeVectorFromMatrix( boltMatrix, NEGATIVE_Y, ent->client->renderInfo.muzzleDir );
  4170. ent->client->renderInfo.mPCalcTime = level.time;
  4171. AngleVectors( ent->client->ps.viewangles, forward, vright, up );
  4172. //CalcMuzzlePoint( ent, forward, vright, up, muzzle, 0 );
  4173. }
  4174. else if ( !ent->enemy )
  4175. {//an NPC with no enemy to auto-aim at
  4176. VectorCopy( ent->client->renderInfo.muzzleDir, forward );
  4177. }
  4178. else
  4179. {//NPC, auto-aim at enemy
  4180. CalcEntitySpot( ent->enemy, SPOT_HEAD, enemy_org1 );
  4181. VectorSubtract (enemy_org1, muzzle1, delta1);
  4182. vectoangles ( delta1, angleToEnemy1 );
  4183. AngleVectors (angleToEnemy1, forward, vright, up);
  4184. }
  4185. }
  4186. else if ( ent->s.weapon == WP_BOT_LASER && ent->enemy )
  4187. {
  4188. vec3_t delta1, enemy_org1, muzzle1;
  4189. vec3_t angleToEnemy1;
  4190. CalcEntitySpot( ent->enemy, SPOT_HEAD, enemy_org1 );
  4191. CalcEntitySpot( ent, SPOT_WEAPON, muzzle1 );
  4192. VectorSubtract (enemy_org1, muzzle1, delta1);
  4193. vectoangles ( delta1, angleToEnemy1 );
  4194. AngleVectors (angleToEnemy1, forward, vright, up);
  4195. }
  4196. else
  4197. {
  4198. if ( (pVeh = G_IsRidingVehicle( ent )) != NULL) //riding a vehicle
  4199. {//use our muzzleDir, can't use viewangles or vehicle m_vOrientation because we may be animated to shoot left or right...
  4200. if ((ent->s.eFlags&EF_NODRAW))//we're inside it
  4201. {
  4202. vec3_t aimAngles;
  4203. VectorCopy( ent->client->renderInfo.muzzleDir, forward );
  4204. vectoangles( forward, aimAngles );
  4205. //we're only keeping the yaw
  4206. aimAngles[PITCH] = ent->client->ps.viewangles[PITCH];
  4207. aimAngles[ROLL] = 0;
  4208. AngleVectors( aimAngles, forward, vright, up );
  4209. }
  4210. else
  4211. {
  4212. vec3_t actorRight;
  4213. vec3_t actorFwd;
  4214. VectorCopy( ent->client->renderInfo.muzzlePoint, muzzle );
  4215. AngleVectors(ent->currentAngles, actorFwd, actorRight, 0);
  4216. // Aiming Left
  4217. //-------------
  4218. if (ent->client->ps.torsoAnim==BOTH_VT_ATL_G || ent->client->ps.torsoAnim==BOTH_VS_ATL_G)
  4219. {
  4220. VectorScale(actorRight, -1.0f, forward);
  4221. }
  4222. // Aiming Right
  4223. //--------------
  4224. else if (ent->client->ps.torsoAnim==BOTH_VT_ATR_G || ent->client->ps.torsoAnim==BOTH_VS_ATR_G)
  4225. {
  4226. VectorCopy(actorRight, forward);
  4227. }
  4228. // Aiming Forward
  4229. //----------------
  4230. else
  4231. {
  4232. VectorCopy(actorFwd, forward);
  4233. }
  4234. // If We Have An Enemy, Fudge The Aim To Hit The Enemy
  4235. if (ent->enemy)
  4236. {
  4237. vec3_t toEnemy;
  4238. VectorSubtract(ent->enemy->currentOrigin, ent->currentOrigin, toEnemy);
  4239. VectorNormalize(toEnemy);
  4240. if (DotProduct(toEnemy, forward)>0.75f &&
  4241. ((ent->s.number==0 && !Q_irand(0,2)) || // the player has a 1 in 3 chance
  4242. (ent->s.number!=0 && !Q_irand(0,5)))) // other guys have a 1 in 6 chance
  4243. {
  4244. VectorCopy(toEnemy, forward);
  4245. }
  4246. else
  4247. {
  4248. forward[0] += Q_flrand(-0.1f, 0.1f);
  4249. forward[1] += Q_flrand(-0.1f, 0.1f);
  4250. forward[2] += Q_flrand(-0.1f, 0.1f);
  4251. }
  4252. }
  4253. }
  4254. }
  4255. else
  4256. {
  4257. AngleVectors( ent->client->ps.viewangles, forward, vright, up );
  4258. }
  4259. }
  4260. ent->alt_fire = alt_fire;
  4261. if (!pVeh)
  4262. {
  4263. if (ent->NPC && (ent->NPC->scriptFlags&SCF_FIRE_WEAPON_NO_ANIM))
  4264. {
  4265. VectorCopy( ent->client->renderInfo.muzzlePoint, muzzle );
  4266. VectorCopy( ent->client->renderInfo.muzzleDir, forward );
  4267. MakeNormalVectors(forward, vright, up);
  4268. }
  4269. else
  4270. {
  4271. CalcMuzzlePoint ( ent, forward, vright, up, muzzle , 0);
  4272. }
  4273. }
  4274. // fire the specific weapon
  4275. switch( ent->s.weapon )
  4276. {
  4277. // Player weapons
  4278. //-----------------
  4279. case WP_SABER:
  4280. return;
  4281. break;
  4282. case WP_BRYAR_PISTOL:
  4283. case WP_BLASTER_PISTOL:
  4284. WP_FireBryarPistol( ent, alt_fire );
  4285. break;
  4286. case WP_BLASTER:
  4287. WP_FireBlaster( ent, alt_fire );
  4288. break;
  4289. case WP_TUSKEN_RIFLE:
  4290. if ( alt_fire )
  4291. {
  4292. WP_FireTuskenRifle( ent );
  4293. }
  4294. else
  4295. {
  4296. WP_Melee( ent );
  4297. }
  4298. break;
  4299. case WP_DISRUPTOR:
  4300. alert = 50; // if you want it to alert enemies, remove this
  4301. WP_FireDisruptor( ent, alt_fire );
  4302. break;
  4303. case WP_BOWCASTER:
  4304. WP_FireBowcaster( ent, alt_fire );
  4305. break;
  4306. case WP_REPEATER:
  4307. WP_FireRepeater( ent, alt_fire );
  4308. break;
  4309. case WP_DEMP2:
  4310. WP_FireDEMP2( ent, alt_fire );
  4311. break;
  4312. case WP_FLECHETTE:
  4313. WP_FireFlechette( ent, alt_fire );
  4314. break;
  4315. case WP_ROCKET_LAUNCHER:
  4316. WP_FireRocket( ent, alt_fire );
  4317. break;
  4318. case WP_CONCUSSION:
  4319. if ( alt_fire )
  4320. {
  4321. WP_FireConcussionAlt( ent );
  4322. }
  4323. else
  4324. {
  4325. WP_FireConcussion( ent );
  4326. }
  4327. break;
  4328. case WP_THERMAL:
  4329. WP_FireThermalDetonator( ent, alt_fire );
  4330. break;
  4331. case WP_TRIP_MINE:
  4332. alert = 0; // if you want it to alert enemies, remove this
  4333. WP_PlaceLaserTrap( ent, alt_fire );
  4334. break;
  4335. case WP_DET_PACK:
  4336. alert = 0; // if you want it to alert enemies, remove this
  4337. WP_FireDetPack( ent, alt_fire );
  4338. break;
  4339. case WP_BOT_LASER:
  4340. WP_BotLaser( ent );
  4341. break;
  4342. case WP_EMPLACED_GUN:
  4343. // doesn't care about whether it's alt-fire or not. We can do an alt-fire if needed
  4344. WP_EmplacedFire( ent );
  4345. break;
  4346. case WP_MELEE:
  4347. alert = 0; // if you want it to alert enemies, remove this
  4348. if ( !alt_fire || !g_debugMelee->integer )
  4349. {
  4350. WP_Melee( ent );
  4351. }
  4352. break;
  4353. case WP_ATST_MAIN:
  4354. WP_ATSTMainFire( ent );
  4355. break;
  4356. case WP_ATST_SIDE:
  4357. // TEMP
  4358. if ( alt_fire )
  4359. {
  4360. // WP_FireRocket( ent, qfalse );
  4361. WP_ATSTSideAltFire(ent);
  4362. }
  4363. else
  4364. {
  4365. // FIXME!
  4366. /* if ( ent->s.number == 0
  4367. && ent->client->NPC_class == CLASS_VEHICLE
  4368. && vehicleData[((CVehicleNPC *)ent->NPC)->m_iVehicleTypeID].type == VH_FIGHTER )
  4369. {
  4370. WP_ATSTMainFire( ent );
  4371. }
  4372. else*/
  4373. {
  4374. WP_ATSTSideFire(ent);
  4375. }
  4376. }
  4377. break;
  4378. case WP_TIE_FIGHTER:
  4379. // TEMP
  4380. WP_EmplacedFire( ent );
  4381. break;
  4382. case WP_RAPID_FIRE_CONC:
  4383. // TEMP
  4384. if ( alt_fire )
  4385. {
  4386. WP_FireRepeater( ent, alt_fire );
  4387. }
  4388. else
  4389. {
  4390. WP_EmplacedFire( ent );
  4391. }
  4392. break;
  4393. case WP_STUN_BATON:
  4394. WP_FireStunBaton( ent, alt_fire );
  4395. break;
  4396. // case WP_BLASTER_PISTOL:
  4397. case WP_JAWA:
  4398. WP_FireBryarPistol( ent, qfalse ); // never an alt-fire?
  4399. break;
  4400. case WP_SCEPTER:
  4401. WP_FireScepter( ent, alt_fire );
  4402. break;
  4403. case WP_NOGHRI_STICK:
  4404. if ( !alt_fire )
  4405. {
  4406. WP_FireNoghriStick( ent );
  4407. }
  4408. //else does melee attack/damage/func
  4409. break;
  4410. case WP_TUSKEN_STAFF:
  4411. default:
  4412. return;
  4413. break;
  4414. }
  4415. if ( !ent->s.number )
  4416. {
  4417. if ( ent->s.weapon == WP_FLECHETTE || (ent->s.weapon == WP_BOWCASTER && !alt_fire) )
  4418. {//these can fire multiple shots, count them individually within the firing functions
  4419. }
  4420. else if ( W_AccuracyLoggableWeapon( ent->s.weapon, alt_fire, MOD_UNKNOWN ) )
  4421. {
  4422. ent->client->sess.missionStats.shotsFired++;
  4423. }
  4424. }
  4425. // We should probably just use this as a default behavior, in special cases, just set alert to false.
  4426. if ( ent->s.number == 0 && alert > 0 )
  4427. {
  4428. if ( ent->client->ps.groundEntityNum == ENTITYNUM_WORLD//FIXME: check for sand contents type?
  4429. && ent->s.weapon != WP_STUN_BATON
  4430. && ent->s.weapon != WP_MELEE
  4431. && ent->s.weapon != WP_TUSKEN_STAFF
  4432. && ent->s.weapon != WP_THERMAL
  4433. && ent->s.weapon != WP_TRIP_MINE
  4434. && ent->s.weapon != WP_DET_PACK )
  4435. {//the vibration of the shot carries through your feet into the ground
  4436. AddSoundEvent( ent, muzzle, alert, AEL_DISCOVERED, qfalse, qtrue );
  4437. }
  4438. else
  4439. {//an in-air alert
  4440. AddSoundEvent( ent, muzzle, alert, AEL_DISCOVERED );
  4441. }
  4442. AddSightEvent( ent, muzzle, alert*2, AEL_DISCOVERED, 20 );
  4443. }
  4444. }
  4445. //NOTE: Emplaced gun moved to g_emplaced.cpp
  4446. /*QUAKED misc_weapon_shooter (1 0 0) (-8 -8 -8) (8 8 8) ALTFIRE TOGGLE
  4447. ALTFIRE - fire the alt-fire of the chosen weapon
  4448. TOGGLE - keep firing until used again (fires at intervals of "wait")
  4449. "wait" - debounce time between refires (defaults to 500)
  4450. "delay" - speed of WP_THERMAL (default is 900)
  4451. "random" - ranges from 0 to random, added to wait (defaults to 0)
  4452. "target" - what to aim at (will update aim every frame if it's a moving target)
  4453. "weapon" - specify the weapon to use (default is WP_BLASTER)
  4454. WP_BRYAR_PISTOL
  4455. WP_BLASTER
  4456. WP_DISRUPTOR
  4457. WP_BOWCASTER
  4458. WP_REPEATER
  4459. WP_DEMP2
  4460. WP_FLECHETTE
  4461. WP_ROCKET_LAUNCHER
  4462. WP_CONCUSSION
  4463. WP_THERMAL
  4464. WP_TRIP_MINE
  4465. WP_DET_PACK
  4466. WP_STUN_BATON
  4467. WP_EMPLACED_GUN
  4468. WP_BOT_LASER
  4469. WP_TURRET
  4470. WP_ATST_MAIN
  4471. WP_ATST_SIDE
  4472. WP_TIE_FIGHTER
  4473. WP_RAPID_FIRE_CONC
  4474. WP_BLASTER_PISTOL
  4475. */
  4476. void misc_weapon_shooter_fire( gentity_t *self )
  4477. {
  4478. FireWeapon( self, (self->spawnflags&1) );
  4479. if ( (self->spawnflags&2) )
  4480. {//repeat
  4481. self->e_ThinkFunc = thinkF_misc_weapon_shooter_fire;
  4482. if (self->random)
  4483. {
  4484. self->nextthink = level.time + self->wait + (int)(random()*self->random);
  4485. }
  4486. else
  4487. {
  4488. self->nextthink = level.time + self->wait;
  4489. }
  4490. }
  4491. }
  4492. void misc_weapon_shooter_use ( gentity_t *self, gentity_t *other, gentity_t *activator )
  4493. {
  4494. if ( self->e_ThinkFunc == thinkF_misc_weapon_shooter_fire )
  4495. {//repeating fire, stop
  4496. self->e_ThinkFunc = thinkF_NULL;
  4497. self->nextthink = -1;
  4498. return;
  4499. }
  4500. //otherwise, fire
  4501. misc_weapon_shooter_fire( self );
  4502. }
  4503. void misc_weapon_shooter_aim( gentity_t *self )
  4504. {
  4505. //update my aim
  4506. if ( self->target )
  4507. {
  4508. gentity_t *targ = G_Find( NULL, FOFS(targetname), self->target );
  4509. if ( targ )
  4510. {
  4511. self->enemy = targ;
  4512. VectorSubtract( targ->currentOrigin, self->currentOrigin, self->client->renderInfo.muzzleDir );
  4513. VectorCopy( targ->currentOrigin, self->pos1 );
  4514. vectoangles( self->client->renderInfo.muzzleDir, self->client->ps.viewangles );
  4515. SetClientViewAngle( self, self->client->ps.viewangles );
  4516. //FIXME: don't keep doing this unless target is a moving target?
  4517. self->nextthink = level.time + FRAMETIME;
  4518. }
  4519. else
  4520. {
  4521. self->enemy = NULL;
  4522. }
  4523. }
  4524. }
  4525. extern stringID_table_t WPTable[];
  4526. void SP_misc_weapon_shooter( gentity_t *self )
  4527. {
  4528. //alloc a client just for the weapon code to use
  4529. self->client = (gclient_s *)gi.Malloc(sizeof(gclient_s), TAG_G_ALLOC, qtrue);
  4530. //set weapon
  4531. self->s.weapon = self->client->ps.weapon = WP_BLASTER;
  4532. if ( self->paintarget )
  4533. {//use a different weapon
  4534. self->s.weapon = self->client->ps.weapon = GetIDForString( WPTable, self->paintarget );
  4535. }
  4536. //set where our muzzle is
  4537. VectorCopy( self->s.origin, self->client->renderInfo.muzzlePoint );
  4538. //permanently updated
  4539. self->client->renderInfo.mPCalcTime = Q3_INFINITE;
  4540. //set up to link
  4541. if ( self->target )
  4542. {
  4543. self->e_ThinkFunc = thinkF_misc_weapon_shooter_aim;
  4544. self->nextthink = level.time + START_TIME_LINK_ENTS;
  4545. }
  4546. else
  4547. {//just set aim angles
  4548. VectorCopy( self->s.angles, self->client->ps.viewangles );
  4549. AngleVectors( self->s.angles, self->client->renderInfo.muzzleDir, NULL, NULL );
  4550. }
  4551. //set up to fire when used
  4552. self->e_UseFunc = useF_misc_weapon_shooter_use;
  4553. if ( !self->wait )
  4554. {
  4555. self->wait = 500;
  4556. }
  4557. }