Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

5357 lines
148 KiB

  1. //========= Copyright © 1996-2008, Valve Corporation, All rights reserved. ====
  2. //
  3. //=============================================================================
  4. #include "cbase.h"
  5. #include "gamemovement.h"
  6. #include "in_buttons.h"
  7. #include <stdarg.h>
  8. #include "movevars_shared.h"
  9. #include "engine/IEngineTrace.h"
  10. #include "SoundEmitterSystem/isoundemittersystembase.h"
  11. #include "decals.h"
  12. #include "coordsize.h"
  13. #include "rumble_shared.h"
  14. #include "cstrike15/basecsgrenade_projectile.h"
  15. #if defined(HL2_DLL) || defined(HL2_CLIENT_DLL)
  16. #include "hl_movedata.h"
  17. #endif
  18. #if defined(HL2_EP3) && !defined(CLIENT_DLL)
  19. #include "ep3/weapon_icegun.h"
  20. #endif
  21. #if (PREDICTION_ERROR_CHECK_LEVEL > 0) && (PREDICTION_ERROR_CHECK_STACKS_FOR_MISSING > 0)
  22. #include "tier0/stacktools.h"
  23. #endif
  24. // memdbgon must be the last include file in a .cpp file!!!
  25. #include "tier0/memdbgon.h"
  26. #define STOP_EPSILON 0.1
  27. #define MAX_CLIP_PLANES 5
  28. #include "filesystem.h"
  29. #include <stdarg.h>
  30. extern IFileSystem *filesystem;
  31. #ifndef CLIENT_DLL
  32. #include "env_player_surface_trigger.h"
  33. static ConVar dispcoll_drawplane( "dispcoll_drawplane", "0" );
  34. #endif
  35. #if defined ( PORTAL2 ) && !defined ( CLIENT_DLL )
  36. #include "portal_player.h"
  37. #endif
  38. // tickcount currently isn't set during prediction, although gpGlobals->curtime and
  39. // gpGlobals->frametime are. We should probably set tickcount (to player->m_nTickBase),
  40. // but we're REALLY close to shipping, so we can change that later and people can use
  41. // player->CurrentCommandNumber() in the meantime.
  42. #define tickcount USE_PLAYER_CURRENT_COMMAND_NUMBER__INSTEAD_OF_TICKCOUNT
  43. #if defined( HL2_DLL )
  44. ConVar xc_uncrouch_on_jump( "xc_uncrouch_on_jump", "1", FCVAR_ARCHIVE, "Uncrouch when jump occurs" );
  45. #endif
  46. #if defined( HL2_DLL ) || defined( HL2_CLIENT_DLL )
  47. ConVar player_limit_jump_speed( "player_limit_jump_speed", "1", FCVAR_REPLICATED );
  48. #endif
  49. // [MD] I'll remove this eventually. For now, I want the ability to A/B the optimizations.
  50. bool g_bMovementOptimizations = true;
  51. // Roughly how often we want to update the info about the ground surface we're on.
  52. // We don't need to do this very often.
  53. #define CATEGORIZE_GROUND_SURFACE_INTERVAL 0.3f
  54. #define CATEGORIZE_GROUND_SURFACE_TICK_INTERVAL ( (int)( CATEGORIZE_GROUND_SURFACE_INTERVAL / TICK_INTERVAL ) )
  55. #define CHECK_STUCK_INTERVAL 1.0f
  56. #define CHECK_STUCK_TICK_INTERVAL ( (int)( CHECK_STUCK_INTERVAL / TICK_INTERVAL ) )
  57. #define CHECK_STUCK_INTERVAL_SP 0.2f
  58. #define CHECK_STUCK_TICK_INTERVAL_SP ( (int)( CHECK_STUCK_INTERVAL_SP / TICK_INTERVAL ) )
  59. //#define CHECK_LADDER_INTERVAL 0.2f
  60. //#define CHECK_LADDER_TICK_INTERVAL ( (int)( CHECK_LADDER_INTERVAL / TICK_INTERVAL ) )
  61. #define CHECK_LADDER_TICK_INTERVAL 2 // every other tick
  62. #define CHECK_LADDER_WEDGE_INTERVAL 0.5f
  63. #define CHECK_LADDER_WEDGE_TICK_INTERVAL ( (int)( CHECK_LADDER_WEDGE_INTERVAL / TICK_INTERVAL ) )
  64. #define NUM_CROUCH_HINTS 3
  65. #define MINIMUM_MOVE_FRACTION 0.0001f
  66. #define EFFECTIVELY_HORIZONTAL_NORMAL_Z 0.0001f
  67. extern IGameMovement *g_pGameMovement;
  68. //-----------------------------------------------------------------------------
  69. // Purpose:
  70. // Input : &vecSrc -
  71. // flDist -
  72. // flDelta -
  73. // Output : Vector
  74. //-----------------------------------------------------------------------------
  75. class CTraceFilterSkipTwoEntitiesAndCheckTeamMask : public CTraceFilterSkipTwoEntities
  76. {
  77. public:
  78. CTraceFilterSkipTwoEntitiesAndCheckTeamMask( const IHandleEntity *passentity = NULL, const IHandleEntity *passentity2 = NULL, int collisionGroup = COLLISION_GROUP_NONE ) :
  79. CTraceFilterSkipTwoEntities( passentity, passentity2, collisionGroup )
  80. {
  81. }
  82. bool ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask )
  83. {
  84. CBaseEntity *pEntity = EntityFromEntityHandle( pHandleEntity );
  85. if ( !pEntity )
  86. return false;
  87. const CBaseEntity *pSelf = EntityFromEntityHandle( GetPassEntity() );
  88. if ( !pSelf )
  89. return false;
  90. if ( GetCollisionGroup() == COLLISION_GROUP_PLAYER_MOVEMENT )
  91. {
  92. if ( pEntity->GetCollisionGroup() == COLLISION_GROUP_PROJECTILE && pSelf->GetTeamNumber() == pEntity->GetTeamNumber() )
  93. return false;
  94. unsigned int myTeamMask = ( pSelf->PhysicsSolidMaskForEntity() & ( CONTENTS_TEAM1 | CONTENTS_TEAM2 ) );
  95. unsigned int otherTeamMask = ( pEntity->PhysicsSolidMaskForEntity() & ( CONTENTS_TEAM1 | CONTENTS_TEAM2 ) );
  96. // See if we have a team and we're on the same team.
  97. // If we are on the same team, then don't collide.
  98. if ( myTeamMask != 0x0 && myTeamMask == otherTeamMask )
  99. {
  100. return false;
  101. }
  102. }
  103. return CTraceFilterSkipTwoEntities::ShouldHitEntity( pHandleEntity, contentsMask );
  104. }
  105. };
  106. #if defined( PLAYER_GETTING_STUCK_TESTING )
  107. // 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
  108. void CMoveData::SetAbsOrigin( const Vector &vec )
  109. {
  110. CGameMovement *gm = dynamic_cast< CGameMovement * >( g_pGameMovement );
  111. if ( gm && gm->GetMoveData() &&
  112. gm->player &&
  113. gm->player->entindex() == 1 &&
  114. gm->player->GetMoveType() == MOVETYPE_WALK )
  115. {
  116. trace_t pm;
  117. gm->TracePlayerBBox( vec, vec, gm->PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, pm );
  118. if ( pm.startsolid || pm.allsolid || pm.fraction != 1.0f )
  119. {
  120. Msg( "Player will become stuck at %f %f %f\n", VectorExpand( vec ) );
  121. }
  122. }
  123. m_vecAbsOrigin = vec;
  124. }
  125. #endif
  126. // See shareddefs.h
  127. #if PREDICTION_ERROR_CHECK_LEVEL > 0
  128. static ConVar diffcheck( "diffcheck", "0", FCVAR_REPLICATED );
  129. static ConVar diffcheck_playerslot( "diffcheck_playerslot", "0", FCVAR_REPLICATED );
  130. static ConVar diffcheck_spew( "diffcheck_spew", "1", FCVAR_REPLICATED, "Actually show diffcheck results." );
  131. class IDiffMgr
  132. {
  133. public:
  134. virtual void StartCommand( int nSlot, bool bServer, int nCommandNumber ) = 0;
  135. virtual void AddToDiff( int nSlot, bool bServer, int nCommandNumber, char const *string ) = 0;
  136. virtual void Validate( int nSlot, bool bServer, int nCommandNumber ) = 0;
  137. };
  138. static IDiffMgr *g_pDiffMgr = NULL;
  139. class CDiffStr
  140. {
  141. public:
  142. CDiffStr()
  143. {
  144. m_str[ 0 ] = 0;
  145. #if PREDICTION_ERROR_CHECK_STACKS_FOR_MISSING > 0
  146. m_stackEntries = 0;
  147. #endif
  148. }
  149. CDiffStr( char const *str )
  150. {
  151. Q_strncpy( m_str, str, sizeof( m_str ) );
  152. #if PREDICTION_ERROR_CHECK_STACKS_FOR_MISSING > 0
  153. m_stackEntries = GetCallStack( m_stackAddresses, ARRAYSIZE(m_stackAddresses), 4 ); //skip this constructor, IDiffMgr::AddToDiff(), DiffPrint(), and IGameMovement::DiffPrint()
  154. #endif
  155. }
  156. CDiffStr( const CDiffStr &src )
  157. {
  158. Q_strncpy( m_str, src.m_str, sizeof( m_str ) );
  159. #if PREDICTION_ERROR_CHECK_STACKS_FOR_MISSING > 0
  160. memcpy( m_stackAddresses, src.m_stackAddresses, sizeof( m_stackAddresses ) );
  161. m_stackEntries = src.m_stackEntries;
  162. #endif
  163. }
  164. char const *String()
  165. {
  166. return m_str;
  167. }
  168. #if PREDICTION_ERROR_CHECK_STACKS_FOR_MISSING > 0
  169. void **StackAddresses( void )
  170. {
  171. return m_stackAddresses;
  172. }
  173. const int StackEntries( void )
  174. {
  175. return m_stackEntries;
  176. }
  177. #endif
  178. private:
  179. char m_str[ 128 ];
  180. #if PREDICTION_ERROR_CHECK_STACKS_FOR_MISSING > 0
  181. void *m_stackAddresses[8];
  182. int m_stackEntries;
  183. #endif
  184. };
  185. // Per tick data
  186. class CDiffInfo
  187. {
  188. public:
  189. CDiffInfo() : m_nCommandNumber( 0 ) {}
  190. CDiffInfo( const CDiffInfo& src )
  191. {
  192. m_nCommandNumber = src.m_nCommandNumber;
  193. for ( int i = 0; i < src.m_Lines.Count(); ++i )
  194. {
  195. m_Lines.AddToTail( src.m_Lines[ i ] );
  196. }
  197. }
  198. static bool Less( const CDiffInfo& lhs, const CDiffInfo& rhs )
  199. {
  200. return lhs.m_nCommandNumber < rhs.m_nCommandNumber;
  201. }
  202. int m_nCommandNumber;
  203. CUtlVector< CDiffStr > m_Lines;
  204. bool m_bChecked;
  205. };
  206. class CDiffManager : public IDiffMgr
  207. {
  208. public:
  209. CDiffManager() :
  210. m_flLastSpew( -1.0f )
  211. {
  212. g_pDiffMgr = this;
  213. }
  214. virtual void StartCommand( int nSlot, bool bServer, int nCommandNumber )
  215. {
  216. #if defined( CLIENT_DLL )
  217. if ( !diffcheck.GetInt() )
  218. return;
  219. g_pDiffMgr = reinterpret_cast< IDiffMgr * >( diffcheck.GetInt() );
  220. g_pDiffMgr->StartCommand( nSlot, bServer, nCommandNumber );
  221. return;
  222. #endif
  223. // Msg( "%s Startcommand %d\n", bServer ? "sv" : "cl", nCommandNumber );
  224. diffcheck.SetValue( reinterpret_cast< int >( this ) );
  225. Assert( CBaseEntity::IsServer() );
  226. CUtlRBTree< CDiffInfo, int >& rb = bServer ? m_Data[ nSlot ].m_Server : m_Data[ nSlot ].m_Client;
  227. CDiffInfo search;
  228. search.m_nCommandNumber = nCommandNumber;
  229. int idx = rb.Find( search );
  230. if ( idx == rb.InvalidIndex() )
  231. {
  232. idx = rb.Insert( search );
  233. }
  234. CDiffInfo *slot = &rb[ idx ];
  235. slot->m_Lines.RemoveAll();
  236. }
  237. virtual void AddToDiff( int nSlot, bool bServer, int nCommandNumber, char const *string )
  238. {
  239. #if defined( CLIENT_DLL )
  240. if ( !diffcheck.GetInt() )
  241. return;
  242. g_pDiffMgr = reinterpret_cast< IDiffMgr * >( diffcheck.GetInt() );
  243. g_pDiffMgr->AddToDiff( nSlot, bServer, nCommandNumber, string );
  244. return;
  245. #endif
  246. Assert( CBaseEntity::IsServer() );
  247. // Msg( "%s Add %d %s\n", bServer ? "sv" : "cl", nCommandNumber, string );
  248. CUtlRBTree< CDiffInfo, int >& rb = bServer ? m_Data[ nSlot ].m_Server : m_Data[ nSlot ].m_Client;
  249. CDiffInfo search;
  250. search.m_nCommandNumber = nCommandNumber;
  251. int idx = rb.Find( search );
  252. if ( idx == rb.InvalidIndex() )
  253. {
  254. Assert( 0 );
  255. idx = rb.Insert( search );
  256. }
  257. CDiffInfo *slot = &rb[ idx ];
  258. CDiffStr line( string );
  259. slot->m_Lines.AddToTail( line );
  260. }
  261. enum EMismatched
  262. {
  263. DIFFCHECK_NOTREADY = 0,
  264. DIFFCHECK_MATCHED,
  265. DIFFCHECK_DIFFERS
  266. };
  267. bool ClientRecordExists( int nSlot, int cmd )
  268. {
  269. CDiffInfo clsearch;
  270. clsearch.m_nCommandNumber = cmd;
  271. int clidx = m_Data[ nSlot ].m_Client.Find( clsearch );
  272. return m_Data[ nSlot ].m_Client.IsValidIndex( clidx );
  273. }
  274. EMismatched IsMismatched( int nSlot, int svidx )
  275. {
  276. CDiffInfo *serverslot = &m_Data[ nSlot ].m_Server[ svidx ];
  277. // Now find the client version of this one
  278. CDiffInfo clsearch;
  279. clsearch.m_nCommandNumber = serverslot->m_nCommandNumber;
  280. int clidx = m_Data[ nSlot ].m_Client.Find( clsearch );
  281. if ( clidx == m_Data[ nSlot ].m_Client.InvalidIndex() )
  282. return DIFFCHECK_NOTREADY;
  283. // Now compare them
  284. CDiffInfo *clientslot = &m_Data[ nSlot ].m_Client[ clidx ];
  285. bool bSpew = false;
  286. if ( serverslot->m_Lines.Count() !=
  287. clientslot->m_Lines.Count() )
  288. {
  289. return DIFFCHECK_DIFFERS;
  290. }
  291. int maxSlot = MAX( serverslot->m_Lines.Count(), clientslot->m_Lines.Count() );
  292. if ( !bSpew )
  293. {
  294. for ( int i = 0; i < maxSlot; ++i )
  295. {
  296. CDiffStr *sv = NULL;
  297. CDiffStr *cl = NULL;
  298. if ( i < serverslot->m_Lines.Count() )
  299. {
  300. sv = &serverslot->m_Lines[ i ];
  301. }
  302. if ( i < clientslot->m_Lines.Count() )
  303. {
  304. cl = &clientslot->m_Lines[ i ];
  305. }
  306. if ( Q_stricmp( sv ? sv->String() : "(missing)", cl ? cl->String() : "(missing)" ) )
  307. {
  308. return DIFFCHECK_DIFFERS;
  309. }
  310. }
  311. }
  312. return DIFFCHECK_MATCHED;
  313. }
  314. virtual void Validate( int nSlot, bool bServer, int nCommandNumber )
  315. {
  316. #if defined( CLIENT_DLL )
  317. if ( !diffcheck.GetInt() )
  318. return;
  319. g_pDiffMgr = reinterpret_cast< IDiffMgr * >( diffcheck.GetInt() );
  320. g_pDiffMgr->Validate( nSlot, bServer, nCommandNumber );
  321. return;
  322. #endif
  323. Assert( CBaseEntity::IsServer() );
  324. // Only do this on the client
  325. if ( !bServer )
  326. return;
  327. if ( !diffcheck_spew.GetBool() )
  328. return;
  329. // Don't care about this player slot
  330. if ( diffcheck_playerslot.GetInt() != nSlot )
  331. return;
  332. // Find the last server command number
  333. if ( m_Data[ nSlot ].m_Server.Count() <= 0 )
  334. return;
  335. int svidx = m_Data[ nSlot ].m_Server.LastInorder();
  336. EMismatched eMisMatched = IsMismatched( nSlot, svidx );
  337. if ( eMisMatched == DIFFCHECK_NOTREADY )
  338. {
  339. return;
  340. }
  341. if ( eMisMatched == DIFFCHECK_DIFFERS )
  342. {
  343. CUtlVector< int > vecPrev;
  344. int nCur = svidx;
  345. do
  346. {
  347. int prev = m_Data[ nSlot ].m_Server.PrevInorder( nCur );
  348. if ( m_Data[ nSlot ].m_Server.IsValidIndex( prev ) &&
  349. ClientRecordExists( nSlot, m_Data[ nSlot ].m_Server[ prev ].m_nCommandNumber ) )
  350. {
  351. //SpewRecords( "prev", prev );
  352. vecPrev.AddToHead( prev );
  353. }
  354. else
  355. {
  356. break;
  357. }
  358. nCur = prev;
  359. } while ( vecPrev.Count() < 10 );
  360. Msg( "-----\n" );
  361. for ( int p = 0; p < vecPrev.Count(); ++p )
  362. {
  363. SpewRecords( nSlot, "prev", vecPrev[ p ] );
  364. }
  365. SpewRecords( nSlot, "bad ", svidx );
  366. }
  367. }
  368. void SpewRecords( int nSlot, char const *prefix, int svidx )
  369. {
  370. CDiffInfo *serverslot = &m_Data[ nSlot ].m_Server[ svidx ];
  371. // Now find the client version of this one
  372. CDiffInfo clsearch;
  373. clsearch.m_nCommandNumber = serverslot->m_nCommandNumber;
  374. int clidx = m_Data[ nSlot ].m_Client.Find( clsearch );
  375. if ( clidx == m_Data[ nSlot ].m_Client.InvalidIndex() )
  376. return;
  377. // Now compare them
  378. CDiffInfo *clientslot = &m_Data[ nSlot ].m_Client[ clidx ];
  379. int maxSlot = MAX( serverslot->m_Lines.Count(), clientslot->m_Lines.Count() );
  380. for ( int i = 0; i < maxSlot; ++i )
  381. {
  382. char const *sv = "(missing)";
  383. char const *cl = "(missing)";
  384. #if PREDICTION_ERROR_CHECK_STACKS_FOR_MISSING > 0
  385. int iHaveString = 0;
  386. #endif
  387. if ( i < serverslot->m_Lines.Count() )
  388. {
  389. sv = serverslot->m_Lines[ i ].String();
  390. #if PREDICTION_ERROR_CHECK_STACKS_FOR_MISSING > 0
  391. iHaveString |= 1;
  392. #endif
  393. }
  394. if ( i < clientslot->m_Lines.Count() )
  395. {
  396. cl = clientslot->m_Lines[ i ].String();
  397. #if PREDICTION_ERROR_CHECK_STACKS_FOR_MISSING > 0
  398. iHaveString |= 2;
  399. #endif
  400. }
  401. bool bDiffers = Q_stricmp( sv, cl ) ? true : false;
  402. Msg( "%s%s%d: sv[%100.100s] cl[%100.100s]\n",
  403. prefix,
  404. bDiffers ? "+++" : " ",
  405. serverslot->m_nCommandNumber,
  406. sv,
  407. cl );
  408. #if PREDICTION_ERROR_CHECK_STACKS_FOR_MISSING > 0
  409. if( (iHaveString > 0) && (iHaveString < 3) )
  410. {
  411. CDiffStr *pHaveString = (iHaveString == 1) ? &serverslot->m_Lines[ i ] : &clientslot->m_Lines[ i ];
  412. char szFormattedStack[4096];
  413. szFormattedStack[0] = '\0';
  414. TranslateStackInfo( pHaveString->StackAddresses(), pHaveString->StackEntries(), szFormattedStack, ARRAYSIZE( szFormattedStack ), "\n\t" );
  415. Msg( "Stack for existing side:\n\t%s\n", szFormattedStack );
  416. }
  417. #endif
  418. }
  419. }
  420. private:
  421. struct User_t
  422. {
  423. User_t() :
  424. m_Client( 0, 0, CDiffInfo::Less ),
  425. m_Server( 0, 0, CDiffInfo::Less )
  426. {
  427. }
  428. CUtlRBTree< CDiffInfo, int > m_Server;
  429. CUtlRBTree< CDiffInfo, int > m_Client;
  430. };
  431. User_t m_Data[ MAX_SPLITSCREEN_PLAYERS ];
  432. float m_flLastSpew;
  433. };
  434. static CDiffManager g_DiffMgr;
  435. bool IsValidForErrorCheck( CBasePlayer *pl )
  436. {
  437. Assert( pl );
  438. if ( !pl->GetCurrentUserCommand() )
  439. return false;
  440. if ( pl->entindex() == 1 )
  441. return true;
  442. if ( pl->IsSplitScreenPlayer() )
  443. return true;
  444. return false;
  445. }
  446. void DiffPrint( bool bServer, int nCommandNumber, char const *fmt, ... )
  447. {
  448. // Only track stuff for local player
  449. CBasePlayer *player = CBaseEntity::GetPredictionPlayer();
  450. if ( !player || !IsValidForErrorCheck( player ) )
  451. {
  452. return;
  453. }
  454. va_list argptr;
  455. char string[1024];
  456. va_start (argptr,fmt);
  457. int len = Q_vsnprintf(string, sizeof( string ), fmt,argptr);
  458. va_end (argptr);
  459. if ( g_pDiffMgr )
  460. {
  461. // Strip any \n at the end that the user accidently put int
  462. if ( len > 0 && string[ len -1 ] == '\n' )
  463. {
  464. string[ len - 1 ] = 0;
  465. }
  466. g_pDiffMgr->AddToDiff( player->GetSplitScreenPlayerSlot(), bServer, nCommandNumber, string );
  467. }
  468. }
  469. void _CheckV( int tick, char const *ctx, const Vector &vel )
  470. {
  471. DiffPrint( CBaseEntity::IsServer(), tick, "%20.20s %f %f %f", ctx, vel.x, vel.y, vel.z );
  472. }
  473. #define CheckV( tick, ctx, vel ) _CheckV( tick, ctx, vel );
  474. static void StartCommand( bool bServer, int nCommandNumber )
  475. {
  476. // Only track stuff for local player
  477. CBasePlayer *player = CBaseEntity::GetPredictionPlayer();
  478. if ( !player || !IsValidForErrorCheck( player ) )
  479. {
  480. return;
  481. }
  482. if ( g_pDiffMgr )
  483. {
  484. g_pDiffMgr->StartCommand( player->GetSplitScreenPlayerSlot(), bServer, nCommandNumber );
  485. }
  486. }
  487. static void Validate( bool bServer, int nCommandNumber )
  488. {
  489. // Only track stuff for local player
  490. CBasePlayer *player = CBaseEntity::GetPredictionPlayer();
  491. if ( !player || !IsValidForErrorCheck( player ) )
  492. {
  493. return;
  494. }
  495. if ( g_pDiffMgr )
  496. {
  497. g_pDiffMgr->Validate( player->GetSplitScreenPlayerSlot(), bServer, nCommandNumber );
  498. }
  499. }
  500. void CGameMovement::DiffPrint( char const *fmt, ... )
  501. {
  502. if ( !player || !player->GetCurrentUserCommand() )
  503. return;
  504. va_list argptr;
  505. char string[1024];
  506. va_start (argptr,fmt);
  507. Q_vsnprintf(string, sizeof( string ), fmt,argptr);
  508. va_end (argptr);
  509. ::DiffPrint( CBaseEntity::IsServer(), player->CurrentCommandNumber(), "%s", string );
  510. }
  511. #else
  512. void DiffPrint( bool bServer, int nCommandNumber, char const *fmt, ... )
  513. {
  514. // Nothing
  515. }
  516. static void StartCommand( bool bServer, int nCommandNumber )
  517. {
  518. }
  519. static void Validate( bool bServer, int nCommandNumber )
  520. {
  521. }
  522. #define CheckV( tick, ctx, vel )
  523. void CGameMovement::DiffPrint( char const *fmt, ... )
  524. {
  525. }
  526. #endif // !PREDICTION_ERROR_CHECK_LEVEL
  527. void COM_Log( const char *pszFile, const char *fmt, ...)
  528. {
  529. va_list argptr;
  530. char string[1024];
  531. FileHandle_t fp;
  532. const char *pfilename;
  533. if ( !pszFile )
  534. {
  535. pfilename = "hllog.txt";
  536. }
  537. else
  538. {
  539. pfilename = pszFile;
  540. }
  541. va_start (argptr,fmt);
  542. Q_vsnprintf(string, sizeof( string ), fmt,argptr);
  543. va_end (argptr);
  544. fp = filesystem->Open( pfilename, "a+t");
  545. if (fp)
  546. {
  547. filesystem->FPrintf(fp, "%s", string);
  548. filesystem->Close(fp);
  549. }
  550. }
  551. #ifndef CLIENT_DLL
  552. //-----------------------------------------------------------------------------
  553. // Purpose: Debug - draw the displacement collision plane.
  554. //-----------------------------------------------------------------------------
  555. void DrawDispCollPlane( CBaseTrace *pTrace )
  556. {
  557. float flLength = 30.0f;
  558. // Create a basis, based on the impact normal.
  559. int nMajorAxis = 0;
  560. Vector vecBasisU, vecBasisV, vecNormal;
  561. vecNormal = pTrace->plane.normal;
  562. float flAxisValue = vecNormal[0];
  563. if ( fabs( vecNormal[1] ) > fabs( flAxisValue ) ) { nMajorAxis = 1; flAxisValue = vecNormal[1]; }
  564. if ( fabs( vecNormal[2] ) > fabs( flAxisValue ) ) { nMajorAxis = 2; }
  565. if ( ( nMajorAxis == 1 ) || ( nMajorAxis == 2 ) )
  566. {
  567. vecBasisU.Init( 1.0f, 0.0f, 0.0f );
  568. }
  569. else
  570. {
  571. vecBasisU.Init( 0.0f, 1.0f, 0.0f );
  572. }
  573. vecBasisV = vecNormal.Cross( vecBasisU );
  574. VectorNormalize( vecBasisV );
  575. vecBasisU = vecBasisV.Cross( vecNormal );
  576. VectorNormalize( vecBasisU );
  577. // Create the impact point. Push off the surface a bit.
  578. Vector vecImpactPoint = pTrace->startpos + pTrace->fraction * ( pTrace->endpos - pTrace->startpos );
  579. vecImpactPoint += vecNormal;
  580. // Generate a quad to represent the plane.
  581. Vector vecPlanePoints[4];
  582. vecPlanePoints[0] = vecImpactPoint + ( vecBasisU * -flLength ) + ( vecBasisV * -flLength );
  583. vecPlanePoints[1] = vecImpactPoint + ( vecBasisU * -flLength ) + ( vecBasisV * flLength );
  584. vecPlanePoints[2] = vecImpactPoint + ( vecBasisU * flLength ) + ( vecBasisV * flLength );
  585. vecPlanePoints[3] = vecImpactPoint + ( vecBasisU * flLength ) + ( vecBasisV * -flLength );
  586. #if 0
  587. // Test facing.
  588. Vector vecEdges[2];
  589. vecEdges[0] = vecPlanePoints[1] - vecPlanePoints[0];
  590. vecEdges[1] = vecPlanePoints[2] - vecPlanePoints[0];
  591. Vector vecCross = vecEdges[0].Cross( vecEdges[1] );
  592. if ( vecCross.Dot( vecNormal ) < 0.0f )
  593. {
  594. // Reverse winding.
  595. }
  596. #endif
  597. // Draw the plane.
  598. NDebugOverlay::Triangle( vecPlanePoints[0], vecPlanePoints[1], vecPlanePoints[2], 125, 125, 125, 125, false, 5.0f );
  599. NDebugOverlay::Triangle( vecPlanePoints[0], vecPlanePoints[2], vecPlanePoints[3], 125, 125, 125, 125, false, 5.0f );
  600. NDebugOverlay::Line( vecPlanePoints[0], vecPlanePoints[1], 255, 255, 255, false, 5.0f );
  601. NDebugOverlay::Line( vecPlanePoints[1], vecPlanePoints[2], 255, 255, 255, false, 5.0f );
  602. NDebugOverlay::Line( vecPlanePoints[2], vecPlanePoints[3], 255, 255, 255, false, 5.0f );
  603. NDebugOverlay::Line( vecPlanePoints[3], vecPlanePoints[0], 255, 255, 255, false, 5.0f );
  604. // Draw the normal.
  605. NDebugOverlay::Line( vecImpactPoint, vecImpactPoint + ( vecNormal * flLength ), 255, 0, 0, false, 5.0f );
  606. }
  607. #endif
  608. //-----------------------------------------------------------------------------
  609. // Purpose: Constructs GameMovement interface
  610. //-----------------------------------------------------------------------------
  611. CGameMovement::CGameMovement( void )
  612. {
  613. m_nOldWaterLevel = WL_NotInWater;
  614. m_flWaterEntryTime = 0;
  615. m_nOnLadder = 0;
  616. m_bProcessingMovement = false;
  617. mv = NULL;
  618. memset( m_flStuckCheckTime, 0, sizeof(m_flStuckCheckTime) );
  619. m_pTraceListData = NULL;
  620. }
  621. //-----------------------------------------------------------------------------
  622. // Purpose: Destructor
  623. //-----------------------------------------------------------------------------
  624. CGameMovement::~CGameMovement( void )
  625. {
  626. if ( enginetrace )
  627. {
  628. enginetrace->FreeTraceListData(m_pTraceListData);
  629. }
  630. }
  631. //--------------------------------------------------------------------------------------------------------
  632. enum
  633. {
  634. MAX_NESTING = 8
  635. };
  636. static CTraceFilterSkipTwoEntitiesAndCheckTeamMask s_TraceFilter[MAX_NESTING];
  637. static int s_nTraceFilterCount = 0;
  638. ITraceFilter *CGameMovement::LockTraceFilter( int collisionGroup )
  639. {
  640. // If this assertion triggers, you forgot to call UnlockTraceFilter
  641. Assert( s_nTraceFilterCount < MAX_NESTING );
  642. if ( s_nTraceFilterCount >= MAX_NESTING )
  643. return NULL;
  644. CTraceFilterSkipTwoEntitiesAndCheckTeamMask *pFilter = &s_TraceFilter[s_nTraceFilterCount++];
  645. pFilter->SetPassEntity( mv->m_nPlayerHandle.Get() );
  646. pFilter->SetCollisionGroup( collisionGroup );
  647. return pFilter;
  648. }
  649. void CGameMovement::UnlockTraceFilter( ITraceFilter *&pFilter )
  650. {
  651. Assert( s_nTraceFilterCount > 0 );
  652. --s_nTraceFilterCount;
  653. Assert( &s_TraceFilter[s_nTraceFilterCount] == pFilter );
  654. pFilter = NULL;
  655. }
  656. //-----------------------------------------------------------------------------
  657. // Purpose: Allow bots etc to use slightly different solid masks
  658. //-----------------------------------------------------------------------------
  659. unsigned int CGameMovement::PlayerSolidMask( bool brushOnly, CBasePlayer *testPlayer ) const
  660. {
  661. return ( brushOnly ) ? MASK_PLAYERSOLID_BRUSHONLY : MASK_PLAYERSOLID;
  662. }
  663. //-----------------------------------------------------------------------------
  664. // Purpose:
  665. // Input : type -
  666. // Output : int
  667. //-----------------------------------------------------------------------------
  668. int CGameMovement::GetCheckInterval( IntervalType_t type )
  669. {
  670. int tickInterval = 1;
  671. switch ( type )
  672. {
  673. default:
  674. tickInterval = 1;
  675. break;
  676. case GROUND:
  677. tickInterval = CATEGORIZE_GROUND_SURFACE_TICK_INTERVAL;
  678. break;
  679. case STUCK:
  680. // 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
  681. if ( player->m_StuckLast != 0 )
  682. {
  683. tickInterval = 1;
  684. }
  685. else
  686. {
  687. if ( gpGlobals->maxClients == 1 )
  688. {
  689. tickInterval = CHECK_STUCK_TICK_INTERVAL_SP;
  690. }
  691. else
  692. {
  693. tickInterval = CHECK_STUCK_TICK_INTERVAL;
  694. }
  695. }
  696. break;
  697. case LADDER:
  698. tickInterval = CHECK_LADDER_TICK_INTERVAL;
  699. break;
  700. case LADDER_WEDGE:
  701. tickInterval = CHECK_LADDER_WEDGE_TICK_INTERVAL;
  702. break;
  703. }
  704. return tickInterval;
  705. }
  706. //-----------------------------------------------------------------------------
  707. // Purpose:
  708. // Input : type -
  709. // Output : Returns true on success, false on failure.
  710. //-----------------------------------------------------------------------------
  711. bool CGameMovement::CheckInterval( IntervalType_t type )
  712. {
  713. int tickInterval = GetCheckInterval( type );
  714. if ( g_bMovementOptimizations )
  715. {
  716. return (player->CurrentCommandNumber() + player->entindex()) % tickInterval == 0;
  717. }
  718. else
  719. {
  720. return true;
  721. }
  722. }
  723. //-----------------------------------------------------------------------------
  724. // Purpose:
  725. // Input : ducked -
  726. // Output : const Vector
  727. //-----------------------------------------------------------------------------
  728. const Vector& CGameMovement::GetPlayerMins( bool ducked ) const
  729. {
  730. return ducked ? VEC_DUCK_HULL_MIN : VEC_HULL_MIN;
  731. }
  732. //-----------------------------------------------------------------------------
  733. // Purpose:
  734. // Input : ducked -
  735. // Output : const Vector
  736. //-----------------------------------------------------------------------------
  737. const Vector& CGameMovement::GetPlayerMaxs( bool ducked ) const
  738. {
  739. return ducked ? VEC_DUCK_HULL_MAX : VEC_HULL_MAX;
  740. }
  741. //-----------------------------------------------------------------------------
  742. // Purpose:
  743. // Input :
  744. // Output : const Vector
  745. //-----------------------------------------------------------------------------
  746. const Vector& CGameMovement::GetPlayerMins( void ) const
  747. {
  748. if ( player->IsObserver() )
  749. {
  750. return VEC_OBS_HULL_MIN;
  751. }
  752. else
  753. {
  754. return player->m_Local.m_bDucked ? VEC_DUCK_HULL_MIN : VEC_HULL_MIN;
  755. }
  756. }
  757. //-----------------------------------------------------------------------------
  758. // Purpose:
  759. // Input :
  760. // Output : const Vector
  761. //-----------------------------------------------------------------------------
  762. const Vector& CGameMovement::GetPlayerMaxs( void ) const
  763. {
  764. if ( player->IsObserver() )
  765. {
  766. return VEC_OBS_HULL_MAX;
  767. }
  768. else
  769. {
  770. return player->m_Local.m_bDucked ? VEC_DUCK_HULL_MAX : VEC_HULL_MAX;
  771. }
  772. }
  773. //-----------------------------------------------------------------------------
  774. // Purpose:
  775. // Input : ducked -
  776. // Output : const Vector
  777. //-----------------------------------------------------------------------------
  778. const Vector& CGameMovement::GetPlayerViewOffset( bool ducked ) const
  779. {
  780. return ducked ? VEC_DUCK_VIEW : VEC_VIEW;
  781. }
  782. CBaseHandle CGameMovement::TestPlayerPosition( const Vector& pos, int collisionGroup, trace_t& pm )
  783. {
  784. ++m_nTraceCount;
  785. Ray_t ray;
  786. ray.Init( pos, pos, GetPlayerMins(), GetPlayerMaxs() );
  787. ITraceFilter *filter = LockTraceFilter( collisionGroup );
  788. UTIL_TraceRay( ray, PlayerSolidMask(), filter, &pm );
  789. UnlockTraceFilter( filter );
  790. if ( (pm.contents & PlayerSolidMask()) && pm.m_pEnt )
  791. return pm.m_pEnt->GetRefEHandle();
  792. return INVALID_EHANDLE;
  793. }
  794. /*
  795. // FIXME FIXME: Does this need to be hooked up?
  796. bool CGameMovement::IsWet() const
  797. {
  798. return ((pev->flags & FL_INRAIN) != 0) || (m_WetTime >= gpGlobals->time);
  799. }
  800. //-----------------------------------------------------------------------------
  801. // Plants player footprint decals
  802. //-----------------------------------------------------------------------------
  803. #define PLAYER_HALFWIDTH 12
  804. void CGameMovement::PlantFootprint( surfacedata_t *psurface )
  805. {
  806. // Can't plant footprints on fake materials (ladders, wading)
  807. if ( psurface->gameMaterial != 'X' )
  808. {
  809. int footprintDecal = -1;
  810. // Figure out which footprint type to plant...
  811. // Use the wet footprint if we're wet...
  812. if (IsWet())
  813. {
  814. footprintDecal = DECAL_FOOTPRINT_WET;
  815. }
  816. else
  817. {
  818. // FIXME: Activate this once we decide to pull the trigger on it.
  819. // NOTE: We could add in snow, mud, others here
  820. // switch(psurface->gameMaterial)
  821. // {
  822. // case 'D':
  823. // footprintDecal = DECAL_FOOTPRINT_DIRT;
  824. // break;
  825. // }
  826. }
  827. if (footprintDecal != -1)
  828. {
  829. Vector right;
  830. AngleVectors( pev->angles, 0, &right, 0 );
  831. // Figure out where the top of the stepping leg is
  832. trace_t tr;
  833. Vector hipOrigin;
  834. VectorMA( pev->origin,
  835. m_IsFootprintOnLeft ? -PLAYER_HALFWIDTH : PLAYER_HALFWIDTH,
  836. right, hipOrigin );
  837. // Find where that leg hits the ground
  838. UTIL_TraceLine( hipOrigin, hipOrigin + Vector(0, 0, -COORD_EXTENT * 1.74),
  839. MASK_SOLID_BRUSHONLY, edict(), COLLISION_GROUP_NONE, &tr);
  840. unsigned char mType = TEXTURETYPE_Find( &tr );
  841. // Splat a decal
  842. CPVSFilter filter( tr.endpos );
  843. te->FootprintDecal( filter, 0.0f, &tr.endpos, &right, ENTINDEX(tr.u.ent),
  844. gDecals[footprintDecal].index, mType );
  845. }
  846. }
  847. // Switch feet for next time
  848. m_IsFootprintOnLeft = !m_IsFootprintOnLeft;
  849. }
  850. #define WET_TIME 5.f // how many seconds till we're completely wet/dry
  851. #define DRY_TIME 20.f // how many seconds till we're completely wet/dry
  852. void CBasePlayer::UpdateWetness()
  853. {
  854. // BRJ 1/7/01
  855. // Check for whether we're in a rainy area....
  856. // Do this by tracing a line straight down with a size guaranteed to
  857. // be larger than the map
  858. // Update wetness based on whether we're in rain or not...
  859. trace_t tr;
  860. UTIL_TraceLine( pev->origin, pev->origin + Vector(0, 0, -COORD_EXTENT * 1.74),
  861. MASK_SOLID_BRUSHONLY, edict(), COLLISION_GROUP_NONE, &tr);
  862. if (tr.surface.flags & SURF_WET)
  863. {
  864. if (! (pev->flags & FL_INRAIN) )
  865. {
  866. // Transition...
  867. // Figure out how wet we are now (we were drying off...)
  868. float wetness = (m_WetTime - gpGlobals->time) / DRY_TIME;
  869. if (wetness < 0.0f)
  870. wetness = 0.0f;
  871. // Here, wet time represents the time at which we get totally wet
  872. m_WetTime = gpGlobals->time + (1.0 - wetness) * WET_TIME;
  873. pev->flags |= FL_INRAIN;
  874. }
  875. }
  876. else
  877. {
  878. if ((pev->flags & FL_INRAIN) != 0)
  879. {
  880. // Transition...
  881. // Figure out how wet we are now (we were getting more wet...)
  882. float wetness = 1.0f + (gpGlobals->time - m_WetTime) / WET_TIME;
  883. if (wetness > 1.0f)
  884. wetness = 1.0f;
  885. // Here, wet time represents the time at which we get totally dry
  886. m_WetTime = gpGlobals->time + wetness * DRY_TIME;
  887. pev->flags &= ~FL_INRAIN;
  888. }
  889. }
  890. }
  891. */
  892. //-----------------------------------------------------------------------------
  893. // Purpose:
  894. //-----------------------------------------------------------------------------
  895. void CGameMovement::CategorizeGroundSurface( trace_t &pm )
  896. {
  897. IPhysicsSurfaceProps *physprops = MoveHelper()->GetSurfaceProps();
  898. player->m_surfaceProps = pm.surface.surfaceProps;
  899. player->m_pSurfaceData = physprops->GetSurfaceData( player->m_surfaceProps );
  900. physprops->GetPhysicsProperties( player->m_surfaceProps, NULL, NULL, &player->m_surfaceFriction, NULL );
  901. // HACKHACK: Scale this to fudge the relationship between vphysics friction values and player friction values.
  902. // A value of 0.8f feels pretty normal for vphysics, whereas 1.0f is normal for players.
  903. // This scaling trivially makes them equivalent. REVISIT if this affects low friction surfaces too much.
  904. player->m_surfaceFriction *= 1.25f;
  905. if ( player->m_surfaceFriction > 1.0f )
  906. player->m_surfaceFriction = 1.0f;
  907. player->m_chTextureType = player->m_pSurfaceData->game.material;
  908. }
  909. bool CGameMovement::IsDead( void ) const
  910. {
  911. return ( player->m_iHealth <= 0 ) ? true : false;
  912. }
  913. //-----------------------------------------------------------------------------
  914. // Figures out how the constraint should slow us down
  915. //-----------------------------------------------------------------------------
  916. float CGameMovement::ComputeConstraintSpeedFactor( void )
  917. {
  918. // If we have a constraint, slow down because of that too.
  919. if ( !mv || mv->m_flConstraintRadius == 0.0f )
  920. return 1.0f;
  921. float flDistSq = mv->GetAbsOrigin().DistToSqr( mv->m_vecConstraintCenter );
  922. float flOuterRadiusSq = mv->m_flConstraintRadius * mv->m_flConstraintRadius;
  923. float flInnerRadiusSq = mv->m_flConstraintRadius - mv->m_flConstraintWidth;
  924. flInnerRadiusSq *= flInnerRadiusSq;
  925. // Only slow us down if we're inside the constraint ring
  926. if ((flDistSq <= flInnerRadiusSq) || (flDistSq >= flOuterRadiusSq))
  927. return 1.0f;
  928. // Only slow us down if we're running away from the center
  929. Vector vecDesired;
  930. VectorMultiply( m_vecForward, mv->m_flForwardMove, vecDesired );
  931. VectorMA( vecDesired, mv->m_flSideMove, m_vecRight, vecDesired );
  932. VectorMA( vecDesired, mv->m_flUpMove, m_vecUp, vecDesired );
  933. Vector vecDelta;
  934. VectorSubtract( mv->GetAbsOrigin(), mv->m_vecConstraintCenter, vecDelta );
  935. VectorNormalize( vecDelta );
  936. VectorNormalize( vecDesired );
  937. if (DotProduct( vecDelta, vecDesired ) < 0.0f)
  938. return 1.0f;
  939. float flFrac = (sqrt(flDistSq) - (mv->m_flConstraintRadius - mv->m_flConstraintWidth)) / mv->m_flConstraintWidth;
  940. float flSpeedFactor = Lerp( flFrac, 1.0f, mv->m_flConstraintSpeedFactor );
  941. return flSpeedFactor;
  942. }
  943. //-----------------------------------------------------------------------------
  944. // Purpose:
  945. //-----------------------------------------------------------------------------
  946. void CGameMovement::CheckParameters( void )
  947. {
  948. QAngle v_angle;
  949. if ( player->GetMoveType() != MOVETYPE_ISOMETRIC &&
  950. player->GetMoveType() != MOVETYPE_NOCLIP &&
  951. player->GetMoveType() != MOVETYPE_OBSERVER )
  952. {
  953. float spd;
  954. float maxspeed;
  955. spd = ( mv->m_flForwardMove * mv->m_flForwardMove ) +
  956. ( mv->m_flSideMove * mv->m_flSideMove ) +
  957. ( mv->m_flUpMove * mv->m_flUpMove );
  958. maxspeed = mv->m_flClientMaxSpeed;
  959. if ( maxspeed != 0.0 )
  960. {
  961. mv->m_flMaxSpeed = MIN( maxspeed, mv->m_flMaxSpeed );
  962. }
  963. // Slow down by the speed factor
  964. float flSpeedFactor = 1.0f;
  965. if (player->m_pSurfaceData)
  966. {
  967. flSpeedFactor = player->m_pSurfaceData->game.maxSpeedFactor;
  968. }
  969. // If we have a constraint, slow down because of that too.
  970. float flConstraintSpeedFactor = ComputeConstraintSpeedFactor();
  971. if (flConstraintSpeedFactor < flSpeedFactor)
  972. flSpeedFactor = flConstraintSpeedFactor;
  973. mv->m_flMaxSpeed *= flSpeedFactor;
  974. if ( g_bMovementOptimizations )
  975. {
  976. // Same thing but only do the sqrt if we have to.
  977. if ( ( spd != 0.0 ) && ( spd > mv->m_flMaxSpeed*mv->m_flMaxSpeed ) )
  978. {
  979. float fRatio = mv->m_flMaxSpeed / sqrt( spd );
  980. mv->m_flForwardMove *= fRatio;
  981. mv->m_flSideMove *= fRatio;
  982. mv->m_flUpMove *= fRatio;
  983. }
  984. }
  985. else
  986. {
  987. spd = sqrt( spd );
  988. if ( ( spd != 0.0 ) && ( spd > mv->m_flMaxSpeed ) )
  989. {
  990. float fRatio = mv->m_flMaxSpeed / spd;
  991. mv->m_flForwardMove *= fRatio;
  992. mv->m_flSideMove *= fRatio;
  993. mv->m_flUpMove *= fRatio;
  994. }
  995. }
  996. }
  997. if ( player->GetFlags() & FL_FROZEN ||
  998. player->GetFlags() & FL_ONTRAIN ||
  999. IsDead() )
  1000. {
  1001. mv->m_flForwardMove = 0;
  1002. mv->m_flSideMove = 0;
  1003. mv->m_flUpMove = 0;
  1004. }
  1005. DecayViewPunchAngle();
  1006. // Take angles from command.
  1007. if ( !IsDead() )
  1008. {
  1009. v_angle = mv->m_vecAngles;
  1010. v_angle = v_angle + player->m_Local.m_viewPunchAngle;
  1011. // Now adjust roll angle
  1012. if ( player->GetMoveType() != MOVETYPE_ISOMETRIC &&
  1013. player->GetMoveType() != MOVETYPE_NOCLIP )
  1014. {
  1015. mv->m_vecAngles[ROLL] = CalcRoll( v_angle, mv->m_vecVelocity, sv_rollangle.GetFloat(), sv_rollspeed.GetFloat() );
  1016. }
  1017. else
  1018. {
  1019. mv->m_vecAngles[ROLL] = 0.0; // v_angle[ ROLL ];
  1020. }
  1021. mv->m_vecAngles[PITCH] = v_angle[PITCH];
  1022. mv->m_vecAngles[YAW] = v_angle[YAW];
  1023. }
  1024. else
  1025. {
  1026. mv->m_vecAngles = mv->m_vecOldAngles;
  1027. }
  1028. // Set dead player view_offset
  1029. if ( IsDead() )
  1030. {
  1031. player->SetViewOffset( VEC_DEAD_VIEWHEIGHT );
  1032. }
  1033. // Adjust client view angles to match values used on server.
  1034. if ( mv->m_vecAngles[YAW] > 180.0f )
  1035. {
  1036. mv->m_vecAngles[YAW] -= 360.0f;
  1037. }
  1038. }
  1039. void CGameMovement::ReduceTimers( void )
  1040. {
  1041. float frame_msec = 1000.0f * gpGlobals->frametime;
  1042. int nFrameMsec = (int)frame_msec;
  1043. if ( player->m_Local.m_nDuckTimeMsecs > 0 )
  1044. {
  1045. player->m_Local.m_nDuckTimeMsecs -= nFrameMsec;
  1046. if ( player->m_Local.m_nDuckTimeMsecs < 0 )
  1047. {
  1048. player->m_Local.m_nDuckTimeMsecs = 0;
  1049. }
  1050. }
  1051. if ( player->m_Local.m_nDuckJumpTimeMsecs > 0 )
  1052. {
  1053. player->m_Local.m_nDuckJumpTimeMsecs -= nFrameMsec;
  1054. if ( player->m_Local.m_nDuckJumpTimeMsecs < 0 )
  1055. {
  1056. player->m_Local.m_nDuckJumpTimeMsecs = 0;
  1057. }
  1058. }
  1059. if ( player->m_Local.m_nJumpTimeMsecs > 0 )
  1060. {
  1061. player->m_Local.m_nJumpTimeMsecs -= nFrameMsec;
  1062. if ( player->m_Local.m_nJumpTimeMsecs < 0 )
  1063. {
  1064. player->m_Local.m_nJumpTimeMsecs = 0;
  1065. }
  1066. }
  1067. if ( player->m_flSwimSoundTime > 0 )
  1068. {
  1069. player->m_flSwimSoundTime -= frame_msec;
  1070. if ( player->m_flSwimSoundTime < 0 )
  1071. {
  1072. player->m_flSwimSoundTime = 0;
  1073. }
  1074. }
  1075. }
  1076. // get a conservative bounds for this player's movement traces
  1077. // This allows gamemovement to optimize those traces
  1078. void CGameMovement::SetupMovementBounds( CMoveData *move )
  1079. {
  1080. if ( m_pTraceListData )
  1081. {
  1082. m_pTraceListData->Reset();
  1083. }
  1084. else
  1085. {
  1086. m_pTraceListData = enginetrace->AllocTraceListData();
  1087. }
  1088. if ( !move->m_nPlayerHandle.IsValid() )
  1089. {
  1090. return;
  1091. }
  1092. CBasePlayer *pPlayer = (CBasePlayer *)move->m_nPlayerHandle.Get();
  1093. Vector moveMins, moveMaxs;
  1094. ClearBounds( moveMins, moveMaxs );
  1095. Vector start = move->GetAbsOrigin();
  1096. float radius = ((move->m_vecVelocity.Length() + move->m_flMaxSpeed) * gpGlobals->frametime) + 1.0f;
  1097. // NOTE: assumes the unducked bbox encloses the ducked bbox
  1098. Vector boxMins = GetPlayerMins(false);
  1099. Vector boxMaxs = GetPlayerMaxs(false);
  1100. // bloat by traveling the max velocity in all directions, plus the stepsize up/down
  1101. Vector bloat;
  1102. bloat.Init(radius, radius, radius);
  1103. bloat.z += pPlayer->m_Local.m_flStepSize;
  1104. AddPointToBounds( start + boxMaxs + bloat, moveMins, moveMaxs );
  1105. AddPointToBounds( start + boxMins - bloat, moveMins, moveMaxs );
  1106. // now build an optimized trace within these bounds
  1107. enginetrace->SetupLeafAndEntityListBox( moveMins, moveMaxs, m_pTraceListData );
  1108. }
  1109. //-----------------------------------------------------------------------------
  1110. // Purpose:
  1111. // Input : *pMove -
  1112. //-----------------------------------------------------------------------------
  1113. void CGameMovement::ProcessMovement( CBasePlayer *pPlayer, CMoveData *pMove )
  1114. {
  1115. m_nTraceCount = 0;
  1116. Assert( pMove && pPlayer );
  1117. float flStoreFrametime = gpGlobals->frametime;
  1118. //!!HACK HACK: Adrian - slow down all player movement by this factor.
  1119. //!!Blame Yahn for this one.
  1120. gpGlobals->frametime *= pPlayer->GetLaggedMovementValue();
  1121. ResetGetWaterContentsForPointCache();
  1122. // Cropping movement speed scales mv->m_fForwardSpeed etc. globally
  1123. // Once we crop, we don't want to recursively crop again, so we set the crop
  1124. // flag globally here once per usercmd cycle.
  1125. m_iSpeedCropped = SPEED_CROPPED_RESET;
  1126. // StartTrackPredictionErrors should have set this
  1127. Assert( pPlayer->IsBot() || player == pPlayer );
  1128. player = pPlayer;
  1129. mv = pMove;
  1130. mv->m_flMaxSpeed = pPlayer->GetPlayerMaxSpeed();
  1131. m_bProcessingMovement = true;
  1132. m_bInStuckTest = false;
  1133. // CheckV( player->CurrentCommandNumber(), "StartPos", mv->GetAbsOrigin() );
  1134. DiffPrint( "start %f %f %f", mv->GetAbsOrigin().x, mv->GetAbsOrigin().y, mv->GetAbsOrigin().z );
  1135. // Run the command.
  1136. PlayerMove();
  1137. FinishMove();
  1138. DiffPrint( "end %f %f %f", mv->GetAbsOrigin().x, mv->GetAbsOrigin().y, mv->GetAbsOrigin().z );
  1139. // CheckV( player->CurrentCommandNumber(), "EndPos", mv->GetAbsOrigin() );
  1140. //This is probably not needed, but just in case.
  1141. gpGlobals->frametime = flStoreFrametime;
  1142. m_bProcessingMovement = false;
  1143. #if !defined( CLIENT_DLL )
  1144. if ( !player->IsBot() )
  1145. {
  1146. VPROF_INCREMENT_COUNTER( "PlayerMovementTraces", m_nTraceCount );
  1147. }
  1148. #endif
  1149. }
  1150. void CGameMovement::Reset( void )
  1151. {
  1152. player = NULL;
  1153. }
  1154. void CGameMovement::StartTrackPredictionErrors( CBasePlayer *pPlayer )
  1155. {
  1156. if ( pPlayer->IsBot() )
  1157. return;
  1158. player = pPlayer;
  1159. #if PREDICTION_ERROR_CHECK_LEVEL > 0
  1160. StartCommand( CBaseEntity::IsServer(), player->CurrentCommandNumber() );
  1161. #endif
  1162. }
  1163. void CGameMovement::FinishTrackPredictionErrors( CBasePlayer *pPlayer )
  1164. {
  1165. if ( pPlayer->IsBot() )
  1166. return;
  1167. Assert( player == pPlayer );
  1168. #if PREDICTION_ERROR_CHECK_LEVEL > 0
  1169. // DiffPrint( "end %f", player->m_Local.m_vecPunchAngleVel.m_Value.x );
  1170. // Call validate at end of checking
  1171. Validate( CBaseEntity::IsServer(), player->CurrentCommandNumber() );
  1172. #endif
  1173. }
  1174. //-----------------------------------------------------------------------------
  1175. // Purpose: Sets ground entity
  1176. //-----------------------------------------------------------------------------
  1177. void CGameMovement::FinishMove( void )
  1178. {
  1179. mv->m_nOldButtons = mv->m_nButtons;
  1180. }
  1181. #define PUNCH_DAMPING 9.0f // bigger number makes the response more damped, smaller is less damped
  1182. // currently the system will overshoot, with larger damping values it won't
  1183. #define PUNCH_SPRING_CONSTANT 65.0f // bigger number increases the speed at which the view corrects
  1184. void CGameMovement::DecayAngles( QAngle& v, float fExp, float fLin, float dT )
  1185. {
  1186. fExp *= dT;
  1187. fLin *= dT;
  1188. v *= expf(-fExp);
  1189. float fMag = v.Length();
  1190. if ( fMag > fLin )
  1191. {
  1192. v *= (1.0f - fLin / fMag);
  1193. }
  1194. else
  1195. {
  1196. v.Init(0.0f, 0.0f, 0.0f);
  1197. }
  1198. }
  1199. extern ConVar view_punch_decay;
  1200. //-----------------------------------------------------------------------------
  1201. // Purpose: Decays the punchangle toward 0,0,0.
  1202. // Modelled as a damped spring
  1203. //-----------------------------------------------------------------------------
  1204. void CGameMovement::DecayViewPunchAngle( void )
  1205. {
  1206. QAngle punchAngle = player->m_Local.m_viewPunchAngle;
  1207. DecayAngles(punchAngle, view_punch_decay.GetFloat(), 0.0f, TICK_INTERVAL);
  1208. player->m_Local.m_viewPunchAngle = punchAngle;
  1209. /*
  1210. if ( player->m_Local.m_viewPunchAngle->LengthSqr() > 0.001 || player->m_Local.m_vecPunchAngleVel->LengthSqr() > 0.001 )
  1211. {
  1212. player->m_Local.m_viewPunchAngle += player->m_Local.m_viewPunchAngleVel * gpGlobals->frametime;
  1213. float damping = 1 - (PUNCH_DAMPING * gpGlobals->frametime);
  1214. if ( damping < 0 )
  1215. {
  1216. damping = 0;
  1217. }
  1218. player->m_Local.m_viewPunchAngleVel *= damping;
  1219. // torsional spring
  1220. // UNDONE: Per-axis spring constant?
  1221. float springForceMagnitude = PUNCH_SPRING_CONSTANT * gpGlobals->frametime;
  1222. springForceMagnitude = clamp(springForceMagnitude, 0, 2 );
  1223. player->m_Local.m_viewPunchAngleVel -= player->m_Local.m_vecPunchAngle * springForceMagnitude;
  1224. // don't wrap around
  1225. player->m_Local.m_vecPunchAngle.Init(
  1226. clamp(player->m_Local.m_viewPunchAngle->x, -89, 89 ),
  1227. clamp(player->m_Local.m_viewPunchAngle->y, -179, 179 ),
  1228. clamp(player->m_Local.m_viewPunchAngle->z, -89, 89 ) );
  1229. }
  1230. else
  1231. {
  1232. player->m_Local.m_viewPunchAngle.Init( 0, 0, 0 );
  1233. player->m_Local.m_viewPunchAngleVel.Init( 0, 0, 0 );
  1234. }
  1235. */
  1236. }
  1237. //-----------------------------------------------------------------------------
  1238. // Purpose:
  1239. //-----------------------------------------------------------------------------
  1240. void CGameMovement::StartGravity( void )
  1241. {
  1242. float ent_gravity;
  1243. if (player->GetGravity())
  1244. ent_gravity = player->GetGravity();
  1245. else
  1246. ent_gravity = 1.0;
  1247. // Add gravity so they'll be in the correct position during movement
  1248. // yes, this 0.5 looks wrong, but it's not.
  1249. mv->m_vecVelocity[2] -= (ent_gravity * sv_gravity.GetFloat() * 0.5 * gpGlobals->frametime );
  1250. mv->m_vecVelocity[2] += player->GetBaseVelocity()[2] * gpGlobals->frametime;
  1251. Vector temp = player->GetBaseVelocity();
  1252. temp[ 2 ] = 0;
  1253. player->SetBaseVelocity( temp );
  1254. CheckVelocity();
  1255. }
  1256. //-----------------------------------------------------------------------------
  1257. // Purpose:
  1258. //-----------------------------------------------------------------------------
  1259. void CGameMovement::CheckWaterJump( void )
  1260. {
  1261. Vector flatforward;
  1262. Vector flatvelocity;
  1263. float curspeed;
  1264. Vector forward = m_vecForward;
  1265. // Already water jumping.
  1266. if (player->m_flWaterJumpTime)
  1267. return;
  1268. // Don't hop out if we just jumped in
  1269. if (mv->m_vecVelocity[2] < -180)
  1270. return; // only hop out if we are moving up
  1271. // See if we are backing up
  1272. flatvelocity[0] = mv->m_vecVelocity[0];
  1273. flatvelocity[1] = mv->m_vecVelocity[1];
  1274. flatvelocity[2] = 0;
  1275. // Must be moving
  1276. curspeed = VectorNormalize( flatvelocity );
  1277. // see if near an edge
  1278. flatforward[0] = forward[0];
  1279. flatforward[1] = forward[1];
  1280. flatforward[2] = 0;
  1281. VectorNormalize (flatforward);
  1282. // Are we backing into water from steps or something? If so, don't pop forward
  1283. if ( curspeed != 0.0 && ( DotProduct( flatvelocity, flatforward ) < 0.0 ) )
  1284. return;
  1285. Vector vecStart;
  1286. // Start line trace at waist height (using the center of the player for this here)
  1287. vecStart= mv->GetAbsOrigin() + (GetPlayerMins() + GetPlayerMaxs() ) * 0.5;
  1288. Vector vecEnd;
  1289. VectorMA( vecStart, 24.0f, flatforward, vecEnd );
  1290. trace_t tr;
  1291. TracePlayerBBox( vecStart, vecEnd, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, tr );
  1292. if ( tr.fraction < 1.0 ) // solid at waist
  1293. {
  1294. IPhysicsObject *pPhysObj = tr.m_pEnt->VPhysicsGetObject();
  1295. if ( pPhysObj )
  1296. {
  1297. if ( pPhysObj->GetGameFlags() & FVPHYSICS_PLAYER_HELD )
  1298. return;
  1299. }
  1300. vecStart.z = mv->GetAbsOrigin().z + player->GetViewOffset().z + WATERJUMP_HEIGHT;
  1301. VectorMA( vecStart, 24.0f, flatforward, vecEnd );
  1302. VectorMA( vec3_origin, -50.0f, tr.plane.normal, player->m_vecWaterJumpVel );
  1303. TracePlayerBBox( vecStart, vecEnd, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, tr );
  1304. if ( tr.fraction == 1.0 ) // open at eye level
  1305. {
  1306. // Now trace down to see if we would actually land on a standable surface.
  1307. VectorCopy( vecEnd, vecStart );
  1308. vecEnd.z -= 1024.0f;
  1309. TracePlayerBBox( vecStart, vecEnd, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, tr );
  1310. if ( ( tr.fraction < 1.0f ) && ( tr.plane.normal.z >= 0.7 ) )
  1311. {
  1312. mv->m_vecVelocity[2] = 256.0f; // Push up
  1313. mv->m_nOldButtons |= IN_JUMP; // Don't jump again until released
  1314. player->AddFlag( FL_WATERJUMP );
  1315. player->m_flWaterJumpTime = 2000.0f; // Do this for 2 seconds
  1316. }
  1317. }
  1318. }
  1319. }
  1320. //-----------------------------------------------------------------------------
  1321. // Purpose:
  1322. //-----------------------------------------------------------------------------
  1323. void CGameMovement::WaterJump( void )
  1324. {
  1325. if (player->m_flWaterJumpTime > 10000)
  1326. player->m_flWaterJumpTime = 10000;
  1327. if (!player->m_flWaterJumpTime)
  1328. return;
  1329. player->m_flWaterJumpTime -= 1000.0f * gpGlobals->frametime;
  1330. if (player->m_flWaterJumpTime <= 0 || !player->GetWaterLevel())
  1331. {
  1332. player->m_flWaterJumpTime = 0;
  1333. player->RemoveFlag( FL_WATERJUMP );
  1334. }
  1335. mv->m_vecVelocity[0] = player->m_vecWaterJumpVel[0];
  1336. mv->m_vecVelocity[1] = player->m_vecWaterJumpVel[1];
  1337. }
  1338. //-----------------------------------------------------------------------------
  1339. // Purpose:
  1340. //-----------------------------------------------------------------------------
  1341. void CGameMovement::WaterMove( void )
  1342. {
  1343. int i;
  1344. Vector wishvel;
  1345. float wishspeed;
  1346. Vector wishdir;
  1347. Vector start, dest;
  1348. Vector temp;
  1349. trace_t pm;
  1350. float speed, newspeed, addspeed, accelspeed;
  1351. Vector forward, right, up;
  1352. AngleVectors (mv->m_vecViewAngles, &forward, &right, &up); // Determine movement angles
  1353. //
  1354. // user intentions
  1355. //
  1356. for (i=0 ; i<3 ; i++)
  1357. {
  1358. wishvel[i] = forward[i]*mv->m_flForwardMove + right[i]*mv->m_flSideMove;
  1359. }
  1360. // if we have the jump key down, move us up as well
  1361. if (mv->m_nButtons & IN_JUMP)
  1362. {
  1363. wishvel[2] += mv->m_flClientMaxSpeed;
  1364. }
  1365. // Sinking after no other movement occurs
  1366. else if (!mv->m_flForwardMove && !mv->m_flSideMove && !mv->m_flUpMove)
  1367. {
  1368. wishvel[2] -= 60; // drift towards bottom
  1369. }
  1370. else // Go straight up by upmove amount.
  1371. {
  1372. // exaggerate upward movement along forward as well
  1373. float upwardMovememnt = mv->m_flForwardMove * forward.z * 2;
  1374. upwardMovememnt = clamp( upwardMovememnt, 0, mv->m_flClientMaxSpeed );
  1375. wishvel[2] += mv->m_flUpMove + upwardMovememnt;
  1376. }
  1377. // Copy it over and determine speed
  1378. VectorCopy (wishvel, wishdir);
  1379. wishspeed = VectorNormalize(wishdir);
  1380. // Cap speed.
  1381. if (wishspeed > mv->m_flMaxSpeed)
  1382. {
  1383. VectorScale (wishvel, mv->m_flMaxSpeed/wishspeed, wishvel);
  1384. wishspeed = mv->m_flMaxSpeed;
  1385. }
  1386. // Slow us down a bit.
  1387. wishspeed *= 0.8;
  1388. // Water friction
  1389. VectorCopy(mv->m_vecVelocity, temp);
  1390. speed = VectorNormalize(temp);
  1391. if (speed)
  1392. {
  1393. newspeed = speed - gpGlobals->frametime * speed * sv_friction.GetFloat() * player->m_surfaceFriction;
  1394. if (newspeed < 0.1f)
  1395. {
  1396. newspeed = 0;
  1397. }
  1398. VectorScale (mv->m_vecVelocity, newspeed/speed, mv->m_vecVelocity);
  1399. }
  1400. else
  1401. {
  1402. newspeed = 0;
  1403. }
  1404. // water acceleration
  1405. if (wishspeed >= 0.1f) // old !
  1406. {
  1407. addspeed = wishspeed - newspeed;
  1408. if (addspeed > 0)
  1409. {
  1410. VectorNormalize(wishvel);
  1411. accelspeed = sv_accelerate.GetFloat() * wishspeed * gpGlobals->frametime * player->m_surfaceFriction;
  1412. if (accelspeed > addspeed)
  1413. {
  1414. accelspeed = addspeed;
  1415. }
  1416. for (i = 0; i < 3; i++)
  1417. {
  1418. float deltaSpeed = accelspeed * wishvel[i];
  1419. mv->m_vecVelocity[i] += deltaSpeed;
  1420. mv->m_outWishVel[i] += deltaSpeed;
  1421. }
  1422. }
  1423. }
  1424. VectorAdd (mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity);
  1425. // Now move
  1426. // assume it is a stair or a slope, so press down from stepheight above
  1427. VectorMA (mv->GetAbsOrigin(), gpGlobals->frametime, mv->m_vecVelocity, dest);
  1428. TracePlayerBBox( mv->GetAbsOrigin(), dest, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, pm );
  1429. if ( pm.fraction == 1.0f )
  1430. {
  1431. VectorCopy( dest, start );
  1432. if ( player->m_Local.m_bAllowAutoMovement )
  1433. {
  1434. start[2] += player->m_Local.m_flStepSize + 1;
  1435. }
  1436. TracePlayerBBox( start, dest, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, pm );
  1437. if (!pm.startsolid && !pm.allsolid)
  1438. {
  1439. float stepDist = pm.endpos.z - mv->GetAbsOrigin().z;
  1440. mv->m_outStepHeight += stepDist;
  1441. // walked up the step, so just keep result and exit
  1442. mv->SetAbsOrigin( pm.endpos );
  1443. VectorSubtract( mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity );
  1444. return;
  1445. }
  1446. // Try moving straight along out normal path.
  1447. TryPlayerMove();
  1448. }
  1449. else
  1450. {
  1451. if ( !player->GetGroundEntity() )
  1452. {
  1453. TryPlayerMove();
  1454. VectorSubtract( mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity );
  1455. return;
  1456. }
  1457. StepMove( dest, pm );
  1458. }
  1459. VectorSubtract( mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity );
  1460. }
  1461. //-----------------------------------------------------------------------------
  1462. // Purpose: Does the basic move attempting to climb up step heights. It uses
  1463. // the mv->GetAbsOrigin() and mv->m_vecVelocity. It returns a new
  1464. // new mv->GetAbsOrigin(), mv->m_vecVelocity, and mv->m_outStepHeight.
  1465. //-----------------------------------------------------------------------------
  1466. void CGameMovement::StepMove( Vector &vecDestination, trace_t &trace )
  1467. {
  1468. //
  1469. // Save the move position and velocity in case we need to put it back later.
  1470. //
  1471. Vector vecPos, vecVel;
  1472. VectorCopy( mv->GetAbsOrigin(), vecPos );
  1473. VectorCopy( mv->m_vecVelocity, vecVel );
  1474. //
  1475. // First try walking straight to where they want to go.
  1476. //
  1477. Vector vecEndPos;
  1478. VectorCopy( vecDestination, vecEndPos );
  1479. TryPlayerMove( &vecEndPos, &trace );
  1480. //
  1481. // mv now contains where they ended up if they tried to walk straight there.
  1482. // Save those results for use later.
  1483. //
  1484. Vector vecDownPos, vecDownVel;
  1485. VectorCopy( mv->GetAbsOrigin(), vecDownPos );
  1486. VectorCopy( mv->m_vecVelocity, vecDownVel );
  1487. //
  1488. // Reset original values to try some other things.
  1489. //
  1490. mv->SetAbsOrigin( vecPos );
  1491. VectorCopy( vecVel, mv->m_vecVelocity );
  1492. //
  1493. // Move up a stair height.
  1494. // Slide forward at the same velocity but from the higher position.
  1495. //
  1496. VectorCopy( mv->GetAbsOrigin(), vecEndPos );
  1497. if ( player->m_Local.m_bAllowAutoMovement )
  1498. {
  1499. vecEndPos.z += player->m_Local.m_flStepSize + DIST_EPSILON;
  1500. }
  1501. // Only step up as high as we have headroom to do so.
  1502. TracePlayerBBox( mv->GetAbsOrigin(), vecEndPos, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, trace );
  1503. if ( !trace.startsolid && !trace.allsolid )
  1504. {
  1505. mv->SetAbsOrigin( trace.endpos );
  1506. }
  1507. TryPlayerMove();
  1508. //
  1509. // Move down a stair (attempt to).
  1510. // Slide forward at the same velocity from the lower position.
  1511. //
  1512. VectorCopy( mv->GetAbsOrigin(), vecEndPos );
  1513. if ( player->m_Local.m_bAllowAutoMovement )
  1514. {
  1515. vecEndPos.z -= player->m_Local.m_flStepSize + DIST_EPSILON;
  1516. }
  1517. TracePlayerBBox( mv->GetAbsOrigin(), vecEndPos, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, trace );
  1518. // If we are not on the ground any more then use the original movement attempt.
  1519. if ( trace.plane.normal[2] < 0.7 )
  1520. {
  1521. mv->SetAbsOrigin( vecDownPos );
  1522. VectorCopy( vecDownVel, mv->m_vecVelocity );
  1523. float flStepDist = mv->GetAbsOrigin().z - vecPos.z;
  1524. if ( flStepDist > 0.0f )
  1525. {
  1526. mv->m_outStepHeight += flStepDist;
  1527. }
  1528. return;
  1529. }
  1530. // If the trace ended up in empty space, copy the end over to the origin.
  1531. if ( !trace.startsolid && !trace.allsolid )
  1532. {
  1533. mv->SetAbsOrigin( trace.endpos );
  1534. }
  1535. // Copy this origin to up.
  1536. Vector vecUpPos;
  1537. VectorCopy( mv->GetAbsOrigin(), vecUpPos );
  1538. // decide which one went farther
  1539. float flDownDist = ( vecDownPos.x - vecPos.x ) * ( vecDownPos.x - vecPos.x ) + ( vecDownPos.y - vecPos.y ) * ( vecDownPos.y - vecPos.y );
  1540. float flUpDist = ( vecUpPos.x - vecPos.x ) * ( vecUpPos.x - vecPos.x ) + ( vecUpPos.y - vecPos.y ) * ( vecUpPos.y - vecPos.y );
  1541. if ( flDownDist > flUpDist )
  1542. {
  1543. mv->SetAbsOrigin( vecDownPos );
  1544. VectorCopy( vecDownVel, mv->m_vecVelocity );
  1545. }
  1546. else
  1547. {
  1548. // copy z value from slide move
  1549. mv->m_vecVelocity.z = vecDownVel.z;
  1550. }
  1551. float flStepDist = mv->GetAbsOrigin().z - vecPos.z;
  1552. if ( flStepDist > 0 )
  1553. {
  1554. mv->m_outStepHeight += flStepDist;
  1555. }
  1556. }
  1557. //-----------------------------------------------------------------------------
  1558. // Purpose:
  1559. //-----------------------------------------------------------------------------
  1560. void CGameMovement::Friction( void )
  1561. {
  1562. float speed, newspeed, control;
  1563. float friction;
  1564. float drop;
  1565. // If we are in water jump cycle, don't apply friction
  1566. if (player->m_flWaterJumpTime)
  1567. return;
  1568. // Calculate speed
  1569. speed = VectorLength( mv->m_vecVelocity );
  1570. // If too slow, return
  1571. if (speed < 0.1f)
  1572. {
  1573. return;
  1574. }
  1575. drop = 0;
  1576. // apply ground friction
  1577. if (player->GetGroundEntity() != NULL) // On an entity that is the ground
  1578. {
  1579. friction = sv_friction.GetFloat() * player->m_surfaceFriction;
  1580. // Bleed off some speed, but if we have less than the bleed
  1581. // threshold, bleed the threshold amount.
  1582. if ( IsGameConsole() )
  1583. {
  1584. if( player->m_Local.m_bDucked )
  1585. {
  1586. control = (speed < sv_stopspeed.GetFloat()) ? sv_stopspeed.GetFloat() : speed;
  1587. }
  1588. else
  1589. {
  1590. #if defined ( TF_DLL ) || defined ( TF_CLIENT_DLL )
  1591. control = (speed < sv_stopspeed.GetFloat()) ? sv_stopspeed.GetFloat() : speed;
  1592. #else
  1593. control = (speed < sv_stopspeed.GetFloat()) ? (sv_stopspeed.GetFloat() * 2.0f) : speed;
  1594. #endif
  1595. }
  1596. }
  1597. else
  1598. {
  1599. control = (speed < sv_stopspeed.GetFloat()) ? sv_stopspeed.GetFloat() : speed;
  1600. }
  1601. // Add the amount to the drop amount.
  1602. drop += control*friction*gpGlobals->frametime;
  1603. }
  1604. // scale the velocity
  1605. newspeed = speed - drop;
  1606. if (newspeed < 0)
  1607. newspeed = 0;
  1608. if ( newspeed != speed )
  1609. {
  1610. // Determine proportion of old speed we are using.
  1611. newspeed /= speed;
  1612. // Adjust velocity according to proportion.
  1613. VectorScale( mv->m_vecVelocity, newspeed, mv->m_vecVelocity );
  1614. }
  1615. mv->m_outWishVel -= (1.f-newspeed) * mv->m_vecVelocity;
  1616. }
  1617. //-----------------------------------------------------------------------------
  1618. // Purpose:
  1619. //-----------------------------------------------------------------------------
  1620. void CGameMovement::FinishGravity( void )
  1621. {
  1622. float ent_gravity;
  1623. if ( player->m_flWaterJumpTime )
  1624. return;
  1625. if ( player->GetGravity() )
  1626. ent_gravity = player->GetGravity();
  1627. else
  1628. ent_gravity = 1.0;
  1629. // Get the correct velocity for the end of the dt
  1630. mv->m_vecVelocity[2] -= (ent_gravity * sv_gravity.GetFloat() * gpGlobals->frametime * 0.5);
  1631. CheckVelocity();
  1632. }
  1633. //-----------------------------------------------------------------------------
  1634. // Purpose:
  1635. // Input : wishdir -
  1636. // accel -
  1637. //-----------------------------------------------------------------------------
  1638. void CGameMovement::AirAccelerate( Vector& wishdir, float wishspeed, float accel )
  1639. {
  1640. int i;
  1641. float addspeed, accelspeed, currentspeed;
  1642. float wishspd;
  1643. wishspd = wishspeed;
  1644. if (player->pl.deadflag)
  1645. return;
  1646. if (player->m_flWaterJumpTime)
  1647. return;
  1648. // Cap speed
  1649. if (wishspd > 30)
  1650. wishspd = 30;
  1651. // Determine veer amount
  1652. currentspeed = mv->m_vecVelocity.Dot(wishdir);
  1653. // See how much to add
  1654. addspeed = wishspd - currentspeed;
  1655. // If not adding any, done.
  1656. if (addspeed <= 0)
  1657. return;
  1658. // Determine acceleration speed after acceleration
  1659. accelspeed = accel * wishspeed * gpGlobals->frametime * player->m_surfaceFriction;
  1660. // Cap it
  1661. if (accelspeed > addspeed)
  1662. accelspeed = addspeed;
  1663. // Adjust pmove vel.
  1664. for (i=0 ; i<3 ; i++)
  1665. {
  1666. mv->m_vecVelocity[i] += accelspeed * wishdir[i];
  1667. mv->m_outWishVel[i] += accelspeed * wishdir[i];
  1668. }
  1669. }
  1670. //-----------------------------------------------------------------------------
  1671. // Purpose:
  1672. //-----------------------------------------------------------------------------
  1673. void CGameMovement::AirMove( void )
  1674. {
  1675. int i;
  1676. Vector wishvel;
  1677. float fmove, smove;
  1678. Vector wishdir;
  1679. float wishspeed;
  1680. Vector forward, right, up;
  1681. AngleVectors (mv->m_vecViewAngles, &forward, &right, &up); // Determine movement angles
  1682. // Copy movement amounts
  1683. fmove = mv->m_flForwardMove;
  1684. smove = mv->m_flSideMove;
  1685. // Zero out z components of movement vectors
  1686. forward[2] = 0;
  1687. right[2] = 0;
  1688. VectorNormalize(forward); // Normalize remainder of vectors
  1689. VectorNormalize(right); //
  1690. for (i=0 ; i<2 ; i++) // Determine x and y parts of velocity
  1691. wishvel[i] = forward[i]*fmove + right[i]*smove;
  1692. wishvel[2] = 0; // Zero out z part of velocity
  1693. VectorCopy (wishvel, wishdir); // Determine maginitude of speed of move
  1694. wishspeed = VectorNormalize(wishdir);
  1695. //
  1696. // clamp to server defined max speed
  1697. //
  1698. if ( wishspeed != 0 && (wishspeed > mv->m_flMaxSpeed))
  1699. {
  1700. VectorScale (wishvel, mv->m_flMaxSpeed/wishspeed, wishvel);
  1701. wishspeed = mv->m_flMaxSpeed;
  1702. }
  1703. AirAccelerate( wishdir, wishspeed, sv_airaccelerate.GetFloat() );
  1704. // Add in any base velocity to the current velocity.
  1705. VectorAdd(mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity );
  1706. TryPlayerMove();
  1707. // 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?)
  1708. VectorSubtract( mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity );
  1709. }
  1710. bool CGameMovement::CanAccelerate()
  1711. {
  1712. // Dead players don't accelerate.
  1713. if (player->pl.deadflag)
  1714. return false;
  1715. // If waterjumping, don't accelerate
  1716. if (player->m_flWaterJumpTime)
  1717. return false;
  1718. return true;
  1719. }
  1720. //-----------------------------------------------------------------------------
  1721. // Purpose:
  1722. // Input : wishdir -
  1723. // wishspeed -
  1724. // accel -
  1725. //-----------------------------------------------------------------------------
  1726. void CGameMovement::Accelerate( Vector& wishdir, float wishspeed, float accel )
  1727. {
  1728. int i;
  1729. float addspeed, accelspeed, currentspeed;
  1730. // This gets overridden because some games (CSPort) want to allow dead (observer) players
  1731. // to be able to move around.
  1732. if ( !CanAccelerate() )
  1733. return;
  1734. // See if we are changing direction a bit
  1735. currentspeed = mv->m_vecVelocity.Dot(wishdir);
  1736. // Reduce wishspeed by the amount of veer.
  1737. addspeed = wishspeed - currentspeed;
  1738. // If not going to add any speed, done.
  1739. if (addspeed <= 0)
  1740. return;
  1741. const float kAccelerationScale = MAX(250.0f, wishspeed);
  1742. // Determine amount of acceleration.
  1743. accelspeed = accel * gpGlobals->frametime * kAccelerationScale * player->m_surfaceFriction;
  1744. // Cap at addspeed
  1745. if (accelspeed > addspeed)
  1746. accelspeed = addspeed;
  1747. // Adjust velocity.
  1748. for (i=0 ; i<3 ; i++)
  1749. {
  1750. mv->m_vecVelocity[i] += accelspeed * wishdir[i];
  1751. }
  1752. }
  1753. //-----------------------------------------------------------------------------
  1754. // Purpose: Try to keep a walking player on the ground when running down slopes etc
  1755. //-----------------------------------------------------------------------------
  1756. void CGameMovement::StayOnGround( void )
  1757. {
  1758. trace_t trace;
  1759. Vector start( mv->GetAbsOrigin() );
  1760. Vector end( mv->GetAbsOrigin() );
  1761. start.z += 2;
  1762. end.z -= player->GetStepSize();
  1763. // See how far up we can go without getting stuck
  1764. TracePlayerBBox( mv->GetAbsOrigin(), start, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, trace );
  1765. start = trace.endpos;
  1766. // using trace.startsolid is unreliable here, it doesn't get set when
  1767. // tracing bounding box vs. terrain
  1768. // Now trace down from a known safe position
  1769. TracePlayerBBox( start, end, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, trace );
  1770. if ( trace.fraction > 0.0f && // must go somewhere
  1771. trace.fraction < 1.0f && // must hit something
  1772. !trace.startsolid && // can't be embedded in a solid
  1773. trace.plane.normal[2] >= 0.7 ) // can't hit a steep slope that we can't stand on anyway
  1774. {
  1775. float flDelta = fabs(mv->GetAbsOrigin().z - trace.endpos.z);
  1776. //This is incredibly hacky. The real problem is that trace returning that strange value we can't network over.
  1777. if ( flDelta > 0.5f * COORD_RESOLUTION)
  1778. {
  1779. mv->SetAbsOrigin( trace.endpos );
  1780. }
  1781. }
  1782. }
  1783. //-----------------------------------------------------------------------------
  1784. // Purpose:
  1785. //-----------------------------------------------------------------------------
  1786. void CGameMovement::WalkMove( void )
  1787. {
  1788. int i;
  1789. Vector wishvel;
  1790. float spd;
  1791. float fmove, smove;
  1792. Vector wishdir;
  1793. float wishspeed;
  1794. Vector dest;
  1795. trace_t pm;
  1796. Vector forward, right, up;
  1797. AngleVectors (mv->m_vecViewAngles, &forward, &right, &up); // Determine movement angles
  1798. CHandle< CBaseEntity > oldground;
  1799. oldground = player->GetGroundEntity();
  1800. // Copy movement amounts
  1801. fmove = mv->m_flForwardMove;
  1802. smove = mv->m_flSideMove;
  1803. // Zero out z components of movement vectors
  1804. if ( g_bMovementOptimizations )
  1805. {
  1806. if ( forward[2] != 0 )
  1807. {
  1808. forward[2] = 0;
  1809. VectorNormalize( forward );
  1810. }
  1811. if ( right[2] != 0 )
  1812. {
  1813. right[2] = 0;
  1814. VectorNormalize( right );
  1815. }
  1816. }
  1817. else
  1818. {
  1819. forward[2] = 0;
  1820. right[2] = 0;
  1821. VectorNormalize (forward); // Normalize remainder of vectors.
  1822. VectorNormalize (right); //
  1823. }
  1824. for (i=0 ; i<2 ; i++) // Determine x and y parts of velocity
  1825. wishvel[i] = forward[i]*fmove + right[i]*smove;
  1826. wishvel[2] = 0; // Zero out z part of velocity
  1827. VectorCopy (wishvel, wishdir); // Determine maginitude of speed of move
  1828. wishspeed = VectorNormalize(wishdir);
  1829. //
  1830. // Clamp to server defined max speed
  1831. //
  1832. if ((wishspeed != 0.0f) && (wishspeed > mv->m_flMaxSpeed))
  1833. {
  1834. VectorScale (wishvel, mv->m_flMaxSpeed/wishspeed, wishvel);
  1835. wishspeed = mv->m_flMaxSpeed;
  1836. }
  1837. // Set pmove velocity
  1838. mv->m_vecVelocity[2] = 0;
  1839. Accelerate( wishdir, wishspeed, sv_accelerate.GetFloat() );
  1840. mv->m_vecVelocity[2] = 0;
  1841. // Additional max speed clamp to keep us from going faster than allowed while turning
  1842. if ( mv->m_vecVelocity.LengthSqr() > mv->m_flMaxSpeed * mv->m_flMaxSpeed )
  1843. {
  1844. float fRatio = mv->m_flMaxSpeed / mv->m_vecVelocity.Length();
  1845. mv->m_vecVelocity *= fRatio;
  1846. }
  1847. // Add in any base velocity to the current velocity.
  1848. VectorAdd (mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity );
  1849. spd = VectorLength( mv->m_vecVelocity );
  1850. if ( spd < 1.0f )
  1851. {
  1852. mv->m_vecVelocity.Init();
  1853. // 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?)
  1854. VectorSubtract( mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity );
  1855. return;
  1856. }
  1857. // first try just moving to the destination
  1858. dest[0] = mv->GetAbsOrigin()[0] + mv->m_vecVelocity[0]*gpGlobals->frametime;
  1859. dest[1] = mv->GetAbsOrigin()[1] + mv->m_vecVelocity[1]*gpGlobals->frametime;
  1860. dest[2] = mv->GetAbsOrigin()[2];
  1861. // first try moving directly to the next spot
  1862. TracePlayerBBox( mv->GetAbsOrigin(), dest, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, pm );
  1863. // If we made it all the way, then copy trace end as new player position.
  1864. mv->m_outWishVel += wishdir * wishspeed;
  1865. if ( pm.fraction == 1 )
  1866. {
  1867. mv->SetAbsOrigin( pm.endpos );
  1868. // 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?)
  1869. VectorSubtract( mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity );
  1870. StayOnGround();
  1871. return;
  1872. }
  1873. // Don't walk up stairs if not on ground.
  1874. if ( oldground == NULL && player->GetWaterLevel() == WL_NotInWater )
  1875. {
  1876. // 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?)
  1877. VectorSubtract( mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity );
  1878. return;
  1879. }
  1880. // If we are jumping out of water, don't do anything more.
  1881. if ( player->m_flWaterJumpTime )
  1882. {
  1883. // 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?)
  1884. VectorSubtract( mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity );
  1885. return;
  1886. }
  1887. StepMove( dest, pm );
  1888. // 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?)
  1889. VectorSubtract( mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity );
  1890. StayOnGround();
  1891. }
  1892. //-----------------------------------------------------------------------------
  1893. // Purpose:
  1894. //-----------------------------------------------------------------------------
  1895. void CGameMovement::FullWalkMove( )
  1896. {
  1897. if ( !CheckWater() )
  1898. {
  1899. StartGravity();
  1900. }
  1901. // If we are leaping out of the water, just update the counters.
  1902. if (player->m_flWaterJumpTime)
  1903. {
  1904. WaterJump();
  1905. TryPlayerMove();
  1906. // See if we are still in water?
  1907. CheckWater();
  1908. return;
  1909. }
  1910. // If we are swimming in the water, see if we are nudging against a place we can jump up out
  1911. // of, and, if so, start out jump. Otherwise, if we are not moving up, then reset jump timer to 0
  1912. if ( player->GetWaterLevel() >= WL_Waist )
  1913. {
  1914. if ( player->GetWaterLevel() == WL_Waist )
  1915. {
  1916. CheckWaterJump();
  1917. }
  1918. // If we are falling again, then we must not trying to jump out of water any more.
  1919. if ( mv->m_vecVelocity[2] < 0 &&
  1920. player->m_flWaterJumpTime )
  1921. {
  1922. player->m_flWaterJumpTime = 0;
  1923. }
  1924. // Was jump button pressed?
  1925. if (mv->m_nButtons & IN_JUMP)
  1926. {
  1927. CheckJumpButton();
  1928. }
  1929. else
  1930. {
  1931. mv->m_nOldButtons &= ~IN_JUMP;
  1932. }
  1933. // Perform regular water movement
  1934. WaterMove();
  1935. // Redetermine position vars
  1936. CategorizePosition();
  1937. // If we are on ground, no downward velocity.
  1938. if ( player->GetGroundEntity() != NULL )
  1939. {
  1940. mv->m_vecVelocity[2] = 0;
  1941. }
  1942. }
  1943. else
  1944. // Not fully underwater
  1945. {
  1946. // Was jump button pressed?
  1947. if (mv->m_nButtons & IN_JUMP)
  1948. {
  1949. CheckJumpButton();
  1950. }
  1951. else
  1952. {
  1953. mv->m_nOldButtons &= ~IN_JUMP;
  1954. }
  1955. // Fricion is handled before we add in any base velocity. That way, if we are on a conveyor,
  1956. // we don't slow when standing still, relative to the conveyor.
  1957. if (player->GetGroundEntity() != NULL)
  1958. {
  1959. mv->m_vecVelocity[2] = 0.0;
  1960. player->m_Local.m_flFallVelocity = 0.0f;
  1961. Friction();
  1962. }
  1963. // Make sure velocity is valid.
  1964. CheckVelocity();
  1965. if (player->GetGroundEntity() != NULL)
  1966. {
  1967. WalkMove();
  1968. // the player is on the ground again
  1969. player->m_bHasWalkMovedSinceLastJump = true;
  1970. }
  1971. else
  1972. {
  1973. AirMove(); // Take into account movement when in air.
  1974. }
  1975. // Set final flags.
  1976. CategorizePosition();
  1977. // Make sure velocity is valid.
  1978. CheckVelocity();
  1979. // Add any remaining gravitational component.
  1980. if ( !CheckWater() )
  1981. {
  1982. FinishGravity();
  1983. }
  1984. // If we are on ground, no downward velocity.
  1985. if ( player->GetGroundEntity() != NULL )
  1986. {
  1987. mv->m_vecVelocity[2] = 0;
  1988. }
  1989. CheckFalling();
  1990. }
  1991. if ( ( m_nOldWaterLevel == WL_NotInWater && player->GetWaterLevel() != WL_NotInWater ) ||
  1992. ( m_nOldWaterLevel != WL_NotInWater && player->GetWaterLevel() == WL_NotInWater ) )
  1993. {
  1994. if ( player->GetAbsVelocity().Length() > 135 )
  1995. {
  1996. PlaySwimSound();
  1997. #if !defined( CLIENT_DLL )
  1998. player->Splash();
  1999. #endif
  2000. }
  2001. }
  2002. }
  2003. //-----------------------------------------------------------------------------
  2004. // Purpose:
  2005. //-----------------------------------------------------------------------------
  2006. void CGameMovement::FullObserverMove( void )
  2007. {
  2008. int mode = player->GetObserverMode();
  2009. if ( mode == OBS_MODE_IN_EYE || mode == OBS_MODE_CHASE )
  2010. {
  2011. CBaseEntity * target = player->GetObserverTarget();
  2012. if ( target != NULL )
  2013. {
  2014. mv->SetAbsOrigin( target->GetAbsOrigin() );
  2015. mv->m_vecViewAngles = target->GetAbsAngles();
  2016. mv->m_vecVelocity = target->GetAbsVelocity();
  2017. }
  2018. return;
  2019. }
  2020. if ( mode != OBS_MODE_ROAMING )
  2021. {
  2022. // don't move in fixed or death cam mode
  2023. return;
  2024. }
  2025. if ( sv_specnoclip.GetBool() )
  2026. {
  2027. // roam in noclip mode
  2028. FullNoClipMove( sv_specspeed.GetFloat(), sv_specaccelerate.GetFloat() );
  2029. return;
  2030. }
  2031. // do a full clipped free roam move:
  2032. Vector wishvel;
  2033. Vector forward, right, up;
  2034. Vector wishdir, wishend;
  2035. float wishspeed;
  2036. AngleVectors (mv->m_vecViewAngles, &forward, &right, &up); // Determine movement angles
  2037. // Copy movement amounts
  2038. float factor = sv_specspeed.GetFloat();
  2039. if ( mv->m_nButtons & IN_SPEED )
  2040. {
  2041. factor /= 2.0f;
  2042. }
  2043. float fmove = mv->m_flForwardMove * factor;
  2044. float smove = mv->m_flSideMove * factor;
  2045. VectorNormalize (forward); // Normalize remainder of vectors
  2046. VectorNormalize (right); //
  2047. for (int i=0 ; i<3 ; i++) // Determine x and y parts of velocity
  2048. wishvel[i] = forward[i]*fmove + right[i]*smove;
  2049. wishvel[2] += mv->m_flUpMove;
  2050. VectorCopy (wishvel, wishdir); // Determine maginitude of speed of move
  2051. wishspeed = VectorNormalize(wishdir);
  2052. //
  2053. // Clamp to server defined max speed
  2054. //
  2055. float maxspeed = sv_maxvelocity.GetFloat();
  2056. if (wishspeed > maxspeed )
  2057. {
  2058. VectorScale (wishvel, mv->m_flMaxSpeed/wishspeed, wishvel);
  2059. wishspeed = maxspeed;
  2060. }
  2061. // Set pmove velocity, give observer 50% acceration bonus
  2062. Accelerate ( wishdir, wishspeed, sv_specaccelerate.GetFloat() );
  2063. float spd = VectorLength( mv->m_vecVelocity );
  2064. if (spd < 1.0f)
  2065. {
  2066. mv->m_vecVelocity.Init();
  2067. return;
  2068. }
  2069. float friction = sv_friction.GetFloat();
  2070. // Add the amount to the drop amount.
  2071. float drop = spd * friction * gpGlobals->frametime;
  2072. // scale the velocity
  2073. float newspeed = spd - drop;
  2074. if (newspeed < 0)
  2075. newspeed = 0;
  2076. // Determine proportion of old speed we are using.
  2077. newspeed /= spd;
  2078. VectorScale( mv->m_vecVelocity, newspeed, mv->m_vecVelocity );
  2079. CheckVelocity();
  2080. TryPlayerMove();
  2081. }
  2082. //-----------------------------------------------------------------------------
  2083. // Purpose:
  2084. //-----------------------------------------------------------------------------
  2085. void CGameMovement::FullNoClipMove( float factor, float maxacceleration )
  2086. {
  2087. Vector wishvel;
  2088. Vector forward, right, up;
  2089. Vector wishdir;
  2090. float wishspeed;
  2091. float maxspeed = sv_maxspeed.GetFloat() * factor;
  2092. #ifdef INFESTED_DLL // ignore roll component for Alien Swarm, this is used for vertical aiming
  2093. QAngle vecViewAngles = mv->m_vecViewAngles;
  2094. vecViewAngles[ROLL] = 0;
  2095. AngleVectors (vecViewAngles, &forward, &right, &up); // Determine movement angles
  2096. #else
  2097. AngleVectors (mv->m_vecViewAngles, &forward, &right, &up); // Determine movement angles
  2098. #endif
  2099. if ( mv->m_nButtons & IN_SPEED )
  2100. {
  2101. factor /= 2.0f;
  2102. }
  2103. // Copy movement amounts
  2104. float fmove = mv->m_flForwardMove * factor;
  2105. float smove = mv->m_flSideMove * factor;
  2106. VectorNormalize (forward); // Normalize remainder of vectors
  2107. VectorNormalize (right); //
  2108. for (int i=0 ; i<3 ; i++) // Determine x and y parts of velocity
  2109. wishvel[i] = forward[i]*fmove + right[i]*smove;
  2110. wishvel[2] += mv->m_flUpMove * factor;
  2111. VectorCopy (wishvel, wishdir); // Determine maginitude of speed of move
  2112. wishspeed = VectorNormalize(wishdir);
  2113. //
  2114. // Clamp to server defined max speed
  2115. //
  2116. if (wishspeed > maxspeed )
  2117. {
  2118. VectorScale (wishvel, maxspeed/wishspeed, wishvel);
  2119. wishspeed = maxspeed;
  2120. }
  2121. if ( maxacceleration > 0.0 )
  2122. {
  2123. // Set pmove velocity
  2124. Accelerate ( wishdir, wishspeed, maxacceleration );
  2125. float spd = VectorLength( mv->m_vecVelocity );
  2126. if (spd < 1.0f)
  2127. {
  2128. mv->m_vecVelocity.Init();
  2129. return;
  2130. }
  2131. // Bleed off some speed, but if we have less than the bleed
  2132. // threshhold, bleed the theshold amount.
  2133. float control = (spd < maxspeed/4.0) ? maxspeed/4.0 : spd;
  2134. float friction = sv_friction.GetFloat() * player->m_surfaceFriction;
  2135. // Add the amount to the drop amount.
  2136. float drop = control * friction * gpGlobals->frametime;
  2137. // scale the velocity
  2138. float newspeed = spd - drop;
  2139. if (newspeed < 0)
  2140. newspeed = 0;
  2141. // Determine proportion of old speed we are using.
  2142. newspeed /= spd;
  2143. VectorScale( mv->m_vecVelocity, newspeed, mv->m_vecVelocity );
  2144. }
  2145. else
  2146. {
  2147. VectorCopy( wishvel, mv->m_vecVelocity );
  2148. }
  2149. // Just move ( don't clip or anything )
  2150. Vector out;
  2151. VectorMA( mv->GetAbsOrigin(), gpGlobals->frametime, mv->m_vecVelocity, out );
  2152. mv->SetAbsOrigin( out );
  2153. // Zero out velocity if in noaccel mode
  2154. if ( maxacceleration < 0.0f )
  2155. {
  2156. mv->m_vecVelocity.Init();
  2157. }
  2158. }
  2159. //-----------------------------------------------------------------------------
  2160. // Checks to see if we should actually jump
  2161. //-----------------------------------------------------------------------------
  2162. void CGameMovement::PlaySwimSound()
  2163. {
  2164. MoveHelper()->StartSound( mv->GetAbsOrigin(), "Player.Swim" );
  2165. }
  2166. //-----------------------------------------------------------------------------
  2167. // Purpose:
  2168. //-----------------------------------------------------------------------------
  2169. bool CGameMovement::CheckJumpButton( void )
  2170. {
  2171. if (player->pl.deadflag)
  2172. {
  2173. mv->m_nOldButtons |= IN_JUMP ; // don't jump again until released
  2174. return false;
  2175. }
  2176. // See if we are waterjumping. If so, decrement count and return.
  2177. if (player->m_flWaterJumpTime)
  2178. {
  2179. player->m_flWaterJumpTime -= gpGlobals->frametime;
  2180. if (player->m_flWaterJumpTime < 0)
  2181. player->m_flWaterJumpTime = 0;
  2182. return false;
  2183. }
  2184. // If we are in the water most of the way...
  2185. if ( player->GetWaterLevel() >= WL_Waist )
  2186. {
  2187. // swimming, not jumping
  2188. SetGroundEntity( NULL );
  2189. if(player->GetWaterType() == CONTENTS_WATER) // We move up a certain amount
  2190. mv->m_vecVelocity[2] = 100;
  2191. else if (player->GetWaterType() == CONTENTS_SLIME)
  2192. mv->m_vecVelocity[2] = 80;
  2193. // play swiming sound
  2194. if ( player->m_flSwimSoundTime <= 0 )
  2195. {
  2196. // Don't play sound again for 1 second
  2197. player->m_flSwimSoundTime = 1000;
  2198. PlaySwimSound();
  2199. }
  2200. return false;
  2201. }
  2202. // the player jumped so this bool will remain false until the player next walks
  2203. player->m_bHasWalkMovedSinceLastJump = false;
  2204. // No more effect
  2205. if (player->GetGroundEntity() == NULL)
  2206. {
  2207. mv->m_nOldButtons |= IN_JUMP;
  2208. return false; // in air, so no effect
  2209. }
  2210. // Don't allow jumping when the player is in a stasis field.
  2211. #ifndef HL2_EPISODIC
  2212. if ( player->m_Local.m_bSlowMovement )
  2213. return false;
  2214. #endif
  2215. if ( mv->m_nOldButtons & IN_JUMP )
  2216. return false; // don't pogo stick
  2217. // Cannot jump will in the unduck transition.
  2218. if ( player->m_Local.m_bDucking && ( player->GetFlags() & FL_DUCKING ) )
  2219. return false;
  2220. // Still updating the eye position.
  2221. if ( player->m_Local.m_nDuckJumpTimeMsecs > 0 )
  2222. return false;
  2223. // In the air now.
  2224. SetGroundEntity( NULL );
  2225. player->PlayStepSound( (Vector &)mv->GetAbsOrigin(), player->m_pSurfaceData, 1.0, true );
  2226. MoveHelper()->PlayerSetAnimation( PLAYER_JUMP );
  2227. float flGroundFactor = 1.0f;
  2228. if (player->m_pSurfaceData)
  2229. {
  2230. flGroundFactor = player->m_pSurfaceData->game.jumpFactor;
  2231. }
  2232. float flMul;
  2233. if ( g_bMovementOptimizations )
  2234. {
  2235. #if defined(HL2_DLL) || defined(HL2_CLIENT_DLL)
  2236. Assert( sv_gravity.GetFloat() == 600.0f );
  2237. flMul = 160.0f; // approx. 21 units.
  2238. #else
  2239. Assert( sv_gravity.GetFloat() == 800.0f );
  2240. flMul = 268.3281572999747f;
  2241. #endif
  2242. }
  2243. else
  2244. {
  2245. flMul = sqrt(2 * sv_gravity.GetFloat() * GAMEMOVEMENT_JUMP_HEIGHT);
  2246. }
  2247. // Acclerate upward
  2248. // If we are ducking...
  2249. float startz = mv->m_vecVelocity[2];
  2250. if ( ( player->m_Local.m_bDucking ) || ( player->GetFlags() & FL_DUCKING ) )
  2251. {
  2252. // d = 0.5 * g * t^2 - distance traveled with linear accel
  2253. // t = sqrt(2.0 * 45 / g) - how long to fall 45 units
  2254. // v = g * t - velocity at the end (just invert it to jump up that high)
  2255. // v = g * sqrt(2.0 * 45 / g )
  2256. // v^2 = g * g * 2.0 * 45 / g
  2257. // v = sqrt( g * 2.0 * 45 )
  2258. mv->m_vecVelocity[2] = flGroundFactor * flMul; // 2 * gravity * height
  2259. }
  2260. else
  2261. {
  2262. mv->m_vecVelocity[2] += flGroundFactor * flMul; // 2 * gravity * height
  2263. }
  2264. // Add a little forward velocity based on your current forward velocity - if you are not sprinting.
  2265. #if defined( HL2_DLL ) || defined( HL2_CLIENT_DLL )
  2266. #ifdef PORTAL
  2267. const bool bAllowBunnyHopperSpeedBoost = true;
  2268. #else
  2269. bool bAllowBunnyHopperSpeedBoost = ( gpGlobals->maxClients == 1 );
  2270. #endif
  2271. if ( bAllowBunnyHopperSpeedBoost )
  2272. {
  2273. CHLMoveData *pMoveData = ( CHLMoveData* )mv;
  2274. Vector vecForward;
  2275. AngleVectors( mv->m_vecViewAngles, &vecForward );
  2276. vecForward.z = 0;
  2277. VectorNormalize( vecForward );
  2278. // We give a certain percentage of the current forward movement as a bonus to the jump speed. That bonus is clipped
  2279. // to not accumulate over time.
  2280. float flSpeedBoostPerc = ( !pMoveData->m_bIsSprinting && !player->m_Local.m_bDucked ) ? 0.5f : 0.1f;
  2281. float flSpeedAddition = fabs( mv->m_flForwardMove * flSpeedBoostPerc );
  2282. float flMaxSpeed = mv->m_flMaxSpeed + ( mv->m_flMaxSpeed * flSpeedBoostPerc );
  2283. float flNewSpeed = ( flSpeedAddition + mv->m_vecVelocity.Length2D() );
  2284. // If we're over the maximum, we want to only boost as much as will get us to the goal speed
  2285. if ( flNewSpeed > flMaxSpeed )
  2286. {
  2287. flSpeedAddition -= flNewSpeed - flMaxSpeed;
  2288. }
  2289. if ( mv->m_flForwardMove < 0.0f )
  2290. flSpeedAddition *= -1.0f;
  2291. // Add it on
  2292. VectorAdd( (vecForward*flSpeedAddition), mv->m_vecVelocity, mv->m_vecVelocity );
  2293. }
  2294. #endif
  2295. FinishGravity();
  2296. CheckV( player->CurrentCommandNumber(), "CheckJump", mv->m_vecVelocity );
  2297. mv->m_outJumpVel.z += mv->m_vecVelocity[2] - startz;
  2298. mv->m_outStepHeight += 0.15f;
  2299. OnJump(mv->m_outJumpVel.z);
  2300. #if !defined( PORTAL2 )
  2301. bool bSetDuckJump = (gpGlobals->maxClients == 1); //most games we only set duck jump if the game is single player
  2302. #else
  2303. const bool bSetDuckJump = true; //in portal 2, do it for both single and multiplayer
  2304. #endif
  2305. // Set jump time.
  2306. if ( bSetDuckJump )
  2307. {
  2308. player->m_Local.m_nJumpTimeMsecs = GAMEMOVEMENT_JUMP_TIME;
  2309. player->m_Local.m_bInDuckJump = true;
  2310. }
  2311. #if defined( HL2_DLL )
  2312. if ( xc_uncrouch_on_jump.GetBool() )
  2313. {
  2314. // Uncrouch when jumping
  2315. if ( player->GetToggledDuckState() )
  2316. {
  2317. player->ToggleDuck();
  2318. }
  2319. }
  2320. #endif
  2321. // Flag that we jumped.
  2322. mv->m_nOldButtons |= IN_JUMP; // don't jump again until released
  2323. return true;
  2324. }
  2325. //-----------------------------------------------------------------------------
  2326. // Purpose:
  2327. //-----------------------------------------------------------------------------
  2328. void CGameMovement::FullLadderMove()
  2329. {
  2330. CheckWater();
  2331. // Was jump button pressed? If so, set velocity to 270 away from ladder.
  2332. if ( mv->m_nButtons & IN_JUMP )
  2333. {
  2334. CheckJumpButton();
  2335. }
  2336. else
  2337. {
  2338. mv->m_nOldButtons &= ~IN_JUMP;
  2339. }
  2340. // Perform the move accounting for any base velocity.
  2341. VectorAdd (mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity);
  2342. TryPlayerMove();
  2343. VectorSubtract (mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity);
  2344. }
  2345. //-----------------------------------------------------------------------------
  2346. // Purpose:
  2347. // Output : int
  2348. //-----------------------------------------------------------------------------
  2349. int CGameMovement::TryPlayerMove( Vector *pFirstDest, trace_t *pFirstTrace )
  2350. {
  2351. int bumpcount, numbumps;
  2352. Vector dir;
  2353. float d;
  2354. int numplanes;
  2355. Vector planes[MAX_CLIP_PLANES];
  2356. Vector primal_velocity, original_velocity;
  2357. Vector new_velocity;
  2358. int i, j;
  2359. trace_t pm;
  2360. Vector end;
  2361. float time_left, allFraction;
  2362. int blocked;
  2363. numbumps = 4; // Bump up to four times
  2364. blocked = 0; // Assume not blocked
  2365. numplanes = 0; // and not sliding along any planes
  2366. VectorCopy (mv->m_vecVelocity, original_velocity); // Store original velocity
  2367. VectorCopy (mv->m_vecVelocity, primal_velocity);
  2368. allFraction = 0;
  2369. time_left = gpGlobals->frametime; // Total time for this movement operation.
  2370. new_velocity.Init();
  2371. for (bumpcount=0 ; bumpcount < numbumps; bumpcount++)
  2372. {
  2373. if ( mv->m_vecVelocity.Length() == 0.0 )
  2374. break;
  2375. // Assume we can move all the way from the current origin to the
  2376. // end point.
  2377. VectorMA( mv->GetAbsOrigin(), time_left, mv->m_vecVelocity, end );
  2378. // See if we can make it from origin to end point.
  2379. if ( g_bMovementOptimizations )
  2380. {
  2381. // If their velocity Z is 0, then we can avoid an extra trace here during WalkMove.
  2382. if ( pFirstDest && ( end == *pFirstDest ) )
  2383. {
  2384. pm = *pFirstTrace;
  2385. }
  2386. else
  2387. {
  2388. #if defined( PLAYER_GETTING_STUCK_TESTING )
  2389. trace_t foo;
  2390. TracePlayerBBox( mv->GetAbsOrigin(), mv->GetAbsOrigin(), PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, foo );
  2391. if ( foo.startsolid || foo.fraction != 1.0f )
  2392. {
  2393. Msg( "bah\n" );
  2394. }
  2395. #endif
  2396. TracePlayerBBox( mv->GetAbsOrigin(), end, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, pm );
  2397. }
  2398. }
  2399. else
  2400. {
  2401. TracePlayerBBox( mv->GetAbsOrigin(), end, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, pm );
  2402. }
  2403. if ( pm.fraction > 0 && pm.fraction < MINIMUM_MOVE_FRACTION )
  2404. {
  2405. //HACK: extremely tiny move fractions cause problems in later computations that determine values using portions of distance moved.
  2406. pm.fraction = 0;
  2407. }
  2408. allFraction += pm.fraction;
  2409. // If we started in a solid object, or we were in solid space
  2410. // the whole way, zero out our velocity and return that we
  2411. // are blocked by floor and wall.
  2412. if (pm.allsolid)
  2413. {
  2414. // entity is trapped in another solid
  2415. VectorCopy (vec3_origin, mv->m_vecVelocity);
  2416. return 4;
  2417. }
  2418. // If we moved some portion of the total distance, then
  2419. // copy the end position into the pmove.origin and
  2420. // zero the plane counter.
  2421. if( pm.fraction > 0 )
  2422. {
  2423. if ( numbumps > 0 && pm.fraction == 1 )
  2424. {
  2425. // There's a precision issue with terrain tracing that can cause a swept box to successfully trace
  2426. // when the end position is stuck in the triangle. Re-run the test with an uswept box to catch that
  2427. // case until the bug is fixed.
  2428. // If we detect getting stuck, don't allow the movement
  2429. trace_t stuck;
  2430. TracePlayerBBox( pm.endpos, pm.endpos, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, stuck );
  2431. if ( stuck.startsolid || stuck.fraction != 1.0f )
  2432. {
  2433. //Msg( "Player will become stuck!!!\n" );
  2434. VectorCopy (vec3_origin, mv->m_vecVelocity);
  2435. break;
  2436. }
  2437. }
  2438. #if defined( PLAYER_GETTING_STUCK_TESTING )
  2439. trace_t foo;
  2440. TracePlayerBBox( pm.endpos, pm.endpos, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, foo );
  2441. if ( foo.startsolid || foo.fraction != 1.0f )
  2442. {
  2443. Msg( "Player will become stuck!!!\n" );
  2444. }
  2445. #endif
  2446. // actually covered some distance
  2447. mv->SetAbsOrigin( pm.endpos);
  2448. VectorCopy (mv->m_vecVelocity, original_velocity);
  2449. numplanes = 0;
  2450. }
  2451. // If we covered the entire distance, we are done
  2452. // and can return.
  2453. if (pm.fraction == 1)
  2454. {
  2455. break; // moved the entire distance
  2456. }
  2457. // Save entity that blocked us (since fraction was < 1.0)
  2458. // for contact
  2459. // Add it if it's not already in the list!!!
  2460. MoveHelper( )->AddToTouched( pm, mv->m_vecVelocity );
  2461. // If the plane we hit has a high z component in the normal, then
  2462. // it's probably a floor
  2463. if (pm.plane.normal[2] > 0.7)
  2464. {
  2465. blocked |= 1; // floor
  2466. }
  2467. // If the plane has a zero z component in the normal, then it's a
  2468. // step or wall
  2469. if ( abs(pm.plane.normal[2]) < EFFECTIVELY_HORIZONTAL_NORMAL_Z )
  2470. {
  2471. pm.plane.normal[2] = 0;
  2472. blocked |= 2; // step / wall
  2473. }
  2474. // Reduce amount of m_flFrameTime left by total time left * fraction
  2475. // that we covered.
  2476. time_left -= time_left * pm.fraction;
  2477. // Did we run out of planes to clip against?
  2478. if (numplanes >= MAX_CLIP_PLANES)
  2479. {
  2480. // this shouldn't really happen
  2481. // Stop our movement if so.
  2482. VectorCopy (vec3_origin, mv->m_vecVelocity);
  2483. //Con_DPrintf("Too many planes 4\n");
  2484. break;
  2485. }
  2486. // Set up next clipping plane
  2487. VectorCopy (pm.plane.normal, planes[numplanes]);
  2488. numplanes++;
  2489. // modify original_velocity so it parallels all of the clip planes
  2490. //
  2491. // reflect player velocity
  2492. // Only give this a try for first impact plane because you can get yourself stuck in an acute corner by jumping in place
  2493. // and pressing forward and nobody was really using this bounce/reflection feature anyway...
  2494. if ( numplanes == 1 &&
  2495. player->GetMoveType() == MOVETYPE_WALK &&
  2496. player->GetGroundEntity() == NULL )
  2497. {
  2498. for ( i = 0; i < numplanes; i++ )
  2499. {
  2500. if ( planes[i][2] > 0.7 )
  2501. {
  2502. // floor or slope
  2503. ClipVelocity( original_velocity, planes[i], new_velocity, 1 );
  2504. VectorCopy( new_velocity, original_velocity );
  2505. }
  2506. else
  2507. {
  2508. ClipVelocity( original_velocity, planes[i], new_velocity, 1.0 + sv_bounce.GetFloat() * (1 - player->m_surfaceFriction) );
  2509. }
  2510. }
  2511. VectorCopy( new_velocity, mv->m_vecVelocity );
  2512. VectorCopy( new_velocity, original_velocity );
  2513. }
  2514. else
  2515. {
  2516. for (i=0 ; i < numplanes ; i++)
  2517. {
  2518. ClipVelocity( original_velocity, planes[i], mv->m_vecVelocity, 1 );
  2519. for (j=0 ; j<numplanes ; j++)
  2520. {
  2521. if (j != i)
  2522. {
  2523. // Are we now moving against this plane?
  2524. if (mv->m_vecVelocity.Dot(planes[j]) < 0)
  2525. break; // not ok
  2526. }
  2527. }
  2528. if (j == numplanes) // Didn't have to clip, so we're ok
  2529. break;
  2530. }
  2531. // Did we go all the way through plane set
  2532. if (i != numplanes)
  2533. { // go along this plane
  2534. // pmove.velocity is set in clipping call, no need to set again.
  2535. ;
  2536. }
  2537. else
  2538. { // go along the crease
  2539. if (numplanes != 2)
  2540. {
  2541. VectorCopy (vec3_origin, mv->m_vecVelocity);
  2542. break;
  2543. }
  2544. CrossProduct (planes[0], planes[1], dir);
  2545. dir.NormalizeInPlace();
  2546. d = dir.Dot(mv->m_vecVelocity);
  2547. VectorScale (dir, d, mv->m_vecVelocity );
  2548. }
  2549. //
  2550. // if original velocity is against the original velocity, stop dead
  2551. // to avoid tiny occilations in sloping corners
  2552. //
  2553. d = mv->m_vecVelocity.Dot(primal_velocity);
  2554. if (d <= 0)
  2555. {
  2556. //Con_DPrintf("Back\n");
  2557. VectorCopy (vec3_origin, mv->m_vecVelocity);
  2558. break;
  2559. }
  2560. }
  2561. }
  2562. if ( allFraction == 0 )
  2563. {
  2564. VectorCopy (vec3_origin, mv->m_vecVelocity);
  2565. }
  2566. // Check if they slammed into a wall
  2567. float fSlamVol = 0.0f;
  2568. float fLateralStoppingAmount = primal_velocity.Length2D() - mv->m_vecVelocity.Length2D();
  2569. if ( fLateralStoppingAmount > PLAYER_MAX_SAFE_FALL_SPEED * 2.0f )
  2570. {
  2571. fSlamVol = 1.0f;
  2572. }
  2573. else if ( fLateralStoppingAmount > PLAYER_MAX_SAFE_FALL_SPEED )
  2574. {
  2575. fSlamVol = 0.85f;
  2576. }
  2577. if ( fSlamVol > 0.0f )
  2578. {
  2579. PlayerRoughLandingEffects( fSlamVol );
  2580. }
  2581. return blocked;
  2582. }
  2583. //-----------------------------------------------------------------------------
  2584. // Purpose: Determine whether or not the player is on a ladder (physprop or world).
  2585. //-----------------------------------------------------------------------------
  2586. inline bool CGameMovement::OnLadder( trace_t &trace )
  2587. {
  2588. if ( trace.contents & CONTENTS_LADDER )
  2589. return true;
  2590. IPhysicsSurfaceProps *pPhysProps = MoveHelper( )->GetSurfaceProps();
  2591. if ( pPhysProps )
  2592. {
  2593. const surfacedata_t *pSurfaceData = pPhysProps->GetSurfaceData( trace.surface.surfaceProps );
  2594. if ( pSurfaceData )
  2595. {
  2596. if ( pSurfaceData->game.climbable != 0 )
  2597. return true;
  2598. }
  2599. }
  2600. return false;
  2601. }
  2602. // [sbodenbender] make ladders easier to climb in cstrike
  2603. #if defined (CSTRIKE_DLL)
  2604. 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 );
  2605. 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 );
  2606. ConVar sv_ladder_scale_speed( "sv_ladder_scale_speed", "0.78", FCVAR_REPLICATED | FCVAR_RELEASE, "Scale top speed on ladders", true, 0.0f, true, 1.0f );
  2607. #endif
  2608. // note, this lets us do some specific code when we start using the ladder Movetype
  2609. #define IGNORE_JUMP_TIME 0.2f
  2610. void CGameMovement::OnStartMoveTypeLadder()
  2611. {
  2612. player->m_ignoreLadderJumpTime = gpGlobals->curtime + IGNORE_JUMP_TIME; // ignore jumpping for IGNORE_JUMP_TIME seconds to give us a chance to catch the ladder without bouncing off
  2613. }
  2614. //-----------------------------------------------------------------------------
  2615. // Purpose:
  2616. //-----------------------------------------------------------------------------
  2617. bool CGameMovement::LadderMove( void )
  2618. {
  2619. trace_t pm;
  2620. bool onFloor;
  2621. Vector floor;
  2622. Vector wishdir;
  2623. Vector end;
  2624. if ( player->GetMoveType() == MOVETYPE_NOCLIP )
  2625. return false;
  2626. if ( !GameHasLadders() )
  2627. return false;
  2628. // If I'm already moving on a ladder, use the previous ladder direction
  2629. if ( player->GetMoveType() == MOVETYPE_LADDER )
  2630. {
  2631. wishdir = -player->m_vecLadderNormal.Get();
  2632. }
  2633. else
  2634. {
  2635. // otherwise, use the direction player is attempting to move
  2636. if ( mv->m_flForwardMove || mv->m_flSideMove )
  2637. {
  2638. for (int i=0 ; i<3 ; i++) // Determine x and y parts of velocity
  2639. wishdir[i] = m_vecForward[i]*mv->m_flForwardMove + m_vecRight[i]*mv->m_flSideMove;
  2640. VectorNormalize(wishdir);
  2641. }
  2642. else
  2643. {
  2644. // Player is not attempting to move, no ladder behavior
  2645. return false;
  2646. }
  2647. }
  2648. // wishdir points toward the ladder if any exists
  2649. VectorMA( mv->GetAbsOrigin(), LadderDistance(), wishdir, end );
  2650. TracePlayerBBox( mv->GetAbsOrigin(), end, LadderMask(), COLLISION_GROUP_PLAYER_MOVEMENT, pm );
  2651. // no ladder in that direction, return
  2652. if ( pm.fraction == 1.0f || !OnLadder( pm ) )
  2653. {
  2654. // we may have walked off the edge of a cliff, check backward to see if we can grab a ladder once over the edge
  2655. bool bCatchCliffLadder = false;
  2656. if( player->m_bHasWalkMovedSinceLastJump && // the player is not in the air due to an intentional jump
  2657. player->GetMoveType() != MOVETYPE_LADDER && // not already laddering
  2658. player->GetGroundEntity() == NULL && // in the air
  2659. mv->m_vecVelocity.z <= 0 && // not flying upward
  2660. mv->m_vecVelocity.z > -50.0f && // not falling too fast
  2661. ( abs(mv->m_vecVelocity.x) > 0 && abs(mv->m_vecVelocity.y) > 0 ) // lateral velocity present
  2662. )
  2663. {
  2664. #define LADDER_EDGE_GRAB_DISTACE 24.0f
  2665. #define LADDER_EDGE_GRAB_HEIGHT_DELTA 6.0f
  2666. Vector vecTraceFrom = mv->GetAbsOrigin(); vecTraceFrom.z -= LADDER_EDGE_GRAB_HEIGHT_DELTA;
  2667. Vector vecTraceTo = mv->GetAbsOrigin() - mv->m_vecVelocity.Normalized() * LADDER_EDGE_GRAB_DISTACE;
  2668. TracePlayerBBox( vecTraceFrom, vecTraceTo, (PlayerSolidMask() & (~CONTENTS_PLAYERCLIP)), COLLISION_GROUP_PLAYER_MOVEMENT, pm );
  2669. //debugoverlay->AddLineOverlay( vecTraceFrom, vecTraceTo, 100,0,0,20,1, 1 );
  2670. if ( pm.fraction != 1.0f && OnLadder( pm ) && pm.plane.normal.z != 1.0f )
  2671. {
  2672. //debugoverlay->AddLineOverlay( vecTraceFrom, vecTraceTo, 255,0,0,20,1, 5 );
  2673. player->SetMoveType( MOVETYPE_LADDER );
  2674. player->SetMoveCollide( MOVECOLLIDE_DEFAULT );
  2675. player->SetLadderNormal( pm.plane.normal );
  2676. mv->m_vecVelocity.Init();
  2677. // The ladder check ignored playerclips, to fix a bug exposed by de_train, where a clipbrush is
  2678. // flush with a ladder. This causes the above tracehull to fail unless we ignore playerclips.
  2679. // However, we have to check for playerclips before we snap to that pos, so we don't warp a
  2680. // player into a clipbrush.
  2681. TracePlayerBBox( vecTraceFrom, vecTraceTo, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, pm );
  2682. mv->SetAbsOrigin( pm.endpos );
  2683. bCatchCliffLadder = true;
  2684. }
  2685. }
  2686. // we didn't catch a ladder, so bail on any further laddering
  2687. if ( !bCatchCliffLadder )
  2688. return false;
  2689. }
  2690. if( player->GetMoveType() != MOVETYPE_LADDER )
  2691. {
  2692. OnStartMoveTypeLadder();
  2693. }
  2694. player->SetMoveType( MOVETYPE_LADDER );
  2695. player->SetMoveCollide( MOVECOLLIDE_DEFAULT );
  2696. player->m_vecLadderNormal = pm.plane.normal;
  2697. // On ladder, convert movement to be relative to the ladder
  2698. VectorCopy( mv->GetAbsOrigin(), floor );
  2699. floor[2] += GetPlayerMins()[2] - 1;
  2700. if( enginetrace->GetPointContents( floor ) == CONTENTS_SOLID || player->GetGroundEntity() != NULL )
  2701. {
  2702. onFloor = true;
  2703. }
  2704. else
  2705. {
  2706. onFloor = false;
  2707. }
  2708. player->SetGravity( 0 );
  2709. float climbSpeed = ClimbSpeed();
  2710. float forwardSpeed = 0, rightSpeed = 0;
  2711. if ( mv->m_nButtons & IN_BACK )
  2712. forwardSpeed -= climbSpeed;
  2713. if ( mv->m_nButtons & IN_FORWARD )
  2714. forwardSpeed += climbSpeed;
  2715. if ( mv->m_nButtons & IN_MOVELEFT )
  2716. rightSpeed -= climbSpeed;
  2717. if ( mv->m_nButtons & IN_MOVERIGHT )
  2718. rightSpeed += climbSpeed;
  2719. if ( mv->m_nButtons & IN_JUMP )
  2720. {
  2721. if( player->m_ignoreLadderJumpTime <= gpGlobals->curtime )
  2722. {
  2723. player->SetMoveType( MOVETYPE_WALK );
  2724. player->SetMoveCollide( MOVECOLLIDE_DEFAULT );
  2725. VectorScale( pm.plane.normal, 270, mv->m_vecVelocity );
  2726. }
  2727. }
  2728. else
  2729. {
  2730. if ( forwardSpeed != 0 || rightSpeed != 0 )
  2731. {
  2732. Vector velocity, perp, cross, lateral, tmp;
  2733. //ALERT(at_console, "pev %.2f %.2f %.2f - ",
  2734. // pev->velocity.x, pev->velocity.y, pev->velocity.z);
  2735. // Calculate player's intended velocity
  2736. //Vector velocity = (forward * gpGlobals->v_forward) + (right * gpGlobals->v_right);
  2737. VectorScale( m_vecForward, forwardSpeed, velocity );
  2738. VectorMA( velocity, rightSpeed, m_vecRight, velocity );
  2739. // Perpendicular in the ladder plane
  2740. VectorCopy( vec3_origin, tmp );
  2741. tmp[2] = 1;
  2742. CrossProduct( tmp, pm.plane.normal, perp );
  2743. VectorNormalize( perp );
  2744. // decompose velocity into ladder plane
  2745. float normal = DotProduct( velocity, pm.plane.normal );
  2746. // This is the velocity into the face of the ladder
  2747. VectorScale( pm.plane.normal, normal, cross );
  2748. // This is the player's additional velocity
  2749. VectorSubtract( velocity, cross, lateral );
  2750. // This turns the velocity into the face of the ladder into velocity that
  2751. // is roughly vertically perpendicular to the face of the ladder.
  2752. // NOTE: It IS possible to face up and move down or face down and move up
  2753. // because the velocity is a sum of the directional velocity and the converted
  2754. // velocity through the face of the ladder -- by design.
  2755. CrossProduct( pm.plane.normal, perp, tmp );
  2756. // [sbodenbender] make ladders easier to climb in cstrike
  2757. #if defined (CSTRIKE_DLL)
  2758. // break lateral into direction along tmp (up the ladder) and direction along perp (perpendicular to ladder)
  2759. float tmpDist = DotProduct( tmp, lateral );
  2760. float perpDist = DotProduct( perp, lateral );
  2761. Vector angleVec = perp * perpDist;
  2762. angleVec += cross;
  2763. // angleVec is our desired movement in the ladder normal/perpendicular plane
  2764. VectorNormalize( angleVec );
  2765. float angleDot = DotProduct( angleVec, pm.plane.normal );
  2766. // angleDot is our angle of incidence to the laddernormal in the ladder normal/perpendicular plane
  2767. if ( angleDot < sv_ladder_angle.GetFloat() )
  2768. lateral = ( tmp * tmpDist ) + ( perp * sv_ladder_dampen.GetFloat() * perpDist );
  2769. #endif // CSTRIKE_DLL
  2770. VectorMA( lateral, -normal, tmp, mv->m_vecVelocity );
  2771. #if defined (CSTRIKE_DLL)
  2772. if ( sv_ladder_scale_speed.GetFloat() > 0 )
  2773. {
  2774. // scale max climb speed
  2775. VectorScale( mv->m_vecVelocity, sv_ladder_scale_speed.GetFloat(), mv->m_vecVelocity );
  2776. }
  2777. #endif // CSTRIKE_DLL
  2778. if ( onFloor && normal > 0 ) // On ground moving away from the ladder
  2779. {
  2780. VectorMA( mv->m_vecVelocity, MAX_CLIMB_SPEED, pm.plane.normal, mv->m_vecVelocity );
  2781. }
  2782. //pev->velocity = lateral - (CrossProduct( trace.vecPlaneNormal, perp ) * normal);
  2783. }
  2784. else
  2785. {
  2786. mv->m_vecVelocity.Init();
  2787. }
  2788. }
  2789. return true;
  2790. }
  2791. //-----------------------------------------------------------------------------
  2792. // Purpose:
  2793. // Input : axis -
  2794. // Output : const char
  2795. //-----------------------------------------------------------------------------
  2796. #if !defined(_STATIC_LINKED) || defined(CLIENT_DLL)
  2797. const char *DescribeAxis( int axis )
  2798. {
  2799. static char sz[ 32 ];
  2800. switch ( axis )
  2801. {
  2802. case 0:
  2803. Q_strncpy( sz, "X", sizeof( sz ) );
  2804. break;
  2805. case 1:
  2806. Q_strncpy( sz, "Y", sizeof( sz ) );
  2807. break;
  2808. case 2:
  2809. default:
  2810. Q_strncpy( sz, "Z", sizeof( sz ) );
  2811. break;
  2812. }
  2813. return sz;
  2814. }
  2815. #else
  2816. const char *DescribeAxis( int axis );
  2817. #endif
  2818. //-----------------------------------------------------------------------------
  2819. // Purpose:
  2820. //-----------------------------------------------------------------------------
  2821. void CGameMovement::CheckVelocity( void )
  2822. {
  2823. int i;
  2824. //
  2825. // bound velocity
  2826. //
  2827. Vector org = mv->GetAbsOrigin();
  2828. for (i=0; i < 3; i++)
  2829. {
  2830. // See if it's bogus.
  2831. if (IS_NAN(mv->m_vecVelocity[i]))
  2832. {
  2833. DevMsg( 1, "PM Got a NaN velocity %s\n", DescribeAxis( i ) );
  2834. mv->m_vecVelocity[i] = 0;
  2835. }
  2836. if (IS_NAN(org[i]))
  2837. {
  2838. DevMsg( 1, "PM Got a NaN origin on %s\n", DescribeAxis( i ) );
  2839. org[ i ] = 0;
  2840. mv->SetAbsOrigin( org );
  2841. }
  2842. // Bound it.
  2843. if (mv->m_vecVelocity[i] > sv_maxvelocity.GetFloat())
  2844. {
  2845. DevMsg( 1, "PM Got a velocity too high on %s\n", DescribeAxis( i ) );
  2846. mv->m_vecVelocity[i] = sv_maxvelocity.GetFloat();
  2847. }
  2848. else if (mv->m_vecVelocity[i] < -sv_maxvelocity.GetFloat())
  2849. {
  2850. DevMsg( 1, "PM Got a velocity too low on %s\n", DescribeAxis( i ) );
  2851. mv->m_vecVelocity[i] = -sv_maxvelocity.GetFloat();
  2852. }
  2853. }
  2854. }
  2855. //-----------------------------------------------------------------------------
  2856. // Purpose:
  2857. //-----------------------------------------------------------------------------
  2858. void CGameMovement::AddGravity( void )
  2859. {
  2860. float ent_gravity;
  2861. if ( player->m_flWaterJumpTime )
  2862. return;
  2863. if (player->GetGravity())
  2864. ent_gravity = player->GetGravity();
  2865. else
  2866. ent_gravity = 1.0;
  2867. // Add gravity incorrectly
  2868. mv->m_vecVelocity[2] -= (ent_gravity * sv_gravity.GetFloat() * gpGlobals->frametime);
  2869. mv->m_vecVelocity[2] += player->GetBaseVelocity()[2] * gpGlobals->frametime;
  2870. Vector temp = player->GetBaseVelocity();
  2871. temp[2] = 0;
  2872. player->SetBaseVelocity( temp );
  2873. CheckVelocity();
  2874. }
  2875. //-----------------------------------------------------------------------------
  2876. // Purpose:
  2877. // Input : push -
  2878. // Output : trace_t
  2879. //-----------------------------------------------------------------------------
  2880. void CGameMovement::PushEntity( Vector& push, trace_t *pTrace )
  2881. {
  2882. Vector end;
  2883. VectorAdd (mv->GetAbsOrigin(), push, end);
  2884. TracePlayerBBox( mv->GetAbsOrigin(), end, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, *pTrace );
  2885. mv->SetAbsOrigin( pTrace->endpos );
  2886. // So we can run impact function afterwards.
  2887. // If
  2888. if ( pTrace->fraction < 1.0 && !pTrace->allsolid )
  2889. {
  2890. MoveHelper( )->AddToTouched( *pTrace, mv->m_vecVelocity );
  2891. }
  2892. }
  2893. //-----------------------------------------------------------------------------
  2894. // Purpose:
  2895. // Input : in -
  2896. // normal -
  2897. // out -
  2898. // overbounce -
  2899. // Output : int
  2900. //-----------------------------------------------------------------------------
  2901. int CGameMovement::ClipVelocity( Vector& in, Vector& normal, Vector& out, float overbounce )
  2902. {
  2903. float backoff;
  2904. float change;
  2905. float angle;
  2906. int i, blocked;
  2907. angle = normal[ 2 ];
  2908. blocked = 0x00; // Assume unblocked.
  2909. if (angle > 0) // If the plane that is blocking us has a positive z component, then assume it's a floor.
  2910. blocked |= 0x01; //
  2911. if (!angle) // If the plane has no Z, it is vertical (wall/step)
  2912. blocked |= 0x02; //
  2913. // Determine how far along plane to slide based on incoming direction.
  2914. backoff = DotProduct (in, normal) * overbounce;
  2915. for (i=0 ; i<3 ; i++)
  2916. {
  2917. change = normal[i]*backoff;
  2918. out[i] = in[i] - change;
  2919. }
  2920. // iterate once to make sure we aren't still moving through the plane
  2921. float adjust = DotProduct( out, normal );
  2922. if( adjust < 0.0f )
  2923. {
  2924. // min this against a small number (but no further from zero than -DIST_EPSILON) to account for crossing a plane with a near-parallel normal
  2925. adjust = MIN( adjust, -DIST_EPSILON );
  2926. out -= ( normal * adjust );
  2927. // Msg( "Adjustment = %lf\n", adjust );
  2928. }
  2929. // Return blocking flags.
  2930. return blocked;
  2931. }
  2932. //-----------------------------------------------------------------------------
  2933. // Purpose: Computes roll angle for a certain movement direction and velocity
  2934. // Input : angles -
  2935. // velocity -
  2936. // rollangle -
  2937. // rollspeed -
  2938. // Output : float
  2939. //-----------------------------------------------------------------------------
  2940. float CGameMovement::CalcRoll ( const QAngle &angles, const Vector &velocity, float rollangle, float rollspeed )
  2941. {
  2942. float sign;
  2943. float side;
  2944. float value;
  2945. Vector forward, right, up;
  2946. AngleVectors (angles, &forward, &right, &up);
  2947. side = DotProduct (velocity, right);
  2948. sign = side < 0 ? -1 : 1;
  2949. side = fabs(side);
  2950. value = rollangle;
  2951. if (side < rollspeed)
  2952. {
  2953. side = side * value / rollspeed;
  2954. }
  2955. else
  2956. {
  2957. side = value;
  2958. }
  2959. return side*sign;
  2960. }
  2961. #define CHECKSTUCK_MINTIME 0.05 // Don't check again too quickly.
  2962. #if !defined(_STATIC_LINKED) || defined(CLIENT_DLL)
  2963. Vector rgv3tStuckTable[54];
  2964. #else
  2965. extern Vector rgv3tStuckTable[54];
  2966. #endif
  2967. #if !defined(_STATIC_LINKED) || defined(CLIENT_DLL)
  2968. //-----------------------------------------------------------------------------
  2969. // Purpose:
  2970. //-----------------------------------------------------------------------------
  2971. void CreateStuckTable( void )
  2972. {
  2973. float x, y, z;
  2974. int idx;
  2975. int i;
  2976. float zi[3];
  2977. static int firsttime = 1;
  2978. if ( !firsttime )
  2979. return;
  2980. firsttime = 0;
  2981. memset(rgv3tStuckTable, 0, sizeof(rgv3tStuckTable));
  2982. idx = 0;
  2983. // Little Moves.
  2984. x = y = 0;
  2985. // Z moves
  2986. for (z = -0.125 ; z <= 0.125 ; z += 0.125)
  2987. {
  2988. rgv3tStuckTable[idx][0] = x;
  2989. rgv3tStuckTable[idx][1] = y;
  2990. rgv3tStuckTable[idx][2] = z;
  2991. idx++;
  2992. }
  2993. x = z = 0;
  2994. // Y moves
  2995. for (y = -0.125 ; y <= 0.125 ; y += 0.125)
  2996. {
  2997. rgv3tStuckTable[idx][0] = x;
  2998. rgv3tStuckTable[idx][1] = y;
  2999. rgv3tStuckTable[idx][2] = z;
  3000. idx++;
  3001. }
  3002. y = z = 0;
  3003. // X moves
  3004. for (x = -0.125 ; x <= 0.125 ; x += 0.125)
  3005. {
  3006. rgv3tStuckTable[idx][0] = x;
  3007. rgv3tStuckTable[idx][1] = y;
  3008. rgv3tStuckTable[idx][2] = z;
  3009. idx++;
  3010. }
  3011. // Remaining multi axis nudges.
  3012. for ( x = - 0.125; x <= 0.125; x += 0.250 )
  3013. {
  3014. for ( y = - 0.125; y <= 0.125; y += 0.250 )
  3015. {
  3016. for ( z = - 0.125; z <= 0.125; z += 0.250 )
  3017. {
  3018. rgv3tStuckTable[idx][0] = x;
  3019. rgv3tStuckTable[idx][1] = y;
  3020. rgv3tStuckTable[idx][2] = z;
  3021. idx++;
  3022. }
  3023. }
  3024. }
  3025. // Big Moves.
  3026. x = y = 0;
  3027. zi[0] = 0.0f;
  3028. zi[1] = 1.0f;
  3029. zi[2] = 6.0f;
  3030. for (i = 0; i < 3; i++)
  3031. {
  3032. // Z moves
  3033. z = zi[i];
  3034. rgv3tStuckTable[idx][0] = x;
  3035. rgv3tStuckTable[idx][1] = y;
  3036. rgv3tStuckTable[idx][2] = z;
  3037. idx++;
  3038. }
  3039. x = z = 0;
  3040. // Y moves
  3041. for (y = -2.0f ; y <= 2.0f ; y += 2.0)
  3042. {
  3043. rgv3tStuckTable[idx][0] = x;
  3044. rgv3tStuckTable[idx][1] = y;
  3045. rgv3tStuckTable[idx][2] = z;
  3046. idx++;
  3047. }
  3048. y = z = 0;
  3049. // X moves
  3050. for (x = -2.0f ; x <= 2.0f ; x += 2.0f)
  3051. {
  3052. rgv3tStuckTable[idx][0] = x;
  3053. rgv3tStuckTable[idx][1] = y;
  3054. rgv3tStuckTable[idx][2] = z;
  3055. idx++;
  3056. }
  3057. // Remaining multi axis nudges.
  3058. for (i = 0 ; i < 3; i++)
  3059. {
  3060. z = zi[i];
  3061. for (x = -2.0f ; x <= 2.0f ; x += 2.0f)
  3062. {
  3063. for (y = -2.0f ; y <= 2.0f ; y += 2.0)
  3064. {
  3065. rgv3tStuckTable[idx][0] = x;
  3066. rgv3tStuckTable[idx][1] = y;
  3067. rgv3tStuckTable[idx][2] = z;
  3068. idx++;
  3069. }
  3070. }
  3071. }
  3072. Assert( idx < sizeof(rgv3tStuckTable)/sizeof(rgv3tStuckTable[0]));
  3073. }
  3074. #else
  3075. extern void CreateStuckTable( void );
  3076. #endif
  3077. //-----------------------------------------------------------------------------
  3078. // Purpose:
  3079. // Input : nIndex -
  3080. // server -
  3081. // offset -
  3082. // Output : int
  3083. //-----------------------------------------------------------------------------
  3084. int GetRandomStuckOffsets( CBasePlayer *pPlayer, Vector& offset)
  3085. {
  3086. // Last time we did a full
  3087. int idx;
  3088. idx = pPlayer->m_StuckLast++;
  3089. VectorCopy(rgv3tStuckTable[idx % 54], offset);
  3090. return (idx % 54);
  3091. }
  3092. //-----------------------------------------------------------------------------
  3093. // Purpose:
  3094. // Input : nIndex -
  3095. // server -
  3096. //-----------------------------------------------------------------------------
  3097. void ResetStuckOffsets( CBasePlayer *pPlayer )
  3098. {
  3099. pPlayer->m_StuckLast = 0;
  3100. }
  3101. //-----------------------------------------------------------------------------
  3102. // Purpose:
  3103. //-----------------------------------------------------------------------------
  3104. bool CGameMovement::IsMovingPlayerStuck( void ) const
  3105. {
  3106. return m_bProcessingMovement && !m_bInStuckTest && player && player->m_StuckLast > 0;
  3107. }
  3108. //-----------------------------------------------------------------------------
  3109. // Purpose:
  3110. //-----------------------------------------------------------------------------
  3111. CBasePlayer *CGameMovement::GetMovingPlayer( void ) const
  3112. {
  3113. return m_bProcessingMovement ? player : NULL;
  3114. }
  3115. //--------------------------------------------------------------------------------------------------------
  3116. void CGameMovement::UnblockPusher( CBasePlayer *pPlayer, CBaseEntity *pPusher )
  3117. {
  3118. // TODO
  3119. }
  3120. #if defined( CLIENT_DLL )
  3121. static ConVar cl_pred_checkstuck( "cl_pred_checkstuck", "0", FCVAR_DEVELOPMENTONLY, "Perform the additional 'stuck' traces on the client side during prediction." );
  3122. #endif
  3123. //-----------------------------------------------------------------------------
  3124. // Purpose:
  3125. // Input : &input -
  3126. // Output : int
  3127. //-----------------------------------------------------------------------------
  3128. int CGameMovement::CheckStuck( void )
  3129. {
  3130. #if defined( CLIENT_DLL )
  3131. // Don't bother trying to jitter the player on the client, the server position will fix any true "stuck" issues and
  3132. // we send player origins with full precision anyway so getting stuck due to epsilon issues shouldn't occur...
  3133. // The potential bad effect would be interacting with NPCs with a lot of lag might feel more warpy w/o the unstuck code running
  3134. if ( !cl_pred_checkstuck.GetBool() )
  3135. return 0;
  3136. #endif
  3137. Vector base;
  3138. Vector offset;
  3139. Vector test;
  3140. EntityHandle_t hitent;
  3141. int idx;
  3142. float fTime;
  3143. trace_t traceresult;
  3144. CreateStuckTable();
  3145. // player->SetStuckCharacter( NULL );
  3146. m_bInStuckTest = true;
  3147. hitent = TestPlayerPosition( mv->GetAbsOrigin(), COLLISION_GROUP_PLAYER_MOVEMENT, traceresult );
  3148. m_bInStuckTest = false;
  3149. if ( hitent == INVALID_ENTITY_HANDLE )
  3150. {
  3151. ResetStuckOffsets( player );
  3152. return 0;
  3153. }
  3154. // Deal with stuckness...
  3155. #ifndef DEDICATED
  3156. if ( developer.GetBool() )
  3157. {
  3158. bool isServer = player->IsServer();
  3159. engine->Con_NPrintf( isServer, "%s stuck on object %i/%s",
  3160. isServer ? "server" : "client",
  3161. hitent.GetEntryIndex(), MoveHelper()->GetName(hitent) );
  3162. }
  3163. #endif
  3164. VectorCopy( mv->GetAbsOrigin(), base );
  3165. //
  3166. // Deal with precision error in network.
  3167. //
  3168. // World or BSP model
  3169. if ( !player->IsServer() )
  3170. {
  3171. if ( MoveHelper()->IsWorldEntity( hitent ) )
  3172. {
  3173. int nReps = 0;
  3174. ResetStuckOffsets( player );
  3175. do
  3176. {
  3177. GetRandomStuckOffsets( player, offset );
  3178. VectorAdd( base, offset, test );
  3179. if ( TestPlayerPosition( test, COLLISION_GROUP_PLAYER_MOVEMENT, traceresult ) == INVALID_ENTITY_HANDLE )
  3180. {
  3181. ResetStuckOffsets( player );
  3182. mv->SetAbsOrigin( test );
  3183. return 0;
  3184. }
  3185. nReps++;
  3186. } while (nReps < 54);
  3187. }
  3188. }
  3189. // Only an issue on the client.
  3190. idx = player->IsServer() ? 0 : 1;
  3191. fTime = Plat_FloatTime();
  3192. // Too soon?
  3193. if ( m_flStuckCheckTime[ player->entindex() ][ idx ] >= fTime - CHECKSTUCK_MINTIME )
  3194. {
  3195. return 1;
  3196. }
  3197. m_flStuckCheckTime[ player->entindex() ][ idx ] = fTime;
  3198. MoveHelper( )->AddToTouched( traceresult, mv->m_vecVelocity );
  3199. m_bInStuckTest = true;
  3200. GetRandomStuckOffsets( player, offset );
  3201. VectorAdd( base, offset, test );
  3202. if ( TestPlayerPosition( test, COLLISION_GROUP_PLAYER_MOVEMENT, traceresult ) == INVALID_ENTITY_HANDLE)
  3203. {
  3204. ResetStuckOffsets( player );
  3205. mv->SetAbsOrigin( test );
  3206. return 0;
  3207. }
  3208. m_bInStuckTest = false;
  3209. return 1;
  3210. }
  3211. //-----------------------------------------------------------------------------
  3212. // Purpose:
  3213. // Output : bool
  3214. //-----------------------------------------------------------------------------
  3215. bool CGameMovement::InWater( void )
  3216. {
  3217. return ( player->GetWaterLevel() > WL_Feet );
  3218. }
  3219. void CGameMovement::ResetGetWaterContentsForPointCache()
  3220. {
  3221. for ( int slot = 0; slot < MAX_PC_CACHE_SLOTS; ++slot )
  3222. {
  3223. for ( int i = 0; i < MAX_PLAYERS; ++i )
  3224. {
  3225. m_CachedGetPointContents[ i ][ slot ] = -9999;
  3226. }
  3227. }
  3228. }
  3229. int CGameMovement::GetWaterContentsForPointCached( const Vector &point, int slot )
  3230. {
  3231. if ( g_bMovementOptimizations )
  3232. {
  3233. Assert( player );
  3234. Assert( slot >= 0 && slot < MAX_PC_CACHE_SLOTS );
  3235. int idx = player->entindex() - 1;
  3236. if ( m_CachedGetPointContents[ idx ][ slot ] == -9999 || point.DistToSqr( m_CachedGetPointContentsPoint[ idx ][ slot ] ) > 1 )
  3237. {
  3238. m_CachedGetPointContents[ idx ][ slot ] = enginetrace->GetPointContents ( point, MASK_WATER );
  3239. m_CachedGetPointContentsPoint[ idx ][ slot ] = point;
  3240. }
  3241. return m_CachedGetPointContents[ idx ][ slot ];
  3242. }
  3243. else
  3244. {
  3245. return enginetrace->GetPointContents ( point, MASK_WATER );
  3246. }
  3247. }
  3248. //-----------------------------------------------------------------------------
  3249. // Purpose: returns the height to check for water
  3250. //-----------------------------------------------------------------------------
  3251. void CGameMovement::GetWaterCheckPosition( int waterLevel, Vector *pos )
  3252. {
  3253. (*pos)[0] = mv->GetAbsOrigin()[0] + (GetPlayerMins()[0] + GetPlayerMaxs()[0]) * 0.5;
  3254. (*pos)[1] = mv->GetAbsOrigin()[1] + (GetPlayerMins()[1] + GetPlayerMaxs()[1]) * 0.5;
  3255. switch ( waterLevel )
  3256. {
  3257. case WL_Eyes:
  3258. (*pos)[2] = mv->GetAbsOrigin()[2] + player->GetViewOffset()[2];
  3259. return;
  3260. case WL_Waist:
  3261. (*pos)[2] = mv->GetAbsOrigin()[2] + (GetPlayerMins()[2] + GetPlayerMaxs()[2])*0.5;
  3262. return;
  3263. default:
  3264. (*pos)[2] = mv->GetAbsOrigin()[2] + GetPlayerMins()[2] + 1;
  3265. }
  3266. }
  3267. //-----------------------------------------------------------------------------
  3268. // Purpose:
  3269. // Input : &input -
  3270. // Output : bool
  3271. //-----------------------------------------------------------------------------
  3272. bool CGameMovement::CheckWater( void )
  3273. {
  3274. Vector point;
  3275. int cont;
  3276. // Pick a spot just above the players feet.
  3277. GetWaterCheckPosition( WL_Feet, &point );
  3278. // Assume that we are not in water at all.
  3279. player->SetWaterLevel( WL_NotInWater );
  3280. player->SetWaterType( CONTENTS_EMPTY );
  3281. // Grab point contents.
  3282. cont = GetWaterContentsForPointCached( point, 0 );
  3283. // Are we under water? (not solid and not empty?)
  3284. if ( cont & MASK_WATER )
  3285. {
  3286. // Set water type
  3287. player->SetWaterType( cont );
  3288. // We are at least at level one
  3289. player->SetWaterLevel( WL_Feet );
  3290. // Now check a point that is at the player hull midpoint.
  3291. GetWaterCheckPosition( WL_Waist, &point );
  3292. cont = GetWaterContentsForPointCached( point, 1 );
  3293. // If that point is also under water...
  3294. if ( cont & MASK_WATER )
  3295. {
  3296. // Set a higher water level.
  3297. player->SetWaterLevel( WL_Waist );
  3298. // Now check the eye position. (view_ofs is relative to the origin)
  3299. GetWaterCheckPosition( WL_Eyes, &point );
  3300. cont = GetWaterContentsForPointCached( point, 2 );
  3301. if ( cont & MASK_WATER )
  3302. player->SetWaterLevel( WL_Eyes ); // In over our eyes
  3303. }
  3304. }
  3305. // if we just transitioned from not in water to in water, record the time it happened
  3306. if ( ( WL_NotInWater == m_nOldWaterLevel ) && ( player->GetWaterLevel() > WL_NotInWater ) )
  3307. {
  3308. m_flWaterEntryTime = gpGlobals->curtime;
  3309. }
  3310. return ( player->GetWaterLevel() > WL_Feet );
  3311. }
  3312. void CGameMovement::SetGroundEntity( trace_t *pm )
  3313. {
  3314. CBaseEntity *newGround = pm ? pm->m_pEnt : NULL;
  3315. CBaseEntity *oldGround = player->GetGroundEntity();
  3316. Vector vecBaseVelocity = player->GetBaseVelocity();
  3317. if ( !oldGround && newGround )
  3318. {
  3319. // Subtract ground velocity at instant we hit ground jumping
  3320. vecBaseVelocity -= newGround->GetAbsVelocity();
  3321. vecBaseVelocity.z = newGround->GetAbsVelocity().z;
  3322. }
  3323. else if ( oldGround && !newGround )
  3324. {
  3325. // Add in ground velocity at instant we started jumping
  3326. vecBaseVelocity += oldGround->GetAbsVelocity();
  3327. vecBaseVelocity.z = oldGround->GetAbsVelocity().z;
  3328. }
  3329. player->SetBaseVelocity( vecBaseVelocity );
  3330. player->SetGroundEntity( newGround );
  3331. // If we are on something...
  3332. if ( newGround )
  3333. {
  3334. CategorizeGroundSurface( *pm );
  3335. // Then we are not in water jump sequence
  3336. player->m_flWaterJumpTime = 0;
  3337. // Standing on an entity other than the world, so signal that we are touching something.
  3338. if ( !pm->DidHitWorld() )
  3339. {
  3340. MoveHelper()->AddToTouched( *pm, mv->m_vecVelocity );
  3341. }
  3342. if( player->GetMoveType() != MOVETYPE_NOCLIP )
  3343. mv->m_vecVelocity.z = 0.0f;
  3344. }
  3345. }
  3346. static inline void DoTrace( ITraceListData *pTraceListData, const Ray_t &ray, uint32 fMask, ITraceFilter *filter, trace_t *ptr, int *counter )
  3347. {
  3348. ++*counter;
  3349. if ( pTraceListData && pTraceListData->CanTraceRay(ray) )
  3350. {
  3351. enginetrace->TraceRayAgainstLeafAndEntityList( ray, pTraceListData, fMask, filter, ptr );
  3352. }
  3353. else
  3354. {
  3355. enginetrace->TraceRay( ray, fMask, filter, ptr );
  3356. }
  3357. }
  3358. //-----------------------------------------------------------------------------
  3359. // Traces the player's collision bounds in quadrants, looking for a plane that
  3360. // can be stood upon (normal's z >= flStandableZ). Regardless of success or failure,
  3361. // replace the fraction and endpos with the original ones, so we don't try to
  3362. // move the player down to the new floor and get stuck on a leaning wall that
  3363. // the original trace hit first.
  3364. //-----------------------------------------------------------------------------
  3365. void TracePlayerBBoxForGround( ITraceListData *pTraceListData, const Vector& start, const Vector& end, const Vector& minsSrc,
  3366. const Vector& maxsSrc, unsigned int fMask,
  3367. ITraceFilter *filter, trace_t& pm, float minGroundNormalZ, bool overwriteEndpos, int *pCounter )
  3368. {
  3369. VPROF( "TracePlayerBBoxForGround" );
  3370. Ray_t ray;
  3371. Vector mins, maxs;
  3372. float fraction = pm.fraction;
  3373. Vector endpos = pm.endpos;
  3374. // Check the -x, -y quadrant
  3375. mins = minsSrc;
  3376. maxs.Init( MIN( 0, maxsSrc.x ), MIN( 0, maxsSrc.y ), maxsSrc.z );
  3377. ray.Init( start, end, mins, maxs );
  3378. DoTrace( pTraceListData, ray, fMask, filter, &pm, pCounter );
  3379. if ( pm.m_pEnt && pm.plane.normal[2] >= minGroundNormalZ)
  3380. {
  3381. if ( overwriteEndpos )
  3382. {
  3383. pm.fraction = fraction;
  3384. pm.endpos = endpos;
  3385. }
  3386. return;
  3387. }
  3388. // Check the +x, +y quadrant
  3389. mins.Init( MAX( 0, minsSrc.x ), MAX( 0, minsSrc.y ), minsSrc.z );
  3390. maxs = maxsSrc;
  3391. ray.Init( start, end, mins, maxs );
  3392. DoTrace( pTraceListData, ray, fMask, filter, &pm, pCounter );
  3393. if ( pm.m_pEnt && pm.plane.normal[2] >= minGroundNormalZ)
  3394. {
  3395. if ( overwriteEndpos )
  3396. {
  3397. pm.fraction = fraction;
  3398. pm.endpos = endpos;
  3399. }
  3400. return;
  3401. }
  3402. // Check the -x, +y quadrant
  3403. mins.Init( minsSrc.x, MAX( 0, minsSrc.y ), minsSrc.z );
  3404. maxs.Init( MIN( 0, maxsSrc.x ), maxsSrc.y, maxsSrc.z );
  3405. ray.Init( start, end, mins, maxs );
  3406. DoTrace( pTraceListData, ray, fMask, filter, &pm, pCounter );
  3407. if ( pm.m_pEnt && pm.plane.normal[2] >= 0.7)
  3408. {
  3409. if ( overwriteEndpos )
  3410. {
  3411. pm.fraction = fraction;
  3412. pm.endpos = endpos;
  3413. }
  3414. return;
  3415. }
  3416. // Check the +x, -y quadrant
  3417. mins.Init( MAX( 0, minsSrc.x ), minsSrc.y, minsSrc.z );
  3418. maxs.Init( maxsSrc.x, MIN( 0, maxsSrc.y ), maxsSrc.z );
  3419. ray.Init( start, end, mins, maxs );
  3420. DoTrace( pTraceListData, ray, fMask, filter, &pm, pCounter );
  3421. if ( pm.m_pEnt && pm.plane.normal[2] >= minGroundNormalZ)
  3422. {
  3423. if ( overwriteEndpos )
  3424. {
  3425. pm.fraction = fraction;
  3426. pm.endpos = endpos;
  3427. }
  3428. return;
  3429. }
  3430. if ( overwriteEndpos )
  3431. {
  3432. pm.fraction = fraction;
  3433. pm.endpos = endpos;
  3434. }
  3435. }
  3436. bool CGameMovement::CheckValidStandableGroundCandidate( trace_t &pm, float flStandableZ )
  3437. {
  3438. //if the trace didn't hit or it hit something bogus, it's not standable.
  3439. if ( !pm.DidHit() || !pm.m_pEnt )
  3440. return false;
  3441. //players are a special case. Strictly speaking they ARE standable. Note this is not
  3442. //where we check for and prevent stacks of players, this is just an answer to the question
  3443. //can I stand on this thing, and in the case of players the answer is Yes until
  3444. //we prove otherwise elsewhere in code.
  3445. if ( pm.m_pEnt->IsPlayer() )
  3446. return true;
  3447. //can't stand if the floor is too steep.
  3448. return ( pm.plane.normal[2] >= flStandableZ );
  3449. }
  3450. //-----------------------------------------------------------------------------
  3451. //-----------------------------------------------------------------------------
  3452. void CGameMovement::CategorizePosition( void )
  3453. {
  3454. Vector point;
  3455. trace_t pm;
  3456. // Reset this each time we-recategorize, otherwise we have bogus friction when we jump into water and plunge downward really quickly
  3457. player->m_surfaceFriction = 1.0f;
  3458. // if the player hull point one unit down is solid, the player
  3459. // is on ground
  3460. // see if standing on something solid
  3461. // Doing this before we move may introduce a potential latency in water detection, but
  3462. // doing it after can get us stuck on the bottom in water if the amount we move up
  3463. // is less than the 1 pixel 'threshold' we're about to snap to. Also, we'll call
  3464. // this several times per frame, so we really need to avoid sticking to the bottom of
  3465. // water on each call, and the converse case will correct itself if called twice.
  3466. CheckWater();
  3467. // observers don't have a ground entity
  3468. if ( player->IsObserver() )
  3469. return;
  3470. float flOffset = 2.0f;
  3471. point[0] = mv->GetAbsOrigin()[0];
  3472. point[1] = mv->GetAbsOrigin()[1];
  3473. point[2] = mv->GetAbsOrigin()[2] - flOffset;
  3474. Vector bumpOrigin;
  3475. bumpOrigin = mv->GetAbsOrigin();
  3476. // Shooting up really fast. Definitely not on ground.
  3477. // On ladder moving up, so not on ground either
  3478. // NOTE: 145 is a jump.
  3479. #define NON_JUMP_VELOCITY 140.0f
  3480. float zvel = mv->m_vecVelocity[2];
  3481. bool bMovingUp = zvel > 0.0f;
  3482. bool bMovingUpRapidly = zvel > NON_JUMP_VELOCITY;
  3483. float flGroundEntityVelZ = 0.0f;
  3484. if ( bMovingUpRapidly )
  3485. {
  3486. // Tracker 73219, 75878: ywb 8/2/07
  3487. // After save/restore (and maybe at other times), we can get a case where we were saved on a lift and
  3488. // after restore we'll have a high local velocity due to the lift making our abs velocity appear high.
  3489. // We need to account for standing on a moving ground object in that case in order to determine if we really
  3490. // are moving away from the object we are standing on at too rapid a speed. Note that CheckJump already sets
  3491. // ground entity to NULL, so this wouldn't have any effect unless we are moving up rapidly not from the jump button.
  3492. CBaseEntity *ground = player->GetGroundEntity();
  3493. if ( ground )
  3494. {
  3495. flGroundEntityVelZ = ground->GetAbsVelocity().z;
  3496. bMovingUpRapidly = ( zvel - flGroundEntityVelZ ) > NON_JUMP_VELOCITY;
  3497. }
  3498. }
  3499. // NOTE YWB 7/5/07: Since we're already doing a traceline here, we'll subsume the StayOnGround (stair debouncing) check into the main traceline we do here to see what we're standing on
  3500. bool bUnderwater = ( player->GetWaterLevel() >= WL_Eyes );
  3501. bool bMoveToEndPos = false;
  3502. if ( player->GetMoveType() == MOVETYPE_WALK &&
  3503. player->GetGroundEntity() != NULL && !bUnderwater )
  3504. {
  3505. // if walking and still think we're on ground, we'll extend trace down by stepsize so we don't bounce down slopes
  3506. bMoveToEndPos = true;
  3507. point.z -= player->m_Local.m_flStepSize;
  3508. }
  3509. // Was on ground, but now suddenly am not
  3510. if ( bMovingUpRapidly ||
  3511. ( bMovingUp && player->GetMoveType() == MOVETYPE_LADDER ) )
  3512. {
  3513. SetGroundEntity( NULL );
  3514. bMoveToEndPos = false;
  3515. }
  3516. else
  3517. {
  3518. // Try and move down.
  3519. TracePlayerBBox( bumpOrigin, point, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, pm );
  3520. // Was on ground, but now suddenly am not. If we hit a steep plane, we are not on ground
  3521. float flStandableZ = 0.7;
  3522. #if defined(HL2_EP3) && !defined(CLIENT_DLL)
  3523. if ( Icegun_IsPlayerIceSurfing() )
  3524. {
  3525. flStandableZ = 0;
  3526. }
  3527. #endif
  3528. if ( !CheckValidStandableGroundCandidate( pm, flStandableZ ) )
  3529. {
  3530. // Test four sub-boxes, to see if any of them would have found shallower slope we could actually stand on
  3531. ITraceFilter *pFilter = LockTraceFilter( COLLISION_GROUP_PLAYER_MOVEMENT );
  3532. TracePlayerBBoxForGround( m_pTraceListData, bumpOrigin, point, GetPlayerMins(),
  3533. GetPlayerMaxs(), PlayerSolidMask(), pFilter, pm, flStandableZ, true, &m_nTraceCount );
  3534. UnlockTraceFilter( pFilter );
  3535. if ( !CheckValidStandableGroundCandidate( pm, flStandableZ ) )
  3536. {
  3537. SetGroundEntity( NULL );
  3538. // probably want to add a check for a +z velocity too!
  3539. if ( ( mv->m_vecVelocity.z > 0.0f ) &&
  3540. ( player->GetMoveType() != MOVETYPE_NOCLIP ) )
  3541. {
  3542. player->m_surfaceFriction = 0.25f;
  3543. }
  3544. bMoveToEndPos = false;
  3545. }
  3546. else
  3547. {
  3548. #ifndef CLIENT_DLL
  3549. CBaseCSGrenadeProjectile* pGrenadeProjectile = dynamic_cast<CBaseCSGrenadeProjectile*>( pm.m_pEnt );
  3550. if ( pGrenadeProjectile )
  3551. {
  3552. pm.m_pEnt->SetCollisionGroup( COLLISION_GROUP_DEBRIS );
  3553. }
  3554. else
  3555. #endif
  3556. {
  3557. SetGroundEntity( &pm );
  3558. }
  3559. }
  3560. }
  3561. else
  3562. {
  3563. #ifndef CLIENT_DLL
  3564. CBaseCSGrenadeProjectile* pGrenadeProjectile = dynamic_cast<CBaseCSGrenadeProjectile*>( pm.m_pEnt );
  3565. if ( pGrenadeProjectile )
  3566. {
  3567. pm.m_pEnt->SetCollisionGroup( COLLISION_GROUP_DEBRIS );
  3568. }
  3569. else
  3570. #endif
  3571. {
  3572. SetGroundEntity( &pm ); // Otherwise, point to index of ent under us.
  3573. }
  3574. }
  3575. #ifndef CLIENT_DLL
  3576. //Adrian: vehicle code handles for us.
  3577. if ( player->IsInAVehicle() == false )
  3578. {
  3579. // If our gamematerial has changed, tell any player surface triggers that are watching
  3580. IPhysicsSurfaceProps *physprops = MoveHelper()->GetSurfaceProps();
  3581. surfacedata_t *pSurfaceProp = physprops->GetSurfaceData( pm.surface.surfaceProps );
  3582. char cCurrGameMaterial = pSurfaceProp->game.material;
  3583. if ( !player->GetGroundEntity() )
  3584. {
  3585. cCurrGameMaterial = 0;
  3586. }
  3587. // Changed?
  3588. if ( player->m_chPreviousTextureType != cCurrGameMaterial )
  3589. {
  3590. CEnvPlayerSurfaceTrigger::SetPlayerSurface( player, cCurrGameMaterial );
  3591. }
  3592. player->m_chPreviousTextureType = cCurrGameMaterial;
  3593. }
  3594. #endif
  3595. }
  3596. // YWB: This logic block essentially lifted from StayOnGround implementation
  3597. if ( bMoveToEndPos &&
  3598. !pm.startsolid && // not sure we need this check as fraction would == 0.0f?
  3599. pm.fraction > 0.0f && // must go somewhere
  3600. pm.fraction < 1.0f ) // must hit something
  3601. {
  3602. #ifndef CLIENT_DLL
  3603. CBaseCSGrenadeProjectile* pGrenadeProjectile = dynamic_cast<CBaseCSGrenadeProjectile*>( pm.m_pEnt );
  3604. if ( pGrenadeProjectile )
  3605. {
  3606. pm.m_pEnt->SetCollisionGroup( COLLISION_GROUP_DEBRIS );
  3607. }
  3608. else
  3609. #endif
  3610. {
  3611. mv->SetAbsOrigin( pm.endpos );
  3612. }
  3613. }
  3614. }
  3615. //-----------------------------------------------------------------------------
  3616. // Purpose: Determine if the player has hit the ground while falling, apply
  3617. // damage, and play the appropriate impact sound.
  3618. //-----------------------------------------------------------------------------
  3619. void CGameMovement::CheckFalling( void )
  3620. {
  3621. // this function really deals with landing, not falling, so ignore everything else
  3622. if ( player->GetGroundEntity() == NULL || player->m_Local.m_flFallVelocity <= 0 )
  3623. return;
  3624. if ( !IsDead() && player->m_Local.m_flFallVelocity >= PLAYER_FALL_PUNCH_THRESHOLD )
  3625. {
  3626. bool bAlive = true;
  3627. float fvol = 0.5;
  3628. if ( player->GetWaterLevel() > WL_NotInWater )
  3629. {
  3630. // They landed in water.
  3631. }
  3632. else
  3633. {
  3634. // Scale it down if we landed on something that's floating...
  3635. if ( player->GetGroundEntity()->IsFloating() )
  3636. {
  3637. player->m_Local.m_flFallVelocity -= PLAYER_LAND_ON_FLOATING_OBJECT;
  3638. }
  3639. //
  3640. // They hit the ground.
  3641. //
  3642. if( player->GetGroundEntity()->GetAbsVelocity().z < 0.0f )
  3643. {
  3644. // Player landed on a descending object. Subtract the velocity of the ground entity.
  3645. player->m_Local.m_flFallVelocity += player->GetGroundEntity()->GetAbsVelocity().z;
  3646. player->m_Local.m_flFallVelocity = Max( 0.1f, (float)player->m_Local.m_flFallVelocity );
  3647. }
  3648. if ( player->m_Local.m_flFallVelocity > PLAYER_MAX_SAFE_FALL_SPEED )
  3649. {
  3650. //
  3651. // If they hit the ground going this fast they may take damage (and die).
  3652. //
  3653. bAlive = MoveHelper( )->PlayerFallingDamage();
  3654. fvol = 1.0;
  3655. }
  3656. else if ( player->m_Local.m_flFallVelocity > PLAYER_MAX_SAFE_FALL_SPEED / 2 )
  3657. {
  3658. fvol = 0.85;
  3659. }
  3660. else if ( player->m_Local.m_flFallVelocity < PLAYER_MIN_BOUNCE_SPEED )
  3661. {
  3662. fvol = 0;
  3663. }
  3664. }
  3665. PlayerRoughLandingEffects( fvol );
  3666. if (bAlive)
  3667. {
  3668. MoveHelper( )->PlayerSetAnimation( PLAYER_WALK );
  3669. }
  3670. }
  3671. #if defined( CSTRIKE15 )
  3672. float flFallVel = player->m_Local.m_flFallVelocity;
  3673. if ( flFallVel > 16.0f && flFallVel <= PLAYER_FATAL_FALL_SPEED )
  3674. {
  3675. // punch view when we hit the ground
  3676. QAngle punchAngle = player->GetViewPunchAngle();
  3677. punchAngle.x = (flFallVel * 0.001);
  3678. if ( punchAngle.x < 0.75 )
  3679. punchAngle.x = 0.75;
  3680. player->SetViewPunchAngle( punchAngle );
  3681. }
  3682. #endif
  3683. //
  3684. // Clear the fall velocity so the impact doesn't happen again.
  3685. //
  3686. OnLand(player->m_Local.m_flFallVelocity);
  3687. player->m_Local.m_flFallVelocity = 0;
  3688. }
  3689. void CGameMovement::PlayerRoughLandingEffects( float fvol )
  3690. {
  3691. if ( fvol > 0.0 )
  3692. {
  3693. //
  3694. // Play landing sound right away.
  3695. player->m_flStepSoundTime = 400;
  3696. // Play step sound for current texture.
  3697. player->PlayStepSound( (Vector &)mv->GetAbsOrigin(), player->m_pSurfaceData, MAX( fvol, 0.1f ), false );
  3698. //
  3699. // Knock the screen around a little bit, temporary effect.
  3700. //
  3701. player->m_Local.m_viewPunchAngle.Set( ROLL, (player->m_Local.m_flFallVelocity - PLAYER_MAX_SAFE_FALL_SPEED) * 0.013 );
  3702. if ( player->m_Local.m_viewPunchAngle[PITCH] > 8 )
  3703. {
  3704. player->m_Local.m_viewPunchAngle.Set( PITCH, 8 );
  3705. }
  3706. #if !defined( CLIENT_DLL )
  3707. player->RumbleEffect( ( fvol > 0.85f ) ? ( RUMBLE_FALL_LONG ) : ( RUMBLE_FALL_SHORT ), 0, RUMBLE_FLAGS_NONE );
  3708. #endif
  3709. }
  3710. }
  3711. //-----------------------------------------------------------------------------
  3712. // Purpose: Use for ease-in, ease-out style interpolation (accel/decel) Used by ducking code.
  3713. // Input : value -
  3714. // scale -
  3715. // Output : float
  3716. //-----------------------------------------------------------------------------
  3717. float CGameMovement::SplineFraction( float value, float scale )
  3718. {
  3719. float valueSquared;
  3720. value = scale * value;
  3721. valueSquared = value * value;
  3722. // Nice little ease-in, ease-out spline-like curve
  3723. return 3 * valueSquared - 2 * valueSquared * value;
  3724. }
  3725. //-----------------------------------------------------------------------------
  3726. // Purpose: Determine if crouch/uncrouch caused player to get stuck in world
  3727. // Input : direction -
  3728. //-----------------------------------------------------------------------------
  3729. void CGameMovement::FixPlayerCrouchStuck( bool upward )
  3730. {
  3731. EntityHandle_t hitent;
  3732. int i;
  3733. Vector test;
  3734. trace_t dummy;
  3735. int direction = upward ? 1 : 0;
  3736. hitent = TestPlayerPosition( mv->GetAbsOrigin(), COLLISION_GROUP_PLAYER_MOVEMENT, dummy );
  3737. if (hitent == INVALID_ENTITY_HANDLE )
  3738. return;
  3739. VectorCopy( mv->GetAbsOrigin(), test );
  3740. for ( i = 0; i < 36; i++ )
  3741. {
  3742. Vector org = mv->GetAbsOrigin();
  3743. org.z += direction;
  3744. mv->SetAbsOrigin( org );
  3745. hitent = TestPlayerPosition( mv->GetAbsOrigin(), COLLISION_GROUP_PLAYER_MOVEMENT, dummy );
  3746. if (hitent == INVALID_ENTITY_HANDLE )
  3747. return;
  3748. }
  3749. mv->SetAbsOrigin( test ); // Failed
  3750. }
  3751. bool CGameMovement::CanUnduck()
  3752. {
  3753. int i;
  3754. trace_t trace;
  3755. Vector newOrigin;
  3756. VectorCopy( mv->GetAbsOrigin(), newOrigin );
  3757. if ( player->GetGroundEntity() != NULL )
  3758. {
  3759. for ( i = 0; i < 3; i++ )
  3760. {
  3761. newOrigin[i] += ( VEC_DUCK_HULL_MIN[i] - VEC_HULL_MIN[i] );
  3762. }
  3763. }
  3764. else
  3765. {
  3766. // If in air an letting go of crouch, make sure we can offset origin to make
  3767. // up for uncrouching
  3768. Vector hullSizeNormal = VEC_HULL_MAX - VEC_HULL_MIN;
  3769. Vector hullSizeCrouch = VEC_DUCK_HULL_MAX - VEC_DUCK_HULL_MIN;
  3770. Vector viewDelta = ( hullSizeNormal - hullSizeCrouch );
  3771. viewDelta.Negate();
  3772. VectorAdd( newOrigin, viewDelta, newOrigin );
  3773. }
  3774. bool saveducked = player->m_Local.m_bDucked;
  3775. player->m_Local.m_bDucked = false;
  3776. TracePlayerBBox( mv->GetAbsOrigin(), newOrigin, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, trace );
  3777. player->m_Local.m_bDucked = saveducked;
  3778. if ( trace.startsolid || ( trace.fraction != 1.0f ) )
  3779. return false;
  3780. return true;
  3781. }
  3782. //-----------------------------------------------------------------------------
  3783. // Purpose: Stop ducking
  3784. //-----------------------------------------------------------------------------
  3785. void CGameMovement::FinishUnDuck( void )
  3786. {
  3787. int i;
  3788. trace_t trace;
  3789. Vector newOrigin;
  3790. VectorCopy( mv->GetAbsOrigin(), newOrigin );
  3791. if ( player->GetGroundEntity() != NULL )
  3792. {
  3793. for ( i = 0; i < 3; i++ )
  3794. {
  3795. newOrigin[i] += ( VEC_DUCK_HULL_MIN[i] - VEC_HULL_MIN[i] );
  3796. }
  3797. }
  3798. else
  3799. {
  3800. // If in air an letting go of crouch, make sure we can offset origin to make
  3801. // up for uncrouching
  3802. Vector hullSizeNormal = VEC_HULL_MAX - VEC_HULL_MIN;
  3803. Vector hullSizeCrouch = VEC_DUCK_HULL_MAX - VEC_DUCK_HULL_MIN;
  3804. Vector viewDelta = ( hullSizeNormal - hullSizeCrouch );
  3805. viewDelta.Negate();
  3806. VectorAdd( newOrigin, viewDelta, newOrigin );
  3807. }
  3808. player->m_Local.m_bDucked = false;
  3809. player->RemoveFlag( FL_DUCKING | FL_ANIMDUCKING );
  3810. player->m_Local.m_bDucking = false;
  3811. player->m_Local.m_bInDuckJump = false;
  3812. player->SetViewOffset( GetPlayerViewOffset( false ) );
  3813. player->m_Local.m_nDuckTimeMsecs = 0;
  3814. mv->SetAbsOrigin( newOrigin );
  3815. #ifdef CLIENT_DLL
  3816. player->ResetLatched();
  3817. #endif
  3818. // Recategorize position since ducking can change origin
  3819. CategorizePosition();
  3820. }
  3821. //-----------------------------------------------------------------------------
  3822. //
  3823. //-----------------------------------------------------------------------------
  3824. void CGameMovement::UpdateDuckJumpEyeOffset( void )
  3825. {
  3826. if ( player->m_Local.m_nDuckJumpTimeMsecs != 0 )
  3827. {
  3828. int nDuckMilliseconds = MAX( 0, GAMEMOVEMENT_DUCK_TIME - player->m_Local.m_nDuckJumpTimeMsecs );
  3829. if ( nDuckMilliseconds > TIME_TO_UNDUCK_MSECS )
  3830. {
  3831. player->m_Local.m_nDuckJumpTimeMsecs = 0;
  3832. SetDuckedEyeOffset( 0.0f );
  3833. }
  3834. else
  3835. {
  3836. float flDuckFraction = SimpleSpline( 1.0f - FractionUnDucked( nDuckMilliseconds ) );
  3837. SetDuckedEyeOffset( flDuckFraction );
  3838. }
  3839. }
  3840. }
  3841. //-----------------------------------------------------------------------------
  3842. // Purpose:
  3843. //-----------------------------------------------------------------------------
  3844. void CGameMovement::FinishUnDuckJump( trace_t &trace )
  3845. {
  3846. Vector vecNewOrigin;
  3847. VectorCopy( mv->GetAbsOrigin(), vecNewOrigin );
  3848. // Up for uncrouching.
  3849. Vector hullSizeNormal = VEC_HULL_MAX - VEC_HULL_MIN;
  3850. Vector hullSizeCrouch = VEC_DUCK_HULL_MAX - VEC_DUCK_HULL_MIN;
  3851. Vector viewDelta = ( hullSizeNormal - hullSizeCrouch );
  3852. float flDeltaZ = viewDelta.z;
  3853. viewDelta.z *= trace.fraction;
  3854. flDeltaZ -= viewDelta.z;
  3855. player->RemoveFlag( FL_DUCKING | FL_ANIMDUCKING );
  3856. player->m_Local.m_bDucked = false;
  3857. player->m_Local.m_bDucking = false;
  3858. player->m_Local.m_bInDuckJump = false;
  3859. player->m_Local.m_nDuckTimeMsecs = 0;
  3860. player->m_Local.m_nDuckJumpTimeMsecs = 0;
  3861. player->m_Local.m_nJumpTimeMsecs = 0;
  3862. Vector vecViewOffset = GetPlayerViewOffset( false );
  3863. vecViewOffset.z -= flDeltaZ;
  3864. player->SetViewOffset( vecViewOffset );
  3865. VectorSubtract( vecNewOrigin, viewDelta, vecNewOrigin );
  3866. mv->SetAbsOrigin( vecNewOrigin );
  3867. // Recategorize position since ducking can change origin
  3868. CategorizePosition();
  3869. }
  3870. //-----------------------------------------------------------------------------
  3871. // Purpose: Finish ducking
  3872. //-----------------------------------------------------------------------------
  3873. void CGameMovement::FinishDuck( void )
  3874. {
  3875. if ( player->GetFlags() & FL_DUCKING )
  3876. return;
  3877. player->AddFlag( FL_DUCKING );
  3878. player->m_Local.m_bDucked = true;
  3879. player->m_Local.m_bDucking = false;
  3880. player->SetViewOffset( GetPlayerViewOffset( true ) );
  3881. // HACKHACK - Fudge for collision bug - no time to fix this properly
  3882. if ( player->GetGroundEntity() != NULL )
  3883. {
  3884. for ( int i = 0; i < 3; i++ )
  3885. {
  3886. Vector org = mv->GetAbsOrigin();
  3887. org[ i ]-= ( VEC_DUCK_HULL_MIN[i] - VEC_HULL_MIN[i] );
  3888. mv->SetAbsOrigin( org );
  3889. }
  3890. }
  3891. else
  3892. {
  3893. Vector hullSizeNormal = VEC_HULL_MAX - VEC_HULL_MIN;
  3894. Vector hullSizeCrouch = VEC_DUCK_HULL_MAX - VEC_DUCK_HULL_MIN;
  3895. Vector viewDelta = ( hullSizeNormal - hullSizeCrouch );
  3896. Vector out;
  3897. VectorAdd( mv->GetAbsOrigin(), viewDelta, out );
  3898. mv->SetAbsOrigin( out );
  3899. #ifdef CLIENT_DLL
  3900. player->ResetLatched();
  3901. #endif
  3902. }
  3903. // See if we are stuck?
  3904. FixPlayerCrouchStuck( true );
  3905. // Recategorize position since ducking can change origin
  3906. CategorizePosition();
  3907. }
  3908. //-----------------------------------------------------------------------------
  3909. // Purpose:
  3910. //-----------------------------------------------------------------------------
  3911. void CGameMovement::StartUnDuckJump( void )
  3912. {
  3913. player->AddFlag( FL_DUCKING );
  3914. player->m_Local.m_bDucked = true;
  3915. player->m_Local.m_bDucking = false;
  3916. player->SetViewOffset( GetPlayerViewOffset( true ) );
  3917. Vector hullSizeNormal = VEC_HULL_MAX - VEC_HULL_MIN;
  3918. Vector hullSizeCrouch = VEC_DUCK_HULL_MAX - VEC_DUCK_HULL_MIN;
  3919. Vector viewDelta = ( hullSizeNormal - hullSizeCrouch );
  3920. Vector out;
  3921. VectorAdd( mv->GetAbsOrigin(), viewDelta, out );
  3922. mv->SetAbsOrigin( out );
  3923. // See if we are stuck?
  3924. FixPlayerCrouchStuck( true );
  3925. // Recategorize position since ducking can change origin
  3926. CategorizePosition();
  3927. }
  3928. //
  3929. //-----------------------------------------------------------------------------
  3930. // Purpose:
  3931. // Input : duckFraction -
  3932. //-----------------------------------------------------------------------------
  3933. void CGameMovement::SetDuckedEyeOffset( float duckFraction )
  3934. {
  3935. duckFraction = SimpleSpline( duckFraction );
  3936. Vector vDuckHullMin = GetPlayerMins( true );
  3937. Vector vStandHullMin = GetPlayerMins( false );
  3938. float fMore = ( vDuckHullMin.z - vStandHullMin.z );
  3939. Vector vecDuckViewOffset = GetPlayerViewOffset( true );
  3940. Vector vecStandViewOffset = GetPlayerViewOffset( false );
  3941. Vector temp = player->GetViewOffset();
  3942. temp.z = ( ( vecDuckViewOffset.z - fMore ) * duckFraction ) +
  3943. ( vecStandViewOffset.z * ( 1 - duckFraction ) );
  3944. player->SetViewOffset( temp );
  3945. }
  3946. //-----------------------------------------------------------------------------
  3947. // Purpose: Crop the speed of the player when ducking and on the ground.
  3948. // Input: bInDuck - is the player already ducking
  3949. // bInAir - is the player in air
  3950. // NOTE: Only crop player speed once.
  3951. //-----------------------------------------------------------------------------
  3952. void CGameMovement::HandleDuckingSpeedCrop( void )
  3953. {
  3954. if ( !( m_iSpeedCropped & SPEED_CROPPED_DUCK ) && ( player->GetFlags() & FL_DUCKING ) && ( player->GetGroundEntity() != NULL ) )
  3955. {
  3956. float frac = 0.33333333f;
  3957. mv->m_flForwardMove *= frac;
  3958. mv->m_flSideMove *= frac;
  3959. mv->m_flUpMove *= frac;
  3960. m_iSpeedCropped |= SPEED_CROPPED_DUCK;
  3961. }
  3962. }
  3963. //-----------------------------------------------------------------------------
  3964. // Purpose: Check to see if we are in a situation where we can unduck jump.
  3965. //-----------------------------------------------------------------------------
  3966. bool CGameMovement::CanUnDuckJump( trace_t &trace )
  3967. {
  3968. // Trace down to the stand position and see if we can stand.
  3969. Vector vecEnd( mv->GetAbsOrigin() );
  3970. vecEnd.z -= 36.0f; // This will have to change if bounding hull change!
  3971. TracePlayerBBox( mv->GetAbsOrigin(), vecEnd, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, trace );
  3972. if ( trace.fraction < 1.0f )
  3973. {
  3974. // Find the endpoint.
  3975. vecEnd.z = mv->GetAbsOrigin().z + ( -36.0f * trace.fraction );
  3976. // Test a normal hull.
  3977. trace_t traceUp;
  3978. bool bWasDucked = player->m_Local.m_bDucked;
  3979. player->m_Local.m_bDucked = false;
  3980. TracePlayerBBox( vecEnd, vecEnd, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, traceUp );
  3981. player->m_Local.m_bDucked = bWasDucked;
  3982. if ( !traceUp.startsolid )
  3983. return true;
  3984. }
  3985. return false;
  3986. }
  3987. //-----------------------------------------------------------------------------
  3988. // Purpose: See if duck button is pressed and do the appropriate things
  3989. //-----------------------------------------------------------------------------
  3990. void CGameMovement::Duck( void )
  3991. {
  3992. int buttonsChanged = ( mv->m_nOldButtons ^ mv->m_nButtons ); // These buttons have changed this frame
  3993. int buttonsPressed = buttonsChanged & mv->m_nButtons; // The changed ones still down are "pressed"
  3994. int buttonsReleased = buttonsChanged & mv->m_nOldButtons; // The changed ones which were previously down are "released"
  3995. // Check to see if we are in the air.
  3996. bool bInAir = ( player->GetGroundEntity() == NULL );
  3997. bool bInDuck = ( player->GetFlags() & FL_DUCKING ) ? true : false;
  3998. bool bDuckJump = ( player->m_Local.m_nJumpTimeMsecs > 0 );
  3999. bool bDuckJumpTime = ( player->m_Local.m_nDuckJumpTimeMsecs > 0 );
  4000. if ( mv->m_nButtons & IN_DUCK )
  4001. {
  4002. mv->m_nOldButtons |= IN_DUCK;
  4003. }
  4004. else
  4005. {
  4006. mv->m_nOldButtons &= ~IN_DUCK;
  4007. }
  4008. // Handle death.
  4009. if ( IsDead() )
  4010. return;
  4011. // Slow down ducked players.
  4012. HandleDuckingSpeedCrop();
  4013. // If the player is holding down the duck button, the player is in duck transition, ducking, or duck-jumping.
  4014. if ( ( mv->m_nButtons & IN_DUCK ) || player->m_Local.m_bDucking || bInDuck || bDuckJump )
  4015. {
  4016. // DUCK
  4017. if ( ( mv->m_nButtons & IN_DUCK ) || bDuckJump )
  4018. {
  4019. // XBOX SERVER ONLY
  4020. #if !defined(CLIENT_DLL)
  4021. if ( IsGameConsole() && buttonsPressed & IN_DUCK )
  4022. {
  4023. // Hinting logic
  4024. if ( player->GetToggledDuckState() && player->m_nNumCrouches < NUM_CROUCH_HINTS )
  4025. {
  4026. UTIL_HudHintText( player, "#Valve_Hint_Crouch" );
  4027. player->m_nNumCrouches++;
  4028. }
  4029. }
  4030. #endif
  4031. // Have the duck button pressed, but the player currently isn't in the duck position.
  4032. if ( ( buttonsPressed & IN_DUCK ) && !bInDuck && !bDuckJump && !bDuckJumpTime )
  4033. {
  4034. player->m_Local.m_nDuckTimeMsecs = GAMEMOVEMENT_DUCK_TIME;
  4035. player->m_Local.m_bDucking = true;
  4036. }
  4037. // The player is in duck transition and not duck-jumping.
  4038. if ( player->m_Local.m_bDucking && !bDuckJump && !bDuckJumpTime )
  4039. {
  4040. int nDuckMilliseconds = MAX( 0, GAMEMOVEMENT_DUCK_TIME - player->m_Local.m_nDuckTimeMsecs );
  4041. // Finish in duck transition when transition time is over, in "duck", in air.
  4042. if ( ( nDuckMilliseconds > TIME_TO_DUCK_MSECS ) || bInDuck || bInAir )
  4043. {
  4044. FinishDuck();
  4045. }
  4046. else
  4047. {
  4048. // Calc parametric time
  4049. float flDuckFraction = SimpleSpline( FractionDucked( nDuckMilliseconds ) );
  4050. SetDuckedEyeOffset( flDuckFraction );
  4051. }
  4052. }
  4053. if ( bDuckJump )
  4054. {
  4055. // Make the bounding box small immediately.
  4056. if ( !bInDuck )
  4057. {
  4058. StartUnDuckJump();
  4059. }
  4060. else
  4061. {
  4062. // Check for a crouch override.
  4063. if ( !( mv->m_nButtons & IN_DUCK ) )
  4064. {
  4065. trace_t trace;
  4066. if ( CanUnDuckJump( trace ) )
  4067. {
  4068. #if defined ( PORTAL2 ) && !defined ( CLIENT_DLL )
  4069. if ( EntityFromEntityHandle( mv->m_nPlayerHandle.Get() ) )
  4070. {
  4071. CPortal_Player *pPortalPlayer = (CPortal_Player*)EntityFromEntityHandle( mv->m_nPlayerHandle.Get() );
  4072. if ( pPortalPlayer && pPortalPlayer->m_hPortalEnvironment.Get() )
  4073. {
  4074. Warning( "--===Portal player unduckedjumped near a portal! This could cause the trajectory to go screwy===--\n" );
  4075. }
  4076. }
  4077. #endif
  4078. FinishUnDuckJump( trace );
  4079. player->m_Local.m_nDuckJumpTimeMsecs = (int)( ( (float)GAMEMOVEMENT_TIME_TO_UNDUCK_MSECS * ( 1.0f - trace.fraction ) ) + (float)GAMEMOVEMENT_TIME_TO_UNDUCK_MSECS_INV );
  4080. }
  4081. }
  4082. }
  4083. }
  4084. }
  4085. // UNDUCK (or attempt to...)
  4086. else
  4087. {
  4088. if ( player->m_Local.m_bInDuckJump )
  4089. {
  4090. // Check for a crouch override.
  4091. if ( !( mv->m_nButtons & IN_DUCK ) )
  4092. {
  4093. trace_t trace;
  4094. if ( CanUnDuckJump( trace ) )
  4095. {
  4096. FinishUnDuckJump( trace );
  4097. if ( trace.fraction < 1.0f )
  4098. {
  4099. player->m_Local.m_nDuckJumpTimeMsecs = (int)( ( (float)GAMEMOVEMENT_TIME_TO_UNDUCK_MSECS * ( 1.0f - trace.fraction ) ) + (float)GAMEMOVEMENT_TIME_TO_UNDUCK_MSECS_INV );
  4100. }
  4101. }
  4102. }
  4103. else
  4104. {
  4105. player->m_Local.m_bInDuckJump = false;
  4106. }
  4107. }
  4108. if ( bDuckJumpTime )
  4109. return;
  4110. // Try to unduck unless automovement is not allowed
  4111. // NOTE: When not onground, you can always unduck
  4112. if ( player->m_Local.m_bAllowAutoMovement || bInAir || player->m_Local.m_bDucking )
  4113. {
  4114. // We released the duck button, we aren't in "duck" and we are not in the air - start unduck transition.
  4115. if ( ( buttonsReleased & IN_DUCK ) )
  4116. {
  4117. if ( bInDuck && !bDuckJump )
  4118. {
  4119. player->m_Local.m_nDuckTimeMsecs = GAMEMOVEMENT_DUCK_TIME;
  4120. }
  4121. else if ( player->m_Local.m_bDucking && !player->m_Local.m_bDucked )
  4122. {
  4123. // Invert time if release before fully ducked!!!
  4124. int elapsedMilliseconds = GAMEMOVEMENT_DUCK_TIME - player->m_Local.m_nDuckTimeMsecs;
  4125. float fracDucked = FractionDucked( elapsedMilliseconds );
  4126. int remainingUnduckMilliseconds = (int)( fracDucked * TIME_TO_UNDUCK_MSECS );
  4127. player->m_Local.m_nDuckTimeMsecs = GAMEMOVEMENT_DUCK_TIME - TIME_TO_UNDUCK_MSECS + remainingUnduckMilliseconds;
  4128. }
  4129. }
  4130. // Check to see if we are capable of unducking.
  4131. if ( CanUnduck() )
  4132. {
  4133. // or unducking
  4134. if ( ( player->m_Local.m_bDucking || player->m_Local.m_bDucked ) )
  4135. {
  4136. int nDuckMilliseconds = MAX( 0, GAMEMOVEMENT_DUCK_TIME - player->m_Local.m_nDuckTimeMsecs );
  4137. // Finish ducking immediately if duck time is over or not on ground
  4138. if ( nDuckMilliseconds > TIME_TO_UNDUCK_MSECS || ( bInAir && !bDuckJump ) )
  4139. {
  4140. FinishUnDuck();
  4141. }
  4142. else
  4143. {
  4144. // Calc parametric time
  4145. float flDuckFraction = SimpleSpline( 1.0f - FractionUnDucked( nDuckMilliseconds ) );
  4146. SetDuckedEyeOffset( flDuckFraction );
  4147. player->m_Local.m_bDucking = true;
  4148. }
  4149. }
  4150. }
  4151. else
  4152. {
  4153. // Still under something where we can't unduck, so make sure we reset this timer so
  4154. // that we'll unduck once we exit the tunnel, etc.
  4155. if ( player->m_Local.m_nDuckTimeMsecs != GAMEMOVEMENT_DUCK_TIME )
  4156. {
  4157. SetDuckedEyeOffset(1.0f);
  4158. player->m_Local.m_nDuckTimeMsecs = GAMEMOVEMENT_DUCK_TIME;
  4159. player->m_Local.m_bDucked = true;
  4160. player->m_Local.m_bDucking = false;
  4161. player->AddFlag( FL_DUCKING );
  4162. }
  4163. }
  4164. }
  4165. }
  4166. }
  4167. // HACK: (jimd 5/25/2006) we have a reoccuring bug (#50063 in Tracker) where the player's
  4168. // view height gets left at the ducked height while the player is standing, but we haven't
  4169. // been able to repro it to find the cause. It may be fixed now due to a change I'm
  4170. // also making in UpdateDuckJumpEyeOffset but just in case, this code will sense the
  4171. // problem and restore the eye to the proper position. It doesn't smooth the transition,
  4172. // but it is preferable to leaving the player's view too low.
  4173. //
  4174. // If the player is still alive and not an observer, check to make sure that
  4175. // his view height is at the standing height.
  4176. else if ( !IsDead() && !player->IsObserver() && !player->IsInAVehicle() )
  4177. {
  4178. if ( ( player->m_Local.m_nDuckJumpTimeMsecs == 0 ) && ( fabs(player->GetViewOffset().z - GetPlayerViewOffset( false ).z) > 0.1 ) )
  4179. {
  4180. // we should rarely ever get here, so assert so a coder knows when it happens
  4181. AssertMsgOnce( 0, "Restoring player view height\n" );
  4182. // set the eye height to the non-ducked height
  4183. SetDuckedEyeOffset(0.0f);
  4184. }
  4185. }
  4186. }
  4187. static ConVar sv_optimizedmovement( "sv_optimizedmovement", "1", FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY );
  4188. //-----------------------------------------------------------------------------
  4189. // Purpose:
  4190. //-----------------------------------------------------------------------------
  4191. void CGameMovement::PlayerMove( void )
  4192. {
  4193. VPROF( "CGameMovement::PlayerMove" );
  4194. CheckParameters();
  4195. // clear output applied velocity
  4196. mv->m_outWishVel.Init();
  4197. mv->m_outJumpVel.Init();
  4198. MoveHelper( )->ResetTouchList(); // Assume we don't touch anything
  4199. ReduceTimers();
  4200. #ifdef INFESTED_DLL // ignore roll component for Alien Swarm, this is used for vertical aiming
  4201. QAngle vecViewAngles = mv->m_vecViewAngles;
  4202. vecViewAngles[ROLL] = 0;
  4203. AngleVectors (vecViewAngles, &m_vecForward, &m_vecRight, &m_vecUp); // Determine movement angles
  4204. #else
  4205. AngleVectors (mv->m_vecViewAngles, &m_vecForward, &m_vecRight, &m_vecUp ); // Determine movement angles
  4206. #endif
  4207. // Always try and unstick us unless we are using a couple of the movement modes
  4208. MoveType_t moveType = player->GetMoveType();
  4209. if ( moveType != MOVETYPE_NOCLIP &&
  4210. moveType != MOVETYPE_NONE &&
  4211. moveType != MOVETYPE_ISOMETRIC &&
  4212. moveType != MOVETYPE_OBSERVER &&
  4213. !player->pl.deadflag )
  4214. {
  4215. if ( CheckInterval( STUCK ) )
  4216. {
  4217. if ( CheckStuck() )
  4218. {
  4219. // Can't move, we're stuck
  4220. return;
  4221. }
  4222. }
  4223. }
  4224. // Now that we are "unstuck", see where we are (player->GetWaterLevel() and type, player->GetGroundEntity()).
  4225. if ( player->GetMoveType() != MOVETYPE_WALK ||
  4226. mv->m_bGameCodeMovedPlayer ||
  4227. !sv_optimizedmovement.GetBool() )
  4228. {
  4229. CategorizePosition();
  4230. }
  4231. else
  4232. {
  4233. if ( mv->m_vecVelocity.z > 250.0f )
  4234. {
  4235. SetGroundEntity( NULL );
  4236. }
  4237. }
  4238. // Store off the starting water level
  4239. m_nOldWaterLevel = player->GetWaterLevel();
  4240. // If we are not on ground, store off how fast we are moving down
  4241. if ( player->GetGroundEntity() == NULL )
  4242. {
  4243. player->m_Local.m_flFallVelocity = -mv->m_vecVelocity[ 2 ];
  4244. }
  4245. m_nOnLadder = 0;
  4246. player->UpdateStepSound( player->m_pSurfaceData, mv->GetAbsOrigin(), mv->m_vecVelocity );
  4247. UpdateDuckJumpEyeOffset();
  4248. Duck();
  4249. // Don't run ladder code if dead on on a train
  4250. if ( !player->pl.deadflag && !(player->GetFlags() & FL_ONTRAIN) )
  4251. {
  4252. // If was not on a ladder now, but was on one before,
  4253. // get off of the ladder
  4254. // TODO: this causes lots of weirdness.
  4255. //bool bCheckLadder = CheckInterval( LADDER );
  4256. //if ( bCheckLadder || player->GetMoveType() == MOVETYPE_LADDER )
  4257. {
  4258. if ( !LadderMove() &&
  4259. ( player->GetMoveType() == MOVETYPE_LADDER ) )
  4260. {
  4261. // Clear ladder stuff unless player is dead or riding a train
  4262. // It will be reset immediately again next frame if necessary
  4263. player->SetMoveType( MOVETYPE_WALK );
  4264. player->SetMoveCollide( MOVECOLLIDE_DEFAULT );
  4265. }
  4266. }
  4267. }
  4268. // Handle movement modes.
  4269. switch (player->GetMoveType())
  4270. {
  4271. case MOVETYPE_NONE:
  4272. break;
  4273. case MOVETYPE_NOCLIP:
  4274. FullNoClipMove( sv_noclipspeed.GetFloat(), sv_noclipaccelerate.GetFloat() );
  4275. break;
  4276. case MOVETYPE_FLY:
  4277. case MOVETYPE_FLYGRAVITY:
  4278. FullTossMove();
  4279. break;
  4280. case MOVETYPE_LADDER:
  4281. FullLadderMove();
  4282. break;
  4283. case MOVETYPE_WALK:
  4284. FullWalkMove();
  4285. break;
  4286. case MOVETYPE_ISOMETRIC:
  4287. //IsometricMove();
  4288. // Could also try: FullTossMove();
  4289. FullWalkMove();
  4290. break;
  4291. case MOVETYPE_OBSERVER:
  4292. FullObserverMove(); // clips against world&players
  4293. break;
  4294. default:
  4295. DevMsg( 1, "Bogus pmove player movetype %i on (%i) 0=cl 1=sv\n", player->GetMoveType(), player->IsServer());
  4296. break;
  4297. }
  4298. }
  4299. //-----------------------------------------------------------------------------
  4300. // Performs the collision resolution for fliers.
  4301. //-----------------------------------------------------------------------------
  4302. void CGameMovement::PerformFlyCollisionResolution( trace_t &pm, Vector &move )
  4303. {
  4304. Vector base;
  4305. float vel;
  4306. float backoff;
  4307. switch (player->GetMoveCollide())
  4308. {
  4309. case MOVECOLLIDE_FLY_CUSTOM:
  4310. // Do nothing; the velocity should have been modified by touch
  4311. // FIXME: It seems wrong for touch to modify velocity
  4312. // given that it can be called in a number of places
  4313. // where collision resolution do *not* in fact occur
  4314. // Should this ever occur for players!?
  4315. Assert(0);
  4316. break;
  4317. case MOVECOLLIDE_FLY_BOUNCE:
  4318. case MOVECOLLIDE_DEFAULT:
  4319. {
  4320. if (player->GetMoveCollide() == MOVECOLLIDE_FLY_BOUNCE)
  4321. backoff = 2.0 - player->m_surfaceFriction;
  4322. else
  4323. backoff = 1;
  4324. ClipVelocity (mv->m_vecVelocity, pm.plane.normal, mv->m_vecVelocity, backoff);
  4325. }
  4326. break;
  4327. default:
  4328. // Invalid collide type!
  4329. Assert(0);
  4330. break;
  4331. }
  4332. // stop if on ground
  4333. if (pm.plane.normal[2] > 0.7)
  4334. {
  4335. base.Init();
  4336. if (mv->m_vecVelocity[2] < sv_gravity.GetFloat() * gpGlobals->frametime)
  4337. {
  4338. // we're rolling on the ground, add static friction.
  4339. SetGroundEntity( &pm );
  4340. mv->m_vecVelocity[2] = 0;
  4341. }
  4342. vel = DotProduct( mv->m_vecVelocity, mv->m_vecVelocity );
  4343. // Con_DPrintf("%f %f: %.0f %.0f %.0f\n", vel, trace.fraction, ent->velocity[0], ent->velocity[1], ent->velocity[2] );
  4344. if (vel < (30 * 30) || (player->GetMoveCollide() != MOVECOLLIDE_FLY_BOUNCE))
  4345. {
  4346. SetGroundEntity( &pm );
  4347. mv->m_vecVelocity.Init();
  4348. }
  4349. else
  4350. {
  4351. VectorScale (mv->m_vecVelocity, (1.0 - pm.fraction) * gpGlobals->frametime * 0.9, move);
  4352. PushEntity( move, &pm );
  4353. }
  4354. VectorSubtract( mv->m_vecVelocity, base, mv->m_vecVelocity );
  4355. }
  4356. }
  4357. //-----------------------------------------------------------------------------
  4358. // Purpose:
  4359. //-----------------------------------------------------------------------------
  4360. void CGameMovement::FullTossMove( void )
  4361. {
  4362. trace_t pm;
  4363. Vector move;
  4364. CheckWater();
  4365. // add velocity if player is moving
  4366. if ( (mv->m_flForwardMove != 0.0f) || (mv->m_flSideMove != 0.0f) || (mv->m_flUpMove != 0.0f))
  4367. {
  4368. Vector forward, right, up;
  4369. float fmove, smove;
  4370. Vector wishdir, wishvel;
  4371. float wishspeed;
  4372. int i;
  4373. AngleVectors (mv->m_vecViewAngles, &forward, &right, &up); // Determine movement angles
  4374. // Copy movement amounts
  4375. fmove = mv->m_flForwardMove;
  4376. smove = mv->m_flSideMove;
  4377. VectorNormalize (forward); // Normalize remainder of vectors.
  4378. VectorNormalize (right); //
  4379. for (i=0 ; i<3 ; i++) // Determine x and y parts of velocity
  4380. wishvel[i] = forward[i]*fmove + right[i]*smove;
  4381. wishvel[2] += mv->m_flUpMove;
  4382. VectorCopy (wishvel, wishdir); // Determine maginitude of speed of move
  4383. wishspeed = VectorNormalize(wishdir);
  4384. //
  4385. // Clamp to server defined max speed
  4386. //
  4387. if (wishspeed > mv->m_flMaxSpeed)
  4388. {
  4389. VectorScale (wishvel, mv->m_flMaxSpeed/wishspeed, wishvel);
  4390. wishspeed = mv->m_flMaxSpeed;
  4391. }
  4392. // Set pmove velocity
  4393. Accelerate ( wishdir, wishspeed, sv_accelerate.GetFloat() );
  4394. }
  4395. if ( mv->m_vecVelocity[2] > 0 )
  4396. {
  4397. SetGroundEntity( NULL );
  4398. }
  4399. // If on ground and not moving, return.
  4400. if ( player->GetGroundEntity() != NULL )
  4401. {
  4402. if (VectorCompare(player->GetBaseVelocity(), vec3_origin) &&
  4403. VectorCompare(mv->m_vecVelocity, vec3_origin))
  4404. return;
  4405. }
  4406. CheckVelocity();
  4407. // add gravity
  4408. if ( player->GetMoveType() == MOVETYPE_FLYGRAVITY )
  4409. {
  4410. AddGravity();
  4411. }
  4412. // move origin
  4413. // Base velocity is not properly accounted for since this entity will move again after the bounce without
  4414. // taking it into account
  4415. VectorAdd (mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity);
  4416. CheckVelocity();
  4417. VectorScale (mv->m_vecVelocity, gpGlobals->frametime, move);
  4418. VectorSubtract (mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity);
  4419. PushEntity( move, &pm ); // Should this clear basevelocity
  4420. CheckVelocity();
  4421. if (pm.allsolid)
  4422. {
  4423. // entity is trapped in another solid
  4424. SetGroundEntity( &pm );
  4425. mv->m_vecVelocity.Init();
  4426. return;
  4427. }
  4428. if (pm.fraction != 1)
  4429. {
  4430. PerformFlyCollisionResolution( pm, move );
  4431. }
  4432. // check for in water
  4433. CheckWater();
  4434. }
  4435. //-----------------------------------------------------------------------------
  4436. // Purpose: TF2 commander mode movement logic
  4437. //-----------------------------------------------------------------------------
  4438. #pragma warning (disable : 4701)
  4439. void CGameMovement::IsometricMove( void )
  4440. {
  4441. int i;
  4442. Vector wishvel;
  4443. float fmove, smove;
  4444. Vector forward, right, up;
  4445. AngleVectors (mv->m_vecViewAngles, &forward, &right, &up); // Determine movement angles
  4446. // Copy movement amounts
  4447. fmove = mv->m_flForwardMove;
  4448. smove = mv->m_flSideMove;
  4449. // No up / down movement
  4450. forward[2] = 0;
  4451. right[2] = 0;
  4452. VectorNormalize (forward); // Normalize remainder of vectors
  4453. VectorNormalize (right); //
  4454. for (i=0 ; i<3 ; i++) // Determine x and y parts of velocity
  4455. wishvel[i] = forward[i]*fmove + right[i]*smove;
  4456. //wishvel[2] += mv->m_flUpMove;
  4457. Vector out;
  4458. VectorMA (mv->GetAbsOrigin(), gpGlobals->frametime, wishvel, out );
  4459. mv->SetAbsOrigin( out );
  4460. // Zero out the velocity so that we don't accumulate a huge downward velocity from
  4461. // gravity, etc.
  4462. mv->m_vecVelocity.Init();
  4463. }
  4464. #pragma warning (default : 4701)
  4465. bool CGameMovement::GameHasLadders() const
  4466. {
  4467. return true;
  4468. }
  4469. #if (PREDICTION_ERROR_CHECK_LEVEL > 0)
  4470. void _Easy_DiffPrint( CBaseEntity *pEntity, const char *szFormatSTring, ... )
  4471. {
  4472. va_list args;
  4473. va_start( args, szFormatSTring );
  4474. //convert the va_list into something we can pass to diffprint
  4475. char szText[4096];
  4476. Q_vsnprintf( szText, sizeof( szText ), szFormatSTring, args );
  4477. g_pGameMovement->DiffPrint( "%s", szText );
  4478. va_end( args );
  4479. }
  4480. #endif