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

2085 lines
52 KiB
C++

//========= 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;
}