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