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.

690 lines
20 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Weapon Knife.
  4. //
  5. //=============================================================================
  6. #include "cbase.h"
  7. #include "tf_gamerules.h"
  8. #include "tf_weapon_knife.h"
  9. #include "decals.h"
  10. #include "debugoverlay_shared.h"
  11. // Client specific.
  12. #ifdef CLIENT_DLL
  13. #include "c_tf_player.h"
  14. #include "c_tf_gamestats.h"
  15. // Server specific.
  16. #else
  17. #include "tf_player.h"
  18. #include "tf_gamestats.h"
  19. #include "ilagcompensationmanager.h"
  20. #endif
  21. //=============================================================================
  22. //
  23. // Weapon Knife tables.
  24. //
  25. IMPLEMENT_NETWORKCLASS_ALIASED( TFKnife, DT_TFWeaponKnife )
  26. BEGIN_NETWORK_TABLE( CTFKnife, DT_TFWeaponKnife )
  27. #if defined( CLIENT_DLL )
  28. RecvPropBool( RECVINFO( m_bReadyToBackstab ) ),
  29. RecvPropBool( RECVINFO( m_bKnifeExists ) ),
  30. RecvPropFloat( RECVINFO( m_flKnifeRegenerateDuration ) ),
  31. RecvPropFloat( RECVINFO( m_flKnifeMeltTimestamp ) ),
  32. #else
  33. SendPropBool( SENDINFO( m_bReadyToBackstab ) ),
  34. SendPropBool( SENDINFO( m_bKnifeExists ) ),
  35. SendPropFloat( SENDINFO( m_flKnifeRegenerateDuration ), 0, SPROP_NOSCALE ),
  36. SendPropFloat( SENDINFO( m_flKnifeMeltTimestamp ), 0, SPROP_NOSCALE ),
  37. #endif
  38. END_NETWORK_TABLE()
  39. BEGIN_PREDICTION_DATA( CTFKnife )
  40. #ifdef CLIENT_DLL
  41. DEFINE_PRED_FIELD( m_bReadyToBackstab, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
  42. #endif
  43. END_PREDICTION_DATA()
  44. LINK_ENTITY_TO_CLASS( tf_weapon_knife, CTFKnife );
  45. PRECACHE_WEAPON_REGISTER( tf_weapon_knife );
  46. //=============================================================================
  47. //
  48. // Weapon Knife functions.
  49. //
  50. //-----------------------------------------------------------------------------
  51. // Purpose:
  52. //-----------------------------------------------------------------------------
  53. CTFKnife::CTFKnife()
  54. {
  55. m_bReadyToBackstab = false;
  56. m_flBlockedTime = 0.f;
  57. m_bAllowHolsterBecauseForced = false;
  58. ResetVars();
  59. }
  60. //-----------------------------------------------------------------------------
  61. // Purpose:
  62. //-----------------------------------------------------------------------------
  63. void CTFKnife::ResetVars( void )
  64. {
  65. m_bKnifeExists = true;
  66. m_flKnifeRegenerateDuration = 1.0f;
  67. m_flKnifeMeltTimestamp = 0.0f;
  68. m_bWasTaunting = false;
  69. m_bAllowHolsterBecauseForced = false;
  70. }
  71. //-----------------------------------------------------------------------------
  72. // Purpose: We are regenerating (ie: resupply cabinet)
  73. //-----------------------------------------------------------------------------
  74. void CTFKnife::WeaponRegenerate( void )
  75. {
  76. BaseClass::WeaponRegenerate();
  77. ResetVars();
  78. }
  79. //-----------------------------------------------------------------------------
  80. // Purpose:
  81. //-----------------------------------------------------------------------------
  82. void CTFKnife::WeaponReset( void )
  83. {
  84. BaseClass::WeaponReset();
  85. ResetVars();
  86. }
  87. //-----------------------------------------------------------------------------
  88. bool CTFKnife::DoSwingTrace( trace_t &trace )
  89. {
  90. return BaseClass::DoSwingTrace( trace );
  91. }
  92. #ifdef GAME_DLL
  93. //-----------------------------------------------------------------------------
  94. // Purpose:
  95. //-----------------------------------------------------------------------------
  96. void CTFKnife::ApplyOnInjuredAttributes( CTFPlayer *pVictim, CTFPlayer *pAttacker, const CTakeDamageInfo &info )
  97. {
  98. BaseClass::ApplyOnInjuredAttributes( pVictim, pAttacker, info );
  99. int iMeltsInFire = 0;
  100. CALL_ATTRIB_HOOK_INT( iMeltsInFire, melts_in_fire );
  101. if ( iMeltsInFire > 0 && info.GetDamageType() & DMG_BURN )
  102. {
  103. if ( m_bKnifeExists )
  104. {
  105. // melt it!
  106. m_bKnifeExists = false;
  107. m_flKnifeRegenerateDuration = iMeltsInFire;
  108. m_flKnifeMeltTimestamp = gpGlobals->curtime;
  109. CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() );
  110. if ( pPlayer )
  111. {
  112. pPlayer->EmitSound( "Icicle.Melt" );
  113. // force switch to sapper
  114. // Set flag to allow holstering during this forced switch (holstering might otherwise have been inhibited by being blocked). This addresses
  115. // a corner-case where a Spy stabbing a Razorback-equipped sniper with the Spycicle and then immediately burned doesn't switch away and could backstab immediately
  116. // even though their knife should have melted.
  117. CBaseCombatWeapon *mySapper = pPlayer->Weapon_GetWeaponByType( TF_WPN_TYPE_BUILDING );
  118. m_bAllowHolsterBecauseForced = true;
  119. if ( !mySapper )
  120. {
  121. // this should never happen
  122. pPlayer->SwitchToNextBestWeapon( this );
  123. }
  124. else
  125. {
  126. pPlayer->Weapon_Switch( mySapper );
  127. }
  128. m_bAllowHolsterBecauseForced = false;
  129. }
  130. }
  131. }
  132. }
  133. //-----------------------------------------------------------------------------
  134. bool CTFKnife::DecreaseRegenerationTime( float value, bool bForce )
  135. {
  136. // didn't do anything
  137. if ( m_bKnifeExists )
  138. return false;
  139. float flTime = value * 0.005f * m_flKnifeRegenerateDuration;
  140. m_flKnifeMeltTimestamp -= flTime;
  141. return true;
  142. }
  143. #endif
  144. //-----------------------------------------------------------------------------
  145. // Purpose: Set stealth attack bool
  146. //-----------------------------------------------------------------------------
  147. void CTFKnife::PrimaryAttack( void )
  148. {
  149. CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() );
  150. if ( !CanAttack() )
  151. return;
  152. // Set the weapon usage mode - primary, secondary.
  153. m_iWeaponMode = TF_WEAPON_PRIMARY_MODE;
  154. m_hBackstabVictim = NULL;
  155. int iBackstabVictimHealth = 0;
  156. #if !defined (CLIENT_DLL)
  157. // Move other players back to history positions based on local player's lag
  158. lagcompensation->StartLagCompensation( pPlayer, pPlayer->GetCurrentCommand() );
  159. #endif
  160. trace_t trace;
  161. if ( DoSwingTrace( trace ) == true )
  162. {
  163. // we will hit something with the attack
  164. if( trace.m_pEnt && trace.m_pEnt->IsPlayer() )
  165. {
  166. CTFPlayer *pTarget = ToTFPlayer( trace.m_pEnt );
  167. if ( pTarget && pTarget->GetTeamNumber() != pPlayer->GetTeamNumber() )
  168. {
  169. // Deal extra damage to players when stabbing them from behind
  170. if ( CanPerformBackstabAgainstTarget( pTarget ) )
  171. {
  172. // store the victim to compare when we do the damage
  173. m_hBackstabVictim.Set( pTarget );
  174. iBackstabVictimHealth = Max( m_hBackstabVictim->GetHealth(), 75 );
  175. }
  176. }
  177. }
  178. }
  179. #ifndef CLIENT_DLL
  180. pPlayer->RemoveInvisibility();
  181. lagcompensation->FinishLagCompensation( pPlayer );
  182. #endif
  183. // Swing the weapon.
  184. Swing( pPlayer );
  185. Smack();
  186. m_flSmackTime = -1.0f;
  187. m_bReadyToBackstab = false; // Hand is down.
  188. #if !defined( CLIENT_DLL )
  189. pPlayer->SpeakWeaponFire();
  190. CTF_GameStats.Event_PlayerFiredWeapon( pPlayer, IsCurrentAttackACrit() );
  191. #endif
  192. #ifdef CLIENT_DLL
  193. C_CTF_GameStats.Event_PlayerFiredWeapon( pPlayer, IsCurrentAttackACrit() );
  194. #endif
  195. bool bSuccessfulBackstab = IsBackstab() && !m_hBackstabVictim->IsAlive();
  196. ETFFlagType ignoreTypes[] = { TF_FLAGTYPE_PLAYER_DESTRUCTION };
  197. if ( ShouldDisguiseOnBackstab() && bSuccessfulBackstab && !pPlayer->HasTheFlag( ignoreTypes, ARRAYSIZE( ignoreTypes ) ) )
  198. {
  199. // Different rules in MvM when stabbing bots
  200. bool bMvM = TFGameRules() && TFGameRules()->IsMannVsMachineMode() && m_hBackstabVictim->IsBot();
  201. if ( bMvM )
  202. {
  203. // Remove the disguise first, otherwise this attribute is overpowered
  204. pPlayer->RemoveDisguise();
  205. }
  206. // We should very quickly disguise as our victim.
  207. const float flDelay = bMvM ? 1.5f : 0.2f;
  208. SetContextThink( &CTFKnife::DisguiseOnKill, gpGlobals->curtime + flDelay, "DisguiseOnKill" );
  209. }
  210. else
  211. {
  212. pPlayer->RemoveDisguise();
  213. }
  214. #ifdef GAME_DLL
  215. int iSanguisuge = 0;
  216. CALL_ATTRIB_HOOK_INT( iSanguisuge, sanguisuge );
  217. if ( bSuccessfulBackstab && iSanguisuge > 0 )
  218. {
  219. // Our health cap is 3x our default maximum health cap. This is so high to make up for
  220. // the fact that our default is lowered by equipping the weapon.
  221. int iBaseMaxHealth = pPlayer->GetMaxHealth() * 3,
  222. iNewHealth = MIN( pPlayer->GetHealth() + iBackstabVictimHealth, iBaseMaxHealth ),
  223. iDeltaHealth = iNewHealth - pPlayer->GetHealth();
  224. if ( iDeltaHealth > 0 )
  225. {
  226. pPlayer->TakeHealth( iDeltaHealth, DMG_IGNORE_MAXHEALTH );
  227. pPlayer->m_Shared.HealthKitPickupEffects( iDeltaHealth );
  228. }
  229. }
  230. #endif // GAME_DLL
  231. }
  232. //-----------------------------------------------------------------------------
  233. // Purpose:
  234. //-----------------------------------------------------------------------------
  235. void CTFKnife::DisguiseOnKill()
  236. {
  237. #ifdef GAME_DLL
  238. if ( !m_hBackstabVictim.Get() )
  239. return;
  240. int nTeam = m_hBackstabVictim->GetTeamNumber();
  241. int nClass = m_hBackstabVictim->GetPlayerClass()->GetClassIndex();
  242. CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() );
  243. if ( pPlayer )
  244. {
  245. pPlayer->m_Shared.Disguise( nTeam, nClass, m_hBackstabVictim.Get(), true );
  246. }
  247. #endif
  248. }
  249. //-----------------------------------------------------------------------------
  250. // Purpose:
  251. //-----------------------------------------------------------------------------
  252. bool CTFKnife::ShouldDisguiseOnBackstab()
  253. {
  254. int iDisguiseAsVictim = 0;
  255. CALL_ATTRIB_HOOK_INT( iDisguiseAsVictim, set_disguise_on_backstab );
  256. if ( iDisguiseAsVictim == 1 )
  257. return true;
  258. else
  259. return false;
  260. }
  261. //-----------------------------------------------------------------------------
  262. // Purpose: Do backstab damage
  263. //-----------------------------------------------------------------------------
  264. float CTFKnife::GetMeleeDamage( CBaseEntity *pTarget, int* piDamageType, int* piCustomDamage )
  265. {
  266. float flBaseDamage = BaseClass::GetMeleeDamage( pTarget, piDamageType, piCustomDamage );
  267. CTFPlayer *pTFOwner = ToTFPlayer( GetPlayerOwner() );
  268. if ( !pTFOwner )
  269. return false;
  270. if ( pTarget->IsPlayer() )
  271. {
  272. if ( IsBackstab() )
  273. {
  274. CTFPlayer *pTFTarget = ToTFPlayer( pTarget );
  275. // Special rules in modes where player power grows significantly
  276. if ( !pTFOwner->IsBot() && pTFTarget && pTFTarget->IsMiniBoss() )
  277. {
  278. // MvM: Cap damage against bots and check for a damage upgrade
  279. float flBonusDmg = 1.f;
  280. CALL_ATTRIB_HOOK_FLOAT( flBonusDmg, mult_dmg );
  281. flBaseDamage = 250.f * flBonusDmg;
  282. // Minibosses: Adjust damage when backstabbing based on level of armor piercing
  283. // Base amount is 25% of normal damage. Each level adds 25% to a cap of 125%.
  284. float flArmorPiercing = 25.f;
  285. CALL_ATTRIB_HOOK_FLOAT_ON_OTHER( pTFOwner, flArmorPiercing, armor_piercing );
  286. flBaseDamage *= clamp( flArmorPiercing / 100.0f, 0.25f, 1.25f );
  287. }
  288. else // Regular game mode, or the attacker is a bot
  289. {
  290. // Do twice the target's health so that random modification will still kill him.
  291. flBaseDamage = pTarget->GetHealth() * 2;
  292. }
  293. // Declare a backstab.
  294. *piCustomDamage = TF_DMG_CUSTOM_BACKSTAB;
  295. }
  296. else if (pTFOwner->m_Shared.IsCritBoosted())
  297. {
  298. m_bCurrentAttackIsCrit = true;
  299. }
  300. else
  301. {
  302. m_bCurrentAttackIsCrit = false; // don't do a crit if we failed the above checks.
  303. }
  304. }
  305. return flBaseDamage;
  306. }
  307. //-----------------------------------------------------------------------------
  308. // Purpose: Are we in a backstab position?
  309. //-----------------------------------------------------------------------------
  310. bool CTFKnife::CanPerformBackstabAgainstTarget( CTFPlayer *pTarget )
  311. {
  312. if ( !pTarget )
  313. return false;
  314. // Immune?
  315. int iNoBackstab = 0;
  316. CALL_ATTRIB_HOOK_INT_ON_OTHER( pTarget, iNoBackstab, cannot_be_backstabbed );
  317. if ( iNoBackstab )
  318. return false;
  319. // Behind and facing target's back?
  320. if ( IsBehindAndFacingTarget( pTarget ) )
  321. return true;
  322. // Is target (bot) disabled via a sapper?
  323. if ( TFGameRules() && TFGameRules()->IsMannVsMachineMode() && pTarget->GetTeamNumber() == TF_TEAM_PVE_INVADERS )
  324. {
  325. if ( pTarget->m_Shared.InCond( TF_COND_MVM_BOT_STUN_RADIOWAVE ) )
  326. return true;
  327. if ( pTarget->m_Shared.InCond( TF_COND_SAPPED ) && !pTarget->IsMiniBoss() )
  328. return true;
  329. }
  330. return false;
  331. }
  332. //-----------------------------------------------------------------------------
  333. // Purpose: Determine if we are reasonably facing our target.
  334. //-----------------------------------------------------------------------------
  335. bool CTFKnife::IsBehindAndFacingTarget( CTFPlayer *pTarget )
  336. {
  337. CTFPlayer *pOwner = ToTFPlayer( GetPlayerOwner() );
  338. if ( !pOwner )
  339. return false;
  340. // Get a vector from owner origin to target origin
  341. Vector vecToTarget;
  342. vecToTarget = pTarget->WorldSpaceCenter() - pOwner->WorldSpaceCenter();
  343. vecToTarget.z = 0.0f;
  344. vecToTarget.NormalizeInPlace();
  345. // Get owner forward view vector
  346. Vector vecOwnerForward;
  347. AngleVectors( pOwner->EyeAngles(), &vecOwnerForward, NULL, NULL );
  348. vecOwnerForward.z = 0.0f;
  349. vecOwnerForward.NormalizeInPlace();
  350. // Get target forward view vector
  351. Vector vecTargetForward;
  352. AngleVectors( pTarget->EyeAngles(), &vecTargetForward, NULL, NULL );
  353. vecTargetForward.z = 0.0f;
  354. vecTargetForward.NormalizeInPlace();
  355. // Make sure owner is behind, facing and aiming at target's back
  356. float flPosVsTargetViewDot = DotProduct( vecToTarget, vecTargetForward ); // Behind?
  357. float flPosVsOwnerViewDot = DotProduct( vecToTarget, vecOwnerForward ); // Facing?
  358. float flViewAnglesDot = DotProduct( vecTargetForward, vecOwnerForward ); // Facestab?
  359. // Debug
  360. // NDebugOverlay::HorzArrow( pTarget->WorldSpaceCenter(), pTarget->WorldSpaceCenter() + 50.0f * vecTargetForward, 5.0f, 0, 255, 0, 255, true, NDEBUG_PERSIST_TILL_NEXT_SERVER );
  361. // NDebugOverlay::HorzArrow( pOwner->WorldSpaceCenter(), pOwner->WorldSpaceCenter() + 50.0f * vecOwnerForward, 5.0f, 0, 255, 0, 255, true, NDEBUG_PERSIST_TILL_NEXT_SERVER );
  362. // DevMsg( "PosDot: %3.2f FacingDot: %3.2f AnglesDot: %3.2f\n", flPosVsTargetViewDot, flPosVsOwnerViewDot, flViewAnglesDot );
  363. return ( flPosVsTargetViewDot > 0.f && flPosVsOwnerViewDot > 0.5 && flViewAnglesDot > -0.3f );
  364. }
  365. //-----------------------------------------------------------------------------
  366. // Purpose:
  367. //-----------------------------------------------------------------------------
  368. bool CTFKnife::CalcIsAttackCriticalHelper( void )
  369. {
  370. // Always crit from behind, never from front
  371. return IsBackstab();
  372. }
  373. //-----------------------------------------------------------------------------
  374. // Purpose:
  375. //-----------------------------------------------------------------------------
  376. bool CTFKnife::CalcIsAttackCriticalHelperNoCrits( void )
  377. {
  378. // Always crit from behind, never from front
  379. return IsBackstab();
  380. }
  381. //-----------------------------------------------------------------------------
  382. // Purpose: Allow melee weapons to send different anim events
  383. // Input : -
  384. //-----------------------------------------------------------------------------
  385. void CTFKnife::SendPlayerAnimEvent( CTFPlayer *pPlayer )
  386. {
  387. if ( IsBackstab() )
  388. {
  389. pPlayer->DoAnimationEvent( PLAYERANIMEVENT_CUSTOM_GESTURE, ACT_MP_ATTACK_STAND_SECONDARYFIRE );
  390. }
  391. else
  392. {
  393. pPlayer->DoAnimationEvent( PLAYERANIMEVENT_ATTACK_PRIMARY );
  394. }
  395. }
  396. //-----------------------------------------------------------------------------
  397. // Purpose:
  398. //-----------------------------------------------------------------------------
  399. bool CTFKnife::CanDeploy( void )
  400. {
  401. m_bKnifeExists = ( gpGlobals->curtime - m_flKnifeMeltTimestamp > m_flKnifeRegenerateDuration );
  402. if ( m_bKnifeExists == false )
  403. {
  404. // melted icicle has not yet regenerated
  405. return false;
  406. }
  407. return BaseClass::CanDeploy();
  408. }
  409. //-----------------------------------------------------------------------------
  410. // Purpose:
  411. //-----------------------------------------------------------------------------
  412. bool CTFKnife::Deploy( void )
  413. {
  414. bool bDeployed = BaseClass::Deploy();
  415. m_bReadyToBackstab = false;
  416. m_bKnifeExists = true;
  417. return bDeployed;
  418. }
  419. //-----------------------------------------------------------------------------
  420. // Purpose:
  421. //-----------------------------------------------------------------------------
  422. void CTFKnife::ItemPreFrame( void )
  423. {
  424. BaseClass::ItemPreFrame();
  425. ProcessDisguiseImpulse();
  426. }
  427. //-----------------------------------------------------------------------------
  428. // Purpose:
  429. //-----------------------------------------------------------------------------
  430. void CTFKnife::ItemPostFrame( void )
  431. {
  432. CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() );
  433. if ( pPlayer )
  434. {
  435. if ( pPlayer->m_Shared.InCond( TF_COND_TAUNTING ) )
  436. {
  437. m_bWasTaunting = true;
  438. }
  439. else if ( m_bWasTaunting )
  440. {
  441. // we were taunting and now we're not
  442. m_bWasTaunting = false;
  443. // force switch away from knife if we can't deploy it right now
  444. if ( !CanDeploy() )
  445. {
  446. // force switch to sapper
  447. CBaseCombatWeapon *mySapper = pPlayer->Weapon_GetWeaponByType( TF_WPN_TYPE_BUILDING );
  448. if ( !mySapper )
  449. {
  450. // this should never happen
  451. pPlayer->SwitchToNextBestWeapon( this );
  452. }
  453. else
  454. {
  455. pPlayer->Weapon_Switch( mySapper );
  456. }
  457. }
  458. }
  459. }
  460. BackstabVMThink();
  461. BaseClass::ItemPostFrame();
  462. }
  463. //-----------------------------------------------------------------------------
  464. // Purpose:
  465. //-----------------------------------------------------------------------------
  466. void CTFKnife::ItemBusyFrame( void )
  467. {
  468. BaseClass::ItemBusyFrame();
  469. ProcessDisguiseImpulse();
  470. }
  471. //-----------------------------------------------------------------------------
  472. // Purpose:
  473. //-----------------------------------------------------------------------------
  474. void CTFKnife::ItemHolsterFrame( void )
  475. {
  476. BaseClass::ItemHolsterFrame();
  477. ProcessDisguiseImpulse();
  478. }
  479. //-----------------------------------------------------------------------------
  480. // Purpose: Special case handling of 'Your Eternal Reward' input polling
  481. //-----------------------------------------------------------------------------
  482. void CTFKnife::ProcessDisguiseImpulse( void )
  483. {
  484. CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() );
  485. if ( !pPlayer )
  486. return;
  487. // Only care about this with the right weapon
  488. if ( GetKnifeType() != KNIFE_DISGUISE_ONKILL )
  489. return;
  490. // If we're not already disguised, ignore
  491. if ( !pPlayer->m_Shared.InCond( TF_COND_DISGUISED ) )
  492. return;
  493. pPlayer->m_Shared.ProcessDisguiseImpulse( pPlayer );
  494. }
  495. //-----------------------------------------------------------------------------
  496. // Purpose:
  497. //-----------------------------------------------------------------------------
  498. void CTFKnife::BackstabVMThink( void )
  499. {
  500. CTFPlayer *pPlayer = ToTFPlayer( GetPlayerOwner() );
  501. if ( !pPlayer )
  502. return;
  503. if ( pPlayer->GetActiveWeapon() != this )
  504. return;
  505. // Don't do this if we are doing something other than idling.
  506. // int iIdealActivity = GetIdealActivity();
  507. // if ( (iIdealActivity != ACT_VM_IDLE) && (iIdealActivity != ACT_BACKSTAB_VM_IDLE) )
  508. // return;
  509. int iActivity = GetActivity();
  510. if ( (iActivity != ACT_VM_IDLE) && (iActivity != ACT_BACKSTAB_VM_IDLE) && (iActivity != ACT_MELEE_VM_IDLE) &&
  511. (iActivity != ACT_ITEM1_VM_IDLE) && (iActivity != ACT_ITEM1_BACKSTAB_VM_IDLE) &&
  512. (iActivity != ACT_ITEM2_VM_IDLE) && (iActivity != ACT_ITEM2_BACKSTAB_VM_IDLE) )
  513. return;
  514. // Are we in backstab range and not cloaked?
  515. trace_t trace;
  516. if ( DoSwingTrace( trace ) == true && CanAttack() )
  517. {
  518. // We will hit something if we attack.
  519. if( trace.m_pEnt && trace.m_pEnt->IsPlayer() )
  520. {
  521. CTFPlayer *pTarget = ToTFPlayer( trace.m_pEnt );
  522. if ( pTarget && pTarget->GetTeamNumber() != pPlayer->GetTeamNumber() )
  523. {
  524. if ( CanPerformBackstabAgainstTarget( pTarget ) )
  525. {
  526. if ( !m_bReadyToBackstab )
  527. {
  528. SendWeaponAnim( ACT_BACKSTAB_VM_UP );
  529. m_bReadyToBackstab = true;
  530. }
  531. }
  532. else if ( m_bReadyToBackstab )
  533. {
  534. SendWeaponAnim( ACT_BACKSTAB_VM_DOWN );
  535. m_bReadyToBackstab = false;
  536. }
  537. }
  538. }
  539. }
  540. else if ( m_bReadyToBackstab )
  541. {
  542. SendWeaponAnim( ACT_BACKSTAB_VM_DOWN );
  543. m_bReadyToBackstab = false;
  544. }
  545. }
  546. //-----------------------------------------------------------------------------
  547. // Purpose: Play animation appropriate to ball status.
  548. //-----------------------------------------------------------------------------
  549. bool CTFKnife::SendWeaponAnim( int iActivity )
  550. {
  551. CTFPlayer *pPlayer = GetTFPlayerOwner();
  552. if ( !pPlayer )
  553. return BaseClass::SendWeaponAnim( iActivity );
  554. if ( m_bReadyToBackstab )
  555. {
  556. switch ( iActivity )
  557. {
  558. case ACT_VM_IDLE:
  559. case ACT_ITEM1_VM_IDLE:
  560. case ACT_ITEM2_VM_IDLE:
  561. iActivity = ACT_BACKSTAB_VM_IDLE;
  562. break;
  563. }
  564. }
  565. return BaseClass::SendWeaponAnim( iActivity );
  566. }
  567. //-----------------------------------------------------------------------------
  568. // Purpose: The spy's backstab was blocked by a player item.
  569. //-----------------------------------------------------------------------------
  570. void CTFKnife::BackstabBlocked( void )
  571. {
  572. CTFPlayer *pPlayer = GetTFPlayerOwner();
  573. if ( !pPlayer )
  574. return;
  575. // Delay the spy's next attack for a time.
  576. pPlayer->SetNextAttack( gpGlobals->curtime + 2.f );
  577. m_flBlockedTime = gpGlobals->curtime;
  578. SendWeaponAnim( ACT_MELEE_VM_STUN );
  579. }
  580. //-----------------------------------------------------------------------------
  581. // Purpose: Spy can't change weapons if knife is hot.
  582. //-----------------------------------------------------------------------------
  583. bool CTFKnife::CanHolster( void ) const
  584. {
  585. if ( !m_bAllowHolsterBecauseForced && gpGlobals->curtime - m_flBlockedTime < 2.f )
  586. return false;
  587. else
  588. return BaseClass::CanHolster();
  589. }