577 lines
14 KiB
C++
577 lines
14 KiB
C++
//========= 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
|
|
} |