summaryrefslogtreecommitdiff
path: root/game/client/cstrike/c_cs_player.cpp
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /game/client/cstrike/c_cs_player.cpp
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'game/client/cstrike/c_cs_player.cpp')
-rw-r--r--game/client/cstrike/c_cs_player.cpp2553
1 files changed, 2553 insertions, 0 deletions
diff --git a/game/client/cstrike/c_cs_player.cpp b/game/client/cstrike/c_cs_player.cpp
new file mode 100644
index 0000000..974dfd4
--- /dev/null
+++ b/game/client/cstrike/c_cs_player.cpp
@@ -0,0 +1,2553 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "c_cs_player.h"
+#include "c_user_message_register.h"
+#include "view.h"
+#include "iclientvehicle.h"
+#include "ivieweffects.h"
+#include "input.h"
+#include "IEffects.h"
+#include "fx.h"
+#include "c_basetempentity.h"
+#include "hud_macros.h" //HOOK_COMMAND
+#include "engine/ivdebugoverlay.h"
+#include "smoke_fog_overlay.h"
+#include "bone_setup.h"
+#include "in_buttons.h"
+#include "r_efx.h"
+#include "dlight.h"
+#include "shake.h"
+#include "cl_animevent.h"
+#include "c_physicsprop.h"
+#include "props_shared.h"
+#include "obstacle_pushaway.h"
+#include "death_pose.h"
+
+#include "effect_dispatch_data.h" //for water ripple / splash effect
+#include "c_te_effect_dispatch.h" //ditto
+#include "c_te_legacytempents.h"
+#include "cs_gamerules.h"
+#include "fx_cs_blood.h"
+#include "c_cs_playerresource.h"
+#include "c_team.h"
+
+#include "history_resource.h"
+#include "ragdoll_shared.h"
+#include "collisionutils.h"
+
+// NVNT - haptics system for spectating
+#include "haptics/haptic_utils.h"
+
+#include "steam/steam_api.h"
+
+#include "cs_blackmarket.h" // for vest/helmet prices
+
+#if defined( CCSPlayer )
+ #undef CCSPlayer
+#endif
+
+#include "materialsystem/imesh.h" //for materials->FindMaterial
+#include "iviewrender.h" //for view->
+
+#include "iviewrender_beams.h" // flashlight beam
+
+//=============================================================================
+// HPE_BEGIN:
+// [menglish] Adding and externing variables needed for the freezecam
+//=============================================================================
+
+static Vector WALL_MIN(-WALL_OFFSET,-WALL_OFFSET,-WALL_OFFSET);
+static Vector WALL_MAX(WALL_OFFSET,WALL_OFFSET,WALL_OFFSET);
+
+extern ConVar spec_freeze_time;
+extern ConVar spec_freeze_traveltime;
+extern ConVar spec_freeze_distance_min;
+extern ConVar spec_freeze_distance_max;
+
+//=============================================================================
+// HPE_END
+//=============================================================================
+
+ConVar cl_left_hand_ik( "cl_left_hand_ik", "0", 0, "Attach player's left hand to rifle with IK." );
+
+ConVar cl_ragdoll_physics_enable( "cl_ragdoll_physics_enable", "1", 0, "Enable/disable ragdoll physics." );
+
+ConVar cl_minmodels( "cl_minmodels", "0", 0, "Uses one player model for each team." );
+ConVar cl_min_ct( "cl_min_ct", "1", 0, "Controls which CT model is used when cl_minmodels is set.", true, 1, true, 4 );
+ConVar cl_min_t( "cl_min_t", "1", 0, "Controls which Terrorist model is used when cl_minmodels is set.", true, 1, true, 4 );
+const float CycleLatchTolerance = 0.15; // amount we can diverge from the server's cycle before we're corrected
+
+extern ConVar mp_playerid_delay;
+extern ConVar mp_playerid_hold;
+extern ConVar sv_allowminmodels;
+
+class CAddonInfo
+{
+public:
+ const char *m_pAttachmentName;
+ const char *m_pWeaponClassName; // The addon uses the w_ model from this weapon.
+ const char *m_pModelName; //If this is present, will use this model instead of looking up the weapon
+ const char *m_pHolsterName;
+};
+
+
+
+// These must follow the ADDON_ ordering.
+CAddonInfo g_AddonInfo[] =
+{
+ { "grenade0", "weapon_flashbang", 0, 0 },
+ { "grenade1", "weapon_flashbang", 0, 0 },
+ { "grenade2", "weapon_hegrenade", 0, 0 },
+ { "grenade3", "weapon_smokegrenade", 0, 0 },
+ { "c4", "weapon_c4", 0, 0 },
+ { "defusekit", 0, "models/weapons/w_defuser.mdl", 0 },
+ { "primary", 0, 0, 0 }, // Primary addon model is looked up based on m_iPrimaryAddon
+ { "pistol", 0, 0, 0 }, // Pistol addon model is looked up based on m_iSecondaryAddon
+ { "eholster", 0, "models/weapons/w_eq_eholster_elite.mdl", "models/weapons/w_eq_eholster.mdl" },
+};
+
+// -------------------------------------------------------------------------------- //
+// 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_CSPlayer *pPlayer = dynamic_cast< C_CSPlayer* >( 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_PREDICTION_DATA( C_CSPlayer )
+#ifdef CS_SHIELD_ENABLED
+ DEFINE_PRED_FIELD( m_bShieldDrawn, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
+#endif
+ DEFINE_PRED_FIELD_TOL( m_flStamina, FIELD_FLOAT, FTYPEDESC_INSENDTABLE, 0.1f ),
+ DEFINE_PRED_FIELD( m_flCycle, FIELD_FLOAT, FTYPEDESC_OVERRIDE | FTYPEDESC_PRIVATE | FTYPEDESC_NOERRORCHECK ),
+ DEFINE_PRED_FIELD( m_iShotsFired, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
+ DEFINE_PRED_FIELD( m_iDirection, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
+ DEFINE_PRED_FIELD( m_bResumeZoom, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
+ DEFINE_PRED_FIELD( m_iLastZoom, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
+
+END_PREDICTION_DATA()
+
+vgui::IImage* GetDefaultAvatarImage( C_BasePlayer *pPlayer )
+{
+ vgui::IImage* result = NULL;
+
+ switch ( pPlayer ? pPlayer->GetTeamNumber() : TEAM_MAXCOUNT )
+ {
+ case TEAM_TERRORIST:
+ result = vgui::scheme()->GetImage( CSTRIKE_DEFAULT_T_AVATAR, true );
+ break;
+
+ case TEAM_CT:
+ result = vgui::scheme()->GetImage( CSTRIKE_DEFAULT_CT_AVATAR, true );
+ break;
+
+ default:
+ result = vgui::scheme()->GetImage( CSTRIKE_DEFAULT_AVATAR, true );
+ break;
+ }
+
+ return result;
+}
+
+// ----------------------------------------------------------------------------- //
+// Client ragdoll entity.
+// ----------------------------------------------------------------------------- //
+
+float g_flDieTranslucentTime = 0.6;
+
+class C_CSRagdoll : public C_BaseAnimatingOverlay
+{
+public:
+ DECLARE_CLASS( C_CSRagdoll, C_BaseAnimatingOverlay );
+ DECLARE_CLIENTCLASS();
+
+ C_CSRagdoll();
+ ~C_CSRagdoll();
+
+ virtual void OnDataChanged( DataUpdateType_t type );
+
+ int GetPlayerEntIndex() const;
+ IRagdoll* GetIRagdoll() const;
+ bool GetRagdollInitBoneArrays( matrix3x4_t *pDeltaBones0, matrix3x4_t *pDeltaBones1, matrix3x4_t *pCurrentBones, float boneDt ) OVERRIDE;
+
+ void ImpactTrace( trace_t *pTrace, int iDamageType, const char *pCustomImpactName );
+
+ virtual void ComputeFxBlend();
+ virtual bool IsTransparent();
+ bool IsInitialized() { return m_bInitialized; }
+ // fading ragdolls don't cast shadows
+ virtual ShadowType_t ShadowCastType()
+ {
+ if ( m_flRagdollSinkStart == -1 )
+ return BaseClass::ShadowCastType();
+ return SHADOWS_NONE;
+ }
+
+ virtual void ValidateModelIndex( void );
+
+private:
+
+ C_CSRagdoll( const C_CSRagdoll & ) {}
+
+ void Interp_Copy( C_BaseAnimatingOverlay *pSourceEntity );
+
+ void CreateLowViolenceRagdoll( void );
+ void CreateCSRagdoll( void );
+
+private:
+
+ EHANDLE m_hPlayer;
+ CNetworkVector( m_vecRagdollVelocity );
+ CNetworkVector( m_vecRagdollOrigin );
+ CNetworkVar(int, m_iDeathPose );
+ CNetworkVar(int, m_iDeathFrame );
+ float m_flRagdollSinkStart;
+ bool m_bInitialized;
+ bool m_bCreatedWhilePlaybackSkipping;
+};
+
+
+IMPLEMENT_CLIENTCLASS_DT_NOBASE( C_CSRagdoll, DT_CSRagdoll, CCSRagdoll )
+ RecvPropVector( RECVINFO_NAME( m_vecNetworkOrigin, m_vecOrigin ) ),
+ RecvPropVector( RECVINFO(m_vecRagdollOrigin) ),
+ RecvPropEHandle( RECVINFO( m_hPlayer ) ),
+ RecvPropInt( RECVINFO( m_nModelIndex ) ),
+ RecvPropInt( RECVINFO(m_nForceBone) ),
+ RecvPropVector( RECVINFO(m_vecForce) ),
+ RecvPropVector( RECVINFO( m_vecRagdollVelocity ) ),
+ RecvPropInt( RECVINFO(m_iDeathPose) ),
+ RecvPropInt( RECVINFO(m_iDeathFrame) ),
+ RecvPropInt(RECVINFO(m_iTeamNum)),
+ RecvPropInt( RECVINFO(m_bClientSideAnimation)),
+END_RECV_TABLE()
+
+
+C_CSRagdoll::C_CSRagdoll()
+{
+ m_flRagdollSinkStart = -1;
+ m_bInitialized = false;
+ m_bCreatedWhilePlaybackSkipping = engine->IsSkippingPlayback();
+}
+
+C_CSRagdoll::~C_CSRagdoll()
+{
+ PhysCleanupFrictionSounds( this );
+}
+
+bool C_CSRagdoll::GetRagdollInitBoneArrays( matrix3x4_t *pDeltaBones0, matrix3x4_t *pDeltaBones1, matrix3x4_t *pCurrentBones, float boneDt )
+{
+ // otherwise use the death pose to set up the ragdoll
+ ForceSetupBonesAtTime( pDeltaBones0, gpGlobals->curtime - boneDt );
+ GetRagdollCurSequenceWithDeathPose( this, pDeltaBones1, gpGlobals->curtime, m_iDeathPose, m_iDeathFrame );
+ return SetupBones( pCurrentBones, MAXSTUDIOBONES, BONE_USED_BY_ANYTHING, gpGlobals->curtime );
+}
+
+void C_CSRagdoll::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_CSRagdoll::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_CSRagdoll::ValidateModelIndex( void )
+{
+ if ( sv_allowminmodels.GetBool() && cl_minmodels.GetBool() )
+ {
+ if ( GetTeamNumber() == TEAM_CT )
+ {
+ int index = cl_min_ct.GetInt() - 1;
+ if ( index >= 0 && index < CTPlayerModels.Count() )
+ {
+ m_nModelIndex = modelinfo->GetModelIndex(CTPlayerModels[index]);
+ }
+ }
+ else if ( GetTeamNumber() == TEAM_TERRORIST )
+ {
+ int index = cl_min_t.GetInt() - 1;
+ if ( index >= 0 && index < TerroristPlayerModels.Count() )
+ {
+ m_nModelIndex = modelinfo->GetModelIndex(TerroristPlayerModels[index]);
+ }
+ }
+ }
+
+ BaseClass::ValidateModelIndex();
+}
+
+
+void C_CSRagdoll::CreateLowViolenceRagdoll( void )
+{
+ // Just play a death animation.
+ // Find a death anim to play.
+ int iMinDeathAnim = 9999, iMaxDeathAnim = -9999;
+ for ( int iAnim=1; iAnim < 100; iAnim++ )
+ {
+ char str[512];
+ Q_snprintf( str, sizeof( str ), "death%d", iAnim );
+ if ( LookupSequence( str ) == -1 )
+ break;
+
+ iMinDeathAnim = MIN( iMinDeathAnim, iAnim );
+ iMaxDeathAnim = MAX( iMaxDeathAnim, iAnim );
+ }
+
+ if ( iMinDeathAnim == 9999 )
+ {
+ CreateCSRagdoll();
+ return;
+ }
+
+ SetNetworkOrigin( m_vecRagdollOrigin );
+ SetAbsOrigin( m_vecRagdollOrigin );
+ SetAbsVelocity( m_vecRagdollVelocity );
+
+ C_CSPlayer *pPlayer = dynamic_cast< C_CSPlayer* >( m_hPlayer.Get() );
+ if ( pPlayer )
+ {
+ if ( !pPlayer->IsDormant() )
+ {
+ // move my current model instance to the ragdoll's so decals are preserved.
+ pPlayer->SnatchModelInstance( this );
+ }
+
+ SetAbsAngles( pPlayer->GetRenderAngles() );
+ SetNetworkAngles( pPlayer->GetRenderAngles() );
+ }
+
+ int iDeathAnim = RandomInt( iMinDeathAnim, iMaxDeathAnim );
+ char str[512];
+ Q_snprintf( str, sizeof( str ), "death%d", iDeathAnim );
+ SetSequence( LookupSequence( str ) );
+ ForceClientSideAnimationOn();
+
+ Interp_Reset( GetVarMapping() );
+}
+
+
+void C_CSRagdoll::CreateCSRagdoll()
+{
+ // 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_CSPlayer *pPlayer = dynamic_cast< C_CSPlayer* >( m_hPlayer.Get() );
+
+ // mark this to prevent model changes from overwriting the death sequence with the server sequence
+ SetReceivedSequence();
+
+ 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 );
+
+ // go ahead and set these on the player in case the code below decides to set up bones using
+ // that entity instead of this one. The local player may not have valid animation
+ pPlayer->SetSequence( iSeq ); // walk_lower, basic pose
+ pPlayer->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() );
+ }
+
+ // Turn it into a ragdoll.
+ if ( cl_ragdoll_physics_enable.GetInt() )
+ {
+ // Make us a ragdoll..
+ m_nRenderFX = kRenderFxRagdoll;
+
+ matrix3x4_t boneDelta0[MAXSTUDIOBONES];
+ matrix3x4_t boneDelta1[MAXSTUDIOBONES];
+ matrix3x4_t currentBones[MAXSTUDIOBONES];
+ const float boneDt = 0.05f;
+
+ //=============================================================================
+ // [pfreese], [tj]
+ // There are visual problems with the attempted blending of the
+ // death pose animations in C_CSRagdoll::GetRagdollInitBoneArrays. The version
+ // in C_BasePlayer::GetRagdollInitBoneArrays doesn't attempt to blend death
+ // poses, so if the player is relevant, use that one regardless of whether the
+ // player is the local one or not.
+ //=============================================================================
+ if ( pPlayer && !pPlayer->IsDormant() )
+ {
+ pPlayer->GetRagdollInitBoneArrays( boneDelta0, boneDelta1, currentBones, boneDt );
+ }
+ else
+ {
+ GetRagdollInitBoneArrays( boneDelta0, boneDelta1, currentBones, boneDt );
+ }
+
+ InitAsClientRagdoll( boneDelta0, boneDelta1, currentBones, boneDt );
+ m_flRagdollSinkStart = -1;
+ }
+ else
+ {
+ m_flRagdollSinkStart = gpGlobals->curtime;
+ DestroyShadow();
+ ClientLeafSystem()->SetRenderGroup( GetRenderHandle(), RENDER_GROUP_TRANSLUCENT_ENTITY );
+ }
+ m_bInitialized = true;
+}
+
+
+void C_CSRagdoll::ComputeFxBlend( void )
+{
+ if ( m_flRagdollSinkStart == -1 )
+ {
+ BaseClass::ComputeFxBlend();
+ }
+ else
+ {
+ float elapsed = gpGlobals->curtime - m_flRagdollSinkStart;
+ float flVal = RemapVal( elapsed, 0, g_flDieTranslucentTime, 255, 0 );
+ flVal = clamp( flVal, 0, 255 );
+ m_nRenderFXBlend = (int)flVal;
+
+#ifdef _DEBUG
+ m_nFXComputeFrame = gpGlobals->framecount;
+#endif
+ }
+}
+
+
+bool C_CSRagdoll::IsTransparent( void )
+{
+ if ( m_flRagdollSinkStart == -1 )
+ {
+ return BaseClass::IsTransparent();
+ }
+ else
+ {
+ return true;
+ }
+}
+
+
+void C_CSRagdoll::OnDataChanged( DataUpdateType_t type )
+{
+ BaseClass::OnDataChanged( type );
+
+ if ( type == DATA_UPDATE_CREATED )
+ {
+ // Prevent replays from creating ragdolls on the first frame of playback after skipping through playback.
+ // If a player died (leaving a ragdoll) previous to the first frame of replay playback,
+ // their ragdoll wasn't yet initialized because OnDataChanged events are queued but not processed
+ // until the first render.
+ if ( engine->IsPlayingDemo() && m_bCreatedWhilePlaybackSkipping )
+ {
+ Release();
+ return;
+ }
+
+ if ( g_RagdollLVManager.IsLowViolence() )
+ {
+ CreateLowViolenceRagdoll();
+ }
+ else
+ {
+ CreateCSRagdoll();
+ }
+ }
+ else
+ {
+ if ( !cl_ragdoll_physics_enable.GetInt() )
+ {
+ // Don't let it set us back to a ragdoll with data from the server.
+ m_nRenderFX = kRenderFxNone;
+ }
+ }
+}
+
+IRagdoll* C_CSRagdoll::GetIRagdoll() const
+{
+ return m_pRagdoll;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Called when the player toggles nightvision
+// Input : *pData - the int value of the nightvision state
+// *pStruct - the player
+// *pOut -
+//-----------------------------------------------------------------------------
+void RecvProxy_NightVision( const CRecvProxyData *pData, void *pStruct, void *pOut )
+{
+ C_CSPlayer *pPlayerData = (C_CSPlayer *) pStruct;
+
+ bool bNightVisionOn = ( pData->m_Value.m_Int > 0 );
+
+ if ( pPlayerData->m_bNightVisionOn != bNightVisionOn )
+ {
+ if ( bNightVisionOn )
+ pPlayerData->m_flNightVisionAlpha = 1;
+ }
+
+ pPlayerData->m_bNightVisionOn = bNightVisionOn;
+}
+
+void RecvProxy_FlashTime( const CRecvProxyData *pData, void *pStruct, void *pOut )
+{
+ C_CSPlayer *pPlayerData = (C_CSPlayer *) pStruct;
+
+ if( pPlayerData != C_BasePlayer::GetLocalPlayer() )
+ return;
+
+ if ( (pPlayerData->m_flFlashDuration != pData->m_Value.m_Float) && pData->m_Value.m_Float > 0 )
+ {
+ pPlayerData->m_flFlashAlpha = 1;
+ }
+
+ pPlayerData->m_flFlashDuration = pData->m_Value.m_Float;
+ pPlayerData->m_flFlashBangTime = gpGlobals->curtime + pPlayerData->m_flFlashDuration;
+}
+
+void RecvProxy_HasDefuser( const CRecvProxyData *pData, void *pStruct, void *pOut )
+{
+ C_CSPlayer *pPlayerData = (C_CSPlayer *)pStruct;
+
+ if (pPlayerData == NULL)
+ {
+ return;
+ }
+
+ bool drawIcon = false;
+
+ if (pData->m_Value.m_Int == 0)
+ {
+ pPlayerData->RemoveDefuser();
+ }
+ else
+ {
+ if (pPlayerData->HasDefuser() == false)
+ {
+ drawIcon = true;
+ }
+ pPlayerData->GiveDefuser();
+ }
+
+ if (pPlayerData->IsLocalPlayer() && drawIcon)
+ {
+ // add to pickup history
+ CHudHistoryResource *pHudHR = GET_HUDELEMENT( CHudHistoryResource );
+
+ if ( pHudHR )
+ {
+ pHudHR->AddToHistory(HISTSLOT_ITEM, "defuser_pickup");
+ }
+ }
+}
+
+void C_CSPlayer::RecvProxy_CycleLatch( const CRecvProxyData *pData, void *pStruct, void *pOut )
+{
+ // This receive proxy looks to see if the server's value is close enough to what we think it should
+ // be. We've been running the same code; this is an error correction for changes we didn't simulate
+ // while they were out of PVS.
+ C_CSPlayer *pPlayer = (C_CSPlayer *)pStruct;
+ if( pPlayer->IsLocalPlayer() )
+ return; // Don't need to fixup ourselves.
+
+ float incomingCycle = (float)(pData->m_Value.m_Int) / 16; // Came in as 4 bit fixed point
+ float currentCycle = pPlayer->GetCycle();
+ bool closeEnough = fabs(currentCycle - incomingCycle) < CycleLatchTolerance;
+ if( fabs(currentCycle - incomingCycle) > (1 - CycleLatchTolerance) )
+ {
+ closeEnough = true;// Handle wrapping around 1->0
+ }
+
+ if( !closeEnough )
+ {
+ // Server disagrees too greatly. Correct our value.
+ if ( pPlayer && pPlayer->GetTeam() )
+ {
+ DevMsg( 2, "%s %s(%d): Cycle latch wants to correct %.2f in to %.2f.\n",
+ pPlayer->GetTeam()->Get_Name(), pPlayer->GetPlayerName(), pPlayer->entindex(), currentCycle, incomingCycle );
+ }
+ pPlayer->SetServerIntendedCycle( incomingCycle );
+ }
+}
+
+void __MsgFunc_ReloadEffect( bf_read &msg )
+{
+ int iPlayer = msg.ReadShort();
+ C_CSPlayer *pPlayer = dynamic_cast< C_CSPlayer* >( C_BaseEntity::Instance( iPlayer ) );
+ if ( pPlayer )
+ pPlayer->PlayReloadEffect();
+
+}
+USER_MESSAGE_REGISTER( ReloadEffect );
+
+BEGIN_RECV_TABLE_NOBASE( C_CSPlayer, DT_CSLocalPlayerExclusive )
+ RecvPropFloat( RECVINFO(m_flStamina) ),
+ RecvPropInt( RECVINFO( m_iDirection ) ),
+ RecvPropInt( RECVINFO( m_iShotsFired ) ),
+ RecvPropFloat( RECVINFO( m_flVelocityModifier ) ),
+
+ RecvPropVector( RECVINFO_NAME( m_vecNetworkOrigin, m_vecOrigin ) ),
+
+ //=============================================================================
+ // HPE_BEGIN:
+ // [tj]Set up the receive table for per-client domination data
+ //=============================================================================
+
+ RecvPropArray3( RECVINFO_ARRAY( m_bPlayerDominated ), RecvPropBool( RECVINFO( m_bPlayerDominated[0] ) ) ),
+ RecvPropArray3( RECVINFO_ARRAY( m_bPlayerDominatingMe ), RecvPropBool( RECVINFO( m_bPlayerDominatingMe[0] ) ) )
+
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+
+END_RECV_TABLE()
+
+
+BEGIN_RECV_TABLE_NOBASE( C_CSPlayer, DT_CSNonLocalPlayerExclusive )
+ RecvPropVector( RECVINFO_NAME( m_vecNetworkOrigin, m_vecOrigin ) ),
+END_RECV_TABLE()
+
+
+IMPLEMENT_CLIENTCLASS_DT( C_CSPlayer, DT_CSPlayer, CCSPlayer )
+ RecvPropDataTable( "cslocaldata", 0, 0, &REFERENCE_RECV_TABLE(DT_CSLocalPlayerExclusive) ),
+ RecvPropDataTable( "csnonlocaldata", 0, 0, &REFERENCE_RECV_TABLE(DT_CSNonLocalPlayerExclusive) ),
+ RecvPropInt( RECVINFO( m_iAddonBits ) ),
+ RecvPropInt( RECVINFO( m_iPrimaryAddon ) ),
+ RecvPropInt( RECVINFO( m_iSecondaryAddon ) ),
+ RecvPropInt( RECVINFO( m_iThrowGrenadeCounter ) ),
+ RecvPropInt( RECVINFO( m_iPlayerState ) ),
+ RecvPropInt( RECVINFO( m_iAccount ) ),
+ RecvPropInt( RECVINFO( m_bInBombZone ) ),
+ RecvPropInt( RECVINFO( m_bInBuyZone ) ),
+ RecvPropInt( RECVINFO( m_iClass ) ),
+ RecvPropInt( RECVINFO( m_ArmorValue ) ),
+ RecvPropFloat( RECVINFO( m_angEyeAngles[0] ) ),
+ RecvPropFloat( RECVINFO( m_angEyeAngles[1] ) ),
+ RecvPropFloat( RECVINFO( m_flStamina ) ),
+ RecvPropInt( RECVINFO( m_bHasDefuser ), 0, RecvProxy_HasDefuser ),
+ RecvPropInt( RECVINFO( m_bNightVisionOn), 0, RecvProxy_NightVision ),
+ RecvPropBool( RECVINFO( m_bHasNightVision ) ),
+
+
+ //=============================================================================
+ // HPE_BEGIN:
+ // [dwenger] Added for fun-fact support
+ //=============================================================================
+
+ //RecvPropBool( RECVINFO( m_bPickedUpDefuser ) ),
+ //RecvPropBool( RECVINFO( m_bDefusedWithPickedUpKit ) ),
+
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+
+ RecvPropBool( RECVINFO( m_bInHostageRescueZone ) ),
+ RecvPropInt( RECVINFO( m_ArmorValue ) ),
+ RecvPropBool( RECVINFO( m_bIsDefusing ) ),
+ RecvPropBool( RECVINFO( m_bResumeZoom ) ),
+ RecvPropInt( RECVINFO( m_iLastZoom ) ),
+
+#ifdef CS_SHIELD_ENABLED
+ RecvPropBool( RECVINFO( m_bHasShield ) ),
+ RecvPropBool( RECVINFO( m_bShieldDrawn ) ),
+#endif
+ RecvPropInt( RECVINFO( m_bHasHelmet ) ),
+ RecvPropVector( RECVINFO( m_vecRagdollVelocity ) ),
+ RecvPropFloat( RECVINFO( m_flFlashDuration ), 0, RecvProxy_FlashTime ),
+ RecvPropFloat( RECVINFO( m_flFlashMaxAlpha)),
+ RecvPropInt( RECVINFO( m_iProgressBarDuration ) ),
+ RecvPropFloat( RECVINFO( m_flProgressBarStartTime ) ),
+ RecvPropEHandle( RECVINFO( m_hRagdoll ) ),
+ RecvPropInt( RECVINFO( m_cycleLatch ), 0, &C_CSPlayer::RecvProxy_CycleLatch ),
+
+END_RECV_TABLE()
+
+
+
+C_CSPlayer::C_CSPlayer() :
+ m_iv_angEyeAngles( "C_CSPlayer::m_iv_angEyeAngles" )
+{
+ m_PlayerAnimState = CreatePlayerAnimState( this, this, LEGANIM_9WAY, true );
+
+ m_angEyeAngles.Init();
+
+ AddVar( &m_angEyeAngles, &m_iv_angEyeAngles, LATCH_SIMULATION_VAR );
+
+ m_iLastAddonBits = m_iAddonBits = 0;
+ m_iLastPrimaryAddon = m_iLastSecondaryAddon = WEAPON_NONE;
+ m_iProgressBarDuration = 0;
+ m_flProgressBarStartTime = 0.0f;
+ m_ArmorValue = 0;
+ m_bHasHelmet = false;
+ m_iIDEntIndex = 0;
+ m_delayTargetIDTimer.Reset();
+ m_iOldIDEntIndex = 0;
+ m_holdTargetIDTimer.Reset();
+ m_iDirection = 0;
+
+ m_Activity = ACT_IDLE;
+
+ m_pFlashlightBeam = NULL;
+ m_fNextThinkPushAway = 0.0f;
+
+ m_serverIntendedCycle = -1.0f;
+
+ view->SetScreenOverlayMaterial( NULL );
+
+ m_bPlayingFreezeCamSound = false;
+}
+
+
+C_CSPlayer::~C_CSPlayer()
+{
+ RemoveAddonModels();
+
+ ReleaseFlashlight();
+
+ m_PlayerAnimState->Release();
+}
+
+
+bool C_CSPlayer::HasDefuser() const
+{
+ return m_bHasDefuser;
+}
+
+void C_CSPlayer::GiveDefuser()
+{
+ m_bHasDefuser = true;
+}
+
+void C_CSPlayer::RemoveDefuser()
+{
+ m_bHasDefuser = false;
+}
+
+bool C_CSPlayer::HasNightVision() const
+{
+ return m_bHasNightVision;
+}
+
+bool C_CSPlayer::IsVIP() const
+{
+ C_CS_PlayerResource *pCSPR = (C_CS_PlayerResource*)GameResources();
+
+ if ( !pCSPR )
+ return false;
+
+ return pCSPR->IsVIP( entindex() );
+}
+
+C_CSPlayer* C_CSPlayer::GetLocalCSPlayer()
+{
+ return (C_CSPlayer*)C_BasePlayer::GetLocalPlayer();
+}
+
+
+CSPlayerState C_CSPlayer::State_Get() const
+{
+ return m_iPlayerState;
+}
+
+
+float C_CSPlayer::GetMinFOV() const
+{
+ // Min FOV for AWP.
+ return 10;
+}
+
+
+int C_CSPlayer::GetAccount() const
+{
+ return m_iAccount;
+}
+
+
+int C_CSPlayer::PlayerClass() const
+{
+ return m_iClass;
+}
+
+bool C_CSPlayer::IsInBuyZone()
+{
+ return m_bInBuyZone;
+}
+
+bool C_CSPlayer::CanShowTeamMenu() const
+{
+ return true;
+}
+
+
+int C_CSPlayer::ArmorValue() const
+{
+ return m_ArmorValue;
+}
+
+bool C_CSPlayer::HasHelmet() const
+{
+ return m_bHasHelmet;
+}
+
+int C_CSPlayer::GetCurrentAssaultSuitPrice()
+{
+ // WARNING: This price logic also exists in CCSPlayer::AttemptToBuyAssaultSuit
+ // and must be kept in sync if changes are made.
+
+ int fullArmor = ArmorValue() >= 100 ? 1 : 0;
+ if ( fullArmor && !HasHelmet() )
+ {
+ return HELMET_PRICE;
+ }
+ else if ( !fullArmor && HasHelmet() )
+ {
+ return KEVLAR_PRICE;
+ }
+ else
+ {
+ // NOTE: This applies to the case where you already have both
+ // as well as the case where you have neither. In the case
+ // where you have both, the item should still have a price
+ // and become disabled when you have little or no money left.
+ return ASSAULTSUIT_PRICE;
+ }
+}
+
+const QAngle& C_CSPlayer::GetRenderAngles()
+{
+ if ( IsRagdoll() )
+ {
+ return vec3_angle;
+ }
+ else
+ {
+ return m_PlayerAnimState->GetRenderAngles();
+ }
+}
+
+
+float g_flFattenAmt = 4;
+void C_CSPlayer::GetShadowRenderBounds( Vector &mins, Vector &maxs, ShadowType_t shadowType )
+{
+ if ( shadowType == SHADOWS_SIMPLE )
+ {
+ // Don't let the render bounds change when we're using blobby shadows, or else the shadow
+ // will pop and stretch.
+ mins = CollisionProp()->OBBMins();
+ maxs = CollisionProp()->OBBMaxs();
+ }
+ else
+ {
+ GetRenderBounds( mins, maxs );
+
+ // We do this because the normal bbox calculations don't take pose params into account, and
+ // the rotation of the guy's upper torso can place his gun a ways out of his bbox, and
+ // the shadow will get cut off as he rotates.
+ //
+ // Thus, we give it some padding here.
+ mins -= Vector( g_flFattenAmt, g_flFattenAmt, 0 );
+ maxs += Vector( g_flFattenAmt, g_flFattenAmt, 0 );
+ }
+}
+
+
+void C_CSPlayer::GetRenderBounds( Vector& theMins, Vector& theMaxs )
+{
+ // TODO POSTSHIP - this hack/fix goes hand-in-hand with a fix in CalcSequenceBoundingBoxes in utils/studiomdl/simplify.cpp.
+ // When we enable the fix in CalcSequenceBoundingBoxes, we can get rid of this.
+ //
+ // What we're doing right here is making sure it only uses the bbox for our lower-body sequences since,
+ // with the current animations and the bug in CalcSequenceBoundingBoxes, are WAY bigger than they need to be.
+ C_BaseAnimating::GetRenderBounds( theMins, theMaxs );
+
+ // If we're ducking, we should reduce the render height by the difference in standing and ducking heights.
+ // This prevents shadows from drawing above ducking players etc.
+ if ( GetFlags() & FL_DUCKING )
+ {
+ theMaxs.z -= 18.5f;
+ }
+}
+
+
+bool C_CSPlayer::GetShadowCastDirection( Vector *pDirection, ShadowType_t shadowType ) const
+{
+ if ( shadowType == SHADOWS_SIMPLE )
+ {
+ // Blobby shadows should sit directly underneath us.
+ pDirection->Init( 0, 0, -1 );
+ return true;
+ }
+ else
+ {
+ return BaseClass::GetShadowCastDirection( pDirection, shadowType );
+ }
+}
+
+
+void C_CSPlayer::VPhysicsUpdate( IPhysicsObject *pPhysics )
+{
+ BaseClass::VPhysicsUpdate( pPhysics );
+}
+
+
+int C_CSPlayer::GetIDTarget() const
+{
+ if ( !m_delayTargetIDTimer.IsElapsed() )
+ return 0;
+
+ if ( m_iIDEntIndex )
+ {
+ return m_iIDEntIndex;
+ }
+
+ if ( m_iOldIDEntIndex && !m_holdTargetIDTimer.IsElapsed() )
+ {
+ return m_iOldIDEntIndex;
+ }
+
+ return 0;
+}
+
+
+void InitializeAddonModelFromWeapon( CWeaponCSBase *weapon, C_BreakableProp *addon )
+{
+ if ( !weapon )
+ {
+ return;
+ }
+
+ const CCSWeaponInfo& weaponInfo = weapon->GetCSWpnData();
+ if ( weaponInfo.m_szAddonModel[0] == 0 )
+ {
+ addon->InitializeAsClientEntity( weaponInfo.szWorldModel, RENDER_GROUP_OPAQUE_ENTITY );
+ }
+ else
+ {
+ addon->InitializeAsClientEntity( weaponInfo.m_szAddonModel, RENDER_GROUP_OPAQUE_ENTITY );
+ }
+}
+
+void C_CSPlayer::CreateAddonModel( int i )
+{
+ COMPILE_TIME_ASSERT( (sizeof( g_AddonInfo ) / sizeof( g_AddonInfo[0] )) == NUM_ADDON_BITS );
+
+ // Create the model entity.
+ CAddonInfo *pAddonInfo = &g_AddonInfo[i];
+
+ int iAttachment = LookupAttachment( pAddonInfo->m_pAttachmentName );
+ if ( iAttachment <= 0 )
+ return;
+
+ C_BreakableProp *pEnt = new C_BreakableProp;
+
+ int addonType = (1<<i);
+ if ( addonType == ADDON_PISTOL || addonType == ADDON_PRIMARY )
+ {
+ CCSWeaponInfo *weaponInfo = GetWeaponInfo( (CSWeaponID)((addonType == ADDON_PRIMARY) ? m_iPrimaryAddon.Get() : m_iSecondaryAddon.Get()) );
+ if ( !weaponInfo )
+ {
+ Warning( "C_CSPlayer::CreateAddonModel: Unable to get weapon info.\n" );
+ pEnt->Release();
+ return;
+ }
+ if ( weaponInfo->m_szAddonModel[0] == 0 )
+ {
+ pEnt->InitializeAsClientEntity( weaponInfo->szWorldModel, RENDER_GROUP_OPAQUE_ENTITY );
+ }
+ else
+ {
+ pEnt->InitializeAsClientEntity( weaponInfo->m_szAddonModel, RENDER_GROUP_OPAQUE_ENTITY );
+ }
+ }
+ else if( pAddonInfo->m_pModelName )
+ {
+ if ( addonType == ADDON_PISTOL2 && !(m_iAddonBits & ADDON_PISTOL ) )
+ {
+ pEnt->InitializeAsClientEntity( pAddonInfo->m_pHolsterName, RENDER_GROUP_OPAQUE_ENTITY );
+ }
+ else
+ {
+ pEnt->InitializeAsClientEntity( pAddonInfo->m_pModelName, RENDER_GROUP_OPAQUE_ENTITY );
+ }
+ }
+ else
+ {
+ WEAPON_FILE_INFO_HANDLE hWpnInfo = LookupWeaponInfoSlot( pAddonInfo->m_pWeaponClassName );
+ if ( hWpnInfo == GetInvalidWeaponInfoHandle() )
+ {
+ Assert( false );
+ return;
+ }
+
+ CCSWeaponInfo *pWeaponInfo = dynamic_cast< CCSWeaponInfo* >( GetFileWeaponInfoFromHandle( hWpnInfo ) );
+ if ( pWeaponInfo )
+ {
+ if ( pWeaponInfo->m_szAddonModel[0] == 0 )
+ pEnt->InitializeAsClientEntity( pWeaponInfo->szWorldModel, RENDER_GROUP_OPAQUE_ENTITY );
+ else
+ pEnt->InitializeAsClientEntity( pWeaponInfo->m_szAddonModel, RENDER_GROUP_OPAQUE_ENTITY );
+ }
+ else
+ {
+ pEnt->Release();
+ Warning( "C_CSPlayer::CreateAddonModel: Unable to get weapon info for %s.\n", pAddonInfo->m_pWeaponClassName );
+ return;
+ }
+ }
+
+ if ( Q_strcmp( pAddonInfo->m_pAttachmentName, "c4" ) )
+ {
+ // fade out all attached models except C4
+ pEnt->SetFadeMinMax( 400, 500 );
+ }
+
+ // Create the addon.
+ CAddonModel *pAddon = &m_AddonModels[m_AddonModels.AddToTail()];
+
+ pAddon->m_hEnt = pEnt;
+ pAddon->m_iAddon = i;
+ pAddon->m_iAttachmentPoint = iAttachment;
+ pEnt->SetParent( this, pAddon->m_iAttachmentPoint );
+ pEnt->SetLocalOrigin( Vector( 0, 0, 0 ) );
+ pEnt->SetLocalAngles( QAngle( 0, 0, 0 ) );
+ if ( IsLocalPlayer() )
+ {
+ pEnt->SetSolid( SOLID_NONE );
+ pEnt->RemoveEFlags( EFL_USE_PARTITION_WHEN_NOT_SOLID );
+ }
+}
+
+
+void C_CSPlayer::UpdateAddonModels()
+{
+ int iCurAddonBits = m_iAddonBits;
+
+ // Don't put addon models on the local player unless in third person.
+ if ( IsLocalPlayer() && !C_BasePlayer::ShouldDrawLocalPlayer() )
+ iCurAddonBits = 0;
+
+ // If the local player is observing this entity in first-person mode, get rid of its addons.
+ C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer();
+ if ( pPlayer && pPlayer->GetObserverMode() == OBS_MODE_IN_EYE && pPlayer->GetObserverTarget() == this )
+ iCurAddonBits = 0;
+
+ // Any changes to the attachments we should have?
+ if ( m_iLastAddonBits == iCurAddonBits &&
+ m_iLastPrimaryAddon == m_iPrimaryAddon &&
+ m_iLastSecondaryAddon == m_iSecondaryAddon )
+ {
+ return;
+ }
+
+ bool rebuildPistol2Addon = false;
+ if ( m_iSecondaryAddon == WEAPON_ELITE && ((m_iLastAddonBits ^ iCurAddonBits) & ADDON_PISTOL) != 0 )
+ {
+ rebuildPistol2Addon = true;
+ }
+ m_iLastAddonBits = iCurAddonBits;
+ m_iLastPrimaryAddon = m_iPrimaryAddon;
+ m_iLastSecondaryAddon = m_iSecondaryAddon;
+
+ // Get rid of any old models.
+ int i,iNext;
+ for ( i=m_AddonModels.Head(); i != m_AddonModels.InvalidIndex(); i = iNext )
+ {
+ iNext = m_AddonModels.Next( i );
+ CAddonModel *pModel = &m_AddonModels[i];
+
+ int addonBit = 1<<pModel->m_iAddon;
+ if ( !( iCurAddonBits & addonBit ) || (rebuildPistol2Addon && addonBit == ADDON_PISTOL2) )
+ {
+ if ( pModel->m_hEnt.Get() )
+ pModel->m_hEnt->Release();
+
+ m_AddonModels.Remove( i );
+ }
+ }
+
+ // Figure out which models we have now.
+ int curModelBits = 0;
+ FOR_EACH_LL( m_AddonModels, j )
+ {
+ curModelBits |= (1<<m_AddonModels[j].m_iAddon);
+ }
+
+ // Add any new models.
+ for ( i=0; i < NUM_ADDON_BITS; i++ )
+ {
+ if ( (iCurAddonBits & (1<<i)) && !( curModelBits & (1<<i) ) )
+ {
+ // Ok, we're supposed to have this one.
+ CreateAddonModel( i );
+ }
+ }
+}
+
+
+void C_CSPlayer::RemoveAddonModels()
+{
+ m_iAddonBits = 0;
+ UpdateAddonModels();
+}
+
+
+void C_CSPlayer::NotifyShouldTransmit( ShouldTransmitState_t state )
+{
+ // Remove all addon models if we go out of the PVS.
+ if ( state == SHOULDTRANSMIT_END )
+ {
+ RemoveAddonModels();
+
+ if( m_pFlashlightBeam != NULL )
+ {
+ ReleaseFlashlight();
+ }
+ }
+
+ BaseClass::NotifyShouldTransmit( state );
+}
+
+
+void C_CSPlayer::UpdateSoundEvents()
+{
+ int iNext;
+ for ( int i=m_SoundEvents.Head(); i != m_SoundEvents.InvalidIndex(); i = iNext )
+ {
+ iNext = m_SoundEvents.Next( i );
+
+ CCSSoundEvent *pEvent = &m_SoundEvents[i];
+ if ( gpGlobals->curtime >= pEvent->m_flEventTime )
+ {
+ CLocalPlayerFilter filter;
+ EmitSound( filter, GetSoundSourceIndex(), STRING( pEvent->m_SoundName ) );
+
+ m_SoundEvents.Remove( i );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+void C_CSPlayer::UpdateMinModels( void )
+{
+ int modelIndex = m_nModelIndex;
+
+ // cl_minmodels convar dependent on sv_allowminmodels convar
+
+ if ( !IsVIP() && sv_allowminmodels.GetBool() && cl_minmodels.GetBool() && !IsLocalPlayer() )
+ {
+ if ( GetTeamNumber() == TEAM_CT )
+ {
+ int index = cl_min_ct.GetInt() - 1;
+ if ( index >= 0 && index < CTPlayerModels.Count() )
+ {
+ modelIndex = modelinfo->GetModelIndex( CTPlayerModels[index] );
+ }
+ }
+ else if ( GetTeamNumber() == TEAM_TERRORIST )
+ {
+ int index = cl_min_t.GetInt() - 1;
+ if ( index >= 0 && index < TerroristPlayerModels.Count() )
+ {
+ modelIndex = modelinfo->GetModelIndex( TerroristPlayerModels[index] );
+ }
+ }
+ }
+
+ SetModelByIndex( modelIndex );
+}
+
+// NVNT gate for spectating.
+static bool inSpectating_Haptics = false;
+//-----------------------------------------------------------------------------
+void C_CSPlayer::ClientThink()
+{
+ BaseClass::ClientThink();
+
+ UpdateSoundEvents();
+
+ UpdateAddonModels();
+
+ UpdateIDTarget();
+
+ if ( gpGlobals->curtime >= m_fNextThinkPushAway )
+ {
+ PerformObstaclePushaway( this );
+ m_fNextThinkPushAway = gpGlobals->curtime + PUSHAWAY_THINK_INTERVAL;
+ }
+
+ // NVNT - check for spectating forces
+ if ( IsLocalPlayer() )
+ {
+ if ( GetTeamNumber() == TEAM_SPECTATOR || !this->IsAlive() || GetLocalOrInEyeCSPlayer() != this )
+ {
+ if (!inSpectating_Haptics)
+ {
+ if ( haptics )
+ haptics->SetNavigationClass("spectate");
+
+ inSpectating_Haptics = true;
+ }
+ }
+ else
+ {
+ if (inSpectating_Haptics)
+ {
+ if ( haptics )
+ haptics->SetNavigationClass("on_foot");
+
+ inSpectating_Haptics = false;
+ }
+ }
+
+ if ( m_iObserverMode == OBS_MODE_FREEZECAM )
+ {
+ //=============================================================================
+ // HPE_BEGIN:
+ // [Forrest] Added sv_disablefreezecam check
+ //=============================================================================
+ static ConVarRef sv_disablefreezecam( "sv_disablefreezecam" );
+ if ( !m_bPlayingFreezeCamSound && !cl_disablefreezecam.GetBool() && !sv_disablefreezecam.GetBool() )
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+ {
+ // Play sound
+ m_bPlayingFreezeCamSound = true;
+
+ CLocalPlayerFilter filter;
+ EmitSound_t ep;
+ ep.m_nChannel = CHAN_VOICE;
+ ep.m_pSoundName = "UI/freeze_cam.wav";
+ ep.m_flVolume = VOL_NORM;
+ ep.m_SoundLevel = SNDLVL_NORM;
+ ep.m_bEmitCloseCaption = false;
+
+ EmitSound( filter, GetSoundSourceIndex(), ep );
+ }
+ }
+ else
+ {
+ m_bPlayingFreezeCamSound = false;
+ }
+ }
+}
+
+
+void C_CSPlayer::OnDataChanged( DataUpdateType_t type )
+{
+ BaseClass::OnDataChanged( type );
+
+ if ( type == DATA_UPDATE_CREATED )
+ {
+ SetNextClientThink( CLIENT_THINK_ALWAYS );
+
+ if ( IsLocalPlayer() )
+ {
+ if ( CSGameRules() && CSGameRules()->IsBlackMarket() )
+ {
+ CSGameRules()->m_pPrices = NULL;
+ CSGameRules()->m_StringTableBlackMarket = NULL;
+ CSGameRules()->GetBlackMarketPriceList();
+
+ CSGameRules()->SetBlackMarketPrices( false );
+ }
+ }
+ }
+
+ UpdateVisibility();
+}
+
+
+void C_CSPlayer::ValidateModelIndex( void )
+{
+ UpdateMinModels();
+}
+
+
+void C_CSPlayer::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 );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool C_CSPlayer::Interpolate( float currentTime )
+{
+ if ( !BaseClass::Interpolate( currentTime ) )
+ return false;
+
+ if ( CSGameRules()->IsFreezePeriod() )
+ {
+ // don't interpolate players position during freeze period
+ SetAbsOrigin( GetNetworkOrigin() );
+ }
+
+ return true;
+}
+
+int C_CSPlayer::GetMaxHealth() const
+{
+ return 100;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Return the local player, or the player being spectated in-eye
+//-----------------------------------------------------------------------------
+C_CSPlayer* GetLocalOrInEyeCSPlayer( void )
+{
+ C_CSPlayer *player = C_CSPlayer::GetLocalCSPlayer();
+
+ if( player && player->GetObserverMode() == OBS_MODE_IN_EYE )
+ {
+ C_BaseEntity *target = player->GetObserverTarget();
+
+ if( target && target->IsPlayer() )
+ {
+ return ToCSPlayer( target );
+ }
+ }
+ return player;
+}
+
+#define MAX_FLASHBANG_OPACITY 75.0f
+
+//-----------------------------------------------------------------------------
+// Purpose: Update this client's targetid entity
+//-----------------------------------------------------------------------------
+void C_CSPlayer::UpdateIDTarget()
+{
+ if ( !IsLocalPlayer() )
+ return;
+
+ // Clear old target and find a new one
+ m_iIDEntIndex = 0;
+
+ // don't show IDs if mp_playerid == 2
+ if ( mp_playerid.GetInt() == 2 )
+ return;
+
+ // don't show IDs if mp_fadetoblack is on
+ if ( mp_fadetoblack.GetBool() && !IsAlive() )
+ return;
+
+ // don't show IDs in chase spec mode
+ if ( GetObserverMode() == OBS_MODE_CHASE ||
+ GetObserverMode() == OBS_MODE_DEATHCAM )
+ return;
+
+ //Check how much of a screen fade we have.
+ //if it's more than 75 then we can't see what's going on so we don't display the id.
+ byte color[4];
+ bool blend;
+ vieweffects->GetFadeParams( &color[0], &color[1], &color[2], &color[3], &blend );
+
+ if ( color[3] > MAX_FLASHBANG_OPACITY && ( IsAlive() || GetObserverMode() == OBS_MODE_IN_EYE ) )
+ return;
+
+ trace_t tr;
+ Vector vecStart, vecEnd;
+ VectorMA( MainViewOrigin(), 2500, MainViewForward(), vecEnd );
+ VectorMA( MainViewOrigin(), 10, MainViewForward(), vecStart );
+ UTIL_TraceLine( vecStart, vecEnd, MASK_VISIBLE_AND_NPCS, GetLocalOrInEyeCSPlayer(), COLLISION_GROUP_NONE, &tr );
+ if ( !tr.startsolid && !tr.DidHitNonWorldEntity() )
+ {
+ CTraceFilterSimple filter( GetLocalOrInEyeCSPlayer(), COLLISION_GROUP_NONE );
+
+ // Check for player hitboxes extending outside their collision bounds
+ const float rayExtension = 40.0f;
+ UTIL_ClipTraceToPlayers(vecStart, vecEnd + MainViewForward() * rayExtension, MASK_SOLID|CONTENTS_HITBOX, &filter, &tr );
+ }
+
+ if ( !tr.startsolid && tr.DidHitNonWorldEntity() )
+ {
+ C_BaseEntity *pEntity = tr.m_pEnt;
+
+ if ( pEntity && (pEntity != this) )
+ {
+ if ( mp_playerid.GetInt() == 1 ) // only show team names
+ {
+ if ( pEntity->GetTeamNumber() != GetTeamNumber() )
+ {
+ return;
+ }
+ }
+
+ //Adrian: If there's a smoke cloud in my way, don't display the name
+ //We check this AFTER we found a player, just so we don't go thru this for nothing.
+ for ( int i = 0; i < m_SmokeGrenades.Count(); i++ )
+ {
+ C_BaseParticleEntity *pSmokeGrenade = (C_BaseParticleEntity*)m_SmokeGrenades.Element( i );
+
+ if ( pSmokeGrenade )
+ {
+ float flHit1, flHit2;
+
+ float flRadius = ( SMOKEGRENADE_PARTICLERADIUS * NUM_PARTICLES_PER_DIMENSION + 1 ) * 0.5f;
+
+ Vector vPos = pSmokeGrenade->GetAbsOrigin();
+
+ /*debugoverlay->AddBoxOverlay( pSmokeGrenade->GetAbsOrigin(), Vector( flRadius, flRadius, flRadius ),
+ Vector( -flRadius, -flRadius, -flRadius ), QAngle( 0, 0, 0 ), 255, 0, 0, 255, 0.2 );*/
+
+ if ( IntersectInfiniteRayWithSphere( MainViewOrigin(), MainViewForward(), vPos, flRadius, &flHit1, &flHit2 ) )
+ {
+ return;
+ }
+ }
+ }
+
+ if ( !GetIDTarget() && ( !m_iOldIDEntIndex || m_holdTargetIDTimer.IsElapsed() ) )
+ {
+ // track when we first mouse over the target
+ m_delayTargetIDTimer.Start( mp_playerid_delay.GetFloat() );
+ }
+ m_iIDEntIndex = pEntity->entindex();
+
+ m_iOldIDEntIndex = m_iIDEntIndex;
+ m_holdTargetIDTimer.Start( mp_playerid_hold.GetFloat() );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Input handling
+//-----------------------------------------------------------------------------
+bool C_CSPlayer::CreateMove( float flInputSampleTime, CUserCmd *pCmd )
+{
+ // Bleh... we will wind up needing to access bones for attachments in here.
+ C_BaseAnimating::AutoAllowBoneAccess boneaccess( true, true );
+
+ return BaseClass::CreateMove( flInputSampleTime, pCmd );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Flash this entity on the radar
+//-----------------------------------------------------------------------------
+bool C_CSPlayer::IsInHostageRescueZone()
+{
+ return m_bInHostageRescueZone;
+}
+
+CWeaponCSBase* C_CSPlayer::GetActiveCSWeapon() const
+{
+ return dynamic_cast< CWeaponCSBase* >( GetActiveWeapon() );
+}
+
+CWeaponCSBase* C_CSPlayer::GetCSWeapon( CSWeaponID id ) const
+{
+ for (int i=0;i<MAX_WEAPONS;i++)
+ {
+ CBaseCombatWeapon *weapon = GetWeapon( i );
+ if ( weapon )
+ {
+ CWeaponCSBase *csWeapon = dynamic_cast< CWeaponCSBase * >( weapon );
+ if ( csWeapon )
+ {
+ if ( id == csWeapon->GetWeaponID() )
+ {
+ return csWeapon;
+ }
+ }
+ }
+ }
+
+ return NULL;
+}
+
+//REMOVEME
+/*
+void C_CSPlayer::SetFireAnimation( PLAYER_ANIM playerAnim )
+{
+ Activity idealActivity = ACT_WALK;
+
+ // Figure out stuff about the current state.
+ float speed = GetAbsVelocity().Length2D();
+ bool isMoving = ( speed != 0.0f ) ? true : false;
+ bool isDucked = ( GetFlags() & FL_DUCKING ) ? true : false;
+ bool isStillJumping = false; //!( GetFlags() & FL_ONGROUND );
+ bool isRunning = false;
+
+ if ( speed > ARBITRARY_RUN_SPEED )
+ {
+ isRunning = true;
+ }
+
+ // Now figure out what to do based on the current state and the new state.
+ switch ( playerAnim )
+ {
+ default:
+ case PLAYER_RELOAD:
+ case PLAYER_ATTACK1:
+ case PLAYER_IDLE:
+ case PLAYER_WALK:
+ // Are we still jumping?
+ // If so, keep playing the jump animation.
+ if ( !isStillJumping )
+ {
+ idealActivity = ACT_WALK;
+
+ if ( isDucked )
+ {
+ idealActivity = !isMoving ? ACT_CROUCHIDLE : ACT_RUN_CROUCH;
+ }
+ else
+ {
+ if ( isRunning )
+ {
+ idealActivity = ACT_RUN;
+ }
+ else
+ {
+ idealActivity = isMoving ? ACT_WALK : ACT_IDLE;
+ }
+ }
+
+ // Allow body yaw to override for standing and turning in place
+ idealActivity = m_PlayerAnimState.BodyYawTranslateActivity( idealActivity );
+ }
+ break;
+
+ case PLAYER_JUMP:
+ idealActivity = ACT_HOP;
+ break;
+
+ case PLAYER_DIE:
+ // Uses Ragdoll now???
+ idealActivity = ACT_DIESIMPLE;
+ break;
+
+ // FIXME: Use overlays for reload, start/leave aiming, attacking
+ case PLAYER_START_AIMING:
+ case PLAYER_LEAVE_AIMING:
+ idealActivity = ACT_WALK;
+ break;
+ }
+
+ CWeaponCSBase *pWeapon = GetActiveCSWeapon();
+
+ if ( pWeapon )
+ {
+ Activity aWeaponActivity = idealActivity;
+
+ if ( playerAnim == PLAYER_ATTACK1 )
+ {
+ switch ( idealActivity )
+ {
+ case ACT_WALK:
+ default:
+ aWeaponActivity = ACT_PLAYER_WALK_FIRE;
+ break;
+ case ACT_RUN:
+ aWeaponActivity = ACT_PLAYER_RUN_FIRE;
+ break;
+ case ACT_IDLE:
+ aWeaponActivity = ACT_PLAYER_IDLE_FIRE;
+ break;
+ case ACT_CROUCHIDLE:
+ aWeaponActivity = ACT_PLAYER_CROUCH_FIRE;
+ break;
+ case ACT_RUN_CROUCH:
+ aWeaponActivity = ACT_PLAYER_CROUCH_WALK_FIRE;
+ break;
+ }
+ }
+
+ m_PlayerAnimState.SetWeaponLayerSequence( pWeapon->GetCSWpnData().m_szAnimExtension, aWeaponActivity );
+ }
+}
+*/
+
+ShadowType_t C_CSPlayer::ShadowCastType( void )
+{
+ if ( !IsVisible() )
+ return SHADOWS_NONE;
+
+ return SHADOWS_RENDER_TO_TEXTURE_DYNAMIC;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns whether or not we can switch to the given weapon.
+// Input : pWeapon -
+//-----------------------------------------------------------------------------
+bool C_CSPlayer::Weapon_CanSwitchTo( CBaseCombatWeapon *pWeapon )
+{
+ if ( !pWeapon->CanDeploy() )
+ return false;
+
+ if ( GetActiveWeapon() )
+ {
+ if ( !GetActiveWeapon()->CanHolster() )
+ return false;
+ }
+
+ return true;
+}
+
+
+void C_CSPlayer::UpdateClientSideAnimation()
+{
+ // We do this in a different order than the base class.
+ // We need our cycle to be valid for when we call the playeranimstate update code,
+ // or else it'll synchronize the upper body anims with the wrong cycle.
+ if ( GetSequence() != -1 )
+ {
+ // move frame forward
+ FrameAdvance( 0.0f ); // 0 means to use the time we last advanced instead of a constant
+ }
+
+ // 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_CSPlayer::GetLocalCSPlayer() )
+ m_PlayerAnimState->Update( EyeAngles()[YAW], m_angEyeAngles[PITCH] );
+ else
+ m_PlayerAnimState->Update( m_angEyeAngles[YAW], m_angEyeAngles[PITCH] );
+
+ if ( GetSequence() != -1 )
+ {
+ // latch old values
+ OnLatchInterpolatedVariables( LATCH_ANIMATION_VAR );
+ }
+}
+
+
+float g_flMuzzleFlashScale=1;
+
+void C_CSPlayer::ProcessMuzzleFlashEvent()
+{
+ CBasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer();
+
+ // Reenable when the weapons have muzzle flash attachments in the right spot.
+ if ( this == pLocalPlayer )
+ return; // don't show own world muzzle flashs in for localplayer
+
+ if ( pLocalPlayer && pLocalPlayer->GetObserverMode() == OBS_MODE_IN_EYE )
+ {
+ // also don't show in 1st person spec mode
+ if ( pLocalPlayer->GetObserverTarget() == this )
+ return;
+ }
+
+ CWeaponCSBase *pWeapon = GetActiveCSWeapon();
+
+ if ( !pWeapon )
+ return;
+
+ bool hasMuzzleFlash = (pWeapon->GetMuzzleFlashStyle() != CS_MUZZLEFLASH_NONE);
+
+ Vector vector;
+ QAngle angles;
+
+ int iAttachment = LookupAttachment( "muzzle_flash" );
+
+ if ( iAttachment >= 0 )
+ {
+ bool bFoundAttachment = GetAttachment( iAttachment, vector, angles );
+ // If we have an attachment, then stick a light on it.
+ if ( bFoundAttachment )
+ {
+ if ( hasMuzzleFlash )
+ {
+ dlight_t *el = effects->CL_AllocDlight( LIGHT_INDEX_MUZZLEFLASH + index );
+ el->origin = vector;
+ el->radius = 70;
+ 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;
+ }
+
+ int shellType = GetShellForAmmoType( pWeapon->GetCSWpnData().szAmmo1 );
+
+ QAngle playerAngle = EyeAngles();
+ Vector vForward, vRight, vUp;
+
+ AngleVectors( playerAngle, &vForward, &vRight, &vUp );
+
+ QAngle angVelocity;
+ Vector vVel = vRight * 100 + vUp * 20;
+ VectorAngles( vVel, angVelocity );
+
+ if ( pWeapon->GetMaxClip1() > 0 )
+ {
+ tempents->CSEjectBrass( vector, angVelocity, 120, shellType, this );
+ }
+ }
+ }
+
+ if ( hasMuzzleFlash )
+ {
+ iAttachment = pWeapon->GetMuzzleAttachment();
+
+ if ( iAttachment > 0 )
+ {
+ float flScale = pWeapon->GetCSWpnData().m_flMuzzleScale;
+ flScale *= 0.75;
+ FX_MuzzleEffectAttached( flScale, pWeapon->GetRefEHandle(), iAttachment, NULL, false );
+
+ }
+ }
+}
+
+const QAngle& C_CSPlayer::EyeAngles()
+{
+ if ( IsLocalPlayer() && !g_nKillCamMode )
+ {
+ return BaseClass::EyeAngles();
+ }
+ else
+ {
+ return m_angEyeAngles;
+ }
+}
+
+bool C_CSPlayer::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() )
+ {
+ if ( IsRagdoll() )
+ return true;
+ }
+
+ return BaseClass::ShouldDraw();
+}
+
+
+bool FindWeaponAttachmentBone( C_BaseCombatWeapon *pWeapon, int &iWeaponBone )
+{
+ if ( !pWeapon )
+ return false;
+
+ CStudioHdr *pHdr = pWeapon->GetModelPtr();
+ if ( !pHdr )
+ return false;
+
+ for ( iWeaponBone=0; iWeaponBone < pHdr->numbones(); iWeaponBone++ )
+ {
+ if ( stricmp( pHdr->pBone( iWeaponBone )->pszName(), "L_Hand_Attach" ) == 0 )
+ break;
+ }
+
+ return iWeaponBone != pHdr->numbones();
+}
+
+
+bool FindMyAttachmentBone( C_BaseAnimating *pModel, int &iBone, CStudioHdr *pHdr )
+{
+ if ( !pHdr )
+ return false;
+
+ for ( iBone=0; iBone < pHdr->numbones(); iBone++ )
+ {
+ if ( stricmp( pHdr->pBone( iBone )->pszName(), "Valvebiped.Bip01_L_Hand" ) == 0 )
+ break;
+ }
+
+ return iBone != pHdr->numbones();
+}
+
+
+inline bool IsBoneChildOf( CStudioHdr *pHdr, int iBone, int iParent )
+{
+ if ( iBone == iParent )
+ return false;
+
+ while ( iBone != -1 )
+ {
+ if ( iBone == iParent )
+ return true;
+
+ iBone = pHdr->pBone( iBone )->parent;
+ }
+ return false;
+}
+
+void ApplyDifferenceTransformToChildren(
+ C_BaseAnimating *pModel,
+ const matrix3x4_t &mSource,
+ const matrix3x4_t &mDest,
+ int iParentBone )
+{
+ CStudioHdr *pHdr = pModel->GetModelPtr();
+ if ( !pHdr )
+ return;
+
+ // Build a matrix to go from mOriginalHand to mHand.
+ // ( mDest * Inverse( mSource ) ) * mSource = mDest
+ matrix3x4_t mSourceInverse, mToDest;
+ MatrixInvert( mSource, mSourceInverse );
+ ConcatTransforms( mDest, mSourceInverse, mToDest );
+
+ // Now multiply iMyBone and all its children by mToWeaponBone.
+ for ( int i=0; i < pHdr->numbones(); i++ )
+ {
+ if ( IsBoneChildOf( pHdr, i, iParentBone ) )
+ {
+ matrix3x4_t &mCur = pModel->GetBoneForWrite( i );
+ matrix3x4_t mNew;
+ ConcatTransforms( mToDest, mCur, mNew );
+ mCur = mNew;
+ }
+ }
+}
+
+
+void GetCorrectionMatrices(
+ const matrix3x4_t &mShoulder,
+ const matrix3x4_t &mElbow,
+ const matrix3x4_t &mHand,
+ matrix3x4_t &mShoulderCorrection,
+ matrix3x4_t &mElbowCorrection
+ )
+{
+ extern void Studio_AlignIKMatrix( matrix3x4_t &mMat, const Vector &vAlignTo );
+
+ // Get the positions of each node so we can get the direction vectors.
+ Vector vShoulder, vElbow, vHand;
+ MatrixPosition( mShoulder, vShoulder );
+ MatrixPosition( mElbow, vElbow );
+ MatrixPosition( mHand, vHand );
+
+ // Get rid of the translation.
+ matrix3x4_t mOriginalShoulder = mShoulder;
+ matrix3x4_t mOriginalElbow = mElbow;
+ MatrixSetColumn( Vector( 0, 0, 0 ), 3, mOriginalShoulder );
+ MatrixSetColumn( Vector( 0, 0, 0 ), 3, mOriginalElbow );
+
+ // Let the IK code align them like it would if we did IK on the joint.
+ matrix3x4_t mAlignedShoulder = mOriginalShoulder;
+ matrix3x4_t mAlignedElbow = mOriginalElbow;
+ Studio_AlignIKMatrix( mAlignedShoulder, vElbow-vShoulder );
+ Studio_AlignIKMatrix( mAlignedElbow, vHand-vElbow );
+
+ // Figure out the transformation from the aligned bones to the original ones.
+ matrix3x4_t mInvAlignedShoulder, mInvAlignedElbow;
+ MatrixInvert( mAlignedShoulder, mInvAlignedShoulder );
+ MatrixInvert( mAlignedElbow, mInvAlignedElbow );
+
+ ConcatTransforms( mInvAlignedShoulder, mOriginalShoulder, mShoulderCorrection );
+ ConcatTransforms( mInvAlignedElbow, mOriginalElbow, mElbowCorrection );
+}
+
+
+void C_CSPlayer::BuildTransformations( CStudioHdr *pHdr, Vector *pos, Quaternion q[], const matrix3x4_t& cameraTransform, int boneMask, CBoneBitList &boneComputed )
+{
+ // First, setup our model's transformations like normal.
+ BaseClass::BuildTransformations( pHdr, pos, q, cameraTransform, boneMask, boneComputed );
+
+ if ( IsLocalPlayer() && !C_BasePlayer::ShouldDrawLocalPlayer() )
+ return;
+
+ if ( !cl_left_hand_ik.GetInt() )
+ return;
+
+ // If our current weapon has a bone named L_Hand_Attach, then we attach the player's
+ // left hand (Valvebiped.Bip01_L_Hand) to it.
+ C_BaseCombatWeapon *pWeapon = GetActiveWeapon();
+
+ if ( !pWeapon )
+ return;
+
+ // Have the weapon setup its bones.
+ pWeapon->SetupBones( NULL, 0, BONE_USED_BY_ANYTHING, gpGlobals->curtime );
+
+ int iWeaponBone = 0;
+ if ( FindWeaponAttachmentBone( pWeapon, iWeaponBone ) )
+ {
+ int iMyBone = 0;
+ if ( FindMyAttachmentBone( this, iMyBone, pHdr ) )
+ {
+ int iHand = iMyBone;
+ int iElbow = pHdr->pBone( iHand )->parent;
+ int iShoulder = pHdr->pBone( iElbow )->parent;
+ matrix3x4_t *pBones = &GetBoneForWrite( 0 );
+
+ // Store off the original hand position.
+ matrix3x4_t mSource = pBones[iHand];
+
+
+ // Figure out the rotation offset from the current shoulder and elbow bone rotations
+ // and what the IK code's alignment code is going to produce, because we'll have to
+ // re-apply that offset after the IK runs.
+ matrix3x4_t mShoulderCorrection, mElbowCorrection;
+ GetCorrectionMatrices( pBones[iShoulder], pBones[iElbow], pBones[iHand], mShoulderCorrection, mElbowCorrection );
+
+
+ // Do the IK solution.
+ Vector vHandTarget;
+ MatrixPosition( pWeapon->GetBone( iWeaponBone ), vHandTarget );
+ Studio_SolveIK( iShoulder, iElbow, iHand, vHandTarget, pBones );
+
+
+ // Now reapply the rotation correction.
+ matrix3x4_t mTempShoulder = pBones[iShoulder];
+ matrix3x4_t mTempElbow = pBones[iElbow];
+ ConcatTransforms( mTempShoulder, mShoulderCorrection, pBones[iShoulder] );
+ ConcatTransforms( mTempElbow, mElbowCorrection, pBones[iElbow] );
+
+
+ // Now apply the transformation on the hand to the fingers.
+ matrix3x4_t &mDest = GetBoneForWrite( iHand );
+ ApplyDifferenceTransformToChildren( this, mSource, mDest, iHand );
+ }
+ }
+}
+
+
+C_BaseAnimating * C_CSPlayer::BecomeRagdollOnClient()
+{
+ return NULL;
+}
+
+
+IRagdoll* C_CSPlayer::GetRepresentativeRagdoll() const
+{
+ if ( m_hRagdoll.Get() )
+ {
+ C_CSRagdoll *pRagdoll = (C_CSRagdoll*)m_hRagdoll.Get();
+
+ return pRagdoll->GetIRagdoll();
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+
+void C_CSPlayer::PlayReloadEffect()
+{
+ // Only play the effect for other players.
+ if ( this == C_CSPlayer::GetLocalCSPlayer() )
+ {
+ Assert( false ); // We shouldn't have been sent this message.
+ return;
+ }
+
+ // Get the view model for our current gun.
+ CWeaponCSBase *pWeapon = GetActiveCSWeapon();
+ if ( !pWeapon )
+ return;
+
+ // The weapon needs two models, world and view, but can only cache one. Synthesize the other.
+ const CCSWeaponInfo &info = pWeapon->GetCSWpnData();
+ const model_t *pModel = modelinfo->GetModel( modelinfo->GetModelIndex( info.szViewModel ) );
+ if ( !pModel )
+ return;
+ CStudioHdr studioHdr( modelinfo->GetStudiomodel( pModel ), mdlcache );
+ if ( !studioHdr.IsValid() )
+ return;
+
+ // Find the reload animation.
+ for ( int iSeq=0; iSeq < studioHdr.GetNumSeq(); iSeq++ )
+ {
+ mstudioseqdesc_t *pSeq = &studioHdr.pSeqdesc( iSeq );
+
+ if ( pSeq->activity == ACT_VM_RELOAD )
+ {
+ float poseParameters[MAXSTUDIOPOSEPARAM];
+ memset( poseParameters, 0, sizeof( poseParameters ) );
+ float cyclesPerSecond = Studio_CPS( &studioHdr, *pSeq, iSeq, poseParameters );
+
+ // Now read out all the sound events with their timing
+ for ( int iEvent=0; iEvent < pSeq->numevents; iEvent++ )
+ {
+ mstudioevent_t *pEvent = pSeq->pEvent( iEvent );
+
+ if ( pEvent->event == CL_EVENT_SOUND )
+ {
+ CCSSoundEvent event;
+ event.m_SoundName = pEvent->options;
+ event.m_flEventTime = gpGlobals->curtime + pEvent->cycle / cyclesPerSecond;
+ m_SoundEvents.AddToTail( event );
+ }
+ }
+
+ break;
+ }
+ }
+}
+
+void C_CSPlayer::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 );
+ }
+}
+
+void C_CSPlayer::FireEvent( const Vector& origin, const QAngle& angles, int event, const char *options )
+{
+ if( event == 7001 )
+ {
+ bool bInWater = ( enginetrace->GetPointContents(origin) & CONTENTS_WATER );
+
+ //Msg( "run event ( %d )\n", bInWater ? 1 : 0 );
+
+ if( bInWater )
+ {
+ //run splash
+ CEffectData data;
+
+ //trace up from foot position to the water surface
+ trace_t tr;
+ Vector vecTrace(0,0,1024);
+ UTIL_TraceLine( origin, origin + vecTrace, MASK_WATER, NULL, COLLISION_GROUP_NONE, &tr );
+ if ( tr.fractionleftsolid )
+ {
+ data.m_vOrigin = origin + (vecTrace * tr.fractionleftsolid);
+ }
+ else
+ {
+ data.m_vOrigin = origin;
+ }
+
+ data.m_vNormal = Vector( 0,0,1 );
+ data.m_flScale = random->RandomFloat( 4.0f, 5.0f );
+ DispatchEffect( "watersplash", data );
+ }
+ }
+ else if( event == 7002 )
+ {
+ bool bInWater = ( enginetrace->GetPointContents(origin) & CONTENTS_WATER );
+
+ //Msg( "walk event ( %d )\n", bInWater ? 1 : 0 );
+
+ if( bInWater )
+ {
+ //walk ripple
+ CEffectData data;
+
+ //trace up from foot position to the water surface
+ trace_t tr;
+ Vector vecTrace(0,0,1024);
+ UTIL_TraceLine( origin, origin + vecTrace, MASK_WATER, NULL, COLLISION_GROUP_NONE, &tr );
+ if ( tr.fractionleftsolid )
+ {
+ data.m_vOrigin = origin + (vecTrace * tr.fractionleftsolid);
+ }
+ else
+ {
+ data.m_vOrigin = origin;
+ }
+
+ data.m_vNormal = Vector( 0,0,1 );
+ data.m_flScale = random->RandomFloat( 4.0f, 7.0f );
+ DispatchEffect( "waterripple", data );
+ }
+ }
+ else
+ BaseClass::FireEvent( origin, angles, event, options );
+}
+
+
+void C_CSPlayer::SetActivity( Activity eActivity )
+{
+ m_Activity = eActivity;
+}
+
+
+Activity C_CSPlayer::GetActivity() const
+{
+ return m_Activity;
+}
+
+
+const Vector& C_CSPlayer::GetRenderOrigin( void )
+{
+ if ( m_hRagdoll.Get() )
+ {
+ C_CSRagdoll *pRagdoll = (C_CSRagdoll*)m_hRagdoll.Get();
+ if ( pRagdoll->IsInitialized() )
+ return pRagdoll->GetRenderOrigin();
+ }
+
+ return BaseClass::GetRenderOrigin();
+}
+
+
+void C_CSPlayer::Simulate( void )
+{
+ if( this != C_BasePlayer::GetLocalPlayer() )
+ {
+ if ( IsEffectActive( EF_DIMLIGHT ) )
+ {
+ QAngle eyeAngles = EyeAngles();
+ Vector vForward;
+ AngleVectors( eyeAngles, &vForward );
+
+ int iAttachment = LookupAttachment( "muzzle_flash" );
+
+ if ( iAttachment < 0 )
+ return;
+
+ Vector vecOrigin;
+ QAngle dummy;
+ GetAttachment( iAttachment, vecOrigin, dummy );
+
+ 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();
+ }
+ }
+
+ BaseClass::Simulate();
+}
+
+void C_CSPlayer::ReleaseFlashlight( void )
+{
+ if( m_pFlashlightBeam )
+ {
+ m_pFlashlightBeam->flags = 0;
+ m_pFlashlightBeam->die = gpGlobals->curtime - 1;
+
+ m_pFlashlightBeam = NULL;
+ }
+}
+
+bool C_CSPlayer::HasC4( void )
+{
+ if( this == C_CSPlayer::GetLocalPlayer() )
+ {
+ return Weapon_OwnsThisType( "weapon_c4" );
+ }
+ else
+ {
+ C_CS_PlayerResource *pCSPR = (C_CS_PlayerResource*)GameResources();
+
+ return pCSPR->HasC4( entindex() );
+ }
+}
+
+void C_CSPlayer::ImpactTrace( trace_t *pTrace, int iDamageType, const char *pCustomImpactName )
+{
+ static ConVar *violence_hblood = cvar->FindVar( "violence_hblood" );
+ if ( violence_hblood && !violence_hblood->GetBool() )
+ return;
+
+ BaseClass::ImpactTrace( pTrace, iDamageType, pCustomImpactName );
+}
+
+
+//-----------------------------------------------------------------------------
+void C_CSPlayer::CalcObserverView( Vector& eyeOrigin, QAngle& eyeAngles, float& fov )
+{
+ /**
+ * TODO: Fix this!
+ // CS:S standing eyeheight is above the collision volume, so we need to pull it
+ // down when we go into close quarters.
+ float maxEyeHeightAboveBounds = VEC_VIEW_SCALED( this ).z - VEC_HULL_MAX_SCALED( this ).z;
+ if ( GetObserverMode() == OBS_MODE_IN_EYE &&
+ maxEyeHeightAboveBounds > 0.0f &&
+ GetObserverTarget() &&
+ GetObserverTarget()->IsPlayer() )
+ {
+ const float eyeClearance = 12.0f; // eye pos must be this far below the ceiling
+
+ C_CSPlayer *target = ToCSPlayer( GetObserverTarget() );
+
+ Vector offset = eyeOrigin - GetAbsOrigin();
+
+ Vector vHullMin = VEC_HULL_MIN_SCALED( this );
+ vHullMin.z = 0.0f;
+ Vector vHullMax = VEC_HULL_MAX_SCALED( this );
+
+ Vector start = GetAbsOrigin();
+ start.z += vHullMax.z;
+ Vector end = start;
+ end.z += eyeClearance + VEC_VIEW_SCALED( this ).z - vHullMax_SCALED( this ).z;
+
+ vHullMax.z = 0.0f;
+
+ Vector fudge( 1, 1, 0 );
+ vHullMin += fudge;
+ vHullMax -= fudge;
+
+ trace_t trace;
+ Ray_t ray;
+ ray.Init( start, end, vHullMin, vHullMax );
+ UTIL_TraceRay( ray, MASK_PLAYERSOLID, target, COLLISION_GROUP_PLAYER_MOVEMENT, &trace );
+
+ if ( trace.fraction < 1.0f )
+ {
+ float est = start.z + trace.fraction * (end.z - start.z) - GetAbsOrigin().z - eyeClearance;
+ if ( ( target->GetFlags() & FL_DUCKING ) == 0 && !target->GetFallVelocity() && !target->IsDucked() )
+ {
+ offset.z = est;
+ }
+ else
+ {
+ offset.z = MIN( est, offset.z );
+ }
+ eyeOrigin.z = GetAbsOrigin().z + offset.z;
+ }
+ }
+ */
+
+ BaseClass::CalcObserverView( eyeOrigin, eyeAngles, fov );
+}
+
+//=============================================================================
+// HPE_BEGIN:
+//=============================================================================
+// [tj] checks if this player has another given player on their Steam friends list.
+bool C_CSPlayer::HasPlayerAsFriend(C_CSPlayer* player)
+{
+ if (!steamapicontext || !steamapicontext->SteamFriends() || !steamapicontext->SteamUtils() || !player)
+ {
+ return false;
+ }
+
+ player_info_t pi;
+ if ( !engine->GetPlayerInfo( player->entindex(), &pi ) )
+ {
+ return false;
+ }
+
+ if ( !pi.friendsID )
+ {
+ return false;
+ }
+
+ // check and see if they're on the local player's friends list
+ CSteamID steamID( pi.friendsID, 1, steamapicontext->SteamUtils()->GetConnectedUniverse(), k_EAccountTypeIndividual );
+ return steamapicontext->SteamFriends()->HasFriend( steamID, k_EFriendFlagImmediate);
+}
+
+// [menglish] Returns whether this player is dominating or is being dominated by the specified player
+bool C_CSPlayer::IsPlayerDominated( int iPlayerIndex )
+{
+ return m_bPlayerDominated.Get( iPlayerIndex );
+}
+
+bool C_CSPlayer::IsPlayerDominatingMe( int iPlayerIndex )
+{
+ return m_bPlayerDominatingMe.Get( iPlayerIndex );
+}
+
+
+// helper interpolation functions
+namespace Interpolators
+{
+ inline float Linear( float t ) { return t; }
+
+ inline float SmoothStep( float t )
+ {
+ t = 3 * t * t - 2.0f * t * t * t;
+ return t;
+ }
+
+ inline float SmoothStep2( float t )
+ {
+ return t * t * t * (t * (t * 6.0f - 15.0f) + 10.0f);
+ }
+
+ inline float SmoothStepStart( float t )
+ {
+ t = 0.5f * t;
+ t = 3 * t * t - 2.0f * t * t * t;
+ t = t* 2.0f;
+ return t;
+ }
+
+ inline float SmoothStepEnd( float t )
+ {
+ t = 0.5f * t + 0.5f;
+ t = 3 * t * t - 2.0f * t * t * t;
+ t = (t - 0.5f) * 2.0f;
+ return t;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Calculate the view for the player while he's in freeze frame observer mode
+//-----------------------------------------------------------------------------
+void C_CSPlayer::CalcFreezeCamView( Vector& eyeOrigin, QAngle& eyeAngles, float& fov )
+{
+ C_BaseEntity *pTarget = GetObserverTarget();
+
+ //=============================================================================
+ // HPE_BEGIN:
+ // [Forrest] Added sv_disablefreezecam check
+ //=============================================================================
+ static ConVarRef sv_disablefreezecam( "sv_disablefreezecam" );
+ if ( !pTarget || cl_disablefreezecam.GetBool() || sv_disablefreezecam.GetBool() )
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+ {
+ return CalcDeathCamView( eyeOrigin, eyeAngles, fov );
+ }
+
+ // pick a zoom camera target
+ Vector vLookAt = pTarget->GetObserverCamOrigin(); // Returns ragdoll origin if they're ragdolled
+ vLookAt += GetChaseCamViewOffset( pTarget );
+
+ // look over ragdoll, not through
+ if ( !pTarget->IsAlive() )
+ vLookAt.z += pTarget->GetBaseAnimating() ? VEC_DEAD_VIEWHEIGHT_SCALED( pTarget->GetBaseAnimating() ).z : VEC_DEAD_VIEWHEIGHT.z;
+
+ // Figure out a view position in front of the target
+ Vector vEyeOnPlane = eyeOrigin;
+ vEyeOnPlane.z = vLookAt.z;
+ Vector vToTarget = vLookAt - vEyeOnPlane;
+ VectorNormalize( vToTarget );
+
+ // goal position of camera is pulled away from target by m_flFreezeFrameDistance
+ Vector vTargetPos = vLookAt - (vToTarget * m_flFreezeFrameDistance);
+
+ // Now trace out from the target, so that we're put in front of any walls
+ trace_t trace;
+ C_BaseEntity::PushEnableAbsRecomputations( false ); // HACK don't recompute positions while doing RayTrace
+ UTIL_TraceHull( vLookAt, vTargetPos, WALL_MIN, WALL_MAX, MASK_SOLID, pTarget, COLLISION_GROUP_NONE, &trace );
+ C_BaseEntity::PopEnableAbsRecomputations();
+ if ( trace.fraction < 1.0 )
+ {
+ // The camera's going to be really close to the target. So we don't end up
+ // looking at someone's chest, aim close freezecams at the target's eyes.
+ vTargetPos = trace.endpos;
+
+ // To stop all close in views looking up at character's chins, move the view up.
+ vTargetPos.z += fabs(vLookAt.z - vTargetPos.z) * 0.85;
+ C_BaseEntity::PushEnableAbsRecomputations( false ); // HACK don't recompute positions while doing RayTrace
+ UTIL_TraceHull( vLookAt, vTargetPos, WALL_MIN, WALL_MAX, MASK_SOLID, pTarget, COLLISION_GROUP_NONE, &trace );
+ C_BaseEntity::PopEnableAbsRecomputations();
+ vTargetPos = trace.endpos;
+ }
+
+ // Look directly at the target
+ vToTarget = vLookAt - vTargetPos;
+ VectorNormalize( vToTarget );
+ VectorAngles( vToTarget, eyeAngles );
+
+ float fCurTime = gpGlobals->curtime - m_flFreezeFrameStartTime;
+ float fInterpolant = clamp( fCurTime / spec_freeze_traveltime.GetFloat(), 0.0f, 1.0f );
+ fInterpolant = Interpolators::SmoothStepEnd( fInterpolant );
+
+ // move the eye toward our killer
+ VectorLerp( m_vecFreezeFrameStart, vTargetPos, fInterpolant, eyeOrigin );
+
+ if ( fCurTime >= spec_freeze_traveltime.GetFloat() && !m_bSentFreezeFrame )
+ {
+ IGameEvent *pEvent = gameeventmanager->CreateEvent( "freezecam_started" );
+ if ( pEvent )
+ {
+ gameeventmanager->FireEventClientSide( pEvent );
+ }
+
+ m_bSentFreezeFrame = true;
+ view->FreezeFrame( spec_freeze_time.GetFloat() );
+ }
+}
+
+float C_CSPlayer::GetDeathCamInterpolationTime()
+{
+ static ConVarRef sv_disablefreezecam( "sv_disablefreezecam" );
+ if ( cl_disablefreezecam.GetBool() || sv_disablefreezecam.GetBool() || !GetObserverTarget() )
+ return spec_freeze_time.GetFloat();
+ else
+ return CS_DEATH_ANIMATION_TIME;
+
+}
+
+
+//=============================================================================
+// HPE_END
+//=============================================================================
+