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.

384 lines
10 KiB

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