Example #1
0
std::string Planet::CardinalSuffix() const {
    std::string retval = "";
    // Planets are grouped into asteroids, and non-asteroids
    // Asteroids receive a localized prefix
    if (Type() == PT_ASTEROIDS)
        retval.append(UserString("NEW_ASTEROIDS_SUFFIX") + " ");

    TemporaryPtr<System> cur_system = GetSystem(SystemID());
    if (cur_system) {
        if (cur_system->OrbitOfPlanet(ID()) < 0) {
            ErrorLogger() << "Planet " << Name() << "(" << ID() << ") "
                          << "has no current orbit";
            retval.append(RomanNumber(1));
            return retval;
        }
        int num_planets_lteq = 0;  // number of planets at this orbit or smaller
        int num_planets_total = 0;
        bool prior_current_planet = true;
        const std::vector<int>& sys_orbits = cur_system->PlanetIDsByOrbit();
        for (std::vector<int>::const_iterator it = sys_orbits.begin();
             it != sys_orbits.end(); ++it)
        {
            if (*it == INVALID_OBJECT_ID)
                continue;

            PlanetType other_planet_type = GetPlanet(*it)->Type();
            if (other_planet_type == INVALID_PLANET_TYPE)
                continue;

            if (Type() != PT_ASTEROIDS) {
                if (other_planet_type != PT_ASTEROIDS) {
                    ++num_planets_total;
                    if (prior_current_planet)
                        ++num_planets_lteq;
                }
            } else {
                if (other_planet_type == PT_ASTEROIDS) {
                    ++num_planets_total;
                    if (prior_current_planet)
                        ++num_planets_lteq;
                }
            }

            if (*it == ID())
                prior_current_planet =false;
        }
        // For asteroids: If no other asteroids in this system, suffix does not receive a number
        if (Type() != PT_ASTEROIDS || (Type() == PT_ASTEROIDS && num_planets_total > 1))
            retval.append(RomanNumber(num_planets_lteq));
    } else {
        ErrorLogger() << "Planet " << Name() << "(" << ID() << ") not assigned to a system";
    }
    return retval;
}
Example #2
0
void Planet::AddBuilding(int building_id) {
    if (this->Contains(building_id)) {
        Logger().debugStream() << "Planet::AddBuilding this planet " << this->Name() << " already contained building " << building_id;
        return;
    }
    //Logger().debugStream() << "Planet::AddBuilding " << this->Name() << " adding building: " << building_id;
    if (Building* building = GetObject<Building>(building_id)) {
        if (System* system = GetObject<System>(this->SystemID())) {
            system->Insert(building);
        } else {
            Logger().errorStream() << "... planet is not located in a system?!?!";
            building->MoveTo(X(), Y());
            building->SetSystem(SystemID());
        }
        building->SetPlanetID(ID());
        m_buildings.insert(building_id);
    } else {
        Logger().errorStream() << "Planet::AddBuilding() : Attempted to add an id of a non-building object to a planet.";
    }
    StateChangedSignal();
}
Example #3
0
QString ConditionalAccessDescriptor::toString() const
{
    return QString("Conditional Access: sid(0x%1) pid(0x%2) data_size(%3)")
        .arg(SystemID(),0,16).arg(PID(),0,16).arg(DataSize());
}
Example #4
0
std::string Planet::CardinalSuffix() const {
    std::string retval = "";
    // Early return for invalid ID
    if (ID() == INVALID_OBJECT_ID) {
        WarnLogger() << "Planet " << Name() << " has invalid ID";
        return retval;
    }

    auto cur_system = GetSystem(SystemID());
    // Early return for no system
    if (!cur_system) {
        ErrorLogger() << "Planet " << Name() << "(" << ID()
                      << ") not assigned to a system";
        return retval;
    }

    // Early return for unknown orbit
    if (cur_system->OrbitOfPlanet(ID()) < 0) {
        WarnLogger() << "Planet " << Name() << "(" << ID() << ") "
                     << "has no current orbit";
        retval.append(RomanNumber(1));
        return retval;
    }

    int num_planets_lteq = 0;  // number of planets at this orbit or smaller
    int num_planets_total = 0;
    bool prior_current_planet = true;

    for (int sys_orbit : cur_system->PlanetIDsByOrbit()) {
        if (sys_orbit == INVALID_OBJECT_ID)
            continue;

        // all other planets are in further orbits
        if (sys_orbit == ID()) {
            prior_current_planet = false;
            ++num_planets_total;
            ++num_planets_lteq;
            continue;
        }

        PlanetType other_planet_type = GetPlanet(sys_orbit)->Type();
        if (other_planet_type == INVALID_PLANET_TYPE)
            continue;

        // only increment suffix for non-asteroid planets
        if (Type() != PT_ASTEROIDS) {
            if (other_planet_type != PT_ASTEROIDS) {
                ++num_planets_total;
                if (prior_current_planet)
                    ++num_planets_lteq;
            }
        } else {
            // unless the planet being named is an asteroid
            // then only increment suffix for asteroid planets
            if (other_planet_type == PT_ASTEROIDS) {
                ++num_planets_total;
                if (prior_current_planet)
                    ++num_planets_lteq;
            }
        }
    }

    // Planets are grouped into asteroids, and non-asteroids
    if (Type() != PT_ASTEROIDS) {
        retval.append(RomanNumber(num_planets_lteq));
    } else {
        // Asteroids receive a localized prefix
        retval.append(UserString("NEW_ASTEROIDS_SUFFIX"));
        // If no other asteroids in this system, do not append an ordinal
        if (num_planets_total > 1)
            retval.append(" " + RomanNumber(num_planets_lteq));
    }
    return retval;
}
Example #5
0
void Fleet::CalculateRoute() const {
    m_travel_distance = 0.0;
    m_travel_route.clear();

    //Logger().debugStream() << "Fleet::CalculateRoute";
    if (m_moving_to == INVALID_OBJECT_ID)
        return;

    if (m_prev_system != INVALID_OBJECT_ID && SystemID() == m_prev_system) {
        // if we haven't actually left yet, we have to move from whichever system we are at now

        if (!GetSystem(m_moving_to))
            return; // destination system doesn't exist or doesn't exist in known universe, so can't move to it.  leave route empty.

        std::pair<std::list<int>, double> path;
        try {
            path = GetUniverse().ShortestPath(m_prev_system, m_moving_to, this->Owner());
        } catch (...) {
            Logger().debugStream() << "Fleet::CalculateRoute couldn't find route to system(s):"
                                   << " fleet's previous: " << m_prev_system << " or moving to: " << m_moving_to;
        }
        m_travel_route = path.first;
        m_travel_distance = path.second;

        return;
    }


    int dest_system_id = m_moving_to;

    // Geoff: commenting out the early exit code of the owner of a fleet doesn't
    // have visibility of the destination system, since this was preventing the
    // human client's attempts to find routes for enemy fleets, for which the
    // player's client doesn't know which systems are visible, and since
    // visibility of a system on the current turn isn't necessary to plot 
    // route to it now that empire's can remember systems they've seen on
    // previous turns.
    //if (universe.GetObjectVisibilityByEmpire(dest_system_id, this->Owner()) <= VIS_NO_VISIBILITY) {
    //    // destination system isn't visible to this fleet's owner, so the fleet can't move to it
    //
    //    // check if system to which fleet is moving is visible to the fleet's owner.  this should always be true, but just in case...
    //    if (universe.GetObjectVisibilityByEmpire(m_next_system, this->Owner()) <= VIS_NO_VISIBILITY)
    //        return; // next system also isn't visible; leave route empty.
    //
    //    // safety check: ensure supposedly visible object actually exists in known universe.
    //    if (!GetSystem(m_next_system)) {
    //        Logger().errorStream() << "Fleet::CalculateRoute found system with id " << m_next_system << " should be visible to this fleet's owner, but the system doesn't exist in the known universe!";
    //        return; // abort if object doesn't exist in known universe... can't path to it if it's not there, even if it's considered visible for some reason...
    //    }
    //
    //    // next system is visible, so move to that instead of ordered destination (m_moving_to)
    //    dest_system_id = m_next_system;
    //}

    // if we're between systems, the shortest route may be through either one
    if (this->CanChangeDirectionEnRoute()) {

        std::pair<std::list<int>, double> path1;
        try {
            path1 = GetUniverse().ShortestPath(m_next_system, dest_system_id, this->Owner());
        } catch (...) {
            Logger().debugStream() << "Fleet::CalculateRoute couldn't find route to system(s):"
                                   << " fleet's next: " << m_next_system << " or destination: " << dest_system_id;
        }
        const std::list<int>& sys_list1 = path1.first;
        if (sys_list1.empty()) {
            Logger().errorStream() << "Fleet::CalculateRoute got empty route from ShortestPath";
            return;
        }
        const UniverseObject* obj = GetUniverseObject(sys_list1.front());
        if (!obj) {
            Logger().errorStream() << "Fleet::CalculateRoute couldn't get path start object with id " << path1.first.front();
            return;
        }
        double dist_x = obj->X() - this->X();
        double dist_y = obj->Y() - this->Y();
        double dist1 = std::sqrt(dist_x*dist_x + dist_y*dist_y);

        std::pair<std::list<int>, double> path2;
        try {
            path2 = GetUniverse().ShortestPath(m_prev_system, dest_system_id, this->Owner());
        } catch (...) {
            Logger().debugStream() << "Fleet::CalculateRoute couldn't find route to system(s):"
                                   << " fleet's previous: " << m_prev_system << " or destination: " << dest_system_id;
        }
        const std::list<int>& sys_list2 = path2.first;
        if (sys_list2.empty()) {
            Logger().errorStream() << "Fleet::CalculateRoute got empty route from ShortestPath";
            return;
        }
        obj = GetUniverseObject(sys_list2.front());
        if (!obj) {
            Logger().errorStream() << "Fleet::CalculateRoute couldn't get path start object with id " << path2.first.front();
            return;
        }
        dist_x = obj->X() - this->X();
        dist_y = obj->Y() - this->Y();
        double dist2 = std::sqrt(dist_x*dist_x + dist_y*dist_y);

        // pick whichever path is quicker
        if (dist1 + path1.second < dist2 + path2.second) {
            m_travel_route = path1.first;
            m_travel_distance = dist1 + path1.second;
        } else {
            m_travel_route = path2.first;
            m_travel_distance = dist2 + path2.second;
        }

    } else {

        std::pair<std::list<int>, double> route;
        try {
            route = GetUniverse().ShortestPath(m_next_system, dest_system_id, this->Owner());
        } catch (...) {
            Logger().debugStream() << "Fleet::CalculateRoute couldn't find route to system(s):"
                                   << " fleet's next: " << m_next_system << " or destination: " << dest_system_id;
        }
        const std::list<int>& sys_list = route.first;
        if (sys_list.empty()) {
            Logger().errorStream() << "Fleet::CalculateRoute got empty route from ShortestPath";
            return;
        }
        const UniverseObject* obj = GetUniverseObject(sys_list.front());
        if (!obj) {
            Logger().errorStream() << "Fleet::CalculateRoute couldn't get path start object with id " << route.first.front();
            return;
        }
        double dist_x = obj->X() - this->X();
        double dist_y = obj->Y() - this->Y();
        double dist = std::sqrt(dist_x*dist_x + dist_y*dist_y);
        m_travel_route = route.first;
        m_travel_distance = dist + route.second;
    }
}
Example #6
0
void Fleet::MovementPhase() {
    //Logger().debugStream() << "Fleet::MovementPhase this: " << this->Name() << " id: " << this->ID();

    m_arrived_this_turn = false;
    m_arrival_starlane = INVALID_OBJECT_ID;

    int prev_prev_system = m_prev_system;

    Empire* empire = Empires().Lookup(this->Owner());

    // if owner of fleet can resupply ships at the location of this fleet, then
    // resupply all ships in this fleet
    if (empire && empire->FleetOrResourceSupplyableAtSystem(this->SystemID()))
        for (Fleet::const_iterator ship_it = this->begin(); ship_it != this->end(); ++ship_it)
            if (Ship* ship = GetShip(*ship_it))
                ship->Resupply();


    System* current_system = GetSystem(SystemID());
    System* const initial_system = current_system;
    std::list<MovePathNode> move_path = this->MovePath();
    std::list<MovePathNode>::const_iterator it = move_path.begin();
    std::list<MovePathNode>::const_iterator next_it = it;
    if (next_it != move_path.end())
        ++next_it;


    // is the ship stuck in a system for a whole turn?
    if (current_system) {
        // in a system.  if there is no system after the current one in the
        // path, or the current and next nodes have the same system id, that
        // is an actual system, then won't be moving this turn.
        if ((next_it == move_path.end()) ||
            (it->object_id != INVALID_OBJECT_ID && it->object_id == next_it->object_id)
           )
        {
            // fuel regeneration for ships in stationary fleet
            if (this->FinalDestinationID() == INVALID_OBJECT_ID ||
                this->FinalDestinationID() == this->SystemID())
            {
                for (Fleet::const_iterator ship_it = this->begin(); ship_it != this->end(); ++ship_it) {
                    if (Ship* ship = GetShip(*ship_it))
                        if (Meter* fuel_meter = ship->UniverseObject::GetMeter(METER_FUEL)) {
                            fuel_meter->AddToCurrent(0.1001);
                            fuel_meter->BackPropegate();
                        }
                }
            }
            return;
        }
    }


    // if fleet not moving, nothing more to do.
    if (move_path.empty() || move_path.size() == 1) {
        //Logger().debugStream() << "Fleet::MovementPhase: Fleet move path is empty or has only one entry.  doing nothing";
        return;
    }

    //Logger().debugStream() << "Fleet::MovementPhase move path:";
    //for (std::list<MovePathNode>::const_iterator it = move_path.begin(); it != move_path.end(); ++it)
    //    Logger().debugStream() << "... (" << it->x << ", " << it->y << ") at object id: " << it->object_id << " eta: " << it->eta << (it->turn_end ? " (end of turn)" : " (during turn)");


    // move fleet in sequence to MovePathNodes it can reach this turn
    double fuel_consumed = 0.0;
    for (it = move_path.begin(); it != move_path.end(); ++it) {
        next_it = it;   ++next_it;

        System* system = GetSystem(it->object_id);

        //Logger().debugStream() << "... node " << (system ? system->Name() : "no system");

        // is this system the last node reached this turn?  either it's an end of turn node,
        // or there are no more nodes after this one on path
        bool node_is_next_stop = (it->turn_end || next_it == move_path.end());


        if (system) {
            // node is a system.  explore system for all owners of this fleet
            if (empire)
                empire->AddExploredSystem(it->object_id);

            prev_prev_system = m_prev_system;
            m_prev_system = system->ID();               // passing a system, so update previous system of this fleet

            bool resupply_here = empire ? empire->FleetOrResourceSupplyableAtSystem(system->ID()) : false;

            // if this system can provide supplies, reset consumed fuel and refuel ships
            if (resupply_here) {
                //Logger().debugStream() << " ... node has fuel supply.  consumed fuel for movement reset to 0 and fleet resupplied";
                fuel_consumed = 0.0;
                for (Fleet::const_iterator ship_it = this->begin(); ship_it != this->end(); ++ship_it) {
                    Ship* ship = GetShip(*ship_it);
                    assert(ship);
                    ship->Resupply();
                }
            }


            if (node_is_next_stop) {                    // is system the last node reached this turn?
                system->Insert(this);                       // fleet ends turn at this node.  insert fleet into system
                current_system = system;
                //Logger().debugStream() << "... ... inserted fleet into system";
                break;
            } else {
                // fleet will continue past this system this turn.
                //Logger().debugStream() << "... ... moved fleet to system (not inserted)";
                if (!resupply_here) {
                    fuel_consumed += 1.0;
                    //Logger().debugStream() << "... ... consuming 1 unit of fuel to continue moving.  total fuel consumed now: " << fuel_consumed;
                } else {
                    //Logger().debugStream() << "... ... not consuming fuel to depart resupply system";
                }
            }

        } else {
            // node is not a system.
            if (node_is_next_stop) {                    // node is not a system, but is it the last node reached this turn?
                MoveTo(it->x, it->y);                       // fleet ends turn at this node.  move fleet here
                //Logger().debugStream() << "... ... moved fleet to position";
                break;
            }
        }
    }


    //Logger().debugStream() << "Fleet::MovementPhase rest of move path:";
    //for (std::list<MovePathNode>::const_iterator it2 = it; it2 != move_path.end(); ++it2)
    //    Logger().debugStream() << "... (" << it2->x << ", " << it2->y << ") at object id: " << it2->object_id << " eta: " << it2->eta << (it2->turn_end ? " (end of turn)" : " (during turn)");

    // update next system
    if (m_moving_to != SystemID() && next_it != move_path.end() && it != move_path.end()) {
        // there is another system later on the path to aim for.  find it
        for (; next_it != move_path.end(); ++next_it) {
            if (GetSystem(next_it->object_id)) {
                //Logger().debugStream() << "___ setting m_next_system to " << next_it->object_id;
                m_next_system = next_it->object_id;
                break;
            }
        }

    } else {
        // no more systems on path
        m_arrived_this_turn = current_system != initial_system;
        m_arrival_starlane = prev_prev_system;
        m_moving_to = m_next_system = m_prev_system = INVALID_OBJECT_ID;
    }


    // consume fuel from ships in fleet
    if (fuel_consumed > 0.0) {
        for (const_iterator ship_it = begin(); ship_it != end(); ++ship_it)
            if (Ship* ship = GetShip(*ship_it))
                if (Meter* meter = ship->UniverseObject::GetMeter(METER_FUEL)) {
                    meter->AddToCurrent(-fuel_consumed);
                    meter->BackPropegate();
                }
    }
}
Example #7
0
void Fleet::SetRoute(const std::list<int>& route) {
    if (route.empty())
        throw std::invalid_argument("Fleet::SetRoute() : Attempted to set an empty route.");

    if (UnknownRoute())
        throw std::invalid_argument("Fleet::SetRoute() : Attempted to set an unknown route.");

    if (m_prev_system != SystemID() && m_prev_system == route.front() && !CanChangeDirectionEnRoute())
        throw std::invalid_argument("Fleet::SetRoute() : Illegally attempted to change a fleet's direction while it was in transit.");

    m_travel_route = route;


    // calculate length of line segments between systems on route, and sum up to determine length of route between
    // systems on route.  (Might later add distance from fleet to first system on route to this to get the total
    // route length, or this may itself be the total route length if the fleet is at the first system on the route).
    m_travel_distance = 0.0;
    for (std::list<int>::const_iterator it = m_travel_route.begin(); it != m_travel_route.end(); ++it) {
        std::list<int>::const_iterator next_it = it;    ++next_it;

        if (next_it == m_travel_route.end())
            break;  // current system is the last on the route, so don't need to add any additional distance.

        const System* cur_sys = GetSystem(*it);
        if (!cur_sys) {
            Logger().errorStream() << "Fleet::SetRoute() couldn't get system with id " << *it;
            return;
        }

        const System* next_sys = GetSystem(*next_it);
        if (!next_sys) {
            Logger().errorStream() << "Fleet::SetRoute() couldn't get system with id " << *next_it;
            return;
        }

        double dist_x = next_sys->X() - cur_sys->X();
        double dist_y = next_sys->Y() - cur_sys->Y();
        m_travel_distance += std::sqrt(dist_x*dist_x + dist_y*dist_y);
    }


    // if resetting to no movement while in a system
    if (SystemID() != INVALID_OBJECT_ID && SystemID() == m_travel_route.back()) {
        m_moving_to = INVALID_OBJECT_ID;
        m_next_system = INVALID_OBJECT_ID;
        m_prev_system = INVALID_OBJECT_ID;

    } else {
        // if we're already moving, add in the distance from where we are to the first system in the route
        if (SystemID() != route.front()) {
            const System* starting_system = GetSystem(route.front());
            if (!starting_system) {
                Logger().errorStream() << "Fleet::SetRoute couldn't get system with id " << route.front();
                return;
            }
            double dist_x = starting_system->X() - this->X();
            double dist_y = starting_system->Y() - this->Y();
            m_travel_distance += std::sqrt(dist_x*dist_x + dist_y*dist_y);
        }
        m_moving_to = m_travel_route.back();
        if (m_prev_system != SystemID() && m_prev_system == m_travel_route.front()) {
            m_prev_system = m_next_system;      // if already in transit and turning around, swap prev and next
        } else if (SystemID() == route.front()) {
            m_prev_system = SystemID();
        }
        std::list<int>::const_iterator it = m_travel_route.begin();
        m_next_system = m_prev_system == SystemID() ? (*++it) : (*it);
    }

    StateChangedSignal();
}
Example #8
0
std::list<MovePathNode> Fleet::MovePath(const std::list<int>& route) const {
    std::list<MovePathNode> retval;

    if (route.empty())
        return retval;                                      // nowhere to go => empty path
    // if (route.size() == 1) do nothing special.  this fleet is probably on the starlane leading to
    //                        its final destination.  normal looping to read destination should work fine
    if (route.size() == 2 && route.front() == route.back())
        return retval;                                      // nowhere to go => empty path
    if (this->Speed() < FLEET_MOVEMENT_EPSILON) {
        retval.push_back(MovePathNode(this->X(), this->Y(), true, ETA_NEVER, this->SystemID(), INVALID_OBJECT_ID, INVALID_OBJECT_ID));
        return retval;                                      // can't move => path is just this system with explanatory ETA
    }

    double fuel =       Fuel();
    double max_fuel =   MaxFuel();

    //Logger().debugStream() << "Fleet " << this->Name() << " movePath fuel: " << fuel << " sys id: " << this->SystemID();

    // determine all systems where fleet(s) can be resupplied if fuel runs out
    int owner = this->Owner();
    const Empire* empire = Empires().Lookup(owner);
    std::set<int> fleet_supplied_systems;
    std::set<int> unobstructed_systems;
    if (empire) {
        fleet_supplied_systems = empire->FleetSupplyableSystemIDs();
        unobstructed_systems = empire->SupplyUnobstructedSystems();
    }

    // determine if, given fuel available and supplyable systems, fleet will ever be able to move
    if (fuel < 1.0 &&
        this->SystemID() != INVALID_OBJECT_ID &&
        fleet_supplied_systems.find(this->SystemID()) == fleet_supplied_systems.end())
    {
        MovePathNode node(this->X(), this->Y(), true, ETA_OUT_OF_RANGE,
                          this->SystemID(),
                          INVALID_OBJECT_ID,
                          INVALID_OBJECT_ID);
        retval.push_back(node);
        return retval;      // can't move => path is just this system with explanatory ETA
    }


    // get iterator pointing to System* on route that is the first after where this fleet is currently.
    // if this fleet is in a system, the iterator will point to the system after the current in the route
    // if this fleet is not in a system, the iterator will point to the first system in the route
    std::list<int>::const_iterator route_it = route.begin();
    if (*route_it == SystemID())
        ++route_it;     // first system in route is current system of this fleet.  skip to the next system
    if (route_it == route.end())
        return retval;  // current system of this fleet is the *only* system in the route.  path is empty.


    // get current, previous and next systems of fleet
    const System* cur_system = GetSystem(this->SystemID());         // may be 0
    const System* prev_system = GetSystem(this->PreviousSystemID());// may be 0 if this fleet is not moving or ordered to move
    const System* next_system = GetSystem(*route_it);               // can't use this->NextSystemID() because this fleet may not be moving and may not have a next system. this might occur when a fleet is in a system, not ordered to move or ordered to move to a system, but a projected fleet move line is being calculated to a different system
    if (!next_system) {
        Logger().errorStream() << "Fleet::MovePath couldn't get next system with id " << *route_it << " for this fleet " << this->Name();
        return retval;
    }

    //Logger().debugStream() << "initial cur system: " << (cur_system ? cur_system->Name() : "(none)") <<
    //                          "  prev system: " << (prev_system ? prev_system->Name() : "(none)") <<
    //                          "  next system: " << (next_system ? next_system->Name() : "(none)");



    // place initial position MovePathNode
    MovePathNode initial_pos(this->X(), this->Y(), false /* not an end of turn node */, 0 /* turns taken to reach position of node */,
                             (cur_system  ? cur_system->ID()  : INVALID_OBJECT_ID),
                             (prev_system ? prev_system->ID() : INVALID_OBJECT_ID),
                             (next_system ? next_system->ID() : INVALID_OBJECT_ID));
    retval.push_back(initial_pos);


    const int       TOO_LONG =              100;        // limit on turns to simulate.  99 turns max keeps ETA to two digits, making UI work better
    int             turns_taken =           1;
    double          turn_dist_remaining =   m_speed;    // additional distance that can be travelled in current turn of fleet movement being simulated
    double          cur_x =                 this->X();
    double          cur_y =                 this->Y();
    double          next_x =                next_system->X();
    double          next_y =                next_system->Y();

    // simulate fleet movement given known speed, starting position, fuel limit and systems on route
    // need to populate retval with MovePathNodes that indicate the correct position, whether this
    // fleet will end a turn at the node, the turns it will take to reach the node, and (when applicable)
    // the current (if at a system), previous and next system IDs at which the fleet will be.  the
    // previous and next system ids are needed to know what starlane a given node is located on, if any.
    // nodes at systems don't need previous system ids to be valid, but should have next system ids
    // valid so that when rendering starlanes using the returned move path, lines departing a system
    // can be drawn on the correct side of the system icon

    while (turns_taken < TOO_LONG) {
        // each loop iteration moves the current position to the next location of interest along the move
        // path, and then adds a node at that position.

        //Logger().debugStream() << " starting iteration";
        //if (cur_system)
        //    Logger().debugStream() << "     at system " << cur_system->Name() << " with id " << cur_system->ID();
        //else
        //    Logger().debugStream() << "     at (" << cur_x << ", " << cur_y << ")";


        // check if fuel limits movement or current system refuels passing fleet
        if (cur_system) {
            // check if current system has fuel supply available
            if (fleet_supplied_systems.find(cur_system->ID()) != fleet_supplied_systems.end()) {
                // current system has fuel supply.  replenish fleet's supply and don't restrict movement
                fuel = max_fuel;
                //Logger().debugStream() << " ... at system with fuel supply.  replenishing and continuing movement";

            } else {
                // current system has no fuel supply.  require fuel to proceed
                if (fuel >= 1.0) {
                    //Logger().debugStream() << " ... at system without fuel supply.  consuming unit of fuel to proceed";
                    fuel -= 1.0;

                } else {
                    //Logger().debugStream() << " ... at system without fuel supply.  have insufficient fuel to continue moving";
                    turns_taken = ETA_OUT_OF_RANGE;
                    break;
                }
            }
        }


        // find distance to next system along path from current position
        double dist_to_next_system = std::sqrt((next_x - cur_x)*(next_x - cur_x) + (next_y - cur_y)*(next_y - cur_y));
        //Logger().debugStream() << " ... dist to next system: " << dist_to_next_system;


        // move ship as far as it can go this turn, or to next system, whichever is closer, and deduct
        // distance travelled from distance travellable this turn
        if (turn_dist_remaining >= FLEET_MOVEMENT_EPSILON) {
            double dist_travelled_this_step = std::min(turn_dist_remaining, dist_to_next_system);

            //Logger().debugStream() << " ... fleet moving " << dist_travelled_this_step << " this iteration.  dist to next system: " << dist_to_next_system << " and turn_dist_remaining: " << turn_dist_remaining;

            double x_dist = next_x - cur_x;
            double y_dist = next_y - cur_y;
            // dist_to_next_system = std::sqrt(x_dist * x_dist + y_dist * y_dist);  // should already equal this distance, so don't need to recalculate
            double unit_vec_x = x_dist / dist_to_next_system;
            double unit_vec_y = y_dist / dist_to_next_system;

            cur_x += unit_vec_x*dist_travelled_this_step;
            cur_y += unit_vec_y*dist_travelled_this_step;

            turn_dist_remaining -= dist_travelled_this_step;
            dist_to_next_system -= dist_travelled_this_step;

            // if moved away any distance from a system, are no longer in that system
            if (cur_system && dist_travelled_this_step >= FLEET_MOVEMENT_EPSILON) {
                prev_system = cur_system;
                cur_system = 0;
            }
        }

        bool end_turn_at_cur_position = false;

        // check if fleet can move any further this turn
        if (turn_dist_remaining < FLEET_MOVEMENT_EPSILON) {
            //Logger().debugStream() << " ... fleet can't move further this turn.";
            turn_dist_remaining = 0.0;      // to prevent any possible precision-related errors
            end_turn_at_cur_position = true;
        }

        // check if current position is close enough to next system on route to qualify as at that system.
        if (dist_to_next_system < FLEET_MOVEMENT_EPSILON) {
            // close enough to be consider to be at next system.
            // set current position to be exactly at next system to avoid rounding issues
            cur_system = next_system;
            cur_x = cur_system->X();    // update positions to ensure no round-off-errors
            cur_y = cur_system->Y();

            //Logger().debugStream() << " ... arrived at system: " << cur_system->Name();


            // attempt to get next system on route, to update next system.  if new current
            // system is the end of the route, abort.
            ++route_it;
            if (route_it == route.end())
                break;

            // update next system on route and distance to it from current position
            next_system = GetEmpireKnownSystem(*route_it, owner);
            if (!next_system) {
                Logger().errorStream() << "Fleet::MovePath couldn't get system with id " << *route_it;
                break;
            }
            next_x = next_system->X();
            next_y = next_system->Y();
        }

        // if new position is an obstructed system, must end turn here
        if (cur_system && unobstructed_systems.find(cur_system->ID()) == unobstructed_systems.end()) {
            turn_dist_remaining = 0.0;
            end_turn_at_cur_position = true;
        }

        // if turn done and turns taken is enough, abort simulation
        if (end_turn_at_cur_position && (turns_taken + 1 >= TOO_LONG)) {
            // exit loop before placing current node to simplify post-loop processing: now all cases require a post-loop node to be added
            ++turns_taken;
            break;
        }

        // add MovePathNode for current position (end of turn position and/or system location)
        MovePathNode cur_pos(cur_x, cur_y, end_turn_at_cur_position, turns_taken,
                             (cur_system  ? cur_system->ID()  : INVALID_OBJECT_ID),
                             (prev_system ? prev_system->ID() : INVALID_OBJECT_ID),
                             (next_system ? next_system->ID() : INVALID_OBJECT_ID));
        retval.push_back(cur_pos);


        // if the turn ended at this position, increment the turns taken and
        // reset the distance remaining to be travelled during the current (now
        // next) turn for the next loop iteration
        if (end_turn_at_cur_position) {
            //Logger().debugStream() << " ... end of simulated turn " << turns_taken;
            ++turns_taken;
            turn_dist_remaining = m_speed;
        }
    }


    // done looping.  may have exited due to reaching end of path, lack of fuel, or turns taken getting too big
    if (turns_taken == TOO_LONG)
        turns_taken = ETA_NEVER;

    MovePathNode final_pos(cur_x, cur_y, true, turns_taken,
                           (cur_system  ? cur_system->ID()  : INVALID_OBJECT_ID),
                           (prev_system ? prev_system->ID() : INVALID_OBJECT_ID),
                           (next_system ? next_system->ID() : INVALID_OBJECT_ID));
    retval.push_back(final_pos);

    return retval;
}