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.

370 lines
9.3 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include "fx_dod_shared.h"
  8. #include "weapon_dodbase.h"
  9. #include "engine/ivdebugoverlay.h"
  10. #ifndef CLIENT_DLL
  11. #include "ilagcompensationmanager.h"
  12. #endif
  13. #ifndef CLIENT_DLL
  14. //=============================================================================
  15. //
  16. // Explosions.
  17. //
  18. class CTEDODExplosion : public CBaseTempEntity
  19. {
  20. public:
  21. DECLARE_CLASS( CTEDODExplosion, CBaseTempEntity );
  22. DECLARE_SERVERCLASS();
  23. CTEDODExplosion( const char *name );
  24. public:
  25. Vector m_vecOrigin;
  26. Vector m_vecNormal;
  27. };
  28. // Singleton to fire explosion objects
  29. static CTEDODExplosion g_TEDODExplosion( "DODExplosion" );
  30. //-----------------------------------------------------------------------------
  31. // Purpose:
  32. // Input : *name -
  33. //-----------------------------------------------------------------------------
  34. CTEDODExplosion::CTEDODExplosion( const char *name ) : CBaseTempEntity( name )
  35. {
  36. m_vecOrigin.Init();
  37. m_vecNormal.Init();
  38. }
  39. IMPLEMENT_SERVERCLASS_ST( CTEDODExplosion, DT_TEDODExplosion )
  40. SendPropFloat( SENDINFO_NOCHECK( m_vecOrigin[0] ), -1, SPROP_COORD_MP_INTEGRAL ),
  41. SendPropFloat( SENDINFO_NOCHECK( m_vecOrigin[1] ), -1, SPROP_COORD_MP_INTEGRAL ),
  42. SendPropFloat( SENDINFO_NOCHECK( m_vecOrigin[2] ), -1, SPROP_COORD_MP_INTEGRAL ),
  43. SendPropVector( SENDINFO_NOCHECK( m_vecNormal ), 6, 0, -1.0f, 1.0f ),
  44. END_SEND_TABLE()
  45. void TE_DODExplosion( IRecipientFilter &filter, float flDelay, const Vector &vecOrigin, const Vector &vecNormal )
  46. {
  47. VectorCopy( vecOrigin, g_TEDODExplosion.m_vecOrigin );
  48. VectorCopy( vecNormal, g_TEDODExplosion.m_vecNormal );
  49. // Send it over the wire
  50. g_TEDODExplosion.Create( filter, flDelay );
  51. }
  52. #endif
  53. #ifdef CLIENT_DLL
  54. #include "fx_impact.h"
  55. extern void FX_TracerSound( const Vector &start, const Vector &end, int iTracerType );
  56. // this is a cheap ripoff from CBaseCombatWeapon::WeaponSound():
  57. void FX_WeaponSound(
  58. int iPlayerIndex,
  59. WeaponSound_t sound_type,
  60. const Vector &vOrigin,
  61. CDODWeaponInfo *pWeaponInfo )
  62. {
  63. // If we have some sounds from the weapon classname.txt file, play a random one of them
  64. const char *shootsound = pWeaponInfo->aShootSounds[ sound_type ];
  65. if ( !shootsound || !shootsound[0] )
  66. return;
  67. CBroadcastRecipientFilter filter; // this is client side only
  68. if ( !te->CanPredict() )
  69. return;
  70. CBaseEntity::EmitSound( filter, iPlayerIndex, shootsound, &vOrigin );
  71. }
  72. class CGroupedSound
  73. {
  74. public:
  75. string_t m_SoundName;
  76. Vector m_vPos;
  77. };
  78. CUtlVector<CGroupedSound> g_GroupedSounds;
  79. // Called by the ImpactSound function.
  80. void ShotgunImpactSoundGroup( const char *pSoundName, const Vector &vEndPos )
  81. {
  82. // Don't play the sound if it's too close to another impact sound.
  83. for ( int i=0; i < g_GroupedSounds.Count(); i++ )
  84. {
  85. CGroupedSound *pSound = &g_GroupedSounds[i];
  86. if ( vEndPos.DistToSqr( pSound->m_vPos ) < 300*300 )
  87. {
  88. if ( Q_stricmp( pSound->m_SoundName, pSoundName ) == 0 )
  89. return;
  90. }
  91. }
  92. // Ok, play the sound and add it to the list.
  93. CLocalPlayerFilter filter;
  94. C_BaseEntity::EmitSound( filter, NULL, pSoundName, &vEndPos );
  95. int tail = g_GroupedSounds.AddToTail();
  96. g_GroupedSounds[tail].m_SoundName = pSoundName;
  97. g_GroupedSounds[tail].m_vPos = vEndPos;
  98. }
  99. void StartGroupingSounds()
  100. {
  101. Assert( g_GroupedSounds.Count() == 0 );
  102. SetImpactSoundRoute( ShotgunImpactSoundGroup );
  103. }
  104. void EndGroupingSounds()
  105. {
  106. g_GroupedSounds.Purge();
  107. SetImpactSoundRoute( NULL );
  108. }
  109. #else
  110. #include "te_firebullets.h"
  111. // Server doesn't play sounds anyway.
  112. void StartGroupingSounds() {}
  113. void EndGroupingSounds() {}
  114. void FX_WeaponSound ( int iPlayerIndex,
  115. WeaponSound_t sound_type,
  116. const Vector &vOrigin,
  117. CDODWeaponInfo *pWeaponInfo ) {};
  118. #endif
  119. // This runs on both the client and the server.
  120. // On the server, it only does the damage calculations.
  121. // On the client, it does all the effects.
  122. void FX_FireBullets(
  123. int iPlayerIndex,
  124. const Vector &vOrigin,
  125. const QAngle &vAngles,
  126. int iWeaponID,
  127. int iMode,
  128. int iSeed,
  129. float flSpread
  130. )
  131. {
  132. bool bDoEffects = true;
  133. #ifdef CLIENT_DLL
  134. C_DODPlayer *pPlayer = ToDODPlayer( ClientEntityList().GetBaseEntity( iPlayerIndex ) );
  135. #else
  136. CDODPlayer *pPlayer = ToDODPlayer( UTIL_PlayerByIndex( iPlayerIndex) );
  137. #endif
  138. const char * weaponAlias = WeaponIDToAlias( iWeaponID );
  139. if ( !weaponAlias )
  140. {
  141. DevMsg("FX_FireBullets: weapon alias for ID %i not found\n", iWeaponID );
  142. return;
  143. }
  144. //MATTTODO: Why are we looking up the weapon info again when every weapon
  145. // stores its own m_pWeaponInfo pointer?
  146. char wpnName[128];
  147. Q_snprintf( wpnName, sizeof( wpnName ), "weapon_%s", weaponAlias );
  148. WEAPON_FILE_INFO_HANDLE hWpnInfo = LookupWeaponInfoSlot( wpnName );
  149. if ( hWpnInfo == GetInvalidWeaponInfoHandle() )
  150. {
  151. DevMsg("FX_FireBullets: LookupWeaponInfoSlot failed for weapon %s\n", wpnName );
  152. return;
  153. }
  154. CDODWeaponInfo *pWeaponInfo = static_cast< CDODWeaponInfo* >( GetFileWeaponInfoFromHandle( hWpnInfo ) );
  155. #ifdef CLIENT_DLL
  156. if( pPlayer && !pPlayer->IsDormant() )
  157. pPlayer->DoAnimationEvent( PLAYERANIMEVENT_FIRE_GUN );
  158. #else
  159. if( pPlayer )
  160. pPlayer->DoAnimationEvent( PLAYERANIMEVENT_FIRE_GUN );
  161. #endif
  162. #ifndef CLIENT_DLL
  163. // if this is server code, send the effect over to client as temp entity
  164. // Dispatch one message for all the bullet impacts and sounds.
  165. TE_FireBullets(
  166. iPlayerIndex,
  167. vOrigin,
  168. vAngles,
  169. iWeaponID,
  170. iMode,
  171. iSeed,
  172. flSpread
  173. );
  174. bDoEffects = false; // no effects on server
  175. // Let the player remember the usercmd he fired a weapon on. Assists in making decisions about lag compensation.
  176. pPlayer->NoteWeaponFired();
  177. #endif
  178. WeaponSound_t sound_type = SINGLE;
  179. if ( bDoEffects)
  180. {
  181. FX_WeaponSound( iPlayerIndex, sound_type, vOrigin, pWeaponInfo );
  182. }
  183. // Fire bullets, calculate impacts & effects
  184. if ( !pPlayer )
  185. return;
  186. StartGroupingSounds();
  187. #if !defined (CLIENT_DLL)
  188. // Move other players back to history positions based on local player's lag
  189. lagcompensation->StartLagCompensation( pPlayer, pPlayer->GetCurrentCommand() );
  190. #endif
  191. RandomSeed( iSeed );
  192. float x, y;
  193. do
  194. {
  195. x = random->RandomFloat( -0.5, 0.5 ) + random->RandomFloat( -0.5, 0.5 );
  196. y = random->RandomFloat( -0.5, 0.5 ) + random->RandomFloat( -0.5, 0.5 );
  197. } while ( (x * x + y * y) > 1.0f );
  198. Vector vecForward, vecRight, vecUp;
  199. AngleVectors( vAngles, &vecForward, &vecRight, &vecUp );
  200. Vector vecDirShooting = vecForward +
  201. x * flSpread * vecRight +
  202. y * flSpread * vecUp;
  203. vecDirShooting.NormalizeInPlace();
  204. FireBulletsInfo_t info( 1 /*shots*/, vOrigin, vecDirShooting, Vector( flSpread, flSpread, FLOAT32_NAN), MAX_COORD_RANGE, pWeaponInfo->iAmmoType );
  205. info.m_flDamage = pWeaponInfo->m_iDamage;
  206. info.m_pAttacker = pPlayer;
  207. pPlayer->FireBullets( info );
  208. #ifdef CLIENT_DLL
  209. {
  210. trace_t tr;
  211. UTIL_TraceLine( vOrigin, vOrigin + vecDirShooting * MAX_COORD_RANGE, MASK_SOLID, pPlayer, COLLISION_GROUP_NONE, &tr );
  212. // if this is a local player, start at attachment on view model
  213. // else start on attachment on weapon model
  214. int iEntIndex = pPlayer->entindex();
  215. int iAttachment = 1;
  216. Vector vecStart = tr.startpos;
  217. QAngle angAttachment;
  218. C_DODPlayer *pLocalPlayer = C_DODPlayer::GetLocalDODPlayer();
  219. bool bInToolRecordingMode = clienttools->IsInRecordingMode();
  220. // try to align tracers to actual weapon barrel if possible
  221. if ( pPlayer->IsLocalPlayer() && !bInToolRecordingMode )
  222. {
  223. C_BaseViewModel *pViewModel = pPlayer->GetViewModel(0);
  224. if ( pViewModel )
  225. {
  226. iEntIndex = pViewModel->entindex();
  227. pViewModel->GetAttachment( iAttachment, vecStart, angAttachment );
  228. }
  229. }
  230. else if ( pLocalPlayer &&
  231. pLocalPlayer->GetObserverTarget() == pPlayer &&
  232. pLocalPlayer->GetObserverMode() == OBS_MODE_IN_EYE )
  233. {
  234. // get our observer target's view model
  235. C_BaseViewModel *pViewModel = pLocalPlayer->GetViewModel(0);
  236. if ( pViewModel )
  237. {
  238. iEntIndex = pViewModel->entindex();
  239. pViewModel->GetAttachment( iAttachment, vecStart, angAttachment );
  240. }
  241. }
  242. else if ( !pPlayer->IsDormant() )
  243. {
  244. // fill in with third person weapon model index
  245. C_BaseCombatWeapon *pWeapon = pPlayer->GetActiveWeapon();
  246. if( pWeapon )
  247. {
  248. iEntIndex = pWeapon->entindex();
  249. int nModelIndex = pWeapon->GetModelIndex();
  250. int nWorldModelIndex = pWeapon->GetWorldModelIndex();
  251. if ( bInToolRecordingMode && nModelIndex != nWorldModelIndex )
  252. {
  253. pWeapon->SetModelIndex( nWorldModelIndex );
  254. }
  255. pWeapon->GetAttachment( iAttachment, vecStart, angAttachment );
  256. if ( bInToolRecordingMode && nModelIndex != nWorldModelIndex )
  257. {
  258. pWeapon->SetModelIndex( nModelIndex );
  259. }
  260. }
  261. }
  262. switch( pWeaponInfo->m_iTracerType )
  263. {
  264. case 1: // Machine gun, heavy tracer
  265. UTIL_Tracer( vecStart, tr.endpos, iEntIndex, TRACER_DONT_USE_ATTACHMENT, 5000.0, true, "BrightTracer" );
  266. break;
  267. case 2: // rifle, smg, light tracer
  268. vecStart += vecDirShooting * 150;
  269. UTIL_Tracer( vecStart, tr.endpos, iEntIndex, TRACER_DONT_USE_ATTACHMENT, 5000.0, true, "FaintTracer" );
  270. break;
  271. case 0: // pistols etc, just do the sound
  272. {
  273. FX_TracerSound( vecStart, tr.endpos, TRACER_TYPE_DEFAULT );
  274. }
  275. default:
  276. break;
  277. }
  278. }
  279. #endif
  280. #if !defined (CLIENT_DLL)
  281. lagcompensation->FinishLagCompensation( pPlayer );
  282. #endif
  283. EndGroupingSounds();
  284. }