Weapon.cpp 83 KB

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