Legs in Firstperson

From Valve Developer Community
Jump to: navigation, search
Русский

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 ) It will not work in a multiplayer mod because it replaces the actual thirdperson model to a new model without arms or head

NOTE: This currently does not work in Source SDK 2013

Requirements

  • Valve's Source Engine code ( 2007 ) Single Player.
  • A 3D Modeling Application ( XSI, 3DS Max, Blender etc )
  • First Person Animation Model Pack ( Available Here ) <-- FIXED MISSING TEXTURES
  • A Brain

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 shared folder of your mod.

The first one here is "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


Once you have finished this add the second file "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 backpeddling
// 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;
    }

    // calc side to side turning
    flYaw = ang - m_flGaitYaw;
    // Invert for mapping into 8way blend
    flYaw = -flYaw;
    flYaw = flYaw - (int)(flYaw / 360) * 360;

    if (flYaw < -180)
    {
        flYaw = flYaw + 360;
    }
    else if (flYaw > 180)
    {
        flYaw = flYaw - 360;
    }
   
    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;

        float yawmagnitude = fabs( yawdelta );

        // If too far, then need to turn in place
        if ( yawmagnitude > 45 )
        {
            rotated_too_far = true;
        }

        // Standing still for a while, rotate feet around to face forward
        // Or rotated too far
        // FIXME:  Play an in place turning animation
        if ( rotated_too_far ||
            ( gpGlobals->curtime > m_flLastTurnTime + mp_facefronttime.GetFloat() ) )
        {
            m_flGoalFeetYaw        = 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

In "c_baseplayer.cpp" search for the function:

bool IsInFreezeCam( void )

and add under it:

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


Next, in the same file, search for

bool C_BasePlayer::ShouldDraw()

and make it look like this:

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

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 function
int C_BasePlayer::DrawModel( int flags )
of the same file 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 function
void C_BasePlayer::AddEntity( void )
and under
// Add in lighting effects
CreateLightEffects();

add

SetLocalAnglesDim(X_INDEX, 0 );


Now, in "c_baseplayer.h", search for

bool IsInFreezeCam( void );

and add under it:

bool IsInEye( void );


In the same file, search for

virtual int DrawModel( int flags );

and add:

const Vector& C_BasePlayer::GetRenderOrigin();


Next, open up "hl2_player.cpp" and 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

	
CHL2_Player::~CHL2_Player( void )
{
	// FPLEGS - Destructor defualt empty! Added code below!
	// 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()
{
	// FPLEGS
	m_pPlayerAnimState = CreatePlayerAnimationState(this);
	m_angEyeAngles.Init();
	// FPLEGS END

	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:

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

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

	// FPLEGS
	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;
 
    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 wacked
        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.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 );
}


Next, go to "hl2_player.h" and add
#include "singleplayer_animstate.h"

to the top of the file.


Now, go to the end of "public:" in the class
class CHL2_Player : public
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;

Weapon activity list tables

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


In 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 },

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


In 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 },

In 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,

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


In 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 },

In 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 },


In 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 },

In 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, download the original player.mdl SMD files for reference here.

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


Finished Product

View the finished product here on youtube.

Acknowledgments

  • Singleplayer Animation Code - Malortie
  • Weapon Activity Tables - Malortie
  • Other Stuff - Zombie Killa
  • Gordon Freeman - VALVe and DPFilms