1 /*-------------------------------------------------------------------------------
2 
3 	BARONY
4 	File: init_game.cpp
5 	Desc: contains game specific initialization code that shouldn't be
6 	seen in the editor.
7 
8 	Copyright 2013-2016 (c) Turning Wheel LLC, all rights reserved.
9 	See LICENSE for details.
10 
11 -------------------------------------------------------------------------------*/
12 
13 #include "main.hpp"
14 #include "draw.hpp"
15 #include "files.hpp"
16 #include "game.hpp"
17 #include "stat.hpp"
18 #include "interface/interface.hpp"
19 #include "messages.hpp"
20 #include "book.hpp"
21 #include "sound.hpp"
22 #include "shops.hpp"
23 #include "scores.hpp"
24 #include "magic/magic.hpp"
25 #include "monster.hpp"
26 #include "net.hpp"
27 #ifdef STEAMWORKS
28 #include <steam/steam_api.h>
29 #include "steam.hpp"
30 #endif
31 #include "menu.hpp"
32 #include "paths.hpp"
33 #include "player.hpp"
34 #include "cppfuncs.hpp"
35 #include "Directory.hpp"
36 #include "mod_tools.hpp"
37 
38 /*-------------------------------------------------------------------------------
39 
40 	initGame
41 
42 	initializes certain game specific resources
43 
44 -------------------------------------------------------------------------------*/
45 
46 #define _LOADSTR1 language[746]
47 #define _LOADSTR2 language[747]
48 #define _LOADSTR3 language[748]
49 #define _LOADSTR4 language[749]
50 
initGame()51 int initGame()
52 {
53 	int c, x;
54 	char name[32];
55 	FILE* fp;
56 
57 	// setup some lists
58 	booksRead.first = NULL;
59 	booksRead.last = NULL;
60 	lobbyChatboxMessages.first = NULL;
61 	lobbyChatboxMessages.last = NULL;
62 
63 	// steam stuff
64 #ifdef STEAMWORKS
65 	cpp_SteamServerWrapper_Instantiate(); //TODO: Remove these wrappers.
66 	cpp_SteamServerClientWrapper_Instantiate();
67 
68 	cpp_SteamServerClientWrapper_OnP2PSessionRequest = &steam_OnP2PSessionRequest;
69 	//cpp_SteamServerClientWrapper_OnGameOverlayActivated = &steam_OnGameOverlayActivated;
70 	cpp_SteamServerClientWrapper_OnLobbyCreated = &steam_OnLobbyCreated;
71 	cpp_SteamServerClientWrapper_OnGameJoinRequested = &steam_OnGameJoinRequested;
72 	cpp_SteamServerClientWrapper_OnLobbyEntered = &steam_OnLobbyEntered;
73 	cpp_SteamServerClientWrapper_GameServerPingOnServerResponded = &steam_GameServerPingOnServerResponded;
74 	cpp_SteamServerClientWrapper_OnLobbyMatchListCallback = &steam_OnLobbyMatchListCallback;
75 	cpp_SteamServerClientWrapper_OnP2PSessionConnectFail = &steam_OnP2PSessionConnectFail;
76 	cpp_SteamServerClientWrapper_OnLobbyDataUpdate = &steam_OnLobbyDataUpdatedCallback;
77  #ifdef USE_EOS
78 	cpp_SteamServerClientWrapper_OnRequestEncryptedAppTicket = &steam_OnRequestEncryptedAppTicket;
79  #endif //USE_EOS
80 #endif
81 
82 	// print a loading message
83 	drawClearBuffers();
84 	int w, h;
85 	TTF_SizeUTF8(ttf16, _LOADSTR1, &w, &h);
86 	ttfPrintText(ttf16, (xres - w) / 2, (yres - h) / 2, _LOADSTR1);
87 
88 	GO_SwapBuffers(screen);
89 
90 	initGameControllers();
91 
92 	// load achievement images
93 	Directory achievementsDir("images/achievements");
94 	for (auto& item : achievementsDir.list)
95 	{
96 		std::string fullPath = achievementsDir.path + std::string("/") + item;
97 		char* name = const_cast<char*>(fullPath.c_str()); // <- evil
98 		achievementImages.emplace(std::make_pair(item, loadImage(name)));
99 	}
100 
101 	// load model offsets
102 	printlog( "loading model offsets...\n");
103 	for ( c = 1; c < NUMMONSTERS; c++ )
104 	{
105 		// initialize all offsets to zero
106 		for ( x = 0; x < 20; x++ )
107 		{
108 			limbs[c][x][0] = 0;
109 			limbs[c][x][1] = 0;
110 			limbs[c][x][2] = 0;
111 		}
112 
113 		// open file
114 		char filename[256];
115 		strcpy(filename, "models/creatures/");
116 		strcat(filename, monstertypename[c]);
117 		strcat(filename, "/limbs.txt");
118 		if ( (fp = openDataFile(filename, "r")) == NULL )
119 		{
120 			continue;
121 		}
122 
123 		// read file
124 		int line;
125 		for ( line = 1; feof(fp) == 0; line++ )
126 		{
127 			char data[256];
128 			int limb = 20;
129 			int dummy;
130 
131 			// read line from file
132 			fgets( data, 256, fp );
133 
134 			// skip blank and comment lines
135 			if ( data[0] == '\n' || data[0] == '\r' || data[0] == '#' )
136 			{
137 				continue;
138 			}
139 
140 			// process line
141 			if ( sscanf( data, "%d", &limb ) != 1 || limb >= 20 || limb < 0 )
142 			{
143 				printlog( "warning: syntax error in '%s':%d\n invalid limb index!\n", filename, line);
144 				continue;
145 			}
146 			if ( sscanf( data, "%d %f %f %f\n", &dummy, &limbs[c][limb][0], &limbs[c][limb][1], &limbs[c][limb][2] ) != 4 )
147 			{
148 				printlog( "warning: syntax error in '%s':%d\n invalid limb offsets!\n", filename, line);
149 				continue;
150 			}
151 		}
152 
153 		// close file
154 		fclose(fp);
155 	}
156 
157 	// print a loading message
158 	drawClearBuffers();
159 	TTF_SizeUTF8(ttf16, _LOADSTR2, &w, &h);
160 	ttfPrintText(ttf16, (xres - w) / 2, (yres - h) / 2, _LOADSTR2);
161 
162 	GO_SwapBuffers(screen);
163 
164 	int newItems = 0;
165 
166 	// load item types
167 	printlog( "loading items...\n");
168 	std::string itemsDirectory = PHYSFS_getRealDir("items/items.txt");
169 	itemsDirectory.append(PHYSFS_getDirSeparator()).append("items/items.txt");
170 	fp = openDataFile(itemsDirectory.c_str(), "r");
171 	for ( c = 0; !feof(fp); ++c )
172 	{
173 		if ( c > SPELLBOOK_DETECT_FOOD )
174 		{
175 			newItems = c - SPELLBOOK_DETECT_FOOD - 1;
176 			items[c].name_identified = language[3500 + newItems * 2];
177 			items[c].name_unidentified = language[3501 + newItems * 2];
178 		}
179 		else if ( c > ARTIFACT_BOW )
180 		{
181 			newItems = c - ARTIFACT_BOW - 1;
182 			items[c].name_identified = language[2200 + newItems * 2];
183 			items[c].name_unidentified = language[2201 + newItems * 2];
184 		}
185 		else
186 		{
187 			items[c].name_identified = language[1545 + c * 2];
188 			items[c].name_unidentified = language[1546 + c * 2];
189 		}
190 		fscanf(fp, "%d", &items[c].index);
191 		while ( fgetc(fp) != '\n' ) if ( feof(fp) )
192 			{
193 				break;
194 			}
195 		fscanf(fp, "%d", &items[c].fpindex);
196 		while ( fgetc(fp) != '\n' ) if ( feof(fp) )
197 			{
198 				break;
199 			}
200 		fscanf(fp, "%d", &items[c].variations);
201 		while ( fgetc(fp) != '\n' ) if ( feof(fp) )
202 			{
203 				break;
204 			}
205 		fscanf(fp, "%s", name);
206 		while ( fgetc(fp) != '\n' ) if ( feof(fp) )
207 			{
208 				break;
209 			}
210 		if ( !strcmp(name, "WEAPON") )
211 		{
212 			items[c].category = WEAPON;
213 		}
214 		else if ( !strcmp(name, "ARMOR") )
215 		{
216 			items[c].category = ARMOR;
217 		}
218 		else if ( !strcmp(name, "AMULET") )
219 		{
220 			items[c].category = AMULET;
221 		}
222 		else if ( !strcmp(name, "POTION") )
223 		{
224 			items[c].category = POTION;
225 		}
226 		else if ( !strcmp(name, "SCROLL") )
227 		{
228 			items[c].category = SCROLL;
229 		}
230 		else if ( !strcmp(name, "MAGICSTAFF") )
231 		{
232 			items[c].category = MAGICSTAFF;
233 		}
234 		else if ( !strcmp(name, "RING") )
235 		{
236 			items[c].category = RING;
237 		}
238 		else if ( !strcmp(name, "SPELLBOOK") )
239 		{
240 			items[c].category = SPELLBOOK;
241 		}
242 		else if ( !strcmp(name, "TOOL") )
243 		{
244 			items[c].category = TOOL;
245 		}
246 		else if ( !strcmp(name, "FOOD") )
247 		{
248 			items[c].category = FOOD;
249 		}
250 		else if ( !strcmp(name, "BOOK") )
251 		{
252 			items[c].category = BOOK;
253 		}
254 		else if ( !strcmp(name, "THROWN") )
255 		{
256 			items[c].category = THROWN;
257 		}
258 		else if ( !strcmp(name, "SPELL_CAT") )
259 		{
260 			items[c].category = SPELL_CAT;
261 		}
262 		else
263 		{
264 			items[c].category = GEM;
265 		}
266 		fscanf(fp, "%d", &items[c].weight);
267 		while ( fgetc(fp) != '\n' ) if ( feof(fp) )
268 			{
269 				break;
270 			}
271 		fscanf(fp, "%d", &items[c].value);
272 		while ( fgetc(fp) != '\n' ) if ( feof(fp) )
273 			{
274 				break;
275 			}
276 		items[c].images.first = NULL;
277 		items[c].images.last = NULL;
278 		while ( 1 )
279 		{
280 			string_t* string = (string_t*) malloc(sizeof(string_t));
281 			string->data = (char*) malloc(sizeof(char) * 64);
282 			string->lines = 1;
283 
284 			node_t* node = list_AddNodeLast(&items[c].images);
285 			node->element = string;
286 			node->deconstructor = &stringDeconstructor;
287 			node->size = sizeof(string_t);
288 			string->node = node;
289 
290 			x = 0;
291 			bool fileend = false;
292 			while ( (string->data[x] = fgetc(fp)) != '\n' )
293 			{
294 				if ( feof(fp) )
295 				{
296 					fileend = true;
297 					break;
298 				}
299 				x++;
300 			}
301 			if ( x == 0 || fileend )
302 			{
303 				list_RemoveNode(node);
304 				break;
305 			}
306 			string->data[x] = 0;
307 		}
308 	}
309 	for ( c = 0; c < NUMITEMS; c++ )
310 	{
311 		items[c].surfaces.first = NULL;
312 		items[c].surfaces.last = NULL;
313 		for ( x = 0; x < list_Size(&items[c].images); x++ )
314 		{
315 			SDL_Surface** surface = (SDL_Surface**) malloc(sizeof(SDL_Surface*));
316 			node_t* node = list_AddNodeLast(&items[c].surfaces);
317 			node->element = surface;
318 			node->deconstructor = &defaultDeconstructor;
319 			node->size = sizeof(SDL_Surface*);
320 
321 			node_t* node2 = list_Node(&items[c].images, x);
322 			string_t* string = (string_t*)node2->element;
323 			std::string itemImgDir;
324 			if ( PHYSFS_getRealDir(string->data) != NULL )
325 			{
326 				itemImgDir = PHYSFS_getRealDir(string->data);
327 				itemImgDir.append(PHYSFS_getDirSeparator()).append(string->data);
328 			}
329 			else
330 			{
331 				itemImgDir = string->data;
332 			}
333 			char imgFileChar[256];
334 			strncpy(imgFileChar, itemImgDir.c_str(), 255);
335 			*surface = loadImage(imgFileChar);
336 		}
337 	}
338 	fclose(fp);
339 	createBooks();
340 	setupSpells();
341 
342 	randomPlayerNamesMale = getLinesFromDataFile(PLAYERNAMES_MALE_FILE);
343 	randomPlayerNamesFemale = getLinesFromDataFile(PLAYERNAMES_FEMALE_FILE);
344 	loadItemLists();
345 
346 #if defined(USE_EOS) || defined(STEAMWORKS)
347 #else
348 	if ( PHYSFS_getRealDir("mythsandoutcasts.key") != NULL )
349 	{
350 		std::string serial = PHYSFS_getRealDir("mythsandoutcasts.key");
351 		serial.append(PHYSFS_getDirSeparator()).append("mythsandoutcasts.key");
352 		// open the serial file
353 		FILE* fp = nullptr;
354 		if ( (fp = fopen(serial.c_str(), "rb")) != NULL )
355 		{
356 			char buf[64];
357 			size_t len = fread(&buf, sizeof(char), 32, fp);
358 			buf[len] = '\0';
359 			serial = buf;
360 			// compute hash
361 			size_t DLCHash = serialHash(serial);
362 			if ( DLCHash == 144425 )
363 			{
364 				printlog("[LICENSE]: Myths and Outcasts DLC license key found.");
365 				enabledDLCPack1 = true;
366 			}
367 			else
368 			{
369 				printlog("[LICENSE]: DLC license key invalid.");
370 			}
371 			fclose(fp);
372 		}
373 	}
374 	if ( PHYSFS_getRealDir("legendsandpariahs.key") != NULL )
375 	{
376 		std::string serial = PHYSFS_getRealDir("legendsandpariahs.key");
377 		serial.append(PHYSFS_getDirSeparator()).append("legendsandpariahs.key");
378 		// open the serial file
379 		FILE* fp = nullptr;
380 		if ( (fp = fopen(serial.c_str(), "rb")) != NULL )
381 		{
382 			char buf[64];
383 			size_t len = fread(&buf, sizeof(char), 32, fp);
384 			buf[len] = '\0';
385 			serial = buf;
386 			// compute hash
387 			size_t DLCHash = serialHash(serial);
388 			if ( DLCHash == 135398 )
389 			{
390 				printlog("[LICENSE]: Legends and Pariahs DLC license key found.");
391 				enabledDLCPack2 = true;
392 			}
393 			else
394 			{
395 				printlog("[LICENSE]: DLC license key invalid.");
396 			}
397 			fclose(fp);
398 		}
399 	}
400 #endif
401 
402 	// print a loading message
403 	drawClearBuffers();
404 	TTF_SizeUTF8(ttf16, _LOADSTR3, &w, &h);
405 	ttfPrintText(ttf16, (xres - w) / 2, (yres - h) / 2, _LOADSTR3);
406 
407 	GO_SwapBuffers(screen);
408 
409 #ifdef USE_FMOD
410 	FMOD_ChannelGroup_SetVolume(music_group, musvolume / 128.f);
411 #elif defined USE_OPENAL
412 	OPENAL_ChannelGroup_SetVolume(music_group, musvolume / 128.f);
413 #endif
414 	removedEntities.first = NULL;
415 	removedEntities.last = NULL;
416 	safePacketsSent.first = NULL;
417 	safePacketsSent.last = NULL;
418 	for ( c = 0; c < MAXPLAYERS; c++ )
419 	{
420 		safePacketsReceivedMap[c].clear();
421 	}
422 	topscores.first = NULL;
423 	topscores.last = NULL;
424 	topscoresMultiplayer.first = NULL;
425 	topscoresMultiplayer.last = NULL;
426 	messages.first = NULL;
427 	messages.last = NULL;
428 	chestInv.first = NULL;
429 	chestInv.last = NULL;
430 	command_history.first = NULL;
431 	command_history.last = NULL;
432 	for ( c = 0; c < MAXPLAYERS; c++ )
433 	{
434 		invitemschest[c] = NULL;
435 		openedChest[c] = NULL;
436 	}
437 	mousex = xres / 2;
438 	mousey = yres / 2;
439 
440 	players = new Player*[MAXPLAYERS];
441 	// default player stats
442 	for (c = 0; c < MAXPLAYERS; c++)
443 	{
444 		players[c] = new Player();
445 		// Stat set to 0 as monster type not needed, values will be filled with default, then overwritten by savegame or the charclass.cpp file
446 		stats[c] = new Stat(0);
447 		if (c > 0)
448 		{
449 			client_disconnected[c] = true;
450 		}
451 		players[c]->entity = nullptr;
452 		stats[c]->sex = static_cast<sex_t>(0);
453 		stats[c]->appearance = 0;
454 		strcpy(stats[c]->name, "");
455 		stats[c]->type = HUMAN;
456 		stats[c]->playerRace = RACE_HUMAN;
457 		stats[c]->FOLLOWERS.first = nullptr;
458 		stats[c]->FOLLOWERS.last = nullptr;
459 		stats[c]->inventory.first = nullptr;
460 		stats[c]->inventory.last = nullptr;
461 		stats[c]->clearStats();
462 		entitiesToDelete[c].first = nullptr;
463 		entitiesToDelete[c].last = nullptr;
464 		if (c == 0)
465 		{
466 			initClass(c);
467 		}
468 	}
469 
470 	// load music
471 #ifdef SOUND
472 #ifdef USE_OPENAL
473 #define FMOD_ChannelGroup_SetVolume OPENAL_ChannelGroup_SetVolume
474 #define fmod_system 0
475 #define FMOD_SOFTWARE 0
476 #define FMOD_System_CreateStream(A, B, C, D, E) OPENAL_CreateStreamSound(B, E)
477 #define FMOD_SOUND OPENAL_BUFFER
478 int fmod_result;
479 #endif
480 
481 	FMOD_ChannelGroup_SetVolume(music_group, musvolume / 128.f);
482 	fmod_result = FMOD_System_CreateStream(fmod_system, "music/introduction.ogg", FMOD_SOFTWARE, NULL, &introductionmusic);
483 	fmod_result = FMOD_System_CreateStream(fmod_system, "music/intermission.ogg", FMOD_SOFTWARE, NULL, &intermissionmusic);
484 	fmod_result = FMOD_System_CreateStream(fmod_system, "music/minetown.ogg", FMOD_SOFTWARE, NULL, &minetownmusic);
485 	fmod_result = FMOD_System_CreateStream(fmod_system, "music/splash.ogg", FMOD_SOFTWARE, NULL, &splashmusic);
486 	fmod_result = FMOD_System_CreateStream(fmod_system, "music/library.ogg", FMOD_SOFTWARE, NULL, &librarymusic);
487 	fmod_result = FMOD_System_CreateStream(fmod_system, "music/shop.ogg", FMOD_SOFTWARE, NULL, &shopmusic);
488 	fmod_result = FMOD_System_CreateStream(fmod_system, "music/herxboss.ogg", FMOD_SOFTWARE, NULL, &herxmusic);
489 	fmod_result = FMOD_System_CreateStream(fmod_system, "music/temple.ogg", FMOD_SOFTWARE, NULL, &templemusic);
490 	fmod_result = FMOD_System_CreateStream(fmod_system, "music/endgame.ogg", FMOD_SOFTWARE, NULL, &endgamemusic);
491 	fmod_result = FMOD_System_CreateStream(fmod_system, "music/escape.ogg", FMOD_SOFTWARE, NULL, &escapemusic);
492 	fmod_result = FMOD_System_CreateStream(fmod_system, "music/devil.ogg", FMOD_SOFTWARE, NULL, &devilmusic);
493 	fmod_result = FMOD_System_CreateStream(fmod_system, "music/sanctum.ogg", FMOD_SOFTWARE, NULL, &sanctummusic);
494 	fmod_result = FMOD_System_CreateStream(fmod_system, "music/tutorial.ogg", FMOD_SOFTWARE, NULL, &tutorialmusic);
495 	if ( PHYSFS_getRealDir("music/gnomishmines.ogg") != NULL )
496 	{
497 		fmod_result = FMOD_System_CreateStream(fmod_system, "music/gnomishmines.ogg", FMOD_SOFTWARE, NULL, &gnomishminesmusic);
498 	}
499 	if ( PHYSFS_getRealDir("music/greatcastle.ogg") != NULL )
500 	{
501 		fmod_result = FMOD_System_CreateStream(fmod_system, "music/greatcastle.ogg", FMOD_SOFTWARE, NULL, &greatcastlemusic);
502 	}
503 	if ( PHYSFS_getRealDir("music/sokoban.ogg") != NULL )
504 	{
505 		fmod_result = FMOD_System_CreateStream(fmod_system, "music/sokoban.ogg", FMOD_SOFTWARE, NULL, &sokobanmusic);
506 	}
507 	if ( PHYSFS_getRealDir("music/caveslair.ogg") != NULL )
508 	{
509 		fmod_result = FMOD_System_CreateStream(fmod_system, "music/caveslair.ogg", FMOD_SOFTWARE, NULL, &caveslairmusic);
510 	}
511 	if ( PHYSFS_getRealDir("music/bramscastle.ogg") != NULL )
512 	{
513 		fmod_result = FMOD_System_CreateStream(fmod_system, "music/bramscastle.ogg", FMOD_SOFTWARE, NULL, &bramscastlemusic);
514 	}
515 	if ( PHYSFS_getRealDir("music/hamlet.ogg") != NULL )
516 	{
517 		fmod_result = FMOD_System_CreateStream(fmod_system, "music/hamlet.ogg", FMOD_SOFTWARE, NULL, &hamletmusic);
518 	}
519 	//fmod_result = FMOD_System_CreateStream(fmod_system, "music/story.ogg", FMOD_SOFTWARE, NULL, &storymusic);
520 
521 	if ( NUMMINESMUSIC > 0 )
522 	{
523 		minesmusic = (FMOD_SOUND**) malloc(sizeof(FMOD_SOUND*)*NUMMINESMUSIC);
524 		for ( c = 0; c < NUMMINESMUSIC; c++ )
525 		{
526 			snprintf(tempstr, 1000, "music/mines%02d.ogg", c);
527 			fmod_result = FMOD_System_CreateStream(fmod_system, tempstr, FMOD_SOFTWARE, NULL, &minesmusic[c]);
528 		}
529 	}
530 	if ( NUMSWAMPMUSIC > 0 )
531 	{
532 		swampmusic = (FMOD_SOUND**) malloc(sizeof(FMOD_SOUND*)*NUMSWAMPMUSIC);
533 		for ( c = 0; c < NUMSWAMPMUSIC; c++ )
534 		{
535 			snprintf(tempstr, 1000, "music/swamp%02d.ogg", c);
536 			fmod_result = FMOD_System_CreateStream(fmod_system, tempstr, FMOD_SOFTWARE, NULL, &swampmusic[c]);
537 		}
538 	}
539 	if ( NUMLABYRINTHMUSIC > 0 )
540 	{
541 		labyrinthmusic = (FMOD_SOUND**) malloc(sizeof(FMOD_SOUND*)*NUMLABYRINTHMUSIC);
542 		for ( c = 0; c < NUMLABYRINTHMUSIC; c++ )
543 		{
544 			snprintf(tempstr, 1000, "music/labyrinth%02d.ogg", c);
545 			fmod_result = FMOD_System_CreateStream(fmod_system, tempstr, FMOD_SOFTWARE, NULL, &labyrinthmusic[c]);
546 		}
547 	}
548 	if ( NUMRUINSMUSIC > 0 )
549 	{
550 		ruinsmusic = (FMOD_SOUND**) malloc(sizeof(FMOD_SOUND*)*NUMRUINSMUSIC);
551 		for ( c = 0; c < NUMRUINSMUSIC; c++ )
552 		{
553 			snprintf(tempstr, 1000, "music/ruins%02d.ogg", c);
554 			fmod_result = FMOD_System_CreateStream(fmod_system, tempstr, FMOD_SOFTWARE, NULL, &ruinsmusic[c]);
555 		}
556 	}
557 	if ( NUMUNDERWORLDMUSIC > 0 )
558 	{
559 		underworldmusic = (FMOD_SOUND**) malloc(sizeof(FMOD_SOUND*)*NUMUNDERWORLDMUSIC);
560 		for ( c = 0; c < NUMUNDERWORLDMUSIC; c++ )
561 		{
562 			snprintf(tempstr, 1000, "music/underworld%02d.ogg", c);
563 			fmod_result = FMOD_System_CreateStream(fmod_system, tempstr, FMOD_SOFTWARE, NULL, &underworldmusic[c]);
564 		}
565 	}
566 	if ( NUMHELLMUSIC > 0 )
567 	{
568 		hellmusic = (FMOD_SOUND**) malloc(sizeof(FMOD_SOUND*)*NUMHELLMUSIC);
569 		for ( c = 0; c < NUMHELLMUSIC; c++ )
570 		{
571 			snprintf(tempstr, 1000, "music/hell%02d.ogg", c);
572 			fmod_result = FMOD_System_CreateStream(fmod_system, tempstr, FMOD_SOFTWARE, NULL, &hellmusic[c]);
573 		}
574 	}
575 	if ( NUMMINOTAURMUSIC > 0 )
576 	{
577 		minotaurmusic = (FMOD_SOUND**) malloc(sizeof(FMOD_SOUND*)*NUMMINOTAURMUSIC);
578 		for ( c = 0; c < NUMMINOTAURMUSIC; c++ )
579 		{
580 			snprintf(tempstr, 1000, "music/minotaur%02d.ogg", c);
581 			fmod_result = FMOD_System_CreateStream(fmod_system, tempstr, FMOD_SOFTWARE, NULL, &minotaurmusic[c]);
582 		}
583 	}
584 	if ( NUMCAVESMUSIC > 0 )
585 	{
586 		cavesmusic = (FMOD_SOUND**) malloc(sizeof(FMOD_SOUND*)*NUMCAVESMUSIC);
587 		for ( c = 0; c < NUMCAVESMUSIC; c++ )
588 		{
589 			snprintf(tempstr, 1000, "music/caves%02d.ogg", c);
590 			fmod_result = FMOD_System_CreateStream(fmod_system, tempstr, FMOD_SOFTWARE, NULL, &cavesmusic[c]);
591 		}
592 	}
593 	if ( NUMCITADELMUSIC > 0 )
594 	{
595 		citadelmusic = (FMOD_SOUND**)malloc(sizeof(FMOD_SOUND*)*NUMCITADELMUSIC);
596 		for ( c = 0; c < NUMCITADELMUSIC; c++ )
597 		{
598 			snprintf(tempstr, 1000, "music/citadel%02d.ogg", c);
599 			fmod_result = FMOD_System_CreateStream(fmod_system, tempstr, FMOD_SOFTWARE, NULL, &citadelmusic[c]);
600 		}
601 	}
602 	if ( NUMINTROMUSIC > 0 )
603 	{
604 		intromusic = (FMOD_SOUND**)malloc(sizeof(FMOD_SOUND*)*NUMINTROMUSIC);
605 		for ( c = 0; c < NUMINTROMUSIC; c++ )
606 		{
607 			if ( c == 0 )
608 			{
609 				strcpy(tempstr, "music/intro.ogg");
610 			}
611 			else
612 			{
613 				snprintf(tempstr, 1000, "music/intro%02d.ogg", c);
614 			}
615 			fmod_result = FMOD_System_CreateStream(fmod_system, tempstr, FMOD_SOFTWARE, NULL, &intromusic[c]);
616 		}
617 	}
618 #ifdef USE_OPENAL
619 #undef FMOD_ChannelGroup_SetVolume
620 #undef fmod_system
621 #undef FMOD_SOFTWARE
622 #undef FMOD_System_CreateStream
623 #undef FMOD_SOUND
624 #endif
625 
626 #endif
627 
628 	// print a loading message
629 	drawClearBuffers();
630 	TTF_SizeUTF8(ttf16, _LOADSTR4, &w, &h);
631 	ttfPrintText(ttf16, (xres - w) / 2, (yres - h) / 2, _LOADSTR4);
632 
633 	GO_SwapBuffers(screen);
634 
635 	// load extraneous game resources
636 	title_bmp = loadImage("images/system/title.png");
637 	logo_bmp = loadImage("images/system/logo.png");
638 	cursor_bmp = loadImage("images/system/cursor.png");
639 	cross_bmp = loadImage("images/system/cross.png");
640 
641 	loadAllScores(SCORESFILE);
642 	loadAllScores(SCORESFILE_MULTIPLAYER);
643 	gameModeManager.Tutorial.init();
644 	if (!loadInterfaceResources())
645 	{
646 		printlog("Failed to load interface resources.\n");
647 		return -1;
648 	}
649 
650 	return 0;
651 }
652 
653 /*-------------------------------------------------------------------------------
654 
655 	deinitGame
656 
657 	deinitializes certain game specific resources
658 
659 -------------------------------------------------------------------------------*/
660 
deinitGame()661 void deinitGame()
662 {
663 	int c, x;
664 
665 	// send disconnect messages
666 	if ( multiplayer == CLIENT )
667 	{
668 		strcpy((char*)net_packet->data, "DISCONNECT");
669 		net_packet->data[10] = clientnum;
670 		net_packet->address.host = net_server.host;
671 		net_packet->address.port = net_server.port;
672 		net_packet->len = 11;
673 		sendPacketSafe(net_sock, -1, net_packet, 0);
674 		printlog("disconnected from server.\n");
675 	}
676 	else if ( multiplayer == SERVER )
677 	{
678 		for ( x = 1; x < MAXPLAYERS; x++ )
679 		{
680 			if ( client_disconnected[x] == true )
681 			{
682 				continue;
683 			}
684 			strcpy((char*)net_packet->data, "DISCONNECT");
685 			net_packet->data[10] = clientnum;
686 			net_packet->address.host = net_clients[x - 1].host;
687 			net_packet->address.port = net_clients[x - 1].port;
688 			net_packet->len = 11;
689 			sendPacketSafe(net_sock, -1, net_packet, x - 1);
690 
691 			stats[x]->freePlayerEquipment();
692 			client_disconnected[x] = true;
693 		}
694 	}
695 
696 	// this short delay makes sure that the disconnect message gets out
697 	Uint32 timetoshutdown = SDL_GetTicks();
698 	while ( SDL_GetTicks() - timetoshutdown < 500 )
699 	{
700 		// handle network messages
701 		if ( multiplayer == CLIENT )
702 		{
703 			clientHandleMessages(fpsLimit);
704 		}
705 		else if ( multiplayer == SERVER )
706 		{
707 			serverHandleMessages(fpsLimit);
708 		}
709 		if ( !(SDL_GetTicks() % 25) && multiplayer )
710 		{
711 			int j = 0;
712 			node_t* node, *nextnode;
713 			for ( node = safePacketsSent.first; node != NULL; node = nextnode )
714 			{
715 				nextnode = node->next;
716 
717 				packetsend_t* packet = (packetsend_t*)node->element;
718 				sendPacket(packet->sock, packet->channel, packet->packet, packet->hostnum);
719 				packet->tries++;
720 				if ( packet->tries >= MAXTRIES )
721 				{
722 					list_RemoveNode(node);
723 				}
724 				j++;
725 				if ( j >= MAXDELETES )
726 				{
727 					break;
728 				}
729 			}
730 		}
731 	}
732 
733 	saveAllScores(SCORESFILE);
734 	saveAllScores(SCORESFILE_MULTIPLAYER);
735 	list_FreeAll(&topscores);
736 	list_FreeAll(&topscoresMultiplayer);
737 	deleteAllNotificationMessages();
738 	list_FreeAll(&removedEntities);
739 	if ( title_bmp != nullptr )
740 	{
741 		SDL_FreeSurface(title_bmp);
742 	}
743 	if ( logo_bmp != nullptr )
744 	{
745 		SDL_FreeSurface(logo_bmp);
746 	}
747 	if ( cursor_bmp != nullptr )
748 	{
749 		SDL_FreeSurface(cursor_bmp);
750 	}
751 	if ( cross_bmp != nullptr )
752 	{
753 		SDL_FreeSurface(cross_bmp);
754 	}
755 	//if(sky_bmp!=NULL)
756 	//	SDL_FreeSurface(sky_bmp);
757 	list_FreeAll(&chestInv);
758 	freeInterfaceResources();
759 	if ( books )
760 	{
761 		for ( c = 0; c < numbooks; c++ )
762 		{
763 			if ( books[c] )
764 			{
765 				if ( books[c]->name )
766 				{
767 					free(books[c]->name);
768 				}
769 				if ( books[c]->text )
770 				{
771 					free(books[c]->text);
772 				}
773 				if ( books[c]->bookgui_render_title )
774 				{
775 					free(books[c]->bookgui_render_title);
776 				}
777 				list_FreeAll(&books[c]->pages);
778 				free(books[c]);
779 			}
780 		}
781 		free(books);
782 	}
783 	appraisal_timer = 0;
784 	appraisal_item = 0;
785 	for ( c = 0; c < MAXPLAYERS; c++ )
786 	{
787 		list_FreeAll(&stats[c]->inventory);
788 	}
789 	if ( multiplayer == CLIENT )
790 	{
791 		if ( shopInv )
792 		{
793 			list_FreeAll(shopInv);
794 			free(shopInv);
795 			shopInv = NULL;
796 		}
797 	}
798 	list_FreeAll(map.entities);
799 	if ( map.creatures )
800 	{
801 		list_FreeAll(map.creatures); //TODO: Need to do this?
802 	}
803 	list_FreeAll(&messages);
804 	if ( multiplayer == SINGLE )
805 	{
806 		list_FreeAll(&channeledSpells[0]);
807 	}
808 	else if ( multiplayer == CLIENT )
809 	{
810 		list_FreeAll(&channeledSpells[clientnum]);
811 	}
812 	else if ( multiplayer == SERVER )
813 	{
814 		for ( c = 0; c < MAXPLAYERS; ++c )
815 		{
816 			list_FreeAll(&channeledSpells[c]);
817 		}
818 	}
819 	list_FreeAll(&spellList);
820 	list_FreeAll(&command_history);
821 
822 	list_FreeAll(&safePacketsSent);
823 	for ( c = 0; c < MAXPLAYERS; c++ )
824 	{
825 		safePacketsReceivedMap[c].clear();
826 	}
827 #ifdef SOUND
828 #ifdef USE_OPENAL
829 #define FMOD_Channel_Stop OPENAL_Channel_Stop
830 #define FMOD_Sound_Release OPENAL_Sound_Release
831 #endif
832 	if ( !no_sound )
833 	{
834 		FMOD_Channel_Stop(music_channel);
835 		FMOD_Channel_Stop(music_channel2);
836 		FMOD_Sound_Release(introductionmusic);
837 		FMOD_Sound_Release(intermissionmusic);
838 		FMOD_Sound_Release(minetownmusic);
839 		FMOD_Sound_Release(splashmusic);
840 		FMOD_Sound_Release(librarymusic);
841 		FMOD_Sound_Release(shopmusic);
842 		FMOD_Sound_Release(herxmusic);
843 		FMOD_Sound_Release(templemusic);
844 		FMOD_Sound_Release(endgamemusic);
845 		FMOD_Sound_Release(escapemusic);
846 		FMOD_Sound_Release(devilmusic);
847 		FMOD_Sound_Release(sanctummusic);
848 		FMOD_Sound_Release(gnomishminesmusic);
849 		FMOD_Sound_Release(greatcastlemusic);
850 		FMOD_Sound_Release(sokobanmusic);
851 		FMOD_Sound_Release(caveslairmusic);
852 		FMOD_Sound_Release(bramscastlemusic);
853 		FMOD_Sound_Release(hamletmusic);
854 		FMOD_Sound_Release(tutorialmusic);
855 		for ( c = 0; c < NUMMINESMUSIC; c++ )
856 		{
857 			FMOD_Sound_Release(minesmusic[c]);
858 		}
859 		if ( minesmusic )
860 		{
861 			free(minesmusic);
862 		}
863 		for ( c = 0; c < NUMSWAMPMUSIC; c++ )
864 		{
865 			FMOD_Sound_Release(swampmusic[c]);
866 		}
867 		if ( swampmusic )
868 		{
869 			free(swampmusic);
870 		}
871 		for ( c = 0; c < NUMLABYRINTHMUSIC; c++ )
872 		{
873 			FMOD_Sound_Release(labyrinthmusic[c]);
874 		}
875 		if ( labyrinthmusic )
876 		{
877 			free(labyrinthmusic);
878 		}
879 		for ( c = 0; c < NUMRUINSMUSIC; c++ )
880 		{
881 			FMOD_Sound_Release(ruinsmusic[c]);
882 		}
883 		if ( ruinsmusic )
884 		{
885 			free(ruinsmusic);
886 		}
887 		for ( c = 0; c < NUMUNDERWORLDMUSIC; c++ )
888 		{
889 			FMOD_Sound_Release(underworldmusic[c]);
890 		}
891 		if ( underworldmusic )
892 		{
893 			free(underworldmusic);
894 		}
895 		for ( c = 0; c < NUMHELLMUSIC; c++ )
896 		{
897 			FMOD_Sound_Release(hellmusic[c]);
898 		}
899 		if ( hellmusic )
900 		{
901 			free(hellmusic);
902 		}
903 		for ( c = 0; c < NUMMINOTAURMUSIC; c++ )
904 		{
905 			FMOD_Sound_Release(minotaurmusic[c]);
906 		}
907 		if ( minotaurmusic )
908 		{
909 			free(minotaurmusic);
910 		}
911 		for ( c = 0; c < NUMCAVESMUSIC; c++ )
912 		{
913 			FMOD_Sound_Release(cavesmusic[c]);
914 		}
915 		if ( cavesmusic )
916 		{
917 			free(cavesmusic);
918 		}
919 		for ( c = 0; c < NUMCITADELMUSIC; c++ )
920 		{
921 			FMOD_Sound_Release(citadelmusic[c]);
922 		}
923 		if ( citadelmusic )
924 		{
925 			free(citadelmusic);
926 		}
927 		for ( c = 0; c < NUMINTROMUSIC; c++ )
928 		{
929 			FMOD_Sound_Release(intromusic[c]);
930 		}
931 		if ( intromusic )
932 		{
933 			free(intromusic);
934 		}
935 	}
936 #ifdef USE_OPENAL
937 #undef FMOD_Channel_Stop
938 #undef FMOD_Sound_Release
939 #endif
940 #endif
941 
942 	// free items
943 	printlog( "freeing item data...\n");
944 	for ( c = 0; c < NUMITEMS; c++ )
945 	{
946 		list_FreeAll(&items[c].images);
947 		node_t* node, *nextnode;
948 		for ( node = items[c].surfaces.first; node != NULL; node = nextnode )
949 		{
950 			nextnode = node->next;
951 			SDL_Surface** surface = (SDL_Surface**)node->element;
952 			if ( surface )
953 				if ( *surface )
954 				{
955 					SDL_FreeSurface(*surface);
956 				}
957 		}
958 		list_FreeAll(&items[c].surfaces);
959 	}
960 
961 	freeSpells();
962 
963 	// pathmaps
964 	if ( pathMapGrounded )
965 	{
966 		free(pathMapGrounded);
967 	}
968 	pathMapGrounded = NULL;
969 	if ( pathMapFlying )
970 	{
971 		free(pathMapFlying);
972 	}
973 	pathMapFlying = NULL;
974 
975 	// clear steam achievement list
976 	list_FreeAll(&booksRead);
977 
978 	// clear lobby chatbox data
979 	list_FreeAll(&lobbyChatboxMessages);
980 
981 	// steam stuff
982 #ifdef STEAMWORKS
983 	cpp_SteamServerWrapper_Destroy();
984 	cpp_SteamServerClientWrapper_Destroy();
985 	if ( currentLobby )
986 	{
987 		SteamMatchmaking()->LeaveLobby(*static_cast<CSteamID*>(currentLobby));
988 		cpp_Free_CSteamID(currentLobby); //TODO: Remove these bodges.
989 		currentLobby = NULL;
990 	}
991 	if ( lobbyToConnectTo )
992 	{
993 		cpp_Free_CSteamID(lobbyToConnectTo);
994 		lobbyToConnectTo = NULL;
995 	}
996 	for ( c = 0; c < MAXPLAYERS; c++ )
997 	{
998 		if ( steamIDRemote[c] )
999 		{
1000 			cpp_Free_CSteamID(steamIDRemote[c]);
1001 			steamIDRemote[c] = NULL;
1002 		}
1003 	}
1004 	for ( c = 0; c < MAX_STEAM_LOBBIES; c++ )
1005 	{
1006 		if ( lobbyIDs[c] )
1007 		{
1008 			cpp_Free_CSteamID(lobbyIDs[c]);
1009 			lobbyIDs[c] = NULL;
1010 		}
1011 	}
1012 #endif
1013 #if defined USE_EOS
1014 	if ( EOS.CurrentLobbyData.currentLobbyIsValid() )
1015 	{
1016 		EOS.leaveLobby();
1017 
1018 		Uint32 shutdownTicks = SDL_GetTicks();
1019 		while ( EOS.CurrentLobbyData.bAwaitingLeaveCallback )
1020 		{
1021 #ifdef APPLE
1022 			SDL_Event event;
1023 			while ( SDL_PollEvent(&event) != 0 )
1024 			{
1025 				//Makes Mac work because Apple had to do it different.
1026 			}
1027 #endif
1028 			EOS_Platform_Tick(EOS.PlatformHandle);
1029 			SDL_Delay(50);
1030 			if ( SDL_GetTicks() - shutdownTicks >= 3000 )
1031 			{
1032 				break;
1033 			}
1034 		}
1035 	}
1036 	EOS.AccountManager.deinit();
1037 	EOS.shutdown();
1038 #endif
1039 
1040 	//Close game controller
1041 	/*if (game_controller)
1042 	{
1043 		SDL_GameControllerClose(game_controller);
1044 		game_controller = nullptr;
1045 	}*/
1046 	if (game_controller)
1047 	{
1048 		delete game_controller;
1049 	}
1050 
1051 	if ( shoparea )
1052 	{
1053 		free(shoparea);
1054 	}
1055 
1056 	for (int i = 0; i < MAXPLAYERS; ++i)
1057 	{
1058 		delete players[i];
1059 	}
1060 	delete[] players;
1061 }
1062