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 script_character.cpp - The character ccl functions. */
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 "civilization.h"
41 #include "grand_strategy.h"
42 #include "map/historical_location.h"
43 #include "map/map_template.h"
44 #include "map/site.h"
45 #include "player.h"
46 #include "province.h"
47 #include "quest.h"
48 #include "religion/deity.h"
49 #include "script.h"
50 #include "spells.h"
51 #include "time/timeline.h"
52 #include "unit/unittype.h"
53 #include "upgrade/upgrade.h"
54 
55 #include "../ai/ai_local.h" //for using AiHelpers
56 
57 /*----------------------------------------------------------------------------
58 --  Variables
59 ----------------------------------------------------------------------------*/
60 
61 /*----------------------------------------------------------------------------
62 --  Functions
63 ----------------------------------------------------------------------------*/
64 
65 /**
66 **  Define a character.
67 **
68 **  @param l  Lua state.
69 */
CclDefineCharacter(lua_State * l)70 static int CclDefineCharacter(lua_State *l)
71 {
72 	LuaCheckArgs(l, 2);
73 	if (!lua_istable(l, 2)) {
74 		LuaError(l, "incorrect argument (expected table)");
75 	}
76 
77 	std::string character_ident = LuaToString(l, 1);
78 	CCharacter *character = CCharacter::GetCharacter(character_ident, false);
79 	bool redefinition = false;
80 	if (!character) {
81 		if (LoadingPersistentHeroes) {
82 			fprintf(stderr, "Character \"%s\" has persistent data, but doesn't exist.", character_ident.c_str());
83 			return 0;
84 		}
85 		character = CCharacter::GetOrAddCharacter(character_ident);
86 	} else {
87 		redefinition = true;
88 		if (!LoadingPersistentHeroes) {
89 			fprintf(stderr, "Character \"%s\" is being redefined.\n", character_ident.c_str());
90 		}
91 	}
92 
93 	std::string faction_ident;
94 	std::vector<std::string> alternate_names;
95 
96 	//  Parse the list:
97 	for (lua_pushnil(l); lua_next(l, 2); lua_pop(l, 1)) {
98 		const char *value = LuaToString(l, -2);
99 
100 		if (!strcmp(value, "Name")) {
101 			character->Name = LuaToString(l, -1);
102 		} else if (!strcmp(value, "AlternateNames")) { // alternate names the character may have, used for building the civilization's personal names
103 			const int args = lua_rawlen(l, -1);
104 			for (int j = 0; j < args; ++j) {
105 				alternate_names.push_back(LuaToString(l, -1, j + 1));
106 			}
107 		} else if (!strcmp(value, "ExtraName")) {
108 			character->ExtraName = LuaToString(l, -1);
109 		} else if (!strcmp(value, "FamilyName")) {
110 			character->FamilyName = LuaToString(l, -1);
111 		} else if (!strcmp(value, "Description")) {
112 			character->Description = LuaToString(l, -1);
113 		} else if (!strcmp(value, "Background")) {
114 			character->Background = LuaToString(l, -1);
115 		} else if (!strcmp(value, "Quote")) {
116 			character->Quote = LuaToString(l, -1);
117 		} else if (!strcmp(value, "Variation")) { //to keep backwards compatibility
118 			character->HairVariation = LuaToString(l, -1);
119 		} else if (!strcmp(value, "HairVariation")) {
120 			character->HairVariation = LuaToString(l, -1);
121 		} else if (!strcmp(value, "Type")) {
122 			std::string unit_type_ident = LuaToString(l, -1);
123 			int unit_type_id = UnitTypeIdByIdent(unit_type_ident);
124 			if (unit_type_id != -1) {
125 				if (character->Type == nullptr || character->Type == UnitTypes[unit_type_id] || character->Type->CanExperienceUpgradeTo(UnitTypes[unit_type_id])) {
126 					character->Type = const_cast<CUnitType *>(&(*UnitTypes[unit_type_id]));
127 					if (character->Level < character->Type->DefaultStat.Variables[LEVEL_INDEX].Value) {
128 						character->Level = character->Type->DefaultStat.Variables[LEVEL_INDEX].Value;
129 					}
130 				}
131 			} else {
132 				LuaError(l, "Unit type \"%s\" doesn't exist." _C_ unit_type_ident.c_str());
133 			}
134 		} else if (!strcmp(value, "Trait")) {
135 			std::string trait_ident = LuaToString(l, -1);
136 			int upgrade_id = UpgradeIdByIdent(trait_ident);
137 			if (upgrade_id != -1) {
138 				character->Trait = AllUpgrades[upgrade_id];
139 			} else {
140 				LuaError(l, "Trait upgrade \"%s\" doesn't exist." _C_ trait_ident.c_str());
141 			}
142 		} else if (!strcmp(value, "BirthDate")) {
143 			CclGetDate(l, &character->BirthDate);
144 		} else if (!strcmp(value, "StartDate")) {
145 			CclGetDate(l, &character->StartDate);
146 		} else if (!strcmp(value, "DeathDate")) {
147 			CclGetDate(l, &character->DeathDate);
148 		} else if (!strcmp(value, "ViolentDeath")) {
149 			character->ViolentDeath = LuaToBoolean(l, -1);
150 		} else if (!strcmp(value, "Civilization")) {
151 			character->Civilization = CCivilization::GetCivilization(LuaToString(l, -1));
152 		} else if (!strcmp(value, "Faction")) {
153 			CFaction *faction = PlayerRaces.GetFaction(LuaToString(l, -1));
154 			if (faction != nullptr) {
155 				character->Faction = faction;
156 			} else {
157 				LuaError(l, "Faction \"%s\" doesn't exist." _C_ faction_ident.c_str());
158 			}
159 		} else if (!strcmp(value, "Father")) {
160 			std::string father_ident = LuaToString(l, -1);
161 			CCharacter *father = CCharacter::GetCharacter(father_ident);
162 			if (father) {
163 				if (father->Gender == MaleGender) {
164 					character->Father = const_cast<CCharacter *>(&(*father));
165 					if (!father->IsParentOf(character_ident)) { //check whether the character has already been set as a child of the father
166 						father->Children.push_back(character);
167 					}
168 					// see if the father's other children aren't already included in the character's siblings, and if they aren't, add them (and add the character to the siblings' sibling list, of course)
169 					for (size_t i = 0; i < father->Children.size(); ++i) {
170 						if (father->Children[i]->Ident != character_ident) {
171 							if (!character->IsSiblingOf(father->Children[i]->Ident)) {
172 								character->Siblings.push_back(father->Children[i]);
173 							}
174 							if (!father->Children[i]->IsSiblingOf(character_ident)) {
175 								father->Children[i]->Siblings.push_back(character);
176 							}
177 						}
178 					}
179 				} else {
180 					LuaError(l, "Character \"%s\" set to be the biological father of \"%s\", but isn't male." _C_ father_ident.c_str() _C_ character_ident.c_str());
181 				}
182 			} else {
183 				LuaError(l, "Character \"%s\" doesn't exist." _C_ father_ident.c_str());
184 			}
185 		} else if (!strcmp(value, "Mother")) {
186 			std::string mother_ident = LuaToString(l, -1);
187 			CCharacter *mother = CCharacter::GetCharacter(mother_ident);
188 			if (mother) {
189 				if (mother->Gender == FemaleGender) {
190 					character->Mother = const_cast<CCharacter *>(&(*mother));
191 					if (!mother->IsParentOf(character_ident)) { //check whether the character has already been set as a child of the mother
192 						mother->Children.push_back(character);
193 					}
194 					// see if the mother's other children aren't already included in the character's siblings, and if they aren't, add them (and add the character to the siblings' sibling list, of course)
195 					for (size_t i = 0; i < mother->Children.size(); ++i) {
196 						if (mother->Children[i]->Ident != character_ident) {
197 							if (!character->IsSiblingOf(mother->Children[i]->Ident)) {
198 								character->Siblings.push_back(mother->Children[i]);
199 							}
200 							if (!mother->Children[i]->IsSiblingOf(character_ident)) {
201 								mother->Children[i]->Siblings.push_back(character);
202 							}
203 						}
204 					}
205 				} else {
206 					LuaError(l, "Character \"%s\" set to be the biological mother of \"%s\", but isn't female (gender is \"%s\")." _C_ mother_ident.c_str() _C_ character_ident.c_str() _C_ GetGenderNameById(mother->Gender).c_str());
207 				}
208 			} else {
209 				LuaError(l, "Character \"%s\" doesn't exist." _C_ mother_ident.c_str());
210 			}
211 		} else if (!strcmp(value, "Children")) {
212 			const int args = lua_rawlen(l, -1);
213 			for (int j = 0; j < args; ++j) {
214 				std::string child_ident = LuaToString(l, -1, j + 1);
215 				CCharacter *child = CCharacter::GetCharacter(child_ident);
216 				if (child) {
217 					if (character->Gender == MaleGender) {
218 						child->Father = character;
219 					} else {
220 						child->Mother = character;
221 					}
222 					if (!character->IsParentOf(child_ident)) { //check whether the character has already been set as a parent of the child
223 						character->Children.push_back(child);
224 					}
225 					// see if the character's other children aren't already included in the child's siblings, and if they aren't, add them (and add the character to the siblings' sibling list too)
226 					for (size_t i = 0; i < character->Children.size(); ++i) {
227 						if (character->Children[i] != child) {
228 							if (!child->IsSiblingOf(character->Children[i]->Ident)) {
229 								child->Siblings.push_back(character->Children[i]);
230 							}
231 							if (!character->Children[i]->IsSiblingOf(child_ident)) {
232 								character->Children[i]->Siblings.push_back(child);
233 							}
234 						}
235 					}
236 				} else {
237 					LuaError(l, "Character \"%s\" doesn't exist." _C_ child_ident.c_str());
238 				}
239 			}
240 		} else if (!strcmp(value, "Gender")) {
241 			character->Gender = GetGenderIdByName(LuaToString(l, -1));
242 		} else if (!strcmp(value, "Icon")) {
243 			character->Icon.Name = LuaToString(l, -1);
244 			character->Icon.Icon = nullptr;
245 			character->Icon.Load();
246 			character->Icon.Icon->Load();
247 		} else if (!strcmp(value, "HeroicIcon")) {
248 			character->HeroicIcon.Name = LuaToString(l, -1);
249 			character->HeroicIcon.Icon = nullptr;
250 			character->HeroicIcon.Load();
251 			character->HeroicIcon.Icon->Load();
252 		} else if (!strcmp(value, "Level")) {
253 			character->Level = LuaToNumber(l, -1);
254 		} else if (!strcmp(value, "ExperiencePercent")) {
255 			character->ExperiencePercent = LuaToNumber(l, -1);
256 		} else if (!strcmp(value, "Deity")) {
257 			CDeity *deity = CDeity::GetDeity(LuaToString(l, -1));
258 			if (deity) {
259 				character->Deity = deity;
260 				if (character->Icon.Name.empty() && !deity->Icon.Name.empty()) {
261 					character->Icon.Name = deity->Icon.Name;
262 					character->Icon.Icon = nullptr;
263 					character->Icon.Load();
264 				}
265 			}
266 		} else if (!strcmp(value, "Conditions")) {
267 			character->Conditions = new LuaCallback(l, -1);
268 		} else if (!strcmp(value, "Abilities")) {
269 			character->Abilities.clear();
270 			const int args = lua_rawlen(l, -1);
271 			for (int j = 0; j < args; ++j) {
272 				std::string ability_ident = LuaToString(l, -1, j + 1);
273 				int ability_id = UpgradeIdByIdent(ability_ident);
274 				if (ability_id != -1) {
275 					character->Abilities.push_back(AllUpgrades[ability_id]);
276 				} else {
277 					fprintf(stderr, "Ability \"%s\" doesn't exist.", ability_ident.c_str());
278 				}
279 			}
280 		} else if (!strcmp(value, "Deities")) {
281 			character->Deities.clear();
282 			const int args = lua_rawlen(l, -1);
283 			for (int j = 0; j < args; ++j) {
284 				std::string deity_ident = LuaToString(l, -1, j + 1);
285 				CDeity *deity = CDeity::GetDeity(deity_ident);
286 				if (deity) {
287 					character->Deities.push_back(deity);
288 				}
289 			}
290 		} else if (!strcmp(value, "ReadWorks")) {
291 			character->ReadWorks.clear();
292 			const int args = lua_rawlen(l, -1);
293 			for (int j = 0; j < args; ++j) {
294 				std::string work_ident = LuaToString(l, -1, j + 1);
295 				int work_id = UpgradeIdByIdent(work_ident);
296 				if (work_id != -1) {
297 					character->ReadWorks.push_back(AllUpgrades[work_id]);
298 				} else {
299 					fprintf(stderr, "Work \"%s\" doesn't exist.", work_ident.c_str());
300 				}
301 			}
302 		} else if (!strcmp(value, "AuthoredWorks")) {
303 			character->AuthoredWorks.clear();
304 			const int args = lua_rawlen(l, -1);
305 			for (int j = 0; j < args; ++j) {
306 				std::string work_ident = LuaToString(l, -1, j + 1);
307 				int work_id = UpgradeIdByIdent(work_ident);
308 				if (work_id != -1) {
309 					character->AuthoredWorks.push_back(AllUpgrades[work_id]);
310 					AllUpgrades[work_id]->Author = character;
311 				} else {
312 					fprintf(stderr, "Work \"%s\" doesn't exist.", work_ident.c_str());
313 				}
314 			}
315 		} else if (!strcmp(value, "LiteraryAppearances")) {
316 			character->LiteraryAppearances.clear();
317 			const int args = lua_rawlen(l, -1);
318 			for (int j = 0; j < args; ++j) {
319 				std::string work_ident = LuaToString(l, -1, j + 1);
320 				int work_id = UpgradeIdByIdent(work_ident);
321 				if (work_id != -1) {
322 					character->LiteraryAppearances.push_back(AllUpgrades[work_id]);
323 					AllUpgrades[work_id]->Characters.push_back(character);
324 				} else {
325 					fprintf(stderr, "Work \"%s\" doesn't exist.", work_ident.c_str());
326 				}
327 			}
328 		} else if (!strcmp(value, "ConsumedElixirs")) {
329 			character->ConsumedElixirs.clear();
330 			const int args = lua_rawlen(l, -1);
331 			for (int j = 0; j < args; ++j) {
332 				std::string elixir_ident = LuaToString(l, -1, j + 1);
333 				int elixir_id = UpgradeIdByIdent(elixir_ident);
334 				if (elixir_id != -1) {
335 					character->ConsumedElixirs.push_back(AllUpgrades[elixir_id]);
336 				} else {
337 					fprintf(stderr, "Elixir \"%s\" doesn't exist.", elixir_ident.c_str());
338 				}
339 			}
340 		} else if (!strcmp(value, "Items")) {
341 			character->Items.clear();
342 			const int args = lua_rawlen(l, -1);
343 			for (int j = 0; j < args; ++j) {
344 				lua_rawgeti(l, -1, j + 1);
345 				CPersistentItem *item = new CPersistentItem;
346 				item->Owner = character;
347 				character->Items.push_back(item);
348 				if (!lua_istable(l, -1)) {
349 					LuaError(l, "incorrect argument (expected table for items)");
350 				}
351 				const int subargs = lua_rawlen(l, -1);
352 				for (int k = 0; k < subargs; ++k) {
353 					value = LuaToString(l, -1, k + 1);
354 					++k;
355 					if (!strcmp(value, "type")) {
356 						std::string item_ident = LuaToString(l, -1, k + 1);
357 						int item_type_id = UnitTypeIdByIdent(item_ident);
358 						if (item_type_id != -1) {
359 							item->Type = const_cast<CUnitType *>(&(*UnitTypes[item_type_id]));
360 						} else {
361 							fprintf(stderr, "Item type \"%s\" doesn't exist.\n", item_ident.c_str());
362 							character->Items.erase(std::remove(character->Items.begin(), character->Items.end(), item), character->Items.end());
363 							delete item;
364 							break;
365 						}
366 					} else if (!strcmp(value, "prefix")) {
367 						std::string upgrade_ident = LuaToString(l, -1, k + 1);
368 						int upgrade_id = UpgradeIdByIdent(upgrade_ident);
369 						if (upgrade_id != -1) {
370 							item->Prefix = const_cast<CUpgrade *>(&(*AllUpgrades[upgrade_id]));
371 						} else {
372 							fprintf(stderr, "Item prefix \"%s\" doesn't exist.", upgrade_ident.c_str());
373 						}
374 					} else if (!strcmp(value, "suffix")) {
375 						std::string upgrade_ident = LuaToString(l, -1, k + 1);
376 						int upgrade_id = UpgradeIdByIdent(upgrade_ident);
377 						if (upgrade_id != -1) {
378 							item->Suffix = const_cast<CUpgrade *>(&(*AllUpgrades[upgrade_id]));
379 						} else {
380 							fprintf(stderr, "Item suffix \"%s\" doesn't exist.", upgrade_ident.c_str());
381 						}
382 					} else if (!strcmp(value, "spell")) {
383 						std::string spell_ident = LuaToString(l, -1, k + 1);
384 						CSpell *spell = CSpell::GetSpell(spell_ident);
385 						if (spell != nullptr) {
386 							item->Spell = const_cast<CSpell *>(&(*spell));
387 						} else {
388 							fprintf(stderr, "Spell \"%s\" doesn't exist.", spell_ident.c_str());
389 						}
390 					} else if (!strcmp(value, "work")) {
391 						std::string upgrade_ident = LuaToString(l, -1, k + 1);
392 						int upgrade_id = UpgradeIdByIdent(upgrade_ident);
393 						if (upgrade_id != -1) {
394 							item->Work = const_cast<CUpgrade *>(&(*AllUpgrades[upgrade_id]));
395 						} else {
396 							fprintf(stderr, "Literary work \"%s\" doesn't exist.", upgrade_ident.c_str());
397 						}
398 					} else if (!strcmp(value, "elixir")) {
399 						std::string upgrade_ident = LuaToString(l, -1, k + 1);
400 						int upgrade_id = UpgradeIdByIdent(upgrade_ident);
401 						if (upgrade_id != -1) {
402 							item->Elixir = const_cast<CUpgrade *>(&(*AllUpgrades[upgrade_id]));
403 						} else {
404 							fprintf(stderr, "Elixir \"%s\" doesn't exist.", upgrade_ident.c_str());
405 						}
406 					} else if (!strcmp(value, "name")) {
407 						item->Name = LuaToString(l, -1, k + 1);
408 					} else if (!strcmp(value, "unique")) {
409 						std::string unique_ident = LuaToString(l, -1, k + 1);
410 						CUniqueItem *unique_item = GetUniqueItem(unique_ident);
411 						item->Unique = unique_item;
412 						if (unique_item != nullptr) {
413 							item->Name = unique_item->Name;
414 							if (unique_item->Type != nullptr) {
415 								item->Type = unique_item->Type;
416 							} else {
417 								fprintf(stderr, "Unique item \"%s\" has no type.\n", unique_item->Ident.c_str());
418 							}
419 							item->Prefix = unique_item->Prefix;
420 							item->Suffix = unique_item->Suffix;
421 							item->Spell = unique_item->Spell;
422 							item->Work = unique_item->Work;
423 							item->Elixir = unique_item->Elixir;
424 						} else {
425 							fprintf(stderr, "Unique item \"%s\" doesn't exist.\n", unique_ident.c_str());
426 						}
427 					} else if (!strcmp(value, "bound")) {
428 						item->Bound = LuaToBoolean(l, -1, k + 1);
429 					} else if (!strcmp(value, "identified")) {
430 						item->Identified = LuaToBoolean(l, -1, k + 1);
431 					} else if (!strcmp(value, "equipped")) {
432 						bool is_equipped = LuaToBoolean(l, -1, k + 1);
433 						if (is_equipped && GetItemClassSlot(item->Type->ItemClass) != -1) {
434 							character->EquippedItems[GetItemClassSlot(item->Type->ItemClass)].push_back(item);
435 						}
436 					} else {
437 						printf("\n%s\n", character->Ident.c_str());
438 						LuaError(l, "Unsupported tag: %s" _C_ value);
439 					}
440 				}
441 				lua_pop(l, 1);
442 			}
443 		} else if (!strcmp(value, "ForbiddenUpgrades")) {
444 			character->ForbiddenUpgrades.clear();
445 			const int args = lua_rawlen(l, -1);
446 			for (int j = 0; j < args; ++j) {
447 				std::string unit_type_ident = LuaToString(l, -1, j + 1);
448 				int unit_type_id = UnitTypeIdByIdent(unit_type_ident);
449 				if (unit_type_id != -1) {
450 					character->ForbiddenUpgrades.push_back(UnitTypes[unit_type_id]);
451 				} else {
452 					LuaError(l, "Unit type \"%s\" doesn't exist." _C_ unit_type_ident.c_str());
453 				}
454 			}
455 		} else if (!strcmp(value, "HistoricalFactions")) {
456 			if (!lua_istable(l, -1)) {
457 				LuaError(l, "incorrect argument");
458 			}
459 			const int subargs = lua_rawlen(l, -1);
460 			for (int j = 0; j < subargs; ++j) {
461 				CDate date;
462 				lua_rawgeti(l, -1, j + 1);
463 				CclGetDate(l, &date);
464 				lua_pop(l, 1);
465 				++j;
466 
467 				std::string historical_faction_name = LuaToString(l, -1, j + 1);
468 				CFaction *historical_faction = PlayerRaces.GetFaction(historical_faction_name);
469 				if (!historical_faction) {
470 					LuaError(l, "Faction \"%s\" doesn't exist." _C_ historical_faction_name.c_str());
471 				}
472 
473 				character->HistoricalFactions.push_back(std::pair<CDate, CFaction *>(date, historical_faction));
474 			}
475 		} else if (!strcmp(value, "HistoricalLocations")) {
476 			if (!lua_istable(l, -1)) {
477 				LuaError(l, "incorrect argument");
478 			}
479 			const int subargs = lua_rawlen(l, -1);
480 			for (int j = 0; j < subargs; ++j) {
481 				CHistoricalLocation *historical_location = new CHistoricalLocation;
482 				lua_rawgeti(l, -1, j + 1);
483 				CclGetDate(l, &historical_location->Date);
484 				lua_pop(l, 1);
485 				++j;
486 
487 				historical_location->MapTemplate = CMapTemplate::GetMapTemplate(LuaToString(l, -1, j + 1));
488 				++j;
489 
490 				lua_rawgeti(l, -1, j + 1);
491 				if (lua_istable(l, -1)) { //coordinates
492 					CclGetPos(l, &historical_location->Position.x, &historical_location->Position.y);
493 				} else { //site ident
494 					std::string site_ident = LuaToString(l, -1);
495 					historical_location->Site = CSite::GetSite(site_ident);
496 					if (!historical_location->Site) {
497 						LuaError(l, "Site \"%s\" doesn't exist.\n" _C_ site_ident.c_str());
498 					}
499 					historical_location->MapTemplate = historical_location->Site->MapTemplate;
500 					historical_location->Position = historical_location->Site->Position;
501 				}
502 				lua_pop(l, 1);
503 
504 				character->HistoricalLocations.push_back(historical_location);
505 			}
506 		} else if (!strcmp(value, "HistoricalTitles")) {
507 			if (!lua_istable(l, -1)) {
508 				LuaError(l, "incorrect argument");
509 			}
510 			const int subargs = lua_rawlen(l, -1);
511 			for (int j = 0; j < subargs; ++j) {
512 				int title = GetCharacterTitleIdByName(LuaToString(l, -1, j + 1));
513 				if (title == -1) {
514 					LuaError(l, "Character title doesn't exist.");
515 				}
516 				++j;
517 				CDate start_date;
518 				lua_rawgeti(l, -1, j + 1);
519 				CclGetDate(l, &start_date);
520 				lua_pop(l, 1);
521 				++j;
522 				CDate end_date;
523 				lua_rawgeti(l, -1, j + 1);
524 				CclGetDate(l, &end_date);
525 				lua_pop(l, 1);
526 				++j;
527 
528 				std::string title_faction_name = LuaToString(l, -1, j + 1);
529 				CFaction *title_faction = PlayerRaces.GetFaction(title_faction_name);
530 				if (!title_faction) {
531 					LuaError(l, "Faction \"%s\" doesn't exist." _C_ title_faction_name.c_str());
532 				}
533 				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
534 					title_faction->HistoricalMinisters[std::tuple<CDate, CDate, int>(start_date, end_date, title)] = character;
535 				}
536 				character->HistoricalTitles.push_back(std::tuple<CDate, CDate, CFaction *, int>(start_date, end_date, title_faction, title));
537 			}
538 		} else {
539 			LuaError(l, "Unsupported tag: %s" _C_ value);
540 		}
541 	}
542 
543 	if (!redefinition) {
544 		if (character->Type->BoolFlag[FAUNA_INDEX].value) {
545 			character->Type->PersonalNames[character->Gender].push_back(character->Name);
546 			for (size_t i = 0; i < alternate_names.size(); ++i) {
547 				character->Type->PersonalNames[character->Gender].push_back(alternate_names[i]);
548 			}
549 		} else if (character->Civilization) {
550 			character->Civilization->PersonalNames[character->Gender].push_back(character->Name);
551 			for (size_t i = 0; i < alternate_names.size(); ++i) {
552 				character->Civilization->PersonalNames[character->Gender].push_back(alternate_names[i]);
553 			}
554 		}
555 	}
556 
557 	if (character->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)
558 		if (character->Type != nullptr && character->Type->Traits.size() == 1) {
559 			character->Trait = character->Type->Traits[0];
560 		}
561 	}
562 
563 	if (character->Gender == NoGender) { //if no gender was set, have the character be the same gender as the unit type (if the unit type has it predefined)
564 		if (character->Type != nullptr && character->Type->DefaultStat.Variables[GENDER_INDEX].Value != 0) {
565 			character->Gender = character->Type->DefaultStat.Variables[GENDER_INDEX].Value;
566 		}
567 	}
568 
569 	//check if the abilities are correct for this character's unit type
570 	if (character->Type != nullptr && character->Abilities.size() > 0 && ((int) AiHelpers.LearnableAbilities.size()) > character->Type->Slot) {
571 		int ability_count = (int) character->Abilities.size();
572 		for (int i = (ability_count - 1); i >= 0; --i) {
573 			if (std::find(AiHelpers.LearnableAbilities[character->Type->Slot].begin(), AiHelpers.LearnableAbilities[character->Type->Slot].end(), character->Abilities[i]) == AiHelpers.LearnableAbilities[character->Type->Slot].end()) {
574 				character->Abilities.erase(std::remove(character->Abilities.begin(), character->Abilities.end(), character->Abilities[i]), character->Abilities.end());
575 			}
576 		}
577 	}
578 
579 	character->GenerateMissingDates();
580 	character->UpdateAttributes();
581 
582 	character->Initialized = true;
583 
584 	return 0;
585 }
586 
587 /**
588 **  Define a custom hero.
589 **
590 **  @param l  Lua state.
591 */
CclDefineCustomHero(lua_State * l)592 static int CclDefineCustomHero(lua_State *l)
593 {
594 	LuaCheckArgs(l, 2);
595 	if (!lua_istable(l, 2)) {
596 		LuaError(l, "incorrect argument (expected table)");
597 	}
598 
599 	std::string hero_ident = LuaToString(l, 1);
600 	CCharacter *hero = GetCustomHero(hero_ident);
601 	if (!hero) {
602 		hero = new CCharacter;
603 		hero->Ident = hero_ident;
604 		CustomHeroes[hero_ident] = hero;
605 	} else {
606 		fprintf(stderr, "Custom hero \"%s\" is being redefined.\n", hero_ident.c_str());
607 	}
608 	hero->Custom = true;
609 
610 	//  Parse the list:
611 	for (lua_pushnil(l); lua_next(l, 2); lua_pop(l, 1)) {
612 		const char *value = LuaToString(l, -2);
613 
614 		if (!strcmp(value, "Name")) {
615 			hero->Name = LuaToString(l, -1);
616 		} else if (!strcmp(value, "ExtraName")) {
617 			hero->ExtraName = LuaToString(l, -1);
618 		} else if (!strcmp(value, "FamilyName")) {
619 			hero->FamilyName = LuaToString(l, -1);
620 		} else if (!strcmp(value, "Dynasty")) { // for backwards compatibility
621 			hero->FamilyName = LuaToString(l, -1);
622 		} else if (!strcmp(value, "Description")) {
623 			hero->Description = LuaToString(l, -1);
624 		} else if (!strcmp(value, "Variation")) { //to keep backwards compatibility
625 			hero->HairVariation = LuaToString(l, -1);
626 		} else if (!strcmp(value, "HairVariation")) {
627 			hero->HairVariation = LuaToString(l, -1);
628 		} else if (!strcmp(value, "Type")) {
629 			std::string unit_type_ident = LuaToString(l, -1);
630 			int unit_type_id = UnitTypeIdByIdent(unit_type_ident);
631 			if (unit_type_id != -1) {
632 				hero->Type = const_cast<CUnitType *>(&(*UnitTypes[unit_type_id]));
633 				if (hero->Level < hero->Type->DefaultStat.Variables[LEVEL_INDEX].Value) {
634 					hero->Level = hero->Type->DefaultStat.Variables[LEVEL_INDEX].Value;
635 				}
636 			} else {
637 				LuaError(l, "Unit type \"%s\" doesn't exist." _C_ unit_type_ident.c_str());
638 			}
639 		} else if (!strcmp(value, "Trait")) {
640 			std::string trait_ident = LuaToString(l, -1);
641 			int upgrade_id = UpgradeIdByIdent(trait_ident);
642 			if (upgrade_id != -1) {
643 				hero->Trait = AllUpgrades[upgrade_id];
644 			} else {
645 				LuaError(l, "Trait upgrade \"%s\" doesn't exist." _C_ trait_ident.c_str());
646 			}
647 		} else if (!strcmp(value, "Civilization")) {
648 			hero->Civilization = CCivilization::GetCivilization(LuaToString(l, -1));
649 		} else if (!strcmp(value, "Gender")) {
650 			hero->Gender = GetGenderIdByName(LuaToString(l, -1));
651 		} else if (!strcmp(value, "Level")) {
652 			hero->Level = LuaToNumber(l, -1);
653 		} else if (!strcmp(value, "ExperiencePercent")) {
654 			hero->ExperiencePercent = LuaToNumber(l, -1);
655 		} else if (!strcmp(value, "Abilities")) {
656 			hero->Abilities.clear();
657 			const int args = lua_rawlen(l, -1);
658 			for (int j = 0; j < args; ++j) {
659 				std::string ability_ident = LuaToString(l, -1, j + 1);
660 				int ability_id = UpgradeIdByIdent(ability_ident);
661 				if (ability_id != -1) {
662 					hero->Abilities.push_back(AllUpgrades[ability_id]);
663 				} else {
664 					fprintf(stderr, "Ability \"%s\" doesn't exist.", ability_ident.c_str());
665 				}
666 			}
667 		} else if (!strcmp(value, "Deities")) {
668 			hero->Deities.clear();
669 			const int args = lua_rawlen(l, -1);
670 			for (int j = 0; j < args; ++j) {
671 				std::string deity_ident = LuaToString(l, -1, j + 1);
672 				CDeity *deity = CDeity::GetDeity(deity_ident);
673 				if (deity) {
674 					hero->Deities.push_back(deity);
675 				}
676 			}
677 		} else if (!strcmp(value, "ReadWorks")) {
678 			hero->ReadWorks.clear();
679 			const int args = lua_rawlen(l, -1);
680 			for (int j = 0; j < args; ++j) {
681 				std::string work_ident = LuaToString(l, -1, j + 1);
682 				int work_id = UpgradeIdByIdent(work_ident);
683 				if (work_id != -1) {
684 					hero->ReadWorks.push_back(AllUpgrades[work_id]);
685 				} else {
686 					fprintf(stderr, "Work \"%s\" doesn't exist.", work_ident.c_str());
687 				}
688 			}
689 		} else if (!strcmp(value, "ConsumedElixirs")) {
690 			hero->ConsumedElixirs.clear();
691 			const int args = lua_rawlen(l, -1);
692 			for (int j = 0; j < args; ++j) {
693 				std::string elixir_ident = LuaToString(l, -1, j + 1);
694 				int elixir_id = UpgradeIdByIdent(elixir_ident);
695 				if (elixir_id != -1) {
696 					hero->ConsumedElixirs.push_back(AllUpgrades[elixir_id]);
697 				} else {
698 					fprintf(stderr, "Elixir \"%s\" doesn't exist.", elixir_ident.c_str());
699 				}
700 			}
701 		} else if (!strcmp(value, "Items")) {
702 			const int args = lua_rawlen(l, -1);
703 			for (int j = 0; j < args; ++j) {
704 				lua_rawgeti(l, -1, j + 1);
705 				CPersistentItem *item = new CPersistentItem;
706 				item->Owner = hero;
707 				hero->Items.push_back(item);
708 				if (!lua_istable(l, -1)) {
709 					LuaError(l, "incorrect argument (expected table for items)");
710 				}
711 				const int subargs = lua_rawlen(l, -1);
712 				for (int k = 0; k < subargs; ++k) {
713 					value = LuaToString(l, -1, k + 1);
714 					++k;
715 					if (!strcmp(value, "type")) {
716 						std::string item_ident = LuaToString(l, -1, k + 1);
717 						int item_type_id = UnitTypeIdByIdent(item_ident);
718 						if (item_type_id != -1) {
719 							item->Type = const_cast<CUnitType *>(&(*UnitTypes[item_type_id]));
720 						} else {
721 							fprintf(stderr, "Item type \"%s\" doesn't exist.\n", item_ident.c_str());
722 							hero->Items.erase(std::remove(hero->Items.begin(), hero->Items.end(), item), hero->Items.end());
723 							delete item;
724 							break;
725 						}
726 					} else if (!strcmp(value, "prefix")) {
727 						std::string upgrade_ident = LuaToString(l, -1, k + 1);
728 						int upgrade_id = UpgradeIdByIdent(upgrade_ident);
729 						if (upgrade_id != -1) {
730 							item->Prefix = const_cast<CUpgrade *>(&(*AllUpgrades[upgrade_id]));
731 						} else {
732 							fprintf(stderr, "Item prefix \"%s\" doesn't exist.", upgrade_ident.c_str());
733 						}
734 					} else if (!strcmp(value, "suffix")) {
735 						std::string upgrade_ident = LuaToString(l, -1, k + 1);
736 						int upgrade_id = UpgradeIdByIdent(upgrade_ident);
737 						if (upgrade_id != -1) {
738 							item->Suffix = const_cast<CUpgrade *>(&(*AllUpgrades[upgrade_id]));
739 						} else {
740 							fprintf(stderr, "Item suffix \"%s\" doesn't exist.", upgrade_ident.c_str());
741 						}
742 					} else if (!strcmp(value, "spell")) {
743 						std::string spell_ident = LuaToString(l, -1, k + 1);
744 						CSpell *spell = CSpell::GetSpell(spell_ident);
745 						if (spell != nullptr) {
746 							item->Spell = const_cast<CSpell *>(&(*spell));
747 						} else {
748 							fprintf(stderr, "Spell \"%s\" doesn't exist.", spell_ident.c_str());
749 						}
750 					} else if (!strcmp(value, "work")) {
751 						std::string upgrade_ident = LuaToString(l, -1, k + 1);
752 						int upgrade_id = UpgradeIdByIdent(upgrade_ident);
753 						if (upgrade_id != -1) {
754 							item->Work = const_cast<CUpgrade *>(&(*AllUpgrades[upgrade_id]));
755 						} else {
756 							fprintf(stderr, "Literary work \"%s\" doesn't exist.", upgrade_ident.c_str());
757 						}
758 					} else if (!strcmp(value, "elixir")) {
759 						std::string upgrade_ident = LuaToString(l, -1, k + 1);
760 						int upgrade_id = UpgradeIdByIdent(upgrade_ident);
761 						if (upgrade_id != -1) {
762 							item->Elixir = const_cast<CUpgrade *>(&(*AllUpgrades[upgrade_id]));
763 						} else {
764 							fprintf(stderr, "Elixir \"%s\" doesn't exist.", upgrade_ident.c_str());
765 						}
766 					} else if (!strcmp(value, "name")) {
767 						item->Name = LuaToString(l, -1, k + 1);
768 					} else if (!strcmp(value, "unique")) {
769 						std::string unique_ident = LuaToString(l, -1, k + 1);
770 						CUniqueItem *unique_item = GetUniqueItem(unique_ident);
771 						item->Unique = unique_item;
772 						if (unique_item != nullptr) {
773 							item->Name = unique_item->Name;
774 							if (unique_item->Type != nullptr) {
775 								item->Type = unique_item->Type;
776 							} else {
777 								fprintf(stderr, "Unique item \"%s\" has no type.\n", item->Name.c_str());
778 							}
779 							item->Prefix = unique_item->Prefix;
780 							item->Suffix = unique_item->Suffix;
781 							item->Spell = unique_item->Spell;
782 							item->Work = unique_item->Work;
783 							item->Elixir = unique_item->Elixir;
784 						} else {
785 							fprintf(stderr, "Unique item \"%s\" doesn't exist.\n", unique_ident.c_str());
786 						}
787 					} else if (!strcmp(value, "bound")) {
788 						item->Bound = LuaToBoolean(l, -1, k + 1);
789 					} else if (!strcmp(value, "identified")) {
790 						item->Identified = LuaToBoolean(l, -1, k + 1);
791 					} else if (!strcmp(value, "equipped")) {
792 						bool is_equipped = LuaToBoolean(l, -1, k + 1);
793 						if (is_equipped && GetItemClassSlot(item->Type->ItemClass) != -1) {
794 							hero->EquippedItems[GetItemClassSlot(item->Type->ItemClass)].push_back(item);
795 						}
796 					} else {
797 						printf("\n%s\n", hero->Ident.c_str());
798 						LuaError(l, "Unsupported tag: %s" _C_ value);
799 					}
800 				}
801 				lua_pop(l, 1);
802 			}
803 		} else if (!strcmp(value, "QuestsInProgress")) {
804 			hero->QuestsInProgress.clear();
805 			const int args = lua_rawlen(l, -1);
806 			for (int j = 0; j < args; ++j) {
807 				std::string quest_name = LuaToString(l, -1, j + 1);
808 				if (GetQuest(quest_name) != nullptr) {
809 					hero->QuestsInProgress.push_back(GetQuest(quest_name));
810 				} else {
811 					LuaError(l, "Quest \"%s\" doesn't exist." _C_ quest_name.c_str());
812 				}
813 			}
814 		} else if (!strcmp(value, "QuestsCompleted")) {
815 			hero->QuestsCompleted.clear();
816 			const int args = lua_rawlen(l, -1);
817 			for (int j = 0; j < args; ++j) {
818 				std::string quest_name = LuaToString(l, -1, j + 1);
819 				if (GetQuest(quest_name) != nullptr) {
820 					hero->QuestsCompleted.push_back(GetQuest(quest_name));
821 				} else {
822 					LuaError(l, "Quest \"%s\" doesn't exist." _C_ quest_name.c_str());
823 				}
824 			}
825 		} else if (!strcmp(value, "ForbiddenUpgrades")) {
826 			hero->ForbiddenUpgrades.clear();
827 			const int args = lua_rawlen(l, -1);
828 			for (int j = 0; j < args; ++j) {
829 				std::string unit_type_ident = LuaToString(l, -1, j + 1);
830 				int unit_type_id = UnitTypeIdByIdent(unit_type_ident);
831 				if (unit_type_id != -1) {
832 					hero->ForbiddenUpgrades.push_back(UnitTypes[unit_type_id]);
833 				} else {
834 					LuaError(l, "Unit type \"%s\" doesn't exist." _C_ unit_type_ident.c_str());
835 				}
836 			}
837 		} else if (!strcmp(value, "Icon")) {
838 			hero->Icon.Name = LuaToString(l, -1);
839 			hero->Icon.Icon = nullptr;
840 			hero->Icon.Load();
841 			hero->Icon.Icon->Load();
842 		} else if (!strcmp(value, "HeroicIcon")) {
843 			hero->HeroicIcon.Name = LuaToString(l, -1);
844 			hero->HeroicIcon.Icon = nullptr;
845 			hero->HeroicIcon.Load();
846 			hero->HeroicIcon.Icon->Load();
847 		} else {
848 			LuaError(l, "Unsupported tag: %s" _C_ value);
849 		}
850 	}
851 
852 	if (hero->Gender == NoGender) { //if no gender was set, have the hero be the same gender as the unit type (if the unit type has it predefined)
853 		if (hero->Type != nullptr && hero->Type->DefaultStat.Variables[GENDER_INDEX].Value != 0) {
854 			hero->Gender = hero->Type->DefaultStat.Variables[GENDER_INDEX].Value;
855 		}
856 	}
857 
858 	//check if the abilities are correct for this hero's unit type
859 	if (hero->Abilities.size() > 0 && ((int) AiHelpers.LearnableAbilities.size()) > hero->Type->Slot) {
860 		int ability_count = (int) hero->Abilities.size();
861 		for (int i = (ability_count - 1); i >= 0; --i) {
862 			if (std::find(AiHelpers.LearnableAbilities[hero->Type->Slot].begin(), AiHelpers.LearnableAbilities[hero->Type->Slot].end(), hero->Abilities[i]) == AiHelpers.LearnableAbilities[hero->Type->Slot].end()) {
863 				hero->Abilities.erase(std::remove(hero->Abilities.begin(), hero->Abilities.end(), hero->Abilities[i]), hero->Abilities.end());
864 			}
865 		}
866 	}
867 
868 	return 0;
869 }
870 
871 /**
872 **  Get character data.
873 **
874 **  @param l  Lua state.
875 */
CclGetCharacterData(lua_State * l)876 static int CclGetCharacterData(lua_State *l)
877 {
878 	if (lua_gettop(l) < 2) {
879 		LuaError(l, "incorrect argument");
880 	}
881 	std::string character_name = LuaToString(l, 1);
882 	CCharacter *character = CCharacter::GetCharacter(character_name);
883 	if (!character) {
884 		LuaError(l, "Character \"%s\" doesn't exist." _C_ character_name.c_str());
885 	}
886 	const char *data = LuaToString(l, 2);
887 
888 	if (!strcmp(data, "Name")) {
889 		lua_pushstring(l, character->Name.c_str());
890 		return 1;
891 	} else if (!strcmp(data, "FamilyName")) {
892 		lua_pushstring(l, character->FamilyName.c_str());
893 		return 1;
894 	} else if (!strcmp(data, "FullName")) {
895 		lua_pushstring(l, character->GetFullName().c_str());
896 		return 1;
897 	} else if (!strcmp(data, "Description")) {
898 		lua_pushstring(l, character->Description.c_str());
899 		return 1;
900 	} else if (!strcmp(data, "Background")) {
901 		lua_pushstring(l, character->Background.c_str());
902 		return 1;
903 	} else if (!strcmp(data, "Quote")) {
904 		lua_pushstring(l, character->Quote.c_str());
905 		return 1;
906 	} else if (!strcmp(data, "Civilization")) {
907 		if (character->Civilization) {
908 			lua_pushstring(l, PlayerRaces.Name[character->Civilization->ID].c_str());
909 		} else {
910 			lua_pushstring(l, "");
911 		}
912 		return 1;
913 	} else if (!strcmp(data, "Faction")) {
914 		if (character->Faction != nullptr) {
915 			lua_pushstring(l, character->Faction->Ident.c_str());
916 		} else {
917 			lua_pushstring(l, "");
918 		}
919 		return 1;
920 	} else if (!strcmp(data, "BirthDate")) {
921 		if (character->BirthDate.Year != 0) {
922 			lua_pushstring(l, character->BirthDate.ToDisplayString(character->GetCalendar()).c_str());
923 		} else {
924 			lua_pushstring(l, "");
925 		}
926 		return 1;
927 	} else if (!strcmp(data, "BirthYear")) {
928 		lua_pushnumber(l, character->BirthDate.Year);
929 		return 1;
930 	} else if (!strcmp(data, "StartDate")) {
931 		if (character->StartDate.Year != 0) {
932 			lua_pushstring(l, character->StartDate.ToDisplayString(character->GetCalendar()).c_str());
933 		} else {
934 			lua_pushstring(l, "");
935 		}
936 		return 1;
937 	} else if (!strcmp(data, "StartYear")) {
938 		lua_pushnumber(l, character->StartDate.Year);
939 		return 1;
940 	} else if (!strcmp(data, "DeathDate")) {
941 		if (character->DeathDate.Year != 0) {
942 			lua_pushstring(l, character->DeathDate.ToDisplayString(character->GetCalendar()).c_str());
943 		} else {
944 			lua_pushstring(l, "");
945 		}
946 		return 1;
947 	} else if (!strcmp(data, "DeathYear")) {
948 		lua_pushnumber(l, character->DeathDate.Year);
949 		return 1;
950 	} else if (!strcmp(data, "ViolentDeath")) {
951 		lua_pushboolean(l, character->ViolentDeath);
952 		return 1;
953 	} else if (!strcmp(data, "Gender")) {
954 		lua_pushstring(l, GetGenderNameById(character->Gender).c_str());
955 		return 1;
956 	} else if (!strcmp(data, "Level")) {
957 		lua_pushnumber(l, character->Level);
958 		return 1;
959 	} else if (!strcmp(data, "Type")) {
960 		if (character->Type != nullptr) {
961 			lua_pushstring(l, character->Type->Ident.c_str());
962 		} else {
963 			lua_pushstring(l, "");
964 		}
965 		return 1;
966 	} else if (!strcmp(data, "Trait")) {
967 		if (character->Trait != nullptr) {
968 			lua_pushstring(l, character->Trait->Ident.c_str());
969 		} else {
970 			lua_pushstring(l, "");
971 		}
972 		return 1;
973 	} else if (!strcmp(data, "Deity")) {
974 		if (character->Deity != nullptr) {
975 			lua_pushstring(l, character->Deity->Ident.c_str());
976 		} else {
977 			lua_pushstring(l, "");
978 		}
979 		return 1;
980 	} else if (!strcmp(data, "Deities")) {
981 		lua_createtable(l, character->Deities.size(), 0);
982 		for (size_t i = 1; i <= character->Deities.size(); ++i)
983 		{
984 			lua_pushstring(l, character->Deities[i-1]->Ident.c_str());
985 			lua_rawseti(l, -2, i);
986 		}
987 		return 1;
988 	} else if (!strcmp(data, "Father")) {
989 		if (character->Father != nullptr) {
990 			lua_pushstring(l, character->Father->Ident.c_str());
991 		} else {
992 			lua_pushstring(l, "");
993 		}
994 		return 1;
995 	} else if (!strcmp(data, "Mother")) {
996 		if (character->Mother != nullptr) {
997 			lua_pushstring(l, character->Mother->Ident.c_str());
998 		} else {
999 			lua_pushstring(l, "");
1000 		}
1001 		return 1;
1002 	} else if (!strcmp(data, "Children")) {
1003 		lua_createtable(l, character->Children.size(), 0);
1004 		for (size_t i = 1; i <= character->Children.size(); ++i)
1005 		{
1006 			lua_pushstring(l, character->Children[i-1]->Ident.c_str());
1007 			lua_rawseti(l, -2, i);
1008 		}
1009 		return 1;
1010 	} else if (!strcmp(data, "Siblings")) {
1011 		lua_createtable(l, character->Siblings.size(), 0);
1012 		for (size_t i = 1; i <= character->Siblings.size(); ++i)
1013 		{
1014 			lua_pushstring(l, character->Siblings[i-1]->Ident.c_str());
1015 			lua_rawseti(l, -2, i);
1016 		}
1017 		return 1;
1018 	} else if (!strcmp(data, "Icon")) {
1019 		lua_pushstring(l, character->GetIcon().Name.c_str());
1020 		return 1;
1021 	} else if (!strcmp(data, "BaseIcon")) {
1022 		lua_pushstring(l, character->Icon.Name.c_str());
1023 		return 1;
1024 	} else if (!strcmp(data, "HairVariation")) {
1025 		lua_pushstring(l, character->HairVariation.c_str());
1026 		return 1;
1027 	} else if (!strcmp(data, "IsUsable")) {
1028 		lua_pushboolean(l, character->IsUsable());
1029 		return 1;
1030 	} else if (!strcmp(data, "Abilities")) {
1031 		lua_createtable(l, character->Abilities.size(), 0);
1032 		for (size_t i = 1; i <= character->Abilities.size(); ++i)
1033 		{
1034 			lua_pushstring(l, character->Abilities[i-1]->Ident.c_str());
1035 			lua_rawseti(l, -2, i);
1036 		}
1037 		return 1;
1038 	} else {
1039 		LuaError(l, "Invalid field: %s" _C_ data);
1040 	}
1041 
1042 	return 0;
1043 }
1044 
1045 /**
1046 **  Get custom hero data.
1047 **
1048 **  @param l  Lua state.
1049 */
CclGetCustomHeroData(lua_State * l)1050 static int CclGetCustomHeroData(lua_State *l)
1051 {
1052 	if (lua_gettop(l) < 2) {
1053 		LuaError(l, "incorrect argument");
1054 	}
1055 	std::string character_name = LuaToString(l, 1);
1056 	CCharacter *character = GetCustomHero(character_name);
1057 	if (!character) {
1058 		LuaError(l, "Custom hero \"%s\" doesn't exist." _C_ character_name.c_str());
1059 	}
1060 	const char *data = LuaToString(l, 2);
1061 
1062 	if (!strcmp(data, "Name")) {
1063 		lua_pushstring(l, character->Name.c_str());
1064 		return 1;
1065 	} else if (!strcmp(data, "FamilyName")) {
1066 		lua_pushstring(l, character->FamilyName.c_str());
1067 		return 1;
1068 	} else if (!strcmp(data, "FullName")) {
1069 		lua_pushstring(l, character->GetFullName().c_str());
1070 		return 1;
1071 	} else if (!strcmp(data, "Civilization")) {
1072 		if (character->Civilization) {
1073 			lua_pushstring(l, PlayerRaces.Name[character->Civilization->ID].c_str());
1074 		} else {
1075 			lua_pushstring(l, "");
1076 		}
1077 		return 1;
1078 	} else if (!strcmp(data, "Gender")) {
1079 		lua_pushstring(l, GetGenderNameById(character->Gender).c_str());
1080 		return 1;
1081 	} else if (!strcmp(data, "Level")) {
1082 		lua_pushnumber(l, character->Level);
1083 		return 1;
1084 	} else if (!strcmp(data, "Type")) {
1085 		if (character->Type != nullptr) {
1086 			lua_pushstring(l, character->Type->Ident.c_str());
1087 		} else {
1088 			lua_pushstring(l, "");
1089 		}
1090 		return 1;
1091 	} else if (!strcmp(data, "Trait")) {
1092 		if (character->Trait != nullptr) {
1093 			lua_pushstring(l, character->Trait->Ident.c_str());
1094 		} else {
1095 			lua_pushstring(l, "");
1096 		}
1097 		return 1;
1098 	} else if (!strcmp(data, "Icon")) {
1099 		lua_pushstring(l, character->GetIcon().Name.c_str());
1100 		return 1;
1101 	} else {
1102 		LuaError(l, "Invalid field: %s" _C_ data);
1103 	}
1104 
1105 	return 0;
1106 }
1107 
CclGetCharacters(lua_State * l)1108 static int CclGetCharacters(lua_State *l)
1109 {
1110 	std::vector<std::string> character_names;
1111 	for (const CCharacter *character : CCharacter::Characters) {
1112 		character_names.push_back(character->Ident);
1113 	}
1114 
1115 	lua_createtable(l, character_names.size(), 0);
1116 	for (size_t i = 1; i <= character_names.size(); ++i)
1117 	{
1118 		lua_pushstring(l, character_names[i-1].c_str());
1119 		lua_rawseti(l, -2, i);
1120 	}
1121 	return 1;
1122 }
1123 
CclGetCustomHeroes(lua_State * l)1124 static int CclGetCustomHeroes(lua_State *l)
1125 {
1126 	std::vector<std::string> character_names;
1127 	for (std::map<std::string, CCharacter *>::iterator iterator = CustomHeroes.begin(); iterator != CustomHeroes.end(); ++iterator) {
1128 		character_names.push_back(iterator->first);
1129 	}
1130 
1131 	lua_createtable(l, character_names.size(), 0);
1132 	for (size_t i = 1; i <= character_names.size(); ++i)
1133 	{
1134 		lua_pushstring(l, character_names[i-1].c_str());
1135 		lua_rawseti(l, -2, i);
1136 	}
1137 	return 1;
1138 }
1139 
CclGetGrandStrategyHeroes(lua_State * l)1140 static int CclGetGrandStrategyHeroes(lua_State *l)
1141 {
1142 	lua_createtable(l, GrandStrategyGame.Heroes.size(), 0);
1143 	for (size_t i = 1; i <= GrandStrategyGame.Heroes.size(); ++i)
1144 	{
1145 		lua_pushstring(l, GrandStrategyGame.Heroes[i-1]->GetFullName().c_str());
1146 		lua_rawseti(l, -2, i);
1147 	}
1148 	return 1;
1149 }
1150 
1151 /**
1152 **  Parse character temporary data
1153 **
1154 **  @param l  Lua state.
1155 */
CclCharacter(lua_State * l)1156 static int CclCharacter(lua_State *l)
1157 {
1158 	const std::string ident = LuaToString(l, 1);
1159 
1160 	if (!lua_istable(l, 2)) {
1161 		LuaError(l, "incorrect argument");
1162 	}
1163 
1164 	CCharacter *character = CCharacter::GetCharacter(ident);
1165 	if (!character) {
1166 		return 0;
1167 	}
1168 
1169 	// Parse the list:
1170 	const int args = lua_rawlen(l, 2);
1171 	for (int j = 0; j < args; ++j) {
1172 		const char *value = LuaToString(l, 2, j + 1);
1173 		++j;
1174 
1175 		if (!strcmp(value, "deity")) {
1176 			CDeity *deity = CDeity::GetDeity(LuaToString(l, 2, j + 1));
1177 			if (deity) {
1178 				character->Deities.push_back(deity);
1179 			}
1180 		} else {
1181 			fprintf(stderr, "Character: Unsupported tag: %s\n", value);
1182 		}
1183 	}
1184 
1185 	return 0;
1186 }
1187 
1188 // ----------------------------------------------------------------------------
1189 
1190 /**
1191 **  Register CCL features for characters.
1192 */
CharacterCclRegister()1193 void CharacterCclRegister()
1194 {
1195 	lua_register(Lua, "DefineCharacter", CclDefineCharacter);
1196 	lua_register(Lua, "DefineCustomHero", CclDefineCustomHero);
1197 	lua_register(Lua, "GetCharacterData", CclGetCharacterData);
1198 	lua_register(Lua, "GetCustomHeroData", CclGetCustomHeroData);
1199 	lua_register(Lua, "GetCharacters", CclGetCharacters);
1200 	lua_register(Lua, "GetCustomHeroes", CclGetCustomHeroes);
1201 	lua_register(Lua, "GetGrandStrategyHeroes", CclGetGrandStrategyHeroes);
1202 	lua_register(Lua, "Character", CclCharacter);
1203 }
1204 
1205 //@}
1206