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.

375 lines
10 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================
  6. #include "cbase.h"
  7. #include "c_tf_projectile_arrow.h"
  8. #include "particles_new.h"
  9. #include "SpriteTrail.h"
  10. #include "c_tf_player.h"
  11. #include "collisionutils.h"
  12. #include "util_shared.h"
  13. #include "c_rope.h"
  14. //-----------------------------------------------------------------------------
  15. IMPLEMENT_NETWORKCLASS_ALIASED( TFProjectile_Arrow, DT_TFProjectile_Arrow )
  16. BEGIN_NETWORK_TABLE( C_TFProjectile_Arrow, DT_TFProjectile_Arrow )
  17. RecvPropBool( RECVINFO( m_bArrowAlight ) ),
  18. RecvPropBool( RECVINFO( m_bCritical ) ),
  19. RecvPropInt( RECVINFO( m_iProjectileType ) ),
  20. END_NETWORK_TABLE()
  21. //-----------------------------------------------------------------------------
  22. IMPLEMENT_NETWORKCLASS_ALIASED( TFProjectile_HealingBolt, DT_TFProjectile_HealingBolt )
  23. BEGIN_NETWORK_TABLE( C_TFProjectile_HealingBolt, DT_TFProjectile_HealingBolt )
  24. END_NETWORK_TABLE()
  25. IMPLEMENT_NETWORKCLASS_ALIASED( TFProjectile_GrapplingHook, DT_TFProjectile_GrapplingHook )
  26. BEGIN_NETWORK_TABLE( C_TFProjectile_GrapplingHook, DT_TFProjectile_GrapplingHook )
  27. END_NETWORK_TABLE()
  28. #define NEAR_MISS_THRESHOLD 120
  29. //-----------------------------------------------------------------------------
  30. // Purpose:
  31. //-----------------------------------------------------------------------------
  32. C_TFProjectile_Arrow::C_TFProjectile_Arrow( void )
  33. {
  34. m_fAttachTime = 0.f;
  35. m_nextNearMissCheck = 0.f;
  36. m_bNearMiss = false;
  37. m_bArrowAlight = false;
  38. m_bCritical = true;
  39. m_pCritEffect = NULL;
  40. m_iCachedDeflect = false;
  41. m_flLifeTime = 40.0f;
  42. }
  43. //-----------------------------------------------------------------------------
  44. // Purpose:
  45. //-----------------------------------------------------------------------------
  46. C_TFProjectile_Arrow::~C_TFProjectile_Arrow( void )
  47. {
  48. }
  49. //-----------------------------------------------------------------------------
  50. // Purpose:
  51. //-----------------------------------------------------------------------------
  52. void C_TFProjectile_Arrow::OnDataChanged( DataUpdateType_t updateType )
  53. {
  54. if ( updateType == DATA_UPDATE_CREATED )
  55. {
  56. SetNextClientThink( CLIENT_THINK_ALWAYS );
  57. #ifdef STAGING_ONLY
  58. if ( m_iProjectileType == TF_PROJECTILE_SNIPERBULLET )
  59. {
  60. switch ( GetTeamNumber() )
  61. {
  62. case TF_TEAM_BLUE:
  63. ParticleProp()->Create( "bullet_distortion_trail", PATTACH_ABSORIGIN_FOLLOW );
  64. break;
  65. case TF_TEAM_RED:
  66. ParticleProp()->Create( "bullet_distortion_trail", PATTACH_ABSORIGIN_FOLLOW );
  67. break;
  68. }
  69. }
  70. else if ( m_bArrowAlight )
  71. #else
  72. if ( m_bArrowAlight )
  73. #endif // STAGING_ONLY
  74. {
  75. ParticleProp()->Create( "flying_flaming_arrow", PATTACH_POINT_FOLLOW, "muzzle" );
  76. }
  77. }
  78. if ( m_bCritical )
  79. {
  80. if ( updateType == DATA_UPDATE_CREATED || m_iCachedDeflect != GetDeflected() )
  81. {
  82. CreateCritTrail();
  83. }
  84. }
  85. m_iCachedDeflect = GetDeflected();
  86. }
  87. //-----------------------------------------------------------------------------
  88. // Purpose:
  89. //-----------------------------------------------------------------------------
  90. void C_TFProjectile_Arrow::NotifyBoneAttached( C_BaseAnimating* attachTarget )
  91. {
  92. BaseClass::NotifyBoneAttached( attachTarget );
  93. m_fAttachTime = gpGlobals->curtime;
  94. SetNextClientThink( CLIENT_THINK_ALWAYS );
  95. }
  96. //-----------------------------------------------------------------------------
  97. // Purpose:
  98. //-----------------------------------------------------------------------------
  99. void C_TFProjectile_Arrow::ClientThink( void )
  100. {
  101. // Perform a near-miss check.
  102. if ( !m_bNearMiss && (gpGlobals->curtime > m_nextNearMissCheck) )
  103. {
  104. CheckNearMiss();
  105. m_nextNearMissCheck = gpGlobals->curtime + 0.05f;
  106. }
  107. // Remove crit effect if we hit a wall.
  108. if ( GetMoveType() == MOVETYPE_NONE && m_pCritEffect )
  109. {
  110. ParticleProp()->StopEmission( m_pCritEffect );
  111. m_pCritEffect = NULL;
  112. }
  113. BaseClass::ClientThink();
  114. // DO THIS LAST: Destroy us automatically after a period of time.
  115. if ( m_pAttachedTo )
  116. {
  117. if ( gpGlobals->curtime - m_fAttachTime > m_flLifeTime )
  118. {
  119. Release();
  120. return;
  121. }
  122. else if ( m_pAttachedTo->IsEffectActive( EF_NODRAW ) && !IsEffectActive( EF_NODRAW ) )
  123. {
  124. AddEffects( EF_NODRAW );
  125. UpdateVisibility();
  126. }
  127. else if ( !m_pAttachedTo->IsEffectActive( EF_NODRAW ) && IsEffectActive( EF_NODRAW ) && (m_pAttachedTo != C_BasePlayer::GetLocalPlayer()) )
  128. {
  129. RemoveEffects( EF_NODRAW );
  130. UpdateVisibility();
  131. }
  132. }
  133. if ( IsDormant() && !IsEffectActive( EF_NODRAW ) )
  134. {
  135. AddEffects( EF_NODRAW );
  136. UpdateVisibility();
  137. }
  138. }
  139. //-----------------------------------------------------------------------------
  140. // Purpose:
  141. //-----------------------------------------------------------------------------
  142. void C_TFProjectile_Arrow::CheckNearMiss( void )
  143. {
  144. // Check against the local player. If we're near him play a near miss sound.
  145. C_TFPlayer *pLocalPlayer = C_TFPlayer::GetLocalTFPlayer();
  146. if ( !pLocalPlayer || !pLocalPlayer->IsAlive() )
  147. return;
  148. // If we are attached to something or stationary we don't want to do near miss checks.
  149. if ( m_pAttachedTo || (GetMoveType() == MOVETYPE_NONE) )
  150. {
  151. m_bNearMiss = true;
  152. return;
  153. }
  154. // Can't hear near miss sounds from friendly arrows.
  155. if ( pLocalPlayer->GetTeamNumber() == GetTeamNumber() )
  156. return;
  157. Vector vecPlayerPos = pLocalPlayer->GetAbsOrigin();
  158. Vector vecArrowPos = GetAbsOrigin(), forward;
  159. AngleVectors( GetAbsAngles(), &forward );
  160. Vector vecArrowDest = GetAbsOrigin() + forward * 200.f;
  161. // If the arrow is moving away from the player just stop checking.
  162. float dist1 = vecArrowPos.DistToSqr( vecPlayerPos );
  163. float dist2 = vecArrowDest.DistToSqr( vecPlayerPos );
  164. if ( dist2 > dist1 )
  165. {
  166. m_bNearMiss = true;
  167. return;
  168. }
  169. // Check to see if the arrow is passing near the player.
  170. Vector vecClosestPoint;
  171. float dist;
  172. CalcClosestPointOnLineSegment( vecPlayerPos, vecArrowPos, vecArrowDest, vecClosestPoint, &dist );
  173. dist = vecPlayerPos.DistTo( vecClosestPoint );
  174. if ( dist > NEAR_MISS_THRESHOLD )
  175. return;
  176. // The arrow is passing close to the local player.
  177. m_bNearMiss = true;
  178. SetNextClientThink( CLIENT_THINK_NEVER );
  179. // If the arrow is about to hit something, don't play the sound and stop this check.
  180. trace_t tr;
  181. UTIL_TraceLine( vecArrowPos, vecArrowPos + forward * 400.f, CONTENTS_HITBOX|CONTENTS_MONSTER|CONTENTS_SOLID, this, COLLISION_GROUP_NONE, &tr );
  182. if ( tr.DidHit() )
  183. return;
  184. // We're good for a near miss!
  185. float soundlen = 0;
  186. EmitSound_t params;
  187. params.m_flSoundTime = 0;
  188. params.m_pSoundName = "Weapon_Arrow.Nearmiss";
  189. params.m_pflSoundDuration = &soundlen;
  190. CSingleUserRecipientFilter localFilter( pLocalPlayer );
  191. EmitSound( localFilter, pLocalPlayer->entindex(), params );
  192. }
  193. //-----------------------------------------------------------------------------
  194. // Purpose:
  195. //-----------------------------------------------------------------------------
  196. void C_TFProjectile_Arrow::CreateCritTrail( void )
  197. {
  198. if ( IsDormant() )
  199. return;
  200. if ( m_pCritEffect )
  201. {
  202. ParticleProp()->StopEmission( m_pCritEffect );
  203. m_pCritEffect = NULL;
  204. }
  205. if ( m_bCritical )
  206. {
  207. switch( GetTeamNumber() )
  208. {
  209. case TF_TEAM_BLUE:
  210. m_pCritEffect = ParticleProp()->Create( "critical_rocket_blue", PATTACH_ABSORIGIN_FOLLOW );
  211. break;
  212. case TF_TEAM_RED:
  213. m_pCritEffect = ParticleProp()->Create( "critical_rocket_red", PATTACH_ABSORIGIN_FOLLOW );
  214. break;
  215. default:
  216. break;
  217. }
  218. }
  219. }
  220. //-----------------------------------------------------------------------------
  221. void C_TFProjectile_HealingBolt::OnDataChanged( DataUpdateType_t updateType )
  222. {
  223. if ( updateType == DATA_UPDATE_CREATED )
  224. {
  225. switch( GetTeamNumber() )
  226. {
  227. case TF_TEAM_BLUE:
  228. ParticleProp()->Create( "healshot_trail_blue", PATTACH_ABSORIGIN_FOLLOW );
  229. break;
  230. case TF_TEAM_RED:
  231. ParticleProp()->Create( "healshot_trail_red", PATTACH_ABSORIGIN_FOLLOW );
  232. break;
  233. }
  234. }
  235. BaseClass::OnDataChanged( updateType );
  236. }
  237. void C_TFProjectile_GrapplingHook::OnDataChanged( DataUpdateType_t updateType )
  238. {
  239. BaseClass::OnDataChanged( updateType );
  240. if ( updateType == DATA_UPDATE_CREATED )
  241. {
  242. int nTeam = GetTeamNumber();
  243. C_TFPlayer *pTFPlayer = ToTFPlayer( GetOwnerEntity() );
  244. if ( pTFPlayer && pTFPlayer->IsPlayerClass( TF_CLASS_SPY ) && pTFPlayer->m_Shared.InCond( TF_COND_DISGUISED ) && pTFPlayer->GetTeamNumber() != GetLocalPlayerTeam() )
  245. {
  246. nTeam = pTFPlayer->m_Shared.GetDisguiseTeam();
  247. }
  248. const char *pszMaterialName = "cable/cable";
  249. switch ( nTeam )
  250. {
  251. case TF_TEAM_BLUE:
  252. pszMaterialName = "cable/cable_blue";
  253. break;
  254. case TF_TEAM_RED:
  255. pszMaterialName = "cable/cable_red";
  256. break;
  257. }
  258. C_BaseEntity *pStartEnt = GetOwnerEntity();
  259. int iAttachment = 0;
  260. if ( pTFPlayer )
  261. {
  262. CTFWeaponBase *pWeapon = assert_cast< CTFWeaponBase* >( pTFPlayer->GetActiveWeapon() );
  263. if ( pWeapon )
  264. {
  265. pStartEnt = pWeapon;
  266. int iMuzzle = pWeapon->LookupAttachment( "muzzle" );
  267. if ( iMuzzle != -1 )
  268. {
  269. iAttachment = iMuzzle;
  270. }
  271. }
  272. }
  273. int iHookAttachment = LookupAttachment( "rope_locator" );
  274. if ( iHookAttachment == -1 )
  275. iHookAttachment = 0;
  276. m_hRope = C_RopeKeyframe::Create( pStartEnt, this, iAttachment, iHookAttachment, 2, pszMaterialName );
  277. SetNextClientThink( CLIENT_THINK_ALWAYS );
  278. }
  279. }
  280. void C_TFProjectile_GrapplingHook::UpdateOnRemove()
  281. {
  282. RemoveRope();
  283. BaseClass::UpdateOnRemove();
  284. }
  285. void C_TFProjectile_GrapplingHook::ClientThink()
  286. {
  287. UpdateRope();
  288. }
  289. void C_TFProjectile_GrapplingHook::UpdateRope()
  290. {
  291. C_TFPlayer *pTFPlayer = ToTFPlayer( GetOwnerEntity() );
  292. if ( !pTFPlayer || !pTFPlayer->IsAlive() )
  293. {
  294. RemoveRope();
  295. return;
  296. }
  297. Vector vecStart = pTFPlayer->WorldSpaceCenter();
  298. if ( pTFPlayer->GetActiveWeapon() )
  299. {
  300. int iAttachment = pTFPlayer->GetActiveWeapon()->LookupAttachment( "muzzle" );
  301. if ( iAttachment != -1 )
  302. {
  303. GetAttachment( iAttachment, vecStart );
  304. }
  305. }
  306. float flDist = vecStart.DistTo( WorldSpaceCenter() );
  307. if ( m_hRope )
  308. {
  309. float flHangDist = pTFPlayer->GetGrapplingHookTarget() ? 0.1f * flDist : 1.5f * flDist;
  310. assert_cast< C_RopeKeyframe* >( m_hRope.Get() )->SetupHangDistance( flHangDist );
  311. }
  312. }
  313. void C_TFProjectile_GrapplingHook::RemoveRope()
  314. {
  315. if ( m_hRope )
  316. {
  317. m_hRope->Release();
  318. m_hRope = NULL;
  319. }
  320. }