summaryrefslogtreecommitdiff
path: root/game/shared/hl2/hl_gamemovement.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'game/shared/hl2/hl_gamemovement.cpp')
-rw-r--r--game/shared/hl2/hl_gamemovement.cpp1152
1 files changed, 1152 insertions, 0 deletions
diff --git a/game/shared/hl2/hl_gamemovement.cpp b/game/shared/hl2/hl_gamemovement.cpp
new file mode 100644
index 0000000..e7e573e
--- /dev/null
+++ b/game/shared/hl2/hl_gamemovement.cpp
@@ -0,0 +1,1152 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Special handling for hl2 usable ladders
+//
+//=============================================================================//
+#include "cbase.h"
+#include "hl_gamemovement.h"
+#include "in_buttons.h"
+#include "utlrbtree.h"
+#include "hl2_shareddefs.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+static ConVar sv_autoladderdismount( "sv_autoladderdismount", "1", FCVAR_REPLICATED, "Automatically dismount from ladders when you reach the end (don't have to +USE)." );
+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." );
+
+static ConVar sv_ladder_useonly( "sv_ladder_useonly", "0", FCVAR_REPLICATED, "If set, ladders can only be mounted by pressing +USE" );
+
+#define USE_DISMOUNT_SPEED 100
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CHL2GameMovement::CHL2GameMovement()
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : type -
+// Output : int
+//-----------------------------------------------------------------------------
+int CHL2GameMovement::GetCheckInterval( IntervalType_t type )
+{
+ // HL2 ladders need to check every frame!!!
+ if ( type == LADDER )
+ return 1;
+
+ return BaseClass::GetCheckInterval( type );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CHL2GameMovement::IsForceMoveActive()
+{
+ LadderMove_t *lm = GetLadderMove();
+ return lm->m_bForceLadderMove;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Debounce the USE button
+//-----------------------------------------------------------------------------
+void CHL2GameMovement::SwallowUseKey()
+{
+ mv->m_nOldButtons |= IN_USE;
+ player->m_afButtonPressed &= ~IN_USE;
+
+ GetHL2Player()->m_bPlayUseDenySound = false;
+}
+
+#if !defined( CLIENT_DLL )
+// This is a simple helper class to reserver a player sized hull at a spot, owned by the current player so that nothing
+// can move into this spot and cause us to get stuck when we get there
+class CReservePlayerSpot : public CBaseEntity
+{
+ DECLARE_CLASS( CReservePlayerSpot, CBaseEntity )
+public:
+ static CReservePlayerSpot *ReserveSpot( CBasePlayer *owner, const Vector& org, const Vector& mins, const Vector& maxs, bool& validspot );
+
+ virtual void Spawn();
+};
+
+CReservePlayerSpot *CReservePlayerSpot::ReserveSpot(
+ CBasePlayer *owner, const Vector& org, const Vector& mins, const Vector& maxs, bool& validspot )
+{
+ CReservePlayerSpot *spot = ( CReservePlayerSpot * )CreateEntityByName( "reserved_spot" );
+ Assert( spot );
+
+ spot->SetAbsOrigin( org );
+ UTIL_SetSize( spot, mins, maxs );
+ spot->SetOwnerEntity( owner );
+ spot->Spawn();
+
+ // See if spot is valid
+ trace_t tr;
+ UTIL_TraceHull(
+ org,
+ org,
+ mins,
+ maxs,
+ MASK_PLAYERSOLID,
+ owner,
+ COLLISION_GROUP_PLAYER_MOVEMENT,
+ &tr );
+
+ validspot = !tr.startsolid;
+
+ if ( !validspot )
+ {
+ Vector org2 = org + Vector( 0, 0, 1 );
+
+ // See if spot is valid
+ trace_t tr;
+ UTIL_TraceHull(
+ org2,
+ org2,
+ mins,
+ maxs,
+ MASK_PLAYERSOLID,
+ owner,
+ COLLISION_GROUP_PLAYER_MOVEMENT,
+ &tr );
+ validspot = !tr.startsolid;
+ }
+
+ return spot;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CReservePlayerSpot::Spawn()
+{
+ BaseClass::Spawn();
+
+ SetSolid( SOLID_BBOX );
+ SetMoveType( MOVETYPE_NONE );
+ // Make entity invisible
+ AddEffects( EF_NODRAW );
+}
+
+LINK_ENTITY_TO_CLASS( reserved_spot, CReservePlayerSpot );
+
+#endif
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : mounting -
+// transit_speed -
+// goalpos -
+// *ladder -
+//-----------------------------------------------------------------------------
+void CHL2GameMovement::StartForcedMove( bool mounting, float transit_speed, const Vector& goalpos, CFuncLadder *ladder )
+{
+ LadderMove_t* lm = GetLadderMove();
+ Assert( lm );
+ // Already active, just ignore
+ if ( lm->m_bForceLadderMove )
+ {
+ return;
+ }
+
+#if !defined( CLIENT_DLL )
+ if ( ladder )
+ {
+ ladder->PlayerGotOn( GetHL2Player() );
+
+ // If the Ladder only wants to be there for automount checking, abort now
+ if ( ladder->DontGetOnLadder() )
+ return;
+ }
+
+ // Reserve goal slot here
+ bool valid = false;
+ lm->m_hReservedSpot = CReservePlayerSpot::ReserveSpot(
+ player,
+ goalpos,
+ GetPlayerMins( ( player->GetFlags() & FL_DUCKING ) ? true : false ),
+ GetPlayerMaxs( ( player->GetFlags() & FL_DUCKING ) ? true : false ),
+ valid );
+ if ( !valid )
+ {
+ // FIXME: Play a deny sound?
+ if ( lm->m_hReservedSpot )
+ {
+ UTIL_Remove( lm->m_hReservedSpot );
+ lm->m_hReservedSpot = NULL;
+ }
+ return;
+ }
+#endif
+
+ // Use current player origin as start and new origin as dest
+ lm->m_vecGoalPosition = goalpos;
+ lm->m_vecStartPosition = mv->GetAbsOrigin();
+
+ // Figure out how long it will take to make the gap based on transit_speed
+ Vector delta = lm->m_vecGoalPosition - lm->m_vecStartPosition;
+
+ float distance = delta.Length();
+
+ Assert( transit_speed > 0.001f );
+
+ // Compute time required to move that distance
+ float transit_time = distance / transit_speed;
+ if ( transit_time < 0.001f )
+ {
+ transit_time = 0.001f;
+ }
+
+ lm->m_bForceLadderMove = true;
+ lm->m_bForceMount = mounting;
+
+ lm->m_flStartTime = gpGlobals->curtime;
+ lm->m_flArrivalTime = lm->m_flStartTime + transit_time;
+
+ lm->m_hForceLadder = ladder;
+
+ // Don't get stuck during this traversal since we'll just be slamming the player origin
+ player->SetMoveType( MOVETYPE_NONE );
+ player->SetMoveCollide( MOVECOLLIDE_DEFAULT );
+ player->SetSolid( SOLID_NONE );
+ SetLadder( ladder );
+
+ // Debounce the use key
+ SwallowUseKey();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns false when finished
+//-----------------------------------------------------------------------------
+bool CHL2GameMovement::ContinueForcedMove()
+{
+ LadderMove_t* lm = GetLadderMove();
+ Assert( lm );
+ Assert( lm->m_bForceLadderMove );
+
+ // Suppress regular motion
+ mv->m_flForwardMove = 0.0f;
+ mv->m_flSideMove = 0.0f;
+ mv->m_flUpMove = 0.0f;
+
+ // How far along are we
+ float frac = ( gpGlobals->curtime - lm->m_flStartTime ) / ( lm->m_flArrivalTime - lm->m_flStartTime );
+ if ( frac > 1.0f )
+ {
+ lm->m_bForceLadderMove = false;
+#if !defined( CLIENT_DLL )
+ // Remove "reservation entity"
+ if ( lm->m_hReservedSpot )
+ {
+ UTIL_Remove( lm->m_hReservedSpot );
+ lm->m_hReservedSpot = NULL;
+ }
+#endif
+ }
+
+ frac = clamp( frac, 0.0f, 1.0f );
+
+ // Move origin part of the way
+ Vector delta = lm->m_vecGoalPosition - lm->m_vecStartPosition;
+
+ // Compute interpolated position
+ Vector org;
+ VectorMA( lm->m_vecStartPosition, frac, delta, org );
+ mv->SetAbsOrigin( org );
+
+ // If finished moving, reset player to correct movetype (or put them on the ladder)
+ if ( !lm->m_bForceLadderMove )
+ {
+ player->SetSolid( SOLID_BBOX );
+ player->SetMoveType( MOVETYPE_WALK );
+
+ if ( lm->m_bForceMount && lm->m_hForceLadder != NULL )
+ {
+ player->SetMoveType( MOVETYPE_LADDER );
+ SetLadder( lm->m_hForceLadder );
+ }
+
+ // Zero out any velocity
+ mv->m_vecVelocity.Init();
+ }
+
+ // Stil active
+ return lm->m_bForceLadderMove;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns true if the player is on a ladder
+// Input : &trace - ignored
+//-----------------------------------------------------------------------------
+bool CHL2GameMovement::OnLadder( trace_t &trace )
+{
+ return ( GetLadder() != NULL ) ? true : false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : ladders -
+// maxdist -
+// **ppLadder -
+// ladderOrigin -
+//-----------------------------------------------------------------------------
+void CHL2GameMovement::Findladder( float maxdist, CFuncLadder **ppLadder, Vector& ladderOrigin, const CFuncLadder *skipLadder )
+{
+ CFuncLadder *bestLadder = NULL;
+ float bestDist = MAX_COORD_INTEGER;
+ Vector bestOrigin;
+
+ bestOrigin.Init();
+
+ float maxdistSqr = maxdist * maxdist;
+
+
+ int c = CFuncLadder::GetLadderCount();
+ for ( int i = 0 ; i < c; i++ )
+ {
+ CFuncLadder *ladder = CFuncLadder::GetLadder( i );
+
+ if ( !ladder->IsEnabled() )
+ continue;
+
+ if ( skipLadder && ladder == skipLadder )
+ continue;
+
+ Vector topPosition;
+ Vector bottomPosition;
+
+ ladder->GetTopPosition( topPosition );
+ ladder->GetBottomPosition( bottomPosition );
+
+ Vector closest;
+ CalcClosestPointOnLineSegment( mv->GetAbsOrigin(), bottomPosition, topPosition, closest, NULL );
+
+ float distSqr = ( closest - mv->GetAbsOrigin() ).LengthSqr();
+
+ // Too far away
+ if ( distSqr > maxdistSqr )
+ {
+ continue;
+ }
+
+ // Need to trace to see if it's clear
+ trace_t tr;
+
+ UTIL_TraceLine( mv->GetAbsOrigin(), closest,
+ MASK_PLAYERSOLID,
+ player,
+ COLLISION_GROUP_NONE,
+ &tr );
+
+ if ( tr.fraction != 1.0f &&
+ tr.m_pEnt &&
+ tr.m_pEnt != ladder )
+ {
+ // Try a trace stepped up from the ground a bit, in case there's something at ground level blocking us.
+ float sizez = GetPlayerMaxs().z - GetPlayerMins().z;
+
+ UTIL_TraceLine( mv->GetAbsOrigin() + Vector( 0, 0, sizez * 0.5f ), closest,
+ MASK_PLAYERSOLID,
+ player,
+ COLLISION_GROUP_NONE,
+ &tr );
+
+ if ( tr.fraction != 1.0f &&
+ tr.m_pEnt &&
+ tr.m_pEnt != ladder &&
+ !tr.m_pEnt->IsSolidFlagSet( FSOLID_TRIGGER ) )
+ {
+ continue;
+ }
+ }
+
+ // See if this is the best one so far
+ if ( distSqr < bestDist )
+ {
+ bestDist = distSqr;
+ bestLadder = ladder;
+ bestOrigin = closest;
+ }
+ }
+
+ // Return best ladder spot
+ *ppLadder = bestLadder;
+ ladderOrigin = bestOrigin;
+
+}
+
+static bool NearbyDismountLessFunc( const NearbyDismount_t& lhs, const NearbyDismount_t& rhs )
+{
+ return lhs.distSqr < rhs.distSqr;
+}
+
+void CHL2GameMovement::GetSortedDismountNodeList( const Vector &org, float radius, CFuncLadder *ladder, CUtlRBTree< NearbyDismount_t, int >& list )
+{
+ float radiusSqr = radius * radius;
+
+ int i;
+ int c = ladder->GetDismountCount();
+ for ( i = 0; i < c; i++ )
+ {
+ CInfoLadderDismount *spot = ladder->GetDismount( i );
+ if ( !spot )
+ continue;
+
+ float distSqr = ( spot->GetAbsOrigin() - org ).LengthSqr();
+ if ( distSqr > radiusSqr )
+ continue;
+
+ NearbyDismount_t nd;
+ nd.dismount = spot;
+ nd.distSqr = distSqr;
+
+ list.Insert( nd );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// *ladder -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CHL2GameMovement::ExitLadderViaDismountNode( CFuncLadder *ladder, bool strict, bool useAlternate )
+{
+ // Find the best ladder exit node
+ float bestDot = -99999.0f;
+ float bestDistance = 99999.0f;
+ Vector bestDest;
+ bool found = false;
+
+ // For 'alternate' dismount
+ bool foundAlternate = false;
+ Vector alternateDest;
+ float alternateDist = 99999.0f;
+
+ CUtlRBTree< NearbyDismount_t, int > nearbyDismounts( 0, 0, NearbyDismountLessFunc );
+
+ GetSortedDismountNodeList( mv->GetAbsOrigin(), 100.0f, ladder, nearbyDismounts );
+
+ int i;
+
+ for ( i = nearbyDismounts.FirstInorder(); i != nearbyDismounts.InvalidIndex() ; i = nearbyDismounts.NextInorder( i ) )
+ {
+ CInfoLadderDismount *spot = nearbyDismounts[ i ].dismount;
+ if ( !spot )
+ {
+ Assert( !"What happened to the spot!!!" );
+ continue;
+ }
+
+ // See if it's valid to put the player there...
+ Vector org = spot->GetAbsOrigin() + Vector( 0, 0, 1 );
+
+ trace_t tr;
+ UTIL_TraceHull(
+ org,
+ org,
+ GetPlayerMins( ( player->GetFlags() & FL_DUCKING ) ? true : false ),
+ GetPlayerMaxs( ( player->GetFlags() & FL_DUCKING ) ? true : false ),
+ MASK_PLAYERSOLID,
+ player,
+ COLLISION_GROUP_PLAYER_MOVEMENT,
+ &tr );
+
+ // Nope...
+ if ( tr.startsolid )
+ {
+ continue;
+ }
+
+ // Find the best dot product
+ Vector vecToSpot = org - ( mv->GetAbsOrigin() + player->GetViewOffset() );
+ vecToSpot.z = 0.0f;
+ float d = VectorNormalize( vecToSpot );
+
+ float dot = vecToSpot.Dot( m_vecForward );
+
+ // We're not facing at it...ignore
+ if ( dot < 0.5f )
+ {
+ if( useAlternate && d < alternateDist )
+ {
+ alternateDest = org;
+ alternateDist = d;
+ foundAlternate = true;
+ }
+
+ continue;
+ }
+
+ if ( dot > bestDot )
+ {
+ bestDest = org;
+ bestDistance = d;
+ bestDot = dot;
+ found = true;
+ }
+ }
+
+ if ( found )
+ {
+ // Require a more specific
+ if ( strict &&
+ ( ( bestDot < 0.7f ) || ( bestDistance > 40.0f ) ) )
+ {
+ return false;
+ }
+
+ StartForcedMove( false, player->MaxSpeed(), bestDest, NULL );
+ return true;
+ }
+
+ if( useAlternate )
+ {
+ // Desperate. Don't refuse to let a person off of a ladder if it can be helped. Use the
+ // alternate dismount if there is one.
+ if( foundAlternate && alternateDist <= 60.0f )
+ {
+ StartForcedMove( false, player->MaxSpeed(), alternateDest, NULL );
+ return true;
+ }
+ }
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : bOnLadder -
+//-----------------------------------------------------------------------------
+void CHL2GameMovement::FullLadderMove()
+{
+#if !defined( CLIENT_DLL )
+ CFuncLadder *ladder = GetLadder();
+ Assert( ladder );
+ if ( !ladder )
+ {
+ return;
+ }
+
+ CheckWater();
+
+ // Was jump button pressed? If so, don't do anything here
+ if ( mv->m_nButtons & IN_JUMP )
+ {
+ CheckJumpButton();
+ return;
+ }
+ else
+ {
+ mv->m_nOldButtons &= ~IN_JUMP;
+ }
+
+ player->SetGroundEntity( NULL );
+
+ // Remember old positions in case we cancel this movement
+ Vector oldVelocity = mv->m_vecVelocity;
+ Vector oldOrigin = mv->GetAbsOrigin();
+
+ Vector topPosition;
+ Vector bottomPosition;
+
+ ladder->GetTopPosition( topPosition );
+ ladder->GetBottomPosition( bottomPosition );
+
+ // Compute parametric distance along ladder vector...
+ float oldt;
+ CalcDistanceSqrToLine( mv->GetAbsOrigin(), topPosition, bottomPosition, &oldt );
+
+ // Perform the move accounting for any base velocity.
+ VectorAdd (mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity);
+ TryPlayerMove();
+ VectorSubtract (mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity);
+
+ // Pressed buttons are "changed(xor)" and'ed with the mask of currently held buttons
+ int buttonsChanged = ( mv->m_nOldButtons ^ mv->m_nButtons ); // These buttons have changed this frame
+ int buttonsPressed = buttonsChanged & mv->m_nButtons;
+ bool pressed_use = ( buttonsPressed & IN_USE ) ? true : false;
+ bool pressing_forward_or_side = mv->m_flForwardMove != 0.0f || mv->m_flSideMove != 0.0f;
+
+ Vector ladderVec = topPosition - bottomPosition;
+ float LadderLength = VectorNormalize( ladderVec );
+ // This test is not perfect by any means, but should help a bit
+ bool moving_along_ladder = false;
+ if ( pressing_forward_or_side )
+ {
+ float fwdDot = m_vecForward.Dot( ladderVec );
+ if ( fabs( fwdDot ) > 0.9f )
+ {
+ moving_along_ladder = true;
+ }
+ }
+
+ // Compute parametric distance along ladder vector...
+ float newt;
+ CalcDistanceSqrToLine( mv->GetAbsOrigin(), topPosition, bottomPosition, &newt );
+
+ // Fudge of 2 units
+ float tolerance = 1.0f / LadderLength;
+
+ bool wouldleaveladder = false;
+ // Moving pPast top or bottom?
+ if ( newt < -tolerance )
+ {
+ wouldleaveladder = newt < oldt;
+ }
+ else if ( newt > ( 1.0f + tolerance ) )
+ {
+ wouldleaveladder = newt > oldt;
+ }
+
+ // See if we are near the top or bottom but not moving
+ float dist1sqr, dist2sqr;
+
+ dist1sqr = ( topPosition - mv->GetAbsOrigin() ).LengthSqr();
+ dist2sqr = ( bottomPosition - mv->GetAbsOrigin() ).LengthSqr();
+
+ float dist = MIN( dist1sqr, dist2sqr );
+ bool neardismountnode = ( dist < 16.0f * 16.0f ) ? true : false;
+ float ladderUnitsPerTick = ( MAX_CLIMB_SPEED * gpGlobals->interval_per_tick );
+ bool neardismountnode2 = ( dist < ladderUnitsPerTick * ladderUnitsPerTick ) ? true : false;
+
+ // Really close to node, cvar is set, and pressing a key, then simulate a +USE
+ bool auto_dismount_use = ( neardismountnode2 &&
+ sv_autoladderdismount.GetBool() &&
+ pressing_forward_or_side &&
+ !moving_along_ladder );
+
+ bool fully_underwater = ( player->GetWaterLevel() == WL_Eyes ) ? true : false;
+
+ // If the user manually pressed use or we're simulating it, then use_dismount will occur
+ bool use_dismount = pressed_use || auto_dismount_use;
+
+ if ( fully_underwater && !use_dismount )
+ {
+ // If fully underwater, we require looking directly at a dismount node
+ /// to "float off" a ladder mid way...
+ if ( ExitLadderViaDismountNode( ladder, true ) )
+ {
+ // See if they +used a dismount point mid-span..
+ return;
+ }
+ }
+
+ // If the movement would leave the ladder and they're not automated or pressing use, disallow the movement
+ if ( !use_dismount )
+ {
+ if ( wouldleaveladder )
+ {
+ // Don't let them leave the ladder if they were on it
+ mv->m_vecVelocity = oldVelocity;
+ mv->SetAbsOrigin( oldOrigin );
+ }
+ return;
+ }
+
+ // If the move would not leave the ladder and we're near close to the end, then just accept the move
+ if ( !wouldleaveladder && !neardismountnode )
+ {
+ // Otherwise, if the move would leave the ladder, disallow it.
+ if ( pressed_use )
+ {
+ if ( ExitLadderViaDismountNode( ladder, false, IsX360() ) )
+ {
+ // See if they +used a dismount point mid-span..
+ return;
+ }
+
+ player->SetMoveType( MOVETYPE_WALK );
+ player->SetMoveCollide( MOVECOLLIDE_DEFAULT );
+ SetLadder( NULL );
+ GetHL2Player()->m_bPlayUseDenySound = false;
+
+ // Dismount with a bit of velocity in facing direction
+ VectorScale( m_vecForward, USE_DISMOUNT_SPEED, mv->m_vecVelocity );
+ mv->m_vecVelocity.z = 50;
+ }
+ return;
+ }
+
+ // Debounce the use key
+ if ( pressed_use )
+ {
+ SwallowUseKey();
+ }
+
+ // Try auto exit, if possible
+ if ( ExitLadderViaDismountNode( ladder, false, pressed_use ) )
+ {
+ return;
+ }
+
+ if ( wouldleaveladder )
+ {
+ // Otherwise, if the move would leave the ladder, disallow it.
+ if ( pressed_use )
+ {
+ player->SetMoveType( MOVETYPE_WALK );
+ player->SetMoveCollide( MOVECOLLIDE_DEFAULT );
+ SetLadder( NULL );
+
+ // Dismount with a bit of velocity in facing direction
+ VectorScale( m_vecForward, USE_DISMOUNT_SPEED, mv->m_vecVelocity );
+ mv->m_vecVelocity.z = 50;
+ }
+ else
+ {
+ mv->m_vecVelocity = oldVelocity;
+ mv->SetAbsOrigin( oldOrigin );
+ }
+ }
+#endif
+}
+
+bool CHL2GameMovement::CheckLadderAutoMountEndPoint( CFuncLadder *ladder, const Vector& bestOrigin )
+{
+ // See if we're really near an endpoint
+ if ( !ladder )
+ return false;
+
+ Vector top, bottom;
+ ladder->GetTopPosition( top );
+ ladder->GetBottomPosition( bottom );
+
+ float d1, d2;
+
+ d1 = ( top - mv->GetAbsOrigin() ).LengthSqr();
+ d2 = ( bottom - mv->GetAbsOrigin() ).LengthSqr();
+
+ if ( d1 > 16 * 16 && d2 > 16 * 16 )
+ return false;
+
+ Vector ladderAxis;
+
+ if ( d1 < 16 * 16 )
+ {
+ // Close to top
+ ladderAxis = bottom - top;
+ }
+ else
+ {
+ ladderAxis = top - bottom;
+ }
+
+ VectorNormalize( ladderAxis );
+
+ if ( ladderAxis.Dot( m_vecForward ) > sv_ladderautomountdot.GetFloat() )
+ {
+ StartForcedMove( true, player->MaxSpeed(), bestOrigin, ladder );
+ return true;
+ }
+
+ return false;
+}
+
+bool CHL2GameMovement::CheckLadderAutoMountCone( CFuncLadder *ladder, const Vector& bestOrigin, float maxAngleDelta, float maxDistToLadder )
+{
+ // Never 'back' onto ladders or stafe onto ladders
+ if ( ladder != NULL &&
+ ( mv->m_flForwardMove > 0.0f ) )
+ {
+ Vector top, bottom;
+ ladder->GetTopPosition( top );
+ ladder->GetBottomPosition( bottom );
+
+ Vector ladderAxis = top - bottom;
+ VectorNormalize( ladderAxis );
+
+ Vector probe = mv->GetAbsOrigin();
+
+ Vector closest;
+ CalcClosestPointOnLineSegment( probe, bottom, top, closest, NULL );
+
+ Vector vecToLadder = closest - probe;
+
+ float dist = VectorNormalize( vecToLadder );
+
+ Vector flatLadder = vecToLadder;
+ flatLadder.z = 0.0f;
+ Vector flatForward = m_vecForward;
+ flatForward.z = 0.0f;
+
+ VectorNormalize( flatLadder );
+ VectorNormalize( flatForward );
+
+ float facingDot = flatForward.Dot( flatLadder );
+ float angle = acos( facingDot ) * 180 / M_PI;
+
+ bool closetoladder = ( dist != 0.0f && dist < maxDistToLadder ) ? true : false;
+ bool reallyclosetoladder = ( dist != 0.0f && dist < 4.0f ) ? true : false;
+
+ bool facingladderaxis = ( angle < maxAngleDelta ) ? true : false;
+ bool facingalongaxis = ( (float)fabs( ladderAxis.Dot( m_vecForward ) ) > sv_ladderautomountdot.GetFloat() ) ? true : false;
+#if 0
+ Msg( "close %i length %.3f maxdist %.3f facing %.3f dot %.3f ang %.3f\n",
+ closetoladder ? 1 : 0,
+ dist,
+ maxDistToLadder,
+ (float)fabs( ladderAxis.Dot( m_vecForward ) ),
+ facingDot,
+ angle);
+#endif
+
+ // Tracker 21776: Don't mount ladders this way if strafing
+ bool strafing = ( fabs( mv->m_flSideMove ) < 1.0f ) ? false : true;
+
+ if ( ( ( facingDot > 0.0f && !strafing ) || facingalongaxis ) &&
+ ( facingladderaxis || reallyclosetoladder ) &&
+ closetoladder )
+ {
+ StartForcedMove( true, player->MaxSpeed(), bestOrigin, ladder );
+ return true;
+ }
+ }
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Must be facing toward ladder
+// Input : *ladder -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool CHL2GameMovement::LookingAtLadder( CFuncLadder *ladder )
+{
+ if ( !ladder )
+ {
+ return false;
+ }
+
+ // Get ladder end points
+ Vector top, bottom;
+ ladder->GetTopPosition( top );
+ ladder->GetBottomPosition( bottom );
+
+ // Find closest point on ladder to player (could be an endpoint)
+ Vector closest;
+ CalcClosestPointOnLineSegment( mv->GetAbsOrigin(), bottom, top, closest, NULL );
+
+ // Flatten our view direction to 2D
+ Vector flatForward = m_vecForward;
+ flatForward.z = 0.0f;
+
+ // Because the ladder itself is not a solid, the player's origin may actually be
+ // permitted to pass it, and that will screw up our dot product.
+ // So back up the player's origin a bit to do the facing calculation.
+ Vector vecAdjustedOrigin = mv->GetAbsOrigin() - 8.0f * flatForward;
+
+ // Figure out vector from player to closest point on ladder
+ Vector vecToLadder = closest - vecAdjustedOrigin;
+
+ // Flatten it to 2D
+ Vector flatLadder = vecToLadder;
+ flatLadder.z = 0.0f;
+
+ // Normalize the vectors (unnecessary)
+ VectorNormalize( flatLadder );
+ VectorNormalize( flatForward );
+
+ // Compute dot product to see if forward is in same direction as vec to ladder
+ float facingDot = flatForward.Dot( flatLadder );
+
+ float requiredDot = ( sv_ladder_useonly.GetBool() ) ? -0.99 : 0.0;
+
+ // Facing same direction if dot > = requiredDot...
+ bool facingladder = ( facingDot >= requiredDot );
+
+ return facingladder;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : &trace -
+//-----------------------------------------------------------------------------
+bool CHL2GameMovement::CheckLadderAutoMount( CFuncLadder *ladder, const Vector& bestOrigin )
+{
+#if !defined( CLIENT_DLL )
+
+ if ( ladder != NULL )
+ {
+ StartForcedMove( true, player->MaxSpeed(), bestOrigin, ladder );
+ return true;
+ }
+
+#endif
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CHL2GameMovement::LadderMove( void )
+{
+
+ if ( player->GetMoveType() == MOVETYPE_NOCLIP )
+ {
+ SetLadder( NULL );
+ return false;
+ }
+
+ // If being forced to mount/dismount continue to act like we are on the ladder
+ if ( IsForceMoveActive() && ContinueForcedMove() )
+ {
+ return true;
+ }
+
+ CFuncLadder *bestLadder = NULL;
+ Vector bestOrigin( 0, 0, 0 );
+
+ CFuncLadder *ladder = GetLadder();
+
+ // Something 1) deactivated the ladder... or 2) something external applied
+ // a force to us. In either case make the player fall, etc.
+ if ( ladder &&
+ ( !ladder->IsEnabled() ||
+ ( player->GetBaseVelocity().LengthSqr() > 1.0f ) ) )
+ {
+ GetHL2Player()->ExitLadder();
+ ladder = NULL;
+ }
+
+ if ( !ladder )
+ {
+ Findladder( 64.0f, &bestLadder, bestOrigin, NULL );
+ }
+
+#if !defined (CLIENT_DLL)
+ if( !ladder && bestLadder && sv_ladder_useonly.GetBool() )
+ {
+ GetHL2Player()->DisplayLadderHudHint();
+ }
+#endif
+
+ int buttonsChanged = ( mv->m_nOldButtons ^ mv->m_nButtons ); // These buttons have changed this frame
+ int buttonsPressed = buttonsChanged & mv->m_nButtons;
+ bool pressed_use = ( buttonsPressed & IN_USE ) ? true : false;
+
+ // If I'm already moving on a ladder, use the previous ladder direction
+ if ( !ladder && !pressed_use )
+ {
+ // If flying through air, allow mounting ladders if we are facing < 15 degress from the ladder and we are close
+ if ( !ladder && !sv_ladder_useonly.GetBool() )
+ {
+ // Tracker 6625: Don't need to be leaping to auto mount using this method...
+ // But if we are on the ground, then we must not be backing into the ladder (Tracker 12961)
+ bool onground = player->GetGroundEntity() ? true : false;
+ if ( !onground || ( mv->m_flForwardMove > 0.0f ) )
+ {
+ if ( CheckLadderAutoMountCone( bestLadder, bestOrigin, 15.0f, 32.0f ) )
+ {
+ return true;
+ }
+ }
+
+ // Pressing forward while looking at ladder and standing (or floating) near a mounting point
+ if ( mv->m_flForwardMove > 0.0f )
+ {
+ if ( CheckLadderAutoMountEndPoint( bestLadder, bestOrigin ) )
+ {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ if ( !ladder &&
+ LookingAtLadder( bestLadder ) &&
+ CheckLadderAutoMount( bestLadder, bestOrigin ) )
+ {
+ return true;
+ }
+
+ // Reassign the ladder
+ ladder = GetLadder();
+ if ( !ladder )
+ {
+ return false;
+ }
+
+ // Don't play the deny sound
+ if ( pressed_use )
+ {
+ GetHL2Player()->m_bPlayUseDenySound = false;
+ }
+
+ // Make sure we are on the ladder
+ player->SetMoveType( MOVETYPE_LADDER );
+ player->SetMoveCollide( MOVECOLLIDE_DEFAULT );
+
+ player->SetGravity( 0.0f );
+
+ float forwardSpeed = 0.0f;
+ float rightSpeed = 0.0f;
+
+ float speed = player->MaxSpeed();
+
+
+ if ( mv->m_nButtons & IN_BACK )
+ {
+ forwardSpeed -= speed;
+ }
+
+ if ( mv->m_nButtons & IN_FORWARD )
+ {
+ forwardSpeed += speed;
+ }
+
+ if ( mv->m_nButtons & IN_MOVELEFT )
+ {
+ rightSpeed -= speed;
+ }
+
+ if ( mv->m_nButtons & IN_MOVERIGHT )
+ {
+ rightSpeed += speed;
+ }
+
+ if ( mv->m_nButtons & IN_JUMP )
+ {
+ player->SetMoveType( MOVETYPE_WALK );
+ // Remove from ladder
+ SetLadder( NULL );
+
+ // Jump in view direction
+ Vector jumpDir = m_vecForward;
+
+ // unless pressing backward or something like that
+ if ( mv->m_flForwardMove < 0.0f )
+ {
+ jumpDir = -jumpDir;
+ }
+
+ VectorNormalize( jumpDir );
+
+ VectorScale( jumpDir, MAX_CLIMB_SPEED, mv->m_vecVelocity );
+ // Tracker 13558: Don't add any extra z velocity if facing downward at all
+ if ( m_vecForward.z >= 0.0f )
+ {
+ mv->m_vecVelocity.z = mv->m_vecVelocity.z + 50;
+ }
+ return false;
+ }
+
+ if ( forwardSpeed != 0 || rightSpeed != 0 )
+ {
+ // See if the player is looking toward the top or the bottom
+ Vector velocity;
+
+ VectorScale( m_vecForward, forwardSpeed, velocity );
+ VectorMA( velocity, rightSpeed, m_vecRight, velocity );
+
+ VectorNormalize( velocity );
+
+ Vector ladderUp;
+ ladder->ComputeLadderDir( ladderUp );
+ VectorNormalize( ladderUp );
+
+ Vector topPosition;
+ Vector bottomPosition;
+
+ ladder->GetTopPosition( topPosition );
+ ladder->GetBottomPosition( bottomPosition );
+
+ // Check to see if we've mounted the ladder in a bogus spot and, if so, just fall off the ladder...
+ float dummyt = 0.0f;
+ float distFromLadderSqr = CalcDistanceSqrToLine( mv->GetAbsOrigin(), topPosition, bottomPosition, &dummyt );
+ if ( distFromLadderSqr > 36.0f )
+ {
+ // Uh oh, we fell off zee ladder...
+ player->SetMoveType( MOVETYPE_WALK );
+ // Remove from ladder
+ SetLadder( NULL );
+ return false;
+ }
+
+ bool ishorizontal = fabs( topPosition.z - bottomPosition.z ) < 64.0f ? true : false;
+
+ float changeover = ishorizontal ? 0.0f : 0.3f;
+
+ float factor = 1.0f;
+ if ( velocity.z >= 0 )
+ {
+ float dotTop = ladderUp.Dot( velocity );
+ if ( dotTop < -changeover )
+ {
+ // Aimed at bottom
+ factor = -1.0f;
+ }
+ }
+ else
+ {
+ float dotBottom = -ladderUp.Dot( velocity );
+ if ( dotBottom > changeover )
+ {
+ factor = -1.0f;
+ }
+ }
+
+#ifdef _XBOX
+ if( sv_ladders_useonly.GetBool() )
+ {
+ // Stick up climbs up, stick down climbs down. No matter which way you're looking.
+ if ( mv->m_nButtons & IN_FORWARD )
+ {
+ factor = 1.0f;
+ }
+ else if( mv->m_nButtons & IN_BACK )
+ {
+ factor = -1.0f;
+ }
+ }
+#endif//_XBOX
+
+ mv->m_vecVelocity = MAX_CLIMB_SPEED * factor * ladderUp;
+ }
+ else
+ {
+ mv->m_vecVelocity.Init();
+ }
+
+ return true;
+}
+
+void CHL2GameMovement::SetGroundEntity( trace_t *pm )
+{
+ CBaseEntity *newGround = pm ? pm->m_pEnt : NULL;
+
+ //Adrian: Special case for combine balls.
+ if ( newGround && newGround->GetCollisionGroup() == HL2COLLISION_GROUP_COMBINE_BALL_NPC )
+ {
+ return;
+ }
+
+ BaseClass::SetGroundEntity( pm );
+}
+
+bool CHL2GameMovement::CanAccelerate()
+{
+#ifdef HL2MP
+ if ( player->IsObserver() )
+ {
+ return true;
+ }
+#endif
+
+ BaseClass::CanAccelerate();
+
+ return true;
+}
+
+
+#ifndef PORTAL // Portal inherits from this but needs to declare it's own global interface
+ // Expose our interface.
+ static CHL2GameMovement g_GameMovement;
+ IGameMovement *g_pGameMovement = ( IGameMovement * )&g_GameMovement;
+
+ EXPOSE_SINGLE_INTERFACE_GLOBALVAR(CGameMovement, IGameMovement,INTERFACENAME_GAMEMOVEMENT, g_GameMovement );
+#endif \ No newline at end of file