diff options
| author | Joe Ludwig <[email protected]> | 2013-06-26 15:22:04 -0700 |
|---|---|---|
| committer | Joe Ludwig <[email protected]> | 2013-06-26 15:22:04 -0700 |
| commit | 39ed87570bdb2f86969d4be821c94b722dc71179 (patch) | |
| tree | abc53757f75f40c80278e87650ea92808274aa59 /mp/src/game/client/c_baseplayer.cpp | |
| download | source-sdk-2013-39ed87570bdb2f86969d4be821c94b722dc71179.tar.xz source-sdk-2013-39ed87570bdb2f86969d4be821c94b722dc71179.zip | |
First version of the SOurce SDK 2013
Diffstat (limited to 'mp/src/game/client/c_baseplayer.cpp')
| -rw-r--r-- | mp/src/game/client/c_baseplayer.cpp | 2984 |
1 files changed, 2984 insertions, 0 deletions
diff --git a/mp/src/game/client/c_baseplayer.cpp b/mp/src/game/client/c_baseplayer.cpp new file mode 100644 index 00000000..8e4ff89f --- /dev/null +++ b/mp/src/game/client/c_baseplayer.cpp @@ -0,0 +1,2984 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Client-side CBasePlayer.
+//
+// - Manages the player's flashlight effect.
+//
+//===========================================================================//
+#include "cbase.h"
+#include "c_baseplayer.h"
+#include "flashlighteffect.h"
+#include "weapon_selection.h"
+#include "history_resource.h"
+#include "iinput.h"
+#include "input.h"
+#include "view.h"
+#include "iviewrender.h"
+#include "iclientmode.h"
+#include "in_buttons.h"
+#include "engine/IEngineSound.h"
+#include "c_soundscape.h"
+#include "usercmd.h"
+#include "c_playerresource.h"
+#include "iclientvehicle.h"
+#include "view_shared.h"
+#include "movevars_shared.h"
+#include "prediction.h"
+#include "tier0/vprof.h"
+#include "filesystem.h"
+#include "bitbuf.h"
+#include "KeyValues.h"
+#include "particles_simple.h"
+#include "fx_water.h"
+#include "hltvcamera.h"
+#include "toolframework/itoolframework.h"
+#include "toolframework_client.h"
+#include "view_scene.h"
+#include "c_vguiscreen.h"
+#include "datacache/imdlcache.h"
+#include "vgui/ISurface.h"
+#include "voice_status.h"
+#include "fx.h"
+#include "dt_utlvector_recv.h"
+#include "cam_thirdperson.h"
+#if defined( REPLAY_ENABLED )
+#include "replay/replaycamera.h"
+#include "replay/ireplaysystem.h"
+#include "replay/ienginereplay.h"
+#endif
+#include "steam/steam_api.h"
+#include "headtrack/isourcevirtualreality.h"
+#include "client_virtualreality.h"
+
+#if defined USES_ECON_ITEMS
+#include "econ_wearable.h"
+#endif
+
+// NVNT haptics system interface
+#include "haptics/ihaptics.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+// Don't alias here
+#if defined( CBasePlayer )
+#undef CBasePlayer
+#endif
+
+int g_nKillCamMode = OBS_MODE_NONE;
+int g_nKillCamTarget1 = 0;
+int g_nKillCamTarget2 = 0;
+
+extern ConVar mp_forcecamera; // in gamevars_shared.h
+
+#define FLASHLIGHT_DISTANCE 1000
+#define MAX_VGUI_INPUT_MODE_SPEED 30
+#define MAX_VGUI_INPUT_MODE_SPEED_SQ (MAX_VGUI_INPUT_MODE_SPEED*MAX_VGUI_INPUT_MODE_SPEED)
+
+static Vector WALL_MIN(-WALL_OFFSET,-WALL_OFFSET,-WALL_OFFSET);
+static Vector WALL_MAX(WALL_OFFSET,WALL_OFFSET,WALL_OFFSET);
+
+bool CommentaryModeShouldSwallowInput( C_BasePlayer *pPlayer );
+
+extern ConVar default_fov;
+#ifndef _XBOX
+extern ConVar sensitivity;
+#endif
+
+static C_BasePlayer *s_pLocalPlayer = NULL;
+
+static ConVar cl_customsounds ( "cl_customsounds", "0", 0, "Enable customized player sound playback" );
+static ConVar spec_track ( "spec_track", "0", 0, "Tracks an entity in spec mode" );
+static ConVar cl_smooth ( "cl_smooth", "1", 0, "Smooth view/eye origin after prediction errors" );
+static ConVar cl_smoothtime (
+ "cl_smoothtime",
+ "0.1",
+ 0,
+ "Smooth client's view after prediction error over this many seconds",
+ true, 0.01, // min/max is 0.01/2.0
+ true, 2.0
+ );
+
+#ifdef CSTRIKE_DLL
+ConVar spec_freeze_time( "spec_freeze_time", "5.0", FCVAR_CHEAT | FCVAR_REPLICATED, "Time spend frozen in observer freeze cam." );
+ConVar spec_freeze_traveltime( "spec_freeze_traveltime", "0.7", FCVAR_CHEAT | FCVAR_REPLICATED, "Time taken to zoom in to frame a target in observer freeze cam.", true, 0.01, false, 0 );
+ConVar spec_freeze_distance_min( "spec_freeze_distance_min", "80", FCVAR_CHEAT, "Minimum random distance from the target to stop when framing them in observer freeze cam." );
+ConVar spec_freeze_distance_max( "spec_freeze_distance_max", "90", FCVAR_CHEAT, "Maximum random distance from the target to stop when framing them in observer freeze cam." );
+#else
+ConVar spec_freeze_time( "spec_freeze_time", "4.0", FCVAR_CHEAT | FCVAR_REPLICATED, "Time spend frozen in observer freeze cam." );
+ConVar spec_freeze_traveltime( "spec_freeze_traveltime", "0.4", FCVAR_CHEAT | FCVAR_REPLICATED, "Time taken to zoom in to frame a target in observer freeze cam.", true, 0.01, false, 0 );
+ConVar spec_freeze_distance_min( "spec_freeze_distance_min", "96", FCVAR_CHEAT, "Minimum random distance from the target to stop when framing them in observer freeze cam." );
+ConVar spec_freeze_distance_max( "spec_freeze_distance_max", "200", FCVAR_CHEAT, "Maximum random distance from the target to stop when framing them in observer freeze cam." );
+#endif
+
+static ConVar cl_first_person_uses_world_model ( "cl_first_person_uses_world_model", "0", FCVAR_ARCHIVE, "Causes the third person model to be drawn instead of the view model" );
+
+ConVar demo_fov_override( "demo_fov_override", "0", FCVAR_CLIENTDLL | FCVAR_DONTRECORD, "If nonzero, this value will be used to override FOV during demo playback." );
+
+void RecvProxy_LocalVelocityX( const CRecvProxyData *pData, void *pStruct, void *pOut );
+void RecvProxy_LocalVelocityY( const CRecvProxyData *pData, void *pStruct, void *pOut );
+void RecvProxy_LocalVelocityZ( const CRecvProxyData *pData, void *pStruct, void *pOut );
+
+void RecvProxy_ObserverTarget( const CRecvProxyData *pData, void *pStruct, void *pOut );
+void RecvProxy_ObserverMode ( const CRecvProxyData *pData, void *pStruct, void *pOut );
+
+// -------------------------------------------------------------------------------- //
+// RecvTable for CPlayerState.
+// -------------------------------------------------------------------------------- //
+
+ BEGIN_RECV_TABLE_NOBASE(CPlayerState, DT_PlayerState)
+ RecvPropInt (RECVINFO(deadflag)),
+ END_RECV_TABLE()
+
+
+BEGIN_RECV_TABLE_NOBASE( CPlayerLocalData, DT_Local )
+ RecvPropArray3( RECVINFO_ARRAY(m_chAreaBits), RecvPropInt(RECVINFO(m_chAreaBits[0]))),
+ RecvPropArray3( RECVINFO_ARRAY(m_chAreaPortalBits), RecvPropInt(RECVINFO(m_chAreaPortalBits[0]))),
+ RecvPropInt(RECVINFO(m_iHideHUD)),
+
+ // View
+
+ RecvPropFloat(RECVINFO(m_flFOVRate)),
+
+ RecvPropInt (RECVINFO(m_bDucked)),
+ RecvPropInt (RECVINFO(m_bDucking)),
+ RecvPropInt (RECVINFO(m_bInDuckJump)),
+ RecvPropFloat (RECVINFO(m_flDucktime)),
+ RecvPropFloat (RECVINFO(m_flDuckJumpTime)),
+ RecvPropFloat (RECVINFO(m_flJumpTime)),
+ RecvPropFloat (RECVINFO(m_flFallVelocity)),
+
+#if PREDICTION_ERROR_CHECK_LEVEL > 1
+ RecvPropFloat (RECVINFO_NAME( m_vecPunchAngle.m_Value[0], m_vecPunchAngle[0])),
+ RecvPropFloat (RECVINFO_NAME( m_vecPunchAngle.m_Value[1], m_vecPunchAngle[1])),
+ RecvPropFloat (RECVINFO_NAME( m_vecPunchAngle.m_Value[2], m_vecPunchAngle[2] )),
+ RecvPropFloat (RECVINFO_NAME( m_vecPunchAngleVel.m_Value[0], m_vecPunchAngleVel[0] )),
+ RecvPropFloat (RECVINFO_NAME( m_vecPunchAngleVel.m_Value[1], m_vecPunchAngleVel[1] )),
+ RecvPropFloat (RECVINFO_NAME( m_vecPunchAngleVel.m_Value[2], m_vecPunchAngleVel[2] )),
+#else
+ RecvPropVector (RECVINFO(m_vecPunchAngle)),
+ RecvPropVector (RECVINFO(m_vecPunchAngleVel)),
+#endif
+
+ RecvPropInt (RECVINFO(m_bDrawViewmodel)),
+ RecvPropInt (RECVINFO(m_bWearingSuit)),
+ RecvPropBool (RECVINFO(m_bPoisoned)),
+ RecvPropFloat (RECVINFO(m_flStepSize)),
+ RecvPropInt (RECVINFO(m_bAllowAutoMovement)),
+
+ // 3d skybox data
+ RecvPropInt(RECVINFO(m_skybox3d.scale)),
+ RecvPropVector(RECVINFO(m_skybox3d.origin)),
+ RecvPropInt(RECVINFO(m_skybox3d.area)),
+
+ // 3d skybox fog data
+ RecvPropInt( RECVINFO( m_skybox3d.fog.enable ) ),
+ RecvPropInt( RECVINFO( m_skybox3d.fog.blend ) ),
+ RecvPropVector( RECVINFO( m_skybox3d.fog.dirPrimary ) ),
+ RecvPropInt( RECVINFO( m_skybox3d.fog.colorPrimary ) ),
+ RecvPropInt( RECVINFO( m_skybox3d.fog.colorSecondary ) ),
+ RecvPropFloat( RECVINFO( m_skybox3d.fog.start ) ),
+ RecvPropFloat( RECVINFO( m_skybox3d.fog.end ) ),
+ RecvPropFloat( RECVINFO( m_skybox3d.fog.maxdensity ) ),
+
+ // fog data
+ RecvPropEHandle( RECVINFO( m_PlayerFog.m_hCtrl ) ),
+
+ // audio data
+ RecvPropVector( RECVINFO( m_audio.localSound[0] ) ),
+ RecvPropVector( RECVINFO( m_audio.localSound[1] ) ),
+ RecvPropVector( RECVINFO( m_audio.localSound[2] ) ),
+ RecvPropVector( RECVINFO( m_audio.localSound[3] ) ),
+ RecvPropVector( RECVINFO( m_audio.localSound[4] ) ),
+ RecvPropVector( RECVINFO( m_audio.localSound[5] ) ),
+ RecvPropVector( RECVINFO( m_audio.localSound[6] ) ),
+ RecvPropVector( RECVINFO( m_audio.localSound[7] ) ),
+ RecvPropInt( RECVINFO( m_audio.soundscapeIndex ) ),
+ RecvPropInt( RECVINFO( m_audio.localBits ) ),
+ RecvPropEHandle( RECVINFO( m_audio.ent ) ),
+END_RECV_TABLE()
+
+// -------------------------------------------------------------------------------- //
+// This data only gets sent to clients that ARE this player entity.
+// -------------------------------------------------------------------------------- //
+
+ BEGIN_RECV_TABLE_NOBASE( C_BasePlayer, DT_LocalPlayerExclusive )
+
+ RecvPropDataTable ( RECVINFO_DT(m_Local),0, &REFERENCE_RECV_TABLE(DT_Local) ),
+
+ RecvPropFloat ( RECVINFO(m_vecViewOffset[0]) ),
+ RecvPropFloat ( RECVINFO(m_vecViewOffset[1]) ),
+ RecvPropFloat ( RECVINFO(m_vecViewOffset[2]) ),
+ RecvPropFloat ( RECVINFO(m_flFriction) ),
+
+ RecvPropArray3 ( RECVINFO_ARRAY(m_iAmmo), RecvPropInt( RECVINFO(m_iAmmo[0])) ),
+
+ RecvPropInt ( RECVINFO(m_fOnTarget) ),
+
+ RecvPropInt ( RECVINFO( m_nTickBase ) ),
+ RecvPropInt ( RECVINFO( m_nNextThinkTick ) ),
+
+ RecvPropEHandle ( RECVINFO( m_hLastWeapon ) ),
+ RecvPropEHandle ( RECVINFO( m_hGroundEntity ) ),
+
+ RecvPropFloat ( RECVINFO(m_vecVelocity[0]), 0, RecvProxy_LocalVelocityX ),
+ RecvPropFloat ( RECVINFO(m_vecVelocity[1]), 0, RecvProxy_LocalVelocityY ),
+ RecvPropFloat ( RECVINFO(m_vecVelocity[2]), 0, RecvProxy_LocalVelocityZ ),
+
+ RecvPropVector ( RECVINFO( m_vecBaseVelocity ) ),
+
+ RecvPropEHandle ( RECVINFO( m_hConstraintEntity)),
+ RecvPropVector ( RECVINFO( m_vecConstraintCenter) ),
+ RecvPropFloat ( RECVINFO( m_flConstraintRadius )),
+ RecvPropFloat ( RECVINFO( m_flConstraintWidth )),
+ RecvPropFloat ( RECVINFO( m_flConstraintSpeedFactor )),
+
+ RecvPropFloat ( RECVINFO( m_flDeathTime )),
+
+ RecvPropInt ( RECVINFO( m_nWaterLevel ) ),
+ RecvPropFloat ( RECVINFO( m_flLaggedMovementValue )),
+
+ END_RECV_TABLE()
+
+
+// -------------------------------------------------------------------------------- //
+// DT_BasePlayer datatable.
+// -------------------------------------------------------------------------------- //
+
+#if defined USES_ECON_ITEMS
+ EXTERN_RECV_TABLE(DT_AttributeList);
+#endif
+
+ IMPLEMENT_CLIENTCLASS_DT(C_BasePlayer, DT_BasePlayer, CBasePlayer)
+ // We have both the local and nonlocal data in here, but the server proxies
+ // only send one.
+ RecvPropDataTable( "localdata", 0, 0, &REFERENCE_RECV_TABLE(DT_LocalPlayerExclusive) ),
+
+#if defined USES_ECON_ITEMS
+ RecvPropDataTable(RECVINFO_DT(m_AttributeList),0, &REFERENCE_RECV_TABLE(DT_AttributeList) ),
+#endif
+
+ RecvPropDataTable(RECVINFO_DT(pl), 0, &REFERENCE_RECV_TABLE(DT_PlayerState), DataTableRecvProxy_StaticDataTable),
+
+ RecvPropInt (RECVINFO(m_iFOV)),
+ RecvPropInt (RECVINFO(m_iFOVStart)),
+ RecvPropFloat (RECVINFO(m_flFOVTime)),
+ RecvPropInt (RECVINFO(m_iDefaultFOV)),
+ RecvPropEHandle (RECVINFO(m_hZoomOwner)),
+
+ RecvPropEHandle( RECVINFO(m_hVehicle) ),
+ RecvPropEHandle( RECVINFO(m_hUseEntity) ),
+
+ RecvPropInt (RECVINFO(m_iHealth)),
+ RecvPropInt (RECVINFO(m_lifeState)),
+
+ RecvPropInt (RECVINFO(m_iBonusProgress)),
+ RecvPropInt (RECVINFO(m_iBonusChallenge)),
+
+ RecvPropFloat (RECVINFO(m_flMaxspeed)),
+ RecvPropInt (RECVINFO(m_fFlags)),
+
+
+ RecvPropInt (RECVINFO(m_iObserverMode), 0, RecvProxy_ObserverMode ),
+ RecvPropEHandle (RECVINFO(m_hObserverTarget), RecvProxy_ObserverTarget ),
+ RecvPropArray ( RecvPropEHandle( RECVINFO( m_hViewModel[0] ) ), m_hViewModel ),
+
+
+ RecvPropString( RECVINFO(m_szLastPlaceName) ),
+
+#if defined USES_ECON_ITEMS
+ RecvPropUtlVector( RECVINFO_UTLVECTOR( m_hMyWearables ), MAX_WEARABLES_SENT_FROM_SERVER, RecvPropEHandle(NULL, 0, 0) ),
+#endif
+
+ END_RECV_TABLE()
+
+BEGIN_PREDICTION_DATA_NO_BASE( CPlayerState )
+
+ DEFINE_PRED_FIELD( deadflag, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
+ // DEFINE_FIELD( netname, string_t ),
+ // DEFINE_FIELD( fixangle, FIELD_INTEGER ),
+ // DEFINE_FIELD( anglechange, FIELD_FLOAT ),
+ // DEFINE_FIELD( v_angle, FIELD_VECTOR ),
+
+END_PREDICTION_DATA()
+
+BEGIN_PREDICTION_DATA_NO_BASE( CPlayerLocalData )
+
+ // DEFINE_PRED_TYPEDESCRIPTION( m_skybox3d, sky3dparams_t ),
+ // DEFINE_PRED_TYPEDESCRIPTION( m_fog, fogparams_t ),
+ // DEFINE_PRED_TYPEDESCRIPTION( m_audio, audioparams_t ),
+ DEFINE_FIELD( m_nStepside, FIELD_INTEGER ),
+
+ DEFINE_PRED_FIELD( m_iHideHUD, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
+#if PREDICTION_ERROR_CHECK_LEVEL > 1
+ DEFINE_PRED_FIELD( m_vecPunchAngle, FIELD_VECTOR, FTYPEDESC_INSENDTABLE ),
+ DEFINE_PRED_FIELD( m_vecPunchAngleVel, FIELD_VECTOR, FTYPEDESC_INSENDTABLE ),
+#else
+ DEFINE_PRED_FIELD_TOL( m_vecPunchAngle, FIELD_VECTOR, FTYPEDESC_INSENDTABLE, 0.125f ),
+ DEFINE_PRED_FIELD_TOL( m_vecPunchAngleVel, FIELD_VECTOR, FTYPEDESC_INSENDTABLE, 0.125f ),
+#endif
+ DEFINE_PRED_FIELD( m_bDrawViewmodel, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
+ DEFINE_PRED_FIELD( m_bWearingSuit, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
+ DEFINE_PRED_FIELD( m_bPoisoned, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
+ DEFINE_PRED_FIELD( m_bAllowAutoMovement, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
+
+ DEFINE_PRED_FIELD( m_bDucked, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
+ DEFINE_PRED_FIELD( m_bDucking, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
+ DEFINE_PRED_FIELD( m_bInDuckJump, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
+ DEFINE_PRED_FIELD( m_flDucktime, FIELD_FLOAT, FTYPEDESC_INSENDTABLE ),
+ DEFINE_PRED_FIELD( m_flDuckJumpTime, FIELD_FLOAT, FTYPEDESC_INSENDTABLE ),
+ DEFINE_PRED_FIELD( m_flJumpTime, FIELD_FLOAT, FTYPEDESC_INSENDTABLE ),
+ DEFINE_PRED_FIELD_TOL( m_flFallVelocity, FIELD_FLOAT, FTYPEDESC_INSENDTABLE, 0.5f ),
+// DEFINE_PRED_FIELD( m_nOldButtons, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
+ DEFINE_FIELD( m_nOldButtons, FIELD_INTEGER ),
+ DEFINE_PRED_FIELD( m_flStepSize, FIELD_FLOAT, FTYPEDESC_INSENDTABLE ),
+ DEFINE_FIELD( m_flFOVRate, FIELD_FLOAT ),
+
+END_PREDICTION_DATA()
+
+BEGIN_PREDICTION_DATA( C_BasePlayer )
+
+ DEFINE_PRED_TYPEDESCRIPTION( m_Local, CPlayerLocalData ),
+ DEFINE_PRED_TYPEDESCRIPTION( pl, CPlayerState ),
+
+ DEFINE_PRED_FIELD( m_iFOV, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
+ DEFINE_PRED_FIELD( m_hZoomOwner, FIELD_EHANDLE, FTYPEDESC_INSENDTABLE ),
+ DEFINE_PRED_FIELD( m_flFOVTime, FIELD_FLOAT, 0 ),
+ DEFINE_PRED_FIELD( m_iFOVStart, FIELD_INTEGER, 0 ),
+
+ DEFINE_PRED_FIELD( m_hVehicle, FIELD_EHANDLE, FTYPEDESC_INSENDTABLE ),
+ DEFINE_PRED_FIELD_TOL( m_flMaxspeed, FIELD_FLOAT, FTYPEDESC_INSENDTABLE, 0.5f ),
+ DEFINE_PRED_FIELD( m_iHealth, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
+ DEFINE_PRED_FIELD( m_iBonusProgress, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
+ DEFINE_PRED_FIELD( m_iBonusChallenge, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
+ DEFINE_PRED_FIELD( m_fOnTarget, FIELD_BOOLEAN, FTYPEDESC_INSENDTABLE ),
+ DEFINE_PRED_FIELD( m_nNextThinkTick, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
+ DEFINE_PRED_FIELD( m_lifeState, FIELD_CHARACTER, FTYPEDESC_INSENDTABLE ),
+ DEFINE_PRED_FIELD( m_nWaterLevel, FIELD_CHARACTER, FTYPEDESC_INSENDTABLE ),
+
+ DEFINE_PRED_FIELD_TOL( m_vecBaseVelocity, FIELD_VECTOR, FTYPEDESC_INSENDTABLE, 0.05 ),
+
+ DEFINE_FIELD( m_nButtons, FIELD_INTEGER ),
+ DEFINE_FIELD( m_flWaterJumpTime, FIELD_FLOAT ),
+ DEFINE_FIELD( m_nImpulse, FIELD_INTEGER ),
+ DEFINE_FIELD( m_flStepSoundTime, FIELD_FLOAT ),
+ DEFINE_FIELD( m_flSwimSoundTime, FIELD_FLOAT ),
+ DEFINE_FIELD( m_vecLadderNormal, FIELD_VECTOR ),
+ DEFINE_FIELD( m_flPhysics, FIELD_INTEGER ),
+ DEFINE_AUTO_ARRAY( m_szAnimExtension, FIELD_CHARACTER ),
+ DEFINE_FIELD( m_afButtonLast, FIELD_INTEGER ),
+ DEFINE_FIELD( m_afButtonPressed, FIELD_INTEGER ),
+ DEFINE_FIELD( m_afButtonReleased, FIELD_INTEGER ),
+ // DEFINE_FIELD( m_vecOldViewAngles, FIELD_VECTOR ),
+
+ // DEFINE_ARRAY( m_iOldAmmo, FIELD_INTEGER, MAX_AMMO_TYPES ),
+
+ //DEFINE_FIELD( m_hOldVehicle, FIELD_EHANDLE ),
+ // DEFINE_FIELD( m_pModelLight, dlight_t* ),
+ // DEFINE_FIELD( m_pEnvironmentLight, dlight_t* ),
+ // DEFINE_FIELD( m_pBrightLight, dlight_t* ),
+ DEFINE_PRED_FIELD( m_hLastWeapon, FIELD_EHANDLE, FTYPEDESC_INSENDTABLE ),
+
+ DEFINE_PRED_FIELD( m_nTickBase, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
+
+ DEFINE_PRED_FIELD( m_hGroundEntity, FIELD_EHANDLE, FTYPEDESC_INSENDTABLE ),
+
+ DEFINE_PRED_ARRAY( m_hViewModel, FIELD_EHANDLE, MAX_VIEWMODELS, FTYPEDESC_INSENDTABLE ),
+
+ DEFINE_FIELD( m_surfaceFriction, FIELD_FLOAT ),
+
+END_PREDICTION_DATA()
+
+LINK_ENTITY_TO_CLASS( player, C_BasePlayer );
+
+// -------------------------------------------------------------------------------- //
+// Functions.
+// -------------------------------------------------------------------------------- //
+C_BasePlayer::C_BasePlayer() : m_iv_vecViewOffset( "C_BasePlayer::m_iv_vecViewOffset" )
+{
+ AddVar( &m_vecViewOffset, &m_iv_vecViewOffset, LATCH_SIMULATION_VAR );
+
+#ifdef _DEBUG
+ m_vecLadderNormal.Init();
+ m_vecOldViewAngles.Init();
+#endif
+
+ m_pFlashlight = NULL;
+
+ m_pCurrentVguiScreen = NULL;
+ m_pCurrentCommand = NULL;
+
+ m_flPredictionErrorTime = -100;
+ m_StuckLast = 0;
+ m_bWasFrozen = false;
+
+ m_bResampleWaterSurface = true;
+
+ ResetObserverMode();
+
+ m_vecPredictionError.Init();
+ m_flPredictionErrorTime = 0;
+
+ m_surfaceProps = 0;
+ m_pSurfaceData = NULL;
+ m_surfaceFriction = 1.0f;
+ m_chTextureType = 0;
+
+ m_flNextAchievementAnnounceTime = 0;
+
+ m_bFiredWeapon = false;
+
+ m_nForceVisionFilterFlags = 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+C_BasePlayer::~C_BasePlayer()
+{
+ DeactivateVguiScreen( m_pCurrentVguiScreen.Get() );
+ if ( this == s_pLocalPlayer )
+ {
+ s_pLocalPlayer = NULL;
+ }
+
+ delete m_pFlashlight;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_BasePlayer::Spawn( void )
+{
+ // Clear all flags except for FL_FULLEDICT
+ ClearFlags();
+ AddFlag( FL_CLIENT );
+
+ int effects = GetEffects() & EF_NOSHADOW;
+ SetEffects( effects );
+
+ m_iFOV = 0; // init field of view.
+
+ SetModel( "models/player.mdl" );
+
+ Precache();
+
+ SetThink(NULL);
+
+ SharedSpawn();
+
+ m_bWasFreezeFraming = false;
+
+ m_bFiredWeapon = false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool C_BasePlayer::AudioStateIsUnderwater( Vector vecMainViewOrigin )
+{
+ if ( IsObserver() )
+ {
+ // Just check the view position
+ int cont = enginetrace->GetPointContents ( vecMainViewOrigin );
+ return (cont & MASK_WATER);
+ }
+
+ return ( GetWaterLevel() >= WL_Eyes );
+}
+
+bool C_BasePlayer::IsHLTV() const
+{
+ return ( IsLocalPlayer() && engine->IsHLTV() );
+}
+
+bool C_BasePlayer::IsReplay() const
+{
+#if defined( REPLAY_ENABLED )
+ return ( IsLocalPlayer() && g_pEngineClientReplay->IsPlayingReplayDemo() );
+#else
+ return false;
+#endif
+}
+
+CBaseEntity *C_BasePlayer::GetObserverTarget() const // returns players target or NULL
+{
+#ifndef _XBOX
+ if ( IsHLTV() )
+ {
+ return HLTVCamera()->GetPrimaryTarget();
+ }
+#if defined( REPLAY_ENABLED )
+ if ( IsReplay() )
+ {
+ return ReplayCamera()->GetPrimaryTarget();
+ }
+#endif
+#endif
+
+ if ( GetObserverMode() == OBS_MODE_ROAMING )
+ {
+ return NULL; // no target in roaming mode
+ }
+ else
+ {
+ if ( IsLocalPlayer() && UseVR() )
+ {
+ // In VR mode, certain views cause disorientation and nausea. So let's not.
+ switch ( m_iObserverMode )
+ {
+ case OBS_MODE_NONE: // not in spectator mode
+ case OBS_MODE_FIXED: // view from a fixed camera position
+ case OBS_MODE_IN_EYE: // follow a player in first person view
+ case OBS_MODE_CHASE: // follow a player in third person view
+ case OBS_MODE_ROAMING: // free roaming
+ return m_hObserverTarget;
+ break;
+ case OBS_MODE_DEATHCAM: // special mode for death cam animation
+ case OBS_MODE_FREEZECAM: // zooms to a target, and freeze-frames on them
+ // These are both terrible - they get overriden to chase, but here we change it to "chase" your own body (which will be ragdolled).
+ return (const_cast<C_BasePlayer*>(this))->GetBaseEntity();
+ break;
+ default:
+ assert ( false );
+ break;
+ }
+ }
+
+ return m_hObserverTarget;
+ }
+}
+
+// Called from Recv Proxy, mainly to reset tone map scale
+void C_BasePlayer::SetObserverTarget( EHANDLE hObserverTarget )
+{
+ // If the observer target is changing to an entity that the client doesn't know about yet,
+ // it can resolve to NULL. If the client didn't have an observer target before, then
+ // comparing EHANDLEs directly will see them as equal, since it uses Get(), and compares
+ // NULL to NULL. To combat this, we need to check against GetEntryIndex() and
+ // GetSerialNumber().
+ if ( hObserverTarget.GetEntryIndex() != m_hObserverTarget.GetEntryIndex() ||
+ hObserverTarget.GetSerialNumber() != m_hObserverTarget.GetSerialNumber())
+ {
+ // Init based on the new handle's entry index and serial number, so that it's Get()
+ // has a chance to become non-NULL even if it currently resolves to NULL.
+ m_hObserverTarget.Init( hObserverTarget.GetEntryIndex(), hObserverTarget.GetSerialNumber() );
+
+ IGameEvent *event = gameeventmanager->CreateEvent( "spec_target_updated" );
+ if ( event )
+ {
+ gameeventmanager->FireEventClientSide( event );
+ }
+
+ if ( IsLocalPlayer() )
+ {
+ ResetToneMapping(1.0);
+ }
+ // NVNT notify haptics of changed player
+ if ( haptics )
+ haptics->OnPlayerChanged();
+
+ if ( IsLocalPlayer() )
+ {
+ // On a change of viewing mode or target, we may want to reset both head and torso to point at the new target.
+ g_ClientVirtualReality.AlignTorsoAndViewToWeapon();
+ }
+ }
+}
+
+
+void C_BasePlayer::SetObserverMode ( int iNewMode )
+{
+ if ( m_iObserverMode != iNewMode )
+ {
+ m_iObserverMode = iNewMode;
+ if ( IsLocalPlayer() )
+ {
+ // On a change of viewing mode or target, we may want to reset both head and torso to point at the new target.
+ g_ClientVirtualReality.AlignTorsoAndViewToWeapon();
+ }
+ }
+}
+
+
+int C_BasePlayer::GetObserverMode() const
+{
+#ifndef _XBOX
+ if ( IsHLTV() )
+ {
+ return HLTVCamera()->GetMode();
+ }
+#if defined( REPLAY_ENABLED )
+ if ( IsReplay() )
+ {
+ return ReplayCamera()->GetMode();
+ }
+#endif
+#endif
+
+ if ( IsLocalPlayer() && UseVR() )
+ {
+ // IN VR mode, certain views cause disorientation and nausea. So let's not.
+ switch ( m_iObserverMode )
+ {
+ case OBS_MODE_NONE: // not in spectator mode
+ case OBS_MODE_FIXED: // view from a fixed camera position
+ case OBS_MODE_IN_EYE: // follow a player in first person view
+ case OBS_MODE_CHASE: // follow a player in third person view
+ case OBS_MODE_ROAMING: // free roaming
+ return m_iObserverMode;
+ break;
+ case OBS_MODE_DEATHCAM: // special mode for death cam animation
+ case OBS_MODE_FREEZECAM: // zooms to a target, and freeze-frames on them
+ // These are both terrible - just do chase of your ragdoll.
+ return OBS_MODE_CHASE;
+ break;
+ default:
+ assert ( false );
+ break;
+ }
+ }
+
+ return m_iObserverMode;
+}
+
+bool C_BasePlayer::ViewModel_IsTransparent( void )
+{
+ return IsTransparent();
+}
+
+bool C_BasePlayer::ViewModel_IsUsingFBTexture( void )
+{
+ return UsesPowerOfTwoFrameBufferTexture();
+}
+
+//-----------------------------------------------------------------------------
+// Used by prediction, sets the view angles for the player
+//-----------------------------------------------------------------------------
+void C_BasePlayer::SetLocalViewAngles( const QAngle &viewAngles )
+{
+ pl.v_angle = viewAngles;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : ang -
+//-----------------------------------------------------------------------------
+void C_BasePlayer::SetViewAngles( const QAngle& ang )
+{
+ SetLocalAngles( ang );
+ SetNetworkAngles( ang );
+}
+
+
+surfacedata_t* C_BasePlayer::GetGroundSurface()
+{
+ //
+ // Find the name of the material that lies beneath the player.
+ //
+ Vector start, end;
+ VectorCopy( GetAbsOrigin(), start );
+ VectorCopy( start, end );
+
+ // Straight down
+ end.z -= 64;
+
+ // Fill in default values, just in case.
+
+ Ray_t ray;
+ ray.Init( start, end, GetPlayerMins(), GetPlayerMaxs() );
+
+ trace_t trace;
+ UTIL_TraceRay( ray, MASK_PLAYERSOLID_BRUSHONLY, this, COLLISION_GROUP_PLAYER_MOVEMENT, &trace );
+
+ if ( trace.fraction == 1.0f )
+ return NULL; // no ground
+
+ return physprops->GetSurfaceData( trace.surface.surfaceProps );
+}
+
+
+//-----------------------------------------------------------------------------
+// returns the player name
+//-----------------------------------------------------------------------------
+const char * C_BasePlayer::GetPlayerName()
+{
+ return g_PR ? g_PR->GetPlayerName( entindex() ) : "";
+}
+
+//-----------------------------------------------------------------------------
+// Is the player dead?
+//-----------------------------------------------------------------------------
+bool C_BasePlayer::IsPlayerDead()
+{
+ return pl.deadflag == true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_BasePlayer::SetVehicleRole( int nRole )
+{
+ if ( !IsInAVehicle() )
+ return;
+
+ // HL2 has only a player in a vehicle.
+ if ( nRole > VEHICLE_ROLE_DRIVER )
+ return;
+
+ char szCmd[64];
+ Q_snprintf( szCmd, sizeof( szCmd ), "vehicleRole %i\n", nRole );
+ engine->ServerCmd( szCmd );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Store original ammo data to see what has changed
+// Input : bnewentity -
+//-----------------------------------------------------------------------------
+void C_BasePlayer::OnPreDataChanged( DataUpdateType_t updateType )
+{
+ for (int i = 0; i < MAX_AMMO_TYPES; ++i)
+ {
+ m_iOldAmmo[i] = GetAmmoCount(i);
+ }
+
+ m_bWasFreezeFraming = (GetObserverMode() == OBS_MODE_FREEZECAM);
+ m_hOldFogController = m_Local.m_PlayerFog.m_hCtrl;
+
+ BaseClass::OnPreDataChanged( updateType );
+}
+
+void C_BasePlayer::PreDataUpdate( DataUpdateType_t updateType )
+{
+ BaseClass::PreDataUpdate( updateType );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : updateType -
+//-----------------------------------------------------------------------------
+void C_BasePlayer::PostDataUpdate( DataUpdateType_t updateType )
+{
+ // This has to occur here as opposed to OnDataChanged so that EHandles to the player created
+ // on this same frame are not stomped because prediction thinks there
+ // isn't a local player yet!!!
+
+ if ( updateType == DATA_UPDATE_CREATED )
+ {
+ // Make sure s_pLocalPlayer is correct
+
+ int iLocalPlayerIndex = engine->GetLocalPlayer();
+
+ if ( g_nKillCamMode )
+ iLocalPlayerIndex = g_nKillCamTarget1;
+
+ if ( iLocalPlayerIndex == index )
+ {
+ Assert( s_pLocalPlayer == NULL );
+ s_pLocalPlayer = this;
+
+ // Reset our sound mixed in case we were in a freeze cam when we
+ // changed level, which would cause the snd_soundmixer to be left modified.
+ ConVar *pVar = (ConVar *)cvar->FindVar( "snd_soundmixer" );
+ pVar->Revert();
+ }
+ }
+
+ bool bForceEFNoInterp = IsNoInterpolationFrame();
+
+ if ( IsLocalPlayer() )
+ {
+ SetSimulatedEveryTick( true );
+ }
+ else
+ {
+ SetSimulatedEveryTick( false );
+
+ // estimate velocity for non local players
+ float flTimeDelta = m_flSimulationTime - m_flOldSimulationTime;
+ if ( flTimeDelta > 0 && !( IsNoInterpolationFrame() || bForceEFNoInterp ) )
+ {
+ Vector newVelo = (GetNetworkOrigin() - GetOldOrigin() ) / flTimeDelta;
+ SetAbsVelocity( newVelo);
+ }
+ }
+
+ BaseClass::PostDataUpdate( updateType );
+
+ // Only care about this for local player
+ if ( IsLocalPlayer() )
+ {
+ QAngle angles;
+ engine->GetViewAngles( angles );
+ if ( updateType == DATA_UPDATE_CREATED )
+ {
+ SetLocalViewAngles( angles );
+ m_flOldPlayerZ = GetLocalOrigin().z;
+ // NVNT the local player has just been created.
+ // set in the "on_foot" navigation.
+ if ( haptics )
+ {
+ haptics->LocalPlayerReset();
+ haptics->SetNavigationClass("on_foot");
+ haptics->ProcessHapticEvent(2,"Movement","BasePlayer");
+ }
+
+ }
+ SetLocalAngles( angles );
+
+ if ( !m_bWasFreezeFraming && GetObserverMode() == OBS_MODE_FREEZECAM )
+ {
+ m_vecFreezeFrameStart = MainViewOrigin();
+ m_flFreezeFrameStartTime = gpGlobals->curtime;
+ m_flFreezeFrameDistance = RandomFloat( spec_freeze_distance_min.GetFloat(), spec_freeze_distance_max.GetFloat() );
+ m_flFreezeZOffset = RandomFloat( -30, 20 );
+ m_bSentFreezeFrame = false;
+ m_nForceVisionFilterFlags = 0;
+
+ C_BaseEntity *target = GetObserverTarget();
+ if ( target && target->IsPlayer() )
+ {
+ C_BasePlayer *player = ToBasePlayer( target );
+ if ( player )
+ {
+ m_nForceVisionFilterFlags = player->GetVisionFilterFlags();
+ CalculateVisionUsingCurrentFlags();
+ }
+ }
+
+ IGameEvent *pEvent = gameeventmanager->CreateEvent( "show_freezepanel" );
+ if ( pEvent )
+ {
+ pEvent->SetInt( "killer", target ? target->entindex() : 0 );
+ gameeventmanager->FireEventClientSide( pEvent );
+ }
+
+ // Force the sound mixer to the freezecam mixer
+ ConVar *pVar = (ConVar *)cvar->FindVar( "snd_soundmixer" );
+ pVar->SetValue( "FreezeCam_Only" );
+ }
+ else if ( m_bWasFreezeFraming && GetObserverMode() != OBS_MODE_FREEZECAM )
+ {
+ IGameEvent *pEvent = gameeventmanager->CreateEvent( "hide_freezepanel" );
+ if ( pEvent )
+ {
+ gameeventmanager->FireEventClientSide( pEvent );
+ }
+
+ view->FreezeFrame(0);
+
+ ConVar *pVar = (ConVar *)cvar->FindVar( "snd_soundmixer" );
+ pVar->Revert();
+
+ m_nForceVisionFilterFlags = 0;
+ CalculateVisionUsingCurrentFlags();
+ }
+ }
+
+ // If we are updated while paused, allow the player origin to be snapped by the
+ // server if we receive a packet from the server
+ if ( engine->IsPaused() || bForceEFNoInterp )
+ {
+ ResetLatched();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool C_BasePlayer::CanSetSoundMixer( void )
+{
+ // Can't set sound mixers when we're in freezecam mode, since it has a code-enforced mixer
+ return (GetObserverMode() != OBS_MODE_FREEZECAM);
+}
+
+void C_BasePlayer::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 PLAY_PLAYER_JINGLE:
+ PlayPlayerJingle();
+ break;
+ }
+}
+
+void C_BasePlayer::OnRestore()
+{
+ BaseClass::OnRestore();
+
+ if ( IsLocalPlayer() )
+ {
+ // debounce the attack key, for if it was used for restore
+ input->ClearInputButton( IN_ATTACK | IN_ATTACK2 );
+ // GetButtonBits() has to be called for the above to take effect
+ input->GetButtonBits( 0 );
+ }
+
+ // For ammo history icons to current value so they don't flash on level transtions
+ for ( int i = 0; i < MAX_AMMO_TYPES; i++ )
+ {
+ m_iOldAmmo[i] = GetAmmoCount(i);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Process incoming data
+//-----------------------------------------------------------------------------
+void C_BasePlayer::OnDataChanged( DataUpdateType_t updateType )
+{
+#if !defined( NO_ENTITY_PREDICTION )
+ if ( IsLocalPlayer() )
+ {
+ SetPredictionEligible( true );
+ }
+#endif
+
+ BaseClass::OnDataChanged( updateType );
+
+ // Only care about this for local player
+ if ( IsLocalPlayer() )
+ {
+ // Reset engine areabits pointer
+ render->SetAreaState( m_Local.m_chAreaBits, m_Local.m_chAreaPortalBits );
+
+ // Check for Ammo pickups.
+ for ( int i = 0; i < MAX_AMMO_TYPES; i++ )
+ {
+ if ( GetAmmoCount(i) > m_iOldAmmo[i] )
+ {
+ // Don't add to ammo pickup if the ammo doesn't do it
+ const FileWeaponInfo_t *pWeaponData = gWR.GetWeaponFromAmmo(i);
+
+ if ( !pWeaponData || !( pWeaponData->iFlags & ITEM_FLAG_NOAMMOPICKUPS ) )
+ {
+ // We got more ammo for this ammo index. Add it to the ammo history
+ CHudHistoryResource *pHudHR = GET_HUDELEMENT( CHudHistoryResource );
+ if( pHudHR )
+ {
+ pHudHR->AddToHistory( HISTSLOT_AMMO, i, abs(GetAmmoCount(i) - m_iOldAmmo[i]) );
+ }
+ }
+ }
+ }
+
+ Soundscape_Update( m_Local.m_audio );
+
+ if ( m_hOldFogController != m_Local.m_PlayerFog.m_hCtrl )
+ {
+ FogControllerChanged( updateType == DATA_UPDATE_CREATED );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Did we just enter a vehicle this frame?
+//-----------------------------------------------------------------------------
+bool C_BasePlayer::JustEnteredVehicle()
+{
+ if ( !IsInAVehicle() )
+ return false;
+
+ return ( m_hOldVehicle == m_hVehicle );
+}
+
+//-----------------------------------------------------------------------------
+// Are we in VGUI input mode?.
+//-----------------------------------------------------------------------------
+bool C_BasePlayer::IsInVGuiInputMode() const
+{
+ return (m_pCurrentVguiScreen.Get() != NULL);
+}
+
+//-----------------------------------------------------------------------------
+// Are we inputing to a view model vgui screen
+//-----------------------------------------------------------------------------
+bool C_BasePlayer::IsInViewModelVGuiInputMode() const
+{
+ C_BaseEntity *pScreenEnt = m_pCurrentVguiScreen.Get();
+
+ if ( !pScreenEnt )
+ return false;
+
+ Assert( dynamic_cast<C_VGuiScreen*>(pScreenEnt) );
+ C_VGuiScreen *pVguiScreen = static_cast<C_VGuiScreen*>(pScreenEnt);
+
+ return ( pVguiScreen->IsAttachedToViewModel() && pVguiScreen->AcceptsInput() );
+}
+
+//-----------------------------------------------------------------------------
+// Check to see if we're in vgui input mode...
+//-----------------------------------------------------------------------------
+void C_BasePlayer::DetermineVguiInputMode( CUserCmd *pCmd )
+{
+ // If we're dead, close down and abort!
+ if ( !IsAlive() )
+ {
+ DeactivateVguiScreen( m_pCurrentVguiScreen.Get() );
+ m_pCurrentVguiScreen.Set( NULL );
+ return;
+ }
+
+ // If we're in vgui mode *and* we're holding down mouse buttons,
+ // stay in vgui mode even if we're outside the screen bounds
+ if (m_pCurrentVguiScreen.Get() && (pCmd->buttons & (IN_ATTACK | IN_ATTACK2)) )
+ {
+ SetVGuiScreenButtonState( m_pCurrentVguiScreen.Get(), pCmd->buttons );
+
+ // Kill all attack inputs if we're in vgui screen mode
+ pCmd->buttons &= ~(IN_ATTACK | IN_ATTACK2);
+ return;
+ }
+
+ // We're not in vgui input mode if we're moving, or have hit a key
+ // that will make us move...
+
+ // Not in vgui mode if we're moving too quickly
+ // ROBIN: Disabled movement preventing VGUI screen usage
+ //if (GetVelocity().LengthSqr() > MAX_VGUI_INPUT_MODE_SPEED_SQ)
+ if ( 0 )
+ {
+ DeactivateVguiScreen( m_pCurrentVguiScreen.Get() );
+ m_pCurrentVguiScreen.Set( NULL );
+ return;
+ }
+
+ // Don't enter vgui mode if we've got combat buttons held down
+ bool bAttacking = false;
+ if ( ((pCmd->buttons & IN_ATTACK) || (pCmd->buttons & IN_ATTACK2)) && !m_pCurrentVguiScreen.Get() )
+ {
+ bAttacking = true;
+ }
+
+ // Not in vgui mode if we're pushing any movement key at all
+ // Not in vgui mode if we're in a vehicle...
+ // ROBIN: Disabled movement preventing VGUI screen usage
+ //if ((pCmd->forwardmove > MAX_VGUI_INPUT_MODE_SPEED) ||
+ // (pCmd->sidemove > MAX_VGUI_INPUT_MODE_SPEED) ||
+ // (pCmd->upmove > MAX_VGUI_INPUT_MODE_SPEED) ||
+ // (pCmd->buttons & IN_JUMP) ||
+ // (bAttacking) )
+ if ( bAttacking || IsInAVehicle() )
+ {
+ DeactivateVguiScreen( m_pCurrentVguiScreen.Get() );
+ m_pCurrentVguiScreen.Set( NULL );
+ return;
+ }
+
+ // Don't interact with world screens when we're in a menu
+ if ( vgui::surface()->IsCursorVisible() )
+ {
+ DeactivateVguiScreen( m_pCurrentVguiScreen.Get() );
+ m_pCurrentVguiScreen.Set( NULL );
+ return;
+ }
+
+ // Not in vgui mode if there are no nearby screens
+ C_BaseEntity *pOldScreen = m_pCurrentVguiScreen.Get();
+
+ m_pCurrentVguiScreen = FindNearbyVguiScreen( EyePosition(), pCmd->viewangles, GetTeamNumber() );
+
+ if (pOldScreen != m_pCurrentVguiScreen)
+ {
+ DeactivateVguiScreen( pOldScreen );
+ ActivateVguiScreen( m_pCurrentVguiScreen.Get() );
+ }
+
+ if (m_pCurrentVguiScreen.Get())
+ {
+ SetVGuiScreenButtonState( m_pCurrentVguiScreen.Get(), pCmd->buttons );
+
+ // Kill all attack inputs if we're in vgui screen mode
+ pCmd->buttons &= ~(IN_ATTACK | IN_ATTACK2);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Input handling
+//-----------------------------------------------------------------------------
+bool C_BasePlayer::CreateMove( float flInputSampleTime, CUserCmd *pCmd )
+{
+ // Allow the vehicle to clamp the view angles
+ if ( IsInAVehicle() )
+ {
+ IClientVehicle *pVehicle = m_hVehicle.Get()->GetClientVehicle();
+ if ( pVehicle )
+ {
+ pVehicle->UpdateViewAngles( this, pCmd );
+ engine->SetViewAngles( pCmd->viewangles );
+ }
+ }
+ else
+ {
+#ifndef _X360
+ if ( joy_autosprint.GetBool() )
+#endif
+ {
+ if ( input->KeyState( &in_joyspeed ) != 0.0f )
+ {
+ pCmd->buttons |= IN_SPEED;
+ }
+ }
+
+ CBaseCombatWeapon *pWeapon = GetActiveWeapon();
+ if ( pWeapon )
+ {
+ pWeapon->CreateMove( flInputSampleTime, pCmd, m_vecOldViewAngles );
+ }
+ }
+
+ // If the frozen flag is set, prevent view movement (server prevents the rest of the movement)
+ if ( GetFlags() & FL_FROZEN )
+ {
+ // Don't stomp the first time we get frozen
+ if ( m_bWasFrozen )
+ {
+ // Stomp the new viewangles with old ones
+ pCmd->viewangles = m_vecOldViewAngles;
+ engine->SetViewAngles( pCmd->viewangles );
+ }
+ else
+ {
+ m_bWasFrozen = true;
+ }
+ }
+ else
+ {
+ m_bWasFrozen = false;
+ }
+
+ m_vecOldViewAngles = pCmd->viewangles;
+
+ // Check to see if we're in vgui input mode...
+ DetermineVguiInputMode( pCmd );
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Player has changed to a new team
+//-----------------------------------------------------------------------------
+void C_BasePlayer::TeamChange( int iNewTeam )
+{
+ // Base class does nothing
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Creates, destroys, and updates the flashlight effect as needed.
+//-----------------------------------------------------------------------------
+void C_BasePlayer::UpdateFlashlight()
+{
+ // The dim light is the flashlight.
+ if ( IsEffectActive( EF_DIMLIGHT ) )
+ {
+ if (!m_pFlashlight)
+ {
+ // Turned on the headlight; create it.
+ m_pFlashlight = new CFlashlightEffect(index);
+
+ if (!m_pFlashlight)
+ return;
+
+ m_pFlashlight->TurnOn();
+ }
+
+ Vector vecForward, vecRight, vecUp;
+ EyeVectors( &vecForward, &vecRight, &vecUp );
+
+ // Update the light with the new position and direction.
+ m_pFlashlight->UpdateLight( EyePosition(), vecForward, vecRight, vecUp, FLASHLIGHT_DISTANCE );
+ }
+ else if (m_pFlashlight)
+ {
+ // Turned off the flashlight; delete it.
+ delete m_pFlashlight;
+ m_pFlashlight = NULL;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Creates player flashlight if it's ative
+//-----------------------------------------------------------------------------
+void C_BasePlayer::Flashlight( void )
+{
+ UpdateFlashlight();
+
+ // Check for muzzle flash and apply to view model
+ C_BaseAnimating *ve = this;
+ if ( GetObserverMode() == OBS_MODE_IN_EYE )
+ {
+ ve = dynamic_cast< C_BaseAnimating* >( GetObserverTarget() );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Engine is asking whether to add this player to the visible entities list
+//-----------------------------------------------------------------------------
+void C_BasePlayer::AddEntity( void )
+{
+ // FIXME/UNDONE: Should the local player say yes to adding itself now
+ // and then, when it ges time to render and it shouldn't still do the render with
+ // STUDIO_EVENTS set so that its attachment points will get updated even if not
+ // in third person?
+
+ // Add in water effects
+ if ( IsLocalPlayer() )
+ {
+ CreateWaterEffects();
+ }
+
+ // If set to invisible, skip. Do this before resetting the entity pointer so it has
+ // valid data to decide whether it's visible.
+ if ( !IsVisible() || !g_pClientMode->ShouldDrawLocalPlayer( this ) )
+ {
+ return;
+ }
+
+ // Server says don't interpolate this frame, so set previous info to new info.
+ if ( IsNoInterpolationFrame() || Teleported() )
+ {
+ ResetLatched();
+ }
+
+ // Add in lighting effects
+ CreateLightEffects();
+}
+
+extern float UTIL_WaterLevel( const Vector &position, float minz, float maxz );
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_BasePlayer::CreateWaterEffects( void )
+{
+ // Must be completely submerged to bother
+ if ( GetWaterLevel() < 3 )
+ {
+ m_bResampleWaterSurface = true;
+ return;
+ }
+
+ // Do special setup if this is our first time back underwater
+ if ( m_bResampleWaterSurface )
+ {
+ // Reset our particle timer
+ m_tWaterParticleTimer.Init( 32 );
+
+ // Find the surface of the water to clip against
+ m_flWaterSurfaceZ = UTIL_WaterLevel( WorldSpaceCenter(), WorldSpaceCenter().z, WorldSpaceCenter().z + 256 );
+ m_bResampleWaterSurface = false;
+ }
+
+ // Make sure the emitter is setup
+ if ( m_pWaterEmitter == NULL )
+ {
+ if ( ( m_pWaterEmitter = WaterDebrisEffect::Create( "splish" ) ) == NULL )
+ return;
+ }
+
+ Vector vecVelocity;
+ GetVectors( &vecVelocity, NULL, NULL );
+
+ Vector offset = WorldSpaceCenter();
+
+ m_pWaterEmitter->SetSortOrigin( offset );
+
+ SimpleParticle *pParticle;
+
+ float curTime = gpGlobals->frametime;
+
+ // Add as many particles as we need
+ while ( m_tWaterParticleTimer.NextEvent( curTime ) )
+ {
+ offset = WorldSpaceCenter() + ( vecVelocity * 128.0f ) + RandomVector( -128, 128 );
+
+ // Make sure we don't start out of the water!
+ if ( offset.z > m_flWaterSurfaceZ )
+ {
+ offset.z = ( m_flWaterSurfaceZ - 8.0f );
+ }
+
+ pParticle = (SimpleParticle *) m_pWaterEmitter->AddParticle( sizeof(SimpleParticle), g_Mat_Fleck_Cement[random->RandomInt(0,1)], offset );
+
+ if (pParticle == NULL)
+ continue;
+
+ pParticle->m_flLifetime = 0.0f;
+ pParticle->m_flDieTime = random->RandomFloat( 2.0f, 4.0f );
+
+ pParticle->m_vecVelocity = RandomVector( -2.0f, 2.0f );
+
+ //FIXME: We should tint these based on the water's fog value!
+ float color = random->RandomInt( 32, 128 );
+ pParticle->m_uchColor[0] = color;
+ pParticle->m_uchColor[1] = color;
+ pParticle->m_uchColor[2] = color;
+
+ pParticle->m_uchStartSize = 1;
+ pParticle->m_uchEndSize = 1;
+
+ pParticle->m_uchStartAlpha = 255;
+ pParticle->m_uchEndAlpha = 0;
+
+ pParticle->m_flRoll = random->RandomInt( 0, 360 );
+ pParticle->m_flRollDelta = random->RandomFloat( -0.5f, 0.5f );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Called when not in tactical mode. Allows view to be overriden for things like driving a tank.
+//-----------------------------------------------------------------------------
+void C_BasePlayer::OverrideView( CViewSetup *pSetup )
+{
+}
+
+bool C_BasePlayer::ShouldInterpolate()
+{
+ // always interpolate myself
+ if ( IsLocalPlayer() )
+ return true;
+#ifndef _XBOX
+ // always interpolate entity if followed by HLTV
+ if ( HLTVCamera()->GetCameraMan() == this )
+ return true;
+#endif
+ return BaseClass::ShouldInterpolate();
+}
+
+
+bool C_BasePlayer::ShouldDraw()
+{
+ return ShouldDrawThisPlayer() && BaseClass::ShouldDraw();
+}
+
+int C_BasePlayer::DrawModel( int flags )
+{
+#ifndef PORTAL
+ // In Portal this check is already performed as part of
+ // C_Portal_Player::DrawModel()
+ if ( !ShouldDrawThisPlayer() )
+ {
+ return 0;
+ }
+#endif
+ return BaseClass::DrawModel( flags );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+Vector C_BasePlayer::GetChaseCamViewOffset( CBaseEntity *target )
+{
+ C_BasePlayer *player = ToBasePlayer( target );
+
+ if ( player )
+ {
+ if ( player->IsAlive() )
+ {
+ if ( player->GetFlags() & FL_DUCKING )
+ {
+ return VEC_DUCK_VIEW_SCALED( player );
+ }
+
+ return VEC_VIEW_SCALED( player );
+ }
+ else
+ {
+ // assume it's the players ragdoll
+ return VEC_DEAD_VIEWHEIGHT_SCALED( player );
+ }
+ }
+
+ // assume it's the players ragdoll
+ return VEC_DEAD_VIEWHEIGHT;
+}
+
+void C_BasePlayer::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;
+ };
+
+ // If our target isn't visible, we're at a camera point of some kind.
+ // Instead of letting the player rotate around an invisible point, treat
+ // the point as a fixed camera.
+ if ( !target->GetBaseAnimating() && !target->GetModel() )
+ {
+ CalcRoamingView( eyeOrigin, eyeAngles, fov );
+ return;
+ }
+
+ // QAngle tmpangles;
+
+ Vector forward, viewpoint;
+
+ // GetObserverCamOrigin() returns ragdoll pos if player is ragdolled
+ Vector origin = target->GetObserverCamOrigin();
+
+ VectorAdd( origin, GetChaseCamViewOffset( target ), origin );
+
+ QAngle viewangles;
+
+ if ( GetObserverMode() == OBS_MODE_IN_EYE )
+ {
+ viewangles = eyeAngles;
+ }
+ else if ( IsLocalPlayer() )
+ {
+ engine->GetViewAngles( viewangles );
+ if ( UseVR() )
+ {
+ // Don't let people play with the pitch - they drive it into the ground or into the air and
+ // it's distracting at best, nauseating at worst (e.g. when it clips through the ground plane).
+ viewangles[PITCH] = 20.0f;
+ }
+ }
+ else
+ {
+ viewangles = EyeAngles();
+ }
+
+ //=============================================================================
+ // HPE_BEGIN:
+ // [Forrest] Fix for (at least one potential case of) CSB-194. Spectating someone
+ // who is headshotted by a teammate and then switching to chase cam leaves
+ // you with a permanent roll to the camera that doesn't decay and is not reset
+ // even when switching to different players or at the start of the next round
+ // if you are still a spectator. (If you spawn as a player, the view is reset.
+ // if you switch spectator modes, the view is reset.)
+ //=============================================================================
+#ifdef CSTRIKE_DLL
+ // The chase camera adopts the yaw and pitch of the previous camera, but the camera
+ // should not roll.
+ viewangles.z = 0;
+#endif
+ //=============================================================================
+ // HPE_END
+ //=============================================================================
+
+ m_flObserverChaseDistance += gpGlobals->frametime*48.0f;
+
+ float flMinDistance = CHASE_CAM_DISTANCE_MIN;
+ float flMaxDistance = CHASE_CAM_DISTANCE_MAX;
+
+ if ( target && target->IsBaseTrain() )
+ {
+ // if this is a train, we want to be back a little further so we can see more of it
+ flMaxDistance *= 2.5f;
+ }
+
+ if ( target )
+ {
+ C_BaseAnimating *pTargetAnimating = target->GetBaseAnimating();
+ if ( pTargetAnimating )
+ {
+ float flScaleSquared = pTargetAnimating->GetModelScale() * pTargetAnimating->GetModelScale();
+ flMinDistance *= flScaleSquared;
+ flMaxDistance *= flScaleSquared;
+ m_flObserverChaseDistance = flMaxDistance;
+ }
+ }
+
+ if ( target && !target->IsPlayer() && target->IsNextBot() )
+ {
+ // if this is a boss, we want to be back a little further so we can see more of it
+ flMaxDistance *= 2.5f;
+ m_flObserverChaseDistance = flMaxDistance;
+ }
+
+ m_flObserverChaseDistance = clamp( m_flObserverChaseDistance, flMinDistance, flMaxDistance );
+
+ AngleVectors( viewangles, &forward );
+
+ VectorNormalize( forward );
+
+ VectorMA(origin, -m_flObserverChaseDistance, forward, viewpoint );
+
+ trace_t trace;
+ CTraceFilterNoNPCsOrPlayer filter( target, COLLISION_GROUP_NONE );
+ C_BaseEntity::PushEnableAbsRecomputations( false ); // HACK don't recompute positions while doing RayTrace
+ UTIL_TraceHull( origin, viewpoint, WALL_MIN, WALL_MAX, MASK_SOLID, &filter, &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();
+}
+
+void C_BasePlayer::CalcRoamingView(Vector& eyeOrigin, QAngle& eyeAngles, float& fov)
+{
+ C_BaseEntity *target = GetObserverTarget();
+
+ if ( !target )
+ {
+ target = this;
+ }
+
+ m_flObserverChaseDistance = 0.0;
+
+ eyeOrigin = target->EyePosition();
+ eyeAngles = target->EyeAngles();
+
+ if ( spec_track.GetInt() > 0 )
+ {
+ C_BaseEntity *target = ClientEntityList().GetBaseEntity( spec_track.GetInt() );
+
+ if ( target )
+ {
+ Vector v = target->GetAbsOrigin(); v.z += 54;
+ QAngle a; VectorAngles( v - eyeOrigin, a );
+
+ NormalizeAngles( a );
+ eyeAngles = a;
+ engine->SetViewAngles( a );
+ }
+ }
+
+ // Apply a smoothing offset to smooth out prediction errors.
+ Vector vSmoothOffset;
+ GetPredictionErrorSmoothingVector( vSmoothOffset );
+ eyeOrigin += vSmoothOffset;
+
+ fov = GetFOV();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Calculate the view for the player while he's in freeze frame observer mode
+//-----------------------------------------------------------------------------
+void C_BasePlayer::CalcFreezeCamView( Vector& eyeOrigin, QAngle& eyeAngles, float& fov )
+{
+ C_BaseEntity *pTarget = GetObserverTarget();
+ if ( !pTarget )
+ {
+ CalcDeathCamView( eyeOrigin, eyeAngles, fov );
+ return;
+ }
+
+ // Zoom towards our target
+ float flCurTime = (gpGlobals->curtime - m_flFreezeFrameStartTime);
+ float flBlendPerc = clamp( flCurTime / spec_freeze_traveltime.GetFloat(), 0.f, 1.f );
+ flBlendPerc = SimpleSpline( flBlendPerc );
+
+ Vector vecCamDesired = pTarget->GetObserverCamOrigin(); // Returns ragdoll origin if they're ragdolled
+ VectorAdd( vecCamDesired, GetChaseCamViewOffset( pTarget ), vecCamDesired );
+ Vector vecCamTarget = vecCamDesired;
+ if ( pTarget->IsAlive() )
+ {
+ // Look at their chest, not their head
+ Vector maxs = pTarget->GetBaseAnimating() ? VEC_HULL_MAX_SCALED( pTarget->GetBaseAnimating() ) : VEC_HULL_MAX;
+ vecCamTarget.z -= (maxs.z * 0.5);
+ }
+ else
+ {
+ 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_TraceHull( vecCamTarget, vecTargetPos, 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.
+ 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_TraceHull( vecCamTarget, vecTargetPos, WALL_MIN, WALL_MAX, 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() );
+ }
+}
+
+void C_BasePlayer::CalcInEyeCamView(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;
+ };
+
+ if ( !target->IsAlive() )
+ {
+ // if dead, show from 3rd person
+ CalcChaseCamView( eyeOrigin, eyeAngles, fov );
+ return;
+ }
+
+ fov = GetFOV(); // TODO use tragets FOV
+
+ m_flObserverChaseDistance = 0.0;
+
+ eyeAngles = target->EyeAngles();
+ eyeOrigin = target->GetAbsOrigin();
+
+ // Apply punch angle
+ VectorAdd( eyeAngles, GetPunchAngle(), eyeAngles );
+
+#if defined( REPLAY_ENABLED )
+ if( engine->IsHLTV() || g_pEngineClientReplay->IsPlayingReplayDemo() )
+#else
+ if( engine->IsHLTV() )
+#endif
+ {
+ C_BaseAnimating *pTargetAnimating = target->GetBaseAnimating();
+ if ( target->GetFlags() & FL_DUCKING )
+ {
+ eyeOrigin += pTargetAnimating ? VEC_DUCK_VIEW_SCALED( pTargetAnimating ) : VEC_DUCK_VIEW;
+ }
+ else
+ {
+ eyeOrigin += pTargetAnimating ? VEC_VIEW_SCALED( pTargetAnimating ) : VEC_VIEW;
+ }
+ }
+ else
+ {
+ Vector offset = GetViewOffset();
+#ifdef HL2MP
+ offset = target->GetViewOffset();
+#endif
+ eyeOrigin += offset; // hack hack
+ }
+
+ engine->SetViewAngles( eyeAngles );
+}
+
+float C_BasePlayer::GetDeathCamInterpolationTime()
+{
+ return DEATH_ANIMATION_TIME;
+}
+
+
+void C_BasePlayer::CalcDeathCamView(Vector& eyeOrigin, QAngle& eyeAngles, float& fov)
+{
+ CBaseEntity * pKiller = NULL;
+
+ if ( mp_forcecamera.GetInt() == OBS_ALLOW_ALL )
+ {
+ // if mp_forcecamera is off let user see killer or look around
+ pKiller = GetObserverTarget();
+ eyeAngles = EyeAngles();
+ }
+
+ float interpolation = ( gpGlobals->curtime - m_flDeathTime ) / GetDeathCamInterpolationTime();
+ interpolation = clamp( interpolation, 0.0f, 1.0f );
+
+ m_flObserverChaseDistance += gpGlobals->frametime*48.0f;
+ m_flObserverChaseDistance = clamp( m_flObserverChaseDistance, ( CHASE_CAM_DISTANCE_MIN * 2 ), CHASE_CAM_DISTANCE_MAX );
+
+ QAngle aForward = eyeAngles;
+ Vector origin = EyePosition();
+
+ // NOTE: This will create the ragdoll in CSS if m_hRagdoll is set, but m_pRagdoll is not yet presetn
+ IRagdoll *pRagdoll = GetRepresentativeRagdoll();
+ if ( pRagdoll )
+ {
+ origin = pRagdoll->GetRagdollOrigin();
+ origin.z += VEC_DEAD_VIEWHEIGHT_SCALED( this ).z;
+ }
+
+ if ( pKiller && pKiller->IsPlayer() && (pKiller != this) )
+ {
+ Vector vKiller = pKiller->EyePosition() - origin;
+ QAngle aKiller; VectorAngles( vKiller, 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();
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Return the weapon to have open the weapon selection on, based upon our currently active weapon
+// Base class just uses the weapon that's currently active.
+//-----------------------------------------------------------------------------
+C_BaseCombatWeapon *C_BasePlayer::GetActiveWeaponForSelection( void )
+{
+ return GetActiveWeapon();
+}
+
+C_BaseAnimating* C_BasePlayer::GetRenderedWeaponModel()
+{
+ // Attach to either their weapon model or their view model.
+ if ( ShouldDrawLocalPlayer() || !IsLocalPlayer() )
+ {
+ return GetActiveWeapon();
+ }
+ else
+ {
+ return GetViewModel();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Gets a pointer to the local player, if it exists yet.
+// Output : C_BasePlayer
+//-----------------------------------------------------------------------------
+C_BasePlayer *C_BasePlayer::GetLocalPlayer( void )
+{
+ return s_pLocalPlayer;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : bThirdperson -
+//-----------------------------------------------------------------------------
+void C_BasePlayer::ThirdPersonSwitch( bool bThirdperson )
+{
+ // We've switch from first to third, or vice versa.
+ UpdateVisibility();
+
+ // Update the visibility of anything bone attached to us.
+ if ( IsLocalPlayer() )
+ {
+ bool bShouldDrawLocalPlayer = ShouldDrawLocalPlayer();
+ for ( int i=0; i<GetNumBoneAttachments(); ++i )
+ {
+ C_BaseAnimating* pBoneAttachment = GetBoneAttachment( i );
+ if ( pBoneAttachment )
+ {
+ if ( bShouldDrawLocalPlayer )
+ {
+ pBoneAttachment->RemoveEffects( EF_NODRAW );
+ }
+ else
+ {
+ pBoneAttachment->AddEffects( EF_NODRAW );
+ }
+ }
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: single place to decide whether the camera is in the first-person position
+// NOTE - ShouldDrawLocalPlayer() can be true even if the camera is in the first-person position, e.g. in VR.
+//-----------------------------------------------------------------------------
+/*static*/ bool C_BasePlayer::LocalPlayerInFirstPersonView()
+{
+ C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer();
+ if ( pLocalPlayer == NULL )
+ {
+ return false;
+ }
+ int ObserverMode = pLocalPlayer->GetObserverMode();
+ if ( ( ObserverMode == OBS_MODE_NONE ) || ( ObserverMode == OBS_MODE_IN_EYE ) )
+ {
+ return !input->CAM_IsThirdPerson() && ( !ToolsEnabled() || !ToolFramework_IsThirdPersonCamera() );
+ }
+ else
+ {
+ // Not looking at the local player, e.g. in a replay in third person mode or freelook.
+ return false;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: single place to decide whether the local player should draw
+//-----------------------------------------------------------------------------
+/*static*/ bool C_BasePlayer::ShouldDrawLocalPlayer()
+{
+ if ( !UseVR() )
+ {
+ return !LocalPlayerInFirstPersonView() || cl_first_person_uses_world_model.GetBool();
+ }
+ else
+ {
+ static ConVarRef vr_first_person_uses_world_model( "vr_first_person_uses_world_model" );
+ return !LocalPlayerInFirstPersonView() || vr_first_person_uses_world_model.GetBool();
+ }
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Purpose: single place to decide whether the camera is in the first-person position
+// NOTE - ShouldDrawLocalPlayer() can be true even if the camera is in the first-person position, e.g. in VR.
+//-----------------------------------------------------------------------------
+bool C_BasePlayer::InFirstPersonView()
+{
+ if ( IsLocalPlayer() )
+ {
+ return LocalPlayerInFirstPersonView();
+ }
+ C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer();
+ if ( pLocalPlayer == NULL )
+ {
+ return false;
+ }
+ // If this is who we're observing in first person, it's counted as the "local" player.
+ if ( pLocalPlayer->GetObserverMode() == OBS_MODE_IN_EYE && pLocalPlayer->GetObserverTarget() == ToBasePlayer(this) )
+ {
+ return LocalPlayerInFirstPersonView();
+ }
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: single place to decide whether the player is being drawn with the standard model (i.e. not the viewmodel)
+// NOTE - ShouldDrawLocalPlayer() can be true even if the camera is in the first-person position, e.g. in VR.
+//-----------------------------------------------------------------------------
+bool C_BasePlayer::ShouldDrawThisPlayer()
+{
+ if ( !InFirstPersonView() )
+ {
+ return true;
+ }
+ if ( !UseVR() && cl_first_person_uses_world_model.GetBool() )
+ {
+ return true;
+ }
+ if ( UseVR() )
+ {
+ static ConVarRef vr_first_person_uses_world_model( "vr_first_person_uses_world_model" );
+ if ( vr_first_person_uses_world_model.GetBool() )
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool C_BasePlayer::IsLocalPlayer( void ) const
+{
+ return ( GetLocalPlayer() == this );
+}
+
+int C_BasePlayer::GetUserID( void )
+{
+ player_info_t pi;
+
+ if ( !engine->GetPlayerInfo( entindex(), &pi ) )
+ return -1;
+
+ return pi.userID;
+}
+
+
+// For weapon prediction
+void C_BasePlayer::SetAnimation( PLAYER_ANIM playerAnim )
+{
+ // FIXME
+}
+
+void C_BasePlayer::UpdateClientData( void )
+{
+ // Update all the items
+ for ( int i = 0; i < WeaponCount(); i++ )
+ {
+ if ( GetWeapon(i) ) // each item updates it's successors
+ GetWeapon(i)->UpdateClientData( this );
+ }
+}
+
+// Prediction stuff
+void C_BasePlayer::PreThink( void )
+{
+#if !defined( NO_ENTITY_PREDICTION )
+ ItemPreFrame();
+
+ UpdateClientData();
+
+ UpdateUnderwaterState();
+
+ // Update the player's fog data if necessary.
+ UpdateFogController();
+
+ if (m_lifeState >= LIFE_DYING)
+ return;
+
+ //
+ // If we're not on the ground, we're falling. Update our falling velocity.
+ //
+ if ( !( GetFlags() & FL_ONGROUND ) )
+ {
+ m_Local.m_flFallVelocity = -GetAbsVelocity().z;
+ }
+#endif
+}
+
+void C_BasePlayer::PostThink( void )
+{
+#if !defined( NO_ENTITY_PREDICTION )
+ MDLCACHE_CRITICAL_SECTION();
+
+ if ( IsAlive())
+ {
+ if ( !CommentaryModeShouldSwallowInput( this ) )
+ {
+ // do weapon stuff
+ ItemPostFrame();
+ }
+
+ if ( GetFlags() & FL_ONGROUND )
+ {
+ m_Local.m_flFallVelocity = 0;
+ }
+
+ // Don't allow bogus sequence on player
+ if ( GetSequence() == -1 )
+ {
+ SetSequence( 0 );
+ }
+
+ StudioFrameAdvance();
+ }
+
+ // Even if dead simulate entities
+ SimulatePlayerSimulatedEntities();
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: send various tool messages - viewoffset, and base class messages (flex and bones)
+//-----------------------------------------------------------------------------
+void C_BasePlayer::GetToolRecordingState( KeyValues *msg )
+{
+ if ( !ToolsEnabled() )
+ return;
+
+ VPROF_BUDGET( "C_BasePlayer::GetToolRecordingState", VPROF_BUDGETGROUP_TOOLS );
+
+ BaseClass::GetToolRecordingState( msg );
+
+ msg->SetInt( "baseplayer", 1 );
+ msg->SetInt( "localplayer", IsLocalPlayer() ? 1 : 0 );
+ msg->SetString( "playername", GetPlayerName() );
+
+ static CameraRecordingState_t state;
+ state.m_flFOV = GetFOV();
+
+ float flZNear = view->GetZNear();
+ float flZFar = view->GetZFar();
+ CalcView( state.m_vecEyePosition, state.m_vecEyeAngles, flZNear, flZFar, state.m_flFOV );
+ state.m_bThirdPerson = !engine->IsPaused() && ::input->CAM_IsThirdPerson();
+
+ // this is a straight copy from ClientModeShared::OverrideView,
+ // When that method is removed in favor of rolling it into CalcView,
+ // then this code can (should!) be removed
+ if ( state.m_bThirdPerson )
+ {
+ Vector cam_ofs = g_ThirdPersonManager.GetCameraOffsetAngles();
+
+ QAngle camAngles;
+ camAngles[ PITCH ] = cam_ofs[ PITCH ];
+ camAngles[ YAW ] = cam_ofs[ YAW ];
+ camAngles[ ROLL ] = 0;
+
+ Vector camForward, camRight, camUp;
+ AngleVectors( camAngles, &camForward, &camRight, &camUp );
+
+ VectorMA( state.m_vecEyePosition, -cam_ofs[ ROLL ], camForward, state.m_vecEyePosition );
+
+ // Override angles from third person camera
+ VectorCopy( camAngles, state.m_vecEyeAngles );
+ }
+
+ msg->SetPtr( "camera", &state );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Simulate the player for this frame
+//-----------------------------------------------------------------------------
+void C_BasePlayer::Simulate()
+{
+ //Frame updates
+ if ( this == C_BasePlayer::GetLocalPlayer() )
+ {
+ //Update the flashlight
+ Flashlight();
+
+ // Update the player's fog data if necessary.
+ UpdateFogController();
+ }
+ else
+ {
+ // update step sounds for all other players
+ Vector vel;
+ EstimateAbsVelocity( vel );
+ UpdateStepSound( GetGroundSurface(), GetAbsOrigin(), vel );
+ }
+
+ BaseClass::Simulate();
+ if ( IsNoInterpolationFrame() || Teleported() )
+ {
+ ResetLatched();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : CBaseViewModel
+// Consider using GetRenderedWeaponModel() instead - it will get the
+// viewmodel or the active weapon as appropriate.
+//-----------------------------------------------------------------------------
+C_BaseViewModel *C_BasePlayer::GetViewModel( int index /*= 0*/, bool bObserverOK )
+{
+ Assert( index >= 0 && index < MAX_VIEWMODELS );
+
+ C_BaseViewModel *vm = m_hViewModel[ index ];
+
+ if ( bObserverOK && GetObserverMode() == OBS_MODE_IN_EYE )
+ {
+ C_BasePlayer *target = ToBasePlayer( GetObserverTarget() );
+
+ // get the targets viewmodel unless the target is an observer itself
+ if ( target && target != this && !target->IsObserver() )
+ {
+ vm = target->GetViewModel( index );
+ }
+ }
+
+ return vm;
+}
+
+C_BaseCombatWeapon *C_BasePlayer::GetActiveWeapon( void ) const
+{
+ const C_BasePlayer *fromPlayer = this;
+
+ // if localplayer is in InEye spectator mode, return weapon on chased player
+ if ( (fromPlayer == GetLocalPlayer()) && ( GetObserverMode() == OBS_MODE_IN_EYE) )
+ {
+ C_BaseEntity *target = GetObserverTarget();
+
+ if ( target && target->IsPlayer() )
+ {
+ fromPlayer = ToBasePlayer( target );
+ }
+ }
+
+ return fromPlayer->C_BaseCombatCharacter::GetActiveWeapon();
+}
+
+//=========================================================
+// Autoaim
+// set crosshair position to point to enemey
+//=========================================================
+Vector C_BasePlayer::GetAutoaimVector( float flScale )
+{
+ // Never autoaim a predicted weapon (for now)
+ Vector forward;
+ AngleVectors( GetAbsAngles() + m_Local.m_vecPunchAngle, &forward );
+ return forward;
+}
+
+void C_BasePlayer::PlayPlayerJingle()
+{
+#ifndef _XBOX
+ // Find player sound for shooter
+ player_info_t info;
+ engine->GetPlayerInfo( entindex(), &info );
+
+ if ( !cl_customsounds.GetBool() )
+ return;
+
+ // Doesn't have a jingle sound
+ if ( !info.customFiles[1] )
+ return;
+
+ char soundhex[ 16 ];
+ Q_binarytohex( (byte *)&info.customFiles[1], sizeof( info.customFiles[1] ), soundhex, sizeof( soundhex ) );
+
+ // See if logo has been downloaded.
+ char fullsoundname[ 512 ];
+ Q_snprintf( fullsoundname, sizeof( fullsoundname ), "sound/temp/%s.wav", soundhex );
+
+ if ( !filesystem->FileExists( fullsoundname ) )
+ {
+ char custname[ 512 ];
+ Q_snprintf( custname, sizeof( custname ), "downloads/%s.dat", soundhex );
+ // it may have been downloaded but not copied under materials folder
+ if ( !filesystem->FileExists( custname ) )
+ return; // not downloaded yet
+
+ // copy from download folder to materials/temp folder
+ // this is done since material system can access only materials/*.vtf files
+
+ if ( !engine->CopyLocalFile( custname, fullsoundname) )
+ return;
+ }
+
+ Q_snprintf( fullsoundname, sizeof( fullsoundname ), "temp/%s.wav", soundhex );
+
+ CLocalPlayerFilter filter;
+
+ EmitSound_t ep;
+ ep.m_nChannel = CHAN_VOICE;
+ ep.m_pSoundName = fullsoundname;
+ ep.m_flVolume = VOL_NORM;
+ ep.m_SoundLevel = SNDLVL_NORM;
+
+ C_BaseEntity::EmitSound( filter, GetSoundSourceIndex(), ep );
+#endif
+}
+
+// Stuff for prediction
+void C_BasePlayer::SetSuitUpdate(const char *name, int fgroup, int iNoRepeat)
+{
+ // FIXME: Do something here?
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void C_BasePlayer::ResetAutoaim( void )
+{
+#if 0
+ if (m_vecAutoAim.x != 0 || m_vecAutoAim.y != 0)
+ {
+ m_vecAutoAim = QAngle( 0, 0, 0 );
+ engine->CrosshairAngle( edict(), 0, 0 );
+ }
+#endif
+ m_fOnTarget = false;
+}
+
+bool C_BasePlayer::ShouldPredict( void )
+{
+#if !defined( NO_ENTITY_PREDICTION )
+ // Do this before calling into baseclass so prediction data block gets allocated
+ if ( IsLocalPlayer() )
+ {
+ return true;
+ }
+#endif
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Special processing for player simulation
+// NOTE: Don't chain to BaseClass!!!!
+//-----------------------------------------------------------------------------
+void C_BasePlayer::PhysicsSimulate( void )
+{
+#if !defined( NO_ENTITY_PREDICTION )
+ VPROF( "C_BasePlayer::PhysicsSimulate" );
+ // If we've got a moveparent, we must simulate that first.
+ CBaseEntity *pMoveParent = GetMoveParent();
+ if (pMoveParent)
+ {
+ pMoveParent->PhysicsSimulate();
+ }
+
+ // Make sure not to simulate this guy twice per frame
+ if (m_nSimulationTick == gpGlobals->tickcount)
+ return;
+
+ m_nSimulationTick = gpGlobals->tickcount;
+
+ if ( !IsLocalPlayer() )
+ return;
+
+ C_CommandContext *ctx = GetCommandContext();
+ Assert( ctx );
+ Assert( ctx->needsprocessing );
+ if ( !ctx->needsprocessing )
+ return;
+
+ ctx->needsprocessing = false;
+
+ // Handle FL_FROZEN.
+ if(GetFlags() & FL_FROZEN)
+ {
+ ctx->cmd.forwardmove = 0;
+ ctx->cmd.sidemove = 0;
+ ctx->cmd.upmove = 0;
+ ctx->cmd.buttons = 0;
+ ctx->cmd.impulse = 0;
+ //VectorCopy ( pl.v_angle, ctx->cmd.viewangles );
+ }
+
+ // Run the next command
+ prediction->RunCommand(
+ this,
+ &ctx->cmd,
+ MoveHelper() );
+#endif
+}
+
+const QAngle& C_BasePlayer::GetPunchAngle()
+{
+ return m_Local.m_vecPunchAngle.Get();
+}
+
+
+void C_BasePlayer::SetPunchAngle( const QAngle &angle )
+{
+ m_Local.m_vecPunchAngle = angle;
+}
+
+
+float C_BasePlayer::GetWaterJumpTime() const
+{
+ return m_flWaterJumpTime;
+}
+
+void C_BasePlayer::SetWaterJumpTime( float flWaterJumpTime )
+{
+ m_flWaterJumpTime = flWaterJumpTime;
+}
+
+float C_BasePlayer::GetSwimSoundTime() const
+{
+ return m_flSwimSoundTime;
+}
+
+void C_BasePlayer::SetSwimSoundTime( float flSwimSoundTime )
+{
+ m_flSwimSoundTime = flSwimSoundTime;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Return true if this object can be +used by the player
+//-----------------------------------------------------------------------------
+bool C_BasePlayer::IsUseableEntity( CBaseEntity *pEntity, unsigned int requiredCaps )
+{
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : float
+//-----------------------------------------------------------------------------
+float C_BasePlayer::GetFOV( void )
+{
+ // Allow users to override the FOV during demo playback
+ bool bUseDemoOverrideFov = engine->IsPlayingDemo() && demo_fov_override.GetFloat() > 0.0f;
+#if defined( REPLAY_ENABLED )
+ bUseDemoOverrideFov = bUseDemoOverrideFov && !g_pEngineClientReplay->IsPlayingReplayDemo();
+#endif
+ if ( bUseDemoOverrideFov )
+ {
+ return clamp( demo_fov_override.GetFloat(), 10.0f, 90.0f );
+ }
+
+ if ( GetObserverMode() == OBS_MODE_IN_EYE )
+ {
+ C_BasePlayer *pTargetPlayer = dynamic_cast<C_BasePlayer*>( GetObserverTarget() );
+
+ // get fov from observer target. Not if target is observer itself
+ if ( pTargetPlayer && !pTargetPlayer->IsObserver() )
+ {
+ return pTargetPlayer->GetFOV();
+ }
+ }
+
+ // Allow our vehicle to override our FOV if it's currently at the default FOV.
+ float flDefaultFOV;
+ IClientVehicle *pVehicle = GetVehicle();
+ if ( pVehicle )
+ {
+ CacheVehicleView();
+ flDefaultFOV = ( m_flVehicleViewFOV == 0 ) ? GetDefaultFOV() : m_flVehicleViewFOV;
+ }
+ else
+ {
+ flDefaultFOV = GetDefaultFOV();
+ }
+
+ float fFOV = ( m_iFOV == 0 ) ? flDefaultFOV : m_iFOV;
+
+ // Don't do lerping during prediction. It's only necessary when actually rendering,
+ // and it'll cause problems due to prediction timing messiness.
+ if ( !prediction->InPrediction() )
+ {
+ // See if we need to lerp the values for local player
+ if ( IsLocalPlayer() && ( fFOV != m_iFOVStart ) && (m_Local.m_flFOVRate > 0.0f ) )
+ {
+ float deltaTime = (float)( gpGlobals->curtime - m_flFOVTime ) / m_Local.m_flFOVRate;
+
+#if !defined( NO_ENTITY_PREDICTION )
+ if ( GetPredictable() )
+ {
+ // m_flFOVTime was set to a predicted time in the future, because the FOV change was predicted.
+ deltaTime = (float)( GetFinalPredictedTime() - m_flFOVTime );
+ deltaTime += ( gpGlobals->interpolation_amount * TICK_INTERVAL );
+ deltaTime /= m_Local.m_flFOVRate;
+ }
+#endif
+
+ if ( deltaTime >= 1.0f )
+ {
+ //If we're past the zoom time, just take the new value and stop lerping
+ m_iFOVStart = fFOV;
+ }
+ else
+ {
+ fFOV = SimpleSplineRemapValClamped( deltaTime, 0.0f, 1.0f, (float) m_iFOVStart, fFOV );
+ }
+ }
+ }
+
+ return fFOV;
+}
+
+void RecvProxy_LocalVelocityX( const CRecvProxyData *pData, void *pStruct, void *pOut )
+{
+ C_BasePlayer *pPlayer = (C_BasePlayer *) pStruct;
+
+ Assert( pPlayer );
+
+ float flNewVel_x = pData->m_Value.m_Float;
+
+ Vector vecVelocity = pPlayer->GetLocalVelocity();
+
+ if( vecVelocity.x != flNewVel_x ) // Should this use an epsilon check?
+ {
+ vecVelocity.x = flNewVel_x;
+ pPlayer->SetLocalVelocity( vecVelocity );
+ }
+}
+
+void RecvProxy_LocalVelocityY( const CRecvProxyData *pData, void *pStruct, void *pOut )
+{
+ C_BasePlayer *pPlayer = (C_BasePlayer *) pStruct;
+
+ Assert( pPlayer );
+
+ float flNewVel_y = pData->m_Value.m_Float;
+
+ Vector vecVelocity = pPlayer->GetLocalVelocity();
+
+ if( vecVelocity.y != flNewVel_y )
+ {
+ vecVelocity.y = flNewVel_y;
+ pPlayer->SetLocalVelocity( vecVelocity );
+ }
+}
+
+void RecvProxy_LocalVelocityZ( const CRecvProxyData *pData, void *pStruct, void *pOut )
+{
+ C_BasePlayer *pPlayer = (C_BasePlayer *) pStruct;
+
+ Assert( pPlayer );
+
+ float flNewVel_z = pData->m_Value.m_Float;
+
+ Vector vecVelocity = pPlayer->GetLocalVelocity();
+
+ if( vecVelocity.z != flNewVel_z )
+ {
+ vecVelocity.z = flNewVel_z;
+ pPlayer->SetLocalVelocity( vecVelocity );
+ }
+}
+
+void RecvProxy_ObserverTarget( const CRecvProxyData *pData, void *pStruct, void *pOut )
+{
+ C_BasePlayer *pPlayer = (C_BasePlayer *) pStruct;
+
+ Assert( pPlayer );
+
+ EHANDLE hTarget;
+
+ RecvProxy_IntToEHandle( pData, pStruct, &hTarget );
+
+ pPlayer->SetObserverTarget( hTarget );
+}
+
+void RecvProxy_ObserverMode( const CRecvProxyData *pData, void *pStruct, void *pOut )
+{
+ C_BasePlayer *pPlayer = (C_BasePlayer *) pStruct;
+
+ Assert( pPlayer );
+
+ pPlayer->SetObserverMode ( pData->m_Value.m_Int );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Remove this player from a vehicle
+//-----------------------------------------------------------------------------
+void C_BasePlayer::LeaveVehicle( void )
+{
+ if ( NULL == m_hVehicle.Get() )
+ return;
+
+// Let server do this for now
+#if 0
+ IClientVehicle *pVehicle = GetVehicle();
+ Assert( pVehicle );
+
+ int nRole = pVehicle->GetPassengerRole( this );
+ Assert( nRole != VEHICLE_ROLE_NONE );
+
+ SetParent( NULL );
+
+ // Find the first non-blocked exit point:
+ Vector vNewPos = GetAbsOrigin();
+ QAngle qAngles = GetAbsAngles();
+ pVehicle->GetPassengerExitPoint( nRole, &vNewPos, &qAngles );
+ OnVehicleEnd( vNewPos );
+ SetAbsOrigin( vNewPos );
+ SetAbsAngles( qAngles );
+
+ m_Local.m_iHideHUD &= ~HIDEHUD_WEAPONSELECTION;
+ RemoveEffects( EF_NODRAW );
+
+ SetMoveType( MOVETYPE_WALK );
+ SetCollisionGroup( COLLISION_GROUP_PLAYER );
+
+ qAngles[ROLL] = 0;
+ SnapEyeAngles( qAngles );
+
+ m_hVehicle = NULL;
+ pVehicle->SetPassenger(nRole, NULL);
+
+ Weapon_Switch( m_hLastWeapon );
+#endif
+}
+
+
+float C_BasePlayer::GetMinFOV() const
+{
+ if ( gpGlobals->maxClients == 1 )
+ {
+ // Let them do whatever they want, more or less, in single player
+ return 5;
+ }
+ else
+ {
+ return 75;
+ }
+}
+
+float C_BasePlayer::GetFinalPredictedTime() const
+{
+ return ( m_nFinalPredictedTick * TICK_INTERVAL );
+}
+
+void C_BasePlayer::NotePredictionError( const Vector &vDelta )
+{
+ // don't worry about prediction errors when dead
+ if ( !IsAlive() )
+ return;
+
+#if !defined( NO_ENTITY_PREDICTION )
+ Vector vOldDelta;
+
+ GetPredictionErrorSmoothingVector( vOldDelta );
+
+ // sum all errors within smoothing time
+ m_vecPredictionError = vDelta + vOldDelta;
+
+ // remember when last error happened
+ m_flPredictionErrorTime = gpGlobals->curtime;
+
+ ResetLatched();
+#endif
+}
+
+
+// offset curtime and setup bones at that time using fake interpolation
+// fake interpolation means we don't have reliable interpolation history (the local player doesn't animate locally)
+// so we just modify cycle and origin directly and use that as a fake guess
+void C_BasePlayer::ForceSetupBonesAtTimeFakeInterpolation( matrix3x4_t *pBonesOut, float curtimeOffset )
+{
+ // we don't have any interpolation data, so fake it
+ float cycle = m_flCycle;
+ Vector origin = GetLocalOrigin();
+
+ // blow the cached prev bones
+ InvalidateBoneCache();
+ // reset root position to flTime
+ Interpolate( gpGlobals->curtime + curtimeOffset );
+
+ // force cycle back by boneDt
+ m_flCycle = fmod( 10 + cycle + m_flPlaybackRate * curtimeOffset, 1.0f );
+ SetLocalOrigin( origin + curtimeOffset * GetLocalVelocity() );
+ // Setup bone state to extrapolate physics velocity
+ SetupBones( pBonesOut, MAXSTUDIOBONES, BONE_USED_BY_ANYTHING, gpGlobals->curtime + curtimeOffset );
+
+ m_flCycle = cycle;
+ SetLocalOrigin( origin );
+}
+
+void C_BasePlayer::GetRagdollInitBoneArrays( matrix3x4_t *pDeltaBones0, matrix3x4_t *pDeltaBones1, matrix3x4_t *pCurrentBones, float boneDt )
+{
+ if ( !IsLocalPlayer() )
+ {
+ BaseClass::GetRagdollInitBoneArrays(pDeltaBones0, pDeltaBones1, pCurrentBones, boneDt);
+ return;
+ }
+ ForceSetupBonesAtTimeFakeInterpolation( pDeltaBones0, -boneDt );
+ ForceSetupBonesAtTimeFakeInterpolation( pDeltaBones1, 0 );
+ float ragdollCreateTime = PhysGetSyncCreateTime();
+ if ( ragdollCreateTime != gpGlobals->curtime )
+ {
+ ForceSetupBonesAtTimeFakeInterpolation( pCurrentBones, ragdollCreateTime - gpGlobals->curtime );
+ }
+ else
+ {
+ SetupBones( pCurrentBones, MAXSTUDIOBONES, BONE_USED_BY_ANYTHING, gpGlobals->curtime );
+ }
+}
+
+
+void C_BasePlayer::GetPredictionErrorSmoothingVector( Vector &vOffset )
+{
+#if !defined( NO_ENTITY_PREDICTION )
+ if ( engine->IsPlayingDemo() || !cl_smooth.GetInt() || !cl_predict->GetInt() || engine->IsPaused() )
+ {
+ vOffset.Init();
+ return;
+ }
+
+ float errorAmount = ( gpGlobals->curtime - m_flPredictionErrorTime ) / cl_smoothtime.GetFloat();
+
+ if ( errorAmount >= 1.0f )
+ {
+ vOffset.Init();
+ return;
+ }
+
+ errorAmount = 1.0f - errorAmount;
+
+ vOffset = m_vecPredictionError * errorAmount;
+#else
+ vOffset.Init();
+#endif
+}
+
+
+IRagdoll* C_BasePlayer::GetRepresentativeRagdoll() const
+{
+ return m_pRagdoll;
+}
+
+IMaterial *C_BasePlayer::GetHeadLabelMaterial( void )
+{
+ if ( GetClientVoiceMgr() == NULL )
+ return NULL;
+
+ return GetClientVoiceMgr()->GetHeadLabelMaterial();
+}
+
+bool IsInFreezeCam( void )
+{
+ C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer();
+ if ( pPlayer && pPlayer->GetObserverMode() == OBS_MODE_FREEZECAM )
+ return true;
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Set the fog controller data per player.
+// Input : &inputdata -
+//-----------------------------------------------------------------------------
+void C_BasePlayer::FogControllerChanged( bool bSnap )
+{
+ if ( m_Local.m_PlayerFog.m_hCtrl )
+ {
+ fogparams_t *pFogParams = &(m_Local.m_PlayerFog.m_hCtrl->m_fog);
+
+ /*
+ Msg("Updating Fog Target: (%d,%d,%d) %.0f,%.0f -> (%d,%d,%d) %.0f,%.0f (%.2f seconds)\n",
+ m_CurrentFog.colorPrimary.GetR(), m_CurrentFog.colorPrimary.GetB(), m_CurrentFog.colorPrimary.GetG(),
+ m_CurrentFog.start.Get(), m_CurrentFog.end.Get(),
+ pFogParams->colorPrimary.GetR(), pFogParams->colorPrimary.GetB(), pFogParams->colorPrimary.GetG(),
+ pFogParams->start.Get(), pFogParams->end.Get(), pFogParams->duration.Get() );*/
+
+
+ // Setup the fog color transition.
+ m_Local.m_PlayerFog.m_OldColor = m_CurrentFog.colorPrimary;
+ m_Local.m_PlayerFog.m_flOldStart = m_CurrentFog.start;
+ m_Local.m_PlayerFog.m_flOldEnd = m_CurrentFog.end;
+
+ m_Local.m_PlayerFog.m_NewColor = pFogParams->colorPrimary;
+ m_Local.m_PlayerFog.m_flNewStart = pFogParams->start;
+ m_Local.m_PlayerFog.m_flNewEnd = pFogParams->end;
+
+ m_Local.m_PlayerFog.m_flTransitionTime = bSnap ? -1 : gpGlobals->curtime;
+
+ m_CurrentFog = *pFogParams;
+
+ // Update the fog player's local fog data with the fog controller's data if need be.
+ UpdateFogController();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Check to see that the controllers data is up to date.
+//-----------------------------------------------------------------------------
+void C_BasePlayer::UpdateFogController( void )
+{
+ if ( m_Local.m_PlayerFog.m_hCtrl )
+ {
+ // Don't bother copying while we're transitioning, since it'll be stomped in UpdateFogBlend();
+ if ( m_Local.m_PlayerFog.m_flTransitionTime == -1 && (m_hOldFogController == m_Local.m_PlayerFog.m_hCtrl) )
+ {
+ fogparams_t *pFogParams = &(m_Local.m_PlayerFog.m_hCtrl->m_fog);
+ if ( m_CurrentFog != *pFogParams )
+ {
+ /*
+ Msg("FORCING UPDATE: (%d,%d,%d) %.0f,%.0f -> (%d,%d,%d) %.0f,%.0f (%.2f seconds)\n",
+ m_CurrentFog.colorPrimary.GetR(), m_CurrentFog.colorPrimary.GetB(), m_CurrentFog.colorPrimary.GetG(),
+ m_CurrentFog.start.Get(), m_CurrentFog.end.Get(),
+ pFogParams->colorPrimary.GetR(), pFogParams->colorPrimary.GetB(), pFogParams->colorPrimary.GetG(),
+ pFogParams->start.Get(), pFogParams->end.Get(), pFogParams->duration.Get() );*/
+
+
+ m_CurrentFog = *pFogParams;
+ }
+ }
+ }
+ else
+ {
+ if ( m_CurrentFog.farz != -1 || m_CurrentFog.enable != false )
+ {
+ // No fog controller in this level. Use default fog parameters.
+ m_CurrentFog.farz = -1;
+ m_CurrentFog.enable = false;
+ }
+ }
+
+ // Update the fog blending state - of necessary.
+ UpdateFogBlend();
+}
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void C_BasePlayer::UpdateFogBlend( void )
+{
+ // Transition.
+ if ( m_Local.m_PlayerFog.m_flTransitionTime != -1 )
+ {
+ float flTimeDelta = gpGlobals->curtime - m_Local.m_PlayerFog.m_flTransitionTime;
+ if ( flTimeDelta < m_CurrentFog.duration )
+ {
+ float flScale = flTimeDelta / m_CurrentFog.duration;
+ m_CurrentFog.colorPrimary.SetR( ( m_Local.m_PlayerFog.m_NewColor.r * flScale ) + ( m_Local.m_PlayerFog.m_OldColor.r * ( 1.0f - flScale ) ) );
+ m_CurrentFog.colorPrimary.SetG( ( m_Local.m_PlayerFog.m_NewColor.g * flScale ) + ( m_Local.m_PlayerFog.m_OldColor.g * ( 1.0f - flScale ) ) );
+ m_CurrentFog.colorPrimary.SetB( ( m_Local.m_PlayerFog.m_NewColor.b * flScale ) + ( m_Local.m_PlayerFog.m_OldColor.b * ( 1.0f - flScale ) ) );
+ m_CurrentFog.start.Set( ( m_Local.m_PlayerFog.m_flNewStart * flScale ) + ( ( m_Local.m_PlayerFog.m_flOldStart * ( 1.0f - flScale ) ) ) );
+ m_CurrentFog.end.Set( ( m_Local.m_PlayerFog.m_flNewEnd * flScale ) + ( ( m_Local.m_PlayerFog.m_flOldEnd * ( 1.0f - flScale ) ) ) );
+ }
+ else
+ {
+ // Slam the final fog values.
+ m_CurrentFog.colorPrimary.SetR( m_Local.m_PlayerFog.m_NewColor.r );
+ m_CurrentFog.colorPrimary.SetG( m_Local.m_PlayerFog.m_NewColor.g );
+ m_CurrentFog.colorPrimary.SetB( m_Local.m_PlayerFog.m_NewColor.b );
+ m_CurrentFog.start.Set( m_Local.m_PlayerFog.m_flNewStart );
+ m_CurrentFog.end.Set( m_Local.m_PlayerFog.m_flNewEnd );
+ m_Local.m_PlayerFog.m_flTransitionTime = -1;
+
+ /*
+ Msg("Finished transition to (%d,%d,%d) %.0f,%.0f\n",
+ m_CurrentFog.colorPrimary.GetR(), m_CurrentFog.colorPrimary.GetB(), m_CurrentFog.colorPrimary.GetG(),
+ m_CurrentFog.start.Get(), m_CurrentFog.end.Get() );*/
+
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool C_BasePlayer::GetSteamID( CSteamID *pID )
+{
+ // try to make this a little more efficient
+
+ player_info_t pi;
+ if ( engine->GetPlayerInfo( entindex(), &pi ) )
+ {
+ if ( pi.friendsID && steamapicontext && steamapicontext->SteamUtils() )
+ {
+#if 1 // new
+ static EUniverse universe = k_EUniverseInvalid;
+
+ if ( universe == k_EUniverseInvalid )
+ universe = steamapicontext->SteamUtils()->GetConnectedUniverse();
+
+ pID->InstancedSet( pi.friendsID, 1, universe, k_EAccountTypeIndividual );
+#else // old
+ pID->InstancedSet( pi.friendsID, 1, steamapicontext->SteamUtils()->GetConnectedUniverse(), k_EAccountTypeIndividual );
+#endif
+
+ return true;
+ }
+ }
+ return false;
+}
+
+#if defined USES_ECON_ITEMS
+//-----------------------------------------------------------------------------
+// Purpose: Update the visibility of our worn items.
+//-----------------------------------------------------------------------------
+void C_BasePlayer::UpdateWearables( void )
+{
+ for ( int i=0; i<m_hMyWearables.Count(); ++i )
+ {
+ CEconWearable* pItem = m_hMyWearables[i];
+ if ( pItem )
+ {
+ pItem->ValidateModelIndex();
+ pItem->UpdateVisibility();
+ }
+ }
+}
+#endif // USES_ECON_ITEMS
+
+
+//-----------------------------------------------------------------------------
+// Purpose: In meathook mode, fix the bone transforms to hang the user's own
+// avatar under the camera.
+//-----------------------------------------------------------------------------
+void C_BasePlayer::BuildFirstPersonMeathookTransformations( CStudioHdr *hdr, Vector *pos, Quaternion q[], const matrix3x4_t& cameraTransform, int boneMask, CBoneBitList &boneComputed, const char *pchHeadBoneName )
+{
+ // Handle meathook mode. If we aren't rendering, just use last frame's transforms
+ if ( !InFirstPersonView() )
+ return;
+
+ // If we're in third-person view, don't do anything special.
+ // If we're in first-person view rendering the main view and using the viewmodel, we shouldn't have even got here!
+ // If we're in first-person view rendering the main view(s), meathook and headless.
+ // If we're in first-person view rendering shadowbuffers/reflections, don't do anything special either (we could do meathook but with a head?)
+ if ( IsAboutToRagdoll() )
+ {
+ // We're re-animating specifically to set up the ragdoll.
+ // Meathook can push the player through the floor, which makes the ragdoll fall through the world, which is no good.
+ // So do nothing.
+ return;
+ }
+
+ if ( !DrawingMainView() )
+ {
+ return;
+ }
+
+ // If we aren't drawing the player anyway, don't mess with the bones. This can happen in Portal.
+ if( !ShouldDrawThisPlayer() )
+ {
+ return;
+ }
+
+ m_BoneAccessor.SetWritableBones( BONE_USED_BY_ANYTHING );
+
+ matrix3x4_t &mHeadTransform = GetBoneForWrite( LookupBone( pchHeadBoneName ) );
+
+ // "up" on the head bone is along the negative Y axis - not sure why.
+ //Vector vHeadTransformUp ( -mHeadTransform[0][1], -mHeadTransform[1][1], -mHeadTransform[2][1] );
+ //Vector vHeadTransformFwd ( mHeadTransform[0][1], mHeadTransform[1][1], mHeadTransform[2][1] );
+ Vector vHeadTransformTranslation ( mHeadTransform[0][3], mHeadTransform[1][3], mHeadTransform[2][3] );
+
+
+ // Find out where the player's head (driven by the HMD) is in the world.
+ // We can't move this with animations or effects without causing nausea, so we need to move
+ // the whole body so that the animated head is in the right place to match the player-controlled head.
+ Vector vHeadUp;
+ bool bMeathookEnable = true;
+ Vector vRealPivotPoint;
+ bool bEnableDecapitation = true;
+ if( UseVR() )
+ {
+ static ConVarRef vr_neck_pivot_ingame_up( "vr_neck_pivot_ingame_up" );
+ static ConVarRef vr_neck_pivot_ingame_fwd( "vr_neck_pivot_ingame_fwd" );
+ static ConVarRef vr_meathook_enable ( "vr_meathook_enable" );
+ static ConVarRef vr_decapitation_enable ( "vr_decapitation_enable" );
+
+ VMatrix mWorldFromMideye = g_ClientVirtualReality.GetWorldFromMidEye();
+
+ bMeathookEnable = vr_meathook_enable.GetBool();
+ bEnableDecapitation = vr_decapitation_enable.GetBool();
+
+ // What we do here is:
+ // * Take the required eye pos+orn - the actual pose the player is controlling with the HMD.
+ // * Go downwards in that space by headtrack_neck_pivot_ingame_* - this is now the neck-pivot in the game world of where the player is actually looking.
+ // * Now place the body of the animated character so that the head bone is at that position.
+ // The head bone is the neck pivot point of the in-game character.
+
+ Vector vRealMidEyePos = mWorldFromMideye.GetTranslation();
+ vRealPivotPoint = vRealMidEyePos - ( mWorldFromMideye.GetUp() * vr_neck_pivot_ingame_up.GetFloat() ) - ( mWorldFromMideye.GetForward() * vr_neck_pivot_ingame_fwd.GetFloat() );
+ }
+ else
+ {
+ // figure out where to put the body from the aim angles
+ Vector vForward, vRight, vUp;
+ AngleVectors( MainViewAngles(), &vForward, &vRight, &vUp );
+
+ vRealPivotPoint = MainViewOrigin() - ( vUp * 7.3f ) - ( vForward * 3.f );
+ }
+
+ Vector vDeltaToAdd = vRealPivotPoint - vHeadTransformTranslation;
+
+
+ if ( bMeathookEnable )
+ {
+ // Now add this offset to the entire skeleton.
+ for (int i = 0; i < hdr->numbones(); i++)
+ {
+ // Only update bones reference by the bone mask.
+ if ( !( hdr->boneFlags( i ) & boneMask ) )
+ {
+ continue;
+ }
+ matrix3x4_t& bone = GetBoneForWrite( i );
+ Vector vBonePos;
+ MatrixGetTranslation ( bone, vBonePos );
+ vBonePos += vDeltaToAdd;
+ MatrixSetTranslation ( vBonePos, bone );
+ }
+ }
+
+ if ( bEnableDecapitation )
+ {
+ // Then scale the head to zero, but leave its position - forms a "neck stub".
+ // This prevents us rendering junk all over the screen, e.g. inside of mouth, etc.
+ MatrixScaleByZero ( mHeadTransform );
+
+ // TODO: right now we nuke the hats by shrinking them to nothing,
+ // but it feels like we should do something more sensible.
+ // For example, for one sniper taunt he takes his hat off and waves it - would be nice to see it then.
+ int iHelm = LookupBone( "prp_helmet" );
+ if ( iHelm != -1 )
+ {
+ // Scale the helmet.
+ matrix3x4_t &transformhelmet = GetBoneForWrite( iHelm );
+ MatrixScaleByZero ( transformhelmet );
+ }
+
+ iHelm = LookupBone( "prp_hat" );
+ if ( iHelm != -1 )
+ {
+ matrix3x4_t &transformhelmet = GetBoneForWrite( iHelm );
+ MatrixScaleByZero ( transformhelmet );
+ }
+ }
+
+}
+
+
+
+void CC_DumpClientSoundscapeData( const CCommand& args )
+{
+ C_BasePlayer *pPlayer = C_BasePlayer::GetLocalPlayer();
+ if ( !pPlayer )
+ return;
+
+ Msg("Client Soundscape data dump:\n");
+ Msg(" Position: %.2f %.2f %.2f\n", pPlayer->GetAbsOrigin().x, pPlayer->GetAbsOrigin().y, pPlayer->GetAbsOrigin().z );
+ Msg(" soundscape index: %d\n", pPlayer->m_Local.m_audio.soundscapeIndex.Get() );
+ Msg(" entity index: %d\n", pPlayer->m_Local.m_audio.ent.Get() ? pPlayer->m_Local.m_audio.ent->entindex() : -1 );
+ if ( pPlayer->m_Local.m_audio.ent.Get() )
+ {
+ Msg(" entity pos: %.2f %.2f %.2f\n", pPlayer->m_Local.m_audio.ent.Get()->GetAbsOrigin().x, pPlayer->m_Local.m_audio.ent.Get()->GetAbsOrigin().y, pPlayer->m_Local.m_audio.ent.Get()->GetAbsOrigin().z );
+ if ( pPlayer->m_Local.m_audio.ent.Get()->IsDormant() )
+ {
+ Msg(" ENTITY IS DORMANT\n");
+ }
+ }
+ bool bFoundOne = false;
+ for ( int i = 0; i < NUM_AUDIO_LOCAL_SOUNDS; i++ )
+ {
+ if ( pPlayer->m_Local.m_audio.localBits & (1<<i) )
+ {
+ if ( !bFoundOne )
+ {
+ Msg(" Sound Positions:\n");
+ bFoundOne = true;
+ }
+
+ Vector vecPos = pPlayer->m_Local.m_audio.localSound[i];
+ Msg(" %d: %.2f %.2f %.2f\n", i, vecPos.x,vecPos.y, vecPos.z );
+ }
+ }
+
+ Msg("End dump.\n");
+}
+static ConCommand soundscape_dumpclient("soundscape_dumpclient", CC_DumpClientSoundscapeData, "Dumps the client's soundscape data.\n", FCVAR_CHEAT);
|