mirror of
https://github.com/alliedmodders/hl2sdk.git
synced 2024-12-23 01:59:43 +08:00
321 lines
9.9 KiB
C++
321 lines
9.9 KiB
C++
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
|
|
//
|
|
// Purpose:
|
|
//
|
|
//=============================================================================//
|
|
#include "cbase.h"
|
|
#include "decals.h"
|
|
#include "materialsystem/IMaterialVar.h"
|
|
#include "ieffects.h"
|
|
#include "fx.h"
|
|
#include "fx_impact.h"
|
|
#include "view.h"
|
|
#include "engine/IStaticPropMgr.h"
|
|
|
|
// memdbgon must be the last include file in a .cpp file!!!
|
|
#include "tier0/memdbgon.h"
|
|
|
|
static ConVar r_drawflecks( "r_drawflecks", "1" );
|
|
extern ConVar r_drawmodeldecals;
|
|
|
|
ImpactSoundRouteFn g_pImpactSoundRouteFn = NULL;
|
|
|
|
//==========================================================================================================================
|
|
// RAGDOLL ENUMERATOR
|
|
//==========================================================================================================================
|
|
CRagdollEnumerator::CRagdollEnumerator( Ray_t& shot, int iDamageType )
|
|
{
|
|
m_rayShot = shot;
|
|
m_iDamageType = iDamageType;
|
|
m_bHit = false;
|
|
}
|
|
|
|
IterationRetval_t CRagdollEnumerator::EnumElement( IHandleEntity *pHandleEntity )
|
|
{
|
|
C_BaseEntity *pEnt = ClientEntityList().GetBaseEntityFromHandle( pHandleEntity->GetRefEHandle() );
|
|
if ( pEnt == NULL )
|
|
return ITERATION_CONTINUE;
|
|
|
|
C_BaseAnimating *pModel = static_cast< C_BaseAnimating * >( pEnt );
|
|
|
|
// If the ragdoll was created on this tick, then the forces were already applied on the server
|
|
if ( pModel == NULL || WasRagdollCreatedOnCurrentTick( pEnt ) )
|
|
return ITERATION_CONTINUE;
|
|
|
|
IPhysicsObject *pPhysicsObject = pModel->VPhysicsGetObject();
|
|
if ( pPhysicsObject == NULL )
|
|
return ITERATION_CONTINUE;
|
|
|
|
trace_t tr;
|
|
enginetrace->ClipRayToEntity( m_rayShot, MASK_SHOT, pModel, &tr );
|
|
|
|
if ( tr.fraction < 1.0 )
|
|
{
|
|
pModel->ImpactTrace( &tr, m_iDamageType, NULL );
|
|
m_bHit = true;
|
|
|
|
//FIXME: Yes? No?
|
|
return ITERATION_STOP;
|
|
}
|
|
|
|
return ITERATION_CONTINUE;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
bool FX_AffectRagdolls( Vector vecOrigin, Vector vecStart, int iDamageType )
|
|
{
|
|
// don't do this when lots of ragdolls are simulating
|
|
if ( s_RagdollLRU.CountRagdolls(true) > 1 )
|
|
return false;
|
|
Ray_t shotRay;
|
|
shotRay.Init( vecStart, vecOrigin );
|
|
|
|
CRagdollEnumerator ragdollEnum( shotRay, iDamageType );
|
|
partition->EnumerateElementsAlongRay( PARTITION_CLIENT_RESPONSIVE_EDICTS, shotRay, false, &ragdollEnum );
|
|
|
|
return ragdollEnum.Hit();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : &data -
|
|
//-----------------------------------------------------------------------------
|
|
void RagdollImpactCallback( const CEffectData &data )
|
|
{
|
|
FX_AffectRagdolls( data.m_vOrigin, data.m_vStart, data.m_nDamageType );
|
|
}
|
|
|
|
DECLARE_CLIENT_EFFECT( "RagdollImpact", RagdollImpactCallback );
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
bool Impact( Vector &vecOrigin, Vector &vecStart, int iMaterial, int iDamageType, int iHitbox, C_BaseEntity *pEntity, trace_t &tr, int nFlags, int maxLODToDecal )
|
|
{
|
|
Assert ( pEntity );
|
|
|
|
// Clear out the trace
|
|
memset( &tr, 0, sizeof(trace_t));
|
|
tr.fraction = 1.0f;
|
|
|
|
// Setup our shot information
|
|
Vector shotDir = vecOrigin - vecStart;
|
|
float flLength = VectorNormalize( shotDir );
|
|
Vector traceExt;
|
|
VectorMA( vecStart, flLength + 8.0f, shotDir, traceExt );
|
|
|
|
// Attempt to hit ragdolls
|
|
|
|
bool bHitRagdoll = false;
|
|
|
|
if ( !pEntity->IsClientCreated() )
|
|
{
|
|
bHitRagdoll = FX_AffectRagdolls( vecOrigin, vecStart, iDamageType );
|
|
}
|
|
|
|
if ( (nFlags & IMPACT_NODECAL) == 0 )
|
|
{
|
|
int decalNumber = decalsystem->GetDecalIndexForName( GetImpactDecal( pEntity, iMaterial, iDamageType ) );
|
|
if ( decalNumber == -1 )
|
|
return false;
|
|
|
|
if ( (pEntity->entindex() == 0) && (iHitbox != 0) )
|
|
{
|
|
staticpropmgr->AddDecalToStaticProp( vecStart, traceExt, iHitbox - 1, decalNumber, true, tr );
|
|
}
|
|
else if ( pEntity )
|
|
{
|
|
// Here we deal with decals on entities.
|
|
pEntity->AddDecal( vecStart, traceExt, vecOrigin, iHitbox, decalNumber, true, tr, maxLODToDecal );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Perform the trace ourselves
|
|
Ray_t ray;
|
|
ray.Init( vecStart, traceExt );
|
|
|
|
if ( (pEntity->entindex() == 0) && (iHitbox != 0) )
|
|
{
|
|
// Special case for world entity with hitbox (that's a static prop)
|
|
ICollideable *pCollideable = staticpropmgr->GetStaticPropByIndex( iHitbox - 1 );
|
|
enginetrace->ClipRayToCollideable( ray, MASK_SHOT, pCollideable, &tr );
|
|
}
|
|
else
|
|
{
|
|
if ( !pEntity )
|
|
return false;
|
|
|
|
enginetrace->ClipRayToEntity( ray, MASK_SHOT, pEntity, &tr );
|
|
}
|
|
}
|
|
|
|
// If we found the surface, emit debris flecks
|
|
bool bReportRagdollImpacts = (nFlags & IMPACT_REPORT_RAGDOLL_IMPACTS) != 0;
|
|
if ( ( tr.fraction == 1.0f ) || ( bHitRagdoll && !bReportRagdollImpacts ) )
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
char const *GetImpactDecal( C_BaseEntity *pEntity, int iMaterial, int iDamageType )
|
|
{
|
|
char const *decalName;
|
|
if ( !pEntity )
|
|
{
|
|
decalName = "Impact.Concrete";
|
|
}
|
|
else
|
|
{
|
|
decalName = pEntity->DamageDecal( iDamageType, iMaterial );
|
|
}
|
|
|
|
// See if we need to offset the decal for material type
|
|
return decalsystem->TranslateDecalForGameMaterial( decalName, iMaterial );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Perform custom effects based on the Decal index
|
|
//-----------------------------------------------------------------------------
|
|
void PerformCustomEffects( const Vector &vecOrigin, trace_t &tr, const Vector &shotDir, int iMaterial, int iScale, int nFlags )
|
|
{
|
|
// Throw out the effect if any of these are true
|
|
if ( tr.surface.flags & (SURF_SKY|SURF_NODRAW|SURF_HINT|SURF_SKIP) )
|
|
return;
|
|
|
|
bool bNoFlecks = !r_drawflecks.GetBool();
|
|
if ( !bNoFlecks )
|
|
{
|
|
bNoFlecks = ( ( nFlags & FLAGS_CUSTIOM_EFFECTS_NOFLECKS ) != 0 );
|
|
}
|
|
|
|
// Cement and wood have dust and flecks
|
|
if ( ( iMaterial == CHAR_TEX_CONCRETE ) || ( iMaterial == CHAR_TEX_TILE ) )
|
|
{
|
|
FX_DebrisFlecks( vecOrigin, &tr, iMaterial, iScale, bNoFlecks );
|
|
}
|
|
else if ( iMaterial == CHAR_TEX_WOOD )
|
|
{
|
|
FX_DebrisFlecks( vecOrigin, &tr, iMaterial, iScale, bNoFlecks );
|
|
}
|
|
else if ( ( iMaterial == CHAR_TEX_DIRT ) || ( iMaterial == CHAR_TEX_SAND ) )
|
|
{
|
|
FX_DustImpact( vecOrigin, &tr, iScale );
|
|
}
|
|
else if ( iMaterial == CHAR_TEX_ANTLION )
|
|
{
|
|
FX_AntlionImpact( vecOrigin, &tr );
|
|
}
|
|
else if ( ( iMaterial == CHAR_TEX_METAL ) || ( iMaterial == CHAR_TEX_VENT ) )
|
|
{
|
|
Vector reflect;
|
|
float dot = shotDir.Dot( tr.plane.normal );
|
|
reflect = shotDir + ( tr.plane.normal * ( dot*-2.0f ) );
|
|
|
|
reflect[0] += random->RandomFloat( -0.2f, 0.2f );
|
|
reflect[1] += random->RandomFloat( -0.2f, 0.2f );
|
|
reflect[2] += random->RandomFloat( -0.2f, 0.2f );
|
|
|
|
FX_MetalSpark( vecOrigin, reflect, tr.plane.normal, iScale );
|
|
}
|
|
else if ( iMaterial == CHAR_TEX_COMPUTER )
|
|
{
|
|
Vector offset = vecOrigin + ( tr.plane.normal * 1.0f );
|
|
|
|
g_pEffects->Sparks( offset );
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Play a sound for an impact. If tr contains a valid hit, use that.
|
|
// If not, use the passed in origin & surface.
|
|
//-----------------------------------------------------------------------------
|
|
void PlayImpactSound( CBaseEntity *pEntity, trace_t &tr, Vector &vecServerOrigin, int nServerSurfaceProp )
|
|
{
|
|
surfacedata_t *pdata;
|
|
Vector vecOrigin;
|
|
|
|
// If the client-side trace hit a different entity than the server, or
|
|
// the server didn't specify a surfaceprop, then use the client-side trace
|
|
// material if it's valid.
|
|
if ( tr.DidHit() && (pEntity != tr.m_pEnt || nServerSurfaceProp == 0) )
|
|
{
|
|
nServerSurfaceProp = tr.surface.surfaceProps;
|
|
}
|
|
pdata = physprops->GetSurfaceData( nServerSurfaceProp );
|
|
if ( tr.fraction < 1.0 )
|
|
{
|
|
vecOrigin = tr.endpos;
|
|
}
|
|
else
|
|
{
|
|
vecOrigin = vecServerOrigin;
|
|
}
|
|
|
|
// Now play the esound
|
|
if ( pdata->sounds.bulletImpact )
|
|
{
|
|
const char *pbulletImpactSoundName = physprops->GetString( pdata->sounds.bulletImpact );
|
|
|
|
if ( g_pImpactSoundRouteFn )
|
|
{
|
|
g_pImpactSoundRouteFn( pbulletImpactSoundName, vecOrigin );
|
|
}
|
|
else
|
|
{
|
|
CLocalPlayerFilter filter;
|
|
C_BaseEntity::EmitSound( filter, NULL, pbulletImpactSoundName, pdata->soundhandles.bulletImpact, &vecOrigin );
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
#ifdef _DEBUG
|
|
Msg("***ERROR: PlayImpactSound() on a surface with 0 bulletImpactCount!\n");
|
|
#endif //_DEBUG
|
|
}
|
|
|
|
|
|
void SetImpactSoundRoute( ImpactSoundRouteFn fn )
|
|
{
|
|
g_pImpactSoundRouteFn = fn;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Pull the impact data out
|
|
// Input : &data -
|
|
// *vecOrigin -
|
|
// *vecAngles -
|
|
// *iMaterial -
|
|
// *iDamageType -
|
|
// *iHitbox -
|
|
// *iEntIndex -
|
|
//-----------------------------------------------------------------------------
|
|
C_BaseEntity *ParseImpactData( const CEffectData &data, Vector *vecOrigin, Vector *vecStart,
|
|
Vector *vecShotDir, short &nSurfaceProp, int &iMaterial, int &iDamageType, int &iHitbox )
|
|
{
|
|
C_BaseEntity *pEntity = data.GetEntity( );
|
|
*vecOrigin = data.m_vOrigin;
|
|
*vecStart = data.m_vStart;
|
|
nSurfaceProp = data.m_nSurfaceProp;
|
|
iDamageType = data.m_nDamageType;
|
|
iHitbox = data.m_nHitBox;
|
|
|
|
*vecShotDir = (*vecOrigin - *vecStart);
|
|
VectorNormalize( *vecShotDir );
|
|
|
|
// Get the material from the surfaceprop
|
|
surfacedata_t *psurfaceData = physprops->GetSurfaceData( data.m_nSurfaceProp );
|
|
iMaterial = psurfaceData->game.material;
|
|
|
|
return pEntity;
|
|
}
|
|
|