source-engine/game/client/hl2mp/c_hl2mp_player.cpp
FluorescentCIAAfricanAmerican 3bf9df6b27 1
2020-04-22 12:56:21 -04:00

985 lines
24 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
LINK_ENTITY_TO_CLASS( player, C_HL2MP_Player );
IMPLEMENT_CLIENTCLASS_DT(C_HL2MP_Player, DT_HL2MP_Player, CHL2MP_Player)
RecvPropFloat( RECVINFO( m_angEyeAngles[0] ) ),
RecvPropFloat( RECVINFO( m_angEyeAngles[1] ) ),
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 ),
END_PREDICTION_DATA()
#define HL2_WALK_SPEED 150
#define HL2_NORM_SPEED 190
#define HL2_SPRINT_SPEED 320
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);
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;
}
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();
HandleSpeedChanges();
if ( m_HL2Local.m_flSuitPower <= 0.0f )
{
if( IsSprinting() )
{
StopSprinting();
}
}
}
const QAngle &C_HL2MP_Player::EyeAngles()
{
if( IsLocalPlayer() )
{
return BaseClass::EyeAngles();
}
else
{
return m_angEyeAngles;
}
}
//-----------------------------------------------------------------------------
// 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) );
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void C_HL2MP_Player::StartSprinting( void )
{
if( m_HL2Local.m_flSuitPower < 10 )
{
// Don't sprint unless there's a reasonable
// amount of suit power.
CPASAttenuationFilter filter( this );
filter.UsePredictionRules();
EmitSound( filter, entindex(), "HL2Player.SprintNoPower" );
return;
}
CPASAttenuationFilter filter( this );
filter.UsePredictionRules();
EmitSound( filter, entindex(), "HL2Player.SprintStart" );
SetMaxSpeed( HL2_SPRINT_SPEED );
m_fIsSprinting = true;
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void C_HL2MP_Player::StopSprinting( void )
{
SetMaxSpeed( HL2_NORM_SPEED );
m_fIsSprinting = false;
}
void C_HL2MP_Player::HandleSpeedChanges( void )
{
int buttonsChanged = m_afButtonPressed | m_afButtonReleased;
if( buttonsChanged & IN_SPEED )
{
// The state of the sprint/run button has changed.
if ( IsSuitEquipped() )
{
if ( !(m_afButtonPressed & IN_SPEED) && IsSprinting() )
{
StopSprinting();
}
else if ( (m_afButtonPressed & IN_SPEED) && !IsSprinting() )
{
if ( CanSprint() )
{
StartSprinting();
}
else
{
// Reset key, so it will be activated post whatever is suppressing it.
m_nButtons &= ~IN_SPEED;
}
}
}
}
else if( buttonsChanged & IN_WALK )
{
if ( IsSuitEquipped() )
{
// The state of the WALK button has changed.
if( IsWalking() && !(m_afButtonPressed & IN_WALK) )
{
StopWalking();
}
else if( !IsWalking() && !IsSprinting() && (m_afButtonPressed & IN_WALK) && !(m_nButtons & IN_DUCK) )
{
StartWalking();
}
}
}
if ( IsSuitEquipped() && m_fIsWalking && !(m_nButtons & IN_WALK) )
StopWalking();
}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
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();
}