// check for collision course with frame body // tandir is normal vector from planet to target pos or dir static bool CheckSuicide(Ship *ship, const vector3d &tandir) { Body *body = ship->GetFrame()->GetBodyFor(); if (!body || !body->IsType(Object::TERRAINBODY)) return false; double vel = ship->GetVelocity().Dot(tandir); // vel towards is negative double dist = ship->GetPosition().Length() - MaxFeatureRad(body); if (vel < -1.0 && vel*vel > 2.0*ship->GetAccelMin()*dist) return true; return false; }
// check whether ship is at risk of colliding with frame body on current path // return values: //0 - no collision //1 - below feature height //2 - unsafe escape from effect radius //3 - unsafe entry to effect radius //4 - probable path intercept static int CheckCollision(Ship *ship, const vector3d &pathdir, double pathdist, const vector3d &tpos, double endvel, double r) { // ship is in obstructor's frame anyway, so is tpos if (pathdist < 100.0) return 0; Body *body = ship->GetFrame()->GetBodyFor(); if (!body) return 0; vector3d spos = ship->GetPosition(); double tlen = tpos.Length(), slen = spos.Length(); double fr = MaxFeatureRad(body); // if target inside, check if direct entry is safe (30 degree) if (tlen < r) { double af = (tlen > fr) ? 0.5 * (1 - (tlen-fr) / (r-fr)) : 0.5; if (pathdir.Dot(tpos) > -af*tlen) if (slen < fr) return 1; else return 3; else return 0; } // if ship inside, check for max feature height and direct escape (30 degree) if (slen < r) { if (slen < fr) return 1; double af = (slen > fr) ? 0.5 * (1 - (slen-fr) / (r-fr)) : 0.5; if (pathdir.Dot(spos) < af*slen) return 2; else return 0; } // now for the intercept calc // find closest point to obstructor double tanlen = -spos.Dot(pathdir); if (tanlen < 0 || tanlen > pathdist) return 0; // closest point outside path vector3d perpdir = (tanlen*pathdir + spos).Normalized(); double perpspeed = ship->GetVelocity().Dot(perpdir); double parspeed = ship->GetVelocity().Dot(pathdir); if (parspeed < 0) parspeed = 0; // shouldn't break any important case if (perpspeed > 0) perpspeed = 0; // prevent attempts to speculatively fly through planets // find time that ship will pass through that point // get velocity as if accelerating from start or end, pick smallest double ivelsqr = endvel*endvel + 2*ship->GetAccelFwd()*(pathdist-tanlen); // could put endvel in here double fvelsqr = parspeed*parspeed + 2*ship->GetAccelFwd()*tanlen; double tanspeed = sqrt(ivelsqr < fvelsqr ? ivelsqr : fvelsqr); double time = tanlen / (0.5 * (parspeed + tanspeed)); // actually correct? double dist = spos.Dot(perpdir) + perpspeed*time; // spos.perpdir should be positive if (dist < r) return 4; return 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; }
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 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; }