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.

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