Counter Strike : Global Offensive Source Code
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.

566 lines
14 KiB

  1. //===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======//
  2. //
  3. // Purpose:
  4. //
  5. //===========================================================================//
  6. #include "cbase.h"
  7. #include "weapon_knife.h"
  8. #include "cs_gamerules.h"
  9. #include "datacache/imdlcache.h"
  10. #if defined( CLIENT_DLL )
  11. #include "c_cs_player.h"
  12. #include "c_te_effect_dispatch.h"
  13. #include "c_rumble.h"
  14. #include "rumble_shared.h"
  15. #else
  16. #include "cs_player.h"
  17. #include "ilagcompensationmanager.h"
  18. #include "te_effect_dispatch.h"
  19. #endif
  20. // NOTE: This has to be the last file included!
  21. #include "tier0/memdbgon.h"
  22. #define KNIFE_BODYHIT_VOLUME 128
  23. #define KNIFE_WALLHIT_VOLUME 512
  24. #define KNIFE_RANGE_SHORT 32
  25. #define KNIFE_RANGE_LONG 48
  26. Vector head_hull_mins( -16, -16, -18 );
  27. Vector head_hull_maxs( 16, 16, 18 );
  28. #ifndef CLIENT_DLL
  29. //-----------------------------------------------------------------------------
  30. // Purpose: Only send to local player if this weapon is the active weapon
  31. // Input : *pStruct -
  32. // *pVarData -
  33. // *pRecipients -
  34. // objectID -
  35. // Output : void*
  36. //-----------------------------------------------------------------------------
  37. void* SendProxy_SendActiveLocalKnifeDataTable( const SendProp *pProp, const void *pStruct, const void *pVarData, CSendProxyRecipients *pRecipients, int objectID )
  38. {
  39. // Get the weapon entity
  40. CBaseCombatWeapon *pWeapon = (CBaseCombatWeapon*)pVarData;
  41. if ( pWeapon )
  42. {
  43. // Only send this chunk of data to the player carrying this weapon
  44. CBasePlayer *pPlayer = ToBasePlayer( pWeapon->GetOwner() );
  45. if ( pPlayer /*&& pPlayer->GetActiveWeapon() == pWeapon*/ )
  46. {
  47. pRecipients->SetOnly( pPlayer->GetClientIndex() );
  48. return (void*)pVarData;
  49. }
  50. }
  51. return NULL;
  52. }
  53. REGISTER_SEND_PROXY_NON_MODIFIED_POINTER( SendProxy_SendActiveLocalKnifeDataTable );
  54. //-----------------------------------------------------------------------------
  55. // Purpose: Only send to local player if this weapon is the active weapon
  56. // Input : *pStruct -
  57. // *pVarData -
  58. // *pRecipients -
  59. // objectID -
  60. // Output : void*
  61. //-----------------------------------------------------------------------------
  62. void* SendProxy_SendActiveLocalKnifeGGDataTable( const SendProp *pProp, const void *pStruct, const void *pVarData, CSendProxyRecipients *pRecipients, int objectID )
  63. {
  64. // Get the weapon entity
  65. CBaseCombatWeapon *pWeapon = (CBaseCombatWeapon*)pVarData;
  66. if ( pWeapon )
  67. {
  68. // Only send this chunk of data to the player carrying this weapon
  69. CBasePlayer *pPlayer = ToBasePlayer( pWeapon->GetOwner() );
  70. if ( pPlayer /*&& pPlayer->GetActiveWeapon() == pWeapon*/ )
  71. {
  72. pRecipients->SetOnly( pPlayer->GetClientIndex() );
  73. return (void*)pVarData;
  74. }
  75. }
  76. return NULL;
  77. }
  78. REGISTER_SEND_PROXY_NON_MODIFIED_POINTER( SendProxy_SendActiveLocalKnifeGGDataTable );
  79. #endif
  80. // ----------------------------------------------------------------------------- //
  81. // CKnife tables.
  82. // ----------------------------------------------------------------------------- //
  83. IMPLEMENT_NETWORKCLASS_ALIASED( Knife, DT_WeaponKnife )
  84. BEGIN_NETWORK_TABLE( CKnife, DT_WeaponKnife )
  85. END_NETWORK_TABLE()
  86. BEGIN_PREDICTION_DATA( CKnife )
  87. END_PREDICTION_DATA()
  88. LINK_ENTITY_TO_CLASS_ALIASED( weapon_knife, Knife );
  89. PRECACHE_REGISTER( weapon_knife );
  90. // ----------------------------------------------------------------------------- //
  91. // CKnife implementation.
  92. // ----------------------------------------------------------------------------- //
  93. CKnife::CKnife()
  94. {
  95. #ifndef CLIENT_DLL
  96. m_swingLeft = true;
  97. #endif
  98. }
  99. bool CKnife::HasPrimaryAmmo()
  100. {
  101. return true;
  102. }
  103. bool CKnife::CanBeSelected()
  104. {
  105. return true;
  106. }
  107. void CKnife::Precache()
  108. {
  109. BaseClass::Precache();
  110. PrecacheScriptSound( "Weapon_Knife.Deploy" );
  111. PrecacheScriptSound( "Weapon_Knife.Slash" );
  112. PrecacheScriptSound( "Weapon_Knife.Stab" );
  113. PrecacheScriptSound( "Weapon_Knife.Hit" );
  114. PrecacheEffect( "KnifeSlash" );
  115. }
  116. void CKnife::Spawn()
  117. {
  118. m_iClip1 = -1;
  119. BaseClass::Spawn();
  120. }
  121. //
  122. // Different knives seem to have a slightly different deploy timing on the animation
  123. // we hardcode the next attack to be consistent with any knife
  124. //
  125. #define hardcoded_knife_deploy_time 1.0f
  126. bool CKnife::Deploy()
  127. {
  128. // NOTE (wills): Knives no longer use model bodygroups to change their appearance
  129. // between CT and T versions. Team-specific knives now support team-specific
  130. // viewmodel and world animations, so they are stored as unique models.
  131. // If a knife needs to look aesthetically different between CT/T teams,
  132. // add an asset_modifier block to the item definition to divert the whole model.
  133. /*
  134. // We need to set the model for the knife based on what team the player is on.
  135. // We must do this after running deploy to avoid frame locking issues.
  136. CCSPlayer *pPlayer = GetPlayerOwner();
  137. if ( pPlayer != NULL )
  138. {
  139. MDLCACHE_CRITICAL_SECTION_(g_pMDLCache);
  140. int bodyPartID = ( pPlayer->GetTeamNumber() == TEAM_TERRORIST ) ? 0 : 1;
  141. SetBodygroup( 0 , bodyPartID );
  142. }
  143. */
  144. if ( !BaseClass::Deploy() )
  145. return false;
  146. // Fix for different knife models having different deploy times. If it's short,
  147. // you just idle a bit before you attack. If it's long, we animation-cancel the
  148. // deploy animation and go straight into the swing/stab after a fixed amount of
  149. // time.
  150. if ( CBasePlayer *pOwner = ToBasePlayer( GetOwner() ) )
  151. pOwner->SetNextAttack( gpGlobals->curtime + hardcoded_knife_deploy_time );
  152. return true;
  153. }
  154. void CKnife::WeaponAnimation ( int iAnimation )
  155. {
  156. /*
  157. int flag;
  158. #if defined( CLIENT_WEAPONS )
  159. flag = FEV_NOTHOST;
  160. #else
  161. flag = 0;
  162. #endif
  163. PLAYBACK_EVENT_FULL( flag, pPlayer->edict(), m_usKnife,
  164. 0.0, (float *)&g_vecZero, (float *)&g_vecZero,
  165. 0.0,
  166. 0.0,
  167. iAnimation, 2, 3, 4 );
  168. */
  169. }
  170. void FindHullIntersection( const Vector &vecSrc, trace_t &tr, const Vector &mins, const Vector &maxs, CBaseEntity *pEntity )
  171. {
  172. int i, j, k;
  173. float distance;
  174. Vector minmaxs[2] = {mins, maxs};
  175. trace_t tmpTrace;
  176. Vector vecHullEnd = tr.endpos;
  177. Vector vecEnd;
  178. distance = 1e6f;
  179. vecHullEnd = vecSrc + ((vecHullEnd - vecSrc)*2);
  180. UTIL_TraceLine( vecSrc, vecHullEnd, MASK_SOLID, pEntity, COLLISION_GROUP_NONE, &tmpTrace );
  181. if ( tmpTrace.fraction < 1.0 )
  182. {
  183. tr = tmpTrace;
  184. return;
  185. }
  186. for ( i = 0; i < 2; i++ )
  187. {
  188. for ( j = 0; j < 2; j++ )
  189. {
  190. for ( k = 0; k < 2; k++ )
  191. {
  192. vecEnd.x = vecHullEnd.x + minmaxs[i][0];
  193. vecEnd.y = vecHullEnd.y + minmaxs[j][1];
  194. vecEnd.z = vecHullEnd.z + minmaxs[k][2];
  195. UTIL_TraceLine( vecSrc, vecEnd, MASK_SOLID, pEntity, COLLISION_GROUP_NONE, &tmpTrace );
  196. if ( tmpTrace.fraction < 1.0 )
  197. {
  198. float thisDistance = (tmpTrace.endpos - vecSrc).Length();
  199. if ( thisDistance < distance )
  200. {
  201. tr = tmpTrace;
  202. distance = thisDistance;
  203. }
  204. }
  205. }
  206. }
  207. }
  208. }
  209. void CKnife::PrimaryAttack()
  210. {
  211. CCSPlayer *pPlayer = GetPlayerOwner();
  212. if ( pPlayer )
  213. {
  214. #if !defined (CLIENT_DLL)
  215. // Move other players back to history positions based on local player's lag
  216. lagcompensation->StartLagCompensation( pPlayer, LAG_COMPENSATE_HITBOXES_ALONG_RAY, pPlayer->EyePosition(), pPlayer->EyeAngles(), KNIFE_RANGE_LONG );
  217. #endif
  218. SwingOrStab( Primary_Mode );
  219. #if !defined (CLIENT_DLL)
  220. lagcompensation->FinishLagCompensation( pPlayer );
  221. #endif
  222. }
  223. }
  224. void CKnife::SecondaryAttack()
  225. {
  226. CCSPlayer *pPlayer = GetPlayerOwner();
  227. if ( pPlayer && !pPlayer->m_bIsDefusing && !CSGameRules()->IsFreezePeriod() )
  228. {
  229. #if !defined (CLIENT_DLL)
  230. // Move other players back to history positions based on local player's lag
  231. lagcompensation->StartLagCompensation( pPlayer, LAG_COMPENSATE_HITBOXES_ALONG_RAY, pPlayer->EyePosition(), pPlayer->EyeAngles(), KNIFE_RANGE_SHORT );
  232. #endif
  233. SwingOrStab( Secondary_Mode );
  234. #if !defined (CLIENT_DLL)
  235. lagcompensation->FinishLagCompensation( pPlayer );
  236. #endif
  237. }
  238. }
  239. #include "effect_dispatch_data.h"
  240. void CKnife::WeaponIdle()
  241. {
  242. if (m_flTimeWeaponIdle > gpGlobals->curtime)
  243. return;
  244. CCSPlayer *pPlayer = GetPlayerOwner();
  245. if ( !pPlayer )
  246. return;
  247. if ( pPlayer->IsShieldDrawn() )
  248. return;
  249. SetWeaponIdleTime( gpGlobals->curtime + 20 );
  250. // only idle if the slid isn't back
  251. SendWeaponAnim( ACT_VM_IDLE );
  252. }
  253. // [tj] Hacky cheat code to control knife damage
  254. #ifndef CLIENT_DLL
  255. ConVar KnifeDamageScale( "knife_damage_scale", "100", FCVAR_DEVELOPMENTONLY );
  256. #endif
  257. bool CKnife::SwingOrStab( CSWeaponMode weaponMode )
  258. {
  259. CCSPlayer *pPlayer = GetPlayerOwner();
  260. if ( !pPlayer )
  261. return false;
  262. #ifndef CLIENT_DLL
  263. pPlayer->PlayerUsedKnife();
  264. #endif
  265. // bStab: false=primary, true=secondary
  266. float fRange = (weaponMode == Primary_Mode) ? KNIFE_RANGE_LONG : KNIFE_RANGE_SHORT; // knife range
  267. Vector vForward; AngleVectors( pPlayer->EyeAngles(), &vForward );
  268. Vector vecSrc = pPlayer->Weapon_ShootPosition();
  269. Vector vecEnd = vecSrc + vForward * fRange;
  270. trace_t tr;
  271. UTIL_TraceLine( vecSrc, vecEnd, MASK_SOLID, pPlayer, COLLISION_GROUP_NONE, &tr );
  272. //check for hitting glass - TODO - fix this hackiness, doesn't always line up with what FindHullIntersection returns
  273. #ifndef CLIENT_DLL
  274. CTakeDamageInfo glassDamage( pPlayer, pPlayer, 42.0f, DMG_BULLET | DMG_NEVERGIB );
  275. TraceAttackToTriggers( glassDamage, tr.startpos, tr.endpos, vForward );
  276. #endif
  277. if ( tr.fraction >= 1.0 )
  278. {
  279. UTIL_TraceHull( vecSrc, vecEnd, head_hull_mins, head_hull_maxs, MASK_SOLID, pPlayer, COLLISION_GROUP_NONE, &tr );
  280. if ( tr.fraction < 1.0 )
  281. {
  282. // Calculate the point of intersection of the line (or hull) and the object we hit
  283. // This is and approximation of the "best" intersection
  284. CBaseEntity *pHit = tr.m_pEnt;
  285. if ( !pHit || pHit->IsBSPModel() )
  286. FindHullIntersection( vecSrc, tr, VEC_DUCK_HULL_MIN, VEC_DUCK_HULL_MAX, pPlayer );
  287. vecEnd = tr.endpos; // This is the point on the actual surface (the hull could have hit space)
  288. }
  289. }
  290. bool bDidHit = tr.fraction < 1.0f;
  291. #ifndef CLIENT_DLL
  292. bool bFirstSwing = (m_flNextPrimaryAttack + 0.4) < gpGlobals->curtime;
  293. if ( bFirstSwing )
  294. {
  295. m_swingLeft = true;
  296. }
  297. #endif
  298. float fPrimDelay, fSecDelay;
  299. if ( weaponMode == Secondary_Mode )
  300. {
  301. fPrimDelay = fSecDelay = bDidHit ? 1.1f : 1.0f;
  302. }
  303. else // swing
  304. {
  305. fPrimDelay = bDidHit ? 0.5f : 0.4f;
  306. fSecDelay = bDidHit ? 0.5f : 0.5f;
  307. }
  308. if ( pPlayer->HasShield() )
  309. {
  310. fPrimDelay += 0.7f; // 0.7 seconds slower if we carry a shield
  311. fSecDelay += 0.7f;
  312. }
  313. m_flNextPrimaryAttack = gpGlobals->curtime + fPrimDelay;
  314. m_flNextSecondaryAttack = gpGlobals->curtime + fSecDelay;
  315. SetWeaponIdleTime( gpGlobals->curtime + 2 );
  316. bool bBackStab = false;
  317. if ( bDidHit )
  318. {
  319. // server side damage calculations
  320. CBaseEntity *pEntity = tr.m_pEnt;
  321. #ifndef CLIENT_DLL
  322. // player "shoot" animation
  323. pPlayer->SetAnimation( PLAYER_ATTACK1 );
  324. ClearMultiDamage();
  325. float flDamage = 0.f; // set below
  326. #endif
  327. if ( pEntity && pEntity->IsPlayer() )
  328. {
  329. Vector vTragetForward;
  330. AngleVectors( pEntity->GetAbsAngles(), &vTragetForward );
  331. Vector2D vecLOS = (pEntity->GetAbsOrigin() - pPlayer->GetAbsOrigin()).AsVector2D();
  332. Vector2DNormalize( vecLOS );
  333. float flDot = vecLOS.Dot( vTragetForward.AsVector2D() );
  334. //Triple the damage if we are stabbing them in the back.
  335. if ( flDot > 0.475f )
  336. bBackStab = true;
  337. }
  338. #ifndef CLIENT_DLL
  339. if ( weaponMode == Secondary_Mode )
  340. {
  341. if ( bBackStab )
  342. {
  343. flDamage = 180.0f;
  344. }
  345. else
  346. {
  347. flDamage = 65.0f;
  348. }
  349. }
  350. else
  351. {
  352. if ( bBackStab )
  353. {
  354. flDamage = 90;
  355. }
  356. else if ( bFirstSwing )
  357. {
  358. // first swing does full damage
  359. flDamage = 40;
  360. }
  361. else
  362. {
  363. // subsequent swings do less
  364. flDamage = 25;
  365. }
  366. }
  367. // [tj] Hacky cheat to lower knife damage for testing
  368. flDamage *= ( KnifeDamageScale.GetInt() / 100.0f );
  369. #endif
  370. if ( weaponMode == Secondary_Mode )
  371. {
  372. SendWeaponAnim( bBackStab ? ACT_VM_SWINGHARD : ACT_VM_HITCENTER2 );
  373. }
  374. else // swing
  375. {
  376. SendWeaponAnim( bBackStab ? ACT_VM_SWINGHIT : ACT_VM_HITCENTER );
  377. }
  378. #ifndef CLIENT_DLL
  379. CCSPlayer::StartNewBulletGroup();
  380. CTakeDamageInfo info( pPlayer, pPlayer, this, flDamage, DMG_SLASH | DMG_NEVERGIB );
  381. CalculateMeleeDamageForce( &info, vForward, tr.endpos, 1.0f/flDamage );
  382. pEntity->DispatchTraceAttack( info, vForward, &tr );
  383. ApplyMultiDamage();
  384. #endif
  385. if ( tr.m_pEnt )
  386. {
  387. CPASAttenuationFilter filter( this );
  388. filter.UsePredictionRules();
  389. if ( tr.m_pEnt->IsPlayer() )
  390. {
  391. EmitSound( filter, entindex(), (weaponMode == Secondary_Mode) ? "Weapon_Knife.Stab" : "Weapon_Knife.Hit" );
  392. }
  393. else
  394. {
  395. EmitSound( filter, entindex(), "Weapon_Knife.HitWall" );
  396. }
  397. }
  398. CEffectData data;
  399. data.m_vOrigin = tr.endpos;
  400. data.m_vStart = tr.startpos;
  401. data.m_nSurfaceProp = tr.surface.surfaceProps;
  402. data.m_nDamageType = DMG_SLASH;
  403. data.m_nHitBox = tr.hitbox;
  404. #ifdef CLIENT_DLL
  405. data.m_hEntity = tr.m_pEnt->GetRefEHandle();
  406. #else
  407. data.m_nEntIndex = tr.m_pEnt->entindex();
  408. #endif
  409. CPASFilter filter( data.m_vOrigin );
  410. #ifndef CLIENT_DLL
  411. filter.RemoveRecipient( pPlayer );
  412. #endif
  413. data.m_vAngles = pPlayer->GetAbsAngles();
  414. data.m_fFlags = 0x1; //IMPACT_NODECAL;
  415. DispatchEffect( filter, 0.0, "KnifeSlash", data );
  416. }
  417. else
  418. {
  419. // play wiff or swish sound
  420. CPASAttenuationFilter filter( this );
  421. filter.UsePredictionRules();
  422. EmitSound( filter, entindex(), "Weapon_Knife.Slash" );
  423. if ( weaponMode == Secondary_Mode )
  424. {
  425. SendWeaponAnim( ACT_VM_MISSCENTER2 );
  426. }
  427. else // swing
  428. {
  429. SendWeaponAnim( ACT_VM_MISSCENTER );
  430. }
  431. }
  432. #ifndef CLIENT_DLL
  433. if ( weaponMode == Secondary_Mode )
  434. {
  435. pPlayer->DoAnimationEvent( bBackStab ? PLAYERANIMEVENT_FIRE_GUN_SECONDARY_SPECIAL1 : PLAYERANIMEVENT_FIRE_GUN_SECONDARY );
  436. m_swingLeft = true;
  437. }
  438. else // swing
  439. {
  440. // See if we are back stabbing and if we're swinging left (opt) or right.
  441. pPlayer->DoAnimationEvent( bBackStab ? ( m_swingLeft ? PLAYERANIMEVENT_FIRE_GUN_PRIMARY_OPT_SPECIAL1 : PLAYERANIMEVENT_FIRE_GUN_PRIMARY_SPECIAL1 ) : ( m_swingLeft ? PLAYERANIMEVENT_FIRE_GUN_PRIMARY_OPT : PLAYERANIMEVENT_FIRE_GUN_PRIMARY) );
  442. m_swingLeft = !m_swingLeft;
  443. }
  444. #else
  445. RumbleEffect( XBX_GetUserId( pPlayer->GetSplitScreenPlayerSlot() ), !bDidHit ? RUMBLE_CROWBAR_SWING : RUMBLE_AR2, 0, RUMBLE_FLAG_RESTART );
  446. #endif
  447. return bDidHit;
  448. }
  449. bool CKnife::CanDrop()
  450. {
  451. return false;
  452. }
  453. // ----------------------------------------------------------------------------- //
  454. // CKnifeGG tables.
  455. // ----------------------------------------------------------------------------- //
  456. IMPLEMENT_NETWORKCLASS_ALIASED( KnifeGG, DT_WeaponKnifeGG )
  457. BEGIN_NETWORK_TABLE( CKnifeGG, DT_WeaponKnifeGG )
  458. END_NETWORK_TABLE()
  459. BEGIN_PREDICTION_DATA( CKnifeGG )
  460. END_PREDICTION_DATA()
  461. LINK_ENTITY_TO_CLASS_ALIASED( weapon_knifegg, KnifeGG );
  462. PRECACHE_REGISTER( weapon_knifegg );