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.

815 lines
24 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // TF Rocket Launcher
  4. //
  5. //=============================================================================
  6. #include "cbase.h"
  7. #include "tf_weapon_rocketlauncher.h"
  8. #include "tf_fx_shared.h"
  9. #include "tf_weaponbase_rocket.h"
  10. #include "in_buttons.h"
  11. #include "tf_gamerules.h"
  12. // Client specific.
  13. #ifdef CLIENT_DLL
  14. #include "c_tf_player.h"
  15. #include <vgui_controls/Panel.h>
  16. #include <vgui/ISurface.h>
  17. #include "soundenvelope.h"
  18. #include "particle_property.h"
  19. // Server specific.
  20. #else
  21. #include "tf_player.h"
  22. #include "tf_obj_sentrygun.h"
  23. #include "tf_projectile_arrow.h"
  24. #endif
  25. #define BOMBARDMENT_ROCKET_MODEL "models/buildables/sentry3_rockets.mdl"
  26. //=============================================================================
  27. //
  28. // Weapon Rocket Launcher tables.
  29. //
  30. IMPLEMENT_NETWORKCLASS_ALIASED( TFRocketLauncher, DT_WeaponRocketLauncher )
  31. BEGIN_NETWORK_TABLE( CTFRocketLauncher, DT_WeaponRocketLauncher )
  32. #ifndef CLIENT_DLL
  33. // SendPropInt( SENDINFO( m_iSecondaryShotsFired ) ),
  34. #else
  35. // RecvPropInt( RECVINFO( m_iSecondaryShotsFired ) ),
  36. #endif
  37. END_NETWORK_TABLE()
  38. BEGIN_PREDICTION_DATA( CTFRocketLauncher )
  39. END_PREDICTION_DATA()
  40. LINK_ENTITY_TO_CLASS( tf_weapon_rocketlauncher, CTFRocketLauncher );
  41. PRECACHE_WEAPON_REGISTER( tf_weapon_rocketlauncher );
  42. // Server specific.
  43. #ifndef CLIENT_DLL
  44. BEGIN_DATADESC( CTFRocketLauncher )
  45. END_DATADESC()
  46. #endif
  47. //=============================================================================
  48. //
  49. // Direct Hit tables.
  50. //
  51. IMPLEMENT_NETWORKCLASS_ALIASED( TFRocketLauncher_DirectHit, DT_WeaponRocketLauncher_DirectHit )
  52. BEGIN_NETWORK_TABLE( CTFRocketLauncher_DirectHit, DT_WeaponRocketLauncher_DirectHit )
  53. END_NETWORK_TABLE()
  54. BEGIN_PREDICTION_DATA( CTFRocketLauncher_DirectHit )
  55. END_PREDICTION_DATA()
  56. LINK_ENTITY_TO_CLASS( tf_weapon_rocketlauncher_directhit, CTFRocketLauncher_DirectHit );
  57. PRECACHE_WEAPON_REGISTER( tf_weapon_rocketlauncher_directhit );
  58. // Server specific.
  59. #ifndef CLIENT_DLL
  60. BEGIN_DATADESC( CTFRocketLauncher_DirectHit )
  61. END_DATADESC()
  62. #endif
  63. //=============================================================================
  64. //
  65. // AIRSTRIKE BEGIN
  66. IMPLEMENT_NETWORKCLASS_ALIASED( TFRocketLauncher_AirStrike, DT_WeaponRocketLauncher_AirStrike )
  67. BEGIN_NETWORK_TABLE( CTFRocketLauncher_AirStrike, DT_WeaponRocketLauncher_AirStrike )
  68. #ifndef CLIENT_DLL
  69. // SendPropInt( SENDINFO( m_iRocketKills ) ),
  70. #else
  71. // RecvPropInt( RECVINFO( m_iRocketKills ) ),
  72. #endif
  73. END_NETWORK_TABLE()
  74. BEGIN_PREDICTION_DATA( CTFRocketLauncher_AirStrike )
  75. END_PREDICTION_DATA()
  76. LINK_ENTITY_TO_CLASS( tf_weapon_rocketlauncher_airstrike, CTFRocketLauncher_AirStrike );
  77. PRECACHE_WEAPON_REGISTER( tf_weapon_rocketlauncher_airstrike );
  78. // Server specific.
  79. #ifndef CLIENT_DLL
  80. BEGIN_DATADESC( CTFRocketLauncher_AirStrike )
  81. END_DATADESC()
  82. #endif
  83. // AIRSTRIKE END
  84. //CREATE_SIMPLE_WEAPON_TABLE( TFRocketLauncher_AirStrike, tf_weapon_rocketlauncher_airstrike )
  85. //CREATE_SIMPLE_WEAPON_TABLE( TFRocketLauncher_Mortar, tf_weapon_rocketlauncher_mortar )
  86. //=============================================================================
  87. //
  88. // Mortar tables.
  89. //
  90. IMPLEMENT_NETWORKCLASS_ALIASED( TFRocketLauncher_Mortar, DT_WeaponRocketLauncher_Mortar )
  91. BEGIN_NETWORK_TABLE( CTFRocketLauncher_Mortar, DT_WeaponRocketLauncher_Mortar )
  92. END_NETWORK_TABLE()
  93. BEGIN_PREDICTION_DATA( CTFRocketLauncher_Mortar )
  94. END_PREDICTION_DATA()
  95. #ifdef STAGING_ONLY
  96. LINK_ENTITY_TO_CLASS( tf_weapon_rocketlauncher_mortar, CTFRocketLauncher_Mortar );
  97. PRECACHE_WEAPON_REGISTER( tf_weapon_rocketlauncher_mortar );
  98. #endif // STAGING_ONLY
  99. // Server specific.
  100. #ifndef CLIENT_DLL
  101. BEGIN_DATADESC( CTFRocketLauncher_Mortar )
  102. END_DATADESC()
  103. #endif
  104. //=============================================================================
  105. //
  106. // Crossbow tables.
  107. //
  108. IMPLEMENT_NETWORKCLASS_ALIASED( TFCrossbow, DT_Crossbow )
  109. BEGIN_NETWORK_TABLE( CTFCrossbow, DT_Crossbow )
  110. #ifdef CLIENT_DLL
  111. RecvPropFloat( RECVINFO( m_flRegenerateDuration ) ),
  112. RecvPropFloat( RECVINFO( m_flLastUsedTimestamp ) ),
  113. #else
  114. SendPropFloat( SENDINFO( m_flRegenerateDuration ), 0, SPROP_NOSCALE ),
  115. SendPropFloat( SENDINFO( m_flLastUsedTimestamp ), 0, SPROP_NOSCALE ),
  116. #endif
  117. END_NETWORK_TABLE()
  118. BEGIN_PREDICTION_DATA( CTFCrossbow )
  119. END_PREDICTION_DATA()
  120. LINK_ENTITY_TO_CLASS( tf_weapon_crossbow, CTFCrossbow );
  121. PRECACHE_WEAPON_REGISTER( tf_weapon_crossbow );
  122. // Server specific.
  123. #ifndef CLIENT_DLL
  124. BEGIN_DATADESC( CTFCrossbow )
  125. END_DATADESC()
  126. #endif
  127. #ifdef STAGING_ONLY
  128. ConVar tf_airstrike_dmg_scale( "tf_airstrike_dmg_scale", "0.65", FCVAR_REPLICATED, "How much damage the mini rockets do compared to regular rocket" );
  129. ConVar tf_mortar_allow_fulltracking( "tf_mortar_allow_fulltracking", "0.0", FCVAR_REPLICATED, "Enable to allow full tracking / infinte redirects for Mortar Launcher" );
  130. #endif // STAGING_ONLY
  131. //-----------------------------------------------------------------------------
  132. // Purpose:
  133. // Input : -
  134. //-----------------------------------------------------------------------------
  135. CTFRocketLauncher::CTFRocketLauncher()
  136. {
  137. m_bReloadsSingly = true;
  138. m_nReloadPitchStep = 0;
  139. #ifdef GAME_DLL
  140. m_bIsOverloading = false;
  141. #endif //GAME_DLL
  142. }
  143. //-----------------------------------------------------------------------------
  144. // Purpose:
  145. // Input : -
  146. //-----------------------------------------------------------------------------
  147. CTFRocketLauncher::~CTFRocketLauncher()
  148. {
  149. }
  150. #ifndef CLIENT_DLL
  151. //-----------------------------------------------------------------------------
  152. // Purpose:
  153. //-----------------------------------------------------------------------------
  154. void CTFRocketLauncher::Precache()
  155. {
  156. BaseClass::Precache();
  157. PrecacheParticleSystem( "rocketbackblast" );
  158. // FIXME: DO WE STILL NEED THESE??
  159. PrecacheScriptSound( "MVM.GiantSoldierRocketShoot" );
  160. PrecacheScriptSound( "MVM.GiantSoldierRocketShootCrit" );
  161. PrecacheScriptSound( "MVM.GiantSoldierRocketExplode" );
  162. PrecacheScriptSound( "Weapon_Airstrike.AltFire" );
  163. PrecacheScriptSound( "Weapon_Airstrike.Fail" );
  164. //Building_Sentrygun.FireRocket
  165. }
  166. #endif
  167. void CTFRocketLauncher::ModifyEmitSoundParams( EmitSound_t &params )
  168. {
  169. bool bBaseReloadSound = V_strcmp( params.m_pSoundName, "Weapon_RPG.Reload" ) == 0;
  170. if ( AutoFiresFullClip() && ( bBaseReloadSound || V_strcmp( params.m_pSoundName, "Weapon_DumpsterRocket.Reload" ) == 0 ) )
  171. {
  172. float fMaxAmmoInClip = GetMaxClip1();
  173. float fAmmoPercentage = static_cast< float >( m_nReloadPitchStep ) / fMaxAmmoInClip;
  174. // Play a sound that gets higher pitched as more ammo is added
  175. if ( bBaseReloadSound )
  176. {
  177. params.m_pSoundName = "Weapon_DumpsterRocket.Reload_FP";
  178. }
  179. else
  180. {
  181. params.m_pSoundName = "Weapon_DumpsterRocket.Reload";
  182. }
  183. params.m_nPitch *= RemapVal( fAmmoPercentage, 0.0f, ( fMaxAmmoInClip - 1.0f ) / fMaxAmmoInClip, 0.79f, 1.19f );
  184. params.m_nFlags |= SND_CHANGE_PITCH;
  185. m_nReloadPitchStep = MIN( GetMaxClip1() - 1, m_nReloadPitchStep + 1 );
  186. // The last rocket goes in right when this sound happens so that you can launch it before a misfire
  187. IncrementAmmo();
  188. m_bReloadedThroughAnimEvent = true;
  189. }
  190. }
  191. void CTFRocketLauncher::Misfire( void )
  192. {
  193. BaseClass::Misfire();
  194. #ifdef GAME_DLL
  195. if ( CanOverload() )
  196. {
  197. CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() );
  198. if ( !pPlayer )
  199. return;
  200. CTFBaseRocket *pRocket = dynamic_cast< CTFBaseRocket* >( BaseClass::FireProjectile( pPlayer ) );
  201. if ( pRocket )
  202. {
  203. trace_t tr;
  204. UTIL_TraceLine( pRocket->GetAbsOrigin(), pPlayer->EyePosition(), MASK_SOLID, pRocket, COLLISION_GROUP_NONE, &tr );
  205. pRocket->Explode( &tr, pPlayer );
  206. }
  207. }
  208. #endif
  209. }
  210. //-----------------------------------------------------------------------------
  211. bool CTFRocketLauncher::CheckReloadMisfire( void )
  212. {
  213. if ( !CanOverload() )
  214. return false;
  215. #ifdef GAME_DLL
  216. CTFPlayer *pPlayer = GetTFPlayerOwner();
  217. if ( m_bIsOverloading )
  218. {
  219. if ( Clip1() > 0 )
  220. {
  221. Misfire();
  222. return true;
  223. }
  224. else
  225. {
  226. m_bIsOverloading = false;
  227. }
  228. }
  229. else if ( Clip1() >= GetMaxClip1() || ( Clip1() > 0 && pPlayer && pPlayer->GetAmmoCount( m_iPrimaryAmmoType ) == 0 ) )
  230. {
  231. Misfire();
  232. m_bIsOverloading = true;
  233. return true;
  234. }
  235. #endif // GAME_DLL
  236. return false;
  237. }
  238. //-----------------------------------------------------------------------------
  239. bool CTFRocketLauncher::ShouldBlockPrimaryFire()
  240. {
  241. return !AutoFiresFullClip();
  242. }
  243. //-----------------------------------------------------------------------------
  244. // Purpose:
  245. //-----------------------------------------------------------------------------
  246. CBaseEntity *CTFRocketLauncher::FireProjectile( CTFPlayer *pPlayer )
  247. {
  248. m_flShowReloadHintAt = gpGlobals->curtime + 30;
  249. CBaseEntity *pRocket = BaseClass::FireProjectile( pPlayer );
  250. m_nReloadPitchStep = MAX( 0, m_nReloadPitchStep - 1 );
  251. #ifdef GAME_DLL
  252. int iProjectile = 0;
  253. CALL_ATTRIB_HOOK_INT( iProjectile, override_projectile_type );
  254. if ( iProjectile == 0 )
  255. {
  256. iProjectile = GetWeaponProjectileType();
  257. }
  258. if ( pPlayer->IsPlayerClass( TF_CLASS_SOLDIER ) && IsCurrentAttackARandomCrit() && ( iProjectile == TF_PROJECTILE_ROCKET ) )
  259. {
  260. // Track consecutive crit shots for achievements
  261. m_iConsecutiveCrits++;
  262. if ( m_iConsecutiveCrits == 2 )
  263. {
  264. pPlayer->AwardAchievement( ACHIEVEMENT_TF_SOLDIER_SHOOT_MULT_CRITS );
  265. }
  266. }
  267. else
  268. {
  269. m_iConsecutiveCrits = 0;
  270. }
  271. m_bIsOverloading = false;
  272. #endif
  273. if ( TFGameRules()->GameModeUsesUpgrades() )
  274. {
  275. PlayUpgradedShootSound( "Weapon_Upgrade.DamageBonus" );
  276. }
  277. #ifdef STAGING_ONLY
  278. #ifdef GAME_DLL
  279. if ( pRocket && pPlayer && pPlayer->RocketJumped() )
  280. {
  281. int iRocketsApplyImpuse = 0;
  282. CALL_ATTRIB_HOOK_INT_ON_OTHER( pPlayer, iRocketsApplyImpuse, mod_rocket_launch_impulse );
  283. if ( iRocketsApplyImpuse )
  284. {
  285. // Apply force in opposite direction of rocket
  286. Vector vecDir = pRocket->GetAbsVelocity();
  287. Vector vecFlightDir = -vecDir;
  288. VectorNormalize( vecFlightDir );
  289. // Apply more force if looking down
  290. QAngle angEye = EyeAngles();
  291. float flForce = ( angEye.x > 60.f ) ? 700.f : 400.f;
  292. Vector vecForce = vecFlightDir * flForce;
  293. // DevMsg( "x.Ang: %f\tForce: %f\n", angEye.x, flForce );
  294. // Prevent insane speeds
  295. float flSpeed = vecForce.NormalizeInPlace();
  296. const float flLimit = Min( 800.f, flSpeed );
  297. pPlayer->ApplyAbsVelocityImpulse( flLimit * vecForce );
  298. }
  299. }
  300. #endif // GAME_DLL
  301. #endif // STAGING_ONLY
  302. return pRocket;
  303. }
  304. //-----------------------------------------------------------------------------
  305. // Purpose:
  306. //-----------------------------------------------------------------------------
  307. void CTFRocketLauncher::ItemPostFrame( void )
  308. {
  309. CTFPlayer *pOwner = ToTFPlayer( GetOwnerEntity() );
  310. if ( !pOwner )
  311. return;
  312. BaseClass::ItemPostFrame();
  313. #ifdef GAME_DLL
  314. if ( m_flShowReloadHintAt && m_flShowReloadHintAt < gpGlobals->curtime )
  315. {
  316. if ( Clip1() < GetMaxClip1() )
  317. {
  318. pOwner->HintMessage( HINT_SOLDIER_RPG_RELOAD );
  319. }
  320. m_flShowReloadHintAt = 0;
  321. }
  322. #endif
  323. }
  324. //-----------------------------------------------------------------------------
  325. // Purpose:
  326. //-----------------------------------------------------------------------------
  327. bool CTFRocketLauncher::DefaultReload( int iClipSize1, int iClipSize2, int iActivity )
  328. {
  329. m_flShowReloadHintAt = 0;
  330. return BaseClass::DefaultReload( iClipSize1, iClipSize2, iActivity );
  331. }
  332. #ifdef CLIENT_DLL
  333. //-----------------------------------------------------------------------------
  334. // Purpose:
  335. //-----------------------------------------------------------------------------
  336. void CTFRocketLauncher::CreateMuzzleFlashEffects( C_BaseEntity *pAttachEnt, int nIndex )
  337. {
  338. BaseClass::CreateMuzzleFlashEffects( pAttachEnt, nIndex );
  339. // Don't do backblast effects in first person
  340. C_TFPlayer *pOwner = ToTFPlayer( GetOwnerEntity() );
  341. if ( pOwner->IsLocalPlayer() )
  342. return;
  343. ParticleProp()->Init( this );
  344. ParticleProp()->Create( "rocketbackblast", PATTACH_POINT_FOLLOW, "backblast" );
  345. }
  346. #endif
  347. //-----------------------------------------------------------------------------
  348. // Purpose:
  349. //-----------------------------------------------------------------------------
  350. int CTFRocketLauncher::GetWeaponProjectileType( void ) const
  351. {
  352. return BaseClass::GetWeaponProjectileType();
  353. }
  354. //----------------------------------------------------------------------------------------------------------------------------------------------------------
  355. // CTFRocketLauncher_AirStrike BEGIN
  356. //----------------------------------------------------------------------------------------------------------------------------------------------------------
  357. CTFRocketLauncher_AirStrike::CTFRocketLauncher_AirStrike()
  358. {
  359. //m_iSecondaryShotsFired = 0;
  360. }
  361. #ifdef GAME_DLL
  362. //----------------------------------------------------------------------------------------------------------------------------------------------------------
  363. void CTFRocketLauncher_AirStrike::OnPlayerKill( CTFPlayer *pVictim, const CTakeDamageInfo &info )
  364. {
  365. BaseClass::OnPlayerKill( pVictim, info );
  366. CTFPlayer *pOwner = ToTFPlayer( GetOwner() );
  367. if ( !pOwner )
  368. return;
  369. int iDecap = pOwner->m_Shared.GetDecapitations() + 1;
  370. if ( pVictim )
  371. {
  372. iDecap += pVictim->m_Shared.GetDecapitations();
  373. }
  374. pOwner->m_Shared.SetDecapitations( iDecap );
  375. int iClipSizeOnKills = 0;
  376. CALL_ATTRIB_HOOK_INT( iClipSizeOnKills, clipsize_increase_on_kill );
  377. if ( iClipSizeOnKills && ( iDecap >= iClipSizeOnKills ) )
  378. {
  379. pOwner->AwardAchievement( ACHIEVEMENT_TF_SOLDIER_AIRSTRIKE_MAX_CLIP );
  380. }
  381. }
  382. //----------------------------------------------------------------------------------------------------------------------------------------------------------
  383. #endif
  384. //-----------------------------------------------------------------------------
  385. int CTFRocketLauncher_AirStrike::GetCount( void )
  386. {
  387. CTFPlayer *pOwner = ToTFPlayer( GetOwner() );
  388. if ( !pOwner )
  389. return 0;
  390. return pOwner->m_Shared.GetDecapitations();
  391. }
  392. ////----------------------------------------------------------------------------------------------------------------------------------------------------------
  393. //void CTFRocketLauncher_AirStrike::PrimaryAttack( void )
  394. //{
  395. // CTFPlayer *pPlayer = GetTFPlayerOwner();
  396. // if ( !pPlayer )
  397. // return;
  398. //
  399. // // If the player is blast jumping and hasn't fired a shot yet, we can initiate
  400. // if ( pPlayer->m_Shared.InCond( TF_COND_BLASTJUMPING ) && m_iSecondaryShotsFired == 0 )
  401. // {
  402. // FireSecondaryRockets();
  403. // }
  404. // else
  405. // {
  406. // BaseClass::PrimaryAttack();
  407. // }
  408. //}
  409. ////----------------------------------------------------------------------------------------------------------------------------------------------------------
  410. //bool CTFRocketLauncher_AirStrike::CanHolster( void )
  411. //{
  412. // if ( m_iSecondaryShotsFired > 0 )
  413. // return false;
  414. //
  415. // return BaseClass::CanHolster();
  416. //}
  417. ////-----------------------------------------------------------------------------
  418. //void CTFRocketLauncher_AirStrike::ItemPostFrame( void )
  419. //{
  420. // // If allowed
  421. // FireSecondaryRockets();
  422. // BaseClass::ItemPostFrame();
  423. //}
  424. ////-----------------------------------------------------------------------------
  425. //void CTFRocketLauncher_AirStrike::ItemBusyFrame( void )
  426. //{
  427. // // If allowed
  428. // FireSecondaryRockets();
  429. // BaseClass::ItemBusyFrame();
  430. //}
  431. //
  432. ////-----------------------------------------------------------------------------
  433. //void CTFRocketLauncher_AirStrike::FireSecondaryRockets()
  434. //{
  435. //#ifdef STAGING_ONLY
  436. // if ( m_flNextPrimaryAttack >= gpGlobals->curtime )
  437. // return;
  438. //
  439. // CTFPlayer *pPlayer = GetTFPlayerOwner();
  440. // if ( !pPlayer )
  441. // return;
  442. //
  443. // if ( !( pPlayer->m_nButtons & IN_ATTACK ) && m_iSecondaryShotsFired == 0 )
  444. // return;
  445. //
  446. // int iAirBombardment = 0;
  447. // CALL_ATTRIB_HOOK_INT( iAirBombardment, rj_air_bombardment );
  448. // if ( !iAirBombardment )
  449. // return;
  450. //
  451. // // This function and its checks are only on the server
  452. // if ( !pPlayer->m_Shared.InCond( TF_COND_BLASTJUMPING ) )
  453. // {
  454. //#ifdef CLIENT_DLL
  455. // // play fail sound locally
  456. // //pPlayer->EmitSound( "Weapon_Airstrike.Fail" );
  457. //#endif
  458. // m_iSecondaryShotsFired = 0;
  459. // return;
  460. // }
  461. //
  462. // if ( m_iClip1 <= 0 && m_iSecondaryShotsFired == 0 )
  463. // return;
  464. //
  465. // if ( m_bReloadsSingly )
  466. // {
  467. // m_iReloadMode.Set( TF_RELOAD_START );
  468. // }
  469. //
  470. // float flFireDelay = m_pWeaponInfo->GetWeaponData( m_iWeaponMode ).m_flTimeFireDelay;
  471. // flFireDelay += GetFireDelay();
  472. // CALL_ATTRIB_HOOK_FLOAT( flFireDelay, mult_postfiredelay );
  473. // CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( pPlayer, flFireDelay, hwn_mult_postfiredelay );
  474. //
  475. // SendWeaponAnim( ACT_VM_PRIMARYATTACK );
  476. // pPlayer->SetAnimation( PLAYER_ATTACK1 );
  477. // pPlayer->DoAnimationEvent( PLAYERANIMEVENT_ATTACK_PRIMARY );
  478. // m_flNextPrimaryAttack = gpGlobals->curtime + flFireDelay / 2.0f;
  479. //
  480. // // Want a different sound
  481. // pPlayer->EmitSound( "Weapon_Airstrike.AltFire" );
  482. //
  483. //#ifdef GAME_DLL
  484. // // Server only - create the rocket.
  485. // Vector vecSrc;
  486. // QAngle angForward;
  487. // Vector vecOffset( 23.5f, 12.0f, -3.0f );
  488. // GetProjectileFireSetup( pPlayer, vecOffset, &vecSrc, &angForward, false );
  489. //
  490. // CTFProjectile_SentryRocket *pProjectile = CTFProjectile_SentryRocket::Create( vecSrc, angForward, this, pPlayer );
  491. //
  492. // if ( pProjectile )
  493. // {
  494. // pProjectile->SetCritical( IsCurrentAttackACrit() );
  495. // pProjectile->SetDamage( GetProjectileDamage() * tf_airstrike_dmg_scale.GetFloat() );
  496. // pProjectile->SetDamageForceScale( tf_airstrike_dmg_scale.GetFloat() );
  497. // }
  498. //
  499. // if ( m_iSecondaryShotsFired == 0 )
  500. // {
  501. // RemoveProjectileAmmo( pPlayer );
  502. // }
  503. //
  504. // m_iSecondaryShotsFired++;
  505. // if ( m_iSecondaryShotsFired >= 3 )
  506. // {
  507. // // Decrement ammo and reset
  508. // m_iSecondaryShotsFired = 0;
  509. // // Give normal delay between shots here
  510. // m_flNextPrimaryAttack = gpGlobals->curtime + flFireDelay;
  511. // }
  512. //#endif
  513. //
  514. //#endif
  515. //}
  516. //----------------------------------------------------------------------------------------------------------------------------------------------------------
  517. // CTFRocketLauncher_Mortar BEGIN
  518. //----------------------------------------------------------------------------------------------------------------------------------------------------------
  519. //CTFRocketLauncher_Mortar::CTFRocketLauncher_Mortar()
  520. //{
  521. //
  522. //}
  523. //----------------------------------------------------------------------------------------------------------------------------------------------------------
  524. CBaseEntity *CTFRocketLauncher_Mortar::FireProjectile( CTFPlayer *pPlayer )
  525. {
  526. // Fire the rocket
  527. CBaseEntity* pRocket = BaseClass::FireProjectile( pPlayer );
  528. // Add it to my list
  529. #ifdef GAME_DLL
  530. m_vecRockets.AddToTail( pRocket );
  531. #endif
  532. return pRocket;
  533. }
  534. //----------------------------------------------------------------------------------------------------------------------------------------------------------
  535. void CTFRocketLauncher_Mortar::SecondaryAttack( void )
  536. {
  537. RedirectRockets();
  538. }
  539. //-----------------------------------------------------------------------------
  540. void CTFRocketLauncher_Mortar::ItemPostFrame( void )
  541. {
  542. #ifdef GAME_DLL
  543. CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
  544. if ( pOwner && pOwner->m_nButtons & IN_ATTACK2 )
  545. {
  546. // If allowed
  547. RedirectRockets();
  548. }
  549. #endif
  550. BaseClass::ItemPostFrame();
  551. }
  552. //-----------------------------------------------------------------------------
  553. void CTFRocketLauncher_Mortar::ItemBusyFrame( void )
  554. {
  555. #ifdef GAME_DLL
  556. CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
  557. if ( pOwner && pOwner->m_nButtons & IN_ATTACK2 )
  558. {
  559. // If allowed
  560. RedirectRockets();
  561. }
  562. #endif
  563. BaseClass::ItemBusyFrame();
  564. }
  565. //-----------------------------------------------------------------------------
  566. void CTFRocketLauncher_Mortar::RedirectRockets( void )
  567. {
  568. #ifdef GAME_DLL
  569. if ( m_vecRockets.Count() <= 0 )
  570. return;
  571. CTFPlayer *pOwner = ToTFPlayer( GetOwnerEntity() );
  572. if ( !pOwner )
  573. return;
  574. Vector vecEye = pOwner->EyePosition();
  575. Vector vecForward, vecRight, vecUp;
  576. AngleVectors( pOwner->EyeAngles(), &vecForward, &vecRight, &vecUp );
  577. trace_t tr;
  578. UTIL_TraceLine( vecEye, vecEye + vecForward * MAX_TRACE_LENGTH, MASK_SOLID, pOwner, COLLISION_GROUP_NONE, &tr );
  579. float flVel = 1100.0f;
  580. FOR_EACH_VEC_BACK( m_vecRockets, i )
  581. {
  582. CBaseEntity* pRocket = m_vecRockets[i].Get();
  583. // Remove targets that have disappeared
  584. if ( !pRocket || pRocket->GetOwnerEntity() != GetOwnerEntity() )
  585. {
  586. m_vecRockets.Remove( i );
  587. continue;
  588. }
  589. // Give the rocket a new target
  590. Vector vecDir = pRocket->WorldSpaceCenter() - tr.endpos;
  591. VectorNormalize( vecDir );
  592. Vector vecVel = pRocket->GetAbsVelocity();
  593. vecVel = -flVel * vecDir;
  594. pRocket->SetAbsVelocity( vecVel );
  595. QAngle newAngles;
  596. VectorAngles( -vecDir, newAngles );
  597. pRocket->SetAbsAngles( newAngles );
  598. #ifdef STAGING_ONLY
  599. if ( !tf_mortar_allow_fulltracking.GetBool() )
  600. {
  601. // only allow a single redirect
  602. m_vecRockets.Remove( i );
  603. }
  604. #else
  605. m_vecRockets.Remove( i );
  606. #endif
  607. }
  608. #endif
  609. }
  610. //----------------------------------------------------------------------------------------------------------------------------------------------------------
  611. // CROSSBOW BEGIN
  612. //----------------------------------------------------------------------------------------------------------------------------------------------------------
  613. bool CTFCrossbow::Holster( CBaseCombatWeapon *pSwitchingTo )
  614. {
  615. // Allow Crossbow to silently reload like the flaregun
  616. if ( m_iClip1 == 0 )
  617. {
  618. // These Values need to match the anim times since all this stuff is actually driven by animation sequence time in the base code
  619. float flFireDelay = ApplyFireDelay( m_pWeaponInfo->GetWeaponData( m_iWeaponMode ).m_flTimeFireDelay );
  620. float flReloadTime = m_pWeaponInfo->GetWeaponData( m_iWeaponMode ).m_flTimeReload;
  621. CALL_ATTRIB_HOOK_FLOAT( flReloadTime, mult_reload_time );
  622. CALL_ATTRIB_HOOK_FLOAT( flReloadTime, mult_reload_time_hidden );
  623. CALL_ATTRIB_HOOK_FLOAT( flReloadTime, fast_reload );
  624. float flIdleTime = GetLastPrimaryAttackTime() + flFireDelay + flReloadTime;
  625. if ( GetWeaponIdleTime() < flIdleTime )
  626. {
  627. SetWeaponIdleTime( flIdleTime );
  628. m_flNextPrimaryAttack = flIdleTime;
  629. }
  630. IncrementAmmo();
  631. }
  632. return BaseClass::Holster( pSwitchingTo );
  633. }
  634. //-----------------------------------------------------------------------------
  635. void CTFCrossbow::SecondaryAttack( void )
  636. {
  637. // If this is the jarate bolt crossbow, make sure we are allowed to do it
  638. int iMilkBolt = 0;
  639. CALL_ATTRIB_HOOK_INT( iMilkBolt, fires_milk_bolt );
  640. if ( iMilkBolt )
  641. {
  642. CTFPlayer *pPlayer = GetTFPlayerOwner();
  643. if ( !pPlayer )
  644. return;
  645. if ( !CanAttack() )
  646. return;
  647. if ( m_flNextPrimaryAttack > gpGlobals->curtime )
  648. return;
  649. // Can we attack
  650. if ( GetProgress() >= 1.0f )
  651. {
  652. // Call Primary Attack and modify the projectile
  653. m_bMilkNextAttack = true;
  654. PrimaryAttack();
  655. m_flRegenerateDuration = iMilkBolt;
  656. m_flLastUsedTimestamp = gpGlobals->curtime;
  657. }
  658. }
  659. }
  660. //-----------------------------------------------------------------------------
  661. void CTFCrossbow::ModifyProjectile( CBaseEntity* pProj )
  662. {
  663. #ifdef GAME_DLL
  664. if ( m_bMilkNextAttack )
  665. {
  666. CTFProjectile_Arrow* pMainArrow = assert_cast<CTFProjectile_Arrow*>( pProj );
  667. if ( pMainArrow )
  668. {
  669. pMainArrow->SetApplyMilkOnHit();
  670. }
  671. }
  672. #endif
  673. m_bMilkNextAttack = false;
  674. }
  675. //-----------------------------------------------------------------------------
  676. void CTFCrossbow::ItemPostFrame( void )
  677. {
  678. BaseClass::ItemPostFrame();
  679. m_bMilkNextAttack = false;
  680. }
  681. //-----------------------------------------------------------------------------
  682. float CTFCrossbow::GetProjectileSpeed( void )
  683. {
  684. return RemapValClamped( 0.75f, 0.0f, 1.f, 1800, 2600 ); // Temp, if we want to ramp.
  685. }
  686. //-----------------------------------------------------------------------------
  687. // Purpose:
  688. //-----------------------------------------------------------------------------
  689. float CTFCrossbow::GetProjectileGravity( void )
  690. {
  691. return RemapValClamped( 0.75f, 0.0f, 1.f, 0.5, 0.1 ); // Temp, if we want to ramp.
  692. }
  693. //-----------------------------------------------------------------------------
  694. // Purpose:
  695. //-----------------------------------------------------------------------------
  696. bool CTFCrossbow::IsViewModelFlipped( void )
  697. {
  698. return !BaseClass::IsViewModelFlipped(); // Invert because arrows are backwards by default.
  699. }
  700. //-----------------------------------------------------------------------------
  701. void CTFCrossbow::WeaponRegenerate( void )
  702. {
  703. BaseClass::WeaponRegenerate();
  704. m_flLastUsedTimestamp = 0;
  705. }
  706. //-----------------------------------------------------------------------------
  707. inline float CTFCrossbow::GetProgress( void )
  708. {
  709. int iMilkBolt = 0;
  710. CALL_ATTRIB_HOOK_INT( iMilkBolt, fires_milk_bolt );
  711. if ( iMilkBolt == 0 )
  712. return 0;
  713. float meltedTime = gpGlobals->curtime - m_flLastUsedTimestamp;
  714. return meltedTime / m_flRegenerateDuration;
  715. }