summaryrefslogtreecommitdiff
path: root/game/client/dod/c_dod_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/dod/c_dod_player.cpp
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'game/client/dod/c_dod_player.cpp')
-rw-r--r--game/client/dod/c_dod_player.cpp2780
1 files changed, 2780 insertions, 0 deletions
diff --git a/game/client/dod/c_dod_player.cpp b/game/client/dod/c_dod_player.cpp
new file mode 100644
index 0000000..c28e5aa
--- /dev/null
+++ b/game/client/dod/c_dod_player.cpp
@@ -0,0 +1,2780 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//===========================================================================//
+
+#include "cbase.h"
+#include "c_dod_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"
+#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 "fx_dod_blood.h"
+#include "effect_dispatch_data.h" //for water ripple / splash effect
+#include "c_te_effect_dispatch.h" //ditto
+#include "dod_gamerules.h"
+#include <igameevents.h>
+#include "physpropclientside.h"
+#include "obstacle_pushaway.h"
+#include "prediction.h"
+#include "viewangleanim.h"
+#include "soundenvelope.h"
+#include "weapon_dodbipodgun.h"
+#include "c_dod_basegrenade.h"
+#include "dod_weapon_parse.h"
+#include "view_scene.h"
+#include "dod_headiconmanager.h"
+#include "c_world.h"
+#include "c_dod_bombtarget.h"
+#include "toolframework/itoolframework.h"
+#include "toolframework_client.h"
+#include "c_team.h"
+#include "collisionutils.h"
+#include "weapon_dodsniper.h"
+// NVNT - haptics system for spectating and grenades
+#include "haptics/haptic_utils.h"
+// NVNT - for grenade effects
+#include "weapon_dodbasegrenade.h"
+// NVNT - for planting bomb effect
+#include "weapon_dodbasebomb.h"
+
+#if defined( CDODPlayer )
+ #undef CDODPlayer
+#endif
+
+#include "iviewrender_beams.h" // flashlight beam
+
+#include "materialsystem/imesh.h" //for materials->FindMaterial
+#include "iviewrender.h" //for view->
+ConVar cl_ragdoll_physics_enable( "cl_ragdoll_physics_enable", "1", 0, "Enable/disable ragdoll physics." );
+
+ConVar cl_autoreload( "cl_autoreload", "1", FCVAR_USERINFO | FCVAR_ARCHIVE, "Set to 1 to auto reload your weapon when it is empty" );
+ConVar cl_autorezoom( "cl_autorezoom", "1", FCVAR_USERINFO | FCVAR_ARCHIVE, "When set to 1, sniper rifles and bazooka weapons will automatically raise after each shot" );
+
+#include "tier0/memdbgon.h"
+
+//======================================================
+//
+// Cold Breath Emitter - for DOD players.
+//
+class ColdBreathEmitter : public CSimpleEmitter
+{
+public:
+
+ ColdBreathEmitter( const char *pDebugName ) : CSimpleEmitter( pDebugName ) {}
+
+ static ColdBreathEmitter *Create( const char *pDebugName )
+ {
+ return new ColdBreathEmitter( pDebugName );
+ }
+
+ void UpdateVelocity( SimpleParticle *pParticle, float timeDelta )
+ {
+ // Float up when lifetime is half gone.
+ pParticle->m_vecVelocity[2] -= ( 8.0f * timeDelta );
+
+
+ // FIXME: optimize this....
+ pParticle->m_vecVelocity *= ExponentialDecay( 0.9, 0.03, timeDelta );
+ }
+
+ virtual float UpdateRoll( SimpleParticle *pParticle, float timeDelta )
+ {
+ pParticle->m_flRoll += pParticle->m_flRollDelta * timeDelta;
+
+ pParticle->m_flRollDelta += pParticle->m_flRollDelta * ( timeDelta * -2.0f );
+
+ //Cap the minimum roll
+ if ( fabs( pParticle->m_flRollDelta ) < 0.5f )
+ {
+ pParticle->m_flRollDelta = ( pParticle->m_flRollDelta > 0.0f ) ? 0.5f : -0.5f;
+ }
+
+ return pParticle->m_flRoll;
+ }
+
+private:
+
+ ColdBreathEmitter( const ColdBreathEmitter & );
+};
+
+
+void RecvProxy_StunTime( const CRecvProxyData *pData, void *pStruct, void *pOut )
+{
+ C_DODPlayer *pPlayerData = (C_DODPlayer *) pStruct;
+
+ if( pPlayerData != C_BasePlayer::GetLocalPlayer() )
+ return;
+
+ if ( (pPlayerData->m_flStunDuration != pData->m_Value.m_Float) && pData->m_Value.m_Float > 0 )
+ {
+ pPlayerData->m_flStunAlpha = 1;
+ }
+
+ pPlayerData->m_flStunDuration = pData->m_Value.m_Float;
+ pPlayerData->m_flStunEffectTime = gpGlobals->curtime + pPlayerData->m_flStunDuration;
+}
+
+// -------------------------------------------------------------------------------- //
+// 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_DODPlayer *pPlayer = dynamic_cast< C_DODPlayer* >( 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()
+
+void RecvProxy_CapAreaIndex( const CRecvProxyData *pData, void *pStruct, void *pOut )
+{
+ CDODPlayerShared *pShared = ( CDODPlayerShared *)pStruct;
+
+ int iCapAreaIndex = pData->m_Value.m_Int;
+
+ if ( iCapAreaIndex != pShared->GetCPIndex() )
+ {
+ pShared->SetCPIndex( iCapAreaIndex );
+ }
+}
+
+// ------------------------------------------------------------------------------------------ //
+// Data tables.
+// ------------------------------------------------------------------------------------------ //
+
+// CDODPlayerShared Data Tables
+//=============================
+
+// specific to the local player ( ideally should not be in CDODPlayerShared! )
+BEGIN_RECV_TABLE_NOBASE( CDODPlayerShared, DT_DODSharedLocalPlayerExclusive )
+ RecvPropInt( RECVINFO( m_iPlayerClass ) ),
+ RecvPropInt( RECVINFO( m_iDesiredPlayerClass ) ),
+ RecvPropFloat( RECVINFO( m_flDeployedYawLimitLeft ) ),
+ RecvPropFloat( RECVINFO( m_flDeployedYawLimitRight ) ),
+ RecvPropInt( RECVINFO( m_iCPIndex ), 0, RecvProxy_CapAreaIndex ),
+ RecvPropArray3( RECVINFO_ARRAY( m_bPlayerDominated ), RecvPropBool( RECVINFO( m_bPlayerDominated[0] ) ) ),
+ RecvPropArray3( RECVINFO_ARRAY( m_bPlayerDominatingMe ), RecvPropBool( RECVINFO( m_bPlayerDominatingMe[0] ) ) ),
+END_RECV_TABLE()
+
+// main table
+BEGIN_RECV_TABLE_NOBASE( CDODPlayerShared, DT_DODPlayerShared )
+ RecvPropFloat( RECVINFO( m_flStamina ) ),
+ RecvPropTime( RECVINFO( m_flSlowedUntilTime ) ),
+ RecvPropBool( RECVINFO( m_bProne ) ),
+ RecvPropBool( RECVINFO( m_bIsSprinting ) ),
+ RecvPropTime( RECVINFO( m_flGoProneTime ) ),
+ RecvPropTime( RECVINFO( m_flUnProneTime ) ),
+ RecvPropTime( RECVINFO( m_flDeployChangeTime ) ),
+ RecvPropFloat( RECVINFO( m_flDeployedHeight ) ),
+ RecvPropBool( RECVINFO( m_bPlanting ) ),
+ RecvPropBool( RECVINFO( m_bDefusing ) ),
+ RecvPropDataTable( "dodsharedlocaldata", 0, 0, &REFERENCE_RECV_TABLE(DT_DODSharedLocalPlayerExclusive) ),
+END_RECV_TABLE()
+
+
+// C_DODPlayer Data Tables
+//=========================
+
+// specific to the local player
+BEGIN_RECV_TABLE_NOBASE( C_DODPlayer, DT_DODLocalPlayerExclusive )
+ RecvPropVector( RECVINFO_NAME( m_vecNetworkOrigin, m_vecOrigin ) ),
+ RecvPropFloat( RECVINFO( m_flStunDuration ), 0, RecvProxy_StunTime ),
+ RecvPropFloat( RECVINFO( m_flStunMaxAlpha)),
+ RecvPropInt( RECVINFO( m_iProgressBarDuration ) ),
+ RecvPropFloat( RECVINFO( m_flProgressBarStartTime ) ),
+END_RECV_TABLE()
+
+// all players except the local player
+BEGIN_RECV_TABLE_NOBASE( C_DODPlayer, DT_DODNonLocalPlayerExclusive )
+ RecvPropVector( RECVINFO_NAME( m_vecNetworkOrigin, m_vecOrigin ) ),
+END_RECV_TABLE()
+
+// main table
+IMPLEMENT_CLIENTCLASS_DT( C_DODPlayer, DT_DODPlayer, CDODPlayer )
+ RecvPropDataTable( RECVINFO_DT( m_Shared ), 0, &REFERENCE_RECV_TABLE( DT_DODPlayerShared ) ),
+
+ RecvPropDataTable( "dodlocaldata", 0, 0, &REFERENCE_RECV_TABLE(DT_DODLocalPlayerExclusive) ),
+ RecvPropDataTable( "dodnonlocaldata", 0, 0, &REFERENCE_RECV_TABLE(DT_DODNonLocalPlayerExclusive) ),
+
+ RecvPropFloat( RECVINFO( m_angEyeAngles[0] ) ),
+ RecvPropFloat( RECVINFO( m_angEyeAngles[1] ) ),
+ RecvPropEHandle( RECVINFO( m_hRagdoll ) ),
+ RecvPropBool( RECVINFO( m_bSpawnInterpCounter ) ),
+ RecvPropInt( RECVINFO( m_iAchievementAwardsMask ) ),
+
+END_RECV_TABLE()
+
+
+// ------------------------------------------------------------------------------------------ //
+// Prediction tables.
+// ------------------------------------------------------------------------------------------ //
+BEGIN_PREDICTION_DATA_NO_BASE( CDODPlayerShared )
+ DEFINE_PRED_FIELD( m_bProne, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
+ DEFINE_PRED_FIELD( m_flStamina, FIELD_FLOAT, FTYPEDESC_INSENDTABLE ),
+ DEFINE_PRED_FIELD( m_bIsSprinting, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
+ DEFINE_PRED_FIELD( m_flGoProneTime, FIELD_FLOAT, FTYPEDESC_INSENDTABLE ),
+ DEFINE_PRED_FIELD( m_flUnProneTime, FIELD_FLOAT, FTYPEDESC_INSENDTABLE ),
+ DEFINE_PRED_FIELD( m_flDeployChangeTime, FIELD_FLOAT, FTYPEDESC_INSENDTABLE ),
+ DEFINE_PRED_FIELD( m_flDeployedHeight, FIELD_FLOAT, FTYPEDESC_INSENDTABLE )
+END_PREDICTION_DATA()
+
+BEGIN_PREDICTION_DATA( C_DODPlayer )
+ DEFINE_PRED_TYPEDESCRIPTION( m_Shared, CDODPlayerShared ),
+ DEFINE_PRED_FIELD( m_flCycle, FIELD_FLOAT, FTYPEDESC_OVERRIDE | FTYPEDESC_PRIVATE | FTYPEDESC_NOERRORCHECK ),
+ DEFINE_PRED_FIELD( m_nSequence, FIELD_INTEGER, FTYPEDESC_OVERRIDE | FTYPEDESC_PRIVATE | FTYPEDESC_NOERRORCHECK ),
+END_PREDICTION_DATA()
+
+
+// ----------------------------------------------------------------------------- //
+// Client ragdoll entity.
+// ----------------------------------------------------------------------------- //
+
+ConVar cl_low_violence( "cl_low_violence", "0" );
+ConVar cl_ragdoll_fade_time( "cl_ragdoll_fade_time", "15", FCVAR_CLIENTDLL );
+ConVar cl_ragdoll_pronecheck_distance( "cl_ragdoll_pronecheck_distance", "64", FCVAR_GAMEDLL );
+
+class C_DODRagdoll : public C_BaseAnimatingOverlay
+{
+public:
+ DECLARE_CLASS( C_DODRagdoll, C_BaseAnimatingOverlay );
+ DECLARE_CLIENTCLASS();
+
+ C_DODRagdoll();
+ ~C_DODRagdoll();
+
+ virtual void OnDataChanged( DataUpdateType_t type );
+
+ int GetPlayerEntIndex() const;
+ IRagdoll* GetIRagdoll() const;
+
+ void ImpactTrace( trace_t *pTrace, int iDamageType, const char *pCustomImpactName );
+
+ void ClientThink( void );
+ void StartFadeOut( float fDelay );
+
+ bool IsRagdollVisible();
+private:
+
+ C_DODRagdoll( const C_DODRagdoll & ) {}
+
+ void Interp_Copy( C_BaseAnimatingOverlay *pSourceEntity );
+
+ void CreateLowViolenceRagdoll();
+ void CreateDODRagdoll();
+
+private:
+
+ EHANDLE m_hPlayer;
+ CNetworkVector( m_vecRagdollVelocity );
+ CNetworkVector( m_vecRagdollOrigin );
+ float m_fDeathTime;
+ bool m_bFadingOut;
+};
+
+
+IMPLEMENT_CLIENTCLASS_DT_NOBASE( C_DODRagdoll, DT_DODRagdoll, CDODRagdoll )
+ 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_DODRagdoll::C_DODRagdoll()
+{
+ m_fDeathTime = -1;
+ m_bFadingOut = false;
+}
+
+C_DODRagdoll::~C_DODRagdoll()
+{
+ PhysCleanupFrictionSounds( this );
+}
+
+void C_DODRagdoll::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_DODRagdoll::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 strength
+
+ // 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 strength
+
+ // apply force where we hit it
+ pPhysicsObject->ApplyForceOffset( dir, hitpos );
+
+ // Blood spray!
+ FX_DOD_BloodSpray( hitpos, dir, 10 );
+ }
+
+ m_pRagdoll->ResetRagdollSleepAfterTime();
+}
+
+
+void C_DODRagdoll::CreateLowViolenceRagdoll()
+{
+ // 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 )
+ {
+ CreateDODRagdoll();
+ }
+ else
+ {
+ int iDeathAnim = RandomInt( iMinDeathAnim, iMaxDeathAnim );
+ char str[512];
+ Q_snprintf( str, sizeof( str ), "death%d", iDeathAnim );
+
+ SetSequence( LookupSequence( str ) );
+ ForceClientSideAnimationOn();
+
+ SetNetworkOrigin( m_vecRagdollOrigin );
+ SetAbsOrigin( m_vecRagdollOrigin );
+ SetAbsVelocity( m_vecRagdollVelocity );
+
+ C_DODPlayer *pPlayer = dynamic_cast< C_DODPlayer* >( m_hPlayer.Get() );
+ if ( pPlayer && !pPlayer->IsDormant() )
+ {
+ // move my current model instance to the ragdoll's so decals are preserved.
+ pPlayer->SnatchModelInstance( this );
+
+ SetAbsAngles( pPlayer->GetRenderAngles() );
+ SetNetworkAngles( pPlayer->GetRenderAngles() );
+ }
+
+ Interp_Reset( GetVarMapping() );
+ }
+}
+
+void C_DODRagdoll::CreateDODRagdoll()
+{
+ // 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_DODPlayer *pPlayer = dynamic_cast< C_DODPlayer* >( m_hPlayer.Get() );
+
+#ifdef _DEBUG
+ DevMsg( 2, "CreateDODRagdoll %d %d\n", gpGlobals->framecount, pPlayer ? pPlayer->entindex() : 0 );
+#endif
+
+ 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.
+ if ( !pPlayer->IsLocalPlayer() && pPlayer->dod_IsInterpolationEnabled() )
+ {
+ 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( "RagdollSpawn" ); // hax, find a neutral standing pose
+ if ( iSeq == -1 )
+ {
+ Assert( false ); // missing look_idle?
+ iSeq = 0;
+ }
+
+ SetSequence( iSeq ); // look_idle, basic pose
+ SetCycle( 0.0 );
+
+ Interp_Reset( varMap );
+ }
+
+ m_nBody = pPlayer->GetBody();
+ }
+ 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 );
+
+ // 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;
+
+ if ( pPlayer && pPlayer == C_BasePlayer::GetLocalPlayer() )
+ {
+ pPlayer->GetRagdollInitBoneArrays( boneDelta0, boneDelta1, currentBones, boneDt );
+ }
+ else
+ {
+ GetRagdollInitBoneArrays( boneDelta0, boneDelta1, currentBones, boneDt );
+ }
+
+ InitAsClientRagdoll( boneDelta0, boneDelta1, currentBones, boneDt );
+ }
+ else
+ {
+ ClientLeafSystem()->SetRenderGroup( GetRenderHandle(), RENDER_GROUP_TRANSLUCENT_ENTITY );
+ }
+
+ // Fade out the ragdoll in a while
+ StartFadeOut( cl_ragdoll_fade_time.GetFloat() );
+ SetNextClientThink( gpGlobals->curtime + 5.0f );
+}
+
+void C_DODRagdoll::OnDataChanged( DataUpdateType_t type )
+{
+ BaseClass::OnDataChanged( type );
+
+ if ( type == DATA_UPDATE_CREATED )
+ {
+ if ( cl_low_violence.GetInt() )
+ {
+ CreateLowViolenceRagdoll();
+ }
+ else
+ {
+ CreateDODRagdoll();
+ }
+ }
+ 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_DODRagdoll::GetIRagdoll() const
+{
+ return m_pRagdoll;
+}
+
+bool C_DODRagdoll::IsRagdollVisible()
+{
+ Vector vMins = Vector(-1,-1,-1); //WorldAlignMins();
+ Vector vMaxs = Vector(1,1,1); //WorldAlignMaxs();
+
+ Vector origin = GetAbsOrigin();
+
+ if( !engine->IsBoxInViewCluster( vMins + origin, vMaxs + origin) )
+ {
+ return false;
+ }
+ else if( engine->CullBox( vMins + origin, vMaxs + origin ) )
+ {
+ return false;
+ }
+
+ return true;
+}
+
+void C_DODRagdoll::ClientThink( void )
+{
+ SetNextClientThink( CLIENT_THINK_ALWAYS );
+
+ if ( m_bFadingOut == true )
+ {
+ int iAlpha = GetRenderColor().a;
+ int iFadeSpeed = 600.0f;
+
+ iAlpha = MAX( iAlpha - ( iFadeSpeed * gpGlobals->frametime ), 0 );
+
+ SetRenderMode( kRenderTransAlpha );
+ SetRenderColorA( iAlpha );
+
+ if ( iAlpha == 0 )
+ {
+ //Release();
+ AddEffects( EF_NODRAW );
+ }
+
+ return;
+ }
+
+ for( int iClient = 1; iClient <= gpGlobals->maxClients; ++iClient )
+ {
+ C_DODPlayer *pEnt = static_cast< C_DODPlayer *> ( UTIL_PlayerByIndex( iClient ) );
+
+ if(!pEnt || !pEnt->IsPlayer())
+ continue;
+
+ if ( m_hPlayer == NULL )
+ continue;
+
+ if ( pEnt->entindex() == m_hPlayer->entindex() )
+ continue;
+
+ if ( pEnt->GetHealth() <= 0 )
+ continue;
+
+ if ( pEnt->m_Shared.IsProne() == false )
+ continue;
+
+ Vector vTargetOrigin = pEnt->GetAbsOrigin();
+ Vector vMyOrigin = GetAbsOrigin();
+
+ Vector vDir = vTargetOrigin - vMyOrigin;
+
+ if ( vDir.Length() > cl_ragdoll_pronecheck_distance.GetInt() )
+ continue;
+
+ SetNextClientThink( CLIENT_THINK_ALWAYS );
+ m_bFadingOut = true;
+ return;
+ }
+
+ // if the player is looking at us, delay the fade
+ if ( IsRagdollVisible() )
+ {
+ StartFadeOut( 5.0 );
+ return;
+ }
+
+ if ( m_fDeathTime > gpGlobals->curtime )
+ return;
+
+ //Release(); // Die
+ AddEffects( EF_NODRAW );
+}
+
+void C_DODRagdoll::StartFadeOut( float fDelay )
+{
+ m_fDeathTime = gpGlobals->curtime + fDelay;
+ SetNextClientThink( CLIENT_THINK_ALWAYS );
+}
+
+// ------------------------------------------------------------------------------------------ //
+// C_DODPlayer implementation.
+// ------------------------------------------------------------------------------------------ //
+C_DODPlayer::C_DODPlayer() :
+ m_iv_angEyeAngles( "C_DODPlayer::m_iv_angEyeAngles" )
+{
+ m_PlayerAnimState = CreatePlayerAnimState( this );
+
+ m_Shared.Init( this );
+
+ m_flPitchRecoilAccumulator = 0.0;
+ m_flYawRecoilAccumulator = 0.0;
+ m_flRecoilTimeRemaining = 0.0;
+
+ m_iProgressBarDuration = 0;
+ m_flProgressBarStartTime = 0.0f;
+
+ AddVar( &m_angEyeAngles, &m_iv_angEyeAngles, LATCH_SIMULATION_VAR );
+
+ m_flProneViewOffset = 0.0;
+ m_bProneSwayingRight = true;
+ m_iIDEntIndex = 0;
+
+ m_Hints.Init( this, NUM_HINTS, g_pszHintMessages );
+
+ m_pFlashlightBeam = NULL;
+
+ m_fNextThinkPushAway = 0.0f;
+
+ // Cold breath.
+ m_bColdBreathOn = false;
+ m_flColdBreathTimeStart = 0.0f;
+ m_flColdBreathTimeEnd = 0.0f;
+ m_hColdBreathEmitter = NULL;
+ m_hColdBreathMaterial = INVALID_MATERIAL_HANDLE;
+
+ m_flHideHeadIconUntilTime = 0.0f;
+
+ m_iAchievementAwardsMask = 0;
+ m_pHeadIconMaterial = NULL;
+}
+
+
+C_DODPlayer::~C_DODPlayer()
+{
+ m_PlayerAnimState->Release();
+
+ ReleaseFlashlight();
+
+ // Kill the stamina sound!
+ if ( m_pStaminaSound )
+ {
+ CSoundEnvelopeController::GetController().SoundDestroy( m_pStaminaSound );
+ m_pStaminaSound = NULL;
+ }
+
+ // Cold breath.
+ DestroyColdBreathEmitter();
+}
+
+
+C_DODPlayer* C_DODPlayer::GetLocalDODPlayer()
+{
+ return ToDODPlayer( C_BasePlayer::GetLocalPlayer() );
+}
+
+IRagdoll* C_DODPlayer::GetRepresentativeRagdoll() const
+{
+ if ( m_hRagdoll.Get() )
+ {
+ C_DODRagdoll *pRagdoll = (C_DODRagdoll*)m_hRagdoll.Get();
+
+ return pRagdoll->GetIRagdoll();
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+const QAngle& C_DODPlayer::GetRenderAngles()
+{
+ if ( IsRagdoll() )
+ {
+ return vec3_angle;
+ }
+ else
+ {
+ return m_PlayerAnimState->GetRenderAngles();
+ }
+}
+
+
+void C_DODPlayer::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_DODPlayer::GetLocalDODPlayer() )
+ m_PlayerAnimState->Update( EyeAngles()[YAW], m_angEyeAngles[PITCH] );
+ else
+ m_PlayerAnimState->Update( m_angEyeAngles[YAW], m_angEyeAngles[PITCH] );
+
+ BaseClass::UpdateClientSideAnimation();
+}
+
+ConVar dod_playachievementsound( "dod_playachievementsound", "1", FCVAR_ARCHIVE );
+
+void C_DODPlayer::OnAchievementAchieved( int iAchievement )
+{
+ // don't draw the head icon for a length of time after showing the particle effect
+ m_flHideHeadIconUntilTime = gpGlobals->curtime + 2.5;
+
+ if ( dod_playachievementsound.GetBool() )
+ {
+ EmitSound( "Achievement.Earned" );
+ }
+
+ BaseClass::OnAchievementAchieved( iAchievement );
+}
+
+int C_DODPlayer::DrawModel( int flags )
+{
+ int nRetval = BaseClass::DrawModel( flags );
+ if ( nRetval != 0 )
+ {
+ // register to draw the head icon, unless we're hiding it due to the "achieved" particle effect
+ if ( gpGlobals->curtime > m_flHideHeadIconUntilTime )
+ {
+ HeadIconManager()->PlayerDrawn( this );
+ }
+ }
+ return nRetval;
+}
+
+void C_DODPlayer::DoAnimationEvent( PlayerAnimEvent_t event, int nData )
+{
+ m_PlayerAnimState->DoAnimationEvent( event, nData );
+}
+
+DODPlayerState C_DODPlayer::State_Get() const
+{
+ return m_iPlayerState;
+}
+
+bool C_DODPlayer::CanShowClassMenu( void )
+{
+ return ( GetTeamNumber() == TEAM_ALLIES || GetTeamNumber() == TEAM_AXIS );
+}
+
+void C_DODPlayer::DoRecoil( int iWpnID, float flWpnRecoil )
+{
+ float flPitchRecoil = flWpnRecoil;
+ float flYawRecoil = flPitchRecoil / 4;
+
+ if( iWpnID == WEAPON_BAR )
+ flYawRecoil = MIN( flYawRecoil, 1.3 );
+
+ if ( m_Shared.IsInMGDeploy() )
+ {
+ flPitchRecoil = 0.0;
+ flYawRecoil = 0.0;
+ }
+ else if ( m_Shared.IsProne() &&
+ iWpnID != WEAPON_30CAL &&
+ iWpnID != WEAPON_MG42 ) //minor hackage
+ {
+ flPitchRecoil = flPitchRecoil / 4;
+ flYawRecoil = flYawRecoil / 4;
+ }
+ else if ( m_Shared.IsDucking() )
+ {
+ flPitchRecoil = flPitchRecoil / 2;
+ flYawRecoil = flYawRecoil / 2;
+ }
+
+ SetRecoilAmount( flPitchRecoil, flYawRecoil );
+}
+
+//Set the amount of pitch and yaw recoil we want to do over the next RECOIL_DURATION seconds
+void C_DODPlayer::SetRecoilAmount( float flPitchRecoil, float flYawRecoil )
+{
+ //Slam the values, abandon previous recoils
+ m_flPitchRecoilAccumulator = flPitchRecoil;
+
+ flYawRecoil = flYawRecoil * random->RandomFloat( 0.8, 1.1 );
+
+ if( random->RandomInt( 0,1 ) <= 0 )
+ m_flYawRecoilAccumulator = flYawRecoil;
+ else
+ m_flYawRecoilAccumulator = -flYawRecoil;
+
+ m_flRecoilTimeRemaining = RECOIL_DURATION;
+}
+
+//Get the amount of recoil we should do this frame
+void C_DODPlayer::GetRecoilToAddThisFrame( float &flPitchRecoil, float &flYawRecoil )
+{
+ if( m_flRecoilTimeRemaining <= 0 )
+ {
+ flPitchRecoil = 0.0;
+ flYawRecoil = 0.0;
+ return;
+ }
+
+ float flRemaining = MIN( m_flRecoilTimeRemaining, gpGlobals->frametime );
+
+ float flRecoilProportion = ( flRemaining / RECOIL_DURATION );
+
+ flPitchRecoil = m_flPitchRecoilAccumulator * flRecoilProportion;
+ flYawRecoil = m_flYawRecoilAccumulator * flRecoilProportion;
+
+ m_flRecoilTimeRemaining -= gpGlobals->frametime;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Input handling
+//-----------------------------------------------------------------------------
+bool C_DODPlayer::CreateMove( float flInputSampleTime, CUserCmd *pCmd )
+{
+ // Lock view if deployed
+ if( m_Shared.IsInMGDeploy() )
+ {
+ m_Shared.ClampDeployedAngles( &pCmd->viewangles );
+ }
+
+ //if we're prone and moving, do some sway
+ if( m_Shared.IsProne() && IsAlive() )
+ {
+ float flSpeed = GetAbsVelocity().Length();
+
+ float flSwayAmount = PRONE_SWAY_AMOUNT * gpGlobals->frametime;
+
+ if( flSpeed > 10 )
+ {
+ if (m_flProneViewOffset >= PRONE_MAX_SWAY)
+ {
+ m_bProneSwayingRight = false;
+ }
+ else if (m_flProneViewOffset <= -PRONE_MAX_SWAY)
+ {
+ m_bProneSwayingRight = true;
+ }
+
+ if (m_bProneSwayingRight)
+ {
+ pCmd->viewangles[YAW] += flSwayAmount;
+ m_flProneViewOffset += flSwayAmount;
+ }
+ else
+ {
+ pCmd->viewangles[YAW] -= flSwayAmount;
+ m_flProneViewOffset -= flSwayAmount;
+ }
+ }
+ else
+ {
+ // Return to 0 prone sway offset gradually
+
+ //Quick Checks to make sure it isn't bigger or smaller than our sway amount
+ if ( (m_flProneViewOffset < 0.0 && m_flProneViewOffset > -flSwayAmount) ||
+ (m_flProneViewOffset > 0.0 && m_flProneViewOffset < flSwayAmount) )
+ {
+ m_flProneViewOffset = 0.0;
+ }
+
+ if (m_flProneViewOffset > 0.0)
+ {
+ pCmd->viewangles[YAW] -= flSwayAmount;
+ m_flProneViewOffset -= flSwayAmount;
+ }
+ else if (m_flProneViewOffset < 0.0)
+ {
+ pCmd->viewangles[YAW] += flSwayAmount;
+ m_flProneViewOffset += flSwayAmount;
+ }
+ }
+ }
+
+ bool bResult = BaseClass::CreateMove( flInputSampleTime, pCmd );
+
+ AvoidPlayers( pCmd );
+
+ return bResult;
+}
+
+// How fast to avoid collisions with center of other object, in units per second
+#define AVOID_SPEED 1000.0f
+ConVar cl_avoidspeed( "cl_avoidspeed", "1000.0f", FCVAR_CLIENTDLL );
+extern ConVar cl_forwardspeed;
+extern ConVar cl_backspeed;
+extern ConVar cl_sidespeed;
+
+bool C_DODPlayer::ShouldDraw( void )
+{
+ if( IsDormant() )
+ return false;
+
+ // 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();
+}
+
+//-----------------------------------------------------------------------------
+// Deal with visibility
+//-----------------------------------------------------------------------------
+void C_DODPlayer::GetToolRecordingState( KeyValues *msg )
+{
+#ifndef _XBOX
+ BaseClass::GetToolRecordingState( msg );
+ BaseEntityRecordingState_t *pBaseEntityState = (BaseEntityRecordingState_t*)msg->GetPtr( "baseentity" );
+ if ( IsLocalPlayer() )
+ {
+ pBaseEntityState->m_bVisible = !IsDormant() && IsAlive() && ( GetTeamNumber() != TEAM_SPECTATOR ) &&
+ ( GetRenderMode() != kRenderNone ) && (GetObserverMode() != OBS_MODE_DEATHCAM) && !IsEffectActive(EF_NODRAW);
+ }
+#endif
+}
+
+
+CWeaponDODBase* C_DODPlayer::GetActiveDODWeapon() const
+{
+ C_BaseCombatWeapon *pWpn = GetActiveWeapon();
+
+ if ( !pWpn )
+ return NULL;
+
+ return dynamic_cast< CWeaponDODBase* >( pWpn );
+}
+
+C_BaseAnimating * C_DODPlayer::BecomeRagdollOnClient()
+{
+ return NULL;
+}
+
+void C_DODPlayer::FireEvent( const Vector& origin, const QAngle& angles, int event, const char *options )
+{
+ if( event == 7002 )
+ {
+ if( this == C_BasePlayer::GetLocalPlayer() )
+ return;
+
+ CWeaponDODBase *pWeapon = GetActiveDODWeapon();
+
+ if ( !pWeapon )
+ return;
+
+ int iAttachment = 2;
+ Vector vecOrigin;
+ QAngle angAngles;
+
+ if( pWeapon->GetAttachment( iAttachment, vecOrigin, angAngles ) )
+ {
+ int shellType = atoi(options);
+
+ CEffectData data;
+ data.m_nHitBox = shellType;
+ data.m_vOrigin = vecOrigin;
+ data.m_vAngles = angAngles;
+ DispatchEffect( "DOD_EjectBrass", data );
+ }
+ }
+ else
+ BaseClass::FireEvent( origin, angles, event, options );
+
+ /*
+ // MATTTODO: water footstep effects
+ if( event == 7001 )
+ {
+ bool bInWater = ( enginetrace->GetPointContents(origin) & CONTENTS_WATER );
+
+ 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 );
+
+ 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 );
+ }
+ }
+ */
+}
+// NVNT gate for spectating.
+static bool inSpectating_Haptics = false;
+// NVNT check grenade things ( -- this is here to avoid modificaions to the grenade class -- )
+static bool s_holdingGrenade = false;
+static bool s_grenadePinPulled = false;
+static bool s_grenadeArmed = false;
+static bool s_bombPlanting = false;
+void C_DODPlayer::ClientThink()
+{
+ BaseClass::ClientThink();
+
+ if ( gpGlobals->curtime >= m_fNextThinkPushAway )
+ {
+ PerformObstaclePushaway( this );
+ m_fNextThinkPushAway = gpGlobals->curtime + PUSHAWAY_THINK_INTERVAL;
+ }
+
+ if ( IsLocalPlayer() )
+ {
+ UpdateIDTarget();
+
+ StaminaSoundThink();
+
+ // Recoil
+ QAngle viewangles;
+ engine->GetViewAngles( viewangles );
+
+ float flYawRecoil;
+ float flPitchRecoil;
+ GetRecoilToAddThisFrame( flPitchRecoil, flYawRecoil );
+
+ // Recoil
+ if( flPitchRecoil > 0 )
+ {
+ //add the recoil pitch
+ viewangles[PITCH] -= flPitchRecoil;
+ viewangles[YAW] += flYawRecoil;
+ }
+
+ // Sniper sway
+ if( m_Shared.IsSniperZoomed() && GetFOV() <= 20 )
+ {
+ //multiply by frametime to balance for framerate changes
+ float x = gpGlobals->frametime * cos( gpGlobals->curtime );
+ float y = gpGlobals->frametime * 2 * cos( 2 * gpGlobals->curtime );
+
+ float scale;
+
+ if( m_Shared.IsDucking() ) //duck
+ scale = ZOOM_SWAY_DUCKING;
+ else if( m_Shared.IsProne() )
+ scale = ZOOM_SWAY_PRONE;
+ else //standing
+ scale = ZOOM_SWAY_STANDING;
+
+ if( GetAbsVelocity().Length() > 10 )
+ scale += ZOOM_SWAY_MOVING_PENALTY;
+
+ viewangles[PITCH] += y * scale;
+ viewangles[YAW] += x * scale;
+ }
+
+ engine->SetViewAngles( viewangles );
+ // NVNT check spectator nav.
+ if( ( ( GetTeamNumber() == TEAM_SPECTATOR ) || ( !this->IsAlive() ) ) ) {
+ if(!inSpectating_Haptics)
+ {
+ if ( haptics )
+ haptics->SetNavigationClass("spectate");
+ inSpectating_Haptics = true;
+ }
+ }else{
+ if(inSpectating_Haptics) {
+ if ( haptics )
+ haptics->SetNavigationClass("on_foot");
+ inSpectating_Haptics = false;
+ }
+ }
+
+ // NVNT check grenade things ( -- this is here to avoid modificaions to the grenade class -- )
+ C_WeaponDODBaseGrenade *heldGrenade = dynamic_cast<C_WeaponDODBaseGrenade*>(GetActiveDODWeapon());
+ if(heldGrenade)
+ {
+ if(!s_holdingGrenade)
+ {
+ s_holdingGrenade = true;
+ }
+ bool pinPulled = heldGrenade->m_bPinPulled;
+ if(pinPulled != s_grenadePinPulled) {
+ if(pinPulled)
+ {
+ if ( haptics )
+ haptics->ProcessHapticEvent(3, "Weapons", heldGrenade->GetClassname(), "PinPulled");
+ }else {
+ if ( haptics )
+ haptics->ProcessHapticEvent(3, "Weapons", heldGrenade->GetClassname(), "PinReplaced");
+ }
+ s_grenadePinPulled = pinPulled;
+ }
+ bool grenadeArmed = heldGrenade->m_bArmed;
+ if(grenadeArmed != s_grenadeArmed) {
+ if(grenadeArmed)
+ {
+ if ( haptics )
+ haptics->ProcessHapticEvent(3, "Weapons", heldGrenade->GetClassname(), "Armed");
+ }else {
+ if ( haptics )
+ haptics->ProcessHapticEvent(3, "Weapons", heldGrenade->GetClassname(), "Unarmed");
+ }
+ s_grenadeArmed = grenadeArmed;
+ }
+ }else{
+ if( s_holdingGrenade )
+ {
+ if(s_grenadeArmed && s_grenadePinPulled) {
+ if ( haptics )
+ haptics->ProcessHapticEvent(3, "Weapons", "Grenades", "Thrown");
+ }
+ s_holdingGrenade = false;
+ s_grenadeArmed = false;
+ s_grenadePinPulled = false;
+ }
+ C_DODBaseBombWeapon *heldBomb = dynamic_cast<C_DODBaseBombWeapon*>(GetActiveDODWeapon());
+
+ if(heldBomb) {
+ bool isPlanting = heldBomb->IsPlanting();
+ if(isPlanting!=s_bombPlanting) {
+ if(isPlanting) {
+ if(!s_bombPlanting) {
+ s_bombPlanting = true;
+ if ( haptics )
+ haptics->ProcessHapticEvent(3, "Weapons", heldBomb->GetClassname(), "Plant");
+ }
+ }else{
+ if(s_bombPlanting) {
+ if ( haptics )
+ haptics->ProcessHapticEvent(3, "Weapons", heldBomb->GetClassname(), "StopPlant");
+ }
+ }
+ }
+ }else if(s_bombPlanting) {
+ s_bombPlanting = false;
+ if ( haptics )
+ haptics->ProcessHapticEvent(3, "Weapons", "Bomb", "Complete");
+ }
+ }
+
+ }
+ else
+ {
+ // Cold breath.
+ UpdateColdBreath();
+ }
+}
+
+// Start or stop the stamina breathing sound if necessary
+void C_DODPlayer::StaminaSoundThink( void )
+{
+ if ( m_bPlayingLowStaminaSound )
+ {
+ if ( !IsAlive() || m_Shared.GetStamina() >= LOW_STAMINA_THRESHOLD )
+ {
+ // stop the sprint sound
+ CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
+ controller.SoundFadeOut( m_pStaminaSound, 1.0, true );
+
+ // SoundFadeOut will destroy this sound, so we will have to create another one
+ // if we go below the threshold again soon
+ m_pStaminaSound = NULL;
+
+ m_bPlayingLowStaminaSound = false;
+ }
+ }
+ else
+ {
+ if ( IsAlive() && m_Shared.GetStamina() < LOW_STAMINA_THRESHOLD )
+ {
+ // we are alive and have low stamina
+ CLocalPlayerFilter filter;
+
+ CSoundEnvelopeController &controller = CSoundEnvelopeController::GetController();
+
+ if ( !m_pStaminaSound )
+ m_pStaminaSound = controller.SoundCreate( filter, entindex(), "Player.Sprint" );
+
+ controller.Play( m_pStaminaSound, 0.0, 100 );
+ controller.SoundChangeVolume( m_pStaminaSound, 1.0, 2.0 );
+
+ m_bPlayingLowStaminaSound = true;
+ }
+ }
+}
+
+void C_DODPlayer::OnDataChanged( DataUpdateType_t type )
+{
+ BaseClass::OnDataChanged( type );
+
+ if ( type == DATA_UPDATE_CREATED )
+ {
+ SetNextClientThink( CLIENT_THINK_ALWAYS );
+ }
+
+ UpdateVisibility();
+}
+
+void C_DODPlayer::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 );
+
+ if( m_bSpawnInterpCounter != m_bSpawnInterpCounterCache )
+ {
+ if ( IsLocalPlayer() )
+ {
+ LocalPlayerRespawn();
+ }
+
+ m_bSpawnInterpCounterCache = m_bSpawnInterpCounter.m_Value;
+ }
+}
+
+// Called every time the player respawns
+void C_DODPlayer::LocalPlayerRespawn( void )
+{
+ MoveToLastReceivedPosition( true );
+ ResetLatched();
+
+ ResetToneMapping(1.0);
+
+ m_Shared.m_bForceProneChange = true;
+
+ m_flLastRespawnTime = gpGlobals->curtime;
+}
+
+class C_FadingPhysPropClientside : public C_PhysPropClientside
+{
+public:
+ DECLARE_CLASS( C_FadingPhysPropClientside, C_PhysPropClientside );
+
+ // if we wake, extend fade time
+
+ virtual void ImpactTrace( trace_t *pTrace, int iDamageType, const char *pCustomImpactName )
+ {
+ // If we haven't started fading
+ if( GetRenderColor().a >= 255 )
+ {
+ // delay the fade
+ StartFadeOut( 10.0 );
+
+ // register the impact
+ BaseClass::ImpactTrace( pTrace, iDamageType, pCustomImpactName );
+ }
+ }
+};
+
+void C_DODPlayer::PopHelmet( Vector vecDir, Vector vecForceOrigin, int iModel )
+{
+ if ( IsDormant() )
+ return; // We can't see them anyway, just bail
+
+ C_FadingPhysPropClientside *pEntity = new C_FadingPhysPropClientside();
+
+ if ( !pEntity )
+ return;
+
+ const model_t *model = modelinfo->GetModel( iModel );
+
+ if ( !model )
+ {
+ DevMsg("CTempEnts::PhysicsProp: model index %i not found\n", iModel );
+ return;
+ }
+
+ Vector vecHead;
+ QAngle angHeadAngles;
+
+ {
+ C_BaseAnimating::AutoAllowBoneAccess boneaccess( true, false );
+ int iAttachment = LookupAttachment( "head" );
+ GetAttachment( iAttachment, vecHead, angHeadAngles ); //attachment 1 is the head attachment
+ }
+
+ pEntity->SetModelName( modelinfo->GetModelName(model) );
+ pEntity->SetAbsOrigin( vecHead );
+ pEntity->SetAbsAngles( angHeadAngles );
+ pEntity->SetPhysicsMode( PHYSICS_MULTIPLAYER_CLIENTSIDE );
+
+ if ( !pEntity->Initialize() )
+ {
+ pEntity->Release();
+ return;
+ }
+
+ IPhysicsObject *pPhysicsObject = pEntity->VPhysicsGetObject();
+
+ if( pPhysicsObject )
+ {
+#ifdef DEBUG
+ if( vecForceOrigin == vec3_origin )
+ {
+ vecForceOrigin = GetAbsOrigin();
+ }
+#endif
+
+ Vector vecForce = vecDir;
+ Vector vecOffset = vecForceOrigin - pEntity->GetAbsOrigin();
+ pPhysicsObject->ApplyForceOffset( vecForce, vecOffset );
+ }
+ else
+ {
+ // failed to create a physics object
+ pEntity->Release();
+ return;
+ }
+
+ pEntity->StartFadeOut( 10.0 );
+}
+
+void C_DODPlayer::ReceiveMessage( int classID, bf_read &msg )
+{
+ if ( classID != GetClientClass()->m_ClassID )
+ {
+ // message is for subclass
+ BaseClass::ReceiveMessage( classID, msg );
+ return;
+ }
+
+ int messageType = msg.ReadByte();
+ switch( messageType )
+ {
+ case DOD_PLAYER_POP_HELMET:
+ {
+ Vector vecDir, vecForceOffset;
+ msg.ReadBitVec3Coord( vecDir );
+ msg.ReadBitVec3Coord( vecForceOffset );
+
+ int model = msg.ReadShort();
+
+ PopHelmet( vecDir, vecForceOffset, model );
+ }
+ break;
+ case DOD_PLAYER_REMOVE_DECALS:
+ {
+ RemoveAllDecals();
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Update this client's target entity
+//-----------------------------------------------------------------------------
+void C_DODPlayer::UpdateIDTarget()
+{
+ Assert( IsLocalPlayer() );
+
+ // Clear old target and find a new one
+ m_iIDEntIndex = 0;
+
+ // don't show IDs in chase spec mode
+ if ( GetObserverMode() == OBS_MODE_CHASE ||
+ GetObserverMode() == OBS_MODE_DEATHCAM )
+ return;
+
+ trace_t tr;
+ Vector vecStart, vecEnd;
+ VectorMA( MainViewOrigin(), 1500, MainViewForward(), vecEnd );
+ VectorMA( MainViewOrigin(), 10, MainViewForward(), vecStart );
+ UTIL_TraceLine( vecStart, vecEnd, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr );
+
+ C_BaseEntity *pEntity = NULL;
+ if ( !tr.startsolid && tr.DidHitNonWorldEntity() )
+ {
+ pEntity = tr.m_pEnt;
+
+ if ( pEntity && (pEntity != this) )
+ {
+ m_iIDEntIndex = pEntity->entindex();
+ }
+ }
+
+ // if we haven't done the weapon hint, and this entity is a weapon,
+ // show the weapon hint
+ if ( m_Hints.HasPlayedHint( HINT_PICK_UP_WEAPON ) == false && pEntity )
+ {
+ Vector vecDist = vecStart - tr.endpos;
+
+ // if m_iIDEntIndex is a CWeaponDODBase, show pick up hint
+ CWeaponDODBase *pWpn = dynamic_cast<CWeaponDODBase *>( pEntity );
+ if ( pWpn && vecDist.Length() < 100 )
+ {
+ HintMessage( HINT_PICK_UP_WEAPON );
+ }
+ }
+}
+
+bool C_DODPlayer::ShouldAutoReload( void )
+{
+ return cl_autoreload.GetBool();
+}
+
+bool C_DODPlayer::ShouldAutoRezoom( void )
+{
+ return cl_autorezoom.GetBool();
+}
+
+void C_DODPlayer::CheckGrenadeHint( Vector vecGrenadeOrigin )
+{
+ if ( m_Hints.HasPlayedHint( HINT_PICK_UP_GRENADE ) == false )
+ {
+ // if its within 500 units
+ float flDist = ( vecGrenadeOrigin - GetAbsOrigin() ).Length2D();
+
+ if ( flDist < 500 )
+ {
+ m_Hints.HintMessage( HINT_PICK_UP_GRENADE );
+ }
+ }
+}
+
+void C_DODPlayer::CheckBombTargetPlantHint( void )
+{
+ if ( m_Hints.HasPlayedHint( HINT_BOMB_TARGET ) == false )
+ {
+ m_Hints.HintMessage( HINT_BOMB_TARGET );
+ }
+}
+
+void C_DODPlayer::CheckBombTargetDefuseHint( void )
+{
+ if ( m_Hints.HasPlayedHint( HINT_DEFUSE_BOMB ) == false )
+ {
+ m_Hints.HintMessage( HINT_DEFUSE_BOMB );
+ }
+}
+
+void C_DODPlayer::LowerWeapon( void )
+{
+ m_bWeaponLowered = true;
+}
+
+void C_DODPlayer::RaiseWeapon( void )
+{
+ m_bWeaponLowered = false;
+}
+
+bool C_DODPlayer::IsWeaponLowered( void )
+{
+ if ( GetMoveType() == MOVETYPE_LADDER )
+ return true;
+
+ CWeaponDODBase *pWeapon = GetActiveDODWeapon();
+
+ if ( !pWeapon )
+ return false;
+
+ // Lower when underwater ( except if its melee )
+ if ( GetWaterLevel() > 2 && pWeapon->GetDODWpnData().m_WeaponType != WPN_TYPE_MELEE )
+ return true;
+
+ if ( m_Shared.IsProne() && GetAbsVelocity().LengthSqr() > 1 )
+ return true;
+
+ if ( m_Shared.IsGoingProne() || m_Shared.IsGettingUpFromProne() )
+ return true;
+
+ if ( m_Shared.IsJumping() )
+ return true;
+
+ if ( m_Shared.IsDefusing() )
+ return true;
+
+ // Lower losing team's weapons in bonus round
+ int state = DODGameRules()->State_Get();
+
+ if ( state == STATE_ALLIES_WIN && GetTeamNumber() == TEAM_AXIS )
+ return true;
+
+ if ( state == STATE_AXIS_WIN && GetTeamNumber() == TEAM_ALLIES )
+ return true;
+
+ if ( m_Shared.IsBazookaDeployed() )
+ return false;
+
+ Vector vel = GetAbsVelocity();
+ if ( vel.Length2D() < 50 )
+ return false;
+
+ if ( m_nButtons & IN_SPEED && ( m_nButtons & IN_FORWARD ) &&
+ m_Shared.GetStamina() >= 5 &&
+ !m_Shared.IsDucking() )
+ return true;
+
+ return m_bWeaponLowered;
+}
+
+// Shadows
+
+ConVar cl_blobbyshadows( "cl_blobbyshadows", "0", FCVAR_CLIENTDLL );
+ShadowType_t C_DODPlayer::ShadowCastType( void )
+{
+ if ( !IsVisible() )
+ return SHADOWS_NONE;
+
+ C_DODPlayer *pLocalPlayer = C_DODPlayer::GetLocalDODPlayer();
+
+ // if we're first person spectating this player
+ if ( pLocalPlayer &&
+ pLocalPlayer->GetObserverTarget() == this &&
+ pLocalPlayer->GetObserverMode() == OBS_MODE_IN_EYE )
+ {
+ return SHADOWS_NONE;
+ }
+
+ if( cl_blobbyshadows.GetBool() )
+ return SHADOWS_SIMPLE;
+
+ return SHADOWS_RENDER_TO_TEXTURE_DYNAMIC;
+}
+
+float g_flFattenAmt = 4;
+void C_DODPlayer::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_DODPlayer::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 );
+}
+
+
+bool C_DODPlayer::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 );
+ }
+}
+
+ConVar cl_muzzleflash_dlight_3rd( "cl_muzzleflash_dlight_3rd", "1" );
+
+void C_DODPlayer::ProcessMuzzleFlashEvent()
+{
+ CBasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer();
+
+ bool bInToolRecordingMode = ToolsEnabled() && clienttools->IsInRecordingMode();
+
+ // Reenable when the weapons have muzzle flash attachments in the right spot.
+ if ( this == pLocalPlayer && !bInToolRecordingMode )
+ 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;
+ }
+
+ CWeaponDODBase *pWeapon = GetActiveDODWeapon();
+ if ( !pWeapon )
+ return;
+
+ int nModelIndex = pWeapon->GetModelIndex();
+ int nWorldModelIndex = pWeapon->GetWorldModelIndex();
+ if ( bInToolRecordingMode && nModelIndex != nWorldModelIndex )
+ {
+ pWeapon->SetModelIndex( nWorldModelIndex );
+ }
+
+ Vector vecOrigin;
+ QAngle angAngles;
+
+ //MATTTODO - use string names of the weapon
+ const static int iMuzzleFlashAttachment = 1;
+ const static int iEjectBrassAttachment = 2;
+
+ // If we have an attachment, then stick a light on it.
+ if ( cl_muzzleflash_dlight_3rd.GetBool() && pWeapon->GetAttachment( iMuzzleFlashAttachment, vecOrigin, angAngles ) )
+ {
+ // Muzzleflash light
+ dlight_t *el = effects->CL_AllocDlight( LIGHT_INDEX_MUZZLEFLASH );
+ el->origin = vecOrigin;
+ el->radius = 70;
+
+ if ( pWeapon->GetDODWpnData().m_WeaponType == WPN_TYPE_SNIPER )
+ el->radius = 150;
+
+ 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;
+
+ if ( bInToolRecordingMode )
+ {
+ Color clr( el->color.r, el->color.g, el->color.b );
+
+ KeyValues *msg = new KeyValues( "TempEntity" );
+
+ msg->SetInt( "te", TE_DYNAMIC_LIGHT );
+ msg->SetString( "name", "TE_DynamicLight" );
+ msg->SetFloat( "time", gpGlobals->curtime );
+ msg->SetFloat( "duration", el->die );
+ msg->SetFloat( "originx", el->origin.x );
+ msg->SetFloat( "originy", el->origin.y );
+ msg->SetFloat( "originz", el->origin.z );
+ msg->SetFloat( "radius", el->radius );
+ msg->SetFloat( "decay", el->decay );
+ msg->SetColor( "color", clr );
+ msg->SetInt( "exponent", el->color.exponent );
+ msg->SetInt( "lightindex", LIGHT_INDEX_MUZZLEFLASH );
+
+ ToolFramework_PostToolMessage( HTOOLHANDLE_INVALID, msg );
+ msg->deleteThis();
+ }
+ }
+
+ const char *pszMuzzleFlashEffect = NULL;
+
+ switch( pWeapon->GetDODWpnData().m_iMuzzleFlashType )
+ {
+ case DOD_MUZZLEFLASH_PISTOL:
+ pszMuzzleFlashEffect = "muzzle_pistols";
+ break;
+ case DOD_MUZZLEFLASH_AUTO:
+ pszMuzzleFlashEffect = "muzzle_fullyautomatic";
+ break;
+ case DOD_MUZZLEFLASH_RIFLE:
+ pszMuzzleFlashEffect = "muzzle_rifles";
+ break;
+ case DOD_MUZZLEFLASH_ROCKET:
+ pszMuzzleFlashEffect = "muzzle_rockets";
+ break;
+ case DOD_MUZZLEFLASH_MG42:
+ pszMuzzleFlashEffect = "muzzle_mg42";
+ break;
+ default:
+ break;
+ }
+
+ if ( pszMuzzleFlashEffect )
+ {
+ DispatchParticleEffect( pszMuzzleFlashEffect, PATTACH_POINT_FOLLOW, pWeapon, 1 );
+ }
+
+ if( pWeapon->ShouldAutoEjectBrass() )
+ {
+ // shell eject
+ if( pWeapon->GetAttachment( iEjectBrassAttachment, vecOrigin, angAngles ) )
+ {
+ int shellType = pWeapon->GetEjectBrassShellType();
+
+ CEffectData data;
+ data.m_nHitBox = shellType;
+ data.m_vOrigin = vecOrigin;
+ data.m_vAngles = angAngles;
+ DispatchEffect( "DOD_EjectBrass", data );
+ }
+ }
+
+ if ( bInToolRecordingMode && nModelIndex != nWorldModelIndex )
+ {
+ pWeapon->SetModelIndex( nModelIndex );
+ }
+}
+
+void C_DODPlayer::NotifyShouldTransmit( ShouldTransmitState_t state )
+{
+ // Remove all effects if we go out of the PVS.
+ if ( state == SHOULDTRANSMIT_END )
+ {
+ if( m_pFlashlightBeam != NULL )
+ {
+ ReleaseFlashlight();
+ }
+ }
+
+ BaseClass::NotifyShouldTransmit( state );
+}
+
+void C_DODPlayer::Simulate( void )
+{
+ if( this != C_BasePlayer::GetLocalPlayer() )
+ {
+ if ( IsEffectActive( EF_DIMLIGHT ) )
+ {
+ QAngle eyeAngles = m_angEyeAngles;
+ Vector vForward;
+ AngleVectors( eyeAngles, &vForward );
+
+ int iAttachment = LookupAttachment( "anim_attachment_RH" );
+
+ 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_DODPlayer::ReleaseFlashlight( void )
+{
+ if( m_pFlashlightBeam )
+ {
+ m_pFlashlightBeam->flags = 0;
+ m_pFlashlightBeam->die = gpGlobals->curtime - 1;
+
+ m_pFlashlightBeam = NULL;
+ }
+}
+
+bool C_DODPlayer::SetFOV( CBaseEntity *pRequester, int FOV, float zoomRate /* = 0.0f */ )
+{
+ /*
+ if( FOV < 30 )
+ {
+ // fade in
+ ScreenFade_t sf;
+ memset( &sf, 0, sizeof( sf ) );
+ sf.a = 255;
+ sf.r = 0;
+ sf.g = 0;
+ sf.b = 0;
+ sf.duration = (unsigned short)((float)(1<<SCREENFADE_FRACBITS) * 2.5f );
+ sf.fadeFlags = FFADE_IN;
+ vieweffects->Fade( sf );
+ }
+ else
+ {
+ //cancel the fade if its active
+ ScreenFade_t sf;
+ memset( &sf, 0, sizeof( sf ) );
+ sf.fadeFlags = FFADE_IN | FFADE_PURGE;
+ vieweffects->Fade( sf );
+ }
+ */
+
+ return true;
+}
+
+void C_DODPlayer::CalcObserverView( Vector& eyeOrigin, QAngle& eyeAngles, float& fov )
+{
+ if( GetObserverMode() == OBS_MODE_DEATHCAM )
+ {
+ CalcDODDeathCamView( eyeOrigin, eyeAngles, fov );
+ }
+ else
+ BaseClass::CalcObserverView( eyeOrigin, eyeAngles, fov );
+}
+
+static Vector WALL_MIN(-WALL_OFFSET,-WALL_OFFSET,-WALL_OFFSET);
+static Vector WALL_MAX(WALL_OFFSET,WALL_OFFSET,WALL_OFFSET);
+
+void C_DODPlayer::CalcDODDeathCamView(Vector& eyeOrigin, QAngle& eyeAngles, float& fov)
+{
+ CBaseEntity * killer = GetObserverTarget();
+
+ //float interpolation = ( gpGlobals->curtime - m_flDeathTime ) / DEATH_ANIMATION_TIME;
+
+ // Interpolate very quickly to the killer and follow
+ float interpolation = ( gpGlobals->curtime - m_flDeathTime ) / 0.2f;
+ interpolation = clamp( interpolation, 0.0f, 1.0f );
+
+ m_flObserverChaseDistance += gpGlobals->frametime*48.0f;
+ m_flObserverChaseDistance = clamp( m_flObserverChaseDistance, CHASE_CAM_DISTANCE_MIN, CHASE_CAM_DISTANCE_MAX );
+
+ QAngle aForward = eyeAngles = EyeAngles();
+ Vector origin = EyePosition();
+
+ IRagdoll *pRagdoll = GetRepresentativeRagdoll();
+ if ( pRagdoll )
+ {
+ origin = pRagdoll->GetRagdollOrigin();
+ origin.z += VEC_DEAD_VIEWHEIGHT_SCALED( this ).z; // look over ragdoll, not through
+ }
+
+ if ( killer && (killer != this) )
+ {
+ Vector vecKiller = killer->GetAbsOrigin();
+
+ C_DODPlayer *player = ToDODPlayer( killer );
+ if ( player && player->IsAlive() )
+ {
+ if ( player->m_Shared.IsProne() )
+ {
+ VectorAdd( vecKiller, VEC_PRONE_VIEW_SCALED( this ), vecKiller );
+ }
+ else if( player->GetFlags() & FL_DUCKING )
+ {
+ VectorAdd( vecKiller, VEC_DUCK_VIEW_SCALED( this ), vecKiller );
+ }
+ else
+ {
+ VectorAdd( vecKiller, VEC_VIEW_SCALED( this ), vecKiller );
+ }
+ }
+
+ Vector vecToKiller = vecKiller - origin;
+ QAngle aKiller;
+ VectorAngles( vecToKiller, aKiller );
+ InterpolateAngles( aForward, aKiller, eyeAngles, interpolation );
+ }
+
+ Vector vForward; AngleVectors( eyeAngles, &vForward );
+
+ VectorNormalize( vForward );
+
+ VectorMA( origin, -m_flObserverChaseDistance, vForward, eyeOrigin );
+
+ 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, this, COLLISION_GROUP_NONE, &trace );
+ C_BaseEntity::PopEnableAbsRecomputations();
+
+ if (trace.fraction < 1.0)
+ {
+ eyeOrigin = trace.endpos;
+ m_flObserverChaseDistance = VectorLength(origin - eyeOrigin);
+ }
+
+ fov = GetFOV();
+}
+
+void C_DODPlayer::CalcChaseCamView(Vector& eyeOrigin, QAngle& eyeAngles, float& fov)
+{
+ C_BaseEntity *target = GetObserverTarget();
+
+ if ( !target )
+ {
+ // just copy a save in-map position
+ VectorCopy( EyePosition(), eyeOrigin );
+ VectorCopy( EyeAngles(), eyeAngles );
+ return;
+ };
+
+ Vector forward, viewpoint;
+
+ // GetRenderOrigin() returns ragdoll pos if player is ragdolled
+ Vector origin = target->GetRenderOrigin();
+
+ C_DODPlayer *player = ToDODPlayer( target );
+
+ if ( player && player->IsAlive() )
+ {
+ if ( player->m_Shared.IsProne() )
+ {
+ VectorAdd( origin, VEC_PRONE_VIEW_SCALED( this ), origin );
+ }
+ else if( player->GetFlags() & FL_DUCKING )
+ {
+ VectorAdd( origin, VEC_DUCK_VIEW_SCALED( this ), origin );
+ }
+ else
+ {
+ VectorAdd( origin, VEC_VIEW_SCALED( this ), origin );
+ }
+ }
+ else
+ {
+ // assume it's the players ragdoll
+ VectorAdd( origin, VEC_DEAD_VIEWHEIGHT_SCALED( this ), origin );
+ }
+
+ QAngle viewangles;
+
+ if ( IsLocalPlayer() )
+ {
+ engine->GetViewAngles( viewangles );
+ }
+ else
+ {
+ viewangles = EyeAngles();
+ }
+
+ m_flObserverChaseDistance += gpGlobals->frametime*48.0f;
+ m_flObserverChaseDistance = clamp( m_flObserverChaseDistance, CHASE_CAM_DISTANCE_MIN, CHASE_CAM_DISTANCE_MAX );
+
+ AngleVectors( viewangles, &forward );
+
+ VectorNormalize( forward );
+
+ VectorMA(origin, -m_flObserverChaseDistance, forward, viewpoint );
+
+ trace_t trace;
+ C_BaseEntity::PushEnableAbsRecomputations( false ); // HACK don't recompute positions while doing RayTrace
+ UTIL_TraceHull( origin, viewpoint, WALL_MIN, WALL_MAX, MASK_SOLID, target, COLLISION_GROUP_NONE, &trace );
+ C_BaseEntity::PopEnableAbsRecomputations();
+
+ if (trace.fraction < 1.0)
+ {
+ viewpoint = trace.endpos;
+ m_flObserverChaseDistance = VectorLength(origin - eyeOrigin);
+ }
+
+ VectorCopy( viewangles, eyeAngles );
+ VectorCopy( viewpoint, eyeOrigin );
+
+ fov = GetFOV();
+}
+
+extern ConVar spec_freeze_traveltime;
+extern ConVar spec_freeze_time;
+extern ConVar cl_dod_freezecam;
+
+//-----------------------------------------------------------------------------
+// Purpose: Calculate the view for the player while he's in freeze frame observer mode
+//-----------------------------------------------------------------------------
+void C_DODPlayer::CalcFreezeCamView( Vector& eyeOrigin, QAngle& eyeAngles, float& fov )
+{
+ C_BaseEntity *pTarget = GetObserverTarget();
+ if ( !pTarget || !cl_dod_freezecam.GetBool() )
+ {
+ CalcDeathCamView( eyeOrigin, eyeAngles, fov );
+ return;
+ }
+
+ // Zoom towards our target
+ float flCurTime = (gpGlobals->curtime - m_flFreezeFrameStartTime);
+ float flBlendPerc = clamp( flCurTime / spec_freeze_traveltime.GetFloat(), 0, 1 );
+ flBlendPerc = SimpleSpline( flBlendPerc );
+
+ // Find the position we would like to be lookin at
+ Vector vecCamDesired = pTarget->GetObserverCamOrigin(); // Returns ragdoll origin if they're ragdolled
+ VectorAdd( vecCamDesired, GetChaseCamViewOffset( pTarget ), vecCamDesired );
+ Vector vecCamTarget = vecCamDesired;
+ if ( !pTarget->IsAlive() )
+ {
+ vecCamTarget.z += pTarget->GetBaseAnimating() ? VEC_DEAD_VIEWHEIGHT_SCALED( pTarget->GetBaseAnimating() ).z : VEC_DEAD_VIEWHEIGHT.z; // look over ragdoll, not through
+ }
+
+ // Figure out a view position in front of the target
+ Vector vecEyeOnPlane = eyeOrigin;
+ vecEyeOnPlane.z = vecCamTarget.z;
+ Vector vecTargetPos = vecCamTarget;
+ Vector vecToTarget = vecTargetPos - vecEyeOnPlane;
+ VectorNormalize( vecToTarget );
+
+ // Stop a few units away from the target, and shift up to be at the same height
+ vecTargetPos = vecCamTarget - (vecToTarget * m_flFreezeFrameDistance);
+ float flEyePosZ = pTarget->EyePosition().z;
+ vecTargetPos.z = flEyePosZ + m_flFreezeZOffset;
+
+ // 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_TraceLine( vecCamTarget, vecTargetPos, 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.
+ vecTargetPos = trace.endpos;
+ vecCamTarget = vecCamDesired;
+
+ // To stop all close in views looking up at character's chins, move the view up.
+ vecTargetPos.z += fabs(vecCamTarget.z - vecTargetPos.z) * 0.85;
+ C_BaseEntity::PushEnableAbsRecomputations( false ); // HACK don't recompute positions while doing RayTrace
+ UTIL_TraceLine( vecCamTarget, vecTargetPos, MASK_SOLID, pTarget, COLLISION_GROUP_NONE, &trace );
+ C_BaseEntity::PopEnableAbsRecomputations();
+ vecTargetPos = trace.endpos;
+ }
+
+ // Look directly at the target
+ vecToTarget = vecCamTarget - vecTargetPos;
+ VectorNormalize( vecToTarget );
+ VectorAngles( vecToTarget, eyeAngles );
+
+ VectorLerp( m_vecFreezeFrameStart, vecTargetPos, flBlendPerc, eyeOrigin );
+
+ if ( flCurTime >= 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() );
+ }
+}
+
+const Vector& C_DODPlayer::GetRenderOrigin( void )
+{
+ if ( !IsAlive() && m_hRagdoll.Get() )
+ return m_hRagdoll.Get()->GetRenderOrigin();
+
+ return BaseClass::GetRenderOrigin();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+Vector C_DODPlayer::GetChaseCamViewOffset( CBaseEntity *target )
+{
+ C_DODPlayer *pPlayer = ToDODPlayer( target );
+
+ if ( pPlayer && pPlayer->IsAlive() )
+ {
+ if ( pPlayer->m_Shared.IsProne() )
+ {
+ return VEC_PRONE_VIEW;
+ }
+ }
+
+ return BaseClass::GetChaseCamViewOffset( target );
+}
+
+const QAngle& C_DODPlayer::EyeAngles()
+{
+ if ( IsLocalPlayer() && g_nKillCamMode == OBS_MODE_NONE )
+ {
+ return BaseClass::EyeAngles();
+ }
+ else
+ {
+ return m_angEyeAngles;
+ }
+}
+
+// Cold breath defines.
+#define COLDBREATH_EMIT_MIN 2.0f
+#define COLDBREATH_EMIT_MAX 3.0f
+#define COLDBREATH_EMIT_SCALE 0.35f
+#define COLDBREATH_PARTICLE_LIFE_MIN 0.25f
+#define COLDBREATH_PARTICLE_LIFE_MAX 1.0f
+#define COLDBREATH_PARTICLE_LIFE_SCALE 0.75
+#define COLDBREATH_PARTICLE_SIZE_MIN 1.0f
+#define COLDBREATH_PARTICLE_SIZE_MAX 4.0f
+#define COLDBREATH_PARTICLE_SIZE_SCALE 1.1f
+#define COLDBREATH_PARTICLE_COUNT 1
+#define COLDBREATH_DURATION_MIN 0.0f
+#define COLDBREATH_DURATION_MAX 1.0f
+#define COLDBREATH_ALPHA_MIN 0.0f
+#define COLDBREATH_ALPHA_MAX 0.3f
+#define COLDBREATH_ENDSCALE_MIN 0.1f
+#define COLDBREATH_ENDSCALE_MAX 0.4f
+
+static ConVar cl_coldbreath_forcestamina( "cl_coldbreath_forcestamina", "0", FCVAR_CHEAT );
+static ConVar cl_coldbreath_enable( "cl_coldbreath_enable", "1" );
+
+//-----------------------------------------------------------------------------
+// Purpose: Create the emitter of cold breath particles
+//-----------------------------------------------------------------------------
+bool C_DODPlayer::CreateColdBreathEmitter( void )
+{
+ // Check to see if we are in a cold breath scenario.
+ if ( !GetClientWorldEntity()->m_bColdWorld )
+ return false;
+
+ // Set cold breath to true.
+ m_bColdBreathOn = true;
+
+ // Create a cold breath emitter if one doesn't already exist.
+ if ( !m_hColdBreathEmitter )
+ {
+ m_hColdBreathEmitter = ColdBreathEmitter::Create( "ColdBreath" );
+ if ( !m_hColdBreathEmitter )
+ return false;
+
+ // Get the particle material.
+ m_hColdBreathMaterial = m_hColdBreathEmitter->GetPMaterial( "sprites/frostbreath" );
+ Assert( m_hColdBreathMaterial != INVALID_MATERIAL_HANDLE );
+
+ // Cache off the head attachment for setting up cold breath.
+ m_iHeadAttach = LookupAttachment( "head" );
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Destroy the cold breath emitter
+//-----------------------------------------------------------------------------
+void C_DODPlayer::DestroyColdBreathEmitter( void )
+{
+#if 0
+ if ( m_hColdBreathEmitter.IsValid() )
+ {
+ UTIL_Remove( m_hColdBreathEmitter );
+ }
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_DODPlayer::UpdateColdBreath( void )
+{
+ if ( !cl_coldbreath_enable.GetBool() )
+ return;
+
+ // Check to see if the cold breath emitter has been created.
+ if ( !m_hColdBreathEmitter.IsValid() )
+ {
+ if ( !CreateColdBreathEmitter() )
+ return;
+ }
+
+ // Cold breath updates.
+ if ( !m_bColdBreathOn )
+ return;
+
+ // Don't emit breath if we are dead.
+ if ( !IsAlive() || IsDormant() )
+ return;
+
+ // Check player speed, do emit cold breath when moving quickly.
+ float flSpeed = GetAbsVelocity().Length();
+ if ( flSpeed > 60.0f )
+ return;
+
+ if ( m_flColdBreathTimeStart < gpGlobals->curtime )
+ {
+ // Spawn cold breath particles.
+ EmitColdBreathParticles();
+
+ // Update the timer.
+ if ( m_flColdBreathTimeEnd < gpGlobals->curtime )
+ {
+ // Check stamina and modify the time accordingly.
+ if ( m_Shared.m_flStamina < LOW_STAMINA_THRESHOLD || cl_coldbreath_forcestamina.GetBool() )
+ {
+ m_flColdBreathTimeStart = gpGlobals->curtime + RandomFloat( COLDBREATH_EMIT_MIN * COLDBREATH_EMIT_SCALE, COLDBREATH_EMIT_MAX * COLDBREATH_EMIT_SCALE );
+ float flDuration = RandomFloat( COLDBREATH_DURATION_MIN, COLDBREATH_DURATION_MAX );
+ m_flColdBreathTimeEnd = m_flColdBreathTimeStart + flDuration;
+ }
+ else
+ {
+ m_flColdBreathTimeStart = gpGlobals->curtime + RandomFloat( COLDBREATH_EMIT_MIN, COLDBREATH_EMIT_MAX );
+ float flDuration = RandomFloat( COLDBREATH_DURATION_MIN, COLDBREATH_DURATION_MAX );
+ m_flColdBreathTimeEnd = m_flColdBreathTimeStart + flDuration;
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_DODPlayer::CalculateIKLocks( float currentTime )
+{
+ if (!m_pIk)
+ return;
+
+ int targetCount = m_pIk->m_target.Count();
+ if ( targetCount == 0 )
+ return;
+
+ // In TF, we might be attaching a player's view to a walking model that's using IK. If we are, it can
+ // get in here during the view setup code, and it's not normally supposed to be able to access the spatial
+ // partition that early in the rendering loop. So we allow access right here for that special case.
+ SpatialPartitionListMask_t curSuppressed = partition->GetSuppressedLists();
+ partition->SuppressLists( PARTITION_ALL_CLIENT_EDICTS, false );
+ CBaseEntity::PushEnableAbsRecomputations( false );
+
+ for (int i = 0; i < targetCount; i++)
+ {
+ trace_t trace;
+ CIKTarget *pTarget = &m_pIk->m_target[i];
+
+ if (!pTarget->IsActive())
+ continue;
+
+ switch( pTarget->type)
+ {
+ case IK_GROUND:
+ {
+ pTarget->SetPos( Vector( pTarget->est.pos.x, pTarget->est.pos.y, GetRenderOrigin().z ));
+ pTarget->SetAngles( GetRenderAngles() );
+ }
+ break;
+
+ case IK_ATTACHMENT:
+ {
+ C_BaseEntity *pEntity = NULL;
+ float flDist = pTarget->est.radius;
+
+ // FIXME: make entity finding sticky!
+ // FIXME: what should the radius check be?
+ for ( CEntitySphereQuery sphere( pTarget->est.pos, 64 ); ( pEntity = sphere.GetCurrentEntity() ) != NULL; sphere.NextEntity() )
+ {
+ C_BaseAnimating *pAnim = pEntity->GetBaseAnimating( );
+ if (!pAnim)
+ continue;
+
+ int iAttachment = pAnim->LookupAttachment( pTarget->offset.pAttachmentName );
+ if (iAttachment <= 0)
+ continue;
+
+ Vector origin;
+ QAngle angles;
+ pAnim->GetAttachment( iAttachment, origin, angles );
+
+ // debugoverlay->AddBoxOverlay( origin, Vector( -1, -1, -1 ), Vector( 1, 1, 1 ), QAngle( 0, 0, 0 ), 255, 0, 0, 0, 0 );
+
+ float d = (pTarget->est.pos - origin).Length();
+
+ if ( d >= flDist)
+ continue;
+
+ flDist = d;
+ pTarget->SetPos( origin );
+ pTarget->SetAngles( angles );
+ // debugoverlay->AddBoxOverlay( pTarget->est.pos, Vector( -pTarget->est.radius, -pTarget->est.radius, -pTarget->est.radius ), Vector( pTarget->est.radius, pTarget->est.radius, pTarget->est.radius), QAngle( 0, 0, 0 ), 0, 255, 0, 0, 0 );
+ }
+
+ if (flDist >= pTarget->est.radius)
+ {
+ // debugoverlay->AddBoxOverlay( pTarget->est.pos, Vector( -pTarget->est.radius, -pTarget->est.radius, -pTarget->est.radius ), Vector( pTarget->est.radius, pTarget->est.radius, pTarget->est.radius), QAngle( 0, 0, 0 ), 0, 0, 255, 0, 0 );
+ // no solution, disable ik rule
+ pTarget->IKFailed( );
+ }
+ }
+ break;
+ }
+ }
+
+ CBaseEntity::PopEnableAbsRecomputations();
+ partition->SuppressLists( curSuppressed, true );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_DODPlayer::EmitColdBreathParticles( void )
+{
+ // Get the position to emit from - look into caching this off we are doing redundant work in the case
+ // of allies (see dod_headiconmanager.cpp).
+ Vector vecOrigin;
+ QAngle vecAngle;
+ GetAttachment( m_iHeadAttach, vecOrigin, vecAngle );
+ Vector vecForward, vecRight, vecUp;
+ AngleVectors( vecAngle, &vecUp, &vecForward, &vecRight );
+
+ vecOrigin += ( vecForward * 8.0f );
+
+ SimpleParticle *pParticle = static_cast<SimpleParticle*>( m_hColdBreathEmitter->AddParticle( sizeof( SimpleParticle ),m_hColdBreathMaterial, vecOrigin ) );
+ if ( pParticle )
+ {
+ pParticle->m_flLifetime = 0.0f;
+ pParticle->m_flDieTime = RandomFloat( COLDBREATH_PARTICLE_LIFE_MIN, COLDBREATH_PARTICLE_LIFE_MAX );
+ if ( m_Shared.m_flStamina < LOW_STAMINA_THRESHOLD || cl_coldbreath_forcestamina.GetBool() )
+ {
+ pParticle->m_flDieTime *= COLDBREATH_PARTICLE_LIFE_SCALE;
+ }
+
+ // Add just a little movement.
+ if ( m_Shared.m_flStamina < LOW_STAMINA_THRESHOLD || cl_coldbreath_forcestamina.GetBool() )
+ {
+ pParticle->m_vecVelocity = ( vecForward * RandomFloat( 10.0f, 30.0f ) ) + ( vecRight * RandomFloat( -2.0f, 2.0f ) ) +
+ ( vecUp * RandomFloat( 0.0f, 0.5f ) );
+ }
+ else
+ {
+ pParticle->m_vecVelocity = ( vecForward * RandomFloat( 10.0f, 20.0f ) ) + ( vecRight * RandomFloat( -2.0f, 2.0f ) ) +
+ ( vecUp * RandomFloat( 0.0f, 1.5f ) );
+ }
+
+ pParticle->m_uchColor[0] = 200;
+ pParticle->m_uchColor[1] = 200;
+ pParticle->m_uchColor[2] = 210;
+
+ float flParticleSize = RandomFloat( COLDBREATH_PARTICLE_SIZE_MIN, COLDBREATH_PARTICLE_SIZE_MAX );
+ float flParticleScale = RandomFloat( COLDBREATH_ENDSCALE_MIN, COLDBREATH_ENDSCALE_MAX );
+ if ( m_Shared.m_flStamina < LOW_STAMINA_THRESHOLD || cl_coldbreath_forcestamina.GetBool() )
+ {
+ pParticle->m_uchEndSize = flParticleSize * COLDBREATH_PARTICLE_SIZE_SCALE;
+ flParticleScale *= COLDBREATH_PARTICLE_SIZE_SCALE;
+ }
+ else
+ {
+ pParticle->m_uchEndSize = flParticleSize;
+ }
+ pParticle->m_uchStartSize = ( flParticleSize * flParticleScale );
+
+ float flAlpha = RandomFloat( COLDBREATH_ALPHA_MIN, COLDBREATH_ALPHA_MAX );
+ pParticle->m_uchStartAlpha = flAlpha * 255;
+ pParticle->m_uchEndAlpha = 0;
+
+ pParticle->m_flRoll = RandomInt( 0, 360 );
+ pParticle->m_flRollDelta = RandomFloat( 0.0f, 1.25f );
+ }
+}
+
+void C_DODPlayer::ComputeWorldSpaceSurroundingBox( Vector *pVecWorldMins, Vector *pVecWorldMaxs )
+{
+ m_Shared.ComputeWorldSpaceSurroundingBox( pVecWorldMins, pVecWorldMaxs );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Try to steer away from any players and objects we might interpenetrate
+//-----------------------------------------------------------------------------
+#define DOD_AVOID_MAX_RADIUS_SQR 5184.0f // Based on player extents and max buildable extents.
+#define DOD_OO_AVOID_MAX_RADIUS_SQR 0.00019f
+
+#define DOD_MAX_SEPARATION_FORCE 256
+
+extern ConVar cl_forwardspeed;
+extern ConVar cl_backspeed;
+extern ConVar cl_sidespeed;
+
+void C_DODPlayer::AvoidPlayers( CUserCmd *pCmd )
+{
+ // Don't test if the player is dead.
+ if ( IsAlive() == false )
+ return;
+
+ C_Team *pTeam = ( C_Team * )GetTeam();
+ if ( !pTeam )
+ return;
+
+ // Up vector.
+ static Vector vecUp( 0.0f, 0.0f, 1.0f );
+
+ Vector vecDODPlayerCenter = GetAbsOrigin();
+ Vector vecDODPlayerMin = GetPlayerMins();
+ Vector vecDODPlayerMax = GetPlayerMaxs();
+ float flZHeight = vecDODPlayerMax.z - vecDODPlayerMin.z;
+ vecDODPlayerCenter.z += 0.5f * flZHeight;
+ VectorAdd( vecDODPlayerMin, vecDODPlayerCenter, vecDODPlayerMin );
+ VectorAdd( vecDODPlayerMax, vecDODPlayerCenter, vecDODPlayerMax );
+
+ // Find an intersecting player or object.
+ int nAvoidPlayerCount = 0;
+ C_DODPlayer *pAvoidPlayerList[MAX_PLAYERS];
+
+ C_DODPlayer *pIntersectPlayer = NULL;
+ float flAvoidRadius = 0.0f;
+
+ Vector vecAvoidCenter, vecAvoidMin, vecAvoidMax;
+ for ( int i = 0; i < pTeam->GetNumPlayers(); ++i )
+ {
+ C_DODPlayer *pAvoidPlayer = static_cast< C_DODPlayer * >( pTeam->GetPlayer( i ) );
+ if ( pAvoidPlayer == NULL )
+ continue;
+ // Is the avoid player me?
+ if ( pAvoidPlayer == this )
+ continue;
+
+ // Save as list to check against for objects.
+ pAvoidPlayerList[nAvoidPlayerCount] = pAvoidPlayer;
+ ++nAvoidPlayerCount;
+
+ // Check to see if the avoid player is dormant.
+ if ( pAvoidPlayer->IsDormant() )
+ continue;
+
+ // Is the avoid player solid?
+ if ( pAvoidPlayer->IsSolidFlagSet( FSOLID_NOT_SOLID ) )
+ continue;
+
+ Vector t1, t2;
+
+ vecAvoidCenter = pAvoidPlayer->GetAbsOrigin();
+ vecAvoidMin = pAvoidPlayer->GetPlayerMins();
+ vecAvoidMax = pAvoidPlayer->GetPlayerMaxs();
+ flZHeight = vecAvoidMax.z - vecAvoidMin.z;
+ vecAvoidCenter.z += 0.5f * flZHeight;
+ VectorAdd( vecAvoidMin, vecAvoidCenter, vecAvoidMin );
+ VectorAdd( vecAvoidMax, vecAvoidCenter, vecAvoidMax );
+
+ if ( IsBoxIntersectingBox( vecDODPlayerMin, vecDODPlayerMax, vecAvoidMin, vecAvoidMax ) )
+ {
+ // Need to avoid this player.
+ if ( !pIntersectPlayer )
+ {
+ pIntersectPlayer = pAvoidPlayer;
+ break;
+ }
+ }
+ }
+
+ // Anything to avoid?
+ if ( !pIntersectPlayer )
+ {
+ return;
+ }
+
+ // Calculate the push strength and direction.
+ Vector vecDelta;
+
+ // Avoid a player - they have precedence.
+ if ( pIntersectPlayer )
+ {
+ VectorSubtract( pIntersectPlayer->WorldSpaceCenter(), vecDODPlayerCenter, vecDelta );
+
+ Vector vRad = pIntersectPlayer->WorldAlignMaxs() - pIntersectPlayer->WorldAlignMins();
+ vRad.z = 0;
+
+ flAvoidRadius = vRad.Length();
+ }
+
+ float flPushStrength = RemapValClamped( vecDelta.Length(), flAvoidRadius, 0, 0, DOD_MAX_SEPARATION_FORCE ); //flPushScale;
+
+ //Msg( "PushScale = %f\n", flPushStrength );
+
+ // Check to see if we have enough push strength to make a difference.
+ if ( flPushStrength < 0.01f )
+ return;
+
+ Vector vecPush;
+ if ( GetAbsVelocity().Length2DSqr() > 0.1f )
+ {
+ Vector vecVelocity = GetAbsVelocity();
+ vecVelocity.z = 0.0f;
+ CrossProduct( vecUp, vecVelocity, vecPush );
+ VectorNormalize( vecPush );
+ }
+ else
+ {
+ // We are not moving, but we're still intersecting.
+ QAngle angView = pCmd->viewangles;
+ angView.x = 0.0f;
+ AngleVectors( angView, NULL, &vecPush, NULL );
+ }
+
+ // Move away from the other player/object.
+ Vector vecSeparationVelocity;
+ if ( vecDelta.Dot( vecPush ) < 0 )
+ {
+ vecSeparationVelocity = vecPush * flPushStrength;
+ }
+ else
+ {
+ vecSeparationVelocity = vecPush * -flPushStrength;
+ }
+
+ // Don't allow the max push speed to be greater than the max player speed.
+ float flMaxPlayerSpeed = MaxSpeed();
+ float flCropFraction = 1.33333333f;
+
+ if ( ( GetFlags() & FL_DUCKING ) && ( GetGroundEntity() != NULL ) )
+ {
+ flMaxPlayerSpeed *= flCropFraction;
+ }
+
+ float flMaxPlayerSpeedSqr = flMaxPlayerSpeed * flMaxPlayerSpeed;
+
+ if ( vecSeparationVelocity.LengthSqr() > flMaxPlayerSpeedSqr )
+ {
+ vecSeparationVelocity.NormalizeInPlace();
+ VectorScale( vecSeparationVelocity, flMaxPlayerSpeed, vecSeparationVelocity );
+ }
+
+ QAngle vAngles = pCmd->viewangles;
+ vAngles.x = 0;
+ Vector currentdir;
+ Vector rightdir;
+
+ AngleVectors( vAngles, &currentdir, &rightdir, NULL );
+
+ Vector vDirection = vecSeparationVelocity;
+
+ VectorNormalize( vDirection );
+
+ float fwd = currentdir.Dot( vDirection );
+ float rt = rightdir.Dot( vDirection );
+
+ float forward = fwd * flPushStrength;
+ float side = rt * flPushStrength;
+
+ //Msg( "fwd: %f - rt: %f - forward: %f - side: %f\n", fwd, rt, forward, side );
+
+ pCmd->forwardmove += forward;
+ pCmd->sidemove += side;
+
+ // Clamp the move to within legal limits, preserving direction. This is a little
+ // complicated because we have different limits for forward, back, and side
+
+ //Msg( "PRECLAMP: forwardmove=%f, sidemove=%f\n", pCmd->forwardmove, pCmd->sidemove );
+
+ float flForwardScale = 1.0f;
+ if ( pCmd->forwardmove > fabs( cl_forwardspeed.GetFloat() ) )
+ {
+ flForwardScale = fabs( cl_forwardspeed.GetFloat() ) / pCmd->forwardmove;
+ }
+ else if ( pCmd->forwardmove < -fabs( cl_backspeed.GetFloat() ) )
+ {
+ flForwardScale = fabs( cl_backspeed.GetFloat() ) / fabs( pCmd->forwardmove );
+ }
+
+ float flSideScale = 1.0f;
+ if ( fabs( pCmd->sidemove ) > fabs( cl_sidespeed.GetFloat() ) )
+ {
+ flSideScale = fabs( cl_sidespeed.GetFloat() ) / fabs( pCmd->sidemove );
+ }
+
+ float flScale = MIN( flForwardScale, flSideScale );
+ pCmd->forwardmove *= flScale;
+ pCmd->sidemove *= flScale;
+
+ //Msg( "Pforwardmove=%f, sidemove=%f\n", pCmd->forwardmove, pCmd->sidemove );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns whether this player is the nemesis of the local player
+//-----------------------------------------------------------------------------
+bool C_DODPlayer::IsNemesisOfLocalPlayer()
+{
+ C_DODPlayer *pLocalPlayer = C_DODPlayer::GetLocalDODPlayer();
+ if ( pLocalPlayer )
+ {
+ // return whether this player is dominating the local player
+ return m_Shared.IsPlayerDominated( pLocalPlayer->entindex() );
+ }
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns whether we should show the nemesis icon for this player
+//-----------------------------------------------------------------------------
+bool C_DODPlayer::ShouldShowNemesisIcon()
+{
+ /*
+ // we should show the nemesis effect on this player if he is the nemesis of the local player,
+ // and is not dead, cloaked or disguised
+ if ( IsNemesisOfLocalPlayer() && g_DODPR && g_PR->IsConnected( entindex() ) )
+ {
+ if ( IsAlive() )
+ return true;
+ }
+ */
+ return false;
+}
+
+int C_DODPlayer::GetActiveAchievementAward( void )
+{
+ int iAward = ACHIEVEMENT_AWARDS_NONE;
+
+ int iClassBit = m_Shared.PlayerClass() + 1;
+
+ if ( m_iAchievementAwardsMask & (1<<ACHIEVEMENT_AWARDS_ALL_PACK_1) )
+ {
+ iAward = ACHIEVEMENT_AWARDS_ALL_PACK_1;
+ }
+ else if ( m_iAchievementAwardsMask & ( 1<<iClassBit ) )
+ {
+ iAward = iClassBit;
+ }
+
+ return iAward;
+}
+
+IMaterial *C_DODPlayer::GetHeadIconMaterial( void )
+{
+ const char *pszMaterial = "";
+
+ int iAchievementAward = GetActiveAchievementAward();
+
+ if ( iAchievementAward >= 0 && iAchievementAward < NUM_ACHIEVEMENT_AWARDS )
+ {
+ switch ( GetTeamNumber() )
+ {
+ case TEAM_ALLIES:
+ pszMaterial = g_pszAchievementAwardMaterials_Allies[iAchievementAward];
+ break;
+ case TEAM_AXIS:
+ pszMaterial = g_pszAchievementAwardMaterials_Axis[iAchievementAward];
+ break;
+ default:
+ break;
+ }
+ }
+
+ IMaterial *pMaterial = NULL;
+ if ( pszMaterial )
+ {
+ pMaterial = materials->FindMaterial( pszMaterial, TEXTURE_GROUP_VGUI );
+ }
+
+ // clear the old one if its different
+ if ( m_pHeadIconMaterial != pMaterial )
+ {
+ if ( m_pHeadIconMaterial )
+ {
+ m_pHeadIconMaterial->DecrementReferenceCount();
+ }
+
+ m_pHeadIconMaterial = pMaterial;
+ m_pHeadIconMaterial->IncrementReferenceCount();
+ }
+
+ return m_pHeadIconMaterial;
+}