Пример #1
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;
}
Пример #2
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;
}
Пример #3
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;
}