text: //========= 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 "sourcevr/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." );
  
  // This only needs to be approximate - it just controls the distance to the pivot-point of the head ("the neck") of the in-game character, not the player's real-world neck length.
  // Ideally we would find this vector by subtracting the neutral-pose difference between the head bone (the pivot point) and the "eyes" attachment point.
  // However, some characters don't have this attachment point, and finding the neutral pose is a pain.
  // This value is found by hand, and a good value depends more on the in-game models than on actual human shapes.
  ConVar cl_meathook_neck_pivot_ingame_up( "cl_meathook_neck_pivot_ingame_up", "7.0" );
  ConVar cl_meathook_neck_pivot_ingame_fwd( "cl_meathook_neck_pivot_ingame_fwd", "3.0" );
  
  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;
  
  	ListenForGameEvent( "base_player_teleported" );
  }
  
  //-----------------------------------------------------------------------------
  // 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 );
  }
  
  void C_BasePlayer::FireGameEvent( IGameEvent *event )
  {
  	if ( FStrEq( event->GetName(), "base_player_teleported" ) )
  	{
  		const int index = event->GetInt( "entindex" );
  		if ( index == entindex() && IsLocalPlayer() )
  		{
  			// In VR, we want to make sure our head and body
  			// are aligned after we teleport.
  			g_ClientVirtualReality.AlignTorsoAndViewToWeapon();
  		}
  	}
  
  }
  
  //-----------------------------------------------------------------------------
  // 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, bool bVguiUpdate)
  {
  	// 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;
  	
  
  	if (bVguiUpdate)
  	{
  		bool tempvguimode = IsInVGuiInputMode();
  		
  		// Check to see if we're in vgui input mode...
  		DetermineVguiInputMode(pCmd);
  
  		if (tempvguimode == !IsInVGuiInputMode())
  		{
  			if (IsInVGuiInputMode())
  			{
  				engine->ClientCmd("vguimode_true");
  			}
  			else
  			{
  				engine->ClientCmd("vguimode_false");
  			}
  		}
  	}
  
  	return true;
  }
  
  bool C_BasePlayer::CreateMove(float flInputSampleTime, CUserCmd *pCmd)
  {
  	return CreateMove(flInputSampleTime, pCmd, false);
  }
  
  //-----------------------------------------------------------------------------
  // 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() );
  	}
  
  	// 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();
  	}
  
  	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())
  	{
  		// Need to do this on the client to avoid prediction errors
  		if ( GetFlags() & FL_DUCKING )
  		{
  			SetCollisionBounds( VEC_DUCK_HULL_MIN, VEC_DUCK_HULL_MAX );
  		}
  		else
  		{
  			SetCollisionBounds( VEC_HULL_MIN, VEC_HULL_MAX );
  		}
  		
  		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 ), "download/user_custom/%c%c/%s.dat", soundhex[0], soundhex[1], 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 );
  
  	int iHead = LookupBone( pchHeadBoneName );
  	if ( iHead == -1 )
  	{
  		return;
  	}
  
  	matrix3x4_t &mHeadTransform = GetBoneForWrite( iHead );
  
  	// "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;
  	Vector vRealPivotPoint;
  	if( UseVR() )
  	{
  		VMatrix mWorldFromMideye = g_ClientVirtualReality.GetWorldFromMidEye();
  
  		// 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 cl_meathook_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() * cl_meathook_neck_pivot_ingame_up.GetFloat() ) - ( mWorldFromMideye.GetForward() * cl_meathook_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 * cl_meathook_neck_pivot_ingame_up.GetFloat() ) - ( vForward * cl_meathook_neck_pivot_ingame_fwd.GetFloat() );		
  	}
  
  	Vector vDeltaToAdd = vRealPivotPoint - vHeadTransformTranslation;
  
  
  	// 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 );
  	}
  
  	// 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);
  