From f56bb35301836e56582a575a75864392a0177875 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8rgen=20P=2E=20Tjern=C3=B8?= Date: Mon, 2 Dec 2013 19:31:46 -0800 Subject: Fix line endings. WHAMMY. --- mp/src/game/shared/gamemovement.cpp | 9818 +++++++++++++++++------------------ 1 file changed, 4909 insertions(+), 4909 deletions(-) (limited to 'mp/src/game/shared/gamemovement.cpp') 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 -#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 - -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 ; jm_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 +#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 + +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 ; jm_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 ); +} + -- cgit v1.2.3