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