diff options
| author | Joe Ludwig <[email protected]> | 2013-06-26 15:22:04 -0700 |
|---|---|---|
| committer | Joe Ludwig <[email protected]> | 2013-06-26 15:22:04 -0700 |
| commit | 39ed87570bdb2f86969d4be821c94b722dc71179 (patch) | |
| tree | abc53757f75f40c80278e87650ea92808274aa59 /mp/src/game/shared/gamemovement.cpp | |
| download | source-sdk-2013-39ed87570bdb2f86969d4be821c94b722dc71179.tar.xz source-sdk-2013-39ed87570bdb2f86969d4be821c94b722dc71179.zip | |
First version of the SOurce SDK 2013
Diffstat (limited to 'mp/src/game/shared/gamemovement.cpp')
| -rw-r--r-- | mp/src/game/shared/gamemovement.cpp | 4909 |
1 files changed, 4909 insertions, 0 deletions
diff --git a/mp/src/game/shared/gamemovement.cpp b/mp/src/game/shared/gamemovement.cpp new file mode 100644 index 00000000..816379f3 --- /dev/null +++ b/mp/src/game/shared/gamemovement.cpp @@ -0,0 +1,4909 @@ +//========= 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
+
+// [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( 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 )
+{
+ IPhysicsSurfaceProps *physprops = MoveHelper()->GetSurfaceProps();
+ player->m_surfaceProps = pm.surface.surfaceProps;
+ player->m_pSurfaceData = physprops->GetSurfaceData( player->m_surfaceProps );
+ physprops->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;
+}
+
+#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 )
+ {
+ 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
+ player->ResetLatched();
+#endif
+
+ // 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
+ player->ResetLatched();
+#endif
+ }
+
+ // 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 );
+}
+
|