summaryrefslogtreecommitdiff
path: root/game/client/hl1/c_hl1mp_player.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'game/client/hl1/c_hl1mp_player.cpp')
-rw-r--r--game/client/hl1/c_hl1mp_player.cpp602
1 files changed, 602 insertions, 0 deletions
diff --git a/game/client/hl1/c_hl1mp_player.cpp b/game/client/hl1/c_hl1mp_player.cpp
new file mode 100644
index 0000000..53bfbbc
--- /dev/null
+++ b/game/client/hl1/c_hl1mp_player.cpp
@@ -0,0 +1,602 @@
+//========= 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;
+}