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.

1151 lines
29 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Special handling for hl2 usable ladders
  4. //
  5. //=============================================================================//
  6. #include "cbase.h"
  7. #include "hl_gamemovement.h"
  8. #include "in_buttons.h"
  9. #include "utlrbtree.h"
  10. #include "hl2_shareddefs.h"
  11. // memdbgon must be the last include file in a .cpp file!!!
  12. #include "tier0/memdbgon.h"
  13. static ConVar sv_autoladderdismount( "sv_autoladderdismount", "1", FCVAR_REPLICATED, "Automatically dismount from ladders when you reach the end (don't have to +USE)." );
  14. static ConVar sv_ladderautomountdot( "sv_ladderautomountdot", "0.4", FCVAR_REPLICATED, "When auto-mounting a ladder by looking up its axis, this is the tolerance for looking now directly along the ladder axis." );
  15. static ConVar sv_ladder_useonly( "sv_ladder_useonly", "0", FCVAR_REPLICATED, "If set, ladders can only be mounted by pressing +USE" );
  16. #define USE_DISMOUNT_SPEED 100
  17. //-----------------------------------------------------------------------------
  18. // Purpose:
  19. //-----------------------------------------------------------------------------
  20. CHL2GameMovement::CHL2GameMovement()
  21. {
  22. }
  23. //-----------------------------------------------------------------------------
  24. // Purpose:
  25. // Input : type -
  26. // Output : int
  27. //-----------------------------------------------------------------------------
  28. int CHL2GameMovement::GetCheckInterval( IntervalType_t type )
  29. {
  30. // HL2 ladders need to check every frame!!!
  31. if ( type == LADDER )
  32. return 1;
  33. return BaseClass::GetCheckInterval( type );
  34. }
  35. //-----------------------------------------------------------------------------
  36. // Purpose:
  37. // Output : Returns true on success, false on failure.
  38. //-----------------------------------------------------------------------------
  39. bool CHL2GameMovement::IsForceMoveActive()
  40. {
  41. LadderMove_t *lm = GetLadderMove();
  42. return lm->m_bForceLadderMove;
  43. }
  44. //-----------------------------------------------------------------------------
  45. // Purpose: Debounce the USE button
  46. //-----------------------------------------------------------------------------
  47. void CHL2GameMovement::SwallowUseKey()
  48. {
  49. mv->m_nOldButtons |= IN_USE;
  50. player->m_afButtonPressed &= ~IN_USE;
  51. GetHL2Player()->m_bPlayUseDenySound = false;
  52. }
  53. #if !defined( CLIENT_DLL )
  54. // This is a simple helper class to reserver a player sized hull at a spot, owned by the current player so that nothing
  55. // can move into this spot and cause us to get stuck when we get there
  56. class CReservePlayerSpot : public CBaseEntity
  57. {
  58. DECLARE_CLASS( CReservePlayerSpot, CBaseEntity )
  59. public:
  60. static CReservePlayerSpot *ReserveSpot( CBasePlayer *owner, const Vector& org, const Vector& mins, const Vector& maxs, bool& validspot );
  61. virtual void Spawn();
  62. };
  63. CReservePlayerSpot *CReservePlayerSpot::ReserveSpot(
  64. CBasePlayer *owner, const Vector& org, const Vector& mins, const Vector& maxs, bool& validspot )
  65. {
  66. CReservePlayerSpot *spot = ( CReservePlayerSpot * )CreateEntityByName( "reserved_spot" );
  67. Assert( spot );
  68. spot->SetAbsOrigin( org );
  69. UTIL_SetSize( spot, mins, maxs );
  70. spot->SetOwnerEntity( owner );
  71. spot->Spawn();
  72. // See if spot is valid
  73. trace_t tr;
  74. UTIL_TraceHull(
  75. org,
  76. org,
  77. mins,
  78. maxs,
  79. MASK_PLAYERSOLID,
  80. owner,
  81. COLLISION_GROUP_PLAYER_MOVEMENT,
  82. &tr );
  83. validspot = !tr.startsolid;
  84. if ( !validspot )
  85. {
  86. Vector org2 = org + Vector( 0, 0, 1 );
  87. // See if spot is valid
  88. trace_t tr;
  89. UTIL_TraceHull(
  90. org2,
  91. org2,
  92. mins,
  93. maxs,
  94. MASK_PLAYERSOLID,
  95. owner,
  96. COLLISION_GROUP_PLAYER_MOVEMENT,
  97. &tr );
  98. validspot = !tr.startsolid;
  99. }
  100. return spot;
  101. }
  102. //-----------------------------------------------------------------------------
  103. // Purpose:
  104. //-----------------------------------------------------------------------------
  105. void CReservePlayerSpot::Spawn()
  106. {
  107. BaseClass::Spawn();
  108. SetSolid( SOLID_BBOX );
  109. SetMoveType( MOVETYPE_NONE );
  110. // Make entity invisible
  111. AddEffects( EF_NODRAW );
  112. }
  113. LINK_ENTITY_TO_CLASS( reserved_spot, CReservePlayerSpot );
  114. #endif
  115. //-----------------------------------------------------------------------------
  116. // Purpose:
  117. // Input : mounting -
  118. // transit_speed -
  119. // goalpos -
  120. // *ladder -
  121. //-----------------------------------------------------------------------------
  122. void CHL2GameMovement::StartForcedMove( bool mounting, float transit_speed, const Vector& goalpos, CFuncLadder *ladder )
  123. {
  124. LadderMove_t* lm = GetLadderMove();
  125. Assert( lm );
  126. // Already active, just ignore
  127. if ( lm->m_bForceLadderMove )
  128. {
  129. return;
  130. }
  131. #if !defined( CLIENT_DLL )
  132. if ( ladder )
  133. {
  134. ladder->PlayerGotOn( GetHL2Player() );
  135. // If the Ladder only wants to be there for automount checking, abort now
  136. if ( ladder->DontGetOnLadder() )
  137. return;
  138. }
  139. // Reserve goal slot here
  140. bool valid = false;
  141. lm->m_hReservedSpot = CReservePlayerSpot::ReserveSpot(
  142. player,
  143. goalpos,
  144. GetPlayerMins( ( player->GetFlags() & FL_DUCKING ) ? true : false ),
  145. GetPlayerMaxs( ( player->GetFlags() & FL_DUCKING ) ? true : false ),
  146. valid );
  147. if ( !valid )
  148. {
  149. // FIXME: Play a deny sound?
  150. if ( lm->m_hReservedSpot )
  151. {
  152. UTIL_Remove( lm->m_hReservedSpot );
  153. lm->m_hReservedSpot = NULL;
  154. }
  155. return;
  156. }
  157. #endif
  158. // Use current player origin as start and new origin as dest
  159. lm->m_vecGoalPosition = goalpos;
  160. lm->m_vecStartPosition = mv->GetAbsOrigin();
  161. // Figure out how long it will take to make the gap based on transit_speed
  162. Vector delta = lm->m_vecGoalPosition - lm->m_vecStartPosition;
  163. float distance = delta.Length();
  164. Assert( transit_speed > 0.001f );
  165. // Compute time required to move that distance
  166. float transit_time = distance / transit_speed;
  167. if ( transit_time < 0.001f )
  168. {
  169. transit_time = 0.001f;
  170. }
  171. lm->m_bForceLadderMove = true;
  172. lm->m_bForceMount = mounting;
  173. lm->m_flStartTime = gpGlobals->curtime;
  174. lm->m_flArrivalTime = lm->m_flStartTime + transit_time;
  175. lm->m_hForceLadder = ladder;
  176. // Don't get stuck during this traversal since we'll just be slamming the player origin
  177. player->SetMoveType( MOVETYPE_NONE );
  178. player->SetMoveCollide( MOVECOLLIDE_DEFAULT );
  179. player->SetSolid( SOLID_NONE );
  180. SetLadder( ladder );
  181. // Debounce the use key
  182. SwallowUseKey();
  183. }
  184. //-----------------------------------------------------------------------------
  185. // Purpose: Returns false when finished
  186. //-----------------------------------------------------------------------------
  187. bool CHL2GameMovement::ContinueForcedMove()
  188. {
  189. LadderMove_t* lm = GetLadderMove();
  190. Assert( lm );
  191. Assert( lm->m_bForceLadderMove );
  192. // Suppress regular motion
  193. mv->m_flForwardMove = 0.0f;
  194. mv->m_flSideMove = 0.0f;
  195. mv->m_flUpMove = 0.0f;
  196. // How far along are we
  197. float frac = ( gpGlobals->curtime - lm->m_flStartTime ) / ( lm->m_flArrivalTime - lm->m_flStartTime );
  198. if ( frac > 1.0f )
  199. {
  200. lm->m_bForceLadderMove = false;
  201. #if !defined( CLIENT_DLL )
  202. // Remove "reservation entity"
  203. if ( lm->m_hReservedSpot )
  204. {
  205. UTIL_Remove( lm->m_hReservedSpot );
  206. lm->m_hReservedSpot = NULL;
  207. }
  208. #endif
  209. }
  210. frac = clamp( frac, 0.0f, 1.0f );
  211. // Move origin part of the way
  212. Vector delta = lm->m_vecGoalPosition - lm->m_vecStartPosition;
  213. // Compute interpolated position
  214. Vector org;
  215. VectorMA( lm->m_vecStartPosition, frac, delta, org );
  216. mv->SetAbsOrigin( org );
  217. // If finished moving, reset player to correct movetype (or put them on the ladder)
  218. if ( !lm->m_bForceLadderMove )
  219. {
  220. player->SetSolid( SOLID_BBOX );
  221. player->SetMoveType( MOVETYPE_WALK );
  222. if ( lm->m_bForceMount && lm->m_hForceLadder != NULL )
  223. {
  224. player->SetMoveType( MOVETYPE_LADDER );
  225. SetLadder( lm->m_hForceLadder );
  226. }
  227. // Zero out any velocity
  228. mv->m_vecVelocity.Init();
  229. }
  230. // Stil active
  231. return lm->m_bForceLadderMove;
  232. }
  233. //-----------------------------------------------------------------------------
  234. // Purpose: Returns true if the player is on a ladder
  235. // Input : &trace - ignored
  236. //-----------------------------------------------------------------------------
  237. bool CHL2GameMovement::OnLadder( trace_t &trace )
  238. {
  239. return ( GetLadder() != NULL ) ? true : false;
  240. }
  241. //-----------------------------------------------------------------------------
  242. // Purpose:
  243. // Input : ladders -
  244. // maxdist -
  245. // **ppLadder -
  246. // ladderOrigin -
  247. //-----------------------------------------------------------------------------
  248. void CHL2GameMovement::Findladder( float maxdist, CFuncLadder **ppLadder, Vector& ladderOrigin, const CFuncLadder *skipLadder )
  249. {
  250. CFuncLadder *bestLadder = NULL;
  251. float bestDist = MAX_COORD_INTEGER;
  252. Vector bestOrigin;
  253. bestOrigin.Init();
  254. float maxdistSqr = maxdist * maxdist;
  255. int c = CFuncLadder::GetLadderCount();
  256. for ( int i = 0 ; i < c; i++ )
  257. {
  258. CFuncLadder *ladder = CFuncLadder::GetLadder( i );
  259. if ( !ladder->IsEnabled() )
  260. continue;
  261. if ( skipLadder && ladder == skipLadder )
  262. continue;
  263. Vector topPosition;
  264. Vector bottomPosition;
  265. ladder->GetTopPosition( topPosition );
  266. ladder->GetBottomPosition( bottomPosition );
  267. Vector closest;
  268. CalcClosestPointOnLineSegment( mv->GetAbsOrigin(), bottomPosition, topPosition, closest, NULL );
  269. float distSqr = ( closest - mv->GetAbsOrigin() ).LengthSqr();
  270. // Too far away
  271. if ( distSqr > maxdistSqr )
  272. {
  273. continue;
  274. }
  275. // Need to trace to see if it's clear
  276. trace_t tr;
  277. UTIL_TraceLine( mv->GetAbsOrigin(), closest,
  278. MASK_PLAYERSOLID,
  279. player,
  280. COLLISION_GROUP_NONE,
  281. &tr );
  282. if ( tr.fraction != 1.0f &&
  283. tr.m_pEnt &&
  284. tr.m_pEnt != ladder )
  285. {
  286. // Try a trace stepped up from the ground a bit, in case there's something at ground level blocking us.
  287. float sizez = GetPlayerMaxs().z - GetPlayerMins().z;
  288. UTIL_TraceLine( mv->GetAbsOrigin() + Vector( 0, 0, sizez * 0.5f ), closest,
  289. MASK_PLAYERSOLID,
  290. player,
  291. COLLISION_GROUP_NONE,
  292. &tr );
  293. if ( tr.fraction != 1.0f &&
  294. tr.m_pEnt &&
  295. tr.m_pEnt != ladder &&
  296. !tr.m_pEnt->IsSolidFlagSet( FSOLID_TRIGGER ) )
  297. {
  298. continue;
  299. }
  300. }
  301. // See if this is the best one so far
  302. if ( distSqr < bestDist )
  303. {
  304. bestDist = distSqr;
  305. bestLadder = ladder;
  306. bestOrigin = closest;
  307. }
  308. }
  309. // Return best ladder spot
  310. *ppLadder = bestLadder;
  311. ladderOrigin = bestOrigin;
  312. }
  313. static bool NearbyDismountLessFunc( const NearbyDismount_t& lhs, const NearbyDismount_t& rhs )
  314. {
  315. return lhs.distSqr < rhs.distSqr;
  316. }
  317. void CHL2GameMovement::GetSortedDismountNodeList( const Vector &org, float radius, CFuncLadder *ladder, CUtlRBTree< NearbyDismount_t, int >& list )
  318. {
  319. float radiusSqr = radius * radius;
  320. int i;
  321. int c = ladder->GetDismountCount();
  322. for ( i = 0; i < c; i++ )
  323. {
  324. CInfoLadderDismount *spot = ladder->GetDismount( i );
  325. if ( !spot )
  326. continue;
  327. float distSqr = ( spot->GetAbsOrigin() - org ).LengthSqr();
  328. if ( distSqr > radiusSqr )
  329. continue;
  330. NearbyDismount_t nd;
  331. nd.dismount = spot;
  332. nd.distSqr = distSqr;
  333. list.Insert( nd );
  334. }
  335. }
  336. //-----------------------------------------------------------------------------
  337. // Purpose:
  338. // *ladder -
  339. // Output : Returns true on success, false on failure.
  340. //-----------------------------------------------------------------------------
  341. bool CHL2GameMovement::ExitLadderViaDismountNode( CFuncLadder *ladder, bool strict, bool useAlternate )
  342. {
  343. // Find the best ladder exit node
  344. float bestDot = -99999.0f;
  345. float bestDistance = 99999.0f;
  346. Vector bestDest;
  347. bool found = false;
  348. // For 'alternate' dismount
  349. bool foundAlternate = false;
  350. Vector alternateDest;
  351. float alternateDist = 99999.0f;
  352. CUtlRBTree< NearbyDismount_t, int > nearbyDismounts( 0, 0, NearbyDismountLessFunc );
  353. GetSortedDismountNodeList( mv->GetAbsOrigin(), 100.0f, ladder, nearbyDismounts );
  354. int i;
  355. for ( i = nearbyDismounts.FirstInorder(); i != nearbyDismounts.InvalidIndex() ; i = nearbyDismounts.NextInorder( i ) )
  356. {
  357. CInfoLadderDismount *spot = nearbyDismounts[ i ].dismount;
  358. if ( !spot )
  359. {
  360. Assert( !"What happened to the spot!!!" );
  361. continue;
  362. }
  363. // See if it's valid to put the player there...
  364. Vector org = spot->GetAbsOrigin() + Vector( 0, 0, 1 );
  365. trace_t tr;
  366. UTIL_TraceHull(
  367. org,
  368. org,
  369. GetPlayerMins( ( player->GetFlags() & FL_DUCKING ) ? true : false ),
  370. GetPlayerMaxs( ( player->GetFlags() & FL_DUCKING ) ? true : false ),
  371. MASK_PLAYERSOLID,
  372. player,
  373. COLLISION_GROUP_PLAYER_MOVEMENT,
  374. &tr );
  375. // Nope...
  376. if ( tr.startsolid )
  377. {
  378. continue;
  379. }
  380. // Find the best dot product
  381. Vector vecToSpot = org - ( mv->GetAbsOrigin() + player->GetViewOffset() );
  382. vecToSpot.z = 0.0f;
  383. float d = VectorNormalize( vecToSpot );
  384. float dot = vecToSpot.Dot( m_vecForward );
  385. // We're not facing at it...ignore
  386. if ( dot < 0.5f )
  387. {
  388. if( useAlternate && d < alternateDist )
  389. {
  390. alternateDest = org;
  391. alternateDist = d;
  392. foundAlternate = true;
  393. }
  394. continue;
  395. }
  396. if ( dot > bestDot )
  397. {
  398. bestDest = org;
  399. bestDistance = d;
  400. bestDot = dot;
  401. found = true;
  402. }
  403. }
  404. if ( found )
  405. {
  406. // Require a more specific
  407. if ( strict &&
  408. ( ( bestDot < 0.7f ) || ( bestDistance > 40.0f ) ) )
  409. {
  410. return false;
  411. }
  412. StartForcedMove( false, player->MaxSpeed(), bestDest, NULL );
  413. return true;
  414. }
  415. if( useAlternate )
  416. {
  417. // Desperate. Don't refuse to let a person off of a ladder if it can be helped. Use the
  418. // alternate dismount if there is one.
  419. if( foundAlternate && alternateDist <= 60.0f )
  420. {
  421. StartForcedMove( false, player->MaxSpeed(), alternateDest, NULL );
  422. return true;
  423. }
  424. }
  425. return false;
  426. }
  427. //-----------------------------------------------------------------------------
  428. // Purpose:
  429. // Input : bOnLadder -
  430. //-----------------------------------------------------------------------------
  431. void CHL2GameMovement::FullLadderMove()
  432. {
  433. #if !defined( CLIENT_DLL )
  434. CFuncLadder *ladder = GetLadder();
  435. Assert( ladder );
  436. if ( !ladder )
  437. {
  438. return;
  439. }
  440. CheckWater();
  441. // Was jump button pressed? If so, don't do anything here
  442. if ( mv->m_nButtons & IN_JUMP )
  443. {
  444. CheckJumpButton();
  445. return;
  446. }
  447. else
  448. {
  449. mv->m_nOldButtons &= ~IN_JUMP;
  450. }
  451. player->SetGroundEntity( NULL );
  452. // Remember old positions in case we cancel this movement
  453. Vector oldVelocity = mv->m_vecVelocity;
  454. Vector oldOrigin = mv->GetAbsOrigin();
  455. Vector topPosition;
  456. Vector bottomPosition;
  457. ladder->GetTopPosition( topPosition );
  458. ladder->GetBottomPosition( bottomPosition );
  459. // Compute parametric distance along ladder vector...
  460. float oldt;
  461. CalcDistanceSqrToLine( mv->GetAbsOrigin(), topPosition, bottomPosition, &oldt );
  462. // Perform the move accounting for any base velocity.
  463. VectorAdd (mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity);
  464. TryPlayerMove();
  465. VectorSubtract (mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity);
  466. // Pressed buttons are "changed(xor)" and'ed with the mask of currently held buttons
  467. int buttonsChanged = ( mv->m_nOldButtons ^ mv->m_nButtons ); // These buttons have changed this frame
  468. int buttonsPressed = buttonsChanged & mv->m_nButtons;
  469. bool pressed_use = ( buttonsPressed & IN_USE ) ? true : false;
  470. bool pressing_forward_or_side = mv->m_flForwardMove != 0.0f || mv->m_flSideMove != 0.0f;
  471. Vector ladderVec = topPosition - bottomPosition;
  472. float LadderLength = VectorNormalize( ladderVec );
  473. // This test is not perfect by any means, but should help a bit
  474. bool moving_along_ladder = false;
  475. if ( pressing_forward_or_side )
  476. {
  477. float fwdDot = m_vecForward.Dot( ladderVec );
  478. if ( fabs( fwdDot ) > 0.9f )
  479. {
  480. moving_along_ladder = true;
  481. }
  482. }
  483. // Compute parametric distance along ladder vector...
  484. float newt;
  485. CalcDistanceSqrToLine( mv->GetAbsOrigin(), topPosition, bottomPosition, &newt );
  486. // Fudge of 2 units
  487. float tolerance = 1.0f / LadderLength;
  488. bool wouldleaveladder = false;
  489. // Moving pPast top or bottom?
  490. if ( newt < -tolerance )
  491. {
  492. wouldleaveladder = newt < oldt;
  493. }
  494. else if ( newt > ( 1.0f + tolerance ) )
  495. {
  496. wouldleaveladder = newt > oldt;
  497. }
  498. // See if we are near the top or bottom but not moving
  499. float dist1sqr, dist2sqr;
  500. dist1sqr = ( topPosition - mv->GetAbsOrigin() ).LengthSqr();
  501. dist2sqr = ( bottomPosition - mv->GetAbsOrigin() ).LengthSqr();
  502. float dist = MIN( dist1sqr, dist2sqr );
  503. bool neardismountnode = ( dist < 16.0f * 16.0f ) ? true : false;
  504. float ladderUnitsPerTick = ( MAX_CLIMB_SPEED * gpGlobals->interval_per_tick );
  505. bool neardismountnode2 = ( dist < ladderUnitsPerTick * ladderUnitsPerTick ) ? true : false;
  506. // Really close to node, cvar is set, and pressing a key, then simulate a +USE
  507. bool auto_dismount_use = ( neardismountnode2 &&
  508. sv_autoladderdismount.GetBool() &&
  509. pressing_forward_or_side &&
  510. !moving_along_ladder );
  511. bool fully_underwater = ( player->GetWaterLevel() == WL_Eyes ) ? true : false;
  512. // If the user manually pressed use or we're simulating it, then use_dismount will occur
  513. bool use_dismount = pressed_use || auto_dismount_use;
  514. if ( fully_underwater && !use_dismount )
  515. {
  516. // If fully underwater, we require looking directly at a dismount node
  517. /// to "float off" a ladder mid way...
  518. if ( ExitLadderViaDismountNode( ladder, true ) )
  519. {
  520. // See if they +used a dismount point mid-span..
  521. return;
  522. }
  523. }
  524. // If the movement would leave the ladder and they're not automated or pressing use, disallow the movement
  525. if ( !use_dismount )
  526. {
  527. if ( wouldleaveladder )
  528. {
  529. // Don't let them leave the ladder if they were on it
  530. mv->m_vecVelocity = oldVelocity;
  531. mv->SetAbsOrigin( oldOrigin );
  532. }
  533. return;
  534. }
  535. // If the move would not leave the ladder and we're near close to the end, then just accept the move
  536. if ( !wouldleaveladder && !neardismountnode )
  537. {
  538. // Otherwise, if the move would leave the ladder, disallow it.
  539. if ( pressed_use )
  540. {
  541. if ( ExitLadderViaDismountNode( ladder, false, IsX360() ) )
  542. {
  543. // See if they +used a dismount point mid-span..
  544. return;
  545. }
  546. player->SetMoveType( MOVETYPE_WALK );
  547. player->SetMoveCollide( MOVECOLLIDE_DEFAULT );
  548. SetLadder( NULL );
  549. GetHL2Player()->m_bPlayUseDenySound = false;
  550. // Dismount with a bit of velocity in facing direction
  551. VectorScale( m_vecForward, USE_DISMOUNT_SPEED, mv->m_vecVelocity );
  552. mv->m_vecVelocity.z = 50;
  553. }
  554. return;
  555. }
  556. // Debounce the use key
  557. if ( pressed_use )
  558. {
  559. SwallowUseKey();
  560. }
  561. // Try auto exit, if possible
  562. if ( ExitLadderViaDismountNode( ladder, false, pressed_use ) )
  563. {
  564. return;
  565. }
  566. if ( wouldleaveladder )
  567. {
  568. // Otherwise, if the move would leave the ladder, disallow it.
  569. if ( pressed_use )
  570. {
  571. player->SetMoveType( MOVETYPE_WALK );
  572. player->SetMoveCollide( MOVECOLLIDE_DEFAULT );
  573. SetLadder( NULL );
  574. // Dismount with a bit of velocity in facing direction
  575. VectorScale( m_vecForward, USE_DISMOUNT_SPEED, mv->m_vecVelocity );
  576. mv->m_vecVelocity.z = 50;
  577. }
  578. else
  579. {
  580. mv->m_vecVelocity = oldVelocity;
  581. mv->SetAbsOrigin( oldOrigin );
  582. }
  583. }
  584. #endif
  585. }
  586. bool CHL2GameMovement::CheckLadderAutoMountEndPoint( CFuncLadder *ladder, const Vector& bestOrigin )
  587. {
  588. // See if we're really near an endpoint
  589. if ( !ladder )
  590. return false;
  591. Vector top, bottom;
  592. ladder->GetTopPosition( top );
  593. ladder->GetBottomPosition( bottom );
  594. float d1, d2;
  595. d1 = ( top - mv->GetAbsOrigin() ).LengthSqr();
  596. d2 = ( bottom - mv->GetAbsOrigin() ).LengthSqr();
  597. if ( d1 > 16 * 16 && d2 > 16 * 16 )
  598. return false;
  599. Vector ladderAxis;
  600. if ( d1 < 16 * 16 )
  601. {
  602. // Close to top
  603. ladderAxis = bottom - top;
  604. }
  605. else
  606. {
  607. ladderAxis = top - bottom;
  608. }
  609. VectorNormalize( ladderAxis );
  610. if ( ladderAxis.Dot( m_vecForward ) > sv_ladderautomountdot.GetFloat() )
  611. {
  612. StartForcedMove( true, player->MaxSpeed(), bestOrigin, ladder );
  613. return true;
  614. }
  615. return false;
  616. }
  617. bool CHL2GameMovement::CheckLadderAutoMountCone( CFuncLadder *ladder, const Vector& bestOrigin, float maxAngleDelta, float maxDistToLadder )
  618. {
  619. // Never 'back' onto ladders or stafe onto ladders
  620. if ( ladder != NULL &&
  621. ( mv->m_flForwardMove > 0.0f ) )
  622. {
  623. Vector top, bottom;
  624. ladder->GetTopPosition( top );
  625. ladder->GetBottomPosition( bottom );
  626. Vector ladderAxis = top - bottom;
  627. VectorNormalize( ladderAxis );
  628. Vector probe = mv->GetAbsOrigin();
  629. Vector closest;
  630. CalcClosestPointOnLineSegment( probe, bottom, top, closest, NULL );
  631. Vector vecToLadder = closest - probe;
  632. float dist = VectorNormalize( vecToLadder );
  633. Vector flatLadder = vecToLadder;
  634. flatLadder.z = 0.0f;
  635. Vector flatForward = m_vecForward;
  636. flatForward.z = 0.0f;
  637. VectorNormalize( flatLadder );
  638. VectorNormalize( flatForward );
  639. float facingDot = flatForward.Dot( flatLadder );
  640. float angle = acos( facingDot ) * 180 / M_PI;
  641. bool closetoladder = ( dist != 0.0f && dist < maxDistToLadder ) ? true : false;
  642. bool reallyclosetoladder = ( dist != 0.0f && dist < 4.0f ) ? true : false;
  643. bool facingladderaxis = ( angle < maxAngleDelta ) ? true : false;
  644. bool facingalongaxis = ( (float)fabs( ladderAxis.Dot( m_vecForward ) ) > sv_ladderautomountdot.GetFloat() ) ? true : false;
  645. #if 0
  646. Msg( "close %i length %.3f maxdist %.3f facing %.3f dot %.3f ang %.3f\n",
  647. closetoladder ? 1 : 0,
  648. dist,
  649. maxDistToLadder,
  650. (float)fabs( ladderAxis.Dot( m_vecForward ) ),
  651. facingDot,
  652. angle);
  653. #endif
  654. // Tracker 21776: Don't mount ladders this way if strafing
  655. bool strafing = ( fabs( mv->m_flSideMove ) < 1.0f ) ? false : true;
  656. if ( ( ( facingDot > 0.0f && !strafing ) || facingalongaxis ) &&
  657. ( facingladderaxis || reallyclosetoladder ) &&
  658. closetoladder )
  659. {
  660. StartForcedMove( true, player->MaxSpeed(), bestOrigin, ladder );
  661. return true;
  662. }
  663. }
  664. return false;
  665. }
  666. //-----------------------------------------------------------------------------
  667. // Purpose: Must be facing toward ladder
  668. // Input : *ladder -
  669. // Output : Returns true on success, false on failure.
  670. //-----------------------------------------------------------------------------
  671. bool CHL2GameMovement::LookingAtLadder( CFuncLadder *ladder )
  672. {
  673. if ( !ladder )
  674. {
  675. return false;
  676. }
  677. // Get ladder end points
  678. Vector top, bottom;
  679. ladder->GetTopPosition( top );
  680. ladder->GetBottomPosition( bottom );
  681. // Find closest point on ladder to player (could be an endpoint)
  682. Vector closest;
  683. CalcClosestPointOnLineSegment( mv->GetAbsOrigin(), bottom, top, closest, NULL );
  684. // Flatten our view direction to 2D
  685. Vector flatForward = m_vecForward;
  686. flatForward.z = 0.0f;
  687. // Because the ladder itself is not a solid, the player's origin may actually be
  688. // permitted to pass it, and that will screw up our dot product.
  689. // So back up the player's origin a bit to do the facing calculation.
  690. Vector vecAdjustedOrigin = mv->GetAbsOrigin() - 8.0f * flatForward;
  691. // Figure out vector from player to closest point on ladder
  692. Vector vecToLadder = closest - vecAdjustedOrigin;
  693. // Flatten it to 2D
  694. Vector flatLadder = vecToLadder;
  695. flatLadder.z = 0.0f;
  696. // Normalize the vectors (unnecessary)
  697. VectorNormalize( flatLadder );
  698. VectorNormalize( flatForward );
  699. // Compute dot product to see if forward is in same direction as vec to ladder
  700. float facingDot = flatForward.Dot( flatLadder );
  701. float requiredDot = ( sv_ladder_useonly.GetBool() ) ? -0.99 : 0.0;
  702. // Facing same direction if dot > = requiredDot...
  703. bool facingladder = ( facingDot >= requiredDot );
  704. return facingladder;
  705. }
  706. //-----------------------------------------------------------------------------
  707. // Purpose:
  708. // Input : &trace -
  709. //-----------------------------------------------------------------------------
  710. bool CHL2GameMovement::CheckLadderAutoMount( CFuncLadder *ladder, const Vector& bestOrigin )
  711. {
  712. #if !defined( CLIENT_DLL )
  713. if ( ladder != NULL )
  714. {
  715. StartForcedMove( true, player->MaxSpeed(), bestOrigin, ladder );
  716. return true;
  717. }
  718. #endif
  719. return false;
  720. }
  721. //-----------------------------------------------------------------------------
  722. // Purpose:
  723. //-----------------------------------------------------------------------------
  724. bool CHL2GameMovement::LadderMove( void )
  725. {
  726. if ( player->GetMoveType() == MOVETYPE_NOCLIP )
  727. {
  728. SetLadder( NULL );
  729. return false;
  730. }
  731. // If being forced to mount/dismount continue to act like we are on the ladder
  732. if ( IsForceMoveActive() && ContinueForcedMove() )
  733. {
  734. return true;
  735. }
  736. CFuncLadder *bestLadder = NULL;
  737. Vector bestOrigin( 0, 0, 0 );
  738. CFuncLadder *ladder = GetLadder();
  739. // Something 1) deactivated the ladder... or 2) something external applied
  740. // a force to us. In either case make the player fall, etc.
  741. if ( ladder &&
  742. ( !ladder->IsEnabled() ||
  743. ( player->GetBaseVelocity().LengthSqr() > 1.0f ) ) )
  744. {
  745. GetHL2Player()->ExitLadder();
  746. ladder = NULL;
  747. }
  748. if ( !ladder )
  749. {
  750. Findladder( 64.0f, &bestLadder, bestOrigin, NULL );
  751. }
  752. #if !defined (CLIENT_DLL)
  753. if( !ladder && bestLadder && sv_ladder_useonly.GetBool() )
  754. {
  755. GetHL2Player()->DisplayLadderHudHint();
  756. }
  757. #endif
  758. int buttonsChanged = ( mv->m_nOldButtons ^ mv->m_nButtons ); // These buttons have changed this frame
  759. int buttonsPressed = buttonsChanged & mv->m_nButtons;
  760. bool pressed_use = ( buttonsPressed & IN_USE ) ? true : false;
  761. // If I'm already moving on a ladder, use the previous ladder direction
  762. if ( !ladder && !pressed_use )
  763. {
  764. // If flying through air, allow mounting ladders if we are facing < 15 degress from the ladder and we are close
  765. if ( !ladder && !sv_ladder_useonly.GetBool() )
  766. {
  767. // Tracker 6625: Don't need to be leaping to auto mount using this method...
  768. // But if we are on the ground, then we must not be backing into the ladder (Tracker 12961)
  769. bool onground = player->GetGroundEntity() ? true : false;
  770. if ( !onground || ( mv->m_flForwardMove > 0.0f ) )
  771. {
  772. if ( CheckLadderAutoMountCone( bestLadder, bestOrigin, 15.0f, 32.0f ) )
  773. {
  774. return true;
  775. }
  776. }
  777. // Pressing forward while looking at ladder and standing (or floating) near a mounting point
  778. if ( mv->m_flForwardMove > 0.0f )
  779. {
  780. if ( CheckLadderAutoMountEndPoint( bestLadder, bestOrigin ) )
  781. {
  782. return true;
  783. }
  784. }
  785. }
  786. return false;
  787. }
  788. if ( !ladder &&
  789. LookingAtLadder( bestLadder ) &&
  790. CheckLadderAutoMount( bestLadder, bestOrigin ) )
  791. {
  792. return true;
  793. }
  794. // Reassign the ladder
  795. ladder = GetLadder();
  796. if ( !ladder )
  797. {
  798. return false;
  799. }
  800. // Don't play the deny sound
  801. if ( pressed_use )
  802. {
  803. GetHL2Player()->m_bPlayUseDenySound = false;
  804. }
  805. // Make sure we are on the ladder
  806. player->SetMoveType( MOVETYPE_LADDER );
  807. player->SetMoveCollide( MOVECOLLIDE_DEFAULT );
  808. player->SetGravity( 0.0f );
  809. float forwardSpeed = 0.0f;
  810. float rightSpeed = 0.0f;
  811. float speed = player->MaxSpeed();
  812. if ( mv->m_nButtons & IN_BACK )
  813. {
  814. forwardSpeed -= speed;
  815. }
  816. if ( mv->m_nButtons & IN_FORWARD )
  817. {
  818. forwardSpeed += speed;
  819. }
  820. if ( mv->m_nButtons & IN_MOVELEFT )
  821. {
  822. rightSpeed -= speed;
  823. }
  824. if ( mv->m_nButtons & IN_MOVERIGHT )
  825. {
  826. rightSpeed += speed;
  827. }
  828. if ( mv->m_nButtons & IN_JUMP )
  829. {
  830. player->SetMoveType( MOVETYPE_WALK );
  831. // Remove from ladder
  832. SetLadder( NULL );
  833. // Jump in view direction
  834. Vector jumpDir = m_vecForward;
  835. // unless pressing backward or something like that
  836. if ( mv->m_flForwardMove < 0.0f )
  837. {
  838. jumpDir = -jumpDir;
  839. }
  840. VectorNormalize( jumpDir );
  841. VectorScale( jumpDir, MAX_CLIMB_SPEED, mv->m_vecVelocity );
  842. // Tracker 13558: Don't add any extra z velocity if facing downward at all
  843. if ( m_vecForward.z >= 0.0f )
  844. {
  845. mv->m_vecVelocity.z = mv->m_vecVelocity.z + 50;
  846. }
  847. return false;
  848. }
  849. if ( forwardSpeed != 0 || rightSpeed != 0 )
  850. {
  851. // See if the player is looking toward the top or the bottom
  852. Vector velocity;
  853. VectorScale( m_vecForward, forwardSpeed, velocity );
  854. VectorMA( velocity, rightSpeed, m_vecRight, velocity );
  855. VectorNormalize( velocity );
  856. Vector ladderUp;
  857. ladder->ComputeLadderDir( ladderUp );
  858. VectorNormalize( ladderUp );
  859. Vector topPosition;
  860. Vector bottomPosition;
  861. ladder->GetTopPosition( topPosition );
  862. ladder->GetBottomPosition( bottomPosition );
  863. // Check to see if we've mounted the ladder in a bogus spot and, if so, just fall off the ladder...
  864. float dummyt = 0.0f;
  865. float distFromLadderSqr = CalcDistanceSqrToLine( mv->GetAbsOrigin(), topPosition, bottomPosition, &dummyt );
  866. if ( distFromLadderSqr > 36.0f )
  867. {
  868. // Uh oh, we fell off zee ladder...
  869. player->SetMoveType( MOVETYPE_WALK );
  870. // Remove from ladder
  871. SetLadder( NULL );
  872. return false;
  873. }
  874. bool ishorizontal = fabs( topPosition.z - bottomPosition.z ) < 64.0f ? true : false;
  875. float changeover = ishorizontal ? 0.0f : 0.3f;
  876. float factor = 1.0f;
  877. if ( velocity.z >= 0 )
  878. {
  879. float dotTop = ladderUp.Dot( velocity );
  880. if ( dotTop < -changeover )
  881. {
  882. // Aimed at bottom
  883. factor = -1.0f;
  884. }
  885. }
  886. else
  887. {
  888. float dotBottom = -ladderUp.Dot( velocity );
  889. if ( dotBottom > changeover )
  890. {
  891. factor = -1.0f;
  892. }
  893. }
  894. #ifdef _XBOX
  895. if( sv_ladders_useonly.GetBool() )
  896. {
  897. // Stick up climbs up, stick down climbs down. No matter which way you're looking.
  898. if ( mv->m_nButtons & IN_FORWARD )
  899. {
  900. factor = 1.0f;
  901. }
  902. else if( mv->m_nButtons & IN_BACK )
  903. {
  904. factor = -1.0f;
  905. }
  906. }
  907. #endif//_XBOX
  908. mv->m_vecVelocity = MAX_CLIMB_SPEED * factor * ladderUp;
  909. }
  910. else
  911. {
  912. mv->m_vecVelocity.Init();
  913. }
  914. return true;
  915. }
  916. void CHL2GameMovement::SetGroundEntity( trace_t *pm )
  917. {
  918. CBaseEntity *newGround = pm ? pm->m_pEnt : NULL;
  919. //Adrian: Special case for combine balls.
  920. if ( newGround && newGround->GetCollisionGroup() == HL2COLLISION_GROUP_COMBINE_BALL_NPC )
  921. {
  922. return;
  923. }
  924. BaseClass::SetGroundEntity( pm );
  925. }
  926. bool CHL2GameMovement::CanAccelerate()
  927. {
  928. #ifdef HL2MP
  929. if ( player->IsObserver() )
  930. {
  931. return true;
  932. }
  933. #endif
  934. BaseClass::CanAccelerate();
  935. return true;
  936. }
  937. #ifndef PORTAL // Portal inherits from this but needs to declare it's own global interface
  938. // Expose our interface.
  939. static CHL2GameMovement g_GameMovement;
  940. IGameMovement *g_pGameMovement = ( IGameMovement * )&g_GameMovement;
  941. EXPOSE_SINGLE_INTERFACE_GLOBALVAR(CGameMovement, IGameMovement,INTERFACENAME_GAMEMOVEMENT, g_GameMovement );
  942. #endif