You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2084 lines
52 KiB
2084 lines
52 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: Implements shared baseplayer class functionality |
|
// |
|
// $NoKeywords: $ |
|
//=============================================================================// |
|
|
|
#include "cbase.h" |
|
#include "movevars_shared.h" |
|
#include "util_shared.h" |
|
#include "datacache/imdlcache.h" |
|
#if defined ( TF_DLL ) || defined ( TF_CLIENT_DLL ) |
|
#include "tf_gamerules.h" |
|
#endif |
|
|
|
#if defined( CLIENT_DLL ) |
|
|
|
#include "iclientvehicle.h" |
|
#include "prediction.h" |
|
#include "c_basedoor.h" |
|
#include "c_world.h" |
|
#include "view.h" |
|
#include "client_virtualreality.h" |
|
#define CRecipientFilter C_RecipientFilter |
|
#include "sourcevr/isourcevirtualreality.h" |
|
|
|
#else |
|
|
|
#include "iservervehicle.h" |
|
#include "trains.h" |
|
#include "world.h" |
|
#include "doors.h" |
|
#include "ai_basenpc.h" |
|
#include "env_zoom.h" |
|
|
|
extern int TrainSpeed(int iSpeed, int iMax); |
|
|
|
#endif |
|
|
|
#if defined( CSTRIKE_DLL ) |
|
#include "weapon_c4.h" |
|
#endif // CSTRIKE_DLL |
|
|
|
#include "in_buttons.h" |
|
#include "engine/IEngineSound.h" |
|
#include "tier0/vprof.h" |
|
#include "SoundEmitterSystem/isoundemittersystembase.h" |
|
#include "decals.h" |
|
#include "obstacle_pushaway.h" |
|
#ifdef SIXENSE |
|
#include "sixense/in_sixense.h" |
|
#endif |
|
|
|
// NVNT haptic utils |
|
#include "haptics/haptic_utils.h" |
|
// memdbgon must be the last include file in a .cpp file!!! |
|
#include "tier0/memdbgon.h" |
|
|
|
#if defined(GAME_DLL) && !defined(_XBOX) |
|
extern ConVar sv_pushaway_max_force; |
|
extern ConVar sv_pushaway_force; |
|
extern ConVar sv_turbophysics; |
|
|
|
class CUsePushFilter : public CTraceFilterEntitiesOnly |
|
{ |
|
public: |
|
bool ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask ) |
|
{ |
|
CBaseEntity *pEntity = EntityFromEntityHandle( pHandleEntity ); |
|
|
|
// Static prop case... |
|
if ( !pEntity ) |
|
return false; |
|
|
|
// Only impact on physics objects |
|
if ( !pEntity->VPhysicsGetObject() ) |
|
return false; |
|
|
|
#if defined( CSTRIKE_DLL ) |
|
// don't push the bomb! |
|
if ( dynamic_cast<CC4*>( pEntity ) ) |
|
return false; |
|
#endif // CSTRIKE_DLL |
|
|
|
return g_pGameRules->CanEntityBeUsePushed( pEntity ); |
|
} |
|
}; |
|
#endif |
|
|
|
#ifdef CLIENT_DLL |
|
ConVar mp_usehwmmodels( "mp_usehwmmodels", "0", NULL, "Enable the use of the hw morph models. (-1 = never, 1 = always, 0 = based upon GPU)" ); // -1 = never, 0 = if hasfastvertextextures, 1 = always |
|
#endif |
|
|
|
bool UseHWMorphModels() |
|
{ |
|
// #ifdef CLIENT_DLL |
|
// if ( mp_usehwmmodels.GetInt() == 0 ) |
|
// return g_pMaterialSystemHardwareConfig->HasFastVertexTextures(); |
|
// |
|
// return mp_usehwmmodels.GetInt() > 0; |
|
// #else |
|
// return false; |
|
// #endif |
|
return false; |
|
} |
|
|
|
void CopySoundNameWithModifierToken( char *pchDest, const char *pchSource, int nMaxLenInChars, const char *pchToken ) |
|
{ |
|
// Copy the sound name |
|
int nSource = 0; |
|
int nDest = 0; |
|
bool bFoundPeriod = false; |
|
|
|
while ( pchSource[ nSource ] != '\0' && nDest < nMaxLenInChars - 2 ) |
|
{ |
|
pchDest[ nDest ] = pchSource[ nSource ]; |
|
nDest++; |
|
nSource++; |
|
|
|
if ( !bFoundPeriod && pchSource[ nSource - 1 ] == '.' ) |
|
{ |
|
// Insert special token after the period |
|
bFoundPeriod = true; |
|
|
|
int nToken = 0; |
|
|
|
while ( pchToken[ nToken ] != '\0' && nDest < nMaxLenInChars - 2 ) |
|
{ |
|
pchDest[ nDest ] = pchToken[ nToken ]; |
|
nDest++; |
|
nToken++; |
|
} |
|
} |
|
} |
|
|
|
pchDest[ nDest ] = '\0'; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Output : float |
|
//----------------------------------------------------------------------------- |
|
float CBasePlayer::GetTimeBase( void ) const |
|
{ |
|
return m_nTickBase * TICK_INTERVAL; |
|
} |
|
|
|
float CBasePlayer::GetPlayerMaxSpeed() |
|
{ |
|
// player max speed is the lower limit of m_flMaxSpeed and sv_maxspeed |
|
float fMaxSpeed = sv_maxspeed.GetFloat(); |
|
if ( MaxSpeed() > 0.0f && MaxSpeed() < fMaxSpeed ) |
|
fMaxSpeed = MaxSpeed(); |
|
|
|
return fMaxSpeed; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Called every usercmd by the player PreThink |
|
//----------------------------------------------------------------------------- |
|
void CBasePlayer::ItemPreFrame() |
|
{ |
|
// Handle use events |
|
PlayerUse(); |
|
|
|
CBaseCombatWeapon *pActive = GetActiveWeapon(); |
|
|
|
// Allow all the holstered weapons to update |
|
for ( int i = 0; i < WeaponCount(); ++i ) |
|
{ |
|
CBaseCombatWeapon *pWeapon = GetWeapon( i ); |
|
|
|
if ( pWeapon == NULL ) |
|
continue; |
|
|
|
if ( pActive == pWeapon ) |
|
continue; |
|
|
|
pWeapon->ItemHolsterFrame(); |
|
} |
|
|
|
if ( gpGlobals->curtime < m_flNextAttack ) |
|
return; |
|
|
|
if (!pActive) |
|
return; |
|
|
|
#if defined( CLIENT_DLL ) |
|
// Not predicting this weapon |
|
if ( !pActive->IsPredicted() ) |
|
return; |
|
#endif |
|
|
|
pActive->ItemPreFrame(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Output : Returns true on success, false on failure. |
|
//----------------------------------------------------------------------------- |
|
bool CBasePlayer::UsingStandardWeaponsInVehicle( void ) |
|
{ |
|
Assert( IsInAVehicle() ); |
|
#if !defined( CLIENT_DLL ) |
|
IServerVehicle *pVehicle = GetVehicle(); |
|
#else |
|
IClientVehicle *pVehicle = GetVehicle(); |
|
#endif |
|
Assert( pVehicle ); |
|
if ( !pVehicle ) |
|
return true; |
|
|
|
// NOTE: We *have* to do this before ItemPostFrame because ItemPostFrame |
|
// may dump us out of the vehicle |
|
int nRole = pVehicle->GetPassengerRole( this ); |
|
bool bUsingStandardWeapons = pVehicle->IsPassengerUsingStandardWeapons( nRole ); |
|
|
|
// Fall through and check weapons, etc. if we're using them |
|
if (!bUsingStandardWeapons ) |
|
return false; |
|
|
|
return true; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Called every usercmd by the player PostThink |
|
//----------------------------------------------------------------------------- |
|
void CBasePlayer::ItemPostFrame() |
|
{ |
|
VPROF( "CBasePlayer::ItemPostFrame" ); |
|
|
|
// Put viewmodels into basically correct place based on new player origin |
|
CalcViewModelView( EyePosition(), EyeAngles() ); |
|
|
|
// Don't process items while in a vehicle. |
|
if ( GetVehicle() ) |
|
{ |
|
#if defined( CLIENT_DLL ) |
|
IClientVehicle *pVehicle = GetVehicle(); |
|
#else |
|
IServerVehicle *pVehicle = GetVehicle(); |
|
#endif |
|
|
|
bool bUsingStandardWeapons = UsingStandardWeaponsInVehicle(); |
|
|
|
#if defined( CLIENT_DLL ) |
|
if ( pVehicle->IsPredicted() ) |
|
#endif |
|
{ |
|
pVehicle->ItemPostFrame( this ); |
|
} |
|
|
|
if (!bUsingStandardWeapons || !GetVehicle()) |
|
return; |
|
} |
|
|
|
|
|
// check if the player is using something |
|
if ( m_hUseEntity != NULL ) |
|
{ |
|
#if !defined( CLIENT_DLL ) |
|
Assert( !IsInAVehicle() ); |
|
ImpulseCommands();// this will call playerUse |
|
#endif |
|
return; |
|
} |
|
|
|
if ( gpGlobals->curtime < m_flNextAttack ) |
|
{ |
|
if ( GetActiveWeapon() ) |
|
{ |
|
GetActiveWeapon()->ItemBusyFrame(); |
|
} |
|
} |
|
else |
|
{ |
|
if ( GetActiveWeapon() && (!IsInAVehicle() || UsingStandardWeaponsInVehicle()) ) |
|
{ |
|
#if defined( CLIENT_DLL ) |
|
// Not predicting this weapon |
|
if ( GetActiveWeapon()->IsPredicted() ) |
|
#endif |
|
|
|
{ |
|
GetActiveWeapon()->ItemPostFrame( ); |
|
} |
|
} |
|
} |
|
|
|
#if !defined( CLIENT_DLL ) |
|
ImpulseCommands(); |
|
#else |
|
// NOTE: If we ever support full impulse commands on the client, |
|
// remove this line and call ImpulseCommands instead. |
|
m_nImpulse = 0; |
|
#endif |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Eye angles |
|
//----------------------------------------------------------------------------- |
|
const QAngle &CBasePlayer::EyeAngles( ) |
|
{ |
|
// NOTE: Viewangles are measured *relative* to the parent's coordinate system |
|
CBaseEntity *pMoveParent = const_cast<CBasePlayer*>(this)->GetMoveParent(); |
|
|
|
if ( !pMoveParent ) |
|
{ |
|
return pl.v_angle; |
|
} |
|
|
|
// FIXME: Cache off the angles? |
|
matrix3x4_t eyesToParent, eyesToWorld; |
|
AngleMatrix( pl.v_angle, eyesToParent ); |
|
ConcatTransforms( pMoveParent->EntityToWorldTransform(), eyesToParent, eyesToWorld ); |
|
|
|
static QAngle angEyeWorld; |
|
MatrixAngles( eyesToWorld, angEyeWorld ); |
|
return angEyeWorld; |
|
} |
|
|
|
|
|
const QAngle &CBasePlayer::LocalEyeAngles() |
|
{ |
|
return pl.v_angle; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Actual Eye position + angles |
|
//----------------------------------------------------------------------------- |
|
Vector CBasePlayer::EyePosition( ) |
|
{ |
|
if ( GetVehicle() != NULL ) |
|
{ |
|
// Return the cached result |
|
CacheVehicleView(); |
|
return m_vecVehicleViewOrigin; |
|
} |
|
else |
|
{ |
|
#ifdef CLIENT_DLL |
|
if ( IsObserver() ) |
|
{ |
|
if ( GetObserverMode() == OBS_MODE_CHASE ) |
|
{ |
|
if ( IsLocalPlayer() ) |
|
{ |
|
return MainViewOrigin(); |
|
} |
|
} |
|
} |
|
#endif |
|
return BaseClass::EyePosition(); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : |
|
// Output : const Vector |
|
//----------------------------------------------------------------------------- |
|
const Vector CBasePlayer::GetPlayerMins( void ) const |
|
{ |
|
if ( IsObserver() ) |
|
{ |
|
return VEC_OBS_HULL_MIN_SCALED( this ); |
|
} |
|
else |
|
{ |
|
if ( GetFlags() & FL_DUCKING ) |
|
{ |
|
return VEC_DUCK_HULL_MIN_SCALED( this ); |
|
} |
|
else |
|
{ |
|
return VEC_HULL_MIN_SCALED( this ); |
|
} |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : |
|
// Output : const Vector |
|
//----------------------------------------------------------------------------- |
|
const Vector CBasePlayer::GetPlayerMaxs( void ) const |
|
{ |
|
if ( IsObserver() ) |
|
{ |
|
return VEC_OBS_HULL_MAX_SCALED( this ); |
|
} |
|
else |
|
{ |
|
if ( GetFlags() & FL_DUCKING ) |
|
{ |
|
return VEC_DUCK_HULL_MAX_SCALED( this ); |
|
} |
|
else |
|
{ |
|
return VEC_HULL_MAX_SCALED( this ); |
|
} |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Update the vehicle view, or simply return the cached position and angles |
|
//----------------------------------------------------------------------------- |
|
void CBasePlayer::CacheVehicleView( void ) |
|
{ |
|
// If we've calculated the view this frame, then there's no need to recalculate it |
|
if ( m_nVehicleViewSavedFrame == gpGlobals->framecount ) |
|
return; |
|
|
|
#ifdef CLIENT_DLL |
|
IClientVehicle *pVehicle = GetVehicle(); |
|
#else |
|
IServerVehicle *pVehicle = GetVehicle(); |
|
#endif |
|
|
|
if ( pVehicle != NULL ) |
|
{ |
|
int nRole = pVehicle->GetPassengerRole( this ); |
|
|
|
// Get our view for this frame |
|
pVehicle->GetVehicleViewPosition( nRole, &m_vecVehicleViewOrigin, &m_vecVehicleViewAngles, &m_flVehicleViewFOV ); |
|
m_nVehicleViewSavedFrame = gpGlobals->framecount; |
|
|
|
#ifdef CLIENT_DLL |
|
if( UseVR() ) |
|
{ |
|
C_BaseAnimating *pVehicleAnimating = dynamic_cast<C_BaseAnimating *>( pVehicle ); |
|
if( pVehicleAnimating ) |
|
{ |
|
int eyeAttachmentIndex = pVehicleAnimating->LookupAttachment( "vehicle_driver_eyes" ); |
|
|
|
Vector vehicleEyeOrigin; |
|
QAngle vehicleEyeAngles; |
|
pVehicleAnimating->GetAttachment( eyeAttachmentIndex, vehicleEyeOrigin, vehicleEyeAngles ); |
|
|
|
g_ClientVirtualReality.OverrideTorsoTransform( vehicleEyeOrigin, vehicleEyeAngles ); |
|
} |
|
} |
|
#endif |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Returns eye vectors |
|
//----------------------------------------------------------------------------- |
|
void CBasePlayer::EyeVectors( Vector *pForward, Vector *pRight, Vector *pUp ) |
|
{ |
|
if ( GetVehicle() != NULL ) |
|
{ |
|
// Cache or retrieve our calculated position in the vehicle |
|
CacheVehicleView(); |
|
AngleVectors( m_vecVehicleViewAngles, pForward, pRight, pUp ); |
|
} |
|
else |
|
{ |
|
AngleVectors( EyeAngles(), pForward, pRight, pUp ); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Returns the eye position and angle vectors. |
|
//----------------------------------------------------------------------------- |
|
void CBasePlayer::EyePositionAndVectors( Vector *pPosition, Vector *pForward, |
|
Vector *pRight, Vector *pUp ) |
|
{ |
|
// Handle the view in the vehicle |
|
if ( GetVehicle() != NULL ) |
|
{ |
|
CacheVehicleView(); |
|
AngleVectors( m_vecVehicleViewAngles, pForward, pRight, pUp ); |
|
|
|
if ( pPosition != NULL ) |
|
{ |
|
*pPosition = m_vecVehicleViewOrigin; |
|
} |
|
} |
|
else |
|
{ |
|
VectorCopy( BaseClass::EyePosition(), *pPosition ); |
|
AngleVectors( EyeAngles(), pForward, pRight, pUp ); |
|
} |
|
} |
|
|
|
#ifdef CLIENT_DLL |
|
surfacedata_t * CBasePlayer::GetFootstepSurface( const Vector &origin, const char *surfaceName ) |
|
{ |
|
return physprops->GetSurfaceData( physprops->GetSurfaceIndex( surfaceName ) ); |
|
} |
|
#endif |
|
|
|
surfacedata_t *CBasePlayer::GetLadderSurface( const Vector &origin ) |
|
{ |
|
#ifdef CLIENT_DLL |
|
return GetFootstepSurface( origin, "ladder" ); |
|
#else |
|
return physprops->GetSurfaceData( physprops->GetSurfaceIndex( "ladder" ) ); |
|
#endif |
|
} |
|
|
|
void CBasePlayer::UpdateStepSound( surfacedata_t *psurface, const Vector &vecOrigin, const Vector &vecVelocity ) |
|
{ |
|
bool bWalking; |
|
float fvol; |
|
Vector knee; |
|
Vector feet; |
|
float height; |
|
float speed; |
|
float velrun; |
|
float velwalk; |
|
int fLadder; |
|
|
|
if ( m_flStepSoundTime > 0 ) |
|
{ |
|
m_flStepSoundTime -= 1000.0f * gpGlobals->frametime; |
|
if ( m_flStepSoundTime < 0 ) |
|
{ |
|
m_flStepSoundTime = 0; |
|
} |
|
} |
|
|
|
if ( m_flStepSoundTime > 0 ) |
|
return; |
|
|
|
if ( GetFlags() & (FL_FROZEN|FL_ATCONTROLS)) |
|
return; |
|
|
|
if ( GetMoveType() == MOVETYPE_NOCLIP || GetMoveType() == MOVETYPE_OBSERVER ) |
|
return; |
|
|
|
if ( !sv_footsteps.GetFloat() ) |
|
return; |
|
|
|
speed = VectorLength( vecVelocity ); |
|
float groundspeed = Vector2DLength( vecVelocity.AsVector2D() ); |
|
|
|
// determine if we are on a ladder |
|
fLadder = ( GetMoveType() == MOVETYPE_LADDER ); |
|
|
|
GetStepSoundVelocities( &velwalk, &velrun ); |
|
|
|
bool onground = ( GetFlags() & FL_ONGROUND ); |
|
bool movingalongground = ( groundspeed > 0.0001f ); |
|
bool moving_fast_enough = ( speed >= velwalk ); |
|
|
|
#ifdef PORTAL |
|
// In Portal we MUST play footstep sounds even when the player is moving very slowly |
|
// This is used to count the number of footsteps they take in the challenge mode |
|
// -Jeep |
|
moving_fast_enough = true; |
|
#endif |
|
|
|
// To hear step sounds you must be either on a ladder or moving along the ground AND |
|
// You must be moving fast enough |
|
|
|
if ( !moving_fast_enough || !(fLadder || ( onground && movingalongground )) ) |
|
return; |
|
|
|
// MoveHelper()->PlayerSetAnimation( PLAYER_WALK ); |
|
|
|
bWalking = speed < velrun; |
|
|
|
VectorCopy( vecOrigin, knee ); |
|
VectorCopy( vecOrigin, feet ); |
|
|
|
height = GetPlayerMaxs()[ 2 ] - GetPlayerMins()[ 2 ]; |
|
|
|
knee[2] = vecOrigin[2] + 0.2 * height; |
|
|
|
// find out what we're stepping in or on... |
|
if ( fLadder ) |
|
{ |
|
psurface = GetLadderSurface(vecOrigin); |
|
fvol = 0.5; |
|
|
|
SetStepSoundTime( STEPSOUNDTIME_ON_LADDER, bWalking ); |
|
} |
|
#ifdef CSTRIKE_DLL |
|
else if ( enginetrace->GetPointContents( knee ) & MASK_WATER ) // we want to use the knee for Cstrike, not the waist |
|
#else |
|
else if ( GetWaterLevel() == WL_Waist ) |
|
#endif // CSTRIKE_DLL |
|
{ |
|
static int iSkipStep = 0; |
|
|
|
if ( iSkipStep == 0 ) |
|
{ |
|
iSkipStep++; |
|
return; |
|
} |
|
|
|
if ( iSkipStep++ == 3 ) |
|
{ |
|
iSkipStep = 0; |
|
} |
|
psurface = physprops->GetSurfaceData( physprops->GetSurfaceIndex( "wade" ) ); |
|
fvol = 0.65; |
|
SetStepSoundTime( STEPSOUNDTIME_WATER_KNEE, bWalking ); |
|
} |
|
else if ( GetWaterLevel() == WL_Feet ) |
|
{ |
|
psurface = physprops->GetSurfaceData( physprops->GetSurfaceIndex( "water" ) ); |
|
fvol = bWalking ? 0.2 : 0.5; |
|
|
|
SetStepSoundTime( STEPSOUNDTIME_WATER_FOOT, bWalking ); |
|
} |
|
else |
|
{ |
|
if ( !psurface ) |
|
return; |
|
|
|
SetStepSoundTime( STEPSOUNDTIME_NORMAL, bWalking ); |
|
|
|
switch ( psurface->game.material ) |
|
{ |
|
default: |
|
case CHAR_TEX_CONCRETE: |
|
fvol = bWalking ? 0.2 : 0.5; |
|
break; |
|
|
|
case CHAR_TEX_METAL: |
|
fvol = bWalking ? 0.2 : 0.5; |
|
break; |
|
|
|
case CHAR_TEX_DIRT: |
|
fvol = bWalking ? 0.25 : 0.55; |
|
break; |
|
|
|
case CHAR_TEX_VENT: |
|
fvol = bWalking ? 0.4 : 0.7; |
|
break; |
|
|
|
case CHAR_TEX_GRATE: |
|
fvol = bWalking ? 0.2 : 0.5; |
|
break; |
|
|
|
case CHAR_TEX_TILE: |
|
fvol = bWalking ? 0.2 : 0.5; |
|
break; |
|
|
|
case CHAR_TEX_SLOSH: |
|
fvol = bWalking ? 0.2 : 0.5; |
|
break; |
|
} |
|
} |
|
|
|
// play the sound |
|
// 65% volume if ducking |
|
if ( GetFlags() & FL_DUCKING ) |
|
{ |
|
fvol *= 0.65; |
|
} |
|
|
|
PlayStepSound( feet, psurface, fvol, false ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : step - |
|
// fvol - |
|
// force - force sound to play |
|
//----------------------------------------------------------------------------- |
|
void CBasePlayer::PlayStepSound( Vector &vecOrigin, surfacedata_t *psurface, float fvol, bool force ) |
|
{ |
|
if ( gpGlobals->maxClients > 1 && !sv_footsteps.GetFloat() ) |
|
return; |
|
|
|
#if defined( CLIENT_DLL ) |
|
// during prediction play footstep sounds only once |
|
if ( prediction->InPrediction() && !prediction->IsFirstTimePredicted() ) |
|
return; |
|
#endif |
|
|
|
if ( !psurface ) |
|
return; |
|
|
|
int nSide = m_Local.m_nStepside; |
|
unsigned short stepSoundName = nSide ? psurface->sounds.stepleft : psurface->sounds.stepright; |
|
if ( !stepSoundName ) |
|
return; |
|
|
|
m_Local.m_nStepside = !nSide; |
|
|
|
CSoundParameters params; |
|
|
|
Assert( nSide == 0 || nSide == 1 ); |
|
|
|
if ( m_StepSoundCache[ nSide ].m_usSoundNameIndex == stepSoundName ) |
|
{ |
|
params = m_StepSoundCache[ nSide ].m_SoundParameters; |
|
} |
|
else |
|
{ |
|
IPhysicsSurfaceProps *physprops = MoveHelper()->GetSurfaceProps(); |
|
const char *pSoundName = physprops->GetString( stepSoundName ); |
|
|
|
// Give child classes an opportunity to override. |
|
pSoundName = GetOverrideStepSound( pSoundName ); |
|
|
|
if ( !CBaseEntity::GetParametersForSound( pSoundName, params, NULL ) ) |
|
return; |
|
|
|
// Only cache if there's one option. Otherwise we'd never here any other sounds |
|
if ( params.count == 1 ) |
|
{ |
|
m_StepSoundCache[ nSide ].m_usSoundNameIndex = stepSoundName; |
|
m_StepSoundCache[ nSide ].m_SoundParameters = params; |
|
} |
|
} |
|
|
|
CRecipientFilter filter; |
|
filter.AddRecipientsByPAS( vecOrigin ); |
|
|
|
#ifndef CLIENT_DLL |
|
// in MP, server removes all players in the vecOrigin's PVS, these players generate the footsteps client side |
|
if ( gpGlobals->maxClients > 1 ) |
|
{ |
|
filter.RemoveRecipientsByPVS( vecOrigin ); |
|
} |
|
#endif |
|
|
|
EmitSound_t ep; |
|
ep.m_nChannel = CHAN_BODY; |
|
ep.m_pSoundName = params.soundname; |
|
#if defined ( TF_DLL ) || defined ( TF_CLIENT_DLL ) |
|
if( TFGameRules()->IsMannVsMachineMode() ) |
|
{ |
|
ep.m_flVolume = params.volume; |
|
} |
|
else |
|
{ |
|
ep.m_flVolume = fvol; |
|
} |
|
#else |
|
ep.m_flVolume = fvol; |
|
#endif |
|
ep.m_SoundLevel = params.soundlevel; |
|
ep.m_nFlags = 0; |
|
ep.m_nPitch = params.pitch; |
|
ep.m_pOrigin = &vecOrigin; |
|
|
|
EmitSound( filter, entindex(), ep ); |
|
|
|
// Kyle says: ugggh. This function may as well be called "PerformPileOfDesperateGameSpecificFootstepHacks". |
|
OnEmitFootstepSound( params, vecOrigin, fvol ); |
|
} |
|
|
|
void CBasePlayer::UpdateButtonState( int nUserCmdButtonMask ) |
|
{ |
|
// Track button info so we can detect 'pressed' and 'released' buttons next frame |
|
m_afButtonLast = m_nButtons; |
|
|
|
// Get button states |
|
m_nButtons = nUserCmdButtonMask; |
|
int buttonsChanged = m_afButtonLast ^ m_nButtons; |
|
|
|
// Debounced button codes for pressed/released |
|
// UNDONE: Do we need auto-repeat? |
|
m_afButtonPressed = buttonsChanged & m_nButtons; // The changed ones still down are "pressed" |
|
m_afButtonReleased = buttonsChanged & (~m_nButtons); // The ones not down are "released" |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CBasePlayer::GetStepSoundVelocities( float *velwalk, float *velrun ) |
|
{ |
|
// UNDONE: need defined numbers for run, walk, crouch, crouch run velocities!!!! |
|
if ( ( GetFlags() & FL_DUCKING) || ( GetMoveType() == MOVETYPE_LADDER ) ) |
|
{ |
|
*velwalk = 60; // These constants should be based on cl_movespeedkey * cl_forwardspeed somehow |
|
*velrun = 80; |
|
} |
|
else |
|
{ |
|
*velwalk = 90; |
|
*velrun = 220; |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CBasePlayer::SetStepSoundTime( stepsoundtimes_t iStepSoundTime, bool bWalking ) |
|
{ |
|
switch ( iStepSoundTime ) |
|
{ |
|
case STEPSOUNDTIME_NORMAL: |
|
case STEPSOUNDTIME_WATER_FOOT: |
|
m_flStepSoundTime = bWalking ? 400 : 300; |
|
break; |
|
|
|
case STEPSOUNDTIME_ON_LADDER: |
|
m_flStepSoundTime = 350; |
|
break; |
|
|
|
case STEPSOUNDTIME_WATER_KNEE: |
|
m_flStepSoundTime = 600; |
|
break; |
|
|
|
default: |
|
Assert(0); |
|
break; |
|
} |
|
|
|
// UNDONE: need defined numbers for run, walk, crouch, crouch run velocities!!!! |
|
if ( ( GetFlags() & FL_DUCKING) || ( GetMoveType() == MOVETYPE_LADDER ) ) |
|
{ |
|
m_flStepSoundTime += 100; |
|
} |
|
} |
|
|
|
Vector CBasePlayer::Weapon_ShootPosition( ) |
|
{ |
|
return EyePosition(); |
|
} |
|
|
|
void CBasePlayer::SetAnimationExtension( const char *pExtension ) |
|
{ |
|
Q_strncpy( m_szAnimExtension, pExtension, sizeof(m_szAnimExtension) ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Set the weapon to switch to when the player uses the 'lastinv' command |
|
//----------------------------------------------------------------------------- |
|
void CBasePlayer::Weapon_SetLast( CBaseCombatWeapon *pWeapon ) |
|
{ |
|
m_hLastWeapon = pWeapon; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Override base class so player can reset autoaim |
|
// Input : |
|
// Output : |
|
//----------------------------------------------------------------------------- |
|
bool CBasePlayer::Weapon_Switch( CBaseCombatWeapon *pWeapon, int viewmodelindex /*=0*/ ) |
|
{ |
|
CBaseCombatWeapon *pLastWeapon = GetActiveWeapon(); |
|
|
|
if ( BaseClass::Weapon_Switch( pWeapon, viewmodelindex )) |
|
{ |
|
if ( pLastWeapon && Weapon_ShouldSetLast( pLastWeapon, GetActiveWeapon() ) ) |
|
{ |
|
Weapon_SetLast( pLastWeapon->GetLastWeapon() ); |
|
} |
|
|
|
CBaseViewModel *pViewModel = GetViewModel( viewmodelindex ); |
|
Assert( pViewModel ); |
|
if ( pViewModel ) |
|
pViewModel->RemoveEffects( EF_NODRAW ); |
|
ResetAutoaim( ); |
|
return true; |
|
} |
|
return false; |
|
} |
|
|
|
void CBasePlayer::SelectLastItem(void) |
|
{ |
|
if ( m_hLastWeapon.Get() == NULL ) |
|
return; |
|
|
|
if ( GetActiveWeapon() && !GetActiveWeapon()->CanHolster() ) |
|
return; |
|
|
|
SelectItem( m_hLastWeapon.Get()->GetClassname(), m_hLastWeapon.Get()->GetSubType() ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Abort any reloads we're in |
|
//----------------------------------------------------------------------------- |
|
void CBasePlayer::AbortReload( void ) |
|
{ |
|
if ( GetActiveWeapon() ) |
|
{ |
|
GetActiveWeapon()->AbortReload(); |
|
} |
|
} |
|
|
|
#if !defined( NO_ENTITY_PREDICTION ) |
|
void CBasePlayer::AddToPlayerSimulationList( CBaseEntity *other ) |
|
{ |
|
CHandle< CBaseEntity > h; |
|
h = other; |
|
// Already in list |
|
if ( m_SimulatedByThisPlayer.Find( h ) != m_SimulatedByThisPlayer.InvalidIndex() ) |
|
return; |
|
|
|
Assert( other->IsPlayerSimulated() ); |
|
|
|
m_SimulatedByThisPlayer.AddToTail( h ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Fixme, this should occur if the player fails to drive simulation |
|
// often enough!!! |
|
// Input : *other - |
|
//----------------------------------------------------------------------------- |
|
void CBasePlayer::RemoveFromPlayerSimulationList( CBaseEntity *other ) |
|
{ |
|
if ( !other ) |
|
return; |
|
|
|
Assert( other->IsPlayerSimulated() ); |
|
Assert( other->GetSimulatingPlayer() == this ); |
|
|
|
|
|
CHandle< CBaseEntity > h; |
|
h = other; |
|
|
|
m_SimulatedByThisPlayer.FindAndRemove( h ); |
|
} |
|
|
|
void CBasePlayer::SimulatePlayerSimulatedEntities( void ) |
|
{ |
|
int c = m_SimulatedByThisPlayer.Count(); |
|
int i; |
|
|
|
for ( i = c - 1; i >= 0; i-- ) |
|
{ |
|
CHandle< CBaseEntity > h; |
|
|
|
h = m_SimulatedByThisPlayer[ i ]; |
|
CBaseEntity *e = h; |
|
|
|
if ( !e || !e->IsPlayerSimulated() ) |
|
{ |
|
m_SimulatedByThisPlayer.Remove( i ); |
|
continue; |
|
} |
|
|
|
#if defined( CLIENT_DLL ) |
|
if ( e->IsClientCreated() && prediction->InPrediction() && !prediction->IsFirstTimePredicted() ) |
|
{ |
|
continue; |
|
} |
|
#endif |
|
Assert( e->IsPlayerSimulated() ); |
|
Assert( e->GetSimulatingPlayer() == this ); |
|
|
|
e->PhysicsSimulate(); |
|
} |
|
|
|
// Loop through all entities again, checking their untouch if flagged to do so |
|
c = m_SimulatedByThisPlayer.Count(); |
|
|
|
for ( i = c - 1; i >= 0; i-- ) |
|
{ |
|
CHandle< CBaseEntity > h; |
|
|
|
h = m_SimulatedByThisPlayer[ i ]; |
|
|
|
CBaseEntity *e = h; |
|
if ( !e || !e->IsPlayerSimulated() ) |
|
{ |
|
m_SimulatedByThisPlayer.Remove( i ); |
|
continue; |
|
} |
|
|
|
#if defined( CLIENT_DLL ) |
|
if ( e->IsClientCreated() && prediction->InPrediction() && !prediction->IsFirstTimePredicted() ) |
|
{ |
|
continue; |
|
} |
|
#endif |
|
|
|
Assert( e->IsPlayerSimulated() ); |
|
Assert( e->GetSimulatingPlayer() == this ); |
|
|
|
if ( !e->GetCheckUntouch() ) |
|
continue; |
|
|
|
e->PhysicsCheckForEntityUntouch(); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CBasePlayer::ClearPlayerSimulationList( void ) |
|
{ |
|
int c = m_SimulatedByThisPlayer.Size(); |
|
int i; |
|
|
|
for ( i = c - 1; i >= 0; i-- ) |
|
{ |
|
CHandle< CBaseEntity > h; |
|
|
|
h = m_SimulatedByThisPlayer[ i ]; |
|
CBaseEntity *e = h; |
|
if ( e ) |
|
{ |
|
e->UnsetPlayerSimulated(); |
|
} |
|
} |
|
|
|
m_SimulatedByThisPlayer.RemoveAll(); |
|
} |
|
#endif |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Return true if we should allow selection of the specified item |
|
//----------------------------------------------------------------------------- |
|
bool CBasePlayer::Weapon_ShouldSelectItem( CBaseCombatWeapon *pWeapon ) |
|
{ |
|
return ( pWeapon != GetActiveWeapon() ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CBasePlayer::SelectItem( const char *pstr, int iSubType ) |
|
{ |
|
if (!pstr) |
|
return; |
|
|
|
CBaseCombatWeapon *pItem = Weapon_OwnsThisType( pstr, iSubType ); |
|
|
|
if (!pItem) |
|
return; |
|
|
|
if( GetObserverMode() != OBS_MODE_NONE ) |
|
return;// Observers can't select things. |
|
|
|
if ( !Weapon_ShouldSelectItem( pItem ) ) |
|
return; |
|
|
|
// FIX, this needs to queue them up and delay |
|
// Make sure the current weapon can be holstered |
|
if ( GetActiveWeapon() ) |
|
{ |
|
if ( !GetActiveWeapon()->CanHolster() ) |
|
return; |
|
|
|
ResetAutoaim( ); |
|
} |
|
|
|
Weapon_Switch( pItem ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
ConVar sv_debug_player_use( "sv_debug_player_use", "0", FCVAR_REPLICATED, "Visualizes +use logic. Green cross=trace success, Red cross=trace too far, Green box=radius success" ); |
|
float IntervalDistance( float x, float x0, float x1 ) |
|
{ |
|
// swap so x0 < x1 |
|
if ( x0 > x1 ) |
|
{ |
|
float tmp = x0; |
|
x0 = x1; |
|
x1 = tmp; |
|
} |
|
|
|
if ( x < x0 ) |
|
return x0-x; |
|
else if ( x > x1 ) |
|
return x - x1; |
|
return 0; |
|
} |
|
|
|
CBaseEntity *CBasePlayer::FindUseEntity() |
|
{ |
|
Vector forward, up; |
|
EyeVectors( &forward, NULL, &up ); |
|
|
|
trace_t tr; |
|
// Search for objects in a sphere (tests for entities that are not solid, yet still useable) |
|
Vector searchCenter = EyePosition(); |
|
|
|
// NOTE: Some debris objects are useable too, so hit those as well |
|
// A button, etc. can be made out of clip brushes, make sure it's +useable via a traceline, too. |
|
int useableContents = MASK_SOLID | CONTENTS_DEBRIS | CONTENTS_PLAYERCLIP; |
|
|
|
#ifdef CSTRIKE_DLL |
|
useableContents = MASK_NPCSOLID_BRUSHONLY | MASK_OPAQUE_AND_NPCS; |
|
#endif |
|
|
|
#ifdef HL1_DLL |
|
useableContents = MASK_SOLID; |
|
#endif |
|
#ifndef CLIENT_DLL |
|
CBaseEntity *pFoundByTrace = NULL; |
|
#endif |
|
|
|
// UNDONE: Might be faster to just fold this range into the sphere query |
|
CBaseEntity *pObject = NULL; |
|
|
|
float nearestDist = FLT_MAX; |
|
// try the hit entity if there is one, or the ground entity if there isn't. |
|
CBaseEntity *pNearest = NULL; |
|
|
|
const int NUM_TANGENTS = 8; |
|
// trace a box at successive angles down |
|
// forward, 45 deg, 30 deg, 20 deg, 15 deg, 10 deg, -10, -15 |
|
const float tangents[NUM_TANGENTS] = { 0, 1, 0.57735026919f, 0.3639702342f, 0.267949192431f, 0.1763269807f, -0.1763269807f, -0.267949192431f }; |
|
for ( int i = 0; i < NUM_TANGENTS; i++ ) |
|
{ |
|
if ( i == 0 ) |
|
{ |
|
UTIL_TraceLine( searchCenter, searchCenter + forward * 1024, useableContents, this, COLLISION_GROUP_NONE, &tr ); |
|
} |
|
else |
|
{ |
|
Vector down = forward - tangents[i]*up; |
|
VectorNormalize(down); |
|
UTIL_TraceHull( searchCenter, searchCenter + down * 72, -Vector(16,16,16), Vector(16,16,16), useableContents, this, COLLISION_GROUP_NONE, &tr ); |
|
} |
|
pObject = tr.m_pEnt; |
|
|
|
#ifndef CLIENT_DLL |
|
pFoundByTrace = pObject; |
|
#endif |
|
bool bUsable = IsUseableEntity(pObject, 0); |
|
while ( pObject && !bUsable && pObject->GetMoveParent() ) |
|
{ |
|
pObject = pObject->GetMoveParent(); |
|
bUsable = IsUseableEntity(pObject, 0); |
|
} |
|
|
|
if ( bUsable ) |
|
{ |
|
Vector delta = tr.endpos - tr.startpos; |
|
float centerZ = CollisionProp()->WorldSpaceCenter().z; |
|
delta.z = IntervalDistance( tr.endpos.z, centerZ + CollisionProp()->OBBMins().z, centerZ + CollisionProp()->OBBMaxs().z ); |
|
float dist = delta.Length(); |
|
if ( dist < PLAYER_USE_RADIUS ) |
|
{ |
|
#ifndef CLIENT_DLL |
|
|
|
if ( sv_debug_player_use.GetBool() ) |
|
{ |
|
NDebugOverlay::Line( searchCenter, tr.endpos, 0, 255, 0, true, 30 ); |
|
NDebugOverlay::Cross3D( tr.endpos, 16, 0, 255, 0, true, 30 ); |
|
} |
|
|
|
if ( pObject->MyNPCPointer() && pObject->MyNPCPointer()->IsPlayerAlly( this ) ) |
|
{ |
|
// If about to select an NPC, do a more thorough check to ensure |
|
// that we're selecting the right one from a group. |
|
pObject = DoubleCheckUseNPC( pObject, searchCenter, forward ); |
|
} |
|
#endif |
|
if ( sv_debug_player_use.GetBool() ) |
|
{ |
|
Msg( "Trace using: %s\n", pObject ? pObject->GetDebugName() : "no usable entity found" ); |
|
} |
|
|
|
pNearest = pObject; |
|
|
|
// if this is directly under the cursor just return it now |
|
if ( i == 0 ) |
|
return pObject; |
|
} |
|
} |
|
} |
|
|
|
// check ground entity first |
|
// if you've got a useable ground entity, then shrink the cone of this search to 45 degrees |
|
// otherwise, search out in a 90 degree cone (hemisphere) |
|
if ( GetGroundEntity() && IsUseableEntity(GetGroundEntity(), FCAP_USE_ONGROUND) ) |
|
{ |
|
pNearest = GetGroundEntity(); |
|
} |
|
if ( pNearest ) |
|
{ |
|
// estimate nearest object by distance from the view vector |
|
Vector point; |
|
pNearest->CollisionProp()->CalcNearestPoint( searchCenter, &point ); |
|
nearestDist = CalcDistanceToLine( point, searchCenter, forward ); |
|
if ( sv_debug_player_use.GetBool() ) |
|
{ |
|
Msg("Trace found %s, dist %.2f\n", pNearest->GetClassname(), nearestDist ); |
|
} |
|
} |
|
|
|
for ( CEntitySphereQuery sphere( searchCenter, PLAYER_USE_RADIUS ); ( pObject = sphere.GetCurrentEntity() ) != NULL; sphere.NextEntity() ) |
|
{ |
|
if ( !pObject ) |
|
continue; |
|
|
|
if ( !IsUseableEntity( pObject, FCAP_USE_IN_RADIUS ) ) |
|
continue; |
|
|
|
// see if it's more roughly in front of the player than previous guess |
|
Vector point; |
|
pObject->CollisionProp()->CalcNearestPoint( searchCenter, &point ); |
|
|
|
Vector dir = point - searchCenter; |
|
VectorNormalize(dir); |
|
float dot = DotProduct( dir, forward ); |
|
|
|
// Need to be looking at the object more or less |
|
if ( dot < 0.8 ) |
|
continue; |
|
|
|
float dist = CalcDistanceToLine( point, searchCenter, forward ); |
|
|
|
if ( sv_debug_player_use.GetBool() ) |
|
{ |
|
Msg("Radius found %s, dist %.2f\n", pObject->GetClassname(), dist ); |
|
} |
|
|
|
if ( dist < nearestDist ) |
|
{ |
|
// Since this has purely been a radius search to this point, we now |
|
// make sure the object isn't behind glass or a grate. |
|
trace_t trCheckOccluded; |
|
UTIL_TraceLine( searchCenter, point, useableContents, this, COLLISION_GROUP_NONE, &trCheckOccluded ); |
|
|
|
if ( trCheckOccluded.fraction == 1.0 || trCheckOccluded.m_pEnt == pObject ) |
|
{ |
|
pNearest = pObject; |
|
nearestDist = dist; |
|
} |
|
} |
|
} |
|
|
|
#ifndef CLIENT_DLL |
|
if ( !pNearest ) |
|
{ |
|
// Haven't found anything near the player to use, nor any NPC's at distance. |
|
// Check to see if the player is trying to select an NPC through a rail, fence, or other 'see-though' volume. |
|
trace_t trAllies; |
|
UTIL_TraceLine( searchCenter, searchCenter + forward * PLAYER_USE_RADIUS, MASK_OPAQUE_AND_NPCS, this, COLLISION_GROUP_NONE, &trAllies ); |
|
|
|
if ( trAllies.m_pEnt && IsUseableEntity( trAllies.m_pEnt, 0 ) && trAllies.m_pEnt->MyNPCPointer() && trAllies.m_pEnt->MyNPCPointer()->IsPlayerAlly( this ) ) |
|
{ |
|
// This is an NPC, take it! |
|
pNearest = trAllies.m_pEnt; |
|
} |
|
} |
|
|
|
if ( pNearest && pNearest->MyNPCPointer() && pNearest->MyNPCPointer()->IsPlayerAlly( this ) ) |
|
{ |
|
pNearest = DoubleCheckUseNPC( pNearest, searchCenter, forward ); |
|
} |
|
|
|
if ( sv_debug_player_use.GetBool() ) |
|
{ |
|
if ( !pNearest ) |
|
{ |
|
NDebugOverlay::Line( searchCenter, tr.endpos, 255, 0, 0, true, 30 ); |
|
NDebugOverlay::Cross3D( tr.endpos, 16, 255, 0, 0, true, 30 ); |
|
} |
|
else if ( pNearest == pFoundByTrace ) |
|
{ |
|
NDebugOverlay::Line( searchCenter, tr.endpos, 0, 255, 0, true, 30 ); |
|
NDebugOverlay::Cross3D( tr.endpos, 16, 0, 255, 0, true, 30 ); |
|
} |
|
else |
|
{ |
|
NDebugOverlay::Box( pNearest->WorldSpaceCenter(), Vector(-8, -8, -8), Vector(8, 8, 8), 0, 255, 0, true, 30 ); |
|
} |
|
} |
|
#endif |
|
|
|
if ( sv_debug_player_use.GetBool() ) |
|
{ |
|
Msg( "Radial using: %s\n", pNearest ? pNearest->GetDebugName() : "no usable entity found" ); |
|
} |
|
|
|
return pNearest; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Handles USE keypress |
|
//----------------------------------------------------------------------------- |
|
void CBasePlayer::PlayerUse ( void ) |
|
{ |
|
#ifdef GAME_DLL |
|
// Was use pressed or released? |
|
if ( ! ((m_nButtons | m_afButtonPressed | m_afButtonReleased) & IN_USE) ) |
|
return; |
|
|
|
if ( IsObserver() ) |
|
{ |
|
// do special use operation in oberserver mode |
|
if ( m_afButtonPressed & IN_USE ) |
|
ObserverUse( true ); |
|
else if ( m_afButtonReleased & IN_USE ) |
|
ObserverUse( false ); |
|
|
|
return; |
|
} |
|
|
|
#if !defined(_XBOX) |
|
// push objects in turbo physics mode |
|
if ( (m_nButtons & IN_USE) && sv_turbophysics.GetBool() ) |
|
{ |
|
Vector forward, up; |
|
EyeVectors( &forward, NULL, &up ); |
|
|
|
trace_t tr; |
|
// Search for objects in a sphere (tests for entities that are not solid, yet still useable) |
|
Vector searchCenter = EyePosition(); |
|
|
|
CUsePushFilter filter; |
|
|
|
UTIL_TraceLine( searchCenter, searchCenter + forward * 96.0f, MASK_SOLID, &filter, &tr ); |
|
|
|
// try the hit entity if there is one, or the ground entity if there isn't. |
|
CBaseEntity *entity = tr.m_pEnt; |
|
|
|
if ( entity ) |
|
{ |
|
IPhysicsObject *pObj = entity->VPhysicsGetObject(); |
|
|
|
if ( pObj ) |
|
{ |
|
Vector vPushAway = (entity->WorldSpaceCenter() - WorldSpaceCenter()); |
|
vPushAway.z = 0; |
|
|
|
float flDist = VectorNormalize( vPushAway ); |
|
flDist = MAX( flDist, 1 ); |
|
|
|
float flForce = sv_pushaway_force.GetFloat() / flDist; |
|
flForce = MIN( flForce, sv_pushaway_max_force.GetFloat() ); |
|
|
|
pObj->ApplyForceOffset( vPushAway * flForce, WorldSpaceCenter() ); |
|
} |
|
} |
|
} |
|
#endif |
|
|
|
if ( m_afButtonPressed & IN_USE ) |
|
{ |
|
// Controlling some latched entity? |
|
if ( ClearUseEntity() ) |
|
{ |
|
return; |
|
} |
|
else |
|
{ |
|
if ( m_afPhysicsFlags & PFLAG_DIROVERRIDE ) |
|
{ |
|
m_afPhysicsFlags &= ~PFLAG_DIROVERRIDE; |
|
m_iTrain = TRAIN_NEW|TRAIN_OFF; |
|
return; |
|
} |
|
else |
|
{ // Start controlling the train! |
|
CBaseEntity *pTrain = GetGroundEntity(); |
|
if ( pTrain && !(m_nButtons & IN_JUMP) && (GetFlags() & FL_ONGROUND) && (pTrain->ObjectCaps() & FCAP_DIRECTIONAL_USE) && pTrain->OnControls(this) ) |
|
{ |
|
m_afPhysicsFlags |= PFLAG_DIROVERRIDE; |
|
m_iTrain = TrainSpeed(pTrain->m_flSpeed, ((CFuncTrackTrain*)pTrain)->GetMaxSpeed()); |
|
m_iTrain |= TRAIN_NEW; |
|
EmitSound( "Player.UseTrain" ); |
|
return; |
|
} |
|
} |
|
} |
|
} |
|
|
|
CBaseEntity *pUseEntity = FindUseEntity(); |
|
|
|
// Found an object |
|
if ( pUseEntity ) |
|
{ |
|
|
|
//!!!UNDONE: traceline here to prevent +USEing buttons through walls |
|
|
|
int caps = pUseEntity->ObjectCaps(); |
|
variant_t emptyVariant; |
|
if ( ( (m_nButtons & IN_USE) && (caps & FCAP_CONTINUOUS_USE) ) || ( (m_afButtonPressed & IN_USE) && (caps & (FCAP_IMPULSE_USE|FCAP_ONOFF_USE)) ) ) |
|
{ |
|
if ( caps & FCAP_CONTINUOUS_USE ) |
|
{ |
|
m_afPhysicsFlags |= PFLAG_USING; |
|
} |
|
|
|
if ( pUseEntity->ObjectCaps() & FCAP_ONOFF_USE ) |
|
{ |
|
pUseEntity->AcceptInput( "Use", this, this, emptyVariant, USE_ON ); |
|
} |
|
else |
|
{ |
|
pUseEntity->AcceptInput( "Use", this, this, emptyVariant, USE_TOGGLE ); |
|
} |
|
} |
|
// UNDONE: Send different USE codes for ON/OFF. Cache last ONOFF_USE object to send 'off' if you turn away |
|
else if ( (m_afButtonReleased & IN_USE) && (pUseEntity->ObjectCaps() & FCAP_ONOFF_USE) ) // BUGBUG This is an "off" use |
|
{ |
|
pUseEntity->AcceptInput( "Use", this, this, emptyVariant, USE_OFF ); |
|
} |
|
} |
|
else if ( m_afButtonPressed & IN_USE ) |
|
{ |
|
PlayUseDenySound(); |
|
} |
|
#endif |
|
} |
|
|
|
ConVar sv_suppress_viewpunch( "sv_suppress_viewpunch", "0", FCVAR_REPLICATED | FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY ); |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CBasePlayer::ViewPunch( const QAngle &angleOffset ) |
|
{ |
|
//See if we're suppressing the view punching |
|
if ( sv_suppress_viewpunch.GetBool() ) |
|
return; |
|
|
|
// We don't allow view kicks in the vehicle |
|
if ( IsInAVehicle() ) |
|
return; |
|
|
|
m_Local.m_vecPunchAngleVel += angleOffset * 20; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CBasePlayer::ViewPunchReset( float tolerance ) |
|
{ |
|
if ( tolerance != 0 ) |
|
{ |
|
tolerance *= tolerance; // square |
|
float check = m_Local.m_vecPunchAngleVel->LengthSqr() + m_Local.m_vecPunchAngle->LengthSqr(); |
|
if ( check > tolerance ) |
|
return; |
|
} |
|
m_Local.m_vecPunchAngle = vec3_angle; |
|
m_Local.m_vecPunchAngleVel = vec3_angle; |
|
} |
|
|
|
#if defined( CLIENT_DLL ) |
|
|
|
#include "iviewrender.h" |
|
#include "ivieweffects.h" |
|
|
|
#endif |
|
|
|
static ConVar smoothstairs( "smoothstairs", "1", FCVAR_REPLICATED, "Smooth player eye z coordinate when traversing stairs." ); |
|
|
|
//----------------------------------------------------------------------------- |
|
// Handle view smoothing when going up or down stairs |
|
//----------------------------------------------------------------------------- |
|
void CBasePlayer::SmoothViewOnStairs( Vector& eyeOrigin ) |
|
{ |
|
CBaseEntity *pGroundEntity = GetGroundEntity(); |
|
float flCurrentPlayerZ = GetLocalOrigin().z; |
|
float flCurrentPlayerViewOffsetZ = GetViewOffset().z; |
|
|
|
// Smooth out stair step ups |
|
// NOTE: Don't want to do this when the ground entity is moving the player |
|
if ( ( pGroundEntity != NULL && pGroundEntity->GetMoveType() == MOVETYPE_NONE ) && ( flCurrentPlayerZ != m_flOldPlayerZ ) && smoothstairs.GetBool() && |
|
m_flOldPlayerViewOffsetZ == flCurrentPlayerViewOffsetZ ) |
|
{ |
|
int dir = ( flCurrentPlayerZ > m_flOldPlayerZ ) ? 1 : -1; |
|
|
|
float steptime = gpGlobals->frametime; |
|
if (steptime < 0) |
|
{ |
|
steptime = 0; |
|
} |
|
|
|
m_flOldPlayerZ += steptime * 150 * dir; |
|
|
|
const float stepSize = 18.0f; |
|
|
|
if ( dir > 0 ) |
|
{ |
|
if (m_flOldPlayerZ > flCurrentPlayerZ) |
|
{ |
|
m_flOldPlayerZ = flCurrentPlayerZ; |
|
} |
|
if (flCurrentPlayerZ - m_flOldPlayerZ > stepSize) |
|
{ |
|
m_flOldPlayerZ = flCurrentPlayerZ - stepSize; |
|
} |
|
} |
|
else |
|
{ |
|
if (m_flOldPlayerZ < flCurrentPlayerZ) |
|
{ |
|
m_flOldPlayerZ = flCurrentPlayerZ; |
|
} |
|
if (flCurrentPlayerZ - m_flOldPlayerZ < -stepSize) |
|
{ |
|
m_flOldPlayerZ = flCurrentPlayerZ + stepSize; |
|
} |
|
} |
|
|
|
eyeOrigin[2] += m_flOldPlayerZ - flCurrentPlayerZ; |
|
} |
|
else |
|
{ |
|
m_flOldPlayerZ = flCurrentPlayerZ; |
|
m_flOldPlayerViewOffsetZ = flCurrentPlayerViewOffsetZ; |
|
} |
|
} |
|
|
|
static bool IsWaterContents( int contents ) |
|
{ |
|
if ( contents & MASK_WATER ) |
|
return true; |
|
|
|
// if ( contents & CONTENTS_TESTFOGVOLUME ) |
|
// return true; |
|
|
|
return false; |
|
} |
|
|
|
void CBasePlayer::ResetObserverMode() |
|
{ |
|
|
|
m_hObserverTarget.Set( 0 ); |
|
m_iObserverMode = (int)OBS_MODE_NONE; |
|
|
|
#ifndef CLIENT_DLL |
|
m_iObserverLastMode = OBS_MODE_ROAMING; |
|
m_bForcedObserverMode = false; |
|
m_afPhysicsFlags &= ~PFLAG_OBSERVER; |
|
#endif |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : eyeOrigin - |
|
// eyeAngles - |
|
// zNear - |
|
// zFar - |
|
// fov - |
|
//----------------------------------------------------------------------------- |
|
void CBasePlayer::CalcView( Vector &eyeOrigin, QAngle &eyeAngles, float &zNear, float &zFar, float &fov ) |
|
{ |
|
#if defined( CLIENT_DLL ) |
|
IClientVehicle *pVehicle; |
|
#else |
|
IServerVehicle *pVehicle; |
|
#endif |
|
pVehicle = GetVehicle(); |
|
|
|
if ( !pVehicle ) |
|
{ |
|
#if defined( CLIENT_DLL ) |
|
if( UseVR() ) |
|
g_ClientVirtualReality.CancelTorsoTransformOverride(); |
|
#endif |
|
|
|
if ( IsObserver() ) |
|
{ |
|
CalcObserverView( eyeOrigin, eyeAngles, fov ); |
|
} |
|
else |
|
{ |
|
CalcPlayerView( eyeOrigin, eyeAngles, fov ); |
|
} |
|
} |
|
else |
|
{ |
|
CalcVehicleView( pVehicle, eyeOrigin, eyeAngles, zNear, zFar, fov ); |
|
} |
|
// NVNT update fov on the haptics dll for input scaling. |
|
#if defined( CLIENT_DLL ) |
|
if(IsLocalPlayer() && haptics) |
|
haptics->UpdatePlayerFOV(fov); |
|
#endif |
|
} |
|
|
|
|
|
void CBasePlayer::CalcViewModelView( const Vector& eyeOrigin, const QAngle& eyeAngles) |
|
{ |
|
for ( int i = 0; i < MAX_VIEWMODELS; i++ ) |
|
{ |
|
CBaseViewModel *vm = GetViewModel( i ); |
|
if ( !vm ) |
|
continue; |
|
|
|
vm->CalcViewModelView( this, eyeOrigin, eyeAngles ); |
|
} |
|
} |
|
|
|
void CBasePlayer::CalcPlayerView( Vector& eyeOrigin, QAngle& eyeAngles, float& fov ) |
|
{ |
|
#if defined( CLIENT_DLL ) |
|
if ( !prediction->InPrediction() ) |
|
{ |
|
// FIXME: Move into prediction |
|
view->DriftPitch(); |
|
} |
|
#endif |
|
|
|
VectorCopy( EyePosition(), eyeOrigin ); |
|
#ifdef SIXENSE |
|
if ( g_pSixenseInput->IsEnabled() ) |
|
{ |
|
VectorCopy( EyeAngles() + GetEyeAngleOffset(), eyeAngles ); |
|
} |
|
else |
|
{ |
|
VectorCopy( EyeAngles(), eyeAngles ); |
|
} |
|
#else |
|
VectorCopy( EyeAngles(), eyeAngles ); |
|
#endif |
|
|
|
#if defined( CLIENT_DLL ) |
|
if ( !prediction->InPrediction() ) |
|
#endif |
|
{ |
|
SmoothViewOnStairs( eyeOrigin ); |
|
} |
|
|
|
// Snack off the origin before bob + water offset are applied |
|
Vector vecBaseEyePosition = eyeOrigin; |
|
|
|
CalcViewRoll( eyeAngles ); |
|
|
|
// Apply punch angle |
|
VectorAdd( eyeAngles, m_Local.m_vecPunchAngle, eyeAngles ); |
|
|
|
#if defined( CLIENT_DLL ) |
|
if ( !prediction->InPrediction() ) |
|
{ |
|
// Shake it up baby! |
|
vieweffects->CalcShake(); |
|
vieweffects->ApplyShake( eyeOrigin, eyeAngles, 1.0 ); |
|
} |
|
#endif |
|
|
|
#if defined( CLIENT_DLL ) |
|
// Apply a smoothing offset to smooth out prediction errors. |
|
Vector vSmoothOffset; |
|
GetPredictionErrorSmoothingVector( vSmoothOffset ); |
|
eyeOrigin += vSmoothOffset; |
|
m_flObserverChaseDistance = 0.0; |
|
#endif |
|
|
|
// calc current FOV |
|
fov = GetFOV(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: The main view setup function for vehicles |
|
//----------------------------------------------------------------------------- |
|
void CBasePlayer::CalcVehicleView( |
|
#if defined( CLIENT_DLL ) |
|
IClientVehicle *pVehicle, |
|
#else |
|
IServerVehicle *pVehicle, |
|
#endif |
|
Vector& eyeOrigin, QAngle& eyeAngles, |
|
float& zNear, float& zFar, float& fov ) |
|
{ |
|
Assert( pVehicle ); |
|
|
|
// Start with our base origin and angles |
|
CacheVehicleView(); |
|
eyeOrigin = m_vecVehicleViewOrigin; |
|
eyeAngles = m_vecVehicleViewAngles; |
|
|
|
#if defined( CLIENT_DLL ) |
|
|
|
fov = GetFOV(); |
|
|
|
// Allows the vehicle to change the clip planes |
|
pVehicle->GetVehicleClipPlanes( zNear, zFar ); |
|
#endif |
|
|
|
// Snack off the origin before bob + water offset are applied |
|
Vector vecBaseEyePosition = eyeOrigin; |
|
|
|
CalcViewRoll( eyeAngles ); |
|
|
|
// Apply punch angle |
|
VectorAdd( eyeAngles, m_Local.m_vecPunchAngle, eyeAngles ); |
|
|
|
#if defined( CLIENT_DLL ) |
|
if ( !prediction->InPrediction() ) |
|
{ |
|
// Shake it up baby! |
|
vieweffects->CalcShake(); |
|
vieweffects->ApplyShake( eyeOrigin, eyeAngles, 1.0 ); |
|
} |
|
#endif |
|
|
|
} |
|
|
|
|
|
void CBasePlayer::CalcObserverView( Vector& eyeOrigin, QAngle& eyeAngles, float& fov ) |
|
{ |
|
#if defined( CLIENT_DLL ) |
|
switch ( GetObserverMode() ) |
|
{ |
|
|
|
case OBS_MODE_DEATHCAM : CalcDeathCamView( eyeOrigin, eyeAngles, fov ); |
|
break; |
|
|
|
case OBS_MODE_ROAMING : // just copy current position without view offset |
|
case OBS_MODE_FIXED : CalcRoamingView( eyeOrigin, eyeAngles, fov ); |
|
break; |
|
|
|
case OBS_MODE_IN_EYE : CalcInEyeCamView( eyeOrigin, eyeAngles, fov ); |
|
break; |
|
|
|
case OBS_MODE_CHASE : CalcChaseCamView( eyeOrigin, eyeAngles, fov ); |
|
break; |
|
|
|
case OBS_MODE_FREEZECAM : CalcFreezeCamView( eyeOrigin, eyeAngles, fov ); |
|
break; |
|
} |
|
#else |
|
// on server just copy target postions, final view positions will be calculated on client |
|
VectorCopy( EyePosition(), eyeOrigin ); |
|
VectorCopy( EyeAngles(), eyeAngles ); |
|
#endif |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Compute roll angle for a particular lateral velocity |
|
// Input : angles - |
|
// velocity - |
|
// rollangle - |
|
// rollspeed - |
|
// Output : float CViewRender::CalcRoll |
|
//----------------------------------------------------------------------------- |
|
float CBasePlayer::CalcRoll (const QAngle& angles, const Vector& velocity, float rollangle, float rollspeed) |
|
{ |
|
float sign; |
|
float side; |
|
float value; |
|
|
|
Vector forward, right, up; |
|
|
|
AngleVectors (angles, &forward, &right, &up); |
|
|
|
// Get amount of lateral movement |
|
side = DotProduct( velocity, right ); |
|
// Right or left side? |
|
sign = side < 0 ? -1 : 1; |
|
side = fabs(side); |
|
|
|
value = rollangle; |
|
// Hit 100% of rollangle at rollspeed. Below that get linear approx. |
|
if ( side < rollspeed ) |
|
{ |
|
side = side * value / rollspeed; |
|
} |
|
else |
|
{ |
|
side = value; |
|
} |
|
|
|
// Scale by right/left sign |
|
return side*sign; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Determine view roll, including data kick |
|
//----------------------------------------------------------------------------- |
|
void CBasePlayer::CalcViewRoll( QAngle& eyeAngles ) |
|
{ |
|
if ( GetMoveType() == MOVETYPE_NOCLIP ) |
|
return; |
|
|
|
float side = CalcRoll( GetAbsAngles(), GetAbsVelocity(), sv_rollangle.GetFloat(), sv_rollspeed.GetFloat() ); |
|
eyeAngles[ROLL] += side; |
|
} |
|
|
|
|
|
void CBasePlayer::DoMuzzleFlash() |
|
{ |
|
for ( int i = 0; i < MAX_VIEWMODELS; i++ ) |
|
{ |
|
CBaseViewModel *vm = GetViewModel( i ); |
|
if ( !vm ) |
|
continue; |
|
|
|
vm->DoMuzzleFlash(); |
|
} |
|
|
|
BaseClass::DoMuzzleFlash(); |
|
} |
|
|
|
|
|
float CBasePlayer::GetFOVDistanceAdjustFactor() |
|
{ |
|
float defaultFOV = (float)GetDefaultFOV(); |
|
float localFOV = (float)GetFOV(); |
|
|
|
if ( localFOV == defaultFOV || defaultFOV < 0.001f ) |
|
{ |
|
return 1.0f; |
|
} |
|
|
|
// If FOV is lower, then we're "zoomed" in and this will give a factor < 1 so apparent LOD distances can be |
|
// shorted accordingly |
|
return localFOV / defaultFOV; |
|
|
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : &vecTracerSrc - |
|
// &tr - |
|
// iTracerType - |
|
//----------------------------------------------------------------------------- |
|
void CBasePlayer::MakeTracer( const Vector &vecTracerSrc, const trace_t &tr, int iTracerType ) |
|
{ |
|
if ( GetActiveWeapon() ) |
|
{ |
|
GetActiveWeapon()->MakeTracer( vecTracerSrc, tr, iTracerType ); |
|
return; |
|
} |
|
|
|
BaseClass::MakeTracer( vecTracerSrc, tr, iTracerType ); |
|
} |
|
|
|
|
|
void CBasePlayer::SharedSpawn() |
|
{ |
|
SetMoveType( MOVETYPE_WALK ); |
|
SetSolid( SOLID_BBOX ); |
|
AddSolidFlags( FSOLID_NOT_STANDABLE ); |
|
SetFriction( 1.0f ); |
|
|
|
pl.deadflag = false; |
|
m_lifeState = LIFE_ALIVE; |
|
m_iHealth = 100; |
|
m_takedamage = DAMAGE_YES; |
|
|
|
m_Local.m_bDrawViewmodel = true; |
|
m_Local.m_flStepSize = sv_stepsize.GetFloat(); |
|
m_Local.m_bAllowAutoMovement = true; |
|
|
|
m_nRenderFX = kRenderFxNone; |
|
m_flNextAttack = gpGlobals->curtime; |
|
m_flMaxspeed = 0.0f; |
|
|
|
MDLCACHE_CRITICAL_SECTION(); |
|
SetSequence( SelectWeightedSequence( ACT_IDLE ) ); |
|
|
|
if ( GetFlags() & FL_DUCKING ) |
|
SetCollisionBounds( VEC_DUCK_HULL_MIN, VEC_DUCK_HULL_MAX ); |
|
else |
|
SetCollisionBounds( VEC_HULL_MIN, VEC_HULL_MAX ); |
|
|
|
// dont let uninitialized value here hurt the player |
|
m_Local.m_flFallVelocity = 0; |
|
|
|
SetBloodColor( BLOOD_COLOR_RED ); |
|
// NVNT inform haptic dll we have just spawned local player |
|
#ifdef CLIENT_DLL |
|
if(IsLocalPlayer() &&haptics) |
|
haptics->LocalPlayerReset(); |
|
#endif |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Output : int |
|
//----------------------------------------------------------------------------- |
|
int CBasePlayer::GetDefaultFOV( void ) const |
|
{ |
|
#if defined( CLIENT_DLL ) |
|
if ( GetObserverMode() == OBS_MODE_IN_EYE ) |
|
{ |
|
C_BasePlayer *pTargetPlayer = dynamic_cast<C_BasePlayer*>( GetObserverTarget() ); |
|
|
|
if ( pTargetPlayer && !pTargetPlayer->IsObserver() ) |
|
{ |
|
return pTargetPlayer->GetDefaultFOV(); |
|
} |
|
} |
|
#endif |
|
|
|
int iFOV = ( m_iDefaultFOV == 0 ) ? g_pGameRules->DefaultFOV() : m_iDefaultFOV; |
|
if ( iFOV > MAX_FOV ) |
|
iFOV = MAX_FOV; |
|
|
|
return iFOV; |
|
} |
|
|
|
void CBasePlayer::AvoidPhysicsProps( CUserCmd *pCmd ) |
|
{ |
|
#ifndef _XBOX |
|
// Don't avoid if noclipping or in movetype none |
|
switch ( GetMoveType() ) |
|
{ |
|
case MOVETYPE_NOCLIP: |
|
case MOVETYPE_NONE: |
|
case MOVETYPE_OBSERVER: |
|
return; |
|
default: |
|
break; |
|
} |
|
|
|
if ( GetObserverMode() != OBS_MODE_NONE || !IsAlive() ) |
|
return; |
|
|
|
AvoidPushawayProps( this, pCmd ); |
|
#endif |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Output : const char |
|
//----------------------------------------------------------------------------- |
|
const char *CBasePlayer::GetTracerType( void ) |
|
{ |
|
if ( GetActiveWeapon() ) |
|
{ |
|
return GetActiveWeapon()->GetTracerType(); |
|
} |
|
|
|
return BaseClass::GetTracerType(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CBasePlayer::ClearZoomOwner( void ) |
|
{ |
|
m_hZoomOwner = NULL; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Sets the FOV of the client, doing interpolation between old and new if requested |
|
// Input : FOV - New FOV |
|
// zoomRate - Amount of time (in seconds) to move between old and new FOV |
|
//----------------------------------------------------------------------------- |
|
bool CBasePlayer::SetFOV( CBaseEntity *pRequester, int FOV, float zoomRate, int iZoomStart /* = 0 */ ) |
|
{ |
|
//NOTENOTE: You MUST specify who is requesting the zoom change |
|
assert( pRequester != NULL ); |
|
if ( pRequester == NULL ) |
|
return false; |
|
|
|
// If we already have an owner, we only allow requests from that owner |
|
if ( ( m_hZoomOwner.Get() != NULL ) && ( m_hZoomOwner.Get() != pRequester ) ) |
|
{ |
|
#ifdef GAME_DLL |
|
if ( CanOverrideEnvZoomOwner( m_hZoomOwner.Get() ) == false ) |
|
#endif |
|
return false; |
|
} |
|
else |
|
{ |
|
//FIXME: Maybe do this is as an accessor instead |
|
if ( FOV == 0 ) |
|
{ |
|
m_hZoomOwner = NULL; |
|
} |
|
else |
|
{ |
|
m_hZoomOwner = pRequester; |
|
} |
|
} |
|
|
|
// Setup our FOV and our scaling time |
|
|
|
if ( iZoomStart > 0 ) |
|
{ |
|
m_iFOVStart = iZoomStart; |
|
} |
|
else |
|
{ |
|
m_iFOVStart = GetFOV(); |
|
} |
|
|
|
m_flFOVTime = gpGlobals->curtime; |
|
m_iFOV = FOV; |
|
|
|
m_Local.m_flFOVRate = zoomRate; |
|
|
|
return true; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CBasePlayer::UpdateUnderwaterState( void ) |
|
{ |
|
if ( GetWaterLevel() == WL_Eyes ) |
|
{ |
|
if ( IsPlayerUnderwater() == false ) |
|
{ |
|
SetPlayerUnderwater( true ); |
|
} |
|
return; |
|
} |
|
|
|
if ( IsPlayerUnderwater() ) |
|
{ |
|
SetPlayerUnderwater( false ); |
|
} |
|
|
|
if ( GetWaterLevel() == 0 ) |
|
{ |
|
if ( GetFlags() & FL_INWATER ) |
|
{ |
|
#ifndef CLIENT_DLL |
|
if ( m_iHealth > 0 && IsAlive() ) |
|
{ |
|
EmitSound( "Player.Wade" ); |
|
} |
|
#endif |
|
RemoveFlag( FL_INWATER ); |
|
} |
|
} |
|
else if ( !(GetFlags() & FL_INWATER) ) |
|
{ |
|
#ifndef CLIENT_DLL |
|
// player enter water sound |
|
if (GetWaterType() == CONTENTS_WATER) |
|
{ |
|
EmitSound( "Player.Wade" ); |
|
} |
|
#endif |
|
|
|
AddFlag( FL_INWATER ); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: data accessor |
|
// ensure that for every emitsound there is a matching stopsound |
|
//----------------------------------------------------------------------------- |
|
void CBasePlayer::SetPlayerUnderwater( bool state ) |
|
{ |
|
if ( m_bPlayerUnderwater != state ) |
|
{ |
|
#if defined( WIN32 ) && !defined( _X360 ) |
|
// NVNT turn on haptic drag when underwater |
|
if(state) |
|
HapticSetDrag(this,1); |
|
else |
|
HapticSetDrag(this,0); |
|
#endif |
|
m_bPlayerUnderwater = state; |
|
|
|
#ifdef CLIENT_DLL |
|
if ( state ) |
|
EmitSound( "Player.AmbientUnderWater" ); |
|
else |
|
StopSound( "Player.AmbientUnderWater" ); |
|
#endif |
|
} |
|
} |
|
|
|
|
|
void CBasePlayer::SetPreviouslyPredictedOrigin( const Vector &vecAbsOrigin ) |
|
{ |
|
m_vecPreviouslyPredictedOrigin = vecAbsOrigin; |
|
} |
|
|
|
const Vector &CBasePlayer::GetPreviouslyPredictedOrigin() const |
|
{ |
|
return m_vecPreviouslyPredictedOrigin; |
|
} |
|
|
|
bool fogparams_t::operator !=( const fogparams_t& other ) const |
|
{ |
|
if ( this->enable != other.enable || |
|
this->blend != other.blend || |
|
!VectorsAreEqual(this->dirPrimary, other.dirPrimary, 0.01f ) || |
|
this->colorPrimary.Get() != other.colorPrimary.Get() || |
|
this->colorSecondary.Get() != other.colorSecondary.Get() || |
|
this->start != other.start || |
|
this->end != other.end || |
|
this->farz != other.farz || |
|
this->maxdensity != other.maxdensity || |
|
this->colorPrimaryLerpTo.Get() != other.colorPrimaryLerpTo.Get() || |
|
this->colorSecondaryLerpTo.Get() != other.colorSecondaryLerpTo.Get() || |
|
this->startLerpTo != other.startLerpTo || |
|
this->endLerpTo != other.endLerpTo || |
|
this->lerptime != other.lerptime || |
|
this->duration != other.duration ) |
|
return true; |
|
|
|
return false; |
|
} |
|
|
|
|