Dynamic NPC Footsteps

Similar to Half-Life, Half-Life 2 contained no dynamic footsteps for NPCs; dynamic footsteps meaning that they change when different surfaces are walked on.
=== src\client\c_baseanimating.h ===
==== Includes ====
* vphysics_interface.h
==== Private Prototypes ====
 	virtual void UpdateStepSound( surfacedata_t *psurface, const Vector &vecOrigin, const Vector &vecVelocity, bool left );
 	virtual void PlayStepSound( Vector &vecOrigin, surfacedata_t *psurface, float fvol, bool left );
 	virtual surfacedata_t *GetGroundSurface();
=== src\client\c_baseanimating.cpp ===
==== Includes ====
* decals.h
* soundemittersystem/isoundemittersystembase.h
==== Player Cloned Functions ====
//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
extern ConVar sv_footsteps;
void C_BaseAnimating::UpdateStepSound( surfacedata_t *psurface, const Vector &vecOrigin, const Vector &vecVelocity, bool left )
{
	int fWalking;
	float fvol;
	Vector knee;
	Vector feet;
	float height;
	if ( GetFlags() & (FL_FROZEN|FL_ATCONTROLS))
		return;
	if ( !sv_footsteps.GetFloat() )
		return;
	float groundspeed = vecVelocity.Length2DSqr();
	bool movingalongground = ( groundspeed > 0.0f );
	// To hear step sounds you must be either on a ladder or moving along the ground AND
	// You must be moving fast enough
	if ( !movingalongground )
		return;
	fWalking = groundspeed < RUN_SPEED_ESTIMATE_SQR;		
	VectorCopy( vecOrigin, knee );
	VectorCopy( vecOrigin, feet );
	height = GetCollideable()->OBBMaxs()[ 2 ] - GetCollideable()->OBBMins()[ 2 ];
	knee[2] = vecOrigin[2] + 0.2 * height;
	// find out what we're stepping in or on...
	if ( enginetrace->GetPointContents( knee ) & MASK_WATER )
	{
		static int iSkipStep = 0;
		if ( iSkipStep == 0 )
		{
			iSkipStep++;
			return;
		}
		if ( iSkipStep++ == 3 )
		{
			iSkipStep = 0;
		}
		psurface = physprops->GetSurfaceData( physprops->GetSurfaceIndex( "wade" ) );
		fvol = 0.65;
	}
	else if ( enginetrace->GetPointContents( feet ) & MASK_WATER )
	{
		psurface = physprops->GetSurfaceData( physprops->GetSurfaceIndex( "water" ) );
		fvol = fWalking ? 0.2 : 0.5;
	}
	else
	{
		if ( !psurface )
			return;
		switch ( psurface->game.material )
		{
		default:
		case CHAR_TEX_CONCRETE:						
			fvol = fWalking ? 0.2 : 0.5;
			break;
		case CHAR_TEX_METAL:	
			fvol = fWalking ? 0.2 : 0.5;
			break;
		case CHAR_TEX_DIRT:
			fvol = fWalking ? 0.25 : 0.55;
			break;
		case CHAR_TEX_VENT:	
			fvol = fWalking ? 0.4 : 0.7;
			break;
		case CHAR_TEX_GRATE:
			fvol = fWalking ? 0.2 : 0.5;
			break;
		case CHAR_TEX_TILE:	
			fvol = fWalking ? 0.2 : 0.5;
			break;
		case CHAR_TEX_SLOSH:
			fvol = fWalking ? 0.2 : 0.5;
			break;
		}
	}
	// play the sound
	// 65% volume if ducking
	if ( GetFlags() & FL_DUCKING )
	{
		fvol *= 0.65;
	}
	PlayStepSound( feet, psurface, fvol, left );
}
//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void C_BaseAnimating::PlayStepSound( Vector &vecOrigin, surfacedata_t *psurface, float fvol, bool left )
{
	if ( gpGlobals->maxClients > 1 && !sv_footsteps.GetFloat() )
		return;
	if ( !psurface )
		return;
	unsigned short stepSoundName = left ? psurface->sounds.stepleft : psurface->sounds.stepright;
	if ( !stepSoundName )
		return;
	const char *pSoundName = physprops->GetString( stepSoundName );
	CSoundParameters params;
	if ( !CBaseEntity::GetParametersForSound( pSoundName, params, NULL ) )
		return;
	CPASFilter filter(vecOrigin);
	EmitSound_t ep;
	ep.m_nChannel = CHAN_BODY;
	ep.m_pSoundName = params.soundname;
	ep.m_flVolume = fvol;
	ep.m_SoundLevel = params.soundlevel;
	ep.m_nFlags = 0;
	ep.m_nPitch = params.pitch;
	ep.m_pOrigin = &vecOrigin;
	EmitSound( filter, entindex(), ep );
}
//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
surfacedata_t *C_BaseAnimating::GetGroundSurface()
{
	//
	// Find the name of the material that lies beneath the player.
	//
	Vector start, end;
	VectorCopy( GetAbsOrigin(), start );
	VectorCopy( start, end );
	// Straight down
	end.z -= 64;
	// Fill in default values, just in case.
	
	Ray_t ray;
	ray.Init( start, end, GetCollideable()->OBBMins(), GetCollideable()->OBBMaxs() );
	trace_t	trace;
	UTIL_TraceRay( ray, MASK_NPCSOLID, this, COLLISION_GROUP_NPC, &trace );
	if ( trace.fraction == 1.0f )
		return NULL;	// no ground
	
	return physprops->GetSurfaceData( trace.surface.surfaceProps );
}
==== Left Foot ====
#ifndef HL2MP
			char pSoundName[256];
			if ( !options || !options[0] )
			{
				options = "NPC_CombineS";
			}
			Vector vel;
			EstimateAbsVelocity( vel );
			// If he's moving fast enough, play the run sound
			if ( vel.Length2DSqr() > RUN_SPEED_ESTIMATE_SQR )
			{
				Q_snprintf( pSoundName, 256, "%s.RunFootstepLeft", options );
			}
			else
			{
				Q_snprintf( pSoundName, 256, "%s.FootstepLeft", options );
			}
			EmitSound( pSoundName );
#endif
↓
			if ( IsNPC() )
			{
				Vector vel;
				EstimateAbsVelocity( vel );
				UpdateStepSound( GetGroundSurface(), GetAbsOrigin(), vel, true );
			}
==== Right Foot ====
#ifndef HL2MP
			char pSoundName[256];
			if ( !options || !options[0] )
			{
				options = "NPC_CombineS";
			}
			Vector vel;
			EstimateAbsVelocity( vel );
			// If he's moving fast enough, play the run sound
			if ( vel.Length2DSqr() > RUN_SPEED_ESTIMATE_SQR )
			{
				Q_snprintf( pSoundName, 256, "%s.RunFootstepRight", options );
			}
			else
			{
				Q_snprintf( pSoundName, 256, "%s.FootstepRight", options );
			}
			EmitSound( pSoundName );
#endif
↓
			if ( IsNPC() )
			{
				Vector vel;
				EstimateAbsVelocity( vel );
				UpdateStepSound( GetGroundSurface(), GetAbsOrigin(), vel, false );
			}
Category:AI Programming