User:Geoff the Medio/Coding
Contents
- 1 System::Insert()
- 2 SidePanel::ClickColoinze()
- 3 FleetColonizeOrder class header
- 4 DeleteFleetOrder class header
- 5 HumanClientApp Header changes
- 6 HumanClientApp Function definition
- 7 FleetColonizeOrder function definitions
- 8 DeleteFleetOrder function definitions
- 9 Destroy / Universe Header changes
- 10 Destroy Effect Functions
- 11 Fleet Movepath Colours
System::Insert()
Replace System::Insert (appropriate version thereof) with this:
int System::Insert(int obj_id, int orbit) { if (orbit < -1) throw std::invalid_argument("System::Insert() : Attempted to place an object in an orbit less than -1"); if (!GetUniverse().Object(obj_id)) throw std::invalid_argument("System::Insert() : Attempted to place an object in a System, when the object is not already in the Universe"); // erase all preexisting instances of object in m_objects std::list< orbit_iterator > del_its; del_its.clear(); for (orbit_iterator it = m_objects.begin(); it != m_objects.end(); ++it) if (it->second == obj_id) del_its.push_back(it); for (std::list<orbit_iterator >::iterator it = del_its.begin(); it != del_its.end(); ++it) m_objects.erase(*it); if (m_orbits <= orbit) m_orbits = orbit + 1; m_objects.insert(std::pair<int, int>(orbit, obj_id)); Fleet *fleet = GetUniverse().Object<Fleet>(obj_id); if(fleet) FleetAddedSignal()(*fleet); StateChangedSignal()(); return orbit; }
This comment should be added to System::Remove to clarify its function:
// assumes only one instance of object is in m_objects
SidePanel::ClickColoinze()
Goes in SidePanel.cpp
void SidePanel::PlanetPanel::ClickColonize() { const Planet *planet = GetPlanet(); int empire_id = HumanClientApp::GetApp()->EmpireID(); std::map<int, int> pending_colonization_orders = HumanClientApp::GetApp()->PendingColonizationOrders(); std::map<int, int>::const_iterator it = pending_colonization_orders.find(planet->ID()); if(it == pending_colonization_orders.end()) // colonize { Ship *ship = FindColonyShip(planet->SystemID()); if(!ship) throw std::runtime_error("SidePanel::PlanetPanel::ClickColonize ship not found!"); Fleet *fleet = ship->GetFleet(); if(!fleet->Accept(StationaryFleetVisitor(*fleet->Owners().begin()))) { GG::ThreeButtonDlg dlg(320,200,UserString("SP_USE_DEPARTING_COLONY_SHIPS_QUESTION"), ClientUI::FONT,ClientUI::PTS,ClientUI::WND_COLOR,ClientUI::CTRL_BORDER_COLOR,ClientUI::CTRL_COLOR,ClientUI::TEXT_COLOR,2, "Yes","No"); dlg.Run(); if(dlg.Result()!=0) return; } HumanClientApp::Orders().IssueOrder( new FleetColonizeOrder(empire_id, ship->ID(), planet->ID()) ); if (!fleet->NumShips()) { HumanClientApp::Orders().IssueOrder( new DeleteFleetOrder( empire_id, fleet->ID()) ); } } else // cancel colonization { const FleetColonizeOrder *col_order = dynamic_cast<const FleetColonizeOrder*>(HumanClientApp::Orders().ExamineOrder(it->second)); int ship_id = col_order ? col_order->ShipID() : UniverseObject::INVALID_OBJECT_ID; Ship *ship = GetUniverse().Object<Ship>(ship_id); int fleet_id = ship->FleetID(); // check if ship's fleet still exists Fleet *fleet = ship ? GetUniverse().Object<Fleet>(fleet_id) : NULL; if (!fleet) { // fleet doesn't exist, so recind order that deleted it, thus recreating it std::map<int, int> del_ords = HumanClientApp::GetApp()->PendingDeleteFleetOrders(); std::map<int, int>::iterator del_ord_it = del_ords.find(fleet_id); if (del_ord_it != del_ords.end()) HumanClientApp::Orders().RecindOrder(del_ord_it->second); // try again to get poitner... fleet = GetUniverse().Object<Fleet>(fleet_id); } if (!fleet) throw std::runtime_error("Sidepanel::ClickColonize: Fleet couldn't be recreated by recinding delete order!"); // Recind colonization order, adding ship back into fleet HumanClientApp::Orders().RecindOrder(it->second); // make sure that fleet appears at a possibly opend FleetWnd for (FleetWnd::FleetWndItr it = FleetWnd::FleetWndBegin(); it != FleetWnd::FleetWndEnd(); ++it) { FleetWnd *fleet_wnd = *it; if(fleet->SystemID() == fleet_wnd->SystemID() && !fleet_wnd->ContainsFleet(fleet->ID())) { fleet_wnd->AddFleet(GetUniverse().Object<Fleet>(fleet->ID())); break; } } } }
FleetColonizeOrder class header
Goes in Order.h
class FleetColonizeOrder : public Order { public: /** \name Structors */ //@{ FleetColonizeOrder(); FleetColonizeOrder(const GG::XMLElement& elem); FleetColonizeOrder(int empire, int ship, int planet); //@} /** \name Accessors */ //@{ int PlanetID() const {return m_planet;} ///< returns ID of the planet to be colonized 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 virtual GG::XMLElement XMLEncode() const; ///< constructs an XMLElement for the order //@} 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 * * 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 * */ //< either ExecuteServerApply or ExecuteServerRevoke is called!!! virtual void ExecuteImpl() const; virtual bool UndoImpl() const; 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 };
DeleteFleetOrder class header
Goes in Order.h
///////////////////////////////////////////////////// // DeleteFleetOrder ///////////////////////////////////////////////////// /** the Order subclass that represents forming a new fleet. Only one of system or position will be used to place the new fleet.*/ class DeleteFleetOrder : public Order { public: /** \name Structors */ //@{ DeleteFleetOrder(); DeleteFleetOrder(const GG::XMLElement& elem); DeleteFleetOrder(int empire, int fleet); //@} /** \name Accessors */ //@{ int FleetID() const {return m_fleet;} ///< returns ID of the fleet to be deleted virtual GG::XMLElement XMLEncode() const; ///< constructs an XMLElement for the order //@} 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 * * 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 * */ //< either ExecuteServerApply or ExecuteServerRevoke is called!!! virtual void ExecuteImpl() const; virtual bool UndoImpl() const; int m_fleet; // these are for undoing this order only mutable std::string m_fleet_name; mutable int m_system_id; };
HumanClientApp Header changes
In HumanClientApp.h, after this declaration:
/** Returns a map from Planet IDs to pending (issued earlier this turn and undo-able) colonization order IDs. */ std::map<int, int> PendingColonizationOrders() const;
Add this:
/** Returns a map from Fleet IDs to pending (issued earlier this turn, may not be undo-able) delete fleet order IDs. */ std::map<int, int> PendingDeleteFleetOrders() const; //@}
HumanClientApp Function definition
In HumanClientApp.cpp, after the code for the function:
std::map<int, int> HumanClientApp::PendingColonizationOrders() const
Add this function:
std::map<int, int> HumanClientApp::PendingDeleteFleetOrders() const { std::map<int, int> retval; for (OrderSet::const_iterator it = m_orders.begin(); it != m_orders.end(); ++it) { if (const DeleteFleetOrder* order = dynamic_cast<const DeleteFleetOrder*>(it->second)) { retval[order->FleetID()] = it->first; } } return retval; }
FleetColonizeOrder function definitions
Goes in Order.cpp
FleetColonizeOrder::FleetColonizeOrder() : Order(), m_ship(UniverseObject::INVALID_OBJECT_ID), m_planet(UniverseObject::INVALID_OBJECT_ID) { } FleetColonizeOrder::FleetColonizeOrder(const GG::XMLElement& elem) : Order(elem.Child("Order")) { if(elem.Tag() != ("FleetColonizeOrder")) throw std::invalid_argument("Attempted to construct FleetColonizeOrder from malformed XMLElement"); m_ship = lexical_cast<int>(elem.Child("m_ship").Text()); m_planet = lexical_cast<int>(elem.Child("m_planet").Text()); m_colony_fleet_id = lexical_cast<int>(elem.Child("m_colony_fleet_id").Text()); } FleetColonizeOrder::FleetColonizeOrder(int empire, int ship, int planet) : Order(empire), m_ship(ship), m_planet(planet) { } void FleetColonizeOrder::ServerExecute() const { Universe& universe = GetUniverse(); universe.Delete(m_ship); Planet* planet = universe.Object<Planet>(m_planet); planet->ResetMaxMeters(); planet->AdjustMaxMeters(); planet->AdjustPop(INITIAL_COLONY_POP); planet->GetMeter(METER_FARMING)->SetCurrent(INITIAL_COLONY_POP); planet->GetMeter(METER_HEALTH)->SetCurrent(planet->GetMeter(METER_HEALTH)->Max()); planet->AddOwner(EmpireID()); if (System* system = planet->GetSystem()) system->AddOwner(EmpireID()); } XMLElement FleetColonizeOrder::XMLEncode() const { XMLElement retval("FleetColonizeOrder"); retval.AppendChild(Order::XMLEncode()); retval.AppendChild(XMLElement("m_ship", lexical_cast<std::string>(m_ship))); retval.AppendChild(XMLElement("m_planet", lexical_cast<std::string>(m_planet))); retval.AppendChild(XMLElement("m_colony_fleet_id", lexical_cast<std::string>(m_colony_fleet_id))); return retval; } void FleetColonizeOrder::ExecuteImpl() const { ValidateEmpireID(); Universe& universe = GetUniverse(); // look up the ship and fleet in question Ship* ship = universe.Object<Ship>(m_ship); Fleet* fleet = universe.Object<Fleet>(ship->FleetID()); // ensure fleet exists if (!fleet) throw std::runtime_error("Illegal fleet id specified in fleet colonize order."); // verify that empire issuing order owns specified fleet if (!fleet->OwnedBy(EmpireID())) throw std::runtime_error("Empire attempted to issue colonize order to another's fleet."); // verify that planet exists and is un-occupied. Planet* planet = universe.Object<Planet>(m_planet); if (planet == NULL) throw std::runtime_error("Colonization order issued with invalid planet id."); if (!planet->Unowned()) throw std::runtime_error("Colonization order issued for owned planet."); // verify that planet is in same system as the fleet if (planet->SystemID() != fleet->SystemID() || planet->SystemID() == UniverseObject::INVALID_OBJECT_ID) { throw std::runtime_error("Fleet specified in colonization order is not in " "specified system."); } planet->SetIsAboutToBeColonized(true); m_colony_fleet_id = fleet->ID(); // record the fleet in which the colony ship started // Remove colony ship from fleet; fleet->RemoveShip(m_ship); } bool FleetColonizeOrder::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(); Fleet* fleet = universe.Object<Fleet>(m_colony_fleet_id); Planet* planet = universe.Object<Planet>(m_planet); planet->SetIsAboutToBeColonized(false); Ship* ship = universe.Object<Ship>(m_ship); // if the fleet from which the colony ship came no longer exists or has moved, make a new one if (!fleet || fleet->SystemID() != ship->SystemID()) { System* system = planet->GetSystem(); fleet = new Fleet("Colony Fleet", system->X(), system->Y(), EmpireID()); universe.Insert(fleet); fleet->AddShip(ship->ID()); system->Insert(fleet); } else { fleet->AddShip(ship->ID()); } return true; }
DeleteFleetOrder function definitions
Goes in Order.cpp
//////////////////////////////////////////////// // DeleteFleetOrder //////////////////////////////////////////////// DeleteFleetOrder::DeleteFleetOrder() : Order(), m_fleet(-1) { } DeleteFleetOrder::DeleteFleetOrder(const GG::XMLElement& elem): Order(elem.Child("Order")) { if(elem.Tag() != ("DeleteFleetOrder")) throw std::invalid_argument("Attempted to construct DeleteFleetOrder from malformed XMLElement"); m_fleet = lexical_cast<int>(elem.Child("m_fleet").Text()); } DeleteFleetOrder::DeleteFleetOrder(int empire, int fleet) : Order(empire), m_fleet(fleet) { } GG::XMLElement DeleteFleetOrder::XMLEncode() const { XMLElement retval("DeleteFleetOrder"); retval.AppendChild(Order::XMLEncode()); retval.AppendChild(XMLElement("m_fleet", lexical_cast<std::string>(m_fleet))); return retval; } void DeleteFleetOrder::ExecuteImpl() const { ValidateEmpireID(); Fleet* fleet = GetUniverse().Object<Fleet>(FleetID()); if(!fleet) throw std::runtime_error("Illegal fleet id specified in fleet colonize order."); if (!fleet->OwnedBy(EmpireID())) throw std::runtime_error("Empire attempted to issue deletion order to another's fleet."); if (fleet->NumShips()) throw std::runtime_error("Attempted to delete an unempty fleet."); // store info so as to be able to recind order m_system_id = fleet->GetSystem()->ID(); m_fleet_name = fleet->Name(); GetUniverse().Delete(FleetID()); } bool DeleteFleetOrder::UndoImpl() const { Universe& universe = GetUniverse(); System* system = universe.Object<System>(m_system_id); Fleet *fleet = new Fleet(m_fleet_name, system->X(), system->Y(), EmpireID()); universe.InsertID(fleet, m_fleet); system->Insert(fleet); return true; }
Destroy / Universe Header changes
In public section of Universe.h, after:
/** Applies all Effects from Buildings, Specials, Techs, etc. */ void ApplyEffects();
add:
/** Adds passed object ID to the list of UniverseObjects that have had Destroy effects executed on them this turn and need to be Deleted after effects processing */ void AddDestroyTarget(int id);
And, in protected section of Universe.h, add:
/** Calls Universe::Delete() to actually delete the memory objects of UniverseObjects that had a Destroy effected executed on them */ void DoDestroyEffectDeletion();
and, also in protected section of Universe.h, add:
std::list<int> m_destroy_effect_targets; ///< ordered list of UniverseObject IDs that have been the target of a Destroy effect this turn and need to be Deleted after effects processing is finished
Destroy Effect Functions
Replace this function in Universe.cpp (minor changes to change how destroy effects are executed):
void Universe::ApplyEffects() { // reset max meter state that is affected by the application of effects for (const_iterator it = begin(); it != end(); ++it) { it->second->ResetMaxMeters(); it->second->AdjustMaxMeters(); } // emtpy list of UniverseObjects that have have had Destroy effects executed on them this turn m_destroy_effect_targets.clear(); // cache all activation and scoping condition results before applying Effects, since the application of // these Effects may affect the activation and scoping evaluations typedef std::multimap<boost::shared_ptr<const Effect::EffectsGroup>, std::pair<int, Effect::EffectsGroup::TargetSet> > EffectsAndTargetsMap; typedef std::pair<boost::shared_ptr<const Effect::EffectsGroup>, std::pair<int, Effect::EffectsGroup::TargetSet> > EffectsAndTargetsMapElem; EffectsAndTargetsMap effects_targets_map; for (EmpireManager::iterator it = Empires().begin(); it != Empires().end(); ++it) { it->second->ClearRefinements(); for (Empire::TechItr tech_it = it->second->TechBegin(); tech_it != it->second->TechEnd(); ++tech_it) { const Tech* tech = GetTech(*tech_it); assert(tech); for (unsigned int i = 0; i < tech->Effects().size(); ++i) { boost::shared_ptr<const Effect::EffectsGroup> effect = tech->Effects()[i]; EffectsAndTargetsMapElem map_elem(effect, std::make_pair(it->second->CapitolID(), Effect::EffectsGroup::TargetSet())); tech->Effects()[i]->GetTargetSet(it->second->CapitolID(), map_elem.second.second); effects_targets_map.insert(map_elem); } } } std::vector<Building*> buildings = FindObjects<Building>(); for (unsigned int i = 0; i < buildings.size(); ++i) { const BuildingType* building_type = buildings[i]->GetBuildingType(); assert(building_type); for (unsigned int j = 0; j < building_type->Effects().size(); ++j) { boost::shared_ptr<const Effect::EffectsGroup> effect = building_type->Effects()[j]; EffectsAndTargetsMapElem map_elem(effect, std::make_pair(buildings[i]->ID(), Effect::EffectsGroup::TargetSet())); building_type->Effects()[j]->GetTargetSet(buildings[i]->ID(), map_elem.second.second); effects_targets_map.insert(map_elem); } } for (Universe::const_iterator it = begin(); it != end(); ++it) { for (std::set<std::string>::const_iterator special_it = it->second->Specials().begin(); special_it != it->second->Specials().end(); ++special_it) { const Special* special = GetSpecial(*special_it); assert(special); for (unsigned int i = 0; i < special->Effects().size(); ++i) { boost::shared_ptr<const Effect::EffectsGroup> effect = special->Effects()[i]; EffectsAndTargetsMapElem map_elem(effect, std::make_pair(it->first, Effect::EffectsGroup::TargetSet())); special->Effects()[i]->GetTargetSet(it->first, map_elem.second.second); effects_targets_map.insert(map_elem); } } } // execute effects on cached results std::map<std::string, Effect::EffectsGroup::TargetSet> executed_nonstacking_effects; for (EffectsAndTargetsMap::const_iterator it = effects_targets_map.begin(); it != effects_targets_map.end(); ++it) { // if other EffectsGroups with the same stacking group have affected some of the targets in the scope of the current EffectsGroup, skip them std::map<std::string, std::set<UniverseObject*> >::iterator non_stacking_it = executed_nonstacking_effects.find(it->first->StackingGroup()); Effect::EffectsGroup::TargetSet targets(it->second.second); if (non_stacking_it != executed_nonstacking_effects.end()) { for (Effect::EffectsGroup::TargetSet::const_iterator object_it = non_stacking_it->second.begin(); object_it != non_stacking_it->second.end(); ++object_it) { targets.erase(*object_it); } } // execute the Effects in the EffectsGroup it->first->Execute(it->second.first, targets); // if this EffectsGroup belongs to a stacking group, add the objects just affected by it to executed_nonstacking_effects if (it->first->StackingGroup() != "") { Effect::EffectsGroup::TargetSet& affected_targets = executed_nonstacking_effects[it->first->StackingGroup()]; for (Effect::EffectsGroup::TargetSet::const_iterator object_it = targets.begin(); object_it != targets.end(); ++object_it) { affected_targets.insert(*object_it); } } } // delete objects that had Destroy effects executed on them DoDestroyEffectDeletion(); }
And add these two new functions (called by the above):
void Universe::AddDestroyTarget(int id) { m_destroy_effect_targets.push_back(id); } void Universe::DoDestroyEffectDeletion() { std::set<int> destroyedObjects; destroyedObjects.clear(); // iterate through destroy effect targets, destroying them and doing side effects as appropriate for (std::list<int>::iterator target_it = m_destroy_effect_targets.begin(); target_it != m_destroy_effect_targets.end(); ++target_it) { int id = *target_it; std::set<int>::iterator dest_objs_it = destroyedObjects.find(id); // ensure object hasn't already been destroyed if (dest_objs_it == destroyedObjects.end()) { // add it to the list of destroyed objects destroyedObjects.insert(id); // find out if object still exists UniverseObject* obj = 0; iterator it = m_objects.find(id); if (it != m_objects.end()) { obj = it->second; // Special cases for various types of UniverseObject if (Ship* ship = universe_object_cast<Ship*>(obj)) { // if a ship is being deleted, and it is the last ship in its fleet, // then the fleet should also be deleted (after the ship is deleted) if (Fleet* fleet = ship->GetFleet()) { fleet->RemoveShip(ship->ID()); if (fleet->NumShips() <= 0) m_destroy_effect_targets.push_back(fleet->ID()); } } else if (Fleet* fleet = universe_object_cast<Fleet*>(obj)) { // if a fleet is being deleted, all its ships should be deleted as well. // (before the fleet is deleted) // Effect execution should happen after player orders are executed, so that // orders to delete fleets or transfer ships from fleets happen before // these destroy effects std::set<int> ships; for(Fleet::const_iterator ship_it = fleet->begin(); ship_it != fleet->end(); ++ship_it) ships.insert(*ship_it); // delete ships NOW, don't just add them to the list of things to delete later. // this way, when when the Delete(fleet's id) function is called below, the fleet's ships // will have already been deleted, so there won't be an exception about attempting to // delete a non-empty fleet. for(std::set<int>::iterator ship_it = ships.begin(); ship_it != ships.end(); ++ship_it) { int ship_id = *ship_it; fleet->RemoveShip(ship_id); Delete(ship_id); destroyedObjects.insert(ship_id); } } Delete(id); } // endif does UniverseObject still exist } // endif has the object already been deleted } // end for loop over list of object IDs to delete }
Fleet Movepath Colours
To colour fleets' movepaths with their empire colour: In MapWnd.cpp, in MapWnd::RenderFleetMovementLines(), immediately before this line:
glBegin(GL_LINES);
Add these lines:
// get color of fleet's owner empire's color GG::Clr emp_clr; const std::set<int> owners = it->first->Owners(); if (owners.size() == 1) { emp_clr = HumanClientApp::Empires().Lookup(*owners.begin())->Color(); } else { emp_clr = GG::Clr(255, 255, 255, 255); } glColor4d(emp_clr.r / 255.0f, emp_clr.g / 255.0f, emp_clr.b / 255.0f, emp_clr.a / 255.0f);
I suspect there's a better way to convert a GG:Clr to OpenGL's components in the range [0.0, 1.0], but I don't know it. In any case, this works.