Dynamic NPC Footsteps

From Valve Developer Community
Jump to: navigation, search

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.

Note.pngNote:This overrides all NPC footsteps; this means the Combine will not sound like they used to when they walk. An extra addition would need to be taken regarding another EmitSound to still include the original sound.

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 );
			}