Difference between revisions of "Legs in Firstperson"

From Valve Developer Community
Jump to: navigation, search
m
 
(5 intermediate revisions by 2 users not shown)
Line 5: Line 5:
 
This is a copy and paste tutorial that will allow you to see your legs in first person.
 
This is a copy and paste tutorial that will allow you to see your legs in first person.
  
NOTE: This only works in Single Player 2007 Engine ( OB )
+
{{Note|This only works in Single Player 2007 Engine (OB). It will not work in a multiplayer mod because it replaces the actual third person model to a new model without arms or head.}}
It will not work in a multiplayer mod because in replaces the actual thirdperson model to a new model without arms or head
 
  
NOTE: This currently does not work in Source SDK 2013  
+
{{Note|This currently does not work in Source SDK 2013.}}
  
 
== Requirements ==
 
== Requirements ==
  
*Valve's Source Engine code ( 2007 ) Single Player.
+
*A single player mod running on Source SDK 2007
*A 3D Modeling Application ( XSI, 3DS Max, Blender etc )
+
*A 3D modeling application (XSI, 3ds Max, Blender, etc.)
*First Person Animation Model Pack ( Available [http://www.mediafire.com/?9sgpy5m6pracb7g Here] ) <-- FIXED MISSING TEXTURES
+
*First person animation model pack (available [http://www.mediafire.com/?9sgpy5m6pracb7g here])
*A Brain
+
*Knowledge of C++
  
==Animation state system implementation.==
+
== Animation state system implementation ==
  
You will need to follow the code by Malortie to get this to work properly.  
+
You will need to follow the code by [[User:Malortie|Malortie]] to get this to work properly.  
  
Create these two files in the shared folder of your mod.
+
Create these two files in the '''src/game/shared/''' folder of your mod.
  
The first one here is "singleplayer_animstate.h".
+
=== singleplayer_animstate.h ===
 
<source lang=cpp>
 
<source lang=cpp>
 
 
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
 
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
 
//
 
//
Line 43: Line 41:
  
 
#ifdef CLIENT_DLL
 
#ifdef CLIENT_DLL
#include "c_baseplayer.h"
+
#include "c_baseplayer.h"
 
#else
 
#else
#include "player.h"
+
#include "player.h"
 
#endif
 
#endif
  
Line 51: Line 49:
 
{
 
{
 
public:
 
public:
    enum
+
enum
    {
+
{
        TURN_NONE = 0,
+
TURN_NONE = 0,
        TURN_LEFT,
+
TURN_LEFT,
        TURN_RIGHT
+
TURN_RIGHT
    };
+
};
 +
 
 +
CSinglePlayerAnimState( CBasePlayer *pPlayer );
  
    CSinglePlayerAnimState( CBasePlayer *pPlayer );
+
void Init( CBasePlayer *pPlayer );
  
    void Init( CBasePlayer *pPlayer );
+
Activity BodyYawTranslateActivity( Activity activity );
  
    Activity            BodyYawTranslateActivity( Activity activity );
+
void Update();
  
    void                Update();
+
const QAngle &GetRenderAngles();
  
    const QAngle&        GetRenderAngles();
+
void GetPoseParameters( CStudioHdr *pStudioHdr, float poseParameter[MAXSTUDIOPOSEPARAM] );
             
 
    void               GetPoseParameters( CStudioHdr *pStudioHdr, float poseParameter[MAXSTUDIOPOSEPARAM] );
 
  
    CBasePlayer       *GetBasePlayer();
+
CBasePlayer *GetBasePlayer();
  
    void Release();
+
void Release();
  
 
private:
 
private:
    void               GetOuterAbsVelocity( Vector& vel );
+
void GetOuterAbsVelocity( Vector &vel );
  
    int                   ConvergeAngles( float goal,float maxrate, float dt, float& current );
+
int ConvergeAngles( float goal, float maxrate, float dt, float &current );
  
    void               EstimateYaw( void );
+
void EstimateYaw( void );
    void               ComputePoseParam_BodyYaw( void );
+
void ComputePoseParam_BodyYaw( void );
    void               ComputePoseParam_BodyPitch( CStudioHdr *pStudioHdr );
+
void ComputePoseParam_BodyPitch( CStudioHdr *pStudioHdr );
    void               ComputePoseParam_BodyLookYaw( void );
+
void ComputePoseParam_BodyLookYaw( void );
    void               ComputePoseParam_HeadPitch( CStudioHdr *pStudioHdr );
+
void ComputePoseParam_HeadPitch( CStudioHdr *pStudioHdr );
    void               ComputePlaybackRate();
+
void ComputePlaybackRate();
  
    CBasePlayer       *m_pPlayer;
+
CBasePlayer *m_pPlayer;
  
    float               m_flGaitYaw;
+
float m_flGaitYaw;
    float               m_flStoredCycle;
+
float m_flStoredCycle;
  
    float               m_flGoalFeetYaw;
+
float m_flGoalFeetYaw;
    float               m_flCurrentFeetYaw;
+
float m_flCurrentFeetYaw;
  
    float               m_flCurrentTorsoYaw;
+
float m_flCurrentTorsoYaw;
  
    float               m_flLastYaw;
+
float m_flLastYaw;
    float               m_flLastTurnTime;
+
float m_flLastTurnTime;
  
    int                   m_nTurningInPlace;
+
int m_nTurningInPlace;
  
    QAngle               m_angRender;
+
QAngle m_angRender;
  
    float               m_flTurnCorrectionTime;
+
float m_flTurnCorrectionTime;
 
};
 
};
  
Line 112: Line 110:
  
  
Once you have finished this add the second file "singleplayer_animstate.cpp"
+
=== singleplayer_animstate.cpp ===
 
 
 
 
 
<source lang=cpp>
 
<source lang=cpp>
 
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
 
//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
Line 135: Line 131:
 
extern ConVar mp_facefronttime, mp_feetyawrate, mp_ik;
 
extern ConVar mp_facefronttime, mp_feetyawrate, mp_ik;
  
#define MIN_TURN_ANGLE_REQUIRING_TURN_ANIMATION       15.0f
+
#define MIN_TURN_ANGLE_REQUIRING_TURN_ANIMATION 15.0f
  
 
CSinglePlayerAnimState *CreatePlayerAnimationState( CBasePlayer *pPlayer )
 
CSinglePlayerAnimState *CreatePlayerAnimationState( CBasePlayer *pPlayer )
 
{
 
{
    MDLCACHE_CRITICAL_SECTION();
+
MDLCACHE_CRITICAL_SECTION();
  
    CSinglePlayerAnimState *pState = new CSinglePlayerAnimState( pPlayer );
+
CSinglePlayerAnimState *pState = new CSinglePlayerAnimState( pPlayer );
    pState->Init(pPlayer);
+
pState->Init(pPlayer);
  
    return pState;
+
return pState;
 
}
 
}
  
 
// Below this many degrees, slow down turning rate linearly
 
// Below this many degrees, slow down turning rate linearly
#define FADE_TURN_DEGREES   45.0f
+
#define FADE_TURN_DEGREES 45.0f
 
// After this, need to start turning feet
 
// After this, need to start turning feet
#define MAX_TORSO_ANGLE       90.0f
+
#define MAX_TORSO_ANGLE 90.0f
 
// Below this amount, don't play a turning animation/perform IK
 
// Below this amount, don't play a turning animation/perform IK
#define MIN_TURN_ANGLE_REQUIRING_TURN_ANIMATION       15.0f
+
#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." );
 
//static ConVar tf2_feetyawrunscale( "tf2_feetyawrunscale", "2", FCVAR_REPLICATED, "Multiplier on tf2_feetyawrate to allow turning faster when running." );
Line 160: Line 156:
 
extern ConVar mp_ik;
 
extern ConVar mp_ik;
  
CSinglePlayerAnimState::CSinglePlayerAnimState( CBasePlayer *pPlayer ): m_pPlayer( pPlayer )
+
CSinglePlayerAnimState::CSinglePlayerAnimState( CBasePlayer *pPlayer ) : m_pPlayer( pPlayer )
 
{
 
{
    m_flGaitYaw = 0.0f;
+
m_flGaitYaw = 0.0f;
    m_flGoalFeetYaw = 0.0f;
+
m_flGoalFeetYaw = 0.0f;
    m_flCurrentFeetYaw = 0.0f;
+
m_flCurrentFeetYaw = 0.0f;
    m_flCurrentTorsoYaw = 0.0f;
+
m_flCurrentTorsoYaw = 0.0f;
    m_flLastYaw = 0.0f;
+
m_flLastYaw = 0.0f;
    m_flLastTurnTime = 0.0f;
+
m_flLastTurnTime = 0.0f;
    m_flTurnCorrectionTime = 0.0f;
+
m_flTurnCorrectionTime = 0.0f;
  
    m_pPlayer = NULL;
+
m_pPlayer = NULL;
 
};
 
};
  
 
void CSinglePlayerAnimState::Init( CBasePlayer *pPlayer )
 
void CSinglePlayerAnimState::Init( CBasePlayer *pPlayer )
 
{
 
{
    m_pPlayer = pPlayer;
+
m_pPlayer = pPlayer;
 
}
 
}
  
 
//-----------------------------------------------------------------------------
 
//-----------------------------------------------------------------------------
// Purpose:
+
// Purpose:  
 
//-----------------------------------------------------------------------------
 
//-----------------------------------------------------------------------------
 
void CSinglePlayerAnimState::Update()
 
void CSinglePlayerAnimState::Update()
 
{
 
{
    m_angRender = GetBasePlayer()->GetLocalAngles();
+
m_angRender = GetBasePlayer()->GetLocalAngles();
  
    ComputePoseParam_BodyYaw();
+
ComputePoseParam_BodyYaw();
    ComputePoseParam_BodyPitch(GetBasePlayer()->GetModelPtr());
+
ComputePoseParam_BodyPitch( GetBasePlayer()->GetModelPtr() );
    ComputePoseParam_BodyLookYaw();
+
ComputePoseParam_BodyLookYaw();
    ComputePoseParam_HeadPitch(GetBasePlayer()->GetModelPtr());
+
ComputePoseParam_HeadPitch( GetBasePlayer()->GetModelPtr() );
  
    ComputePlaybackRate();
+
ComputePlaybackRate();
 
}
 
}
  
 
void CSinglePlayerAnimState::Release()
 
void CSinglePlayerAnimState::Release()
 
{
 
{
    delete this;
+
delete this;
 
}
 
}
  
 
//-----------------------------------------------------------------------------
 
//-----------------------------------------------------------------------------
// Purpose:
+
// Purpose:  
 
//-----------------------------------------------------------------------------
 
//-----------------------------------------------------------------------------
 
void CSinglePlayerAnimState::ComputePlaybackRate()
 
void CSinglePlayerAnimState::ComputePlaybackRate()
 
{
 
{
    // Determine ideal playback rate
+
// Determine ideal playback rate
    Vector vel;
+
Vector vel;
    GetOuterAbsVelocity( vel );
+
GetOuterAbsVelocity( vel );
 +
 
 +
float speed = vel.Length2D();
  
    float speed = vel.Length2D();
+
bool isMoving = (speed > 0.5f) ? true : false;
  
    bool isMoving = ( speed > 0.5f ) ? true : false;
+
float maxspeed = GetBasePlayer()->GetSequenceGroundSpeed( GetBasePlayer()->GetSequence() );
  
    float maxspeed = GetBasePlayer()->GetSequenceGroundSpeed( GetBasePlayer()->GetSequence() );
+
if ( isMoving && (maxspeed > 0.0f) )
 
+
{
    if ( isMoving && ( maxspeed > 0.0f ) )
+
float flFactor = 1.0f;
    {
 
        float flFactor = 1.0f;
 
  
        // Note this gets set back to 1.0 if sequence changes due to ResetSequenceInfo below
+
// Note this gets set back to 1.0 if sequence changes due to ResetSequenceInfo below
        GetBasePlayer()->SetPlaybackRate( ( speed * flFactor ) / maxspeed );
+
GetBasePlayer()->SetPlaybackRate( (speed * flFactor) / maxspeed );
  
        // BUG BUG:
+
// BUG BUG:
        // This stuff really should be m_flPlaybackRate = speed / m_flGroundSpeed
+
// This stuff really should be m_flPlaybackRate = speed / m_flGroundSpeed
    }
+
}
    else
+
else
    {
+
{
        GetBasePlayer()->SetPlaybackRate( 1.0f );
+
GetBasePlayer()->SetPlaybackRate( 1.0f );
    }
+
}
 
}
 
}
  
 
//-----------------------------------------------------------------------------
 
//-----------------------------------------------------------------------------
// Purpose:
+
// Purpose:  
 
// Output : CBasePlayer
 
// Output : CBasePlayer
 
//-----------------------------------------------------------------------------
 
//-----------------------------------------------------------------------------
 
CBasePlayer *CSinglePlayerAnimState::GetBasePlayer()
 
CBasePlayer *CSinglePlayerAnimState::GetBasePlayer()
 
{
 
{
    return m_pPlayer;
+
return m_pPlayer;
 
}
 
}
  
 
//-----------------------------------------------------------------------------
 
//-----------------------------------------------------------------------------
// Purpose:
+
// Purpose:  
 
// Input  : dt -
 
// Input  : dt -
 
//-----------------------------------------------------------------------------
 
//-----------------------------------------------------------------------------
 
void CSinglePlayerAnimState::EstimateYaw( void )
 
void CSinglePlayerAnimState::EstimateYaw( void )
 
{
 
{
    float dt = gpGlobals->frametime;
+
float dt = gpGlobals->frametime;
  
    if ( !dt )
+
if ( !dt )
    {
+
return;
        return;
 
    }
 
  
    Vector est_velocity;
+
Vector est_velocity;
    QAngle   angles;
+
QAngle angles;
  
    GetOuterAbsVelocity( est_velocity );
+
GetOuterAbsVelocity( est_velocity );
  
    angles = GetBasePlayer()->GetLocalAngles();
+
angles = GetBasePlayer()->GetLocalAngles();
  
    if ( est_velocity[1] == 0 && est_velocity[0] == 0 )
+
if ( est_velocity[1] == 0 && est_velocity[0] == 0 )
    {
+
{
        float flYawDiff = angles[YAW] - m_flGaitYaw;
+
float flYawDiff = angles[YAW] - m_flGaitYaw;
        flYawDiff = flYawDiff - (int)(flYawDiff / 360) * 360;
+
        if (flYawDiff > 180)
+
flYawDiff = flYawDiff - (int)(flYawDiff / 360) * 360;
            flYawDiff -= 360;
+
if ( flYawDiff > 180 )
        if (flYawDiff < -180)
+
flYawDiff -= 360;
            flYawDiff += 360;
+
if ( flYawDiff < -180 )
 +
flYawDiff += 360;
  
        if (dt < 0.25)
+
if (dt < 0.25)
            flYawDiff *= dt * 4;
+
flYawDiff *= dt * 4;
        else
+
else
            flYawDiff *= dt;
+
flYawDiff *= dt;
  
        m_flGaitYaw += flYawDiff;
+
m_flGaitYaw += flYawDiff;
        m_flGaitYaw = m_flGaitYaw - (int)(m_flGaitYaw / 360) * 360;
+
m_flGaitYaw = m_flGaitYaw - (int)(m_flGaitYaw / 360) * 360;
    }
+
}
    else
+
else
    {
+
{
        m_flGaitYaw = (atan2(est_velocity[1], est_velocity[0]) * 180 / M_PI);
+
m_flGaitYaw = (atan2(est_velocity[1], est_velocity[0]) * 180 / M_PI);
  
        if (m_flGaitYaw > 180)
+
if ( m_flGaitYaw > 180 )
            m_flGaitYaw = 180;
+
m_flGaitYaw = 180;
        else if (m_flGaitYaw < -180)
+
else if ( m_flGaitYaw < -180 )
            m_flGaitYaw = -180;
+
m_flGaitYaw = -180;
    }
+
}
 
}
 
}
  
 
//-----------------------------------------------------------------------------
 
//-----------------------------------------------------------------------------
// Purpose: Override for backpeddling
+
// Purpose: Override for backpedaling
 
// Input  : dt -
 
// Input  : dt -
 
//-----------------------------------------------------------------------------
 
//-----------------------------------------------------------------------------
 
void CSinglePlayerAnimState::ComputePoseParam_BodyYaw( void )
 
void CSinglePlayerAnimState::ComputePoseParam_BodyYaw( void )
 
{
 
{
    int iYaw = GetBasePlayer()->LookupPoseParameter( "move_yaw" );
+
int iYaw = GetBasePlayer()->LookupPoseParameter( "move_yaw" );
    if ( iYaw < 0 )
+
 
        return;
+
if ( iYaw < 0 )
 +
return;
  
    // view direction relative to movement
+
// View direction relative to movement
    float flYaw;    
+
float flYaw;
  
    EstimateYaw();
+
EstimateYaw();
  
    QAngle   angles = GetBasePlayer()->GetLocalAngles();
+
QAngle angles = GetBasePlayer()->GetLocalAngles();
    float ang = angles[ YAW ];
+
float ang = angles[YAW];
    if ( ang > 180.0f )
+
if ( ang > 180.0f )
    {
+
ang -= 360.0f;
        ang -= 360.0f;
+
else if ( ang < -180.0f )
    }
+
ang += 360.0f;
    else if ( ang < -180.0f )
 
    {
 
        ang += 360.0f;
 
    }
 
  
    // calc side to side turning
+
// Calculate side to side turning
    flYaw = ang - m_flGaitYaw;
+
flYaw = ang - m_flGaitYaw;
    // Invert for mapping into 8way blend
 
    flYaw = -flYaw;
 
    flYaw = flYaw - (int)(flYaw / 360) * 360;
 
  
    if (flYaw < -180)
+
// Invert for mapping into 8way blend
    {
+
flYaw = -flYaw;
        flYaw = flYaw + 360;
+
flYaw = flYaw - (int)(flYaw / 360) * 360;
    }
+
 
    else if (flYaw > 180)
+
if ( flYaw < -180 )
    {
+
flYaw = flYaw + 360;
        flYaw = flYaw - 360;
+
else if ( flYaw > 180 )
    }
+
flYaw = flYaw - 360;
 
+
 
    GetBasePlayer()->SetPoseParameter( iYaw, flYaw );
+
GetBasePlayer()->SetPoseParameter( iYaw, flYaw );
  
 
#ifndef CLIENT_DLL
 
#ifndef CLIENT_DLL
        //Adrian: Make the model's angle match the legs so the hitboxes match on both sides.
+
// Adrian: Make the model's angle match the legs so the hitboxes match on both sides.
        GetBasePlayer()->SetLocalAngles( QAngle( GetBasePlayer()->EyeAngles().x, m_flCurrentFeetYaw, 0 ) );
+
GetBasePlayer()->SetLocalAngles( QAngle( GetBasePlayer()->EyeAngles().x, m_flCurrentFeetYaw, 0 ) );
 
#endif
 
#endif
 
}
 
}
  
 
//-----------------------------------------------------------------------------
 
//-----------------------------------------------------------------------------
// Purpose:
+
// Purpose:  
 
//-----------------------------------------------------------------------------
 
//-----------------------------------------------------------------------------
 
void CSinglePlayerAnimState::ComputePoseParam_BodyPitch( CStudioHdr *pStudioHdr )
 
void CSinglePlayerAnimState::ComputePoseParam_BodyPitch( CStudioHdr *pStudioHdr )
 
{
 
{
    // Get pitch from v_angle
+
// Get pitch from v_angle
    float flPitch = GetBasePlayer()->GetLocalAngles()[ PITCH ];
+
float flPitch = GetBasePlayer()->GetLocalAngles()[PITCH];
  
    if ( flPitch > 180.0f )
+
if ( flPitch > 180.0f )
    {
+
{
        flPitch -= 360.0f;
+
flPitch -= 360.0f;
    }
+
}
    flPitch = clamp( flPitch, -90, 90 );
+
flPitch = clamp( flPitch, -90, 90 );
  
    QAngle absangles = GetBasePlayer()->GetAbsAngles();
+
QAngle absangles = GetBasePlayer()->GetAbsAngles();
    absangles.x = 0.0f;
+
absangles.x = 0.0f;
    m_angRender = absangles;
+
m_angRender = absangles;
  
    // See if we have a blender for pitch
+
// See if we have a blender for pitch
    GetBasePlayer()->SetPoseParameter( pStudioHdr, "aim_pitch", flPitch );
+
GetBasePlayer()->SetPoseParameter( pStudioHdr, "aim_pitch", flPitch );
 
}
 
}
  
 
//-----------------------------------------------------------------------------
 
//-----------------------------------------------------------------------------
// Purpose:
+
// Purpose:  
 
// Input  : goal -
 
// Input  : goal -
//           maxrate -
+
//         maxrate -
//           dt -
+
//         dt -
//           current -
+
//         current -
 
// Output : int
 
// Output : int
 
//-----------------------------------------------------------------------------
 
//-----------------------------------------------------------------------------
int CSinglePlayerAnimState::ConvergeAngles( float goal,float maxrate, float dt, float& current )
+
int CSinglePlayerAnimState::ConvergeAngles( float goal, float maxrate, float dt, float &current )
 
{
 
{
    int direction = TURN_NONE;
+
int direction = TURN_NONE;
  
    float anglediff = goal - current;
+
float anglediff = goal - current;
    float anglediffabs = fabs( anglediff );
+
float anglediffabs = fabs( anglediff );
  
    anglediff = AngleNormalize( anglediff );
+
anglediff = AngleNormalize( anglediff );
  
    float scale = 1.0f;
+
float scale = 1.0f;
    if ( anglediffabs <= FADE_TURN_DEGREES )
+
if ( anglediffabs <= FADE_TURN_DEGREES )
    {
+
{
        scale = 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;
+
// Always do at least a bit of the turn ( 1% )
 +
scale = clamp( scale, 0.01f, 1.0f );
 +
}
  
    if ( fabs( anglediff ) < maxmove )
+
float maxmove = maxrate * dt * scale;
    {
 
        current = goal;
 
    }
 
    else
 
    {
 
        if ( anglediff > 0 )
 
        {
 
            current += maxmove;
 
            direction = TURN_LEFT;
 
        }
 
        else
 
        {
 
            current -= maxmove;
 
            direction = TURN_RIGHT;
 
        }
 
    }
 
  
    current = AngleNormalize( current );
+
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;
+
return direction;
 
}
 
}
  
 
void CSinglePlayerAnimState::ComputePoseParam_BodyLookYaw( void )
 
void CSinglePlayerAnimState::ComputePoseParam_BodyLookYaw( void )
 
{
 
{
    QAngle absangles = GetBasePlayer()->GetAbsAngles();
+
QAngle absangles = GetBasePlayer()->GetAbsAngles();
    absangles.y = AngleNormalize( absangles.y );
+
absangles.y = AngleNormalize( absangles.y );
    m_angRender = absangles;
+
m_angRender = absangles;
  
    // See if we even have a blender for pitch
+
// See if we even have a blender for pitch
    int upper_body_yaw = GetBasePlayer()->LookupPoseParameter( "aim_yaw" );
+
int upper_body_yaw = GetBasePlayer()->LookupPoseParameter( "aim_yaw" );
    if ( upper_body_yaw < 0 )
 
    {
 
        return;
 
    }
 
  
    // Assume upper and lower bodies are aligned and that we're not turning
+
if ( upper_body_yaw < 0 )
    float flGoalTorsoYaw = 0.0f;
+
return;
    int turning = TURN_NONE;
 
    float turnrate = 360.0f;
 
  
    Vector vel;
+
// Assume upper and lower bodies are aligned and that we're not turning
 
+
float flGoalTorsoYaw = 0.0f;
    GetOuterAbsVelocity( vel );
+
int turning = TURN_NONE;
 +
float turnrate = 360.0f;
  
    bool isMoving = ( vel.Length() > 1.0f ) ? true : false;
+
Vector vel;
  
    if ( !isMoving )
+
GetOuterAbsVelocity( vel );
    {
 
        // Just stopped moving, try and clamp feet
 
        if ( m_flLastTurnTime <= 0.0f )
 
        {
 
            m_flLastTurnTime    = gpGlobals->curtime;
 
            m_flLastYaw            = GetBasePlayer()->EyeAngles().y;
 
            // Snap feet to be perfectly aligned with torso/eyes
 
            m_flGoalFeetYaw        = GetBasePlayer()->EyeAngles().y;
 
            m_flCurrentFeetYaw    = m_flGoalFeetYaw;
 
            m_nTurningInPlace    = TURN_NONE;
 
        }
 
  
        // If rotating in place, update stasis timer
+
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 = GetBasePlayer()->EyeAngles().y;
  
        if ( m_flLastYaw != GetBasePlayer()->EyeAngles().y )
+
// Snap feet to be perfectly aligned with torso/eyes
        {
+
m_flGoalFeetYaw = GetBasePlayer()->EyeAngles().y;
            m_flLastTurnTime    = gpGlobals->curtime;
+
m_flCurrentFeetYaw = m_flGoalFeetYaw;
            m_flLastYaw            = GetBasePlayer()->EyeAngles().y;
+
m_nTurningInPlace = TURN_NONE;
        }
+
}
  
        if ( m_flGoalFeetYaw != m_flCurrentFeetYaw )
+
// If rotating in place, update stasis timer
        {
+
if ( m_flLastYaw != GetBasePlayer()->EyeAngles().y )
            m_flLastTurnTime   = gpGlobals->curtime;
+
{
        }
+
m_flLastTurnTime = gpGlobals->curtime;
 +
m_flLastYaw = GetBasePlayer()->EyeAngles().y;
 +
}
  
        turning = ConvergeAngles( m_flGoalFeetYaw, turnrate, gpGlobals->frametime, m_flCurrentFeetYaw );
+
if ( m_flGoalFeetYaw != m_flCurrentFeetYaw )
 +
m_flLastTurnTime = gpGlobals->curtime;
  
        QAngle eyeAngles = GetBasePlayer()->EyeAngles();
+
turning = ConvergeAngles( m_flGoalFeetYaw, turnrate, gpGlobals->frametime, m_flCurrentFeetYaw );
        QAngle vAngle = GetBasePlayer()->GetLocalAngles();
 
  
        // See how far off current feetyaw is from true yaw
+
QAngle eyeAngles = GetBasePlayer()->EyeAngles();
        float yawdelta = GetBasePlayer()->EyeAngles().y - m_flCurrentFeetYaw;
+
QAngle vAngle = GetBasePlayer()->GetLocalAngles();
        yawdelta = AngleNormalize( yawdelta );
 
  
        bool rotated_too_far = false;
+
// See how far off current feetyaw is from true yaw
 +
float yawdelta = GetBasePlayer()->EyeAngles().y - m_flCurrentFeetYaw;
 +
yawdelta = AngleNormalize( yawdelta );
  
        float yawmagnitude = fabs( yawdelta );
+
bool rotated_too_far = false;
  
        // If too far, then need to turn in place
+
// If too far, then need to turn in place
        if ( yawmagnitude > 45 )
+
float yawmagnitude = fabs( yawdelta );
        {
+
if ( yawmagnitude > 45 )
            rotated_too_far = true;
+
rotated_too_far = true;
        }
 
  
        // Standing still for a while, rotate feet around to face forward
+
// Standing still for a while, rotate feet around to face forward or rotated too far
        // Or rotated too far
+
// FIXME: Play an in place turning animation
        // FIXME: Play an in place turning animation
+
if ( rotated_too_far || (gpGlobals->curtime > m_flLastTurnTime + mp_facefronttime.GetFloat()) )
        if ( rotated_too_far ||
+
{
            ( gpGlobals->curtime > m_flLastTurnTime + mp_facefronttime.GetFloat() ) )
+
m_flGoalFeetYaw = GetBasePlayer()->EyeAngles().y;
        {
+
m_flLastTurnTime = gpGlobals->curtime;
            m_flGoalFeetYaw       = GetBasePlayer()->EyeAngles().y;
 
            m_flLastTurnTime   = gpGlobals->curtime;
 
  
        /*   float yd = m_flCurrentFeetYaw - m_flGoalFeetYaw;
+
/*
            if ( yd > 0 )
+
float yd = m_flCurrentFeetYaw - m_flGoalFeetYaw;
            {
+
if ( yd > 0 )
                m_nTurningInPlace = TURN_RIGHT;
+
{
            }
+
m_nTurningInPlace = TURN_RIGHT;
            else if ( yd < 0 )
+
}
            {
+
else if ( yd < 0 )
                m_nTurningInPlace = TURN_LEFT;
+
{
            }
+
m_nTurningInPlace = TURN_LEFT;
            else
+
}
            {
+
else
                m_nTurningInPlace = TURN_NONE;
+
{
            }
+
m_nTurningInPlace = TURN_NONE;
 +
}
  
            turning = ConvergeAngles( m_flGoalFeetYaw, turnrate, gpGlobals->frametime, m_flCurrentFeetYaw );
+
turning = ConvergeAngles( m_flGoalFeetYaw, turnrate, gpGlobals->frametime, m_flCurrentFeetYaw );
            yawdelta = GetBasePlayer()->EyeAngles().y - m_flCurrentFeetYaw;*/
+
yawdelta = GetBasePlayer()->EyeAngles().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 = GetBasePlayer()->EyeAngles().y;
 +
flGoalTorsoYaw = 0.0f;
 +
m_flCurrentTorsoYaw = GetBasePlayer()->EyeAngles().y - m_flCurrentFeetYaw;
 +
}
  
        // Snap upper body into position since the delta is already smoothed for the feet
+
if ( turning == TURN_NONE )
        flGoalTorsoYaw = yawdelta;
+
m_nTurningInPlace = turning;
        m_flCurrentTorsoYaw = flGoalTorsoYaw;
 
    }
 
    else
 
    {
 
        m_flLastTurnTime = 0.0f;
 
        m_nTurningInPlace = TURN_NONE;
 
        m_flCurrentFeetYaw = m_flGoalFeetYaw = GetBasePlayer()->EyeAngles().y;
 
        flGoalTorsoYaw = 0.0f;
 
        m_flCurrentTorsoYaw = GetBasePlayer()->EyeAngles().y - m_flCurrentFeetYaw;
 
    }
 
  
    if ( turning == TURN_NONE )
+
if ( m_nTurningInPlace != TURN_NONE )
    {
+
{
        m_nTurningInPlace = turning;
+
// 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;
    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 = GetBasePlayer()->GetAbsAngles();
 
    absangles.y = m_flCurrentFeetYaw;
 
    m_angRender = absangles;
 
 
 
    GetBasePlayer()->SetPoseParameter( upper_body_yaw, clamp( m_flCurrentTorsoYaw, -60.0f, 60.0f ) );
 
  
    /*
+
// Rotate entire body into position
    // FIXME: Adrian, what is this?
+
absangles = GetBasePlayer()->GetAbsAngles();
    int body_yaw = GetBasePlayer()->LookupPoseParameter( "body_yaw" );
+
absangles.y = m_flCurrentFeetYaw;
 +
m_angRender = absangles;
  
    if ( body_yaw >= 0 )
+
GetBasePlayer()->SetPoseParameter( upper_body_yaw, clamp( m_flCurrentTorsoYaw, -60.0f, 60.0f ) );
    {
 
        GetBasePlayer()->SetPoseParameter( body_yaw, 30 );
 
    }
 
    */
 
  
 +
/*
 +
// FIXME: Adrian, what is this?
 +
int body_yaw = GetBasePlayer()->LookupPoseParameter( "body_yaw" );
 +
if ( body_yaw >= 0 )
 +
GetBasePlayer()->SetPoseParameter( body_yaw, 30 );
 +
*/
 
}
 
}
  
 
//-----------------------------------------------------------------------------
 
//-----------------------------------------------------------------------------
// Purpose:
+
// Purpose:  
 
//-----------------------------------------------------------------------------
 
//-----------------------------------------------------------------------------
 
void CSinglePlayerAnimState::ComputePoseParam_HeadPitch( CStudioHdr *pStudioHdr )
 
void CSinglePlayerAnimState::ComputePoseParam_HeadPitch( CStudioHdr *pStudioHdr )
 
{
 
{
    // Get pitch from v_angle
+
// Get pitch from v_angle
    int iHeadPitch = GetBasePlayer()->LookupPoseParameter("head_pitch");
+
int iHeadPitch = GetBasePlayer()->LookupPoseParameter( "head_pitch" );
  
    float flPitch = GetBasePlayer()->EyeAngles()[PITCH];
+
float flPitch = GetBasePlayer()->EyeAngles()[PITCH];
  
    if ( flPitch > 180.0f )
+
if ( flPitch > 180.0f )
    {
+
{
        flPitch -= 360.0f;
+
flPitch -= 360.0f;
    }
+
}
    flPitch = clamp( flPitch, -90, 90 );
+
flPitch = clamp( flPitch, -90, 90 );
  
    QAngle absangles = GetBasePlayer()->GetAbsAngles();
+
QAngle absangles = GetBasePlayer()->GetAbsAngles();
    absangles.x = 0.0f;
+
absangles.x = 0.0f;
    m_angRender = absangles;
+
m_angRender = absangles;
  
    GetBasePlayer()->SetPoseParameter( pStudioHdr, iHeadPitch, flPitch );
+
GetBasePlayer()->SetPoseParameter( pStudioHdr, iHeadPitch, flPitch );
 
}
 
}
  
 
 
//-----------------------------------------------------------------------------
 
//-----------------------------------------------------------------------------
// Purpose:
+
// Purpose:  
 
// Input  : activity -
 
// Input  : activity -
 
// Output : Activity
 
// Output : Activity
Line 581: Line 554:
 
Activity CSinglePlayerAnimState::BodyYawTranslateActivity( Activity activity )
 
Activity CSinglePlayerAnimState::BodyYawTranslateActivity( Activity activity )
 
{
 
{
    // Not even standing still, sigh
+
// Not even standing still, sigh
    if ( activity != ACT_IDLE )
+
if ( activity != ACT_IDLE )
        return activity;
+
return activity;
  
    // Not turning
+
// Not turning
    switch ( m_nTurningInPlace )
+
switch ( m_nTurningInPlace )
    {
+
{
    default:
+
default:
    case TURN_NONE:
+
case TURN_NONE:
        return activity;
+
return activity;
    /*
+
/*
    case TURN_RIGHT:
+
case TURN_RIGHT:
        return ACT_TURNRIGHT45;
+
return ACT_TURNRIGHT45;
    case TURN_LEFT:
+
case TURN_LEFT:
        return ACT_TURNLEFT45;
+
return ACT_TURNLEFT45;
    */
+
*/
    case TURN_RIGHT:
+
case TURN_RIGHT:
    case TURN_LEFT:
+
case TURN_LEFT:
        return mp_ik.GetBool() ? ACT_TURN : activity;
+
return mp_ik.GetBool() ? ACT_TURN : activity;
    }
+
}
  
    Assert( 0 );
+
Assert( 0 );
    return activity;
+
return activity;
 
}
 
}
  
const QAngle& CSinglePlayerAnimState::GetRenderAngles()
+
const QAngle &CSinglePlayerAnimState::GetRenderAngles()
 
{
 
{
    return m_angRender;
+
return m_angRender;
 
}
 
}
  
void CSinglePlayerAnimState::GetOuterAbsVelocity( Vector& vel )
+
void CSinglePlayerAnimState::GetOuterAbsVelocity( Vector &vel )
 
{
 
{
 
#if defined( CLIENT_DLL )
 
#if defined( CLIENT_DLL )
    GetBasePlayer()->EstimateAbsVelocity( vel );
+
GetBasePlayer()->EstimateAbsVelocity( vel );
 
#else
 
#else
    vel = GetBasePlayer()->GetAbsVelocity();
+
vel = GetBasePlayer()->GetAbsVelocity();
 
#endif
 
#endif
 
}
 
}
 
 
</source>
 
</source>
  
 +
== Full implementation ==
  
==Full Implementation==
+
=== c_baseplayer.h ===
  
In "c_baseplayer.cpp" search for the function:
+
Search for <code>bool IsInFreezeCam( void );</code> and under it add:
 +
<source lang=cpp>bool IsInEye( void );</source>
  
<source land=cpp>bool IsInFreezeCam( void )</source>
+
Search for <code>virtual int DrawModel( int flags );</code> and under it add:
 +
<source lang=cpp>const Vector &C_BasePlayer::GetRenderOrigin();</source>
  
and add under it:
+
=== c_baseplayer.cpp ===
  
 +
Search for the function <code>bool IsInFreezeCam( void )</code> and under it add:
 
<source lang=cpp>
 
<source lang=cpp>
 
bool IsInEye( void )
 
bool IsInEye( void )
Line 636: Line 612:
 
C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer();
 
C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer();
 
if ( pPlayer && pPlayer->GetObserverMode() == OBS_MODE_IN_EYE )
 
if ( pPlayer && pPlayer->GetObserverMode() == OBS_MODE_IN_EYE )
return true;
+
return true;
 
}
 
}
 
</source>
 
</source>
  
 
+
Next, search for <code>bool C_BasePlayer::ShouldDraw()</code> and make it look like this:
Next, in the same file, search for  
 
 
 
<source lang=cpp>bool C_BasePlayer::ShouldDraw()</source>
 
 
 
and make it look like this:
 
 
 
 
<source lang=cpp>
 
<source lang=cpp>
 
bool C_BasePlayer::ShouldDraw()
 
bool C_BasePlayer::ShouldDraw()
Line 652: Line 622:
 
return BaseClass::ShouldDraw();
 
return BaseClass::ShouldDraw();
 
}
 
}
 +
</source>
  
static ConVar cl_legs("cl_legs", "1", FCVAR_CHEAT, "Enable or disable player leg rendering", true, 0, true, 1);
+
then and under that add:
static ConVar cl_legs_origin_shift("cl_legs_origin_shift", "-17", FCVAR_CHEAT, "Amount in game units to shift the player model relative to the direction the player is facing");
+
<source lang=cpp>
static ConVar cl_legs_clip_height("cl_legs_clip_height", "0", FCVAR_CHEAT, "Amount in game units of the player model to render up to [0 = disable]", true, 0, false, 0);
+
static ConVar cl_legs( "cl_legs", "1", FCVAR_CHEAT, "Enable or disable player leg rendering", true, 0, true, 1 );
 +
static ConVar cl_legs_origin_shift( "cl_legs_origin_shift", "-17", FCVAR_CHEAT, "Amount in game units to shift the player model relative to the direction the player is facing" );
 +
static ConVar cl_legs_clip_height( "cl_legs_clip_height", "0", FCVAR_CHEAT, "Amount in game units of the player model to render up to [0 = disable]", true, 0, false, 0 );
  
const Vector& C_BasePlayer::GetRenderOrigin( void )
+
const Vector &C_BasePlayer::GetRenderOrigin( void )
 
{
 
{
 
// If we're not observing this player, or if we're not drawing it at the
 
// If we're not observing this player, or if we're not drawing it at the
Line 663: Line 636:
 
// NOTE: the GetCurrentlyDrawingEntity check is here to make sure the
 
// NOTE: the GetCurrentlyDrawingEntity check is here to make sure the
 
// shadow is rendered from the correct origin
 
// shadow is rendered from the correct origin
if(!IsInEye() || view->GetCurrentlyDrawingEntity() != this)
+
if( !IsInEye() || view->GetCurrentlyDrawingEntity() != this )
 
return BaseClass::GetRenderOrigin();
 
return BaseClass::GetRenderOrigin();
+
 
 
// Get the forward vector
 
// Get the forward vector
 
static Vector forward; // static because this method returns a reference
 
static Vector forward; // static because this method returns a reference
 
AngleVectors(GetRenderAngles(), &forward);
 
AngleVectors(GetRenderAngles(), &forward);
+
 
 
// Shift the render origin by a fixed amount
 
// Shift the render origin by a fixed amount
 
forward *= cl_legs_origin_shift.GetFloat();
 
forward *= cl_legs_origin_shift.GetFloat();
 
forward += GetAbsOrigin();
 
forward += GetAbsOrigin();
+
 
 
return forward;
 
return forward;
 
}
 
}
 
</source>
 
</source>
  
 +
Now, in the <code>int C_BasePlayer::DrawModel( int flags )</code> function add:
 +
<source lang=cpp>
 +
CMatRenderContextPtr context( materials );
  
Now, in the function <source lang=cpp>int C_BasePlayer::DrawModel( int flags )</source>
+
if ( cl_legs_clip_height.GetInt() > 0 )
of the same file add: <source lang=cpp>
+
{
CMatRenderContextPtr context(materials);
+
context->SetHeightClipMode( MATERIAL_HEIGHTCLIPMODE_RENDER_BELOW_HEIGHT );
 
+
context->SetHeightClipZ( GetAbsOrigin().z + cl_legs_clip_height.GetFloat() );
        if(cl_legs_clip_height.GetInt() > 0)
+
}
        {
 
                context->SetHeightClipMode(MATERIAL_HEIGHTCLIPMODE_RENDER_BELOW_HEIGHT);
 
                context->SetHeightClipZ(GetAbsOrigin().z+cl_legs_clip_height.GetFloat());
 
        }
 
 
</source>
 
</source>
  
 
+
Now, search for the <code>void C_BasePlayer::AddEntity( void )</code> function and under
Now, search for the function <source lang=cpp>void C_BasePlayer::AddEntity( void )</source>
+
<source lang=cpp>
 
+
// Add in lighting effects
and under <source lang=cpp>
+
CreateLightEffects();
// Add in lighting effects
 
CreateLightEffects();
 
 
</source>
 
</source>
  
add  
+
add:
 
<source lang=cpp>
 
<source lang=cpp>
SetLocalAnglesDim(X_INDEX, 0 );
+
SetLocalAnglesDim( X_INDEX, 0 );
 
</source>
 
</source>
  
 +
=== hl2_player.h ===
 +
Add <code>#include "singleplayer_animstate.h"</code> to the top of the file.
  
Now, in "c_baseplayer.h", search for
 
  
<source lang=cpp>bool IsInFreezeCam( void );</source>
+
Now, go to the end of the <code>public:</code> section in the <code>class CHL2_Player</code> class and add
 +
<source lang=cpp>void SetAnimation( PLAYER_ANIM playerAnim );</source>
  
and add under it:
 
  
<source lang=cpp>bool IsInEye( void );</source>
+
Now, go right to the end of the class, in the <code>private:</code> section and add:
 +
<source lang=cpp>
 +
CSinglePlayerAnimState *m_pPlayerAnimState;
 +
QAngle m_angEyeAngles;
 +
</source>
  
 +
=== hl2_player.cpp ===
 +
Add <source lang=cpp>#define PLAYER_MODEL "models/player.mdl"</source>
 +
with all the other <code>#define</code>'s
  
In the same file, search for
 
 
<source lang=cpp>virtual int DrawModel( int flags );</source>
 
  
 +
Now, search for <source lang=cpp>void CHL2_Player::Precache( void )</source>
 
and add:
 
and add:
 
+
<source lang=cpp>PrecacheModel( PLAYER_MODEL );</source>
<source lang=cpp>const Vector& C_BasePlayer::GetRenderOrigin();</source>
 
 
 
 
 
Next, open up "hl2_player.cpp" and add <source lang=cpp>#define PLAYER_MODEL "models/player.mdl"</source>
 
with all the other #define's
 
 
 
 
 
Now, search for <source lang=cpp>void CHL2_Player::Precache( void )</source>
 
and add
 
<source lang=cpp>PrecacheModel(PLAYER_MODEL);</source>
 
  
  
Line 736: Line 702:
 
SetModel( "models/player.mdl" );
 
SetModel( "models/player.mdl" );
 
</source>
 
</source>
and set it to
+
and set it to:
 
<source lang=cpp>
 
<source lang=cpp>
 
SetModel( PLAYER_MODEL );
 
SetModel( PLAYER_MODEL );
Line 742: Line 708:
  
  
Now, search for  
+
Now, search for <code>CHL2_Player::~CHL2_Player( void )</code> and make it look like this:
<source lang=cpp>CHL2_Player::~CHL2_Player( void )</source>
+
<source lang=cpp highlight=3-8>
and make it look like
 
<source lang=cpp>
 
 
CHL2_Player::~CHL2_Player( void )
 
CHL2_Player::~CHL2_Player( void )
 
{
 
{
// FPLEGS - Destructor defualt empty! Added code below!
 
 
// Clears the animation state.
 
// Clears the animation state.
 
if ( m_pPlayerAnimState != NULL )
 
if ( m_pPlayerAnimState != NULL )
Line 758: Line 721:
 
</source>
 
</source>
  
 
+
Now, search for <code>CHL2_Player::CHL2_Player()</code> and make it look like:
Now, search for  
+
<source lang=cpp highlight=3-4>
<source lang=cpp>CHL2_Player::CHL2_Player()</source>
 
and make it look like:
 
<source lang=cpp>
 
 
CHL2_Player::CHL2_Player()
 
CHL2_Player::CHL2_Player()
 
{
 
{
// FPLEGS
+
m_pPlayerAnimState = CreatePlayerAnimationState( this );
m_pPlayerAnimState = CreatePlayerAnimationState(this);
 
 
m_angEyeAngles.Init();
 
m_angEyeAngles.Init();
// FPLEGS END
 
  
m_nNumMissPositions = 0;
+
m_nNumMissPositions = 0;
 
m_pPlayerAISquad = 0;
 
m_pPlayerAISquad = 0;
 
m_bSprintEnabled = true;
 
m_bSprintEnabled = true;
Line 779: Line 737:
 
</source>
 
</source>
  
 
+
Now, head up to <code>void CHL2_Player::PostThink( void )</code> and make it look like this:
Now, head up to <source lang=cpp>void CHL2_Player::PostThink( void )</source>
+
<source lang=cpp highlight=10-14>
and make it look like:
 
<source lang=cpp>
 
 
void CHL2_Player::PostThink( void )
 
void CHL2_Player::PostThink( void )
 
{
 
{
Line 789: Line 745:
 
if ( !g_fGameOver && !IsPlayerLockedInPlace() && IsAlive() )
 
if ( !g_fGameOver && !IsPlayerLockedInPlace() && IsAlive() )
 
{
 
{
HandleAdmireGlovesAnimation();
+
HandleAdmireGlovesAnimation();
 
}
 
}
  
// FPLEGS
 
 
m_angEyeAngles = EyeAngles();
 
m_angEyeAngles = EyeAngles();
 
QAngle angles = GetLocalAngles();
 
QAngle angles = GetLocalAngles();
Line 800: Line 755:
 
}
 
}
 
</source>
 
</source>
 
  
 
Now, add this to the end of the file:
 
Now, add this to the end of the file:
Line 806: Line 760:
 
void CHL2_Player::SetAnimation( PLAYER_ANIM playerAnim )
 
void CHL2_Player::SetAnimation( PLAYER_ANIM playerAnim )
 
{
 
{
    int animDesired;
+
int animDesired;
+
float speed = GetAbsVelocity().Length2D();
    float speed;
+
 
+
if ( GetFlags() & (FL_FROZEN | FL_ATCONTROLS) )
    speed = GetAbsVelocity().Length2D();
+
{
+
speed = 0;
    if ( GetFlags() & ( FL_FROZEN | FL_ATCONTROLS ) )
+
playerAnim = PLAYER_IDLE;
    {
+
}
        speed = 0;
+
 
        playerAnim = PLAYER_IDLE;
+
Activity idealActivity = ACT_HL2MP_RUN;
    }
+
 
+
if ( playerAnim == PLAYER_JUMP )
    Activity idealActivity = ACT_HL2MP_RUN;
+
{
+
if ( HasWeapons() )
    if ( playerAnim == PLAYER_JUMP )
+
idealActivity = ACT_HL2MP_JUMP;
    {
+
else
        if ( HasWeapons() )
+
idealActivity = ACT_JUMP;
            idealActivity = ACT_HL2MP_JUMP;
+
}
        else
+
else if ( playerAnim == PLAYER_DIE )
            idealActivity = ACT_JUMP;
+
{
    }
+
if ( m_lifeState == LIFE_ALIVE )
    else if ( playerAnim == PLAYER_DIE )
+
return;
    {
+
}
        if ( m_lifeState == LIFE_ALIVE )
+
else if ( playerAnim == PLAYER_ATTACK1 )
        {
+
{
            return;
+
if ( GetActivity() == ACT_HOVER     ||  
        }
+
GetActivity() == ACT_SWIM        ||  
    }
+
GetActivity() == ACT_HOP         ||  
    else if ( playerAnim == PLAYER_ATTACK1 )
+
GetActivity() == ACT_LEAP        ||  
    {
+
GetActivity() == ACT_DIESIMPLE )
        if ( GetActivity( ) == ACT_HOVER   ||
+
{
            GetActivity( ) == ACT_SWIM        ||
+
idealActivity = GetActivity();
            GetActivity( ) == ACT_HOP       ||
+
}
            GetActivity( ) == ACT_LEAP        ||
+
else
            GetActivity( ) == ACT_DIESIMPLE )
+
{
        {
+
idealActivity = ACT_HL2MP_GESTURE_RANGE_ATTACK;
            idealActivity = GetActivity( );
+
}
        }
+
}
        else
+
else if ( playerAnim == PLAYER_RELOAD )
        {
+
{
            idealActivity = ACT_HL2MP_GESTURE_RANGE_ATTACK;
+
idealActivity = ACT_HL2MP_GESTURE_RELOAD;
        }
+
}
    }
+
else if ( playerAnim == PLAYER_IDLE || playerAnim == PLAYER_WALK )
    else if ( playerAnim == PLAYER_RELOAD )
+
{
    {
+
if ( !(GetFlags() & FL_ONGROUND) && (GetActivity() == ACT_HL2MP_JUMP || GetActivity() == ACT_JUMP) ) // Still jumping
        idealActivity = ACT_HL2MP_GESTURE_RELOAD;
+
{
    }
+
idealActivity = GetActivity( );
    else if ( playerAnim == PLAYER_IDLE || playerAnim == PLAYER_WALK )
+
}
    {
+
else if ( GetWaterLevel() > 1 )
        if ( !( GetFlags() & FL_ONGROUND ) && ( GetActivity( ) == ACT_HL2MP_JUMP || GetActivity( ) == ACT_JUMP ) )   // Still jumping
+
{
        {
+
if ( speed == 0 )
            idealActivity = GetActivity( );
+
{
        }  
+
if ( HasWeapons() )
        else if ( GetWaterLevel() > 1 )
+
idealActivity = ACT_HL2MP_IDLE;
        {
+
else
            if ( speed == 0 )
+
idealActivity = ACT_IDLE;
            {
+
}
                if ( HasWeapons() )
+
else
                    idealActivity = ACT_HL2MP_IDLE;
+
{
                else
+
if ( HasWeapons() )
                    idealActivity = ACT_IDLE;
+
idealActivity = ACT_HL2MP_RUN;
            }
+
else
            else
+
idealActivity = ACT_RUN;
            {
+
}
                if ( HasWeapons() )
+
}
                    idealActivity = ACT_HL2MP_RUN;
+
else
                else
+
{
                    idealActivity = ACT_RUN;
+
if ( GetFlags() & FL_DUCKING )
            }
+
{
        }
+
if ( speed > 0 )
        else
+
{
        {
+
if ( HasWeapons() )
            if ( GetFlags() & FL_DUCKING )
+
idealActivity = ACT_HL2MP_WALK_CROUCH;
            {
+
else
                if ( speed > 0 )
+
idealActivity = ACT_WALK_CROUCH;
                {
+
}
                    if ( HasWeapons() )
+
else
                        idealActivity = ACT_HL2MP_WALK_CROUCH;
+
{
                    else
+
if ( HasWeapons() )
                        idealActivity = ACT_WALK_CROUCH;
+
idealActivity = ACT_HL2MP_IDLE_CROUCH;
                }
+
else
                else
+
idealActivity = ACT_COVER_LOW;
                {
+
}
                    if ( HasWeapons() )
+
}
                        idealActivity = ACT_HL2MP_IDLE_CROUCH;
+
else
                    else
+
{
                        idealActivity = ACT_COVER_LOW;
+
if ( speed > 0 )
                }
+
{
            }
+
{
            else
+
if ( HasWeapons() )
            {
+
idealActivity = ACT_HL2MP_RUN;
                if ( speed > 0 )
+
else
                {
+
{
                    {
+
if ( speed > HL2_WALK_SPEED + 20.0f )
                        if ( HasWeapons() )
+
idealActivity = ACT_RUN;
                            idealActivity = ACT_HL2MP_RUN;
+
else
                        else
+
idealActivity = ACT_WALK;
                        {
+
}
                            if ( speed > HL2_WALK_SPEED + 20.0f )
+
}
                                idealActivity = ACT_RUN;
+
}
                            else
+
else
                                idealActivity = ACT_WALK;
+
{
                        }
+
if ( HasWeapons() )
                    }
+
idealActivity = ACT_HL2MP_IDLE;
                }
+
else
                else
+
idealActivity = ACT_IDLE;
                {
+
}
                    if ( HasWeapons() )
+
}
                    idealActivity = ACT_HL2MP_IDLE;
+
}
                    else
+
 
                    idealActivity = ACT_IDLE;
+
//idealActivity = TranslateTeamActivity( idealActivity );
                }
+
}
            }
+
 
        }
+
if ( IsInAVehicle() )
+
idealActivity = ACT_COVER_LOW;
        //idealActivity = TranslateTeamActivity( idealActivity );
+
 
    }
+
if ( idealActivity == ACT_HL2MP_GESTURE_RANGE_ATTACK )
+
{
    if ( IsInAVehicle() )
+
RestartGesture( Weapon_TranslateActivity( idealActivity ) );
    {
+
 
        idealActivity = ACT_COVER_LOW;
+
// FIXME: this seems a bit whacked
    }
+
Weapon_SetActivity( Weapon_TranslateActivity( ACT_RANGE_ATTACK1 ), 0 );
+
 
    if ( idealActivity == ACT_HL2MP_GESTURE_RANGE_ATTACK )
+
return;
    {
+
}
        RestartGesture( Weapon_TranslateActivity( idealActivity ) );
+
else if ( idealActivity == ACT_HL2MP_GESTURE_RELOAD )
+
{
        // FIXME: this seems a bit wacked
+
RestartGesture( Weapon_TranslateActivity( idealActivity ) );
        Weapon_SetActivity( Weapon_TranslateActivity( ACT_RANGE_ATTACK1 ), 0 );
+
return;
+
}
        return;
+
else
    }
+
{
    else if ( idealActivity == ACT_HL2MP_GESTURE_RELOAD )
+
SetActivity( idealActivity );
    {
+
 
        RestartGesture( Weapon_TranslateActivity( idealActivity ) );
+
animDesired = SelectWeightedSequence( Weapon_TranslateActivity( idealActivity ) );
        return;
+
 
    }
+
if ( animDesired == -1 )
    else
+
{
    {
+
animDesired = SelectWeightedSequence( idealActivity );
        SetActivity( idealActivity );
 
 
        animDesired = SelectWeightedSequence( Weapon_TranslateActivity ( idealActivity ) );
 
 
        if (animDesired == -1)
 
        {
 
            animDesired = SelectWeightedSequence( idealActivity );
 
 
            if ( animDesired == -1 )
 
            {
 
                animDesired = 0;
 
            }
 
        }
 
 
        // Already using the desired animation?
 
        if ( GetSequence() == animDesired )
 
            return;
 
 
        m_flPlaybackRate = 1.0;
 
        ResetSequence( animDesired );
 
        SetCycle( 0 );
 
        return;
 
    }
 
 
    // Already using the desired animation?
 
    if ( GetSequence() == animDesired )
 
        return;
 
 
    //Msg( "Set animation to %d\n", animDesired );
 
    // Reset to first frame of desired animation
 
    ResetSequence( animDesired );
 
    SetCycle( 0 );
 
}
 
</source>
 
  
 +
if ( animDesired == -1 )
 +
animDesired = 0;
 +
}
  
Next, go to "hl2_player.h" and add <source lang=cpp>#include "singleplayer_animstate.h"</source>
+
// Already using the desired animation?
to the top of the file.
+
if ( GetSequence() == animDesired )
 +
return;
  
 +
m_flPlaybackRate = 1.0f;
 +
ResetSequence( animDesired );
 +
SetCycle( 0 );
  
Now, go to the end of "public:" in the class <source lang=cpp>class CHL2_Player : public </source>
+
return;
and add <source lang=cpp>void SetAnimation( PLAYER_ANIM playerAnim );</source>
+
}
  
 +
// Already using the desired animation?
 +
if ( GetSequence() == animDesired )
 +
return;
  
Now, go right to the end of the class, in the "private:" section and add
+
//Msg( "Set animation to %d\n", animDesired );
<source lang=cpp>
+
// Reset to first frame of desired animation
CSinglePlayerAnimState *m_pPlayerAnimState;
+
ResetSequence( animDesired );
QAngle m_angEyeAngles;
+
SetCycle( 0 );
 +
}
 
</source>
 
</source>
  
==Weapon activity list tables==
+
== Weapon activity list tables ==
  
In weapon_357.cpp:
+
=== weapon_357.cpp ===
  
In the class description, under the macro "DECLARE_DATADESC()", add this line:
+
In the class description, under the macro <code>DECLARE_DATADESC()</code>, add this line:
 
<source lang=cpp>DECLARE_ACTTABLE();</source>
 
<source lang=cpp>DECLARE_ACTTABLE();</source>
  
Line 1,006: Line 937:
 
acttable_t CWeapon357::m_acttable[] =
 
acttable_t CWeapon357::m_acttable[] =
 
{
 
{
    { ACT_HL2MP_IDLE,                   ACT_HL2MP_IDLE_PISTOL,                   false },
+
{ ACT_HL2MP_IDLE, ACT_HL2MP_IDLE_PISTOL, false },
    { ACT_HL2MP_RUN,                   ACT_HL2MP_RUN_PISTOL,                   false },
+
{ ACT_HL2MP_RUN, ACT_HL2MP_RUN_PISTOL, false },
    { ACT_HL2MP_IDLE_CROUCH,           ACT_HL2MP_IDLE_CROUCH_PISTOL,           false },
+
{ ACT_HL2MP_IDLE_CROUCH, ACT_HL2MP_IDLE_CROUCH_PISTOL, false },
    { ACT_HL2MP_WALK_CROUCH,           ACT_HL2MP_WALK_CROUCH_PISTOL,           false },
+
{ ACT_HL2MP_WALK_CROUCH, ACT_HL2MP_WALK_CROUCH_PISTOL, false },
    { ACT_HL2MP_GESTURE_RANGE_ATTACK,   ACT_HL2MP_GESTURE_RANGE_ATTACK_PISTOL,   false },
+
{ ACT_HL2MP_GESTURE_RANGE_ATTACK, ACT_HL2MP_GESTURE_RANGE_ATTACK_PISTOL, false },
    { ACT_HL2MP_GESTURE_RELOAD,           ACT_HL2MP_GESTURE_RELOAD_PISTOL,       false },
+
{ ACT_HL2MP_GESTURE_RELOAD, ACT_HL2MP_GESTURE_RELOAD_PISTOL, false },
    { ACT_HL2MP_JUMP,                   ACT_HL2MP_JUMP_PISTOL,                   false },
+
{ ACT_HL2MP_JUMP, ACT_HL2MP_JUMP_PISTOL, false },
    { ACT_RANGE_ATTACK1,               ACT_RANGE_ATTACK_PISTOL,               false },
+
{ ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_PISTOL, false },
 
};
 
};
  
Line 1,019: Line 950:
 
</source>
 
</source>
  
 +
=== weapon_ar2.cpp ===
  
In weapon_ar2.cpp:
+
There is already an activity list. At it's foot, after <code>//{ ACT_RANGE_ATTACK2, ACT_RANGE_ATTACK_AR2_GRENADE, true },</code> add the following list:
 
 
There is already an activity list. At it's foot, after //   { ACT_RANGE_ATTACK2, ACT_RANGE_ATTACK_AR2_GRENADE, true },
 
 
 
add the following list:
 
 
<source lang=cpp>
 
<source lang=cpp>
{ ACT_HL2MP_IDLE,                   ACT_HL2MP_IDLE_AR2,                   false },
+
{ ACT_HL2MP_IDLE, ACT_HL2MP_IDLE_AR2, false },
    { ACT_HL2MP_RUN,                   ACT_HL2MP_RUN_AR2,                   false },
+
{ ACT_HL2MP_RUN, ACT_HL2MP_RUN_AR2, false },
    { ACT_HL2MP_IDLE_CROUCH,           ACT_HL2MP_IDLE_CROUCH_AR2,           false },
+
{ ACT_HL2MP_IDLE_CROUCH, ACT_HL2MP_IDLE_CROUCH_AR2, false },
    { ACT_HL2MP_WALK_CROUCH,           ACT_HL2MP_WALK_CROUCH_AR2,           false },
+
{ ACT_HL2MP_WALK_CROUCH, ACT_HL2MP_WALK_CROUCH_AR2, false },
    { ACT_HL2MP_GESTURE_RANGE_ATTACK,   ACT_HL2MP_GESTURE_RANGE_ATTACK_AR2,   false },
+
{ ACT_HL2MP_GESTURE_RANGE_ATTACK, ACT_HL2MP_GESTURE_RANGE_ATTACK_AR2, false },
    { ACT_HL2MP_GESTURE_RELOAD,           ACT_GESTURE_RELOAD_SMG1,       false },
+
{ ACT_HL2MP_GESTURE_RELOAD, ACT_GESTURE_RELOAD_SMG1, false },
    { ACT_HL2MP_JUMP,                   ACT_HL2MP_JUMP_AR2,                   false },
+
{ ACT_HL2MP_JUMP, ACT_HL2MP_JUMP_AR2, false },
    { ACT_RANGE_ATTACK1,               ACT_RANGE_ATTACK_AR2,               false },
+
{ ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_AR2, false },
 
</source>
 
</source>
  
In weapon_crossbow.cpp:
+
=== weapon_crossbow.cpp ===
  
In the class description, around line 450, under the macro "DECLARE_DATADESC()", add this line:
+
In the class description, around line 450, under the macro <code>DECLARE_DATADESC()</code>, add this line:
 
<source lang=cpp>DECLARE_ACTTABLE();</source>
 
<source lang=cpp>DECLARE_ACTTABLE();</source>
  
Line 1,045: Line 973:
 
acttable_t CWeaponCrossbow::m_acttable[] =
 
acttable_t CWeaponCrossbow::m_acttable[] =
 
{
 
{
    { ACT_HL2MP_IDLE,                   ACT_HL2MP_IDLE_CROSSBOW,                   false },
+
{ ACT_HL2MP_IDLE, ACT_HL2MP_IDLE_CROSSBOW, false },
    { ACT_HL2MP_RUN,                   ACT_HL2MP_RUN_CROSSBOW,                   false },
+
{ ACT_HL2MP_RUN, ACT_HL2MP_RUN_CROSSBOW, false },
    { ACT_HL2MP_IDLE_CROUCH,           ACT_HL2MP_IDLE_CROUCH_CROSSBOW,           false },
+
{ ACT_HL2MP_IDLE_CROUCH, ACT_HL2MP_IDLE_CROUCH_CROSSBOW, false },
    { ACT_HL2MP_WALK_CROUCH,           ACT_HL2MP_WALK_CROUCH_CROSSBOW,           false },
+
{ ACT_HL2MP_WALK_CROUCH, ACT_HL2MP_WALK_CROUCH_CROSSBOW, false },
    { ACT_HL2MP_GESTURE_RANGE_ATTACK,   ACT_HL2MP_GESTURE_RANGE_ATTACK_CROSSBOW,   false },
+
{ ACT_HL2MP_GESTURE_RANGE_ATTACK, ACT_HL2MP_GESTURE_RANGE_ATTACK_CROSSBOW, false },
    { ACT_HL2MP_GESTURE_RELOAD,           ACT_HL2MP_GESTURE_RELOAD_CROSSBOW,       false },
+
{ ACT_HL2MP_GESTURE_RELOAD, ACT_HL2MP_GESTURE_RELOAD_CROSSBOW, false },
    { ACT_HL2MP_JUMP,                   ACT_HL2MP_JUMP_CROSSBOW,                   false },
+
{ ACT_HL2MP_JUMP, ACT_HL2MP_JUMP_CROSSBOW, false },
    { ACT_RANGE_ATTACK1,               ACT_RANGE_ATTACK_SHOTGUN,               false },
+
{ ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_SHOTGUN, false },
 
};
 
};
  
Line 1,058: Line 986:
 
</source>
 
</source>
  
 +
=== weapon_crowbar.cpp ===
  
In weapon_crowbar.cpp:
+
There is already an activity list. At it's foot, after <code>{ ACT_IDLE_ANGRY, ACT_IDLE_ANGRY_MELEE, false },</code> add the following list:
 
 
There is already an activity list. At it's foot, after
 
{ ACT_IDLE_ANGRY,       ACT_IDLE_ANGRY_MELEE,   false },
 
 
 
add the following list:
 
 
<source lang=cpp>
 
<source lang=cpp>
{ ACT_RANGE_ATTACK1,               ACT_RANGE_ATTACK_SLAM, true },
+
{ ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_SLAM, true },
    { ACT_HL2MP_IDLE,                   ACT_HL2MP_IDLE_MELEE,                   false },
+
{ ACT_HL2MP_IDLE, ACT_HL2MP_IDLE_MELEE, false },
    { ACT_HL2MP_RUN,                   ACT_HL2MP_RUN_MELEE,                   false },
+
{ ACT_HL2MP_RUN, ACT_HL2MP_RUN_MELEE, false },
    { ACT_HL2MP_IDLE_CROUCH,           ACT_HL2MP_IDLE_CROUCH_MELEE,           false },
+
{ ACT_HL2MP_IDLE_CROUCH, ACT_HL2MP_IDLE_CROUCH_MELEE, false },
    { ACT_HL2MP_WALK_CROUCH,           ACT_HL2MP_WALK_CROUCH_MELEE,           false },
+
{ ACT_HL2MP_WALK_CROUCH, ACT_HL2MP_WALK_CROUCH_MELEE, false },
    { ACT_HL2MP_GESTURE_RANGE_ATTACK,   ACT_HL2MP_GESTURE_RANGE_ATTACK_MELEE,   false },
+
{ ACT_HL2MP_GESTURE_RANGE_ATTACK, ACT_HL2MP_GESTURE_RANGE_ATTACK_MELEE, false },
    { ACT_HL2MP_GESTURE_RELOAD,           ACT_HL2MP_GESTURE_RELOAD_MELEE,           false },
+
{ ACT_HL2MP_GESTURE_RELOAD, ACT_HL2MP_GESTURE_RELOAD_MELEE, false },
    { ACT_HL2MP_JUMP,                   ACT_HL2MP_JUMP_MELEE,                   false },
+
{ ACT_HL2MP_JUMP, ACT_HL2MP_JUMP_MELEE, false },
 
</source>
 
</source>
  
In weapon_frag.cpp:
+
=== weapon_frag.cpp ===
  
There is already an activity list. At it's foot, after
+
There is already an activity list. At it's foot, after <code>{ ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_SLAM, true },</code> add the following list:
{ ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_SLAM, true },
 
 
 
add the following list:
 
 
<source lang=cpp>
 
<source lang=cpp>
{ ACT_HL2MP_IDLE,                   ACT_HL2MP_IDLE_GRENADE,                   false },
+
{ ACT_HL2MP_IDLE, ACT_HL2MP_IDLE_GRENADE, false },
    { ACT_HL2MP_RUN,                   ACT_HL2MP_RUN_GRENADE,                   false },
+
{ ACT_HL2MP_RUN, ACT_HL2MP_RUN_GRENADE, false },
    { ACT_HL2MP_IDLE_CROUCH,           ACT_HL2MP_IDLE_CROUCH_GRENADE,           false },
+
{ ACT_HL2MP_IDLE_CROUCH, ACT_HL2MP_IDLE_CROUCH_GRENADE, false },
    { ACT_HL2MP_WALK_CROUCH,           ACT_HL2MP_WALK_CROUCH_GRENADE,           false },
+
{ ACT_HL2MP_WALK_CROUCH, ACT_HL2MP_WALK_CROUCH_GRENADE, false },
    { ACT_HL2MP_GESTURE_RANGE_ATTACK,   ACT_HL2MP_GESTURE_RANGE_ATTACK_GRENADE,   false },
+
{ ACT_HL2MP_GESTURE_RANGE_ATTACK, ACT_HL2MP_GESTURE_RANGE_ATTACK_GRENADE, false },
    { ACT_HL2MP_GESTURE_RELOAD,           ACT_HL2MP_GESTURE_RELOAD_GRENADE,       false },
+
{ ACT_HL2MP_GESTURE_RELOAD, ACT_HL2MP_GESTURE_RELOAD_GRENADE, false },
    { ACT_HL2MP_JUMP,
+
{ ACT_HL2MP_JUMP, ACT_HL2MP_JUMP_GRENADE, false },
 
</source>
 
</source>
  
In weapon_physcannon.cpp:
+
=== weapon_physcannon.cpp ===
  
In the class description, around line 1203, under the macro "DECLARE_DATADESC()", add this line:
+
In the class description, around line 1203, under the macro <code>DECLARE_DATADESC()</code>, add this line:
 
<source lang=cpp>DECLARE_ACTTABLE();</source>
 
<source lang=cpp>DECLARE_ACTTABLE();</source>
  
Line 1,101: Line 1,022:
 
acttable_t CWeaponPhysCannon::m_acttable[] =
 
acttable_t CWeaponPhysCannon::m_acttable[] =
 
{
 
{
    { ACT_HL2MP_IDLE,                   ACT_HL2MP_IDLE_PHYSGUN,                   false },
+
{ ACT_HL2MP_IDLE, ACT_HL2MP_IDLE_PHYSGUN, false },
    { ACT_HL2MP_RUN,                   ACT_HL2MP_RUN_PHYSGUN,                   false },
+
{ ACT_HL2MP_RUN, ACT_HL2MP_RUN_PHYSGUN, false },
    { ACT_HL2MP_IDLE_CROUCH,           ACT_HL2MP_IDLE_CROUCH_PHYSGUN,           false },
+
{ ACT_HL2MP_IDLE_CROUCH, ACT_HL2MP_IDLE_CROUCH_PHYSGUN, false },
    { ACT_HL2MP_WALK_CROUCH,           ACT_HL2MP_WALK_CROUCH_PHYSGUN,           false },
+
{ ACT_HL2MP_WALK_CROUCH, ACT_HL2MP_WALK_CROUCH_PHYSGUN, false },
    { ACT_HL2MP_GESTURE_RANGE_ATTACK,   ACT_HL2MP_GESTURE_RANGE_ATTACK_PHYSGUN,   false },
+
{ ACT_HL2MP_GESTURE_RANGE_ATTACK, ACT_HL2MP_GESTURE_RANGE_ATTACK_PHYSGUN, false },
    { ACT_HL2MP_GESTURE_RELOAD,           ACT_HL2MP_GESTURE_RELOAD_PHYSGUN,       false },
+
{ ACT_HL2MP_GESTURE_RELOAD, ACT_HL2MP_GESTURE_RELOAD_PHYSGUN, false },
    { ACT_HL2MP_JUMP,                   ACT_HL2MP_JUMP_PHYSGUN,                   false },
+
{ ACT_HL2MP_JUMP, ACT_HL2MP_JUMP_PHYSGUN, false },
    { ACT_RANGE_ATTACK1,               ACT_RANGE_ATTACK_SLAM,               false },
+
{ ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_SLAM, false },
 
};
 
};
  
Line 1,114: Line 1,035:
 
</source>
 
</source>
  
 +
=== weapon_pistol.cpp ===
  
In weapon_pistol.cpp:
+
There is already an activity list. At it's foot, after <code>{ ACT_RUN, ACT_RUN_PISTOL, false },</code> add the following list:
 
 
There is already an activity list. At it's foot, after
 
{ ACT_RUN,   ACT_RUN_PISTOL,   false },
 
 
 
add the following list:
 
 
<source lang=cpp>
 
<source lang=cpp>
{ ACT_HL2MP_IDLE,                   ACT_HL2MP_IDLE_PISTOL,                   false },
+
{ ACT_HL2MP_IDLE, ACT_HL2MP_IDLE_PISTOL, false },
    { ACT_HL2MP_RUN,                   ACT_HL2MP_RUN_PISTOL,                   false },
+
{ ACT_HL2MP_RUN, ACT_HL2MP_RUN_PISTOL, false },
    { ACT_HL2MP_IDLE_CROUCH,           ACT_HL2MP_IDLE_CROUCH_PISTOL,           false },
+
{ ACT_HL2MP_IDLE_CROUCH, ACT_HL2MP_IDLE_CROUCH_PISTOL, false },
    { ACT_HL2MP_WALK_CROUCH,           ACT_HL2MP_WALK_CROUCH_PISTOL,           false },
+
{ ACT_HL2MP_WALK_CROUCH, ACT_HL2MP_WALK_CROUCH_PISTOL, false },
    { ACT_HL2MP_GESTURE_RANGE_ATTACK,   ACT_HL2MP_GESTURE_RANGE_ATTACK_PISTOL,   false },
+
{ ACT_HL2MP_GESTURE_RANGE_ATTACK, ACT_HL2MP_GESTURE_RANGE_ATTACK_PISTOL, false },
    { ACT_HL2MP_GESTURE_RELOAD,           ACT_HL2MP_GESTURE_RELOAD_PISTOL,       false },
+
{ ACT_HL2MP_GESTURE_RELOAD, ACT_HL2MP_GESTURE_RELOAD_PISTOL, false },
    { ACT_HL2MP_JUMP,                   ACT_HL2MP_JUMP_PISTOL,                   false },
+
{ ACT_HL2MP_JUMP, ACT_HL2MP_JUMP_PISTOL, false },
    { ACT_RANGE_ATTACK1,               ACT_RANGE_ATTACK_PISTOL,               false },
+
{ ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_PISTOL, false },
 
</source>
 
</source>
  
In weapon_rpg.cpp:
+
=== weapon_rpg.cpp ===
 
 
There is already an activity list. At it's foot, after
 
{ ACT_COVER_LOW,    ACT_COVER_LOW_RPG,    true },
 
  
add the following list:
+
There is already an activity list. At it's foot, after <code>{ ACT_COVER_LOW, ACT_COVER_LOW_RPG, true },</code> add the following list:
 
<source lang=cpp>
 
<source lang=cpp>
{ ACT_HL2MP_IDLE,                   ACT_HL2MP_IDLE_RPG,                   false },
+
{ ACT_HL2MP_IDLE, ACT_HL2MP_IDLE_RPG, false },
    { ACT_HL2MP_RUN,                   ACT_HL2MP_RUN_RPG,                   false },
+
{ ACT_HL2MP_RUN, ACT_HL2MP_RUN_RPG, false },
    { ACT_HL2MP_IDLE_CROUCH,           ACT_HL2MP_IDLE_CROUCH_RPG,           false },
+
{ ACT_HL2MP_IDLE_CROUCH, ACT_HL2MP_IDLE_CROUCH_RPG, false },
    { ACT_HL2MP_WALK_CROUCH,           ACT_HL2MP_WALK_CROUCH_RPG,           false },
+
{ ACT_HL2MP_WALK_CROUCH, ACT_HL2MP_WALK_CROUCH_RPG, false },
    { ACT_HL2MP_GESTURE_RANGE_ATTACK,   ACT_HL2MP_GESTURE_RANGE_ATTACK_RPG,   false },
+
{ ACT_HL2MP_GESTURE_RANGE_ATTACK, ACT_HL2MP_GESTURE_RANGE_ATTACK_RPG, false },
    { ACT_HL2MP_GESTURE_RELOAD,           ACT_HL2MP_GESTURE_RELOAD_RPG,       false },
+
{ ACT_HL2MP_GESTURE_RELOAD, ACT_HL2MP_GESTURE_RELOAD_RPG, false },
    { ACT_HL2MP_JUMP,                   ACT_HL2MP_JUMP_RPG,                   false },
+
{ ACT_HL2MP_JUMP, ACT_HL2MP_JUMP_RPG, false },
    { ACT_RANGE_ATTACK1,               ACT_RANGE_ATTACK_RPG,               false },
+
{ ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_RPG, false },
 
</source>
 
</source>
  
 +
=== weapon_shotgun.cpp ===
  
In weapon_shotgun.cpp:
+
There is already an activity list. At it's foot, after <code>{ ACT_GESTURE_RELOAD, ACT_GESTURE_RELOAD_SHOTGUN, false },</code> add the following list:
 
 
There is already an activity list. At it's foot, after
 
{ ACT_GESTURE_RELOAD,   ACT_GESTURE_RELOAD_SHOTGUN,       false },
 
 
 
add the following list:
 
 
<source lang=cpp>
 
<source lang=cpp>
{ ACT_HL2MP_IDLE,                   ACT_HL2MP_IDLE_SHOTGUN,                   false },
+
{ ACT_HL2MP_IDLE, ACT_HL2MP_IDLE_SHOTGUN, false },
    { ACT_HL2MP_RUN,                   ACT_HL2MP_RUN_SHOTGUN,                   false },
+
{ ACT_HL2MP_RUN, ACT_HL2MP_RUN_SHOTGUN, false },
    { ACT_HL2MP_IDLE_CROUCH,           ACT_HL2MP_IDLE_CROUCH_SHOTGUN,           false },
+
{ ACT_HL2MP_IDLE_CROUCH, ACT_HL2MP_IDLE_CROUCH_SHOTGUN, false },
    { ACT_HL2MP_WALK_CROUCH,           ACT_HL2MP_WALK_CROUCH_SHOTGUN,           false },
+
{ ACT_HL2MP_WALK_CROUCH, ACT_HL2MP_WALK_CROUCH_SHOTGUN, false },
    { ACT_HL2MP_GESTURE_RANGE_ATTACK,   ACT_HL2MP_GESTURE_RANGE_ATTACK_SHOTGUN,   false },
+
{ ACT_HL2MP_GESTURE_RANGE_ATTACK, ACT_HL2MP_GESTURE_RANGE_ATTACK_SHOTGUN, false },
    { ACT_HL2MP_GESTURE_RELOAD,           ACT_HL2MP_GESTURE_RELOAD_SHOTGUN,       false },
+
{ ACT_HL2MP_GESTURE_RELOAD, ACT_HL2MP_GESTURE_RELOAD_SHOTGUN, false },
    { ACT_HL2MP_JUMP,                   ACT_HL2MP_JUMP_SHOTGUN,                   false },
+
{ ACT_HL2MP_JUMP, ACT_HL2MP_JUMP_SHOTGUN, false },
    { ACT_RANGE_ATTACK1,               ACT_RANGE_ATTACK_SHOTGUN,               false },
+
{ ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_SHOTGUN, false },
 
</source>
 
</source>
  
In weapon_smg1.cpp:
+
=== weapon_smg1.cpp ===
  
There is already an activity list. At it's foot, after
+
There is already an activity list. At it's foot, after <code>{ ACT_GESTURE_RELOAD, ACT_GESTURE_RELOAD_SMG1, true },</code> add the following list:
{ ACT_GESTURE_RELOAD,   ACT_GESTURE_RELOAD_SMG1,   true },
 
 
 
add the following list:
 
 
<source lang=cpp>
 
<source lang=cpp>
{ ACT_HL2MP_IDLE,                   ACT_HL2MP_IDLE_SMG1,                   false },
+
{ ACT_HL2MP_IDLE, ACT_HL2MP_IDLE_SMG1, false },
    { ACT_HL2MP_RUN,                   ACT_HL2MP_RUN_SMG1,                   false },
+
{ ACT_HL2MP_RUN, ACT_HL2MP_RUN_SMG1, false },
    { ACT_HL2MP_IDLE_CROUCH,           ACT_HL2MP_IDLE_CROUCH_SMG1,           false },
+
{ ACT_HL2MP_IDLE_CROUCH, ACT_HL2MP_IDLE_CROUCH_SMG1, false },
    { ACT_HL2MP_WALK_CROUCH,           ACT_HL2MP_WALK_CROUCH_SMG1,           false },
+
{ ACT_HL2MP_WALK_CROUCH, ACT_HL2MP_WALK_CROUCH_SMG1, false },
    { ACT_HL2MP_GESTURE_RANGE_ATTACK,   ACT_HL2MP_GESTURE_RANGE_ATTACK_SMG1,   false },
+
{ ACT_HL2MP_GESTURE_RANGE_ATTACK, ACT_HL2MP_GESTURE_RANGE_ATTACK_SMG1, false },
    { ACT_HL2MP_GESTURE_RELOAD,           ACT_GESTURE_RELOAD_SMG1,       false },
+
{ ACT_HL2MP_GESTURE_RELOAD, ACT_GESTURE_RELOAD_SMG1, false },
    { ACT_HL2MP_JUMP,                   ACT_HL2MP_JUMP_SMG1,                   false },
+
{ ACT_HL2MP_JUMP, ACT_HL2MP_JUMP_SMG1, false },
    { ACT_RANGE_ATTACK1,               ACT_RANGE_ATTACK_SMG1,               false },
+
{ ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_SMG1, false },
 
</source>
 
</source>
  
==Creating the Model==
+
== Creating the model ==
If you do create a custom model for this, download the original player.mdl SMD files for reference [http://www.mediafire.com/download.php?nv0befqe5a7rr1u here].
 
  
When creating the actual model make it look like the one in gordon_body_ref.smd or like in the picture [http://img689.imageshack.us/img689/7449/part1s.jpg here].
+
If you do create a custom model for this, look at the original '''player.mdl''' SMD files found in the provided archive for reference.
  
 +
When creating the actual model make it look like the one in '''gordon_body_ref.smd''' or like in the picture [http://img689.imageshack.us/img689/7449/part1s.jpg here] (REMOVED).
  
==Finished Product==
+
== Finished product ==
  
View the finished product [http://youtu.be/GiPg3-QJeic here] on youtube.
+
View the finished product [http://youtu.be/GiPg3-QJeic here] on YouTube (REMOVED).
  
==Acknowledgments==
+
== Acknowledgments ==
  
*Singleplayer Animation Code - Malortie
+
*Singleplayer animation code - Malortie
*Weapon Activity Tables       - Malortie
+
*Weapon activity tables       - Malortie
*Other Stuff                 - Zombie Killa
+
*Other stuff                 - Zombie Killa
 
*Gordon Freeman              - VALVe and DPFilms
 
*Gordon Freeman              - VALVe and DPFilms
 +
 +
{{DISPLAYTITLE:Legs in First Person}}
 +
[[Category: Programming]]
 +
[[Category: Tutorials]]

Latest revision as of 17:19, 23 August 2020

Русский

This is a copy and paste tutorial that will allow you to see your legs in first person.

Note.png Note: This only works in Single Player 2007 Engine (OB). It will not work in a multiplayer mod because it replaces the actual third person model to a new model without arms or head.
Note.png Note: This currently does not work in Source SDK 2013.

Requirements

  • A single player mod running on Source SDK 2007
  • A 3D modeling application (XSI, 3ds Max, Blender, etc.)
  • First person animation model pack (available here)
  • Knowledge of C++

Animation state system implementation

You will need to follow the code by Malortie to get this to work properly.

Create these two files in the src/game/shared/ folder of your mod.

singleplayer_animstate.h

//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose: Single Player animation state 'handler'. This utility is used
//            to evaluate the pose parameter value based on the direction
//            and speed of the player.
//
//====================================================================================//

#ifndef SINGLEPLAYER_ANIMSTATE_H
#define SINGLEPLAYER_ANIMSTATE_H
#ifdef _WIN32
#pragma once
#endif

#include "cbase.h"

#ifdef CLIENT_DLL
	#include "c_baseplayer.h"
#else
	#include "player.h"
#endif

class CSinglePlayerAnimState
{
public:
	enum
	{
		TURN_NONE = 0,
		TURN_LEFT,
		TURN_RIGHT
	};

	CSinglePlayerAnimState( CBasePlayer *pPlayer );

	void				Init( CBasePlayer *pPlayer );

	Activity			BodyYawTranslateActivity( Activity activity );

	void				Update();

	const QAngle		&GetRenderAngles();

	void				GetPoseParameters( CStudioHdr *pStudioHdr, float poseParameter[MAXSTUDIOPOSEPARAM] );

	CBasePlayer			*GetBasePlayer();

	void				Release();

private:
	void				GetOuterAbsVelocity( Vector &vel );

	int					ConvergeAngles( float goal, float maxrate, float dt, float &current );

	void				EstimateYaw( void );
	void				ComputePoseParam_BodyYaw( void );
	void				ComputePoseParam_BodyPitch( CStudioHdr *pStudioHdr );
	void				ComputePoseParam_BodyLookYaw( void );
	void				ComputePoseParam_HeadPitch( CStudioHdr *pStudioHdr );
	void				ComputePlaybackRate();

	CBasePlayer			*m_pPlayer;

	float				m_flGaitYaw;
	float				m_flStoredCycle;

	float				m_flGoalFeetYaw;
	float				m_flCurrentFeetYaw;

	float				m_flCurrentTorsoYaw;

	float				m_flLastYaw;
	float				m_flLastTurnTime;

	int					m_nTurningInPlace;

	QAngle				m_angRender;

	float				m_flTurnCorrectionTime;
};

CSinglePlayerAnimState *CreatePlayerAnimationState( CBasePlayer *pPlayer );

#endif // SINGLEPLAYER_ANIMSTATE_H


singleplayer_animstate.cpp

//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose: Single Player animation state 'handler'. This utility is used
//            to evaluate the pose parameter value based on the direction
//            and speed of the player.
//
//====================================================================================//

#include "cbase.h"
#include "singleplayer_animstate.h"
#include "tier0/vprof.h"
#include "animation.h"
#include "studio.h"
#include "apparent_velocity_helper.h"
#include "utldict.h"
#include "filesystem.h"

extern ConVar mp_facefronttime, mp_feetyawrate, mp_ik;

#define MIN_TURN_ANGLE_REQUIRING_TURN_ANIMATION 15.0f

CSinglePlayerAnimState *CreatePlayerAnimationState( CBasePlayer *pPlayer )
{
	MDLCACHE_CRITICAL_SECTION();

	CSinglePlayerAnimState *pState = new CSinglePlayerAnimState( pPlayer );
	pState->Init(pPlayer);

	return pState;
}

// 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;

CSinglePlayerAnimState::CSinglePlayerAnimState( CBasePlayer *pPlayer ) : m_pPlayer( pPlayer )
{
	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;

	m_pPlayer = NULL;
};

void CSinglePlayerAnimState::Init( CBasePlayer *pPlayer )
{
	m_pPlayer = pPlayer;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CSinglePlayerAnimState::Update()
{
	m_angRender = GetBasePlayer()->GetLocalAngles();

	ComputePoseParam_BodyYaw();
	ComputePoseParam_BodyPitch( GetBasePlayer()->GetModelPtr() );
	ComputePoseParam_BodyLookYaw();
	ComputePoseParam_HeadPitch( GetBasePlayer()->GetModelPtr() );

	ComputePlaybackRate();
}

void CSinglePlayerAnimState::Release()
{
	delete this;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CSinglePlayerAnimState::ComputePlaybackRate()
{
	// Determine ideal playback rate
	Vector vel;
	GetOuterAbsVelocity( vel );

	float speed = vel.Length2D();

	bool isMoving = (speed > 0.5f) ? true : false;

	float maxspeed = GetBasePlayer()->GetSequenceGroundSpeed( GetBasePlayer()->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
		GetBasePlayer()->SetPlaybackRate( (speed * flFactor) / maxspeed );

		// BUG BUG:
		// This stuff really should be m_flPlaybackRate = speed / m_flGroundSpeed
	}
	else
	{
		GetBasePlayer()->SetPlaybackRate( 1.0f );
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
// Output : CBasePlayer
//-----------------------------------------------------------------------------
CBasePlayer *CSinglePlayerAnimState::GetBasePlayer()
{
	return m_pPlayer;
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : dt -
//-----------------------------------------------------------------------------
void CSinglePlayerAnimState::EstimateYaw( void )
{
	float dt = gpGlobals->frametime;

	if ( !dt )
		return;

	Vector est_velocity;
	QAngle angles;

	GetOuterAbsVelocity( est_velocity );

	angles = GetBasePlayer()->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 backpedaling
// Input  : dt -
//-----------------------------------------------------------------------------
void CSinglePlayerAnimState::ComputePoseParam_BodyYaw( void )
{
	int iYaw = GetBasePlayer()->LookupPoseParameter( "move_yaw" );

	if ( iYaw < 0 )
		return;

	// View direction relative to movement
	float flYaw;

	EstimateYaw();

	QAngle angles = GetBasePlayer()->GetLocalAngles();
	float ang = angles[YAW];
	if ( ang > 180.0f )
		ang -= 360.0f;
	else if ( ang < -180.0f )
		ang += 360.0f;

	// Calculate 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;

	GetBasePlayer()->SetPoseParameter( iYaw, flYaw );

#ifndef CLIENT_DLL
	// Adrian: Make the model's angle match the legs so the hitboxes match on both sides.
	GetBasePlayer()->SetLocalAngles( QAngle( GetBasePlayer()->EyeAngles().x, m_flCurrentFeetYaw, 0 ) );
#endif
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CSinglePlayerAnimState::ComputePoseParam_BodyPitch( CStudioHdr *pStudioHdr )
{
	// Get pitch from v_angle
	float flPitch = GetBasePlayer()->GetLocalAngles()[PITCH];

	if ( flPitch > 180.0f )
	{
		flPitch -= 360.0f;
	}
	flPitch = clamp( flPitch, -90, 90 );

	QAngle absangles = GetBasePlayer()->GetAbsAngles();
	absangles.x = 0.0f;
	m_angRender = absangles;

	// See if we have a blender for pitch
	GetBasePlayer()->SetPoseParameter( pStudioHdr, "aim_pitch", flPitch );
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : goal -
//          maxrate -
//          dt -
//          current -
// Output : int
//-----------------------------------------------------------------------------
int CSinglePlayerAnimState::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 CSinglePlayerAnimState::ComputePoseParam_BodyLookYaw( void )
{
	QAngle absangles = GetBasePlayer()->GetAbsAngles();
	absangles.y = AngleNormalize( absangles.y );
	m_angRender = absangles;

	// See if we even have a blender for pitch
	int upper_body_yaw = GetBasePlayer()->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			= GetBasePlayer()->EyeAngles().y;

			// Snap feet to be perfectly aligned with torso/eyes
			m_flGoalFeetYaw		= GetBasePlayer()->EyeAngles().y;
			m_flCurrentFeetYaw	= m_flGoalFeetYaw;
			m_nTurningInPlace	= TURN_NONE;
		}

		// If rotating in place, update stasis timer
		if ( m_flLastYaw != GetBasePlayer()->EyeAngles().y )
		{
			m_flLastTurnTime	= gpGlobals->curtime;
			m_flLastYaw			= GetBasePlayer()->EyeAngles().y;
		}

		if ( m_flGoalFeetYaw != m_flCurrentFeetYaw )
			m_flLastTurnTime = gpGlobals->curtime;

		turning = ConvergeAngles( m_flGoalFeetYaw, turnrate, gpGlobals->frametime, m_flCurrentFeetYaw );

		QAngle eyeAngles = GetBasePlayer()->EyeAngles();
		QAngle vAngle = GetBasePlayer()->GetLocalAngles();

		// See how far off current feetyaw is from true yaw
		float yawdelta = GetBasePlayer()->EyeAngles().y - m_flCurrentFeetYaw;
		yawdelta = AngleNormalize( yawdelta );

		bool rotated_too_far = false;

		// If too far, then need to turn in place
		float yawmagnitude = fabs( yawdelta );
		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  = GetBasePlayer()->EyeAngles().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 = GetBasePlayer()->EyeAngles().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 = GetBasePlayer()->EyeAngles().y;
		flGoalTorsoYaw		= 0.0f;
		m_flCurrentTorsoYaw	= GetBasePlayer()->EyeAngles().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	= GetBasePlayer()->GetAbsAngles();
	absangles.y	= m_flCurrentFeetYaw;
	m_angRender	= absangles;

	GetBasePlayer()->SetPoseParameter( upper_body_yaw, clamp( m_flCurrentTorsoYaw, -60.0f, 60.0f ) );

	/*
	// FIXME: Adrian, what is this?
	int body_yaw = GetBasePlayer()->LookupPoseParameter( "body_yaw" );
	if ( body_yaw >= 0 )
		GetBasePlayer()->SetPoseParameter( body_yaw, 30 );
	*/
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CSinglePlayerAnimState::ComputePoseParam_HeadPitch( CStudioHdr *pStudioHdr )
{
	// Get pitch from v_angle
	int iHeadPitch = GetBasePlayer()->LookupPoseParameter( "head_pitch" );

	float flPitch = GetBasePlayer()->EyeAngles()[PITCH];

	if ( flPitch > 180.0f )
	{
		flPitch -= 360.0f;
	}
	flPitch = clamp( flPitch, -90, 90 );

	QAngle absangles = GetBasePlayer()->GetAbsAngles();
	absangles.x = 0.0f;
	m_angRender = absangles;

	GetBasePlayer()->SetPoseParameter( pStudioHdr, iHeadPitch, flPitch );
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : activity -
// Output : Activity
//-----------------------------------------------------------------------------
Activity CSinglePlayerAnimState::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 &CSinglePlayerAnimState::GetRenderAngles()
{
	return m_angRender;
}

void CSinglePlayerAnimState::GetOuterAbsVelocity( Vector &vel )
{
#if defined( CLIENT_DLL )
	GetBasePlayer()->EstimateAbsVelocity( vel );
#else
	vel = GetBasePlayer()->GetAbsVelocity();
#endif
}

Full implementation

c_baseplayer.h

Search for bool IsInFreezeCam( void ); and under it add:

bool IsInEye( void );

Search for virtual int DrawModel( int flags ); and under it add:

const Vector &C_BasePlayer::GetRenderOrigin();

c_baseplayer.cpp

Search for the function bool IsInFreezeCam( void ) and under it add:

bool IsInEye( void )
{
	C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer();
	if ( pPlayer && pPlayer->GetObserverMode() == OBS_MODE_IN_EYE )
		return true;
}

Next, search for bool C_BasePlayer::ShouldDraw() and make it look like this:

bool C_BasePlayer::ShouldDraw()
{
	return BaseClass::ShouldDraw();
}

then and under that add:

static ConVar cl_legs( "cl_legs", "1", FCVAR_CHEAT, "Enable or disable player leg rendering", true, 0, true, 1 );
static ConVar cl_legs_origin_shift( "cl_legs_origin_shift", "-17", FCVAR_CHEAT, "Amount in game units to shift the player model relative to the direction the player is facing" );
static ConVar cl_legs_clip_height( "cl_legs_clip_height", "0", FCVAR_CHEAT, "Amount in game units of the player model to render up to [0 = disable]", true, 0, false, 0 );

const Vector &C_BasePlayer::GetRenderOrigin( void )
{
	// If we're not observing this player, or if we're not drawing it at the
	// moment then use the normal absolute origin.
	// NOTE: the GetCurrentlyDrawingEntity check is here to make sure the
	// shadow is rendered from the correct origin
	if( !IsInEye() || view->GetCurrentlyDrawingEntity() != this )
		return BaseClass::GetRenderOrigin();

	// Get the forward vector
	static Vector forward; // static because this method returns a reference
	AngleVectors(GetRenderAngles(), &forward);

	// Shift the render origin by a fixed amount
	forward *= cl_legs_origin_shift.GetFloat();
	forward += GetAbsOrigin();

	return forward;
}

Now, in the int C_BasePlayer::DrawModel( int flags ) function add:

	CMatRenderContextPtr context( materials );

	if ( cl_legs_clip_height.GetInt() > 0 )
	{
		context->SetHeightClipMode( MATERIAL_HEIGHTCLIPMODE_RENDER_BELOW_HEIGHT );
		context->SetHeightClipZ( GetAbsOrigin().z + cl_legs_clip_height.GetFloat() );
	}

Now, search for the void C_BasePlayer::AddEntity( void ) function and under

	// Add in lighting effects
	CreateLightEffects();

add:

	SetLocalAnglesDim( X_INDEX, 0 );

hl2_player.h

Add #include "singleplayer_animstate.h" to the top of the file.


Now, go to the end of the public: section in the class CHL2_Player class and add

void SetAnimation( PLAYER_ANIM playerAnim );


Now, go right to the end of the class, in the private: section and add:

	CSinglePlayerAnimState *m_pPlayerAnimState;
	QAngle m_angEyeAngles;

hl2_player.cpp

Add

#define PLAYER_MODEL "models/player.mdl"

with all the other #define's


Now, search for

void CHL2_Player::Precache( void )

and add:

PrecacheModel( PLAYER_MODEL );


Now, search for

void CHL2_Player::Spawn(void)

and find the following line

SetModel( "models/player.mdl" );

and set it to:

SetModel( PLAYER_MODEL );


Now, search for CHL2_Player::~CHL2_Player( void ) and make it look like this:

CHL2_Player::~CHL2_Player( void )
{
	// Clears the animation state.
	if ( m_pPlayerAnimState != NULL )
	{
	    m_pPlayerAnimState->Release();
	    m_pPlayerAnimState = NULL;
	}
}

Now, search for CHL2_Player::CHL2_Player() and make it look like:

CHL2_Player::CHL2_Player()
{
	m_pPlayerAnimState = CreatePlayerAnimationState( this );
	m_angEyeAngles.Init();

	m_nNumMissPositions = 0;
	m_pPlayerAISquad = 0;
	m_bSprintEnabled = true;

	m_flArmorReductionTime = 0.0f;
	m_iArmorReductionFrom = 0;
}

Now, head up to void CHL2_Player::PostThink( void ) and make it look like this:

void CHL2_Player::PostThink( void )
{
	BaseClass::PostThink();

	if ( !g_fGameOver && !IsPlayerLockedInPlace() && IsAlive() )
	{
		HandleAdmireGlovesAnimation();
	}

	m_angEyeAngles = EyeAngles();
	QAngle angles = GetLocalAngles();
	angles[PITCH] = 0;
	SetLocalAngles( angles );
	m_pPlayerAnimState->Update();
}

Now, add this to the end of the file:

void CHL2_Player::SetAnimation( PLAYER_ANIM playerAnim )
{
	int animDesired;
	float speed = GetAbsVelocity().Length2D();

	if ( GetFlags() & (FL_FROZEN | FL_ATCONTROLS) )
	{
		speed = 0;
		playerAnim = PLAYER_IDLE;
	}

	Activity idealActivity = ACT_HL2MP_RUN;

	if ( playerAnim == PLAYER_JUMP )
	{
		if ( HasWeapons() )
			idealActivity = ACT_HL2MP_JUMP;
		else
			idealActivity = ACT_JUMP;
	}
	else if ( playerAnim == PLAYER_DIE )
	{
		if ( m_lifeState == LIFE_ALIVE )
			return;
	}
	else if ( playerAnim == PLAYER_ATTACK1 )
	{
		if ( GetActivity() == ACT_HOVER      || 
			GetActivity() == ACT_SWIM        || 
			GetActivity() == ACT_HOP         || 
			GetActivity() == ACT_LEAP        || 
			GetActivity() == ACT_DIESIMPLE )
		{
			idealActivity = GetActivity();
		}
		else
		{
			idealActivity = ACT_HL2MP_GESTURE_RANGE_ATTACK;
		}
	}
	else if ( playerAnim == PLAYER_RELOAD )
	{
		idealActivity = ACT_HL2MP_GESTURE_RELOAD;
	}
	else if ( playerAnim == PLAYER_IDLE || playerAnim == PLAYER_WALK )
	{
		if ( !(GetFlags() & FL_ONGROUND) && (GetActivity() == ACT_HL2MP_JUMP || GetActivity() == ACT_JUMP) ) // Still jumping
		{
			idealActivity = GetActivity( );
		}
		else if ( GetWaterLevel() > 1 )
		{
			if ( speed == 0 )
			{
				if ( HasWeapons() )
					idealActivity = ACT_HL2MP_IDLE;
				else
					idealActivity = ACT_IDLE;
			}
			else
			{
				if ( HasWeapons() )
					idealActivity = ACT_HL2MP_RUN;
				else
					idealActivity = ACT_RUN;
			}
		}
		else
		{
			if ( GetFlags() & FL_DUCKING )
			{
				if ( speed > 0 )
				{
					if ( HasWeapons() )
						idealActivity = ACT_HL2MP_WALK_CROUCH;
					else
						idealActivity = ACT_WALK_CROUCH;
				}
				else
				{
					if ( HasWeapons() )
						idealActivity = ACT_HL2MP_IDLE_CROUCH;
					else
						idealActivity = ACT_COVER_LOW;
				}
			}
			else
			{
				if ( speed > 0 )
				{
					{
						if ( HasWeapons() )
							idealActivity = ACT_HL2MP_RUN;
						else
						{
							if ( speed > HL2_WALK_SPEED + 20.0f )
								idealActivity = ACT_RUN;
							else
								idealActivity = ACT_WALK;
						}
					}
				}
				else
				{
					if ( HasWeapons() )
						idealActivity = ACT_HL2MP_IDLE;
					else
						idealActivity = ACT_IDLE;
				}
			}
		}

		//idealActivity = TranslateTeamActivity( idealActivity );
	}

	if ( IsInAVehicle() )
		idealActivity = ACT_COVER_LOW;

	if ( idealActivity == ACT_HL2MP_GESTURE_RANGE_ATTACK )
	{
		RestartGesture( Weapon_TranslateActivity( idealActivity ) );

		// FIXME: this seems a bit whacked
		Weapon_SetActivity( Weapon_TranslateActivity( ACT_RANGE_ATTACK1 ), 0 );

		return;
	}
	else if ( idealActivity == ACT_HL2MP_GESTURE_RELOAD )
	{
		RestartGesture( Weapon_TranslateActivity( idealActivity ) );
		return;
	}
	else
	{
		SetActivity( idealActivity );

		animDesired = SelectWeightedSequence( Weapon_TranslateActivity( idealActivity ) );

		if ( animDesired == -1 )
		{
			animDesired = SelectWeightedSequence( idealActivity );

			if ( animDesired == -1 )
				animDesired = 0;
		}

		// Already using the desired animation?
		if ( GetSequence() == animDesired )
			return;

		m_flPlaybackRate = 1.0f;
		ResetSequence( animDesired );
		SetCycle( 0 );

		return;
	}

	// Already using the desired animation?
	if ( GetSequence() == animDesired )
		return;

	//Msg( "Set animation to %d\n", animDesired );
	// Reset to first frame of desired animation
	ResetSequence( animDesired );
	SetCycle( 0 );
}

Weapon activity list tables

weapon_357.cpp

In the class description, under the macro DECLARE_DATADESC(), add this line:

DECLARE_ACTTABLE();

Under the class definition around line 50, add this activity table list:

acttable_t CWeapon357::m_acttable[] =
{
	{ ACT_HL2MP_IDLE,					ACT_HL2MP_IDLE_PISTOL,					false },
	{ ACT_HL2MP_RUN,					ACT_HL2MP_RUN_PISTOL,					false },
	{ ACT_HL2MP_IDLE_CROUCH,			ACT_HL2MP_IDLE_CROUCH_PISTOL,			false },
	{ ACT_HL2MP_WALK_CROUCH,			ACT_HL2MP_WALK_CROUCH_PISTOL,			false },
	{ ACT_HL2MP_GESTURE_RANGE_ATTACK,	ACT_HL2MP_GESTURE_RANGE_ATTACK_PISTOL,	false },
	{ ACT_HL2MP_GESTURE_RELOAD,			ACT_HL2MP_GESTURE_RELOAD_PISTOL,		false },
	{ ACT_HL2MP_JUMP,					ACT_HL2MP_JUMP_PISTOL,					false },
	{ ACT_RANGE_ATTACK1,				ACT_RANGE_ATTACK_PISTOL,				false },
};

IMPLEMENT_ACTTABLE( CWeapon357 );

weapon_ar2.cpp

There is already an activity list. At it's foot, after //{ ACT_RANGE_ATTACK2, ACT_RANGE_ATTACK_AR2_GRENADE, true }, add the following list:

	{ ACT_HL2MP_IDLE,					ACT_HL2MP_IDLE_AR2,						false },
	{ ACT_HL2MP_RUN,					ACT_HL2MP_RUN_AR2,						false },
	{ ACT_HL2MP_IDLE_CROUCH,			ACT_HL2MP_IDLE_CROUCH_AR2,				false },
	{ ACT_HL2MP_WALK_CROUCH,			ACT_HL2MP_WALK_CROUCH_AR2,				false },
	{ ACT_HL2MP_GESTURE_RANGE_ATTACK,	ACT_HL2MP_GESTURE_RANGE_ATTACK_AR2,		false },
	{ ACT_HL2MP_GESTURE_RELOAD,			ACT_GESTURE_RELOAD_SMG1,				false },
	{ ACT_HL2MP_JUMP,					ACT_HL2MP_JUMP_AR2,						false },
	{ ACT_RANGE_ATTACK1,				ACT_RANGE_ATTACK_AR2,					false },

weapon_crossbow.cpp

In the class description, around line 450, under the macro DECLARE_DATADESC(), add this line:

DECLARE_ACTTABLE();

Under the class definition around line 489, add this activity table list:

acttable_t CWeaponCrossbow::m_acttable[] =
{
	{ ACT_HL2MP_IDLE,					ACT_HL2MP_IDLE_CROSSBOW,					false },
	{ ACT_HL2MP_RUN,					ACT_HL2MP_RUN_CROSSBOW,						false },
	{ ACT_HL2MP_IDLE_CROUCH,			ACT_HL2MP_IDLE_CROUCH_CROSSBOW,				false },
	{ ACT_HL2MP_WALK_CROUCH,			ACT_HL2MP_WALK_CROUCH_CROSSBOW,				false },
	{ ACT_HL2MP_GESTURE_RANGE_ATTACK,	ACT_HL2MP_GESTURE_RANGE_ATTACK_CROSSBOW,	false },
	{ ACT_HL2MP_GESTURE_RELOAD,			ACT_HL2MP_GESTURE_RELOAD_CROSSBOW,			false },
	{ ACT_HL2MP_JUMP,					ACT_HL2MP_JUMP_CROSSBOW,					false },
	{ ACT_RANGE_ATTACK1,				ACT_RANGE_ATTACK_SHOTGUN,					false },
};

IMPLEMENT_ACTTABLE( CWeaponCrossbow );

weapon_crowbar.cpp

There is already an activity list. At it's foot, after { ACT_IDLE_ANGRY, ACT_IDLE_ANGRY_MELEE, false }, add the following list:

	{ ACT_RANGE_ATTACK1,				ACT_RANGE_ATTACK_SLAM,					true },
	{ ACT_HL2MP_IDLE,					ACT_HL2MP_IDLE_MELEE,					false },
	{ ACT_HL2MP_RUN,					ACT_HL2MP_RUN_MELEE,					false },
	{ ACT_HL2MP_IDLE_CROUCH,			ACT_HL2MP_IDLE_CROUCH_MELEE,			false },
	{ ACT_HL2MP_WALK_CROUCH,			ACT_HL2MP_WALK_CROUCH_MELEE,			false },
	{ ACT_HL2MP_GESTURE_RANGE_ATTACK,	ACT_HL2MP_GESTURE_RANGE_ATTACK_MELEE,	false },
	{ ACT_HL2MP_GESTURE_RELOAD,			ACT_HL2MP_GESTURE_RELOAD_MELEE,			false },
	{ ACT_HL2MP_JUMP,					ACT_HL2MP_JUMP_MELEE,					false },

weapon_frag.cpp

There is already an activity list. At it's foot, after { ACT_RANGE_ATTACK1, ACT_RANGE_ATTACK_SLAM, true }, add the following list:

	{ ACT_HL2MP_IDLE,					ACT_HL2MP_IDLE_GRENADE,						false },
	{ ACT_HL2MP_RUN,					ACT_HL2MP_RUN_GRENADE,						false },
	{ ACT_HL2MP_IDLE_CROUCH,			ACT_HL2MP_IDLE_CROUCH_GRENADE,				false },
	{ ACT_HL2MP_WALK_CROUCH,			ACT_HL2MP_WALK_CROUCH_GRENADE,				false },
	{ ACT_HL2MP_GESTURE_RANGE_ATTACK,	ACT_HL2MP_GESTURE_RANGE_ATTACK_GRENADE,		false },
	{ ACT_HL2MP_GESTURE_RELOAD,			ACT_HL2MP_GESTURE_RELOAD_GRENADE,			false },
	{ ACT_HL2MP_JUMP,					ACT_HL2MP_JUMP_GRENADE,						false },

weapon_physcannon.cpp

In the class description, around line 1203, under the macro DECLARE_DATADESC(), add this line:

DECLARE_ACTTABLE();

Under the class definition around line 1374, add this activity table list:

acttable_t CWeaponPhysCannon::m_acttable[] =
{
	{ ACT_HL2MP_IDLE,					ACT_HL2MP_IDLE_PHYSGUN,						false },
	{ ACT_HL2MP_RUN,					ACT_HL2MP_RUN_PHYSGUN,						false },
	{ ACT_HL2MP_IDLE_CROUCH,			ACT_HL2MP_IDLE_CROUCH_PHYSGUN,				false },
	{ ACT_HL2MP_WALK_CROUCH,			ACT_HL2MP_WALK_CROUCH_PHYSGUN,				false },
	{ ACT_HL2MP_GESTURE_RANGE_ATTACK,	ACT_HL2MP_GESTURE_RANGE_ATTACK_PHYSGUN,		false },
	{ ACT_HL2MP_GESTURE_RELOAD,			ACT_HL2MP_GESTURE_RELOAD_PHYSGUN,			false },
	{ ACT_HL2MP_JUMP,					ACT_HL2MP_JUMP_PHYSGUN,						false },
	{ ACT_RANGE_ATTACK1,				ACT_RANGE_ATTACK_SLAM,						false },
};

IMPLEMENT_ACTTABLE( CWeaponPhysCannon );

weapon_pistol.cpp

There is already an activity list. At it's foot, after { ACT_RUN, ACT_RUN_PISTOL, false }, add the following list:

	{ ACT_HL2MP_IDLE,					ACT_HL2MP_IDLE_PISTOL,					false },
	{ ACT_HL2MP_RUN,					ACT_HL2MP_RUN_PISTOL,					false },
	{ ACT_HL2MP_IDLE_CROUCH,			ACT_HL2MP_IDLE_CROUCH_PISTOL,			false },
	{ ACT_HL2MP_WALK_CROUCH,			ACT_HL2MP_WALK_CROUCH_PISTOL,			false },
	{ ACT_HL2MP_GESTURE_RANGE_ATTACK,	ACT_HL2MP_GESTURE_RANGE_ATTACK_PISTOL,	false },
	{ ACT_HL2MP_GESTURE_RELOAD,			ACT_HL2MP_GESTURE_RELOAD_PISTOL,		false },
	{ ACT_HL2MP_JUMP,					ACT_HL2MP_JUMP_PISTOL,					false },
	{ ACT_RANGE_ATTACK1,				ACT_RANGE_ATTACK_PISTOL,				false },

weapon_rpg.cpp

There is already an activity list. At it's foot, after { ACT_COVER_LOW, ACT_COVER_LOW_RPG, true }, add the following list:

	{ ACT_HL2MP_IDLE,					ACT_HL2MP_IDLE_RPG,						false },
	{ ACT_HL2MP_RUN,					ACT_HL2MP_RUN_RPG,						false },
	{ ACT_HL2MP_IDLE_CROUCH,			ACT_HL2MP_IDLE_CROUCH_RPG,				false },
	{ ACT_HL2MP_WALK_CROUCH,			ACT_HL2MP_WALK_CROUCH_RPG,				false },
	{ ACT_HL2MP_GESTURE_RANGE_ATTACK,	ACT_HL2MP_GESTURE_RANGE_ATTACK_RPG,		false },
	{ ACT_HL2MP_GESTURE_RELOAD,			ACT_HL2MP_GESTURE_RELOAD_RPG,			false },
	{ ACT_HL2MP_JUMP,					ACT_HL2MP_JUMP_RPG,						false },
	{ ACT_RANGE_ATTACK1,				ACT_RANGE_ATTACK_RPG,					false },

weapon_shotgun.cpp

There is already an activity list. At it's foot, after { ACT_GESTURE_RELOAD, ACT_GESTURE_RELOAD_SHOTGUN, false }, add the following list:

	{ ACT_HL2MP_IDLE,					ACT_HL2MP_IDLE_SHOTGUN,						false },
	{ ACT_HL2MP_RUN,					ACT_HL2MP_RUN_SHOTGUN,						false },
	{ ACT_HL2MP_IDLE_CROUCH,			ACT_HL2MP_IDLE_CROUCH_SHOTGUN,				false },
	{ ACT_HL2MP_WALK_CROUCH,			ACT_HL2MP_WALK_CROUCH_SHOTGUN,				false },
	{ ACT_HL2MP_GESTURE_RANGE_ATTACK,	ACT_HL2MP_GESTURE_RANGE_ATTACK_SHOTGUN,		false },
	{ ACT_HL2MP_GESTURE_RELOAD,			ACT_HL2MP_GESTURE_RELOAD_SHOTGUN,			false },
	{ ACT_HL2MP_JUMP,					ACT_HL2MP_JUMP_SHOTGUN,						false },
	{ ACT_RANGE_ATTACK1,				ACT_RANGE_ATTACK_SHOTGUN,					false },

weapon_smg1.cpp

There is already an activity list. At it's foot, after { ACT_GESTURE_RELOAD, ACT_GESTURE_RELOAD_SMG1, true }, add the following list:

	{ ACT_HL2MP_IDLE,					ACT_HL2MP_IDLE_SMG1,					false },
	{ ACT_HL2MP_RUN,					ACT_HL2MP_RUN_SMG1,						false },
	{ ACT_HL2MP_IDLE_CROUCH,			ACT_HL2MP_IDLE_CROUCH_SMG1,				false },
	{ ACT_HL2MP_WALK_CROUCH,			ACT_HL2MP_WALK_CROUCH_SMG1,				false },
	{ ACT_HL2MP_GESTURE_RANGE_ATTACK,	ACT_HL2MP_GESTURE_RANGE_ATTACK_SMG1,	false },
	{ ACT_HL2MP_GESTURE_RELOAD,			ACT_GESTURE_RELOAD_SMG1,				false },
	{ ACT_HL2MP_JUMP,					ACT_HL2MP_JUMP_SMG1,					false },
	{ ACT_RANGE_ATTACK1,				ACT_RANGE_ATTACK_SMG1,					false },

Creating the model

If you do create a custom model for this, look at the original player.mdl SMD files found in the provided archive for reference.

When creating the actual model make it look like the one in gordon_body_ref.smd or like in the picture here (REMOVED).

Finished product

View the finished product here on YouTube (REMOVED).

Acknowledgments

  • Singleplayer animation code - Malortie
  • Weapon activity tables - Malortie
  • Other stuff - Zombie Killa
  • Gordon Freeman - VALVe and DPFilms