//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "tf_gamemovement.h"
#include "in_buttons.h"
#include "tier0/vprof.h"
#include "SoundEmitterSystem/isoundemittersystembase.h"

#define	SPEED_STOP_THRESHOLD		1.0f
#define BUMP_MAX_COUNT				8

//#define IMPACT_NORMAL_FLOOR			0.35f
#define	IMPACT_NORMAL_FLOOR			0.7f
#define IMPACT_NORMAL_WALL			0.0f

enum
{
	MOVEMENT_BLOCKED_NONE  = 0x0,
	MOVEMENT_BLOCKED_WALL  = 0x1,
	MOVEMENT_BLOCKED_FLOOR = 0x2,
	MOVEMENT_BLOCKED_ALL   = 0x4
};

#define SPEED_CROP_FRACTION_WALKING		0.4f
#define SPEED_CROP_FRACTION_USING		0.3f
#define SPEED_CROP_FRACTION_DUCKING		0.3f

char    *va(char *format, ...);

extern bool g_bMovementOptimizations;

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFGameMovement::CategorizePosition( void )
{
	VPROF( "CTFGameMovement::CategorizePosition" );

	// Doing this before we move may introduce a potential latency in water detection, but
	// doing it after can get us stuck on the bottom in water if the amount we move up
	// is less than the 1 pixel 'threshold' we're about to snap to.	Also, we'll call
	// this several times per frame, so we really need to avoid sticking to the bottom of
	// water on each call, and the converse case will correct itself if called twice.
	CheckWater();

	trace_t trace;
	Vector vStart( mv->m_vecAbsOrigin.x, mv->m_vecAbsOrigin.y, mv->m_vecAbsOrigin.z );
	Vector vEnd( mv->m_vecAbsOrigin.x, mv->m_vecAbsOrigin.y, mv->m_vecAbsOrigin.z - 66 );

	// Assume we are not on the ground
	SetGroundEntity( NULL );
	m_pSurfaceData = NULL;
	m_surfaceFriction = 1.0f;
	m_chTextureType = 'C';

	// Check our velocity in z (are we shooting up really fast - then we are not on ground).
	if ( mv->m_vecVelocity.z <= 180.0f )
	{
		// Move downward.
		TracePlayerBBox( vStart, vEnd, MASK_PLAYERSOLID, COLLISION_GROUP_PLAYER_MOVEMENT, trace );


		// Check to see if we are on the ground
		if ( ( ( trace.plane.normal.z >= IMPACT_NORMAL_FLOOR ) || trace.IsDispSurfaceWalkable() ) && ( trace.fraction < 0.06f ) )
		{
			SetGroundEntity( &trace );
			VectorCopy( trace.plane.normal, m_vecGroundNormal );

			// Add standing on object to touch list
			if ( trace.DidHitNonWorldEntity() )
			{
				MoveHelper()->AddToTouched( trace, mv->m_vecVelocity );
			}

			// Reset water jumping.
			player->m_flWaterJumpTime = 0.0f;

			// If we could make the move, drop us down that 1 pixel
			if ( ( int )player->GetWaterLevel() < WL_Waist && !trace.startsolid && !trace.allsolid )
			{
				// check distance we would like to move -- this is supposed to just keep up
				// "on the ground" surface not stap us back to earth
				mv->m_vecAbsOrigin = vStart + trace.fraction * ( vEnd - vStart );
//				VectorCopy( trace.endpos, mv->m_vecAbsOrigin );
			}		
		}

		// NOTE: should this be surrounded by trace.fraction <= 1.0f ?????

		// Setup surface properties.
		{
			VPROF( "CTFGameMovement::CategorizePosition / Surface Props" );
			IPhysicsSurfaceProps *physprops = MoveHelper( )->GetSurfaceProps();
			m_surfaceProps = trace.surface.surfaceProps;
			m_pSurfaceData = physprops->GetSurfaceData( m_surfaceProps );
			physprops->GetPhysicsProperties( m_surfaceProps, NULL, NULL, &m_surfaceFriction, NULL );
		}

		// HACKHACK: Scale this to fudge the relationship between vphysics friction values and player friction values.
		// A value of 0.8f feels pretty normal for vphysics, whereas 1.0f is normal for players.
		// This scaling trivially makes them equivalent.  REVISIT if this affects low friction surfaces too much.
		m_surfaceFriction *= 1.25f;
		if ( m_surfaceFriction > 1.0f )
			m_surfaceFriction = 1.0f;
		m_chTextureType = m_pSurfaceData->game.material;
	}

	// If we are not on the ground...
	if ( player->GetGroundEntity() == NULL )
	{
		player->m_Local.m_flFallVelocity = -mv->m_vecVelocity.z;
	}

	// Store off the starting water level
	m_nOldWaterLevel = player->GetWaterLevel();
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFGameMovement::HandleLadder( void )
{
	// No ladder movement if the player is dead or on a train.
	if ( player->pl.deadflag || ( player->GetFlags() & FL_ONTRAIN ) )
		return;

	// Ladder initialization.
	m_nOnLadder = 0;

	if ( !BaseClass::LadderMove() && ( player->GetMoveType() == MOVETYPE_LADDER ) )
	{
		// clear ladder stuff unless player is noclipping or being lifted by barnacle. 
		// it will be set immediately again next frame if necessary
		player->SetMoveType( MOVETYPE_WALK );
		player->SetMoveCollide( MOVECOLLIDE_DEFAULT );
	}
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFGameMovement::SpeedCrop( void )
{
	// Verify speed hasn't been cropped already (shouldn't have!!!).
	if ( m_iSpeedCropped & SPEED_CROPPED_DUCK )
		return;

	// Set the speed cropped flag.
	m_iSpeedCropped		|= SPEED_CROPPED_DUCK;

	// If the "walking" key is pressed -- crop speed.
	if ( mv->m_nButtons & IN_SPEED )
	{
		mv->m_flForwardMove *= SPEED_CROP_FRACTION_WALKING;
		mv->m_flSideMove *= SPEED_CROP_FRACTION_WALKING;
		mv->m_flUpMove *= SPEED_CROP_FRACTION_WALKING;
	}
	// If the "use" key is pressed and the player is on the ground -- crop speed.
	else if ( ( mv->m_nButtons & IN_USE ) && ( player->GetGroundEntity() != NULL ) )
	{
		mv->m_flForwardMove *= SPEED_CROP_FRACTION_USING;
		mv->m_flSideMove *= SPEED_CROP_FRACTION_USING;
		mv->m_flUpMove *= SPEED_CROP_FRACTION_USING;
	}
	// If the player is "ducking" -- crop speed
	else if ( player->GetFlags() & FL_DUCKING )
	{
		mv->m_flForwardMove *= SPEED_CROP_FRACTION_DUCKING;
		mv->m_flSideMove *= SPEED_CROP_FRACTION_DUCKING;
		mv->m_flUpMove *= SPEED_CROP_FRACTION_DUCKING;
	}

	// Moving backwards happens more slowly
	float flAngle = atan2( mv->m_flSideMove, mv->m_flForwardMove ) / M_PI;
	flAngle = 2.0f * (fabs(flAngle) - 0.5f);
	flAngle = clamp( flAngle, 0.0f, 1.0f );
	float flFactor = 1.0f - fabs(flAngle) * (1.0f - sv_backspeed.GetFloat());

	mv->m_flForwardMove *= flFactor;
	mv->m_flSideMove *= flFactor;
}


//-----------------------------------------------------------------------------
// Figures out how the constraint should slow us down
//-----------------------------------------------------------------------------
float CTFGameMovement::ComputeConstraintSpeedFactor( void )
{
	// If we have a constraint, slow down because of that too...
	// Get the TF movement data.
	CTFMoveData *pTFMove = TFMove();
	if ( !pTFMove || pTFMove->m_flConstraintRadius == 0.0f )
		return 1.0f;

	float flDistSq = mv->m_vecAbsOrigin.DistToSqr( pTFMove->m_vecConstraintCenter );

	float flOuterRadiusSq = pTFMove->m_flConstraintRadius * pTFMove->m_flConstraintRadius;
	float flInnerRadiusSq = pTFMove->m_flConstraintRadius - pTFMove->m_flConstraintWidth;
	flInnerRadiusSq *= flInnerRadiusSq;

	// Only slow us down if we're inside the constraint ring
	if ((flDistSq <= flInnerRadiusSq) || (flDistSq >= flOuterRadiusSq))
		return 1.0f;

	// Only slow us down if we're running away from the center
	Vector vecDesired;
	VectorMultiply( m_vecForward, mv->m_flForwardMove, vecDesired );
	VectorMA( vecDesired, mv->m_flSideMove, m_vecRight, vecDesired );
	VectorMA( vecDesired, mv->m_flUpMove, m_vecUp, vecDesired );

	Vector vecDelta;
	VectorSubtract( mv->m_vecAbsOrigin, pTFMove->m_vecConstraintCenter, vecDelta );
	VectorNormalize( vecDelta );
	VectorNormalize( vecDesired );
	if (DotProduct( vecDelta, vecDesired ) < 0.0f)
		return 1.0f;

	float flFrac = (sqrt(flDistSq) - (pTFMove->m_flConstraintRadius - pTFMove->m_flConstraintWidth)) / pTFMove->m_flConstraintWidth;

	float flSpeedFactor = Lerp( flFrac, 1.0f, pTFMove->m_flConstraintSpeedFactor ); 
	return flSpeedFactor;
}


//-----------------------------------------------------------------------------
// Purpose: Called in PrePlayerMove(), this function clamps the player's overall
//          speed.  It is clamped to either the maximum client's or server's 
//          speed (whichever is lower).
//-----------------------------------------------------------------------------
void CTFGameMovement::SetupSpeed( void )
{
	// Reset the outgoing applied velocity.
	mv->m_outWishVel.Init();

	// Don't clamp speed if we are sin an "ISOMETRIC" or "NOCLIP" movetype.
	if ( ( player->GetMoveType() == MOVETYPE_ISOMETRIC ) ||
		 ( player->GetMoveType() == MOVETYPE_NOCLIP ) )
		 return;

	// Some events negate speed, check for these first.
	if ( ( player->GetFlags() & FL_FROZEN ) || ( player->GetFlags() & FL_ONTRAIN ) ||  IsDead() )
	{
		mv->m_flForwardMove = 0;
		mv->m_flSideMove = 0;
		mv->m_flUpMove = 0;
		return;
	}

	// Calculate the players max speed given movements.
	float flSpeed = ( mv->m_flForwardMove * mv->m_flForwardMove ) +
		            ( mv->m_flSideMove * mv->m_flSideMove ) +
			 	    ( mv->m_flUpMove * mv->m_flUpMove );
	if ( flSpeed == 0.0f )
		return;

	flSpeed = sqrt( flSpeed );

	// NOTE: The client max speed was set to the movement max speed (mv->m_flMaxSpeed)
	//       in the SetupMove, however the _ProcessMovement code post sets
	//       mv->m_flMaxSpeed to the maximum server speed (sv_maxspeed),
	//       thus, the need to the check.  If we forego the maximum server speed, 
	//       this code can be removed.
	if ( mv->m_flClientMaxSpeed )
	{
		mv->m_flMaxSpeed = MIN( mv->m_flClientMaxSpeed, mv->m_flMaxSpeed );
	}

	// Slow down by the speed factor
	float flSpeedFactor = 1.0f;
	if (m_pSurfaceData)
	{
		flSpeedFactor = m_pSurfaceData->game.maxSpeedFactor;
	}

	// If we have a constraint, slow down because of that too...
	// Get the TF movement data
	float flConstraintSpeedFactor = ComputeConstraintSpeedFactor();
	if (flConstraintSpeedFactor < flSpeedFactor)
		flSpeedFactor = flConstraintSpeedFactor;

	mv->m_flMaxSpeed *= flSpeedFactor;

	if ( flSpeed > mv->m_flMaxSpeed )
	{
		float flRatio = mv->m_flMaxSpeed / flSpeed;
		mv->m_flForwardMove *= flRatio;
		mv->m_flSideMove *= flRatio;
		mv->m_flUpMove *= flRatio;
	}

	// Crop speed if necessary.
	SpeedCrop();
}


//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFGameMovement::FinishUnDuck( void )
{
	int i;
	trace_t trace;
	Vector newOrigin;

	Vector vDuckHullMin = GetPlayerMins( true );
	Vector vStandHullMin = GetPlayerMins( false );

	VectorCopy( mv->m_vecAbsOrigin, newOrigin );

	if ( player->GetGroundEntity() != NULL )
	{
		for ( i = 0; i < 3; i++ )
		{
			newOrigin[i] += ( vDuckHullMin[i] - vStandHullMin[i] );
		}
	}
	else
	{
  		Vector viewDelta = player->GetViewOffset() - GetPlayerViewOffset( false );
		VectorAdd( newOrigin, viewDelta, newOrigin );
	}
	
	TracePlayerBBox( newOrigin, newOrigin, MASK_PLAYERSOLID, COLLISION_GROUP_PLAYER_MOVEMENT, trace );

	if ( !trace.startsolid )
	{
		player->m_Local.m_bDucked = false;

		// Oh, no, changing hulls stuck us into something, try unsticking downward first.
		TracePlayerBBox( newOrigin, newOrigin, MASK_PLAYERSOLID, COLLISION_GROUP_PLAYER_MOVEMENT, trace );
		if ( trace.startsolid )
		{
			// See if we are stuck?  If so, stay ducked with the duck hull until we have a clear spot
			player->m_Local.m_bDucked = true;
			return;
		}

		player->RemoveFlag( FL_DUCKING );
		player->m_Local.m_bDucking  = false;
		player->SetViewOffset( GetPlayerViewOffset( false ) );
		player->m_Local.m_flDucktime = 0;
		
		VectorCopy( newOrigin, mv->m_vecAbsOrigin );
	}
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFGameMovement::HandleDuck( void )
{
	// Store button presses and changes.
	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"

	if ( mv->m_nButtons & IN_DUCK )
	{
		mv->m_nOldButtons |= IN_DUCK;
	}
	else
	{
		mv->m_nOldButtons &= ~IN_DUCK;
	}

	// Handle the player dead case.
	if ( IsDead() && ( player->GetFlags() & FL_DUCKING ) )
	{
		FinishUnDuck();
		return;
	}

	if ( ( mv->m_nButtons & IN_DUCK ) || ( player->m_Local.m_bDucking ) || ( player->GetFlags() & FL_DUCKING ) )
	{
		// Remove all movement when ducking!
		mv->m_flForwardMove = 0.0f;
		mv->m_flSideMove = 0.0f;
		mv->m_flUpMove = 0.0f;

		if ( mv->m_nButtons & IN_DUCK )
		{
			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;
			
			if ( player->m_Local.m_bDucking )
			{
				// Finish ducking immediately if duck time is over or not on ground
				if ( ( duckseconds > TIME_TO_DUCK ) || 
					( player->GetGroundEntity() == NULL ) )
				{
					FinishDuck();
				}
				else
				{
					// Calc parametric time
					float duckFraction = SimpleSpline( duckseconds / TIME_TO_DUCK );
					SetDuckedEyeOffset( duckFraction );
				}
			}
		}
		else
		{
			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 ( player->m_Local.m_bDucking ) // or unducking
			{
				// 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 );
				}
			}
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFGameMovement::SetupViewAngles( void )
{
	// Cache the view angles.
	AngleVectors( mv->m_vecViewAngles, &m_vecForward, &m_vecRight, &m_vecUp );

	// Add a view punch if necessary.
	QAngle	v_angle = ( mv->m_vecViewAngles + player->m_Local.m_vecPunchAngle );
	
	// Adjust the view roll angle.
	if ( ( player->GetMoveType() != MOVETYPE_ISOMETRIC ) && ( player->GetMoveType() != MOVETYPE_NOCLIP ) )
	{
		mv->m_vecViewAngles[ROLL] = CalcRoll( v_angle, mv->m_vecVelocity, sv_rollangle.GetFloat(), sv_rollspeed.GetFloat() ) * 4.0f;
	}
	else
	{
		mv->m_vecViewAngles[ROLL] = 0.0f;
	}

	// Copy the yaw and pitch.
	// NOTE: Adjust the client view angles to match the values on the server.
	mv->m_vecViewAngles[PITCH] = v_angle[PITCH];
	mv->m_vecViewAngles[YAW] = v_angle[YAW];
	if ( mv->m_vecViewAngles[YAW] > 180.0f )
	{
		mv->m_vecViewAngles[YAW] -= 360.0f;
	}
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CTFGameMovement::CheckDeath( void )
{
	// If we are dead, setup the appropriate data
	if ( IsDead() )
	{
		mv->m_flForwardMove = 0.0f;
		mv->m_flSideMove = 0.0f;
		mv->m_flUpMove = 0.0f;

		VectorCopy( mv->m_vecOldAngles, mv->m_vecViewAngles );

		player->SetViewOffset( VEC_DEAD_VIEWHEIGHT_SCALED( this ) );

		return true;
	}

	return false;
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFGameMovement::UpdateTimers( void )
{
	BaseClass::ReduceTimers();
	BaseClass::DecayPunchAngle();
}


//-----------------------------------------------------------------------------
// Should the step sound play?
//-----------------------------------------------------------------------------
bool CTFGameMovement::ShouldPlayStepSound( surfacedata_t *psurface, float fvol )
{
	if ( !m_nOnLadder && Vector2DLength( mv->m_vecVelocity.AsVector2D() ) <= 100 )
		return false;

	return true;
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : step - 
//			fvol - 
//			force - force sound to play
//-----------------------------------------------------------------------------
void CTFGameMovement::PlayStepSound( surfacedata_t *psurface, float fvol, bool force )
{
	if ( gpGlobals->maxClients > 1 && !sv_footsteps.GetFloat() )
		return;

	if ( !psurface )
		return;

	if (!force && !ShouldPlayStepSound( psurface, fvol ))
		return;

// TODO:  See note above, should this be hooked up?
//	PlantFootprint( psurface );

	unsigned short stepSoundName = player->m_Local.m_nStepside ? psurface->sounds.stepleft : psurface->sounds.stepright;
	player->m_Local.m_nStepside = !player->m_Local.m_nStepside;

	if ( !stepSoundName )
		return;

	if ( !mv->m_bFirstRunOfFunctions )
		return;

	IPhysicsSurfaceProps *physprops = MoveHelper( )->GetSurfaceProps();
	const char *pSoundName = physprops->GetString( stepSoundName );
	char szSound[256];

	// Prepend our team's footsteps
	if ( player->GetTeamNumber() == TEAM_HUMANS )
	{
		Q_snprintf( szSound, sizeof(szSound), "Human.%s", pSoundName );
	}
	else if ( player->GetTeamNumber() == TEAM_ALIENS )
	{
		Q_snprintf( szSound, sizeof(szSound), "Alien.%s", pSoundName );
	}
	else
	{
		return;
	}

	CSoundParameters params;
	if ( !CBaseEntity::GetParametersForSound( szSound, params, NULL ) )
		return;

	MoveHelper( )->StartSound( mv->m_vecAbsOrigin, CHAN_BODY, params.soundname, fvol, params.soundlevel, 0, params.pitch );
}	

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
int CTFGameMovement::CheckStuck( void )
{
	VPROF( "CTFGameMovement::CheckStuck" );

	// NOTE: I am not bothering much with this as I am going to 
	//       implement an new UnSticking process.
	if ( ( player->GetMoveType() == MOVETYPE_NOCLIP ) ||
         ( player->GetMoveType() == MOVETYPE_NONE ) ||
		 ( player->GetMoveType() == MOVETYPE_ISOMETRIC ) )
		 return 0;

	return BaseClass::CheckStuck();
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CTFGameMovement::PrePlayerMove( void )
{
	VPROF( "CTFGameMovement::PrePlayerMove" );

	// Assume we don't touch anything (Reset the touch list).
	MoveHelper()->ResetTouchList();

	// Check to see if we are stuck.
	if ( CheckStuck() )
		return false;

	// Update (reduce) movement timers.
	UpdateTimers();

	// Check to see if the player is dead and setup death data, otherwise setup 
	// the players view angles.
	if ( !CheckDeath() )
	{
		SetupViewAngles();
	}

	// Handle ducking.
	HandleDuck();

	// Handle ladder.
	HandleLadder();

	// Categorize the player's position.
	CategorizePosition();

	// Calculate the player's movement speed (has to happen after categorize position)
	SetupSpeed();

	// Update our stepping sound (based on the player's location).
	player->UpdateStepSound( m_pSurfaceData, mv->m_vecAbsOrigin, mv->m_vecVelocity );

	return true;
}


//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFGameMovement::PostPlayerMove( void )
{
}


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTFGameMovement::HandlePlayerMove( void )
{
	// Handle movement.
	switch ( player->GetMoveType() )
	{
		case MOVETYPE_NONE:
			{
				break;
			}
		case MOVETYPE_NOCLIP:
			{
				FullNoClipMove( sv_noclipspeed.GetFloat(), sv_noclipaccelerate.GetFloat() );
				break;
			}
		case MOVETYPE_FLY:
		case MOVETYPE_FLYGRAVITY:
			{
				FullTossMove();
				break;
			}

		case MOVETYPE_LADDER:
			{
				FullLadderMove(); 
				break;
			}

		case MOVETYPE_WALK:
			{
				// This should be moved elsewhere!!!  Just get it going for now.
				CTFMoveData *pTFMove = TFMove();
				Vector vecPlayerOrigin( mv->m_vecAbsOrigin.x, mv->m_vecAbsOrigin.y, mv->m_vecAbsOrigin.z );
				FullWalkMove();
				Vector vecPlayerDelta;
				VectorSubtract( mv->m_vecAbsOrigin, vecPlayerOrigin, vecPlayerDelta );

				if ( ( fabs( vecPlayerDelta.x ) > 0.0001f ) || 
					 ( fabs( vecPlayerDelta.y ) > 0.0001f ) ||
					 ( fabs( vecPlayerDelta.z ) > 0.0001f ) )
				{
					VectorCopy( vecPlayerDelta, pTFMove->m_vecPosDelta );
				}
				break;
			}

		case MOVETYPE_ISOMETRIC:
			{
				//IsometricMove();
				// Could also try:  FullTossMove();
				FullWalkMove();
				break;
			}

		default:
			{
				DevMsg( 1, "Bogus pmove player movetype %i on (%i) 0=cl 1=sv\n", player->GetMoveType(), player->IsServer());
				break;
			}
	}
}


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTFGameMovement::PlayerMove( void )
{
	VPROF( "CTFGameMovement::PlayerMove" );

	// Setup and initialization pre-player movement.
	if (!PrePlayerMove())
		return;

	// Handle Movement
	HandlePlayerMove();

	// Clean-up and updates post-player movement.
	PostPlayerMove();
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTFGameMovement::FullWalkMove()
{
	VPROF( "CTFGameMovement::FullWalkMove" );

	if ( !CheckWater() ) 
	{
		StartGravity();
	}

	// If we are leaping out of the water, just update the counters.
	if (player->m_flWaterJumpTime)
	{
		WaterJump();
		TryPlayerMove();
		// See if we are still in water?
		CheckWater();
		return;
	}

	// If we are swimming in the water, see if we are nudging against a place we can jump up out
	//  of, and, if so, start out jump.  Otherwise, if we are not moving up, then reset jump timer to 0
	if ( player->GetWaterLevel() >= WL_Waist ) 
	{
		if ( player->GetWaterLevel() == WL_Waist )
		{
			CheckWaterJump();
		}

			// If we are falling again, then we must not trying to jump out of water any more.
		if ( mv->m_vecVelocity[2] < 0 && 
			 player->m_flWaterJumpTime )
		{
			player->m_flWaterJumpTime = 0;
		}

		// Was jump button pressed?
		if (mv->m_nButtons & IN_JUMP)
		{
			CheckJumpButton();
		}
		else
		{
			mv->m_nOldButtons &= ~IN_JUMP;
		}

		// Perform regular water movement
		WaterMove();
		VectorSubtract (mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity);

		// Redetermine position vars
		CategorizePosition();
	}
	else
	// Not fully underwater
	{
		// Was jump button pressed?
		if (mv->m_nButtons & IN_JUMP)
		{
			CheckJumpButton();
		}
		else
		{
			mv->m_nOldButtons &= ~IN_JUMP;
		}

		// Fricion is handled before we add in any base velocity. That way, if we are on a conveyor, 
		//  we don't slow when standing still, relative to the conveyor.
		if (player->GetGroundEntity() != NULL)
		{
			mv->m_vecVelocity[2] = 0.0;
			Friction();
		}

		// Make sure velocity is valid.
		CheckVelocity();

		if (player->GetGroundEntity() != NULL)
		{
//			WalkMove();
			WalkMove2();
		}
		else
		{
 			AirMove();  // Take into account movement when in air.
		}

		// Set final flags.
		CategorizePosition();

		// Now pull the base velocity back out.   Base velocity is set if you are on a moving object, like
		//  a conveyor (or maybe another monster?)
		VectorSubtract (mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity );

		// Make sure velocity is valid.
		CheckVelocity();

		// Add any remaining gravitational component.
		if ( !CheckWater() )
		{
			FinishGravity();
		}

		// If we are on ground, no downward velocity.
		if ( player->GetGroundEntity() != NULL )
		{
			mv->m_vecVelocity[2] = 0;
		}
		CheckFalling();
	}

	if  ( ( m_nOldWaterLevel == WL_NotInWater && player->GetWaterLevel() != WL_NotInWater ) ||
		  ( m_nOldWaterLevel != WL_NotInWater && player->GetWaterLevel() == WL_NotInWater ) )
	{
		PlaySwimSound();
	}
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFGameMovement::AirMove( void )
{
	VPROF( "CTFGameMovement::AirMove" );

	// NOTE: This was causing some additional problems with walking on physics
	//       objects.  The movement system needs to be cleaned up!  Keep the old
	//       code until the system has been fixed.s
	BaseClass::AirMove();
	return;


	int			i;
	Vector		wishvel;
	float		fmove, smove;
	Vector		wishdir;
	float		wishspeed;
	Vector forward, right, up;

	// Initialize the movement stack.
	VectorCopy( mv->m_vecAbsOrigin, m_aMovementStack[0].m_vecPosition );
	VectorCopy( m_vecGroundNormal, m_aMovementStack[0].m_vecImpactNormal );
	m_nMovementStackSize = 1;

	AngleVectors (mv->m_vecViewAngles, &forward, &right, &up);  // Determine movement angles
	
	// Copy movement amounts
	fmove = mv->m_flForwardMove;
	smove = mv->m_flSideMove;
	
	// Zero out z components of movement vectors
	forward[2] = 0;
	right[2]   = 0;
	VectorNormalize(forward);  // Normalize remainder of vectors
	VectorNormalize(right);    // 

	for (i=0 ; i<2 ; i++)       // Determine x and y parts of velocity
		wishvel[i] = forward[i]*fmove + right[i]*smove;
	wishvel[2] = 0;             // Zero out z part of velocity

	VectorCopy (wishvel, wishdir);   // Determine maginitude of speed of move
	wishspeed = VectorNormalize(wishdir);

	//
	// clamp to server defined max speed
	//
	if (wishspeed > mv->m_flMaxSpeed)
	{
		VectorScale (wishvel, mv->m_flMaxSpeed/wishspeed, wishvel);
		wishspeed = mv->m_flMaxSpeed;
	}
	
	AirAccelerate( wishdir, wishspeed, sv_airaccelerate.GetFloat() );

	// Add in any base velocity to the current velocity.
	VectorAdd(mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity );

	TryPlayerMove2();

	// Try to stand up.
	TryStanding();
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFGameMovement::WalkMove( void )
{
	_WalkMove();
}

//-----------------------------------------------------------------------------
// Purpose: This is here until the new movement code is complete.  I needed
//          to override hl2's walk move so that "floors" are identified 
//          differently.
//-----------------------------------------------------------------------------
void CTFGameMovement::_WalkMove( void )
{
	VPROF( "CTFGameMovement::_WalkMove" );

	int clip;
	int i;

	Vector wishvel;
	float spd;
	float fmove, smove;
	Vector wishdir;
	float wishspeed;

	Vector dest, start;
	Vector original, originalvel;
	Vector down, downvel;
	float downdist, updist;
	trace_t pm;
	Vector forward, right, up;

	AngleVectors (mv->m_vecViewAngles, &forward, &right, &up);  // Determine movement angles

	CHandle< CBaseEntity > oldground;
	oldground = player->GetGroundEntity();
	
	// Copy movement amounts
	fmove = mv->m_flForwardMove;
	smove = mv->m_flSideMove;
	
	// Zero out z components of movement vectors
	forward[2] = 0;
	right[2]   = 0;
	
	VectorNormalize (forward);  // Normalize remainder of vectors.
	VectorNormalize (right);    // 

	for (i=0 ; i<2 ; i++)       // Determine x and y parts of velocity
		wishvel[i] = forward[i]*fmove + right[i]*smove;
	
	wishvel[2] = 0;             // Zero out z part of velocity

	VectorCopy (wishvel, wishdir);   // Determine maginitude of speed of move
	wishspeed = VectorNormalize(wishdir);

	//
	// Clamp to server defined max speed
	//
	if (wishspeed > mv->m_flMaxSpeed)
	{
		VectorScale (wishvel, mv->m_flMaxSpeed/wishspeed, wishvel);
		wishspeed = mv->m_flMaxSpeed;
	}

	// Set pmove velocity
	mv->m_vecVelocity[2] = 0;
	Accelerate ( wishdir, wishspeed, sv_accelerate.GetFloat() );
	mv->m_vecVelocity[2] = 0;

	// Add in any base velocity to the current velocity.
	VectorAdd (mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity );

	spd = VectorLength( mv->m_vecVelocity );

	if (spd < 1.0f)
	{
		mv->m_vecVelocity.Init();
		return;
	}

	// first try just moving to the destination	
	dest[0] = mv->m_vecAbsOrigin[0] + mv->m_vecVelocity[0]*gpGlobals->frametime;
	dest[1] = mv->m_vecAbsOrigin[1] + mv->m_vecVelocity[1]*gpGlobals->frametime;	
	dest[2] = mv->m_vecAbsOrigin[2];

	// first try moving directly to the next spot
	VectorCopy (dest, start);

	TracePlayerBBox( mv->m_vecAbsOrigin, dest, MASK_PLAYERSOLID, COLLISION_GROUP_PLAYER_MOVEMENT, pm );

	// If we made it all the way, then copy trace end
	//  as new player position.

	mv->m_outWishVel += wishdir * wishspeed;

	if (pm.fraction == 1)
	{
		VectorCopy( pm.endpos, mv->m_vecAbsOrigin );
		return;
	}

	if (oldground == NULL &&   // Don't walk up stairs if not on ground.
		player->GetWaterLevel()  == 0)
		return;

	if (player->m_flWaterJumpTime)         // If we are jumping out of water, don't do anything more.
		return;

	// Try sliding forward both on ground and up 16 pixels
	//  take the move that goes farthest
	VectorCopy (mv->m_vecAbsOrigin, original);       // Save out original pos &
	VectorCopy (mv->m_vecVelocity, originalvel);  //  velocity.

	// Slide move
	clip = TryPlayerMove();

	// Copy the results out
	VectorCopy (mv->m_vecAbsOrigin  , down);
	VectorCopy (mv->m_vecVelocity, downvel);

	// Reset original values.
	VectorCopy (original, mv->m_vecAbsOrigin);
	VectorCopy (originalvel, mv->m_vecVelocity);

	// Start out up one stair height
	VectorCopy (mv->m_vecAbsOrigin, dest);
	dest[2] += player->m_Local.m_flStepSize;

	TracePlayerBBox( mv->m_vecAbsOrigin, dest, MASK_PLAYERSOLID, COLLISION_GROUP_PLAYER_MOVEMENT, pm );

	// If we started okay and made it part of the way at least,
	//  copy the results to the movement start position and then
	//  run another move try.
	if (!pm.startsolid && !pm.allsolid)
	{
		VectorCopy (pm.endpos, mv->m_vecAbsOrigin);
	}

	// slide move the rest of the way.
	clip = TryPlayerMove();

	// Now try going back down from the end point
	//  press down the stepheight
	VectorCopy (mv->m_vecAbsOrigin, dest);
	dest[2] -= player->m_Local.m_flStepSize;

	TracePlayerBBox( mv->m_vecAbsOrigin, dest, MASK_PLAYERSOLID, COLLISION_GROUP_PLAYER_MOVEMENT, pm );

	// If we are not on the ground any more then
	//  use the original movement attempt
	if ( pm.plane.normal[2] < IMPACT_NORMAL_FLOOR )
		goto usedown;
	// If the trace ended up in empty space, copy the end
	//  over to the origin.
	if (!pm.startsolid && !pm.allsolid)
	{
		VectorCopy (pm.endpos, mv->m_vecAbsOrigin);
	}
	// Copy this origion to up.
	VectorCopy (mv->m_vecAbsOrigin, up);

	// decide which one went farther
	downdist = (down[0] - original[0])*(down[0] - original[0])
		     + (down[1] - original[1])*(down[1] - original[1]);
	updist   = (up[0]   - original[0])*(up[0]   - original[0])
		     + (up[1]   - original[1])*(up[1]   - original[1]);

	if (downdist > updist)
	{
usedown:
	;
		VectorCopy (down   , mv->m_vecAbsOrigin);
		VectorCopy (downvel, mv->m_vecVelocity);
	}
	else // copy z value from slide move
		mv->m_vecVelocity[2] = downvel[2];

	float stepDist = mv->m_vecAbsOrigin.z - original.z;
	if ( stepDist > 0 )
	{
		mv->m_outStepHeight += stepDist;
	}
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFGameMovement::AccelerateWithoutMomentum( Vector &wishdir, float wishspeed, float accel )
{
	// No acceleration if the player is water-jumping or dead.
	if ( player->pl.deadflag || player->m_flWaterJumpTime )
		return;

	// See if we are changing direction a bit
//	float flCurrentSpeed = mv->m_vecVelocity.Dot( wishdir );
	float flCurrentSpeed = mv->m_vecVelocity.Length();

	// Reduce wishspeed by the amount of veer.
	float flAddSpeed = wishspeed - flCurrentSpeed;

	// If not going to add any speed, done.
	if ( flAddSpeed <= 0.0f )
		return;

	// Determine amount of accleration.
	float flAccelSpeed = accel * gpGlobals->frametime * wishspeed * m_surfaceFriction;

	// Cap at addspeed
	if ( flAccelSpeed > flAddSpeed )
	{
		flAccelSpeed = flAddSpeed;
	}

	// Adjust velocity.
	for ( int iAxis = 0; iAxis < 3; iAxis++ )
	{
		mv->m_vecVelocity[iAxis] += flAccelSpeed * wishdir[iAxis];
	}
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFGameMovement::Accelerate( Vector &wishdir, float wishspeed, float accel )
{
	// No acceleration if the player is water-jumping or dead.
	if ( player->pl.deadflag || player->m_flWaterJumpTime )
		return;

	// See if we are changing direction a bit
//	float flCurrentSpeed = mv->m_vecVelocity.Dot( wishdir );
	float flCurrentSpeed = mv->m_vecVelocity.Length();

	// Reduce wishspeed by the amount of veer.
	float flAddSpeed = wishspeed - flCurrentSpeed;

	// If not going to add any speed, done.
	if ( flAddSpeed <= 0.0f )
		return;

	// Determine amount of accleration.
	float flAccelSpeed = accel * gpGlobals->frametime * wishspeed * m_surfaceFriction;

	// Cap at addspeed
	if ( flAccelSpeed > flAddSpeed )
	{
		flAccelSpeed = flAddSpeed;
	}

	// Gravity.
	float flGravityAdj = CalcGravityAdjustment( wishdir );

	// Adjust velocity.
	for ( int iAxis = 0; iAxis < 3; iAxis++ )
	{
		mv->m_vecVelocity[iAxis] += ( flAccelSpeed * wishdir[iAxis] * flGravityAdj );
	}
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
float CTFGameMovement::CalcGravityAdjustment( const Vector &wishdir )
{
	// Get the TF movement data.
	CTFMoveData *pTFMove = TFMove();
	if ( !pTFMove )
		return 1.0f;

	if ( player->GetMoveType() == MOVETYPE_NOCLIP )
		return 1.0f;

	Vector vecPositionDelta = pTFMove->m_vecPosDelta;
	VectorNormalize( vecPositionDelta );

	// Hard-code my table
	float flModifier = 0.0f;
	if ( vecPositionDelta.z < -0.342f )
	{
		flModifier = 1.25f;
	}
	else if ( vecPositionDelta.z < 0.0f )
	{
		flModifier = 1.15f;
	}
	else if ( vecPositionDelta.z < 0.342f )	/* >20 */
	{
		flModifier = 1.0f;
	}
	else if ( vecPositionDelta.z < 0.5f ) /* 20-30 */
	{
		float flDelta = 0.5f - vecPositionDelta.z;
		flDelta /= 0.158f;
		flDelta = 1.0f - flDelta;
		flModifier = 1.0f - ( 0.25 * flDelta );
	}
	else if ( vecPositionDelta.z < 0.707f ) /* 30-45 */
	{
		float flDelta = 0.707f - vecPositionDelta.z;
		flDelta /= 0.207f;
		flDelta = 1.0f - flDelta;
		flModifier = 0.75 - ( 0.25f * flDelta );
	}
	else if ( vecPositionDelta.z < 0.766f ) /* 45-50 */
	{
		float flDelta = 0.766f - vecPositionDelta.z;
		flDelta /= 0.059f;
		flDelta = 1.0f - flDelta;
		flModifier = 0.5f - ( 0.40f * flDelta );
	}
	else
	{
		flModifier = 0.35f;
	}

	AddToMomentumList( flModifier );
	flModifier = GetMomentum();

#if 0
	// Debug!
	if ( player->IsServer() )
	{
		Msg( "Server:Gravity Adj = %lf\n", flModifier );
	}
	else
	{
		Msg( "Client:Gravity Adj = %lf\n", flModifier );
	}
#endif

	return flModifier;
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CTFGameMovement::CalcWishVelocityAndPosition( Vector &vWishPos, Vector &vWishDir,
												   float &flWishSpeed )
{
	//
	// Determine the movement angles.
	//
	Vector vForward, vRight, vUp;
	AngleVectors( mv->m_vecViewAngles, &vForward, &vRight, &vUp );

	//
	// Zero out the z component of the movement vectors and renormalize.
	//
	vForward.z = 0.0f;
	VectorNormalize( vForward );
	vRight.z = 0.0f;
	VectorNormalize( vRight );

	//
	// Determine the xy parts of the velocity.
	//
	Vector vWishVel( 0.0f, 0.0f, 0.0f );
	for ( int axis = 0; axis < 2; axis++ )
	{
		vWishVel[axis] = ( vForward[axis] * mv->m_flForwardMove ) +
			             ( vRight[axis] * mv->m_flSideMove );
	}
	vWishVel.z = 0.0f;

	//
	// Componentize the velocity into direction and speed.
	//
	VectorCopy( vWishVel, vWishDir );
	flWishSpeed = VectorNormalize( vWishDir );
	if ( flWishSpeed > mv->m_flMaxSpeed )
	{
		VectorScale( vWishVel, ( mv->m_flMaxSpeed / flWishSpeed ), vWishVel );
		flWishSpeed = mv->m_flMaxSpeed;
	}

	//
	// Accelerate (in the plane).
	//
	mv->m_vecVelocity.z = 0.0f;
	Accelerate( vWishDir, flWishSpeed, sv_accelerate.GetFloat() );
	mv->m_vecVelocity.z = 0.0f;

	// Add in any base velocity (from conveyers, etc.) to the current velocity.
	VectorAdd( mv->m_vecVelocity, player->GetBaseVelocity(), mv->m_vecVelocity );

	//
	// Stop the player (zero out velocity) if the player's speed is below a
	// given threshold.
	//
	float flSpeed = VectorLength( mv->m_vecVelocity );
	if ( flSpeed < SPEED_STOP_THRESHOLD )
	{
		mv->m_vecVelocity.Init();
		return false;
	}

	//
	// Calculate the wish position.
	//
	vWishPos.x = mv->m_vecAbsOrigin.x + ( mv->m_vecVelocity.x * gpGlobals->frametime );
	vWishPos.y = mv->m_vecAbsOrigin.y + ( mv->m_vecVelocity.y * gpGlobals->frametime );
	vWishPos.z = mv->m_vecAbsOrigin.z;

	return true;
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
inline void CTFGameMovement::TracePlayerBBoxWithStep( const Vector &vStart, const Vector &vEnd, 
							unsigned int fMask, int collisionGroup, trace_t &trace )
{
	VPROF( "CTFGameMovement::TracePlayerBBoxWithStep" );

	Vector vHullMin = GetPlayerMins( player->m_Local.m_bDucked );
	vHullMin.z += player->m_Local.m_flStepSize;
	Vector vHullMax = GetPlayerMaxs( player->m_Local.m_bDucked );

	Ray_t ray;
	ray.Init( vStart, vEnd, vHullMin, vHullMax );
	UTIL_TraceRay( ray, fMask, mv->m_nPlayerHandle.Get(), collisionGroup, &trace );
}

#if 0
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
inline void CGameMovement::TracePlayerBBox( const Vector &start, const Vector &end, 
							unsigned int fMask, int collisionGroup, trace_t& pm )
{
	VPROF( "CTFGameMovement::TracePlayerBBox" );

	Ray_t ray;
	ray.Init( start, end, GetPlayerMins( player->m_Local.m_bDucked ), GetPlayerMaxs( player->m_Local.m_bDucked ) );
	UTIL_TraceRay( ray, fMask, mv->m_nPlayerHandle.Get(), collisionGroup, &pm );
}
#endif

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
inline int CTFGameMovement::BlockerType( const Vector &vImpactNormal )
{
	// If the impact plane has a high z component in the normal, then
	// it is probably a floor.
	if ( vImpactNormal.z > IMPACT_NORMAL_FLOOR )
		return 1;

	// If the impact plane has a zero z component in the normal, then it is
	// probably a step or wall.
	if ( vImpactNormal.z == IMPACT_NORMAL_WALL )
		return 2;

	// None
	return 0;
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CTFGameMovement::RedirectGroundVelocity( const trace_t &trace )
{
	// Check for max planes.
	if ( m_nImpactPlaneCount >= MAX_IMPACT_PLANES )
	{
		mv->m_vecVelocity.Init();
		return false;
	}

	// Add the impact plane normal to the list.
	VectorCopy( trace.plane.normal, m_aImpactPlaneNormals[m_nImpactPlaneCount] );
	m_nImpactPlaneCount++;
	
	int iPlane, iPlane2;
	for ( iPlane = 0; iPlane < m_nImpactPlaneCount; iPlane++ )
	{
		// Reduce and redirect the player's velocity.
		Vector vecVelocity( mv->m_vecVelocity.x, mv->m_vecVelocity.y, mv->m_vecVelocity.z );

		// Don't let negatively sloped surfaces drive you into the ground.
		if ( m_aImpactPlaneNormals[iPlane].z < 0.0f )
		{
			m_aImpactPlaneNormals[iPlane].z = 0.0f;
			VectorNormalize( m_aImpactPlaneNormals[iPlane] );
		}

		ClipVelocity( vecVelocity, m_aImpactPlaneNormals[iPlane], mv->m_vecVelocity, 1.0f );

		// Check to see if we need to continue clipping?
		for( iPlane2 = 0; iPlane2 < m_nImpactPlaneCount; iPlane2++ )
		{
			if ( iPlane != iPlane2 )
			{
				if ( mv->m_vecVelocity.Dot( m_aImpactPlaneNormals[iPlane2] ) < 0.0f )
					break;
			}
		}
		if ( iPlane2 == m_nImpactPlaneCount )
			break;
	}

	if ( iPlane == m_nImpactPlaneCount )
	{
		// Go along the crease here!
		if ( m_nImpactPlaneCount != 2 )
		{
			mv->m_vecVelocity.Init();
			return false;
		}
		else
		{
			Vector vecCross;
			CrossProduct( m_aImpactPlaneNormals[0], m_aImpactPlaneNormals[1], vecCross );
			float flScalar = vecCross.Dot( mv->m_vecVelocity );
			VectorScale( vecCross, flScalar, mv->m_vecVelocity );
		}
	}

	// If the original velocity is against the current velocity, stop dead to 
	// avoid tiny occilations in sloping corners
	if ( mv->m_vecVelocity.Dot( m_vecOriginalVelocity ) <= 0.0f )
	{
		mv->m_vecVelocity.Init();
		return false;
	}

	return true;
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CTFGameMovement::RedirectAirVelocity( const trace_t &trace )
{
	// Check for max planes.
	if ( m_nImpactPlaneCount >= MAX_IMPACT_PLANES )
	{
		mv->m_vecVelocity.Init();
		return false;
	}

	// Add the impact plane normal to the list.
	VectorCopy( trace.plane.normal, m_aImpactPlaneNormals[m_nImpactPlaneCount] );
	m_nImpactPlaneCount++;

	for ( int iPlane = 0; iPlane < m_nImpactPlaneCount; iPlane++ )
	{
		// Reduce and redirect the player's velocity.
		Vector vecVelocity( mv->m_vecVelocity.x, mv->m_vecVelocity.y, mv->m_vecVelocity.z );

		// Don't let negatively sloped surfaces drive you into the ground.
		if ( m_aImpactPlaneNormals[iPlane].z < 0.0f )
		{
			m_aImpactPlaneNormals[iPlane].z = 0.0f;
			VectorNormalize( m_aImpactPlaneNormals[iPlane] );
		}

		if ( m_aImpactPlaneNormals[iPlane].z > IMPACT_NORMAL_FLOOR )
		{
			ClipVelocity( vecVelocity, m_aImpactPlaneNormals[iPlane], mv->m_vecVelocity, 1.0f );
		}
		else
		{
			ClipVelocity( vecVelocity, m_aImpactPlaneNormals[iPlane], mv->m_vecVelocity, 1.0f + sv_bounce.GetFloat() * ( 1.0f - m_surfaceFriction ) );
		}
	}	

	return true;
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFGameMovement::CollisionResponseNone( const trace_t &trace )
{
	// Move the player to the trace's ending position.
	VectorCopy( trace.endpos, mv->m_vecAbsOrigin );

	// Add the position to the stack.
	if ( m_nMovementStackSize < MOVEMENTSTACK_MAXSIZE )
	{
		VectorCopy( mv->m_vecAbsOrigin, m_aMovementStack[m_nMovementStackSize].m_vecPosition );
		VectorCopy( mv->m_vecVelocity, m_aMovementStack[m_nMovementStackSize].m_vecVelocity );
		VectorCopy( m_aMovementStack[m_nMovementStackSize].m_vecImpactNormal, 
			m_aMovementStack[m_nMovementStackSize].m_vecImpactNormal );
		m_nMovementStackSize++;
	}
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFGameMovement::CollisionResponseStuck( void )
{
	mv->m_vecVelocity.Init();
	//DevMsg( 1, "CollisionResponseStuck: %s is stuck (%s).\n", player->GetClassname(), player->IsServer() ? "sv" : "cl" );
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CTFGameMovement::CollisionResponseGeneric( const trace_t &trace, int &nBlocked )
{
	// Check for any movement.
	if ( trace.fraction > 0.0f )
	{
		// Move the partial move and reset the impact plane count.
		VectorCopy( trace.endpos, mv->m_vecAbsOrigin );
		m_nImpactPlaneCount = 0;

		// Add the data to the movement stack. -- the velocity is wrong here!!!
		if ( m_nMovementStackSize < MOVEMENTSTACK_MAXSIZE )
		{
			VectorCopy( mv->m_vecAbsOrigin, m_aMovementStack[m_nMovementStackSize].m_vecPosition );
			VectorCopy( mv->m_vecVelocity, m_aMovementStack[m_nMovementStackSize].m_vecVelocity );
			VectorCopy( trace.plane.normal, m_aMovementStack[m_nMovementStackSize].m_vecImpactNormal );
			m_nMovementStackSize++;
		}
	}

	// Sanity check - Impact plane count.
	Assert( m_nImpactPlaneCount < MAX_IMPACT_PLANES );

	// Add the entity to the touched list (list itself maintains uniqueness).
	MoveHelper()->AddToTouched( trace, mv->m_vecVelocity );

	// Check for special "blockers" (walls, steps, floor).
    nBlocked |= BlockerType( trace.plane.normal );
	
	// Re-direct or bounce the player's velocity vector.
	if ( player->GetGroundEntity() != NULL )
	{
		return RedirectGroundVelocity( trace );
	}
	else
	{
		return RedirectAirVelocity( trace );
	}
}

//-----------------------------------------------------------------------------
// Purpose: See comment _WalkMove
//-----------------------------------------------------------------------------
int CTFGameMovement::TryPlayerMove( Vector *pFirstDest=NULL, trace_t *pFirstTrace=NULL )
{
	VPROF( "CTFGameMovement::TryPlayerMove" );

	int			bumpcount, numbumps;
	Vector		dir;
	float		d;
	int			numplanes;
	Vector		planes[5/*MAX_CLIP_PLANES*/];
	Vector		primal_velocity, original_velocity;
	Vector      new_velocity;
	int			i, j;
	trace_t	pm;
	Vector		end;
	float		time_left, allFraction;
	int			blocked;		
	
	numbumps  = 4;           // Bump up to four times
	
	blocked   = 0;           // Assume not blocked
	numplanes = 0;           //  and not sliding along any planes
	VectorCopy (mv->m_vecVelocity, original_velocity);  // Store original velocity
	VectorCopy (mv->m_vecVelocity, primal_velocity);
	
	allFraction = 0;
	time_left = gpGlobals->frametime;   // Total time for this movement operation.

	new_velocity.Init();

	for (bumpcount=0 ; bumpcount < numbumps; bumpcount++)
	{
		if ( mv->m_vecVelocity.Length() == 0.0 )
			break;

		// Assume we can move all the way from the current origin to the
		//  end point.
		VectorMA( mv->m_vecAbsOrigin, time_left, mv->m_vecVelocity, end );

		// See if we can make it from origin to end point.
		if ( g_bMovementOptimizations )
		{
			// If their velocity Z is 0, then we can avoid an extra trace here during WalkMove.
			if ( pFirstDest && end == *pFirstDest )
				pm = *pFirstTrace;
			else
				TracePlayerBBox( mv->m_vecAbsOrigin, end, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, pm );
		}
		else
		{
			TracePlayerBBox( mv->m_vecAbsOrigin, end, PlayerSolidMask(), COLLISION_GROUP_PLAYER_MOVEMENT, pm );
		}

		allFraction += pm.fraction;

		// If we started in a solid object, or we were in solid space
		//  the whole way, zero out our velocity and return that we
		//  are blocked by floor and wall.
		if (pm.allsolid)
		{	
			// entity is trapped in another solid
			VectorCopy (vec3_origin, mv->m_vecVelocity);
			return 4;
		}

		// If we moved some portion of the total distance, then
		//  copy the end position into the pmove.origin and 
		//  zero the plane counter.
		if( pm.fraction > 0 )
		{	
			// actually covered some distance
			VectorCopy (pm.endpos, mv->m_vecAbsOrigin);
			VectorCopy (mv->m_vecVelocity, original_velocity);
			numplanes = 0;
		}

		// If we covered the entire distance, we are done
		//  and can return.
		if (pm.fraction == 1)
		{
			 break;		// moved the entire distance
		}

		// Indicate a collision occurred...
		OnTryPlayerMoveCollision( pm );

		// Save entity that blocked us (since fraction was < 1.0)
		//  for contact
		// Add it if it's not already in the list!!!
		MoveHelper( )->AddToTouched( pm, mv->m_vecVelocity );

		// If the plane we hit has a high z component in the normal, then
		//  it's probably a floor
		if (pm.plane.normal[2] > IMPACT_NORMAL_FLOOR)
		{
			blocked |= 1;		// floor
		}
		// If the plane has a zero z component in the normal, then it's a 
		//  step or wall
		if (!pm.plane.normal[2])
		{
			blocked |= 2;		// step / wall
		}

		// Reduce amount of m_flFrameTime left by total time left * fraction
		//  that we covered.
		time_left -= time_left * pm.fraction;
		
		// Did we run out of planes to clip against?
		if (numplanes >= 5/*MAX_CLIP_PLANES*/)
		{	
			// this shouldn't really happen
			//  Stop our movement if so.
			VectorCopy (vec3_origin, mv->m_vecVelocity);
			//Con_DPrintf("Too many planes 4\n");

			break;
		}

		// Set up next clipping plane
		VectorCopy (pm.plane.normal, planes[numplanes]);
		numplanes++;

		// modify original_velocity so it parallels all of the clip planes
		//
		if ( player->GetMoveType() == MOVETYPE_WALK &&
			((player->GetGroundEntity() == NULL) /*|| (mv->m_fFriction != 1)*/ ) )	// relfect player velocity
		{
			for ( i = 0; i < numplanes; i++ )
			{
				if ( planes[i][2] > IMPACT_NORMAL_FLOOR  )
				{
					// floor or slope
					ClipVelocity( original_velocity, planes[i], new_velocity, 1 );
					VectorCopy( new_velocity, original_velocity );
				}
				else
				{
					ClipVelocity( original_velocity, planes[i], new_velocity, 1.0 + sv_bounce.GetFloat() * (1 - m_surfaceFriction) );
				}
			}

			VectorCopy( new_velocity, mv->m_vecVelocity );
			VectorCopy( new_velocity, original_velocity );
		}
		else
		{
			for (i=0 ; i < numplanes ; i++)
			{
				ClipVelocity (
					original_velocity,
					planes[i],
					mv->m_vecVelocity,
					1);

				for (j=0 ; j<numplanes ; j++)
					if (j != i)
					{
						// Are we now moving against this plane?
						if (mv->m_vecVelocity.Dot(planes[j]) < 0)
							break;	// not ok
					}
				if (j == numplanes)  // Didn't have to clip, so we're ok
					break;
			}
			
			// Did we go all the way through plane set
			if (i != numplanes)
			{	// go along this plane
				// pmove.velocity is set in clipping call, no need to set again.
				;  
			}
			else
			{	// go along the crease
				if (numplanes != 2)
				{
					VectorCopy (vec3_origin, mv->m_vecVelocity);
					break;
				}
				CrossProduct (planes[0], planes[1], dir);
				d = dir.Dot(mv->m_vecVelocity);
				VectorScale (dir, d, mv->m_vecVelocity );
			}

			//
			// if original velocity is against the original velocity, stop dead
			// to avoid tiny occilations in sloping corners
			//
			d = mv->m_vecVelocity.Dot(primal_velocity);
			if (d <= 0)
			{
				//Con_DPrintf("Back\n");
				VectorCopy (vec3_origin, mv->m_vecVelocity);
				break;
			}
		}
	}

	if ( allFraction == 0 )
	{
		VectorCopy (vec3_origin, mv->m_vecVelocity);
	}

	return blocked;
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
int CTFGameMovement::TryPlayerMove2( void )
{
	VPROF( "CTFGameMovement::TryPlayerMove2" );

	int	nBlocked = MOVEMENT_BLOCKED_NONE;
	trace_t	trace;
	float flTimeLeft = gpGlobals->frametime;
	m_nImpactPlaneCount = 0;

	// Save off the original velocity -- coming in.
	VectorCopy( mv->m_vecVelocity, m_vecOriginalVelocity );

	float flTotalFraction = 0.0f;
	for ( int iBump = 0; iBump < BUMP_MAX_COUNT; iBump++ )
	{
		// Check to see if we have any velocity left. EXPENSIVE!!!!
		if ( mv->m_vecVelocity.Length() == 0.0f )
			break;

		//
		// Calculate the wish position. 
		//
		Vector vecWishPos;
		VectorMA( mv->m_vecAbsOrigin, flTimeLeft, mv->m_vecVelocity, vecWishPos );

		//
		// Attempt the move.
		//
		// NOTE:  This check can hit players and objects
		TracePlayerBBoxWithStep( mv->m_vecAbsOrigin, vecWishPos, MASK_PLAYERSOLID, COLLISION_GROUP_PLAYER, trace );

		//  but if it didn't no need to do another trace for movement only
		if ( trace.fraction == 1.0f )
		{
			flTotalFraction += trace.fraction;
			CollisionResponseNone( trace );
			break;
		}
		else
		{
			// Add the entity to the touched list (list itself maintains uniqueness).
			MoveHelper()->AddToTouched( trace, mv->m_vecVelocity );
			// Do a non-solid to player trace now, instead, and override what's in trace
			TracePlayerBBoxWithStep( mv->m_vecAbsOrigin, vecWishPos, MASK_PLAYERSOLID, COLLISION_GROUP_PLAYER_MOVEMENT, trace );
		}

		flTotalFraction += trace.fraction;

		// Handle stuck in solid.
		if ( trace.allsolid )
		{
			CollisionResponseStuck();
			return MOVEMENT_BLOCKED_ALL;
		}

		// Handle full movement.
		if ( trace.fraction == 1.0f )
		{
			CollisionResponseNone( trace );
			break;
		}

		// Handle partial movement.
		if ( !CollisionResponseGeneric( trace, nBlocked ) )
			break;

		// Reduce the time left.
		flTimeLeft -= ( flTimeLeft * trace.fraction );

		/*
		//
		// Attempt the move.
		//
		TracePlayerBBoxWithStep( mv->m_vecAbsOrigin, vecWishPos, MASK_PLAYERSOLID, COLLISION_GROUP_PLAYER_MOVEMENT, trace );

		flTotalFraction += trace.fraction;

		// Handle stuck in solid.
		if ( trace.allsolid )
		{
			CollisionResponseStuck();
			return MOVEMENT_BLOCKED_ALL;
		}

		// Handle full movement.
		if ( trace.fraction == 1.0f )
		{
			CollisionResponseNone( trace );
			break;
		}

		// Handle partial movement.
		if ( !CollisionResponseGeneric( trace, nBlocked ) )
			break;

		// Reduce the time left.
		flTimeLeft -= ( flTimeLeft * trace.fraction );
		*/
	}
	
	// Finally, check for any movement.
	if ( flTotalFraction == 0.0f )
	{
		CollisionResponseStuck();
	}

	return nBlocked;
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFGameMovement::TryStanding( void )
{
	VPROF( "CTFGameMovement::TryStanding" );

	// Step down.
	Vector vecStandPos( mv->m_vecAbsOrigin.x, mv->m_vecAbsOrigin.y, mv->m_vecAbsOrigin.z - player->m_Local.m_flStepSize );

	trace_t trace;
	TracePlayerBBoxWithStep( mv->m_vecAbsOrigin, vecStandPos, MASK_PLAYERSOLID, COLLISION_GROUP_PLAYER_MOVEMENT, trace );
	if ( trace.fraction == 1.0f )
		return;

	// Attempt to stand up.
	vecStandPos.z = mv->m_vecAbsOrigin.z + ( ( 1.0f - trace.fraction ) * player->m_Local.m_flStepSize );
	TracePlayerBBoxWithStep( mv->m_vecAbsOrigin, vecStandPos, MASK_PLAYERSOLID, COLLISION_GROUP_PLAYER_MOVEMENT, trace );

	// A fraction of 1.0 means we stood up fine - done.
	if ( trace.fraction == 1.0f )
	{
		VectorCopy( trace.endpos, mv->m_vecAbsOrigin );
		return;
	}

	// Use the movement stack to pop back and resolve the stand.
	if ( m_nMovementStackSize > 0 )
	{
		VectorCopy( m_aMovementStack[m_nMovementStackSize-1].m_vecPosition, mv->m_vecAbsOrigin );
		VectorCopy( m_aMovementStack[m_nMovementStackSize-1].m_vecVelocity, mv->m_vecVelocity );
		m_nMovementStackSize--; 
		TryStanding();
	}
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFGameMovement::ResolveStanding( void )
{
	VPROF( "CTFGameMovement::ResolveStanding" );

	//
	// Attempt to move down twice your step height.  Anything between 0.5 and 1.0
	// is a valid "stand" value.
	//
	Vector vecStandPos( mv->m_vecAbsOrigin.x, mv->m_vecAbsOrigin.y, mv->m_vecAbsOrigin.z - ( player->m_Local.m_flStepSize * 2.0f ) );

	trace_t trace;
	TracePlayerBBoxWithStep( mv->m_vecAbsOrigin, vecStandPos, MASK_PLAYERSOLID, COLLISION_GROUP_PLAYER_MOVEMENT, trace );

	// Anything between 0.5 and 1.0 is a valid stand value
	if ( fabs( trace.fraction - 0.5 ) < 0.0004f )
	{
		return;
	}

//	if ( trace.fraction == 0.5 )
//	{
//		VectorCopy( trace.endpos, mv->m_vecAbsOrigin );
//		return;
//	}

	if ( trace.fraction > 0.5f )
	{
		trace.fraction -= 0.5f;
		Vector vecNewOrigin;
		vecNewOrigin = mv->m_vecAbsOrigin + trace.fraction * ( vecStandPos - mv->m_vecAbsOrigin );
		mv->m_vecAbsOrigin = vecNewOrigin;
		return;
	}

	// Less than 0.5 mean we need to attempt to push up the difference.
	vecStandPos.z = ( mv->m_vecAbsOrigin.z + ( ( 0.5f - trace.fraction ) * ( player->m_Local.m_flStepSize * 2.0f ) ) );
	TracePlayerBBoxWithStep( mv->m_vecAbsOrigin, vecStandPos, MASK_PLAYERSOLID, COLLISION_GROUP_PLAYER_MOVEMENT, trace );
	
	// A fraction of 1.0 means we stood up fine - done.
	if ( trace.fraction == 1.0f )
	{
		VectorCopy( trace.endpos, mv->m_vecAbsOrigin );
		return;
	}

	// Use the movement stack to pop back and resolve the stand.
	if ( m_nMovementStackSize > 0 )
	{
		VectorCopy( m_aMovementStack[m_nMovementStackSize-1].m_vecPosition, mv->m_vecAbsOrigin );
		VectorCopy( m_aMovementStack[m_nMovementStackSize-1].m_vecVelocity, mv->m_vecVelocity );
		m_nMovementStackSize--; 
		ResolveStanding();
	}
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTFGameMovement::WalkMove2( void )
{
	VPROF( "CTFGameMovement::WalkMove2" );

	// Initialize the movement stack.
	VectorCopy( mv->m_vecAbsOrigin, m_aMovementStack[0].m_vecPosition );
	VectorCopy( m_vecGroundNormal, m_aMovementStack[0].m_vecImpactNormal );
	m_nMovementStackSize = 1;

	// Calculate the wish velocity and position.  The function returns false if 
	// the velocity is zero, it returns true otherwise.
	Vector vWishPos, vWishDir;
	float flWishSpeed;
	if ( !CalcWishVelocityAndPosition( vWishPos, vWishDir, flWishSpeed ) )
		return;

	// For physics player shadow.
	mv->m_outWishVel += vWishDir * flWishSpeed;

	// Lift up the players feet (bring the minimum z componenet up by the step
	// size) and sweep.
	TryPlayerMove2();

	// Try to stand up at movement's end.
	ResolveStanding();

	// For physics player shadow.
	float flStepHeight = mv->m_vecAbsOrigin.z - m_aMovementStack[0].m_vecPosition.z;
	if ( flStepHeight > 0.0f )
	{
		mv->m_outStepHeight += flStepHeight;
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTFGameMovement::SetMomentumList( float flValue )
{
	// Get the TF movement data.
	CTFMoveData *pTFMove = TFMove();
	if ( !pTFMove )
		return;

	// Fill in the momentum list.
	pTFMove->m_iMomentumHead = 0;
	for ( int iMomentum = 0; iMomentum < CTFMoveData::MOMENTUM_MAXSIZE; iMomentum++ )
	{
		pTFMove->m_aMomentum[iMomentum] = flValue;
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CTFGameMovement::AddToMomentumList( float flValue )
{
	// Get the TF movement data.
	CTFMoveData *pTFMove = TFMove();
	if ( !pTFMove )
		return;

	// Insert the new gravity value into the list.
	pTFMove->m_aMomentum[pTFMove->m_iMomentumHead] = flValue;
	pTFMove->m_iMomentumHead = ( pTFMove->m_iMomentumHead + 1 ) % CTFMoveData::MOMENTUM_MAXSIZE;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
float CTFGameMovement::GetMomentum( void )
{
	// Get the TF movement data.
	CTFMoveData *pTFMove = TFMove();
	if ( !pTFMove )
		return 1.0f;

	float flTotal = 0.0f;
	for ( int iMomentum = 0; iMomentum < CTFMoveData::MOMENTUM_MAXSIZE; iMomentum++ )
	{
		flTotal += pTFMove->m_aMomentum[iMomentum];
	}
	
	flTotal /= CTFMoveData::MOMENTUM_MAXSIZE;

	return flTotal;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
bool CTFGameMovement::CheckJumpButton( void )
{
	if (player->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 (player->m_flWaterJumpTime)
	{
		player->m_flWaterJumpTime -= gpGlobals->frametime;
		if (player->m_flWaterJumpTime < 0)
			player->m_flWaterJumpTime = 0;
		
		return false;
	}

	// If we are in the water most of the way...
	if ( player->GetWaterLevel() >= 2 )
	{	
		// swimming, not jumping
		SetGroundEntity( NULL );

		if(player->GetWaterType() == CONTENTS_WATER)    // We move up a certain amount
			mv->m_vecVelocity[2] = 100;
		else if (player->GetWaterType() == CONTENTS_SLIME)
			mv->m_vecVelocity[2] = 80;
		
		// play swiming sound
		if ( player->m_flSwimSoundTime <= 0 )
		{
			// Don't play sound again for 1 second
			player->m_flSwimSoundTime = 1000;
			PlaySwimSound();
		}

		return false;
	}

	// No more effect
 	if (player->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

	// In the air now.
    SetGroundEntity( NULL );
	
	PlayStepSound( m_pSurfaceData, 1.0, true );
	
	MoveHelper()->PlayerSetAnimation( PLAYER_JUMP );

	float flGroundFactor = 1.0f;
	if (m_pSurfaceData)
	{
		flGroundFactor = m_pSurfaceData->game.jumpFactor; 
	}

	float flMul;
	flMul = sqrt(2 * GetCurrentGravity() * 45.0);

	// Acclerate upward
	// If we are ducking...
	float startz = mv->m_vecVelocity[2];
	if ( (  player->m_Local.m_bDucking ) || (  player->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 * flMul;  // 2 * gravity * height
	}
	else
	{
		mv->m_vecVelocity[2] += flGroundFactor * flMul;  // 2 * gravity * height
	}

	FinishGravity();

	mv->m_outJumpVel.z += mv->m_vecVelocity[2] - startz;
	mv->m_outStepHeight += 0.1f;

	// Flag that we jumped.
	mv->m_nOldButtons |= IN_JUMP;	// don't jump again until released
	return true;
}