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.

858 lines
26 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // TF Grappling Hook
  4. //
  5. //=============================================================================
  6. #include "cbase.h"
  7. #include "in_buttons.h"
  8. #include "tf_weapon_grapplinghook.h"
  9. // Client specific.
  10. #ifdef CLIENT_DLL
  11. #include "c_tf_player.h"
  12. #include "gc_clientsystem.h"
  13. #include "prediction.h"
  14. #include "soundenvelope.h"
  15. // Server specific.
  16. #else
  17. #include "tf_player.h"
  18. #include "entity_rune.h"
  19. #include "effect_dispatch_data.h"
  20. #include "tf_fx.h"
  21. #include "func_respawnroom.h"
  22. #endif
  23. //=============================================================================
  24. //
  25. // Grappling hook tables.
  26. //
  27. IMPLEMENT_NETWORKCLASS_ALIASED( TFGrapplingHook, DT_GrapplingHook )
  28. BEGIN_NETWORK_TABLE( CTFGrapplingHook, DT_GrapplingHook )
  29. #ifdef GAME_DLL
  30. SendPropEHandle( SENDINFO( m_hProjectile ) ),
  31. #else // GAME_DLL
  32. RecvPropEHandle( RECVINFO( m_hProjectile ) ),
  33. #endif // CLIENT_DLL
  34. END_NETWORK_TABLE()
  35. BEGIN_PREDICTION_DATA( CTFGrapplingHook )
  36. END_PREDICTION_DATA()
  37. LINK_ENTITY_TO_CLASS( tf_weapon_grapplinghook, CTFGrapplingHook );
  38. PRECACHE_WEAPON_REGISTER( tf_weapon_grapplinghook );
  39. // Server specific.
  40. #ifndef CLIENT_DLL
  41. BEGIN_DATADESC( CTFGrapplingHook )
  42. END_DATADESC()
  43. #endif // CLIENT_DLL
  44. // This is basically a copy of s_acttableMeleeAllclass table except primary fire to use grappling hook specific
  45. acttable_t s_grapplinghook_normal_acttable[] =
  46. {
  47. { ACT_MP_STAND_IDLE, ACT_MP_STAND_MELEE_ALLCLASS, false },
  48. { ACT_MP_CROUCH_IDLE, ACT_MP_CROUCH_MELEE_ALLCLASS, false },
  49. { ACT_MP_RUN, ACT_MP_RUN_MELEE_ALLCLASS, false },
  50. { ACT_MP_WALK, ACT_MP_WALK_MELEE_ALLCLASS, false },
  51. { ACT_MP_AIRWALK, ACT_GRAPPLE_PULL_IDLE, false },
  52. { ACT_MP_CROUCHWALK, ACT_MP_CROUCHWALK_MELEE_ALLCLASS, false },
  53. { ACT_MP_JUMP, ACT_MP_JUMP_MELEE_ALLCLASS, false },
  54. { ACT_MP_JUMP_START, ACT_MP_JUMP_START_MELEE_ALLCLASS, false },
  55. { ACT_MP_JUMP_FLOAT, ACT_MP_JUMP_FLOAT_MELEE_ALLCLASS, false },
  56. { ACT_MP_JUMP_LAND, ACT_MP_JUMP_LAND_MELEE_ALLCLASS, false },
  57. { ACT_MP_SWIM, ACT_MP_SWIM_MELEE_ALLCLASS, false },
  58. { ACT_MP_DOUBLEJUMP_CROUCH, ACT_MP_DOUBLEJUMP_CROUCH_MELEE, false },
  59. { ACT_MP_ATTACK_STAND_PRIMARYFIRE, ACT_GRAPPLE_FIRE_START, false },
  60. { ACT_MP_ATTACK_CROUCH_PRIMARYFIRE, ACT_GRAPPLE_FIRE_START, false },
  61. { ACT_MP_ATTACK_SWIM_PRIMARYFIRE, ACT_GRAPPLE_FIRE_START, false },
  62. { ACT_MP_ATTACK_AIRWALK_PRIMARYFIRE, ACT_GRAPPLE_FIRE_START, false },
  63. { ACT_MP_ATTACK_STAND_SECONDARYFIRE, ACT_MP_ATTACK_STAND_MELEE_SECONDARY, false },
  64. { ACT_MP_ATTACK_CROUCH_SECONDARYFIRE, ACT_MP_ATTACK_CROUCH_MELEE_SECONDARY,false },
  65. { ACT_MP_ATTACK_SWIM_SECONDARYFIRE, ACT_MP_ATTACK_SWIM_MELEE_ALLCLASS, false },
  66. { ACT_MP_ATTACK_AIRWALK_SECONDARYFIRE, ACT_MP_ATTACK_AIRWALK_MELEE_ALLCLASS, false },
  67. { ACT_MP_GESTURE_FLINCH, ACT_MP_GESTURE_FLINCH_MELEE, false },
  68. { ACT_MP_GRENADE1_DRAW, ACT_MP_MELEE_GRENADE1_DRAW, false },
  69. { ACT_MP_GRENADE1_IDLE, ACT_MP_MELEE_GRENADE1_IDLE, false },
  70. { ACT_MP_GRENADE1_ATTACK, ACT_MP_MELEE_GRENADE1_ATTACK, false },
  71. { ACT_MP_GRENADE2_DRAW, ACT_MP_MELEE_GRENADE2_DRAW, false },
  72. { ACT_MP_GRENADE2_IDLE, ACT_MP_MELEE_GRENADE2_IDLE, false },
  73. { ACT_MP_GRENADE2_ATTACK, ACT_MP_MELEE_GRENADE2_ATTACK, false },
  74. { ACT_MP_GESTURE_VC_HANDMOUTH, ACT_MP_GESTURE_VC_HANDMOUTH_MELEE, false },
  75. { ACT_MP_GESTURE_VC_FINGERPOINT, ACT_MP_GESTURE_VC_FINGERPOINT_MELEE, false },
  76. { ACT_MP_GESTURE_VC_FISTPUMP, ACT_MP_GESTURE_VC_FISTPUMP_MELEE, false },
  77. { ACT_MP_GESTURE_VC_THUMBSUP, ACT_MP_GESTURE_VC_THUMBSUP_MELEE, false },
  78. { ACT_MP_GESTURE_VC_NODYES, ACT_MP_GESTURE_VC_NODYES_MELEE, false },
  79. { ACT_MP_GESTURE_VC_NODNO, ACT_MP_GESTURE_VC_NODNO_MELEE, false },
  80. };
  81. // This is basically a copy of s_acttableSecondary table except primary fire to use grappling hook specific
  82. acttable_t s_grapplinghook_engineer_acttable[] =
  83. {
  84. { ACT_MP_STAND_IDLE, ACT_MP_STAND_SECONDARY, false },
  85. { ACT_MP_CROUCH_IDLE, ACT_MP_CROUCH_SECONDARY, false },
  86. { ACT_MP_RUN, ACT_MP_RUN_SECONDARY, false },
  87. { ACT_MP_WALK, ACT_MP_WALK_SECONDARY, false },
  88. { ACT_MP_AIRWALK, ACT_MP_AIRWALK_SECONDARY, false },
  89. { ACT_MP_CROUCHWALK, ACT_MP_CROUCHWALK_SECONDARY, false },
  90. { ACT_MP_JUMP, ACT_MP_JUMP_SECONDARY, false },
  91. { ACT_MP_JUMP_START, ACT_MP_JUMP_START_SECONDARY, false },
  92. { ACT_MP_JUMP_FLOAT, ACT_MP_JUMP_FLOAT_SECONDARY, false },
  93. { ACT_MP_JUMP_LAND, ACT_MP_JUMP_LAND_SECONDARY, false },
  94. { ACT_MP_SWIM, ACT_MP_SWIM_SECONDARY, false },
  95. { ACT_MP_DOUBLEJUMP_CROUCH, ACT_MP_DOUBLEJUMP_CROUCH_SECONDARY, false },
  96. { ACT_MP_ATTACK_STAND_PRIMARYFIRE, ACT_GRAPPLE_FIRE_START, false },
  97. { ACT_MP_ATTACK_CROUCH_PRIMARYFIRE, ACT_GRAPPLE_FIRE_START, false },
  98. { ACT_MP_ATTACK_SWIM_PRIMARYFIRE, ACT_GRAPPLE_FIRE_START, false },
  99. { ACT_MP_ATTACK_AIRWALK_PRIMARYFIRE, ACT_GRAPPLE_FIRE_START, false },
  100. { ACT_MP_RELOAD_STAND, ACT_MP_RELOAD_STAND_SECONDARY, false },
  101. { ACT_MP_RELOAD_STAND_LOOP, ACT_MP_RELOAD_STAND_SECONDARY_LOOP, false },
  102. { ACT_MP_RELOAD_STAND_END, ACT_MP_RELOAD_STAND_SECONDARY_END, false },
  103. { ACT_MP_RELOAD_CROUCH, ACT_MP_RELOAD_CROUCH_SECONDARY, false },
  104. { ACT_MP_RELOAD_CROUCH_LOOP,ACT_MP_RELOAD_CROUCH_SECONDARY_LOOP,false },
  105. { ACT_MP_RELOAD_CROUCH_END, ACT_MP_RELOAD_CROUCH_SECONDARY_END, false },
  106. { ACT_MP_RELOAD_SWIM, ACT_MP_RELOAD_SWIM_SECONDARY, false },
  107. { ACT_MP_RELOAD_SWIM_LOOP, ACT_MP_RELOAD_SWIM_SECONDARY_LOOP, false },
  108. { ACT_MP_RELOAD_SWIM_END, ACT_MP_RELOAD_SWIM_SECONDARY_END, false },
  109. { ACT_MP_RELOAD_AIRWALK, ACT_MP_RELOAD_AIRWALK_SECONDARY, false },
  110. { ACT_MP_RELOAD_AIRWALK_LOOP, ACT_MP_RELOAD_AIRWALK_SECONDARY_LOOP, false },
  111. { ACT_MP_RELOAD_AIRWALK_END,ACT_MP_RELOAD_AIRWALK_SECONDARY_END,false },
  112. { ACT_MP_GESTURE_FLINCH, ACT_MP_GESTURE_FLINCH_SECONDARY, false },
  113. { ACT_MP_GRENADE1_DRAW, ACT_MP_SECONDARY_GRENADE1_DRAW, false },
  114. { ACT_MP_GRENADE1_IDLE, ACT_MP_SECONDARY_GRENADE1_IDLE, false },
  115. { ACT_MP_GRENADE1_ATTACK, ACT_MP_SECONDARY_GRENADE1_ATTACK, false },
  116. { ACT_MP_GRENADE2_DRAW, ACT_MP_SECONDARY_GRENADE2_DRAW, false },
  117. { ACT_MP_GRENADE2_IDLE, ACT_MP_SECONDARY_GRENADE2_IDLE, false },
  118. { ACT_MP_GRENADE2_ATTACK, ACT_MP_SECONDARY_GRENADE2_ATTACK, false },
  119. { ACT_MP_ATTACK_STAND_GRENADE, ACT_MP_ATTACK_STAND_GRENADE, false },
  120. { ACT_MP_ATTACK_CROUCH_GRENADE, ACT_MP_ATTACK_STAND_GRENADE, false },
  121. { ACT_MP_ATTACK_SWIM_GRENADE, ACT_MP_ATTACK_STAND_GRENADE, false },
  122. { ACT_MP_ATTACK_AIRWALK_GRENADE, ACT_MP_ATTACK_STAND_GRENADE, false },
  123. { ACT_MP_GESTURE_VC_HANDMOUTH, ACT_MP_GESTURE_VC_HANDMOUTH_SECONDARY, false },
  124. { ACT_MP_GESTURE_VC_FINGERPOINT, ACT_MP_GESTURE_VC_FINGERPOINT_SECONDARY, false },
  125. { ACT_MP_GESTURE_VC_FISTPUMP, ACT_MP_GESTURE_VC_FISTPUMP_SECONDARY, false },
  126. { ACT_MP_GESTURE_VC_THUMBSUP, ACT_MP_GESTURE_VC_THUMBSUP_SECONDARY, false },
  127. { ACT_MP_GESTURE_VC_NODYES, ACT_MP_GESTURE_VC_NODYES_SECONDARY, false },
  128. { ACT_MP_GESTURE_VC_NODNO, ACT_MP_GESTURE_VC_NODNO_SECONDARY, false },
  129. };
  130. acttable_t *CTFGrapplingHook::ActivityList( int &iActivityCount )
  131. {
  132. CTFPlayer *pOwner = GetTFPlayerOwner();
  133. if ( pOwner )
  134. {
  135. if ( pOwner->IsPlayerClass( TF_CLASS_ENGINEER ) )
  136. {
  137. iActivityCount = ARRAYSIZE( s_grapplinghook_engineer_acttable );
  138. return s_grapplinghook_engineer_acttable;
  139. }
  140. else
  141. {
  142. iActivityCount = ARRAYSIZE( s_grapplinghook_normal_acttable );
  143. return s_grapplinghook_normal_acttable;
  144. }
  145. }
  146. return BaseClass::ActivityList( iActivityCount );
  147. }
  148. poseparamtable_t s_grapplinghook_normal_poseparamtable[] =
  149. {
  150. { "R_hand_grip", 14 },
  151. { "R_arm", 2 },
  152. };
  153. poseparamtable_t s_grapplinghook_engineer_poseparamtable[] =
  154. {
  155. { "r_handposes_engineer", 1 },
  156. };
  157. poseparamtable_t *CTFGrapplingHook::PoseParamList( int &iPoseParamCount )
  158. {
  159. CTFPlayer *pOwner = GetTFPlayerOwner();
  160. if ( pOwner )
  161. {
  162. if ( pOwner->IsPlayerClass( TF_CLASS_ENGINEER ) )
  163. {
  164. iPoseParamCount = ARRAYSIZE( s_grapplinghook_engineer_poseparamtable );
  165. return s_grapplinghook_engineer_poseparamtable;
  166. }
  167. else
  168. {
  169. iPoseParamCount = ARRAYSIZE( s_grapplinghook_normal_poseparamtable );
  170. return s_grapplinghook_normal_poseparamtable;
  171. }
  172. }
  173. return BaseClass::PoseParamList( iPoseParamCount );
  174. }
  175. ConVar tf_grapplinghook_projectile_speed( "tf_grapplinghook_projectile_speed", "1500", FCVAR_REPLICATED | FCVAR_CHEAT, "How fast does the grappliing hook projectile travel" );
  176. ConVar tf_grapplinghook_max_distance( "tf_grapplinghook_max_distance", "2000", FCVAR_REPLICATED | FCVAR_CHEAT, "Valid distance for grappling hook to travel" );
  177. ConVar tf_grapplinghook_fire_delay( "tf_grapplinghook_fire_delay", "0.5", FCVAR_REPLICATED | FCVAR_CHEAT );
  178. float m_flNextSupernovaDenyWarning = 0.f;
  179. //-----------------------------------------------------------------------------
  180. // Purpose:
  181. //-----------------------------------------------------------------------------
  182. CTFGrapplingHook::CTFGrapplingHook()
  183. {
  184. #ifdef GAME_DLL
  185. m_bReleasedAfterLatched = false;
  186. #endif // GAME_DLL
  187. #ifdef CLIENT_DLL
  188. m_pHookSound = NULL;
  189. m_bLatched = false;
  190. #endif // CLIENT_DLL
  191. }
  192. void CTFGrapplingHook::Precache()
  193. {
  194. BaseClass::Precache();
  195. #ifdef GAME_DLL
  196. PrecacheScriptSound( "WeaponGrapplingHook.Shoot" );
  197. #endif // GAME_DLL
  198. }
  199. //-----------------------------------------------------------------------------
  200. // Purpose:
  201. //-----------------------------------------------------------------------------
  202. CBaseEntity *CTFGrapplingHook::FireProjectile( CTFPlayer *pPlayer )
  203. {
  204. #ifdef GAME_DLL
  205. Assert( m_hProjectile == NULL );
  206. m_hProjectile = BaseClass::FireProjectile( pPlayer );
  207. return m_hProjectile;
  208. #else
  209. return BaseClass::FireProjectile( pPlayer );
  210. #endif // GAME_DLL
  211. }
  212. //-----------------------------------------------------------------------------
  213. // Purpose:
  214. //-----------------------------------------------------------------------------
  215. void CTFGrapplingHook::ItemPostFrame( void )
  216. {
  217. CTFPlayer *pOwner = GetTFPlayerOwner();
  218. if (!pOwner)
  219. return;
  220. #ifdef CLIENT_DLL
  221. static bool bFired = false;
  222. #endif // CLIENT_DLL
  223. CBaseEntity *pHookTarget = pOwner->GetGrapplingHookTarget();
  224. bool bJump = ( pOwner->m_nButtons & IN_JUMP ) > 0;
  225. bool bForceReleaseHook = pHookTarget != NULL && bJump;
  226. if ( !bForceReleaseHook && ( pOwner->m_nButtons & IN_ATTACK || pOwner->IsUsingActionSlot() ) )
  227. {
  228. #ifdef CLIENT_DLL
  229. if ( !bFired )
  230. {
  231. PrimaryAttack();
  232. bFired = true;
  233. }
  234. #else
  235. PrimaryAttack();
  236. #endif // CLIENT_DLL
  237. if ( pOwner->GetGrapplingHookTarget() )
  238. {
  239. SendWeaponAnim( ACT_GRAPPLE_PULL_START );
  240. }
  241. else if ( m_hProjectile )
  242. {
  243. SendWeaponAnim( ACT_VM_PRIMARYATTACK );
  244. }
  245. else
  246. {
  247. SendWeaponAnim( ACT_GRAPPLE_IDLE );
  248. }
  249. }
  250. else
  251. {
  252. #ifdef CLIENT_DLL
  253. bFired = false;
  254. #endif // CLIENT_DLL
  255. OnHookReleased( bForceReleaseHook );
  256. }
  257. if ( pOwner->GetViewModel(0) )
  258. {
  259. pOwner->GetViewModel(0)->SetPlaybackRate( 1.f );
  260. }
  261. if ( pOwner->GetViewModel(1) )
  262. {
  263. pOwner->GetViewModel(1)->SetPlaybackRate( 1.f );
  264. }
  265. BaseClass::ItemPostFrame();
  266. }
  267. //-----------------------------------------------------------------------------
  268. // Purpose:
  269. //-----------------------------------------------------------------------------
  270. bool CTFGrapplingHook::CanAttack( void )
  271. {
  272. CTFPlayer *pPlayer = GetTFPlayerOwner();
  273. if ( pPlayer->m_Shared.IsFeignDeathReady() )
  274. return false;
  275. return BaseClass::CanAttack();
  276. }
  277. //-----------------------------------------------------------------------------
  278. // Purpose:
  279. //-----------------------------------------------------------------------------
  280. void CTFGrapplingHook::PrimaryAttack( void )
  281. {
  282. CTFPlayer *pOwner = GetTFPlayerOwner();
  283. #ifdef GAME_DLL
  284. // make sure to unlatch from the current target and remove the old projectile before we fire a new one
  285. if ( m_bReleasedAfterLatched )
  286. {
  287. RemoveHookProjectile( true );
  288. }
  289. #endif // GAME_DLL
  290. if ( m_hProjectile )
  291. return;
  292. if ( m_flNextPrimaryAttack > gpGlobals->curtime )
  293. return;
  294. if ( pOwner && pOwner->m_Shared.IsControlStunned() )
  295. return;
  296. Vector vecSrc;
  297. QAngle angForward;
  298. Vector vecOffset( 23.5f, -8.0f, -3.0f ); // copied from CTFWeaponBaseGun::FireArrow
  299. GetProjectileFireSetup( pOwner, vecOffset, &vecSrc, &angForward, false );
  300. Vector vecForward;
  301. AngleVectors( angForward, &vecForward );
  302. // check if aiming at skybox
  303. trace_t tr;
  304. UTIL_TraceLine( vecSrc, vecSrc + tf_grapplinghook_max_distance.GetFloat() * vecForward, MASK_SOLID, pOwner, COLLISION_GROUP_DEBRIS, &tr );
  305. if ( !tr.DidHit() || ( tr.fraction < 1.0 && tr.surface.flags & SURF_SKY ) )
  306. {
  307. #ifdef CLIENT_DLL
  308. // play fail sound on client here
  309. if ( pOwner && prediction->IsFirstTimePredicted() )
  310. {
  311. pOwner->EmitSound( "Player.DenyWeaponSelection" );
  312. }
  313. #endif // CLIENT_DLL
  314. return;
  315. }
  316. BaseClass::PrimaryAttack();
  317. m_flNextPrimaryAttack = gpGlobals->curtime + tf_grapplinghook_fire_delay.GetFloat();
  318. }
  319. //-----------------------------------------------------------------------------
  320. // Purpose:
  321. //-----------------------------------------------------------------------------
  322. bool CTFGrapplingHook::Deploy( void )
  323. {
  324. #ifdef GAME_DLL
  325. RemoveHookProjectile( true );
  326. m_bReleasedAfterLatched = IsLatchedToTargetPlayer();
  327. #endif // GAME_DLL
  328. return BaseClass::Deploy();
  329. }
  330. //-----------------------------------------------------------------------------
  331. // Purpose:
  332. //-----------------------------------------------------------------------------
  333. bool CTFGrapplingHook::Holster( CBaseCombatWeapon *pSwitchingTo )
  334. {
  335. #ifdef GAME_DLL
  336. RemoveHookProjectile();
  337. m_bReleasedAfterLatched = IsLatchedToTargetPlayer();
  338. #endif // GAME_DLL
  339. return BaseClass::Holster( pSwitchingTo );
  340. }
  341. //-----------------------------------------------------------------------------
  342. // Purpose:
  343. //-----------------------------------------------------------------------------
  344. void CTFGrapplingHook::GetProjectileFireSetup( CTFPlayer *pPlayer, Vector vecOffset, Vector *vecSrc, QAngle *angForward, bool bHitTeammates /*= true*/, float flEndDist /*= 2000.f*/ )
  345. {
  346. BaseClass::GetProjectileFireSetup( pPlayer, vecOffset, vecSrc, angForward, bHitTeammates, flEndDist );
  347. }
  348. //-----------------------------------------------------------------------------
  349. // Purpose:
  350. //-----------------------------------------------------------------------------
  351. float CTFGrapplingHook::GetProjectileSpeed()
  352. {
  353. CTFPlayer *pOwner = GetTFPlayerOwner();
  354. if ( pOwner && pOwner->m_Shared.GetCarryingRuneType() == RUNE_AGILITY )
  355. {
  356. switch ( pOwner->GetPlayerClass()->GetClassIndex() )
  357. {
  358. case TF_CLASS_SOLDIER:
  359. case TF_CLASS_HEAVYWEAPONS:
  360. return 2600.f;
  361. default:
  362. return 3000.f;
  363. }
  364. }
  365. return tf_grapplinghook_projectile_speed.GetFloat();
  366. }
  367. //-----------------------------------------------------------------------------
  368. // Purpose:
  369. //-----------------------------------------------------------------------------
  370. bool CTFGrapplingHook::SendWeaponAnim( int actBase )
  371. {
  372. CTFPlayer *pPlayer = GetTFPlayerOwner();
  373. if ( !pPlayer )
  374. return BaseClass::SendWeaponAnim( actBase );
  375. if ( actBase == ACT_VM_DRAW )
  376. {
  377. actBase = ACT_GRAPPLE_DRAW;
  378. }
  379. else if ( pPlayer->GetGrapplingHookTarget() )
  380. {
  381. if ( GetActivity() != ACT_GRAPPLE_PULL_START && GetActivity() != ACT_GRAPPLE_PULL_IDLE )
  382. {
  383. bool bResult = BaseClass::SendWeaponAnim( ACT_GRAPPLE_PULL_START );
  384. //DevMsg("pull start %f\n", gpGlobals->curtime );
  385. m_startPullingTimer.Start( SequenceDuration() );
  386. return bResult;
  387. }
  388. else
  389. {
  390. if ( GetActivity() == ACT_GRAPPLE_PULL_IDLE )
  391. return true;
  392. if ( GetActivity() == ACT_GRAPPLE_PULL_START && m_startPullingTimer.HasStarted() && !m_startPullingTimer.IsElapsed() )
  393. return true;
  394. actBase = ACT_GRAPPLE_PULL_IDLE;
  395. //DevMsg("pull idle %f\n", gpGlobals->curtime );
  396. m_startPullingTimer.Invalidate();
  397. }
  398. }
  399. else if ( actBase == ACT_VM_PRIMARYATTACK )
  400. {
  401. if ( GetActivity() != ACT_GRAPPLE_FIRE_START && GetActivity() != ACT_GRAPPLE_FIRE_IDLE )
  402. {
  403. bool bResult = BaseClass::SendWeaponAnim( ACT_GRAPPLE_FIRE_START );
  404. //DevMsg("fire start %f\n", gpGlobals->curtime );
  405. m_startFiringTimer.Start( SequenceDuration() );
  406. return bResult;
  407. }
  408. else
  409. {
  410. if ( GetActivity() == ACT_GRAPPLE_FIRE_IDLE )
  411. return true;
  412. if ( GetActivity() == ACT_GRAPPLE_FIRE_START && m_startFiringTimer.HasStarted() && !m_startFiringTimer.IsElapsed() )
  413. return true;
  414. actBase = ACT_GRAPPLE_FIRE_IDLE;
  415. //DevMsg("fire idle %f\n", gpGlobals->curtime );
  416. m_startFiringTimer.Invalidate();
  417. }
  418. }
  419. else
  420. {
  421. if ( GetActivity() == ACT_GRAPPLE_PULL_IDLE )
  422. {
  423. actBase = ACT_GRAPPLE_PULL_END;
  424. //DevMsg("pull end %f\n", gpGlobals->curtime );
  425. }
  426. else
  427. {
  428. if ( GetActivity() == ACT_GRAPPLE_IDLE )
  429. return true;
  430. actBase = ACT_GRAPPLE_IDLE;
  431. //DevMsg("grapple idle %f\n", gpGlobals->curtime );
  432. }
  433. }
  434. return BaseClass::SendWeaponAnim( actBase );
  435. }
  436. //-----------------------------------------------------------------------------
  437. // Purpose:
  438. //-----------------------------------------------------------------------------
  439. void CTFGrapplingHook::PlayWeaponShootSound( void )
  440. {
  441. #ifdef GAME_DLL
  442. EmitSound( "WeaponGrapplingHook.Shoot" );
  443. #endif // GAME_DLL
  444. }
  445. #ifdef GAME_DLL
  446. //-----------------------------------------------------------------------------
  447. // Purpose:
  448. //-----------------------------------------------------------------------------
  449. void CTFGrapplingHook::ActivateRune()
  450. {
  451. CTFPlayer *pOwner = GetTFPlayerOwner();
  452. if ( pOwner && pOwner->m_Shared.IsRuneCharged() )
  453. {
  454. RuneTypes_t type = pOwner->m_Shared.GetCarryingRuneType();
  455. if ( type == RUNE_SUPERNOVA )
  456. {
  457. // don't allow stealthed player to activate the power
  458. if ( pOwner->m_Shared.IsStealthed() )
  459. return;
  460. int nEnemyTeam = GetEnemyTeam( pOwner->GetTeamNumber() );
  461. // apply super nova effect to all enemies
  462. bool bHitAnyTarget = false;
  463. CUtlVector< CTFPlayer* > vecPlayers;
  464. CUtlVector< CTFPlayer* > vecVictims;
  465. CollectPlayers( &vecPlayers, nEnemyTeam, COLLECT_ONLY_LIVING_PLAYERS );
  466. const float flEffectRadiusSqr = Sqr( 1500.f );
  467. const float flMinPushForce = 200.f;
  468. const float flMaxPushForce = 500.f;
  469. const float flMinStunDuration = 2.f;
  470. const float flMaxStunDuration = 4.f;
  471. for ( int i = 0; i < vecPlayers.Count(); ++i )
  472. {
  473. CTFPlayer *pOther = vecPlayers[i];
  474. Vector toPlayer = pOther->WorldSpaceCenter() - pOwner->WorldSpaceCenter();
  475. float flDistSqr = toPlayer.LengthSqr();
  476. // Collect valid enemies
  477. if ( flDistSqr <= flEffectRadiusSqr && pOwner->IsLineOfSightClear( pOther, CBaseCombatCharacter::IGNORE_ACTORS ) && !PointInRespawnRoom( pOther, pOther->WorldSpaceCenter() ) )
  478. {
  479. vecVictims.AddToTail( pOther );
  480. }
  481. }
  482. // if there is more than one victim, the stun duration increases
  483. float flStunDuration = MIN( flMinStunDuration + ( ( vecVictims.Count() - 1 ) * 0.5 ), flMaxStunDuration );
  484. for ( int i = 0; i < vecVictims.Count(); ++i )
  485. {
  486. // force enemy to drop rune, stun, and push them
  487. CTFPlayer *pOther = vecVictims[i];
  488. const char *pszEffect = pOwner->GetTeamNumber() == TF_TEAM_RED ? "powerup_supernova_strike_red" : "powerup_supernova_strike_blue";
  489. CPVSFilter filter( WorldSpaceCenter() );
  490. Vector vStart = pOwner->WorldSpaceCenter();
  491. Vector vEnd = pOther->GetAbsOrigin() + Vector( 0, 0, 56 );
  492. te_tf_particle_effects_control_point_t controlPoint = { PATTACH_ABSORIGIN, vEnd };
  493. TE_TFParticleEffectComplex( filter, 0.f, pszEffect, vStart, QAngle( 0.f, 0.f, 0.f ), NULL, &controlPoint, pOther, PATTACH_CUSTOMORIGIN );
  494. pOther->DropRune( false, pOwner->GetTeamNumber() );
  495. pOther->DropFlag();
  496. pOther->m_Shared.StunPlayer( flStunDuration, 1.f, TF_STUN_MOVEMENT | TF_STUN_CONTROLS, pOwner );
  497. // send the player flying
  498. // make sure we push players up and away
  499. Vector toPlayer = pOther->WorldSpaceCenter() - pOwner->WorldSpaceCenter();
  500. toPlayer.z = 0.0f;
  501. toPlayer.NormalizeInPlace();
  502. toPlayer.z = 1.0f;
  503. // scale push force based on distance from the supernova origin
  504. float flDistSqr = toPlayer.LengthSqr();
  505. float flPushForce = RemapValClamped( flDistSqr, 0.f, flEffectRadiusSqr, flMaxPushForce, flMinPushForce );
  506. Vector vPush = flPushForce * toPlayer;
  507. pOther->ApplyAbsVelocityImpulse( vPush );
  508. bHitAnyTarget = true;
  509. }
  510. // don't deploy with no target
  511. if ( bHitAnyTarget )
  512. {
  513. // play effect
  514. const char *pszEffect = pOwner->GetTeamNumber() == TF_TEAM_RED ? "powerup_supernova_explode_red" : "powerup_supernova_explode_blue";
  515. CEffectData data;
  516. data.m_nHitBox = GetParticleSystemIndex( pszEffect );
  517. data.m_vOrigin = pOwner->GetAbsOrigin();
  518. data.m_vAngles = vec3_angle;
  519. CPASFilter filter( data.m_vOrigin );
  520. filter.SetIgnorePredictionCull( true );
  521. te->DispatchEffect( filter, 0.0, data.m_vOrigin, "ParticleEffect", data );
  522. pOwner->EmitSound( "Powerup.PickUpSupernovaActivate" );
  523. // remove the power and reposition instantly
  524. pOwner->m_Shared.SetCarryingRuneType( RUNE_NONE );
  525. CTFRune::RepositionRune( type, TEAM_ANY );
  526. }
  527. else
  528. {
  529. if ( gpGlobals->curtime > m_flNextSupernovaDenyWarning )
  530. {
  531. m_flNextSupernovaDenyWarning = gpGlobals->curtime + 0.5f;
  532. CSingleUserRecipientFilter singleFilter( pOwner );
  533. EmitSound( singleFilter, pOwner->entindex(), "Player.UseDeny" );
  534. ClientPrint( pOwner, HUD_PRINTCENTER, "#TF_Powerup_Supernova_Deny" );
  535. }
  536. }
  537. }
  538. }
  539. }
  540. //-----------------------------------------------------------------------------
  541. // Purpose:
  542. //-----------------------------------------------------------------------------
  543. void CTFGrapplingHook::RemoveHookProjectile( bool bForce /*= false*/ )
  544. {
  545. if ( !bForce && IsLatchedToTargetPlayer() )
  546. {
  547. // don't remove the projectile until we unlatched from the target (by hooking again)
  548. return;
  549. }
  550. if ( m_hProjectile )
  551. {
  552. UTIL_Remove( m_hProjectile );
  553. m_hProjectile = NULL;
  554. }
  555. }
  556. bool CTFGrapplingHook::IsLatchedToTargetPlayer() const
  557. {
  558. CTFPlayer *pOwner = GetTFPlayerOwner();
  559. return pOwner && pOwner->GetGrapplingHookTarget() && pOwner->GetGrapplingHookTarget()->IsPlayer();
  560. }
  561. #endif // GAME_DLL
  562. void CTFGrapplingHook::OnHookReleased( bool bForce )
  563. {
  564. #ifdef GAME_DLL
  565. RemoveHookProjectile( bForce );
  566. m_bReleasedAfterLatched = IsLatchedToTargetPlayer();
  567. #endif // GAME_DLL
  568. if ( GetActivity() != ACT_GRAPPLE_DRAW && GetActivity() != ACT_GRAPPLE_IDLE && GetActivity() != ACT_GRAPPLE_PULL_END )
  569. SendWeaponAnim( ACT_GRAPPLE_PULL_END );
  570. if ( bForce )
  571. m_flNextPrimaryAttack = gpGlobals->curtime + tf_grapplinghook_fire_delay.GetFloat();
  572. }
  573. #ifdef CLIENT_DLL
  574. //-----------------------------------------------------------------------------
  575. // Purpose:
  576. //-----------------------------------------------------------------------------
  577. void CTFGrapplingHook::UpdateOnRemove()
  578. {
  579. StopHookSound();
  580. BaseClass::UpdateOnRemove();
  581. }
  582. //-----------------------------------------------------------------------------
  583. // Purpose:
  584. //-----------------------------------------------------------------------------
  585. void CTFGrapplingHook::OnDataChanged( DataUpdateType_t type )
  586. {
  587. BaseClass::OnDataChanged( type );
  588. UpdateHookSound();
  589. }
  590. //-----------------------------------------------------------------------------
  591. // Purpose:
  592. //-----------------------------------------------------------------------------
  593. void CTFGrapplingHook::StartHookSound()
  594. {
  595. StopHookSound();
  596. CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
  597. CLocalPlayerFilter filter;
  598. m_pHookSound = controller.SoundCreate( filter, entindex(), "WeaponGrapplingHook.ReelStart" );
  599. controller.Play( m_pHookSound, 1.0, 100 );
  600. }
  601. //-----------------------------------------------------------------------------
  602. // Purpose:
  603. //-----------------------------------------------------------------------------
  604. void CTFGrapplingHook::StopHookSound()
  605. {
  606. if ( m_pHookSound )
  607. {
  608. CSoundEnvelopeController::GetController().SoundDestroy( m_pHookSound );
  609. m_pHookSound = NULL;
  610. }
  611. }
  612. //-----------------------------------------------------------------------------
  613. // Purpose:
  614. //-----------------------------------------------------------------------------
  615. void CTFGrapplingHook::UpdateHookSound()
  616. {
  617. CTFPlayer *pOwner = GetTFPlayerOwner();
  618. if ( pOwner )
  619. {
  620. bool bLatched = pOwner->GetGrapplingHookTarget() != NULL && m_hProjectile != NULL;
  621. if ( m_bLatched != bLatched )
  622. {
  623. if ( !m_bLatched )
  624. {
  625. StartHookSound();
  626. }
  627. else
  628. {
  629. StopHookSound();
  630. CLocalPlayerFilter filter;
  631. EmitSound( filter, entindex(), "WeaponGrapplingHook.ReelStop" );
  632. }
  633. m_bLatched = bLatched;
  634. }
  635. }
  636. }
  637. //-----------------------------------------------------------------------------
  638. // CEquipGrapplingHookNotification
  639. //-----------------------------------------------------------------------------
  640. void CEquipGrapplingHookNotification::Accept()
  641. {
  642. m_bHasTriggered = true;
  643. CPlayerInventory *pLocalInv = TFInventoryManager()->GetLocalInventory();
  644. if ( !pLocalInv )
  645. {
  646. MarkForDeletion();
  647. return;
  648. }
  649. C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
  650. if ( !pLocalPlayer )
  651. {
  652. MarkForDeletion();
  653. return;
  654. }
  655. // try to equip non-stock-grapplinghook first
  656. /*static CSchemaItemDefHandle pItemDef_GrapplingHook( "TF_WEAPON_GRAPPLINGHOOK" );
  657. Assert( pItemDef_GrapplingHook );
  658. CEconItemView *pGrapplingHook = NULL;
  659. if ( pItemDef_GrapplingHook )
  660. {
  661. for ( int i = 0 ; i < pLocalInv->GetItemCount() ; ++i )
  662. {
  663. CEconItemView *pItem = pLocalInv->GetItem( i );
  664. Assert( pItem );
  665. if ( pItem->GetItemDefinition() == pItemDef_GrapplingHook )
  666. {
  667. pGrapplingHook = pItem;
  668. break;
  669. }
  670. }
  671. }*/
  672. // Default item becomes a grappling hook in this mode
  673. itemid_t iItemId = INVALID_ITEM_ID;
  674. /*if ( pGrapplingHook )
  675. {
  676. iItemId = pGrapplingHook->GetItemID();
  677. }*/
  678. if ( iItemId == INVALID_ITEM_ID )
  679. {
  680. iItemId = 0;
  681. static CSchemaItemDefHandle pItemDef_Grapple( "TF_WEAPON_GRAPPLINGHOOK" );
  682. CEconItemView *pDefaultGrapple = TFInventoryManager()->GetBaseItemForClass( pLocalPlayer->GetPlayerClass()->GetClassIndex(), LOADOUT_POSITION_ACTION );
  683. if ( pDefaultGrapple )
  684. {
  685. if ( pDefaultGrapple->GetItemDefinition() == pItemDef_Grapple )
  686. {
  687. iItemId = pDefaultGrapple->GetItemID();
  688. }
  689. }
  690. }
  691. TFInventoryManager()->EquipItemInLoadout( pLocalPlayer->GetPlayerClass()->GetClassIndex(), LOADOUT_POSITION_ACTION, iItemId );
  692. // Tell the GC to tell server that we should respawn if we're in a respawn room
  693. GCSDK::CGCMsg< GCSDK::MsgGCEmpty_t > msg( k_EMsgGCRespawnPostLoadoutChange );
  694. GCClientSystem()->BSendMessage( msg );
  695. MarkForDeletion();
  696. }
  697. //===========================================================================================
  698. void CEquipGrapplingHookNotification::UpdateTick()
  699. {
  700. C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
  701. if ( pLocalPlayer )
  702. {
  703. CTFGrapplingHook *pGrapplingHook = dynamic_cast<CTFGrapplingHook*>( pLocalPlayer->Weapon_OwnsThisID( TF_WEAPON_GRAPPLINGHOOK ) );
  704. if ( pGrapplingHook )
  705. {
  706. MarkForDeletion();
  707. }
  708. }
  709. }
  710. #endif // CLIENT_DLL