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" );
74 CLASS_DECLARATION( idAnimatedEntity, idWeapon )
75 EVENT( EV_Weapon_Clear, idWeapon::Event_Clear )
76 EVENT( EV_Weapon_GetOwner, idWeapon::Event_GetOwner )
77 EVENT( EV_Weapon_State, idWeapon::Event_WeaponState )
78 EVENT( EV_Weapon_WeaponReady, idWeapon::Event_WeaponReady )
79 EVENT( EV_Weapon_WeaponOutOfAmmo, idWeapon::Event_WeaponOutOfAmmo )
80 EVENT( EV_Weapon_WeaponReloading, idWeapon::Event_WeaponReloading )
81 EVENT( EV_Weapon_WeaponHolstered, idWeapon::Event_WeaponHolstered )
82 EVENT( EV_Weapon_WeaponRising, idWeapon::Event_WeaponRising )
83 EVENT( EV_Weapon_WeaponLowering, idWeapon::Event_WeaponLowering )
84 EVENT( EV_Weapon_UseAmmo, idWeapon::Event_UseAmmo )
85 EVENT( EV_Weapon_AddToClip, idWeapon::Event_AddToClip )
86 EVENT( EV_Weapon_AmmoInClip, idWeapon::Event_AmmoInClip )
87 EVENT( EV_Weapon_AmmoAvailable, idWeapon::Event_AmmoAvailable )
88 EVENT( EV_Weapon_TotalAmmoCount, idWeapon::Event_TotalAmmoCount )
89 EVENT( EV_Weapon_ClipSize, idWeapon::Event_ClipSize )
90 EVENT( AI_PlayAnim, idWeapon::Event_PlayAnim )
91 EVENT( AI_PlayCycle, idWeapon::Event_PlayCycle )
92 EVENT( AI_SetBlendFrames, idWeapon::Event_SetBlendFrames )
93 EVENT( AI_GetBlendFrames, idWeapon::Event_GetBlendFrames )
94 EVENT( AI_AnimDone, idWeapon::Event_AnimDone )
95 EVENT( EV_Weapon_Next, idWeapon::Event_Next )
96 EVENT( EV_SetSkin, idWeapon::Event_SetSkin )
97 EVENT( EV_Weapon_Flashlight, idWeapon::Event_Flashlight )
98 EVENT( EV_Light_GetLightParm, idWeapon::Event_GetLightParm )
99 EVENT( EV_Light_SetLightParm, idWeapon::Event_SetLightParm )
100 EVENT( EV_Light_SetLightParms, idWeapon::Event_SetLightParms )
101 EVENT( EV_Weapon_LaunchProjectiles, idWeapon::Event_LaunchProjectiles )
102 EVENT( EV_Weapon_CreateProjectile, idWeapon::Event_CreateProjectile )
103 EVENT( EV_Weapon_EjectBrass, idWeapon::Event_EjectBrass )
104 EVENT( EV_Weapon_Melee, idWeapon::Event_Melee )
105 EVENT( EV_Weapon_GetWorldModel, idWeapon::Event_GetWorldModel )
106 EVENT( EV_Weapon_AllowDrop, idWeapon::Event_AllowDrop )
107 EVENT( EV_Weapon_AutoReload, idWeapon::Event_AutoReload )
108 EVENT( EV_Weapon_NetReload, idWeapon::Event_NetReload )
109 EVENT( EV_Weapon_IsInvisible, idWeapon::Event_IsInvisible )
110 EVENT( EV_Weapon_NetEndReload, idWeapon::Event_NetEndReload )
113 /***********************************************************************
117 ***********************************************************************/
124 idWeapon::idWeapon() {
130 memset( &guiLight, 0, sizeof( guiLight ) );
131 memset( &muzzleFlash, 0, sizeof( muzzleFlash ) );
132 memset( &worldMuzzleFlash, 0, sizeof( worldMuzzleFlash ) );
133 memset( &nozzleGlow, 0, sizeof( nozzleGlow ) );
136 flashColor = vec3_origin;
137 muzzleFlashHandle = -1;
138 worldMuzzleFlashHandle = -1;
140 nozzleGlowHandle = -1;
150 fl.networkSync = true;
155 idWeapon::~idWeapon()
158 idWeapon::~idWeapon() {
160 delete worldModel.GetEntity();
169 void idWeapon::Spawn( void ) {
170 if ( !gameLocal.isClient ) {
171 // setup the world model
172 worldModel = static_cast< idAnimatedEntity * >( gameLocal.SpawnEntityType( idAnimatedEntity::Type, NULL ) );
173 worldModel.GetEntity()->fl.networkSync = true;
176 thread = new idThread();
177 thread->ManualDelete();
178 thread->ManualControl();
185 Only called at player spawn time, not each weapon switch
188 void idWeapon::SetOwner( idPlayer *_owner ) {
191 SetName( va( "%s_weapon", owner->name.c_str() ) );
193 if ( worldModel.GetEntity() ) {
194 worldModel.GetEntity()->SetName( va( "%s_weapon_worldmodel", owner->name.c_str() ) );
200 idWeapon::ShouldConstructScriptObjectAtSpawn
202 Called during idEntity::Spawn to see if it should construct the script object or not.
203 Overridden by subclasses that need to spawn the script object themselves.
206 bool idWeapon::ShouldConstructScriptObjectAtSpawn( void ) const {
212 idWeapon::CacheWeapon
215 void idWeapon::CacheWeapon( const char *weaponName ) {
216 const idDeclEntityDef *weaponDef;
217 const char *brassDefName;
218 const char *clipModelName;
222 weaponDef = gameLocal.FindEntityDef( weaponName, false );
227 // precache the brass collision model
228 brassDefName = weaponDef->dict.GetString( "def_ejectBrass" );
229 if ( brassDefName[0] ) {
230 const idDeclEntityDef *brassDef = gameLocal.FindEntityDef( brassDefName, false );
232 brassDef->dict.GetString( "clipmodel", "", &clipModelName );
233 if ( !clipModelName[0] ) {
234 clipModelName = brassDef->dict.GetString( "model" ); // use the visual model
236 // load the trace model
237 collisionModelManager->TrmFromModel( clipModelName, trm );
241 guiName = weaponDef->dict.GetString( "gui" );
243 uiManager->FindGui( guiName, true, false, true );
252 void idWeapon::Save( idSaveGame *savefile ) const {
254 savefile->WriteInt( status );
255 savefile->WriteObject( thread );
256 savefile->WriteString( state );
257 savefile->WriteString( idealState );
258 savefile->WriteInt( animBlendFrames );
259 savefile->WriteInt( animDoneTime );
260 savefile->WriteBool( isLinked );
262 savefile->WriteObject( owner );
263 worldModel.Save( savefile );
265 savefile->WriteInt( hideTime );
266 savefile->WriteFloat( hideDistance );
267 savefile->WriteInt( hideStartTime );
268 savefile->WriteFloat( hideStart );
269 savefile->WriteFloat( hideEnd );
270 savefile->WriteFloat( hideOffset );
271 savefile->WriteBool( hide );
272 savefile->WriteBool( disabled );
274 savefile->WriteInt( berserk );
276 savefile->WriteVec3( playerViewOrigin );
277 savefile->WriteMat3( playerViewAxis );
279 savefile->WriteVec3( viewWeaponOrigin );
280 savefile->WriteMat3( viewWeaponAxis );
282 savefile->WriteVec3( muzzleOrigin );
283 savefile->WriteMat3( muzzleAxis );
285 savefile->WriteVec3( pushVelocity );
287 savefile->WriteString( weaponDef->GetName() );
288 savefile->WriteFloat( meleeDistance );
289 savefile->WriteString( meleeDefName );
290 savefile->WriteInt( brassDelay );
291 savefile->WriteString( icon );
293 savefile->WriteInt( guiLightHandle );
294 savefile->WriteRenderLight( guiLight );
296 savefile->WriteInt( muzzleFlashHandle );
297 savefile->WriteRenderLight( muzzleFlash );
299 savefile->WriteInt( worldMuzzleFlashHandle );
300 savefile->WriteRenderLight( worldMuzzleFlash );
302 savefile->WriteVec3( flashColor );
303 savefile->WriteInt( muzzleFlashEnd );
304 savefile->WriteInt( flashTime );
306 savefile->WriteBool( lightOn );
307 savefile->WriteBool( silent_fire );
309 savefile->WriteInt( kick_endtime );
310 savefile->WriteInt( muzzle_kick_time );
311 savefile->WriteInt( muzzle_kick_maxtime );
312 savefile->WriteAngles( muzzle_kick_angles );
313 savefile->WriteVec3( muzzle_kick_offset );
315 savefile->WriteInt( ammoType );
316 savefile->WriteInt( ammoRequired );
317 savefile->WriteInt( clipSize );
318 savefile->WriteInt( ammoClip );
319 savefile->WriteInt( lowAmmo );
320 savefile->WriteBool( powerAmmo );
323 savefile->WriteInt( 0 );
325 savefile->WriteInt( zoomFov );
327 savefile->WriteJoint( barrelJointView );
328 savefile->WriteJoint( flashJointView );
329 savefile->WriteJoint( ejectJointView );
330 savefile->WriteJoint( guiLightJointView );
331 savefile->WriteJoint( ventLightJointView );
333 savefile->WriteJoint( flashJointWorld );
334 savefile->WriteJoint( barrelJointWorld );
335 savefile->WriteJoint( ejectJointWorld );
337 savefile->WriteBool( hasBloodSplat );
339 savefile->WriteSoundShader( sndHum );
341 savefile->WriteParticle( weaponSmoke );
342 savefile->WriteInt( weaponSmokeStartTime );
343 savefile->WriteBool( continuousSmoke );
344 savefile->WriteParticle( strikeSmoke );
345 savefile->WriteInt( strikeSmokeStartTime );
346 savefile->WriteVec3( strikePos );
347 savefile->WriteMat3( strikeAxis );
348 savefile->WriteInt( nextStrikeFx );
350 savefile->WriteBool( nozzleFx );
351 savefile->WriteInt( nozzleFxFade );
353 savefile->WriteInt( lastAttack );
355 savefile->WriteInt( nozzleGlowHandle );
356 savefile->WriteRenderLight( nozzleGlow );
358 savefile->WriteVec3( nozzleGlowColor );
359 savefile->WriteMaterial( nozzleGlowShader );
360 savefile->WriteFloat( nozzleGlowRadius );
362 savefile->WriteInt( weaponAngleOffsetAverages );
363 savefile->WriteFloat( weaponAngleOffsetScale );
364 savefile->WriteFloat( weaponAngleOffsetMax );
365 savefile->WriteFloat( weaponOffsetTime );
366 savefile->WriteFloat( weaponOffsetScale );
368 savefile->WriteBool( allowDrop );
369 savefile->WriteObject( projectileEnt );
378 void idWeapon::Restore( idRestoreGame *savefile ) {
380 savefile->ReadInt( (int &)status );
381 savefile->ReadObject( reinterpret_cast<idClass *&>( thread ) );
382 savefile->ReadString( state );
383 savefile->ReadString( idealState );
384 savefile->ReadInt( animBlendFrames );
385 savefile->ReadInt( animDoneTime );
386 savefile->ReadBool( isLinked );
388 // Re-link script fields
389 WEAPON_ATTACK.LinkTo( scriptObject, "WEAPON_ATTACK" );
390 WEAPON_RELOAD.LinkTo( scriptObject, "WEAPON_RELOAD" );
391 WEAPON_NETRELOAD.LinkTo( scriptObject, "WEAPON_NETRELOAD" );
392 WEAPON_NETENDRELOAD.LinkTo( scriptObject, "WEAPON_NETENDRELOAD" );
393 WEAPON_NETFIRING.LinkTo( scriptObject, "WEAPON_NETFIRING" );
394 WEAPON_RAISEWEAPON.LinkTo( scriptObject, "WEAPON_RAISEWEAPON" );
395 WEAPON_LOWERWEAPON.LinkTo( scriptObject, "WEAPON_LOWERWEAPON" );
397 savefile->ReadObject( reinterpret_cast<idClass *&>( owner ) );
398 worldModel.Restore( savefile );
400 savefile->ReadInt( hideTime );
401 savefile->ReadFloat( hideDistance );
402 savefile->ReadInt( hideStartTime );
403 savefile->ReadFloat( hideStart );
404 savefile->ReadFloat( hideEnd );
405 savefile->ReadFloat( hideOffset );
406 savefile->ReadBool( hide );
407 savefile->ReadBool( disabled );
409 savefile->ReadInt( berserk );
411 savefile->ReadVec3( playerViewOrigin );
412 savefile->ReadMat3( playerViewAxis );
414 savefile->ReadVec3( viewWeaponOrigin );
415 savefile->ReadMat3( viewWeaponAxis );
417 savefile->ReadVec3( muzzleOrigin );
418 savefile->ReadMat3( muzzleAxis );
420 savefile->ReadVec3( pushVelocity );
423 savefile->ReadString( objectname );
424 weaponDef = gameLocal.FindEntityDef( objectname );
425 meleeDef = gameLocal.FindEntityDef( weaponDef->dict.GetString( "def_melee" ), false );
427 const idDeclEntityDef *projectileDef = gameLocal.FindEntityDef( weaponDef->dict.GetString( "def_projectile" ), false );
428 if ( projectileDef ) {
429 projectileDict = projectileDef->dict;
431 projectileDict.Clear();
434 const idDeclEntityDef *brassDef = gameLocal.FindEntityDef( weaponDef->dict.GetString( "def_ejectBrass" ), false );
436 brassDict = brassDef->dict;
441 savefile->ReadFloat( meleeDistance );
442 savefile->ReadString( meleeDefName );
443 savefile->ReadInt( brassDelay );
444 savefile->ReadString( icon );
446 savefile->ReadInt( guiLightHandle );
447 savefile->ReadRenderLight( guiLight );
449 savefile->ReadInt( muzzleFlashHandle );
450 savefile->ReadRenderLight( muzzleFlash );
452 savefile->ReadInt( worldMuzzleFlashHandle );
453 savefile->ReadRenderLight( worldMuzzleFlash );
455 savefile->ReadVec3( flashColor );
456 savefile->ReadInt( muzzleFlashEnd );
457 savefile->ReadInt( flashTime );
459 savefile->ReadBool( lightOn );
460 savefile->ReadBool( silent_fire );
462 savefile->ReadInt( kick_endtime );
463 savefile->ReadInt( muzzle_kick_time );
464 savefile->ReadInt( muzzle_kick_maxtime );
465 savefile->ReadAngles( muzzle_kick_angles );
466 savefile->ReadVec3( muzzle_kick_offset );
468 savefile->ReadInt( (int &)ammoType );
469 savefile->ReadInt( ammoRequired );
470 savefile->ReadInt( clipSize );
471 savefile->ReadInt( ammoClip );
472 savefile->ReadInt( lowAmmo );
473 savefile->ReadBool( powerAmmo );
475 // savegame versions <= 17
477 savefile->ReadInt( foo );
479 savefile->ReadInt( zoomFov );
481 savefile->ReadJoint( barrelJointView );
482 savefile->ReadJoint( flashJointView );
483 savefile->ReadJoint( ejectJointView );
484 savefile->ReadJoint( guiLightJointView );
485 savefile->ReadJoint( ventLightJointView );
487 savefile->ReadJoint( flashJointWorld );
488 savefile->ReadJoint( barrelJointWorld );
489 savefile->ReadJoint( ejectJointWorld );
491 savefile->ReadBool( hasBloodSplat );
493 savefile->ReadSoundShader( sndHum );
495 savefile->ReadParticle( weaponSmoke );
496 savefile->ReadInt( weaponSmokeStartTime );
497 savefile->ReadBool( continuousSmoke );
498 savefile->ReadParticle( strikeSmoke );
499 savefile->ReadInt( strikeSmokeStartTime );
500 savefile->ReadVec3( strikePos );
501 savefile->ReadMat3( strikeAxis );
502 savefile->ReadInt( nextStrikeFx );
504 savefile->ReadBool( nozzleFx );
505 savefile->ReadInt( nozzleFxFade );
507 savefile->ReadInt( lastAttack );
509 savefile->ReadInt( nozzleGlowHandle );
510 savefile->ReadRenderLight( nozzleGlow );
512 savefile->ReadVec3( nozzleGlowColor );
513 savefile->ReadMaterial( nozzleGlowShader );
514 savefile->ReadFloat( nozzleGlowRadius );
516 savefile->ReadInt( weaponAngleOffsetAverages );
517 savefile->ReadFloat( weaponAngleOffsetScale );
518 savefile->ReadFloat( weaponAngleOffsetMax );
519 savefile->ReadFloat( weaponOffsetTime );
520 savefile->ReadFloat( weaponOffsetScale );
522 savefile->ReadBool( allowDrop );
523 savefile->ReadObject( reinterpret_cast<idClass *&>( projectileEnt ) );
526 /***********************************************************************
528 Weapon definition management
530 ***********************************************************************/
537 void idWeapon::Clear( void ) {
538 CancelEvents( &EV_Weapon_Clear );
540 DeconstructScriptObject();
543 WEAPON_ATTACK.Unlink();
544 WEAPON_RELOAD.Unlink();
545 WEAPON_NETRELOAD.Unlink();
546 WEAPON_NETENDRELOAD.Unlink();
547 WEAPON_NETFIRING.Unlink();
548 WEAPON_RAISEWEAPON.Unlink();
549 WEAPON_LOWERWEAPON.Unlink();
551 if ( muzzleFlashHandle != -1 ) {
552 gameRenderWorld->FreeLightDef( muzzleFlashHandle );
553 muzzleFlashHandle = -1;
555 if ( muzzleFlashHandle != -1 ) {
556 gameRenderWorld->FreeLightDef( muzzleFlashHandle );
557 muzzleFlashHandle = -1;
559 if ( worldMuzzleFlashHandle != -1 ) {
560 gameRenderWorld->FreeLightDef( worldMuzzleFlashHandle );
561 worldMuzzleFlashHandle = -1;
563 if ( guiLightHandle != -1 ) {
564 gameRenderWorld->FreeLightDef( guiLightHandle );
567 if ( nozzleGlowHandle != -1 ) {
568 gameRenderWorld->FreeLightDef( nozzleGlowHandle );
569 nozzleGlowHandle = -1;
572 memset( &renderEntity, 0, sizeof( renderEntity ) );
573 renderEntity.entityNum = entityNumber;
575 renderEntity.noShadow = true;
576 renderEntity.noSelfShadow = true;
577 renderEntity.customSkin = NULL;
579 // set default shader parms
580 renderEntity.shaderParms[ SHADERPARM_RED ] = 1.0f;
581 renderEntity.shaderParms[ SHADERPARM_GREEN ]= 1.0f;
582 renderEntity.shaderParms[ SHADERPARM_BLUE ] = 1.0f;
583 renderEntity.shaderParms[3] = 1.0f;
584 renderEntity.shaderParms[ SHADERPARM_TIMEOFFSET ] = 0.0f;
585 renderEntity.shaderParms[5] = 0.0f;
586 renderEntity.shaderParms[6] = 0.0f;
587 renderEntity.shaderParms[7] = 0.0f;
589 if ( refSound.referenceSound ) {
590 refSound.referenceSound->Free( true );
592 memset( &refSound, 0, sizeof( refSound_t ) );
594 // setting diversity to 0 results in no random sound. -1 indicates random.
595 refSound.diversity = -1.0f;
598 // don't spatialize the weapon sounds
599 refSound.listenerId = owner->GetListenerId();
602 // clear out the sounds from our spawnargs since we'll copy them from the weapon def
603 const idKeyValue *kv = spawnArgs.MatchPrefix( "snd_" );
605 spawnArgs.Delete( kv->GetKey() );
606 kv = spawnArgs.MatchPrefix( "snd_" );
610 hideDistance = -15.0f;
611 hideStartTime = gameLocal.time - hideTime;
619 weaponSmokeStartTime = 0;
620 continuousSmoke = false;
622 strikeSmokeStartTime = 0;
624 strikeAxis = mat3_identity;
629 playerViewAxis.Identity();
630 playerViewOrigin.Zero();
631 viewWeaponAxis.Identity();
632 viewWeaponOrigin.Zero();
633 muzzleAxis.Identity();
637 status = WP_HOLSTERED;
643 projectileDict.Clear();
646 meleeDistance = 0.0f;
661 muzzle_kick_time = 0;
662 muzzle_kick_maxtime = 0;
663 muzzle_kick_angles.Zero();
664 muzzle_kick_offset.Zero();
668 barrelJointView = INVALID_JOINT;
669 flashJointView = INVALID_JOINT;
670 ejectJointView = INVALID_JOINT;
671 guiLightJointView = INVALID_JOINT;
672 ventLightJointView = INVALID_JOINT;
674 barrelJointWorld = INVALID_JOINT;
675 flashJointWorld = INVALID_JOINT;
676 ejectJointWorld = INVALID_JOINT;
678 hasBloodSplat = false;
682 nozzleGlowHandle = -1;
683 nozzleGlowShader = NULL;
684 nozzleGlowRadius = 10;
685 nozzleGlowColor.Zero();
687 weaponAngleOffsetAverages = 0;
688 weaponAngleOffsetScale = 0.0f;
689 weaponAngleOffsetMax = 0.0f;
690 weaponOffsetTime = 0.0f;
691 weaponOffsetScale = 0.0f;
695 animator.ClearAllAnims( gameLocal.time, 0 );
701 projectileEnt = NULL;
708 idWeapon::InitWorldModel
711 void idWeapon::InitWorldModel( const idDeclEntityDef *def ) {
714 ent = worldModel.GetEntity();
719 const char *model = def->dict.GetString( "model_world" );
720 const char *attach = def->dict.GetString( "joint_attach" );
722 ent->SetSkin( NULL );
723 if ( model[0] && attach[0] ) {
725 ent->SetModel( model );
726 if ( ent->GetAnimator()->ModelDef() ) {
727 ent->SetSkin( ent->GetAnimator()->ModelDef()->GetDefaultSkin() );
729 ent->GetPhysics()->SetContents( 0 );
730 ent->GetPhysics()->SetClipModel( NULL, 1.0f );
731 ent->BindToJoint( owner, attach, true );
732 ent->GetPhysics()->SetOrigin( vec3_origin );
733 ent->GetPhysics()->SetAxis( mat3_identity );
735 // supress model in player views, but allow it in mirrors and remote views
736 renderEntity_t *worldModelRenderEntity = ent->GetRenderEntity();
737 if ( worldModelRenderEntity ) {
738 worldModelRenderEntity->suppressSurfaceInViewID = owner->entityNumber+1;
739 worldModelRenderEntity->suppressShadowInViewID = owner->entityNumber+1;
740 worldModelRenderEntity->suppressShadowInLightID = LIGHTID_VIEW_MUZZLE_FLASH + owner->entityNumber;
747 flashJointWorld = ent->GetAnimator()->GetJointHandle( "flash" );
748 barrelJointWorld = ent->GetAnimator()->GetJointHandle( "muzzle" );
749 ejectJointWorld = ent->GetAnimator()->GetJointHandle( "eject" );
754 idWeapon::GetWeaponDef
757 void idWeapon::GetWeaponDef( const char *objectname, int ammoinclip ) {
759 const char *objectType;
762 const char *projectileName;
763 const char *brassDefName;
764 const char *smokeName;
769 if ( !objectname || !objectname[ 0 ] ) {
775 weaponDef = gameLocal.FindEntityDef( objectname );
777 ammoType = GetAmmoNumForName( weaponDef->dict.GetString( "ammoType" ) );
778 ammoRequired = weaponDef->dict.GetInt( "ammoRequired" );
779 clipSize = weaponDef->dict.GetInt( "clipSize" );
780 lowAmmo = weaponDef->dict.GetInt( "lowAmmo" );
782 icon = weaponDef->dict.GetString( "icon" );
783 silent_fire = weaponDef->dict.GetBool( "silent_fire" );
784 powerAmmo = weaponDef->dict.GetBool( "powerAmmo" );
786 muzzle_kick_time = SEC2MS( weaponDef->dict.GetFloat( "muzzle_kick_time" ) );
787 muzzle_kick_maxtime = SEC2MS( weaponDef->dict.GetFloat( "muzzle_kick_maxtime" ) );
788 muzzle_kick_angles = weaponDef->dict.GetAngles( "muzzle_kick_angles" );
789 muzzle_kick_offset = weaponDef->dict.GetVector( "muzzle_kick_offset" );
791 hideTime = SEC2MS( weaponDef->dict.GetFloat( "hide_time", "0.3" ) );
792 hideDistance = weaponDef->dict.GetFloat( "hide_distance", "-15" );
795 smokeName = weaponDef->dict.GetString( "smoke_muzzle" );
796 if ( *smokeName != '\0' ) {
797 weaponSmoke = static_cast<const idDeclParticle *>( declManager->FindType( DECL_PARTICLE, smokeName ) );
801 continuousSmoke = weaponDef->dict.GetBool( "continuousSmoke" );
802 weaponSmokeStartTime = ( continuousSmoke ) ? gameLocal.time : 0;
804 smokeName = weaponDef->dict.GetString( "smoke_strike" );
805 if ( *smokeName != '\0' ) {
806 strikeSmoke = static_cast<const idDeclParticle *>( declManager->FindType( DECL_PARTICLE, smokeName ) );
810 strikeSmokeStartTime = 0;
812 strikeAxis = mat3_identity;
816 memset( &guiLight, 0, sizeof( guiLight ) );
817 const char *guiLightShader = weaponDef->dict.GetString( "mtr_guiLightShader" );
818 if ( *guiLightShader != '\0' ) {
819 guiLight.shader = declManager->FindMaterial( guiLightShader, false );
820 guiLight.lightRadius[0] = guiLight.lightRadius[1] = guiLight.lightRadius[2] = 3;
821 guiLight.pointLight = true;
824 // setup the view model
825 vmodel = weaponDef->dict.GetString( "model_view" );
828 // setup the world model
829 InitWorldModel( weaponDef );
831 // copy the sounds from the weapon view model def into out spawnargs
832 const idKeyValue *kv = weaponDef->dict.MatchPrefix( "snd_" );
834 spawnArgs.Set( kv->GetKey(), kv->GetValue() );
835 kv = weaponDef->dict.MatchPrefix( "snd_", kv );
838 // find some joints in the model for locating effects
839 barrelJointView = animator.GetJointHandle( "barrel" );
840 flashJointView = animator.GetJointHandle( "flash" );
841 ejectJointView = animator.GetJointHandle( "eject" );
842 guiLightJointView = animator.GetJointHandle( "guiLight" );
843 ventLightJointView = animator.GetJointHandle( "ventLight" );
845 // get the projectile
846 projectileDict.Clear();
848 projectileName = weaponDef->dict.GetString( "def_projectile" );
849 if ( projectileName[0] != '\0' ) {
850 const idDeclEntityDef *projectileDef = gameLocal.FindEntityDef( projectileName, false );
851 if ( !projectileDef ) {
852 gameLocal.Warning( "Unknown projectile '%s' in weapon '%s'", projectileName, objectname );
854 const char *spawnclass = projectileDef->dict.GetString( "spawnclass" );
855 idTypeInfo *cls = idClass::GetClass( spawnclass );
856 if ( !cls || !cls->IsType( idProjectile::Type ) ) {
857 gameLocal.Warning( "Invalid spawnclass '%s' on projectile '%s' (used by weapon '%s')", spawnclass, projectileName, objectname );
859 projectileDict = projectileDef->dict;
864 // set up muzzleflash render light
865 const idMaterial*flashShader;
870 bool flashPointLight;
872 weaponDef->dict.GetString( "mtr_flashShader", "", &shader );
873 flashShader = declManager->FindMaterial( shader, false );
874 flashPointLight = weaponDef->dict.GetBool( "flashPointLight", "1" );
875 weaponDef->dict.GetVector( "flashColor", "0 0 0", flashColor );
876 flashRadius = (float)weaponDef->dict.GetInt( "flashRadius" ); // if 0, no light will spawn
877 flashTime = SEC2MS( weaponDef->dict.GetFloat( "flashTime", "0.25" ) );
878 flashTarget = weaponDef->dict.GetVector( "flashTarget" );
879 flashUp = weaponDef->dict.GetVector( "flashUp" );
880 flashRight = weaponDef->dict.GetVector( "flashRight" );
882 memset( &muzzleFlash, 0, sizeof( muzzleFlash ) );
883 muzzleFlash.lightId = LIGHTID_VIEW_MUZZLE_FLASH + owner->entityNumber;
884 muzzleFlash.allowLightInViewID = owner->entityNumber+1;
886 // the weapon lights will only be in first person
887 guiLight.allowLightInViewID = owner->entityNumber+1;
888 nozzleGlow.allowLightInViewID = owner->entityNumber+1;
890 muzzleFlash.pointLight = flashPointLight;
891 muzzleFlash.shader = flashShader;
892 muzzleFlash.shaderParms[ SHADERPARM_RED ] = flashColor[0];
893 muzzleFlash.shaderParms[ SHADERPARM_GREEN ] = flashColor[1];
894 muzzleFlash.shaderParms[ SHADERPARM_BLUE ] = flashColor[2];
895 muzzleFlash.shaderParms[ SHADERPARM_TIMESCALE ] = 1.0f;
897 muzzleFlash.lightRadius[0] = flashRadius;
898 muzzleFlash.lightRadius[1] = flashRadius;
899 muzzleFlash.lightRadius[2] = flashRadius;
901 if ( !flashPointLight ) {
902 muzzleFlash.target = flashTarget;
903 muzzleFlash.up = flashUp;
904 muzzleFlash.right = flashRight;
905 muzzleFlash.end = flashTarget;
908 // the world muzzle flash is the same, just positioned differently
909 worldMuzzleFlash = muzzleFlash;
910 worldMuzzleFlash.suppressLightInViewID = owner->entityNumber+1;
911 worldMuzzleFlash.allowLightInViewID = 0;
912 worldMuzzleFlash.lightId = LIGHTID_WORLD_MUZZLE_FLASH + owner->entityNumber;
914 //-----------------------------------
916 nozzleFx = weaponDef->dict.GetBool("nozzleFx");
917 nozzleFxFade = weaponDef->dict.GetInt("nozzleFxFade", "1500");
918 nozzleGlowColor = weaponDef->dict.GetVector("nozzleGlowColor", "1 1 1");
919 nozzleGlowRadius = weaponDef->dict.GetFloat("nozzleGlowRadius", "10");
920 weaponDef->dict.GetString( "mtr_nozzleGlowShader", "", &shader );
921 nozzleGlowShader = declManager->FindMaterial( shader, false );
923 // get the melee damage def
924 meleeDistance = weaponDef->dict.GetFloat( "melee_distance" );
925 meleeDefName = weaponDef->dict.GetString( "def_melee" );
926 if ( meleeDefName.Length() ) {
927 meleeDef = gameLocal.FindEntityDef( meleeDefName, false );
929 gameLocal.Error( "Unknown melee '%s'", meleeDefName.c_str() );
935 brassDelay = weaponDef->dict.GetInt( "ejectBrassDelay", "0" );
936 brassDefName = weaponDef->dict.GetString( "def_ejectBrass" );
938 if ( brassDefName[0] ) {
939 const idDeclEntityDef *brassDef = gameLocal.FindEntityDef( brassDefName, false );
941 gameLocal.Warning( "Unknown brass '%s'", brassDefName );
943 brassDict = brassDef->dict;
947 if ( ( ammoType < 0 ) || ( ammoType >= AMMO_NUMTYPES ) ) {
948 gameLocal.Warning( "Unknown ammotype in object '%s'", objectname );
951 ammoClip = ammoinclip;
952 if ( ( ammoClip < 0 ) || ( ammoClip > clipSize ) ) {
953 // first time using this weapon so have it fully loaded to start
955 ammoAvail = owner->inventory.HasAmmo( ammoType, ammoRequired );
956 if ( ammoClip > ammoAvail ) {
957 ammoClip = ammoAvail;
961 renderEntity.gui[ 0 ] = NULL;
962 guiName = weaponDef->dict.GetString( "gui" );
964 renderEntity.gui[ 0 ] = uiManager->FindGui( guiName, true, false, true );
967 zoomFov = weaponDef->dict.GetInt( "zoomFov", "70" );
968 berserk = weaponDef->dict.GetInt( "berserk", "2" );
970 weaponAngleOffsetAverages = weaponDef->dict.GetInt( "weaponAngleOffsetAverages", "10" );
971 weaponAngleOffsetScale = weaponDef->dict.GetFloat( "weaponAngleOffsetScale", "0.25" );
972 weaponAngleOffsetMax = weaponDef->dict.GetFloat( "weaponAngleOffsetMax", "10" );
974 weaponOffsetTime = weaponDef->dict.GetFloat( "weaponOffsetTime", "400" );
975 weaponOffsetScale = weaponDef->dict.GetFloat( "weaponOffsetScale", "0.005" );
977 if ( !weaponDef->dict.GetString( "weapon_scriptobject", NULL, &objectType ) ) {
978 gameLocal.Error( "No 'weapon_scriptobject' set on '%s'.", objectname );
981 // setup script object
982 if ( !scriptObject.SetType( objectType ) ) {
983 gameLocal.Error( "Script object '%s' not found on weapon '%s'.", objectType, objectname );
986 WEAPON_ATTACK.LinkTo( scriptObject, "WEAPON_ATTACK" );
987 WEAPON_RELOAD.LinkTo( scriptObject, "WEAPON_RELOAD" );
988 WEAPON_NETRELOAD.LinkTo( scriptObject, "WEAPON_NETRELOAD" );
989 WEAPON_NETENDRELOAD.LinkTo( scriptObject, "WEAPON_NETENDRELOAD" );
990 WEAPON_NETFIRING.LinkTo( scriptObject, "WEAPON_NETFIRING" );
991 WEAPON_RAISEWEAPON.LinkTo( scriptObject, "WEAPON_RAISEWEAPON" );
992 WEAPON_LOWERWEAPON.LinkTo( scriptObject, "WEAPON_LOWERWEAPON" );
994 spawnArgs = weaponDef->dict;
996 shader = spawnArgs.GetString( "snd_hum" );
997 if ( shader && *shader ) {
998 sndHum = declManager->FindSound( shader );
999 StartSoundShader( sndHum, SND_CHANNEL_BODY, 0, false, NULL );
1004 // call script object's constructor
1005 ConstructScriptObject();
1007 // make sure we have the correct skin
1011 /***********************************************************************
1015 ***********************************************************************/
1022 const char *idWeapon::Icon( void ) const {
1031 void idWeapon::UpdateGUI( void ) {
1032 if ( !renderEntity.gui[ 0 ] ) {
1036 if ( status == WP_HOLSTERED ) {
1040 if ( owner->weaponGone ) {
1041 // dropping weapons was implemented wierd, so we have to not update the gui when it happens or we'll get a negative ammo count
1045 if ( gameLocal.localClientNum != owner->entityNumber ) {
1046 // if updating the hud for a followed client
1047 if ( gameLocal.localClientNum >= 0 && gameLocal.entities[ gameLocal.localClientNum ] && gameLocal.entities[ gameLocal.localClientNum ]->IsType( idPlayer::Type ) ) {
1048 idPlayer *p = static_cast< idPlayer * >( gameLocal.entities[ gameLocal.localClientNum ] );
1049 if ( !p->spectating || p->spectator != owner->entityNumber ) {
1057 int inclip = AmmoInClip();
1058 int ammoamount = AmmoAvailable();
1060 if ( ammoamount < 0 ) {
1061 // show infinite ammo
1062 renderEntity.gui[ 0 ]->SetStateString( "player_ammo", "" );
1064 // show remaining ammo
1065 renderEntity.gui[ 0 ]->SetStateString( "player_totalammo", va( "%i", ammoamount - inclip) );
1066 renderEntity.gui[ 0 ]->SetStateString( "player_ammo", ClipSize() ? va( "%i", inclip ) : "--" );
1067 renderEntity.gui[ 0 ]->SetStateString( "player_clips", ClipSize() ? va("%i", ammoamount / ClipSize()) : "--" );
1068 renderEntity.gui[ 0 ]->SetStateString( "player_allammo", va( "%i/%i", inclip, ammoamount - inclip ) );
1070 renderEntity.gui[ 0 ]->SetStateBool( "player_ammo_empty", ( ammoamount == 0 ) );
1071 renderEntity.gui[ 0 ]->SetStateBool( "player_clip_empty", ( inclip == 0 ) );
1072 renderEntity.gui[ 0 ]->SetStateBool( "player_clip_low", ( inclip <= lowAmmo ) );
1075 /***********************************************************************
1077 Model and muzzleflash
1079 ***********************************************************************/
1083 idWeapon::UpdateFlashPosition
1086 void idWeapon::UpdateFlashPosition( void ) {
1087 // the flash has an explicit joint for locating it
1088 GetGlobalJointTransform( true, flashJointView, muzzleFlash.origin, muzzleFlash.axis );
1090 // if the desired point is inside or very close to a wall, back it up until it is clear
1091 idVec3 start = muzzleFlash.origin - playerViewAxis[0] * 16;
1092 idVec3 end = muzzleFlash.origin + playerViewAxis[0] * 8;
1094 gameLocal.clip.TracePoint( tr, start, end, MASK_SHOT_RENDERMODEL, owner );
1095 // be at least 8 units away from a solid
1096 muzzleFlash.origin = tr.endpos - playerViewAxis[0] * 8;
1098 // put the world muzzle flash on the end of the joint, no matter what
1099 GetGlobalJointTransform( false, flashJointWorld, worldMuzzleFlash.origin, worldMuzzleFlash.axis );
1104 idWeapon::MuzzleFlashLight
1107 void idWeapon::MuzzleFlashLight( void ) {
1109 if ( !lightOn && ( !g_muzzleFlash.GetBool() || !muzzleFlash.lightRadius[0] ) ) {
1113 if ( flashJointView == INVALID_JOINT ) {
1117 UpdateFlashPosition();
1119 // these will be different each fire
1120 muzzleFlash.shaderParms[ SHADERPARM_TIMEOFFSET ] = -MS2SEC( gameLocal.time );
1121 muzzleFlash.shaderParms[ SHADERPARM_DIVERSITY ] = renderEntity.shaderParms[ SHADERPARM_DIVERSITY ];
1123 worldMuzzleFlash.shaderParms[ SHADERPARM_TIMEOFFSET ] = -MS2SEC( gameLocal.time );
1124 worldMuzzleFlash.shaderParms[ SHADERPARM_DIVERSITY ] = renderEntity.shaderParms[ SHADERPARM_DIVERSITY ];
1126 // the light will be removed at this time
1127 muzzleFlashEnd = gameLocal.time + flashTime;
1129 if ( muzzleFlashHandle != -1 ) {
1130 gameRenderWorld->UpdateLightDef( muzzleFlashHandle, &muzzleFlash );
1131 gameRenderWorld->UpdateLightDef( worldMuzzleFlashHandle, &worldMuzzleFlash );
1133 muzzleFlashHandle = gameRenderWorld->AddLightDef( &muzzleFlash );
1134 worldMuzzleFlashHandle = gameRenderWorld->AddLightDef( &worldMuzzleFlash );
1140 idWeapon::UpdateSkin
1143 bool idWeapon::UpdateSkin( void ) {
1144 const function_t *func;
1150 func = scriptObject.GetFunction( "UpdateSkin" );
1152 common->Warning( "Can't find function 'UpdateSkin' in object '%s'", scriptObject.GetTypeName() );
1156 // use the frameCommandThread since it's safe to use outside of framecommands
1157 gameLocal.frameCommandThread->CallFunction( this, func, true );
1158 gameLocal.frameCommandThread->Execute();
1168 void idWeapon::SetModel( const char *modelname ) {
1169 assert( modelname );
1171 if ( modelDefHandle >= 0 ) {
1172 gameRenderWorld->RemoveDecals( modelDefHandle );
1175 renderEntity.hModel = animator.SetModel( modelname );
1176 if ( renderEntity.hModel ) {
1177 renderEntity.customSkin = animator.ModelDef()->GetDefaultSkin();
1178 animator.GetJoints( &renderEntity.numJoints, &renderEntity.joints );
1180 renderEntity.customSkin = NULL;
1181 renderEntity.callback = NULL;
1182 renderEntity.numJoints = 0;
1183 renderEntity.joints = NULL;
1186 // hide the model until an animation is played
1192 idWeapon::GetGlobalJointTransform
1194 This returns the offset and axis of a weapon bone in world space, suitable for attaching models or lights
1197 bool idWeapon::GetGlobalJointTransform( bool viewModel, const jointHandle_t jointHandle, idVec3 &offset, idMat3 &axis ) {
1200 if ( animator.GetJointTransform( jointHandle, gameLocal.time, offset, axis ) ) {
1201 offset = offset * viewWeaponAxis + viewWeaponOrigin;
1202 axis = axis * viewWeaponAxis;
1207 if ( worldModel.GetEntity() && worldModel.GetEntity()->GetAnimator()->GetJointTransform( jointHandle, gameLocal.time, offset, axis ) ) {
1208 offset = worldModel.GetEntity()->GetPhysics()->GetOrigin() + offset * worldModel.GetEntity()->GetPhysics()->GetAxis();
1209 axis = axis * worldModel.GetEntity()->GetPhysics()->GetAxis();
1213 offset = viewWeaponOrigin;
1214 axis = viewWeaponAxis;
1220 idWeapon::SetPushVelocity
1223 void idWeapon::SetPushVelocity( const idVec3 &pushVelocity ) {
1224 this->pushVelocity = pushVelocity;
1228 /***********************************************************************
1230 State control/player interface
1232 ***********************************************************************/
1239 void idWeapon::Think( void ) {
1240 // do nothing because the present is called from the player through PresentWeapon
1248 void idWeapon::Raise( void ) {
1250 WEAPON_RAISEWEAPON = true;
1259 void idWeapon::PutAway( void ) {
1260 hasBloodSplat = false;
1262 WEAPON_LOWERWEAPON = true;
1269 NOTE: this is only for impulse-triggered reload, auto reload is scripted
1272 void idWeapon::Reload( void ) {
1274 WEAPON_RELOAD = true;
1280 idWeapon::LowerWeapon
1283 void idWeapon::LowerWeapon( void ) {
1286 hideEnd = hideDistance;
1287 if ( gameLocal.time - hideStartTime < hideTime ) {
1288 hideStartTime = gameLocal.time - ( hideTime - ( gameLocal.time - hideStartTime ) );
1290 hideStartTime = gameLocal.time;
1298 idWeapon::RaiseWeapon
1301 void idWeapon::RaiseWeapon( void ) {
1305 hideStart = hideDistance;
1307 if ( gameLocal.time - hideStartTime < hideTime ) {
1308 hideStartTime = gameLocal.time - ( hideTime - ( gameLocal.time - hideStartTime ) );
1310 hideStartTime = gameLocal.time;
1318 idWeapon::HideWeapon
1321 void idWeapon::HideWeapon( void ) {
1323 if ( worldModel.GetEntity() ) {
1324 worldModel.GetEntity()->Hide();
1331 idWeapon::ShowWeapon
1334 void idWeapon::ShowWeapon( void ) {
1336 if ( worldModel.GetEntity() ) {
1337 worldModel.GetEntity()->Show();
1346 idWeapon::HideWorldModel
1349 void idWeapon::HideWorldModel( void ) {
1350 if ( worldModel.GetEntity() ) {
1351 worldModel.GetEntity()->Hide();
1357 idWeapon::ShowWorldModel
1360 void idWeapon::ShowWorldModel( void ) {
1361 if ( worldModel.GetEntity() ) {
1362 worldModel.GetEntity()->Show();
1371 void idWeapon::OwnerDied( void ) {
1373 SetState( "OwnerDied", 0 );
1378 if ( worldModel.GetEntity() ) {
1379 worldModel.GetEntity()->Hide();
1382 // don't clear the weapon immediately since the owner might have killed himself by firing the weapon
1383 // within the current stack frame
1384 PostEventMS( &EV_Weapon_Clear, 0 );
1389 idWeapon::BeginAttack
1392 void idWeapon::BeginAttack( void ) {
1393 if ( status != WP_OUTOFAMMO ) {
1394 lastAttack = gameLocal.time;
1401 if ( !WEAPON_ATTACK ) {
1403 StopSound( SND_CHANNEL_BODY, false );
1406 WEAPON_ATTACK = true;
1414 void idWeapon::EndAttack( void ) {
1415 if ( !WEAPON_ATTACK.IsLinked() ) {
1418 if ( WEAPON_ATTACK ) {
1419 WEAPON_ATTACK = false;
1421 StartSoundShader( sndHum, SND_CHANNEL_BODY, 0, false, NULL );
1431 bool idWeapon::IsReady( void ) const {
1432 return !hide && !IsHidden() && ( ( status == WP_RELOAD ) || ( status == WP_READY ) || ( status == WP_OUTOFAMMO ) );
1437 idWeapon::IsReloading
1440 bool idWeapon::IsReloading( void ) const {
1441 return ( status == WP_RELOAD );
1446 idWeapon::IsHolstered
1449 bool idWeapon::IsHolstered( void ) const {
1450 return ( status == WP_HOLSTERED );
1455 idWeapon::ShowCrosshair
1458 bool idWeapon::ShowCrosshair( void ) const {
1459 return !( state == idStr( WP_RISING ) || state == idStr( WP_LOWERING ) || state == idStr( WP_HOLSTERED ) );
1463 =====================
1465 =====================
1467 bool idWeapon::CanDrop( void ) const {
1468 if ( !weaponDef || !worldModel.GetEntity() ) {
1471 const char *classname = weaponDef->dict.GetString( "def_dropItem" );
1472 if ( !classname[ 0 ] ) {
1480 idWeapon::WeaponStolen
1483 void idWeapon::WeaponStolen( void ) {
1484 assert( !gameLocal.isClient );
1485 if ( projectileEnt ) {
1487 SetState( "WeaponStolen", 0 );
1490 projectileEnt = NULL;
1493 // set to holstered so we can switch weapons right away
1494 status = WP_HOLSTERED;
1500 =====================
1502 =====================
1504 idEntity * idWeapon::DropItem( const idVec3 &velocity, int activateDelay, int removeDelay, bool died ) {
1505 if ( !weaponDef || !worldModel.GetEntity() ) {
1511 const char *classname = weaponDef->dict.GetString( "def_dropItem" );
1512 if ( !classname[0] ) {
1515 StopSound( SND_CHANNEL_BODY, true );
1516 StopSound( SND_CHANNEL_BODY3, true );
1518 return idMoveableItem::DropItem( classname, worldModel.GetEntity()->GetPhysics()->GetOrigin(), worldModel.GetEntity()->GetPhysics()->GetAxis(), velocity, activateDelay, removeDelay );
1521 /***********************************************************************
1523 Script state management
1525 ***********************************************************************/
1528 =====================
1530 =====================
1532 void idWeapon::SetState( const char *statename, int blendFrames ) {
1533 const function_t *func;
1539 func = scriptObject.GetFunction( statename );
1542 gameLocal.Error( "Can't find function '%s' in object '%s'", statename, scriptObject.GetTypeName() );
1545 thread->CallFunction( this, func, true );
1548 animBlendFrames = blendFrames;
1549 if ( g_debugWeapon.GetBool() ) {
1550 gameLocal.Printf( "%d: weapon state : %s\n", gameLocal.time, statename );
1557 /***********************************************************************
1561 ***********************************************************************/
1565 idWeapon::UpdateNozzelFx
1568 void idWeapon::UpdateNozzleFx( void ) {
1576 int la = gameLocal.time - lastAttack + 1;
1579 if ( la < nozzleFxFade ) {
1580 s = ((float)la / nozzleFxFade);
1583 renderEntity.shaderParms[5] = s;
1584 renderEntity.shaderParms[6] = l;
1586 if ( ventLightJointView == INVALID_JOINT ) {
1593 if ( nozzleGlowHandle == -1 ) {
1594 memset(&nozzleGlow, 0, sizeof(nozzleGlow));
1596 nozzleGlow.allowLightInViewID = owner->entityNumber+1;
1598 nozzleGlow.pointLight = true;
1599 nozzleGlow.noShadows = true;
1600 nozzleGlow.lightRadius.x = nozzleGlowRadius;
1601 nozzleGlow.lightRadius.y = nozzleGlowRadius;
1602 nozzleGlow.lightRadius.z = nozzleGlowRadius;
1603 nozzleGlow.shader = nozzleGlowShader;
1604 nozzleGlow.shaderParms[ SHADERPARM_TIMESCALE ] = 1.0f;
1605 nozzleGlow.shaderParms[ SHADERPARM_TIMEOFFSET ] = -MS2SEC( gameLocal.time );
1606 GetGlobalJointTransform( true, ventLightJointView, nozzleGlow.origin, nozzleGlow.axis );
1607 nozzleGlowHandle = gameRenderWorld->AddLightDef(&nozzleGlow);
1610 GetGlobalJointTransform( true, ventLightJointView, nozzleGlow.origin, nozzleGlow.axis );
1612 nozzleGlow.shaderParms[ SHADERPARM_RED ] = nozzleGlowColor.x * s;
1613 nozzleGlow.shaderParms[ SHADERPARM_GREEN ] = nozzleGlowColor.y * s;
1614 nozzleGlow.shaderParms[ SHADERPARM_BLUE ] = nozzleGlowColor.z * s;
1615 gameRenderWorld->UpdateLightDef(nozzleGlowHandle, &nozzleGlow);
1621 idWeapon::BloodSplat
1624 bool idWeapon::BloodSplat( float size ) {
1626 idMat3 localAxis, axistemp;
1627 idVec3 localOrigin, normal;
1629 if ( hasBloodSplat ) {
1633 hasBloodSplat = true;
1635 if ( modelDefHandle < 0 ) {
1639 if ( !GetGlobalJointTransform( true, ejectJointView, localOrigin, localAxis ) ) {
1643 localOrigin[0] += gameLocal.random.RandomFloat() * -10.0f;
1644 localOrigin[1] += gameLocal.random.RandomFloat() * 1.0f;
1645 localOrigin[2] += gameLocal.random.RandomFloat() * -2.0f;
1647 normal = idVec3( gameLocal.random.CRandomFloat(), -gameLocal.random.RandomFloat(), -1 );
1650 idMath::SinCos16( gameLocal.random.RandomFloat() * idMath::TWO_PI, s, c );
1652 localAxis[2] = -normal;
1653 localAxis[2].NormalVectors( axistemp[0], axistemp[1] );
1654 localAxis[0] = axistemp[ 0 ] * c + axistemp[ 1 ] * -s;
1655 localAxis[1] = axistemp[ 0 ] * -s + axistemp[ 1 ] * -c;
1657 localAxis[0] *= 1.0f / size;
1658 localAxis[1] *= 1.0f / size;
1660 idPlane localPlane[2];
1662 localPlane[0] = localAxis[0];
1663 localPlane[0][3] = -(localOrigin * localAxis[0]) + 0.5f;
1665 localPlane[1] = localAxis[1];
1666 localPlane[1][3] = -(localOrigin * localAxis[1]) + 0.5f;
1668 const idMaterial *mtr = declManager->FindMaterial( "textures/decals/duffysplatgun" );
1670 gameRenderWorld->ProjectOverlay( modelDefHandle, localPlane, mtr );
1676 /***********************************************************************
1680 ***********************************************************************/
1684 idWeapon::MuzzleRise
1686 The machinegun and chaingun will incrementally back up as they are being fired
1689 void idWeapon::MuzzleRise( idVec3 &origin, idMat3 &axis ) {
1695 time = kick_endtime - gameLocal.time;
1700 if ( muzzle_kick_maxtime <= 0 ) {
1704 if ( time > muzzle_kick_maxtime ) {
1705 time = muzzle_kick_maxtime;
1708 amount = ( float )time / ( float )muzzle_kick_maxtime;
1709 ang = muzzle_kick_angles * amount;
1710 offset = muzzle_kick_offset * amount;
1712 origin = origin - axis * offset;
1713 axis = ang.ToMat3() * axis;
1718 idWeapon::ConstructScriptObject
1720 Called during idEntity::Spawn. Calls the constructor on the script object.
1721 Can be overridden by subclasses when a thread doesn't need to be allocated.
1724 idThread *idWeapon::ConstructScriptObject( void ) {
1725 const function_t *constructor;
1727 thread->EndThread();
1729 // call script object's constructor
1730 constructor = scriptObject.GetConstructor();
1731 if ( !constructor ) {
1732 gameLocal.Error( "Missing constructor on '%s' for weapon", scriptObject.GetTypeName() );
1735 // init the script object's data
1736 scriptObject.ClearObject();
1737 thread->CallFunction( this, constructor, true );
1745 idWeapon::DeconstructScriptObject
1747 Called during idEntity::~idEntity. Calls the destructor on the script object.
1748 Can be overridden by subclasses when a thread doesn't need to be allocated.
1749 Not called during idGameLocal::MapShutdown.
1752 void idWeapon::DeconstructScriptObject( void ) {
1753 const function_t *destructor;
1759 // don't bother calling the script object's destructor on map shutdown
1760 if ( gameLocal.GameState() == GAMESTATE_SHUTDOWN ) {
1764 thread->EndThread();
1766 // call script object's destructor
1767 destructor = scriptObject.GetDestructor();
1769 // start a thread that will run immediately and end
1770 thread->CallFunction( this, destructor, true );
1772 thread->EndThread();
1775 // clear out the object's memory
1776 scriptObject.ClearObject();
1781 idWeapon::UpdateScript
1784 void idWeapon::UpdateScript( void ) {
1791 // only update the script on new frames
1792 if ( !gameLocal.isNewFrame ) {
1796 if ( idealState.Length() ) {
1797 SetState( idealState, animBlendFrames );
1800 // update script state, which may call Event_LaunchProjectiles, among other things
1802 while( ( thread->Execute() || idealState.Length() ) && count-- ) {
1803 // happens for weapons with no clip (like grenades)
1804 if ( idealState.Length() ) {
1805 SetState( idealState, animBlendFrames );
1809 WEAPON_RELOAD = false;
1814 idWeapon::AlertMonsters
1817 void idWeapon::AlertMonsters( void ) {
1820 idVec3 end = muzzleFlash.origin + muzzleFlash.axis * muzzleFlash.target;
1822 gameLocal.clip.TracePoint( tr, muzzleFlash.origin, end, CONTENTS_OPAQUE | MASK_SHOT_RENDERMODEL | CONTENTS_FLASHLIGHT_TRIGGER, owner );
1823 if ( g_debugWeapon.GetBool() ) {
1824 gameRenderWorld->DebugLine( colorYellow, muzzleFlash.origin, end, 0 );
1825 gameRenderWorld->DebugArrow( colorGreen, muzzleFlash.origin, tr.endpos, 2, 0 );
1828 if ( tr.fraction < 1.0f ) {
1829 ent = gameLocal.GetTraceEntity( tr );
1830 if ( ent->IsType( idAI::Type ) ) {
1831 static_cast<idAI *>( ent )->TouchedByFlashlight( owner );
1832 } else if ( ent->IsType( idTrigger::Type ) ) {
1833 ent->Signal( SIG_TOUCH );
1834 ent->ProcessEvent( &EV_Touch, owner, &tr );
1838 // jitter the trace to try to catch cases where a trace down the center doesn't hit the monster
1839 end += muzzleFlash.axis * muzzleFlash.right * idMath::Sin16( MS2SEC( gameLocal.time ) * 31.34f );
1840 end += muzzleFlash.axis * muzzleFlash.up * idMath::Sin16( MS2SEC( gameLocal.time ) * 12.17f );
1841 gameLocal.clip.TracePoint( tr, muzzleFlash.origin, end, CONTENTS_OPAQUE | MASK_SHOT_RENDERMODEL | CONTENTS_FLASHLIGHT_TRIGGER, owner );
1842 if ( g_debugWeapon.GetBool() ) {
1843 gameRenderWorld->DebugLine( colorYellow, muzzleFlash.origin, end, 0 );
1844 gameRenderWorld->DebugArrow( colorGreen, muzzleFlash.origin, tr.endpos, 2, 0 );
1847 if ( tr.fraction < 1.0f ) {
1848 ent = gameLocal.GetTraceEntity( tr );
1849 if ( ent->IsType( idAI::Type ) ) {
1850 static_cast<idAI *>( ent )->TouchedByFlashlight( owner );
1851 } else if ( ent->IsType( idTrigger::Type ) ) {
1852 ent->Signal( SIG_TOUCH );
1853 ent->ProcessEvent( &EV_Touch, owner, &tr );
1860 idWeapon::PresentWeapon
1863 void idWeapon::PresentWeapon( bool showViewModel ) {
1864 playerViewOrigin = owner->firstPersonViewOrigin;
1865 playerViewAxis = owner->firstPersonViewAxis;
1867 // calculate weapon position based on player movement bobbing
1868 owner->CalculateViewWeaponPos( viewWeaponOrigin, viewWeaponAxis );
1870 // hide offset is for dropping the gun when approaching a GUI or NPC
1871 // This is simpler to manage than doing the weapon put-away animation
1872 if ( gameLocal.time - hideStartTime < hideTime ) {
1873 float frac = ( float )( gameLocal.time - hideStartTime ) / ( float )hideTime;
1874 if ( hideStart < hideEnd ) {
1876 frac = 1.0f - frac * frac;
1880 hideOffset = hideStart + ( hideEnd - hideStart ) * frac;
1882 hideOffset = hideEnd;
1883 if ( hide && disabled ) {
1887 viewWeaponOrigin += hideOffset * viewWeaponAxis[ 2 ];
1889 // kick up based on repeat firing
1890 MuzzleRise( viewWeaponOrigin, viewWeaponAxis );
1892 // set the physics position and orientation
1893 GetPhysics()->SetOrigin( viewWeaponOrigin );
1894 GetPhysics()->SetAxis( viewWeaponAxis );
1897 // update the weapon script
1905 // only show the surface in player view
1906 renderEntity.allowSurfaceInViewID = owner->entityNumber+1;
1908 // crunch the depth range so it never pokes into walls this breaks the machine gun gui
1909 renderEntity.weaponDepthHack = true;
1911 // present the model
1912 if ( showViewModel ) {
1918 if ( worldModel.GetEntity() && worldModel.GetEntity()->GetRenderEntity() ) {
1919 // deal with the third-person visible world model
1920 // don't show shadows of the world model in first person
1921 if ( gameLocal.isMultiplayer || g_showPlayerShadow.GetBool() || pm_thirdPerson.GetBool() ) {
1922 worldModel.GetEntity()->GetRenderEntity()->suppressShadowInViewID = 0;
1924 worldModel.GetEntity()->GetRenderEntity()->suppressShadowInViewID = owner->entityNumber+1;
1925 worldModel.GetEntity()->GetRenderEntity()->suppressShadowInLightID = LIGHTID_VIEW_MUZZLE_FLASH + owner->entityNumber;
1934 if ( showViewModel && !disabled && weaponSmoke && ( weaponSmokeStartTime != 0 ) ) {
1935 // use the barrel joint if available
1936 if ( barrelJointView ) {
1937 GetGlobalJointTransform( true, barrelJointView, muzzleOrigin, muzzleAxis );
1939 // default to going straight out the view
1940 muzzleOrigin = playerViewOrigin;
1941 muzzleAxis = playerViewAxis;
1943 // spit out a particle
1944 if ( !gameLocal.smokeParticles->EmitSmoke( weaponSmoke, weaponSmokeStartTime, gameLocal.random.RandomFloat(), muzzleOrigin, muzzleAxis ) ) {
1945 weaponSmokeStartTime = ( continuousSmoke ) ? gameLocal.time : 0;
1949 if ( showViewModel && strikeSmoke && strikeSmokeStartTime != 0 ) {
1950 // spit out a particle
1951 if ( !gameLocal.smokeParticles->EmitSmoke( strikeSmoke, strikeSmokeStartTime, gameLocal.random.RandomFloat(), strikePos, strikeAxis ) ) {
1952 strikeSmokeStartTime = 0;
1956 // remove the muzzle flash light when it's done
1957 if ( ( !lightOn && ( gameLocal.time >= muzzleFlashEnd ) ) || IsHidden() ) {
1958 if ( muzzleFlashHandle != -1 ) {
1959 gameRenderWorld->FreeLightDef( muzzleFlashHandle );
1960 muzzleFlashHandle = -1;
1962 if ( worldMuzzleFlashHandle != -1 ) {
1963 gameRenderWorld->FreeLightDef( worldMuzzleFlashHandle );
1964 worldMuzzleFlashHandle = -1;
1968 // update the muzzle flash light, so it moves with the gun
1969 if ( muzzleFlashHandle != -1 ) {
1970 UpdateFlashPosition();
1971 gameRenderWorld->UpdateLightDef( muzzleFlashHandle, &muzzleFlash );
1972 gameRenderWorld->UpdateLightDef( worldMuzzleFlashHandle, &worldMuzzleFlash );
1974 // wake up monsters with the flashlight
1975 if ( !gameLocal.isMultiplayer && lightOn && !owner->fl.notarget ) {
1980 // update the gui light
1981 if ( guiLight.lightRadius[0] && guiLightJointView != INVALID_JOINT ) {
1982 GetGlobalJointTransform( true, guiLightJointView, guiLight.origin, guiLight.axis );
1984 if ( ( guiLightHandle != -1 ) ) {
1985 gameRenderWorld->UpdateLightDef( guiLightHandle, &guiLight );
1987 guiLightHandle = gameRenderWorld->AddLightDef( &guiLight );
1991 if ( status != WP_READY && sndHum ) {
1992 StopSound( SND_CHANNEL_BODY, false );
2000 idWeapon::EnterCinematic
2003 void idWeapon::EnterCinematic( void ) {
2004 StopSound( SND_CHANNEL_ANY, false );
2007 SetState( "EnterCinematic", 0 );
2010 WEAPON_ATTACK = false;
2011 WEAPON_RELOAD = false;
2012 WEAPON_NETRELOAD = false;
2013 WEAPON_NETENDRELOAD = false;
2014 WEAPON_NETFIRING = false;
2015 WEAPON_RAISEWEAPON = false;
2016 WEAPON_LOWERWEAPON = false;
2026 idWeapon::ExitCinematic
2029 void idWeapon::ExitCinematic( void ) {
2033 SetState( "ExitCinematic", 0 );
2042 idWeapon::NetCatchup
2045 void idWeapon::NetCatchup( void ) {
2047 SetState( "NetCatchup", 0 );
2054 idWeapon::GetZoomFov
2057 int idWeapon::GetZoomFov( void ) {
2063 idWeapon::GetWeaponAngleOffsets
2066 void idWeapon::GetWeaponAngleOffsets( int *average, float *scale, float *max ) {
2067 *average = weaponAngleOffsetAverages;
2068 *scale = weaponAngleOffsetScale;
2069 *max = weaponAngleOffsetMax;
2074 idWeapon::GetWeaponTimeOffsets
2077 void idWeapon::GetWeaponTimeOffsets( float *time, float *scale ) {
2078 *time = weaponOffsetTime;
2079 *scale = weaponOffsetScale;
2083 /***********************************************************************
2087 ***********************************************************************/
2091 idWeapon::GetAmmoNumForName
2094 ammo_t idWeapon::GetAmmoNumForName( const char *ammoname ) {
2096 const idDict *ammoDict;
2100 ammoDict = gameLocal.FindEntityDefDict( "ammo_types", false );
2102 gameLocal.Error( "Could not find entity definition for 'ammo_types'\n" );
2105 if ( !ammoname[ 0 ] ) {
2109 if ( !ammoDict->GetInt( ammoname, "-1", num ) ) {
2110 gameLocal.Error( "Unknown ammo type '%s'", ammoname );
2113 if ( ( num < 0 ) || ( num >= AMMO_NUMTYPES ) ) {
2114 gameLocal.Error( "Ammo type '%s' value out of range. Maximum ammo types is %d.\n", ammoname, AMMO_NUMTYPES );
2117 return ( ammo_t )num;
2122 idWeapon::GetAmmoNameForNum
2125 const char *idWeapon::GetAmmoNameForNum( ammo_t ammonum ) {
2128 const idDict *ammoDict;
2129 const idKeyValue *kv;
2132 ammoDict = gameLocal.FindEntityDefDict( "ammo_types", false );
2134 gameLocal.Error( "Could not find entity definition for 'ammo_types'\n" );
2137 sprintf( text, "%d", ammonum );
2139 num = ammoDict->GetNumKeyVals();
2140 for( i = 0; i < num; i++ ) {
2141 kv = ammoDict->GetKeyVal( i );
2142 if ( kv->GetValue() == text ) {
2143 return kv->GetKey();
2152 idWeapon::GetAmmoPickupNameForNum
2155 const char *idWeapon::GetAmmoPickupNameForNum( ammo_t ammonum ) {
2158 const idDict *ammoDict;
2159 const idKeyValue *kv;
2161 ammoDict = gameLocal.FindEntityDefDict( "ammo_names", false );
2163 gameLocal.Error( "Could not find entity definition for 'ammo_names'\n" );
2166 const char *name = GetAmmoNameForNum( ammonum );
2168 if ( name && *name ) {
2169 num = ammoDict->GetNumKeyVals();
2170 for( i = 0; i < num; i++ ) {
2171 kv = ammoDict->GetKeyVal( i );
2172 if ( idStr::Icmp( kv->GetKey(), name) == 0 ) {
2173 return kv->GetValue();
2183 idWeapon::AmmoAvailable
2186 int idWeapon::AmmoAvailable( void ) const {
2188 return owner->inventory.HasAmmo( ammoType, ammoRequired );
2196 idWeapon::AmmoInClip
2199 int idWeapon::AmmoInClip( void ) const {
2205 idWeapon::ResetAmmoClip
2208 void idWeapon::ResetAmmoClip( void ) {
2214 idWeapon::GetAmmoType
2217 ammo_t idWeapon::GetAmmoType( void ) const {
2226 int idWeapon::ClipSize( void ) const {
2235 int idWeapon::LowAmmo() const {
2241 idWeapon::AmmoRequired
2244 int idWeapon::AmmoRequired( void ) const {
2245 return ammoRequired;
2250 idWeapon::WriteToSnapshot
2253 void idWeapon::WriteToSnapshot( idBitMsgDelta &msg ) const {
2254 msg.WriteBits( ammoClip, ASYNC_PLAYER_INV_CLIP_BITS );
2255 msg.WriteBits( worldModel.GetSpawnId(), 32 );
2256 msg.WriteBits( lightOn, 1 );
2257 msg.WriteBits( isFiring ? 1 : 0, 1 );
2262 idWeapon::ReadFromSnapshot
2265 void idWeapon::ReadFromSnapshot( const idBitMsgDelta &msg ) {
2266 ammoClip = msg.ReadBits( ASYNC_PLAYER_INV_CLIP_BITS );
2267 worldModel.SetSpawnId( msg.ReadBits( 32 ) );
2268 bool snapLight = msg.ReadBits( 1 ) != 0;
2269 isFiring = msg.ReadBits( 1 ) != 0;
2271 // WEAPON_NETFIRING is only turned on for other clients we're predicting. not for local client
2272 if ( owner && gameLocal.localClientNum != owner->entityNumber && WEAPON_NETFIRING.IsLinked() ) {
2274 // immediately go to the firing state so we don't skip fire animations
2275 if ( !WEAPON_NETFIRING && isFiring ) {
2276 idealState = "Fire";
2279 // immediately switch back to idle
2280 if ( WEAPON_NETFIRING && !isFiring ) {
2281 idealState = "Idle";
2284 WEAPON_NETFIRING = isFiring;
2287 if ( snapLight != lightOn ) {
2294 idWeapon::ClientReceiveEvent
2297 bool idWeapon::ClientReceiveEvent( int event, int time, const idBitMsg &msg ) {
2300 case EVENT_RELOAD: {
2301 if ( gameLocal.time - time < 1000 ) {
2302 if ( WEAPON_NETRELOAD.IsLinked() ) {
2303 WEAPON_NETRELOAD = true;
2304 WEAPON_NETENDRELOAD = false;
2309 case EVENT_ENDRELOAD: {
2310 if ( WEAPON_NETENDRELOAD.IsLinked() ) {
2311 WEAPON_NETENDRELOAD = true;
2315 case EVENT_CHANGESKIN: {
2316 int index = gameLocal.ClientRemapDecl( DECL_SKIN, msg.ReadLong() );
2317 renderEntity.customSkin = ( index != -1 ) ? static_cast<const idDeclSkin *>( declManager->DeclByIndex( DECL_SKIN, index ) ) : NULL;
2319 if ( worldModel.GetEntity() ) {
2320 worldModel.GetEntity()->SetSkin( renderEntity.customSkin );
2325 return idEntity::ClientReceiveEvent( event, time, msg );
2331 /***********************************************************************
2335 ***********************************************************************/
2339 idWeapon::Event_Clear
2342 void idWeapon::Event_Clear( void ) {
2348 idWeapon::Event_GetOwner
2351 void idWeapon::Event_GetOwner( void ) {
2352 idThread::ReturnEntity( owner );
2357 idWeapon::Event_WeaponState
2360 void idWeapon::Event_WeaponState( const char *statename, int blendFrames ) {
2361 const function_t *func;
2363 func = scriptObject.GetFunction( statename );
2366 gameLocal.Error( "Can't find function '%s' in object '%s'", statename, scriptObject.GetTypeName() );
2369 idealState = statename;
2371 if ( !idealState.Icmp( "Fire" ) ) {
2377 animBlendFrames = blendFrames;
2378 thread->DoneProcessing();
2383 idWeapon::Event_WeaponReady
2386 void idWeapon::Event_WeaponReady( void ) {
2389 WEAPON_RAISEWEAPON = false;
2392 StartSoundShader( sndHum, SND_CHANNEL_BODY, 0, false, NULL );
2399 idWeapon::Event_WeaponOutOfAmmo
2402 void idWeapon::Event_WeaponOutOfAmmo( void ) {
2403 status = WP_OUTOFAMMO;
2405 WEAPON_RAISEWEAPON = false;
2411 idWeapon::Event_WeaponReloading
2414 void idWeapon::Event_WeaponReloading( void ) {
2420 idWeapon::Event_WeaponHolstered
2423 void idWeapon::Event_WeaponHolstered( void ) {
2424 status = WP_HOLSTERED;
2426 WEAPON_LOWERWEAPON = false;
2432 idWeapon::Event_WeaponRising
2435 void idWeapon::Event_WeaponRising( void ) {
2438 WEAPON_LOWERWEAPON = false;
2440 owner->WeaponRisingCallback();
2445 idWeapon::Event_WeaponLowering
2448 void idWeapon::Event_WeaponLowering( void ) {
2449 status = WP_LOWERING;
2451 WEAPON_RAISEWEAPON = false;
2453 owner->WeaponLoweringCallback();
2458 idWeapon::Event_UseAmmo
2461 void idWeapon::Event_UseAmmo( int amount ) {
2462 if ( gameLocal.isClient ) {
2466 owner->inventory.UseAmmo( ammoType, ( powerAmmo ) ? amount : ( amount * ammoRequired ) );
2467 if ( clipSize && ammoRequired ) {
2468 ammoClip -= powerAmmo ? amount : ( amount * ammoRequired );
2469 if ( ammoClip < 0 ) {
2477 idWeapon::Event_AddToClip
2480 void idWeapon::Event_AddToClip( int amount ) {
2483 if ( gameLocal.isClient ) {
2488 if ( ammoClip > clipSize ) {
2489 ammoClip = clipSize;
2492 ammoAvail = owner->inventory.HasAmmo( ammoType, ammoRequired );
2493 if ( ammoClip > ammoAvail ) {
2494 ammoClip = ammoAvail;
2500 idWeapon::Event_AmmoInClip
2503 void idWeapon::Event_AmmoInClip( void ) {
2504 int ammo = AmmoInClip();
2505 idThread::ReturnFloat( ammo );
2510 idWeapon::Event_AmmoAvailable
2513 void idWeapon::Event_AmmoAvailable( void ) {
2514 int ammoAvail = owner->inventory.HasAmmo( ammoType, ammoRequired );
2515 idThread::ReturnFloat( ammoAvail );
2520 idWeapon::Event_TotalAmmoCount
2523 void idWeapon::Event_TotalAmmoCount( void ) {
2524 int ammoAvail = owner->inventory.HasAmmo( ammoType, 1 );
2525 idThread::ReturnFloat( ammoAvail );
2530 idWeapon::Event_ClipSize
2533 void idWeapon::Event_ClipSize( void ) {
2534 idThread::ReturnFloat( clipSize );
2539 idWeapon::Event_AutoReload
2542 void idWeapon::Event_AutoReload( void ) {
2544 if ( gameLocal.isClient ) {
2545 idThread::ReturnFloat( 0.0f );
2548 idThread::ReturnFloat( gameLocal.userInfo[ owner->entityNumber ].GetBool( "ui_autoReload" ) );
2553 idWeapon::Event_NetReload
2556 void idWeapon::Event_NetReload( void ) {
2558 if ( gameLocal.isServer ) {
2559 ServerSendEvent( EVENT_RELOAD, NULL, false, -1 );
2565 idWeapon::Event_NetEndReload
2568 void idWeapon::Event_NetEndReload( void ) {
2570 if ( gameLocal.isServer ) {
2571 ServerSendEvent( EVENT_ENDRELOAD, NULL, false, -1 );
2577 idWeapon::Event_PlayAnim
2580 void idWeapon::Event_PlayAnim( int channel, const char *animname ) {
2583 anim = animator.GetAnim( animname );
2585 gameLocal.Warning( "missing '%s' animation on '%s' (%s)", animname, name.c_str(), GetEntityDefName() );
2586 animator.Clear( channel, gameLocal.time, FRAME2MS( animBlendFrames ) );
2589 if ( !( owner && owner->GetInfluenceLevel() ) ) {
2592 animator.PlayAnim( channel, anim, gameLocal.time, FRAME2MS( animBlendFrames ) );
2593 animDoneTime = animator.CurrentAnim( channel )->GetEndTime();
2594 if ( worldModel.GetEntity() ) {
2595 anim = worldModel.GetEntity()->GetAnimator()->GetAnim( animname );
2597 worldModel.GetEntity()->GetAnimator()->PlayAnim( channel, anim, gameLocal.time, FRAME2MS( animBlendFrames ) );
2601 animBlendFrames = 0;
2602 idThread::ReturnInt( 0 );
2607 idWeapon::Event_PlayCycle
2610 void idWeapon::Event_PlayCycle( int channel, const char *animname ) {
2613 anim = animator.GetAnim( animname );
2615 gameLocal.Warning( "missing '%s' animation on '%s' (%s)", animname, name.c_str(), GetEntityDefName() );
2616 animator.Clear( channel, gameLocal.time, FRAME2MS( animBlendFrames ) );
2619 if ( !( owner && owner->GetInfluenceLevel() ) ) {
2622 animator.CycleAnim( channel, anim, gameLocal.time, FRAME2MS( animBlendFrames ) );
2623 animDoneTime = animator.CurrentAnim( channel )->GetEndTime();
2624 if ( worldModel.GetEntity() ) {
2625 anim = worldModel.GetEntity()->GetAnimator()->GetAnim( animname );
2626 worldModel.GetEntity()->GetAnimator()->CycleAnim( channel, anim, gameLocal.time, FRAME2MS( animBlendFrames ) );
2629 animBlendFrames = 0;
2630 idThread::ReturnInt( 0 );
2635 idWeapon::Event_AnimDone
2638 void idWeapon::Event_AnimDone( int channel, int blendFrames ) {
2639 if ( animDoneTime - FRAME2MS( blendFrames ) <= gameLocal.time ) {
2640 idThread::ReturnInt( true );
2642 idThread::ReturnInt( false );
2648 idWeapon::Event_SetBlendFrames
2651 void idWeapon::Event_SetBlendFrames( int channel, int blendFrames ) {
2652 animBlendFrames = blendFrames;
2657 idWeapon::Event_GetBlendFrames
2660 void idWeapon::Event_GetBlendFrames( int channel ) {
2661 idThread::ReturnInt( animBlendFrames );
2666 idWeapon::Event_Next
2669 void idWeapon::Event_Next( void ) {
2670 // change to another weapon if possible
2671 owner->NextBestWeapon();
2676 idWeapon::Event_SetSkin
2679 void idWeapon::Event_SetSkin( const char *skinname ) {
2680 const idDeclSkin *skinDecl;
2682 if ( !skinname || !skinname[ 0 ] ) {
2685 skinDecl = declManager->FindSkin( skinname );
2688 renderEntity.customSkin = skinDecl;
2691 if ( worldModel.GetEntity() ) {
2692 worldModel.GetEntity()->SetSkin( skinDecl );
2695 if ( gameLocal.isServer ) {
2697 byte msgBuf[MAX_EVENT_PARAM_SIZE];
2699 msg.Init( msgBuf, sizeof( msgBuf ) );
2700 msg.WriteLong( ( skinDecl != NULL ) ? gameLocal.ServerRemapDecl( -1, DECL_SKIN, skinDecl->Index() ) : -1 );
2701 ServerSendEvent( EVENT_CHANGESKIN, &msg, false, -1 );
2707 idWeapon::Event_Flashlight
2710 void idWeapon::Event_Flashlight( int enable ) {
2722 idWeapon::Event_GetLightParm
2725 void idWeapon::Event_GetLightParm( int parmnum ) {
2726 if ( ( parmnum < 0 ) || ( parmnum >= MAX_ENTITY_SHADER_PARMS ) ) {
2727 gameLocal.Error( "shader parm index (%d) out of range", parmnum );
2730 idThread::ReturnFloat( muzzleFlash.shaderParms[ parmnum ] );
2735 idWeapon::Event_SetLightParm
2738 void idWeapon::Event_SetLightParm( int parmnum, float value ) {
2739 if ( ( parmnum < 0 ) || ( parmnum >= MAX_ENTITY_SHADER_PARMS ) ) {
2740 gameLocal.Error( "shader parm index (%d) out of range", parmnum );
2743 muzzleFlash.shaderParms[ parmnum ] = value;
2744 worldMuzzleFlash.shaderParms[ parmnum ] = value;
2750 idWeapon::Event_SetLightParms
2753 void idWeapon::Event_SetLightParms( float parm0, float parm1, float parm2, float parm3 ) {
2754 muzzleFlash.shaderParms[ SHADERPARM_RED ] = parm0;
2755 muzzleFlash.shaderParms[ SHADERPARM_GREEN ] = parm1;
2756 muzzleFlash.shaderParms[ SHADERPARM_BLUE ] = parm2;
2757 muzzleFlash.shaderParms[ SHADERPARM_ALPHA ] = parm3;
2759 worldMuzzleFlash.shaderParms[ SHADERPARM_RED ] = parm0;
2760 worldMuzzleFlash.shaderParms[ SHADERPARM_GREEN ] = parm1;
2761 worldMuzzleFlash.shaderParms[ SHADERPARM_BLUE ] = parm2;
2762 worldMuzzleFlash.shaderParms[ SHADERPARM_ALPHA ] = parm3;
2769 idWeapon::Event_CreateProjectile
2772 void idWeapon::Event_CreateProjectile( void ) {
2773 if ( !gameLocal.isClient ) {
2774 projectileEnt = NULL;
2775 gameLocal.SpawnEntityDef( projectileDict, &projectileEnt, false );
2776 if ( projectileEnt ) {
2777 projectileEnt->SetOrigin( GetPhysics()->GetOrigin() );
2778 projectileEnt->Bind( owner, false );
2779 projectileEnt->Hide();
2781 idThread::ReturnEntity( projectileEnt );
2783 idThread::ReturnEntity( NULL );
2789 idWeapon::Event_LaunchProjectiles
2792 void idWeapon::Event_LaunchProjectiles( int num_projectiles, float spread, float fuseOffset, float launchPower, float dmgPower ) {
2803 idBounds ownerBounds, projBounds;
2809 if ( !projectileDict.GetNumKeyVals() ) {
2810 const char *classname = weaponDef->dict.GetString( "classname" );
2811 gameLocal.Warning( "No projectile defined on '%s'", classname );
2815 // avoid all ammo considerations on an MP client
2816 if ( !gameLocal.isClient ) {
2818 // check if we're out of ammo or the clip is empty
2819 int ammoAvail = owner->inventory.HasAmmo( ammoType, ammoRequired );
2820 if ( !ammoAvail || ( ( clipSize != 0 ) && ( ammoClip <= 0 ) ) ) {
2824 // if this is a power ammo weapon ( currently only the bfg ) then make sure
2825 // we only fire as much power as available in each clip
2827 // power comes in as a float from zero to max
2828 // if we use this on more than the bfg will need to define the max
2829 // in the .def as opposed to just in the script so proper calcs
2830 // can be done here.
2831 dmgPower = ( int )dmgPower + 1;
2832 if ( dmgPower > ammoClip ) {
2833 dmgPower = ammoClip;
2837 owner->inventory.UseAmmo( ammoType, ( powerAmmo ) ? dmgPower : ammoRequired );
2838 if ( clipSize && ammoRequired ) {
2839 ammoClip -= powerAmmo ? dmgPower : 1;
2844 if ( !silent_fire ) {
2845 // wake up nearby monsters
2846 gameLocal.AlertAI( owner );
2849 // set the shader parm to the time of last projectile firing,
2850 // which the gun material shaders can reference for single shot barrel glows, etc
2851 renderEntity.shaderParms[ SHADERPARM_DIVERSITY ] = gameLocal.random.CRandomFloat();
2852 renderEntity.shaderParms[ SHADERPARM_TIMEOFFSET ] = -MS2SEC( gameLocal.realClientTime );
2854 if ( worldModel.GetEntity() ) {
2855 worldModel.GetEntity()->SetShaderParm( SHADERPARM_DIVERSITY, renderEntity.shaderParms[ SHADERPARM_DIVERSITY ] );
2856 worldModel.GetEntity()->SetShaderParm( SHADERPARM_TIMEOFFSET, renderEntity.shaderParms[ SHADERPARM_TIMEOFFSET ] );
2859 // calculate the muzzle position
2860 if ( barrelJointView != INVALID_JOINT && projectileDict.GetBool( "launchFromBarrel" ) ) {
2861 // there is an explicit joint for the muzzle
2862 GetGlobalJointTransform( true, barrelJointView, muzzleOrigin, muzzleAxis );
2864 // go straight out of the view
2865 muzzleOrigin = playerViewOrigin;
2866 muzzleAxis = playerViewAxis;
2869 // add some to the kick time, incrementally moving repeat firing weapons back
2870 if ( kick_endtime < gameLocal.realClientTime ) {
2871 kick_endtime = gameLocal.realClientTime;
2873 kick_endtime += muzzle_kick_time;
2874 if ( kick_endtime > gameLocal.realClientTime + muzzle_kick_maxtime ) {
2875 kick_endtime = gameLocal.realClientTime + muzzle_kick_maxtime;
2878 if ( gameLocal.isClient ) {
2880 // predict instant hit projectiles
2881 if ( projectileDict.GetBool( "net_instanthit" ) ) {
2882 float spreadRad = DEG2RAD( spread );
2883 muzzle_pos = muzzleOrigin + playerViewAxis[ 0 ] * 2.0f;
2884 for( i = 0; i < num_projectiles; i++ ) {
2885 ang = idMath::Sin( spreadRad * gameLocal.random.RandomFloat() );
2886 spin = (float)DEG2RAD( 360.0f ) * gameLocal.random.RandomFloat();
2887 dir = playerViewAxis[ 0 ] + playerViewAxis[ 2 ] * ( ang * idMath::Sin( spin ) ) - playerViewAxis[ 1 ] * ( ang * idMath::Cos( spin ) );
2889 gameLocal.clip.Translation( tr, muzzle_pos, muzzle_pos + dir * 4096.0f, NULL, mat3_identity, MASK_SHOT_RENDERMODEL, owner );
2890 if ( tr.fraction < 1.0f ) {
2891 idProjectile::ClientPredictionCollide( this, projectileDict, tr, vec3_origin, true );
2898 ownerBounds = owner->GetPhysics()->GetAbsBounds();
2900 owner->AddProjectilesFired( num_projectiles );
2902 float spreadRad = DEG2RAD( spread );
2903 for( i = 0; i < num_projectiles; i++ ) {
2904 ang = idMath::Sin( spreadRad * gameLocal.random.RandomFloat() );
2905 spin = (float)DEG2RAD( 360.0f ) * gameLocal.random.RandomFloat();
2906 dir = playerViewAxis[ 0 ] + playerViewAxis[ 2 ] * ( ang * idMath::Sin( spin ) ) - playerViewAxis[ 1 ] * ( ang * idMath::Cos( spin ) );
2909 if ( projectileEnt ) {
2910 ent = projectileEnt;
2913 projectileEnt = NULL;
2915 gameLocal.SpawnEntityDef( projectileDict, &ent, false );
2918 if ( !ent || !ent->IsType( idProjectile::Type ) ) {
2919 const char *projectileName = weaponDef->dict.GetString( "def_projectile" );
2920 gameLocal.Error( "'%s' is not an idProjectile", projectileName );
2923 if ( projectileDict.GetBool( "net_instanthit" ) ) {
2924 // don't synchronize this on top of the already predicted effect
2925 ent->fl.networkSync = false;
2928 proj = static_cast<idProjectile *>(ent);
2929 proj->Create( owner, muzzleOrigin, dir );
2931 projBounds = proj->GetPhysics()->GetBounds().Rotate( proj->GetPhysics()->GetAxis() );
2933 // make sure the projectile starts inside the bounding box of the owner
2935 muzzle_pos = muzzleOrigin + playerViewAxis[ 0 ] * 2.0f;
2936 if ( ( ownerBounds - projBounds).RayIntersection( muzzle_pos, playerViewAxis[0], distance ) ) {
2937 start = muzzle_pos + distance * playerViewAxis[0];
2939 start = ownerBounds.GetCenter();
2941 gameLocal.clip.Translation( tr, start, muzzle_pos, proj->GetPhysics()->GetClipModel(), proj->GetPhysics()->GetClipModel()->GetAxis(), MASK_SHOT_RENDERMODEL, owner );
2942 muzzle_pos = tr.endpos;
2945 proj->Launch( muzzle_pos, dir, pushVelocity, fuseOffset, launchPower, dmgPower );
2949 PostEventMS( &EV_Weapon_EjectBrass, brassDelay );
2952 // add the light for the muzzleflash
2957 owner->WeaponFireFeedback( &weaponDef->dict );
2959 // reset muzzle smoke
2960 weaponSmokeStartTime = gameLocal.realClientTime;
2964 =====================
2965 idWeapon::Event_Melee
2966 =====================
2968 void idWeapon::Event_Melee( void ) {
2973 gameLocal.Error( "No meleeDef on '%s'", weaponDef->dict.GetString( "classname" ) );
2976 if ( !gameLocal.isClient ) {
2977 idVec3 start = playerViewOrigin;
2978 idVec3 end = start + playerViewAxis[0] * ( meleeDistance * owner->PowerUpModifier( MELEE_DISTANCE ) );
2979 gameLocal.clip.TracePoint( tr, start, end, MASK_SHOT_RENDERMODEL, owner );
2980 if ( tr.fraction < 1.0f ) {
2981 ent = gameLocal.GetTraceEntity( tr );
2986 if ( g_debugWeapon.GetBool() ) {
2987 gameRenderWorld->DebugLine( colorYellow, start, end, 100 );
2989 gameRenderWorld->DebugBounds( colorRed, ent->GetPhysics()->GetBounds(), ent->GetPhysics()->GetOrigin(), 100 );
2994 const char *hitSound = meleeDef->dict.GetString( "snd_miss" );
2998 float push = meleeDef->dict.GetFloat( "push" );
2999 idVec3 impulse = -push * owner->PowerUpModifier( SPEED ) * tr.c.normal;
3001 if ( gameLocal.world->spawnArgs.GetBool( "no_Weapons" ) && ( ent->IsType( idActor::Type ) || ent->IsType( idAFAttachment::Type) ) ) {
3002 idThread::ReturnInt( 0 );
3006 ent->ApplyImpulse( this, tr.c.id, tr.c.point, impulse );
3008 // weapon stealing - do this before damaging so weapons are not dropped twice
3009 if ( gameLocal.isMultiplayer
3010 && weaponDef && weaponDef->dict.GetBool( "stealing" )
3011 && ent->IsType( idPlayer::Type )
3012 && !owner->PowerUpActive( BERSERK )
3013 && ( gameLocal.gameType != GAME_TDM || gameLocal.serverInfo.GetBool( "si_teamDamage" ) || ( owner->team != static_cast< idPlayer * >( ent )->team ) )
3015 owner->StealWeapon( static_cast< idPlayer * >( ent ) );
3018 if ( ent->fl.takedamage ) {
3019 idVec3 kickDir, globalKickDir;
3020 meleeDef->dict.GetVector( "kickDir", "0 0 0", kickDir );
3021 globalKickDir = muzzleAxis * kickDir;
3022 ent->Damage( owner, owner, globalKickDir, meleeDefName, owner->PowerUpModifier( MELEE_DAMAGE ), tr.c.id );
3026 if ( weaponDef->dict.GetBool( "impact_damage_effect" ) ) {
3028 if ( ent->spawnArgs.GetBool( "bleed" ) ) {
3030 hitSound = meleeDef->dict.GetString( owner->PowerUpActive( BERSERK ) ? "snd_hit_berserk" : "snd_hit" );
3032 ent->AddDamageEffect( tr, impulse, meleeDef->dict.GetString( "classname" ) );
3036 int type = tr.c.material->GetSurfaceType();
3037 if ( type == SURFTYPE_NONE ) {
3038 type = GetDefaultSurfaceType();
3041 const char *materialType = gameLocal.sufaceTypeNames[ type ];
3043 // start impact sound based on material type
3044 hitSound = meleeDef->dict.GetString( va( "snd_%s", materialType ) );
3045 if ( *hitSound == '\0' ) {
3046 hitSound = meleeDef->dict.GetString( "snd_metal" );
3049 if ( gameLocal.time > nextStrikeFx ) {
3052 decal = weaponDef->dict.GetString( "mtr_strike" );
3053 if ( decal && *decal ) {
3054 gameLocal.ProjectDecal( tr.c.point, -tr.c.normal, 8.0f, true, 6.0, decal );
3056 nextStrikeFx = gameLocal.time + 200;
3061 strikeSmokeStartTime = gameLocal.time;
3062 strikePos = tr.c.point;
3063 strikeAxis = -tr.endAxis;
3068 if ( *hitSound != '\0' ) {
3069 const idSoundShader *snd = declManager->FindSound( hitSound );
3070 StartSoundShader( snd, SND_CHANNEL_BODY2, 0, true, NULL );
3073 idThread::ReturnInt( hit );
3074 owner->WeaponFireFeedback( &weaponDef->dict );
3078 idThread::ReturnInt( 0 );
3079 owner->WeaponFireFeedback( &weaponDef->dict );
3083 =====================
3084 idWeapon::Event_GetWorldModel
3085 =====================
3087 void idWeapon::Event_GetWorldModel( void ) {
3088 idThread::ReturnEntity( worldModel.GetEntity() );
3092 =====================
3093 idWeapon::Event_AllowDrop
3094 =====================
3096 void idWeapon::Event_AllowDrop( int allow ) {
3106 idWeapon::Event_EjectBrass
3108 Toss a shell model out from the breach if the bone is present
3111 void idWeapon::Event_EjectBrass( void ) {
3112 if ( !g_showBrass.GetBool() || !owner->CanShowWeaponViewmodel() ) {
3116 if ( ejectJointView == INVALID_JOINT || !brassDict.GetNumKeyVals() ) {
3120 if ( gameLocal.isClient ) {
3125 idVec3 origin, linear_velocity, angular_velocity;
3128 if ( !GetGlobalJointTransform( true, ejectJointView, origin, axis ) ) {
3132 gameLocal.SpawnEntityDef( brassDict, &ent, false );
3133 if ( !ent || !ent->IsType( idDebris::Type ) ) {
3134 gameLocal.Error( "'%s' is not an idDebris", weaponDef ? weaponDef->dict.GetString( "def_ejectBrass" ) : "def_ejectBrass" );
3136 idDebris *debris = static_cast<idDebris *>(ent);
3137 debris->Create( owner, origin, axis );
3140 linear_velocity = 40 * ( playerViewAxis[0] + playerViewAxis[1] + playerViewAxis[2] );
3141 angular_velocity.Set( 10 * gameLocal.random.CRandomFloat(), 10 * gameLocal.random.CRandomFloat(), 10 * gameLocal.random.CRandomFloat() );
3143 debris->GetPhysics()->SetLinearVelocity( linear_velocity );
3144 debris->GetPhysics()->SetAngularVelocity( angular_velocity );
3149 idWeapon::Event_IsInvisible
3152 void idWeapon::Event_IsInvisible( void ) {
3154 idThread::ReturnFloat( 0 );
3157 idThread::ReturnFloat( owner->PowerUpActive( INVISIBILITY ) ? 1 : 0 );
3162 idWeapon::ClientPredictionThink
3165 void idWeapon::ClientPredictionThink( void ) {