Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1496 lines
40 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================
  6. #include "cbase.h"
  7. #include "tf_weapon_minigun.h"
  8. #include "decals.h"
  9. #include "in_buttons.h"
  10. #include "tf_fx_shared.h"
  11. #include "debugoverlay_shared.h"
  12. #include "tf_gamerules.h"
  13. // Client specific.
  14. #ifdef CLIENT_DLL
  15. #include "c_tf_player.h"
  16. #include "soundenvelope.h"
  17. #include "achievementmgr.h"
  18. #include "baseachievement.h"
  19. #include "achievements_tf.h"
  20. #include "prediction.h"
  21. #include "clientmode_tf.h"
  22. #include "bone_setup.h"
  23. // NVNT haptics system interface
  24. #include "haptics/ihaptics.h"
  25. // Server specific.
  26. #else
  27. #include "tf_player.h"
  28. #include "particle_parse.h"
  29. #include "tf_gamestats.h"
  30. #include "baseprojectile.h"
  31. #endif
  32. #define MAX_BARREL_SPIN_VELOCITY 20
  33. #define TF_MINIGUN_SPINUP_TIME 0.75f
  34. #define TF_MINIGUN_PENALTY_PERIOD 1.f
  35. //=============================================================================
  36. //
  37. // Weapon Minigun tables.
  38. //
  39. IMPLEMENT_NETWORKCLASS_ALIASED( TFMinigun, DT_WeaponMinigun )
  40. BEGIN_NETWORK_TABLE( CTFMinigun, DT_WeaponMinigun )
  41. // Client specific.
  42. #ifdef CLIENT_DLL
  43. RecvPropInt( RECVINFO( m_iWeaponState ) ),
  44. RecvPropBool( RECVINFO( m_bCritShot ) )
  45. // Server specific.
  46. #else
  47. SendPropInt( SENDINFO( m_iWeaponState ), 4, SPROP_UNSIGNED | SPROP_CHANGES_OFTEN ),
  48. SendPropBool( SENDINFO( m_bCritShot ) )
  49. #endif
  50. END_NETWORK_TABLE()
  51. #ifdef CLIENT_DLL
  52. BEGIN_PREDICTION_DATA( CTFMinigun )
  53. DEFINE_FIELD( m_iWeaponState, FIELD_INTEGER ),
  54. END_PREDICTION_DATA()
  55. #endif
  56. LINK_ENTITY_TO_CLASS( tf_weapon_minigun, CTFMinigun );
  57. PRECACHE_WEAPON_REGISTER( tf_weapon_minigun );
  58. // Server specific.
  59. #ifndef CLIENT_DLL
  60. BEGIN_DATADESC( CTFMinigun )
  61. END_DATADESC()
  62. #endif
  63. //=============================================================================
  64. //
  65. // Weapon Minigun functions.
  66. //
  67. //-----------------------------------------------------------------------------
  68. // Purpose: Constructor.
  69. //-----------------------------------------------------------------------------
  70. CTFMinigun::CTFMinigun()
  71. {
  72. #ifdef CLIENT_DLL
  73. m_pSoundCur = NULL;
  74. m_hEjectBrassWeapon = NULL;
  75. m_pEjectBrassEffect = NULL;
  76. m_iEjectBrassAttachment = -1;
  77. m_hMuzzleEffectWeapon = NULL;
  78. m_pMuzzleEffect = NULL;
  79. m_iMuzzleAttachment = -1;
  80. m_nShotsFired = 0;
  81. ListenForGameEvent( "teamplay_round_active" );
  82. ListenForGameEvent( "localplayer_respawn" );
  83. m_bRageDraining = false;
  84. m_bPrevRageDraining = false;
  85. #endif
  86. m_bAttack3Down = false;
  87. WeaponReset();
  88. }
  89. //-----------------------------------------------------------------------------
  90. // Purpose: Destructor.
  91. //-----------------------------------------------------------------------------
  92. CTFMinigun::~CTFMinigun()
  93. {
  94. WeaponReset();
  95. }
  96. //-----------------------------------------------------------------------------
  97. // Purpose:
  98. //-----------------------------------------------------------------------------
  99. void CTFMinigun::WeaponReset( void )
  100. {
  101. BaseClass::WeaponReset();
  102. CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() );
  103. if ( pPlayer )
  104. {
  105. pPlayer->m_Shared.RemoveCond( TF_COND_AIMING );
  106. pPlayer->TeamFortress_SetSpeed();
  107. #ifdef GAME_DLL
  108. pPlayer->ClearWeaponFireScene();
  109. m_flAegisCheckTime = 0.0f;
  110. #endif
  111. m_flNextRingOfFireAttackTime = 0.0f;
  112. m_flLastAmmoDrainTime = gpGlobals->curtime;
  113. m_flAccumulatedAmmoDrain = 0.0f;
  114. }
  115. SetWeaponState( AC_STATE_IDLE );
  116. m_iWeaponMode = TF_WEAPON_PRIMARY_MODE;
  117. m_bCritShot = false;
  118. m_flStartedFiringAt = -1.0f;
  119. m_flStartedWindUpAt = -1.f;
  120. m_flNextFiringSpeech = 0.0f;
  121. m_flBarrelAngle = 0.0f;
  122. m_flBarrelCurrentVelocity = 0.0f;
  123. m_flBarrelTargetVelocity = 0.0f;
  124. #ifdef CLIENT_DLL
  125. if ( m_pSoundCur )
  126. {
  127. CSoundEnvelopeController::GetController().SoundDestroy( m_pSoundCur );
  128. m_pSoundCur = NULL;
  129. }
  130. m_iMinigunSoundCur = -1;
  131. m_flMinigunSoundCurrentPitch = 1.0f;
  132. StopMuzzleEffect();
  133. StopBrassEffect();
  134. #endif
  135. }
  136. #ifdef GAME_DLL
  137. int CTFMinigun::UpdateTransmitState( void )
  138. {
  139. // ALWAYS transmit to all clients.
  140. return SetTransmitState( FL_EDICT_ALWAYS );
  141. }
  142. #endif
  143. //-----------------------------------------------------------------------------
  144. // Purpose:
  145. //-----------------------------------------------------------------------------
  146. void CTFMinigun::Precache( void )
  147. {
  148. PrecacheScriptSound( "Halloween.HeadlessBossAxeHitWorld" );
  149. // FIXME: Do we still need these??
  150. PrecacheScriptSound( "MVM.GiantHeavyGunWindUp" );
  151. PrecacheScriptSound( "MVM.GiantHeavyGunWindDown" );
  152. PrecacheScriptSound( "MVM.GiantHeavyGunFire" );
  153. PrecacheScriptSound( "MVM.GiantHeavyGunSpin" );
  154. BaseClass::Precache();
  155. }
  156. //-----------------------------------------------------------------------------
  157. // Purpose:
  158. //-----------------------------------------------------------------------------
  159. void CTFMinigun::ItemPostFrame( void )
  160. {
  161. // Prevent base code from ever playing empty sounds, minigun handles them manually.
  162. m_flNextEmptySoundTime = gpGlobals->curtime + 1.0;
  163. #ifdef GAME_DLL
  164. CBasePlayer *pOwner = GetPlayerOwner();
  165. if ( pOwner )
  166. {
  167. if ( ( pOwner->m_nButtons & IN_ATTACK3 ) && !m_bAttack3Down )
  168. {
  169. ActivatePushBackAttackMode();
  170. m_bAttack3Down = true;
  171. }
  172. else if ( !( pOwner->m_nButtons & IN_ATTACK3 ) && m_bAttack3Down )
  173. {
  174. m_bAttack3Down = false;
  175. }
  176. }
  177. #endif // GAME_DLL
  178. BaseClass::ItemPostFrame();
  179. }
  180. //-----------------------------------------------------------------------------
  181. // Purpose:
  182. //-----------------------------------------------------------------------------
  183. void CTFMinigun::PrimaryAttack()
  184. {
  185. SharedAttack();
  186. }
  187. //-----------------------------------------------------------------------------
  188. // Purpose:
  189. //-----------------------------------------------------------------------------
  190. void CTFMinigun::SharedAttack()
  191. {
  192. CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() );
  193. if ( !pPlayer )
  194. return;
  195. if ( !CanAttack() )
  196. {
  197. WeaponIdle();
  198. return;
  199. }
  200. #ifdef CLIENT_DLL
  201. m_bRageDraining = pPlayer->m_Shared.IsRageDraining();
  202. #endif // CLIENT_DLL
  203. if ( pPlayer->m_nButtons & IN_ATTACK )
  204. {
  205. m_iWeaponMode = TF_WEAPON_PRIMARY_MODE;
  206. }
  207. else if ( pPlayer->m_nButtons & IN_ATTACK2 )
  208. {
  209. m_iWeaponMode = TF_WEAPON_SECONDARY_MODE;
  210. }
  211. switch ( m_iWeaponState )
  212. {
  213. default:
  214. case AC_STATE_IDLE:
  215. {
  216. // Removed the need for cells to powerup the AC
  217. WindUp();
  218. float flSpinUpTime = TF_MINIGUN_SPINUP_TIME;
  219. CALL_ATTRIB_HOOK_FLOAT( flSpinUpTime, mult_minigun_spinup_time );
  220. float flSpinTimeMultiplier = Max( flSpinUpTime, 0.00001f );
  221. if ( pPlayer->GetViewModel( 0 ) )
  222. {
  223. pPlayer->GetViewModel( 0 )->SetPlaybackRate( TF_MINIGUN_SPINUP_TIME / flSpinTimeMultiplier );
  224. }
  225. if ( pPlayer->GetViewModel( 1 ) )
  226. {
  227. pPlayer->GetViewModel( 1 )->SetPlaybackRate( TF_MINIGUN_SPINUP_TIME / flSpinTimeMultiplier );
  228. }
  229. m_flNextPrimaryAttack = gpGlobals->curtime + flSpinUpTime;
  230. m_flNextSecondaryAttack = gpGlobals->curtime + flSpinUpTime;
  231. m_flTimeWeaponIdle = gpGlobals->curtime + flSpinUpTime;
  232. m_flStartedFiringAt = -1.f;
  233. pPlayer->DoAnimationEvent( PLAYERANIMEVENT_ATTACK_PRE );
  234. break;
  235. }
  236. case AC_STATE_STARTFIRING:
  237. {
  238. // Start playing the looping fire sound
  239. if ( m_flNextPrimaryAttack <= gpGlobals->curtime )
  240. {
  241. if ( m_iWeaponMode == TF_WEAPON_SECONDARY_MODE )
  242. {
  243. SetWeaponState( AC_STATE_SPINNING );
  244. }
  245. else
  246. {
  247. SetWeaponState( AC_STATE_FIRING );
  248. }
  249. #ifdef GAME_DLL
  250. if ( m_iWeaponState == AC_STATE_SPINNING )
  251. {
  252. pPlayer->SpeakWeaponFire( MP_CONCEPT_WINDMINIGUN );
  253. }
  254. else
  255. {
  256. pPlayer->SpeakWeaponFire( MP_CONCEPT_FIREMINIGUN );
  257. }
  258. #endif
  259. m_flNextSecondaryAttack = m_flNextPrimaryAttack = m_flTimeWeaponIdle = gpGlobals->curtime + 0.1;
  260. }
  261. break;
  262. }
  263. case AC_STATE_FIRING:
  264. {
  265. if ( m_iWeaponMode == TF_WEAPON_SECONDARY_MODE )
  266. {
  267. SetWeaponState( AC_STATE_SPINNING );
  268. }
  269. if ( m_iWeaponState == AC_STATE_SPINNING )
  270. {
  271. #ifdef GAME_DLL
  272. pPlayer->ClearWeaponFireScene();
  273. pPlayer->SpeakWeaponFire( MP_CONCEPT_WINDMINIGUN );
  274. #endif
  275. m_flNextSecondaryAttack = m_flNextPrimaryAttack = m_flTimeWeaponIdle = gpGlobals->curtime + 0.1;
  276. }
  277. else if ( pPlayer->GetAmmoCount(m_iPrimaryAmmoType) <= 0 )
  278. {
  279. SetWeaponState( AC_STATE_DRYFIRE );
  280. }
  281. else
  282. {
  283. if ( m_flStartedFiringAt < 0 )
  284. {
  285. m_flStartedFiringAt = gpGlobals->curtime;
  286. }
  287. #ifdef GAME_DLL
  288. if ( m_flNextFiringSpeech < gpGlobals->curtime )
  289. {
  290. m_flNextFiringSpeech = gpGlobals->curtime + 5.0;
  291. pPlayer->SpeakConceptIfAllowed( MP_CONCEPT_MINIGUN_FIREWEAPON );
  292. }
  293. #endif
  294. #ifdef CLIENT_DLL
  295. int nAmmo = 0;
  296. if ( prediction->IsFirstTimePredicted() &&
  297. C_BasePlayer::GetLocalPlayer() == pPlayer )
  298. {
  299. nAmmo = pPlayer->GetAmmoCount( m_iPrimaryAmmoType );
  300. }
  301. #endif
  302. // Only fire if we're actually shooting
  303. BaseClass::PrimaryAttack(); // fire and do timers
  304. #ifdef CLIENT_DLL
  305. if ( prediction->IsFirstTimePredicted() &&
  306. C_BasePlayer::GetLocalPlayer() == pPlayer &&
  307. nAmmo != pPlayer->GetAmmoCount( m_iPrimaryAmmoType ) ) // did PrimaryAttack() fire a shot? (checking our ammo to find out)
  308. {
  309. m_nShotsFired++;
  310. if ( m_nShotsFired == 1000 ) // == and not >= so we don't keep awarding this every shot after it's achieved
  311. {
  312. g_AchievementMgrTF.OnAchievementEvent( ACHIEVEMENT_TF_HEAVY_FIRE_LOTS );
  313. }
  314. // NVNT the local player fired a shot. notify the haptics system.
  315. if ( haptics )
  316. haptics->ProcessHapticEvent(2,"Weapons","minigun_fire");
  317. }
  318. #endif
  319. CalcIsAttackCritical();
  320. m_bCritShot = IsCurrentAttackACrit();
  321. pPlayer->DoAnimationEvent( PLAYERANIMEVENT_ATTACK_PRIMARY );
  322. #ifdef GAME_DLL
  323. int iAttackProjectiles = 0;
  324. CALL_ATTRIB_HOOK_INT( iAttackProjectiles, attack_projectiles );
  325. #ifdef TF_RAID_MODE
  326. if ( TFGameRules()->IsBossBattleMode() )
  327. {
  328. iAttackProjectiles = 1;
  329. }
  330. #endif // TF_RAID_MODE
  331. if ( iAttackProjectiles )
  332. {
  333. AttackEnemyProjectiles();
  334. }
  335. #endif // GAME_DLL
  336. m_flTimeWeaponIdle = gpGlobals->curtime + 0.2;
  337. }
  338. break;
  339. }
  340. case AC_STATE_DRYFIRE:
  341. {
  342. m_flStartedFiringAt = -1.f;
  343. m_flStartedWindUpAt = -1.f;
  344. if ( pPlayer->GetAmmoCount(m_iPrimaryAmmoType) > 0 )
  345. {
  346. SetWeaponState( AC_STATE_FIRING );
  347. }
  348. else if ( m_iWeaponMode == TF_WEAPON_SECONDARY_MODE )
  349. {
  350. SetWeaponState( AC_STATE_SPINNING );
  351. }
  352. SendWeaponAnim( ACT_VM_SECONDARYATTACK );
  353. break;
  354. }
  355. case AC_STATE_SPINNING:
  356. {
  357. m_flStartedFiringAt = -1.f;
  358. if ( m_iWeaponMode == TF_WEAPON_PRIMARY_MODE )
  359. {
  360. if ( pPlayer->GetAmmoCount(m_iPrimaryAmmoType) > 0 )
  361. {
  362. #ifdef GAME_DLL
  363. pPlayer->ClearWeaponFireScene();
  364. pPlayer->SpeakWeaponFire( MP_CONCEPT_FIREMINIGUN );
  365. #endif
  366. SetWeaponState( AC_STATE_FIRING );
  367. }
  368. else
  369. {
  370. SetWeaponState( AC_STATE_DRYFIRE );
  371. }
  372. }
  373. SendWeaponAnim( ACT_VM_SECONDARYATTACK );
  374. break;
  375. }
  376. }
  377. if ( pPlayer->GetAmmoCount( m_iPrimaryAmmoType ) > 0 )
  378. {
  379. if ( m_iWeaponState > AC_STATE_STARTFIRING )
  380. {
  381. int nRingOfFireWhileAiming = 0;
  382. CALL_ATTRIB_HOOK_INT( nRingOfFireWhileAiming, ring_of_fire_while_aiming );
  383. if ( nRingOfFireWhileAiming != 0 )
  384. {
  385. RingOfFireAttack( nRingOfFireWhileAiming );
  386. }
  387. }
  388. if ( m_iWeaponState == AC_STATE_SPINNING || m_iWeaponState == AC_STATE_FIRING )
  389. {
  390. int nUsesAmmoWhileAiming = 0;
  391. CALL_ATTRIB_HOOK_INT( nUsesAmmoWhileAiming, uses_ammo_while_aiming );
  392. if ( nUsesAmmoWhileAiming > 0 )
  393. {
  394. m_flAccumulatedAmmoDrain += nUsesAmmoWhileAiming * ( gpGlobals->curtime - m_flLastAmmoDrainTime );
  395. m_flLastAmmoDrainTime = gpGlobals->curtime;
  396. if ( m_flAccumulatedAmmoDrain > 1.0f )
  397. {
  398. int nAmmoRemoved = m_flAccumulatedAmmoDrain;
  399. pPlayer->RemoveAmmo( nAmmoRemoved, m_iPrimaryAmmoType );
  400. m_flAccumulatedAmmoDrain -= nAmmoRemoved;
  401. }
  402. }
  403. }
  404. }
  405. }
  406. void CTFMinigun::SetWeaponState( MinigunState_t nState )
  407. {
  408. if ( m_iWeaponState != nState )
  409. {
  410. if ( m_iWeaponState == AC_STATE_IDLE || m_iWeaponState == AC_STATE_STARTFIRING || m_iWeaponState == AC_STATE_DRYFIRE )
  411. {
  412. // Transitioning from non firing or non fully spinning states resets when our drain start point and when the ring of fire can start
  413. m_flLastAmmoDrainTime = gpGlobals->curtime;
  414. m_flNextRingOfFireAttackTime = gpGlobals->curtime + 0.5f;
  415. }
  416. m_iWeaponState = nState;
  417. }
  418. }
  419. //-----------------------------------------------------------------------------
  420. // Purpose: Fall through to Primary Attack
  421. //-----------------------------------------------------------------------------
  422. void CTFMinigun::SecondaryAttack( void )
  423. {
  424. CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() );
  425. if ( !pPlayer )
  426. return;
  427. SharedAttack();
  428. }
  429. void CTFMinigun::RingOfFireAttack( int nDamage )
  430. {
  431. if ( m_flNextRingOfFireAttackTime == 0.0f || m_flNextRingOfFireAttackTime > gpGlobals->curtime )
  432. return;
  433. CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() );
  434. if ( !pPlayer )
  435. return;
  436. #ifdef GAME_DLL
  437. Vector vOrigin = pPlayer->GetAbsOrigin();
  438. const float flFireRadius = 135.0f;
  439. const float flFireRadiusSqr = flFireRadius * flFireRadius;
  440. CBaseEntity *pEntity = NULL;
  441. for ( CEntitySphereQuery sphere( vOrigin, flFireRadius ); (pEntity = sphere.GetCurrentEntity()) != NULL; sphere.NextEntity() )
  442. {
  443. // Skip players on the same team or who are invuln
  444. CTFPlayer *pVictim = ToTFPlayer( pEntity );
  445. if ( !pVictim || InSameTeam( pVictim ) || pVictim->m_Shared.InCond( TF_COND_INVULNERABLE ) )
  446. continue;
  447. // Make sure their bounding box is near our ground plane
  448. Vector vMins = pVictim->GetPlayerMins();
  449. Vector vMaxs = pVictim->GetPlayerMaxs();
  450. if ( !( vOrigin.z > pVictim->GetAbsOrigin().z + vMins.z - 32.0f && vOrigin.z < pVictim->GetAbsOrigin().z + vMaxs.z ) )
  451. {
  452. continue;
  453. }
  454. // CEntitySphereQuery actually does a box test. So we need to make sure the distance is less than the radius first.
  455. Vector vecPos;
  456. pEntity->CollisionProp()->CalcNearestPoint( vOrigin, &vecPos );
  457. if ( ( vOrigin - vecPos ).LengthSqr() > flFireRadiusSqr )
  458. continue;
  459. // Finally LOS test
  460. trace_t tr;
  461. Vector vecSrc = WorldSpaceCenter();
  462. Vector vecSpot = pEntity->WorldSpaceCenter();
  463. CTraceFilterSimple filter( this, COLLISION_GROUP_PROJECTILE );
  464. UTIL_TraceLine( vecSrc, vecSpot, MASK_SOLID_BRUSHONLY, &filter, &tr );
  465. // If we don't trace the whole way to the target, and we didn't hit the target entity, we're blocked
  466. if ( tr.fraction != 1.0 && tr.m_pEnt != pEntity )
  467. continue;
  468. pVictim->TakeDamage( CTakeDamageInfo( pPlayer, pPlayer, this, vec3_origin, vOrigin, nDamage, DMG_PLASMA, 0, &vOrigin ) );
  469. }
  470. DispatchParticleEffect( "heavy_ring_of_fire", pPlayer->GetAbsOrigin(), vec3_angle );
  471. #else
  472. DispatchParticleEffect( "heavy_ring_of_fire_fp", pPlayer->GetAbsOrigin(), vec3_angle );
  473. #endif // #ifdef GAME_DLL
  474. m_flNextRingOfFireAttackTime = gpGlobals->curtime + 0.5f;
  475. }
  476. #ifdef GAME_DLL
  477. //-----------------------------------------------------------------------------
  478. // Purpose: Scans along a line for rockets and grenades to destroy
  479. //-----------------------------------------------------------------------------
  480. void CTFMinigun::AttackEnemyProjectiles( void )
  481. {
  482. if ( gpGlobals->curtime < m_flAegisCheckTime )
  483. return;
  484. CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() );
  485. if ( !pPlayer )
  486. return;
  487. // Parameters
  488. const int nSweepDist = 300; // How far out
  489. const int nHitDist = ( pPlayer->IsMiniBoss() ) ? 56 : 38; // How far from the center line (radial)
  490. float flRechargeTime = 0.1f;
  491. // Pos
  492. const Vector &vecGunPos = ( pPlayer->IsMiniBoss() ) ? pPlayer->Weapon_ShootPosition() : pPlayer->EyePosition();
  493. Vector vecForward;
  494. AngleVectors( GetAbsAngles(), &vecForward );
  495. Vector vecGunAimEnd = vecGunPos + vecForward * (float)nSweepDist;
  496. bool bDebug = false;
  497. if ( bDebug )
  498. {
  499. // NDebugOverlay::Sphere( vecGunPos + vecForward * nSweepDist, nSweepDist, 0, 255, 0, 40, 5 );
  500. NDebugOverlay::Box( vecGunPos, -Vector( 5, 5, 5 ), Vector( 5, 5, 5 ), 255, 0, 0, 40, 5 );
  501. NDebugOverlay::Box( vecGunAimEnd, -Vector( 5, 5, 5 ), Vector( 5, 5, 5 ), 255, 0, 0, 40, 5 );
  502. NDebugOverlay::Line( vecGunPos, vecGunAimEnd, 255, 255, 255, true, 5 );
  503. }
  504. // Iterate through each grenade/rocket in the sphere
  505. const int nMaxEnts = 32;
  506. CBaseEntity *pObjects[ nMaxEnts ];
  507. int nCount = UTIL_EntitiesInSphere( pObjects, nMaxEnts, vecGunPos, nSweepDist, FL_GRENADE );
  508. for ( int i = 0; i < nCount; i++ )
  509. {
  510. if ( InSameTeam( pObjects[i] ) )
  511. continue;
  512. // Hit?
  513. const Vector &vecGrenadePos = pObjects[i]->GetAbsOrigin();
  514. float flDistToLine = CalcDistanceToLineSegment( vecGrenadePos, vecGunPos, vecGunAimEnd );
  515. if ( flDistToLine <= nHitDist )
  516. {
  517. if ( pPlayer->FVisible( pObjects[i], MASK_SOLID ) == false )
  518. continue;
  519. if ( ( pObjects[i]->GetFlags() & FL_ONGROUND ) )
  520. continue;
  521. if ( !pObjects[i]->IsDeflectable() )
  522. continue;
  523. CBaseProjectile *pProjectile = dynamic_cast< CBaseProjectile* >( pObjects[i] );
  524. if ( pProjectile && pProjectile->IsDestroyable() )
  525. {
  526. pProjectile->IncrementDestroyableHitCount();
  527. if ( bDebug )
  528. {
  529. NDebugOverlay::Box( vecGrenadePos, -Vector( 5, 5, 5 ), Vector( 5, 5, 5 ), 255, 0, 255, 40, 5 );
  530. }
  531. // Did we destroy it?
  532. int iAttackProjectiles = 0;
  533. CALL_ATTRIB_HOOK_INT( iAttackProjectiles, attack_projectiles );
  534. int nHitsRequired = m_bCritShot ? 1 : (int)RemapValClamped( iAttackProjectiles, 1, 2, 2, 1 );
  535. if ( pProjectile->GetDestroyableHitCount() >= nHitsRequired )
  536. {
  537. pProjectile->Destroy( false, true );
  538. EmitSound( "Halloween.HeadlessBossAxeHitWorld" );
  539. CTF_GameStats.Event_PlayerAwardBonusPoints( pPlayer, NULL, 2 );
  540. // Weaker version has a longer cooldown
  541. if ( iAttackProjectiles < 2 )
  542. {
  543. flRechargeTime = 0.3f;
  544. }
  545. }
  546. else
  547. {
  548. // Nicked it
  549. pObjects[i]->EmitSound( "FX_RicochetSound.Ricochet" );
  550. }
  551. }
  552. }
  553. }
  554. m_flAegisCheckTime = gpGlobals->curtime + flRechargeTime;
  555. }
  556. //-----------------------------------------------------------------------------
  557. // Purpose: Reduces damage and adds extra knockback.
  558. //-----------------------------------------------------------------------------
  559. void CTFMinigun::ActivatePushBackAttackMode( void )
  560. {
  561. CTFPlayer *pOwner = ToTFPlayer( GetPlayerOwner() );
  562. if ( !pOwner )
  563. return;
  564. int iRage = 0;
  565. CALL_ATTRIB_HOOK_INT_ON_OTHER( pOwner, iRage, generate_rage_on_dmg );
  566. if ( !iRage )
  567. return;
  568. if ( pOwner->m_Shared.IsRageDraining() )
  569. return;
  570. if ( pOwner->m_Shared.GetRageMeter() < 100.f )
  571. {
  572. pOwner->EmitSound( "Player.DenyWeaponSelection" );
  573. return;
  574. }
  575. pOwner->m_Shared.StartRageDrain();
  576. EmitSound( "Heavy.Battlecry03" );
  577. }
  578. #endif
  579. //-----------------------------------------------------------------------------
  580. // Purpose: UI Progress (same as GetProgress() without the division by 100.0f)
  581. //-----------------------------------------------------------------------------
  582. bool CTFMinigun::IsRageFull( void )
  583. {
  584. CTFPlayer *pPlayer = GetTFPlayerOwner();
  585. if ( !pPlayer )
  586. return false;
  587. return ( pPlayer->m_Shared.GetRageMeter() >= 100.0f );
  588. }
  589. //-----------------------------------------------------------------------------
  590. // Purpose:
  591. //-----------------------------------------------------------------------------
  592. bool CTFMinigun::EffectMeterShouldFlash( void )
  593. {
  594. CTFPlayer *pPlayer = GetTFPlayerOwner();
  595. if ( !pPlayer )
  596. return false;
  597. if ( pPlayer && ( IsRageFull() || pPlayer->m_Shared.IsRageDraining() ) )
  598. return true;
  599. else
  600. return false;
  601. }
  602. //-----------------------------------------------------------------------------
  603. // Purpose:
  604. //-----------------------------------------------------------------------------
  605. bool CTFMinigun::CanInspect() const
  606. {
  607. return BaseClass::CanInspect() && CanHolster();
  608. }
  609. //-----------------------------------------------------------------------------
  610. // Purpose: UI Progress
  611. //-----------------------------------------------------------------------------
  612. float CTFMinigun::GetProgress( void )
  613. {
  614. CTFPlayer *pPlayer = GetTFPlayerOwner();
  615. if ( !pPlayer )
  616. return 0.f;
  617. return pPlayer->m_Shared.GetRageMeter() / 100.0f;
  618. }
  619. //-----------------------------------------------------------------------------
  620. // Purpose:
  621. //-----------------------------------------------------------------------------
  622. void CTFMinigun::WindUp( void )
  623. {
  624. // Get the player owning the weapon.
  625. CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() );
  626. if ( !pPlayer )
  627. return;
  628. // Play wind-up animation and sound (SPECIAL1).
  629. SendWeaponAnim( ACT_MP_ATTACK_STAND_PREFIRE );
  630. // Set the appropriate firing state.
  631. SetWeaponState( AC_STATE_STARTFIRING );
  632. pPlayer->m_Shared.AddCond( TF_COND_AIMING );
  633. #ifndef CLIENT_DLL
  634. pPlayer->StopRandomExpressions();
  635. #endif
  636. #ifdef CLIENT_DLL
  637. WeaponSoundUpdate();
  638. #endif
  639. // Update player's speed
  640. pPlayer->TeamFortress_SetSpeed();
  641. if ( m_flStartedWindUpAt == -1.f )
  642. {
  643. m_flStartedWindUpAt = gpGlobals->curtime;
  644. }
  645. }
  646. //-----------------------------------------------------------------------------
  647. // Purpose:
  648. //-----------------------------------------------------------------------------
  649. bool CTFMinigun::CanHolster( void ) const
  650. {
  651. bool bCanHolster = CanHolsterWhileSpinning();
  652. CTFPlayer *pPlayer = GetTFPlayerOwner();
  653. if( pPlayer )
  654. {
  655. // PASSTIME need to be able to immediately holster when you catch the ball
  656. if ( pPlayer->m_Shared.HasPasstimeBall() )
  657. return true;
  658. // TF_COND_MELEE_ONLY need to be able to immediately holster and switch to melee weapon
  659. if ( pPlayer->m_Shared.InCond( TF_COND_MELEE_ONLY ) )
  660. return true;
  661. }
  662. #ifdef STAGING_ONLY
  663. // Agility powerup allows holstering while spinning
  664. bCanHolster |= ( pPlayer && pPlayer->m_Shared.GetCarryingRuneType() == RUNE_AGILITY );
  665. #endif //STAGING_ONLY
  666. if ( bCanHolster )
  667. {
  668. if ( m_iWeaponState == AC_STATE_STARTFIRING || m_iWeaponState == AC_STATE_FIRING )
  669. return false;
  670. }
  671. else
  672. {
  673. if ( m_iWeaponState > AC_STATE_IDLE )
  674. return false;
  675. if ( GetActivity() == ACT_MP_ATTACK_STAND_POSTFIRE || GetActivity() == ACT_PRIMARY_ATTACK_STAND_POSTFIRE )
  676. {
  677. if ( !IsViewModelSequenceFinished() )
  678. return false;
  679. }
  680. }
  681. return BaseClass::CanHolster();
  682. }
  683. //-----------------------------------------------------------------------------
  684. // Purpose:
  685. //-----------------------------------------------------------------------------
  686. bool CTFMinigun::Holster( CBaseCombatWeapon *pSwitchingTo )
  687. {
  688. if ( m_iWeaponState > AC_STATE_IDLE )
  689. {
  690. WindDown();
  691. }
  692. return BaseClass::Holster( pSwitchingTo );
  693. }
  694. //-----------------------------------------------------------------------------
  695. // Purpose:
  696. //-----------------------------------------------------------------------------
  697. bool CTFMinigun::Lower( void )
  698. {
  699. if ( m_iWeaponState > AC_STATE_IDLE )
  700. {
  701. WindDown();
  702. }
  703. return BaseClass::Lower();
  704. }
  705. //-----------------------------------------------------------------------------
  706. // Purpose:
  707. //-----------------------------------------------------------------------------
  708. void CTFMinigun::WindDown( void )
  709. {
  710. // Get the player owning the weapon.
  711. CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() );
  712. if ( !pPlayer )
  713. return;
  714. SendWeaponAnim( ACT_MP_ATTACK_STAND_POSTFIRE );
  715. #ifdef CLIENT_DLL
  716. if ( !HasSpinSounds() && m_iWeaponState == AC_STATE_FIRING )
  717. {
  718. PlayStopFiringSound();
  719. }
  720. #endif
  721. // Set the appropriate firing state.
  722. SetWeaponState( AC_STATE_IDLE );
  723. pPlayer->m_Shared.RemoveCond( TF_COND_AIMING );
  724. #ifdef CLIENT_DLL
  725. WeaponSoundUpdate();
  726. #else
  727. pPlayer->ClearWeaponFireScene();
  728. #endif
  729. // Time to weapon idle.
  730. m_flTimeWeaponIdle = gpGlobals->curtime + 2.0;
  731. // Update player's speed
  732. pPlayer->TeamFortress_SetSpeed();
  733. #ifdef CLIENT_DLL
  734. m_flBarrelTargetVelocity = 0;
  735. C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
  736. if ( pLocalPlayer && GetOwner() == pLocalPlayer )
  737. {
  738. IGameEvent *event = gameeventmanager->CreateEvent( "localplayer_winddown" );
  739. if ( event )
  740. {
  741. gameeventmanager->FireEventClientSide( event );
  742. }
  743. }
  744. #endif
  745. m_flStartedWindUpAt = -1.f;
  746. }
  747. //-----------------------------------------------------------------------------
  748. // Purpose:
  749. //-----------------------------------------------------------------------------
  750. void CTFMinigun::WeaponIdle()
  751. {
  752. if ( gpGlobals->curtime < m_flTimeWeaponIdle )
  753. return;
  754. // Always wind down if we've hit here, because it only happens when the player has stopped firing/spinning
  755. if ( m_iWeaponState != AC_STATE_IDLE )
  756. {
  757. CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() );
  758. if ( pPlayer )
  759. {
  760. pPlayer->DoAnimationEvent( PLAYERANIMEVENT_ATTACK_POST );
  761. }
  762. WindDown();
  763. return;
  764. }
  765. BaseClass::WeaponIdle();
  766. m_flTimeWeaponIdle = gpGlobals->curtime + 12.5;// how long till we do this again.
  767. }
  768. //-----------------------------------------------------------------------------
  769. // Purpose:
  770. //-----------------------------------------------------------------------------
  771. void CTFMinigun::FireGameEvent( IGameEvent * event )
  772. {
  773. #ifdef CLIENT_DLL
  774. C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer();
  775. if ( pLocalPlayer && GetOwner() == pLocalPlayer )
  776. {
  777. if ( FStrEq( event->GetName(), "teamplay_round_active" ) ||
  778. FStrEq( event->GetName(), "localplayer_respawn" ) )
  779. {
  780. m_nShotsFired = 0;
  781. }
  782. }
  783. BaseClass::FireGameEvent( event );
  784. #endif
  785. }
  786. //-----------------------------------------------------------------------------
  787. // Purpose:
  788. //-----------------------------------------------------------------------------
  789. bool CTFMinigun::SendWeaponAnim( int iActivity )
  790. {
  791. #ifdef CLIENT_DLL
  792. // Client procedurally animates the barrel bone
  793. if ( iActivity == ACT_MP_ATTACK_STAND_PRIMARYFIRE || iActivity == ACT_MP_ATTACK_STAND_PREFIRE )
  794. {
  795. m_flBarrelTargetVelocity = MAX_BARREL_SPIN_VELOCITY;
  796. }
  797. else if ( iActivity == ACT_MP_ATTACK_STAND_POSTFIRE )
  798. {
  799. m_flBarrelTargetVelocity = 0;
  800. }
  801. #endif
  802. // When we start firing, play the startup firing anim first
  803. if ( iActivity == ACT_VM_PRIMARYATTACK )
  804. {
  805. // If we're already playing the fire anim, let it continue. It loops.
  806. if ( GetActivity() == ACT_VM_PRIMARYATTACK )
  807. return true;
  808. // Otherwise, play the start it
  809. return BaseClass::SendWeaponAnim( ACT_VM_PRIMARYATTACK );
  810. }
  811. return BaseClass::SendWeaponAnim( iActivity );
  812. }
  813. //-----------------------------------------------------------------------------
  814. // Purpose: This will force the minigun to turn off the firing sound and play the spinning sound
  815. //-----------------------------------------------------------------------------
  816. void CTFMinigun::HandleFireOnEmpty( void )
  817. {
  818. if ( m_iWeaponState == AC_STATE_FIRING || m_iWeaponState == AC_STATE_SPINNING )
  819. {
  820. SetWeaponState( AC_STATE_DRYFIRE );
  821. SendWeaponAnim( ACT_VM_SECONDARYATTACK );
  822. if ( m_iWeaponMode == TF_WEAPON_SECONDARY_MODE )
  823. {
  824. SetWeaponState ( AC_STATE_SPINNING );
  825. }
  826. }
  827. }
  828. //-----------------------------------------------------------------------------
  829. // Purpose:
  830. //-----------------------------------------------------------------------------
  831. float CTFMinigun::GetProjectileDamage( void )
  832. {
  833. float flDamage = BaseClass::GetProjectileDamage();
  834. if ( GetFiringDuration() < TF_MINIGUN_PENALTY_PERIOD )
  835. {
  836. float flMod = 1.f;
  837. flMod = RemapValClamped( GetFiringDuration(), 0.2f, TF_MINIGUN_PENALTY_PERIOD, 0.5f, 1.f );
  838. flDamage *= flMod;
  839. }
  840. return flDamage;
  841. }
  842. //-----------------------------------------------------------------------------
  843. // Purpose:
  844. //-----------------------------------------------------------------------------
  845. float CTFMinigun::GetWeaponSpread( void )
  846. {
  847. float flSpread = BaseClass::GetWeaponSpread();
  848. // How long have we been spun up - sans the min period required to fire
  849. float flPreFireWindUp = GetWindUpDuration() - TF_MINIGUN_SPINUP_TIME;
  850. // DevMsg( "PreFireTime: %.2f\n", flPreFireWindUp );
  851. if ( GetFiringDuration() < TF_MINIGUN_PENALTY_PERIOD && flPreFireWindUp < 1.f )
  852. {
  853. // If we've spun up - prior to pressing fire - reduce accuracy penalty
  854. float flSpinTime = Max( flPreFireWindUp, GetFiringDuration() );
  855. const float flMaxSpread = 1.5f;
  856. float flMod = RemapValClamped( flSpinTime, 0.f, TF_MINIGUN_PENALTY_PERIOD, flMaxSpread, 1.f );
  857. // DevMsg( "SpreadMod: %.2f\n", flMod );
  858. flSpread *= flMod;
  859. }
  860. return flSpread;
  861. }
  862. #ifdef CLIENT_DLL
  863. //-----------------------------------------------------------------------------
  864. // Purpose:
  865. //-----------------------------------------------------------------------------
  866. CStudioHdr *CTFMinigun::OnNewModel( void )
  867. {
  868. CStudioHdr *hdr = BaseClass::OnNewModel();
  869. m_iBarrelBone = LookupBone( "barrel" );
  870. // skip resetting this while recording in the tool
  871. // we change the weapon to the worldmodel and back to the viewmodel when recording
  872. // which causes the minigun to not spin while recording
  873. if ( !IsToolRecording() )
  874. {
  875. m_flBarrelAngle = 0;
  876. m_flBarrelCurrentVelocity = 0;
  877. m_flBarrelTargetVelocity = 0;
  878. }
  879. return hdr;
  880. }
  881. //-----------------------------------------------------------------------------
  882. // Purpose:
  883. //-----------------------------------------------------------------------------
  884. void CTFMinigun::StandardBlendingRules( CStudioHdr *hdr, Vector pos[], Quaternion q[], float currentTime, int boneMask )
  885. {
  886. BaseClass::StandardBlendingRules( hdr, pos, q, currentTime, boneMask );
  887. if (m_iBarrelBone != -1)
  888. {
  889. UpdateBarrelMovement();
  890. // Weapon happens to be aligned to (0,0,0)
  891. // If that changes, use this code block instead to
  892. // modify the angles
  893. /*
  894. RadianEuler a;
  895. QuaternionAngles( q[iBarrelBone], a );
  896. a.x = m_flBarrelAngle;
  897. AngleQuaternion( a, q[iBarrelBone] );
  898. */
  899. AngleQuaternion( RadianEuler( 0, 0, m_flBarrelAngle ), q[m_iBarrelBone] );
  900. }
  901. }
  902. //-----------------------------------------------------------------------------
  903. // Purpose: Updates the velocity and position of the rotating barrel
  904. //-----------------------------------------------------------------------------
  905. void CTFMinigun::UpdateBarrelMovement()
  906. {
  907. if ( m_flBarrelCurrentVelocity != m_flBarrelTargetVelocity )
  908. {
  909. float flBarrelAcceleration = CanHolsterWhileSpinning() ? 0.5f : 0.1f;
  910. // update barrel velocity to bring it up to speed or to rest
  911. m_flBarrelCurrentVelocity = Approach( m_flBarrelTargetVelocity, m_flBarrelCurrentVelocity, flBarrelAcceleration );
  912. if ( 0 == m_flBarrelCurrentVelocity )
  913. {
  914. // if we've stopped rotating, turn off the wind-down sound
  915. WeaponSoundUpdate();
  916. }
  917. }
  918. // update the barrel rotation based on current velocity
  919. m_flBarrelAngle += m_flBarrelCurrentVelocity * gpGlobals->frametime;
  920. }
  921. //-----------------------------------------------------------------------------
  922. // Purpose:
  923. //-----------------------------------------------------------------------------
  924. void CTFMinigun::OnDataChanged( DataUpdateType_t updateType )
  925. {
  926. // Brass ejection and muzzle flash.
  927. HandleBrassEffect();
  928. HandleMuzzleEffect();
  929. BaseClass::OnDataChanged( updateType );
  930. WeaponSoundUpdate();
  931. // Turn off the firing sound here for the Tomislav
  932. if( m_iPrevMinigunState == AC_STATE_FIRING &&
  933. ( m_iWeaponState == AC_STATE_SPINNING || m_iWeaponState == AC_STATE_IDLE ) )
  934. {
  935. if ( !HasSpinSounds() )
  936. {
  937. PlayStopFiringSound();
  938. }
  939. }
  940. m_iPrevMinigunState = m_iWeaponState;
  941. }
  942. //-----------------------------------------------------------------------------
  943. // Purpose:
  944. //-----------------------------------------------------------------------------
  945. void CTFMinigun::UpdateOnRemove( void )
  946. {
  947. if ( m_pSoundCur )
  948. {
  949. CSoundEnvelopeController::GetController().SoundDestroy( m_pSoundCur );
  950. m_pSoundCur = NULL;
  951. }
  952. // Force the particle system off.
  953. StopMuzzleEffect();
  954. StopBrassEffect();
  955. BaseClass::UpdateOnRemove();
  956. }
  957. //-----------------------------------------------------------------------------
  958. // Purpose:
  959. //-----------------------------------------------------------------------------
  960. void CTFMinigun::SetDormant( bool bDormant )
  961. {
  962. // If I'm going from active to dormant and I'm carried by another player, stop our firing sound.
  963. if ( !IsCarriedByLocalPlayer() )
  964. {
  965. // Am I firing? Stop the firing sound.
  966. if ( !IsDormant() && bDormant && m_iWeaponState >= AC_STATE_FIRING )
  967. {
  968. WeaponSoundUpdate();
  969. }
  970. // If firing and going dormant - stop the brass effect.
  971. if ( !IsDormant() && bDormant && m_iWeaponState != AC_STATE_IDLE )
  972. {
  973. StopMuzzleEffect();
  974. StopBrassEffect();
  975. }
  976. }
  977. // Deliberately skip base combat weapon
  978. C_BaseEntity::SetDormant( bDormant );
  979. }
  980. //-----------------------------------------------------------------------------
  981. // Purpose:
  982. // won't be called for w_ version of the model, so this isn't getting updated twice
  983. //-----------------------------------------------------------------------------
  984. void CTFMinigun::ItemPreFrame( void )
  985. {
  986. UpdateBarrelMovement();
  987. BaseClass::ItemPreFrame();
  988. }
  989. //-----------------------------------------------------------------------------
  990. // Purpose:
  991. //-----------------------------------------------------------------------------
  992. void CTFMinigun::StartBrassEffect()
  993. {
  994. StopBrassEffect();
  995. m_hEjectBrassWeapon = GetWeaponForEffect();
  996. if ( !m_hEjectBrassWeapon )
  997. return;
  998. if ( UsingViewModel() && !g_pClientMode->ShouldDrawViewModel() )
  999. {
  1000. // Prevent effects when the ViewModel is hidden with r_drawviewmodel=0
  1001. return;
  1002. }
  1003. // Try and setup the attachment point if it doesn't already exist.
  1004. // This caching will mess up if we go third person from first - we only do this in taunts and don't fire so we should
  1005. // be okay for now.
  1006. if ( m_iEjectBrassAttachment == -1 )
  1007. {
  1008. m_iEjectBrassAttachment = m_hEjectBrassWeapon->LookupAttachment( "eject_brass" );
  1009. }
  1010. // Start the brass ejection, if a system hasn't already been started.
  1011. if ( m_iEjectBrassAttachment > 0 && m_pEjectBrassEffect == NULL )
  1012. {
  1013. m_pEjectBrassEffect = m_hEjectBrassWeapon->ParticleProp()->Create( "eject_minigunbrass", PATTACH_POINT_FOLLOW, m_iEjectBrassAttachment );
  1014. }
  1015. }
  1016. //-----------------------------------------------------------------------------
  1017. // Purpose:
  1018. //-----------------------------------------------------------------------------
  1019. void CTFMinigun::StartMuzzleEffect()
  1020. {
  1021. StopMuzzleEffect();
  1022. m_hMuzzleEffectWeapon = GetWeaponForEffect();
  1023. if ( !m_hMuzzleEffectWeapon )
  1024. return;
  1025. if ( UsingViewModel() && !g_pClientMode->ShouldDrawViewModel() )
  1026. {
  1027. // Prevent effects when the ViewModel is hidden with r_drawviewmodel=0
  1028. return;
  1029. }
  1030. // Try and setup the attachment point if it doesn't already exist.
  1031. // This caching will mess up if we go third person from first - we only do this in taunts and don't fire so we should
  1032. // be okay for now.
  1033. if ( m_iMuzzleAttachment <= 0 )
  1034. {
  1035. m_iMuzzleAttachment = m_hMuzzleEffectWeapon->LookupAttachment( "muzzle" );
  1036. }
  1037. // Start the muzzle flash, if a system hasn't already been started.
  1038. if ( m_iMuzzleAttachment > 0 && m_pMuzzleEffect == NULL )
  1039. {
  1040. m_pMuzzleEffect = m_hMuzzleEffectWeapon->ParticleProp()->Create( "muzzle_minigun_constant", PATTACH_POINT_FOLLOW, m_iMuzzleAttachment );
  1041. }
  1042. }
  1043. //-----------------------------------------------------------------------------
  1044. // Purpose:
  1045. //-----------------------------------------------------------------------------
  1046. void CTFMinigun::StopBrassEffect()
  1047. {
  1048. if ( !m_hEjectBrassWeapon )
  1049. return;
  1050. // Stop the brass ejection.
  1051. if ( m_pEjectBrassEffect )
  1052. {
  1053. m_hEjectBrassWeapon->ParticleProp()->StopEmission( m_pEjectBrassEffect );
  1054. m_hEjectBrassWeapon = NULL;
  1055. m_pEjectBrassEffect = NULL;
  1056. }
  1057. }
  1058. //-----------------------------------------------------------------------------
  1059. // Purpose:
  1060. //-----------------------------------------------------------------------------
  1061. void CTFMinigun::StopMuzzleEffect()
  1062. {
  1063. if ( !m_hMuzzleEffectWeapon )
  1064. return;
  1065. // Stop the muzzle flash.
  1066. if ( m_pMuzzleEffect )
  1067. {
  1068. m_hMuzzleEffectWeapon->ParticleProp()->StopEmission( m_pMuzzleEffect );
  1069. m_hMuzzleEffectWeapon = NULL;
  1070. m_pMuzzleEffect = NULL;
  1071. }
  1072. }
  1073. //-----------------------------------------------------------------------------
  1074. // Purpose:
  1075. //-----------------------------------------------------------------------------
  1076. void CTFMinigun::HandleBrassEffect()
  1077. {
  1078. if ( m_iWeaponState == AC_STATE_FIRING && m_pEjectBrassEffect == NULL )
  1079. {
  1080. StartBrassEffect();
  1081. }
  1082. else if ( m_iWeaponState != AC_STATE_FIRING && m_pEjectBrassEffect )
  1083. {
  1084. StopBrassEffect();
  1085. }
  1086. }
  1087. //-----------------------------------------------------------------------------
  1088. // Purpose:
  1089. //-----------------------------------------------------------------------------
  1090. void CTFMinigun::HandleMuzzleEffect()
  1091. {
  1092. if ( m_iWeaponState == AC_STATE_FIRING && m_pMuzzleEffect == NULL )
  1093. {
  1094. StartMuzzleEffect();
  1095. }
  1096. else if ( m_iWeaponState != AC_STATE_FIRING && m_pMuzzleEffect )
  1097. {
  1098. StopMuzzleEffect();
  1099. }
  1100. }
  1101. //-----------------------------------------------------------------------------
  1102. // Purpose: View model barrel rotation angle. Calculated here, implemented in
  1103. // tf_viewmodel.cpp
  1104. //-----------------------------------------------------------------------------
  1105. float CTFMinigun::GetBarrelRotation( void )
  1106. {
  1107. return m_flBarrelAngle;
  1108. }
  1109. //-----------------------------------------------------------------------------
  1110. // Purpose:
  1111. //-----------------------------------------------------------------------------
  1112. void CTFMinigun::ViewModelAttachmentBlending( CStudioHdr *hdr, Vector pos[], Quaternion q[], float currentTime, int boneMask )
  1113. {
  1114. int iBarrelBone = Studio_BoneIndexByName( hdr, "barrel" );
  1115. // Assert( iBarrelBone != -1 );
  1116. if ( iBarrelBone != -1 )
  1117. {
  1118. if ( hdr->boneFlags( iBarrelBone ) & boneMask )
  1119. {
  1120. RadianEuler a;
  1121. QuaternionAngles( q[iBarrelBone], a );
  1122. a.z = GetBarrelRotation();
  1123. AngleQuaternion( a, q[iBarrelBone] );
  1124. }
  1125. }
  1126. }
  1127. //-----------------------------------------------------------------------------
  1128. // Purpose:
  1129. //-----------------------------------------------------------------------------
  1130. void CTFMinigun::CreateMove( float flInputSampleTime, CUserCmd *pCmd, const QAngle &vecOldViewAngles )
  1131. {
  1132. // Prevent jumping while firing
  1133. if ( m_iWeaponState != AC_STATE_IDLE )
  1134. {
  1135. pCmd->buttons &= ~IN_JUMP;
  1136. }
  1137. BaseClass::CreateMove( flInputSampleTime, pCmd, vecOldViewAngles );
  1138. }
  1139. //-----------------------------------------------------------------------------
  1140. // Purpose: Ensures the correct sound (including silence) is playing for
  1141. // current weapon state.
  1142. //-----------------------------------------------------------------------------
  1143. void CTFMinigun::WeaponSoundUpdate()
  1144. {
  1145. // determine the desired sound for our current state
  1146. int iSound = -1;
  1147. switch ( m_iWeaponState )
  1148. {
  1149. case AC_STATE_IDLE:
  1150. if ( !HasSpinSounds() && m_iMinigunSoundCur == SPECIAL2 )
  1151. {
  1152. // Don't turn off SPECIAL2 (stop firing sound) for non spinning miniguns.
  1153. // We don't have a wind-down sound.
  1154. return;
  1155. }
  1156. else if ( HasSpinSounds() && m_flBarrelCurrentVelocity > 0 )
  1157. {
  1158. iSound = SPECIAL2; // wind down sound
  1159. if ( m_flBarrelTargetVelocity > 0 )
  1160. {
  1161. m_flBarrelTargetVelocity = 0;
  1162. }
  1163. }
  1164. else
  1165. {
  1166. iSound = -1;
  1167. }
  1168. break;
  1169. case AC_STATE_STARTFIRING:
  1170. iSound = SPECIAL1; // wind up sound
  1171. break;
  1172. case AC_STATE_FIRING:
  1173. {
  1174. if ( m_bCritShot == true )
  1175. {
  1176. iSound = BURST; // Crit sound
  1177. }
  1178. else
  1179. {
  1180. iSound = WPN_DOUBLE; // firing sound
  1181. }
  1182. }
  1183. break;
  1184. case AC_STATE_SPINNING:
  1185. if ( HasSpinSounds() )
  1186. iSound = SPECIAL3; // spinning sound
  1187. else
  1188. return;
  1189. break;
  1190. case AC_STATE_DRYFIRE:
  1191. iSound = EMPTY; // out of ammo, still trying to fire
  1192. break;
  1193. default:
  1194. Assert( false );
  1195. break;
  1196. }
  1197. // Get the pitch we should play at
  1198. float flPitch = 1.0f;
  1199. float flSpeed = ApplyFireDelay( 1.0f );
  1200. if ( flSpeed != 1.0f )
  1201. {
  1202. flPitch = RemapValClamped( flSpeed, 1.5f, 0.5f, 80.f, 120.f );
  1203. }
  1204. if ( m_bRageDraining )
  1205. {
  1206. flPitch /= 1.65;
  1207. }
  1208. CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
  1209. // if we're already playing the desired sound, nothing to do
  1210. if ( m_iMinigunSoundCur == iSound && m_bPrevRageDraining == m_bRageDraining )
  1211. {
  1212. // If the pitch is different we need to modify it
  1213. if ( m_flMinigunSoundCurrentPitch != flPitch )
  1214. {
  1215. m_flMinigunSoundCurrentPitch = flPitch;
  1216. if ( m_pSoundCur )
  1217. {
  1218. controller.SoundChangePitch( m_pSoundCur, m_flMinigunSoundCurrentPitch, 0.3f );
  1219. }
  1220. }
  1221. return;
  1222. }
  1223. // if we're playing some other sound, stop it
  1224. if ( m_pSoundCur )
  1225. {
  1226. // Stop the previous sound immediately
  1227. CSoundEnvelopeController::GetController().SoundDestroy( m_pSoundCur );
  1228. m_pSoundCur = NULL;
  1229. }
  1230. m_iMinigunSoundCur = iSound;
  1231. // if there's no sound to play for current state, we're done
  1232. if ( -1 == iSound )
  1233. return;
  1234. m_flMinigunSoundCurrentPitch = flPitch;
  1235. // play the appropriate sound
  1236. const char *shootsound = GetShootSound( iSound );
  1237. CLocalPlayerFilter filter;
  1238. m_pSoundCur = controller.SoundCreate( filter, entindex(), shootsound );
  1239. controller.Play( m_pSoundCur, 1.0, 100 );
  1240. controller.SoundChangeVolume( m_pSoundCur, 1.0, 0.1 );
  1241. if ( m_flMinigunSoundCurrentPitch != 1.0f )
  1242. {
  1243. controller.SoundChangePitch( m_pSoundCur, m_flMinigunSoundCurrentPitch, 0.0 );
  1244. }
  1245. m_bPrevRageDraining = m_bRageDraining;
  1246. }
  1247. void CTFMinigun::PlayStopFiringSound()
  1248. {
  1249. if ( m_pSoundCur )
  1250. {
  1251. CSoundEnvelopeController::GetController().SoundDestroy( m_pSoundCur );
  1252. m_pSoundCur = NULL;
  1253. }
  1254. m_iMinigunSoundCur = SPECIAL2;
  1255. CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
  1256. const char *shootsound = GetShootSound( SPECIAL2 );
  1257. CLocalPlayerFilter filter;
  1258. m_pSoundCur = controller.SoundCreate( filter, entindex(), shootsound );
  1259. controller.Play( m_pSoundCur, 1.0, 100 );
  1260. controller.SoundChangeVolume( m_pSoundCur, 1.0, 0.1 );
  1261. }
  1262. #endif