mirror of
https://github.com/alliedmodders/hl2sdk.git
synced 2026-10-12 01:08:00 +08:00
1230 lines
32 KiB
C++
1230 lines
32 KiB
C++
//========= Copyright Valve Corporation, All rights reserved. ============//
|
|
//
|
|
// Purpose: Player for HL2.
|
|
//
|
|
//=============================================================================//
|
|
|
|
#include "cbase.h"
|
|
#include "vcollide_parse.h"
|
|
#include "c_hl2mp_player.h"
|
|
#include "view.h"
|
|
#include "takedamageinfo.h"
|
|
#include "hl2mp_gamerules.h"
|
|
#include "in_buttons.h"
|
|
#include "iviewrender_beams.h" // flashlight beam
|
|
#include "r_efx.h"
|
|
#include "dlight.h"
|
|
|
|
// Don't alias here
|
|
#if defined( CHL2MP_Player )
|
|
#undef CHL2MP_Player
|
|
#endif
|
|
|
|
// misyl: Can be set to Msg if you want some info for debugging prediction
|
|
#define MsgPredTest(...)
|
|
#define MsgPredTest2(...)
|
|
|
|
ConVar sv_infinite_aux_power( "sv_infinite_aux_power", "0", FCVAR_CHEAT | FCVAR_REPLICATED );
|
|
|
|
LINK_ENTITY_TO_CLASS( player, C_HL2MP_Player );
|
|
|
|
// specific to the local player
|
|
BEGIN_RECV_TABLE_NOBASE( C_HL2MP_Player, DT_HL2MPLocalPlayerExclusive )
|
|
RecvPropVectorXY( RECVINFO_NAME( m_vecNetworkOrigin, m_vecOrigin ) ),
|
|
RecvPropFloat( RECVINFO_NAME( m_vecNetworkOrigin[2], m_vecOrigin[2] ) ),
|
|
|
|
RecvPropFloat( RECVINFO( m_angEyeAngles[0] ) ),
|
|
RecvPropFloat( RECVINFO( m_angEyeAngles[1] ) ),
|
|
END_RECV_TABLE()
|
|
|
|
// all players except the local player
|
|
BEGIN_RECV_TABLE_NOBASE( C_HL2MP_Player, DT_HL2MPNonLocalPlayerExclusive )
|
|
RecvPropVectorXY( RECVINFO_NAME( m_vecNetworkOrigin, m_vecOrigin ) ),
|
|
RecvPropFloat( RECVINFO_NAME( m_vecNetworkOrigin[2], m_vecOrigin[2] ) ),
|
|
|
|
RecvPropFloat( RECVINFO( m_angEyeAngles[0] ) ),
|
|
RecvPropFloat( RECVINFO( m_angEyeAngles[1] ) ),
|
|
END_RECV_TABLE()
|
|
|
|
IMPLEMENT_CLIENTCLASS_DT(C_HL2MP_Player, DT_HL2MP_Player, CHL2MP_Player)
|
|
RecvPropDataTable( "hl2mplocaldata", 0, 0, &REFERENCE_RECV_TABLE( DT_HL2MPLocalPlayerExclusive ) ),
|
|
RecvPropDataTable( "hl2mpnonlocaldata", 0, 0, &REFERENCE_RECV_TABLE( DT_HL2MPNonLocalPlayerExclusive ) ),
|
|
|
|
RecvPropEHandle( RECVINFO( m_hRagdoll ) ),
|
|
RecvPropInt( RECVINFO( m_iSpawnInterpCounter ) ),
|
|
RecvPropInt( RECVINFO( m_iPlayerSoundType) ),
|
|
|
|
RecvPropBool( RECVINFO( m_fIsWalking ) ),
|
|
END_RECV_TABLE()
|
|
|
|
BEGIN_PREDICTION_DATA( C_HL2MP_Player )
|
|
DEFINE_PRED_FIELD( m_fIsWalking, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
|
|
|
|
// misyl: Ammo is server side entities in HL2MP. Not catastrophic to error about.
|
|
// Just let the server stomp all over us.
|
|
//
|
|
// There is 1 instance in which is can be a runaway pred error, and that is if you have eg. ar2
|
|
// with just altfire ammo, and get new ammo and we force reload. But the additional pred error sorts that out itself
|
|
// without this for every pickup which is 1000% more common.
|
|
DEFINE_PRED_ARRAY( m_iAmmo, FIELD_INTEGER, MAX_AMMO_TYPES, FTYPEDESC_INSENDTABLE | FTYPEDESC_OVERRIDE | FTYPEDESC_NOERRORCHECK ),
|
|
END_PREDICTION_DATA()
|
|
|
|
ConVar hl2_walkspeed( "hl2_walkspeed", "150", FCVAR_REPLICATED );
|
|
ConVar hl2_normspeed( "hl2_normspeed", "190", FCVAR_REPLICATED );
|
|
ConVar hl2_sprintspeed( "hl2_sprintspeed", "320", FCVAR_REPLICATED );
|
|
|
|
#define HL2_WALK_SPEED hl2_walkspeed.GetFloat()
|
|
#define HL2_NORM_SPEED hl2_normspeed.GetFloat()
|
|
#define HL2_SPRINT_SPEED hl2_sprintspeed.GetFloat()
|
|
|
|
static ConVar cl_playermodel( "cl_playermodel", "none", FCVAR_USERINFO | FCVAR_ARCHIVE | FCVAR_SERVER_CAN_EXECUTE, "Default Player Model");
|
|
static ConVar cl_defaultweapon( "cl_defaultweapon", "weapon_physcannon", FCVAR_USERINFO | FCVAR_ARCHIVE, "Default Spawn Weapon");
|
|
|
|
void SpawnBlood (Vector vecSpot, const Vector &vecDir, int bloodColor, float flDamage);
|
|
|
|
//
|
|
// SUIT POWER DEVICES
|
|
//
|
|
#define SUITPOWER_CHARGE_RATE 12.5 // 100 units in 8 seconds
|
|
|
|
#ifdef HL2MP
|
|
CSuitPowerDevice SuitDeviceSprint( bits_SUIT_DEVICE_SPRINT, 25.0f ); // 100 units in 4 seconds
|
|
#else
|
|
CSuitPowerDevice SuitDeviceSprint( bits_SUIT_DEVICE_SPRINT, 12.5f ); // 100 units in 8 seconds
|
|
#endif
|
|
|
|
#ifdef HL2_EPISODIC
|
|
CSuitPowerDevice SuitDeviceFlashlight( bits_SUIT_DEVICE_FLASHLIGHT, 1.111 ); // 100 units in 90 second
|
|
#else
|
|
CSuitPowerDevice SuitDeviceFlashlight( bits_SUIT_DEVICE_FLASHLIGHT, 2.222 ); // 100 units in 45 second
|
|
#endif
|
|
CSuitPowerDevice SuitDeviceBreather( bits_SUIT_DEVICE_BREATHER, 6.7f ); // 100 units in 15 seconds (plus three padded seconds)
|
|
|
|
C_HL2MP_Player::C_HL2MP_Player() : m_PlayerAnimState( this ), m_iv_angEyeAngles( "C_HL2MP_Player::m_iv_angEyeAngles" )
|
|
{
|
|
m_iIDEntIndex = 0;
|
|
m_iSpawnInterpCounterCache = 0;
|
|
|
|
m_angEyeAngles.Init();
|
|
|
|
AddVar( &m_angEyeAngles, &m_iv_angEyeAngles, LATCH_SIMULATION_VAR );
|
|
|
|
m_EntClientFlags |= ENTCLIENTFLAG_DONTUSEIK;
|
|
m_blinkTimer.Invalidate();
|
|
|
|
m_pFlashlightBeam = NULL;
|
|
|
|
SuitPower_Initialize();
|
|
}
|
|
|
|
C_HL2MP_Player::~C_HL2MP_Player( void )
|
|
{
|
|
ReleaseFlashlight();
|
|
}
|
|
|
|
int C_HL2MP_Player::GetIDTarget() const
|
|
{
|
|
return m_iIDEntIndex;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Update this client's target entity
|
|
//-----------------------------------------------------------------------------
|
|
void C_HL2MP_Player::UpdateIDTarget()
|
|
{
|
|
if ( !IsLocalPlayer() )
|
|
return;
|
|
|
|
// Clear old target and find a new one
|
|
m_iIDEntIndex = 0;
|
|
|
|
// don't show IDs in chase spec mode
|
|
if ( GetObserverMode() == OBS_MODE_CHASE ||
|
|
GetObserverMode() == OBS_MODE_DEATHCAM )
|
|
return;
|
|
|
|
trace_t tr;
|
|
Vector vecStart, vecEnd;
|
|
VectorMA( MainViewOrigin(), 1500, MainViewForward(), vecEnd );
|
|
VectorMA( MainViewOrigin(), 10, MainViewForward(), vecStart );
|
|
UTIL_TraceLine( vecStart, vecEnd, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr );
|
|
|
|
if ( !tr.startsolid && tr.DidHitNonWorldEntity() )
|
|
{
|
|
C_BaseEntity *pEntity = tr.m_pEnt;
|
|
|
|
if ( pEntity && (pEntity != this) )
|
|
{
|
|
m_iIDEntIndex = pEntity->entindex();
|
|
}
|
|
}
|
|
}
|
|
|
|
void C_HL2MP_Player::TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator )
|
|
{
|
|
Vector vecOrigin = ptr->endpos - vecDir * 4;
|
|
|
|
float flDistance = 0.0f;
|
|
|
|
if ( info.GetAttacker() )
|
|
{
|
|
flDistance = (ptr->endpos - info.GetAttacker()->GetAbsOrigin()).Length();
|
|
}
|
|
|
|
if ( m_takedamage )
|
|
{
|
|
AddMultiDamage( info, this );
|
|
|
|
int blood = BloodColor();
|
|
|
|
CBaseEntity *pAttacker = info.GetAttacker();
|
|
|
|
if ( pAttacker )
|
|
{
|
|
if ( HL2MPRules()->IsTeamplay() && pAttacker->InSameTeam( this ) == true )
|
|
return;
|
|
}
|
|
|
|
if ( blood != DONT_BLEED )
|
|
{
|
|
SpawnBlood( vecOrigin, vecDir, blood, flDistance );// a little surface blood.
|
|
TraceBleed( flDistance, vecDir, ptr, info.GetDamageType() );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
C_HL2MP_Player* C_HL2MP_Player::GetLocalHL2MPPlayer()
|
|
{
|
|
return (C_HL2MP_Player*)C_BasePlayer::GetLocalPlayer();
|
|
}
|
|
|
|
void C_HL2MP_Player::Initialize( void )
|
|
{
|
|
m_headYawPoseParam = LookupPoseParameter( "head_yaw" );
|
|
GetPoseParameterRange( m_headYawPoseParam, m_headYawMin, m_headYawMax );
|
|
|
|
m_headPitchPoseParam = LookupPoseParameter( "head_pitch" );
|
|
GetPoseParameterRange( m_headPitchPoseParam, m_headPitchMin, m_headPitchMax );
|
|
|
|
CStudioHdr *hdr = GetModelPtr();
|
|
for ( int i = 0; i < hdr->GetNumPoseParameters() ; i++ )
|
|
{
|
|
SetPoseParameter( hdr, i, 0.0 );
|
|
}
|
|
}
|
|
|
|
CStudioHdr *C_HL2MP_Player::OnNewModel( void )
|
|
{
|
|
CStudioHdr *hdr = BaseClass::OnNewModel();
|
|
|
|
Initialize( );
|
|
|
|
return hdr;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
/**
|
|
* Orient head and eyes towards m_lookAt.
|
|
*/
|
|
void C_HL2MP_Player::UpdateLookAt( void )
|
|
{
|
|
// head yaw
|
|
if (m_headYawPoseParam < 0 || m_headPitchPoseParam < 0)
|
|
return;
|
|
|
|
// orient eyes
|
|
m_viewtarget = m_vLookAtTarget;
|
|
|
|
// blinking
|
|
if (m_blinkTimer.IsElapsed())
|
|
{
|
|
m_blinktoggle = !m_blinktoggle;
|
|
m_blinkTimer.Start( RandomFloat( 1.5f, 4.0f ) );
|
|
}
|
|
|
|
// Figure out where we want to look in world space.
|
|
QAngle desiredAngles;
|
|
Vector to = m_vLookAtTarget - EyePosition();
|
|
VectorAngles( to, desiredAngles );
|
|
|
|
// Figure out where our body is facing in world space.
|
|
QAngle bodyAngles( 0, 0, 0 );
|
|
bodyAngles[YAW] = GetLocalAngles()[YAW];
|
|
|
|
|
|
float flBodyYawDiff = bodyAngles[YAW] - m_flLastBodyYaw;
|
|
m_flLastBodyYaw = bodyAngles[YAW];
|
|
|
|
|
|
// Set the head's yaw.
|
|
float desired = AngleNormalize( desiredAngles[YAW] - bodyAngles[YAW] );
|
|
desired = clamp( desired, m_headYawMin, m_headYawMax );
|
|
m_flCurrentHeadYaw = ApproachAngle( desired, m_flCurrentHeadYaw, 130 * gpGlobals->frametime );
|
|
|
|
// Counterrotate the head from the body rotation so it doesn't rotate past its target.
|
|
m_flCurrentHeadYaw = AngleNormalize( m_flCurrentHeadYaw - flBodyYawDiff );
|
|
desired = clamp( desired, m_headYawMin, m_headYawMax );
|
|
|
|
SetPoseParameter( m_headYawPoseParam, m_flCurrentHeadYaw );
|
|
|
|
|
|
// Set the head's yaw.
|
|
desired = AngleNormalize( desiredAngles[PITCH] );
|
|
desired = clamp( desired, m_headPitchMin, m_headPitchMax );
|
|
|
|
m_flCurrentHeadPitch = ApproachAngle( desired, m_flCurrentHeadPitch, 130 * gpGlobals->frametime );
|
|
m_flCurrentHeadPitch = AngleNormalize( m_flCurrentHeadPitch );
|
|
SetPoseParameter( m_headPitchPoseParam, m_flCurrentHeadPitch );
|
|
}
|
|
|
|
void C_HL2MP_Player::ClientThink( void )
|
|
{
|
|
bool bFoundViewTarget = false;
|
|
|
|
Vector vForward;
|
|
AngleVectors( GetLocalAngles(), &vForward );
|
|
|
|
for( int iClient = 1; iClient <= gpGlobals->maxClients; ++iClient )
|
|
{
|
|
CBaseEntity *pEnt = UTIL_PlayerByIndex( iClient );
|
|
if(!pEnt || !pEnt->IsPlayer())
|
|
continue;
|
|
|
|
if ( pEnt->entindex() == entindex() )
|
|
continue;
|
|
|
|
Vector vTargetOrigin = pEnt->GetAbsOrigin();
|
|
Vector vMyOrigin = GetAbsOrigin();
|
|
|
|
Vector vDir = vTargetOrigin - vMyOrigin;
|
|
|
|
if ( vDir.Length() > 128 )
|
|
continue;
|
|
|
|
VectorNormalize( vDir );
|
|
|
|
if ( DotProduct( vForward, vDir ) < 0.0f )
|
|
continue;
|
|
|
|
m_vLookAtTarget = pEnt->EyePosition();
|
|
bFoundViewTarget = true;
|
|
break;
|
|
}
|
|
|
|
if ( bFoundViewTarget == false )
|
|
{
|
|
m_vLookAtTarget = GetAbsOrigin() + vForward * 512;
|
|
}
|
|
|
|
UpdateIDTarget();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
int C_HL2MP_Player::DrawModel( int flags )
|
|
{
|
|
if ( !m_bReadyToDraw )
|
|
return 0;
|
|
|
|
return BaseClass::DrawModel(flags);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Should this object receive shadows?
|
|
//-----------------------------------------------------------------------------
|
|
bool C_HL2MP_Player::ShouldReceiveProjectedTextures( int flags )
|
|
{
|
|
Assert( flags & SHADOW_FLAGS_PROJECTED_TEXTURE_TYPE_MASK );
|
|
|
|
if ( IsEffectActive( EF_NODRAW ) )
|
|
return false;
|
|
|
|
if( flags & SHADOW_FLAGS_FLASHLIGHT )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return BaseClass::ShouldReceiveProjectedTextures( flags );
|
|
}
|
|
|
|
void C_HL2MP_Player::DoImpactEffect( trace_t &tr, int nDamageType )
|
|
{
|
|
if ( GetActiveWeapon() )
|
|
{
|
|
GetActiveWeapon()->DoImpactEffect( tr, nDamageType );
|
|
return;
|
|
}
|
|
|
|
BaseClass::DoImpactEffect( tr, nDamageType );
|
|
}
|
|
|
|
void C_HL2MP_Player::PreThink( void )
|
|
{
|
|
QAngle vTempAngles = GetLocalAngles();
|
|
|
|
if ( GetLocalPlayer() == this )
|
|
{
|
|
vTempAngles[PITCH] = EyeAngles()[PITCH];
|
|
}
|
|
else
|
|
{
|
|
vTempAngles[PITCH] = m_angEyeAngles[PITCH];
|
|
}
|
|
|
|
if ( vTempAngles[YAW] < 0.0f )
|
|
{
|
|
vTempAngles[YAW] += 360.0f;
|
|
}
|
|
|
|
SetLocalAngles( vTempAngles );
|
|
|
|
BaseClass::PreThink();
|
|
}
|
|
|
|
const QAngle &C_HL2MP_Player::EyeAngles()
|
|
{
|
|
if( IsLocalPlayer() )
|
|
{
|
|
return BaseClass::EyeAngles();
|
|
}
|
|
else
|
|
{
|
|
return m_angEyeAngles;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Charge battery fully, turn off all devices.
|
|
//-----------------------------------------------------------------------------
|
|
void C_HL2MP_Player::SuitPower_Initialize( void )
|
|
{
|
|
m_HL2Local.m_bitsActiveDevices = 0x00000000;
|
|
m_HL2Local.m_flSuitPower = 100.0;
|
|
m_HL2Local.m_flSuitPowerLoad = 0.0;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Interface to drain power from the suit's power supply.
|
|
// Input: Amount of charge to remove (expressed as percentage of full charge)
|
|
// Output: Returns TRUE if successful, FALSE if not enough power available.
|
|
//-----------------------------------------------------------------------------
|
|
bool C_HL2MP_Player::SuitPower_Drain( float flPower )
|
|
{
|
|
// Suitpower cheat on?
|
|
if ( sv_infinite_aux_power.GetBool() )
|
|
return true;
|
|
|
|
m_HL2Local.m_flSuitPower -= flPower;
|
|
|
|
if ( m_HL2Local.m_flSuitPower < 0.01 )
|
|
{
|
|
// Power is depleted!
|
|
// Clamp and fail
|
|
m_HL2Local.m_flSuitPower = 0.0;
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Interface to add power to the suit's power supply
|
|
// Input: Amount of charge to add
|
|
//-----------------------------------------------------------------------------
|
|
void C_HL2MP_Player::SuitPower_Charge( float flPower )
|
|
{
|
|
m_HL2Local.m_flSuitPower += flPower;
|
|
|
|
if( m_HL2Local.m_flSuitPower > 100.0 )
|
|
{
|
|
// Full charge, clamp.
|
|
m_HL2Local.m_flSuitPower = 100.0;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//-----------------------------------------------------------------------------
|
|
bool C_HL2MP_Player::SuitPower_IsDeviceActive( const CSuitPowerDevice &device )
|
|
{
|
|
return (m_HL2Local.m_bitsActiveDevices & device.GetDeviceID()) != 0;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//-----------------------------------------------------------------------------
|
|
bool C_HL2MP_Player::SuitPower_AddDevice( const CSuitPowerDevice &device )
|
|
{
|
|
// Make sure this device is NOT active!!
|
|
if( m_HL2Local.m_bitsActiveDevices & device.GetDeviceID() )
|
|
return false;
|
|
|
|
if( !IsSuitEquipped() )
|
|
return false;
|
|
|
|
m_HL2Local.m_bitsActiveDevices |= device.GetDeviceID();
|
|
m_HL2Local.m_flSuitPowerLoad += device.GetDeviceDrainRate();
|
|
return true;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//-----------------------------------------------------------------------------
|
|
bool C_HL2MP_Player::SuitPower_RemoveDevice( const CSuitPowerDevice &device )
|
|
{
|
|
// Make sure this device is active!!
|
|
if( ! (m_HL2Local.m_bitsActiveDevices & device.GetDeviceID()) )
|
|
return false;
|
|
|
|
if( !IsSuitEquipped() )
|
|
return false;
|
|
|
|
// Take a little bit of suit power when you disable a device. If the device is shutting off
|
|
// because the battery is drained, no harm done, the battery charge cannot go below 0.
|
|
// This code in combination with the delay before the suit can start recharging are a defense
|
|
// against exploits where the player could rapidly tap sprint and never run out of power.
|
|
MsgPredTest2( "[Client %d] [A REMOVE] m_HL2Local.m_flSuitPower: %f\n", gpGlobals->tickcount, m_HL2Local.m_flSuitPower );
|
|
SuitPower_Drain( device.GetDeviceDrainRate() * 0.1f );
|
|
MsgPredTest2( "[Client %d] [B REMOVE] m_HL2Local.m_flSuitPower: %f\n", gpGlobals->tickcount, m_HL2Local.m_flSuitPower );
|
|
|
|
m_HL2Local.m_bitsActiveDevices &= ~device.GetDeviceID();
|
|
m_HL2Local.m_flSuitPowerLoad -= device.GetDeviceDrainRate();
|
|
|
|
if( m_HL2Local.m_bitsActiveDevices == 0x00000000 )
|
|
{
|
|
// With this device turned off, we can set this timer which tells us when the
|
|
// suit power system entered a no-load state.
|
|
m_HL2Local.m_flTimeAllSuitDevicesOff = gpGlobals->curtime;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//-----------------------------------------------------------------------------
|
|
#define SUITPOWER_BEGIN_RECHARGE_DELAY 0.5f
|
|
bool C_HL2MP_Player::SuitPower_ShouldRecharge( void )
|
|
{
|
|
// Make sure all devices are off.
|
|
if( m_HL2Local.m_bitsActiveDevices != 0x00000000 )
|
|
return false;
|
|
|
|
// Is the system fully charged?
|
|
if( m_HL2Local.m_flSuitPower >= 100.0f )
|
|
return false;
|
|
|
|
// Has the system been in a no-load state for long enough
|
|
// to begin recharging?
|
|
if( gpGlobals->curtime < m_HL2Local.m_flTimeAllSuitDevicesOff + SUITPOWER_BEGIN_RECHARGE_DELAY )
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
void C_HL2MP_Player::SuitPower_Update( void )
|
|
{
|
|
if( SuitPower_ShouldRecharge() )
|
|
{
|
|
SuitPower_Charge( SUITPOWER_CHARGE_RATE * gpGlobals->frametime );
|
|
}
|
|
else if( m_HL2Local.m_bitsActiveDevices )
|
|
{
|
|
float flPowerLoad = m_HL2Local.m_flSuitPowerLoad;
|
|
|
|
//Since stickysprint quickly shuts off sprint if it isn't being used, this isn't an issue.
|
|
// misyl: no sticky sprint for hl2mp.
|
|
//if ( !sv_stickysprint.GetBool() )
|
|
{
|
|
if( SuitPower_IsDeviceActive(SuitDeviceSprint) )
|
|
{
|
|
if( CloseEnough(fabs(GetAbsVelocity().x), 0.0f) && CloseEnough(fabs(GetAbsVelocity().y), 0.0f) )
|
|
{
|
|
if ( CloseEnough( m_HL2Local.m_flSuitPowerLoad, SuitDeviceSprint.GetDeviceDrainRate() ) )
|
|
{
|
|
flPowerLoad = 0.0f;
|
|
}
|
|
else
|
|
{
|
|
// If player's not moving, don't drain sprint juice.
|
|
flPowerLoad -= SuitDeviceSprint.GetDeviceDrainRate();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if( SuitPower_IsDeviceActive(SuitDeviceFlashlight) )
|
|
{
|
|
//float factor;
|
|
|
|
//factor = 1.0f / m_flFlashlightPowerDrainScale;
|
|
|
|
float factor = 1.0f;
|
|
|
|
flPowerLoad -= ( SuitDeviceFlashlight.GetDeviceDrainRate() * (1.0f - factor) );
|
|
}
|
|
|
|
SuitPower_Drain( flPowerLoad * gpGlobals->frametime );
|
|
|
|
}
|
|
MsgPredTest2( "[Client %d] m_HL2Local.m_flSuitPower: %f m_fIsSprinting: %d\n", gpGlobals->tickcount, m_HL2Local.m_flSuitPower, m_fIsSprinting ? 1 : 0 );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void C_HL2MP_Player::AddEntity( void )
|
|
{
|
|
BaseClass::AddEntity();
|
|
|
|
QAngle vTempAngles = GetLocalAngles();
|
|
vTempAngles[PITCH] = m_angEyeAngles[PITCH];
|
|
|
|
SetLocalAngles( vTempAngles );
|
|
|
|
m_PlayerAnimState.Update();
|
|
|
|
// Zero out model pitch, blending takes care of all of it.
|
|
SetLocalAnglesDim( X_INDEX, 0 );
|
|
|
|
if( this != C_BasePlayer::GetLocalPlayer() )
|
|
{
|
|
if ( IsEffectActive( EF_DIMLIGHT ) )
|
|
{
|
|
int iAttachment = LookupAttachment( "anim_attachment_RH" );
|
|
|
|
if ( iAttachment < 0 )
|
|
return;
|
|
|
|
Vector vecOrigin;
|
|
QAngle eyeAngles = m_angEyeAngles;
|
|
|
|
GetAttachment( iAttachment, vecOrigin, eyeAngles );
|
|
|
|
Vector vForward;
|
|
AngleVectors( eyeAngles, &vForward );
|
|
|
|
trace_t tr;
|
|
UTIL_TraceLine( vecOrigin, vecOrigin + (vForward * 200), MASK_SHOT, this, COLLISION_GROUP_NONE, &tr );
|
|
|
|
if( !m_pFlashlightBeam )
|
|
{
|
|
BeamInfo_t beamInfo;
|
|
beamInfo.m_nType = TE_BEAMPOINTS;
|
|
beamInfo.m_vecStart = tr.startpos;
|
|
beamInfo.m_vecEnd = tr.endpos;
|
|
beamInfo.m_pszModelName = "sprites/glow01.vmt";
|
|
beamInfo.m_pszHaloName = "sprites/glow01.vmt";
|
|
beamInfo.m_flHaloScale = 3.0;
|
|
beamInfo.m_flWidth = 8.0f;
|
|
beamInfo.m_flEndWidth = 35.0f;
|
|
beamInfo.m_flFadeLength = 300.0f;
|
|
beamInfo.m_flAmplitude = 0;
|
|
beamInfo.m_flBrightness = 60.0;
|
|
beamInfo.m_flSpeed = 0.0f;
|
|
beamInfo.m_nStartFrame = 0.0;
|
|
beamInfo.m_flFrameRate = 0.0;
|
|
beamInfo.m_flRed = 255.0;
|
|
beamInfo.m_flGreen = 255.0;
|
|
beamInfo.m_flBlue = 255.0;
|
|
beamInfo.m_nSegments = 8;
|
|
beamInfo.m_bRenderable = true;
|
|
beamInfo.m_flLife = 0.5;
|
|
beamInfo.m_nFlags = FBEAM_FOREVER | FBEAM_ONLYNOISEONCE | FBEAM_NOTILE | FBEAM_HALOBEAM;
|
|
|
|
m_pFlashlightBeam = beams->CreateBeamPoints( beamInfo );
|
|
}
|
|
|
|
if( m_pFlashlightBeam )
|
|
{
|
|
BeamInfo_t beamInfo;
|
|
beamInfo.m_vecStart = tr.startpos;
|
|
beamInfo.m_vecEnd = tr.endpos;
|
|
beamInfo.m_flRed = 255.0;
|
|
beamInfo.m_flGreen = 255.0;
|
|
beamInfo.m_flBlue = 255.0;
|
|
|
|
beams->UpdateBeamInfo( m_pFlashlightBeam, beamInfo );
|
|
|
|
dlight_t *el = effects->CL_AllocDlight( 0 );
|
|
el->origin = tr.endpos;
|
|
el->radius = 50;
|
|
el->color.r = 200;
|
|
el->color.g = 200;
|
|
el->color.b = 200;
|
|
el->die = gpGlobals->curtime + 0.1;
|
|
}
|
|
}
|
|
else if ( m_pFlashlightBeam )
|
|
{
|
|
ReleaseFlashlight();
|
|
}
|
|
}
|
|
}
|
|
|
|
ShadowType_t C_HL2MP_Player::ShadowCastType( void )
|
|
{
|
|
if ( !IsVisible() )
|
|
return SHADOWS_NONE;
|
|
|
|
return SHADOWS_RENDER_TO_TEXTURE_DYNAMIC;
|
|
}
|
|
|
|
|
|
const QAngle& C_HL2MP_Player::GetRenderAngles()
|
|
{
|
|
if ( IsRagdoll() )
|
|
{
|
|
return vec3_angle;
|
|
}
|
|
else
|
|
{
|
|
return m_PlayerAnimState.GetRenderAngles();
|
|
}
|
|
}
|
|
|
|
bool C_HL2MP_Player::ShouldDraw( void )
|
|
{
|
|
// If we're dead, our ragdoll will be drawn for us instead.
|
|
if ( !IsAlive() )
|
|
return false;
|
|
|
|
// if( GetTeamNumber() == TEAM_SPECTATOR )
|
|
// return false;
|
|
|
|
if( IsLocalPlayer() && IsRagdoll() )
|
|
return true;
|
|
|
|
if ( IsRagdoll() )
|
|
return false;
|
|
|
|
return BaseClass::ShouldDraw();
|
|
}
|
|
|
|
void C_HL2MP_Player::NotifyShouldTransmit( ShouldTransmitState_t state )
|
|
{
|
|
if ( state == SHOULDTRANSMIT_END )
|
|
{
|
|
if( m_pFlashlightBeam != NULL )
|
|
{
|
|
ReleaseFlashlight();
|
|
}
|
|
}
|
|
|
|
BaseClass::NotifyShouldTransmit( state );
|
|
}
|
|
|
|
void C_HL2MP_Player::OnDataChanged( DataUpdateType_t type )
|
|
{
|
|
BaseClass::OnDataChanged( type );
|
|
|
|
if ( type == DATA_UPDATE_CREATED )
|
|
{
|
|
SetNextClientThink( CLIENT_THINK_ALWAYS );
|
|
}
|
|
|
|
UpdateVisibility();
|
|
}
|
|
|
|
void C_HL2MP_Player::PostDataUpdate( DataUpdateType_t updateType )
|
|
{
|
|
if ( m_iSpawnInterpCounter != m_iSpawnInterpCounterCache )
|
|
{
|
|
MoveToLastReceivedPosition( true );
|
|
ResetLatched();
|
|
m_iSpawnInterpCounterCache = m_iSpawnInterpCounter;
|
|
}
|
|
|
|
BaseClass::PostDataUpdate( updateType );
|
|
}
|
|
|
|
void C_HL2MP_Player::ReleaseFlashlight( void )
|
|
{
|
|
if( m_pFlashlightBeam )
|
|
{
|
|
m_pFlashlightBeam->flags = 0;
|
|
m_pFlashlightBeam->die = gpGlobals->curtime - 1;
|
|
|
|
m_pFlashlightBeam = NULL;
|
|
}
|
|
}
|
|
|
|
float C_HL2MP_Player::GetFOV( void )
|
|
{
|
|
//Find our FOV with offset zoom value
|
|
float flFOVOffset = C_BasePlayer::GetFOV() + GetZoom();
|
|
|
|
// Clamp FOV in MP
|
|
int min_fov = GetMinFOV();
|
|
|
|
// Don't let it go too low
|
|
flFOVOffset = MAX( min_fov, flFOVOffset );
|
|
|
|
return flFOVOffset;
|
|
}
|
|
|
|
//=========================================================
|
|
// Autoaim
|
|
// set crosshair position to point to enemey
|
|
//=========================================================
|
|
Vector C_HL2MP_Player::GetAutoaimVector( float flDelta )
|
|
{
|
|
// Never autoaim a predicted weapon (for now)
|
|
Vector forward;
|
|
AngleVectors( EyeAngles() + m_Local.m_vecPunchAngle, &forward );
|
|
return forward;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Returns whether or not we are allowed to sprint now.
|
|
//-----------------------------------------------------------------------------
|
|
bool C_HL2MP_Player::CanSprint( void )
|
|
{
|
|
return ( (!m_Local.m_bDucked && !m_Local.m_bDucking) && (GetWaterLevel() != 3) );
|
|
}
|
|
|
|
extern ConVar sv_maxspeed;
|
|
|
|
void C_HL2MP_Player::HandleSpeedChanges( CMoveData *mv )
|
|
{
|
|
int nChangedButtons = mv->m_nButtons ^ mv->m_nOldButtons;
|
|
|
|
bool bJustPressedSpeed = !!( nChangedButtons & IN_SPEED );
|
|
|
|
const bool bWantSprint = ( CanSprint() && IsSuitEquipped() && ( mv->m_nButtons & IN_SPEED ) );
|
|
const bool bWantsToChangeSprinting = ( m_HL2Local.m_bNewSprinting != bWantSprint ) && ( nChangedButtons & IN_SPEED ) != 0;
|
|
|
|
bool bSprinting = m_HL2Local.m_bNewSprinting;
|
|
if ( bWantsToChangeSprinting )
|
|
{
|
|
if ( bWantSprint )
|
|
{
|
|
if ( m_HL2Local.m_flSuitPower < 10.0f )
|
|
{
|
|
if ( bJustPressedSpeed )
|
|
{
|
|
CPASAttenuationFilter filter( this );
|
|
filter.UsePredictionRules();
|
|
EmitSound( filter, entindex(), "HL2Player.SprintNoPower" );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
bSprinting = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
bSprinting = false;
|
|
}
|
|
}
|
|
|
|
if ( m_HL2Local.m_flSuitPower < 0.01 )
|
|
{
|
|
bSprinting = false;
|
|
}
|
|
|
|
bool bWantWalking;
|
|
|
|
if ( IsSuitEquipped() )
|
|
{
|
|
bWantWalking = ( mv->m_nButtons & IN_WALK ) && !bSprinting && !( mv->m_nButtons & IN_DUCK );
|
|
}
|
|
else
|
|
{
|
|
bWantWalking = true;
|
|
}
|
|
|
|
if ( bWantWalking )
|
|
{
|
|
bSprinting = false;
|
|
}
|
|
|
|
m_HL2Local.m_bNewSprinting = bSprinting;
|
|
|
|
if ( bSprinting )
|
|
{
|
|
if ( bJustPressedSpeed )
|
|
{
|
|
CPASAttenuationFilter filter( this );
|
|
filter.UsePredictionRules();
|
|
EmitSound( filter, entindex(), "HL2Player.SprintStart" );
|
|
}
|
|
mv->m_flClientMaxSpeed = HL2_SPRINT_SPEED;
|
|
}
|
|
else if ( bWantWalking )
|
|
{
|
|
mv->m_flClientMaxSpeed = HL2_WALK_SPEED;
|
|
}
|
|
else
|
|
{
|
|
mv->m_flClientMaxSpeed = HL2_NORM_SPEED;
|
|
}
|
|
|
|
mv->m_flMaxSpeed = sv_maxspeed.GetFloat();
|
|
}
|
|
|
|
void C_HL2MP_Player::ReduceTimers( CMoveData* mv )
|
|
{
|
|
bool bSprinting = mv->m_flClientMaxSpeed == HL2_SPRINT_SPEED;
|
|
|
|
if ( bSprinting )
|
|
{
|
|
SuitPower_AddDevice( SuitDeviceSprint );
|
|
}
|
|
else
|
|
{
|
|
SuitPower_RemoveDevice( SuitDeviceSprint );
|
|
}
|
|
|
|
SuitPower_Update();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//-----------------------------------------------------------------------------
|
|
void C_HL2MP_Player::StartWalking( void )
|
|
{
|
|
SetMaxSpeed( HL2_WALK_SPEED );
|
|
m_fIsWalking = true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//-----------------------------------------------------------------------------
|
|
void C_HL2MP_Player::StopWalking( void )
|
|
{
|
|
SetMaxSpeed( HL2_NORM_SPEED );
|
|
m_fIsWalking = false;
|
|
}
|
|
|
|
void C_HL2MP_Player::ItemPreFrame( void )
|
|
{
|
|
if ( GetFlags() & FL_FROZEN )
|
|
return;
|
|
|
|
// Disallow shooting while zooming
|
|
if ( m_nButtons & IN_ZOOM )
|
|
{
|
|
//FIXME: Held weapons like the grenade get sad when this happens
|
|
m_nButtons &= ~(IN_ATTACK|IN_ATTACK2);
|
|
}
|
|
|
|
BaseClass::ItemPreFrame();
|
|
|
|
}
|
|
|
|
void C_HL2MP_Player::ItemPostFrame( void )
|
|
{
|
|
if ( GetFlags() & FL_FROZEN )
|
|
return;
|
|
|
|
BaseClass::ItemPostFrame();
|
|
}
|
|
|
|
C_BaseAnimating *C_HL2MP_Player::BecomeRagdollOnClient()
|
|
{
|
|
// Let the C_CSRagdoll entity do this.
|
|
// m_builtRagdoll = true;
|
|
return NULL;
|
|
}
|
|
|
|
void C_HL2MP_Player::CalcView( Vector &eyeOrigin, QAngle &eyeAngles, float &zNear, float &zFar, float &fov )
|
|
{
|
|
if ( m_lifeState != LIFE_ALIVE && !IsObserver() )
|
|
{
|
|
Vector origin = EyePosition();
|
|
|
|
IRagdoll *pRagdoll = GetRepresentativeRagdoll();
|
|
|
|
if ( pRagdoll )
|
|
{
|
|
origin = pRagdoll->GetRagdollOrigin();
|
|
origin.z += VEC_DEAD_VIEWHEIGHT_SCALED( this ).z; // look over ragdoll, not through
|
|
}
|
|
|
|
BaseClass::CalcView( eyeOrigin, eyeAngles, zNear, zFar, fov );
|
|
|
|
eyeOrigin = origin;
|
|
|
|
Vector vForward;
|
|
AngleVectors( eyeAngles, &vForward );
|
|
|
|
VectorNormalize( vForward );
|
|
VectorMA( origin, -CHASE_CAM_DISTANCE_MAX, vForward, eyeOrigin );
|
|
|
|
Vector WALL_MIN( -WALL_OFFSET, -WALL_OFFSET, -WALL_OFFSET );
|
|
Vector WALL_MAX( WALL_OFFSET, WALL_OFFSET, WALL_OFFSET );
|
|
|
|
trace_t trace; // clip against world
|
|
C_BaseEntity::PushEnableAbsRecomputations( false ); // HACK don't recompute positions while doing RayTrace
|
|
UTIL_TraceHull( origin, eyeOrigin, WALL_MIN, WALL_MAX, MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &trace );
|
|
C_BaseEntity::PopEnableAbsRecomputations();
|
|
|
|
if (trace.fraction < 1.0)
|
|
{
|
|
eyeOrigin = trace.endpos;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
BaseClass::CalcView( eyeOrigin, eyeAngles, zNear, zFar, fov );
|
|
}
|
|
|
|
IRagdoll* C_HL2MP_Player::GetRepresentativeRagdoll() const
|
|
{
|
|
if ( m_hRagdoll.Get() )
|
|
{
|
|
C_HL2MPRagdoll *pRagdoll = (C_HL2MPRagdoll*)m_hRagdoll.Get();
|
|
|
|
return pRagdoll->GetIRagdoll();
|
|
}
|
|
else
|
|
{
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
//HL2MPRAGDOLL
|
|
|
|
|
|
IMPLEMENT_CLIENTCLASS_DT_NOBASE( C_HL2MPRagdoll, DT_HL2MPRagdoll, CHL2MPRagdoll )
|
|
RecvPropVector( RECVINFO(m_vecRagdollOrigin) ),
|
|
RecvPropEHandle( RECVINFO( m_hPlayer ) ),
|
|
RecvPropInt( RECVINFO( m_nModelIndex ) ),
|
|
RecvPropInt( RECVINFO(m_nForceBone) ),
|
|
RecvPropVector( RECVINFO(m_vecForce) ),
|
|
RecvPropVector( RECVINFO( m_vecRagdollVelocity ) )
|
|
END_RECV_TABLE()
|
|
|
|
|
|
|
|
C_HL2MPRagdoll::C_HL2MPRagdoll()
|
|
{
|
|
|
|
}
|
|
|
|
C_HL2MPRagdoll::~C_HL2MPRagdoll()
|
|
{
|
|
PhysCleanupFrictionSounds( this );
|
|
|
|
if ( m_hPlayer )
|
|
{
|
|
m_hPlayer->CreateModelInstance();
|
|
}
|
|
}
|
|
|
|
void C_HL2MPRagdoll::Interp_Copy( C_BaseAnimatingOverlay *pSourceEntity )
|
|
{
|
|
if ( !pSourceEntity )
|
|
return;
|
|
|
|
VarMapping_t *pSrc = pSourceEntity->GetVarMapping();
|
|
VarMapping_t *pDest = GetVarMapping();
|
|
|
|
// Find all the VarMapEntry_t's that represent the same variable.
|
|
for ( int i = 0; i < pDest->m_Entries.Count(); i++ )
|
|
{
|
|
VarMapEntry_t *pDestEntry = &pDest->m_Entries[i];
|
|
const char *pszName = pDestEntry->watcher->GetDebugName();
|
|
for ( int j=0; j < pSrc->m_Entries.Count(); j++ )
|
|
{
|
|
VarMapEntry_t *pSrcEntry = &pSrc->m_Entries[j];
|
|
if ( !Q_strcmp( pSrcEntry->watcher->GetDebugName(), pszName ) )
|
|
{
|
|
pDestEntry->watcher->Copy( pSrcEntry->watcher );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void C_HL2MPRagdoll::ImpactTrace( trace_t *pTrace, int iDamageType, const char *pCustomImpactName )
|
|
{
|
|
IPhysicsObject *pPhysicsObject = VPhysicsGetObject();
|
|
|
|
if( !pPhysicsObject )
|
|
return;
|
|
|
|
Vector dir = pTrace->endpos - pTrace->startpos;
|
|
|
|
if ( iDamageType == DMG_BLAST )
|
|
{
|
|
dir *= 4000; // adjust impact strenght
|
|
|
|
// apply force at object mass center
|
|
pPhysicsObject->ApplyForceCenter( dir );
|
|
}
|
|
else
|
|
{
|
|
Vector hitpos;
|
|
|
|
VectorMA( pTrace->startpos, pTrace->fraction, dir, hitpos );
|
|
VectorNormalize( dir );
|
|
|
|
dir *= 4000; // adjust impact strenght
|
|
|
|
// apply force where we hit it
|
|
pPhysicsObject->ApplyForceOffset( dir, hitpos );
|
|
|
|
// Blood spray!
|
|
// FX_CS_BloodSpray( hitpos, dir, 10 );
|
|
}
|
|
|
|
m_pRagdoll->ResetRagdollSleepAfterTime();
|
|
}
|
|
|
|
|
|
void C_HL2MPRagdoll::CreateHL2MPRagdoll( void )
|
|
{
|
|
// First, initialize all our data. If we have the player's entity on our client,
|
|
// then we can make ourselves start out exactly where the player is.
|
|
C_HL2MP_Player *pPlayer = dynamic_cast< C_HL2MP_Player* >( m_hPlayer.Get() );
|
|
|
|
if ( pPlayer && !pPlayer->IsDormant() )
|
|
{
|
|
// move my current model instance to the ragdoll's so decals are preserved.
|
|
pPlayer->SnatchModelInstance( this );
|
|
|
|
VarMapping_t *varMap = GetVarMapping();
|
|
|
|
// Copy all the interpolated vars from the player entity.
|
|
// The entity uses the interpolated history to get bone velocity.
|
|
bool bRemotePlayer = (pPlayer != C_BasePlayer::GetLocalPlayer());
|
|
if ( bRemotePlayer )
|
|
{
|
|
Interp_Copy( pPlayer );
|
|
|
|
SetAbsAngles( pPlayer->GetRenderAngles() );
|
|
GetRotationInterpolator().Reset();
|
|
|
|
m_flAnimTime = pPlayer->m_flAnimTime;
|
|
SetSequence( pPlayer->GetSequence() );
|
|
m_flPlaybackRate = pPlayer->GetPlaybackRate();
|
|
}
|
|
else
|
|
{
|
|
// This is the local player, so set them in a default
|
|
// pose and slam their velocity, angles and origin
|
|
SetAbsOrigin( m_vecRagdollOrigin );
|
|
|
|
SetAbsAngles( pPlayer->GetRenderAngles() );
|
|
|
|
SetAbsVelocity( m_vecRagdollVelocity );
|
|
|
|
int iSeq = pPlayer->GetSequence();
|
|
if ( iSeq == -1 )
|
|
{
|
|
Assert( false ); // missing walk_lower?
|
|
iSeq = 0;
|
|
}
|
|
|
|
SetSequence( iSeq ); // walk_lower, basic pose
|
|
SetCycle( 0.0 );
|
|
|
|
Interp_Reset( varMap );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// overwrite network origin so later interpolation will
|
|
// use this position
|
|
SetNetworkOrigin( m_vecRagdollOrigin );
|
|
|
|
SetAbsOrigin( m_vecRagdollOrigin );
|
|
SetAbsVelocity( m_vecRagdollVelocity );
|
|
|
|
Interp_Reset( GetVarMapping() );
|
|
|
|
}
|
|
|
|
SetModelIndex( m_nModelIndex );
|
|
|
|
// Make us a ragdoll..
|
|
m_nRenderFX = kRenderFxRagdoll;
|
|
|
|
matrix3x4_t boneDelta0[MAXSTUDIOBONES];
|
|
matrix3x4_t boneDelta1[MAXSTUDIOBONES];
|
|
matrix3x4_t currentBones[MAXSTUDIOBONES];
|
|
const float boneDt = 0.05f;
|
|
|
|
if ( pPlayer && !pPlayer->IsDormant() )
|
|
{
|
|
pPlayer->GetRagdollInitBoneArrays( boneDelta0, boneDelta1, currentBones, boneDt );
|
|
}
|
|
else
|
|
{
|
|
GetRagdollInitBoneArrays( boneDelta0, boneDelta1, currentBones, boneDt );
|
|
}
|
|
|
|
InitAsClientRagdoll( boneDelta0, boneDelta1, currentBones, boneDt );
|
|
}
|
|
|
|
|
|
void C_HL2MPRagdoll::OnDataChanged( DataUpdateType_t type )
|
|
{
|
|
BaseClass::OnDataChanged( type );
|
|
|
|
if ( type == DATA_UPDATE_CREATED )
|
|
{
|
|
CreateHL2MPRagdoll();
|
|
}
|
|
}
|
|
|
|
IRagdoll* C_HL2MPRagdoll::GetIRagdoll() const
|
|
{
|
|
return m_pRagdoll;
|
|
}
|
|
|
|
void C_HL2MPRagdoll::UpdateOnRemove( void )
|
|
{
|
|
VPhysicsSetObject( NULL );
|
|
|
|
BaseClass::UpdateOnRemove();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: clear out any face/eye values stored in the material system
|
|
//-----------------------------------------------------------------------------
|
|
void C_HL2MPRagdoll::SetupWeights( const matrix3x4_t *pBoneToWorld, int nFlexWeightCount, float *pFlexWeights, float *pFlexDelayedWeights )
|
|
{
|
|
BaseClass::SetupWeights( pBoneToWorld, nFlexWeightCount, pFlexWeights, pFlexDelayedWeights );
|
|
|
|
static float destweight[128];
|
|
static bool bIsInited = false;
|
|
|
|
CStudioHdr *hdr = GetModelPtr();
|
|
if ( !hdr )
|
|
return;
|
|
|
|
int nFlexDescCount = hdr->numflexdesc();
|
|
if ( nFlexDescCount )
|
|
{
|
|
Assert( !pFlexDelayedWeights );
|
|
memset( pFlexWeights, 0, nFlexWeightCount * sizeof(float) );
|
|
}
|
|
|
|
if ( m_iEyeAttachment > 0 )
|
|
{
|
|
matrix3x4_t attToWorld;
|
|
if (GetAttachment( m_iEyeAttachment, attToWorld ))
|
|
{
|
|
Vector local, tmp;
|
|
local.Init( 1000.0f, 0.0f, 0.0f );
|
|
VectorTransform( local, attToWorld, tmp );
|
|
modelrender->SetViewTarget( GetModelPtr(), GetBody(), tmp );
|
|
}
|
|
}
|
|
}
|
|
|
|
void C_HL2MP_Player::PostThink( void )
|
|
{
|
|
BaseClass::PostThink();
|
|
|
|
// Store the eye angles pitch so the client can compute its animation state correctly.
|
|
m_angEyeAngles = EyeAngles();
|
|
|
|
if ( GetFlags() & FL_DUCKING )
|
|
{
|
|
SetCollisionBounds( VEC_CROUCH_TRACE_MIN, VEC_CROUCH_TRACE_MAX );
|
|
}
|
|
} |