Beispiel #1
0
void FleetMoveOrder::ExecuteImpl() const {
    ValidateEmpireID();

    TemporaryPtr<Fleet> fleet = GetFleet(FleetID());
    if (!fleet) {
        ErrorLogger() << "Empire with id " << EmpireID() << " ordered fleet with id " << FleetID() << " to move, but no such fleet exists";
        return;
    }

    TemporaryPtr<const System> destination_system = GetEmpireKnownSystem(DestinationSystemID(), EmpireID());
    if (!destination_system) {
        ErrorLogger() << "Empire with id " << EmpireID() << " ordered fleet to move to system with id " << DestinationSystemID() << " but no such system is known to that empire";
        return;
    }

    // reject empty routes
    if (m_route.empty()) {
        ErrorLogger() << "Empire with id " << EmpireID() << " ordered fleet to move on empty route";
        return;
    }

    // verify that empire specified in order owns specified fleet
    if (!fleet->OwnedBy(EmpireID()) ) {
        ErrorLogger() << "Empire with id " << EmpireID() << " order to move but does not own fleet with id " << FleetID();
        return;
    }


    // verify fleet route first system
    int fleet_sys_id = fleet->SystemID();
    if (!m_append || fleet->TravelRoute().empty()) {
        if (fleet_sys_id != INVALID_OBJECT_ID) {
            // fleet is in a system.  Its move path should also start from that system.
            if (fleet_sys_id != m_start_system) {
                ErrorLogger() << "Empire with id " << EmpireID()
                              << " ordered a fleet to move from a system with id " << m_start_system
                              << " that it is not at.  Fleet is located at system with id " << fleet_sys_id;
                return;
            }
        } else {
            // fleet is not in a system.  Its move path should start from the next system it is moving to.
            int next_system = fleet->NextSystemID();
            if (next_system != m_start_system) {
                ErrorLogger() << "Empire with id " << EmpireID()
                              << " ordered a fleet to move starting from a system with id " << m_start_system
                              << ", but the fleet's next destination is system with id " << next_system;
                return;
            }
        }
    } else {
        // We should append and there is something to append to
        int last_system = fleet->TravelRoute().back();
        if (last_system != m_start_system) {
            ErrorLogger() << "Empire with id " << EmpireID()
                          << " ordered a fleet to continue from system with id " << m_start_system
                          << ", but the fleet's current route won't lead there, it leads to system " << last_system;
            return;
        }
    }


    // convert list of ids to list of System
    std::list<int> route_list;

    if(m_append && !fleet->TravelRoute().empty()){
        route_list = fleet->TravelRoute();
        route_list.erase(--route_list.end());// Remove the last one since it is the first one of the other
    }

    std::copy(m_route.begin(), m_route.end(), std::back_inserter(route_list));


    // validate route.  Only allow travel between systems connected in series by starlanes known to this fleet's owner.

    // check destination validity: disallow movement that's out of range
    std::pair<int, int> eta = fleet->ETA(fleet->MovePath(route_list));
    if (eta.first == Fleet::ETA_NEVER || eta.first == Fleet::ETA_OUT_OF_RANGE) {
        DebugLogger() << "FleetMoveOrder::ExecuteImpl rejected out of range move order";
        return;
    }

    std::string waypoints;
    for (std::list<int>::iterator it = route_list.begin(); it != route_list.end(); ++it) {
        waypoints += std::string(" ") + boost::lexical_cast<std::string>(*it);
    }
    DebugLogger() << "FleetMoveOrder::ExecuteImpl Setting route of fleet " << fleet->ID() << " to " << waypoints;

    fleet->SetRoute(route_list);
}
Beispiel #2
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;
}
Beispiel #3
0
CombatInfo::CombatInfo(int system_id_, int turn_) :
    turn(turn_),
    system_id(system_id_)
{
    TemporaryPtr<System> system = ::GetSystem(system_id);
    if (!system) {
        Logger().errorStream() << "CombatInfo constructed with invalid system id: " << system_id;
        return;
    }

    // add system to full / complete objects in combat - NOTE: changed from copy of system
    objects.Insert(system);


    // find ships and their owners in system
    std::vector<TemporaryPtr<Ship> > ships =
        Objects().FindObjects<Ship>(system->ShipIDs());

    for (std::vector<TemporaryPtr<Ship> >::const_iterator ship_it = ships.begin();
         ship_it != ships.end(); ++ship_it)
    {
        TemporaryPtr<Ship> ship = *ship_it;
        // add owner to empires that have assets in this battle
        empire_ids.insert(ship->Owner());

        objects.Insert(ship);
    }

    // find planets and their owners in system
    std::vector<TemporaryPtr<Planet> > planets =
        Objects().FindObjects<Planet>(system->PlanetIDs());

    for (std::vector<TemporaryPtr<Planet> >::const_iterator planet_it = planets.begin();
         planet_it != planets.end(); ++planet_it)
    {
        TemporaryPtr<Planet> planet = *planet_it;
        // if planet is populated, add owner to empires that have assets in this battle
        if (planet->CurrentMeterValue(METER_POPULATION) > 0.0)
            empire_ids.insert(planet->Owner());

        objects.Insert(planet);
    }

    // TODO: should buildings be considered separately?

    // now that all participants in the battle have been found, loop through
    // objects again to assemble each participant empire's latest
    // known information about all objects in this battle

    // system
    for (std::set<int>::const_iterator empire_it = empire_ids.begin();
         empire_it != empire_ids.end(); ++empire_it)
    {
        int empire_id = *empire_it;
        if (empire_id == ALL_EMPIRES)
            continue;
        empire_known_objects[empire_id].Insert(GetEmpireKnownSystem(system->ID(), empire_id));
    }

    // ships
    for (std::vector<TemporaryPtr<Ship> >::const_iterator it = ships.begin();
         it != ships.end(); ++it)
    {
        TemporaryPtr<Ship> ship = *it;
        int ship_id = ship->ID();
        TemporaryPtr<const Fleet> fleet = GetFleet(ship->FleetID());
        if (!fleet) {
            Logger().errorStream() << "CombatInfo::CombatInfo couldn't get fleet with id "
                                   << ship->FleetID() << " in system " << system->Name() << " (" << system_id << ")";
            continue;
        }

        for (std::set<int>::const_iterator empire_it = empire_ids.begin();
             empire_it != empire_ids.end(); ++empire_it)
        {
            int empire_id = *empire_it;
            if (empire_id == ALL_EMPIRES)
                continue;
            if (GetUniverse().GetObjectVisibilityByEmpire(ship_id, empire_id) >= VIS_BASIC_VISIBILITY ||
                   (fleet->Aggressive() &&
                       (empire_id == ALL_EMPIRES ||
                        fleet->Unowned() ||
                        Empires().GetDiplomaticStatus(empire_id, fleet->Owner()) == DIPLO_WAR)))
            { empire_known_objects[empire_id].Insert(GetEmpireKnownShip(ship->ID(), empire_id));}
        }
    }

    // planets
    for (std::vector<TemporaryPtr<Planet> >::const_iterator it = planets.begin();
         it != planets.end(); ++it)
    {
        TemporaryPtr<Planet> planet = *it;
        int planet_id = planet->ID();

        for (std::set<int>::const_iterator empire_it = empire_ids.begin(); empire_it != empire_ids.end(); ++empire_it) {
            int empire_id = *empire_it;
            if (empire_id == ALL_EMPIRES)
                continue;
            if (GetUniverse().GetObjectVisibilityByEmpire(planet_id, empire_id) >= VIS_BASIC_VISIBILITY) {
                empire_known_objects[empire_id].Insert(GetEmpireKnownPlanet(planet->ID(), empire_id));
            }
        }
    }

    // after battle is simulated, any changes to latest known or actual objects
    // will be copied back to the main Universe's ObjectMap and the Universe's
    // empire latest known objects ObjectMap - NOTE: Using the real thing now
}