diff options
Diffstat (limited to 'game/client/fx_blood.cpp')
| -rw-r--r-- | game/client/fx_blood.cpp | 591 |
1 files changed, 591 insertions, 0 deletions
diff --git a/game/client/fx_blood.cpp b/game/client/fx_blood.cpp new file mode 100644 index 0000000..441084b --- /dev/null +++ b/game/client/fx_blood.cpp @@ -0,0 +1,591 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: A blood spray effect, like a big exit wound, used when people are +// violently impaled, skewered, eviscerated, etc. +// +//=============================================================================// + +#include "cbase.h" +#include "clienteffectprecachesystem.h" +#include "fx_sparks.h" +#include "iefx.h" +#include "c_te_effect_dispatch.h" +#include "particles_ez.h" +#include "decals.h" +#include "engine/IEngineSound.h" +#include "fx_quad.h" +#include "engine/ivdebugoverlay.h" +#include "shareddefs.h" +#include "fx.h" +#include "fx_blood.h" +#include "effect_color_tables.h" +#include "particle_simple3d.h" +#include "particle_parse.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +CLIENTEFFECT_REGISTER_BEGIN( PrecacheEffectBloodSpray ) +CLIENTEFFECT_MATERIAL( "effects/blood_core" ) +CLIENTEFFECT_MATERIAL( "effects/blood_gore" ) +CLIENTEFFECT_MATERIAL( "effects/blood_drop" ) +CLIENTEFFECT_MATERIAL( "effects/blood_puff" ) +CLIENTEFFECT_REGISTER_END() + +// Cached material handles +PMaterialHandle g_Blood_Core = NULL; +PMaterialHandle g_Blood_Gore = NULL; +PMaterialHandle g_Blood_Drops = NULL; + +//----------------------------------------------------------------------------- +// Purpose: +// Input : bloodtype - +// r - +// g - +// b - +//----------------------------------------------------------------------------- +void GetBloodColor( int bloodtype, colorentry_t &color ) +{ + int i; + + for( i = 0 ; i < COLOR_TABLE_SIZE( bloodcolors ) ; i++ ) + { + if( bloodcolors[i].index == bloodtype ) + { + color = bloodcolors[ i ]; + return; + } + } + + // build a ridiculous default color + color.r = 255; + color.g = 0; + color.b = 255; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : &origin - +// &normal - +// scale - +// r - +// g - +// b - +// flags - +//----------------------------------------------------------------------------- +void FX_BloodSpray( const Vector &origin, const Vector &normal, float scale, unsigned char r, unsigned char g, unsigned char b, int flags ) +{ + if ( UTIL_IsLowViolence() ) + return; + + //debugoverlay->AddLineOverlay( origin, origin + normal * 72, 255, 255, 255, true, 10 ); + + Vector offset; + float spread = 0.2f; + + //Find area ambient light color and use it to tint smoke + Vector worldLight = WorldGetLightForPoint( origin, true ); + Vector color = Vector( (float)(worldLight[0] * r) / 255.0f, (float)(worldLight[1] * g) / 255.0f, (float)(worldLight[2] * b) / 255.0f ); + float colorRamp; + + int i; + + Vector offDir; + + Vector right; + Vector up; + + if (normal != Vector(0, 0, 1) ) + { + right = normal.Cross( Vector(0, 0, 1) ); + up = right.Cross( normal ); + } + else + { + right = Vector(0, 0, 1); + up = right.Cross( normal ); + } + + // + // Dump out drops + // + if (flags & FX_BLOODSPRAY_DROPS) + { + TrailParticle *tParticle; + + CSmartPtr<CTrailParticles> pTrailEmitter = CTrailParticles::Create( "blooddrops" ); + if ( !pTrailEmitter ) + return; + + pTrailEmitter->SetSortOrigin( origin ); + + // Partial gravity on blood drops. + pTrailEmitter->SetGravity( 600.0 ); + + pTrailEmitter->GetBinding().SetBBox( origin - Vector( 32, 32, 32 ), origin + Vector( 32, 32, 32 ) ); + pTrailEmitter->SetFlag( bitsPARTICLE_TRAIL_VELOCITY_DAMPEN ); + pTrailEmitter->SetVelocityDampen( 0.2f ); + + PMaterialHandle hMaterial = ParticleMgr()->GetPMaterial( "effects/blood_drop" ); + + // + // Long stringy drops of blood. + // + for ( i = 0; i < 14; i++ ) + { + // Originate from within a circle 'scale' inches in diameter. + offset = origin; + offset += right * random->RandomFloat( -0.5f, 0.5f ) * scale; + offset += up * random->RandomFloat( -0.5f, 0.5f ) * scale; + + tParticle = (TrailParticle *) pTrailEmitter->AddParticle( sizeof(TrailParticle), hMaterial, offset ); + + if ( tParticle == NULL ) + break; + + tParticle->m_flLifetime = 0.0f; + + offDir = normal + RandomVector( -0.3f, 0.3f ); + + tParticle->m_vecVelocity = offDir * random->RandomFloat( 4.0f * scale, 40.0f * scale ); + tParticle->m_vecVelocity[2] += random->RandomFloat( 4.0f, 16.0f ) * scale; + + tParticle->m_flWidth = random->RandomFloat( 0.125f, 0.275f ) * scale; + tParticle->m_flLength = random->RandomFloat( 0.02f, 0.03f ) * scale; + tParticle->m_flDieTime = random->RandomFloat( 0.5f, 1.0f ); + + FloatToColor32( tParticle->m_color, color[0], color[1], color[2], 1.0f ); + } + + // + // Shorter droplets. + // + for ( i = 0; i < 24; i++ ) + { + // Originate from within a circle 'scale' inches in diameter. + offset = origin; + offset += right * random->RandomFloat( -0.5f, 0.5f ) * scale; + offset += up * random->RandomFloat( -0.5f, 0.5f ) * scale; + + tParticle = (TrailParticle *) pTrailEmitter->AddParticle( sizeof(TrailParticle), hMaterial, offset ); + + if ( tParticle == NULL ) + break; + + tParticle->m_flLifetime = 0.0f; + + offDir = normal + RandomVector( -1.0f, 1.0f ); + offDir[2] += random->RandomFloat(0, 1.0f); + + tParticle->m_vecVelocity = offDir * random->RandomFloat( 2.0f * scale, 25.0f * scale ); + tParticle->m_vecVelocity[2] += random->RandomFloat( 4.0f, 16.0f ) * scale; + + tParticle->m_flWidth = random->RandomFloat( 0.25f, 0.375f ) * scale; + tParticle->m_flLength = random->RandomFloat( 0.0025f, 0.005f ) * scale; + tParticle->m_flDieTime = random->RandomFloat( 0.5f, 1.0f ); + + FloatToColor32( tParticle->m_color, color[0], color[1], color[2], 1.0f ); + } + } + + if ((flags & FX_BLOODSPRAY_GORE) || (flags & FX_BLOODSPRAY_CLOUD)) + { + CSmartPtr<CBloodSprayEmitter> pSimple = CBloodSprayEmitter::Create( "bloodgore" ); + if ( !pSimple ) + return; + + pSimple->SetSortOrigin( origin ); + pSimple->SetGravity( 0 ); + + PMaterialHandle hMaterial; + + // + // Tight blossom of blood at the center. + // + if (flags & FX_BLOODSPRAY_GORE) + { + hMaterial = ParticleMgr()->GetPMaterial( "effects/blood_gore" ); + + SimpleParticle *pParticle; + + for ( i = 0; i < 6; i++ ) + { + // Originate from within a circle 'scale' inches in diameter. + offset = origin + ( 0.5 * scale * normal ); + offset += right * random->RandomFloat( -0.5f, 0.5f ) * scale; + offset += up * random->RandomFloat( -0.5f, 0.5f ) * scale; + + pParticle = (SimpleParticle *) pSimple->AddParticle( sizeof( SimpleParticle ), hMaterial, offset ); + + if ( pParticle != NULL ) + { + pParticle->m_flLifetime = 0.0f; + pParticle->m_flDieTime = 0.3f; + + spread = 0.2f; + pParticle->m_vecVelocity.Random( -spread, spread ); + pParticle->m_vecVelocity += normal * random->RandomInt( 10, 100 ); + //VectorNormalize( pParticle->m_vecVelocity ); + + colorRamp = random->RandomFloat( 0.75f, 1.25f ); + + pParticle->m_uchColor[0] = MIN( 1.0f, color[0] * colorRamp ) * 255.0f; + pParticle->m_uchColor[1] = MIN( 1.0f, color[1] * colorRamp ) * 255.0f; + pParticle->m_uchColor[2] = MIN( 1.0f, color[2] * colorRamp ) * 255.0f; + + pParticle->m_uchStartSize = random->RandomFloat( scale * 0.25, scale ); + pParticle->m_uchEndSize = pParticle->m_uchStartSize * 2; + + pParticle->m_uchStartAlpha = random->RandomInt( 200, 255 ); + pParticle->m_uchEndAlpha = 0; + + pParticle->m_flRoll = random->RandomInt( 0, 360 ); + pParticle->m_flRollDelta = 0.0f; + } + } + } + + // + // Diffuse cloud just in front of the exit wound. + // + if (flags & FX_BLOODSPRAY_CLOUD) + { + hMaterial = ParticleMgr()->GetPMaterial( "effects/blood_puff" ); + + SimpleParticle *pParticle; + + for ( i = 0; i < 6; i++ ) + { + // Originate from within a circle '2 * scale' inches in diameter. + offset = origin + ( scale * normal ); + offset += right * random->RandomFloat( -1, 1 ) * scale; + offset += up * random->RandomFloat( -1, 1 ) * scale; + + pParticle = (SimpleParticle *) pSimple->AddParticle( sizeof( SimpleParticle ), hMaterial, offset ); + + if ( pParticle != NULL ) + { + pParticle->m_flLifetime = 0.0f; + pParticle->m_flDieTime = random->RandomFloat( 0.5f, 0.8f); + + spread = 0.5f; + pParticle->m_vecVelocity.Random( -spread, spread ); + pParticle->m_vecVelocity += normal * random->RandomInt( 100, 200 ); + + colorRamp = random->RandomFloat( 0.75f, 1.25f ); + + pParticle->m_uchColor[0] = MIN( 1.0f, color[0] * colorRamp ) * 255.0f; + pParticle->m_uchColor[1] = MIN( 1.0f, color[1] * colorRamp ) * 255.0f; + pParticle->m_uchColor[2] = MIN( 1.0f, color[2] * colorRamp ) * 255.0f; + + pParticle->m_uchStartSize = random->RandomFloat( scale * 1.5f, scale * 2.0f ); + pParticle->m_uchEndSize = pParticle->m_uchStartSize * 4; + + pParticle->m_uchStartAlpha = random->RandomInt( 80, 128 ); + pParticle->m_uchEndAlpha = 0; + + pParticle->m_flRoll = random->RandomInt( 0, 360 ); + pParticle->m_flRollDelta = 0.0f; + } + } + } + } + + // TODO: Play a sound? + //CLocalPlayerFilter filter; + //C_BaseEntity::EmitSound( filter, SOUND_FROM_WORLD, CHAN_VOICE, "Physics.WaterSplash", 1.0, ATTN_NORM, 0, 100, &origin ); +} + +//----------------------------------------------------------------------------- +// Purpose: Used for bullets hitting bleeding surfaces +// Input : origin - +// normal - +// scale - This parameter is not currently used +//----------------------------------------------------------------------------- +void FX_BloodBulletImpact( const Vector &origin, const Vector &normal, float scale /*NOTE: Unused!*/, unsigned char r, unsigned char g, unsigned char b ) +{ + if ( UTIL_IsLowViolence() ) + return; + + Vector offset; + + //Find area ambient light color and use it to tint smoke + Vector worldLight = WorldGetLightForPoint( origin, true ); + + if ( gpGlobals->maxClients > 1 ) + { + worldLight = Vector( 1.0, 1.0, 1.0 ); + r = 96; + g = 0; + b = 10; + } + + Vector color = Vector( (float)(worldLight[0] * r) / 255.0f, (float)(worldLight[1] * g) / 255.0f, (float)(worldLight[2] * b) / 255.0f ); + float colorRamp; + + Vector offDir; + + CSmartPtr<CBloodSprayEmitter> pSimple = CBloodSprayEmitter::Create( "bloodgore" ); + if ( !pSimple ) + return; + + pSimple->SetSortOrigin( origin ); + pSimple->SetGravity( 200 ); + + // Setup a bounding box to contain the particles without (stops auto-updating) + pSimple->GetBinding().SetBBox( origin - Vector( 16, 16, 16 ), origin + Vector( 16, 16, 16 ) ); + + // Cache the material if we haven't already + if ( g_Blood_Core == NULL ) + { + g_Blood_Core = ParticleMgr()->GetPMaterial( "effects/blood_core" ); + } + + SimpleParticle *pParticle; + + Vector dir = normal * RandomVector( -0.5f, 0.5f ); + + offset = origin + ( 2.0f * normal ); + + pParticle = (SimpleParticle *) pSimple->AddParticle( sizeof( SimpleParticle ), g_Blood_Core, offset ); + + if ( pParticle != NULL ) + { + pParticle->m_flLifetime = 0.0f; + pParticle->m_flDieTime = random->RandomFloat( 0.25f, 0.5f); + + pParticle->m_vecVelocity = dir * random->RandomFloat( 16.0f, 32.0f ); + pParticle->m_vecVelocity[2] -= random->RandomFloat( 8.0f, 16.0f ); + + colorRamp = random->RandomFloat( 0.75f, 2.0f ); + + pParticle->m_uchColor[0] = MIN( 1.0f, color[0] * colorRamp ) * 255.0f; + pParticle->m_uchColor[1] = MIN( 1.0f, color[1] * colorRamp ) * 255.0f; + pParticle->m_uchColor[2] = MIN( 1.0f, color[2] * colorRamp ) * 255.0f; + + pParticle->m_uchStartSize = random->RandomInt( 2, 4 ); + pParticle->m_uchEndSize = pParticle->m_uchStartSize * 8; + + pParticle->m_uchStartAlpha = 255; + pParticle->m_uchEndAlpha = 0; + + pParticle->m_flRoll = random->RandomInt( 0, 360 ); + pParticle->m_flRollDelta = 0.0f; + } + + // Cache the material if we haven't already + if ( g_Blood_Gore == NULL ) + { + g_Blood_Gore = ParticleMgr()->GetPMaterial( "effects/blood_gore" ); + } + + for ( int i = 0; i < 4; i++ ) + { + offset = origin + ( 2.0f * normal ); + + pParticle = (SimpleParticle *) pSimple->AddParticle( sizeof( SimpleParticle ), g_Blood_Gore, offset ); + + if ( pParticle != NULL ) + { + pParticle->m_flLifetime = 0.0f; + pParticle->m_flDieTime = random->RandomFloat( 0.5f, 0.75f); + + pParticle->m_vecVelocity = dir * random->RandomFloat( 16.0f, 32.0f )*(i+1); + pParticle->m_vecVelocity[2] -= random->RandomFloat( 32.0f, 64.0f )*(i+1); + + colorRamp = random->RandomFloat( 0.75f, 2.0f ); + + pParticle->m_uchColor[0] = MIN( 1.0f, color[0] * colorRamp ) * 255.0f; + pParticle->m_uchColor[1] = MIN( 1.0f, color[1] * colorRamp ) * 255.0f; + pParticle->m_uchColor[2] = MIN( 1.0f, color[2] * colorRamp ) * 255.0f; + + pParticle->m_uchStartSize = random->RandomInt( 2, 4 ); + pParticle->m_uchEndSize = pParticle->m_uchStartSize * 4; + + pParticle->m_uchStartAlpha = 255; + pParticle->m_uchEndAlpha = 0; + + pParticle->m_flRoll = random->RandomInt( 0, 360 ); + pParticle->m_flRollDelta = 0.0f; + } + } + + // + // Dump out drops + // + TrailParticle *tParticle; + + CSmartPtr<CTrailParticles> pTrailEmitter = CTrailParticles::Create( "blooddrops" ); + if ( !pTrailEmitter ) + return; + + pTrailEmitter->SetSortOrigin( origin ); + + // Partial gravity on blood drops + pTrailEmitter->SetGravity( 400.0 ); + + // Enable simple collisions with nearby surfaces + pTrailEmitter->Setup(origin, &normal, 1, 10, 100, 400, 0.2, 0 ); + + if ( g_Blood_Drops == NULL ) + { + g_Blood_Drops = ParticleMgr()->GetPMaterial( "effects/blood_drop" ); + } + + // + // Shorter droplets + // + for ( int i = 0; i < 8; i++ ) + { + // Originate from within a circle 'scale' inches in diameter + offset = origin; + + tParticle = (TrailParticle *) pTrailEmitter->AddParticle( sizeof(TrailParticle), g_Blood_Drops, offset ); + + if ( tParticle == NULL ) + break; + + tParticle->m_flLifetime = 0.0f; + + offDir = RandomVector( -1.0f, 1.0f ); + + tParticle->m_vecVelocity = offDir * random->RandomFloat( 64.0f, 128.0f ); + + tParticle->m_flWidth = random->RandomFloat( 0.5f, 2.0f ); + tParticle->m_flLength = random->RandomFloat( 0.05f, 0.15f ); + tParticle->m_flDieTime = random->RandomFloat( 0.25f, 0.5f ); + + FloatToColor32( tParticle->m_color, color[0], color[1], color[2], 1.0f ); + } + + // TODO: Play a sound? + //CLocalPlayerFilter filter; + //C_BaseEntity::EmitSound( filter, SOUND_FROM_WORLD, CHAN_VOICE, "Physics.WaterSplash", 1.0, ATTN_NORM, 0, 100, &origin ); +} + +// FIXME: This will be simplified when the initializer can take color parameters as an input +// For now, we use different systems + +struct ParticleForBlood_t +{ + int nColor; + const char *lpszParticleSystemName; +}; + +ParticleForBlood_t bloodCallbacks[] = +{ + { BLOOD_COLOR_RED, "blood_impact_red_01" }, + { BLOOD_COLOR_GREEN, "blood_impact_green_01" }, + { BLOOD_COLOR_YELLOW, "blood_impact_yellow_01" }, +#if defined( HL2_EPISODIC ) + { BLOOD_COLOR_ANTLION, "blood_impact_antlion_01" }, // FIXME: Move to Base HL2 + { BLOOD_COLOR_ZOMBIE, "blood_impact_zombie_01" }, // FIXME: Move to Base HL2 + { BLOOD_COLOR_ANTLION_WORKER, "blood_impact_antlion_worker_01" }, +#endif // HL2_EPISODIC +}; + +//----------------------------------------------------------------------------- +// Purpose: Intercepts the blood spray message. +//----------------------------------------------------------------------------- +void BloodSprayCallback( const CEffectData &data ) +{ + colorentry_t color; + + GetBloodColor( data.m_nColor, color ); + FX_BloodSpray( data.m_vOrigin, data.m_vNormal, data.m_flScale, color.r, color.g, color.b, data.m_fFlags ); +} + +DECLARE_CLIENT_EFFECT( "bloodspray", BloodSprayCallback ); + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void BloodImpactCallback( const CEffectData & data ) +{ + bool bFoundBlood = false; + + // Find which sort of blood we are + for ( int i = 0; i < ARRAYSIZE( bloodCallbacks ); i++ ) + { + if ( bloodCallbacks[i].nColor == data.m_nColor ) + { + QAngle vecAngles; + VectorAngles( -data.m_vNormal, vecAngles ); + DispatchParticleEffect( bloodCallbacks[i].lpszParticleSystemName, data.m_vOrigin, vecAngles ); + bFoundBlood = true; + break; + } + } + + if ( bFoundBlood == false ) + { + Vector vecPosition; + vecPosition = data.m_vOrigin; + + // Fetch the blood color. + colorentry_t color; + GetBloodColor( data.m_nColor, color ); + + FX_BloodBulletImpact( vecPosition, data.m_vNormal, data.m_flScale, color.r, color.g, color.b ); + } +} + +DECLARE_CLIENT_EFFECT( "BloodImpact", BloodImpactCallback ); + +//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- +void HunterDamageCallback( const CEffectData &data ) +{ + CSmartPtr<CSimple3DEmitter> pGlassEmitter = CSimple3DEmitter::Create( "HunterDamage" ); + if ( pGlassEmitter == NULL ) + return; + + pGlassEmitter->SetSortOrigin( data.m_vOrigin ); + + // Handle increased scale + const float flMaxSpeed = 400.0f; + const float flMinSpeed = 50.0f; + float flAngularSpray = 1.0f; + + // Setup our collision information + pGlassEmitter->m_ParticleCollision.Setup( data.m_vOrigin, &data.m_vNormal, flAngularSpray, flMinSpeed, flMaxSpeed, 600.0f, 0.2f ); + + Vector dir, end; + + int numFlecks = 32; + + Particle3D *pFleckParticle; + Vector spawnOffset; + + //Dump out flecks + for ( int i = 0; i < numFlecks; i++ ) + { + spawnOffset = data.m_vOrigin + RandomVector( -32.0f, 32.0f ); + pFleckParticle = (Particle3D *) pGlassEmitter->AddParticle( sizeof(Particle3D), g_Mat_Fleck_Antlion[random->RandomInt(0,1)], spawnOffset ); + + if ( pFleckParticle == NULL ) + break; + + pFleckParticle->m_flLifeRemaining = random->RandomFloat( 2.0f, 3.0f ); + + dir[0] = data.m_vNormal[0] + random->RandomFloat( -flAngularSpray, flAngularSpray ); + dir[1] = data.m_vNormal[1] + random->RandomFloat( -flAngularSpray, flAngularSpray ); + dir[2] = data.m_vNormal[2] + random->RandomFloat( -flAngularSpray, flAngularSpray ); + + pFleckParticle->m_uchSize = random->RandomInt( 3, 8 ); + + pFleckParticle->m_vecVelocity = dir * random->RandomFloat( flMinSpeed, flMaxSpeed); + + pFleckParticle->m_vAngles = RandomAngle( 0, 360 ); + pFleckParticle->m_flAngSpeed = random->RandomFloat( -800, 800 ); + + unsigned char color = 255; + pFleckParticle->m_uchFrontColor[0] = color; + pFleckParticle->m_uchFrontColor[1] = color; + pFleckParticle->m_uchFrontColor[2] = color; + pFleckParticle->m_uchBackColor[0] = color * 0.25f; + pFleckParticle->m_uchBackColor[1] = color * 0.25f; + pFleckParticle->m_uchBackColor[2] = color * 0.25f; + } +} + +DECLARE_CLIENT_EFFECT( "HunterDamage", HunterDamageCallback ); |