1 #include "Species.h"
2 
3 #include "Conditions.h"
4 #include "Effect.h"
5 #include "PopCenter.h"
6 #include "Ship.h"
7 #include "UniverseObject.h"
8 #include "ValueRefs.h"
9 #include "Enums.h"
10 #include "../util/OptionsDB.h"
11 #include "../util/Logger.h"
12 #include "../util/Random.h"
13 #include "../util/AppInterface.h"
14 #include "../util/CheckSums.h"
15 #include "../util/ScopedTimer.h"
16 
17 #include <boost/filesystem/fstream.hpp>
18 
19 #include <iterator>
20 
21 
22 /////////////////////////////////////////////////
23 // FocusType                                   //
24 /////////////////////////////////////////////////
FocusType(const std::string & name,const std::string & description,std::unique_ptr<Condition::Condition> && location,const std::string & graphic)25 FocusType::FocusType(const std::string& name, const std::string& description,
26                      std::unique_ptr<Condition::Condition>&& location,
27                      const std::string& graphic) :
28     m_name(name),
29     m_description(description),
30     m_location(std::move(location)),
31     m_graphic(graphic)
32 {}
33 
~FocusType()34 FocusType::~FocusType()
35 {}
36 
Dump(unsigned short ntabs) const37 std::string FocusType::Dump(unsigned short ntabs) const {
38     std::string retval = DumpIndent(ntabs) + "FocusType\n";
39     retval += DumpIndent(ntabs+1) + "name = \"" + m_name + "\"\n";
40     retval += DumpIndent(ntabs+1) + "description = \"" + m_description + "\"\n";
41     retval += DumpIndent(ntabs+1) + "location = \n";
42     retval += m_location->Dump(ntabs+2);
43     retval += DumpIndent(ntabs+1) + "graphic = \"" + m_graphic + "\"\n";
44     return retval;
45 }
46 
GetCheckSum() const47 unsigned int FocusType::GetCheckSum() const {
48     unsigned int retval{0};
49 
50     CheckSums::CheckSumCombine(retval, m_name);
51     CheckSums::CheckSumCombine(retval, m_description);
52     CheckSums::CheckSumCombine(retval, m_location);
53     CheckSums::CheckSumCombine(retval, m_graphic);
54 
55     return retval;
56 }
57 
58 /////////////////////////////////////////////////
59 // Species                                     //
60 /////////////////////////////////////////////////
61 namespace {
PlanetTypeToString(PlanetType type)62     std::string PlanetTypeToString(PlanetType type) {
63         switch (type) {
64         case PT_SWAMP:      return "Swamp";
65         case PT_TOXIC:      return "Toxic";
66         case PT_INFERNO:    return "Inferno";
67         case PT_RADIATED:   return "Radiated";
68         case PT_BARREN:     return "Barren";
69         case PT_TUNDRA:     return "Tundra";
70         case PT_DESERT:     return "Desert";
71         case PT_TERRAN:     return "Terran";
72         case PT_OCEAN:      return "Ocean";
73         case PT_ASTEROIDS:  return "Asteroids";
74         case PT_GASGIANT:   return "GasGiant";
75         default:            return "?";
76         }
77     }
PlanetEnvironmentToString(PlanetEnvironment env)78     std::string PlanetEnvironmentToString(PlanetEnvironment env) {
79         switch (env) {
80         case PE_UNINHABITABLE:  return "Uninhabitable";
81         case PE_HOSTILE:        return "Hostile";
82         case PE_POOR:           return "Poor";
83         case PE_ADEQUATE:       return "Adequate";
84         case PE_GOOD:           return "Good";
85         default:                return "?";
86         }
87     }
88 }
89 
Species(const SpeciesStrings & strings,const std::vector<FocusType> & foci,const std::string & preferred_focus,const std::map<PlanetType,PlanetEnvironment> & planet_environments,std::vector<std::unique_ptr<Effect::EffectsGroup>> && effects,std::unique_ptr<Condition::Condition> && combat_targets,const SpeciesParams & params,const std::set<std::string> & tags,const std::string & graphic)90 Species::Species(const SpeciesStrings& strings,
91                  const std::vector<FocusType>& foci,
92                  const std::string& preferred_focus,
93                  const std::map<PlanetType, PlanetEnvironment>& planet_environments,
94                  std::vector<std::unique_ptr<Effect::EffectsGroup>>&& effects,
95                  std::unique_ptr<Condition::Condition>&& combat_targets,
96                  const SpeciesParams& params,
97                  const std::set<std::string>& tags,
98                  const std::string& graphic) :
99     m_name(strings.name),
100     m_description(strings.desc),
101     m_gameplay_description(strings.gameplay_desc),
102     m_foci(foci),
103     m_preferred_focus(preferred_focus),
104     m_planet_environments(planet_environments),
105     m_combat_targets(std::move(combat_targets)),
106     m_playable(params.playable),
107     m_native(params.native),
108     m_can_colonize(params.can_colonize),
109     m_can_produce_ships(params.can_produce_ships),
110     m_graphic(graphic)
111 {
112     for (auto&& effect : effects)
113         m_effects.emplace_back(std::move(effect));
114 
115     Init();
116 
117     for (const std::string& tag : tags)
118         m_tags.insert(boost::to_upper_copy<std::string>(tag));
119 }
120 
~Species()121 Species::~Species()
122 {}
123 
Init()124 void Species::Init() {
125     for (auto& effect : m_effects) {
126         effect->SetTopLevelContent(m_name);
127     }
128 
129     if (!m_location) {
130         // set up a Condition structure to match popcenters that have
131         // (not uninhabitable) environment for this species
132         std::vector<std::unique_ptr<ValueRef::ValueRef< ::PlanetEnvironment>>> environments_vec;
133         environments_vec.push_back(
134             std::make_unique<ValueRef::Constant<PlanetEnvironment>>( ::PE_UNINHABITABLE));
135         auto this_species_name_ref =
136             std::make_unique<ValueRef::Constant<std::string>>(m_name);  // m_name specifies this species
137         auto enviro_cond = std::unique_ptr<Condition::Condition>(
138             std::make_unique<Condition::Not>(
139                 std::unique_ptr<Condition::Condition>(
140                     std::make_unique<Condition::PlanetEnvironment>(
141                         std::move(environments_vec), std::move(this_species_name_ref)))));
142 
143         auto type_cond = std::unique_ptr<Condition::Condition>(std::make_unique<Condition::Type>(
144             std::make_unique<ValueRef::Constant<UniverseObjectType>>( ::OBJ_POP_CENTER)));
145 
146         std::vector<std::unique_ptr<Condition::Condition>> operands;
147         operands.push_back(std::move(enviro_cond));
148         operands.push_back(std::move(type_cond));
149 
150         m_location = std::unique_ptr<Condition::Condition>(std::make_unique<Condition::And>(std::move(operands)));
151     }
152     m_location->SetTopLevelContent(m_name);
153 
154     if (m_combat_targets)
155         m_combat_targets->SetTopLevelContent(m_name);
156 
157     TraceLogger() << "Species::Init: " << Dump();
158 }
159 
Dump(unsigned short ntabs) const160 std::string Species::Dump(unsigned short ntabs) const {
161     std::string retval = DumpIndent(ntabs) + "Species\n";
162     retval += DumpIndent(ntabs+1) + "name = \"" + m_name + "\"\n";
163     retval += DumpIndent(ntabs+1) + "description = \"" + m_description + "\"\n";
164     retval += DumpIndent(ntabs+1) + "gameplay_description = \"" + m_gameplay_description + "\"\n";
165     if (m_playable)
166         retval += DumpIndent(ntabs+1) + "Playable\n";
167     if (m_native)
168         retval += DumpIndent(ntabs+1) + "Native\n";
169     if (m_can_produce_ships)
170         retval += DumpIndent(ntabs+1) + "CanProduceShips\n";
171     if (m_can_colonize)
172         retval += DumpIndent(ntabs+1) + "CanColonize\n";
173     if (m_foci.size() == 1) {
174         retval += DumpIndent(ntabs+1) + "foci =\n";
175         m_foci.begin()->Dump(ntabs+1);
176     } else {
177         retval += DumpIndent(ntabs+1) + "foci = [\n";
178         for (const FocusType& focus : m_foci)
179             retval += focus.Dump(ntabs+2);
180         retval += DumpIndent(ntabs+1) + "]\n";
181     }
182     if (m_effects.size() == 1) {
183         retval += DumpIndent(ntabs+1) + "effectsgroups =\n";
184         retval += m_effects[0]->Dump(ntabs+2);
185     } else {
186         retval += DumpIndent(ntabs+1) + "effectsgroups = [\n";
187         for (auto& effect : m_effects)
188             retval += effect->Dump(ntabs+2);
189         retval += DumpIndent(ntabs+1) + "]\n";
190     }
191     if (m_combat_targets)
192         retval += DumpIndent(ntabs+1) + "combatTargets = " + m_combat_targets->Dump(ntabs+2);
193     if (m_planet_environments.size() == 1) {
194         retval += DumpIndent(ntabs+1) + "environments =\n";
195         retval += DumpIndent(ntabs+2) + "type = " + PlanetTypeToString(m_planet_environments.begin()->first)
196             + " environment = " + PlanetEnvironmentToString(m_planet_environments.begin()->second)
197             + "\n";
198     } else {
199         retval += DumpIndent(ntabs+1) + "environments = [\n";
200         for (const auto& entry : m_planet_environments) {
201             retval += DumpIndent(ntabs+2) + "type = " + PlanetTypeToString(entry.first)
202                 + " environment = " + PlanetEnvironmentToString(entry.second)
203                 + "\n";
204         }
205         retval += DumpIndent(ntabs+1) + "]\n";
206     }
207     retval += DumpIndent(ntabs+1) + "graphic = \"" + m_graphic + "\"\n";
208     return retval;
209 }
210 
GameplayDescription() const211 std::string Species::GameplayDescription() const {
212     std::stringstream result;
213 
214     result << UserString(m_gameplay_description);
215 
216     bool requires_separator = true;
217 
218     for (auto& effect : m_effects) {
219         const std::string& description = effect->GetDescription();
220         if (description.empty())
221             continue;
222 
223         if (requires_separator) {
224             result << "\n";
225             requires_separator = false;
226         }
227 
228         result << UserString(description) << "\n";
229     }
230 
231     return result.str();
232 }
233 
GetPlanetEnvironment(PlanetType planet_type) const234 PlanetEnvironment Species::GetPlanetEnvironment(PlanetType planet_type) const {
235     auto it = m_planet_environments.find(planet_type);
236     if (it == m_planet_environments.end())
237         return PE_UNINHABITABLE;
238     else
239         return it->second;
240 }
241 
242 namespace {
RingNextPlanetType(PlanetType current_type)243     PlanetType RingNextPlanetType(PlanetType current_type) {
244         PlanetType next(PlanetType(int(current_type)+1));
245         if (next >= PT_ASTEROIDS)
246             next = PT_SWAMP;
247         return next;
248     }
RingPreviousPlanetType(PlanetType current_type)249     PlanetType RingPreviousPlanetType(PlanetType current_type) {
250         PlanetType next(PlanetType(int(current_type)-1));
251         if (next <= INVALID_PLANET_TYPE)
252             next = PT_OCEAN;
253         return next;
254     }
255 }
256 
NextBetterPlanetType(PlanetType initial_planet_type) const257 PlanetType Species::NextBetterPlanetType(PlanetType initial_planet_type) const {
258     // some types can't be terraformed
259     if (initial_planet_type == PT_GASGIANT)
260         return PT_GASGIANT;
261     if (initial_planet_type == PT_ASTEROIDS)
262         return PT_ASTEROIDS;
263     if (initial_planet_type == INVALID_PLANET_TYPE)
264         return INVALID_PLANET_TYPE;
265     if (initial_planet_type == NUM_PLANET_TYPES)
266         return NUM_PLANET_TYPES;
267     // and sometimes there's no variation data
268     if (m_planet_environments.empty())
269         return initial_planet_type;
270 
271     // determine which environment rating is the best available for this species,
272     // excluding gas giants and asteroids
273     PlanetEnvironment best_environment = PE_UNINHABITABLE;
274     //std::set<PlanetType> best_types;
275     for (const auto& entry : m_planet_environments) {
276         if (entry.first < PT_ASTEROIDS) {
277             if (entry.second == best_environment) {
278                 //best_types.insert(entry.first);
279             } else if (entry.second > best_environment) {
280                 best_environment = entry.second;
281                 //best_types.clear();
282                 //best_types.insert(entry.first);
283             }
284         }
285     }
286 
287     // if no improvement available, abort early
288     PlanetEnvironment initial_environment = GetPlanetEnvironment(initial_planet_type);
289     if (initial_environment >= best_environment)
290         return initial_planet_type;
291 
292     // find which of the best types is closest to the current type
293     int forward_steps_to_best = 0;
294     for (PlanetType type = RingNextPlanetType(initial_planet_type); type != initial_planet_type; type = RingNextPlanetType(type)) {
295         forward_steps_to_best++;
296         if (GetPlanetEnvironment(type) == best_environment)
297             break;
298     }
299     int backward_steps_to_best = 0;
300     for (PlanetType type = RingPreviousPlanetType(initial_planet_type); type != initial_planet_type; type = RingPreviousPlanetType(type)) {
301         backward_steps_to_best++;
302         if (GetPlanetEnvironment(type) == best_environment)
303             break;
304     }
305     if (forward_steps_to_best <= backward_steps_to_best)
306         return RingNextPlanetType(initial_planet_type);
307     else
308         return RingPreviousPlanetType(initial_planet_type);
309 }
310 
AddHomeworld(int homeworld_id)311 void Species::AddHomeworld(int homeworld_id) {
312     if (!Objects().get(homeworld_id))
313         DebugLogger() << "Species asked to add homeworld id " << homeworld_id << " but there is no such object in the Universe";
314     if (m_homeworlds.count(homeworld_id))
315         return;
316     m_homeworlds.insert(homeworld_id);
317     // TODO if needed: StateChangedSignal();
318 }
319 
RemoveHomeworld(int homeworld_id)320 void Species::RemoveHomeworld(int homeworld_id) {
321     if (!m_homeworlds.count(homeworld_id)) {
322         DebugLogger() << "Species asked to remove homeworld id " << homeworld_id << " but doesn't have that id as a homeworld";
323         return;
324     }
325     m_homeworlds.erase(homeworld_id);
326     // TODO if needed: StateChangedSignal();
327 }
328 
SetHomeworlds(const std::set<int> & homeworld_ids)329 void Species::SetHomeworlds(const std::set<int>& homeworld_ids) {
330     if (m_homeworlds == homeworld_ids)
331         return;
332     m_homeworlds = homeworld_ids;
333     // TODO if needed: StateChangedSignal();
334 }
335 
SetEmpireOpinions(const std::map<int,double> & opinions)336 void Species::SetEmpireOpinions(const std::map<int, double>& opinions)
337 {}
338 
SetEmpireOpinion(int empire_id,double opinion)339 void Species::SetEmpireOpinion(int empire_id, double opinion)
340 {}
341 
SetOtherSpeciesOpinions(const std::map<std::string,double> & opinions)342 void Species::SetOtherSpeciesOpinions(const std::map<std::string, double>& opinions)
343 {}
344 
SetOtherSpeciesOpinion(const std::string & species_name,double opinion)345 void Species::SetOtherSpeciesOpinion(const std::string& species_name, double opinion)
346 {}
347 
GetCheckSum() const348 unsigned int Species::GetCheckSum() const {
349     unsigned int retval{0};
350 
351     CheckSums::CheckSumCombine(retval, m_name);
352     CheckSums::CheckSumCombine(retval, m_description);
353     CheckSums::CheckSumCombine(retval, m_gameplay_description);
354     // opinions and homeworlds are per-game specific, so not included in checksum
355     CheckSums::CheckSumCombine(retval, m_foci);
356     CheckSums::CheckSumCombine(retval, m_preferred_focus);
357     CheckSums::CheckSumCombine(retval, m_planet_environments);
358     CheckSums::CheckSumCombine(retval, m_combat_targets);
359     CheckSums::CheckSumCombine(retval, m_effects);
360     CheckSums::CheckSumCombine(retval, m_location);
361     CheckSums::CheckSumCombine(retval, m_playable);
362     CheckSums::CheckSumCombine(retval, m_native);
363     CheckSums::CheckSumCombine(retval, m_can_colonize);
364     CheckSums::CheckSumCombine(retval, m_can_produce_ships);
365     CheckSums::CheckSumCombine(retval, m_tags);
366     CheckSums::CheckSumCombine(retval, m_graphic);
367 
368     return retval;
369 }
370 
371 /////////////////////////////////////////////////
372 // SpeciesManager                              //
373 /////////////////////////////////////////////////
374 // static(s)
375 SpeciesManager* SpeciesManager::s_instance = nullptr;
376 
operator ()(const std::map<std::string,std::unique_ptr<Species>>::value_type & species_entry) const377 bool SpeciesManager::PlayableSpecies::operator()(
378     const std::map<std::string, std::unique_ptr<Species>>::value_type& species_entry) const
379 { return species_entry.second->Playable(); }
380 
operator ()(const std::map<std::string,std::unique_ptr<Species>>::value_type & species_entry) const381 bool SpeciesManager::NativeSpecies::operator()(
382     const std::map<std::string, std::unique_ptr<Species>>::value_type& species_entry) const
383 { return species_entry.second->Native(); }
384 
SpeciesManager()385 SpeciesManager::SpeciesManager() {
386     if (s_instance)
387         throw std::runtime_error("Attempted to create more than one SpeciesManager.");
388 
389     // Only update the global pointer on sucessful construction.
390     s_instance = this;
391 }
392 
GetSpecies(const std::string & name) const393 const Species* SpeciesManager::GetSpecies(const std::string& name) const {
394     CheckPendingSpeciesTypes();
395     auto it = m_species.find(name);
396     return it != m_species.end() ? it->second.get() : nullptr;
397 }
398 
GetSpecies(const std::string & name)399 Species* SpeciesManager::GetSpecies(const std::string& name) {
400     CheckPendingSpeciesTypes();
401     auto it = m_species.find(name);
402     return it != m_species.end() ? it->second.get() : nullptr;
403 }
404 
GetSpeciesID(const std::string & name) const405 int SpeciesManager::GetSpeciesID(const std::string& name) const {
406     CheckPendingSpeciesTypes();
407     auto it = m_species.find(name);
408     if (it == m_species.end())
409         return -1;
410     return std::distance(m_species.begin(), it);
411 }
412 
GetSpeciesManager()413 SpeciesManager& SpeciesManager::GetSpeciesManager() {
414     static SpeciesManager manager;
415     return manager;
416 }
417 
SetSpeciesTypes(Pending::Pending<std::pair<SpeciesTypeMap,CensusOrder>> && future)418 void SpeciesManager::SetSpeciesTypes(Pending::Pending<std::pair<SpeciesTypeMap, CensusOrder>>&& future)
419 { m_pending_types = std::move(future); }
420 
CheckPendingSpeciesTypes() const421 void SpeciesManager::CheckPendingSpeciesTypes() const {
422     if (!m_pending_types) {
423         if (m_species.empty())
424             throw;
425         return;
426     }
427 
428     auto container = std::make_pair(std::move(m_species), m_census_order);
429 
430     Pending::SwapPending(m_pending_types, container);
431 
432     m_species = std::move(container.first);
433     m_census_order = std::move(container.second);
434 }
435 
begin() const436 SpeciesManager::iterator SpeciesManager::begin() const {
437     CheckPendingSpeciesTypes();
438     return m_species.begin();
439 }
440 
end() const441 SpeciesManager::iterator SpeciesManager::end() const {
442     CheckPendingSpeciesTypes();
443     return m_species.end();
444 }
445 
playable_begin() const446 SpeciesManager::playable_iterator SpeciesManager::playable_begin() const
447 { return playable_iterator(PlayableSpecies(), begin(), end()); }
448 
playable_end() const449 SpeciesManager::playable_iterator SpeciesManager::playable_end() const
450 { return playable_iterator(PlayableSpecies(), end(), end()); }
451 
native_begin() const452 SpeciesManager::native_iterator SpeciesManager::native_begin() const
453 { return native_iterator(NativeSpecies(), begin(), end()); }
454 
native_end() const455 SpeciesManager::native_iterator SpeciesManager::native_end() const
456 { return native_iterator(NativeSpecies(), end(), end()); }
457 
census_order() const458 const SpeciesManager::CensusOrder& SpeciesManager::census_order() const {
459     CheckPendingSpeciesTypes();
460     return m_census_order;
461 }
462 
empty() const463 bool SpeciesManager::empty() const {
464     CheckPendingSpeciesTypes();
465     return m_species.empty();
466 }
467 
NumSpecies() const468 int SpeciesManager::NumSpecies() const {
469     CheckPendingSpeciesTypes();
470     return m_species.size();
471 }
472 
NumPlayableSpecies() const473 int SpeciesManager::NumPlayableSpecies() const
474 { return std::distance(playable_begin(), playable_end()); }
475 
NumNativeSpecies() const476 int SpeciesManager::NumNativeSpecies() const
477 { return std::distance(native_begin(), native_end()); }
478 
479 namespace {
480     const std::string EMPTY_STRING;
481 }
482 
RandomSpeciesName() const483 const std::string& SpeciesManager::RandomSpeciesName() const {
484     CheckPendingSpeciesTypes();
485     if (m_species.empty())
486         return EMPTY_STRING;
487 
488     int species_idx = RandSmallInt(0, static_cast<int>(m_species.size()) - 1);
489     return std::next(begin(), species_idx)->first;
490 }
491 
RandomPlayableSpeciesName() const492 const std::string& SpeciesManager::RandomPlayableSpeciesName() const {
493     if (NumPlayableSpecies() <= 0)
494         return EMPTY_STRING;
495 
496     int species_idx = RandSmallInt(0, NumPlayableSpecies() - 1);
497     return std::next(playable_begin(), species_idx)->first;
498 }
499 
SequentialPlayableSpeciesName(int id) const500 const std::string& SpeciesManager::SequentialPlayableSpeciesName(int id) const {
501     if (NumPlayableSpecies() <= 0)
502         return EMPTY_STRING;
503 
504     int species_idx = id % NumPlayableSpecies();
505     DebugLogger() << "SpeciesManager::SequentialPlayableSpeciesName has " << NumPlayableSpecies() << " and is given id " << id << " yielding index " << species_idx;
506     return std::next(playable_begin(), species_idx)->first;
507 }
508 
ClearSpeciesHomeworlds()509 void SpeciesManager::ClearSpeciesHomeworlds() {
510     CheckPendingSpeciesTypes();
511     for (auto& entry : m_species)
512         entry.second->SetHomeworlds(std::set<int>());
513 }
514 
SetSpeciesHomeworlds(const std::map<std::string,std::set<int>> & species_homeworld_ids)515 void SpeciesManager::SetSpeciesHomeworlds(const std::map<std::string, std::set<int>>& species_homeworld_ids) {
516     CheckPendingSpeciesTypes();
517     ClearSpeciesHomeworlds();
518     for (auto& entry : species_homeworld_ids) {
519         const std::string& species_name = entry.first;
520         const std::set<int>& homeworlds = entry.second;
521 
522         Species* species = nullptr;
523         auto species_it = m_species.find(species_name);
524         if (species_it != end())
525             species = species_it->second.get();
526 
527         if (species) {
528             species->SetHomeworlds(homeworlds);
529         } else {
530             ErrorLogger() << "SpeciesManager::SetSpeciesHomeworlds couldn't find a species with name " << species_name << " to assign homeworlds to";
531         }
532     }
533 }
534 
SetSpeciesEmpireOpinions(const std::map<std::string,std::map<int,float>> & species_empire_opinions)535 void SpeciesManager::SetSpeciesEmpireOpinions(const std::map<std::string, std::map<int, float>>& species_empire_opinions)
536 { m_species_empire_opinions = species_empire_opinions; }
537 
SetSpeciesEmpireOpinion(const std::string & species_name,int empire_id,float opinion)538 void SpeciesManager::SetSpeciesEmpireOpinion(const std::string& species_name, int empire_id, float opinion)
539 { m_species_empire_opinions[species_name][empire_id] = opinion; }
540 
SetSpeciesSpeciesOpinions(const std::map<std::string,std::map<std::string,float>> & species_species_opinions)541 void SpeciesManager::SetSpeciesSpeciesOpinions(const std::map<std::string, std::map<std::string, float>>& species_species_opinions)
542 { m_species_species_opinions = species_species_opinions; }
543 
SetSpeciesSpeciesOpinion(const std::string & opinionated_species,const std::string & rated_species,float opinion)544 void SpeciesManager::SetSpeciesSpeciesOpinion(const std::string& opinionated_species, const std::string& rated_species, float opinion)
545 { m_species_species_opinions[opinionated_species][rated_species] = opinion; }
546 
GetSpeciesHomeworldsMap(int encoding_empire) const547 std::map<std::string, std::set<int>> SpeciesManager::GetSpeciesHomeworldsMap(int encoding_empire/* = ALL_EMPIRES*/) const {
548     CheckPendingSpeciesTypes();
549     std::map<std::string, std::set<int>> retval;
550     for (const auto& entry : m_species) {
551         const std::string species_name = entry.first;
552         const Species* species = entry.second.get();
553         if (!species) {
554             ErrorLogger() << "SpeciesManager::GetSpeciesHomeworldsMap found a null species pointer in SpeciesManager?!";
555             continue;
556         }
557         for (int homeworld_id : species->Homeworlds())
558             retval[species_name].insert(homeworld_id);
559     }
560     return retval;
561 }
562 
GetSpeciesEmpireOpinionsMap(int encoding_empire) const563 const std::map<std::string, std::map<int, float>>& SpeciesManager::GetSpeciesEmpireOpinionsMap(int encoding_empire/* = ALL_EMPIRES*/) const
564 { return m_species_empire_opinions; }
565 
GetSpeciesSpeciesOpinionsMap(int encoding_empire) const566 const std::map<std::string, std::map<std::string, float>>& SpeciesManager::GetSpeciesSpeciesOpinionsMap(int encoding_empire/* = ALL_EMPIRES*/) const
567 { return m_species_species_opinions; }
568 
SpeciesEmpireOpinion(const std::string & species_name,int empire_id) const569 float SpeciesManager::SpeciesEmpireOpinion(const std::string& species_name, int empire_id) const {
570     auto sp_it = m_species_empire_opinions.find(species_name);
571     if (sp_it == m_species_empire_opinions.end())
572         return 0.0f;
573     const std::map<int, float>& emp_map = sp_it->second;
574     auto emp_it = emp_map.find(empire_id);
575     if (emp_it == emp_map.end())
576         return 0.0f;
577     return emp_it->second;
578 }
579 
SpeciesSpeciesOpinion(const std::string & opinionated_species_name,const std::string & rated_species_name) const580 float SpeciesManager::SpeciesSpeciesOpinion(const std::string& opinionated_species_name, const std::string& rated_species_name) const {
581     auto sp_it = m_species_species_opinions.find(opinionated_species_name);
582     if (sp_it == m_species_species_opinions.end())
583         return 0.0f;
584     const auto& ra_sp_map = sp_it->second;
585     auto ra_sp_it = ra_sp_map.find(rated_species_name);
586     if (ra_sp_it == ra_sp_map.end())
587         return 0.0f;
588     return ra_sp_it->second;
589 }
590 
ClearSpeciesOpinions()591 void SpeciesManager::ClearSpeciesOpinions() {
592     m_species_empire_opinions.clear();
593     m_species_species_opinions.clear();
594 }
595 
UpdatePopulationCounter()596 void SpeciesManager::UpdatePopulationCounter() {
597     // ships of each species and design
598     m_species_object_populations.clear();
599     for (const auto& entry : Objects().ExistingObjects()) {
600         auto obj = entry.second;
601         if (obj->ObjectType() != OBJ_PLANET && obj->ObjectType() != OBJ_POP_CENTER)
602             continue;
603 
604         auto pop_center = std::dynamic_pointer_cast<const PopCenter>(obj);
605         if (!pop_center)
606             continue;
607 
608         const std::string& species = pop_center->SpeciesName();
609         if (species.empty())
610             continue;
611 
612         try {
613             m_species_object_populations[species][obj->ID()] += obj->GetMeter(METER_POPULATION)->Current();
614         } catch (...) {
615             continue;
616         }
617     }
618 }
619 
SpeciesObjectPopulations(int encoding_empire)620 std::map<std::string, std::map<int, float>>& SpeciesManager::SpeciesObjectPopulations(int encoding_empire)
621 { return m_species_object_populations; }
622 
SpeciesShipsDestroyed(int encoding_empire)623 std::map<std::string, std::map<std::string, int>>& SpeciesManager::SpeciesShipsDestroyed(int encoding_empire)
624 { return m_species_species_ships_destroyed; }
625 
GetCheckSum() const626 unsigned int SpeciesManager::GetCheckSum() const {
627     CheckPendingSpeciesTypes();
628     unsigned int retval{0};
629     for (auto const& name_type_pair : m_species)
630         CheckSums::CheckSumCombine(retval, name_type_pair);
631     CheckSums::CheckSumCombine(retval, m_species.size());
632 
633     DebugLogger() << "SpeciesManager checksum: " << retval;
634     return retval;
635 }
636 
637 ///////////////////////////////////////////////////////////
638 // Free Functions                                        //
639 ///////////////////////////////////////////////////////////
GetSpeciesManager()640 SpeciesManager& GetSpeciesManager()
641 { return SpeciesManager::GetSpeciesManager(); }
642 
GetSpecies(const std::string & name)643 const Species* GetSpecies(const std::string& name)
644 { return SpeciesManager::GetSpeciesManager().GetSpecies(name); }
645