1 #include "Empire.h"
2 
3 #include "../util/i18n.h"
4 #include "../util/Random.h"
5 #include "../util/Logger.h"
6 #include "../util/AppInterface.h"
7 #include "../util/SitRepEntry.h"
8 #include "../universe/Building.h"
9 #include "../universe/BuildingType.h"
10 #include "../universe/Fleet.h"
11 #include "../universe/Ship.h"
12 #include "../universe/ShipDesign.h"
13 #include "../universe/ShipHull.h"
14 #include "../universe/ShipPart.h"
15 #include "../universe/Planet.h"
16 #include "../universe/System.h"
17 #include "../universe/Tech.h"
18 #include "../universe/UniverseObject.h"
19 #include "../universe/UnlockableItem.h"
20 #include "EmpireManager.h"
21 #include "Supply.h"
22 
23 #include <boost/uuid/uuid_io.hpp>
24 #include <unordered_set>
25 
26 
27 namespace {
28     const float EPSILON = 0.01f;
29     const std::string EMPTY_STRING;
30 
31     DeclareThreadSafeLogger(supply);
32 }
33 
34 
35 ////////////
36 // Empire //
37 ////////////
Empire()38 Empire::Empire() :
39     m_authenticated(false),
40     m_research_queue(m_id),
41     m_production_queue(m_id)
42 { Init(); }
43 
Empire(const std::string & name,const std::string & player_name,int empire_id,const GG::Clr & color,bool authenticated)44 Empire::Empire(const std::string& name, const std::string& player_name,
45                int empire_id, const GG::Clr& color, bool authenticated) :
46     m_id(empire_id),
47     m_name(name),
48     m_player_name(player_name),
49     m_authenticated(authenticated),
50     m_color(color),
51     m_research_queue(m_id),
52     m_production_queue(m_id)
53 {
54     DebugLogger() << "Empire::Empire(" << name << ", " << player_name << ", " << empire_id << ", colour)";
55     Init();
56 }
57 
Init()58 void Empire::Init() {
59     m_resource_pools[RE_RESEARCH] = std::make_shared<ResourcePool>(RE_RESEARCH);
60     m_resource_pools[RE_INDUSTRY] = std::make_shared<ResourcePool>(RE_INDUSTRY);
61     m_resource_pools[RE_TRADE] = std::make_shared<ResourcePool>(RE_TRADE);
62 
63     m_eliminated = false;
64 
65     m_meters[UserStringNop("METER_DETECTION_STRENGTH")];
66     m_meters[UserStringNop("METER_BUILDING_COST_FACTOR")];
67     m_meters[UserStringNop("METER_SHIP_COST_FACTOR")];
68     m_meters[UserStringNop("METER_TECH_COST_FACTOR")];
69 }
70 
~Empire()71 Empire::~Empire()
72 { ClearSitRep(); }
73 
Name() const74 const std::string& Empire::Name() const
75 { return m_name; }
76 
PlayerName() const77 const std::string& Empire::PlayerName() const
78 { return m_player_name; }
79 
IsAuthenticated() const80 bool Empire::IsAuthenticated() const
81 { return m_authenticated; }
82 
EmpireID() const83 int Empire::EmpireID() const
84 { return m_id; }
85 
Color() const86 const GG::Clr& Empire::Color() const
87 { return m_color; }
88 
CapitalID() const89 int Empire::CapitalID() const
90 { return m_capital_id; }
91 
SourceID() const92 int Empire::SourceID() const {
93     auto good_source = Source();
94     return good_source ? good_source->ID() : INVALID_OBJECT_ID;
95 }
96 
Source() const97 std::shared_ptr<const UniverseObject> Empire::Source() const {
98     if (m_eliminated)
99         return nullptr;
100 
101     // Use the current source if valid
102     auto valid_current_source = Objects().get(m_source_id);
103     if (valid_current_source && valid_current_source->OwnedBy(m_id))
104         return valid_current_source;
105 
106     // Try the capital
107     auto capital_as_source = Objects().get(m_capital_id);
108     if (capital_as_source && capital_as_source->OwnedBy(m_id)) {
109         m_source_id = m_capital_id;
110         return capital_as_source;
111     }
112 
113     // Find any object owned by the empire
114     // TODO determine if ExistingObjects() is faster and acceptable
115     for (const auto& obj : Objects().all()) {
116         if (obj->OwnedBy(m_id)) {
117             m_source_id = obj->ID();
118             return obj;
119         }
120     }
121 
122     m_source_id = INVALID_OBJECT_ID;
123     return nullptr;
124 }
125 
Dump() const126 std::string Empire::Dump() const {
127     std::string retval = "Empire name: " + m_name +
128                          " ID: " + std::to_string(m_id) +
129                          " Capital ID: " + std::to_string(m_capital_id);
130     retval += " meters:\n";
131     for (const auto& meter : m_meters) {
132         retval += UserString(meter.first) + ": " +
133                   std::to_string(meter.second.Initial()) + "\n";
134     }
135     return retval;
136 }
137 
SetCapitalID(int id)138 void Empire::SetCapitalID(int id) {
139     m_capital_id = INVALID_OBJECT_ID;
140     m_source_id = INVALID_OBJECT_ID;
141 
142     if (id == INVALID_OBJECT_ID)
143         return;
144 
145     // Verify that the capital exists and is owned by the empire
146     auto possible_capital = Objects().ExistingObject(id);
147     if (possible_capital && possible_capital->OwnedBy(m_id))
148         m_capital_id = id;
149 
150     auto possible_source = Objects().get(id);
151     if (possible_source && possible_source->OwnedBy(m_id))
152         m_source_id = id;
153 }
154 
GetMeter(const std::string & name)155 Meter* Empire::GetMeter(const std::string& name) {
156     auto it = m_meters.find(name);
157     if (it != m_meters.end())
158         return &(it->second);
159     else
160         return nullptr;
161 }
162 
GetMeter(const std::string & name) const163 const Meter* Empire::GetMeter(const std::string& name) const {
164     auto it = m_meters.find(name);
165     if (it != m_meters.end())
166         return &(it->second);
167     else
168         return nullptr;
169 }
170 
BackPropagateMeters()171 void Empire::BackPropagateMeters() {
172     for (auto& meter : m_meters)
173         meter.second.BackPropagate();
174 }
175 
ResearchableTech(const std::string & name) const176 bool Empire::ResearchableTech(const std::string& name) const {
177     const Tech* tech = GetTech(name);
178     if (!tech)
179         return false;
180     for (const auto& prereq : tech->Prerequisites()) {
181         if (!m_techs.count(prereq))
182             return false;
183     }
184     return true;
185 }
186 
HasResearchedPrereqAndUnresearchedPrereq(const std::string & name) const187 bool Empire::HasResearchedPrereqAndUnresearchedPrereq(const std::string& name) const {
188     const Tech* tech = GetTech(name);
189     if (!tech)
190         return false;
191     bool one_unresearched = false;
192     bool one_researched = false;
193     for (const auto& prereq : tech->Prerequisites()) {
194         if (m_techs.count(prereq))
195             one_researched = true;
196         else
197             one_unresearched = true;
198     }
199     return one_unresearched && one_researched;
200 }
201 
GetResearchQueue() const202 const ResearchQueue& Empire::GetResearchQueue() const
203 { return m_research_queue; }
204 
ResearchProgress(const std::string & name) const205 float Empire::ResearchProgress(const std::string& name) const {
206     auto it = m_research_progress.find(name);
207     if (it == m_research_progress.end())
208         return 0.0f;
209     const Tech* tech = GetTech(it->first);
210     if (!tech)
211         return 0.0f;
212     float tech_cost = tech->ResearchCost(m_id);
213     return it->second * tech_cost;
214 }
215 
ResearchedTechs() const216 const std::map<std::string, int>& Empire::ResearchedTechs() const
217 { return m_techs; }
218 
TechResearched(const std::string & name) const219 bool Empire::TechResearched(const std::string& name) const
220 { return m_techs.count(name); }
221 
GetTechStatus(const std::string & name) const222 TechStatus Empire::GetTechStatus(const std::string& name) const {
223     if (TechResearched(name)) return TS_COMPLETE;
224     if (ResearchableTech(name)) return TS_RESEARCHABLE;
225     if (HasResearchedPrereqAndUnresearchedPrereq(name)) return TS_HAS_RESEARCHED_PREREQ;
226     return TS_UNRESEARCHABLE;
227 }
228 
TopPriorityEnqueuedTech() const229 const std::string& Empire::TopPriorityEnqueuedTech() const {
230     if (m_research_queue.empty())
231         return EMPTY_STRING;
232     auto it = m_research_queue.begin();
233     const std::string& tech = it->name;
234     return tech;
235 }
236 
MostExpensiveEnqueuedTech() const237 const std::string& Empire::MostExpensiveEnqueuedTech() const {
238     if (m_research_queue.empty())
239         return EMPTY_STRING;
240     float biggest_cost = -99999.9f; // arbitrary small number
241 
242     const ResearchQueue::Element* best_elem = nullptr;
243 
244     for (const auto& elem : m_research_queue) {
245         const Tech* tech = GetTech(elem.name);
246         if (!tech)
247             continue;
248         float tech_cost = tech->ResearchCost(m_id);
249         if (tech_cost > biggest_cost) {
250             biggest_cost = tech_cost;
251             best_elem = &elem;
252         }
253     }
254 
255     if (best_elem)
256         return best_elem->name;
257     return EMPTY_STRING;
258 }
259 
LeastExpensiveEnqueuedTech() const260 const std::string& Empire::LeastExpensiveEnqueuedTech() const {
261     if (m_research_queue.empty())
262         return EMPTY_STRING;
263     float smallest_cost = 999999.9f; // arbitrary large number
264 
265     const ResearchQueue::Element* best_elem = nullptr;
266 
267     for (const auto& elem : m_research_queue) {
268         const Tech* tech = GetTech(elem.name);
269         if (!tech)
270             continue;
271         float tech_cost = tech->ResearchCost(m_id);
272         if (tech_cost < smallest_cost) {
273             smallest_cost = tech_cost;
274             best_elem = &elem;
275         }
276     }
277 
278     if (best_elem)
279         return best_elem->name;
280     return EMPTY_STRING;
281 }
282 
MostRPSpentEnqueuedTech() const283 const std::string& Empire::MostRPSpentEnqueuedTech() const {
284     float most_spent = -999999.9f;  // arbitrary small number
285     const std::map<std::string, float>::value_type* best_progress = nullptr;
286 
287     for (const auto& progress : m_research_progress) {
288         const auto& tech_name = progress.first;
289         if (!m_research_queue.InQueue(tech_name))
290             continue;
291         float rp_spent = progress.second;
292         if (rp_spent > most_spent) {
293             best_progress = &progress;
294             most_spent = rp_spent;
295         }
296     }
297 
298     if (best_progress)
299         return best_progress->first;
300     return EMPTY_STRING;
301 }
302 
MostRPCostLeftEnqueuedTech() const303 const std::string& Empire::MostRPCostLeftEnqueuedTech() const {
304     float most_left = -999999.9f;  // arbitrary small number
305     const std::map<std::string, float>::value_type* best_progress = nullptr;
306 
307     for (const auto& progress : m_research_progress) {
308         const auto& tech_name = progress.first;
309         const Tech* tech = GetTech(tech_name);
310         if (!tech)
311             continue;
312 
313         if (!m_research_queue.InQueue(tech_name))
314             continue;
315 
316         float rp_spent = progress.second;
317         float rp_total_cost = tech->ResearchCost(m_id);
318         float rp_left = std::max(0.0f, rp_total_cost - rp_spent);
319 
320         if (rp_left > most_left) {
321             best_progress = &progress;
322             most_left = rp_left;
323         }
324     }
325 
326     if (best_progress)
327         return best_progress->first;
328     return EMPTY_STRING;
329 }
330 
TopPriorityResearchableTech() const331 const std::string& Empire::TopPriorityResearchableTech() const {
332     if (m_research_queue.empty())
333         return EMPTY_STRING;
334     for (const auto& elem : m_research_queue) {
335         if (this->ResearchableTech(elem.name))
336             return elem.name;
337     }
338     return EMPTY_STRING;
339 }
340 
MostExpensiveResearchableTech() const341 const std::string& Empire::MostExpensiveResearchableTech() const {
342     return EMPTY_STRING;    // TODO: IMPLEMENT THIS
343 }
344 
LeastExpensiveResearchableTech() const345 const std::string& Empire::LeastExpensiveResearchableTech() const {
346     return EMPTY_STRING;    // TODO: IMPLEMENT THIS
347 }
348 
MostRPSpentResearchableTech() const349 const std::string& Empire::MostRPSpentResearchableTech() const {
350     return EMPTY_STRING;    // TODO: IMPLEMENT THIS
351 }
352 
MostRPCostLeftResearchableTech() const353 const std::string& Empire::MostRPCostLeftResearchableTech() const {
354     return EMPTY_STRING;    // TODO: IMPLEMENT THIS
355 }
356 
AvailableBuildingTypes() const357 const std::set<std::string>& Empire::AvailableBuildingTypes() const
358 { return m_available_building_types; }
359 
BuildingTypeAvailable(const std::string & name) const360 bool Empire::BuildingTypeAvailable(const std::string& name) const
361 { return m_available_building_types.count(name); }
362 
ShipDesigns() const363 const std::set<int>& Empire::ShipDesigns() const
364 { return m_known_ship_designs; }
365 
AvailableShipDesigns() const366 std::set<int> Empire::AvailableShipDesigns() const {
367     // create new map containing all ship designs that are available
368     std::set<int> retval;
369     for (int design_id : m_known_ship_designs) {
370         if (ShipDesignAvailable(design_id))
371             retval.insert(design_id);
372     }
373     return retval;
374 }
375 
ShipDesignAvailable(int ship_design_id) const376 bool Empire::ShipDesignAvailable(int ship_design_id) const {
377     const ShipDesign* design = GetShipDesign(ship_design_id);
378     return design ? ShipDesignAvailable(*design) : false;
379 }
380 
ShipDesignAvailable(const ShipDesign & design) const381 bool Empire::ShipDesignAvailable(const ShipDesign& design) const {
382     if (!design.Producible()) return false;
383 
384     // design is kept, but still need to verify that it is buildable at this time.  Part or hull tech
385     // requirements might prevent it from being built.
386     for (const auto& name : design.Parts()) {
387         if (name.empty())
388             continue;   // empty slot can't be unavailable
389         if (!ShipPartAvailable(name))
390             return false;
391     }
392     if (!ShipHullAvailable(design.Hull()))
393         return false;
394 
395     // if there are no reasons the design isn't available, then by default it is available
396     return true;
397 }
398 
ShipDesignKept(int ship_design_id) const399 bool Empire::ShipDesignKept(int ship_design_id) const
400 { return m_known_ship_designs.count(ship_design_id); }
401 
AvailableShipParts() const402 const std::set<std::string>& Empire::AvailableShipParts() const
403 { return m_available_ship_parts; }
404 
ShipPartAvailable(const std::string & name) const405 bool Empire::ShipPartAvailable(const std::string& name) const
406 { return m_available_ship_parts.count(name); }
407 
AvailableShipHulls() const408 const std::set<std::string>& Empire::AvailableShipHulls() const
409 { return m_available_ship_hulls; }
410 
ShipHullAvailable(const std::string & name) const411 bool Empire::ShipHullAvailable(const std::string& name) const
412 { return m_available_ship_hulls.count(name); }
413 
GetProductionQueue() const414 const ProductionQueue& Empire::GetProductionQueue() const
415 { return m_production_queue; }
416 
ProductionStatus(int i) const417 float Empire::ProductionStatus(int i) const {
418     if (0 > i || i >= static_cast<int>(m_production_queue.size()))
419         return -1.0f;
420     float item_progress = m_production_queue[i].progress;
421     float item_cost;
422     int item_time;
423     std::tie(item_cost, item_time) = this->ProductionCostAndTime(m_production_queue[i]);
424     return item_progress * item_cost * m_production_queue[i].blocksize;
425 }
426 
ProductionCostAndTime(const ProductionQueue::Element & element) const427 std::pair<float, int> Empire::ProductionCostAndTime(const ProductionQueue::Element& element) const
428 { return ProductionCostAndTime(element.item, element.location); }
429 
ProductionCostAndTime(const ProductionQueue::ProductionItem & item,int location_id) const430 std::pair<float, int> Empire::ProductionCostAndTime(const ProductionQueue::ProductionItem& item,
431                                                     int location_id) const
432 {
433     if (item.build_type == BT_BUILDING) {
434         const BuildingType* type = GetBuildingType(item.name);
435         if (!type)
436             return std::make_pair(-1.0, -1);
437         return std::make_pair(type->ProductionCost(m_id, location_id),
438                               type->ProductionTime(m_id, location_id));
439 
440     } else if (item.build_type == BT_SHIP) {
441         const ShipDesign* design = GetShipDesign(item.design_id);
442         if (design)
443             return std::make_pair(design->ProductionCost(m_id, location_id),
444                                   design->ProductionTime(m_id, location_id));
445         return std::make_pair(-1.0, -1);
446 
447     } else if (item.build_type == BT_STOCKPILE) {
448         return std::make_pair(1.0, 1);
449     }
450     ErrorLogger() << "Empire::ProductionCostAndTime was passed a ProductionItem with an invalid BuildType";
451     return std::make_pair(-1.0, -1);
452 }
453 
HasExploredSystem(int ID) const454 bool Empire::HasExploredSystem(int ID) const
455 { return m_explored_systems.count(ID); }
456 
ProducibleItem(BuildType build_type,int location_id) const457 bool Empire::ProducibleItem(BuildType build_type, int location_id) const {
458     if (build_type == BT_SHIP)
459         throw std::invalid_argument("Empire::ProducibleItem was passed BuildType BT_SHIP with no further parameters, but ship designs are tracked by number");
460 
461     if (build_type == BT_BUILDING)
462         throw std::invalid_argument("Empire::ProducibleItem was passed BuildType BT_BUILDING with no further parameters, but buildings are tracked by name");
463 
464     if (location_id == INVALID_OBJECT_ID)
465         return false;
466 
467     // must own the production location...
468     auto location = Objects().get(location_id);
469     if (!location) {
470         WarnLogger() << "Empire::ProducibleItem for BT_STOCKPILE unable to get location object with id " << location_id;
471         return false;
472     }
473 
474     if (!location->OwnedBy(m_id))
475         return false;
476 
477     if (!std::dynamic_pointer_cast<const ResourceCenter>(location))
478         return false;
479 
480     if (build_type == BT_STOCKPILE) {
481         return true;
482 
483     } else {
484         ErrorLogger() << "Empire::ProducibleItem was passed an invalid BuildType";
485         return false;
486     }
487 
488 }
489 
ProducibleItem(BuildType build_type,const std::string & name,int location) const490 bool Empire::ProducibleItem(BuildType build_type, const std::string& name, int location) const {
491     // special case to check for ships being passed with names, not design ids
492     if (build_type == BT_SHIP)
493         throw std::invalid_argument("Empire::ProducibleItem was passed BuildType BT_SHIP with a name, but ship designs are tracked by number");
494 
495     if (build_type == BT_STOCKPILE)
496         throw std::invalid_argument("Empire::ProducibleItem was passed BuildType BT_STOCKPILE with a name, but the stockpile does not need an identification");
497 
498     if (build_type == BT_BUILDING && !BuildingTypeAvailable(name))
499         return false;
500 
501     const auto* building_type = GetBuildingType(name);
502     if (!building_type || !building_type->Producible())
503         return false;
504 
505     auto build_location = Objects().get(location);
506     if (!build_location)
507         return false;
508 
509     if (build_type == BT_BUILDING) {
510         // specified location must be a valid production location for that building type
511         return building_type->ProductionLocation(m_id, location);
512 
513     } else {
514         ErrorLogger() << "Empire::ProducibleItem was passed an invalid BuildType";
515         return false;
516     }
517 }
518 
ProducibleItem(BuildType build_type,int design_id,int location) const519 bool Empire::ProducibleItem(BuildType build_type, int design_id, int location) const {
520     // special case to check for buildings being passed with ids, not names
521     if (build_type == BT_BUILDING)
522         throw std::invalid_argument("Empire::ProducibleItem was passed BuildType BT_BUILDING with a design id number, but buildings are tracked by name");
523 
524     if (build_type == BT_STOCKPILE)
525         throw std::invalid_argument("Empire::ProducibleItem was passed BuildType BT_STOCKPILE with a design id, but the stockpile does not need an identification");
526 
527     if (build_type == BT_SHIP && !ShipDesignAvailable(design_id))
528         return false;
529 
530     // design must be known to this empire
531     const ShipDesign* ship_design = GetShipDesign(design_id);
532     if (!ship_design || !ship_design->Producible())
533         return false;
534 
535     auto build_location = Objects().get(location);
536     if (!build_location) return false;
537 
538     if (build_type == BT_SHIP) {
539         // specified location must be a valid production location for this design
540         return ship_design->ProductionLocation(m_id, location);
541 
542     } else {
543         ErrorLogger() << "Empire::ProducibleItem was passed an invalid BuildType";
544         return false;
545     }
546 }
547 
ProducibleItem(const ProductionQueue::ProductionItem & item,int location) const548 bool Empire::ProducibleItem(const ProductionQueue::ProductionItem& item, int location) const {
549     if (item.build_type == BT_BUILDING)
550         return ProducibleItem(item.build_type, item.name, location);
551     else if (item.build_type == BT_SHIP)
552         return ProducibleItem(item.build_type, item.design_id, location);
553     else if (item.build_type == BT_STOCKPILE)
554         return ProducibleItem(item.build_type, location);
555     else
556         throw std::invalid_argument("Empire::ProducibleItem was passed a ProductionItem with an invalid BuildType");
557     return false;
558 }
559 
EnqueuableItem(BuildType build_type,const std::string & name,int location) const560 bool Empire::EnqueuableItem(BuildType build_type, const std::string& name, int location) const {
561     if (build_type != BT_BUILDING)
562         return false;
563 
564     const auto* building_type = GetBuildingType(name);
565     if (!building_type || !building_type->Producible())
566         return false;
567 
568     auto build_location = Objects().get(location);
569     if (!build_location)
570         return false;
571 
572     // specified location must be a valid production location for that building type
573     return building_type->EnqueueLocation(m_id, location);
574 }
575 
EnqueuableItem(const ProductionQueue::ProductionItem & item,int location) const576 bool Empire::EnqueuableItem(const ProductionQueue::ProductionItem& item, int location) const {
577     if (item.build_type == BT_BUILDING)
578         return EnqueuableItem(item.build_type, item.name, location);
579     else if (item.build_type == BT_SHIP)    // ships don't have a distinction between enqueuable and producible
580         return ProducibleItem(item.build_type, item.design_id, location);
581     else if (item.build_type == BT_STOCKPILE) // stockpile does not have a distinction between enqueuable and producible
582         return ProducibleItem(item.build_type, location);
583     else
584         throw std::invalid_argument("Empire::ProducibleItem was passed a ProductionItem with an invalid BuildType");
585     return false;
586 }
587 
588 
NumSitRepEntries(int turn) const589 int Empire::NumSitRepEntries(int turn/* = INVALID_GAME_TURN*/) const {
590     if (turn == INVALID_GAME_TURN)
591         return m_sitrep_entries.size();
592     int count = 0;
593     for (const SitRepEntry& sitrep : m_sitrep_entries)
594         if (sitrep.GetTurn() == turn)
595             count++;
596     return count;
597 }
598 
Eliminated() const599 bool Empire::Eliminated() const {
600     return m_eliminated;
601 }
602 
Eliminate()603 void Empire::Eliminate() {
604     m_eliminated = true;
605 
606     for (auto& entry : Empires())
607         entry.second->AddSitRepEntry(CreateEmpireEliminatedSitRep(EmpireID()));
608 
609     // some Empire data not cleared when eliminating since it might be useful
610     // to remember later, and having it doesn't hurt anything (as opposed to
611     // the production queue that might actually cause some problems if left
612     // uncleared after elimination
613 
614     m_capital_id = INVALID_OBJECT_ID;
615     // m_newly_researched_techs
616     // m_techs
617     m_research_queue.clear();
618     m_research_progress.clear();
619     m_production_queue.clear();
620     // m_available_building_types;
621     // m_available_ship_parts;
622     // m_available_ship_hulls;
623     // m_explored_systems;
624     // m_known_ship_designs;
625     m_sitrep_entries.clear();
626     for (auto& entry : m_resource_pools)
627         entry.second->SetObjects(std::vector<int>());
628     m_population_pool.SetPopCenters(std::vector<int>());
629 
630     // m_ship_names_used;
631     m_supply_system_ranges.clear();
632     m_supply_unobstructed_systems.clear();
633 }
634 
Won() const635 bool Empire::Won() const {
636     return !m_victories.empty();
637 }
638 
Win(const std::string & reason)639 void Empire::Win(const std::string& reason) {
640     if (m_victories.insert(reason).second) {
641         for (auto& entry : Empires()) {
642             entry.second->AddSitRepEntry(CreateVictorySitRep(reason, EmpireID()));
643         }
644     }
645 }
646 
Ready() const647 bool Empire::Ready() const
648 { return m_ready; }
649 
SetReady(bool ready)650 void Empire::SetReady(bool ready)
651 { m_ready = ready; }
652 
UpdateSystemSupplyRanges(const std::set<int> & known_objects)653 void Empire::UpdateSystemSupplyRanges(const std::set<int>& known_objects) {
654     //std::cout << "Empire::UpdateSystemSupplyRanges() for empire " << this->Name() << std::endl;
655     m_supply_system_ranges.clear();
656 
657     // as of this writing, only planets can generate supply propagation
658     std::vector<std::shared_ptr<const UniverseObject>> owned_planets;
659     for (const auto& planet: Objects().find<Planet>(known_objects)) {
660         if (!planet)
661             continue;
662         if (planet->OwnedBy(this->EmpireID()))
663             owned_planets.push_back(planet);
664     }
665 
666     //std::cout << "... empire owns " << owned_planets.size() << " planets" << std::endl;
667     for (auto& obj : owned_planets) {
668         //std::cout << "... considering owned planet: " << obj->Name() << std::endl;
669 
670         // ensure object is within a system, from which it can distribute supplies
671         int system_id = obj->SystemID();
672         if (system_id == INVALID_OBJECT_ID)
673             continue;   // TODO: consider future special case if current object is itself a system
674 
675         // check if object has a supply meter
676         if (obj->GetMeter(METER_SUPPLY)) {
677             // get resource supply range for next turn for this object
678             float supply_range = obj->GetMeter(METER_SUPPLY)->Initial();
679 
680             // if this object can provide more supply range than the best previously checked object in this system, record its range as the new best for the system
681             auto system_it = m_supply_system_ranges.find(system_id);  // try to find a previous entry for this system's supply range
682             if (system_it == m_supply_system_ranges.end() || supply_range > system_it->second) {// if there is no previous entry, or the previous entry is shorter than the new one, add or replace the entry
683                 //std::cout << " ... object " << obj->Name() << " has resource supply range: " << resource_supply_range << std::endl;
684                 m_supply_system_ranges[system_id] = supply_range;
685             }
686         }
687     }
688 }
689 
UpdateSystemSupplyRanges()690 void Empire::UpdateSystemSupplyRanges() {
691     const Universe& universe = GetUniverse();
692     const ObjectMap& empire_known_objects = EmpireKnownObjects(this->EmpireID());
693 
694     // get ids of objects partially or better visible to this empire.
695     const std::set<int>& known_destroyed_objects = universe.EmpireKnownDestroyedObjectIDs(this->EmpireID());
696 
697     std::set<int> known_objects_set;
698 
699     // exclude objects known to have been destroyed (or rather, include ones that aren't known by this empire to be destroyed)
700     for (const auto& obj : empire_known_objects.all())
701         if (!known_destroyed_objects.count(obj->ID()))
702             known_objects_set.insert(obj->ID());
703     UpdateSystemSupplyRanges(known_objects_set);
704 }
705 
UpdateUnobstructedFleets()706 void Empire::UpdateUnobstructedFleets() {
707     const std::set<int>& known_destroyed_objects =
708         GetUniverse().EmpireKnownDestroyedObjectIDs(this->EmpireID());
709 
710     for (const auto& system : Objects().find<System>(m_supply_unobstructed_systems)) {
711         if (!system)
712             continue;
713 
714         for (auto& fleet : Objects().find<Fleet>(system->FleetIDs())) {
715             if (known_destroyed_objects.count(fleet->ID()))
716                 continue;
717             if (fleet->OwnedBy(m_id))
718                 fleet->SetArrivalStarlane(system->ID());
719         }
720     }
721 }
722 
UpdateSupplyUnobstructedSystems(bool precombat)723 void Empire::UpdateSupplyUnobstructedSystems(bool precombat /*=false*/) {
724     Universe& universe = GetUniverse();
725 
726     // get ids of systems partially or better visible to this empire.
727     // TODO: make a UniverseObjectVisitor for objects visible to an empire at a specified visibility or greater
728     const std::set<int>& known_destroyed_objects = universe.EmpireKnownDestroyedObjectIDs(this->EmpireID());
729 
730     std::set<int> known_systems_set;
731 
732     // exclude systems known to have been destroyed (or rather, include ones that aren't known to be destroyed)
733     for (const auto& sys : EmpireKnownObjects(this->EmpireID()).all<System>())
734         if (!known_destroyed_objects.count(sys->ID()))
735             known_systems_set.insert(sys->ID());
736     UpdateSupplyUnobstructedSystems(known_systems_set, precombat);
737 }
738 
UpdateSupplyUnobstructedSystems(const std::set<int> & known_systems,bool precombat)739 void Empire::UpdateSupplyUnobstructedSystems(const std::set<int>& known_systems, bool precombat /*=false*/) {
740     TraceLogger(supply) << "UpdateSupplyUnobstructedSystems (allowing supply propagation) for empire " << m_id;
741     m_supply_unobstructed_systems.clear();
742 
743     // get systems with historically at least partial visibility
744     std::set<int> systems_with_at_least_partial_visibility_at_some_point;
745     for (int system_id : known_systems) {
746         const auto& vis_turns = GetUniverse().GetObjectVisibilityTurnMapByEmpire(system_id, m_id);
747         if (vis_turns.count(VIS_PARTIAL_VISIBILITY))
748             systems_with_at_least_partial_visibility_at_some_point.insert(system_id);
749     }
750 
751     // get all fleets, or just those visible to this client's empire
752     const auto& known_destroyed_objects = GetUniverse().EmpireKnownDestroyedObjectIDs(this->EmpireID());
753 
754     // get empire supply ranges
755     std::map<int, std::map<int, float>> empire_system_supply_ranges;
756     for (const auto& entry : Empires()) {
757         const Empire* empire = entry.second;
758         empire_system_supply_ranges[entry.first] = empire->SystemSupplyRanges();
759     }
760 
761     // find systems that contain fleets that can either maintain supply or block supply.
762     // to affect supply in either manner, a fleet must be armed & aggressive, & must be not
763     // trying to depart the systme.  Qualifying enemy fleets will blockade if no friendly fleets
764     // are present, or if the friendly fleets were already blockade-restricted and the enemy
765     // fleets were not (meaning that the enemy fleets were continuing an existing blockade)
766     // Friendly fleets can preserve available starlane accesss even if they are trying to leave the system
767 
768     // Unrestricted lane access (i.e, (fleet->ArrivalStarlane() == system->ID()) ) is used as a proxy for
769     // order of arrival -- if an enemy has unrestricted lane access and you don't, they must have arrived
770     // before you, or be in cahoots with someone who did.
771     std::set<int> systems_containing_friendly_fleets;
772     std::set<int> systems_with_lane_preserving_fleets;
773     std::set<int> unrestricted_friendly_systems;
774     std::set<int> systems_containing_obstructing_objects;
775     std::set<int> unrestricted_obstruction_systems;
776     for (auto& fleet : GetUniverse().Objects().all<Fleet>()) {
777         int system_id = fleet->SystemID();
778         if (system_id == INVALID_OBJECT_ID) {
779             continue;   // not in a system, so can't affect system obstruction
780         } else if (known_destroyed_objects.count(fleet->ID())) {
781             continue; //known to be destroyed so can't affect supply, important just in case being updated on client side
782         }
783 
784         TraceLogger(supply) << "Fleet " << fleet->ID() << " is in system " << system_id << " with next system " << fleet->NextSystemID() << " and is owned by " << fleet->Owner() << " armed: " << fleet->HasArmedShips() << " and agressive: " << fleet->Aggressive();
785         if (fleet->HasArmedShips() && fleet->Aggressive()) {
786             if (fleet->OwnedBy(m_id)) {
787                 if (fleet->NextSystemID() == INVALID_OBJECT_ID || fleet->NextSystemID() == fleet->SystemID()) {
788                     systems_containing_friendly_fleets.insert(system_id);
789                     if (fleet->ArrivalStarlane() == system_id)
790                         unrestricted_friendly_systems.insert(system_id);
791                     else
792                         systems_with_lane_preserving_fleets.insert(system_id);
793                 }
794             } else if (fleet->NextSystemID() == INVALID_OBJECT_ID || fleet->NextSystemID() == fleet->SystemID()) {
795                 int fleet_owner = fleet->Owner();
796                 bool fleet_at_war = fleet_owner == ALL_EMPIRES || Empires().GetDiplomaticStatus(m_id, fleet_owner) == DIPLO_WAR;
797                 // newly created ships are not allowed to block supply since they have not even potentially gone
798                 // through a combat round at the present location.  Potential sources for such new ships are monsters
799                 // created via Effect.  (Ships/fleets constructed by empires are currently created at a later stage of
800                 // turn processing, but even if such were moved forward they should be similarly restricted.)  For
801                 // checks after combat and prior to turn advancement, we check against age zero here.  For checks
802                 // after turn advancement but prior to combat we check against age 1.  Because the
803                 // fleets themselves may be created and/or destroyed purely as organizational matters, we check ship
804                 // age not fleet age.
805                 int cutoff_age = precombat ? 1 : 0;
806                 if (fleet_at_war && fleet->MaxShipAgeInTurns() > cutoff_age) {
807                     systems_containing_obstructing_objects.insert(system_id);
808                     if (fleet->ArrivalStarlane() == system_id)
809                         unrestricted_obstruction_systems.insert(system_id);
810                 }
811             }
812         }
813     }
814 
815     TraceLogger(supply) << "Empire::UpdateSupplyUnobstructedSystems systems with obstructing objects for empire " << m_id << " : " << [&]() {
816         std::stringstream ss;
817         for (int obj_id : systems_containing_obstructing_objects)
818         { ss << obj_id << ", "; }
819         return ss.str();
820     }();
821 
822     DebugLogger() << "Preserved System-Lanes for empire " << m_name << " (" << m_id << ") : " << [&]() {
823         std::stringstream ss2;
824         for (auto sys_lanes : m_preserved_system_exit_lanes) {
825             ss2 << "[Sys: " << sys_lanes.first << " : (";
826             for (auto lane : sys_lanes.second)
827             { ss2 << lane << " "; }
828             ss2 << ")]  ";
829         }
830         return ss2.str();
831     }();
832 
833     DebugLogger() << "Systems with lane-preserving fleets for empire " << m_name << " (" << m_id << ") : " << [&]() {
834         std::stringstream ss3;
835         for (auto sys_id : systems_with_lane_preserving_fleets)
836         { ss3 << sys_id << ", "; }
837         return ss3.str();
838     }();
839 
840 
841     // check each potential supplyable system for whether it can propagate supply.
842     for (const auto& sys : Objects().find<System>(known_systems)) {
843         if (!sys)
844             continue;
845 
846         // has empire ever seen this system with partial or better visibility?
847         if (!systems_with_at_least_partial_visibility_at_some_point.count(sys->ID())) {
848             TraceLogger(supply) << "System " << sys->Name() << " (" << sys->ID() << ") has never been seen";
849             continue;
850         }
851 
852         // if system is explored, then whether it can propagate supply depends
853         // on what friendly / enemy ships and planets are in the system
854 
855         if (unrestricted_friendly_systems.count(sys->ID())) {
856             // in unrestricted friendly systems, supply can propagate
857             m_supply_unobstructed_systems.insert(sys->ID());
858             TraceLogger(supply) << "System " << sys->Name() << " (" << sys->ID() << ") +++ is unrestricted and friendly";
859 
860         } else if (systems_containing_friendly_fleets.count(sys->ID())) {
861             // if there are unrestricted friendly ships, and no unrestricted enemy fleets, supply can propagate
862             if (!unrestricted_obstruction_systems.count(sys->ID())) {
863                 m_supply_unobstructed_systems.insert(sys->ID());
864                 TraceLogger(supply) << "System " << sys->Name() << " (" << sys->ID() << ") +++ has friendly fleets and no obstructions";
865             } else {
866                 TraceLogger(supply) << "System " << sys->Name() << " (" << sys->ID() << ") --- is has friendly fleets but has obstructions";
867             }
868 
869         } else if (!systems_containing_obstructing_objects.count(sys->ID())) {
870             // if there are no friendly fleets or obstructing enemy fleets, supply can propagate
871             m_supply_unobstructed_systems.insert(sys->ID());
872             TraceLogger(supply) << "System " << sys->Name() << " (" << sys->ID() << ") +++ has no obstructing objects";
873 
874         } else if (!systems_with_lane_preserving_fleets.count(sys->ID())) {
875             // if there are obstructing enemy fleets but no friendly fleets that could maintain
876             // lane access, supply cannot propagate and this empire's available system exit
877             TraceLogger(supply) << "System " << sys->Name() << " (" << sys->ID() << ") --- has no lane preserving fleets";
878 
879             // lanes for this system are cleared
880             if (!m_preserved_system_exit_lanes[sys->ID()].empty()) {
881                 std::stringstream ssca;
882                 ssca << "Empire::UpdateSupplyUnobstructedSystems clearing preserved lanes for system ("
883                      << sys->ID() << "); available lanes were:";
884                 for (int system_id : m_preserved_system_exit_lanes[sys->ID()])
885                     ssca << system_id << ", ";
886                 TraceLogger(supply) << ssca.str();
887             }
888             m_preserved_system_exit_lanes[sys->ID()].clear();
889 
890         } else {
891             TraceLogger(supply) << "Empire::UpdateSupplyUnobstructedSystems : Restricted system " << sys->ID() << " with no friendly fleets, no obustrcting enemy fleets, and no lane-preserving fleets";
892         }
893     }
894 }
895 
RecordPendingLaneUpdate(int start_system_id,int dest_system_id)896 void Empire::RecordPendingLaneUpdate(int start_system_id, int dest_system_id) {
897     if (!m_supply_unobstructed_systems.count(start_system_id))
898         m_pending_system_exit_lanes[start_system_id].insert(dest_system_id);
899     else { // if the system is unobstructed, mark all its lanes as avilable
900         for (const auto& lane : Objects().get<System>(start_system_id)->StarlanesWormholes()) {
901             m_pending_system_exit_lanes[start_system_id].insert(lane.first); // will add both starlanes and wormholes
902         }
903     }
904 }
905 
UpdatePreservedLanes()906 void Empire::UpdatePreservedLanes() {
907     for (auto& system : m_pending_system_exit_lanes) {
908         m_preserved_system_exit_lanes[system.first].insert(system.second.begin(), system.second.end());
909         system.second.clear();
910     }
911     m_pending_system_exit_lanes.clear(); // TODO: consider: not really necessary, & may be more efficient to not clear.
912 }
913 
SystemSupplyRanges() const914 const std::map<int, float>& Empire::SystemSupplyRanges() const
915 { return m_supply_system_ranges; }
916 
SupplyUnobstructedSystems() const917 const std::set<int>& Empire::SupplyUnobstructedSystems() const
918 { return m_supply_unobstructed_systems; }
919 
PreservedLaneTravel(int start_system_id,int dest_system_id) const920 const bool Empire::PreservedLaneTravel(int start_system_id, int dest_system_id) const {
921     auto find_it = m_preserved_system_exit_lanes.find(start_system_id);
922     return find_it != m_preserved_system_exit_lanes.end()
923             && find_it->second.count(dest_system_id);
924 }
925 
ExploredSystems() const926 const std::set<int>& Empire::ExploredSystems() const
927 { return m_explored_systems; }
928 
KnownStarlanes() const929 const std::map<int, std::set<int>> Empire::KnownStarlanes() const {
930     // compile starlanes leading into or out of each system
931     std::map<int, std::set<int>> retval;
932 
933     const Universe& universe = GetUniverse();
934     TraceLogger(supply) << "Empire::KnownStarlanes for empire " << m_id;
935 
936     const std::set<int>& known_destroyed_objects = universe.EmpireKnownDestroyedObjectIDs(this->EmpireID());
937     for (const auto& sys : Objects().all<System>())
938     {
939         int start_id = sys->ID();
940         TraceLogger(supply) << "system " << start_id << " has up to " << sys->StarlanesWormholes().size() << " lanes / wormholes";
941 
942         // exclude lanes starting at systems known to be destroyed
943         if (known_destroyed_objects.count(start_id)) {
944             TraceLogger(supply) << "system " << start_id << " known destroyed, so lanes from it are unknown";
945             continue;
946         }
947 
948         for (const auto& lane : sys->StarlanesWormholes()) {
949             int end_id = lane.first;
950             bool is_wormhole = lane.second;
951             if (is_wormhole || known_destroyed_objects.count(end_id))
952                 continue;   // is a wormhole, not a starlane, or is connected to a known destroyed system
953             retval[start_id].insert(end_id);
954             retval[end_id].insert(start_id);
955         }
956 
957         TraceLogger(supply) << "system " << start_id << " had " << retval[start_id].size() << " known lanes";
958     }
959 
960     TraceLogger(supply) << "Total of " << retval.size() << " systems had known lanes";
961     return retval;
962 }
963 
VisibleStarlanes() const964 const std::map<int, std::set<int>> Empire::VisibleStarlanes() const {
965     std::map<int, std::set<int>> retval;   // compile starlanes leading into or out of each system
966 
967     const Universe& universe = GetUniverse();
968     const ObjectMap& objects = universe.Objects();
969 
970     for (const auto& sys : objects.all<System>())
971     {
972         int start_id = sys->ID();
973 
974         // is system visible to this empire?
975         if (universe.GetObjectVisibilityByEmpire(start_id, m_id) <= VIS_NO_VISIBILITY)
976             continue;
977 
978         // get system's visible lanes for this empire
979         for (auto& lane : sys->VisibleStarlanesWormholes(m_id)) {
980             if (lane.second)
981                 continue;   // is a wormhole, not a starlane
982             int end_id = lane.first;
983             retval[start_id].insert(end_id);
984             retval[end_id].insert(start_id);
985         }
986     }
987 
988     return retval;
989 }
990 
SitRepBegin() const991 Empire::SitRepItr Empire::SitRepBegin() const
992 { return m_sitrep_entries.begin(); }
993 
SitRepEnd() const994 Empire::SitRepItr Empire::SitRepEnd() const
995 { return m_sitrep_entries.end(); }
996 
ProductionPoints() const997 float Empire::ProductionPoints() const
998 { return GetResourcePool(RE_INDUSTRY)->TotalOutput(); }
999 
GetResourcePool(ResourceType resource_type) const1000 const std::shared_ptr<ResourcePool> Empire::GetResourcePool(ResourceType resource_type) const {
1001     auto it = m_resource_pools.find(resource_type);
1002     if (it == m_resource_pools.end())
1003         return nullptr;
1004     return it->second;
1005 }
1006 
ResourceStockpile(ResourceType type) const1007 float Empire::ResourceStockpile(ResourceType type) const {
1008     auto it = m_resource_pools.find(type);
1009     if (it == m_resource_pools.end())
1010         throw std::invalid_argument("Empire::ResourceStockpile passed invalid ResourceType");
1011     return it->second->Stockpile();
1012 }
1013 
ResourceOutput(ResourceType type) const1014 float Empire::ResourceOutput(ResourceType type) const {
1015     auto it = m_resource_pools.find(type);
1016     if (it == m_resource_pools.end())
1017         throw std::invalid_argument("Empire::ResourceOutput passed invalid ResourceType");
1018     return it->second->TotalOutput();
1019 }
1020 
ResourceAvailable(ResourceType type) const1021 float Empire::ResourceAvailable(ResourceType type) const {
1022     auto it = m_resource_pools.find(type);
1023     if (it == m_resource_pools.end())
1024         throw std::invalid_argument("Empire::ResourceAvailable passed invalid ResourceType");
1025     return it->second->TotalAvailable();
1026 }
1027 
GetPopulationPool() const1028 const PopulationPool& Empire::GetPopulationPool() const
1029 { return m_population_pool; }
1030 
Population() const1031 float Empire::Population() const
1032 { return m_population_pool.Population(); }
1033 
SetResourceStockpile(ResourceType resource_type,float stockpile)1034 void Empire::SetResourceStockpile(ResourceType resource_type, float stockpile) {
1035     auto it = m_resource_pools.find(resource_type);
1036     if (it == m_resource_pools.end())
1037         throw std::invalid_argument("Empire::SetResourceStockpile passed invalid ResourceType");
1038     return it->second->SetStockpile(stockpile);
1039 }
1040 
PlaceTechInQueue(const std::string & name,int pos)1041 void Empire::PlaceTechInQueue(const std::string& name, int pos/* = -1*/) {
1042     // do not add tech that is already researched
1043     if (name.empty() || TechResearched(name) || m_techs.count(name) || m_newly_researched_techs.count(name))
1044         return;
1045     const Tech* tech = GetTech(name);
1046     if (!tech || !tech->Researchable())
1047         return;
1048 
1049     auto it = m_research_queue.find(name);
1050 
1051     if (pos < 0 || static_cast<int>(m_research_queue.size()) <= pos) {
1052         // default to putting at end
1053         bool paused = false;
1054         if (it != m_research_queue.end()) {
1055             paused = it->paused;
1056             m_research_queue.erase(it);
1057         }
1058         m_research_queue.push_back(name, paused);
1059     } else {
1060         // put at requested position
1061         if (it < m_research_queue.begin() + pos)
1062             --pos;
1063         bool paused = false;
1064         if (it != m_research_queue.end()) {
1065             paused = it->paused;
1066             m_research_queue.erase(it);
1067         }
1068         m_research_queue.insert(m_research_queue.begin() + pos, name, paused);
1069     }
1070 }
1071 
RemoveTechFromQueue(const std::string & name)1072 void Empire::RemoveTechFromQueue(const std::string& name) {
1073     auto it = m_research_queue.find(name);
1074     if (it != m_research_queue.end())
1075         m_research_queue.erase(it);
1076 }
1077 
PauseResearch(const std::string & name)1078 void Empire::PauseResearch(const std::string& name) {
1079     auto it = m_research_queue.find(name);
1080     if (it != m_research_queue.end())
1081         it->paused = true;
1082 }
1083 
ResumeResearch(const std::string & name)1084 void Empire::ResumeResearch(const std::string& name){
1085     auto it = m_research_queue.find(name);
1086     if (it != m_research_queue.end())
1087         it->paused = false;
1088 }
1089 
SetTechResearchProgress(const std::string & name,float progress)1090 void Empire::SetTechResearchProgress(const std::string& name, float progress) {
1091     const Tech* tech = GetTech(name);
1092     if (!tech) {
1093         ErrorLogger() << "Empire::SetTechResearchProgress no such tech as: " << name;
1094         return;
1095     }
1096     if (TechResearched(name))
1097         return; // can't affect already-researched tech
1098 
1099     // set progress
1100     float clamped_progress = std::min(1.0f, std::max(0.0f, progress));
1101     m_research_progress[name] = clamped_progress;
1102 
1103     // if tech is complete, ensure it is on the queue, so it will be researched next turn
1104     if (clamped_progress >= tech->ResearchCost(m_id) &&
1105             !m_research_queue.InQueue(name))
1106         m_research_queue.push_back(name);
1107 
1108     // don't just give tech to empire, as another effect might reduce its progress before end of turn
1109 }
1110 
1111 const unsigned int MAX_PROD_QUEUE_SIZE = 500;
1112 
PlaceProductionOnQueue(const ProductionQueue::ProductionItem & item,boost::uuids::uuid uuid,int number,int blocksize,int location,int pos)1113 void Empire::PlaceProductionOnQueue(const ProductionQueue::ProductionItem& item,
1114                                     boost::uuids::uuid uuid, int number,
1115                                     int blocksize, int location, int pos/* = -1*/)
1116 {
1117     if (m_production_queue.size() >= MAX_PROD_QUEUE_SIZE) {
1118         ErrorLogger() << "Empire::PlaceProductionOnQueue() : Maximum queue size reached. Aborting enqueue";
1119         return;
1120     }
1121 
1122     if (item.build_type == BT_BUILDING) {
1123         // only buildings have a distinction between enqueuable and producible...
1124         if (!EnqueuableItem(BT_BUILDING, item.name, location)) {
1125             ErrorLogger() << "Empire::PlaceProductionOnQueue() : Attempted to place non-enqueuable item in queue: build_type: Building"
1126                           << "  name: " << item.name << "  location: " << location;
1127             return;
1128         }
1129         if (!ProducibleItem(BT_BUILDING, item.name, location)) {
1130             ErrorLogger() << "Empire::PlaceProductionOnQueue() : Placed a non-buildable item in queue: build_type: Building"
1131                           << "  name: " << item.name << "  location: " << location;
1132             return;
1133         }
1134 
1135     } else if (item.build_type == BT_SHIP) {
1136         if (!ProducibleItem(BT_SHIP, item.design_id, location)) {
1137             ErrorLogger() << "Empire::PlaceProductionOnQueue() : Placed a non-buildable item in queue: build_type: Ship"
1138                           << "  design_id: " << item.design_id << "  location: " << location;
1139             return;
1140         }
1141 
1142     } else if (item.build_type == BT_STOCKPILE) {
1143         if (!ProducibleItem(BT_STOCKPILE, location)) {
1144             ErrorLogger() << "Empire::PlaceProductionOnQueue() : Placed a non-buildable item in queue: build_type: Stockpile"
1145                           << "  location: " << location;
1146             return;
1147         }
1148 
1149     } else {
1150         throw std::invalid_argument("Empire::PlaceProductionOnQueue was passed a ProductionQueue::ProductionItem with an invalid BuildType");
1151     }
1152 
1153     ProductionQueue::Element elem{item, m_id, uuid, number, number, blocksize,
1154                                   location, false, item.build_type != BT_STOCKPILE};
1155     if (pos < 0 || static_cast<int>(m_production_queue.size()) <= pos)
1156         m_production_queue.push_back(elem);
1157     else
1158         m_production_queue.insert(m_production_queue.begin() + pos, elem);
1159 }
1160 
SetProductionQuantityAndBlocksize(int index,int quantity,int blocksize)1161 void Empire::SetProductionQuantityAndBlocksize(int index, int quantity, int blocksize) {
1162     if (index < 0 || static_cast<int>(m_production_queue.size()) <= index)
1163         throw std::runtime_error("Empire::SetProductionQuantity() : Attempted to adjust the quantity of items to be built in a nonexistent production queue item.");
1164     DebugLogger() << "Empire::SetProductionQuantityAndBlocksize() called for item "<< m_production_queue[index].item.name << "with new quant " << quantity << " and new blocksize " << blocksize;
1165     if (quantity < 1)
1166         throw std::runtime_error("Empire::SetProductionQuantity() : Attempted to set the quantity of a build run to a value less than zero.");
1167     if (m_production_queue[index].item.build_type == BT_BUILDING && ((1 < quantity) || ( 1 < blocksize) ))
1168         throw std::runtime_error("Empire::SetProductionQuantity() : Attempted to build more than one instance of a building in the same build run.");
1169     int original_quantity = m_production_queue[index].remaining;
1170     //int original_blocksize = m_production_queue[index].blocksize;
1171     blocksize = std::max(1, blocksize);
1172     m_production_queue[index].remaining = quantity;
1173     m_production_queue[index].ordered += quantity - original_quantity;
1174     m_production_queue[index].blocksize = blocksize;
1175     //std::cout << "original block size: " << original_blocksize << "  new blocksize: " << blocksize << "  memory blocksize: " << m_production_queue[index].blocksize_memory << std::endl;
1176     if (blocksize <= m_production_queue[index].blocksize_memory) {
1177         // if reducing block size, progress on retained portion is unchanged.
1178         // if increasing block size, progress is proportionally reduced, unless undoing a recent reduction in block size
1179         m_production_queue[index].progress = m_production_queue[index].progress_memory;
1180     } else {
1181         m_production_queue[index].progress = m_production_queue[index].progress_memory * m_production_queue[index].blocksize_memory / blocksize;
1182     }
1183 }
1184 
SplitIncompleteProductionItem(int index,boost::uuids::uuid uuid)1185 void Empire::SplitIncompleteProductionItem(int index, boost::uuids::uuid uuid) {
1186     DebugLogger() << "Empire::SplitIncompleteProductionItem() called for index " << index;
1187     if (index < 0 || static_cast<int>(m_production_queue.size()) <= index)
1188         throw std::runtime_error("Empire::SplitIncompleteProductionItem() : Attempted to adjust the quantity of items to be built in a nonexistent production queue item.");
1189     if (m_production_queue[index].item.build_type == BT_BUILDING)
1190         throw std::runtime_error("Empire::SplitIncompleteProductionItem() : Attempted to split a production item that is not a ship.");
1191 
1192     ProductionQueue::Element& elem = m_production_queue[index];
1193 
1194     // if "splitting" an item with just 1 remaining, do nothing
1195     if (elem.remaining <= 1)
1196         return;
1197 
1198     // add duplicate
1199     int new_item_quantity = elem.remaining - 1;
1200     elem.remaining = 1; // reduce remaining on specified to 1
1201     PlaceProductionOnQueue(elem.item, uuid, new_item_quantity, elem.blocksize, elem.location, index + 1);
1202 }
1203 
DuplicateProductionItem(int index,boost::uuids::uuid uuid)1204 void Empire::DuplicateProductionItem(int index, boost::uuids::uuid uuid) {
1205     DebugLogger() << "Empire::DuplicateProductionItem() called for index " << index << " with new UUID: " << boost::uuids::to_string(uuid);
1206     if (index < 0 || static_cast<int>(m_production_queue.size()) <= index)
1207         throw std::runtime_error("Empire::DuplicateProductionItem() : Attempted to adjust the quantity of items to be built in a nonexistent production queue item.");
1208 
1209     auto& elem = m_production_queue[index];
1210     PlaceProductionOnQueue(elem.item, uuid, elem.remaining, elem.blocksize, elem.location, index + 1);
1211 }
1212 
SetProductionRallyPoint(int index,int rally_point_id)1213 void Empire::SetProductionRallyPoint(int index, int rally_point_id) {
1214     if (index < 0 || static_cast<int>(m_production_queue.size()) <= index)
1215         throw std::runtime_error("Empire::SetProductionQuantity() : Attempted to adjust the quantity of items to be built in a nonexistent production queue item.");
1216     m_production_queue[index].rally_point_id = rally_point_id;
1217 }
1218 
SetProductionQuantity(int index,int quantity)1219 void Empire::SetProductionQuantity(int index, int quantity) {
1220     if (index < 0 || static_cast<int>(m_production_queue.size()) <= index)
1221         throw std::runtime_error("Empire::SetProductionQuantity() : Attempted to adjust the quantity of items to be built in a nonexistent production queue item.");
1222     if (quantity < 1)
1223         throw std::runtime_error("Empire::SetProductionQuantity() : Attempted to set the quantity of a build run to a value less than zero.");
1224     if (m_production_queue[index].item.build_type == BT_BUILDING && 1 < quantity)
1225         throw std::runtime_error("Empire::SetProductionQuantity() : Attempted to build more than one instance of a building in the same build run.");
1226     int original_quantity = m_production_queue[index].remaining;
1227     m_production_queue[index].remaining = quantity;
1228     m_production_queue[index].ordered += quantity - original_quantity;
1229 }
1230 
MoveProductionWithinQueue(int index,int new_index)1231 void Empire::MoveProductionWithinQueue(int index, int new_index) {
1232     if (index < new_index)
1233         --new_index;
1234     if (index < 0 || static_cast<int>(m_production_queue.size()) <= index ||
1235         new_index < 0 || static_cast<int>(m_production_queue.size()) <= new_index)
1236     {
1237         DebugLogger() << "Empire::MoveProductionWithinQueue index: " << index << "  new index: "
1238                       << new_index << "  queue size: " << m_production_queue.size();
1239         ErrorLogger() << "Attempted to move a production queue item to or from an invalid index.";
1240         return;
1241     }
1242     auto build = m_production_queue[index];
1243     m_production_queue.erase(index);
1244     m_production_queue.insert(m_production_queue.begin() + new_index, build);
1245 }
1246 
RemoveProductionFromQueue(int index)1247 void Empire::RemoveProductionFromQueue(int index) {
1248     if (index < 0 || static_cast<int>(m_production_queue.size()) <= index) {
1249         DebugLogger() << "Empire::RemoveProductionFromQueue index: " << index << "  queue size: " << m_production_queue.size();
1250         ErrorLogger() << "Attempted to delete a production queue item with an invalid index.";
1251         return;
1252     }
1253     m_production_queue.erase(index);
1254 }
1255 
PauseProduction(int index)1256 void Empire::PauseProduction(int index) {
1257     if (index < 0 || static_cast<int>(m_production_queue.size()) <= index) {
1258         DebugLogger() << "Empire::PauseProduction index: " << index << "  queue size: " << m_production_queue.size();
1259         ErrorLogger() << "Attempted pause a production queue item with an invalid index.";
1260         return;
1261     }
1262     m_production_queue[index].paused = true;
1263 }
1264 
ResumeProduction(int index)1265 void Empire::ResumeProduction(int index) {
1266     if (index < 0 || static_cast<int>(m_production_queue.size()) <= index) {
1267         DebugLogger() << "Empire::ResumeProduction index: " << index << "  queue size: " << m_production_queue.size();
1268         ErrorLogger() << "Attempted resume a production queue item with an invalid index.";
1269         return;
1270     }
1271     m_production_queue[index].paused = false;
1272 }
1273 
AllowUseImperialPP(int index,bool allow)1274 void Empire::AllowUseImperialPP(int index, bool allow /*=true*/) {
1275     if (index < 0 || static_cast<int>(m_production_queue.size()) <= index) {
1276         DebugLogger() << "Empire::AllowUseImperialPP index: " << index << "  queue size: " << m_production_queue.size();
1277         ErrorLogger() << "Attempted allow/disallow use of the imperial PP stockpile for a production queue item with an invalid index.";
1278         return;
1279     }
1280     DebugLogger() << "Empire::AllowUseImperialPP allow: " << allow << "  index: " << index << "  queue size: " << m_production_queue.size();
1281     m_production_queue[index].allowed_imperial_stockpile_use = allow;
1282 }
1283 
ConquerProductionQueueItemsAtLocation(int location_id,int empire_id)1284 void Empire::ConquerProductionQueueItemsAtLocation(int location_id, int empire_id) {
1285     if (location_id == INVALID_OBJECT_ID) {
1286         ErrorLogger() << "Empire::ConquerProductionQueueItemsAtLocation: tried to conquer build items located at an invalid location";
1287         return;
1288     }
1289 
1290     DebugLogger() << "Empire::ConquerProductionQueueItemsAtLocation: conquering items located at "
1291                   << location_id << " to empire " << empire_id;
1292 
1293     Empire* to_empire = GetEmpire(empire_id);    // may be null
1294     if (!to_empire && empire_id != ALL_EMPIRES) {
1295         ErrorLogger() << "Couldn't get empire with id " << empire_id;
1296         return;
1297     }
1298 
1299 
1300     for (auto& entry : Empires()) {
1301         int from_empire_id = entry.first;
1302         if (from_empire_id == empire_id) continue;    // skip this empire; can't capture one's own ProductionItems
1303 
1304         Empire* from_empire = entry.second;
1305         ProductionQueue& queue = from_empire->m_production_queue;
1306 
1307         for (auto queue_it = queue.begin(); queue_it != queue.end(); ) {
1308             auto elem = *queue_it;
1309             if (elem.location != location_id) {
1310                 ++queue_it;
1311                 continue; // skip projects with wrong location
1312             }
1313 
1314             ProductionQueue::ProductionItem item = elem.item;
1315 
1316             if (item.build_type == BT_BUILDING) {
1317                 std::string name = item.name;
1318                 const BuildingType* type = GetBuildingType(name);
1319                 if (!type) {
1320                     ErrorLogger() << "ConquerProductionQueueItemsAtLocation couldn't get building with name " << name;
1321                     continue;
1322                 }
1323 
1324                 CaptureResult result = type->GetCaptureResult(from_empire_id, empire_id, location_id, true);
1325 
1326                 if (result == CR_DESTROY) {
1327                     // item removed from current queue, NOT added to conquerer's queue
1328                     queue_it = queue.erase(queue_it);
1329 
1330                 } else if (result == CR_CAPTURE) {
1331                     if (to_empire) {
1332                         // item removed from current queue, added to conquerer's queue
1333                         ProductionQueue::Element new_elem(item, empire_id, elem.uuid, elem.ordered,
1334                                                           elem.remaining, 1, location_id);
1335                         new_elem.progress = elem.progress;
1336                         to_empire->m_production_queue.push_back(new_elem);
1337 
1338                         queue_it = queue.erase(queue_it);
1339                     } else {
1340                         // else do nothing; no empire can't capure things
1341                         ++queue_it;
1342                     }
1343 
1344                 } else if (result == INVALID_CAPTURE_RESULT) {
1345                     ErrorLogger() << "Empire::ConquerBuildsAtLocationFromEmpire: BuildingType had an invalid CaptureResult";
1346                 } else {
1347                     ++queue_it;
1348                 }
1349                 // otherwise do nothing: item left on current queue, conquerer gets nothing
1350             } else {
1351                 ++queue_it;
1352             }
1353 
1354             // TODO: other types of build item...
1355         }
1356     }
1357 }
1358 
AddNewlyResearchedTechToGrantAtStartOfNextTurn(const std::string & name)1359 void Empire::AddNewlyResearchedTechToGrantAtStartOfNextTurn(const std::string& name) {
1360     const Tech* tech = GetTech(name);
1361     if (!tech) {
1362         ErrorLogger() << "Empire::AddNewlyResearchedTechToGrantAtStartOfNextTurn given an invalid tech: " << name;
1363         return;
1364     }
1365 
1366     if (m_techs.count(name))
1367         return;
1368 
1369     // Mark given tech to be granted at next turn. If it was already marked, skip writing a SitRep message
1370     m_newly_researched_techs.insert(name);
1371 }
1372 
ApplyNewTechs()1373 void Empire::ApplyNewTechs() {
1374     for (auto new_tech : m_newly_researched_techs) {
1375         const Tech* tech = GetTech(new_tech);
1376         if (!tech) {
1377             ErrorLogger() << "Empire::ApplyNewTech has an invalid entry in m_newly_researched_techs: " << new_tech;
1378             continue;
1379         }
1380 
1381         for (const UnlockableItem& item : tech->UnlockedItems())
1382             UnlockItem(item);  // potential infinite if a tech (in)directly unlocks itself?
1383 
1384         if (!m_techs.count(new_tech)) {
1385             m_techs[new_tech] = CurrentTurn();
1386             AddSitRepEntry(CreateTechResearchedSitRep(new_tech));
1387         }
1388     }
1389     m_newly_researched_techs.clear();
1390 }
1391 
UnlockItem(const UnlockableItem & item)1392 void Empire::UnlockItem(const UnlockableItem& item) {
1393     switch (item.type) {
1394     case UIT_BUILDING:
1395         AddBuildingType(item.name);
1396         break;
1397     case UIT_SHIP_PART:
1398         AddShipPart(item.name);
1399         break;
1400     case UIT_SHIP_HULL:
1401         AddShipHull(item.name);
1402         break;
1403     case UIT_SHIP_DESIGN:
1404         AddShipDesign(GetPredefinedShipDesignManager().GetDesignID(item.name));
1405         break;
1406     case UIT_TECH:
1407         AddNewlyResearchedTechToGrantAtStartOfNextTurn(item.name);
1408         break;
1409     default:
1410         ErrorLogger() << "Empire::UnlockItem : passed UnlockableItem with unrecognized UnlockableItemType";
1411     }
1412 }
1413 
AddBuildingType(const std::string & name)1414 void Empire::AddBuildingType(const std::string& name) {
1415     const BuildingType* building_type = GetBuildingType(name);
1416     if (!building_type) {
1417         ErrorLogger() << "Empire::AddBuildingType given an invalid building type name: " << name;
1418         return;
1419     }
1420     if (!building_type->Producible())
1421         return;
1422     if (m_available_building_types.count(name))
1423         return;
1424     m_available_building_types.insert(name);
1425     AddSitRepEntry(CreateBuildingTypeUnlockedSitRep(name));
1426 }
1427 
AddShipPart(const std::string & name)1428 void Empire::AddShipPart(const std::string& name) {
1429     const ShipPart* ship_part = GetShipPart(name);
1430     if (!ship_part) {
1431         ErrorLogger() << "Empire::AddShipPart given an invalid ship part name: " << name;
1432         return;
1433     }
1434     if (!ship_part->Producible())
1435         return;
1436     m_available_ship_parts.insert(name);
1437     AddSitRepEntry(CreateShipPartUnlockedSitRep(name));
1438 }
1439 
AddShipHull(const std::string & name)1440 void Empire::AddShipHull(const std::string& name) {
1441     const ShipHull* ship_hull = GetShipHull(name);
1442     if (!ship_hull) {
1443         ErrorLogger() << "Empire::AddShipHull given an invalid hull type name: " << name;
1444         return;
1445     }
1446     if (!ship_hull->Producible())
1447         return;
1448     m_available_ship_hulls.insert(name);
1449     AddSitRepEntry(CreateShipHullUnlockedSitRep(name));
1450 }
1451 
AddExploredSystem(int ID)1452 void Empire::AddExploredSystem(int ID) {
1453     if (Objects().get<System>(ID))
1454         m_explored_systems.insert(ID);
1455     else
1456         ErrorLogger() << "Empire::AddExploredSystem given an invalid system id: " << ID;
1457 }
1458 
NewShipName()1459 std::string Empire::NewShipName() {
1460     static std::vector<std::string> ship_names = UserStringList("SHIP_NAMES");
1461     if (ship_names.empty())
1462         ship_names.push_back(UserString("OBJ_SHIP"));
1463 
1464     // select name randomly from list
1465     int ship_name_idx = RandSmallInt(0, static_cast<int>(ship_names.size()) - 1);
1466     std::string retval = ship_names[ship_name_idx];
1467     int times_name_used = ++m_ship_names_used[retval];
1468     if (1 < times_name_used)
1469         retval += " " + RomanNumber(times_name_used);
1470     return retval;
1471 }
1472 
AddShipDesign(int ship_design_id,int next_design_id)1473 void Empire::AddShipDesign(int ship_design_id, int next_design_id) {
1474     /* Check if design id is valid.  That is, check that it corresponds to an
1475      * existing shipdesign in the universe.  On clients, this means that this
1476      * empire knows about this ship design and the server consequently sent the
1477      * design to this player.  On the server, all existing ship designs will be
1478      * valid, so this just adds this design's id to those that this empire will
1479      * retain as one of it's ship designs, which are those displayed in the GUI
1480      * list of available designs for human players, and */
1481     if (ship_design_id == next_design_id)
1482         return;
1483 
1484     const ShipDesign* ship_design = GetUniverse().GetShipDesign(ship_design_id);
1485     if (ship_design) {  // don't check if design is producible; adding a ship design is useful for more than just producing it
1486         // design is valid, so just add the id to empire's set of ids that it knows about
1487         if (!m_known_ship_designs.count(ship_design_id)) {
1488             m_known_ship_designs.insert(ship_design_id);
1489 
1490             ShipDesignsChangedSignal();
1491 
1492             TraceLogger() << "AddShipDesign::  " << ship_design->Name() << " (" << ship_design_id
1493                           << ") to empire #" << EmpireID();
1494         }
1495     } else {
1496         // design in not valid
1497         ErrorLogger() << "Empire::AddShipDesign(int ship_design_id) was passed a design id that this empire doesn't know about, or that doesn't exist";
1498     }
1499 }
1500 
AddShipDesign(ShipDesign * ship_design)1501 int Empire::AddShipDesign(ShipDesign* ship_design) {
1502     Universe& universe = GetUniverse();
1503     /* check if there already exists this same design in the universe.  On clients, this checks whether this empire
1504        knows of this exact design and is trying to re-add it.  On the server, this checks whether this exact design
1505        exists at all yet */
1506     for (Universe::ship_design_iterator it = universe.beginShipDesigns(); it != universe.endShipDesigns(); ++it) {
1507         if (ship_design == it->second) {
1508             // ship design is already present in universe.  just need to add it to the empire's set of ship designs
1509             int ship_design_id = it->first;
1510             AddShipDesign(ship_design_id);
1511             return ship_design_id;
1512         }
1513     }
1514 
1515     bool success = universe.InsertShipDesign(ship_design);
1516 
1517     if (!success) {
1518         ErrorLogger() << "Empire::AddShipDesign Unable to add new design to universe";
1519         return INVALID_OBJECT_ID;
1520     }
1521 
1522     auto new_design_id = ship_design->ID();
1523     AddShipDesign(new_design_id);
1524 
1525     return new_design_id;
1526 }
1527 
RemoveShipDesign(int ship_design_id)1528 void Empire::RemoveShipDesign(int ship_design_id) {
1529     if (m_known_ship_designs.count(ship_design_id)) {
1530         m_known_ship_designs.erase(ship_design_id);
1531         ShipDesignsChangedSignal();
1532     } else {
1533         DebugLogger() << "Empire::RemoveShipDesign: this empire did not have design with id " << ship_design_id;
1534     }
1535 }
1536 
AddSitRepEntry(const SitRepEntry & entry)1537 void Empire::AddSitRepEntry(const SitRepEntry& entry)
1538 { m_sitrep_entries.push_back(entry); }
1539 
RemoveTech(const std::string & name)1540 void Empire::RemoveTech(const std::string& name)
1541 { m_techs.erase(name); }
1542 
LockItem(const UnlockableItem & item)1543 void Empire::LockItem(const UnlockableItem& item) {
1544     switch (item.type) {
1545     case UIT_BUILDING:
1546         RemoveBuildingType(item.name);
1547         break;
1548     case UIT_SHIP_PART:
1549         RemoveShipPart(item.name);
1550         break;
1551     case UIT_SHIP_HULL:
1552         RemoveShipHull(item.name);
1553         break;
1554     case UIT_SHIP_DESIGN:
1555         RemoveShipDesign(GetPredefinedShipDesignManager().GetDesignID(item.name));
1556         break;
1557     case UIT_TECH:
1558         RemoveTech(item.name);
1559         break;
1560     default:
1561         ErrorLogger() << "Empire::LockItem : passed UnlockableItem with unrecognized UnlockableItemType";
1562     }
1563 }
1564 
RemoveBuildingType(const std::string & name)1565 void Empire::RemoveBuildingType(const std::string& name) {
1566     if (!m_available_building_types.count(name))
1567         DebugLogger() << "Empire::RemoveBuildingType asked to remove building type " << name << " that was no available to this empire";
1568     m_available_building_types.erase(name);
1569 }
1570 
RemoveShipPart(const std::string & name)1571 void Empire::RemoveShipPart(const std::string& name) {
1572     auto it = m_available_ship_parts.find(name);
1573     if (it == m_available_ship_parts.end())
1574         DebugLogger() << "Empire::RemoveShipPart asked to remove part type " << name << " that was no available to this empire";
1575     m_available_ship_parts.erase(name);
1576 }
1577 
RemoveShipHull(const std::string & name)1578 void Empire::RemoveShipHull(const std::string& name) {
1579     auto it = m_available_ship_hulls.find(name);
1580     if (it == m_available_ship_hulls.end())
1581         DebugLogger() << "Empire::RemoveShipHull asked to remove hull type " << name << " that was no available to this empire";
1582     m_available_ship_hulls.erase(name);
1583 }
1584 
ClearSitRep()1585 void Empire::ClearSitRep()
1586 { m_sitrep_entries.clear(); }
1587 
1588 namespace {
1589     // remove nonexistant / invalid techs from queue
SanitizeResearchQueue(ResearchQueue & queue)1590     void SanitizeResearchQueue(ResearchQueue& queue) {
1591         bool done = false;
1592         while (!done) {
1593             auto it = queue.begin();
1594             while (true) {
1595                 if (it == queue.end()) {
1596                     done = true;        // got all the way through the queue without finding an invalid tech
1597                     break;
1598                 } else if (!GetTech(it->name)) {
1599                     DebugLogger() << "SanitizeResearchQueue for empire " << queue.EmpireID() << " removed invalid tech: " << it->name;
1600                     queue.erase(it);    // remove invalid tech, end inner loop without marking as finished
1601                     break;
1602                 } else {
1603                     ++it;               // check next element
1604                 }
1605             }
1606         }
1607     }
1608 }
1609 
CheckResearchProgress()1610 std::vector<std::string> Empire::CheckResearchProgress() {
1611     SanitizeResearchQueue(m_research_queue);
1612 
1613     float spent_rp{0.0f};
1614     float total_rp_available = m_resource_pools[RE_RESEARCH]->TotalAvailable();
1615 
1616     // process items on queue
1617     std::vector<std::string> to_erase_from_queue_and_grant_next_turn;
1618     for (auto& elem : m_research_queue) {
1619         const Tech* tech = GetTech(elem.name);
1620         if (!tech) {
1621             ErrorLogger() << "Empire::CheckResearchProgress couldn't find tech on queue, even after sanitizing!";
1622             continue;
1623         }
1624         float& progress = m_research_progress[elem.name];
1625         float tech_cost = tech->ResearchCost(m_id);
1626         progress += elem.allocated_rp / std::max(EPSILON, tech_cost);
1627         spent_rp += elem.allocated_rp;
1628         if (tech->ResearchCost(m_id) - EPSILON <= progress * tech_cost) {
1629             m_research_progress.erase(elem.name);
1630             to_erase_from_queue_and_grant_next_turn.push_back(elem.name);
1631         }
1632     }
1633 
1634     //DebugLogger() << m_research_queue.Dump();
1635     float rp_left_to_spend = total_rp_available - spent_rp;
1636     //DebugLogger() << "leftover RP: " << rp_left_to_spend;
1637     // auto-allocate any excess RP left over after player-specified queued techs
1638 
1639     // if there are left over RPs, any tech on the queue presumably can't
1640     // have RP allocated to it
1641     std::unordered_set<std::string> techs_not_suitable_for_auto_allocation;
1642     for (auto& elem : m_research_queue)
1643         techs_not_suitable_for_auto_allocation.insert(elem.name);
1644 
1645     // for all available and suitable techs, store ordered by cost to complete
1646     std::multimap<double, std::string> costs_to_complete_available_unpaused_techs;
1647     for (const auto& tech : GetTechManager()) {
1648         const std::string& tech_name = tech->Name();
1649         if (techs_not_suitable_for_auto_allocation.count(tech_name) > 0)
1650             continue;
1651         if (this->GetTechStatus(tech_name) != TS_RESEARCHABLE)
1652             continue;
1653         if (!tech->Researchable())
1654             continue;
1655         double progress = this->ResearchProgress(tech_name);
1656         double total_cost = tech->ResearchCost(m_id);
1657         if (progress >= total_cost)
1658             continue;
1659         costs_to_complete_available_unpaused_techs.emplace(total_cost - progress, tech_name);
1660     }
1661 
1662     // in order of minimum additional cost to complete, allocate RP to
1663     // techs up to available RP and per-turn limits
1664     for (auto const& cost_tech : costs_to_complete_available_unpaused_techs) {
1665         if (rp_left_to_spend <= EPSILON)
1666             break;
1667 
1668         const Tech* tech = GetTech(cost_tech.second);
1669         if (!tech)
1670             continue;
1671 
1672         //DebugLogger() << "extra tech: " << cost_tech.second << " needs: " << cost_tech.first << " more RP to finish";
1673 
1674         float RPs_per_turn_limit = tech->PerTurnCost(m_id);
1675         float tech_total_cost = tech->ResearchCost(m_id);
1676         float progress_fraction = m_research_progress[cost_tech.second];
1677 
1678         float progress_fraction_left = 1.0f - progress_fraction;
1679         float max_progress_per_turn = RPs_per_turn_limit / tech_total_cost;
1680         float progress_possible_with_available_rp = rp_left_to_spend / tech_total_cost;
1681 
1682         //DebugLogger() << "... progress left: " << progress_fraction_left
1683         //              << " max per turn: " << max_progress_per_turn
1684         //              << " progress possible with available rp: " << progress_possible_with_available_rp;
1685 
1686         float progress_increase = std::min(
1687             progress_fraction_left,
1688             std::min(max_progress_per_turn, progress_possible_with_available_rp));
1689 
1690         float consumed_rp = progress_increase * tech_total_cost;
1691 
1692         m_research_progress[cost_tech.second] += progress_increase;
1693         rp_left_to_spend -= consumed_rp;
1694 
1695         if (tech->ResearchCost(m_id) - EPSILON <= m_research_progress[cost_tech.second] * tech_total_cost)
1696             to_erase_from_queue_and_grant_next_turn.push_back(cost_tech.second);
1697 
1698         //DebugLogger() << "... allocated: " << consumed_rp << " to increase progress by: " << progress_increase;
1699     }
1700 
1701     // remove completed items from queue (after consuming extra RP, as that
1702     // determination uses the contents of the queue as input)
1703     for (const std::string& tech_name : to_erase_from_queue_and_grant_next_turn) {
1704         auto temp_it = m_research_queue.find(tech_name);
1705         if (temp_it != m_research_queue.end())
1706             m_research_queue.erase(temp_it);
1707     }
1708 
1709     // can uncomment following line when / if research stockpiling is enabled...
1710     // m_resource_pools[RE_RESEARCH]->SetStockpile(m_resource_pools[RE_RESEARCH]->TotalAvailable() - m_research_queue.TotalRPsSpent());
1711     return to_erase_from_queue_and_grant_next_turn;
1712 }
1713 
CheckProductionProgress()1714 void Empire::CheckProductionProgress() {
1715     DebugLogger() << "========Empire::CheckProductionProgress=======";
1716     // following commented line should be redundant, as previous call to
1717     // UpdateResourcePools should have generated necessary info
1718     // m_production_queue.Update();
1719 
1720     Universe& universe = GetUniverse();
1721 
1722     std::map<int, std::vector<std::shared_ptr<Ship>>> system_new_ships;
1723     std::map<int, int> new_ship_rally_point_ids;
1724 
1725     // preprocess the queue to get all the costs and times of all items
1726     // at every location at which they are being produced,
1727     // before doing any generation of new objects or other modifications
1728     // of the gamestate. this will ensure that the cost of items doesn't
1729     // change while the queue is being processed, so that if there is
1730     // sufficent PP to complete an object at the start of a turn,
1731     // items above it on the queue getting finished don't increase the
1732     // cost and result in it not being finished that turn.
1733     std::map<std::pair<ProductionQueue::ProductionItem, int>, std::pair<float, int>>
1734         queue_item_costs_and_times;
1735     for (auto& elem : m_production_queue) {
1736         // for items that don't depend on location, only store cost/time once
1737         int location_id = (elem.item.CostIsProductionLocationInvariant() ? INVALID_OBJECT_ID : elem.location);
1738         auto key = std::make_pair(elem.item, location_id);
1739 
1740         if (!queue_item_costs_and_times.count(key))
1741             queue_item_costs_and_times[key] = ProductionCostAndTime(elem);
1742     }
1743 
1744     //for (auto& entry : queue_item_costs_and_times)
1745     //{ DebugLogger() << entry.first.first.design_id << " : " << entry.second.first; }
1746 
1747 
1748     // go through queue, updating production progress.  If a production item is
1749     // completed, create the produced object or take whatever other action is
1750     // appropriate, and record that queue item as complete, so it can be erased
1751     // from the queue
1752     std::vector<int> to_erase;
1753     for (unsigned int i = 0; i < m_production_queue.size(); ++i) {
1754         auto& elem = m_production_queue[i];
1755         float item_cost;
1756         int build_turns;
1757 
1758         // for items that don't depend on location, only store cost/time once
1759         int location_id = (elem.item.CostIsProductionLocationInvariant() ? INVALID_OBJECT_ID : elem.location);
1760         std::pair<ProductionQueue::ProductionItem, int> key(elem.item, location_id);
1761 
1762         std::tie(item_cost, build_turns) = queue_item_costs_and_times[key];
1763         if (item_cost < 0.01f || build_turns < 1) {
1764             ErrorLogger() << "Empire::CheckProductionProgress got strang cost/time: " << item_cost << " / " << build_turns;
1765             break;
1766         }
1767 
1768         item_cost *= elem.blocksize;
1769 
1770         DebugLogger() << "elem: " << elem.Dump();
1771         DebugLogger() << "   allocated: " << elem.allocated_pp;
1772         DebugLogger() << "   initial progress: " << elem.progress;
1773 
1774         elem.progress += elem.allocated_pp / std::max(EPSILON, item_cost);  // add progress for allocated PP to queue item
1775         elem.progress_memory = elem.progress;
1776         elem.blocksize_memory = elem.blocksize;
1777 
1778         DebugLogger() << "   updated progress: " << elem.progress;
1779         DebugLogger() << " ";
1780 
1781         std::string build_description;
1782         switch (elem.item.build_type) {
1783             case BT_BUILDING: {
1784                 build_description = "BuildingType " + elem.item.name;
1785                 break;
1786             }
1787             case BT_SHIP: {
1788                 build_description = "Ships(s) with design id " + std::to_string(elem.item.design_id);
1789                 break;
1790             }
1791             case BT_STOCKPILE: {
1792                 build_description = "Stockpile PP transfer";
1793                 break;
1794             }
1795             default:
1796                 build_description = "unknown build type";
1797         }
1798 
1799         auto build_location = Objects().get(elem.location);
1800         if (!build_location || (elem.item.build_type == BT_BUILDING && build_location->ObjectType() != OBJ_PLANET)) {
1801             ErrorLogger() << "Couldn't get valid build location for completed " << build_description;
1802             continue;
1803         }
1804         auto system = Objects().get<System>(build_location->SystemID());
1805         // TODO: account for shipyards and/or other ship production
1806         // sites that are in interstellar space, if needed
1807         if (!system) {
1808             ErrorLogger() << "Empire::CheckProductionProgress couldn't get system for producing new " << build_description;
1809             continue;
1810         }
1811 
1812         // check location condition before each item is created, so
1813         // that items being produced can prevent subsequent
1814         // completions on the same turn from going through
1815         if (!this->ProducibleItem(elem.item, elem.location)) {
1816             DebugLogger() << "Location test failed for " << build_description << " at location " << build_location->Name();
1817             continue;
1818         }
1819 
1820 
1821         // only if accumulated PP is sufficient, the item can be completed
1822         if (item_cost - EPSILON > elem.progress*item_cost)
1823             continue;
1824 
1825 
1826         // only if consumed resources are available, then item can be completd
1827         bool consumption_impossible = false;
1828         std::map<std::string, std::map<int, float>> sc = elem.item.CompletionSpecialConsumption(elem.location);
1829         for (auto& special_type : sc) {
1830             if (consumption_impossible)
1831                 break;
1832             for (auto& special_meter : special_type.second) {
1833                 auto obj = Objects().get(special_meter.first);
1834                 float capacity = obj ? obj->SpecialCapacity(special_type.first) : 0.0f;
1835                 if (capacity < special_meter.second * elem.blocksize) {
1836                     consumption_impossible = true;
1837                     break;
1838                 }
1839             }
1840         }
1841         auto mc = elem.item.CompletionMeterConsumption(elem.location);
1842         for (auto& meter_type : mc) {
1843             if (consumption_impossible)
1844                 break;
1845             for (auto& object_meter : meter_type.second) {
1846                 auto obj = Objects().get(object_meter.first);
1847                 const Meter* meter = obj ? obj->GetMeter(meter_type.first) : nullptr;
1848                 if (!meter || meter->Current() < object_meter.second * elem.blocksize) {
1849                     consumption_impossible = true;
1850                     break;
1851                 }
1852             }
1853         }
1854         if (consumption_impossible)
1855             continue;
1856 
1857 
1858         // deduct progress for complete item from accumulated progress, so that next
1859         // repetition can continue accumulating PP, but don't set progress to 0, as
1860         // this way overflow progress / PP allocated this turn can be used for the
1861         // next repetition of the item.
1862         elem.progress -= 1.0f;
1863         if (elem.progress < 0.0f) {
1864             if (elem.progress < -1e-3)
1865                 ErrorLogger() << "Somehow got negative progress (" << elem.progress
1866                               << ") after deducting progress for completed item...";
1867             elem.progress = 0.0f;
1868         }
1869 
1870         elem.progress_memory = elem.progress;
1871         DebugLogger() << "Completed an item: " << elem.item.name;
1872 
1873 
1874         // consume the item's special and meter consumption
1875         for (auto& special_type : sc) {
1876             for (auto& special_meter : special_type.second) {
1877                 auto obj = Objects().get(special_meter.first);
1878                 if (!obj)
1879                     continue;
1880                 if (!obj->HasSpecial(special_type.first))
1881                     continue;
1882                 float cur_capacity = obj->SpecialCapacity(special_type.first);
1883                 float new_capacity = std::max(0.0f, cur_capacity - special_meter.second * elem.blocksize);
1884                 obj->SetSpecialCapacity(special_type.first, new_capacity);
1885             }
1886         }
1887         for (auto& meter_type : mc) {
1888             for (const auto& object_meter : meter_type.second) {
1889                 auto obj = Objects().get(object_meter.first);
1890                 if (!obj)
1891                     continue;
1892                 Meter*meter = obj->GetMeter(meter_type.first);
1893                 if (!meter)
1894                     continue;
1895                 float cur_meter = meter->Current();
1896                 float new_meter = cur_meter - object_meter.second * elem.blocksize;
1897                 meter->SetCurrent(new_meter);
1898                 meter->BackPropagate();
1899             }
1900         }
1901 
1902 
1903         // create actual thing(s) being produced
1904         switch (elem.item.build_type) {
1905         case BT_BUILDING: {
1906             auto planet = Objects().get<Planet>(elem.location);
1907 
1908             // create new building
1909             auto building = universe.InsertNew<Building>(m_id, elem.item.name, m_id);
1910             planet->AddBuilding(building->ID());
1911             building->SetPlanetID(planet->ID());
1912             system->Insert(building);
1913 
1914             // record building production in empire stats
1915             if (m_building_types_produced.count(elem.item.name))
1916                 m_building_types_produced[elem.item.name]++;
1917             else
1918                 m_building_types_produced[elem.item.name] = 1;
1919 
1920             AddSitRepEntry(CreateBuildingBuiltSitRep(building->ID(), planet->ID()));
1921             DebugLogger() << "New Building created on turn: " << CurrentTurn();
1922             break;
1923         }
1924 
1925         case BT_SHIP: {
1926             if (elem.blocksize < 1)
1927                 break;   // nothing to do!
1928 
1929             // get species for this ship.  use popcenter species if build
1930             // location is a popcenter, or use ship species if build
1931             // location is a ship, or use empire capital species if there
1932             // is a valid capital, or otherwise ???
1933             // TODO: Add more fallbacks if necessary
1934             std::string species_name;
1935             if (auto location_pop_center = std::dynamic_pointer_cast<const PopCenter>(build_location))
1936                 species_name = location_pop_center->SpeciesName();
1937             else if (auto location_ship = std::dynamic_pointer_cast<const Ship>(build_location))
1938                 species_name = location_ship->SpeciesName();
1939             else if (auto capital_planet = Objects().get<Planet>(this->CapitalID()))
1940                 species_name = capital_planet->SpeciesName();
1941             // else give up...
1942             if (species_name.empty()) {
1943                 // only really a problem for colony ships, which need to have a species to function
1944                 const auto* design = GetShipDesign(elem.item.design_id);
1945                 if (!design) {
1946                     ErrorLogger() << "Couldn't get ShipDesign with id: " << elem.item.design_id;
1947                     break;
1948                 }
1949                 if (design->CanColonize()) {
1950                     ErrorLogger() << "Couldn't get species in order to make colony ship!";
1951                     break;
1952                 }
1953             }
1954 
1955             std::shared_ptr<Ship> ship;
1956 
1957             for (int count = 0; count < elem.blocksize; count++) {
1958                 // create ship
1959                 ship = universe.InsertNew<Ship>(m_id, elem.item.design_id, species_name, m_id);
1960                 system->Insert(ship);
1961 
1962                 // record ship production in empire stats
1963                 if (m_ship_designs_produced.count(elem.item.design_id))
1964                     m_ship_designs_produced[elem.item.design_id]++;
1965                 else
1966                     m_ship_designs_produced[elem.item.design_id] = 1;
1967                 if (m_species_ships_produced.count(species_name))
1968                     m_species_ships_produced[species_name]++;
1969                 else
1970                     m_species_ships_produced[species_name] = 1;
1971 
1972 
1973                 // set active meters that have associated max meters to an
1974                 // initial very large value, so that when the active meters are
1975                 // later clamped, they will equal the max meter after effects
1976                 // have been applied, letting new ships start with maxed
1977                 // everything that is traced with an associated max meter.
1978                 ship->SetShipMetersToMax();
1979                 // set ship speed so that it can be affected by non-zero speed checks
1980                 if (auto* design = GetShipDesign(elem.item.design_id))
1981                     ship->GetMeter(METER_SPEED)->Set(design->Speed(), design->Speed());
1982                 ship->BackPropagateMeters();
1983 
1984                 ship->Rename(NewShipName());
1985 
1986                 // store ships to put into fleets later
1987                 system_new_ships[system->ID()].push_back(ship);
1988 
1989                 // store ship rally points
1990                 if (elem.rally_point_id != INVALID_OBJECT_ID)
1991                     new_ship_rally_point_ids[ship->ID()] = elem.rally_point_id;
1992             }
1993             // add sitrep
1994             if (elem.blocksize == 1) {
1995                 AddSitRepEntry(CreateShipBuiltSitRep(ship->ID(), system->ID(), ship->DesignID()));
1996                 DebugLogger() << "New Ship, id " << ship->ID() << ", created on turn: " << ship->CreationTurn();
1997             } else {
1998                 AddSitRepEntry(CreateShipBlockBuiltSitRep(system->ID(), ship->DesignID(), elem.blocksize));
1999                 DebugLogger() << "New block of "<< elem.blocksize << " ships created on turn: " << ship->CreationTurn();
2000             }
2001             break;
2002         }
2003 
2004         case BT_STOCKPILE: {
2005             DebugLogger() << "Finished a transfer to stockpile";
2006             break;
2007         }
2008 
2009         default:
2010             ErrorLogger() << "Build item of unknown build type finished on production queue.";
2011             break;
2012         }
2013 
2014         if (!--m_production_queue[i].remaining) {   // decrement number of remaining items to be produced in current queue element
2015             to_erase.push_back(i);                  // remember completed element so that it can be removed from queue
2016             DebugLogger() << "Marking completed production queue item to be removed from queue";
2017         }
2018     }
2019 
2020     // create fleets for new ships and put ships into fleets
2021     for (auto& entry : system_new_ships) {
2022         auto system = Objects().get<System>(entry.first);
2023         if (!system) {
2024             ErrorLogger() << "Couldn't get system with id " << entry.first << " for creating new fleets for newly produced ships";
2025             continue;
2026         }
2027 
2028         auto& new_ships = entry.second;
2029         if (new_ships.empty())
2030             continue;
2031 
2032         // group ships into fleets by rally point and design
2033         std::map<int, std::map<int, std::vector<std::shared_ptr<Ship>>>>
2034             new_ships_by_rally_point_id_and_design_id;
2035         for (auto& ship : new_ships) {
2036             int rally_point_id = INVALID_OBJECT_ID;
2037 
2038             auto rally_it = new_ship_rally_point_ids.find(ship->ID());
2039             if (rally_it != new_ship_rally_point_ids.end())
2040                 rally_point_id = rally_it->second;
2041 
2042             new_ships_by_rally_point_id_and_design_id[rally_point_id][ship->DesignID()].push_back(ship);
2043         }
2044 
2045         // create fleets for ships with the same rally point, grouped by
2046         // ship design
2047         // Do not group unarmed ships with no troops (i.e. scouts and
2048         // colony ships).
2049         for (auto& rally_ships : new_ships_by_rally_point_id_and_design_id) {
2050             int rally_point_id = rally_ships.first;
2051             auto& new_ships_by_design = rally_ships.second;
2052 
2053             for (auto& ships_by_design : new_ships_by_design) {
2054                 std::vector<int> ship_ids;
2055 
2056                 auto& ships = ships_by_design.second;
2057                 if (ships.empty())
2058                     continue;
2059 
2060                 // create a single fleet for combat ships and individual
2061                 // fleets for non-combat ships
2062                 bool individual_fleets = !((*ships.begin())->IsArmed()
2063                                            || (*ships.begin())->HasFighters()
2064                                            || (*ships.begin())->CanHaveTroops()
2065                                            || (*ships.begin())->CanBombard());
2066 
2067                 std::vector<std::shared_ptr<Fleet>> fleets;
2068                 std::shared_ptr<Fleet> fleet;
2069 
2070                 if (!individual_fleets) {
2071                     fleet = universe.InsertNew<Fleet>("", system->X(), system->Y(), m_id);
2072 
2073                     system->Insert(fleet);
2074                     // set prev system to prevent conflicts with CalculateRouteTo used for
2075                     // rally points below, but leave next system as INVALID_OBJECT_ID so
2076                     // fleet won't necessarily be disqualified from making blockades if it
2077                     // is left stationary
2078                     fleet->SetNextAndPreviousSystems(INVALID_OBJECT_ID, system->ID());
2079                     // set invalid arrival starlane so that fleet won't necessarily be free from blockades
2080                     fleet->SetArrivalStarlane(INVALID_OBJECT_ID);
2081 
2082                     fleets.push_back(fleet);
2083                 }
2084 
2085                 for (auto& ship : ships) {
2086                     if (individual_fleets) {
2087                         fleet = universe.InsertNew<Fleet>("", system->X(), system->Y(), m_id);
2088 
2089                         system->Insert(fleet);
2090                         // set prev system to prevent conflicts with CalculateRouteTo used for
2091                         // rally points below, but leave next system as INVALID_OBJECT_ID so
2092                         // fleet won't necessarily be disqualified from making blockades if it
2093                         // is left stationary
2094                         fleet->SetNextAndPreviousSystems(INVALID_OBJECT_ID, system->ID());
2095                         // set invalid arrival starlane so that fleet won't necessarily be free from blockades
2096                         fleet->SetArrivalStarlane(INVALID_OBJECT_ID);
2097 
2098                         fleets.push_back(fleet);
2099                     }
2100                     ship_ids.push_back(ship->ID());
2101                     fleet->AddShips({ship->ID()});
2102                     ship->SetFleetID(fleet->ID());
2103                 }
2104 
2105                 for (auto& next_fleet : fleets) {
2106                     // rename fleet, given its id and the ship that is in it
2107                     next_fleet->Rename(next_fleet->GenerateFleetName());
2108                     next_fleet->SetAggressive(next_fleet->HasArmedShips());
2109 
2110                     if (rally_point_id != INVALID_OBJECT_ID) {
2111                         if (Objects().get<System>(rally_point_id)) {
2112                             next_fleet->CalculateRouteTo(rally_point_id);
2113                         } else if (auto rally_obj = Objects().get(rally_point_id)) {
2114                             if (Objects().get<System>(rally_obj->SystemID()))
2115                                 next_fleet->CalculateRouteTo(rally_obj->SystemID());
2116                         } else {
2117                             ErrorLogger() << "Unable to find system to route to with rally point id: " << rally_point_id;
2118                         }
2119                     }
2120 
2121                     DebugLogger() << "New Fleet \"" << next_fleet->Name()
2122                                   <<"\" created on turn: " << next_fleet->CreationTurn();
2123                 }
2124             }
2125         }
2126     }
2127 
2128     // removed completed items from queue
2129     for (auto it = to_erase.rbegin(); it != to_erase.rend(); ++it)
2130         m_production_queue.erase(*it);
2131 
2132     // update stockpile
2133     SetResourceStockpile(RE_INDUSTRY, m_production_queue.ExpectedNewStockpileAmount());
2134 }
2135 
CheckTradeSocialProgress()2136 void Empire::CheckTradeSocialProgress()
2137 { m_resource_pools[RE_TRADE]->SetStockpile(m_resource_pools[RE_TRADE]->TotalAvailable()); }
2138 
SetColor(const GG::Clr & color)2139 void Empire::SetColor(const GG::Clr& color)
2140 { m_color = color; }
2141 
SetName(const std::string & name)2142 void Empire::SetName(const std::string& name)
2143 { m_name = name; }
2144 
SetPlayerName(const std::string & player_name)2145 void Empire::SetPlayerName(const std::string& player_name)
2146 { m_player_name = player_name; }
2147 
InitResourcePools()2148 void Empire::InitResourcePools() {
2149     // get this empire's owned resource centers and ships (which can both produce resources)
2150     std::vector<int> res_centers;
2151     res_centers.reserve(Objects().ExistingResourceCenters().size());
2152     for (const auto& entry : Objects().ExistingResourceCenters()) {
2153         if (!entry.second->OwnedBy(m_id))
2154             continue;
2155         res_centers.push_back(entry.first);
2156     }
2157     for (const auto& entry : Objects().ExistingShips()) {
2158         if (!entry.second->OwnedBy(m_id))
2159             continue;
2160         res_centers.push_back(entry.first);
2161     }
2162     m_resource_pools[RE_RESEARCH]->SetObjects(res_centers);
2163     m_resource_pools[RE_INDUSTRY]->SetObjects(res_centers);
2164     m_resource_pools[RE_TRADE]->SetObjects(res_centers);
2165 
2166     // get this empire's owned population centers
2167     std::vector<int> pop_centers;
2168     pop_centers.reserve(Objects().ExistingPopCenters().size());
2169     for (const auto& entry : Objects().ExistingPopCenters()) {
2170         if (entry.second->OwnedBy(m_id))
2171             pop_centers.push_back(entry.first);
2172     }
2173     m_population_pool.SetPopCenters(pop_centers);
2174 
2175 
2176     // inform the blockadeable resource pools about systems that can share
2177     m_resource_pools[RE_INDUSTRY]->SetConnectedSupplyGroups(GetSupplyManager().ResourceSupplyGroups(m_id));
2178 
2179     // set non-blockadeable resource pools to share resources between all systems
2180     std::set<std::set<int>> sets_set;
2181     std::set<int> all_systems_set;
2182     for (const auto& entry : Objects().ExistingSystems()) {
2183         all_systems_set.insert(entry.first);
2184     }
2185     sets_set.insert(all_systems_set);
2186     m_resource_pools[RE_RESEARCH]->SetConnectedSupplyGroups(sets_set);
2187     m_resource_pools[RE_TRADE]->SetConnectedSupplyGroups(sets_set);
2188 }
2189 
UpdateResourcePools()2190 void Empire::UpdateResourcePools() {
2191     // updating queues, allocated_rp, distribution and growth each update their
2192     // respective pools, (as well as the ways in which the resources are used,
2193     // which needs to be done simultaneously to keep things consistent)
2194     UpdateResearchQueue();
2195     UpdateProductionQueue();
2196     UpdateTradeSpending();
2197     UpdatePopulationGrowth();
2198 }
2199 
UpdateResearchQueue()2200 void Empire::UpdateResearchQueue() {
2201     m_resource_pools[RE_RESEARCH]->Update();
2202     m_research_queue.Update(m_resource_pools[RE_RESEARCH]->TotalAvailable(), m_research_progress);
2203     m_resource_pools[RE_RESEARCH]->ChangedSignal();
2204 }
2205 
UpdateProductionQueue()2206 void Empire::UpdateProductionQueue() {
2207     DebugLogger() << "========= Production Update for empire: " << EmpireID() << " ========";
2208 
2209     m_resource_pools[RE_INDUSTRY]->Update();
2210     m_production_queue.Update();
2211     m_resource_pools[RE_INDUSTRY]->ChangedSignal();
2212 }
2213 
UpdateTradeSpending()2214 void Empire::UpdateTradeSpending() {
2215     m_resource_pools[RE_TRADE]->Update(); // recalculate total trade production
2216     m_resource_pools[RE_TRADE]->ChangedSignal();
2217 }
2218 
UpdatePopulationGrowth()2219 void Empire::UpdatePopulationGrowth()
2220 { m_population_pool.Update(); }
2221 
ResetMeters()2222 void Empire::ResetMeters() {
2223     for (auto& entry : m_meters) {
2224         entry.second.ResetCurrent();
2225     }
2226 }
2227 
UpdateOwnedObjectCounters()2228 void Empire::UpdateOwnedObjectCounters() {
2229     // ships of each species and design
2230     m_species_ships_owned.clear();
2231     m_ship_designs_owned.clear();
2232     for (const auto& entry : Objects().ExistingShips()) {
2233         if (!entry.second->OwnedBy(this->EmpireID()))
2234             continue;
2235         auto ship = std::dynamic_pointer_cast<const Ship>(entry.second);
2236         if (!ship)
2237             continue;
2238         if (!ship->SpeciesName().empty())
2239             m_species_ships_owned[ship->SpeciesName()]++;
2240         m_ship_designs_owned[ship->DesignID()]++;
2241     }
2242 
2243     // ships in the queue for which production started
2244     m_ship_designs_in_production.clear();
2245     for (const auto& elem : m_production_queue) {
2246         ProductionQueue::ProductionItem item = elem.item;
2247 
2248         if ((item.build_type == BT_SHIP) && (elem.progress > 0.0f)) {
2249             m_ship_designs_in_production[item.design_id] += elem.blocksize;
2250         }
2251     }
2252 
2253     // update ship part counts
2254     m_ship_parts_owned.clear();
2255     m_ship_part_class_owned.clear();
2256     for (const auto& design_count : m_ship_designs_owned) {
2257         const ShipDesign* design = GetShipDesign(design_count.first);
2258         if (!design)
2259             continue;
2260 
2261         // update count of ShipParts
2262         for (const auto& ship_part : design->ShipPartCount())
2263             m_ship_parts_owned[ship_part.first] += ship_part.second * design_count.second;
2264 
2265         // update count of ShipPartClasses
2266         for (const auto& part_class : design->PartClassCount())
2267             m_ship_part_class_owned[part_class.first] += part_class.second * design_count.second;
2268     }
2269 
2270     // colonies of each species, and unspecified outposts
2271     m_species_colonies_owned.clear();
2272     m_outposts_owned = 0;
2273     for (const auto& entry : Objects().ExistingPlanets()) {
2274         if (!entry.second->OwnedBy(this->EmpireID()))
2275             continue;
2276         auto planet = std::dynamic_pointer_cast<const Planet>(entry.second);
2277         if (!planet)
2278             continue;
2279         if (planet->SpeciesName().empty())
2280             m_outposts_owned++;
2281         else
2282             m_species_colonies_owned[planet->SpeciesName()]++;
2283     }
2284 
2285     // buildings of each type
2286     m_building_types_owned.clear();
2287     for (const auto& entry : Objects().ExistingBuildings()) {
2288         if (!entry.second->OwnedBy(this->EmpireID()))
2289             continue;
2290         auto building = std::dynamic_pointer_cast<const Building>(entry.second);
2291         if (!building)
2292             continue;
2293         m_building_types_owned[building->BuildingTypeName()]++;
2294     }
2295 }
2296 
SetAuthenticated(bool authenticated)2297 void Empire::SetAuthenticated(bool authenticated /*= true*/)
2298 { m_authenticated = authenticated; }
2299 
TotalShipsOwned() const2300 int Empire::TotalShipsOwned() const {
2301     // sum up counts for each ship design owned by this empire
2302     // (not using species ship counts, as an empire could potentially own a
2303     //  ship that has no species...)
2304     int counter = 0;
2305     for (const auto& entry : m_ship_designs_owned)
2306     { counter += entry.second; }
2307     return counter;
2308 }
2309 
RecordShipShotDown(const Ship & ship)2310 void Empire::RecordShipShotDown(const Ship& ship) {
2311     m_empire_ships_destroyed[ship.Owner()]++;
2312     m_ship_designs_destroyed[ship.DesignID()]++;
2313     m_species_ships_destroyed[ship.SpeciesName()]++;
2314 }
2315 
RecordShipLost(const Ship & ship)2316 void Empire::RecordShipLost(const Ship& ship) {
2317     m_species_ships_lost[ship.SpeciesName()]++;
2318     m_ship_designs_lost[ship.DesignID()]++;
2319 }
2320 
RecordShipScrapped(const Ship & ship)2321 void Empire::RecordShipScrapped(const Ship& ship) {
2322     m_ship_designs_scrapped[ship.DesignID()]++;
2323     m_species_ships_scrapped[ship.SpeciesName()]++;
2324 }
2325 
RecordBuildingScrapped(const Building & building)2326 void Empire::RecordBuildingScrapped(const Building& building) {
2327     m_building_types_scrapped[building.BuildingTypeName()]++;
2328 }
2329 
RecordPlanetInvaded(const Planet & planet)2330 void Empire::RecordPlanetInvaded(const Planet& planet) {
2331     m_species_planets_invaded[planet.SpeciesName()]++;
2332 }
2333 
RecordPlanetDepopulated(const Planet & planet)2334 void Empire::RecordPlanetDepopulated(const Planet& planet) {
2335     m_species_planets_depoped[planet.SpeciesName()]++;
2336 }
2337 
TotalShipPartsOwned() const2338 int Empire::TotalShipPartsOwned() const {
2339     // sum counts of all ship parts owned by this empire
2340     int retval = 0;
2341 
2342     for (const auto& part_class : m_ship_part_class_owned)
2343         retval += part_class.second;
2344 
2345     return retval;
2346 }
2347 
TotalBuildingsOwned() const2348 int Empire::TotalBuildingsOwned() const {
2349     // sum up counts for each building type owned by this empire
2350     int counter = 0;
2351     for (const auto& entry : m_building_types_owned)
2352     { counter += entry.second; }
2353     return counter;
2354 }
2355