bool AIParagonCmdOrbit::TimeStepUpdate()
{
	if (!ProcessChild()) {
		if (m_child->GetCommandName() == CmdName::CMD_PARAGON_FLYTO) {
			if (m_child->GetChildCommand() &&
				m_child->GetChildCommand()->GetCommandName() == CmdName::CMD_PARAGON_STEERAROUND)
			{
				AIParagonCmdSteerAround* cmd = reinterpret_cast<AIParagonCmdSteerAround*>(
					m_child->GetChildCommand());
				if (cmd->GetRemainingDistance() > 25000.0 && cmd->GetRemainingDistance() < 110000.0) {
					delete m_child;
					m_child = nullptr;
				}
			}
		}
		return false;
	}

	TerrainBody* planet = reinterpret_cast<TerrainBody*>(m_targetFrame->GetBody());
	assert(planet);

	// Orbit position is adjusted to avoid perfectly oposite direction (which might cause
	// vector math to go berserk with NaNs)
	vector3d ship_pos = m_ship->GetPositionRelTo(m_targetFrame) - (m_ship->GetOrientRelTo(m_targetFrame).VectorZ() * 5000.0);
	vector3d ship_to_planet_dir = (planet->GetPosition() - ship_pos).Normalized();
	vector3d orbit_position = planet->GetPosition() + (ship_to_planet_dir * GetTransitRadius(planet));
	m_child = new AIParagonCmdFlyTo(m_ship, m_targetFrame, orbit_position, 0, true);
	return false;
}
bool AIParagonCmdFlyTo::TimeStepUpdate()
{
	if (!ProcessChild()) return false;
	if (m_target) {
		m_targetPosition = m_target->GetPosition();
		if (m_targetType == Object::PLANET && m_targetPosition.LengthSqr() <= 1.0) {
			m_targetPosition =
				(m_ship->GetPositionRelTo(m_targetFrame) - m_target->GetPosition()).Normalized() * TRANSIT_GRAVITY_RANGE_1;
		}
	}
	CacheData();

	if (GoToTransitDistance()) return false;

	// Calculate direction and distance of travel
	bool clear_path = CheckClearPath(m_ship, m_targetFrame, m_targetPosition);
	if (!clear_path) {
		m_child = new AIParagonCmdSteerAround(m_ship, m_targetFrame, m_targetPosition);
		return false;
	}

	double distance = m_data.ship_to_target_distance - m_arrivalRadius;
	m_remainingDistance = distance;

	// If Transit drive 2 is active monitor the distance to slightly lower it when approaching the planet
	if (m_ship->GetFlightMode() == Ship::EFM_TRANSIT
		&& m_ship->GetController()->GetSpeedLimit() > TRANSIT_DRIVE_1_SPEED
		&& distance < TRANSIT_DRIVE_2_SPEED) 
	{
		m_ship->GetController()->SetSpeedLimit(TRANSIT_DRIVE_1_SPEED);
		m_ship->SetVelocity(-m_ship->GetOrient().VectorZ() * std::max<double>(distance, TRANSIT_DRIVE_1_SPEED));
	}
	// Transit/Maneuver mode depends on how far the target location is
	bool closer = m_targetPosition.LengthSqr() > m_data.ship_pos.LengthSqr();
	if (!closer) {
		if (distance > ARRIVAL_DISTANCE) {
			if (m_ship->GetFlightMode() != Ship::EFM_TRANSIT && m_ship->IsTransitPossible()) {
				m_ship->StartTransitDrive();
			}
		} else if (distance > DESTINATION_RADIUS) {
			if (m_ship->GetFlightMode() == Ship::EFM_TRANSIT) {
				m_ship->StopTransitDrive();
			}
		} else {
			m_ship->GetController()->SetSpeedLimit(m_ship->GetMaxManeuverSpeed());
			m_ship->SetVelocity(-m_ship->GetOrient().VectorZ() * std::min<double>(distance, m_ship->GetMaxManeuverSpeed())); // Creates a crash.
			return true;
		}
	} else {
		if (m_ship->GetFlightMode() == Ship::EFM_TRANSIT) {
			m_ship->StopTransitDrive();
		}
		return true;
	}
	m_ship->AIFaceDirection(m_targetFrame->GetOrientRelTo(m_ship->GetFrame()) * m_data.ship_to_target_dir);
	m_ship->CalculateVelocity(false);

	return false;
}
示例#3
0
bool AICmdFormation::TimeStepUpdate()
{
	if (!m_target) return true;
	if (!ProcessChild()) return false;		// In case we're doing an intercept

	if (m_ship->GetFlightState() == Ship::FLYING) m_ship->SetWheelState(false);
	else { LaunchShip(m_ship); return false; }

	// if too far away, do an intercept first
	// TODO: adjust distance cap by timestep so we don't bounce?
	if (m_target->GetPositionRelTo(m_ship).Length() > 30000.0) {
		m_child = new AICmdFlyTo(m_ship, m_target);
		ProcessChild(); return false;
	}

	matrix3x3d torient = m_target->GetOrientRelTo(m_ship->GetFrame());
	vector3d relpos = m_target->GetPositionRelTo(m_ship) + torient * m_posoff;
	vector3d relvel = -m_target->GetVelocityRelTo(m_ship);
	double targdist = relpos.Length();
	vector3d reldir = (targdist < 1e-16) ? vector3d(1,0,0) : relpos/targdist;

	// adjust for target acceleration
	matrix3x3d forient = m_target->GetFrame()->GetOrientRelTo(m_ship->GetFrame());
	vector3d targaccel = forient * m_target->GetLastForce() / m_target->GetMass();
	relvel -= targaccel * Pi::game->GetTimeStep();
	double maxdecel = m_ship->GetAccelFwd() + targaccel.Dot(reldir);
	if (maxdecel < 0.0) maxdecel = 0.0;

	// linear thrust
	double ispeed = calc_ivel(targdist, 0.0, maxdecel);
	vector3d vdiff = ispeed*reldir - relvel;
	m_ship->AIChangeVelDir(vdiff * m_ship->GetOrient());
	if (m_target->IsDecelerating()) m_ship->SetDecelerating(true);

	m_ship->AIFaceDirection(-torient.VectorZ());
	return false;					// never self-terminates
}
示例#4
0
bool AICmdFlyAround::TimeStepUpdate()
{
	if (m_targmode == 1 && !m_target) return true;
	if (!ProcessChild()) return false;

	// Not necessary unless it's a tier 1 AI
	if (m_ship->GetFlightState() == Ship::FLYING) m_ship->SetWheelState(false);
	else { LaunchShip(m_ship); return false; }

	double timestep = Pi::game->GetTimeStep();
	vector3d targpos = Targpos();		// target position in ship's frame
	vector3d obspos = m_obstructor->GetPositionRelTo(m_ship);
	double obsdist = obspos.Length();
	vector3d obsdir = obspos / obsdist;
	vector3d relpos = targpos - m_ship->GetPosition();

	// if too far away, fly to tangent
	if (obsdist > 1.1*m_alt)
	{
		double v;
		Frame *obsframe = GetNonRotFrame(m_obstructor);
		vector3d tangent = GenerateTangent(m_ship, obsframe, targpos, m_alt);
		vector3d tpos_obs = GetPosInFrame(obsframe, m_ship->GetFrame(), targpos);
		if (m_targmode != 1 && m_targmode != 2) v = m_vel;
		else if (relpos.LengthSqr() < obsdist) v = 0.0;
		else v = MaxVel((tpos_obs-tangent).Length(), tpos_obs.Length());
		m_child = new AICmdFlyTo(m_ship, obsframe, tangent, v, true);
		ProcessChild(); return false;
	}

	// limit m_vel by target proximity & distance covered per frame
	double vel = (m_targmode != 1 && m_targmode != 2) ? m_vel
		: MaxVel(relpos.Length(), targpos.Length());

	// all calculations in ship's frame
	vector3d fwddir = (obsdir.Cross(relpos).Cross(obsdir)).Normalized();
	vector3d tanvel = vel * fwddir;

	// frame body suicide check, response
	if (CheckSuicide(m_ship, -obsdir)) {
		m_ship->AIFaceDirection(m_ship->GetPosition());		// face away from planet
		m_ship->AIMatchVel(vector3d(0.0)); return false;
	}

	// max feature avoidance check, response
	if (obsdist < MaxFeatureRad(m_obstructor)) {
		double ang = m_ship->AIFaceDirection(-obsdir);
		m_ship->AIMatchVel(ang < 0.05 ? 1000.0 * -obsdir : 0.0);
		return false;
	}

	// calculate target velocity
	double alt = (tanvel * timestep + obspos).Length();		// unnecessary?
	double ivel = calc_ivel(alt - m_alt, 0.0, m_ship->GetAccelMin());

	vector3d finalvel = tanvel + ivel * obsdir;
	m_ship->AIMatchVel(finalvel);
	m_ship->AIFaceDirection(fwddir);

//	vector3d newhead = GenerateTangent(m_ship, m_obstructor->GetFrame(), fwddir);
//	newhead = GetPosInFrame(m_ship->GetFrame(), m_obstructor->GetFrame(), newhead);
//	m_ship->AIFaceDirection(newhead-m_ship->GetPosition());

	// termination condition for orbits
	vector3d thrust = m_ship->GetThrusterState();
	if (m_targmode >= 3 && thrust.LengthSqr() < 0.01) m_targmode++;
	if (m_targmode == 5) { m_ship->SetThrusterState(vector3d(0.0)); return true; }
	return false;
}
示例#5
0
bool AICmdDock::TimeStepUpdate()
{
	if (!ProcessChild()) return false;
	if (!m_target) return true;
	if (m_state == 1) m_state = 2;				// finished moving into dock start pos
	if (m_ship->GetFlightState() != Ship::FLYING) {		// todo: should probably launch if docked with something else
		m_ship->ClearThrusterState();
		return true; // docked, hopefully
	}

	// if we're not close to target, do a flyto first
	double targdist = m_target->GetPositionRelTo(m_ship).Length();
	if (targdist > m_target->GetBoundingRadius() * VICINITY_MUL * 1.5) {
		m_child = new AICmdFlyTo(m_ship, m_target);
		ProcessChild(); return false;
	}

	int port = m_target->GetMyDockingPort(m_ship);
	if (port == -1) {
		std::string msg;
		m_target->GetDockingClearance(m_ship, msg);
		port = m_target->GetMyDockingPort(m_ship);
		if (port == -1) { m_ship->AIMessage(Ship::AIERROR_REFUSED_PERM); return true; }
	}

	// state 0,2: Get docking data
	if (m_state == 0 || m_state == 2 || m_state == 4) {
		const SpaceStationType *type = m_target->GetSpaceStationType();
		SpaceStationType::positionOrient_t dockpos;
		type->GetShipApproachWaypoints(port, (m_state==0)?1:2, dockpos);
		matrix4x4d trot; m_target->GetRotMatrix(trot);
		if (m_state != 2) m_dockpos = trot * dockpos.pos + m_target->GetPosition();
		m_dockdir = (trot * dockpos.xaxis.Cross(dockpos.yaxis)).Normalized();
		m_dockupdir = (trot * dockpos.yaxis).Normalized();		// don't trust these enough
		if (type->dockMethod == SpaceStationType::ORBITAL) m_dockupdir = -m_dockupdir;
		m_state++;
	}

	if (m_state == 1) {			// fly to first docking waypoint
		m_child = new AICmdFlyTo(m_ship, m_target->GetFrame(), m_dockpos, 0.0, false);
		ProcessChild(); return false;
	}

	// second docking waypoint
	m_ship->SetWheelState(true);
	vector3d targpos = GetPosInFrame(m_ship->GetFrame(), m_target->GetFrame(), m_dockpos);
	vector3d relpos = targpos - m_ship->GetPosition();
	vector3d reldir = relpos.NormalizedSafe();
	vector3d relvel = m_ship->GetVelocityRelTo(m_target);
	double maxdecel = GetMaxDecel(m_ship, reldir, 0, 0);
	maxdecel -= GetGravityAtPos(m_target->GetFrame(), m_dockpos);
	m_ship->AIMatchPosVel2(reldir, relpos.Length(), relvel, 0.0, maxdecel);

	// massive pile of crap needed to get updir right outside the frame
	Frame *sframe = m_target->GetFrame();
	double ang = sframe->GetAngVelocity().Length() * Pi::game->GetTimeStep();
	matrix4x4d m; Frame::GetFrameTransform(sframe, m_ship->GetFrame(), m);
	if (!is_zero_general(ang) && sframe != m_ship->GetFrame()) {
		vector3d axis = sframe->GetAngVelocity().Normalized();
		m = m * matrix4x4d::RotateMatrix(ang, axis.x, axis.y, axis.z);
	}
	vector3d updir = m.ApplyRotationOnly(m_dockupdir);
	bool fin = m_ship->AIFaceOrient(m_dockdir, updir);
	if (m_state < 5 && fin && m_ship->GetWheelState() >= 1.0f) m_state++;

#ifdef DEBUG_AUTOPILOT
printf("AICmdDock dist = %.1f, speed = %.1f, ythrust = %.2f, state = %i\n",
	targdist, relvel.Length(), m_ship->GetThrusterState().y, m_state);
#endif

	return false;
}
示例#6
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;
}
示例#7
0
bool AICmdDock::TimeStepUpdate()
{
	if (!ProcessChild()) return false;
	if (!m_target) return true;
	if (m_state == 1) m_state = 2;				// finished moving into dock start pos
	if (m_ship->GetFlightState() != Ship::FLYING) {		// todo: should probably launch if docked with something else
		m_ship->ClearThrusterState();
		return true; // docked, hopefully
	}

	// if we're not close to target, do a flyto first
	double targdist = m_target->GetPositionRelTo(m_ship).Length();
	if (targdist > 16000.0) {
		m_child = new AICmdFlyTo(m_ship, m_target);
		ProcessChild(); return false;
	}

	int port = m_target->GetMyDockingPort(m_ship);
	if (port == -1) {
		std::string msg;
		m_target->GetDockingClearance(m_ship, msg);
		port = m_target->GetMyDockingPort(m_ship);
		if (port == -1) { m_ship->AIMessage(Ship::AIERROR_REFUSED_PERM); return true; }
	}

	// state 0,2: Get docking data
	if (m_state == 0 || m_state == 2 || m_state == 4) {
		const SpaceStationType *type = m_target->GetStationType();
		SpaceStationType::positionOrient_t dockpos;
		type->GetShipApproachWaypoints(port, (m_state==0)?1:2, dockpos);
		if (m_state != 2) m_dockpos = dockpos.pos;
		m_dockdir = dockpos.xaxis.Cross(dockpos.yaxis).Normalized();
		m_dockupdir = dockpos.yaxis.Normalized();		// don't trust these enough
		if (type->dockMethod == SpaceStationType::ORBITAL) m_dockupdir = -m_dockupdir;
		else if (m_state == 4) m_dockpos -= m_dockupdir * (m_ship->GetAabb().min.y + 1.0);
		if (m_state != 2) m_dockpos = m_target->GetOrient() * m_dockpos + m_target->GetPosition();
		m_state++;
		// should have m_dockpos in target frame, dirs relative to target orient
	}

	if (m_state == 1) {			// fly to first docking waypoint
		m_child = new AICmdFlyTo(m_ship, m_target->GetFrame(), m_dockpos, 0.0, false);
		ProcessChild(); return false;
	}

	// second docking waypoint
	m_ship->SetWheelState(true);
	vector3d targpos = GetPosInFrame(m_ship->GetFrame(), m_target->GetFrame(), m_dockpos);
	vector3d relpos = targpos - m_ship->GetPosition();
	vector3d reldir = relpos.NormalizedSafe();
	vector3d relvel = -m_target->GetVelocityRelTo(m_ship);

	double maxdecel = m_ship->GetAccelUp() - GetGravityAtPos(m_target->GetFrame(), m_dockpos);
	double ispeed = calc_ivel(relpos.Length(), 0.0, maxdecel);
	vector3d vdiff = ispeed*reldir - relvel;
	m_ship->AIChangeVelDir(vdiff * m_ship->GetOrient());
	if (vdiff.Dot(reldir) < 0) m_ship->SetDecelerating(true);

	// get rotation of station for next frame
	matrix3x3d trot = m_target->GetOrientRelTo(m_ship->GetFrame());
	double av = m_target->GetAngVelocity().Length();
	double ang = av * Pi::game->GetTimeStep();
	if (ang > 1e-16) {
		vector3d axis = m_target->GetAngVelocity().Normalized();
		trot = trot * matrix3x3d::Rotate(ang, axis);
	}
	double af;
	if (m_target->GetStationType()->dockMethod == SpaceStationType::ORBITAL)
		af = m_ship->AIFaceDirection(trot * m_dockdir);
	else af = m_ship->AIFaceDirection(m_ship->GetPosition().Cross(m_ship->GetOrient().VectorX()));
	if (af < 0.01) af = m_ship->AIFaceUpdir(trot * m_dockupdir, av) - ang;
	if (m_state < 5 && af < 0.01 && m_ship->GetWheelState() >= 1.0f) m_state++;

#ifdef DEBUG_AUTOPILOT
printf("AICmdDock dist = %.1f, speed = %.1f, ythrust = %.2f, state = %i\n",
	targdist, relvel.Length(), m_ship->GetThrusterState().y, m_state);
#endif

	return false;
}
示例#8
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;
}
示例#9
0
bool AICmdKill::TimeStepUpdate()
{
	if (!ProcessChild()) return false;
	if (!m_target || m_target->IsDead()) return true;

	if (m_ship->GetFlightState() == Ship::FLYING) m_ship->SetWheelState(false);
	else { LaunchShip(m_ship); return false; }

	const matrix3x3d &rot = m_ship->GetOrient();
	const ShipType &stype = m_ship->GetShipType();
	vector3d targpos = m_target->GetPositionRelTo(m_ship);
	vector3d targvel = m_target->GetVelocityRelTo(m_ship);
	vector3d targdir = targpos.NormalizedSafe();
	vector3d heading = -rot.VectorZ();
	// Accel will be wrong for a frame on timestep changes, but it doesn't matter
	vector3d targaccel = (m_target->GetVelocity() - m_lastVel) / Pi::game->GetTimeStep();
	m_lastVel = m_target->GetVelocity();		// may need next frame
	vector3d leaddir = m_ship->AIGetLeadDir(m_target, targaccel, 0);

	if (targpos.Length() >= VICINITY_MIN+1000.0) {	// if really far from target, intercept
//		printf("%s started AUTOPILOT\n", m_ship->GetLabel().c_str());
		m_child = new AICmdFlyTo(m_ship, m_target);
		ProcessChild(); return false;
	}

	// turn towards target lead direction, add inaccuracy
	// trigger recheck when angular velocity reaches zero or after certain time

	if (m_leadTime < Pi::game->GetTime())
	{
		double skillShoot = 0.5;		// todo: should come from AI stats

		double headdiff = (leaddir - heading).Length();
		double leaddiff = (leaddir - targdir).Length();
		m_leadTime = Pi::game->GetTime() + headdiff + (1.0*Pi::rng.Double()*skillShoot);

		// lead inaccuracy based on diff between heading and leaddir
		vector3d r(Pi::rng.Double()-0.5, Pi::rng.Double()-0.5, Pi::rng.Double()-0.5);
		vector3d newoffset = r * (0.02 + 2.0*leaddiff + 2.0*headdiff)*Pi::rng.Double()*skillShoot;
		m_leadOffset = (heading - leaddir);		// should be already...
		m_leadDrift = (newoffset - m_leadOffset) / (m_leadTime - Pi::game->GetTime());

		// Shoot only when close to target

		double vissize = 1.3 * m_ship->GetPhysRadius() / targpos.Length();
		vissize += (0.05 + 0.5*leaddiff)*Pi::rng.Double()*skillShoot;
		if (vissize > headdiff) m_ship->SetGunState(0,1);
		else m_ship->SetGunState(0,0);
		if (targpos.LengthSqr() > 4000*4000) m_ship->SetGunState(0,0);		// temp
	}
	m_leadOffset += m_leadDrift * Pi::game->GetTimeStep();
	double leadAV = (leaddir-targdir).Dot((leaddir-heading).NormalizedSafe());	// leaddir angvel
	m_ship->AIFaceDirection((leaddir + m_leadOffset).Normalized(), leadAV);


	vector3d evadethrust(0,0,0);
	if (m_evadeTime < Pi::game->GetTime())		// evasion time!
	{
		double skillEvade = 0.5;			// todo: should come from AI stats
		m_evadeTime = Pi::game->GetTime() + Pi::rng.Double(3.0,10.0) * skillEvade;
		if (heading.Dot(targdir) < 0.7) skillEvade += 0.5;		// not in view
		skillEvade += Pi::rng.Double(-0.5,0.5);

		vector3d targhead = -m_target->GetOrient().VectorZ() * rot;		// obj space
		vector3d targav = m_target->GetAngVelocity();

		if (skillEvade < 1.6 && targhead.z < 0.0) {		// smart chase
			vector3d objvel = targvel * rot;			// obj space targvel
			if ((objvel.x*objvel.x + objvel.y*objvel.y) < 10000) {
				evadethrust.x = objvel.x > 0.0 ? 1.0 : -1.0;
				evadethrust.y = objvel.y > 0.0 ? 1.0 : -1.0;
			}
		}
		else
		{
			skillEvade += targpos.Length() / 2000;				// 0.25 per 500m

			if (skillEvade < 1.0 && targav.Length() < 0.05) {	// smart evade, assumes facing
				evadethrust.x = targhead.x < 0.0 ? 1.0 : -1.0;
				evadethrust.y = targhead.y < 0.0 ? 1.0 : -1.0;
			}
			else if (skillEvade < 1.3) {			// random two-thruster evade
				evadethrust.x = (Pi::rng.Int32()&8) ? 1.0 : -1.0;
				evadethrust.y = (Pi::rng.Int32()&4) ? 1.0 : -1.0;
			}
			else if (skillEvade < 1.6) {			// one thruster only
				if (Pi::rng.Int32()&8)
					evadethrust.x = (Pi::rng.Int32()&4) ? 1.0 : -1.0;
				else evadethrust.y = (Pi::rng.Int32()&4) ? 1.0 : -1.0;
			}
			// else no evade thrust
		}
	}
	else evadethrust = m_ship->GetThrusterState();


	// todo: some logic behind desired range? pass from higher level
	if (m_closeTime < Pi::game->GetTime())
	{
		double skillEvade = 0.5;
		if (heading.Dot(targdir) < 0.7) skillEvade += 0.5;		// not in view

		m_closeTime = Pi::game->GetTime() + skillEvade * Pi::rng.Double(1.0,5.0);

		double reqdist = 500.0 + skillEvade * Pi::rng.Double(-500.0, 250);
		double dist = targpos.Length(), ispeed;
		double rearaccel = stype.linThrust[ShipType::THRUSTER_REVERSE] / m_ship->GetMass();
		rearaccel += targaccel.Dot(targdir);
		// v = sqrt(2as), positive => towards
		double as2 = 2.0 * rearaccel * (dist - reqdist);
		if (as2 > 0) ispeed = sqrt(as2); else ispeed = -sqrt(-as2);
		double vdiff = ispeed + targvel.Dot(targdir);

		if (skillEvade + Pi::rng.Double() > 1.5) evadethrust.z = 0.0;
		else if (vdiff*vdiff < 400.0) evadethrust.z = 0.0;
		else evadethrust.z = (vdiff > 0.0) ? -1.0 : 1.0;
	}
	else evadethrust.z = m_ship->GetThrusterState().z;
	m_ship->SetThrusterState(evadethrust);

	return false;
}
示例#10
0
bool AIParagonCmdSteerAround::TimeStepUpdate()
{
	if (!ProcessChild()) {
		return false;
	}
	if(m_ship->GetFrame()->GetBody() == nullptr) {
		return true;
	}
	if (!m_ship->IsTransitPossible()) { // SteerAround must always be in transit range, otherwise flyto can do the job
		//assert(false);
		m_child = new AIParagonCmdGoTo(m_ship, m_targetFrame, m_targetPosition, true);
		return false;
	}

	CacheData();

	if (m_data.target_to_sbody_distance > DESTINATION_RADIUS) {
		m_remainingDistance = SAFEACOS(-m_data.ship_to_sbody_dir.Dot(-m_data.target_to_sbody_dir)) *
			m_data.sbody_transit_radius;
	} else {
		m_remainingDistance = m_data.ship_to_sbody_distance - m_data.sbody_transit_radius;
	}

	if (m_stage == ESS_ENTER) {
		m_actionDesc = "Heading towards Entry point";
		CalculateEntryPoint();
		if (ApplyMotion(m_entryPoint)) {
			m_stage = ESS_AROUND;
		}
		return false;
	} else if (m_stage == ESS_AROUND) {
		m_actionDesc = "Steering around obstacle";
		CalculateExitPoint();
		const double transit_range = 2500.0;
		const double transit_low = m_data.sbody_transit_radius + transit_range;
		const double transit_high = m_data.sbody_transit_radius + (transit_range * 2.0);
		const double transit_altitude = transit_low + ((transit_high - transit_low) / 2.0);
		vector3d up_vector = -m_data.ship_to_sbody_dir;
		vector3d right_vector = m_data.ship_to_exit_dir.Cross(up_vector).Normalized();
		vector3d velocity_vector = up_vector.Cross(right_vector).Normalized();
		const double altitude = m_data.ship_to_sbody_distance;
		double transit_factor = 1.0;
		if (!m_heatDisposalMode && m_ship->GetHullTemperature() > 0.8) {
			m_heatDisposalMode = true;
		} else if (m_heatDisposalMode && m_ship->GetHullTemperature() <= 0.1) {
			m_heatDisposalMode = false;
		}
		if (altitude > transit_altitude) {
			if (altitude > transit_altitude + transit_range) {
				velocity_vector = velocity_vector + (-up_vector * 0.007);
			} else if(altitude > transit_altitude + 500.0) {
				velocity_vector = velocity_vector + (-up_vector * 0.002);
			} else if(altitude > transit_altitude + 100.0) {
				velocity_vector = velocity_vector + (-up_vector * 0.001);
			}
		} else if (altitude <= transit_altitude) {
			if (altitude < transit_altitude - transit_range) {
				velocity_vector = velocity_vector + (up_vector * 0.35);
			} else if(altitude < transit_altitude - 500.0) {
				velocity_vector = velocity_vector + (up_vector * 0.002);
			} else if (altitude < transit_altitude - 100.0) {
				velocity_vector = velocity_vector + (up_vector * 0.001);
			}
		}
		double distance = std::min<double>(m_remainingDistance, m_data.ship_to_exit_distance);
		if (distance > TRANSIT_DRIVE_1_SPEED) {
			if (!m_heatDisposalMode) {
				if (m_ship->GetFlightMode() != Ship::EFM_TRANSIT) {
					m_ship->StartTransitDrive();
				}
			} else {
				if (m_ship->GetFlightMode() == Ship::EFM_TRANSIT) {
					m_ship->StopTransitDrive();
				}
			}
		} else if (distance > DESTINATION_RADIUS) {
			if (m_ship->GetFlightMode() == Ship::EFM_TRANSIT) {
				transit_factor = Clamp(
					//m_remainingDistance / ARRIVAL_DISTANCE, 
					distance / TRANSIT_DRIVE_1_SPEED,
					0.1, 1.0);
			}
		} else {
			if (m_ship->GetFlightMode() == Ship::EFM_TRANSIT) {
				m_ship->StopTransitDrive();
			}
			m_stage = ESS_EXIT;
		}
		m_ship->AIFaceDirection(m_targetFrame->GetOrientRelTo(m_ship->GetFrame()) * velocity_vector);
		m_ship->AIFaceUpdir(m_targetFrame->GetOrientRelTo(m_ship->GetFrame()) * up_vector);
		m_ship->CalculateVelocity(true, transit_factor);
		return false;
	} else if (m_stage == ESS_EXIT) {
		m_actionDesc = "Heading towards exit point";
		CalculateExitPoint();
		const double distance = (m_data.ship_pos - m_exitPoint).Length();
		if (distance > DESTINATION_RADIUS) {
			vector3d exit_point = m_exitPoint;
			// Transfer exit point to target frame to pass it to goto
			FrameCorrectPosition(m_data.sframe, m_targetFrame, exit_point);
			m_child = new AIParagonCmdGoTo(m_ship, m_targetFrame, exit_point);
			//m_child = new AIParagonCmdGoTo(m_ship, m_targetFrame, exit_point, 
			//	m_ship->GetOrientRelTo(m_targetFrame), 0.0, m_ship->GetMaxManeuverSpeed());
			m_stage = ESS_END;
			return false;
		}
	}
	return true;
}
示例#11
0
bool AIParagonCmdDock::TimeStepUpdate()
{
	m_ship->SetRemotlyDocked(false);
	if (m_ship->GetFlightState() == Ship::JUMPING || !ProcessChild()) {
		return false;
	}
	if (!m_station) {
		return true;
	}

	// Fly to station
	if (m_station->GetPositionRelTo(m_ship).Length() > GetArrivalRadius(m_station) + 100.0) {
		m_child = new AIParagonCmdFlyTo(m_ship, m_station);
		ProcessChild();
		return false;
	}
	
	if(m_state == EDS_ZERO) {
		if (m_ship->GetVelocity().Length() > 100.0) { // Anything I write here causes weird behavior!
			m_ship->SetDecelerating(true);
			m_ship->GetController()->SetSpeedLimit(0.0);
			m_ship->AIMatchVel(vector3d(0.0, 0.0, 0.0));
			return false;
		} else {
			m_ship->SetDecelerating(false);
			m_ship->SetJuice(1.0);
			m_ship->ClearThrusterState();
			m_ship->SetAngThrusterState(vector3d(0.0, 0.0, 0.0));
			m_ship->SetVelocity(vector3d(0.0, 0.0, 0.0));
			m_state = EDS_START;
			return false;
		}
	} else if (m_state == EDS_START) {				// Zero speed
		m_ship->SetVelocity(vector3d(0.0, 0.0, 0.0));
		int port = m_station->GetMyDockingPort(m_ship);
		if (port == -1) {
			std::string msg;
			bool cleared = m_station->GetDockingClearance(m_ship, msg);
			port = m_station->GetMyDockingPort(m_ship);
			if (!cleared || port == -1) {
				m_ship->AIMessage(Ship::AIERROR_REMOTE_DOCKING);
				m_ship->SetRemotlyDocked(true);
				port = m_station->GetRemoteDockingPort();
				m_ship->SetDockedWith(m_station, port);
				return true;
			}
		}
		//m_station->RequestDockingApproach(m_ship);
		m_state = EDS_WAIT_FOR_GO;
		return false;
	} else if (m_state == EDS_WAIT_FOR_GO) {// Get docking signal from station (docking queue)
		// Disabled for now, leaving station overrides the approach queue so it's a bit pointless.
		/*if(m_station->CheckDockingApproachSignal(m_ship)) {
			m_state = EDS_WAYPOINTS;
		} else {
			// Waiting for approach signal
			if(m_ship->IsPlayerShip() && !m_waitingForSignalMessage) {
				Pi::cpan->InfLog()->ImportantMessage(m_station->GetLabel(), "Standby for approach signal");
				m_waitingForSignalMessage = true;
			}
		}*/
		m_state = EDS_WAYPOINTS;
		return false;
	} else if (m_state == EDS_WAYPOINTS) {	// Prepare docking points
		SpaceStationType::positionOrient_t po1, po2;
		vector3d ship_pos = m_ship->GetPositionRelTo(m_station);

		bool got_orient_1 = m_station->GetStationType()->GetShipApproachWaypoints(
			m_station->GetMyDockingPort(m_ship), 1, po1);
		bool got_orient_2 = m_station->GetStationType()->GetShipApproachWaypoints(
			m_station->GetMyDockingPort(m_ship), 2, po2);
		m_approachPoints.push_back(po1); // Used for approach standby distance
		m_approachPoints.push_back(po1);
		m_approachPoints.push_back(po2);

		// Apply frame offset
		if (m_station->GetStationType()->dockMethod != SpaceStationType::ORBITAL) {
			for (unsigned int i = 0; i < m_approachPoints.size(); ++i) {
				/*m_approachPoints[i].pos -= m_approachPoints[i].yaxis.Normalized() * (
				m_ship->GetAabb().min.y + 1.0);*/
				m_approachPoints[i].pos = m_station->GetOrient() * m_approachPoints[i].pos +
					m_station->GetPosition();
				m_approachPoints[i].xaxis = m_station->GetOrient() * m_approachPoints[i].xaxis;
				m_approachPoints[i].yaxis = m_station->GetOrient() * m_approachPoints[i].yaxis;
				m_approachPoints[i].zaxis = m_station->GetOrient() * m_approachPoints[i].zaxis;
			}
			m_nextApproachPoint = 2;
		} else {
			// For orbital stations, point 0 should be standby for approach distance from point 1
			double station_to_point_dist;
			vector3d station_to_point = (m_approachPoints[0].pos - m_station->GetPosition()).Normalized(
				station_to_point_dist);
			m_approachPoints[0].pos = station_to_point * GetArrivalRadius(m_station);
			m_nextApproachPoint = 1;

			vector3d station_to_ship = (ship_pos - m_station->GetPosition()).Normalized();
			double ship_angle = station_to_point.Dot(station_to_ship);
			if(ship_angle < 0.0f) { // Ship needs to go around
				// Calculate perpendicular vector between station to point and station to ship
				vector3d v = -station_to_point;
				v = v * station_to_ship.Dot(v);
				SpaceStationType::positionOrient_t perp_po = m_approachPoints[0];
				perp_po.pos = (station_to_ship - v).NormalizedSafe() * GetArrivalRadius(m_station);
				m_approachPoints.insert(m_approachPoints.begin(),  perp_po);
			}
		}

		m_child = new AIParagonCmdGoTo(m_ship, m_station->GetFrame(), m_approachPoints[0]);
		ProcessChild();
		m_state = EDS_DOCKING;
		return false;
	} else if (m_state == EDS_DOCKING) {	// Proceed to dock
		m_ship->ClearThrusterState();
		m_ship->SetAngThrusterState(vector3d(0.0, 0.0, 0.0));

		if(m_station->GetStationType()->dockMethod == SpaceStationType::ORBITAL) {
			// Update end point (keeps changing due to station local rotation/translation)
			m_station->GetStationType()->GetShipApproachWaypoints(m_station->GetMyDockingPort(m_ship), 2,
				m_approachPoints.back());
			m_child = new AIParagonCmdGoTo(m_ship, m_station->GetFrame(), 
				m_approachPoints[m_nextApproachPoint]);
		} else {
			m_child = new AIParagonCmdGoTo(m_ship, m_station->GetFrame(),
				m_approachPoints[m_nextApproachPoint - 1], m_approachPoints[m_nextApproachPoint]);
		}

		ProcessChild();
		m_nextApproachPoint += 1;
		if(m_nextApproachPoint >= m_approachPoints.size()) {
			m_state = EDS_DOCK;
		}
		return false;
	} else if (m_state == EDS_DOCK) {		// Dock!
		return false;
	} /*else {
		VSLog::stream << "Paragon Docking: Unknown state " << m_state << std::endl;
		VSLog::outputLog();
	}*/

	return false;
}
示例#12
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;
}