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_quest.cpp - The quest 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 "quest.h"
39 
40 #include "character.h"
41 #include "civilization.h"
42 #include "dialogue.h"
43 #include "luacallback.h"
44 #include "map/map.h"
45 #include "map/map_template.h"
46 #include "map/site.h"
47 #include "player.h"
48 #include "script.h"
49 #include "unit/unittype.h"
50 #include "upgrade/upgrade.h"
51 
52 /*----------------------------------------------------------------------------
53 --  Variables
54 ----------------------------------------------------------------------------*/
55 
56 /*----------------------------------------------------------------------------
57 --  Functions
58 ----------------------------------------------------------------------------*/
59 
60 /**
61 **  Define a quest.
62 **
63 **  @param l  Lua state.
64 */
CclDefineQuest(lua_State * l)65 static int CclDefineQuest(lua_State *l)
66 {
67 	LuaCheckArgs(l, 2);
68 	if (!lua_istable(l, 2)) {
69 		LuaError(l, "incorrect argument (expected table)");
70 	}
71 
72 	std::string quest_ident = LuaToString(l, 1);
73 	CQuest *quest = GetQuest(quest_ident);
74 	if (!quest) {
75 		quest = new CQuest;
76 		quest->ID = Quests.size();
77 		Quests.push_back(quest);
78 		quest->Ident = quest_ident;
79 	}
80 
81 	//  Parse the list:
82 	for (lua_pushnil(l); lua_next(l, 2); lua_pop(l, 1)) {
83 		const char *value = LuaToString(l, -2);
84 
85 		if (!strcmp(value, "Name")) {
86 			quest->Name = LuaToString(l, -1);
87 		} else if (!strcmp(value, "Description")) {
88 			quest->Description = LuaToString(l, -1);
89 		} else if (!strcmp(value, "World")) {
90 			quest->World = LuaToString(l, -1);
91 		} else if (!strcmp(value, "Map")) {
92 			quest->Map = LuaToString(l, -1);
93 		} else if (!strcmp(value, "Scenario")) {
94 			quest->Scenario = LuaToString(l, -1);
95 		} else if (!strcmp(value, "RequiredQuest")) {
96 			quest->RequiredQuest = LuaToString(l, -1);
97 		} else if (!strcmp(value, "RequiredTechnology")) {
98 			quest->RequiredTechnology = LuaToString(l, -1);
99 		} else if (!strcmp(value, "Area")) {
100 			quest->Area = LuaToString(l, -1);
101 		} else if (!strcmp(value, "Briefing")) {
102 			quest->Briefing = LuaToString(l, -1);
103 		} else if (!strcmp(value, "BriefingBackground")) {
104 			quest->BriefingBackground = LuaToString(l, -1);
105 		} else if (!strcmp(value, "BriefingMusic")) {
106 			quest->BriefingMusic = LuaToString(l, -1);
107 		} else if (!strcmp(value, "LoadingMusic")) {
108 			quest->LoadingMusic = LuaToString(l, -1);
109 		} else if (!strcmp(value, "MapMusic")) {
110 			quest->MapMusic = LuaToString(l, -1);
111 		} else if (!strcmp(value, "StartSpeech")) {
112 			quest->StartSpeech = LuaToString(l, -1);
113 		} else if (!strcmp(value, "InProgressSpeech")) {
114 			quest->InProgressSpeech = LuaToString(l, -1);
115 		} else if (!strcmp(value, "CompletionSpeech")) {
116 			quest->CompletionSpeech = LuaToString(l, -1);
117 		} else if (!strcmp(value, "Rewards")) {
118 			quest->Rewards = LuaToString(l, -1);
119 		} else if (!strcmp(value, "Hint")) {
120 			quest->Hint = LuaToString(l, -1);
121 		} else if (!strcmp(value, "Civilization")) {
122 			CCivilization *civilization = CCivilization::GetCivilization(LuaToString(l, -1));
123 			if (civilization) {
124 				quest->Civilization = civilization->ID;
125 			}
126 		} else if (!strcmp(value, "PlayerColor")) {
127 			std::string color_name = LuaToString(l, -1);
128 			int color = GetPlayerColorIndexByName(color_name);
129 			if (color != -1) {
130 				quest->PlayerColor = color;
131 			} else {
132 				LuaError(l, "Player color \"%s\" doesn't exist." _C_ color_name.c_str());
133 			}
134 		} else if (!strcmp(value, "Hidden")) {
135 			quest->Hidden = LuaToBoolean(l, -1);
136 		} else if (!strcmp(value, "Competitive")) {
137 			quest->Competitive = LuaToBoolean(l, -1);
138 		} else if (!strcmp(value, "Unobtainable")) {
139 			quest->Unobtainable = LuaToBoolean(l, -1);
140 		} else if (!strcmp(value, "Uncompleteable")) {
141 			quest->Uncompleteable = LuaToBoolean(l, -1);
142 		} else if (!strcmp(value, "Unfailable")) {
143 			quest->Unfailable = LuaToBoolean(l, -1);
144 		} else if (!strcmp(value, "Icon")) {
145 			quest->Icon.Name = LuaToString(l, -1);
146 			quest->Icon.Icon = nullptr;
147 			quest->Icon.Load();
148 			quest->Icon.Icon->Load();
149 		} else if (!strcmp(value, "QuestGiver")) {
150 			std::string quest_giver_name = TransliterateText(LuaToString(l, -1));
151 			CCharacter *quest_giver = CCharacter::GetCharacter(quest_giver_name);
152 			if (quest_giver) {
153 				quest->QuestGiver = quest_giver;
154 			} else {
155 				LuaError(l, "Character \"%s\" doesn't exist." _C_ quest_giver_name.c_str());
156 			}
157 		} else if (!strcmp(value, "IntroductionDialogue")) {
158 			std::string dialogue_ident = LuaToString(l, -1);
159 			CDialogue *dialogue = CDialogue::GetDialogue(dialogue_ident);
160 			if (dialogue) {
161 				quest->IntroductionDialogue = dialogue;
162 			} else {
163 				LuaError(l, "Dialogue \"%s\" doesn't exist." _C_ dialogue_ident.c_str());
164 			}
165 		} else if (!strcmp(value, "Conditions")) {
166 			quest->Conditions = new LuaCallback(l, -1);
167 		} else if (!strcmp(value, "AcceptEffects")) {
168 			quest->AcceptEffects = new LuaCallback(l, -1);
169 		} else if (!strcmp(value, "CompletionEffects")) {
170 			quest->CompletionEffects = new LuaCallback(l, -1);
171 		} else if (!strcmp(value, "FailEffects")) {
172 			quest->FailEffects = new LuaCallback(l, -1);
173 		} else if (!strcmp(value, "ObjectiveStrings")) {
174 			quest->ObjectiveStrings.clear();
175 			const int args = lua_rawlen(l, -1);
176 			for (int j = 0; j < args; ++j) {
177 				quest->ObjectiveStrings.push_back(LuaToString(l, -1, j + 1));
178 			}
179 		} else if (!strcmp(value, "BriefingSounds")) {
180 			quest->BriefingSounds.clear();
181 			const int args = lua_rawlen(l, -1);
182 			for (int j = 0; j < args; ++j) {
183 				quest->BriefingSounds.push_back(LuaToString(l, -1, j + 1));
184 			}
185 		// objective types
186 		} else if (!strcmp(value, "Objectives")) {
187 			const int args = lua_rawlen(l, -1);
188 			for (int j = 0; j < args; ++j) {
189 				lua_rawgeti(l, -1, j + 1);
190 				CQuestObjective *objective = new CQuestObjective;
191 				objective->Quest = quest;
192 				quest->Objectives.push_back(objective);
193 				if (!lua_istable(l, -1)) {
194 					LuaError(l, "incorrect argument (expected table for quest objectives)");
195 				}
196 				const int subargs = lua_rawlen(l, -1);
197 				for (int k = 0; k < subargs; ++k) {
198 					value = LuaToString(l, -1, k + 1);
199 					++k;
200 					if (!strcmp(value, "objective-type")) {
201 						objective->ObjectiveType = GetQuestObjectiveTypeIdByName(LuaToString(l, -1, k + 1));
202 						if (objective->ObjectiveType == -1) {
203 							LuaError(l, "Objective type doesn't exist.");
204 						}
205 						if (objective->ObjectiveType == HeroMustSurviveObjectiveType) {
206 							objective->Quantity = 0;
207 						}
208 					} else if (!strcmp(value, "objective-string")) {
209 						objective->ObjectiveString = LuaToString(l, -1, k + 1);
210 					} else if (!strcmp(value, "quantity")) {
211 						objective->Quantity = LuaToNumber(l, -1, k + 1);
212 					} else if (!strcmp(value, "resource")) {
213 						int resource = GetResourceIdByName(LuaToString(l, -1, k + 1));
214 						if (resource == -1) {
215 							LuaError(l, "Resource doesn't exist.");
216 						}
217 						objective->Resource = resource;
218 					} else if (!strcmp(value, "unit-class")) {
219 						int unit_class = GetUnitTypeClassIndexByName(LuaToString(l, -1, k + 1));
220 						if (unit_class == -1) {
221 							LuaError(l, "Unit class doesn't exist.");
222 						}
223 						objective->UnitClass = unit_class;
224 					} else if (!strcmp(value, "unit-type")) {
225 						CUnitType *unit_type = UnitTypeByIdent(LuaToString(l, -1, k + 1));
226 						if (!unit_type) {
227 							LuaError(l, "Unit type doesn't exist.");
228 						}
229 						objective->UnitTypes.push_back(unit_type);
230 					} else if (!strcmp(value, "upgrade")) {
231 						CUpgrade *upgrade = CUpgrade::Get(LuaToString(l, -1, k + 1));
232 						if (!upgrade) {
233 							LuaError(l, "Upgrade doesn't exist.");
234 						}
235 						objective->Upgrade = upgrade;
236 					} else if (!strcmp(value, "character")) {
237 						CCharacter *character = CCharacter::GetCharacter(LuaToString(l, -1, k + 1));
238 						if (!character) {
239 							LuaError(l, "Character doesn't exist.");
240 						}
241 						objective->Character = character;
242 					} else if (!strcmp(value, "unique")) {
243 						CUniqueItem *unique = GetUniqueItem(LuaToString(l, -1, k + 1));
244 						if (!unique) {
245 							LuaError(l, "Unique doesn't exist.");
246 						}
247 						objective->Unique = unique;
248 					} else if (!strcmp(value, "settlement")) {
249 						CSite *site = CSite::GetSite(LuaToString(l, -1, k + 1));
250 						if (!site) {
251 							LuaError(l, "Site doesn't exist.");
252 						}
253 						objective->Settlement = site;
254 					} else if (!strcmp(value, "faction")) {
255 						CFaction *faction = PlayerRaces.GetFaction(LuaToString(l, -1, k + 1));
256 						if (!faction) {
257 							LuaError(l, "Faction doesn't exist.");
258 						}
259 						objective->Faction = faction;
260 					} else {
261 						printf("\n%s\n", quest->Ident.c_str());
262 						LuaError(l, "Unsupported tag: %s" _C_ value);
263 					}
264 				}
265 				lua_pop(l, 1);
266 			}
267 		} else if (!strcmp(value, "HeroesMustSurvive")) {
268 			quest->HeroesMustSurvive.clear();
269 			const int args = lua_rawlen(l, -1);
270 			for (int j = 0; j < args; ++j) {
271 				CCharacter *hero = CCharacter::GetCharacter(LuaToString(l, -1, j + 1));
272 				if (!hero) {
273 					LuaError(l, "Hero doesn't exist.");
274 				}
275 				quest->HeroesMustSurvive.push_back(hero);
276 			}
277 		} else {
278 			LuaError(l, "Unsupported tag: %s" _C_ value);
279 		}
280 	}
281 
282 	if (!quest->Hidden && quest->Civilization != -1 && std::find(CCivilization::Civilizations[quest->Civilization]->Quests.begin(), CCivilization::Civilizations[quest->Civilization]->Quests.end(), quest) == CCivilization::Civilizations[quest->Civilization]->Quests.end()) {
283 		CCivilization::Civilizations[quest->Civilization]->Quests.push_back(quest);
284 	}
285 
286 	return 0;
287 }
288 
CclGetQuests(lua_State * l)289 static int CclGetQuests(lua_State *l)
290 {
291 	lua_createtable(l, Quests.size(), 0);
292 	for (size_t i = 1; i <= Quests.size(); ++i)
293 	{
294 		lua_pushstring(l, Quests[i-1]->Ident.c_str());
295 		lua_rawseti(l, -2, i);
296 	}
297 	return 1;
298 }
299 
300 /**
301 **  Get quest data.
302 **
303 **  @param l  Lua state.
304 */
CclGetQuestData(lua_State * l)305 static int CclGetQuestData(lua_State *l)
306 {
307 	if (lua_gettop(l) < 2) {
308 		LuaError(l, "incorrect argument");
309 	}
310 	std::string quest_ident = LuaToString(l, 1);
311 	const CQuest *quest = GetQuest(quest_ident);
312 	if (!quest) {
313 		fprintf(stderr, "Quest \"%s\" doesn't exist.\n", quest_ident.c_str());
314 		lua_pushnil(l);
315 		return 0;
316 	}
317 	const char *data = LuaToString(l, 2);
318 
319 	if (!strcmp(data, "Name")) {
320 		lua_pushstring(l, quest->Name.c_str());
321 		return 1;
322 	} else if (!strcmp(data, "Description")) {
323 		lua_pushstring(l, quest->Description.c_str());
324 		return 1;
325 	} else if (!strcmp(data, "World")) {
326 		lua_pushstring(l, quest->World.c_str());
327 		return 1;
328 	} else if (!strcmp(data, "Map")) {
329 		lua_pushstring(l, quest->Map.c_str());
330 		return 1;
331 	} else if (!strcmp(data, "Scenario")) {
332 		lua_pushstring(l, quest->Scenario.c_str());
333 		return 1;
334 	} else if (!strcmp(data, "RequiredQuest")) {
335 		lua_pushstring(l, quest->RequiredQuest.c_str());
336 		return 1;
337 	} else if (!strcmp(data, "RequiredTechnology")) {
338 		lua_pushstring(l, quest->RequiredTechnology.c_str());
339 		return 1;
340 	} else if (!strcmp(data, "Area")) {
341 		lua_pushstring(l, quest->Area.c_str());
342 		return 1;
343 	} else if (!strcmp(data, "Briefing")) {
344 		lua_pushstring(l, quest->Briefing.c_str());
345 		return 1;
346 	} else if (!strcmp(data, "BriefingBackground")) {
347 		lua_pushstring(l, quest->BriefingBackground.c_str());
348 		return 1;
349 	} else if (!strcmp(data, "BriefingMusic")) {
350 		lua_pushstring(l, quest->BriefingMusic.c_str());
351 		return 1;
352 	} else if (!strcmp(data, "LoadingMusic")) {
353 		lua_pushstring(l, quest->LoadingMusic.c_str());
354 		return 1;
355 	} else if (!strcmp(data, "MapMusic")) {
356 		lua_pushstring(l, quest->MapMusic.c_str());
357 		return 1;
358 	} else if (!strcmp(data, "StartSpeech")) {
359 		lua_pushstring(l, quest->StartSpeech.c_str());
360 		return 1;
361 	} else if (!strcmp(data, "InProgressSpeech")) {
362 		lua_pushstring(l, quest->InProgressSpeech.c_str());
363 		return 1;
364 	} else if (!strcmp(data, "CompletionSpeech")) {
365 		lua_pushstring(l, quest->CompletionSpeech.c_str());
366 		return 1;
367 	} else if (!strcmp(data, "Civilization")) {
368 		if (quest->Civilization != -1) {
369 			lua_pushstring(l, PlayerRaces.Name[quest->Civilization].c_str());
370 		} else {
371 			lua_pushstring(l, "");
372 		}
373 		return 1;
374 	} else if (!strcmp(data, "PlayerColor")) {
375 		lua_pushstring(l, PlayerColorNames[quest->PlayerColor].c_str());
376 		return 1;
377 	} else if (!strcmp(data, "Hidden")) {
378 		lua_pushboolean(l, quest->Hidden);
379 		return 1;
380 	} else if (!strcmp(data, "Completed")) {
381 		lua_pushboolean(l, quest->Completed);
382 		return 1;
383 	} else if (!strcmp(data, "Competitive")) {
384 		lua_pushboolean(l, quest->Competitive);
385 		return 1;
386 	} else if (!strcmp(data, "HighestCompletedDifficulty")) {
387 		lua_pushnumber(l, quest->HighestCompletedDifficulty);
388 		return 1;
389 	} else if (!strcmp(data, "Icon")) {
390 		lua_pushstring(l, quest->Icon.Name.c_str());
391 		return 1;
392 	} else if (!strcmp(data, "QuestGiver")) {
393 		lua_pushstring(l, quest->QuestGiver->Ident.c_str());
394 		return 1;
395 	} else if (!strcmp(data, "Objectives")) {
396 		lua_createtable(l, quest->ObjectiveStrings.size(), 0);
397 		for (size_t i = 1; i <= quest->ObjectiveStrings.size(); ++i)
398 		{
399 			lua_pushstring(l, quest->ObjectiveStrings[i-1].c_str());
400 			lua_rawseti(l, -2, i);
401 		}
402 		return 1;
403 	} else if (!strcmp(data, "BriefingSounds")) {
404 		lua_createtable(l, quest->BriefingSounds.size(), 0);
405 		for (size_t i = 1; i <= quest->BriefingSounds.size(); ++i)
406 		{
407 			lua_pushstring(l, quest->BriefingSounds[i-1].c_str());
408 			lua_rawseti(l, -2, i);
409 		}
410 		return 1;
411 	} else {
412 		LuaError(l, "Invalid field: %s" _C_ data);
413 	}
414 
415 	return 0;
416 }
417 
418 /**
419 **  Define a campaign.
420 **
421 **  @param l  Lua state.
422 */
CclDefineCampaign(lua_State * l)423 static int CclDefineCampaign(lua_State *l)
424 {
425 	LuaCheckArgs(l, 2);
426 	if (!lua_istable(l, 2)) {
427 		LuaError(l, "incorrect argument (expected table)");
428 	}
429 
430 	std::string campaign_ident = LuaToString(l, 1);
431 	CCampaign *campaign = GetCampaign(campaign_ident);
432 	if (!campaign) {
433 		campaign = new CCampaign;
434 		campaign->ID = Campaigns.size();
435 		Campaigns.push_back(campaign);
436 		campaign->Ident = campaign_ident;
437 	}
438 
439 	//  Parse the list:
440 	for (lua_pushnil(l); lua_next(l, 2); lua_pop(l, 1)) {
441 		const char *value = LuaToString(l, -2);
442 
443 		if (!strcmp(value, "Name")) {
444 			campaign->Name = LuaToString(l, -1);
445 		} else if (!strcmp(value, "Description")) {
446 			campaign->Description = LuaToString(l, -1);
447 		} else if (!strcmp(value, "Faction")) {
448 			campaign->Faction = PlayerRaces.GetFaction(LuaToString(l, -1));
449 		} else if (!strcmp(value, "Hidden")) {
450 			campaign->Hidden = LuaToBoolean(l, -1);
451 		} else if (!strcmp(value, "Sandbox")) {
452 			campaign->Sandbox = LuaToBoolean(l, -1);
453 		} else if (!strcmp(value, "StartYear")) {
454 			campaign->StartDate.Year = LuaToNumber(l, -1);
455 		} else if (!strcmp(value, "StartDate")) {
456 			CclGetDate(l, &campaign->StartDate);
457 		} else if (!strcmp(value, "StartEffects")) {
458 			campaign->StartEffects = new LuaCallback(l, -1);
459 		} else if (!strcmp(value, "RequiredQuests")) {
460 			campaign->RequiredQuests.clear();
461 			const int args = lua_rawlen(l, -1);
462 			for (int j = 0; j < args; ++j) {
463 				std::string quest_ident = LuaToString(l, -1, j + 1);
464 				CQuest *required_quest = GetQuest(quest_ident);
465 				if (required_quest) {
466 					campaign->RequiredQuests.push_back(required_quest);
467 				} else {
468 					LuaError(l, "Quest \"%s\" doesn't exist." _C_ quest_ident.c_str());
469 				}
470 			}
471 		} else if (!strcmp(value, "MapTemplates")) {
472 			campaign->MapTemplates.clear();
473 			campaign->MapSizes.clear();
474 			campaign->MapTemplateStartPos.clear();
475 			const int args = lua_rawlen(l, -1);
476 			for (int j = 0; j < args; ++j) {
477 				std::string map_template_ident = LuaToString(l, -1, j + 1);
478 				CMapTemplate *map_template = CMapTemplate::GetOrAddMapTemplate(map_template_ident);
479 				campaign->MapTemplates.push_back(map_template);
480 				++j;
481 
482 				lua_rawgeti(l, -1, j + 1);
483 				Vec2i map_template_start_pos;
484 				CclGetPos(l, &map_template_start_pos.x, &map_template_start_pos.y);
485 				campaign->MapTemplateStartPos.push_back(map_template_start_pos);
486 				lua_pop(l, 1);
487 				++j;
488 
489 				lua_rawgeti(l, -1, j + 1);
490 				Vec2i map_size;
491 				CclGetPos(l, &map_size.x, &map_size.y);
492 				campaign->MapSizes.push_back(map_size);
493 				lua_pop(l, 1);
494 			}
495 		} else if (!strcmp(value, "MapTemplate")) {
496 			std::string map_template_ident = LuaToString(l, -1);
497 			CMapTemplate *map_template = CMapTemplate::GetOrAddMapTemplate(map_template_ident);
498 			campaign->MapTemplates.push_back(map_template);
499 		} else if (!strcmp(value, "MapTemplateStartPos")) {
500 			Vec2i map_template_start_pos;
501 			CclGetPos(l, &map_template_start_pos.x, &map_template_start_pos.y);
502 			campaign->MapTemplateStartPos.push_back(map_template_start_pos);
503 		} else if (!strcmp(value, "MapSize")) {
504 			Vec2i map_size;
505 			CclGetPos(l, &map_size.x, &map_size.y);
506 			campaign->MapSizes.push_back(map_size);
507 		} else {
508 			LuaError(l, "Unsupported tag: %s" _C_ value);
509 		}
510 	}
511 
512 	return 0;
513 }
514 
CclGetCampaigns(lua_State * l)515 static int CclGetCampaigns(lua_State *l)
516 {
517 	lua_createtable(l, Campaigns.size(), 0);
518 	for (size_t i = 1; i <= Campaigns.size(); ++i)
519 	{
520 		lua_pushstring(l, Campaigns[i-1]->Ident.c_str());
521 		lua_rawseti(l, -2, i);
522 	}
523 	return 1;
524 }
525 
526 /**
527 **  Get campaign data.
528 **
529 **  @param l  Lua state.
530 */
CclGetCampaignData(lua_State * l)531 static int CclGetCampaignData(lua_State *l)
532 {
533 	if (lua_gettop(l) < 2) {
534 		LuaError(l, "incorrect argument");
535 	}
536 	std::string campaign_ident = LuaToString(l, 1);
537 	const CCampaign *campaign = GetCampaign(campaign_ident);
538 	if (!campaign) {
539 		LuaError(l, "Campaign \"%s\" doesn't exist." _C_ campaign_ident.c_str());
540 	}
541 	const char *data = LuaToString(l, 2);
542 
543 	if (!strcmp(data, "Name")) {
544 		lua_pushstring(l, campaign->Name.c_str());
545 		return 1;
546 	} else if (!strcmp(data, "Description")) {
547 		lua_pushstring(l, campaign->Description.c_str());
548 		return 1;
549 	} else if (!strcmp(data, "StartYear")) {
550 		lua_pushnumber(l, campaign->StartDate.Year);
551 		return 1;
552 	} else if (!strcmp(data, "Faction")) {
553 		if (campaign->Faction) {
554 			lua_pushstring(l, campaign->Faction->Ident.c_str());
555 		} else {
556 			lua_pushstring(l, "");
557 		}
558 		return 1;
559 	} else if (!strcmp(data, "Hidden")) {
560 		lua_pushboolean(l, campaign->Hidden);
561 		return 1;
562 	} else if (!strcmp(data, "Sandbox")) {
563 		lua_pushboolean(l, campaign->Sandbox);
564 		return 1;
565 	} else if (!strcmp(data, "RequiredQuests")) {
566 		lua_createtable(l, campaign->RequiredQuests.size(), 0);
567 		for (size_t i = 1; i <= campaign->RequiredQuests.size(); ++i)
568 		{
569 			lua_pushstring(l, campaign->RequiredQuests[i-1]->Ident.c_str());
570 			lua_rawseti(l, -2, i);
571 		}
572 		return 1;
573 	} else if (!strcmp(data, "MapTemplate")) {
574 		if (!campaign->MapTemplates.empty()) {
575 			lua_pushstring(l, campaign->MapTemplates[0]->Ident.c_str());
576 		} else {
577 			lua_pushstring(l, "");
578 		}
579 		return 1;
580 	} else if (!strcmp(data, "MapWidth")) {
581 		lua_pushnumber(l, campaign->MapSizes[0].x);
582 		return 1;
583 	} else if (!strcmp(data, "MapHeight")) {
584 		lua_pushnumber(l, campaign->MapSizes[0].y);
585 		return 1;
586 	} else if (!strcmp(data, "MapTemplateStartPosX")) {
587 		lua_pushnumber(l, campaign->MapTemplateStartPos[0].x);
588 		return 1;
589 	} else if (!strcmp(data, "MapTemplateStartPosY")) {
590 		lua_pushnumber(l, campaign->MapTemplateStartPos[0].y);
591 		return 1;
592 	} else {
593 		LuaError(l, "Invalid field: %s" _C_ data);
594 	}
595 
596 	return 0;
597 }
598 
599 /**
600 **  Define an achievement.
601 **
602 **  @param l  Lua state.
603 */
CclDefineAchievement(lua_State * l)604 static int CclDefineAchievement(lua_State *l)
605 {
606 	LuaCheckArgs(l, 2);
607 	if (!lua_istable(l, 2)) {
608 		LuaError(l, "incorrect argument (expected table)");
609 	}
610 
611 	std::string achievement_ident = LuaToString(l, 1);
612 	CAchievement *achievement = GetAchievement(achievement_ident);
613 	if (!achievement) {
614 		achievement = new CAchievement;
615 		Achievements.push_back(achievement);
616 		achievement->Ident = achievement_ident;
617 	}
618 
619 	//  Parse the list:
620 	for (lua_pushnil(l); lua_next(l, 2); lua_pop(l, 1)) {
621 		const char *value = LuaToString(l, -2);
622 
623 		if (!strcmp(value, "Name")) {
624 			achievement->Name = LuaToString(l, -1);
625 		} else if (!strcmp(value, "Description")) {
626 			achievement->Description = LuaToString(l, -1);
627 		} else if (!strcmp(value, "PlayerColor")) {
628 			std::string color_name = LuaToString(l, -1);
629 			int color = GetPlayerColorIndexByName(color_name);
630 			if (color != -1) {
631 				achievement->PlayerColor = color;
632 			} else {
633 				LuaError(l, "Player color \"%s\" doesn't exist." _C_ color_name.c_str());
634 			}
635 		} else if (!strcmp(value, "CharacterLevel")) {
636 			achievement->CharacterLevel = LuaToNumber(l, -1);
637 		} else if (!strcmp(value, "Difficulty")) {
638 			achievement->Difficulty = LuaToNumber(l, -1);
639 		} else if (!strcmp(value, "Hidden")) {
640 			achievement->Hidden = LuaToBoolean(l, -1);
641 		} else if (!strcmp(value, "Unobtainable")) {
642 			achievement->Unobtainable = LuaToBoolean(l, -1);
643 		} else if (!strcmp(value, "Icon")) {
644 			achievement->Icon.Name = LuaToString(l, -1);
645 			achievement->Icon.Icon = nullptr;
646 			achievement->Icon.Load();
647 			achievement->Icon.Icon->Load();
648 		} else if (!strcmp(value, "Character")) {
649 			std::string character_name = TransliterateText(LuaToString(l, -1));
650 			CCharacter *character = CCharacter::GetCharacter(character_name);
651 			if (character) {
652 				achievement->Character = character;
653 			} else {
654 				LuaError(l, "Character \"%s\" doesn't exist." _C_ character_name.c_str());
655 			}
656 		} else if (!strcmp(value, "CharacterType")) {
657 			std::string unit_type_ident = LuaToString(l, -1);
658 			int unit_type_id = UnitTypeIdByIdent(unit_type_ident);
659 			if (unit_type_id != -1) {
660 				achievement->CharacterType = UnitTypes[unit_type_id];
661 			} else {
662 				LuaError(l, "Unit type \"%s\" doesn't exist." _C_ unit_type_ident.c_str());
663 			}
664 		} else if (!strcmp(value, "RequiredQuests")) {
665 			achievement->RequiredQuests.clear();
666 			const int args = lua_rawlen(l, -1);
667 			for (int j = 0; j < args; ++j) {
668 				std::string quest_ident = LuaToString(l, -1, j + 1);
669 				CQuest *required_quest = GetQuest(quest_ident);
670 				if (required_quest) {
671 					achievement->RequiredQuests.push_back(required_quest);
672 				} else {
673 					LuaError(l, "Quest \"%s\" doesn't exist." _C_ quest_ident.c_str());
674 				}
675 			}
676 		} else {
677 			LuaError(l, "Unsupported tag: %s" _C_ value);
678 		}
679 	}
680 
681 	return 0;
682 }
683 
CclGetAchievements(lua_State * l)684 static int CclGetAchievements(lua_State *l)
685 {
686 	lua_createtable(l, Achievements.size(), 0);
687 	for (size_t i = 1; i <= Achievements.size(); ++i)
688 	{
689 		lua_pushstring(l, Achievements[i-1]->Ident.c_str());
690 		lua_rawseti(l, -2, i);
691 	}
692 	return 1;
693 }
694 
695 /**
696 **  Get achievement data.
697 **
698 **  @param l  Lua state.
699 */
CclGetAchievementData(lua_State * l)700 static int CclGetAchievementData(lua_State *l)
701 {
702 	if (lua_gettop(l) < 2) {
703 		LuaError(l, "incorrect argument");
704 	}
705 	std::string achievement_ident = LuaToString(l, 1);
706 	const CAchievement *achievement = GetAchievement(achievement_ident);
707 	if (!achievement) {
708 		LuaError(l, "Achievement \"%s\" doesn't exist." _C_ achievement_ident.c_str());
709 	}
710 	const char *data = LuaToString(l, 2);
711 
712 	if (!strcmp(data, "Name")) {
713 		lua_pushstring(l, achievement->Name.c_str());
714 		return 1;
715 	} else if (!strcmp(data, "Description")) {
716 		lua_pushstring(l, achievement->Description.c_str());
717 		return 1;
718 	} else if (!strcmp(data, "PlayerColor")) {
719 		lua_pushstring(l, PlayerColorNames[achievement->PlayerColor].c_str());
720 		return 1;
721 	} else if (!strcmp(data, "Character")) {
722 		if (achievement->Character) {
723 			lua_pushstring(l, achievement->Character->Ident.c_str());
724 		} else {
725 			lua_pushstring(l, "");
726 		}
727 		return 1;
728 	} else if (!strcmp(data, "CharacterType")) {
729 		if (achievement->CharacterType) {
730 			lua_pushstring(l, achievement->CharacterType->Ident.c_str());
731 		} else {
732 			lua_pushstring(l, "");
733 		}
734 		return 1;
735 	} else if (!strcmp(data, "CharacterLevel")) {
736 		lua_pushnumber(l, achievement->CharacterLevel);
737 		return 1;
738 	} else if (!strcmp(data, "Difficulty")) {
739 		lua_pushnumber(l, achievement->Difficulty);
740 		return 1;
741 	} else if (!strcmp(data, "Hidden")) {
742 		lua_pushboolean(l, achievement->Hidden);
743 		return 1;
744 	} else if (!strcmp(data, "Obtained")) {
745 		lua_pushboolean(l, achievement->Obtained);
746 		return 1;
747 	} else if (!strcmp(data, "Unobtainable")) {
748 		lua_pushboolean(l, achievement->Unobtainable);
749 		return 1;
750 	} else if (!strcmp(data, "Progress")) {
751 		lua_pushnumber(l, achievement->GetProgress());
752 		return 1;
753 	} else if (!strcmp(data, "ProgressMax")) {
754 		lua_pushnumber(l, achievement->GetProgressMax());
755 		return 1;
756 	} else if (!strcmp(data, "Icon")) {
757 		lua_pushstring(l, achievement->Icon.Name.c_str());
758 		return 1;
759 	} else {
760 		LuaError(l, "Invalid field: %s" _C_ data);
761 	}
762 
763 	return 0;
764 }
765 
766 /**
767 **  Define a dialogue.
768 **
769 **  @param l  Lua state.
770 */
CclDefineDialogue(lua_State * l)771 static int CclDefineDialogue(lua_State *l)
772 {
773 	LuaCheckArgs(l, 2);
774 	if (!lua_istable(l, 2)) {
775 		LuaError(l, "incorrect argument (expected table)");
776 	}
777 
778 	std::string dialogue_ident = LuaToString(l, 1);
779 	CDialogue *dialogue = CDialogue::GetOrAddDialogue(dialogue_ident);
780 
781 	//  Parse the list:
782 	for (lua_pushnil(l); lua_next(l, 2); lua_pop(l, 1)) {
783 		const char *value = LuaToString(l, -2);
784 
785 		if (!strcmp(value, "Nodes")) {
786 			const int args = lua_rawlen(l, -1);
787 			for (int j = 0; j < args; ++j) {
788 				lua_rawgeti(l, -1, j + 1);
789 				CDialogueNode *node = new CDialogueNode;
790 				node->ID = dialogue->Nodes.size();
791 				dialogue->Nodes.push_back(node);
792 				node->Dialogue = dialogue;
793 				if (!lua_istable(l, -1)) {
794 					LuaError(l, "incorrect argument (expected table for dialogue nodes)");
795 				}
796 				const int subargs = lua_rawlen(l, -1);
797 				for (int k = 0; k < subargs; ++k) {
798 					value = LuaToString(l, -1, k + 1);
799 					++k;
800 					if (!strcmp(value, "speaker")) {
801 						node->SpeakerType = LuaToString(l, -1, k + 1);
802 						++k;
803 						node->Speaker = LuaToString(l, -1, k + 1);
804 					} else if (!strcmp(value, "speaker-player")) {
805 						node->SpeakerPlayer = LuaToString(l, -1, k + 1);
806 					} else if (!strcmp(value, "text")) {
807 						node->Text = LuaToString(l, -1, k + 1);
808 					} else if (!strcmp(value, "conditions")) {
809 						lua_rawgeti(l, -1, k + 1);
810 						node->Conditions = new LuaCallback(l, -1);
811 						lua_pop(l, 1);
812 					} else if (!strcmp(value, "immediate-effects")) {
813 						lua_rawgeti(l, -1, k + 1);
814 						node->ImmediateEffects = new LuaCallback(l, -1);
815 						lua_pop(l, 1);
816 					} else if (!strcmp(value, "options")) {
817 						lua_rawgeti(l, -1, k + 1);
818 						const int subsubargs = lua_rawlen(l, -1);
819 						for (int n = 0; n < subsubargs; ++n) {
820 							node->Options.push_back(LuaToString(l, -1, n + 1));
821 						}
822 						lua_pop(l, 1);
823 					} else if (!strcmp(value, "option-effects")) {
824 						lua_rawgeti(l, -1, k + 1);
825 						const int subsubargs = lua_rawlen(l, -1);
826 						for (int n = 0; n < subsubargs; ++n) {
827 							lua_rawgeti(l, -1, n + 1);
828 							node->OptionEffects.push_back(new LuaCallback(l, -1));
829 							lua_pop(l, 1);
830 						}
831 						lua_pop(l, 1);
832 					} else if (!strcmp(value, "option-tooltips")) {
833 						lua_rawgeti(l, -1, k + 1);
834 						const int subsubargs = lua_rawlen(l, -1);
835 						for (int n = 0; n < subsubargs; ++n) {
836 							node->OptionTooltips.push_back(LuaToString(l, -1, n + 1));
837 						}
838 						lua_pop(l, 1);
839 					} else {
840 						printf("\n%s\n", dialogue->Ident.c_str());
841 						LuaError(l, "Unsupported tag: %s" _C_ value);
842 					}
843 				}
844 				lua_pop(l, 1);
845 			}
846 		} else {
847 			LuaError(l, "Unsupported tag: %s" _C_ value);
848 		}
849 	}
850 
851 	return 0;
852 }
853 
854 // ----------------------------------------------------------------------------
855 
856 /**
857 **  Register CCL features for quests.
858 */
QuestCclRegister()859 void QuestCclRegister()
860 {
861 	lua_register(Lua, "DefineQuest", CclDefineQuest);
862 	lua_register(Lua, "GetQuests", CclGetQuests);
863 	lua_register(Lua, "GetQuestData", CclGetQuestData);
864 	lua_register(Lua, "DefineCampaign", CclDefineCampaign);
865 	lua_register(Lua, "GetCampaigns", CclGetCampaigns);
866 	lua_register(Lua, "GetCampaignData", CclGetCampaignData);
867 	lua_register(Lua, "DefineAchievement", CclDefineAchievement);
868 	lua_register(Lua, "GetAchievements", CclGetAchievements);
869 	lua_register(Lua, "GetAchievementData", CclGetAchievementData);
870 	lua_register(Lua, "DefineDialogue", CclDefineDialogue);
871 }
872 
873 //@}
874