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; }
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 }
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; }
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; }
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; }
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; }
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; }
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; }
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; }
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; }
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; }