Modified source engine (2017) developed by valve and leaked in 2020. Not for commercial purporses
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.
 
 
 
 
 
 

180 lines
5.4 KiB

//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: A blood spray effect to expose successful hits.
//
//=============================================================================//
#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_blood.h"
#include "view.h"
#include "c_tf_player.h"
#include "debugoverlay_shared.h"
#include "tf_gamerules.h"
#include "c_basetempentity.h"
#include "tier0/vprof.h"
//-----------------------------------------------------------------------------
// Purpose: Intercepts the blood spray message.
//-----------------------------------------------------------------------------
void TFBloodSprayCallback( Vector vecOrigin, Vector vecNormal, ClientEntityHandle_t hEntity )
{
QAngle vecAngles;
VectorAngles( -vecNormal, vecAngles );
// determine if the bleeding player is underwater
bool bUnderwater = false;
C_TFPlayer *pPlayer = dynamic_cast<C_TFPlayer*>( ClientEntityList().GetBaseEntityFromHandle( hEntity ) );
if ( pPlayer && ( WL_Eyes == pPlayer->GetWaterLevel() ) )
{
bUnderwater = true;
}
bool bPyroVision = false;
#ifdef CLIENT_DLL
// Use birthday fun if the local player has an item that allows them to see it (Pyro Goggles)
if ( IsLocalPlayerUsingVisionFilterFlags( TF_VISION_FILTER_PYRO ) )
{
bPyroVision = true;
}
#endif
if ( !bUnderwater && TFGameRules() && TFGameRules()->IsBirthday() && RandomFloat(0,1) < 0.2 )
{
DispatchParticleEffect( "bday_blood", vecOrigin, vecAngles, pPlayer );
}
else if ( TFGameRules() && bPyroVision )
{
DispatchParticleEffect( "pyrovision_blood", vecOrigin, vecAngles, pPlayer );
}
else if ( UTIL_IsLowViolence() )
{
DispatchParticleEffect( bUnderwater ? "lowV_water_blood_impact_red_01" : "lowV_blood_impact_red_01", vecOrigin, vecAngles, pPlayer );
}
else
{
DispatchParticleEffect( bUnderwater ? "water_blood_impact_red_01" : "blood_impact_red_01", vecOrigin, vecAngles, pPlayer );
}
// if underwater, don't add additional spray
if ( bUnderwater )
return;
// Now throw out a spray away from the view
// Get the distance to the view
float flDistance = (vecOrigin - MainViewOrigin()).Length();
float flLODDistance = 0.25 * (flDistance / 512);
Vector right, up;
if (vecNormal != Vector(0, 0, 1) )
{
right = vecNormal.Cross( Vector(0, 0, 1) );
up = right.Cross( vecNormal );
}
else
{
right = Vector(0, 0, 1);
up = right.Cross( vecNormal );
}
// If the normal's too close to being along the view, push it out
Vector vecForward, vecRight;
AngleVectors( MainViewAngles(), &vecForward, &vecRight, NULL );
float flDot = DotProduct( vecNormal, vecForward );
if ( fabs(flDot) > 0.5 )
{
float flPush = random->RandomFloat(0.5, 1.5) + flLODDistance;
float flRightDot = DotProduct( vecNormal, vecRight );
// If we're up close, randomly move it around. If we're at a distance, always push it to the side
// Up close, this can move it back towards the view, but the random chance still looks better
if ( ( flDistance >= 512 && flRightDot > 0 ) || ( flDistance < 512 && RandomFloat(0,1) > 0.5 ) )
{
// Turn it to the right
vecNormal += (vecRight * flPush);
}
else
{
// Turn it to the left
vecNormal -= (vecRight * flPush);
}
}
VectorAngles( vecNormal, vecAngles );
if ( flDistance < 400 )
{
DispatchParticleEffect( UTIL_IsLowViolence() ? "lowV_blood_spray_red_01" : "blood_spray_red_01", vecOrigin, vecAngles, pPlayer );
}
else
{
DispatchParticleEffect( UTIL_IsLowViolence() ? "lowV_blood_spray_red_01_far" : "blood_spray_red_01_far", vecOrigin, vecAngles, pPlayer );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
class C_TETFBlood : public C_BaseTempEntity
{
public:
DECLARE_CLASS( C_TETFBlood, C_BaseTempEntity );
DECLARE_CLIENTCLASS();
C_TETFBlood( void );
virtual void PostDataUpdate( DataUpdateType_t updateType );
public:
Vector m_vecOrigin;
Vector m_vecNormal;
ClientEntityHandle_t m_hEntity;
};
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
C_TETFBlood::C_TETFBlood( void )
{
m_vecOrigin.Init();
m_vecNormal.Init();
m_hEntity = INVALID_EHANDLE_INDEX;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void C_TETFBlood::PostDataUpdate( DataUpdateType_t updateType )
{
VPROF( "C_TETFBlood::PostDataUpdate" );
TFBloodSprayCallback( m_vecOrigin, m_vecNormal, m_hEntity );
}
static void RecvProxy_BloodEntIndex( const CRecvProxyData *pData, void *pStruct, void *pOut )
{
int nEntIndex = pData->m_Value.m_Int;
((C_TETFBlood*)pStruct)->m_hEntity = (nEntIndex < 0) ? INVALID_EHANDLE_INDEX : ClientEntityList().EntIndexToHandle( nEntIndex );
}
IMPLEMENT_CLIENTCLASS_EVENT_DT(C_TETFBlood, DT_TETFBlood, CTETFBlood)
RecvPropFloat( RECVINFO( m_vecOrigin[0] ) ),
RecvPropFloat( RECVINFO( m_vecOrigin[1] ) ),
RecvPropFloat( RECVINFO( m_vecOrigin[2] ) ),
RecvPropVector( RECVINFO(m_vecNormal)),
RecvPropInt( "entindex", 0, SIZEOF_IGNORE, 0, RecvProxy_BloodEntIndex ),
END_RECV_TABLE()