Weapon.cpp 110 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976
  1. /*
  2. ===========================================================================
  3. Doom 3 GPL Source Code
  4. Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
  5. This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
  6. Doom 3 Source Code is free software: you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation, either version 3 of the License, or
  9. (at your option) any later version.
  10. Doom 3 Source Code is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. GNU General Public License for more details.
  14. You should have received a copy of the GNU General Public License
  15. along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
  16. In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
  17. If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
  18. ===========================================================================
  19. */
  20. #include "../idlib/precompiled.h"
  21. #pragma hdrstop
  22. #include "Game_local.h"
  23. /***********************************************************************
  24. idWeapon
  25. ***********************************************************************/
  26. //
  27. // event defs
  28. //
  29. const idEventDef EV_Weapon_Clear( "<clear>" );
  30. const idEventDef EV_Weapon_GetOwner( "getOwner", NULL, 'e' );
  31. const idEventDef EV_Weapon_Next( "nextWeapon" );
  32. const idEventDef EV_Weapon_State( "weaponState", "sd" );
  33. const idEventDef EV_Weapon_UseAmmo( "useAmmo", "d" );
  34. const idEventDef EV_Weapon_AddToClip( "addToClip", "d" );
  35. const idEventDef EV_Weapon_AmmoInClip( "ammoInClip", NULL, 'f' );
  36. const idEventDef EV_Weapon_AmmoAvailable( "ammoAvailable", NULL, 'f' );
  37. const idEventDef EV_Weapon_TotalAmmoCount( "totalAmmoCount", NULL, 'f' );
  38. const idEventDef EV_Weapon_ClipSize( "clipSize", NULL, 'f' );
  39. const idEventDef EV_Weapon_WeaponOutOfAmmo( "weaponOutOfAmmo" );
  40. const idEventDef EV_Weapon_WeaponReady( "weaponReady" );
  41. const idEventDef EV_Weapon_WeaponReloading( "weaponReloading" );
  42. const idEventDef EV_Weapon_WeaponHolstered( "weaponHolstered" );
  43. const idEventDef EV_Weapon_WeaponRising( "weaponRising" );
  44. const idEventDef EV_Weapon_WeaponLowering( "weaponLowering" );
  45. const idEventDef EV_Weapon_Flashlight( "flashlight", "d" );
  46. const idEventDef EV_Weapon_LaunchProjectiles( "launchProjectiles", "dffff" );
  47. const idEventDef EV_Weapon_CreateProjectile( "createProjectile", NULL, 'e' );
  48. const idEventDef EV_Weapon_EjectBrass( "ejectBrass" );
  49. const idEventDef EV_Weapon_Melee( "melee", NULL, 'd' );
  50. const idEventDef EV_Weapon_GetWorldModel( "getWorldModel", NULL, 'e' );
  51. const idEventDef EV_Weapon_AllowDrop( "allowDrop", "d" );
  52. const idEventDef EV_Weapon_AutoReload( "autoReload", NULL, 'f' );
  53. const idEventDef EV_Weapon_NetReload( "netReload" );
  54. const idEventDef EV_Weapon_IsInvisible( "isInvisible", NULL, 'f' );
  55. const idEventDef EV_Weapon_NetEndReload( "netEndReload" );
  56. #ifdef _D3XP
  57. const idEventDef EV_Weapon_GrabberHasTarget( "grabberHasTarget", NULL, 'd' );
  58. const idEventDef EV_Weapon_Grabber( "grabber", "d" );
  59. const idEventDef EV_Weapon_Grabber_SetGrabDistance( "grabberGrabDistance", "f" );
  60. const idEventDef EV_Weapon_LaunchProjectilesEllipse( "launchProjectilesEllipse", "dffff" );
  61. const idEventDef EV_Weapon_LaunchPowerup( "launchPowerup", "sfd" );
  62. const idEventDef EV_Weapon_StartWeaponSmoke( "startWeaponSmoke" );
  63. const idEventDef EV_Weapon_StopWeaponSmoke( "stopWeaponSmoke" );
  64. const idEventDef EV_Weapon_StartWeaponParticle( "startWeaponParticle", "s" );
  65. const idEventDef EV_Weapon_StopWeaponParticle( "stopWeaponParticle", "s" );
  66. const idEventDef EV_Weapon_StartWeaponLight( "startWeaponLight", "s" );
  67. const idEventDef EV_Weapon_StopWeaponLight( "stopWeaponLight", "s" );
  68. #endif
  69. //
  70. // class def
  71. //
  72. CLASS_DECLARATION( idAnimatedEntity, idWeapon )
  73. EVENT( EV_Weapon_Clear, idWeapon::Event_Clear )
  74. EVENT( EV_Weapon_GetOwner, idWeapon::Event_GetOwner )
  75. EVENT( EV_Weapon_State, idWeapon::Event_WeaponState )
  76. EVENT( EV_Weapon_WeaponReady, idWeapon::Event_WeaponReady )
  77. EVENT( EV_Weapon_WeaponOutOfAmmo, idWeapon::Event_WeaponOutOfAmmo )
  78. EVENT( EV_Weapon_WeaponReloading, idWeapon::Event_WeaponReloading )
  79. EVENT( EV_Weapon_WeaponHolstered, idWeapon::Event_WeaponHolstered )
  80. EVENT( EV_Weapon_WeaponRising, idWeapon::Event_WeaponRising )
  81. EVENT( EV_Weapon_WeaponLowering, idWeapon::Event_WeaponLowering )
  82. EVENT( EV_Weapon_UseAmmo, idWeapon::Event_UseAmmo )
  83. EVENT( EV_Weapon_AddToClip, idWeapon::Event_AddToClip )
  84. EVENT( EV_Weapon_AmmoInClip, idWeapon::Event_AmmoInClip )
  85. EVENT( EV_Weapon_AmmoAvailable, idWeapon::Event_AmmoAvailable )
  86. EVENT( EV_Weapon_TotalAmmoCount, idWeapon::Event_TotalAmmoCount )
  87. EVENT( EV_Weapon_ClipSize, idWeapon::Event_ClipSize )
  88. EVENT( AI_PlayAnim, idWeapon::Event_PlayAnim )
  89. EVENT( AI_PlayCycle, idWeapon::Event_PlayCycle )
  90. EVENT( AI_SetBlendFrames, idWeapon::Event_SetBlendFrames )
  91. EVENT( AI_GetBlendFrames, idWeapon::Event_GetBlendFrames )
  92. EVENT( AI_AnimDone, idWeapon::Event_AnimDone )
  93. EVENT( EV_Weapon_Next, idWeapon::Event_Next )
  94. EVENT( EV_SetSkin, idWeapon::Event_SetSkin )
  95. EVENT( EV_Weapon_Flashlight, idWeapon::Event_Flashlight )
  96. EVENT( EV_Light_GetLightParm, idWeapon::Event_GetLightParm )
  97. EVENT( EV_Light_SetLightParm, idWeapon::Event_SetLightParm )
  98. EVENT( EV_Light_SetLightParms, idWeapon::Event_SetLightParms )
  99. EVENT( EV_Weapon_LaunchProjectiles, idWeapon::Event_LaunchProjectiles )
  100. EVENT( EV_Weapon_CreateProjectile, idWeapon::Event_CreateProjectile )
  101. EVENT( EV_Weapon_EjectBrass, idWeapon::Event_EjectBrass )
  102. EVENT( EV_Weapon_Melee, idWeapon::Event_Melee )
  103. EVENT( EV_Weapon_GetWorldModel, idWeapon::Event_GetWorldModel )
  104. EVENT( EV_Weapon_AllowDrop, idWeapon::Event_AllowDrop )
  105. EVENT( EV_Weapon_AutoReload, idWeapon::Event_AutoReload )
  106. EVENT( EV_Weapon_NetReload, idWeapon::Event_NetReload )
  107. EVENT( EV_Weapon_IsInvisible, idWeapon::Event_IsInvisible )
  108. EVENT( EV_Weapon_NetEndReload, idWeapon::Event_NetEndReload )
  109. #ifdef _D3XP
  110. EVENT( EV_Weapon_Grabber, idWeapon::Event_Grabber )
  111. EVENT( EV_Weapon_GrabberHasTarget, idWeapon::Event_GrabberHasTarget )
  112. EVENT( EV_Weapon_Grabber_SetGrabDistance, idWeapon::Event_GrabberSetGrabDistance )
  113. EVENT( EV_Weapon_LaunchProjectilesEllipse, idWeapon::Event_LaunchProjectilesEllipse )
  114. EVENT( EV_Weapon_LaunchPowerup, idWeapon::Event_LaunchPowerup )
  115. EVENT( EV_Weapon_StartWeaponSmoke, idWeapon::Event_StartWeaponSmoke )
  116. EVENT( EV_Weapon_StopWeaponSmoke, idWeapon::Event_StopWeaponSmoke )
  117. EVENT( EV_Weapon_StartWeaponParticle, idWeapon::Event_StartWeaponParticle )
  118. EVENT( EV_Weapon_StopWeaponParticle, idWeapon::Event_StopWeaponParticle )
  119. EVENT( EV_Weapon_StartWeaponLight, idWeapon::Event_StartWeaponLight )
  120. EVENT( EV_Weapon_StopWeaponLight, idWeapon::Event_StopWeaponLight )
  121. #endif
  122. END_CLASS
  123. /***********************************************************************
  124. init
  125. ***********************************************************************/
  126. /*
  127. ================
  128. idWeapon::idWeapon()
  129. ================
  130. */
  131. idWeapon::idWeapon() {
  132. owner = NULL;
  133. worldModel = NULL;
  134. weaponDef = NULL;
  135. thread = NULL;
  136. memset( &guiLight, 0, sizeof( guiLight ) );
  137. memset( &muzzleFlash, 0, sizeof( muzzleFlash ) );
  138. memset( &worldMuzzleFlash, 0, sizeof( worldMuzzleFlash ) );
  139. memset( &nozzleGlow, 0, sizeof( nozzleGlow ) );
  140. muzzleFlashEnd = 0;
  141. flashColor = vec3_origin;
  142. muzzleFlashHandle = -1;
  143. worldMuzzleFlashHandle = -1;
  144. guiLightHandle = -1;
  145. nozzleGlowHandle = -1;
  146. modelDefHandle = -1;
  147. #ifdef _D3XP
  148. grabberState = -1;
  149. #endif
  150. berserk = 2;
  151. brassDelay = 0;
  152. allowDrop = true;
  153. Clear();
  154. fl.networkSync = true;
  155. }
  156. /*
  157. ================
  158. idWeapon::~idWeapon()
  159. ================
  160. */
  161. idWeapon::~idWeapon() {
  162. Clear();
  163. delete worldModel.GetEntity();
  164. }
  165. /*
  166. ================
  167. idWeapon::Spawn
  168. ================
  169. */
  170. void idWeapon::Spawn( void ) {
  171. if ( !gameLocal.isClient ) {
  172. // setup the world model
  173. worldModel = static_cast< idAnimatedEntity * >( gameLocal.SpawnEntityType( idAnimatedEntity::Type, NULL ) );
  174. worldModel.GetEntity()->fl.networkSync = true;
  175. }
  176. #ifdef _D3XP
  177. if ( 1 /*!gameLocal.isMultiplayer*/ ) {
  178. grabber.Initialize();
  179. }
  180. #endif
  181. thread = new idThread();
  182. thread->ManualDelete();
  183. thread->ManualControl();
  184. }
  185. /*
  186. ================
  187. idWeapon::SetOwner
  188. Only called at player spawn time, not each weapon switch
  189. ================
  190. */
  191. void idWeapon::SetOwner( idPlayer *_owner ) {
  192. assert( !owner );
  193. owner = _owner;
  194. SetName( va( "%s_weapon", owner->name.c_str() ) );
  195. if ( worldModel.GetEntity() ) {
  196. worldModel.GetEntity()->SetName( va( "%s_weapon_worldmodel", owner->name.c_str() ) );
  197. }
  198. }
  199. /*
  200. ================
  201. idWeapon::ShouldConstructScriptObjectAtSpawn
  202. Called during idEntity::Spawn to see if it should construct the script object or not.
  203. Overridden by subclasses that need to spawn the script object themselves.
  204. ================
  205. */
  206. bool idWeapon::ShouldConstructScriptObjectAtSpawn( void ) const {
  207. return false;
  208. }
  209. /*
  210. ================
  211. idWeapon::CacheWeapon
  212. ================
  213. */
  214. void idWeapon::CacheWeapon( const char *weaponName ) {
  215. const idDeclEntityDef *weaponDef;
  216. const char *brassDefName;
  217. const char *clipModelName;
  218. idTraceModel trm;
  219. const char *guiName;
  220. weaponDef = gameLocal.FindEntityDef( weaponName, false );
  221. if ( !weaponDef ) {
  222. return;
  223. }
  224. // precache the brass collision model
  225. brassDefName = weaponDef->dict.GetString( "def_ejectBrass" );
  226. if ( brassDefName[0] ) {
  227. const idDeclEntityDef *brassDef = gameLocal.FindEntityDef( brassDefName, false );
  228. if ( brassDef ) {
  229. brassDef->dict.GetString( "clipmodel", "", &clipModelName );
  230. if ( !clipModelName[0] ) {
  231. clipModelName = brassDef->dict.GetString( "model" ); // use the visual model
  232. }
  233. // load the trace model
  234. collisionModelManager->TrmFromModel( clipModelName, trm );
  235. }
  236. }
  237. guiName = weaponDef->dict.GetString( "gui" );
  238. if ( guiName[0] ) {
  239. uiManager->FindGui( guiName, true, false, true );
  240. }
  241. }
  242. /*
  243. ================
  244. idWeapon::Save
  245. ================
  246. */
  247. void idWeapon::Save( idSaveGame *savefile ) const {
  248. savefile->WriteInt( status );
  249. savefile->WriteObject( thread );
  250. savefile->WriteString( state );
  251. savefile->WriteString( idealState );
  252. savefile->WriteInt( animBlendFrames );
  253. savefile->WriteInt( animDoneTime );
  254. savefile->WriteBool( isLinked );
  255. savefile->WriteObject( owner );
  256. worldModel.Save( savefile );
  257. savefile->WriteInt( hideTime );
  258. savefile->WriteFloat( hideDistance );
  259. savefile->WriteInt( hideStartTime );
  260. savefile->WriteFloat( hideStart );
  261. savefile->WriteFloat( hideEnd );
  262. savefile->WriteFloat( hideOffset );
  263. savefile->WriteBool( hide );
  264. savefile->WriteBool( disabled );
  265. savefile->WriteInt( berserk );
  266. savefile->WriteVec3( playerViewOrigin );
  267. savefile->WriteMat3( playerViewAxis );
  268. savefile->WriteVec3( viewWeaponOrigin );
  269. savefile->WriteMat3( viewWeaponAxis );
  270. savefile->WriteVec3( muzzleOrigin );
  271. savefile->WriteMat3( muzzleAxis );
  272. savefile->WriteVec3( pushVelocity );
  273. savefile->WriteString( weaponDef->GetName() );
  274. savefile->WriteFloat( meleeDistance );
  275. savefile->WriteString( meleeDefName );
  276. savefile->WriteInt( brassDelay );
  277. savefile->WriteString( icon );
  278. savefile->WriteInt( guiLightHandle );
  279. savefile->WriteRenderLight( guiLight );
  280. savefile->WriteInt( muzzleFlashHandle );
  281. savefile->WriteRenderLight( muzzleFlash );
  282. savefile->WriteInt( worldMuzzleFlashHandle );
  283. savefile->WriteRenderLight( worldMuzzleFlash );
  284. savefile->WriteVec3( flashColor );
  285. savefile->WriteInt( muzzleFlashEnd );
  286. savefile->WriteInt( flashTime );
  287. savefile->WriteBool( lightOn );
  288. savefile->WriteBool( silent_fire );
  289. savefile->WriteInt( kick_endtime );
  290. savefile->WriteInt( muzzle_kick_time );
  291. savefile->WriteInt( muzzle_kick_maxtime );
  292. savefile->WriteAngles( muzzle_kick_angles );
  293. savefile->WriteVec3( muzzle_kick_offset );
  294. savefile->WriteInt( ammoType );
  295. savefile->WriteInt( ammoRequired );
  296. savefile->WriteInt( clipSize );
  297. savefile->WriteInt( ammoClip );
  298. savefile->WriteInt( lowAmmo );
  299. savefile->WriteBool( powerAmmo );
  300. // savegames <= 17
  301. savefile->WriteInt( 0 );
  302. savefile->WriteInt( zoomFov );
  303. savefile->WriteJoint( barrelJointView );
  304. savefile->WriteJoint( flashJointView );
  305. savefile->WriteJoint( ejectJointView );
  306. savefile->WriteJoint( guiLightJointView );
  307. savefile->WriteJoint( ventLightJointView );
  308. savefile->WriteJoint( flashJointWorld );
  309. savefile->WriteJoint( barrelJointWorld );
  310. savefile->WriteJoint( ejectJointWorld );
  311. savefile->WriteBool( hasBloodSplat );
  312. savefile->WriteSoundShader( sndHum );
  313. savefile->WriteParticle( weaponSmoke );
  314. savefile->WriteInt( weaponSmokeStartTime );
  315. savefile->WriteBool( continuousSmoke );
  316. savefile->WriteParticle( strikeSmoke );
  317. savefile->WriteInt( strikeSmokeStartTime );
  318. savefile->WriteVec3( strikePos );
  319. savefile->WriteMat3( strikeAxis );
  320. savefile->WriteInt( nextStrikeFx );
  321. savefile->WriteBool( nozzleFx );
  322. savefile->WriteInt( nozzleFxFade );
  323. savefile->WriteInt( lastAttack );
  324. savefile->WriteInt( nozzleGlowHandle );
  325. savefile->WriteRenderLight( nozzleGlow );
  326. savefile->WriteVec3( nozzleGlowColor );
  327. savefile->WriteMaterial( nozzleGlowShader );
  328. savefile->WriteFloat( nozzleGlowRadius );
  329. savefile->WriteInt( weaponAngleOffsetAverages );
  330. savefile->WriteFloat( weaponAngleOffsetScale );
  331. savefile->WriteFloat( weaponAngleOffsetMax );
  332. savefile->WriteFloat( weaponOffsetTime );
  333. savefile->WriteFloat( weaponOffsetScale );
  334. savefile->WriteBool( allowDrop );
  335. savefile->WriteObject( projectileEnt );
  336. #ifdef _D3XP
  337. savefile->WriteStaticObject( grabber );
  338. savefile->WriteInt( grabberState );
  339. savefile->WriteJoint ( smokeJointView );
  340. savefile->WriteInt(weaponParticles.Num());
  341. for(int i = 0; i < weaponParticles.Num(); i++) {
  342. WeaponParticle_t* part = weaponParticles.GetIndex(i);
  343. savefile->WriteString( part->name );
  344. savefile->WriteString( part->particlename );
  345. savefile->WriteBool( part->active );
  346. savefile->WriteInt( part->startTime );
  347. savefile->WriteJoint( part->joint );
  348. savefile->WriteBool( part->smoke );
  349. if(!part->smoke) {
  350. savefile->WriteObject(part->emitter);
  351. }
  352. }
  353. savefile->WriteInt(weaponLights.Num());
  354. for(int i = 0; i < weaponLights.Num(); i++) {
  355. WeaponLight_t* light = weaponLights.GetIndex(i);
  356. savefile->WriteString( light->name );
  357. savefile->WriteBool( light->active );
  358. savefile->WriteInt( light->startTime );
  359. savefile->WriteJoint( light->joint );
  360. savefile->WriteInt( light->lightHandle );
  361. savefile->WriteRenderLight( light->light );
  362. }
  363. #endif
  364. }
  365. /*
  366. ================
  367. idWeapon::Restore
  368. ================
  369. */
  370. void idWeapon::Restore( idRestoreGame *savefile ) {
  371. savefile->ReadInt( (int &)status );
  372. savefile->ReadObject( reinterpret_cast<idClass *&>( thread ) );
  373. savefile->ReadString( state );
  374. savefile->ReadString( idealState );
  375. savefile->ReadInt( animBlendFrames );
  376. savefile->ReadInt( animDoneTime );
  377. savefile->ReadBool( isLinked );
  378. // Re-link script fields
  379. WEAPON_ATTACK.LinkTo( scriptObject, "WEAPON_ATTACK" );
  380. WEAPON_RELOAD.LinkTo( scriptObject, "WEAPON_RELOAD" );
  381. WEAPON_PICKER.LinkTo( scriptObject, "WEAPON_PICKER" );
  382. WEAPON_NETRELOAD.LinkTo( scriptObject, "WEAPON_NETRELOAD" );
  383. WEAPON_NETENDRELOAD.LinkTo( scriptObject, "WEAPON_NETENDRELOAD" );
  384. WEAPON_NETFIRING.LinkTo( scriptObject, "WEAPON_NETFIRING" );
  385. WEAPON_RAISEWEAPON.LinkTo( scriptObject, "WEAPON_RAISEWEAPON" );
  386. WEAPON_LOWERWEAPON.LinkTo( scriptObject, "WEAPON_LOWERWEAPON" );
  387. savefile->ReadObject( reinterpret_cast<idClass *&>( owner ) );
  388. worldModel.Restore( savefile );
  389. savefile->ReadInt( hideTime );
  390. savefile->ReadFloat( hideDistance );
  391. savefile->ReadInt( hideStartTime );
  392. savefile->ReadFloat( hideStart );
  393. savefile->ReadFloat( hideEnd );
  394. savefile->ReadFloat( hideOffset );
  395. savefile->ReadBool( hide );
  396. savefile->ReadBool( disabled );
  397. savefile->ReadInt( berserk );
  398. savefile->ReadVec3( playerViewOrigin );
  399. savefile->ReadMat3( playerViewAxis );
  400. savefile->ReadVec3( viewWeaponOrigin );
  401. savefile->ReadMat3( viewWeaponAxis );
  402. savefile->ReadVec3( muzzleOrigin );
  403. savefile->ReadMat3( muzzleAxis );
  404. savefile->ReadVec3( pushVelocity );
  405. idStr objectname;
  406. savefile->ReadString( objectname );
  407. weaponDef = gameLocal.FindEntityDef( objectname );
  408. meleeDef = gameLocal.FindEntityDef( weaponDef->dict.GetString( "def_melee" ), false );
  409. const idDeclEntityDef *projectileDef = gameLocal.FindEntityDef( weaponDef->dict.GetString( "def_projectile" ), false );
  410. if ( projectileDef ) {
  411. projectileDict = projectileDef->dict;
  412. } else {
  413. projectileDict.Clear();
  414. }
  415. const idDeclEntityDef *brassDef = gameLocal.FindEntityDef( weaponDef->dict.GetString( "def_ejectBrass" ), false );
  416. if ( brassDef ) {
  417. brassDict = brassDef->dict;
  418. } else {
  419. brassDict.Clear();
  420. }
  421. savefile->ReadFloat( meleeDistance );
  422. savefile->ReadString( meleeDefName );
  423. savefile->ReadInt( brassDelay );
  424. savefile->ReadString( icon );
  425. savefile->ReadInt( guiLightHandle );
  426. savefile->ReadRenderLight( guiLight );
  427. #ifdef _D3XP
  428. if ( guiLightHandle >= 0 ) {
  429. guiLightHandle = gameRenderWorld->AddLightDef( &guiLight );
  430. }
  431. #endif
  432. savefile->ReadInt( muzzleFlashHandle );
  433. savefile->ReadRenderLight( muzzleFlash );
  434. #ifdef _D3XP
  435. if ( muzzleFlashHandle >= 0 ) {
  436. muzzleFlashHandle = gameRenderWorld->AddLightDef( &muzzleFlash );
  437. }
  438. #endif
  439. savefile->ReadInt( worldMuzzleFlashHandle );
  440. savefile->ReadRenderLight( worldMuzzleFlash );
  441. #ifdef _D3XP
  442. if ( worldMuzzleFlashHandle >= 0 ) {
  443. worldMuzzleFlashHandle = gameRenderWorld->AddLightDef( &worldMuzzleFlash );
  444. }
  445. #endif
  446. savefile->ReadVec3( flashColor );
  447. savefile->ReadInt( muzzleFlashEnd );
  448. savefile->ReadInt( flashTime );
  449. savefile->ReadBool( lightOn );
  450. savefile->ReadBool( silent_fire );
  451. savefile->ReadInt( kick_endtime );
  452. savefile->ReadInt( muzzle_kick_time );
  453. savefile->ReadInt( muzzle_kick_maxtime );
  454. savefile->ReadAngles( muzzle_kick_angles );
  455. savefile->ReadVec3( muzzle_kick_offset );
  456. savefile->ReadInt( (int &)ammoType );
  457. savefile->ReadInt( ammoRequired );
  458. savefile->ReadInt( clipSize );
  459. savefile->ReadInt( ammoClip );
  460. savefile->ReadInt( lowAmmo );
  461. savefile->ReadBool( powerAmmo );
  462. // savegame versions <= 17
  463. int foo;
  464. savefile->ReadInt( foo );
  465. savefile->ReadInt( zoomFov );
  466. savefile->ReadJoint( barrelJointView );
  467. savefile->ReadJoint( flashJointView );
  468. savefile->ReadJoint( ejectJointView );
  469. savefile->ReadJoint( guiLightJointView );
  470. savefile->ReadJoint( ventLightJointView );
  471. savefile->ReadJoint( flashJointWorld );
  472. savefile->ReadJoint( barrelJointWorld );
  473. savefile->ReadJoint( ejectJointWorld );
  474. savefile->ReadBool( hasBloodSplat );
  475. savefile->ReadSoundShader( sndHum );
  476. savefile->ReadParticle( weaponSmoke );
  477. savefile->ReadInt( weaponSmokeStartTime );
  478. savefile->ReadBool( continuousSmoke );
  479. savefile->ReadParticle( strikeSmoke );
  480. savefile->ReadInt( strikeSmokeStartTime );
  481. savefile->ReadVec3( strikePos );
  482. savefile->ReadMat3( strikeAxis );
  483. savefile->ReadInt( nextStrikeFx );
  484. savefile->ReadBool( nozzleFx );
  485. savefile->ReadInt( nozzleFxFade );
  486. savefile->ReadInt( lastAttack );
  487. savefile->ReadInt( nozzleGlowHandle );
  488. savefile->ReadRenderLight( nozzleGlow );
  489. #ifdef _D3XP
  490. if ( nozzleGlowHandle >= 0 ) {
  491. nozzleGlowHandle = gameRenderWorld->AddLightDef( &nozzleGlow );
  492. }
  493. #endif
  494. savefile->ReadVec3( nozzleGlowColor );
  495. savefile->ReadMaterial( nozzleGlowShader );
  496. savefile->ReadFloat( nozzleGlowRadius );
  497. savefile->ReadInt( weaponAngleOffsetAverages );
  498. savefile->ReadFloat( weaponAngleOffsetScale );
  499. savefile->ReadFloat( weaponAngleOffsetMax );
  500. savefile->ReadFloat( weaponOffsetTime );
  501. savefile->ReadFloat( weaponOffsetScale );
  502. savefile->ReadBool( allowDrop );
  503. savefile->ReadObject( reinterpret_cast<idClass *&>( projectileEnt ) );
  504. #ifdef _D3XP
  505. savefile->ReadStaticObject( grabber );
  506. savefile->ReadInt( grabberState );
  507. savefile->ReadJoint ( smokeJointView );
  508. int particleCount;
  509. savefile->ReadInt( particleCount );
  510. for(int i = 0; i < particleCount; i++) {
  511. WeaponParticle_t newParticle;
  512. memset(&newParticle, 0, sizeof(newParticle));
  513. idStr name, particlename;
  514. savefile->ReadString( name );
  515. savefile->ReadString( particlename );
  516. strcpy( newParticle.name, name.c_str() );
  517. strcpy( newParticle.particlename, particlename.c_str() );
  518. savefile->ReadBool( newParticle.active );
  519. savefile->ReadInt( newParticle.startTime );
  520. savefile->ReadJoint( newParticle.joint );
  521. savefile->ReadBool( newParticle.smoke );
  522. if(newParticle.smoke) {
  523. newParticle.particle = static_cast<const idDeclParticle *>( declManager->FindType( DECL_PARTICLE, particlename, false ) );
  524. } else {
  525. savefile->ReadObject(reinterpret_cast<idClass *&>(newParticle.emitter));
  526. }
  527. weaponParticles.Set(newParticle.name, newParticle);
  528. }
  529. int lightCount;
  530. savefile->ReadInt( lightCount );
  531. for(int i = 0; i < lightCount; i++) {
  532. WeaponLight_t newLight;
  533. memset(&newLight, 0, sizeof(newLight));
  534. idStr name;
  535. savefile->ReadString( name );
  536. strcpy( newLight.name, name.c_str() );
  537. savefile->ReadBool( newLight.active );
  538. savefile->ReadInt( newLight.startTime );
  539. savefile->ReadJoint( newLight.joint );
  540. savefile->ReadInt( newLight.lightHandle );
  541. savefile->ReadRenderLight( newLight.light );
  542. if ( newLight.lightHandle >= 0 ) {
  543. newLight.lightHandle = gameRenderWorld->AddLightDef( &newLight.light );
  544. }
  545. weaponLights.Set(newLight.name, newLight);
  546. }
  547. #endif
  548. }
  549. /***********************************************************************
  550. Weapon definition management
  551. ***********************************************************************/
  552. /*
  553. ================
  554. idWeapon::Clear
  555. ================
  556. */
  557. void idWeapon::Clear( void ) {
  558. CancelEvents( &EV_Weapon_Clear );
  559. DeconstructScriptObject();
  560. scriptObject.Free();
  561. WEAPON_ATTACK.Unlink();
  562. WEAPON_RELOAD.Unlink();
  563. WEAPON_PICKER.Unlink();
  564. WEAPON_NETRELOAD.Unlink();
  565. WEAPON_NETENDRELOAD.Unlink();
  566. WEAPON_NETFIRING.Unlink();
  567. WEAPON_RAISEWEAPON.Unlink();
  568. WEAPON_LOWERWEAPON.Unlink();
  569. if ( muzzleFlashHandle != -1 ) {
  570. gameRenderWorld->FreeLightDef( muzzleFlashHandle );
  571. muzzleFlashHandle = -1;
  572. }
  573. if ( muzzleFlashHandle != -1 ) {
  574. gameRenderWorld->FreeLightDef( muzzleFlashHandle );
  575. muzzleFlashHandle = -1;
  576. }
  577. if ( worldMuzzleFlashHandle != -1 ) {
  578. gameRenderWorld->FreeLightDef( worldMuzzleFlashHandle );
  579. worldMuzzleFlashHandle = -1;
  580. }
  581. if ( guiLightHandle != -1 ) {
  582. gameRenderWorld->FreeLightDef( guiLightHandle );
  583. guiLightHandle = -1;
  584. }
  585. if ( nozzleGlowHandle != -1 ) {
  586. gameRenderWorld->FreeLightDef( nozzleGlowHandle );
  587. nozzleGlowHandle = -1;
  588. }
  589. memset( &renderEntity, 0, sizeof( renderEntity ) );
  590. renderEntity.entityNum = entityNumber;
  591. renderEntity.noShadow = true;
  592. renderEntity.noSelfShadow = true;
  593. renderEntity.customSkin = NULL;
  594. // set default shader parms
  595. renderEntity.shaderParms[ SHADERPARM_RED ] = 1.0f;
  596. renderEntity.shaderParms[ SHADERPARM_GREEN ]= 1.0f;
  597. renderEntity.shaderParms[ SHADERPARM_BLUE ] = 1.0f;
  598. renderEntity.shaderParms[3] = 1.0f;
  599. renderEntity.shaderParms[ SHADERPARM_TIMEOFFSET ] = 0.0f;
  600. renderEntity.shaderParms[5] = 0.0f;
  601. renderEntity.shaderParms[6] = 0.0f;
  602. renderEntity.shaderParms[7] = 0.0f;
  603. if ( refSound.referenceSound ) {
  604. refSound.referenceSound->Free( true );
  605. }
  606. memset( &refSound, 0, sizeof( refSound_t ) );
  607. // setting diversity to 0 results in no random sound. -1 indicates random.
  608. refSound.diversity = -1.0f;
  609. if ( owner ) {
  610. // don't spatialize the weapon sounds
  611. refSound.listenerId = owner->GetListenerId();
  612. }
  613. // clear out the sounds from our spawnargs since we'll copy them from the weapon def
  614. const idKeyValue *kv = spawnArgs.MatchPrefix( "snd_" );
  615. while( kv ) {
  616. spawnArgs.Delete( kv->GetKey() );
  617. kv = spawnArgs.MatchPrefix( "snd_" );
  618. }
  619. hideTime = 300;
  620. hideDistance = -15.0f;
  621. hideStartTime = gameLocal.time - hideTime;
  622. hideStart = 0.0f;
  623. hideEnd = 0.0f;
  624. hideOffset = 0.0f;
  625. hide = false;
  626. disabled = false;
  627. weaponSmoke = NULL;
  628. weaponSmokeStartTime = 0;
  629. continuousSmoke = false;
  630. strikeSmoke = NULL;
  631. strikeSmokeStartTime = 0;
  632. strikePos.Zero();
  633. strikeAxis = mat3_identity;
  634. nextStrikeFx = 0;
  635. icon = "";
  636. playerViewAxis.Identity();
  637. playerViewOrigin.Zero();
  638. viewWeaponAxis.Identity();
  639. viewWeaponOrigin.Zero();
  640. muzzleAxis.Identity();
  641. muzzleOrigin.Zero();
  642. pushVelocity.Zero();
  643. status = WP_HOLSTERED;
  644. state = "";
  645. idealState = "";
  646. animBlendFrames = 0;
  647. animDoneTime = 0;
  648. projectileDict.Clear();
  649. meleeDef = NULL;
  650. meleeDefName = "";
  651. meleeDistance = 0.0f;
  652. brassDict.Clear();
  653. flashTime = 250;
  654. lightOn = false;
  655. silent_fire = false;
  656. #ifdef _D3XP
  657. grabberState = -1;
  658. grabber.Update( owner, true );
  659. #endif
  660. ammoType = 0;
  661. ammoRequired = 0;
  662. ammoClip = 0;
  663. clipSize = 0;
  664. lowAmmo = 0;
  665. powerAmmo = false;
  666. kick_endtime = 0;
  667. muzzle_kick_time = 0;
  668. muzzle_kick_maxtime = 0;
  669. muzzle_kick_angles.Zero();
  670. muzzle_kick_offset.Zero();
  671. zoomFov = 90;
  672. barrelJointView = INVALID_JOINT;
  673. flashJointView = INVALID_JOINT;
  674. ejectJointView = INVALID_JOINT;
  675. guiLightJointView = INVALID_JOINT;
  676. ventLightJointView = INVALID_JOINT;
  677. barrelJointWorld = INVALID_JOINT;
  678. flashJointWorld = INVALID_JOINT;
  679. ejectJointWorld = INVALID_JOINT;
  680. #ifdef _D3XP
  681. smokeJointView = INVALID_JOINT;
  682. //Clean up the weapon particles
  683. for(int i = 0; i < weaponParticles.Num(); i++) {
  684. WeaponParticle_t* part = weaponParticles.GetIndex(i);
  685. if(!part->smoke) {
  686. //Destroy the emitters
  687. part->emitter->PostEventMS(&EV_Remove, 0 );
  688. }
  689. }
  690. weaponParticles.Clear();
  691. //Clean up the weapon lights
  692. for(int i = 0; i < weaponLights.Num(); i++) {
  693. WeaponLight_t* light = weaponLights.GetIndex(i);
  694. if ( light->lightHandle != -1 ) {
  695. gameRenderWorld->FreeLightDef( light->lightHandle );
  696. }
  697. }
  698. weaponLights.Clear();
  699. #endif
  700. hasBloodSplat = false;
  701. nozzleFx = false;
  702. nozzleFxFade = 1500;
  703. lastAttack = 0;
  704. nozzleGlowHandle = -1;
  705. nozzleGlowShader = NULL;
  706. nozzleGlowRadius = 10;
  707. nozzleGlowColor.Zero();
  708. weaponAngleOffsetAverages = 10;
  709. weaponAngleOffsetScale = 0.25f;
  710. weaponAngleOffsetMax = 10.0f;
  711. weaponOffsetTime = 1000.0f;
  712. weaponOffsetScale = 0.001f;
  713. /*
  714. weaponAngleOffsetAverages = 0;
  715. weaponAngleOffsetScale = 0.0f;
  716. weaponAngleOffsetMax = 0.0f;
  717. weaponOffsetTime = 0.0f;
  718. weaponOffsetScale = 0.0f;
  719. */
  720. allowDrop = true;
  721. animator.ClearAllAnims( gameLocal.time, 0 );
  722. FreeModelDef();
  723. sndHum = NULL;
  724. isLinked = false;
  725. projectileEnt = NULL;
  726. isFiring = false;
  727. }
  728. /*
  729. ================
  730. idWeapon::InitWorldModel
  731. ================
  732. */
  733. void idWeapon::InitWorldModel( const idDeclEntityDef *def ) {
  734. idEntity *ent;
  735. ent = worldModel.GetEntity();
  736. assert( ent );
  737. assert( def );
  738. const char *model = def->dict.GetString( "model_world" );
  739. const char *attach = def->dict.GetString( "joint_attach" );
  740. ent->SetSkin( NULL );
  741. if ( model[0] && attach[0] ) {
  742. ent->Show();
  743. ent->SetModel( model );
  744. if ( ent->GetAnimator()->ModelDef() ) {
  745. ent->SetSkin( ent->GetAnimator()->ModelDef()->GetDefaultSkin() );
  746. }
  747. ent->GetPhysics()->SetContents( 0 );
  748. ent->GetPhysics()->SetClipModel( NULL, 1.0f );
  749. ent->BindToJoint( owner, attach, true );
  750. ent->GetPhysics()->SetOrigin( vec3_origin );
  751. ent->GetPhysics()->SetAxis( mat3_identity );
  752. // supress model in player views, but allow it in mirrors and remote views
  753. renderEntity_t *worldModelRenderEntity = ent->GetRenderEntity();
  754. if ( worldModelRenderEntity ) {
  755. worldModelRenderEntity->suppressSurfaceInViewID = owner->entityNumber+1;
  756. worldModelRenderEntity->suppressShadowInViewID = owner->entityNumber+1;
  757. worldModelRenderEntity->suppressShadowInLightID = LIGHTID_VIEW_MUZZLE_FLASH + owner->entityNumber;
  758. }
  759. } else {
  760. ent->SetModel( "" );
  761. ent->Hide();
  762. }
  763. flashJointWorld = ent->GetAnimator()->GetJointHandle( "flash" );
  764. barrelJointWorld = ent->GetAnimator()->GetJointHandle( "muzzle" );
  765. ejectJointWorld = ent->GetAnimator()->GetJointHandle( "eject" );
  766. }
  767. /*
  768. ================
  769. idWeapon::GetWeaponDef
  770. ================
  771. */
  772. void idWeapon::GetWeaponDef( const char *objectname, int ammoinclip ) {
  773. const char *shader;
  774. const char *objectType;
  775. const char *vmodel;
  776. const char *guiName;
  777. const char *projectileName;
  778. const char *brassDefName;
  779. const char *smokeName;
  780. int ammoAvail;
  781. Clear();
  782. if ( !objectname || !objectname[ 0 ] ) {
  783. return;
  784. }
  785. assert( owner );
  786. weaponDef = gameLocal.FindEntityDef( objectname );
  787. ammoType = GetAmmoNumForName( weaponDef->dict.GetString( "ammoType" ) );
  788. ammoRequired = weaponDef->dict.GetInt( "ammoRequired" );
  789. clipSize = weaponDef->dict.GetInt( "clipSize" );
  790. lowAmmo = weaponDef->dict.GetInt( "lowAmmo" );
  791. icon = weaponDef->dict.GetString( "icon" );
  792. silent_fire = weaponDef->dict.GetBool( "silent_fire" );
  793. powerAmmo = weaponDef->dict.GetBool( "powerAmmo" );
  794. muzzle_kick_time = SEC2MS( weaponDef->dict.GetFloat( "muzzle_kick_time" ) );
  795. muzzle_kick_maxtime = SEC2MS( weaponDef->dict.GetFloat( "muzzle_kick_maxtime" ) );
  796. muzzle_kick_angles = weaponDef->dict.GetAngles( "muzzle_kick_angles" );
  797. muzzle_kick_offset = weaponDef->dict.GetVector( "muzzle_kick_offset" );
  798. hideTime = SEC2MS( weaponDef->dict.GetFloat( "hide_time", "0.3" ) );
  799. hideDistance = weaponDef->dict.GetFloat( "hide_distance", "-15" );
  800. placerModel = weaponDef->dict.GetString( "model" );
  801. // muzzle smoke
  802. smokeName = weaponDef->dict.GetString( "smoke_muzzle" );
  803. if ( *smokeName != '\0' ) {
  804. weaponSmoke = static_cast<const idDeclParticle *>( declManager->FindType( DECL_PARTICLE, smokeName ) );
  805. } else {
  806. weaponSmoke = NULL;
  807. }
  808. continuousSmoke = weaponDef->dict.GetBool( "continuousSmoke" );
  809. weaponSmokeStartTime = ( continuousSmoke ) ? gameLocal.time : 0;
  810. smokeName = weaponDef->dict.GetString( "smoke_strike" );
  811. if ( *smokeName != '\0' ) {
  812. strikeSmoke = static_cast<const idDeclParticle *>( declManager->FindType( DECL_PARTICLE, smokeName ) );
  813. } else {
  814. strikeSmoke = NULL;
  815. }
  816. strikeSmokeStartTime = 0;
  817. strikePos.Zero();
  818. strikeAxis = mat3_identity;
  819. nextStrikeFx = 0;
  820. // setup gui light
  821. memset( &guiLight, 0, sizeof( guiLight ) );
  822. const char *guiLightShader = weaponDef->dict.GetString( "mtr_guiLightShader" );
  823. if ( *guiLightShader != '\0' ) {
  824. guiLight.shader = declManager->FindMaterial( guiLightShader, false );
  825. guiLight.lightRadius[0] = guiLight.lightRadius[1] = guiLight.lightRadius[2] = 3;
  826. guiLight.pointLight = true;
  827. }
  828. // setup the view model
  829. vmodel = weaponDef->dict.GetString( "model_view" );
  830. SetModel( vmodel );
  831. // setup the world model
  832. InitWorldModel( weaponDef );
  833. // copy the sounds from the weapon view model def into out spawnargs
  834. const idKeyValue *kv = weaponDef->dict.MatchPrefix( "snd_" );
  835. while( kv ) {
  836. spawnArgs.Set( kv->GetKey(), kv->GetValue() );
  837. kv = weaponDef->dict.MatchPrefix( "snd_", kv );
  838. }
  839. // find some joints in the model for locating effects
  840. barrelJointView = animator.GetJointHandle( "barrel" );
  841. flashJointView = animator.GetJointHandle( "flash" );
  842. ejectJointView = animator.GetJointHandle( "eject" );
  843. guiLightJointView = animator.GetJointHandle( "guiLight" );
  844. ventLightJointView = animator.GetJointHandle( "ventLight" );
  845. #ifdef _D3XP
  846. idStr smokeJoint = weaponDef->dict.GetString("smoke_joint");
  847. if(smokeJoint.Length() > 0) {
  848. smokeJointView = animator.GetJointHandle( smokeJoint );
  849. } else {
  850. smokeJointView = INVALID_JOINT;
  851. }
  852. #endif
  853. // get the projectile
  854. projectileDict.Clear();
  855. projectileName = weaponDef->dict.GetString( "def_projectile" );
  856. if ( projectileName[0] != '\0' ) {
  857. const idDeclEntityDef *projectileDef = gameLocal.FindEntityDef( projectileName, false );
  858. if ( !projectileDef ) {
  859. gameLocal.Warning( "Unknown projectile '%s' in weapon '%s'", projectileName, objectname );
  860. } else {
  861. const char *spawnclass = projectileDef->dict.GetString( "spawnclass" );
  862. idTypeInfo *cls = idClass::GetClass( spawnclass );
  863. if ( !cls || !cls->IsType( idProjectile::Type ) ) {
  864. gameLocal.Warning( "Invalid spawnclass '%s' on projectile '%s' (used by weapon '%s')", spawnclass, projectileName, objectname );
  865. } else {
  866. projectileDict = projectileDef->dict;
  867. }
  868. }
  869. }
  870. // set up muzzleflash render light
  871. const idMaterial*flashShader;
  872. idVec3 flashTarget;
  873. idVec3 flashUp;
  874. idVec3 flashRight;
  875. float flashRadius;
  876. bool flashPointLight;
  877. weaponDef->dict.GetString( "mtr_flashShader", "", &shader );
  878. flashShader = declManager->FindMaterial( shader, false );
  879. flashPointLight = weaponDef->dict.GetBool( "flashPointLight", "1" );
  880. weaponDef->dict.GetVector( "flashColor", "0 0 0", flashColor );
  881. flashRadius = (float)weaponDef->dict.GetInt( "flashRadius" ); // if 0, no light will spawn
  882. flashTime = SEC2MS( weaponDef->dict.GetFloat( "flashTime", "0.25" ) );
  883. flashTarget = weaponDef->dict.GetVector( "flashTarget" );
  884. flashUp = weaponDef->dict.GetVector( "flashUp" );
  885. flashRight = weaponDef->dict.GetVector( "flashRight" );
  886. memset( &muzzleFlash, 0, sizeof( muzzleFlash ) );
  887. muzzleFlash.lightId = LIGHTID_VIEW_MUZZLE_FLASH + owner->entityNumber;
  888. muzzleFlash.allowLightInViewID = owner->entityNumber+1;
  889. // the weapon lights will only be in first person
  890. guiLight.allowLightInViewID = owner->entityNumber+1;
  891. nozzleGlow.allowLightInViewID = owner->entityNumber+1;
  892. muzzleFlash.pointLight = flashPointLight;
  893. muzzleFlash.shader = flashShader;
  894. muzzleFlash.shaderParms[ SHADERPARM_RED ] = flashColor[0];
  895. muzzleFlash.shaderParms[ SHADERPARM_GREEN ] = flashColor[1];
  896. muzzleFlash.shaderParms[ SHADERPARM_BLUE ] = flashColor[2];
  897. muzzleFlash.shaderParms[ SHADERPARM_TIMESCALE ] = 1.0f;
  898. muzzleFlash.lightRadius[0] = flashRadius;
  899. muzzleFlash.lightRadius[1] = flashRadius;
  900. muzzleFlash.lightRadius[2] = flashRadius;
  901. if ( !flashPointLight ) {
  902. muzzleFlash.target = flashTarget;
  903. muzzleFlash.up = flashUp;
  904. muzzleFlash.right = flashRight;
  905. muzzleFlash.end = flashTarget;
  906. }
  907. // the world muzzle flash is the same, just positioned differently
  908. worldMuzzleFlash = muzzleFlash;
  909. worldMuzzleFlash.suppressLightInViewID = owner->entityNumber+1;
  910. worldMuzzleFlash.allowLightInViewID = 0;
  911. worldMuzzleFlash.lightId = LIGHTID_WORLD_MUZZLE_FLASH + owner->entityNumber;
  912. //-----------------------------------
  913. nozzleFx = weaponDef->dict.GetBool("nozzleFx");
  914. nozzleFxFade = weaponDef->dict.GetInt("nozzleFxFade", "1500");
  915. nozzleGlowColor = weaponDef->dict.GetVector("nozzleGlowColor", "1 1 1");
  916. nozzleGlowRadius = weaponDef->dict.GetFloat("nozzleGlowRadius", "10");
  917. weaponDef->dict.GetString( "mtr_nozzleGlowShader", "", &shader );
  918. nozzleGlowShader = declManager->FindMaterial( shader, false );
  919. // get the melee damage def
  920. meleeDistance = weaponDef->dict.GetFloat( "melee_distance" );
  921. meleeDefName = weaponDef->dict.GetString( "def_melee" );
  922. if ( meleeDefName.Length() ) {
  923. meleeDef = gameLocal.FindEntityDef( meleeDefName, false );
  924. if ( !meleeDef ) {
  925. gameLocal.Error( "Unknown melee '%s'", meleeDefName.c_str() );
  926. }
  927. }
  928. // get the brass def
  929. brassDict.Clear();
  930. brassDelay = weaponDef->dict.GetInt( "ejectBrassDelay", "0" );
  931. brassDefName = weaponDef->dict.GetString( "def_ejectBrass" );
  932. if ( brassDefName[0] ) {
  933. const idDeclEntityDef *brassDef = gameLocal.FindEntityDef( brassDefName, false );
  934. if ( !brassDef ) {
  935. gameLocal.Warning( "Unknown brass '%s'", brassDefName );
  936. } else {
  937. brassDict = brassDef->dict;
  938. }
  939. }
  940. if ( ( ammoType < 0 ) || ( ammoType >= AMMO_NUMTYPES ) ) {
  941. gameLocal.Warning( "Unknown ammotype in object '%s'", objectname );
  942. }
  943. ammoClip = ammoinclip;
  944. if ( ( ammoClip < 0 ) || ( ammoClip > clipSize ) ) {
  945. // first time using this weapon so have it fully loaded to start
  946. ammoClip = clipSize;
  947. ammoAvail = owner->inventory.HasAmmo( ammoType, ammoRequired );
  948. if ( ammoClip > ammoAvail ) {
  949. ammoClip = ammoAvail;
  950. }
  951. //bc remove this auto clip behavior.
  952. /*
  953. #ifdef _D3XP
  954. //In D3XP we use ammo as soon as it is moved into the clip. This allows for weapons that share ammo
  955. owner->inventory.UseAmmo(ammoType, ammoClip);
  956. #endif
  957. */
  958. }
  959. renderEntity.gui[ 0 ] = NULL;
  960. guiName = weaponDef->dict.GetString( "gui" );
  961. if ( guiName[0] ) {
  962. renderEntity.gui[ 0 ] = uiManager->FindGui( guiName, true, false, true );
  963. }
  964. zoomFov = weaponDef->dict.GetInt( "zoomFov", "30" ); //default zoom amount.
  965. berserk = weaponDef->dict.GetInt( "berserk", "2" );
  966. weaponAngleOffsetAverages = weaponDef->dict.GetInt( "weaponAngleOffsetAverages", "10" );
  967. weaponAngleOffsetScale = weaponDef->dict.GetFloat( "weaponAngleOffsetScale", "0.25" );
  968. weaponAngleOffsetMax = weaponDef->dict.GetFloat( "weaponAngleOffsetMax", "10" );
  969. weaponOffsetTime = weaponDef->dict.GetFloat( "weaponOffsetTime", "1000" );
  970. weaponOffsetScale = weaponDef->dict.GetFloat( "weaponOffsetScale", "0.001" );
  971. if ( !weaponDef->dict.GetString( "weapon_scriptobject", NULL, &objectType ) ) {
  972. gameLocal.Error( "No 'weapon_scriptobject' set on '%s'.", objectname );
  973. }
  974. // setup script object
  975. if ( !scriptObject.SetType( objectType ) ) {
  976. gameLocal.Error( "Script object '%s' not found on weapon '%s'.", objectType, objectname );
  977. }
  978. WEAPON_ATTACK.LinkTo( scriptObject, "WEAPON_ATTACK" );
  979. WEAPON_RELOAD.LinkTo( scriptObject, "WEAPON_RELOAD" );
  980. WEAPON_PICKER.LinkTo( scriptObject, "WEAPON_PICKER" );
  981. WEAPON_NETRELOAD.LinkTo( scriptObject, "WEAPON_NETRELOAD" );
  982. WEAPON_NETENDRELOAD.LinkTo( scriptObject, "WEAPON_NETENDRELOAD" );
  983. WEAPON_NETFIRING.LinkTo( scriptObject, "WEAPON_NETFIRING" );
  984. WEAPON_RAISEWEAPON.LinkTo( scriptObject, "WEAPON_RAISEWEAPON" );
  985. WEAPON_LOWERWEAPON.LinkTo( scriptObject, "WEAPON_LOWERWEAPON" );
  986. spawnArgs = weaponDef->dict;
  987. shader = spawnArgs.GetString( "snd_hum" );
  988. if ( shader && *shader ) {
  989. sndHum = declManager->FindSound( shader );
  990. StartSoundShader( sndHum, SND_CHANNEL_BODY, 0, false, NULL );
  991. }
  992. isLinked = true;
  993. // call script object's constructor
  994. ConstructScriptObject();
  995. // make sure we have the correct skin
  996. UpdateSkin();
  997. #ifdef _D3XP
  998. idEntity *ent = worldModel.GetEntity();
  999. DetermineTimeGroup( weaponDef->dict.GetBool( "slowmo", "0" ) );
  1000. if ( ent ) {
  1001. ent->DetermineTimeGroup( weaponDef->dict.GetBool( "slowmo", "0" ) );
  1002. }
  1003. //Initialize the particles
  1004. if ( !gameLocal.isMultiplayer ) {
  1005. const idKeyValue *pkv = weaponDef->dict.MatchPrefix( "weapon_particle", NULL );
  1006. while( pkv ) {
  1007. WeaponParticle_t newParticle;
  1008. memset( &newParticle, 0, sizeof( newParticle ) );
  1009. idStr name = pkv->GetValue();
  1010. strcpy(newParticle.name, name.c_str());
  1011. idStr jointName = weaponDef->dict.GetString(va("%s_joint", name.c_str()));
  1012. newParticle.joint = animator.GetJointHandle(jointName.c_str());
  1013. newParticle.smoke = weaponDef->dict.GetBool(va("%s_smoke", name.c_str()));
  1014. newParticle.active = false;
  1015. newParticle.startTime = 0;
  1016. idStr particle = weaponDef->dict.GetString(va("%s_particle", name.c_str()));
  1017. strcpy(newParticle.particlename, particle.c_str());
  1018. if(newParticle.smoke) {
  1019. newParticle.particle = static_cast<const idDeclParticle *>( declManager->FindType( DECL_PARTICLE, particle, false ) );
  1020. } else {
  1021. idDict args;
  1022. const idDeclEntityDef *emitterDef = gameLocal.FindEntityDef( "func_emitter", false );
  1023. args = emitterDef->dict;
  1024. args.Set("model", particle.c_str());
  1025. args.SetBool("start_off", true);
  1026. idEntity* ent;
  1027. gameLocal.SpawnEntityDef(args, &ent, false);
  1028. newParticle.emitter = (idFuncEmitter*)ent;
  1029. newParticle.emitter->BecomeActive(TH_THINK);
  1030. }
  1031. weaponParticles.Set(name.c_str(), newParticle);
  1032. pkv = weaponDef->dict.MatchPrefix( "weapon_particle", pkv );
  1033. }
  1034. const idKeyValue *lkv = weaponDef->dict.MatchPrefix( "weapon_light", NULL );
  1035. while( lkv ) {
  1036. WeaponLight_t newLight;
  1037. memset( &newLight, 0, sizeof( newLight ) );
  1038. newLight.lightHandle = -1;
  1039. newLight.active = false;
  1040. newLight.startTime = 0;
  1041. idStr name = lkv->GetValue();
  1042. strcpy(newLight.name, name.c_str());
  1043. idStr jointName = weaponDef->dict.GetString(va("%s_joint", name.c_str()));
  1044. newLight.joint = animator.GetJointHandle(jointName.c_str());
  1045. idStr shader = weaponDef->dict.GetString(va("%s_shader", name.c_str()));
  1046. newLight.light.shader = declManager->FindMaterial( shader, false );
  1047. float radius = weaponDef->dict.GetFloat(va("%s_radius", name.c_str()));
  1048. newLight.light.lightRadius[0] = newLight.light.lightRadius[1] = newLight.light.lightRadius[2] = radius;
  1049. newLight.light.pointLight = true;
  1050. newLight.light.noShadows = true;
  1051. newLight.light.allowLightInViewID = owner->entityNumber+1;
  1052. weaponLights.Set(name.c_str(), newLight);
  1053. lkv = weaponDef->dict.MatchPrefix( "weapon_light", lkv );
  1054. }
  1055. }
  1056. #endif
  1057. }
  1058. /***********************************************************************
  1059. GUIs
  1060. ***********************************************************************/
  1061. /*
  1062. ================
  1063. idWeapon::Icon
  1064. ================
  1065. */
  1066. const char *idWeapon::Icon( void ) const {
  1067. return icon;
  1068. }
  1069. /*
  1070. ================
  1071. idWeapon::UpdateGUI
  1072. ================
  1073. */
  1074. void idWeapon::UpdateGUI( void ) {
  1075. if ( !renderEntity.gui[ 0 ] ) {
  1076. return;
  1077. }
  1078. if ( status == WP_HOLSTERED ) {
  1079. return;
  1080. }
  1081. if ( owner->weaponGone ) {
  1082. // dropping weapons was implemented wierd, so we have to not update the gui when it happens or we'll get a negative ammo count
  1083. return;
  1084. }
  1085. if ( gameLocal.localClientNum != owner->entityNumber ) {
  1086. // if updating the hud for a followed client
  1087. if ( gameLocal.localClientNum >= 0 && gameLocal.entities[ gameLocal.localClientNum ] && gameLocal.entities[ gameLocal.localClientNum ]->IsType( idPlayer::Type ) ) {
  1088. idPlayer *p = static_cast< idPlayer * >( gameLocal.entities[ gameLocal.localClientNum ] );
  1089. if ( !p->spectating || p->spectator != owner->entityNumber ) {
  1090. return;
  1091. }
  1092. } else {
  1093. return;
  1094. }
  1095. }
  1096. int inclip = AmmoInClip();
  1097. int ammoamount = AmmoAvailable();
  1098. if ( ammoamount < 0 ) {
  1099. // show infinite ammo
  1100. renderEntity.gui[ 0 ]->SetStateString( "player_ammo", "" );
  1101. } else {
  1102. // show remaining ammo
  1103. #ifdef _D3XP
  1104. renderEntity.gui[ 0 ]->SetStateString( "player_totalammo", va( "%i", ammoamount) );
  1105. #else
  1106. renderEntity.gui[ 0 ]->SetStateString( "player_totalammo", va( "%i", ammoamount - inclip) );
  1107. #endif
  1108. renderEntity.gui[ 0 ]->SetStateString( "player_ammo", ClipSize() ? va( "%i", inclip ) : "--" );
  1109. renderEntity.gui[ 0 ]->SetStateString( "player_clips", ClipSize() ? va("%i", ammoamount / ClipSize()) : "--" );
  1110. #ifdef _D3XP
  1111. renderEntity.gui[ 0 ]->SetStateString( "player_allammo", va( "%i/%i", inclip, ammoamount ) );
  1112. #else
  1113. renderEntity.gui[ 0 ]->SetStateString( "player_allammo", va( "%i/%i", inclip, ammoamount - inclip ) );
  1114. #endif
  1115. }
  1116. renderEntity.gui[ 0 ]->SetStateBool( "player_ammo_empty", ( ammoamount == 0 ) );
  1117. renderEntity.gui[ 0 ]->SetStateBool( "player_clip_empty", ( inclip == 0 ) );
  1118. renderEntity.gui[ 0 ]->SetStateBool( "player_clip_low", ( inclip <= lowAmmo ) );
  1119. #ifdef _D3XP
  1120. //Let the HUD know the total amount of ammo regardless of the ammo required value
  1121. renderEntity.gui[ 0 ]->SetStateString( "player_ammo_count", va("%i", AmmoCount()));
  1122. //Grabber Gui Info
  1123. renderEntity.gui[ 0 ]->SetStateString( "grabber_state", va("%i", grabberState));
  1124. #endif
  1125. renderEntity.gui[ 0 ]->SetStateString( "text_objective", va("%s", owner->text_objective.c_str()));
  1126. renderEntity.gui[ 0 ]->SetStateString( "text_alarms", va("%d", owner->text_alarms));
  1127. int minutes = owner->text_missionclock / 60.0f;
  1128. float seconds = owner->text_missionclock - (minutes * 60);
  1129. int deciseconds = (seconds - (int)seconds) * 10.0f;
  1130. renderEntity.gui[ 0 ]->SetStateString( "text_missionclock", va("%d:%02d.%d", minutes, (int)(seconds), deciseconds ));
  1131. }
  1132. /***********************************************************************
  1133. Model and muzzleflash
  1134. ***********************************************************************/
  1135. /*
  1136. ================
  1137. idWeapon::UpdateFlashPosition
  1138. ================
  1139. */
  1140. void idWeapon::UpdateFlashPosition( void ) {
  1141. // the flash has an explicit joint for locating it
  1142. GetGlobalJointTransform( true, flashJointView, muzzleFlash.origin, muzzleFlash.axis );
  1143. // if the desired point is inside or very close to a wall, back it up until it is clear
  1144. idVec3 start = muzzleFlash.origin - playerViewAxis[0] * 16;
  1145. idVec3 end = muzzleFlash.origin + playerViewAxis[0] * 8;
  1146. trace_t tr;
  1147. gameLocal.clip.TracePoint( tr, start, end, MASK_SHOT_RENDERMODEL, owner );
  1148. // be at least 8 units away from a solid
  1149. muzzleFlash.origin = tr.endpos - playerViewAxis[0] * 8;
  1150. // put the world muzzle flash on the end of the joint, no matter what
  1151. GetGlobalJointTransform( false, flashJointWorld, worldMuzzleFlash.origin, worldMuzzleFlash.axis );
  1152. }
  1153. /*
  1154. ================
  1155. idWeapon::MuzzleFlashLight
  1156. ================
  1157. */
  1158. void idWeapon::MuzzleFlashLight( void ) {
  1159. if ( !lightOn && ( !g_muzzleFlash.GetBool() || !muzzleFlash.lightRadius[0] ) ) {
  1160. return;
  1161. }
  1162. if ( flashJointView == INVALID_JOINT ) {
  1163. return;
  1164. }
  1165. UpdateFlashPosition();
  1166. // these will be different each fire
  1167. muzzleFlash.shaderParms[ SHADERPARM_TIMEOFFSET ] = -MS2SEC( gameLocal.time );
  1168. muzzleFlash.shaderParms[ SHADERPARM_DIVERSITY ] = renderEntity.shaderParms[ SHADERPARM_DIVERSITY ];
  1169. worldMuzzleFlash.shaderParms[ SHADERPARM_TIMEOFFSET ] = -MS2SEC( gameLocal.time );
  1170. worldMuzzleFlash.shaderParms[ SHADERPARM_DIVERSITY ] = renderEntity.shaderParms[ SHADERPARM_DIVERSITY ];
  1171. // the light will be removed at this time
  1172. muzzleFlashEnd = gameLocal.time + flashTime;
  1173. if ( muzzleFlashHandle != -1 ) {
  1174. gameRenderWorld->UpdateLightDef( muzzleFlashHandle, &muzzleFlash );
  1175. gameRenderWorld->UpdateLightDef( worldMuzzleFlashHandle, &worldMuzzleFlash );
  1176. } else {
  1177. muzzleFlashHandle = gameRenderWorld->AddLightDef( &muzzleFlash );
  1178. worldMuzzleFlashHandle = gameRenderWorld->AddLightDef( &worldMuzzleFlash );
  1179. }
  1180. }
  1181. /*
  1182. ================
  1183. idWeapon::UpdateSkin
  1184. ================
  1185. */
  1186. bool idWeapon::UpdateSkin( void ) {
  1187. const function_t *func;
  1188. if ( !isLinked ) {
  1189. return false;
  1190. }
  1191. func = scriptObject.GetFunction( "UpdateSkin" );
  1192. if ( !func ) {
  1193. common->Warning( "Can't find function 'UpdateSkin' in object '%s'", scriptObject.GetTypeName() );
  1194. return false;
  1195. }
  1196. // use the frameCommandThread since it's safe to use outside of framecommands
  1197. gameLocal.frameCommandThread->CallFunction( this, func, true );
  1198. gameLocal.frameCommandThread->Execute();
  1199. return true;
  1200. }
  1201. /*
  1202. ================
  1203. idWeapon::SetModel
  1204. ================
  1205. */
  1206. void idWeapon::SetModel( const char *modelname ) {
  1207. assert( modelname );
  1208. if ( modelDefHandle >= 0 ) {
  1209. gameRenderWorld->RemoveDecals( modelDefHandle );
  1210. }
  1211. renderEntity.hModel = animator.SetModel( modelname );
  1212. if ( renderEntity.hModel ) {
  1213. renderEntity.customSkin = animator.ModelDef()->GetDefaultSkin();
  1214. animator.GetJoints( &renderEntity.numJoints, &renderEntity.joints );
  1215. } else {
  1216. renderEntity.customSkin = NULL;
  1217. renderEntity.callback = NULL;
  1218. renderEntity.numJoints = 0;
  1219. renderEntity.joints = NULL;
  1220. }
  1221. // hide the model until an animation is played
  1222. Hide();
  1223. }
  1224. /*
  1225. ================
  1226. idWeapon::GetGlobalJointTransform
  1227. This returns the offset and axis of a weapon bone in world space, suitable for attaching models or lights
  1228. ================
  1229. */
  1230. bool idWeapon::GetGlobalJointTransform( bool viewModel, const jointHandle_t jointHandle, idVec3 &offset, idMat3 &axis ) {
  1231. if ( viewModel ) {
  1232. // view model
  1233. if ( animator.GetJointTransform( jointHandle, gameLocal.time, offset, axis ) ) {
  1234. offset = offset * viewWeaponAxis + viewWeaponOrigin;
  1235. axis = axis * viewWeaponAxis;
  1236. return true;
  1237. }
  1238. } else {
  1239. // world model
  1240. if ( worldModel.GetEntity() && worldModel.GetEntity()->GetAnimator()->GetJointTransform( jointHandle, gameLocal.time, offset, axis ) ) {
  1241. offset = worldModel.GetEntity()->GetPhysics()->GetOrigin() + offset * worldModel.GetEntity()->GetPhysics()->GetAxis();
  1242. axis = axis * worldModel.GetEntity()->GetPhysics()->GetAxis();
  1243. return true;
  1244. }
  1245. }
  1246. offset = viewWeaponOrigin;
  1247. axis = viewWeaponAxis;
  1248. return false;
  1249. }
  1250. /*
  1251. ================
  1252. idWeapon::SetPushVelocity
  1253. ================
  1254. */
  1255. void idWeapon::SetPushVelocity( const idVec3 &pushVelocity ) {
  1256. this->pushVelocity = pushVelocity;
  1257. }
  1258. /***********************************************************************
  1259. State control/player interface
  1260. ***********************************************************************/
  1261. /*
  1262. ================
  1263. idWeapon::Think
  1264. ================
  1265. */
  1266. void idWeapon::Think( void ) {
  1267. // do nothing because the present is called from the player through PresentWeapon
  1268. }
  1269. /*
  1270. ================
  1271. idWeapon::Raise
  1272. ================
  1273. */
  1274. void idWeapon::Raise( void ) {
  1275. if ( isLinked ) {
  1276. WEAPON_RAISEWEAPON = true;
  1277. }
  1278. }
  1279. /*
  1280. ================
  1281. idWeapon::PutAway
  1282. ================
  1283. */
  1284. void idWeapon::PutAway( void ) {
  1285. hasBloodSplat = false;
  1286. if ( isLinked ) {
  1287. WEAPON_LOWERWEAPON = true;
  1288. }
  1289. }
  1290. /*
  1291. ================
  1292. idWeapon::Reload
  1293. NOTE: this is only for impulse-triggered reload, auto reload is scripted
  1294. ================
  1295. */
  1296. void idWeapon::Reload( void ) {
  1297. if ( isLinked ) {
  1298. WEAPON_RELOAD = true;
  1299. }
  1300. }
  1301. void idWeapon::Picker( void ) {
  1302. if ( isLinked ) {
  1303. WEAPON_PICKER = true;
  1304. }
  1305. }
  1306. /*
  1307. ================
  1308. idWeapon::LowerWeapon
  1309. ================
  1310. */
  1311. void idWeapon::LowerWeapon( void ) {
  1312. if ( !hide ) {
  1313. hideStart = 0.0f;
  1314. hideEnd = hideDistance;
  1315. if ( gameLocal.time - hideStartTime < hideTime ) {
  1316. hideStartTime = gameLocal.time - ( hideTime - ( gameLocal.time - hideStartTime ) );
  1317. } else {
  1318. hideStartTime = gameLocal.time;
  1319. }
  1320. hide = true;
  1321. }
  1322. }
  1323. /*
  1324. ================
  1325. idWeapon::RaiseWeapon
  1326. ================
  1327. */
  1328. void idWeapon::RaiseWeapon( void ) {
  1329. Show();
  1330. if ( hide ) {
  1331. hideStart = hideDistance;
  1332. hideEnd = 0.0f;
  1333. if ( gameLocal.time - hideStartTime < hideTime ) {
  1334. hideStartTime = gameLocal.time - ( hideTime - ( gameLocal.time - hideStartTime ) );
  1335. } else {
  1336. hideStartTime = gameLocal.time;
  1337. }
  1338. hide = false;
  1339. }
  1340. }
  1341. /*
  1342. ================
  1343. idWeapon::HideWeapon
  1344. ================
  1345. */
  1346. void idWeapon::HideWeapon( void ) {
  1347. Hide();
  1348. if ( worldModel.GetEntity() ) {
  1349. worldModel.GetEntity()->Hide();
  1350. }
  1351. muzzleFlashEnd = 0;
  1352. }
  1353. /*
  1354. ================
  1355. idWeapon::ShowWeapon
  1356. ================
  1357. */
  1358. void idWeapon::ShowWeapon( void ) {
  1359. Show();
  1360. if ( worldModel.GetEntity() ) {
  1361. worldModel.GetEntity()->Show();
  1362. }
  1363. if ( lightOn ) {
  1364. MuzzleFlashLight();
  1365. }
  1366. }
  1367. /*
  1368. ================
  1369. idWeapon::HideWorldModel
  1370. ================
  1371. */
  1372. void idWeapon::HideWorldModel( void ) {
  1373. if ( worldModel.GetEntity() ) {
  1374. worldModel.GetEntity()->Hide();
  1375. }
  1376. }
  1377. /*
  1378. ================
  1379. idWeapon::ShowWorldModel
  1380. ================
  1381. */
  1382. void idWeapon::ShowWorldModel( void ) {
  1383. if ( worldModel.GetEntity() ) {
  1384. worldModel.GetEntity()->Show();
  1385. }
  1386. }
  1387. /*
  1388. ================
  1389. idWeapon::OwnerDied
  1390. ================
  1391. */
  1392. void idWeapon::OwnerDied( void ) {
  1393. if ( isLinked ) {
  1394. SetState( "OwnerDied", 0 );
  1395. thread->Execute();
  1396. #ifdef _D3XP
  1397. // Update the grabber effects
  1398. if ( /*!gameLocal.isMultiplayer &&*/ grabberState != -1 ) {
  1399. grabber.Update( owner, hide );
  1400. }
  1401. #endif
  1402. }
  1403. Hide();
  1404. if ( worldModel.GetEntity() ) {
  1405. worldModel.GetEntity()->Hide();
  1406. }
  1407. // don't clear the weapon immediately since the owner might have killed himself by firing the weapon
  1408. // within the current stack frame
  1409. PostEventMS( &EV_Weapon_Clear, 0 );
  1410. }
  1411. /*
  1412. ================
  1413. idWeapon::BeginAttack
  1414. ================
  1415. */
  1416. void idWeapon::BeginAttack( void ) {
  1417. if ( status != WP_OUTOFAMMO ) {
  1418. lastAttack = gameLocal.time;
  1419. }
  1420. if ( !isLinked ) {
  1421. return;
  1422. }
  1423. if ( !WEAPON_ATTACK ) {
  1424. if ( sndHum && grabberState == -1 ) { // _D3XP :: don't stop grabber hum
  1425. StopSound( SND_CHANNEL_BODY, false );
  1426. }
  1427. }
  1428. WEAPON_ATTACK = true;
  1429. }
  1430. /*
  1431. ================
  1432. idWeapon::EndAttack
  1433. ================
  1434. */
  1435. void idWeapon::EndAttack( void ) {
  1436. if ( !WEAPON_ATTACK.IsLinked() ) {
  1437. return;
  1438. }
  1439. if ( WEAPON_ATTACK ) {
  1440. WEAPON_ATTACK = false;
  1441. if ( sndHum && grabberState == -1 ) { // _D3XP :: don't stop grabber hum
  1442. StartSoundShader( sndHum, SND_CHANNEL_BODY, 0, false, NULL );
  1443. }
  1444. }
  1445. }
  1446. /*
  1447. ================
  1448. idWeapon::isReady
  1449. ================
  1450. */
  1451. bool idWeapon::IsReady( void ) const {
  1452. return !hide && !IsHidden() && ( ( status == WP_RELOAD ) || ( status == WP_READY ) || ( status == WP_OUTOFAMMO ) );
  1453. }
  1454. /*
  1455. ================
  1456. idWeapon::IsReloading
  1457. ================
  1458. */
  1459. bool idWeapon::IsReloading( void ) const {
  1460. return ( status == WP_RELOAD );
  1461. }
  1462. /*
  1463. ================
  1464. idWeapon::IsHolstered
  1465. ================
  1466. */
  1467. bool idWeapon::IsHolstered( void ) const {
  1468. return ( status == WP_HOLSTERED );
  1469. }
  1470. /*
  1471. ================
  1472. idWeapon::ShowCrosshair
  1473. ================
  1474. */
  1475. bool idWeapon::ShowCrosshair( void ) const {
  1476. return !( state == idStr( WP_RISING ) || state == idStr( WP_LOWERING ) || state == idStr( WP_HOLSTERED ) );
  1477. }
  1478. /*
  1479. =====================
  1480. idWeapon::CanDrop
  1481. =====================
  1482. */
  1483. bool idWeapon::CanDrop( void ) const {
  1484. if ( !weaponDef || !worldModel.GetEntity() ) {
  1485. return false;
  1486. }
  1487. const char *classname = weaponDef->dict.GetString( "def_dropItem" );
  1488. if ( !classname[ 0 ] ) {
  1489. return false;
  1490. }
  1491. return true;
  1492. }
  1493. /*
  1494. ================
  1495. idWeapon::WeaponStolen
  1496. ================
  1497. */
  1498. void idWeapon::WeaponStolen( void ) {
  1499. assert( !gameLocal.isClient );
  1500. if ( projectileEnt ) {
  1501. if ( isLinked ) {
  1502. SetState( "WeaponStolen", 0 );
  1503. thread->Execute();
  1504. }
  1505. projectileEnt = NULL;
  1506. }
  1507. // set to holstered so we can switch weapons right away
  1508. status = WP_HOLSTERED;
  1509. HideWeapon();
  1510. }
  1511. /*
  1512. =====================
  1513. idWeapon::DropItem
  1514. =====================
  1515. */
  1516. idEntity * idWeapon::DropItem( const idVec3 &velocity, int activateDelay, int removeDelay, bool died ) {
  1517. if ( !weaponDef || !worldModel.GetEntity() ) {
  1518. return NULL;
  1519. }
  1520. if ( !allowDrop ) {
  1521. return NULL;
  1522. }
  1523. const char *classname = weaponDef->dict.GetString( "def_dropItem" );
  1524. if ( !classname[0] ) {
  1525. return NULL;
  1526. }
  1527. StopSound( SND_CHANNEL_BODY, true );
  1528. StopSound( SND_CHANNEL_BODY3, true );
  1529. return idMoveableItem::DropItem( classname, worldModel.GetEntity()->GetPhysics()->GetOrigin(), worldModel.GetEntity()->GetPhysics()->GetAxis(), velocity, activateDelay, removeDelay );
  1530. }
  1531. /***********************************************************************
  1532. Script state management
  1533. ***********************************************************************/
  1534. /*
  1535. =====================
  1536. idWeapon::SetState
  1537. =====================
  1538. */
  1539. void idWeapon::SetState( const char *statename, int blendFrames ) {
  1540. const function_t *func;
  1541. if ( !isLinked ) {
  1542. return;
  1543. }
  1544. func = scriptObject.GetFunction( statename );
  1545. if ( !func ) {
  1546. assert( 0 );
  1547. gameLocal.Error( "Can't find function '%s' in object '%s'", statename, scriptObject.GetTypeName() );
  1548. }
  1549. thread->CallFunction( this, func, true );
  1550. state = statename;
  1551. animBlendFrames = blendFrames;
  1552. if ( g_debugWeapon.GetBool() ) {
  1553. gameLocal.Printf( "%d: weapon state : %s\n", gameLocal.time, statename );
  1554. }
  1555. idealState = "";
  1556. }
  1557. /***********************************************************************
  1558. Particles/Effects
  1559. ***********************************************************************/
  1560. /*
  1561. ================
  1562. idWeapon::UpdateNozzelFx
  1563. ================
  1564. */
  1565. void idWeapon::UpdateNozzleFx( void ) {
  1566. if ( !nozzleFx ) {
  1567. return;
  1568. }
  1569. //
  1570. // shader parms
  1571. //
  1572. int la = gameLocal.time - lastAttack + 1;
  1573. float s = 1.0f;
  1574. float l = 0.0f;
  1575. if ( la < nozzleFxFade ) {
  1576. s = ((float)la / nozzleFxFade);
  1577. l = 1.0f - s;
  1578. }
  1579. renderEntity.shaderParms[5] = s;
  1580. renderEntity.shaderParms[6] = l;
  1581. if ( ventLightJointView == INVALID_JOINT ) {
  1582. return;
  1583. }
  1584. //
  1585. // vent light
  1586. //
  1587. if ( nozzleGlowHandle == -1 ) {
  1588. memset(&nozzleGlow, 0, sizeof(nozzleGlow));
  1589. if ( owner ) {
  1590. nozzleGlow.allowLightInViewID = owner->entityNumber+1;
  1591. }
  1592. nozzleGlow.pointLight = true;
  1593. nozzleGlow.noShadows = true;
  1594. nozzleGlow.lightRadius.x = nozzleGlowRadius;
  1595. nozzleGlow.lightRadius.y = nozzleGlowRadius;
  1596. nozzleGlow.lightRadius.z = nozzleGlowRadius;
  1597. nozzleGlow.shader = nozzleGlowShader;
  1598. nozzleGlow.shaderParms[ SHADERPARM_TIMESCALE ] = 1.0f;
  1599. nozzleGlow.shaderParms[ SHADERPARM_TIMEOFFSET ] = -MS2SEC( gameLocal.time );
  1600. GetGlobalJointTransform( true, ventLightJointView, nozzleGlow.origin, nozzleGlow.axis );
  1601. nozzleGlowHandle = gameRenderWorld->AddLightDef(&nozzleGlow);
  1602. }
  1603. GetGlobalJointTransform( true, ventLightJointView, nozzleGlow.origin, nozzleGlow.axis );
  1604. nozzleGlow.shaderParms[ SHADERPARM_RED ] = nozzleGlowColor.x * s;
  1605. nozzleGlow.shaderParms[ SHADERPARM_GREEN ] = nozzleGlowColor.y * s;
  1606. nozzleGlow.shaderParms[ SHADERPARM_BLUE ] = nozzleGlowColor.z * s;
  1607. gameRenderWorld->UpdateLightDef(nozzleGlowHandle, &nozzleGlow);
  1608. }
  1609. /*
  1610. ================
  1611. idWeapon::BloodSplat
  1612. ================
  1613. */
  1614. bool idWeapon::BloodSplat( float size ) {
  1615. float s, c;
  1616. idMat3 localAxis, axistemp;
  1617. idVec3 localOrigin, normal;
  1618. if ( hasBloodSplat ) {
  1619. return true;
  1620. }
  1621. hasBloodSplat = true;
  1622. if ( modelDefHandle < 0 ) {
  1623. return false;
  1624. }
  1625. if ( !GetGlobalJointTransform( true, ejectJointView, localOrigin, localAxis ) ) {
  1626. return false;
  1627. }
  1628. localOrigin[0] += gameLocal.random.RandomFloat() * -10.0f;
  1629. localOrigin[1] += gameLocal.random.RandomFloat() * 1.0f;
  1630. localOrigin[2] += gameLocal.random.RandomFloat() * -2.0f;
  1631. normal = idVec3( gameLocal.random.CRandomFloat(), -gameLocal.random.RandomFloat(), -1 );
  1632. normal.Normalize();
  1633. idMath::SinCos16( gameLocal.random.RandomFloat() * idMath::TWO_PI, s, c );
  1634. localAxis[2] = -normal;
  1635. localAxis[2].NormalVectors( axistemp[0], axistemp[1] );
  1636. localAxis[0] = axistemp[ 0 ] * c + axistemp[ 1 ] * -s;
  1637. localAxis[1] = axistemp[ 0 ] * -s + axistemp[ 1 ] * -c;
  1638. localAxis[0] *= 1.0f / size;
  1639. localAxis[1] *= 1.0f / size;
  1640. idPlane localPlane[2];
  1641. localPlane[0] = localAxis[0];
  1642. localPlane[0][3] = -(localOrigin * localAxis[0]) + 0.5f;
  1643. localPlane[1] = localAxis[1];
  1644. localPlane[1][3] = -(localOrigin * localAxis[1]) + 0.5f;
  1645. const idMaterial *mtr = declManager->FindMaterial( "textures/decals/duffysplatgun" );
  1646. gameRenderWorld->ProjectOverlay( modelDefHandle, localPlane, mtr );
  1647. return true;
  1648. }
  1649. /***********************************************************************
  1650. Visual presentation
  1651. ***********************************************************************/
  1652. /*
  1653. ================
  1654. idWeapon::MuzzleRise
  1655. The machinegun and chaingun will incrementally back up as they are being fired
  1656. ================
  1657. */
  1658. void idWeapon::MuzzleRise( idVec3 &origin, idMat3 &axis ) {
  1659. int time;
  1660. float amount;
  1661. idAngles ang;
  1662. idVec3 offset;
  1663. time = kick_endtime - gameLocal.time;
  1664. if ( time <= 0 ) {
  1665. return;
  1666. }
  1667. if ( muzzle_kick_maxtime <= 0 ) {
  1668. return;
  1669. }
  1670. if ( time > muzzle_kick_maxtime ) {
  1671. time = muzzle_kick_maxtime;
  1672. }
  1673. amount = ( float )time / ( float )muzzle_kick_maxtime;
  1674. ang = muzzle_kick_angles * amount;
  1675. offset = muzzle_kick_offset * amount;
  1676. origin = origin - axis * offset;
  1677. axis = ang.ToMat3() * axis;
  1678. }
  1679. /*
  1680. ================
  1681. idWeapon::ConstructScriptObject
  1682. Called during idEntity::Spawn. Calls the constructor on the script object.
  1683. Can be overridden by subclasses when a thread doesn't need to be allocated.
  1684. ================
  1685. */
  1686. idThread *idWeapon::ConstructScriptObject( void ) {
  1687. const function_t *constructor;
  1688. thread->EndThread();
  1689. // call script object's constructor
  1690. constructor = scriptObject.GetConstructor();
  1691. if ( !constructor ) {
  1692. gameLocal.Error( "Missing constructor on '%s' for weapon", scriptObject.GetTypeName() );
  1693. }
  1694. // init the script object's data
  1695. scriptObject.ClearObject();
  1696. thread->CallFunction( this, constructor, true );
  1697. thread->Execute();
  1698. return thread;
  1699. }
  1700. /*
  1701. ================
  1702. idWeapon::DeconstructScriptObject
  1703. Called during idEntity::~idEntity. Calls the destructor on the script object.
  1704. Can be overridden by subclasses when a thread doesn't need to be allocated.
  1705. Not called during idGameLocal::MapShutdown.
  1706. ================
  1707. */
  1708. void idWeapon::DeconstructScriptObject( void ) {
  1709. const function_t *destructor;
  1710. if ( !thread ) {
  1711. return;
  1712. }
  1713. // don't bother calling the script object's destructor on map shutdown
  1714. if ( gameLocal.GameState() == GAMESTATE_SHUTDOWN ) {
  1715. return;
  1716. }
  1717. thread->EndThread();
  1718. // call script object's destructor
  1719. destructor = scriptObject.GetDestructor();
  1720. if ( destructor ) {
  1721. // start a thread that will run immediately and end
  1722. thread->CallFunction( this, destructor, true );
  1723. thread->Execute();
  1724. thread->EndThread();
  1725. }
  1726. // clear out the object's memory
  1727. scriptObject.ClearObject();
  1728. }
  1729. /*
  1730. ================
  1731. idWeapon::UpdateScript
  1732. ================
  1733. */
  1734. void idWeapon::UpdateScript( void ) {
  1735. int count;
  1736. if ( !isLinked ) {
  1737. return;
  1738. }
  1739. // only update the script on new frames
  1740. if ( !gameLocal.isNewFrame ) {
  1741. return;
  1742. }
  1743. if ( idealState.Length() ) {
  1744. SetState( idealState, animBlendFrames );
  1745. }
  1746. // update script state, which may call Event_LaunchProjectiles, among other things
  1747. count = 3;
  1748. while( ( thread->Execute() || idealState.Length() ) && count-- ) {
  1749. // happens for weapons with no clip (like grenades)
  1750. if ( idealState.Length() ) {
  1751. SetState( idealState, animBlendFrames );
  1752. }
  1753. }
  1754. WEAPON_RELOAD = false;
  1755. WEAPON_PICKER = false;
  1756. }
  1757. /*
  1758. ================
  1759. idWeapon::AlertMonsters
  1760. ================
  1761. */
  1762. void idWeapon::AlertMonsters( void ) {
  1763. trace_t tr;
  1764. idEntity *ent;
  1765. idVec3 end = muzzleFlash.origin + muzzleFlash.axis * muzzleFlash.target;
  1766. gameLocal.clip.TracePoint( tr, muzzleFlash.origin, end, CONTENTS_OPAQUE | MASK_SHOT_RENDERMODEL | CONTENTS_FLASHLIGHT_TRIGGER, owner );
  1767. if ( g_debugWeapon.GetBool() ) {
  1768. gameRenderWorld->DebugLine( colorYellow, muzzleFlash.origin, end, 0 );
  1769. gameRenderWorld->DebugArrow( colorGreen, muzzleFlash.origin, tr.endpos, 2, 0 );
  1770. }
  1771. if ( tr.fraction < 1.0f ) {
  1772. ent = gameLocal.GetTraceEntity( tr );
  1773. if ( ent->IsType( idAI::Type ) ) {
  1774. static_cast<idAI *>( ent )->TouchedByFlashlight( owner );
  1775. } else if ( ent->IsType( idTrigger::Type ) ) {
  1776. ent->Signal( SIG_TOUCH );
  1777. ent->ProcessEvent( &EV_Touch, owner, &tr );
  1778. }
  1779. }
  1780. // jitter the trace to try to catch cases where a trace down the center doesn't hit the monster
  1781. end += muzzleFlash.axis * muzzleFlash.right * idMath::Sin16( MS2SEC( gameLocal.time ) * 31.34f );
  1782. end += muzzleFlash.axis * muzzleFlash.up * idMath::Sin16( MS2SEC( gameLocal.time ) * 12.17f );
  1783. gameLocal.clip.TracePoint( tr, muzzleFlash.origin, end, CONTENTS_OPAQUE | MASK_SHOT_RENDERMODEL | CONTENTS_FLASHLIGHT_TRIGGER, owner );
  1784. if ( g_debugWeapon.GetBool() ) {
  1785. gameRenderWorld->DebugLine( colorYellow, muzzleFlash.origin, end, 0 );
  1786. gameRenderWorld->DebugArrow( colorGreen, muzzleFlash.origin, tr.endpos, 2, 0 );
  1787. }
  1788. if ( tr.fraction < 1.0f ) {
  1789. ent = gameLocal.GetTraceEntity( tr );
  1790. if ( ent->IsType( idAI::Type ) ) {
  1791. static_cast<idAI *>( ent )->TouchedByFlashlight( owner );
  1792. } else if ( ent->IsType( idTrigger::Type ) ) {
  1793. ent->Signal( SIG_TOUCH );
  1794. ent->ProcessEvent( &EV_Touch, owner, &tr );
  1795. }
  1796. }
  1797. }
  1798. /*
  1799. ================
  1800. idWeapon::PresentWeapon
  1801. ================
  1802. */
  1803. void idWeapon::PresentWeapon( bool showViewModel ) {
  1804. playerViewOrigin = owner->firstPersonViewOrigin;
  1805. playerViewAxis = owner->firstPersonViewAxis;
  1806. // calculate weapon position based on player movement bobbing
  1807. owner->CalculateViewWeaponPos( viewWeaponOrigin, viewWeaponAxis );
  1808. // hide offset is for dropping the gun when approaching a GUI or NPC
  1809. // This is simpler to manage than doing the weapon put-away animation
  1810. if ( gameLocal.time - hideStartTime < hideTime ) {
  1811. float frac = ( float )( gameLocal.time - hideStartTime ) / ( float )hideTime;
  1812. if ( hideStart < hideEnd ) {
  1813. frac = 1.0f - frac;
  1814. frac = 1.0f - frac * frac;
  1815. } else {
  1816. frac = frac * frac;
  1817. }
  1818. hideOffset = hideStart + ( hideEnd - hideStart ) * frac;
  1819. } else {
  1820. hideOffset = hideEnd;
  1821. if ( hide && disabled ) {
  1822. Hide();
  1823. }
  1824. }
  1825. viewWeaponOrigin += hideOffset * viewWeaponAxis[ 2 ];
  1826. // kick up based on repeat firing
  1827. MuzzleRise( viewWeaponOrigin, viewWeaponAxis );
  1828. // set the physics position and orientation
  1829. GetPhysics()->SetOrigin( viewWeaponOrigin );
  1830. GetPhysics()->SetAxis( viewWeaponAxis );
  1831. UpdateVisuals();
  1832. // update the weapon script
  1833. UpdateScript();
  1834. UpdateGUI();
  1835. // update animation
  1836. UpdateAnimation();
  1837. // only show the surface in player view
  1838. renderEntity.allowSurfaceInViewID = owner->entityNumber+1;
  1839. // crunch the depth range so it never pokes into walls this breaks the machine gun gui
  1840. renderEntity.weaponDepthHack = true;
  1841. // present the model
  1842. if ( showViewModel ) {
  1843. Present();
  1844. } else {
  1845. FreeModelDef();
  1846. }
  1847. if ( worldModel.GetEntity() && worldModel.GetEntity()->GetRenderEntity() ) {
  1848. // deal with the third-person visible world model
  1849. // don't show shadows of the world model in first person
  1850. if ( gameLocal.isMultiplayer || g_showPlayerShadow.GetBool() || pm_thirdPerson.GetBool() ) {
  1851. worldModel.GetEntity()->GetRenderEntity()->suppressShadowInViewID = 0;
  1852. } else {
  1853. worldModel.GetEntity()->GetRenderEntity()->suppressShadowInViewID = owner->entityNumber+1;
  1854. worldModel.GetEntity()->GetRenderEntity()->suppressShadowInLightID = LIGHTID_VIEW_MUZZLE_FLASH + owner->entityNumber;
  1855. }
  1856. }
  1857. if ( nozzleFx ) {
  1858. UpdateNozzleFx();
  1859. }
  1860. // muzzle smoke
  1861. if ( showViewModel && !disabled && weaponSmoke && ( weaponSmokeStartTime != 0 ) ) {
  1862. // use the barrel joint if available
  1863. #ifdef _D3XP
  1864. if(smokeJointView != INVALID_JOINT) {
  1865. GetGlobalJointTransform( true, smokeJointView, muzzleOrigin, muzzleAxis );
  1866. } else if (barrelJointView != INVALID_JOINT) {
  1867. GetGlobalJointTransform( true, barrelJointView, muzzleOrigin, muzzleAxis );
  1868. #else
  1869. if ( barrelJointView ) {
  1870. GetGlobalJointTransform( true, barrelJointView, muzzleOrigin, muzzleAxis );
  1871. #endif
  1872. } else {
  1873. // default to going straight out the view
  1874. muzzleOrigin = playerViewOrigin;
  1875. muzzleAxis = playerViewAxis;
  1876. }
  1877. // spit out a particle
  1878. if ( !gameLocal.smokeParticles->EmitSmoke( weaponSmoke, weaponSmokeStartTime, gameLocal.random.RandomFloat(), muzzleOrigin, muzzleAxis, timeGroup /*_D3XP*/ ) ) {
  1879. weaponSmokeStartTime = ( continuousSmoke ) ? gameLocal.time : 0;
  1880. }
  1881. }
  1882. if ( showViewModel && strikeSmoke && strikeSmokeStartTime != 0 ) {
  1883. // spit out a particle
  1884. if ( !gameLocal.smokeParticles->EmitSmoke( strikeSmoke, strikeSmokeStartTime, gameLocal.random.RandomFloat(), strikePos, strikeAxis, timeGroup /*_D3XP*/ ) ) {
  1885. strikeSmokeStartTime = 0;
  1886. }
  1887. }
  1888. #ifdef _D3XP
  1889. if ( showViewModel && !hide ) {
  1890. for( int i = 0; i < weaponParticles.Num(); i++ ) {
  1891. WeaponParticle_t* part = weaponParticles.GetIndex(i);
  1892. if(part->active) {
  1893. if(part->smoke) {
  1894. if(part->joint != INVALID_JOINT) {
  1895. GetGlobalJointTransform( true, part->joint, muzzleOrigin, muzzleAxis );
  1896. } else {
  1897. // default to going straight out the view
  1898. muzzleOrigin = playerViewOrigin;
  1899. muzzleAxis = playerViewAxis;
  1900. }
  1901. if ( !gameLocal.smokeParticles->EmitSmoke( part->particle, part->startTime, gameLocal.random.RandomFloat(), muzzleOrigin, muzzleAxis, timeGroup /*_D3XP*/ ) ) {
  1902. part->active = false; // all done
  1903. part->startTime = 0;
  1904. }
  1905. } else {
  1906. //Manually update the position of the emitter so it follows the weapon
  1907. renderEntity_t* rendEnt = part->emitter->GetRenderEntity();
  1908. GetGlobalJointTransform( true, part->joint, rendEnt->origin, rendEnt->axis );
  1909. if ( part->emitter->GetModelDefHandle() != -1 ) {
  1910. gameRenderWorld->UpdateEntityDef( part->emitter->GetModelDefHandle(), rendEnt );
  1911. }
  1912. }
  1913. }
  1914. }
  1915. for(int i = 0; i < weaponLights.Num(); i++) {
  1916. WeaponLight_t* light = weaponLights.GetIndex(i);
  1917. if(light->active) {
  1918. GetGlobalJointTransform( true, light->joint, light->light.origin, light->light.axis );
  1919. if ( ( light->lightHandle != -1 ) ) {
  1920. gameRenderWorld->UpdateLightDef( light->lightHandle, &light->light );
  1921. } else {
  1922. light->lightHandle = gameRenderWorld->AddLightDef( &light->light );
  1923. }
  1924. }
  1925. }
  1926. }
  1927. // Update the grabber effects
  1928. if ( grabberState != -1 ) {
  1929. grabberState = grabber.Update( owner, hide );
  1930. }
  1931. #endif
  1932. // remove the muzzle flash light when it's done
  1933. if ( ( !lightOn && ( gameLocal.time >= muzzleFlashEnd ) ) || IsHidden() ) {
  1934. if ( muzzleFlashHandle != -1 ) {
  1935. gameRenderWorld->FreeLightDef( muzzleFlashHandle );
  1936. muzzleFlashHandle = -1;
  1937. }
  1938. if ( worldMuzzleFlashHandle != -1 ) {
  1939. gameRenderWorld->FreeLightDef( worldMuzzleFlashHandle );
  1940. worldMuzzleFlashHandle = -1;
  1941. }
  1942. }
  1943. // update the muzzle flash light, so it moves with the gun
  1944. if ( muzzleFlashHandle != -1 ) {
  1945. UpdateFlashPosition();
  1946. gameRenderWorld->UpdateLightDef( muzzleFlashHandle, &muzzleFlash );
  1947. gameRenderWorld->UpdateLightDef( worldMuzzleFlashHandle, &worldMuzzleFlash );
  1948. // wake up monsters with the flashlight
  1949. if ( !gameLocal.isMultiplayer && lightOn && !owner->fl.notarget ) {
  1950. AlertMonsters();
  1951. }
  1952. }
  1953. // update the gui light
  1954. if ( guiLight.lightRadius[0] && guiLightJointView != INVALID_JOINT ) {
  1955. GetGlobalJointTransform( true, guiLightJointView, guiLight.origin, guiLight.axis );
  1956. if ( ( guiLightHandle != -1 ) ) {
  1957. gameRenderWorld->UpdateLightDef( guiLightHandle, &guiLight );
  1958. } else {
  1959. guiLightHandle = gameRenderWorld->AddLightDef( &guiLight );
  1960. }
  1961. }
  1962. if ( status != WP_READY && sndHum ) {
  1963. StopSound( SND_CHANNEL_BODY, false );
  1964. }
  1965. UpdateSound();
  1966. }
  1967. /*
  1968. ================
  1969. idWeapon::EnterCinematic
  1970. ================
  1971. */
  1972. void idWeapon::EnterCinematic( void ) {
  1973. StopSound( SND_CHANNEL_ANY, false );
  1974. if ( isLinked ) {
  1975. SetState( "EnterCinematic", 0 );
  1976. thread->Execute();
  1977. WEAPON_ATTACK = false;
  1978. WEAPON_RELOAD = false;
  1979. WEAPON_NETRELOAD = false;
  1980. WEAPON_NETENDRELOAD = false;
  1981. WEAPON_NETFIRING = false;
  1982. WEAPON_RAISEWEAPON = false;
  1983. WEAPON_LOWERWEAPON = false;
  1984. #ifdef _D3XP
  1985. grabber.Update( this->GetOwner(), true );
  1986. #endif
  1987. }
  1988. disabled = true;
  1989. LowerWeapon();
  1990. }
  1991. /*
  1992. ================
  1993. idWeapon::ExitCinematic
  1994. ================
  1995. */
  1996. void idWeapon::ExitCinematic( void ) {
  1997. disabled = false;
  1998. if ( isLinked ) {
  1999. SetState( "ExitCinematic", 0 );
  2000. thread->Execute();
  2001. }
  2002. RaiseWeapon();
  2003. }
  2004. /*
  2005. ================
  2006. idWeapon::NetCatchup
  2007. ================
  2008. */
  2009. void idWeapon::NetCatchup( void ) {
  2010. if ( isLinked ) {
  2011. SetState( "NetCatchup", 0 );
  2012. thread->Execute();
  2013. }
  2014. }
  2015. /*
  2016. ================
  2017. idWeapon::GetZoomFov
  2018. ================
  2019. */
  2020. int idWeapon::GetZoomFov( void ) {
  2021. return zoomFov;
  2022. }
  2023. /*
  2024. ================
  2025. idWeapon::GetWeaponAngleOffsets
  2026. ================
  2027. */
  2028. void idWeapon::GetWeaponAngleOffsets( int *average, float *scale, float *max ) {
  2029. *average = weaponAngleOffsetAverages;
  2030. *scale = weaponAngleOffsetScale;
  2031. *max = weaponAngleOffsetMax;
  2032. }
  2033. /*
  2034. ================
  2035. idWeapon::GetWeaponTimeOffsets
  2036. ================
  2037. */
  2038. void idWeapon::GetWeaponTimeOffsets( float *time, float *scale ) {
  2039. *time = weaponOffsetTime;
  2040. *scale = weaponOffsetScale;
  2041. }
  2042. /***********************************************************************
  2043. Ammo
  2044. ***********************************************************************/
  2045. /*
  2046. ================
  2047. idWeapon::GetAmmoNumForName
  2048. ================
  2049. */
  2050. ammo_t idWeapon::GetAmmoNumForName( const char *ammoname ) {
  2051. int num;
  2052. const idDict *ammoDict;
  2053. assert( ammoname );
  2054. ammoDict = gameLocal.FindEntityDefDict( "ammo_types", false );
  2055. if ( !ammoDict ) {
  2056. gameLocal.Warning( "Could not find entity definition for 'ammo_types'\n" );
  2057. }
  2058. if ( !ammoname[ 0 ] ) {
  2059. return 0;
  2060. }
  2061. if ( !ammoDict->GetInt( ammoname, "-1", num ) ) {
  2062. #ifdef _D3XP
  2063. //Lets look in a game specific ammo type definition for the weapon
  2064. idStr gamedir;
  2065. int i;
  2066. for ( i = 0; i < 2; i++ ) {
  2067. if ( i == 0 ) {
  2068. gamedir = cvarSystem->GetCVarString( "fs_game_base" );
  2069. } else if ( i == 1 ) {
  2070. gamedir = cvarSystem->GetCVarString( "fs_game" );
  2071. }
  2072. if ( gamedir.Length() > 0 ) {
  2073. ammoDict = gameLocal.FindEntityDefDict( va("ammo_types_%s", gamedir.c_str()), false );
  2074. if ( ammoDict ) {
  2075. if ( ammoDict->GetInt( ammoname, "-1", num ) ) {
  2076. break;
  2077. }
  2078. }
  2079. }
  2080. }
  2081. if ( i == 2 ) {
  2082. gameLocal.Error( "Unknown ammo type '%s'", ammoname );
  2083. }
  2084. #endif
  2085. }
  2086. if ( ( num < 0 ) || ( num >= AMMO_NUMTYPES ) ) {
  2087. gameLocal.Error( "Ammo type '%s' value out of range. Maximum ammo types is %d.\n", ammoname, AMMO_NUMTYPES );
  2088. }
  2089. return ( ammo_t )num;
  2090. }
  2091. /*
  2092. ================
  2093. idWeapon::GetAmmoNameForNum
  2094. ================
  2095. */
  2096. const char *idWeapon::GetAmmoNameForNum( ammo_t ammonum ) {
  2097. int i;
  2098. int num;
  2099. const idDict *ammoDict;
  2100. const idKeyValue *kv;
  2101. char text[ 32 ];
  2102. ammoDict = gameLocal.FindEntityDefDict( "ammo_types", false );
  2103. if ( !ammoDict ) {
  2104. gameLocal.Warning( "Could not find entity definition for 'ammo_types'\n" );
  2105. }
  2106. sprintf( text, "%d", ammonum );
  2107. num = ammoDict->GetNumKeyVals();
  2108. for( i = 0; i < num; i++ ) {
  2109. kv = ammoDict->GetKeyVal( i );
  2110. if ( kv->GetValue() == text ) {
  2111. return kv->GetKey();
  2112. }
  2113. }
  2114. #ifdef _D3XP
  2115. // Look in the game specific ammo types
  2116. idStr gamedir;
  2117. for ( i = 0; i < 2; i++ ) {
  2118. if ( i == 0 ) {
  2119. gamedir = cvarSystem->GetCVarString( "fs_game_base" );
  2120. } else if ( i == 1 ) {
  2121. gamedir = cvarSystem->GetCVarString( "fs_game" );
  2122. }
  2123. if ( gamedir.Length() > 0 ) {
  2124. ammoDict = gameLocal.FindEntityDefDict( va("ammo_types_%s", gamedir.c_str()), false );
  2125. if ( ammoDict ) {
  2126. num = ammoDict->GetNumKeyVals();
  2127. for( i = 0; i < num; i++ ) {
  2128. kv = ammoDict->GetKeyVal( i );
  2129. if ( kv->GetValue() == text ) {
  2130. return kv->GetKey();
  2131. }
  2132. }
  2133. }
  2134. }
  2135. }
  2136. #endif
  2137. return NULL;
  2138. }
  2139. /*
  2140. ================
  2141. idWeapon::GetAmmoPickupNameForNum
  2142. ================
  2143. */
  2144. const char *idWeapon::GetAmmoPickupNameForNum( ammo_t ammonum ) {
  2145. int i;
  2146. int num;
  2147. const idDict *ammoDict;
  2148. const idKeyValue *kv;
  2149. ammoDict = gameLocal.FindEntityDefDict( "ammo_names", false );
  2150. if ( !ammoDict ) {
  2151. gameLocal.Error( "Could not find entity definition for 'ammo_names'\n" );
  2152. }
  2153. const char *name = GetAmmoNameForNum( ammonum );
  2154. if ( name && *name ) {
  2155. num = ammoDict->GetNumKeyVals();
  2156. for( i = 0; i < num; i++ ) {
  2157. kv = ammoDict->GetKeyVal( i );
  2158. if ( idStr::Icmp( kv->GetKey(), name) == 0 ) {
  2159. return kv->GetValue();
  2160. }
  2161. }
  2162. }
  2163. return "";
  2164. }
  2165. /*
  2166. ================
  2167. idWeapon::AmmoAvailable
  2168. ================
  2169. */
  2170. int idWeapon::AmmoAvailable( void ) const {
  2171. if ( owner ) {
  2172. return owner->inventory.HasAmmo( ammoType, ammoRequired );
  2173. } else {
  2174. return 0;
  2175. }
  2176. }
  2177. /*
  2178. ================
  2179. idWeapon::AmmoInClip
  2180. ================
  2181. */
  2182. int idWeapon::AmmoInClip( void ) const {
  2183. return ammoClip;
  2184. }
  2185. /*
  2186. ================
  2187. idWeapon::ResetAmmoClip
  2188. ================
  2189. */
  2190. void idWeapon::ResetAmmoClip( void ) {
  2191. ammoClip = -1;
  2192. }
  2193. /*
  2194. ================
  2195. idWeapon::GetAmmoType
  2196. ================
  2197. */
  2198. ammo_t idWeapon::GetAmmoType( void ) const {
  2199. return ammoType;
  2200. }
  2201. /*
  2202. ================
  2203. idWeapon::ClipSize
  2204. ================
  2205. */
  2206. int idWeapon::ClipSize( void ) const {
  2207. return clipSize;
  2208. }
  2209. /*
  2210. ================
  2211. idWeapon::LowAmmo
  2212. ================
  2213. */
  2214. int idWeapon::LowAmmo() const {
  2215. return lowAmmo;
  2216. }
  2217. /*
  2218. ================
  2219. idWeapon::AmmoRequired
  2220. ================
  2221. */
  2222. int idWeapon::AmmoRequired( void ) const {
  2223. return ammoRequired;
  2224. }
  2225. #ifdef _D3XP
  2226. /*
  2227. ================
  2228. idWeapon::GetGrabberState
  2229. Returns the current grabberState
  2230. ================
  2231. */
  2232. int idWeapon::GetGrabberState() const {
  2233. return grabberState;
  2234. }
  2235. /*
  2236. ================
  2237. idWeapon::AmmoCount
  2238. Returns the total number of rounds regardless of the required ammo
  2239. ================
  2240. */
  2241. int idWeapon::AmmoCount() const {
  2242. if ( owner ) {
  2243. return owner->inventory.HasAmmo( ammoType, 1 );
  2244. } else {
  2245. return 0;
  2246. }
  2247. }
  2248. #endif
  2249. /*
  2250. ================
  2251. idWeapon::WriteToSnapshot
  2252. ================
  2253. */
  2254. void idWeapon::WriteToSnapshot( idBitMsgDelta &msg ) const {
  2255. msg.WriteBits( ammoClip, ASYNC_PLAYER_INV_CLIP_BITS );
  2256. msg.WriteBits( worldModel.GetSpawnId(), 32 );
  2257. msg.WriteBits( lightOn, 1 );
  2258. msg.WriteBits( isFiring ? 1 : 0, 1 );
  2259. }
  2260. /*
  2261. ================
  2262. idWeapon::ReadFromSnapshot
  2263. ================
  2264. */
  2265. void idWeapon::ReadFromSnapshot( const idBitMsgDelta &msg ) {
  2266. ammoClip = msg.ReadBits( ASYNC_PLAYER_INV_CLIP_BITS );
  2267. worldModel.SetSpawnId( msg.ReadBits( 32 ) );
  2268. bool snapLight = msg.ReadBits( 1 ) != 0;
  2269. isFiring = msg.ReadBits( 1 ) != 0;
  2270. // WEAPON_NETFIRING is only turned on for other clients we're predicting. not for local client
  2271. if ( owner && gameLocal.localClientNum != owner->entityNumber && WEAPON_NETFIRING.IsLinked() ) {
  2272. // immediately go to the firing state so we don't skip fire animations
  2273. if ( !WEAPON_NETFIRING && isFiring ) {
  2274. idealState = "Fire";
  2275. }
  2276. // immediately switch back to idle
  2277. if ( WEAPON_NETFIRING && !isFiring ) {
  2278. idealState = "Idle";
  2279. }
  2280. WEAPON_NETFIRING = isFiring;
  2281. }
  2282. if ( snapLight != lightOn ) {
  2283. Reload();
  2284. }
  2285. }
  2286. /*
  2287. ================
  2288. idWeapon::ClientReceiveEvent
  2289. ================
  2290. */
  2291. bool idWeapon::ClientReceiveEvent( int event, int time, const idBitMsg &msg ) {
  2292. switch( event ) {
  2293. case EVENT_RELOAD: {
  2294. if ( gameLocal.time - time < 1000 ) {
  2295. if ( WEAPON_NETRELOAD.IsLinked() ) {
  2296. WEAPON_NETRELOAD = true;
  2297. WEAPON_NETENDRELOAD = false;
  2298. }
  2299. }
  2300. return true;
  2301. }
  2302. case EVENT_ENDRELOAD: {
  2303. if ( WEAPON_NETENDRELOAD.IsLinked() ) {
  2304. WEAPON_NETENDRELOAD = true;
  2305. }
  2306. return true;
  2307. }
  2308. case EVENT_CHANGESKIN: {
  2309. int index = gameLocal.ClientRemapDecl( DECL_SKIN, msg.ReadLong() );
  2310. renderEntity.customSkin = ( index != -1 ) ? static_cast<const idDeclSkin *>( declManager->DeclByIndex( DECL_SKIN, index ) ) : NULL;
  2311. UpdateVisuals();
  2312. if ( worldModel.GetEntity() ) {
  2313. worldModel.GetEntity()->SetSkin( renderEntity.customSkin );
  2314. }
  2315. return true;
  2316. }
  2317. default: {
  2318. return idEntity::ClientReceiveEvent( event, time, msg );
  2319. }
  2320. }
  2321. return false;
  2322. }
  2323. /***********************************************************************
  2324. Script events
  2325. ***********************************************************************/
  2326. /*
  2327. ===============
  2328. idWeapon::Event_Clear
  2329. ===============
  2330. */
  2331. void idWeapon::Event_Clear( void ) {
  2332. Clear();
  2333. }
  2334. /*
  2335. ===============
  2336. idWeapon::Event_GetOwner
  2337. ===============
  2338. */
  2339. void idWeapon::Event_GetOwner( void ) {
  2340. idThread::ReturnEntity( owner );
  2341. }
  2342. /*
  2343. ===============
  2344. idWeapon::Event_WeaponState
  2345. ===============
  2346. */
  2347. void idWeapon::Event_WeaponState( const char *statename, int blendFrames ) {
  2348. const function_t *func;
  2349. func = scriptObject.GetFunction( statename );
  2350. if ( !func ) {
  2351. assert( 0 );
  2352. gameLocal.Error( "Can't find function '%s' in object '%s'", statename, scriptObject.GetTypeName() );
  2353. }
  2354. idealState = statename;
  2355. if ( !idealState.Icmp( "Fire" ) ) {
  2356. isFiring = true;
  2357. } else {
  2358. isFiring = false;
  2359. }
  2360. animBlendFrames = blendFrames;
  2361. thread->DoneProcessing();
  2362. }
  2363. /*
  2364. ===============
  2365. idWeapon::Event_WeaponReady
  2366. ===============
  2367. */
  2368. void idWeapon::Event_WeaponReady( void ) {
  2369. status = WP_READY;
  2370. if ( isLinked ) {
  2371. WEAPON_RAISEWEAPON = false;
  2372. }
  2373. if ( sndHum ) {
  2374. StartSoundShader( sndHum, SND_CHANNEL_BODY, 0, false, NULL );
  2375. }
  2376. }
  2377. /*
  2378. ===============
  2379. idWeapon::Event_WeaponOutOfAmmo
  2380. ===============
  2381. */
  2382. void idWeapon::Event_WeaponOutOfAmmo( void ) {
  2383. status = WP_OUTOFAMMO;
  2384. if ( isLinked ) {
  2385. WEAPON_RAISEWEAPON = false;
  2386. }
  2387. }
  2388. /*
  2389. ===============
  2390. idWeapon::Event_WeaponReloading
  2391. ===============
  2392. */
  2393. void idWeapon::Event_WeaponReloading( void ) {
  2394. status = WP_RELOAD;
  2395. }
  2396. /*
  2397. ===============
  2398. idWeapon::Event_WeaponHolstered
  2399. ===============
  2400. */
  2401. void idWeapon::Event_WeaponHolstered( void ) {
  2402. status = WP_HOLSTERED;
  2403. if ( isLinked ) {
  2404. WEAPON_LOWERWEAPON = false;
  2405. }
  2406. }
  2407. /*
  2408. ===============
  2409. idWeapon::Event_WeaponRising
  2410. ===============
  2411. */
  2412. void idWeapon::Event_WeaponRising( void ) {
  2413. status = WP_RISING;
  2414. if ( isLinked ) {
  2415. WEAPON_LOWERWEAPON = false;
  2416. }
  2417. owner->WeaponRisingCallback();
  2418. }
  2419. /*
  2420. ===============
  2421. idWeapon::Event_WeaponLowering
  2422. ===============
  2423. */
  2424. void idWeapon::Event_WeaponLowering( void ) {
  2425. status = WP_LOWERING;
  2426. if ( isLinked ) {
  2427. WEAPON_RAISEWEAPON = false;
  2428. }
  2429. owner->WeaponLoweringCallback();
  2430. }
  2431. /*
  2432. ===============
  2433. idWeapon::Event_UseAmmo
  2434. ===============
  2435. */
  2436. void idWeapon::Event_UseAmmo( int amount ) {
  2437. if ( gameLocal.isClient ) {
  2438. return;
  2439. }
  2440. owner->inventory.UseAmmo( ammoType, ( powerAmmo ) ? amount : ( amount * ammoRequired ) );
  2441. if ( clipSize && ammoRequired ) {
  2442. ammoClip -= powerAmmo ? amount : ( amount * ammoRequired );
  2443. if ( ammoClip < 0 ) {
  2444. ammoClip = 0;
  2445. }
  2446. }
  2447. }
  2448. /*
  2449. ===============
  2450. idWeapon::Event_AddToClip
  2451. ===============
  2452. */
  2453. void idWeapon::Event_AddToClip( int amount ) {
  2454. int ammoAvail;
  2455. if ( gameLocal.isClient ) {
  2456. return;
  2457. }
  2458. #ifdef _D3XP
  2459. int oldAmmo = ammoClip;
  2460. ammoAvail = owner->inventory.HasAmmo( ammoType, ammoRequired ) + AmmoInClip();
  2461. #endif
  2462. ammoClip += amount;
  2463. if ( ammoClip > clipSize ) {
  2464. ammoClip = clipSize;
  2465. }
  2466. #ifdef _D3XP
  2467. #else
  2468. ammoAvail = owner->inventory.HasAmmo( ammoType, ammoRequired );
  2469. #endif
  2470. if ( ammoClip > ammoAvail ) {
  2471. ammoClip = ammoAvail;
  2472. }
  2473. #ifdef _D3XP
  2474. // for shared ammo we need to use the ammo when it is moved into the clip
  2475. int usedAmmo = ammoClip - oldAmmo;
  2476. owner->inventory.UseAmmo(ammoType, usedAmmo);
  2477. #endif
  2478. }
  2479. /*
  2480. ===============
  2481. idWeapon::Event_AmmoInClip
  2482. ===============
  2483. */
  2484. void idWeapon::Event_AmmoInClip( void ) {
  2485. int ammo = AmmoInClip();
  2486. idThread::ReturnFloat( ammo );
  2487. }
  2488. /*
  2489. ===============
  2490. idWeapon::Event_AmmoAvailable
  2491. ===============
  2492. */
  2493. void idWeapon::Event_AmmoAvailable( void ) {
  2494. #ifdef _D3XP
  2495. int ammoAvail = owner->inventory.HasAmmo( ammoType, ammoRequired );
  2496. ammoAvail += AmmoInClip();
  2497. #else
  2498. int ammoAvail = owner->inventory.HasAmmo( ammoType, ammoRequired );
  2499. #endif
  2500. idThread::ReturnFloat( ammoAvail );
  2501. }
  2502. /*
  2503. ===============
  2504. idWeapon::Event_TotalAmmoCount
  2505. ===============
  2506. */
  2507. void idWeapon::Event_TotalAmmoCount( void ) {
  2508. int ammoAvail = owner->inventory.HasAmmo( ammoType, 1 );
  2509. idThread::ReturnFloat( ammoAvail );
  2510. }
  2511. /*
  2512. ===============
  2513. idWeapon::Event_ClipSize
  2514. ===============
  2515. */
  2516. void idWeapon::Event_ClipSize( void ) {
  2517. idThread::ReturnFloat( clipSize );
  2518. }
  2519. /*
  2520. ===============
  2521. idWeapon::Event_AutoReload
  2522. ===============
  2523. */
  2524. void idWeapon::Event_AutoReload( void ) {
  2525. assert( owner );
  2526. if ( gameLocal.isClient ) {
  2527. idThread::ReturnFloat( 0.0f );
  2528. return;
  2529. }
  2530. idThread::ReturnFloat( gameLocal.userInfo[ owner->entityNumber ].GetBool( "ui_autoReload" ) );
  2531. }
  2532. /*
  2533. ===============
  2534. idWeapon::Event_NetReload
  2535. ===============
  2536. */
  2537. void idWeapon::Event_NetReload( void ) {
  2538. assert( owner );
  2539. if ( gameLocal.isServer ) {
  2540. ServerSendEvent( EVENT_RELOAD, NULL, false, -1 );
  2541. }
  2542. }
  2543. /*
  2544. ===============
  2545. idWeapon::Event_NetEndReload
  2546. ===============
  2547. */
  2548. void idWeapon::Event_NetEndReload( void ) {
  2549. assert( owner );
  2550. if ( gameLocal.isServer ) {
  2551. ServerSendEvent( EVENT_ENDRELOAD, NULL, false, -1 );
  2552. }
  2553. }
  2554. /*
  2555. ===============
  2556. idWeapon::Event_PlayAnim
  2557. ===============
  2558. */
  2559. void idWeapon::Event_PlayAnim( int channel, const char *animname ) {
  2560. int anim;
  2561. anim = animator.GetAnim( animname );
  2562. if ( !anim ) {
  2563. gameLocal.Warning( "missing '%s' animation on '%s' (%s)", animname, name.c_str(), GetEntityDefName() );
  2564. animator.Clear( channel, gameLocal.time, FRAME2MS( animBlendFrames ) );
  2565. animDoneTime = 0;
  2566. } else {
  2567. if ( !( owner && owner->GetInfluenceLevel() ) ) {
  2568. Show();
  2569. }
  2570. animator.PlayAnim( channel, anim, gameLocal.time, FRAME2MS( animBlendFrames ) );
  2571. animDoneTime = animator.CurrentAnim( channel )->GetEndTime();
  2572. if ( worldModel.GetEntity() ) {
  2573. anim = worldModel.GetEntity()->GetAnimator()->GetAnim( animname );
  2574. if ( anim ) {
  2575. worldModel.GetEntity()->GetAnimator()->PlayAnim( channel, anim, gameLocal.time, FRAME2MS( animBlendFrames ) );
  2576. }
  2577. }
  2578. }
  2579. animBlendFrames = 0;
  2580. idThread::ReturnInt( 0 );
  2581. }
  2582. /*
  2583. ===============
  2584. idWeapon::Event_PlayCycle
  2585. ===============
  2586. */
  2587. void idWeapon::Event_PlayCycle( int channel, const char *animname ) {
  2588. int anim;
  2589. anim = animator.GetAnim( animname );
  2590. if ( !anim ) {
  2591. gameLocal.Warning( "missing '%s' animation on '%s' (%s)", animname, name.c_str(), GetEntityDefName() );
  2592. animator.Clear( channel, gameLocal.time, FRAME2MS( animBlendFrames ) );
  2593. animDoneTime = 0;
  2594. } else {
  2595. if ( !( owner && owner->GetInfluenceLevel() ) ) {
  2596. Show();
  2597. }
  2598. animator.CycleAnim( channel, anim, gameLocal.time, FRAME2MS( animBlendFrames ) );
  2599. animDoneTime = animator.CurrentAnim( channel )->GetEndTime();
  2600. if ( worldModel.GetEntity() ) {
  2601. anim = worldModel.GetEntity()->GetAnimator()->GetAnim( animname );
  2602. worldModel.GetEntity()->GetAnimator()->CycleAnim( channel, anim, gameLocal.time, FRAME2MS( animBlendFrames ) );
  2603. }
  2604. }
  2605. animBlendFrames = 0;
  2606. idThread::ReturnInt( 0 );
  2607. }
  2608. /*
  2609. ===============
  2610. idWeapon::Event_AnimDone
  2611. ===============
  2612. */
  2613. void idWeapon::Event_AnimDone( int channel, int blendFrames ) {
  2614. if ( animDoneTime - FRAME2MS( blendFrames ) <= gameLocal.time ) {
  2615. idThread::ReturnInt( true );
  2616. } else {
  2617. idThread::ReturnInt( false );
  2618. }
  2619. }
  2620. /*
  2621. ===============
  2622. idWeapon::Event_SetBlendFrames
  2623. ===============
  2624. */
  2625. void idWeapon::Event_SetBlendFrames( int channel, int blendFrames ) {
  2626. animBlendFrames = blendFrames;
  2627. }
  2628. /*
  2629. ===============
  2630. idWeapon::Event_GetBlendFrames
  2631. ===============
  2632. */
  2633. void idWeapon::Event_GetBlendFrames( int channel ) {
  2634. idThread::ReturnInt( animBlendFrames );
  2635. }
  2636. /*
  2637. ================
  2638. idWeapon::Event_Next
  2639. ================
  2640. */
  2641. void idWeapon::Event_Next( void ) {
  2642. // change to another weapon if possible
  2643. owner->NextBestWeapon();
  2644. }
  2645. /*
  2646. ================
  2647. idWeapon::Event_SetSkin
  2648. ================
  2649. */
  2650. void idWeapon::Event_SetSkin( const char *skinname ) {
  2651. const idDeclSkin *skinDecl;
  2652. if ( !skinname || !skinname[ 0 ] ) {
  2653. skinDecl = NULL;
  2654. } else {
  2655. skinDecl = declManager->FindSkin( skinname );
  2656. }
  2657. renderEntity.customSkin = skinDecl;
  2658. UpdateVisuals();
  2659. if ( worldModel.GetEntity() ) {
  2660. worldModel.GetEntity()->SetSkin( skinDecl );
  2661. }
  2662. if ( gameLocal.isServer ) {
  2663. idBitMsg msg;
  2664. byte msgBuf[MAX_EVENT_PARAM_SIZE];
  2665. msg.Init( msgBuf, sizeof( msgBuf ) );
  2666. msg.WriteLong( ( skinDecl != NULL ) ? gameLocal.ServerRemapDecl( -1, DECL_SKIN, skinDecl->Index() ) : -1 );
  2667. ServerSendEvent( EVENT_CHANGESKIN, &msg, false, -1 );
  2668. }
  2669. }
  2670. /*
  2671. ================
  2672. idWeapon::Event_Flashlight
  2673. ================
  2674. */
  2675. void idWeapon::Event_Flashlight( int enable ) {
  2676. if ( enable ) {
  2677. lightOn = true;
  2678. MuzzleFlashLight();
  2679. } else {
  2680. lightOn = false;
  2681. muzzleFlashEnd = 0;
  2682. }
  2683. }
  2684. /*
  2685. ================
  2686. idWeapon::Event_GetLightParm
  2687. ================
  2688. */
  2689. void idWeapon::Event_GetLightParm( int parmnum ) {
  2690. if ( ( parmnum < 0 ) || ( parmnum >= MAX_ENTITY_SHADER_PARMS ) ) {
  2691. gameLocal.Error( "shader parm index (%d) out of range", parmnum );
  2692. }
  2693. idThread::ReturnFloat( muzzleFlash.shaderParms[ parmnum ] );
  2694. }
  2695. /*
  2696. ================
  2697. idWeapon::Event_SetLightParm
  2698. ================
  2699. */
  2700. void idWeapon::Event_SetLightParm( int parmnum, float value ) {
  2701. if ( ( parmnum < 0 ) || ( parmnum >= MAX_ENTITY_SHADER_PARMS ) ) {
  2702. gameLocal.Error( "shader parm index (%d) out of range", parmnum );
  2703. }
  2704. muzzleFlash.shaderParms[ parmnum ] = value;
  2705. worldMuzzleFlash.shaderParms[ parmnum ] = value;
  2706. UpdateVisuals();
  2707. }
  2708. /*
  2709. ================
  2710. idWeapon::Event_SetLightParms
  2711. ================
  2712. */
  2713. void idWeapon::Event_SetLightParms( float parm0, float parm1, float parm2, float parm3 ) {
  2714. muzzleFlash.shaderParms[ SHADERPARM_RED ] = parm0;
  2715. muzzleFlash.shaderParms[ SHADERPARM_GREEN ] = parm1;
  2716. muzzleFlash.shaderParms[ SHADERPARM_BLUE ] = parm2;
  2717. muzzleFlash.shaderParms[ SHADERPARM_ALPHA ] = parm3;
  2718. worldMuzzleFlash.shaderParms[ SHADERPARM_RED ] = parm0;
  2719. worldMuzzleFlash.shaderParms[ SHADERPARM_GREEN ] = parm1;
  2720. worldMuzzleFlash.shaderParms[ SHADERPARM_BLUE ] = parm2;
  2721. worldMuzzleFlash.shaderParms[ SHADERPARM_ALPHA ] = parm3;
  2722. UpdateVisuals();
  2723. }
  2724. #ifdef _D3XP
  2725. /*
  2726. ================
  2727. idWeapon::Event_Grabber
  2728. ================
  2729. */
  2730. void idWeapon::Event_Grabber( int enable ) {
  2731. if ( enable ) {
  2732. grabberState = 0;
  2733. } else {
  2734. grabberState = -1;
  2735. }
  2736. }
  2737. /*
  2738. ================
  2739. idWeapon::Event_GrabberHasTarget
  2740. ================
  2741. */
  2742. void idWeapon::Event_GrabberHasTarget() {
  2743. idThread::ReturnInt( grabberState );
  2744. }
  2745. /*
  2746. ================
  2747. idWeapon::Event_GrabberSetGrabDistance
  2748. ================
  2749. */
  2750. void idWeapon::Event_GrabberSetGrabDistance( float dist ) {
  2751. grabber.SetDragDistance( dist );
  2752. }
  2753. #endif
  2754. /*
  2755. ================
  2756. idWeapon::Event_CreateProjectile
  2757. ================
  2758. */
  2759. void idWeapon::Event_CreateProjectile( void ) {
  2760. if ( !gameLocal.isClient ) {
  2761. projectileEnt = NULL;
  2762. gameLocal.SpawnEntityDef( projectileDict, &projectileEnt, false );
  2763. if ( projectileEnt ) {
  2764. projectileEnt->SetOrigin( GetPhysics()->GetOrigin() );
  2765. projectileEnt->Bind( owner, false );
  2766. projectileEnt->Hide();
  2767. }
  2768. idThread::ReturnEntity( projectileEnt );
  2769. } else {
  2770. idThread::ReturnEntity( NULL );
  2771. }
  2772. }
  2773. /*
  2774. ================
  2775. idWeapon::Event_LaunchProjectiles
  2776. ================
  2777. */
  2778. void idWeapon::Event_LaunchProjectiles( int num_projectiles, float spread, float fuseOffset, float launchPower, float dmgPower ) {
  2779. idProjectile *proj;
  2780. idEntity *ent;
  2781. int i;
  2782. idVec3 dir;
  2783. float ang;
  2784. float spin;
  2785. float distance;
  2786. trace_t tr;
  2787. idVec3 start;
  2788. idVec3 muzzle_pos;
  2789. idBounds ownerBounds, projBounds;
  2790. if ( IsHidden() ) {
  2791. return;
  2792. }
  2793. if ( !projectileDict.GetNumKeyVals() ) {
  2794. const char *classname = weaponDef->dict.GetString( "classname" );
  2795. gameLocal.Warning( "No projectile defined on '%s'", classname );
  2796. return;
  2797. }
  2798. // avoid all ammo considerations on an MP client
  2799. if ( !gameLocal.isClient ) {
  2800. #ifdef _D3XP
  2801. int ammoAvail = owner->inventory.HasAmmo( ammoType, ammoRequired );
  2802. if ( ( clipSize != 0 ) && ( ammoClip <= 0 ) ) {
  2803. return;
  2804. }
  2805. #else
  2806. // check if we're out of ammo or the clip is empty
  2807. int ammoAvail = owner->inventory.HasAmmo( ammoType, ammoRequired );
  2808. if ( !ammoAvail || ( ( clipSize != 0 ) && ( ammoClip <= 0 ) ) ) {
  2809. return;
  2810. }
  2811. #endif
  2812. // if this is a power ammo weapon ( currently only the bfg ) then make sure
  2813. // we only fire as much power as available in each clip
  2814. if ( powerAmmo ) {
  2815. // power comes in as a float from zero to max
  2816. // if we use this on more than the bfg will need to define the max
  2817. // in the .def as opposed to just in the script so proper calcs
  2818. // can be done here.
  2819. dmgPower = ( int )dmgPower + 1;
  2820. if ( dmgPower > ammoClip ) {
  2821. dmgPower = ammoClip;
  2822. }
  2823. }
  2824. #ifdef _D3XP
  2825. if(clipSize == 0) {
  2826. //Weapons with a clip size of 0 launch strait from inventory without moving to a clip
  2827. #endif
  2828. //In D3XP we used the ammo when the ammo was moved into the clip so we don't want to
  2829. //use it now.
  2830. owner->inventory.UseAmmo( ammoType, ( powerAmmo ) ? dmgPower : ammoRequired );
  2831. #ifdef _D3XP
  2832. }
  2833. #endif
  2834. if ( clipSize && ammoRequired ) {
  2835. #ifdef _D3XP
  2836. ammoClip -= powerAmmo ? dmgPower : ammoRequired;
  2837. #else
  2838. ammoClip -= powerAmmo ? dmgPower : 1;
  2839. #endif
  2840. }
  2841. }
  2842. if ( !silent_fire ) {
  2843. // wake up nearby monsters
  2844. gameLocal.AlertAI( owner );
  2845. }
  2846. // set the shader parm to the time of last projectile firing,
  2847. // which the gun material shaders can reference for single shot barrel glows, etc
  2848. renderEntity.shaderParms[ SHADERPARM_DIVERSITY ] = gameLocal.random.CRandomFloat();
  2849. renderEntity.shaderParms[ SHADERPARM_TIMEOFFSET ] = -MS2SEC( gameLocal.realClientTime );
  2850. if ( worldModel.GetEntity() ) {
  2851. worldModel.GetEntity()->SetShaderParm( SHADERPARM_DIVERSITY, renderEntity.shaderParms[ SHADERPARM_DIVERSITY ] );
  2852. worldModel.GetEntity()->SetShaderParm( SHADERPARM_TIMEOFFSET, renderEntity.shaderParms[ SHADERPARM_TIMEOFFSET ] );
  2853. }
  2854. // calculate the muzzle position
  2855. if ( barrelJointView != INVALID_JOINT && projectileDict.GetBool( "launchFromBarrel" ) ) {
  2856. // there is an explicit joint for the muzzle
  2857. GetGlobalJointTransform( true, barrelJointView, muzzleOrigin, muzzleAxis );
  2858. } else {
  2859. // go straight out of the view
  2860. muzzleOrigin = playerViewOrigin;
  2861. muzzleAxis = playerViewAxis;
  2862. }
  2863. // add some to the kick time, incrementally moving repeat firing weapons back
  2864. if ( kick_endtime < gameLocal.realClientTime ) {
  2865. kick_endtime = gameLocal.realClientTime;
  2866. }
  2867. kick_endtime += muzzle_kick_time;
  2868. if ( kick_endtime > gameLocal.realClientTime + muzzle_kick_maxtime ) {
  2869. kick_endtime = gameLocal.realClientTime + muzzle_kick_maxtime;
  2870. }
  2871. if ( gameLocal.isClient ) {
  2872. // predict instant hit projectiles
  2873. if ( projectileDict.GetBool( "net_instanthit" ) ) {
  2874. float spreadRad = DEG2RAD( spread );
  2875. muzzle_pos = muzzleOrigin + playerViewAxis[ 0 ] * 2.0f;
  2876. for( i = 0; i < num_projectiles; i++ ) {
  2877. ang = idMath::Sin( spreadRad * gameLocal.random.RandomFloat() );
  2878. spin = (float)DEG2RAD( 360.0f ) * gameLocal.random.RandomFloat();
  2879. dir = playerViewAxis[ 0 ] + playerViewAxis[ 2 ] * ( ang * idMath::Sin( spin ) ) - playerViewAxis[ 1 ] * ( ang * idMath::Cos( spin ) );
  2880. dir.Normalize();
  2881. gameLocal.clip.Translation( tr, muzzle_pos, muzzle_pos + dir * 4096.0f, NULL, mat3_identity, MASK_SHOT_RENDERMODEL, owner );
  2882. if ( tr.fraction < 1.0f ) {
  2883. idProjectile::ClientPredictionCollide( this, projectileDict, tr, vec3_origin, true );
  2884. }
  2885. }
  2886. }
  2887. } else {
  2888. ownerBounds = owner->GetPhysics()->GetAbsBounds();
  2889. owner->AddProjectilesFired( num_projectiles );
  2890. float spreadRad = DEG2RAD( spread );
  2891. for( i = 0; i < num_projectiles; i++ ) {
  2892. ang = idMath::Sin( spreadRad * gameLocal.random.RandomFloat() );
  2893. spin = (float)DEG2RAD( 360.0f ) * gameLocal.random.RandomFloat();
  2894. dir = playerViewAxis[ 0 ] + playerViewAxis[ 2 ] * ( ang * idMath::Sin( spin ) ) - playerViewAxis[ 1 ] * ( ang * idMath::Cos( spin ) );
  2895. dir.Normalize();
  2896. if ( projectileEnt ) {
  2897. ent = projectileEnt;
  2898. ent->Show();
  2899. ent->Unbind();
  2900. projectileEnt = NULL;
  2901. } else {
  2902. gameLocal.SpawnEntityDef( projectileDict, &ent, false );
  2903. }
  2904. if ( !ent || !ent->IsType( idProjectile::Type ) ) {
  2905. const char *projectileName = weaponDef->dict.GetString( "def_projectile" );
  2906. gameLocal.Error( "'%s' is not an idProjectile", projectileName );
  2907. }
  2908. if ( projectileDict.GetBool( "net_instanthit" ) ) {
  2909. // don't synchronize this on top of the already predicted effect
  2910. ent->fl.networkSync = false;
  2911. }
  2912. proj = static_cast<idProjectile *>(ent);
  2913. proj->Create( owner, muzzleOrigin, dir );
  2914. projBounds = proj->GetPhysics()->GetBounds().Rotate( proj->GetPhysics()->GetAxis() );
  2915. // make sure the projectile starts inside the bounding box of the owner
  2916. if ( i == 0 ) {
  2917. muzzle_pos = muzzleOrigin + playerViewAxis[ 0 ] * 2.0f;
  2918. if ( ( ownerBounds - projBounds).RayIntersection( muzzle_pos, playerViewAxis[0], distance ) ) {
  2919. start = muzzle_pos + distance * playerViewAxis[0];
  2920. } else {
  2921. start = ownerBounds.GetCenter();
  2922. }
  2923. gameLocal.clip.Translation( tr, start, muzzle_pos, proj->GetPhysics()->GetClipModel(), proj->GetPhysics()->GetClipModel()->GetAxis(), MASK_SHOT_RENDERMODEL, owner );
  2924. muzzle_pos = tr.endpos;
  2925. }
  2926. proj->Launch( muzzle_pos, dir, pushVelocity, fuseOffset, launchPower, dmgPower );
  2927. }
  2928. // toss the brass
  2929. #ifdef _D3XP
  2930. if(brassDelay >= 0)
  2931. #endif
  2932. PostEventMS( &EV_Weapon_EjectBrass, brassDelay );
  2933. }
  2934. // add the light for the muzzleflash
  2935. if ( !lightOn ) {
  2936. MuzzleFlashLight();
  2937. }
  2938. owner->WeaponFireFeedback( &weaponDef->dict );
  2939. // reset muzzle smoke
  2940. weaponSmokeStartTime = gameLocal.realClientTime;
  2941. }
  2942. #ifdef _D3XP
  2943. /*
  2944. ================
  2945. idWeapon::Event_LaunchProjectilesEllipse
  2946. ================
  2947. */
  2948. void idWeapon::Event_LaunchProjectilesEllipse( int num_projectiles, float spreada, float spreadb, float fuseOffset, float power ) {
  2949. idProjectile *proj;
  2950. idEntity *ent;
  2951. int i;
  2952. idVec3 dir;
  2953. float anga, angb;
  2954. float spin;
  2955. float distance;
  2956. trace_t tr;
  2957. idVec3 start;
  2958. idVec3 muzzle_pos;
  2959. idBounds ownerBounds, projBounds;
  2960. if ( IsHidden() ) {
  2961. return;
  2962. }
  2963. if ( !projectileDict.GetNumKeyVals() ) {
  2964. const char *classname = weaponDef->dict.GetString( "classname" );
  2965. gameLocal.Warning( "No projectile defined on '%s'", classname );
  2966. return;
  2967. }
  2968. // avoid all ammo considerations on a client
  2969. if ( !gameLocal.isClient ) {
  2970. int ammoAvail = owner->inventory.HasAmmo( ammoType, ammoRequired );
  2971. if ( ( clipSize != 0 ) && ( ammoClip <= 0 ) ) {
  2972. return;
  2973. }
  2974. if( clipSize == 0 ) {
  2975. //Weapons with a clip size of 0 launch strait from inventory without moving to a clip
  2976. owner->inventory.UseAmmo( ammoType, ammoRequired );
  2977. }
  2978. if ( clipSize && ammoRequired ) {
  2979. ammoClip -= ammoRequired;
  2980. }
  2981. if ( !silent_fire ) {
  2982. // wake up nearby monsters
  2983. gameLocal.AlertAI( owner );
  2984. }
  2985. }
  2986. // set the shader parm to the time of last projectile firing,
  2987. // which the gun material shaders can reference for single shot barrel glows, etc
  2988. renderEntity.shaderParms[ SHADERPARM_DIVERSITY ] = gameLocal.random.CRandomFloat();
  2989. renderEntity.shaderParms[ SHADERPARM_TIMEOFFSET ] = -MS2SEC( gameLocal.time );
  2990. if ( worldModel.GetEntity() ) {
  2991. worldModel.GetEntity()->SetShaderParm( SHADERPARM_DIVERSITY, renderEntity.shaderParms[ SHADERPARM_DIVERSITY ] );
  2992. worldModel.GetEntity()->SetShaderParm( SHADERPARM_TIMEOFFSET, renderEntity.shaderParms[ SHADERPARM_TIMEOFFSET ] );
  2993. }
  2994. // calculate the muzzle position
  2995. if ( barrelJointView != INVALID_JOINT && projectileDict.GetBool( "launchFromBarrel" ) ) {
  2996. // there is an explicit joint for the muzzle
  2997. GetGlobalJointTransform( true, barrelJointView, muzzleOrigin, muzzleAxis );
  2998. } else {
  2999. // go straight out of the view
  3000. muzzleOrigin = playerViewOrigin;
  3001. muzzleAxis = playerViewAxis;
  3002. }
  3003. // add some to the kick time, incrementally moving repeat firing weapons back
  3004. if ( kick_endtime < gameLocal.time ) {
  3005. kick_endtime = gameLocal.time;
  3006. }
  3007. kick_endtime += muzzle_kick_time;
  3008. if ( kick_endtime > gameLocal.time + muzzle_kick_maxtime ) {
  3009. kick_endtime = gameLocal.time + muzzle_kick_maxtime;
  3010. }
  3011. if ( !gameLocal.isClient ) {
  3012. ownerBounds = owner->GetPhysics()->GetAbsBounds();
  3013. owner->AddProjectilesFired( num_projectiles );
  3014. float spreadRadA = DEG2RAD( spreada );
  3015. float spreadRadB = DEG2RAD( spreadb );
  3016. for( i = 0; i < num_projectiles; i++ ) {
  3017. //Ellipse Form
  3018. spin = (float)DEG2RAD( 360.0f ) * gameLocal.random.RandomFloat();
  3019. anga = idMath::Sin(spreadRadA * gameLocal.random.RandomFloat());
  3020. angb = idMath::Sin(spreadRadB * gameLocal.random.RandomFloat());
  3021. dir = playerViewAxis[ 0 ] + playerViewAxis[ 2 ] * ( angb*idMath::Sin( spin ) ) - playerViewAxis[ 1 ] * ( anga*idMath::Cos( spin ) );
  3022. dir.Normalize();
  3023. gameLocal.SpawnEntityDef( projectileDict, &ent );
  3024. if ( !ent || !ent->IsType( idProjectile::Type ) ) {
  3025. const char *projectileName = weaponDef->dict.GetString( "def_projectile" );
  3026. gameLocal.Error( "'%s' is not an idProjectile", projectileName );
  3027. }
  3028. proj = static_cast<idProjectile *>(ent);
  3029. proj->Create( owner, muzzleOrigin, dir );
  3030. projBounds = proj->GetPhysics()->GetBounds().Rotate( proj->GetPhysics()->GetAxis() );
  3031. // make sure the projectile starts inside the bounding box of the owner
  3032. if ( i == 0 ) {
  3033. muzzle_pos = muzzleOrigin + playerViewAxis[ 0 ] * 2.0f;
  3034. if ( ( ownerBounds - projBounds).RayIntersection( muzzle_pos, playerViewAxis[0], distance ) ) {
  3035. start = muzzle_pos + distance * playerViewAxis[0];
  3036. }
  3037. else {
  3038. start = ownerBounds.GetCenter();
  3039. }
  3040. gameLocal.clip.Translation( tr, start, muzzle_pos, proj->GetPhysics()->GetClipModel(), proj->GetPhysics()->GetClipModel()->GetAxis(), MASK_SHOT_RENDERMODEL, owner );
  3041. muzzle_pos = tr.endpos;
  3042. }
  3043. proj->Launch( muzzle_pos, dir, pushVelocity, fuseOffset, power );
  3044. }
  3045. // toss the brass
  3046. if( brassDelay >= 0 ) {
  3047. PostEventMS( &EV_Weapon_EjectBrass, brassDelay );
  3048. }
  3049. }
  3050. // add the light for the muzzleflash
  3051. if ( !lightOn ) {
  3052. MuzzleFlashLight();
  3053. }
  3054. owner->WeaponFireFeedback( &weaponDef->dict );
  3055. // reset muzzle smoke
  3056. weaponSmokeStartTime = gameLocal.time;
  3057. }
  3058. /**
  3059. * Gives the player a powerup as if it were a weapon shot. It will use the ammo amount specified
  3060. * as ammoRequired.
  3061. */
  3062. void idWeapon::Event_LaunchPowerup( const char* powerup, float duration, int useAmmo ) {
  3063. if ( IsHidden() ) {
  3064. return;
  3065. }
  3066. // check if we're out of ammo
  3067. if(useAmmo) {
  3068. int ammoAvail = owner->inventory.HasAmmo( ammoType, ammoRequired );
  3069. if ( !ammoAvail ) {
  3070. return;
  3071. }
  3072. owner->inventory.UseAmmo( ammoType, ammoRequired );
  3073. }
  3074. // set the shader parm to the time of last projectile firing,
  3075. // which the gun material shaders can reference for single shot barrel glows, etc
  3076. renderEntity.shaderParms[ SHADERPARM_DIVERSITY ] = gameLocal.random.CRandomFloat();
  3077. renderEntity.shaderParms[ SHADERPARM_TIMEOFFSET ] = -MS2SEC( gameLocal.time );
  3078. if ( worldModel.GetEntity() ) {
  3079. worldModel.GetEntity()->SetShaderParm( SHADERPARM_DIVERSITY, renderEntity.shaderParms[ SHADERPARM_DIVERSITY ] );
  3080. worldModel.GetEntity()->SetShaderParm( SHADERPARM_TIMEOFFSET, renderEntity.shaderParms[ SHADERPARM_TIMEOFFSET ] );
  3081. }
  3082. // add the light for the muzzleflash
  3083. if ( !lightOn ) {
  3084. MuzzleFlashLight();
  3085. }
  3086. owner->Give(powerup, va("%f", duration));
  3087. }
  3088. void idWeapon::Event_StartWeaponSmoke() {
  3089. // reset muzzle smoke
  3090. weaponSmokeStartTime = gameLocal.time;
  3091. }
  3092. void idWeapon::Event_StopWeaponSmoke() {
  3093. // reset muzzle smoke
  3094. weaponSmokeStartTime = 0;
  3095. }
  3096. void idWeapon::Event_StartWeaponParticle( const char* name) {
  3097. WeaponParticle_t* part;
  3098. weaponParticles.Get(name, &part);
  3099. if(part) {
  3100. part->active = true;
  3101. part->startTime = gameLocal.time;
  3102. //Toggle the emitter
  3103. if(!part->smoke) {
  3104. part->emitter->Show();
  3105. part->emitter->PostEventMS(&EV_Activate, 0, this);
  3106. }
  3107. }
  3108. }
  3109. void idWeapon::Event_StopWeaponParticle( const char* name) {
  3110. WeaponParticle_t* part;
  3111. weaponParticles.Get(name, &part);
  3112. if(part) {
  3113. part->active = false;
  3114. part->startTime = 0;
  3115. //Toggle the emitter
  3116. if(!part->smoke) {
  3117. part->emitter->Hide();
  3118. part->emitter->PostEventMS(&EV_Activate, 0, this);
  3119. }
  3120. }
  3121. }
  3122. void idWeapon::Event_StartWeaponLight( const char* name) {
  3123. WeaponLight_t* light;
  3124. weaponLights.Get(name, &light);
  3125. if(light) {
  3126. light->active = true;
  3127. light->startTime = gameLocal.time;
  3128. }
  3129. }
  3130. void idWeapon::Event_StopWeaponLight( const char* name) {
  3131. WeaponLight_t* light;
  3132. weaponLights.Get(name, &light);
  3133. if(light) {
  3134. light->active = false;
  3135. light->startTime = 0;
  3136. if(light->lightHandle != -1) {
  3137. gameRenderWorld->FreeLightDef( light->lightHandle );
  3138. light->lightHandle = -1;
  3139. }
  3140. }
  3141. }
  3142. #endif
  3143. /*
  3144. =====================
  3145. idWeapon::Event_Melee
  3146. =====================
  3147. */
  3148. void idWeapon::Event_Melee( void ) {
  3149. idEntity *ent;
  3150. trace_t tr;
  3151. if ( !meleeDef ) {
  3152. gameLocal.Error( "No meleeDef on '%s'", weaponDef->dict.GetString( "classname" ) );
  3153. }
  3154. if ( !gameLocal.isClient ) {
  3155. idVec3 start = playerViewOrigin;
  3156. idVec3 end = start + playerViewAxis[0] * ( meleeDistance * owner->PowerUpModifier( MELEE_DISTANCE ) );
  3157. gameLocal.clip.TracePoint( tr, start, end, MASK_SHOT_RENDERMODEL, owner );
  3158. if ( tr.fraction < 1.0f ) {
  3159. ent = gameLocal.GetTraceEntity( tr );
  3160. } else {
  3161. ent = NULL;
  3162. }
  3163. if ( g_debugWeapon.GetBool() ) {
  3164. gameRenderWorld->DebugLine( colorYellow, start, end, 100 );
  3165. if ( ent ) {
  3166. gameRenderWorld->DebugBounds( colorRed, ent->GetPhysics()->GetBounds(), ent->GetPhysics()->GetOrigin(), 100 );
  3167. }
  3168. }
  3169. bool hit = false;
  3170. const char *hitSound = meleeDef->dict.GetString( "snd_miss" );
  3171. if ( ent ) {
  3172. float push = meleeDef->dict.GetFloat( "push" );
  3173. idVec3 impulse = -push * owner->PowerUpModifier( SPEED ) * tr.c.normal;
  3174. if ( gameLocal.world->spawnArgs.GetBool( "no_Weapons" ) && ( ent->IsType( idActor::Type ) || ent->IsType( idAFAttachment::Type) ) ) {
  3175. idThread::ReturnInt( 0 );
  3176. return;
  3177. }
  3178. ent->ApplyImpulse( this, tr.c.id, tr.c.point, impulse );
  3179. // weapon stealing - do this before damaging so weapons are not dropped twice
  3180. if ( gameLocal.isMultiplayer
  3181. && weaponDef && weaponDef->dict.GetBool( "stealing" )
  3182. && ent->IsType( idPlayer::Type )
  3183. && !owner->PowerUpActive( BERSERK )
  3184. && ( (gameLocal.gameType != GAME_TDM ) || gameLocal.serverInfo.GetBool( "si_teamDamage" ) || ( owner->team != static_cast< idPlayer * >( ent )->team ) )
  3185. ) {
  3186. #ifdef CTF /* Code is formed oddly for easy merge */
  3187. if ( gameLocal.mpGame.IsGametypeFlagBased() )
  3188. { /* Do nothing ... */ }
  3189. else
  3190. #endif
  3191. owner->StealWeapon( static_cast< idPlayer * >( ent ) );
  3192. }
  3193. if ( ent->fl.takedamage ) {
  3194. idVec3 kickDir, globalKickDir;
  3195. meleeDef->dict.GetVector( "kickDir", "0 0 0", kickDir );
  3196. globalKickDir = muzzleAxis * kickDir;
  3197. #ifdef _D3XP
  3198. //Adjust the melee powerup modifier for the invulnerability boss fight
  3199. float mod = owner->PowerUpModifier( MELEE_DAMAGE );
  3200. if(!strcmp(ent->GetEntityDefName(), "monster_hunter_invul")) {
  3201. //Only do a quater of the damage mod
  3202. mod *= 0.25f;
  3203. }
  3204. ent->Damage( owner, owner, globalKickDir, meleeDefName, mod, tr.c.id );
  3205. #else
  3206. ent->Damage( owner, owner, globalKickDir, meleeDefName, owner->PowerUpModifier( MELEE_DAMAGE ), tr.c.id );
  3207. #endif
  3208. hit = true;
  3209. }
  3210. if ( weaponDef->dict.GetBool( "impact_damage_effect" ) ) {
  3211. if ( ent->spawnArgs.GetBool( "bleed" ) ) {
  3212. hitSound = meleeDef->dict.GetString( owner->PowerUpActive( BERSERK ) ? "snd_hit_berserk" : "snd_hit" );
  3213. ent->AddDamageEffect( tr, impulse, meleeDef->dict.GetString( "classname" ) );
  3214. } else {
  3215. int type = tr.c.material->GetSurfaceType();
  3216. if ( type == SURFTYPE_NONE ) {
  3217. type = GetDefaultSurfaceType();
  3218. }
  3219. const char *materialType = gameLocal.sufaceTypeNames[ type ];
  3220. // start impact sound based on material type
  3221. hitSound = meleeDef->dict.GetString( va( "snd_%s", materialType ) );
  3222. if ( *hitSound == '\0' ) {
  3223. hitSound = meleeDef->dict.GetString( "snd_metal" );
  3224. }
  3225. if ( gameLocal.time > nextStrikeFx ) {
  3226. const char *decal;
  3227. // project decal
  3228. decal = weaponDef->dict.GetString( "mtr_strike" );
  3229. if ( decal && *decal ) {
  3230. gameLocal.ProjectDecal( tr.c.point, -tr.c.normal, 8.0f, true, 6.0, decal );
  3231. }
  3232. nextStrikeFx = gameLocal.time + 200;
  3233. } else {
  3234. hitSound = "";
  3235. }
  3236. strikeSmokeStartTime = gameLocal.time;
  3237. strikePos = tr.c.point;
  3238. strikeAxis = -tr.endAxis;
  3239. }
  3240. }
  3241. }
  3242. if ( *hitSound != '\0' ) {
  3243. const idSoundShader *snd = declManager->FindSound( hitSound );
  3244. StartSoundShader( snd, SND_CHANNEL_BODY2, 0, true, NULL );
  3245. }
  3246. idThread::ReturnInt( hit );
  3247. owner->WeaponFireFeedback( &weaponDef->dict );
  3248. return;
  3249. }
  3250. idThread::ReturnInt( 0 );
  3251. owner->WeaponFireFeedback( &weaponDef->dict );
  3252. }
  3253. /*
  3254. =====================
  3255. idWeapon::Event_GetWorldModel
  3256. =====================
  3257. */
  3258. void idWeapon::Event_GetWorldModel( void ) {
  3259. idThread::ReturnEntity( worldModel.GetEntity() );
  3260. }
  3261. /*
  3262. =====================
  3263. idWeapon::Event_AllowDrop
  3264. =====================
  3265. */
  3266. void idWeapon::Event_AllowDrop( int allow ) {
  3267. if ( allow ) {
  3268. allowDrop = true;
  3269. } else {
  3270. allowDrop = false;
  3271. }
  3272. }
  3273. /*
  3274. ================
  3275. idWeapon::Event_EjectBrass
  3276. Toss a shell model out from the breach if the bone is present
  3277. ================
  3278. */
  3279. void idWeapon::Event_EjectBrass( void ) {
  3280. if ( !g_showBrass.GetBool() || !owner->CanShowWeaponViewmodel() ) {
  3281. return;
  3282. }
  3283. if ( ejectJointView == INVALID_JOINT || !brassDict.GetNumKeyVals() ) {
  3284. return;
  3285. }
  3286. if ( gameLocal.isClient ) {
  3287. return;
  3288. }
  3289. idMat3 axis;
  3290. idVec3 origin, linear_velocity, angular_velocity;
  3291. idEntity *ent;
  3292. if ( !GetGlobalJointTransform( true, ejectJointView, origin, axis ) ) {
  3293. return;
  3294. }
  3295. gameLocal.SpawnEntityDef( brassDict, &ent, false );
  3296. if ( !ent || !ent->IsType( idDebris::Type ) ) {
  3297. gameLocal.Error( "'%s' is not an idDebris", weaponDef ? weaponDef->dict.GetString( "def_ejectBrass" ) : "def_ejectBrass" );
  3298. }
  3299. idDebris *debris = static_cast<idDebris *>(ent);
  3300. debris->Create( owner, origin, axis );
  3301. debris->Launch();
  3302. linear_velocity = 40 * ( playerViewAxis[0] + playerViewAxis[1] + playerViewAxis[2] );
  3303. angular_velocity.Set( 10 * gameLocal.random.CRandomFloat(), 10 * gameLocal.random.CRandomFloat(), 10 * gameLocal.random.CRandomFloat() );
  3304. debris->GetPhysics()->SetLinearVelocity( linear_velocity );
  3305. debris->GetPhysics()->SetAngularVelocity( angular_velocity );
  3306. }
  3307. /*
  3308. ===============
  3309. idWeapon::Event_IsInvisible
  3310. ===============
  3311. */
  3312. void idWeapon::Event_IsInvisible( void ) {
  3313. if ( !owner ) {
  3314. idThread::ReturnFloat( 0 );
  3315. return;
  3316. }
  3317. idThread::ReturnFloat( owner->PowerUpActive( INVISIBILITY ) ? 1 : 0 );
  3318. }
  3319. /*
  3320. ===============
  3321. idWeapon::ClientPredictionThink
  3322. ===============
  3323. */
  3324. void idWeapon::ClientPredictionThink( void ) {
  3325. UpdateAnimation();
  3326. }