2 ===========================================================================
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
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.
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.
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/>.
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.
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.
26 ===========================================================================
29 #include "../idlib/precompiled.h"
32 #include "Game_local.h"
34 /***********************************************************************
38 ***********************************************************************/
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" );
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" );
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 )
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 )
139 /***********************************************************************
143 ***********************************************************************/
150 idWeapon::idWeapon() {
156 memset( &guiLight, 0, sizeof( guiLight ) );
157 memset( &muzzleFlash, 0, sizeof( muzzleFlash ) );
158 memset( &worldMuzzleFlash, 0, sizeof( worldMuzzleFlash ) );
159 memset( &nozzleGlow, 0, sizeof( nozzleGlow ) );
162 flashColor = vec3_origin;
163 muzzleFlashHandle = -1;
164 worldMuzzleFlashHandle = -1;
166 nozzleGlowHandle = -1;
179 fl.networkSync = true;
184 idWeapon::~idWeapon()
187 idWeapon::~idWeapon() {
189 delete worldModel.GetEntity();
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;
206 if ( 1 /*!gameLocal.isMultiplayer*/ ) {
207 grabber.Initialize();
211 thread = new idThread();
212 thread->ManualDelete();
213 thread->ManualControl();
220 Only called at player spawn time, not each weapon switch
223 void idWeapon::SetOwner( idPlayer *_owner ) {
226 SetName( va( "%s_weapon", owner->name.c_str() ) );
228 if ( worldModel.GetEntity() ) {
229 worldModel.GetEntity()->SetName( va( "%s_weapon_worldmodel", owner->name.c_str() ) );
235 idWeapon::ShouldConstructScriptObjectAtSpawn
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.
241 bool idWeapon::ShouldConstructScriptObjectAtSpawn( void ) const {
247 idWeapon::CacheWeapon
250 void idWeapon::CacheWeapon( const char *weaponName ) {
251 const idDeclEntityDef *weaponDef;
252 const char *brassDefName;
253 const char *clipModelName;
257 weaponDef = gameLocal.FindEntityDef( weaponName, false );
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 );
267 brassDef->dict.GetString( "clipmodel", "", &clipModelName );
268 if ( !clipModelName[0] ) {
269 clipModelName = brassDef->dict.GetString( "model" ); // use the visual model
271 // load the trace model
272 collisionModelManager->TrmFromModel( clipModelName, trm );
276 guiName = weaponDef->dict.GetString( "gui" );
278 uiManager->FindGui( guiName, true, false, true );
287 void idWeapon::Save( idSaveGame *savefile ) const {
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 );
297 savefile->WriteObject( owner );
298 worldModel.Save( savefile );
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 );
309 savefile->WriteInt( berserk );
311 savefile->WriteVec3( playerViewOrigin );
312 savefile->WriteMat3( playerViewAxis );
314 savefile->WriteVec3( viewWeaponOrigin );
315 savefile->WriteMat3( viewWeaponAxis );
317 savefile->WriteVec3( muzzleOrigin );
318 savefile->WriteMat3( muzzleAxis );
320 savefile->WriteVec3( pushVelocity );
322 savefile->WriteString( weaponDef->GetName() );
323 savefile->WriteFloat( meleeDistance );
324 savefile->WriteString( meleeDefName );
325 savefile->WriteInt( brassDelay );
326 savefile->WriteString( icon );
328 savefile->WriteInt( guiLightHandle );
329 savefile->WriteRenderLight( guiLight );
331 savefile->WriteInt( muzzleFlashHandle );
332 savefile->WriteRenderLight( muzzleFlash );
334 savefile->WriteInt( worldMuzzleFlashHandle );
335 savefile->WriteRenderLight( worldMuzzleFlash );
337 savefile->WriteVec3( flashColor );
338 savefile->WriteInt( muzzleFlashEnd );
339 savefile->WriteInt( flashTime );
341 savefile->WriteBool( lightOn );
342 savefile->WriteBool( silent_fire );
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 );
350 savefile->WriteInt( ammoType );
351 savefile->WriteInt( ammoRequired );
352 savefile->WriteInt( clipSize );
353 savefile->WriteInt( ammoClip );
354 savefile->WriteInt( lowAmmo );
355 savefile->WriteBool( powerAmmo );
358 savefile->WriteInt( 0 );
360 savefile->WriteInt( zoomFov );
362 savefile->WriteJoint( barrelJointView );
363 savefile->WriteJoint( flashJointView );
364 savefile->WriteJoint( ejectJointView );
365 savefile->WriteJoint( guiLightJointView );
366 savefile->WriteJoint( ventLightJointView );
368 savefile->WriteJoint( flashJointWorld );
369 savefile->WriteJoint( barrelJointWorld );
370 savefile->WriteJoint( ejectJointWorld );
372 savefile->WriteBool( hasBloodSplat );
374 savefile->WriteSoundShader( sndHum );
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 );
385 savefile->WriteBool( nozzleFx );
386 savefile->WriteInt( nozzleFxFade );
388 savefile->WriteInt( lastAttack );
390 savefile->WriteInt( nozzleGlowHandle );
391 savefile->WriteRenderLight( nozzleGlow );
393 savefile->WriteVec3( nozzleGlowColor );
394 savefile->WriteMaterial( nozzleGlowShader );
395 savefile->WriteFloat( nozzleGlowRadius );
397 savefile->WriteInt( weaponAngleOffsetAverages );
398 savefile->WriteFloat( weaponAngleOffsetScale );
399 savefile->WriteFloat( weaponAngleOffsetMax );
400 savefile->WriteFloat( weaponOffsetTime );
401 savefile->WriteFloat( weaponOffsetScale );
403 savefile->WriteBool( allowDrop );
404 savefile->WriteObject( projectileEnt );
407 savefile->WriteStaticObject( grabber );
408 savefile->WriteInt( grabberState );
410 savefile->WriteJoint ( smokeJointView );
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 );
422 savefile->WriteObject(part->emitter);
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 );
444 void idWeapon::Restore( idRestoreGame *savefile ) {
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 );
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" );
463 savefile->ReadObject( reinterpret_cast<idClass *&>( owner ) );
464 worldModel.Restore( savefile );
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 );
475 savefile->ReadInt( berserk );
477 savefile->ReadVec3( playerViewOrigin );
478 savefile->ReadMat3( playerViewAxis );
480 savefile->ReadVec3( viewWeaponOrigin );
481 savefile->ReadMat3( viewWeaponAxis );
483 savefile->ReadVec3( muzzleOrigin );
484 savefile->ReadMat3( muzzleAxis );
486 savefile->ReadVec3( pushVelocity );
489 savefile->ReadString( objectname );
490 weaponDef = gameLocal.FindEntityDef( objectname );
491 meleeDef = gameLocal.FindEntityDef( weaponDef->dict.GetString( "def_melee" ), false );
493 const idDeclEntityDef *projectileDef = gameLocal.FindEntityDef( weaponDef->dict.GetString( "def_projectile" ), false );
494 if ( projectileDef ) {
495 projectileDict = projectileDef->dict;
497 projectileDict.Clear();
500 const idDeclEntityDef *brassDef = gameLocal.FindEntityDef( weaponDef->dict.GetString( "def_ejectBrass" ), false );
502 brassDict = brassDef->dict;
507 savefile->ReadFloat( meleeDistance );
508 savefile->ReadString( meleeDefName );
509 savefile->ReadInt( brassDelay );
510 savefile->ReadString( icon );
512 savefile->ReadInt( guiLightHandle );
513 savefile->ReadRenderLight( guiLight );
515 if ( guiLightHandle >= 0 ) {
516 guiLightHandle = gameRenderWorld->AddLightDef( &guiLight );
520 savefile->ReadInt( muzzleFlashHandle );
521 savefile->ReadRenderLight( muzzleFlash );
523 if ( muzzleFlashHandle >= 0 ) {
524 muzzleFlashHandle = gameRenderWorld->AddLightDef( &muzzleFlash );
528 savefile->ReadInt( worldMuzzleFlashHandle );
529 savefile->ReadRenderLight( worldMuzzleFlash );
531 if ( worldMuzzleFlashHandle >= 0 ) {
532 worldMuzzleFlashHandle = gameRenderWorld->AddLightDef( &worldMuzzleFlash );
536 savefile->ReadVec3( flashColor );
537 savefile->ReadInt( muzzleFlashEnd );
538 savefile->ReadInt( flashTime );
540 savefile->ReadBool( lightOn );
541 savefile->ReadBool( silent_fire );
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 );
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 );
556 // savegame versions <= 17
558 savefile->ReadInt( foo );
560 savefile->ReadInt( zoomFov );
562 savefile->ReadJoint( barrelJointView );
563 savefile->ReadJoint( flashJointView );
564 savefile->ReadJoint( ejectJointView );
565 savefile->ReadJoint( guiLightJointView );
566 savefile->ReadJoint( ventLightJointView );
568 savefile->ReadJoint( flashJointWorld );
569 savefile->ReadJoint( barrelJointWorld );
570 savefile->ReadJoint( ejectJointWorld );
572 savefile->ReadBool( hasBloodSplat );
574 savefile->ReadSoundShader( sndHum );
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 );
585 savefile->ReadBool( nozzleFx );
586 savefile->ReadInt( nozzleFxFade );
588 savefile->ReadInt( lastAttack );
590 savefile->ReadInt( nozzleGlowHandle );
591 savefile->ReadRenderLight( nozzleGlow );
593 if ( nozzleGlowHandle >= 0 ) {
594 nozzleGlowHandle = gameRenderWorld->AddLightDef( &nozzleGlow );
598 savefile->ReadVec3( nozzleGlowColor );
599 savefile->ReadMaterial( nozzleGlowShader );
600 savefile->ReadFloat( nozzleGlowRadius );
602 savefile->ReadInt( weaponAngleOffsetAverages );
603 savefile->ReadFloat( weaponAngleOffsetScale );
604 savefile->ReadFloat( weaponAngleOffsetMax );
605 savefile->ReadFloat( weaponOffsetTime );
606 savefile->ReadFloat( weaponOffsetScale );
608 savefile->ReadBool( allowDrop );
609 savefile->ReadObject( reinterpret_cast<idClass *&>( projectileEnt ) );
612 savefile->ReadStaticObject( grabber );
613 savefile->ReadInt( grabberState );
615 savefile->ReadJoint ( smokeJointView );
618 savefile->ReadInt( particleCount );
619 for(int i = 0; i < particleCount; i++) {
620 WeaponParticle_t newParticle;
621 memset(&newParticle, 0, sizeof(newParticle));
623 idStr name, particlename;
624 savefile->ReadString( name );
625 savefile->ReadString( particlename );
627 strcpy( newParticle.name, name.c_str() );
628 strcpy( newParticle.particlename, particlename.c_str() );
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 ) );
637 savefile->ReadObject(reinterpret_cast<idClass *&>(newParticle.emitter));
640 weaponParticles.Set(newParticle.name, newParticle);
644 savefile->ReadInt( lightCount );
645 for(int i = 0; i < lightCount; i++) {
646 WeaponLight_t newLight;
647 memset(&newLight, 0, sizeof(newLight));
650 savefile->ReadString( name );
651 strcpy( newLight.name, name.c_str() );
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 );
661 weaponLights.Set(newLight.name, newLight);
666 /***********************************************************************
668 Weapon definition management
670 ***********************************************************************/
677 void idWeapon::Clear( void ) {
678 CancelEvents( &EV_Weapon_Clear );
680 DeconstructScriptObject();
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();
691 if ( muzzleFlashHandle != -1 ) {
692 gameRenderWorld->FreeLightDef( muzzleFlashHandle );
693 muzzleFlashHandle = -1;
695 if ( muzzleFlashHandle != -1 ) {
696 gameRenderWorld->FreeLightDef( muzzleFlashHandle );
697 muzzleFlashHandle = -1;
699 if ( worldMuzzleFlashHandle != -1 ) {
700 gameRenderWorld->FreeLightDef( worldMuzzleFlashHandle );
701 worldMuzzleFlashHandle = -1;
703 if ( guiLightHandle != -1 ) {
704 gameRenderWorld->FreeLightDef( guiLightHandle );
707 if ( nozzleGlowHandle != -1 ) {
708 gameRenderWorld->FreeLightDef( nozzleGlowHandle );
709 nozzleGlowHandle = -1;
712 memset( &renderEntity, 0, sizeof( renderEntity ) );
713 renderEntity.entityNum = entityNumber;
715 renderEntity.noShadow = true;
716 renderEntity.noSelfShadow = true;
717 renderEntity.customSkin = NULL;
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;
729 if ( refSound.referenceSound ) {
730 refSound.referenceSound->Free( true );
732 memset( &refSound, 0, sizeof( refSound_t ) );
734 // setting diversity to 0 results in no random sound. -1 indicates random.
735 refSound.diversity = -1.0f;
738 // don't spatialize the weapon sounds
739 refSound.listenerId = owner->GetListenerId();
742 // clear out the sounds from our spawnargs since we'll copy them from the weapon def
743 const idKeyValue *kv = spawnArgs.MatchPrefix( "snd_" );
745 spawnArgs.Delete( kv->GetKey() );
746 kv = spawnArgs.MatchPrefix( "snd_" );
750 hideDistance = -15.0f;
751 hideStartTime = gameLocal.time - hideTime;
759 weaponSmokeStartTime = 0;
760 continuousSmoke = false;
762 strikeSmokeStartTime = 0;
764 strikeAxis = mat3_identity;
769 playerViewAxis.Identity();
770 playerViewOrigin.Zero();
771 viewWeaponAxis.Identity();
772 viewWeaponOrigin.Zero();
773 muzzleAxis.Identity();
777 status = WP_HOLSTERED;
783 projectileDict.Clear();
786 meleeDistance = 0.0f;
795 grabber.Update( owner, true );
806 muzzle_kick_time = 0;
807 muzzle_kick_maxtime = 0;
808 muzzle_kick_angles.Zero();
809 muzzle_kick_offset.Zero();
813 barrelJointView = INVALID_JOINT;
814 flashJointView = INVALID_JOINT;
815 ejectJointView = INVALID_JOINT;
816 guiLightJointView = INVALID_JOINT;
817 ventLightJointView = INVALID_JOINT;
819 barrelJointWorld = INVALID_JOINT;
820 flashJointWorld = INVALID_JOINT;
821 ejectJointWorld = INVALID_JOINT;
824 smokeJointView = INVALID_JOINT;
826 //Clean up the weapon particles
827 for(int i = 0; i < weaponParticles.Num(); i++) {
828 WeaponParticle_t* part = weaponParticles.GetIndex(i);
830 //Destroy the emitters
831 part->emitter->PostEventMS(&EV_Remove, 0 );
834 weaponParticles.Clear();
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 );
843 weaponLights.Clear();
846 hasBloodSplat = false;
850 nozzleGlowHandle = -1;
851 nozzleGlowShader = NULL;
852 nozzleGlowRadius = 10;
853 nozzleGlowColor.Zero();
855 weaponAngleOffsetAverages = 0;
856 weaponAngleOffsetScale = 0.0f;
857 weaponAngleOffsetMax = 0.0f;
858 weaponOffsetTime = 0.0f;
859 weaponOffsetScale = 0.0f;
863 animator.ClearAllAnims( gameLocal.time, 0 );
869 projectileEnt = NULL;
876 idWeapon::InitWorldModel
879 void idWeapon::InitWorldModel( const idDeclEntityDef *def ) {
882 ent = worldModel.GetEntity();
887 const char *model = def->dict.GetString( "model_world" );
888 const char *attach = def->dict.GetString( "joint_attach" );
890 ent->SetSkin( NULL );
891 if ( model[0] && attach[0] ) {
893 ent->SetModel( model );
894 if ( ent->GetAnimator()->ModelDef() ) {
895 ent->SetSkin( ent->GetAnimator()->ModelDef()->GetDefaultSkin() );
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 );
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;
915 flashJointWorld = ent->GetAnimator()->GetJointHandle( "flash" );
916 barrelJointWorld = ent->GetAnimator()->GetJointHandle( "muzzle" );
917 ejectJointWorld = ent->GetAnimator()->GetJointHandle( "eject" );
922 idWeapon::GetWeaponDef
925 void idWeapon::GetWeaponDef( const char *objectname, int ammoinclip ) {
927 const char *objectType;
930 const char *projectileName;
931 const char *brassDefName;
932 const char *smokeName;
937 if ( !objectname || !objectname[ 0 ] ) {
943 weaponDef = gameLocal.FindEntityDef( objectname );
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" );
950 icon = weaponDef->dict.GetString( "icon" );
951 silent_fire = weaponDef->dict.GetBool( "silent_fire" );
952 powerAmmo = weaponDef->dict.GetBool( "powerAmmo" );
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" );
959 hideTime = SEC2MS( weaponDef->dict.GetFloat( "hide_time", "0.3" ) );
960 hideDistance = weaponDef->dict.GetFloat( "hide_distance", "-15" );
963 smokeName = weaponDef->dict.GetString( "smoke_muzzle" );
964 if ( *smokeName != '\0' ) {
965 weaponSmoke = static_cast<const idDeclParticle *>( declManager->FindType( DECL_PARTICLE, smokeName ) );
969 continuousSmoke = weaponDef->dict.GetBool( "continuousSmoke" );
970 weaponSmokeStartTime = ( continuousSmoke ) ? gameLocal.time : 0;
972 smokeName = weaponDef->dict.GetString( "smoke_strike" );
973 if ( *smokeName != '\0' ) {
974 strikeSmoke = static_cast<const idDeclParticle *>( declManager->FindType( DECL_PARTICLE, smokeName ) );
978 strikeSmokeStartTime = 0;
980 strikeAxis = mat3_identity;
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;
992 // setup the view model
993 vmodel = weaponDef->dict.GetString( "model_view" );
996 // setup the world model
997 InitWorldModel( weaponDef );
999 // copy the sounds from the weapon view model def into out spawnargs
1000 const idKeyValue *kv = weaponDef->dict.MatchPrefix( "snd_" );
1002 spawnArgs.Set( kv->GetKey(), kv->GetValue() );
1003 kv = weaponDef->dict.MatchPrefix( "snd_", kv );
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" );
1014 idStr smokeJoint = weaponDef->dict.GetString("smoke_joint");
1015 if(smokeJoint.Length() > 0) {
1016 smokeJointView = animator.GetJointHandle( smokeJoint );
1018 smokeJointView = INVALID_JOINT;
1022 // get the projectile
1023 projectileDict.Clear();
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 );
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 );
1036 projectileDict = projectileDef->dict;
1041 // set up muzzleflash render light
1042 const idMaterial*flashShader;
1047 bool flashPointLight;
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" );
1059 memset( &muzzleFlash, 0, sizeof( muzzleFlash ) );
1060 muzzleFlash.lightId = LIGHTID_VIEW_MUZZLE_FLASH + owner->entityNumber;
1061 muzzleFlash.allowLightInViewID = owner->entityNumber+1;
1063 // the weapon lights will only be in first person
1064 guiLight.allowLightInViewID = owner->entityNumber+1;
1065 nozzleGlow.allowLightInViewID = owner->entityNumber+1;
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;
1074 muzzleFlash.lightRadius[0] = flashRadius;
1075 muzzleFlash.lightRadius[1] = flashRadius;
1076 muzzleFlash.lightRadius[2] = flashRadius;
1078 if ( !flashPointLight ) {
1079 muzzleFlash.target = flashTarget;
1080 muzzleFlash.up = flashUp;
1081 muzzleFlash.right = flashRight;
1082 muzzleFlash.end = flashTarget;
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;
1091 //-----------------------------------
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 );
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 );
1106 gameLocal.Error( "Unknown melee '%s'", meleeDefName.c_str() );
1110 // get the brass def
1112 brassDelay = weaponDef->dict.GetInt( "ejectBrassDelay", "0" );
1113 brassDefName = weaponDef->dict.GetString( "def_ejectBrass" );
1115 if ( brassDefName[0] ) {
1116 const idDeclEntityDef *brassDef = gameLocal.FindEntityDef( brassDefName, false );
1118 gameLocal.Warning( "Unknown brass '%s'", brassDefName );
1120 brassDict = brassDef->dict;
1124 if ( ( ammoType < 0 ) || ( ammoType >= AMMO_NUMTYPES ) ) {
1125 gameLocal.Warning( "Unknown ammotype in object '%s'", objectname );
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;
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);
1142 renderEntity.gui[ 0 ] = NULL;
1143 guiName = weaponDef->dict.GetString( "gui" );
1145 renderEntity.gui[ 0 ] = uiManager->FindGui( guiName, true, false, true );
1148 zoomFov = weaponDef->dict.GetInt( "zoomFov", "70" );
1149 berserk = weaponDef->dict.GetInt( "berserk", "2" );
1151 weaponAngleOffsetAverages = weaponDef->dict.GetInt( "weaponAngleOffsetAverages", "10" );
1152 weaponAngleOffsetScale = weaponDef->dict.GetFloat( "weaponAngleOffsetScale", "0.25" );
1153 weaponAngleOffsetMax = weaponDef->dict.GetFloat( "weaponAngleOffsetMax", "10" );
1155 weaponOffsetTime = weaponDef->dict.GetFloat( "weaponOffsetTime", "400" );
1156 weaponOffsetScale = weaponDef->dict.GetFloat( "weaponOffsetScale", "0.005" );
1158 if ( !weaponDef->dict.GetString( "weapon_scriptobject", NULL, &objectType ) ) {
1159 gameLocal.Error( "No 'weapon_scriptobject' set on '%s'.", objectname );
1162 // setup script object
1163 if ( !scriptObject.SetType( objectType ) ) {
1164 gameLocal.Error( "Script object '%s' not found on weapon '%s'.", objectType, objectname );
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" );
1175 spawnArgs = weaponDef->dict;
1177 shader = spawnArgs.GetString( "snd_hum" );
1178 if ( shader && *shader ) {
1179 sndHum = declManager->FindSound( shader );
1180 StartSoundShader( sndHum, SND_CHANNEL_BODY, 0, false, NULL );
1185 // call script object's constructor
1186 ConstructScriptObject();
1188 // make sure we have the correct skin
1192 idEntity *ent = worldModel.GetEntity();
1193 DetermineTimeGroup( weaponDef->dict.GetBool( "slowmo", "0" ) );
1195 ent->DetermineTimeGroup( weaponDef->dict.GetBool( "slowmo", "0" ) );
1198 //Initialize the particles
1199 if ( !gameLocal.isMultiplayer ) {
1201 const idKeyValue *pkv = weaponDef->dict.MatchPrefix( "weapon_particle", NULL );
1203 WeaponParticle_t newParticle;
1204 memset( &newParticle, 0, sizeof( newParticle ) );
1206 idStr name = pkv->GetValue();
1208 strcpy(newParticle.name, name.c_str());
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;
1216 idStr particle = weaponDef->dict.GetString(va("%s_particle", name.c_str()));
1217 strcpy(newParticle.particlename, particle.c_str());
1219 if(newParticle.smoke) {
1220 newParticle.particle = static_cast<const idDeclParticle *>( declManager->FindType( DECL_PARTICLE, particle, false ) );
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);
1230 gameLocal.SpawnEntityDef(args, &ent, false);
1231 newParticle.emitter = (idFuncEmitter*)ent;
1233 newParticle.emitter->BecomeActive(TH_THINK);
1236 weaponParticles.Set(name.c_str(), newParticle);
1238 pkv = weaponDef->dict.MatchPrefix( "weapon_particle", pkv );
1241 const idKeyValue *lkv = weaponDef->dict.MatchPrefix( "weapon_light", NULL );
1243 WeaponLight_t newLight;
1244 memset( &newLight, 0, sizeof( newLight ) );
1246 newLight.lightHandle = -1;
1247 newLight.active = false;
1248 newLight.startTime = 0;
1250 idStr name = lkv->GetValue();
1251 strcpy(newLight.name, name.c_str());
1253 idStr jointName = weaponDef->dict.GetString(va("%s_joint", name.c_str()));
1254 newLight.joint = animator.GetJointHandle(jointName.c_str());
1256 idStr shader = weaponDef->dict.GetString(va("%s_shader", name.c_str()));
1257 newLight.light.shader = declManager->FindMaterial( shader, false );
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;
1264 newLight.light.allowLightInViewID = owner->entityNumber+1;
1266 weaponLights.Set(name.c_str(), newLight);
1268 lkv = weaponDef->dict.MatchPrefix( "weapon_light", lkv );
1274 /***********************************************************************
1278 ***********************************************************************/
1285 const char *idWeapon::Icon( void ) const {
1294 void idWeapon::UpdateGUI( void ) {
1295 if ( !renderEntity.gui[ 0 ] ) {
1299 if ( status == WP_HOLSTERED ) {
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
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 ) {
1320 int inclip = AmmoInClip();
1321 int ammoamount = AmmoAvailable();
1323 if ( ammoamount < 0 ) {
1324 // show infinite ammo
1325 renderEntity.gui[ 0 ]->SetStateString( "player_ammo", "" );
1327 // show remaining ammo
1329 renderEntity.gui[ 0 ]->SetStateString( "player_totalammo", va( "%i", ammoamount) );
1331 renderEntity.gui[ 0 ]->SetStateString( "player_totalammo", va( "%i", ammoamount - inclip) );
1333 renderEntity.gui[ 0 ]->SetStateString( "player_ammo", ClipSize() ? va( "%i", inclip ) : "--" );
1334 renderEntity.gui[ 0 ]->SetStateString( "player_clips", ClipSize() ? va("%i", ammoamount / ClipSize()) : "--" );
1337 renderEntity.gui[ 0 ]->SetStateString( "player_allammo", va( "%i/%i", inclip, ammoamount ) );
1339 renderEntity.gui[ 0 ]->SetStateString( "player_allammo", va( "%i/%i", inclip, ammoamount - inclip ) );
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 ) );
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()));
1351 renderEntity.gui[ 0 ]->SetStateString( "grabber_state", va("%i", grabberState));
1355 /***********************************************************************
1357 Model and muzzleflash
1359 ***********************************************************************/
1363 idWeapon::UpdateFlashPosition
1366 void idWeapon::UpdateFlashPosition( void ) {
1367 // the flash has an explicit joint for locating it
1368 GetGlobalJointTransform( true, flashJointView, muzzleFlash.origin, muzzleFlash.axis );
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;
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;
1378 // put the world muzzle flash on the end of the joint, no matter what
1379 GetGlobalJointTransform( false, flashJointWorld, worldMuzzleFlash.origin, worldMuzzleFlash.axis );
1384 idWeapon::MuzzleFlashLight
1387 void idWeapon::MuzzleFlashLight( void ) {
1389 if ( !lightOn && ( !g_muzzleFlash.GetBool() || !muzzleFlash.lightRadius[0] ) ) {
1393 if ( flashJointView == INVALID_JOINT ) {
1397 UpdateFlashPosition();
1399 // these will be different each fire
1400 muzzleFlash.shaderParms[ SHADERPARM_TIMEOFFSET ] = -MS2SEC( gameLocal.time );
1401 muzzleFlash.shaderParms[ SHADERPARM_DIVERSITY ] = renderEntity.shaderParms[ SHADERPARM_DIVERSITY ];
1403 worldMuzzleFlash.shaderParms[ SHADERPARM_TIMEOFFSET ] = -MS2SEC( gameLocal.time );
1404 worldMuzzleFlash.shaderParms[ SHADERPARM_DIVERSITY ] = renderEntity.shaderParms[ SHADERPARM_DIVERSITY ];
1406 // the light will be removed at this time
1407 muzzleFlashEnd = gameLocal.time + flashTime;
1409 if ( muzzleFlashHandle != -1 ) {
1410 gameRenderWorld->UpdateLightDef( muzzleFlashHandle, &muzzleFlash );
1411 gameRenderWorld->UpdateLightDef( worldMuzzleFlashHandle, &worldMuzzleFlash );
1413 muzzleFlashHandle = gameRenderWorld->AddLightDef( &muzzleFlash );
1414 worldMuzzleFlashHandle = gameRenderWorld->AddLightDef( &worldMuzzleFlash );
1420 idWeapon::UpdateSkin
1423 bool idWeapon::UpdateSkin( void ) {
1424 const function_t *func;
1430 func = scriptObject.GetFunction( "UpdateSkin" );
1432 common->Warning( "Can't find function 'UpdateSkin' in object '%s'", scriptObject.GetTypeName() );
1436 // use the frameCommandThread since it's safe to use outside of framecommands
1437 gameLocal.frameCommandThread->CallFunction( this, func, true );
1438 gameLocal.frameCommandThread->Execute();
1448 void idWeapon::SetModel( const char *modelname ) {
1449 assert( modelname );
1451 if ( modelDefHandle >= 0 ) {
1452 gameRenderWorld->RemoveDecals( modelDefHandle );
1455 renderEntity.hModel = animator.SetModel( modelname );
1456 if ( renderEntity.hModel ) {
1457 renderEntity.customSkin = animator.ModelDef()->GetDefaultSkin();
1458 animator.GetJoints( &renderEntity.numJoints, &renderEntity.joints );
1460 renderEntity.customSkin = NULL;
1461 renderEntity.callback = NULL;
1462 renderEntity.numJoints = 0;
1463 renderEntity.joints = NULL;
1466 // hide the model until an animation is played
1472 idWeapon::GetGlobalJointTransform
1474 This returns the offset and axis of a weapon bone in world space, suitable for attaching models or lights
1477 bool idWeapon::GetGlobalJointTransform( bool viewModel, const jointHandle_t jointHandle, idVec3 &offset, idMat3 &axis ) {
1480 if ( animator.GetJointTransform( jointHandle, gameLocal.time, offset, axis ) ) {
1481 offset = offset * viewWeaponAxis + viewWeaponOrigin;
1482 axis = axis * viewWeaponAxis;
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();
1493 offset = viewWeaponOrigin;
1494 axis = viewWeaponAxis;
1500 idWeapon::SetPushVelocity
1503 void idWeapon::SetPushVelocity( const idVec3 &pushVelocity ) {
1504 this->pushVelocity = pushVelocity;
1508 /***********************************************************************
1510 State control/player interface
1512 ***********************************************************************/
1519 void idWeapon::Think( void ) {
1520 // do nothing because the present is called from the player through PresentWeapon
1528 void idWeapon::Raise( void ) {
1530 WEAPON_RAISEWEAPON = true;
1539 void idWeapon::PutAway( void ) {
1540 hasBloodSplat = false;
1542 WEAPON_LOWERWEAPON = true;
1549 NOTE: this is only for impulse-triggered reload, auto reload is scripted
1552 void idWeapon::Reload( void ) {
1554 WEAPON_RELOAD = true;
1560 idWeapon::LowerWeapon
1563 void idWeapon::LowerWeapon( void ) {
1566 hideEnd = hideDistance;
1567 if ( gameLocal.time - hideStartTime < hideTime ) {
1568 hideStartTime = gameLocal.time - ( hideTime - ( gameLocal.time - hideStartTime ) );
1570 hideStartTime = gameLocal.time;
1578 idWeapon::RaiseWeapon
1581 void idWeapon::RaiseWeapon( void ) {
1585 hideStart = hideDistance;
1587 if ( gameLocal.time - hideStartTime < hideTime ) {
1588 hideStartTime = gameLocal.time - ( hideTime - ( gameLocal.time - hideStartTime ) );
1590 hideStartTime = gameLocal.time;
1598 idWeapon::HideWeapon
1601 void idWeapon::HideWeapon( void ) {
1603 if ( worldModel.GetEntity() ) {
1604 worldModel.GetEntity()->Hide();
1611 idWeapon::ShowWeapon
1614 void idWeapon::ShowWeapon( void ) {
1616 if ( worldModel.GetEntity() ) {
1617 worldModel.GetEntity()->Show();
1626 idWeapon::HideWorldModel
1629 void idWeapon::HideWorldModel( void ) {
1630 if ( worldModel.GetEntity() ) {
1631 worldModel.GetEntity()->Hide();
1637 idWeapon::ShowWorldModel
1640 void idWeapon::ShowWorldModel( void ) {
1641 if ( worldModel.GetEntity() ) {
1642 worldModel.GetEntity()->Show();
1651 void idWeapon::OwnerDied( void ) {
1653 SetState( "OwnerDied", 0 );
1657 // Update the grabber effects
1658 if ( /*!gameLocal.isMultiplayer &&*/ grabberState != -1 ) {
1659 grabber.Update( owner, hide );
1665 if ( worldModel.GetEntity() ) {
1666 worldModel.GetEntity()->Hide();
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 );
1676 idWeapon::BeginAttack
1679 void idWeapon::BeginAttack( void ) {
1680 if ( status != WP_OUTOFAMMO ) {
1681 lastAttack = gameLocal.time;
1688 if ( !WEAPON_ATTACK ) {
1689 if ( sndHum && grabberState == -1 ) { // _D3XP :: don't stop grabber hum
1690 StopSound( SND_CHANNEL_BODY, false );
1693 WEAPON_ATTACK = true;
1701 void idWeapon::EndAttack( void ) {
1702 if ( !WEAPON_ATTACK.IsLinked() ) {
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 );
1718 bool idWeapon::IsReady( void ) const {
1719 return !hide && !IsHidden() && ( ( status == WP_RELOAD ) || ( status == WP_READY ) || ( status == WP_OUTOFAMMO ) );
1724 idWeapon::IsReloading
1727 bool idWeapon::IsReloading( void ) const {
1728 return ( status == WP_RELOAD );
1733 idWeapon::IsHolstered
1736 bool idWeapon::IsHolstered( void ) const {
1737 return ( status == WP_HOLSTERED );
1742 idWeapon::ShowCrosshair
1745 bool idWeapon::ShowCrosshair( void ) const {
1746 return !( state == idStr( WP_RISING ) || state == idStr( WP_LOWERING ) || state == idStr( WP_HOLSTERED ) );
1750 =====================
1752 =====================
1754 bool idWeapon::CanDrop( void ) const {
1755 if ( !weaponDef || !worldModel.GetEntity() ) {
1758 const char *classname = weaponDef->dict.GetString( "def_dropItem" );
1759 if ( !classname[ 0 ] ) {
1767 idWeapon::WeaponStolen
1770 void idWeapon::WeaponStolen( void ) {
1771 assert( !gameLocal.isClient );
1772 if ( projectileEnt ) {
1774 SetState( "WeaponStolen", 0 );
1777 projectileEnt = NULL;
1780 // set to holstered so we can switch weapons right away
1781 status = WP_HOLSTERED;
1787 =====================
1789 =====================
1791 idEntity * idWeapon::DropItem( const idVec3 &velocity, int activateDelay, int removeDelay, bool died ) {
1792 if ( !weaponDef || !worldModel.GetEntity() ) {
1798 const char *classname = weaponDef->dict.GetString( "def_dropItem" );
1799 if ( !classname[0] ) {
1802 StopSound( SND_CHANNEL_BODY, true );
1803 StopSound( SND_CHANNEL_BODY3, true );
1805 return idMoveableItem::DropItem( classname, worldModel.GetEntity()->GetPhysics()->GetOrigin(), worldModel.GetEntity()->GetPhysics()->GetAxis(), velocity, activateDelay, removeDelay );
1808 /***********************************************************************
1810 Script state management
1812 ***********************************************************************/
1815 =====================
1817 =====================
1819 void idWeapon::SetState( const char *statename, int blendFrames ) {
1820 const function_t *func;
1826 func = scriptObject.GetFunction( statename );
1829 gameLocal.Error( "Can't find function '%s' in object '%s'", statename, scriptObject.GetTypeName() );
1832 thread->CallFunction( this, func, true );
1835 animBlendFrames = blendFrames;
1836 if ( g_debugWeapon.GetBool() ) {
1837 gameLocal.Printf( "%d: weapon state : %s\n", gameLocal.time, statename );
1844 /***********************************************************************
1848 ***********************************************************************/
1852 idWeapon::UpdateNozzelFx
1855 void idWeapon::UpdateNozzleFx( void ) {
1863 int la = gameLocal.time - lastAttack + 1;
1866 if ( la < nozzleFxFade ) {
1867 s = ((float)la / nozzleFxFade);
1870 renderEntity.shaderParms[5] = s;
1871 renderEntity.shaderParms[6] = l;
1873 if ( ventLightJointView == INVALID_JOINT ) {
1880 if ( nozzleGlowHandle == -1 ) {
1881 memset(&nozzleGlow, 0, sizeof(nozzleGlow));
1883 nozzleGlow.allowLightInViewID = owner->entityNumber+1;
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);
1897 GetGlobalJointTransform( true, ventLightJointView, nozzleGlow.origin, nozzleGlow.axis );
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);
1908 idWeapon::BloodSplat
1911 bool idWeapon::BloodSplat( float size ) {
1913 idMat3 localAxis, axistemp;
1914 idVec3 localOrigin, normal;
1916 if ( hasBloodSplat ) {
1920 hasBloodSplat = true;
1922 if ( modelDefHandle < 0 ) {
1926 if ( !GetGlobalJointTransform( true, ejectJointView, localOrigin, localAxis ) ) {
1930 localOrigin[0] += gameLocal.random.RandomFloat() * -10.0f;
1931 localOrigin[1] += gameLocal.random.RandomFloat() * 1.0f;
1932 localOrigin[2] += gameLocal.random.RandomFloat() * -2.0f;
1934 normal = idVec3( gameLocal.random.CRandomFloat(), -gameLocal.random.RandomFloat(), -1 );
1937 idMath::SinCos16( gameLocal.random.RandomFloat() * idMath::TWO_PI, s, c );
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;
1944 localAxis[0] *= 1.0f / size;
1945 localAxis[1] *= 1.0f / size;
1947 idPlane localPlane[2];
1949 localPlane[0] = localAxis[0];
1950 localPlane[0][3] = -(localOrigin * localAxis[0]) + 0.5f;
1952 localPlane[1] = localAxis[1];
1953 localPlane[1][3] = -(localOrigin * localAxis[1]) + 0.5f;
1955 const idMaterial *mtr = declManager->FindMaterial( "textures/decals/duffysplatgun" );
1957 gameRenderWorld->ProjectOverlay( modelDefHandle, localPlane, mtr );
1963 /***********************************************************************
1967 ***********************************************************************/
1971 idWeapon::MuzzleRise
1973 The machinegun and chaingun will incrementally back up as they are being fired
1976 void idWeapon::MuzzleRise( idVec3 &origin, idMat3 &axis ) {
1982 time = kick_endtime - gameLocal.time;
1987 if ( muzzle_kick_maxtime <= 0 ) {
1991 if ( time > muzzle_kick_maxtime ) {
1992 time = muzzle_kick_maxtime;
1995 amount = ( float )time / ( float )muzzle_kick_maxtime;
1996 ang = muzzle_kick_angles * amount;
1997 offset = muzzle_kick_offset * amount;
1999 origin = origin - axis * offset;
2000 axis = ang.ToMat3() * axis;
2005 idWeapon::ConstructScriptObject
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.
2011 idThread *idWeapon::ConstructScriptObject( void ) {
2012 const function_t *constructor;
2014 thread->EndThread();
2016 // call script object's constructor
2017 constructor = scriptObject.GetConstructor();
2018 if ( !constructor ) {
2019 gameLocal.Error( "Missing constructor on '%s' for weapon", scriptObject.GetTypeName() );
2022 // init the script object's data
2023 scriptObject.ClearObject();
2024 thread->CallFunction( this, constructor, true );
2032 idWeapon::DeconstructScriptObject
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.
2039 void idWeapon::DeconstructScriptObject( void ) {
2040 const function_t *destructor;
2046 // don't bother calling the script object's destructor on map shutdown
2047 if ( gameLocal.GameState() == GAMESTATE_SHUTDOWN ) {
2051 thread->EndThread();
2053 // call script object's destructor
2054 destructor = scriptObject.GetDestructor();
2056 // start a thread that will run immediately and end
2057 thread->CallFunction( this, destructor, true );
2059 thread->EndThread();
2062 // clear out the object's memory
2063 scriptObject.ClearObject();
2068 idWeapon::UpdateScript
2071 void idWeapon::UpdateScript( void ) {
2078 // only update the script on new frames
2079 if ( !gameLocal.isNewFrame ) {
2083 if ( idealState.Length() ) {
2084 SetState( idealState, animBlendFrames );
2087 // update script state, which may call Event_LaunchProjectiles, among other things
2089 while( ( thread->Execute() || idealState.Length() ) && count-- ) {
2090 // happens for weapons with no clip (like grenades)
2091 if ( idealState.Length() ) {
2092 SetState( idealState, animBlendFrames );
2096 WEAPON_RELOAD = false;
2101 idWeapon::AlertMonsters
2104 void idWeapon::AlertMonsters( void ) {
2107 idVec3 end = muzzleFlash.origin + muzzleFlash.axis * muzzleFlash.target;
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 );
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 );
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 );
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 );
2147 idWeapon::PresentWeapon
2150 void idWeapon::PresentWeapon( bool showViewModel ) {
2151 playerViewOrigin = owner->firstPersonViewOrigin;
2152 playerViewAxis = owner->firstPersonViewAxis;
2154 // calculate weapon position based on player movement bobbing
2155 owner->CalculateViewWeaponPos( viewWeaponOrigin, viewWeaponAxis );
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 ) {
2163 frac = 1.0f - frac * frac;
2167 hideOffset = hideStart + ( hideEnd - hideStart ) * frac;
2169 hideOffset = hideEnd;
2170 if ( hide && disabled ) {
2174 viewWeaponOrigin += hideOffset * viewWeaponAxis[ 2 ];
2176 // kick up based on repeat firing
2177 MuzzleRise( viewWeaponOrigin, viewWeaponAxis );
2179 // set the physics position and orientation
2180 GetPhysics()->SetOrigin( viewWeaponOrigin );
2181 GetPhysics()->SetAxis( viewWeaponAxis );
2184 // update the weapon script
2192 // only show the surface in player view
2193 renderEntity.allowSurfaceInViewID = owner->entityNumber+1;
2195 // crunch the depth range so it never pokes into walls this breaks the machine gun gui
2196 renderEntity.weaponDepthHack = true;
2198 // present the model
2199 if ( showViewModel ) {
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;
2211 worldModel.GetEntity()->GetRenderEntity()->suppressShadowInViewID = owner->entityNumber+1;
2212 worldModel.GetEntity()->GetRenderEntity()->suppressShadowInLightID = LIGHTID_VIEW_MUZZLE_FLASH + owner->entityNumber;
2221 if ( showViewModel && !disabled && weaponSmoke && ( weaponSmokeStartTime != 0 ) ) {
2222 // use the barrel joint if available
2225 if(smokeJointView != INVALID_JOINT) {
2226 GetGlobalJointTransform( true, smokeJointView, muzzleOrigin, muzzleAxis );
2227 } else if (barrelJointView != INVALID_JOINT) {
2228 GetGlobalJointTransform( true, barrelJointView, muzzleOrigin, muzzleAxis );
2230 if ( barrelJointView ) {
2231 GetGlobalJointTransform( true, barrelJointView, muzzleOrigin, muzzleAxis );
2234 // default to going straight out the view
2235 muzzleOrigin = playerViewOrigin;
2236 muzzleAxis = playerViewAxis;
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;
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;
2252 if ( showViewModel && !hide ) {
2254 for( int i = 0; i < weaponParticles.Num(); i++ ) {
2255 WeaponParticle_t* part = weaponParticles.GetIndex(i);
2259 if(part->joint != INVALID_JOINT) {
2260 GetGlobalJointTransform( true, part->joint, muzzleOrigin, muzzleAxis );
2262 // default to going straight out the view
2263 muzzleOrigin = playerViewOrigin;
2264 muzzleAxis = playerViewAxis;
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;
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 );
2275 if ( part->emitter->GetModelDefHandle() != -1 ) {
2276 gameRenderWorld->UpdateEntityDef( part->emitter->GetModelDefHandle(), rendEnt );
2282 for(int i = 0; i < weaponLights.Num(); i++) {
2283 WeaponLight_t* light = weaponLights.GetIndex(i);
2287 GetGlobalJointTransform( true, light->joint, light->light.origin, light->light.axis );
2288 if ( ( light->lightHandle != -1 ) ) {
2289 gameRenderWorld->UpdateLightDef( light->lightHandle, &light->light );
2291 light->lightHandle = gameRenderWorld->AddLightDef( &light->light );
2297 // Update the grabber effects
2298 if ( grabberState != -1 ) {
2299 grabberState = grabber.Update( owner, hide );
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;
2309 if ( worldMuzzleFlashHandle != -1 ) {
2310 gameRenderWorld->FreeLightDef( worldMuzzleFlashHandle );
2311 worldMuzzleFlashHandle = -1;
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 );
2321 // wake up monsters with the flashlight
2322 if ( !gameLocal.isMultiplayer && lightOn && !owner->fl.notarget ) {
2327 // update the gui light
2328 if ( guiLight.lightRadius[0] && guiLightJointView != INVALID_JOINT ) {
2329 GetGlobalJointTransform( true, guiLightJointView, guiLight.origin, guiLight.axis );
2331 if ( ( guiLightHandle != -1 ) ) {
2332 gameRenderWorld->UpdateLightDef( guiLightHandle, &guiLight );
2334 guiLightHandle = gameRenderWorld->AddLightDef( &guiLight );
2338 if ( status != WP_READY && sndHum ) {
2339 StopSound( SND_CHANNEL_BODY, false );
2347 idWeapon::EnterCinematic
2350 void idWeapon::EnterCinematic( void ) {
2351 StopSound( SND_CHANNEL_ANY, false );
2354 SetState( "EnterCinematic", 0 );
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;
2366 grabber.Update( this->GetOwner(), true );
2377 idWeapon::ExitCinematic
2380 void idWeapon::ExitCinematic( void ) {
2384 SetState( "ExitCinematic", 0 );
2393 idWeapon::NetCatchup
2396 void idWeapon::NetCatchup( void ) {
2398 SetState( "NetCatchup", 0 );
2405 idWeapon::GetZoomFov
2408 int idWeapon::GetZoomFov( void ) {
2414 idWeapon::GetWeaponAngleOffsets
2417 void idWeapon::GetWeaponAngleOffsets( int *average, float *scale, float *max ) {
2418 *average = weaponAngleOffsetAverages;
2419 *scale = weaponAngleOffsetScale;
2420 *max = weaponAngleOffsetMax;
2425 idWeapon::GetWeaponTimeOffsets
2428 void idWeapon::GetWeaponTimeOffsets( float *time, float *scale ) {
2429 *time = weaponOffsetTime;
2430 *scale = weaponOffsetScale;
2434 /***********************************************************************
2438 ***********************************************************************/
2442 idWeapon::GetAmmoNumForName
2445 ammo_t idWeapon::GetAmmoNumForName( const char *ammoname ) {
2447 const idDict *ammoDict;
2451 ammoDict = gameLocal.FindEntityDefDict( "ammo_types", false );
2453 gameLocal.Error( "Could not find entity definition for 'ammo_types'\n" );
2456 if ( !ammoname[ 0 ] ) {
2460 if ( !ammoDict->GetInt( ammoname, "-1", num ) ) {
2462 //Lets look in a game specific ammo type definition for the weapon
2465 for ( i = 0; i < 2; i++ ) {
2467 gamedir = cvarSystem->GetCVarString( "fs_game_base" );
2468 } else if ( i == 1 ) {
2469 gamedir = cvarSystem->GetCVarString( "fs_game" );
2471 if ( gamedir.Length() > 0 ) {
2472 ammoDict = gameLocal.FindEntityDefDict( va("ammo_types_%s", gamedir.c_str()), false );
2474 if ( ammoDict->GetInt( ammoname, "-1", num ) ) {
2481 gameLocal.Error( "Unknown ammo type '%s'", ammoname );
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 );
2490 return ( ammo_t )num;
2495 idWeapon::GetAmmoNameForNum
2498 const char *idWeapon::GetAmmoNameForNum( ammo_t ammonum ) {
2501 const idDict *ammoDict;
2502 const idKeyValue *kv;
2505 ammoDict = gameLocal.FindEntityDefDict( "ammo_types", false );
2507 gameLocal.Error( "Could not find entity definition for 'ammo_types'\n" );
2510 sprintf( text, "%d", ammonum );
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();
2521 // Look in the game specific ammo types
2523 for ( i = 0; i < 2; i++ ) {
2525 gamedir = cvarSystem->GetCVarString( "fs_game_base" );
2526 } else if ( i == 1 ) {
2527 gamedir = cvarSystem->GetCVarString( "fs_game" );
2529 if ( gamedir.Length() > 0 ) {
2530 ammoDict = gameLocal.FindEntityDefDict( va("ammo_types_%s", gamedir.c_str()), false );
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();
2549 idWeapon::GetAmmoPickupNameForNum
2552 const char *idWeapon::GetAmmoPickupNameForNum( ammo_t ammonum ) {
2555 const idDict *ammoDict;
2556 const idKeyValue *kv;
2558 ammoDict = gameLocal.FindEntityDefDict( "ammo_names", false );
2560 gameLocal.Error( "Could not find entity definition for 'ammo_names'\n" );
2563 const char *name = GetAmmoNameForNum( ammonum );
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();
2580 idWeapon::AmmoAvailable
2583 int idWeapon::AmmoAvailable( void ) const {
2585 return owner->inventory.HasAmmo( ammoType, ammoRequired );
2593 idWeapon::AmmoInClip
2596 int idWeapon::AmmoInClip( void ) const {
2602 idWeapon::ResetAmmoClip
2605 void idWeapon::ResetAmmoClip( void ) {
2611 idWeapon::GetAmmoType
2614 ammo_t idWeapon::GetAmmoType( void ) const {
2623 int idWeapon::ClipSize( void ) const {
2632 int idWeapon::LowAmmo() const {
2638 idWeapon::AmmoRequired
2641 int idWeapon::AmmoRequired( void ) const {
2642 return ammoRequired;
2648 idWeapon::GetGrabberState
2650 Returns the current grabberState
2653 int idWeapon::GetGrabberState() const {
2655 return grabberState;
2662 Returns the total number of rounds regardless of the required ammo
2665 int idWeapon::AmmoCount() const {
2668 return owner->inventory.HasAmmo( ammoType, 1 );
2677 idWeapon::WriteToSnapshot
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 );
2689 idWeapon::ReadFromSnapshot
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;
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() ) {
2701 // immediately go to the firing state so we don't skip fire animations
2702 if ( !WEAPON_NETFIRING && isFiring ) {
2703 idealState = "Fire";
2706 // immediately switch back to idle
2707 if ( WEAPON_NETFIRING && !isFiring ) {
2708 idealState = "Idle";
2711 WEAPON_NETFIRING = isFiring;
2714 if ( snapLight != lightOn ) {
2721 idWeapon::ClientReceiveEvent
2724 bool idWeapon::ClientReceiveEvent( int event, int time, const idBitMsg &msg ) {
2727 case EVENT_RELOAD: {
2728 if ( gameLocal.time - time < 1000 ) {
2729 if ( WEAPON_NETRELOAD.IsLinked() ) {
2730 WEAPON_NETRELOAD = true;
2731 WEAPON_NETENDRELOAD = false;
2736 case EVENT_ENDRELOAD: {
2737 if ( WEAPON_NETENDRELOAD.IsLinked() ) {
2738 WEAPON_NETENDRELOAD = true;
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;
2746 if ( worldModel.GetEntity() ) {
2747 worldModel.GetEntity()->SetSkin( renderEntity.customSkin );
2752 return idEntity::ClientReceiveEvent( event, time, msg );
2758 /***********************************************************************
2762 ***********************************************************************/
2766 idWeapon::Event_Clear
2769 void idWeapon::Event_Clear( void ) {
2775 idWeapon::Event_GetOwner
2778 void idWeapon::Event_GetOwner( void ) {
2779 idThread::ReturnEntity( owner );
2784 idWeapon::Event_WeaponState
2787 void idWeapon::Event_WeaponState( const char *statename, int blendFrames ) {
2788 const function_t *func;
2790 func = scriptObject.GetFunction( statename );
2793 gameLocal.Error( "Can't find function '%s' in object '%s'", statename, scriptObject.GetTypeName() );
2796 idealState = statename;
2798 if ( !idealState.Icmp( "Fire" ) ) {
2804 animBlendFrames = blendFrames;
2805 thread->DoneProcessing();
2810 idWeapon::Event_WeaponReady
2813 void idWeapon::Event_WeaponReady( void ) {
2816 WEAPON_RAISEWEAPON = false;
2819 StartSoundShader( sndHum, SND_CHANNEL_BODY, 0, false, NULL );
2826 idWeapon::Event_WeaponOutOfAmmo
2829 void idWeapon::Event_WeaponOutOfAmmo( void ) {
2830 status = WP_OUTOFAMMO;
2832 WEAPON_RAISEWEAPON = false;
2838 idWeapon::Event_WeaponReloading
2841 void idWeapon::Event_WeaponReloading( void ) {
2847 idWeapon::Event_WeaponHolstered
2850 void idWeapon::Event_WeaponHolstered( void ) {
2851 status = WP_HOLSTERED;
2853 WEAPON_LOWERWEAPON = false;
2859 idWeapon::Event_WeaponRising
2862 void idWeapon::Event_WeaponRising( void ) {
2865 WEAPON_LOWERWEAPON = false;
2867 owner->WeaponRisingCallback();
2872 idWeapon::Event_WeaponLowering
2875 void idWeapon::Event_WeaponLowering( void ) {
2876 status = WP_LOWERING;
2878 WEAPON_RAISEWEAPON = false;
2880 owner->WeaponLoweringCallback();
2885 idWeapon::Event_UseAmmo
2888 void idWeapon::Event_UseAmmo( int amount ) {
2889 if ( gameLocal.isClient ) {
2893 owner->inventory.UseAmmo( ammoType, ( powerAmmo ) ? amount : ( amount * ammoRequired ) );
2894 if ( clipSize && ammoRequired ) {
2895 ammoClip -= powerAmmo ? amount : ( amount * ammoRequired );
2896 if ( ammoClip < 0 ) {
2904 idWeapon::Event_AddToClip
2907 void idWeapon::Event_AddToClip( int amount ) {
2910 if ( gameLocal.isClient ) {
2915 int oldAmmo = ammoClip;
2916 ammoAvail = owner->inventory.HasAmmo( ammoType, ammoRequired ) + AmmoInClip();
2920 if ( ammoClip > clipSize ) {
2921 ammoClip = clipSize;
2926 ammoAvail = owner->inventory.HasAmmo( ammoType, ammoRequired );
2929 if ( ammoClip > ammoAvail ) {
2930 ammoClip = ammoAvail;
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);
2942 idWeapon::Event_AmmoInClip
2945 void idWeapon::Event_AmmoInClip( void ) {
2946 int ammo = AmmoInClip();
2947 idThread::ReturnFloat( ammo );
2952 idWeapon::Event_AmmoAvailable
2955 void idWeapon::Event_AmmoAvailable( void ) {
2957 int ammoAvail = owner->inventory.HasAmmo( ammoType, ammoRequired );
2958 ammoAvail += AmmoInClip();
2960 int ammoAvail = owner->inventory.HasAmmo( ammoType, ammoRequired );
2963 idThread::ReturnFloat( ammoAvail );
2968 idWeapon::Event_TotalAmmoCount
2971 void idWeapon::Event_TotalAmmoCount( void ) {
2972 int ammoAvail = owner->inventory.HasAmmo( ammoType, 1 );
2973 idThread::ReturnFloat( ammoAvail );
2978 idWeapon::Event_ClipSize
2981 void idWeapon::Event_ClipSize( void ) {
2982 idThread::ReturnFloat( clipSize );
2987 idWeapon::Event_AutoReload
2990 void idWeapon::Event_AutoReload( void ) {
2992 if ( gameLocal.isClient ) {
2993 idThread::ReturnFloat( 0.0f );
2996 idThread::ReturnFloat( gameLocal.userInfo[ owner->entityNumber ].GetBool( "ui_autoReload" ) );
3001 idWeapon::Event_NetReload
3004 void idWeapon::Event_NetReload( void ) {
3006 if ( gameLocal.isServer ) {
3007 ServerSendEvent( EVENT_RELOAD, NULL, false, -1 );
3013 idWeapon::Event_NetEndReload
3016 void idWeapon::Event_NetEndReload( void ) {
3018 if ( gameLocal.isServer ) {
3019 ServerSendEvent( EVENT_ENDRELOAD, NULL, false, -1 );
3025 idWeapon::Event_PlayAnim
3028 void idWeapon::Event_PlayAnim( int channel, const char *animname ) {
3031 anim = animator.GetAnim( animname );
3033 gameLocal.Warning( "missing '%s' animation on '%s' (%s)", animname, name.c_str(), GetEntityDefName() );
3034 animator.Clear( channel, gameLocal.time, FRAME2MS( animBlendFrames ) );
3037 if ( !( owner && owner->GetInfluenceLevel() ) ) {
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 );
3045 worldModel.GetEntity()->GetAnimator()->PlayAnim( channel, anim, gameLocal.time, FRAME2MS( animBlendFrames ) );
3049 animBlendFrames = 0;
3050 idThread::ReturnInt( 0 );
3055 idWeapon::Event_PlayCycle
3058 void idWeapon::Event_PlayCycle( int channel, const char *animname ) {
3061 anim = animator.GetAnim( animname );
3063 gameLocal.Warning( "missing '%s' animation on '%s' (%s)", animname, name.c_str(), GetEntityDefName() );
3064 animator.Clear( channel, gameLocal.time, FRAME2MS( animBlendFrames ) );
3067 if ( !( owner && owner->GetInfluenceLevel() ) ) {
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 ) );
3077 animBlendFrames = 0;
3078 idThread::ReturnInt( 0 );
3083 idWeapon::Event_AnimDone
3086 void idWeapon::Event_AnimDone( int channel, int blendFrames ) {
3087 if ( animDoneTime - FRAME2MS( blendFrames ) <= gameLocal.time ) {
3088 idThread::ReturnInt( true );
3090 idThread::ReturnInt( false );
3096 idWeapon::Event_SetBlendFrames
3099 void idWeapon::Event_SetBlendFrames( int channel, int blendFrames ) {
3100 animBlendFrames = blendFrames;
3105 idWeapon::Event_GetBlendFrames
3108 void idWeapon::Event_GetBlendFrames( int channel ) {
3109 idThread::ReturnInt( animBlendFrames );
3114 idWeapon::Event_Next
3117 void idWeapon::Event_Next( void ) {
3118 // change to another weapon if possible
3119 owner->NextBestWeapon();
3124 idWeapon::Event_SetSkin
3127 void idWeapon::Event_SetSkin( const char *skinname ) {
3128 const idDeclSkin *skinDecl;
3130 if ( !skinname || !skinname[ 0 ] ) {
3133 skinDecl = declManager->FindSkin( skinname );
3136 renderEntity.customSkin = skinDecl;
3139 if ( worldModel.GetEntity() ) {
3140 worldModel.GetEntity()->SetSkin( skinDecl );
3143 if ( gameLocal.isServer ) {
3145 byte msgBuf[MAX_EVENT_PARAM_SIZE];
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 );
3155 idWeapon::Event_Flashlight
3158 void idWeapon::Event_Flashlight( int enable ) {
3170 idWeapon::Event_GetLightParm
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 );
3178 idThread::ReturnFloat( muzzleFlash.shaderParms[ parmnum ] );
3183 idWeapon::Event_SetLightParm
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 );
3191 muzzleFlash.shaderParms[ parmnum ] = value;
3192 worldMuzzleFlash.shaderParms[ parmnum ] = value;
3198 idWeapon::Event_SetLightParms
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;
3207 worldMuzzleFlash.shaderParms[ SHADERPARM_RED ] = parm0;
3208 worldMuzzleFlash.shaderParms[ SHADERPARM_GREEN ] = parm1;
3209 worldMuzzleFlash.shaderParms[ SHADERPARM_BLUE ] = parm2;
3210 worldMuzzleFlash.shaderParms[ SHADERPARM_ALPHA ] = parm3;
3218 idWeapon::Event_Grabber
3221 void idWeapon::Event_Grabber( int enable ) {
3231 idWeapon::Event_GrabberHasTarget
3234 void idWeapon::Event_GrabberHasTarget() {
3235 idThread::ReturnInt( grabberState );
3240 idWeapon::Event_GrabberSetGrabDistance
3243 void idWeapon::Event_GrabberSetGrabDistance( float dist ) {
3245 grabber.SetDragDistance( dist );
3251 idWeapon::Event_CreateProjectile
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();
3263 idThread::ReturnEntity( projectileEnt );
3265 idThread::ReturnEntity( NULL );
3271 idWeapon::Event_LaunchProjectiles
3274 void idWeapon::Event_LaunchProjectiles( int num_projectiles, float spread, float fuseOffset, float launchPower, float dmgPower ) {
3285 idBounds ownerBounds, projBounds;
3291 if ( !projectileDict.GetNumKeyVals() ) {
3292 const char *classname = weaponDef->dict.GetString( "classname" );
3293 gameLocal.Warning( "No projectile defined on '%s'", classname );
3297 // avoid all ammo considerations on an MP client
3298 if ( !gameLocal.isClient ) {
3302 int ammoAvail = owner->inventory.HasAmmo( ammoType, ammoRequired );
3303 if ( ( clipSize != 0 ) && ( ammoClip <= 0 ) ) {
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 ) ) ) {
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
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;
3329 //Weapons with a clip size of 0 launch strait from inventory without moving to a clip
3331 //In D3XP we used the ammo when the ammo was moved into the clip so we don't want to
3333 owner->inventory.UseAmmo( ammoType, ( powerAmmo ) ? dmgPower : ammoRequired );
3339 if ( clipSize && ammoRequired ) {
3341 ammoClip -= powerAmmo ? dmgPower : ammoRequired;
3343 ammoClip -= powerAmmo ? dmgPower : 1;
3349 if ( !silent_fire ) {
3350 // wake up nearby monsters
3351 gameLocal.AlertAI( owner );
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 );
3359 if ( worldModel.GetEntity() ) {
3360 worldModel.GetEntity()->SetShaderParm( SHADERPARM_DIVERSITY, renderEntity.shaderParms[ SHADERPARM_DIVERSITY ] );
3361 worldModel.GetEntity()->SetShaderParm( SHADERPARM_TIMEOFFSET, renderEntity.shaderParms[ SHADERPARM_TIMEOFFSET ] );
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 );
3369 // go straight out of the view
3370 muzzleOrigin = playerViewOrigin;
3371 muzzleAxis = playerViewAxis;
3374 // add some to the kick time, incrementally moving repeat firing weapons back
3375 if ( kick_endtime < gameLocal.realClientTime ) {
3376 kick_endtime = gameLocal.realClientTime;
3378 kick_endtime += muzzle_kick_time;
3379 if ( kick_endtime > gameLocal.realClientTime + muzzle_kick_maxtime ) {
3380 kick_endtime = gameLocal.realClientTime + muzzle_kick_maxtime;
3383 if ( gameLocal.isClient ) {
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 ) );
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 );
3403 ownerBounds = owner->GetPhysics()->GetAbsBounds();
3405 owner->AddProjectilesFired( num_projectiles );
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 ) );
3414 if ( projectileEnt ) {
3415 ent = projectileEnt;
3418 projectileEnt = NULL;
3420 gameLocal.SpawnEntityDef( projectileDict, &ent, false );
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 );
3428 if ( projectileDict.GetBool( "net_instanthit" ) ) {
3429 // don't synchronize this on top of the already predicted effect
3430 ent->fl.networkSync = false;
3433 proj = static_cast<idProjectile *>(ent);
3434 proj->Create( owner, muzzleOrigin, dir );
3436 projBounds = proj->GetPhysics()->GetBounds().Rotate( proj->GetPhysics()->GetAxis() );
3438 // make sure the projectile starts inside the bounding box of the owner
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];
3444 start = ownerBounds.GetCenter();
3446 gameLocal.clip.Translation( tr, start, muzzle_pos, proj->GetPhysics()->GetClipModel(), proj->GetPhysics()->GetClipModel()->GetAxis(), MASK_SHOT_RENDERMODEL, owner );
3447 muzzle_pos = tr.endpos;
3450 proj->Launch( muzzle_pos, dir, pushVelocity, fuseOffset, launchPower, dmgPower );
3457 PostEventMS( &EV_Weapon_EjectBrass, brassDelay );
3460 // add the light for the muzzleflash
3465 owner->WeaponFireFeedback( &weaponDef->dict );
3467 // reset muzzle smoke
3468 weaponSmokeStartTime = gameLocal.realClientTime;
3474 idWeapon::Event_LaunchProjectilesEllipse
3477 void idWeapon::Event_LaunchProjectilesEllipse( int num_projectiles, float spreada, float spreadb, float fuseOffset, float power ) {
3488 idBounds ownerBounds, projBounds;
3494 if ( !projectileDict.GetNumKeyVals() ) {
3495 const char *classname = weaponDef->dict.GetString( "classname" );
3496 gameLocal.Warning( "No projectile defined on '%s'", classname );
3500 // avoid all ammo considerations on a client
3501 if ( !gameLocal.isClient ) {
3503 int ammoAvail = owner->inventory.HasAmmo( ammoType, ammoRequired );
3504 if ( ( clipSize != 0 ) && ( ammoClip <= 0 ) ) {
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 );
3513 if ( clipSize && ammoRequired ) {
3514 ammoClip -= ammoRequired;
3517 if ( !silent_fire ) {
3518 // wake up nearby monsters
3519 gameLocal.AlertAI( owner );
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 );
3529 if ( worldModel.GetEntity() ) {
3530 worldModel.GetEntity()->SetShaderParm( SHADERPARM_DIVERSITY, renderEntity.shaderParms[ SHADERPARM_DIVERSITY ] );
3531 worldModel.GetEntity()->SetShaderParm( SHADERPARM_TIMEOFFSET, renderEntity.shaderParms[ SHADERPARM_TIMEOFFSET ] );
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 );
3539 // go straight out of the view
3540 muzzleOrigin = playerViewOrigin;
3541 muzzleAxis = playerViewAxis;
3544 // add some to the kick time, incrementally moving repeat firing weapons back
3545 if ( kick_endtime < gameLocal.time ) {
3546 kick_endtime = gameLocal.time;
3548 kick_endtime += muzzle_kick_time;
3549 if ( kick_endtime > gameLocal.time + muzzle_kick_maxtime ) {
3550 kick_endtime = gameLocal.time + muzzle_kick_maxtime;
3553 if ( !gameLocal.isClient ) {
3554 ownerBounds = owner->GetPhysics()->GetAbsBounds();
3556 owner->AddProjectilesFired( num_projectiles );
3558 float spreadRadA = DEG2RAD( spreada );
3559 float spreadRadB = DEG2RAD( spreadb );
3561 for( i = 0; i < num_projectiles; i++ ) {
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 ) );
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 );
3575 proj = static_cast<idProjectile *>(ent);
3576 proj->Create( owner, muzzleOrigin, dir );
3578 projBounds = proj->GetPhysics()->GetBounds().Rotate( proj->GetPhysics()->GetAxis() );
3580 // make sure the projectile starts inside the bounding box of the owner
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];
3587 start = ownerBounds.GetCenter();
3589 gameLocal.clip.Translation( tr, start, muzzle_pos, proj->GetPhysics()->GetClipModel(), proj->GetPhysics()->GetClipModel()->GetAxis(), MASK_SHOT_RENDERMODEL, owner );
3590 muzzle_pos = tr.endpos;
3593 proj->Launch( muzzle_pos, dir, pushVelocity, fuseOffset, power );
3597 if( brassDelay >= 0 ) {
3598 PostEventMS( &EV_Weapon_EjectBrass, brassDelay );
3602 // add the light for the muzzleflash
3607 owner->WeaponFireFeedback( &weaponDef->dict );
3609 // reset muzzle smoke
3610 weaponSmokeStartTime = gameLocal.time;
3615 * Gives the player a powerup as if it were a weapon shot. It will use the ammo amount specified
3618 void idWeapon::Event_LaunchPowerup( const char* powerup, float duration, int useAmmo ) {
3624 // check if we're out of ammo
3626 int ammoAvail = owner->inventory.HasAmmo( ammoType, ammoRequired );
3630 owner->inventory.UseAmmo( ammoType, ammoRequired );
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 );
3638 if ( worldModel.GetEntity() ) {
3639 worldModel.GetEntity()->SetShaderParm( SHADERPARM_DIVERSITY, renderEntity.shaderParms[ SHADERPARM_DIVERSITY ] );
3640 worldModel.GetEntity()->SetShaderParm( SHADERPARM_TIMEOFFSET, renderEntity.shaderParms[ SHADERPARM_TIMEOFFSET ] );
3643 // add the light for the muzzleflash
3648 owner->Give(powerup, va("%f", duration));
3653 void idWeapon::Event_StartWeaponSmoke() {
3655 // reset muzzle smoke
3656 weaponSmokeStartTime = gameLocal.time;
3659 void idWeapon::Event_StopWeaponSmoke() {
3661 // reset muzzle smoke
3662 weaponSmokeStartTime = 0;
3665 void idWeapon::Event_StartWeaponParticle( const char* name) {
3666 WeaponParticle_t* part;
3667 weaponParticles.Get(name, &part);
3669 part->active = true;
3670 part->startTime = gameLocal.time;
3672 //Toggle the emitter
3674 part->emitter->Show();
3675 part->emitter->PostEventMS(&EV_Activate, 0, this);
3680 void idWeapon::Event_StopWeaponParticle( const char* name) {
3681 WeaponParticle_t* part;
3682 weaponParticles.Get(name, &part);
3684 part->active = false;
3685 part->startTime = 0;
3687 //Toggle the emitter
3689 part->emitter->Hide();
3690 part->emitter->PostEventMS(&EV_Activate, 0, this);
3695 void idWeapon::Event_StartWeaponLight( const char* name) {
3696 WeaponLight_t* light;
3697 weaponLights.Get(name, &light);
3699 light->active = true;
3700 light->startTime = gameLocal.time;
3704 void idWeapon::Event_StopWeaponLight( const char* name) {
3705 WeaponLight_t* light;
3706 weaponLights.Get(name, &light);
3708 light->active = false;
3709 light->startTime = 0;
3710 if(light->lightHandle != -1) {
3711 gameRenderWorld->FreeLightDef( light->lightHandle );
3712 light->lightHandle = -1;
3718 =====================
3719 idWeapon::Event_Melee
3720 =====================
3722 void idWeapon::Event_Melee( void ) {
3727 gameLocal.Error( "No meleeDef on '%s'", weaponDef->dict.GetString( "classname" ) );
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 );
3740 if ( g_debugWeapon.GetBool() ) {
3741 gameRenderWorld->DebugLine( colorYellow, start, end, 100 );
3743 gameRenderWorld->DebugBounds( colorRed, ent->GetPhysics()->GetBounds(), ent->GetPhysics()->GetOrigin(), 100 );
3748 const char *hitSound = meleeDef->dict.GetString( "snd_miss" );
3752 float push = meleeDef->dict.GetFloat( "push" );
3753 idVec3 impulse = -push * owner->PowerUpModifier( SPEED ) * tr.c.normal;
3755 if ( gameLocal.world->spawnArgs.GetBool( "no_Weapons" ) && ( ent->IsType( idActor::Type ) || ent->IsType( idAFAttachment::Type) ) ) {
3756 idThread::ReturnInt( 0 );
3760 ent->ApplyImpulse( this, tr.c.id, tr.c.point, impulse );
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 ) )
3770 #ifdef CTF /* Code is formed oddly for easy merge */
3772 if ( gameLocal.mpGame.IsGametypeFlagBased() )
3773 { /* Do nothing ... */ }
3776 owner->StealWeapon( static_cast< idPlayer * >( ent ) );
3779 if ( ent->fl.takedamage ) {
3780 idVec3 kickDir, globalKickDir;
3781 meleeDef->dict.GetVector( "kickDir", "0 0 0", kickDir );
3782 globalKickDir = muzzleAxis * kickDir;
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
3790 ent->Damage( owner, owner, globalKickDir, meleeDefName, mod, tr.c.id );
3792 ent->Damage( owner, owner, globalKickDir, meleeDefName, owner->PowerUpModifier( MELEE_DAMAGE ), tr.c.id );
3797 if ( weaponDef->dict.GetBool( "impact_damage_effect" ) ) {
3799 if ( ent->spawnArgs.GetBool( "bleed" ) ) {
3801 hitSound = meleeDef->dict.GetString( owner->PowerUpActive( BERSERK ) ? "snd_hit_berserk" : "snd_hit" );
3803 ent->AddDamageEffect( tr, impulse, meleeDef->dict.GetString( "classname" ) );
3807 int type = tr.c.material->GetSurfaceType();
3808 if ( type == SURFTYPE_NONE ) {
3809 type = GetDefaultSurfaceType();
3812 const char *materialType = gameLocal.sufaceTypeNames[ type ];
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" );
3820 if ( gameLocal.time > nextStrikeFx ) {
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 );
3827 nextStrikeFx = gameLocal.time + 200;
3832 strikeSmokeStartTime = gameLocal.time;
3833 strikePos = tr.c.point;
3834 strikeAxis = -tr.endAxis;
3839 if ( *hitSound != '\0' ) {
3840 const idSoundShader *snd = declManager->FindSound( hitSound );
3841 StartSoundShader( snd, SND_CHANNEL_BODY2, 0, true, NULL );
3844 idThread::ReturnInt( hit );
3845 owner->WeaponFireFeedback( &weaponDef->dict );
3849 idThread::ReturnInt( 0 );
3850 owner->WeaponFireFeedback( &weaponDef->dict );
3854 =====================
3855 idWeapon::Event_GetWorldModel
3856 =====================
3858 void idWeapon::Event_GetWorldModel( void ) {
3859 idThread::ReturnEntity( worldModel.GetEntity() );
3863 =====================
3864 idWeapon::Event_AllowDrop
3865 =====================
3867 void idWeapon::Event_AllowDrop( int allow ) {
3877 idWeapon::Event_EjectBrass
3879 Toss a shell model out from the breach if the bone is present
3882 void idWeapon::Event_EjectBrass( void ) {
3883 if ( !g_showBrass.GetBool() || !owner->CanShowWeaponViewmodel() ) {
3887 if ( ejectJointView == INVALID_JOINT || !brassDict.GetNumKeyVals() ) {
3891 if ( gameLocal.isClient ) {
3896 idVec3 origin, linear_velocity, angular_velocity;
3899 if ( !GetGlobalJointTransform( true, ejectJointView, origin, axis ) ) {
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" );
3907 idDebris *debris = static_cast<idDebris *>(ent);
3908 debris->Create( owner, origin, axis );
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() );
3914 debris->GetPhysics()->SetLinearVelocity( linear_velocity );
3915 debris->GetPhysics()->SetAngularVelocity( angular_velocity );
3920 idWeapon::Event_IsInvisible
3923 void idWeapon::Event_IsInvisible( void ) {
3925 idThread::ReturnFloat( 0 );
3928 idThread::ReturnFloat( owner->PowerUpActive( INVISIBILITY ) ? 1 : 0 );
3933 idWeapon::ClientPredictionThink
3936 void idWeapon::ClientPredictionThink( void ) {