You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3002 lines
90 KiB
3002 lines
90 KiB
//========= 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);
|
|
|