Пример #1
0
// ok, need thing to step down through bodies and find closest approach
// modify targpos directly to aim short of dangerous bodies
static bool ParentSafetyAdjust(Ship *ship, Frame *targframe, vector3d &targpos, vector3d &targvel)
{
	Body *body = 0;
	Frame *frame = targframe->GetNonRotFrame();
	while (frame)
	{
		if (ship->GetFrame()->GetNonRotFrame() == frame) break;		// ship in frame, stop
		if (frame->GetBody()) body = frame->GetBody();			// ignore grav points?

		double sdist = ship->GetPositionRelTo(frame).Length();
		if (sdist < frame->GetRadius()) break;					// ship inside frame, stop

		frame = frame->GetParent()->GetNonRotFrame();			// check next frame down
	}
	if (!body) return false;

	// aim for zero velocity at surface of that body
	// still along path to target

	vector3d targpos2 = targpos - ship->GetPosition();
	double targdist = targpos2.Length();
	double bodydist = body->GetPositionRelTo(ship).Length() - MaxEffectRad(body, ship)*1.5;
	if (targdist < bodydist) return false;
	targpos -= (targdist - bodydist) * targpos2 / targdist;
	targvel = body->GetVelocityRelTo(ship->GetFrame());
	return true;
}
Пример #2
0
// ok, need thing to step down through bodies and find closest approach
// modify targpos directly to aim short of dangerous bodies
static bool ParentSafetyAdjust(Ship *ship, Frame *targframe, const vector3d &posoff, vector3d &targpos)
{
	Body *body = 0;
	Frame *frame = targframe->IsRotatingFrame() ? targframe->m_parent : targframe;
	while (frame)
	{
		double sdist = ship->GetPositionRelTo(frame).Length();				// ship position in that frame
		if (sdist < frame->GetRadius()) break;

		while (frame && !(body = frame->GetBodyFor()))
			frame = frame->m_parent;
		if (!frame) return false;

		frame = body->GetFrame()->m_parent;
		if (body->HasDoubleFrame()) frame = frame->m_parent;
	}
	if (!body) return false;

	// ok, so if body != 0, aim for zero velocity at distance to surface of that body
	// still along path to target

	vector3d targpos2 = targpos - ship->GetPosition();
	double targdist = targpos2.Length();
	double bodydist = body->GetPositionRelTo(ship).Length() - MaxEffectRad(body, ship)*1.5;
	if (targdist < bodydist) return false;
	targpos -= (targdist - bodydist) * targpos2 / targdist;
//	printf("Adjusted targpos for safety from %s: old = %.1f, new = %.1f\n",
//		body->GetLabel().c_str(), targdist, (targpos-ship->GetPosition()).Length());
	return true;
}
Пример #3
0
AICmdFlyAround::AICmdFlyAround(Ship *ship, Body *obstructor, double relalt, float hungriness)
	: AICommand (ship, CMD_FLYAROUND)
{
	m_fuelEconomy = Clamp(hungriness, 0.0f, 1.0f);
	double alt = relalt*MaxEffectRad(obstructor, ship);
	Setup(obstructor, alt, 0.0, 3, 0, 0, vector3d(0.0));
}
Пример #4
0
// Fly to vicinity of body
AICmdFlyTo::AICmdFlyTo(Ship *ship, Body *target) : AICommand(ship, CMD_FLYTO)
{
	m_frame = 0; m_state = -6; m_lockhead = true; m_endvel = 0; m_tangent = false;
	if (!target->IsType(Object::TERRAINBODY)) m_dist = VICINITY_MIN;
	else m_dist = VICINITY_MUL*MaxEffectRad(target, ship);

	if (target->IsType(Object::SPACESTATION) && static_cast<SpaceStation*>(target)->IsGroundStation()) {
		m_posoff = target->GetPosition() + 15000.0 * target->GetOrient().VectorY();
//		m_posoff += 500.0 * target->GetOrient().VectorX();
		m_targframe = target->GetFrame(); m_target = 0;
	}
	else { m_target = target; m_targframe = 0; }

	if (ship->GetPositionRelTo(target).Length() <= 15000.0) m_targframe = 0;
}
Пример #5
0
// Pursue ship, not body
AICmdFlyTo::AICmdFlyTo(Ship *ship, Ship *target, float hungriness) : AICommand (ship, CMD_FLYTO)
{
	double dist = std::max(VICINITY_MIN, VICINITY_MUL*MaxEffectRad(target, ship));
	m_targframe = GetNonRotFrame(target);
	m_posoff = dist * m_ship->GetPositionRelTo(m_targframe).Normalized();
	m_posoff += target->GetPosition();

	m_endvel = 0; m_tangent = 0;
	m_state = -1; m_frame = 0;

	// check if we're already close enough
	if (dist > m_ship->GetPositionRelTo(target).Length()) m_state = 5;

	m_fuelEconomy = Clamp(hungriness, 0.0f, 1.0f);;
	m_targetShip = target;
}
Пример #6
0
// Fly to "vicinity" of body
AICmdFlyTo::AICmdFlyTo(Ship *ship, Body *target) : AICommand (ship, CMD_FLYTO)
{
	double dist = std::max(VICINITY_MIN, VICINITY_MUL*MaxEffectRad(target, ship));
	if (target->IsType(Object::SPACESTATION) && static_cast<SpaceStation *>(target)->IsGroundStation()) {
		matrix4x4d rot; target->GetRotMatrix(rot);
		m_posoff = target->GetPosition() + dist * vector3d(rot[4], rot[5], rot[6]);		// up vector for starport
		m_targframe = target->GetFrame();
	}
	else {
		m_targframe = GetNonRotFrame(target);
		m_posoff = dist * m_ship->GetPositionRelTo(m_targframe).Normalized();
		m_posoff += target->GetPosition();
	}

	m_endvel = 0; m_tangent = 0;
	m_state = -1; m_frame = 0;

	// check if we're already close enough
	if (dist > m_ship->GetPositionRelTo(target).Length()) m_state = 5;
}
Пример #7
0
AICmdFlyAround::AICmdFlyAround(Ship *ship, Body *obstructor, double relalt)
	: AICommand (ship, CMD_FLYAROUND)
{
	double alt = relalt*MaxEffectRad(obstructor, ship);
	Setup(obstructor, alt, 0.0, 3, 0, 0, vector3d(0.0));
}
Пример #8
0
bool AICmdFlyTo::TimeStepUpdate()
{
	double timestep = Pi::game->GetTimeStep();
	vector3d targvel = GetVelInFrame(m_ship->GetFrame(), m_targframe, m_posoff);
	vector3d relvel = m_ship->GetVelocity() - targvel;
	vector3d targpos = GetPosInFrame(m_ship->GetFrame(), m_targframe, m_posoff);
	bool safe = ParentSafetyAdjust(m_ship, m_targframe, m_posoff, targpos);
	double endvel = safe ? 0.0 : m_endvel;			// don't use endvel if safety-adjusted
	vector3d relpos = targpos - m_ship->GetPosition();
	vector3d reldir = relpos.NormalizedSafe();
	double targdist = relpos.Length();

	// sort out gear, launching
	if (m_ship->GetFlightState() == Ship::FLYING) m_ship->SetWheelState(false);
	else { LaunchShip(m_ship); return false; }

	// frame switch stuff - clear children/collision state
	if (m_frame != m_ship->GetFrame()) {
		if (m_child) { delete m_child; m_child = 0; }
		if (m_frame && m_tangent) return true;		// regen tangent on frame switch
		m_frame = m_ship->GetFrame();
		m_reldir = reldir;							// for +vel termination condition
	}

#ifdef DEBUG_AUTOPILOT
if (m_ship->IsType(Object::PLAYER))
printf("Autopilot dist = %.1f, speed = %.1f, zthrust = %.2f, term = %.3f, state = %i\n",
	targdist, relvel.Length(), m_ship->GetThrusterState().z, reldir.Dot(m_reldir), m_state);
#endif

	Body *body = m_frame->GetBodyFor();
	double erad = MaxEffectRad(body, m_ship);
	if (!m_tangent || !(body == m_targframe->GetBodyFor()))
	{
		// process path collisions with frame body
		int coll = CheckCollision(m_ship, reldir, targdist, targpos, endvel, erad);
		if (coll == 0) {				// no collision
			if (m_child) { delete m_child; m_child = 0; m_state = -1; }
		}
		else if (coll == 1) {			// below feature height, target not below
			double ang = m_ship->AIFaceDirection(m_ship->GetPosition());
			m_ship->AIMatchVel(ang < 0.05 ? 1000.0 * m_ship->GetPosition().Normalized() : 0.0);
			m_state = -3; return false;
		}
		else {							// same thing for 2/3/4
			if (!m_child) m_child =
				new AICmdFlyAround(m_ship, body, erad, 0.0, m_targframe, m_posoff);
			ProcessChild(); m_state = -5; return false;
		}
	}

	// if dangerously close to local body, pretend target isn't moving
	if (body) {
		double localdist = m_ship->GetPosition().Length();
		if (targdist > localdist && localdist < 1.5*MaxFeatureRad(body))
			relvel += targvel;
	}

	// regenerate state to flipmode if we're off course
	bool overshoot = CheckOvershoot(m_ship, reldir, targdist, relvel, endvel);
	if (m_tangent && m_state == -4 && !overshoot) return true;			// bail out
	if (m_state < 0) m_state = GetFlipMode(m_ship, relpos, relvel);

	// linear thrust
	double ang, maxdecel = GetMaxDecel(m_ship, reldir, m_state, &ang);
	maxdecel -= GetGravityAtPos(m_targframe, m_posoff);
	if(maxdecel <= 0) { m_ship->AIMessage(Ship::AIERROR_GRAV_TOO_HIGH); return true; }
	bool cap = m_ship->AIMatchPosVel2(reldir, targdist, relvel, endvel, maxdecel);

	// path overshoot check, response
	if (m_state < 3 && overshoot) {
		double ispeed = calc_ivel(targdist, endvel, maxdecel);
		m_ship->AIFaceDirection(ispeed*reldir - relvel);
		m_state = -4; return false;
	}

	// flip check - if facing forward and not accelerating at maximum
	if (m_state == 1 && ang > 0.99 && !cap) m_state = 2;

	// termination conditions
	if (m_state == 3) m_state++;					// finished last adjustment, hopefully
	else if (endvel > 0.0) { if (reldir.Dot(m_reldir) < 0.9) m_state = 4; }
	else if (targdist < 0.5*m_ship->GetAccelMin()*timestep*timestep) m_state = 3;

	// set heading according to current state
	if (m_state < 2) {		// this shit still needed? yeah, sort of
		vector3d newrelpos = targpos - m_ship->AIGetNextFramePos();
		if ((newrelpos + reldir*100.0).Dot(relpos) <= 0.0)
			m_ship->AIFaceDirection(reldir);			// last frames turning workaround
		else m_ship->AIFaceDirection(newrelpos);
	}
	else if (m_state == 2) m_ship->AIFaceDirection(-reldir);	// hmm. -relvel instead?
	else m_ship->AIMatchAngVelObjSpace(vector3d(0.0));

	if (m_state == 4) return true;
	return false;
}
Пример #9
0
bool AICmdFlyTo::TimeStepUpdate()
{
	if (!m_target && !m_targframe) return true;			// deleted object

	// sort out gear, launching
	if (m_ship->GetFlightState() == Ship::FLYING) m_ship->SetWheelState(false);
	else { LaunchShip(m_ship); return false; }

	// generate base target pos (with vicinity adjustment) & vel 
	double timestep = Pi::game->GetTimeStep();
	vector3d targpos, targvel;
	if (m_target) {
		targpos = m_target->GetPositionRelTo(m_ship->GetFrame());
		targpos -= (targpos - m_ship->GetPosition()).NormalizedSafe() * m_dist;
		targvel = m_target->GetVelocityRelTo(m_ship->GetFrame());
	} else {
		targpos = GetPosInFrame(m_ship->GetFrame(), m_targframe, m_posoff);
		targvel = GetVelInFrame(m_ship->GetFrame(), m_targframe, m_posoff);		
	}
	Frame *targframe = m_target ? m_target->GetFrame() : m_targframe;
	ParentSafetyAdjust(m_ship, targframe, targpos, targvel);
	vector3d relpos = targpos - m_ship->GetPosition();
	vector3d reldir = relpos.NormalizedSafe();
	vector3d relvel = targvel - m_ship->GetVelocity();
	double targdist = relpos.Length();

#ifdef DEBUG_AUTOPILOT
if (m_ship->IsType(Object::PLAYER))
printf("Autopilot dist = %.1f, speed = %.1f, zthrust = %.2f, state = %i\n",
	targdist, relvel.Length(), m_ship->GetThrusterState().z, m_state);
#endif

	// frame switch stuff - clear children/collision state
	if (m_frame != m_ship->GetFrame()) {
		if (m_child) { delete m_child; m_child = 0; }
		if (m_tangent && m_frame) return true;		// regen tangent on frame switch
		m_reldir = reldir;							// for +vel termination condition
		m_frame = m_ship->GetFrame();
	}

// TODO: collision needs to be processed according to vdiff, not reldir?

	Body *body = m_frame->GetBody();
	double erad = MaxEffectRad(body, m_ship);
	if ((m_target && body != m_target)
		|| (m_targframe && (!m_tangent || body != m_targframe->GetBody())))
	{
		int coll = CheckCollision(m_ship, reldir, targdist, targpos, m_endvel, erad);
		if (coll == 0) {				// no collision
			if (m_child) { delete m_child; m_child = 0; }
		}
		else if (coll == 1) {			// below feature height, target not below
			double ang = m_ship->AIFaceDirection(m_ship->GetPosition());
			m_ship->AIMatchVel(ang < 0.05 ? 1000.0 * m_ship->GetPosition().Normalized() : vector3d(0.0));
		}
		else {							// same thing for 2/3/4
			if (!m_child) m_child = new AICmdFlyAround(m_ship, m_frame->GetBody(), erad*1.05, 0.0);
			static_cast<AICmdFlyAround*>(m_child)->SetTargPos(targpos);
			ProcessChild();
		}
		if (coll) { m_state = -coll; return false; }
	}
	if (m_state < 0 && m_state > -6 && m_tangent) return true;			// bail out
	if (m_state < 0) m_state = targdist > 10000000.0 ? 1 : 0;			// still lame

	double maxdecel = m_state ? m_ship->GetAccelFwd() : m_ship->GetAccelRev();
	double gravdir = -reldir.Dot(m_ship->GetPosition().Normalized());
	maxdecel -= gravdir * GetGravityAtPos(m_ship->GetFrame(), m_ship->GetPosition());
	if (maxdecel < 0) maxdecel = 0.0;

	// target ship acceleration adjustment
	if (m_target && m_target->IsType(Object::SHIP)) {
		Ship *targship = static_cast<Ship*>(m_target);
		matrix3x3d orient = m_target->GetFrame()->GetOrientRelTo(m_frame);
		vector3d targaccel = orient * targship->GetLastForce() / m_target->GetMass();
		// fudge: targets accelerating towards you are usually going to flip
		if (targaccel.Dot(reldir) < 0.0 && !targship->IsDecelerating()) targaccel *= 0.5;
		relvel += targaccel * timestep;
		maxdecel += targaccel.Dot(reldir);
		// if we have margin lower than 10%, fly as if 10% anyway
		maxdecel = std::max(maxdecel, 0.1*m_ship->GetAccelFwd());
	}

	double curspeed = -relvel.Dot(reldir);
	double tt = sqrt(2.0*targdist / maxdecel);
	if (tt < timestep) tt = timestep;
	vector3d perpvel = relvel + reldir * curspeed;
	double perpspeed = perpvel.Length();
	vector3d perpdir = (perpspeed > 1e-30) ? perpvel / perpspeed : vector3d(0,0,1);

	double sidefactor = perpspeed / (tt*0.5);
	if (curspeed > (tt+timestep)*maxdecel || maxdecel < sidefactor) {
		m_ship->AIFaceDirection(relvel);
		m_ship->AIMatchVel(targvel);
		m_state = -5; return false;
	}
	else maxdecel = sqrt(maxdecel*maxdecel - sidefactor*sidefactor);

	// ignore targvel if we could clear with side thrusters in a fraction of minimum time
//	if (perpspeed < tt*0.01*m_ship->GetAccelMin()) perpspeed = 0;

	// calculate target speed
	double ispeed = (maxdecel < 1e-10) ? 0.0 : calc_ivel(targdist, m_endvel, maxdecel);

	// cap target speed according to spare fuel remaining
	double fuelspeed = m_ship->GetSpeedReachedWithFuel();
	if (m_target && m_target->IsType(Object::SHIP)) fuelspeed -=
		m_ship->GetVelocityRelTo(Pi::game->GetSpace()->GetRootFrame()).Length();
	if (ispeed > curspeed && curspeed > 0.9*fuelspeed) ispeed = curspeed;

	// Don't exit a frame faster than some fraction of radius
//	double maxframespeed = 0.2 * m_frame->GetRadius() / timestep;
//	if (m_frame->GetParent() && ispeed > maxframespeed) ispeed = maxframespeed;

	// cap perpspeed according to what's needed now
	perpspeed = std::min(perpspeed, 2.0*sidefactor*timestep);
	
	// cap sdiff by thrust...
	double sdiff = ispeed - curspeed;
	double linaccel = sdiff < 0 ?
		std::max(sdiff, -m_ship->GetAccelFwd()*timestep) :
		std::min(sdiff, m_ship->GetAccelFwd()*timestep);

	// linear thrust application, decel check
	vector3d vdiff = linaccel*reldir + perpspeed*perpdir;
	bool decel = sdiff <= 0;
	m_ship->SetDecelerating(decel);
	if (decel) m_ship->AIChangeVelBy(vdiff * m_ship->GetOrient());
	else m_ship->AIChangeVelDir(vdiff * m_ship->GetOrient());

	// work out which way to head 
	vector3d head = reldir;
	if (!m_state && sdiff < -1.2*maxdecel*timestep) m_state = 1;
	if (m_state && sdiff < maxdecel*timestep*60) head = -head;
	if (!m_state && decel) sidefactor = -sidefactor;
	head = head*maxdecel + perpdir*sidefactor;

	// face appropriate direction
	if (m_state >= 3) m_ship->AIMatchAngVelObjSpace(vector3d(0.0));
	else m_ship->AIFaceDirection(head);
	if (body && body->IsType(Object::PLANET) && m_ship->GetPosition().LengthSqr() < 2*erad*erad)
		m_ship->AIFaceUpdir(m_ship->GetPosition());		// turn bottom thruster towards planet

	// termination conditions: check
	if (m_state >= 3) return true;					// finished last adjustment, hopefully
	if (m_endvel > 0.0) { if (reldir.Dot(m_reldir) < 0.9) return true; }
	else if (targdist < 0.5*m_ship->GetAccelMin()*timestep*timestep) m_state = 3;
	return false;
}
Пример #10
0
bool AICmdFlyTo::TimeStepUpdate()
{
	double timestep = Pi::game->GetTimeStep();
	vector3d targvel = GetVelInFrame(m_ship->GetFrame(), m_targframe, m_posoff);
	vector3d relvel = m_ship->GetVelocity() - targvel;
	vector3d targpos = GetPosInFrame(m_ship->GetFrame(), m_targframe, m_posoff);

	// only if pursuing a ship -- repeat actions from contructor again, since the position of the ship changed
	if(m_targetShip != 0) {
		m_targframe = m_targetShip->GetFrame();
		m_posoff = m_ship->GetPositionRelTo(m_targframe);
		m_posoff += m_targetShip->GetPosition();
		targpos = m_targetShip->GetPositionRelTo(m_ship->GetFrame());
		targpos.x += VICINITY_MIN/2; // avoid collisions, set target a bit away
		targvel = m_targetShip->GetVelocityRelTo(m_ship->GetFrame()); // todo: check general frame!
		relvel = m_ship->GetVelocity() - targvel;
	}

	bool safe = ParentSafetyAdjust(m_ship, m_targframe, m_posoff, targpos);
	double endvel = safe ? 0.0 : m_endvel;			// don't use endvel if safety-adjusted
	vector3d relpos = targpos - m_ship->GetPosition();
	vector3d reldir = relpos.NormalizedSafe();
	double targdist = relpos.Length();
	double haveFuelToReachThisVelSafely;

	m_fuelEconomy = Clamp(m_fuelEconomy, 0.0f, 1.0f);
	haveFuelToReachThisVelSafely = m_ship->GetVelocityReachedWithFuelUsed(1.0/(6-3*m_fuelEconomy) * m_ship->GetFuel());

	// sort out gear, launching
	if (m_ship->GetFlightState() == Ship::FLYING) m_ship->SetWheelState(false);
	else { LaunchShip(m_ship); return false; }

	// frame switch stuff - clear children/collision state
	if (m_frame != m_ship->GetFrame()) {
		if (m_child) { delete m_child; m_child = 0; }
		if (m_frame && m_tangent) return true;		// regen tangent on frame switch
		m_frame = m_ship->GetFrame();
		m_reldir = reldir;							// for +vel termination condition
	}

#ifdef DEBUG_AUTOPILOT
if (m_ship->IsType(Object::PLAYER))
printf("Autopilot dist = %.1f, speed = %.1f, zthrust = %.2f, term = %.3f, crit = %.3f, fuel = %.3f, exhaust = %.0f, safeVel = %.0f, state = %i\n",
	targdist, relvel.Length(), m_ship->GetThrusterState().z, reldir.Dot(m_reldir),
	relvel.Dot(reldir)/(relvel.Length()+1e-7), m_ship->GetFuel(),
	m_ship->GetEffectiveExhaustVelocity() , haveFuelToReachThisVelSafely, m_state);
#endif

	Body *body = m_frame->GetBodyFor();
	double erad = MaxEffectRad(body, m_ship);
	if (m_targetShip == 0 && (!m_tangent || !(body == m_targframe->GetBodyFor())))
	{
		// process path collisions with frame body
		int coll = CheckCollision(m_ship, reldir, targdist, targpos, endvel, erad);
		if (coll == 0) {				// no collision
			if (m_child) { delete m_child; m_child = 0; m_state = -1; }
		}
		else if (coll == 1) {			// below feature height, target not below
			double ang = m_ship->AIFaceDirection(m_ship->GetPosition());
			m_ship->AIMatchVel(ang < 0.05 ? 1000.0 * m_ship->GetPosition().Normalized() : vector3d(0.0));
			m_state = -3; return false;
		}
		else {							// same thing for 2/3/4
			if (!m_child) m_child =
				new AICmdFlyAround(m_ship, body, erad, 0.0, m_targframe, m_posoff, m_fuelEconomy);
			ProcessChild(); m_state = -5; return false;
		}
	}

	// if dangerously close to local body, pretend target isn't moving
	if (body) {
		double localdist = m_ship->GetPosition().Length();
		if (targdist > localdist && localdist < 1.5*MaxFeatureRad(body))
			relvel += targvel;
	}

	// regenerate state to flipmode if we're off course
	bool overshoot = CheckOvershoot(m_ship, reldir, targdist, relvel, endvel);
	if (m_tangent && m_state == -4 && !overshoot) return true;			// bail out
	if (m_state < 0) m_state = GetFlipMode(m_ship, relpos, relvel);

	// linear thrust
	double ang, maxdecel = GetMaxDecel(m_ship, reldir, m_state, &ang);
	maxdecel -= GetGravityAtPos(m_targframe, m_posoff);
	if(maxdecel <= 0) { m_ship->AIMessage(Ship::AIERROR_GRAV_TOO_HIGH); return true; }
	bool cap = m_ship->AIMatchPosVel2(reldir, targdist, relvel, endvel, maxdecel);

	// path overshoot check, response
	if (m_state < 3 && overshoot) {
		double ispeed = calc_ivel(targdist, endvel, maxdecel);
		m_ship->AIFaceDirection(ispeed*reldir - relvel);
		m_state = -4; return false;
	}

	// turn thrusters off when in acceleration phase and low on fuel
	double angleCos = relvel.Dot(reldir)/(relvel.Length()+1e-7);
	if(m_state == 1 && relvel.Length() > haveFuelToReachThisVelSafely &&
			// match direction precisely if close, if more than a day far, save the fuel and do not be so strict about the direction
			(angleCos > 0.9999 || (angleCos > 0.995 && targdist/(haveFuelToReachThisVelSafely+1e-7) > 86400))
			) {
		m_ship->SetThrusterState(vector3d(0.0));
	} else if(m_state == 1 && relvel.Length() > 1.1*haveFuelToReachThisVelSafely) {
		// match direction without increasing speed .. saves fuel in deceleration phase
		vector3d v;
		matrix4x4d m;

		m_ship->GetRotMatrix(m);
		v = targvel + relvel.NormalizedSafe()*haveFuelToReachThisVelSafely;
		m_ship->AIMatchVel(v);
	}

	// flip check - if facing forward and not accelerating at maximum
	if (m_state == 1 && ang > 0.99 && !cap) m_state = 2;

	// termination conditions
	if (m_state == 3) m_state++;					// finished last adjustment, hopefully
	else if (endvel > 0.0) { if (reldir.Dot(m_reldir) < 0.9) m_state = 4; }
	else if (targdist < 0.5*m_ship->GetAccelMin()*timestep*timestep) m_state = 3;

	// set heading according to current state
	if (m_state < 2) {		// this shit still needed? yeah, sort of
		vector3d newrelpos = targpos - m_ship->AIGetNextFramePos();
		if ((newrelpos + reldir*100.0).Dot(relpos) <= 0.0)
			m_ship->AIFaceDirection(reldir);			// last frames turning workaround
		else m_ship->AIFaceDirection(newrelpos);
	}
	else if (m_state == 2) m_ship->AIFaceDirection(-reldir);	// hmm. -relvel instead?
	else m_ship->AIMatchAngVelObjSpace(vector3d(0.0));

	if (m_state == 4) return true;
	return false;
}