Weapon.cpp 115 KB

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