source-sdk-2013-mapbase/sp/src/game/client/c_baseplayer.cpp
Joe Ludwig beaae8ac45 Updated the SDK with the latest code from the TF and HL2 branches
* Adds support for Visual Studio 2012 and 2013
* VR Mode:
. Switches from headtrack.dll to sourcevr.dll
. Improved readability of the UI in VR
. Removed the IPD calibration tool. TF2 will now obey the Oculus
configuration file. Use the Oculus calibration tool in your SDK or
install and run "OpenVR" under Tools in Steam to calibrate your IPD.
. Added dropdown to enable VR mode in the Video options. Removed the -vr
command line option.
. Added the ability to switch in and out of VR mode without quitting the
game
. By default VR mode will run full screen. To switch back to a
borderless window set the vr_force_windowed convar.
. Added support for VR mode on Linux
* Many assorted bug fixes and other changes from Team Fortress in
various shared files
2013-12-03 08:54:16 -08:00

3003 lines
90 KiB
C++

//========= 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 )
{
// Allow the vehicle to clamp the view angles
if ( IsInAVehicle() )
{
IClientVehicle *pVehicle = m_hVehicle.Get()->GetClientVehicle();
if ( pVehicle )
{
pVehicle->UpdateViewAngles( this, pCmd );
engine->SetViewAngles( pCmd->viewangles );
}
}
else
{
#ifndef _X360
if ( joy_autosprint.GetBool() )
#endif
{
if ( input->KeyState( &in_joyspeed ) != 0.0f )
{
pCmd->buttons |= IN_SPEED;
}
}
CBaseCombatWeapon *pWeapon = GetActiveWeapon();
if ( pWeapon )
{
pWeapon->CreateMove( flInputSampleTime, pCmd, m_vecOldViewAngles );
}
}
// If the frozen flag is set, prevent view movement (server prevents the rest of the movement)
if ( GetFlags() & FL_FROZEN )
{
// Don't stomp the first time we get frozen
if ( m_bWasFrozen )
{
// Stomp the new viewangles with old ones
pCmd->viewangles = m_vecOldViewAngles;
engine->SetViewAngles( pCmd->viewangles );
}
else
{
m_bWasFrozen = true;
}
}
else
{
m_bWasFrozen = false;
}
m_vecOldViewAngles = pCmd->viewangles;
// Check to see if we're in vgui input mode...
DetermineVguiInputMode( pCmd );
return true;
}
//-----------------------------------------------------------------------------
// Purpose: Player has changed to a new team
//-----------------------------------------------------------------------------
void C_BasePlayer::TeamChange( int iNewTeam )
{
// Base class does nothing
}
//-----------------------------------------------------------------------------
// Purpose: Creates, destroys, and updates the flashlight effect as needed.
//-----------------------------------------------------------------------------
void C_BasePlayer::UpdateFlashlight()
{
// The dim light is the flashlight.
if ( IsEffectActive( EF_DIMLIGHT ) )
{
if (!m_pFlashlight)
{
// Turned on the headlight; create it.
m_pFlashlight = new CFlashlightEffect(index);
if (!m_pFlashlight)
return;
m_pFlashlight->TurnOn();
}
Vector vecForward, vecRight, vecUp;
EyeVectors( &vecForward, &vecRight, &vecUp );
// Update the light with the new position and direction.
m_pFlashlight->UpdateLight( EyePosition(), vecForward, vecRight, vecUp, FLASHLIGHT_DISTANCE );
}
else if (m_pFlashlight)
{
// Turned off the flashlight; delete it.
delete m_pFlashlight;
m_pFlashlight = NULL;
}
}
//-----------------------------------------------------------------------------
// Purpose: Creates player flashlight if it's ative
//-----------------------------------------------------------------------------
void C_BasePlayer::Flashlight( void )
{
UpdateFlashlight();
// Check for muzzle flash and apply to view model
C_BaseAnimating *ve = this;
if ( GetObserverMode() == OBS_MODE_IN_EYE )
{
ve = dynamic_cast< C_BaseAnimating* >( GetObserverTarget() );
}
}
//-----------------------------------------------------------------------------
// Purpose: Engine is asking whether to add this player to the visible entities list
//-----------------------------------------------------------------------------
void C_BasePlayer::AddEntity( void )
{
// FIXME/UNDONE: Should the local player say yes to adding itself now
// and then, when it ges time to render and it shouldn't still do the render with
// STUDIO_EVENTS set so that its attachment points will get updated even if not
// in third person?
// Add in water effects
if ( IsLocalPlayer() )
{
CreateWaterEffects();
}
// If set to invisible, skip. Do this before resetting the entity pointer so it has
// valid data to decide whether it's visible.
if ( !IsVisible() || !g_pClientMode->ShouldDrawLocalPlayer( this ) )
{
return;
}
// Server says don't interpolate this frame, so set previous info to new info.
if ( IsNoInterpolationFrame() || Teleported() )
{
ResetLatched();
}
// Add in lighting effects
CreateLightEffects();
}
extern float UTIL_WaterLevel( const Vector &position, float minz, float maxz );
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void C_BasePlayer::CreateWaterEffects( void )
{
// Must be completely submerged to bother
if ( GetWaterLevel() < 3 )
{
m_bResampleWaterSurface = true;
return;
}
// Do special setup if this is our first time back underwater
if ( m_bResampleWaterSurface )
{
// Reset our particle timer
m_tWaterParticleTimer.Init( 32 );
// Find the surface of the water to clip against
m_flWaterSurfaceZ = UTIL_WaterLevel( WorldSpaceCenter(), WorldSpaceCenter().z, WorldSpaceCenter().z + 256 );
m_bResampleWaterSurface = false;
}
// Make sure the emitter is setup
if ( m_pWaterEmitter == NULL )
{
if ( ( m_pWaterEmitter = WaterDebrisEffect::Create( "splish" ) ) == NULL )
return;
}
Vector vecVelocity;
GetVectors( &vecVelocity, NULL, NULL );
Vector offset = WorldSpaceCenter();
m_pWaterEmitter->SetSortOrigin( offset );
SimpleParticle *pParticle;
float curTime = gpGlobals->frametime;
// Add as many particles as we need
while ( m_tWaterParticleTimer.NextEvent( curTime ) )
{
offset = WorldSpaceCenter() + ( vecVelocity * 128.0f ) + RandomVector( -128, 128 );
// Make sure we don't start out of the water!
if ( offset.z > m_flWaterSurfaceZ )
{
offset.z = ( m_flWaterSurfaceZ - 8.0f );
}
pParticle = (SimpleParticle *) m_pWaterEmitter->AddParticle( sizeof(SimpleParticle), g_Mat_Fleck_Cement[random->RandomInt(0,1)], offset );
if (pParticle == NULL)
continue;
pParticle->m_flLifetime = 0.0f;
pParticle->m_flDieTime = random->RandomFloat( 2.0f, 4.0f );
pParticle->m_vecVelocity = RandomVector( -2.0f, 2.0f );
//FIXME: We should tint these based on the water's fog value!
float color = random->RandomInt( 32, 128 );
pParticle->m_uchColor[0] = color;
pParticle->m_uchColor[1] = color;
pParticle->m_uchColor[2] = color;
pParticle->m_uchStartSize = 1;
pParticle->m_uchEndSize = 1;
pParticle->m_uchStartAlpha = 255;
pParticle->m_uchEndAlpha = 0;
pParticle->m_flRoll = random->RandomInt( 0, 360 );
pParticle->m_flRollDelta = random->RandomFloat( -0.5f, 0.5f );
}
}
//-----------------------------------------------------------------------------
// Called when not in tactical mode. Allows view to be overriden for things like driving a tank.
//-----------------------------------------------------------------------------
void C_BasePlayer::OverrideView( CViewSetup *pSetup )
{
}
bool C_BasePlayer::ShouldInterpolate()
{
// always interpolate myself
if ( IsLocalPlayer() )
return true;
#ifndef _XBOX
// always interpolate entity if followed by HLTV
if ( HLTVCamera()->GetCameraMan() == this )
return true;
#endif
return BaseClass::ShouldInterpolate();
}
bool C_BasePlayer::ShouldDraw()
{
return ShouldDrawThisPlayer() && BaseClass::ShouldDraw();
}
int C_BasePlayer::DrawModel( int flags )
{
#ifndef PORTAL
// In Portal this check is already performed as part of
// C_Portal_Player::DrawModel()
if ( !ShouldDrawThisPlayer() )
{
return 0;
}
#endif
return BaseClass::DrawModel( flags );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
Vector C_BasePlayer::GetChaseCamViewOffset( CBaseEntity *target )
{
C_BasePlayer *player = ToBasePlayer( target );
if ( player )
{
if ( player->IsAlive() )
{
if ( player->GetFlags() & FL_DUCKING )
{
return VEC_DUCK_VIEW_SCALED( player );
}
return VEC_VIEW_SCALED( player );
}
else
{
// assume it's the players ragdoll
return VEC_DEAD_VIEWHEIGHT_SCALED( player );
}
}
// assume it's the players ragdoll
return VEC_DEAD_VIEWHEIGHT;
}
void C_BasePlayer::CalcChaseCamView(Vector& eyeOrigin, QAngle& eyeAngles, float& fov)
{
C_BaseEntity *target = GetObserverTarget();
if ( !target )
{
// just copy a save in-map position
VectorCopy( EyePosition(), eyeOrigin );
VectorCopy( EyeAngles(), eyeAngles );
return;
};
// If our target isn't visible, we're at a camera point of some kind.
// Instead of letting the player rotate around an invisible point, treat
// the point as a fixed camera.
if ( !target->GetBaseAnimating() && !target->GetModel() )
{
CalcRoamingView( eyeOrigin, eyeAngles, fov );
return;
}
// QAngle tmpangles;
Vector forward, viewpoint;
// GetObserverCamOrigin() returns ragdoll pos if player is ragdolled
Vector origin = target->GetObserverCamOrigin();
VectorAdd( origin, GetChaseCamViewOffset( target ), origin );
QAngle viewangles;
if ( GetObserverMode() == OBS_MODE_IN_EYE )
{
viewangles = eyeAngles;
}
else if ( IsLocalPlayer() )
{
engine->GetViewAngles( viewangles );
if ( UseVR() )
{
// Don't let people play with the pitch - they drive it into the ground or into the air and
// it's distracting at best, nauseating at worst (e.g. when it clips through the ground plane).
viewangles[PITCH] = 20.0f;
}
}
else
{
viewangles = EyeAngles();
}
//=============================================================================
// HPE_BEGIN:
// [Forrest] Fix for (at least one potential case of) CSB-194. Spectating someone
// who is headshotted by a teammate and then switching to chase cam leaves
// you with a permanent roll to the camera that doesn't decay and is not reset
// even when switching to different players or at the start of the next round
// if you are still a spectator. (If you spawn as a player, the view is reset.
// if you switch spectator modes, the view is reset.)
//=============================================================================
#ifdef CSTRIKE_DLL
// The chase camera adopts the yaw and pitch of the previous camera, but the camera
// should not roll.
viewangles.z = 0;
#endif
//=============================================================================
// HPE_END
//=============================================================================
m_flObserverChaseDistance += gpGlobals->frametime*48.0f;
float flMinDistance = CHASE_CAM_DISTANCE_MIN;
float flMaxDistance = CHASE_CAM_DISTANCE_MAX;
if ( target && target->IsBaseTrain() )
{
// if this is a train, we want to be back a little further so we can see more of it
flMaxDistance *= 2.5f;
}
if ( target )
{
C_BaseAnimating *pTargetAnimating = target->GetBaseAnimating();
if ( pTargetAnimating )
{
float flScaleSquared = pTargetAnimating->GetModelScale() * pTargetAnimating->GetModelScale();
flMinDistance *= flScaleSquared;
flMaxDistance *= flScaleSquared;
m_flObserverChaseDistance = flMaxDistance;
}
}
if ( target && !target->IsPlayer() && target->IsNextBot() )
{
// if this is a boss, we want to be back a little further so we can see more of it
flMaxDistance *= 2.5f;
m_flObserverChaseDistance = flMaxDistance;
}
m_flObserverChaseDistance = clamp( m_flObserverChaseDistance, flMinDistance, flMaxDistance );
AngleVectors( viewangles, &forward );
VectorNormalize( forward );
VectorMA(origin, -m_flObserverChaseDistance, forward, viewpoint );
trace_t trace;
CTraceFilterNoNPCsOrPlayer filter( target, COLLISION_GROUP_NONE );
C_BaseEntity::PushEnableAbsRecomputations( false ); // HACK don't recompute positions while doing RayTrace
UTIL_TraceHull( origin, viewpoint, WALL_MIN, WALL_MAX, MASK_SOLID, &filter, &trace );
C_BaseEntity::PopEnableAbsRecomputations();
if (trace.fraction < 1.0)
{
viewpoint = trace.endpos;
m_flObserverChaseDistance = VectorLength(origin - eyeOrigin);
}
VectorCopy( viewangles, eyeAngles );
VectorCopy( viewpoint, eyeOrigin );
fov = GetFOV();
}
void C_BasePlayer::CalcRoamingView(Vector& eyeOrigin, QAngle& eyeAngles, float& fov)
{
C_BaseEntity *target = GetObserverTarget();
if ( !target )
{
target = this;
}
m_flObserverChaseDistance = 0.0;
eyeOrigin = target->EyePosition();
eyeAngles = target->EyeAngles();
if ( spec_track.GetInt() > 0 )
{
C_BaseEntity *target = ClientEntityList().GetBaseEntity( spec_track.GetInt() );
if ( target )
{
Vector v = target->GetAbsOrigin(); v.z += 54;
QAngle a; VectorAngles( v - eyeOrigin, a );
NormalizeAngles( a );
eyeAngles = a;
engine->SetViewAngles( a );
}
}
// Apply a smoothing offset to smooth out prediction errors.
Vector vSmoothOffset;
GetPredictionErrorSmoothingVector( vSmoothOffset );
eyeOrigin += vSmoothOffset;
fov = GetFOV();
}
//-----------------------------------------------------------------------------
// Purpose: Calculate the view for the player while he's in freeze frame observer mode
//-----------------------------------------------------------------------------
void C_BasePlayer::CalcFreezeCamView( Vector& eyeOrigin, QAngle& eyeAngles, float& fov )
{
C_BaseEntity *pTarget = GetObserverTarget();
if ( !pTarget )
{
CalcDeathCamView( eyeOrigin, eyeAngles, fov );
return;
}
// Zoom towards our target
float flCurTime = (gpGlobals->curtime - m_flFreezeFrameStartTime);
float flBlendPerc = clamp( flCurTime / spec_freeze_traveltime.GetFloat(), 0.f, 1.f );
flBlendPerc = SimpleSpline( flBlendPerc );
Vector vecCamDesired = pTarget->GetObserverCamOrigin(); // Returns ragdoll origin if they're ragdolled
VectorAdd( vecCamDesired, GetChaseCamViewOffset( pTarget ), vecCamDesired );
Vector vecCamTarget = vecCamDesired;
if ( pTarget->IsAlive() )
{
// Look at their chest, not their head
Vector maxs = pTarget->GetBaseAnimating() ? VEC_HULL_MAX_SCALED( pTarget->GetBaseAnimating() ) : VEC_HULL_MAX;
vecCamTarget.z -= (maxs.z * 0.5);
}
else
{
vecCamTarget.z += pTarget->GetBaseAnimating() ? VEC_DEAD_VIEWHEIGHT_SCALED( pTarget->GetBaseAnimating() ).z : VEC_DEAD_VIEWHEIGHT.z; // look over ragdoll, not through
}
// Figure out a view position in front of the target
Vector vecEyeOnPlane = eyeOrigin;
vecEyeOnPlane.z = vecCamTarget.z;
Vector vecTargetPos = vecCamTarget;
Vector vecToTarget = vecTargetPos - vecEyeOnPlane;
VectorNormalize( vecToTarget );
// Stop a few units away from the target, and shift up to be at the same height
vecTargetPos = vecCamTarget - (vecToTarget * m_flFreezeFrameDistance);
float flEyePosZ = pTarget->EyePosition().z;
vecTargetPos.z = flEyePosZ + m_flFreezeZOffset;
// Now trace out from the target, so that we're put in front of any walls
trace_t trace;
C_BaseEntity::PushEnableAbsRecomputations( false ); // HACK don't recompute positions while doing RayTrace
UTIL_TraceHull( vecCamTarget, vecTargetPos, WALL_MIN, WALL_MAX, MASK_SOLID, pTarget, COLLISION_GROUP_NONE, &trace );
C_BaseEntity::PopEnableAbsRecomputations();
if (trace.fraction < 1.0)
{
// The camera's going to be really close to the target. So we don't end up
// looking at someone's chest, aim close freezecams at the target's eyes.
vecTargetPos = trace.endpos;
vecCamTarget = vecCamDesired;
// To stop all close in views looking up at character's chins, move the view up.
vecTargetPos.z += fabs(vecCamTarget.z - vecTargetPos.z) * 0.85;
C_BaseEntity::PushEnableAbsRecomputations( false ); // HACK don't recompute positions while doing RayTrace
UTIL_TraceHull( vecCamTarget, vecTargetPos, WALL_MIN, WALL_MAX, MASK_SOLID, pTarget, COLLISION_GROUP_NONE, &trace );
C_BaseEntity::PopEnableAbsRecomputations();
vecTargetPos = trace.endpos;
}
// Look directly at the target
vecToTarget = vecCamTarget - vecTargetPos;
VectorNormalize( vecToTarget );
VectorAngles( vecToTarget, eyeAngles );
VectorLerp( m_vecFreezeFrameStart, vecTargetPos, flBlendPerc, eyeOrigin );
if ( flCurTime >= spec_freeze_traveltime.GetFloat() && !m_bSentFreezeFrame )
{
IGameEvent *pEvent = gameeventmanager->CreateEvent( "freezecam_started" );
if ( pEvent )
{
gameeventmanager->FireEventClientSide( pEvent );
}
m_bSentFreezeFrame = true;
view->FreezeFrame( spec_freeze_time.GetFloat() );
}
}
void C_BasePlayer::CalcInEyeCamView(Vector& eyeOrigin, QAngle& eyeAngles, float& fov)
{
C_BaseEntity *target = GetObserverTarget();
if ( !target )
{
// just copy a save in-map position
VectorCopy( EyePosition(), eyeOrigin );
VectorCopy( EyeAngles(), eyeAngles );
return;
};
if ( !target->IsAlive() )
{
// if dead, show from 3rd person
CalcChaseCamView( eyeOrigin, eyeAngles, fov );
return;
}
fov = GetFOV(); // TODO use tragets FOV
m_flObserverChaseDistance = 0.0;
eyeAngles = target->EyeAngles();
eyeOrigin = target->GetAbsOrigin();
// Apply punch angle
VectorAdd( eyeAngles, GetPunchAngle(), eyeAngles );
#if defined( REPLAY_ENABLED )
if( engine->IsHLTV() || g_pEngineClientReplay->IsPlayingReplayDemo() )
#else
if( engine->IsHLTV() )
#endif
{
C_BaseAnimating *pTargetAnimating = target->GetBaseAnimating();
if ( target->GetFlags() & FL_DUCKING )
{
eyeOrigin += pTargetAnimating ? VEC_DUCK_VIEW_SCALED( pTargetAnimating ) : VEC_DUCK_VIEW;
}
else
{
eyeOrigin += pTargetAnimating ? VEC_VIEW_SCALED( pTargetAnimating ) : VEC_VIEW;
}
}
else
{
Vector offset = GetViewOffset();
#ifdef HL2MP
offset = target->GetViewOffset();
#endif
eyeOrigin += offset; // hack hack
}
engine->SetViewAngles( eyeAngles );
}
float C_BasePlayer::GetDeathCamInterpolationTime()
{
return DEATH_ANIMATION_TIME;
}
void C_BasePlayer::CalcDeathCamView(Vector& eyeOrigin, QAngle& eyeAngles, float& fov)
{
CBaseEntity * pKiller = NULL;
if ( mp_forcecamera.GetInt() == OBS_ALLOW_ALL )
{
// if mp_forcecamera is off let user see killer or look around
pKiller = GetObserverTarget();
eyeAngles = EyeAngles();
}
float interpolation = ( gpGlobals->curtime - m_flDeathTime ) / GetDeathCamInterpolationTime();
interpolation = clamp( interpolation, 0.0f, 1.0f );
m_flObserverChaseDistance += gpGlobals->frametime*48.0f;
m_flObserverChaseDistance = clamp( m_flObserverChaseDistance, ( CHASE_CAM_DISTANCE_MIN * 2 ), CHASE_CAM_DISTANCE_MAX );
QAngle aForward = eyeAngles;
Vector origin = EyePosition();
// NOTE: This will create the ragdoll in CSS if m_hRagdoll is set, but m_pRagdoll is not yet presetn
IRagdoll *pRagdoll = GetRepresentativeRagdoll();
if ( pRagdoll )
{
origin = pRagdoll->GetRagdollOrigin();
origin.z += VEC_DEAD_VIEWHEIGHT_SCALED( this ).z;
}
if ( pKiller && pKiller->IsPlayer() && (pKiller != this) )
{
Vector vKiller = pKiller->EyePosition() - origin;
QAngle aKiller; VectorAngles( vKiller, aKiller );
InterpolateAngles( aForward, aKiller, eyeAngles, interpolation );
};
Vector vForward; AngleVectors( eyeAngles, &vForward );
VectorNormalize( vForward );
VectorMA( origin, -m_flObserverChaseDistance, vForward, eyeOrigin );
trace_t trace; // clip against world
C_BaseEntity::PushEnableAbsRecomputations( false ); // HACK don't recompute positions while doing RayTrace
UTIL_TraceHull( origin, eyeOrigin, WALL_MIN, WALL_MAX, MASK_SOLID, this, COLLISION_GROUP_NONE, &trace );
C_BaseEntity::PopEnableAbsRecomputations();
if (trace.fraction < 1.0)
{
eyeOrigin = trace.endpos;
m_flObserverChaseDistance = VectorLength(origin - eyeOrigin);
}
fov = GetFOV();
}
//-----------------------------------------------------------------------------
// Purpose: Return the weapon to have open the weapon selection on, based upon our currently active weapon
// Base class just uses the weapon that's currently active.
//-----------------------------------------------------------------------------
C_BaseCombatWeapon *C_BasePlayer::GetActiveWeaponForSelection( void )
{
return GetActiveWeapon();
}
C_BaseAnimating* C_BasePlayer::GetRenderedWeaponModel()
{
// Attach to either their weapon model or their view model.
if ( ShouldDrawLocalPlayer() || !IsLocalPlayer() )
{
return GetActiveWeapon();
}
else
{
return GetViewModel();
}
}
//-----------------------------------------------------------------------------
// Purpose: Gets a pointer to the local player, if it exists yet.
// Output : C_BasePlayer
//-----------------------------------------------------------------------------
C_BasePlayer *C_BasePlayer::GetLocalPlayer( void )
{
return s_pLocalPlayer;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : bThirdperson -
//-----------------------------------------------------------------------------
void C_BasePlayer::ThirdPersonSwitch( bool bThirdperson )
{
// We've switch from first to third, or vice versa.
UpdateVisibility();
// Update the visibility of anything bone attached to us.
if ( IsLocalPlayer() )
{
bool bShouldDrawLocalPlayer = ShouldDrawLocalPlayer();
for ( int i=0; i<GetNumBoneAttachments(); ++i )
{
C_BaseAnimating* pBoneAttachment = GetBoneAttachment( i );
if ( pBoneAttachment )
{
if ( bShouldDrawLocalPlayer )
{
pBoneAttachment->RemoveEffects( EF_NODRAW );
}
else
{
pBoneAttachment->AddEffects( EF_NODRAW );
}
}
}
}
}
//-----------------------------------------------------------------------------
// Purpose: single place to decide whether the camera is in the first-person position
// NOTE - ShouldDrawLocalPlayer() can be true even if the camera is in the first-person position, e.g. in VR.
//-----------------------------------------------------------------------------
/*static*/ bool C_BasePlayer::LocalPlayerInFirstPersonView()
{
C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer();
if ( pLocalPlayer == NULL )
{
return false;
}
int ObserverMode = pLocalPlayer->GetObserverMode();
if ( ( ObserverMode == OBS_MODE_NONE ) || ( ObserverMode == OBS_MODE_IN_EYE ) )
{
return !input->CAM_IsThirdPerson() && ( !ToolsEnabled() || !ToolFramework_IsThirdPersonCamera() );
}
// 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);