1 /*-------------------------------------------------------------------------------
2
3 BARONY
4 File: mod_tools.cpp
5 Desc: misc modding tools
6
7 Copyright 2013-2016 (c) Turning Wheel LLC, all rights reserved.
8 See LICENSE for details.
9
10 -------------------------------------------------------------------------------*/
11 #include "items.hpp"
12 #include "mod_tools.hpp"
13 #include "menu.hpp"
14 #include "classdescriptions.hpp"
15 #include "draw.hpp"
16
17 MonsterStatCustomManager monsterStatCustomManager;
18 MonsterCurveCustomManager monsterCurveCustomManager;
19 GameplayCustomManager gameplayCustomManager;
20 GameModeManager_t gameModeManager;
21 const std::vector<std::string> MonsterStatCustomManager::itemStatusStrings =
22 {
23 "broken",
24 "decrepit",
25 "worn",
26 "serviceable",
27 "excellent"
28 };
29
30 const std::vector<std::string> MonsterStatCustomManager::shopkeeperTypeStrings =
31 {
32 "equipment",
33 "hats",
34 "jewelry",
35 "books",
36 "apothecary",
37 "staffs",
38 "food",
39 "hardware",
40 "hunting",
41 "general"
42 };
43
startTutorial(std::string mapToSet)44 void GameModeManager_t::Tutorial_t::startTutorial(std::string mapToSet)
45 {
46 if ( mapToSet.compare("") == 0 )
47 {
48 launchHub();
49 }
50 else
51 {
52 if ( mapToSet.find(".lmp") == std::string::npos )
53 {
54 mapToSet.append(".lmp");
55 }
56 setTutorialMap(mapToSet);
57 }
58 gameModeManager.setMode(gameModeManager.GameModes::GAME_MODE_TUTORIAL);
59 stats[0]->clearStats();
60 strcpy(stats[0]->name, "Player");
61 stats[0]->sex = static_cast<sex_t>(rand() % 2);
62 stats[0]->playerRace = RACE_HUMAN;
63 stats[0]->appearance = rand() % NUMAPPEARANCES;
64 initClass(0);
65 }
66
buttonReturnToTutorialHub(button_t * my)67 void GameModeManager_t::Tutorial_t::buttonReturnToTutorialHub(button_t* my)
68 {
69 buttonStartSingleplayer(nullptr);
70 gameModeManager.Tutorial.launchHub();
71 }
72
buttonRestartTrial(button_t * my)73 void GameModeManager_t::Tutorial_t::buttonRestartTrial(button_t* my)
74 {
75 std::string mapname = map.name;
76 if ( mapname.find("Tutorial Hub") == std::string::npos
77 && mapname.find("Tutorial ") != std::string::npos )
78 {
79 buttonStartSingleplayer(nullptr);
80 std::string number = mapname.substr(mapname.find("Tutorial ") + strlen("Tutorial "), 2);
81 std::string filename = "tutorial";
82 filename.append(std::to_string(stoi(number)));
83 filename.append(".lmp");
84 gameModeManager.Tutorial.setTutorialMap(filename);
85 gameModeManager.Tutorial.dungeonLevel = currentlevel;
86
87 int tutorialNum = stoi(number);
88 if ( tutorialNum > 0 && tutorialNum <= gameModeManager.Tutorial.kNumTutorialLevels )
89 {
90 gameModeManager.Tutorial.onMapRestart(tutorialNum);
91 }
92 return;
93 }
94 buttonReturnToTutorialHub(nullptr);
95 }
96
openGameoverWindow()97 void GameModeManager_t::Tutorial_t::openGameoverWindow()
98 {
99 node_t* node;
100 buttonCloseSubwindow(nullptr);
101 list_FreeAll(&button_l);
102 deleteallbuttons = true;
103
104 subwindow = 1;
105 subx1 = xres / 2 - 288;
106 subx2 = xres / 2 + 288;
107 suby1 = yres / 2 - 160;
108 suby2 = yres / 2 + 160;
109 button_t* button;
110
111 shootmode = false;
112 strcpy(subtext, language[1133]);
113 strcat(subtext, language[1134]);
114 strcat(subtext, language[1137]);
115
116
117 // identify all inventory items
118 for ( node = stats[clientnum]->inventory.first; node != NULL; node = node->next )
119 {
120 Item* item = (Item*)node->element;
121 item->identified = true;
122 }
123
124 // Restart
125 button = newButton();
126 strcpy(button->label, language[3957]);
127 button->x = subx2 - strlen(language[3957]) * 12 - 16;
128 button->y = suby2 - 28;
129 button->sizex = strlen(language[3957]) * 12 + 8;
130 button->sizey = 20;
131 button->action = &buttonRestartTrial;
132 button->visible = 1;
133 button->focused = 1;
134 button->joykey = joyimpulses[INJOY_MENU_NEXT];
135
136 // Return to Hub
137 button = newButton();
138 strcpy(button->label, language[3958]);
139 button->x = subx1 + 8;
140 button->y = suby2 - 28;
141 button->sizex = strlen(language[3958]) * 12 + 8;
142 button->sizey = 20;
143 button->action = &buttonReturnToTutorialHub;
144 button->visible = 1;
145 button->focused = 1;
146 button->joykey = joyimpulses[INJOY_MENU_CANCEL];
147
148 // death hints
149 if ( currentlevel / LENGTH_OF_LEVEL_REGION < 1 )
150 {
151 strcat(subtext, language[1145 + rand() % 15]);
152 }
153
154 // close button
155 button = newButton();
156 strcpy(button->label, "x");
157 button->x = subx2 - 20;
158 button->y = suby1;
159 button->sizex = 20;
160 button->sizey = 20;
161 button->action = &buttonCloseSubwindow;
162 button->visible = 1;
163 button->focused = 1;
164 button->key = SDL_SCANCODE_ESCAPE;
165 button->joykey = joyimpulses[INJOY_MENU_CANCEL];
166 }
167
readFromFile()168 void GameModeManager_t::Tutorial_t::readFromFile()
169 {
170 levels.clear();
171 if ( PHYSFS_getRealDir("/data/tutorial_strings.json") )
172 {
173 std::string inputPath = PHYSFS_getRealDir("/data/tutorial_strings.json");
174 inputPath.append("/data/tutorial_strings.json");
175
176 FILE* fp = fopen(inputPath.c_str(), "rb");
177 if ( !fp )
178 {
179 printlog("[JSON]: Error: Could not locate json file %s", inputPath.c_str());
180 return;
181 }
182 char buf[65536];
183 rapidjson::FileReadStream is(fp, buf, sizeof(buf));
184 fclose(fp);
185
186 rapidjson::Document d;
187 d.ParseStream(is);
188 if ( !d.HasMember("version") || !d.HasMember("levels") )
189 {
190 printlog("[JSON]: Error: No 'version' value in json file, or JSON syntax incorrect! %s", inputPath.c_str());
191 return;
192 }
193 int version = d["version"].GetInt();
194
195 for ( rapidjson::Value::ConstMemberIterator level_itr = d["levels"].MemberBegin(); level_itr != d["levels"].MemberEnd(); ++level_itr )
196 {
197 Tutorial_t::Level_t level;
198 level.filename = level_itr->name.GetString();
199 level.title = level_itr->value["title"].GetString();
200 level.description = level_itr->value["desc"].GetString();
201 levels.push_back(level);
202 }
203 Menu.windowTitle = d["window_title"].GetString();
204 Menu.defaultHoverText = d["default_hover_text"].GetString();
205 printlog("[JSON]: Successfully read json file %s", inputPath.c_str());
206 }
207 else
208 {
209 return;
210 }
211
212 if ( PHYSFS_getRealDir("/data/tutorial_scores.json") )
213 {
214 std::string inputPath = PHYSFS_getRealDir("/data/tutorial_scores.json");
215 inputPath.append("/data/tutorial_scores.json");
216
217 FILE* fp = fopen(inputPath.c_str(), "rb");
218 if ( !fp )
219 {
220 printlog("[JSON]: Error: Could not locate json file %s", inputPath.c_str());
221 return;
222 }
223 char buf[65536];
224 rapidjson::FileReadStream is(fp, buf, sizeof(buf));
225 fclose(fp);
226
227 rapidjson::Document d;
228 d.ParseStream(is);
229 if ( !d.HasMember("version") || !d.HasMember("levels") )
230 {
231 printlog("[JSON]: Error: No 'version' value in json file, or JSON syntax incorrect! %s", inputPath.c_str());
232 return;
233 }
234 int version = d["version"].GetInt();
235
236 this->FirstTimePrompt.showFirstTimePrompt = d["first_time_prompt"].GetBool();
237
238 for ( auto it = levels.begin(); it != levels.end(); ++it )
239 {
240 if ( d["levels"].HasMember(it->filename.c_str()) && d["levels"][it->filename.c_str()].HasMember("completion_time") )
241 {
242 it->completionTime = d["levels"][it->filename.c_str()]["completion_time"].GetUint();
243 }
244 }
245 printlog("[JSON]: Successfully read json file %s", inputPath.c_str());
246 }
247 else
248 {
249 printlog("[JSON]: File /data/tutorial_scores.json does not exist, creating...");
250
251 rapidjson::Document d;
252 d.SetObject();
253 CustomHelpers::addMemberToRoot(d, "version", rapidjson::Value(1));
254 CustomHelpers::addMemberToRoot(d, "first_time_prompt", rapidjson::Value(true));
255
256 this->FirstTimePrompt.showFirstTimePrompt = true;
257
258 rapidjson::Value levelsObj(rapidjson::kObjectType);
259 CustomHelpers::addMemberToRoot(d, "levels", levelsObj);
260 for ( auto it = levels.begin(); it != levels.end(); ++it )
261 {
262 rapidjson::Value level(rapidjson::kObjectType);
263 level.AddMember("completion_time", rapidjson::Value(it->completionTime), d.GetAllocator());
264 CustomHelpers::addMemberToSubkey(d, "levels", it->filename, level);
265 }
266 writeToFile(d);
267 }
268 }
269
writeToDocument()270 void GameModeManager_t::Tutorial_t::writeToDocument()
271 {
272 if ( levels.empty() )
273 {
274 printlog("[JSON]: Could not write tutorial scores to file due to empty levels vector.");
275 return;
276 }
277
278 if ( !PHYSFS_getRealDir("/data/tutorial_scores.json") )
279 {
280 printlog("[JSON]: Error file /data/tutorial_scores.json does not exist");
281 return;
282 }
283
284 std::string inputPath = PHYSFS_getRealDir("/data/tutorial_scores.json");
285 inputPath.append("/data/tutorial_scores.json");
286
287 FILE* fp = fopen(inputPath.c_str(), "rb");
288 if ( !fp )
289 {
290 printlog("[JSON]: Error: Could not locate json file %s", inputPath.c_str());
291 return;
292 }
293 char buf[65536];
294 rapidjson::FileReadStream is(fp, buf, sizeof(buf));
295 fclose(fp);
296
297 rapidjson::Document d;
298 d.ParseStream(is);
299
300 d["first_time_prompt"].SetBool(this->FirstTimePrompt.showFirstTimePrompt);
301
302 for ( auto it = levels.begin(); it != levels.end(); ++it )
303 {
304 d["levels"][it->filename.c_str()]["completion_time"].SetUint(it->completionTime);
305 }
306
307 writeToFile(d);
308 }
309
open()310 void GameModeManager_t::Tutorial_t::Menu_t::open()
311 {
312 bWindowOpen = true;
313 windowScroll = 0;
314 this->selectedMenuItem = -1;
315
316 // create window
317 subwindow = 1;
318 subx1 = xres / 2 - 420;
319 subx2 = xres / 2 + 420;
320 suby1 = yres / 2 - 300;
321 suby2 = yres / 2 + 300;
322 strcpy(subtext, "Hall of Trials");
323
324 // close button
325 button_t* button = newButton();
326 strcpy(button->label, "x");
327 button->x = subx2 - 20;
328 button->y = suby1;
329 button->sizex = 20;
330 button->sizey = 20;
331 button->action = &buttonCloseSubwindow;
332 button->visible = 1;
333 button->focused = 1;
334 button->key = SDL_SCANCODE_ESCAPE;
335 button->joykey = joyimpulses[INJOY_MENU_CANCEL];
336 }
337
onClickEntry()338 void GameModeManager_t::Tutorial_t::Menu_t::onClickEntry()
339 {
340 if ( this->selectedMenuItem == -1 )
341 {
342 return;
343 }
344 buttonStartSingleplayer(nullptr);
345 gameModeManager.setMode(GameModeManager_t::GAME_MODE_TUTORIAL_INIT);
346 if ( gameModeManager.Tutorial.FirstTimePrompt.showFirstTimePrompt )
347 {
348 gameModeManager.Tutorial.FirstTimePrompt.showFirstTimePrompt = false;
349 gameModeManager.Tutorial.writeToDocument();
350 }
351 gameModeManager.Tutorial.startTutorial(gameModeManager.Tutorial.levels.at(this->selectedMenuItem).filename);
352 steamStatisticUpdate(STEAM_STAT_TUTORIAL_ENTERED, ESteamStatTypes::STEAM_STAT_INT, 1);
353 }
354
createPrompt()355 void GameModeManager_t::Tutorial_t::FirstTimePrompt_t::createPrompt()
356 {
357 bWindowOpen = true;
358 showFirstTimePrompt = false;
359
360 if ( !title_bmp )
361 {
362 return;
363 }
364
365 // create window
366 subwindow = 1;
367 subx1 = xres / 2 - ((0.75 * title_bmp->w / 2) + 52);
368 subx2 = xres / 2 + ((0.75 * title_bmp->w / 2) + 52);
369 suby1 = yres / 2 - ((0.75 * title_bmp->h / 2) + 88);
370 suby2 = yres / 2 + ((0.75 * title_bmp->h / 2) + 88);
371 strcpy(subtext, "");
372
373 Uint32 centerWindowX = subx1 + (subx2 - subx1) / 2;
374
375 button_t* button = newButton();
376 strcpy(button->label, language[3965]);
377 button->sizex = strlen(language[3965]) * 10 + 8;
378 button->sizey = 20;
379 button->x = centerWindowX - button->sizex / 2;
380 button->y = suby2 - 28 - 24;
381 button->action = &buttonPromptEnterTutorialHub;
382 button->visible = 1;
383 button->focused = 1;
384
385 button = newButton();
386 strcpy(button->label, language[3966]);
387 button->sizex = strlen(language[3966]) * 12 + 8;
388 button->sizey = 20;
389 button->x = centerWindowX - button->sizex / 2;
390 button->y = suby2 - 28;
391 button->action = &buttonSkipPrompt;
392 button->visible = 1;
393 button->focused = 1;
394 }
395
drawDialogue()396 void GameModeManager_t::Tutorial_t::FirstTimePrompt_t::drawDialogue()
397 {
398 if ( !bWindowOpen )
399 {
400 return;
401 }
402 Uint32 centerWindowX = subx1 + (subx2 - subx1) / 2;
403
404 SDL_Rect pos;
405 pos.x = centerWindowX - 0.75 * title_bmp->w / 2;
406 pos.y = suby1 + 4;
407 pos.w = 0.75 * title_bmp->w;
408 pos.h = 0.75 * title_bmp->h;
409 SDL_Rect scaled;
410 scaled.x = 0;
411 scaled.y = 0;
412 scaled.w = title_bmp->w * 0.75;
413 scaled.h = title_bmp->h * 0.75;
414 drawImageScaled(title_bmp, &scaled, &pos);
415
416 ttfPrintTextFormattedColor(ttf12, centerWindowX - strlen(language[3936]) * TTF12_WIDTH / 2, suby2 + 8 - TTF12_HEIGHT * 13, SDL_MapRGB(mainsurface->format, 255, 255, 0), language[3936]);
417 ttfPrintTextFormatted(ttf12, centerWindowX - (longestline(language[3967]) * TTF12_WIDTH) / 2, suby2 + 8 - TTF12_HEIGHT * 11, language[3967]);
418 ttfPrintTextFormatted(ttf12, centerWindowX - (longestline(language[3967]) * TTF12_WIDTH) / 2 - TTF12_WIDTH / 2, suby2 + 8 - TTF12_HEIGHT * 11, language[3968]);
419 }
420
buttonSkipPrompt(button_t * my)421 void GameModeManager_t::Tutorial_t::FirstTimePrompt_t::buttonSkipPrompt(button_t* my)
422 {
423 gameModeManager.Tutorial.FirstTimePrompt.doButtonSkipPrompt = true;
424 gameModeManager.Tutorial.writeToDocument();
425 }
426
buttonPromptEnterTutorialHub(button_t * my)427 void GameModeManager_t::Tutorial_t::FirstTimePrompt_t::buttonPromptEnterTutorialHub(button_t* my)
428 {
429 gameModeManager.Tutorial.Menu.selectedMenuItem = 0; // set the tutorial hub to enter.
430 gameModeManager.Tutorial.Menu.onClickEntry();
431 gameModeManager.Tutorial.writeToDocument();
432 }