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.

482 lines
13 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: A blood spray effect to expose successful hits.
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include "clienteffectprecachesystem.h"
  8. #include "fx_sparks.h"
  9. #include "iefx.h"
  10. #include "c_te_effect_dispatch.h"
  11. #include "particles_ez.h"
  12. #include "decals.h"
  13. #include "engine/IEngineSound.h"
  14. #include "fx_quad.h"
  15. #include "engine/ivdebugoverlay.h"
  16. #include "shareddefs.h"
  17. #include "fx_blood.h"
  18. #include "view.h"
  19. #include "c_cs_player.h"
  20. CLIENTEFFECT_REGISTER_BEGIN( PrecacheEffectCSBloodSpray )
  21. CLIENTEFFECT_MATERIAL( "effects/blood_gore" )
  22. CLIENTEFFECT_MATERIAL( "effects/blood_drop" )
  23. CLIENTEFFECT_MATERIAL( "effects/blood_puff" )
  24. CLIENTEFFECT_REGISTER_END()
  25. class CHitEffectRamp
  26. {
  27. public:
  28. float m_flDamageAmount;
  29. float m_flMinAlpha;
  30. float m_flMaxAlpha;
  31. float m_flMinSize;
  32. float m_flMaxSize;
  33. float m_flMinVelocity;
  34. float m_flMaxVelocity;
  35. };
  36. void InterpolateRamp(
  37. const CHitEffectRamp &a,
  38. const CHitEffectRamp &b,
  39. CHitEffectRamp &out,
  40. int iDamage )
  41. {
  42. float t = RemapVal( iDamage, a.m_flDamageAmount, b.m_flDamageAmount, 0, 1 );
  43. out.m_flMinAlpha = FLerp( a.m_flMinAlpha, b.m_flMinAlpha, t );
  44. out.m_flMaxAlpha = FLerp( a.m_flMaxAlpha, b.m_flMaxAlpha, t );
  45. out.m_flMinAlpha = clamp( out.m_flMinAlpha, 0, 255 );
  46. out.m_flMaxAlpha = clamp( out.m_flMaxAlpha, 0, 255 );
  47. out.m_flMinSize = FLerp( a.m_flMinSize, b.m_flMinSize, t );
  48. out.m_flMaxSize = FLerp( a.m_flMaxSize, b.m_flMaxSize, t );
  49. out.m_flMinVelocity = FLerp( a.m_flMinVelocity, b.m_flMinVelocity, t );
  50. out.m_flMaxVelocity = FLerp( a.m_flMaxVelocity, b.m_flMaxVelocity, t );
  51. }
  52. void FX_HitEffectSmoke(
  53. CSmartPtr<CBloodSprayEmitter> pEmitter,
  54. int iDamage,
  55. const Vector &vEntryPoint,
  56. const Vector &vDirection,
  57. float flScale)
  58. {
  59. SimpleParticle newParticle;
  60. PMaterialHandle hMaterial = ParticleMgr()->GetPMaterial( "effects/blood_gore" );
  61. // These parameters create a ramp based on how much damage the shot did.
  62. CHitEffectRamp ramps[2] =
  63. {
  64. {
  65. 0,
  66. 30, // min/max alpha
  67. 70,
  68. 0.5, // min/max size
  69. 1,
  70. 0, // min/max velocity (not used here)
  71. 0
  72. },
  73. {
  74. 50,
  75. 30, // min/max alpha
  76. 70,
  77. 1, // min/max size
  78. 2,
  79. 0, // min/max velocity (not used here)
  80. 0
  81. }
  82. };
  83. CHitEffectRamp interpolatedRamp;
  84. InterpolateRamp(
  85. ramps[0],
  86. ramps[1],
  87. interpolatedRamp,
  88. iDamage );
  89. for ( int i=0; i < 2; i++ )
  90. {
  91. SimpleParticle &newParticle = *pEmitter->AddSimpleParticle( hMaterial, vEntryPoint, 0, 0 );
  92. newParticle.m_flLifetime = 0.0f;
  93. newParticle.m_flDieTime = 3.0f;
  94. newParticle.m_uchStartSize = random->RandomInt(
  95. interpolatedRamp.m_flMinSize,
  96. interpolatedRamp.m_flMaxSize ) * flScale;
  97. newParticle.m_uchEndSize = newParticle.m_uchStartSize * 4;
  98. newParticle.m_vecVelocity = Vector( 0, 0, 5 ) + RandomVector( -2, 2 );
  99. newParticle.m_uchStartAlpha = random->RandomInt(
  100. interpolatedRamp.m_flMinSize,
  101. interpolatedRamp.m_flMaxSize );
  102. newParticle.m_uchEndAlpha = 0;
  103. newParticle.m_flRoll = random->RandomFloat( 0, 360 );
  104. newParticle.m_flRollDelta = random->RandomFloat( -1, 1 );
  105. newParticle.m_iFlags = SIMPLE_PARTICLE_FLAG_NO_VEL_DECAY;
  106. float colorRamp = random->RandomFloat( 0.5f, 1.25f );
  107. newParticle.m_uchColor[0] = MIN( 1.0f, colorRamp ) * 255.0f;
  108. newParticle.m_uchColor[1] = MIN( 1.0f, colorRamp ) * 255.0f;
  109. newParticle.m_uchColor[2] = MIN( 1.0f, colorRamp ) * 255.0f;
  110. }
  111. }
  112. void FX_HitEffectBloodSpray(
  113. CSmartPtr<CBloodSprayEmitter> pEmitter,
  114. int iDamage,
  115. const Vector &vEntryPoint,
  116. const Vector &vSprayNormal,
  117. const char *pMaterialName,
  118. float flLODDistance,
  119. float flDistanceScale,
  120. float flScale,
  121. float flSpeed )
  122. {
  123. PMaterialHandle hMaterial = ParticleMgr()->GetPMaterial( pMaterialName );
  124. SimpleParticle *pParticle;
  125. float color[3] = { 1.0, 0, 0 };
  126. Vector up( 0, 0, 1 );
  127. Vector right = up.Cross( vSprayNormal );
  128. VectorNormalize( right );
  129. // These parameters create a ramp based on how much damage the shot did.
  130. CHitEffectRamp ramps[2] =
  131. {
  132. {
  133. 0,
  134. 80, // min/max alpha
  135. 128,
  136. flScale/2,// min/max size
  137. flScale,
  138. 10, // min/max velocity
  139. 20
  140. },
  141. {
  142. 50,
  143. 80, // min/max alpha
  144. 128,
  145. flScale/2,// min/max size
  146. flScale,
  147. 30, // min/max velocity
  148. 60
  149. }
  150. };
  151. CHitEffectRamp interpolatedRamp;
  152. InterpolateRamp(
  153. ramps[0],
  154. ramps[1],
  155. interpolatedRamp,
  156. iDamage );
  157. for ( int i = 0; i < 6; i++ )
  158. {
  159. // Originate from within a circle '2 * scale' inches in diameter.
  160. Vector offset = vEntryPoint + ( flScale * vSprayNormal * 0.5 );
  161. offset += right * random->RandomFloat( -1, 1 ) * flScale;
  162. offset += up * random->RandomFloat( -1, 1 ) * flScale;
  163. pParticle = pEmitter->AddSimpleParticle( hMaterial, offset, 0, 0 );
  164. if ( pParticle != NULL )
  165. {
  166. pParticle->m_flLifetime = 0.0f;
  167. pParticle->m_flDieTime = random->RandomFloat( 0.7f, 1.3f);
  168. // All the particles are between red and white. The whiter the particle is, the slower it goes.
  169. float whiteness = random->RandomFloat( 0.1, 0.7 );
  170. float speedFactor = 1 - whiteness;
  171. float spread = 0.5f;
  172. pParticle->m_vecVelocity.Random( -spread, spread );
  173. pParticle->m_vecVelocity += vSprayNormal * random->RandomInt( interpolatedRamp.m_flMinVelocity, interpolatedRamp.m_flMaxVelocity ) * flSpeed * speedFactor;
  174. float colorRamp = random->RandomFloat( 0.5f, 0.75f ) + flLODDistance;
  175. pParticle->m_uchColor[0] = MIN( 1.0f, color[0] * colorRamp ) * 255.0f;
  176. pParticle->m_uchColor[1] = MIN( 1.0f, whiteness * colorRamp ) * 255.0f;
  177. pParticle->m_uchColor[2] = MIN( 1.0f, whiteness * colorRamp ) * 255.0f;
  178. pParticle->m_uchStartSize = random->RandomFloat( interpolatedRamp.m_flMinSize, interpolatedRamp.m_flMaxSize ) * flDistanceScale;
  179. pParticle->m_uchEndSize = pParticle->m_uchStartSize * 4 * flDistanceScale;
  180. pParticle->m_uchStartAlpha = random->RandomInt( interpolatedRamp.m_flMinAlpha, interpolatedRamp.m_flMaxAlpha );
  181. pParticle->m_uchEndAlpha = 0;
  182. pParticle->m_flRoll = random->RandomInt( 0, 360 );
  183. pParticle->m_flRollDelta = random->RandomFloat( -4.0f, 4.0f );
  184. }
  185. }
  186. }
  187. void FX_HitEffectBloodSplatter(
  188. CSmartPtr<CBloodSprayEmitter> pTrailEmitter,
  189. int iDamage,
  190. const Vector &vExitPoint,
  191. const Vector &vSplatterNormal,
  192. float flLODDistance )
  193. {
  194. float flScale = 4;
  195. pTrailEmitter->SetSortOrigin( vExitPoint );
  196. PMaterialHandle hMaterial = ParticleMgr()->GetPMaterial( "effects/blood_drop" );
  197. Vector up( 0, 0, 1 );
  198. Vector right = up.Cross( vSplatterNormal );
  199. VectorNormalize( right );
  200. // These parameters create a ramp based on how much damage the shot did.
  201. CHitEffectRamp ramps[2] =
  202. {
  203. {
  204. 0,
  205. 0, // min/max alpha
  206. 75,
  207. 1.5f, // min/max size
  208. 2.0f,
  209. 25.0f * flScale, // min/max velocity
  210. 35.0f * flScale
  211. },
  212. {
  213. 50,
  214. 0, // min/max alpha
  215. 140,
  216. 1.5f,// min/max size
  217. 2.0f,
  218. 65.0f * flScale, // min/max velocity
  219. 75.0f * flScale
  220. }
  221. };
  222. CHitEffectRamp interpolatedRamp;
  223. InterpolateRamp(
  224. ramps[0],
  225. ramps[1],
  226. interpolatedRamp,
  227. iDamage );
  228. for ( int i = 0; i < 20; i++ )
  229. {
  230. // Originate from within a circle 'scale' inches in diameter.
  231. Vector offset = vExitPoint;
  232. offset += right * random->RandomFloat( -0.15f, 0.15f ) * flScale;
  233. offset += up * random->RandomFloat( -0.15f, 0.15f ) * flScale;
  234. SimpleParticle *tParticle = (SimpleParticle*)pTrailEmitter->AddSimpleParticle(
  235. hMaterial,
  236. vExitPoint,
  237. random->RandomFloat( 0.225f, 0.35f ),
  238. random->RandomFloat( interpolatedRamp.m_flMinSize, interpolatedRamp.m_flMaxSize )
  239. );
  240. if ( tParticle == NULL )
  241. break;
  242. Vector offDir = vSplatterNormal + RandomVector( -0.05f, 0.05f );
  243. tParticle->m_vecVelocity = offDir * random->RandomFloat( interpolatedRamp.m_flMinVelocity, interpolatedRamp.m_flMaxVelocity );
  244. tParticle->m_iFlags = SIMPLE_PARTICLE_FLAG_NO_VEL_DECAY;
  245. tParticle->m_uchColor[0] = 150;
  246. tParticle->m_uchColor[1] = 0;
  247. tParticle->m_uchColor[2] = 0;
  248. tParticle->m_uchStartAlpha = interpolatedRamp.m_flMaxAlpha / 2;
  249. tParticle->m_uchEndAlpha = 0;
  250. }
  251. }
  252. //-----------------------------------------------------------------------------
  253. // Purpose:
  254. // Input : origin -
  255. // normal -
  256. // scale -
  257. //-----------------------------------------------------------------------------
  258. void FX_CS_BloodSpray( const Vector &origin, const Vector &normal, float flDamage )
  259. {
  260. if ( UTIL_IsLowViolence() )
  261. return;
  262. static ConVar *violence_hblood = cvar->FindVar( "violence_hblood" );
  263. if ( violence_hblood && !violence_hblood->GetBool() )
  264. return;
  265. Vector offset;
  266. int i;
  267. float r = 64;
  268. float g = 0;
  269. float b = 4;
  270. float scale = 0.5 + clamp( flDamage/50.f, 0.0, 1.0 ) ;
  271. //Find area ambient light color and use it to tint smoke
  272. Vector worldLight = WorldGetLightForPoint( origin, true );
  273. Vector color = Vector( (float)(worldLight[0] * r) / 255.0f, (float)(worldLight[1] * g) / 255.0f, (float)(worldLight[2] * b) / 255.0f );
  274. float colorRamp;
  275. Vector offDir;
  276. CSmartPtr<CBloodSprayEmitter> pSimple = CBloodSprayEmitter::Create( "bloodgore" );
  277. if ( !pSimple )
  278. return;
  279. pSimple->SetSortOrigin( origin );
  280. pSimple->SetGravity( 0 );
  281. // Blood impact
  282. PMaterialHandle hMaterial = ParticleMgr()->GetPMaterial( "effects/blood_core" );
  283. SimpleParticle *pParticle;
  284. Vector dir = normal * RandomVector( -0.5f, 0.5f );
  285. offset = origin + ( 2.0f * normal );
  286. pParticle = (SimpleParticle *) pSimple->AddParticle( sizeof( SimpleParticle ), hMaterial, offset );
  287. if ( pParticle != NULL )
  288. {
  289. pParticle->m_flLifetime = 0.0f;
  290. pParticle->m_flDieTime = 0.75f;
  291. pParticle->m_vecVelocity = dir * random->RandomFloat( 16.0f, 32.0f );
  292. pParticle->m_vecVelocity[2] -= random->RandomFloat( 8.0f, 16.0f );
  293. colorRamp = random->RandomFloat( 0.75f, 2.0f );
  294. pParticle->m_uchColor[0] = MIN( 1.0f, color[0] * colorRamp ) * 255.0f;
  295. pParticle->m_uchColor[1] = MIN( 1.0f, color[1] * colorRamp ) * 255.0f;
  296. pParticle->m_uchColor[2] = MIN( 1.0f, color[2] * colorRamp ) * 255.0f;
  297. pParticle->m_uchStartSize = 8;
  298. pParticle->m_uchEndSize = 32;
  299. pParticle->m_uchStartAlpha = 255;
  300. pParticle->m_uchEndAlpha = 0;
  301. pParticle->m_flRoll = random->RandomInt( 0, 360 );
  302. pParticle->m_flRollDelta = 0;
  303. }
  304. hMaterial = ParticleMgr()->GetPMaterial( "effects/blood_gore" );
  305. for ( i = 0; i < 4; i++ )
  306. {
  307. offset = origin + ( 2.0f * normal );
  308. pParticle = (SimpleParticle *) pSimple->AddParticle( sizeof( SimpleParticle ), hMaterial, offset );
  309. if ( pParticle != NULL )
  310. {
  311. pParticle->m_flLifetime = 0.0f;
  312. pParticle->m_flDieTime = random->RandomFloat( 0.75f, 1.0f);
  313. pParticle->m_vecVelocity = dir * random->RandomFloat( 16.0f, 32.0f )*(i+1);
  314. pParticle->m_vecVelocity[2] -= random->RandomFloat( 16.0f, 32.0f )*(i+1);
  315. colorRamp = random->RandomFloat( 0.75f, 2.0f );
  316. pParticle->m_uchColor[0] = MIN( 1.0f, color[0] * colorRamp ) * 255.0f;
  317. pParticle->m_uchColor[1] = MIN( 1.0f, color[1] * colorRamp ) * 255.0f;
  318. pParticle->m_uchColor[2] = MIN( 1.0f, color[2] * colorRamp ) * 255.0f;
  319. pParticle->m_uchStartSize = scale * random->RandomInt( 4, 8 );
  320. pParticle->m_uchEndSize = pParticle->m_uchStartSize * 4;
  321. pParticle->m_uchStartAlpha = 255;
  322. pParticle->m_uchEndAlpha = 0;
  323. pParticle->m_flRoll = random->RandomInt( 0, 360 );
  324. pParticle->m_flRollDelta = 0;
  325. }
  326. }
  327. //
  328. // Dump out drops
  329. //
  330. TrailParticle *tParticle;
  331. CSmartPtr<CTrailParticles> pTrailEmitter = CTrailParticles::Create( "blooddrops" );
  332. if ( !pTrailEmitter )
  333. return;
  334. pTrailEmitter->SetSortOrigin( origin );
  335. // Partial gravity on blood drops
  336. pTrailEmitter->SetGravity( 400.0 );
  337. // Enable simple collisions with nearby surfaces
  338. pTrailEmitter->Setup(origin, &normal, 1, 10, 100, 400, 0.2, 0 );
  339. hMaterial = ParticleMgr()->GetPMaterial( "effects/blood_drop" );
  340. //
  341. // Shorter droplets
  342. //
  343. for ( i = 0; i < 32; i++ )
  344. {
  345. // Originate from within a circle 'scale' inches in diameter
  346. offset = origin;
  347. tParticle = (TrailParticle *) pTrailEmitter->AddParticle( sizeof(TrailParticle), hMaterial, offset );
  348. if ( tParticle == NULL )
  349. break;
  350. tParticle->m_flLifetime = 0.0f;
  351. offDir = RandomVector( -1.0f, 1.0f );
  352. tParticle->m_vecVelocity = offDir * random->RandomFloat( 32.0f, 128.0f );
  353. tParticle->m_flWidth = scale * random->RandomFloat( 1.0f, 3.0f );
  354. tParticle->m_flLength = random->RandomFloat( 0.1f, 0.15f );
  355. tParticle->m_flDieTime = random->RandomFloat( 0.5f, 1.0f );
  356. FloatToColor32( tParticle->m_color, color[0], color[1], color[2], 1.0f );
  357. }
  358. }
  359. //-----------------------------------------------------------------------------
  360. // Purpose:
  361. // Input : bloodtype -
  362. // r -
  363. // g -
  364. // b -
  365. //-----------------------------------------------------------------------------
  366. void GetBloodColorForTeam( int iTeam, unsigned char &r, unsigned char &g, unsigned char &b )
  367. {
  368. }
  369. //-----------------------------------------------------------------------------
  370. // Purpose: Intercepts the blood spray message.
  371. //-----------------------------------------------------------------------------
  372. void CSBloodSprayCallback( const CEffectData &data )
  373. {
  374. FX_CS_BloodSpray( data.m_vOrigin, data.m_vNormal, data.m_flMagnitude );
  375. }
  376. DECLARE_CLIENT_EFFECT( "csblood", CSBloodSprayCallback );