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