//========= 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(); }