|
|
//========= 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 || GetObserverMode() == OBS_MODE_POI ) { 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 { const char *pSoundName = MoveHelper()->GetSurfaceProps()->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() && !pItem->ForceWeaponSwitch() ) 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_POI : // PASSTIME
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:
//-----------------------------------------------------------------------------
bool CBasePlayer::IsLerpingFOV( void ) const { // If it's immediate, just do it
if (m_Local.m_flFOVRate == 0.0f) return false;
float deltaTime = (float)(gpGlobals->curtime - m_flFOVTime) / m_Local.m_flFOVRate; return deltaTime < 1.f; }
//-----------------------------------------------------------------------------
// 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; }
|