//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // // $NoKeywords: $ // //=============================================================================// #include "cbase.h" #include "c_hl1mp_player.h" #include "c_basetempentity.h" #include "iinput.h" // Don't alias here #if defined( CHL1MP_Player ) #undef CHL1MP_Player #endif ////////////////////////////////////////////////////////////////////// class C_TEPlayerAnimEvent : public C_BaseTempEntity { public: DECLARE_CLASS( C_TEPlayerAnimEvent, C_BaseTempEntity ); DECLARE_CLIENTCLASS(); virtual void PostDataUpdate( DataUpdateType_t updateType ) { // Create the effect. C_HL1MP_Player *pPlayer = dynamic_cast< C_HL1MP_Player* >( m_hPlayer.Get() ); if ( pPlayer && !pPlayer->IsDormant() ) { pPlayer->DoAnimationEvent( (PlayerAnimEvent_t)m_iEvent.Get(), m_nData ); } } public: CNetworkHandle( CBasePlayer, m_hPlayer ); CNetworkVar( int, m_iEvent ); CNetworkVar( int, m_nData ); }; IMPLEMENT_CLIENTCLASS_EVENT( C_TEPlayerAnimEvent, DT_TEPlayerAnimEvent, CTEPlayerAnimEvent ); // ------------------------------------------------------------------------------------------ // // Data tables and prediction tables. // ------------------------------------------------------------------------------------------ // BEGIN_RECV_TABLE_NOBASE( C_TEPlayerAnimEvent, DT_TEPlayerAnimEvent ) RecvPropEHandle( RECVINFO( m_hPlayer ) ), RecvPropInt( RECVINFO( m_iEvent ) ), RecvPropInt( RECVINFO( m_nData ) ) END_RECV_TABLE() IMPLEMENT_CLIENTCLASS_DT( C_HL1MP_Player, DT_HL1MP_Player, CHL1MP_Player ) RecvPropFloat( RECVINFO( m_angEyeAngles[0] ) ), RecvPropFloat( RECVINFO( m_angEyeAngles[1] ) ), RecvPropEHandle( RECVINFO( m_hRagdoll ) ), RecvPropInt( RECVINFO( m_iSpawnInterpCounter ) ), RecvPropInt( RECVINFO( m_iRealSequence ) ), // RecvPropDataTable( RECVINFO_DT( m_Shared ), 0, &REFERENCE_RECV_TABLE( DT_TFCPlayerShared ) ) END_RECV_TABLE() BEGIN_PREDICTION_DATA( C_HL1MP_Player ) END_PREDICTION_DATA() ///////////////////////////////////////////////////////////////////// static ConVar cl_playermodel( "cl_playermodel", "none", FCVAR_USERINFO | FCVAR_ARCHIVE | FCVAR_SERVER_CAN_EXECUTE, "Default Player Model"); C_HL1MP_Player::C_HL1MP_Player( void ) : m_iv_angEyeAngles( "C_HL1MP_Player::m_iv_angEyeAngles" ) { m_PlayerAnimState = CreatePlayerAnimState( this ); m_angEyeAngles.Init(); m_fLastPredFreeze = -1; // cant interpolate ... buggy? it keeps resetting the angle to 0,0,0 // AddVar( &m_angEyeAngles, &m_iv_angEyeAngles, LATCH_SIMULATION_VAR ); } C_HL1MP_Player::~C_HL1MP_Player() { m_PlayerAnimState->Release(); } const QAngle& C_HL1MP_Player::GetRenderAngles() { if ( IsRagdoll() ) { return vec3_angle; } else { return m_PlayerAnimState->GetRenderAngles(); } } void C_HL1MP_Player::UpdateClientSideAnimation() { // Update the animation data. It does the local check here so this works when using // a third-person camera (and we don't have valid player angles). if ( this == C_BasePlayer::GetLocalPlayer() ) m_PlayerAnimState->Update( EyeAngles()[YAW], m_angEyeAngles[PITCH] ); else m_PlayerAnimState->Update( m_angEyeAngles[YAW], m_angEyeAngles[PITCH] ); BaseClass::UpdateClientSideAnimation(); } void C_HL1MP_Player::DoAnimationEvent( PlayerAnimEvent_t event, int nData ) { m_PlayerAnimState->DoAnimationEvent( event, nData ); } void C_HL1MP_Player::ProcessMuzzleFlashEvent() { #if 0 // Reenable when the weapons have muzzle flash attachments in the right spot. if ( this != C_BasePlayer::GetLocalPlayer() ) { Vector vAttachment; QAngle dummyAngles; C_WeaponTFCBase *pWeapon = m_Shared.GetActiveTFCWeapon(); if ( pWeapon ) { int iAttachment = pWeapon->LookupAttachment( "muzzle_flash" ); if ( iAttachment > 0 ) { float flScale = 1; pWeapon->GetAttachment( iAttachment, vAttachment, dummyAngles ); // The way the models are setup, the up vector points along the barrel. Vector vForward, vRight, vUp; AngleVectors( dummyAngles, &vForward, &vRight, &vUp ); VectorAngles( vUp, dummyAngles ); FX_MuzzleEffect( vAttachment, dummyAngles, flScale, 0, NULL, true ); } } } Vector vAttachment; QAngle dummyAngles; bool bFoundAttachment = GetAttachment( 1, vAttachment, dummyAngles ); // If we have an attachment, then stick a light on it. if ( bFoundAttachment ) { dlight_t *el = effects->CL_AllocDlight( LIGHT_INDEX_MUZZLEFLASH + index ); el->origin = vAttachment; el->radius = 24; el->decay = el->radius / 0.05f; el->die = gpGlobals->curtime + 0.05f; el->color.r = 255; el->color.g = 192; el->color.b = 64; el->color.exponent = 5; } #endif } void C_HL1MP_Player::Spawn( void ) { BaseClass::Spawn(); // SetModel( "models/player/mp/barney/barney.mdl" ); m_iSpawnInterpCounterCache = 0; UpdateVisibility(); } void C_HL1MP_Player::AddEntity( void ) { BaseClass::AddEntity(); //m_PlayerAnimState.Update(); // SetLocalAnglesDim( X_INDEX, 0 ); } void C_HL1MP_Player::OnDataChanged( DataUpdateType_t type ) { BaseClass::OnDataChanged( type ); if ( type == DATA_UPDATE_CREATED ) { SetNextClientThink( CLIENT_THINK_ALWAYS ); } UpdateVisibility(); } C_BaseAnimating *C_HL1MP_Player::BecomeRagdollOnClient() { // Handled elsewhere return NULL; } void C_HL1MP_Player::PreThink( void ) { BaseClass::PreThink(); return; 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(); #if 0 HandleSpeedChanges(); if ( m_HL2Local.m_flSuitPower <= 0.0f ) { if( IsSprinting() ) { StopSprinting(); } } #endif } void C_HL1MP_Player::PostDataUpdate( DataUpdateType_t updateType ) { // C_BaseEntity assumes we're networking the entity's angles, so pretend that it // networked the same value we already have. SetNetworkAngles( GetLocalAngles() ); BaseClass::PostDataUpdate( updateType ); } void C_HL1MP_Player::ClientThink( void ) { BaseClass::ClientThink(); // m_PlayerAnimState.Update(); } //HL1MPRAGDOLL IMPLEMENT_CLIENTCLASS_DT_NOBASE( C_HL1MPRagdoll, DT_HL1MPRagdoll, CHL1MPRagdoll ) 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_HL1MPRagdoll::C_HL1MPRagdoll() { } C_HL1MPRagdoll::~C_HL1MPRagdoll() { PhysCleanupFrictionSounds( this ); if ( m_hPlayer ) { m_hPlayer->CreateModelInstance(); } } void C_HL1MPRagdoll::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_HL1MPRagdoll::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 ); } m_pRagdoll->ResetRagdollSleepAfterTime(); } void C_HL1MP_Player::CalcView( Vector &eyeOrigin, QAngle &eyeAngles, float &zNear, float &zFar, float &fov ) { if ( m_lifeState != LIFE_ALIVE ) { 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_HL1MP_Player::GetRepresentativeRagdoll() const { if ( m_hRagdoll.Get() ) { C_HL1MPRagdoll *pRagdoll = (C_HL1MPRagdoll*)m_hRagdoll.Get(); return pRagdoll->GetIRagdoll(); } else { return NULL; } } void C_HL1MPRagdoll::CreateHL1MPRagdoll( 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_HL1MP_Player *pPlayer = dynamic_cast< C_HL1MP_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_HL1MPRagdoll::OnDataChanged( DataUpdateType_t type ) { BaseClass::OnDataChanged( type ); if ( type == DATA_UPDATE_CREATED ) { CreateHL1MPRagdoll(); } } IRagdoll* C_HL1MPRagdoll::GetIRagdoll() const { return m_pRagdoll; } void C_HL1MPRagdoll::UpdateOnRemove( void ) { VPhysicsSetObject( NULL ); BaseClass::UpdateOnRemove(); } //----------------------------------------------------------------------------- // Purpose: clear out any face/eye values stored in the material system //----------------------------------------------------------------------------- void C_HL1MPRagdoll::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 ); } } } bool C_HL1MP_Player::ShouldDraw( void ) { if ( !IsAlive() ) return false; if ( IsLocalPlayer() && IsRagdoll() ) return true; if ( IsRagdoll() ) return false; return BaseClass::ShouldDraw(); } bool C_HL1MP_Player::ShouldPredict( void ) { // Do this before calling into baseclass so prediction data block gets allocated if ( IsLocalPlayer() ) { #if 0 // Disable prediction when player hops onto a moving train or elevator :-/ if ( GetGroundEntity() && GetGroundEntity()->GetMoveType() == MOVETYPE_PUSH ) { Vector vel = GetGroundEntity()->GetLocalVelocity(); if ( vel.Length() > 0.002f ) { m_fLastPredFreeze = gpGlobals->curtime; } } // disable prediction for 3 seconds after touching a moving entity if ( ( gpGlobals->curtime - m_fLastPredFreeze ) < 3 ) { if ( GetPredictable() ) { QuickShutdownPredictable(); } return false; } if ( !GetPredictable() && IsIntermediateDataAllocated() ) { QuickInitPredictable(); } #endif return true; } return false; }