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.

345 lines
8.5 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include "fx_cs_shared.h"
  8. #include "weapon_csbase.h"
  9. #ifndef CLIENT_DLL
  10. #include "ilagcompensationmanager.h"
  11. #endif
  12. ConVar weapon_accuracy_logging( "weapon_accuracy_logging", "0", FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY | FCVAR_ARCHIVE );
  13. #ifdef CLIENT_DLL
  14. #include "fx_impact.h"
  15. // this is a cheap ripoff from CBaseCombatWeapon::WeaponSound():
  16. void FX_WeaponSound(
  17. int iPlayerIndex,
  18. WeaponSound_t sound_type,
  19. const Vector &vOrigin,
  20. CCSWeaponInfo *pWeaponInfo, float flSoundTime )
  21. {
  22. // If we have some sounds from the weapon classname.txt file, play a random one of them
  23. const char *shootsound = pWeaponInfo->aShootSounds[ sound_type ];
  24. if ( !shootsound || !shootsound[0] )
  25. return;
  26. CBroadcastRecipientFilter filter; // this is client side only
  27. if ( !te->CanPredict() )
  28. return;
  29. CBaseEntity::EmitSound( filter, iPlayerIndex, shootsound, &vOrigin, flSoundTime );
  30. }
  31. class CGroupedSound
  32. {
  33. public:
  34. string_t m_SoundName;
  35. Vector m_vPos;
  36. };
  37. CUtlVector<CGroupedSound> g_GroupedSounds;
  38. // Called by the ImpactSound function.
  39. void ShotgunImpactSoundGroup( const char *pSoundName, const Vector &vEndPos )
  40. {
  41. int i;
  42. // Don't play the sound if it's too close to another impact sound.
  43. for ( i=0; i < g_GroupedSounds.Count(); i++ )
  44. {
  45. CGroupedSound *pSound = &g_GroupedSounds[i];
  46. if ( vEndPos.DistToSqr( pSound->m_vPos ) < 300*300 )
  47. {
  48. if ( Q_stricmp( pSound->m_SoundName, pSoundName ) == 0 )
  49. return;
  50. }
  51. }
  52. // Ok, play the sound and add it to the list.
  53. CLocalPlayerFilter filter;
  54. C_BaseEntity::EmitSound( filter, NULL, pSoundName, &vEndPos );
  55. i = g_GroupedSounds.AddToTail();
  56. g_GroupedSounds[i].m_SoundName = pSoundName;
  57. g_GroupedSounds[i].m_vPos = vEndPos;
  58. }
  59. void StartGroupingSounds()
  60. {
  61. Assert( g_GroupedSounds.Count() == 0 );
  62. SetImpactSoundRoute( ShotgunImpactSoundGroup );
  63. }
  64. void EndGroupingSounds()
  65. {
  66. g_GroupedSounds.Purge();
  67. SetImpactSoundRoute( NULL );
  68. }
  69. #else
  70. #include "te_shotgun_shot.h"
  71. // Server doesn't play sounds anyway.
  72. void StartGroupingSounds() {}
  73. void EndGroupingSounds() {}
  74. void FX_WeaponSound ( int iPlayerIndex,
  75. WeaponSound_t sound_type,
  76. const Vector &vOrigin,
  77. CCSWeaponInfo *pWeaponInfo, float flSoundTime ) {};
  78. #endif
  79. // This runs on both the client and the server.
  80. // On the server, it only does the damage calculations.
  81. // On the client, it does all the effects.
  82. void FX_FireBullets(
  83. int iPlayerIndex,
  84. const Vector &vOrigin,
  85. const QAngle &vAngles,
  86. int iWeaponID,
  87. int iMode,
  88. int iSeed,
  89. float fInaccuracy,
  90. float fSpread,
  91. float flSoundTime
  92. )
  93. {
  94. bool bDoEffects = true;
  95. #ifdef CLIENT_DLL
  96. C_CSPlayer *pPlayer = ToCSPlayer( ClientEntityList().GetBaseEntity( iPlayerIndex ) );
  97. #else
  98. CCSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByIndex( iPlayerIndex) );
  99. #endif
  100. const char * weaponAlias = WeaponIDToAlias( iWeaponID );
  101. if ( !weaponAlias )
  102. {
  103. DevMsg("FX_FireBullets: weapon alias for ID %i not found\n", iWeaponID );
  104. return;
  105. }
  106. #if !defined(CLIENT_DLL)
  107. if ( weapon_accuracy_logging.GetBool() )
  108. {
  109. char szFlags[256];
  110. V_strcpy(szFlags, " ");
  111. // #if defined(CLIENT_DLL)
  112. // V_strcat(szFlags, "CLIENT ", sizeof(szFlags));
  113. // #else
  114. // V_strcat(szFlags, "SERVER ", sizeof(szFlags));
  115. // #endif
  116. //
  117. if ( pPlayer->GetMoveType() == MOVETYPE_LADDER )
  118. V_strcat(szFlags, "LADDER ", sizeof(szFlags));
  119. if ( FBitSet( pPlayer->GetFlags(), FL_ONGROUND ) )
  120. V_strcat(szFlags, "GROUND ", sizeof(szFlags));
  121. if ( FBitSet( pPlayer->GetFlags(), FL_DUCKING) )
  122. V_strcat(szFlags, "DUCKING ", sizeof(szFlags));
  123. float fVelocity = pPlayer->GetAbsVelocity().Length2D();
  124. Msg("FireBullets @ %10f [ %s ]: inaccuracy=%f spread=%f max dispersion=%f mode=%2i vel=%10f seed=%3i %s\n",
  125. gpGlobals->curtime, weaponAlias, fInaccuracy, fSpread, fInaccuracy + fSpread, iMode, fVelocity, iSeed, szFlags);
  126. }
  127. #endif
  128. char wpnName[128];
  129. Q_snprintf( wpnName, sizeof( wpnName ), "weapon_%s", weaponAlias );
  130. WEAPON_FILE_INFO_HANDLE hWpnInfo = LookupWeaponInfoSlot( wpnName );
  131. if ( hWpnInfo == GetInvalidWeaponInfoHandle() )
  132. {
  133. DevMsg("FX_FireBullets: LookupWeaponInfoSlot failed for weapon %s\n", wpnName );
  134. return;
  135. }
  136. CCSWeaponInfo *pWeaponInfo = static_cast< CCSWeaponInfo* >( GetFileWeaponInfoFromHandle( hWpnInfo ) );
  137. // Do the firing animation event.
  138. if ( pPlayer && !pPlayer->IsDormant() )
  139. {
  140. if ( iMode == Primary_Mode )
  141. pPlayer->GetPlayerAnimState()->DoAnimationEvent( PLAYERANIMEVENT_FIRE_GUN_PRIMARY );
  142. else
  143. pPlayer->GetPlayerAnimState()->DoAnimationEvent( PLAYERANIMEVENT_FIRE_GUN_SECONDARY );
  144. }
  145. #ifndef CLIENT_DLL
  146. // if this is server code, send the effect over to client as temp entity
  147. // Dispatch one message for all the bullet impacts and sounds.
  148. TE_FireBullets(
  149. iPlayerIndex,
  150. vOrigin,
  151. vAngles,
  152. iWeaponID,
  153. iMode,
  154. iSeed,
  155. fInaccuracy,
  156. fSpread
  157. );
  158. // Let the player remember the usercmd he fired a weapon on. Assists in making decisions about lag compensation.
  159. pPlayer->NoteWeaponFired();
  160. bDoEffects = false; // no effects on server
  161. #endif
  162. iSeed++;
  163. int iDamage = pWeaponInfo->m_iDamage;
  164. float flRange = pWeaponInfo->m_flRange;
  165. int iPenetration = pWeaponInfo->m_iPenetration;
  166. float flRangeModifier = pWeaponInfo->m_flRangeModifier;
  167. int iAmmoType = pWeaponInfo->iAmmoType;
  168. WeaponSound_t sound_type = SINGLE;
  169. // CS HACK, tweak some weapon values based on primary/secondary mode
  170. if ( iWeaponID == WEAPON_GLOCK )
  171. {
  172. if ( iMode == Secondary_Mode )
  173. {
  174. iDamage = 18; // reduced power for burst shots
  175. flRangeModifier = 0.9f;
  176. }
  177. }
  178. else if ( iWeaponID == WEAPON_M4A1 )
  179. {
  180. if ( iMode == Secondary_Mode )
  181. {
  182. flRangeModifier = 0.95f; // slower bullets in silenced mode
  183. sound_type = SPECIAL1;
  184. }
  185. }
  186. else if ( iWeaponID == WEAPON_USP )
  187. {
  188. if ( iMode == Secondary_Mode )
  189. {
  190. iDamage = 30; // reduced damage in silenced mode
  191. sound_type = SPECIAL1;
  192. }
  193. }
  194. if ( bDoEffects)
  195. {
  196. FX_WeaponSound( iPlayerIndex, sound_type, vOrigin, pWeaponInfo, flSoundTime );
  197. }
  198. // Fire bullets, calculate impacts & effects
  199. if ( !pPlayer )
  200. return;
  201. StartGroupingSounds();
  202. #ifdef GAME_DLL
  203. pPlayer->StartNewBulletGroup();
  204. #endif
  205. #if !defined (CLIENT_DLL)
  206. // Move other players back to history positions based on local player's lag
  207. lagcompensation->StartLagCompensation( pPlayer, pPlayer->GetCurrentCommand() );
  208. #endif
  209. RandomSeed( iSeed ); // init random system with this seed
  210. // Get accuracy displacement
  211. float fTheta0 = RandomFloat(0.0f, 2.0f * M_PI);
  212. float fRadius0 = RandomFloat(0.0f, fInaccuracy);
  213. float x0 = fRadius0 * cosf(fTheta0);
  214. float y0 = fRadius0 * sinf(fTheta0);
  215. const int kMaxBullets = 16;
  216. float x1[kMaxBullets], y1[kMaxBullets];
  217. Assert(pWeaponInfo->m_iBullets <= kMaxBullets);
  218. // the RNG can be desynchronized by FireBullet(), so pre-generate all spread offsets
  219. for ( int iBullet=0; iBullet < pWeaponInfo->m_iBullets; iBullet++ )
  220. {
  221. float fTheta1 = RandomFloat(0.0f, 2.0f * M_PI);
  222. float fRadius1 = RandomFloat(0.0f, fSpread);
  223. x1[iBullet] = fRadius1 * cosf(fTheta1);
  224. y1[iBullet] = fRadius1 * sinf(fTheta1);
  225. }
  226. for ( int iBullet=0; iBullet < pWeaponInfo->m_iBullets; iBullet++ )
  227. {
  228. pPlayer->FireBullet(
  229. vOrigin,
  230. vAngles,
  231. flRange,
  232. iPenetration,
  233. iAmmoType,
  234. iDamage,
  235. flRangeModifier,
  236. pPlayer,
  237. bDoEffects,
  238. x0 + x1[iBullet], y0 + y1[iBullet] );
  239. }
  240. #if !defined (CLIENT_DLL)
  241. lagcompensation->FinishLagCompensation( pPlayer );
  242. #endif
  243. EndGroupingSounds();
  244. }
  245. // This runs on both the client and the server.
  246. // On the server, it dispatches a TE_PlantBomb to visible clients.
  247. // On the client, it plays the planting animation.
  248. void FX_PlantBomb( int iPlayerIndex, const Vector &vOrigin, PlantBombOption_t option )
  249. {
  250. #ifdef CLIENT_DLL
  251. C_CSPlayer *pPlayer = ToCSPlayer( ClientEntityList().GetBaseEntity( iPlayerIndex ) );
  252. #else
  253. CCSPlayer *pPlayer = ToCSPlayer( UTIL_PlayerByIndex( iPlayerIndex) );
  254. #endif
  255. // Do the firing animation event.
  256. if ( pPlayer && !pPlayer->IsDormant() )
  257. {
  258. switch ( option )
  259. {
  260. case PLANTBOMB_PLANT:
  261. {
  262. pPlayer->GetPlayerAnimState()->DoAnimationEvent( PLAYERANIMEVENT_FIRE_GUN_PRIMARY );
  263. }
  264. break;
  265. case PLANTBOMB_ABORT:
  266. {
  267. pPlayer->GetPlayerAnimState()->DoAnimationEvent( PLAYERANIMEVENT_CLEAR_FIRING );
  268. }
  269. break;
  270. }
  271. }
  272. #ifndef CLIENT_DLL
  273. // if this is server code, send the effect over to client as temp entity
  274. // Dispatch one message for all the bullet impacts and sounds.
  275. TE_PlantBomb( iPlayerIndex, vOrigin, option );
  276. #endif
  277. }