1 // _________ __ __
2 // / _____// |_____________ _/ |______ ____ __ __ ______
3 // \_____ \\ __\_ __ \__ \\ __\__ \ / ___\| | \/ ___/
4 // / \| | | | \// __ \| | / __ \_/ /_/ > | /\___ |
5 // /_______ /|__| |__| (____ /__| (____ /\___ /|____//____ >
6 // \/ \/ \//_____/ \/
7 // ______________________ ______________________
8 // T H E W A R B E G I N S
9 // Stratagus - A free fantasy real time strategy game engine
10 //
11 /**@name character.cpp - The character source file. */
12 //
13 // (c) Copyright 2015-2019 by Andrettin
14 //
15 // This program is free software; you can redistribute it and/or modify
16 // it under the terms of the GNU General Public License as published by
17 // the Free Software Foundation; only version 2 of the License.
18 //
19 // This program is distributed in the hope that it will be useful,
20 // but WITHOUT ANY WARRANTY; without even the implied warranty of
21 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 // GNU General Public License for more details.
23 //
24 // You should have received a copy of the GNU General Public License
25 // along with this program; if not, write to the Free Software
26 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
27 // 02111-1307, USA.
28 //
29
30 //@{
31
32 /*----------------------------------------------------------------------------
33 -- Includes
34 ----------------------------------------------------------------------------*/
35
36 #include "stratagus.h"
37
38 #include "character.h"
39
40 #include <ctype.h>
41
42 #include <string>
43 #include <map>
44
45 #include "civilization.h"
46 #include "config.h"
47 #include "game.h"
48 #include "iocompat.h"
49 #include "iolib.h"
50 #include "item.h"
51 #include "map/historical_location.h"
52 #include "map/map_template.h"
53 #include "parameters.h"
54 #include "player.h"
55 #include "province.h"
56 #include "quest.h"
57 #include "religion/deity.h"
58 #include "religion/deity_domain.h"
59 #include "spells.h"
60 #include "time/calendar.h"
61 #include "unit/unit.h"
62 #include "unit/unit_type_variation.h"
63 #include "upgrade/upgrade.h"
64 #include "upgrade/upgrade_modifier.h"
65
66 #include "../ai/ai_local.h" //for using AiHelpers
67
68 /*----------------------------------------------------------------------------
69 -- Variables
70 ----------------------------------------------------------------------------*/
71
72 std::vector<CCharacter *> CCharacter::Characters;
73 std::map<std::string, CCharacter *> CCharacter::CharactersByIdent;
74 std::map<std::string, CCharacter *> CustomHeroes;
75 CCharacter *CurrentCustomHero = nullptr;
76 bool LoadingPersistentHeroes = false;
77
78 /*----------------------------------------------------------------------------
79 -- Functions
80 ----------------------------------------------------------------------------*/
81
GetCharacter(const std::string & ident,const bool should_find)82 CCharacter *CCharacter::GetCharacter(const std::string &ident, const bool should_find)
83 {
84 std::map<std::string, CCharacter *>::const_iterator find_iterator = CCharacter::CharactersByIdent.find(ident);
85
86 if (find_iterator != CCharacter::CharactersByIdent.end()) {
87 return find_iterator->second;
88 }
89
90 for (CCharacter *character : CCharacter::Characters) { // for backwards compatibility
91 if (character->GetFullName() == ident) {
92 return character;
93 }
94 }
95
96 if (should_find) {
97 fprintf(stderr, "Invalid character: \"%s\".\n", ident.c_str());
98 }
99
100 return nullptr;
101 }
102
GetOrAddCharacter(const std::string & ident)103 CCharacter *CCharacter::GetOrAddCharacter(const std::string &ident)
104 {
105 CCharacter *character = CCharacter::GetCharacter(ident, false);
106
107 if (!character) {
108 character = new CCharacter;
109 character->Ident = ident;
110 CCharacter::Characters.push_back(character);
111 CCharacter::CharactersByIdent[ident] = character;
112 }
113
114 return character;
115 }
116
ClearCharacters()117 void CCharacter::ClearCharacters()
118 {
119 for (CCharacter *character : CCharacter::Characters) {
120 for (CPersistentItem *item : character->Items) {
121 delete item;
122 }
123 character->Items.clear();
124 delete character;
125 }
126 CCharacter::Characters.clear();
127 CCharacter::CharactersByIdent.clear();
128
129 for (std::map<std::string, CCharacter *>::iterator iterator = CustomHeroes.begin(); iterator != CustomHeroes.end(); ++iterator) {
130 for (CPersistentItem *item : iterator->second->Items) {
131 delete item;
132 }
133 iterator->second->Items.clear();
134 delete iterator->second;
135 }
136 CustomHeroes.clear();
137 }
138
~CCharacter()139 CCharacter::~CCharacter()
140 {
141 if (this->Conditions) {
142 delete Conditions;
143 }
144
145 for (size_t i = 0; i < this->HistoricalLocations.size(); ++i) {
146 delete this->HistoricalLocations[i];
147 }
148 }
149
GenerateCharacterHistory()150 void CCharacter::GenerateCharacterHistory()
151 {
152 for (CCharacter *character : CCharacter::Characters) {
153 character->GenerateHistory();
154 }
155 }
156
ResetCharacterHistory()157 void CCharacter::ResetCharacterHistory()
158 {
159 for (CCharacter *character : CCharacter::Characters) {
160 character->ResetHistory();
161 }
162 }
163
164 /**
165 ** @brief Process data provided by a configuration file
166 **
167 ** @param config_data The configuration data
168 */
ProcessConfigData(const CConfigData * config_data)169 void CCharacter::ProcessConfigData(const CConfigData *config_data)
170 {
171 if (this->Initialized) {
172 fprintf(stderr, "Character \"%s\" is being redefined.\n", this->Ident.c_str());
173 }
174
175 bool name_changed = false;
176 bool family_name_changed = false;
177
178 for (size_t i = 0; i < config_data->Properties.size(); ++i) {
179 std::string key = config_data->Properties[i].first;
180 std::string value = config_data->Properties[i].second;
181
182 if (key == "name") {
183 this->Name = value;
184 name_changed = true;
185 } else if (key == "family_name") {
186 this->FamilyName = value;
187 family_name_changed = true;
188 } else if (key == "unit_type") {
189 value = FindAndReplaceString(value, "_", "-");
190 CUnitType *unit_type = UnitTypeByIdent(value);
191 if (unit_type) {
192 if (this->Type == nullptr || this->Type == unit_type || this->Type->CanExperienceUpgradeTo(unit_type)) {
193 this->Type = unit_type;
194 if (this->Level < this->Type->DefaultStat.Variables[LEVEL_INDEX].Value) {
195 this->Level = this->Type->DefaultStat.Variables[LEVEL_INDEX].Value;
196 }
197
198 if (this->Gender == NoGender) { //if no gender was set so far, have the character be the same gender as the unit type (if the unit type has it predefined)
199 if (this->Type->DefaultStat.Variables[GENDER_INDEX].Value != 0) {
200 this->Gender = this->Type->DefaultStat.Variables[GENDER_INDEX].Value;
201 }
202 }
203 }
204 } else {
205 fprintf(stderr, "Unit type \"%s\" does not exist.\n", value.c_str());
206 }
207 } else if (key == "gender") {
208 this->Gender = GetGenderIdByName(value);
209 } else if (key == "civilization") {
210 value = FindAndReplaceString(value, "_", "-");
211 this->Civilization = CCivilization::GetCivilization(value);
212 } else if (key == "faction") {
213 value = FindAndReplaceString(value, "_", "-");
214 CFaction *faction = PlayerRaces.GetFaction(value);
215 if (faction) {
216 if (!this->Faction) {
217 this->Faction = faction;
218 }
219 this->Factions.push_back(faction);
220 } else {
221 fprintf(stderr, "Faction \"%s\" does not exist.\n", value.c_str());
222 }
223 } else if (key == "hair_variation") {
224 value = FindAndReplaceString(value, "_", "-");
225 this->HairVariation = value;
226 } else if (key == "trait") {
227 value = FindAndReplaceString(value, "_", "-");
228 CUpgrade *upgrade = CUpgrade::Get(value);
229 if (upgrade) {
230 this->Trait = upgrade;
231 } else {
232 fprintf(stderr, "Upgrade \"%s\" does not exist.\n", value.c_str());
233 }
234 } else if (key == "level") {
235 this->Level = std::stoi(value);
236 } else if (key == "birth_date") {
237 value = FindAndReplaceString(value, "_", "-");
238 this->BirthDate = CDate::FromString(value);
239 } else if (key == "start_date") {
240 value = FindAndReplaceString(value, "_", "-");
241 this->StartDate = CDate::FromString(value);
242 } else if (key == "death_date") {
243 value = FindAndReplaceString(value, "_", "-");
244 this->DeathDate = CDate::FromString(value);
245 } else if (key == "violent_death") {
246 this->ViolentDeath = StringToBool(value);
247 } else if (key == "deity") {
248 value = FindAndReplaceString(value, "_", "-");
249 CDeity *deity = CDeity::GetDeity(value);
250 if (deity) {
251 this->Deities.push_back(deity);
252 if (LoadingHistory) {
253 this->GeneratedDeities.push_back(deity);
254 }
255 }
256 } else if (key == "description") {
257 this->Description = value;
258 } else if (key == "background") {
259 this->Background = value;
260 } else if (key == "quote") {
261 this->Quote = value;
262 } else if (key == "icon") {
263 value = FindAndReplaceString(value, "_", "-");
264 this->Icon.Name = value;
265 this->Icon.Icon = nullptr;
266 this->Icon.Load();
267 this->Icon.Icon->Load();
268 } else if (key == "heroic_icon") {
269 value = FindAndReplaceString(value, "_", "-");
270 this->HeroicIcon.Name = value;
271 this->HeroicIcon.Icon = nullptr;
272 this->HeroicIcon.Load();
273 this->HeroicIcon.Icon->Load();
274 } else if (key == "forbidden_upgrade") {
275 value = FindAndReplaceString(value, "_", "-");
276 CUnitType *unit_type = UnitTypeByIdent(value);
277 if (unit_type) {
278 this->ForbiddenUpgrades.push_back(unit_type);
279 } else {
280 fprintf(stderr, "Unit type \"%s\" does not exist.\n", value.c_str());
281 }
282 } else if (key == "ability") {
283 value = FindAndReplaceString(value, "_", "-");
284 CUpgrade *ability_upgrade = CUpgrade::Get(value);
285 if (ability_upgrade) {
286 this->Abilities.push_back(ability_upgrade);
287 } else {
288 fprintf(stderr, "Upgrade \"%s\" does not exist.\n", value.c_str());
289 }
290 } else if (key == "read_work") {
291 value = FindAndReplaceString(value, "_", "-");
292 CUpgrade *upgrade = CUpgrade::Get(value);
293 if (upgrade) {
294 this->ReadWorks.push_back(upgrade);
295 } else {
296 fprintf(stderr, "Upgrade \"%s\" does not exist.\n", value.c_str());
297 }
298 } else if (key == "consumed_elixir") {
299 value = FindAndReplaceString(value, "_", "-");
300 CUpgrade *upgrade = CUpgrade::Get(value);
301 if (upgrade) {
302 this->ConsumedElixirs.push_back(upgrade);
303 } else {
304 fprintf(stderr, "Upgrade \"%s\" does not exist.\n", value.c_str());
305 }
306 } else if (key == "preferred_deity_domain") {
307 value = FindAndReplaceString(value, "_", "-");
308 CDeityDomain *deity_domain = CDeityDomain::GetDeityDomain(value);
309 if (deity_domain) {
310 this->PreferredDeityDomains.push_back(deity_domain);
311 }
312 } else {
313 fprintf(stderr, "Invalid character property: \"%s\".\n", key.c_str());
314 }
315 }
316
317 for (const CConfigData *child_config_data : config_data->Children) {
318 if (child_config_data->Tag == "historical_location") {
319 CHistoricalLocation *historical_location = new CHistoricalLocation;
320 historical_location->ProcessConfigData(child_config_data);
321
322 if (historical_location->Date.Year == 0 || !historical_location->MapTemplate) {
323 delete historical_location;
324 continue;
325 }
326
327 this->HistoricalLocations.push_back(historical_location);
328 } else if (child_config_data->Tag == "historical_title") {
329 int title = -1;
330 CDate start_date;
331 CDate end_date;
332 CFaction *title_faction = nullptr;
333
334 for (size_t j = 0; j < child_config_data->Properties.size(); ++j) {
335 std::string key = child_config_data->Properties[j].first;
336 std::string value = child_config_data->Properties[j].second;
337
338 if (key == "title") {
339 value = FindAndReplaceString(value, "_", "-");
340 title = GetCharacterTitleIdByName(value);
341 if (title == -1) {
342 fprintf(stderr, "Character title \"%s\" does not exist.\n", value.c_str());
343 }
344 } else if (key == "start_date") {
345 value = FindAndReplaceString(value, "_", "-");
346 start_date = CDate::FromString(value);
347 } else if (key == "end_date") {
348 value = FindAndReplaceString(value, "_", "-");
349 end_date = CDate::FromString(value);
350 } else if (key == "faction") {
351 value = FindAndReplaceString(value, "_", "-");
352 title_faction = PlayerRaces.GetFaction(value);
353 if (!title_faction) {
354 fprintf(stderr, "Faction \"%s\" does not exist.\n", value.c_str());
355 }
356 } else {
357 fprintf(stderr, "Invalid historical title property: \"%s\".\n", key.c_str());
358 }
359 }
360
361 if (title == -1) {
362 fprintf(stderr, "Historical title has no title.\n");
363 continue;
364 }
365
366 if (!title_faction) {
367 fprintf(stderr, "Historical title has no faction.\n");
368 continue;
369 }
370
371 if (start_date.Year != 0 && end_date.Year != 0 && IsMinisterialTitle(title)) { // don't put in the faction's historical data if a blank year was given
372 title_faction->HistoricalMinisters[std::tuple<CDate, CDate, int>(start_date, end_date, title)] = this;
373 }
374
375 this->HistoricalTitles.push_back(std::tuple<CDate, CDate, CFaction *, int>(start_date, end_date, title_faction, title));
376 } else if (child_config_data->Tag == "item") {
377 CPersistentItem *item = new CPersistentItem;
378 item->Owner = this;
379 this->Items.push_back(item);
380 item->ProcessConfigData(child_config_data);
381 } else {
382 fprintf(stderr, "Invalid character property: \"%s\".\n", child_config_data->Tag.c_str());
383 }
384 }
385
386 //use the character's name for name generation (do this only after setting all properties so that the type, civilization and gender will have been parsed if given
387 if (this->Type != nullptr && this->Type->BoolFlag[FAUNA_INDEX].value) {
388 if (name_changed) {
389 this->Type->PersonalNames[this->Gender].push_back(this->Name);
390 }
391 } else if (this->Civilization) {
392 if (name_changed) {
393 this->Civilization->PersonalNames[this->Gender].push_back(this->Name);
394 }
395 if (family_name_changed) {
396 this->Civilization->FamilyNames.push_back(this->FamilyName);
397 }
398 }
399
400 if (this->Trait == nullptr) { //if no trait was set, have the character be the same trait as the unit type (if the unit type has a single one predefined)
401 if (this->Type != nullptr && this->Type->Traits.size() == 1) {
402 this->Trait = this->Type->Traits[0];
403 }
404 }
405
406 //check if the abilities are correct for this character's unit type
407 if (this->Type != nullptr && this->Abilities.size() > 0 && ((int) AiHelpers.LearnableAbilities.size()) > this->Type->Slot) {
408 int ability_count = (int) this->Abilities.size();
409 for (int i = (ability_count - 1); i >= 0; --i) {
410 if (std::find(AiHelpers.LearnableAbilities[this->Type->Slot].begin(), AiHelpers.LearnableAbilities[this->Type->Slot].end(), this->Abilities[i]) == AiHelpers.LearnableAbilities[this->Type->Slot].end()) {
411 this->Abilities.erase(std::remove(this->Abilities.begin(), this->Abilities.end(), this->Abilities[i]), this->Abilities.end());
412 }
413 }
414 }
415
416 this->GenerateMissingDates();
417 this->UpdateAttributes();
418
419 this->Initialized = true;
420 }
421
422 /**
423 ** @brief Generate missing data for the character as a part of history generation
424 */
GenerateHistory()425 void CCharacter::GenerateHistory()
426 {
427 //generate history (but skip already-generated data)
428 bool history_changed = false;
429
430 if (!this->PreferredDeityDomains.empty() && this->Deities.size() < PlayerDeityMax && this->CanWorship()) { //if the character can worship deities, but worships less deities than the maximum, generate them for the character
431 int deities_missing = PlayerDeityMax - this->Deities.size();
432 CReligion *character_religion = this->GetReligion();
433
434 for (int i = 0; i < deities_missing; ++i) {
435 std::vector<CDeity *> potential_deities;
436 int best_score = 0;
437 const bool has_major_deity = this->HasMajorDeity();
438
439 for (size_t j = 0; j < CDeity::Deities.size(); ++j) {
440 CDeity *deity = CDeity::Deities[j];
441
442 if (!deity->DeityUpgrade) {
443 continue; //don't use deities that have no corresponding deity upgrade for the character
444 }
445
446 if (deity->Major == has_major_deity) {
447 continue; //try to get a major deity if the character has none, or a minor deity otherwise
448 }
449
450 if (std::find(deity->Civilizations.begin(), deity->Civilizations.end(), this->Civilization) == deity->Civilizations.end()) {
451 continue;
452 }
453
454 if (character_religion && std::find(deity->Religions.begin(), deity->Religions.end(), character_religion) == deity->Religions.end()) {
455 continue; //the deity should be compatible with the character's religion
456 }
457
458 int score = 0;
459
460 bool has_domains = true;
461 for (size_t k = 0; k < this->PreferredDeityDomains.size(); ++k) {
462 if (std::find(deity->Domains.begin(), deity->Domains.end(), this->PreferredDeityDomains[k]) != deity->Domains.end()) {
463 score++;
464 }
465 }
466
467 if (score < best_score) {
468 continue;
469 } else if (score > best_score) {
470 potential_deities.clear();
471 best_score = score;
472 }
473
474 potential_deities.push_back(deity);
475 }
476
477 if (!potential_deities.empty()) {
478 CDeity *chosen_deity = potential_deities[SyncRand(potential_deities.size())];
479 this->Deities.push_back(chosen_deity);
480 this->GeneratedDeities.push_back(chosen_deity);
481
482 if (!character_religion) {
483 character_religion = this->GetReligion();
484 }
485
486 history_changed = true;
487 } else {
488 fprintf(stderr, "Could not generate deity for character: \"%s\".\n", this->Ident.c_str());
489 }
490 }
491 }
492
493 if (history_changed) {
494 this->SaveHistory();
495 }
496 }
497
498 /**
499 ** @brief Reset generated history for the character
500 */
ResetHistory()501 void CCharacter::ResetHistory()
502 {
503 for (size_t i = 0; i < this->GeneratedDeities.size(); ++i) {
504 this->Deities.erase(std::remove(this->Deities.begin(), this->Deities.end(), this->GeneratedDeities[i]), this->Deities.end());
505 }
506
507 this->GeneratedDeities.clear();
508 }
509
510 /**
511 ** @brief Save generated history for the character
512 */
SaveHistory()513 void CCharacter::SaveHistory()
514 {
515 struct stat tmp;
516 std::string path = Parameters::Instance.GetUserDirectory();
517
518 if (!GameName.empty()) {
519 path += "/";
520 path += GameName;
521 }
522 path += "/";
523 path += "history/";
524 if (stat(path.c_str(), &tmp) < 0) {
525 makedir(path.c_str(), 0777);
526 }
527 path += "characters/";
528 if (stat(path.c_str(), &tmp) < 0) {
529 makedir(path.c_str(), 0777);
530 }
531 path += FindAndReplaceString(this->Ident, "-", "_");
532 path += ".cfg";
533
534 FILE *fd = fopen(path.c_str(), "w");
535 if (!fd) {
536 fprintf(stderr, "Cannot open file %s for writing.\n", path.c_str());
537 return;
538 }
539
540 fprintf(fd, "[character]\n");
541 fprintf(fd, "\tident = %s\n", FindAndReplaceString(this->Ident, "-", "_").c_str());
542 for (size_t i = 0; i < this->GeneratedDeities.size(); ++i) {
543 fprintf(fd, "\tdeity = %s\n", FindAndReplaceString(this->GeneratedDeities[i]->Ident, "-", "_").c_str());
544 }
545 fprintf(fd, "[/character]\n");
546
547 fclose(fd);
548 }
549
GenerateMissingDates()550 void CCharacter::GenerateMissingDates()
551 {
552 if (this->DeathDate.Year == 0 && this->BirthDate.Year != 0) { //if the character is missing a death date so far, give it +60 years after the birth date
553 this->DeathDate.Year = this->BirthDate.Year + 60;
554 this->DeathDate.Month = this->BirthDate.Month;
555 this->DeathDate.Day = this->BirthDate.Day;
556 this->DeathDate.Timeline = this->BirthDate.Timeline;
557 }
558
559 if (this->BirthDate.Year == 0 && this->DeathDate.Year != 0) { //if the character is missing a birth date so far, give it 60 years before the death date
560 this->BirthDate.Year = this->DeathDate.Year - 60;
561 this->BirthDate.Month = this->DeathDate.Month;
562 this->BirthDate.Day = this->DeathDate.Day;
563 this->BirthDate.Timeline = this->DeathDate.Timeline;
564 }
565
566 if (this->StartDate.Year == 0 && this->BirthDate.Year != 0) { //if the character is missing a start date so far, give it +30 years after the birth date
567 this->StartDate.Year = this->BirthDate.Year + 30;
568 this->StartDate.Month = this->BirthDate.Month;
569 this->StartDate.Day = this->BirthDate.Day;
570 this->StartDate.Timeline = this->BirthDate.Timeline;
571 }
572
573 if (this->BirthDate.Year == 0 && this->StartDate.Year != 0) { //if the character is missing a birth date so far, give it 30 years before the start date
574 this->BirthDate.Year = this->StartDate.Year - 30;
575 this->BirthDate.Month = this->StartDate.Month;
576 this->BirthDate.Day = this->StartDate.Day;
577 this->BirthDate.Timeline = this->StartDate.Timeline;
578 }
579
580 if (this->DeathDate.Year == 0 && this->StartDate.Year != 0) { //if the character is missing a death date so far, give it +30 years after the start date
581 this->DeathDate.Year = this->StartDate.Year + 30;
582 this->DeathDate.Month = this->StartDate.Month;
583 this->DeathDate.Day = this->StartDate.Day;
584 this->DeathDate.Timeline = this->StartDate.Timeline;
585 }
586
587 if (this->StartDate.Year == 0 && this->DeathDate.Year != 0) { //if the character is missing a start date so far, give it 30 years before the death date
588 this->StartDate.Year = this->DeathDate.Year - 30;
589 this->StartDate.Month = this->DeathDate.Month;
590 this->StartDate.Day = this->DeathDate.Day;
591 this->StartDate.Timeline = this->DeathDate.Timeline;
592 }
593 }
594
GetMartialAttribute() const595 int CCharacter::GetMartialAttribute() const
596 {
597 if ((this->Type->Class != -1 && UnitTypeClasses[this->Type->Class] == "thief") || this->Type->DefaultStat.Variables[ATTACKRANGE_INDEX].Value > 1) {
598 return DexterityAttribute;
599 } else {
600 return StrengthAttribute;
601 }
602 }
603
GetAttributeModifier(int attribute) const604 int CCharacter::GetAttributeModifier(int attribute) const
605 {
606 return this->Attributes[attribute] - 10;
607 }
608
609 /**
610 ** @brief Get the character's religion
611 **
612 ** @return The religion if found, or null otherwise
613 */
GetReligion() const614 CReligion *CCharacter::GetReligion() const
615 {
616 //get the first religion of the character's first deity, since at present we don't set the religion directly for the character
617
618 for (size_t i = 0; i < this->Deities.size(); ++i) {
619 if (!this->Deities[i]->Religions.empty()) {
620 return this->Deities[i]->Religions[0];
621 }
622 }
623
624 return nullptr;
625 }
626
GetLanguage() const627 CLanguage *CCharacter::GetLanguage() const
628 {
629 return PlayerRaces.GetCivilizationLanguage(this->Civilization->ID);
630 }
631
GetCalendar() const632 CCalendar *CCharacter::GetCalendar() const
633 {
634 if (this->Civilization) {
635 return this->Civilization->GetCalendar();
636 }
637
638 return CCalendar::BaseCalendar;
639 }
640
IsParentOf(const std::string & child_ident) const641 bool CCharacter::IsParentOf(const std::string &child_ident) const
642 {
643 for (size_t i = 0; i < this->Children.size(); ++i) {
644 if (this->Children[i]->Ident == child_ident) {
645 return true;
646 }
647 }
648 return false;
649 }
650
IsChildOf(const std::string & parent_ident) const651 bool CCharacter::IsChildOf(const std::string &parent_ident) const
652 {
653 if ((this->Father != nullptr && this->Father->Ident == parent_ident) || (this->Mother != nullptr && this->Mother->Ident == parent_ident)) {
654 return true;
655 }
656 return false;
657 }
658
IsSiblingOf(const std::string & sibling_ident) const659 bool CCharacter::IsSiblingOf(const std::string &sibling_ident) const
660 {
661 for (size_t i = 0; i < this->Siblings.size(); ++i) {
662 if (this->Siblings[i]->Ident == sibling_ident) {
663 return true;
664 }
665 }
666 return false;
667 }
668
IsItemEquipped(const CPersistentItem * item) const669 bool CCharacter::IsItemEquipped(const CPersistentItem *item) const
670 {
671 int item_slot = GetItemClassSlot(item->Type->ItemClass);
672
673 if (item_slot == -1) {
674 return false;
675 }
676
677 if (std::find(EquippedItems[item_slot].begin(), EquippedItems[item_slot].end(), item) != EquippedItems[item_slot].end()) {
678 return true;
679 }
680
681 return false;
682 }
683
IsUsable() const684 bool CCharacter::IsUsable() const
685 {
686 if (this->Type->DefaultStat.Variables[GENDER_INDEX].Value != 0 && this->Gender != this->Type->DefaultStat.Variables[GENDER_INDEX].Value) {
687 return false; // hero not usable if their unit type has a set gender which is different from the hero's (this is because this means that the unit type lacks appropriate graphics for that gender)
688 }
689
690 return true;
691 }
692
CanAppear(bool ignore_neutral) const693 bool CCharacter::CanAppear(bool ignore_neutral) const
694 {
695 if (!this->IsUsable()) {
696 return false;
697 }
698
699 for (int i = 0; i < PlayerMax; ++i) {
700 if (ignore_neutral && i == PlayerNumNeutral) {
701 continue;
702 }
703 if (Players[i].HasHero(this)) {
704 return false;
705 }
706 }
707
708 return true;
709 }
710
711 /**
712 ** @brief Get whether the character can worship a deity
713 **
714 ** @return True if the character can worship, false otherwise
715 */
CanWorship() const716 bool CCharacter::CanWorship() const
717 {
718 if (this->Deity) {
719 return false; //the character cannot worship a deity if it is itself a deity
720 }
721
722 if (this->Type->BoolFlag[FAUNA_INDEX].value) {
723 return false; //the character cannot worship a deity if it is not sentient
724 }
725
726 return true;
727 }
728
729 /**
730 ** @brief Get whether the character has a major deity in its worshipped deities list
731 **
732 ** @return True if the character has a major deity, false otherwise
733 */
HasMajorDeity() const734 bool CCharacter::HasMajorDeity() const
735 {
736 for (size_t i = 0; i < this->Deities.size(); ++i) {
737 if (this->Deities[i]->Major) {
738 return true;
739 }
740 }
741
742 return false;
743 }
744
GetFullName() const745 std::string CCharacter::GetFullName() const
746 {
747 std::string full_name = this->Name;
748 if (!this->ExtraName.empty()) {
749 full_name += " " + this->ExtraName;
750 }
751 if (!this->FamilyName.empty()) {
752 full_name += " " + this->FamilyName;
753 }
754 return full_name;
755 }
756
GetIcon() const757 IconConfig CCharacter::GetIcon() const
758 {
759 if (this->Level >= 3 && this->HeroicIcon.Icon) {
760 return this->HeroicIcon;
761 } else if (this->Icon.Icon) {
762 return this->Icon;
763 } else if (!this->HairVariation.empty() && this->Type->GetVariation(this->HairVariation) != nullptr && !this->Type->GetVariation(this->HairVariation)->Icon.Name.empty()) {
764 return this->Type->GetVariation(this->HairVariation)->Icon;
765 } else {
766 return this->Type->Icon;
767 }
768 }
769
GetItem(CUnit & item) const770 CPersistentItem *CCharacter::GetItem(CUnit &item) const
771 {
772 for (size_t i = 0; i < this->Items.size(); ++i) {
773 if (this->Items[i]->Type == item.Type && this->Items[i]->Prefix == item.Prefix && this->Items[i]->Suffix == item.Suffix && this->Items[i]->Spell == item.Spell && this->Items[i]->Work == item.Work && this->Items[i]->Elixir == item.Elixir && this->Items[i]->Unique == item.Unique && this->Items[i]->Bound == item.Bound && this->Items[i]->Identified == item.Identified && this->IsItemEquipped(this->Items[i]) == item.Container->IsItemEquipped(&item)) {
774 if (this->Items[i]->Name.empty() || this->Items[i]->Name == item.Name) {
775 return this->Items[i];
776 }
777 }
778 }
779 return nullptr;
780 }
781
UpdateAttributes()782 void CCharacter::UpdateAttributes()
783 {
784 if (this->Type == nullptr) {
785 return;
786 }
787
788 for (int i = 0; i < MaxAttributes; ++i) {
789 int var = GetAttributeVariableIndex(i);
790 this->Attributes[i] = this->Type->DefaultStat.Variables[var].Value;
791 for (const CUpgradeModifier *modifier : CUpgradeModifier::UpgradeModifiers) {
792 if (
793 (this->Trait != nullptr && modifier->UpgradeId == this->Trait->ID)
794 || std::find(this->Abilities.begin(), this->Abilities.end(), AllUpgrades[modifier->UpgradeId]) != this->Abilities.end()
795 ) {
796 if (modifier->Modifier.Variables[var].Value != 0) {
797 this->Attributes[i] += modifier->Modifier.Variables[var].Value;
798 }
799 }
800 }
801 }
802 }
803
GetAttributeVariableIndex(int attribute)804 int GetAttributeVariableIndex(int attribute)
805 {
806 if (attribute == StrengthAttribute) {
807 return STRENGTH_INDEX;
808 } else if (attribute == DexterityAttribute) {
809 return DEXTERITY_INDEX;
810 } else if (attribute == IntelligenceAttribute) {
811 return INTELLIGENCE_INDEX;
812 } else if (attribute == CharismaAttribute) {
813 return CHARISMA_INDEX;
814 } else {
815 return -1;
816 }
817 }
818
GetCustomHero(const std::string & hero_ident)819 CCharacter *GetCustomHero(const std::string &hero_ident)
820 {
821 if (CustomHeroes.find(hero_ident) != CustomHeroes.end()) {
822 return CustomHeroes[hero_ident];
823 }
824
825 for (std::map<std::string, CCharacter *>::iterator iterator = CustomHeroes.begin(); iterator != CustomHeroes.end(); ++iterator) { // for backwards compatibility
826 if (iterator->second->GetFullName() == hero_ident) {
827 return iterator->second;
828 }
829 }
830
831 return nullptr;
832 }
833
834 /**
835 ** Save heroes
836 */
SaveHeroes()837 void SaveHeroes()
838 {
839 for (CCharacter *character : CCharacter::Characters) { //save characters
840 SaveHero(character);
841 }
842
843 for (std::map<std::string, CCharacter *>::iterator iterator = CustomHeroes.begin(); iterator != CustomHeroes.end(); ++iterator) { //save custom heroes
844 SaveHero(iterator->second);
845 }
846
847 //see if the old heroes.lua save file is present, and if so, delete it
848 std::string path = Parameters::Instance.GetUserDirectory();
849
850 if (!GameName.empty()) {
851 path += "/";
852 path += GameName;
853 }
854 path += "/";
855 path += "heroes.lua";
856
857 if (CanAccessFile(path.c_str())) {
858 unlink(path.c_str());
859 }
860 }
861
SaveHero(CCharacter * hero)862 void SaveHero(CCharacter *hero)
863 {
864 struct stat tmp;
865 std::string path = Parameters::Instance.GetUserDirectory();
866
867 if (!GameName.empty()) {
868 path += "/";
869 path += GameName;
870 }
871 path += "/";
872 path += "heroes/";
873 if (stat(path.c_str(), &tmp) < 0) {
874 makedir(path.c_str(), 0777);
875 }
876 if (hero->Custom) {
877 path += "custom/";
878 if (stat(path.c_str(), &tmp) < 0) {
879 makedir(path.c_str(), 0777);
880 }
881 }
882 std::string old_path = path;
883 path += hero->Ident;
884 path += ".lua";
885 old_path += hero->GetFullName();
886 old_path += ".lua";
887 if (CanAccessFile(old_path.c_str())) {
888 unlink(old_path.c_str());
889 }
890
891 FILE *fd = fopen(path.c_str(), "w");
892 if (!fd) {
893 fprintf(stderr, "Cannot open file %s for writing.\n", path.c_str());
894 return;
895 }
896
897 if (!hero->Custom) {
898 fprintf(fd, "DefineCharacter(\"%s\", {\n", hero->Ident.c_str());
899 } else {
900 fprintf(fd, "DefineCustomHero(\"%s\", {\n", hero->Ident.c_str());
901 fprintf(fd, "\tName = \"%s\",\n", hero->Name.c_str());
902 if (!hero->ExtraName.empty()) {
903 fprintf(fd, "\tExtraName = \"%s\",\n", hero->ExtraName.c_str());
904 }
905 if (!hero->FamilyName.empty()) {
906 fprintf(fd, "\tFamilyName = \"%s\",\n", hero->FamilyName.c_str());
907 }
908 if (hero->Gender != NoGender) {
909 fprintf(fd, "\tGender = \"%s\",\n", GetGenderNameById(hero->Gender).c_str());
910 }
911 if (hero->Civilization) {
912 fprintf(fd, "\tCivilization = \"%s\",\n", PlayerRaces.Name[hero->Civilization->ID].c_str());
913 }
914 }
915 if (hero->Type != nullptr) {
916 fprintf(fd, "\tType = \"%s\",\n", hero->Type->Ident.c_str());
917 }
918 if (hero->Custom) {
919 if (hero->Trait != nullptr) {
920 fprintf(fd, "\tTrait = \"%s\",\n", hero->Trait->Ident.c_str());
921 }
922 if (!hero->HairVariation.empty()) {
923 fprintf(fd, "\tHairVariation = \"%s\",\n", hero->HairVariation.c_str());
924 }
925 }
926 if (hero->Level != 0) {
927 fprintf(fd, "\tLevel = %d,\n", hero->Level);
928 }
929 if (hero->ExperiencePercent != 0) {
930 fprintf(fd, "\tExperiencePercent = %d,\n", hero->ExperiencePercent);
931 }
932 if (hero->Abilities.size() > 0) {
933 fprintf(fd, "\tAbilities = {");
934 for (size_t j = 0; j < hero->Abilities.size(); ++j) {
935 fprintf(fd, "\"%s\"", hero->Abilities[j]->Ident.c_str());
936 if (j < (hero->Abilities.size() - 1)) {
937 fprintf(fd, ", ");
938 }
939 }
940 fprintf(fd, "},\n");
941 }
942 if (hero->Custom && hero->Deities.size() > 0) {
943 fprintf(fd, "\tDeities = {");
944 for (size_t j = 0; j < hero->Deities.size(); ++j) {
945 fprintf(fd, "\"%s\"", hero->Deities[j]->Ident.c_str());
946 if (j < (hero->Deities.size() - 1)) {
947 fprintf(fd, ", ");
948 }
949 }
950 fprintf(fd, "},\n");
951 }
952 if (hero->ReadWorks.size() > 0) {
953 fprintf(fd, "\tReadWorks = {");
954 for (size_t j = 0; j < hero->ReadWorks.size(); ++j) {
955 fprintf(fd, "\"%s\"", hero->ReadWorks[j]->Ident.c_str());
956 if (j < (hero->ReadWorks.size() - 1)) {
957 fprintf(fd, ", ");
958 }
959 }
960 fprintf(fd, "},\n");
961 }
962 if (hero->ConsumedElixirs.size() > 0) {
963 fprintf(fd, "\tConsumedElixirs = {");
964 for (size_t j = 0; j < hero->ConsumedElixirs.size(); ++j) {
965 fprintf(fd, "\"%s\"", hero->ConsumedElixirs[j]->Ident.c_str());
966 if (j < (hero->ConsumedElixirs.size() - 1)) {
967 fprintf(fd, ", ");
968 }
969 }
970 fprintf(fd, "},\n");
971 }
972 if (hero->Items.size() > 0) {
973 fprintf(fd, "\tItems = {");
974 for (size_t j = 0; j < hero->Items.size(); ++j) {
975 fprintf(fd, "\n\t\t{");
976 fprintf(fd, "\n\t\t\t\"type\", \"%s\",", hero->Items[j]->Type->Ident.c_str());
977 if (hero->Items[j]->Prefix != nullptr) {
978 fprintf(fd, "\n\t\t\t\"prefix\", \"%s\",", hero->Items[j]->Prefix->Ident.c_str());
979 }
980 if (hero->Items[j]->Suffix != nullptr) {
981 fprintf(fd, "\n\t\t\t\"suffix\", \"%s\",", hero->Items[j]->Suffix->Ident.c_str());
982 }
983 if (hero->Items[j]->Spell != nullptr) {
984 fprintf(fd, "\n\t\t\t\"spell\", \"%s\",", hero->Items[j]->Spell->Ident.c_str());
985 }
986 if (hero->Items[j]->Work != nullptr) {
987 fprintf(fd, "\n\t\t\t\"work\", \"%s\",", hero->Items[j]->Work->Ident.c_str());
988 }
989 if (hero->Items[j]->Elixir != nullptr) {
990 fprintf(fd, "\n\t\t\t\"elixir\", \"%s\",", hero->Items[j]->Elixir->Ident.c_str());
991 }
992 if (!hero->Items[j]->Name.empty()) {
993 fprintf(fd, "\n\t\t\t\"name\", \"%s\",", hero->Items[j]->Name.c_str());
994 }
995 if (hero->Items[j]->Unique) { // affixes, name and etc. will be inherited from the unique item, but we set those previous characteristics for unique items anyway, so that if a unique item no longer exists in the game's code (i.e. if it is from a mod that has been deactivated) the character retains an item with the same affixes, name and etc., even though it will no longer be unique
996 fprintf(fd, "\n\t\t\t\"unique\", \"%s\",", hero->Items[j]->Unique->Ident.c_str());
997 }
998 if (hero->Items[j]->Bound) {
999 fprintf(fd, "\n\t\t\t\"bound\", true,");
1000 }
1001 if (!hero->Items[j]->Identified) {
1002 fprintf(fd, "\n\t\t\t\"identified\", false,");
1003 }
1004 if (hero->IsItemEquipped(hero->Items[j])) {
1005 fprintf(fd, "\n\t\t\t\"equipped\", true");
1006 }
1007 fprintf(fd, "\n\t\t}");
1008 if (j < (hero->Items.size() - 1)) {
1009 fprintf(fd, ",");
1010 }
1011 }
1012 fprintf(fd, "\n\t},\n");
1013 }
1014
1015 if (hero->Custom) {
1016 if (hero->QuestsInProgress.size() > 0) {
1017 fprintf(fd, "\tQuestsInProgress = {");
1018 for (size_t j = 0; j < hero->QuestsInProgress.size(); ++j) {
1019 fprintf(fd, "\"%s\"", hero->QuestsInProgress[j]->Name.c_str());
1020 if (j < (hero->QuestsInProgress.size() - 1)) {
1021 fprintf(fd, ", ");
1022 }
1023 }
1024 fprintf(fd, "},\n");
1025 }
1026 if (hero->QuestsCompleted.size() > 0) {
1027 fprintf(fd, "\tQuestsCompleted = {");
1028 for (size_t j = 0; j < hero->QuestsCompleted.size(); ++j) {
1029 fprintf(fd, "\"%s\"", hero->QuestsCompleted[j]->Name.c_str());
1030 if (j < (hero->QuestsCompleted.size() - 1)) {
1031 fprintf(fd, ", ");
1032 }
1033 }
1034 fprintf(fd, "},\n");
1035 }
1036 }
1037 fprintf(fd, "})\n\n");
1038
1039 fclose(fd);
1040 }
1041
HeroAddQuest(const std::string & hero_full_name,const std::string & quest_name)1042 void HeroAddQuest(const std::string &hero_full_name, const std::string &quest_name)
1043 {
1044 CCharacter *hero = GetCustomHero(hero_full_name);
1045 if (!hero) {
1046 fprintf(stderr, "Custom hero \"%s\" does not exist.\n", hero_full_name.c_str());
1047 }
1048
1049 CQuest *quest = GetQuest(quest_name);
1050 if (!quest) {
1051 fprintf(stderr, "Quest \"%s\" does not exist.\n", quest_name.c_str());
1052 }
1053
1054 hero->QuestsInProgress.push_back(quest);
1055 }
1056
HeroCompleteQuest(const std::string & hero_full_name,const std::string & quest_name)1057 void HeroCompleteQuest(const std::string &hero_full_name, const std::string &quest_name)
1058 {
1059 CCharacter *hero = GetCustomHero(hero_full_name);
1060 if (!hero) {
1061 fprintf(stderr, "Custom hero \"%s\" does not exist.\n", hero_full_name.c_str());
1062 }
1063
1064 CQuest *quest = GetQuest(quest_name);
1065 if (!quest) {
1066 fprintf(stderr, "Quest \"%s\" does not exist.\n", quest_name.c_str());
1067 }
1068
1069 hero->QuestsInProgress.erase(std::remove(hero->QuestsInProgress.begin(), hero->QuestsInProgress.end(), quest), hero->QuestsInProgress.end());
1070 hero->QuestsCompleted.push_back(quest);
1071 }
1072
SaveCustomHero(const std::string & hero_full_name)1073 void SaveCustomHero(const std::string &hero_full_name)
1074 {
1075 CCharacter *hero = GetCustomHero(hero_full_name);
1076 if (!hero) {
1077 fprintf(stderr, "Custom hero \"%s\" does not exist.\n", hero_full_name.c_str());
1078 }
1079
1080 SaveHero(hero);
1081 }
1082
DeleteCustomHero(const std::string & hero_full_name)1083 void DeleteCustomHero(const std::string &hero_full_name)
1084 {
1085 CCharacter *hero = GetCustomHero(hero_full_name);
1086 if (!hero) {
1087 fprintf(stderr, "Custom hero \"%s\" does not exist.\n", hero_full_name.c_str());
1088 }
1089
1090 if (CurrentCustomHero == hero) {
1091 CurrentCustomHero = nullptr;
1092 }
1093
1094 //delete hero save file
1095 std::string path = Parameters::Instance.GetUserDirectory();
1096 if (!GameName.empty()) {
1097 path += "/";
1098 path += GameName;
1099 }
1100 path += "/";
1101 path += "heroes/";
1102 if (hero->Custom) {
1103 path += "custom/";
1104 }
1105 path += hero->Ident;
1106 path += ".lua";
1107 if (CanAccessFile(path.c_str())) {
1108 unlink(path.c_str());
1109 }
1110
1111 CustomHeroes.erase(hero_full_name);
1112 delete hero;
1113 }
1114
SetCurrentCustomHero(const std::string & hero_full_name)1115 void SetCurrentCustomHero(const std::string &hero_full_name)
1116 {
1117 if (!hero_full_name.empty()) {
1118 CCharacter *hero = GetCustomHero(hero_full_name);
1119 if (!hero) {
1120 fprintf(stderr, "Custom hero \"%s\" does not exist.\n", hero_full_name.c_str());
1121 }
1122
1123 CurrentCustomHero = const_cast<CCharacter *>(&(*hero));
1124 } else {
1125 CurrentCustomHero = nullptr;
1126 }
1127 }
1128
GetCurrentCustomHero()1129 std::string GetCurrentCustomHero()
1130 {
1131 if (CurrentCustomHero != nullptr) {
1132 return CurrentCustomHero->Ident;
1133 } else {
1134 return "";
1135 }
1136 }
1137
ChangeCustomHeroCivilization(const std::string & hero_full_name,const std::string & civilization_name,const std::string & new_hero_name,const std::string & new_hero_family_name)1138 void ChangeCustomHeroCivilization(const std::string &hero_full_name, const std::string &civilization_name, const std::string &new_hero_name, const std::string &new_hero_family_name)
1139 {
1140 if (!hero_full_name.empty()) {
1141 CCharacter *hero = GetCustomHero(hero_full_name);
1142 if (!hero) {
1143 fprintf(stderr, "Custom hero \"%s\" does not exist.\n", hero_full_name.c_str());
1144 }
1145
1146 CCivilization *civilization = CCivilization::GetCivilization(civilization_name);
1147 if (civilization) {
1148 //delete old hero save file
1149 std::string path = Parameters::Instance.GetUserDirectory();
1150 if (!GameName.empty()) {
1151 path += "/";
1152 path += GameName;
1153 }
1154 path += "/";
1155 path += "heroes/";
1156 if (hero->Custom) {
1157 path += "custom/";
1158 }
1159 path += hero->Ident;
1160 path += ".lua";
1161 if (CanAccessFile(path.c_str())) {
1162 unlink(path.c_str());
1163 }
1164
1165 //now, update the hero
1166 hero->Civilization = civilization;
1167 int new_unit_type_id = PlayerRaces.GetCivilizationClassUnitType(hero->Civilization->ID, hero->Type->Class);
1168 if (new_unit_type_id != -1) {
1169 hero->Type = const_cast<CUnitType *>(&(*UnitTypes[new_unit_type_id]));
1170 hero->Name = new_hero_name;
1171 hero->FamilyName = new_hero_family_name;
1172 SaveHero(hero);
1173
1174 CustomHeroes.erase(hero_full_name);
1175 CustomHeroes[hero->Ident] = hero;
1176 }
1177 }
1178 }
1179 }
1180
IsNameValidForCustomHero(const std::string & hero_name,const std::string & hero_family_name)1181 bool IsNameValidForCustomHero(const std::string &hero_name, const std::string &hero_family_name)
1182 {
1183 std::string hero_full_name = hero_name;
1184 if (!hero_family_name.empty()) {
1185 hero_full_name += " ";
1186 hero_full_name += hero_family_name;
1187 }
1188
1189 if (GetCustomHero(hero_full_name) != nullptr) {
1190 return false; //name already used
1191 }
1192
1193 if (hero_name.empty()) {
1194 return false;
1195 }
1196
1197 if (
1198 hero_full_name.find('\n') != -1
1199 || hero_full_name.find('\\') != -1
1200 || hero_full_name.find('/') != -1
1201 || hero_full_name.find('.') != -1
1202 || hero_full_name.find('*') != -1
1203 || hero_full_name.find('[') != -1
1204 || hero_full_name.find(']') != -1
1205 || hero_full_name.find(':') != -1
1206 || hero_full_name.find(';') != -1
1207 || hero_full_name.find('=') != -1
1208 || hero_full_name.find(',') != -1
1209 || hero_full_name.find('<') != -1
1210 || hero_full_name.find('>') != -1
1211 || hero_full_name.find('?') != -1
1212 || hero_full_name.find('|') != -1
1213 ) {
1214 return false;
1215 }
1216
1217 if (hero_name.find_first_not_of(' ') == std::string::npos) {
1218 return false; //name contains only spaces
1219 }
1220
1221 if (!hero_family_name.empty() && hero_family_name.find_first_not_of(' ') == std::string::npos) {
1222 return false; //family name contains only spaces
1223 }
1224
1225 return true;
1226 }
1227
GetGenderNameById(int gender)1228 std::string GetGenderNameById(int gender)
1229 {
1230 if (gender == NoGender) {
1231 return "no-gender";
1232 } else if (gender == MaleGender) {
1233 return "male";
1234 } else if (gender == FemaleGender) {
1235 return "female";
1236 } else if (gender == AsexualGender) {
1237 return "asexual";
1238 }
1239
1240 return "";
1241 }
1242
GetGenderIdByName(const std::string & gender)1243 int GetGenderIdByName(const std::string &gender)
1244 {
1245 if (gender == "no-gender") {
1246 return NoGender;
1247 } else if (gender == "male") {
1248 return MaleGender;
1249 } else if (gender == "female") {
1250 return FemaleGender;
1251 } else if (gender == "asexual") {
1252 return AsexualGender;
1253 }
1254
1255 return -1;
1256 }
1257
GetCharacterTitleNameById(int title)1258 std::string GetCharacterTitleNameById(int title)
1259 {
1260 if (title == CharacterTitleHeadOfState) {
1261 return "head-of-state";
1262 } else if (title == CharacterTitleHeadOfGovernment) {
1263 return "head-of-government";
1264 } else if (title == CharacterTitleEducationMinister) {
1265 return "education-minister";
1266 } else if (title == CharacterTitleFinanceMinister) {
1267 return "finance-minister";
1268 } else if (title == CharacterTitleForeignMinister) {
1269 return "foreign-minister";
1270 } else if (title == CharacterTitleIntelligenceMinister) {
1271 return "intelligence-minister";
1272 } else if (title == CharacterTitleInteriorMinister) {
1273 return "interior-minister";
1274 } else if (title == CharacterTitleJusticeMinister) {
1275 return "justice-minister";
1276 } else if (title == CharacterTitleWarMinister) {
1277 return "war-minister";
1278 } else if (title == CharacterTitleGovernor) {
1279 return "governor";
1280 } else if (title == CharacterTitleMayor) {
1281 return "mayor";
1282 }
1283
1284 return "";
1285 }
1286
GetCharacterTitleIdByName(const std::string & title)1287 int GetCharacterTitleIdByName(const std::string &title)
1288 {
1289 if (title == "head-of-state") {
1290 return CharacterTitleHeadOfState;
1291 } else if (title == "head-of-government") {
1292 return CharacterTitleHeadOfGovernment;
1293 } else if (title == "education-minister") {
1294 return CharacterTitleEducationMinister;
1295 } else if (title == "finance-minister") {
1296 return CharacterTitleFinanceMinister;
1297 } else if (title == "foreign-minister") {
1298 return CharacterTitleForeignMinister;
1299 } else if (title == "intelligence-minister") {
1300 return CharacterTitleIntelligenceMinister;
1301 } else if (title == "interior-minister") {
1302 return CharacterTitleInteriorMinister;
1303 } else if (title == "justice-minister") {
1304 return CharacterTitleJusticeMinister;
1305 } else if (title == "war-minister") {
1306 return CharacterTitleWarMinister;
1307 } else if (title == "governor") {
1308 return CharacterTitleGovernor;
1309 } else if (title == "mayor") {
1310 return CharacterTitleMayor;
1311 }
1312
1313 return -1;
1314 }
1315
IsMinisterialTitle(int title)1316 bool IsMinisterialTitle(int title)
1317 {
1318 return (title == CharacterTitleHeadOfState || title == CharacterTitleHeadOfGovernment || title == CharacterTitleEducationMinister || title == CharacterTitleFinanceMinister || title == CharacterTitleForeignMinister || title == CharacterTitleIntelligenceMinister || title == CharacterTitleInteriorMinister || title == CharacterTitleJusticeMinister || title == CharacterTitleWarMinister);
1319 }
1320 //@}
1321