Team Fortress 2 Source Code as on 22/4/2020
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.

4929 lines
135 KiB

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