예제 #1
0
// here bot updates important info that is used multiple times along the thinking process
void CSDKBot::InfoGathering()
{
	if (!GetEnemy())
	{
		m_flBotToEnemyDist = 9999;
		m_flHeightDifToEnemy = 0;
		m_bEnemyOnSights = false;

		m_flDistTraveled += fabs(GetLocalVelocity().Length()); // this is used for stuck checking,
		return;
	}

	m_flBotToEnemyDist = (GetLocalOrigin() - GetEnemy()->GetLocalOrigin()).Length();

	trace_t tr;
	UTIL_TraceHull( EyePosition(), GetEnemy()->EyePosition() - Vector(0,0,20), -BotTestHull, BotTestHull, MASK_SHOT, this, COLLISION_GROUP_NONE, &tr );

	if( tr.m_pEnt == GetEnemy() ) // vision line between both
		m_bEnemyOnSights = true;
	else
		m_bEnemyOnSights = false;

	m_bInRangeToAttack = (m_flBotToEnemyDist < m_flMinRangeAttack) && FInViewCone( GetEnemy() );

	m_flDistTraveled += fabs(GetLocalVelocity().Length()); // this is used for stuck checking,

	m_flHeightDifToEnemy = GetLocalOrigin().z - GetEnemy()->GetLocalOrigin().z;
}
예제 #2
0
파일: mom_player.cpp 프로젝트: horse-f/game
void CMomentumPlayer::CalculateAverageStats()
{
    ConVarRef hvel("mom_speedometer_hvel");

    if (g_Timer.IsRunning())
    {
        int currentStage = g_Timer.GetCurrentStageNumber();

        m_flStageTotalSync[currentStage] += m_flStrafeSync;
        m_flStageTotalSync2[currentStage] += m_flStrafeSync2;
        m_flStageTotalVelocity[currentStage] += hvel.GetBool() ? GetLocalVelocity().Length2D() : GetLocalVelocity().Length();

        m_nStageAvgCount[currentStage]++;

        m_flStageStrafeSyncAvg[currentStage] = m_flStageTotalSync[currentStage] / float(m_nStageAvgCount[currentStage]);
        m_flStageStrafeSync2Avg[currentStage] = m_flStageTotalSync2[currentStage] / float(m_nStageAvgCount[currentStage]);
        m_flStageVelocityAvg[currentStage] = m_flStageTotalVelocity[currentStage] / float(m_nStageAvgCount[currentStage]);

        //stage 0 is "overall" - also update these as well, no matter which stage we are on
        m_flStageTotalSync[0] += m_flStrafeSync;
        m_flStageTotalSync2[0] += m_flStrafeSync2;
        m_flStageTotalVelocity[0] += hvel.GetBool() ? GetLocalVelocity().Length2D() : GetLocalVelocity().Length();
        m_nStageAvgCount[0]++;

        m_flStageStrafeSyncAvg[0] = m_flStageTotalSync[currentStage] / float(m_nStageAvgCount[currentStage]);
        m_flStageStrafeSync2Avg[0] = m_flStageTotalSync2[currentStage] / float(m_nStageAvgCount[currentStage]);
        m_flStageVelocityAvg[0] = m_flStageTotalVelocity[currentStage] / float(m_nStageAvgCount[currentStage]);
    }

    // think once per 0.1 second interval so we avoid making the totals extremely large
    SetNextThink(gpGlobals->curtime + AVERAGE_STATS_INTERVAL, "THINK_AVERAGE_STATS");
}
예제 #3
0
파일: mom_player.cpp 프로젝트: horse-f/game
//This limits the player's speed in the start zone, depending on which gamemode the player is currently playing.
//On surf/other, it only limits practice mode speed. On bhop/scroll, it limits the movement speed above a certain threshhold, and 
//clamps the player's velocity if they go above it. This is to prevent prespeeding and is different per gamemode due to the different
//respective playstyles of surf and bhop.
void CMomentumPlayer::LimitSpeedInStartZone()
{
    ConVarRef gm("mom_gamemode");
    CTriggerTimerStart *startTrigger = g_Timer.GetStartTrigger();
    bool bhopGameMode = (gm.GetInt() == MOMGM_BHOP || gm.GetInt() == MOMGM_SCROLL);
    if (m_bInsideStartZone)
    {
        if (GetGroundEntity() == NULL && !g_Timer.IsPracticeMode(this)) //don't count ticks in air if we're in practice mode
            m_nTicksInAir++;
        else
            m_nTicksInAir = 0;

        //set bhop flag to true so we can't prespeed with practice mode
        if (g_Timer.IsPracticeMode(this)) m_bDidPlayerBhop = true; 

        //depending on gamemode, limit speed outright when player exceeds punish vel
        if (bhopGameMode  && ((!g_Timer.IsRunning() && m_nTicksInAir > MAX_AIRTIME_TICKS)))
        {
            Vector velocity = GetLocalVelocity();
            float PunishVelSquared = startTrigger->GetPunishSpeed()*startTrigger->GetPunishSpeed();
            if (velocity.Length2DSqr() > PunishVelSquared) //more efficent to check agaisnt the square of velocity
            {
                velocity = (velocity / velocity.Length()) * startTrigger->GetPunishSpeed();
                SetAbsVelocity(Vector(velocity.x, velocity.y, velocity.z));
            }
        }
    }
    SetNextThink(gpGlobals->curtime, "CURTIME_FOR_START");
}
예제 #4
0
파일: mom_player.cpp 프로젝트: Asunaya/game
void CMomentumPlayer::CheckForBhop()
{
    if (GetGroundEntity() != nullptr)
    {
        m_flTicksOnGround += gpGlobals->interval_per_tick;
        // true is player is on ground for less than 10 ticks, false if they are on ground for more s
        m_bDidPlayerBhop = (m_flTicksOnGround < NUM_TICKS_TO_BHOP * gpGlobals->interval_per_tick) != 0;
        if (!m_bDidPlayerBhop)
            m_iSuccessiveBhops = 0;
        if (m_nButtons & IN_JUMP)
        {
            m_RunData.m_flLastJumpVel = GetLocalVelocity().Length2D();
            m_iSuccessiveBhops++;
            if (g_Timer->IsRunning())
            {
                int currentZone = m_RunData.m_iCurrentZone;
                m_RunStats.SetZoneJumps(0, m_RunStats.GetZoneJumps(0) + 1);
                m_RunStats.SetZoneJumps(currentZone, m_RunStats.GetZoneJumps(currentZone) + 1);
            }
        }
    }
    else
        m_flTicksOnGround = 0;

    SetNextThink(gpGlobals->curtime, "CURTIME");
}
예제 #5
0
파일: mom_player.cpp 프로젝트: horse-f/game
void CMomentumPlayer::CheckForBhop()
{
    if (GetGroundEntity() != NULL)
    {
        m_flTicksOnGround += gpGlobals->interval_per_tick;
        // true is player is on ground for less than 10 ticks, false if they are on ground for more s
        m_bDidPlayerBhop = (m_flTicksOnGround < NUM_TICKS_TO_BHOP * gpGlobals->interval_per_tick) != 0;
        if (!m_bDidPlayerBhop)
            m_iSuccessiveBhops = 0;
        if (m_nButtons & IN_JUMP)
        {
            m_flLastJumpVel = GetLocalVelocity().Length2D();
            m_iSuccessiveBhops++;
            if (g_Timer.IsRunning())
            {
                int currentStage = g_Timer.GetCurrentStageNumber();
                m_nStageJumps[0]++;
                m_nStageJumps[currentStage]++;
            }
        }
    }
    else
        m_flTicksOnGround = 0;

    SetNextThink(gpGlobals->curtime, "CURTIME");
}
예제 #6
0
파일: mom_player.cpp 프로젝트: Asunaya/game
// Obtains the player's previous origin using a vector as the base, subtracting one tick's worth of velocity.
Vector CMomentumPlayer::GetPrevOrigin(const Vector &base)
{
    Vector velocity = GetLocalVelocity();
    Vector prevOrigin(base.x - (velocity.x * gpGlobals->interval_per_tick),
        base.y - (velocity.y * gpGlobals->interval_per_tick),
        base.z - (velocity.z * gpGlobals->interval_per_tick));
    return prevOrigin;
}
예제 #7
0
파일: mom_player.cpp 프로젝트: Asunaya/game
void CMomentumPlayer::CalculateAverageStats()
{

    if (g_Timer->IsRunning())
    {
        int currentZone = m_RunData.m_iCurrentZone;//g_Timer->GetCurrentZoneNumber();

        m_flZoneTotalSync[currentZone] += m_RunData.m_flStrafeSync;
        m_flZoneTotalSync2[currentZone] += m_RunData.m_flStrafeSync2;
        m_flZoneTotalVelocity[currentZone][0] += GetLocalVelocity().Length();
        m_flZoneTotalVelocity[currentZone][1] += GetLocalVelocity().Length2D();

        m_nZoneAvgCount[currentZone]++;

        m_RunStats.SetZoneStrafeSyncAvg(currentZone, m_flZoneTotalSync[currentZone] / float(m_nZoneAvgCount[currentZone]));
        m_RunStats.SetZoneStrafeSync2Avg(currentZone, m_flZoneTotalSync2[currentZone] / float(m_nZoneAvgCount[currentZone]));
        m_RunStats.SetZoneVelocityAvg(currentZone,
            m_flZoneTotalVelocity[currentZone][0] / float(m_nZoneAvgCount[currentZone]),
            m_flZoneTotalVelocity[currentZone][1] / float(m_nZoneAvgCount[currentZone]));

        // stage 0 is "overall" - also update these as well, no matter which stage we are on
        m_flZoneTotalSync[0] += m_RunData.m_flStrafeSync;
        m_flZoneTotalSync2[0] += m_RunData.m_flStrafeSync2;
        m_flZoneTotalVelocity[0][0] += GetLocalVelocity().Length();
        m_flZoneTotalVelocity[0][1] += GetLocalVelocity().Length2D();
        m_nZoneAvgCount[0]++;

        m_RunStats.SetZoneStrafeSyncAvg(0, m_flZoneTotalSync[currentZone] / float(m_nZoneAvgCount[currentZone]));
        m_RunStats.SetZoneStrafeSync2Avg(0, m_flZoneTotalSync2[currentZone] / float(m_nZoneAvgCount[currentZone]));
        m_RunStats.SetZoneVelocityAvg(0, 
            m_flZoneTotalVelocity[currentZone][0] / float(m_nZoneAvgCount[currentZone]), 
            m_flZoneTotalVelocity[currentZone][1] / float(m_nZoneAvgCount[currentZone]));
    }

    // think once per 0.1 second interval so we avoid making the totals extremely large
    SetNextThink(gpGlobals->curtime + AVERAGE_STATS_INTERVAL, "THINK_AVERAGE_STATS");
}
예제 #8
0
void CSDKPlayer::UpdateViewBobRamp()
{
#ifdef CLIENT_DLL
	// It's not filled in for remote clients, so force the local one since it's the same.
	float flMaxBobSpeed = C_SDKPlayer::GetLocalSDKPlayer()->m_Shared.m_flRunSpeed*0.7f;
#else
	float flMaxBobSpeed = m_Shared.m_flRunSpeed*0.7f;
#endif

	float flBobRampGoal = RemapValClamped(GetLocalVelocity().LengthSqr(), 0, flMaxBobSpeed*flMaxBobSpeed, 0, 1);
	if (!(GetFlags() & FL_ONGROUND) || m_Shared.IsRolling() || m_Shared.IsSliding())
		flBobRampGoal = 0;

	m_Shared.m_flViewBobRamp = Approach(flBobRampGoal, m_Shared.m_flViewBobRamp, gpGlobals->frametime*m_flSlowMoMultiplier*4);
}
예제 #9
0
void CCharacter::Jump()
{
	if (!GetGroundEntity())
		return;

	SetGroundEntity(NULL);

	Vector vecLocalUp = GetUpVector();
	if (HasMoveParent())
	{
		TMatrix mGlobalToLocal = GetMoveParent()->GetGlobalToLocalTransform();
		vecLocalUp = mGlobalToLocal.TransformNoTranslate(vecLocalUp);
	}

	SetLocalVelocity(GetLocalVelocity() + vecLocalUp * JumpStrength());
}
예제 #10
0
void C_SkeletonPlayer::UpdateClientSideAnimation()
{
	int curSeq = GetSequence();
	
	Vector vel = GetLocalVelocity();
	//EstimateAbsVelocity( vel );

	int goalSeq = curSeq;

	if ( vel.LengthSqr() > 4 )
	{
		QAngle velAng;
		VectorAngles( vel, velAng );

		goalSeq = SelectWeightedSequence( ACT_RUN );

		float speed = vel.Length2D();
		float yaw = AngleNormalize( -(GetRenderAngles().y - velAng.y) );
		float seqspeed = 150.0f;
		float rate = speed / seqspeed;

		SetPoseParameter( LookupPoseParameter( "move_x" ), cos( DEG2RAD( yaw ) ) * rate );
		SetPoseParameter( LookupPoseParameter( "move_y" ), -sin( DEG2RAD( yaw ) ) * rate );

		SetPlaybackRate( clamp( rate * 0.6f, 1, 1.5f ) );
	}
	else
		goalSeq = SelectWeightedSequence( ACT_IDLE );

	if ( curSeq != goalSeq )
	{
		ResetSequence( goalSeq );
	}

	//m_flAnimTime = gpGlobals->curtime;
	//StudioFrameAdvance();

	if ( GetCycle() >= 1.0f )
		SetCycle( GetCycle() - 1.0f );
}
예제 #11
0
//-----------------------------------------------------------------------------
//	Little hack to avoid game crash when changing bodygroup (DmitRex)
//-----------------------------------------------------------------------------
void CZombie::SetHeadlessModel( void )
{
	SetModel("");
	CreateRagGib( "models/zombie/classic.mdl", GetLocalOrigin(), GetLocalAngles(), GetLocalVelocity(), 0, ShouldIgniteZombieGib() );
}
예제 #12
0
//-----------------------------------------------------------------------------
// Client-side obstacle avoidance
//-----------------------------------------------------------------------------
void C_BaseHLPlayer::PerformClientSideObstacleAvoidance( float flFrameTime, CUserCmd *pCmd )
{
	// Don't avoid if noclipping or in movetype none
	switch ( GetMoveType() )
	{
	case MOVETYPE_NOCLIP:
	case MOVETYPE_NONE:
	case MOVETYPE_OBSERVER:
		return;
	default:
		break;
	}

	// Try to steer away from any objects/players we might interpenetrate
	Vector size = WorldAlignSize();

	float radius = 0.7f * sqrt( size.x * size.x + size.y * size.y );
	float curspeed = GetLocalVelocity().Length2D();

	//int slot = 1;
	//engine->Con_NPrintf( slot++, "speed %f\n", curspeed );
	//engine->Con_NPrintf( slot++, "radius %f\n", radius );

	// If running, use a larger radius
	float factor = 1.0f;

	if ( curspeed > 150.0f )
	{
		curspeed = MIN( 2048.0f, curspeed );
		factor = ( 1.0f + ( curspeed - 150.0f ) / 150.0f );

		//engine->Con_NPrintf( slot++, "scaleup (%f) to radius %f\n", factor, radius * factor );

		radius = radius * factor;
	}

	Vector currentdir;
	Vector rightdir;

	QAngle vAngles = pCmd->viewangles;
	vAngles.x = 0;

	AngleVectors( vAngles, &currentdir, &rightdir, NULL );
		
	bool istryingtomove = false;
	bool ismovingforward = false;
	if ( fabs( pCmd->forwardmove ) > 0.0f || 
		fabs( pCmd->sidemove ) > 0.0f )
	{
		istryingtomove = true;
		if ( pCmd->forwardmove > 1.0f )
		{
			ismovingforward = true;
		}
	}

	if ( istryingtomove == true )
		 radius *= 1.3f;

	CPlayerAndObjectEnumerator avoid( radius );
	partition->EnumerateElementsInSphere( PARTITION_CLIENT_SOLID_EDICTS, GetAbsOrigin(), radius, false, &avoid );

	// Okay, decide how to avoid if there's anything close by
	int c = avoid.GetObjectCount();
	if ( c <= 0 )
		return;

	//engine->Con_NPrintf( slot++, "moving %s forward %s\n", istryingtomove ? "true" : "false", ismovingforward ? "true" : "false"  );

	float adjustforwardmove = 0.0f;
	float adjustsidemove	= 0.0f;

	for ( int i = 0; i < c; i++ )
	{
		C_AI_BaseNPC *obj = dynamic_cast< C_AI_BaseNPC *>(avoid.GetObject( i ));

		if( !obj )
			continue;

		Vector vecToObject = obj->GetAbsOrigin() - GetAbsOrigin();

		float flDist = vecToObject.Length2D();
		
		// Figure out a 2D radius for the object
		Vector vecWorldMins, vecWorldMaxs;
		obj->CollisionProp()->WorldSpaceAABB( &vecWorldMins, &vecWorldMaxs );
		Vector objSize = vecWorldMaxs - vecWorldMins;

		float objectradius = 0.5f * sqrt( objSize.x * objSize.x + objSize.y * objSize.y );

		//Don't run this code if the NPC is not moving UNLESS we are in stuck inside of them.
		if ( !obj->IsMoving() && flDist > objectradius )
			  continue;

		if ( flDist > objectradius && obj->IsEffectActive( EF_NODRAW ) )
		{
			obj->RemoveEffects( EF_NODRAW );
		}

		Vector vecNPCVelocity;
		obj->EstimateAbsVelocity( vecNPCVelocity );
		float flNPCSpeed = VectorNormalize( vecNPCVelocity );

		Vector vPlayerVel = GetAbsVelocity();
		VectorNormalize( vPlayerVel );

		float flHit1, flHit2;
		Vector vRayDir = vecToObject;
		VectorNormalize( vRayDir );

		float flVelProduct = DotProduct( vecNPCVelocity, vPlayerVel );
		float flDirProduct = DotProduct( vRayDir, vPlayerVel );

		if ( !IntersectInfiniteRayWithSphere(
				GetAbsOrigin(),
				vRayDir,
				obj->GetAbsOrigin(),
				radius,
				&flHit1,
				&flHit2 ) )
			continue;

        Vector dirToObject = -vecToObject;
		VectorNormalize( dirToObject );

		float fwd = 0;
		float rt = 0;

		float sidescale = 2.0f;
		float forwardscale = 1.0f;
		bool foundResult = false;

		Vector vMoveDir = vecNPCVelocity;
		if ( flNPCSpeed > 0.001f )
		{
			// This NPC is moving. First try deflecting the player left or right relative to the NPC's velocity.
			// Start with whatever side they're on relative to the NPC's velocity.
			Vector vecNPCTrajectoryRight = CrossProduct( vecNPCVelocity, Vector( 0, 0, 1) );
			int iDirection = ( vecNPCTrajectoryRight.Dot( dirToObject ) > 0 ) ? 1 : -1;
			for ( int nTries = 0; nTries < 2; nTries++ )
			{
				Vector vecTryMove = vecNPCTrajectoryRight * iDirection;
				VectorNormalize( vecTryMove );
				
				Vector vTestPosition = GetAbsOrigin() + vecTryMove * radius * 2;

				if ( TestMove( vTestPosition, size.z * 2, radius * 2, obj->GetAbsOrigin(), vMoveDir ) )
				{
					fwd = currentdir.Dot( vecTryMove );
					rt = rightdir.Dot( vecTryMove );
					
					//Msg( "PUSH DEFLECT fwd=%f, rt=%f\n", fwd, rt );
					
					foundResult = true;
					break;
				}
				else
				{
					// Try the other direction.
					iDirection *= -1;
				}
			}
		}
		else
		{
			// the object isn't moving, so try moving opposite the way it's facing
			Vector vecNPCForward;
			obj->GetVectors( &vecNPCForward, NULL, NULL );
			
			Vector vTestPosition = GetAbsOrigin() - vecNPCForward * radius * 2;
			if ( TestMove( vTestPosition, size.z * 2, radius * 2, obj->GetAbsOrigin(), vMoveDir ) )
			{
				fwd = currentdir.Dot( -vecNPCForward );
				rt = rightdir.Dot( -vecNPCForward );

				if ( flDist < objectradius )
				{
					obj->AddEffects( EF_NODRAW );
				}

				//Msg( "PUSH AWAY FACE fwd=%f, rt=%f\n", fwd, rt );

				foundResult = true;
			}
		}

		if ( !foundResult )
		{
			// test if we can move in the direction the object is moving
			Vector vTestPosition = GetAbsOrigin() + vMoveDir * radius * 2;
			if ( TestMove( vTestPosition, size.z * 2, radius * 2, obj->GetAbsOrigin(), vMoveDir ) )
			{
				fwd = currentdir.Dot( vMoveDir );
				rt = rightdir.Dot( vMoveDir );

				if ( flDist < objectradius )
				{
					obj->AddEffects( EF_NODRAW );
				}

				//Msg( "PUSH ALONG fwd=%f, rt=%f\n", fwd, rt );

				foundResult = true;
			}
			else
			{
				// try moving directly away from the object
				Vector vTestPosition = GetAbsOrigin() - dirToObject * radius * 2;
				if ( TestMove( vTestPosition, size.z * 2, radius * 2, obj->GetAbsOrigin(), vMoveDir ) )
				{
					fwd = currentdir.Dot( -dirToObject );
					rt = rightdir.Dot( -dirToObject );
					foundResult = true;

					//Msg( "PUSH AWAY fwd=%f, rt=%f\n", fwd, rt );
				}
			}
		}

		if ( !foundResult )
		{
			// test if we can move through the object
			Vector vTestPosition = GetAbsOrigin() - vMoveDir * radius * 2;
			fwd = currentdir.Dot( -vMoveDir );
			rt = rightdir.Dot( -vMoveDir );

			if ( flDist < objectradius )
			{
				obj->AddEffects( EF_NODRAW );
			}

			//Msg( "PUSH THROUGH fwd=%f, rt=%f\n", fwd, rt );

			foundResult = true;
		}

		// If running, then do a lot more sideways veer since we're not going to do anything to
		//  forward velocity
		if ( istryingtomove )
		{
			sidescale = 6.0f;
		}

		if ( flVelProduct > 0.0f && flDirProduct > 0.0f )
		{
			sidescale = 0.1f;
		}

		float force = 1.0f;
		float forward = forwardscale * fwd * force * AVOID_SPEED;
		float side = sidescale * rt * force * AVOID_SPEED;

		adjustforwardmove	+= forward;
		adjustsidemove		+= side;
	}

	pCmd->forwardmove	+= adjustforwardmove;
	pCmd->sidemove		+= adjustsidemove;
	
	// Clamp the move to within legal limits, preserving direction. This is a little
	// complicated because we have different limits for forward, back, and side

	//Msg( "PRECLAMP: forwardmove=%f, sidemove=%f\n", pCmd->forwardmove, pCmd->sidemove );

	float flForwardScale = 1.0f;
	if ( pCmd->forwardmove > fabs( cl_forwardspeed.GetFloat() ) )
	{
		flForwardScale = fabs( cl_forwardspeed.GetFloat() ) / pCmd->forwardmove;
	}
	else if ( pCmd->forwardmove < -fabs( cl_backspeed.GetFloat() ) )
	{
		flForwardScale = fabs( cl_backspeed.GetFloat() ) / fabs( pCmd->forwardmove );
	}
	
	float flSideScale = 1.0f;
	if ( fabs( pCmd->sidemove ) > fabs( cl_sidespeed.GetFloat() ) )
	{
		flSideScale = fabs( cl_sidespeed.GetFloat() ) / fabs( pCmd->sidemove );
	}
	
	float flScale = MIN( flForwardScale, flSideScale );
	pCmd->forwardmove *= flScale;
	pCmd->sidemove *= flScale;

	//Msg( "POSTCLAMP: forwardmove=%f, sidemove=%f\n", pCmd->forwardmove, pCmd->sidemove );
}
예제 #13
0
파일: mom_player.cpp 프로젝트: Asunaya/game
void CMomentumPlayer::UpdateRunStats()
{
    float velocity = GetLocalVelocity().Length();
    float velocity2D = GetLocalVelocity().Length2D();

    if (g_Timer->IsRunning())
    {
        int currentZone = m_RunData.m_iCurrentZone;//g_Timer->GetCurrentZoneNumber();
        if (!m_bPrevTimerRunning) // timer started on this tick
        {
            // Compare against successive bhops to avoid incrimenting when the player was in the air without jumping
            // (for surf)
            if (GetGroundEntity() == nullptr && m_iSuccessiveBhops)
            {
                m_RunStats.SetZoneJumps(0, m_RunStats.GetZoneJumps(0) + 1);
                m_RunStats.SetZoneJumps(currentZone, m_RunStats.GetZoneJumps(currentZone) + 1);
            }
            if (m_nButtons & IN_MOVERIGHT || m_nButtons & IN_MOVELEFT)
            {
                m_RunStats.SetZoneStrafes(0, m_RunStats.GetZoneStrafes(0) + 1);
                m_RunStats.SetZoneStrafes(currentZone, m_RunStats.GetZoneStrafes(currentZone) + 1);
            }
        }
        if (m_nButtons & IN_MOVELEFT && !(m_nPrevButtons & IN_MOVELEFT))
        {
            m_RunStats.SetZoneStrafes(0, m_RunStats.GetZoneStrafes(0) + 1);
            m_RunStats.SetZoneStrafes(currentZone, m_RunStats.GetZoneStrafes(currentZone) + 1);
        }
        else if (m_nButtons & IN_MOVERIGHT && !(m_nPrevButtons & IN_MOVERIGHT))
        {
            m_RunStats.SetZoneStrafes(0, m_RunStats.GetZoneStrafes(0) + 1);
            m_RunStats.SetZoneStrafes(currentZone, m_RunStats.GetZoneStrafes(currentZone) + 1);
        }
        //  ---- MAX VELOCITY ----
        float maxOverallVel = velocity;
        float maxOverallVel2D = velocity2D;

        float maxCurrentVel = velocity;
        float maxCurrentVel2D = velocity2D;

        if (maxOverallVel <= m_RunStats.GetZoneVelocityMax(0, false))
            maxOverallVel = m_RunStats.GetZoneVelocityMax(0, false);

        if (maxOverallVel2D <= m_RunStats.GetZoneVelocityMax(0, true))
            maxOverallVel2D = m_RunStats.GetZoneVelocityMax(0, true);

        if (maxCurrentVel <= m_RunStats.GetZoneVelocityMax(currentZone, false))
            maxCurrentVel = m_RunStats.GetZoneVelocityMax(currentZone, false);

        if (maxCurrentVel2D <= m_RunStats.GetZoneVelocityMax(currentZone, true))
            maxCurrentVel2D = m_RunStats.GetZoneVelocityMax(currentZone, true);

        m_RunStats.SetZoneVelocityMax(0, maxOverallVel, maxOverallVel2D);
        m_RunStats.SetZoneVelocityMax(currentZone, maxCurrentVel, maxCurrentVel2D);
        // ----------

        //  ---- STRAFE SYNC -----
        float SyncVelocity = GetLocalVelocity().Length2DSqr(); // we always want HVEL for checking velocity sync
        if (!(GetFlags() & (FL_ONGROUND | FL_INWATER)) && GetMoveType() != MOVETYPE_LADDER)
        {
            if (EyeAngles().y > m_qangLastAngle.y) // player turned left
            {
                m_nStrafeTicks++;
                if ((m_nButtons & IN_MOVELEFT) && !(m_nButtons & IN_MOVERIGHT))
                    m_nPerfectSyncTicks++;
                if (SyncVelocity > m_flLastSyncVelocity)
                    m_nAccelTicks++;
            }
            else if (EyeAngles().y < m_qangLastAngle.y) // player turned right
            {
                m_nStrafeTicks++;
                if ((m_nButtons & IN_MOVERIGHT) && !(m_nButtons & IN_MOVELEFT))
                    m_nPerfectSyncTicks++;
                if (SyncVelocity > m_flLastSyncVelocity)
                    m_nAccelTicks++;
            }
        }
        if (m_nStrafeTicks && m_nAccelTicks && m_nPerfectSyncTicks)
        {
            m_RunData.m_flStrafeSync = (float(m_nPerfectSyncTicks) / float(m_nStrafeTicks)) *
                                       100.0f; // ticks strafing perfectly / ticks strafing
            m_RunData.m_flStrafeSync2 =
                (float(m_nAccelTicks) / float(m_nStrafeTicks)) * 100.0f; // ticks gaining speed / ticks strafing
        }
        // ----------

        m_qangLastAngle = EyeAngles();
        m_flLastSyncVelocity = SyncVelocity;
        // this might be used in a later update
        // m_flLastVelocity = velocity;

        m_bPrevTimerRunning = g_Timer->IsRunning();
        m_nPrevButtons = m_nButtons;
    }

    // think once per tick
    SetNextThink(gpGlobals->curtime + gpGlobals->interval_per_tick, "THINK_EVERY_TICK");
}
예제 #14
0
//-----------------------------------------------------------------------------
// Purpose: Returns the velocity imparted to players standing on us.
//-----------------------------------------------------------------------------
void CBaseToggle::GetGroundVelocityToApply( Vector &vecGroundVel )
{
	vecGroundVel = GetLocalVelocity();
}
예제 #15
0
void CCharacter::MoveThink()
{
	if (!GetGroundEntity())
		return;

	if (m_vecGoalVelocity.LengthSqr())
		m_vecGoalVelocity.Normalize();

	m_vecMoveVelocity.x = Approach(m_vecGoalVelocity.x, m_vecMoveVelocity.x, GameServer()->GetFrameTime()*4);
	m_vecMoveVelocity.y = 0;
	m_vecMoveVelocity.z = Approach(m_vecGoalVelocity.z, m_vecMoveVelocity.z, GameServer()->GetFrameTime()*4);

	if (m_vecMoveVelocity.LengthSqr() > 0)
	{
		TMatrix m = GetLocalTransform();

		Vector vecUp = GetUpVector();
		
		if (HasMoveParent())
		{
			TMatrix mGlobalToLocal = GetMoveParent()->GetGlobalToLocalTransform();
			vecUp = mGlobalToLocal.TransformNoTranslate(vecUp);
		}

		Vector vecRight = m.GetForwardVector().Cross(vecUp).Normalized();
		Vector vecForward = vecUp.Cross(vecRight).Normalized();
		m.SetColumn(0, vecForward);
		m.SetColumn(1, vecUp);
		m.SetColumn(2, vecRight);

		TVector vecMove = m_vecMoveVelocity * CharacterSpeed();
		TVector vecLocalVelocity = m.TransformNoTranslate(vecMove);

		SetLocalVelocity(vecLocalVelocity);
	}
	else
		SetLocalVelocity(TVector());

	eastl::vector<CEntityHandle<CBaseEntity> > apCollisionList;

	size_t iMaxEntities = GameServer()->GetMaxEntities();
	for (size_t j = 0; j < iMaxEntities; j++)
	{
		CBaseEntity* pEntity2 = CBaseEntity::GetEntity(j);

		if (!pEntity2)
			continue;

		if (pEntity2->IsDeleted())
			continue;

		if (pEntity2 == this)
			continue;

		if (!pEntity2->ShouldCollide())
			continue;

		apCollisionList.push_back(pEntity2);
	}

	TMatrix mGlobalToLocalRotation;
	if (HasMoveParent())
	{
		mGlobalToLocalRotation = GetMoveParent()->GetGlobalToLocalTransform();
		mGlobalToLocalRotation.SetTranslation(TVector());
	}

	float flSimulationFrameTime = 0.01f;

	// Break simulations up into consistent small steps to preserve accuracy.
	for (; m_flMoveSimulationTime < GameServer()->GetGameTime(); m_flMoveSimulationTime += flSimulationFrameTime)
	{
		TVector vecVelocity = GetLocalVelocity();

		TVector vecLocalOrigin = GetLocalOrigin();
		TVector vecGlobalOrigin = GetGlobalOrigin();

		vecVelocity = vecVelocity * flSimulationFrameTime;

		TVector vecLocalDestination = vecLocalOrigin + vecVelocity;
		TVector vecGlobalDestination = vecLocalDestination;
		if (GetMoveParent())
			vecGlobalDestination = GetMoveParent()->GetGlobalTransform() * vecLocalDestination;

		TVector vecNewLocalOrigin = vecLocalDestination;

		size_t iTries = 0;
		while (true)
		{
			iTries++;

			TVector vecPoint, vecNormal;

			TVector vecLocalCollisionPoint, vecGlobalCollisionPoint;

			bool bContact = false;
			for (size_t i = 0; i < apCollisionList.size(); i++)
			{
				CBaseEntity* pEntity2 = apCollisionList[i];

				if (GetMoveParent() == pEntity2)
				{
					if (pEntity2->CollideLocal(vecLocalOrigin, vecLocalDestination, vecPoint, vecNormal))
					{
						bContact = true;
						Touching(pEntity2);
						vecLocalCollisionPoint = vecPoint;
						vecGlobalCollisionPoint = GetMoveParent()->GetGlobalTransform() * vecPoint;
					}
				}
				else
				{
					if (pEntity2->Collide(vecGlobalOrigin, vecGlobalDestination, vecPoint, vecNormal))
					{
						bContact = true;
						Touching(pEntity2);
						vecGlobalCollisionPoint = vecPoint;
						if (GetMoveParent())
						{
							vecLocalCollisionPoint = GetMoveParent()->GetGlobalToLocalTransform() * vecPoint;
							vecNormal = GetMoveParent()->GetGlobalToLocalTransform().TransformNoTranslate(vecNormal);
						}
						else
							vecLocalCollisionPoint = vecGlobalCollisionPoint;
					}
				}
			}

			if (bContact)
			{
				vecNewLocalOrigin = vecLocalCollisionPoint;
				vecVelocity -= vecLocalCollisionPoint - vecLocalOrigin;
			}

			if (!bContact)
				break;

			if (iTries > 4)
				break;

			vecLocalOrigin = vecLocalCollisionPoint;
			vecGlobalOrigin = vecGlobalCollisionPoint;

			// Clip the velocity to the surface normal of whatever we hit.
			TFloat flDistance = vecVelocity.Dot(vecNormal);

			vecVelocity = vecVelocity - vecNormal * flDistance;

			// Do it one more time just to make sure we're not headed towards the plane.
			TFloat flAdjust = vecVelocity.Dot(vecNormal);
			if (flAdjust < 0.0f)
				vecVelocity -= (vecNormal * flAdjust);

			vecLocalDestination = vecLocalOrigin + vecVelocity;

			if (GetMoveParent())
				vecGlobalDestination = GetMoveParent()->GetGlobalTransform() * vecLocalDestination;
			else
				vecGlobalDestination = vecLocalDestination;

			SetLocalVelocity(vecVelocity.Normalized() * GetLocalVelocity().Length());
		}

		SetLocalOrigin(vecNewLocalOrigin);

		// Try to keep the player on the ground.
		// Untested.
		/*TVector vecStart = GetGlobalOrigin() + GetGlobalTransform().GetUpVector()*m_flMaxStepSize;
		TVector vecEnd = GetGlobalOrigin() - GetGlobalTransform().GetUpVector()*m_flMaxStepSize;

		// First go up a bit
		TVector vecHit, vecNormal;
		Game()->TraceLine(GetGlobalOrigin(), vecStart, vecHit, vecNormal, NULL);
		vecStart = vecHit;

		// Now see if there's ground underneath us.
		bool bHit = Game()->TraceLine(vecStart, vecEnd, vecHit, vecNormal, NULL);
		if (bHit && vecNormal.y >= TFloat(0.7f))
			SetGlobalOrigin(vecHit);*/

		m_flMoveSimulationTime += flSimulationFrameTime;
	}
}
예제 #16
0
//Called from PhysicsSimulate() or ReceiveMessage()
bool CDHLProjectile::OnTouch( trace_t &touchtr, bool bDecalOnly /*= false*/, ITraceFilter* pTraceFilter /*= NULL*/ )
{
	//Direction
	Vector vecDir = touchtr.endpos - touchtr.startpos;
	if ( vecDir == vec3_origin ) //Sometimes endpos==startpos so we need to get dir from velocity instead
	{
		#ifdef CLIENT_DLL
			vecDir = GetLocalVelocity();
		#else
			vecDir = m_vecCurVelocity;
		#endif
		VectorNormalize( vecDir );
	}

	CBaseEntity* ent = touchtr.m_pEnt;
	if ( !ent )
		return false;

	if ( touchtr.DidHit() )
	{
		//Never collide with self, shooter, or other projectiles
		if ( ent == this || dynamic_cast<CDHLProjectile*>(ent)
			|| ent == (CBaseEntity*)m_pShooter )
			//|| ( (m_iType == DHL_PROJECTILE_TYPE_COMBATKNIFE) && (ent == m_pFiringWeapon) ) ) //Combat knife - don't collide with weapon ent
			return false;

		//Hack: Sometimes hits are registered prematurely (usually to the torso area) with no hitbox.  Pretend nothing happened unless one is found.
		if ( ent->IsPlayer() && touchtr.hitgroup == 0 )
			return false;

		//Check friendly fire
		if ( CheckFriendlyFire( ent ) )
		{
			if ( !bDecalOnly )
			{
				ClearMultiDamage();

				//Do damage
				CTakeDamageInfo	dmgInfo( this, GetOwnerEntity(), m_iDamage, DMG_BULLET  );
				if ( m_iType == DHL_PROJECTILE_TYPE_COMBATKNIFE )
				{
					//CalculateMeleeDamageForce( &dmgInfo, vecDir, touchtr.endpos, 0.01f );
					Vector vecForce = vecDir;
					VectorNormalize( vecForce );
					//vecForce *= 10.0f; //Ripped from C_ClientRagdoll::ImpactTrace
					dmgInfo.SetDamageForce( vecForce );

					#ifndef CLIENT_DLL
						if ( IsOnFire() )
						{
							CBaseAnimating* pBAnim = dynamic_cast<CBaseAnimating*>(ent);
							if ( pBAnim )
								pBAnim->Ignite( 10.0f, false );
						}
					#endif
				}
				else
					CalculateBulletDamageForce( &dmgInfo, m_iAmmoType, vecDir, touchtr.endpos, 1.0f );
				dmgInfo.SetDamagePosition( touchtr.endpos );
				ent->DispatchTraceAttack( dmgInfo, vecDir, &touchtr );
				ApplyMultiDamage();
			}

			#ifdef CLIENT_DLL
				if ( ent->GetCollisionGroup() == COLLISION_GROUP_BREAKABLE_GLASS )
					return false;

				//Decals and such
				if ( !( touchtr.surface.flags & SURF_SKY ) && !touchtr.allsolid )
				{
					IPredictionSystem::SuppressEvents( false );
					if ( (m_iType == DHL_PROJECTILE_TYPE_BULLET || m_iType == DHL_PROJECTILE_TYPE_PELLET) )
					{
						UTIL_ImpactTrace( &touchtr, DMG_BULLET );
					}
					if ( m_iType == DHL_PROJECTILE_TYPE_COMBATKNIFE )
						PlayImpactSound( touchtr.m_pEnt, touchtr, touchtr.endpos, touchtr.surface.surfaceProps );
					IPredictionSystem::SuppressEvents( !prediction->IsFirstTimePredicted() );
				}
			#endif
		}

		if ( pTraceFilter && m_iType != DHL_PROJECTILE_TYPE_COMBATKNIFE )
		{
			PenetrationData_t nPenetrationData = DHLShared::TestPenetration( touchtr, m_pShooter, pTraceFilter, 
				m_iTimesPenetrated, m_flDistanceTravelled, m_iAmmoType );
			if ( nPenetrationData.m_bShouldPenetrate )
			{
				m_flDistanceTravelled += GetLocalOrigin().DistTo( nPenetrationData.m_vecNewBulletPos );
				MoveProjectileToPosition( nPenetrationData.m_vecNewBulletPos );
				m_iTimesPenetrated++;
				return true; //Keep going - but don't do anything else in this frame of PhysicsSimulate()
			}
		}
	
		//We're done unless what we hit was breakable glass
		if ( ent->GetCollisionGroup() != COLLISION_GROUP_BREAKABLE_GLASS )
		{
			#ifdef CLIENT_DLL
				m_bCollided = true;
				AddEffects( EF_NODRAW );
				if ( m_pTrail ) //NULL pointer here sometimes somehow...
					m_pTrail->AddEffects( EF_NODRAW );
			#else
				EntityMessageBegin( this );
					WRITE_BYTE( MSG_NOTIFY_REMOVAL );
				MessageEnd();
				
				if ( touchtr.DidHitWorld() && m_iType == DHL_PROJECTILE_TYPE_COMBATKNIFE && !( touchtr.surface.flags & SURF_SKY ) )
				{
					CBaseCombatWeapon* pKnifeEnt = assert_cast<CBaseCombatWeapon*>(CreateEntityByName( "weapon_combatknife" ));
					if ( pKnifeEnt )
					{
						pKnifeEnt->AddSpawnFlags( SF_NORESPAWN ); //Needed for weapon spawn & VPhysics setup to work correctly
						pKnifeEnt->SetAbsOrigin( touchtr.endpos );
						QAngle angles = vec3_angle;
						Vector vecKnifeDir = touchtr.startpos - touchtr.endpos;
						VectorAngles( vecKnifeDir, angles );
						angles[PITCH] -= 15.0f; //Correct for the .mdl being offset a bit
						pKnifeEnt->SetLocalAngles( angles );
						DispatchSpawn( pKnifeEnt );

						//Spawns vphys object and sets it up, essentially a copy of CWeaponHL2MPBase::FallInit()
						pKnifeEnt->VPhysicsDestroyObject();
						//Using SOLID_VPHYSICS instead of SOLID_BBOX (as ordinary weapons do) helps resolve some of the client side collision oddities
						Assert( pKnifeEnt->VPhysicsInitNormal( SOLID_VPHYSICS, FSOLID_NOT_STANDABLE | FSOLID_TRIGGER, true ) );
						pKnifeEnt->SetPickupTouch(); //Sets up automagic removal after time
						IPhysicsObject* pKnifePhys = pKnifeEnt->VPhysicsGetObject();
						if ( pKnifePhys )
						{
							//Knives are solid to bullets...the only way to make them non-solid to bullets is to do SetSolid( SOLID_NONE ) or AddSolidFlags( FSOLID_NOT_SOLID )
							//which breaks the +use pickup even with FSOLID_TRIGGER set.  Let's just call it a feature :)
							pKnifePhys->EnableMotion( false );
							pKnifePhys->EnableCollisions( false );
						}

						if ( IsOnFire() )
							pKnifeEnt->Ignite( 10.0f, false );
					}
				}

				//SetThink( &CDHLProjectile::SUB_Remove );
				//SetNextThink( gpGlobals->curtime + 0.1 );
				//SUB_Remove();
				//SetMoveType( MOVETYPE_NONE );
				m_flRemoveAt = gpGlobals->curtime + 0.1f; //Give the notification message a head start so that the client will have time to react
			#endif
		}
	}
	return true;
}
예제 #17
0
void CDHLProjectile::PhysicsSimulate( void )
{
	//-------------------------------------------------------------------------------
	//Our own movement/physics simulation!
	//-------------------------------------------------------------------------------
	#ifdef CLIENT_DLL
		if ( m_bCollided )
			return;

		if ( !m_pShooter && m_hShooter )
			m_pShooter = m_hShooter.Get();
	#else
		if ( m_flRemoveAt > 0.0f )
		{
			if ( m_flRemoveAt < gpGlobals->curtime )
			{
				m_flRemoveAt = 0.0f;
				SUB_Remove();
			}
			return;
		}
		if ( IsMarkedForDeletion() )
			return;
	#endif

	float flFrametime = gpGlobals->frametime;
	//Scale for slow motion
	if ( DHLRules() )
	{
		if ( (m_iType == DHL_PROJECTILE_TYPE_BULLET || m_iType == DHL_PROJECTILE_TYPE_PELLET) )
			flFrametime *= (dhl_bulletspeed.GetFloat() * DHLRules()->GetTimescale());
		else if ( m_iType == DHL_PROJECTILE_TYPE_COMBATKNIFE )
			flFrametime *= (dhl_knifespeed.GetFloat() * DHLRules()->GetTimescale());
		else
			flFrametime *= DHLRules()->GetTimescale();
	}

	Vector vecDir = vec3_origin;
#ifndef CLIENT_DLL
	Vector vecStartPos = m_vecCurPosition; //This is where we are
	Vector vecEndPos = m_vecCurPosition; //This is where we're going
	Vector vecVelocity = m_vecCurVelocity; //Velocity
#else
	Vector vecStartPos = GetLocalOrigin(); //This is where we are
	Vector vecEndPos = GetLocalOrigin(); //This is where we're going
	Vector vecVelocity = GetLocalVelocity(); //Velocity
#endif
	//Find out where we should move to
	if ( vecVelocity != vec3_origin )
	{
		static ConVarRef gravVar( "sv_gravity" );
		//Gravity
		float newZVelocity = vecVelocity.z - ( flFrametime * gravVar.GetFloat() * GetGravity() );
		vecVelocity.z = ( (vecVelocity.z + newZVelocity) / 2 );

		vecDir = vecVelocity;
		VectorNormalize( vecDir );

		//Gravity needs to be cumulative
		#ifndef CLIENT_DLL
			m_vecCurVelocity = vecVelocity;
		#else
			SetLocalVelocity( vecVelocity );
		#endif
		vecVelocity *= flFrametime;
		vecEndPos = vecStartPos + vecVelocity;
		if ( vecEndPos.IsValid() )
		{
			CTraceFilterSkipTwoEntities movetrfilter( this, m_pShooter, COLLISION_GROUP_NONE );
			trace_t movetr;
			UTIL_TraceLine( vecStartPos, vecEndPos, MASK_SHOT, &movetrfilter, &movetr );

			#ifndef CLIENT_DLL
				//Trace to triggers so we can hit surf glass and such
				CTakeDamageInfo	triggerInfo( this, GetOwnerEntity(), m_iDamage, DMG_BULLET );
				if ( m_iType == DHL_PROJECTILE_TYPE_COMBATKNIFE )
				{
					//CalculateMeleeDamageForce( &triggerInfo, vecDir, movetr.endpos, 0.7f );
					Vector vecForce = vecDir;
					VectorNormalize( vecForce );
					//vecForce *= 10.0f;
					triggerInfo.SetDamageForce( vecForce );
				}
				else
					CalculateBulletDamageForce( &triggerInfo, m_iAmmoType, vecDir, movetr.endpos, 1.0f );
				triggerInfo.SetDamagePosition( movetr.endpos );
				TraceAttackToTriggers( triggerInfo, movetr.startpos, movetr.endpos, vecDir );
			#else
				//Hit ragdolls on the client
				CBaseEntity* pEnt = DHL_FX_AffectRagdolls( movetr.endpos, movetr.startpos, DMG_BULLET, &m_RagdollHitList );

				//Keep track of ones we've hit
				if ( pEnt )
					m_RagdollHitList.AddToTail( pEnt );
			#endif

			if ( movetr.DidHit() )
				if ( OnTouch( movetr, false, &movetrfilter ) )
					return;
			
			MoveProjectileToPosition( vecEndPos );
			m_flDistanceTravelled += vecEndPos.DistTo( vecStartPos );

			#ifndef CLIENT_DLL
				//On rare occasions the projectile likes to fly right through the world and keep going forever, causing a memory leak
				if ( m_flDistanceTravelled > MAX_TRACE_LENGTH )
				{
					SUB_Remove();
					//SetThink( &CDHLProjectile::SUB_Remove );
					//SetNextThink( gpGlobals->curtime + 0.1 );
				}
			#endif

		}

		//Simulate Angles
		//QAngle angles;
		#ifdef CLIENT_DLL
			QAngle angles = GetLocalAngles();
			//VectorAngles( vecDir, angles );
			//angles.z = GetLocalAngles().z; //Vector conversion loses z
			QAngle angVel = GetLocalAngularVelocity();
			angles += angVel * flFrametime;
			SetLocalAngles( angles );
			SetNetworkAngles( angles );
		#endif
	}
}
void UPhysicsMovementComponent::ServerUpdateMovementVars()
{
    location = GetLocalLocation();
    velocity = GetLocalVelocity();
}
예제 #19
0
파일: mom_player.cpp 프로젝트: horse-f/game
void CMomentumPlayer::UpdateRunStats()
{
    //should velocity be XY or XYZ?
    ConVarRef hvel("mom_speedometer_hvel");
    IGameEvent *playerMoveEvent = gameeventmanager->CreateEvent("keypress");
    float velocity = hvel.GetBool() ? GetLocalVelocity().Length2D() : GetLocalVelocity().Length();

    if (g_Timer.IsRunning())
    {
        int currentStage = g_Timer.GetCurrentStageNumber();
        if (!m_bPrevTimerRunning) //timer started on this tick
        {    
            //Reset old run stats
            ResetRunStats();
            m_flStartSpeed = GetLocalVelocity().Length2D(); //prestrafe should always be XY only
            //Comapre against successive bhops to avoid incrimenting when the player was in the air without jumping (for surf)
            if (GetGroundEntity() == NULL && m_iSuccessiveBhops)
            {
                m_nStageJumps[0]++;
            }
        }
        if (m_nButtons & IN_MOVELEFT && !(m_nPrevButtons & IN_MOVELEFT))
        {
            m_nStageStrafes[0]++;
            m_nStageStrafes[currentStage]++;
        }
        else if (m_nButtons & IN_MOVERIGHT && !(m_nPrevButtons & IN_MOVERIGHT))
        {
            m_nStageStrafes[0]++;
            m_nStageStrafes[currentStage]++;
        }
        //  ---- MAX VELOCITY ----
        if (velocity > m_flStageVelocityMax[0])
            m_flStageVelocityMax[0] = velocity;
        //also do max velocity per stage
        if (velocity > m_flStageVelocityMax[currentStage])
            m_flStageVelocityMax[currentStage] = velocity;
        // ----------

        // --- STAGE ENTER VELOCITY ---
    }   
    //  ---- STRAFE SYNC -----
    float SyncVelocity = GetLocalVelocity().Length2DSqr(); //we always want HVEL for checking velocity sync
    if (!(GetFlags() & (FL_ONGROUND | FL_INWATER)) && GetMoveType() != MOVETYPE_LADDER)
    {
        if (EyeAngles().y > m_qangLastAngle.y) //player turned left 
        {
            m_nStrafeTicks++;
            if ((m_nButtons & IN_MOVELEFT) && !(m_nButtons & IN_MOVERIGHT))
                m_nPerfectSyncTicks++;
            if (SyncVelocity > m_flLastSyncVelocity)
                m_nAccelTicks++;
        }
        else if (EyeAngles().y < m_qangLastAngle.y) //player turned right 
        {
            m_nStrafeTicks++;
            if ((m_nButtons & IN_MOVERIGHT) && !(m_nButtons & IN_MOVELEFT))
                m_nPerfectSyncTicks++;
            if (SyncVelocity > m_flLastSyncVelocity)
                m_nAccelTicks++;
        }
    }
    if (m_nStrafeTicks && m_nAccelTicks && m_nPerfectSyncTicks)
    {
        m_flStrafeSync = ((float)m_nPerfectSyncTicks / (float)m_nStrafeTicks) * 100; // ticks strafing perfectly / ticks strafing
        m_flStrafeSync2 = ((float)m_nAccelTicks / (float)m_nStrafeTicks) * 100; // ticks gaining speed / ticks strafing
    }
    // ----------


    m_qangLastAngle = EyeAngles();
    m_flLastSyncVelocity = SyncVelocity;
    //this might be used in a later update
    //m_flLastVelocity = velocity;

    m_bPrevTimerRunning = g_Timer.IsRunning();
    m_nPrevButtons = m_nButtons;

    if (playerMoveEvent)
    {
        playerMoveEvent->SetInt("num_strafes", m_nStageStrafes[0]);
        playerMoveEvent->SetInt("num_jumps", m_nStageJumps[0]);
        if ((m_nButtons & IN_JUMP && GetGroundEntity() != NULL) || m_nButtons & IN_MOVELEFT | IN_MOVERIGHT)
            gameeventmanager->FireEvent(playerMoveEvent);
    }

    //think once per tick   
    SetNextThink(gpGlobals->curtime + gpGlobals->interval_per_tick, "THINK_EVERY_TICK");
}