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.
577 lines
14 KiB
577 lines
14 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: |
|
// |
|
// $NoKeywords: $ |
|
// |
|
//=============================================================================// |
|
#include "cbase.h" |
|
|
|
#ifdef CLIENT_DLL |
|
#include "c_hl2mp_player.h" |
|
#include "prediction.h" |
|
#define CRecipientFilter C_RecipientFilter |
|
#else |
|
#include "hl2mp_player.h" |
|
#endif |
|
|
|
#include "engine/IEngineSound.h" |
|
#include "SoundEmitterSystem/isoundemittersystembase.h" |
|
|
|
extern ConVar sv_footsteps; |
|
|
|
const char *g_ppszPlayerSoundPrefixNames[PLAYER_SOUNDS_MAX] = |
|
{ |
|
"NPC_Citizen", |
|
"NPC_CombineS", |
|
"NPC_MetroPolice", |
|
}; |
|
|
|
const char *CHL2MP_Player::GetPlayerModelSoundPrefix( void ) |
|
{ |
|
return g_ppszPlayerSoundPrefixNames[m_iPlayerSoundType]; |
|
} |
|
|
|
void CHL2MP_Player::PrecacheFootStepSounds( void ) |
|
{ |
|
int iFootstepSounds = ARRAYSIZE( g_ppszPlayerSoundPrefixNames ); |
|
int i; |
|
|
|
for ( i = 0; i < iFootstepSounds; ++i ) |
|
{ |
|
char szFootStepName[128]; |
|
|
|
Q_snprintf( szFootStepName, sizeof( szFootStepName ), "%s.RunFootstepLeft", g_ppszPlayerSoundPrefixNames[i] ); |
|
PrecacheScriptSound( szFootStepName ); |
|
|
|
Q_snprintf( szFootStepName, sizeof( szFootStepName ), "%s.RunFootstepRight", g_ppszPlayerSoundPrefixNames[i] ); |
|
PrecacheScriptSound( szFootStepName ); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Consider the weapon's built-in accuracy, this character's proficiency with |
|
// the weapon, and the status of the target. Use this information to determine |
|
// how accurately to shoot at the target. |
|
//----------------------------------------------------------------------------- |
|
Vector CHL2MP_Player::GetAttackSpread( CBaseCombatWeapon *pWeapon, CBaseEntity *pTarget ) |
|
{ |
|
if ( pWeapon ) |
|
return pWeapon->GetBulletSpread( WEAPON_PROFICIENCY_PERFECT ); |
|
|
|
return VECTOR_CONE_15DEGREES; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : step - |
|
// fvol - |
|
// force - force sound to play |
|
//----------------------------------------------------------------------------- |
|
void CHL2MP_Player::PlayStepSound( Vector &vecOrigin, surfacedata_t *psurface, float fvol, bool force ) |
|
{ |
|
if ( gpGlobals->maxClients > 1 && !sv_footsteps.GetFloat() ) |
|
return; |
|
|
|
#if defined( CLIENT_DLL ) |
|
// during prediction play footstep sounds only once |
|
if ( !prediction->IsFirstTimePredicted() ) |
|
return; |
|
#endif |
|
|
|
if ( GetFlags() & FL_DUCKING ) |
|
return; |
|
|
|
m_Local.m_nStepside = !m_Local.m_nStepside; |
|
|
|
char szStepSound[128]; |
|
|
|
if ( m_Local.m_nStepside ) |
|
{ |
|
Q_snprintf( szStepSound, sizeof( szStepSound ), "%s.RunFootstepLeft", g_ppszPlayerSoundPrefixNames[m_iPlayerSoundType] ); |
|
} |
|
else |
|
{ |
|
Q_snprintf( szStepSound, sizeof( szStepSound ), "%s.RunFootstepRight", g_ppszPlayerSoundPrefixNames[m_iPlayerSoundType] ); |
|
} |
|
|
|
CSoundParameters params; |
|
if ( GetParametersForSound( szStepSound, params, NULL ) == false ) |
|
return; |
|
|
|
CRecipientFilter filter; |
|
filter.AddRecipientsByPAS( vecOrigin ); |
|
|
|
#ifndef CLIENT_DLL |
|
// im MP, server removed all players in origins PVS, these players |
|
// generate the footsteps clientside |
|
if ( gpGlobals->maxClients > 1 ) |
|
filter.RemoveRecipientsByPVS( vecOrigin ); |
|
#endif |
|
|
|
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 ); |
|
} |
|
|
|
|
|
//========================== |
|
// ANIMATION CODE |
|
//========================== |
|
|
|
|
|
// Below this many degrees, slow down turning rate linearly |
|
#define FADE_TURN_DEGREES 45.0f |
|
// After this, need to start turning feet |
|
#define MAX_TORSO_ANGLE 90.0f |
|
// Below this amount, don't play a turning animation/perform IK |
|
#define MIN_TURN_ANGLE_REQUIRING_TURN_ANIMATION 15.0f |
|
|
|
static ConVar tf2_feetyawrunscale( "tf2_feetyawrunscale", "2", FCVAR_REPLICATED, "Multiplier on tf2_feetyawrate to allow turning faster when running." ); |
|
extern ConVar sv_backspeed; |
|
extern ConVar mp_feetyawrate; |
|
extern ConVar mp_facefronttime; |
|
extern ConVar mp_ik; |
|
|
|
CPlayerAnimState::CPlayerAnimState( CHL2MP_Player *outer ) |
|
: m_pOuter( outer ) |
|
{ |
|
m_flGaitYaw = 0.0f; |
|
m_flGoalFeetYaw = 0.0f; |
|
m_flCurrentFeetYaw = 0.0f; |
|
m_flCurrentTorsoYaw = 0.0f; |
|
m_flLastYaw = 0.0f; |
|
m_flLastTurnTime = 0.0f; |
|
m_flTurnCorrectionTime = 0.0f; |
|
}; |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CPlayerAnimState::Update() |
|
{ |
|
m_angRender = GetOuter()->GetLocalAngles(); |
|
m_angRender[ PITCH ] = m_angRender[ ROLL ] = 0.0f; |
|
|
|
ComputePoseParam_BodyYaw(); |
|
ComputePoseParam_BodyPitch(GetOuter()->GetModelPtr()); |
|
ComputePoseParam_BodyLookYaw(); |
|
|
|
ComputePlaybackRate(); |
|
|
|
#ifdef CLIENT_DLL |
|
GetOuter()->UpdateLookAt(); |
|
#endif |
|
|
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CPlayerAnimState::ComputePlaybackRate() |
|
{ |
|
// Determine ideal playback rate |
|
Vector vel; |
|
GetOuterAbsVelocity( vel ); |
|
|
|
float speed = vel.Length2D(); |
|
|
|
bool isMoving = ( speed > 0.5f ) ? true : false; |
|
|
|
float maxspeed = GetOuter()->GetSequenceGroundSpeed( GetOuter()->GetSequence() ); |
|
|
|
if ( isMoving && ( maxspeed > 0.0f ) ) |
|
{ |
|
float flFactor = 1.0f; |
|
|
|
// Note this gets set back to 1.0 if sequence changes due to ResetSequenceInfo below |
|
GetOuter()->SetPlaybackRate( ( speed * flFactor ) / maxspeed ); |
|
|
|
// BUG BUG: |
|
// This stuff really should be m_flPlaybackRate = speed / m_flGroundSpeed |
|
} |
|
else |
|
{ |
|
GetOuter()->SetPlaybackRate( 1.0f ); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Output : CBasePlayer |
|
//----------------------------------------------------------------------------- |
|
CHL2MP_Player *CPlayerAnimState::GetOuter() |
|
{ |
|
return m_pOuter; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : dt - |
|
//----------------------------------------------------------------------------- |
|
void CPlayerAnimState::EstimateYaw( void ) |
|
{ |
|
float dt = gpGlobals->frametime; |
|
|
|
if ( !dt ) |
|
{ |
|
return; |
|
} |
|
|
|
Vector est_velocity; |
|
QAngle angles; |
|
|
|
GetOuterAbsVelocity( est_velocity ); |
|
|
|
angles = GetOuter()->GetLocalAngles(); |
|
|
|
if ( est_velocity[1] == 0 && est_velocity[0] == 0 ) |
|
{ |
|
float flYawDiff = angles[YAW] - m_flGaitYaw; |
|
flYawDiff = flYawDiff - (int)(flYawDiff / 360) * 360; |
|
if (flYawDiff > 180) |
|
flYawDiff -= 360; |
|
if (flYawDiff < -180) |
|
flYawDiff += 360; |
|
|
|
if (dt < 0.25) |
|
flYawDiff *= dt * 4; |
|
else |
|
flYawDiff *= dt; |
|
|
|
m_flGaitYaw += flYawDiff; |
|
m_flGaitYaw = m_flGaitYaw - (int)(m_flGaitYaw / 360) * 360; |
|
} |
|
else |
|
{ |
|
m_flGaitYaw = (atan2(est_velocity[1], est_velocity[0]) * 180 / M_PI); |
|
|
|
if (m_flGaitYaw > 180) |
|
m_flGaitYaw = 180; |
|
else if (m_flGaitYaw < -180) |
|
m_flGaitYaw = -180; |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Override for backpeddling |
|
// Input : dt - |
|
//----------------------------------------------------------------------------- |
|
void CPlayerAnimState::ComputePoseParam_BodyYaw( void ) |
|
{ |
|
int iYaw = GetOuter()->LookupPoseParameter( "move_yaw" ); |
|
if ( iYaw < 0 ) |
|
return; |
|
|
|
// view direction relative to movement |
|
float flYaw; |
|
|
|
EstimateYaw(); |
|
|
|
QAngle angles = GetOuter()->GetLocalAngles(); |
|
float ang = angles[ YAW ]; |
|
if ( ang > 180.0f ) |
|
{ |
|
ang -= 360.0f; |
|
} |
|
else if ( ang < -180.0f ) |
|
{ |
|
ang += 360.0f; |
|
} |
|
|
|
// calc side to side turning |
|
flYaw = ang - m_flGaitYaw; |
|
// Invert for mapping into 8way blend |
|
flYaw = -flYaw; |
|
flYaw = flYaw - (int)(flYaw / 360) * 360; |
|
|
|
if (flYaw < -180) |
|
{ |
|
flYaw = flYaw + 360; |
|
} |
|
else if (flYaw > 180) |
|
{ |
|
flYaw = flYaw - 360; |
|
} |
|
|
|
GetOuter()->SetPoseParameter( iYaw, flYaw ); |
|
|
|
#ifndef CLIENT_DLL |
|
//Adrian: Make the model's angle match the legs so the hitboxes match on both sides. |
|
GetOuter()->SetLocalAngles( QAngle( GetOuter()->GetAnimEyeAngles().x, m_flCurrentFeetYaw, 0 ) ); |
|
#endif |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CPlayerAnimState::ComputePoseParam_BodyPitch( CStudioHdr *pStudioHdr ) |
|
{ |
|
// Get pitch from v_angle |
|
float flPitch = GetOuter()->GetLocalAngles()[ PITCH ]; |
|
|
|
if ( flPitch > 180.0f ) |
|
{ |
|
flPitch -= 360.0f; |
|
} |
|
flPitch = clamp( flPitch, -90, 90 ); |
|
|
|
QAngle absangles = GetOuter()->GetAbsAngles(); |
|
absangles.x = 0.0f; |
|
m_angRender = absangles; |
|
m_angRender[ PITCH ] = m_angRender[ ROLL ] = 0.0f; |
|
|
|
// See if we have a blender for pitch |
|
GetOuter()->SetPoseParameter( pStudioHdr, "aim_pitch", flPitch ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : goal - |
|
// maxrate - |
|
// dt - |
|
// current - |
|
// Output : int |
|
//----------------------------------------------------------------------------- |
|
int CPlayerAnimState::ConvergeAngles( float goal,float maxrate, float dt, float& current ) |
|
{ |
|
int direction = TURN_NONE; |
|
|
|
float anglediff = goal - current; |
|
float anglediffabs = fabs( anglediff ); |
|
|
|
anglediff = AngleNormalize( anglediff ); |
|
|
|
float scale = 1.0f; |
|
if ( anglediffabs <= FADE_TURN_DEGREES ) |
|
{ |
|
scale = anglediffabs / FADE_TURN_DEGREES; |
|
// Always do at least a bit of the turn ( 1% ) |
|
scale = clamp( scale, 0.01f, 1.0f ); |
|
} |
|
|
|
float maxmove = maxrate * dt * scale; |
|
|
|
if ( fabs( anglediff ) < maxmove ) |
|
{ |
|
current = goal; |
|
} |
|
else |
|
{ |
|
if ( anglediff > 0 ) |
|
{ |
|
current += maxmove; |
|
direction = TURN_LEFT; |
|
} |
|
else |
|
{ |
|
current -= maxmove; |
|
direction = TURN_RIGHT; |
|
} |
|
} |
|
|
|
current = AngleNormalize( current ); |
|
|
|
return direction; |
|
} |
|
|
|
void CPlayerAnimState::ComputePoseParam_BodyLookYaw( void ) |
|
{ |
|
QAngle absangles = GetOuter()->GetAbsAngles(); |
|
absangles.y = AngleNormalize( absangles.y ); |
|
m_angRender = absangles; |
|
m_angRender[ PITCH ] = m_angRender[ ROLL ] = 0.0f; |
|
|
|
// See if we even have a blender for pitch |
|
int upper_body_yaw = GetOuter()->LookupPoseParameter( "aim_yaw" ); |
|
if ( upper_body_yaw < 0 ) |
|
{ |
|
return; |
|
} |
|
|
|
// Assume upper and lower bodies are aligned and that we're not turning |
|
float flGoalTorsoYaw = 0.0f; |
|
int turning = TURN_NONE; |
|
float turnrate = 360.0f; |
|
|
|
Vector vel; |
|
|
|
GetOuterAbsVelocity( vel ); |
|
|
|
bool isMoving = ( vel.Length() > 1.0f ) ? true : false; |
|
|
|
if ( !isMoving ) |
|
{ |
|
// Just stopped moving, try and clamp feet |
|
if ( m_flLastTurnTime <= 0.0f ) |
|
{ |
|
m_flLastTurnTime = gpGlobals->curtime; |
|
m_flLastYaw = GetOuter()->GetAnimEyeAngles().y; |
|
// Snap feet to be perfectly aligned with torso/eyes |
|
m_flGoalFeetYaw = GetOuter()->GetAnimEyeAngles().y; |
|
m_flCurrentFeetYaw = m_flGoalFeetYaw; |
|
m_nTurningInPlace = TURN_NONE; |
|
} |
|
|
|
// If rotating in place, update stasis timer |
|
if ( m_flLastYaw != GetOuter()->GetAnimEyeAngles().y ) |
|
{ |
|
m_flLastTurnTime = gpGlobals->curtime; |
|
m_flLastYaw = GetOuter()->GetAnimEyeAngles().y; |
|
} |
|
|
|
if ( m_flGoalFeetYaw != m_flCurrentFeetYaw ) |
|
{ |
|
m_flLastTurnTime = gpGlobals->curtime; |
|
} |
|
|
|
turning = ConvergeAngles( m_flGoalFeetYaw, turnrate, gpGlobals->frametime, m_flCurrentFeetYaw ); |
|
|
|
QAngle eyeAngles = GetOuter()->GetAnimEyeAngles(); |
|
QAngle vAngle = GetOuter()->GetLocalAngles(); |
|
|
|
// See how far off current feetyaw is from true yaw |
|
float yawdelta = GetOuter()->GetAnimEyeAngles().y - m_flCurrentFeetYaw; |
|
yawdelta = AngleNormalize( yawdelta ); |
|
|
|
bool rotated_too_far = false; |
|
|
|
float yawmagnitude = fabs( yawdelta ); |
|
|
|
// If too far, then need to turn in place |
|
if ( yawmagnitude > 45 ) |
|
{ |
|
rotated_too_far = true; |
|
} |
|
|
|
// Standing still for a while, rotate feet around to face forward |
|
// Or rotated too far |
|
// FIXME: Play an in place turning animation |
|
if ( rotated_too_far || |
|
( gpGlobals->curtime > m_flLastTurnTime + mp_facefronttime.GetFloat() ) ) |
|
{ |
|
m_flGoalFeetYaw = GetOuter()->GetAnimEyeAngles().y; |
|
m_flLastTurnTime = gpGlobals->curtime; |
|
|
|
/* float yd = m_flCurrentFeetYaw - m_flGoalFeetYaw; |
|
if ( yd > 0 ) |
|
{ |
|
m_nTurningInPlace = TURN_RIGHT; |
|
} |
|
else if ( yd < 0 ) |
|
{ |
|
m_nTurningInPlace = TURN_LEFT; |
|
} |
|
else |
|
{ |
|
m_nTurningInPlace = TURN_NONE; |
|
} |
|
|
|
turning = ConvergeAngles( m_flGoalFeetYaw, turnrate, gpGlobals->frametime, m_flCurrentFeetYaw ); |
|
yawdelta = GetOuter()->GetAnimEyeAngles().y - m_flCurrentFeetYaw;*/ |
|
|
|
} |
|
|
|
// Snap upper body into position since the delta is already smoothed for the feet |
|
flGoalTorsoYaw = yawdelta; |
|
m_flCurrentTorsoYaw = flGoalTorsoYaw; |
|
} |
|
else |
|
{ |
|
m_flLastTurnTime = 0.0f; |
|
m_nTurningInPlace = TURN_NONE; |
|
m_flCurrentFeetYaw = m_flGoalFeetYaw = GetOuter()->GetAnimEyeAngles().y; |
|
flGoalTorsoYaw = 0.0f; |
|
m_flCurrentTorsoYaw = GetOuter()->GetAnimEyeAngles().y - m_flCurrentFeetYaw; |
|
} |
|
|
|
|
|
if ( turning == TURN_NONE ) |
|
{ |
|
m_nTurningInPlace = turning; |
|
} |
|
|
|
if ( m_nTurningInPlace != TURN_NONE ) |
|
{ |
|
// If we're close to finishing the turn, then turn off the turning animation |
|
if ( fabs( m_flCurrentFeetYaw - m_flGoalFeetYaw ) < MIN_TURN_ANGLE_REQUIRING_TURN_ANIMATION ) |
|
{ |
|
m_nTurningInPlace = TURN_NONE; |
|
} |
|
} |
|
|
|
// Rotate entire body into position |
|
absangles = GetOuter()->GetAbsAngles(); |
|
absangles.y = m_flCurrentFeetYaw; |
|
m_angRender = absangles; |
|
m_angRender[ PITCH ] = m_angRender[ ROLL ] = 0.0f; |
|
|
|
GetOuter()->SetPoseParameter( upper_body_yaw, clamp( m_flCurrentTorsoYaw, -60.0f, 60.0f ) ); |
|
|
|
/* |
|
// FIXME: Adrian, what is this? |
|
int body_yaw = GetOuter()->LookupPoseParameter( "body_yaw" ); |
|
|
|
if ( body_yaw >= 0 ) |
|
{ |
|
GetOuter()->SetPoseParameter( body_yaw, 30 ); |
|
} |
|
*/ |
|
|
|
} |
|
|
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : activity - |
|
// Output : Activity |
|
//----------------------------------------------------------------------------- |
|
Activity CPlayerAnimState::BodyYawTranslateActivity( Activity activity ) |
|
{ |
|
// Not even standing still, sigh |
|
if ( activity != ACT_IDLE ) |
|
return activity; |
|
|
|
// Not turning |
|
switch ( m_nTurningInPlace ) |
|
{ |
|
default: |
|
case TURN_NONE: |
|
return activity; |
|
/* |
|
case TURN_RIGHT: |
|
return ACT_TURNRIGHT45; |
|
case TURN_LEFT: |
|
return ACT_TURNLEFT45; |
|
*/ |
|
case TURN_RIGHT: |
|
case TURN_LEFT: |
|
return mp_ik.GetBool() ? ACT_TURN : activity; |
|
} |
|
|
|
Assert( 0 ); |
|
return activity; |
|
} |
|
|
|
const QAngle& CPlayerAnimState::GetRenderAngles() |
|
{ |
|
return m_angRender; |
|
} |
|
|
|
|
|
void CPlayerAnimState::GetOuterAbsVelocity( Vector& vel ) |
|
{ |
|
#if defined( CLIENT_DLL ) |
|
GetOuter()->EstimateAbsVelocity( vel ); |
|
#else |
|
vel = GetOuter()->GetAbsVelocity(); |
|
#endif |
|
} |