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