You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
407 lines
9.6 KiB
407 lines
9.6 KiB
//========= 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() ); |
|
} |