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