diff options
| author | Jørgen P. Tjernø <[email protected]> | 2013-12-02 19:31:46 -0800 |
|---|---|---|
| committer | Jørgen P. Tjernø <[email protected]> | 2013-12-02 19:46:31 -0800 |
| commit | f56bb35301836e56582a575a75864392a0177875 (patch) | |
| tree | de61ddd39de3e7df52759711950b4c288592f0dc /mp/src/game/shared/gamemovement.cpp | |
| parent | Mark some more files as text. (diff) | |
| download | source-sdk-2013-f56bb35301836e56582a575a75864392a0177875.tar.xz source-sdk-2013-f56bb35301836e56582a575a75864392a0177875.zip | |
Fix line endings. WHAMMY.
Diffstat (limited to 'mp/src/game/shared/gamemovement.cpp')
| -rw-r--r-- | mp/src/game/shared/gamemovement.cpp | 9818 |
1 files changed, 4909 insertions, 4909 deletions
diff --git a/mp/src/game/shared/gamemovement.cpp b/mp/src/game/shared/gamemovement.cpp index 816379f3..4f1132b1 100644 --- a/mp/src/game/shared/gamemovement.cpp +++ b/mp/src/game/shared/gamemovement.cpp @@ -1,4909 +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 );
-}
-
+//========= 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 ); +} + |