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