示例#1
0
void CFlex::DoBodyLean( void )
{
	CAI_NPC *myNpc = MyNPCPointer( );

	if (myNpc)
	{
		Vector vecDelta;
		Vector vecPos;
		Vector vecOrigin = GetAbsOrigin();

		if (m_vecPrevOrigin == vec3_origin)
		{
			m_vecPrevOrigin = vecOrigin;
		}

		vecDelta = vecOrigin - m_vecPrevOrigin;
		vecDelta.x = clamp( vecDelta.x, -50, 50 );
		vecDelta.y = clamp( vecDelta.y, -50, 50 );
		vecDelta.z = clamp( vecDelta.z, -50, 50 );

		float dt = gpGlobals->curtime - GetLastThink();
		bool bSkip = ((GetFlags() & (FL_FLY | FL_SWIM)) != 0) || (GetMoveParent() != NULL) || (GetGroundEntity() == NULL) || (GetGroundEntity()->IsMoving());
		bSkip |= myNpc->TaskRanAutomovement() || (myNpc->GetVehicleEntity() != NULL);

		if (!bSkip)
		{
			if (vecDelta.LengthSqr() > m_vecPrevVelocity.LengthSqr())
			{
				float decay =  ExponentialDecay( 0.6, 0.1, dt );
				m_vecPrevVelocity = m_vecPrevVelocity * (decay) + vecDelta * (1.f - decay);
			}
			else
			{
				float decay =  ExponentialDecay( 0.4, 0.1, dt );
				m_vecPrevVelocity = m_vecPrevVelocity * (decay) + vecDelta * (1.f - decay);
			}

			vecPos = m_vecPrevOrigin + m_vecPrevVelocity;

			float decay =  ExponentialDecay( 0.5, 0.1, dt );
			m_vecShift = m_vecShift * (decay) + (vecOrigin - vecPos) * (1.f - decay); // FIXME: Scale this
			m_vecLean = (vecOrigin - vecPos) * 1.0; // FIXME: Scale this
		}
		else
		{
			m_vecPrevVelocity = vecDelta;
			float decay =  ExponentialDecay( 0.5, 0.1, dt );
			m_vecShift = m_vecLean * decay;
			m_vecLean = m_vecShift * decay;
 		}

		m_vecPrevOrigin = vecOrigin;

		/*
		DevMsg( "%.2f %.2f %.2f  (%.2f %.2f %.2f)\n", 
			m_vecLean.Get().x, m_vecLean.Get().y, m_vecLean.Get().z,
			vecDelta.x, vecDelta.y, vecDelta.z );
		*/
	}
}
示例#2
0
//-----------------------------------------------------------------------------
// Purpose: Turns a npc towards its ideal yaw.
// Input  : yawSpeed - Yaw speed in degrees per 1/10th of a second.
//			flInterval - Time interval to turn, -1 uses time since last think.
// Output : Returns the number of degrees turned.
//-----------------------------------------------------------------------------
void CAI_Motor::UpdateYaw( int yawSpeed )
{
	// Don't do this if our yaw is locked
	if ( IsYawLocked() )
		return;

	GetOuter()->SetUpdatedYaw();

	float ideal, current, newYaw;
	
	if ( yawSpeed == -1 )
		yawSpeed = GetYawSpeed();

	// NOTE: GetIdealYaw() will never exactly be reached because UTIL_AngleMod
	// also truncates the angle to 16 bits of resolution. So lets truncate it here.
	current = UTIL_AngleMod( GetLocalAngles().y );
	ideal = UTIL_AngleMod( GetIdealYaw() );

	// FIXME: this needs a proper interval
	float dt = MIN( 0.2, gpGlobals->curtime - GetLastThink() );
	
	newYaw = AI_ClampYaw( (float)yawSpeed * 10.0, current, ideal, dt );
		
	if (newYaw != current)
	{
		QAngle angles = GetLocalAngles();
		angles.y = newYaw;
		SetLocalAngles( angles );
	}
}
示例#3
0
void CWallHealth::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
{ 
	// Make sure that we have a caller
	if (!pActivator)
		return;
	// if it's not a player, ignore
	if ( !pActivator->IsPlayer() )
		return;

	// if there is no juice left, turn it off
	if (m_iJuice <= 0)
	{
		SetFrame( 1 );
		Off();
	}

	// if the player doesn't have the suit, or there is no juice left, make the deny noise
	if( ( m_iJuice <= 0 ) || ( !( pActivator->GetWeapons().Any( 1 << WEAPON_SUIT ) ) ) )
	{
		if (m_flSoundTime <= gpGlobals->time)
		{
			m_flSoundTime = gpGlobals->time + 0.62;
			EMIT_SOUND( this, CHAN_ITEM, "items/medshotno1.wav", 1.0, ATTN_NORM );
		}
		return;
	}

	SetNextThink( GetLastThink() + 0.25 );
	SetThink(&CWallHealth::Off);

	// Time to recharge yet?

	if (m_flNextCharge >= gpGlobals->time)
		return;

	// Play the on sound or the looping charging sound
	if (!m_iOn)
	{
		m_iOn++;
		EMIT_SOUND( this, CHAN_ITEM, "items/medshot4.wav", 1.0, ATTN_NORM );
		m_flSoundTime = 0.56 + gpGlobals->time;
	}
	if ((m_iOn == 1) && (m_flSoundTime <= gpGlobals->time))
	{
		m_iOn++;
		EMIT_SOUND( this, CHAN_STATIC, "items/medcharge4.wav", 1.0, ATTN_NORM );
	}


	// charge the player
	if ( pActivator->GiveHealth( 1, DMG_GENERIC ) )
	{
		m_iJuice--;
	}

	// govern the rate of charge
	m_flNextCharge = gpGlobals->time + 0.1;
}
//-----------------------------------------------------------------------------
// Purpose: Returns the magnitude of the entity's angular velocity.
//-----------------------------------------------------------------------------
float CPointAngularVelocitySensor::SampleAngularVelocity(CBaseEntity *pEntity)
{
	if (pEntity->GetMoveType() == MOVETYPE_VPHYSICS)
	{
		IPhysicsObject *pPhys = pEntity->VPhysicsGetObject();
		if (pPhys != NULL)
		{
			Vector vecVelocity;
			AngularImpulse vecAngVelocity;
			pPhys->GetVelocity(&vecVelocity, &vecAngVelocity);

			QAngle angles;
			pPhys->GetPosition( NULL, &angles );

			float dt = gpGlobals->curtime - GetLastThink();
			if ( dt == 0 )
				dt = 0.1;

			// HACKHACK: We don't expect a real 'delta' orientation here, just enough of an error estimate to tell if this thing
			// is trying to move, but failing.
			QAngle delta = angles - m_lastOrientation;

			if ( ( delta.Length() / dt )  < ( vecAngVelocity.Length() * 0.01 ) )
			{
				return 0.0f;
			}
			m_lastOrientation = angles;

			if ( m_bUseHelper == false )
			{
				return vecAngVelocity.Length();
			}
			else
			{
				Vector vLine = m_vecAxis - GetAbsOrigin();
				VectorNormalize( vLine );

				Vector vecWorldAngVelocity;
				pPhys->LocalToWorldVector( &vecWorldAngVelocity, vecAngVelocity );
				float flDot = DotProduct( vecWorldAngVelocity, vLine );

				return flDot;
			}
		}
	}
	else
	{
		QAngle vecAngVel = pEntity->GetLocalAngularVelocity();
		float flMax = MAX(fabs(vecAngVel[PITCH]), fabs(vecAngVel[YAW]));

		return MAX(flMax, fabs(vecAngVel[ROLL]));
	}

	return 0;
}
示例#5
0
void CWallHealth::Off(void)
{
	// Stop looping sound.
	if (m_iOn > 1)
		STOP_SOUND( this, CHAN_STATIC, "items/medcharge4.wav" );

	m_iOn = 0;

	if ((!m_iJuice) &&  ( ( m_iReactivate = g_pGameRules->FlHealthChargerRechargeTime() ) > 0) )
	{
		SetNextThink( GetLastThink() + m_iReactivate );
		SetThink(&CWallHealth::Recharge);
	}
	else
		SetThink( &CWallHealth::SUB_DoNothing );
}
示例#6
0
void CAI_Motor::UpdateYaw( int yawSpeed )
{
	float ideal, current, newYaw;
	
	if ( yawSpeed = -1 )
		yawSpeed = GetYawSpeed();
		
	// NOTE: GetIdealYaw() will never exactly be reached because UTIL_AngleMod
	// also truncates the angle to 16 bits of resolution. So lets truncate it here.
	current = UTIL_AngleMod( GetLocalAngles().y );
	ideal = UTIL_AngleMod( GetIdealYaw() );
	
	newYaw = AI_ClampYaw( (float)yawSpeed * 10.0, current, ideal, gpGlobals->curtime - GetLastThink() );
		
	if (newYaw != current)
	{
		QAngle angles = GetLocalAngles();
		angles.y = newYaw;
		SetLocalAngles( angles );

		// ENTITY MUST BE RELINKED to recompute absangles
		engine->RelinkEntity( GetEdict(), false );
	}
}
示例#7
0
//
// The door has reached the "up" position.  Either go back down, or wait for another activation.
//
void CBaseDoor::DoorHitTop( void )
{
	if( !GetSpawnFlags().Any( SF_DOOR_SILENT ) )
	{
		STOP_SOUND( this, CHAN_STATIC, ( char* ) STRING( pev->noiseMoving ) );
		EMIT_SOUND( this, CHAN_STATIC, ( char* ) STRING( pev->noiseArrived ), 1, ATTN_NORM );
	}

	ASSERT( m_toggle_state == TS_GOING_UP );
	m_toggle_state = TS_AT_TOP;

	// toggle-doors don't come down automatically, they wait for refire.
	if( GetSpawnFlags().Any( SF_DOOR_NO_AUTO_RETURN ) )
	{
		// Re-instate touch method, movement is complete
		if( !GetSpawnFlags().Any( SF_DOOR_USE_ONLY ) )
			SetTouch( &CBaseDoor::DoorTouch );
	}
	else
	{
		// In flWait seconds, DoorGoDown will fire, unless wait is -1, then door stays open
		SetNextThink( GetLastThink() + m_flWait );
		SetThink( &CBaseDoor::DoorGoDown );

		if( m_flWait == -1 )
		{
			SetNextThink( -1 );
		}
	}

	// Fire the close target (if startopen is set, then "top" is closed) - netname is the close target
	if( HasNetName() && GetSpawnFlags().Any( SF_DOOR_START_OPEN ) )
		FireTargets( GetNetName(), m_hActivator, this, USE_TOGGLE, 0 );

	SUB_UseTargets( m_hActivator, USE_TOGGLE, 0 ); // this isn't finished
}
示例#8
0
//-----------------------------------------------------------------------------
// Run all of the AI for elements within the range iStart to iEnd 
//-----------------------------------------------------------------------------
void CNPC_Blob::DoBlobBatchedAI( int iStart, int iEnd )
{
	float flInterval = gpGlobals->curtime - GetLastThink();

	// Local fields for sin-wave movement variance
	float flMySine;
	float flAmplitude = npc_blob_sin_amplitude.GetFloat();
	float flMyAmplitude;
	Vector vecRight;
	Vector vecForward;

	// Local fields for attract/repel
	float minDistSqr = Square( m_flMinElementDist );
	float flBlobSpeed = blob_element_speed.GetFloat();
	float flSpeed;

	// Local fields for speed limiting
	float flMinSpeed = blob_element_speed.GetFloat() * 0.5f;
	float flMaxSpeed = blob_element_speed.GetFloat() * 1.5f;
	bool bEnforceSpeedLimit;
	bool bEnforceRelativePositions;
	bool bDoMovementVariation;
	bool bDoOrientation = npc_blob_use_orientation.GetBool();
	float flIdleSpeedFactor = npc_blob_idle_speed_factor.GetFloat();

	// Group cohesion
	float flBlobRadiusSqr = Square( blob_radius.GetFloat() + 48.0f ); // Four feet of fudge

	// Build a right-hand vector along which we'll add some sine wave data to give each
	// element a unique insect-like undulation along an axis perpendicular to their path,
	// which makes the entire group look far less orderly
	if( GetEnemy() != NULL )
	{
		// If I have an enemy, the right-hand vector is perpendicular to a straight line 
		// from the group's centroid to the enemy's origin.
		vecForward = GetEnemy()->GetAbsOrigin() - m_vecCentroid;
		VectorNormalize( vecForward );
		vecRight.x = vecForward.y;
		vecRight.y = -vecForward.x;
	}
	else
	{
		// If there is no enemy, wobble along the axis from the centroid to me.
		vecForward = GetAbsOrigin() - m_vecCentroid;
		VectorNormalize( vecForward );
		vecRight.x = vecForward.y;
		vecRight.y = -vecForward.x;
	}

	//--
	// MAIN LOOP - Run all of the elements in the set iStart to iEnd
	//--
	for( int i = iStart ; i < iEnd ; i++ )
	{
		CBlobElement *pThisElement = m_Elements[ i ];

		//--
		// Initial movement
		//--
		// Start out with bEnforceSpeedLimit set to false. This is because an element
		// can't overspeed if it's moving undisturbed towards its target entity or 
		// target location. An element can only under or overspeed when it is repelled 
		// by multiple other elements in the group. See "Relative Positions" below.
		//
		// Initialize some 'defaults' that may be changed for each iteration of this loop
		bEnforceSpeedLimit = false;
		bEnforceRelativePositions = true;
		bDoMovementVariation = true;
		flSpeed = flBlobSpeed;

		switch( pThisElement->GetActiveMovementRule() )
		{
		case BLOB_MOVE_DONT_MOVE:
			{
				pThisElement->SetElementVelocity( vec3_origin, true );

				trace_t tr;
				Vector vecOrigin = pThisElement->GetAbsOrigin();

				UTIL_TraceLine( vecOrigin, vecOrigin - Vector( 0, 0, 16), MASK_SHOT, this, COLLISION_GROUP_NONE, &tr );

				if( tr.fraction < 1.0f )
				{
					QAngle angles;

					VectorAngles( tr.plane.normal, angles );

					float flSwap = angles.x;

					angles.x = -angles.y;
					angles.y = flSwap;

					pThisElement->SetAbsAngles( angles );
				}
			}
			continue;
			break;

		case BLOB_MOVE_TO_TARGET_LOCATION:
			{
				Vector vecDiff = pThisElement->GetAbsOrigin() - pThisElement->m_vecTargetLocation;

				if( vecDiff.Length2DSqr() <= Square(80.0f) )
				{
					// Don't shove this guy around any more, let him get to his goal position.
					flSpeed *= 0.5f;
					bEnforceRelativePositions = false;
					bDoMovementVariation = false;
				}

				pThisElement->MoveTowardsTargetLocation( flSpeed );
			}
			break;

		case BLOB_MOVE_TO_TARGET_ENTITY:
			{
				if( !IsMoving() && GetEnemy() == NULL )
				{
					if( pThisElement->GetAbsOrigin().DistToSqr( GetAbsOrigin() ) <= flBlobRadiusSqr )
					{
						flSpeed = (flSpeed * flIdleSpeedFactor) * pThisElement->m_flRandomEightyPercent;
					}
				}
				pThisElement->MoveTowardsTargetEntity( flSpeed );
			}
			break;

		default:
			Msg("ERROR: Blob Element with unspecified Movement Rule\n");
			break;
		}

		//---
		// Relative positions
		//--
		// Check this element against ALL other elements. If the two elements are closer
		// than the allowed minimum distance, repel this element away. (The other element
		// will repel when its AI runs). A single element can be repelled by many other 
		// elements. This is why bEnforceSpeedLimit is set to true if any of the repelling
		// code runs for this element. Multiple attempts to repel an element in the same
		// direction will cause overspeed. Conflicting attempts to repel an element in opposite
		// directions will cause underspeed.
		Vector vecDir = Vector( 0, 0, 0 );
		Vector vecThisElementOrigin = pThisElement->GetAbsOrigin();

		if( bEnforceRelativePositions )
		{
			for( int j = 0 ; j < m_Elements.Count() ; j++ )
			{
				// This is the innermost loop! We should optimize here, if anywhere.

				// If this element is on the wall, then don't be repelled by anyone. Repelling
				// elements that are trying to climb a wall usually make them look like they 
				// fall off the wall a few times while climbing.
				if( pThisElement->m_bOnWall )
					continue;

				CBlobElement *pThatElement = m_Elements[ j ];
				if( i != j )
				{
					Vector vecThatElementOrigin = pThatElement->GetAbsOrigin();
					float distSqr = vecThisElementOrigin.DistToSqr( vecThatElementOrigin );

					if( distSqr < minDistSqr )
					{
						// Too close to the other element. Move away.
						float flRepelSpeed;
						Vector vecRepelDir = ( vecThisElementOrigin - vecThatElementOrigin );

						vecRepelDir.NormalizeInPlace();
						flRepelSpeed = (flSpeed * ( 1.0f - ( distSqr / minDistSqr ) ) ) * pThatElement->GetSinePhase(); 
						pThisElement->AddElementVelocity( vecRepelDir * flRepelSpeed, true );

						// Since we altered this element's velocity after it was initially set, there's a chance
						// that the sums of multiple vectors will cause the element to over or underspeed, so 
						// mark it for speed limit enforcement
						bEnforceSpeedLimit = true;
					}
				}
			}
		}

		//--
		// Movement variation
		//--
		if( bDoMovementVariation )
		{
			flMySine = sin( gpGlobals->curtime * pThisElement->GetSineFrequency() );
			flMyAmplitude = flAmplitude * pThisElement->GetSineAmplitude();
			pThisElement->AddElementVelocity( vecRight * (flMySine * flMyAmplitude), true );
		}

		// Avoidance
		for( int a = 0 ; a < m_iNumAvoidOrigins ; a++ )
		{
			Vector vecAvoidDir = pThisElement->GetAbsOrigin() - m_vecAvoidOrigin[ a ];

			if( vecAvoidDir.LengthSqr() <= (m_flAvoidRadiusSqr * pThisElement->m_flRandomEightyPercent) )
			{
				VectorNormalize( vecAvoidDir );
				pThisElement->AddElementVelocity( vecAvoidDir * (flSpeed * 2.0f), true );
				break;
			}
		}

		//--
		// Speed limits
		//---
		if( bEnforceSpeedLimit == true )
		{
			pThisElement->EnforceSpeedLimits( flMinSpeed, flMaxSpeed );
		}

		//--
		// Wall crawling
		//--
		pThisElement->ModifyVelocityForSurface( flInterval, flSpeed );

		// For identifying stuck elements.
		pThisElement->m_vecPrevOrigin = pThisElement->GetAbsOrigin(); 

		pThisElement->m_flDistFromCentroidSqr = pThisElement->m_vecPrevOrigin.DistToSqr( m_vecCentroid );

		// Orientation
		if( bDoOrientation )
		{
			QAngle angles;
			VectorAngles( pThisElement->GetAbsVelocity(), angles );
			pThisElement->SetAbsAngles( angles );
		}

/*
		//--
		// Stragglers/Group integrity
		//
		if( pThisElement->m_flDistFromCentroidSqr > flStragglerDistSqr )
		{
			NDebugOverlay::Line( pThisElement->GetAbsOrigin(), m_vecCentroid, 255, 0, 0, false, 0.025f );
		}
*/
	}
}
示例#9
0
void CASW_Rocket::SeekThink( void )
{
	// If we have a grace period, go solid when it ends
	if ( m_flGracePeriodEndsAt )
	{
		if ( m_flGracePeriodEndsAt < gpGlobals->curtime )
		{
			RemoveSolidFlags( FSOLID_NOT_SOLID );
			m_flGracePeriodEndsAt = 0;
		}
	}

	Vector vNewVelocity = GetAbsVelocity();
	
	if ( m_bFlyingWild )
	{
		// wobble crazily. Poll for a new target every quarter second, and if none is found, go
		// careering off.
		if ( gpGlobals->curtime >= m_flNextWobbleTime )
		{
			Assert( !m_hHomingTarget.Get() );
			CBaseEntity *pHomingTarget = FindPotentialTarget(); 
			if ( pHomingTarget )
			{
				SetTarget( pHomingTarget );
				m_bFlyingWild = false;
			}
			else
			{
				// pick a new wobble direction
				/*
				m_vWobbleAngles = GetAbsAngles();
				m_vWobbleAngles.y =  m_vWobbleAngles.y + RandomFloat( -asw_rocket_wobble_amp.GetFloat(), asw_rocket_wobble_amp.GetFloat() ) ;
				if ( m_vWobbleAngles.y < 0 )
				{
					m_vWobbleAngles.y = 360 + m_vWobbleAngles.y;
				}
				else if ( m_vWobbleAngles.y > 360 )
				{
					m_vWobbleAngles.y = fmod( m_vWobbleAngles.y, 360 );
				}

				*/

				m_vWobbleAngles = GetAbsAngles();
				m_vWobbleAngles.y = fmodf( m_vWobbleAngles.y + RandomFloat( -asw_rocket_wobble_amp.GetFloat(), asw_rocket_wobble_amp.GetFloat() ), 360 );

				m_flNextWobbleTime = gpGlobals->curtime + asw_rocket_wobble_freq.GetFloat();
			}
		}
	}


	if ( !m_bFlyingWild )
	{
		Vector	targetPos;
		FindHomingPosition( &targetPos );

		// find target direction
		Vector	vTargetDir;
		VectorSubtract( targetPos, GetAbsOrigin(), vTargetDir );
		float flDist = VectorNormalize( vTargetDir );

		// find current direction
		Vector	vDir	= GetAbsVelocity();
		//float	flSpeed	= VectorNormalize( vDir );

		vNewVelocity = IntegrateRocketThrust( vTargetDir, flDist );

		// face direction of movement
		QAngle	finalAngles;
		VectorAngles( vNewVelocity, finalAngles );
		SetAbsAngles( finalAngles );

		// set to the new calculated velocity
		SetAbsVelocity( vNewVelocity );
	}
	else // wobble crazily
	{
#pragma message("TODO: straighten out this math")
		if ( gpGlobals->curtime >= m_flNextWobbleTime )
		{
			// pick a new wobble direction
			m_vWobbleAngles = GetAbsAngles();
			m_vWobbleAngles.y = fmodf( m_vWobbleAngles.y + RandomFloat( -asw_rocket_wobble_amp.GetFloat(), asw_rocket_wobble_amp.GetFloat() ), 360 );

			m_flNextWobbleTime = gpGlobals->curtime + asw_rocket_wobble_freq.GetFloat();
		}
		QAngle finalAngles = GetAbsAngles();
		finalAngles.y = ApproachAngle( m_vWobbleAngles.y, finalAngles.y, 360.f * (gpGlobals->curtime - GetLastThink()) );

		Vector forward;
		AngleVectors( finalAngles, &forward );
		vNewVelocity = forward * FastSqrtEst( vNewVelocity.LengthSqr() );
		if ( IsWallDodging() )
		{
			ComputeWallDodge( vNewVelocity );
			finalAngles.y = ApproachAngle( m_vWobbleAngles.y, finalAngles.y, 360.f * (gpGlobals->curtime - GetLastThink()) );
		}

		vNewVelocity = IntegrateRocketThrust( forward, 0 );

		// face direction of movement
		SetAbsAngles( finalAngles );

		// set to the new calculated velocity
		SetAbsVelocity( vNewVelocity );
	}

	// blow us up after our lifetime
	if (GetLifeFraction() >= 1.0f)
	{
		Explode();
	}
	else
	{
		// Think as soon as possible
		SetNextThink( gpGlobals->curtime );
	}
}
示例#10
0
//-----------------------------------------------------------------------------
// Purpose:
// Input  :
// Output :
//-----------------------------------------------------------------------------
void CAI_BaseFlyingBot::TurnHeadToTarget(float flInterval, const Vector &MoveTarget )
{
	float flDestYaw = VecToYaw( MoveTarget - GetLocalOrigin() );
	
	float newYaw = AI_ClampYaw( GetHeadTurnRate() * 10.0f, m_fHeadYaw, flDestYaw, gpGlobals->curtime - GetLastThink() );
		
	if ( newYaw != m_fHeadYaw )
	{
		m_fHeadYaw = newYaw;
	}

	// Set us to face that way
	SetBoneController( 0, m_fHeadYaw );
}
//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
bool CNPC_VehicleDriver::OverridePathMove( float flInterval )
{
	// Setup our initial path data if we've just started running a path
	if ( !m_pCurrentWaypoint )
	{
		m_vecPrevPoint = GetAbsOrigin();
		m_vecPrevPrevPoint = GetAbsOrigin();
		m_vecDesiredPosition = GetNavigator()->GetCurWaypointPos();	
		CalculatePostPoints();
	
		// Init our two waypoints
		m_Waypoints[0] = new CVehicleWaypoint( m_vecPrevPrevPoint, m_vecPrevPoint, m_vecDesiredPosition, m_vecPostPoint );
		m_Waypoints[1] = new CVehicleWaypoint( m_vecPrevPoint, m_vecDesiredPosition, m_vecPostPoint, m_vecPostPostPoint );
		m_pCurrentWaypoint = m_Waypoints[0];
		m_pNextWaypoint = m_Waypoints[1];

		m_flDistanceAlongSpline = 0.2;
	}

	// Have we reached our target? See if we've passed the current waypoint's plane.
	Vector vecAbsMins, vecAbsMaxs;
	CollisionProp()->WorldSpaceAABB( &vecAbsMins, &vecAbsMaxs );
	if ( BoxOnPlaneSide( vecAbsMins, vecAbsMaxs, &m_pCurrentWaypoint->planeWaypoint ) == 3 )
	{
		if ( WaypointReached() )
			return true;
	}

	// Did we bypass it and reach the next one already?
	if ( m_pNextWaypoint && BoxOnPlaneSide( vecAbsMins, vecAbsMaxs, &m_pNextWaypoint->planeWaypoint ) == 3 )
	{
		if ( WaypointReached() )
			return true;
	}

	// We may have just teleported, so check to make sure we have a waypoint
	if ( !m_pCurrentWaypoint || !m_pNextWaypoint )
		return false;

	// Figure out which spline we're trucking along
	CVehicleWaypoint *pCurrentSplineBeingTraversed = m_pCurrentWaypoint;
	if ( m_flDistanceAlongSpline > 1 )
	{
		pCurrentSplineBeingTraversed = m_pNextWaypoint;
	}

	// Get our current speed, and check it against the length of the spline to know how far to advance our marker
	AngularImpulse angVel;
	Vector vecVelocity;
	IPhysicsObject *pVehiclePhysics = m_hVehicleEntity->VPhysicsGetObject();

	if( !pVehiclePhysics )
	{
		// I think my vehicle has been destroyed.
		return false;
	}

	pVehiclePhysics->GetVelocity( &vecVelocity, &angVel );
	float flSpeed = vecVelocity.Length();
	float flIncTime = gpGlobals->curtime - GetLastThink();
	float flIncrement = flIncTime * (flSpeed / pCurrentSplineBeingTraversed->GetLength());

	// Now advance our point along the spline
	m_flDistanceAlongSpline = clamp( m_flDistanceAlongSpline + flIncrement, 0, 2);
	if ( m_flDistanceAlongSpline > 1 )
	{
		// We crossed the spline boundary
		pCurrentSplineBeingTraversed = m_pNextWaypoint;
	}

	Vector vSplinePoint = pCurrentSplineBeingTraversed->GetPointAt( m_flDistanceAlongSpline > 1 ? m_flDistanceAlongSpline-1 : m_flDistanceAlongSpline );
	Vector vSplineTangent = pCurrentSplineBeingTraversed->GetTangentAt( m_flDistanceAlongSpline > 1 ? m_flDistanceAlongSpline-1 : m_flDistanceAlongSpline );

	// Now that we've got the target spline point & tangent, use it to decide what our desired velocity is.
	// If we're close to the tangent, just use the tangent. Otherwise, Lerp towards it.
	Vector vecToDesired = (vSplinePoint - GetAbsOrigin());
	float flDistToDesired = VectorNormalize( vecToDesired );
	float flTangentLength = VectorNormalize( vSplineTangent );

	if ( flDistToDesired > (flTangentLength * 0.75) )
	{
		m_vecDesiredVelocity = vecToDesired * flTangentLength;
	}
	else
	{
		VectorLerp( vSplineTangent, vecToDesired * flTangentLength, (flDistToDesired / (flTangentLength * 0.5)), m_vecDesiredVelocity );
	}

	// Decrease speed according to the turn we're trying to make
	Vector vecRight;
	m_hVehicleEntity->GetVectors( NULL, &vecRight, NULL );
	Vector vecNormVel = m_vecDesiredVelocity;
	VectorNormalize( vecNormVel );
	float flDotRight = DotProduct( vecRight, vecNormVel );
	flSpeed = (1.0 - fabs(flDotRight));
	// Don't go slower than we've been told to go
	if ( flSpeed < m_flDriversMinSpeed )
	{
		flSpeed = m_flDriversMinSpeed;
	}
	m_vecDesiredVelocity = vecNormVel * (flSpeed * m_flMaxSpeed);

	// Bunch o'debug
	if ( g_debug_vehicledriver.GetInt() & DRIVER_DEBUG_PATH )
	{
		NDebugOverlay::Box( m_vecPrevPrevPoint, -Vector(15,15,15), Vector(15,15,15), 192,0,0, true, 0.1);
		NDebugOverlay::Box( m_vecPrevPoint, -Vector(20,20,20), Vector(20,20,20), 255,0,0, true, 0.1);
		NDebugOverlay::Box( m_vecPostPoint, -Vector(20,20,20), Vector(20,20,20), 0,192,0, true, 0.1);
		NDebugOverlay::Box( m_vecPostPostPoint, -Vector(20,20,20), Vector(20,20,20), 0,128,0, true, 0.1);
		NDebugOverlay::Box( vSplinePoint, -Vector(10,10,10), Vector(10,10,10), 0,0,255, true, 0.1);
		NDebugOverlay::Line( vSplinePoint, vSplinePoint + (vSplineTangent * 40), 0,0,255, true, 0.1);

		//NDebugOverlay::HorzArrow( pCurrentSplineBeingTraversed->splinePoints[0], pCurrentSplineBeingTraversed->splinePoints[1], 30, 255,255,255,0, false, 0.1f );
		//NDebugOverlay::HorzArrow( pCurrentSplineBeingTraversed->splinePoints[1], pCurrentSplineBeingTraversed->splinePoints[2], 20, 255,255,255,0, false, 0.1f );
		//NDebugOverlay::HorzArrow( pCurrentSplineBeingTraversed->splinePoints[2], pCurrentSplineBeingTraversed->splinePoints[3], 10, 255,255,255,0, false, 0.1f );

		// Draw the plane we're checking against for waypoint passing
		Vector vecPlaneRight;
		CrossProduct( m_pCurrentWaypoint->planeWaypoint.normal, Vector(0,0,1), vecPlaneRight );
		Vector vecPlane = m_pCurrentWaypoint->splinePoints[2];
		NDebugOverlay::Line( vecPlane + (vecPlaneRight * -100), vecPlane + (vecPlaneRight * 100), 255,0,0, true, 0.1);

		// Draw the next plane too
		CrossProduct( m_pNextWaypoint->planeWaypoint.normal, Vector(0,0,1), vecPlaneRight );
		vecPlane = m_pNextWaypoint->splinePoints[2];
		NDebugOverlay::Line( vecPlane + (vecPlaneRight * -100), vecPlane + (vecPlaneRight * 100), 192,0,0, true, 0.1);
	}

	if ( g_debug_vehicledriver.GetInt() & DRIVER_DEBUG_PATH_SPLINE )
	{
		for ( int i = 0; i < 10; i++ )
		{
			Vector vecTarget = m_pCurrentWaypoint->GetPointAt( 0.1 * i );
			Vector vecTangent = m_pCurrentWaypoint->GetTangentAt( 0.1 * i );
			VectorNormalize(vecTangent);
			NDebugOverlay::Box( vecTarget, -Vector(10,10,10), Vector(10,10,10), 255,0,0, true, 0.1 );
			NDebugOverlay::Line( vecTarget, vecTarget + (vecTangent * 10), 255,255,0, true, 0.1);
		}
	}

	return true;
}