|
|
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "gamemovement.h"
#include "in_buttons.h"
#include <stdarg.h>
#include "movevars_shared.h"
#include "engine/IEngineTrace.h"
#include "SoundEmitterSystem/isoundemittersystembase.h"
#include "decals.h"
#include "coordsize.h"
#include "rumble_shared.h"
#if defined(HL2_DLL) || defined(HL2_CLIENT_DLL)
#include "hl_movedata.h"
#endif
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
#define STOP_EPSILON 0.1
#define MAX_CLIP_PLANES 5
#include "filesystem.h"
#include <stdarg.h>
extern IFileSystem *filesystem;
#ifndef CLIENT_DLL
#include "env_player_surface_trigger.h"
static ConVar dispcoll_drawplane( "dispcoll_drawplane", "0" ); #endif
// tickcount currently isn't set during prediction, although gpGlobals->curtime and
// gpGlobals->frametime are. We should probably set tickcount (to player->m_nTickBase),
// but we're REALLY close to shipping, so we can change that later and people can use
// player->CurrentCommandNumber() in the meantime.
#define tickcount USE_PLAYER_CURRENT_COMMAND_NUMBER__INSTEAD_OF_TICKCOUNT
#if defined( HL2_DLL )
ConVar xc_uncrouch_on_jump( "xc_uncrouch_on_jump", "1", FCVAR_ARCHIVE, "Uncrouch when jump occurs" ); #endif
#if defined( HL2_DLL ) || defined( HL2_CLIENT_DLL )
ConVar player_limit_jump_speed( "player_limit_jump_speed", "1", FCVAR_REPLICATED ); #endif
// option_duck_method is a carrier convar. Its sole purpose is to serve an easy-to-flip
// convar which is ONLY set by the X360 controller menu to tell us which way to bind the
// duck controls. Its value is meaningless anytime we don't have the options window open.
ConVar option_duck_method("option_duck_method", "1", FCVAR_REPLICATED|FCVAR_ARCHIVE );// 0 = HOLD to duck, 1 = Duck is a toggle
#ifdef STAGING_ONLY
#ifdef CLIENT_DLL
ConVar debug_latch_reset_onduck( "debug_latch_reset_onduck", "1", FCVAR_CHEAT ); #endif
#endif
// [MD] I'll remove this eventually. For now, I want the ability to A/B the optimizations.
bool g_bMovementOptimizations = true;
// Roughly how often we want to update the info about the ground surface we're on.
// We don't need to do this very often.
#define CATEGORIZE_GROUND_SURFACE_INTERVAL 0.3f
#define CATEGORIZE_GROUND_SURFACE_TICK_INTERVAL ( (int)( CATEGORIZE_GROUND_SURFACE_INTERVAL / TICK_INTERVAL ) )
#define CHECK_STUCK_INTERVAL 1.0f
#define CHECK_STUCK_TICK_INTERVAL ( (int)( CHECK_STUCK_INTERVAL / TICK_INTERVAL ) )
#define CHECK_STUCK_INTERVAL_SP 0.2f
#define CHECK_STUCK_TICK_INTERVAL_SP ( (int)( CHECK_STUCK_INTERVAL_SP / TICK_INTERVAL ) )
#define CHECK_LADDER_INTERVAL 0.2f
#define CHECK_LADDER_TICK_INTERVAL ( (int)( CHECK_LADDER_INTERVAL / TICK_INTERVAL ) )
#define NUM_CROUCH_HINTS 3
extern IGameMovement *g_pGameMovement;
#if defined( PLAYER_GETTING_STUCK_TESTING )
// If you ever get stuck walking around, then you can run this code to find the code which would leave the player in a bad spot
void CMoveData::SetAbsOrigin( const Vector &vec ) { CGameMovement *gm = dynamic_cast< CGameMovement * >( g_pGameMovement ); if ( gm && gm->GetMoveData() && gm->player && gm->player->entindex() == 1 && gm->player->GetMoveType() == MOVETYPE_WALK ) { trace_t pm; gm->TracePlayerBBox( vec, vec, gm->PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, pm ); if ( pm.startsolid || pm.allsolid || pm.fraction != 1.0f ) { Msg( "Player will become stuck at %f %f %f\n", VectorExpand( vec ) ); } }
m_vecAbsOrigin = vec; }
#endif
// See shareddefs.h
#if PREDICTION_ERROR_CHECK_LEVEL > 0
static ConVar diffcheck( "diffcheck", "0", FCVAR_REPLICATED );
class IDiffMgr { public: virtual void StartCommand( bool bServer, int nCommandNumber ) = 0; virtual void AddToDiff( bool bServer, int nCommandNumber, char const *string ) = 0; virtual void Validate( bool bServer, int nCommandNumber ) = 0; };
static IDiffMgr *g_pDiffMgr = NULL;
class CDiffStr { public: CDiffStr() { m_str[ 0 ] = 0; }
CDiffStr( char const *str ) { Q_strncpy( m_str, str, sizeof( m_str ) ); }
CDiffStr( const CDiffStr &src ) { Q_strncpy( m_str, src.m_str, sizeof( m_str ) ); }
char const *String() { return m_str; } private:
char m_str[ 128 ]; };
// Per tick data
class CDiffInfo { public: CDiffInfo() : m_nCommandNumber( 0 ) {} CDiffInfo( const CDiffInfo& src ) { m_nCommandNumber = src.m_nCommandNumber; for ( int i = 0; i < src.m_Lines.Count(); ++i ) { m_Lines.AddToTail( src.m_Lines[ i ] ); } }
static bool Less( const CDiffInfo& lhs, const CDiffInfo& rhs ) { return lhs.m_nCommandNumber < rhs.m_nCommandNumber; } int m_nCommandNumber; CUtlVector< CDiffStr > m_Lines; bool m_bChecked; };
class CDiffManager : public IDiffMgr { public: CDiffManager() : m_Client( 0, 0, CDiffInfo::Less ), m_Server( 0, 0, CDiffInfo::Less ), m_flLastSpew( -1.0f ) { g_pDiffMgr = this; }
virtual void StartCommand( bool bServer, int nCommandNumber ) { #if defined( CLIENT_DLL )
if ( !diffcheck.GetInt() ) return;
g_pDiffMgr = reinterpret_cast< IDiffMgr * >( diffcheck.GetInt() ); g_pDiffMgr->StartCommand( bServer, nCommandNumber ); return; #endif
// Msg( "%s Startcommand %d\n", bServer ? "sv" : "cl", nCommandNumber );
diffcheck.SetValue( reinterpret_cast< int >( this ) );
Assert( CBaseEntity::IsServer() );
CUtlRBTree< CDiffInfo, int >& rb = bServer ? m_Server : m_Client;
CDiffInfo search; search.m_nCommandNumber = nCommandNumber; int idx = rb.Find( search ); if ( idx == rb.InvalidIndex() ) { idx = rb.Insert( search ); }
CDiffInfo *slot = &rb[ idx ]; slot->m_Lines.RemoveAll(); }
virtual void AddToDiff( bool bServer, int nCommandNumber, char const *string ) { #if defined( CLIENT_DLL )
if ( !diffcheck.GetInt() ) return;
g_pDiffMgr = reinterpret_cast< IDiffMgr * >( diffcheck.GetInt() ); g_pDiffMgr->AddToDiff( bServer, nCommandNumber, string ); return; #endif
Assert( CBaseEntity::IsServer() );
// Msg( "%s Add %d %s\n", bServer ? "sv" : "cl", nCommandNumber, string );
CUtlRBTree< CDiffInfo, int >& rb = bServer ? m_Server : m_Client;
CDiffInfo search; search.m_nCommandNumber = nCommandNumber; int idx = rb.Find( search ); if ( idx == rb.InvalidIndex() ) { Assert( 0 ); idx = rb.Insert( search ); }
CDiffInfo *slot = &rb[ idx ]; CDiffStr line( string ); slot->m_Lines.AddToTail( line ); }
enum EMismatched { DIFFCHECK_NOTREADY = 0, DIFFCHECK_MATCHED, DIFFCHECK_DIFFERS };
bool ClientRecordExists( int cmd ) { CDiffInfo clsearch; clsearch.m_nCommandNumber = cmd; int clidx = m_Client.Find( clsearch ); return m_Client.IsValidIndex( clidx ); }
EMismatched IsMismatched( int svidx ) { CDiffInfo *serverslot = &m_Server[ svidx ];
// Now find the client version of this one
CDiffInfo clsearch; clsearch.m_nCommandNumber = serverslot->m_nCommandNumber; int clidx = m_Client.Find( clsearch ); if ( clidx == m_Client.InvalidIndex() ) return DIFFCHECK_NOTREADY;
// Now compare them
CDiffInfo *clientslot = &m_Client[ clidx ];
bool bSpew = false; if ( serverslot->m_Lines.Count() != clientslot->m_Lines.Count() ) { return DIFFCHECK_DIFFERS; }
int maxSlot = MAX( serverslot->m_Lines.Count(), clientslot->m_Lines.Count() ); if ( !bSpew ) { for ( int i = 0; i < maxSlot; ++i ) { CDiffStr *sv = NULL; CDiffStr *cl = NULL; if ( i < serverslot->m_Lines.Count() ) { sv = &serverslot->m_Lines[ i ]; } if ( i < clientslot->m_Lines.Count() ) { cl = &clientslot->m_Lines[ i ]; }
if ( Q_stricmp( sv ? sv->String() : "(missing)", cl ? cl->String() : "(missing)" ) ) { return DIFFCHECK_DIFFERS; } } }
return DIFFCHECK_MATCHED; }
virtual void Validate( bool bServer, int nCommandNumber ) { #if defined( CLIENT_DLL )
if ( !diffcheck.GetInt() ) return;
g_pDiffMgr = reinterpret_cast< IDiffMgr * >( diffcheck.GetInt() ); g_pDiffMgr->Validate( bServer, nCommandNumber ); return; #endif
Assert( CBaseEntity::IsServer() );
// Only do this on the client
if ( !bServer ) return;
// Find the last server command number
if ( m_Server.Count() <= 0 ) return;
int svidx = m_Server.LastInorder(); EMismatched eMisMatched = IsMismatched( svidx ); if ( eMisMatched == DIFFCHECK_NOTREADY ) { return; }
if ( eMisMatched == DIFFCHECK_DIFFERS ) { CUtlVector< int > vecPrev;
int nCur = svidx; do { int prev = m_Server.PrevInorder( nCur ); if ( m_Server.IsValidIndex( prev ) && ClientRecordExists( m_Server[ prev ].m_nCommandNumber ) ) { //SpewRecords( "prev", prev );
vecPrev.AddToHead( prev ); } else { break; }
nCur = prev; } while ( vecPrev.Count() < 10 );
Msg( "-----\n" );
for ( int p = 0; p < vecPrev.Count(); ++p ) { SpewRecords( "prev", vecPrev[ p ] ); }
SpewRecords( "bad ", svidx ); } }
void SpewRecords( char const *prefix, int svidx ) { CDiffInfo *serverslot = &m_Server[ svidx ];
// Now find the client version of this one
CDiffInfo clsearch; clsearch.m_nCommandNumber = serverslot->m_nCommandNumber; int clidx = m_Client.Find( clsearch ); if ( clidx == m_Client.InvalidIndex() ) return;
// Now compare them
CDiffInfo *clientslot = &m_Client[ clidx ];
int maxSlot = MAX( serverslot->m_Lines.Count(), clientslot->m_Lines.Count() );
for ( int i = 0; i < maxSlot; ++i ) { char const *sv = "(missing)"; char const *cl = "(missing)";
if ( i < serverslot->m_Lines.Count() ) { sv = serverslot->m_Lines[ i ].String(); } if ( i < clientslot->m_Lines.Count() ) { cl = clientslot->m_Lines[ i ].String(); }
bool bDiffers = Q_stricmp( sv, cl ) ? true : false;
Msg( "%s%s%d: sv[%50.50s] cl[%50.50s]\n", prefix, bDiffers ? "+++" : " ", serverslot->m_nCommandNumber, sv, cl ); } } private:
CUtlRBTree< CDiffInfo, int > m_Server; CUtlRBTree< CDiffInfo, int > m_Client; float m_flLastSpew; };
static CDiffManager g_DiffMgr;
void DiffPrint( bool bServer, int nCommandNumber, char const *fmt, ... ) { // Only track stuff for local player
CBasePlayer *pPlayer = CBaseEntity::GetPredictionPlayer(); if ( pPlayer && pPlayer->entindex() != 1 ) { return; }
va_list argptr; char string[1024]; va_start (argptr,fmt); int len = Q_vsnprintf(string, sizeof( string ), fmt,argptr); va_end (argptr);
if ( g_pDiffMgr ) { // Strip any \n at the end that the user accidently put int
if ( len > 0 && string[ len -1 ] == '\n' ) { string[ len - 1 ] = 0; } g_pDiffMgr->AddToDiff( bServer, nCommandNumber, string ); } }
void _CheckV( int tick, char const *ctx, const Vector &vel ) { DiffPrint( CBaseEntity::IsServer(), tick, "%20.20s %f %f %f", ctx, vel.x, vel.y, vel.z ); }
#define CheckV( tick, ctx, vel ) _CheckV( tick, ctx, vel );
static void StartCommand( bool bServer, int nCommandNumber ) { // Only track stuff for local player
CBasePlayer *pPlayer = CBaseEntity::GetPredictionPlayer(); if ( pPlayer && pPlayer->entindex() != 1 ) { return; }
if ( g_pDiffMgr ) { g_pDiffMgr->StartCommand( bServer, nCommandNumber ); } }
static void Validate( bool bServer, int nCommandNumber ) { // Only track stuff for local player
CBasePlayer *pPlayer = CBaseEntity::GetPredictionPlayer(); if ( pPlayer && pPlayer->entindex() != 1 ) { return; }
if ( g_pDiffMgr ) { g_pDiffMgr->Validate( bServer, nCommandNumber ); } }
void CGameMovement::DiffPrint( char const *fmt, ... ) { if ( !player ) return;
va_list argptr; char string[1024]; va_start (argptr,fmt); Q_vsnprintf(string, sizeof( string ), fmt,argptr); va_end (argptr);
::DiffPrint( CBaseEntity::IsServer(), player->CurrentCommandNumber(), "%s", string ); }
#else
static void DiffPrint( bool bServer, int nCommandNumber, char const *fmt, ... ) { // Nothing
} static void StartCommand( bool bServer, int nCommandNumber ) { }
static void Validate( bool bServer, int nCommandNumber ) { }
#define CheckV( tick, ctx, vel )
void CGameMovement::DiffPrint( char const *fmt, ... ) { }
#endif // !PREDICTION_ERROR_CHECK_LEVEL
#ifndef _XBOX
void COM_Log( const char *pszFile, const char *fmt, ...) { va_list argptr; char string[1024]; FileHandle_t fp; const char *pfilename; if ( !pszFile ) { pfilename = "hllog.txt"; } else { pfilename = pszFile; } va_start (argptr,fmt); Q_vsnprintf(string, sizeof( string ), fmt,argptr); va_end (argptr);
fp = filesystem->Open( pfilename, "a+t"); if (fp) { filesystem->FPrintf(fp, "%s", string); filesystem->Close(fp); } } #endif
#ifndef CLIENT_DLL
//-----------------------------------------------------------------------------
// Purpose: Debug - draw the displacement collision plane.
//-----------------------------------------------------------------------------
void DrawDispCollPlane( CBaseTrace *pTrace ) { float flLength = 30.0f;
// Create a basis, based on the impact normal.
int nMajorAxis = 0; Vector vecBasisU, vecBasisV, vecNormal; vecNormal = pTrace->plane.normal; float flAxisValue = vecNormal[0]; if ( fabs( vecNormal[1] ) > fabs( flAxisValue ) ) { nMajorAxis = 1; flAxisValue = vecNormal[1]; } if ( fabs( vecNormal[2] ) > fabs( flAxisValue ) ) { nMajorAxis = 2; } if ( ( nMajorAxis == 1 ) || ( nMajorAxis == 2 ) ) { vecBasisU.Init( 1.0f, 0.0f, 0.0f ); } else { vecBasisU.Init( 0.0f, 1.0f, 0.0f ); }
vecBasisV = vecNormal.Cross( vecBasisU ); VectorNormalize( vecBasisV );
vecBasisU = vecBasisV.Cross( vecNormal ); VectorNormalize( vecBasisU );
// Create the impact point. Push off the surface a bit.
Vector vecImpactPoint = pTrace->startpos + pTrace->fraction * ( pTrace->endpos - pTrace->startpos ); vecImpactPoint += vecNormal;
// Generate a quad to represent the plane.
Vector vecPlanePoints[4]; vecPlanePoints[0] = vecImpactPoint + ( vecBasisU * -flLength ) + ( vecBasisV * -flLength ); vecPlanePoints[1] = vecImpactPoint + ( vecBasisU * -flLength ) + ( vecBasisV * flLength ); vecPlanePoints[2] = vecImpactPoint + ( vecBasisU * flLength ) + ( vecBasisV * flLength ); vecPlanePoints[3] = vecImpactPoint + ( vecBasisU * flLength ) + ( vecBasisV * -flLength );
#if 0
// Test facing.
Vector vecEdges[2]; vecEdges[0] = vecPlanePoints[1] - vecPlanePoints[0]; vecEdges[1] = vecPlanePoints[2] - vecPlanePoints[0]; Vector vecCross = vecEdges[0].Cross( vecEdges[1] ); if ( vecCross.Dot( vecNormal ) < 0.0f ) { // Reverse winding.
} #endif
// Draw the plane.
NDebugOverlay::Triangle( vecPlanePoints[0], vecPlanePoints[1], vecPlanePoints[2], 125, 125, 125, 125, false, 5.0f ); NDebugOverlay::Triangle( vecPlanePoints[0], vecPlanePoints[2], vecPlanePoints[3], 125, 125, 125, 125, false, 5.0f );
NDebugOverlay::Line( vecPlanePoints[0], vecPlanePoints[1], 255, 255, 255, false, 5.0f ); NDebugOverlay::Line( vecPlanePoints[1], vecPlanePoints[2], 255, 255, 255, false, 5.0f ); NDebugOverlay::Line( vecPlanePoints[2], vecPlanePoints[3], 255, 255, 255, false, 5.0f ); NDebugOverlay::Line( vecPlanePoints[3], vecPlanePoints[0], 255, 255, 255, false, 5.0f );
// Draw the normal.
NDebugOverlay::Line( vecImpactPoint, vecImpactPoint + ( vecNormal * flLength ), 255, 0, 0, false, 5.0f ); } #endif
//-----------------------------------------------------------------------------
// Purpose: Constructs GameMovement interface
//-----------------------------------------------------------------------------
CGameMovement::CGameMovement( void ) { m_nOldWaterLevel = WL_NotInWater; m_flWaterEntryTime = 0; m_nOnLadder = 0;
mv = NULL;
memset( m_flStuckCheckTime, 0, sizeof(m_flStuckCheckTime) ); }
//-----------------------------------------------------------------------------
// Purpose: Destructor
//-----------------------------------------------------------------------------
CGameMovement::~CGameMovement( void ) { }
//-----------------------------------------------------------------------------
// Purpose: Allow bots etc to use slightly different solid masks
//-----------------------------------------------------------------------------
unsigned int CGameMovement::PlayerSolidMask( bool brushOnly ) { return ( brushOnly ) ? MASK_PLAYERSOLID_BRUSHONLY : MASK_PLAYERSOLID; }
//-----------------------------------------------------------------------------
// Purpose:
// Input : type -
// Output : int
//-----------------------------------------------------------------------------
int CGameMovement::GetCheckInterval( IntervalType_t type ) { int tickInterval = 1; switch ( type ) { default: tickInterval = 1; break; case GROUND: tickInterval = CATEGORIZE_GROUND_SURFACE_TICK_INTERVAL; break; case STUCK: // If we are in the process of being "stuck", then try a new position every command tick until m_StuckLast gets reset back down to zero
if ( player->m_StuckLast != 0 ) { tickInterval = 1; } else { if ( gpGlobals->maxClients == 1 ) { tickInterval = CHECK_STUCK_TICK_INTERVAL_SP; } else { tickInterval = CHECK_STUCK_TICK_INTERVAL; } } break; case LADDER: tickInterval = CHECK_LADDER_TICK_INTERVAL; break; } return tickInterval; }
//-----------------------------------------------------------------------------
// Purpose:
// Input : type -
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
bool CGameMovement::CheckInterval( IntervalType_t type ) { int tickInterval = GetCheckInterval( type );
if ( g_bMovementOptimizations ) { return (player->CurrentCommandNumber() + player->entindex()) % tickInterval == 0; } else { return true; } }
//-----------------------------------------------------------------------------
// Purpose:
// Input : ducked -
// Output : const Vector
//-----------------------------------------------------------------------------
Vector CGameMovement::GetPlayerMins( bool ducked ) const { return ducked ? VEC_DUCK_HULL_MIN_SCALED( player ) : VEC_HULL_MIN_SCALED( player ); }
//-----------------------------------------------------------------------------
// Purpose:
// Input : ducked -
// Output : const Vector
//-----------------------------------------------------------------------------
Vector CGameMovement::GetPlayerMaxs( bool ducked ) const { return ducked ? VEC_DUCK_HULL_MAX_SCALED( player ) : VEC_HULL_MAX_SCALED( player ); }
//-----------------------------------------------------------------------------
// Purpose:
// Input :
// Output : const Vector
//-----------------------------------------------------------------------------
Vector CGameMovement::GetPlayerMins( void ) const { if ( player->IsObserver() ) { return VEC_OBS_HULL_MIN_SCALED( player ); } else { return player->m_Local.m_bDucked ? VEC_DUCK_HULL_MIN_SCALED( player ) : VEC_HULL_MIN_SCALED( player ); } }
//-----------------------------------------------------------------------------
// Purpose:
// Input :
// Output : const Vector
//-----------------------------------------------------------------------------
Vector CGameMovement::GetPlayerMaxs( void ) const { if ( player->IsObserver() ) { return VEC_OBS_HULL_MAX_SCALED( player ); } else { return player->m_Local.m_bDucked ? VEC_DUCK_HULL_MAX_SCALED( player ) : VEC_HULL_MAX_SCALED( player ); } }
//-----------------------------------------------------------------------------
// Purpose:
// Input : ducked -
// Output : const Vector
//-----------------------------------------------------------------------------
Vector CGameMovement::GetPlayerViewOffset( bool ducked ) const { return ducked ? VEC_DUCK_VIEW_SCALED( player ) : VEC_VIEW_SCALED( player ); }
#if 0
//-----------------------------------------------------------------------------
// Traces player movement + position
//-----------------------------------------------------------------------------
inline void CGameMovement::TracePlayerBBox( const Vector& start, const Vector& end, unsigned int fMask, int collisionGroup, trace_t& pm ) { VPROF( "CGameMovement::TracePlayerBBox" );
Ray_t ray; ray.Init( start, end, GetPlayerMins(), GetPlayerMaxs() ); UTIL_TraceRay( ray, fMask, mv->m_nPlayerHandle.Get(), collisionGroup, &pm ); } #endif
CBaseHandle CGameMovement::TestPlayerPosition( const Vector& pos, int collisionGroup, trace_t& pm ) { Ray_t ray; ray.Init( pos, pos, GetPlayerMins(), GetPlayerMaxs() ); UTIL_TraceRay( ray, PlayerSolidMask(), mv->m_nPlayerHandle.Get(), collisionGroup, &pm ); if ( (pm.contents & PlayerSolidMask()) && pm.m_pEnt ) { return pm.m_pEnt->GetRefEHandle(); } else { return INVALID_EHANDLE_INDEX; } }
/*
// FIXME FIXME: Does this need to be hooked up?
bool CGameMovement::IsWet() const { return ((pev->flags & FL_INRAIN) != 0) || (m_WetTime >= gpGlobals->time); }
//-----------------------------------------------------------------------------
// Plants player footprint decals
//-----------------------------------------------------------------------------
#define PLAYER_HALFWIDTH 12
void CGameMovement::PlantFootprint( surfacedata_t *psurface ) { // Can't plant footprints on fake materials (ladders, wading)
if ( psurface->gameMaterial != 'X' ) { int footprintDecal = -1;
// Figure out which footprint type to plant...
// Use the wet footprint if we're wet...
if (IsWet()) { footprintDecal = DECAL_FOOTPRINT_WET; } else { // FIXME: Activate this once we decide to pull the trigger on it.
// NOTE: We could add in snow, mud, others here
// switch(psurface->gameMaterial)
// {
// case 'D':
// footprintDecal = DECAL_FOOTPRINT_DIRT;
// break;
// }
}
if (footprintDecal != -1) { Vector right; AngleVectors( pev->angles, 0, &right, 0 );
// Figure out where the top of the stepping leg is
trace_t tr; Vector hipOrigin; VectorMA( pev->origin, m_IsFootprintOnLeft ? -PLAYER_HALFWIDTH : PLAYER_HALFWIDTH, right, hipOrigin );
// Find where that leg hits the ground
UTIL_TraceLine( hipOrigin, hipOrigin + Vector(0, 0, -COORD_EXTENT * 1.74), MASK_SOLID_BRUSHONLY, edict(), COLLISION_GROUP_NONE, &tr);
unsigned char mType = TEXTURETYPE_Find( &tr );
// Splat a decal
CPVSFilter filter( tr.endpos ); te->FootprintDecal( filter, 0.0f, &tr.endpos, &right, ENTINDEX(tr.u.ent), gDecals[footprintDecal].index, mType );
} }
// Switch feet for next time
m_IsFootprintOnLeft = !m_IsFootprintOnLeft; }
#define WET_TIME 5.f // how many seconds till we're completely wet/dry
#define DRY_TIME 20.f // how many seconds till we're completely wet/dry
void CBasePlayer::UpdateWetness() { // BRJ 1/7/01
// Check for whether we're in a rainy area....
// Do this by tracing a line straight down with a size guaranteed to
// be larger than the map
// Update wetness based on whether we're in rain or not...
trace_t tr; UTIL_TraceLine( pev->origin, pev->origin + Vector(0, 0, -COORD_EXTENT * 1.74), MASK_SOLID_BRUSHONLY, edict(), COLLISION_GROUP_NONE, &tr); if (tr.surface.flags & SURF_WET) { if (! (pev->flags & FL_INRAIN) ) { // Transition...
// Figure out how wet we are now (we were drying off...)
float wetness = (m_WetTime - gpGlobals->time) / DRY_TIME; if (wetness < 0.0f) wetness = 0.0f;
// Here, wet time represents the time at which we get totally wet
m_WetTime = gpGlobals->time + (1.0 - wetness) * WET_TIME;
pev->flags |= FL_INRAIN; } } else { if ((pev->flags & FL_INRAIN) != 0) { // Transition...
// Figure out how wet we are now (we were getting more wet...)
float wetness = 1.0f + (gpGlobals->time - m_WetTime) / WET_TIME; if (wetness > 1.0f) wetness = 1.0f;
// Here, wet time represents the time at which we get totally dry
m_WetTime = gpGlobals->time + wetness * DRY_TIME;
pev->flags &= ~FL_INRAIN; } } } */
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CGameMovement::CategorizeGroundSurface( trace_t &pm ) { player->m_surfaceProps = pm.surface.surfaceProps; player->m_pSurfaceData = MoveHelper()->GetSurfaceProps()->GetSurfaceData( player->m_surfaceProps ); MoveHelper()->GetSurfaceProps()->GetPhysicsProperties( player->m_surfaceProps, NULL, NULL, &player->m_surfaceFriction, NULL ); // HACKHACK: Scale this to fudge the relationship between vphysics friction values and player friction values.
// A value of 0.8f feels pretty normal for vphysics, whereas 1.0f is normal for players.
// This scaling trivially makes them equivalent. REVISIT if this affects low friction surfaces too much.
player->m_surfaceFriction *= 1.25f; if ( player->m_surfaceFriction > 1.0f ) player->m_surfaceFriction = 1.0f;
player->m_chTextureType = player->m_pSurfaceData->game.material; }
bool CGameMovement::IsDead( void ) const { return ( player->m_iHealth <= 0 && !player->IsAlive() ); }
//-----------------------------------------------------------------------------
// Figures out how the constraint should slow us down
//-----------------------------------------------------------------------------
float CGameMovement::ComputeConstraintSpeedFactor( void ) { // If we have a constraint, slow down because of that too.
if ( !mv || mv->m_flConstraintRadius == 0.0f ) return 1.0f;
float flDistSq = mv->GetAbsOrigin().DistToSqr( mv->m_vecConstraintCenter );
float flOuterRadiusSq = mv->m_flConstraintRadius * mv->m_flConstraintRadius; float flInnerRadiusSq = mv->m_flConstraintRadius - mv->m_flConstraintWidth; flInnerRadiusSq *= flInnerRadiusSq;
// Only slow us down if we're inside the constraint ring
if ((flDistSq <= flInnerRadiusSq) || (flDistSq >= flOuterRadiusSq)) return 1.0f;
// Only slow us down if we're running away from the center
Vector vecDesired; VectorMultiply( m_vecForward, mv->m_flForwardMove, vecDesired ); VectorMA( vecDesired, mv->m_flSideMove, m_vecRight, vecDesired ); VectorMA( vecDesired, mv->m_flUpMove, m_vecUp, vecDesired );
Vector vecDelta; VectorSubtract( mv->GetAbsOrigin(), mv->m_vecConstraintCenter, vecDelta ); VectorNormalize( vecDelta ); VectorNormalize( vecDesired ); if (DotProduct( vecDelta, vecDesired ) < 0.0f) return 1.0f;
float flFrac = (sqrt(flDistSq) - (mv->m_flConstraintRadius - mv->m_flConstraintWidth)) / mv->m_flConstraintWidth;
float flSpeedFactor = Lerp( flFrac, 1.0f, mv->m_flConstraintSpeedFactor ); return flSpeedFactor; }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CGameMovement::CheckParameters( void ) { QAngle v_angle;
if ( player->GetMoveType() != MOVETYPE_ISOMETRIC && player->GetMoveType() != MOVETYPE_NOCLIP && player->GetMoveType() != MOVETYPE_OBSERVER ) { float spd; float maxspeed;
spd = ( mv->m_flForwardMove * mv->m_flForwardMove ) + ( mv->m_flSideMove * mv->m_flSideMove ) + ( mv->m_flUpMove * mv->m_flUpMove );
maxspeed = mv->m_flClientMaxSpeed; if ( maxspeed != 0.0 ) { mv->m_flMaxSpeed = MIN( maxspeed, mv->m_flMaxSpeed ); }
// Slow down by the speed factor
float flSpeedFactor = 1.0f; if (player->m_pSurfaceData) { flSpeedFactor = player->m_pSurfaceData->game.maxSpeedFactor; }
// If we have a constraint, slow down because of that too.
float flConstraintSpeedFactor = ComputeConstraintSpeedFactor(); if (flConstraintSpeedFactor < flSpeedFactor) flSpeedFactor = flConstraintSpeedFactor;
mv->m_flMaxSpeed *= flSpeedFactor;
if ( g_bMovementOptimizations ) { // Same thing but only do the sqrt if we have to.
if ( ( spd != 0.0 ) && ( spd > mv->m_flMaxSpeed*mv->m_flMaxSpeed ) ) { float fRatio = mv->m_flMaxSpeed / sqrt( spd ); mv->m_flForwardMove *= fRatio; mv->m_flSideMove *= fRatio; mv->m_flUpMove *= fRatio; } } else { spd = sqrt( spd ); if ( ( spd != 0.0 ) && ( spd > mv->m_flMaxSpeed ) ) { float fRatio = mv->m_flMaxSpeed / spd; mv->m_flForwardMove *= fRatio; mv->m_flSideMove *= fRatio; mv->m_flUpMove *= fRatio; } } }
if ( player->GetFlags() & FL_FROZEN || player->GetFlags() & FL_ONTRAIN || IsDead() ) { mv->m_flForwardMove = 0; mv->m_flSideMove = 0; mv->m_flUpMove = 0; }
DecayPunchAngle();
// Take angles from command.
if ( !IsDead() ) { v_angle = mv->m_vecAngles; v_angle = v_angle + player->m_Local.m_vecPunchAngle;
// Now adjust roll angle
if ( player->GetMoveType() != MOVETYPE_ISOMETRIC && player->GetMoveType() != MOVETYPE_NOCLIP ) { mv->m_vecAngles[ROLL] = CalcRoll( v_angle, mv->m_vecVelocity, sv_rollangle.GetFloat(), sv_rollspeed.GetFloat() ); } else { mv->m_vecAngles[ROLL] = 0.0; // v_angle[ ROLL ];
} mv->m_vecAngles[PITCH] = v_angle[PITCH]; mv->m_vecAngles[YAW] = v_angle[YAW]; } else { mv->m_vecAngles = mv->m_vecOldAngles; }
// Set dead player view_offset
if ( IsDead() ) { player->SetViewOffset( VEC_DEAD_VIEWHEIGHT_SCALED( player ) ); }
// Adjust client view angles to match values used on server.
if ( mv->m_vecAngles[YAW] > 180.0f ) { mv->m_vecAngles[YAW] -= 360.0f; } }
void CGameMovement::ReduceTimers( void ) { float frame_msec = 1000.0f * gpGlobals->frametime;
if ( player->m_Local.m_flDucktime > 0 ) { player->m_Local.m_flDucktime -= frame_msec; if ( player->m_Local.m_flDucktime < 0 ) { player->m_Local.m_flDucktime = 0; } } if ( player->m_Local.m_flDuckJumpTime > 0 ) { player->m_Local.m_flDuckJumpTime -= frame_msec; if ( player->m_Local.m_flDuckJumpTime < 0 ) { player->m_Local.m_flDuckJumpTime = 0; } } if ( player->m_Local.m_flJumpTime > 0 ) { player->m_Local.m_flJumpTime -= frame_msec; if ( player->m_Local.m_flJumpTime < 0 ) { player->m_Local.m_flJumpTime = 0; } } if ( player->m_flSwimSoundTime > 0 ) { player->m_flSwimSoundTime -= frame_msec; if ( player->m_flSwimSoundTime < 0 ) { player->m_flSwimSoundTime = 0; } } }
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pMove -
//-----------------------------------------------------------------------------
void CGameMovement::ProcessMovement( CBasePlayer *pPlayer, CMoveData *pMove ) { Assert( pMove && pPlayer );
float flStoreFrametime = gpGlobals->frametime;
//!!HACK HACK: Adrian - slow down all player movement by this factor.
//!!Blame Yahn for this one.
gpGlobals->frametime *= pPlayer->GetLaggedMovementValue();
ResetGetPointContentsCache();
// Cropping movement speed scales mv->m_fForwardSpeed etc. globally
// Once we crop, we don't want to recursively crop again, so we set the crop
// flag globally here once per usercmd cycle.
m_iSpeedCropped = SPEED_CROPPED_RESET; // StartTrackPredictionErrors should have set this
Assert( player == pPlayer ); player = pPlayer;
mv = pMove; mv->m_flMaxSpeed = pPlayer->GetPlayerMaxSpeed();
// CheckV( player->CurrentCommandNumber(), "StartPos", mv->GetAbsOrigin() );
DiffPrint( "start %f %f %f", mv->GetAbsOrigin().x, mv->GetAbsOrigin().y, mv->GetAbsOrigin().z );
// Run the command.
PlayerMove();
FinishMove();
DiffPrint( "end %f %f %f", mv->GetAbsOrigin().x, mv->GetAbsOrigin().y, mv->GetAbsOrigin().z );
// CheckV( player->CurrentCommandNumber(), "EndPos", mv->GetAbsOrigin() );
//This is probably not needed, but just in case.
gpGlobals->frametime = flStoreFrametime;
// player = NULL;
}
void CGameMovement::StartTrackPredictionErrors( CBasePlayer *pPlayer ) { player = pPlayer;
#if PREDICTION_ERROR_CHECK_LEVEL > 0
StartCommand( CBaseEntity::IsServer(), player->CurrentCommandNumber() ); #endif
}
void CGameMovement::FinishTrackPredictionErrors( CBasePlayer *pPlayer ) { #if PREDICTION_ERROR_CHECK_LEVEL > 0
Assert( player == pPlayer );
// DiffPrint( "end %f", player->m_Local.m_vecPunchAngleVel.m_Value.x );
// Call validate at end of checking
Validate( CBaseEntity::IsServer(), player->CurrentCommandNumber() ); #endif
}
//-----------------------------------------------------------------------------
// Purpose: Sets ground entity
//-----------------------------------------------------------------------------
void CGameMovement::FinishMove( void ) { mv->m_nOldButtons = mv->m_nButtons; mv->m_flOldForwardMove = mv->m_flForwardMove; }
#define PUNCH_DAMPING 9.0f // bigger number makes the response more damped, smaller is less damped
// currently the system will overshoot, with larger damping values it won't
#define PUNCH_SPRING_CONSTANT 65.0f // bigger number increases the speed at which the view corrects
//-----------------------------------------------------------------------------
// Purpose: Decays the punchangle toward 0,0,0.
// Modelled as a damped spring
//-----------------------------------------------------------------------------
void CGameMovement::DecayPunchAngle( void ) { if ( player->m_Local.m_vecPunchAngle->LengthSqr() > 0.001 || player->m_Local.m_vecPunchAngleVel->LengthSqr() > 0.001 ) { player->m_Local.m_vecPunchAngle += player->m_Local.m_vecPunchAngleVel * gpGlobals->frametime; float damping = 1 - (PUNCH_DAMPING * gpGlobals->frametime); if ( damping < 0 ) { damping = 0; } player->m_Local.m_vecPunchAngleVel *= damping; // torsional spring
// UNDONE: Per-axis spring constant?
float springForceMagnitude = PUNCH_SPRING_CONSTANT * gpGlobals->frametime; springForceMagnitude = clamp(springForceMagnitude, 0.f, 2.f ); player->m_Local.m_vecPunchAngleVel -= player->m_Local.m_vecPunchAngle * springForceMagnitude;
// don't wrap around
player->m_Local.m_vecPunchAngle.Init( clamp(player->m_Local.m_vecPunchAngle->x, -89.f, 89.f ), clamp(player->m_Local.m_vecPunchAngle->y, -179.f, 179.f ), clamp(player->m_Local.m_vecPunchAngle->z, -89.f, 89.f ) ); } else { player->m_Local.m_vecPunchAngle.Init( 0, 0, 0 ); player->m_Local.m_vecPunchAngleVel.Init( 0, 0, 0 ); } }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CGameMovement::StartGravity( void ) { float ent_gravity; if (player->GetGravity()) ent_gravity = player->GetGravity(); else ent_gravity = 1.0;
// Add gravity so they'll be in the correct position during movement
// yes, this 0.5 looks wrong, but it's not.
mv->m_vecVelocity[2] -= (ent_gravity * GetCurrentGravity() * 0.5 * gpGlobals->frametime ); mv->m_vecVelocity[2] += player->GetBaseVelocity()[2] * gpGlobals->frametime;
Vector temp = player->GetBaseVelocity(); temp[ 2 ] = 0; player->SetBaseVelocity( temp );
CheckVelocity(); }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CGameMovement::CheckWaterJump( void ) { Vector flatforward; Vector forward; Vector flatvelocity; float curspeed;
AngleVectors( mv->m_vecViewAngles, &forward ); // Determine movement angles
// Already water jumping.
if (player->m_flWaterJumpTime) return;
// Don't hop out if we just jumped in
if (mv->m_vecVelocity[2] < -180) return; // only hop out if we are moving up
// See if we are backing up
flatvelocity[0] = mv->m_vecVelocity[0]; flatvelocity[1] = mv->m_vecVelocity[1]; flatvelocity[2] = 0;
// Must be moving
curspeed = VectorNormalize( flatvelocity ); // see if near an edge
flatforward[0] = forward[0]; flatforward[1] = forward[1]; flatforward[2] = 0; VectorNormalize (flatforward);
// Are we backing into water from steps or something? If so, don't pop forward
if ( curspeed != 0.0 && ( DotProduct( flatvelocity, flatforward ) < 0.0 ) ) return;
Vector vecStart; // Start line trace at waist height (using the center of the player for this here)
vecStart = mv->GetAbsOrigin() + (GetPlayerMins() + GetPlayerMaxs() ) * 0.5;
Vector vecEnd; VectorMA( vecStart, 24.0f, flatforward, vecEnd ); trace_t tr; TracePlayerBBox( vecStart, vecEnd, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, tr ); if ( tr.fraction < 1.0 ) // solid at waist
{ IPhysicsObject *pPhysObj = tr.m_pEnt->VPhysicsGetObject(); if ( pPhysObj ) { if ( pPhysObj->GetGameFlags() & FVPHYSICS_PLAYER_HELD ) return; }
vecStart.z = mv->GetAbsOrigin().z + player->GetViewOffset().z + WATERJUMP_HEIGHT; VectorMA( vecStart, 24.0f, flatforward, vecEnd ); VectorMA( vec3_origin, -50.0f, tr.plane.normal, player->m_vecWaterJumpVel );
TracePlayerBBox( vecStart, vecEnd, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, tr ); if ( tr.fraction == 1.0 ) // open at eye level
{ // Now trace down to see if we would actually land on a standable surface.
VectorCopy( vecEnd, vecStart ); vecEnd.z -= 1024.0f; TracePlayerBBox( vecStart, vecEnd, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, tr ); if ( ( tr.fraction < 1.0f ) && ( tr.plane.normal.z >= 0.7 ) ) { mv->m_vecVelocity[2] = 256.0f; // Push up
mv->m_nOldButtons |= IN_JUMP; // Don't jump again until released
player->AddFlag( FL_WATERJUMP ); player->m_flWaterJumpTime = 2000.0f; // Do this for 2 seconds
} } } }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CGameMovement::WaterJump( void ) { if (player->m_flWaterJumpTime > 10000) player->m_flWaterJumpTime = 10000;
if (!player->m_flWaterJumpTime) return;
player->m_flWaterJumpTime -= 1000.0f * gpGlobals->frametime;
if (player->m_flWaterJumpTime <= 0 || !player->GetWaterLevel()) { player->m_flWaterJumpTime = 0; player->RemoveFlag( FL_WATERJUMP ); } mv->m_vecVelocity[0] = player->m_vecWaterJumpVel[0]; mv->m_vecVelocity[1] = player->m_vecWaterJumpVel[1]; }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CGameMovement::WaterMove( void ) { int i; Vector wishvel; float wishspeed; Vector wishdir; Vector start, dest; Vector temp; trace_t pm; float speed, newspeed, addspeed, accelspeed; Vector forward, right, up;
AngleVectors (mv->m_vecViewAngles, &forward, &right, &up); // Determine movement angles
//
// user intentions
//
for (i=0 ; i<3 ; i++) { wishvel[i] = forward[i]*mv->m_flForwardMove + right[i]*mv->m_flSideMove; }
// if we have the jump key down, move us up as well
if (mv->m_nButtons & IN_JUMP) { wishvel[2] += mv->m_flClientMaxSpeed; } // Sinking after no other movement occurs
else if (!mv->m_flForwardMove && !mv->m_flSideMove && !mv->m_flUpMove) { wishvel[2] -= 60; // drift towards bottom
} else // Go straight up by upmove amount.
{ // exaggerate upward movement along forward as well
float upwardMovememnt = mv->m_flForwardMove * forward.z * 2; upwardMovememnt = clamp( upwardMovememnt, 0.f, mv->m_flClientMaxSpeed ); wishvel[2] += mv->m_flUpMove + upwardMovememnt; }
// Copy it over and determine speed
VectorCopy (wishvel, wishdir); wishspeed = VectorNormalize(wishdir);
// Cap speed.
if (wishspeed > mv->m_flMaxSpeed) { VectorScale (wishvel, mv->m_flMaxSpeed/wishspeed, wishvel); wishspeed = mv->m_flMaxSpeed; }
// Slow us down a bit.
wishspeed *= 0.8; // Water friction
VectorCopy(mv->m_vecVelocity, temp); speed = VectorNormalize(temp); if (speed) { newspeed = speed - gpGlobals->frametime * speed * sv_friction.GetFloat() * player->m_surfaceFriction; if (newspeed < 0.1f) { newspeed = 0; }
VectorScale (mv->m_vecVelocity, newspeed/speed, mv->m_vecVelocity); } else { newspeed = 0; }
// water acceleration
if (wishspeed >= 0.1f) // old !
{ addspeed = wishspeed - newspeed; if (addspeed > 0) { VectorNormalize(wishvel); accelspeed = sv_accelerate.GetFloat() * wishspeed * gpGlobals->frametime * player->m_surfaceFriction; if (accelspeed > addspeed) { accelspeed = addspeed; }
for (i = 0; i < 3; i++) { float deltaSpeed = accelspeed * wishvel[i]; mv->m_vecVelocity[i] += deltaSpeed; mv->m_outWishVel[i] += deltaSpeed; } } }
VectorAdd (mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity);
// Now move
// assume it is a stair or a slope, so press down from stepheight above
VectorMA (mv->GetAbsOrigin(), gpGlobals->frametime, mv->m_vecVelocity, dest); TracePlayerBBox( mv->GetAbsOrigin(), dest, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, pm ); if ( pm.fraction == 1.0f ) { VectorCopy( dest, start ); if ( player->m_Local.m_bAllowAutoMovement ) { start[2] += player->m_Local.m_flStepSize + 1; } TracePlayerBBox( start, dest, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, pm ); if (!pm.startsolid && !pm.allsolid) { float stepDist = pm.endpos.z - mv->GetAbsOrigin().z; mv->m_outStepHeight += stepDist; // walked up the step, so just keep result and exit
mv->SetAbsOrigin( pm.endpos ); VectorSubtract( mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity ); return; }
// Try moving straight along out normal path.
TryPlayerMove(); } else { if ( !player->GetGroundEntity() ) { TryPlayerMove(); VectorSubtract( mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity ); return; }
StepMove( dest, pm ); } VectorSubtract( mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity ); }
//-----------------------------------------------------------------------------
// Purpose: Does the basic move attempting to climb up step heights. It uses
// the mv->GetAbsOrigin() and mv->m_vecVelocity. It returns a new
// new mv->GetAbsOrigin(), mv->m_vecVelocity, and mv->m_outStepHeight.
//-----------------------------------------------------------------------------
void CGameMovement::StepMove( Vector &vecDestination, trace_t &trace ) { Vector vecEndPos; VectorCopy( vecDestination, vecEndPos );
// Try sliding forward both on ground and up 16 pixels
// take the move that goes farthest
Vector vecPos, vecVel; VectorCopy( mv->GetAbsOrigin(), vecPos ); VectorCopy( mv->m_vecVelocity, vecVel );
// Slide move down.
TryPlayerMove( &vecEndPos, &trace ); // Down results.
Vector vecDownPos, vecDownVel; VectorCopy( mv->GetAbsOrigin(), vecDownPos ); VectorCopy( mv->m_vecVelocity, vecDownVel ); // Reset original values.
mv->SetAbsOrigin( vecPos ); VectorCopy( vecVel, mv->m_vecVelocity ); // Move up a stair height.
VectorCopy( mv->GetAbsOrigin(), vecEndPos ); if ( player->m_Local.m_bAllowAutoMovement ) { vecEndPos.z += player->m_Local.m_flStepSize + DIST_EPSILON; } TracePlayerBBox( mv->GetAbsOrigin(), vecEndPos, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, trace ); if ( !trace.startsolid && !trace.allsolid ) { mv->SetAbsOrigin( trace.endpos ); } // Slide move up.
TryPlayerMove(); // Move down a stair (attempt to).
VectorCopy( mv->GetAbsOrigin(), vecEndPos ); if ( player->m_Local.m_bAllowAutoMovement ) { vecEndPos.z -= player->m_Local.m_flStepSize + DIST_EPSILON; } TracePlayerBBox( mv->GetAbsOrigin(), vecEndPos, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, trace ); // If we are not on the ground any more then use the original movement attempt.
if ( trace.plane.normal[2] < 0.7 ) { mv->SetAbsOrigin( vecDownPos ); VectorCopy( vecDownVel, mv->m_vecVelocity ); float flStepDist = mv->GetAbsOrigin().z - vecPos.z; if ( flStepDist > 0.0f ) { mv->m_outStepHeight += flStepDist; } return; } // If the trace ended up in empty space, copy the end over to the origin.
if ( !trace.startsolid && !trace.allsolid ) { mv->SetAbsOrigin( trace.endpos ); } // Copy this origin to up.
Vector vecUpPos; VectorCopy( mv->GetAbsOrigin(), vecUpPos ); // decide which one went farther
float flDownDist = ( vecDownPos.x - vecPos.x ) * ( vecDownPos.x - vecPos.x ) + ( vecDownPos.y - vecPos.y ) * ( vecDownPos.y - vecPos.y ); float flUpDist = ( vecUpPos.x - vecPos.x ) * ( vecUpPos.x - vecPos.x ) + ( vecUpPos.y - vecPos.y ) * ( vecUpPos.y - vecPos.y ); if ( flDownDist > flUpDist ) { mv->SetAbsOrigin( vecDownPos ); VectorCopy( vecDownVel, mv->m_vecVelocity ); } else { // copy z value from slide move
mv->m_vecVelocity.z = vecDownVel.z; } float flStepDist = mv->GetAbsOrigin().z - vecPos.z; if ( flStepDist > 0 ) { mv->m_outStepHeight += flStepDist; } }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CGameMovement::Friction( void ) { float speed, newspeed, control; float friction; float drop; // If we are in water jump cycle, don't apply friction
if (player->m_flWaterJumpTime) return;
// Calculate speed
speed = VectorLength( mv->m_vecVelocity ); // If too slow, return
if (speed < 0.1f) { return; }
drop = 0;
// apply ground friction
if (player->GetGroundEntity() != NULL) // On an entity that is the ground
{ friction = sv_friction.GetFloat() * player->m_surfaceFriction;
// Bleed off some speed, but if we have less than the bleed
// threshold, bleed the threshold amount.
if ( IsX360() ) { if( player->m_Local.m_bDucked ) { control = (speed < sv_stopspeed.GetFloat()) ? sv_stopspeed.GetFloat() : speed; } else { #if defined ( TF_DLL ) || defined ( TF_CLIENT_DLL )
control = (speed < sv_stopspeed.GetFloat()) ? sv_stopspeed.GetFloat() : speed; #else
control = (speed < sv_stopspeed.GetFloat()) ? (sv_stopspeed.GetFloat() * 2.0f) : speed; #endif
} } else { control = (speed < sv_stopspeed.GetFloat()) ? sv_stopspeed.GetFloat() : speed; }
// Add the amount to the drop amount.
drop += control*friction*gpGlobals->frametime; }
// scale the velocity
newspeed = speed - drop; if (newspeed < 0) newspeed = 0;
if ( newspeed != speed ) { // Determine proportion of old speed we are using.
newspeed /= speed; // Adjust velocity according to proportion.
VectorScale( mv->m_vecVelocity, newspeed, mv->m_vecVelocity ); }
mv->m_outWishVel -= (1.f-newspeed) * mv->m_vecVelocity; }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CGameMovement::FinishGravity( void ) { float ent_gravity;
if ( player->m_flWaterJumpTime ) return;
if ( player->GetGravity() ) ent_gravity = player->GetGravity(); else ent_gravity = 1.0;
// Get the correct velocity for the end of the dt
mv->m_vecVelocity[2] -= (ent_gravity * GetCurrentGravity() * gpGlobals->frametime * 0.5);
CheckVelocity(); }
//-----------------------------------------------------------------------------
// Purpose:
// Input : wishdir -
// accel -
//-----------------------------------------------------------------------------
void CGameMovement::AirAccelerate( Vector& wishdir, float wishspeed, float accel ) { int i; float addspeed, accelspeed, currentspeed; float wishspd;
wishspd = wishspeed; if (player->pl.deadflag) return; if (player->m_flWaterJumpTime) return;
// Cap speed
if ( wishspd > GetAirSpeedCap() ) wishspd = GetAirSpeedCap();
// Determine veer amount
currentspeed = mv->m_vecVelocity.Dot(wishdir);
// See how much to add
addspeed = wishspd - currentspeed;
// If not adding any, done.
if (addspeed <= 0) return;
// Determine acceleration speed after acceleration
accelspeed = accel * wishspeed * gpGlobals->frametime * player->m_surfaceFriction;
// Cap it
if (accelspeed > addspeed) accelspeed = addspeed; // Adjust pmove vel.
for (i=0 ; i<3 ; i++) { mv->m_vecVelocity[i] += accelspeed * wishdir[i]; mv->m_outWishVel[i] += accelspeed * wishdir[i]; } }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CGameMovement::AirMove( void ) { int i; Vector wishvel; float fmove, smove; Vector wishdir; float wishspeed; Vector forward, right, up;
AngleVectors (mv->m_vecViewAngles, &forward, &right, &up); // Determine movement angles
// Copy movement amounts
fmove = mv->m_flForwardMove; smove = mv->m_flSideMove; // Zero out z components of movement vectors
forward[2] = 0; right[2] = 0; VectorNormalize(forward); // Normalize remainder of vectors
VectorNormalize(right); //
for (i=0 ; i<2 ; i++) // Determine x and y parts of velocity
wishvel[i] = forward[i]*fmove + right[i]*smove; wishvel[2] = 0; // Zero out z part of velocity
VectorCopy (wishvel, wishdir); // Determine maginitude of speed of move
wishspeed = VectorNormalize(wishdir);
//
// clamp to server defined max speed
//
if ( wishspeed != 0 && (wishspeed > mv->m_flMaxSpeed)) { VectorScale (wishvel, mv->m_flMaxSpeed/wishspeed, wishvel); wishspeed = mv->m_flMaxSpeed; } AirAccelerate( wishdir, wishspeed, sv_airaccelerate.GetFloat() );
// Add in any base velocity to the current velocity.
VectorAdd(mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity );
TryPlayerMove();
// Now pull the base velocity back out. Base velocity is set if you are on a moving object, like a conveyor (or maybe another monster?)
VectorSubtract( mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity ); }
bool CGameMovement::CanAccelerate() { // Dead players don't accelerate.
if (player->pl.deadflag) return false;
// If waterjumping, don't accelerate
if (player->m_flWaterJumpTime) return false;
return true; }
//-----------------------------------------------------------------------------
// Purpose:
// Input : wishdir -
// wishspeed -
// accel -
//-----------------------------------------------------------------------------
void CGameMovement::Accelerate( Vector& wishdir, float wishspeed, float accel ) { int i; float addspeed, accelspeed, currentspeed;
// This gets overridden because some games (CSPort) want to allow dead (observer) players
// to be able to move around.
if ( !CanAccelerate() ) return;
// See if we are changing direction a bit
currentspeed = mv->m_vecVelocity.Dot(wishdir);
// Reduce wishspeed by the amount of veer.
addspeed = wishspeed - currentspeed;
// If not going to add any speed, done.
if (addspeed <= 0) return;
// Determine amount of accleration.
accelspeed = accel * gpGlobals->frametime * wishspeed * player->m_surfaceFriction;
// Cap at addspeed
if (accelspeed > addspeed) accelspeed = addspeed; // Adjust velocity.
for (i=0 ; i<3 ; i++) { mv->m_vecVelocity[i] += accelspeed * wishdir[i]; } }
//-----------------------------------------------------------------------------
// Purpose: Try to keep a walking player on the ground when running down slopes etc
//-----------------------------------------------------------------------------
void CGameMovement::StayOnGround( void ) { trace_t trace; Vector start( mv->GetAbsOrigin() ); Vector end( mv->GetAbsOrigin() ); start.z += 2; end.z -= player->GetStepSize();
// See how far up we can go without getting stuck
TracePlayerBBox( mv->GetAbsOrigin(), start, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, trace ); start = trace.endpos;
// using trace.startsolid is unreliable here, it doesn't get set when
// tracing bounding box vs. terrain
// Now trace down from a known safe position
TracePlayerBBox( start, end, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, trace ); if ( trace.fraction > 0.0f && // must go somewhere
trace.fraction < 1.0f && // must hit something
!trace.startsolid && // can't be embedded in a solid
trace.plane.normal[2] >= 0.7 ) // can't hit a steep slope that we can't stand on anyway
{ float flDelta = fabs(mv->GetAbsOrigin().z - trace.endpos.z);
//This is incredibly hacky. The real problem is that trace returning that strange value we can't network over.
if ( flDelta > 0.5f * COORD_RESOLUTION) { mv->SetAbsOrigin( trace.endpos ); } } }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CGameMovement::WalkMove( void ) { int i;
Vector wishvel; float spd; float fmove, smove; Vector wishdir; float wishspeed;
Vector dest; trace_t pm; Vector forward, right, up;
AngleVectors (mv->m_vecViewAngles, &forward, &right, &up); // Determine movement angles
CHandle< CBaseEntity > oldground; oldground = player->GetGroundEntity(); // Copy movement amounts
fmove = mv->m_flForwardMove; smove = mv->m_flSideMove;
// Zero out z components of movement vectors
if ( g_bMovementOptimizations ) { if ( forward[2] != 0 ) { forward[2] = 0; VectorNormalize( forward ); }
if ( right[2] != 0 ) { right[2] = 0; VectorNormalize( right ); } } else { forward[2] = 0; right[2] = 0; VectorNormalize (forward); // Normalize remainder of vectors.
VectorNormalize (right); //
}
for (i=0 ; i<2 ; i++) // Determine x and y parts of velocity
wishvel[i] = forward[i]*fmove + right[i]*smove; wishvel[2] = 0; // Zero out z part of velocity
VectorCopy (wishvel, wishdir); // Determine maginitude of speed of move
wishspeed = VectorNormalize(wishdir);
//
// Clamp to server defined max speed
//
if ((wishspeed != 0.0f) && (wishspeed > mv->m_flMaxSpeed)) { VectorScale (wishvel, mv->m_flMaxSpeed/wishspeed, wishvel); wishspeed = mv->m_flMaxSpeed; }
// Set pmove velocity
mv->m_vecVelocity[2] = 0; Accelerate ( wishdir, wishspeed, sv_accelerate.GetFloat() ); mv->m_vecVelocity[2] = 0;
// Add in any base velocity to the current velocity.
VectorAdd (mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity );
spd = VectorLength( mv->m_vecVelocity );
if ( spd < 1.0f ) { mv->m_vecVelocity.Init(); // Now pull the base velocity back out. Base velocity is set if you are on a moving object, like a conveyor (or maybe another monster?)
VectorSubtract( mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity ); return; }
// first try just moving to the destination
dest[0] = mv->GetAbsOrigin()[0] + mv->m_vecVelocity[0]*gpGlobals->frametime; dest[1] = mv->GetAbsOrigin()[1] + mv->m_vecVelocity[1]*gpGlobals->frametime; dest[2] = mv->GetAbsOrigin()[2];
// first try moving directly to the next spot
TracePlayerBBox( mv->GetAbsOrigin(), dest, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, pm );
// If we made it all the way, then copy trace end as new player position.
mv->m_outWishVel += wishdir * wishspeed;
if ( pm.fraction == 1 ) { mv->SetAbsOrigin( pm.endpos ); // Now pull the base velocity back out. Base velocity is set if you are on a moving object, like a conveyor (or maybe another monster?)
VectorSubtract( mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity );
StayOnGround(); return; }
// Don't walk up stairs if not on ground.
if ( oldground == NULL && player->GetWaterLevel() == 0 ) { // Now pull the base velocity back out. Base velocity is set if you are on a moving object, like a conveyor (or maybe another monster?)
VectorSubtract( mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity ); return; }
// If we are jumping out of water, don't do anything more.
if ( player->m_flWaterJumpTime ) { // Now pull the base velocity back out. Base velocity is set if you are on a moving object, like a conveyor (or maybe another monster?)
VectorSubtract( mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity ); return; }
StepMove( dest, pm );
// Now pull the base velocity back out. Base velocity is set if you are on a moving object, like a conveyor (or maybe another monster?)
VectorSubtract( mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity );
StayOnGround(); }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CGameMovement::FullWalkMove( ) { if ( !CheckWater() ) { StartGravity(); }
// If we are leaping out of the water, just update the counters.
if (player->m_flWaterJumpTime) { WaterJump(); TryPlayerMove(); // See if we are still in water?
CheckWater(); return; }
// If we are swimming in the water, see if we are nudging against a place we can jump up out
// of, and, if so, start out jump. Otherwise, if we are not moving up, then reset jump timer to 0
if ( player->GetWaterLevel() >= WL_Waist ) { if ( player->GetWaterLevel() == WL_Waist ) { CheckWaterJump(); }
// If we are falling again, then we must not trying to jump out of water any more.
if ( mv->m_vecVelocity[2] < 0 && player->m_flWaterJumpTime ) { player->m_flWaterJumpTime = 0; }
// Was jump button pressed?
if (mv->m_nButtons & IN_JUMP) { CheckJumpButton(); } else { mv->m_nOldButtons &= ~IN_JUMP; }
// Perform regular water movement
WaterMove();
// Redetermine position vars
CategorizePosition();
// If we are on ground, no downward velocity.
if ( player->GetGroundEntity() != NULL ) { mv->m_vecVelocity[2] = 0; } } else // Not fully underwater
{ // Was jump button pressed?
if (mv->m_nButtons & IN_JUMP) { CheckJumpButton(); } else { mv->m_nOldButtons &= ~IN_JUMP; }
// Fricion is handled before we add in any base velocity. That way, if we are on a conveyor,
// we don't slow when standing still, relative to the conveyor.
if (player->GetGroundEntity() != NULL) { mv->m_vecVelocity[2] = 0.0; Friction(); }
// Make sure velocity is valid.
CheckVelocity();
if (player->GetGroundEntity() != NULL) { WalkMove(); } else { AirMove(); // Take into account movement when in air.
}
// Set final flags.
CategorizePosition();
// Make sure velocity is valid.
CheckVelocity();
// Add any remaining gravitational component.
if ( !CheckWater() ) { FinishGravity(); }
// If we are on ground, no downward velocity.
if ( player->GetGroundEntity() != NULL ) { mv->m_vecVelocity[2] = 0; } CheckFalling(); }
if ( ( m_nOldWaterLevel == WL_NotInWater && player->GetWaterLevel() != WL_NotInWater ) || ( m_nOldWaterLevel != WL_NotInWater && player->GetWaterLevel() == WL_NotInWater ) ) { PlaySwimSound(); #if !defined( CLIENT_DLL )
player->Splash(); #endif
} }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CGameMovement::FullObserverMove( void ) { int mode = player->GetObserverMode();
if ( mode == OBS_MODE_IN_EYE || mode == OBS_MODE_CHASE || mode == OBS_MODE_POI ) { CBaseEntity * target = player->GetObserverTarget();
if ( target != NULL ) { mv->SetAbsOrigin( target->GetAbsOrigin() ); mv->m_vecViewAngles = target->GetAbsAngles(); mv->m_vecVelocity = target->GetAbsVelocity(); }
return; }
if ( mode != OBS_MODE_ROAMING ) { // don't move in fixed or death cam mode
return; }
if ( sv_specnoclip.GetBool() ) { // roam in noclip mode
FullNoClipMove( sv_specspeed.GetFloat(), sv_specaccelerate.GetFloat() ); return; }
// do a full clipped free roam move:
Vector wishvel; Vector forward, right, up; Vector wishdir, wishend; float wishspeed;
AngleVectors (mv->m_vecViewAngles, &forward, &right, &up); // Determine movement angles
// Copy movement amounts
float factor = sv_specspeed.GetFloat();
if ( mv->m_nButtons & IN_SPEED ) { factor /= 2.0f; }
float fmove = mv->m_flForwardMove * factor; float smove = mv->m_flSideMove * factor; VectorNormalize (forward); // Normalize remainder of vectors
VectorNormalize (right); //
for (int i=0 ; i<3 ; i++) // Determine x and y parts of velocity
wishvel[i] = forward[i]*fmove + right[i]*smove; wishvel[2] += mv->m_flUpMove;
VectorCopy (wishvel, wishdir); // Determine maginitude of speed of move
wishspeed = VectorNormalize(wishdir);
//
// Clamp to server defined max speed
//
float maxspeed = sv_maxvelocity.GetFloat();
if (wishspeed > maxspeed ) { VectorScale (wishvel, mv->m_flMaxSpeed/wishspeed, wishvel); wishspeed = maxspeed; }
// Set pmove velocity, give observer 50% acceration bonus
Accelerate ( wishdir, wishspeed, sv_specaccelerate.GetFloat() );
float spd = VectorLength( mv->m_vecVelocity ); if (spd < 1.0f) { mv->m_vecVelocity.Init(); return; } float friction = sv_friction.GetFloat(); // Add the amount to the drop amount.
float drop = spd * friction * gpGlobals->frametime;
// scale the velocity
float newspeed = spd - drop;
if (newspeed < 0) newspeed = 0;
// Determine proportion of old speed we are using.
newspeed /= spd;
VectorScale( mv->m_vecVelocity, newspeed, mv->m_vecVelocity );
CheckVelocity();
TryPlayerMove(); }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CGameMovement::FullNoClipMove( float factor, float maxacceleration ) { Vector wishvel; Vector forward, right, up; Vector wishdir; float wishspeed; float maxspeed = sv_maxspeed.GetFloat() * factor;
AngleVectors (mv->m_vecViewAngles, &forward, &right, &up); // Determine movement angles
if ( mv->m_nButtons & IN_SPEED ) { factor /= 2.0f; } // Copy movement amounts
float fmove = mv->m_flForwardMove * factor; float smove = mv->m_flSideMove * factor; VectorNormalize (forward); // Normalize remainder of vectors
VectorNormalize (right); //
for (int i=0 ; i<3 ; i++) // Determine x and y parts of velocity
wishvel[i] = forward[i]*fmove + right[i]*smove; wishvel[2] += mv->m_flUpMove * factor;
VectorCopy (wishvel, wishdir); // Determine maginitude of speed of move
wishspeed = VectorNormalize(wishdir);
//
// Clamp to server defined max speed
//
if (wishspeed > maxspeed ) { VectorScale (wishvel, maxspeed/wishspeed, wishvel); wishspeed = maxspeed; }
if ( maxacceleration > 0.0 ) { // Set pmove velocity
Accelerate ( wishdir, wishspeed, maxacceleration );
float spd = VectorLength( mv->m_vecVelocity ); if (spd < 1.0f) { mv->m_vecVelocity.Init(); return; } // Bleed off some speed, but if we have less than the bleed
// threshhold, bleed the theshold amount.
float control = (spd < maxspeed/4.0) ? maxspeed/4.0 : spd; float friction = sv_friction.GetFloat() * player->m_surfaceFriction; // Add the amount to the drop amount.
float drop = control * friction * gpGlobals->frametime;
// scale the velocity
float newspeed = spd - drop; if (newspeed < 0) newspeed = 0;
// Determine proportion of old speed we are using.
newspeed /= spd; VectorScale( mv->m_vecVelocity, newspeed, mv->m_vecVelocity ); } else { VectorCopy( wishvel, mv->m_vecVelocity ); }
// Just move ( don't clip or anything )
Vector out; VectorMA( mv->GetAbsOrigin(), gpGlobals->frametime, mv->m_vecVelocity, out ); mv->SetAbsOrigin( out );
// Zero out velocity if in noaccel mode
if ( maxacceleration < 0.0f ) { mv->m_vecVelocity.Init(); } }
//-----------------------------------------------------------------------------
// Checks to see if we should actually jump
//-----------------------------------------------------------------------------
void CGameMovement::PlaySwimSound() { MoveHelper()->StartSound( mv->GetAbsOrigin(), "Player.Swim" ); }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CGameMovement::CheckJumpButton( void ) { if (player->pl.deadflag) { mv->m_nOldButtons |= IN_JUMP ; // don't jump again until released
return false; }
// See if we are waterjumping. If so, decrement count and return.
if (player->m_flWaterJumpTime) { player->m_flWaterJumpTime -= gpGlobals->frametime; if (player->m_flWaterJumpTime < 0) player->m_flWaterJumpTime = 0; return false; }
// If we are in the water most of the way...
if ( player->GetWaterLevel() >= 2 ) { // swimming, not jumping
SetGroundEntity( NULL );
if(player->GetWaterType() == CONTENTS_WATER) // We move up a certain amount
mv->m_vecVelocity[2] = 100; else if (player->GetWaterType() == CONTENTS_SLIME) mv->m_vecVelocity[2] = 80; // play swiming sound
if ( player->m_flSwimSoundTime <= 0 ) { // Don't play sound again for 1 second
player->m_flSwimSoundTime = 1000; PlaySwimSound(); }
return false; }
// No more effect
if (player->GetGroundEntity() == NULL) { mv->m_nOldButtons |= IN_JUMP; return false; // in air, so no effect
}
// Don't allow jumping when the player is in a stasis field.
#ifndef HL2_EPISODIC
if ( player->m_Local.m_bSlowMovement ) return false; #endif
if ( mv->m_nOldButtons & IN_JUMP ) return false; // don't pogo stick
// Cannot jump will in the unduck transition.
if ( player->m_Local.m_bDucking && ( player->GetFlags() & FL_DUCKING ) ) return false;
// Still updating the eye position.
if ( player->m_Local.m_flDuckJumpTime > 0.0f ) return false;
// In the air now.
SetGroundEntity( NULL ); player->PlayStepSound( (Vector &)mv->GetAbsOrigin(), player->m_pSurfaceData, 1.0, true ); MoveHelper()->PlayerSetAnimation( PLAYER_JUMP );
float flGroundFactor = 1.0f; if (player->m_pSurfaceData) { flGroundFactor = player->m_pSurfaceData->game.jumpFactor; }
float flMul; if ( g_bMovementOptimizations ) { #if defined(HL2_DLL) || defined(HL2_CLIENT_DLL)
Assert( GetCurrentGravity() == 600.0f ); flMul = 160.0f; // approx. 21 units.
#else
Assert( GetCurrentGravity() == 800.0f ); flMul = 268.3281572999747f; #endif
} else { flMul = sqrt(2 * GetCurrentGravity() * GAMEMOVEMENT_JUMP_HEIGHT); }
// Acclerate upward
// If we are ducking...
float startz = mv->m_vecVelocity[2]; if ( ( player->m_Local.m_bDucking ) || ( player->GetFlags() & FL_DUCKING ) ) { // d = 0.5 * g * t^2 - distance traveled with linear accel
// t = sqrt(2.0 * 45 / g) - how long to fall 45 units
// v = g * t - velocity at the end (just invert it to jump up that high)
// v = g * sqrt(2.0 * 45 / g )
// v^2 = g * g * 2.0 * 45 / g
// v = sqrt( g * 2.0 * 45 )
mv->m_vecVelocity[2] = flGroundFactor * flMul; // 2 * gravity * height
} else { mv->m_vecVelocity[2] += flGroundFactor * flMul; // 2 * gravity * height
}
// Add a little forward velocity based on your current forward velocity - if you are not sprinting.
#if defined( HL2_DLL ) || defined( HL2_CLIENT_DLL )
if ( gpGlobals->maxClients == 1 ) { CHLMoveData *pMoveData = ( CHLMoveData* )mv; Vector vecForward; AngleVectors( mv->m_vecViewAngles, &vecForward ); vecForward.z = 0; VectorNormalize( vecForward ); // We give a certain percentage of the current forward movement as a bonus to the jump speed. That bonus is clipped
// to not accumulate over time.
float flSpeedBoostPerc = ( !pMoveData->m_bIsSprinting && !player->m_Local.m_bDucked ) ? 0.5f : 0.1f; float flSpeedAddition = fabs( mv->m_flForwardMove * flSpeedBoostPerc ); float flMaxSpeed = mv->m_flMaxSpeed + ( mv->m_flMaxSpeed * flSpeedBoostPerc ); float flNewSpeed = ( flSpeedAddition + mv->m_vecVelocity.Length2D() );
// If we're over the maximum, we want to only boost as much as will get us to the goal speed
if ( flNewSpeed > flMaxSpeed ) { flSpeedAddition -= flNewSpeed - flMaxSpeed; }
if ( mv->m_flForwardMove < 0.0f ) flSpeedAddition *= -1.0f;
// Add it on
VectorAdd( (vecForward*flSpeedAddition), mv->m_vecVelocity, mv->m_vecVelocity ); } #endif
FinishGravity();
CheckV( player->CurrentCommandNumber(), "CheckJump", mv->m_vecVelocity );
mv->m_outJumpVel.z += mv->m_vecVelocity[2] - startz; mv->m_outStepHeight += 0.15f;
OnJump(mv->m_outJumpVel.z);
// Set jump time.
if ( gpGlobals->maxClients == 1 ) { player->m_Local.m_flJumpTime = GAMEMOVEMENT_JUMP_TIME; player->m_Local.m_bInDuckJump = true; }
#if defined( HL2_DLL )
if ( xc_uncrouch_on_jump.GetBool() ) { // Uncrouch when jumping
if ( player->GetToggledDuckState() ) { player->ToggleDuck(); } }
#endif
// Flag that we jumped.
mv->m_nOldButtons |= IN_JUMP; // don't jump again until released
return true; }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CGameMovement::FullLadderMove() { CheckWater();
// Was jump button pressed? If so, set velocity to 270 away from ladder.
if ( mv->m_nButtons & IN_JUMP ) { CheckJumpButton(); } else { mv->m_nOldButtons &= ~IN_JUMP; } // Perform the move accounting for any base velocity.
VectorAdd (mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity); TryPlayerMove(); VectorSubtract (mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity); }
//-----------------------------------------------------------------------------
// Purpose:
// Output : int
//-----------------------------------------------------------------------------
int CGameMovement::TryPlayerMove( Vector *pFirstDest, trace_t *pFirstTrace ) { int bumpcount, numbumps; Vector dir; float d; int numplanes; Vector planes[MAX_CLIP_PLANES]; Vector primal_velocity, original_velocity; Vector new_velocity; int i, j; trace_t pm; Vector end; float time_left, allFraction; int blocked; numbumps = 4; // Bump up to four times
blocked = 0; // Assume not blocked
numplanes = 0; // and not sliding along any planes
VectorCopy (mv->m_vecVelocity, original_velocity); // Store original velocity
VectorCopy (mv->m_vecVelocity, primal_velocity); allFraction = 0; time_left = gpGlobals->frametime; // Total time for this movement operation.
new_velocity.Init();
for (bumpcount=0 ; bumpcount < numbumps; bumpcount++) { if ( mv->m_vecVelocity.Length() == 0.0 ) break;
// Assume we can move all the way from the current origin to the
// end point.
VectorMA( mv->GetAbsOrigin(), time_left, mv->m_vecVelocity, end );
// See if we can make it from origin to end point.
if ( g_bMovementOptimizations ) { // If their velocity Z is 0, then we can avoid an extra trace here during WalkMove.
if ( pFirstDest && end == *pFirstDest ) pm = *pFirstTrace; else { #if defined( PLAYER_GETTING_STUCK_TESTING )
trace_t foo; TracePlayerBBox( mv->GetAbsOrigin(), mv->GetAbsOrigin(), PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, foo ); if ( foo.startsolid || foo.fraction != 1.0f ) { Msg( "bah\n" ); } #endif
TracePlayerBBox( mv->GetAbsOrigin(), end, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, pm ); } } else { TracePlayerBBox( mv->GetAbsOrigin(), end, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, pm ); }
allFraction += pm.fraction;
// If we started in a solid object, or we were in solid space
// the whole way, zero out our velocity and return that we
// are blocked by floor and wall.
if (pm.allsolid) { // entity is trapped in another solid
VectorCopy (vec3_origin, mv->m_vecVelocity); return 4; }
// If we moved some portion of the total distance, then
// copy the end position into the pmove.origin and
// zero the plane counter.
if( pm.fraction > 0 ) { if ( numbumps > 0 && pm.fraction == 1 ) { // There's a precision issue with terrain tracing that can cause a swept box to successfully trace
// when the end position is stuck in the triangle. Re-run the test with an uswept box to catch that
// case until the bug is fixed.
// If we detect getting stuck, don't allow the movement
trace_t stuck; TracePlayerBBox( pm.endpos, pm.endpos, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, stuck ); if ( stuck.startsolid || stuck.fraction != 1.0f ) { //Msg( "Player will become stuck!!!\n" );
VectorCopy (vec3_origin, mv->m_vecVelocity); break; } }
#if defined( PLAYER_GETTING_STUCK_TESTING )
trace_t foo; TracePlayerBBox( pm.endpos, pm.endpos, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, foo ); if ( foo.startsolid || foo.fraction != 1.0f ) { Msg( "Player will become stuck!!!\n" ); } #endif
// actually covered some distance
mv->SetAbsOrigin( pm.endpos); VectorCopy (mv->m_vecVelocity, original_velocity); numplanes = 0; }
// If we covered the entire distance, we are done
// and can return.
if (pm.fraction == 1) { break; // moved the entire distance
}
// Save entity that blocked us (since fraction was < 1.0)
// for contact
// Add it if it's not already in the list!!!
MoveHelper( )->AddToTouched( pm, mv->m_vecVelocity );
// If the plane we hit has a high z component in the normal, then
// it's probably a floor
if (pm.plane.normal[2] > 0.7) { blocked |= 1; // floor
} // If the plane has a zero z component in the normal, then it's a
// step or wall
if (!pm.plane.normal[2]) { blocked |= 2; // step / wall
}
// Reduce amount of m_flFrameTime left by total time left * fraction
// that we covered.
time_left -= time_left * pm.fraction;
// Did we run out of planes to clip against?
if (numplanes >= MAX_CLIP_PLANES) { // this shouldn't really happen
// Stop our movement if so.
VectorCopy (vec3_origin, mv->m_vecVelocity); //Con_DPrintf("Too many planes 4\n");
break; }
// Set up next clipping plane
VectorCopy (pm.plane.normal, planes[numplanes]); numplanes++;
// modify original_velocity so it parallels all of the clip planes
//
// reflect player velocity
// Only give this a try for first impact plane because you can get yourself stuck in an acute corner by jumping in place
// and pressing forward and nobody was really using this bounce/reflection feature anyway...
if ( numplanes == 1 && player->GetMoveType() == MOVETYPE_WALK && player->GetGroundEntity() == NULL ) { for ( i = 0; i < numplanes; i++ ) { if ( planes[i][2] > 0.7 ) { // floor or slope
ClipVelocity( original_velocity, planes[i], new_velocity, 1 ); VectorCopy( new_velocity, original_velocity ); } else { ClipVelocity( original_velocity, planes[i], new_velocity, 1.0 + sv_bounce.GetFloat() * (1 - player->m_surfaceFriction) ); } }
VectorCopy( new_velocity, mv->m_vecVelocity ); VectorCopy( new_velocity, original_velocity ); } else { for (i=0 ; i < numplanes ; i++) { ClipVelocity ( original_velocity, planes[i], mv->m_vecVelocity, 1);
for (j=0 ; j<numplanes ; j++) if (j != i) { // Are we now moving against this plane?
if (mv->m_vecVelocity.Dot(planes[j]) < 0) break; // not ok
} if (j == numplanes) // Didn't have to clip, so we're ok
break; } // Did we go all the way through plane set
if (i != numplanes) { // go along this plane
// pmove.velocity is set in clipping call, no need to set again.
; } else { // go along the crease
if (numplanes != 2) { VectorCopy (vec3_origin, mv->m_vecVelocity); break; } CrossProduct (planes[0], planes[1], dir); dir.NormalizeInPlace(); d = dir.Dot(mv->m_vecVelocity); VectorScale (dir, d, mv->m_vecVelocity ); }
//
// if original velocity is against the original velocity, stop dead
// to avoid tiny occilations in sloping corners
//
d = mv->m_vecVelocity.Dot(primal_velocity); if (d <= 0) { //Con_DPrintf("Back\n");
VectorCopy (vec3_origin, mv->m_vecVelocity); break; } } }
if ( allFraction == 0 ) { VectorCopy (vec3_origin, mv->m_vecVelocity); }
// Check if they slammed into a wall
float fSlamVol = 0.0f;
float fLateralStoppingAmount = primal_velocity.Length2D() - mv->m_vecVelocity.Length2D(); if ( fLateralStoppingAmount > PLAYER_MAX_SAFE_FALL_SPEED * 2.0f ) { fSlamVol = 1.0f; } else if ( fLateralStoppingAmount > PLAYER_MAX_SAFE_FALL_SPEED ) { fSlamVol = 0.85f; }
PlayerRoughLandingEffects( fSlamVol );
return blocked; }
//-----------------------------------------------------------------------------
// Purpose: Determine whether or not the player is on a ladder (physprop or world).
//-----------------------------------------------------------------------------
inline bool CGameMovement::OnLadder( trace_t &trace ) { if ( trace.contents & CONTENTS_LADDER ) return true;
IPhysicsSurfaceProps *pPhysProps = MoveHelper( )->GetSurfaceProps(); if ( pPhysProps ) { const surfacedata_t *pSurfaceData = pPhysProps->GetSurfaceData( trace.surface.surfaceProps ); if ( pSurfaceData ) { if ( pSurfaceData->game.climbable != 0 ) return true; } }
return false; }
//=============================================================================
// HPE_BEGIN
// [sbodenbender] make ladders easier to climb in cstrike
//=============================================================================
#if defined (CSTRIKE_DLL)
ConVar sv_ladder_dampen ( "sv_ladder_dampen", "0.2", FCVAR_REPLICATED, "Amount to dampen perpendicular movement on a ladder", true, 0.0f, true, 1.0f ); ConVar sv_ladder_angle( "sv_ladder_angle", "-0.707", FCVAR_REPLICATED, "Cos of angle of incidence to ladder perpendicular for applying ladder_dampen", true, -1.0f, true, 1.0f ); #endif
//=============================================================================
// HPE_END
//=============================================================================
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CGameMovement::LadderMove( void ) { trace_t pm; bool onFloor; Vector floor; Vector wishdir; Vector end;
if ( player->GetMoveType() == MOVETYPE_NOCLIP ) return false;
if ( !GameHasLadders() ) return false;
// If I'm already moving on a ladder, use the previous ladder direction
if ( player->GetMoveType() == MOVETYPE_LADDER ) { wishdir = -player->m_vecLadderNormal; } else { // otherwise, use the direction player is attempting to move
if ( mv->m_flForwardMove || mv->m_flSideMove ) { for (int i=0 ; i<3 ; i++) // Determine x and y parts of velocity
wishdir[i] = m_vecForward[i]*mv->m_flForwardMove + m_vecRight[i]*mv->m_flSideMove;
VectorNormalize(wishdir); } else { // Player is not attempting to move, no ladder behavior
return false; } }
// wishdir points toward the ladder if any exists
VectorMA( mv->GetAbsOrigin(), LadderDistance(), wishdir, end ); TracePlayerBBox( mv->GetAbsOrigin(), end, LadderMask(), COLLISION_GROUP_PLAYER_MOVEMENT, pm );
// no ladder in that direction, return
if ( pm.fraction == 1.0f || !OnLadder( pm ) ) return false;
player->SetMoveType( MOVETYPE_LADDER ); player->SetMoveCollide( MOVECOLLIDE_DEFAULT );
player->m_vecLadderNormal = pm.plane.normal;
// On ladder, convert movement to be relative to the ladder
VectorCopy( mv->GetAbsOrigin(), floor ); floor[2] += GetPlayerMins()[2] - 1;
if( enginetrace->GetPointContents( floor ) == CONTENTS_SOLID || player->GetGroundEntity() != NULL ) { onFloor = true; } else { onFloor = false; }
player->SetGravity( 0 );
float climbSpeed = ClimbSpeed();
float forwardSpeed = 0, rightSpeed = 0; if ( mv->m_nButtons & IN_BACK ) forwardSpeed -= climbSpeed; if ( mv->m_nButtons & IN_FORWARD ) forwardSpeed += climbSpeed; if ( mv->m_nButtons & IN_MOVELEFT ) rightSpeed -= climbSpeed; if ( mv->m_nButtons & IN_MOVERIGHT ) rightSpeed += climbSpeed;
if ( mv->m_nButtons & IN_JUMP ) { player->SetMoveType( MOVETYPE_WALK ); player->SetMoveCollide( MOVECOLLIDE_DEFAULT );
VectorScale( pm.plane.normal, 270, mv->m_vecVelocity ); } else { if ( forwardSpeed != 0 || rightSpeed != 0 ) { Vector velocity, perp, cross, lateral, tmp;
//ALERT(at_console, "pev %.2f %.2f %.2f - ",
// pev->velocity.x, pev->velocity.y, pev->velocity.z);
// Calculate player's intended velocity
//Vector velocity = (forward * gpGlobals->v_forward) + (right * gpGlobals->v_right);
VectorScale( m_vecForward, forwardSpeed, velocity ); VectorMA( velocity, rightSpeed, m_vecRight, velocity );
// Perpendicular in the ladder plane
VectorCopy( vec3_origin, tmp ); tmp[2] = 1; CrossProduct( tmp, pm.plane.normal, perp ); VectorNormalize( perp );
// decompose velocity into ladder plane
float normal = DotProduct( velocity, pm.plane.normal );
// This is the velocity into the face of the ladder
VectorScale( pm.plane.normal, normal, cross );
// This is the player's additional velocity
VectorSubtract( velocity, cross, lateral );
// This turns the velocity into the face of the ladder into velocity that
// is roughly vertically perpendicular to the face of the ladder.
// NOTE: It IS possible to face up and move down or face down and move up
// because the velocity is a sum of the directional velocity and the converted
// velocity through the face of the ladder -- by design.
CrossProduct( pm.plane.normal, perp, tmp );
//=============================================================================
// HPE_BEGIN
// [sbodenbender] make ladders easier to climb in cstrike
//=============================================================================
#if defined (CSTRIKE_DLL)
// break lateral into direction along tmp (up the ladder) and direction along perp (perpendicular to ladder)
float tmpDist = DotProduct ( tmp, lateral ); float perpDist = DotProduct ( perp, lateral );
Vector angleVec = perp * perpDist; angleVec += cross; // angleVec is our desired movement in the ladder normal/perpendicular plane
VectorNormalize(angleVec); float angleDot = DotProduct(angleVec, pm.plane.normal); // angleDot is our angle of incidence to the laddernormal in the ladder normal/perpendicular plane
if (angleDot < sv_ladder_angle.GetFloat()) lateral = (tmp * tmpDist) + (perp * sv_ladder_dampen.GetFloat() * perpDist); #endif // CSTRIKE_DLL
//=============================================================================
// HPE_END
//=============================================================================
VectorMA( lateral, -normal, tmp, mv->m_vecVelocity );
if ( onFloor && normal > 0 ) // On ground moving away from the ladder
{ VectorMA( mv->m_vecVelocity, MAX_CLIMB_SPEED, pm.plane.normal, mv->m_vecVelocity ); } //pev->velocity = lateral - (CrossProduct( trace.vecPlaneNormal, perp ) * normal);
} else { mv->m_vecVelocity.Init(); } }
return true; }
//-----------------------------------------------------------------------------
// Purpose:
// Input : axis -
// Output : const char
//-----------------------------------------------------------------------------
#if !defined(_STATIC_LINKED) || defined(CLIENT_DLL)
const char *DescribeAxis( int axis ) { static char sz[ 32 ];
switch ( axis ) { case 0: Q_strncpy( sz, "X", sizeof( sz ) ); break; case 1: Q_strncpy( sz, "Y", sizeof( sz ) ); break; case 2: default: Q_strncpy( sz, "Z", sizeof( sz ) ); break; }
return sz; } #else
const char *DescribeAxis( int axis ); #endif
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CGameMovement::CheckVelocity( void ) { int i;
//
// bound velocity
//
Vector org = mv->GetAbsOrigin();
for (i=0; i < 3; i++) { // See if it's bogus.
if (IS_NAN(mv->m_vecVelocity[i])) { DevMsg( 1, "PM Got a NaN velocity %s\n", DescribeAxis( i ) ); mv->m_vecVelocity[i] = 0; }
if (IS_NAN(org[i])) { DevMsg( 1, "PM Got a NaN origin on %s\n", DescribeAxis( i ) ); org[ i ] = 0; mv->SetAbsOrigin( org ); }
// Bound it.
if (mv->m_vecVelocity[i] > sv_maxvelocity.GetFloat()) { DevMsg( 1, "PM Got a velocity too high on %s\n", DescribeAxis( i ) ); mv->m_vecVelocity[i] = sv_maxvelocity.GetFloat(); } else if (mv->m_vecVelocity[i] < -sv_maxvelocity.GetFloat()) { DevMsg( 1, "PM Got a velocity too low on %s\n", DescribeAxis( i ) ); mv->m_vecVelocity[i] = -sv_maxvelocity.GetFloat(); } } }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CGameMovement::AddGravity( void ) { float ent_gravity;
if ( player->m_flWaterJumpTime ) return;
if (player->GetGravity()) ent_gravity = player->GetGravity(); else ent_gravity = 1.0;
// Add gravity incorrectly
mv->m_vecVelocity[2] -= (ent_gravity * GetCurrentGravity() * gpGlobals->frametime); mv->m_vecVelocity[2] += player->GetBaseVelocity()[2] * gpGlobals->frametime; Vector temp = player->GetBaseVelocity(); temp[2] = 0; player->SetBaseVelocity( temp ); CheckVelocity(); }
//-----------------------------------------------------------------------------
// Purpose:
// Input : push -
// Output : trace_t
//-----------------------------------------------------------------------------
void CGameMovement::PushEntity( Vector& push, trace_t *pTrace ) { Vector end; VectorAdd (mv->GetAbsOrigin(), push, end); TracePlayerBBox( mv->GetAbsOrigin(), end, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, *pTrace ); mv->SetAbsOrigin( pTrace->endpos );
// So we can run impact function afterwards.
// If
if ( pTrace->fraction < 1.0 && !pTrace->allsolid ) { MoveHelper( )->AddToTouched( *pTrace, mv->m_vecVelocity ); } }
//-----------------------------------------------------------------------------
// Purpose:
// Input : in -
// normal -
// out -
// overbounce -
// Output : int
//-----------------------------------------------------------------------------
int CGameMovement::ClipVelocity( Vector& in, Vector& normal, Vector& out, float overbounce ) { float backoff; float change; float angle; int i, blocked; angle = normal[ 2 ];
blocked = 0x00; // Assume unblocked.
if (angle > 0) // If the plane that is blocking us has a positive z component, then assume it's a floor.
blocked |= 0x01; //
if (!angle) // If the plane has no Z, it is vertical (wall/step)
blocked |= 0x02; //
// Determine how far along plane to slide based on incoming direction.
backoff = DotProduct (in, normal) * overbounce;
for (i=0 ; i<3 ; i++) { change = normal[i]*backoff; out[i] = in[i] - change; } // iterate once to make sure we aren't still moving through the plane
float adjust = DotProduct( out, normal ); if( adjust < 0.0f ) { out -= ( normal * adjust ); // Msg( "Adjustment = %lf\n", adjust );
}
// Return blocking flags.
return blocked; }
//-----------------------------------------------------------------------------
// Purpose: Computes roll angle for a certain movement direction and velocity
// Input : angles -
// velocity -
// rollangle -
// rollspeed -
// Output : float
//-----------------------------------------------------------------------------
float CGameMovement::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); side = DotProduct (velocity, right); sign = side < 0 ? -1 : 1; side = fabs(side); value = rollangle; if (side < rollspeed) { side = side * value / rollspeed; } else { side = value; } return side*sign; }
#define CHECKSTUCK_MINTIME 0.05 // Don't check again too quickly.
#if !defined(_STATIC_LINKED) || defined(CLIENT_DLL)
Vector rgv3tStuckTable[54]; #else
extern Vector rgv3tStuckTable[54]; #endif
#if !defined(_STATIC_LINKED) || defined(CLIENT_DLL)
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CreateStuckTable( void ) { float x, y, z; int idx; int i; float zi[3]; static int firsttime = 1;
if ( !firsttime ) return;
firsttime = 0;
memset(rgv3tStuckTable, 0, sizeof(rgv3tStuckTable));
idx = 0; // Little Moves.
x = y = 0; // Z moves
for (z = -0.125 ; z <= 0.125 ; z += 0.125) { rgv3tStuckTable[idx][0] = x; rgv3tStuckTable[idx][1] = y; rgv3tStuckTable[idx][2] = z; idx++; } x = z = 0; // Y moves
for (y = -0.125 ; y <= 0.125 ; y += 0.125) { rgv3tStuckTable[idx][0] = x; rgv3tStuckTable[idx][1] = y; rgv3tStuckTable[idx][2] = z; idx++; } y = z = 0; // X moves
for (x = -0.125 ; x <= 0.125 ; x += 0.125) { rgv3tStuckTable[idx][0] = x; rgv3tStuckTable[idx][1] = y; rgv3tStuckTable[idx][2] = z; idx++; }
// Remaining multi axis nudges.
for ( x = - 0.125; x <= 0.125; x += 0.250 ) { for ( y = - 0.125; y <= 0.125; y += 0.250 ) { for ( z = - 0.125; z <= 0.125; z += 0.250 ) { rgv3tStuckTable[idx][0] = x; rgv3tStuckTable[idx][1] = y; rgv3tStuckTable[idx][2] = z; idx++; } } }
// Big Moves.
x = y = 0; zi[0] = 0.0f; zi[1] = 1.0f; zi[2] = 6.0f;
for (i = 0; i < 3; i++) { // Z moves
z = zi[i]; rgv3tStuckTable[idx][0] = x; rgv3tStuckTable[idx][1] = y; rgv3tStuckTable[idx][2] = z; idx++; }
x = z = 0;
// Y moves
for (y = -2.0f ; y <= 2.0f ; y += 2.0) { rgv3tStuckTable[idx][0] = x; rgv3tStuckTable[idx][1] = y; rgv3tStuckTable[idx][2] = z; idx++; } y = z = 0; // X moves
for (x = -2.0f ; x <= 2.0f ; x += 2.0f) { rgv3tStuckTable[idx][0] = x; rgv3tStuckTable[idx][1] = y; rgv3tStuckTable[idx][2] = z; idx++; }
// Remaining multi axis nudges.
for (i = 0 ; i < 3; i++) { z = zi[i]; for (x = -2.0f ; x <= 2.0f ; x += 2.0f) { for (y = -2.0f ; y <= 2.0f ; y += 2.0) { rgv3tStuckTable[idx][0] = x; rgv3tStuckTable[idx][1] = y; rgv3tStuckTable[idx][2] = z; idx++; } } } Assert( idx < sizeof(rgv3tStuckTable)/sizeof(rgv3tStuckTable[0])); } #else
extern void CreateStuckTable( void ); #endif
//-----------------------------------------------------------------------------
// Purpose:
// Input : nIndex -
// server -
// offset -
// Output : int
//-----------------------------------------------------------------------------
int GetRandomStuckOffsets( CBasePlayer *pPlayer, Vector& offset) { // Last time we did a full
int idx; idx = pPlayer->m_StuckLast++;
VectorCopy(rgv3tStuckTable[idx % 54], offset);
return (idx % 54); }
//-----------------------------------------------------------------------------
// Purpose:
// Input : nIndex -
// server -
//-----------------------------------------------------------------------------
void ResetStuckOffsets( CBasePlayer *pPlayer ) { pPlayer->m_StuckLast = 0; }
//-----------------------------------------------------------------------------
// Purpose:
// Input : &input -
// Output : int
//-----------------------------------------------------------------------------
int CGameMovement::CheckStuck( void ) { Vector base; Vector offset; Vector test; EntityHandle_t hitent; int idx; float fTime; trace_t traceresult;
CreateStuckTable();
hitent = TestPlayerPosition( mv->GetAbsOrigin(), COLLISION_GROUP_PLAYER_MOVEMENT, traceresult ); if ( hitent == INVALID_ENTITY_HANDLE ) { ResetStuckOffsets( player ); return 0; }
// Deal with stuckness...
#ifndef DEDICATED
if ( developer.GetBool() ) { bool isServer = player->IsServer(); engine->Con_NPrintf( isServer, "%s stuck on object %i/%s", isServer ? "server" : "client", hitent.GetEntryIndex(), MoveHelper()->GetName(hitent) ); } #endif
VectorCopy( mv->GetAbsOrigin(), base );
//
// Deal with precision error in network.
//
// World or BSP model
if ( !player->IsServer() ) { if ( MoveHelper()->IsWorldEntity( hitent ) ) { int nReps = 0; ResetStuckOffsets( player ); do { GetRandomStuckOffsets( player, offset ); VectorAdd( base, offset, test ); if ( TestPlayerPosition( test, COLLISION_GROUP_PLAYER_MOVEMENT, traceresult ) == INVALID_ENTITY_HANDLE ) { ResetStuckOffsets( player ); mv->SetAbsOrigin( test ); return 0; } nReps++; } while (nReps < 54); } }
// Only an issue on the client.
idx = player->IsServer() ? 0 : 1;
fTime = engine->Time(); // Too soon?
if ( m_flStuckCheckTime[ player->entindex() ][ idx ] >= fTime - CHECKSTUCK_MINTIME ) { return 1; } m_flStuckCheckTime[ player->entindex() ][ idx ] = fTime;
MoveHelper( )->AddToTouched( traceresult, mv->m_vecVelocity ); GetRandomStuckOffsets( player, offset ); VectorAdd( base, offset, test );
if ( TestPlayerPosition( test, COLLISION_GROUP_PLAYER_MOVEMENT, traceresult ) == INVALID_ENTITY_HANDLE) { ResetStuckOffsets( player ); mv->SetAbsOrigin( test ); return 0; }
return 1; }
//-----------------------------------------------------------------------------
// Purpose:
// Output : bool
//-----------------------------------------------------------------------------
bool CGameMovement::InWater( void ) { return ( player->GetWaterLevel() > WL_Feet ); }
void CGameMovement::ResetGetPointContentsCache() { for ( int slot = 0; slot < MAX_PC_CACHE_SLOTS; ++slot ) { for ( int i = 0; i < MAX_PLAYERS; ++i ) { m_CachedGetPointContents[ i ][ slot ] = -9999; } } }
int CGameMovement::GetPointContentsCached( const Vector &point, int slot ) { if ( g_bMovementOptimizations ) { Assert( player ); Assert( slot >= 0 && slot < MAX_PC_CACHE_SLOTS );
int idx = player->entindex() - 1;
if ( m_CachedGetPointContents[ idx ][ slot ] == -9999 || point.DistToSqr( m_CachedGetPointContentsPoint[ idx ][ slot ] ) > 1 ) { m_CachedGetPointContents[ idx ][ slot ] = enginetrace->GetPointContents ( point ); m_CachedGetPointContentsPoint[ idx ][ slot ] = point; } return m_CachedGetPointContents[ idx ][ slot ]; } else { return enginetrace->GetPointContents ( point ); } }
//-----------------------------------------------------------------------------
// Purpose:
// Input : &input -
// Output : bool
//-----------------------------------------------------------------------------
bool CGameMovement::CheckWater( void ) { Vector point; int cont;
Vector vPlayerMins = GetPlayerMins(); Vector vPlayerMaxs = GetPlayerMaxs();
// Pick a spot just above the players feet.
point[0] = mv->GetAbsOrigin()[0] + (vPlayerMins[0] + vPlayerMaxs[0]) * 0.5; point[1] = mv->GetAbsOrigin()[1] + (vPlayerMins[1] + vPlayerMaxs[1]) * 0.5; point[2] = mv->GetAbsOrigin()[2] + vPlayerMins[2] + 1; // Assume that we are not in water at all.
player->SetWaterLevel( WL_NotInWater ); player->SetWaterType( CONTENTS_EMPTY );
// Grab point contents.
cont = GetPointContentsCached( point, 0 ); // Are we under water? (not solid and not empty?)
if ( cont & MASK_WATER ) { // Set water type
player->SetWaterType( cont );
// We are at least at level one
player->SetWaterLevel( WL_Feet );
// Now check a point that is at the player hull midpoint.
point[2] = mv->GetAbsOrigin()[2] + (vPlayerMins[2] + vPlayerMaxs[2])*0.5; cont = GetPointContentsCached( point, 1 ); // If that point is also under water...
if ( cont & MASK_WATER ) { // Set a higher water level.
player->SetWaterLevel( WL_Waist );
// Now check the eye position. (view_ofs is relative to the origin)
point[2] = mv->GetAbsOrigin()[2] + player->GetViewOffset()[2]; cont = GetPointContentsCached( point, 2 ); if ( cont & MASK_WATER ) player->SetWaterLevel( WL_Eyes ); // In over our eyes
}
// Adjust velocity based on water current, if any.
if ( cont & MASK_CURRENT ) { Vector v; VectorClear(v); if ( cont & CONTENTS_CURRENT_0 ) v[0] += 1; if ( cont & CONTENTS_CURRENT_90 ) v[1] += 1; if ( cont & CONTENTS_CURRENT_180 ) v[0] -= 1; if ( cont & CONTENTS_CURRENT_270 ) v[1] -= 1; if ( cont & CONTENTS_CURRENT_UP ) v[2] += 1; if ( cont & CONTENTS_CURRENT_DOWN ) v[2] -= 1;
// BUGBUG -- this depends on the value of an unspecified enumerated type
// The deeper we are, the stronger the current.
Vector temp; VectorMA( player->GetBaseVelocity(), 50.0*player->GetWaterLevel(), v, temp ); player->SetBaseVelocity( temp ); } }
// if we just transitioned from not in water to in water, record the time it happened
if ( ( WL_NotInWater == m_nOldWaterLevel ) && ( player->GetWaterLevel() > WL_NotInWater ) ) { m_flWaterEntryTime = gpGlobals->curtime; }
return ( player->GetWaterLevel() > WL_Feet ); }
void CGameMovement::SetGroundEntity( trace_t *pm ) { CBaseEntity *newGround = pm ? pm->m_pEnt : NULL;
CBaseEntity *oldGround = player->GetGroundEntity(); Vector vecBaseVelocity = player->GetBaseVelocity();
if ( !oldGround && newGround ) { // Subtract ground velocity at instant we hit ground jumping
vecBaseVelocity -= newGround->GetAbsVelocity(); vecBaseVelocity.z = newGround->GetAbsVelocity().z; } else if ( oldGround && !newGround ) { // Add in ground velocity at instant we started jumping
vecBaseVelocity += oldGround->GetAbsVelocity(); vecBaseVelocity.z = oldGround->GetAbsVelocity().z; }
player->SetBaseVelocity( vecBaseVelocity ); player->SetGroundEntity( newGround );
// If we are on something...
if ( newGround ) { CategorizeGroundSurface( *pm );
// Then we are not in water jump sequence
player->m_flWaterJumpTime = 0;
// Standing on an entity other than the world, so signal that we are touching something.
if ( !pm->DidHitWorld() ) { MoveHelper()->AddToTouched( *pm, mv->m_vecVelocity ); }
mv->m_vecVelocity.z = 0.0f; } }
//-----------------------------------------------------------------------------
// Traces the player's collision bounds in quadrants, looking for a plane that
// can be stood upon (normal's z >= 0.7f). Regardless of success or failure,
// replace the fraction and endpos with the original ones, so we don't try to
// move the player down to the new floor and get stuck on a leaning wall that
// the original trace hit first.
//-----------------------------------------------------------------------------
void TracePlayerBBoxForGround( const Vector& start, const Vector& end, const Vector& minsSrc, const Vector& maxsSrc, IHandleEntity *player, unsigned int fMask, int collisionGroup, trace_t& pm ) { VPROF( "TracePlayerBBoxForGround" );
Ray_t ray; Vector mins, maxs;
float fraction = pm.fraction; Vector endpos = pm.endpos;
// Check the -x, -y quadrant
mins = minsSrc; maxs.Init( MIN( 0, maxsSrc.x ), MIN( 0, maxsSrc.y ), maxsSrc.z ); ray.Init( start, end, mins, maxs ); UTIL_TraceRay( ray, fMask, player, collisionGroup, &pm ); if ( pm.m_pEnt && pm.plane.normal[2] >= 0.7) { pm.fraction = fraction; pm.endpos = endpos; return; }
// Check the +x, +y quadrant
mins.Init( MAX( 0, minsSrc.x ), MAX( 0, minsSrc.y ), minsSrc.z ); maxs = maxsSrc; ray.Init( start, end, mins, maxs ); UTIL_TraceRay( ray, fMask, player, collisionGroup, &pm ); if ( pm.m_pEnt && pm.plane.normal[2] >= 0.7) { pm.fraction = fraction; pm.endpos = endpos; return; }
// Check the -x, +y quadrant
mins.Init( minsSrc.x, MAX( 0, minsSrc.y ), minsSrc.z ); maxs.Init( MIN( 0, maxsSrc.x ), maxsSrc.y, maxsSrc.z ); ray.Init( start, end, mins, maxs ); UTIL_TraceRay( ray, fMask, player, collisionGroup, &pm ); if ( pm.m_pEnt && pm.plane.normal[2] >= 0.7) { pm.fraction = fraction; pm.endpos = endpos; return; }
// Check the +x, -y quadrant
mins.Init( MAX( 0, minsSrc.x ), minsSrc.y, minsSrc.z ); maxs.Init( maxsSrc.x, MIN( 0, maxsSrc.y ), maxsSrc.z ); ray.Init( start, end, mins, maxs ); UTIL_TraceRay( ray, fMask, player, collisionGroup, &pm ); if ( pm.m_pEnt && pm.plane.normal[2] >= 0.7) { pm.fraction = fraction; pm.endpos = endpos; return; }
pm.fraction = fraction; pm.endpos = endpos; }
//-----------------------------------------------------------------------------
// Traces the player's collision bounds in quadrants, looking for a plane that
// can be stood upon (normal's z >= 0.7f). Regardless of success or failure,
// replace the fraction and endpos with the original ones, so we don't try to
// move the player down to the new floor and get stuck on a leaning wall that
// the original trace hit first.
//-----------------------------------------------------------------------------
void CGameMovement::TryTouchGroundInQuadrants( const Vector& start, const Vector& end, unsigned int fMask, int collisionGroup, trace_t& pm ) { VPROF( "CGameMovement::TryTouchGroundInQuadrants" );
Vector mins, maxs; Vector minsSrc = GetPlayerMins(); Vector maxsSrc = GetPlayerMaxs();
float fraction = pm.fraction; Vector endpos = pm.endpos;
// Check the -x, -y quadrant
mins = minsSrc; maxs.Init( MIN( 0, maxsSrc.x ), MIN( 0, maxsSrc.y ), maxsSrc.z ); TryTouchGround( start, end, mins, maxs, fMask, collisionGroup, pm ); if ( pm.m_pEnt && pm.plane.normal[2] >= 0.7) { pm.fraction = fraction; pm.endpos = endpos; return; }
// Check the +x, +y quadrant
mins.Init( MAX( 0, minsSrc.x ), MAX( 0, minsSrc.y ), minsSrc.z ); maxs = maxsSrc; TryTouchGround( start, end, mins, maxs, fMask, collisionGroup, pm ); if ( pm.m_pEnt && pm.plane.normal[2] >= 0.7) { pm.fraction = fraction; pm.endpos = endpos; return; }
// Check the -x, +y quadrant
mins.Init( minsSrc.x, MAX( 0, minsSrc.y ), minsSrc.z ); maxs.Init( MIN( 0, maxsSrc.x ), maxsSrc.y, maxsSrc.z ); TryTouchGround( start, end, mins, maxs, fMask, collisionGroup, pm ); if ( pm.m_pEnt && pm.plane.normal[2] >= 0.7) { pm.fraction = fraction; pm.endpos = endpos; return; }
// Check the +x, -y quadrant
mins.Init( MAX( 0, minsSrc.x ), minsSrc.y, minsSrc.z ); maxs.Init( maxsSrc.x, MIN( 0, maxsSrc.y ), maxsSrc.z ); TryTouchGround( start, end, mins, maxs, fMask, collisionGroup, pm ); if ( pm.m_pEnt && pm.plane.normal[2] >= 0.7) { pm.fraction = fraction; pm.endpos = endpos; return; }
pm.fraction = fraction; pm.endpos = endpos; }
//-----------------------------------------------------------------------------
// Purpose:
// Input : &input -
//-----------------------------------------------------------------------------
void CGameMovement::CategorizePosition( void ) { Vector point; trace_t pm;
// Reset this each time we-recategorize, otherwise we have bogus friction when we jump into water and plunge downward really quickly
player->m_surfaceFriction = 1.0f;
// if the player hull point one unit down is solid, the player
// is on ground
// see if standing on something solid
// Doing this before we move may introduce a potential latency in water detection, but
// doing it after can get us stuck on the bottom in water if the amount we move up
// is less than the 1 pixel 'threshold' we're about to snap to. Also, we'll call
// this several times per frame, so we really need to avoid sticking to the bottom of
// water on each call, and the converse case will correct itself if called twice.
CheckWater();
// observers don't have a ground entity
if ( player->IsObserver() ) return;
float flOffset = 2.0f;
point[0] = mv->GetAbsOrigin()[0]; point[1] = mv->GetAbsOrigin()[1]; point[2] = mv->GetAbsOrigin()[2] - flOffset;
Vector bumpOrigin; bumpOrigin = mv->GetAbsOrigin();
// Shooting up really fast. Definitely not on ground.
// On ladder moving up, so not on ground either
// NOTE: 145 is a jump.
#define NON_JUMP_VELOCITY 140.0f
float zvel = mv->m_vecVelocity[2]; bool bMovingUp = zvel > 0.0f; bool bMovingUpRapidly = zvel > NON_JUMP_VELOCITY; float flGroundEntityVelZ = 0.0f; if ( bMovingUpRapidly ) { // Tracker 73219, 75878: ywb 8/2/07
// After save/restore (and maybe at other times), we can get a case where we were saved on a lift and
// after restore we'll have a high local velocity due to the lift making our abs velocity appear high.
// We need to account for standing on a moving ground object in that case in order to determine if we really
// are moving away from the object we are standing on at too rapid a speed. Note that CheckJump already sets
// ground entity to NULL, so this wouldn't have any effect unless we are moving up rapidly not from the jump button.
CBaseEntity *ground = player->GetGroundEntity(); if ( ground ) { flGroundEntityVelZ = ground->GetAbsVelocity().z; bMovingUpRapidly = ( zvel - flGroundEntityVelZ ) > NON_JUMP_VELOCITY; } }
// Was on ground, but now suddenly am not
if ( bMovingUpRapidly || ( bMovingUp && player->GetMoveType() == MOVETYPE_LADDER ) ) { SetGroundEntity( NULL ); } else { // Try and move down.
TryTouchGround( bumpOrigin, point, GetPlayerMins(), GetPlayerMaxs(), MASK_PLAYERSOLID, COLLISION_GROUP_PLAYER_MOVEMENT, pm ); // Was on ground, but now suddenly am not. If we hit a steep plane, we are not on ground
if ( !pm.m_pEnt || pm.plane.normal[2] < 0.7 ) { // Test four sub-boxes, to see if any of them would have found shallower slope we could actually stand on
TryTouchGroundInQuadrants( bumpOrigin, point, MASK_PLAYERSOLID, COLLISION_GROUP_PLAYER_MOVEMENT, pm );
if ( !pm.m_pEnt || pm.plane.normal[2] < 0.7 ) { SetGroundEntity( NULL ); // probably want to add a check for a +z velocity too!
if ( ( mv->m_vecVelocity.z > 0.0f ) && ( player->GetMoveType() != MOVETYPE_NOCLIP ) ) { player->m_surfaceFriction = 0.25f; } } else { SetGroundEntity( &pm ); } } else { SetGroundEntity( &pm ); // Otherwise, point to index of ent under us.
}
#ifndef CLIENT_DLL
//Adrian: vehicle code handles for us.
if ( player->IsInAVehicle() == false ) { // If our gamematerial has changed, tell any player surface triggers that are watching
IPhysicsSurfaceProps *physprops = MoveHelper()->GetSurfaceProps(); surfacedata_t *pSurfaceProp = physprops->GetSurfaceData( pm.surface.surfaceProps ); char cCurrGameMaterial = pSurfaceProp->game.material; if ( !player->GetGroundEntity() ) { cCurrGameMaterial = 0; }
// Changed?
if ( player->m_chPreviousTextureType != cCurrGameMaterial ) { CEnvPlayerSurfaceTrigger::SetPlayerSurface( player, cCurrGameMaterial ); }
player->m_chPreviousTextureType = cCurrGameMaterial; } #endif
} }
//-----------------------------------------------------------------------------
// Purpose: Determine if the player has hit the ground while falling, apply
// damage, and play the appropriate impact sound.
//-----------------------------------------------------------------------------
void CGameMovement::CheckFalling( void ) { // this function really deals with landing, not falling, so early out otherwise
if ( player->GetGroundEntity() == NULL || player->m_Local.m_flFallVelocity <= 0 ) return;
if ( !IsDead() && player->m_Local.m_flFallVelocity >= PLAYER_FALL_PUNCH_THRESHOLD ) { bool bAlive = true; float fvol = 0.5;
if ( player->GetWaterLevel() > 0 ) { // They landed in water.
} else { // Scale it down if we landed on something that's floating...
if ( player->GetGroundEntity()->IsFloating() ) { player->m_Local.m_flFallVelocity -= PLAYER_LAND_ON_FLOATING_OBJECT; }
//
// They hit the ground.
//
if( player->GetGroundEntity()->GetAbsVelocity().z < 0.0f ) { // Player landed on a descending object. Subtract the velocity of the ground entity.
player->m_Local.m_flFallVelocity += player->GetGroundEntity()->GetAbsVelocity().z; player->m_Local.m_flFallVelocity = MAX( 0.1f, player->m_Local.m_flFallVelocity ); }
if ( player->m_Local.m_flFallVelocity > PLAYER_MAX_SAFE_FALL_SPEED ) { //
// If they hit the ground going this fast they may take damage (and die).
//
bAlive = MoveHelper( )->PlayerFallingDamage(); fvol = 1.0; } else if ( player->m_Local.m_flFallVelocity > PLAYER_MAX_SAFE_FALL_SPEED / 2 ) { fvol = 0.85; } else if ( player->m_Local.m_flFallVelocity < PLAYER_MIN_BOUNCE_SPEED ) { fvol = 0; } }
PlayerRoughLandingEffects( fvol );
if (bAlive) { MoveHelper( )->PlayerSetAnimation( PLAYER_WALK ); } }
// let any subclasses know that the player has landed and how hard
OnLand(player->m_Local.m_flFallVelocity); //
// Clear the fall velocity so the impact doesn't happen again.
//
player->m_Local.m_flFallVelocity = 0; }
void CGameMovement::PlayerRoughLandingEffects( float fvol ) { if ( fvol > 0.0 ) { //
// Play landing sound right away.
player->m_flStepSoundTime = 400;
// Play step sound for current texture.
player->PlayStepSound( (Vector &)mv->GetAbsOrigin(), player->m_pSurfaceData, fvol, true );
//
// Knock the screen around a little bit, temporary effect.
//
player->m_Local.m_vecPunchAngle.Set( ROLL, player->m_Local.m_flFallVelocity * 0.013 );
if ( player->m_Local.m_vecPunchAngle[PITCH] > 8 ) { player->m_Local.m_vecPunchAngle.Set( PITCH, 8 ); }
#if !defined( CLIENT_DLL )
player->RumbleEffect( ( fvol > 0.85f ) ? ( RUMBLE_FALL_LONG ) : ( RUMBLE_FALL_SHORT ), 0, RUMBLE_FLAGS_NONE ); #endif
} }
//-----------------------------------------------------------------------------
// Purpose: Use for ease-in, ease-out style interpolation (accel/decel) Used by ducking code.
// Input : value -
// scale -
// Output : float
//-----------------------------------------------------------------------------
float CGameMovement::SplineFraction( float value, float scale ) { float valueSquared;
value = scale * value; valueSquared = value * value;
// Nice little ease-in, ease-out spline-like curve
return 3 * valueSquared - 2 * valueSquared * value; }
//-----------------------------------------------------------------------------
// Purpose: Determine if crouch/uncrouch caused player to get stuck in world
// Input : direction -
//-----------------------------------------------------------------------------
void CGameMovement::FixPlayerCrouchStuck( bool upward ) { EntityHandle_t hitent; int i; Vector test; trace_t dummy;
int direction = upward ? 1 : 0;
hitent = TestPlayerPosition( mv->GetAbsOrigin(), COLLISION_GROUP_PLAYER_MOVEMENT, dummy ); if (hitent == INVALID_ENTITY_HANDLE ) return; VectorCopy( mv->GetAbsOrigin(), test ); for ( i = 0; i < 36; i++ ) { Vector org = mv->GetAbsOrigin(); org.z += direction; mv->SetAbsOrigin( org ); hitent = TestPlayerPosition( mv->GetAbsOrigin(), COLLISION_GROUP_PLAYER_MOVEMENT, dummy ); if (hitent == INVALID_ENTITY_HANDLE ) return; }
mv->SetAbsOrigin( test ); // Failed
}
bool CGameMovement::CanUnduck() { int i; trace_t trace; Vector newOrigin;
VectorCopy( mv->GetAbsOrigin(), newOrigin );
if ( player->GetGroundEntity() != NULL ) { for ( i = 0; i < 3; i++ ) { newOrigin[i] += ( VEC_DUCK_HULL_MIN_SCALED( player )[i] - VEC_HULL_MIN_SCALED( player )[i] ); } } else { // If in air an letting go of crouch, make sure we can offset origin to make
// up for uncrouching
Vector hullSizeNormal = VEC_HULL_MAX_SCALED( player ) - VEC_HULL_MIN_SCALED( player ); Vector hullSizeCrouch = VEC_DUCK_HULL_MAX_SCALED( player ) - VEC_DUCK_HULL_MIN_SCALED( player ); Vector viewDelta = ( hullSizeNormal - hullSizeCrouch ); viewDelta.Negate(); VectorAdd( newOrigin, viewDelta, newOrigin ); }
bool saveducked = player->m_Local.m_bDucked; player->m_Local.m_bDucked = false; TracePlayerBBox( mv->GetAbsOrigin(), newOrigin, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, trace ); player->m_Local.m_bDucked = saveducked; if ( trace.startsolid || ( trace.fraction != 1.0f ) ) return false;
return true; }
//-----------------------------------------------------------------------------
// Purpose: Stop ducking
//-----------------------------------------------------------------------------
void CGameMovement::FinishUnDuck( void ) { int i; trace_t trace; Vector newOrigin;
VectorCopy( mv->GetAbsOrigin(), newOrigin );
if ( player->GetGroundEntity() != NULL ) { for ( i = 0; i < 3; i++ ) { newOrigin[i] += ( VEC_DUCK_HULL_MIN_SCALED( player )[i] - VEC_HULL_MIN_SCALED( player )[i] ); } } else { // If in air an letting go of crouch, make sure we can offset origin to make
// up for uncrouching
Vector hullSizeNormal = VEC_HULL_MAX_SCALED( player ) - VEC_HULL_MIN_SCALED( player ); Vector hullSizeCrouch = VEC_DUCK_HULL_MAX_SCALED( player ) - VEC_DUCK_HULL_MIN_SCALED( player ); Vector viewDelta = ( hullSizeNormal - hullSizeCrouch ); viewDelta.Negate(); VectorAdd( newOrigin, viewDelta, newOrigin ); }
player->m_Local.m_bDucked = false; player->RemoveFlag( FL_DUCKING ); player->m_Local.m_bDucking = false; player->m_Local.m_bInDuckJump = false; player->SetViewOffset( GetPlayerViewOffset( false ) ); player->m_Local.m_flDucktime = 0;
mv->SetAbsOrigin( newOrigin );
#ifdef CLIENT_DLL
#ifdef STAGING_ONLY
if ( debug_latch_reset_onduck.GetBool() ) { player->ResetLatched(); } #else
player->ResetLatched(); #endif
#endif // CLIENT_DLL
// Recategorize position since ducking can change origin
CategorizePosition(); }
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CGameMovement::UpdateDuckJumpEyeOffset( void ) { if ( player->m_Local.m_flDuckJumpTime != 0.0f ) { float flDuckMilliseconds = MAX( 0.0f, GAMEMOVEMENT_DUCK_TIME - ( float )player->m_Local.m_flDuckJumpTime ); float flDuckSeconds = flDuckMilliseconds / GAMEMOVEMENT_DUCK_TIME; if ( flDuckSeconds > TIME_TO_UNDUCK ) { player->m_Local.m_flDuckJumpTime = 0.0f; SetDuckedEyeOffset( 0.0f ); } else { float flDuckFraction = SimpleSpline( 1.0f - ( flDuckSeconds / TIME_TO_UNDUCK ) ); SetDuckedEyeOffset( flDuckFraction ); } } }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CGameMovement::FinishUnDuckJump( trace_t &trace ) { Vector vecNewOrigin; VectorCopy( mv->GetAbsOrigin(), vecNewOrigin );
// Up for uncrouching.
Vector hullSizeNormal = VEC_HULL_MAX_SCALED( player ) - VEC_HULL_MIN_SCALED( player ); Vector hullSizeCrouch = VEC_DUCK_HULL_MAX_SCALED( player ) - VEC_DUCK_HULL_MIN_SCALED( player ); Vector viewDelta = ( hullSizeNormal - hullSizeCrouch );
float flDeltaZ = viewDelta.z; viewDelta.z *= trace.fraction; flDeltaZ -= viewDelta.z;
player->RemoveFlag( FL_DUCKING ); player->m_Local.m_bDucked = false; player->m_Local.m_bDucking = false; player->m_Local.m_bInDuckJump = false; player->m_Local.m_flDucktime = 0.0f; player->m_Local.m_flDuckJumpTime = 0.0f; player->m_Local.m_flJumpTime = 0.0f;
Vector vecViewOffset = GetPlayerViewOffset( false ); vecViewOffset.z -= flDeltaZ; player->SetViewOffset( vecViewOffset );
VectorSubtract( vecNewOrigin, viewDelta, vecNewOrigin ); mv->SetAbsOrigin( vecNewOrigin );
// Recategorize position since ducking can change origin
CategorizePosition(); }
//-----------------------------------------------------------------------------
// Purpose: Finish ducking
//-----------------------------------------------------------------------------
void CGameMovement::FinishDuck( void ) { if ( player->GetFlags() & FL_DUCKING ) return;
player->AddFlag( FL_DUCKING ); player->m_Local.m_bDucked = true; player->m_Local.m_bDucking = false;
player->SetViewOffset( GetPlayerViewOffset( true ) );
// HACKHACK - Fudge for collision bug - no time to fix this properly
if ( player->GetGroundEntity() != NULL ) { for ( int i = 0; i < 3; i++ ) { Vector org = mv->GetAbsOrigin(); org[ i ]-= ( VEC_DUCK_HULL_MIN_SCALED( player )[i] - VEC_HULL_MIN_SCALED( player )[i] ); mv->SetAbsOrigin( org ); } } else { Vector hullSizeNormal = VEC_HULL_MAX_SCALED( player ) - VEC_HULL_MIN_SCALED( player ); Vector hullSizeCrouch = VEC_DUCK_HULL_MAX_SCALED( player ) - VEC_DUCK_HULL_MIN_SCALED( player ); Vector viewDelta = ( hullSizeNormal - hullSizeCrouch ); Vector out; VectorAdd( mv->GetAbsOrigin(), viewDelta, out ); mv->SetAbsOrigin( out );
#ifdef CLIENT_DLL
#ifdef STAGING_ONLY
if ( debug_latch_reset_onduck.GetBool() ) { player->ResetLatched(); } #else
player->ResetLatched(); #endif
#endif // CLIENT_DLL
}
// See if we are stuck?
FixPlayerCrouchStuck( true );
// Recategorize position since ducking can change origin
CategorizePosition(); }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CGameMovement::StartUnDuckJump( void ) { player->AddFlag( FL_DUCKING ); player->m_Local.m_bDucked = true; player->m_Local.m_bDucking = false;
player->SetViewOffset( GetPlayerViewOffset( true ) );
Vector hullSizeNormal = VEC_HULL_MAX_SCALED( player ) - VEC_HULL_MIN_SCALED( player ); Vector hullSizeCrouch = VEC_DUCK_HULL_MAX_SCALED( player ) - VEC_DUCK_HULL_MIN_SCALED( player ); Vector viewDelta = ( hullSizeNormal - hullSizeCrouch ); Vector out; VectorAdd( mv->GetAbsOrigin(), viewDelta, out ); mv->SetAbsOrigin( out );
// See if we are stuck?
FixPlayerCrouchStuck( true );
// Recategorize position since ducking can change origin
CategorizePosition(); }
//
//-----------------------------------------------------------------------------
// Purpose:
// Input : duckFraction -
//-----------------------------------------------------------------------------
void CGameMovement::SetDuckedEyeOffset( float duckFraction ) { Vector vDuckHullMin = GetPlayerMins( true ); Vector vStandHullMin = GetPlayerMins( false );
float fMore = ( vDuckHullMin.z - vStandHullMin.z );
Vector vecDuckViewOffset = GetPlayerViewOffset( true ); Vector vecStandViewOffset = GetPlayerViewOffset( false ); Vector temp = player->GetViewOffset(); temp.z = ( ( vecDuckViewOffset.z - fMore ) * duckFraction ) + ( vecStandViewOffset.z * ( 1 - duckFraction ) ); player->SetViewOffset( temp ); }
//-----------------------------------------------------------------------------
// Purpose: Crop the speed of the player when ducking and on the ground.
// Input: bInDuck - is the player already ducking
// bInAir - is the player in air
// NOTE: Only crop player speed once.
//-----------------------------------------------------------------------------
void CGameMovement::HandleDuckingSpeedCrop( void ) { if ( !( m_iSpeedCropped & SPEED_CROPPED_DUCK ) && ( player->GetFlags() & FL_DUCKING ) && ( player->GetGroundEntity() != NULL ) ) { float frac = 0.33333333f; mv->m_flForwardMove *= frac; mv->m_flSideMove *= frac; mv->m_flUpMove *= frac; m_iSpeedCropped |= SPEED_CROPPED_DUCK; } }
//-----------------------------------------------------------------------------
// Purpose: Check to see if we are in a situation where we can unduck jump.
//-----------------------------------------------------------------------------
bool CGameMovement::CanUnDuckJump( trace_t &trace ) { // Trace down to the stand position and see if we can stand.
Vector vecEnd( mv->GetAbsOrigin() ); vecEnd.z -= 36.0f; // This will have to change if bounding hull change!
TracePlayerBBox( mv->GetAbsOrigin(), vecEnd, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, trace ); if ( trace.fraction < 1.0f ) { // Find the endpoint.
vecEnd.z = mv->GetAbsOrigin().z + ( -36.0f * trace.fraction );
// Test a normal hull.
trace_t traceUp; bool bWasDucked = player->m_Local.m_bDucked; player->m_Local.m_bDucked = false; TracePlayerBBox( vecEnd, vecEnd, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, traceUp ); player->m_Local.m_bDucked = bWasDucked; if ( !traceUp.startsolid ) return true; }
return false; }
//-----------------------------------------------------------------------------
// Purpose: See if duck button is pressed and do the appropriate things
//-----------------------------------------------------------------------------
void CGameMovement::Duck( void ) { int buttonsChanged = ( mv->m_nOldButtons ^ mv->m_nButtons ); // These buttons have changed this frame
int buttonsPressed = buttonsChanged & mv->m_nButtons; // The changed ones still down are "pressed"
int buttonsReleased = buttonsChanged & mv->m_nOldButtons; // The changed ones which were previously down are "released"
// Check to see if we are in the air.
bool bInAir = ( player->GetGroundEntity() == NULL ); bool bInDuck = ( player->GetFlags() & FL_DUCKING ) ? true : false; bool bDuckJump = ( player->m_Local.m_flJumpTime > 0.0f ); bool bDuckJumpTime = ( player->m_Local.m_flDuckJumpTime > 0.0f );
if ( mv->m_nButtons & IN_DUCK ) { mv->m_nOldButtons |= IN_DUCK; } else { mv->m_nOldButtons &= ~IN_DUCK; }
// Handle death.
if ( IsDead() ) return;
// Slow down ducked players.
HandleDuckingSpeedCrop();
// If the player is holding down the duck button, the player is in duck transition, ducking, or duck-jumping.
if ( ( mv->m_nButtons & IN_DUCK ) || player->m_Local.m_bDucking || bInDuck || bDuckJump ) { // DUCK
if ( ( mv->m_nButtons & IN_DUCK ) || bDuckJump ) { // XBOX SERVER ONLY
#if !defined(CLIENT_DLL)
if ( IsX360() && buttonsPressed & IN_DUCK ) { // Hinting logic
if ( player->GetToggledDuckState() && player->m_nNumCrouches < NUM_CROUCH_HINTS ) { UTIL_HudHintText( player, "#Valve_Hint_Crouch" ); player->m_nNumCrouches++; } } #endif
// Have the duck button pressed, but the player currently isn't in the duck position.
if ( ( buttonsPressed & IN_DUCK ) && !bInDuck && !bDuckJump && !bDuckJumpTime ) { player->m_Local.m_flDucktime = GAMEMOVEMENT_DUCK_TIME; player->m_Local.m_bDucking = true; } // The player is in duck transition and not duck-jumping.
if ( player->m_Local.m_bDucking && !bDuckJump && !bDuckJumpTime ) { float flDuckMilliseconds = MAX( 0.0f, GAMEMOVEMENT_DUCK_TIME - ( float )player->m_Local.m_flDucktime ); float flDuckSeconds = flDuckMilliseconds * 0.001f; // Finish in duck transition when transition time is over, in "duck", in air.
if ( ( flDuckSeconds > TIME_TO_DUCK ) || bInDuck || bInAir ) { FinishDuck(); } else { // Calc parametric time
float flDuckFraction = SimpleSpline( flDuckSeconds / TIME_TO_DUCK ); SetDuckedEyeOffset( flDuckFraction ); } }
if ( bDuckJump ) { // Make the bounding box small immediately.
if ( !bInDuck ) { StartUnDuckJump(); } else { // Check for a crouch override.
if ( !( mv->m_nButtons & IN_DUCK ) ) { trace_t trace; if ( CanUnDuckJump( trace ) ) { FinishUnDuckJump( trace ); player->m_Local.m_flDuckJumpTime = ( GAMEMOVEMENT_TIME_TO_UNDUCK * ( 1.0f - trace.fraction ) ) + GAMEMOVEMENT_TIME_TO_UNDUCK_INV; } } } } } // UNDUCK (or attempt to...)
else { if ( player->m_Local.m_bInDuckJump ) { // Check for a crouch override.
if ( !( mv->m_nButtons & IN_DUCK ) ) { trace_t trace; if ( CanUnDuckJump( trace ) ) { FinishUnDuckJump( trace ); if ( trace.fraction < 1.0f ) { player->m_Local.m_flDuckJumpTime = ( GAMEMOVEMENT_TIME_TO_UNDUCK * ( 1.0f - trace.fraction ) ) + GAMEMOVEMENT_TIME_TO_UNDUCK_INV; } } } else { player->m_Local.m_bInDuckJump = false; } }
if ( bDuckJumpTime ) return;
// Try to unduck unless automovement is not allowed
// NOTE: When not onground, you can always unduck
if ( player->m_Local.m_bAllowAutoMovement || bInAir || player->m_Local.m_bDucking ) { // We released the duck button, we aren't in "duck" and we are not in the air - start unduck transition.
if ( ( buttonsReleased & IN_DUCK ) ) { if ( bInDuck && !bDuckJump ) { player->m_Local.m_flDucktime = GAMEMOVEMENT_DUCK_TIME; } else if ( player->m_Local.m_bDucking && !player->m_Local.m_bDucked ) { // Invert time if release before fully ducked!!!
float unduckMilliseconds = 1000.0f * TIME_TO_UNDUCK; float duckMilliseconds = 1000.0f * TIME_TO_DUCK; float elapsedMilliseconds = GAMEMOVEMENT_DUCK_TIME - player->m_Local.m_flDucktime;
float fracDucked = elapsedMilliseconds / duckMilliseconds; float remainingUnduckMilliseconds = fracDucked * unduckMilliseconds;
player->m_Local.m_flDucktime = GAMEMOVEMENT_DUCK_TIME - unduckMilliseconds + remainingUnduckMilliseconds; } }
// Check to see if we are capable of unducking.
if ( CanUnduck() ) { // or unducking
if ( ( player->m_Local.m_bDucking || player->m_Local.m_bDucked ) ) { float flDuckMilliseconds = MAX( 0.0f, GAMEMOVEMENT_DUCK_TIME - (float)player->m_Local.m_flDucktime ); float flDuckSeconds = flDuckMilliseconds * 0.001f; // Finish ducking immediately if duck time is over or not on ground
if ( flDuckSeconds > TIME_TO_UNDUCK || ( bInAir && !bDuckJump ) ) { FinishUnDuck(); } else { // Calc parametric time
float flDuckFraction = SimpleSpline( 1.0f - ( flDuckSeconds / TIME_TO_UNDUCK ) ); SetDuckedEyeOffset( flDuckFraction ); player->m_Local.m_bDucking = true; } } } else { // Still under something where we can't unduck, so make sure we reset this timer so
// that we'll unduck once we exit the tunnel, etc.
if ( player->m_Local.m_flDucktime != GAMEMOVEMENT_DUCK_TIME ) { SetDuckedEyeOffset(1.0f); player->m_Local.m_flDucktime = GAMEMOVEMENT_DUCK_TIME; player->m_Local.m_bDucked = true; player->m_Local.m_bDucking = false; player->AddFlag( FL_DUCKING ); } } } } } // HACK: (jimd 5/25/2006) we have a reoccuring bug (#50063 in Tracker) where the player's
// view height gets left at the ducked height while the player is standing, but we haven't
// been able to repro it to find the cause. It may be fixed now due to a change I'm
// also making in UpdateDuckJumpEyeOffset but just in case, this code will sense the
// problem and restore the eye to the proper position. It doesn't smooth the transition,
// but it is preferable to leaving the player's view too low.
//
// If the player is still alive and not an observer, check to make sure that
// his view height is at the standing height.
else if ( !IsDead() && !player->IsObserver() && !player->IsInAVehicle() ) { if ( ( player->m_Local.m_flDuckJumpTime == 0.0f ) && ( fabs(player->GetViewOffset().z - GetPlayerViewOffset( false ).z) > 0.1 ) ) { // we should rarely ever get here, so assert so a coder knows when it happens
Assert(0); DevMsg( 1, "Restoring player view height\n" );
// set the eye height to the non-ducked height
SetDuckedEyeOffset(0.0f); } } }
static ConVar sv_optimizedmovement( "sv_optimizedmovement", "1", FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY );
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CGameMovement::PlayerMove( void ) { VPROF( "CGameMovement::PlayerMove" );
CheckParameters(); // clear output applied velocity
mv->m_outWishVel.Init(); mv->m_outJumpVel.Init();
MoveHelper( )->ResetTouchList(); // Assume we don't touch anything
ReduceTimers();
AngleVectors (mv->m_vecViewAngles, &m_vecForward, &m_vecRight, &m_vecUp ); // Determine movement angles
// Always try and unstick us unless we are using a couple of the movement modes
if ( player->GetMoveType() != MOVETYPE_NOCLIP && player->GetMoveType() != MOVETYPE_NONE && player->GetMoveType() != MOVETYPE_ISOMETRIC && player->GetMoveType() != MOVETYPE_OBSERVER && !player->pl.deadflag ) { if ( CheckInterval( STUCK ) ) { if ( CheckStuck() ) { // Can't move, we're stuck
return; } } }
// Now that we are "unstuck", see where we are (player->GetWaterLevel() and type, player->GetGroundEntity()).
if ( player->GetMoveType() != MOVETYPE_WALK || mv->m_bGameCodeMovedPlayer || !sv_optimizedmovement.GetBool() ) { CategorizePosition(); } else { if ( mv->m_vecVelocity.z > 250.0f ) { SetGroundEntity( NULL ); } }
// Store off the starting water level
m_nOldWaterLevel = player->GetWaterLevel();
// If we are not on ground, store off how fast we are moving down
if ( player->GetGroundEntity() == NULL ) { player->m_Local.m_flFallVelocity = -mv->m_vecVelocity[ 2 ]; }
m_nOnLadder = 0;
player->UpdateStepSound( player->m_pSurfaceData, mv->GetAbsOrigin(), mv->m_vecVelocity );
UpdateDuckJumpEyeOffset(); Duck();
// Don't run ladder code if dead on on a train
if ( !player->pl.deadflag && !(player->GetFlags() & FL_ONTRAIN) ) { // If was not on a ladder now, but was on one before,
// get off of the ladder
// TODO: this causes lots of weirdness.
//bool bCheckLadder = CheckInterval( LADDER );
//if ( bCheckLadder || player->GetMoveType() == MOVETYPE_LADDER )
{ if ( !LadderMove() && ( player->GetMoveType() == MOVETYPE_LADDER ) ) { // Clear ladder stuff unless player is dead or riding a train
// It will be reset immediately again next frame if necessary
player->SetMoveType( MOVETYPE_WALK ); player->SetMoveCollide( MOVECOLLIDE_DEFAULT ); } } }
#if 0
Msg("%i, %i, %s, player = %8x, move type = %2i, ground entity = %8x, velocity = (%f %f %f)\n", player->CurrentCommandNumber(), player->m_nTickBase, player->IsServer() ? "SERVER" : "CLIENT", player, player->GetMoveType(), player->GetGroundEntity(), mv->m_vecVelocity[0], mv->m_vecVelocity[1], mv->m_vecVelocity[2]);
#endif
// Handle movement modes.
switch (player->GetMoveType()) { case MOVETYPE_NONE: break;
case MOVETYPE_NOCLIP: FullNoClipMove( sv_noclipspeed.GetFloat(), sv_noclipaccelerate.GetFloat() ); break;
case MOVETYPE_FLY: case MOVETYPE_FLYGRAVITY: FullTossMove(); break;
case MOVETYPE_LADDER: FullLadderMove(); break;
case MOVETYPE_WALK: FullWalkMove(); break;
case MOVETYPE_ISOMETRIC: //IsometricMove();
// Could also try: FullTossMove();
FullWalkMove(); break; case MOVETYPE_OBSERVER: FullObserverMove(); // clips against world&players
break;
default: DevMsg( 1, "Bogus pmove player movetype %i on (%i) 0=cl 1=sv\n", player->GetMoveType(), player->IsServer()); break; } }
//-----------------------------------------------------------------------------
// Performs the collision resolution for fliers.
//-----------------------------------------------------------------------------
void CGameMovement::PerformFlyCollisionResolution( trace_t &pm, Vector &move ) { Vector base; float vel; float backoff;
switch (player->GetMoveCollide()) { case MOVECOLLIDE_FLY_CUSTOM: // Do nothing; the velocity should have been modified by touch
// FIXME: It seems wrong for touch to modify velocity
// given that it can be called in a number of places
// where collision resolution do *not* in fact occur
// Should this ever occur for players!?
Assert(0); break;
case MOVECOLLIDE_FLY_BOUNCE: case MOVECOLLIDE_DEFAULT: { if (player->GetMoveCollide() == MOVECOLLIDE_FLY_BOUNCE) backoff = 2.0 - player->m_surfaceFriction; else backoff = 1;
ClipVelocity (mv->m_vecVelocity, pm.plane.normal, mv->m_vecVelocity, backoff); } break;
default: // Invalid collide type!
Assert(0); break; }
// stop if on ground
if (pm.plane.normal[2] > 0.7) { base.Init(); if (mv->m_vecVelocity[2] < GetCurrentGravity() * gpGlobals->frametime) { // we're rolling on the ground, add static friction.
SetGroundEntity( &pm ); mv->m_vecVelocity[2] = 0; }
vel = DotProduct( mv->m_vecVelocity, mv->m_vecVelocity );
// Con_DPrintf("%f %f: %.0f %.0f %.0f\n", vel, trace.fraction, ent->velocity[0], ent->velocity[1], ent->velocity[2] );
if (vel < (30 * 30) || (player->GetMoveCollide() != MOVECOLLIDE_FLY_BOUNCE)) { SetGroundEntity( &pm ); mv->m_vecVelocity.Init(); } else { VectorScale (mv->m_vecVelocity, (1.0 - pm.fraction) * gpGlobals->frametime * 0.9, move); PushEntity( move, &pm ); } VectorSubtract( mv->m_vecVelocity, base, mv->m_vecVelocity ); } }
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CGameMovement::FullTossMove( void ) { trace_t pm; Vector move; CheckWater();
// add velocity if player is moving
if ( (mv->m_flForwardMove != 0.0f) || (mv->m_flSideMove != 0.0f) || (mv->m_flUpMove != 0.0f)) { Vector forward, right, up; float fmove, smove; Vector wishdir, wishvel; float wishspeed; int i;
AngleVectors (mv->m_vecViewAngles, &forward, &right, &up); // Determine movement angles
// Copy movement amounts
fmove = mv->m_flForwardMove; smove = mv->m_flSideMove;
VectorNormalize (forward); // Normalize remainder of vectors.
VectorNormalize (right); //
for (i=0 ; i<3 ; i++) // Determine x and y parts of velocity
wishvel[i] = forward[i]*fmove + right[i]*smove;
wishvel[2] += mv->m_flUpMove;
VectorCopy (wishvel, wishdir); // Determine maginitude of speed of move
wishspeed = VectorNormalize(wishdir);
//
// Clamp to server defined max speed
//
if (wishspeed > mv->m_flMaxSpeed) { VectorScale (wishvel, mv->m_flMaxSpeed/wishspeed, wishvel); wishspeed = mv->m_flMaxSpeed; }
// Set pmove velocity
Accelerate ( wishdir, wishspeed, sv_accelerate.GetFloat() ); }
if ( mv->m_vecVelocity[2] > 0 ) { SetGroundEntity( NULL ); }
// If on ground and not moving, return.
if ( player->GetGroundEntity() != NULL ) { if (VectorCompare(player->GetBaseVelocity(), vec3_origin) && VectorCompare(mv->m_vecVelocity, vec3_origin)) return; }
CheckVelocity();
// add gravity
if ( player->GetMoveType() == MOVETYPE_FLYGRAVITY ) { AddGravity(); }
// move origin
// Base velocity is not properly accounted for since this entity will move again after the bounce without
// taking it into account
VectorAdd (mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity); CheckVelocity();
VectorScale (mv->m_vecVelocity, gpGlobals->frametime, move); VectorSubtract (mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity);
PushEntity( move, &pm ); // Should this clear basevelocity
CheckVelocity();
if (pm.allsolid) { // entity is trapped in another solid
SetGroundEntity( &pm ); mv->m_vecVelocity.Init(); return; } if (pm.fraction != 1) { PerformFlyCollisionResolution( pm, move ); } // check for in water
CheckWater(); }
//-----------------------------------------------------------------------------
// Purpose: TF2 commander mode movement logic
//-----------------------------------------------------------------------------
#pragma warning (disable : 4701)
void CGameMovement::IsometricMove( void ) { int i; Vector wishvel; float fmove, smove; Vector forward, right, up;
AngleVectors (mv->m_vecViewAngles, &forward, &right, &up); // Determine movement angles
// Copy movement amounts
fmove = mv->m_flForwardMove; smove = mv->m_flSideMove; // No up / down movement
forward[2] = 0; right[2] = 0;
VectorNormalize (forward); // Normalize remainder of vectors
VectorNormalize (right); //
for (i=0 ; i<3 ; i++) // Determine x and y parts of velocity
wishvel[i] = forward[i]*fmove + right[i]*smove; //wishvel[2] += mv->m_flUpMove;
Vector out; VectorMA (mv->GetAbsOrigin(), gpGlobals->frametime, wishvel, out ); mv->SetAbsOrigin( out ); // Zero out the velocity so that we don't accumulate a huge downward velocity from
// gravity, etc.
mv->m_vecVelocity.Init(); }
#pragma warning (default : 4701)
bool CGameMovement::GameHasLadders() const { return true; }
//-----------------------------------------------------------------------------
// Purpose: Traces player movement + position
//-----------------------------------------------------------------------------
void CGameMovement::TracePlayerBBox( const Vector& start, const Vector& end, unsigned int fMask, int collisionGroup, trace_t& pm ) { VPROF( "CGameMovement::TracePlayerBBox" );
Ray_t ray; ray.Init( start, end, GetPlayerMins(), GetPlayerMaxs() ); UTIL_TraceRay( ray, fMask, mv->m_nPlayerHandle.Get(), collisionGroup, &pm );
}
//-----------------------------------------------------------------------------
// Purpose: overridded by game classes to limit results (to standable objects for example)
//-----------------------------------------------------------------------------
void CGameMovement::TryTouchGround( const Vector& start, const Vector& end, const Vector& mins, const Vector& maxs, unsigned int fMask, int collisionGroup, trace_t& pm ) { VPROF( "CGameMovement::TryTouchGround" );
Ray_t ray; ray.Init( start, end, mins, maxs ); UTIL_TraceRay( ray, fMask, mv->m_nPlayerHandle.Get(), collisionGroup, &pm ); }
|