You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1147 lines
32 KiB
1147 lines
32 KiB
//========= 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"); |
|
ConVar cs_autojump( "cs_autojump", "0", FCVAR_REPLICATED | FCVAR_NOTIFY ); |
|
|
|
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) && |
|
(!cs_autojump.GetBool() && m_pCSPlayer->GetGroundEntity()) ) |
|
{ |
|
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 ); |
|
|
|
}
|
|
|