diff options
Diffstat (limited to 'game/shared/cstrike/cs_gamemovement.cpp')
| -rw-r--r-- | game/shared/cstrike/cs_gamemovement.cpp | 1144 |
1 files changed, 1144 insertions, 0 deletions
diff --git a/game/shared/cstrike/cs_gamemovement.cpp b/game/shared/cstrike/cs_gamemovement.cpp new file mode 100644 index 0000000..fa5c52a --- /dev/null +++ b/game/shared/cstrike/cs_gamemovement.cpp @@ -0,0 +1,1144 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include "cbase.h" +#include "gamemovement.h" +#include "cs_gamerules.h" +#include "in_buttons.h" +#include "movevars_shared.h" +#include "weapon_csbase.h" + +#ifdef CLIENT_DLL + #include "c_cs_player.h" +#else + #include "cs_player.h" + #include "KeyValues.h" +#endif + +#define STAMINA_MAX 100.0 +#define STAMINA_COST_JUMP 25.0 +#define STAMINA_COST_FALL 20.0 +#define STAMINA_RECOVER_RATE 19.0 + +extern bool g_bMovementOptimizations; + +ConVar sv_timebetweenducks( "sv_timebetweenducks", "0", FCVAR_REPLICATED, "Minimum time before recognizing consecutive duck key", true, 0.0, true, 2.0 ); +ConVar sv_enableboost( "sv_enableboost", "0", FCVAR_REPLICATED | FCVAR_NOTIFY, "Allow boost exploits"); + + +class CCSGameMovement : public CGameMovement +{ +public: + DECLARE_CLASS( CCSGameMovement, CGameMovement ); + + CCSGameMovement(); + + virtual void ProcessMovement( CBasePlayer *pPlayer, CMoveData *pMove ); + virtual bool CanAccelerate(); + virtual bool CheckJumpButton( void ); + virtual void PreventBunnyJumping( void ); + virtual void ReduceTimers( void ); + virtual void WalkMove( void ); + virtual void AirMove( void ); + virtual bool LadderMove( void ); + virtual void DecayPunchAngle( void ); + virtual void CheckParameters( void ); + + // allow overridden versions to respond to jumping + virtual void OnJump( float fImpulse ); + virtual void OnLand( float fVelocity ); + + // Ducking + virtual void Duck( void ); + virtual void FinishUnDuck( void ); + virtual void FinishDuck( void ); + virtual bool CanUnduck(); + virtual void HandleDuckingSpeedCrop(); + + + virtual bool OnLadder( trace_t &trace ); + virtual float LadderDistance( void ) const + { + if ( player->GetMoveType() == MOVETYPE_LADDER ) + return 10.0f; + return 2.0f; + } + + virtual unsigned int LadderMask( void ) const + { + return MASK_PLAYERSOLID & ( ~CONTENTS_PLAYERCLIP ); + } + + virtual float ClimbSpeed( void ) const; + virtual float LadderLateralMultiplier( void ) const; + + virtual void TryTouchGround( const Vector& start, const Vector& end, const Vector& mins, const Vector& maxs, unsigned int fMask, int collisionGroup, trace_t& pm ); + + +protected: + virtual void PlayerMove(); + + void CheckForLadders( bool wasOnGround ); + virtual unsigned int PlayerSolidMask( bool brushOnly = false ); ///< returns the solid mask for the given player, so bots can have a more-restrictive set + + float m_fTimeLastUnducked; + +public: + CCSPlayer *m_pCSPlayer; +}; + +//----------------------------------------------------------------------------- +// Purpose: used by the TryTouchGround function to exclude non-standables from +// consideration +//----------------------------------------------------------------------------- + +bool CheckForStandable( IHandleEntity *pHandleEntity, int contentsMask ) +{ + CBaseEntity *pEntity = EntityFromEntityHandle( pHandleEntity ); + + if ( !pEntity ) + return false; + + return ( pEntity->IsPlayer() && pEntity->GetGroundEntity() != NULL ) || pEntity->IsStandable(); +} + + +// Expose our interface. +static CCSGameMovement g_GameMovement; +IGameMovement *g_pGameMovement = ( IGameMovement * )&g_GameMovement; + +EXPOSE_SINGLE_INTERFACE_GLOBALVAR(CGameMovement, IGameMovement,INTERFACENAME_GAMEMOVEMENT, g_GameMovement ); + + +// ---------------------------------------------------------------------------------------- // +// CCSGameMovement. +// ---------------------------------------------------------------------------------------- // + +CCSGameMovement::CCSGameMovement() +{ + m_fTimeLastUnducked = 0.0f; +} + +//----------------------------------------------------------------------------- +// Purpose: Allow bots etc to use slightly different solid masks +//----------------------------------------------------------------------------- +unsigned int CCSGameMovement::PlayerSolidMask( bool brushOnly ) +{ + bool isBot = !player || player->IsBot(); + + if ( brushOnly ) + { + if ( isBot ) + { + return MASK_PLAYERSOLID_BRUSHONLY | CONTENTS_MONSTERCLIP; + } + else + { + return MASK_PLAYERSOLID_BRUSHONLY; + } + } + else + { + if ( isBot ) + { + return MASK_PLAYERSOLID | CONTENTS_MONSTERCLIP; + } + else + { + return MASK_PLAYERSOLID; + } + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CCSGameMovement::CheckParameters( void ) +{ + QAngle v_angle; + + // maintaining auto-duck during jumps + if ( m_pCSPlayer->m_duckUntilOnGround ) + { + if ( !player->GetGroundEntity() ) + { + if ( mv->m_nButtons & IN_DUCK ) + { + m_pCSPlayer->m_duckUntilOnGround = false; // player hit +duck, so cancel our auto duck + } + else + { + mv->m_nButtons |= IN_DUCK; + } + } + } + + // it would be nice to put this into the player->GetPlayerMaxSpeed() method, but + // this flag is only stored in the move! + if ( mv->m_nButtons & IN_SPEED ) + { + mv->m_flMaxSpeed *= CS_PLAYER_SPEED_WALK_MODIFIER; + } + + if ( player->GetMoveType() != MOVETYPE_ISOMETRIC && + player->GetMoveType() != MOVETYPE_NOCLIP && + player->GetMoveType() != MOVETYPE_OBSERVER ) + { + float spd; + + spd = ( mv->m_flForwardMove * mv->m_flForwardMove ) + + ( mv->m_flSideMove * mv->m_flSideMove ) + + ( mv->m_flUpMove * mv->m_flUpMove ); + + + // Slow down by the speed factor + float flSpeedFactor = 1.0f; + if (player->m_pSurfaceData) + { + flSpeedFactor = player->m_pSurfaceData->game.maxSpeedFactor; + } + + // If we have a constraint, slow down because of that too. + float flConstraintSpeedFactor = ComputeConstraintSpeedFactor(); + if (flConstraintSpeedFactor < flSpeedFactor) + flSpeedFactor = flConstraintSpeedFactor; + + // Take the player's velocity modifier into account + if ( FBitSet( m_pCSPlayer->GetFlags(), FL_ONGROUND ) ) + { + flSpeedFactor *= m_pCSPlayer->m_flVelocityModifier; + } + + + mv->m_flMaxSpeed *= flSpeedFactor; + + if ( g_bMovementOptimizations ) + { + // Same thing but only do the sqrt if we have to. + if ( ( spd != 0.0 ) && ( spd > mv->m_flMaxSpeed*mv->m_flMaxSpeed ) ) + { + float fRatio = mv->m_flMaxSpeed / sqrt( spd ); + mv->m_flForwardMove *= fRatio; + mv->m_flSideMove *= fRatio; + mv->m_flUpMove *= fRatio; + } + } + else + { + spd = sqrt( spd ); + if ( ( spd != 0.0 ) && ( spd > mv->m_flMaxSpeed ) ) + { + float fRatio = mv->m_flMaxSpeed / spd; + mv->m_flForwardMove *= fRatio; + mv->m_flSideMove *= fRatio; + mv->m_flUpMove *= fRatio; + } + } + } + + + if ( player->GetFlags() & FL_FROZEN || + player->GetFlags() & FL_ONTRAIN || + IsDead() ) + { + mv->m_flForwardMove = 0; + mv->m_flSideMove = 0; + mv->m_flUpMove = 0; + } + + DecayPunchAngle(); + + // Take angles from command. + if ( !IsDead() ) + { + v_angle = mv->m_vecAngles; + v_angle = v_angle + player->m_Local.m_vecPunchAngle; + + // Now adjust roll angle + if ( player->GetMoveType() != MOVETYPE_ISOMETRIC && + player->GetMoveType() != MOVETYPE_NOCLIP ) + { + mv->m_vecAngles[ROLL] = CalcRoll( v_angle, mv->m_vecVelocity, sv_rollangle.GetFloat(), sv_rollspeed.GetFloat() ); + } + else + { + mv->m_vecAngles[ROLL] = 0.0; // v_angle[ ROLL ]; + } + mv->m_vecAngles[PITCH] = v_angle[PITCH]; + mv->m_vecAngles[YAW] = v_angle[YAW]; + } + else + { + mv->m_vecAngles = mv->m_vecOldAngles; + } + + // Set dead player view_offset + if ( IsDead() ) + { + player->SetViewOffset( VEC_DEAD_VIEWHEIGHT ); + } + + // Adjust client view angles to match values used on server. + if ( mv->m_vecAngles[YAW] > 180.0f ) + { + mv->m_vecAngles[YAW] -= 360.0f; + } + + // If we're standing on a player, then force them off. + if ( !player->IsObserver() && ( player->GetMoveType() != MOVETYPE_LADDER ) ) + { + int nLevels = 0; + CBaseEntity *pCurGround = player->GetGroundEntity(); + while ( pCurGround && pCurGround->IsPlayer() && nLevels < 1000 ) + { + pCurGround = pCurGround->GetGroundEntity(); + ++nLevels; + } + if ( nLevels == 1000 ) + Warning( "BUG: CCSGameMovement::CheckParameters - too many stacking levels.\n" ); + + // If they're stacked too many levels deep, slide them off. + if ( nLevels > 1 ) + { + mv->m_flForwardMove = mv->m_flMaxSpeed * 3; + mv->m_flSideMove = 0; + mv->m_nButtons = 0; + mv->m_nImpulseCommand = 0; + } + } +} + + +void CCSGameMovement::ProcessMovement( CBasePlayer *pBasePlayer, CMoveData *pMove ) +{ + m_pCSPlayer = ToCSPlayer( pBasePlayer ); + Assert( m_pCSPlayer ); + + BaseClass::ProcessMovement( pBasePlayer, pMove ); +} + + +bool CCSGameMovement::CanAccelerate() +{ + // Only allow the player to accelerate when in certain states. + CSPlayerState curState = m_pCSPlayer->State_Get(); + if ( curState == STATE_ACTIVE ) + { + return player->GetWaterJumpTime() == 0; + } + else if ( player->IsObserver() ) + { + return true; + } + else + { + return false; + } +} + + +void CCSGameMovement::PlayerMove() +{ + if ( !m_pCSPlayer->CanMove() ) + { + mv->m_flForwardMove = 0; + mv->m_flSideMove = 0; + mv->m_flUpMove = 0; + mv->m_nButtons &= ~(IN_JUMP | IN_FORWARD | IN_BACK | IN_MOVELEFT | IN_MOVERIGHT); + } + + BaseClass::PlayerMove(); + + if ( FBitSet( m_pCSPlayer->GetFlags(), FL_ONGROUND ) ) + { + if ( m_pCSPlayer->m_flVelocityModifier < 1.0 ) + { + m_pCSPlayer->m_flVelocityModifier += gpGlobals->frametime / 3.0f; + } + + if ( m_pCSPlayer->m_flVelocityModifier > 1.0 ) + m_pCSPlayer->m_flVelocityModifier = 1.0; + } + +#if !defined(CLIENT_DLL) + if ( m_pCSPlayer->IsAlive() ) + { + // Check if our eye height is too close to the ceiling and lower it. + // This is needed because we have taller models with the old collision bounds. + + const float eyeClearance = 12.0f; // eye pos must be this far below the ceiling + + Vector offset = player->GetViewOffset(); + + Vector vHullMin = GetPlayerMins( player->m_Local.m_bDucked ); + vHullMin.z = 0.0f; + Vector vHullMax = GetPlayerMaxs( player->m_Local.m_bDucked ); + + Vector start = player->GetAbsOrigin(); + start.z += vHullMax.z; + Vector end = start; + end.z += eyeClearance - vHullMax.z; + end.z += player->m_Local.m_bDucked ? VEC_DUCK_VIEW_SCALED( player ).z : VEC_VIEW_SCALED( player ).z; + + vHullMax.z = 0.0f; + + Vector fudge( 1, 1, 0 ); + vHullMin += fudge; + vHullMax -= fudge; + + trace_t trace; + Ray_t ray; + ray.Init( start, end, vHullMin, vHullMax ); + UTIL_TraceRay( ray, PlayerSolidMask(), mv->m_nPlayerHandle.Get(), COLLISION_GROUP_PLAYER_MOVEMENT, &trace ); + + if ( trace.fraction < 1.0f ) + { + float est = start.z + trace.fraction * (end.z - start.z) - player->GetAbsOrigin().z - eyeClearance; + if ( ( player->GetFlags() & FL_DUCKING ) == 0 && !player->m_Local.m_bDucking && !player->m_Local.m_bDucked ) + { + offset.z = est; + } + else + { + offset.z = MIN( est, offset.z ); + } + player->SetViewOffset( offset ); + } + else + { + if ( ( player->GetFlags() & FL_DUCKING ) == 0 && !player->m_Local.m_bDucking && !player->m_Local.m_bDucked ) + { + + player->SetViewOffset( VEC_VIEW_SCALED( player ) ); + } + else if ( m_pCSPlayer->m_duckUntilOnGround ) + { + // Duck Hull, but we're in the air. Calculate where the view would be. + Vector hullSizeNormal = VEC_HULL_MAX_SCALED( player ) - VEC_HULL_MIN_SCALED( player ); + Vector hullSizeCrouch = VEC_DUCK_HULL_MAX_SCALED( player ) - VEC_DUCK_HULL_MIN_SCALED( player ); + + // We've got the duck hull, pulled up to the top of where the player should be + Vector lowerClearance = hullSizeNormal - hullSizeCrouch; + Vector duckEyeHeight = GetPlayerViewOffset( false ) - lowerClearance; + player->SetViewOffset( duckEyeHeight ); + } + else if( player->m_Local.m_bDucked && !player->m_Local.m_bDucking ) + { + player->SetViewOffset( VEC_DUCK_VIEW ); + } + } + } +#endif +} + + +void CCSGameMovement::WalkMove( void ) +{ + if ( m_pCSPlayer->m_flStamina > 0 ) + { + float flRatio; + + flRatio = ( STAMINA_MAX - ( ( m_pCSPlayer->m_flStamina / 1000.0 ) * STAMINA_RECOVER_RATE ) ) / STAMINA_MAX; + + // This Goldsrc code was run with variable timesteps and it had framerate dependencies. + // People looking at Goldsrc for reference are usually + // (these days) measuring the stoppage at 60fps or greater, so we need + // to account for the fact that Goldsrc was applying more stopping power + // since it applied the slowdown across more frames. + float flReferenceFrametime = 1.0f / 70.0f; + float flFrametimeRatio = gpGlobals->frametime / flReferenceFrametime; + + flRatio = pow( flRatio, flFrametimeRatio ); + + mv->m_vecVelocity.x *= flRatio; + mv->m_vecVelocity.y *= flRatio; + } + + BaseClass::WalkMove(); + + CheckForLadders( player->GetGroundEntity() != NULL ); +} + + +//------------------------------------------------------------------------------------------------------------------------------- +void CCSGameMovement::AirMove( void ) +{ + BaseClass::AirMove(); + + CheckForLadders( false ); +} + + +//------------------------------------------------------------------------------------------------------------------------------- +bool CCSGameMovement::OnLadder( trace_t &trace ) +{ + if ( trace.plane.normal.z == 1.0f ) + return false; + + return BaseClass::OnLadder( trace ); +} + + +//------------------------------------------------------------------------------------------------------------------------------- +bool CCSGameMovement::LadderMove( void ) +{ + bool isOnLadder = BaseClass::LadderMove(); + if ( isOnLadder && m_pCSPlayer ) + { + m_pCSPlayer->SurpressLadderChecks( mv->GetAbsOrigin(), m_pCSPlayer->m_vecLadderNormal ); + } + + return isOnLadder; +} + + +//------------------------------------------------------------------------------------------------------------------------------- +/** + * In CS, crouching up ladders goes slowly and doesn't make a sound. + */ +float CCSGameMovement::ClimbSpeed( void ) const +{ + if ( mv->m_nButtons & IN_DUCK ) + { + return BaseClass::ClimbSpeed() * CS_PLAYER_SPEED_CLIMB_MODIFIER; + } + else + { + return BaseClass::ClimbSpeed(); + } +} + + +//------------------------------------------------------------------------------------------------------------------------------- +/** +* In CS, strafing on ladders goes slowly. +*/ +float CCSGameMovement::LadderLateralMultiplier( void ) const +{ + if ( mv->m_nButtons & IN_DUCK ) + { + return 1.0f; + } + else + { + return 0.5f; + } +} + + +//------------------------------------------------------------------------------------------------------------------------------- +/** + * Looks behind and beneath the player in the air, in case he skips out over the top of a ladder. If the + * trace hits a ladder, the player is snapped to the ladder. + */ +void CCSGameMovement::CheckForLadders( bool wasOnGround ) +{ + if ( !wasOnGround ) + { + // If we're higher than the last place we were on the ground, bail - obviously we're not dropping + // past a ladder we might want to grab. + if ( mv->GetAbsOrigin().z > m_pCSPlayer->m_lastStandingPos.z ) + return; + + Vector dir = -m_pCSPlayer->m_lastStandingPos + mv->GetAbsOrigin(); + if ( !dir.x && !dir.y ) + { + // If we're dropping straight down, we don't know which way to look for a ladder. Oh well. + return; + } + + dir.z = 0.0f; + float dist = dir.NormalizeInPlace(); + if ( dist > 64.0f ) + { + // Don't grab ladders too far behind us. + return; + } + + trace_t trace; + + TracePlayerBBox( + mv->GetAbsOrigin(), + m_pCSPlayer->m_lastStandingPos - dir*(5+dist), + (PlayerSolidMask() & (~CONTENTS_PLAYERCLIP)), COLLISION_GROUP_PLAYER_MOVEMENT, trace ); + + if ( trace.fraction != 1.0f && OnLadder( trace ) && trace.plane.normal.z != 1.0f ) + { + if ( m_pCSPlayer->CanGrabLadder( trace.endpos, trace.plane.normal ) ) + { + player->SetMoveType( MOVETYPE_LADDER ); + player->SetMoveCollide( MOVECOLLIDE_DEFAULT ); + + player->SetLadderNormal( trace.plane.normal ); + mv->m_vecVelocity.Init(); + + // The ladder check ignored playerclips, to fix a bug exposed by de_train, where a clipbrush is + // flush with a ladder. This causes the above tracehull to fail unless we ignore playerclips. + // However, we have to check for playerclips before we snap to that pos, so we don't warp a + // player into a clipbrush. + TracePlayerBBox( + mv->GetAbsOrigin(), + m_pCSPlayer->m_lastStandingPos - dir*(5+dist), + PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, trace ); + + mv->SetAbsOrigin( trace.endpos ); + } + } + } + else + { + m_pCSPlayer->m_lastStandingPos = mv->GetAbsOrigin(); + } +} + + +void CCSGameMovement::ReduceTimers( void ) +{ + float frame_msec = 1000.0f * gpGlobals->frametime; + + if ( m_pCSPlayer->m_flStamina > 0 ) + { + m_pCSPlayer->m_flStamina -= frame_msec; + + if ( m_pCSPlayer->m_flStamina < 0 ) + { + m_pCSPlayer->m_flStamina = 0; + } + } + + BaseClass::ReduceTimers(); +} + +ConVar sv_enablebunnyhopping( "sv_enablebunnyhopping", "0", FCVAR_REPLICATED | FCVAR_NOTIFY ); + +// Only allow bunny jumping up to 1.1x server / player maxspeed setting +#define BUNNYJUMP_MAX_SPEED_FACTOR 1.1f + +// taken from TF2 but changed BUNNYJUMP_MAX_SPEED_FACTOR from 1.1 to 1.0 +void CCSGameMovement::PreventBunnyJumping() +{ + // Speed at which bunny jumping is limited + float maxscaledspeed = BUNNYJUMP_MAX_SPEED_FACTOR * player->m_flMaxspeed; + if ( maxscaledspeed <= 0.0f ) + return; + + // Current player speed + float spd = mv->m_vecVelocity.Length(); + + if ( spd <= maxscaledspeed ) + return; + + // Apply this cropping fraction to velocity + float fraction = ( maxscaledspeed / spd ); + + mv->m_vecVelocity *= fraction; + + +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CCSGameMovement::CheckJumpButton( void ) +{ + if (m_pCSPlayer->pl.deadflag) + { + mv->m_nOldButtons |= IN_JUMP ; // don't jump again until released + return false; + } + + // See if we are waterjumping. If so, decrement count and return. + if (m_pCSPlayer->m_flWaterJumpTime) + { + m_pCSPlayer->m_flWaterJumpTime -= gpGlobals->frametime; + if (m_pCSPlayer->m_flWaterJumpTime < 0) + m_pCSPlayer->m_flWaterJumpTime = 0; + + return false; + } + + // If we are in the water most of the way... + if ( m_pCSPlayer->GetWaterLevel() >= 2 ) + { + // swimming, not jumping + SetGroundEntity( NULL ); + + if(m_pCSPlayer->GetWaterType() == CONTENTS_WATER) // We move up a certain amount + mv->m_vecVelocity[2] = 100; + else if (m_pCSPlayer->GetWaterType() == CONTENTS_SLIME) + mv->m_vecVelocity[2] = 80; + + // play swiming sound + if ( m_pCSPlayer->m_flSwimSoundTime <= 0 ) + { + // Don't play sound again for 1 second + m_pCSPlayer->m_flSwimSoundTime = 1000; + PlaySwimSound(); + } + + return false; + } + + // No more effect + if (m_pCSPlayer->GetGroundEntity() == NULL) + { + mv->m_nOldButtons |= IN_JUMP; + return false; // in air, so no effect + } + + if ( mv->m_nOldButtons & IN_JUMP ) + return false; // don't pogo stick + + if ( !sv_enablebunnyhopping.GetBool() ) + { + PreventBunnyJumping(); + } + + // In the air now. + SetGroundEntity( NULL ); + + m_pCSPlayer->PlayStepSound( (Vector &)mv->GetAbsOrigin(), player->m_pSurfaceData, 1.0, true ); + + //MoveHelper()->PlayerSetAnimation( PLAYER_JUMP ); + m_pCSPlayer->DoAnimationEvent( PLAYERANIMEVENT_JUMP ); + + float flGroundFactor = 1.0f; + if (player->m_pSurfaceData) + { + flGroundFactor = player->m_pSurfaceData->game.jumpFactor; + } + + // if we weren't ducking, bots and hostages do a crouchjump programatically + if ( (!player || player->IsBot()) && !(mv->m_nButtons & IN_DUCK) ) + { + m_pCSPlayer->m_duckUntilOnGround = true; + FinishDuck(); + } + + // Acclerate upward + // If we are ducking... + float startz = mv->m_vecVelocity[2]; + if ( m_pCSPlayer->m_duckUntilOnGround || ( m_pCSPlayer->m_Local.m_bDucking ) || ( m_pCSPlayer->GetFlags() & FL_DUCKING ) ) + { + // d = 0.5 * g * t^2 - distance traveled with linear accel + // t = sqrt(2.0 * 45 / g) - how long to fall 45 units + // v = g * t - velocity at the end (just invert it to jump up that high) + // v = g * sqrt(2.0 * 45 / g ) + // v^2 = g * g * 2.0 * 45 / g + // v = sqrt( g * 2.0 * 45 ) + + mv->m_vecVelocity[2] = flGroundFactor * sqrt(2 * 800 * 57.0); // 2 * gravity * height + } + else + { + mv->m_vecVelocity[2] += flGroundFactor * sqrt(2 * 800 * 57.0); // 2 * gravity * height + } + + if ( m_pCSPlayer->m_flStamina > 0 ) + { + float flRatio; + + flRatio = ( STAMINA_MAX - ( ( m_pCSPlayer->m_flStamina / 1000.0 ) * STAMINA_RECOVER_RATE ) ) / STAMINA_MAX; + + mv->m_vecVelocity[2] *= flRatio; + } + + m_pCSPlayer->m_flStamina = ( STAMINA_COST_JUMP / STAMINA_RECOVER_RATE ) * 1000.0; + + FinishGravity(); + + mv->m_outWishVel.z += mv->m_vecVelocity[2] - startz; + mv->m_outStepHeight += 0.1f; + + OnJump(mv->m_outWishVel.z); + +#ifndef CLIENT_DLL + // allow bots to react + IGameEvent * event = gameeventmanager->CreateEvent( "player_jump" ); + if ( event ) + { + event->SetInt( "userid", m_pCSPlayer->GetUserID() ); + gameeventmanager->FireEvent( event ); + } +#endif + + // Flag that we jumped. + mv->m_nOldButtons |= IN_JUMP; // don't jump again until released + return true; +} + + +void CCSGameMovement::DecayPunchAngle( void ) +{ + float len; + + Vector vPunchAngle; + + vPunchAngle.x = m_pCSPlayer->m_Local.m_vecPunchAngle->x; + vPunchAngle.y = m_pCSPlayer->m_Local.m_vecPunchAngle->y; + vPunchAngle.z = m_pCSPlayer->m_Local.m_vecPunchAngle->z; + + len = VectorNormalize ( vPunchAngle ); + len -= (10.0 + len * 0.5) * gpGlobals->frametime; + len = MAX( len, 0.0 ); + VectorScale ( vPunchAngle, len, vPunchAngle ); + + m_pCSPlayer->m_Local.m_vecPunchAngle.Set( 0, vPunchAngle.x ); + m_pCSPlayer->m_Local.m_vecPunchAngle.Set( 1, vPunchAngle.y ); + m_pCSPlayer->m_Local.m_vecPunchAngle.Set( 2, vPunchAngle.z ); +} + + +void CCSGameMovement::HandleDuckingSpeedCrop() +{ + //============================================================================= + // HPE_BEGIN: + // [Forrest] + //============================================================================= + // Movement speed in free look camera mode is unaffected by ducking state. + if ( player->GetObserverMode() == OBS_MODE_ROAMING ) + return; + //============================================================================= + // HPE_END + //============================================================================= + + if ( !( m_iSpeedCropped & SPEED_CROPPED_DUCK ) ) + { + if ( ( mv->m_nButtons & IN_DUCK ) || ( player->m_Local.m_bDucking ) || ( player->GetFlags() & FL_DUCKING ) ) + { + mv->m_flForwardMove *= CS_PLAYER_SPEED_DUCK_MODIFIER; + mv->m_flSideMove *= CS_PLAYER_SPEED_DUCK_MODIFIER; + mv->m_flUpMove *= CS_PLAYER_SPEED_DUCK_MODIFIER; + m_iSpeedCropped |= SPEED_CROPPED_DUCK; + } + } +} + +bool CCSGameMovement::CanUnduck() +{ + trace_t trace; + Vector newOrigin; + + VectorCopy( mv->GetAbsOrigin(), newOrigin ); + + if ( player->GetGroundEntity() != NULL ) + { + newOrigin += VEC_DUCK_HULL_MIN_SCALED( player ) - VEC_HULL_MIN_SCALED( player ); + } + else + { + // If in air an letting go of croush, make sure we can offset origin to make + // up for uncrouching + Vector hullSizeNormal = VEC_HULL_MAX_SCALED( player ) - VEC_HULL_MIN_SCALED( player ); + Vector hullSizeCrouch = VEC_DUCK_HULL_MAX_SCALED( player ) - VEC_DUCK_HULL_MIN_SCALED( player ); + + newOrigin += -0.5f * ( hullSizeNormal - hullSizeCrouch ); + } + + UTIL_TraceHull( mv->GetAbsOrigin(), newOrigin, VEC_HULL_MIN_SCALED( player ), VEC_HULL_MAX_SCALED( player ), PlayerSolidMask(), player, COLLISION_GROUP_PLAYER_MOVEMENT, &trace ); + + if ( trace.startsolid || ( trace.fraction != 1.0f ) ) + return false; + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Stop ducking +//----------------------------------------------------------------------------- +void CCSGameMovement::FinishUnDuck( void ) +{ + trace_t trace; + Vector newOrigin; + + VectorCopy( mv->GetAbsOrigin(), newOrigin ); + + if ( player->GetGroundEntity() != NULL ) + { + newOrigin += VEC_DUCK_HULL_MIN_SCALED( player ) - VEC_HULL_MIN_SCALED( player ); + } + else + { + // If in air an letting go of croush, make sure we can offset origin to make + // up for uncrouching + Vector hullSizeNormal = VEC_HULL_MAX_SCALED( player ) - VEC_HULL_MIN_SCALED( player ); + Vector hullSizeCrouch = VEC_DUCK_HULL_MAX_SCALED( player ) - VEC_DUCK_HULL_MIN_SCALED( player ); + + Vector viewDelta = -0.5f * ( hullSizeNormal - hullSizeCrouch ); + + VectorAdd( newOrigin, viewDelta, newOrigin ); + } + + player->m_Local.m_bDucked = false; + player->RemoveFlag( FL_DUCKING ); + player->m_Local.m_bDucking = false; + player->SetViewOffset( GetPlayerViewOffset( false ) ); + player->m_Local.m_flDucktime = 0; + + mv->SetAbsOrigin( newOrigin ); + + // Recategorize position since ducking can change origin + CategorizePosition(); +} + +//----------------------------------------------------------------------------- +// Purpose: Finish ducking +//----------------------------------------------------------------------------- +void CCSGameMovement::FinishDuck( void ) +{ + + Vector hullSizeNormal = VEC_HULL_MAX_SCALED( player ) - VEC_HULL_MIN_SCALED( player ); + Vector hullSizeCrouch = VEC_DUCK_HULL_MAX_SCALED( player ) - VEC_DUCK_HULL_MIN_SCALED( player ); + + Vector viewDelta = 0.5f * ( hullSizeNormal - hullSizeCrouch ); + + player->SetViewOffset( GetPlayerViewOffset( true ) ); + player->AddFlag( FL_DUCKING ); + player->m_Local.m_bDucking = false; + + if ( !player->m_Local.m_bDucked ) + { + + Vector org = mv->GetAbsOrigin(); + + if ( player->GetGroundEntity() != NULL ) + { + org -= VEC_DUCK_HULL_MIN_SCALED( player ) - VEC_HULL_MIN_SCALED( player ); + } + else + { + org += viewDelta; + } + mv->SetAbsOrigin( org ); + + player->m_Local.m_bDucked = true; + } + + // See if we are stuck? + FixPlayerCrouchStuck( true ); + + // Recategorize position since ducking can change origin + CategorizePosition(); +} + +//----------------------------------------------------------------------------- +// Purpose: See if duck button is pressed and do the appropriate things +//----------------------------------------------------------------------------- +void CCSGameMovement::Duck( void ) +{ + + // Fix taken from zblock for rapid crouch/stand not showing stand on other clients + + if ( player->GetFlags() & FL_ONGROUND ) + { + // if prevent crouch + if ( !( mv->m_nButtons & IN_DUCK ) && ( mv->m_nOldButtons & IN_DUCK ) ) + { + // Player has released crouch and moving to standing + m_fTimeLastUnducked = gpGlobals->curtime; + } + else if ( ( mv->m_nButtons & IN_DUCK ) && !( mv->m_nOldButtons & IN_DUCK ) ) + { + // Crouch from standing + if ( ( player->GetFlags() & FL_DUCKING ) + && ( m_fTimeLastUnducked > (gpGlobals->curtime - sv_timebetweenducks.GetFloat() ) ) ) + { + // if the server thinks the player is still crouched + // AND the time the player started to stand (from being ducked) was less than 300ms ago + // prevent the player from ducking again + mv->m_nButtons &= ~IN_DUCK; + } + } + } + + int buttonsChanged = ( mv->m_nOldButtons ^ mv->m_nButtons ); // These buttons have changed this frame + int buttonsPressed = buttonsChanged & mv->m_nButtons; // The changed ones still down are "pressed" + int buttonsReleased = buttonsChanged & mv->m_nOldButtons; // The changed ones which were previously down are "released" + + // Check to see if we are in the air. + bool bInAir = player->GetGroundEntity() == NULL && player->GetMoveType() != MOVETYPE_LADDER; + + if ( mv->m_nButtons & IN_DUCK ) + { + mv->m_nOldButtons |= IN_DUCK; + } + else + { + mv->m_nOldButtons &= ~IN_DUCK; + } + + if ( IsDead() ) + { + // Unduck + if ( player->GetFlags() & FL_DUCKING ) + { + FinishUnDuck(); + } + return; + } + + HandleDuckingSpeedCrop(); + + if ( m_pCSPlayer->m_duckUntilOnGround ) + { + if ( !bInAir ) + { + m_pCSPlayer->m_duckUntilOnGround = false; + if ( CanUnduck() ) + { + FinishUnDuck(); + } + return; + } + else + { + if ( mv->m_vecVelocity.z > 0.0f ) + return; + + // Check if we can un-duck. We want to unduck if we have space for the standing hull, and + // if it is less than 2 inches off the ground. + trace_t trace; + Vector newOrigin; + Vector groundCheck; + + VectorCopy( mv->GetAbsOrigin(), newOrigin ); + Vector hullSizeNormal = VEC_HULL_MAX_SCALED( player ) - VEC_HULL_MIN_SCALED( player ); + Vector hullSizeCrouch = VEC_DUCK_HULL_MAX_SCALED( player ) - VEC_DUCK_HULL_MIN_SCALED( player ); + newOrigin -= ( hullSizeNormal - hullSizeCrouch ); + groundCheck = newOrigin; + groundCheck.z -= player->GetStepSize(); + + UTIL_TraceHull( newOrigin, groundCheck, VEC_HULL_MIN_SCALED( player ), VEC_HULL_MAX_SCALED( player ), PlayerSolidMask(), player, COLLISION_GROUP_PLAYER_MOVEMENT, &trace ); + + if ( trace.startsolid || trace.fraction == 1.0f ) + return; // Can't even stand up, or there's no ground underneath us + + m_pCSPlayer->m_duckUntilOnGround = false; + if ( CanUnduck() ) + { + FinishUnDuck(); + } + return; + } + } + + // Holding duck, in process of ducking or fully ducked? + if ( ( mv->m_nButtons & IN_DUCK ) || ( player->m_Local.m_bDucking ) || ( player->GetFlags() & FL_DUCKING ) ) + { + if ( mv->m_nButtons & IN_DUCK ) + { + bool alreadyDucked = ( player->GetFlags() & FL_DUCKING ) ? true : false; + + if ( (buttonsPressed & IN_DUCK ) && !( player->GetFlags() & FL_DUCKING ) ) + { + // Use 1 second so super long jump will work + player->m_Local.m_flDucktime = 1000; + player->m_Local.m_bDucking = true; + } + + float duckmilliseconds = MAX( 0.0f, 1000.0f - (float)player->m_Local.m_flDucktime ); + float duckseconds = duckmilliseconds / 1000.0f; + + //time = MAX( 0.0, ( 1.0 - (float)player->m_Local.m_flDucktime / 1000.0 ) ); + + if ( player->m_Local.m_bDucking ) + { + if ( !( player->GetFlags() & FL_ANIMDUCKING ) ) + player->AddFlag( FL_ANIMDUCKING ); + + // Finish ducking immediately if duck time is over or not on ground + if ( ( duckseconds > TIME_TO_DUCK ) || + ( player->GetGroundEntity() == NULL ) || + alreadyDucked) + { + FinishDuck(); + } + else + { + // Calc parametric time + float duckFraction = SimpleSpline( duckseconds / TIME_TO_DUCK ); + SetDuckedEyeOffset( duckFraction ); + } + } + } + else + { + // Try to unduck unless automovement is not allowed + // NOTE: When not onground, you can always unduck + if ( player->m_Local.m_bAllowAutoMovement || player->GetGroundEntity() == NULL ) + { + if ( (buttonsReleased & IN_DUCK ) && ( player->GetFlags() & FL_DUCKING ) ) + { + // Use 1 second so super long jump will work + player->m_Local.m_flDucktime = 1000; + player->m_Local.m_bDucking = true; // or unducking + } + + float duckmilliseconds = MAX( 0.0f, 1000.0f - (float)player->m_Local.m_flDucktime ); + float duckseconds = duckmilliseconds / 1000.0f; + + if ( CanUnduck() ) + { + if ( player->m_Local.m_bDucking || + player->m_Local.m_bDucked ) // or unducking + { + if ( player->GetFlags() & FL_ANIMDUCKING ) + player->RemoveFlag( FL_ANIMDUCKING ); + + // Finish ducking immediately if duck time is over or not on ground + if ( ( duckseconds > TIME_TO_UNDUCK ) || + ( player->GetGroundEntity() == NULL ) ) + { + FinishUnDuck(); + } + else + { + // Calc parametric time + float duckFraction = SimpleSpline( 1.0f - ( duckseconds / TIME_TO_UNDUCK ) ); + SetDuckedEyeOffset( duckFraction ); + } + } + } + else + { + // Still under something where we can't unduck, so make sure we reset this timer so + // that we'll unduck once we exit the tunnel, etc. + player->m_Local.m_flDucktime = 1000; + } + } + } + } +} + + +void CCSGameMovement::OnJump( float fImpulse ) +{ + m_pCSPlayer->OnJump( fImpulse ); +} + +void CCSGameMovement::OnLand( float fVelocity ) +{ + m_pCSPlayer->OnLand( fVelocity ); +} + +//----------------------------------------------------------------------------- +// Purpose: Essentially the same as TracePlayerBBox, but adds a callback to +// exclude entities that are not standable (except for other players) +//----------------------------------------------------------------------------- +void CCSGameMovement::TryTouchGround( const Vector& start, const Vector& end, const Vector& mins, const Vector& maxs, unsigned int fMask, int collisionGroup, trace_t& pm ) +{ + VPROF( "CCSGameMovement::TryTouchGround" ); + + Ray_t ray; + ray.Init( start, end, mins, maxs ); + + ShouldHitFunc_t pStandingTestCallback = sv_enableboost.GetBool() ? NULL : CheckForStandable; + + UTIL_TraceRay( ray, fMask, mv->m_nPlayerHandle.Get(), collisionGroup, &pm, pStandingTestCallback ); + +} |