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.

718 lines
19 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================
  6. #include "cbase.h"
  7. #include "tf_weapon_grenadelauncher.h"
  8. #include "tf_fx_shared.h"
  9. #include "tf_weapon_grenade_pipebomb.h"
  10. #include "tf_gamerules.h"
  11. #include "in_buttons.h"
  12. #include "tf_weaponbase_gun.h"
  13. // Client specific.
  14. #ifdef CLIENT_DLL
  15. #include "c_tf_player.h"
  16. #include "c_tf_gamestats.h"
  17. #include "bone_setup.h"
  18. // Server specific.
  19. #else
  20. #include "tf_player.h"
  21. #include "tf_gamestats.h"
  22. #include "tf_fx.h"
  23. #endif
  24. ConVar tf_double_donk_window( "tf_double_donk_window", "0.5", FCVAR_CHEAT | FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY, "How long after an impact from a cannonball that an explosion will count as a double-donk." );
  25. #define TF_TUBE_COUNT 6
  26. // X is time as a fraction of cProceduralBarrelRotationTime, which is in seconds.
  27. // Y is rotation in degrees
  28. // Z is slope at Y.
  29. // These are hermite spline control points that match maya.
  30. const Vector cProceduralBarrelRotationAnimationPoints[] =
  31. {
  32. Vector( 0, 0, 0 ),
  33. Vector( 0.7519f, 63.546f, 0 ),
  34. Vector( 1.0f, 60, 0 )
  35. };
  36. static_assert( ARRAYSIZE( cProceduralBarrelRotationAnimationPoints ) > 1, "cProceduralBarrelRotationAnimationPoints must have at least two elements." );
  37. const float cProceduralBarrelRotationTime = 0.2666f;
  38. //=============================================================================
  39. //
  40. // Weapon Grenade Launcher tables.
  41. //
  42. IMPLEMENT_NETWORKCLASS_ALIASED( TFGrenadeLauncher, DT_WeaponGrenadeLauncher )
  43. BEGIN_NETWORK_TABLE( CTFGrenadeLauncher, DT_WeaponGrenadeLauncher )
  44. #ifdef CLIENT_DLL
  45. RecvPropFloat( RECVINFO( m_flDetonateTime ) ),
  46. RecvPropInt( RECVINFO( m_iCurrentTube ) ),
  47. RecvPropInt( RECVINFO( m_iGoalTube ) ),
  48. #else
  49. SendPropFloat( SENDINFO( m_flDetonateTime ) ),
  50. SendPropInt( SENDINFO( m_iCurrentTube ) ),
  51. SendPropInt( SENDINFO( m_iGoalTube ) ),
  52. #endif
  53. END_NETWORK_TABLE()
  54. #ifdef CLIENT_DLL
  55. BEGIN_PREDICTION_DATA( CTFGrenadeLauncher )
  56. DEFINE_FIELD( m_flDetonateTime, FIELD_FLOAT ),
  57. DEFINE_FIELD( m_iCurrentTube, FIELD_INTEGER ),
  58. DEFINE_FIELD( m_iGoalTube, FIELD_INTEGER )
  59. END_PREDICTION_DATA()
  60. #endif
  61. LINK_ENTITY_TO_CLASS( tf_weapon_grenadelauncher, CTFGrenadeLauncher );
  62. PRECACHE_WEAPON_REGISTER( tf_weapon_grenadelauncher );
  63. CREATE_SIMPLE_WEAPON_TABLE( TFCannon, tf_weapon_cannon )
  64. // Server specific.
  65. #ifndef CLIENT_DLL
  66. BEGIN_DATADESC( CTFGrenadeLauncher )
  67. END_DATADESC()
  68. #endif
  69. #define TF_GRENADE_LAUNCER_MIN_VEL 1200
  70. #define TF_DETONATE_MODE_AIR 2
  71. #define TF_WEAPON_CANNON_CHARGE_SOUND "Weapon_LooseCannon.Charge"
  72. //=============================================================================
  73. //
  74. // Weapon Grenade Launcher functions.
  75. //
  76. //-----------------------------------------------------------------------------
  77. // Purpose:
  78. // Input : -
  79. //-----------------------------------------------------------------------------
  80. CTFGrenadeLauncher::CTFGrenadeLauncher()
  81. {
  82. m_bReloadsSingly = true;
  83. #ifdef CLIENT_DLL
  84. m_pCannonFuseSparkEffect = NULL;
  85. m_pCannonCharge = NULL;
  86. #endif // CLIENT_DLL
  87. }
  88. //-----------------------------------------------------------------------------
  89. // Purpose:
  90. // Input : -
  91. //-----------------------------------------------------------------------------
  92. CTFGrenadeLauncher::~CTFGrenadeLauncher()
  93. {
  94. }
  95. //-----------------------------------------------------------------------------
  96. // Purpose:
  97. //-----------------------------------------------------------------------------
  98. void CTFGrenadeLauncher::Spawn( void )
  99. {
  100. m_iAltFireHint = HINT_ALTFIRE_GRENADELAUNCHER;
  101. BaseClass::Spawn();
  102. ResetDetonateTime();
  103. }
  104. //-----------------------------------------------------------------------------
  105. // Purpose: Reset the charge when we holster
  106. //-----------------------------------------------------------------------------
  107. bool CTFGrenadeLauncher::Holster( CBaseCombatWeapon *pSwitchingTo )
  108. {
  109. ResetDetonateTime();
  110. return BaseClass::Holster( pSwitchingTo );
  111. }
  112. //-----------------------------------------------------------------------------
  113. // Purpose: Reset the charge when we deploy
  114. //-----------------------------------------------------------------------------
  115. bool CTFGrenadeLauncher::Deploy( void )
  116. {
  117. ResetDetonateTime();
  118. return BaseClass::Deploy();
  119. }
  120. //-----------------------------------------------------------------------------
  121. // Purpose:
  122. //-----------------------------------------------------------------------------
  123. int CTFGrenadeLauncher::GetMaxClip1( void ) const
  124. {
  125. #ifdef _X360
  126. return TF_GRENADE_LAUNCHER_XBOX_CLIP;
  127. #endif
  128. return BaseClass::GetMaxClip1();
  129. }
  130. //-----------------------------------------------------------------------------
  131. // Purpose:
  132. //-----------------------------------------------------------------------------
  133. int CTFGrenadeLauncher::GetDefaultClip1( void ) const
  134. {
  135. #ifdef _X360
  136. return TF_GRENADE_LAUNCHER_XBOX_CLIP;
  137. #endif
  138. return BaseClass::GetDefaultClip1();
  139. }
  140. //-----------------------------------------------------------------------------
  141. // Purpose:
  142. //-----------------------------------------------------------------------------
  143. void CTFGrenadeLauncher::PrimaryAttack( void )
  144. {
  145. // Check for ammunition.
  146. if ( m_iClip1 <= 0 && m_iClip1 != -1 )
  147. return;
  148. // Are we capable of firing again?
  149. if ( m_flNextPrimaryAttack > gpGlobals->curtime )
  150. return;
  151. if ( !CanAttack() )
  152. {
  153. ResetDetonateTime();
  154. return;
  155. }
  156. m_iWeaponMode = TF_WEAPON_PRIMARY_MODE;
  157. if ( CanCharge() )
  158. {
  159. if ( m_flDetonateTime == 0.f )
  160. {
  161. m_flDetonateTime = gpGlobals->curtime + GetMortarDetonateTimeLength();
  162. SendWeaponAnim( ACT_VM_PULLBACK );
  163. #ifdef CLIENT_DLL
  164. EmitSound( TF_WEAPON_CANNON_CHARGE_SOUND );
  165. #endif // CLIENT_DLL
  166. }
  167. else
  168. {
  169. #ifdef CLIENT_DLL
  170. StartChargeEffects();
  171. #endif // CLIENT_DLL
  172. }
  173. }
  174. else
  175. {
  176. LaunchGrenade();
  177. }
  178. }
  179. //-----------------------------------------------------------------------------
  180. // Purpose:
  181. //-----------------------------------------------------------------------------
  182. void CTFGrenadeLauncher::ItemPostFrame( void )
  183. {
  184. BaseClass::ItemPostFrame();
  185. if ( m_flDetonateTime > 0.f )
  186. {
  187. if ( m_flDetonateTime > gpGlobals->curtime )
  188. {
  189. CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() );
  190. if ( !pPlayer )
  191. return;
  192. // If we're not holding down the attack button, launch our grenade
  193. if ( m_iClip1 > 0 && !(pPlayer->m_nButtons & IN_ATTACK) )
  194. {
  195. LaunchGrenade();
  196. }
  197. }
  198. else
  199. {
  200. Misfire();
  201. }
  202. }
  203. }
  204. //-----------------------------------------------------------------------------
  205. // Purpose:
  206. //-----------------------------------------------------------------------------
  207. void CTFGrenadeLauncher::Misfire( void )
  208. {
  209. BaseClass::Misfire();
  210. LaunchGrenade();
  211. }
  212. //-----------------------------------------------------------------------------
  213. // Purpose:
  214. //-----------------------------------------------------------------------------
  215. void CTFGrenadeLauncher::WeaponIdle( void )
  216. {
  217. BaseClass::WeaponIdle();
  218. }
  219. //-----------------------------------------------------------------------------
  220. // Purpose:
  221. //-----------------------------------------------------------------------------
  222. void CTFGrenadeLauncher::FireProjectileInternal( CTFPlayer* pTFPlayer )
  223. {
  224. #ifdef GAME_DLL
  225. CTFGrenadePipebombProjectile *pProjectile = static_cast<CTFGrenadePipebombProjectile*>( FireProjectile( pTFPlayer ) );
  226. if ( pProjectile )
  227. {
  228. if ( GetDetonateMode() == TF_DETONATE_MODE_AIR )
  229. {
  230. pProjectile->m_bWallShatter = true;
  231. }
  232. if ( m_flDetonateTime > 0.f )
  233. {
  234. float flDetonateTimeLength = ( gpGlobals->curtime - GetChargeBeginTime() );
  235. pProjectile->SetDetonateTimerLength( flDetonateTimeLength );
  236. if ( flDetonateTimeLength == 0.f )
  237. {
  238. trace_t tr;
  239. UTIL_TraceLine( pProjectile->GetAbsOrigin(), pTFPlayer->EyePosition(), MASK_SOLID, pProjectile, COLLISION_GROUP_NONE, &tr );
  240. pProjectile->Explode( &tr, GetDamageType() );
  241. }
  242. }
  243. float flDetonationPenalty = 1.0f;
  244. CALL_ATTRIB_HOOK_FLOAT( flDetonationPenalty, grenade_detonation_damage_penalty );
  245. if ( flDetonationPenalty != 1.0f )
  246. {
  247. // Setting the initial damage of a grenade lower will set its fused time damage lower
  248. // on contact detonations reset the damage to max
  249. pProjectile->SetDamage( pProjectile->GetDamage() * flDetonationPenalty );
  250. }
  251. }
  252. #else
  253. FireProjectile( pTFPlayer );
  254. #endif
  255. }
  256. //-----------------------------------------------------------------------------
  257. // Purpose:
  258. //-----------------------------------------------------------------------------
  259. void CTFGrenadeLauncher::WeaponReset( void )
  260. {
  261. BaseClass::WeaponReset();
  262. ResetDetonateTime();
  263. m_iCurrentTube = 0;
  264. m_iGoalTube = 0;
  265. m_bCurrentAndGoalTubeEqual = true;
  266. }
  267. //-----------------------------------------------------------------------------
  268. // Purpose:
  269. //-----------------------------------------------------------------------------
  270. bool CTFGrenadeLauncher::SendWeaponAnim( int iActivity )
  271. {
  272. // Client procedurally animates the barrel bone
  273. if ( iActivity == ACT_VM_PRIMARYATTACK )
  274. {
  275. m_iGoalTube = ( m_iCurrentTube + 1 ) % TF_TUBE_COUNT;
  276. m_flBarrelRotateBeginTime = gpGlobals->curtime;
  277. }
  278. // When we start firing, play the startup firing anim first
  279. if ( iActivity == ACT_VM_PRIMARYATTACK )
  280. {
  281. // If we're already playing the fire anim, let it continue. It loops.
  282. if ( GetActivity() == ACT_VM_PRIMARYATTACK )
  283. return true;
  284. // Otherwise, play the start it
  285. return BaseClass::SendWeaponAnim( ACT_VM_PRIMARYATTACK );
  286. }
  287. return BaseClass::SendWeaponAnim( iActivity );
  288. }
  289. //-----------------------------------------------------------------------------
  290. // Purpose:
  291. //-----------------------------------------------------------------------------
  292. void CTFGrenadeLauncher::PostFire()
  293. {
  294. // Set next attack times.
  295. float flFireDelay = ApplyFireDelay( m_pWeaponInfo->GetWeaponData( m_iWeaponMode ).m_flTimeFireDelay );
  296. m_flNextPrimaryAttack = gpGlobals->curtime + flFireDelay;
  297. SetWeaponIdleTime( gpGlobals->curtime + SequenceDuration() );
  298. // Check the reload mode and behave appropriately.
  299. if ( m_bReloadsSingly )
  300. {
  301. m_iReloadMode.Set( TF_RELOAD_START );
  302. }
  303. #ifndef CLIENT_DLL
  304. if ( CanCharge() )
  305. {
  306. Vector vPosition;
  307. QAngle qAngles;
  308. if ( GetAttachment( "muzzle", vPosition, qAngles ) )
  309. {
  310. CPVSFilter filter( vPosition );
  311. TE_TFParticleEffect( filter, 0.f, "loose_cannon_bang", PATTACH_POINT, this, "muzzle" );
  312. }
  313. }
  314. #endif
  315. ResetDetonateTime();
  316. }
  317. //-----------------------------------------------------------------------------
  318. // Purpose:
  319. //-----------------------------------------------------------------------------
  320. void CTFGrenadeLauncher::LaunchGrenade( void )
  321. {
  322. // Get the player owning the weapon.
  323. CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() );
  324. if ( !pPlayer )
  325. return;
  326. CalcIsAttackCritical();
  327. SendWeaponAnim( ACT_VM_PRIMARYATTACK );
  328. pPlayer->SetAnimation( PLAYER_ATTACK1 );
  329. pPlayer->DoAnimationEvent( PLAYERANIMEVENT_ATTACK_PRIMARY );
  330. if ( !AutoFiresFullClipAllAtOnce() )
  331. {
  332. FireProjectileInternal( pPlayer );
  333. }
  334. else
  335. {
  336. int nCurrentClipSize = m_iClip1;
  337. m_nLauncherSlot = 0;
  338. int iSeed = CBaseEntity::GetPredictionRandomSeed() & 255;
  339. QAngle punchAngle = pPlayer->GetPunchAngle();
  340. for ( int i=0; i<nCurrentClipSize; ++i, ++iSeed )
  341. {
  342. RandomSeed( iSeed );
  343. FireProjectileInternal( pPlayer );
  344. if ( i == 0 )
  345. {
  346. punchAngle = pPlayer->GetPunchAngle();
  347. }
  348. }
  349. pPlayer->SetPunchAngle( punchAngle );
  350. }
  351. #ifdef CLIENT_DLL
  352. C_CTF_GameStats.Event_PlayerFiredWeapon( pPlayer, IsCurrentAttackACrit() );
  353. StopSound( TF_WEAPON_CANNON_CHARGE_SOUND );
  354. #else
  355. pPlayer->SpeakWeaponFire();
  356. CTF_GameStats.Event_PlayerFiredWeapon( pPlayer, IsCurrentAttackACrit() );
  357. #endif
  358. PostFire();
  359. if ( TFGameRules()->GameModeUsesUpgrades() )
  360. {
  361. PlayUpgradedShootSound( "Weapon_Upgrade.DamageBonus" );
  362. }
  363. }
  364. void CTFGrenadeLauncher::AddDonkVictim( const CBaseEntity* pVictim )
  365. {
  366. // Clear out old donk victims
  367. FOR_EACH_VEC_BACK( m_vecDonkVictims, i )
  368. {
  369. if( m_vecDonkVictims[i].m_flExpireTime <= gpGlobals->curtime )
  370. {
  371. m_vecDonkVictims.Remove( i );
  372. }
  373. }
  374. // Add new donk victim
  375. Donks_t& donk = m_vecDonkVictims[ m_vecDonkVictims.AddToTail() ];
  376. donk.m_hVictim.Set( pVictim );
  377. donk.m_flExpireTime = gpGlobals->curtime + tf_double_donk_window.GetFloat();
  378. }
  379. bool CTFGrenadeLauncher::IsDoubleDonk( const CBaseEntity* pVictim ) const
  380. {
  381. if( GetWeaponID() != TF_WEAPON_CANNON )
  382. return false;
  383. // Check each donk victim to see if we've donked them recently enough to
  384. // score a "double-donk"
  385. FOR_EACH_VEC( m_vecDonkVictims, i )
  386. {
  387. if( gpGlobals->curtime < m_vecDonkVictims[i].m_flExpireTime && m_vecDonkVictims[i].m_hVictim.Get() == pVictim )
  388. {
  389. return true;
  390. }
  391. }
  392. return false;
  393. }
  394. float CTFGrenadeLauncher::GetProjectileSpeed( void )
  395. {
  396. CTFPlayer *pOwner = ToTFPlayer( GetOwner() );
  397. if ( pOwner && pOwner->m_Shared.GetCarryingRuneType() == RUNE_PRECISION )
  398. return 3000.f;
  399. float flLaunchSpeed = TF_GRENADE_LAUNCER_MIN_VEL;
  400. CALL_ATTRIB_HOOK_FLOAT( flLaunchSpeed, mult_projectile_speed );
  401. return flLaunchSpeed;
  402. }
  403. int CTFGrenadeLauncher::GetDetonateMode( void ) const
  404. {
  405. int iMode = 0;
  406. CALL_ATTRIB_HOOK_INT( iMode, set_detonate_mode );
  407. return iMode;
  408. }
  409. //-----------------------------------------------------------------------------
  410. // Purpose: Detonate this demoman's pipebombs
  411. //-----------------------------------------------------------------------------
  412. void CTFGrenadeLauncher::SecondaryAttack( void )
  413. {
  414. #ifdef GAME_DLL
  415. if ( !CanAttack() )
  416. return;
  417. CTFPlayer *pOwner = ToTFPlayer( GetOwner() );
  418. pOwner->DoClassSpecialSkill();
  419. #endif
  420. }
  421. bool CTFGrenadeLauncher::Reload( void )
  422. {
  423. return BaseClass::Reload();
  424. }
  425. void CTFGrenadeLauncher::FireFullClipAtOnce( void )
  426. {
  427. m_iWeaponMode = TF_WEAPON_PRIMARY_MODE;
  428. LaunchGrenade();
  429. }
  430. bool CTFGrenadeLauncher::CanCharge( void )
  431. {
  432. if ( GetWeaponID() == TF_WEAPON_CANNON )
  433. {
  434. return GetMortarDetonateTimeLength() > 0.f;
  435. }
  436. return false;
  437. }
  438. float CTFGrenadeLauncher::GetChargeBeginTime( void )
  439. {
  440. // Inverse begin time logic to get charge bar to decrease from a full bar instead of increase from an empty bar
  441. float flMortarDetonateTimeLength = GetMortarDetonateTimeLength();
  442. float flModDetonateTimeLength = flMortarDetonateTimeLength;
  443. if ( m_flDetonateTime > 0.f )
  444. {
  445. flModDetonateTimeLength = Clamp( m_flDetonateTime - gpGlobals->curtime, 0.f, flMortarDetonateTimeLength );
  446. }
  447. return gpGlobals->curtime - flModDetonateTimeLength;
  448. }
  449. float CTFGrenadeLauncher::GetChargeMaxTime( void )
  450. {
  451. return GetMortarDetonateTimeLength();
  452. }
  453. void CTFGrenadeLauncher::ResetDetonateTime()
  454. {
  455. m_flDetonateTime = 0.f;
  456. #ifdef CLIENT_DLL
  457. StopChargeEffects();
  458. #endif // CLIENT_DLL
  459. }
  460. float CTFGrenadeLauncher::GetMortarDetonateTimeLength()
  461. {
  462. float flMortarDetonateTimeLength = 0.f;
  463. CALL_ATTRIB_HOOK_FLOAT( flMortarDetonateTimeLength, grenade_launcher_mortar_mode );
  464. return flMortarDetonateTimeLength;
  465. }
  466. #ifdef CLIENT_DLL
  467. void CTFGrenadeLauncher::StartChargeEffects()
  468. {
  469. if ( !m_pCannonFuseSparkEffect )
  470. {
  471. m_pCannonFuseSparkEffect = ParticleProp()->Create( "loose_cannon_sparks", PATTACH_POINT_FOLLOW, "cannon_fuse" );
  472. }
  473. if ( !m_pCannonCharge )
  474. {
  475. m_pCannonCharge = ParticleProp()->Create( "loose_cannon_buildup_smoke3", PATTACH_POINT_FOLLOW, "muzzle" );
  476. }
  477. }
  478. void CTFGrenadeLauncher::StopChargeEffects()
  479. {
  480. if ( m_pCannonFuseSparkEffect )
  481. {
  482. ParticleProp()->StopEmission( m_pCannonFuseSparkEffect );
  483. m_pCannonFuseSparkEffect = NULL;
  484. }
  485. if ( m_pCannonCharge )
  486. {
  487. ParticleProp()->StopEmission( m_pCannonCharge );
  488. m_pCannonCharge = NULL;
  489. }
  490. }
  491. //-----------------------------------------------------------------------------
  492. // Purpose:
  493. //-----------------------------------------------------------------------------
  494. CStudioHdr *CTFGrenadeLauncher::OnNewModel( void )
  495. {
  496. CStudioHdr *hdr = BaseClass::OnNewModel();
  497. m_iBarrelBone = LookupBone( "procedural_chamber" );
  498. return hdr;
  499. }
  500. //-----------------------------------------------------------------------------
  501. // Purpose:
  502. //-----------------------------------------------------------------------------
  503. void CTFGrenadeLauncher::StandardBlendingRules( CStudioHdr *hdr, Vector pos[], Quaternion q[], float currentTime, int boneMask )
  504. {
  505. BaseClass::StandardBlendingRules( hdr, pos, q, currentTime, boneMask );
  506. if (m_iBarrelBone != -1)
  507. {
  508. UpdateBarrelMovement();
  509. AngleQuaternion( RadianEuler( 0, 0, m_flBarrelAngle ), q[m_iBarrelBone] );
  510. }
  511. }
  512. //-----------------------------------------------------------------------------
  513. // Purpose: For third person weapons.
  514. //-----------------------------------------------------------------------------
  515. void CTFGrenadeLauncher::OnDataChanged( DataUpdateType_t type )
  516. {
  517. if ( m_bCurrentAndGoalTubeEqual && m_iCurrentTube != m_iGoalTube )
  518. m_flBarrelRotateBeginTime = gpGlobals->curtime;
  519. m_bCurrentAndGoalTubeEqual = ( m_iCurrentTube == m_iGoalTube );
  520. BaseClass::OnDataChanged( type );
  521. }
  522. //-----------------------------------------------------------------------------
  523. // Purpose: Updates the velocity and position of the rotating barrel
  524. //-----------------------------------------------------------------------------
  525. void CTFGrenadeLauncher::UpdateBarrelMovement( void )
  526. {
  527. if ( m_iGoalTube != m_iCurrentTube )
  528. {
  529. float flPartialRotationDeg = 0.0f;
  530. const float tVal = ( gpGlobals->curtime - m_flBarrelRotateBeginTime ) / cProceduralBarrelRotationTime;
  531. if ( tVal < 1.0f )
  532. {
  533. Assert( cProceduralBarrelRotationAnimationPoints[ 0 ].x == 0.0f );
  534. Assert( cProceduralBarrelRotationAnimationPoints[ ARRAYSIZE( cProceduralBarrelRotationAnimationPoints ) - 1 ].x == 1.0f );
  535. const Vector* pFirst = NULL;
  536. const Vector* pSecond = NULL;
  537. for ( int i = 1; i < ARRAYSIZE( cProceduralBarrelRotationAnimationPoints ); ++i )
  538. {
  539. // Need to be increasing in time, or we won't find the right span.
  540. Assert( cProceduralBarrelRotationAnimationPoints[ i - 1 ].x < cProceduralBarrelRotationAnimationPoints[ i ].x );
  541. if ( tVal <= cProceduralBarrelRotationAnimationPoints[ i ].x )
  542. {
  543. pFirst = &cProceduralBarrelRotationAnimationPoints[ i - 1 ];
  544. pSecond = &cProceduralBarrelRotationAnimationPoints[ i ];
  545. break;
  546. }
  547. }
  548. Assert( pFirst && pSecond );
  549. float flPartialT = ( tVal - pFirst->x ) / ( pSecond->x - pFirst->x );
  550. flPartialRotationDeg = Hermite_Spline( pFirst->y, pSecond->y, pFirst->z, pSecond->z, flPartialT );
  551. }
  552. else
  553. {
  554. m_iCurrentTube = m_iGoalTube;
  555. m_bCurrentAndGoalTubeEqual = true;
  556. }
  557. const float flBaseDeg = 60.0f * m_iCurrentTube;
  558. m_flBarrelAngle = DEG2RAD( flBaseDeg + flPartialRotationDeg );
  559. }
  560. }
  561. void CTFGrenadeLauncher::ViewModelAttachmentBlending( CStudioHdr *hdr, Vector pos[], Quaternion q[], float currentTime, int boneMask )
  562. {
  563. int iBarrelBone = Studio_BoneIndexByName( hdr, "procedural_chamber" );
  564. // Assert( iBarrelBone != -1 );
  565. if ( iBarrelBone != -1 )
  566. {
  567. if ( hdr->boneFlags( iBarrelBone ) & boneMask )
  568. {
  569. RadianEuler a;
  570. QuaternionAngles( q[ iBarrelBone ], a );
  571. a.z = m_flBarrelAngle;
  572. AngleQuaternion( a, q[ iBarrelBone ] );
  573. }
  574. }
  575. }
  576. #endif //CLIENT_DLL
  577. //-----------------------------------------------------------------------------
  578. // Purpose:
  579. // won't be called for w_ version of the model, so this isn't getting updated twice
  580. //-----------------------------------------------------------------------------
  581. void CTFGrenadeLauncher::ItemPreFrame( void )
  582. {
  583. #ifdef CLIENT_DLL
  584. UpdateBarrelMovement();
  585. #endif
  586. #ifdef GAME_DLL
  587. if ( gpGlobals->curtime > m_flBarrelRotateBeginTime + cProceduralBarrelRotationTime )
  588. m_iCurrentTube = m_iGoalTube;
  589. #endif
  590. BaseClass::ItemPreFrame();
  591. }