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.

372 lines
10 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Stun Stick- beating stick with a zappy end
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "cbase.h"
  8. #include "npcevent.h"
  9. #include "npc_metropolice.h"
  10. #include "weapon_stunstick.h"
  11. #include "IEffects.h"
  12. // memdbgon must be the last include file in a .cpp file!!!
  13. #include "tier0/memdbgon.h"
  14. ConVar sk_plr_dmg_stunstick ( "sk_plr_dmg_stunstick","0");
  15. ConVar sk_npc_dmg_stunstick ( "sk_npc_dmg_stunstick","0");
  16. extern ConVar metropolice_move_and_melee;
  17. //-----------------------------------------------------------------------------
  18. // CWeaponStunStick
  19. //-----------------------------------------------------------------------------
  20. IMPLEMENT_SERVERCLASS_ST(CWeaponStunStick, DT_WeaponStunStick)
  21. SendPropInt( SENDINFO( m_bActive ), 1, SPROP_UNSIGNED ),
  22. END_SEND_TABLE()
  23. #ifndef HL2MP
  24. LINK_ENTITY_TO_CLASS( weapon_stunstick, CWeaponStunStick );
  25. PRECACHE_WEAPON_REGISTER( weapon_stunstick );
  26. #endif
  27. acttable_t CWeaponStunStick::m_acttable[] =
  28. {
  29. { ACT_MELEE_ATTACK1, ACT_MELEE_ATTACK_SWING, true },
  30. { ACT_IDLE_ANGRY, ACT_IDLE_ANGRY_MELEE, true },
  31. };
  32. IMPLEMENT_ACTTABLE(CWeaponStunStick);
  33. BEGIN_DATADESC( CWeaponStunStick )
  34. DEFINE_FIELD( m_bActive, FIELD_BOOLEAN ),
  35. END_DATADESC()
  36. //-----------------------------------------------------------------------------
  37. // Constructor
  38. //-----------------------------------------------------------------------------
  39. CWeaponStunStick::CWeaponStunStick( void )
  40. {
  41. // HACK: Don't call SetStunState because this tried to Emit a sound before
  42. // any players are connected which is a bug
  43. m_bActive = false;
  44. }
  45. //-----------------------------------------------------------------------------
  46. //-----------------------------------------------------------------------------
  47. void CWeaponStunStick::Spawn()
  48. {
  49. Precache();
  50. BaseClass::Spawn();
  51. AddSolidFlags( FSOLID_NOT_STANDABLE );
  52. }
  53. void CWeaponStunStick::Precache()
  54. {
  55. BaseClass::Precache();
  56. PrecacheScriptSound( "Weapon_StunStick.Activate" );
  57. PrecacheScriptSound( "Weapon_StunStick.Deactivate" );
  58. }
  59. //-----------------------------------------------------------------------------
  60. // Purpose: Get the damage amount for the animation we're doing
  61. // Input : hitActivity - currently played activity
  62. // Output : Damage amount
  63. //-----------------------------------------------------------------------------
  64. float CWeaponStunStick::GetDamageForActivity( Activity hitActivity )
  65. {
  66. if ( ( GetOwner() != NULL ) && ( GetOwner()->IsPlayer() ) )
  67. return sk_plr_dmg_stunstick.GetFloat();
  68. return sk_npc_dmg_stunstick.GetFloat();
  69. }
  70. //-----------------------------------------------------------------------------
  71. // Attempt to lead the target (needed because citizens can't hit manhacks with the crowbar!)
  72. //-----------------------------------------------------------------------------
  73. extern ConVar sk_crowbar_lead_time;
  74. int CWeaponStunStick::WeaponMeleeAttack1Condition( float flDot, float flDist )
  75. {
  76. // Attempt to lead the target (needed because citizens can't hit manhacks with the crowbar!)
  77. CAI_BaseNPC *pNPC = GetOwner()->MyNPCPointer();
  78. CBaseEntity *pEnemy = pNPC->GetEnemy();
  79. if (!pEnemy)
  80. return COND_NONE;
  81. Vector vecVelocity;
  82. AngularImpulse angVelocity;
  83. pEnemy->GetVelocity( &vecVelocity, &angVelocity );
  84. // Project where the enemy will be in a little while, add some randomness so he doesn't always hit
  85. float dt = sk_crowbar_lead_time.GetFloat();
  86. dt += random->RandomFloat( -0.3f, 0.2f );
  87. if ( dt < 0.0f )
  88. dt = 0.0f;
  89. Vector vecExtrapolatedPos;
  90. VectorMA( pEnemy->WorldSpaceCenter(), dt, vecVelocity, vecExtrapolatedPos );
  91. Vector vecDelta;
  92. VectorSubtract( vecExtrapolatedPos, pNPC->WorldSpaceCenter(), vecDelta );
  93. if ( fabs( vecDelta.z ) > 70 )
  94. {
  95. return COND_TOO_FAR_TO_ATTACK;
  96. }
  97. Vector vecForward = pNPC->BodyDirection2D( );
  98. vecDelta.z = 0.0f;
  99. float flExtrapolatedDot = DotProduct2D( vecDelta.AsVector2D(), vecForward.AsVector2D() );
  100. if ((flDot < 0.7) && (flExtrapolatedDot < 0.7))
  101. {
  102. return COND_NOT_FACING_ATTACK;
  103. }
  104. float flExtrapolatedDist = Vector2DNormalize( vecDelta.AsVector2D() );
  105. if( pEnemy->IsPlayer() )
  106. {
  107. //Vector vecDir = pEnemy->GetSmoothedVelocity();
  108. //float flSpeed = VectorNormalize( vecDir );
  109. // If player will be in front of me in one-half second, clock his arse.
  110. Vector vecProjectEnemy = pEnemy->GetAbsOrigin() + (pEnemy->GetAbsVelocity() * 0.35);
  111. Vector vecProjectMe = GetAbsOrigin();
  112. if( (vecProjectMe - vecProjectEnemy).Length2D() <= 48.0f )
  113. {
  114. return COND_CAN_MELEE_ATTACK1;
  115. }
  116. }
  117. /*
  118. if( metropolice_move_and_melee.GetBool() )
  119. {
  120. if( pNPC->IsMoving() )
  121. {
  122. flTargetDist *= 1.5f;
  123. }
  124. }
  125. */
  126. float flTargetDist = 48.0f;
  127. if ((flDist > flTargetDist) && (flExtrapolatedDist > flTargetDist))
  128. {
  129. return COND_TOO_FAR_TO_ATTACK;
  130. }
  131. return COND_CAN_MELEE_ATTACK1;
  132. }
  133. //-----------------------------------------------------------------------------
  134. // Purpose:
  135. //-----------------------------------------------------------------------------
  136. void CWeaponStunStick::ImpactEffect( trace_t &traceHit )
  137. {
  138. //Glowing spark effect for hit
  139. //UTIL_DecalTrace( &m_trLineHit, "PlasmaGlowFade" );
  140. //FIXME: need new decals
  141. UTIL_ImpactTrace( &traceHit, DMG_CLUB );
  142. }
  143. void CWeaponStunStick::Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator )
  144. {
  145. switch( pEvent->event )
  146. {
  147. case EVENT_WEAPON_MELEE_HIT:
  148. {
  149. // Trace up or down based on where the enemy is...
  150. // But only if we're basically facing that direction
  151. Vector vecDirection;
  152. AngleVectors( GetAbsAngles(), &vecDirection );
  153. CBaseEntity *pEnemy = pOperator->MyNPCPointer() ? pOperator->MyNPCPointer()->GetEnemy() : NULL;
  154. if ( pEnemy )
  155. {
  156. Vector vecDelta;
  157. VectorSubtract( pEnemy->WorldSpaceCenter(), pOperator->Weapon_ShootPosition(), vecDelta );
  158. VectorNormalize( vecDelta );
  159. Vector2D vecDelta2D = vecDelta.AsVector2D();
  160. Vector2DNormalize( vecDelta2D );
  161. if ( DotProduct2D( vecDelta2D, vecDirection.AsVector2D() ) > 0.8f )
  162. {
  163. vecDirection = vecDelta;
  164. }
  165. }
  166. Vector vecEnd;
  167. VectorMA( pOperator->Weapon_ShootPosition(), 32, vecDirection, vecEnd );
  168. // Stretch the swing box down to catch low level physics objects
  169. CBaseEntity *pHurt = pOperator->CheckTraceHullAttack( pOperator->Weapon_ShootPosition(), vecEnd,
  170. Vector(-16,-16,-40), Vector(16,16,16), GetDamageForActivity( GetActivity() ), DMG_CLUB, 0.5f, false );
  171. // did I hit someone?
  172. if ( pHurt )
  173. {
  174. // play sound
  175. WeaponSound( MELEE_HIT );
  176. CBasePlayer *pPlayer = ToBasePlayer( pHurt );
  177. CNPC_MetroPolice *pCop = dynamic_cast<CNPC_MetroPolice *>(pOperator);
  178. bool bFlashed = false;
  179. if ( pCop != NULL && pPlayer != NULL )
  180. {
  181. // See if we need to knock out this target
  182. if ( pCop->ShouldKnockOutTarget( pHurt ) )
  183. {
  184. float yawKick = random->RandomFloat( -48, -24 );
  185. //Kick the player angles
  186. pPlayer->ViewPunch( QAngle( -16, yawKick, 2 ) );
  187. color32 white = {255,255,255,255};
  188. UTIL_ScreenFade( pPlayer, white, 0.2f, 1.0f, FFADE_OUT|FFADE_PURGE|FFADE_STAYOUT );
  189. bFlashed = true;
  190. pCop->KnockOutTarget( pHurt );
  191. break;
  192. }
  193. else
  194. {
  195. // Notify that we've stunned a target
  196. pCop->StunnedTarget( pHurt );
  197. }
  198. }
  199. // Punch angles
  200. if ( pPlayer != NULL && !(pPlayer->GetFlags() & FL_GODMODE) )
  201. {
  202. float yawKick = random->RandomFloat( -48, -24 );
  203. //Kick the player angles
  204. pPlayer->ViewPunch( QAngle( -16, yawKick, 2 ) );
  205. Vector dir = pHurt->GetAbsOrigin() - GetAbsOrigin();
  206. // If the player's on my head, don't knock him up
  207. if ( pPlayer->GetGroundEntity() == pOperator )
  208. {
  209. dir = vecDirection;
  210. dir.z = 0;
  211. }
  212. VectorNormalize(dir);
  213. dir *= 500.0f;
  214. //If not on ground, then don't make them fly!
  215. if ( !(pPlayer->GetFlags() & FL_ONGROUND ) )
  216. dir.z = 0.0f;
  217. //Push the target back
  218. pHurt->ApplyAbsVelocityImpulse( dir );
  219. if ( !bFlashed )
  220. {
  221. color32 red = {128,0,0,128};
  222. UTIL_ScreenFade( pPlayer, red, 0.5f, 0.1f, FFADE_IN );
  223. }
  224. // Force the player to drop anyting they were holding
  225. pPlayer->ForceDropOfCarriedPhysObjects();
  226. }
  227. // do effect?
  228. }
  229. else
  230. {
  231. WeaponSound( MELEE_MISS );
  232. }
  233. }
  234. break;
  235. default:
  236. BaseClass::Operator_HandleAnimEvent( pEvent, pOperator );
  237. break;
  238. }
  239. }
  240. //-----------------------------------------------------------------------------
  241. // Purpose: Sets the state of the stun stick
  242. //-----------------------------------------------------------------------------
  243. void CWeaponStunStick::SetStunState( bool state )
  244. {
  245. m_bActive = state;
  246. if ( m_bActive )
  247. {
  248. //FIXME: START - Move to client-side
  249. Vector vecAttachment;
  250. GetAttachment( 1, vecAttachment );
  251. g_pEffects->Sparks( vecAttachment );
  252. //FIXME: END - Move to client-side
  253. EmitSound( "Weapon_StunStick.Activate" );
  254. }
  255. else
  256. {
  257. EmitSound( "Weapon_StunStick.Deactivate" );
  258. }
  259. }
  260. //-----------------------------------------------------------------------------
  261. // Purpose:
  262. // Output : Returns true on success, false on failure.
  263. //-----------------------------------------------------------------------------
  264. bool CWeaponStunStick::Deploy( void )
  265. {
  266. SetStunState( true );
  267. return BaseClass::Deploy();
  268. }
  269. //-----------------------------------------------------------------------------
  270. // Purpose:
  271. //-----------------------------------------------------------------------------
  272. bool CWeaponStunStick::Holster( CBaseCombatWeapon *pSwitchingTo )
  273. {
  274. if ( BaseClass::Holster( pSwitchingTo ) == false )
  275. return false;
  276. SetStunState( false );
  277. return true;
  278. }
  279. //-----------------------------------------------------------------------------
  280. // Purpose:
  281. // Input : &vecVelocity -
  282. //-----------------------------------------------------------------------------
  283. void CWeaponStunStick::Drop( const Vector &vecVelocity )
  284. {
  285. SetStunState( false );
  286. BaseClass::Drop( vecVelocity );
  287. }
  288. //-----------------------------------------------------------------------------
  289. // Purpose:
  290. // Output : Returns true on success, false on failure.
  291. //-----------------------------------------------------------------------------
  292. bool CWeaponStunStick::GetStunState( void )
  293. {
  294. return m_bActive;
  295. }