From: <geo...@us...> - 2010-08-16 07:25:55
|
Revision: 3732 http://freeorion.svn.sourceforge.net/freeorion/revision/?rev=3732&view=rev Author: geoffthemedio Date: 2010-08-16 07:25:47 +0000 (Mon, 16 Aug 2010) Log Message: ----------- -Renamed FleetColonizeOrder to just ColonizeOrder, as it has nothing specifically to do with fleets. -Rewrote how ColonizeOrder works. Now a ship that is ordered to colonize is given a state variable to record what planet is to be colonized, similar to how ScrapOrder sets a flag on the object. A ship is no longer immediately deleted on clients when ordered to colonize, greatly simplifying the whole colonize order execution, and in particular, undo execution process. This rewrite is incomplete, though, as the UI in the client hasn't been updated to show that a particular ship has been ordered to colonize. Ordering and cancelling colonization does work, though. -Also rewrote and pulled out into separate functions the actual colonizing of planets by ships marked by colonize orders during turn resolution. -Slightly modified colonization resolution so that an empire can't colonize if there are enemy armed ships in a system. Previously this was only a limitation if there was more than one empire trying to colonize the same planet. -Groomed some comments. Modified Paths: -------------- trunk/FreeOrion/AI/AIInterface.cpp trunk/FreeOrion/AI/AIInterface.h trunk/FreeOrion/AI/PythonAI.cpp trunk/FreeOrion/UI/SidePanel.cpp trunk/FreeOrion/msvc2008/src/Version.cpp trunk/FreeOrion/server/ServerApp.cpp trunk/FreeOrion/universe/Ship.cpp trunk/FreeOrion/universe/Ship.h trunk/FreeOrion/util/Order.cpp trunk/FreeOrion/util/Order.h trunk/FreeOrion/util/SerializeOrderSet.cpp trunk/FreeOrion/util/SerializeUniverse.cpp Modified: trunk/FreeOrion/AI/AIInterface.cpp =================================================================== --- trunk/FreeOrion/AI/AIInterface.cpp 2010-08-15 23:24:26 UTC (rev 3731) +++ trunk/FreeOrion/AI/AIInterface.cpp 2010-08-16 07:25:47 UTC (rev 3732) @@ -419,7 +419,7 @@ return 1; } - int IssueFleetColonizeOrder(int ship_id, int planet_id) { + int IssueColonizeOrder(int ship_id, int planet_id) { const Universe& universe = AIClientApp::GetApp()->GetUniverse(); const ObjectMap& objects = universe.Objects(); int empire_id = AIClientApp::GetApp()->EmpireID(); @@ -427,49 +427,49 @@ // make sure ship_id is a ship... const Ship* ship = objects.Object<Ship>(ship_id); if (!ship) { - Logger().errorStream() << "AIInterface::IssueFleetColonizeOrder : passed an invalid ship_id"; + Logger().errorStream() << "AIInterface::IssueColonizeOrder : passed an invalid ship_id"; return 0; } // get fleet of ship const Fleet* fleet = objects.Object<Fleet>(ship->FleetID()); if (!fleet) { - Logger().errorStream() << "AIInterface::IssueFleetColonizeOrder : ship with passed ship_id has invalid fleet_id"; + Logger().errorStream() << "AIInterface::IssueColonizeOrder : ship with passed ship_id has invalid fleet_id"; return 0; } // make sure player owns ship and its fleet if (!fleet->WhollyOwnedBy(empire_id)) { - Logger().errorStream() << "AIInterface::IssueFleetColonizeOrder : empire does not own fleet of passed ship"; + Logger().errorStream() << "AIInterface::IssueColonizeOrder : empire does not own fleet of passed ship"; return 0; } if (!ship->WhollyOwnedBy(empire_id)) { - Logger().errorStream() << "AIInterface::IssueFleetColonizeOrder : empire does not own passed ship"; + Logger().errorStream() << "AIInterface::IssueColonizeOrder : empire does not own passed ship"; return 0; } // verify that planet exists and is un-occupied. const Planet* planet = objects.Object<Planet>(planet_id); if (!planet) { - Logger().errorStream() << "AIInterface::IssueFleetColonizeOrder : no planet with passed planet_id"; + Logger().errorStream() << "AIInterface::IssueColonizeOrder : no planet with passed planet_id"; return 0; } if (!planet->Unowned()) { - Logger().errorStream() << "AIInterface::IssueFleetColonizeOrder : planet with passed planet_id is already owned or colonized"; + Logger().errorStream() << "AIInterface::IssueColonizeOrder : planet with passed planet_id is already owned or colonized"; return 0; } // verify that planet is in same system as the fleet if (planet->SystemID() != fleet->SystemID()) { - Logger().errorStream() << "AIInterface::IssueFleetColonizeOrder : fleet and planet are not in the same system"; + Logger().errorStream() << "AIInterface::IssueColonizeOrder : fleet and planet are not in the same system"; return 0; } if (ship->SystemID() == UniverseObject::INVALID_OBJECT_ID) { - Logger().errorStream() << "AIInterface::IssueFleetColonizeOrder : ship is not in a system"; + Logger().errorStream() << "AIInterface::IssueColonizeOrder : ship is not in a system"; return 0; } - AIClientApp::GetApp()->Orders().IssueOrder(OrderPtr(new FleetColonizeOrder(empire_id, ship_id, planet_id))); + AIClientApp::GetApp()->Orders().IssueOrder(OrderPtr(new ColonizeOrder(empire_id, ship_id, planet_id))); return 1; } Modified: trunk/FreeOrion/AI/AIInterface.h =================================================================== --- trunk/FreeOrion/AI/AIInterface.h 2010-08-15 23:24:26 UTC (rev 3731) +++ trunk/FreeOrion/AI/AIInterface.h 2010-08-16 07:25:47 UTC (rev 3732) @@ -72,7 +72,7 @@ int IssueNewFleetOrder(const std::string& fleet_name, const std::vector<int>& ship_ids); int IssueNewFleetOrder(const std::string& fleet_name, int ship_id); int IssueFleetTransferOrder(int ship_id, int new_fleet_id); - int IssueFleetColonizeOrder(int ship_id, int planet_id); + int IssueColonizeOrder(int ship_id, int planet_id); int IssueDeleteFleetOrder(); int IssueChangeFocusOrder(int planet_id, const std::string& focus); Modified: trunk/FreeOrion/AI/PythonAI.cpp =================================================================== --- trunk/FreeOrion/AI/PythonAI.cpp 2010-08-15 23:24:26 UTC (rev 3731) +++ trunk/FreeOrion/AI/PythonAI.cpp 2010-08-16 07:25:47 UTC (rev 3732) @@ -111,7 +111,7 @@ def("issueScrapOrder", AIIntScrap); def("issueNewFleetOrder", AIIntNewFleet); def("issueFleetTransferOrder", AIInterface::IssueFleetTransferOrder); - def("issueColonizeOrder", AIInterface::IssueFleetColonizeOrder); + def("issueColonizeOrder", AIInterface::IssueColonizeOrder); def("issueChangeFocusOrder", AIInterface::IssueChangeFocusOrder); def("issueEnqueueTechOrder", AIInterface::IssueEnqueueTechOrder); def("issueDequeueTechOrder", AIInterface::IssueDequeueTechOrder); Modified: trunk/FreeOrion/UI/SidePanel.cpp =================================================================== --- trunk/FreeOrion/UI/SidePanel.cpp 2010-08-15 23:24:26 UTC (rev 3731) +++ trunk/FreeOrion/UI/SidePanel.cpp 2010-08-16 07:25:47 UTC (rev 3732) @@ -366,7 +366,7 @@ return retval; const OrderSet& orders = app->Orders(); for (OrderSet::const_iterator it = orders.begin(); it != orders.end(); ++it) { - if (boost::shared_ptr<FleetColonizeOrder> order = boost::dynamic_pointer_cast<FleetColonizeOrder>(it->second)) { + if (boost::shared_ptr<ColonizeOrder> order = boost::dynamic_pointer_cast<ColonizeOrder>(it->second)) { retval[order->PlanetID()] = it->first; } } @@ -1327,7 +1327,7 @@ int empire_id = HumanClientApp::GetApp()->EmpireID(); std::map<int, int> pending_colonization_orders = PendingColonizationOrders(); - std::map<int, int>::const_iterator it = pending_colonization_orders.find(planet->ID()); + std::map<int, int>::const_iterator it = pending_colonization_orders.find(m_planet_id); // colonize if (it == pending_colonization_orders.end()) { @@ -1346,7 +1346,7 @@ return; } - HumanClientApp::GetApp()->Orders().IssueOrder(OrderPtr(new FleetColonizeOrder(empire_id, ship->ID(), planet->ID()))); + HumanClientApp::GetApp()->Orders().IssueOrder(OrderPtr(new ColonizeOrder(empire_id, ship->ID(), m_planet_id))); } else { // cancel colonization Modified: trunk/FreeOrion/msvc2008/src/Version.cpp =================================================================== --- trunk/FreeOrion/msvc2008/src/Version.cpp 2010-08-15 23:24:26 UTC (rev 3731) +++ trunk/FreeOrion/msvc2008/src/Version.cpp 2010-08-16 07:25:47 UTC (rev 3732) @@ -1,7 +1,7 @@ #include "../../util/Version.h" namespace { - static const std::string retval = "v0.3.15 [SVN 3728]"; + static const std::string retval = "v0.3.15 [SVN 3732]"; } const std::string& FreeOrionVersionString() Modified: trunk/FreeOrion/server/ServerApp.cpp =================================================================== --- trunk/FreeOrion/server/ServerApp.cpp 2010-08-15 23:24:26 UTC (rev 3731) +++ trunk/FreeOrion/server/ServerApp.cpp 2010-08-16 07:25:47 UTC (rev 3732) @@ -1094,6 +1094,242 @@ } } +namespace { + /** Does colonization, with safety checks */ + bool ColonizePlanet(ObjectMap& objects, int ship_id, int planet_id) { + Ship* ship = objects.Object<Ship>(ship_id); + if (!ship) { + Logger().errorStream() << "ColonizePlanet couldn't get ship with id " << ship_id; + return false; + } + Planet* planet = objects.Object<Planet>(planet_id); + if (!planet) { + Logger().errorStream() << "ColonizePlanet couldn't get planet with id " << planet_id; + return false; + } + + // get species to colonize with: species of ship + const std::string& species_name = ship->SpeciesName(); + if (species_name.empty()) { + Logger().errorStream() << "ColonizePlanet ship has no species"; + return false; + } + const Species* species = GetSpecies(species_name); + if (!species) { + Logger().errorStream() << "ColonizePlanet couldn't get species with name: " << species_name; + return false; + } + + // get empire to give ownership of planet to + const std::set<int>& owners = ship->Owners(); + if (owners.size() != 1) { + Logger().errorStream() << "ColonizePlanet couldn't get an empire to colonize with"; + return false; + } + int empire_id = *owners.begin(); + + // get colonist capacity of ship: sum of capacities of parts + double colonist_capacity = 0.0; + const ShipDesign* design = ship->Design(); + if (!design) { + Logger().errorStream() << "ColonizePlanet couldn't find ship's design!"; + return false; + } + const std::vector<std::string>& parts = design->Parts(); + for (std::vector<std::string>::const_iterator it = parts.begin(); it != parts.end(); ++it) { + const std::string& part_name = *it; + if (part_name.empty()) + continue; + const PartType* part_type = GetPartType(part_name); + if (!part_type) { + Logger().errorStream() << "ColonizePlanet couldn't find ship part type: " << part_name; + continue; + } + if (part_type->Class() == PC_COLONY) { + colonist_capacity += boost::get<double>(part_type->Stats()); + } + } + if (colonist_capacity <= 0.0) { + Logger().debugStream() << "ColonizePlanet colonize order executed by ship with zero colonist capacity!"; + return false; + } + + + // all checks passed. proceed with colonization. + + + planet->Reset(); + planet->SetSpecies(species_name); // do this BEFORE destroying the ship, since species_name is a const reference to Ship::m_species_name + + GetUniverse().Destroy(ship->ID()); + + + // find a focus to give planets by default. use first defined available focus. + // the planet's AvailableFoci function should return a vector of all names of + // available foci. + std::vector<std::string> available_foci = planet->AvailableFoci(); + if (!available_foci.empty()) + planet->SetFocus(*available_foci.begin()); + + planet->GetMeter(METER_POPULATION)->SetCurrent(colonist_capacity); + planet->GetMeter(METER_FARMING)->SetCurrent(planet->GetMeter(METER_POPULATION)->Current()); + planet->GetMeter(METER_HEALTH)->SetCurrent(20.0); + planet->BackPropegateMeters(); + + planet->AddOwner(empire_id); + + const std::set<int>& planet_buildings = planet->Buildings(); + for (std::set<int>::const_iterator it = planet_buildings.begin(); it != planet_buildings.end(); ++it) { + if (Building* building = objects.Object<Building>(*it)) { + building->ClearOwners(); + building->AddOwner(empire_id); // TODO: only add owner if empire has visibility of building. Need to add a check for this every turn... maybe doesn't need to be done during colonization at all? + } else { + Logger().errorStream() << "ColonizePlanet couldn't find building with id: " << *it; + } + } + + return true; + } + + /** Determines which ships ordered to colonize planet succeed, does + * appropriate colonization, and cleans up after colonization orders */ + void HandleColonization(ObjectMap& objects, EmpireManager& empires) { + // collect, for each planet, what ships have been ordered to colonize it + std::map<int, std::map<int, std::set<int> > > planet_empire_colonization_ship_ids; // map from planet ID to map from empire ID to set of ship IDs + std::vector<Ship*> ships = objects.FindObjects<Ship>(); + for (std::vector<Ship*>::iterator it = ships.begin(); it != ships.end(); ++it) { + const Ship* ship = *it; + if (!ship) { + Logger().errorStream() << "HandleColonization couldn't get ship"; + continue; + } + int colonize_planet_id = ship->OrderedColonizePlanet(); + const Planet* planet = objects.Object<Planet>(colonize_planet_id); + if (!planet) + continue; + if (ship->Owners().size() != 1) + continue; + int owner_empire_id = *ship->Owners().begin(); + if (owner_empire_id == ALL_EMPIRES) + continue; + int ship_id = ship->ID(); + if (ship_id == UniverseObject::INVALID_OBJECT_ID) + continue; + if (ship->SystemID() != planet->SystemID() || ship->SystemID() == UniverseObject::INVALID_OBJECT_ID) + continue; + planet_empire_colonization_ship_ids[colonize_planet_id][owner_empire_id].insert(ship_id); + } + + // execute colonization when: + // 1) a single empire is colonizing a planet in a system that contains no enemy armed ships + // 2) multiple empires try to colonize the same planet, but only one empire has armed ships in the system + // otherwise, no colonization happens in the system + for (std::map<int, std::map<int, std::set<int> > >::iterator planet_it = planet_empire_colonization_ship_ids.begin(); + planet_it != planet_empire_colonization_ship_ids.end(); + ++planet_it) + { + int planet_id = planet_it->first; + Planet* planet = objects.Object<Planet>(planet_id); + if (!planet) { + Logger().errorStream() << "HandleColonization couldn't get planet with id " << planet_id; + continue; + } + if (!planet->Unowned()) { + Logger().errorStream() << "HandleColonization trying to colonize an already-occupied planet!"; + continue; + } + + int system_id = planet->SystemID(); + const System* system = objects.Object<System>(system_id); + if (!system) { + Logger().errorStream() << "HandleColonization couldn't get system with id " << system_id; + continue; + } + + // find which empires have armed ships in system + std::set<int> empires_with_armed_ships_in_system; + std::vector<int> system_fleet_ids = system->FindObjectIDs<Fleet>(); + for (std::vector<int>::const_iterator fleet_it = system_fleet_ids.begin(); fleet_it != system_fleet_ids.end(); ++fleet_it) { + const Fleet* fleet = objects.Object<Fleet>(*fleet_it); + if (fleet->HasArmedShips()) { + const std::set<int>& owners = fleet->Owners(); + for (std::set<int>::const_iterator owner_it = owners.begin(); owner_it != owners.end(); ++owner_it) + empires_with_armed_ships_in_system.insert(*owner_it); + } + } + + // no empires can colonize if there are more than one empire's armed fleets in system + if (empires_with_armed_ships_in_system.size() > 1) + continue; + + bool planet_colonized = false; + + // check if any of the ships that want to colonize belong to the empire with armed ships + std::map<int, std::set<int> >& empire_colonization_ship_map = planet_it->second; + for (std::map<int, std::set<int> >::iterator empire_it = empire_colonization_ship_map.begin(); + empire_it != empire_colonization_ship_map.end(); + ++empire_it) + { + int empire_id = empire_it->first; + if (!empires_with_armed_ships_in_system.empty() && + empires_with_armed_ships_in_system.find(empire_id) == empires_with_armed_ships_in_system.end()) + { + // this empire can't colonize here this turn + continue; + } + + std::set<int>& colonizing_ships = empire_it->second; + if (colonizing_ships.empty()) { + Logger().errorStream() << "HandleColonization got empty set of ships attempting to colonize planet for an empire?!"; + continue; + } + + // pick the first ship this empire has that is trying to colonize this planet. + int ship_id = *colonizing_ships.begin(); + + + // do colonization + if (!ColonizePlanet(objects, ship_id, planet_id)) + continue; // skip sitrep if colonization failed + + // record successful colonization + planet_colonized = true; + // remove ship from ships that wanted to colonize + colonizing_ships.erase(ship_id); + + // sitrep about colonization + Empire* empire = empires.Lookup(empire_id); + if (!empire) { + Logger().errorStream() << "HandleColonization couldn't get empire with id " << empire_id; + } else { + empire->AddSitRepEntry(CreatePlanetColonizedSitRep(planet_id)); + } + + break; // only one colonization per planet. + } + planet->ResetIsAboutToBeColonized(); + + // if planet was colonized, remove colonize status from any other + // ships that wanted to colonize this planet + if (planet_colonized) { + for (std::map<int, std::set<int> >::iterator empire_it = empire_colonization_ship_map.begin(); + empire_it != empire_colonization_ship_map.end(); + ++empire_it) + { + std::set<int>& colonizing_ships = empire_it->second; + for (std::set<int>::const_iterator ship_it = colonizing_ships.begin(); ship_it != colonizing_ships.end(); ++ship_it) + if (Ship* ship = objects.Object<Ship>(*ship_it)) + ship->ClearColonizePlanet(); + } + } + } + + // TODO elsewhere: clear colonization planet of ships when that plant + // has been destroyed or become invisible, or if the ship leaves + // the system? + } +} + void ServerApp::PreCombatProcessTurns() { EmpireManager& empires = Empires(); @@ -1108,6 +1344,7 @@ } Logger().debugStream() << "ServerApp::ProcessTurns executing orders"; + // execute orders for (std::map<int, OrderSet*>::iterator it = m_turn_sequence.begin(); it != m_turn_sequence.end(); ++it) { // broadcast UI message to all players @@ -1118,118 +1355,21 @@ empire->ClearSitRep(); OrderSet* order_set = it->second; - // execute order set - for (OrderSet::const_iterator order_it = order_set->begin(); order_it != order_set->end(); ++order_it) { + for (OrderSet::const_iterator order_it = order_set->begin(); order_it != order_set->end(); ++order_it) order_it->second->Execute(); - } } - Logger().debugStream() << "ServerApp::ProcessTurns colonize order filtering"; - // filter FleetColonizeOrder for later processing - typedef std::map<int, std::vector<boost::shared_ptr<FleetColonizeOrder> > > ColonizeOrderMap; - ColonizeOrderMap colonize_order_map; - for (std::map<int, OrderSet*>::iterator it = m_turn_sequence.begin(); it != m_turn_sequence.end(); ++it) { - OrderSet* order_set = it->second; - - // filter FleetColonizeOrder and sort them per planet - boost::shared_ptr<FleetColonizeOrder> order; - for (OrderSet::const_iterator order_it = order_set->begin(); order_it != order_set->end(); ++order_it) { - if ((order = boost::dynamic_pointer_cast<FleetColonizeOrder>(order_it->second))) { - ColonizeOrderMap::iterator it = colonize_order_map.find(order->PlanetID()); - if (it == colonize_order_map.end()) { - colonize_order_map.insert(std::make_pair(order->PlanetID(),std::vector<boost::shared_ptr<FleetColonizeOrder> >())); - it = colonize_order_map.find(order->PlanetID()); - } - it->second.push_back(order); - } - } - } - - // clean up orders, which are no longer needed ClearEmpireTurnOrders(); - // colonization apply be the following rules - // 1 - if there is only own empire which tries to colonize a planet, is allowed to do so - // 2 - if there are more than one empire then - // 2.a - if only one empire which tries to colonize (empire who don't are ignored) is armed, this empire wins the race - // 2.b - if more than one empire is armed or all forces are unarmed, no one can colonize the planet - for (ColonizeOrderMap::iterator colonize_order_map_it = colonize_order_map.begin(); colonize_order_map_it != colonize_order_map.end(); ++colonize_order_map_it) { - Planet* planet = objects.Object<Planet>(colonize_order_map_it->first); - if (!planet) { - Logger().errorStream() << "ProcessTurns couldn't get planet with id " << colonize_order_map_it->first; - continue; - } - std::vector<boost::shared_ptr<FleetColonizeOrder> >& colonize_orders = colonize_order_map_it->second; + Logger().debugStream() << "ServerApp::ProcessTurns colonize order filtering"; + HandleColonization(objects, empires); - // only one empire? - if (colonize_orders.size() == 1) { - colonize_orders[0]->ServerExecute(); - Empire* empire = empires.Lookup(colonize_orders[0]->EmpireID()); - empire->AddSitRepEntry(CreatePlanetColonizedSitRep(planet->ID())); - } else { - int system_id = planet->SystemID(); - const System* system = objects.Object<System>(system_id); - if (!system) { - Logger().errorStream() << "ProcessTurns couldn't get system with ID " << system_id; - continue; - } - std::set<int> set_empire_with_military; - std::vector<int> fleet_ids = system->FindObjectIDs<Fleet>(); - for (std::vector<int>::const_iterator it = fleet_ids.begin(); it != fleet_ids.end(); ++it) { - const Fleet* fleet = GetObject<Fleet>(*it); - if (!fleet) { - Logger().errorStream() << "ProcessTurns couldn't get fleet with id " << *it; - continue; - } + Logger().debugStream() << "ServerApp::ProcessTurns scrapping"; - for (Fleet::const_iterator ship_it = fleet->begin(); ship_it != fleet->end(); ++ship_it) - if (const Ship* ship = objects.Object<Ship>(*ship_it)) - if (ship->IsArmed()) { - set_empire_with_military.insert(*fleet->Owners().begin()); - break; - } - } - - // set the first empire as winner for now - int winner = 0; - // is the current winner armed? - bool winner_is_armed = set_empire_with_military.find(colonize_orders[0]->EmpireID()) != set_empire_with_military.end(); - for (unsigned int i = 1; i < colonize_orders.size(); i++) - // is this empire armed? - if (set_empire_with_military.find(colonize_orders[i]->EmpireID()) != set_empire_with_military.end()) { - // if this empire is armed and the former winner too, noone can win - if (winner_is_armed) { - winner = -1; // no winner!! - break; // won't find a winner! - } - winner = i; // this empire is the winner for now - winner_is_armed = true; // and has armed forces - } - else - // this empire isn't armed - if (!winner_is_armed) - winner = -1; // if the current winner isn't armed, a winner must be armed!!!! - - for (int i = 0; i < static_cast<int>(colonize_orders.size()); i++) { - if (winner == i) { - colonize_orders[i]->ServerExecute(); - Empire* empire = empires.Lookup(colonize_orders[i]->EmpireID()); - empire->AddSitRepEntry(CreatePlanetColonizedSitRep(planet->ID())); - } else { - colonize_orders[i]->Undo(); - } - } - } - - planet->ResetIsAboutToBeColonized(); - } - - - Logger().debugStream() << "ServerApp::ProcessTurns scrapping"; // scrap orders std::vector<int> objects_to_scrap; for (ObjectMap::iterator it = objects.begin(); it != objects.end(); ++it) { @@ -1244,6 +1384,7 @@ } for (std::vector<int>::const_iterator it = objects_to_scrap.begin(); it != objects_to_scrap.end(); ++it) m_universe.Destroy(*it); + // check for empty fleets after scrapping std::vector<Fleet*> fleets = objects.FindObjects<Fleet>(); for (std::vector<Fleet*>::iterator it = fleets.begin(); it != fleets.end(); ++it) { Modified: trunk/FreeOrion/universe/Ship.cpp =================================================================== --- trunk/FreeOrion/universe/Ship.cpp 2010-08-15 23:24:26 UTC (rev 3731) +++ trunk/FreeOrion/universe/Ship.cpp 2010-08-16 07:25:47 UTC (rev 3732) @@ -29,13 +29,15 @@ Ship::Ship() : m_design_id(ShipDesign::INVALID_DESIGN_ID), m_fleet_id(INVALID_OBJECT_ID), - m_ordered_scrapped(false) + m_ordered_scrapped(false), + m_ordered_colonize_planet_id(INVALID_OBJECT_ID) {} Ship::Ship(int empire_id, int design_id, const std::string& species_name) : m_design_id(design_id), m_fleet_id(INVALID_OBJECT_ID), m_ordered_scrapped(false), + m_ordered_colonize_planet_id(INVALID_OBJECT_ID), m_species_name(species_name) { if (!GetShipDesign(design_id)) @@ -154,8 +156,9 @@ this->m_species_name = copied_ship->m_species_name; if (vis >= VIS_FULL_VISIBILITY) { - this->m_ordered_scrapped = copied_ship->m_ordered_scrapped; - this->m_part_meters = copied_ship->m_part_meters; + this->m_ordered_scrapped = copied_ship->m_ordered_scrapped; + this->m_ordered_colonize_planet_id= copied_ship->m_ordered_colonize_planet_id; + this->m_part_meters = copied_ship->m_part_meters; } } } @@ -278,6 +281,9 @@ bool Ship::OrderedScrapped() const { return m_ordered_scrapped; } +int Ship::OrderedColonizePlanet() const +{ return m_ordered_colonize_planet_id; } + const Meter* Ship::GetMeter(MeterType type, const std::string& part_name) const { return const_cast<Ship*>(this)->GetMeter(type, part_name); } @@ -355,8 +361,7 @@ void Ship::SetOrderedScrapped(bool b) { - bool initial_status = m_ordered_scrapped; - if (b == initial_status) return; + if (b == m_ordered_scrapped) return; m_ordered_scrapped = b; StateChangedSignal(); if (Fleet* fleet = GetObject<Fleet>(this->FleetID())) { @@ -365,6 +370,22 @@ } } +void Ship::SetColonizePlanet(int planet_id) +{ + if (planet_id == m_ordered_colonize_planet_id) return; + m_ordered_colonize_planet_id = planet_id; + StateChangedSignal(); + if (Fleet* fleet = GetObject<Fleet>(this->FleetID())) { + fleet->RecalculateFleetSpeed(); + fleet->StateChangedSignal(); + } +} + +void Ship::ClearColonizePlanet() +{ + SetColonizePlanet(INVALID_OBJECT_ID); +} + Meter* Ship::GetMeter(MeterType type, const std::string& part_name) { Meter* retval = 0; Modified: trunk/FreeOrion/universe/Ship.h =================================================================== --- trunk/FreeOrion/universe/Ship.h 2010-08-15 23:24:26 UTC (rev 3731) +++ trunk/FreeOrion/universe/Ship.h 2010-08-16 07:25:47 UTC (rev 3732) @@ -46,7 +46,8 @@ virtual double NextTurnCurrentMeterValue(MeterType type) const; ///< returns expected value of specified meter current value on the next turn - bool OrderedScrapped() const; + bool OrderedScrapped() const; ///< returns true iff this ship has been ordered scrapped, or false otherwise + int OrderedColonizePlanet() const; ///< returns the ID of the planet this ship has been ordered to colonize, or INVALID_OBJECT_ID if this ship hasn't been ordered to colonize a planet const Meter* GetMeter(MeterType type, const std::string& part_name) const; ///< returns the requested Meter, or 0 if no such Meter of that type is found in this object //@} @@ -67,6 +68,8 @@ virtual void MoveTo(double x, double y); void SetOrderedScrapped(bool b = true); ///< flags ship for scrapping + void SetColonizePlanet(int planet_id); ///< marks ship to colonize the indicated planet + void ClearColonizePlanet(); ///< marks ship to colonize no planets Meter* GetMeter(MeterType type, const std::string& part_name); ///< returns the requested Meter, or 0 if no such Meter of that type is found in this object //@} @@ -80,6 +83,7 @@ int m_design_id; int m_fleet_id; bool m_ordered_scrapped; + int m_ordered_colonize_planet_id; ConsumablesMap m_fighters; ConsumablesMap m_missiles; PartMeters m_part_meters; Modified: trunk/FreeOrion/util/Order.cpp =================================================================== --- trunk/FreeOrion/util/Order.cpp 2010-08-15 23:24:26 UTC (rev 3731) +++ trunk/FreeOrion/util/Order.cpp 2010-08-16 07:25:47 UTC (rev 3732) @@ -387,231 +387,96 @@ //////////////////////////////////////////////// -// FleetColonizeOrder +// ColonizeOrder //////////////////////////////////////////////// -FleetColonizeOrder::FleetColonizeOrder() : +ColonizeOrder::ColonizeOrder() : Order(), m_ship(UniverseObject::INVALID_OBJECT_ID), m_planet(UniverseObject::INVALID_OBJECT_ID) {} -FleetColonizeOrder::FleetColonizeOrder(int empire, int ship, int planet) : +ColonizeOrder::ColonizeOrder(int empire, int ship, int planet) : Order(empire), m_ship(ship), m_planet(planet) {} -void FleetColonizeOrder::ServerExecute() const +void ColonizeOrder::ExecuteImpl() const { - Universe& universe = GetUniverse(); - ObjectMap& objects = universe.Objects(); + ValidateEmpireID(); + int empire_id = EmpireID(); - - Planet* planet = objects.Object<Planet>(m_planet); - if (!planet) { - Logger().errorStream() << "Empire attempted to colonize a nonexistant planet"; + Ship* ship = GetObject<Ship>(m_ship); + if (!ship) { + Logger().errorStream() << "ColonizeOrder::ExecuteImpl couldn't get ship with id " << m_ship; return; } - const Ship* ship = objects.Object<Ship>(m_ship); - if (!ship) { - Logger().errorStream() << "FleetColonizeOrder::ServerExecute couldn't find ship with id " << m_ship; + if (!ship->CanColonize()) { + Logger().errorStream() << "ColonizeOrder::ExecuteImpl got ship that can't colonize"; return; } - - - // get species to colonize with: species of ship + if (!ship->OwnedBy(empire_id)) { + Logger().errorStream() << "ColonizeOrder::ExecuteImpl got ship that isn't owned by the order-issuing empire"; + return; + } const std::string& species_name = ship->SpeciesName(); if (species_name.empty()) { - Logger().errorStream() << "FleetColonizeOrder::ServerExecute ship has no species"; + Logger().errorStream() << "ColonizeOrder::ExecuteImpl got ship with no species"; return; } const Species* species = GetSpecies(species_name); if (!species) { - Logger().errorStream() << "FleetColonizeOrder::ServerExecute couldn't get species with name: " << species_name; + Logger().errorStream() << "ColonizeOrder::ExecuteImpl couldn't get species with name " << species_name; return; } - - - // get colonist capacity of ship: sum of capacities of parts - double colonist_capacity = 0.0; - const ShipDesign* design = ship->Design(); - if (!design) { - Logger().errorStream() << "FleetColonizeOrder::ServerExecute couldn't find ship's design!"; + Planet* planet = GetObject<Planet>(m_planet); + if (!planet) { + Logger().errorStream() << "ColonizeOrder::ExecuteImpl couldn't get planet with id " << m_planet; return; } - const std::vector<std::string>& parts = design->Parts(); - for (std::vector<std::string>::const_iterator it = parts.begin(); it != parts.end(); ++it) { - const std::string& part_name = *it; - if (part_name.empty()) - continue; - const PartType* part_type = GetPartType(part_name); - if (!part_type) { - Logger().errorStream() << "FleetColonizeOrder::ServerExecute couldn't find ship part type: " << part_name; - continue; - } - if (part_type->Class() == PC_COLONY) { - colonist_capacity += boost::get<double>(part_type->Stats()); - } - } - if (colonist_capacity <= 0.0) { - Logger().debugStream() << "colonize order executed by ship with zero colonist capacity!"; + int ship_system_id = ship->SystemID(); + if (ship_system_id == UniverseObject::INVALID_OBJECT_ID) { + Logger().errorStream() << "ColonizeOrder::ExecuteImpl given id of ship not in a system"; return; } - - - // all checks passed. proceed with colonization. - - - planet->Reset(); - planet->SetSpecies(species_name); // do this BEFORE destroying the ship, since species_name is a const reference to Ship::m_species_name - - universe.Destroy(m_ship); - - - // find a focus to give planets by default. use first defined available focus. - // the planet's AvailableFoci function should return a vector of all names of - // available foci. - std::vector<std::string> available_foci = planet->AvailableFoci(); - if (!available_foci.empty()) - planet->SetFocus(*available_foci.begin()); - - planet->GetMeter(METER_POPULATION)->SetCurrent(colonist_capacity); - planet->GetMeter(METER_FARMING)->SetCurrent(planet->GetMeter(METER_POPULATION)->Current()); - planet->GetMeter(METER_HEALTH)->SetCurrent(20.0); - planet->BackPropegateMeters(); - - planet->AddOwner(EmpireID()); - - const std::set<int>& planet_buildings = planet->Buildings(); - for (std::set<int>::const_iterator it = planet_buildings.begin(); it != planet_buildings.end(); ++it) { - if (Building* building = objects.Object<Building>(*it)) { - building->ClearOwners(); - building->AddOwner(EmpireID()); // TODO: only add owner if empire has visibility of building. Need to add a check for this every turn... maybe doesn't need to be done during colonization at all? - } else { - Logger().errorStream() << "FleetColonizeOrder::ServerExecute couldn't find building with id: " << *it; - } - } - - //Logger().debugStream() << "colonizing planet " << planet->Name() << " by empire " << EmpireID() << " meters:"; - //for (MeterType meter_type = MeterType(0); meter_type != NUM_METER_TYPES; meter_type = MeterType(meter_type + 1)) - // if (const Meter* meter = planet->GetMeter(meter_type)) - // Logger().debugStream() << "type: " << boost::lexical_cast<std::string>(meter_type) << " val: " << meter->Initial(); -} - -void FleetColonizeOrder::ExecuteImpl() const -{ - ValidateEmpireID(); - - Universe& universe = GetUniverse(); - ObjectMap& objects = universe.Objects(); - - // look up the ship and fleet in question - Ship* ship = objects.Object<Ship>(m_ship); - if (!ship) { - Logger().errorStream() << "Empire attempted to colonize with a nonexistant ship"; + int planet_system_id = planet->SystemID(); + if (ship_system_id != planet_system_id) { + Logger().errorStream() << "ColonizeOrder::ExecuteImpl given ids of ship and planet not in the same system"; return; } - - Fleet* fleet = objects.Object<Fleet>(ship->FleetID()); - if (!fleet) { - Logger().errorStream() << "Empire attempte to colonize with a ship that somehow doesn't have a fleet...?"; + if (planet->IsAboutToBeColonized()) { + Logger().errorStream() << "ColonizeOrder::ExecuteImpl given id planet that is already being colonized"; return; } - // verify that empire issuing order owns specified fleet - if (!fleet->OwnedBy(EmpireID())) { - Logger().errorStream() << "Empire attempted to issue colonize order to another's fleet."; - return; - } - - // verify that planet exists and is un-occupied. - Planet* planet = objects.Object<Planet>(m_planet); - if (!planet) { - Logger().errorStream() << "Colonization order issued with invalid planet id."; - return; - } - - if (!planet->Unowned()) { - Logger().errorStream() << "Colonization order issued for owned planet."; - return; - } - - // verify that planet is in same system as the fleet - if (planet->SystemID() != fleet->SystemID() || planet->SystemID() == UniverseObject::INVALID_OBJECT_ID) { - Logger().errorStream() << "Fleet specified in colonization order is not in specified system."; - return; - } - planet->SetIsAboutToBeColonized(true); - - m_colony_fleet_id = fleet->ID(); // record the fleet in which the colony ship started - m_colony_fleet_name = fleet->Name(); - - // Remove colony ship from fleet; if colony ship is only ship in fleet, destroy the fleet. - // This leaves the ship in existence, and in its starting system, but not in any fleet; - // this situation will be resolved by either ServerExecute() or UndoImpl(). - if (fleet->NumShips() == 1) { - universe.Destroy(m_colony_fleet_id); - } else { - fleet->RemoveShip(m_ship); - } + ship->SetColonizePlanet(m_planet); } -bool FleetColonizeOrder::UndoImpl() const +bool ColonizeOrder::UndoImpl() const { - // Note that this function does double duty: it serves as a normal client-side undo, but must also - // serve as a server-side undo, when more than one empire tries to colonize the same planet at the - // same time. - - Universe& universe = GetUniverse(); - ObjectMap& objects = universe.Objects(); - - Planet* planet = objects.Object<Planet>(m_planet); + Planet* planet = GetObject<Planet>(m_planet); if (!planet) { - Logger().errorStream() << "Attempting to undo a fleet colonize order with an invalid planet id"; + Logger().errorStream() << "ColonizeOrder::UndoImpl couldn't get planet with id " << m_planet; return false; } - Fleet* fleet = objects.Object<Fleet>(m_colony_fleet_id); - // not having a fleet is OK - it may have been removed if the colony ship was the last ship in the fleet - Ship* ship = objects.Object<Ship>(m_ship); - if (!ship) { - Logger().errorStream() << "Attempting to under a fleet colonize order with an invalid ship id"; + if (!planet->IsAboutToBeColonized()) { + Logger().errorStream() << "ColonizeOrder::UndoImpl planet is not about to be colonized..."; return false; } - // if the fleet from which the colony ship came no longer exists or has moved, recreate it - if (!fleet || fleet->SystemID() != ship->SystemID()) { - System* system = objects.Object<System>(planet->SystemID()); - - int new_fleet_id = m_colony_fleet_id; - std::string new_fleet_name = m_colony_fleet_name; - - if (fleet && (fleet->SystemID() != ship->SystemID())) { - // fleet still exists, but it or ship are no longer in the same system, so - // need to create a new fleet for the ship in the system it is in - - // new id for new fleet - new_fleet_id = GetNewObjectID(); - // name for new fleet - std::vector<int> ship_ids; ship_ids.push_back(m_ship); - new_fleet_name = Fleet::GenerateFleetName(ship_ids, new_fleet_id); // potential bug?! Client and Server might be using different languge files, meaning the client will see fleet name in its set language on the turn of the undo, but will see a different language fleet name on the next turn which is permanently set by the server undo - } - - fleet = new Fleet(new_fleet_name, system->X(), system->Y(), EmpireID()); - if (new_fleet_id == UniverseObject::INVALID_OBJECT_ID) { - Logger().errorStream() << "FleetColonizeOrder::UndoImpl(): Unable to obtain a new fleet ID"; - return false; - } - fleet->GetMeter(METER_STEALTH)->SetCurrent(Meter::LARGE_VALUE); - - universe.InsertID(fleet, new_fleet_id); - fleet->AddShip(ship->ID()); - system->Insert(fleet); - } else { - fleet->AddShip(ship->ID()); + Ship* ship = GetObject<Ship>(m_ship); + if (!ship) { + Logger().errorStream() << "ColonizeOrder::UndoImpl couldn't get ship with id " << m_ship; + return false; } + if (ship->OrderedColonizePlanet() != m_planet) { + Logger().errorStream() << "ColonizeOrder::UndoImpl ship is not about to colonize planet"; + return false; + } planet->SetIsAboutToBeColonized(false); + ship->ClearColonizePlanet(); return true; } @@ -970,6 +835,8 @@ } else if (Building* building = GetObject<Building>(m_object_id)) { if (building->OwnedBy(empire_id)) building->SetOrderedScrapped(false); + } else { + return false; } return true; } Modified: trunk/FreeOrion/util/Order.h =================================================================== --- trunk/FreeOrion/util/Order.h 2010-08-15 23:24:26 UTC (rev 3731) +++ trunk/FreeOrion/util/Order.h 2010-08-16 07:25:47 UTC (rev 3732) @@ -22,7 +22,7 @@ side. Subclass-defined UndoImpl() \a must return true, indicating that the call had some effect; the default implementation does nothing and returns false. Note that only some Order subclasses define UndoImpl(), specifically those that need to be undone before another order of a similar type can be - issued. For example, FleetColonizeOrder needs to be undoable; otherwise, once the user clicks the + issued. For example, ColonizeOrder needs to be undoable; otherwise, once the user clicks the colonize button, she is locked in to this decision. */ class Order { @@ -237,15 +237,15 @@ ///////////////////////////////////////////////////// -// FleetColonizeOrder +// ColonizeOrder ///////////////////////////////////////////////////// /** the Order subclass that represents a planet colonization action*/ -class FleetColonizeOrder : public Order +class ColonizeOrder : public Order { public: /** \name Structors */ //@{ - FleetColonizeOrder(); - FleetColonizeOrder(int empire, int ship, int planet); + ColonizeOrder(); + ColonizeOrder(int empire, int ship, int planet); //@} /** \name Accessors */ //@{ @@ -253,24 +253,17 @@ int ShipID () const {return m_ship ;} ///< returns ID of the ship which is colonizing the planet //@} - virtual void ServerExecute() const; ///< called if the server allows the colonization effort - private: /** * Preconditions: - * - m_fleet must be the ID of a fleet owned by issuing empire * - m_planet must be the ID of an un-owned planet. - * - the fleet and the planet must have the same x,y coordinates - * - the fleet must contain a colony ship + * - m_ship must be the the ID of a ship owned by the issuing empire + * - m_ship must be the ID of a ship that can colonize and that is in + * the same system as the planet. * * Postconditions: - * - a colony ship will be removed from the fleet and deallocated - * if the fleet becomes empty it will be deallocated. - * - the empire issuing the order will be added to the list of owners - * for the planet - * - the planet's population will be increased - * - the planet will be added to the empire's list of owned planets - * + * - The ship with ID m_ship will be marked to colonize the planet with + * id m_planet during the next turn processing. */ virtual void ExecuteImpl() const; virtual bool UndoImpl() const; @@ -278,10 +271,6 @@ int m_ship; int m_planet; - // these are for undoing this order only - mutable int m_colony_fleet_id; // the fleet from which the colony ship was taken - mutable std::string m_colony_fleet_name; // the name of fleet from which the colony ship was taken - friend class boost::serialization::access; template <class Archive> void serialize(Archive& ar, const unsigned int version); @@ -455,18 +444,21 @@ private: /** * Preconditions of execute: - * - For creating a new design, the passed design is a valid reference to a design created by the - * empire issuing the order - * - For remembering an existing ship design, there exists a ship design with the passed id, and - * the empire is aware of this ship design - * - For removing a shipdesign from the empire's set of designs, there empire has a design with the - * passed id in its set of designs + * - For creating a new design, the passed design is a valid reference + * to a design created by the empire issuing the order + * - For remembering an existing ship design, there exists a ship design + * with the passed id, and the empire is aware of this ship design + * - For removing a shipdesign from the empire's set of designs, there + * empire has a design with the passed id in its set of designs * * Postconditions: - * - For creating a new ship design, the universe will contain a new ship design, and the creating - * empire will have the new design as of of its designs - * - For remembering a ship design, the empire will have the design's id in its set of design ids - * - For removing a design, the empire will no longer have the design's id in its set of design ids + * - For creating a new ship design, the universe will contain a new ship + * design, and the creating empire will have the new design as of of + * its designs + * - For remembering a ship design, the empire will have the design's id + * in its set of design ids + * - For removing a design, the empire will no longer have the design's + * id in its set of design ids */ virtual void ExecuteImpl() const; @@ -505,7 +497,7 @@ * - the object must be scrappable: ships or buildings * * Postconditions: - * - the object is deleted + * - the object is marked to be scrapped during the next turn processing. */ virtual void ExecuteImpl() const; virtual bool UndoImpl() const; Modified: trunk/FreeOrion/util/SerializeOrderSet.cpp =================================================================== --- trunk/FreeOrion/util/SerializeOrderSet.cpp 2010-08-15 23:24:26 UTC (rev 3731) +++ trunk/FreeOrion/util/SerializeOrderSet.cpp 2010-08-16 07:25:47 UTC (rev 3732) @@ -17,7 +17,7 @@ BOOST_CLASS_EXPORT(NewFleetOrder) BOOST_CLASS_EXPORT(FleetMoveOrder) BOOST_CLASS_EXPORT(FleetTransferOrder) -BOOST_CLASS_EXPORT(FleetColonizeOrder) +BOOST_CLASS_EXPORT(ColonizeOrder) BOOST_CLASS_EXPORT(DeleteFleetOrder) BOOST_CLASS_EXPORT(ChangeFocusOrder) BOOST_CLASS_EXPORT(ResearchQueueOrder) @@ -70,13 +70,11 @@ } template <class Archive> -void FleetColonizeOrder::serialize(Archive& ar, const unsigned int version) +void ColonizeOrder::serialize(Archive& ar, const unsigned int version) { ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(Order) & BOOST_SERIALIZATION_NVP(m_ship) - & BOOST_SERIALIZATION_NVP(m_planet) - & BOOST_SERIALIZATION_NVP(m_colony_fleet_id) - & BOOST_SERIALIZATION_NVP(m_colony_fleet_name); + & BOOST_SERIALIZATION_NVP(m_planet); } template <class Archive> Modified: trunk/FreeOrion/util/SerializeUniverse.cpp =================================================================== --- trunk/FreeOrion/util/SerializeUniverse.cpp 2010-08-15 23:24:26 UTC (rev 3731) +++ trunk/FreeOrion/util/SerializeUniverse.cpp 2010-08-16 07:25:47 UTC (rev 3732) @@ -155,6 +155,7 @@ & BOOST_SERIALIZATION_NVP(m_design_id) & BOOST_SERIALIZATION_NVP(m_fleet_id) & BOOST_SERIALIZATION_NVP(m_ordered_scrapped) + & BOOST_SERIALIZATION_NVP(m_ordered_colonize_planet_id) & BOOST_SERIALIZATION_NVP(m_fighters) & BOOST_SERIALIZATION_NVP(m_missiles) & BOOST_SERIALIZATION_NVP(m_part_meters) |