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 }