diff options
Diffstat (limited to 'game/shared/hl2/hl_gamemovement.cpp')
| -rw-r--r-- | game/shared/hl2/hl_gamemovement.cpp | 1152 |
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 |