コード例 #1
0
AIMotorMoveResult_t CAI_Motor::MoveGroundExecute( const AILocalMoveGoal_t &move, AIMoveTrace_t *pTraceResult )
{
	// --------------------------------------------
	// turn in the direction of movement
	// --------------------------------------------
	MoveFacing( move );

	// --------------------------------------------
	float flNewSpeed = GetIdealSpeed();

	// --------------------------------------------
	// calculate actual travel distance
	// --------------------------------------------

	// assuming linear acceleration, how far will I travel?
	float flTotal = 0.5 * (GetCurSpeed() + flNewSpeed) * GetMoveInterval();

	// can I move farther in this interval than I'm supposed to?
	if ( flTotal > move.maxDist )
	{
		if ( !(move.flags & AILMG_CONSUME_INTERVAL) )
		{
			// only use a portion of the time interval
			SetMoveInterval( GetMoveInterval() * (1 - move.maxDist / flTotal) );
		}
		else
			SetMoveInterval( 0 );
		flTotal = move.maxDist;
	}
	else
	{
		// use all the time
		SetMoveInterval( 0 );
	}

	SetMoveVel( move.dir * flNewSpeed );

	// --------------------------------------------
	// walk the distance
	// --------------------------------------------
	AIMotorMoveResult_t result = AIM_SUCCESS;
	if ( flTotal > 0.0 )
	{
		Vector vecFrom = GetLocalOrigin();
		Vector vecTo = vecFrom + move.dir * flTotal;
		
		result = MoveGroundStep( vecTo, move.pMoveTarget, -1, true, pTraceResult );

		if ( result == AIM_FAILED )
			MoveStop();
	}
	else if ( !OnMoveStalled( move ) )
	{
		result = AIM_FAILED;
	}


	return result;
}
コード例 #2
0
//-----------------------------------------------------------------------------
// Purpose: Look ahead my stopping distance, or at least my hull width
//-----------------------------------------------------------------------------
float CAI_Motor::MinCheckDist( void )
{
	// Take the groundspeed into account
	float flMoveDist = GetMoveInterval() * GetIdealSpeed();
	float flMinDist = max( MinStoppingDist(), flMoveDist);
	if ( flMinDist < GetHullWidth() )
		flMinDist = GetHullWidth();
	return flMinDist;
}
コード例 #3
0
ファイル: ai_motor.cpp プロジェクト: Au-heppa/swarm-sdk
AIMotorMoveResult_t CAI_Motor::MoveGroundExecute( const AILocalMoveGoal_t &move, AIMoveTrace_t *pTraceResult )
{
	// --------------------------------------------
	// turn in the direction of movement
	// --------------------------------------------
	MoveFacing( move );

	// --------------------------------------------
	return MoveGroundExecuteWalk( move, GetIdealSpeed(), CalcIntervalMove(), pTraceResult );
}
コード例 #4
0
bool CASW_Simple_Alien::PerformMovement(float deltatime)
{
	if (!m_bMoving)
		return false;

	if (m_hMoveTarget.Get())	// if we're moving to a specific object, update our target vector with its current location
	{
		m_vecMoveTarget = m_hMoveTarget->GetAbsOrigin();
	}
	Vector vecStartDiff = m_vecMoveTarget - GetAbsOrigin();
	vecStartDiff.z = 0;

	// check we're not there already
	if (vecStartDiff.Length2D() <= m_fArrivedTolerance)
	{
		FinishedMovement();
		return true;
	}
	
	// work out the new position we want to be in
	Vector vecDir = vecStartDiff;
	vecDir.NormalizeInPlace();
	//Msg("moving with delta %f\n", delta);
	Vector vecNewPos = GetAbsOrigin() + vecDir * GetIdealSpeed() * deltatime;
	if (TryMove(GetAbsOrigin(), vecNewPos, deltatime))
	{
		// apply gravity to our new position
		ApplyGravity(vecNewPos, deltatime);

		// we moved (at least some portion, maybe all)
		UTIL_SetOrigin(this, vecNewPos);

		// check if we're close enough, or have gone past, the move target
		Vector vecEndDiff = m_vecMoveTarget - GetAbsOrigin();
		vecEndDiff.z = 0;
		if (vecStartDiff.Dot(vecEndDiff) < 0 || vecEndDiff.Length2D() <= m_fArrivedTolerance)
		{
			// we've arrived
			FinishedMovement();
		}
		return true;
	}
	else
	{
		vecNewPos = GetAbsOrigin();
		if (ApplyGravity(vecNewPos, deltatime))	// if we failed to move forward, make sure gravity is still applied
		{
			// we moved (at least some portion, maybe all)
			UTIL_SetOrigin(this, vecNewPos);
		}
	}
	
	FailedMove();
	return false;
}
コード例 #5
0
AIMotorMoveResult_t CAI_Motor::MoveFlyExecute( const AILocalMoveGoal_t &move, AIMoveTrace_t *pTraceResult )
{
	// turn in the direction of movement
	MoveFacing( move );

	// calc accel/decel rates
	float flNewSpeed = GetIdealSpeed();
	SetMoveVel( move.dir * flNewSpeed );

	float flTotal = 0.5 * (GetCurSpeed() + flNewSpeed) * GetMoveInterval();

	float distance = move.maxDist;

	// can I move farther in this interval than I'm supposed to?
	if (flTotal > distance)
	{
		// only use a portion of the time interval
		SetMoveInterval( GetMoveInterval() * (1 - distance / flTotal) );
		flTotal = distance;
	}
	else
	{
		// use all the time
		SetMoveInterval( 0 );
	}

	Vector vecStart, vecEnd;
	vecStart = GetLocalOrigin();
	VectorMA( vecStart, flTotal, move.dir, vecEnd );

	AIMoveTrace_t moveTrace;
	GetMoveProbe()->MoveLimit( NAV_FLY, vecStart, vecEnd, MASK_NPCSOLID, NULL, &moveTrace );
	if ( pTraceResult )
		*pTraceResult = moveTrace;
	
	// Check for total blockage
	if (fabs(moveTrace.flDistObstructed - flTotal) <= 1e-1)
	{
		// But if we bumped into our target, then we succeeded!
		if ( move.pMoveTarget && (moveTrace.pObstruction == move.pMoveTarget) )
			return AIM_PARTIAL_HIT_TARGET;

		return AIM_FAILED;
	}

	// The true argument here causes it to touch all triggers
	// in the volume swept from the previous position to the current position
	UTIL_SetOrigin(GetOuter(), moveTrace.vEndPosition, true);

	return (IsMoveBlocked(moveTrace.fStatus)) ? AIM_PARTIAL_HIT_WORLD : AIM_SUCCESS;
}
コード例 #6
0
//-----------------------------------------------------------------------------
// Returns the estimate in seconds before we reach our nav goal.
// -1 means we don't know / haven't calculated it yet.
//-----------------------------------------------------------------------------
float CAI_BaseNPC::GetTimeToNavGoal()
{
	float flDist = GetNavigator()->BuildAndGetPathDistToGoal();
	if ( flDist < 0 )
	{
		return -1.0f;
	}

	float flSpeed = GetIdealSpeed();

	// FIXME: needs to consider stopping time!
	if (flSpeed > 0 && flDist > 0)
	{
		return flDist / flSpeed;
	}
	return 0.0;
}
コード例 #7
0
//-----------------------------------------------------------------------------
// Purpose: how fast should I be going ideally
//-----------------------------------------------------------------------------
float CAI_Motor::IdealVelocity( void )
{
	// FIXME: this should be a per-entity setting so run speeds are not based on animation speeds
	return GetIdealSpeed() * GetPlaybackRate();
}
コード例 #8
0
float CASW_Parasite::GetIdealAccel( ) const
{
	return GetIdealSpeed() * 1.5f;
}
コード例 #9
0
ファイル: ai_motor.cpp プロジェクト: Au-heppa/swarm-sdk
//-----------------------------------------------------------------------------
float CAI_Motor::CalcIntervalMove()
{
	// assuming linear acceleration, how far will I travel?
	return 0.5 * (GetCurSpeed() + GetIdealSpeed()) * GetMoveInterval();
}
コード例 #10
0
ファイル: asw_harvester.cpp プロジェクト: Au-heppa/swarm-sdk
float CASW_Harvester::GetIdealAccel( ) const
{
	return GetIdealSpeed() * 1.5f;
}
コード例 #11
0
void CAI_BlendedMotor::SetMoveScriptAnim( float flNewSpeed )
{
	// don't bother if the npc is dead
	if (!GetOuter()->IsAlive())
		return;

	// insert ideal layers
	// FIXME: needs full transitions, as well as starting vs stopping sequences, leaning, etc.

	CAI_Navigator *pNavigator = GetNavigator();

	SetPlaybackRate( m_flCurrRate );
	// calc weight of idle animation layer that suppresses the run animation
	float flWeight = 0.0;
	if (GetIdealSpeed() > 0.0)
	{
		flWeight = 1.0 - (flNewSpeed / (GetIdealSpeed()  * GetPlaybackRate()));
	}
	if (flWeight < 0.0)
	{
		m_flCurrRate = flNewSpeed / GetIdealSpeed();
		m_flCurrRate = clamp( m_flCurrRate, 0.0, 1.0 );
		SetPlaybackRate( m_flCurrRate );
		flWeight = 0.0;
	}
	// Msg("weight %.3f rate %.3f\n", flWeight, m_flCurrRate );
	m_flCurrRate = min( m_flCurrRate + (1.0 - m_flCurrRate) * 0.8, 1.0 );

	if (m_nSavedGoalActivity == ACT_INVALID)
	{
		ResetGoalSequence();
	}

	// detect state change
	Activity activity = GetOuter()->NPC_TranslateActivity( m_nSavedGoalActivity );
	if ( activity != m_nSavedTranslatedGoalActivity )
	{
		m_nSavedTranslatedGoalActivity = activity;
		m_nInteriorSequence = ACT_INVALID;
		m_nGoalSequence = pNavigator->GetArrivalSequence( m_nPrimarySequence );
	}

	if (m_bDeceleratingToGoal)
	{
		// find that sequence to play when at goal
		m_nGoalSequence = pNavigator->GetArrivalSequence( m_nPrimarySequence );

		if (m_nGoalSequence == ACT_INVALID)
		{
			m_nGoalSequence = GetInteriorSequence( m_nPrimarySequence );
		}

		Assert( m_nGoalSequence != ACT_INVALID );
	}

	if (m_flSecondaryWeight == 1.0 || (m_iSecondaryLayer != -1 && m_nPrimarySequence == m_nSecondarySequence))
	{
		// secondary layer at full strength last time, delete the primary and shift down
		RemoveLayer( m_iPrimaryLayer, 0.0, 0.0 );

		m_iPrimaryLayer = m_iSecondaryLayer;
		m_nPrimarySequence = m_nSecondarySequence;
		m_iSecondaryLayer = -1;
		m_nSecondarySequence = ACT_INVALID;
		m_flSecondaryWeight = 0.0;
	}

	// look for transition sequence if needed
	if (m_nSecondarySequence == ACT_INVALID)
	{
		if (!m_bDeceleratingToGoal && m_nGoalSequence != GetInteriorSequence( m_nPrimarySequence ))
		{
			// strob interior sequence in case it changed
			m_nGoalSequence = GetInteriorSequence( m_nPrimarySequence );
		}

		if (m_nGoalSequence != ACT_INVALID && m_nPrimarySequence != m_nGoalSequence)
		{
			// Msg("From %s to %s\n", GetOuter()->GetSequenceName( m_nPrimarySequence ), GetOuter()->GetSequenceName( m_nGoalSequence ) );
			m_nSecondarySequence = GetOuter()->FindTransitionSequence(m_nPrimarySequence, m_nGoalSequence, NULL);
			if (m_nSecondarySequence == ACT_INVALID)
				m_nSecondarySequence = m_nGoalSequence;
		}
	}

	// set blending for 
	if (m_nSecondarySequence != ACT_INVALID)
	{
		if (m_iSecondaryLayer == -1)
		{
			m_iSecondaryLayer = AddLayeredSequence( m_nSecondarySequence, 0 );
			SetLayerWeight( m_iSecondaryLayer, 0.0 );
			if (m_nSecondarySequence == m_nGoalSequence)
			{
				SetLayerPlaybackRate( m_iSecondaryLayer, 0.0 );
			}
			else
			{
				SetLayerPlaybackRate( m_iSecondaryLayer, 1.0 );
			}
			SetLayerNoRestore( m_iSecondaryLayer, true );
			m_flSecondaryWeight = 0.0;
		}

		m_flSecondaryWeight = min( m_flSecondaryWeight + 0.3, 1.0 );

		if (m_flSecondaryWeight < 1.0)
		{
			SetLayerWeight( m_iPrimaryLayer, (flWeight - m_flSecondaryWeight * flWeight) / (1.0f - m_flSecondaryWeight * flWeight) );
			SetLayerWeight( m_iSecondaryLayer, flWeight * m_flSecondaryWeight );
		}
		else
		{
			SetLayerWeight( m_iPrimaryLayer, 0.0f );
			SetLayerWeight( m_iSecondaryLayer, flWeight );
		}
	}
	else
	{
		// recreate layer if missing
		if (m_iPrimaryLayer == -1)
		{
			MoveContinue();
		}

		// try to catch a stale layer
		if (m_iSecondaryLayer != -1)
		{
			// secondary layer at full strength last time, delete the primary and shift down
			RemoveLayer( m_iSecondaryLayer, 0.0, 0.0 );
			m_iSecondaryLayer = -1;
			m_nSecondarySequence = ACT_INVALID;
			m_flSecondaryWeight = 0.0;
		}

		// debounce
		// flWeight = flWeight * 0.5 + 0.5 * GetOuter()->GetLayerWeight( m_iPrimaryLayer );
		SetLayerWeight( m_iPrimaryLayer, flWeight );
	}
}
コード例 #12
0
void CAI_BlendedMotor::BuildVelocityScript( const AILocalMoveGoal_t &move )
{
	int i;
	float a;

	float idealVelocity = GetIdealSpeed();
	if (idealVelocity == 0)
	{
		idealVelocity = 50;
	}

	float idealAccel = GetIdealAccel();
	if (idealAccel == 0)
	{
		idealAccel = 100;
	}

	AI_Movementscript_t script;

	// set current location as start of script
	script.vecLocation = GetAbsOrigin();
	script.flMaxVelocity = GetCurSpeed();
	m_scriptMove.AddToTail( script );

	//-------------------------

	extern ConVar *npc_height_adjust;
	if (npc_height_adjust->GetBool() && move.bHasTraced && move.directTrace.flTotalDist != move.thinkTrace.flTotalDist)
	{
		float flDist = (move.directTrace.vEndPosition - m_scriptMove[0].vecLocation).Length2D();
		float flHeight = move.directTrace.vEndPosition.z - m_scriptMove[0].vecLocation.z;
		float flDelta;

		if (flDist > 0)
		{
			flDelta = flHeight / flDist;
		}
		else
		{
			flDelta = 0;
		}

		m_flPredictiveSpeedAdjust = 1.1 - fabs( flDelta );
		m_flPredictiveSpeedAdjust = clamp( m_flPredictiveSpeedAdjust, (flHeight > 0.0) ? 0.5 : 0.8, 1.0 );

		/*
		if ((GetOuter()->m_debugOverlays & OVERLAY_NPC_SELECTED_BIT))
		{
			Msg("m_flPredictiveSpeedAdjust %.3f  %.1f %.1f\n", m_flPredictiveSpeedAdjust, flHeight, flDist );
			NDebugOverlay::Box( move.directTrace.vEndPosition, Vector( -2, -2, -2 ), Vector( 2, 2, 2 ), 0,255,255, 0, 0.12 );
		}
		*/
	}
	if (npc_height_adjust->GetBool())
	{
		float flDist = (move.thinkTrace.vEndPosition - m_vecPrevOrigin2).Length2D();
		float flHeight = move.thinkTrace.vEndPosition.z - m_vecPrevOrigin2.z;
		float flDelta;

		if (flDist > 0)
		{
			flDelta = flHeight / flDist;
		}
		else
		{
			flDelta = 0;
		}

		float newSpeedAdjust = 1.1 - fabs( flDelta );
		newSpeedAdjust = clamp( newSpeedAdjust, (flHeight > 0.0) ? 0.5 : 0.8, 1.0 );

		// debounce speed adjust
		if (newSpeedAdjust < m_flReactiveSpeedAdjust)
		{
			m_flReactiveSpeedAdjust = m_flReactiveSpeedAdjust * 0.2 + newSpeedAdjust * 0.8;
		}
		else
		{
			m_flReactiveSpeedAdjust = m_flReactiveSpeedAdjust * 0.5 + newSpeedAdjust * 0.5;
		}

		// filter through origins
		m_vecPrevOrigin2 = m_vecPrevOrigin1;
		m_vecPrevOrigin1 = GetAbsOrigin();

		/*
		if ((GetOuter()->m_debugOverlays & OVERLAY_NPC_SELECTED_BIT))
		{
			NDebugOverlay::Box( m_vecPrevOrigin2, Vector( -2, -2, -2 ), Vector( 2, 2, 2 ), 255,0,255, 0, 0.12 );
			NDebugOverlay::Box( move.thinkTrace.vEndPosition, Vector( -2, -2, -2 ), Vector( 2, 2, 2 ), 255,0,255, 0, 0.12 );
			Msg("m_flReactiveSpeedAdjust %.3f  %.1f %.1f\n", m_flReactiveSpeedAdjust, flHeight, flDist );
		}
		*/
	}

	idealVelocity = idealVelocity * min( m_flReactiveSpeedAdjust, m_flPredictiveSpeedAdjust );

	//-------------------------

	bool bAddedExpected = false;

	// add all waypoint locations and velocities
	AI_Waypoint_t *pCurWaypoint = GetNavigator()->GetPath()->GetCurWaypoint();

	// there has to be at least one waypoint
	Assert( pCurWaypoint );

	while (pCurWaypoint && (pCurWaypoint->NavType() == NAV_GROUND || pCurWaypoint->NavType() == NAV_FLY) /*&& flTotalDist / idealVelocity < 3.0*/) // limit lookahead to 3 seconds
	{
		script.Init();
		AI_Waypoint_t *pNext = pCurWaypoint->GetNext();

		if (ai_path_adjust_speed_on_immediate_turns->GetBool() && !bAddedExpected)
		{
			// hack in next expected immediate location for move
			script.vecLocation = GetAbsOrigin() + move.dir * move.curExpectedDist;
			bAddedExpected = true;
			pNext = pCurWaypoint;
		}
		else
		{
			script.vecLocation = pCurWaypoint->vecLocation;
			script.pWaypoint = pCurWaypoint;
		}

		//DevMsg("waypoint %.1f %.1f %.1f\n", script.vecLocation.x, script.vecLocation.y, script.vecLocation.z );

		if (pNext)
		{
			switch( pNext->NavType())
			{
			case NAV_GROUND:
			case NAV_FLY:
				{
					Vector d1 = pNext->vecLocation - script.vecLocation;
					Vector d2 = script.vecLocation - m_scriptMove[m_scriptMove.Count()-1].vecLocation;
					
					// remove very short, non terminal ground links
					// FIXME: is this safe?  Maybe just check for co-located ground points?
					if (d1.Length2D() < 1.0)
					{
						/*
						if (m_scriptMove.Count() > 1)
						{
							int i = m_scriptMove.Count() - 1;
							m_scriptMove[i].vecLocation = pCurWaypoint->vecLocation;
							m_scriptMove[i].pWaypoint = pCurWaypoint;
						}
						*/
						pCurWaypoint = pNext;
						continue;
					}

					d1.z = 0;
					VectorNormalize( d1 );
					d2.z = 0;
					VectorNormalize( d2 );

					// figure velocity
					float dot = (DotProduct( d1, d2 ) + 0.2);
					if (dot > 0)
					{
						dot = clamp( dot, 0.0f, 1.0f );
						script.flMaxVelocity = idealVelocity * dot;
					}
					else
					{
						script.flMaxVelocity = 0;
					}
				}
				break;
			case NAV_JUMP:

				// FIXME: information about what the jump should look like isn't stored in the waypoints
				// this'll need to call 
				//    GetMoveProbe()->MoveLimit( NAV_JUMP, GetLocalOrigin(), GetPath()->CurWaypointPos(), MASK_NPCSOLID, GetNavTargetEntity(), &moveTrace );
				// to get how far/fast the jump will be, but this is also stateless, so it'd call it per frame.
				// So far it's not clear that the moveprobe doesn't also call this.....

				{
					float minJumpHeight = 0;
					float maxHorzVel = max( GetCurSpeed(), 100 );
					float gravity = sv_gravity->GetFloat() * GetOuter()->GetGravity();
					Vector vecApex;
					Vector rawJumpVel = GetMoveProbe()->CalcJumpLaunchVelocity(script.vecLocation, pNext->vecLocation, gravity, &minJumpHeight, maxHorzVel, &vecApex );

					script.flMaxVelocity = rawJumpVel.Length2D();
					// Msg("%.1f\n", script.flMaxVelocity );
				}
				break;
			case NAV_CLIMB:
				{
					/*
					CAI_Node *pClimbNode = GetNavigator()->GetNetwork()->GetNode(pNext->iNodeID);

					check: pClimbNode->m_eNodeInfo
						bits_NODE_CLIMB_BOTTOM, 
						bits_NODE_CLIMB_ON, 
						bits_NODE_CLIMB_OFF_FORWARD, 
						bits_NODE_CLIMB_OFF_LEFT, 
						bits_NODE_CLIMB_OFF_RIGHT
					*/

					script.flMaxVelocity = 0;
				}
				break;
			/*
			case NAV_FLY:
				// FIXME: can there be a NAV_GROUND -> NAV_FLY transition?
				script.flMaxVelocity = 0;
				break;
			*/
			default:
				break;
			}
		}
		else
		{
			script.flMaxVelocity = GetNavigator()->GetArrivalSpeed();
			// Assert( script.flMaxVelocity == 0 );
		}

		m_scriptMove.AddToTail( script );
		pCurWaypoint = pNext;
	}


	//-------------------------

	// update distances
	float flTotalDist = 0;
	for (i = 0; i < m_scriptMove.Count() - 1; i++ )
	{
		flTotalDist += m_scriptMove[i].flDist = (m_scriptMove[i+1].vecLocation - m_scriptMove[i].vecLocation).Length2D();
	}

	//-------------------------

	if ( !m_bDeceleratingToGoal && m_scriptMove.Count() && flTotalDist > 0 )
	{
		float flNeededAccel = DeltaV( m_scriptMove[0].flMaxVelocity, m_scriptMove[m_scriptMove.Count() - 1].flMaxVelocity, flTotalDist );
		m_bDeceleratingToGoal =  (flNeededAccel < -idealAccel);
		//Assert( flNeededAccel != idealAccel);
	}

	//-------------------------

	// insert slowdown points due to blocking
	if (ai_path_insert_pause_at_obstruction->GetBool() && move.directTrace.pObstruction)
	{
		float distToObstruction = (move.directTrace.vEndPosition - m_scriptMove[0].vecLocation).Length2D();

		// HACK move obstruction out "stepsize" to account for it being based on stand position and not a trace
		distToObstruction = distToObstruction + 16;

		InsertSlowdown( distToObstruction, idealAccel, false );
	}

	if (ai_path_insert_pause_at_est_end->GetBool() && GetNavigator()->GetArrivalDistance() > 0.0)
	{
		InsertSlowdown( flTotalDist - GetNavigator()->GetArrivalDistance(), idealAccel, true );
	}

	// calc initial velocity based on immediate direction changes
	if ( ai_path_adjust_speed_on_immediate_turns->GetBool() && m_scriptMove.Count() > 1)
	{
		/*
		if ((GetOuter()->m_debugOverlays & OVERLAY_NPC_SELECTED_BIT))
		{
			Vector tmp = m_scriptMove[1].vecLocation - m_scriptMove[0].vecLocation;
			VectorNormalize( tmp );
			NDebugOverlay::Line( m_scriptMove[0].vecLocation + Vector( 0, 0, 10 ), m_scriptMove[0].vecLocation + tmp * 32 + Vector( 0, 0, 10 ), 255,255,255, true, 0.1 );
			
			NDebugOverlay::Line( m_scriptMove[0].vecLocation + Vector( 0, 0, 10 ), m_scriptMove[1].vecLocation + Vector( 0, 0, 10 ), 255,0,0, true, 0.1 );

			tmp = GetCurVel();
			VectorNormalize( tmp );
			NDebugOverlay::Line( m_scriptMove[0].vecLocation + Vector( 0, 0, 10 ), m_scriptMove[0].vecLocation + tmp * 32 + Vector( 0, 0, 10 ), 0,0,255, true, 0.1 );
		}
		*/

		Vector d1 = m_scriptMove[1].vecLocation - m_scriptMove[0].vecLocation;
		d1.z = 0;
		VectorNormalize( d1 );

		Vector d2 = GetCurVel();
		d2.z = 0;
		VectorNormalize( d2 );

		float dot = (DotProduct( d1, d2 ) + MIN_STEER_DOT);
		dot = clamp( dot, 0.0f, 1.0f );
		m_scriptMove[0].flMaxVelocity = m_scriptMove[0].flMaxVelocity * dot;
	}

	// clamp forward velocities
	for (i = 0; i < m_scriptMove.Count() - 1; i++ )
	{
		// find needed acceleration
		float dv = m_scriptMove[i+1].flMaxVelocity - m_scriptMove[i].flMaxVelocity;

		if (dv > 0.0)
		{
			// find time, distance to accel to next max vel
			float t1 = dv / idealAccel;
			float d1 = m_scriptMove[i].flMaxVelocity * t1 + 0.5 * (idealAccel) * t1 * t1;

			// is there enough distance
			if (d1 > m_scriptMove[i].flDist)
			{
				float r1, r2;

				// clamp the next velocity to the possible accel in the given distance
				if (SolveQuadratic( 0.5 * idealAccel, m_scriptMove[i].flMaxVelocity, -m_scriptMove[i].flDist, r1, r2 ))
				{
					m_scriptMove[i+1].flMaxVelocity = m_scriptMove[i].flMaxVelocity + idealAccel * r1;
				}
			}
		}
	}

	// clamp decel velocities
	for (i = m_scriptMove.Count() - 1; i > 0; i-- )
	{
		// find needed deceleration
		float dv = m_scriptMove[i].flMaxVelocity - m_scriptMove[i-1].flMaxVelocity;

		if (dv < 0.0)
		{
			// find time, distance to decal to next max vel
			float t1 = -dv / idealAccel;
			float d1 = m_scriptMove[i].flMaxVelocity * t1 + 0.5 * (idealAccel) * t1 * t1;

			// is there enough distance
			if (d1 > m_scriptMove[i-1].flDist)
			{
				float r1, r2;
				
				// clamp the next velocity to the possible decal in the given distance
				if (SolveQuadratic( 0.5 * idealAccel, m_scriptMove[i].flMaxVelocity, -m_scriptMove[i-1].flDist, r1, r2 ))
				{
					m_scriptMove[i-1].flMaxVelocity = m_scriptMove[i].flMaxVelocity + idealAccel * r1;
				}
			}
		}
	}

	/*
	for (i = 0; i < m_scriptMove.Count(); i++)
	{
		NDebugOverlay::Text( m_scriptMove[i].vecLocation, (const char *)CFmtStr( "%.2f ", m_scriptMove[i].flMaxVelocity  ), false, 0.1 );
		// DevMsg("%.2f ", m_scriptMove[i].flMaxVelocity );
	}
	// DevMsg("\n");
	*/

	// insert intermediate ideal velocities
	for (i = 0; i < m_scriptMove.Count() - 1;)
	{
		// accel to ideal
		float t1 = (idealVelocity - m_scriptMove[i].flMaxVelocity) / idealAccel;
		float d1 = m_scriptMove[i].flMaxVelocity * t1 + 0.5 * (idealAccel) * t1 * t1;

		// decel from ideal
		float t2 = (idealVelocity - m_scriptMove[i+1].flMaxVelocity) / idealAccel;
		float d2 = m_scriptMove[i+1].flMaxVelocity * t2 + 0.5 * (idealAccel) * t2 * t2;

		m_scriptMove[i].flDist = (m_scriptMove[i+1].vecLocation - m_scriptMove[i].vecLocation).Length2D();

		// is it possible to accel and decal to idealVelocity between next two nodes
		if (d1 + d2 < m_scriptMove[i].flDist)
		{
			Vector start =  m_scriptMove[i].vecLocation;
			Vector end = m_scriptMove[i+1].vecLocation;
			float dist = m_scriptMove[i].flDist;

			// insert the two points needed to end accel and start decel
			if (d1 > 1.0 && t1 > 0.1)
			{
				a = d1 / dist;

				script.Init();
				script.vecLocation = end * a + start * (1 - a);
				script.flMaxVelocity = idealVelocity;
				m_scriptMove.InsertAfter( i, script );
				i++;
			}

			if (dist - d2 > 1.0 && t2 > 0.1)
			{
				// DevMsg("%.2f : ", a );

				a = (dist - d2) / dist;

				script.Init();
				script.vecLocation = end * a + start * (1 - a);
				script.flMaxVelocity = idealVelocity;
				m_scriptMove.InsertAfter( i, script );
				i++;
			}

			i++;
		}
		else
		{
			// check to see if the amount of change needed to reach target is less than the ideal acceleration
			float flNeededAccel = fabs( DeltaV( m_scriptMove[i].flMaxVelocity, m_scriptMove[i+1].flMaxVelocity, m_scriptMove[i].flDist ) );
			if (flNeededAccel < idealAccel)
			{
				// if so, they it's possible to get a bit towards the ideal velocity
				float v1 = m_scriptMove[i].flMaxVelocity;
				float v2 = m_scriptMove[i+1].flMaxVelocity;
				float dist = m_scriptMove[i].flDist;

				// based on solving:
				//		v1+A*t1-v2-A*t2=0
				//		v1*t1+0.5*A*t1*t1+v2*t2+0.5*A*t2*t2-D=0

				float tmp = idealAccel*dist+0.5*v1*v1+0.5*v2*v2;
				Assert( tmp >= 0 );
				t1 = (-v1+sqrt( tmp )) / idealAccel;
				t2 = (v1+idealAccel*t1-v2)/idealAccel;

				// if this assert hits, write down the v1, v2, dist, and idealAccel numbers and send them to me (Ken).
				// go ahead the comment it out, it's safe, but I'd like to know a test case where it's happening
				//Assert( t1 > 0 && t2 > 0 );

				// check to make sure it's really worth it
				if (t1 > 0.0 && t2 > 0.0)
				{
					d1 = v1 * t1 + 0.5 * idealAccel * t1 * t1;
					
					/*
					d2 = v2 * t2 + 0.5 * idealAccel * t2 * t2;
					Assert( fabs( d1 + d2 - dist ) < 0.001 );
					*/

					float a = d1 / m_scriptMove[i].flDist;
					script.Init();
					script.vecLocation = m_scriptMove[i+1].vecLocation * a + m_scriptMove[i].vecLocation * (1 - a);
					script.flMaxVelocity = m_scriptMove[i].flMaxVelocity + idealAccel * t1;

					if (script.flMaxVelocity < idealVelocity)
					{
						// DevMsg("insert %.2f %.2f %.2f\n", m_scriptMove[i].flMaxVelocity, script.flMaxVelocity, m_scriptMove[i+1].flMaxVelocity ); 
						m_scriptMove.InsertAfter( i, script );
						i += 1;
					}
				}
			}
			i += 1;
		}
	}

	// clamp min velocities
	for (i = 0; i < m_scriptMove.Count(); i++)
	{
		m_scriptMove[i].flMaxVelocity = max( m_scriptMove[i].flMaxVelocity, MIN_VELOCITY );
	}

	// rebuild fields
	m_scriptMove[0].flElapsedTime = 0;
	for (i = 0; i < m_scriptMove.Count() - 1; )
	{
		m_scriptMove[i].flDist = (m_scriptMove[i+1].vecLocation - m_scriptMove[i].vecLocation).Length2D();

		if (m_scriptMove[i].flMaxVelocity == 0 && m_scriptMove[i+1].flMaxVelocity == 0)
		{
			// force a minimum velocity 
			//CE_assert
			//Assert( 0 );
			m_scriptMove[i+1].flMaxVelocity = 1.0;
		}

		float t = m_scriptMove[i].flDist / (0.5 * (m_scriptMove[i].flMaxVelocity + m_scriptMove[i+1].flMaxVelocity));
		m_scriptMove[i].flTime = t;

		/*
		if (m_scriptMove[i].flDist < 0.01)
		{
			// Assert( m_scriptMove[i+1].pWaypoint == NULL );

			m_scriptMove.Remove( i + 1 );
			continue;
		}
		*/

		m_scriptMove[i+1].flElapsedTime = m_scriptMove[i].flElapsedTime + m_scriptMove[i].flTime;

		i++;
	}

	/*
	for (i = 0; i < m_scriptMove.Count(); i++)
	{
		DevMsg("(%.2f : %.2f : %.2f)", m_scriptMove[i].flMaxVelocity, m_scriptMove[i].flDist, m_scriptMove[i].flTime );
		// DevMsg("(%.2f:%.2f)", m_scriptMove[i].flTime, m_scriptMove[i].flElapsedTime );
	}
	DevMsg("\n");
	*/
}