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.

363 lines
10 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Base code for any melee based weapon
  4. //
  5. //=====================================================================================//
  6. #include "cbase.h"
  7. #include "weapon_sdkbase.h"
  8. #include "sdk_weapon_melee.h"
  9. #include "sdk_gamerules.h"
  10. #include "ammodef.h"
  11. #include "mathlib/mathlib.h"
  12. #include "in_buttons.h"
  13. #include "animation.h"
  14. #if defined( CLIENT_DLL )
  15. #include "c_sdk_player.h"
  16. #else
  17. #include "sdk_player.h"
  18. #include "ndebugoverlay.h"
  19. #include "te_effect_dispatch.h"
  20. #include "ilagcompensationmanager.h"
  21. #endif
  22. // memdbgon must be the last include file in a .cpp file!!!
  23. #include "tier0/memdbgon.h"
  24. IMPLEMENT_NETWORKCLASS_ALIASED( WeaponSDKMelee, DT_WeaponSDKMelee )
  25. BEGIN_NETWORK_TABLE( CWeaponSDKMelee, DT_WeaponSDKMelee )
  26. END_NETWORK_TABLE()
  27. BEGIN_PREDICTION_DATA( CWeaponSDKMelee )
  28. END_PREDICTION_DATA()
  29. #define MELEE_HULL_DIM 16
  30. static const Vector g_meleeMins(-MELEE_HULL_DIM,-MELEE_HULL_DIM,-MELEE_HULL_DIM);
  31. static const Vector g_meleeMaxs(MELEE_HULL_DIM,MELEE_HULL_DIM,MELEE_HULL_DIM);
  32. //-----------------------------------------------------------------------------
  33. // Constructor
  34. //-----------------------------------------------------------------------------
  35. CWeaponSDKMelee::CWeaponSDKMelee()
  36. {
  37. m_bFiresUnderwater = true;
  38. }
  39. //-----------------------------------------------------------------------------
  40. // Purpose: Spawn the weapon
  41. //-----------------------------------------------------------------------------
  42. void CWeaponSDKMelee::Spawn( void )
  43. {
  44. m_fMinRange1 = 0;
  45. m_fMinRange2 = 0;
  46. m_fMaxRange1 = 64;
  47. m_fMaxRange2 = 64;
  48. //Call base class first
  49. BaseClass::Spawn();
  50. }
  51. //-----------------------------------------------------------------------------
  52. // Purpose: Precache the weapon
  53. //-----------------------------------------------------------------------------
  54. void CWeaponSDKMelee::Precache( void )
  55. {
  56. //Call base class first
  57. BaseClass::Precache();
  58. }
  59. //------------------------------------------------------------------------------
  60. // Purpose : Update weapon
  61. //------------------------------------------------------------------------------
  62. void CWeaponSDKMelee::ItemPostFrame( void )
  63. {
  64. CSDKPlayer *pPlayer = GetPlayerOwner();
  65. if ( pPlayer == NULL )
  66. return;
  67. if ( (pPlayer->m_nButtons & IN_ATTACK) && (m_flNextPrimaryAttack <= gpGlobals->curtime) && pPlayer->CanAttack() )
  68. {
  69. PrimaryAttack();
  70. }
  71. else if ( (pPlayer->m_nButtons & IN_ATTACK2) && (m_flNextSecondaryAttack <= gpGlobals->curtime) && pPlayer->CanAttack() )
  72. {
  73. SecondaryAttack();
  74. }
  75. else
  76. {
  77. WeaponIdle();
  78. }
  79. }
  80. //------------------------------------------------------------------------------
  81. // Purpose :
  82. // Input :
  83. // Output :
  84. //------------------------------------------------------------------------------
  85. void CWeaponSDKMelee::PrimaryAttack()
  86. {
  87. #ifndef CLIENT_DLL
  88. CSDKPlayer *pPlayer = ToSDKPlayer( GetPlayerOwner() );
  89. // Move other players back to history positions based on local player's lag
  90. lagcompensation->StartLagCompensation( pPlayer, pPlayer->GetCurrentCommand() );
  91. #endif
  92. Swing( false );
  93. #ifndef CLIENT_DLL
  94. // Move other players back to history positions based on local player's lag
  95. lagcompensation->FinishLagCompensation( pPlayer );
  96. #endif
  97. }
  98. //------------------------------------------------------------------------------
  99. // Purpose :
  100. // Input :
  101. // Output :
  102. //------------------------------------------------------------------------------
  103. void CWeaponSDKMelee::SecondaryAttack()
  104. {
  105. Swing( true );
  106. }
  107. //------------------------------------------------------------------------------
  108. // Purpose: Implement impact function
  109. //------------------------------------------------------------------------------
  110. void CWeaponSDKMelee::Hit( trace_t &traceHit, Activity nHitActivity )
  111. {
  112. CSDKPlayer *pPlayer = ToSDKPlayer( GetOwner() );
  113. //Do view kick
  114. // AddViewKick();
  115. CBaseEntity *pHitEntity = traceHit.m_pEnt;
  116. //Apply damage to a hit target
  117. if ( pHitEntity != NULL )
  118. {
  119. Vector hitDirection;
  120. pPlayer->EyeVectors( &hitDirection, NULL, NULL );
  121. VectorNormalize( hitDirection );
  122. #ifndef CLIENT_DLL
  123. CTakeDamageInfo info( GetOwner(), GetOwner(), GetDamageForActivity( nHitActivity ), DMG_CLUB );
  124. if( pPlayer && pHitEntity->IsNPC() )
  125. {
  126. // If bonking an NPC, adjust damage.
  127. info.AdjustPlayerDamageInflictedForSkillLevel();
  128. }
  129. CalculateMeleeDamageForce( &info, hitDirection, traceHit.endpos );
  130. pHitEntity->DispatchTraceAttack( info, hitDirection, &traceHit );
  131. ApplyMultiDamage();
  132. // Now hit all triggers along the ray that...
  133. TraceAttackToTriggers( info, traceHit.startpos, traceHit.endpos, hitDirection );
  134. #endif
  135. WeaponSound( MELEE_HIT );
  136. }
  137. // Apply an impact effect
  138. ImpactEffect( traceHit );
  139. }
  140. Activity CWeaponSDKMelee::ChooseIntersectionPointAndActivity( trace_t &hitTrace, const Vector &mins, const Vector &maxs, CSDKPlayer *pOwner )
  141. {
  142. int i, j, k;
  143. float distance;
  144. const float *minmaxs[2] = {mins.Base(), maxs.Base()};
  145. trace_t tmpTrace;
  146. Vector vecHullEnd = hitTrace.endpos;
  147. Vector vecEnd;
  148. distance = 1e6f;
  149. Vector vecSrc = hitTrace.startpos;
  150. vecHullEnd = vecSrc + ((vecHullEnd - vecSrc)*2);
  151. UTIL_TraceLine( vecSrc, vecHullEnd, MASK_SHOT_HULL, pOwner, COLLISION_GROUP_NONE, &tmpTrace );
  152. if ( tmpTrace.fraction == 1.0 )
  153. {
  154. for ( i = 0; i < 2; i++ )
  155. {
  156. for ( j = 0; j < 2; j++ )
  157. {
  158. for ( k = 0; k < 2; k++ )
  159. {
  160. vecEnd.x = vecHullEnd.x + minmaxs[i][0];
  161. vecEnd.y = vecHullEnd.y + minmaxs[j][1];
  162. vecEnd.z = vecHullEnd.z + minmaxs[k][2];
  163. UTIL_TraceLine( vecSrc, vecEnd, MASK_SHOT_HULL, pOwner, COLLISION_GROUP_NONE, &tmpTrace );
  164. if ( tmpTrace.fraction < 1.0 )
  165. {
  166. float thisDistance = (tmpTrace.endpos - vecSrc).Length();
  167. if ( thisDistance < distance )
  168. {
  169. hitTrace = tmpTrace;
  170. distance = thisDistance;
  171. }
  172. }
  173. }
  174. }
  175. }
  176. }
  177. else
  178. {
  179. hitTrace = tmpTrace;
  180. }
  181. return ACT_VM_HITCENTER;
  182. }
  183. //-----------------------------------------------------------------------------
  184. // Purpose:
  185. // Input : &traceHit -
  186. //-----------------------------------------------------------------------------
  187. bool CWeaponSDKMelee::ImpactWater( const Vector &start, const Vector &end )
  188. {
  189. //FIXME: This doesn't handle the case of trying to splash while being underwater, but that's not going to look good
  190. // right now anyway...
  191. // We must start outside the water
  192. if ( UTIL_PointContents( start ) & (CONTENTS_WATER|CONTENTS_SLIME))
  193. return false;
  194. // We must end inside of water
  195. if ( !(UTIL_PointContents( end ) & (CONTENTS_WATER|CONTENTS_SLIME)))
  196. return false;
  197. trace_t waterTrace;
  198. UTIL_TraceLine( start, end, (CONTENTS_WATER|CONTENTS_SLIME), GetOwner(), COLLISION_GROUP_NONE, &waterTrace );
  199. if ( waterTrace.fraction < 1.0f )
  200. {
  201. #ifndef CLIENT_DLL
  202. CEffectData data;
  203. data.m_fFlags = 0;
  204. data.m_vOrigin = waterTrace.endpos;
  205. data.m_vNormal = waterTrace.plane.normal;
  206. data.m_flScale = 8.0f;
  207. // See if we hit slime
  208. if ( waterTrace.contents & CONTENTS_SLIME )
  209. {
  210. data.m_fFlags |= FX_WATER_IN_SLIME;
  211. }
  212. DispatchEffect( "watersplash", data );
  213. #endif
  214. }
  215. return true;
  216. }
  217. //-----------------------------------------------------------------------------
  218. // Purpose:
  219. //-----------------------------------------------------------------------------
  220. void CWeaponSDKMelee::ImpactEffect( trace_t &traceHit )
  221. {
  222. // See if we hit water (we don't do the other impact effects in this case)
  223. if ( ImpactWater( traceHit.startpos, traceHit.endpos ) )
  224. return;
  225. //FIXME: need new decals
  226. UTIL_ImpactTrace( &traceHit, DMG_CLUB );
  227. }
  228. //------------------------------------------------------------------------------
  229. // Purpose : Starts the swing of the weapon and determines the animation
  230. // Input : bIsSecondary - is this a secondary attack?
  231. //------------------------------------------------------------------------------
  232. void CWeaponSDKMelee::Swing( int bIsSecondary )
  233. {
  234. trace_t traceHit;
  235. // Try a ray
  236. CSDKPlayer *pOwner = ToSDKPlayer( GetOwner() );
  237. if ( !pOwner )
  238. return;
  239. Vector swingStart = pOwner->Weapon_ShootPosition( );
  240. Vector forward;
  241. pOwner->EyeVectors( &forward, NULL, NULL );
  242. Vector swingEnd = swingStart + forward * GetRange();
  243. UTIL_TraceLine( swingStart, swingEnd, MASK_SHOT_HULL, pOwner, COLLISION_GROUP_NONE, &traceHit );
  244. Activity nHitActivity = ACT_VM_HITCENTER;
  245. #ifndef CLIENT_DLL
  246. // Like bullets, melee traces have to trace against triggers.
  247. CTakeDamageInfo triggerInfo( GetOwner(), GetOwner(), GetDamageForActivity( nHitActivity ), DMG_CLUB );
  248. TraceAttackToTriggers( triggerInfo, traceHit.startpos, traceHit.endpos, vec3_origin );
  249. #endif
  250. if ( traceHit.fraction == 1.0 )
  251. {
  252. float meleeHullRadius = 1.732f * MELEE_HULL_DIM; // hull is +/- 16, so use cuberoot of 2 to determine how big the hull is from center to the corner point
  253. // Back off by hull "radius"
  254. swingEnd -= forward * meleeHullRadius;
  255. UTIL_TraceHull( swingStart, swingEnd, g_meleeMins, g_meleeMaxs, MASK_SHOT_HULL, pOwner, COLLISION_GROUP_NONE, &traceHit );
  256. if ( traceHit.fraction < 1.0 && traceHit.m_pEnt )
  257. {
  258. Vector vecToTarget = traceHit.m_pEnt->GetAbsOrigin() - swingStart;
  259. VectorNormalize( vecToTarget );
  260. float dot = vecToTarget.Dot( forward );
  261. // YWB: Make sure they are sort of facing the guy at least...
  262. if ( dot < 0.70721f )
  263. {
  264. // Force amiss
  265. traceHit.fraction = 1.0f;
  266. }
  267. else
  268. {
  269. nHitActivity = ChooseIntersectionPointAndActivity( traceHit, g_meleeMins, g_meleeMaxs, pOwner );
  270. }
  271. }
  272. }
  273. WeaponSound( SINGLE );
  274. // -------------------------
  275. // Miss
  276. // -------------------------
  277. if ( traceHit.fraction == 1.0f )
  278. {
  279. nHitActivity = bIsSecondary ? ACT_VM_MISSCENTER2 : ACT_VM_MISSCENTER;
  280. // We want to test the first swing again
  281. Vector testEnd = swingStart + forward * GetRange();
  282. // See if we happened to hit water
  283. ImpactWater( swingStart, testEnd );
  284. }
  285. else
  286. {
  287. Hit( traceHit, nHitActivity );
  288. }
  289. // Send the anim
  290. SendWeaponAnim( nHitActivity );
  291. pOwner->DoAnimationEvent( PLAYERANIMEVENT_ATTACK_PRIMARY );
  292. //Setup our next attack times
  293. m_flNextPrimaryAttack = gpGlobals->curtime + GetFireRate();
  294. m_flNextSecondaryAttack = gpGlobals->curtime + SequenceDuration();
  295. }