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