//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // //=============================================================================// #include "cbase.h" #include "c_sdk_player.h" #include "weapon_sdkbase.h" #include "c_basetempentity.h" #if defined( CSDKPlayer ) #undef CSDKPlayer #endif // -------------------------------------------------------------------------------- // // Player animation event. Sent to the client when a player fires, jumps, reloads, etc.. // -------------------------------------------------------------------------------- // 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_SDKPlayer *pPlayer = dynamic_cast< C_SDKPlayer* >( 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 ); BEGIN_RECV_TABLE_NOBASE( C_TEPlayerAnimEvent, DT_TEPlayerAnimEvent ) RecvPropEHandle( RECVINFO( m_hPlayer ) ), RecvPropInt( RECVINFO( m_iEvent ) ), RecvPropInt( RECVINFO( m_nData ) ) END_RECV_TABLE() BEGIN_RECV_TABLE_NOBASE( C_SDKPlayer, DT_SDKLocalPlayerExclusive ) RecvPropInt( RECVINFO( m_iShotsFired ) ), END_RECV_TABLE() IMPLEMENT_CLIENTCLASS_DT( C_SDKPlayer, DT_SDKPlayer, CSDKPlayer ) RecvPropDataTable( "sdklocaldata", 0, 0, &REFERENCE_RECV_TABLE(DT_SDKLocalPlayerExclusive) ), RecvPropFloat( RECVINFO( m_angEyeAngles[0] ) ), RecvPropFloat( RECVINFO( m_angEyeAngles[1] ) ), RecvPropInt( RECVINFO( m_iThrowGrenadeCounter ) ), RecvPropEHandle( RECVINFO( m_hRagdoll ) ), END_RECV_TABLE() BEGIN_PREDICTION_DATA( C_SDKPlayer ) DEFINE_PRED_FIELD( m_flCycle, FIELD_FLOAT, FTYPEDESC_OVERRIDE | FTYPEDESC_PRIVATE | FTYPEDESC_NOERRORCHECK ), DEFINE_PRED_FIELD( m_iShotsFired, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ), END_PREDICTION_DATA() class C_SDKRagdoll : public C_BaseAnimatingOverlay { public: DECLARE_CLASS( C_SDKRagdoll, C_BaseAnimatingOverlay ); DECLARE_CLIENTCLASS(); C_SDKRagdoll(); ~C_SDKRagdoll(); virtual void OnDataChanged( DataUpdateType_t type ); int GetPlayerEntIndex() const; IRagdoll* GetIRagdoll() const; void ImpactTrace( trace_t *pTrace, int iDamageType, const char *pCustomImpactName ); private: C_SDKRagdoll( const C_SDKRagdoll & ) {} void Interp_Copy( C_BaseAnimatingOverlay *pSourceEntity ); void CreateRagdoll(); private: EHANDLE m_hPlayer; CNetworkVector( m_vecRagdollVelocity ); CNetworkVector( m_vecRagdollOrigin ); }; IMPLEMENT_CLIENTCLASS_DT_NOBASE( C_SDKRagdoll, DT_SDKRagdoll, CSDKRagdoll ) 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_SDKRagdoll::C_SDKRagdoll() { } C_SDKRagdoll::~C_SDKRagdoll() { PhysCleanupFrictionSounds( this ); } void C_SDKRagdoll::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]; for ( int j=0; j < pSrc->m_Entries.Count(); j++ ) { VarMapEntry_t *pSrcEntry = &pSrc->m_Entries[j]; if ( !Q_strcmp( pSrcEntry->watcher->GetDebugName(), pDestEntry->watcher->GetDebugName() ) ) { pDestEntry->watcher->Copy( pSrcEntry->watcher ); break; } } } } void C_SDKRagdoll::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_SDKRagdoll::CreateRagdoll() { // 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_SDKPlayer *pPlayer = dynamic_cast< C_SDKPlayer* >( 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 = LookupSequence( "walk_lower" ); 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_SDKRagdoll::OnDataChanged( DataUpdateType_t type ) { BaseClass::OnDataChanged( type ); if ( type == DATA_UPDATE_CREATED ) { CreateRagdoll(); } } IRagdoll* C_SDKRagdoll::GetIRagdoll() const { return m_pRagdoll; } C_BaseAnimating * C_SDKPlayer::BecomeRagdollOnClient() { // Let the C_CSRagdoll entity do this. // m_builtRagdoll = true; return NULL; } IRagdoll* C_SDKPlayer::GetRepresentativeRagdoll() const { if ( m_hRagdoll.Get() ) { C_SDKRagdoll *pRagdoll = (C_SDKRagdoll*)m_hRagdoll.Get(); return pRagdoll->GetIRagdoll(); } else { return NULL; } } C_SDKPlayer::C_SDKPlayer() : m_iv_angEyeAngles( "C_SDKPlayer::m_iv_angEyeAngles" ) { m_PlayerAnimState = CreatePlayerAnimState( this, this, LEGANIM_9WAY, true ); m_angEyeAngles.Init(); AddVar( &m_angEyeAngles, &m_iv_angEyeAngles, LATCH_SIMULATION_VAR ); } C_SDKPlayer::~C_SDKPlayer() { m_PlayerAnimState->Release(); } C_SDKPlayer* C_SDKPlayer::GetLocalSDKPlayer() { return ToSDKPlayer( C_BasePlayer::GetLocalPlayer() ); } const QAngle& C_SDKPlayer::GetRenderAngles() { if ( IsRagdoll() ) { return vec3_angle; } else { return m_PlayerAnimState->GetRenderAngles(); } } void C_SDKPlayer::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_SDKPlayer::GetLocalSDKPlayer() ) m_PlayerAnimState->Update( EyeAngles()[YAW], m_angEyeAngles[PITCH] ); else m_PlayerAnimState->Update( m_angEyeAngles[YAW], m_angEyeAngles[PITCH] ); BaseClass::UpdateClientSideAnimation(); } void C_SDKPlayer::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_SDKPlayer::OnDataChanged( DataUpdateType_t type ) { BaseClass::OnDataChanged( type ); if ( type == DATA_UPDATE_CREATED ) { SetNextClientThink( CLIENT_THINK_ALWAYS ); } UpdateVisibility(); } void C_SDKPlayer::DoAnimationEvent( PlayerAnimEvent_t event, int nData ) { if ( event == PLAYERANIMEVENT_THROW_GRENADE ) { // Let the server handle this event. It will update m_iThrowGrenadeCounter and the client will // pick up the event in CCSPlayerAnimState. } else { m_PlayerAnimState->DoAnimationEvent( event, nData ); } } bool C_SDKPlayer::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; return BaseClass::ShouldDraw(); } CWeaponSDKBase* C_SDKPlayer::GetActiveSDKWeapon() const { return dynamic_cast< CWeaponSDKBase* >( GetActiveWeapon() ); }