1 /*-------------------------------------------------------------------------------
2 
3 	BARONY
4 	File: menu.cpp
5 	Desc: contains code for all menu buttons in the game
6 
7 	Copyright 2013-2016 (c) Turning Wheel LLC, all rights reserved.
8 	See LICENSE for details.
9 
10 -------------------------------------------------------------------------------*/
11 
12 #include <list>
13 #include "main.hpp"
14 #include "draw.hpp"
15 #include "game.hpp"
16 #include "stat.hpp"
17 #include "messages.hpp"
18 #include "entity.hpp"
19 #include "files.hpp"
20 #include "menu.hpp"
21 #include "classdescriptions.hpp"
22 #include "interface/interface.hpp"
23 #include "magic/magic.hpp"
24 #include "sound.hpp"
25 #include "items.hpp"
26 #include "init.hpp"
27 #include "shops.hpp"
28 #include "monster.hpp"
29 #include "scores.hpp"
30 #include "menu.hpp"
31 #include "net.hpp"
32 #ifdef STEAMWORKS
33 #include <steam/steam_api.h>
34 #include "steam.hpp"
35 #endif
36 #include "prng.hpp"
37 #include "credits.hpp"
38 #include "paths.hpp"
39 #include "collision.hpp"
40 #include "player.hpp"
41 #include "cppfuncs.hpp"
42 #include "colors.hpp"
43 #include <ctime>
44 #include "sys/stat.h"
45 #include "eos.hpp"
46 #include "mod_tools.hpp"
47 #include "interface/ui.hpp"
48 #include "lobbies.hpp"
49 #include <sstream>
50 
51 #ifdef STEAMWORKS
52 //Helper func. //TODO: Bugger.
cpp_SteamMatchmaking_GetLobbyOwner(void * steamIDLobby)53 void* cpp_SteamMatchmaking_GetLobbyOwner(void* steamIDLobby)
54 {
55 	CSteamID* id = new CSteamID();
56 	*id = SteamMatchmaking()->GetLobbyOwner(*static_cast<CSteamID*>(steamIDLobby));
57 	return id; //Still don't like this method.
58 }
59 // get player names in a lobby
cpp_SteamMatchmaking_GetLobbyMember(void * steamIDLobby,int index)60 void* cpp_SteamMatchmaking_GetLobbyMember(void* steamIDLobby, int index)
61 {
62 	CSteamID* id = new CSteamID();
63 	*id = SteamMatchmaking()->GetLobbyMemberByIndex(*static_cast<CSteamID*>(currentLobby), index);
64 	return id;
65 }
66 uint64 SteamAPICall_NumPlayersOnline = 0;
67 NumberOfCurrentPlayers_t NumberOfCurrentPlayers;
68 int steamOnlinePlayers = 0;
69 #endif
70 
71 // menu variables
72 bool lobby_window = false;
73 bool settings_window = false;
74 int connect_window = 0;
75 int charcreation_step = 0;
76 int loadGameSaveShowRectangle = 0; // stores the current amount of savegames available, to use when drawing load game window boxes.
77 int singleplayerSavegameFreeSlot = -1; // used on multiplayer/single player select window to store if savefile exists.
78 int multiplayerSavegameFreeSlot = -1; // used on multiplayer/single player select window to store if savefile exists.
79 int raceSelect = 0;
80 Uint32 lobbyWindowSvFlags = 0;
81 /*
82  * settings_tab
83  * valid values:
84  *		- 0 = Video settings
85  *		- 1 = Audio settings
86  *		- 2 = Keyboard/binding settings
87  *		- 3 = Mouse settings
88  *		- 4 = Gamepad bindings
89  *		- 5 = Gamepad settings
90  *		- 6 = Misc settings
91  */
92 int settings_tab = 0;
93 button_t* button_video_tab = nullptr;
94 button_t* button_audio_tab = nullptr;
95 button_t* button_keyboard_tab = nullptr;
96 button_t* button_mouse_tab = nullptr;
97 button_t* button_gamepad_bindings_tab = nullptr;
98 button_t* button_gamepad_settings_tab = nullptr;
99 button_t* button_misc_tab = nullptr;
100 
101 int score_window = 0;
102 int score_window_to_delete = 0;
103 bool score_window_delete_multiplayer = false;
104 int score_leaderboard_window = 0;
105 
106 int savegames_window = 0;
107 int savegames_window_scroll = 0;
108 std::vector<std::tuple<int, int, int, std::string>> savegamesList; // tuple - last modified, multiplayer type, file entry, and description of save game.
109 
110 // gamemods window stuff.
111 int gamemods_window = 0;
112 std::list<std::string> currentDirectoryFiles;
113 std::list<std::string> directoryFilesListToUpload;
114 std::string directoryToUpload;
115 std::string directoryPath;
116 int gamemods_window_scroll = 0;
117 int gamemods_window_fileSelect = 0;
118 int gamemods_uploadStatus = 0;
119 char gamemods_uploadTitle[32] = "Title";
120 char gamemods_uploadDescription[32] = "Description";
121 int gamemods_currentEditField = 0;
122 bool gamemods_workshopSetPropertyReturn[3] = { false, false, false };
123 int gamemods_subscribedItemsStatus = 0;
124 char gamemods_newBlankDirectory[32] = "";
125 char gamemods_newBlankDirectoryOldName[32] = "";
126 int gamemods_newBlankDirectoryStatus = 0;
127 int gamemods_numCurrentModsLoaded = -1;
128 const int gamemods_maxTags = 10;
129 std::vector<std::pair<std::string, std::string>> gamemods_mountedFilepaths;
130 std::list<std::string> gamemods_localModFoldernames;
131 bool gamemods_modelsListRequiresReload = false;
132 bool gamemods_modelsListLastStartedUnmodded = false; // if starting regular game that had to reset model list, use this to reinit custom models.
133 bool gamemods_soundListRequiresReload = false;
134 bool gamemods_soundsListLastStartedUnmodded = false; // if starting regular game that had to reset sounds list, use this to reinit custom sounds.
135 bool gamemods_tileListRequireReloadUnmodded = false;
136 bool gamemods_spriteImagesRequireReloadUnmodded = false;
137 bool gamemods_booksRequireReloadUnmodded = false;
138 bool gamemods_musicRequireReloadUnmodded = false;
139 bool gamemods_langRequireReloadUnmodded = false;
140 bool gamemods_itemSpritesRequireReloadUnmodded = false;
141 bool gamemods_itemsTxtRequireReloadUnmodded = false;
142 bool gamemods_itemsGlobalTxtRequireReloadUnmodded = false;
143 bool gamemods_monsterLimbsRequireReloadUnmodded = false;
144 bool gamemods_systemImagesReloadUnmodded = false;
145 bool gamemods_customContentLoadedFirstTime = false;
146 bool gamemods_disableSteamAchievements = false;
147 bool gamemods_modPreload = false;
148 
149 sex_t lastSex = MALE;
150 PlayerRaces lastRace = RACE_HUMAN;
151 int lastAppearance = 0;
152 bool enabledDLCPack1 = false;
153 bool enabledDLCPack2 = false;
154 bool showRaceInfo = false;
155 #ifdef STEAMWORKS
156 std::vector<SteamUGCDetails_t *> workshopSubscribedItemList;
157 std::vector<std::pair<std::string, uint64>> gamemods_workshopLoadedFileIDMap;
158 #else
159 bool serialEnterWindow = false;
160 char serialInputText[64] = "";
161 int serialVerifyWindow = 0;
162 #endif // STEAMWORKS
163 
164 
165 bool scoreDisplayMultiplayer = false;
166 int settings_xres, settings_yres;
167 
168 typedef std::tuple<int, int> resolution;
169 std::list<resolution> resolutions;
170 Uint32 settings_fov;
171 Uint32 settings_fps;
172 bool settings_smoothlighting;
173 int settings_fullscreen, settings_shaking, settings_bobbing;
174 bool settings_borderless = false;
175 real_t settings_gamma;
176 int settings_sfxvolume, settings_musvolume;
177 int settings_sfxAmbientVolume;
178 int settings_sfxEnvironmentVolume;
179 int settings_impulses[NUMIMPULSES];
180 int settings_joyimpulses[NUM_JOY_IMPULSES];
181 int settings_reversemouse;
182 real_t settings_mousespeed;
183 bool settings_broadcast;
184 bool settings_nohud;
185 bool settings_colorblind;
186 bool settings_spawn_blood;
187 bool settings_light_flicker;
188 bool settings_vsync;
189 bool settings_status_effect_icons = true;
190 bool settings_minimap_ping_mute = false;
191 bool settings_mute_audio_on_focus_lost = false;
192 bool settings_mute_player_monster_sounds = false;
193 int settings_minimap_transparency_foreground = 0;
194 int settings_minimap_transparency_background = 0;
195 int settings_minimap_scale = 4;
196 int settings_minimap_object_zoom = 0;
197 char portnumber_char[6];
198 char connectaddress[64];
199 char classtoquickstart[256] = "";
200 bool spawn_blood = true;
201 int multiplayerselect = SINGLE;
202 int menuselect = 0;
203 bool settings_auto_hotbar_new_items = true;
204 bool settings_auto_hotbar_categories[NUM_HOTBAR_CATEGORIES] = { true, true, true, true,
205 																true, true, true, true,
206 																true, true, true, true };
207 int settings_autosort_inventory_categories[NUM_AUTOSORT_CATEGORIES] = {	0, 0, 0, 0,
208 																		0, 0, 0, 0,
209 																		0, 0, 0, 0 };
210 bool settings_hotbar_numkey_quick_add = false;
211 bool settings_disable_messages = true;
212 bool settings_right_click_protect = false;
213 bool settings_auto_appraise_new_items = true;
214 bool playing_random_char = false;
215 bool colorblind = false;
216 bool settings_lock_right_sidebar = false;
217 bool settings_show_game_timer_always = false;
218 bool settings_uiscale_charactersheet = false;
219 bool settings_uiscale_skillspage = false;
220 real_t settings_uiscale_hotbar = 1.f;
221 real_t settings_uiscale_playerbars = 1.f;
222 real_t settings_uiscale_chatlog = 1.f;
223 real_t settings_uiscale_inventory = 1.f;
224 bool settings_hide_statusbar = false;
225 bool settings_hide_playertags = false;
226 bool settings_show_skill_values = false;
227 bool settings_disableMultithreadedSteamNetworking = true;
228 bool settings_disableFPSLimitOnNetworkMessages = false;
229 Sint32 oslidery = 0;
230 
231 //Gamepad settings.
232 bool settings_gamepad_leftx_invert = false;
233 bool settings_gamepad_lefty_invert = false;
234 bool settings_gamepad_rightx_invert = false;
235 bool settings_gamepad_righty_invert = false;
236 bool settings_gamepad_menux_invert = false;
237 bool settings_gamepad_menuy_invert = false;
238 
239 int settings_gamepad_deadzone = 1;
240 int settings_gamepad_rightx_sensitivity = 1;
241 int settings_gamepad_righty_sensitivity = 1;
242 int settings_gamepad_menux_sensitivity = 1;
243 int settings_gamepad_menuy_sensitivity = 1;
244 
245 Uint32 colorWhite = 0xFFFFFFFF;
246 
247 int firstendmoviealpha[30];
248 int secondendmoviealpha[30];
249 int thirdendmoviealpha[30];
250 int fourthendmoviealpha[30];
251 int intromoviealpha[30];
252 int rebindkey = -1;
253 int rebindaction = -1;
254 
255 Sint32 gearrot = 0;
256 Sint32 gearsize = 5000;
257 Uint16 logoalpha = 0;
258 int credittime = 0;
259 int creditstage = 0;
260 int intromovietime = 0;
261 int intromoviestage = 0;
262 int firstendmovietime = 0;
263 int firstendmoviestage = 0;
264 int secondendmovietime = 0;
265 int secondendmoviestage = 0;
266 int thirdendmoviestage = 0;
267 int thirdendmovietime = 0;
268 int thirdEndNumLines = 6;
269 int fourthendmoviestage = 0;
270 int fourthendmovietime = 0;
271 int fourthEndNumLines = 13;
272 real_t drunkextend = 0;
273 bool losingConnection[MAXPLAYERS] = { false };
274 bool subtitleVisible = false;
275 int subtitleCurrent = 0;
276 
277 // new text crawls...
278 int DLCendmoviealpha[8][30] = { 0 };
279 int DLCendmovieStageAndTime[8][2] = { 0 };
280 
281 int DLCendmovieNumLines[8] =
282 {
283 	7,	// MOVIE_MIDGAME_HERX_MONSTERS,
284 	8,	// MOVIE_MIDGAME_BAPHOMET_MONSTERS,
285 	8,	// MOVIE_MIDGAME_BAPHOMET_HUMAN_AUTOMATON,
286 	5,	// MOVIE_CLASSIC_WIN_MONSTERS,
287 	7,	// MOVIE_CLASSIC_WIN_BAPHOMET_MONSTERS,
288 	13,	// MOVIE_WIN_AUTOMATON,
289 	13,	// MOVIE_WIN_DEMONS_UNDEAD,
290 	13	// MOVIE_WIN_BEASTS
291 };
292 char epilogueHostName[128] = "";
293 int epilogueHostRace = 0;
294 int epilogueMultiplayerType = SINGLE;
295 
296 //Confirm resolution window stuff.
297 bool resolutionChanged = false;
298 bool confirmResolutionWindow = false;
299 int resolutionConfirmationTimer = 0;
300 Sint32 oldXres;
301 Sint32 oldYres;
302 Sint32 oldFullscreen;
303 bool oldBorderless = false;
304 real_t oldGamma;
305 bool oldVerticalSync = false;
306 button_t* revertResolutionButton = nullptr;
307 
308 void buttonCloseSettingsSubwindow(button_t* my);
309 
getSettingsTabButton()310 button_t* getSettingsTabButton()
311 {
312 	switch ( settings_tab )
313 	{
314 		case SETTINGS_VIDEO_TAB:
315 			return button_video_tab;
316 		case SETTINGS_AUDIO_TAB:
317 			return button_audio_tab;
318 		case SETTINGS_KEYBOARD_TAB:
319 			return button_keyboard_tab;
320 		case SETTINGS_MOUSE_TAB:
321 			return button_mouse_tab;
322 		case SETTINGS_GAMEPAD_BINDINGS_TAB:
323 			return button_gamepad_bindings_tab;
324 		case SETTINGS_GAMEPAD_SETTINGS_TAB:
325 			return button_gamepad_settings_tab;
326 		case SETTINGS_MISC_TAB:
327 			return button_misc_tab;
328 	}
329 
330 	return nullptr;
331 }
332 
changeSettingsTab(int option)333 void changeSettingsTab(int option)
334 {
335 	if ( getSettingsTabButton() )
336 	{
337 		getSettingsTabButton()->outline = false;
338 	}
339 
340 	settings_tab = option;
341 
342 	if ( settings_tab < 0 )
343 	{
344 		settings_tab = NUM_SETTINGS_TABS - 1;
345 	}
346 	if ( settings_tab >= NUM_SETTINGS_TABS )
347 	{
348 		settings_tab = 0;
349 	}
350 
351 	if ( getSettingsTabButton() )
352 	{
353 		button_t* button = getSettingsTabButton();
354 		button->outline = true;
355 		int x = button->x + (button->sizex / 2);
356 		int y = button->y + (button->sizey / 2);
357 	}
358 }
359 
360 std::vector<std::pair<std::string, int>> menuOptions;
initMenuOptions()361 void initMenuOptions()
362 {
363 	menuOptions.clear();
364 #if (defined USE_EOS && !defined STEAMWORKS)
365 	menuOptions.push_back(std::make_pair(language[1303], 1)); // start game
366 	menuOptions.push_back(std::make_pair(language[1304], 2)); // intro
367 	menuOptions.push_back(std::make_pair(language[3964], 3)); // hall of trials
368 	menuOptions.push_back(std::make_pair(language[1305], 4)); // statistics
369 	menuOptions.push_back(std::make_pair(language[3971], 5)); // achievements
370 	menuOptions.push_back(std::make_pair(language[1306], 6)); // settings
371 	menuOptions.push_back(std::make_pair(language[1307], 7)); // credits
372 	menuOptions.push_back(std::make_pair(language[2978], 8)); // custom content
373 	menuOptions.push_back(std::make_pair(language[1308], 9)); // quit
374 #else
375 	menuOptions.push_back(std::make_pair(language[1303], 1)); // start game
376 	menuOptions.push_back(std::make_pair(language[1304], 2)); // intro
377 	menuOptions.push_back(std::make_pair(language[3964], 3)); // hall of trials
378 	menuOptions.push_back(std::make_pair(language[1305], 4)); // statistics
379 	menuOptions.push_back(std::make_pair(language[1306], 5)); // settings
380 	menuOptions.push_back(std::make_pair(language[1307], 6)); // credits
381 	menuOptions.push_back(std::make_pair(language[2978], 7)); // custom content
382 #ifdef STEAMWORKS
383 	menuOptions.push_back(std::make_pair(language[2979], 8)); // workshop
384 	menuOptions.push_back(std::make_pair(language[1308], 9)); // quit
385 #else
386 	menuOptions.push_back(std::make_pair(language[1308], 8)); // quit
387 #endif
388 #endif
389 }
390 
getPrevMenuOption(int & currentMenuOption)391 void getPrevMenuOption(int& currentMenuOption)
392 {
393 	for ( auto it = menuOptions.begin(); it != menuOptions.end(); )
394 	{
395 		if ( (*it).second == currentMenuOption )
396 		{
397 			if ( it == menuOptions.begin() )
398 			{
399 				currentMenuOption = menuOptions.back().second;
400 				return;
401 			}
402 			--it;
403 			currentMenuOption = (*it).second;
404 			return;
405 		}
406 		++it;
407 	}
408 
409 	currentMenuOption = menuOptions.at(0).second; // default value.
410 }
411 
getNextMenuOption(int & currentMenuOption)412 void getNextMenuOption(int& currentMenuOption)
413 {
414 	for ( auto it = menuOptions.begin(); it != menuOptions.end(); )
415 	{
416 		if ( (*it).second == currentMenuOption )
417 		{
418 			++it;
419 			if ( it == menuOptions.end() )
420 			{
421 				currentMenuOption = menuOptions.at(0).second;
422 				return;
423 			}
424 			currentMenuOption = (*it).second;
425 			return;
426 		}
427 		++it;
428 	}
429 
430 	currentMenuOption = menuOptions.at(0).second; // default value.
431 }
432 
navigateMainMenuItems(bool mode)433 void navigateMainMenuItems(bool mode)
434 {
435 	if ( menuOptions.empty() )
436 	{
437 		return;
438 	}
439 
440 	int numInGameMenuOptions = 4 + (multiplayer != CLIENT);
441 	if ( !mode && gameModeManager.getMode() == GameModeManager_t::GAME_MODE_TUTORIAL )
442 	{
443 		if ( !strcmp(map.name, "Tutorial Hub") )
444 		{
445 			numInGameMenuOptions = 4;
446 		}
447 	}
448 
449 #if defined USE_EOS && !defined STEAMWORKS
450 	numInGameMenuOptions += 1;
451 #endif
452 
453 	int warpx, warpy;
454 	if (menuselect == 0)
455 	{
456 		//No menu item selected.
457 		if ( keystatus[SDL_SCANCODE_UP] || (*inputPressed(joyimpulses[INJOY_DPAD_UP]) && rebindaction == -1) )
458 		{
459 			keystatus[SDL_SCANCODE_UP] = 0;
460 			if ( rebindaction == -1 )
461 			{
462 				*inputPressed(joyimpulses[INJOY_DPAD_UP]) = 0;
463 			}
464 			draw_cursor = false;
465 			menuselect = menuOptions.at(0).second;
466 			//Warp cursor to menu item, for gamepad convenience.
467 			warpx = 50 + 18;
468 			warpy = (yres / 4) + 80 + (18 / 2); //I am a wizard. I hate magic numbers.
469 			SDL_WarpMouseInWindow(screen, warpx, warpy);
470 		}
471 		else if ( keystatus[SDL_SCANCODE_DOWN] || (*inputPressed(joyimpulses[INJOY_DPAD_DOWN]) && rebindaction == -1) )
472 		{
473 			keystatus[SDL_SCANCODE_DOWN] = 0;
474 			if ( rebindaction == -1 )
475 			{
476 				*inputPressed(joyimpulses[INJOY_DPAD_DOWN]) = 0;
477 			}
478 			draw_cursor = false;
479 			menuselect = menuOptions.at(0).second;
480 			warpx = 50 + 18;
481 			warpy = (yres / 4) + 80 + (18 / 2);
482 			SDL_WarpMouseInWindow(screen, warpx, warpy);
483 		}
484 	}
485 	else
486 	{
487 		if ( keystatus[SDL_SCANCODE_UP] || (*inputPressed(joyimpulses[INJOY_DPAD_UP]) && rebindaction == -1) )
488 		{
489 			keystatus[SDL_SCANCODE_UP] = 0;
490 			if ( rebindaction == -1 )
491 			{
492 				*inputPressed(joyimpulses[INJOY_DPAD_UP]) = 0;
493 			}
494 			draw_cursor = false;
495 			if ( mode )
496 			{
497 				getPrevMenuOption(menuselect);
498 			}
499 			else
500 			{
501 				menuselect--;
502 				if ( menuselect == 0 )
503 				{
504 					menuselect = numInGameMenuOptions;
505 				}
506 			}
507 
508 			warpx = 50 + 18;
509 			warpy = (((yres / 4) + 80 + (18 / 2)) + ((menuselect - 1) * 24));
510 			SDL_WarpMouseInWindow(screen, warpx, warpy);
511 		}
512 		else if (keystatus[SDL_SCANCODE_DOWN] || (*inputPressed(joyimpulses[INJOY_DPAD_DOWN]) && rebindaction == -1) )
513 		{
514 			keystatus[SDL_SCANCODE_DOWN] = 0;
515 			if ( rebindaction == -1 )
516 			{
517 				*inputPressed(joyimpulses[INJOY_DPAD_DOWN]) = 0;
518 			}
519 			draw_cursor = false;
520 			if ( mode )
521 			{
522 				getNextMenuOption(menuselect);
523 			}
524 			else
525 			{
526 				menuselect++;
527 				if ( menuselect > numInGameMenuOptions )
528 				{
529 					menuselect = 1;
530 				}
531 			}
532 			warpx = 50 + 18;
533 			warpy = (((yres / 4) + 80 + (18 / 2)) + ((menuselect - 1) * 24));
534 			SDL_WarpMouseInWindow(screen, warpx, warpy);
535 		}
536 	}
537 }
538 
printJoybindingNames(const SDL_Rect & currentPos,int c,bool & rebindingaction)539 void inline printJoybindingNames(const SDL_Rect& currentPos, int c, bool &rebindingaction)
540 {
541 	ttfPrintText(ttf8, currentPos.x, currentPos.y, language[1948 + c]);
542 	if ( mousestatus[SDL_BUTTON_LEFT] && !rebindingaction )
543 	{
544 		if ( omousex >= currentPos.x && omousex < subx2 - 24 )
545 		{
546 			if ( omousey >= currentPos.y && omousey < currentPos.y + 12 )
547 			{
548 				mousestatus[SDL_BUTTON_LEFT] = 0;
549 				if ( settings_joyimpulses[c] != UNBOUND_JOYBINDING )
550 				{
551 					settings_joyimpulses[c] = UNBOUND_JOYBINDING; //Unbind the joybinding if clicked on.
552 				}
553 				else
554 				{
555 					lastkeypressed = 0;
556 					rebindingaction = true;
557 					rebindaction = c;
558 				}
559 			}
560 		}
561 	}
562 
563 	if ( c != rebindaction )
564 	{
565 		if ( !strcmp(getInputName(settings_joyimpulses[c]), "Unassigned key" ))
566 		{
567 			ttfPrintTextColor(ttf8, currentPos.x + 232, currentPos.y, uint32ColorBaronyBlue(*mainsurface), true, getInputName(settings_joyimpulses[c]));
568 		}
569 		else if ( !strcmp(getInputName(settings_joyimpulses[c]), "Unknown key") || !strcmp(getInputName(settings_joyimpulses[c]), "Unknown trigger") )
570 		{
571 			ttfPrintTextColor(ttf8, currentPos.x + 232, currentPos.y, uint32ColorRed(*mainsurface), true, getInputName(settings_joyimpulses[c]));
572 		}
573 		else
574 		{
575 			ttfPrintText(ttf8, currentPos.x + 232, currentPos.y, getInputName(settings_joyimpulses[c]));
576 		}
577 	}
578 	else
579 	{
580 		ttfPrintTextColor(ttf8, currentPos.x + 232, currentPos.y, uint32ColorGreen(*mainsurface), true, "...");
581 	}
582 }
583 
584 enum CharacterDLCValidation : int
585 {
586 	INVALID_CHARACTER,
587 	VALID_OK_CHARACTER,
588 	INVALID_REQUIREDLC1,
589 	INVALID_REQUIREDLC2,
590 	INVALID_REQUIRE_ACHIEVEMENT
591 };
592 
isAchievementUnlockedForClassUnlock(PlayerRaces race)593 bool isAchievementUnlockedForClassUnlock(PlayerRaces race)
594 {
595 #ifdef STEAMWORKS
596 	bool unlocked = false;
597 	if ( enabledDLCPack1 && race == RACE_SKELETON && SteamUserStats()->GetAchievement("BARONY_ACH_BONY_BARON", &unlocked) )
598 	{
599 		return unlocked;
600 	}
601 	else if ( enabledDLCPack1 && race == RACE_VAMPIRE && SteamUserStats()->GetAchievement("BARONY_ACH_BUCKTOOTH_BARON", &unlocked) )
602 	{
603 		return unlocked;
604 	}
605 	else if ( enabledDLCPack1 && race == RACE_SUCCUBUS && SteamUserStats()->GetAchievement("BARONY_ACH_BOMBSHELL_BARON", &unlocked) )
606 	{
607 		return unlocked;
608 	}
609 	else if ( enabledDLCPack1 && race == RACE_GOATMAN && SteamUserStats()->GetAchievement("BARONY_ACH_BLEATING_BARON", &unlocked) )
610 	{
611 		return unlocked;
612 	}
613 	else if ( enabledDLCPack2 && race == RACE_AUTOMATON && SteamUserStats()->GetAchievement("BARONY_ACH_BOILERPLATE_BARON", &unlocked) )
614 	{
615 		return unlocked;
616 	}
617 	else if ( enabledDLCPack2 && race == RACE_INCUBUS && SteamUserStats()->GetAchievement("BARONY_ACH_BAD_BOY_BARON", &unlocked) )
618 	{
619 		return unlocked;
620 	}
621 	else if ( enabledDLCPack2 && race == RACE_GOBLIN && SteamUserStats()->GetAchievement("BARONY_ACH_BAYOU_BARON", &unlocked) )
622 	{
623 		return unlocked;
624 	}
625 	else if ( enabledDLCPack2 && race == RACE_INSECTOID && SteamUserStats()->GetAchievement("BARONY_ACH_BUGGAR_BARON", &unlocked) )
626 	{
627 		return unlocked;
628 	}
629 #elif defined USE_EOS
630 	if ( enabledDLCPack1 && race == RACE_SKELETON && achievementUnlocked("BARONY_ACH_BONY_BARON") )
631 	{
632 		return true;
633 	}
634 	else if ( enabledDLCPack1 && race == RACE_VAMPIRE && achievementUnlocked("BARONY_ACH_BUCKTOOTH_BARON") )
635 	{
636 		return true;
637 	}
638 	else if ( enabledDLCPack1 && race == RACE_SUCCUBUS && achievementUnlocked("BARONY_ACH_BOMBSHELL_BARON") )
639 	{
640 		return true;
641 	}
642 	else if ( enabledDLCPack1 && race == RACE_GOATMAN && achievementUnlocked("BARONY_ACH_BLEATING_BARON") )
643 	{
644 		return true;
645 	}
646 	else if ( enabledDLCPack2 && race == RACE_AUTOMATON && achievementUnlocked("BARONY_ACH_BOILERPLATE_BARON") )
647 	{
648 		return true;
649 	}
650 	else if ( enabledDLCPack2 && race == RACE_INCUBUS && achievementUnlocked("BARONY_ACH_BAD_BOY_BARON") )
651 	{
652 		return true;
653 	}
654 	else if ( enabledDLCPack2 && race == RACE_GOBLIN && achievementUnlocked("BARONY_ACH_BAYOU_BARON") )
655 	{
656 		return true;
657 	}
658 	else if ( enabledDLCPack2 && race == RACE_INSECTOID && achievementUnlocked("BARONY_ACH_BUGGAR_BARON") )
659 	{
660 		return true;
661 	}
662 #else
663 	return false;
664 #endif // STEAMWORKS
665 	return false;
666 }
667 
isCharacterValidFromDLC(Stat & myStats,int characterClass)668 int isCharacterValidFromDLC(Stat& myStats, int characterClass)
669 {
670 	switch ( characterClass )
671 	{
672 		case CLASS_CONJURER:
673 		case CLASS_ACCURSED:
674 		case CLASS_MESMER:
675 		case CLASS_BREWER:
676 			if ( !enabledDLCPack1 )
677 			{
678 				return INVALID_REQUIREDLC1;
679 			}
680 			break;
681 		case CLASS_MACHINIST:
682 		case CLASS_PUNISHER:
683 		case CLASS_SHAMAN:
684 		case CLASS_HUNTER:
685 			if ( !enabledDLCPack2 )
686 			{
687 				return INVALID_REQUIREDLC2;
688 			}
689 			break;
690 		default:
691 			break;
692 	}
693 
694 	switch ( myStats.playerRace )
695 	{
696 		case RACE_SKELETON:
697 		case RACE_VAMPIRE:
698 		case RACE_SUCCUBUS:
699 		case RACE_GOATMAN:
700 			if ( !enabledDLCPack1 )
701 			{
702 				return INVALID_REQUIREDLC1;
703 			}
704 			break;
705 		case RACE_AUTOMATON:
706 		case RACE_INCUBUS:
707 		case RACE_GOBLIN:
708 		case RACE_INSECTOID:
709 			if ( !enabledDLCPack2 )
710 			{
711 				return INVALID_REQUIREDLC2;
712 			}
713 			break;
714 		default:
715 			break;
716 	}
717 
718 	if ( myStats.playerRace == RACE_HUMAN )
719 	{
720 		return VALID_OK_CHARACTER;
721 	}
722 	else if ( myStats.playerRace > RACE_HUMAN && myStats.appearance == 1 )
723 	{
724 		return VALID_OK_CHARACTER; // aesthetic only option.
725 	}
726 	if ( characterClass <= CLASS_MONK )
727 	{
728 		return VALID_OK_CHARACTER;
729 	}
730 
731 	switch ( characterClass )
732 	{
733 		case CLASS_CONJURER:
734 			if ( myStats.playerRace == RACE_SKELETON )
735 			{
736 				return VALID_OK_CHARACTER;
737 			}
738 			return isAchievementUnlockedForClassUnlock(RACE_SKELETON) ? VALID_OK_CHARACTER : INVALID_REQUIRE_ACHIEVEMENT;
739 			break;
740 		case CLASS_ACCURSED:
741 			if ( myStats.playerRace == RACE_VAMPIRE )
742 			{
743 				return VALID_OK_CHARACTER;
744 			}
745 			return isAchievementUnlockedForClassUnlock(RACE_VAMPIRE) ? VALID_OK_CHARACTER : INVALID_REQUIRE_ACHIEVEMENT;
746 			break;
747 		case CLASS_MESMER:
748 			if ( myStats.playerRace == RACE_SUCCUBUS )
749 			{
750 				return VALID_OK_CHARACTER;
751 			}
752 			return isAchievementUnlockedForClassUnlock(RACE_SUCCUBUS) ? VALID_OK_CHARACTER : INVALID_REQUIRE_ACHIEVEMENT;
753 			break;
754 		case CLASS_BREWER:
755 			if ( myStats.playerRace == RACE_GOATMAN )
756 			{
757 				return VALID_OK_CHARACTER;
758 			}
759 			return isAchievementUnlockedForClassUnlock(RACE_GOATMAN) ? VALID_OK_CHARACTER : INVALID_REQUIRE_ACHIEVEMENT;
760 			break;
761 		case CLASS_MACHINIST:
762 			if ( myStats.playerRace == RACE_AUTOMATON )
763 			{
764 				return VALID_OK_CHARACTER;
765 			}
766 			return isAchievementUnlockedForClassUnlock(RACE_AUTOMATON) ? VALID_OK_CHARACTER : INVALID_REQUIRE_ACHIEVEMENT;
767 			break;
768 		case CLASS_PUNISHER:
769 			if ( myStats.playerRace == RACE_INCUBUS )
770 			{
771 				return VALID_OK_CHARACTER;
772 			}
773 			return isAchievementUnlockedForClassUnlock(RACE_INCUBUS) ? VALID_OK_CHARACTER : INVALID_REQUIRE_ACHIEVEMENT;
774 			break;
775 		case CLASS_SHAMAN:
776 			if ( myStats.playerRace == RACE_GOBLIN )
777 			{
778 				return VALID_OK_CHARACTER;
779 			}
780 			return isAchievementUnlockedForClassUnlock(RACE_GOBLIN) ? VALID_OK_CHARACTER : INVALID_REQUIRE_ACHIEVEMENT;
781 			break;
782 		case CLASS_HUNTER:
783 			if ( myStats.playerRace == RACE_INSECTOID )
784 			{
785 				return VALID_OK_CHARACTER;
786 			}
787 			return isAchievementUnlockedForClassUnlock(RACE_INSECTOID) ? VALID_OK_CHARACTER : INVALID_REQUIRE_ACHIEVEMENT;
788 			break;
789 		default:
790 			break;
791 	}
792 
793 	return INVALID_CHARACTER;
794 }
795 
pauseMenuOnInputPressed()796 void inline pauseMenuOnInputPressed()
797 {
798 	mousestatus[SDL_BUTTON_LEFT] = 0;
799 	keystatus[SDL_SCANCODE_RETURN] = 0;
800 	playSound(139, 64);
801 	if ( rebindaction == -1 )
802 	{
803 		*inputPressed(joyimpulses[INJOY_MENU_NEXT]) = 0;
804 	}
805 }
806 
handleInGamePauseMenu()807 void handleInGamePauseMenu()
808 {
809 	Uint32 colorGray = uint32ColorGray(*mainsurface);
810 	const bool inputIsPressed = (mousestatus[SDL_BUTTON_LEFT] || keystatus[SDL_SCANCODE_RETURN] || (*inputPressed(joyimpulses[INJOY_MENU_NEXT]) && rebindaction == -1));
811 	SDL_Rect text;
812 	text.x = 50;
813 	text.h = 18;
814 	text.w = 18;
815 
816 	int numOption = 1;
817 
818 	text.y = yres / 4 + 80;
819 	if ( ((omousex >= text.x && omousex < text.x + strlen(language[1309]) * text.w && omousey >= text.y && omousey < text.y + text.h) || (menuselect == numOption)) && subwindow == 0 && introstage == 1 )
820 	{
821 		// resume game
822 		menuselect = 1;
823 		ttfPrintTextFormattedColor(ttf16, text.x, text.y, colorGray, language[1309]);
824 		if ( inputIsPressed )
825 		{
826 			pauseMenuOnInputPressed();
827 			pauseGame(1, MAXPLAYERS);
828 		}
829 	}
830 	else
831 	{
832 		ttfPrintText(ttf16, text.x, text.y, language[1309]);
833 	}
834 
835 	bool achievementsMenu = false;
836 #if !defined STEAMWORKS
837 #ifdef USE_EOS
838 	achievementsMenu = true;
839 #endif
840 #endif
841 	text.y += 24;
842 	++numOption;
843 	if ( achievementsMenu )
844 	{
845 		if ( ((omousex >= text.x && omousex < text.x + strlen(language[3971]) * text.w && omousey >= text.y && omousey < text.y + text.h) || (menuselect == numOption)) && subwindow == 0 && introstage == 1 )
846 		{
847 			// settings menu
848 			menuselect = numOption;
849 			ttfPrintTextFormattedColor(ttf16, text.x, text.y, colorGray, language[3971]);
850 			if ( inputIsPressed )
851 			{
852 				pauseMenuOnInputPressed();
853 				openAchievementsWindow();
854 			}
855 		}
856 		else
857 		{
858 			ttfPrintText(ttf16, text.x, text.y, language[3971]);
859 		}
860 		text.y += 24;
861 		++numOption;
862 	}
863 
864 	if ( ((omousex >= text.x && omousex < text.x + strlen(language[1306]) * text.w && omousey >= text.y && omousey < text.y + text.h) || (menuselect == numOption)) && subwindow == 0 && introstage == 1 )
865 	{
866 		// settings menu
867 		menuselect = numOption;
868 		ttfPrintTextFormattedColor(ttf16, text.x, text.y, colorGray, language[1306]);
869 		if ( inputIsPressed )
870 		{
871 			pauseMenuOnInputPressed();
872 			openSettingsWindow();
873 		}
874 	}
875 	else
876 	{
877 		ttfPrintText(ttf16, text.x, text.y, language[1306]);
878 	}
879 	char* endgameText = NULL;
880 	char* quitgameText = language[1313];
881 	bool singleplayerAliveEndGameAndSave = false;
882 	bool singleplayerBossLevelDisableSaveOnExit = false;
883 	bool multiplayerAliveEndGameAndSave = false;
884 	if ( multiplayer == SINGLE )
885 	{
886 		if ( stats[clientnum] && stats[clientnum]->HP > 0 )
887 		{
888 			endgameText = language[3919];
889 			singleplayerAliveEndGameAndSave = true;
890 			if ( !strncmp(map.name, "Boss", 4)
891 				|| !strncmp(map.name, "Hell Boss", 9)
892 				|| !strncmp(map.name, "Sanctum", 7) )
893 			{
894 				// boss floor, no save scumming easily!
895 				singleplayerAliveEndGameAndSave = false;
896 				singleplayerBossLevelDisableSaveOnExit = true;
897 				endgameText = language[1310];
898 				quitgameText = language[3987];
899 			}
900 		}
901 		else
902 		{
903 			endgameText = language[1310];
904 			quitgameText = language[3987];
905 			singleplayerAliveEndGameAndSave = false;
906 		}
907 	}
908 	else if ( multiplayer == SERVER )
909 	{
910 		endgameText = language[1310];
911 		quitgameText = language[3987];
912 		for ( int i = 0; i < MAXPLAYERS; ++i )
913 		{
914 			if ( !client_disconnected[i] && stats[i] && stats[i]->HP > 0 )
915 			{
916 				multiplayerAliveEndGameAndSave = true;
917 				quitgameText = language[1313];
918 				endgameText = language[3019];
919 				break;
920 			}
921 		}
922 	}
923 	else
924 	{
925 		endgameText = language[3019];
926 	}
927 
928 	text.y += 24;
929 	++numOption;
930 	if ( ((omousex >= text.x && omousex < text.x + strlen(endgameText) * text.w && omousey >= text.y && omousey < text.y + text.h) || (menuselect == numOption)) && subwindow == 0 && introstage == 1 )
931 	{
932 		// end game / return to main menu
933 		menuselect = numOption;
934 		ttfPrintTextFormattedColor(ttf16, text.x, text.y, colorGray, endgameText);
935 		if ( inputIsPressed )
936 		{
937 			pauseMenuOnInputPressed();
938 
939 			// create confirmation window
940 			subwindow = 1;
941 			if ( multiplayer == SINGLE )
942 			{
943 				subx1 = xres / 2 - 144;
944 				subx2 = xres / 2 + 144;
945 				suby1 = yres / 2 - 64;
946 				suby2 = yres / 2 + 64;
947 				if ( singleplayerAliveEndGameAndSave )
948 				{
949 					strcpy(subtext, language[3920]);
950 					subx1 = xres / 2 - 188;
951 					subx2 = xres / 2 + 188;
952 					suby1 = yres / 2 - 64;
953 					suby2 = yres / 2 + 64;
954 
955 					// add a cancel button
956 					button_t* button = newButton();
957 					strcpy(button->label, language[1316]);
958 					button->x = subx2 - strlen(language[1316]) * 12 - 16;
959 					button->y = suby2 - 28;
960 					button->sizex = strlen(language[1316]) * 12 + 8;
961 					button->sizey = 20;
962 					button->action = &buttonCloseSubwindow;
963 					button->visible = 1;
964 					button->focused = 1;
965 				}
966 				else
967 				{
968 					subx1 = xres / 2 - 188;
969 					subx2 = xres / 2 + 188;
970 					if ( singleplayerBossLevelDisableSaveOnExit )
971 					{
972 						strcpy(subtext, language[3990]);
973 					}
974 					else
975 					{
976 						strcpy(subtext, language[1129]);
977 					}
978 				}
979 			}
980 			else
981 			{
982 				subx1 = xres / 2 - 224;
983 				subx2 = xres / 2 + 224;
984 				if ( multiplayer == SERVER )
985 				{
986 					if ( multiplayerAliveEndGameAndSave )
987 					{
988 						suby1 = yres / 2 - 100;
989 						suby2 = yres / 2 + 100;
990 						strcpy(subtext, language[3021]);
991 					}
992 					else
993 					{
994 						suby1 = yres / 2 - 90;
995 						suby2 = yres / 2 + 90;
996 						strcpy(subtext, language[3986]);
997 					}
998 				}
999 				else if ( multiplayer == CLIENT )
1000 				{
1001 					suby1 = yres / 2 - 112;
1002 					suby2 = yres / 2 + 112;
1003 					strcpy(subtext, language[3020]);
1004 				}
1005 			}
1006 
1007 			// close button
1008 			button_t* button = newButton();
1009 			strcpy(button->label, "x");
1010 			button->x = subx2 - 20;
1011 			button->y = suby1;
1012 			button->sizex = 20;
1013 			button->sizey = 20;
1014 			button->action = &buttonCloseSubwindow;
1015 			button->visible = 1;
1016 			button->focused = 1;
1017 			button->key = SDL_SCANCODE_ESCAPE;
1018 			button->joykey = joyimpulses[INJOY_MENU_CANCEL];
1019 
1020 			// yes button
1021 			button = newButton();
1022 			strcpy(button->label, language[1314]);
1023 			button->x = subx1 + 8;
1024 			button->y = suby2 - 28;
1025 			button->sizex = strlen(language[1314]) * 12 + 8;
1026 			button->sizey = 20;
1027 			if ( multiplayer == SINGLE )
1028 			{
1029 				if ( singleplayerAliveEndGameAndSave )
1030 				{
1031 					button->action = &buttonCloseAndEndGameConfirm;
1032 				}
1033 				else
1034 				{
1035 					button->action = &buttonEndGameConfirm;
1036 				}
1037 			}
1038 			else
1039 			{
1040 				button->action = &buttonCloseAndEndGameConfirm;
1041 			}
1042 			button->visible = 1;
1043 			button->focused = 1;
1044 			button->key = SDL_SCANCODE_RETURN;
1045 			button->joykey = joyimpulses[INJOY_MENU_NEXT];
1046 
1047 			if ( multiplayer == SINGLE && singleplayerAliveEndGameAndSave )
1048 			{
1049 				// noop - button created earlier.
1050 			}
1051 			else
1052 			{
1053 				// cancel button
1054 				button = newButton();
1055 				strcpy(button->label, language[1316]);
1056 				button->x = subx2 - strlen(language[1316]) * 12 - 16;
1057 				button->y = suby2 - 28;
1058 				button->sizex = strlen(language[1316]) * 12 + 8;
1059 				button->sizey = 20;
1060 				button->action = &buttonCloseSubwindow;
1061 				button->visible = 1;
1062 				button->focused = 1;
1063 			}
1064 		}
1065 	}
1066 	else
1067 	{
1068 		ttfPrintText(ttf16, text.x, text.y, endgameText);
1069 	}
1070 
1071 	if ( multiplayer != CLIENT )
1072 	{
1073 		text.y += 24;
1074 		++numOption;
1075 		if ( ((omousex >= text.x && omousex < text.x + strlen(language[1312]) * text.w && omousey >= text.y && omousey < text.y + text.h) || (menuselect == numOption)) && subwindow == 0 && introstage == 1 )
1076 		{
1077 			//restart game
1078 			menuselect = numOption;
1079 			ttfPrintTextFormattedColor(ttf16, text.x, text.y, colorGray, language[1312]);
1080 			if ( inputIsPressed )
1081 			{
1082 				pauseMenuOnInputPressed();
1083 
1084 				// create confirmation window
1085 				subwindow = 1;
1086 				subx1 = xres / 2 - 164;
1087 				subx2 = xres / 2 + 164;
1088 				suby1 = yres / 2 - 48;
1089 				suby2 = yres / 2 + 48;
1090 				strcpy(subtext, language[1130]);
1091 
1092 				// close button
1093 				button_t* button = newButton();
1094 				strcpy(button->label, "x");
1095 				button->x = subx2 - 20;
1096 				button->y = suby1;
1097 				button->sizex = 20;
1098 				button->sizey = 20;
1099 				button->action = &buttonCloseSubwindow;
1100 				button->visible = 1;
1101 				button->focused = 1;
1102 				button->key = SDL_SCANCODE_ESCAPE;
1103 				button->joykey = joyimpulses[INJOY_MENU_CANCEL];
1104 
1105 				// yes button
1106 				button = newButton();
1107 				strcpy(button->label, language[1314]);
1108 				button->x = subx1 + 8;
1109 				button->y = suby2 - 28;
1110 				button->sizex = strlen(language[1314]) * 12 + 8;
1111 				button->sizey = 20;
1112 				if ( multiplayer == SINGLE )
1113 				{
1114 					button->action = &buttonStartSingleplayer;
1115 				}
1116 				else
1117 				{
1118 					button->action = &buttonStartServer;
1119 				}
1120 				button->visible = 1;
1121 				button->focused = 1;
1122 				button->key = SDL_SCANCODE_RETURN;
1123 				button->joykey = joyimpulses[INJOY_MENU_NEXT];
1124 
1125 				// no button
1126 				button = newButton();
1127 				strcpy(button->label, language[1315]);
1128 				button->x = subx2 - strlen(language[1315]) * 12 - 16;
1129 				button->y = suby2 - 28;
1130 				button->sizex = strlen(language[1315]) * 12 + 8;
1131 				button->sizey = 20;
1132 				button->action = &buttonCloseSubwindow;
1133 				button->visible = 1;
1134 				button->focused = 1;
1135 			}
1136 		}
1137 		else
1138 		{
1139 			ttfPrintText(ttf16, text.x, text.y, language[1312]);
1140 		}
1141 	}
1142 
1143 	text.y += 24;
1144 	++numOption;
1145 	if ( ((omousex >= text.x && omousex < text.x + strlen(quitgameText) * text.w && omousey >= text.y && omousey < text.y + text.h) || (menuselect == numOption)) && subwindow == 0 && introstage == 1 )
1146 	{
1147 		menuselect = numOption;
1148 		// save & quit
1149 		ttfPrintTextFormattedColor(ttf16, text.x, text.y, colorGray, quitgameText);
1150 		if ( inputIsPressed )
1151 		{
1152 			pauseMenuOnInputPressed();
1153 
1154 			// create confirmation window
1155 			subwindow = 1;
1156 			subx1 = xres / 2 - 188;
1157 			subx2 = xres / 2 + 188;
1158 			suby1 = yres / 2 - 64;
1159 			suby2 = yres / 2 + 64;
1160 			strcpy(subtext, language[1131]);
1161 			if ( multiplayer == SINGLE )
1162 			{
1163 				if ( !singleplayerAliveEndGameAndSave )
1164 				{
1165 					if ( singleplayerBossLevelDisableSaveOnExit )
1166 					{
1167 						strcpy(subtext, language[3991]);
1168 					}
1169 					else
1170 					{
1171 						strcpy(subtext, language[3988]);
1172 					}
1173 				}
1174 			}
1175 			else if ( multiplayer == SERVER )
1176 			{
1177 				if ( !multiplayerAliveEndGameAndSave )
1178 				{
1179 					subx1 = xres / 2 - 224;
1180 					subx2 = xres / 2 + 224;
1181 					suby1 = yres / 2 - 64;
1182 					suby2 = yres / 2 + 64;
1183 					strcpy(subtext, language[3989]);
1184 				}
1185 			}
1186 
1187 			// yes button
1188 			button_t* button = newButton();
1189 			strcpy(button->label, language[1314]);
1190 			button->x = subx1 + 8;
1191 			button->y = suby2 - 28;
1192 			button->sizex = strlen(language[1314]) * 12 + 8;
1193 			button->sizey = 20;
1194 			button->action = &buttonQuitConfirm;
1195 			button->visible = 1;
1196 			button->focused = 1;
1197 			button->key = SDL_SCANCODE_RETURN;
1198 			button->joykey = joyimpulses[INJOY_MENU_NEXT]; //TODO: Select which button to activate via dpad.
1199 
1200 			// no button
1201 			// button = newButton();
1202 			// strcpy(button->label, language[1315]);
1203 			// button->sizex = strlen(language[1315]) * 12 + 8;
1204 			// button->sizey = 20;
1205 			// button->x = subx1 + (subx2 - subx1) / 2 - button->sizex / 2;
1206 			// button->y = suby2 - 28;
1207 			// button->action = &buttonQuitNoSaveConfirm;
1208 			// button->visible = 1;
1209 			// button->focused = 1;
1210 
1211 			// cancel button
1212 			button = newButton();
1213 			strcpy(button->label, language[1316]);
1214 			button->x = subx2 - strlen(language[1316]) * 12 - 16;
1215 			button->y = suby2 - 28;
1216 			button->sizex = strlen(language[1316]) * 12 + 8;
1217 			button->sizey = 20;
1218 			button->action = &buttonCloseSubwindow;
1219 			button->visible = 1;
1220 			button->focused = 1;
1221 
1222 			// close button
1223 			button = newButton();
1224 			strcpy(button->label, "x");
1225 			button->x = subx2 - 20;
1226 			button->y = suby1;
1227 			button->sizex = 20;
1228 			button->sizey = 20;
1229 			button->action = &buttonCloseSubwindow;
1230 			button->visible = 1;
1231 			button->focused = 1;
1232 			button->key = SDL_SCANCODE_ESCAPE;
1233 			button->joykey = joyimpulses[INJOY_MENU_CANCEL];
1234 		}
1235 	}
1236 	else
1237 	{
1238 		ttfPrintText(ttf16, text.x, text.y, quitgameText);
1239 	}
1240 }
1241 
handleTutorialPauseMenu()1242 void handleTutorialPauseMenu()
1243 {
1244 	const Uint32 colorGray = uint32ColorGray(*mainsurface);
1245 	const bool inputIsPressed = (mousestatus[SDL_BUTTON_LEFT] || keystatus[SDL_SCANCODE_RETURN] || (*inputPressed(joyimpulses[INJOY_MENU_NEXT]) && rebindaction == -1));
1246 	SDL_Rect text;
1247 	text.x = 50;
1248 	text.h = 18;
1249 	text.w = 18;
1250 
1251 	bool mapIsTutorialHub = false;
1252 	if ( !strcmp(map.name, "Tutorial Hub") )
1253 	{
1254 		mapIsTutorialHub = true;
1255 	}
1256 
1257 	int numOption = 1;
1258 	bool achievementsMenu = false;
1259 #if !defined STEAMWORKS
1260 #ifdef USE_EOS
1261 	achievementsMenu = true;
1262 #endif
1263 #endif
1264 
1265 	text.y = yres / 4 + 80;
1266 	if ( ((omousex >= text.x && omousex < text.x + strlen(language[1309]) * text.w && omousey >= text.y && omousey < text.y + text.h) || (menuselect == numOption)) && subwindow == 0 && introstage == 1 )
1267 	{
1268 		// resume game
1269 		menuselect = numOption;
1270 		ttfPrintTextFormattedColor(ttf16, text.x, text.y, colorGray, language[1309]);
1271 		if ( inputIsPressed )
1272 		{
1273 			pauseMenuOnInputPressed();
1274 			pauseGame(1, MAXPLAYERS);
1275 		}
1276 	}
1277 	else
1278 	{
1279 		ttfPrintText(ttf16, text.x, text.y, language[1309]);
1280 	}
1281 
1282 	//++numOption;
1283 	//text.y += 24;
1284 	//if ( ((omousex >= text.x && omousex < text.x + strlen(language[1306]) * text.w && omousey >= text.y && omousey < text.y + text.h) || (menuselect == numOption)) && subwindow == 0 && introstage == 1 )
1285 	//{
1286 	//	// settings menu
1287 	//	menuselect = numOption;
1288 	//	ttfPrintTextFormattedColor(ttf16, text.x, text.y, colorGray, language[1306]);
1289 	//	if ( inputIsPressed )
1290 	//	{
1291 	//		pauseMenuOnInputPressed();
1292 	//		gameModeManager.Tutorial.Menu.open();
1293 	//	}
1294 	//}
1295 	//else
1296 	//{
1297 	//	ttfPrintText(ttf16, text.x, text.y, language[1306]);
1298 	//}
1299 
1300 	if ( achievementsMenu )
1301 	{
1302 		++numOption;
1303 		text.y += 24;
1304 		if ( ((omousex >= text.x && omousex < text.x + strlen(language[3971]) * text.w && omousey >= text.y && omousey < text.y + text.h) || (menuselect == numOption)) && subwindow == 0 && introstage == 1 )
1305 		{
1306 			// achievements menu
1307 			menuselect = numOption;
1308 			ttfPrintTextFormattedColor(ttf16, text.x, text.y, colorGray, language[3971]);
1309 			if ( inputIsPressed )
1310 			{
1311 				pauseMenuOnInputPressed();
1312 				openAchievementsWindow();
1313 			}
1314 		}
1315 		else
1316 		{
1317 			ttfPrintText(ttf16, text.x, text.y, language[3971]);
1318 		}
1319 	}
1320 
1321 	++numOption;
1322 	text.y += 24;
1323 	if ( ((omousex >= text.x && omousex < text.x + strlen(language[1306]) * text.w && omousey >= text.y && omousey < text.y + text.h) || (menuselect == numOption)) && subwindow == 0 && introstage == 1 )
1324 	{
1325 		// settings menu
1326 		menuselect = numOption;
1327 		ttfPrintTextFormattedColor(ttf16, text.x, text.y, colorGray, language[1306]);
1328 		if ( inputIsPressed )
1329 		{
1330 			pauseMenuOnInputPressed();
1331 			openSettingsWindow();
1332 		}
1333 	}
1334 	else
1335 	{
1336 		ttfPrintText(ttf16, text.x, text.y, language[1306]);
1337 	}
1338 
1339 	++numOption;
1340 	text.y += 24;
1341 	char* returnToHubOptionText = language[3958];
1342 	if ( mapIsTutorialHub )
1343 	{
1344 		returnToHubOptionText = language[3969];
1345 	}
1346 
1347 	if ( ((omousex >= text.x && omousex < text.x + strlen(returnToHubOptionText) * text.w && omousey >= text.y && omousey < text.y + text.h) || (menuselect == numOption)) && subwindow == 0 && introstage == 1 )
1348 	{
1349 		// return to hub
1350 		menuselect = numOption;
1351 		ttfPrintTextFormattedColor(ttf16, text.x, text.y, colorGray, returnToHubOptionText);
1352 		if ( inputIsPressed )
1353 		{
1354 			pauseMenuOnInputPressed();
1355 
1356 			// create confirmation window
1357 			subwindow = 1;
1358 			subx1 = xres / 2 - 144;
1359 			subx2 = xres / 2 + 144;
1360 			suby1 = yres / 2 - 48;
1361 			suby2 = yres / 2 + 48;
1362 
1363 			if ( mapIsTutorialHub )
1364 			{
1365 				strcpy(subtext, language[3970]);
1366 			}
1367 			else
1368 			{
1369 				strcpy(subtext, language[3960]);
1370 			}
1371 
1372 			// add a cancel button
1373 			button_t* button = newButton();
1374 			strcpy(button->label, language[1316]);
1375 			button->x = subx2 - strlen(language[1316]) * 12 - 16;
1376 			button->y = suby2 - 28;
1377 			button->sizex = strlen(language[1316]) * 12 + 8;
1378 			button->sizey = 20;
1379 			button->action = &buttonCloseSubwindow;
1380 			button->visible = 1;
1381 			button->focused = 1;
1382 
1383 			// close button
1384 			button = newButton();
1385 			strcpy(button->label, "x");
1386 			button->x = subx2 - 20;
1387 			button->y = suby1;
1388 			button->sizex = 20;
1389 			button->sizey = 20;
1390 			button->action = &buttonCloseSubwindow;
1391 			button->visible = 1;
1392 			button->focused = 1;
1393 			button->key = SDL_SCANCODE_ESCAPE;
1394 			button->joykey = joyimpulses[INJOY_MENU_CANCEL];
1395 
1396 			// yes button
1397 			button = newButton();
1398 			strcpy(button->label, language[1314]);
1399 			button->x = subx1 + 8;
1400 			button->y = suby2 - 28;
1401 			button->sizex = strlen(language[1314]) * 12 + 8;
1402 			button->sizey = 20;
1403 			button->action = &gameModeManager.Tutorial.buttonReturnToTutorialHub;
1404 			button->visible = 1;
1405 			button->focused = 1;
1406 			button->key = SDL_SCANCODE_RETURN;
1407 			button->joykey = joyimpulses[INJOY_MENU_NEXT];
1408 		}
1409 	}
1410 	else
1411 	{
1412 		ttfPrintText(ttf16, text.x, text.y, returnToHubOptionText);
1413 	}
1414 
1415 	if ( !mapIsTutorialHub )
1416 	{
1417 		++numOption;
1418 		text.y += 24;
1419 		if ( ((omousex >= text.x && omousex < text.x + strlen(language[3957]) * text.w && omousey >= text.y && omousey < text.y + text.h) || (menuselect == numOption)) && subwindow == 0 && introstage == 1 )
1420 		{
1421 			//restart game
1422 			menuselect = numOption;
1423 			ttfPrintTextFormattedColor(ttf16, text.x, text.y, colorGray, language[3957]);
1424 			if ( inputIsPressed )
1425 			{
1426 				pauseMenuOnInputPressed();
1427 
1428 				// create confirmation window
1429 				subwindow = 1;
1430 				subx1 = xres / 2 - 144;
1431 				subx2 = xres / 2 + 144;
1432 				suby1 = yres / 2 - 48;
1433 				suby2 = yres / 2 + 48;
1434 				strcpy(subtext, language[3956]);
1435 
1436 				// add a cancel button
1437 				button_t* button = newButton();
1438 				strcpy(button->label, language[1316]);
1439 				button->x = subx2 - strlen(language[1316]) * 12 - 16;
1440 				button->y = suby2 - 28;
1441 				button->sizex = strlen(language[1316]) * 12 + 8;
1442 				button->sizey = 20;
1443 				button->action = &buttonCloseSubwindow;
1444 				button->visible = 1;
1445 				button->focused = 1;
1446 
1447 				// close button
1448 				button = newButton();
1449 				strcpy(button->label, "x");
1450 				button->x = subx2 - 20;
1451 				button->y = suby1;
1452 				button->sizex = 20;
1453 				button->sizey = 20;
1454 				button->action = &buttonCloseSubwindow;
1455 				button->visible = 1;
1456 				button->focused = 1;
1457 				button->key = SDL_SCANCODE_ESCAPE;
1458 				button->joykey = joyimpulses[INJOY_MENU_CANCEL];
1459 
1460 				// yes button
1461 				button = newButton();
1462 				strcpy(button->label, language[1314]);
1463 				button->x = subx1 + 8;
1464 				button->y = suby2 - 28;
1465 				button->sizex = strlen(language[1314]) * 12 + 8;
1466 				button->sizey = 20;
1467 				button->action = &gameModeManager.Tutorial.buttonRestartTrial;
1468 				button->visible = 1;
1469 				button->focused = 1;
1470 				button->key = SDL_SCANCODE_RETURN;
1471 				button->joykey = joyimpulses[INJOY_MENU_NEXT];
1472 			}
1473 		}
1474 		else
1475 		{
1476 			ttfPrintText(ttf16, text.x, text.y, language[3957]);
1477 		}
1478 	}
1479 
1480 	text.y += 24;
1481 	++numOption;
1482 
1483 	if ( ((omousex >= 50 && omousex < 50 + strlen(language[3959]) * 18 && omousey >= text.y && omousey < text.y + text.h) || (menuselect == numOption)) && subwindow == 0 && introstage == 1 )
1484 	{
1485 		menuselect = numOption;
1486 		// return to main menu
1487 		ttfPrintTextFormattedColor(ttf16, text.x, text.y, colorGray, language[3959]);
1488 		if ( inputIsPressed )
1489 		{
1490 			pauseMenuOnInputPressed();
1491 
1492 			// create confirmation window
1493 			subwindow = 1;
1494 			subx1 = xres / 2 - 144;
1495 			subx2 = xres / 2 + 144;
1496 			suby1 = yres / 2 - 48;
1497 			suby2 = yres / 2 + 48;
1498 			strcpy(subtext, language[3961]);
1499 
1500 			// yes button
1501 			button_t* button = newButton();
1502 			strcpy(button->label, language[1314]);
1503 			button->x = subx1 + 8;
1504 			button->y = suby2 - 28;
1505 			button->sizex = strlen(language[1314]) * 12 + 8;
1506 			button->sizey = 20;
1507 			button->action = &buttonEndGameConfirm;
1508 			button->visible = 1;
1509 			button->focused = 1;
1510 			button->key = SDL_SCANCODE_RETURN;
1511 			button->joykey = joyimpulses[INJOY_MENU_NEXT]; //TODO: Select which button to activate via dpad.
1512 
1513 			// cancel button
1514 			button = newButton();
1515 			strcpy(button->label, language[1316]);
1516 			button->x = subx2 - strlen(language[1316]) * 12 - 16;
1517 			button->y = suby2 - 28;
1518 			button->sizex = strlen(language[1316]) * 12 + 8;
1519 			button->sizey = 20;
1520 			button->action = &buttonCloseSubwindow;
1521 			button->visible = 1;
1522 			button->focused = 1;
1523 
1524 			// close button
1525 			button = newButton();
1526 			strcpy(button->label, "x");
1527 			button->x = subx2 - 20;
1528 			button->y = suby1;
1529 			button->sizex = 20;
1530 			button->sizey = 20;
1531 			button->action = &buttonCloseSubwindow;
1532 			button->visible = 1;
1533 			button->focused = 1;
1534 			button->key = SDL_SCANCODE_ESCAPE;
1535 			button->joykey = joyimpulses[INJOY_MENU_CANCEL];
1536 		}
1537 	}
1538 	else
1539 	{
1540 		ttfPrintText(ttf16, text.x, text.y, language[3959]);
1541 	}
1542 }
1543 
1544 /*-------------------------------------------------------------------------------
1545 
1546 	handleMainMenu
1547 
1548 	draws & processes the game menu; if passed true, does the whole menu,
1549 	otherwise just handles the reduced ingame menu
1550 
1551 -------------------------------------------------------------------------------*/
1552 
handleMainMenu(bool mode)1553 void handleMainMenu(bool mode)
1554 {
1555 	int x, c;
1556 	//int y;
1557 	bool b;
1558 	//int tilesreceived=0;
1559 	//Mix_Music **music, *intromusic, *splashmusic, *creditsmusic;
1560 	node_t* node, *nextnode;
1561 	Entity* entity;
1562 	FILE* fp;
1563 	//SDL_Surface *sky_bmp;
1564 	button_t* button;
1565 
1566 #ifdef STEAMWORKS
1567 	if ( SteamApps()->BIsDlcInstalled(1010820) )
1568 	{
1569 		enabledDLCPack1 = true;
1570 	}
1571 	if ( SteamApps()->BIsDlcInstalled(1010821) )
1572 	{
1573 		enabledDLCPack2 = true;
1574 	}
1575 #else
1576 #endif // STEAMWORKS
1577 	if ( menuOptions.empty() )
1578 	{
1579 		initMenuOptions();
1580 	}
1581 
1582 	if ( !movie )
1583 	{
1584 		// title pic
1585 		SDL_Rect src;
1586 		src.x = 20;
1587 		src.y = 20;
1588 		src.w = title_bmp->w * (230.0 / 240.0); // new banner scaled to old size.
1589 		src.h = title_bmp->h * (230.0 / 240.0);
1590 		if ( mode || introstage != 5 )
1591 		{
1592 			drawImageScaled(title_bmp, nullptr, &src);
1593 		}
1594 		if ( mode && subtitleVisible )
1595 		{
1596 			Uint32 colorYellow = SDL_MapRGBA(mainsurface->format, 255, 255, 0, 255);
1597 			Uint32 len = strlen(language[1910 + subtitleCurrent]);
1598 			ttfPrintTextColor(ttf16, src.x + src.w / 2 - (len * TTF16_WIDTH) / 2, src.y + src.h - 32, colorYellow, true, language[1910 + subtitleCurrent]);
1599 		}
1600 #ifdef STEAMWORKS
1601 		if ( mode )
1602 		{
1603 			// print community links
1604 			if ( SteamUser()->BLoggedOn() )
1605 			{
1606 				if ( ticks % 50 == 0 )
1607 				{
1608 					UIToastNotificationManager.createCommunityNotification();
1609 				}
1610 
1611 				// upgrade steam achievement for existing hunters
1612 				if ( ticks % 250 == 0 )
1613 				{
1614 					bool unlocked = false;
1615 					if ( SteamUserStats()->GetAchievement("BARONY_ACH_GUDIPARIAN_BAZI", &unlocked) )
1616 					{
1617 						if ( unlocked )
1618 						{
1619 							steamAchievement("BARONY_ACH_RANGER_DANGER");
1620 						}
1621 					}
1622 				}
1623 			}
1624 		}
1625 #elif defined USE_EOS
1626 		if ( mode )
1627 		{
1628 			if ( ticks % 50 == 0 )
1629 			{
1630 				UIToastNotificationManager.createCommunityNotification();
1631 			}
1632 		}
1633 #endif
1634 
1635 #ifdef USE_EOS
1636 		if ( mode && EOS.StatGlobalManager.bPromoEnabled )
1637 		{
1638 			if ( ticks % 100 == 0 )
1639 			{
1640 				UIToastNotificationManager.createPromoNotification();
1641 			}
1642 		}
1643 #endif
1644 
1645 		// gray text color
1646 		Uint32 colorGray = SDL_MapRGBA(mainsurface->format, 128, 128, 128, 255);
1647 
1648 		// print game version
1649 		if ( mode || introstage != 5 )
1650 		{
1651 			char version[64];
1652 			strcpy(version, __DATE__ + 7);
1653 			strcat(version, ".");
1654 			if ( !strncmp(__DATE__, "Jan", 3) )
1655 			{
1656 				strcat(version, "01");
1657 			}
1658 			else if ( !strncmp(__DATE__, "Feb", 3) )
1659 			{
1660 				strcat(version, "02");
1661 			}
1662 			else if ( !strncmp(__DATE__, "Mar", 3) )
1663 			{
1664 				strcat(version, "03");
1665 			}
1666 			else if ( !strncmp(__DATE__, "Apr", 3) )
1667 			{
1668 				strcat(version, "04");
1669 			}
1670 			else if ( !strncmp(__DATE__, "May", 3) )
1671 			{
1672 				strcat(version, "05");
1673 			}
1674 			else if ( !strncmp(__DATE__, "Jun", 3) )
1675 			{
1676 				strcat(version, "06");
1677 			}
1678 			else if ( !strncmp(__DATE__, "Jul", 3) )
1679 			{
1680 				strcat(version, "07");
1681 			}
1682 			else if ( !strncmp(__DATE__, "Aug", 3) )
1683 			{
1684 				strcat(version, "08");
1685 			}
1686 			else if ( !strncmp(__DATE__, "Sep", 3) )
1687 			{
1688 				strcat(version, "09");
1689 			}
1690 			else if ( !strncmp(__DATE__, "Oct", 3) )
1691 			{
1692 				strcat(version, "10");
1693 			}
1694 			else if ( !strncmp(__DATE__, "Nov", 3) )
1695 			{
1696 				strcat(version, "11");
1697 			}
1698 			else if ( !strncmp(__DATE__, "Dec", 3) )
1699 			{
1700 				strcat(version, "12");
1701 			}
1702 			strcat(version, ".");
1703 			int day = atoi(__DATE__ + 4);
1704 			if (day >= 10)
1705 			{
1706 				strncat(version, __DATE__ + 4, 2);
1707 			}
1708 			else
1709 			{
1710 				strcat(version, "0");
1711 				strncat(version, __DATE__ + 5, 1);
1712 			}
1713 			int w, h;
1714 			TTF_SizeUTF8(ttf8, version, &w, &h);
1715 			ttfPrintTextFormatted(ttf8, xres - 8 - w, yres - 4 - h, "%s", version);
1716 			int h2 = h;
1717 			TTF_SizeUTF8(ttf8, VERSION, &w, &h);
1718 			ttfPrintTextFormatted(ttf8, xres - 8 - w, yres - 8 - h - h2, VERSION);
1719 			if ( gamemods_numCurrentModsLoaded >= 0 || conductGameChallenges[CONDUCT_MODDED] )
1720 			{
1721 				if ( gamemods_numCurrentModsLoaded >= 0 )
1722 				{
1723 					ttfPrintTextFormatted(ttf8, xres - 8 - TTF8_WIDTH * 16, yres - 12 - h - h2 * 2, "%2d mod(s) loaded", gamemods_numCurrentModsLoaded);
1724 				}
1725 				else if ( !mode )
1726 				{
1727 					ttfPrintTextFormatted(ttf8, xres - 8 - TTF8_WIDTH * 24, yres - 12 - h - h2 * 2, "Using modified map files");
1728 				}
1729 			}
1730 #if (defined STEAMWORKS || defined USE_EOS)
1731 			if ( gamemods_disableSteamAchievements
1732 				|| (intro == false &&
1733 					(conductGameChallenges[CONDUCT_CHEATS_ENABLED]
1734 					|| conductGameChallenges[CONDUCT_LIFESAVING])) )
1735 			{
1736 				TTF_SizeUTF8(ttf8, language[3003], &w, &h);
1737 				if ( gamemods_numCurrentModsLoaded < 0 && !conductGameChallenges[CONDUCT_MODDED] )
1738 				{
1739 					h = -4;
1740 				}
1741 				if ( gameModeManager.getMode() != GameModeManager_t::GAME_MODE_DEFAULT )
1742 				{
1743 					// achievements are disabled
1744 					ttfPrintTextFormatted(ttf8, xres - 8 - w, yres - 16 - h - h2 * 3, language[3003]);
1745 				}
1746 				else
1747 				{
1748 					// achievements are disabled
1749 					ttfPrintTextFormatted(ttf8, xres - 8 - w, yres - 16 - h - h2 * 3, language[3003]);
1750 				}
1751 			}
1752 #endif
1753 
1754 #ifdef STEAMWORKS
1755 			TTF_SizeUTF8(ttf8, language[2549], &w, &h);
1756 			if ( (omousex >= xres - 8 - w && omousex < xres && omousey >= 8 && omousey < 8 + h)
1757 				&& subwindow == 0
1758 				&& introstage == 1
1759 				&& SteamUser()->BLoggedOn() )
1760 			{
1761 				if ( mousestatus[SDL_BUTTON_LEFT] )
1762 				{
1763 					mousestatus[SDL_BUTTON_LEFT] = 0;
1764 					playSound(139, 64);
1765 					SteamAPICall_NumPlayersOnline = SteamUserStats()->GetNumberOfCurrentPlayers();
1766 				}
1767 				ttfPrintTextFormattedColor(ttf8, xres - 8 - w, 8, colorGray, language[2549], steamOnlinePlayers);
1768 			}
1769 			else if ( SteamUser()->BLoggedOn() )
1770 			{
1771 				ttfPrintTextFormatted(ttf8, xres - 8 - w, 8, language[2549], steamOnlinePlayers);
1772 			}
1773 			if ( intro == false )
1774 			{
1775 				if ( conductGameChallenges[CONDUCT_CHEATS_ENABLED] )
1776 				{
1777 					TTF_SizeUTF8(ttf8, language[2986], &w, &h);
1778 					ttfPrintTextFormatted(ttf8, xres - 8 - w, 8 + h, language[2986]);
1779 				}
1780 			}
1781 			if ( SteamUser()->BLoggedOn() && SteamAPICall_NumPlayersOnline == 0 )
1782 			{
1783 				SteamAPICall_NumPlayersOnline = SteamUserStats()->GetNumberOfCurrentPlayers();
1784 			}
1785 			bool bFailed = false;
1786 			if ( SteamUser()->BLoggedOn() )
1787 			{
1788 				SteamUtils()->GetAPICallResult(SteamAPICall_NumPlayersOnline, &NumberOfCurrentPlayers, sizeof(NumberOfCurrentPlayers_t), 1107, &bFailed);
1789 				if ( NumberOfCurrentPlayers.m_bSuccess )
1790 				{
1791 					steamOnlinePlayers = NumberOfCurrentPlayers.m_cPlayers;
1792 				}
1793 				uint64 id = SteamUser()->GetSteamID().ConvertToUint64();
1794 			}
1795 #elif defined USE_EOS
1796 #else
1797 			if ( intro && introstage == 1 )
1798 			{
1799 				TTF_SizeUTF8(ttf8, language[3402], &w, &h);
1800 				if ( (omousex >= xres - 8 - w && omousex < xres && omousey >= 8 && omousey < 8 + h)
1801 					&& subwindow == 0 )
1802 				{
1803 					if ( mousestatus[SDL_BUTTON_LEFT] )
1804 					{
1805 						mousestatus[SDL_BUTTON_LEFT] = 0;
1806 						playSound(139, 64);
1807 						windowEnterSerialPrompt();
1808 
1809 					}
1810 					ttfPrintTextFormattedColor(ttf8, xres - 8 - w, 8, colorGray, language[3402]);
1811 				}
1812 				else
1813 				{
1814 					ttfPrintTextFormatted(ttf8, xres - 8 - w, 8, language[3402]);
1815 				}
1816 			}
1817 #endif // STEAMWORKS
1818 		}
1819 		// navigate with arrow keys
1820 		if (!subwindow)
1821 		{
1822 			navigateMainMenuItems(mode);
1823 		}
1824 
1825 		// draw menu
1826 		if ( mode )
1827 		{
1828 			/*
1829 			 * Mouse menu item select/highlight implicitly handled here.
1830 			 */
1831 			if ( keystatus[SDL_SCANCODE_L] && (keystatus[SDL_SCANCODE_LCTRL] || keystatus[SDL_SCANCODE_RCTRL]) )
1832 			{
1833 				buttonOpenCharacterCreationWindow(nullptr);
1834 				client_classes[clientnum] = CLASS_BARBARIAN;
1835 				stats[0]->appearance = 0;
1836 				stats[0]->playerRace = RACE_HUMAN;
1837 				initClass(0);
1838 				strcpy(stats[0]->name, "The Server");
1839 				keystatus[SDL_SCANCODE_L] = 0;
1840 				keystatus[SDL_SCANCODE_LCTRL] = 0;
1841 				keystatus[SDL_SCANCODE_RCTRL] = 0;
1842 				multiplayerselect = SERVER;
1843 				charcreation_step = 6;
1844 				camera_charsheet_offsetyaw = (330) * PI / 180;
1845 				directConnect = true;
1846 				strcpy(portnumber_char, "12345");
1847 				buttonHostLobby(nullptr);
1848 			}
1849 
1850 			if ( keystatus[SDL_SCANCODE_M] && (keystatus[SDL_SCANCODE_LCTRL] || keystatus[SDL_SCANCODE_RCTRL]) )
1851 			{
1852 				buttonOpenCharacterCreationWindow(nullptr);
1853 				client_classes[clientnum] = CLASS_BARBARIAN;
1854 				stats[0]->appearance = 0;
1855 				stats[0]->playerRace = RACE_HUMAN;
1856 				initClass(0);
1857 				strcpy(stats[0]->name, "The Client");
1858 				keystatus[SDL_SCANCODE_M] = 0;
1859 				keystatus[SDL_SCANCODE_LCTRL] = 0;
1860 				keystatus[SDL_SCANCODE_RCTRL] = 0;
1861 				multiplayerselect = CLIENT;
1862 				charcreation_step = 6;
1863 				camera_charsheet_offsetyaw = (330) * PI / 180;
1864 				directConnect = true;
1865 				strcpy(connectaddress, "localhost:12345");
1866 				buttonJoinLobby(nullptr);
1867 			}
1868 
1869 			bool mainMenuSelectInputIsPressed = (mousestatus[SDL_BUTTON_LEFT] || keystatus[SDL_SCANCODE_RETURN] || (*inputPressed(joyimpulses[INJOY_MENU_NEXT]) && rebindaction == -1));
1870 
1871 			//"Start Game" button.
1872 			SDL_Rect text;
1873 			text.x = 50;
1874 			text.h = 18;
1875 			text.w = 18;
1876 
1877 			const Uint32 numMenuOptions = menuOptions.size();
1878 			Uint32 menuIndex = 0;
1879 			text.y = yres / 4 + 80 + (menuOptions.at(menuIndex).second - 1) * 24;
1880 			Uint32 menuOptionSize = std::max(static_cast<Uint32>(menuOptions.at(menuIndex).first.size()), static_cast<Uint32>(4));
1881 
1882 			if ( ((omousex >= text.x && omousex < text.x + menuOptionSize * text.w && omousey >= text.y && omousey < text.y + text.h) || (menuselect == menuOptions.at(menuIndex).second)) && subwindow == 0 && introstage == 1 )
1883 			{
1884 				menuselect = menuOptions.at(menuIndex).second;
1885 				ttfPrintTextFormattedColor(ttf16, text.x, text.y, colorGray, "%s", menuOptions.at(menuIndex).first.c_str());
1886 				if ( mainMenuSelectInputIsPressed )
1887 				{
1888 					pauseMenuOnInputPressed();
1889 
1890 					// look for a save game
1891 					bool reloadModels = false;
1892 					int modelsIndexUpdateStart = 1;
1893 					int modelsIndexUpdateEnd = nummodels;
1894 
1895 					bool reloadSounds = false;
1896 					if ( gamemods_customContentLoadedFirstTime )
1897 					{
1898 						if ( physfsSearchModelsToUpdate() || !gamemods_modelsListModifiedIndexes.empty() )
1899 						{
1900 							reloadModels = true; // we had some models already loaded which should be reset
1901 						}
1902 						if ( physfsSearchSoundsToUpdate() )
1903 						{
1904 							reloadSounds = true; // we had some sounds already loaded which should be reset
1905 						}
1906 					}
1907 
1908 					gamemodsClearAllMountedPaths();
1909 
1910 					if ( reloadModels )
1911 					{
1912 						// print a loading message
1913 						drawClearBuffers();
1914 						int w, h;
1915 						TTF_SizeUTF8(ttf16, language[2990], &w, &h);
1916 						ttfPrintText(ttf16, (xres - w) / 2, (yres - h) / 2, language[2990]);
1917 						GO_SwapBuffers(screen);
1918 
1919 						physfsModelIndexUpdate(modelsIndexUpdateStart, modelsIndexUpdateEnd, true);
1920 						generatePolyModels(modelsIndexUpdateStart, modelsIndexUpdateEnd, false);
1921 						gamemods_modelsListLastStartedUnmodded = true;
1922 					}
1923 					if ( reloadSounds )
1924 					{
1925 						// print a loading message
1926 						drawClearBuffers();
1927 						int w, h;
1928 						TTF_SizeUTF8(ttf16, language[2988], &w, &h);
1929 						ttfPrintText(ttf16, (xres - w) / 2, (yres - h) / 2, language[2988]);
1930 						GO_SwapBuffers(screen);
1931 						physfsReloadSounds(true);
1932 						gamemods_soundsListLastStartedUnmodded = true;
1933 					}
1934 
1935 					if ( gamemods_tileListRequireReloadUnmodded )
1936 					{
1937 						drawClearBuffers();
1938 						int w, h;
1939 						TTF_SizeUTF8(ttf16, language[3018], &w, &h);
1940 						ttfPrintText(ttf16, (xres - w) / 2, (yres - h) / 2, language[3018]);
1941 						GO_SwapBuffers(screen);
1942 						physfsReloadTiles(true);
1943 						gamemods_tileListRequireReloadUnmodded = false;
1944 					}
1945 
1946 					if ( gamemods_booksRequireReloadUnmodded )
1947 					{
1948 						drawClearBuffers();
1949 						int w, h;
1950 						TTF_SizeUTF8(ttf16, language[2992], &w, &h);
1951 						ttfPrintText(ttf16, (xres - w) / 2, (yres - h) / 2, language[2992]);
1952 						GO_SwapBuffers(screen);
1953 						physfsReloadBooks();
1954 						gamemods_booksRequireReloadUnmodded = false;
1955 					}
1956 
1957 					if ( gamemods_musicRequireReloadUnmodded )
1958 					{
1959 						gamemodsUnloadCustomThemeMusic();
1960 						drawClearBuffers();
1961 						int w, h;
1962 						TTF_SizeUTF8(ttf16, language[2994], &w, &h);
1963 						ttfPrintText(ttf16, (xres - w) / 2, (yres - h) / 2, language[2994]);
1964 						GO_SwapBuffers(screen);
1965 						bool reloadIntroMusic = false;
1966 						physfsReloadMusic(reloadIntroMusic, true);
1967 						if ( reloadIntroMusic )
1968 						{
1969 #ifdef SOUND
1970 							playmusic(intromusic[rand() % (NUMINTROMUSIC - 1)], false, true, true);
1971 #endif
1972 						}
1973 						gamemods_musicRequireReloadUnmodded = false;
1974 					}
1975 
1976 					if ( gamemods_langRequireReloadUnmodded )
1977 					{
1978 						drawClearBuffers();
1979 						int w, h;
1980 						TTF_SizeUTF8(ttf16, language[3005], &w, &h);
1981 						ttfPrintText(ttf16, (xres - w) / 2, (yres - h) / 2, language[3005]);
1982 						GO_SwapBuffers(screen);
1983 						reloadLanguage();
1984 						gamemods_langRequireReloadUnmodded = false;
1985 					}
1986 
1987 					if ( gamemods_itemsTxtRequireReloadUnmodded )
1988 					{
1989 						drawClearBuffers();
1990 						int w, h;
1991 						TTF_SizeUTF8(ttf16, language[3009], &w, &h);
1992 						ttfPrintText(ttf16, (xres - w) / 2, (yres - h) / 2, language[3009]);
1993 						GO_SwapBuffers(screen);
1994 						physfsReloadItemsTxt();
1995 						gamemods_itemsTxtRequireReloadUnmodded = false;
1996 					}
1997 
1998 					if ( gamemods_itemSpritesRequireReloadUnmodded )
1999 					{
2000 						drawClearBuffers();
2001 						int w, h;
2002 						TTF_SizeUTF8(ttf16, language[3007], &w, &h);
2003 						ttfPrintText(ttf16, (xres - w) / 2, (yres - h) / 2, language[3007]);
2004 						GO_SwapBuffers(screen);
2005 						physfsReloadItemSprites(true);
2006 						gamemods_itemSpritesRequireReloadUnmodded = false;
2007 					}
2008 
2009 					if ( gamemods_spriteImagesRequireReloadUnmodded )
2010 					{
2011 						drawClearBuffers();
2012 						int w, h;
2013 						TTF_SizeUTF8(ttf16, language[3016], &w, &h);
2014 						ttfPrintText(ttf16, (xres - w) / 2, (yres - h) / 2, language[3016]);
2015 						GO_SwapBuffers(screen);
2016 						physfsReloadSprites(true);
2017 						gamemods_spriteImagesRequireReloadUnmodded = false;
2018 					}
2019 
2020 					if ( gamemods_itemsGlobalTxtRequireReloadUnmodded )
2021 					{
2022 						gamemods_itemsGlobalTxtRequireReloadUnmodded = false;
2023 						printlog("[PhysFS]: Unloaded modified items/items_global.txt file, reloading item spawn levels...");
2024 						loadItemLists();
2025 					}
2026 
2027 					if ( gamemods_monsterLimbsRequireReloadUnmodded )
2028 					{
2029 						drawClearBuffers();
2030 						int w, h;
2031 						TTF_SizeUTF8(ttf16, language[3014], &w, &h);
2032 						ttfPrintText(ttf16, (xres - w) / 2, (yres - h) / 2, language[3014]);
2033 						GO_SwapBuffers(screen);
2034 						physfsReloadMonsterLimbFiles();
2035 						gamemods_monsterLimbsRequireReloadUnmodded = false;
2036 					}
2037 
2038 					if ( gamemods_systemImagesReloadUnmodded )
2039 					{
2040 						drawClearBuffers();
2041 						int w, h;
2042 						TTF_SizeUTF8(ttf16, language[3016], &w, &h);
2043 						ttfPrintText(ttf16, (xres - w) / 2, (yres - h) / 2, language[3016]);
2044 						GO_SwapBuffers(screen);
2045 						physfsReloadSystemImages();
2046 						gamemods_systemImagesReloadUnmodded = false;
2047 						systemResourceImagesToReload.clear();
2048 
2049 						// tidy up some other resource files.
2050 						rightsidebar_titlebar_img = spell_list_titlebar_bmp;
2051 						rightsidebar_slot_img = spell_list_gui_slot_bmp;
2052 						rightsidebar_slot_highlighted_img = spell_list_gui_slot_highlighted_bmp;
2053 					}
2054 
2055 					gamemods_disableSteamAchievements = false;
2056 
2057 					if ( gameModeManager.Tutorial.FirstTimePrompt.showFirstTimePrompt )
2058 					{
2059 						gameModeManager.Tutorial.FirstTimePrompt.createPrompt();
2060 					}
2061 					else
2062 					{
2063 						if ( anySaveFileExists() )
2064 						{
2065 							//openLoadGameWindow(NULL);
2066 							openNewLoadGameWindow(nullptr);
2067 						}
2068 						else
2069 						{
2070 							buttonOpenCharacterCreationWindow(NULL);
2071 						}
2072 					}
2073 				}
2074 			}
2075 			else
2076 			{
2077 				ttfPrintText(ttf16, text.x, text.y, menuOptions.at(menuIndex).first.c_str());
2078 			}
2079 
2080 			++menuIndex;
2081 			text.y = yres / 4 + 80 + (menuOptions.at(menuIndex).second - 1) * 24;
2082 			menuOptionSize = std::max(static_cast<Uint32>(menuOptions.at(menuIndex).first.size()), static_cast<Uint32>(4));
2083 
2084 			//"Introduction" button.
2085 			if ( ((omousex >= text.x && omousex < text.x + menuOptionSize * text.w && omousey >= text.y && omousey < text.y + text.h) || (menuselect == menuOptions.at(menuIndex).second)) && subwindow == 0 && introstage == 1 )
2086 			{
2087 				menuselect = menuOptions.at(menuIndex).second;
2088 				ttfPrintTextFormattedColor(ttf16, text.x, text.y, colorGray, "%s", menuOptions.at(menuIndex).first.c_str());
2089 				if ( mainMenuSelectInputIsPressed )
2090 				{
2091 					pauseMenuOnInputPressed();
2092 
2093 					introstage = 6; // goes to intro movie
2094 					fadeout = true;
2095 #ifdef MUSIC
2096 					playmusic(introductionmusic, true, true, false);
2097 #endif
2098 				}
2099 			}
2100 			else
2101 			{
2102 				ttfPrintText(ttf16, text.x, text.y, menuOptions.at(menuIndex).first.c_str());
2103 			}
2104 
2105 			++menuIndex;
2106 			text.y = yres / 4 + 80 + (menuOptions.at(menuIndex).second - 1) * 24;
2107 			menuOptionSize = std::max(static_cast<Uint32>(menuOptions.at(menuIndex).first.size()), static_cast<Uint32>(4));
2108 
2109 			//"Hall of Trials" Button.
2110 			if ( ((omousex >= text.x && omousex < text.x + menuOptionSize * text.w && omousey >= text.y && omousey < text.y + text.h) || (menuselect == menuOptions.at(menuIndex).second)) && subwindow == 0 && introstage == 1 )
2111 			{
2112 				menuselect = menuOptions.at(menuIndex).second;
2113 				ttfPrintTextFormattedColor(ttf16, text.x, text.y, colorGray, "%s", menuOptions.at(menuIndex).first.c_str());
2114 				if ( mainMenuSelectInputIsPressed )
2115 				{
2116 					pauseMenuOnInputPressed();
2117 
2118 					gameModeManager.Tutorial.readFromFile();
2119 					gameModeManager.Tutorial.Menu.open();
2120 				}
2121 			}
2122 			else
2123 			{
2124 				ttfPrintText(ttf16, text.x, text.y, menuOptions.at(menuIndex).first.c_str());
2125 			}
2126 
2127 			++menuIndex;
2128 			text.y = yres / 4 + 80 + (menuOptions.at(menuIndex).second - 1) * 24;
2129 			menuOptionSize = std::max(static_cast<Uint32>(menuOptions.at(menuIndex).first.size()), static_cast<Uint32>(4));
2130 
2131 			//"Statistics" Button.
2132 			if ( ((omousex >= text.x && omousex < text.x + menuOptionSize * text.w && omousey >= text.y && omousey < text.y + text.h) || (menuselect == menuOptions.at(menuIndex).second)) && subwindow == 0 && introstage == 1 )
2133 			{
2134 				menuselect = menuOptions.at(menuIndex).second;
2135 				ttfPrintTextFormattedColor(ttf16, text.x, text.y, colorGray, "%s", menuOptions.at(menuIndex).first.c_str());
2136 				if ( mainMenuSelectInputIsPressed )
2137 				{
2138 					pauseMenuOnInputPressed();
2139 
2140 					buttonOpenScoresWindow(nullptr);
2141 				}
2142 			}
2143 			else
2144 			{
2145 				ttfPrintText(ttf16, text.x, text.y, menuOptions.at(menuIndex).first.c_str());
2146 			}
2147 
2148 #if (defined USE_EOS && !defined STEAMWORKS)
2149 			++menuIndex;
2150 			text.y = yres / 4 + 80 + (menuOptions.at(menuIndex).second - 1) * 24;
2151 			menuOptionSize = std::max(static_cast<Uint32>(menuOptions.at(menuIndex).first.size()), static_cast<Uint32>(4));
2152 
2153 			//"Achievements" Button.
2154 			if ( ((omousex >= text.x && omousex < text.x + menuOptionSize * text.w && omousey >= text.y && omousey < text.y + text.h) || (menuselect == menuOptions.at(menuIndex).second)) && subwindow == 0 && introstage == 1 )
2155 			{
2156 				menuselect = menuOptions.at(menuIndex).second;
2157 				ttfPrintTextFormattedColor(ttf16, text.x, text.y, colorGray, "%s", menuOptions.at(menuIndex).first.c_str());
2158 				if ( mainMenuSelectInputIsPressed )
2159 				{
2160 					pauseMenuOnInputPressed();
2161 
2162 					openAchievementsWindow();
2163 				}
2164 			}
2165 			else
2166 			{
2167 				ttfPrintText(ttf16, text.x, text.y, menuOptions.at(menuIndex).first.c_str());
2168 			}
2169 #endif
2170 
2171 			++menuIndex;
2172 			text.y = yres / 4 + 80 + (menuOptions.at(menuIndex).second - 1) * 24;
2173 			menuOptionSize = std::max(static_cast<Uint32>(menuOptions.at(menuIndex).first.size()), static_cast<Uint32>(4));
2174 
2175 			//"Settings" button.
2176 			if ( ((omousex >= text.x && omousex < text.x + menuOptionSize * text.w && omousey >= text.y && omousey < text.y + text.h) || (menuselect == menuOptions.at(menuIndex).second)) && subwindow == 0 && introstage == 1 )
2177 			{
2178 				menuselect = menuOptions.at(menuIndex).second;
2179 				ttfPrintTextFormattedColor(ttf16, text.x, text.y, colorGray, "%s", menuOptions.at(menuIndex).first.c_str());
2180 				if ( mainMenuSelectInputIsPressed )
2181 				{
2182 					pauseMenuOnInputPressed();
2183 					openSettingsWindow();
2184 				}
2185 			}
2186 			else
2187 			{
2188 				ttfPrintText(ttf16, text.x, text.y, menuOptions.at(menuIndex).first.c_str());
2189 			}
2190 
2191 			++menuIndex;
2192 			text.y = yres / 4 + 80 + (menuOptions.at(menuIndex).second - 1) * 24;
2193 			menuOptionSize = std::max(static_cast<Uint32>(menuOptions.at(menuIndex).first.size()), static_cast<Uint32>(4));
2194 
2195 			//"Credits" button
2196 			if ( ((omousex >= text.x && omousex < text.x + menuOptionSize * text.w && omousey >= text.y && omousey < text.y + text.h) || (menuselect == menuOptions.at(menuIndex).second)) && subwindow == 0 && introstage == 1 )
2197 			{
2198 				menuselect = menuOptions.at(menuIndex).second;
2199 				ttfPrintTextFormattedColor(ttf16, text.x, text.y, colorGray, "%s", menuOptions.at(menuIndex).first.c_str());
2200 				if ( mainMenuSelectInputIsPressed )
2201 				{
2202 					pauseMenuOnInputPressed();
2203 					introstage = 4; // goes to credits
2204 					fadeout = true;
2205 				}
2206 			}
2207 			else
2208 			{
2209 				ttfPrintText(ttf16, text.x, text.y, menuOptions.at(menuIndex).first.c_str());
2210 			}
2211 
2212 			++menuIndex;
2213 			text.y = yres / 4 + 80 + (menuOptions.at(menuIndex).second - 1) * 24;
2214 			menuOptionSize = std::max(static_cast<Uint32>(menuOptions.at(menuIndex).first.size()), static_cast<Uint32>(4));
2215 
2216 			//"Custom content" button.
2217 			if ( ((omousex >= text.x && omousex < text.x + menuOptionSize * text.w && omousey >= text.y && omousey < text.y + text.h) || (menuselect == menuOptions.at(menuIndex).second)) && subwindow == 0 && introstage == 1 )
2218 			{
2219 				menuselect = menuOptions.at(menuIndex).second;
2220 				ttfPrintTextFormattedColor(ttf16, text.x, text.y, colorGray, "%s", menuOptions.at(menuIndex).first.c_str());
2221 				if ( mainMenuSelectInputIsPressed )
2222 				{
2223 					pauseMenuOnInputPressed();
2224 					gamemodsCustomContentInit();
2225 				}
2226 			}
2227 			else
2228 			{
2229 				ttfPrintText(ttf16, text.x, text.y, menuOptions.at(menuIndex).first.c_str());
2230 			}
2231 #ifdef STEAMWORKS
2232 			++menuIndex;
2233 			text.y = yres / 4 + 80 + (menuOptions.at(menuIndex).second - 1) * 24;
2234 			menuOptionSize = std::max(static_cast<Uint32>(menuOptions.at(menuIndex).first.size()), static_cast<Uint32>(4));
2235 
2236 			if ( ((omousex >= text.x && omousex < text.x + menuOptionSize * text.w && omousey >= text.y && omousey < text.y + text.h) || (menuselect == menuOptions.at(menuIndex).second)) && subwindow == 0 && introstage == 1 )
2237 			{
2238 				menuselect = menuOptions.at(menuIndex).second;
2239 				ttfPrintTextFormattedColor(ttf16, text.x, text.y, colorGray, "%s", menuOptions.at(menuIndex).first.c_str());
2240 				if ( mainMenuSelectInputIsPressed )
2241 				{
2242 					pauseMenuOnInputPressed();
2243 					gamemodsSubscribedItemsInit();
2244 				}
2245 			}
2246 			else
2247 			{
2248 				ttfPrintText(ttf16, text.x, text.y, menuOptions.at(menuIndex).first.c_str());
2249 			}
2250 #endif
2251 			++menuIndex;
2252 			text.y = yres / 4 + 80 + (menuOptions.at(menuIndex).second - 1) * 24;
2253 			menuOptionSize = std::max(static_cast<Uint32>(menuOptions.at(menuIndex).first.size()), static_cast<Uint32>(4));
2254 
2255 			//"Quit" button.
2256 			if ( ((omousex >= text.x && omousex < text.x + menuOptionSize * text.w && omousey >= text.y && omousey < text.y + text.h) || (menuselect == menuOptions.at(menuIndex).second)) && subwindow == 0 && introstage == 1 )
2257 			{
2258 				menuselect = menuOptions.at(menuIndex).second;
2259 				ttfPrintTextFormattedColor(ttf16, text.x, text.y, colorGray, "%s", menuOptions.at(menuIndex).first.c_str());
2260 				if ( mainMenuSelectInputIsPressed )
2261 				{
2262 					pauseMenuOnInputPressed();
2263 
2264 					// create confirmation window
2265 					subwindow = 1;
2266 					subx1 = xres / 2 - 128;
2267 					subx2 = xres / 2 + 128;
2268 					suby1 = yres / 2 - 40;
2269 					suby2 = yres / 2 + 40;
2270 					strcpy(subtext, language[1128]);
2271 
2272 					// close button
2273 					button = newButton();
2274 					strcpy(button->label, "x");
2275 					button->x = subx2 - 20;
2276 					button->y = suby1;
2277 					button->sizex = 20;
2278 					button->sizey = 20;
2279 					button->action = &buttonCloseSubwindow;
2280 					button->visible = 1;
2281 					button->focused = 1;
2282 					button->key = SDL_SCANCODE_ESCAPE;
2283 					button->joykey = joyimpulses[INJOY_MENU_CANCEL];
2284 
2285 					// yes button
2286 					button = newButton();
2287 					strcpy(button->label, language[1314]);
2288 					button->x = subx1 + 8;
2289 					button->y = suby2 - 28;
2290 					button->sizex = strlen(language[1314]) * 12 + 8;
2291 					button->sizey = 20;
2292 					button->action = &buttonQuitConfirm;
2293 					button->visible = 1;
2294 					button->focused = 1;
2295 					button->key = SDL_SCANCODE_RETURN;
2296 					button->joykey = joyimpulses[INJOY_MENU_NEXT];
2297 
2298 					// no button
2299 					button = newButton();
2300 					strcpy(button->label, language[1315]);
2301 					button->x = subx2 - strlen(language[1315]) * 12 - 16;
2302 					button->y = suby2 - 28;
2303 					button->sizex = strlen(language[1315]) * 12 + 8;
2304 					button->sizey = 20;
2305 					button->action = &buttonCloseSubwindow;
2306 					button->visible = 1;
2307 					button->focused = 1;
2308 				}
2309 			}
2310 			else
2311 			{
2312 				ttfPrintText(ttf16, text.x, text.y, menuOptions.at(menuIndex).first.c_str());
2313 			}
2314 		}
2315 		else
2316 		{
2317 			if ( introstage != 5 )
2318 			{
2319 				if ( gameModeManager.getMode() == GameModeManager_t::GAME_MODE_DEFAULT )
2320 				{
2321 					handleInGamePauseMenu();
2322 				}
2323 				else if ( gameModeManager.getMode() == GameModeManager_t::GAME_MODE_TUTORIAL )
2324 				{
2325 					handleTutorialPauseMenu();
2326 				}
2327 			}
2328 		}
2329 
2330 		LobbyHandler.handleLobbyListRequests();
2331 
2332 		//Confirm Resolution Change Window
2333 		if ( confirmResolutionWindow )
2334 		{
2335 			subx1 = xres / 2 - 128;
2336 			subx2 = xres / 2 + 128;
2337 			suby1 = yres / 2 - 40;
2338 			suby2 = yres / 2 + 40;
2339 			drawWindowFancy(subx1, suby1, subx2, suby2);
2340 
2341 			if ( SDL_GetTicks() >= resolutionConfirmationTimer + RESOLUTION_CONFIRMATION_TIME )
2342 			{
2343 				//Automatically revert.
2344 				buttonRevertResolution(revertResolutionButton);
2345 			}
2346 		}
2347 
2348 		// draw subwindow
2349 		if ( subwindow )
2350 		{
2351 			drawWindowFancy(subx1, suby1, subx2, suby2);
2352 			if ( loadGameSaveShowRectangle > 0 )
2353 			{
2354 				SDL_Rect saveBox;
2355 				saveBox.x = subx1 + 4;
2356 				saveBox.y = suby1 + TTF12_HEIGHT * 2;
2357 				saveBox.w = subx2 - subx1 - 8;
2358 				saveBox.h = TTF12_HEIGHT * 3;
2359 				drawWindowFancy(saveBox.x, saveBox.y, saveBox.x + saveBox.w, saveBox.y + saveBox.h);
2360 				if ( gamemods_numCurrentModsLoaded >= 0 )
2361 				{
2362 					drawRect(&saveBox, uint32ColorGreen(*mainsurface), 32);
2363 				}
2364 				else
2365 				{
2366 					drawRect(&saveBox, uint32ColorBaronyBlue(*mainsurface), 32);
2367 				}
2368 				if ( loadGameSaveShowRectangle == 2 )
2369 				{
2370 					saveBox.y = suby1 + TTF12_HEIGHT * 5 + 2;
2371 					//drawTooltip(&saveBox);
2372 					drawWindowFancy(saveBox.x, saveBox.y, saveBox.x + saveBox.w, saveBox.y + saveBox.h);
2373 					if ( gamemods_numCurrentModsLoaded >= 0 )
2374 					{
2375 						drawRect(&saveBox, uint32ColorGreen(*mainsurface), 32);
2376 					}
2377 					else
2378 					{
2379 						drawRect(&saveBox, uint32ColorBaronyBlue(*mainsurface), 32);
2380 					}
2381 				}
2382 			}
2383 			if ( gamemods_window == 1 || gamemods_window == 2 || gamemods_window == 5 )
2384 			{
2385 				drawWindowFancy(subx1 + 4, suby1 + 44 + 10 * TTF12_HEIGHT,
2386 					subx2 - 4, suby2 - 4);
2387 			}
2388 			if ( subtext != NULL )
2389 			{
2390 				if ( strncmp(subtext, language[740], 12) )
2391 				{
2392 					ttfPrintTextFormatted(ttf12, subx1 + 8, suby1 + 8, subtext);
2393 				}
2394 				else
2395 				{
2396 					ttfPrintTextFormatted(ttf16, subx1 + 8, suby1 + 8, subtext);
2397 				}
2398 			}
2399 			if ( loadGameSaveShowRectangle > 0 && gamemods_numCurrentModsLoaded >= 0 )
2400 			{
2401 				ttfPrintTextFormattedColor(ttf12, subx1 + 8, suby2 - TTF12_HEIGHT * 5, uint32ColorBaronyBlue(*mainsurface), "%s", language[2982]);
2402 			}
2403 		}
2404 		else
2405 		{
2406 			loadGameSaveShowRectangle = 0;
2407 		}
2408 
2409 		LobbyHandler.drawLobbyFilters();
2410 
2411 		// process button actions
2412 		handleButtons();
2413 
2414 		LobbyHandler.handleLobbyBrowser();
2415 	}
2416 
2417 	// character creation screen
2418 	if ( charcreation_step >= 1 && charcreation_step < 6 )
2419 	{
2420 		if ( gamemods_numCurrentModsLoaded >= 0 )
2421 		{
2422 			ttfPrintText(ttf16, subx1 + 8, suby1 + 8, language[2980]);
2423 		}
2424 		else
2425 		{
2426 			ttfPrintText(ttf16, subx1 + 8, suby1 + 8, language[1318]);
2427 		}
2428 
2429 		// draw character window
2430 		if (players[clientnum] != nullptr && players[clientnum]->entity != nullptr)
2431 		{
2432 			camera_charsheet.x = players[clientnum]->entity->x / 16.0 + 1.118 * cos(camera_charsheet_offsetyaw); // + 1
2433 			camera_charsheet.y = players[clientnum]->entity->y / 16.0 + 1.118 * sin(camera_charsheet_offsetyaw); // -.5
2434 			if ( !stats[clientnum]->EFFECTS[EFF_ASLEEP] )
2435 			{
2436 				camera_charsheet.z = players[clientnum]->entity->z * 2;
2437 			}
2438 			else
2439 			{
2440 				camera_charsheet.z = 1.5;
2441 			}
2442 			camera_charsheet.ang = atan2(players[clientnum]->entity->y / 16.0 - camera_charsheet.y, players[clientnum]->entity->x / 16.0 - camera_charsheet.x);
2443 			camera_charsheet.vang = PI / 24;
2444 			camera_charsheet.winw = 360;
2445 			camera_charsheet.winy = suby1 + 32;
2446 			camera_charsheet.winh = suby2 - 96 - camera_charsheet.winy;
2447 			camera_charsheet.winx = subx2 - camera_charsheet.winw - 32;
2448 			SDL_Rect pos;
2449 			pos.x = camera_charsheet.winx;
2450 			pos.y = camera_charsheet.winy;
2451 			pos.w = camera_charsheet.winw;
2452 			pos.h = camera_charsheet.winh;
2453 			drawRect(&pos, 0, 255);
2454 			b = players[clientnum]->entity->flags[BRIGHT];
2455 			players[clientnum]->entity->flags[BRIGHT] = true;
2456 			if (!playing_random_char)
2457 			{
2458 				if ( !players[clientnum]->entity->flags[INVISIBLE] )
2459 				{
2460 					real_t ofov = fov;
2461 					fov = 50;
2462 					glDrawVoxel(&camera_charsheet, players[clientnum]->entity, REALCOLORS);
2463 					fov = ofov;
2464 				}
2465 				players[clientnum]->entity->flags[BRIGHT] = b;
2466 				c = 0;
2467 				for ( node = players[clientnum]->entity->children.first; node != NULL; node = node->next )
2468 				{
2469 					if ( c == 0 )
2470 					{
2471 						c++;
2472 					}
2473 					entity = (Entity*) node->element;
2474 					if ( !entity->flags[INVISIBLE] )
2475 					{
2476 						b = entity->flags[BRIGHT];
2477 						entity->flags[BRIGHT] = true;
2478 						real_t ofov = fov;
2479 						fov = 50;
2480 						glDrawVoxel(&camera_charsheet, entity, REALCOLORS);
2481 						fov = ofov;
2482 						entity->flags[BRIGHT] = b;
2483 					}
2484 					c++;
2485 				}
2486 			}
2487 			SDL_Rect rotateBtn;
2488 			rotateBtn.w = 24;
2489 			rotateBtn.h = 24;
2490 			rotateBtn.x = camera_charsheet.winx + camera_charsheet.winw - rotateBtn.w;
2491 			rotateBtn.y = camera_charsheet.winy + camera_charsheet.winh - rotateBtn.h;
2492 			drawWindow(rotateBtn.x, rotateBtn.y, rotateBtn.x + rotateBtn.w, rotateBtn.y + rotateBtn.h);
2493 			if ( mouseInBounds(rotateBtn.x, rotateBtn.x + rotateBtn.w, rotateBtn.y, rotateBtn.y + rotateBtn.h) )
2494 			{
2495 				if ( mousestatus[SDL_BUTTON_LEFT] )
2496 				{
2497 					camera_charsheet_offsetyaw += 0.05;
2498 					if ( camera_charsheet_offsetyaw > 2 * PI )
2499 					{
2500 						camera_charsheet_offsetyaw -= 2 * PI;
2501 					}
2502 					drawDepressed(rotateBtn.x, rotateBtn.y, rotateBtn.x + rotateBtn.w, rotateBtn.y + rotateBtn.h);
2503 				}
2504 			}
2505 			ttfPrintText(ttf12, rotateBtn.x + 4, rotateBtn.y + 6, ">");
2506 
2507 			rotateBtn.x = camera_charsheet.winx + camera_charsheet.winw - rotateBtn.w * 2 - 4;
2508 			rotateBtn.y = camera_charsheet.winy + camera_charsheet.winh - rotateBtn.h;
2509 			drawWindow(rotateBtn.x, rotateBtn.y, rotateBtn.x + rotateBtn.w, rotateBtn.y + rotateBtn.h);
2510 			if ( mouseInBounds(rotateBtn.x, rotateBtn.x + rotateBtn.w, rotateBtn.y, rotateBtn.y + rotateBtn.h) )
2511 			{
2512 				if ( mousestatus[SDL_BUTTON_LEFT] )
2513 				{
2514 					camera_charsheet_offsetyaw -= 0.05;
2515 					if ( camera_charsheet_offsetyaw < 0.f )
2516 					{
2517 						camera_charsheet_offsetyaw += 2 * PI;
2518 					}
2519 					drawDepressed(rotateBtn.x, rotateBtn.y, rotateBtn.x + rotateBtn.w, rotateBtn.y + rotateBtn.h);
2520 				}
2521 			}
2522 			ttfPrintText(ttf12, rotateBtn.x + 4, rotateBtn.y + 6, "<");
2523 
2524 			SDL_Rect raceInfoBtn;
2525 			raceInfoBtn.y = rotateBtn.y;
2526 			raceInfoBtn.w = longestline(language[3373]) * TTF12_WIDTH + 8 + 4;
2527 			raceInfoBtn.x = rotateBtn.x - raceInfoBtn.w - 4;
2528 			raceInfoBtn.h = rotateBtn.h;
2529 			drawWindow(raceInfoBtn.x, raceInfoBtn.y, raceInfoBtn.x + raceInfoBtn.w, raceInfoBtn.y + raceInfoBtn.h);
2530 			if ( mouseInBounds(raceInfoBtn.x, raceInfoBtn.x + raceInfoBtn.w, raceInfoBtn.y, raceInfoBtn.y + raceInfoBtn.h) )
2531 			{
2532 				if ( *inputPressed(joyimpulses[INJOY_MENU_LEFT_CLICK]) || mousestatus[SDL_BUTTON_LEFT] )
2533 				{
2534 					//drawDepressed(raceInfoBtn.x, raceInfoBtn.y, raceInfoBtn.x + raceInfoBtn.w, raceInfoBtn.y + raceInfoBtn.h);
2535 					mousestatus[SDL_BUTTON_LEFT] = 0;
2536 					*inputPressed(joyimpulses[INJOY_MENU_LEFT_CLICK]) = 0;
2537 					showRaceInfo = !showRaceInfo;
2538 					playSound(139, 64);
2539 				}
2540 			}
2541 			if ( showRaceInfo )
2542 			{
2543 				pos.y += 2;
2544 				pos.h -= raceInfoBtn.h + 6;
2545 				pos.x += 2;
2546 				pos.w -= 6;
2547 				drawRect(&pos, 0, 168);
2548 				drawLine(pos.x, pos.y, pos.x + pos.w, pos.y, SDL_MapRGB(mainsurface->format, 0, 192, 255), 255);
2549 				drawLine(pos.x, pos.y + pos.h, pos.x + pos.w, pos.y + pos.h, SDL_MapRGB(mainsurface->format, 0, 192, 255), 255);
2550 				drawLine(pos.x, pos.y, pos.x, pos.y + pos.h, SDL_MapRGB(mainsurface->format, 0, 192, 255), 255);
2551 				drawLine(pos.x + pos.w, pos.y, pos.x + pos.w, pos.y + pos.h, SDL_MapRGB(mainsurface->format, 0, 192, 255), 255);
2552 				if ( stats[0]->playerRace >= RACE_HUMAN )
2553 				{
2554 					ttfPrintText(ttf12, pos.x + 12, pos.y + 6, language[3375 + stats[0]->playerRace]);
2555 				}
2556 				ttfPrintText(ttf12, raceInfoBtn.x + 4, raceInfoBtn.y + 6, language[3374]);
2557 			}
2558 			else
2559 			{
2560 				ttfPrintText(ttf12, raceInfoBtn.x + 4, raceInfoBtn.y + 6, language[3373]);
2561 			}
2562 		}
2563 
2564 		// skin DLC check flags.
2565 		bool skipFirstDLC = false;
2566 		bool skipSecondDLC = false;
2567 		if ( enabledDLCPack2 && !enabledDLCPack1 )
2568 		{
2569 			skipFirstDLC = true;
2570 			if ( stats[0]->playerRace > 0 && stats[0]->playerRace <= RACE_GOATMAN )
2571 			{
2572 				stats[0]->playerRace = RACE_HUMAN;
2573 			}
2574 		}
2575 		else if ( enabledDLCPack1 && !enabledDLCPack2 )
2576 		{
2577 			skipSecondDLC = true;
2578 			if ( stats[0]->playerRace > RACE_GOATMAN )
2579 			{
2580 				stats[0]->playerRace = RACE_HUMAN;
2581 			}
2582 		}
2583 		else if ( !enabledDLCPack1 && !enabledDLCPack2 )
2584 		{
2585 			stats[0]->playerRace = RACE_HUMAN;
2586 		}
2587 
2588 		// sexes/race
2589 		if ( charcreation_step == 1 )
2590 		{
2591 			ttfPrintText(ttf16, subx1 + 24, suby1 + 32, language[1319]);
2592 			Uint32 colorStep1 = uint32ColorWhite(*mainsurface);
2593 			if ( raceSelect != 0 )
2594 			{
2595 				colorStep1 = uint32ColorGray(*mainsurface);
2596 			}
2597 			if ( stats[0]->sex == 0 )
2598 			{
2599 				ttfPrintTextFormattedColor(ttf16, subx1 + 32, suby1 + 56, colorStep1, "[o] %s", language[1321]);
2600 				ttfPrintTextFormattedColor(ttf16, subx1 + 32, suby1 + 73, colorStep1, "[ ] %s", language[1322]);
2601 
2602 				ttfPrintTextFormattedColor(ttf12, subx1 + 8, suby2 - 80, uint32ColorWhite(*mainsurface), language[1320], language[1321]);
2603 			}
2604 			else
2605 			{
2606 				ttfPrintTextFormattedColor(ttf16, subx1 + 32, suby1 + 56, colorStep1, "[ ] %s", language[1321]);
2607 				ttfPrintTextFormattedColor(ttf16, subx1 + 32, suby1 + 73, colorStep1, "[o] %s", language[1322]);
2608 
2609 				ttfPrintTextFormattedColor(ttf12, subx1 + 8, suby2 - 80, uint32ColorWhite(*mainsurface), language[1320], language[1322]);
2610 			}
2611 			ttfPrintTextFormattedColor(ttf12, subx1 + 8, suby2 - 56, uint32ColorWhite(*mainsurface), language[3175]);
2612 
2613 			// race
2614 			if ( raceSelect != 1 )
2615 			{
2616 				colorStep1 = uint32ColorGray(*mainsurface);
2617 			}
2618 			else if ( raceSelect == 1 )
2619 			{
2620 				colorStep1 = uint32ColorWhite(*mainsurface);
2621 			}
2622 			ttfPrintText(ttf16, subx1 + 24, suby1 + 108, language[3160]);
2623 			int pady = suby1 + 108 + 24;
2624 			bool isLocked = false;
2625 			for ( int c = 0; c < NUMPLAYABLERACES; )
2626 			{
2627 				if ( raceSelect == 1 )
2628 				{
2629 					if ( skipSecondDLC )
2630 					{
2631 						if ( c > RACE_GOATMAN )
2632 						{
2633 							colorStep1 = uint32ColorGray(*mainsurface);
2634 						}
2635 						else
2636 						{
2637 							colorStep1 = uint32ColorWhite(*mainsurface);
2638 						}
2639 					}
2640 					else if ( skipFirstDLC )
2641 					{
2642 						if ( c > RACE_HUMAN && c <= RACE_GOATMAN )
2643 						{
2644 							colorStep1 = uint32ColorGray(*mainsurface);
2645 						}
2646 						else
2647 						{
2648 							colorStep1 = uint32ColorWhite(*mainsurface);
2649 						}
2650 					}
2651 					else if ( !(enabledDLCPack2 && enabledDLCPack1) )
2652 					{
2653 						if ( c > RACE_HUMAN )
2654 						{
2655 							colorStep1 = uint32ColorGray(*mainsurface);
2656 						}
2657 						else
2658 						{
2659 							colorStep1 = uint32ColorWhite(*mainsurface);
2660 						}
2661 					}
2662 					else if ( enabledDLCPack2 && enabledDLCPack1 )
2663 					{
2664 						colorStep1 = uint32ColorWhite(*mainsurface);
2665 					}
2666 				}
2667 				if ( stats[0]->playerRace == c )
2668 				{
2669 					ttfPrintTextFormattedColor(ttf16, subx1 + 32, pady, colorStep1, "[o] %s", language[3161 + c]);
2670 				}
2671 				else
2672 				{
2673 					if ( skipSecondDLC )
2674 					{
2675 						if ( c > RACE_GOATMAN )
2676 						{
2677 							isLocked = true;
2678 						}
2679 					}
2680 					else if ( skipFirstDLC )
2681 					{
2682 						if ( c >= RACE_SKELETON && c < RACE_AUTOMATON )
2683 						{
2684 							isLocked = true;
2685 						}
2686 					}
2687 					else if ( !enabledDLCPack1 && !enabledDLCPack2 )
2688 					{
2689 						isLocked = true;
2690 					}
2691 					if ( isLocked )
2692 					{
2693 						SDL_Rect img;
2694 						img.x = subx1 + 32 + 10;
2695 						img.y = pady - 2;
2696 						img.w = 22;
2697 						img.h = 20;
2698 						drawImageScaled(sidebar_unlock_bmp, nullptr, &img);
2699 						ttfPrintTextFormattedColor(ttf16, subx1 + 32, pady, colorStep1, "[ ] %s", language[3161 + c]);
2700 					}
2701 					else
2702 					{
2703 						ttfPrintTextFormattedColor(ttf16, subx1 + 32, pady, colorStep1, "[ ] %s", language[3161 + c]);
2704 					}
2705 				}
2706 
2707 				if ( skipFirstDLC )
2708 				{
2709 					if ( c == RACE_HUMAN )
2710 					{
2711 						c = RACE_AUTOMATON;
2712 					}
2713 					else if ( c == RACE_INSECTOID )
2714 					{
2715 						c = RACE_SKELETON;
2716 						pady += 8;
2717 					}
2718 					else if ( c == RACE_GOATMAN )
2719 					{
2720 						c = NUMPLAYABLERACES;
2721 					}
2722 					else
2723 					{
2724 						++c;
2725 					}
2726 				}
2727 				else
2728 				{
2729 					if ( skipSecondDLC && c == RACE_GOATMAN )
2730 					{
2731 						pady += 8;
2732 					}
2733 					else if ( !enabledDLCPack1 && !enabledDLCPack2 && c == RACE_HUMAN )
2734 					{
2735 						pady += 8;
2736 					}
2737 					++c;
2738 				}
2739 				pady += 17;
2740 			}
2741 
2742 			pady += 24;
2743 			if ( isLocked )
2744 			{
2745 				pady -= 8;
2746 			}
2747 			bool displayRaceOptions = false;
2748 			if ( raceSelect != 2 )
2749 			{
2750 				colorStep1 = uint32ColorGray(*mainsurface);
2751 			}
2752 			else
2753 			{
2754 				colorStep1 = uint32ColorWhite(*mainsurface);
2755 			}
2756 			if ( stats[0]->playerRace > 0 )
2757 			{
2758 				displayRaceOptions = true;
2759 				ttfPrintText(ttf16, subx1 + 24, pady, language[3176]);
2760 				pady += 24;
2761 				char raceOptionBuffer[128];
2762 				snprintf(raceOptionBuffer, 63, language[3177], language[3161 + stats[0]->playerRace]);
2763 				if ( stats[0]->appearance > 1 )
2764 				{
2765 					stats[0]->appearance = lastAppearance;
2766 				}
2767 				if ( stats[0]->appearance == 0 )
2768 				{
2769 					ttfPrintTextFormattedColor(ttf16, subx1 + 32, pady, colorStep1, "[o] %s", raceOptionBuffer);
2770 					ttfPrintTextFormattedColor(ttf16, subx1 + 32, pady + 17, colorStep1, "[ ] %s", language[3178]);
2771 				}
2772 				else if ( stats[0]->appearance == 1 )
2773 				{
2774 					ttfPrintTextFormattedColor(ttf16, subx1 + 32, pady, colorStep1, "[ ] %s", raceOptionBuffer);
2775 					ttfPrintTextFormattedColor(ttf16, subx1 + 32, pady + 17, colorStep1, "[o] %s", language[3178]);
2776 				}
2777 
2778 			}
2779 
2780 			pady = suby1 + 108 + 24;
2781 			lastRace = static_cast<PlayerRaces>(stats[0]->playerRace);
2782 			if ( omousex >= subx1 + 40 && omousex < subx1 + 72 )
2783 			{
2784 				if ( omousey >= suby1 + 56 && omousey < suby1 + 72 )
2785 				{
2786 					if ( mousestatus[SDL_BUTTON_LEFT] )
2787 					{
2788 						raceSelect = 0;
2789 						mousestatus[SDL_BUTTON_LEFT] = 0;
2790 						stats[0]->sex = MALE;
2791 						lastSex = MALE;
2792 						if ( stats[0]->playerRace == RACE_SUCCUBUS )
2793 						{
2794 							if ( enabledDLCPack2 )
2795 							{
2796 								stats[0]->playerRace = RACE_INCUBUS;
2797 								if ( client_classes[0] == CLASS_MESMER && stats[0]->appearance == 0 )
2798 								{
2799 									if ( isCharacterValidFromDLC(*stats[0], client_classes[0]) != VALID_OK_CHARACTER )
2800 									{
2801 										client_classes[0] = CLASS_PUNISHER;
2802 									}
2803 									stats[0]->clearStats();
2804 									initClass(0);
2805 								}
2806 							}
2807 							else
2808 							{
2809 								stats[0]->playerRace = RACE_HUMAN;
2810 							}
2811 						}
2812 					}
2813 				}
2814 				else if ( omousey >= suby1 + 72 && omousey < suby1 + 90 )
2815 				{
2816 					if ( mousestatus[SDL_BUTTON_LEFT] )
2817 					{
2818 						raceSelect = 0;
2819 						mousestatus[SDL_BUTTON_LEFT] = 0;
2820 						stats[0]->sex = FEMALE;
2821 						lastSex = FEMALE;
2822 						if ( stats[0]->playerRace == RACE_INCUBUS )
2823 						{
2824 							if ( enabledDLCPack1 )
2825 							{
2826 								stats[0]->playerRace = RACE_SUCCUBUS;
2827 								if ( client_classes[0] == CLASS_PUNISHER && stats[0]->appearance == 0 )
2828 								{
2829 									if ( isCharacterValidFromDLC(*stats[0], client_classes[0]) != VALID_OK_CHARACTER )
2830 									{
2831 										client_classes[0] = CLASS_MESMER;
2832 									}
2833 									stats[0]->clearStats();
2834 									initClass(0);
2835 								}
2836 							}
2837 							else
2838 							{
2839 								stats[0]->playerRace = RACE_HUMAN;
2840 							}
2841 						}
2842 					}
2843 				}
2844 				else if ( omousey >= pady && omousey < pady + NUMPLAYABLERACES * 17 + (isLocked ? 8 : 0) )
2845 				{
2846 					for ( c = 0; c < NUMPLAYABLERACES; ++c )
2847 					{
2848 						if ( omousey >= pady && omousey < pady + 17 )
2849 						{
2850 							bool disableSelect = false;
2851 							if ( skipSecondDLC )
2852 							{
2853 								if ( c > RACE_GOATMAN )
2854 								{
2855 									disableSelect = true;
2856 								}
2857 							}
2858 							else if ( skipFirstDLC )
2859 							{
2860 								if ( c > RACE_GOATMAN && c <= RACE_INSECTOID ) // this is weird cause we're reordering the menu above...
2861 								{
2862 									disableSelect = true;
2863 								}
2864 							}
2865 							else if ( !enabledDLCPack1 && !enabledDLCPack2 )
2866 							{
2867 								if ( c != RACE_HUMAN )
2868 								{
2869 									disableSelect = true;
2870 								}
2871 							}
2872 							if ( !disableSelect && mousestatus[SDL_BUTTON_LEFT] )
2873 							{
2874 								raceSelect = 1;
2875 								mousestatus[SDL_BUTTON_LEFT] = 0;
2876 								if ( !disableSelect )
2877 								{
2878 									PlayerRaces lastRace = static_cast<PlayerRaces>(stats[0]->playerRace);
2879 									if ( skipFirstDLC )
2880 									{
2881 										// this is weird cause we're reordering the menu above...
2882 										if ( c > RACE_GOATMAN )
2883 										{
2884 											stats[0]->playerRace = c - 4;
2885 										}
2886 										else if ( c > RACE_HUMAN )
2887 										{
2888 											stats[0]->playerRace = c + 4;
2889 										}
2890 										else
2891 										{
2892 											stats[0]->playerRace = c;
2893 										}
2894 									}
2895 									else
2896 									{
2897 										stats[0]->playerRace = c;
2898 									}
2899 									mousestatus[SDL_BUTTON_LEFT] = 0;
2900 									if ( stats[0]->playerRace == RACE_INCUBUS )
2901 									{
2902 										stats[0]->sex = MALE;
2903 									}
2904 									else if ( stats[0]->playerRace == RACE_SUCCUBUS )
2905 									{
2906 										stats[0]->sex = FEMALE;
2907 									}
2908 									else if ( lastRace == RACE_SUCCUBUS || lastRace == RACE_INCUBUS )
2909 									{
2910 										stats[0]->sex = lastSex;
2911 									}
2912 									// convert human class to monster special classes on reselect.
2913 									if ( stats[0]->playerRace != RACE_HUMAN && lastRace != RACE_HUMAN && client_classes[0] > CLASS_MONK
2914 										&& stats[0]->appearance == 0 )
2915 									{
2916 										if ( isCharacterValidFromDLC(*stats[0], client_classes[0]) != VALID_OK_CHARACTER )
2917 										{
2918 											client_classes[0] = CLASS_MONK + stats[0]->playerRace;
2919 										}
2920 										stats[0]->clearStats();
2921 										initClass(0);
2922 									}
2923 									else if ( stats[0]->playerRace != RACE_HUMAN && lastRace == RACE_HUMAN && client_classes[0] > CLASS_MONK
2924 										&& lastAppearance == 0 )
2925 									{
2926 										if ( isCharacterValidFromDLC(*stats[0], client_classes[0]) != VALID_OK_CHARACTER )
2927 										{
2928 											client_classes[0] = CLASS_MONK + stats[0]->playerRace;
2929 										}
2930 										stats[0]->clearStats();
2931 										initClass(0);
2932 									}
2933 									else if ( stats[0]->playerRace != RACE_GOATMAN && lastRace == RACE_GOATMAN )
2934 									{
2935 										stats[0]->clearStats();
2936 										initClass(0);
2937 									}
2938 									// appearance reset.
2939 									if ( stats[0]->playerRace == RACE_HUMAN && lastRace != RACE_HUMAN )
2940 									{
2941 										stats[0]->appearance = rand() % NUMAPPEARANCES;
2942 									}
2943 									else if ( stats[0]->playerRace != RACE_HUMAN && lastRace == RACE_HUMAN )
2944 									{
2945 										stats[0]->appearance = lastAppearance;
2946 									}
2947 								}
2948 								break;
2949 							}
2950 							else if ( disableSelect )
2951 							{
2952 								SDL_Rect tooltip;
2953 								tooltip.x = omousex + 16;
2954 								tooltip.y = omousey + 16;
2955 								tooltip.h = TTF12_HEIGHT + 8;
2956 #if (defined STEAMWORKS || defined USE_EOS)
2957 								if ( c > RACE_GOATMAN && c <= RACE_INSECTOID && !skipFirstDLC )
2958 								{
2959 									tooltip.h = TTF12_HEIGHT * 2 + 8;
2960 									tooltip.w = longestline(language[3917]) * TTF12_WIDTH + 8;
2961 									drawTooltip(&tooltip);
2962 									ttfPrintTextFormattedColor(ttf12, tooltip.x + 4, tooltip.y + 6, uint32ColorOrange(*mainsurface), language[3917]);
2963 								}
2964 								else
2965 								{
2966 									tooltip.h = TTF12_HEIGHT * 2 + 8;
2967 									tooltip.w = longestline(language[3200]) * TTF12_WIDTH + 8;
2968 									drawTooltip(&tooltip);
2969 									ttfPrintTextFormattedColor(ttf12, tooltip.x + 4, tooltip.y + 6, uint32ColorOrange(*mainsurface), language[3200]);
2970 								}
2971 #ifdef STEAMWORKS
2972 								if ( SteamUser()->BLoggedOn() )
2973 								{
2974 									if ( mousestatus[SDL_BUTTON_LEFT] )
2975 									{
2976 										if ( SteamUtils()->IsOverlayEnabled() )
2977 										{
2978 											SteamFriends()->ActivateGameOverlayToStore(STEAM_APPID, k_EOverlayToStoreFlag_None);
2979 										}
2980 										else
2981 										{
2982 											if ( c > RACE_GOATMAN && c <= RACE_INSECTOID && !skipFirstDLC )
2983 											{
2984 												openURLTryWithOverlay(language[3993]);
2985 											}
2986 											else
2987 											{
2988 												openURLTryWithOverlay(language[3992]);
2989 											}
2990 										}
2991 										mousestatus[SDL_BUTTON_LEFT] = 0;
2992 									}
2993 								}
2994 #elif defined USE_EOS
2995 								if ( c > RACE_GOATMAN && c <= RACE_INSECTOID && !skipFirstDLC )
2996 								{
2997 									if ( mousestatus[SDL_BUTTON_LEFT] )
2998 									{
2999 										openURLTryWithOverlay(language[3985]);
3000 										mousestatus[SDL_BUTTON_LEFT] = 0;
3001 									}
3002 								}
3003 								else
3004 								{
3005 									if ( mousestatus[SDL_BUTTON_LEFT] )
3006 									{
3007 										openURLTryWithOverlay(language[3984]);
3008 										mousestatus[SDL_BUTTON_LEFT] = 0;
3009 									}
3010 								}
3011 #endif
3012 #else
3013 								if ( c > RACE_GOATMAN && c <= RACE_INSECTOID )
3014 								{
3015 									tooltip.w = longestline(language[3372]) * TTF12_WIDTH + 8;
3016 									drawTooltip(&tooltip);
3017 									ttfPrintTextFormattedColor(ttf12, tooltip.x + 4, tooltip.y + 6, uint32ColorOrange(*mainsurface), language[3372]);
3018 								}
3019 								else
3020 								{
3021 									tooltip.w = longestline(language[3199]) * TTF12_WIDTH + 8;
3022 									drawTooltip(&tooltip);
3023 									ttfPrintTextFormattedColor(ttf12, tooltip.x + 4, tooltip.y + 6, uint32ColorOrange(*mainsurface), language[3199]);
3024 								}
3025 #endif // STEAMWORKS
3026 							}
3027 						}
3028 						pady += 17;
3029 						if ( isLocked )
3030 						{
3031 							if ( skipFirstDLC && c == RACE_INSECTOID )
3032 							{
3033 								pady += 8;
3034 							}
3035 							else
3036 							{
3037 								if ( skipSecondDLC && c == RACE_GOATMAN )
3038 								{
3039 									pady += 8;
3040 								}
3041 								else if ( !enabledDLCPack1 && !enabledDLCPack2 && c == RACE_HUMAN )
3042 								{
3043 									pady += 8;
3044 								}
3045 							}
3046 						}
3047 					}
3048 				}
3049 				else if ( omousey >= pady + (NUMPLAYABLERACES * 17) + 48 && omousey < pady + (NUMPLAYABLERACES * 17) + 82 )
3050 				{
3051 					if ( mousestatus[SDL_BUTTON_LEFT] )
3052 					{
3053 						mousestatus[SDL_BUTTON_LEFT] = 0;
3054 						if ( stats[0]->playerRace > 0 )
3055 						{
3056 							if ( omousey < pady + (NUMPLAYABLERACES * 17) + 64 ) // first option
3057 							{
3058 								if ( stats[0]->appearance != 0 )
3059 								{
3060 									stats[0]->appearance = 0; // use racial passives
3061 									// convert human class to monster special classes on reselect.
3062 									if ( client_classes[0] > CLASS_MONK )
3063 									{
3064 										if ( isCharacterValidFromDLC(*stats[0], client_classes[0]) != VALID_OK_CHARACTER )
3065 										{
3066 											client_classes[0] = CLASS_MONK + stats[0]->playerRace;
3067 										}
3068 									}
3069 								}
3070 							}
3071 							else
3072 							{
3073 								stats[0]->appearance = 1; // act as human
3074 							}
3075 							lastAppearance = stats[0]->appearance;
3076 							stats[0]->clearStats();
3077 							initClass(0);
3078 							raceSelect = 2;
3079 							mousestatus[SDL_BUTTON_LEFT] = 0;
3080 						}
3081 					}
3082 				}
3083 			}
3084 			if ( keystatus[SDL_SCANCODE_UP] || (*inputPressed(joyimpulses[INJOY_DPAD_UP]) && rebindaction == -1) )
3085 			{
3086 				keystatus[SDL_SCANCODE_UP] = 0;
3087 				if ( rebindaction == -1 )
3088 				{
3089 					*inputPressed(joyimpulses[INJOY_DPAD_UP]) = 0;
3090 				}
3091 				draw_cursor = false;
3092 				if ( raceSelect == 1 )
3093 				{
3094 					PlayerRaces lastRace = static_cast<PlayerRaces>(stats[0]->playerRace);
3095 					if ( !enabledDLCPack1 && !enabledDLCPack2 )
3096 					{
3097 						// do nothing.
3098 						stats[0]->playerRace = RACE_HUMAN;
3099 					}
3100 					else if ( stats[0]->playerRace <= 0 )
3101 					{
3102 						if ( skipSecondDLC )
3103 						{
3104 							stats[0]->playerRace = NUMPLAYABLERACES - 5;
3105 						}
3106 						else if ( enabledDLCPack2 )
3107 						{
3108 							stats[0]->playerRace = NUMPLAYABLERACES - 1;
3109 						}
3110 					}
3111 					else
3112 					{
3113 						if ( skipFirstDLC && stats[0]->playerRace == RACE_AUTOMATON )
3114 						{
3115 							stats[0]->playerRace = RACE_HUMAN;
3116 						}
3117 						else
3118 						{
3119 							--stats[0]->playerRace;
3120 						}
3121 					}
3122 					if ( stats[0]->playerRace == RACE_INCUBUS )
3123 					{
3124 						stats[0]->sex = MALE;
3125 					}
3126 					else if ( stats[0]->playerRace == RACE_SUCCUBUS )
3127 					{
3128 						stats[0]->sex = FEMALE;
3129 					}
3130 					else if ( lastRace == RACE_SUCCUBUS || lastRace == RACE_INCUBUS )
3131 					{
3132 						stats[0]->sex = lastSex;
3133 					}
3134 					// convert human class to monster special classes on reselect.
3135 					if ( stats[0]->playerRace != RACE_HUMAN && lastRace != RACE_HUMAN && client_classes[0] > CLASS_MONK
3136 						&& stats[0]->appearance == 0 )
3137 					{
3138 						if ( isCharacterValidFromDLC(*stats[0], client_classes[0]) != VALID_OK_CHARACTER )
3139 						{
3140 							client_classes[0] = CLASS_MONK + stats[0]->playerRace;
3141 						}
3142 						stats[0]->clearStats();
3143 						initClass(0);
3144 					}
3145 					else if ( stats[0]->playerRace != RACE_HUMAN && lastRace == RACE_HUMAN && client_classes[0] > CLASS_MONK
3146 						&& lastAppearance == 0 )
3147 					{
3148 						if ( isCharacterValidFromDLC(*stats[0], client_classes[0]) != VALID_OK_CHARACTER )
3149 						{
3150 							client_classes[0] = CLASS_MONK + stats[0]->playerRace;
3151 						}
3152 						stats[0]->clearStats();
3153 						initClass(0);
3154 					}
3155 					else if ( stats[0]->playerRace != RACE_GOATMAN && lastRace == RACE_GOATMAN )
3156 					{
3157 						stats[0]->clearStats();
3158 						initClass(0);
3159 					}
3160 					// appearance reset.
3161 					if ( stats[0]->playerRace == RACE_HUMAN && lastRace != RACE_HUMAN )
3162 					{
3163 						stats[0]->appearance = rand() % NUMAPPEARANCES;
3164 					}
3165 					else if ( stats[0]->playerRace != RACE_HUMAN && lastRace == RACE_HUMAN )
3166 					{
3167 						stats[0]->appearance = lastAppearance;
3168 					}
3169 				}
3170 				else if ( raceSelect == 0 )
3171 				{
3172 					stats[0]->sex = static_cast<sex_t>((stats[0]->sex == MALE));
3173 					lastSex = stats[0]->sex;
3174 					if ( stats[0]->playerRace == RACE_INCUBUS )
3175 					{
3176 						stats[0]->sex = MALE;
3177 					}
3178 					else if ( stats[0]->playerRace == RACE_SUCCUBUS )
3179 					{
3180 						stats[0]->sex = FEMALE;
3181 					}
3182 				}
3183 				else if ( raceSelect == 2 )
3184 				{
3185 					if ( stats[0]->appearance != 0 )
3186 					{
3187 						stats[0]->appearance = 0;
3188 						// convert human class to monster special classes on reselect.
3189 						if ( stats[0]->playerRace != RACE_HUMAN && client_classes[0] > CLASS_MONK )
3190 						{
3191 							if ( isCharacterValidFromDLC(*stats[0], client_classes[0]) != VALID_OK_CHARACTER )
3192 							{
3193 								client_classes[0] = CLASS_MONK + stats[0]->playerRace;
3194 							}
3195 							stats[0]->clearStats();
3196 							initClass(0);
3197 						}
3198 					}
3199 					else
3200 					{
3201 						stats[0]->appearance = 1;
3202 					}
3203 					lastAppearance = stats[0]->appearance;
3204 					stats[0]->clearStats();
3205 					initClass(0);
3206 				}
3207 			}
3208 			if ( keystatus[SDL_SCANCODE_DOWN] || (*inputPressed(joyimpulses[INJOY_DPAD_DOWN]) && rebindaction == -1) )
3209 			{
3210 				keystatus[SDL_SCANCODE_DOWN] = 0;
3211 				if ( rebindaction == -1 )
3212 				{
3213 					*inputPressed(joyimpulses[INJOY_DPAD_DOWN]) = 0;
3214 				}
3215 				draw_cursor = false;
3216 				if ( raceSelect == 1 )
3217 				{
3218 					PlayerRaces lastRace = static_cast<PlayerRaces>(stats[0]->playerRace);
3219 					if ( !enabledDLCPack1 && !enabledDLCPack2 )
3220 					{
3221 						// do nothing.
3222 						stats[0]->playerRace = RACE_HUMAN;
3223 					}
3224 					else if ( skipSecondDLC )
3225 					{
3226 						if ( stats[0]->playerRace >= NUMPLAYABLERACES - 5 )
3227 						{
3228 							stats[0]->playerRace = RACE_HUMAN;
3229 						}
3230 						else
3231 						{
3232 							++stats[0]->playerRace;
3233 						}
3234 					}
3235 					else if ( skipFirstDLC )
3236 					{
3237 						if ( stats[0]->playerRace >= RACE_GOATMAN && stats[0]->playerRace < NUMPLAYABLERACES - 1 )
3238 						{
3239 							++stats[0]->playerRace;
3240 						}
3241 						else if ( stats[0]->playerRace == RACE_HUMAN )
3242 						{
3243 							stats[0]->playerRace = RACE_AUTOMATON;
3244 						}
3245 						else if ( stats[0]->playerRace == NUMPLAYABLERACES - 1 )
3246 						{
3247 							stats[0]->playerRace = RACE_HUMAN;
3248 						}
3249 					}
3250 					else
3251 					{
3252 						if ( stats[0]->playerRace >= NUMPLAYABLERACES - 1 )
3253 						{
3254 							stats[0]->playerRace = RACE_HUMAN;
3255 						}
3256 						else
3257 						{
3258 							++stats[0]->playerRace;
3259 						}
3260 					}
3261 					if ( stats[0]->playerRace == RACE_INCUBUS )
3262 					{
3263 						stats[0]->sex = MALE;
3264 					}
3265 					else if ( stats[0]->playerRace == RACE_SUCCUBUS )
3266 					{
3267 						stats[0]->sex = FEMALE;
3268 					}
3269 					else if ( lastRace == RACE_SUCCUBUS || lastRace == RACE_INCUBUS )
3270 					{
3271 						stats[0]->sex = lastSex;
3272 					}
3273 					// convert human class to monster special classes on reselect.
3274 					if ( stats[0]->playerRace != RACE_HUMAN && lastRace != RACE_HUMAN && client_classes[0] > CLASS_MONK
3275 						&& stats[0]->appearance == 0 )
3276 					{
3277 						if ( isCharacterValidFromDLC(*stats[0], client_classes[0]) != VALID_OK_CHARACTER )
3278 						{
3279 							client_classes[0] = CLASS_MONK + stats[0]->playerRace;
3280 						}
3281 						stats[0]->clearStats();
3282 						initClass(0);
3283 					}
3284 					else if ( stats[0]->playerRace != RACE_HUMAN && lastRace == RACE_HUMAN && client_classes[0] > CLASS_MONK
3285 						&& lastAppearance == 0 )
3286 					{
3287 						if ( isCharacterValidFromDLC(*stats[0], client_classes[0]) != VALID_OK_CHARACTER )
3288 						{
3289 							client_classes[0] = CLASS_MONK + stats[0]->playerRace;
3290 						}
3291 						stats[0]->clearStats();
3292 						initClass(0);
3293 					}
3294 					else if ( stats[0]->playerRace != RACE_GOATMAN && lastRace == RACE_GOATMAN )
3295 					{
3296 						stats[0]->clearStats();
3297 						initClass(0);
3298 					}
3299 					// appearance reset.
3300 					if ( stats[0]->playerRace == RACE_HUMAN && lastRace != RACE_HUMAN )
3301 					{
3302 						stats[0]->appearance = rand() % NUMAPPEARANCES;
3303 					}
3304 					else if ( stats[0]->playerRace != RACE_HUMAN && lastRace == RACE_HUMAN )
3305 					{
3306 						stats[0]->appearance = lastAppearance;
3307 					}
3308 				}
3309 				else if ( raceSelect == 0 )
3310 				{
3311 					stats[0]->sex = static_cast<sex_t>((stats[0]->sex == MALE));
3312 					lastSex = stats[0]->sex;
3313 					if ( stats[0]->playerRace == RACE_INCUBUS )
3314 					{
3315 						stats[0]->sex = MALE;
3316 					}
3317 					else if ( stats[0]->playerRace == RACE_SUCCUBUS )
3318 					{
3319 						stats[0]->sex = FEMALE;
3320 					}
3321 				}
3322 				else if ( raceSelect == 2 )
3323 				{
3324 					if ( stats[0]->appearance != 0 )
3325 					{
3326 						stats[0]->appearance = 0;
3327 						// convert human class to monster special classes on reselect.
3328 						if ( stats[0]->playerRace != RACE_HUMAN && client_classes[0] > CLASS_MONK )
3329 						{
3330 							if ( isCharacterValidFromDLC(*stats[0], client_classes[0]) != VALID_OK_CHARACTER )
3331 							{
3332 								client_classes[0] = CLASS_MONK + stats[0]->playerRace;
3333 							}
3334 							stats[0]->clearStats();
3335 							initClass(0);
3336 						}
3337 					}
3338 					else
3339 					{
3340 						stats[0]->appearance = 1;
3341 					}
3342 					lastAppearance = stats[0]->appearance;
3343 					stats[0]->clearStats();
3344 					initClass(0);
3345 				}
3346 			}
3347 			if ( keystatus[SDL_SCANCODE_RIGHT] || (*inputPressed(joyimpulses[INJOY_DPAD_RIGHT]) && rebindaction == -1) )
3348 			{
3349 				keystatus[SDL_SCANCODE_RIGHT] = 0;
3350 				if ( rebindaction == -1 )
3351 				{
3352 					*inputPressed(joyimpulses[INJOY_DPAD_RIGHT]) = 0;
3353 				}
3354 				draw_cursor = false;
3355 				++raceSelect;
3356 				if ( stats[0]->playerRace == RACE_HUMAN )
3357 				{
3358 					if ( raceSelect > 1 )
3359 					{
3360 						raceSelect = 0;
3361 					}
3362 				}
3363 				else if ( raceSelect > 2 )
3364 				{
3365 					raceSelect = 0;
3366 				}
3367 			}
3368 			if ( keystatus[SDL_SCANCODE_LEFT] || (*inputPressed(joyimpulses[INJOY_DPAD_LEFT]) && rebindaction == -1) )
3369 			{
3370 				keystatus[SDL_SCANCODE_LEFT] = 0;
3371 				if ( rebindaction == -1 )
3372 				{
3373 					*inputPressed(joyimpulses[INJOY_DPAD_LEFT]) = 0;
3374 				}
3375 				draw_cursor = false;
3376 				--raceSelect;
3377 				if ( stats[0]->playerRace == RACE_HUMAN )
3378 				{
3379 					if ( raceSelect < 0 )
3380 					{
3381 						raceSelect = 1;
3382 					}
3383 				}
3384 				else if ( raceSelect < 0 )
3385 				{
3386 					raceSelect = 2;
3387 				}
3388 			}
3389 			if ( lastRace != RACE_GOATMAN && stats[0]->playerRace == RACE_GOATMAN && stats[0]->appearance == 0 )
3390 			{
3391 				stats[0]->clearStats();
3392 				initClass(0);
3393 			}
3394 		}
3395 
3396 		// classes
3397 		else if ( charcreation_step == 2 )
3398 		{
3399 			ttfPrintText(ttf16, subx1 + 24, suby1 + 32, language[1323]);
3400 			int entriesToDisplay = NUMCLASSES;
3401 			if ( enabledDLCPack1 && enabledDLCPack2 )
3402 			{
3403 				entriesToDisplay = NUMCLASSES;
3404 			}
3405 			else if ( enabledDLCPack1 || enabledDLCPack2 )
3406 			{
3407 				entriesToDisplay = NUMCLASSES - 4;
3408 			}
3409 			else
3410 			{
3411 				entriesToDisplay = CLASS_MONK + 1;
3412 			}
3413 
3414 			std::set<int> availableClasses;
3415 			std::set<int> lockedClasses;
3416 			std::vector<int> displayedClasses;
3417 			for ( c = 0; c < NUMCLASSES; ++c )
3418 			{
3419 				int result = isCharacterValidFromDLC(*stats[0], c);
3420 				if ( result == VALID_OK_CHARACTER )
3421 				{
3422 					availableClasses.insert(c);
3423 					displayedClasses.push_back(c);
3424 				}
3425 				else if ( result == INVALID_REQUIRE_ACHIEVEMENT )
3426 				{
3427 					lockedClasses.insert(c);
3428 				}
3429 			}
3430 
3431 			for ( auto it = lockedClasses.begin(); it != lockedClasses.end(); ++it )
3432 			{
3433 				displayedClasses.push_back(*it);
3434 			}
3435 
3436 			int drawLockedTooltip = 0;
3437 			SDL_Rect tooltip;
3438 			for ( c = 0; c < entriesToDisplay; c++ )
3439 			{
3440 				int classToPick = displayedClasses.at(c);
3441 				int pady = suby1 + 56 + 16 * c;
3442 				if ( lockedClasses.find(classToPick) != lockedClasses.end() )
3443 				{
3444 					pady += 8;
3445 				}
3446 
3447 				if ( mousestatus[SDL_BUTTON_LEFT] )
3448 				{
3449 					if ( omousex >= subx1 + 40 && omousex < subx1 + 72 )
3450 					{
3451 						if ( omousey >= pady && omousey < pady + 16 )
3452 						{
3453 							mousestatus[SDL_BUTTON_LEFT] = 0;
3454 							if ( isCharacterValidFromDLC(*stats[0], classToPick) == VALID_OK_CHARACTER )
3455 							{
3456 								int previousClassPicked = client_classes[0];
3457 								client_classes[0] = classToPick;
3458 								if ( previousClassPicked != client_classes[0] )
3459 								{
3460 									// reset class loadout
3461 									stats[0]->clearStats();
3462 									initClass(0);
3463 								}
3464 							}
3465 						}
3466 					}
3467 				}
3468 
3469 				bool classLocked = false;
3470 
3471 				if ( classToPick == client_classes[0] )
3472 				{
3473 					ttfPrintTextFormatted(ttf16, subx1 + 32, pady, "[o] %s", playerClassLangEntry(classToPick, 0));
3474 				}
3475 				else
3476 				{
3477 					if ( lockedClasses.find(classToPick) != lockedClasses.end() )
3478 					{
3479 						classLocked = true;
3480 						SDL_Rect img;
3481 						img.x = subx1 + 32 + 10;
3482 						img.y = pady - 2;
3483 						img.w = 22;
3484 						img.h = 20;
3485 						drawImageScaled(sidebar_unlock_bmp, nullptr, &img);
3486 						ttfPrintTextFormattedColor(ttf16, subx1 + 32, pady, uint32ColorGray(*mainsurface), "[ ] %s", playerClassLangEntry(classToPick, 0));
3487 
3488 						if ( mouseInBounds(subx1 + 40, subx1 + 72, pady, pady + 16) )
3489 						{
3490 #if (defined STEAMWORKS || defined USE_EOS)
3491 							tooltip.x = omousex + 16;
3492 							tooltip.y = omousey + 16;
3493 							tooltip.h = TTF12_HEIGHT + 8;
3494 							if ( classToPick > CLASS_MONK )
3495 							{
3496 								int langline = 3927 + classToPick - CLASS_CONJURER;
3497 								tooltip.w = longestline(language[langline]) * TTF12_WIDTH + 8;
3498 								drawLockedTooltip = langline;
3499 							}
3500 #endif
3501 						}
3502 					}
3503 					else
3504 					{
3505 						ttfPrintTextFormatted(ttf16, subx1 + 32, pady, "[ ] %s", playerClassLangEntry(classToPick, 0));
3506 					}
3507 				}
3508 
3509 				if ( keystatus[SDL_SCANCODE_UP] || (*inputPressed(joyimpulses[INJOY_DPAD_UP]) && rebindaction == -1) )
3510 				{
3511 					keystatus[SDL_SCANCODE_UP] = 0;
3512 					if ( rebindaction == -1 )
3513 					{
3514 						*inputPressed(joyimpulses[INJOY_DPAD_UP]) = 0;
3515 					}
3516 					draw_cursor = false;
3517 
3518 					int previousClassPicked = client_classes[0];
3519 					if ( client_classes[0] == 0 )
3520 					{
3521 						client_classes[0] = *(availableClasses.rbegin());;
3522 					}
3523 					else
3524 					{
3525 						auto it = availableClasses.find(client_classes[0]);
3526 						if ( it != availableClasses.end() )
3527 						{
3528 							client_classes[0] = *(--it); // get previous element
3529 						}
3530 						else
3531 						{
3532 							client_classes[0] = 0;
3533 						}
3534 					}
3535 
3536 					if ( previousClassPicked != client_classes[0] )
3537 					{
3538 						// reset class loadout
3539 						stats[0]->clearStats();
3540 						initClass(0);
3541 					}
3542 				}
3543 				if ( keystatus[SDL_SCANCODE_DOWN] || (*inputPressed(joyimpulses[INJOY_DPAD_DOWN]) && rebindaction == -1) )
3544 				{
3545 					keystatus[SDL_SCANCODE_DOWN] = 0;
3546 					if ( rebindaction == -1 )
3547 					{
3548 						*inputPressed(joyimpulses[INJOY_DPAD_DOWN]) = 0;
3549 					}
3550 					draw_cursor = false;
3551 
3552 					auto it = availableClasses.find(client_classes[0]);
3553 					int previousClassPicked = client_classes[0];
3554 					if ( it != availableClasses.end() )
3555 					{
3556 						auto nextIt = std::next(it, 1);
3557 						if ( nextIt == availableClasses.end() )
3558 						{
3559 							client_classes[0] = 0;
3560 						}
3561 						else
3562 						{
3563 							client_classes[0] = *(++it); // get next element
3564 						}
3565 					}
3566 					else
3567 					{
3568 						client_classes[0] = 0;
3569 					}
3570 
3571 					if ( previousClassPicked != client_classes[0] )
3572 					{
3573 						// reset class loadout
3574 						stats[0]->clearStats();
3575 						initClass(0);
3576 					}
3577 				}
3578 			}
3579 
3580 			// class description
3581 			ttfPrintText(ttf12, subx1 + 8, suby2 - 80, playerClassDescription(client_classes[0], 0));
3582 
3583 			if ( drawLockedTooltip > 0 )
3584 			{
3585 				drawTooltip(&tooltip);
3586 				ttfPrintTextFormattedColor(ttf12, tooltip.x + 4, tooltip.y + 6,
3587 					uint32ColorOrange(*mainsurface), language[drawLockedTooltip]);
3588 			}
3589 		}
3590 
3591 		// faces
3592 		else if ( charcreation_step == 3 )
3593 		{
3594 			ttfPrintText(ttf16, subx1 + 24, suby1 + 32, language[1324]);
3595 			for ( c = 0; c < NUMAPPEARANCES; c++ )
3596 			{
3597 				if ( stats[0]->appearance == c )
3598 				{
3599 					ttfPrintTextFormatted(ttf16, subx1 + 32, suby1 + 56 + c * 16, "[o] %s", language[20 + c]);
3600 					ttfPrintText(ttf12, subx1 + 8, suby2 - 80, language[38 + c]);
3601 				}
3602 				else
3603 				{
3604 					ttfPrintTextFormatted(ttf16, subx1 + 32, suby1 + 56 + c * 16, "[ ] %s", language[20 + c]);
3605 				}
3606 				if ( mousestatus[SDL_BUTTON_LEFT] )
3607 				{
3608 					if ( omousex >= subx1 + 40 && omousex < subx1 + 72 )
3609 					{
3610 						if ( omousey >= suby1 + 56 + 16 * c && omousey < suby1 + 72 + 16 * c )
3611 						{
3612 							mousestatus[SDL_BUTTON_LEFT] = 0;
3613 							stats[0]->appearance = c;
3614 						}
3615 					}
3616 				}
3617 				if ( keystatus[SDL_SCANCODE_UP] || (*inputPressed(joyimpulses[INJOY_DPAD_UP]) && rebindaction == -1) )
3618 				{
3619 					keystatus[SDL_SCANCODE_UP] = 0;
3620 					if ( rebindaction == -1 )
3621 					{
3622 						*inputPressed(joyimpulses[INJOY_DPAD_UP]) = 0;
3623 					}
3624 					draw_cursor = false;
3625 					stats[0]->appearance--;
3626 					if (stats[0]->appearance >= NUMAPPEARANCES)
3627 					{
3628 						stats[0]->appearance = NUMAPPEARANCES - 1;
3629 					}
3630 				}
3631 				if ( keystatus[SDL_SCANCODE_DOWN] || (*inputPressed(joyimpulses[INJOY_DPAD_DOWN]) && rebindaction == -1) )
3632 				{
3633 					keystatus[SDL_SCANCODE_DOWN] = 0;
3634 					if ( rebindaction == -1 )
3635 					{
3636 						*inputPressed(joyimpulses[INJOY_DPAD_DOWN]) = 0;
3637 					}
3638 					draw_cursor = false;
3639 					stats[0]->appearance++;
3640 					if (stats[0]->appearance >= NUMAPPEARANCES)
3641 					{
3642 						stats[0]->appearance = 0;
3643 					}
3644 				}
3645 			}
3646 		}
3647 
3648 		// name
3649 		else if ( charcreation_step == 4 )
3650 		{
3651 			ttfPrintText(ttf16, subx1 + 24, suby1 + 32, language[1325]);
3652 			drawDepressed(subx1 + 40, suby1 + 56, subx1 + 364, suby1 + 88);
3653 			ttfPrintText(ttf16, subx1 + 48, suby1 + 64, stats[0]->name);
3654 			ttfPrintText(ttf12, subx1 + 8, suby2 - 80, language[1326]);
3655 
3656 			// enter character name
3657 			if ( !SDL_IsTextInputActive() )
3658 			{
3659 				inputstr = stats[0]->name;
3660 				SDL_StartTextInput();
3661 			}
3662 			//strncpy(stats[0]->name,inputstr,16);
3663 			inputlen = 22;
3664 			if (lastname != "" && strlen(inputstr) == 0)
3665 			{
3666 				strncat(inputstr, lastname.c_str(), std::max<size_t>(0, inputlen - strlen(inputstr)));
3667 				lastname = ""; // Set this to nothing while we're currently editing so it doesn't keep filling it.  We'll save it again if we leave this tab.
3668 			}
3669 
3670 			if ( (ticks - cursorflash) % TICKS_PER_SECOND < TICKS_PER_SECOND / 2 )
3671 			{
3672 				int x;
3673 				TTF_SizeUTF8(ttf16, stats[0]->name, &x, NULL);
3674 				ttfPrintText(ttf16, subx1 + 48 + x, suby1 + 64, "_");
3675 			}
3676 		}
3677 
3678 		// gamemode
3679 		else if ( charcreation_step == 5 )
3680 		{
3681 			ttfPrintText(ttf16, subx1 + 24, suby1 + 32, language[1327]);
3682 
3683 			std::vector<Sint32> optionY;
3684 			std::vector<char*> optionTexts;
3685 			std::vector<char*> optionSubtexts;
3686 			std::vector<char*> optionDescriptions;
3687 			std::vector<Uint32> displayedOptionToGamemode;
3688 			Uint32 optionHeight = TTF12_HEIGHT + 2;
3689 			int nummodes = 3;
3690 #if (defined USE_EOS && defined STEAMWORKS)
3691 			nummodes += 2;
3692 			if ( LobbyHandler.crossplayEnabled )
3693 			{
3694 				nummodes += 1;
3695 				optionY.insert(optionY.end(), { suby1 + 56, suby1 + 86, suby1 + 128, suby1 + 178, suby1 + 216, suby1 + 256 });
3696 				optionTexts.insert(optionTexts.end(), { language[1328], language[1330], language[1330], language[1332], language[1330], language[1332] });
3697 				optionDescriptions.insert(optionDescriptions.end(), { language[1329], language[3946], language[3947], language[3945], language[1538], language[1539] });
3698 				optionSubtexts.insert(optionSubtexts.end(), { nullptr, language[3943], language[3944], nullptr, language[1537], language[1537] });
3699 				displayedOptionToGamemode.insert(displayedOptionToGamemode.end(), { SINGLE, SERVER, SERVERCROSSPLAY, CLIENT, DIRECTSERVER, DIRECTCLIENT});
3700 			}
3701 			else
3702 			{
3703 				optionY.insert(optionY.end(), { suby1 + 56, suby1 + 76, suby1 + 96, suby1 + 136, suby1 + 176, 0 });
3704 				optionTexts.insert(optionTexts.end(), { language[1328], language[1330], language[1332], language[1330], language[1332], nullptr });
3705 				optionDescriptions.insert(optionDescriptions.end(), { language[1329], language[1331], language[1333], language[1538], language[1539], nullptr });
3706 				optionSubtexts.insert(optionSubtexts.end(), { nullptr, nullptr, nullptr, language[1537], language[1537], nullptr });
3707 				displayedOptionToGamemode.insert(displayedOptionToGamemode.end(), { SINGLE, SERVER, CLIENT, DIRECTSERVER, DIRECTCLIENT, 0 });
3708 			}
3709 #elif (defined(USE_EOS) || defined(STEAMWORKS))
3710 			nummodes += 2;
3711 			optionY.insert(optionY.end(), { suby1 + 56, suby1 + 76, suby1 + 96, suby1 + 136, suby1 + 176, 0 });
3712 			optionTexts.insert(optionTexts.end(), { language[1328], language[1330], language[1332], language[1330], language[1332], nullptr });
3713 			optionDescriptions.insert(optionDescriptions.end(), { language[1329], language[1331], language[1333], language[1538], language[1539], nullptr });
3714 			optionSubtexts.insert(optionSubtexts.end(), { nullptr, nullptr, nullptr, language[1537], language[1537] });
3715 			displayedOptionToGamemode.insert(displayedOptionToGamemode.end(), { SINGLE, SERVER, CLIENT, DIRECTSERVER, DIRECTCLIENT, 0 });
3716 #else
3717 			optionY.insert(optionY.end(), { suby1 + 56, suby1 + 76, suby1 + 96, suby1 + 136, suby1 + 176, 0 });
3718 			optionTexts.insert(optionTexts.end(), { language[1328], language[1330], language[1332], language[1330], language[1332], nullptr });
3719 			optionDescriptions.insert(optionDescriptions.end(), { language[1329], language[1331], language[1333], language[1538], language[1539], nullptr });
3720 			optionSubtexts.insert(optionSubtexts.end(), { nullptr, nullptr, nullptr, language[1537], language[1537] });
3721 			displayedOptionToGamemode.insert(displayedOptionToGamemode.end(), { SINGLE, SERVER, CLIENT, DIRECTSERVER, DIRECTCLIENT, 0 });
3722 #endif
3723 			for ( int mode = 0; mode < nummodes; mode++ )
3724 			{
3725 				char selected = ' ';
3726 				if ( multiplayerselect == displayedOptionToGamemode.at(mode) )
3727 				{
3728 					selected = 'o';
3729 				}
3730 				if ( optionSubtexts.at(mode) == nullptr )
3731 				{
3732 					ttfPrintTextFormatted(ttf16, subx1 + 32, optionY.at(mode), "[%c] %s", selected, optionTexts.at(mode));
3733 				}
3734 				else
3735 				{
3736 					ttfPrintTextFormatted(ttf16, subx1 + 32, optionY.at(mode), "[%c] %s\n     %s", selected, optionTexts.at(mode), optionSubtexts.at(mode));
3737 				}
3738 				if ( selected == 'o' ) // draw description
3739 				{
3740 					ttfPrintText(ttf12, subx1 + 8, suby2 - 80, optionDescriptions.at(mode));
3741 				}
3742 
3743 				if ( multiplayerselect == SINGLE )
3744 				{
3745 					if ( singleplayerSavegameFreeSlot == -1 )
3746 					{
3747 						ttfPrintTextColor(ttf12, subx1 + 8, suby2 - 60, uint32ColorOrange(*mainsurface), true, language[2965]);
3748 					}
3749 				}
3750 				else if ( multiplayerselect > SINGLE )
3751 				{
3752 					if ( multiplayerSavegameFreeSlot == -1 )
3753 					{
3754 						ttfPrintTextColor(ttf12, subx1 + 8, suby2 - 60, uint32ColorOrange(*mainsurface), true, language[2966]);
3755 					}
3756 					if ( gamemods_numCurrentModsLoaded >= 0 )
3757 					{
3758 						ttfPrintTextColor(ttf12, subx1 + 8, suby2 - 60 - TTF12_HEIGHT * 6, uint32ColorOrange(*mainsurface), true, language[2981]);
3759 					}
3760 				}
3761 				if ( gamemods_numCurrentModsLoaded >= 0 )
3762 				{
3763 					ttfPrintTextColor(ttf12, subx1 + 8, suby2 - 60 + TTF12_HEIGHT, uint32ColorBaronyBlue(*mainsurface), true, language[2982]);
3764 				}
3765 				if ( mousestatus[SDL_BUTTON_LEFT] )
3766 				{
3767 					if ( omousex >= subx1 + 40 && omousex < subx1 + 72 )
3768 					{
3769 						if ( omousey >= optionY.at(mode) && omousey < (optionY.at(mode) + optionHeight) )
3770 						{
3771 							mousestatus[SDL_BUTTON_LEFT] = 0;
3772 							multiplayerselect = displayedOptionToGamemode.at(mode);
3773 						}
3774 					}
3775 				}
3776 			}
3777 			if (keystatus[SDL_SCANCODE_UP] || (*inputPressed(joyimpulses[INJOY_DPAD_UP]) && rebindaction == -1) )
3778 			{
3779 				keystatus[SDL_SCANCODE_UP] = 0;
3780 				if ( rebindaction == -1 )
3781 				{
3782 					*inputPressed(joyimpulses[INJOY_DPAD_UP]) = 0;
3783 				}
3784 				draw_cursor = false;
3785 
3786 				Uint32 vIndex = 0;
3787 				for ( auto& option : displayedOptionToGamemode )
3788 				{
3789 					if ( option == multiplayerselect )
3790 					{
3791 						break;
3792 					}
3793 					++vIndex;
3794 				}
3795 				if ( vIndex > 0 )
3796 				{
3797 					multiplayerselect = displayedOptionToGamemode.at(vIndex - 1);
3798 				}
3799 				else
3800 				{
3801 					multiplayerselect = displayedOptionToGamemode.at(nummodes - 1);
3802 				}
3803 			}
3804 			if ( keystatus[SDL_SCANCODE_DOWN] || (*inputPressed(joyimpulses[INJOY_DPAD_DOWN]) && rebindaction == -1) )
3805 			{
3806 				keystatus[SDL_SCANCODE_DOWN] = 0;
3807 				if ( rebindaction == -1 )
3808 				{
3809 					*inputPressed(joyimpulses[INJOY_DPAD_DOWN]) = 0;
3810 				}
3811 				draw_cursor = false;
3812 
3813 				Uint32 vIndex = 0;
3814 				for ( auto& option : displayedOptionToGamemode )
3815 				{
3816 					if ( option == multiplayerselect )
3817 					{
3818 						break;
3819 					}
3820 					++vIndex;
3821 				}
3822 				if ( vIndex >= nummodes - 1 )
3823 				{
3824 					multiplayerselect = displayedOptionToGamemode.at(0);
3825 				}
3826 				else
3827 				{
3828 					multiplayerselect = displayedOptionToGamemode.at(vIndex + 1);
3829 				}
3830 			}
3831 		}
3832 	}
3833 
3834 	// serial window.
3835 #if (!defined STEAMWORKS && !defined USE_EOS)
3836 	if ( intro && introstage == 1 && subwindow && !strcmp(subtext, language[3403]) && serialEnterWindow )
3837 	{
3838 		drawDepressed(subx1 + 8, suby1 + 32, subx2 - 8, suby1 + 56);
3839 		ttfPrintText(ttf12, subx1 + 16, suby1 + 40, serialInputText);
3840 
3841 		// enter character name
3842 		if ( serialVerifyWindow == 0 && !SDL_IsTextInputActive() )
3843 		{
3844 			inputstr = serialInputText;
3845 			SDL_StartTextInput();
3846 		}
3847 		//strncpy(stats[0]->name,inputstr,16);
3848 		inputlen = 63;
3849 
3850 		if ( serialVerifyWindow > 0 )
3851 		{
3852 			if ( serialVerifyWindow % 10 < 3 )
3853 			{
3854 				ttfPrintTextFormattedColor(ttf12, subx1 + 16, suby2 - 20, uint32ColorOrange(*mainsurface), "Verifying");
3855 			}
3856 			else if ( serialVerifyWindow % 10 < 5 )
3857 			{
3858 				ttfPrintTextFormattedColor(ttf12, subx1 + 16, suby2 - 20, uint32ColorOrange(*mainsurface), "Verifying.");
3859 			}
3860 			else if ( serialVerifyWindow % 10 < 7 )
3861 			{
3862 				ttfPrintTextFormattedColor(ttf12, subx1 + 16, suby2 - 20, uint32ColorOrange(*mainsurface), "Verifying..");
3863 			}
3864 			else if ( serialVerifyWindow % 10 < 10 )
3865 			{
3866 				ttfPrintTextFormattedColor(ttf12, subx1 + 16, suby2 - 20, uint32ColorOrange(*mainsurface), "Verifying...");
3867 			}
3868 			if ( ticks % (TICKS_PER_SECOND / 2) == 0 )
3869 			{
3870 				++serialVerifyWindow;
3871 				if ( serialVerifyWindow >= 25 )
3872 				{
3873 					std::string serial = serialInputText;
3874 
3875 					// compute hash
3876 					if ( !serial.empty() )
3877 					{
3878 						std::size_t DLCHash = serialHash(serial);
3879 						if ( DLCHash == 144425 )
3880 						{
3881 							printlog("[LICENSE]: Myths and Outcasts DLC license key found.");
3882 							enabledDLCPack1 = true;
3883 							windowSerialResult(1);
3884 						}
3885 						else if ( DLCHash == 135398 )
3886 						{
3887 							printlog("[LICENSE]: Legends and Pariahs DLC license key found.");
3888 							enabledDLCPack2 = true;
3889 							windowSerialResult(2);
3890 						}
3891 						else
3892 						{
3893 							printlog("[LICENSE]: DLC license key invalid.");
3894 							windowSerialResult(0);
3895 						}
3896 					}
3897 					else
3898 					{
3899 						windowSerialResult(0);
3900 					}
3901 				}
3902 			}
3903 		}
3904 		else if ( (ticks - cursorflash) % TICKS_PER_SECOND < TICKS_PER_SECOND / 2 )
3905 		{
3906 			int x;
3907 			TTF_SizeUTF8(ttf12, serialInputText, &x, NULL);
3908 			ttfPrintText(ttf12, subx1 + 16 + x, suby1 + 40, "_");
3909 		}
3910 	}
3911 #endif
3912 
3913 	// settings window
3914 	if ( settings_window == true )
3915 	{
3916 		drawWindowFancy(subx1 + 16, suby1 + 44, subx2 - 16, suby2 - 32);
3917 
3918 		int hovering_selection = -1; //0 to NUM_SERVER_FLAGS used for the game flags settings, e.g. are traps enabled, are cheats enabled, is minotaur enabled, etc.
3919 		SDL_Rect tooltip_box;
3920 
3921 		if ( *inputPressed(joyimpulses[INJOY_MENU_SETTINGS_NEXT]) && rebindaction == -1 )
3922 		{
3923 			*inputPressed(joyimpulses[INJOY_MENU_SETTINGS_NEXT]) = 0;;
3924 			changeSettingsTab(settings_tab + 1);
3925 		}
3926 		if ( *inputPressed(joyimpulses[INJOY_MENU_SETTINGS_PREV]) && rebindaction == -1 )
3927 		{
3928 			*inputPressed(joyimpulses[INJOY_MENU_SETTINGS_PREV]) = 0;
3929 			changeSettingsTab(settings_tab - 1);
3930 		}
3931 
3932 		// video tab
3933 		if ( settings_tab == SETTINGS_VIDEO_TAB )
3934 		{
3935 			// resolution
3936 			ttfPrintText(ttf12, subx1 + 24, suby1 + 60, language[1338]);
3937 			c=0;
3938 			for ( auto cur : resolutions )
3939 			{
3940 				int width, height;
3941 				std::tie (width, height) = cur;
3942 				if ( settings_xres == width && settings_yres == height )
3943 				{
3944 					ttfPrintTextFormatted(ttf12, subx1 + 32, suby1 + 84 + c * 16, "[o] %dx%d", width, height);
3945 				}
3946 				else
3947 				{
3948 					ttfPrintTextFormatted(ttf12, subx1 + 32, suby1 + 84 + c * 16, "[ ] %dx%d", width, height);
3949 				}
3950 				if ( mousestatus[SDL_BUTTON_LEFT] )
3951 				{
3952 					if ( omousex >= subx1 + 38 && omousex < subx1 + 62 )
3953 					{
3954 						if ( omousey >= suby1 + 84 + c * 16 && omousey < suby1 + 96 + c * 16 )
3955 						{
3956 							mousestatus[SDL_BUTTON_LEFT] = 0;
3957 							settings_xres = width;
3958 							settings_yres = height;
3959 							resolutionChanged = true;
3960 						}
3961 					}
3962 				}
3963 				c++;
3964 			}
3965 
3966 			// extra options
3967 			ttfPrintText(ttf12, subx1 + 224, suby1 + 60, language[1339]);
3968 			if ( settings_smoothlighting )
3969 			{
3970 				ttfPrintTextFormatted(ttf12, subx1 + 236, suby1 + 84, "[x] %s", language[1340]);
3971 			}
3972 			else
3973 			{
3974 				ttfPrintTextFormatted(ttf12, subx1 + 236, suby1 + 84, "[ ] %s", language[1340]);
3975 			}
3976 			if ( settings_fullscreen )
3977 			{
3978 				ttfPrintTextFormatted(ttf12, subx1 + 236, suby1 + 108, "[x] %s", language[1341]);
3979 			}
3980 			else
3981 			{
3982 				ttfPrintTextFormatted(ttf12, subx1 + 236, suby1 + 108, "[ ] %s", language[1341]);
3983 			}
3984 			if ( settings_shaking )
3985 			{
3986 				ttfPrintTextFormatted(ttf12, subx1 + 236, suby1 + 132, "[x] %s", language[1342]);
3987 			}
3988 			else
3989 			{
3990 				ttfPrintTextFormatted(ttf12, subx1 + 236, suby1 + 132, "[ ] %s", language[1342]);
3991 			}
3992 			if ( settings_bobbing )
3993 			{
3994 				ttfPrintTextFormatted(ttf12, subx1 + 236, suby1 + 156, "[x] %s", language[1343]);
3995 			}
3996 			else
3997 			{
3998 				ttfPrintTextFormatted(ttf12, subx1 + 236, suby1 + 156, "[ ] %s", language[1343]);
3999 			}
4000 			if ( settings_spawn_blood )
4001 			{
4002 				ttfPrintTextFormatted(ttf12, subx1 + 236, suby1 + 180, "[x] %s", language[1344]);
4003 			}
4004 			else
4005 			{
4006 				ttfPrintTextFormatted(ttf12, subx1 + 236, suby1 + 180, "[ ] %s", language[1344]);
4007 			}
4008 			if ( settings_colorblind )
4009 			{
4010 				ttfPrintTextFormatted(ttf12, subx1 + 236, suby1 + 204, "[x] %s", language[1345]);
4011 			}
4012 			else
4013 			{
4014 				ttfPrintTextFormatted(ttf12, subx1 + 236, suby1 + 204, "[ ] %s", language[1345]);
4015 			}
4016 			if ( settings_light_flicker )
4017 			{
4018 				ttfPrintTextFormatted(ttf12, subx1 + 236, suby1 + 228, "[x] %s", language[2967]);
4019 			}
4020 			else
4021 			{
4022 				ttfPrintTextFormatted(ttf12, subx1 + 236, suby1 + 228, "[ ] %s", language[2967]);
4023 			}
4024 			if ( settings_vsync )
4025 			{
4026 				ttfPrintTextFormatted(ttf12, subx1 + 236, suby1 + 252, "[x] %s", language[3011]);
4027 			}
4028 			else
4029 			{
4030 				ttfPrintTextFormatted(ttf12, subx1 + 236, suby1 + 252, "[ ] %s", language[3011]);
4031 			}
4032 			if ( settings_status_effect_icons )
4033 			{
4034 				ttfPrintTextFormatted(ttf12, subx1 + 236, suby1 + 276, "[x] %s", language[3357]);
4035 			}
4036 			else
4037 			{
4038 				ttfPrintTextFormatted(ttf12, subx1 + 236, suby1 + 276, "[ ] %s", language[3357]);
4039 			}
4040 			if ( settings_borderless )
4041 			{
4042 				ttfPrintTextFormatted(ttf12, subx1 + 236, suby1 + 300, "[x] %s", language[3935]);
4043 			}
4044 			else
4045 			{
4046 				ttfPrintTextFormatted(ttf12, subx1 + 236, suby1 + 300, "[ ] %s", language[3935]);
4047 			}
4048 
4049 			if ( mousestatus[SDL_BUTTON_LEFT] )
4050 			{
4051 				if ( omousex >= subx1 + 242 && omousex < subx1 + 266 )
4052 				{
4053 					if ( omousey >= suby1 + 84 && omousey < suby1 + 84 + 12 )
4054 					{
4055 						mousestatus[SDL_BUTTON_LEFT] = 0;
4056 						settings_smoothlighting = (settings_smoothlighting == 0);
4057 					}
4058 					else if ( omousey >= suby1 + 108 && omousey < suby1 + 108 + 12 )
4059 					{
4060 						mousestatus[SDL_BUTTON_LEFT] = 0;
4061 						settings_fullscreen = (settings_fullscreen == 0);
4062 					}
4063 					else if ( omousey >= suby1 + 132 && omousey < suby1 + 132 + 12 )
4064 					{
4065 						mousestatus[SDL_BUTTON_LEFT] = 0;
4066 						settings_shaking = (settings_shaking == 0);
4067 					}
4068 					else if ( omousey >= suby1 + 156 && omousey < suby1 + 156 + 12 )
4069 					{
4070 						mousestatus[SDL_BUTTON_LEFT] = 0;
4071 						settings_bobbing = (settings_bobbing == 0);
4072 					}
4073 					else if ( omousey >= suby1 + 180 && omousey < suby1 + 180 + 12 )
4074 					{
4075 						mousestatus[SDL_BUTTON_LEFT] = 0;
4076 						settings_spawn_blood = (settings_spawn_blood == 0);
4077 					}
4078 					else if ( omousey >= suby1 + 204 && omousey < suby1 + 204 + 12 )
4079 					{
4080 						mousestatus[SDL_BUTTON_LEFT] = 0;
4081 						settings_colorblind = (settings_colorblind == false);
4082 					}
4083 					else if ( omousey >= suby1 + 228 && omousey < suby1 + 228 + 12 )
4084 					{
4085 						mousestatus[SDL_BUTTON_LEFT] = 0;
4086 						settings_light_flicker = (settings_light_flicker == false);
4087 					}
4088 					else if ( omousey >= suby1 + 252 && omousey < suby1 + 252 + 12 )
4089 					{
4090 						mousestatus[SDL_BUTTON_LEFT] = 0;
4091 						settings_vsync = (settings_vsync == false);
4092 					}
4093 					else if ( omousey >= suby1 + 276 && omousey < suby1 + 276 + 12 )
4094 					{
4095 						mousestatus[SDL_BUTTON_LEFT] = 0;
4096 						settings_status_effect_icons = (settings_status_effect_icons == false);
4097 					}
4098 					else if ( omousey >= suby1 + 300 && omousey < suby1 + 300 + 12 )
4099 					{
4100 						mousestatus[SDL_BUTTON_LEFT] = 0;
4101 						settings_borderless = (settings_borderless == false);
4102 					}
4103 				}
4104 			}
4105 
4106 			// minimap options
4107 			ttfPrintText(ttf12, subx1 + 498, suby1 + 60, language[3022]);
4108 
4109 			// total scale
4110 			ttfPrintText(ttf12, subx1 + 498, suby1 + 84, language[3025]);
4111 			doSlider(subx1 + 498, suby1 + 84 + 24, 10, 2, 16, 1, (int*)(&settings_minimap_scale));
4112 
4113 			// objects (players/allies/minotaur) scale
4114 			ttfPrintText(ttf12, subx1 + 498, suby1 + 130, language[3026]);
4115 			doSlider(subx1 + 498, suby1 + 130 + 24, 10, 0, 4, 1, (int*)(&settings_minimap_object_zoom));
4116 
4117 			// foreground transparency
4118 			ttfPrintText(ttf12, subx1 + 498, suby1 + 176, language[3023]);
4119 			doSlider(subx1 + 498, suby1 + 176 + 24, 10, 0, 100, 1, (int*)(&settings_minimap_transparency_foreground));
4120 
4121 			// background transparency
4122 			ttfPrintText(ttf12, subx1 + 498, suby1 + 222, language[3024]);
4123 			doSlider(subx1 + 498, suby1 + 222 + 24, 10, 0, 100, 1, (int*)(&settings_minimap_transparency_background));
4124 
4125 			// UI options
4126 			ttfPrintText(ttf12, subx1 + 498, suby1 + 276, language[3034]);
4127 
4128 			if ( settings_uiscale_charactersheet )
4129 			{
4130 				ttfPrintTextFormatted(ttf12, subx1 + 498, suby1 + 300, "[x] %s", language[3027]);
4131 			}
4132 			else
4133 			{
4134 				ttfPrintTextFormatted(ttf12, subx1 + 498, suby1 + 300, "[ ] %s", language[3027]);
4135 			}
4136 			if ( settings_uiscale_skillspage )
4137 			{
4138 				ttfPrintTextFormatted(ttf12, subx1 + 498, suby1 + 324, "[x] %s", language[3028]);
4139 			}
4140 			else
4141 			{
4142 				ttfPrintTextFormatted(ttf12, subx1 + 498, suby1 + 324, "[ ] %s", language[3028]);
4143 			}
4144 			if ( settings_hide_statusbar )
4145 			{
4146 				ttfPrintTextFormatted(ttf12, subx1 + 498, suby1 + 348, "[x] %s", language[3033]);
4147 			}
4148 			else
4149 			{
4150 				ttfPrintTextFormatted(ttf12, subx1 + 498, suby1 + 348, "[ ] %s", language[3033]);
4151 			}
4152 			if ( settings_hide_playertags )
4153 			{
4154 				ttfPrintTextFormatted(ttf12, subx1 + 498, suby1 + 372, "[x] %s", language[3136]);
4155 			}
4156 			else
4157 			{
4158 				ttfPrintTextFormatted(ttf12, subx1 + 498, suby1 + 372, "[ ] %s", language[3136]);
4159 			}
4160 			if ( settings_show_skill_values )
4161 			{
4162 				ttfPrintTextFormatted(ttf12, subx1 + 498, suby1 + 396, "[x] %s", language[3159]);
4163 			}
4164 			else
4165 			{
4166 				ttfPrintTextFormatted(ttf12, subx1 + 498, suby1 + 396, "[ ] %s", language[3159]);
4167 			}
4168 
4169 			if ( mousestatus[SDL_BUTTON_LEFT] )
4170 			{
4171 				if ( omousex >= subx1 + 498 && omousex < subx1 + 522 )
4172 				{
4173 					if ( omousey >= suby1 + 300 && omousey < suby1 + 300 + 12 )
4174 					{
4175 						mousestatus[SDL_BUTTON_LEFT] = 0;
4176 						settings_uiscale_charactersheet = (settings_uiscale_charactersheet == 0);
4177 					}
4178 					else if ( omousey >= suby1 + 324 && omousey < suby1 + 324 + 12 )
4179 					{
4180 						mousestatus[SDL_BUTTON_LEFT] = 0;
4181 						settings_uiscale_skillspage = (settings_uiscale_skillspage == 0);
4182 					}
4183 					else if ( omousey >= suby1 + 348 && omousey < suby1 + 348 + 12 )
4184 					{
4185 						mousestatus[SDL_BUTTON_LEFT] = 0;
4186 						settings_hide_statusbar = (settings_hide_statusbar == 0);
4187 					}
4188 					else if ( omousey >= suby1 + 372 && omousey < suby1 + 372 + 12 )
4189 					{
4190 						mousestatus[SDL_BUTTON_LEFT] = 0;
4191 						settings_hide_playertags = (settings_hide_playertags == 0);
4192 					}
4193 					else if ( omousey >= suby1 + 396 && omousey < suby1 + 396 + 12 )
4194 					{
4195 						mousestatus[SDL_BUTTON_LEFT] = 0;
4196 						settings_show_skill_values = (settings_show_skill_values == 0);
4197 					}
4198 				}
4199 			}
4200 			// UI scale sliders
4201 			ttfPrintText(ttf12, subx1 + 498, suby2 - 220, language[3029]);
4202 			doSliderF(subx1 + 498, suby2 - 220 + 24, 10, 1.f, 2.f, 0.25, &settings_uiscale_hotbar);
4203 			ttfPrintText(ttf12, subx1 + 498, suby2 - 174, language[3030]);
4204 			doSliderF(subx1 + 498, suby2 - 174 + 24, 10, 1.f, 2.f, 0.25, &settings_uiscale_chatlog);
4205 			ttfPrintText(ttf12, subx1 + 498, suby2 - 128, language[3031]);
4206 			doSliderF(subx1 + 498, suby2 - 128 + 24, 10, 1.f, 2.f, 0.25, &settings_uiscale_playerbars);
4207 			ttfPrintText(ttf12, subx1 + 498, suby2 - 80, language[3032]);
4208 			doSliderF(subx1 + 498, suby2 - 80 + 24, 10, 1.f, 2.f, 0.25, &settings_uiscale_inventory);
4209 
4210 			// fov slider
4211 			ttfPrintText(ttf12, subx1 + 24, suby2 - 174, language[1346]);
4212 			doSlider(subx1 + 24, suby2 - 148, 14, 40, 100, 1, (int*)(&settings_fov));
4213 
4214 			// gamma slider
4215 			ttfPrintText(ttf12, subx1 + 24, suby2 - 128, language[1347]);
4216 			doSliderF(subx1 + 24, suby2 - 104, 14, 0.25, 2.f, 0.25, &settings_gamma);
4217 
4218 			// fps slider
4219 			ttfPrintText(ttf12, subx1 + 24, suby2 - 80, language[2411]);
4220 			doSlider(subx1 + 24, suby2 - 56, 14, 60, 144, 1, (int*)(&settings_fps));
4221 		}
4222 
4223 		// audio tab
4224 		if ( settings_tab == SETTINGS_AUDIO_TAB )
4225 		{
4226 			ttfPrintText(ttf12, subx1 + 24, suby1 + 60, language[1348]);
4227 			doSlider(subx1 + 24, suby1 + 84, 15, 0, 128, 0, &settings_sfxvolume);
4228 
4229 			ttfPrintText(ttf12, subx1 + 24, suby1 + 108, language[3972]);
4230 			doSlider(subx1 + 24, suby1 + 132, 15, 0, 128, 0, &settings_sfxAmbientVolume);
4231 
4232 			ttfPrintText(ttf12, subx1 + 24, suby1 + 156, language[3973]);
4233 			doSlider(subx1 + 24, suby1 + 180, 15, 0, 128, 0, &settings_sfxEnvironmentVolume);
4234 
4235 			ttfPrintText(ttf12, subx1 + 24, suby1 + 204, language[1349]);
4236 			doSlider(subx1 + 24, suby1 + 228, 15, 0, 128, 0, &settings_musvolume);
4237 
4238 			if ( settings_minimap_ping_mute )
4239 			{
4240 				ttfPrintTextFormatted(ttf12, subx1 + 24, suby1 + 264, "[x] %s", language[3012]);
4241 			}
4242 			else
4243 			{
4244 				ttfPrintTextFormatted(ttf12, subx1 + 24, suby1 + 264, "[ ] %s", language[3012]);
4245 			}
4246 			if ( settings_mute_audio_on_focus_lost )
4247 			{
4248 				ttfPrintTextFormatted(ttf12, subx1 + 24, suby1 + 288, "[x] %s", language[3158]);
4249 			}
4250 			else
4251 			{
4252 				ttfPrintTextFormatted(ttf12, subx1 + 24, suby1 + 288, "[ ] %s", language[3158]);
4253 			}
4254 			if ( settings_mute_player_monster_sounds )
4255 			{
4256 				ttfPrintTextFormatted(ttf12, subx1 + 24, suby1 + 312, "[x] %s", language[3371]);
4257 			}
4258 			else
4259 			{
4260 				ttfPrintTextFormatted(ttf12, subx1 + 24, suby1 + 312, "[ ] %s", language[3371]);
4261 			}
4262 			if ( mousestatus[SDL_BUTTON_LEFT] )
4263 			{
4264 				if ( omousex >= subx1 + 30 && omousex < subx1 + 54 )
4265 				{
4266 					if ( omousey >= suby1 + 168 && omousey < suby1 + 264 + 12 )
4267 					{
4268 						mousestatus[SDL_BUTTON_LEFT] = 0;
4269 						settings_minimap_ping_mute = (settings_minimap_ping_mute == false);
4270 					}
4271 					else if ( omousey >= suby1 + 192 && omousey < suby1 + 288 + 12 )
4272 					{
4273 						mousestatus[SDL_BUTTON_LEFT] = 0;
4274 						settings_mute_audio_on_focus_lost = (settings_mute_audio_on_focus_lost == false);
4275 					}
4276 					else if ( omousey >= suby1 + 216 && omousey < suby1 + 312 + 12 )
4277 					{
4278 						mousestatus[SDL_BUTTON_LEFT] = 0;
4279 						settings_mute_player_monster_sounds = (settings_mute_player_monster_sounds == false);
4280 					}
4281 				}
4282 			}
4283 		}
4284 
4285 		// keyboard tab
4286 		if ( settings_tab == SETTINGS_KEYBOARD_TAB )
4287 		{
4288 			ttfPrintText(ttf12, subx1 + 24, suby1 + 60, language[1350]);
4289 
4290 			bool rebindingkey = false;
4291 			if ( rebindkey != -1 )
4292 			{
4293 				rebindingkey = true;
4294 			}
4295 
4296 			for ( int c = 0; c < NUMIMPULSES; c++ )
4297 			{
4298 				if ( c < 14 )
4299 				{
4300 					ttfPrintText(ttf12, subx1 + 24, suby1 + 84 + 16 * c, language[1351 + c]);
4301 				}
4302 				else if ( c < 16 )
4303 				{
4304 					ttfPrintText(ttf12, subx1 + 24, suby1 + 84 + 16 * c, language[1940 + (c - 14)]);
4305 				}
4306 				else if ( c < 22 )
4307 				{
4308 					ttfPrintText(ttf12, subx1 + 24, suby1 + 84 + 16 * c, language[1986 + (c - 16)]);
4309 				}
4310 				else if ( c < 25 )
4311 				{
4312 					ttfPrintText(ttf12, subx1 + 24, suby1 + 84 + 16 * c, language[3901 + (c - 22)]);
4313 				}
4314 				if ( mousestatus[SDL_BUTTON_LEFT] && !rebindingkey )
4315 				{
4316 					if ( omousex >= subx1 + 24 && omousex < subx2 - 24 )
4317 					{
4318 						if ( omousey >= suby1 + 84 + c * 16 && omousey < suby1 + 96 + c * 16 )
4319 						{
4320 							mousestatus[SDL_BUTTON_LEFT] = 0;
4321 							lastkeypressed = 0;
4322 							rebindingkey = true;
4323 							rebindkey = c;
4324 						}
4325 					}
4326 				}
4327 				if ( c != rebindkey )
4328 				{
4329 					if ( !strcmp(getInputName(settings_impulses[c]), "Unassigned key" ))
4330 					{
4331 						ttfPrintTextColor(ttf12, subx1 + 256, suby1 + 84 + c * 16, uint32ColorBaronyBlue(*mainsurface), true, getInputName(settings_impulses[c]));
4332 					}
4333 					else if ( !strcmp(getInputName(settings_impulses[c]), "Unknown key") || !strcmp(getInputName(settings_impulses[c]), "Unknown trigger") )
4334 					{
4335 						ttfPrintTextColor(ttf12, subx1 + 256, suby1 + 84 + c * 16, uint32ColorRed(*mainsurface), true, getInputName(settings_impulses[c]));
4336 					}
4337 					else
4338 					{
4339 						ttfPrintText(ttf12, subx1 + 256, suby1 + 84 + c * 16, getInputName(settings_impulses[c]));
4340 					}
4341 				}
4342 				else
4343 				{
4344 					ttfPrintTextColor(ttf12, subx1 + 256, suby1 + 84 + c * 16, uint32ColorGreen(*mainsurface), true, "...");
4345 				}
4346 			}
4347 
4348 			if ( rebindkey != -1 && lastkeypressed )
4349 			{
4350 				if ( lastkeypressed == SDL_SCANCODE_ESCAPE )
4351 				{
4352 					keystatus[SDL_SCANCODE_ESCAPE] = 0;
4353 					lastkeypressed = 0;
4354 					rebindkey = -1;
4355 				}
4356 				else
4357 				{
4358 					settings_impulses[rebindkey] = lastkeypressed;
4359 					if ( lastkeypressed == 283 )
4360 					{
4361 						mousestatus[SDL_BUTTON_LEFT] = 0;  // fixes mouse-left not registering bug
4362 					}
4363 					rebindkey = -1;
4364 				}
4365 			}
4366 		}
4367 
4368 		// mouse tab
4369 		if ( settings_tab == SETTINGS_MOUSE_TAB )
4370 		{
4371 			ttfPrintText(ttf12, subx1 + 24, suby1 + 60, language[1365]);
4372 			doSliderF(subx1 + 24, suby1 + 84, 11, 0, 128, 1, &settings_mousespeed);
4373 
4374 			// checkboxes
4375 			if ( settings_reversemouse )
4376 			{
4377 				ttfPrintTextFormatted(ttf12, subx1 + 24, suby1 + 108, "[x] %s", language[1366]);
4378 			}
4379 			else
4380 			{
4381 				ttfPrintTextFormatted(ttf12, subx1 + 24, suby1 + 108, "[ ] %s", language[1366]);
4382 			}
4383 			if ( settings_smoothmouse )
4384 			{
4385 				ttfPrintTextFormatted(ttf12, subx1 + 24, suby1 + 132, "[x] %s", language[1367]);
4386 			}
4387 			else
4388 			{
4389 				ttfPrintTextFormatted(ttf12, subx1 + 24, suby1 + 132, "[ ] %s", language[1367]);
4390 			}
4391 			if ( settings_disablemouserotationlimit )
4392 			{
4393 				ttfPrintTextFormatted(ttf12, subx1 + 24, suby1 + 156, "[x] %s", language[3918]);
4394 			}
4395 			else
4396 			{
4397 				ttfPrintTextFormatted(ttf12, subx1 + 24, suby1 + 156, "[ ] %s", language[3918]);
4398 			}
4399 			if ( mousestatus[SDL_BUTTON_LEFT] )
4400 			{
4401 				if ( omousex >= subx1 + 30 && omousex < subx1 + 54 )
4402 				{
4403 					if ( omousey >= suby1 + 108 && omousey < suby1 + 120 )
4404 					{
4405 						mousestatus[SDL_BUTTON_LEFT] = 0;
4406 						settings_reversemouse = (settings_reversemouse == 0);
4407 					}
4408 					if ( omousey >= suby1 + 132 && omousey < suby1 + 144 )
4409 					{
4410 						mousestatus[SDL_BUTTON_LEFT] = 0;
4411 						settings_smoothmouse = (settings_smoothmouse == 0);
4412 					}
4413 					if ( omousey >= suby1 + 156 && omousey < suby1 + 168 )
4414 					{
4415 						mousestatus[SDL_BUTTON_LEFT] = 0;
4416 						settings_disablemouserotationlimit = (settings_disablemouserotationlimit == 0);
4417 					}
4418 				}
4419 			}
4420 		}
4421 
4422 		//Gamepad tab
4423 		if (settings_tab == SETTINGS_GAMEPAD_BINDINGS_TAB)
4424 		{
4425 			SDL_Rect startPos;
4426 			startPos.x = subx1 + 24;
4427 			startPos.y = suby1 + 60;
4428 			SDL_Rect currentPos = startPos;
4429 			ttfPrintText(ttf8, currentPos.x, currentPos.y, language[1996]);
4430 			currentPos.y += 24;
4431 
4432 			bool rebindingaction = false;
4433 			if (rebindaction != -1)
4434 			{
4435 				rebindingaction = true;
4436 			}
4437 
4438 			//Print out the bi-functional bindings.
4439 			for ( int c = 0; c < INDEX_JOYBINDINGS_START_MENU; ++c, currentPos.y += 12 )
4440 			{
4441 				printJoybindingNames(currentPos, c, rebindingaction);
4442 			}
4443 
4444 			//Print out the menu-exclusive bindings.
4445 			currentPos.y += 12;
4446 			drawLine(subx1 + 24, currentPos.y - 6, subx2 - 24, currentPos.y - 6, uint32ColorGray(*mainsurface), 255);
4447 			ttfPrintText(ttf8, currentPos.x, currentPos.y, language[1994]);
4448 			currentPos.y += 18;
4449 			for ( c = INDEX_JOYBINDINGS_START_MENU; c < INDEX_JOYBINDINGS_START_GAME; ++c, currentPos.y += 12 )
4450 			{
4451 				printJoybindingNames(currentPos, c, rebindingaction);
4452 			}
4453 
4454 			//Print out the game-exclusive bindings.
4455 			currentPos.y += 12;
4456 			drawLine(subx1 + 24, currentPos.y - 6, subx2 - 24, currentPos.y - 6, uint32ColorGray(*mainsurface), 255);
4457 			ttfPrintText(ttf8, currentPos.x, currentPos.y, language[1995]);
4458 			currentPos.y += 18;
4459 			for ( c = INDEX_JOYBINDINGS_START_GAME; c < NUM_JOY_IMPULSES; ++c, currentPos.y += 12 )
4460 			{
4461 				printJoybindingNames(currentPos, c, rebindingaction);
4462 			}
4463 
4464 			if (rebindaction != -1 && lastkeypressed)
4465 			{
4466 
4467 				if (lastkeypressed >= 299)   /* Is a joybutton. */
4468 				{
4469 					settings_joyimpulses[rebindaction] = lastkeypressed;
4470 					*inputPressed(lastkeypressed) = 0; //To prevent bugs where the button will still be treated as pressed after assigning it, potentially doing wonky things.
4471 					rebindaction = -1;
4472 				}
4473 				else
4474 				{
4475 					if (lastkeypressed == SDL_SCANCODE_ESCAPE)
4476 					{
4477 						keystatus[SDL_SCANCODE_ESCAPE] = 0;
4478 					}
4479 					lastkeypressed = 0;
4480 					rebindaction = -1;
4481 				}
4482 			}
4483 		}
4484 
4485 		//General gamepad settings
4486 		if (settings_tab == SETTINGS_GAMEPAD_SETTINGS_TAB)
4487 		{
4488 			int current_option_x = subx1 + 24;
4489 			int current_option_y = suby1 + 60;
4490 
4491 			//Checkboxes.
4492 			if (settings_gamepad_leftx_invert)
4493 			{
4494 				ttfPrintTextFormatted(ttf12, current_option_x, current_option_y, "[x] %s", language[2401]);
4495 			}
4496 			else
4497 			{
4498 				ttfPrintTextFormatted(ttf12, current_option_x, current_option_y, "[ ] %s", language[2401]);
4499 			}
4500 
4501 			if (mousestatus[SDL_BUTTON_LEFT] && mouseInBounds(current_option_x, current_option_x + strlen("[x]")*TTF12_WIDTH, current_option_y, current_option_y + TTF12_HEIGHT))
4502 			{
4503 				mousestatus[SDL_BUTTON_LEFT] = 0;
4504 				settings_gamepad_leftx_invert = !settings_gamepad_leftx_invert;
4505 			}
4506 
4507 			current_option_y += 24;
4508 
4509 			if (settings_gamepad_lefty_invert)
4510 			{
4511 				ttfPrintTextFormatted(ttf12, current_option_x, current_option_y, "[x] %s", language[2402]);
4512 			}
4513 			else
4514 			{
4515 				ttfPrintTextFormatted(ttf12, current_option_x, current_option_y, "[ ] %s", language[2402]);
4516 			}
4517 
4518 			if (mousestatus[SDL_BUTTON_LEFT] && mouseInBounds(current_option_x, current_option_x + strlen("[x]")*TTF12_WIDTH, current_option_y, current_option_y + TTF12_HEIGHT))
4519 			{
4520 				mousestatus[SDL_BUTTON_LEFT] = 0;
4521 				settings_gamepad_lefty_invert = !settings_gamepad_lefty_invert;
4522 			}
4523 
4524 			current_option_y += 24;
4525 
4526 			if (settings_gamepad_rightx_invert)
4527 			{
4528 				ttfPrintTextFormatted(ttf12, current_option_x, current_option_y, "[x] %s", language[2403]);
4529 			}
4530 			else
4531 			{
4532 				ttfPrintTextFormatted(ttf12, current_option_x, current_option_y, "[ ] %s", language[2403]);
4533 			}
4534 
4535 			if (mousestatus[SDL_BUTTON_LEFT] && mouseInBounds(current_option_x, current_option_x + strlen("[x]")*TTF12_WIDTH, current_option_y, current_option_y + TTF12_HEIGHT))
4536 			{
4537 				mousestatus[SDL_BUTTON_LEFT] = 0;
4538 				settings_gamepad_rightx_invert = !settings_gamepad_rightx_invert;
4539 			}
4540 
4541 			current_option_y += 24;
4542 
4543 			if (settings_gamepad_righty_invert)
4544 			{
4545 				ttfPrintTextFormatted(ttf12, current_option_x, current_option_y, "[x] %s", language[2404]);
4546 			}
4547 			else
4548 			{
4549 				ttfPrintTextFormatted(ttf12, current_option_x, current_option_y, "[ ] %s", language[2404]);
4550 			}
4551 
4552 			if (mousestatus[SDL_BUTTON_LEFT] && mouseInBounds(current_option_x, current_option_x + strlen("[x]")*TTF12_WIDTH, current_option_y, current_option_y + TTF12_HEIGHT))
4553 			{
4554 				mousestatus[SDL_BUTTON_LEFT] = 0;
4555 				settings_gamepad_righty_invert = !settings_gamepad_righty_invert;
4556 			}
4557 
4558 			current_option_y += 24;
4559 
4560 			if (settings_gamepad_menux_invert)
4561 			{
4562 				ttfPrintTextFormatted(ttf12, current_option_x, current_option_y, "[x] %s", language[2405]);
4563 			}
4564 			else
4565 			{
4566 				ttfPrintTextFormatted(ttf12, current_option_x, current_option_y, "[ ] %s", language[2405]);
4567 			}
4568 
4569 			if (mousestatus[SDL_BUTTON_LEFT] && mouseInBounds(current_option_x, current_option_x + strlen("[x]")*TTF12_WIDTH, current_option_y, current_option_y + TTF12_HEIGHT))
4570 			{
4571 				mousestatus[SDL_BUTTON_LEFT] = 0;
4572 				settings_gamepad_menux_invert = !settings_gamepad_menux_invert;
4573 			}
4574 
4575 			current_option_y += 24;
4576 
4577 			if (settings_gamepad_menuy_invert)
4578 			{
4579 				ttfPrintTextFormatted(ttf12, current_option_x, current_option_y, "[x] %s", language[2406]);
4580 			}
4581 			else
4582 			{
4583 				ttfPrintTextFormatted(ttf12, current_option_x, current_option_y, "[ ] %s", language[2406]);
4584 			}
4585 
4586 			if (mousestatus[SDL_BUTTON_LEFT] && mouseInBounds(current_option_x, current_option_x + strlen("[x]")*TTF12_WIDTH, current_option_y, current_option_y + TTF12_HEIGHT))
4587 			{
4588 				mousestatus[SDL_BUTTON_LEFT] = 0;
4589 				settings_gamepad_menuy_invert = !settings_gamepad_menuy_invert;
4590 			}
4591 
4592 			current_option_y += 24;
4593 
4594 			ttfPrintText(ttf12, current_option_x, current_option_y, language[2407]);
4595 			current_option_y += 24;
4596 			//doSlider(current_option_x, current_option_y, 11, 1, 2000, 200, &settings_gamepad_rightx_sensitivity, font8x8_bmp, 12); //Doesn't like any fonts besides the default.
4597 			doSlider(current_option_x, current_option_y, 11, 1, 4096, 100, &settings_gamepad_rightx_sensitivity);
4598 
4599 			current_option_y += 24;
4600 
4601 			ttfPrintText(ttf12, current_option_x, current_option_y, language[2408]);
4602 			current_option_y += 24;
4603 			//doSlider(current_option_x, current_option_y, 11, 1, 2000, 200, &settings_gamepad_righty_sensitivity, font8x8_bmp, 12);
4604 			doSlider(current_option_x, current_option_y, 11, 1, 4096, 100, &settings_gamepad_righty_sensitivity);
4605 
4606 			current_option_y += 24;
4607 
4608 			ttfPrintText(ttf12, current_option_x, current_option_y, language[2409]);
4609 			current_option_y += 24;
4610 			//doSlider(current_option_x, current_option_y, 11, 1, 2000, 200, &settings_gamepad_menux_sensitivity, font8x8_bmp, 12);
4611 			doSlider(current_option_x, current_option_y, 11, 1, 4096, 100, &settings_gamepad_menux_sensitivity);
4612 
4613 			current_option_y += 24;
4614 
4615 			ttfPrintText(ttf12, current_option_x, current_option_y, language[2410]);
4616 			current_option_y += 24;
4617 			//doSlider(current_option_x, current_option_y, 11, 1, 2000, 200, &settings_gamepad_menuy_sensitivity, font8x8_bmp, 12);
4618 			doSlider(current_option_x, current_option_y, 11, 1, 4096, 100, &settings_gamepad_menuy_sensitivity);
4619 		}
4620 
4621 		// miscellaneous options
4622 		if (settings_tab == SETTINGS_MISC_TAB)
4623 		{
4624 			int current_x = subx1;
4625 			int current_y = suby1 + 60;
4626 
4627 			ttfPrintText(ttf12, subx1 + 24, current_y, language[1371]);
4628 			current_y += 24;
4629 
4630 			int options_start_y = current_y;
4631 			if ( settings_broadcast )
4632 			{
4633 				ttfPrintTextFormatted(ttf12, subx1 + 36, current_y, "[x] %s", language[1372]);
4634 			}
4635 			else
4636 			{
4637 				ttfPrintTextFormatted(ttf12, subx1 + 36, current_y, "[ ] %s", language[1372]);
4638 			}
4639 			current_y += 16;
4640 			if ( settings_nohud )
4641 			{
4642 				ttfPrintTextFormatted(ttf12, subx1 + 36, current_y, "[x] %s", language[1373]);
4643 			}
4644 			else
4645 			{
4646 				ttfPrintTextFormatted(ttf12, subx1 + 36, current_y, "[ ] %s", language[1373]);
4647 			}
4648 			current_y += 16;
4649 			int hotbar_options_x = subx1 + 72 + 256;
4650 			int hotbar_options_y = current_y;
4651 			if ( settings_auto_hotbar_new_items )
4652 			{
4653 				ttfPrintTextFormatted(ttf12, subx1 + 36, current_y, "[x] %s", language[1374]);
4654 				int pad_x = hotbar_options_x;
4655 				int pad_y = hotbar_options_y;
4656 				drawWindowFancy(pad_x - 16, pad_y - 32, pad_x + 4 * 128 + 16, pad_y + 48 + 16);
4657 				ttfPrintTextFormatted(ttf12, pad_x, current_y - 16, "%s", language[2583]);
4658 				for ( int i = 0; i < (NUM_HOTBAR_CATEGORIES); ++i )
4659 				{
4660 					if ( settings_auto_hotbar_categories[i] == true )
4661 					{
4662 						ttfPrintTextFormatted(ttf12, pad_x, pad_y, "[x] %s", language[2571 + i]);
4663 					}
4664 					else
4665 					{
4666 						ttfPrintTextFormatted(ttf12, pad_x, pad_y, "[ ] %s", language[2571 + i]);
4667 					}
4668 					pad_x += 128;
4669 					if ( i == 3 || i == 7 )
4670 					{
4671 						pad_x = hotbar_options_x;
4672 						pad_y += 16;
4673 					}
4674 				}
4675 			}
4676 			else
4677 			{
4678 				ttfPrintTextFormatted(ttf12, subx1 + 36, current_y, "[ ] %s", language[1374]);
4679 			}
4680 
4681 			// autosort inventory categories
4682 			int autosort_options_x = subx1 + 72 + 256;
4683 			int autosort_options_y = current_y + 112;
4684 			int pad_x = autosort_options_x;
4685 			int pad_y = autosort_options_y;
4686 			drawWindowFancy(pad_x - 16, pad_y - 32, pad_x + 4 * 128 + 16, pad_y + 48 + 16);
4687 			ttfPrintTextFormatted(ttf12, pad_x, current_y - 16 + 112, "%s", language[2912]);
4688 
4689 			// draw the values for autosort
4690 			for ( int i = 0; i < (NUM_AUTOSORT_CATEGORIES); ++i )
4691 			{
4692 				ttfPrintTextFormatted(ttf12, pad_x, pad_y, "<");
4693 				Uint32 autosortColor = uint32ColorGreen(*mainsurface);
4694 				int padValue_x = pad_x;
4695 				if ( settings_autosort_inventory_categories[i] < 0 )
4696 				{
4697 					autosortColor = uint32ColorRed(*mainsurface);
4698 					padValue_x += 4; // centre the negative numbers.
4699 				}
4700 				else if ( settings_autosort_inventory_categories[i] == 0 )
4701 				{
4702 					autosortColor = uint32ColorWhite(*mainsurface);
4703 				}
4704 				ttfPrintTextFormattedColor(ttf12, padValue_x, pad_y, autosortColor, " %2d", settings_autosort_inventory_categories[i]);
4705 				if ( i == NUM_AUTOSORT_CATEGORIES - 1 )
4706 				{
4707 					ttfPrintTextFormatted(ttf12, pad_x, pad_y, "    > %s", language[2916]);
4708 				}
4709 				else
4710 				{
4711 					ttfPrintTextFormatted(ttf12, pad_x, pad_y, "    > %s", language[2571 + i]);
4712 				}
4713 				pad_x += 128;
4714 				if ( i == 3 || i == 7 )
4715 				{
4716 					pad_x = autosort_options_x;
4717 					pad_y += 16;
4718 				}
4719 			}
4720 
4721 			pad_x = autosort_options_x + (strlen(language[2912]) - 3) * (TTF12_WIDTH) + 8; // 3 chars from the end of string.
4722 			pad_y = autosort_options_y;
4723 			// hover text for autosort title text
4724 			if ( mouseInBounds(pad_x - 4, pad_x + 3 * TTF12_WIDTH + 8, current_y - 16 + 112, current_y - 16 + 124) )
4725 			{
4726 				tooltip_box.x = omousex - TTF12_WIDTH * 32;
4727 				tooltip_box.y = omousey - (TTF12_HEIGHT * 3 + 16);
4728 				tooltip_box.w = strlen(language[2914]) * TTF12_WIDTH + 8;
4729 				tooltip_box.h = TTF12_HEIGHT * 3 + 8;
4730 				drawTooltip(&tooltip_box);
4731 				ttfPrintTextFormatted(ttf12, tooltip_box.x + 4, tooltip_box.y + 4, language[2913]);
4732 				ttfPrintTextFormatted(ttf12, tooltip_box.x + 4, tooltip_box.y + 4 + TTF12_HEIGHT, language[2914]);
4733 				ttfPrintTextFormatted(ttf12, tooltip_box.x + 4, tooltip_box.y + 4 + TTF12_HEIGHT * 2, language[2915]);
4734 			}
4735 
4736 			current_y += 16;
4737 			if ( settings_auto_appraise_new_items )
4738 			{
4739 				ttfPrintTextFormatted(ttf12, subx1 + 36, current_y, "[x] %s", language[1997]);
4740 			}
4741 			else
4742 			{
4743 				ttfPrintTextFormatted(ttf12, subx1 + 36, current_y, "[ ] %s", language[1997]);
4744 			}
4745 			current_y += 16;
4746 			if ( settings_disable_messages )
4747 			{
4748 				ttfPrintTextFormatted(ttf12, subx1 + 36, current_y, "[x] %s", language[1536]);
4749 			}
4750 			else
4751 			{
4752 				ttfPrintTextFormatted(ttf12, subx1 + 36, current_y, "[ ] %s", language[1536]);
4753 			}
4754 			current_y += 16;
4755 			if ( settings_right_click_protect )
4756 			{
4757 				ttfPrintTextFormatted(ttf12, subx1 + 36, current_y, "[x] %s", language[1998]);
4758 			}
4759 			else
4760 			{
4761 				ttfPrintTextFormatted(ttf12, subx1 + 36, current_y, "[ ] %s", language[1998]);
4762 			}
4763 			current_y += 16;
4764 			if ( settings_hotbar_numkey_quick_add )
4765 			{
4766 				ttfPrintTextFormatted(ttf12, subx1 + 36, current_y, "[x] %s", language[2590]);
4767 			}
4768 			else
4769 			{
4770 				ttfPrintTextFormatted(ttf12, subx1 + 36, current_y, "[ ] %s", language[2590]);
4771 			}
4772 			current_y += 16;
4773 			if ( settings_lock_right_sidebar )
4774 			{
4775 				ttfPrintTextFormatted(ttf12, subx1 + 36, current_y, "[x] %s", language[2598]);
4776 			}
4777 			else
4778 			{
4779 				ttfPrintTextFormatted(ttf12, subx1 + 36, current_y, "[ ] %s", language[2598]);
4780 			}
4781 			current_y += 16;
4782 			if ( settings_show_game_timer_always )
4783 			{
4784 				ttfPrintTextFormatted(ttf12, subx1 + 36, current_y, "[x] %s", language[2983]);
4785 			}
4786 			else
4787 			{
4788 				ttfPrintTextFormatted(ttf12, subx1 + 36, current_y, "[ ] %s", language[2983]);
4789 			}
4790 			current_y += 32;
4791 
4792 			// server flag elements
4793 			ttfPrintText(ttf12, subx1 + 24, current_y, language[1375]);
4794 			current_y += 24;
4795 
4796 
4797 			int server_flags_start_y = current_y;
4798 			for ( int i = 0; i < NUM_SERVER_FLAGS; i++, current_y += 16 )
4799 			{
4800 				char flagStringBuffer[512] = "";
4801 				if ( i < 5 )
4802 				{
4803 					strncpy(flagStringBuffer, language[153 + i], 255);
4804 				}
4805 				else
4806 				{
4807 					strncpy(flagStringBuffer, language[2917 - 5 + i], 255);
4808 				}
4809 
4810 				bool flagEnabled = settings_svFlags & power(2, i);
4811 				if ( multiplayer == CLIENT )
4812 				{
4813 					flagEnabled = svFlags & power(2, i); // clients get the data from the server and don't cache it
4814 				}
4815 
4816 				if ( flagEnabled )
4817 				{
4818 					ttfPrintTextFormatted(ttf12, subx1 + 36, current_y, "[x] %s", flagStringBuffer);
4819 				}
4820 				else
4821 				{
4822 					ttfPrintTextFormatted(ttf12, subx1 + 36, current_y, "[ ] %s", flagStringBuffer);
4823 				}
4824 				if (mouseInBounds(subx1 + 36 + 6, subx1 + 36 + 24 + 6, current_y, current_y + 12))   //So many gosh dang magic numbers ._.
4825 				{
4826 					if ( i < 5 )
4827 					{
4828 						strncpy(flagStringBuffer, language[1942 + i], 255);
4829 					}
4830 					else
4831 					{
4832 						strncpy(flagStringBuffer, language[2921 - 5 + i], 255);
4833 					}
4834 					if (strlen(flagStringBuffer) > 0)   //Don't bother drawing a tooltip if the file doesn't say anything.
4835 					{
4836 						hovering_selection = i;
4837 #if (!defined STEAMWORKS && !defined USE_EOS)
4838 						if ( hovering_selection == 0 )
4839 						{
4840 							hovering_selection = -1; // don't show cheats tooltip about disabling achievements.
4841 						}
4842 #endif // STEAMWORKS
4843 						tooltip_box.x = omousex + 16;
4844 						tooltip_box.y = omousey + 8; //I hate magic numbers :|. These should probably be replaced with omousex + mousecursorsprite->width, omousey + mousecursorsprite->height, respectively.
4845 						if ( i == 2 || i == 3 || i == 5 || i == 6 || i == 7 )
4846 						{
4847 							tooltip_box.h = TTF12_HEIGHT * 2 + 8;
4848 						}
4849 						else if ( i == 4 || i == 8 )
4850 						{
4851 							tooltip_box.h = TTF12_HEIGHT * 3 + 8;
4852 						}
4853 						else
4854 						{
4855 							tooltip_box.h = TTF12_HEIGHT + 8;
4856 						}
4857 						if ( gameModeManager.isServerflagDisabledForCurrentMode(i) )
4858 						{
4859 							strcat(flagStringBuffer, language[3962]); // changing flags disabled.
4860 							tooltip_box.h += TTF12_HEIGHT;
4861 						}
4862 						tooltip_box.w = longestline(flagStringBuffer) * TTF12_WIDTH + 8; //MORE MAGIC NUMBERS. HNNGH. I can guess what they all do, but dang.
4863 					}
4864 				}
4865 			}
4866 
4867 			// network options
4868 			current_y += 32;
4869 			ttfPrintText(ttf12, subx1 + 24, current_y, language[3146]);
4870 			current_y += 24;
4871 			int networking_options_start_y = current_y;
4872 			if ( settings_disableFPSLimitOnNetworkMessages )
4873 			{
4874 				ttfPrintTextFormatted(ttf12, subx1 + 36, current_y, "[x] %s", "disable netcode FPS optimization");
4875 			}
4876 			else
4877 			{
4878 				ttfPrintTextFormatted(ttf12, subx1 + 36, current_y, "[ ] %s", "disable netcode FPS optimization");
4879 			}
4880 			current_y += 16;
4881 #ifdef STEAMWORKS
4882 			if ( settings_disableMultithreadedSteamNetworking )
4883 			{
4884 				ttfPrintTextFormatted(ttf12, subx1 + 36, current_y, "[x] %s", language[3147]);
4885 			}
4886 			else
4887 			{
4888 				ttfPrintTextFormatted(ttf12, subx1 + 36, current_y, "[ ] %s", language[3147]);
4889 			}
4890 #ifdef USE_EOS
4891 			current_y += 16;
4892 			ttfPrintTextFormatted(ttf12, subx1 + 36, current_y, "[%c] %s", LobbyHandler.settings_crossplayEnabled ? 'x' : ' ', language[3948]);
4893 #endif
4894 #endif // STEAMWORKS
4895 
4896 			if (hovering_selection > -1)
4897 			{
4898 				drawTooltip(&tooltip_box);
4899 				if (hovering_selection < NUM_SERVER_FLAGS)
4900 				{
4901 					char flagStringBuffer[512] = "";
4902 					if ( hovering_selection < 5 )
4903 					{
4904 						strncpy(flagStringBuffer, language[1942 + hovering_selection], 255);
4905 					}
4906 					else
4907 					{
4908 						strncpy(flagStringBuffer, language[2921 - 5 + hovering_selection], 255);
4909 					}
4910 					if ( gameModeManager.isServerflagDisabledForCurrentMode(hovering_selection) )
4911 					{
4912 						strcat(flagStringBuffer, language[3962]); // changing flags disabled.
4913 					}
4914 					ttfPrintTextFormatted(ttf12, tooltip_box.x + 4, tooltip_box.y + 4, flagStringBuffer);
4915 				}
4916 			}
4917 
4918 			current_y = options_start_y;
4919 
4920 			if ( mousestatus[SDL_BUTTON_LEFT] )
4921 			{
4922 				if ( omousex >= subx1 + 42 && omousex < subx1 + 66 )
4923 				{
4924 					if ( omousey >= current_y && omousey < current_y + 12 )
4925 					{
4926 						mousestatus[SDL_BUTTON_LEFT] = 0;
4927 						settings_broadcast = (settings_broadcast == false);
4928 					}
4929 					else if ( omousey >= (current_y += 16) && omousey < current_y + 12 )
4930 					{
4931 						mousestatus[SDL_BUTTON_LEFT] = 0;
4932 						settings_nohud = (settings_nohud == false);
4933 					}
4934 					else if ( omousey >= (current_y += 16) && omousey < current_y + 12 )
4935 					{
4936 						mousestatus[SDL_BUTTON_LEFT] = 0;
4937 						settings_auto_hotbar_new_items = (settings_auto_hotbar_new_items == false);
4938 					}
4939 					else if ( omousey >= (current_y += 16) && omousey < current_y + 12 )
4940 					{
4941 						mousestatus[SDL_BUTTON_LEFT] = 0;
4942 						settings_auto_appraise_new_items = (settings_auto_appraise_new_items == false);
4943 					}
4944 					else if ( omousey >= (current_y += 16) && omousey < current_y + 12 )
4945 					{
4946 						mousestatus[SDL_BUTTON_LEFT] = 0;
4947 						settings_disable_messages = (settings_disable_messages == false);
4948 					}
4949 					else if ( omousey >= (current_y += 16) && omousey < current_y + 12 )
4950 					{
4951 						mousestatus[SDL_BUTTON_LEFT] = 0;
4952 						settings_right_click_protect = (settings_right_click_protect == false);
4953 					}
4954 					else if ( omousey >= (current_y += 16) && omousey < current_y + 12 )
4955 					{
4956 						mousestatus[SDL_BUTTON_LEFT] = 0;
4957 						settings_hotbar_numkey_quick_add = (settings_hotbar_numkey_quick_add == false);
4958 					}
4959 					else if ( omousey >= (current_y += 16) && omousey < current_y + 12 )
4960 					{
4961 						mousestatus[SDL_BUTTON_LEFT] = 0;
4962 						settings_lock_right_sidebar = (settings_lock_right_sidebar == false);
4963 					}
4964 					else if ( omousey >= (current_y += 16) && omousey < current_y + 12 )
4965 					{
4966 						mousestatus[SDL_BUTTON_LEFT] = 0;
4967 						settings_show_game_timer_always = (settings_show_game_timer_always == false);
4968 					}
4969 				}
4970 				else
4971 				{
4972 					if ( settings_auto_hotbar_new_items )
4973 					{
4974 						if ( mousestatus[SDL_BUTTON_LEFT] )
4975 						{
4976 							for ( int i = 0; i < NUM_HOTBAR_CATEGORIES; ++i )
4977 							{
4978 								if ( mouseInBounds(hotbar_options_x, hotbar_options_x + 24, hotbar_options_y, hotbar_options_y + 12) )
4979 								{
4980 									settings_auto_hotbar_categories[i] = !settings_auto_hotbar_categories[i];
4981 									mousestatus[SDL_BUTTON_LEFT] = 0;
4982 								}
4983 								hotbar_options_x += 128;
4984 								if ( i == 3 || i == 7 )
4985 								{
4986 									hotbar_options_x -= (128 * 4);
4987 									hotbar_options_y += 16;
4988 								}
4989 							}
4990 						}
4991 					}
4992 
4993 					// autosort category toggles
4994 					if ( mousestatus[SDL_BUTTON_LEFT] )
4995 					{
4996 						for ( int i = 0; i < NUM_AUTOSORT_CATEGORIES; ++i )
4997 						{
4998 							if ( mouseInBounds(autosort_options_x, autosort_options_x + 16, autosort_options_y, autosort_options_y + 12) )
4999 							{
5000 								--settings_autosort_inventory_categories[i];
5001 								if ( settings_autosort_inventory_categories[i] < -9 )
5002 								{
5003 									settings_autosort_inventory_categories[i] = 9;
5004 								}
5005 								mousestatus[SDL_BUTTON_LEFT] = 0;
5006 							}
5007 							else if ( mouseInBounds(autosort_options_x + 36, autosort_options_x + 52, autosort_options_y, autosort_options_y + 12) )
5008 							{
5009 								++settings_autosort_inventory_categories[i];
5010 								if ( settings_autosort_inventory_categories[i] > 9 )
5011 								{
5012 									settings_autosort_inventory_categories[i] = -9;
5013 								}
5014 								mousestatus[SDL_BUTTON_LEFT] = 0;
5015 							}
5016 							autosort_options_x += 128;
5017 							if ( i == 3 || i == 7 )
5018 							{
5019 								autosort_options_x -= (128 * 4);
5020 								autosort_options_y += 16;
5021 							}
5022 						}
5023 					}
5024 				}
5025 
5026 				if ( multiplayer != CLIENT )
5027 				{
5028 					current_y = server_flags_start_y;
5029 					for ( int i = 0; i < NUM_SERVER_FLAGS; i++, current_y += 16 )
5030 					{
5031 						if ( !gameModeManager.isServerflagDisabledForCurrentMode(i)
5032 							&& mouseInBounds(subx1 + 36 + 6, subx1 + 36 + 24 + 6, current_y, current_y + 12) )
5033 						{
5034 							mousestatus[SDL_BUTTON_LEFT] = 0;
5035 
5036 							// toggle flag
5037 							settings_svFlags ^= power(2, i);
5038 						}
5039 					}
5040 				}
5041 			}
5042 
5043 
5044 			if ( omousex >= subx1 + 42 && omousex < subx1 + 66 )
5045 			{
5046 				tooltip_box.x = omousex + 16;
5047 				tooltip_box.y = omousey + 8;
5048 				tooltip_box.h = TTF12_HEIGHT + 8;
5049 
5050 				// networking hover text and mouse selection
5051 				current_y = networking_options_start_y;
5052 				if ( omousey >= current_y && omousey < current_y + 12 )
5053 				{
5054 					//tooltip_box.w = longestline(language[3148]) * TTF12_WIDTH + 8;
5055 					//tooltip_box.h = TTF12_HEIGHT * 2 + 8;
5056 					//drawTooltip(&tooltip_box);
5057 					//ttfPrintTextFormatted(ttf12, tooltip_box.x + 4, tooltip_box.y + 4, language[3148]);
5058 					if ( mousestatus[SDL_BUTTON_LEFT] )
5059 					{
5060 						mousestatus[SDL_BUTTON_LEFT] = 0;
5061 						settings_disableFPSLimitOnNetworkMessages = (settings_disableFPSLimitOnNetworkMessages == false);
5062 					}
5063 				}
5064 				current_y += 16;
5065 #ifdef STEAMWORKS
5066 				if ( omousey >= current_y && omousey < current_y + 12 )
5067 				{
5068 					tooltip_box.w = longestline(language[3148]) * TTF12_WIDTH + 8;
5069 					tooltip_box.h = TTF12_HEIGHT * 2 + 8;
5070 					drawTooltip(&tooltip_box);
5071 					ttfPrintTextFormatted(ttf12, tooltip_box.x + 4, tooltip_box.y + 4, language[3148]);
5072 					if ( mousestatus[SDL_BUTTON_LEFT] )
5073 					{
5074 						mousestatus[SDL_BUTTON_LEFT] = 0;
5075 						settings_disableMultithreadedSteamNetworking = true;// (settings_disableMultithreadedSteamNetworking == false);
5076 					}
5077 				}
5078 #ifdef USE_EOS
5079 				current_y += 16;
5080 				if ( omousey >= current_y && omousey < current_y + 12 )
5081 				{
5082 					/*tooltip_box.w = longestline(language[3148]) * TTF12_WIDTH + 8;
5083 					tooltip_box.h = TTF12_HEIGHT * 2 + 8;
5084 					drawTooltip(&tooltip_box);
5085 					ttfPrintTextFormatted(ttf12, tooltip_box.x + 4, tooltip_box.y + 4, language[3148]);*/
5086 					if ( mousestatus[SDL_BUTTON_LEFT] )
5087 					{
5088 						mousestatus[SDL_BUTTON_LEFT] = 0;
5089 						LobbyHandler.settings_crossplayEnabled = !LobbyHandler.settings_crossplayEnabled;
5090 					}
5091 				}
5092 #endif
5093 #endif // STEAMWORKS
5094 
5095 
5096 				current_y = options_start_y;
5097 
5098 				if ( omousey >= current_y && omousey < current_y + 12 ) // ip broadcast
5099 				{
5100 					tooltip_box.w = longestline(language[3149]) * TTF12_WIDTH + 8;
5101 					tooltip_box.h = TTF12_HEIGHT + 8;
5102 					drawTooltip(&tooltip_box);
5103 					ttfPrintTextFormatted(ttf12, tooltip_box.x + 4, tooltip_box.y + 4, language[3149]);
5104 				}
5105 				else if ( omousey >= (current_y += 16) && omousey < current_y + 12 ) // no hud
5106 				{
5107 					tooltip_box.w = longestline(language[3150]) * TTF12_WIDTH + 8;
5108 					tooltip_box.h = TTF12_HEIGHT + 8;
5109 					drawTooltip(&tooltip_box);
5110 					ttfPrintTextFormatted(ttf12, tooltip_box.x + 4, tooltip_box.y + 4, language[3150]);
5111 				}
5112 				else if ( omousey >= (current_y += 16) && omousey < current_y + 12 ) // auto add to hotbar
5113 				{
5114 					tooltip_box.w = longestline(language[3151]) * TTF12_WIDTH + 8;
5115 					tooltip_box.h = TTF12_HEIGHT * 2 + 8;
5116 					drawTooltip(&tooltip_box);
5117 					ttfPrintTextFormatted(ttf12, tooltip_box.x + 4, tooltip_box.y + 4, language[3151]);
5118 				}
5119 				else if ( omousey >= (current_y += 16) && omousey < current_y + 12 ) // auto appraisal
5120 				{
5121 					tooltip_box.w = longestline(language[3152]) * TTF12_WIDTH + 8;
5122 					tooltip_box.h = TTF12_HEIGHT * 2 + 8;
5123 					drawTooltip(&tooltip_box);
5124 					ttfPrintTextFormatted(ttf12, tooltip_box.x + 4, tooltip_box.y + 4, language[3152]);
5125 				}
5126 				else if ( omousey >= (current_y += 16) && omousey < current_y + 12 ) // no messages
5127 				{
5128 					tooltip_box.w = longestline(language[3153]) * TTF12_WIDTH + 8;
5129 					tooltip_box.h = TTF12_HEIGHT * 2 + 8;
5130 					drawTooltip(&tooltip_box);
5131 					ttfPrintTextFormatted(ttf12, tooltip_box.x + 4, tooltip_box.y + 4, language[3153]);
5132 				}
5133 				else if ( omousey >= (current_y += 16) && omousey < current_y + 12 ) // right click protect
5134 				{
5135 					tooltip_box.w = longestline(language[3154]) * TTF12_WIDTH + 8;
5136 					tooltip_box.h = TTF12_HEIGHT * 2 + 8;
5137 					drawTooltip(&tooltip_box);
5138 					ttfPrintTextFormatted(ttf12, tooltip_box.x + 4, tooltip_box.y + 4, language[3154]);
5139 				}
5140 				else if ( omousey >= (current_y += 16) && omousey < current_y + 12 ) // numkey hotbar
5141 				{
5142 					tooltip_box.w = longestline(language[3155]) * TTF12_WIDTH + 8;
5143 					tooltip_box.h = TTF12_HEIGHT * 3 + 8;
5144 					drawTooltip(&tooltip_box);
5145 					ttfPrintTextFormatted(ttf12, tooltip_box.x + 4, tooltip_box.y + 4, language[3155]);
5146 				}
5147 				else if ( omousey >= (current_y += 16) && omousey < current_y + 12 ) // lock ride sidebar
5148 				{
5149 					tooltip_box.w = longestline(language[3156]) * TTF12_WIDTH + 8;
5150 					tooltip_box.h = TTF12_HEIGHT * 3 + 8;
5151 					drawTooltip(&tooltip_box);
5152 					ttfPrintTextFormatted(ttf12, tooltip_box.x + 4, tooltip_box.y + 4, language[3156]);
5153 				}
5154 				else if ( omousey >= (current_y += 16) && omousey < current_y + 12 ) // show timer always
5155 				{
5156 					tooltip_box.w = longestline(language[3157]) * TTF12_WIDTH + 8;
5157 					tooltip_box.h = TTF12_HEIGHT * 3 + 8;
5158 					drawTooltip(&tooltip_box);
5159 					ttfPrintTextFormatted(ttf12, tooltip_box.x + 4, tooltip_box.y + 4, language[3157]);
5160 				}
5161 			}
5162 		}
5163 	}
5164 
5165 	// achievements window
5166 	if ( subwindow && achievements_window )
5167 	{
5168 		if ( mousestatus[SDL_BUTTON_WHEELDOWN] )
5169 		{
5170 			mousestatus[SDL_BUTTON_WHEELDOWN] = 0;
5171 			buttonAchievementsDown(nullptr);
5172 		}
5173 		if ( mousestatus[SDL_BUTTON_WHEELUP] )
5174 		{
5175 			mousestatus[SDL_BUTTON_WHEELUP] = 0;
5176 			buttonAchievementsUp(nullptr);
5177 		}
5178 
5179 		char page_str[128];
5180 		int num_achievements = achievementNames.size();
5181 		if ( num_achievements > 0 )
5182 		{
5183 			int num_unlocked = 0;
5184 			int num_locked_and_hidden = 0;
5185 			int num_displayed = num_achievements;
5186 			for (auto& item : achievementNames)
5187 			{
5188 				if (achievementUnlocked(item.first.c_str()))
5189 				{
5190 					++num_unlocked;
5191 				}
5192 				else if ( achievementHidden.find(item.first.c_str()) != achievementHidden.end() )
5193 				{
5194 					++num_locked_and_hidden;
5195 					--num_displayed;
5196 				}
5197 			}
5198 
5199 			if ( num_locked_and_hidden > 0 )
5200 			{
5201 				++num_displayed; // add 1 hidden entry.
5202 			}
5203 
5204 			int max_pages = num_displayed / 6 + ((num_displayed % 6) ? 1 : 0);
5205 			achievements_window_page = std::min(achievements_window_page, max_pages);
5206 
5207 			snprintf(page_str, sizeof(page_str), "Page %d / %d\n\nUnlocked %d / %d achievements (%d%%)",
5208 				achievements_window_page, max_pages, num_unlocked, num_achievements, (num_unlocked * 100) / num_achievements);
5209 			ttfPrintText(ttf12, subx1 + 8, suby1 + 30, page_str);
5210 
5211 			int first_ach = (achievements_window_page - 1) * 6;
5212 			// list achievement images
5213 			int index = 0;
5214 			bool bFirstHiddenAchievement = true;
5215 			for (auto& item : achievementNamesSorted)
5216 			{
5217 				bool hiddenAchievement = (achievementHidden.find(item.first) != achievementHidden.end());
5218 				bool unlocked = achievementUnlocked(item.first.c_str());
5219 				if ( index < first_ach )
5220 				{
5221 					++index; continue;
5222 				}
5223 				if ( hiddenAchievement )
5224 				{
5225 					if ( !bFirstHiddenAchievement )
5226 					{
5227 						continue;
5228 					}
5229 					bFirstHiddenAchievement = false;
5230 				}
5231 
5232 				const int iconAreaWidth = 12 + 64 + 12;
5233 
5234 				SDL_Rect bodyBox;
5235 				bodyBox.x = subx1 + 4 + iconAreaWidth;
5236 				bodyBox.y = suby1 + 80 + 4 + (index - first_ach) * 80;
5237 				bodyBox.w = subx2 - subx1 - 30 - 8 - iconAreaWidth;
5238 				bodyBox.h = 80 - 8;
5239 				drawWindowFancy(bodyBox.x, bodyBox.y, bodyBox.x + bodyBox.w, bodyBox.y + bodyBox.h);
5240 
5241 				SDL_Rect iconBox;
5242 				iconBox.x = subx1 + 4;
5243 				iconBox.y = bodyBox.y;
5244 				iconBox.w = iconAreaWidth;
5245 				iconBox.h = bodyBox.h;
5246 				drawWindowFancy(iconBox.x, iconBox.y, iconBox.x + iconBox.w, iconBox.y + iconBox.h);
5247 
5248 				SDL_Rect bodyHighlight;
5249 				bodyHighlight.x = bodyBox.x + 2;
5250 				bodyHighlight.y = bodyBox.y + 2;
5251 				bodyHighlight.w = bodyBox.w - 4;
5252 				bodyHighlight.h = bodyBox.h - 4;
5253 
5254 				SDL_Rect iconHighlight;
5255 				iconHighlight.x = iconBox.x + 2;
5256 				iconHighlight.y = iconBox.y + 2;
5257 				iconHighlight.w = iconBox.w - 4;
5258 				iconHighlight.h = iconBox.h - 4;
5259 
5260 				if ( achievementUnlocked(item.first.c_str()) )
5261 				{
5262 					drawRect(&bodyHighlight, uint32ColorBaronyBlue(*mainsurface), 64);
5263 					drawRect(&iconHighlight, SDL_MapRGB(mainsurface->format, 1, 0, 16), 255);
5264 				}
5265 				else
5266 				{
5267 					drawRect(&bodyHighlight, SDL_MapRGB(mainsurface->format, 128, 128, 128), 64);
5268 					drawRect(&iconHighlight, SDL_MapRGB(mainsurface->format, 36, 36, 36), 255);
5269 				}
5270 
5271 				// draw name
5272 				Uint32 nameColor = unlocked ? SDL_MapRGB(mainsurface->format, 0, 255, 255) : SDL_MapRGB(mainsurface->format, 128, 128, 128);
5273 				if ( hiddenAchievement && !unlocked )
5274 				{
5275 					ttfPrintTextColor(ttf12, subx1 + 100, suby1 + 92 + (index - first_ach) * 80, nameColor, true, "Hidden Achievements");
5276 				}
5277 				else
5278 				{
5279 					ttfPrintTextColor(ttf12, subx1 + 100, suby1 + 92 + (index - first_ach) * 80, nameColor, true, item.second.c_str());
5280 				}
5281 
5282 				// draw description
5283 				if ( !(hiddenAchievement && !unlocked) )
5284 				{
5285 					auto it = achievementDesc.find(item.first);
5286 					if ( it != achievementDesc.end() )
5287 					{
5288 						auto item = *it;
5289 						std::string sub = item.second.length() > 140 ? item.second.substr(0, 140) + "..." : item.second;
5290 						for ( size_t c, offset = 0;;)
5291 						{
5292 							size_t lastoffset = offset;
5293 							for ( c = lastoffset + 1; c < sub.size(); ++c )
5294 							{
5295 								if ( sub[c] == ' ' )
5296 								{
5297 									break;
5298 								}
5299 							}
5300 							offset = c;
5301 							if ( offset > 70 && lastoffset )
5302 							{
5303 								sub[lastoffset] = '\n';
5304 								break;
5305 							}
5306 							if ( offset >= sub.size() )
5307 							{
5308 								break;
5309 							}
5310 						}
5311 						ttfPrintText(ttf12, subx1 + 100, suby1 + 120 + (index - first_ach) * 80, sub.c_str());
5312 					}
5313 				}
5314 				else if ( hiddenAchievement && !unlocked )
5315 				{
5316 					ttfPrintTextFormatted(ttf12, subx1 + 100, suby1 + 120 + (index - first_ach) * 80,
5317 						"+%d hidden achievements remain...", num_locked_and_hidden);
5318 				}
5319 
5320 				// draw progress
5321 				if ( !unlocked )
5322 				{
5323 					auto it = achievementProgress.find(item.first);
5324 					if ( it != achievementProgress.end() )
5325 					{
5326 						int maxValue = steamStatAchStringsAndMaxVals[it->second].second;
5327 						int currentValue = g_SteamStats[it->second].m_iValue;
5328 						int percent = (int)floor(currentValue * 100 / static_cast<double>(maxValue));
5329 						//char percent_str[32] = { 0 };
5330 						// snprintf(percent_str, sizeof(percent_str), "%3d%% complete", percent);
5331 						// ttfPrintTextColor(ttf12, subx2 - 330, suby1 + 92 + (index - first_ach) * 80, uint32ColorWhite(*mainsurface), true, percent_str);
5332 
5333 						SDL_Rect progressbar;
5334 						progressbar.x = subx2 - 330 + (4 * TTF12_WIDTH) + TTF12_WIDTH;
5335 						progressbar.y = suby1 + 92 + (index - first_ach) * 80 - 4;
5336 						progressbar.h = TTF12_HEIGHT + 2;
5337 						progressbar.w = (bodyBox.x + bodyBox.w) - progressbar.x - 4;
5338 						drawWindowFancy(progressbar.x - 2, progressbar.y - 2, progressbar.x + progressbar.w + 2, progressbar.y + progressbar.h + 2);
5339 
5340 						drawRect(&progressbar, SDL_MapRGB(mainsurface->format, 36, 36, 36), 255);
5341 						progressbar.w = std::min((bodyBox.x + bodyBox.w) - progressbar.x - 4, static_cast<int>(progressbar.w * percent / 100.0));
5342 						drawRect(&progressbar, uint32ColorBaronyBlue(*mainsurface), 92);
5343 						progressbar.w = (bodyBox.x + bodyBox.w) - progressbar.x - TTF12_WIDTH;
5344 
5345 						char progress_str[32] = { 0 };
5346 						snprintf(progress_str, sizeof(progress_str), "%d / %d", currentValue, maxValue);
5347 						ttfPrintTextColor(ttf12, progressbar.x + progressbar.w / 2 - (strlen(progress_str) * TTF12_WIDTH) / 2,
5348 							suby1 + 92 + (index - first_ach) * 80, uint32ColorWhite(*mainsurface), true, progress_str);
5349 					}
5350 				}
5351 
5352 				// draw unlock time
5353 				if ( unlocked )
5354 				{
5355 					auto it = achievementUnlockTime.find(item.first);
5356 					if ( it != achievementUnlockTime.end() )
5357 					{
5358 						char buffer[64];
5359 						time_t t = (time_t)it->second;
5360 						struct tm* tm_info = localtime(&t);
5361 						strftime(buffer, sizeof(buffer), "Unlocked %Y/%m/%d at %H:%M:%S", tm_info);
5362 
5363 						char text[64];
5364 						snprintf(text, sizeof(text), "%32s", buffer);
5365 						ttfPrintTextColor(ttf12, subx2 - 330, suby1 + 92 + (index - first_ach) * 80, uint32ColorYellow(*mainsurface), true, text);
5366 					}
5367 				}
5368 
5369 				// draw image
5370 				std::string img = unlocked ? item.first + ".png" : item.first + "_l.png";
5371 				if ( !unlocked && hiddenAchievement )
5372 				{
5373 					img = "LOCKED_ACHIEVEMENT.png";
5374 				}
5375 				auto it = achievementImages.find(img);
5376 				if ( it != achievementImages.end() )
5377 				{
5378 					SDL_Rect rect;
5379 					rect.x = subx1 + 16;
5380 					rect.y = suby1 + 88 + (index - first_ach) * 80;
5381 					rect.w = 64;
5382 					rect.h = 64;
5383 					drawImage((*it).second, NULL, &rect);
5384 				}
5385 
5386 				++index;
5387 				if (index >= first_ach + 6)
5388 				{
5389 					break;
5390 				}
5391 			}
5392 		}
5393 		else
5394 		{
5395 			ttfPrintText(ttf12, subx1 + 8, suby1 + 100, language[709]);
5396 		}
5397 	}
5398 
5399 	// connect window
5400 	if ( connect_window )
5401 	{
5402 		if ( connect_window == SERVER )
5403 		{
5404 			drawDepressed(subx1 + 8, suby1 + 40, subx2 - 8, suby1 + 64);
5405 			ttfPrintText(ttf12, subx1 + 12, suby1 + 46, portnumber_char);
5406 
5407 			// enter port number
5408 			if ( !SDL_IsTextInputActive() )
5409 			{
5410 				SDL_StartTextInput();
5411 				inputstr = portnumber_char;
5412 			}
5413 			//strncpy(portnumber_char,inputstr,5);
5414 			inputlen = 5;
5415 			if ( (ticks - cursorflash) % TICKS_PER_SECOND < TICKS_PER_SECOND / 2 )
5416 			{
5417 				int x;
5418 				TTF_SizeUTF8(ttf12, portnumber_char, &x, NULL);
5419 				ttfPrintText(ttf12, subx1 + 12 + x, suby1 + 46, "_");
5420 			}
5421 		}
5422 		else if ( connect_window == CLIENT )
5423 		{
5424 			drawDepressed(subx1 + 8, suby1 + 40, subx2 - 8, suby1 + 64);
5425 			if ( !broadcast )
5426 			{
5427 				ttfPrintText(ttf12, subx1 + 12, suby1 + 46, connectaddress);
5428 			}
5429 			else
5430 			{
5431 				int i;
5432 				for ( i = 0; i < strlen(connectaddress); i++ )
5433 				{
5434 					ttfPrintText(ttf12, subx1 + 12 + 12 * i, suby1 + 46, "*");
5435 				}
5436 			}
5437 
5438 			// enter address
5439 			if ( !SDL_IsTextInputActive() )
5440 			{
5441 				SDL_StartTextInput();
5442 				inputstr = connectaddress;
5443 			}
5444 			//strncpy(connectaddress,inputstr,31);
5445 			inputlen = 31;
5446 			if ( (ticks - cursorflash) % TICKS_PER_SECOND < TICKS_PER_SECOND / 2 )
5447 			{
5448 				int x;
5449 				TTF_SizeUTF8(ttf12, connectaddress, &x, NULL);
5450 				ttfPrintText(ttf12, subx1 + 12 + x, suby1 + 46, "_");
5451 			}
5452 		}
5453 	}
5454 
5455 	// communicating with clients
5456 	if ( multiplayer == SERVER && mode )
5457 	{
5458 		//void *newSteamID = NULL; //TODO: Bugger void pointers!
5459 #ifdef STEAMWORKS
5460 		CSteamID newSteamID;
5461 #endif
5462 #if defined USE_EOS
5463 		EOS_ProductUserId newRemoteProductId = nullptr;
5464 #endif
5465 
5466 		// hosting the lobby
5467 		int numpacket;
5468 		for ( numpacket = 0; numpacket < PACKET_LIMIT; numpacket++ )
5469 		{
5470 			if ( directConnect )
5471 			{
5472 				if ( !SDLNet_UDP_Recv(net_sock, net_packet) )
5473 				{
5474 					break;
5475 				}
5476 			}
5477 			else
5478 			{
5479 				if ( LobbyHandler.getP2PType() == LobbyHandler_t::LobbyServiceType::LOBBY_STEAM )
5480 				{
5481 #ifdef STEAMWORKS
5482 					uint32_t packetlen = 0;
5483 					if ( !SteamNetworking()->IsP2PPacketAvailable(&packetlen, 0) )
5484 					{
5485 						break;
5486 					}
5487 					packetlen = std::min<int>(packetlen, NET_PACKET_SIZE - 1);
5488 					/*if ( newSteamID ) {
5489 						cpp_Free_CSteamID( newSteamID );
5490 						newSteamID = NULL;
5491 					}*/
5492 					//newSteamID = c_AllocateNew_CSteamID();
5493 					Uint32 bytesRead = 0;
5494 					if ( !SteamNetworking()->ReadP2PPacket(net_packet->data, packetlen, &bytesRead, &newSteamID, 0) )
5495 					{
5496 						continue;
5497 					}
5498 					net_packet->len = packetlen;
5499 					if ( packetlen < sizeof(DWORD) )
5500 					{
5501 						continue;    // junk packet, skip //TODO: Investigate the cause of this. During earlier testing, we were getting bombarded with untold numbers of these malformed packets, as if the entire steam network were being routed through this game.
5502 					}
5503 
5504 					CSteamID mySteamID = SteamUser()->GetSteamID();
5505 					if ( mySteamID.ConvertToUint64() == newSteamID.ConvertToUint64() )
5506 					{
5507 						continue;
5508 					}
5509 #endif
5510 				}
5511 				else if ( LobbyHandler.getP2PType() == LobbyHandler_t::LobbyServiceType::LOBBY_CROSSPLAY )
5512 				{
5513 #if defined USE_EOS
5514 					if ( !EOS.HandleReceivedMessages(&newRemoteProductId) )
5515 					{
5516 						continue;
5517 					}
5518 					EOS.P2PConnectionInfo.insertProductIdIntoPeers(newRemoteProductId);
5519 #endif // USE_EOS
5520 				}
5521 			}
5522 
5523 			if ( handleSafePacket() )
5524 			{
5525 				continue;
5526 			}
5527 			if (!strncmp((char*)net_packet->data, "BARONY_JOIN_REQUEST", 19))
5528 			{
5529 				int playerNum = MAXPLAYERS;
5530 				if ( !directConnect )
5531 				{
5532 					if ( LobbyHandler.getP2PType() == LobbyHandler_t::LobbyServiceType::LOBBY_STEAM )
5533 					{
5534 #ifdef STEAMWORKS
5535 						bool skipJoin = false;
5536 						for ( int c = 0; c < MAXPLAYERS; c++ )
5537 						{
5538 							if ( client_disconnected[c] || !steamIDRemote[c] )
5539 							{
5540 								continue;
5541 							}
5542 							if ( newSteamID.ConvertToUint64() == (static_cast<CSteamID*>(steamIDRemote[c]))->ConvertToUint64() )
5543 							{
5544 								// we've already accepted this player. NEXT!
5545 								skipJoin = true;
5546 								break;
5547 							}
5548 						}
5549 						if ( skipJoin )
5550 						{
5551 							continue;
5552 						}
5553 #endif
5554 					}
5555 					else if ( LobbyHandler.getP2PType() == LobbyHandler_t::LobbyServiceType::LOBBY_CROSSPLAY )
5556 					{
5557 #if defined USE_EOS
5558 						bool skipJoin = false;
5559 						/*for ( c = 0; c < MAXPLAYERS; c++ )
5560 						{
5561 							if ( client_disconnected[c] )
5562 							{
5563 								continue;
5564 							}
5565 						}*/
5566 						EOSFuncs::logInfo("newRemoteProductId: %s", EOSFuncs::Helpers_t::productIdToString(newRemoteProductId));
5567 						if ( newRemoteProductId && EOS.P2PConnectionInfo.isPeerIndexed(newRemoteProductId) )
5568 						{
5569 							if ( EOS.P2PConnectionInfo.getIndexFromPeerId(newRemoteProductId) >= 0 )
5570 							{
5571 								// we've already accepted this player. NEXT!
5572 								skipJoin = true;
5573 							}
5574 						}
5575 						if ( skipJoin )
5576 						{
5577 							continue;
5578 						}
5579 #endif // USE_EOS
5580 					}
5581 				}
5582 				NetworkingLobbyJoinRequestResult result = lobbyPlayerJoinRequest(playerNum);
5583 				if ( result == NetworkingLobbyJoinRequestResult::NET_LOBBY_JOIN_P2P_FAILURE )
5584 				{
5585 					if ( LobbyHandler.getP2PType() == LobbyHandler_t::LobbyServiceType::LOBBY_STEAM )
5586 					{
5587 #ifdef STEAMWORKS
5588 						for ( int responses = 0; responses < 5; ++responses )
5589 						{
5590 							SteamNetworking()->SendP2PPacket(newSteamID, net_packet->data, net_packet->len, k_EP2PSendReliable, 0);
5591 							SDL_Delay(5);
5592 						}
5593 #endif
5594 					}
5595 					else if ( LobbyHandler.getP2PType() == LobbyHandler_t::LobbyServiceType::LOBBY_CROSSPLAY )
5596 					{
5597 #if defined USE_EOS
5598 						for ( int responses = 0; responses < 5; ++responses )
5599 						{
5600 							EOS.SendMessageP2P(newRemoteProductId, net_packet->data, net_packet->len);
5601 							SDL_Delay(5);
5602 						}
5603 #endif
5604 					}
5605 				}
5606 				else if ( result == NetworkingLobbyJoinRequestResult::NET_LOBBY_JOIN_P2P_SUCCESS )
5607 				{
5608 					if ( LobbyHandler.getP2PType() == LobbyHandler_t::LobbyServiceType::LOBBY_STEAM )
5609 					{
5610 #ifdef STEAMWORKS
5611 						if ( steamIDRemote[playerNum - 1] )
5612 						{
5613 							cpp_Free_CSteamID(steamIDRemote[playerNum - 1]);
5614 						}
5615 						steamIDRemote[playerNum - 1] = new CSteamID();
5616 						*static_cast<CSteamID*>(steamIDRemote[playerNum - 1]) = newSteamID;
5617 						for ( int responses = 0; responses < 5; ++responses )
5618 						{
5619 							SteamNetworking()->SendP2PPacket(*static_cast<CSteamID* >(steamIDRemote[playerNum - 1]), net_packet->data, net_packet->len, k_EP2PSendReliable, 0);
5620 							SDL_Delay(5);
5621 						}
5622 #endif
5623 					}
5624 					else if ( LobbyHandler.getP2PType() == LobbyHandler_t::LobbyServiceType::LOBBY_CROSSPLAY )
5625 					{
5626 #if defined USE_EOS
5627 						EOS.P2PConnectionInfo.assignPeerIndex(newRemoteProductId, playerNum - 1);
5628 						for ( int responses = 0; responses < 5; ++responses )
5629 						{
5630 							EOS.SendMessageP2P(EOS.P2PConnectionInfo.getPeerIdFromIndex(playerNum - 1), net_packet->data, net_packet->len);
5631 							SDL_Delay(5);
5632 						}
5633 #endif
5634 					}
5635 				}
5636 				continue;
5637 			}
5638 
5639 			// got a chat message
5640 			else if (!strncmp((char*)net_packet->data, "CMSG", 4))
5641 			{
5642 				int i;
5643 				for ( i = 1; i < MAXPLAYERS; i++ )
5644 				{
5645 					if ( client_disconnected[i] )
5646 					{
5647 						continue;
5648 					}
5649 					net_packet->address.host = net_clients[i - 1].host;
5650 					net_packet->address.port = net_clients[i - 1].port;
5651 					sendPacketSafe(net_sock, -1, net_packet, i - 1);
5652 				}
5653 				newString(&lobbyChatboxMessages, 0xFFFFFFFF, (char*)(&net_packet->data[4]));
5654 				playSound(238, 64);
5655 				continue;
5656 			}
5657 
5658 			// player disconnected
5659 			else if (!strncmp((char*)net_packet->data, "PLAYERDISCONNECT", 16))
5660 			{
5661 				client_disconnected[net_packet->data[16]] = true;
5662 				for ( int c = 1; c < MAXPLAYERS; c++ )
5663 				{
5664 					if ( client_disconnected[c] )
5665 					{
5666 						continue;
5667 					}
5668 					net_packet->address.host = net_clients[c - 1].host;
5669 					net_packet->address.port = net_clients[c - 1].port;
5670 					net_packet->len = 17;
5671 					sendPacketSafe(net_sock, -1, net_packet, c - 1);
5672 				}
5673 				char shortname[32] = { 0 };
5674 				strncpy(shortname, stats[net_packet->data[16]]->name, 22);
5675 				newString(&lobbyChatboxMessages, 0xFFFFFFFF, language[1376], shortname);
5676 				continue;
5677 			}
5678 
5679 			// client requesting new svFlags
5680 			else if (!strncmp((char*)net_packet->data, "SVFL", 4))
5681 			{
5682 				// update svFlags for everyone
5683 				SDLNet_Write32(svFlags, &net_packet->data[4]);
5684 				net_packet->len = 8;
5685 
5686 				for ( int c = 1; c < MAXPLAYERS; c++ )
5687 				{
5688 					if ( client_disconnected[c] )
5689 					{
5690 						continue;
5691 					}
5692 					net_packet->address.host = net_clients[c - 1].host;
5693 					net_packet->address.port = net_clients[c - 1].port;
5694 					sendPacketSafe(net_sock, -1, net_packet, c - 1);
5695 				}
5696 				continue;
5697 			}
5698 
5699 			// keepalive
5700 			else if (!strncmp((char*)net_packet->data, "KEEPALIVE", 9))
5701 			{
5702 				client_keepalive[net_packet->data[9]] = ticks;
5703 				continue; // just a keep alive
5704 			}
5705 		}
5706 	}
5707 
5708 	// communicating with server
5709 	if ( multiplayer == CLIENT && mode )
5710 	{
5711 		if ( receivedclientnum == false )
5712 		{
5713 #ifdef STEAMWORKS
5714 			CSteamID newSteamID;
5715 			if ( !directConnect && LobbyHandler.getP2PType() == LobbyHandler_t::LobbyServiceType::LOBBY_STEAM )
5716 			{
5717 				// waiting on host response.
5718 				if ( joinLobbyWaitingForHostResponse )
5719 				{
5720 					// waiting on host response.
5721 					if ( ticks - client_keepalive[0] >= 15 * TICKS_PER_SECOND ) // 15 second timeout
5722 					{
5723 						buttonDisconnect(nullptr);
5724 						openFailedConnectionWindow(CLIENT);
5725 						strcpy(subtext, LobbyHandler_t::getLobbyJoinFailedConnectString(static_cast<int>(LobbyHandler_t::LOBBY_JOIN_TIMEOUT)).c_str());
5726 						connectingToLobbyStatus = EResult::k_EResultOK;
5727 					}
5728 				}
5729 			}
5730 #endif
5731 #if defined USE_EOS
5732 			EOS_ProductUserId newRemoteProductId = nullptr;
5733 			if ( !directConnect && LobbyHandler.getP2PType() == LobbyHandler_t::LobbyServiceType::LOBBY_CROSSPLAY )
5734 			{
5735 				if ( EOS.bJoinLobbyWaitingForHostResponse )
5736 				{
5737 					// waiting on host response.
5738 					if ( ticks - client_keepalive[0] >= 15 * TICKS_PER_SECOND ) // 15 second timeout
5739 					{
5740 						buttonDisconnect(nullptr);
5741 						openFailedConnectionWindow(CLIENT);
5742 						strcpy(subtext, LobbyHandler_t::getLobbyJoinFailedConnectString(static_cast<int>(LobbyHandler_t::LOBBY_JOIN_TIMEOUT)).c_str());
5743 						EOS.ConnectingToLobbyStatus = static_cast<int>(EOS_EResult::EOS_Success);
5744 					}
5745 				}
5746 			}
5747 #endif
5748 
5749 			// trying to connect to the server and get a player number
5750 			// receive the packet:
5751 			bool gotPacket = false;
5752 			if ( directConnect )
5753 			{
5754 				if ( SDLNet_TCP_Recv(net_tcpsock, net_packet->data, 4 + MAXPLAYERS * (5 + 23)) )
5755 				{
5756 					gotPacket = true;
5757 				}
5758 			}
5759 			else if ( LobbyHandler.getP2PType() == LobbyHandler_t::LobbyServiceType::LOBBY_STEAM )
5760 			{
5761 #ifdef STEAMWORKS
5762 				for ( Uint32 numpacket = 0; numpacket < PACKET_LIMIT && net_packet; numpacket++ )
5763 				{
5764 					Uint32 packetlen = 0;
5765 					if ( !SteamNetworking()->IsP2PPacketAvailable(&packetlen, 0) )
5766 					{
5767 						break;
5768 					}
5769 					packetlen = std::min<int>(packetlen, NET_PACKET_SIZE - 1);
5770 					Uint32 bytesRead = 0;
5771 					if ( !SteamNetworking()->ReadP2PPacket(net_packet->data, packetlen, &bytesRead, &newSteamID, 0) || bytesRead != 4 + MAXPLAYERS * (5 + 23) )
5772 					{
5773 						continue;
5774 					}
5775 					net_packet->len = packetlen;
5776 					if ( packetlen < sizeof(DWORD) )
5777 					{
5778 						continue;
5779 					}
5780 
5781 					CSteamID mySteamID = SteamUser()->GetSteamID();
5782 					if ( mySteamID.ConvertToUint64() == newSteamID.ConvertToUint64() )
5783 					{
5784 						continue;
5785 					}
5786 					if ( (int)net_packet->data[3] < '0'
5787 						&& (int)net_packet->data[0] == 0
5788 						&& (int)net_packet->data[1] == 0
5789 						&& (int)net_packet->data[2] == 0 )
5790 					{
5791 						// data encoded with [0 0 0 clientnum] - directly sends an INT, if the character is < '0', then it is non-alphanumeric character.
5792 						// likely not some other form of data - like an old "GOTP" from a recently closed session.
5793 						gotPacket = true;
5794 					}
5795 					else
5796 					{
5797 						continue;
5798 					}
5799 					break;
5800 				}
5801 #endif
5802 			}
5803 			else if ( LobbyHandler.getP2PType() == LobbyHandler_t::LobbyServiceType::LOBBY_CROSSPLAY )
5804 			{
5805 #ifdef USE_EOS
5806 				for ( Uint32 numpacket = 0; numpacket < PACKET_LIMIT; numpacket++ )
5807 				{
5808 					if ( !EOS.HandleReceivedMessages(&newRemoteProductId) )
5809 					{
5810 						continue;
5811 					}
5812 					if ( (int)net_packet->data[3] < '0'
5813 						&& (int)net_packet->data[0] == 0
5814 						&& (int)net_packet->data[1] == 0
5815 						&& (int)net_packet->data[2] == 0 )
5816 					{
5817 						// data encoded with [0 0 0 clientnum] - directly sends an INT, if the character is < '0', then it is non-alphanumeric character.
5818 						// likely not some other form of data - like an old "GOTP" from a recently closed session.
5819 						gotPacket = true;
5820 					}
5821 					else
5822 					{
5823 						continue;
5824 					}
5825 					break;
5826 				}
5827 #endif // USE_EOS
5828 			}
5829 
5830 
5831 			// parse the packet:
5832 			if ( gotPacket )
5833 			{
5834 				list_FreeAll(&button_l);
5835 				deleteallbuttons = true;
5836 
5837 				//clientnum = (int)SDLNet_Read32(&net_packet->data[0]);
5838 				clientnum = (int)net_packet->data[3];
5839 				//printlog("%d %d %d %d", (int)net_packet->data[0], (int)net_packet->data[1], (int)net_packet->data[2], (int)net_packet->data[3]);
5840 
5841 				if ( clientnum >= MAXPLAYERS || clientnum <= 0 )
5842 				{
5843 					printlog("connection attempt denied by server, error code: %d.\n", clientnum);
5844 					multiplayer = SINGLE;
5845 					if ( !directConnect )
5846 					{
5847 						if ( LobbyHandler.getP2PType() == LobbyHandler_t::LobbyServiceType::LOBBY_STEAM )
5848 						{
5849 #ifdef STEAMWORKS
5850 							// if we got a packet, flush any remaining packets from the queue.
5851 							Uint32 startTicks = SDL_GetTicks();
5852 							Uint32 checkTicks = startTicks;
5853 							while ( (checkTicks - startTicks) < 2000 )
5854 							{
5855 								SteamAPI_RunCallbacks();
5856 								Uint32 packetlen = 0;
5857 								if ( SteamNetworking()->IsP2PPacketAvailable(&packetlen, 0) )
5858 								{
5859 									packetlen = std::min<int>(packetlen, NET_PACKET_SIZE - 1);
5860 									Uint32 bytesRead = 0;
5861 									char buffer[NET_PACKET_SIZE];
5862 									if ( SteamNetworking()->ReadP2PPacket(buffer, packetlen, &bytesRead, &newSteamID, 0) )
5863 									{
5864 										checkTicks = SDL_GetTicks(); // found a packet, extend the wait time.
5865 									}
5866 									buffer[4] = '\0';
5867 									if ( (int)buffer[3] < '0'
5868 										&& (int)buffer[0] == 0
5869 										&& (int)buffer[1] == 0
5870 										&& (int)buffer[2] == 0 )
5871 									{
5872 										printlog("[Steam Lobby]: Clearing P2P packet queue: received: %d", (int)buffer[3]);
5873 									}
5874 									else
5875 									{
5876 										printlog("[Steam Lobby]: Clearing P2P packet queue: received: %s", buffer);
5877 									}
5878 								}
5879 								SDL_Delay(10);
5880 								if ( (SDL_GetTicks() - startTicks) > 5000 )
5881 								{
5882 									// hard break at 3 seconds.
5883 									break;
5884 								}
5885 							}
5886 #endif
5887 						}
5888 						else if ( LobbyHandler.getP2PType() == LobbyHandler_t::LobbyServiceType::LOBBY_CROSSPLAY )
5889 						{
5890 #if defined USE_EOS
5891 							// if we got a packet, flush any remaining packets from the queue.
5892 							Uint32 startTicks = SDL_GetTicks();
5893 							Uint32 checkTicks = startTicks;
5894 							while ( (checkTicks - startTicks) < 2000 )
5895 							{
5896 								EOS_Platform_Tick(EOS.PlatformHandle);
5897 								if ( EOS.HandleReceivedMessagesAndIgnore(&newRemoteProductId) )
5898 								{
5899 									checkTicks = SDL_GetTicks(); // found a packet, extend the wait time.
5900 								}
5901 								SDL_Delay(10);
5902 								if ( (SDL_GetTicks() - startTicks) > 5000 )
5903 								{
5904 									// hard break at 3 seconds.
5905 									break;
5906 								}
5907 							}
5908 #endif // USE_EOS
5909 						}
5910 					}
5911 
5912 					// close current window
5913 					buttonCloseSubwindow(NULL);
5914 					for ( node = button_l.first; node != NULL; node = nextnode )
5915 					{
5916 						nextnode = node->next;
5917 						button = (button_t*)node->element;
5918 						if ( button->focused )
5919 						{
5920 							list_RemoveNode(button->node);
5921 						}
5922 					}
5923 
5924 #ifdef STEAMWORKS
5925 					if ( !directConnect )
5926 					{
5927 						if ( currentLobby )
5928 						{
5929 							SteamMatchmaking()->LeaveLobby(*static_cast<CSteamID*>(currentLobby));
5930 							cpp_Free_CSteamID( currentLobby ); //TODO: Bugger this.
5931 							currentLobby = NULL;
5932 						}
5933 					}
5934 #endif
5935 #if defined USE_EOS
5936 					if ( !directConnect )
5937 					{
5938 						if ( EOS.CurrentLobbyData.currentLobbyIsValid() )
5939 						{
5940 							EOS.leaveLobby();
5941 						}
5942 					}
5943 #endif
5944 
5945 					// create new window
5946 					subwindow = 1;
5947 					subx1 = xres / 2 - 256;
5948 					subx2 = xres / 2 + 256;
5949 					suby1 = yres / 2 - 48;
5950 					suby2 = yres / 2 + 48;
5951 					strcpy(subtext, language[1377]);
5952 					if ( clientnum == MAXPLAYERS )
5953 					{
5954 						strcat(subtext, language[1378]);
5955 					}
5956 					else if ( clientnum == MAXPLAYERS + 1 )
5957 					{
5958 						strcat(subtext, language[1379]);
5959 					}
5960 					else if ( clientnum == MAXPLAYERS + 2 )
5961 					{
5962 						strcat(subtext, language[1380]);
5963 					}
5964 					else if ( clientnum == MAXPLAYERS + 3 )
5965 					{
5966 						strcat(subtext, language[1381]);
5967 					}
5968 					else if ( clientnum == MAXPLAYERS + 4 )
5969 					{
5970 						strcat(subtext, language[1382]);
5971 					}
5972 					else if ( clientnum == MAXPLAYERS + 5 )
5973 					{
5974 						strcat(subtext, language[1383]);
5975 					}
5976 					else
5977 					{
5978 						strcat(subtext, language[1384]);
5979 					}
5980 					clientnum = 0;
5981 
5982 					// close button
5983 					button = newButton();
5984 					strcpy(button->label, "x");
5985 					button->x = subx2 - 20;
5986 					button->y = suby1;
5987 					button->sizex = 20;
5988 					button->sizey = 20;
5989 					button->action = &buttonCloseSubwindow;
5990 					button->visible = 1;
5991 					button->focused = 1;
5992 					button->key = SDL_SCANCODE_ESCAPE;
5993 					button->joykey = joyimpulses[INJOY_MENU_CANCEL];
5994 
5995 					// okay button
5996 					button = newButton();
5997 					strcpy(button->label, language[732]);
5998 					button->x = subx2 - (subx2 - subx1) / 2 - 28;
5999 					button->y = suby2 - 28;
6000 					button->sizex = 56;
6001 					button->sizey = 20;
6002 					button->action = &buttonCloseSubwindow;
6003 					button->visible = 1;
6004 					button->focused = 1;
6005 					button->key = SDL_SCANCODE_RETURN;
6006 					button->joykey = joyimpulses[INJOY_MENU_NEXT];
6007 				}
6008 				else
6009 				{
6010 					// join game succeeded, advance to lobby
6011 					client_keepalive[0] = ticks;
6012 					receivedclientnum = true;
6013 					printlog("connected to server.\n");
6014 					client_disconnected[clientnum] = false;
6015 					if ( !loadingsavegame )
6016 					{
6017 						stats[clientnum]->appearance = stats[0]->appearance;
6018 					}
6019 
6020 					// now set up everybody else
6021 					for ( int c = 0; c < MAXPLAYERS; c++ )
6022 					{
6023 						client_disconnected[c] = false;
6024 						client_classes[c] = net_packet->data[4 + c * (5 + 23)]; // class
6025 						stats[c]->sex = static_cast<sex_t>(net_packet->data[5 + c * (5 + 23)]); // sex
6026 						client_disconnected[c] = net_packet->data[6 + c * (5 + 23)]; // connectedness :p
6027 						stats[c]->appearance = net_packet->data[7 + c * (5 + 23)]; // appearance
6028 						stats[c]->playerRace = net_packet->data[8 + c * (5 + 23)]; // player race
6029 						strcpy(stats[c]->name, (char*)(&net_packet->data[9 + c * (5 + 23)]));  // name
6030 					}
6031 
6032 					// request svFlags
6033 					strcpy((char*)net_packet->data, "SVFL");
6034 					net_packet->len = 4;
6035 					net_packet->address.host = net_server.host;
6036 					net_packet->address.port = net_server.port;
6037 					sendPacketSafe(net_sock, -1, net_packet, 0);
6038 
6039 					// open lobby window
6040 					lobby_window = true;
6041 					subwindow = 1;
6042 					subx1 = xres / 2 - 400;
6043 					subx2 = xres / 2 + 400;
6044 #ifdef PANDORA
6045 					suby1 = yres / 2 - ((yres==480)?230:290);
6046 					suby2 = yres / 2 + ((yres==480)?230:290);
6047 #else
6048 					suby1 = yres / 2 - 300;
6049 					suby2 = yres / 2 + 300;
6050 #endif
6051 
6052 					if ( directConnect )
6053 					{
6054 						strcpy(subtext, language[1385]);
6055 						if ( !broadcast )
6056 						{
6057 							strcat(subtext, last_ip);
6058 						}
6059 						else
6060 						{
6061 							strcat(subtext, "HIDDEN FOR BROADCAST");
6062 						}
6063 					}
6064 					else
6065 					{
6066 						strcpy(subtext, language[1386]);
6067 					}
6068 					strcat(subtext, language[1387]);
6069 
6070 					// disconnect button
6071 					button = newButton();
6072 					strcpy(button->label, language[1311]);
6073 					button->sizex = strlen(language[1311]) * 12 + 8;
6074 					button->sizey = 20;
6075 					button->x = subx1 + 4;
6076 					button->y = suby2 - 24;
6077 					button->action = &buttonDisconnect;
6078 					button->visible = 1;
6079 					button->focused = 1;
6080 					button->joykey = joyimpulses[INJOY_MENU_CANCEL];
6081 #ifdef STEAMWORKS
6082 					if ( !directConnect && LobbyHandler.getJoiningType() == LobbyHandler_t::LobbyServiceType::LOBBY_STEAM )
6083 					{
6084 						const char* serverNumModsChar = SteamMatchmaking()->GetLobbyData(*static_cast<CSteamID*>(currentLobby), "svNumMods");
6085 						int serverNumModsLoaded = atoi(serverNumModsChar);
6086 						if ( serverNumModsLoaded > 0 )
6087 						{
6088 							// subscribe to server loaded mods button
6089 							button = newButton();
6090 							strcpy(button->label, language[2984]);
6091 							button->sizex = strlen(language[2984]) * 12 + 8;
6092 							button->sizey = 20;
6093 							button->x = subx2 - 4 - button->sizex;
6094 							button->y = suby2 - 24;
6095 							button->action = &buttonGamemodsSubscribeToHostsModFiles;
6096 							button->visible = 1;
6097 							button->focused = 1;
6098 
6099 							// mount server mods button
6100 							button = newButton();
6101 							strcpy(button->label, language[2985]);
6102 							button->sizex = strlen(language[2985]) * 12 + 8;
6103 							button->sizey = 20;
6104 							button->x = subx2 - 4 - button->sizex;
6105 							button->y = suby2 - 24;
6106 							button->action = &buttonGamemodsMountHostsModFiles;
6107 							button->visible = 0;
6108 							button->focused = 1;
6109 
6110 							g_SteamWorkshop->CreateQuerySubscribedItems(k_EUserUGCList_Subscribed, k_EUGCMatchingUGCType_All, k_EUserUGCListSortOrder_LastUpdatedDesc);
6111 							g_SteamWorkshop->subscribedCallStatus = 0;
6112 						}
6113 					}
6114 #endif // STEAMWORKS
6115 				}
6116 			}
6117 		}
6118 		else if ( multiplayer == CLIENT )
6119 		{
6120 #ifdef STEAMWORKS
6121 			CSteamID newSteamID;
6122 			joinLobbyWaitingForHostResponse = false;
6123 #endif
6124 #ifdef USE_EOS
6125 			EOS.bJoinLobbyWaitingForHostResponse = false;
6126 #endif
6127 			int numpacket;
6128 			for ( numpacket = 0; numpacket < PACKET_LIMIT && net_packet; numpacket++ )
6129 			{
6130 				if ( directConnect )
6131 				{
6132 					if ( !SDLNet_UDP_Recv(net_sock, net_packet) )
6133 					{
6134 						break;
6135 					}
6136 				}
6137 				else
6138 				{
6139 					if ( LobbyHandler.getP2PType() == LobbyHandler_t::LobbyServiceType::LOBBY_STEAM )
6140 					{
6141 #ifdef STEAMWORKS
6142 						uint32_t packetlen = 0;
6143 						if ( !SteamNetworking()->IsP2PPacketAvailable(&packetlen, 0) )
6144 						{
6145 							break;
6146 						}
6147 						packetlen = std::min<int>(packetlen, NET_PACKET_SIZE - 1);
6148 						Uint32 bytesRead = 0;
6149 						if ( !SteamNetworking()->ReadP2PPacket(net_packet->data, packetlen, &bytesRead, &newSteamID, 0) ) //TODO: Sometimes if a host closes a lobby, it can crash here for a client.
6150 						{
6151 							continue;
6152 						}
6153 						net_packet->len = packetlen;
6154 						if ( packetlen < sizeof(DWORD) )
6155 						{
6156 							continue;    //TODO: Again, figure out why this is happening.
6157 						}
6158 
6159 						CSteamID mySteamID = SteamUser()->GetSteamID();
6160 						if (mySteamID.ConvertToUint64() == newSteamID.ConvertToUint64())
6161 						{
6162 							continue;
6163 						}
6164 #endif
6165 					}
6166 					else if ( LobbyHandler.getP2PType() == LobbyHandler_t::LobbyServiceType::LOBBY_CROSSPLAY )
6167 					{
6168 #if defined USE_EOS
6169 						EOS_ProductUserId remoteId = nullptr;
6170 						if ( !EOS.HandleReceivedMessages(&remoteId) )
6171 						{
6172 							continue;
6173 						}
6174 #endif // USE_EOS
6175 					}
6176 				}
6177 
6178 				if ( handleSafePacket() )
6179 				{
6180 					continue;
6181 				}
6182 
6183 				// game start
6184 				if (!strncmp((char*)net_packet->data, "BARONY_GAME_START", 17))
6185 				{
6186 					lobbyWindowSvFlags = SDLNet_Read32(&net_packet->data[17]);
6187 					uniqueGameKey = SDLNet_Read32(&net_packet->data[21]);
6188 					buttonCloseSubwindow(NULL);
6189 					numplayers = MAXPLAYERS;
6190 					introstage = 3;
6191 					fadeout = true;
6192 					if ( net_packet->data[25] == 0 )
6193 					{
6194 						loadingsavegame = 0;
6195 					}
6196 					continue;
6197 				}
6198 
6199 				// new player
6200 				else if (!strncmp((char*)net_packet->data, "NEWPLAYER", 9))
6201 				{
6202 					client_disconnected[net_packet->data[9]] = false;
6203 					client_classes[net_packet->data[9]] = net_packet->data[10];
6204 					stats[net_packet->data[9]]->sex = static_cast<sex_t>(net_packet->data[11]);
6205 					stats[net_packet->data[9]]->appearance = net_packet->data[12];
6206 					stats[net_packet->data[9]]->playerRace = net_packet->data[13];
6207 					strcpy(stats[net_packet->data[9]]->name, (char*)(&net_packet->data[14]));
6208 
6209 					char shortname[32] = { 0 };
6210 					strncpy(shortname, stats[net_packet->data[9]]->name, 22);
6211 					newString(&lobbyChatboxMessages, 0xFFFFFFFF, language[1388], shortname);
6212 					continue;
6213 				}
6214 
6215 				// player disconnect
6216 				else if (!strncmp((char*)net_packet->data, "PLAYERDISCONNECT", 16) || !strncmp((char*)net_packet->data, "KICK", 4) )
6217 				{
6218 					int playerDisconnected = 0;
6219 					if ( !strncmp((char*)net_packet->data, "KICK", 4) )
6220 					{
6221 						playerDisconnected = clientnum;
6222 					}
6223 					else
6224 					{
6225 						playerDisconnected = net_packet->data[16];
6226 						client_disconnected[playerDisconnected] = true;
6227 					}
6228 					if ( playerDisconnected == clientnum || net_packet->data[16] == 0 )
6229 					{
6230 						// close lobby window
6231 						buttonCloseSubwindow(NULL);
6232 						for ( node = button_l.first; node != NULL; node = nextnode )
6233 						{
6234 							nextnode = node->next;
6235 							button = (button_t*)node->element;
6236 							if ( button->focused )
6237 							{
6238 								list_RemoveNode(button->node);
6239 							}
6240 						}
6241 
6242 						// create new window
6243 						subwindow = 1;
6244 						subx1 = xres / 2 - 256;
6245 						subx2 = xres / 2 + 256;
6246 						suby1 = yres / 2 - 40;
6247 						suby2 = yres / 2 + 40;
6248 
6249 						if ( playerDisconnected == clientnum )
6250 						{
6251 							strcpy(subtext, language[1127]);
6252 						}
6253 						else
6254 						{
6255 							strcpy(subtext, language[1126]);
6256 						}
6257 
6258 						// close button
6259 						button = newButton();
6260 						strcpy(button->label, "x");
6261 						button->x = subx2 - 20;
6262 						button->y = suby1;
6263 						button->sizex = 20;
6264 						button->sizey = 20;
6265 						button->action = &buttonCloseSubwindow;
6266 						button->visible = 1;
6267 						button->focused = 1;
6268 						button->joykey = joyimpulses[INJOY_MENU_CANCEL];
6269 
6270 						// okay button
6271 						button = newButton();
6272 						strcpy(button->label, language[732]);
6273 						button->x = subx2 - (subx2 - subx1) / 2 - 20;
6274 						button->y = suby2 - 24;
6275 						button->sizex = 56;
6276 						button->sizey = 20;
6277 						button->action = &buttonCloseSubwindow;
6278 						button->visible = 1;
6279 						button->focused = 1;
6280 						button->joykey = joyimpulses[INJOY_MENU_NEXT];
6281 
6282 						// reset multiplayer status
6283 						multiplayer = SINGLE;
6284 						stats[0]->sex = stats[clientnum]->sex;
6285 						client_classes[0] = client_classes[clientnum];
6286 						strcpy(stats[0]->name, stats[clientnum]->name);
6287 						clientnum = 0;
6288 						client_disconnected[0] = false;
6289 						for ( int c = 1; c < MAXPLAYERS; c++ )
6290 						{
6291 							client_disconnected[c] = true;
6292 						}
6293 
6294 						// close any existing net interfaces
6295 						closeNetworkInterfaces();
6296 
6297 #ifdef STEAMWORKS
6298 						if ( !directConnect )
6299 						{
6300 							if ( currentLobby )
6301 							{
6302 								SteamMatchmaking()->LeaveLobby(*static_cast<CSteamID*>(currentLobby));
6303 								cpp_Free_CSteamID(currentLobby); //TODO: Bugger this.
6304 								currentLobby = NULL;
6305 							}
6306 						}
6307 #endif
6308 #if defined USE_EOS
6309 						if ( !directConnect )
6310 						{
6311 							if ( EOS.CurrentLobbyData.currentLobbyIsValid() )
6312 							{
6313 								EOS.leaveLobby();
6314 							}
6315 						}
6316 #endif
6317 					}
6318 					else
6319 					{
6320 						char shortname[32] = { 0 };
6321 						strncpy(shortname, stats[net_packet->data[16]]->name, 22);
6322 						newString(&lobbyChatboxMessages, 0xFFFFFFFF, language[1376], shortname);
6323 					}
6324 					continue;
6325 				}
6326 
6327 				// got a chat message
6328 				else if (!strncmp((char*)net_packet->data, "CMSG", 4))
6329 				{
6330 					newString(&lobbyChatboxMessages, 0xFFFFFFFF, (char*)(&net_packet->data[4]));
6331 					playSound(238, 64);
6332 					continue;
6333 				}
6334 
6335 				// update svFlags
6336 				else if (!strncmp((char*)net_packet->data, "SVFL", 4))
6337 				{
6338 					lobbyWindowSvFlags = SDLNet_Read32(&net_packet->data[4]);
6339 					continue;
6340 				}
6341 
6342 				// keepalive
6343 				else if (!strncmp((char*)net_packet->data, "KEEPALIVE", 9))
6344 				{
6345 					client_keepalive[0] = ticks;
6346 					continue; // just a keep alive
6347 				}
6348 			}
6349 		}
6350 	}
6351 	if ( multiplayer == SINGLE )
6352 	{
6353 		receivedclientnum = false;
6354 	}
6355 
6356 	// lobby window
6357 	if ( lobby_window )
6358 	{
6359 
6360 		int hovering_selection = -1; //0 to NUM_SERVER_FLAGS used for the server flags settings, e.g. are traps, cheats, minotaur, etc enabled.
6361 		SDL_Rect tooltip_box;
6362 
6363 		// player info text
6364 		for ( int c = 0; c < MAXPLAYERS; ++c )
6365 		{
6366 			if ( client_disconnected[c] )
6367 			{
6368 				continue;
6369 			}
6370 			string charDisplayName = "";
6371 			charDisplayName = stats[c]->name;
6372 
6373 			if ( !directConnect && LobbyHandler.getP2PType() == LobbyHandler_t::LobbyServiceType::LOBBY_STEAM )
6374 			{
6375 #ifdef STEAMWORKS
6376 				if ( c != clientnum )
6377 				{
6378 					for ( int remoteIDIndex = 0; remoteIDIndex < MAXPLAYERS; ++remoteIDIndex )
6379 					{
6380 						if ( steamIDRemote[remoteIDIndex] )
6381 						{
6382 							const char* memberNumChar = SteamMatchmaking()->GetLobbyMemberData(*static_cast<CSteamID*>(currentLobby), *static_cast<CSteamID*>(steamIDRemote[remoteIDIndex]), "clientnum");
6383 							if ( memberNumChar )
6384 							{
6385 								std::string str = memberNumChar;
6386 								if ( str.compare("") != 0 )
6387 								{
6388 									int memberNum = std::stoi(str);
6389 									if ( memberNum >= 0 && memberNum < MAXPLAYERS && memberNum == c )
6390 									{
6391 										charDisplayName += " (";
6392 										charDisplayName += SteamFriends()->GetFriendPersonaName(*static_cast<CSteamID*>(steamIDRemote[remoteIDIndex]));
6393 										charDisplayName += ")";
6394 									}
6395 								}
6396 							}
6397 						}
6398 					}
6399 				}
6400 				else
6401 				{
6402 					charDisplayName += " (";
6403 					charDisplayName += SteamFriends()->GetPersonaName();
6404 					charDisplayName += ")";
6405 
6406 					if ( currentLobby )
6407 					{
6408 						if ( ticks % 10 == 0 )
6409 						{
6410 							const char* memberNumChar = SteamMatchmaking()->GetLobbyMemberData(*static_cast<CSteamID*>(currentLobby), SteamUser()->GetSteamID(), "clientnum");
6411 							if ( memberNumChar )
6412 							{
6413 								std::string str = memberNumChar;
6414 								if ( str.compare("") == 0 || str.compare(std::to_string(clientnum)) != 0 )
6415 								{
6416 									SteamMatchmaking()->SetLobbyMemberData(*static_cast<CSteamID*>(currentLobby), "clientnum", std::to_string(clientnum).c_str());
6417 									printlog("[STEAM Lobbies]: Updating clientnum %d to lobby member data", clientnum);
6418 								}
6419 							}
6420 						}
6421 					}
6422 				}
6423 #endif
6424 			}
6425 			else if ( !directConnect && LobbyHandler.getP2PType() == LobbyHandler_t::LobbyServiceType::LOBBY_CROSSPLAY )
6426 			{
6427 #if defined USE_EOS
6428 				if ( c == clientnum )
6429 				{
6430 					charDisplayName += " (";
6431 					charDisplayName += EOS.CurrentUserInfo.Name;
6432 					charDisplayName += ")";
6433 
6434 					if ( EOS.CurrentLobbyData.currentLobbyIsValid()
6435 						&& EOS.CurrentLobbyData.getClientnumMemberAttribute(EOS.CurrentUserInfo.getProductUserIdHandle()) < 0 )
6436 					{
6437 						if ( EOS.CurrentLobbyData.assignClientnumMemberAttribute(EOS.CurrentUserInfo.getProductUserIdHandle(), clientnum) )
6438 						{
6439 							EOS.CurrentLobbyData.modifyLobbyMemberAttributeForCurrentUser();
6440 						}
6441 					}
6442 				}
6443 				else
6444 				{
6445 					for ( auto& player : EOS.CurrentLobbyData.playersInLobby )
6446 					{
6447 						if ( player.clientNumber == c )
6448 						{
6449 							charDisplayName += " (";
6450 							charDisplayName += player.name;
6451 							charDisplayName += ")";
6452 						}
6453 					}
6454 				}
6455 #endif
6456 			}
6457 
6458 			std::string raceAndClass = language[3161 + stats[c]->playerRace];
6459 			raceAndClass += " ";
6460 			if ( stats[c]->playerRace > RACE_HUMAN && stats[c]->appearance != 0 )
6461 			{
6462 				raceAndClass += "(aesthetic) ";
6463 			}
6464 			raceAndClass += playerClassLangEntry(client_classes[c], c);
6465 
6466 			if ( charDisplayName.size() > 39 )
6467 			{
6468 				charDisplayName = charDisplayName.substr(0, 37);
6469 				charDisplayName += "..";
6470 			}
6471 
6472 			if ( stats[c]->sex )
6473 			{
6474 				ttfPrintTextFormatted(ttf12, subx1 + 8, suby1 + 80 + 60 * c, "%d:  %s\n    %s\n    %s", c + 1, charDisplayName.c_str(), language[1322], raceAndClass.c_str());
6475 			}
6476 			else
6477 			{
6478 				ttfPrintTextFormatted(ttf12, subx1 + 8, suby1 + 80 + 60 * c, "%d:  %s\n    %s\n    %s", c + 1, charDisplayName.c_str(), language[1321], raceAndClass.c_str());
6479 			}
6480 		}
6481 
6482 		// select gui element w/ mouse
6483 		if ( mousestatus[SDL_BUTTON_LEFT] )
6484 		{
6485 			if ( mouseInBounds(subx1 + 16, subx2 - 16, suby2 - 48, suby2 - 32) )
6486 			{
6487 				mousestatus[SDL_BUTTON_LEFT] = 0;
6488 
6489 				// chatbox
6490 				inputstr = lobbyChatbox;
6491 				inputlen = LOBBY_CHATBOX_LENGTH - 1;
6492 				cursorflash = ticks;
6493 			}
6494 			else if ( mouseInBounds(xres / 2, subx2 - 32, suby1 + 56, suby1 + 68) && !directConnect )
6495 			{
6496 				mousestatus[SDL_BUTTON_LEFT] = 0;
6497 
6498 				// lobby name
6499 				if ( LobbyHandler.getP2PType() == LobbyHandler_t::LobbyServiceType::LOBBY_STEAM )
6500 				{
6501 #ifdef STEAMWORKS
6502 					inputstr = currentLobbyName;
6503 					inputlen = 31;
6504 #endif
6505 				}
6506 				else if ( LobbyHandler.getP2PType() == LobbyHandler_t::LobbyServiceType::LOBBY_CROSSPLAY )
6507 				{
6508 #if defined USE_EOS
6509 					inputstr = EOS.currentLobbyName;
6510 					inputlen = 31;
6511 #endif
6512 				}
6513 				cursorflash = ticks;
6514 			}
6515 
6516 			// server flags
6517 			int i;
6518 			if ( multiplayer == SERVER )
6519 			{
6520 				for ( i = 0; i < NUM_SERVER_FLAGS; i++ )
6521 				{
6522 					if ( mouseInBounds(xres / 2 + 8 + 6, xres / 2 + 8 + 30, suby1 + 80 + i * 16, suby1 + 92 + i * 16) )
6523 					{
6524 						mousestatus[SDL_BUTTON_LEFT] = 0;
6525 
6526 						// toggle flag
6527 						svFlags ^= power(2, i);
6528 
6529 						// update client flags
6530 						strcpy((char*)net_packet->data, "SVFL");
6531 						SDLNet_Write32(svFlags, &net_packet->data[4]);
6532 						net_packet->len = 8;
6533 
6534 						int c;
6535 						for ( c = 1; c < MAXPLAYERS; c++ )
6536 						{
6537 							if ( client_disconnected[c] )
6538 							{
6539 								continue;
6540 							}
6541 							net_packet->address.host = net_clients[c - 1].host;
6542 							net_packet->address.port = net_clients[c - 1].port;
6543 							sendPacketSafe(net_sock, -1, net_packet, c - 1);
6544 						}
6545 
6546 						// update lobby data
6547 						if ( !directConnect && LobbyHandler.getHostingType() == LobbyHandler_t::LobbyServiceType::LOBBY_STEAM )
6548 						{
6549 #ifdef STEAMWORKS
6550 							char svFlagsChar[16];
6551 							snprintf(svFlagsChar, 15, "%d", svFlags);
6552 							SteamMatchmaking()->SetLobbyData(*static_cast<CSteamID*>(currentLobby), "svFlags", svFlagsChar);
6553 #endif
6554 						}
6555 					}
6556 				}
6557 			}
6558 
6559 			// switch lobby type
6560 			if ( multiplayer == SERVER && !directConnect )
6561 			{
6562 				if ( LobbyHandler.getHostingType() == LobbyHandler_t::LobbyServiceType::LOBBY_STEAM )
6563 				{
6564 #ifdef STEAMWORKS
6565 					for ( Uint32 i = 0; i < 2; i++ )
6566 					{
6567 						if ( mouseInBounds(xres / 2 + 8 + 6, xres / 2 + 8 + 30, suby1 + 256 + i * 16, suby1 + 268 + i * 16) )
6568 						{
6569 							mousestatus[SDL_BUTTON_LEFT] = 0;
6570 							switch ( i )
6571 							{
6572 								default:
6573 									currentLobbyType = k_ELobbyTypePrivate;
6574 									break;
6575 								case 1:
6576 									currentLobbyType = k_ELobbyTypePublic;
6577 									break;
6578 								/*case 2:
6579 									currentLobbyType = k_ELobbyTypeFriendsOnly;
6580 									// deprecated by steam, doesn't return in getLobbyList.
6581 									break;*/
6582 							}
6583 							SteamMatchmaking()->SetLobbyType(*static_cast<CSteamID*>(currentLobby), currentLobbyType);
6584 						}
6585 					}
6586 #endif
6587 				}
6588 				else if ( LobbyHandler.getHostingType() == LobbyHandler_t::LobbyServiceType::LOBBY_CROSSPLAY )
6589 				{
6590 #ifdef USE_EOS
6591 					for ( Uint32 i = 0; i < 2; i++ )
6592 					{
6593 						if ( mouseInBounds(xres / 2 + 8 + 6, xres / 2 + 8 + 30, suby1 + 256 + i * 16, suby1 + 268 + i * 16) )
6594 						{
6595 							mousestatus[SDL_BUTTON_LEFT] = 0;
6596 							switch ( i )
6597 							{
6598 								default:
6599 									EOS.currentPermissionLevel = EOS_ELobbyPermissionLevel::EOS_LPL_JOINVIAPRESENCE;
6600 									break;
6601 								case 1:
6602 									EOS.currentPermissionLevel = EOS_ELobbyPermissionLevel::EOS_LPL_PUBLICADVERTISED;
6603 									break;
6604 							}
6605 						}
6606 					}
6607 #endif
6608 				}
6609 			}
6610 		}
6611 
6612 		// switch textboxes with TAB
6613 		if ( multiplayer == SERVER )
6614 		{
6615 			if ( keystatus[SDL_SCANCODE_TAB] )
6616 			{
6617 				keystatus[SDL_SCANCODE_TAB] = 0;
6618 				if ( !directConnect && LobbyHandler.getHostingType() == LobbyHandler_t::LobbyServiceType::LOBBY_STEAM )
6619 				{
6620 #ifdef STEAMWORKS
6621 					if ( inputstr == currentLobbyName )
6622 					{
6623 						inputstr = lobbyChatbox;
6624 						inputlen = LOBBY_CHATBOX_LENGTH - 1;
6625 					}
6626 					else
6627 					{
6628 						inputstr = currentLobbyName;
6629 						inputlen = 31;
6630 					}
6631 #endif
6632 				}
6633 				else if ( !directConnect && LobbyHandler.getHostingType() == LobbyHandler_t::LobbyServiceType::LOBBY_CROSSPLAY )
6634 				{
6635 #if defined USE_EOS
6636 					if ( inputstr == EOS.currentLobbyName )
6637 					{
6638 						inputstr = lobbyChatbox;
6639 						inputlen = LOBBY_CHATBOX_LENGTH - 1;
6640 					}
6641 					else
6642 					{
6643 						inputstr = EOS.currentLobbyName;
6644 						inputlen = 31;
6645 					}
6646 #endif
6647 				}
6648 			}
6649 		}
6650 
6651 		// server flag elements
6652 		int i;
6653 		for ( i = 0; i < NUM_SERVER_FLAGS; i++ )
6654 		{
6655 			char flagStringBuffer[256] = "";
6656 			if ( i < 5 )
6657 			{
6658 				strncpy(flagStringBuffer, language[153 + i], 255);
6659 			}
6660 			else
6661 			{
6662 				strncpy(flagStringBuffer, language[2917 - 5 + i], 255);
6663 			}
6664 
6665 			Uint32 displayedSvFlags = svFlags;
6666 			if ( multiplayer == CLIENT )
6667 			{
6668 				displayedSvFlags = lobbyWindowSvFlags;
6669 			}
6670 
6671 			if ( displayedSvFlags & power(2, i) )
6672 			{
6673 				ttfPrintTextFormatted(ttf12, xres / 2 + 8, suby1 + 80 + 16 * i, "[x] %s", flagStringBuffer);
6674 			}
6675 			else
6676 			{
6677 				ttfPrintTextFormatted(ttf12, xres / 2 + 8, suby1 + 80 + 16 * i, "[ ] %s", flagStringBuffer);
6678 			}
6679 			if (mouseInBounds((xres / 2) + 8 + 6, (xres / 2) + 8 + 30, suby1 + 80 + (i * 16), suby1 + 92 + (i * 16)))   //So many gosh dang magic numbers ._.
6680 			{
6681 				if ( i < 5 )
6682 				{
6683 					strncpy(flagStringBuffer, language[1942 + i], 255);
6684 				}
6685 				else
6686 				{
6687 					strncpy(flagStringBuffer, language[2921 - 5 + i], 255);
6688 				}
6689 				if (strlen(flagStringBuffer) > 0)   //Don't bother drawing a tooltip if the file doesn't say anything.
6690 				{
6691 					hovering_selection = i;
6692 #if !defined STEAMWORKS && !defined USE_EOS
6693 					if ( hovering_selection == 0 )
6694 					{
6695 						hovering_selection = -1; // don't show cheats tooltip about disabling achievements.
6696 					}
6697 #endif // STEAMWORKS
6698 					tooltip_box.x = mousex - 256;
6699 					tooltip_box.y = mousey + 8;
6700 					tooltip_box.w = longestline(flagStringBuffer) * TTF12_WIDTH + 8; //MORE MAGIC NUMBERS. HNNGH. I can guess what they all do, but dang.
6701 					tooltip_box.h = TTF12_HEIGHT + 8;
6702 					if ( i == 2 || i == 3 || i == 5 || i == 6 || i == 7 )
6703 					{
6704 						tooltip_box.h = TTF12_HEIGHT * 2 + 8;
6705 					}
6706 					else if ( i == 4 || i == 8)
6707 					{
6708 						tooltip_box.h = TTF12_HEIGHT * 3 + 8;
6709 					}
6710 					else
6711 					{
6712 						tooltip_box.h = TTF12_HEIGHT + 8;
6713 					}
6714 				}
6715 			}
6716 		}
6717 
6718 		// lobby type elements
6719 		if ( !directConnect && LobbyHandler.getP2PType() == LobbyHandler_t::LobbyServiceType::LOBBY_STEAM )
6720 		{
6721 #ifdef STEAMWORKS
6722 			if ( multiplayer == SERVER )
6723 			{
6724 				for ( Uint32 i = 0; i < 2; i++ )
6725 				{
6726 					if ( (i == 0 && currentLobbyType == k_ELobbyTypePrivate)
6727 						|| (i == 1 && currentLobbyType == k_ELobbyTypePublic) )
6728 					{
6729 						ttfPrintTextFormatted(ttf12, xres / 2 + 8, suby1 + 256 + 16 * i, "[o] %s", language[250 + i]);
6730 					}
6731 					else
6732 					{
6733 						ttfPrintTextFormatted(ttf12, xres / 2 + 8, suby1 + 256 + 16 * i, "[ ] %s", language[250 + i]);
6734 					}
6735 				}
6736 			}
6737 #endif
6738 		}
6739 		else if ( !directConnect && LobbyHandler.getP2PType() == LobbyHandler_t::LobbyServiceType::LOBBY_CROSSPLAY )
6740 		{
6741 #ifdef USE_EOS
6742 			for ( Uint32 i = 0; i < 2; i++ )
6743 			{
6744 				if ( (i == 0 && EOS.CurrentLobbyData.LobbyAttributes.PermissionLevel == static_cast<Uint32>(EOS_ELobbyPermissionLevel::EOS_LPL_JOINVIAPRESENCE))
6745 					|| (i == 1 && EOS.CurrentLobbyData.LobbyAttributes.PermissionLevel == static_cast<Uint32>(EOS_ELobbyPermissionLevel::EOS_LPL_PUBLICADVERTISED)) )
6746 				{
6747 					ttfPrintTextFormatted(ttf12, xres / 2 + 8, suby1 + 256 + 16 * i, "[o] %s", language[250 + i]);
6748 				}
6749 				else
6750 				{
6751 					ttfPrintTextFormatted(ttf12, xres / 2 + 8, suby1 + 256 + 16 * i, "[ ] %s", language[250 + i]);
6752 				}
6753 			}
6754 
6755 			if ( EOS.CurrentLobbyData.LobbyAttributes.gameJoinKey.compare("") != 0 )
6756 			{
6757 				ttfPrintTextFormatted(ttf12, xres / 2 + 8, suby1 + 256 + 16 * 3, "Lobby invite code: %s", EOS.CurrentLobbyData.LobbyAttributes.gameJoinKey.c_str());
6758 			}
6759 #endif
6760 		}
6761 
6762 		if ( !directConnect && LobbyHandler.getP2PType() == LobbyHandler_t::LobbyServiceType::LOBBY_STEAM )
6763 		{
6764 #ifdef STEAMWORKS
6765 			// server name
6766 			drawDepressed(xres / 2, suby1 + 56, xres / 2 + 388, suby1 + 72);
6767 			ttfPrintTextFormatted(ttf12, xres / 2 + 2, suby1 + 58, "%s", currentLobbyName);
6768 			if ( inputstr == currentLobbyName )
6769 			{
6770 				if ( (ticks - cursorflash) % TICKS_PER_SECOND < TICKS_PER_SECOND / 2 )
6771 				{
6772 					int x;
6773 					TTF_SizeUTF8(ttf12, currentLobbyName, &x, NULL);
6774 					ttfPrintTextFormatted(ttf12, xres / 2 + 2 + x, suby1 + 58, "_");
6775 				}
6776 			}
6777 
6778 			// update server name
6779 			if ( currentLobby )
6780 			{
6781 				const char* lobbyName = SteamMatchmaking()->GetLobbyData( *static_cast<CSteamID*>(currentLobby), "name");
6782 				if ( lobbyName )
6783 				{
6784 					if ( strcmp(lobbyName, currentLobbyName) )
6785 					{
6786 						if ( multiplayer == CLIENT )
6787 						{
6788 							// update the lobby name on our end
6789 							snprintf( currentLobbyName, 31, "%s", lobbyName );
6790 						}
6791 						else if ( multiplayer == SERVER )
6792 						{
6793 							// update the backend's copy of the lobby name
6794 							SteamMatchmaking()->SetLobbyData(*static_cast<CSteamID*>(currentLobby), "name", currentLobbyName);
6795 						}
6796 					}
6797 				}
6798 				if ( multiplayer == SERVER )
6799 				{
6800 					const char* lobbyTimeStr = SteamMatchmaking()->GetLobbyData(*static_cast<CSteamID*>(currentLobby), "lobbyModifiedTime");
6801 					if ( lobbyTimeStr )
6802 					{
6803 						Uint32 lobbyTime = static_cast<Uint32>(atoi(lobbyTimeStr));
6804 						if ( SteamUtils()->GetServerRealTime() >= lobbyTime + 3 )
6805 						{
6806 							//printlog("Updated server time");
6807 							char modifiedTime[32];
6808 							snprintf(modifiedTime, 31, "%d", SteamUtils()->GetServerRealTime());
6809 							SteamMatchmaking()->SetLobbyData(*static_cast<CSteamID*>(currentLobby), "lobbyModifiedTime", modifiedTime);
6810 						}
6811 					}
6812 				}
6813 			}
6814 #endif
6815 		}
6816 		else if ( !directConnect && LobbyHandler.getP2PType() == LobbyHandler_t::LobbyServiceType::LOBBY_CROSSPLAY )
6817 		{
6818 #if defined USE_EOS
6819 			// server name
6820 			drawDepressed(xres / 2, suby1 + 56, xres / 2 + 388, suby1 + 72);
6821 			ttfPrintTextFormatted(ttf12, xres / 2 + 2, suby1 + 58, "%s", EOS.currentLobbyName);
6822 			if ( inputstr == EOS.currentLobbyName )
6823 			{
6824 				if ( (ticks - cursorflash) % TICKS_PER_SECOND < TICKS_PER_SECOND / 2 )
6825 				{
6826 					int x;
6827 					TTF_SizeUTF8(ttf12, EOS.currentLobbyName, &x, NULL);
6828 					ttfPrintTextFormatted(ttf12, xres / 2 + 2 + x, suby1 + 58, "_");
6829 				}
6830 			}
6831 
6832 			// update server name
6833 			if ( multiplayer == CLIENT )
6834 			{
6835 				// update the lobby name on our end
6836 				snprintf(EOS.currentLobbyName, 31, "%s", EOS.CurrentLobbyData.LobbyAttributes.lobbyName.c_str());
6837 			}
6838 			else if ( multiplayer == SERVER )
6839 			{
6840 				// update the backend's copy of the lobby name and other properties
6841 				if ( ticks % TICKS_PER_SECOND == 0 && EOS.CurrentLobbyData.currentLobbyIsValid() )
6842 				{
6843 					if ( EOS.CurrentLobbyData.LobbyAttributes.lobbyName.compare(EOS.currentLobbyName) != 0
6844 						&& strcmp(EOS.currentLobbyName, "") != 0 )
6845 					{
6846 						EOS.CurrentLobbyData.updateLobbyForHost(EOSFuncs::LobbyData_t::HostUpdateLobbyTypes::LOBBY_UPDATE_MAIN_MENU);
6847 					}
6848 					else if ( EOS.CurrentLobbyData.LobbyAttributes.serverFlags != svFlags )
6849 					{
6850 						EOS.CurrentLobbyData.updateLobbyForHost(EOSFuncs::LobbyData_t::HostUpdateLobbyTypes::LOBBY_UPDATE_MAIN_MENU);
6851 					}
6852 					else if ( EOS.CurrentLobbyData.LobbyAttributes.PermissionLevel != static_cast<Uint32>(EOS.currentPermissionLevel) )
6853 					{
6854 						EOS.CurrentLobbyData.updateLobbyForHost(EOSFuncs::LobbyData_t::HostUpdateLobbyTypes::LOBBY_UPDATE_MAIN_MENU);
6855 					}
6856 				}
6857 			}
6858 #endif
6859 		}
6860 
6861 		// chatbox gui elements
6862 		drawDepressed(subx1 + 16, suby2 - 256, subx2 - 16, suby2 - 48);
6863 		drawDepressed(subx1 + 16, suby2 - 48, subx2 - 16, suby2 - 32);
6864 
6865 		// draw chatbox main text
6866 		int y = suby2 - 50;
6867 		for ( c = 0; c < 20; c++ )
6868 		{
6869 			node_t* node = list_Node(&lobbyChatboxMessages, list_Size(&lobbyChatboxMessages) - c - 1);
6870 			if ( node )
6871 			{
6872 				string_t* str = (string_t*)node->element;
6873 				y -= str->lines * TTF12_HEIGHT;
6874 				if ( y < suby2 - 254 )   // there were some tall messages and we're out of space
6875 				{
6876 					break;
6877 				}
6878 				ttfPrintTextFormatted(ttf12, subx1 + 18, y, str->data);
6879 			}
6880 			else
6881 			{
6882 				break;
6883 			}
6884 		}
6885 		while ( list_Size(&lobbyChatboxMessages) > 20 )
6886 		{
6887 			// if there are too many messages to fit the chatbox, just cull them
6888 			list_RemoveNode(lobbyChatboxMessages.first);
6889 		}
6890 
6891 		// handle chatbox text entry
6892 		if ( !SDL_IsTextInputActive() )
6893 		{
6894 			// this is the default text entry box in this window.
6895 			inputstr = lobbyChatbox;
6896 			inputlen = LOBBY_CHATBOX_LENGTH - 1;
6897 			SDL_StartTextInput();
6898 		}
6899 		if ( keystatus[SDL_SCANCODE_RETURN] && strlen(lobbyChatbox) > 0 )
6900 		{
6901 			keystatus[SDL_SCANCODE_RETURN] = 0;
6902 			if ( multiplayer != CLIENT )
6903 			{
6904 				playSound(238, 64);
6905 			}
6906 
6907 			char shortname[32] = {0};
6908 			strncpy(shortname, stats[clientnum]->name, 22);
6909 
6910 			char msg[LOBBY_CHATBOX_LENGTH + 32] = { 0 };
6911 			snprintf(msg, LOBBY_CHATBOX_LENGTH, "%s: %s", shortname, lobbyChatbox);
6912 			if ( strlen(lobbyChatbox) > LOBBY_CHATBOX_LENGTH - strlen(shortname) - 2 )
6913 			{
6914 				msg[strlen(msg)] = '\n';
6915 				int i;
6916 				for ( i = 0; i < strlen(shortname) + 2; i++ )
6917 				{
6918 					snprintf((char*)(msg + strlen(msg)), (LOBBY_CHATBOX_LENGTH + 31) - strlen(msg), " ");
6919 				}
6920 				snprintf((char*)(msg + strlen(msg)), (LOBBY_CHATBOX_LENGTH + 31) - strlen(msg), "%s", (char*)(lobbyChatbox + LOBBY_CHATBOX_LENGTH - strlen(shortname) - 2));
6921 			}
6922 			if ( multiplayer != CLIENT )
6923 			{
6924 				newString(&lobbyChatboxMessages, 0xFFFFFFFF, msg);  // servers print their messages right away
6925 			}
6926 
6927 			int playerToKick = -1;
6928 			bool skipMessageRelayToClients = false;
6929 			if ( multiplayer == SERVER )
6930 			{
6931 				std::string chatboxStr = lobbyChatbox;
6932 				if ( (chatboxStr.size() > strlen("/kick "))
6933 					&& chatboxStr.find("/kick ") != std::string::npos && chatboxStr.find("/kick ") == size_t(0U) )
6934 				{
6935 					// find player num to kick
6936 					chatboxStr = chatboxStr.substr(chatboxStr.find("/kick ") + strlen("/kick "));
6937 					std::istringstream(chatboxStr) >> playerToKick;
6938 					playerToKick -= 1;
6939 					if ( playerToKick > 0 && playerToKick < MAXPLAYERS )
6940 					{
6941 						char kickMsg[LOBBY_CHATBOX_LENGTH + 32] = { 0 };
6942 						if ( !client_disconnected[playerToKick] )
6943 						{
6944 							char clientShortName[32] = { 0 };
6945 							strncpy(clientShortName, stats[playerToKick]->name, 22);
6946 							snprintf(kickMsg, LOBBY_CHATBOX_LENGTH, language[279], playerToKick + 1, clientShortName);
6947 							newString(&lobbyChatboxMessages, 0xFFFFFFFF, kickMsg);  // servers print their messages right away
6948 
6949 							strcpy(msg, kickMsg);
6950 						}
6951 						else
6952 						{
6953 							newString(&lobbyChatboxMessages, 0xFFFFFFFF, "***   Invalid player to kick   ***");
6954 							skipMessageRelayToClients = true;
6955 							playerToKick = -1;
6956 						}
6957 					}
6958 					else
6959 					{
6960 						newString(&lobbyChatboxMessages, 0xFFFFFFFF, "***   Invalid player to kick   ***");
6961 						skipMessageRelayToClients = true;
6962 						playerToKick = -1;
6963 					}
6964 				}
6965 			}
6966 
6967 			strcpy(lobbyChatbox, "");
6968 
6969 			// send the message
6970 			strcpy((char*)net_packet->data, "CMSG");
6971 			strcat((char*)(net_packet->data), msg);
6972 			net_packet->len = 4 + strlen(msg) + 1;
6973 			net_packet->data[net_packet->len - 1] = 0;
6974 			if ( multiplayer == CLIENT )
6975 			{
6976 				net_packet->address.host = net_server.host;
6977 				net_packet->address.port = net_server.port;
6978 				sendPacketSafe(net_sock, -1, net_packet, 0);
6979 			}
6980 			else if ( multiplayer == SERVER )
6981 			{
6982 				for ( int i = 1; i < MAXPLAYERS; i++ )
6983 				{
6984 					if ( client_disconnected[i] )
6985 					{
6986 						continue;
6987 					}
6988 					if ( playerToKick == i )
6989 					{
6990 						continue;
6991 					}
6992 					if ( skipMessageRelayToClients )
6993 					{
6994 						continue;
6995 					}
6996 					net_packet->address.host = net_clients[i - 1].host;
6997 					net_packet->address.port = net_clients[i - 1].port;
6998 					sendPacketSafe(net_sock, -1, net_packet, i - 1);
6999 				}
7000 
7001 				if ( playerToKick > 0 )
7002 				{
7003 					// send message to kicked player
7004 					strcpy((char*)net_packet->data, "KICK");
7005 					net_packet->address.host = net_clients[playerToKick - 1].host;
7006 					net_packet->address.port = net_clients[playerToKick - 1].port;
7007 					net_packet->len = 4;
7008 					sendPacketSafe(net_sock, -1, net_packet, playerToKick - 1);
7009 
7010 					client_disconnected[playerToKick] = true;
7011 					// notify everyone else.
7012 					for ( int i = 1; i < MAXPLAYERS; i++ )
7013 					{
7014 						if ( client_disconnected[i] )
7015 						{
7016 							continue;
7017 						}
7018 						strncpy((char*)(net_packet->data), "PLAYERDISCONNECT", 16);
7019 						net_packet->data[16] = playerToKick;
7020 						net_packet->address.host = net_clients[i - 1].host;
7021 						net_packet->address.port = net_clients[i - 1].port;
7022 						net_packet->len = 17;
7023 						sendPacketSafe(net_sock, -1, net_packet, i - 1);
7024 					}
7025 					char shortname[32] = { 0 };
7026 					strncpy(shortname, stats[playerToKick]->name, 22);
7027 					newString(&lobbyChatboxMessages, 0xFFFFFFFF, language[1376], shortname);
7028 				}
7029 			}
7030 		}
7031 
7032 		// draw chatbox entry text and cursor
7033 		ttfPrintTextFormatted(ttf12, subx1 + 18, suby2 - 46, ">%s", lobbyChatbox);
7034 		if ( inputstr == lobbyChatbox )
7035 		{
7036 			if ( (ticks - cursorflash) % TICKS_PER_SECOND < TICKS_PER_SECOND / 2 )
7037 			{
7038 				int x;
7039 				TTF_SizeUTF8(ttf12, lobbyChatbox, &x, NULL);
7040 				ttfPrintTextFormatted(ttf12, subx1 + 18 + x + TTF12_WIDTH, suby2 - 46, "_");
7041 			}
7042 		}
7043 
7044 		if (hovering_selection > -1)
7045 		{
7046 			drawTooltip(&tooltip_box);
7047 			char flagStringBuffer[256] = "";
7048 			if ( hovering_selection < 5 )
7049 			{
7050 				strncpy(flagStringBuffer, language[1942 + hovering_selection], 255);
7051 			}
7052 			else
7053 			{
7054 				strncpy(flagStringBuffer, language[2921 - 5 + hovering_selection], 255);
7055 			}
7056 			if (hovering_selection < NUM_SERVER_FLAGS)
7057 			{
7058 				ttfPrintTextFormatted(ttf12, tooltip_box.x + 4, tooltip_box.y + 4, flagStringBuffer);
7059 			}
7060 		}
7061 #ifdef STEAMWORKS
7062 		// draw server workshop mod list
7063 		if ( !directConnect && currentLobby )
7064 		{
7065 			const char* serverNumModsChar = SteamMatchmaking()->GetLobbyData(*static_cast<CSteamID*>(currentLobby), "svNumMods");
7066 			int serverNumModsLoaded = atoi(serverNumModsChar);
7067 			if ( serverNumModsLoaded > 0 )
7068 			{
7069 				char tagName[32];
7070 				std::vector<std::string> serverFileIdsLoaded;
7071 				std::string modList = "Clients can click '";
7072 				modList.append(language[2984]).append("' and \n'").append(language[2985]);
7073 				modList.append("' to automatically subscribe and \n");
7074 				modList.append("mount workshop items loaded in the host's lobby.\n\n");
7075 				modList.append("All clients should be running the same mod load order\n");
7076 				modList.append("to prevent any crashes or undefined behavior.\n\n");
7077 				modList.append("Game client may need to be closed to install and detect\nnew subscriptions due to Workshop limitations.\n");
7078 				int numToolboxLines = 9;
7079 				bool itemNeedsSubscribing = false;
7080 				bool itemNeedsMounting = false;
7081 				Uint32 modsStatusColor = uint32ColorBaronyBlue(*mainsurface);
7082 				bool modListOutOfOrder = false;
7083 				for ( int lines = 0; lines < serverNumModsLoaded; ++lines )
7084 				{
7085 					snprintf(tagName, 32, "svMod%d", lines);
7086 					const char* serverModFileID = SteamMatchmaking()->GetLobbyData(*static_cast<CSteamID*>(currentLobby), tagName);
7087 					if ( strcmp(serverModFileID, "") )
7088 					{
7089 						if ( gamemodsCheckIfSubscribedAndDownloadedFileID(atoi(serverModFileID)) == false )
7090 						{
7091 							modList.append("Workshop item not subscribed or installed: ");
7092 							modList.append(serverModFileID);
7093 							modList.append("\n");
7094 							itemNeedsSubscribing = true;
7095 							++numToolboxLines;
7096 						}
7097 						else if ( gamemodsCheckFileIDInLoadedPaths(atoi(serverModFileID)) == false )
7098 						{
7099 							modList.append("Workshop item downloaded but not loaded: ");
7100 							modList.append(serverModFileID);
7101 							modList.append("\n");
7102 							itemNeedsMounting = true;
7103 							++numToolboxLines;
7104 						}
7105 						serverFileIdsLoaded.push_back(serverModFileID);
7106 					}
7107 				}
7108 				if ( itemNeedsSubscribing )
7109 				{
7110 					for ( node = button_l.first; node != NULL; node = nextnode )
7111 					{
7112 						nextnode = node->next;
7113 						button = (button_t*)node->element;
7114 						if ( button->action == &buttonGamemodsMountHostsModFiles )
7115 						{
7116 							button->visible = 0;
7117 						}
7118 						if ( button->action == &buttonGamemodsSubscribeToHostsModFiles )
7119 						{
7120 							button->visible = 1;
7121 						}
7122 					}
7123 				}
7124 				else
7125 				{
7126 					if ( itemNeedsMounting )
7127 					{
7128 						modsStatusColor = uint32ColorOrange(*mainsurface);
7129 						for ( node = button_l.first; node != NULL; node = nextnode )
7130 						{
7131 							nextnode = node->next;
7132 							button = (button_t*)node->element;
7133 							if ( button->action == &buttonGamemodsMountHostsModFiles )
7134 							{
7135 								button->visible = 1;
7136 							}
7137 							if ( button->action == &buttonGamemodsSubscribeToHostsModFiles )
7138 							{
7139 								button->visible = 0;
7140 							}
7141 						}
7142 					}
7143 					else if ( gamemodsIsClientLoadOrderMatchingHost(serverFileIdsLoaded)
7144 						&& serverFileIdsLoaded.size() == gamemods_workshopLoadedFileIDMap.size() )
7145 					{
7146 						modsStatusColor = uint32ColorGreen(*mainsurface);
7147 						for ( node = button_l.first; node != NULL; node = nextnode )
7148 						{
7149 							nextnode = node->next;
7150 							button = (button_t*)node->element;
7151 							if ( button->action == &buttonGamemodsMountHostsModFiles )
7152 							{
7153 								button->visible = 0;
7154 							}
7155 							if ( button->action == &buttonGamemodsSubscribeToHostsModFiles )
7156 							{
7157 								button->visible = 0;
7158 							}
7159 						}
7160 					}
7161 					else
7162 					{
7163 						modsStatusColor = uint32ColorOrange(*mainsurface);
7164 						modListOutOfOrder = true;
7165 						for ( node = button_l.first; node != NULL; node = nextnode )
7166 						{
7167 							nextnode = node->next;
7168 							button = (button_t*)node->element;
7169 							if ( button->action == &buttonGamemodsMountHostsModFiles )
7170 							{
7171 								button->visible = 1;
7172 							}
7173 							if ( button->action == &buttonGamemodsSubscribeToHostsModFiles )
7174 							{
7175 								button->visible = 0;
7176 							}
7177 						}
7178 					}
7179 				}
7180 				ttfPrintTextFormattedColor(ttf12, xres / 2 + 8, suby1 + 304, modsStatusColor, "%2d mod(s) loaded by host (?)", serverNumModsLoaded);
7181 				std::string modStatusString;
7182 				if ( itemNeedsSubscribing )
7183 				{
7184 					modStatusString = "Your client is missing mods in subscriptions";
7185 				}
7186 				else if ( itemNeedsMounting )
7187 				{
7188 					modStatusString = "Your client is missing mods in load order";
7189 				}
7190 				else if ( modListOutOfOrder )
7191 				{
7192 					modStatusString = "Your client mod list is out of order";
7193 				}
7194 				else
7195 				{
7196 					modStatusString = "Your client has complete mod list";
7197 				}
7198 
7199 				int lineStartListLoadedMods = numToolboxLines;
7200 				numToolboxLines += serverNumModsLoaded + 3;
7201 				std::string clientModString = "Your client mod list:\n";
7202 				for ( std::vector<std::pair<std::string, uint64>>::iterator it = gamemods_workshopLoadedFileIDMap.begin();
7203 					it != gamemods_workshopLoadedFileIDMap.end(); ++it )
7204 				{
7205 					clientModString.append(std::to_string(it->second));
7206 					clientModString.append("\n");
7207 				}
7208 				std::string serverModString = "Server mod list:\n";
7209 				for ( std::vector<std::string>::iterator it = serverFileIdsLoaded.begin(); it != serverFileIdsLoaded.end(); ++it )
7210 				{
7211 					serverModString.append(*it);
7212 					serverModString.append("\n");
7213 				}
7214 
7215 				ttfPrintTextFormattedColor(ttf12, xres / 2 + 8, suby1 + 320, modsStatusColor, "%s", modStatusString.c_str());
7216 				if ( mouseInBounds(xres / 2 + 8, xres / 2 + 8 + 31 * TTF12_WIDTH, suby1 + 304, suby1 + 320 + TTF12_HEIGHT) )
7217 				{
7218 					tooltip_box.w = 60 * TTF12_WIDTH + 8;
7219 					tooltip_box.x = mousex - 16 - tooltip_box.w;
7220 					tooltip_box.y = mousey + 8;
7221 					tooltip_box.h = numToolboxLines * TTF12_HEIGHT + 8;
7222 					drawTooltip(&tooltip_box);
7223 					ttfPrintTextFormatted(ttf12, tooltip_box.x + 4, tooltip_box.y + 8, "%s", modList.c_str());
7224 					ttfPrintTextFormattedColor(ttf12, tooltip_box.x + 4, tooltip_box.y + lineStartListLoadedMods * TTF12_HEIGHT + 16,
7225 						modsStatusColor, "%s", clientModString.c_str());
7226 					ttfPrintTextFormattedColor(ttf12, tooltip_box.x + 4 + 24 * TTF12_WIDTH, tooltip_box.y + lineStartListLoadedMods * TTF12_HEIGHT + 16,
7227 						modsStatusColor, "%s", serverModString.c_str());
7228 				}
7229 				if ( multiplayer == CLIENT && itemNeedsMounting )
7230 				{
7231 					if ( g_SteamWorkshop->subscribedCallStatus == 1 )
7232 					{
7233 						ttfPrintTextFormattedColor(ttf12, subx2 - 64 * TTF12_WIDTH, suby2 - 4 - TTF12_HEIGHT, uint32ColorOrange(*mainsurface),
7234 							"retrieving data...");
7235 					}
7236 					else if ( g_SteamWorkshop->subscribedCallStatus == 2 )
7237 					{
7238 						ttfPrintTextFormattedColor(ttf12, subx2 - 64 * TTF12_WIDTH, suby2 - 4 - TTF12_HEIGHT, uint32ColorOrange(*mainsurface),
7239 							"please retry mount operation.");
7240 					}
7241 					else
7242 					{
7243 						ttfPrintTextFormattedColor(ttf12, subx2 - 64 * TTF12_WIDTH, suby2 - 4 - TTF12_HEIGHT, uint32ColorOrange(*mainsurface),
7244 							"press mount button.");
7245 					}
7246 				}
7247 			}
7248 		}
7249 #endif // STEAMWORKS
7250 
7251 		// handle keepalive timeouts (lobby)
7252 		if ( multiplayer == SERVER )
7253 		{
7254 			for ( int i = 1; i < MAXPLAYERS; i++ )
7255 			{
7256 				if ( client_disconnected[i] )
7257 				{
7258 					continue;
7259 				}
7260 				bool clientHasLostP2P = false;
7261 				if ( !directConnect && LobbyHandler.getHostingType() == LobbyHandler_t::LobbyServiceType::LOBBY_STEAM )
7262 				{
7263 #ifdef STEAMWORKS
7264 					if ( !steamIDRemote[i - 1] )
7265 					{
7266 						clientHasLostP2P = true;
7267 					}
7268 #endif
7269 				}
7270 				else if ( !directConnect && LobbyHandler.getHostingType() == LobbyHandler_t::LobbyServiceType::LOBBY_CROSSPLAY )
7271 				{
7272 #ifdef USE_EOS
7273 					if ( !EOS.P2PConnectionInfo.isPeerStillValid(i - 1) )
7274 					{
7275 						clientHasLostP2P = true;
7276 					}
7277 #endif
7278 				}
7279 				if ( clientHasLostP2P || (ticks - client_keepalive[i] > TICKS_PER_SECOND * 30) )
7280 				{
7281 					client_disconnected[i] = true;
7282 					strncpy((char*)(net_packet->data), "PLAYERDISCONNECT", 16);
7283 					net_packet->data[16] = i;
7284 					net_packet->len = 17;
7285 					for ( int c = 1; c < MAXPLAYERS; c++ )
7286 					{
7287 						if ( client_disconnected[c] )
7288 						{
7289 							continue;
7290 						}
7291 						net_packet->address.host = net_clients[c - 1].host;
7292 						net_packet->address.port = net_clients[c - 1].port;
7293 						sendPacketSafe(net_sock, -1, net_packet, c - 1);
7294 					}
7295 					char shortname[32] = { 0 };
7296 					strncpy(shortname, stats[i]->name, 22);
7297 					newString(&lobbyChatboxMessages, 0xFFFFFFFF, language[1376], shortname);
7298 					continue;
7299 				}
7300 			}
7301 		}
7302 		else if ( multiplayer == CLIENT )
7303 		{
7304 			bool hostHasLostP2P = false;
7305 			if ( !directConnect && LobbyHandler.getP2PType() == LobbyHandler_t::LobbyServiceType::LOBBY_STEAM )
7306 			{
7307 #ifdef STEAMWORKS
7308 				if ( !steamIDRemote[0] )
7309 				{
7310 					hostHasLostP2P = true;
7311 				}
7312 #endif
7313 			}
7314 			else if ( !directConnect && LobbyHandler.getP2PType() == LobbyHandler_t::LobbyServiceType::LOBBY_CROSSPLAY )
7315 			{
7316 #ifdef USE_EOS
7317 				if ( !EOS.P2PConnectionInfo.isPeerStillValid(0) )
7318 				{
7319 					hostHasLostP2P = true;
7320 				}
7321 #endif
7322 			}
7323 
7324 			if ( hostHasLostP2P || (ticks - client_keepalive[0] > TICKS_PER_SECOND * 30) )
7325 			{
7326 				buttonDisconnect(NULL);
7327 				openFailedConnectionWindow(3); // lost connection to server box
7328 			}
7329 		}
7330 
7331 		// send keepalive messages every second
7332 		if ( ticks % (TICKS_PER_SECOND * 1) == 0 && multiplayer != SINGLE )
7333 		{
7334 			strcpy((char*)net_packet->data, "KEEPALIVE");
7335 			net_packet->data[9] = clientnum;
7336 			net_packet->len = 10;
7337 			if ( multiplayer == CLIENT )
7338 			{
7339 				net_packet->address.host = net_server.host;
7340 				net_packet->address.port = net_server.port;
7341 				sendPacketSafe(net_sock, -1, net_packet, 0);
7342 			}
7343 			else if ( multiplayer == SERVER )
7344 			{
7345 				int i;
7346 				for ( i = 1; i < MAXPLAYERS; i++ )
7347 				{
7348 					if ( client_disconnected[i] )
7349 					{
7350 						continue;
7351 					}
7352 					net_packet->address.host = net_clients[i - 1].host;
7353 					net_packet->address.port = net_clients[i - 1].port;
7354 					sendPacketSafe(net_sock, -1, net_packet, i - 1);
7355 				}
7356 			}
7357 		}
7358 	}
7359 
7360 	// leaderboards window
7361 #ifdef STEAMWORKS
7362 	if ( score_leaderboard_window != 0 && g_SteamLeaderboards )
7363 	{
7364 		int numEntriesToShow = 15;
7365 		int filenameMaxLength = 48;
7366 		int filename_padx = subx1 + 16;
7367 		int filename_pady = suby1 + 32;
7368 		int filename_padx2 = subx2 - 16 - 40;
7369 		int filename_pady2 = filename_pady + numEntriesToShow * TTF12_HEIGHT + 8;
7370 		int filename_rowHeight = TTF12_HEIGHT + 4;
7371 		int numEntriesTotal = 0;
7372 
7373 		ttfPrintTextFormattedColor(ttf16, filename_padx, filename_pady, uint32ColorWhite(*mainsurface), "%s",
7374 			g_SteamLeaderboards->leaderboardNames[g_SteamLeaderboards->LeaderboardView.boardToDownload].c_str());
7375 
7376 		filename_pady += 3 * TTF12_HEIGHT;
7377 		if ( !g_SteamLeaderboards->b_LeaderboardInit )
7378 		{
7379 			// waiting for leaderboard to be found...
7380 		}
7381 		else if ( g_SteamLeaderboards->b_LeaderboardInit && !g_SteamLeaderboards->b_ScoresDownloaded )
7382 		{
7383 			// wait for leaderboard to be downloaded...
7384 			if ( score_leaderboard_window == 1 )
7385 			{
7386 				g_SteamLeaderboards->DownloadScores(g_SteamLeaderboards->LeaderboardView.requestType, g_SteamLeaderboards->LeaderboardView.rangeStart,
7387 					g_SteamLeaderboards->LeaderboardView.rangeEnd);
7388 				score_leaderboard_window = 2;
7389 			}
7390 			ttfPrintTextFormattedColor(ttf12, filename_padx, filename_pady + 2 * TTF12_HEIGHT, uint32ColorOrange(*mainsurface), "Downloading entries...");
7391 		}
7392 		else
7393 		{
7394 
7395 			if ( g_SteamLeaderboards->b_ScoresDownloaded )
7396 			{
7397 				numEntriesTotal = g_SteamLeaderboards->m_nLeaderboardEntries;
7398 				if ( numEntriesTotal <= 0 )
7399 				{
7400 					ttfPrintTextFormattedColor(ttf12, filename_padx, filename_pady + 2 * TTF12_HEIGHT, uint32ColorGreen(*mainsurface), "No Leaderboard entries for this category");
7401 				}
7402 			}
7403 
7404 			SDL_Rect tooltip; // we will draw the tooltip after drawing the other elements of the display window.
7405 
7406 			tooltip.x = omousex + 8;
7407 			tooltip.y = omousey + 8;
7408 			tooltip.w = 32 + TTF12_WIDTH * 14;
7409 			tooltip.h = TTF12_HEIGHT + 8;
7410 
7411 			filename_pady += 2 * TTF12_HEIGHT;
7412 
7413 			// do slider
7414 			SDL_Rect slider;
7415 			slider.x = filename_padx2 + 8;
7416 			slider.y = filename_pady - 8;
7417 			slider.h = suby2 - (filename_pady + 20);
7418 			slider.w = 32;
7419 
7420 			int entriesToScroll = std::max(static_cast<int>((numEntriesTotal / numEntriesToShow) - 1), 0);
7421 			entriesToScroll = entriesToScroll * numEntriesToShow + (numEntriesTotal % numEntriesToShow);
7422 
7423 			bool drawScrollTooltip = false;
7424 
7425 			// handle slider movement.
7426 			if ( numEntriesTotal > numEntriesToShow )
7427 			{
7428 				drawRect(&slider, SDL_MapRGB(mainsurface->format, 64, 64, 64), 255);
7429 				if ( mouseInBounds(filename_padx, slider.x + slider.w,
7430 					slider.y, slider.y + slider.h) )
7431 				{
7432 					if ( mouseInBounds(slider.x, slider.x + slider.w,
7433 						slider.y, slider.y + slider.h) )
7434 					{
7435 						drawScrollTooltip = true;
7436 					}
7437 					if ( mousestatus[SDL_BUTTON_WHEELUP] )
7438 					{
7439 						g_SteamLeaderboards->LeaderboardView.scrollIndex = std::max(g_SteamLeaderboards->LeaderboardView.scrollIndex - 1, 0);
7440 						mousestatus[SDL_BUTTON_WHEELUP] = 0;
7441 					}
7442 					if ( mousestatus[SDL_BUTTON_WHEELDOWN] )
7443 					{
7444 						g_SteamLeaderboards->LeaderboardView.scrollIndex = std::min(g_SteamLeaderboards->LeaderboardView.scrollIndex + 1, entriesToScroll);
7445 						mousestatus[SDL_BUTTON_WHEELDOWN] = 0;
7446 					}
7447 				}
7448 
7449 				if ( keystatus[SDL_SCANCODE_UP] )
7450 				{
7451 					g_SteamLeaderboards->LeaderboardView.scrollIndex = std::max(g_SteamLeaderboards->LeaderboardView.scrollIndex - 1, 0);
7452 					keystatus[SDL_SCANCODE_UP] = 0;
7453 				}
7454 				if ( keystatus[SDL_SCANCODE_DOWN] )
7455 				{
7456 					g_SteamLeaderboards->LeaderboardView.scrollIndex = std::min(g_SteamLeaderboards->LeaderboardView.scrollIndex + 1, entriesToScroll);
7457 					keystatus[SDL_SCANCODE_DOWN] = 0;
7458 				}
7459 				slider.h *= (1 / static_cast<real_t>(entriesToScroll + 1));
7460 				slider.y += slider.h * savegames_window_scroll;
7461 				if ( g_SteamLeaderboards->LeaderboardView.scrollIndex == entriesToScroll ) // reached end.
7462 				{
7463 					slider.y += (suby2 - 28) - (slider.y + slider.h); // bottom of slider is (suby2 - 28), so move the y level to imitate hitting the bottom in case of rounding error.
7464 				}
7465 				drawWindowFancy(slider.x, slider.y, slider.x + slider.w, slider.y + slider.h); // draw shortened list relative slider.
7466 			}
7467 			else
7468 			{
7469 				//drawRect(&slider, SDL_MapRGB(mainsurface->format, 64, 64, 64), 255);
7470 				drawWindowFancy(slider.x, slider.y, slider.x + slider.w, slider.y + slider.h);
7471 			}
7472 
7473 			// draw the content
7474 			for ( int i = 0; i < numEntriesTotal; ++i )
7475 			{
7476 				filename_padx = subx1 + 16;
7477 				if ( i >= g_SteamLeaderboards->LeaderboardView.scrollIndex && i < numEntriesToShow + g_SteamLeaderboards->LeaderboardView.scrollIndex )
7478 				{
7479 					drawWindowFancy(filename_padx, filename_pady - 8, filename_padx2, filename_pady + filename_rowHeight);
7480 					SDL_Rect highlightEntry;
7481 					highlightEntry.x = filename_padx;
7482 					highlightEntry.y = filename_pady - 8;
7483 					highlightEntry.w = filename_padx2 - filename_padx;
7484 					highlightEntry.h = filename_rowHeight + 8;
7485 					drawRect(&highlightEntry, uint32ColorBaronyBlue(*mainsurface), 64);
7486 
7487 					char steamID[32] = "";
7488 					if ( strlen(SteamFriends()->GetFriendPersonaName(g_SteamLeaderboards->m_leaderboardEntries[i].m_steamIDUser)) > 18 )
7489 					{
7490 						strncpy(steamID, SteamFriends()->GetFriendPersonaName(g_SteamLeaderboards->m_leaderboardEntries[i].m_steamIDUser), 16);
7491 						strcat(steamID, "..");
7492 					}
7493 					else
7494 					{
7495 						strncpy(steamID, SteamFriends()->GetFriendPersonaName(g_SteamLeaderboards->m_leaderboardEntries[i].m_steamIDUser), 18);
7496 					}
7497 					if ( g_SteamLeaderboards->LeaderboardView.boardToDownload != LEADERBOARD_NONE
7498 						&& g_SteamLeaderboards->LeaderboardView.boardToDownload % 2 == 1 )
7499 					{
7500 						Uint32 sec = (g_SteamLeaderboards->m_leaderboardEntries[i].m_nScore) % 60;
7501 						Uint32 min = ((g_SteamLeaderboards->m_leaderboardEntries[i].m_nScore) / 60) % 60;
7502 						Uint32 hour = ((g_SteamLeaderboards->m_leaderboardEntries[i].m_nScore) / 60) / 60;
7503 
7504 						ttfPrintTextFormatted(ttf12, filename_padx + 8, filename_pady, "#%2d [%18s]:   Time: %02d:%02d:%02d  Score: %6d",
7505 							g_SteamLeaderboards->m_leaderboardEntries[i].m_nGlobalRank,
7506 							steamID, hour, min, sec, g_SteamLeaderboards->downloadedTags[i][TAG_TOTAL_SCORE]);
7507 					}
7508 					else
7509 					{
7510 						Uint32 sec = (g_SteamLeaderboards->downloadedTags[i][TAG_COMPLETION_TIME]) % 60;
7511 						Uint32 min = ((g_SteamLeaderboards->downloadedTags[i][TAG_COMPLETION_TIME]) / 60) % 60;
7512 						Uint32 hour = ((g_SteamLeaderboards->downloadedTags[i][TAG_COMPLETION_TIME]) / 60) / 60;
7513 						ttfPrintTextFormatted(ttf12, filename_padx + 8, filename_pady, "#%2d [%18s]:   Score: %6d  Time: %02d:%02d:%02d",
7514 							g_SteamLeaderboards->m_leaderboardEntries[i].m_nGlobalRank,
7515 							steamID, g_SteamLeaderboards->m_leaderboardEntries[i].m_nScore, hour, min, sec);
7516 					}
7517 
7518 					filename_padx = filename_padx2 - (15 * TTF12_WIDTH + 16);
7519 					int text_x = filename_padx;
7520 					int text_y = filename_pady;
7521 					if ( drawClickableButton(filename_padx, filename_pady, 15 * TTF12_WIDTH + 8, TTF12_HEIGHT, 0) && score_leaderboard_window != 3 )
7522 					{
7523 						score_leaderboard_window = 3;
7524 						g_SteamLeaderboards->currentLeaderBoardIndex = i;
7525 						steamLeaderboardReadScore(g_SteamLeaderboards->downloadedTags[g_SteamLeaderboards->currentLeaderBoardIndex]);
7526 						for ( node = button_l.first; node != NULL; node = node->next )
7527 						{
7528 							button = (button_t*)node->element;
7529 							button->visible = 0;
7530 						}
7531 					}
7532 					ttfPrintTextFormatted(ttf12, text_x + 8, text_y, "%s", "View character");
7533 
7534 					filename_padx = filename_padx2 - (2 * TTF12_WIDTH + 14);
7535 					text_x = filename_padx;
7536 
7537 					filename_pady += 3 * filename_rowHeight / 2;
7538 				}
7539 			}
7540 		}
7541 	}
7542 #endif
7543 	// statistics window
7544 	if ( score_window || score_leaderboard_window == 3 )
7545 	{
7546 		if ( score_leaderboard_window == 3 )
7547 		{
7548 			drawWindowFancy(subx1 + 10, suby1 + 20, subx2 - 10, suby2 - 10);
7549 			if ( drawClickableButton(subx2 - 10 * TTF12_WIDTH - 3, suby1 + 20 + 8, 8 * TTF12_WIDTH, TTF12_HEIGHT, 0) )
7550 			{
7551 				score_leaderboard_window = 2;
7552 				for ( node = button_l.first; node != NULL; node = node->next )
7553 				{
7554 					button = (button_t*)node->element;
7555 					button->visible = 1;
7556 				}
7557 			}
7558 			ttfPrintTextFormatted(ttf12, subx2 - 10 * TTF12_WIDTH + 9, suby1 + 20 + 8, "close");
7559 		}
7560 		// draw button label... shamelessly hacked together from "multiplayer scores toggle button" initialisation...
7561 		int toggleText_x = subx2 - 44 - strlen("show multiplayer") * 12;
7562 		int toggleText_y = suby1 + 4 ;
7563 		int w = 0;
7564 		int h = 0;
7565 		list_t* scoresPtr = &topscores;
7566 
7567 		if ( !score_leaderboard_window )
7568 		{
7569 			if ( !scoreDisplayMultiplayer )
7570 			{
7571 				TTF_SizeUTF8(ttf12, "show multiplayer", &w, &h);
7572 				ttfPrintText(ttf12, toggleText_x + (strlen("show multiplayer") * 12 + 8 - w) / 2, toggleText_y + (20 - h) / 2 + 3, "show multiplayer");
7573 			}
7574 			else
7575 			{
7576 				scoresPtr = &topscoresMultiplayer;
7577 				TTF_SizeUTF8(ttf12, "show solo", &w, &h);
7578 				ttfPrintText(ttf12, toggleText_x + (strlen("show multiplayer") * 12 + 8 - w) / 2, toggleText_y + (20 - h) / 2 + 3, "show solo");
7579 			}
7580 		}
7581 		else
7582 		{
7583 			scoresPtr = nullptr;
7584 		}
7585 
7586 		if ( !list_Size(scoresPtr) && !score_leaderboard_window )
7587 		{
7588 #define NOSCORESSTR language[1389]
7589 			ttfPrintTextFormatted(ttf16, xres / 2 - strlen(NOSCORESSTR) * 9, yres / 2 - 9, NOSCORESSTR);
7590 		}
7591 		else
7592 		{
7593 			if ( !score_leaderboard_window )
7594 			{
7595 				if ( scoreDisplayMultiplayer )
7596 				{
7597 					ttfPrintTextFormatted(ttf16, subx1 + 8, suby1 + 8, "%s - %d / %d", language[2958], score_window, list_Size(&topscoresMultiplayer));
7598 				}
7599 				else
7600 				{
7601 					ttfPrintTextFormatted(ttf16, subx1 + 8, suby1 + 8, "%s - %d / %d", language[1390], score_window, list_Size(&topscores));
7602 				}
7603 			}
7604 
7605 			// draw character window
7606 			if (players[clientnum] != nullptr && players[clientnum]->entity != nullptr)
7607 			{
7608 				camera_charsheet.x = players[clientnum]->entity->x / 16.0 + 1.118 * cos(camera_charsheet_offsetyaw); // + 1
7609 				camera_charsheet.y = players[clientnum]->entity->y / 16.0 + 1.118 * sin(camera_charsheet_offsetyaw); // -.5
7610 				camera_charsheet.z = players[clientnum]->entity->z * 2;
7611 				camera_charsheet.ang = atan2(players[clientnum]->entity->y / 16.0 - camera_charsheet.y, players[clientnum]->entity->x / 16.0 - camera_charsheet.x);
7612 				camera_charsheet.vang = PI / 24;
7613 				camera_charsheet.winw = 400;
7614 				camera_charsheet.winy = suby1 + 32;
7615 				camera_charsheet.winh = suby2 - 96 - camera_charsheet.winy;
7616 				camera_charsheet.winx = subx1 + 32;
7617 				SDL_Rect pos;
7618 				pos.x = camera_charsheet.winx;
7619 				pos.y = camera_charsheet.winy;
7620 				pos.w = camera_charsheet.winw;
7621 				pos.h = camera_charsheet.winh;
7622 				drawRect(&pos, 0, 255);
7623 				b = players[clientnum]->entity->flags[BRIGHT];
7624 				players[clientnum]->entity->flags[BRIGHT] = true;
7625 				if ( !players[clientnum]->entity->flags[INVISIBLE] )
7626 				{
7627 					real_t ofov = fov;
7628 					fov = 50;
7629 					glDrawVoxel(&camera_charsheet, players[clientnum]->entity, REALCOLORS);
7630 					fov = ofov;
7631 				}
7632 				players[clientnum]->entity->flags[BRIGHT] = b;
7633 				c = 0;
7634 				for ( node = players[clientnum]->entity->children.first; node != NULL; node = node->next )
7635 				{
7636 					if ( c == 0 )
7637 					{
7638 						c++;
7639 					}
7640 					entity = (Entity*) node->element;
7641 					if ( !entity->flags[INVISIBLE] )
7642 					{
7643 						b = entity->flags[BRIGHT];
7644 						entity->flags[BRIGHT] = true;
7645 						real_t ofov = fov;
7646 						fov = 50;
7647 						glDrawVoxel(&camera_charsheet, entity, REALCOLORS);
7648 						fov = ofov;
7649 						entity->flags[BRIGHT] = b;
7650 					}
7651 					c++;
7652 				}
7653 				SDL_Rect rotateBtn;
7654 				rotateBtn.w = 24;
7655 				rotateBtn.h = 24;
7656 				rotateBtn.x = camera_charsheet.winx + camera_charsheet.winw - rotateBtn.w;
7657 				rotateBtn.y = camera_charsheet.winy + camera_charsheet.winh - rotateBtn.h;
7658 				drawWindow(rotateBtn.x, rotateBtn.y, rotateBtn.x + rotateBtn.w, rotateBtn.y + rotateBtn.h);
7659 				if ( mouseInBounds(rotateBtn.x, rotateBtn.x + rotateBtn.w, rotateBtn.y, rotateBtn.y + rotateBtn.h) )
7660 				{
7661 					if ( mousestatus[SDL_BUTTON_LEFT] )
7662 					{
7663 						camera_charsheet_offsetyaw += 0.05;
7664 						if ( camera_charsheet_offsetyaw > 2 * PI )
7665 						{
7666 							camera_charsheet_offsetyaw -= 2 * PI;
7667 						}
7668 						drawDepressed(rotateBtn.x, rotateBtn.y, rotateBtn.x + rotateBtn.w, rotateBtn.y + rotateBtn.h);
7669 					}
7670 				}
7671 				ttfPrintText(ttf12, rotateBtn.x + 6, rotateBtn.y + 6, ">");
7672 
7673 				rotateBtn.x = camera_charsheet.winx + camera_charsheet.winw - rotateBtn.w * 2 - 4;
7674 				rotateBtn.y = camera_charsheet.winy + camera_charsheet.winh - rotateBtn.h;
7675 				drawWindow(rotateBtn.x, rotateBtn.y, rotateBtn.x + rotateBtn.w, rotateBtn.y + rotateBtn.h);
7676 				if ( mouseInBounds(rotateBtn.x, rotateBtn.x + rotateBtn.w, rotateBtn.y, rotateBtn.y + rotateBtn.h) )
7677 				{
7678 					if ( mousestatus[SDL_BUTTON_LEFT] )
7679 					{
7680 						camera_charsheet_offsetyaw -= 0.05;
7681 						if ( camera_charsheet_offsetyaw < 0.f )
7682 						{
7683 							camera_charsheet_offsetyaw += 2 * PI;
7684 						}
7685 						drawDepressed(rotateBtn.x, rotateBtn.y, rotateBtn.x + rotateBtn.w, rotateBtn.y + rotateBtn.h);
7686 					}
7687 				}
7688 				ttfPrintText(ttf12, rotateBtn.x + 4, rotateBtn.y + 6, "<");
7689 			}
7690 
7691 			// print name and class
7692 			if ( victory )
7693 			{
7694 				ttfPrintTextFormatted(ttf16, subx1 + 448, suby1 + 40, language[1391]);
7695 				ttfPrintTextFormatted(ttf16, subx1 + 448, suby1 + 56, "%s", stats[clientnum]->name);
7696 				int creature = HUMAN;
7697 				if ( players[clientnum] && players[clientnum]->entity )
7698 				{
7699 					creature = static_cast<int>(players[clientnum]->entity->getMonsterFromPlayerRace(stats[clientnum]->playerRace));
7700 				}
7701 				if ( victory == 1 )
7702 				{
7703 					if ( creature != HUMAN )
7704 					{
7705 						ttfPrintTextFormatted(ttf16, subx1 + 448, suby1 + 72, language[3359], monstertypenamecapitalized[creature]);
7706 					}
7707 					else
7708 					{
7709 						ttfPrintTextFormatted(ttf16, subx1 + 448, suby1 + 72, language[1392]);
7710 					}
7711 				}
7712 				else if ( victory == 2 )
7713 				{
7714 					if ( creature != HUMAN )
7715 					{
7716 						ttfPrintTextFormatted(ttf16, subx1 + 448, suby1 + 72, language[3360], monstertypenamecapitalized[creature]);
7717 					}
7718 					else
7719 					{
7720 						ttfPrintTextFormatted(ttf16, subx1 + 448, suby1 + 72, language[1393]);
7721 					}
7722 				}
7723 				else if ( victory == 3 )
7724 				{
7725 					if ( creature != HUMAN )
7726 					{
7727 						ttfPrintTextFormatted(ttf16, subx1 + 448, suby1 + 72, language[3361],
7728 							language[3363 - 1 + stats[clientnum]->playerRace]);
7729 					}
7730 					else
7731 					{
7732 						ttfPrintTextFormatted(ttf16, subx1 + 448, suby1 + 72, language[2911]);
7733 					}
7734 				}
7735 			}
7736 			else
7737 			{
7738 				ttfPrintTextFormatted(ttf16, subx1 + 448, suby1 + 40, language[1394]);
7739 				ttfPrintTextFormatted(ttf16, subx1 + 448, suby1 + 56, "%s", stats[clientnum]->name);
7740 
7741 				char classname[64];
7742 				strcpy(classname, playerClassLangEntry(client_classes[0], clientnum));
7743 				classname[0] -= 32;
7744 				int creature = HUMAN;
7745 				if ( players[clientnum] && players[clientnum]->entity )
7746 				{
7747 					creature = static_cast<int>(players[clientnum]->entity->getMonsterFromPlayerRace(stats[clientnum]->playerRace));
7748 				}
7749 				if ( creature != HUMAN )
7750 				{
7751 					ttfPrintTextFormatted(ttf16, subx1 + 448, suby1 + 72, language[3362], monstertypenamecapitalized[creature], classname);
7752 				}
7753 				else
7754 				{
7755 					ttfPrintTextFormatted(ttf16, subx1 + 448, suby1 + 72, language[1395], classname);
7756 				}
7757 			}
7758 
7759 			// print total score
7760 			if ( score_leaderboard_window != 3 )
7761 			{
7762 				node = list_Node(scoresPtr, score_window - 1);
7763 				if ( node )
7764 				{
7765 					score_t* score = (score_t*)node->element;
7766 					ttfPrintTextFormatted(ttf16, subx1 + 448, suby1 + 104, language[1404], totalScore(score));
7767 				}
7768 			}
7769 			else
7770 			{
7771 #ifdef STEAMWORKS
7772 				ttfPrintTextFormatted(ttf16, subx1 + 448, suby1 + 104, language[1404], g_SteamLeaderboards->downloadedTags[g_SteamLeaderboards->currentLeaderBoardIndex][TAG_TOTAL_SCORE]);
7773 #endif // STEAMWORKS
7774 			}
7775 
7776 			Entity* playerEntity = nullptr;
7777 			if ( players[clientnum] )
7778 			{
7779 				playerEntity = players[clientnum]->entity;
7780 			}
7781 
7782 			// print character stats
7783 			ttfPrintTextFormatted(ttf12, subx1 + 456, suby1 + 128, language[359], stats[clientnum]->LVL, playerClassLangEntry(client_classes[clientnum], clientnum));
7784 			ttfPrintTextFormatted(ttf12, subx1 + 456, suby1 + 140, language[1396], stats[clientnum]->EXP);
7785 			ttfPrintTextFormatted(ttf12, subx1 + 456, suby1 + 152, language[1397], stats[clientnum]->GOLD);
7786 			ttfPrintTextFormatted(ttf12, subx1 + 456, suby1 + 164, language[361], currentlevel);
7787 
7788 			ttfPrintTextFormatted(ttf12, subx1 + 456, suby1 + 188, language[1398], statGetSTR(stats[clientnum], playerEntity), stats[clientnum]->STR);
7789 			ttfPrintTextFormatted(ttf12, subx1 + 456, suby1 + 200, language[1399], statGetDEX(stats[clientnum], playerEntity), stats[clientnum]->DEX);
7790 			ttfPrintTextFormatted(ttf12, subx1 + 456, suby1 + 212, language[1400], statGetCON(stats[clientnum], playerEntity), stats[clientnum]->CON);
7791 			ttfPrintTextFormatted(ttf12, subx1 + 456, suby1 + 224, language[1401], statGetINT(stats[clientnum], playerEntity), stats[clientnum]->INT);
7792 			ttfPrintTextFormatted(ttf12, subx1 + 456, suby1 + 236, language[1402], statGetPER(stats[clientnum], playerEntity), stats[clientnum]->PER);
7793 			ttfPrintTextFormatted(ttf12, subx1 + 456, suby1 + 248, language[1403], statGetCHR(stats[clientnum], playerEntity), stats[clientnum]->CHR);
7794 
7795 			// time
7796 			Uint32 sec = (completionTime / TICKS_PER_SECOND) % 60;
7797 			Uint32 min = ((completionTime / TICKS_PER_SECOND) / 60) % 60;
7798 			Uint32 hour = ((completionTime / TICKS_PER_SECOND) / 60) / 60;
7799 			ttfPrintTextFormatted(ttf12, subx1 + 32, suby2 - 80, "%s: %02d:%02d:%02d. %s:", language[1405], hour, min, sec, language[1406]);
7800 			if ( !conductPenniless && !conductFoodless && !conductVegetarian && !conductIlliterate && !conductGameChallenges[CONDUCT_HARDCORE]
7801 				&& !conductGameChallenges[CONDUCT_CHEATS_ENABLED]
7802 				&& !conductGameChallenges[CONDUCT_MODDED]
7803 				&& !conductGameChallenges[CONDUCT_LIFESAVING]
7804 				&& !conductGameChallenges[CONDUCT_KEEPINVENTORY]
7805 				&& !conductGameChallenges[CONDUCT_BRAWLER]
7806 				&& !conductGameChallenges[CONDUCT_RANGED_ONLY]
7807 				&& !conductGameChallenges[CONDUCT_BLESSED_BOOTS_SPEED]
7808 				&& !conductGameChallenges[CONDUCT_BOOTS_SPEED]
7809 				&& !conductGameChallenges[CONDUCT_ACCURSED]
7810 				&& !conductGameChallenges[CONDUCT_MULTIPLAYER])
7811 			{
7812 				ttfPrintText(ttf12, subx1 + 32, suby2 - 64, language[1407]);
7813 			}
7814 			else
7815 			{
7816 				int b = 0;
7817 				strcpy(tempstr, " ");
7818 				if ( conductPenniless )
7819 				{
7820 					strcat(tempstr, language[1408]);
7821 					++b;
7822 				}
7823 				if ( conductFoodless )
7824 				{
7825 					if ( b > 0 )
7826 					{
7827 						strcat(tempstr, ", ");
7828 					}
7829 					strcat(tempstr, language[1409]);
7830 					++b;
7831 				}
7832 				if ( conductVegetarian )
7833 				{
7834 					if ( b > 0 )
7835 					{
7836 						strcat(tempstr, ", ");
7837 					}
7838 					strcat(tempstr, language[1410]);
7839 					++b;
7840 				}
7841 				if ( conductIlliterate )
7842 				{
7843 					if ( b > 0 )
7844 					{
7845 						strcat(tempstr, ", ");
7846 					}
7847 					strcat(tempstr, language[1411]);
7848 					++b;
7849 				}
7850 				for ( int c = 0; c < NUM_CONDUCT_CHALLENGES; ++c )
7851 				{
7852 					if ( conductGameChallenges[c] != 0 )
7853 					{
7854 						if ( b > 0 )
7855 						{
7856 							strcat(tempstr, ", ");
7857 						}
7858 						if ( b > 0 && b % 4 == 0 )
7859 						{
7860 							strcat(tempstr, "\n ");
7861 						}
7862 						strcat(tempstr, language[2925 + c]);
7863 						++b;
7864 					}
7865 				}
7866 				/*	if ( b > 0 )
7867 				{
7868 					tempstr[strlen(tempstr) - 2] = 0;
7869 				}*/
7870 				ttfPrintTextFormatted(ttf12, subx1 + 20, suby2 - 64, tempstr);
7871 			}
7872 
7873 			// kills
7874 			int x = 0, y = 0;
7875 			ttfPrintText(ttf12, subx1 + 456, suby1 + 272, language[1412]);
7876 			bool nokills = true;
7877 			for ( x = 0; x < NUMMONSTERS; x++ )
7878 			{
7879 				if ( kills[x] )
7880 				{
7881 					nokills = false;
7882 					if ( kills[x] > 1 )
7883 					{
7884 						if ( x < KOBOLD )
7885 						{
7886 							ttfPrintTextFormatted(ttf12, subx1 + 456 + (y / 14) * 156, suby1 + 296 + (y % 14) * 12, "%d %s", kills[x], language[111 + x]);
7887 						}
7888 						else
7889 						{
7890 							ttfPrintTextFormatted(ttf12, subx1 + 456 + (y / 14) * 156, suby1 + 296 + (y % 14) * 12, "%d %s", kills[x], language[2050 + (x - KOBOLD)]);
7891 						}
7892 					}
7893 					else
7894 					{
7895 						if ( x < KOBOLD )
7896 						{
7897 							ttfPrintTextFormatted(ttf12, subx1 + 456 + (y / 14) * 156, suby1 + 296 + (y % 14) * 12, "%d %s", kills[x], language[90 + x]);
7898 						}
7899 						else
7900 						{
7901 							ttfPrintTextFormatted(ttf12, subx1 + 456 + (y / 14) * 156, suby1 + 296 + (y % 14) * 12, "%d %s", kills[x], language[2000 + (x - KOBOLD)]);
7902 						}
7903 					}
7904 					y++;
7905 				}
7906 			}
7907 			if ( nokills )
7908 			{
7909 				ttfPrintText(ttf12, subx1 + 456, suby1 + 296, language[1413]);
7910 			}
7911 		}
7912 	}
7913 	else
7914 	{
7915 		scoreDisplayMultiplayer = false;
7916 	}
7917 
7918 	if ( savegames_window != 0 )
7919 	{
7920 		int numSavesToShow = 5;
7921 		int filenameMaxLength = 48;
7922 		int filename_padx = subx1 + 16;
7923 		int filename_pady = suby1 + 32;
7924 		int filename_padx2 = subx2 - 16 - 40;
7925 		int filename_pady2 = filename_pady + numSavesToShow * TTF12_HEIGHT + 8;
7926 		int filename_rowHeight = 2 * TTF12_HEIGHT + 8;
7927 		filename_pady += 3 * TTF12_HEIGHT;
7928 		int numSaves = savegamesList.size();
7929 		if ( numSaves > 0 )
7930 		{
7931 			//ttfPrintTextFormattedColor(ttf12, filename_padx, filename_pady, uint32ColorGreen(*mainsurface), language[3066]);
7932 		}
7933 
7934 		SDL_Rect tooltip; // we will draw the tooltip after drawing the other elements of the display window.
7935 
7936 		tooltip.x = omousex + 8;
7937 		tooltip.y = omousey + 8;
7938 		tooltip.w = 32 + TTF12_WIDTH * 14;
7939 		tooltip.h = TTF12_HEIGHT + 8;
7940 
7941 		filename_pady += 2 * TTF12_HEIGHT;
7942 
7943 		// do slider
7944 		SDL_Rect slider;
7945 		slider.x = filename_padx2 + 8;
7946 		slider.y = filename_pady - 8;
7947 		slider.h = suby2 - (filename_pady + 20);
7948 		slider.w = 32;
7949 
7950 		int entriesToScroll = std::max(static_cast<int>((numSaves / numSavesToShow) - 1), 0);
7951 		entriesToScroll = entriesToScroll * numSavesToShow + (numSaves % numSavesToShow);
7952 
7953 		bool drawScrollTooltip = false;
7954 
7955 		// handle slider movement.
7956 		if ( numSaves > numSavesToShow )
7957 		{
7958 			drawRect(&slider, SDL_MapRGB(mainsurface->format, 64, 64, 64), 255);
7959 			if ( mouseInBounds(filename_padx, slider.x + slider.w,
7960 				slider.y, slider.y + slider.h) )
7961 			{
7962 				if ( mouseInBounds(slider.x, slider.x + slider.w,
7963 					slider.y, slider.y + slider.h) )
7964 				{
7965 					drawScrollTooltip = true;
7966 				}
7967 				if ( mousestatus[SDL_BUTTON_WHEELUP] )
7968 				{
7969 					savegames_window_scroll = std::max(savegames_window_scroll - 1, 0);
7970 					mousestatus[SDL_BUTTON_WHEELUP] = 0;
7971 				}
7972 				if ( mousestatus[SDL_BUTTON_WHEELDOWN] )
7973 				{
7974 					savegames_window_scroll = std::min(savegames_window_scroll + 1, entriesToScroll);
7975 					mousestatus[SDL_BUTTON_WHEELDOWN] = 0;
7976 				}
7977 			}
7978 
7979 			if ( keystatus[SDL_SCANCODE_UP] )
7980 			{
7981 				savegames_window_scroll = std::max(savegames_window_scroll - 1, 0);
7982 				keystatus[SDL_SCANCODE_UP] = 0;
7983 			}
7984 			if ( keystatus[SDL_SCANCODE_DOWN] )
7985 			{
7986 				savegames_window_scroll = std::min(savegames_window_scroll + 1, entriesToScroll);
7987 				keystatus[SDL_SCANCODE_DOWN] = 0;
7988 			}
7989 			slider.h *= (1 / static_cast<real_t>(entriesToScroll + 1));
7990 			slider.y += slider.h * savegames_window_scroll;
7991 			if ( savegames_window_scroll == entriesToScroll ) // reached end.
7992 			{
7993 				slider.y += (suby2 - 28) - (slider.y + slider.h); // bottom of slider is (suby2 - 28), so move the y level to imitate hitting the bottom in case of rounding error.
7994 			}
7995 			drawWindowFancy(slider.x, slider.y, slider.x + slider.w, slider.y + slider.h); // draw shortened list relative slider.
7996 		}
7997 		else
7998 		{
7999 			//drawRect(&slider, SDL_MapRGB(mainsurface->format, 64, 64, 64), 255);
8000 			drawWindowFancy(slider.x, slider.y, slider.x + slider.w, slider.y + slider.h);
8001 		}
8002 
8003 		bool drawDeleteTooltip = false;
8004 		int numSingleplayerSaves = 0;
8005 		int numMultiplayerSaves = 0;
8006 
8007 		// draw the content
8008 		for ( int i = 0; i < numSaves && !savegamesList.empty(); ++i )
8009 		{
8010 			filename_padx = subx1 + 16;
8011 
8012 			std::vector<std::tuple<int, int, int, std::string>>::iterator it = savegamesList.begin();
8013 			std::advance(it, i);
8014 			std::tuple<int, int, int, std::string> entry = *it;
8015 
8016 			if ( std::get<1>(entry) != SINGLE )
8017 			{
8018 				++numMultiplayerSaves;
8019 			}
8020 			else
8021 			{
8022 				++numSingleplayerSaves;
8023 			}
8024 
8025 			if ( i >= savegames_window_scroll && i < numSavesToShow + savegames_window_scroll )
8026 			{
8027 				drawWindowFancy(filename_padx, filename_pady - 8, filename_padx2, filename_pady + filename_rowHeight);
8028 				SDL_Rect highlightEntry;
8029 				highlightEntry.x = filename_padx;
8030 				highlightEntry.y = filename_pady - 8;
8031 				highlightEntry.w = filename_padx2 - filename_padx;
8032 				highlightEntry.h = filename_rowHeight + 8;
8033 				if ( gamemods_numCurrentModsLoaded >= 0 )
8034 				{
8035 					if ( std::get<1>(entry) == SINGLE ) // single player.
8036 					{
8037 						drawRect(&highlightEntry, uint32ColorGreen(*mainsurface), 64);
8038 					}
8039 					else
8040 					{
8041 						drawRect(&highlightEntry, uint32ColorGreen(*mainsurface), 32);
8042 					}
8043 				}
8044 				else
8045 				{
8046 					if ( std::get<1>(entry) == SINGLE ) // single player.
8047 					{
8048 						drawRect(&highlightEntry, SDL_MapRGB(mainsurface->format, 128, 128, 128), 48);
8049 						//drawRect(&highlightEntry, uint32ColorBaronyBlue(*mainsurface), 16);
8050 					}
8051 					else
8052 					{
8053 						drawRect(&highlightEntry, uint32ColorBaronyBlue(*mainsurface), 32);
8054 					}
8055 				}
8056 
8057 				ttfPrintTextFormatted(ttf12, filename_padx + 8, filename_pady, "[%d]: %s", i + 1, std::get<3>(entry).c_str());
8058 
8059 				filename_padx = filename_padx2 - (13 * TTF12_WIDTH + 16);
8060 				int text_x = filename_padx;
8061 				int text_y = filename_pady + 10;
8062 				if ( drawClickableButton(filename_padx, filename_pady, 10 * TTF12_WIDTH + 8, TTF12_HEIGHT * 2 + 4, 0) )
8063 				{
8064 					if ( std::get<1>(entry) == SINGLE )
8065 					{
8066 						savegameCurrentFileIndex = std::get<2>(entry);
8067 						buttonLoadSingleplayerGame(nullptr);
8068 					}
8069 					else
8070 					{
8071 						savegameCurrentFileIndex = std::get<2>(entry);
8072 						buttonLoadMultiplayerGame(nullptr);
8073 					}
8074 				}
8075 				ttfPrintTextFormatted(ttf12, text_x + 8, text_y, "%s", "Load Game");
8076 
8077 				filename_padx = filename_padx2 - (2 * TTF12_WIDTH + 14);
8078 				text_x = filename_padx;
8079 				if ( drawClickableButton(filename_padx, filename_pady, 2 * TTF12_WIDTH + 8, TTF12_HEIGHT * 2 + 4, uint32ColorRed(*mainsurface)) )
8080 				{
8081 					if ( std::get<1>(entry) == SINGLE )
8082 					{
8083 						savegameCurrentFileIndex = std::get<2>(entry);
8084 						buttonDeleteSavedSoloGame(nullptr);
8085 					}
8086 					else
8087 					{
8088 						savegameCurrentFileIndex = std::get<2>(entry);
8089 						buttonDeleteSavedMultiplayerGame(nullptr);
8090 					}
8091 				}
8092 				ttfPrintTextFormatted(ttf12, text_x + 6, text_y, "%s", "X");
8093 				if ( mouseInBounds(filename_padx, filename_padx + 2 * TTF12_WIDTH + 8, filename_pady, filename_pady + TTF12_HEIGHT * 2 + 4) )
8094 				{
8095 					drawDeleteTooltip = true;
8096 				}
8097 
8098 				filename_pady += 3 * filename_rowHeight / 2;
8099 			}
8100 		}
8101 
8102 		Uint32 saveNumColor = uint32ColorGreen(*mainsurface);
8103 		if ( numSingleplayerSaves == SAVE_GAMES_MAX )
8104 		{
8105 			saveNumColor = uint32ColorOrange(*mainsurface);
8106 		}
8107 		ttfPrintTextFormattedColor(ttf12, subx2 - (longestline(language[3067]) * TTF12_WIDTH), suby1 + 44, saveNumColor,
8108 			language[3067], numSingleplayerSaves, SAVE_GAMES_MAX);
8109 
8110 		saveNumColor = uint32ColorGreen(*mainsurface);
8111 		if ( numMultiplayerSaves == SAVE_GAMES_MAX )
8112 		{
8113 			saveNumColor = uint32ColorOrange(*mainsurface);
8114 		}
8115 		ttfPrintTextFormattedColor(ttf12, subx2 - (longestline(language[3068]) * TTF12_WIDTH), suby1 + 44 + TTF12_HEIGHT + 4, saveNumColor,
8116 			language[3068], numMultiplayerSaves, SAVE_GAMES_MAX);
8117 
8118 		// draw the tooltip we initialised earlier.
8119 		if ( drawDeleteTooltip )
8120 		{
8121 			tooltip.w = longestline(language[3064]) * TTF12_WIDTH + 16;
8122 			drawTooltip(&tooltip);
8123 			ttfPrintTextFormatted(ttf12, tooltip.x + 6, tooltip.y + 6, language[3064]);
8124 		}
8125 		else if ( drawScrollTooltip )
8126 		{
8127 			tooltip.w = longestline(language[3066]) * TTF12_WIDTH + 16;
8128 			drawTooltip(&tooltip);
8129 			ttfPrintTextFormatted(ttf12, tooltip.x + 6, tooltip.y + 6, language[3066]);
8130 		}
8131 	}
8132 	else if ( gamemods_window != 0 )
8133 	{
8134 		int filenameMaxLength = 24;
8135 		int filename_padx = subx1 + 16;
8136 		int filename_pady = suby1 + 32;
8137 		int numFileEntries = 10;
8138 
8139 		int filename_padx2 = filename_padx + filenameMaxLength * TTF12_WIDTH + 8;
8140 		int filename_pady2 = filename_pady + numFileEntries * TTF12_HEIGHT + 8;
8141 #ifdef STEAMWORKS
8142 		if ( gamemods_window == 1 || gamemods_window == 2 || gamemods_window == 5 )
8143 		{
8144 			if ( !currentDirectoryFiles.empty() )
8145 			{
8146 				int lineNumber = 0;
8147 				std::string line;
8148 				std::list<std::string>::const_iterator it = currentDirectoryFiles.begin();
8149 				std::advance(it, gamemods_window_scroll);
8150 
8151 				drawWindow(filename_padx, filename_pady - 2,
8152 					filename_padx2, filename_pady2);
8153 
8154 				SDL_Rect pos;
8155 				pos.x = filename_padx;
8156 				pos.y = filename_pady - 2 + std::max(gamemods_window_fileSelect - 1, 0) * TTF12_HEIGHT;
8157 				pos.w = filenameMaxLength * TTF12_WIDTH + 8;
8158 				pos.h = TTF12_HEIGHT;
8159 				if ( gamemods_window_fileSelect != 0 )
8160 				{
8161 					drawRect(&pos, SDL_MapRGB(mainsurface->format, 64, 64, 64), 255);
8162 				}
8163 
8164 				for ( ; it != currentDirectoryFiles.end() && lineNumber < numFileEntries; ++it )
8165 				{
8166 					line = *it;
8167 					line = line.substr(0, filenameMaxLength);
8168 					ttfPrintTextFormatted(ttf12, filename_padx, filename_pady + lineNumber * TTF12_HEIGHT, "%s", line.c_str());
8169 					++lineNumber;
8170 				}
8171 				int entriesToScroll = std::max(static_cast<int>((currentDirectoryFiles.size() / numFileEntries) - 1), 0);
8172 				entriesToScroll = entriesToScroll * numFileEntries + (currentDirectoryFiles.size() % numFileEntries);
8173 
8174 				if ( mouseInBounds(filename_padx - 4, filename_padx2,
8175 					filename_pady, filename_pady2) && currentDirectoryFiles.size() > numFileEntries )
8176 				{
8177 					if ( mousestatus[SDL_BUTTON_WHEELUP] )
8178 					{
8179 						gamemods_window_scroll = std::max(gamemods_window_scroll - 1, 0);
8180 						mousestatus[SDL_BUTTON_WHEELUP] = 0;
8181 					}
8182 					if ( mousestatus[SDL_BUTTON_WHEELDOWN] )
8183 					{
8184 						gamemods_window_scroll = std::min(gamemods_window_scroll + 1, entriesToScroll);
8185 						mousestatus[SDL_BUTTON_WHEELDOWN] = 0;
8186 					}
8187 				}
8188 				for ( int i = 1; i <= numFileEntries; ++i )
8189 				{
8190 					if ( mouseInBounds(filename_padx - 4, filename_padx2,
8191 						filename_pady - 2, filename_pady - 2 + i * TTF12_HEIGHT) )
8192 					{
8193 						if ( mousestatus[SDL_BUTTON_LEFT] )
8194 						{
8195 							gamemods_window_fileSelect = i;
8196 							mousestatus[SDL_BUTTON_LEFT] = 0;
8197 						}
8198 					}
8199 				}
8200 				if ( !directoryToUpload.empty() )
8201 				{
8202 					ttfPrintTextFormatted(ttf12, filename_padx, filename_pady2 + TTF12_HEIGHT, "folder to upload: %s", directoryToUpload.c_str());
8203 				}
8204 			}
8205 		}
8206 		if ( gamemods_window == 2 || gamemods_window == 5 )
8207 		{
8208 			numFileEntries = 20;
8209 			filename_padx = subx2 - (filenameMaxLength * TTF12_WIDTH + 16);
8210 			filename_padx2 = subx2 - 16;
8211 			filename_pady = filename_pady2 + 2 * TTF12_HEIGHT;
8212 			filename_pady2 = filename_pady + numFileEntries * TTF12_HEIGHT + 2;
8213 			if ( !directoryFilesListToUpload.empty() )
8214 			{
8215 				ttfPrintTextFormatted(ttf12, filename_padx, filename_pady - TTF12_HEIGHT, "file preview in folder:");
8216 				drawWindow(filename_padx, filename_pady - 2,
8217 					filename_padx2, filename_pady2);
8218 				int lineNumber = 0;
8219 				std::string line;
8220 				for ( std::list<std::string>::const_iterator it = directoryFilesListToUpload.begin(); it != directoryFilesListToUpload.end() && lineNumber < 20; ++it )
8221 				{
8222 					line = *it;
8223 					if ( line.size() >= filenameMaxLength )
8224 					{
8225 						line = line.substr(0, filenameMaxLength - 3);
8226 						line.append("..");
8227 					}
8228 					else
8229 					{
8230 						line = line.substr(0, filenameMaxLength);
8231 					}
8232 					ttfPrintTextFormatted(ttf12, filename_padx, filename_pady + lineNumber * TTF12_HEIGHT, "%s", line.c_str());
8233 					++lineNumber;
8234 				}
8235 			}
8236 
8237 			int status_padx = subx1 + 16;
8238 			int status_pady = filename_pady;
8239 			if ( gamemods_uploadStatus != 0 && g_SteamWorkshop )
8240 			{
8241 				status_pady += 3 * TTF12_HEIGHT;
8242 				if ( gamemods_window == 2 )
8243 				{
8244 					if ( g_SteamWorkshop->SubmitItemUpdateResult.m_eResult == 0
8245 						&& gamemods_uploadStatus < 5 )
8246 					{
8247 						switch ( g_SteamWorkshop->createItemResult.m_eResult )
8248 						{
8249 							case 0:
8250 								ttfPrintTextFormatted(ttf12, status_padx, status_pady, "creating item...");
8251 								break;
8252 							case k_EResultOK:
8253 								if ( gamemods_uploadStatus < 2 )
8254 								{
8255 									for ( node = button_l.first; node != NULL; node = nextnode )
8256 									{
8257 										nextnode = node->next;
8258 										button = (button_t*)node->element;
8259 										if ( button->action == &buttonGamemodsPrepareWorkshopItemUpload )
8260 										{
8261 											button->visible = false;
8262 										}
8263 									}
8264 									gamemods_uploadStatus = 2;
8265 									g_SteamWorkshop->StartItemUpdate();
8266 								}
8267 								else
8268 								{
8269 									if ( g_SteamWorkshop->UGCUpdateHandle == 0 )
8270 									{
8271 										ttfPrintTextFormattedColor(ttf12, status_padx, status_pady, uint32ColorOrange(*mainsurface), "item created! awaiting file handle...");
8272 									}
8273 									else
8274 									{
8275 										if ( gamemods_uploadStatus == 2 )
8276 										{
8277 											gamemods_uploadStatus = 3;
8278 											// set item fields button
8279 											button = newButton();
8280 											strcpy(button->label, "set item fields");
8281 											button->x = subx1 + 16;
8282 											button->y = suby1 + TTF12_HEIGHT * 34;
8283 											button->sizex = 16 * TTF12_WIDTH + 8;
8284 											button->sizey = 32;
8285 											button->action = &buttonGamemodsSetWorkshopItemFields;
8286 											button->visible = 1;
8287 											button->focused = 1;
8288 											gamemods_currentEditField = 0;
8289 										}
8290 										ttfPrintTextFormattedColor(ttf12, status_padx, status_pady, uint32ColorGreen(*mainsurface), "item and file handle create success!");
8291 									}
8292 								}
8293 								break;
8294 							default:
8295 								// error in createItem!
8296 								ttfPrintTextFormatted(ttf12, status_padx, status_pady, "error in creating item!");
8297 								break;
8298 						}
8299 						status_pady += 2 * TTF12_HEIGHT;
8300 						if ( gamemods_uploadStatus >= 3 && g_SteamWorkshop->SubmitItemUpdateResult.m_eResult == 0 )
8301 						{
8302 							for ( int fields = 0; fields < 2; ++fields )
8303 							{
8304 								status_pady += TTF12_HEIGHT;
8305 								drawDepressed(status_padx, status_pady - 4, status_padx + 32 * TTF12_WIDTH, status_pady + TTF12_HEIGHT);
8306 								switch ( fields )
8307 								{
8308 									case 0:
8309 										ttfPrintText(ttf12, status_padx + 8, status_pady - TTF12_HEIGHT, "Enter a title:");
8310 										if ( gamemods_uploadStatus == 3 && gamemods_workshopSetPropertyReturn[0] )
8311 										{
8312 											ttfPrintTextColor(ttf12, status_padx + 20 * TTF12_WIDTH, status_pady - TTF12_HEIGHT, uint32ColorGreen(*mainsurface), true, "success set");
8313 										}
8314 										ttfPrintText(ttf12, status_padx + 8, status_pady, gamemods_uploadTitle);
8315 										break;
8316 									case 1:
8317 										ttfPrintText(ttf12, status_padx + 8, status_pady - TTF12_HEIGHT, "Enter description:");
8318 										if ( gamemods_uploadStatus == 3 && gamemods_workshopSetPropertyReturn[1] )
8319 										{
8320 
8321 										}
8322 										ttfPrintText(ttf12, status_padx + 8, status_pady, gamemods_uploadDescription);
8323 										break;
8324 									default:
8325 										break;
8326 								}
8327 								if ( gamemods_uploadStatus == 4 )
8328 								{
8329 									if ( gamemods_workshopSetPropertyReturn[fields] )
8330 									{
8331 										ttfPrintTextColor(ttf12, status_padx + 20 * TTF12_WIDTH, status_pady - TTF12_HEIGHT, uint32ColorGreen(*mainsurface), true, "success set");
8332 									}
8333 									else
8334 									{
8335 										ttfPrintTextColor(ttf12, status_padx + 20 * TTF12_WIDTH, status_pady - TTF12_HEIGHT, uint32ColorRed(*mainsurface), true, "error!");
8336 									}
8337 								}
8338 
8339 								if ( mouseInBounds(status_padx, status_padx + 32 * TTF12_WIDTH, status_pady - 4, status_pady + TTF12_HEIGHT) )
8340 								{
8341 									if ( mousestatus[SDL_BUTTON_LEFT] )
8342 									{
8343 										switch ( fields )
8344 										{
8345 											case 0:
8346 												inputstr = gamemods_uploadTitle;
8347 												gamemods_currentEditField = 0;
8348 												break;
8349 											case 1:
8350 												inputstr = gamemods_uploadDescription;
8351 												gamemods_currentEditField = 1;
8352 												break;
8353 											default:
8354 												break;
8355 										}
8356 										mousestatus[SDL_BUTTON_LEFT] = 0;
8357 									}
8358 								}
8359 
8360 								if ( gamemods_uploadStatus == 3 && !SDL_IsTextInputActive() )
8361 								{
8362 									inputstr = gamemods_uploadTitle;
8363 									SDL_StartTextInput();
8364 								}
8365 								inputlen = 30;
8366 								if ( SDL_IsTextInputActive() && gamemods_currentEditField == fields
8367 									&& (ticks - cursorflash) % TICKS_PER_SECOND < TICKS_PER_SECOND / 2 )
8368 								{
8369 									int x;
8370 									TTF_SizeUTF8(ttf12, inputstr, &x, NULL);
8371 									ttfPrintText(ttf12, status_padx + x + 8, status_pady, "_");
8372 								}
8373 								status_pady += 2 * TTF12_HEIGHT;
8374 							}
8375 							if ( gamemods_uploadStatus >= 4 )
8376 							{
8377 								if ( gamemods_workshopSetPropertyReturn[2] )
8378 								{
8379 									ttfPrintTextColor(ttf12, status_padx, status_pady, uint32ColorGreen(*mainsurface), true, "folder path success set");
8380 								}
8381 								else
8382 								{
8383 									ttfPrintTextColor(ttf12, status_padx, status_pady, uint32ColorRed(*mainsurface), true, "error in folder path!");
8384 								}
8385 							}
8386 
8387 							status_pady += 2 * TTF12_HEIGHT;
8388 							// set some workshop item tags
8389 							ttfPrintText(ttf12, status_padx, status_pady, "Select workshop tags");
8390 							int tag_padx = status_padx;
8391 							int tag_pady = status_pady + TTF12_HEIGHT;
8392 							int tag_padx1 = tag_padx + 20 * TTF12_WIDTH;
8393 							gamemodsDrawWorkshopItemTagToggle("dungeons", tag_padx, tag_pady);
8394 							gamemodsDrawWorkshopItemTagToggle("textures", tag_padx1, tag_pady);
8395 							tag_pady += TTF12_HEIGHT + 4;
8396 							gamemodsDrawWorkshopItemTagToggle("models", tag_padx, tag_pady);
8397 							gamemodsDrawWorkshopItemTagToggle("gameplay", tag_padx1, tag_pady);
8398 							tag_pady += TTF12_HEIGHT + 4;
8399 							gamemodsDrawWorkshopItemTagToggle("audio", tag_padx, tag_pady);
8400 							gamemodsDrawWorkshopItemTagToggle("translations", tag_padx1, tag_pady);
8401 							tag_pady += TTF12_HEIGHT + 4;
8402 							gamemodsDrawWorkshopItemTagToggle("misc", tag_padx, tag_pady);
8403 						}
8404 					}
8405 				}
8406 				else if ( gamemods_window == 5 && g_SteamWorkshop->m_myWorkshopItemToModify.m_nPublishedFileId != 0 && gamemods_uploadStatus < 5 )
8407 				{
8408 					std::string line = g_SteamWorkshop->m_myWorkshopItemToModify.m_rgchTitle;
8409 					if ( line.size() > filenameMaxLength )
8410 					{
8411 						line = line.substr(0, filenameMaxLength - 2);
8412 						line.append("..");
8413 					}
8414 					status_pady += 2 * TTF12_HEIGHT;
8415 					ttfPrintTextFormattedColor(ttf12, status_padx + 8, status_pady, uint32ColorBaronyBlue(*mainsurface), "Title:");
8416 					status_pady += TTF12_HEIGHT;
8417 					ttfPrintTextFormatted(ttf12, status_padx + 8, status_pady, "%s", line.c_str());
8418 
8419 					line = g_SteamWorkshop->m_myWorkshopItemToModify.m_rgchDescription;
8420 					if ( line.size() > filenameMaxLength )
8421 					{
8422 						line = line.substr(0, filenameMaxLength - 2);
8423 						line.append("..");
8424 					}
8425 					status_pady += TTF12_HEIGHT;
8426 					ttfPrintTextFormattedColor(ttf12, status_padx + 8, status_pady, uint32ColorBaronyBlue(*mainsurface), "Description:");
8427 					status_pady += TTF12_HEIGHT;
8428 					ttfPrintTextFormatted(ttf12, status_padx + 8, status_pady, "%s", line.c_str());
8429 
8430 					status_pady += 2 * TTF12_HEIGHT;
8431 					// set some workshop item tags
8432 					ttfPrintText(ttf12, status_padx, status_pady, "Modify workshop tags");
8433 					int tag_padx = status_padx;
8434 					int tag_pady = status_pady + TTF12_HEIGHT;
8435 					int tag_padx1 = tag_padx + 20 * TTF12_WIDTH;
8436 					gamemodsDrawWorkshopItemTagToggle("dungeons", tag_padx, tag_pady);
8437 					gamemodsDrawWorkshopItemTagToggle("textures", tag_padx1, tag_pady);
8438 					tag_pady += TTF12_HEIGHT + 4;
8439 					gamemodsDrawWorkshopItemTagToggle("models", tag_padx, tag_pady);
8440 					gamemodsDrawWorkshopItemTagToggle("gameplay", tag_padx1, tag_pady);
8441 					tag_pady += TTF12_HEIGHT + 4;
8442 					gamemodsDrawWorkshopItemTagToggle("audio", tag_padx, tag_pady);
8443 					gamemodsDrawWorkshopItemTagToggle("translations", tag_padx1, tag_pady);
8444 					tag_pady += TTF12_HEIGHT + 4;
8445 					gamemodsDrawWorkshopItemTagToggle("misc", tag_padx, tag_pady);
8446 
8447 					status_pady += 6 * TTF12_HEIGHT;
8448 					if ( directoryFilesListToUpload.empty() )
8449 					{
8450 						ttfPrintTextFormattedColor(ttf12, status_padx, status_pady, uint32ColorGreen(*mainsurface), "Only Workshop tags will be updated.");
8451 					}
8452 					else
8453 					{
8454 						ttfPrintTextFormattedColor(ttf12, status_padx, status_pady, uint32ColorOrange(*mainsurface), "Workshop file contents will be updated.");
8455 					}
8456 				}
8457 				status_pady += 5 * TTF12_HEIGHT;
8458 				if ( gamemods_uploadStatus >= 5 )
8459 				{
8460 					uint64 bytesProc;
8461 					uint64 bytesTotal;
8462 					int status = SteamUGC()->GetItemUpdateProgress(g_SteamWorkshop->UGCUpdateHandle, &bytesProc, &bytesTotal);
8463 					if ( g_SteamWorkshop->SubmitItemUpdateResult.m_eResult != 0 )
8464 					{
8465 						for ( node = button_l.first; node != NULL; node = nextnode )
8466 						{
8467 							nextnode = node->next;
8468 							button = (button_t*)node->element;
8469 							if ( button->action == &buttonGamemodsPrepareWorkshopItemUpload
8470 								|| button->action == &buttonGamemodsStartUploadItem
8471 								|| button->action == &buttonGamemodsSetWorkshopItemFields
8472 								|| button->action == &buttonGamemodsSelectDirectoryForUpload
8473 								|| button->action == &buttonGamemodsModifyExistingWorkshopItemFields
8474 								|| button->action == &buttonGamemodsCancelModifyFileContents )
8475 							{
8476 								button->visible = false;
8477 							}
8478 						}
8479 						if ( g_SteamWorkshop->SubmitItemUpdateResult.m_eResult == k_EResultOK )
8480 						{
8481 							if ( g_SteamWorkshop->uploadSuccessTicks == 0 )
8482 							{
8483 								g_SteamWorkshop->uploadSuccessTicks = ticks;
8484 							}
8485 							else
8486 							{
8487 								if ( ticks - g_SteamWorkshop->uploadSuccessTicks > TICKS_PER_SECOND * 5 )
8488 								{
8489 									//cleanup the interface.
8490 									buttonCloseSubwindow(NULL);
8491 									list_FreeAll(&button_l);
8492 									deleteallbuttons = true;
8493 									gamemodsSubscribedItemsInit();
8494 								}
8495 							}
8496 							ttfPrintTextFormattedColor(ttf12, status_padx, status_pady, uint32ColorGreen(*mainsurface), "successfully uploaded!");
8497 							ttfPrintTextFormattedColor(ttf12, status_padx, status_pady + TTF12_HEIGHT, uint32ColorGreen(*mainsurface), "reloading window in %d...!", 5 - ((ticks - g_SteamWorkshop->uploadSuccessTicks) / TICKS_PER_SECOND));
8498 						}
8499 						else
8500 						{
8501 							ttfPrintTextFormattedColor(ttf12, status_padx, status_pady, uint32ColorOrange(*mainsurface), "error! %d", g_SteamWorkshop->SubmitItemUpdateResult.m_eResult);
8502 							ttfPrintTextFormattedColor(ttf12, status_padx, status_pady + TTF12_HEIGHT, uint32ColorOrange(*mainsurface), "close the window and try again.");
8503 						}
8504 					}
8505 					else
8506 					{
8507 						ttfPrintTextFormattedColor(ttf12, status_padx, status_pady, uint32ColorOrange(*mainsurface), "uploading... status %d", status);
8508 						ttfPrintTextFormattedColor(ttf12, status_padx, status_pady + TTF12_HEIGHT, uint32ColorOrange(*mainsurface), "bytes processed: %d", bytesProc);
8509 					}
8510 				}
8511 			}
8512 		}
8513 		if ( gamemods_window == 3 || gamemods_window == 4 )
8514 		{
8515 			numFileEntries = 8;
8516 			filenameMaxLength = 48;
8517 			filename_padx = subx1 + 16;
8518 			filename_pady = suby1 + 32;
8519 			filename_padx2 = subx2 - 16 - 40;
8520 			filename_pady2 = filename_pady + numFileEntries * TTF12_HEIGHT + 8;
8521 			int filename_rowHeight = 2 * TTF12_HEIGHT + 8;
8522 
8523 
8524 			if ( gamemods_subscribedItemsStatus == 0 )
8525 			{
8526 				if ( g_SteamWorkshop->SteamUGCQueryCompleted.m_eResult == k_EResultOK )
8527 				{
8528 					gamemods_subscribedItemsStatus = 1;
8529 				}
8530 			}
8531 			else
8532 			{
8533 				filename_pady += 1 * TTF12_HEIGHT;
8534 
8535 
8536 				filename_pady += 2 * TTF12_HEIGHT;
8537 				if ( gamemods_window == 3 )
8538 				{
8539 					ttfPrintTextFormattedColor(ttf12, filename_padx, filename_pady, uint32ColorGreen(*mainsurface), "successfully retrieved subscribed items!");
8540 				}
8541 				else
8542 				{
8543 					ttfPrintTextFormattedColor(ttf12, filename_padx, filename_pady, uint32ColorGreen(*mainsurface), "successfully retrieved my workshop items!");
8544 				}
8545 
8546 				std::string modInfoStr = "current loaded mods (hover for info): ";
8547 				SDL_Rect tooltip; // we will draw the tooltip after drawing the other elements of the display window.
8548 				bool drawModLoadOrder = false;
8549 				int drawExtendedInformationForMod = -1; // value of 0 or greater will draw.
8550 				int maxDescriptionLines = 10;
8551 
8552 				tooltip.x = omousex - 256;
8553 				tooltip.y = omousey + 16;
8554 				tooltip.w = 32 + TTF12_WIDTH * 64;
8555 				tooltip.h = (gamemods_mountedFilepaths.size() + 1) * TTF12_HEIGHT + 8;
8556 
8557 				if ( gamemods_mountedFilepaths.size() > 0 && gamemods_window == 3 )
8558 				{
8559 					ttfPrintTextFormatted(ttf12, filename_padx2 - modInfoStr.length() * TTF12_WIDTH - 16, filename_pady, "%s %2d", modInfoStr.c_str(), gamemods_mountedFilepaths.size());
8560 					if ( mouseInBounds(filename_padx2 - modInfoStr.length() * TTF12_WIDTH - 16, filename_padx2, filename_pady, filename_pady + TTF12_HEIGHT) )
8561 					{
8562 						drawModLoadOrder = true;
8563 					}
8564 					else
8565 					{
8566 						drawModLoadOrder = false;
8567 					}
8568 				}
8569 
8570 				filename_pady += 2 * TTF12_HEIGHT;
8571 
8572 				// do slider
8573 				SDL_Rect slider;
8574 				slider.x = filename_padx2 + 8;
8575 				slider.y = filename_pady - 8;
8576 				slider.h = suby2 - (filename_pady + 20);
8577 				slider.w = 32;
8578 
8579 				int numSubscribedItemsReturned = g_SteamWorkshop->SteamUGCQueryCompleted.m_unNumResultsReturned;
8580 				int entriesToScroll = std::max(static_cast<int>((numSubscribedItemsReturned / numFileEntries) - 1), 0);
8581 				entriesToScroll = entriesToScroll * numFileEntries + (numSubscribedItemsReturned % numFileEntries);
8582 
8583 				// handle slider movement.
8584 				if ( numSubscribedItemsReturned > numFileEntries )
8585 				{
8586 					drawRect(&slider, SDL_MapRGB(mainsurface->format, 64, 64, 64), 255);
8587 					if ( mouseInBounds(filename_padx, slider.x + slider.w,
8588 						slider.y, slider.y + slider.h) )
8589 					{
8590 						if ( mousestatus[SDL_BUTTON_WHEELUP] )
8591 						{
8592 							gamemods_window_scroll = std::max(gamemods_window_scroll - 1, 0);
8593 							mousestatus[SDL_BUTTON_WHEELUP] = 0;
8594 						}
8595 						if ( mousestatus[SDL_BUTTON_WHEELDOWN] )
8596 						{
8597 							gamemods_window_scroll = std::min(gamemods_window_scroll + 1, entriesToScroll);
8598 							mousestatus[SDL_BUTTON_WHEELDOWN] = 0;
8599 						}
8600 					}
8601 
8602 					if ( keystatus[SDL_SCANCODE_UP] )
8603 					{
8604 						gamemods_window_scroll = std::max(gamemods_window_scroll - 1, 0);
8605 						keystatus[SDL_SCANCODE_UP] = 0;
8606 					}
8607 					if ( keystatus[SDL_SCANCODE_DOWN] )
8608 					{
8609 						gamemods_window_scroll = std::min(gamemods_window_scroll + 1, entriesToScroll);
8610 						keystatus[SDL_SCANCODE_DOWN] = 0;
8611 					}
8612 					slider.h *= (1 / static_cast<real_t>(entriesToScroll + 1));
8613 					slider.y += slider.h * gamemods_window_scroll;
8614 					if ( gamemods_window_scroll == entriesToScroll ) // reached end.
8615 					{
8616 						slider.y += (suby2 - 28) - (slider.y + slider.h); // bottom of slider is (suby2 - 28), so move the y level to imitate hitting the bottom in case of rounding error.
8617 					}
8618 					drawWindowFancy(slider.x, slider.y, slider.x + slider.w, slider.y + slider.h); // draw shortened list relative slider.
8619 				}
8620 
8621 				// draw last message results
8622 				if ( ticks - g_SteamWorkshop->LastActionResult.creationTick < TICKS_PER_SECOND * 5 )
8623 				{
8624 					ttfPrintTextFormattedColor(ttf12, filename_padx + 8, suby2 - TTF12_HEIGHT - 4, uint32ColorOrange(*mainsurface), "%s returned status %d",
8625 						g_SteamWorkshop->LastActionResult.actionMsg.c_str(), static_cast<int>(g_SteamWorkshop->LastActionResult.lastResult));
8626 				}
8627 
8628 				// draw the content
8629 				for ( int i = gamemods_window_scroll; i < numSubscribedItemsReturned && i < numFileEntries + gamemods_window_scroll; ++i )
8630 				{
8631 					filename_padx = subx1 + 16;
8632 					SteamUGCDetails_t itemDetails = g_SteamWorkshop->m_subscribedItemListDetails[i];
8633 					char fullpath[PATH_MAX];
8634 					if ( itemDetails.m_eResult == k_EResultOK )
8635 					{
8636 						drawWindowFancy(filename_padx, filename_pady - 8, filename_padx2, filename_pady + filename_rowHeight);
8637 						SDL_Rect highlightEntry;
8638 						highlightEntry.x = filename_padx;
8639 						highlightEntry.y = filename_pady - 8;
8640 						highlightEntry.w = filename_padx2 - filename_padx;
8641 						highlightEntry.h = filename_rowHeight + 8;
8642 						drawRect(&highlightEntry, SDL_MapRGB(mainsurface->format, 128, 128, 128), 64);
8643 
8644 						bool itemDownloaded = SteamUGC()->GetItemInstallInfo(itemDetails.m_nPublishedFileId, NULL, fullpath, PATH_MAX, NULL);
8645 						bool pathIsMounted = gamemodsIsPathInMountedFiles(fullpath);
8646 
8647 						if ( pathIsMounted && gamemods_window == 3 )
8648 						{
8649 							SDL_Rect pos;
8650 							pos.x = filename_padx + 2;
8651 							pos.y = filename_pady - 6;
8652 							pos.w = filename_padx2 - filename_padx - 4;
8653 							pos.h = filename_rowHeight + 4;
8654 							drawRect(&pos, uint32ColorGreen(*mainsurface), 64);
8655 						}
8656 
8657 						// draw preview title
8658 						std::string line = itemDetails.m_rgchTitle;
8659 						std::size_t found = line.find_first_of('\n');
8660 						if ( found != std::string::npos && found < filenameMaxLength - 2 )
8661 						{
8662 							line = line.substr(0, found); // don't print out newlines.
8663 							line.append("..");
8664 						}
8665 						else if ( line.length() >= filenameMaxLength )
8666 						{
8667 							line = line.substr(0, filenameMaxLength - 2);
8668 							line.append("..");
8669 						}
8670 						ttfPrintTextFormatted(ttf12, filename_padx + 8, filename_pady, "Title: %s", line.c_str());
8671 
8672 						// draw preview description
8673 						line = itemDetails.m_rgchDescription;
8674 						found = line.find_first_of('\n');
8675 						if ( found != std::string::npos && found < filenameMaxLength - 2 )
8676 						{
8677 							line = line.substr(0, found);
8678 							line.append("..");
8679 						}
8680 						else if ( line.length() >= filenameMaxLength )
8681 						{
8682 							line = line.substr(0, filenameMaxLength - 2);
8683 							line.append("..");
8684 						}
8685 						ttfPrintTextFormatted(ttf12, filename_padx + 8, filename_pady + TTF12_HEIGHT, "Desc: %s", line.c_str());
8686 
8687 						// if hovering over title or description, provide more info...
8688 						if ( mouseInBounds(filename_padx + 8, filename_padx + 8 + 52 * TTF12_WIDTH, filename_pady + TTF12_HEIGHT, filename_pady + 2 * TTF12_HEIGHT) )
8689 						{
8690 							drawExtendedInformationForMod = i;
8691 						}
8692 
8693 						filename_padx = filename_padx2 - (12 * TTF12_WIDTH + 16) * 2;
8694 						// download button
8695 						if ( gamemods_window == 3 )
8696 						{
8697 							if ( !itemDownloaded )
8698 							{
8699 								if ( gamemodsDrawClickableButton(filename_padx, filename_pady, 12 * TTF12_WIDTH + 8, TTF12_HEIGHT, uint32ColorBaronyBlue(*mainsurface), " Download ", 0) )
8700 								{
8701 									SteamUGC()->DownloadItem(itemDetails.m_nPublishedFileId, true);
8702 								}
8703 							}
8704 							filename_padx += (12 * TTF12_WIDTH + 16);
8705 							// mount button
8706 							if ( itemDownloaded && !pathIsMounted )
8707 							{
8708 								if ( gamemodsDrawClickableButton(filename_padx, filename_pady, 12 * TTF12_WIDTH + 8, TTF12_HEIGHT, 0, " Load Item ", 0) )
8709 								{
8710 									if ( PHYSFS_mount(fullpath, NULL, 0) )
8711 									{
8712 										gamemods_mountedFilepaths.push_back(std::make_pair(fullpath, itemDetails.m_rgchTitle));
8713 										gamemods_workshopLoadedFileIDMap.push_back(std::make_pair(itemDetails.m_rgchTitle, itemDetails.m_nPublishedFileId));
8714 										gamemods_modelsListRequiresReload = true;
8715 										gamemods_soundListRequiresReload = true;
8716 									}
8717 								}
8718 							}
8719 							filename_padx -= (12 * TTF12_WIDTH + 16);
8720 						}
8721 						if ( gamemods_window == 4 )
8722 						{
8723 							filename_padx += (12 * TTF12_WIDTH + 16);
8724 							// edit content button
8725 							if ( gamemodsDrawClickableButton(filename_padx, filename_pady + filename_rowHeight / 4, 12 * TTF12_WIDTH + 8, TTF12_HEIGHT, uint32ColorBaronyBlue(*mainsurface), "  Update  ", 0) )
8726 							{
8727 								buttonGamemodsOpenModifyExistingWindow(nullptr);
8728 								gamemods_window = 5;
8729 								gamemods_uploadStatus = 1;
8730 								g_SteamWorkshop->m_myWorkshopItemToModify = itemDetails;
8731 
8732 								// grab the current item tags and store them for modification.
8733 								std::string workshopItemTagString = g_SteamWorkshop->m_myWorkshopItemToModify.m_rgchTags;
8734 								std::size_t found = workshopItemTagString.find(",");
8735 								g_SteamWorkshop->workshopItemTags.clear();
8736 								while ( found != std::string::npos )
8737 								{
8738 									std::string line = workshopItemTagString.substr(0, found);
8739 									workshopItemTagString = workshopItemTagString.substr(found + 1); // skip the "," character.
8740 									g_SteamWorkshop->workshopItemTags.push_back(line); // store the comma separated value.
8741 									found = workshopItemTagString.find(",");
8742 								}
8743 								// add the final string.
8744 								g_SteamWorkshop->workshopItemTags.push_back(workshopItemTagString);
8745 							}
8746 						}
8747 						filename_pady += filename_rowHeight / 2;
8748 						if ( gamemods_window == 3 )
8749 						{
8750 							// unsubscribe button
8751 							if ( gamemodsDrawClickableButton(filename_padx, filename_pady, 12 * TTF12_WIDTH + 8, TTF12_HEIGHT, uint32ColorRed(*mainsurface), "Unsubscribe", 0) )
8752 							{
8753 								if ( pathIsMounted )
8754 								{
8755 									if ( PHYSFS_unmount(fullpath) )
8756 									{
8757 										if ( gamemodsRemovePathFromMountedFiles(fullpath) )
8758 										{
8759 											printlog("[%s] is removed from the search path.\n", fullpath);
8760 											gamemods_modelsListRequiresReload = true;
8761 											gamemods_soundListRequiresReload = true;
8762 										}
8763 									}
8764 								}
8765 								g_SteamWorkshop->UnsubscribeItemFileID(itemDetails.m_nPublishedFileId);
8766 								gamemods_window_scroll = 0;
8767 							}
8768 							filename_padx += (12 * TTF12_WIDTH + 16);
8769 							// unmount button
8770 							if ( itemDownloaded && pathIsMounted )
8771 							{
8772 								if ( gamemodsDrawClickableButton(filename_padx, filename_pady, 12 * TTF12_WIDTH + 8, TTF12_HEIGHT, 0, "Unload Item", 0) )
8773 								{
8774 									if ( PHYSFS_unmount(fullpath) )
8775 									{
8776 										if ( gamemodsRemovePathFromMountedFiles(fullpath) )
8777 										{
8778 											printlog("[%s] is removed from the search path.\n", fullpath);
8779 											gamemods_modelsListRequiresReload = true;
8780 											gamemods_soundListRequiresReload = true;
8781 										}
8782 									}
8783 								}
8784 							}
8785 						}
8786 					}
8787 					filename_pady += filename_rowHeight;
8788 				}
8789 
8790 				// draw the tooltip we initialised earlier.
8791 				if ( drawModLoadOrder )
8792 				{
8793 					drawTooltip(&tooltip);
8794 					int numLoadedModLine = 1;
8795 					ttfPrintTextFormattedColor(ttf12, tooltip.x + 4, tooltip.y + 4, uint32ColorBaronyBlue(*mainsurface),
8796 						"Current load list: (first is lowest priority)");
8797 					for ( std::vector<std::pair<std::string, std::string>>::iterator it = gamemods_mountedFilepaths.begin(); it != gamemods_mountedFilepaths.end(); ++it )
8798 					{
8799 						std::pair<std::string, std::string> line = *it;
8800 						modInfoStr = line.second;
8801 						if ( modInfoStr.length() > 64 )
8802 						{
8803 							modInfoStr = modInfoStr.substr(0, 64 - 2).append("..");
8804 						}
8805 						ttfPrintTextFormatted(ttf12, tooltip.x + 4, tooltip.y + 4 + numLoadedModLine * TTF12_HEIGHT, "%2d) %s", numLoadedModLine, modInfoStr.c_str());
8806 						++numLoadedModLine;
8807 					}
8808 				}
8809 				else if ( drawExtendedInformationForMod >= 0 )
8810 				{
8811 					int tooltip_pady = 8;
8812 					SteamUGCDetails_t itemDetails = g_SteamWorkshop->m_subscribedItemListDetails[drawExtendedInformationForMod];
8813 					// draw the information.
8814 					std::string line;
8815 
8816 					line = itemDetails.m_rgchDescription;
8817 					line.erase(std::remove(line.begin(), line.end(), '\r'), line.end());
8818 					//line.erase(std::remove(line.begin(), line.end(), '\n'), line.end());
8819 					std::string subString;
8820 					std::string outputStr;
8821 					std::size_t found = line.find('\n');
8822 					int numlines = 0;
8823 					while ( line.length() >= 62 || (found != std::string::npos && found < 62) )
8824 					{
8825 						if ( numlines >= maxDescriptionLines )
8826 						{
8827 							break;
8828 						}
8829 						if ( found != std::string::npos && found < 62 )
8830 						{
8831 							// found newline.
8832 							subString = line.substr(0, found);
8833 							line = line.substr(found + 1);
8834 						}
8835 						else
8836 						{
8837 							subString = line.substr(0, 62);
8838 							if ( subString.at(subString.length() - 1) != ' ' || line.at(62) != ' ' )
8839 							{
8840 								// handle word wrapping.
8841 								std::size_t lastSpace = subString.find_last_of(' ');
8842 								if ( lastSpace != std::string::npos )
8843 								{
8844 									subString = subString.substr(0, lastSpace);
8845 									line = line.substr(lastSpace, line.length());
8846 								}
8847 								else
8848 								{
8849 									line = line.substr(62);
8850 								}
8851 							}
8852 							else
8853 							{
8854 								line = line.substr(62);
8855 							}
8856 						}
8857 						outputStr.append(subString);
8858 						outputStr += '\n';
8859 						outputStr.append("  ");
8860 						found = line.find('\n');
8861 						++numlines;
8862 					}
8863 
8864 					subString = line;
8865 					outputStr.append(subString);
8866 
8867 					tooltip.h = (6 + std::min(maxDescriptionLines, numlines)) * TTF12_HEIGHT + 12;
8868 					drawTooltip(&tooltip);
8869 
8870 					// draw description title.
8871 					line = itemDetails.m_rgchTitle;
8872 					found = line.find_first_of('\n');
8873 					if ( found != std::string::npos && found < 62 )
8874 					{
8875 						line = line.substr(0, found);
8876 						line.append("..");
8877 					}
8878 					else if ( line.length() >= 64 )
8879 					{
8880 						line = line.substr(0, 62);
8881 						line.append("..");
8882 					}
8883 					ttfPrintTextFormattedColor(ttf12, tooltip.x + 8, tooltip.y + tooltip_pady, uint32ColorBaronyBlue(*mainsurface), "%s", line.c_str());
8884 					tooltip_pady += TTF12_HEIGHT * 2;
8885 
8886 					// draw description body.
8887 					ttfPrintTextFormatted(ttf12, tooltip.x + 8, tooltip.y + tooltip_pady, "  %s", outputStr.c_str());
8888 
8889 					tooltip_pady += TTF12_HEIGHT * (numlines + 2);
8890 
8891 					// draw tags.
8892 					ttfPrintTextFormattedColor(ttf12, tooltip.x + 8, tooltip.y + tooltip_pady, uint32ColorBaronyBlue(*mainsurface), "tags:");
8893 					tooltip_pady += TTF12_HEIGHT;
8894 					line = itemDetails.m_rgchTags;
8895 
8896 					int tooltip_padx = 0;
8897 					if ( !line.empty() )
8898 					{
8899 						subString = line;
8900 						ttfPrintTextFormatted(ttf12, tooltip.x + 8, tooltip.y + tooltip_pady, "  %s", subString.c_str());
8901 					}
8902 				}
8903 			}
8904 		}
8905 #endif //STEAMWORKS
8906 
8907 		if ( gamemods_window == 6 )
8908 		{
8909 			// create blank file structure for a mod.
8910 			filename_padx = subx1 + 16;
8911 			filename_pady = suby1 + 32;
8912 			ttfPrintTextFormatted(ttf12, filename_padx, filename_pady, "Enter base folder name for template directory");
8913 			filename_pady += TTF12_HEIGHT + 8;
8914 			drawDepressed(filename_padx - 4, filename_pady - 4, filename_padx + 32 * TTF12_WIDTH + 8, filename_pady + TTF12_HEIGHT);
8915 			ttfPrintTextFormatted(ttf12, filename_padx, filename_pady, "%s", gamemods_newBlankDirectory);
8916 			if ( !SDL_IsTextInputActive() )
8917 			{
8918 				inputstr = gamemods_newBlankDirectory;
8919 				SDL_StartTextInput();
8920 			}
8921 			inputlen = 30;
8922 			if ( SDL_IsTextInputActive()
8923 				&& (ticks - cursorflash) % TICKS_PER_SECOND < TICKS_PER_SECOND / 2 )
8924 			{
8925 				int x;
8926 				TTF_SizeUTF8(ttf12, inputstr, &x, NULL);
8927 				ttfPrintText(ttf12, filename_padx + x, filename_pady, "_");
8928 			}
8929 			filename_pady += TTF12_HEIGHT + 8;
8930 			if ( strcmp(gamemods_newBlankDirectoryOldName, gamemods_newBlankDirectory) == 0 )
8931 			{
8932 				if ( gamemods_newBlankDirectoryStatus == -1 )
8933 				{
8934 					ttfPrintTextFormattedColor(ttf12, filename_padx, filename_pady, uint32ColorRed(*mainsurface), "Error: could not create directory %s/, already exists in mods/ folder", gamemods_newBlankDirectory);
8935 				}
8936 				else if ( gamemods_newBlankDirectoryStatus == 1 )
8937 				{
8938 					ttfPrintTextFormattedColor(ttf12, filename_padx, filename_pady, uint32ColorGreen(*mainsurface), "Successfully created directory %s/ in mods/ folder", gamemods_newBlankDirectory);
8939 				}
8940 			}
8941 		}
8942 		if ( gamemods_window == 7 )
8943 		{
8944 			numFileEntries = 8;
8945 			filenameMaxLength = 48;
8946 			filename_padx = subx1 + 16;
8947 			filename_pady = suby1 + 32;
8948 			filename_padx2 = subx2 - 16 - 40;
8949 			filename_pady2 = filename_pady + numFileEntries * TTF12_HEIGHT + 8;
8950 			int filename_rowHeight = 2 * TTF12_HEIGHT + 8;
8951 			filename_pady += 3 * TTF12_HEIGHT;
8952 			int numLocalFolders = std::max(static_cast<int>(gamemods_localModFoldernames.size() - 2), 0);
8953 			if ( numLocalFolders > 0 )
8954 			{
8955 				ttfPrintTextFormattedColor(ttf12, filename_padx, filename_pady, uint32ColorGreen(*mainsurface), "successfully retrieved local items!");
8956 			}
8957 			else
8958 			{
8959 				ttfPrintTextFormattedColor(ttf12, filename_padx, filename_pady, uint32ColorOrange(*mainsurface), "no folders found!");
8960 				ttfPrintTextFormattedColor(ttf12, filename_padx, filename_pady + TTF12_HEIGHT + 8, uint32ColorOrange(*mainsurface), "to get started create a new folder, or copy shared custom content to the mods/ folder");
8961 			}
8962 
8963 			std::string modInfoStr = "current loaded mods (hover for info): ";
8964 			SDL_Rect tooltip; // we will draw the tooltip after drawing the other elements of the display window.
8965 			bool drawModLoadOrder = false;
8966 			int drawExtendedInformationForMod = -1; // value of 0 or greater will draw.
8967 			int maxDescriptionLines = 10;
8968 
8969 			tooltip.x = omousex - 256;
8970 			tooltip.y = omousey + 16;
8971 			tooltip.w = 32 + TTF12_WIDTH * 64;
8972 			tooltip.h = (gamemods_mountedFilepaths.size() + 1) * TTF12_HEIGHT + 8;
8973 
8974 			if ( gamemods_mountedFilepaths.size() > 0 )
8975 			{
8976 				ttfPrintTextFormatted(ttf12, filename_padx2 - modInfoStr.length() * TTF12_WIDTH - 16, filename_pady, "%s %2d", modInfoStr.c_str(), gamemods_mountedFilepaths.size());
8977 				if ( mouseInBounds(filename_padx2 - modInfoStr.length() * TTF12_WIDTH - 16, filename_padx2, filename_pady, filename_pady + TTF12_HEIGHT) )
8978 				{
8979 					drawModLoadOrder = true;
8980 				}
8981 				else
8982 				{
8983 					drawModLoadOrder = false;
8984 				}
8985 			}
8986 
8987 			filename_pady += 2 * TTF12_HEIGHT;
8988 
8989 			// do slider
8990 			SDL_Rect slider;
8991 			slider.x = filename_padx2 + 8;
8992 			slider.y = filename_pady - 8;
8993 			slider.h = suby2 - (filename_pady + 20);
8994 			slider.w = 32;
8995 
8996 			int entriesToScroll = std::max(static_cast<int>((numLocalFolders / numFileEntries) - 1), 0);
8997 			entriesToScroll = entriesToScroll * numFileEntries + (numLocalFolders % numFileEntries);
8998 
8999 			// handle slider movement.
9000 			if ( numLocalFolders > numFileEntries )
9001 			{
9002 				drawRect(&slider, SDL_MapRGB(mainsurface->format, 64, 64, 64), 255);
9003 				if ( mouseInBounds(filename_padx, slider.x + slider.w,
9004 					slider.y, slider.y + slider.h) )
9005 				{
9006 					if ( mousestatus[SDL_BUTTON_WHEELUP] )
9007 					{
9008 						gamemods_window_scroll = std::max(gamemods_window_scroll - 1, 0);
9009 						mousestatus[SDL_BUTTON_WHEELUP] = 0;
9010 					}
9011 					if ( mousestatus[SDL_BUTTON_WHEELDOWN] )
9012 					{
9013 						gamemods_window_scroll = std::min(gamemods_window_scroll + 1, entriesToScroll);
9014 						mousestatus[SDL_BUTTON_WHEELDOWN] = 0;
9015 					}
9016 				}
9017 
9018 				if ( keystatus[SDL_SCANCODE_UP] )
9019 				{
9020 					gamemods_window_scroll = std::max(gamemods_window_scroll - 1, 0);
9021 					keystatus[SDL_SCANCODE_UP] = 0;
9022 				}
9023 				if ( keystatus[SDL_SCANCODE_DOWN] )
9024 				{
9025 					gamemods_window_scroll = std::min(gamemods_window_scroll + 1, entriesToScroll);
9026 					keystatus[SDL_SCANCODE_DOWN] = 0;
9027 				}
9028 				slider.h *= (1 / static_cast<real_t>(entriesToScroll + 1));
9029 				slider.y += slider.h * gamemods_window_scroll;
9030 				if ( gamemods_window_scroll == entriesToScroll ) // reached end.
9031 				{
9032 					slider.y += (suby2 - 28) - (slider.y + slider.h); // bottom of slider is (suby2 - 28), so move the y level to imitate hitting the bottom in case of rounding error.
9033 				}
9034 				drawWindowFancy(slider.x, slider.y, slider.x + slider.w, slider.y + slider.h); // draw shortened list relative slider.
9035 			}
9036 
9037 			// draw the content
9038 			for ( int i = gamemods_window_scroll; i < numLocalFolders && i < numFileEntries + gamemods_window_scroll; ++i )
9039 			{
9040 				filename_padx = subx1 + 16;
9041 
9042 				std::list<std::string>::iterator it = gamemods_localModFoldernames.begin();
9043 				std::advance(it, 2); // skip the "." and ".." directories.
9044 				std::advance(it, i);
9045 				std::string folderName = *it;
9046 
9047 				drawWindowFancy(filename_padx, filename_pady - 8, filename_padx2, filename_pady + filename_rowHeight);
9048 				SDL_Rect highlightEntry;
9049 				highlightEntry.x = filename_padx;
9050 				highlightEntry.y = filename_pady - 8;
9051 				highlightEntry.w = filename_padx2 - filename_padx;
9052 				highlightEntry.h = filename_rowHeight + 8;
9053 				drawRect(&highlightEntry, SDL_MapRGB(mainsurface->format, 128, 128, 128), 64);
9054 
9055 				std::string path = outputdir;
9056 				path.append(PHYSFS_getDirSeparator()).append("mods").append(PHYSFS_getDirSeparator()).append(folderName);
9057 				bool pathIsMounted = gamemodsIsPathInMountedFiles(path);
9058 
9059 				if ( pathIsMounted )
9060 				{
9061 					SDL_Rect pos;
9062 					pos.x = filename_padx + 2;
9063 					pos.y = filename_pady - 6;
9064 					pos.w = filename_padx2 - filename_padx - 4;
9065 					pos.h = filename_rowHeight + 4;
9066 					drawRect(&pos, uint32ColorGreen(*mainsurface), 64);
9067 				}
9068 
9069 				if ( folderName.length() >= filenameMaxLength )
9070 				{
9071 					folderName = folderName.substr(0, 46);
9072 					folderName.append("..");
9073 				}
9074 				ttfPrintTextFormatted(ttf12, filename_padx + 8, filename_pady + TTF12_HEIGHT / 2, "Folder Name: %s", folderName.c_str());
9075 
9076 				filename_padx = filename_padx2 - (12 * TTF12_WIDTH + 16);
9077 				// mount button
9078 				if ( !pathIsMounted )
9079 				{
9080 					if ( gamemodsDrawClickableButton(filename_padx, filename_pady, 12 * TTF12_WIDTH + 8, TTF12_HEIGHT, 0, " Load Item ", 0) )
9081 					{
9082 						if ( PHYSFS_mount(path.c_str(), NULL, 0) )
9083 						{
9084 							gamemods_mountedFilepaths.push_back(std::make_pair(path, folderName));
9085 							printlog("[PhysFS]: [%s] is in the search path.\n", path.c_str());
9086 							gamemods_modelsListRequiresReload = true;
9087 							gamemods_soundListRequiresReload = true;
9088 						}
9089 					}
9090 				}
9091 				filename_pady += filename_rowHeight / 2;
9092 				// unmount button
9093 				if ( pathIsMounted )
9094 				{
9095 					if ( gamemodsDrawClickableButton(filename_padx, filename_pady, 12 * TTF12_WIDTH + 8, TTF12_HEIGHT, 0, "Unload Item", 0) )
9096 					{
9097 						if ( PHYSFS_unmount(path.c_str()) )
9098 						{
9099 							if ( gamemodsRemovePathFromMountedFiles(path) )
9100 							{
9101 								printlog("[PhysFS]: [%s] is removed from the search path.\n", path.c_str());
9102 								gamemods_modelsListRequiresReload = true;
9103 								gamemods_soundListRequiresReload = true;
9104 							}
9105 						}
9106 					}
9107 				}
9108 				filename_pady += filename_rowHeight;
9109 			}
9110 
9111 			// draw the tooltip we initialised earlier.
9112 			if ( drawModLoadOrder )
9113 			{
9114 				drawTooltip(&tooltip);
9115 				int numLoadedModLine = 1;
9116 				ttfPrintTextFormattedColor(ttf12, tooltip.x + 4, tooltip.y + 4, uint32ColorBaronyBlue(*mainsurface),
9117 					"Current load list: (first is lowest priority)");
9118 				for ( std::vector<std::pair<std::string, std::string>>::iterator it = gamemods_mountedFilepaths.begin(); it != gamemods_mountedFilepaths.end(); ++it )
9119 				{
9120 					std::pair<std::string, std::string> line = *it;
9121 					modInfoStr = line.second;
9122 					if ( modInfoStr.length() > 64 )
9123 					{
9124 						modInfoStr = modInfoStr.substr(0, 64 - 2).append("..");
9125 					}
9126 					ttfPrintTextFormatted(ttf12, tooltip.x + 4, tooltip.y + 4 + numLoadedModLine * TTF12_HEIGHT, "%2d) %s", numLoadedModLine, modInfoStr.c_str());
9127 					++numLoadedModLine;
9128 				}
9129 			}
9130 		}
9131 	}
9132 	else if ( gameModeManager.Tutorial.Menu.isOpen() )
9133 	{
9134 		int numFileEntries = 11;
9135 
9136 		int filenameMaxLength = 48;
9137 		int filename_padx = subx1 + 16;
9138 		int filename_pady = suby1 + 32;
9139 		int filename_padx2 = subx2 - 16;
9140 		int filename_rowHeight = 1.25 * TTF12_HEIGHT + 8;
9141 
9142 		auto& menu = gameModeManager.Tutorial.Menu;
9143 		ttfPrintTextFormattedColor(ttf12, filename_padx + 8, filename_pady + 8, uint32ColorWhite(*mainsurface), "%s", menu.windowTitle.c_str());
9144 
9145 		filename_pady += 4 * TTF12_HEIGHT;
9146 
9147 		// do slider
9148 		SDL_Rect slider;
9149 		slider.x = filename_padx2 + 8;
9150 		slider.y = filename_pady - 8;
9151 		slider.h = suby2 - (filename_pady + 20);
9152 		slider.w = 32;
9153 
9154 		int entriesToScroll = static_cast<Uint32>(std::max(((static_cast<int>(gameModeManager.Tutorial.levels.size()) / numFileEntries) - 1), 0));
9155 		entriesToScroll = entriesToScroll * numFileEntries + (gameModeManager.Tutorial.levels.size() % numFileEntries);
9156 
9157 		menu.selectedMenuItem = -1;
9158 
9159 		// handle slider movement.
9160 		if ( gameModeManager.Tutorial.levels.size() > numFileEntries )
9161 		{
9162 			drawRect(&slider, SDL_MapRGB(mainsurface->format, 64, 64, 64), 255);
9163 			if ( mouseInBounds(filename_padx, slider.x + slider.w,
9164 				slider.y, slider.y + slider.h) )
9165 			{
9166 				if ( mousestatus[SDL_BUTTON_WHEELUP] )
9167 				{
9168 					menu.windowScroll = std::max(menu.windowScroll - 1, 0);
9169 					mousestatus[SDL_BUTTON_WHEELUP] = 0;
9170 				}
9171 				if ( mousestatus[SDL_BUTTON_WHEELDOWN] )
9172 				{
9173 					menu.windowScroll = std::min(menu.windowScroll + 1, entriesToScroll);
9174 					mousestatus[SDL_BUTTON_WHEELDOWN] = 0;
9175 				}
9176 			}
9177 
9178 			if ( keystatus[SDL_SCANCODE_UP] )
9179 			{
9180 				menu.windowScroll = std::max(menu.windowScroll - 1, 0);
9181 				keystatus[SDL_SCANCODE_UP] = 0;
9182 			}
9183 			if ( keystatus[SDL_SCANCODE_DOWN] )
9184 			{
9185 				menu.windowScroll = std::min(menu.windowScroll + 1, entriesToScroll);
9186 				keystatus[SDL_SCANCODE_DOWN] = 0;
9187 			}
9188 			slider.h *= (1 / static_cast<real_t>(entriesToScroll + 1));
9189 			slider.y += slider.h * menu.windowScroll;
9190 			if ( menu.windowScroll == entriesToScroll ) // reached end.
9191 			{
9192 				slider.y += (suby2 - 28) - (slider.y + slider.h); // bottom of slider is (suby2 - 28), so move the y level to imitate hitting the bottom in case of rounding error.
9193 			}
9194 			drawWindowFancy(slider.x, slider.y, slider.x + slider.w, slider.y + slider.h); // draw shortened list relative slider.
9195 		}
9196 
9197 		// draw the content
9198 		for ( int i = menu.windowScroll; i < gameModeManager.Tutorial.levels.size() && i < numFileEntries + menu.windowScroll; ++i )
9199 		{
9200 			filename_padx = subx1 + 16;
9201 
9202 			auto it = gameModeManager.Tutorial.levels.begin();
9203 			std::advance(it, i);
9204 			std::string folderName = (*it).title;
9205 
9206 			drawWindowFancy(filename_padx, filename_pady - 8, filename_padx2, filename_pady + filename_rowHeight);
9207 
9208 			SDL_Rect highlightEntry;
9209 			highlightEntry.x = filename_padx;
9210 			highlightEntry.y = filename_pady - 8;
9211 			highlightEntry.w = filename_padx2 - filename_padx;
9212 			highlightEntry.h = filename_rowHeight + 8;
9213 			drawRect(&highlightEntry, SDL_MapRGB(mainsurface->format, 128, 128, 128), 64);
9214 
9215 			if ( mouseInBounds(highlightEntry.x, highlightEntry.x + highlightEntry.w, highlightEntry.y - 2, highlightEntry.y + highlightEntry.h + 4) )
9216 			{
9217 				menu.selectedMenuItem = i;
9218 			}
9219 
9220 			if ( menu.selectedMenuItem == i )
9221 			{
9222 				SDL_Rect pos;
9223 				pos.x = filename_padx + 2;
9224 				pos.y = filename_pady - 6;
9225 				pos.w = filename_padx2 - filename_padx - 4;
9226 				pos.h = filename_rowHeight + 4;
9227 				drawRect(&pos, uint32ColorBaronyBlue(*mainsurface), 64);
9228 				ttfPrintTextFormattedColor(ttf12, filename_padx + 8, suby2 - 3 * TTF12_HEIGHT, uint32ColorYellow(*mainsurface), "%s", (*it).description.c_str());
9229 			}
9230 
9231 			SDL_Rect btn;
9232 			btn.w = 6 * TTF12_WIDTH + 8;
9233 			btn.h = highlightEntry.h - 12;
9234 			btn.x = highlightEntry.x + highlightEntry.w - btn.w - 4;
9235 			btn.y = highlightEntry.y + 8;
9236 			if ( i == 0 )
9237 			{
9238 				btn.w = 11 * TTF12_WIDTH + 8;
9239 				btn.x = highlightEntry.x + highlightEntry.w - btn.w - 4;
9240 				if ( drawClickableButton(btn.x, btn.y, btn.w, btn.h, 0) )
9241 				{
9242 					menu.onClickEntry();
9243 				}
9244 				ttfPrintTextFormatted(ttf12, btn.x + 11, filename_pady + 4, "%s", "Enter Hub");
9245 			}
9246 			else
9247 			{
9248 				if ( drawClickableButton(btn.x, btn.y, btn.w, btn.h, 0) )
9249 				{
9250 					menu.onClickEntry();
9251 				}
9252 				ttfPrintTextFormatted(ttf12, btn.x + 7, filename_pady + 4, "%s", "Begin");
9253 			}
9254 
9255 			ttfPrintTextFormatted(ttf12, filename_padx + 8, filename_pady + 4, "%s", (*it).title.c_str());
9256 
9257 			if ( i != 0 )
9258 			{
9259 				const Uint32 sec = ((*it).completionTime / TICKS_PER_SECOND) % 60;
9260 				const Uint32 min = (((*it).completionTime / TICKS_PER_SECOND) / 60) % 60;
9261 				const Uint32 hour = (((*it).completionTime / TICKS_PER_SECOND) / 60) / 60;
9262 				const Uint32 timeTextWidth = 28 * TTF12_WIDTH + 8;
9263 				if ( (*it).completionTime > 0 )
9264 				{
9265 					ttfPrintTextFormatted(ttf12, btn.x - timeTextWidth, filename_pady + 4, "Best time: %02d:%02d:%02d", hour, min, sec);
9266 				}
9267 				else
9268 				{
9269 					ttfPrintTextFormatted(ttf12, btn.x - timeTextWidth, filename_pady + 4, "Not completed");
9270 				}
9271 			}
9272 
9273 			filename_padx = filename_padx2 - (12 * TTF12_WIDTH + 16);
9274 			filename_pady += 1.5 * filename_rowHeight;
9275 		}
9276 
9277 		if ( menu.selectedMenuItem == -1 )
9278 		{
9279 			ttfPrintTextFormattedColor(ttf12, subx1 + 16 + 8, suby2 - 3 * TTF12_HEIGHT, uint32ColorYellow(*mainsurface), "%s", menu.defaultHoverText.c_str());
9280 		}
9281 	}
9282 	else if ( gameModeManager.Tutorial.FirstTimePrompt.isOpen() )
9283 	{
9284 		gameModeManager.Tutorial.FirstTimePrompt.drawDialogue();
9285 		if ( gameModeManager.Tutorial.FirstTimePrompt.doButtonSkipPrompt )
9286 		{
9287 			gameModeManager.Tutorial.FirstTimePrompt.doButtonSkipPrompt = false;
9288 			if ( anySaveFileExists() )
9289 			{
9290 				openNewLoadGameWindow(nullptr);
9291 			}
9292 			else
9293 			{
9294 				buttonOpenCharacterCreationWindow(NULL);
9295 			}
9296 		}
9297 	}
9298 
9299 	// handle fade actions
9300 	if ( fadefinished )
9301 	{
9302 		if ( introstage == 2 )   // quit game
9303 		{
9304 			introstage = 0;
9305 			mainloop = 0;
9306 		}
9307 		else if ( introstage == 3 )     // new game
9308 		{
9309 			bool bWasOnMainMenu = intro;
9310 			introstage = 1;
9311 			fadefinished = false;
9312 			fadeout = false;
9313 			gamePaused = false;
9314 			multiplayerselect = SINGLE;
9315 			intro = true; //Fix items auto-adding to the hotbar on game restart.
9316 			swapWeaponGimpTimer = 0;
9317 			pickaxeGimpTimer = 0;
9318 
9319 			if ( gameModeManager.getMode() == GameModeManager_t::GAME_MODE_DEFAULT )
9320 			{
9321 				if ( !mode )
9322 				{
9323 					// restarting game, make a highscore
9324 					saveScore();
9325 					deleteSaveGame(multiplayer);
9326 					loadingsavegame = 0;
9327 				}
9328 			}
9329 			camera_charsheet_offsetyaw = (330) * PI / 180; // reset player camera view.
9330 
9331 			// undo shopkeeper grudge
9332 			swornenemies[SHOPKEEPER][HUMAN] = false;
9333 			monsterally[SHOPKEEPER][HUMAN] = true;
9334 			swornenemies[SHOPKEEPER][AUTOMATON] = false;
9335 			monsterally[SHOPKEEPER][AUTOMATON] = true;
9336 
9337 			// setup game //TODO: Move into a function startGameStuff() or something.
9338 			entity_uids = 1;
9339 			loading = true;
9340 			darkmap = false;
9341 			deinitShapeshiftHotbar();
9342 			for ( c = 0; c < NUM_HOTBAR_ALTERNATES; ++c )
9343 			{
9344 				selected_spell_alternate[c] = NULL;
9345 				hotbarShapeshiftInit[c] = false;
9346 			}
9347 			selected_spell = NULL;
9348 			selected_spell_last_appearance = -1;
9349 			shootmode = true;
9350 			currentlevel = startfloor;
9351 			secretlevel = false;
9352 			victory = 0;
9353 			completionTime = 0;
9354 
9355 			setDefaultPlayerConducts(); // penniless, foodless etc.
9356 			if ( startfloor != 0 )
9357 			{
9358 				conductGameChallenges[CONDUCT_CHEATS_ENABLED] = 1;
9359 			}
9360 			enemyHPDamageBarHandler.HPBars.clear();
9361 
9362 			minimapPings.clear(); // clear minimap pings
9363 			globalLightModifierActive = GLOBAL_LIGHT_MODIFIER_STOPPED;
9364 			gameplayCustomManager.readFromFile();
9365 			textSourceScript.scriptVariables.clear();
9366 
9367 			if ( multiplayer == CLIENT )
9368 			{
9369 				gameModeManager.currentSession.saveServerFlags();
9370 				svFlags = lobbyWindowSvFlags;
9371 			}
9372 			else if ( !loadingsavegame && bWasOnMainMenu )
9373 			{
9374 				gameModeManager.currentSession.saveServerFlags();
9375 			}
9376 
9377 			if ( gameModeManager.getMode() == GameModeManager_t::GAME_MODE_TUTORIAL )
9378 			{
9379 				svFlags &= ~(SV_FLAG_HARDCORE);
9380 				svFlags &= ~(SV_FLAG_CHEATS);
9381 				svFlags &= ~(SV_FLAG_LIFESAVING);
9382 				svFlags &= ~(SV_FLAG_CLASSIC);
9383 				svFlags &= ~(SV_FLAG_KEEPINVENTORY);
9384 				svFlags |= SV_FLAG_HUNGER;
9385 				svFlags |= SV_FLAG_FRIENDLYFIRE;
9386 				svFlags |= SV_FLAG_MINOTAURS;
9387 				svFlags |= SV_FLAG_TRAPS;
9388 
9389 				if ( gameModeManager.Tutorial.dungeonLevel >= 0 )
9390 				{
9391 					currentlevel = gameModeManager.Tutorial.dungeonLevel;
9392 					gameModeManager.Tutorial.dungeonLevel = -1;
9393 				}
9394 			}
9395 
9396 			// clear follower menu entities.
9397 			FollowerMenu.closeFollowerMenuGUI(true);
9398 
9399 			list_FreeAll(&damageIndicators);
9400 			for ( c = 0; c < NUMMONSTERS; c++ )
9401 			{
9402 				kills[c] = 0;
9403 			}
9404 
9405 			// close chests
9406 			for ( c = 0; c < MAXPLAYERS; ++c )
9407 			{
9408 				if ( c > 0 && !client_disconnected[c] )
9409 				{
9410 					if ( openedChest[c] )
9411 					{
9412 						openedChest[c]->closeChestServer();
9413 					}
9414 				}
9415 				else if ( c == 0 )
9416 				{
9417 					if ( openedChest[c] )
9418 					{
9419 						openedChest[c]->closeChest();
9420 					}
9421 				}
9422 			}
9423 
9424 			// disable cheats
9425 			noclip = false;
9426 			godmode = false;
9427 			buddhamode = false;
9428 			everybodyfriendly = false;
9429 			gameloopFreezeEntities = false;
9430 
9431 #ifdef STEAMWORKS
9432 			if ( !directConnect )
9433 			{
9434 				if ( currentLobby )
9435 				{
9436 					// once the game is started, the lobby is no longer needed.
9437 					// when all steam users have left the lobby,
9438 					// the lobby is destroyed automatically on the backend.
9439 
9440 					SteamMatchmaking()->LeaveLobby(*static_cast<CSteamID*>(currentLobby));
9441 					cpp_Free_CSteamID(currentLobby); //TODO: Bugger this.
9442 					currentLobby = NULL;
9443 				}
9444 			}
9445 #elif defined USE_EOS
9446 			if ( !directConnect )
9447 			{
9448 				/*if ( EOS.CurrentLobbyData.currentLobbyIsValid() )
9449 				{
9450 					EOS.leaveLobby();
9451 				}*/
9452 			}
9453 #endif
9454 
9455 			// load dungeon
9456 			if ( multiplayer != CLIENT )
9457 			{
9458 				// stop all sounds
9459 #ifdef USE_FMOD
9460 				if ( sound_group )
9461 				{
9462 					FMOD_ChannelGroup_Stop(sound_group);
9463 				}
9464 				if ( soundAmbient_group )
9465 				{
9466 					FMOD_ChannelGroup_Stop(soundAmbient_group);
9467 				}
9468 				if ( soundEnvironment_group )
9469 				{
9470 					FMOD_ChannelGroup_Stop(soundEnvironment_group);
9471 				}
9472 #elif defined USE_OPENAL
9473 				if ( sound_group )
9474 				{
9475 					OPENAL_ChannelGroup_Stop(sound_group);
9476 				}
9477 				if ( soundAmbient_group )
9478 				{
9479 					OPENAL_ChannelGroup_Stop(soundAmbient_group);
9480 				}
9481 				if ( soundEnvironment_group )
9482 				{
9483 					OPENAL_ChannelGroup_Stop(soundEnvironment_group);
9484 				}
9485 #endif
9486 
9487 				// generate a unique game key (used to identify compatible save games)
9488 				prng_seed_time();
9489 				if ( multiplayer == SINGLE )
9490 				{
9491 					uniqueGameKey = prng_get_uint();
9492 					if ( !uniqueGameKey )
9493 					{
9494 						uniqueGameKey++;
9495 					}
9496 				}
9497 
9498 				// reset class loadout
9499 				if ( !loadingsavegame )
9500 				{
9501 					stats[0]->clearStats();
9502 					initClass(0);
9503 					mapseed = 0;
9504 				}
9505 				else
9506 				{
9507 					loadGame(0);
9508 				}
9509 
9510 				// hack to fix these things from breaking everything...
9511 				hudarm = NULL;
9512 				hudweapon = NULL;
9513 				magicLeftHand = NULL;
9514 				magicRightHand = NULL;
9515 
9516 				for ( node = map.entities->first; node != nullptr; node = node->next )
9517 				{
9518 					entity = (Entity*)node->element;
9519 					entity->flags[NOUPDATE] = true;
9520 				}
9521 				lastEntityUIDs = entity_uids;
9522 				numplayers = 0;
9523 				int checkMapHash = -1;
9524 				if ( loadingmap == false )
9525 				{
9526 					physfsLoadMapFile(currentlevel, mapseed, false, &checkMapHash);
9527 					if ( checkMapHash == 0 )
9528 					{
9529 						conductGameChallenges[CONDUCT_MODDED] = 1;
9530 					}
9531 				}
9532 				else
9533 				{
9534 					if ( genmap == false )
9535 					{
9536 						std::string fullMapName = physfsFormatMapName(maptoload);
9537 						loadMap(fullMapName.c_str(), &map, map.entities, map.creatures, &checkMapHash);
9538 						if ( checkMapHash == 0 )
9539 						{
9540 							conductGameChallenges[CONDUCT_MODDED] = 1;
9541 						}
9542 					}
9543 					else
9544 					{
9545 						generateDungeon(maptoload, mapseed);
9546 					}
9547 				}
9548 				assignActions(&map);
9549 				generatePathMaps();
9550 
9551 				achievementObserver.updateData();
9552 
9553 				if ( loadingsavegame )
9554 				{
9555 					for ( c = 0; c < MAXPLAYERS; c++ )
9556 					{
9557 						if ( players[c] && players[c]->entity && !client_disconnected[c] )
9558 						{
9559 							if ( stats[c] && stats[c]->EFFECTS[EFF_POLYMORPH] && stats[c]->playerPolymorphStorage != NOTHING )
9560 							{
9561 								players[c]->entity->effectPolymorph = stats[c]->playerPolymorphStorage;
9562 								serverUpdateEntitySkill(players[c]->entity, 50); // update visual polymorph effect for clients.
9563 								serverUpdateEffects(c);
9564 							}
9565 							if ( stats[c] && stats[c]->EFFECTS[EFF_SHAPESHIFT] && stats[c]->playerShapeshiftStorage != NOTHING )
9566 							{
9567 								players[c]->entity->effectShapeshift = stats[c]->playerShapeshiftStorage;
9568 								serverUpdateEntitySkill(players[c]->entity, 53); // update visual shapeshift effect for clients.
9569 								serverUpdateEffects(c);
9570 							}
9571 							if ( stats[c] && stats[c]->EFFECTS[EFF_VAMPIRICAURA] && stats[c]->EFFECTS_TIMERS[EFF_VAMPIRICAURA] == -2 )
9572 							{
9573 								players[c]->entity->playerVampireCurse = 1;
9574 								serverUpdateEntitySkill(players[c]->entity, 51); // update curse progression
9575 							}
9576 						}
9577 					}
9578 
9579 					list_t* followers = loadGameFollowers();
9580 					if ( followers )
9581 					{
9582 						int c;
9583 						for ( c = 0; c < MAXPLAYERS; c++ )
9584 						{
9585 							node_t* tempNode = list_Node(followers, c);
9586 							if ( tempNode )
9587 							{
9588 								list_t* tempFollowers = (list_t*)tempNode->element;
9589 								if (players[c] && players[c]->entity && !client_disconnected[c])
9590 								{
9591 									node_t* node;
9592 									node_t* gyrobotNode = nullptr;
9593 									Entity* gyrobotEntity = nullptr;
9594 									std::vector<node_t*> allyRobotNodes;
9595 									for ( node = tempFollowers->first; node != NULL; node = node->next )
9596 									{
9597 										Stat* tempStats = (Stat*)node->element;
9598 										if ( tempStats && tempStats->type == GYROBOT )
9599 										{
9600 											gyrobotNode = node;
9601 											break;
9602 										}
9603 									}
9604 									for ( node = tempFollowers->first; node != NULL; node = node->next )
9605 									{
9606 										Stat* tempStats = (Stat*)node->element;
9607 										if ( tempStats && (tempStats->type == DUMMYBOT
9608 											|| tempStats->type == SENTRYBOT
9609 											|| tempStats->type == SPELLBOT) )
9610 										{
9611 											// gyrobot will pick up these guys into it's inventory, otherwise leave them behind.
9612 											if ( gyrobotNode )
9613 											{
9614 												allyRobotNodes.push_back(node);
9615 											}
9616 											continue;
9617 										}
9618 										Entity* monster = summonMonster(tempStats->type, players[c]->entity->x, players[c]->entity->y);
9619 										if ( monster )
9620 										{
9621 											if ( node == gyrobotNode )
9622 											{
9623 												gyrobotEntity = monster;
9624 											}
9625 											monster->skill[3] = 1; // to mark this monster partially initialized
9626 											list_RemoveNode(monster->children.last);
9627 
9628 											node_t* newNode = list_AddNodeLast(&monster->children);
9629 											newNode->element = tempStats->copyStats();
9630 											//newNode->deconstructor = &tempStats->~Stat;
9631 											newNode->size = sizeof(tempStats);
9632 
9633 											Stat* monsterStats = (Stat*)newNode->element;
9634 											monsterStats->leader_uid = players[c]->entity->getUID();
9635 											monster->flags[USERFLAG2] = true;
9636 											/*if ( !monsterally[HUMAN][monsterStats->type] )
9637 											{
9638 											}*/
9639 											monster->monsterAllyIndex = c;
9640 											if ( multiplayer == SERVER )
9641 											{
9642 												serverUpdateEntitySkill(monster, 42); // update monsterAllyIndex for clients.
9643 											}
9644 
9645 											if ( multiplayer != CLIENT )
9646 											{
9647 												monster->monsterAllyClass = monsterStats->allyClass;
9648 												monster->monsterAllyPickupItems = monsterStats->allyItemPickup;
9649 												if ( stats[c]->playerSummonPERCHR != 0 && !strcmp(monsterStats->name, "skeleton knight") )
9650 												{
9651 													monster->monsterAllySummonRank = (stats[c]->playerSummonPERCHR & 0x0000FF00) >> 8;
9652 												}
9653 												else if ( stats[c]->playerSummon2PERCHR != 0 && !strcmp(monsterStats->name, "skeleton sentinel") )
9654 												{
9655 													monster->monsterAllySummonRank = (stats[c]->playerSummon2PERCHR & 0x0000FF00) >> 8;
9656 												}
9657 												serverUpdateEntitySkill(monster, 46); // update monsterAllyClass
9658 												serverUpdateEntitySkill(monster, 44); // update monsterAllyPickupItems
9659 												serverUpdateEntitySkill(monster, 50); // update monsterAllySummonRank
9660 											}
9661 
9662 											newNode = list_AddNodeLast(&stats[c]->FOLLOWERS);
9663 											newNode->deconstructor = &defaultDeconstructor;
9664 											Uint32* myuid = (Uint32*) malloc(sizeof(Uint32));
9665 											newNode->element = myuid;
9666 											*myuid = monster->getUID();
9667 
9668 											if ( c > 0 && multiplayer == SERVER )
9669 											{
9670 												strcpy((char*)net_packet->data, "LEAD");
9671 												SDLNet_Write32((Uint32)monster->getUID(), &net_packet->data[4]);
9672 												strcpy((char*)(&net_packet->data[8]), monsterStats->name);
9673 												net_packet->data[8 + strlen(monsterStats->name)] = 0;
9674 												net_packet->address.host = net_clients[c - 1].host;
9675 												net_packet->address.port = net_clients[c - 1].port;
9676 												net_packet->len = 8 + strlen(monsterStats->name) + 1;
9677 												sendPacketSafe(net_sock, -1, net_packet, c - 1);
9678 
9679 												serverUpdateAllyStat(c, monster->getUID(), monsterStats->LVL, monsterStats->HP, monsterStats->MAXHP, monsterStats->type);
9680 											}
9681 
9682 											if ( !FollowerMenu.recentEntity && c == clientnum )
9683 											{
9684 												FollowerMenu.recentEntity = monster;
9685 											}
9686 										}
9687 									}
9688 									if ( gyrobotEntity && !allyRobotNodes.empty() )
9689 									{
9690 										Stat* gyroStats = gyrobotEntity->getStats();
9691 										for ( auto it = allyRobotNodes.begin(); gyroStats && it != allyRobotNodes.end(); ++it )
9692 										{
9693 											node_t* botNode = *it;
9694 											if ( botNode )
9695 											{
9696 												Stat* tempStats = (Stat*)botNode->element;
9697 												if ( tempStats )
9698 												{
9699 													ItemType type = WOODEN_SHIELD;
9700 													if ( tempStats->type == SENTRYBOT )
9701 													{
9702 														type = TOOL_SENTRYBOT;
9703 													}
9704 													else if ( tempStats->type == SPELLBOT )
9705 													{
9706 														type = TOOL_SPELLBOT;
9707 													}
9708 													else if ( tempStats->type == DUMMYBOT )
9709 													{
9710 														type = TOOL_DUMMYBOT;
9711 													}
9712 													int appearance = monsterTinkeringConvertHPToAppearance(tempStats);
9713 													if ( type != WOODEN_SHIELD )
9714 													{
9715 														Item* item = newItem(type, static_cast<Status>(tempStats->monsterTinkeringStatus),
9716 															0, 1, appearance, true, &gyroStats->inventory);
9717 													}
9718 												}
9719 											}
9720 										}
9721 									}
9722 								}
9723 							}
9724 						}
9725 						list_FreeAll(followers);
9726 						free(followers);
9727 					}
9728 				}
9729 
9730 				if ( multiplayer == SINGLE )
9731 				{
9732 					saveGame();
9733 				}
9734 			}
9735 			else
9736 			{
9737 				// hack to fix these things from breaking everything...
9738 				hudarm = NULL;
9739 				hudweapon = NULL;
9740 				magicLeftHand = NULL;
9741 				magicRightHand = NULL;
9742 
9743 				client_disconnected[0] = false;
9744 
9745 				// initialize class
9746 				if ( !loadingsavegame )
9747 				{
9748 					stats[clientnum]->clearStats();
9749 					initClass(clientnum);
9750 					mapseed = 0;
9751 				}
9752 				else
9753 				{
9754 					loadGame(clientnum);
9755 				}
9756 
9757 				// stop all sounds
9758 #ifdef USE_FMOD
9759 				if ( sound_group )
9760 				{
9761 					FMOD_ChannelGroup_Stop(sound_group);
9762 				}
9763 				if ( soundAmbient_group )
9764 				{
9765 					FMOD_ChannelGroup_Stop(soundAmbient_group);
9766 				}
9767 				if ( soundEnvironment_group )
9768 				{
9769 					FMOD_ChannelGroup_Stop(soundEnvironment_group);
9770 				}
9771 #elif defined USE_OPENAL
9772 				if ( sound_group )
9773 				{
9774 					OPENAL_ChannelGroup_Stop(sound_group);
9775 				}
9776 				if ( soundAmbient_group )
9777 				{
9778 					OPENAL_ChannelGroup_Stop(soundAmbient_group);
9779 				}
9780 				if ( soundEnvironment_group )
9781 				{
9782 					OPENAL_ChannelGroup_Stop(soundEnvironment_group);
9783 				}
9784 #endif
9785 				// load next level
9786 				entity_uids = 1;
9787 				lastEntityUIDs = entity_uids;
9788 				numplayers = 0;
9789 
9790 				int checkMapHash = -1;
9791 				if ( loadingmap == false )
9792 				{
9793 					physfsLoadMapFile(currentlevel, mapseed, false, &checkMapHash);
9794 					if ( checkMapHash == 0 )
9795 					{
9796 						conductGameChallenges[CONDUCT_MODDED] = 1;
9797 					}
9798 				}
9799 				else
9800 				{
9801 					if ( genmap == false )
9802 					{
9803 						std::string fullMapName = physfsFormatMapName(maptoload);
9804 						loadMap(fullMapName.c_str(), &map, map.entities, map.creatures, &checkMapHash);
9805 						if ( checkMapHash == 0 )
9806 						{
9807 							conductGameChallenges[CONDUCT_MODDED] = 1;
9808 						}
9809 					}
9810 					else
9811 					{
9812 						generateDungeon(maptoload, rand());
9813 					}
9814 				}
9815 				assignActions(&map);
9816 				generatePathMaps();
9817 				for ( node = map.entities->first; node != nullptr; node = nextnode )
9818 				{
9819 					nextnode = node->next;
9820 					Entity* entity = (Entity*)node->element;
9821 					if ( entity->flags[NOUPDATE] )
9822 					{
9823 						list_RemoveNode(entity->mynode);    // we're anticipating this entity data from server
9824 					}
9825 				}
9826 
9827 				printlog("Done.\n");
9828 			}
9829 
9830 			// spice of life achievement
9831 			usedClass[client_classes[clientnum]] = true;
9832 			bool usedAllClasses = true;
9833 			for ( c = 0; c <= CLASS_MONK; c++ )
9834 			{
9835 				if ( !usedClass[c] )
9836 				{
9837 					usedAllClasses = false;
9838 				}
9839 			}
9840 			if ( usedAllClasses )
9841 			{
9842 				steamAchievement("BARONY_ACH_SPICE_OF_LIFE");
9843 			}
9844 
9845 			if ( stats[clientnum]->playerRace >= 0 && stats[clientnum]->playerRace <= RACE_INSECTOID )
9846 			{
9847 				usedRace[stats[clientnum]->playerRace] = true;
9848 			}
9849 			// new achievement
9850 			usedAllClasses = true;
9851 			for ( c = 0; c <= CLASS_HUNTER; ++c )
9852 			{
9853 				if ( !usedClass[c] )
9854 				{
9855 					usedAllClasses = false;
9856 				}
9857 			}
9858 			bool usedAllRaces = true;
9859 			for ( c = RACE_HUMAN; c <= RACE_INSECTOID; ++c )
9860 			{
9861 				if ( !usedRace[c] )
9862 				{
9863 					usedAllRaces = false;
9864 				}
9865 			}
9866 			if ( usedAllClasses && usedAllRaces )
9867 			{
9868 				steamAchievement("BARONY_ACH_I_WANT_IT_ALL");
9869 			}
9870 
9871 			if ( gameModeManager.getMode() == GameModeManager_t::GAME_MODE_DEFAULT && !loadingsavegame )
9872 			{
9873 				steamStatisticUpdate(STEAM_STAT_GAMES_STARTED, STEAM_STAT_INT, 1);
9874 				achievementObserver.updateGlobalStat(STEAM_GSTAT_GAMES_STARTED);
9875 			}
9876 
9877 			// delete game data clutter
9878 			list_FreeAll(&messages);
9879 			list_FreeAll(&command_history);
9880 			list_FreeAll(&safePacketsSent);
9881 			for ( c = 0; c < MAXPLAYERS; c++ )
9882 			{
9883 				safePacketsReceivedMap[c].clear();
9884 			}
9885 			deleteAllNotificationMessages();
9886 			if ( !loadingsavegame ) // don't delete the followers we just created!
9887 			{
9888 				for (c = 0; c < MAXPLAYERS; c++)
9889 				{
9890 					list_FreeAll(&stats[c]->FOLLOWERS);
9891 				}
9892 			}
9893 
9894 			if ( loadingsavegame && multiplayer != CLIENT )
9895 			{
9896 				loadingsavegame = 0;
9897 			}
9898 
9899 			enchantedFeatherScrollSeed.seed(uniqueGameKey);
9900 			enchantedFeatherScrollsShuffled.clear();
9901 			enchantedFeatherScrollsShuffled = enchantedFeatherScrollsFixedList;
9902 			std::shuffle(enchantedFeatherScrollsShuffled.begin(), enchantedFeatherScrollsShuffled.end(), enchantedFeatherScrollSeed);
9903 			for ( auto it = enchantedFeatherScrollsShuffled.begin(); it != enchantedFeatherScrollsShuffled.end(); ++it )
9904 			{
9905 				//printlog("Sequence: %d", *it);
9906 			}
9907 
9908 			list_FreeAll(&removedEntities);
9909 			list_FreeAll(&chestInv);
9910 
9911 			// make some messages
9912 			startMessages();
9913 
9914 			// kick off the main loop!
9915 			pauseGame(1, 0);
9916 			loading = false;
9917 			intro = false;
9918 		}
9919 		else if ( introstage == 4 )     // credits
9920 		{
9921 			fadefinished = false;
9922 			fadeout = false;
9923 			if ( creditstage == 0 && victory == 3 )
9924 			{
9925 #ifdef MUSIC
9926 			playmusic(citadelmusic[0], true, false, false);
9927 #endif
9928 			}
9929 			creditstage++;
9930 			if ( creditstage >= 15 )
9931 			{
9932 #ifdef MUSIC
9933 				if ( victory == 3 )
9934 				{
9935 					playmusic(intromusic[2], true, false, false);
9936 				}
9937 				else
9938 				{
9939 					playmusic(intromusic[rand() % 2], true, false, false);
9940 				}
9941 #endif
9942 				introstage = 1;
9943 				credittime = 0;
9944 				creditstage = 0;
9945 				movie = false;
9946 			}
9947 			else
9948 			{
9949 				credittime = 0;
9950 				movie = true;
9951 			}
9952 		}
9953 		else if ( introstage == 5 )     // end game
9954 		{
9955 			bool endTutorial = false;
9956 			if ( gameModeManager.getMode() != GameModeManager_t::GAME_MODE_DEFAULT )
9957 			{
9958 				victory = 0;
9959 				gameModeManager.setMode(GameModeManager_t::GAME_MODE_DEFAULT);
9960 				endTutorial = true;
9961 			}
9962 
9963 			// in greater numbers achievement
9964 			if ( victory )
9965 			{
9966 				int k = 0;
9967 				for ( c = 0; c < MAXPLAYERS; c++ )
9968 				{
9969 					if (players[c] && players[c]->entity)
9970 					{
9971 						k++;
9972 					}
9973 				}
9974 				if ( k >= 2 )
9975 				{
9976 					steamAchievement("BARONY_ACH_IN_GREATER_NUMBERS");
9977 				}
9978 
9979 				if ( (victory == 1 && currentlevel >= 20)
9980 					|| (victory == 2 && currentlevel >= 24)
9981 					|| (victory == 3 && currentlevel >= 35) )
9982 				{
9983 					if ( client_classes[clientnum] == CLASS_ACCURSED )
9984 					{
9985 						if ( stats[clientnum]->EFFECTS[EFF_VAMPIRICAURA] && stats[clientnum]->EFFECTS_TIMERS[EFF_VAMPIRICAURA] == -2 )
9986 						{
9987 							conductGameChallenges[CONDUCT_ACCURSED] = 1;
9988 						}
9989 					}
9990 					if ( completionTime < 20 * 60 * TICKS_PER_SECOND )
9991 					{
9992 						conductGameChallenges[CONDUCT_BOOTS_SPEED] = 1;
9993 					}
9994 					achievementObserver.updateGlobalStat(STEAM_GSTAT_GAMES_WON);
9995 				}
9996 			}
9997 
9998 			// figure out the victory crawl texts...
9999 			int movieCrawlType = -1;
10000 			if ( victory )
10001 			{
10002 				if ( stats[0] )
10003 				{
10004 					strcpy(epilogueHostName, stats[0]->name);
10005 					epilogueHostRace = RACE_HUMAN;
10006 					if ( stats[0]->playerRace > 0 && stats[0]->appearance == 0 )
10007 					{
10008 						epilogueHostRace = stats[0]->playerRace;
10009 					}
10010 					epilogueMultiplayerType = multiplayer;
10011 					if ( victory == 1 && epilogueHostRace > 0 && epilogueHostRace != RACE_AUTOMATON )
10012 					{
10013 						// herx defeat by monsters.
10014 						movieCrawlType = MOVIE_CLASSIC_WIN_MONSTERS;
10015 					}
10016 					else if ( victory == 2 && epilogueHostRace > 0 && epilogueHostRace != RACE_AUTOMATON )
10017 					{
10018 						// baphomet defeat by monsters.
10019 						movieCrawlType = MOVIE_CLASSIC_WIN_BAPHOMET_MONSTERS;
10020 					}
10021 					else if ( victory == 3 )
10022 					{
10023 						switch ( epilogueHostRace )
10024 						{
10025 							case RACE_AUTOMATON:
10026 								movieCrawlType = MOVIE_WIN_AUTOMATON;
10027 								break;
10028 							case RACE_SKELETON:
10029 							case RACE_VAMPIRE:
10030 							case RACE_SUCCUBUS:
10031 							case RACE_INCUBUS:
10032 								movieCrawlType = MOVIE_WIN_DEMONS_UNDEAD;
10033 								break;
10034 							case RACE_GOATMAN:
10035 							case RACE_GOBLIN:
10036 							case RACE_INSECTOID:
10037 								movieCrawlType = MOVIE_WIN_BEASTS;
10038 								break;
10039 							case RACE_HUMAN:
10040 								break;
10041 							default:
10042 								break;
10043 						}
10044 					}
10045 				}
10046 			}
10047 
10048 			// make a highscore!
10049 			if ( !endTutorial )
10050 			{
10051 				int saveScoreResult = saveScore();
10052 			}
10053 
10054 			// pick a new subtitle :)
10055 			subtitleCurrent = rand() % NUMSUBTITLES;
10056 			subtitleVisible = true;
10057 
10058 			for ( c = 0; c < NUMMONSTERS; c++ )
10059 			{
10060 				kills[c] = 0;
10061 			}
10062 
10063 			// stop all sounds
10064 #ifdef USE_FMOD
10065 			if ( sound_group )
10066 			{
10067 				FMOD_ChannelGroup_Stop(sound_group);
10068 			}
10069 			if ( soundAmbient_group )
10070 			{
10071 				FMOD_ChannelGroup_Stop(soundAmbient_group);
10072 			}
10073 			if ( soundEnvironment_group )
10074 			{
10075 				FMOD_ChannelGroup_Stop(soundEnvironment_group);
10076 			}
10077 #elif defined USE_OPENAL
10078 			if ( sound_group )
10079 			{
10080 				OPENAL_ChannelGroup_Stop(sound_group);
10081 			}
10082 			if ( soundAmbient_group )
10083 			{
10084 				OPENAL_ChannelGroup_Stop(soundAmbient_group);
10085 			}
10086 			if ( soundEnvironment_group )
10087 			{
10088 				OPENAL_ChannelGroup_Stop(soundEnvironment_group);
10089 			}
10090 #endif
10091 
10092 			// send disconnect messages
10093 			if (multiplayer == CLIENT)
10094 			{
10095 				strcpy((char*)net_packet->data, "DISCONNECT");
10096 				net_packet->data[10] = clientnum;
10097 				net_packet->address.host = net_server.host;
10098 				net_packet->address.port = net_server.port;
10099 				net_packet->len = 11;
10100 				sendPacketSafe(net_sock, -1, net_packet, 0);
10101 				printlog("disconnected from server.\n");
10102 			}
10103 			else if (multiplayer == SERVER)
10104 			{
10105 				for (x = 1; x < MAXPLAYERS; x++)
10106 				{
10107 					if ( client_disconnected[x] == true )
10108 					{
10109 						continue;
10110 					}
10111 					strcpy((char*)net_packet->data, "DISCONNECT");
10112 					net_packet->data[10] = clientnum;
10113 					net_packet->address.host = net_clients[x - 1].host;
10114 					net_packet->address.port = net_clients[x - 1].port;
10115 					net_packet->len = 11;
10116 					sendPacketSafe(net_sock, -1, net_packet, x - 1);
10117 					client_disconnected[x] = true;
10118 				}
10119 			}
10120 
10121 			// clean up shopInv
10122 			if ( multiplayer == CLIENT )
10123 			{
10124 				if ( shopInv )
10125 				{
10126 					list_FreeAll(shopInv);
10127 					free(shopInv);
10128 					shopInv = NULL;
10129 				}
10130 			}
10131 
10132 			// delete save game
10133 			if ( !savethisgame && !endTutorial )
10134 			{
10135 				deleteSaveGame(multiplayer);
10136 			}
10137 			else
10138 			{
10139 				savethisgame = false;
10140 			}
10141 
10142 			if ( victory )
10143 			{
10144 				// conduct achievements
10145 				if ( (victory == 1 && currentlevel >= 20)
10146 					|| (victory == 2 && currentlevel >= 24)
10147 					|| (victory == 3 && currentlevel >= 35) )
10148 				{
10149 					if ( conductPenniless )
10150 					{
10151 						steamAchievement("BARONY_ACH_PENNILESS_CONDUCT");
10152 					}
10153 					if ( conductFoodless )
10154 					{
10155 						steamAchievement("BARONY_ACH_FOODLESS_CONDUCT");
10156 					}
10157 					if ( conductVegetarian )
10158 					{
10159 						steamAchievement("BARONY_ACH_VEGETARIAN_CONDUCT");
10160 					}
10161 					if ( conductIlliterate )
10162 					{
10163 						steamAchievement("BARONY_ACH_ILLITERATE_CONDUCT");
10164 					}
10165 
10166 					if ( completionTime < 20 * 60 * TICKS_PER_SECOND )
10167 					{
10168 						steamAchievement("BARONY_ACH_BOOTS_OF_SPEED");
10169 					}
10170 				}
10171 
10172 				if ( victory == 1 )
10173 				{
10174 					if ( currentlevel >= 20 )
10175 					{
10176 						if ( conductGameChallenges[CONDUCT_HARDCORE] )
10177 						{
10178 							steamAchievement("BARONY_ACH_HARDCORE");
10179 						}
10180 					}
10181 				}
10182 				else if ( victory == 2 )
10183 				{
10184 					if ( currentlevel >= 24 )
10185 					{
10186 						if ( conductGameChallenges[CONDUCT_HARDCORE] )
10187 						{
10188 							steamAchievement("BARONY_ACH_HARDCORE");
10189 						}
10190 					}
10191 				}
10192 				else if ( victory == 3 )
10193 				{
10194 					if ( currentlevel >= 35 )
10195 					{
10196 						if ( conductGameChallenges[CONDUCT_BRAWLER] )
10197 						{
10198 							steamAchievement("BARONY_ACH_BRAWLER");
10199 						}
10200 						if ( conductGameChallenges[CONDUCT_BLESSED_BOOTS_SPEED] )
10201 						{
10202 							steamAchievement("BARONY_ACH_PLUS_BOOTS_OF_SPEED");
10203 						}
10204 						if ( conductGameChallenges[CONDUCT_HARDCORE] )
10205 						{
10206 							steamAchievement("BARONY_ACH_POST_HARDCORE");
10207 						}
10208 
10209 						if ( client_classes[clientnum] == CLASS_MESMER )
10210 						{
10211 							steamAchievement("BARONY_ACH_COMMANDER_CHIEF");
10212 						}
10213 						else if ( client_classes[clientnum] == CLASS_BREWER )
10214 						{
10215 							steamAchievement("BARONY_ACH_DRUNK_POWER");
10216 						}
10217 						else if ( client_classes[clientnum] == CLASS_ACCURSED )
10218 						{
10219 							steamAchievement("BARONY_ACH_POWER_HUNGRY");
10220 							if ( stats[clientnum]->EFFECTS[EFF_VAMPIRICAURA] && stats[clientnum]->EFFECTS_TIMERS[EFF_VAMPIRICAURA] == -2 )
10221 							{
10222 								if ( stats[clientnum] && (svFlags & SV_FLAG_HUNGER) )
10223 								{
10224 									steamAchievement("BARONY_ACH_BLOOD_IS_THE_LIFE");
10225 								}
10226 							}
10227 						}
10228 						else if ( client_classes[clientnum] == CLASS_HUNTER )
10229 						{
10230 							steamAchievement("BARONY_ACH_RANGER_DANGER");
10231 							if ( conductGameChallenges[CONDUCT_RANGED_ONLY] )
10232 							{
10233 								steamAchievement("BARONY_ACH_GUDIPARIAN_BAZI");
10234 							}
10235 						}
10236 						else if ( client_classes[clientnum] == CLASS_CONJURER )
10237 						{
10238 							steamAchievement("BARONY_ACH_TURN_UNDEAD");
10239 						}
10240 						else if ( client_classes[clientnum] == CLASS_SHAMAN )
10241 						{
10242 							steamAchievement("BARONY_ACH_MY_FINAL_FORM");
10243 						}
10244 						else if ( client_classes[clientnum] == CLASS_PUNISHER )
10245 						{
10246 							steamAchievement("BARONY_ACH_TIME_TO_SUFFER");
10247 						}
10248 						else if ( client_classes[clientnum] == CLASS_MACHINIST )
10249 						{
10250 							steamAchievement("BARONY_ACH_LIKE_CLOCKWORK");
10251 						}
10252 
10253 						if ( stats[clientnum] && stats[clientnum]->appearance == 0 )
10254 						{
10255 							switch ( stats[clientnum]->playerRace )
10256 							{
10257 								case RACE_SKELETON:
10258 									steamAchievement("BARONY_ACH_BONY_BARON");
10259 									break;
10260 								case RACE_SUCCUBUS:
10261 									steamAchievement("BARONY_ACH_BOMBSHELL_BARON");
10262 									break;
10263 								case RACE_GOATMAN:
10264 									steamAchievement("BARONY_ACH_BLEATING_BARON");
10265 									break;
10266 								case RACE_VAMPIRE:
10267 									steamAchievement("BARONY_ACH_BUCKTOOTH_BARON");
10268 									break;
10269 								case RACE_INCUBUS:
10270 									steamAchievement("BARONY_ACH_BAD_BOY_BARON");
10271 									break;
10272 								case RACE_INSECTOID:
10273 									steamAchievement("BARONY_ACH_BUGGAR_BARON");
10274 									break;
10275 								case RACE_AUTOMATON:
10276 									steamAchievement("BARONY_ACH_BOILERPLATE_BARON");
10277 									break;
10278 								case RACE_GOBLIN:
10279 									steamAchievement("BARONY_ACH_BAYOU_BARON");
10280 									break;
10281 								default:
10282 									break;
10283 							}
10284 						}
10285 					}
10286 				}
10287 			}
10288 
10289 			// reset game
10290 			darkmap = false;
10291 			appraisal_timer = 0;
10292 			appraisal_item = 0;
10293 			multiplayer = 0;
10294 			shootmode = true;
10295 			currentlevel = 0;
10296 			secretlevel = false;
10297 			clientnum = 0;
10298 			introstage = 1;
10299 			intro = true;
10300 			deinitShapeshiftHotbar();
10301 			for ( c = 0; c < NUM_HOTBAR_ALTERNATES; ++c )
10302 			{
10303 				selected_spell_alternate[c] = NULL;
10304 				hotbarShapeshiftInit[c] = false;
10305 			}
10306 			gameModeManager.currentSession.restoreSavedServerFlags();
10307 			selected_spell = NULL; //So you don't start off with a spell when the game restarts.
10308 			selected_spell_last_appearance = -1;
10309 			client_classes[0] = 0;
10310 			spellcastingAnimationManager_deactivate(&cast_animation);
10311 			SDL_StopTextInput();
10312 
10313 			// delete game data clutter
10314 			list_FreeAll(&messages);
10315 			list_FreeAll(&command_history);
10316 			list_FreeAll(&safePacketsSent);
10317 			for ( c = 0; c < MAXPLAYERS; c++ )
10318 			{
10319 				safePacketsReceivedMap[c].clear();
10320 			}
10321 			deleteAllNotificationMessages();
10322 			for (c = 0; c < MAXPLAYERS; c++)
10323 			{
10324 				stats[c]->freePlayerEquipment();
10325 				list_FreeAll(&stats[c]->inventory);
10326 				list_FreeAll(&stats[c]->FOLLOWERS);
10327 			}
10328 			list_FreeAll(&removedEntities);
10329 			list_FreeAll(&chestInv);
10330 
10331 			// default player stats
10332 			for ( c = 0; c < MAXPLAYERS; c++ )
10333 			{
10334 				if ( c > 0 )
10335 				{
10336 					client_disconnected[c] = true;
10337 				}
10338 				else
10339 				{
10340 					client_disconnected[c] = false;
10341 				}
10342 				players[c]->entity = nullptr; //TODO: PLAYERSWAP VERIFY. Need to do anything else?
10343 				stats[c]->sex = static_cast<sex_t>(0);
10344 				stats[c]->appearance = 0;
10345 				strcpy(stats[c]->name, "");
10346 				stats[c]->type = HUMAN;
10347 				stats[c]->playerRace = RACE_HUMAN;
10348 				stats[c]->clearStats();
10349 				entitiesToDelete[c].first = NULL;
10350 				entitiesToDelete[c].last = NULL;
10351 				if ( c == 0 )
10352 				{
10353 					initClass(c);
10354 				}
10355 			}
10356 
10357 			// hack to fix these things from breaking everything...
10358 			hudarm = NULL;
10359 			hudweapon = NULL;
10360 			magicLeftHand = NULL;
10361 			magicRightHand = NULL;
10362 
10363 			// load menu level
10364 			int menuMapType = 0;
10365 			if ( victory == 3 )
10366 			{
10367 				menuMapType = loadMainMenuMap(true, true);
10368 			}
10369 			else
10370 			{
10371 				switch ( rand() % 2 )
10372 				{
10373 					case 0:
10374 						menuMapType = loadMainMenuMap(true, false);
10375 						break;
10376 					case 1:
10377 						menuMapType = loadMainMenuMap(false, false);
10378 						break;
10379 					default:
10380 						break;
10381 				}
10382 			}
10383 			for (int c = 0; c < MAXPLAYERS; ++c) {
10384 				cameras[c].vang = 0;
10385 			}
10386 			numplayers = 0;
10387 			assignActions(&map);
10388 			generatePathMaps();
10389 			gamePaused = false;
10390 			if ( !victory )
10391 			{
10392 				fadefinished = false;
10393 				fadeout = false;
10394 #ifdef MUSIC
10395 				if ( menuMapType )
10396 				{
10397 					playmusic(intromusic[2], true, false, false);
10398 				}
10399 				else
10400 				{
10401 					playmusic(intromusic[rand() % 2], true, false, false);
10402 				}
10403 #endif
10404 			}
10405 			else
10406 			{
10407 				if ( victory == 1 )
10408 				{
10409 					introstage = 7;
10410 				}
10411 				else if ( victory == 2 )
10412 				{
10413 					introstage = 8;
10414 				}
10415 				else if ( victory == 3 )
10416 				{
10417 					introstage = 10;
10418 				}
10419 
10420 				if ( movieCrawlType >= 0 ) // overrides the introstage 7,8,10 sequences for DLC monsters.
10421 				{
10422 					introstage = 11 + movieCrawlType;
10423 				}
10424 			}
10425 
10426 			// finish handling invite
10427 #ifdef STEAMWORKS
10428 			if ( stillConnectingToLobby )
10429 			{
10430 				processLobbyInvite();
10431 			}
10432 #endif
10433 #if defined USE_EOS
10434 			if ( !directConnect )
10435 			{
10436 				if ( EOS.CurrentLobbyData.currentLobbyIsValid() )
10437 				{
10438 					EOS.leaveLobby();
10439 				}
10440 			}
10441 #endif
10442 		}
10443 		else if ( introstage == 6 )     // introduction cutscene
10444 		{
10445 			fadefinished = false;
10446 			fadeout = false;
10447 			intromoviestage++;
10448 			if ( intromoviestage >= 9 )
10449 			{
10450 #ifdef MUSIC
10451 				playmusic(intromusic[1], true, false, false);
10452 #endif
10453 				introstage = 1;
10454 				intromovietime = 0;
10455 				intromoviestage = 0;
10456 				int c;
10457 				for ( c = 0; c < 30; c++ )
10458 				{
10459 					intromoviealpha[c] = 0;
10460 				}
10461 				movie = false;
10462 			}
10463 			else
10464 			{
10465 				intromovietime = 0;
10466 				movie = true;
10467 			}
10468 		}
10469 		else if ( introstage == 7 )     // win game sequence (herx)
10470 		{
10471 #ifdef MUSIC
10472 			if ( firstendmoviestage == 0 )
10473 			{
10474 				playmusic(endgamemusic, true, true, false);
10475 			}
10476 #endif
10477 			firstendmoviestage++;
10478 			if ( firstendmoviestage >= 5 )
10479 			{
10480 				introstage = 4;
10481 				firstendmovietime = 0;
10482 				firstendmoviestage = 0;
10483 				int c;
10484 				for ( c = 0; c < 30; c++ )
10485 				{
10486 					firstendmoviealpha[c] = 0;
10487 				}
10488 				fadeout = true;
10489 			}
10490 			else
10491 			{
10492 				fadefinished = false;
10493 				fadeout = false;
10494 				firstendmovietime = 0;
10495 				movie = true;
10496 			}
10497 		}
10498 		else if ( introstage == 8 )     // win game sequence (devil)
10499 		{
10500 #ifdef MUSIC
10501 			if ( secondendmoviestage == 0 )
10502 			{
10503 				playmusic(endgamemusic, true, true, false);
10504 			}
10505 #endif
10506 			secondendmoviestage++;
10507 			if ( secondendmoviestage >= 5 )
10508 			{
10509 				introstage = 4;
10510 				secondendmovietime = 0;
10511 				secondendmoviestage = 0;
10512 				int c;
10513 				for ( c = 0; c < 30; c++ )
10514 				{
10515 					secondendmoviealpha[c] = 0;
10516 				}
10517 				fadeout = true;
10518 			}
10519 			else
10520 			{
10521 				fadefinished = false;
10522 				fadeout = false;
10523 				secondendmovietime = 0;
10524 				movie = true;
10525 			}
10526 		}
10527 		else if ( introstage == 9 )     // mid game sequence
10528 		{
10529 #ifdef MUSIC
10530 			if ( thirdendmoviestage == 0 )
10531 			{
10532 				playmusic(endgamemusic, true, true, false);
10533 			}
10534 #endif
10535 			thirdendmoviestage++;
10536 			if ( thirdendmoviestage >= thirdEndNumLines )
10537 			{
10538 				int c;
10539 				for ( c = 0; c < 30; c++ )
10540 				{
10541 					thirdendmoviealpha[c] = 0;
10542 				}
10543 				fadefinished = false;
10544 				fadeout = false;
10545 				if ( multiplayer != CLIENT )
10546 				{
10547 					movie = false; // allow normal pause screen.
10548 					thirdendmoviestage = 0;
10549 					thirdendmovietime = 0;
10550 					introstage = 1; // return to normal game functionality
10551 					skipLevelsOnLoad = 5;
10552 					loadnextlevel = true; // load the next level.
10553 					pauseGame(1, false); // unpause game
10554 				}
10555 			}
10556 			else
10557 			{
10558 				fadefinished = false;
10559 				fadeout = false;
10560 				thirdendmovietime = 0;
10561 				movie = true;
10562 			}
10563 		}
10564 		else if ( introstage == 10 )     // expansion end game sequence
10565 		{
10566 #ifdef MUSIC
10567 			if ( fourthendmoviestage == 0 )
10568 			{
10569 				playmusic(endgamemusic, true, true, false);
10570 			}
10571 #endif
10572 			fourthendmoviestage++;
10573 			if ( fourthendmoviestage >= fourthEndNumLines )
10574 			{
10575 				int c;
10576 				for ( c = 0; c < 30; c++ )
10577 				{
10578 					fourthendmoviealpha[c] = 0;
10579 				}
10580 				introstage = 4;
10581 				fourthendmovietime = 0;
10582 				fourthendmoviestage = 0;
10583 				fadeout = true;
10584 			}
10585 			else
10586 			{
10587 				fadefinished = false;
10588 				fadeout = false;
10589 				fourthendmovietime = 0;
10590 				movie = true;
10591 			}
10592 		}
10593 		else if ( introstage >= 11 && introstage <= 15 )     // new mid and classic end sequences.
10594 		{
10595 			int movieType = introstage - 11;
10596 			for ( int i = 0; i < 8; ++i )
10597 			{
10598 				if ( i != movieType )
10599 				{
10600 					// clean the other end stage credits.
10601 					DLCendmovieStageAndTime[i][MOVIE_STAGE] = 0;
10602 					DLCendmovieStageAndTime[i][MOVIE_TIME] = 0;
10603 				}
10604 			}
10605 #ifdef MUSIC
10606 			if ( DLCendmovieStageAndTime[movieType][MOVIE_STAGE] == 0 )
10607 			{
10608 				playmusic(endgamemusic, true, true, false);
10609 			}
10610 #endif
10611 			DLCendmovieStageAndTime[movieType][MOVIE_STAGE]++;
10612 			if ( movieType == MOVIE_CLASSIC_WIN_BAPHOMET_MONSTERS || movieType == MOVIE_CLASSIC_WIN_MONSTERS )
10613 			{
10614 				// win crawls.
10615 				if ( DLCendmovieStageAndTime[movieType][MOVIE_STAGE] >= DLCendmovieNumLines[movieType] )
10616 				{
10617 					introstage = 4;
10618 					DLCendmovieStageAndTime[movieType][MOVIE_TIME] = 0;
10619 					DLCendmovieStageAndTime[movieType][MOVIE_STAGE] = 0;
10620 					int c;
10621 					for ( c = 0; c < 30; c++ )
10622 					{
10623 						DLCendmoviealpha[movieType][c] = 0;
10624 					}
10625 					fadeout = true;
10626 				}
10627 				else
10628 				{
10629 					fadefinished = false;
10630 					fadeout = false;
10631 					DLCendmovieStageAndTime[movieType][MOVIE_TIME] = 0;
10632 					movie = true;
10633 				}
10634 			}
10635 			else
10636 			{
10637 				// mid-game sequences
10638 				if ( DLCendmovieStageAndTime[movieType][MOVIE_STAGE] >= DLCendmovieNumLines[movieType] )
10639 				{
10640 					int c;
10641 					for ( c = 0; c < 30; c++ )
10642 					{
10643 						DLCendmoviealpha[movieType][c] = 0;
10644 					}
10645 					fadefinished = false;
10646 					fadeout = false;
10647 					if ( multiplayer != CLIENT )
10648 					{
10649 						movie = false; // allow normal pause screen.
10650 						DLCendmovieStageAndTime[movieType][MOVIE_STAGE] = 0;
10651 						DLCendmovieStageAndTime[movieType][MOVIE_TIME] = 0;
10652 						introstage = 1; // return to normal game functionality
10653 						if ( movieType == MOVIE_MIDGAME_HERX_MONSTERS )
10654 						{
10655 							skipLevelsOnLoad = 5;
10656 						}
10657 						else
10658 						{
10659 							skipLevelsOnLoad = 0;
10660 						}
10661 						loadnextlevel = true; // load the next level.
10662 						pauseGame(1, false); // unpause game
10663 					}
10664 				}
10665 				else
10666 				{
10667 					fadefinished = false;
10668 					fadeout = false;
10669 					DLCendmovieStageAndTime[movieType][MOVIE_TIME] = 0;
10670 					movie = true;
10671 				}
10672 			}
10673 		}
10674 		else if ( introstage >= 16 && introstage <= 18 )     // expansion end game sequence DLC
10675 		{
10676 			int movieType = introstage - 11;
10677 			for ( int i = 0; i < 8; ++i )
10678 			{
10679 				if ( i != movieType )
10680 				{
10681 					// clean the other end stage credits.
10682 					DLCendmovieStageAndTime[i][MOVIE_STAGE] = 0;
10683 					DLCendmovieStageAndTime[i][MOVIE_TIME] = 0;
10684 				}
10685 			}
10686 #ifdef MUSIC
10687 			if ( DLCendmovieStageAndTime[movieType][MOVIE_STAGE] == 0 )
10688 			{
10689 				playmusic(endgamemusic, true, true, false);
10690 			}
10691 #endif
10692 			DLCendmovieStageAndTime[movieType][MOVIE_STAGE]++;
10693 			if ( DLCendmovieStageAndTime[movieType][MOVIE_STAGE] >= DLCendmovieNumLines[movieType] )
10694 			{
10695 				int c;
10696 				for ( c = 0; c < 30; c++ )
10697 				{
10698 					DLCendmoviealpha[movieType][c] = 0;
10699 				}
10700 				introstage = 4;
10701 				DLCendmovieStageAndTime[movieType][MOVIE_TIME] = 0;
10702 				DLCendmovieStageAndTime[movieType][MOVIE_STAGE] = 0;
10703 				fadeout = true;
10704 			}
10705 			else
10706 			{
10707 				fadefinished = false;
10708 				fadeout = false;
10709 				DLCendmovieStageAndTime[movieType][MOVIE_TIME] = 0;
10710 				movie = true;
10711 			}
10712 		}
10713 	}
10714 
10715 	// credits sequence
10716 	if ( creditstage > 0 )
10717 	{
10718 		if ( (credittime >= 300 && (creditstage <= 11 || creditstage > 13)) || (credittime >= 180 && creditstage == 12) ||
10719 		        (credittime >= 480 && creditstage == 13) || mousestatus[SDL_BUTTON_LEFT] || (*inputPressed(joyimpulses[INJOY_MENU_NEXT]) && rebindaction == -1) )
10720 		{
10721 			mousestatus[SDL_BUTTON_LEFT] = 0;
10722 			if ( rebindaction == -1 )
10723 			{
10724 				*inputPressed(joyimpulses[INJOY_MENU_NEXT]) = 0;
10725 			}
10726 			introstage = 4;
10727 			fadeout = true;
10728 		}
10729 
10730 		// stages
10731 		Uint32 colorBlue = SDL_MapRGBA(mainsurface->format, 0, 92, 255, 255);
10732 		if ( creditstage == 1 )
10733 		{
10734 			ttfPrintTextFormattedColor(ttf16, xres / 2 - (TTF16_WIDTH / 2)*strlen(language[56]), yres / 2 - 9 - 18, colorBlue, language[56]);
10735 			ttfPrintTextFormatted(ttf16, xres / 2 - (TTF16_WIDTH / 2)*strlen(CREDITSLINE02), yres / 2 - 9 + 18, CREDITSLINE02);
10736 		}
10737 		else if ( creditstage == 2 )
10738 		{
10739 			ttfPrintTextFormattedColor(ttf16, xres / 2 - (TTF16_WIDTH / 2)*strlen(language[57]), yres / 2 - 9 - 18, colorBlue, language[57]);
10740 			ttfPrintTextFormatted(ttf16, xres / 2 - (TTF16_WIDTH / 2)*strlen(CREDITSLINE04), yres / 2 - 9 + 18, CREDITSLINE04);
10741 		}
10742 		else if ( creditstage == 3 )
10743 		{
10744 			ttfPrintTextFormattedColor(ttf16, xres / 2 - (TTF16_WIDTH / 2)*strlen(language[58]), yres / 2 - 9 - 18, colorBlue, language[58]);
10745 			ttfPrintTextFormatted(ttf16, xres / 2 - (TTF16_WIDTH / 2)*strlen(CREDITSLINE06), yres / 2 - 9, CREDITSLINE06);
10746 			ttfPrintTextFormatted(ttf16, xres / 2 - (TTF16_WIDTH / 2)*strlen(CREDITSLINE40), yres / 2 - 9 + 18, CREDITSLINE40);
10747 		}
10748 		else if ( creditstage == 4 )
10749 		{
10750 			ttfPrintTextFormattedColor(ttf16, xres / 2 - (TTF16_WIDTH / 2)*strlen(language[59]), yres / 2 - 9 - 18, colorBlue, language[59]);
10751 			ttfPrintTextFormatted(ttf16, xres / 2 - (TTF16_WIDTH / 2)*strlen(CREDITSLINE39), yres / 2 + 9, CREDITSLINE39);
10752 		}
10753 		else if ( creditstage == 5 )
10754 		{
10755 			ttfPrintTextFormattedColor(ttf16, xres / 2 - (TTF16_WIDTH / 2)*strlen(language[60]), yres / 2 - 9 - 18, colorBlue, language[60]);
10756 			ttfPrintTextFormatted(ttf16, xres / 2 - (TTF16_WIDTH / 2)*strlen(CREDITSLINE11), yres / 2 - 9, CREDITSLINE11);
10757 			ttfPrintTextFormatted(ttf16, xres / 2 - (TTF16_WIDTH / 2)*strlen(CREDITSLINE08), yres / 2 - 9 + 18, CREDITSLINE08);
10758 			ttfPrintTextFormatted(ttf16, xres / 2 - (TTF16_WIDTH / 2)*strlen(CREDITSLINE09), yres / 2 + 9 + 18 * 1, CREDITSLINE09);
10759 		}
10760 		else if ( creditstage == 6 )
10761 		{
10762 			ttfPrintTextFormattedColor(ttf16, xres / 2 - (TTF16_WIDTH / 2)*strlen(language[61]), yres / 2 - 9 - 18, colorBlue, language[61]);
10763 			ttfPrintTextFormatted(ttf16, xres / 2 - (TTF16_WIDTH / 2)*strlen(CREDITSLINE13), yres / 2 - 9 + 18, CREDITSLINE13);
10764 		}
10765 		else if ( creditstage == 7 )
10766 		{
10767 			ttfPrintTextFormattedColor(ttf16, xres / 2 - (TTF16_WIDTH / 2)*strlen(language[62]), yres / 2 - 9 - 18 * 4, colorBlue, language[62]);
10768 			ttfPrintTextFormatted(ttf16, xres / 2 - (TTF16_WIDTH / 2)*strlen(CREDITSLINE15), yres / 2 - 9 - 18 * 2, CREDITSLINE15);
10769 			ttfPrintTextFormatted(ttf16, xres / 2 - (TTF16_WIDTH / 2)*strlen(CREDITSLINE16), yres / 2 - 9 - 18 * 1, CREDITSLINE16);
10770 			ttfPrintTextFormatted(ttf16, xres / 2 - (TTF16_WIDTH / 2)*strlen(CREDITSLINE17), yres / 2 - 9, CREDITSLINE17);
10771 			ttfPrintTextFormatted(ttf16, xres / 2 - (TTF16_WIDTH / 2)*strlen(CREDITSLINE18), yres / 2 + 9, CREDITSLINE18);
10772 			ttfPrintTextFormatted(ttf16, xres / 2 - (TTF16_WIDTH / 2)*strlen(CREDITSLINE19), yres / 2 + 9 + 18 * 1, CREDITSLINE19);
10773 			ttfPrintTextFormatted(ttf16, xres / 2 - (TTF16_WIDTH / 2)*strlen(CREDITSLINE20), yres / 2 + 9 + 18 * 2, CREDITSLINE20);
10774 			ttfPrintTextFormatted(ttf16, xres / 2 - (TTF16_WIDTH / 2)*strlen(CREDITSLINE21), yres / 2 + 9 + 18 * 3, CREDITSLINE21);
10775 		}
10776 		else if ( creditstage == 8 )
10777 		{
10778 			ttfPrintTextFormattedColor(ttf16, xres / 2 - (TTF16_WIDTH / 2)*strlen(language[63]), yres / 2 - 9 - 18 * 4, colorBlue, language[63]);
10779 			ttfPrintTextFormatted(ttf16, xres / 2 - (TTF16_WIDTH / 2)*strlen(CREDITSLINE23), yres / 2 - 9 - 18 * 2, CREDITSLINE23);
10780 			ttfPrintTextFormatted(ttf16, xres / 2 - (TTF16_WIDTH / 2)*strlen(CREDITSLINE24), yres / 2 - 9 - 18 * 1, CREDITSLINE24);
10781 			ttfPrintTextFormatted(ttf16, xres / 2 - (TTF16_WIDTH / 2)*strlen(CREDITSLINE25), yres / 2 - 9, CREDITSLINE25);
10782 			ttfPrintTextFormatted(ttf16, xres / 2 - (TTF16_WIDTH / 2)*strlen(CREDITSLINE26), yres / 2 + 9, CREDITSLINE26);
10783 			ttfPrintTextFormatted(ttf16, xres / 2 - (TTF16_WIDTH / 2)*strlen(CREDITSLINE27), yres / 2 + 9 + 18 * 1, CREDITSLINE27);
10784 			ttfPrintTextFormatted(ttf16, xres / 2 - (TTF16_WIDTH / 2)*strlen(CREDITSLINE28), yres / 2 + 9 + 18 * 2, CREDITSLINE28);
10785 			ttfPrintTextFormatted(ttf16, xres / 2 - (TTF16_WIDTH / 2)*strlen(CREDITSLINE29), yres / 2 + 9 + 18 * 3, CREDITSLINE29);
10786 			ttfPrintTextFormatted(ttf16, xres / 2 - (TTF16_WIDTH / 2)*strlen(CREDITSLINE30), yres / 2 + 9 + 18 * 4, CREDITSLINE30);
10787 		}
10788 		else if ( creditstage == 9 )
10789 		{
10790 			ttfPrintTextFormattedColor(ttf16, xres / 2 - (TTF16_WIDTH / 2)*strlen(language[2585]), yres / 2 - 9 - 18, colorBlue, language[2585]);
10791 			ttfPrintTextFormattedColor(ttf16, xres / 2 - (TTF16_WIDTH / 2)*strlen(language[2586]), yres / 2 - 9, colorBlue, language[2586]);
10792 			ttfPrintTextFormattedColor(ttf16, xres / 2 - (TTF16_WIDTH / 2)*strlen(language[2587]), yres / 2 - 9 + 18, colorBlue, language[2587]);
10793 			ttfPrintTextFormattedColor(ttf16, xres / 2 - (TTF16_WIDTH / 2)*strlen(language[2588]), yres / 2 + 9 + 18, colorBlue, language[2588]);
10794 		}
10795 		else if ( creditstage == 10 )
10796 		{
10797 			ttfPrintTextFormattedColor(ttf16, xres / 2 - (TTF16_WIDTH / 2)*strlen(language[64]), yres / 2 - 9 - 18, colorBlue, language[64]);
10798 			ttfPrintTextFormatted(ttf16, xres / 2 - (TTF16_WIDTH / 2)*strlen(language[65]), yres / 2 - 9 + 18, language[65]);
10799 		}
10800 		else if ( creditstage == 11 )
10801 		{
10802 
10803 			// title
10804 			SDL_Rect src;
10805 			src.x = 0;
10806 			src.y = 0;
10807 			src.w = title_bmp->w;
10808 			src.h = title_bmp->h;
10809 			SDL_Rect dest;
10810 			dest.x = xres / 2 - (title_bmp->w) / 2;
10811 			dest.y = yres / 2 - title_bmp->h / 2 - 96;
10812 			dest.w = xres;
10813 			dest.h = yres;
10814 			drawImage(title_bmp, &src, &dest);
10815 			// text
10816 			ttfPrintTextFormatted(ttf16, xres / 2 - (TTF16_WIDTH / 2) * strlen(language[66]), yres / 2, language[66]);
10817 			ttfPrintTextFormatted(ttf16, xres / 2 - (TTF16_WIDTH / 2) * strlen(language[67]), yres / 2 + 20, language[67]);
10818 			ttfPrintTextFormatted(ttf16, xres / 2 - (TTF16_WIDTH / 2) * strlen(language[68]), yres / 2 + 40, language[68]);
10819 			ttfPrintTextFormattedColor(ttf16, xres / 2 - (TTF16_WIDTH / 2) * strlen(language[69]), yres / 2 + 60, colorBlue, language[69]);
10820 
10821 			// logo
10822 			src.x = 0;
10823 			src.y = 0;
10824 			src.w = logo_bmp->w;
10825 			src.h = logo_bmp->h;
10826 			dest.x = xres / 2 - (logo_bmp->w) / 2;
10827 			dest.y = yres / 2 + 80;
10828 			dest.w = xres;
10829 			dest.h = yres;
10830 			drawImage(logo_bmp, &src, &dest);
10831 		}
10832 		else if ( creditstage == 13 )
10833 		{
10834 			ttfPrintTextFormatted(ttf16, xres / 2 - (TTF16_WIDTH / 2)*strlen(CREDITSLINE37), yres / 2 - 9, CREDITSLINE37);
10835 			//ttfPrintTextFormattedColor(ttf16,xres/2-(TTF16_WIDTH/2)*strlen(CREDITSLINE37),yres/2+9,colorBlue,CREDITSLINE38);
10836 		}
10837 	}
10838 
10839 	// intro sequence
10840 	if ( intromoviestage > 0 )
10841 	{
10842 		SDL_Rect pos;
10843 		pos.x = 0;
10844 		pos.y = 0;
10845 		pos.w = xres;
10846 		pos.h = (((real_t)xres) / backdrop_cursed_bmp->w) * backdrop_cursed_bmp->h;
10847 		drawImageScaled(backdrop_cursed_bmp, NULL, &pos);
10848 
10849 		if ( intromovietime >= 600 || mousestatus[SDL_BUTTON_LEFT] || keystatus[SDL_SCANCODE_ESCAPE] ||
10850 		        keystatus[SDL_SCANCODE_SPACE] || keystatus[SDL_SCANCODE_RETURN] || (intromovietime >= 120 && intromoviestage == 1) || (*inputPressed(joyimpulses[INJOY_MENU_NEXT]) && rebindaction == -1) )
10851 		{
10852 			intromovietime = 0;
10853 			mousestatus[SDL_BUTTON_LEFT] = 0;
10854 			if ( rebindaction == -1 )
10855 			{
10856 				*inputPressed(joyimpulses[INJOY_MENU_NEXT]) = 0;
10857 			}
10858 			if ( intromoviestage != 9 )
10859 			{
10860 				intromoviestage++;
10861 			}
10862 			else
10863 			{
10864 				introstage = 6;
10865 				fadeout = true;
10866 			}
10867 		}
10868 
10869 		if ( intromoviestage >= 1 )
10870 		{
10871 			intromoviealpha[8] = std::min(intromoviealpha[8] + 2, 255);
10872 			Uint32 color = 0x00FFFFFF;
10873 			color += std::min(std::max(0, intromoviealpha[8]), 255) << 24;
10874 			ttfPrintTextColor(ttf16, 16, yres - 32, color, true, language[1414]);
10875 		}
10876 		if ( intromoviestage >= 2 )
10877 		{
10878 			intromoviealpha[0] = std::min(intromoviealpha[0] + 2, 255);
10879 			Uint32 color = 0x00FFFFFF;
10880 			color += std::min(std::max(0, intromoviealpha[0]), 255) << 24;
10881 			ttfPrintTextColor(ttf16, 16 + (xres - 960) / 2, 16 + (yres - 600) / 2, color, true, language[1415]);
10882 		}
10883 		if ( intromoviestage >= 3 )
10884 		{
10885 			intromoviealpha[1] = std::min(intromoviealpha[1] + 2, 255);
10886 			Uint32 color = 0x00FFFFFF;
10887 			color += std::min(std::max(0, intromoviealpha[1]), 255) << 24;
10888 			ttfPrintTextColor(ttf16, 16 + (xres - 960) / 2, 16 + (yres - 600) / 2, color, true, language[1416]);
10889 		}
10890 		if ( intromoviestage >= 4 )
10891 		{
10892 			intromoviealpha[2] = std::min(intromoviealpha[2] + 2, 255);
10893 			Uint32 color = 0x00FFFFFF;
10894 			color += std::min(std::max(0, intromoviealpha[2]), 255) << 24;
10895 			ttfPrintTextColor(ttf16, 16 + (xres - 960) / 2, 16 + (yres - 600) / 2, color, true, language[1417]);
10896 		}
10897 		if ( intromoviestage >= 5 )
10898 		{
10899 			intromoviealpha[3] = std::min(intromoviealpha[3] + 2, 255);
10900 			Uint32 color = 0x00FFFFFF;
10901 			color += std::min(std::max(0, intromoviealpha[3]), 255) << 24;
10902 			ttfPrintTextColor(ttf16, 16 + (xres - 960) / 2, 16 + (yres - 600) / 2, color, true, language[1418]);
10903 		}
10904 		if ( intromoviestage >= 6 )
10905 		{
10906 			intromoviealpha[4] = std::min(intromoviealpha[4] + 2, 255);
10907 			Uint32 color = 0x00FFFFFF;
10908 			color += std::min(std::max(0, intromoviealpha[4]), 255) << 24;
10909 			ttfPrintTextColor(ttf16, 16 + (xres - 960) / 2, 16 + (yres - 600) / 2, color, true, language[1419]);
10910 		}
10911 		if ( intromoviestage >= 7 )
10912 		{
10913 			intromoviealpha[5] = std::min(intromoviealpha[5] + 2, 255);
10914 			Uint32 color = 0x00FFFFFF;
10915 			color += std::min(std::max(0, intromoviealpha[5]), 255) << 24;
10916 			ttfPrintTextColor(ttf16, 16 + (xres - 960) / 2, 16 + (yres - 600) / 2, color, true, language[1420]);
10917 		}
10918 		if ( intromoviestage >= 8 )
10919 		{
10920 			intromoviealpha[6] = std::min(intromoviealpha[6] + 2, 255);
10921 			Uint32 color = 0x00FFFFFF;
10922 			color += std::min(std::max(0, intromoviealpha[6]), 255) << 24;
10923 			ttfPrintTextColor(ttf16, 16 + (xres - 960) / 2, 16 + (yres - 600) / 2, color, true, language[1421]);
10924 		}
10925 		if ( intromoviestage == 9 )
10926 		{
10927 			intromoviealpha[7] = std::min(intromoviealpha[7] + 2, 255);
10928 			Uint32 color = 0x00FFFFFF;
10929 			color += std::min(std::max(0, intromoviealpha[7]), 255) << 24;
10930 			ttfPrintTextColor(ttf16, 16 + (xres - 960) / 2, 16 + (yres - 600) / 2, color, true, language[1422]);
10931 		}
10932 	}
10933 
10934 	// first end sequence (defeating herx)
10935 	if ( firstendmoviestage > 0 )
10936 	{
10937 		SDL_Rect pos;
10938 		pos.x = 0;
10939 		pos.y = 0;
10940 		pos.w = xres;
10941 		pos.h = (((real_t)xres) / backdrop_minotaur_bmp->w) * backdrop_minotaur_bmp->h;
10942 		drawImageScaled(backdrop_minotaur_bmp, NULL, &pos);
10943 
10944 		if ( firstendmovietime >= 600 || mousestatus[SDL_BUTTON_LEFT] || keystatus[SDL_SCANCODE_ESCAPE] ||
10945 		        keystatus[SDL_SCANCODE_SPACE] || keystatus[SDL_SCANCODE_RETURN] || (firstendmovietime >= 120 && firstendmoviestage == 1) )
10946 		{
10947 			firstendmovietime = 0;
10948 			mousestatus[SDL_BUTTON_LEFT] = 0;
10949 			if ( firstendmoviestage != 5 )
10950 			{
10951 				firstendmoviestage++;
10952 			}
10953 			else
10954 			{
10955 				introstage = 7;
10956 				fadeout = true;
10957 			}
10958 		}
10959 
10960 		if ( firstendmoviestage >= 1 )
10961 		{
10962 			firstendmoviealpha[8] = std::min(firstendmoviealpha[8] + 2, 255);
10963 			Uint32 color = 0x00FFFFFF;
10964 			color += std::min(std::max(0, firstendmoviealpha[8]), 255) << 24;
10965 			ttfPrintTextColor(ttf16, 16, yres - 32, color, true, language[1414]);
10966 
10967 			int titlex = 16;
10968 			int titley = 12;
10969 			// epilogues
10970 			if ( epilogueMultiplayerType == CLIENT )
10971 			{
10972 				if ( strcmp(epilogueHostName, "") )
10973 				{
10974 					ttfPrintTextFormattedColor(ttf16, titlex, titley, color, language[3819], epilogueHostName, language[3821 + epilogueHostRace]); // says who's story type it is.
10975 				}
10976 			}
10977 			else
10978 			{
10979 				ttfPrintTextFormattedColor(ttf16, titlex, titley, color, language[3830], language[3821 + epilogueHostRace]); // says who's story type it is
10980 			}
10981 		}
10982 		if ( firstendmoviestage >= 2 )
10983 		{
10984 			firstendmoviealpha[0] = std::min(firstendmoviealpha[0] + 2, 255);
10985 			Uint32 color = 0x00FFFFFF;
10986 			color += std::min(std::max(0, firstendmoviealpha[0]), 255) << 24;
10987 			ttfPrintTextColor(ttf16, 16 + (xres - 960) / 2, 16 + (yres - 600) / 2, color, true, language[1423]);
10988 		}
10989 		if ( firstendmoviestage >= 3 )
10990 		{
10991 			firstendmoviealpha[1] = std::min(firstendmoviealpha[1] + 2, 255);
10992 			Uint32 color = 0x00FFFFFF;
10993 			color += std::min(std::max(0, firstendmoviealpha[1]), 255) << 24;
10994 			ttfPrintTextColor(ttf16, 16 + (xres - 960) / 2, 16 + (yres - 600) / 2, color, true, language[1424]);
10995 		}
10996 		if ( firstendmoviestage >= 4 )
10997 		{
10998 			firstendmoviealpha[2] = std::min(firstendmoviealpha[2] + 2, 255);
10999 			Uint32 color = 0x00FFFFFF;
11000 			color += std::min(std::max(0, firstendmoviealpha[2]), 255) << 24;
11001 			ttfPrintTextColor(ttf16, 16 + (xres - 960) / 2, 16 + (yres - 600) / 2, color, true, language[1425]);
11002 		}
11003 		if ( firstendmoviestage == 5 )
11004 		{
11005 			firstendmoviealpha[3] = std::min(firstendmoviealpha[3] + 2, 255);
11006 			Uint32 color = 0x00FFFFFF;
11007 			color += std::min(std::max(0, firstendmoviealpha[3]), 255) << 24;
11008 			ttfPrintTextColor(ttf16, 16 + (xres - 960) / 2, 16 + (yres - 600) / 2, color, true, language[1426]);
11009 		}
11010 	}
11011 
11012 	// second end sequence (defeating the devil)
11013 	if ( secondendmoviestage > 0 )
11014 	{
11015 		SDL_Rect pos;
11016 		pos.x = 0;
11017 		pos.y = 0;
11018 		pos.w = xres;
11019 		pos.h = (((real_t)xres) / backdrop_minotaur_bmp->w) * backdrop_minotaur_bmp->h;
11020 		drawImageScaled(backdrop_minotaur_bmp, NULL, &pos);
11021 
11022 		if ( secondendmovietime >= 600 || mousestatus[SDL_BUTTON_LEFT] || keystatus[SDL_SCANCODE_ESCAPE] ||
11023 		        keystatus[SDL_SCANCODE_SPACE] || keystatus[SDL_SCANCODE_RETURN] || (secondendmovietime >= 120 && secondendmoviestage == 1) )
11024 		{
11025 			secondendmovietime = 0;
11026 			mousestatus[SDL_BUTTON_LEFT] = 0;
11027 			if ( secondendmoviestage != 7 )
11028 			{
11029 				secondendmoviestage++;
11030 			}
11031 			else
11032 			{
11033 				introstage = 8;
11034 				fadeout = true;
11035 			}
11036 		}
11037 
11038 		if ( secondendmoviestage >= 1 )
11039 		{
11040 			secondendmoviealpha[8] = std::min(secondendmoviealpha[8] + 2, 255);
11041 			Uint32 color = 0x00FFFFFF;
11042 			color += std::min(std::max(0, secondendmoviealpha[8]), 255) << 24;
11043 			ttfPrintTextColor(ttf16, 16, yres - 32, color, true, language[1414]);
11044 
11045 			int titlex = 16;
11046 			int titley = 12;
11047 			// epilogues
11048 			if ( epilogueMultiplayerType == CLIENT )
11049 			{
11050 				if ( strcmp(epilogueHostName, "") )
11051 				{
11052 					ttfPrintTextFormattedColor(ttf16, titlex, titley, color, language[3819], epilogueHostName, language[3821 + epilogueHostRace]); // says who's story type it is.
11053 				}
11054 			}
11055 			else
11056 			{
11057 				ttfPrintTextFormattedColor(ttf16, titlex, titley, color, language[3830], language[3821 + epilogueHostRace]); // says who's story type it is
11058 			}
11059 		}
11060 		if ( secondendmoviestage >= 2 )
11061 		{
11062 			secondendmoviealpha[0] = std::min(secondendmoviealpha[0] + 2, 255);
11063 			Uint32 color = 0x00FFFFFF;
11064 			color += std::min(std::max(0, secondendmoviealpha[0]), 255) << 24;
11065 			ttfPrintTextColor(ttf16, 16 + (xres - 960) / 2, 16 + (yres - 600) / 22, color, true, language[1427]);
11066 		}
11067 		if ( secondendmoviestage >= 3 )
11068 		{
11069 			secondendmoviealpha[1] = std::min(secondendmoviealpha[1] + 2, 255);
11070 			Uint32 color = 0x00FFFFFF;
11071 			color += std::min(std::max(0, secondendmoviealpha[1]), 255) << 24;
11072 			ttfPrintTextColor(ttf16, 16 + (xres - 960) / 2, 16 + (yres - 600) / 2, color, true, language[1428]);
11073 		}
11074 		if ( secondendmoviestage >= 4 )
11075 		{
11076 			secondendmoviealpha[2] = std::min(secondendmoviealpha[2] + 2, 255);
11077 			Uint32 color = 0x00FFFFFF;
11078 			color += std::min(std::max(0, secondendmoviealpha[2]), 255) << 24;
11079 			ttfPrintTextColor(ttf16, 16 + (xres - 960) / 2, 16 + (yres - 600) / 2, color, true, language[1429]);
11080 		}
11081 		if ( secondendmoviestage >= 5 )
11082 		{
11083 			secondendmoviealpha[3] = std::min(secondendmoviealpha[3] + 2, 255);
11084 			Uint32 color = 0x00FFFFFF;
11085 			color += std::min(std::max(0, secondendmoviealpha[3]), 255) << 24;
11086 			ttfPrintTextColor(ttf16, 16 + (xres - 960) / 2, 16 + (yres - 600) / 2, color, true, language[1430]);
11087 		}
11088 		if ( secondendmoviestage >= 6 )
11089 		{
11090 			secondendmoviealpha[4] = std::min(secondendmoviealpha[4] + 2, 255);
11091 			Uint32 color = 0x00FFFFFF;
11092 			color += std::min(std::max(0, secondendmoviealpha[4]), 255) << 24;
11093 			ttfPrintTextColor(ttf16, 16 + (xres - 960) / 2, 16 + (yres - 600) / 2, color, true, language[1431]);
11094 		}
11095 		if ( secondendmoviestage == 7 )
11096 		{
11097 			secondendmoviealpha[5] = std::min(secondendmoviealpha[5] + 2, 255);
11098 			Uint32 color = 0x00FFFFFF;
11099 			color += std::min(std::max(0, secondendmoviealpha[5]), 255) << 24;
11100 			ttfPrintTextColor(ttf16, 16 + (xres - 960) / 2, 16 + (yres - 600) / 2, color, true, language[1432]);
11101 		}
11102 	}
11103 
11104 	// third end movie stage
11105 	if ( thirdendmoviestage > 0 )
11106 	{
11107 		SDL_Rect pos;
11108 		pos.x = 0;
11109 		pos.y = 0;
11110 		pos.w = xres;
11111 		pos.h = (((real_t)xres) / backdrop_minotaur_bmp->w) * backdrop_minotaur_bmp->h;
11112 		drawRect(&pos, 0, 255);
11113 		drawImageScaled(backdrop_minotaur_bmp, NULL, &pos);
11114 
11115 		if ( thirdendmovietime >= 600 || mousestatus[SDL_BUTTON_LEFT] || keystatus[SDL_SCANCODE_ESCAPE] ||
11116 			keystatus[SDL_SCANCODE_SPACE] || keystatus[SDL_SCANCODE_RETURN] || (thirdendmovietime >= 120 && thirdendmoviestage == 1) )
11117 		{
11118 			thirdendmovietime = 0;
11119 			mousestatus[SDL_BUTTON_LEFT] = 0;
11120 			if ( thirdendmoviestage < thirdEndNumLines )
11121 			{
11122 				thirdendmoviestage++;
11123 			}
11124 			else if ( thirdendmoviestage == thirdEndNumLines )
11125 			{
11126 				if ( multiplayer != CLIENT )
11127 				{
11128 					fadeout = true;
11129 					++thirdendmoviestage;
11130 				}
11131 			}
11132 		}
11133 		Uint32 color = 0x00FFFFFF;
11134 		if ( thirdendmoviestage >= 1 )
11135 		{
11136 			if ( thirdendmoviestage >= 6 )
11137 			{
11138 				thirdendmoviealpha[8] = std::max(thirdendmoviealpha[8] - 4, 0); // click to continue decrease alpha
11139 				if ( thirdendmoviealpha[8] == 0 )
11140 				{
11141 					thirdendmoviealpha[10] = std::min(thirdendmoviealpha[10] + 4, 255);
11142 					color = 0x00FFFFFF;
11143 					color += std::min(std::max(0, thirdendmoviealpha[10]), 255) << 24;
11144 					if ( multiplayer == CLIENT )
11145 					{
11146 						ttfPrintTextColor(ttf16, 16, yres - 32, color, true, language[3833]);
11147 					}
11148 					else
11149 					{
11150 						ttfPrintTextColor(ttf16, 16, yres - 32, color, true, language[3832]);
11151 					}
11152 				}
11153 			}
11154 			else
11155 			{
11156 				thirdendmoviealpha[8] = std::min(thirdendmoviealpha[8] + 2, 255); // click to continue increase alpha
11157 			}
11158 			color = 0x00FFFFFF;
11159 			color += std::min(std::max(0, thirdendmoviealpha[8]), 255) << 24;
11160 			ttfPrintTextColor(ttf16, 16, yres - 32, color, true, language[2606]);
11161 
11162 			if ( stats[0] )
11163 			{
11164 				int titlex = 16;
11165 				int titley = 12;
11166 				int race = RACE_HUMAN;
11167 				if ( stats[0]->playerRace > 0 && stats[0]->appearance == 0 )
11168 				{
11169 					race = stats[0]->playerRace;
11170 				}
11171 				// interludes
11172 				if ( multiplayer == CLIENT )
11173 				{
11174 					if ( strcmp(stats[0]->name, "") )
11175 					{
11176 						ttfPrintTextFormattedColor(ttf16, titlex, titley, color, language[3820], stats[0]->name, language[3821 + race]); // says who's story type it is.
11177 					}
11178 				}
11179 				else
11180 				{
11181 					ttfPrintTextFormattedColor(ttf16, titlex, titley, color, language[3831], language[3821 + race]); // says who's story type it is.
11182 				}
11183 			}
11184 		}
11185 		if ( thirdendmoviestage >= 2 )
11186 		{
11187 			thirdendmoviealpha[0] = std::min(thirdendmoviealpha[0] + 2, 255);
11188 			color = 0x00FFFFFF;
11189 			color += std::min(std::max(0, thirdendmoviealpha[0]), 255) << 24;
11190 			ttfPrintTextColor(ttf16, 16 + (xres - 960) / 2, 16 + (yres - 600) / 2, color, true, language[2600]);
11191 		}
11192 		if ( thirdendmoviestage >= 3 )
11193 		{
11194 			thirdendmoviealpha[1] = std::min(thirdendmoviealpha[1] + 2, 255);
11195 			color = 0x00FFFFFF;
11196 			color += std::min(std::max(0, thirdendmoviealpha[1]), 255) << 24;
11197 			ttfPrintTextColor(ttf16, 16 + (xres - 960) / 2, 16 + (yres - 600) / 2, color, true, language[2601]);
11198 		}
11199 		if ( thirdendmoviestage >= 4 )
11200 		{
11201 			thirdendmoviealpha[2] = std::min(thirdendmoviealpha[2] + 2, 255);
11202 			color = 0x00FFFFFF;
11203 			color += std::min(std::max(0, thirdendmoviealpha[2]), 255) << 24;
11204 			ttfPrintTextColor(ttf16, 16 + (xres - 960) / 2, 16 + (yres - 600) / 2, color, true, language[2602]);
11205 		}
11206 		if ( thirdendmoviestage >= 5 )
11207 		{
11208 			thirdendmoviealpha[3] = std::min(thirdendmoviealpha[3] + 2, 255);
11209 			color = 0x00FFFFFF;
11210 			color += std::min(std::max(0, thirdendmoviealpha[3]), 255) << 24;
11211 			ttfPrintTextColor(ttf16, 16 + (xres - 960) / 2, 16 + (yres - 600) / 2, color, true, language[2603]);
11212 		}
11213 	}
11214 	// fourth (expansion) end movie stage
11215 	if ( fourthendmoviestage > 0 )
11216 	{
11217 		SDL_Rect pos;
11218 		pos.x = 0;
11219 		pos.y = 0;
11220 		pos.w = xres;
11221 		pos.h = (((real_t)xres) / backdrop_blessed_bmp->w) * backdrop_blessed_bmp->h;
11222 		drawRect(&pos, 0, 255);
11223 		drawImageScaled(backdrop_blessed_bmp, NULL, &pos);
11224 
11225 		if ( fourthendmovietime >= 600
11226 			|| (mousestatus[SDL_BUTTON_LEFT]
11227 				&& fourthendmoviestage < 10
11228 				&& fourthendmoviestage != 10
11229 				&& fourthendmoviestage != 5
11230 				&& fourthendmoviestage != 1)
11231 			|| (fourthendmovietime >= 120 && fourthendmoviestage == 1)
11232 			|| (fourthendmovietime >= 60 && fourthendmoviestage == 5)
11233 			|| (fourthendmovietime >= 240 && fourthendmoviestage == 10)
11234 			|| (fourthendmovietime >= 200 && fourthendmoviestage == 11)
11235 			|| (fourthendmovietime >= 60 && fourthendmoviestage == 12)
11236 			|| (fourthendmovietime >= 400 && fourthendmoviestage == 13)
11237 			)
11238 		{
11239 			fourthendmovietime = 0;
11240 			mousestatus[SDL_BUTTON_LEFT] = 0;
11241 			if ( fourthendmoviestage < fourthEndNumLines )
11242 			{
11243 				fourthendmoviestage++;
11244 			}
11245 			else if ( fourthendmoviestage == fourthEndNumLines )
11246 			{
11247 				fadeout = true;
11248 				introstage = 10;
11249 			}
11250 		}
11251 		Uint32 color = 0x00FFFFFF;
11252 		if ( fourthendmoviestage >= 1 )
11253 		{
11254 			if ( fourthendmoviestage >= 10 )
11255 			{
11256 				fourthendmoviealpha[8] = std::max(fourthendmoviealpha[8] - 2, 0);
11257 			}
11258 			else
11259 			{
11260 				fourthendmoviealpha[8] = std::min(fourthendmoviealpha[8] + 2, 255);
11261 			}
11262 			color = 0x00FFFFFF;
11263 			color += std::min(std::max(0, fourthendmoviealpha[8]), 255) << 24;
11264 			ttfPrintTextColor(ttf16, 16, yres - 32, color, true, language[2606]);
11265 
11266 			if ( stats[0] )
11267 			{
11268 				color = 0x00FFFFFF;
11269 				if ( fourthendmoviestage >= 10 )
11270 				{
11271 					fourthendmoviealpha[9] = std::max(fourthendmoviealpha[9] - 2, 0);
11272 				}
11273 				else
11274 				{
11275 					fourthendmoviealpha[9] = std::min(fourthendmoviealpha[9] + 2, 255);
11276 				}
11277 				color += std::min(std::max(0, fourthendmoviealpha[9]), 255) << 24;
11278 				int titlex = 16;
11279 				int titley = 12;
11280 				if ( epilogueMultiplayerType == CLIENT )
11281 				{
11282 					if ( strcmp(epilogueHostName, "") )
11283 					{
11284 						ttfPrintTextFormattedColor(ttf16, titlex, titley, color, language[3819], epilogueHostName, language[3821 + epilogueHostRace]); // says who's story type it is.
11285 					}
11286 				}
11287 				else
11288 				{
11289 					ttfPrintTextFormattedColor(ttf16, titlex, titley, color, language[3830], language[3821 + epilogueHostRace]); // singleplayer story
11290 				}
11291 			}
11292 		}
11293 		if ( fourthendmoviestage >= 2 )
11294 		{
11295 			if ( fourthendmoviestage < 5 )
11296 			{
11297 				fourthendmoviealpha[0] = std::min(fourthendmoviealpha[0] + 2, 255);
11298 			}
11299 			color = 0x00FFFFFF;
11300 			color += std::min(std::max(0, fourthendmoviealpha[0]), 255) << 24;
11301 			ttfPrintTextColor(ttf16, 16 + (xres - 960) / 2, 16 + (yres - 600) / 2, color, true, language[2607]);
11302 		}
11303 		if ( fourthendmoviestage >= 3 )
11304 		{
11305 			if ( fourthendmoviestage < 5 )
11306 			{
11307 				fourthendmoviealpha[1] = std::min(fourthendmoviealpha[1] + 2, 255);
11308 			}
11309 			color = 0x00FFFFFF;
11310 			color += std::min(std::max(0, fourthendmoviealpha[1]), 255) << 24;
11311 			ttfPrintTextColor(ttf16, 16 + (xres - 960) / 2, 16 + (yres - 600) / 2, color, true, language[2608]);
11312 		}
11313 		if ( fourthendmoviestage >= 4 )
11314 		{
11315 			if ( fourthendmoviestage < 5 )
11316 			{
11317 				fourthendmoviealpha[2] = std::min(fourthendmoviealpha[2] + 2, 255);
11318 			}
11319 			color = 0x00FFFFFF;
11320 			color += std::min(std::max(0, fourthendmoviealpha[2]), 255) << 24;
11321 			ttfPrintTextColor(ttf16, 16 + (xres - 960) / 2, 16 + (yres - 600) / 2, color, true, language[2609]);
11322 		}
11323 		if ( fourthendmoviestage >= 5 )
11324 		{
11325 			fourthendmoviealpha[0] = std::max(fourthendmoviealpha[2] - 2, 0);
11326 			fourthendmoviealpha[1] = std::max(fourthendmoviealpha[2] - 2, 0);
11327 			fourthendmoviealpha[2] = std::max(fourthendmoviealpha[2] - 2, 0);
11328 		}
11329 		if ( fourthendmoviestage >= 6 )
11330 		{
11331 			if ( fourthendmoviestage < 10 )
11332 			{
11333 				fourthendmoviealpha[3] = std::min(fourthendmoviealpha[3] + 2, 255);
11334 			}
11335 			color = 0x00FFFFFF;
11336 			color += std::min(std::max(0, fourthendmoviealpha[3]), 255) << 24;
11337 			ttfPrintTextColor(ttf16, 16 + (xres - 960) / 2, 16 + (yres - 600) / 2, color, true, language[2610]);
11338 		}
11339 		if ( fourthendmoviestage >= 7 )
11340 		{
11341 			if ( fourthendmoviestage < 10 )
11342 			{
11343 				fourthendmoviealpha[4] = std::min(fourthendmoviealpha[4] + 2, 255);
11344 			}
11345 			color = 0x00FFFFFF;
11346 			color += std::min(std::max(0, fourthendmoviealpha[4]), 255) << 24;
11347 			ttfPrintTextColor(ttf16, 16 + (xres - 960) / 2, 16 + (yres - 600) / 2, color, true, language[2611]);
11348 		}
11349 		if ( fourthendmoviestage >= 8 )
11350 		{
11351 			if ( fourthendmoviestage < 10 )
11352 			{
11353 				fourthendmoviealpha[5] = std::min(fourthendmoviealpha[5] + 2, 255);
11354 			}
11355 			color = 0x00FFFFFF;
11356 			color += std::min(std::max(0, fourthendmoviealpha[5]), 255) << 24;
11357 			ttfPrintTextColor(ttf16, 16 + (xres - 960) / 2, 16 + (yres - 600) / 2, color, true, language[2612]);
11358 		}
11359 		if ( fourthendmoviestage >= 9 )
11360 		{
11361 			if ( fourthendmoviestage < 10 )
11362 			{
11363 				fourthendmoviealpha[6] = std::min(fourthendmoviealpha[6] + 2, 255);
11364 			}
11365 			color = 0x00FFFFFF;
11366 			color += std::min(std::max(0, fourthendmoviealpha[6]), 255) << 24;
11367 			ttfPrintTextColor(ttf16, 16 + (xres - 960) / 2, 16 + (yres - 600) / 2, color, true, language[2613]);
11368 		}
11369 		if ( fourthendmoviestage >= 10 )
11370 		{
11371 			fourthendmoviealpha[3] = std::max(fourthendmoviealpha[3] - 2, 0);
11372 			fourthendmoviealpha[4] = std::max(fourthendmoviealpha[4] - 2, 0);
11373 			fourthendmoviealpha[5] = std::max(fourthendmoviealpha[5] - 2, 0);
11374 			fourthendmoviealpha[6] = std::max(fourthendmoviealpha[6] - 2, 0);
11375 		}
11376 		if ( fourthendmoviestage >= 11 )
11377 		{
11378 			fourthendmoviealpha[7] = std::min(fourthendmoviealpha[7] + 2, 255);
11379 			color = 0x00FFFFFF;
11380 			color += std::min(std::max(0, fourthendmoviealpha[7]), 255) << 24;
11381 			ttfPrintTextColor(ttf16, 16 + (xres/ 2) - 256, (yres / 2) - 64, color, true, language[2614]);
11382 			if ( fourthendmovietime % 50 == 0 )
11383 			{
11384 				steamAchievement("BARONY_ACH_ALWAYS_WAITING");
11385 			}
11386 		}
11387 		if ( fourthendmoviestage >= 13 )
11388 		{
11389 			fadealpha = std::min(fadealpha + 2, 255);
11390 		}
11391 	}
11392 
11393 	// new end movie stage
11394 	int movieType = -1;
11395 	for ( int i = 0; i < 8; ++i )
11396 	{
11397 		if ( DLCendmovieStageAndTime[i][MOVIE_STAGE] > 0 )
11398 		{
11399 			movieType = i;
11400 			break;
11401 		}
11402 	}
11403 	if ( movieType >= MOVIE_MIDGAME_HERX_MONSTERS && movieType <= MOVIE_CLASSIC_WIN_BAPHOMET_MONSTERS )
11404 	{
11405 		SDL_Rect pos;
11406 		pos.x = 0;
11407 		pos.y = 0;
11408 		pos.w = xres;
11409 		pos.h = (((real_t)xres) / backdrop_minotaur_bmp->w) * backdrop_minotaur_bmp->h;
11410 		drawRect(&pos, 0, 255);
11411 		drawImageScaled(backdrop_minotaur_bmp, NULL, &pos);
11412 
11413 		if ( DLCendmovieStageAndTime[movieType][MOVIE_TIME] >= 600 || mousestatus[SDL_BUTTON_LEFT] || keystatus[SDL_SCANCODE_ESCAPE] ||
11414 			keystatus[SDL_SCANCODE_SPACE] || keystatus[SDL_SCANCODE_RETURN] || (DLCendmovieStageAndTime[movieType][MOVIE_TIME] >= 120 && DLCendmovieStageAndTime[movieType][MOVIE_STAGE] == 1) )
11415 		{
11416 			DLCendmovieStageAndTime[movieType][MOVIE_TIME] = 0;
11417 			mousestatus[SDL_BUTTON_LEFT] = 0;
11418 			if ( DLCendmovieStageAndTime[movieType][MOVIE_STAGE] < DLCendmovieNumLines[movieType] )
11419 			{
11420 				DLCendmovieStageAndTime[movieType][MOVIE_STAGE]++;
11421 			}
11422 			else if ( DLCendmovieStageAndTime[movieType][MOVIE_STAGE] == DLCendmovieNumLines[movieType] )
11423 			{
11424 				if ( movieType == MOVIE_MIDGAME_BAPHOMET_HUMAN_AUTOMATON
11425 					|| movieType == MOVIE_MIDGAME_BAPHOMET_MONSTERS
11426 					|| movieType == MOVIE_MIDGAME_HERX_MONSTERS )
11427 				{
11428 					// midgame - clients pause until host continues.
11429 					if ( multiplayer != CLIENT )
11430 					{
11431 						fadeout = true;
11432 						++DLCendmovieStageAndTime[movieType][MOVIE_STAGE];
11433 					}
11434 				}
11435 				else
11436 				{
11437 					// endgame - fade and return to credits.
11438 					if ( movieType == MOVIE_CLASSIC_WIN_BAPHOMET_MONSTERS
11439 						|| movieType == MOVIE_CLASSIC_WIN_MONSTERS )
11440 					{
11441 						// classic mode end.
11442 						introstage = 11 + movieType;
11443 					}
11444 					fadeout = true;
11445 				}
11446 			}
11447 		}
11448 
11449 		std::vector<char*> langEntries;
11450 		switch ( movieType )
11451 		{
11452 			case MOVIE_MIDGAME_HERX_MONSTERS:
11453 				langEntries.push_back(language[3771]);
11454 				langEntries.push_back(language[3772]);
11455 				langEntries.push_back(language[3773]);
11456 				langEntries.push_back(language[3774]);
11457 				langEntries.push_back(language[3775]);
11458 				break;
11459 			case MOVIE_MIDGAME_BAPHOMET_MONSTERS:
11460 				langEntries.push_back(language[3776]);
11461 				langEntries.push_back(language[3777]);
11462 				langEntries.push_back(language[3778]);
11463 				langEntries.push_back(language[3779]);
11464 				langEntries.push_back(language[3780]);
11465 				langEntries.push_back(language[3781]);
11466 				break;
11467 			case MOVIE_MIDGAME_BAPHOMET_HUMAN_AUTOMATON:
11468 				langEntries.push_back(language[3782]);
11469 				langEntries.push_back(language[3783]);
11470 				langEntries.push_back(language[3784]);
11471 				langEntries.push_back(language[3785]);
11472 				langEntries.push_back(language[3786]);
11473 				langEntries.push_back(language[3787]);
11474 				break;
11475 			case MOVIE_CLASSIC_WIN_MONSTERS:
11476 				langEntries.push_back(language[3788]);
11477 				langEntries.push_back(language[3789]);
11478 				langEntries.push_back(language[3790]);
11479 				langEntries.push_back(language[3791]);
11480 				break;
11481 			case MOVIE_CLASSIC_WIN_BAPHOMET_MONSTERS:
11482 				langEntries.push_back(language[3792]);
11483 				langEntries.push_back(language[3793]);
11484 				langEntries.push_back(language[3794]);
11485 				langEntries.push_back(language[3795]);
11486 				langEntries.push_back(language[3796]);
11487 				langEntries.push_back(language[3797]);
11488 				break;
11489 			default:
11490 				break;
11491 		}
11492 
11493 		Uint32 color = 0x00FFFFFF;
11494 		if ( DLCendmovieStageAndTime[movieType][MOVIE_STAGE] >= 1 )
11495 		{
11496 			if ( !(movieType == MOVIE_CLASSIC_WIN_BAPHOMET_MONSTERS
11497 				|| movieType == MOVIE_CLASSIC_WIN_MONSTERS) )
11498 			{
11499 				// only do this on interludes, not intermission.
11500 				if ( DLCendmovieStageAndTime[movieType][MOVIE_STAGE] >= DLCendmovieNumLines[movieType] )
11501 				{
11502 					DLCendmoviealpha[movieType][8] = std::max(DLCendmoviealpha[movieType][8] - 4, 0); // click to continue decrease alpha
11503 					if ( DLCendmoviealpha[movieType][8] == 0 )
11504 					{
11505 						DLCendmoviealpha[movieType][10] = std::min(DLCendmoviealpha[movieType][10] + 4, 255);
11506 						color = 0x00FFFFFF;
11507 						color += std::min(std::max(0, DLCendmoviealpha[movieType][10]), 255) << 24;
11508 						if ( multiplayer == CLIENT )
11509 						{
11510 							ttfPrintTextColor(ttf16, 16, yres - 32, color, true, language[3833]);
11511 						}
11512 						else
11513 						{
11514 							ttfPrintTextColor(ttf16, 16, yres - 32, color, true, language[3832]);
11515 						}
11516 					}
11517 				}
11518 				else
11519 				{
11520 					DLCendmoviealpha[movieType][8] = std::min(DLCendmoviealpha[movieType][8] + 2, 255); // click to continue increase alpha
11521 				}
11522 			}
11523 			else
11524 			{
11525 				DLCendmoviealpha[movieType][8] = std::min(DLCendmoviealpha[movieType][8] + 2, 255); // click to continue increase alpha
11526 			}
11527 
11528 			color = 0x00FFFFFF;
11529 			color += std::min(std::max(0, DLCendmoviealpha[movieType][8]), 255) << 24;
11530 			ttfPrintTextColor(ttf16, 16, yres - 32, color, true, language[2606]); // click to continue
11531 
11532 			if ( stats[0] )
11533 			{
11534 				int titlex = 16;
11535 				int titley = 12;
11536 
11537 				if ( movieType == MOVIE_CLASSIC_WIN_BAPHOMET_MONSTERS
11538 					|| movieType == MOVIE_CLASSIC_WIN_MONSTERS )
11539 				{
11540 					// epilogues
11541 					if ( epilogueMultiplayerType == CLIENT )
11542 					{
11543 						if ( strcmp(epilogueHostName, "") )
11544 						{
11545 							ttfPrintTextFormattedColor(ttf16, titlex, titley, color, language[3819], epilogueHostName, language[3821 + epilogueHostRace]); // says who's story type it is.
11546 						}
11547 					}
11548 					else
11549 					{
11550 						ttfPrintTextFormattedColor(ttf16, titlex, titley, color, language[3830], language[3821 + epilogueHostRace]); // says who's story type it is
11551 					}
11552 				}
11553 				else
11554 				{
11555 					int race = RACE_HUMAN;
11556 					if ( stats[0]->playerRace > 0 && stats[0]->appearance == 0 )
11557 					{
11558 						race = stats[0]->playerRace;
11559 					}
11560 					// interludes
11561 					if ( multiplayer == CLIENT )
11562 					{
11563 						if ( strcmp(stats[0]->name, "") )
11564 						{
11565 							ttfPrintTextFormattedColor(ttf16, titlex, titley, color, language[3820], stats[0]->name, language[3821 + race]); // says who's story type it is.
11566 						}
11567 					}
11568 					else
11569 					{
11570 						ttfPrintTextFormattedColor(ttf16, titlex, titley, color, language[3831], language[3821 + race]); // says who's story type it is.
11571 					}
11572 				}
11573 			}
11574 		}
11575 
11576 		int index = 0;
11577 		for ( index = 0; index < DLCendmovieNumLines[movieType]; ++index )
11578 		{
11579 			if ( DLCendmovieStageAndTime[movieType][MOVIE_STAGE] >= index + 2 && langEntries.size() > index )
11580 			{
11581 				DLCendmoviealpha[movieType][index] = std::min(DLCendmoviealpha[movieType][index] + 2, 255);
11582 				color = 0x00FFFFFF;
11583 				color += std::min(std::max(0, DLCendmoviealpha[movieType][index]), 255) << 24;
11584 				ttfPrintTextColor(ttf16, 16 + (xres - 960) / 2, 16 + (yres - 600) / 2, color, true, langEntries.at(index));
11585 			}
11586 		}
11587 	}
11588 	else if ( movieType > MOVIE_CLASSIC_WIN_BAPHOMET_MONSTERS )
11589 	{
11590 		SDL_Rect pos;
11591 		pos.x = 0;
11592 		pos.y = 0;
11593 		pos.w = xres;
11594 		pos.h = (((real_t)xres) / backdrop_blessed_bmp->w) * backdrop_blessed_bmp->h;
11595 		drawRect(&pos, 0, 255);
11596 		drawImageScaled(backdrop_blessed_bmp, NULL, &pos);
11597 
11598 		if ( DLCendmovieStageAndTime[movieType][MOVIE_TIME] >= 600
11599 			|| (mousestatus[SDL_BUTTON_LEFT]
11600 				&& DLCendmovieStageAndTime[movieType][MOVIE_STAGE] < 10
11601 				&& DLCendmovieStageAndTime[movieType][MOVIE_STAGE] != 10
11602 				&& DLCendmovieStageAndTime[movieType][MOVIE_STAGE] != 5
11603 				&& DLCendmovieStageAndTime[movieType][MOVIE_STAGE] != 1)
11604 			|| (DLCendmovieStageAndTime[movieType][MOVIE_TIME] >= 120 && DLCendmovieStageAndTime[movieType][MOVIE_STAGE] == 1)
11605 			|| (DLCendmovieStageAndTime[movieType][MOVIE_TIME] >= 60 && DLCendmovieStageAndTime[movieType][MOVIE_STAGE] == 5)
11606 			|| (DLCendmovieStageAndTime[movieType][MOVIE_TIME] >= 240 && DLCendmovieStageAndTime[movieType][MOVIE_STAGE] == 10)
11607 			|| (DLCendmovieStageAndTime[movieType][MOVIE_TIME] >= 200 && DLCendmovieStageAndTime[movieType][MOVIE_STAGE] == 11)
11608 			|| (DLCendmovieStageAndTime[movieType][MOVIE_TIME] >= 60 && DLCendmovieStageAndTime[movieType][MOVIE_STAGE] == 12)
11609 			|| (DLCendmovieStageAndTime[movieType][MOVIE_TIME] >= 400 && DLCendmovieStageAndTime[movieType][MOVIE_STAGE] == 13)
11610 			)
11611 		{
11612 			DLCendmovieStageAndTime[movieType][MOVIE_TIME] = 0;
11613 			mousestatus[SDL_BUTTON_LEFT] = 0;
11614 			if ( DLCendmovieStageAndTime[movieType][MOVIE_STAGE] < DLCendmovieNumLines[movieType] )
11615 			{
11616 				DLCendmovieStageAndTime[movieType][MOVIE_STAGE]++;
11617 			}
11618 			else if ( DLCendmovieStageAndTime[movieType][MOVIE_STAGE] == DLCendmovieNumLines[movieType] )
11619 			{
11620 				fadeout = true;
11621 				introstage = 11 + movieType;
11622 			}
11623 		}
11624 
11625 		std::vector<char*> langEntries;
11626 		switch ( movieType )
11627 		{
11628 			case MOVIE_WIN_AUTOMATON:
11629 				langEntries.push_back(language[3798]);
11630 				langEntries.push_back(language[3799]);
11631 				langEntries.push_back(language[3800]);
11632 				langEntries.push_back(language[3801]);
11633 				langEntries.push_back(language[3802]);
11634 				langEntries.push_back(language[3803]);
11635 				langEntries.push_back(language[3804]);
11636 				break;
11637 			case MOVIE_WIN_DEMONS_UNDEAD:
11638 				langEntries.push_back(language[3805]);
11639 				langEntries.push_back(language[3806]);
11640 				langEntries.push_back(language[3807]);
11641 				langEntries.push_back(language[3808]);
11642 				langEntries.push_back(language[3809]);
11643 				langEntries.push_back(language[3810]);
11644 				langEntries.push_back(language[3811]);
11645 				break;
11646 			case MOVIE_WIN_BEASTS:
11647 				langEntries.push_back(language[3812]);
11648 				langEntries.push_back(language[3813]);
11649 				langEntries.push_back(language[3814]);
11650 				langEntries.push_back(language[3815]);
11651 				langEntries.push_back(language[3816]);
11652 				langEntries.push_back(language[3817]);
11653 				langEntries.push_back(language[3818]);
11654 				break;
11655 			default:
11656 				break;
11657 		}
11658 
11659 		Uint32 color = 0x00FFFFFF;
11660 		if ( DLCendmovieStageAndTime[movieType][MOVIE_STAGE] >= 1 )
11661 		{
11662 			if ( DLCendmovieStageAndTime[movieType][MOVIE_STAGE] >= 10 )
11663 			{
11664 				DLCendmoviealpha[movieType][8] = std::max(DLCendmoviealpha[movieType][8] - 2, 0);
11665 			}
11666 			else
11667 			{
11668 				DLCendmoviealpha[movieType][8] = std::min(DLCendmoviealpha[movieType][8] + 2, 255);
11669 			}
11670 			color = 0x00FFFFFF;
11671 			color += std::min(std::max(0, DLCendmoviealpha[movieType][8]), 255) << 24;
11672 			ttfPrintTextColor(ttf16, 16, yres - 32, color, true, language[2606]); // click to continue.
11673 
11674 			if ( stats[0] )
11675 			{
11676 				color = 0x00FFFFFF;
11677 				if ( DLCendmovieStageAndTime[movieType][MOVIE_STAGE] >= 10 )
11678 				{
11679 					DLCendmoviealpha[movieType][9] = std::max(DLCendmoviealpha[movieType][9] - 2, 0);
11680 				}
11681 				else
11682 				{
11683 					DLCendmoviealpha[movieType][9] = std::min(DLCendmoviealpha[movieType][9] + 2, 255);
11684 				}
11685 				color += std::min(std::max(0, DLCendmoviealpha[movieType][9]), 255) << 24;
11686 				int titlex = 16;
11687 				int titley = 12;
11688 				if ( epilogueMultiplayerType == CLIENT )
11689 				{
11690 					if ( strcmp(epilogueHostName, "") )
11691 					{
11692 						ttfPrintTextFormattedColor(ttf16, titlex, titley, color, language[3819], epilogueHostName, language[3821 + epilogueHostRace]); // says who's story type it is.
11693 					}
11694 				}
11695 				else
11696 				{
11697 					ttfPrintTextFormattedColor(ttf16, titlex, titley, color, language[3830], language[3821 + epilogueHostRace]); // singleplayer story
11698 				}
11699 			}
11700 		}
11701 		if ( DLCendmovieStageAndTime[movieType][MOVIE_STAGE] >= 2 )
11702 		{
11703 			if ( DLCendmovieStageAndTime[movieType][MOVIE_STAGE] < 5 )
11704 			{
11705 				DLCendmoviealpha[movieType][0] = std::min(DLCendmoviealpha[movieType][0] + 2, 255);
11706 			}
11707 			color = 0x00FFFFFF;
11708 			color += std::min(std::max(0, DLCendmoviealpha[movieType][0]), 255) << 24;
11709 			ttfPrintTextColor(ttf16, 16 + (xres - 960) / 2, 16 + (yres - 600) / 2, color, true, langEntries.at(0));
11710 		}
11711 		if ( DLCendmovieStageAndTime[movieType][MOVIE_STAGE] >= 3 )
11712 		{
11713 			if ( DLCendmovieStageAndTime[movieType][MOVIE_STAGE] < 5 )
11714 			{
11715 				DLCendmoviealpha[movieType][1] = std::min(DLCendmoviealpha[movieType][1] + 2, 255);
11716 			}
11717 			color = 0x00FFFFFF;
11718 			color += std::min(std::max(0, DLCendmoviealpha[movieType][1]), 255) << 24;
11719 			ttfPrintTextColor(ttf16, 16 + (xres - 960) / 2, 16 + (yres - 600) / 2, color, true, langEntries.at(1));
11720 		}
11721 		if ( DLCendmovieStageAndTime[movieType][MOVIE_STAGE] >= 4 )
11722 		{
11723 			if ( DLCendmovieStageAndTime[movieType][MOVIE_STAGE] < 5 )
11724 			{
11725 				DLCendmoviealpha[movieType][2] = std::min(DLCendmoviealpha[movieType][2] + 2, 255);
11726 			}
11727 			color = 0x00FFFFFF;
11728 			color += std::min(std::max(0, DLCendmoviealpha[movieType][2]), 255) << 24;
11729 			ttfPrintTextColor(ttf16, 16 + (xres - 960) / 2, 16 + (yres - 600) / 2, color, true, langEntries.at(2));
11730 		}
11731 		if ( DLCendmovieStageAndTime[movieType][MOVIE_STAGE] >= 5 )
11732 		{
11733 			DLCendmoviealpha[movieType][0] = std::max(DLCendmoviealpha[movieType][2] - 2, 0);
11734 			DLCendmoviealpha[movieType][1] = std::max(DLCendmoviealpha[movieType][2] - 2, 0);
11735 			DLCendmoviealpha[movieType][2] = std::max(DLCendmoviealpha[movieType][2] - 2, 0);
11736 		}
11737 		if ( DLCendmovieStageAndTime[movieType][MOVIE_STAGE] >= 6 )
11738 		{
11739 			if ( DLCendmovieStageAndTime[movieType][MOVIE_STAGE] < 10 )
11740 			{
11741 				DLCendmoviealpha[movieType][3] = std::min(DLCendmoviealpha[movieType][3] + 2, 255);
11742 			}
11743 			color = 0x00FFFFFF;
11744 			color += std::min(std::max(0, DLCendmoviealpha[movieType][3]), 255) << 24;
11745 			ttfPrintTextColor(ttf16, 16 + (xres - 960) / 2, 16 + (yres - 600) / 2, color, true, langEntries.at(3));
11746 		}
11747 		if ( DLCendmovieStageAndTime[movieType][MOVIE_STAGE] >= 7 )
11748 		{
11749 			if ( DLCendmovieStageAndTime[movieType][MOVIE_STAGE] < 10 )
11750 			{
11751 				DLCendmoviealpha[movieType][4] = std::min(DLCendmoviealpha[movieType][4] + 2, 255);
11752 			}
11753 			color = 0x00FFFFFF;
11754 			color += std::min(std::max(0, DLCendmoviealpha[movieType][4]), 255) << 24;
11755 			ttfPrintTextColor(ttf16, 16 + (xres - 960) / 2, 16 + (yres - 600) / 2, color, true, langEntries.at(4));
11756 		}
11757 		if ( DLCendmovieStageAndTime[movieType][MOVIE_STAGE] >= 8 )
11758 		{
11759 			if ( DLCendmovieStageAndTime[movieType][MOVIE_STAGE] < 10 )
11760 			{
11761 				DLCendmoviealpha[movieType][5] = std::min(DLCendmoviealpha[movieType][5] + 2, 255);
11762 			}
11763 			color = 0x00FFFFFF;
11764 			color += std::min(std::max(0, DLCendmoviealpha[movieType][5]), 255) << 24;
11765 			ttfPrintTextColor(ttf16, 16 + (xres - 960) / 2, 16 + (yres - 600) / 2, color, true, langEntries.at(5));
11766 		}
11767 		if ( DLCendmovieStageAndTime[movieType][MOVIE_STAGE] >= 9 )
11768 		{
11769 			if ( DLCendmovieStageAndTime[movieType][MOVIE_STAGE] < 10 )
11770 			{
11771 				DLCendmoviealpha[movieType][6] = std::min(DLCendmoviealpha[movieType][6] + 2, 255);
11772 			}
11773 			color = 0x00FFFFFF;
11774 			color += std::min(std::max(0, DLCendmoviealpha[movieType][6]), 255) << 24;
11775 			ttfPrintTextColor(ttf16, 16 + (xres - 960) / 2, 16 + (yres - 600) / 2, color, true, langEntries.at(6));
11776 		}
11777 		if ( DLCendmovieStageAndTime[movieType][MOVIE_STAGE] >= 10 )
11778 		{
11779 			DLCendmoviealpha[movieType][3] = std::max(DLCendmoviealpha[movieType][3] - 2, 0);
11780 			DLCendmoviealpha[movieType][4] = std::max(DLCendmoviealpha[movieType][4] - 2, 0);
11781 			DLCendmoviealpha[movieType][5] = std::max(DLCendmoviealpha[movieType][5] - 2, 0);
11782 			DLCendmoviealpha[movieType][6] = std::max(DLCendmoviealpha[movieType][6] - 2, 0);
11783 		}
11784 		if ( DLCendmovieStageAndTime[movieType][MOVIE_STAGE] >= 11 )
11785 		{
11786 			DLCendmoviealpha[movieType][7] = std::min(DLCendmoviealpha[movieType][7] + 2, 255);
11787 			color = 0x00FFFFFF;
11788 			color += std::min(std::max(0, DLCendmoviealpha[movieType][7]), 255) << 24;
11789 			ttfPrintTextColor(ttf16, 16 + (xres / 2) - 256, (yres / 2) - 64, color, true, language[2614]);
11790 			if ( DLCendmovieStageAndTime[movieType][MOVIE_TIME] % 50 == 0 )
11791 			{
11792 				steamAchievement("BARONY_ACH_ALWAYS_WAITING");
11793 			}
11794 		}
11795 		if ( DLCendmovieStageAndTime[movieType][MOVIE_STAGE] >= 13 )
11796 		{
11797 			fadealpha = std::min(fadealpha + 2, 255);
11798 		}
11799 	}
11800 }
11801 
11802 /*-------------------------------------------------------------------------------
11803 
11804 	button functions
11805 
11806 	this section contains numerous button functions for the game
11807 
11808 -------------------------------------------------------------------------------*/
11809 
11810 // opens the gameover window
openGameoverWindow()11811 void openGameoverWindow()
11812 {
11813 	node_t* node;
11814 	buttonCloseSubwindow(nullptr);
11815 	list_FreeAll(&button_l);
11816 	deleteallbuttons = true;
11817 
11818 	subwindow = 1;
11819 	subx1 = xres / 2 - 288;
11820 	subx2 = xres / 2 + 288;
11821 	suby1 = yres / 2 - 160;
11822 	suby2 = yres / 2 + 160;
11823 	button_t* button;
11824 
11825 	// calculate player score
11826 	char scorenum[16];
11827 	score_t* score = scoreConstructor();
11828 	Uint32 total = totalScore(score);
11829 	snprintf(scorenum, 16, "%d\n\n", total);
11830 
11831 	bool madetop = false;
11832 	list_t* scoresPtr = &topscores;
11833 	if ( score->conductGameChallenges[CONDUCT_MULTIPLAYER] )
11834 	{
11835 		scoresPtr = &topscoresMultiplayer;
11836 	}
11837 	if ( !list_Size(scoresPtr) )
11838 	{
11839 		madetop = true;
11840 	}
11841 	else if ( list_Size(scoresPtr) < MAXTOPSCORES )
11842 	{
11843 		madetop = true;
11844 	}
11845 	else if ( totalScore((score_t*)scoresPtr->last->element) < total )
11846 	{
11847 		madetop = true;
11848 	}
11849 
11850 	scoreDeconstructor((void*)score);
11851 
11852 	shootmode = false;
11853 	if ( multiplayer == SINGLE )
11854 	{
11855 		strcpy(subtext, language[1133]);
11856 
11857 		strcat(subtext, language[1134]);
11858 
11859 		strcat(subtext, language[1135]);
11860 		strcat(subtext, scorenum);
11861 
11862 		if ( madetop )
11863 		{
11864 			strcat(subtext, language[1136]);
11865 		}
11866 		else
11867 		{
11868 			strcat(subtext, language[1137]);
11869 		}
11870 
11871 		// identify all inventory items
11872 		for ( node = stats[clientnum]->inventory.first; node != NULL; node = node->next )
11873 		{
11874 			Item* item = (Item*)node->element;
11875 			item->identified = true;
11876 		}
11877 
11878 		// Restart
11879 		button = newButton();
11880 		strcpy(button->label, language[1138]);
11881 		button->x = subx2 - strlen(language[1138]) * 12 - 16;
11882 		button->y = suby2 - 28;
11883 		button->sizex = strlen(language[1138]) * 12 + 8;
11884 		button->sizey = 20;
11885 		button->action = &buttonStartSingleplayer;
11886 		button->visible = 1;
11887 		button->focused = 1;
11888 		button->joykey = joyimpulses[INJOY_MENU_NEXT];
11889 
11890 		// Return to Main Menu
11891 		button = newButton();
11892 		strcpy(button->label, language[1139]);
11893 		button->x = subx1 + 8;
11894 		button->y = suby2 - 28;
11895 		button->sizex = strlen(language[1139]) * 12 + 8;
11896 		button->sizey = 20;
11897 		button->action = &buttonEndGameConfirm;
11898 		button->visible = 1;
11899 		button->focused = 1;
11900 		button->joykey = joyimpulses[INJOY_MENU_CANCEL];
11901 	}
11902 	else
11903 	{
11904 		strcpy(subtext, language[1140]);
11905 
11906 		bool survivingPlayer = false;
11907 		int c;
11908 		for (c = 0; c < MAXPLAYERS; c++)
11909 		{
11910 			if (!client_disconnected[c] && players[c]->entity)
11911 			{
11912 				survivingPlayer = true;
11913 				break;
11914 			}
11915 		}
11916 		if ( survivingPlayer )
11917 		{
11918 			strcat(subtext, language[1141]);
11919 		}
11920 		else
11921 		{
11922 			strcat(subtext, language[1142]);
11923 		}
11924 
11925 		strcat(subtext, language[1143]);
11926 		strcat(subtext, scorenum);
11927 
11928 		strcat(subtext, "\n\n");
11929 
11930 		// Okay
11931 		button = newButton();
11932 		strcpy(button->label, language[1144]);
11933 		button->sizex = strlen(language[1144]) * 12 + 8;
11934 		button->sizey = 20;
11935 		button->x = subx1 + (subx2 - subx1) / 2 - button->sizex / 2;
11936 		button->y = suby2 - 28;
11937 		button->action = &buttonCloseSubwindow;
11938 		button->visible = 1;
11939 		button->focused = 1;
11940 		button->joykey = joyimpulses[INJOY_MENU_NEXT];
11941 	}
11942 
11943 	// death hints
11944 	if ( currentlevel / LENGTH_OF_LEVEL_REGION < 1 )
11945 	{
11946 		strcat(subtext, language[1145 + rand() % 15]);
11947 	}
11948 
11949 	// close button
11950 	button = newButton();
11951 	strcpy(button->label, "x");
11952 	button->x = subx2 - 20;
11953 	button->y = suby1;
11954 	button->sizex = 20;
11955 	button->sizey = 20;
11956 	button->action = &buttonCloseSubwindow;
11957 	button->visible = 1;
11958 	button->focused = 1;
11959 	button->key = SDL_SCANCODE_ESCAPE;
11960 	button->joykey = joyimpulses[INJOY_MENU_CANCEL];
11961 }
11962 
11963 // get
getResolutionList()11964 void getResolutionList()
11965 {
11966 	// for now just use the resolution modes on the first
11967 	// display.
11968 	int numdisplays = SDL_GetNumVideoDisplays();
11969 	int nummodes = SDL_GetNumDisplayModes(0);
11970 	int im;
11971 	int c;
11972 
11973 	printlog("display count: %d.\n", numdisplays);
11974 	printlog("display mode count: %d.\n", nummodes);
11975 
11976 	for (im = 0; im < nummodes; im++)
11977 	{
11978 		SDL_DisplayMode mode;
11979 		SDL_GetDisplayMode(0, im, &mode);
11980 		// resolutions below 960x600 are not supported
11981 		if ( mode.w >= 960 && mode.h >= 600 )
11982 		{
11983 			resolution res(mode.w, mode.h);
11984 			resolutions.push_back(res);
11985 		}
11986 	}
11987 
11988 	// Sort by total number of pixels
11989 	resolutions.sort([](resolution a, resolution b) {
11990 		return std::get<0>(a) * std::get<1>(a) > std::get<0>(b) * std::get<1>(b);
11991 	});
11992 	resolutions.unique();
11993 
11994 	const Uint32 kMaxResolutionsToDisplay = 22;
11995 	for ( auto it = resolutions.end(); it != resolutions.begin() && resolutions.size() >= kMaxResolutionsToDisplay; )
11996 	{
11997 		// cull some resolutions in smallest -> largest order.
11998 		--it;
11999 
12000 		int w = std::get<0>(*it);
12001 		int h = std::get<1>(*it);
12002 		int ratio = (w * 100) / h;
12003 		if ( ratio == 125 || ratio > 200 ) // 5x4, 32x15
12004 		{
12005 			it = resolutions.erase(it);
12006 		}
12007 		else if ( (w == 1152 && h == 864) || (w == 2048 && h == 1536) ) // explicit exclude for these odd 4x3 resolutions
12008 		{
12009 			it = resolutions.erase(it);
12010 		}
12011 	}
12012 }
12013 
12014 bool achievements_window = false;
12015 int achievements_window_page = 1;
12016 
buttonAchievementsUp(button_t * my)12017 void buttonAchievementsUp(button_t* my)
12018 {
12019 	achievements_window_page =
12020 		std::max(1, achievements_window_page - 1);
12021 }
12022 
buttonAchievementsDown(button_t * my)12023 void buttonAchievementsDown(button_t* my)
12024 {
12025 	int num_achievements = achievementNames.size();
12026 	if ( num_achievements == 0 )
12027 	{
12028 		return;
12029 	}
12030 	int max_pages = num_achievements / 6 + ((num_achievements % 6) ? 1 : 0);
12031 	achievements_window_page = std::min(max_pages, achievements_window_page + 1);
12032 }
12033 
12034 // sets up the achievements window
openAchievementsWindow()12035 void openAchievementsWindow()
12036 {
12037 	achievements_window = true;
12038 	achievements_window_page = 1;
12039 	subwindow = 1;
12040 	subx1 = xres / 2 - 400;
12041 	subx2 = xres / 2 + 400;
12042 	suby1 = yres / 2 - 280;
12043 	suby2 = yres / 2 + 280;
12044 	strcpy(subtext, language[3971]);
12045 #ifdef USE_EOS
12046 	EOS.loadAchievementData();
12047 #endif
12048 	// close button
12049 	{
12050 		button_t* button = newButton();
12051 		strcpy(button->label, "x");
12052 		button->x = subx2 - 20;
12053 		button->y = suby1;
12054 		button->sizex = 20;
12055 		button->sizey = 20;
12056 		button->action = &closeAchievementsWindow;
12057 		button->visible = 1;
12058 		button->focused = 1;
12059 		button->key = SDL_SCANCODE_ESCAPE;
12060 		button->joykey = joyimpulses[INJOY_MENU_CANCEL];
12061 	}
12062 
12063 	// up / prev page button
12064 	{
12065 		button_t* button = newButton();
12066 		strcpy(button->label, u8"\u25B2");
12067 		button->x = subx2 - 33;
12068 		button->y = suby1 + 84;
12069 		button->sizex = 30;
12070 		button->sizey = 30;
12071 		button->action = &buttonAchievementsUp;
12072 		button->visible = 1;
12073 		button->focused = 1;
12074 		button->key = SDL_SCANCODE_UP;
12075 		button->joykey = joyimpulses[INJOY_MENU_SETTINGS_PREV];
12076 	}
12077 
12078 	// down / next page button
12079 	{
12080 		button_t* button = newButton();
12081 		strcpy(button->label, u8"\u25BC");
12082 		button->x = subx2 - 33;
12083 		button->y = suby2 - 34;
12084 		button->sizex = 30;
12085 		button->sizey = 30;
12086 		button->action = &buttonAchievementsDown;
12087 		button->visible = 1;
12088 		button->focused = 1;
12089 		button->key = SDL_SCANCODE_DOWN;
12090 		button->joykey = joyimpulses[INJOY_MENU_SETTINGS_NEXT];
12091 	}
12092 }
12093 
closeAchievementsWindow(button_t * my)12094 void closeAchievementsWindow(button_t* my)
12095 {
12096 	buttonCloseSubwindow(my);
12097 }
12098 
12099 // sets up the settings window
openSettingsWindow()12100 void openSettingsWindow()
12101 {
12102 	button_t* button;
12103 	int c;
12104 
12105 	getResolutionList();
12106 
12107 	// set the "settings" variables
12108 	settings_xres = xres;
12109 	settings_yres = yres;
12110 	settings_fov = fov;
12111 	settings_svFlags = svFlags;
12112 	settings_smoothlighting = smoothlighting;
12113 	settings_fullscreen = fullscreen;
12114 	settings_borderless = borderless;
12115 	settings_shaking = shaking;
12116 	settings_bobbing = bobbing;
12117 	settings_spawn_blood = spawn_blood;
12118 	settings_light_flicker = flickerLights;
12119 	settings_vsync = verticalSync;
12120 	settings_status_effect_icons = showStatusEffectIcons;
12121 	settings_colorblind = colorblind;
12122 	settings_gamma = vidgamma;
12123 	settings_fps = fpsLimit;
12124 	settings_sfxvolume = sfxvolume;
12125 	settings_sfxAmbientVolume = sfxAmbientVolume;
12126 	settings_sfxEnvironmentVolume = sfxEnvironmentVolume;
12127 	settings_musvolume = musvolume;
12128 	settings_minimap_ping_mute = minimapPingMute;
12129 	settings_mute_audio_on_focus_lost = mute_audio_on_focus_lost;
12130 	settings_mute_player_monster_sounds = mute_player_monster_sounds;
12131 	settings_minimap_transparency_foreground = minimapTransparencyForeground;
12132 	settings_minimap_transparency_background = minimapTransparencyBackground;
12133 	settings_minimap_scale = minimapScale;
12134 	settings_minimap_object_zoom = minimapObjectZoom;
12135 	settings_uiscale_charactersheet = uiscale_charactersheet;
12136 	settings_uiscale_skillspage = uiscale_skillspage;
12137 	settings_uiscale_inventory = uiscale_inventory;
12138 	settings_uiscale_hotbar = uiscale_hotbar;
12139 	settings_uiscale_chatlog = uiscale_chatlog;
12140 	settings_uiscale_playerbars = uiscale_playerbars;
12141 	settings_hide_statusbar = hide_statusbar;
12142 	settings_hide_playertags = hide_playertags;
12143 	settings_show_skill_values = show_skill_values;
12144 	settings_disableMultithreadedSteamNetworking = disableMultithreadedSteamNetworking;
12145 	settings_disableFPSLimitOnNetworkMessages = disableFPSLimitOnNetworkMessages;
12146 	LobbyHandler.settings_crossplayEnabled = LobbyHandler.crossplayEnabled;
12147 	for (c = 0; c < NUMIMPULSES; c++)
12148 	{
12149 		settings_impulses[c] = impulses[c];
12150 	}
12151 	for (c = 0; c < NUM_JOY_IMPULSES; c++)
12152 	{
12153 		settings_joyimpulses[c] = joyimpulses[c];
12154 	}
12155 	settings_reversemouse = reversemouse;
12156 	settings_smoothmouse = smoothmouse;
12157 	settings_disablemouserotationlimit = disablemouserotationlimit;
12158 	settings_mousespeed = mousespeed;
12159 	settings_broadcast = broadcast;
12160 	settings_nohud = nohud;
12161 	settings_auto_hotbar_new_items = auto_hotbar_new_items;
12162 	for ( c = 0; c < NUM_HOTBAR_CATEGORIES; ++c )
12163 	{
12164 		settings_auto_hotbar_categories[c] = auto_hotbar_categories[c];
12165 	}
12166 	for ( c = 0; c < NUM_AUTOSORT_CATEGORIES; ++c )
12167 	{
12168 		settings_autosort_inventory_categories[c] = autosort_inventory_categories[c];
12169 	}
12170 	settings_hotbar_numkey_quick_add = hotbar_numkey_quick_add;
12171 	settings_disable_messages = disable_messages;
12172 	settings_right_click_protect = right_click_protect;
12173 	settings_auto_appraise_new_items = auto_appraise_new_items;
12174 	settings_lock_right_sidebar = lock_right_sidebar;
12175 	settings_show_game_timer_always = show_game_timer_always;
12176 
12177 	settings_gamepad_leftx_invert = gamepad_leftx_invert;
12178 	settings_gamepad_lefty_invert = gamepad_lefty_invert;
12179 	settings_gamepad_rightx_invert = gamepad_rightx_invert;
12180 	settings_gamepad_righty_invert = gamepad_righty_invert;
12181 	settings_gamepad_menux_invert = gamepad_menux_invert;
12182 	settings_gamepad_menuy_invert = gamepad_menuy_invert;
12183 
12184 	settings_gamepad_deadzone = gamepad_deadzone;
12185 	settings_gamepad_rightx_sensitivity = gamepad_rightx_sensitivity;
12186 	settings_gamepad_righty_sensitivity = gamepad_righty_sensitivity;
12187 	settings_gamepad_menux_sensitivity = gamepad_menux_sensitivity;
12188 	settings_gamepad_menuy_sensitivity = gamepad_menuy_sensitivity;
12189 
12190 	// create settings window
12191 	settings_window = true;
12192 	subwindow = 1;
12193 	//subx1 = xres/2-256;
12194 	subx1 = xres / 2 - 448;
12195 	//subx2 = xres/2+256;
12196 	subx2 = xres / 2 + 448;
12197 	//suby1 = yres/2-192;
12198 	//suby2 = yres/2+192;
12199 #ifdef PANDORA
12200 	suby1 = yres / 2 - ((yres==480)?210:278);
12201 	suby2 = yres / 2 + ((yres==480)?210:278);
12202 #else
12203 	suby1 = yres / 2 - 320;
12204 	suby2 = yres / 2 + 320;
12205 #endif
12206 	strcpy(subtext, language[1306]);
12207 
12208 	// close button
12209 	button = newButton();
12210 	strcpy(button->label, "x");
12211 	button->x = subx2 - 20;
12212 	button->y = suby1;
12213 	button->sizex = 20;
12214 	button->sizey = 20;
12215 	button->action = &buttonCloseSettingsSubwindow;
12216 	button->visible = 1;
12217 	button->focused = 1;
12218 	button->key = SDL_SCANCODE_ESCAPE;
12219 	button->joykey = joyimpulses[INJOY_MENU_CANCEL];
12220 
12221 	// cancel button
12222 	button = newButton();
12223 	strcpy(button->label, language[1316]);
12224 	button->x = subx1 + 8;
12225 	button->y = suby2 - 28;
12226 	button->sizex = strlen(language[1316]) * 12 + 8;
12227 	button->sizey = 20;
12228 	button->action = &buttonCloseSubwindow;
12229 	button->visible = 1;
12230 	button->focused = 1;
12231 
12232 	// ok button
12233 	button = newButton();
12234 	strcpy(button->label, language[1433]);
12235 	button->x = subx2 - strlen(language[1433]) * 12 - 16;
12236 	button->y = suby2 - 28;
12237 	button->sizex = strlen(language[1433]) * 12 + 8;
12238 	button->sizey = 20;
12239 	button->action = &buttonSettingsOK;
12240 	button->visible = 1;
12241 	button->focused = 1;
12242 	button->key = SDL_SCANCODE_RETURN;
12243 	button->joykey = joyimpulses[INJOY_MENU_NEXT];
12244 
12245 	// accept button
12246 	button = newButton();
12247 	strcpy(button->label, language[1317]);
12248 	button->x = subx2 - strlen(language[1317]) * 12 - 16 - strlen(language[1317]) * 12 - 16;
12249 	button->y = suby2 - 28;
12250 	button->sizex = strlen(language[1317]) * 12 + 8;
12251 	button->sizey = 20;
12252 	button->action = &buttonSettingsAccept;
12253 	button->visible = 1;
12254 	button->focused = 1;
12255 
12256 	int tabx_so_far = subx1 + 16;
12257 
12258 	//TODO: Select tab based off of dpad left & right.
12259 	//TODO: Maybe golden highlighting & stuff.
12260 
12261 	// video tab
12262 	button = newButton();
12263 	strcpy(button->label, language[1434]);
12264 	button->x = tabx_so_far;
12265 	button->y = suby1 + 24;
12266 	button->sizex = strlen(language[1434]) * 12 + 8;
12267 	button->sizey = 20;
12268 	button->action = &buttonVideoTab;
12269 	button->visible = 1;
12270 	button->focused = 1;
12271 	button_video_tab = button;
12272 
12273 	tabx_so_far += strlen(language[1434]) * 12 + 8;
12274 
12275 	// audio tab
12276 	button = newButton();
12277 	strcpy(button->label, language[1435]);
12278 	button->x = tabx_so_far;
12279 	button->y = suby1 + 24;
12280 	button->sizex = strlen(language[1435]) * 12 + 8;
12281 	button->sizey = 20;
12282 	button->action = &buttonAudioTab;
12283 	button->visible = 1;
12284 	button->focused = 1;
12285 	button_audio_tab = button;
12286 
12287 	tabx_so_far += strlen(language[1435]) * 12 + 8;
12288 
12289 	// keyboard tab
12290 	button = newButton();
12291 	strcpy(button->label, language[1436]);
12292 	button->x = tabx_so_far;
12293 	button->y = suby1 + 24;
12294 	button->sizex = strlen(language[1436]) * 12 + 8;
12295 	button->sizey = 20;
12296 	button->action = &buttonKeyboardTab;
12297 	button->visible = 1;
12298 	button->focused = 1;
12299 	button_keyboard_tab = button;
12300 
12301 	tabx_so_far += strlen(language[1436]) * 12 + 8;
12302 
12303 	// mouse tab
12304 	button = newButton();
12305 	strcpy(button->label, language[1437]);
12306 	button->x = tabx_so_far;
12307 	button->y = suby1 + 24;
12308 	button->sizex = strlen(language[1437]) * 12 + 8;
12309 	button->sizey = 20;
12310 	button->action = &buttonMouseTab;
12311 	button->visible = 1;
12312 	button->focused = 1;
12313 	button_mouse_tab = button;
12314 
12315 	tabx_so_far += strlen(language[1437]) * 12 + 8;
12316 
12317 	//Gamepad bindings tab.
12318 	button = newButton();
12319 	strcpy(button->label, language[1947]);
12320 	button->x = tabx_so_far;
12321 	button->y = suby1 + 24;
12322 	button->sizex = strlen(language[1947]) * 12 + 8;
12323 	button->sizey = 20;
12324 	button->action = &buttonGamepadBindingsTab;
12325 	button->visible = 1;
12326 	button->focused = 1;
12327 	button_gamepad_bindings_tab = button;
12328 
12329 	tabx_so_far += strlen(language[1947]) * 12 + 8;
12330 
12331 	//Gamepad settings tab.
12332 	button = newButton();
12333 	strcpy(button->label, language[2400]);
12334 	button->x = tabx_so_far;
12335 	button->y = suby1 + 24;
12336 	button->sizex = strlen(language[2400]) * 12 + 8;
12337 	button->sizey = 20;
12338 	button->action = &buttonGamepadSettingsTab;
12339 	button->visible = 1;
12340 	button->focused = 1;
12341 	button_gamepad_settings_tab = button;
12342 
12343 	tabx_so_far += strlen(language[2400]) * 12 + 8;
12344 
12345 	// misc tab
12346 	button = newButton();
12347 	strcpy(button->label, language[1438]);
12348 	button->x =  tabx_so_far;
12349 	button->y = suby1 + 24;
12350 	button->sizex = strlen(language[1438]) * 12 + 8;
12351 	button->sizey = 20;
12352 	button->action = &buttonMiscTab;
12353 	button->visible = 1;
12354 	button->focused = 1;
12355 	button_misc_tab = button;
12356 
12357 	//Initialize resolution confirmation window related variables.
12358 	resolutionChanged = false;
12359 	resolutionConfirmationTimer = 0;
12360 
12361 	changeSettingsTab(settings_tab);
12362 }
12363 
12364 
12365 // opens the wait window for steam lobby (getting lobby list, etc.)
openSteamLobbyWaitWindow(button_t * my)12366 void openSteamLobbyWaitWindow(button_t* my)
12367 {
12368 	button_t* button;
12369 
12370 	// close current window
12371 #ifdef STEAMWORKS
12372 	bool prevConnectingToLobbyWindow = connectingToLobbyWindow;
12373 	if ( connectingToLobbyWindow )
12374 	{
12375 		// we quit the connection window before joining lobby, but invite was mid-flight.
12376 		denyLobbyJoinEvent = true;
12377 	}
12378 	else if ( joinLobbyWaitingForHostResponse )
12379 	{
12380 		// we quit the connection window after lobby join, but before host has accepted us.
12381 		joinLobbyWaitingForHostResponse = false;
12382 		buttonDisconnect(nullptr);
12383 		openFailedConnectionWindow(CLIENT);
12384 		strcpy(subtext, LobbyHandler_t::getLobbyJoinFailedConnectString(static_cast<int>(LobbyHandler_t::LOBBY_JOIN_CANCELLED)).c_str());
12385 		return;
12386 	}
12387 #endif
12388 #if defined USE_EOS
12389 	if ( EOS.bConnectingToLobbyWindow )
12390 	{
12391 		// we quit the connection window before joining lobby, but invite was mid-flight.
12392 		EOS.CurrentLobbyData.bDenyLobbyJoinEvent = true;
12393 	}
12394 	else if ( EOS.bJoinLobbyWaitingForHostResponse )
12395 	{
12396 		// we quit the connection window after lobby join, but before host has accepted us.
12397 		EOS.bJoinLobbyWaitingForHostResponse = false;
12398 		buttonDisconnect(nullptr);
12399 		openFailedConnectionWindow(CLIENT);
12400 		strcpy(subtext, LobbyHandler_t::getLobbyJoinFailedConnectString(static_cast<int>(LobbyHandler_t::LOBBY_JOIN_CANCELLED)).c_str());
12401 		return;
12402 	}
12403 #endif
12404 
12405 
12406 	buttonCloseSubwindow(NULL);
12407 	list_FreeAll(&button_l);
12408 	deleteallbuttons = true;
12409 
12410 	// create new window
12411 	subwindow = 1;
12412 #ifdef STEAMWORKS
12413 	requestingLobbies = true;
12414 #endif
12415 #if defined USE_EOS
12416 	EOS.bRequestingLobbies = true;
12417 #endif // USE_EOS
12418 
12419 	subx1 = xres / 2 - 256;
12420 	subx2 = xres / 2 + 256;
12421 	suby1 = yres / 2 - 64;
12422 	suby2 = yres / 2 + 64;
12423 	strcpy(subtext, language[1444]);
12424 #ifdef STEAMWORKS
12425 	//c_SteamMatchmaking_RequestLobbyList();
12426 	//SteamMatchmaking()->RequestLobbyList(); //TODO: Is this sufficient for it to work?
12427 	cpp_SteamMatchmaking_RequestLobbyList();
12428 #endif
12429 
12430 	LobbyHandler.selectedLobbyInList = 0;
12431 
12432 #if defined USE_EOS
12433 #ifdef STEAMWORKS
12434 	if ( EOS.CurrentUserInfo.bUserLoggedIn )
12435 	{
12436 		EOS.searchLobbies(EOSFuncs::LobbyParameters_t::LobbySearchOptions::LOBBY_SEARCH_ALL,
12437 			EOSFuncs::LobbyParameters_t::LobbyJoinOptions::LOBBY_DONT_JOIN, "");
12438 	}
12439 	else
12440 	{
12441 		EOS.bRequestingLobbies = false; // don't attempt search if not logged in
12442 	}
12443 #else
12444 	EOS.searchLobbies(EOSFuncs::LobbyParameters_t::LobbySearchOptions::LOBBY_SEARCH_ALL,
12445 		EOSFuncs::LobbyParameters_t::LobbyJoinOptions::LOBBY_DONT_JOIN, "");
12446 #endif
12447 #endif // USE_EOS
12448 
12449 
12450 	// close button
12451 	button = newButton();
12452 	strcpy(button->label, "x");
12453 	button->x = subx2 - 20;
12454 	button->y = suby1;
12455 	button->sizex = 20;
12456 	button->sizey = 20;
12457 	button->action = &buttonCloseSubwindow;
12458 	button->visible = 1;
12459 	button->focused = 1;
12460 	button->key = SDL_SCANCODE_ESCAPE;
12461 	button->joykey = joyimpulses[INJOY_MENU_CANCEL];
12462 
12463 	// cancel button
12464 	button = newButton();
12465 	strcpy(button->label, language[1316]);
12466 	button->sizex = strlen(language[1316]) * 12 + 8;
12467 	button->sizey = 20;
12468 	button->x = subx1 + (subx2 - subx1) / 2 - button->sizex / 2;
12469 	button->y = suby2 - 28;
12470 	button->action = &buttonCloseSubwindow;
12471 	button->visible = 1;
12472 	button->focused = 1;
12473 }
12474 
12475 // "failed to connect" message
openFailedConnectionWindow(int mode)12476 void openFailedConnectionWindow(int mode)
12477 {
12478 	button_t* button;
12479 
12480 	// close current window
12481 	buttonCloseSubwindow(NULL);
12482 	list_FreeAll(&button_l);
12483 	deleteallbuttons = true;
12484 
12485 	// create new window
12486 	subwindow = 1;
12487 	subx1 = xres / 2 - 256;
12488 	subx2 = xres / 2 + 256;
12489 	suby1 = yres / 2 - 64;
12490 	suby2 = yres / 2 + 64;
12491 	if ( directConnect )
12492 	{
12493 		if ( mode == CLIENT )
12494 		{
12495 			strcpy(subtext, language[1439]);
12496 			strcat(subtext, SDLNet_GetError());
12497 		}
12498 		else if ( mode == SERVER )
12499 		{
12500 			strcpy(subtext, language[1440]);
12501 			strcat(subtext, SDLNet_GetError());
12502 		}
12503 		else
12504 		{
12505 			strcpy(subtext, language[1443]);
12506 		}
12507 	}
12508 	else
12509 	{
12510 		if ( mode == CLIENT )
12511 		{
12512 			strcpy(subtext, language[1441]);
12513 		}
12514 		else if ( mode == SERVER )
12515 		{
12516 			strcpy(subtext, language[1442]);
12517 		}
12518 		else
12519 		{
12520 			strcpy(subtext, language[1443]);
12521 		}
12522 	}
12523 
12524 	// close button
12525 	button = newButton();
12526 	strcpy(button->label, "x");
12527 	button->x = subx2 - 20;
12528 	button->y = suby1;
12529 	button->sizex = 20;
12530 	button->sizey = 20;
12531 	button->action = &buttonCloseSubwindow;
12532 	button->visible = 1;
12533 	button->focused = 1;
12534 	button->key = SDL_SCANCODE_ESCAPE;
12535 	button->joykey = joyimpulses[INJOY_MENU_CANCEL];
12536 
12537 	// okay button
12538 	button = newButton();
12539 	strcpy(button->label, language[732]);
12540 	button->x = subx2 - (subx2 - subx1) / 2 - strlen(language[732]) * 6;
12541 	button->y = suby2 - 24;
12542 	button->sizex = strlen(language[732]) * 12 + 8;
12543 	button->sizey = 20;
12544 	button->visible = 1;
12545 	button->focused = 1;
12546 	button->key = SDL_SCANCODE_RETURN;
12547 	button->joykey = joyimpulses[INJOY_MENU_NEXT];
12548 
12549 	if ( directConnect )
12550 	{
12551 		if ( mode == CLIENT )
12552 		{
12553 			button->action = &buttonJoinMultiplayer;
12554 		}
12555 		else if ( mode == SERVER )
12556 		{
12557 			button->action = &buttonHostMultiplayer;
12558 		}
12559 		else
12560 		{
12561 			button->action = &buttonCloseSubwindow;
12562 		}
12563 	}
12564 	else
12565 	{
12566 		if ( mode == CLIENT )
12567 		{
12568 			button->action = &openSteamLobbyWaitWindow;
12569 		}
12570 		else if ( mode == SERVER )
12571 		{
12572 			button->action = &buttonCloseSubwindow;
12573 		}
12574 		else
12575 		{
12576 			button->action = &buttonCloseSubwindow;
12577 		}
12578 	}
12579 
12580 	multiplayer = SINGLE;
12581 	clientnum = 0;
12582 }
12583 
12584 // opens the lobby browser window (steam client only)
openSteamLobbyBrowserWindow(button_t * my)12585 void openSteamLobbyBrowserWindow(button_t* my)
12586 {
12587 	button_t* button;
12588 
12589 	// close current window
12590 	buttonCloseSubwindow(NULL);
12591 	list_FreeAll(&button_l);
12592 	deleteallbuttons = true;
12593 
12594 	// create new window
12595 	subwindow = 1;
12596 	subx1 = xres / 2 - 280;
12597 	subx2 = xres / 2 + 280;
12598 	suby1 = yres / 2 - 198;
12599 	suby2 = yres / 2 + 198;
12600 	strcpy(subtext, language[1334]);
12601 
12602 	bool showCrossplayLobbyFilters = false;
12603 
12604 	// setup lobby browser
12605 #ifdef STEAMWORKS //TODO: Should this whole function be ifdeffed?
12606 	selectedSteamLobby = 0;
12607 #endif
12608 #if defined USE_EOS
12609 	EOS.LobbySearchResults.selectedLobby = 0;
12610 	showCrossplayLobbyFilters = true;
12611 #ifdef STEAMWORKS
12612 	if ( !LobbyHandler.crossplayEnabled )
12613 	{
12614 		showCrossplayLobbyFilters = false;
12615 	}
12616 #endif
12617 #endif
12618 	slidery = 0;
12619 	oslidery = 0;
12620 
12621 	// close button
12622 	button = newButton();
12623 	strcpy(button->label, "x");
12624 	button->x = subx2 - 20;
12625 	button->y = suby1;
12626 	button->sizex = 20;
12627 	button->sizey = 20;
12628 	button->action = &buttonCloseSubwindow;
12629 	button->visible = 1;
12630 	button->focused = 1;
12631 	button->key = SDL_SCANCODE_ESCAPE;
12632 	button->joykey = joyimpulses[INJOY_MENU_CANCEL];
12633 
12634 	// join button
12635 	button = newButton();
12636 	strcpy(button->label, language[1445]);
12637 	button->x = subx1 + 8;
12638 	button->y = suby2 - 56;
12639 	button->sizex = strlen(language[1445]) * 12 + 8;
12640 	button->sizey = 20;
12641 #if defined STEAMWORKS || defined USE_EOS
12642 	button->action = &buttonSteamLobbyBrowserJoinGame;
12643 #endif
12644 	button->visible = 1;
12645 	button->focused = 1;
12646 	button->key = SDL_SCANCODE_RETURN;
12647 	button->joykey = joyimpulses[INJOY_MENU_NEXT];
12648 
12649 	// refresh button
12650 	button = newButton();
12651 	strcpy(button->label, language[1446]);
12652 	button->x = subx1 + 8;
12653 	button->y = suby2 - 28;
12654 	button->sizex = strlen(language[1446]) * 12 + 8;
12655 	button->sizey = 20;
12656 #if defined STEAMWORKS || defined USE_EOS
12657 	button->action = &buttonSteamLobbyBrowserRefresh;
12658 #endif
12659 	button->visible = 1;
12660 	button->focused = 1;
12661 	button->joykey = joyimpulses[INJOY_MENU_REFRESH_LOBBY]; //"y" refreshes
12662 
12663 	if ( showCrossplayLobbyFilters )
12664 	{
12665 		// filter button
12666 		button = newButton();
12667 		strcpy(button->label, language[3950]);
12668 		button->sizex = strlen(language[3950]) * 12 + 8;
12669 		button->sizey = 20;
12670 		button->x = subx2 - 8 - button->sizex;
12671 		button->y = suby2 - 56;
12672 #if defined STEAMWORKS || defined USE_EOS
12673 		button->action = &LobbyHandler_t::filterLobbyButton;
12674 #endif
12675 		button->visible = 1;
12676 		button->focused = 1;
12677 
12678 		button = newButton();
12679 		button->x = 0;
12680 		button->y = 0;
12681 		button->sizex = 0;
12682 		button->sizey = 0;
12683 		button->visible = 0;
12684 		button->focused = 0;
12685 		strcpy(button->label, language[3953]);
12686 		button->action = &LobbyHandler.searchLobbyWithFilter;
12687 	}
12688 }
12689 
12690 // steam lobby browser join game
buttonSteamLobbyBrowserJoinGame(button_t * my)12691 void buttonSteamLobbyBrowserJoinGame(button_t* my)
12692 {
12693 	LobbyHandler.setLobbyJoinTypeOfCurrentSelection();
12694 	LobbyHandler.setP2PType(LobbyHandler.getJoiningType());
12695 	if ( LobbyHandler.getJoiningType() == LobbyHandler_t::LobbyServiceType::LOBBY_STEAM )
12696 	{
12697 #ifdef STEAMWORKS
12698 		button_t* button;
12699 		int lobbyIndex = std::min(std::max(0, selectedSteamLobby), MAX_STEAM_LOBBIES - 1);
12700 		if ( lobbyIDs[lobbyIndex] )
12701 		{
12702 			// clear buttons
12703 			list_FreeAll(&button_l);
12704 			deleteallbuttons = true;
12705 
12706 			// create new window
12707 			subwindow = 1;
12708 			subx1 = xres / 2 - 256;
12709 			subx2 = xres / 2 + 256;
12710 			suby1 = yres / 2 - 64;
12711 			suby2 = yres / 2 + 64;
12712 			strcpy(subtext, language[1447]);
12713 
12714 			// close button
12715 			button = newButton();
12716 			strcpy(button->label, "x");
12717 			button->x = subx2 - 20;
12718 			button->y = suby1;
12719 			button->sizex = 20;
12720 			button->sizey = 20;
12721 			button->action = &openSteamLobbyWaitWindow;
12722 			button->visible = 1;
12723 			button->focused = 1;
12724 			button->key = SDL_SCANCODE_ESCAPE;
12725 			button->joykey = joyimpulses[INJOY_MENU_CANCEL];
12726 
12727 			// cancel button
12728 			button = newButton();
12729 			strcpy(button->label, language[1316]);
12730 			button->sizex = strlen(language[1316]) * 12 + 8;
12731 			button->sizey = 20;
12732 			button->x = subx1 + (subx2 - subx1) / 2 - button->sizex / 2;
12733 			button->y = suby2 - 28;
12734 			button->action = &openSteamLobbyWaitWindow;
12735 			button->visible = 1;
12736 			button->focused = 1;
12737 
12738 			connectingToLobby = true;
12739 			connectingToLobbyWindow = true;
12740 			strncpy(currentLobbyName, lobbyText[lobbyIndex], 31);
12741 			LobbyHandler.steamValidateAndJoinLobby(*static_cast<CSteamID*>(lobbyIDs[lobbyIndex]));
12742 		}
12743 #endif
12744 	}
12745 	else if ( LobbyHandler.getJoiningType() == LobbyHandler_t::LobbyServiceType::LOBBY_CROSSPLAY )
12746 	{
12747 #if defined USE_EOS
12748 		button_t* button;
12749 		int lobbyIndex = std::min(std::max(0, EOS.LobbySearchResults.selectedLobby), EOS.kMaxLobbiesToSearch - 1);
12750 		if ( !EOS.LobbySearchResults.results.empty() )
12751 		{
12752 			// clear buttons
12753 			list_FreeAll(&button_l);
12754 			deleteallbuttons = true;
12755 
12756 			// create new window
12757 			subwindow = 1;
12758 			subx1 = xres / 2 - 256;
12759 			subx2 = xres / 2 + 256;
12760 			suby1 = yres / 2 - 64;
12761 			suby2 = yres / 2 + 64;
12762 			strcpy(subtext, language[1447]);
12763 
12764 			// close button
12765 			button = newButton();
12766 			strcpy(button->label, "x");
12767 			button->x = subx2 - 20;
12768 			button->y = suby1;
12769 			button->sizex = 20;
12770 			button->sizey = 20;
12771 			button->action = &openSteamLobbyWaitWindow;
12772 			button->visible = 1;
12773 			button->focused = 1;
12774 			button->key = SDL_SCANCODE_ESCAPE;
12775 			button->joykey = joyimpulses[INJOY_MENU_CANCEL];
12776 
12777 			// cancel button
12778 			button = newButton();
12779 			strcpy(button->label, language[1316]);
12780 			button->sizex = strlen(language[1316]) * 12 + 8;
12781 			button->sizey = 20;
12782 			button->x = subx1 + (subx2 - subx1) / 2 - button->sizex / 2;
12783 			button->y = suby2 - 28;
12784 			button->action = &openSteamLobbyWaitWindow;
12785 			button->visible = 1;
12786 			button->focused = 1;
12787 
12788 			EOS.bConnectingToLobby = true;
12789 			EOS.bConnectingToLobbyWindow = true;
12790 			strncpy(EOS.currentLobbyName, EOS.LobbySearchResults.getResultFromDisplayedIndex(lobbyIndex)->LobbyAttributes.lobbyName.c_str(), 31);
12791 			std::string lobbyToJoin = EOS.LobbySearchResults.getResultFromDisplayedIndex(lobbyIndex)->LobbyId;
12792 			EOS.searchLobbies(EOSFuncs::LobbyParameters_t::LobbySearchOptions::LOBBY_SEARCH_BY_LOBBYID,
12793 				EOSFuncs::LobbyParameters_t::LobbyJoinOptions::LOBBY_JOIN_FIRST_SEARCH_RESULT,
12794 				lobbyToJoin.c_str());
12795 		}
12796 #endif // USE_EOS
12797 	}
12798 	else if ( LobbyHandler.getJoiningType() == LobbyHandler_t::LobbyServiceType::LOBBY_DISABLE )
12799 	{
12800 		LobbyHandler_t::logError("Invalid lobby join type from current browser selection: %d, list size %d", LobbyHandler.selectedLobbyInList, LobbyHandler.lobbyDisplayedSearchResults.size());
12801 	}
12802 	return;
12803 }
12804 
12805 // steam lobby browser refresh
buttonSteamLobbyBrowserRefresh(button_t * my)12806 void buttonSteamLobbyBrowserRefresh(button_t* my)
12807 {
12808 #if defined STEAMWORKS || defined USE_EOS
12809 	LobbyHandler.showLobbyFilters = false;
12810 	openSteamLobbyWaitWindow(my);
12811 #endif
12812 }
12813 
12814 // quit game button
buttonQuitConfirm(button_t * my)12815 void buttonQuitConfirm(button_t* my)
12816 {
12817 	subwindow = 0;
12818 	introstage = 2; // prepares to quit the whole game
12819 	fadeout = true;
12820 }
12821 
12822 // quit game button (no save)
buttonQuitNoSaveConfirm(button_t * my)12823 void buttonQuitNoSaveConfirm(button_t* my)
12824 {
12825 	buttonQuitConfirm(my);
12826 	deleteSaveGame(multiplayer);
12827 
12828 	// make a highscore!
12829 	saveScore();
12830 }
12831 
12832 // end game button
12833 bool savethisgame = false;
buttonEndGameConfirm(button_t * my)12834 void buttonEndGameConfirm(button_t* my)
12835 {
12836 	savethisgame = false;
12837 	subwindow = 0;
12838 	introstage = 5; // prepares to end the current game (throws to main menu)
12839 	fadeout = true;
12840 	//Edge case for freeing channeled spells on a client.
12841 	if (multiplayer == CLIENT)
12842 	{
12843 		list_FreeAll(&channeledSpells[clientnum]);
12844 	}
12845 	if ( !intro )
12846 	{
12847 		pauseGame(2, false);
12848 	}
12849 }
12850 
buttonEndGameConfirmSave(button_t * my)12851 void buttonEndGameConfirmSave(button_t* my)
12852 {
12853 	subwindow = 0;
12854 	introstage = 5; // prepares to end the current game (throws to main menu)
12855 	fadeout = true;
12856 	savethisgame = true;
12857 	if ( !intro )
12858 	{
12859 		pauseGame(2, false);
12860 	}
12861 }
12862 
12863 // generic close window button
buttonCloseSubwindow(button_t * my)12864 void buttonCloseSubwindow(button_t* my)
12865 {
12866 	int c;
12867 	for ( c = 0; c < 512; c++ )
12868 	{
12869 		keystatus[c] = 0;
12870 	}
12871 	if ( !subwindow )
12872 	{
12873 		return;
12874 	}
12875 
12876 	loadGameSaveShowRectangle = 0;
12877 	singleplayerSavegameFreeSlot = -1; // clear this value when closing window
12878 	multiplayerSavegameFreeSlot = -1;  // clear this value when closing window
12879 	directoryPath = "";
12880 	gamemodsWindowClearVariables();
12881 	if ( score_window || score_leaderboard_window )
12882 	{
12883 		// reset class loadout
12884 		stats[0]->sex = static_cast<sex_t>(0);
12885 		stats[0]->appearance = 0;
12886 		stats[0]->playerRace = RACE_HUMAN;
12887 		strcpy(stats[0]->name, "");
12888 		stats[0]->type = HUMAN;
12889 		client_classes[0] = 0;
12890 		stats[0]->clearStats();
12891 		initClass(0);
12892 	}
12893 	rebindkey = -1;
12894 #ifdef STEAMWORKS
12895 	requestingLobbies = false;
12896 	connectingToLobbyWindow = false;
12897 	connectingToLobby = false;
12898 	joinLobbyWaitingForHostResponse = false;
12899 #endif
12900 #if defined USE_EOS
12901 	EOS.bRequestingLobbies = false;
12902 	EOS.bJoinLobbyWaitingForHostResponse = false;
12903 	EOS.bConnectingToLobby = false;
12904 	EOS.bConnectingToLobbyWindow = false;
12905 #endif
12906 
12907 #if !defined STEAMWORKS && !defined USE_EOS
12908 	serialEnterWindow = false;
12909 #endif
12910 
12911 	score_window = 0;
12912 	score_leaderboard_window = 0;
12913 	gamemods_window = 0;
12914 	gameModeManager.Tutorial.Menu.close();
12915 	gameModeManager.Tutorial.FirstTimePrompt.close();
12916 	savegames_window = 0;
12917 	savegames_window_scroll = 0;
12918 	achievements_window = false;
12919 	achievements_window_page = 1;
12920 	lobby_window = false;
12921 	settings_window = false;
12922 	connect_window = 0;
12923 #ifdef STEAMWORKS
12924 	if ( charcreation_step )
12925 	{
12926 		if ( lobbyToConnectTo )
12927 		{
12928 			// cancel lobby invitation acceptance
12929 			cpp_Free_CSteamID(lobbyToConnectTo); //TODO: Bugger this.
12930 			lobbyToConnectTo = NULL;
12931 		}
12932 	}
12933 #endif
12934 
12935 	charcreation_step = 0;
12936 	subwindow = 0;
12937 	if ( SDL_IsTextInputActive() )
12938 	{
12939 		SDL_StopTextInput();
12940 	}
12941 	playSound(138, 64);
12942 }
12943 
buttonCloseSettingsSubwindow(button_t * my)12944 void buttonCloseSettingsSubwindow(button_t* my)
12945 {
12946 	if ( rebindkey != -1 || rebindaction != -1 )
12947 	{
12948 		//Do not close settings subwindow if rebinding a key/gamepad button/whatever.
12949 		return;
12950 	}
12951 
12952 	buttonCloseSubwindow(my);
12953 }
12954 
buttonCloseAndEndGameConfirm(button_t * my)12955 void buttonCloseAndEndGameConfirm(button_t* my)
12956 {
12957 	//Edge case for freeing channeled spells on a client.
12958 	if (multiplayer == CLIENT)
12959 	{
12960 		list_FreeAll(&channeledSpells[clientnum]);
12961 	}
12962 	buttonCloseSubwindow(my);
12963 	buttonEndGameConfirmSave(my);
12964 }
12965 
12966 Uint32 charcreation_ticks = 0;
12967 
12968 // move player forward through creation dialogue
buttonContinue(button_t * my)12969 void buttonContinue(button_t* my)
12970 {
12971 	button_t* button;
12972 
12973 	if ( ticks - charcreation_ticks < TICKS_PER_SECOND / 10 )
12974 	{
12975 		return;
12976 	}
12977 	charcreation_ticks = ticks;
12978 	if ( charcreation_step == 4 && !strcmp(stats[0]->name, "") )
12979 	{
12980 		return;
12981 	}
12982 
12983 	charcreation_step++;
12984 	if ( charcreation_step == 3 && stats[0]->playerRace != RACE_HUMAN )
12985 	{
12986 		charcreation_step = 4; // skip appearance window
12987 	}
12988 	if ( charcreation_step == 4 )
12989 	{
12990 		inputstr = stats[0]->name;
12991 		SDL_StartTextInput();
12992 		inputlen = 22;
12993 	}
12994 	else if ( charcreation_step == 5 )
12995 	{
12996 		// look for a gap in save game numbering
12997 		savegameCurrentFileIndex = 0;
12998 		for ( int fileNumber = 0; fileNumber < SAVE_GAMES_MAX; ++fileNumber )
12999 		{
13000 			if ( !saveGameExists(true, fileNumber) )
13001 			{
13002 				singleplayerSavegameFreeSlot = fileNumber;
13003 				break;
13004 			}
13005 		}
13006 		for ( int fileNumber = 0; fileNumber < SAVE_GAMES_MAX; ++fileNumber )
13007 		{
13008 			if ( !saveGameExists(false, fileNumber) )
13009 			{
13010 				multiplayerSavegameFreeSlot = fileNumber;
13011 				break;
13012 			}
13013 		}
13014 		if ( SDL_IsTextInputActive() )
13015 		{
13016 			lastname = (string)stats[0]->name;
13017 			SDL_StopTextInput();
13018 		}
13019 #ifdef STEAMWORKS
13020 		if ( lobbyToConnectTo )
13021 		{
13022 			charcreation_step = 0;
13023 
13024 			// since we skip step 6 we never set the correct free slot.
13025 			reloadSavegamesList(false);
13026 			if ( multiplayerSavegameFreeSlot == -1 )
13027 			{
13028 				savegameCurrentFileIndex = 0;
13029 				std::vector<std::tuple<int, int, int, std::string>>::reverse_iterator it = savegamesList.rbegin();
13030 				for ( ; it != savegamesList.rend(); ++it )
13031 				{
13032 					std::tuple<int, int, int, std::string> entry = *it;
13033 					if ( std::get<1>(entry) != SINGLE )
13034 					{
13035 						savegameCurrentFileIndex = std::get<2>(entry);
13036 						break;
13037 					}
13038 				}
13039 			}
13040 			else
13041 			{
13042 				savegameCurrentFileIndex = multiplayerSavegameFreeSlot;
13043 			}
13044 
13045 			// clear buttons
13046 			list_FreeAll(&button_l);
13047 			deleteallbuttons = true;
13048 
13049 			// create new window
13050 			subwindow = 1;
13051 			subx1 = xres / 2 - 256;
13052 			subx2 = xres / 2 + 256;
13053 			suby1 = yres / 2 - 64;
13054 			suby2 = yres / 2 + 64;
13055 			strcpy(subtext, language[1447]);
13056 
13057 			// close button
13058 			button = newButton();
13059 			strcpy(button->label, "x");
13060 			button->x = subx2 - 20;
13061 			button->y = suby1;
13062 			button->sizex = 20;
13063 			button->sizey = 20;
13064 			button->action = &openSteamLobbyWaitWindow;
13065 			button->visible = 1;
13066 			button->focused = 1;
13067 			button->key = SDL_SCANCODE_ESCAPE;
13068 			button->joykey = joyimpulses[INJOY_MENU_CANCEL];
13069 
13070 			// cancel button
13071 			button = newButton();
13072 			strcpy(button->label, language[1316]);
13073 			button->sizex = strlen(language[1316]) * 12 + 8;
13074 			button->sizey = 20;
13075 			button->x = subx1 + (subx2 - subx1) / 2 - button->sizex / 2;
13076 			button->y = suby2 - 28;
13077 			button->action = &openSteamLobbyWaitWindow;
13078 			button->visible = 1;
13079 			button->focused = 1;
13080 
13081 			connectingToLobby = true;
13082 			connectingToLobbyWindow = true;
13083 			strncpy( currentLobbyName, "", 31 );
13084 			LobbyHandler.steamValidateAndJoinLobby(*static_cast<CSteamID*>(lobbyToConnectTo));
13085 			cpp_Free_CSteamID(lobbyToConnectTo); //TODO: Bugger this.
13086 			lobbyToConnectTo = NULL;
13087 		}
13088 #endif
13089 	}
13090 	else if ( charcreation_step == 6 )
13091 	{
13092 		// store this character into previous character.
13093 		lastCreatedCharacterSex = stats[0]->sex;
13094 		lastCreatedCharacterClass = client_classes[0];
13095 		lastCreatedCharacterAppearance = stats[0]->appearance;
13096 		lastCreatedCharacterRace = stats[0]->playerRace;
13097 
13098 		if ( multiplayerselect != SINGLE )
13099 		{
13100 			if ( multiplayerSavegameFreeSlot == -1 )
13101 			{
13102 				savegameCurrentFileIndex = 0;
13103 				std::vector<std::tuple<int, int, int, std::string>>::reverse_iterator it = savegamesList.rbegin();
13104 				for ( ; it != savegamesList.rend(); ++it )
13105 				{
13106 					std::tuple<int, int, int, std::string> entry = *it;
13107 					if ( std::get<1>(entry) != SINGLE )
13108 					{
13109 						savegameCurrentFileIndex = std::get<2>(entry);
13110 						break;
13111 					}
13112 				}
13113 			}
13114 			else
13115 			{
13116 				savegameCurrentFileIndex = multiplayerSavegameFreeSlot;
13117 			}
13118 			multiplayerSavegameFreeSlot = -1;
13119 		}
13120 
13121 		if ( multiplayerselect == SINGLE )
13122 		{
13123 			if ( singleplayerSavegameFreeSlot == -1 )
13124 			{
13125 				savegameCurrentFileIndex = 0;
13126 				std::vector<std::tuple<int, int, int, std::string>>::reverse_iterator it = savegamesList.rbegin();
13127 				for ( ; it != savegamesList.rend(); ++it )
13128 				{
13129 					std::tuple<int, int, int, std::string> entry = *it;
13130 					if ( std::get<1>(entry) == SINGLE )
13131 					{
13132 						savegameCurrentFileIndex = std::get<2>(entry);
13133 						break;
13134 					}
13135 				}
13136 			}
13137 			else
13138 			{
13139 				savegameCurrentFileIndex = singleplayerSavegameFreeSlot;
13140 			}
13141 			buttonStartSingleplayer(my);
13142 			singleplayerSavegameFreeSlot = -1;
13143 		}
13144 		else if ( multiplayerselect == SERVER || multiplayerselect == SERVERCROSSPLAY )
13145 		{
13146 #if (defined STEAMWORKS || defined USE_EOS)
13147 			directConnect = false;
13148 #else
13149 			directConnect = true;
13150 #endif
13151 #ifdef STEAMWORKS
13152 			if ( multiplayerselect == SERVERCROSSPLAY )
13153 			{
13154 				LobbyHandler.hostingType = LobbyHandler_t::LobbyServiceType::LOBBY_CROSSPLAY;
13155 				LobbyHandler.setP2PType(LobbyHandler_t::LobbyServiceType::LOBBY_CROSSPLAY);
13156 			}
13157 			else
13158 			{
13159 				LobbyHandler.hostingType = LobbyHandler_t::LobbyServiceType::LOBBY_STEAM;
13160 				LobbyHandler.setP2PType(LobbyHandler_t::LobbyServiceType::LOBBY_STEAM);
13161 			}
13162 #endif
13163 			buttonHostMultiplayer(my);
13164 		}
13165 		else if ( multiplayerselect == CLIENT )
13166 		{
13167 #if (defined STEAMWORKS || defined USE_EOS)
13168 			directConnect = false;
13169 			openSteamLobbyWaitWindow(my);
13170 #else
13171 			directConnect = true;
13172 			buttonJoinMultiplayer(my);
13173 #endif
13174 		}
13175 		else if ( multiplayerselect == DIRECTSERVER )
13176 		{
13177 			directConnect = true;
13178 			buttonHostMultiplayer(my);
13179 		}
13180 		else if ( multiplayerselect == DIRECTCLIENT )
13181 		{
13182 			directConnect = true;
13183 			buttonJoinMultiplayer(my);
13184 		}
13185 	}
13186 }
13187 
13188 // move player backward through creation dialogue
buttonBack(button_t * my)13189 void buttonBack(button_t* my)
13190 {
13191 	charcreation_step--;
13192 	if (charcreation_step < 4)
13193 	{
13194 		playing_random_char = false;
13195 	}
13196 
13197 
13198 	if (charcreation_step == 3)
13199 	{
13200 		// If we've backed out, save what name was input for later
13201 		lastname = (string)inputstr;
13202 		SDL_StopTextInput();
13203 		if ( stats[0]->playerRace != RACE_HUMAN )
13204 		{
13205 			charcreation_step = 2; // skip appearance window for non-human races.
13206 		}
13207 	}
13208 	else if ( charcreation_step == 1 )
13209 	{
13210 		raceSelect = 0; // reset the race selection menu to select sex
13211 		if ( stats[0]->playerRace != RACE_HUMAN )
13212 		{
13213 			stats[0]->clearStats();
13214 			initClass(0);
13215 		}
13216 	}
13217 	else if ( charcreation_step == 0 )
13218 	{
13219 		buttonCloseSubwindow(my);
13220 	}
13221 }
13222 
13223 // start a singleplayer game
buttonStartSingleplayer(button_t * my)13224 void buttonStartSingleplayer(button_t* my)
13225 {
13226 	buttonCloseSubwindow(my);
13227 	multiplayer = SINGLE;
13228 	numplayers = 0;
13229 	introstage = 3;
13230 	fadeout = true;
13231 	if ( !intro )
13232 	{
13233 		// intro is true if starting from main menu, otherwise we're restarting the game.
13234 		pauseGame(2, false);
13235 	}
13236 }
13237 
13238 // host a multiplayer game
buttonHostMultiplayer(button_t * my)13239 void buttonHostMultiplayer(button_t* my)
13240 {
13241 	button_t* button;
13242 
13243 	// refresh keepalive
13244 	int c;
13245 	for ( c = 0; c < MAXPLAYERS; c++ )
13246 	{
13247 		client_keepalive[c] = ticks;
13248 	}
13249 
13250 	if ( !directConnect )
13251 	{
13252 		snprintf(portnumber_char, 6, "%d", DEFAULT_PORT);
13253 		buttonHostLobby(my);
13254 	}
13255 	else
13256 	{
13257 		// close current window
13258 		buttonCloseSubwindow(my);
13259 		list_FreeAll(&button_l);
13260 		deleteallbuttons = true;
13261 
13262 		// open port window
13263 		connect_window = SERVER;
13264 		subwindow = 1;
13265 		subx1 = xres / 2 - 128;
13266 		subx2 = xres / 2 + 128;
13267 		suby1 = yres / 2 - 56;
13268 		suby2 = yres / 2 + 56;
13269 		strcpy(subtext, language[1448]);
13270 
13271 		// close button
13272 		button = newButton();
13273 		strcpy(button->label, "x");
13274 		button->x = subx2 - 20;
13275 		button->y = suby1;
13276 		button->sizex = 20;
13277 		button->sizey = 20;
13278 		button->action = &buttonCloseSubwindow;
13279 		button->visible = 1;
13280 		button->focused = 1;
13281 		button->key = SDL_SCANCODE_ESCAPE;
13282 		button->joykey = joyimpulses[INJOY_MENU_CANCEL];
13283 
13284 		// host button
13285 		button = newButton();
13286 		strcpy(button->label, language[1449]);
13287 		button->sizex = strlen(language[1449]) * 12 + 8;
13288 		button->sizey = 20;
13289 		button->x = subx2 - button->sizex - 4;
13290 		button->y = suby2 - 24;
13291 		button->action = &buttonHostLobby;
13292 		button->visible = 1;
13293 		button->focused = 1;
13294 		button->key = SDL_SCANCODE_RETURN;
13295 		button->joykey = joyimpulses[INJOY_MENU_NEXT];
13296 
13297 		// cancel button
13298 		button = newButton();
13299 		strcpy(button->label, language[1316]);
13300 		button->sizex = strlen(language[1316]) * 12 + 8;
13301 		button->sizey = 20;
13302 		button->x = subx1 + 4;
13303 		button->y = suby2 - 24;
13304 		button->action = &buttonCloseSubwindow;
13305 		button->visible = 1;
13306 		button->focused = 1;
13307 		strcpy(portnumber_char, last_port); //Copy the last used port.
13308 	}
13309 }
13310 
13311 // join a multiplayer game
buttonJoinMultiplayer(button_t * my)13312 void buttonJoinMultiplayer(button_t* my)
13313 {
13314 	button_t* button;
13315 
13316 	// close current window
13317 	buttonCloseSubwindow(my);
13318 	list_FreeAll(&button_l);
13319 	deleteallbuttons = true;
13320 
13321 	// open port window
13322 	connect_window = CLIENT;
13323 	subwindow = 1;
13324 	subx1 = xres / 2 - 210;
13325 	subx2 = xres / 2 + 210;
13326 	suby1 = yres / 2 - 56;
13327 	suby2 = yres / 2 + 56;
13328 	strcpy(subtext, language[1450]);
13329 
13330 	// close button
13331 	button = newButton();
13332 	strcpy(button->label, "x");
13333 	button->x = subx2 - 20;
13334 	button->y = suby1;
13335 	button->sizex = 20;
13336 	button->sizey = 20;
13337 	button->action = &buttonCloseSubwindow;
13338 	button->visible = 1;
13339 	button->focused = 1;
13340 	button->key = SDL_SCANCODE_ESCAPE;
13341 	button->joykey = joyimpulses[INJOY_MENU_CANCEL];
13342 
13343 	// join button
13344 	button = newButton();
13345 	strcpy(button->label, language[1451]);
13346 	button->sizex = strlen(language[1451]) * 12 + 8;
13347 	button->sizey = 20;
13348 	button->x = subx2 - button->sizex - 4;
13349 	button->y = suby2 - 24;
13350 	button->action = &buttonJoinLobby;
13351 	button->visible = 1;
13352 	button->focused = 1;
13353 	button->key = SDL_SCANCODE_RETURN;
13354 	button->joykey = joyimpulses[INJOY_MENU_NEXT];
13355 
13356 	// cancel button
13357 	button = newButton();
13358 	strcpy(button->label, language[1316]);
13359 	button->x = subx1 + 4;
13360 	button->y = suby2 - 24;
13361 	button->sizex = strlen(language[1316]) * 12 + 8;
13362 	button->sizey = 20;
13363 	button->action = &buttonCloseSubwindow;
13364 	button->visible = 1;
13365 	button->focused = 1;
13366 
13367 	strcpy(connectaddress, last_ip); //Copy the last used IP.
13368 }
13369 
13370 // starts a lobby as host
buttonHostLobby(button_t * my)13371 void buttonHostLobby(button_t* my)
13372 {
13373 	button_t* button;
13374 	char *portnumbererr;
13375 	int c;
13376 
13377 	// close current window
13378 	buttonCloseSubwindow(my);
13379 	list_FreeAll(&button_l);
13380 	deleteallbuttons = true;
13381 	portnumber = (Uint16)strtol(portnumber_char, &portnumbererr, 10); // get the port number from the text field
13382 	list_FreeAll(&lobbyChatboxMessages);
13383 
13384 	if ( *portnumbererr != '\0' || portnumber < 1024 )
13385 	{
13386 		printlog("warning: invalid port number: %d\n", portnumber);
13387 		openFailedConnectionWindow(SERVER);
13388 		return;
13389 	}
13390 
13391 	newString(&lobbyChatboxMessages, 0xFFFFFFFF, language[1452]);
13392 	if ( loadingsavegame )
13393 	{
13394 		newString(&lobbyChatboxMessages, 0xFFFFFFFF, language[1453]);
13395 	}
13396 
13397 	// close any existing net interfaces
13398 	closeNetworkInterfaces();
13399 
13400 	if ( !directConnect )
13401 	{
13402 		if ( LobbyHandler.getHostingType() == LobbyHandler_t::LobbyServiceType::LOBBY_STEAM )
13403 		{
13404 #if defined STEAMWORKS
13405 			for ( c = 0; c < MAXPLAYERS; c++ )
13406 			{
13407 				if ( steamIDRemote[c] )
13408 				{
13409 					cpp_Free_CSteamID(steamIDRemote[c]); //TODO: Bugger this.
13410 					steamIDRemote[c] = NULL;
13411 				}
13412 			}
13413 			currentLobbyType = k_ELobbyTypePrivate;
13414 			cpp_SteamMatchmaking_CreateLobby(currentLobbyType, MAXPLAYERS);
13415 #endif
13416 		}
13417 		else if ( LobbyHandler.getHostingType() == LobbyHandler_t::LobbyServiceType::LOBBY_CROSSPLAY )
13418 		{
13419 #ifdef USE_EOS
13420 			EOS.createLobby();
13421 #endif
13422 		}
13423 	}
13424 	else
13425 	{
13426 		// resolve host's address
13427 		if (SDLNet_ResolveHost(&net_server, NULL, portnumber) == -1)
13428 		{
13429 			printlog( "warning: resolving host at localhost:%d has failed.\n", portnumber);
13430 			openFailedConnectionWindow(SERVER);
13431 			return;
13432 		}
13433 
13434 		// open sockets
13435 		if (!(net_sock = SDLNet_UDP_Open(portnumber)))
13436 		{
13437 			printlog( "warning: SDLNet_UDP_open has failed: %s\n", SDLNet_GetError());
13438 			openFailedConnectionWindow(SERVER);
13439 			return;
13440 		}
13441 		if (!(net_tcpsock = SDLNet_TCP_Open(&net_server)))
13442 		{
13443 			printlog( "warning: SDLNet_TCP_open has failed: %s\n", SDLNet_GetError());
13444 			openFailedConnectionWindow(SERVER);
13445 			return;
13446 		}
13447 		tcpset = SDLNet_AllocSocketSet(MAXPLAYERS);
13448 		SDLNet_TCP_AddSocket(tcpset, net_tcpsock);
13449 	}
13450 
13451 	// allocate data for client connections
13452 	net_clients = (IPaddress*) malloc(sizeof(IPaddress) * MAXPLAYERS);
13453 	net_tcpclients = (TCPsocket*) malloc(sizeof(TCPsocket) * MAXPLAYERS);
13454 	for ( c = 0; c < MAXPLAYERS; c++ )
13455 	{
13456 		net_tcpclients[c] = NULL;
13457 	}
13458 
13459 	// allocate packet data
13460 	if (!(net_packet = SDLNet_AllocPacket(NET_PACKET_SIZE)))
13461 	{
13462 		printlog( "warning: packet allocation failed: %s\n", SDLNet_GetError());
13463 		openFailedConnectionWindow(SERVER);
13464 		return;
13465 	}
13466 
13467 	if ( directConnect )
13468 	{
13469 		printlog( "server initialized successfully.\n");
13470 	}
13471 	else
13472 	{
13473 		printlog( "steam lobby opened successfully.\n");
13474 	}
13475 
13476 	// open lobby window
13477 	multiplayer = SERVER;
13478 	lobby_window = true;
13479 	subwindow = 1;
13480 	subx1 = xres / 2 - 400;
13481 	subx2 = xres / 2 + 400;
13482 #ifdef PANDORA
13483 	suby1 = yres / 2 - ((yres==480)?230:290);
13484 	suby2 = yres / 2 + ((yres==480)?230:290);
13485 #else
13486 	suby1 = yres / 2 - 300;
13487 	suby2 = yres / 2 + 300;
13488 #endif
13489 	if ( directConnect )
13490 	{
13491 		strcpy(subtext, language[1454]);
13492 		strcat(subtext, portnumber_char);
13493 		strcat(subtext, language[1456]);
13494 	}
13495 	else
13496 	{
13497 		strcpy(subtext, language[1455]);
13498 		strcat(subtext, language[1456]);
13499 	}
13500 
13501 	// start game button
13502 	button = newButton();
13503 	strcpy(button->label, language[1457]);
13504 	button->sizex = strlen(language[1457]) * 12 + 8;
13505 	button->sizey = 20;
13506 	button->x = subx2 - button->sizex - 4;
13507 	button->y = suby2 - 24;
13508 	button->action = &buttonStartServer;
13509 	button->visible = 1;
13510 	button->focused = 1;
13511 	button->joykey = joyimpulses[INJOY_MENU_NEXT];
13512 
13513 	// disconnect button
13514 	button = newButton();
13515 	strcpy(button->label, language[1311]);
13516 	button->sizex = strlen(language[1311]) * 12 + 8;
13517 	button->sizey = 20;
13518 	button->x = subx1 + 4;
13519 	button->y = suby2 - 24;
13520 	button->action = &buttonDisconnect;
13521 	button->visible = 1;
13522 	button->focused = 1;
13523 	button->key = SDL_SCANCODE_ESCAPE;
13524 	button->joykey = joyimpulses[INJOY_MENU_CANCEL];
13525 	c = button->x + button->sizex + 4;
13526 
13527 	// invite friends button
13528 	if ( !directConnect )
13529 	{
13530 #ifdef STEAMWORKS
13531 		if ( LobbyHandler.getHostingType() == LobbyHandler_t::LobbyServiceType::LOBBY_STEAM )
13532 		{
13533 			button = newButton();
13534 			strcpy(button->label, language[1458]);
13535 			button->sizex = strlen(language[1458]) * 12 + 8;
13536 			button->sizey = 20;
13537 			button->x = c;
13538 			button->y = suby2 - 24;
13539 			button->action = &buttonInviteFriends;
13540 			button->visible = 1;
13541 			button->focused = 1;
13542 		}
13543 #endif
13544 	}
13545 
13546 	if ( loadingsavegame )
13547 	{
13548 		loadGame(clientnum);
13549 	}
13550 
13551 	strcpy(last_port, portnumber_char);
13552 	saveConfig("default.cfg");
13553 }
13554 
13555 // joins a lobby as client
13556 // if direct-ip, this is called directly after pressing join
13557 // otherwise for matchmaking, this is called asynchronously after a matchmaking lobby has been joined
buttonJoinLobby(button_t * my)13558 void buttonJoinLobby(button_t* my)
13559 {
13560 	button_t* button;
13561 	int c;
13562 
13563 	// refresh keepalive
13564 	client_keepalive[0] = ticks;
13565 
13566 	// close current window
13567 	bool temp1 = false;
13568 	bool temp2 = false;
13569 	bool temp3 = false;
13570 	bool temp4 = false;
13571 #ifdef STEAMWORKS
13572 	temp1 = connectingToLobby;
13573 	temp2 = connectingToLobbyWindow;
13574 	if ( !directConnect && LobbyHandler.getJoiningType() == LobbyHandler_t::LobbyServiceType::LOBBY_STEAM )
13575 	{
13576 		joinLobbyWaitingForHostResponse = true;
13577 	}
13578 #endif
13579 #if defined USE_EOS
13580 	temp3 = EOS.bConnectingToLobby;
13581 	temp4 = EOS.bConnectingToLobbyWindow;
13582 	if ( !directConnect && LobbyHandler.getJoiningType() == LobbyHandler_t::LobbyServiceType::LOBBY_CROSSPLAY)
13583 	{
13584 		EOS.bJoinLobbyWaitingForHostResponse = true;
13585 	}
13586 #endif
13587 	if ( directConnect )
13588 	{
13589 		buttonCloseSubwindow(my);
13590 	}
13591 	list_FreeAll(&button_l);
13592 	deleteallbuttons = true;
13593 #ifdef STEAMWORKS
13594 	connectingToLobby = temp1;
13595 	connectingToLobbyWindow = temp2;
13596 #endif
13597 #if defined USE_EOS
13598 	EOS.bConnectingToLobby = temp3;
13599 	EOS.bConnectingToLobbyWindow = temp4;
13600 #endif
13601 
13602 	multiplayer = CLIENT;
13603 	if ( loadingsavegame )
13604 	{
13605 		loadGame(getSaveGameClientnum(false));
13606 	}
13607 
13608 	// open wait window
13609 	list_FreeAll(&lobbyChatboxMessages);
13610 	newString(&lobbyChatboxMessages, 0xFFFFFFFF, language[1452]);
13611 	subwindow = 1;
13612 	subx1 = xres / 2 - 256;
13613 	subx2 = xres / 2 + 256;
13614 	suby1 = yres / 2 - 64;
13615 	suby2 = yres / 2 + 64;
13616 	strcpy(subtext, language[1459]);
13617 
13618 	// close button
13619 	button = newButton();
13620 	strcpy(button->label, "x");
13621 	button->x = subx2 - 20;
13622 	button->y = suby1;
13623 	button->sizex = 20;
13624 	button->sizey = 20;
13625 	button->action = &openSteamLobbyWaitWindow;
13626 	button->visible = 1;
13627 	button->focused = 1;
13628 	button->key = SDL_SCANCODE_ESCAPE;
13629 	button->joykey = joyimpulses[INJOY_MENU_CANCEL];
13630 
13631 	// cancel button
13632 	button = newButton();
13633 	strcpy(button->label, language[1316]);
13634 	button->sizex = strlen(language[1316]) * 12 + 8;
13635 	button->sizey = 20;
13636 	button->x = subx1 + (subx2 - subx1) / 2 - button->sizex / 2;
13637 	button->y = suby2 - 28;
13638 	button->action = &openSteamLobbyWaitWindow;
13639 	button->visible = 1;
13640 	button->focused = 1;
13641 
13642 	if ( directConnect )
13643 	{
13644 		for ( c = 0; c < sizeof(connectaddress); c++ )
13645 		{
13646 			if ( connectaddress[c] == ':' )
13647 			{
13648 				break;
13649 			}
13650 		}
13651 		char *portnumbererr;
13652 		strncpy(address, connectaddress, c); // get the address from the text field
13653 		portnumber = (Uint16)strtol(&connectaddress[c + 1], &portnumbererr, 10); // get the port number from the text field
13654 		if ( *portnumbererr != '\0' || portnumber < 1024 )
13655 		{
13656 			printlog("warning: invalid port number %d.\n", portnumber);
13657 			SDLNet_SetError("Invalid address %s.\nExample: 192.168.0.100:12345", connectaddress);
13658 			openFailedConnectionWindow(CLIENT);
13659 			return;
13660 		}
13661 		strcpy(last_ip, connectaddress);
13662 		saveConfig("default.cfg");
13663 	}
13664 
13665 	// close any existing net interfaces
13666 	closeNetworkInterfaces();
13667 
13668 	if ( directConnect )
13669 	{
13670 		// resolve host's address
13671 		printlog("resolving host's address at %s:%d...\n", address, portnumber);
13672 		if (SDLNet_ResolveHost(&net_server, address, portnumber) == -1)
13673 		{
13674 			printlog( "warning: resolving host at %s:%d has failed.\n", address, portnumber);
13675 			openFailedConnectionWindow(CLIENT);
13676 			return;
13677 		}
13678 
13679 		// open sockets
13680 		printlog("opening TCP and UDP sockets...\n");
13681 		if (!(net_sock = SDLNet_UDP_Open(0)))
13682 		{
13683 			printlog( "warning: SDLNet_UDP_open has failed.\n");
13684 			openFailedConnectionWindow(CLIENT);
13685 			return;
13686 		}
13687 		if (!(net_tcpsock = SDLNet_TCP_Open(&net_server)))
13688 		{
13689 			printlog( "warning: SDLNet_TCP_open has failed.\n");
13690 			openFailedConnectionWindow(CLIENT);
13691 			return;
13692 		}
13693 		tcpset = SDLNet_AllocSocketSet(MAXPLAYERS);
13694 		SDLNet_TCP_AddSocket(tcpset, net_tcpsock);
13695 	}
13696 
13697 	// allocate packet data
13698 	if (!(net_packet = SDLNet_AllocPacket(NET_PACKET_SIZE)))
13699 	{
13700 		printlog( "warning: packet allocation failed.\n");
13701 		openFailedConnectionWindow(CLIENT);
13702 		return;
13703 	}
13704 
13705 	if ( directConnect )
13706 	{
13707 		printlog( "successfully contacted server at %s:%d.\n", address, portnumber);
13708 	}
13709 
13710 	printlog( "submitting join request...\n");
13711 
13712 	// send join request
13713 	strcpy((char*)net_packet->data, "BARONY_JOIN_REQUEST");
13714 	if ( loadingsavegame )
13715 	{
13716 		strncpy((char*)net_packet->data + 19, stats[getSaveGameClientnum(false)]->name, 22);
13717 		SDLNet_Write32((Uint32)client_classes[getSaveGameClientnum(false)], &net_packet->data[42]);
13718 		SDLNet_Write32((Uint32)stats[getSaveGameClientnum(false)]->sex, &net_packet->data[46]);
13719 		Uint32 appearanceAndRace = ((Uint8)stats[getSaveGameClientnum(false)]->appearance << 8); // store in bits 8 - 15
13720 		appearanceAndRace |= (Uint8)stats[getSaveGameClientnum(false)]->playerRace; // store in bits 0 - 7
13721 		SDLNet_Write32(appearanceAndRace, &net_packet->data[50]);
13722 		strcpy((char*)net_packet->data + 54, VERSION);
13723 		net_packet->data[62] = 0;
13724 		net_packet->data[63] = getSaveGameClientnum(false);
13725 	}
13726 	else
13727 	{
13728 		strncpy((char*)net_packet->data + 19, stats[0]->name, 22);
13729 		SDLNet_Write32((Uint32)client_classes[0], &net_packet->data[42]);
13730 		SDLNet_Write32((Uint32)stats[0]->sex, &net_packet->data[46]);
13731 		Uint32 appearanceAndRace = ((Uint8)stats[0]->appearance << 8);
13732 		appearanceAndRace |= ((Uint8)stats[0]->playerRace);
13733 		SDLNet_Write32(appearanceAndRace, &net_packet->data[50]);
13734 		strcpy((char*)net_packet->data + 54, VERSION);
13735 		net_packet->data[62] = 0;
13736 		net_packet->data[63] = 0;
13737 	}
13738 	if ( loadingsavegame )
13739 	{
13740 		// send over the map seed being used
13741 		SDLNet_Write32(getSaveGameMapSeed(false), &net_packet->data[64]);
13742 	}
13743 	else
13744 	{
13745 		SDLNet_Write32(0, &net_packet->data[64]);
13746 	}
13747 	SDLNet_Write32(loadingsavegame, &net_packet->data[68]); // send unique game key
13748 	net_packet->address.host = net_server.host;
13749 	net_packet->address.port = net_server.port;
13750 	net_packet->len = 72;
13751 	if ( !directConnect )
13752 	{
13753 #if (defined STEAMWORKS || defined USE_EOS)
13754 		sendPacket(net_sock, -1, net_packet, 0);
13755 		SDL_Delay(5);
13756 		sendPacket(net_sock, -1, net_packet, 0);
13757 		SDL_Delay(5);
13758 		sendPacket(net_sock, -1, net_packet, 0);
13759 		SDL_Delay(5);
13760 		sendPacket(net_sock, -1, net_packet, 0);
13761 		SDL_Delay(5);
13762 		sendPacket(net_sock, -1, net_packet, 0);
13763 		SDL_Delay(5);
13764 #endif
13765 	}
13766 	else
13767 	{
13768 		sendPacket(net_sock, -1, net_packet, 0);
13769 	}
13770 }
13771 
13772 // starts the game as server
buttonStartServer(button_t * my)13773 void buttonStartServer(button_t* my)
13774 {
13775 	int c;
13776 
13777 	// close window
13778 	buttonCloseSubwindow(my);
13779 
13780 	multiplayer = SERVER;
13781 
13782 	if ( !intro )
13783 	{
13784 		// intro is true if starting from main menu, otherwise we're restarting the game.
13785 		// set the main menu camera to the player camera coordinates if restarting midgame.
13786 		menucam.x = cameras[clientnum].x;
13787 		menucam.y = cameras[clientnum].y;
13788 		menucam.z = cameras[clientnum].z;
13789 		menucam.ang = cameras[clientnum].ang;
13790 		menucam.vang = cameras[clientnum].vang;
13791 	}
13792 
13793 	intro = true;
13794 	introstage = 3;
13795 	numplayers = 0;
13796 	fadeout = true;
13797 
13798 	// send the ok to start
13799 	for ( c = 1; c < MAXPLAYERS; c++ )
13800 	{
13801 		if ( !client_disconnected[c] )
13802 		{
13803 			if ( !loadingsavegame || !intro )
13804 			{
13805 				stats[c]->clearStats();
13806 				initClass(c);
13807 			}
13808 			else
13809 			{
13810 				loadGame(c);
13811 			}
13812 		}
13813 	}
13814 	uniqueGameKey = prng_get_uint();
13815 	if ( !uniqueGameKey )
13816 	{
13817 		uniqueGameKey++;
13818 	}
13819 	for ( c = 1; c < MAXPLAYERS; c++ )
13820 	{
13821 		if ( client_disconnected[c] )
13822 		{
13823 			continue;
13824 		}
13825 		strcpy((char*)net_packet->data, "BARONY_GAME_START");
13826 		SDLNet_Write32(svFlags, &net_packet->data[17]);
13827 		SDLNet_Write32(uniqueGameKey, &net_packet->data[21]);
13828 		if ( loadingsavegame == 0 )
13829 		{
13830 			net_packet->data[25] = 0;
13831 		}
13832 		else
13833 		{
13834 			net_packet->data[25] = 1;
13835 		}
13836 		net_packet->address.host = net_clients[c - 1].host;
13837 		net_packet->address.port = net_clients[c - 1].port;
13838 		net_packet->len = 26;
13839 		sendPacketSafe(net_sock, -1, net_packet, c - 1);
13840 	}
13841 }
13842 
13843 // opens the steam dialog to invite friends
buttonInviteFriends(button_t * my)13844 void buttonInviteFriends(button_t* my)
13845 {
13846 #ifdef STEAMWORKS
13847 	if (SteamUser()->BLoggedOn() && currentLobby)
13848 	{
13849 		SteamFriends()->ActivateGameOverlayInviteDialog(*static_cast<CSteamID*>(currentLobby));
13850 	}
13851 #else
13852 #ifdef USE_EOS
13853 	EOS.showFriendsOverlay();
13854 #endif
13855 #endif
13856 }
13857 
13858 // disconnects from whatever lobby the game is connected to
buttonDisconnect(button_t * my)13859 void buttonDisconnect(button_t* my)
13860 {
13861 	int c;
13862 
13863 	if ( multiplayer == SERVER )
13864 	{
13865 		// send disconnect message to clients
13866 		for ( c = 1; c < MAXPLAYERS; c++ )
13867 		{
13868 			if ( client_disconnected[c] )
13869 			{
13870 				continue;
13871 			}
13872 			strcpy((char*)net_packet->data, "PLAYERDISCONNECT");
13873 			net_packet->data[16] = clientnum;
13874 			net_packet->address.host = net_clients[c - 1].host;
13875 			net_packet->address.port = net_clients[c - 1].port;
13876 			net_packet->len = 17;
13877 			sendPacketSafe(net_sock, -1, net_packet, c - 1);
13878 		}
13879 	}
13880 	else
13881 	{
13882 		// send disconnect message to server
13883 		strcpy((char*)net_packet->data, "PLAYERDISCONNECT");
13884 		net_packet->data[16] = clientnum;
13885 		net_packet->address.host = net_server.host;
13886 		net_packet->address.port = net_server.port;
13887 		net_packet->len = 17;
13888 		sendPacketSafe(net_sock, -1, net_packet, 0);
13889 	}
13890 
13891 	// reset multiplayer status
13892 	multiplayer = SINGLE;
13893 	stats[0]->sex = stats[clientnum]->sex;
13894 	client_classes[0] = client_classes[clientnum];
13895 	strcpy(stats[0]->name, stats[clientnum]->name);
13896 	clientnum = 0;
13897 	client_disconnected[0] = false;
13898 	for ( c = 1; c < MAXPLAYERS; c++ )
13899 	{
13900 		client_disconnected[c] = true;
13901 	}
13902 
13903 	// close any existing net interfaces
13904 	closeNetworkInterfaces();
13905 #ifdef STEAMWORKS
13906 	if ( currentLobby )
13907 	{
13908 		SteamMatchmaking()->LeaveLobby(*static_cast<CSteamID*>(currentLobby));
13909 		cpp_Free_CSteamID(currentLobby); //TODO: Bugger this.
13910 		currentLobby = NULL;
13911 	}
13912 #endif
13913 #if defined USE_EOS
13914 	if ( EOS.CurrentLobbyData.currentLobbyIsValid() )
13915 	{
13916 		EOS.leaveLobby();
13917 	}
13918 #endif
13919 
13920 	// close lobby window
13921 	buttonCloseSubwindow(my);
13922 }
13923 
13924 // open the video tab in the settings window
buttonVideoTab(button_t * my)13925 void buttonVideoTab(button_t* my)
13926 {
13927 	changeSettingsTab(SETTINGS_VIDEO_TAB);
13928 }
13929 
13930 // open the audio tab in the settings window
buttonAudioTab(button_t * my)13931 void buttonAudioTab(button_t* my)
13932 {
13933 	changeSettingsTab(SETTINGS_AUDIO_TAB);
13934 }
13935 
13936 // open the keyboard tab in the settings window
buttonKeyboardTab(button_t * my)13937 void buttonKeyboardTab(button_t* my)
13938 {
13939 	changeSettingsTab(SETTINGS_KEYBOARD_TAB);
13940 }
13941 
13942 // open the mouse tab in the settings window
buttonMouseTab(button_t * my)13943 void buttonMouseTab(button_t* my)
13944 {
13945 	changeSettingsTab(SETTINGS_MOUSE_TAB);
13946 }
13947 
13948 //Open the gamepad bindings tab in the settings window
buttonGamepadBindingsTab(button_t * my)13949 void buttonGamepadBindingsTab(button_t* my)
13950 {
13951 	changeSettingsTab(SETTINGS_GAMEPAD_BINDINGS_TAB);
13952 }
13953 
13954 //Open the general gamepad settings tab in the settings window
buttonGamepadSettingsTab(button_t * my)13955 void buttonGamepadSettingsTab(button_t* my)
13956 {
13957 	changeSettingsTab(SETTINGS_GAMEPAD_SETTINGS_TAB);
13958 }
13959 
13960 // open the misc tab in the settings window
buttonMiscTab(button_t * my)13961 void buttonMiscTab(button_t* my)
13962 {
13963 	changeSettingsTab(SETTINGS_MISC_TAB);
13964 }
13965 
applySettings()13966 void applySettings()
13967 {
13968 	int c;
13969 
13970 	// set video options
13971 	fov = settings_fov;
13972 	smoothlighting = settings_smoothlighting;
13973 	oldFullscreen = fullscreen;
13974 	oldBorderless = borderless;
13975 	fullscreen = settings_fullscreen;
13976 	borderless = settings_borderless;
13977 	shaking = settings_shaking;
13978 	bobbing = settings_bobbing;
13979 	spawn_blood = settings_spawn_blood;
13980 	flickerLights = settings_light_flicker;
13981 	oldVerticalSync = verticalSync;
13982 	verticalSync = settings_vsync;
13983 	showStatusEffectIcons = settings_status_effect_icons;
13984 	colorblind = settings_colorblind;
13985 	oldGamma = vidgamma;
13986 	vidgamma = settings_gamma;
13987 	fpsLimit = settings_fps;
13988 	oldXres = xres;
13989 	oldYres = yres;
13990 	xres = settings_xres;
13991 	yres = settings_yres;
13992 
13993 	if ( svFlags != settings_svFlags && multiplayer != CLIENT )
13994 	{
13995 		svFlags = settings_svFlags;
13996 		if ( !intro && multiplayer == SERVER )
13997 		{
13998 			// update client flags
13999 			strcpy((char*)net_packet->data, "SVFL");
14000 			SDLNet_Write32(svFlags, &net_packet->data[4]);
14001 			net_packet->len = 8;
14002 
14003 			for ( int c = 1; c < MAXPLAYERS; ++c )
14004 			{
14005 				if ( client_disconnected[c] )
14006 				{
14007 					continue;
14008 				}
14009 				net_packet->address.host = net_clients[c - 1].host;
14010 				net_packet->address.port = net_clients[c - 1].port;
14011 				sendPacketSafe(net_sock, -1, net_packet, c - 1);
14012 				messagePlayer(c, language[276]);
14013 			}
14014 			messagePlayer(clientnum, language[276]);
14015 		}
14016 	}
14017 
14018 	minimapTransparencyForeground = settings_minimap_transparency_foreground;
14019 	minimapTransparencyBackground = settings_minimap_transparency_background;
14020 	minimapScale = settings_minimap_scale;
14021 	minimapObjectZoom = settings_minimap_object_zoom;
14022 
14023 	cameras[0].winx = 0;
14024 	cameras[0].winy = 0;
14025 	cameras[0].winw = std::min(cameras[0].winw, xres);
14026 	cameras[0].winh = std::min(cameras[0].winh, yres);
14027 	if(xres!=oldXres || yres!=oldYres || oldFullscreen!=fullscreen || oldGamma!=vidgamma
14028 		|| oldVerticalSync != verticalSync || oldBorderless != borderless )
14029 	{
14030 		if ( !changeVideoMode() )
14031 		{
14032 			printlog("critical error! Attempting to abort safely...\n");
14033 			mainloop = 0;
14034 		}
14035 		if ( zbuffer != NULL )
14036 		{
14037 			free(zbuffer);
14038 		}
14039 		zbuffer = (real_t*) malloc(sizeof(real_t) * xres * yres);
14040 		if ( clickmap != NULL )
14041 		{
14042 			free(clickmap);
14043 		}
14044 		clickmap = (Entity**) malloc(sizeof(Entity*)*xres * yres);
14045 	}
14046 	// set audio options
14047 	sfxvolume = settings_sfxvolume;
14048 	sfxAmbientVolume = settings_sfxAmbientVolume;
14049 	sfxEnvironmentVolume = settings_sfxEnvironmentVolume;
14050 	musvolume = settings_musvolume;
14051 	minimapPingMute = settings_minimap_ping_mute;
14052 	mute_audio_on_focus_lost = settings_mute_audio_on_focus_lost;
14053 	mute_player_monster_sounds = settings_mute_player_monster_sounds;
14054 
14055 #ifdef USE_FMOD
14056 	FMOD_ChannelGroup_SetVolume(music_group, musvolume / 128.f);
14057 	FMOD_ChannelGroup_SetVolume(sound_group, sfxvolume / 128.f);
14058 	FMOD_ChannelGroup_SetVolume(soundAmbient_group, sfxAmbientVolume / 128.f);
14059 	FMOD_ChannelGroup_SetVolume(soundEnvironment_group, sfxEnvironmentVolume / 128.f);
14060 #elif defined USE_OPENAL
14061 	OPENAL_ChannelGroup_SetVolume(music_group, musvolume / 128.f);
14062 	OPENAL_ChannelGroup_SetVolume(sound_group, sfxvolume / 128.f);
14063 	OPENAL_ChannelGroup_SetVolume(soundAmbient_group, sfxAmbientVolume / 128.f);
14064 	OPENAL_ChannelGroup_SetVolume(soundEnvironment_group, sfxEnvironmentVolume / 128.f);
14065 #endif
14066 
14067 	// set keyboard options
14068 	for (c = 0; c < NUMIMPULSES; c++)
14069 	{
14070 		impulses[c] = settings_impulses[c];
14071 	}
14072 	for (c = 0; c < NUM_JOY_IMPULSES; c++)
14073 	{
14074 		joyimpulses[c] = settings_joyimpulses[c];
14075 	}
14076 
14077 	// set mouse options
14078 	reversemouse = settings_reversemouse;
14079 	smoothmouse = settings_smoothmouse;
14080 	disablemouserotationlimit = settings_disablemouserotationlimit;
14081 	mousespeed = settings_mousespeed;
14082 
14083 	// set misc options
14084 	broadcast = settings_broadcast;
14085 	nohud = settings_nohud;
14086 
14087 	auto_hotbar_new_items = settings_auto_hotbar_new_items;
14088 	for ( c = 0; c < NUM_HOTBAR_CATEGORIES; ++c )
14089 	{
14090 		auto_hotbar_categories[c] = settings_auto_hotbar_categories[c];
14091 	}
14092 	for ( c = 0; c < NUM_AUTOSORT_CATEGORIES; ++c )
14093 	{
14094 		autosort_inventory_categories[c] = settings_autosort_inventory_categories[c];
14095 	}
14096 	hotbar_numkey_quick_add = settings_hotbar_numkey_quick_add;
14097 	disable_messages = settings_disable_messages;
14098 	right_click_protect = settings_right_click_protect;
14099 	auto_appraise_new_items = settings_auto_appraise_new_items;
14100 	lock_right_sidebar = settings_lock_right_sidebar;
14101 	show_game_timer_always = settings_show_game_timer_always;
14102 
14103 	gamepad_leftx_invert = settings_gamepad_leftx_invert;
14104 	gamepad_lefty_invert = settings_gamepad_lefty_invert;
14105 	gamepad_rightx_invert = settings_gamepad_rightx_invert;
14106 	gamepad_righty_invert = settings_gamepad_righty_invert;
14107 	gamepad_menux_invert = settings_gamepad_menux_invert;
14108 	gamepad_menuy_invert = settings_gamepad_menuy_invert;
14109 
14110 
14111 	gamepad_deadzone = settings_gamepad_deadzone;
14112 	gamepad_rightx_sensitivity = settings_gamepad_rightx_sensitivity;
14113 	gamepad_righty_sensitivity = settings_gamepad_righty_sensitivity;
14114 	gamepad_menux_sensitivity = settings_gamepad_menux_sensitivity;
14115 	gamepad_menuy_sensitivity = settings_gamepad_menuy_sensitivity;
14116 
14117 	uiscale_charactersheet = settings_uiscale_charactersheet;
14118 	uiscale_skillspage = settings_uiscale_skillspage;
14119 	uiscale_inventory = settings_uiscale_inventory;
14120 	uiscale_hotbar = settings_uiscale_hotbar;
14121 	uiscale_chatlog = settings_uiscale_chatlog;
14122 	uiscale_playerbars = settings_uiscale_playerbars;
14123 	hide_statusbar = settings_hide_statusbar;
14124 	hide_playertags = settings_hide_playertags;
14125 	show_skill_values = settings_show_skill_values;
14126 	if ( net_handler && disableMultithreadedSteamNetworking != settings_disableMultithreadedSteamNetworking )
14127 	{
14128 		net_handler->toggleMultithreading(settings_disableMultithreadedSteamNetworking);
14129 	}
14130 	disableMultithreadedSteamNetworking = settings_disableMultithreadedSteamNetworking;
14131 	disableFPSLimitOnNetworkMessages = settings_disableFPSLimitOnNetworkMessages;
14132 #ifdef USE_EOS
14133 	if ( LobbyHandler.settings_crossplayEnabled )
14134 	{
14135 		if ( !LobbyHandler.crossplayEnabled )
14136 		{
14137 			LobbyHandler.settings_crossplayEnabled = false;
14138 			EOS.CrossplayAccountManager.trySetupFromSettingsMenu = true;
14139 		}
14140 	}
14141 	else
14142 	{
14143 		if ( LobbyHandler.crossplayEnabled )
14144 		{
14145 			LobbyHandler.crossplayEnabled = false;
14146 			EOS.CrossplayAccountManager.logOut = true;
14147 		}
14148 	}
14149 #endif
14150 	saveConfig("default.cfg");
14151 }
14152 
openConfirmResolutionWindow()14153 void openConfirmResolutionWindow()
14154 {
14155 	mousestatus[SDL_BUTTON_LEFT] = 0;
14156 	keystatus[SDL_SCANCODE_RETURN] = 0;
14157 	*inputPressed(joyimpulses[INJOY_MENU_NEXT]) = 0;
14158 	playSound(139, 64);
14159 
14160 	//Create confirmation window
14161 	subwindow = 1;
14162 	subx1 = xres / 2 - 128;
14163 	subx2 = xres / 2 + 128;
14164 	suby1 = yres / 2 - 40;
14165 	suby2 = yres / 2 + 40;
14166 	strcpy(subtext, "Testing resolution.\nWill revert in 10 seconds.");
14167 
14168 	//Accept button
14169 	button_t* button = newButton();
14170 	strcpy(button->label, "Accept");
14171 	button->x = subx1 + 8;
14172 	button->y = suby2 - 28;
14173 	button->sizex = strlen("Accept") * 12 + 8;
14174 	button->sizey = 20;
14175 	button->action = &buttonAcceptResolution;
14176 	button->visible = 1;
14177 	button->focused = 1;
14178 	button->key = SDL_SCANCODE_RETURN;
14179 	button->joykey = joyimpulses[INJOY_MENU_NEXT];
14180 
14181 	//Revert button
14182 	button = newButton();
14183 	strcpy(button->label, "Revert");
14184 	button->x = subx2 - strlen("Revert") * 12 - 16;
14185 	button->y = suby2 - 28;
14186 	button->sizex = strlen("Revert") * 12 + 8;
14187 	button->sizey = 20;
14188 	button->action = &buttonRevertResolution;
14189 	button->visible = 1;
14190 	button->focused = 1;
14191 	button->key = SDL_SCANCODE_ESCAPE;
14192 	button->joykey = joyimpulses[INJOY_MENU_CANCEL];
14193 	revertResolutionButton = button;
14194 
14195 	resolutionConfirmationTimer = SDL_GetTicks();
14196 	confirmResolutionWindow = true;
14197 }
14198 
buttonAcceptResolution(button_t * my)14199 void buttonAcceptResolution(button_t* my)
14200 {
14201 	confirmResolutionWindow = false;
14202 	buttonCloseSubwindow(my);
14203 	list_FreeAll(&button_l);
14204 	deleteallbuttons = true;
14205 	revertResolutionButton = nullptr;
14206 
14207 	applySettings();
14208 }
14209 
buttonRevertResolution(button_t * my)14210 void buttonRevertResolution(button_t* my)
14211 {
14212 	revertResolution();
14213 
14214 	confirmResolutionWindow = false;
14215 	buttonCloseSubwindow(my);
14216 	list_FreeAll(&button_l);
14217 	deleteallbuttons = true;
14218 	revertResolutionButton = nullptr;
14219 }
14220 
revertResolution()14221 void revertResolution()
14222 {
14223 	settings_xres = oldXres;
14224 	settings_yres = oldYres;
14225 
14226 	applySettings();
14227 }
14228 
14229 // settings accept button
buttonSettingsAccept(button_t * my)14230 void buttonSettingsAccept(button_t* my)
14231 {
14232 	applySettings();
14233 
14234 	if ( resolutionChanged )
14235 	{
14236 		buttonCloseSettingsSubwindow(my);
14237 		resolutionChanged = false;
14238 		list_FreeAll(&button_l);
14239 		deleteallbuttons = true;
14240 		openConfirmResolutionWindow();
14241 	}
14242 	else
14243 	{
14244 		// we need to reposition the settings window now.
14245 		buttonCloseSubwindow(my);
14246 		list_FreeAll(&button_l);
14247 		deleteallbuttons = true;
14248 		openSettingsWindow();
14249 	}
14250 }
14251 
14252 // settings okay button
buttonSettingsOK(button_t * my)14253 void buttonSettingsOK(button_t* my)
14254 {
14255 	buttonSettingsAccept(my);
14256 	if ( !confirmResolutionWindow )
14257 	{
14258 		buttonCloseSubwindow(my);
14259 	}
14260 }
14261 
14262 // next score button (statistics window)
buttonScoreNext(button_t * my)14263 void buttonScoreNext(button_t* my)
14264 {
14265 	if ( scoreDisplayMultiplayer )
14266 	{
14267 		score_window = std::min<int>(score_window + 1, std::max<Uint32>(1, list_Size(&topscoresMultiplayer)));
14268 	}
14269 	else
14270 	{
14271 		score_window = std::min<int>(score_window + 1, std::max<Uint32>(1, list_Size(&topscores)));
14272 	}
14273 	loadScore(score_window - 1);
14274 	camera_charsheet_offsetyaw = (330) * PI / 180;
14275 }
14276 
14277 // previous score button (statistics window)
buttonScorePrev(button_t * my)14278 void buttonScorePrev(button_t* my)
14279 {
14280 	score_window = std::max(score_window - 1, 1);
14281 	loadScore(score_window - 1);
14282 	camera_charsheet_offsetyaw = (330) * PI / 180;
14283 }
14284 
buttonScoreToggle(button_t * my)14285 void buttonScoreToggle(button_t* my)
14286 {
14287 	score_window = 1;
14288 	camera_charsheet_offsetyaw = (330) * PI / 180;
14289 	scoreDisplayMultiplayer = !scoreDisplayMultiplayer;
14290 	loadScore(score_window - 1);
14291 }
14292 
14293 #ifdef STEAMWORKS
14294 
buttonLeaderboardFetch(button_t * my)14295 void buttonLeaderboardFetch(button_t* my)
14296 {
14297 	if ( g_SteamLeaderboards )
14298 	{
14299 		g_SteamLeaderboards->DownloadScores(g_SteamLeaderboards->LeaderboardView.requestType,
14300 			g_SteamLeaderboards->LeaderboardView.rangeStart, g_SteamLeaderboards->LeaderboardView.rangeEnd);
14301 	}
14302 }
14303 
buttonLeaderboardNextCategory(button_t * my)14304 void buttonLeaderboardNextCategory(button_t* my)
14305 {
14306 	if ( g_SteamLeaderboards )
14307 	{
14308 		int offset = (g_SteamLeaderboards->b_ShowDLCScores ? 16 : 0);
14309 		g_SteamLeaderboards->LeaderboardView.boardToDownload =
14310 			std::min(g_SteamLeaderboards->LeaderboardView.boardToDownload + 1, (int)LEADERBOARD_MULTIPLAYER_HELL_SCORE + offset);
14311 		g_SteamLeaderboards->b_ScoresDownloaded = false;
14312 		score_leaderboard_window = 1;
14313 		g_SteamLeaderboards->FindLeaderboard(g_SteamLeaderboards->leaderboardNames[g_SteamLeaderboards->LeaderboardView.boardToDownload].c_str());
14314 	}
14315 }
14316 
buttonLeaderboardPrevCategory(button_t * my)14317 void buttonLeaderboardPrevCategory(button_t* my)
14318 {
14319 	if ( g_SteamLeaderboards )
14320 	{
14321 		int offset = (g_SteamLeaderboards->b_ShowDLCScores ? 16 : 0);
14322 		g_SteamLeaderboards->LeaderboardView.boardToDownload =
14323 			std::max(g_SteamLeaderboards->LeaderboardView.boardToDownload - 1, (int)LEADERBOARD_NORMAL_TIME + offset);
14324 		g_SteamLeaderboards->b_ScoresDownloaded = false;
14325 		score_leaderboard_window = 1;
14326 		g_SteamLeaderboards->FindLeaderboard(g_SteamLeaderboards->leaderboardNames[g_SteamLeaderboards->LeaderboardView.boardToDownload].c_str());
14327 	}
14328 }
14329 
buttonDLCLeaderboardFetch(button_t * my)14330 void buttonDLCLeaderboardFetch(button_t* my)
14331 {
14332 	if ( g_SteamLeaderboards )
14333 	{
14334 		if ( g_SteamLeaderboards->b_ShowDLCScores )
14335 		{
14336 			if ( g_SteamLeaderboards->LeaderboardView.boardToDownload > LEADERBOARD_MULTIPLAYER_HELL_SCORE )
14337 			{
14338 				g_SteamLeaderboards->LeaderboardView.boardToDownload -= 16;
14339 			}
14340 		}
14341 		else
14342 		{
14343 			if ( g_SteamLeaderboards->LeaderboardView.boardToDownload <= LEADERBOARD_MULTIPLAYER_HELL_SCORE )
14344 			{
14345 				g_SteamLeaderboards->LeaderboardView.boardToDownload += 16;
14346 			}
14347 		}
14348 		g_SteamLeaderboards->b_ShowDLCScores = !g_SteamLeaderboards->b_ShowDLCScores;
14349 		g_SteamLeaderboards->b_ScoresDownloaded = false;
14350 		score_leaderboard_window = 1;
14351 		g_SteamLeaderboards->FindLeaderboard(g_SteamLeaderboards->leaderboardNames[g_SteamLeaderboards->LeaderboardView.boardToDownload].c_str());
14352 	}
14353 
14354 }
14355 
buttonOpenSteamLeaderboards(button_t * my)14356 void buttonOpenSteamLeaderboards(button_t* my)
14357 {
14358 	if ( g_SteamLeaderboards )
14359 	{
14360 		// close current window
14361 		buttonCloseSubwindow(nullptr);
14362 		list_FreeAll(&button_l);
14363 		deleteallbuttons = true;
14364 
14365 		// create confirmation window
14366 		subwindow = 1;
14367 		subx1 = xres / 2 - 390;
14368 		subx2 = xres / 2 + 390;
14369 		suby1 = yres / 2 - 300;
14370 		suby2 = yres / 2 + 300;
14371 		score_leaderboard_window = 1;
14372 		g_SteamLeaderboards->LeaderboardView.boardToDownload = LEADERBOARD_NORMAL_TIME;
14373 		g_SteamLeaderboards->b_ScoresDownloaded = false;
14374 		g_SteamLeaderboards->FindLeaderboard(g_SteamLeaderboards->leaderboardNames[g_SteamLeaderboards->LeaderboardView.boardToDownload].c_str());
14375 
14376 		strcpy(subtext, "Steam Leaderboards");
14377 
14378 		// close button
14379 		button_t* button = newButton();
14380 		strcpy(button->label, "x");
14381 		button->x = subx2 - 20;
14382 		button->y = suby1 + 4;
14383 		button->sizex = 20;
14384 		button->sizey = 20;
14385 		button->action = &buttonCloseSubwindow;
14386 		button->visible = 1;
14387 		button->focused = 1;
14388 		button->key = SDL_SCANCODE_ESCAPE;
14389 		button->joykey = joyimpulses[INJOY_MENU_CANCEL];
14390 
14391 		// next button
14392 		button = newButton();
14393 		strcpy(button->label, ">");
14394 		button->sizex = strlen(">") * 12 + 8;
14395 		button->sizey = 20;
14396 		button->x = subx2 - button->sizex - 4;
14397 		button->y = suby2 - 24;
14398 		button->action = &buttonLeaderboardNextCategory;
14399 		button->visible = 1;
14400 		button->focused = 1;
14401 		button->key = SDL_SCANCODE_RIGHT;
14402 		button->joykey = joyimpulses[INJOY_DPAD_RIGHT];
14403 
14404 		// previous button
14405 		button = newButton();
14406 		strcpy(button->label, "<");
14407 		button->sizex = strlen("<") * 12 + 8;
14408 		button->sizey = 20;
14409 		button->x = subx1 + 4;
14410 		button->y = suby2 - 24;
14411 		button->action = &buttonLeaderboardPrevCategory;
14412 		button->visible = 1;
14413 		button->focused = 1;
14414 		button->key = SDL_SCANCODE_LEFT;
14415 		button->joykey = joyimpulses[INJOY_DPAD_LEFT];
14416 
14417 		// fetch leaderboards
14418 		button = newButton();
14419 		strcpy(button->label, "Fetch Leaderboard");
14420 		button->y = suby1 + 3 * TTF12_HEIGHT + 8;
14421 		button->sizex = 25 * TTF12_WIDTH + 8;
14422 		button->x = subx2 - button->sizex - 8;
14423 		button->sizey = 32;
14424 		button->action = &buttonLeaderboardFetch;
14425 		button->visible = 1;
14426 		button->focused = 1;
14427 
14428 		// fetch DLC leaderboards
14429 		button_t* dlcScoreButton = newButton();
14430 		strcpy(dlcScoreButton->label, "Toggle DLC Scores");
14431 		dlcScoreButton->y = suby1 + 3 * TTF12_HEIGHT + 8;
14432 		dlcScoreButton->sizex = 25 * TTF12_WIDTH + 8;
14433 		dlcScoreButton->x = button->x - dlcScoreButton->sizex - 8;
14434 		dlcScoreButton->sizey = 32;
14435 		dlcScoreButton->action = &buttonDLCLeaderboardFetch;
14436 		dlcScoreButton->visible = 1;
14437 		dlcScoreButton->focused = 1;
14438 	}
14439 }
14440 #endif
14441 
buttonOpenScoresWindow(button_t * my)14442 void buttonOpenScoresWindow(button_t* my)
14443 {
14444 	// create statistics window
14445 	clientnum = 0;
14446 	subwindow = 1;
14447 	score_window = 1;
14448 	camera_charsheet_offsetyaw = (330) * PI / 180;
14449 	loadScore(0);
14450 	subx1 = xres / 2 - 400;
14451 	subx2 = xres / 2 + 400;
14452 #ifdef PANDORA
14453 	suby1 = yres / 2 - ((yres == 480) ? 200 : 240);
14454 	suby2 = yres / 2 + ((yres == 480) ? 200 : 240);
14455 #else
14456 	suby1 = yres / 2 - 260;
14457 	suby2 = yres / 2 + 260;
14458 #endif
14459 	strcpy(subtext, "");
14460 
14461 	// close button
14462 	button_t* button = newButton();
14463 	strcpy(button->label, "x");
14464 	button->x = subx2 - 20;
14465 	button->y = suby1 + 4;
14466 	button->sizex = 20;
14467 	button->sizey = 20;
14468 	button->action = &buttonCloseSubwindow;
14469 	button->visible = 1;
14470 	button->focused = 1;
14471 	button->key = SDL_SCANCODE_ESCAPE;
14472 	button->joykey = joyimpulses[INJOY_MENU_CANCEL];
14473 
14474 	// next button
14475 	button = newButton();
14476 	strcpy(button->label, ">");
14477 	button->sizex = strlen(">") * 12 + 8;
14478 	button->sizey = 20;
14479 	button->x = subx2 - button->sizex - 4;
14480 	button->y = suby2 - 24;
14481 	button->action = &buttonScoreNext;
14482 	button->visible = 1;
14483 	button->focused = 1;
14484 	button->key = SDL_SCANCODE_RIGHT;
14485 	button->joykey = joyimpulses[INJOY_DPAD_RIGHT];
14486 
14487 	// previous button
14488 	button = newButton();
14489 	strcpy(button->label, "<");
14490 	button->sizex = strlen("<") * 12 + 8;
14491 	button->sizey = 20;
14492 	button->x = subx1 + 4;
14493 	button->y = suby2 - 24;
14494 	button->action = &buttonScorePrev;
14495 	button->visible = 1;
14496 	button->focused = 1;
14497 	button->key = SDL_SCANCODE_LEFT;
14498 	button->joykey = joyimpulses[INJOY_DPAD_LEFT];
14499 
14500 	// multiplayer scores toggle button
14501 	button = newButton();
14502 	strcpy(button->label, "");
14503 	button->sizex = strlen("show multiplayer") * 12 + 8;
14504 	button->sizey = 20;
14505 	button->x = subx2 - 44 - strlen("show multiplayer") * 12;
14506 	button->y = suby1 + 4;
14507 	button->action = &buttonScoreToggle;
14508 	button->visible = 1;
14509 	button->focused = 1;
14510 
14511 	// delete single score button
14512 	button = newButton();
14513 	strcpy(button->label, "delete score");
14514 	button->sizex = strlen("delete score") * 12 + 8;
14515 	button->sizey = 20;
14516 	button->x = subx2 - 44 - (strlen("delete score") + strlen("show multiplayer") + 1) * 12;
14517 	button->y = suby1 + 4;
14518 	button->action = &buttonDeleteScoreWindow;
14519 	button->visible = 1;
14520 	button->focused = 1;
14521 
14522 	// open steam leaderboards button
14523 #ifdef STEAMWORKS
14524 	button = newButton();
14525 	strcpy(button->label, language[3095]);
14526 	button->sizex = strlen(language[3095]) * 12 + 8;
14527 	button->sizey = 20;
14528 	button->x = subx2 - 44 - strlen(language[3095]) * 12;
14529 	button->y = suby2 - 8 - TTF12_HEIGHT;
14530 	button->action = &buttonOpenSteamLeaderboards;
14531 	button->visible = 1;
14532 	button->focused = 1;
14533 #endif // STEAMWORKS
14534 }
14535 
buttonDeleteCurrentScore(button_t * my)14536 void buttonDeleteCurrentScore(button_t* my)
14537 {
14538 	node_t* node = nullptr;
14539 	if ( score_window_delete_multiplayer )
14540 	{
14541 		node = list_Node(&topscoresMultiplayer, score_window_to_delete - 1);
14542 		if ( node )
14543 		{
14544 			list_RemoveNode(node);
14545 			score_window_to_delete = std::max(score_window_to_delete - 1, 1);
14546 		}
14547 	}
14548 	else
14549 	{
14550 		node = list_Node(&topscores, score_window_to_delete - 1);
14551 		if ( node )
14552 		{
14553 			list_RemoveNode(node);
14554 			score_window_to_delete = std::max(score_window_to_delete - 1, 1);
14555 		}
14556 	}
14557 }
14558 
14559 // handles slider
doSlider(int x,int y,int dots,int minvalue,int maxvalue,int increment,int * var,SDL_Surface * slider_font,int slider_font_char_width)14560 void doSlider(int x, int y, int dots, int minvalue, int maxvalue, int increment, int* var, SDL_Surface* slider_font, int slider_font_char_width)
14561 {
14562 	int c;
14563 
14564 	// build bar
14565 	strcpy(tempstr, "| ");
14566 	for ( c = 0; c < dots; c++ )
14567 	{
14568 		strcat(tempstr, ". ");
14569 	}
14570 	strcat(tempstr, "| %d");
14571 	printTextFormatted(slider_font, x, y, tempstr, *var);
14572 
14573 	// control
14574 	int range = maxvalue - minvalue;
14575 	int sliderLength = ((strlen(tempstr) - 4) * (slider_font->w / slider_font_char_width));
14576 	if ( mousestatus[SDL_BUTTON_LEFT] )
14577 	{
14578 		if ( omousex >= x && omousex < x + sliderLength + (slider_font->w / slider_font_char_width) )
14579 		{
14580 			if ( omousey >= y - (slider_font->h / slider_font_char_width) / 2 && omousey < y + ((slider_font->h / slider_font_char_width) / 2) * 3 )
14581 			{
14582 				*var = ((real_t)(mousex - x - (slider_font->w / slider_font_char_width) / 2) / sliderLength) * range + minvalue;
14583 				if ( increment )
14584 				{
14585 					*var += increment / 2;
14586 					*var /= increment;
14587 					*var *= increment;
14588 				}
14589 				*var = std::min(std::max(minvalue, *var), maxvalue);
14590 			}
14591 		}
14592 	}
14593 
14594 	// draw slider
14595 	int sliderx = x + (slider_font->w / slider_font_char_width) / 2;
14596 	sliderx += (((real_t)(*var) - minvalue) / range) * sliderLength;
14597 	drawWindowFancy( sliderx - (slider_font->w / slider_font_char_width) / 2, y - (slider_font->h / slider_font_char_width) / 2, sliderx + (slider_font->w / slider_font_char_width) / 2, y + ((slider_font->h / slider_font_char_width) / 2) * 3);
14598 }
14599 
14600 // handles slider (float)
doSliderF(int x,int y,int dots,real_t minvalue,real_t maxvalue,real_t increment,real_t * var)14601 void doSliderF(int x, int y, int dots, real_t minvalue, real_t maxvalue, real_t increment, real_t* var)
14602 {
14603 	int c;
14604 
14605 	// build bar
14606 	strcpy(tempstr, "| ");
14607 	for ( c = 0; c < dots; c++ )
14608 	{
14609 		strcat(tempstr, ". ");
14610 	}
14611 	strcat(tempstr, "| %.3f");
14612 	printTextFormatted(SLIDERFONT, x, y, tempstr, *var);
14613 
14614 	// control
14615 	real_t range = maxvalue - minvalue;
14616 	int sliderLength = ((strlen(tempstr) - 6) * (SLIDERFONT->w / 16));
14617 	if ( mousestatus[SDL_BUTTON_LEFT] )
14618 	{
14619 		if ( omousex >= x && omousex < x + sliderLength + (SLIDERFONT->w / 16) )
14620 		{
14621 			if ( omousey >= y - (SLIDERFONT->h / 16) / 2 && omousey < y + ((SLIDERFONT->h / 16) / 2) * 3 )
14622 			{
14623 				*var = ((real_t)(mousex - x - (SLIDERFONT->w / 16) / 2) / sliderLength) * range + minvalue;
14624 				if ( increment )
14625 				{
14626 					*var += increment / 2;
14627 					*var /= increment;
14628 					*var = floor(*var);
14629 					*var *= increment;
14630 				}
14631 				*var = fmin(fmax(minvalue, *var), maxvalue);
14632 			}
14633 		}
14634 	}
14635 
14636 	// draw slider
14637 	int sliderx = x + (SLIDERFONT->w / 16) / 2;
14638 	sliderx += (((*var) - minvalue) / range) * sliderLength;
14639 	drawWindowFancy( sliderx - (SLIDERFONT->w / 16) / 2, y - (SLIDERFONT->h / 16) / 2, sliderx + (SLIDERFONT->w / 16) / 2, y + ((SLIDERFONT->h / 16) / 2) * 3);
14640 }
14641 
openLoadGameWindow(button_t * my)14642 void openLoadGameWindow(button_t* my)
14643 {
14644 	button_t* button;
14645 
14646 	// close current window
14647 	buttonCloseSubwindow(NULL);
14648 	list_FreeAll(&button_l);
14649 	deleteallbuttons = true;
14650 
14651 	// create confirmation window
14652 	subwindow = 1;
14653 	subx1 = xres / 2 - 256;
14654 	subx2 = xres / 2 + 256;
14655 	suby1 = yres / 2 - 128;
14656 	suby2 = yres / 2 + 128;
14657 	strcpy(subtext, language[1460]);
14658 	bool singleplayerSave = saveGameExists(true);
14659 	bool multiplayerSave = saveGameExists(false);
14660 
14661 	char saveGameName[1024] = "";
14662 	if ( singleplayerSave && multiplayerSave )
14663 	{
14664 		strncpy(saveGameName, getSaveGameName(true), 1024);
14665 		strcat(subtext, saveGameName);
14666 		strcat(subtext, "\n\n");
14667 		strncpy(saveGameName, getSaveGameName(false), 1024);
14668 		loadGameSaveShowRectangle = 2;
14669 
14670 		suby1 = yres / 2 - 152;
14671 		suby2 = yres / 2 + 152;
14672 	}
14673 	else if ( singleplayerSave )
14674 	{
14675 		strncpy(saveGameName, getSaveGameName(true), 1024);
14676 		loadGameSaveShowRectangle = 1;
14677 	}
14678 	else if ( multiplayerSave )
14679 	{
14680 		strncpy(saveGameName, getSaveGameName(false), 1024);
14681 		loadGameSaveShowRectangle = 1;
14682 	}
14683 	strcat(subtext, saveGameName);
14684 	strcat(subtext, language[1461]);
14685 
14686 	if ( gamemods_numCurrentModsLoaded >= 0 )
14687 	{
14688 		suby1 -= 24;
14689 		suby2 += 24;
14690 	}
14691 
14692 	// close button
14693 	button = newButton();
14694 	strcpy(button->label, "x");
14695 	button->x = subx2 - 20;
14696 	button->y = suby1;
14697 	button->sizex = 20;
14698 	button->sizey = 20;
14699 	button->action = &buttonCloseSubwindow;
14700 	button->visible = 1;
14701 	button->focused = 1;
14702 	button->key = SDL_SCANCODE_ESCAPE;
14703 	button->joykey = joyimpulses[INJOY_MENU_CANCEL];
14704 
14705 	// yes solo button
14706 	button = newButton();
14707 	if ( multiplayerSave && !singleplayerSave )
14708 	{
14709 		strcpy(button->label, language[2959]);
14710 		button->action = &buttonLoadMultiplayerGame;
14711 	}
14712 	else
14713 	{
14714 		strcpy(button->label, language[1462]);
14715 		button->action = &buttonLoadSingleplayerGame;
14716 	}
14717 	button->sizex = strlen(language[2959]) * 9 + 16;
14718 	button->sizey = 20;
14719 	button->x = subx1 + (subx2 - subx1) / 2 - button->sizex / 2;
14720 	button->y = suby2 - 52;
14721 	if ( singleplayerSave && multiplayerSave )
14722 	{
14723 		button->x -= 124;
14724 	}
14725 	else
14726 	{
14727 		button->sizex = strlen(language[1463]) * 12 + 8; // resize to be wider
14728 		button->x = subx1 + (subx2 - subx1) / 2 - button->sizex / 2; // resize to match new width
14729 	}
14730 	button->visible = 1;
14731 	button->focused = 1;
14732 	button->joykey = joyimpulses[INJOY_MENU_NEXT]; //load save game yes => "a" button
14733 
14734 	// yes multiplayer button
14735 	if ( singleplayerSave && multiplayerSave )
14736 	{
14737 		button = newButton();
14738 		strcpy(button->label, language[2959]);
14739 		button->sizex = strlen(language[2959]) * 9 + 16;
14740 		button->sizey = 20;
14741 		button->x = subx1 + (subx2 - subx1) / 2 - button->sizex / 2 + 124;
14742 		button->y = suby2 - 52;
14743 		button->action = &buttonLoadMultiplayerGame;
14744 		button->visible = 1;
14745 		button->focused = 1;
14746 		//button->joykey = joyimpulses[INJOY_MENU_NEXT]; //load save game yes => "a" button
14747 	}
14748 
14749 	// no button
14750 	button = newButton();
14751 	strcpy(button->label, language[1463]);
14752 	button->sizex = strlen(language[1463]) * 10 + 8;
14753 	button->sizey = 20;
14754 	button->x = subx1 + (subx2 - subx1) / 2 - button->sizex / 2;
14755 	button->y = suby2 - 28;
14756 	button->action = &buttonOpenCharacterCreationWindow;
14757 	button->visible = 1;
14758 	button->focused = 1;
14759 	button->key = SDL_SCANCODE_RETURN;
14760 	button->joykey = joyimpulses[INJOY_MENU_DONT_LOAD_SAVE]; //load save game no => "y" button
14761 
14762 	// delete savegame button
14763 	if ( singleplayerSave || multiplayerSave )
14764 	{
14765 		button = newButton();
14766 		strcpy(button->label, language[2961]);
14767 		button->sizex = strlen(language[2961]) * 12 + 8;
14768 		button->sizey = 20;
14769 		button->x = subx2 - button->sizex - 8;
14770 		button->y = suby1 + TTF12_HEIGHT * 2 + 4;
14771 		if ( singleplayerSave && multiplayerSave)
14772 		{
14773 			button->action = &buttonDeleteSavedSoloGame; // showing 2 entries, single player delete
14774 		}
14775 		if ( singleplayerSave && !multiplayerSave ) // showing 1 entry, single player delete
14776 		{
14777 			button->action = &buttonDeleteSavedSoloGame;
14778 		}
14779 		if ( !singleplayerSave && multiplayerSave ) // showing 1 entry, multi player delete
14780 		{
14781 			button->action = &buttonDeleteSavedMultiplayerGame;
14782 		}
14783 		button->visible = 1;
14784 		button->focused = 1;
14785 	}
14786 	if ( singleplayerSave && multiplayerSave )
14787 	{
14788 		button = newButton();
14789 		strcpy(button->label, language[2961]);
14790 		button->sizex = strlen(language[2961]) * 12 + 8;
14791 		button->sizey = 20;
14792 		button->x = subx2 - button->sizex - 8;
14793 		button->y = suby1 + TTF12_HEIGHT * 5 + 6;
14794 		button->action = &buttonDeleteSavedMultiplayerGame;
14795 		button->visible = 1;
14796 		button->focused = 1;
14797 	}
14798 }
14799 
reloadSavegamesList(bool showWindow)14800 void reloadSavegamesList(bool showWindow)
14801 {
14802 	savegamesList.clear();
14803 
14804 	// load single player files
14805 	for ( int fileNumber = 0; fileNumber < SAVE_GAMES_MAX; ++fileNumber )
14806 	{
14807 		if ( saveGameExists(true, fileNumber) )
14808 		{
14809 			time_t timeNow = std::time(nullptr);
14810 			struct tm *tm = nullptr;
14811 			char path[PATH_MAX] = "";
14812 			char savefile[PATH_MAX] = "";
14813 			strncpy(savefile, setSaveGameFileName(true, false, fileNumber).c_str(), PATH_MAX - 1);
14814 			completePath(path, savefile, outputdir);
14815 #ifdef WINDOWS
14816 			struct _stat result;
14817 			if ( _stat(path, &result) == 0 )
14818 			{
14819 				tm = localtime(&result.st_mtime);
14820 			}
14821 #else
14822 			struct stat result;
14823 			if ( stat(path, &result) == 0 )
14824 			{
14825 				tm = localtime(&result.st_mtime);
14826 			}
14827 #endif
14828 			if ( tm )
14829 			{
14830 				int timeDifference = std::difftime(timeNow, mktime(tm));
14831 				char* saveGameName = getSaveGameName(true, fileNumber);
14832 				if ( saveGameName )
14833 				{
14834 					savegamesList.push_back(std::make_tuple(timeDifference, getSaveGameType(true, fileNumber), fileNumber, saveGameName));
14835 					free(saveGameName);
14836 				}
14837 			}
14838 		}
14839 	}
14840 	// load multiplayer files
14841 	for ( int fileNumber = 0; fileNumber < SAVE_GAMES_MAX; ++fileNumber )
14842 	{
14843 		if ( saveGameExists(false, fileNumber) )
14844 		{
14845 			time_t timeNow = std::time(nullptr);
14846 			struct tm *tm = nullptr;
14847 			char path[PATH_MAX] = "";
14848 			char savefile[PATH_MAX] = "";
14849 			strncpy(savefile, setSaveGameFileName(false, false, fileNumber).c_str(), PATH_MAX - 1);
14850 			completePath(path, savefile, outputdir);
14851 #ifdef WINDOWS
14852 			struct _stat result;
14853 			if ( _stat(path, &result) == 0 )
14854 			{
14855 				tm = localtime(&result.st_mtime);
14856 			}
14857 #else
14858 			struct stat result;
14859 			if ( stat(path, &result) == 0 )
14860 			{
14861 				tm = localtime(&result.st_mtime);
14862 			}
14863 #endif
14864 			if ( tm )
14865 			{
14866 				int timeDifference = std::difftime(timeNow, mktime(tm));
14867 				char* saveGameName = getSaveGameName(false, fileNumber);
14868 				if ( saveGameName )
14869 				{
14870 					savegamesList.push_back(std::make_tuple(timeDifference, getSaveGameType(false, fileNumber), fileNumber, saveGameName));
14871 					free(saveGameName);
14872 				}
14873 			}
14874 		}
14875 	}
14876 	if ( showWindow )
14877 	{
14878 		savegames_window = 1;
14879 	}
14880 	std::sort(savegamesList.begin(), savegamesList.end());
14881 }
14882 
openNewLoadGameWindow(button_t * my)14883 void openNewLoadGameWindow(button_t* my)
14884 {
14885 	// close current window
14886 	buttonCloseSubwindow(nullptr);
14887 	list_FreeAll(&button_l);
14888 	deleteallbuttons = true;
14889 
14890 	// create confirmation window
14891 	subwindow = 1;
14892 	subx1 = xres / 2 - 380;
14893 	subx2 = xres / 2 + 380;
14894 	suby1 = yres / 2 - 210;
14895 	suby2 = yres / 2 + 210;
14896 	strcpy(subtext, language[3065]);
14897 
14898 	// close button
14899 	button_t* button = newButton();
14900 	strcpy(button->label, "x");
14901 	button->x = subx2 - 20;
14902 	button->y = suby1;
14903 	button->sizex = 20;
14904 	button->sizey = 20;
14905 	button->action = &buttonCloseSubwindow;
14906 	button->visible = 1;
14907 	button->focused = 1;
14908 	button->key = SDL_SCANCODE_ESCAPE;
14909 	button->joykey = joyimpulses[INJOY_MENU_CANCEL];
14910 
14911 	button = newButton();
14912 	strcpy(button->label, language[1463]);
14913 	button->sizex = strlen(language[1463]) * 10 + 8;
14914 	button->sizey = 36;
14915 	button->x = subx1 + 16;
14916 	button->y = suby1 + 42;
14917 	button->action = &buttonOpenCharacterCreationWindow;
14918 	button->visible = 1;
14919 	button->focused = 1;
14920 	button->key = SDL_SCANCODE_RETURN;
14921 	button->joykey = joyimpulses[INJOY_MENU_DONT_LOAD_SAVE]; //load save game no => "y" button
14922 
14923 	reloadSavegamesList();
14924 }
14925 
buttonDeleteSavedSoloGame(button_t * my)14926 void buttonDeleteSavedSoloGame(button_t* my)
14927 {
14928 	// close current window
14929 	buttonCloseSubwindow(nullptr);
14930 	list_FreeAll(&button_l);
14931 	deleteallbuttons = true;
14932 
14933 	loadGameSaveShowRectangle = 1;
14934 
14935 	// create confirmation window
14936 	subwindow = 1;
14937 	subx1 = xres / 2 - 288;
14938 	subx2 = xres / 2 + 288;
14939 	suby1 = yres / 2 - 80;
14940 	suby2 = yres / 2 + 80;
14941 	char saveGameName[1024];
14942 	strcpy(subtext, language[2963]);
14943 	strncpy(saveGameName, getSaveGameName(true), 1024);
14944 	strcat(subtext, saveGameName);
14945 	// close button
14946 	button_t* button = newButton();
14947 	strcpy(button->label, "x");
14948 	button->x = subx2 - 20;
14949 	button->y = suby1;
14950 	button->sizex = 20;
14951 	button->sizey = 20;
14952 	button->action = &buttonCloseSubwindow;
14953 	button->visible = 1;
14954 	button->focused = 1;
14955 	button->key = SDL_SCANCODE_ESCAPE;
14956 	button->joykey = joyimpulses[INJOY_MENU_CANCEL];
14957 
14958 	// delete button
14959 	button = newButton();
14960 	strcpy(button->label, language[2961]);
14961 	button->sizex = strlen(language[2961]) * 12 + 8;
14962 	button->sizey = 20;
14963 	button->x = subx1 + (subx2 - subx1) / 2 - button->sizex / 2;
14964 	button->y = suby2 - 56;
14965 	button->action = &buttonConfirmDeleteSoloFile;
14966 	button->visible = 1;
14967 	button->focused = 1;
14968 	button->key = SDL_SCANCODE_RETURN;
14969 	//button->joykey = joyimpulses[INJOY_MENU_DONT_LOAD_SAVE]; //load save game no => "y" button
14970 
14971 	// close button
14972 	button = newButton();
14973 	strcpy(button->label, language[2962]);
14974 	button->sizex = strlen(language[2962]) * 12 + 8;
14975 	button->sizey = 20;
14976 	button->x = subx1 + (subx2 - subx1) / 2 - button->sizex / 2;
14977 	button->y = suby2 - 28;
14978 	button->action = &buttonCloseSubwindow;
14979 	button->visible = 1;
14980 	button->focused = 1;
14981 }
14982 
buttonDeleteSavedMultiplayerGame(button_t * my)14983 void buttonDeleteSavedMultiplayerGame(button_t* my)
14984 {
14985 	// close current window
14986 	buttonCloseSubwindow(nullptr);
14987 	list_FreeAll(&button_l);
14988 	deleteallbuttons = true;
14989 
14990 	loadGameSaveShowRectangle = 1;
14991 
14992 	// create confirmation window
14993 	subwindow = 1;
14994 	subx1 = xres / 2 - 288;
14995 	subx2 = xres / 2 + 288;
14996 	suby1 = yres / 2 - 80;
14997 	suby2 = yres / 2 + 80;
14998 	char saveGameName[1024];
14999 	strcpy(subtext, language[2964]);
15000 	strncpy(saveGameName, getSaveGameName(false), 1024);
15001 	strcat(subtext, saveGameName);
15002 	// close button
15003 	button_t* button = newButton();
15004 	strcpy(button->label, "x");
15005 	button->x = subx2 - 20;
15006 	button->y = suby1;
15007 	button->sizex = 20;
15008 	button->sizey = 20;
15009 	button->action = &buttonCloseSubwindow;
15010 	button->visible = 1;
15011 	button->focused = 1;
15012 	button->key = SDL_SCANCODE_ESCAPE;
15013 	button->joykey = joyimpulses[INJOY_MENU_CANCEL];
15014 
15015 	// delete button
15016 	button = newButton();
15017 	strcpy(button->label, language[2961]);
15018 	button->sizex = strlen(language[2961]) * 12 + 8;
15019 	button->sizey = 20;
15020 	button->x = subx1 + (subx2 - subx1) / 2 - button->sizex / 2;
15021 	button->y = suby2 - 56;
15022 	button->action = &buttonConfirmDeleteMultiplayerFile;
15023 	button->visible = 1;
15024 	button->focused = 1;
15025 	button->key = SDL_SCANCODE_RETURN;
15026 	//button->joykey = joyimpulses[INJOY_MENU_DONT_LOAD_SAVE]; //load save game no => "y" button
15027 
15028 	// close button
15029 	button = newButton();
15030 	strcpy(button->label, language[2962]);
15031 	button->sizex = strlen(language[2962]) * 12 + 8;
15032 	button->sizey = 20;
15033 	button->x = subx1 + (subx2 - subx1) / 2 - button->sizex / 2;
15034 	button->y = suby2 - 28;
15035 	button->action = &buttonCloseSubwindow;
15036 	button->visible = 1;
15037 	button->focused = 1;
15038 }
15039 
buttonConfirmDeleteSoloFile(button_t * my)15040 void buttonConfirmDeleteSoloFile(button_t* my)
15041 {
15042 	// close current window
15043 	buttonCloseSubwindow(nullptr);
15044 	list_FreeAll(&button_l);
15045 	deleteallbuttons = true;
15046 	loadGameSaveShowRectangle = 0;
15047 	deleteSaveGame(SINGLE);
15048 	if ( anySaveFileExists() ) // check for saved game to load up
15049 	{
15050 		openNewLoadGameWindow(nullptr);
15051 	}
15052 	playSound(153, 96);
15053 }
15054 
buttonConfirmDeleteMultiplayerFile(button_t * my)15055 void buttonConfirmDeleteMultiplayerFile(button_t* my)
15056 {
15057 	// close current window
15058 	buttonCloseSubwindow(nullptr);
15059 	list_FreeAll(&button_l);
15060 	deleteallbuttons = true;
15061 	loadGameSaveShowRectangle = 0;
15062 	deleteSaveGame(CLIENT);
15063 	if ( anySaveFileExists() ) // check for saved game to load up
15064 	{
15065 		openNewLoadGameWindow(nullptr);
15066 	}
15067 	playSound(153, 96);
15068 }
15069 
15070 
buttonDeleteScoreCancel(button_t * my)15071 void buttonDeleteScoreCancel(button_t* my)
15072 {
15073 	// close current window
15074 	buttonCloseSubwindow(nullptr);
15075 	list_FreeAll(&button_l);
15076 	deleteallbuttons = true;
15077 
15078 	buttonOpenScoresWindow(nullptr);
15079 	score_window = score_window_to_delete;
15080 	scoreDisplayMultiplayer = score_window_delete_multiplayer;
15081 	score_window_to_delete = 0;
15082 	score_window_delete_multiplayer = false;
15083 
15084 	loadScore(score_window - 1);
15085 }
15086 
buttonDeleteScoreConfirm(button_t * my)15087 void buttonDeleteScoreConfirm(button_t* my)
15088 {
15089 	buttonDeleteCurrentScore(nullptr);
15090 	buttonDeleteScoreCancel(nullptr);
15091 }
15092 
buttonDeleteScoreWindow(button_t * my)15093 void buttonDeleteScoreWindow(button_t* my)
15094 {
15095 	score_window_to_delete = score_window;
15096 	score_window_delete_multiplayer = scoreDisplayMultiplayer;
15097 
15098 	// close current window
15099 	buttonCloseSubwindow(nullptr);
15100 	list_FreeAll(&button_l);
15101 	deleteallbuttons = true;
15102 
15103 	// create confirmation window
15104 	subwindow = 1;
15105 	subx1 = xres / 2 - 244;
15106 	subx2 = xres / 2 + 244;
15107 	suby1 = yres / 2 - 60;
15108 	suby2 = yres / 2 + 60;
15109 	strcpy(subtext, language[3002]);
15110 
15111 	// close button
15112 	button_t* button = newButton();
15113 	strcpy(button->label, "x");
15114 	button->x = subx2 - 20;
15115 	button->y = suby1;
15116 	button->sizex = 20;
15117 	button->sizey = 20;
15118 	button->action = &buttonDeleteScoreCancel;
15119 	button->visible = 1;
15120 	button->focused = 1;
15121 	button->key = SDL_SCANCODE_ESCAPE;
15122 	button->joykey = joyimpulses[INJOY_MENU_CANCEL];
15123 
15124 	// delete button
15125 	button = newButton();
15126 	strcpy(button->label, language[3001]);
15127 	button->sizex = strlen(language[3001]) * 12 + 8;
15128 	button->sizey = 20;
15129 	button->x = subx1 + (subx2 - subx1) / 2 - button->sizex / 2;
15130 	button->y = suby2 - 56;
15131 	button->action = &buttonDeleteScoreConfirm;
15132 	button->visible = 1;
15133 	button->focused = 1;
15134 	button->key = SDL_SCANCODE_RETURN;
15135 
15136 	// close button
15137 	button = newButton();
15138 	strcpy(button->label, language[2962]);
15139 	button->sizex = strlen(language[2962]) * 12 + 8;
15140 	button->sizey = 20;
15141 	button->x = subx1 + (subx2 - subx1) / 2 - button->sizex / 2;
15142 	button->y = suby2 - 28;
15143 	button->action = &buttonDeleteScoreCancel;
15144 	button->visible = 1;
15145 	button->focused = 1;
15146 }
15147 
buttonOpenCharacterCreationWindow(button_t * my)15148 void buttonOpenCharacterCreationWindow(button_t* my)
15149 {
15150 	button_t* button;
15151 
15152 	playing_random_char = false;
15153 	loadingsavegame = 0;
15154 	loadGameSaveShowRectangle = 0;
15155 	// reset class loadout
15156 	clientnum = 0;
15157 	stats[0]->sex = static_cast<sex_t>(0 + rand() % 2);
15158 	stats[0]->appearance = 0 + rand() % NUMAPPEARANCES;
15159 	stats[0]->playerRace = RACE_HUMAN;
15160 	strcpy(stats[0]->name, "");
15161 	stats[0]->type = HUMAN;
15162 	client_classes[0] = 0;
15163 	stats[0]->clearStats();
15164 	initClass(0);
15165 
15166 	// close current window
15167 	if ( subwindow )
15168 	{
15169 		buttonCloseSubwindow(NULL);
15170 		list_FreeAll(&button_l);
15171 		deleteallbuttons = true;
15172 	}
15173 
15174 	// create character creation window
15175 	charcreation_step = 1;
15176 	raceSelect = 0;
15177 	camera_charsheet_offsetyaw = (330) * PI / 180;
15178 	subwindow = 1;
15179 	subx1 = xres / 2 - 400;
15180 	subx2 = xres / 2 + 400;
15181 	suby1 = yres / 2 - 260;
15182 	suby2 = yres / 2 + 260;
15183 	strcpy(subtext, "");
15184 
15185 	// close button
15186 	button = newButton();
15187 	strcpy(button->label, "x");
15188 	button->x = subx2 - 20;
15189 	button->y = suby1;
15190 	button->sizex = 20;
15191 	button->sizey = 20;
15192 	button->action = &buttonCloseSubwindow;
15193 	button->visible = 1;
15194 	button->focused = 1;
15195 	button->joykey = joyimpulses[INJOY_PAUSE_MENU];
15196 
15197 	if ( lastCreatedCharacterClass >= 0
15198 		&& lastCreatedCharacterAppearance >= 0
15199 		&& lastCreatedCharacterSex >= 0 )
15200 	{
15201 		button_t* replayCharacterBtn = newButton();
15202 		strcpy(replayCharacterBtn->label, language[3000]);
15203 		replayCharacterBtn->sizex = strlen(language[3000]) * 12 + 8;
15204 		replayCharacterBtn->sizey = 20;
15205 		replayCharacterBtn->x = button->x - (replayCharacterBtn->sizex + 4); // take position of button attributes above.
15206 		replayCharacterBtn->y = button->y;
15207 		replayCharacterBtn->action = &buttonReplayLastCharacter;
15208 		replayCharacterBtn->visible = 1;
15209 		replayCharacterBtn->focused = 1;
15210 	}
15211 
15212 	// Continue ...
15213 	button = newButton();
15214 	strcpy(button->label, language[1464]);
15215 	button->sizex = strlen(language[1464]) * 12 + 8;
15216 	button->sizey = 20;
15217 	button->x = subx2 - button->sizex - 4;
15218 	button->y = suby2 - 24;
15219 	button->action = &buttonContinue;
15220 	button->visible = 1;
15221 	button->focused = 1;
15222 	button->key = SDL_SCANCODE_RETURN;
15223 	button->joykey = joyimpulses[INJOY_MENU_NEXT];
15224 
15225 	// Back ...
15226 	button = newButton();
15227 	strcpy(button->label, language[1465]);
15228 	button->x = subx1 + 4;
15229 	button->y = suby2 - 24;
15230 	button->sizex = strlen(language[1465]) * 12 + 8;
15231 	button->sizey = 20;
15232 	button->action = &buttonBack;
15233 	button->visible = 1;
15234 	button->focused = 1;
15235 	button->key = SDL_SCANCODE_ESCAPE;
15236 	button->joykey = joyimpulses[INJOY_MENU_CANCEL];
15237 	int button_back_x = button->x;
15238 	int button_back_width = button->sizex;
15239 
15240 	// Random Character ...
15241 	button = newButton();
15242 	strcpy(button->label, language[1466]);
15243 	button->x = button_back_x + button_back_width + 4;
15244 	button->y = suby2 - 24;
15245 	button->sizex = strlen(language[1466]) * 12 + 8;
15246 	button->sizey = 20;
15247 	button->action = &buttonRandomCharacter;
15248 	button->visible = 1;
15249 	button->focused = 1;
15250 	button->key = SDL_SCANCODE_R; //NOTE: This might cause the character to randomly R when you're typing a name. So far, exactly one user has reported something like this happening exactly once in the entirety of existence.
15251 	button->joykey = joyimpulses[INJOY_MENU_RANDOM_CHAR]; //random character => "y" button
15252 
15253 	//Random Name.
15254 	button = newButton();
15255 	strcpy(button->label, language[2498]);
15256 	button->x = button_back_x + button_back_width + 4;
15257 	button->y = suby2 - 24;
15258 	button->sizex = strlen(language[2498]) * 12 + 8;
15259 	button->sizey = 20;
15260 	button->action = &buttonRandomName;
15261 	button->visible = 1;
15262 	button->focused = 1;
15263 	button->joykey = joyimpulses[INJOY_MENU_RANDOM_NAME];
15264 }
15265 
buttonLoadSingleplayerGame(button_t * button)15266 void buttonLoadSingleplayerGame(button_t* button)
15267 {
15268 	loadGameSaveShowRectangle = 0;
15269 	savegamesList.clear();
15270 	loadingsavegame = getSaveGameUniqueGameKey(true);
15271 	int mul = getSaveGameType(true);
15272 
15273 	if ( mul == DIRECTSERVER )
15274 	{
15275 		directConnect = true;
15276 		buttonHostMultiplayer(button);
15277 	}
15278 	else if ( mul == DIRECTCLIENT )
15279 	{
15280 		directConnect = true;
15281 		buttonJoinMultiplayer(button);
15282 	}
15283 	else if ( mul == SINGLE )
15284 	{
15285 		buttonStartSingleplayer(button);
15286 	}
15287 	else
15288 	{
15289 		directConnect = false;
15290 #if defined(USE_EOS) || defined(STEAMWORKS)
15291 		if ( mul == SERVERCROSSPLAY )
15292 		{
15293 			// if steamworks, the hosting type can change if crossplay is enabled or not.
15294 #ifdef STEAMWORKS
15295 			LobbyHandler.hostingType = LobbyHandler_t::LobbyServiceType::LOBBY_STEAM;
15296 			LobbyHandler.setP2PType(LobbyHandler_t::LobbyServiceType::LOBBY_STEAM);
15297 #ifdef USE_EOS
15298 			if ( LobbyHandler.crossplayEnabled )
15299 			{
15300 				LobbyHandler.hostingType = LobbyHandler_t::LobbyServiceType::LOBBY_CROSSPLAY;
15301 				LobbyHandler.setP2PType(LobbyHandler_t::LobbyServiceType::LOBBY_CROSSPLAY);
15302 			}
15303 #endif
15304 #elif defined USE_EOS
15305 			// if just eos, then force hosting settings to default.
15306 			LobbyHandler.hostingType = LobbyHandler_t::LobbyServiceType::LOBBY_CROSSPLAY;
15307 			LobbyHandler.setP2PType(LobbyHandler_t::LobbyServiceType::LOBBY_CROSSPLAY);
15308 #endif
15309 			buttonHostMultiplayer(button);
15310 		}
15311 		else if ( mul == SERVER )
15312 		{
15313 			if ( getSaveGameVersionNum(true) <= 335 )
15314 			{
15315 				// legacy support for steam ver not remembering if crossplay or not. no action.
15316 				// starting with v3.3.6, (mul == SERVERCROSSPLAY) detects from the savefile.
15317 			}
15318 			else
15319 			{
15320 #ifdef STEAMWORKS
15321 				LobbyHandler.hostingType = LobbyHandler_t::LobbyServiceType::LOBBY_STEAM;
15322 				LobbyHandler.setP2PType(LobbyHandler_t::LobbyServiceType::LOBBY_STEAM);
15323 #endif
15324 			}
15325 			buttonHostMultiplayer(button);
15326 		}
15327 		else if ( mul == CLIENT )
15328 		{
15329 #ifdef STEAMWORKS
15330 			if ( !lobbyToConnectTo )
15331 			{
15332 				openSteamLobbyWaitWindow(button);
15333 			}
15334 			else
15335 			{
15336 				// close current window
15337 				list_FreeAll(&button_l);
15338 				deleteallbuttons = true;
15339 
15340 				// create new window
15341 				subwindow = 1;
15342 				subx1 = xres / 2 - 256;
15343 				subx2 = xres / 2 + 256;
15344 				suby1 = yres / 2 - 64;
15345 				suby2 = yres / 2 + 64;
15346 				strcpy(subtext, language[1447]);
15347 
15348 				// close button
15349 				button = newButton();
15350 				strcpy(button->label, "x");
15351 				button->x = subx2 - 20;
15352 				button->y = suby1;
15353 				button->sizex = 20;
15354 				button->sizey = 20;
15355 				button->action = &openSteamLobbyWaitWindow;
15356 				button->visible = 1;
15357 				button->focused = 1;
15358 				button->key = SDL_SCANCODE_ESCAPE;
15359 				button->joykey = joyimpulses[INJOY_MENU_CANCEL];
15360 
15361 				// cancel button
15362 				button = newButton();
15363 				strcpy(button->label, language[1316]);
15364 				button->sizex = strlen(language[1316]) * 12 + 8;
15365 				button->sizey = 20;
15366 				button->x = subx1 + (subx2 - subx1) / 2 - button->sizex / 2;
15367 				button->y = suby2 - 28;
15368 				button->action = &openSteamLobbyWaitWindow;
15369 				button->visible = 1;
15370 				button->focused = 1;
15371 
15372 				connectingToLobby = true;
15373 				connectingToLobbyWindow = true;
15374 				strncpy( currentLobbyName, "", 31 );
15375 				LobbyHandler.steamValidateAndJoinLobby(*static_cast<CSteamID*>(lobbyToConnectTo));
15376 				cpp_Free_CSteamID(lobbyToConnectTo);
15377 				lobbyToConnectTo = NULL;
15378 			}
15379 #elif defined USE_EOS
15380 			openSteamLobbyWaitWindow(button);
15381 #endif
15382 		}
15383 		else
15384 		{
15385 			buttonStartSingleplayer(button);
15386 		}
15387 #endif
15388 	}
15389 }
15390 
buttonLoadMultiplayerGame(button_t * button)15391 void buttonLoadMultiplayerGame(button_t* button)
15392 {
15393 	loadGameSaveShowRectangle = 0;
15394 	savegamesList.clear();
15395 	loadingsavegame = getSaveGameUniqueGameKey(false);
15396 	int mul = getSaveGameType(false);
15397 
15398 	if ( mul == DIRECTSERVER )
15399 	{
15400 		directConnect = true;
15401 		buttonHostMultiplayer(button);
15402 	}
15403 	else if ( mul == DIRECTCLIENT )
15404 	{
15405 		directConnect = true;
15406 		buttonJoinMultiplayer(button);
15407 	}
15408 	else if ( mul == SINGLE )
15409 	{
15410 		buttonStartSingleplayer(button);
15411 	}
15412 	else
15413 	{
15414 		directConnect = false;
15415 #if defined(USE_EOS) || defined(STEAMWORKS)
15416 		if ( mul == SERVERCROSSPLAY )
15417 		{
15418 			// if steamworks, the hosting type can change if crossplay is enabled or not.
15419 #ifdef STEAMWORKS
15420 			LobbyHandler.hostingType = LobbyHandler_t::LobbyServiceType::LOBBY_STEAM;
15421 			LobbyHandler.setP2PType(LobbyHandler_t::LobbyServiceType::LOBBY_STEAM);
15422 #ifdef USE_EOS
15423 			if ( LobbyHandler.crossplayEnabled )
15424 			{
15425 				LobbyHandler.hostingType = LobbyHandler_t::LobbyServiceType::LOBBY_CROSSPLAY;
15426 				LobbyHandler.setP2PType(LobbyHandler_t::LobbyServiceType::LOBBY_CROSSPLAY);
15427 			}
15428 #endif
15429 #elif defined USE_EOS
15430 			// if just eos, then force hosting settings to default.
15431 			LobbyHandler.hostingType = LobbyHandler_t::LobbyServiceType::LOBBY_CROSSPLAY;
15432 			LobbyHandler.setP2PType(LobbyHandler_t::LobbyServiceType::LOBBY_CROSSPLAY);
15433 #endif
15434 			buttonHostMultiplayer(button);
15435 		}
15436 		else if ( mul == SERVER )
15437 		{
15438 			if ( getSaveGameVersionNum(false) <= 335 )
15439 			{
15440 				// legacy support for steam ver not remembering if crossplay or not. no action.
15441 				// starting with v3.3.6, (mul == SERVERCROSSPLAY) detects from the savefile.
15442 			}
15443 			else
15444 			{
15445 #ifdef STEAMWORKS
15446 				LobbyHandler.hostingType = LobbyHandler_t::LobbyServiceType::LOBBY_STEAM;
15447 				LobbyHandler.setP2PType(LobbyHandler_t::LobbyServiceType::LOBBY_STEAM);
15448 #endif
15449 			}
15450 			buttonHostMultiplayer(button);
15451 		}
15452 		else if ( mul == CLIENT )
15453 		{
15454 #ifdef STEAMWORKS
15455 			if ( !lobbyToConnectTo )
15456 			{
15457 				openSteamLobbyWaitWindow(button);
15458 			}
15459 			else
15460 			{
15461 				list_FreeAll(&button_l);
15462 				deleteallbuttons = true;
15463 
15464 				// create new window
15465 				subwindow = 1;
15466 				subx1 = xres / 2 - 256;
15467 				subx2 = xres / 2 + 256;
15468 				suby1 = yres / 2 - 64;
15469 				suby2 = yres / 2 + 64;
15470 				strcpy(subtext, language[1447]);
15471 
15472 				// close button
15473 				button = newButton();
15474 				strcpy(button->label, "x");
15475 				button->x = subx2 - 20;
15476 				button->y = suby1;
15477 				button->sizex = 20;
15478 				button->sizey = 20;
15479 				button->action = &openSteamLobbyWaitWindow;
15480 				button->visible = 1;
15481 				button->focused = 1;
15482 				button->key = SDL_SCANCODE_ESCAPE;
15483 				button->joykey = joyimpulses[INJOY_MENU_CANCEL];
15484 
15485 				// cancel button
15486 				button = newButton();
15487 				strcpy(button->label, language[1316]);
15488 				button->sizex = strlen(language[1316]) * 12 + 8;
15489 				button->sizey = 20;
15490 				button->x = subx1 + (subx2 - subx1) / 2 - button->sizex / 2;
15491 				button->y = suby2 - 28;
15492 				button->action = &openSteamLobbyWaitWindow;
15493 				button->visible = 1;
15494 				button->focused = 1;
15495 
15496 				connectingToLobby = true;
15497 				connectingToLobbyWindow = true;
15498 				strncpy(currentLobbyName, "", 31);
15499 				LobbyHandler.steamValidateAndJoinLobby(*static_cast<CSteamID*>(lobbyToConnectTo));
15500 				cpp_Free_CSteamID(lobbyToConnectTo);
15501 				lobbyToConnectTo = NULL;
15502 			}
15503 #elif defined USE_EOS
15504 			openSteamLobbyWaitWindow(button);
15505 #endif
15506 		}
15507 		else
15508 		{
15509 			buttonStartSingleplayer(button);
15510 		}
15511 #endif
15512 	}
15513 }
15514 
buttonRandomCharacter(button_t * my)15515 void buttonRandomCharacter(button_t* my)
15516 {
15517 	playing_random_char = true;
15518 	charcreation_step = 4;
15519 	camera_charsheet_offsetyaw = (330) * PI / 180;
15520 	stats[0]->sex = static_cast<sex_t>(rand() % 2);
15521 	client_classes[0] = rand() % (CLASS_MONK + 1);//NUMCLASSES;
15522 	stats[0]->clearStats();
15523 	if ( enabledDLCPack1 || enabledDLCPack2 )
15524 	{
15525 		stats[0]->playerRace = rand() % NUMPLAYABLERACES;
15526 		if ( !enabledDLCPack1 )
15527 		{
15528 			while ( stats[0]->playerRace == RACE_SKELETON || stats[0]->playerRace == RACE_VAMPIRE
15529 				|| stats[0]->playerRace == RACE_SUCCUBUS || stats[0]->playerRace == RACE_GOATMAN )
15530 			{
15531 				stats[0]->playerRace = rand() % NUMPLAYABLERACES;
15532 			}
15533 		}
15534 		else if ( !enabledDLCPack2 )
15535 		{
15536 			while ( stats[0]->playerRace == RACE_AUTOMATON || stats[0]->playerRace == RACE_GOBLIN
15537 				|| stats[0]->playerRace == RACE_INCUBUS || stats[0]->playerRace == RACE_INSECTOID )
15538 			{
15539 				stats[0]->playerRace = rand() % NUMPLAYABLERACES;
15540 			}
15541 		}
15542 		if ( stats[0]->playerRace == RACE_INCUBUS )
15543 		{
15544 			stats[0]->sex = MALE;
15545 		}
15546 		else if ( stats[0]->playerRace == RACE_SUCCUBUS )
15547 		{
15548 			stats[0]->sex = FEMALE;
15549 		}
15550 
15551 		if ( stats[0]->playerRace == RACE_HUMAN )
15552 		{
15553 			client_classes[0] = rand() % (NUMCLASSES);
15554 			if ( !enabledDLCPack1 )
15555 			{
15556 				while ( client_classes[0] == CLASS_CONJURER || client_classes[0] == CLASS_ACCURSED
15557 					|| client_classes[0] == CLASS_MESMER || client_classes[0] == CLASS_BREWER )
15558 				{
15559 					client_classes[0] = rand() % (NUMCLASSES);
15560 				}
15561 			}
15562 			else if ( !enabledDLCPack2 )
15563 			{
15564 				while ( client_classes[0] == CLASS_HUNTER || client_classes[0] == CLASS_SHAMAN
15565 					|| client_classes[0] == CLASS_PUNISHER || client_classes[0] == CLASS_MACHINIST )
15566 				{
15567 					client_classes[0] = rand() % (NUMCLASSES);
15568 				}
15569 			}
15570 			stats[0]->appearance = rand() % NUMAPPEARANCES;
15571 		}
15572 		else
15573 		{
15574 			client_classes[0] = rand() % (CLASS_MONK + 2);
15575 			if ( client_classes[0] > CLASS_MONK )
15576 			{
15577 				client_classes[0] = CLASS_MONK + stats[0]->playerRace; // monster specific classes.
15578 			}
15579 			stats[0]->appearance = 0;
15580 		}
15581 	}
15582 	else
15583 	{
15584 		stats[0]->playerRace = RACE_HUMAN;
15585 		stats[0]->appearance = rand() % NUMAPPEARANCES;
15586 	}
15587 	initClass(0);
15588 }
15589 
buttonReplayLastCharacter(button_t * my)15590 void buttonReplayLastCharacter(button_t* my)
15591 {
15592 	if ( lastCreatedCharacterClass >= 0 )
15593 	{
15594 		playing_random_char = false;
15595 		camera_charsheet_offsetyaw = (330) * PI / 180;
15596 		stats[0]->sex = static_cast<sex_t>(lastCreatedCharacterSex);
15597 		stats[0]->playerRace = std::min(std::max(static_cast<int>(RACE_HUMAN), lastCreatedCharacterRace), static_cast<int>(RACE_INSECTOID));
15598 		client_classes[0] = std::min(std::max(0, lastCreatedCharacterClass), static_cast<int>(CLASS_HUNTER));
15599 
15600 		switch ( isCharacterValidFromDLC(*stats[0], lastCreatedCharacterClass) )
15601 		{
15602 			case VALID_OK_CHARACTER:
15603 				// do nothing.
15604 				break;
15605 			case INVALID_REQUIREDLC1:
15606 			case INVALID_REQUIREDLC2:
15607 				// class or race invalid.
15608 				if ( stats[0]->playerRace > RACE_HUMAN )
15609 				{
15610 					stats[0]->playerRace = RACE_HUMAN;
15611 				}
15612 				if ( client_classes[0] > CLASS_MONK )
15613 				{
15614 					client_classes[0] = CLASS_BARBARIAN;
15615 				}
15616 				break;
15617 			case INVALID_CHARACTER:
15618 				// invalid for whatever reason, reset.
15619 				stats[0]->playerRace = RACE_HUMAN;
15620 				client_classes[0] = CLASS_BARBARIAN;
15621 				break;
15622 			case INVALID_REQUIRE_ACHIEVEMENT:
15623 				// required achievement for class mixing among races, so race is valid.
15624 				client_classes[0] = CLASS_BARBARIAN;
15625 				break;
15626 			default:
15627 				// invalid for whatever reason, reset.
15628 				stats[0]->playerRace = RACE_HUMAN;
15629 				client_classes[0] = CLASS_BARBARIAN;
15630 				break;
15631 		}
15632 
15633 		stats[0]->clearStats();
15634 		initClass(0);
15635 		stats[0]->appearance = lastCreatedCharacterAppearance;
15636 		strcpy(stats[0]->name, lastname.c_str());
15637 		charcreation_step = 4; // set the step to 4, so clicking continue advances to 5 (single/multiplayer select)
15638 		buttonContinue(nullptr);
15639 	}
15640 }
15641 
buttonRandomName(button_t * my)15642 void buttonRandomName(button_t* my)
15643 {
15644 	if ( !SDL_IsTextInputActive() || charcreation_step != 4 )
15645 	{
15646 		return;
15647 	}
15648 
15649 	std::vector<std::string> *names;
15650 
15651 	if ( stats[0]->sex == MALE )
15652 	{
15653 		names = &randomPlayerNamesMale;
15654 	}
15655 	else
15656 	{
15657 		names = &randomPlayerNamesFemale;
15658 	}
15659 
15660 	if ( !names->size() )
15661 	{
15662 		printlog("Warning: Random Name: Need names to pick from!");
15663 		return;
15664 	}
15665 	std::string name;
15666 	try
15667 	{
15668 		name = randomEntryFromVector(*names);
15669 	}
15670 	catch ( const char* e )
15671 	{
15672 		printlog("Error: Random Name: \"%s\"", e);
15673 		return;
15674 	}
15675 	catch ( ... )
15676 	{
15677 		printlog("Error: Failed to choose random name.");
15678 		return;
15679 	}
15680 
15681 	strncpy(inputstr, name.c_str(), std::min<size_t>(name.length(), inputlen));
15682 	inputstr[std::min<size_t>(name.length(), inputlen)] = '\0';
15683 }
15684 
buttonGamemodsOpenDirectory(button_t * my)15685 void buttonGamemodsOpenDirectory(button_t* my)
15686 {
15687 	if ( gamemods_window_fileSelect != 0 && !currentDirectoryFiles.empty() )
15688 	{
15689 		std::list<std::string>::const_iterator it = currentDirectoryFiles.begin();
15690 		std::advance(it, std::max(gamemods_window_scroll + gamemods_window_fileSelect - 1, 0));
15691 		std::string directoryName = *it;
15692 
15693 		if ( directoryName.compare("..") == 0 || directoryName.compare(".") == 0 )
15694 		{
15695 			if ( !strcmp(outputdir, "./") )
15696 			{
15697 				directoryPath.append(directoryName);
15698 				directoryPath.append(PHYSFS_getDirSeparator());
15699 			}
15700 			else
15701 			{
15702 				directoryPath = outputdir;
15703 				directoryPath.append(PHYSFS_getDirSeparator()).append(directoryName).append(PHYSFS_getDirSeparator());
15704 			}
15705 		}
15706 		else
15707 		{
15708 			if ( !strcmp(outputdir, "./") )
15709 			{
15710 				directoryPath.append(directoryName);
15711 				directoryPath.append(PHYSFS_getDirSeparator());
15712 			}
15713 			else
15714 			{
15715 				directoryPath = outputdir;
15716 				directoryPath.append(PHYSFS_getDirSeparator()).append(directoryName).append(PHYSFS_getDirSeparator());
15717 			}
15718 		}
15719 		gamemods_window_fileSelect = 0;
15720 		gamemods_window_scroll = 0;
15721 		currentDirectoryFiles = directoryContents(directoryPath.c_str(), true, false);
15722 	}
15723 }
15724 
buttonGamemodsPrevDirectory(button_t * my)15725 void buttonGamemodsPrevDirectory(button_t* my)
15726 {
15727 	gamemods_window_fileSelect = 0;
15728 	gamemods_window_scroll = 0;
15729 	directoryPath.append("..");
15730 	directoryPath.append(PHYSFS_getDirSeparator());
15731 	currentDirectoryFiles = directoryContents(directoryPath.c_str(), true, false);
15732 }
15733 
15734 
writeLevelsTxt(std::string modFolder)15735 void writeLevelsTxt(std::string modFolder)
15736 {
15737 	std::string path = BASE_DATA_DIR;
15738 	path.append("mods/").append(modFolder);
15739 	if ( access(path.c_str(), F_OK) == 0 )
15740 	{
15741 		std::string writeFile = modFolder + "/maps/levels.txt";
15742 		PHYSFS_File *physfp = PHYSFS_openWrite(writeFile.c_str());
15743 		if ( physfp != NULL )
15744 		{
15745 			PHYSFS_writeBytes(physfp, "map: start\n", 11);
15746 			PHYSFS_writeBytes(physfp, "gen: mine\n", 10);
15747 			PHYSFS_writeBytes(physfp, "gen: mine\n", 10);
15748 			PHYSFS_writeBytes(physfp, "gen: mine\n", 10);
15749 			PHYSFS_writeBytes(physfp, "gen: mine\n", 10);
15750 			PHYSFS_writeBytes(physfp, "map: minetoswamp\n", 17);
15751 			PHYSFS_writeBytes(physfp, "gen: swamp\n", 11);
15752 			PHYSFS_writeBytes(physfp, "gen: swamp\n", 11);
15753 			PHYSFS_writeBytes(physfp, "gen: swamp\n", 11);
15754 			PHYSFS_writeBytes(physfp, "gen: swamp\n", 11);
15755 			PHYSFS_writeBytes(physfp, "map: swamptolabyrinth\n", 22);
15756 			PHYSFS_writeBytes(physfp, "gen: labyrinth\n", 15);
15757 			PHYSFS_writeBytes(physfp, "gen: labyrinth\n", 15);
15758 			PHYSFS_writeBytes(physfp, "gen: labyrinth\n", 15);
15759 			PHYSFS_writeBytes(physfp, "gen: labyrinth\n", 15);
15760 			PHYSFS_writeBytes(physfp, "map: labyrinthtoruins\n", 22);
15761 			PHYSFS_writeBytes(physfp, "gen: ruins\n", 11);
15762 			PHYSFS_writeBytes(physfp, "gen: ruins\n", 11);
15763 			PHYSFS_writeBytes(physfp, "gen: ruins\n", 11);
15764 			PHYSFS_writeBytes(physfp, "gen: ruins\n", 11);
15765 			PHYSFS_writeBytes(physfp, "map: boss\n", 10);
15766 			PHYSFS_writeBytes(physfp, "gen: hell\n", 10);
15767 			PHYSFS_writeBytes(physfp, "gen: hell\n", 10);
15768 			PHYSFS_writeBytes(physfp, "gen: hell\n", 10);
15769 			PHYSFS_writeBytes(physfp, "map: hellboss\n", 14);
15770 			PHYSFS_writeBytes(physfp, "map: hamlet\n", 12);
15771 			PHYSFS_writeBytes(physfp, "gen: caves\n", 11);
15772 			PHYSFS_writeBytes(physfp, "gen: caves\n", 11);
15773 			PHYSFS_writeBytes(physfp, "gen: caves\n", 11);
15774 			PHYSFS_writeBytes(physfp, "gen: caves\n", 11);
15775 			PHYSFS_writeBytes(physfp, "map: cavestocitadel\n", 20);
15776 			PHYSFS_writeBytes(physfp, "gen: citadel\n", 13);
15777 			PHYSFS_writeBytes(physfp, "gen: citadel\n", 13);
15778 			PHYSFS_writeBytes(physfp, "gen: citadel\n", 13);
15779 			PHYSFS_writeBytes(physfp, "gen: citadel\n", 13);
15780 			PHYSFS_writeBytes(physfp, "map: sanctum", 12);
15781 			PHYSFS_close(physfp);
15782 		}
15783 		else
15784 		{
15785 			printlog("[PhysFS]: Failed to open %s/maps/levels.txt for writing.", path.c_str());
15786 		}
15787 	}
15788 	else
15789 	{
15790 		printlog("[PhysFS]: Failed to write levels.txt in %s", path.c_str());
15791 	}
15792 }
15793 
buttonGamemodsCreateModDirectory(button_t * my)15794 void buttonGamemodsCreateModDirectory(button_t* my)
15795 {
15796 	std::string baseDir = outputdir;
15797 	baseDir.append(PHYSFS_getDirSeparator()).append("mods").append(PHYSFS_getDirSeparator()).append(gamemods_newBlankDirectory);
15798 
15799 	if ( access(baseDir.c_str(), F_OK) == 0 )
15800 	{
15801 		// folder already exists!
15802 		gamemods_newBlankDirectoryStatus = -1;
15803 	}
15804 	else
15805 	{
15806 		if ( PHYSFS_mkdir(gamemods_newBlankDirectory) )
15807 		{
15808 			gamemods_newBlankDirectoryStatus = 1;
15809 			std::string dir = gamemods_newBlankDirectory;
15810 			std::string folder = "/books";
15811 			PHYSFS_mkdir((dir + folder).c_str());
15812 			folder = "/editor";
15813 			PHYSFS_mkdir((dir + folder).c_str());
15814 
15815 			folder = "/images";
15816 			PHYSFS_mkdir((dir + folder).c_str());
15817 			std::string subfolder = "/sprites";
15818 			PHYSFS_mkdir((dir + folder + subfolder).c_str());
15819 			subfolder = "/system";
15820 			PHYSFS_mkdir((dir + folder + subfolder).c_str());
15821 			subfolder = "/tiles";
15822 			PHYSFS_mkdir((dir + folder + subfolder).c_str());
15823 
15824 			folder = "/items";
15825 			PHYSFS_mkdir((dir + folder).c_str());
15826 			subfolder = "/images";
15827 			PHYSFS_mkdir((dir + folder + subfolder).c_str());
15828 
15829 			folder = "/lang";
15830 			PHYSFS_mkdir((dir + folder).c_str());
15831 			folder = "/maps";
15832 			PHYSFS_mkdir((dir + folder).c_str());
15833 			writeLevelsTxt(gamemods_newBlankDirectory);
15834 
15835 			folder = "/models";
15836 			PHYSFS_mkdir((dir + folder).c_str());
15837 			subfolder = "/creatures";
15838 			PHYSFS_mkdir((dir + folder + subfolder).c_str());
15839 			subfolder = "/decorations";
15840 			PHYSFS_mkdir((dir + folder + subfolder).c_str());
15841 			subfolder = "/doors";
15842 			PHYSFS_mkdir((dir + folder + subfolder).c_str());
15843 			subfolder = "/items";
15844 			PHYSFS_mkdir((dir + folder + subfolder).c_str());
15845 			subfolder = "/particles";
15846 			PHYSFS_mkdir((dir + folder + subfolder).c_str());
15847 
15848 			folder = "/music";
15849 			PHYSFS_mkdir((dir + folder).c_str());
15850 			folder = "/sound";
15851 			PHYSFS_mkdir((dir + folder).c_str());
15852 		}
15853 	}
15854 	strcpy(gamemods_newBlankDirectoryOldName, gamemods_newBlankDirectory);
15855 }
15856 
buttonGamemodsCreateNewModTemplate(button_t * my)15857 void buttonGamemodsCreateNewModTemplate(button_t* my)
15858 {
15859 	buttonCloseSubwindow(nullptr);
15860 	list_FreeAll(&button_l);
15861 	deleteallbuttons = true;
15862 
15863 	gamemods_window = 6;
15864 
15865 	// create window
15866 	subwindow = 1;
15867 	subx1 = xres / 2 - 400;
15868 	subx2 = xres / 2 + 400;
15869 	suby1 = yres / 2 - 70;
15870 	suby2 = yres / 2 + 70;
15871 	strcpy(subtext, "Create new blank mod template");
15872 
15873 	// close button
15874 	button_t* button = newButton();
15875 	strcpy(button->label, "x");
15876 	button->x = subx2 - 20;
15877 	button->y = suby1;
15878 	button->sizex = 20;
15879 	button->sizey = 20;
15880 	button->action = &buttonCloseSubwindow;
15881 	button->visible = 1;
15882 	button->focused = 1;
15883 	button->key = SDL_SCANCODE_ESCAPE;
15884 	button->joykey = joyimpulses[INJOY_MENU_CANCEL];
15885 
15886 	// create button
15887 	button = newButton();
15888 	strcpy(button->label, "create");
15889 	button->x = subx2 - (strlen(button->label) * TTF12_WIDTH + 8);
15890 	button->y = suby2 - TTF12_HEIGHT - 8;
15891 	button->sizex = strlen(button->label) * TTF12_WIDTH + 8;
15892 	button->sizey = 20;
15893 	button->action = &buttonGamemodsCreateModDirectory;
15894 	button->visible = 1;
15895 	button->focused = 1;
15896 }
15897 
15898 
buttonGamemodsBaseDirectory(button_t * my)15899 void buttonGamemodsBaseDirectory(button_t* my)
15900 {
15901 	gamemods_window_fileSelect = 0;
15902 	gamemods_window_scroll = 0;
15903 	directoryPath = outputdir;
15904 	directoryToUpload = directoryPath;
15905 	currentDirectoryFiles = directoryContents(directoryPath.c_str(), true, false);
15906 }
15907 
15908 #ifdef STEAMWORKS
buttonGamemodsSelectDirectoryForUpload(button_t * my)15909 void buttonGamemodsSelectDirectoryForUpload(button_t* my)
15910 {
15911 	if ( !currentDirectoryFiles.empty() )
15912 	{
15913 		std::list<std::string>::const_iterator it = currentDirectoryFiles.begin();
15914 		std::advance(it, std::max(gamemods_window_scroll + gamemods_window_fileSelect - 1, 0));
15915 		std::string directoryName = *it;
15916 
15917 		if ( directoryName.compare("..") == 0 || directoryName.compare(".") == 0 )
15918 		{
15919 			directoryToUpload = directoryName;
15920 			directoryToUpload.append(PHYSFS_getDirSeparator());
15921 		}
15922 		else
15923 		{
15924 			directoryToUpload = directoryPath;
15925 			directoryToUpload.append(directoryName);
15926 			directoryToUpload.append(PHYSFS_getDirSeparator());
15927 		}
15928 	}
15929 	if ( gamemods_window != 5 )
15930 	{
15931 		if ( g_SteamWorkshop )
15932 		{
15933 			g_SteamWorkshop->createItemResult = {};
15934 		}
15935 		gamemods_uploadStatus = 0;
15936 		gamemods_window = 2;
15937 	}
15938 	directoryFilesListToUpload = directoryContents(directoryToUpload.c_str(), true, true);
15939 }
15940 
buttonGamemodsPrepareWorkshopItemUpload(button_t * my)15941 void buttonGamemodsPrepareWorkshopItemUpload(button_t* my)
15942 {
15943 	if ( SteamUser()->BLoggedOn() && g_SteamWorkshop )
15944 	{
15945 		g_SteamWorkshop->CreateItem();
15946 		gamemods_uploadStatus = 1;
15947 	}
15948 }
15949 
buttonGamemodsCancelModifyFileContents(button_t * my)15950 void buttonGamemodsCancelModifyFileContents(button_t* my)
15951 {
15952 	directoryFilesListToUpload.clear();
15953 }
15954 
buttonGamemodsPrepareWorkshopItemUpdate(button_t * my)15955 void buttonGamemodsPrepareWorkshopItemUpdate(button_t* my)
15956 {
15957 	if ( SteamUser()->BLoggedOn() && g_SteamWorkshop )
15958 	{
15959 		g_SteamWorkshop->CreateItem();
15960 		gamemods_uploadStatus = 1;
15961 	}
15962 }
15963 
buttonGamemodsSetWorkshopItemFields(button_t * my)15964 void buttonGamemodsSetWorkshopItemFields(button_t* my)
15965 {
15966 	if ( SteamUser()->BLoggedOn() && g_SteamWorkshop )
15967 	{
15968 		bool itemTagSetSuccess = false;
15969 		if ( g_SteamWorkshop->UGCUpdateHandle != 0 )
15970 		{
15971 			if ( !strcmp(gamemods_uploadTitle, "") )
15972 			{
15973 				strcpy(gamemods_uploadTitle, "Title");
15974 			}
15975 			gamemods_workshopSetPropertyReturn[0] = SteamUGC()->SetItemTitle(g_SteamWorkshop->UGCUpdateHandle, gamemods_uploadTitle);
15976 			if ( !strcmp(gamemods_uploadDescription, "") )
15977 			{
15978 				strcpy(gamemods_uploadDescription, "Description");
15979 			}
15980 			gamemods_workshopSetPropertyReturn[1] = SteamUGC()->SetItemDescription(g_SteamWorkshop->UGCUpdateHandle, gamemods_uploadDescription);
15981 #ifdef WINDOWS
15982 			char pathbuffer[PATH_MAX];
15983 			GetFullPathName(directoryToUpload.c_str(), PATH_MAX, pathbuffer, NULL);
15984 			std::string fullpath = pathbuffer;
15985 #else
15986 			char pathbuffer[PATH_MAX];
15987 			realpath(directoryToUpload.c_str(), pathbuffer);
15988 			std::string fullpath = pathbuffer;
15989 #endif
15990 			if ( access(fullpath.c_str(), F_OK) == 0 )
15991 			{
15992 				gamemods_workshopSetPropertyReturn[2] = SteamUGC()->SetItemContent(g_SteamWorkshop->UGCUpdateHandle, fullpath.c_str());
15993 				// set preview image.
15994 				bool imagePreviewFound = false;
15995 				std::string imgPath = fullpath;
15996 				imgPath.append("preview.jpg");
15997 				if ( !imagePreviewFound && access((imgPath).c_str(), F_OK) == 0 )
15998 				{
15999 					imagePreviewFound = SteamUGC()->SetItemPreview(g_SteamWorkshop->UGCUpdateHandle, imgPath.c_str());
16000 				}
16001 				imgPath = fullpath;
16002 				imgPath.append("preview.png");
16003 				if ( !imagePreviewFound && access((imgPath).c_str(), F_OK) == 0 )
16004 				{
16005 					imagePreviewFound = SteamUGC()->SetItemPreview(g_SteamWorkshop->UGCUpdateHandle, imgPath.c_str());
16006 				}
16007 				imgPath = fullpath;
16008 				imgPath.append("preview.jpg");
16009 				if ( !imagePreviewFound && access((imgPath).c_str(), F_OK) == 0 )
16010 				{
16011 					imagePreviewFound = SteamUGC()->SetItemPreview(g_SteamWorkshop->UGCUpdateHandle, imgPath.c_str());
16012 				}
16013 				if ( !imagePreviewFound )
16014 				{
16015 					printlog("Failed to upload image for workshop item!");
16016 				}
16017 			}
16018 
16019 			// some mumbo jumbo to work with the steam API needing const char[][]
16020 			SteamParamStringArray_t SteamParamStringArray;
16021 			SteamParamStringArray.m_nNumStrings = g_SteamWorkshop->workshopItemTags.size() + 1;
16022 
16023 			// construct new char[][]
16024 			char **tagArray = new char*[gamemods_maxTags];
16025 			int i = 0;
16026 			for ( i = 0; i < gamemods_maxTags; ++i )
16027 			{
16028 				tagArray[i] = new char[32];
16029 			}
16030 
16031 			// copy all the items into this new char[][].
16032 			std::string line;
16033 			i = 0;
16034 			for ( std::list<std::string>::iterator it = g_SteamWorkshop->workshopItemTags.begin(); it != g_SteamWorkshop->workshopItemTags.end(); ++it )
16035 			{
16036 				line = *it;
16037 				strcpy(tagArray[i], line.c_str());
16038 				++i;
16039 			}
16040 			strcpy(tagArray[i], VERSION); // copy the version number as a tag.
16041 
16042 			// set the tags in the API call.
16043 			SteamParamStringArray.m_ppStrings = const_cast<const char**>(tagArray);
16044 			itemTagSetSuccess = SteamUGC()->SetItemTags(g_SteamWorkshop->UGCUpdateHandle, &SteamParamStringArray);
16045 
16046 			// delete the allocated char[][]
16047 			for ( i = 0; i < gamemods_maxTags; ++i )
16048 			{
16049 				delete[] tagArray[i];
16050 			}
16051 			delete[] tagArray;
16052 		}
16053 		gamemods_uploadStatus = 4;
16054 		if ( itemTagSetSuccess && gamemods_workshopSetPropertyReturn[0] && gamemods_workshopSetPropertyReturn[1] && gamemods_workshopSetPropertyReturn[2] )
16055 		{
16056 			my->visible = false;
16057 			// set item fields button
16058 			button_t* button = newButton();
16059 			strcpy(button->label, "upload!");
16060 			button->x = subx1 + 16;
16061 			button->y = suby1 + TTF12_HEIGHT * 34;
16062 			button->sizex = 16 * TTF12_WIDTH + 8;
16063 			button->sizey = 32;
16064 			button->action = &buttonGamemodsStartUploadItem;
16065 			button->visible = 1;
16066 			button->focused = 1;
16067 			gamemods_currentEditField = 0;
16068 		}
16069 	}
16070 }
16071 
buttonGamemodsModifyExistingWorkshopItemFields(button_t * my)16072 void buttonGamemodsModifyExistingWorkshopItemFields(button_t* my)
16073 {
16074 	if ( SteamUser()->BLoggedOn() && g_SteamWorkshop && g_SteamWorkshop->m_myWorkshopItemToModify.m_nPublishedFileId != 0 )
16075 	{
16076 		g_SteamWorkshop->StartItemExistingUpdate(g_SteamWorkshop->m_myWorkshopItemToModify.m_nPublishedFileId);
16077 		if ( g_SteamWorkshop->UGCUpdateHandle != 0 )
16078 		{
16079 			bool itemTagSetSuccess = false;
16080 			bool itemContentSetSuccess = false;
16081 			if ( !directoryFilesListToUpload.empty() )
16082 			{
16083 #ifdef WINDOWS
16084 				char pathbuffer[PATH_MAX];
16085 				GetFullPathName(directoryToUpload.c_str(), PATH_MAX, pathbuffer, NULL);
16086 				std::string fullpath = pathbuffer;
16087 #else
16088 				char pathbuffer[PATH_MAX];
16089 				realpath(directoryToUpload.c_str(), pathbuffer);
16090 				std::string fullpath = pathbuffer;
16091 #endif
16092 				if ( access(fullpath.c_str(), F_OK) == 0 )
16093 				{
16094 					itemContentSetSuccess = SteamUGC()->SetItemContent(g_SteamWorkshop->UGCUpdateHandle, fullpath.c_str());
16095 					// set preview image.
16096 					bool imagePreviewFound = false;
16097 					std::string imgPath = fullpath;
16098 					imgPath.append("preview.jpg");
16099 					if ( !imagePreviewFound && access((imgPath).c_str(), F_OK) == 0 )
16100 					{
16101 						imagePreviewFound = SteamUGC()->SetItemPreview(g_SteamWorkshop->UGCUpdateHandle, imgPath.c_str());
16102 					}
16103 					imgPath = fullpath;
16104 					imgPath.append("preview.png");
16105 					if ( !imagePreviewFound && access((imgPath).c_str(), F_OK) == 0 )
16106 					{
16107 						imagePreviewFound = SteamUGC()->SetItemPreview(g_SteamWorkshop->UGCUpdateHandle, imgPath.c_str());
16108 					}
16109 					imgPath = fullpath;
16110 					imgPath.append("preview.jpg");
16111 					if ( !imagePreviewFound && access((imgPath).c_str(), F_OK) == 0 )
16112 					{
16113 						imagePreviewFound = SteamUGC()->SetItemPreview(g_SteamWorkshop->UGCUpdateHandle, imgPath.c_str());
16114 					}
16115 					if ( !imagePreviewFound )
16116 					{
16117 						printlog("Failed to upload image for workshop item!");
16118 					}
16119 				}
16120 			}
16121 
16122 			// some mumbo jumbo to work with the steam API needing const char[][]
16123 			SteamParamStringArray_t SteamParamStringArray;
16124 			SteamParamStringArray.m_nNumStrings = g_SteamWorkshop->workshopItemTags.size() + 1;
16125 
16126 			// construct new char[][]
16127 			char **tagArray = new char*[gamemods_maxTags];
16128 			int i = 0;
16129 			for ( i = 0; i < gamemods_maxTags; ++i )
16130 			{
16131 				tagArray[i] = new char[32];
16132 			}
16133 
16134 			// copy all the items into this new char[][].
16135 			std::string line;
16136 			i = 0;
16137 			for ( std::list<std::string>::iterator it = g_SteamWorkshop->workshopItemTags.begin(); it != g_SteamWorkshop->workshopItemTags.end(); ++it )
16138 			{
16139 				line = *it;
16140 				strcpy(tagArray[i], line.c_str());
16141 				++i;
16142 			}
16143 			strcpy(tagArray[i], VERSION); // copy the version number as a tag.
16144 
16145 			// set the tags in the API call.
16146 			SteamParamStringArray.m_ppStrings = const_cast<const char**>(tagArray);
16147 			itemTagSetSuccess = SteamUGC()->SetItemTags(g_SteamWorkshop->UGCUpdateHandle, &SteamParamStringArray);
16148 
16149 			// delete the allocated char[][]
16150 			for ( i = 0; i < gamemods_maxTags; ++i )
16151 			{
16152 				delete[] tagArray[i];
16153 			}
16154 			delete[] tagArray;
16155 
16156 			if ( itemTagSetSuccess && (directoryFilesListToUpload.empty() || (!directoryFilesListToUpload.empty() && itemContentSetSuccess)) )
16157 			{
16158 				my->visible = false;
16159 				// set item fields button
16160 				button_t* button = newButton();
16161 				strcpy(button->label, "upload!");
16162 				button->x = subx1 + 16;
16163 				button->y = suby1 + TTF12_HEIGHT * 34;
16164 				button->sizex = 16 * TTF12_WIDTH + 8;
16165 				button->sizey = 32;
16166 				button->action = &buttonGamemodsStartUploadItem;
16167 				button->visible = 1;
16168 				button->focused = 1;
16169 				gamemods_currentEditField = 0;
16170 			}
16171 		}
16172 	}
16173 }
16174 
buttonGamemodsStartUploadItem(button_t * my)16175 void buttonGamemodsStartUploadItem(button_t* my)
16176 {
16177 	if ( SteamUser()->BLoggedOn() && g_SteamWorkshop && g_SteamWorkshop->UGCUpdateHandle != 0 )
16178 	{
16179 		if ( gamemods_window == 5 )
16180 		{
16181 			g_SteamWorkshop->SubmitItemUpdate("Item updated.");
16182 		}
16183 		else
16184 		{
16185 			g_SteamWorkshop->SubmitItemUpdate("First upload.");
16186 		}
16187 		gamemods_uploadStatus = 5;
16188 		my->visible = false;
16189 	}
16190 }
16191 
gamemodsWindowUploadInit(bool creatingNewItem)16192 void gamemodsWindowUploadInit(bool creatingNewItem)
16193 {
16194 	gamemods_window = 1;
16195 	currentDirectoryFiles = directoryContents(outputdir, true, false);
16196 	directoryToUpload = outputdir;
16197 
16198 	// create window
16199 	subwindow = 1;
16200 	subx1 = xres / 2 - 320;
16201 	subx2 = xres / 2 + 320;
16202 	suby1 = yres / 2 - 300;
16203 	suby2 = yres / 2 + 300;
16204 	strcpy(subtext, "Upload to workshop");
16205 
16206 	// close button
16207 	button_t* button = newButton();
16208 	strcpy(button->label, "x");
16209 	button->x = subx2 - 20;
16210 	button->y = suby1;
16211 	button->sizex = 20;
16212 	button->sizey = 20;
16213 	button->action = &buttonCloseSubwindow;
16214 	button->visible = 1;
16215 	button->focused = 1;
16216 	button->key = SDL_SCANCODE_ESCAPE;
16217 	button->joykey = joyimpulses[INJOY_MENU_CANCEL];
16218 
16219 	// subscribed items window button
16220 	button = newButton();
16221 	strcpy(button->label, "view workshop items");
16222 	button->x = subx2 - 40 - strlen(button->label) * TTF12_WIDTH;
16223 	button->y = suby1;
16224 	button->sizex = strlen(button->label) * TTF12_WIDTH + 16;
16225 	button->sizey = 20;
16226 	button->action = &buttonGamemodsOpenSubscribedWindow;
16227 	button->visible = 1;
16228 	button->focused = 1;
16229 
16230 	// previous directory button
16231 	button = newButton();
16232 	strcpy(button->label, "home directory");
16233 	button->x = subx1 + 250;
16234 	button->y = suby1 + 32;
16235 	button->sizex = strlen("home directory") * 12 + 8;
16236 	button->sizey = 20;
16237 	button->action = &buttonGamemodsBaseDirectory;
16238 	button->visible = 1;
16239 	button->focused = 1;
16240 
16241 
16242 	// open directory button
16243 	button = newButton();
16244 	strcpy(button->label, "open");
16245 	button->x = subx1 + 250;
16246 	button->y = suby1 + 56;
16247 	button->sizex = strlen("home directory") * 12 + 8;
16248 	button->sizey = 20;
16249 	button->action = &buttonGamemodsOpenDirectory;
16250 	button->visible = 1;
16251 	button->focused = 1;
16252 
16253 	// previous directory button
16254 	button = newButton();
16255 	strcpy(button->label, "previous folder");
16256 	button->x = subx1 + 250;
16257 	button->y = suby1 + 80;
16258 	button->sizex = strlen("home directory") * 12 + 8;
16259 	button->sizey = 20;
16260 	button->action = &buttonGamemodsPrevDirectory;
16261 	button->visible = 1;
16262 	button->focused = 1;
16263 
16264 	// previous directory button
16265 	button = newButton();
16266 	strcpy(button->label, "new mod folder");
16267 	button->x = subx1 + 250;
16268 	button->y = suby1 + 128;
16269 	button->sizex = strlen("new mod folder") * 12 + 8;
16270 	button->sizey = 20;
16271 	button->action = &buttonGamemodsCreateNewModTemplate;
16272 	button->visible = 1;
16273 	button->focused = 1;
16274 
16275 	// select directory button
16276 	button = newButton();
16277 	strcpy(button->label, "select folder to upload");
16278 	button->x = subx1 + 16;
16279 	button->y = suby1 + 14 * TTF12_HEIGHT + 8;
16280 	button->sizex = 24 * TTF12_WIDTH + 8;
16281 	button->sizey = 32;
16282 	button->action = &buttonGamemodsSelectDirectoryForUpload;
16283 	button->visible = 1;
16284 	button->focused = 1;
16285 
16286 	// prepare directory button
16287 	button_t* button2 = newButton();
16288 	if ( creatingNewItem )
16289 	{
16290 		strcpy(button2->label, "prepare");
16291 		button2->action = &buttonGamemodsPrepareWorkshopItemUpload;
16292 	}
16293 	else
16294 	{
16295 		strcpy(button2->label, "deselect folder");
16296 		button2->action = &buttonGamemodsCancelModifyFileContents;
16297 	}
16298 	button2->x = button->x + button->sizex + 4;
16299 	button2->y = button->y;
16300 	button2->sizex = 16 * TTF12_WIDTH + 8;
16301 	button2->sizey = 32;
16302 	button2->visible = 1;
16303 	button2->focused = 1;
16304 
16305 	if ( !creatingNewItem )
16306 	{
16307 		// modify item fields button
16308 		button = newButton();
16309 		strcpy(button->label, "modify tags/content");
16310 		button->x = subx1 + 16;
16311 		button->y = suby1 + TTF12_HEIGHT * 34;
16312 		button->sizex = 22 * TTF12_WIDTH + 8;
16313 		button->sizey = 32;
16314 		button->action = &buttonGamemodsModifyExistingWorkshopItemFields;
16315 		button->visible = 1;
16316 		button->focused = 1;
16317 	}
16318 }
16319 
gamemodsSubscribedItemsInit()16320 void gamemodsSubscribedItemsInit()
16321 {
16322 	gamemods_window = 3;
16323 	currentDirectoryFiles = directoryContents(outputdir, true, false);
16324 	directoryToUpload = outputdir;
16325 
16326 	gamemodsMountAllExistingPaths();
16327 
16328 	// create confirmation window
16329 	subwindow = 1;
16330 	subx1 = xres / 2 - 420;
16331 	subx2 = xres / 2 + 420;
16332 	suby1 = yres / 2 - 300;
16333 	suby2 = yres / 2 + 300;
16334 	strcpy(subtext, "Workshop items");
16335 
16336 	// close button
16337 	button_t* button = newButton();
16338 	strcpy(button->label, "x");
16339 	button->x = subx2 - 20;
16340 	button->y = suby1;
16341 	button->sizex = 20;
16342 	button->sizey = 20;
16343 	button->action = &buttonCloseSubwindow;
16344 	button->visible = 1;
16345 	button->focused = 1;
16346 	button->key = SDL_SCANCODE_ESCAPE;
16347 	button->joykey = joyimpulses[INJOY_MENU_CANCEL];
16348 
16349 	// upload window button
16350 	button = newButton();
16351 	strcpy(button->label, "upload workshop content");
16352 	button->x = subx2 - 40 - strlen(button->label) * TTF12_WIDTH;
16353 	button->y = suby1;
16354 	button->sizex = strlen(button->label) * TTF12_WIDTH + 16;
16355 	button->sizey = 20;
16356 	button->action = &buttonGamemodsOpenUploadWindow;
16357 	button->visible = 1;
16358 	button->focused = 1;
16359 
16360 	// fetch subscribed items button
16361 	button = newButton();
16362 	strcpy(button->label, "get subscribed item list");
16363 	button->x = subx1 + 16;
16364 	button->y = suby1 + 2 * TTF12_HEIGHT + 8;
16365 	button->sizex = 25 * TTF12_WIDTH + 8;
16366 	button->sizey = 32;
16367 	button->action = &buttonGamemodsGetSubscribedItems;
16368 	button->visible = 1;
16369 	button->focused = 1;
16370 
16371 	// fetch my workshop items
16372 	button_t* button2 = newButton();
16373 	strcpy(button2->label, "my workshop items");
16374 	button2->x = button->x + button->sizex + 16;
16375 	button2->y = suby1 + 2 * TTF12_HEIGHT + 8;
16376 	button2->sizex = 25 * TTF12_WIDTH + 8;
16377 	button2->sizey = 32;
16378 	button2->action = &buttonGamemodsGetMyWorkshopItems;
16379 	button2->visible = 1;
16380 	button2->focused = 1;
16381 
16382 	// start modded game
16383 	button = newButton();
16384 	strcpy(button->label, "start modded game");
16385 	button->sizex = 25 * TTF12_WIDTH + 8;
16386 	button->sizey = 32;
16387 	button->x = subx2 - (button->sizex + 16);
16388 	button->y = suby1 + 2 * TTF12_HEIGHT + 8;
16389 	button->action = &buttonGamemodsStartModdedGame;
16390 	button->visible = 1;
16391 	button->focused = 1;
16392 }
16393 
buttonGamemodsOpenUploadWindow(button_t * my)16394 void buttonGamemodsOpenUploadWindow(button_t* my)
16395 {
16396 	buttonCloseSubwindow(nullptr);
16397 	list_FreeAll(&button_l);
16398 	deleteallbuttons = true;
16399 	gamemodsWindowUploadInit(true);
16400 }
16401 
buttonGamemodsOpenModifyExistingWindow(button_t * my)16402 void buttonGamemodsOpenModifyExistingWindow(button_t* my)
16403 {
16404 	buttonCloseSubwindow(nullptr);
16405 	list_FreeAll(&button_l);
16406 	deleteallbuttons = true;
16407 	gamemodsWindowUploadInit(false);
16408 }
16409 
buttonGamemodsOpenSubscribedWindow(button_t * my)16410 void buttonGamemodsOpenSubscribedWindow(button_t* my)
16411 {
16412 	buttonCloseSubwindow(nullptr);
16413 	list_FreeAll(&button_l);
16414 	deleteallbuttons = true;
16415 	gamemodsSubscribedItemsInit();
16416 }
16417 
buttonGamemodsGetSubscribedItems(button_t * my)16418 void buttonGamemodsGetSubscribedItems(button_t* my)
16419 {
16420 	if ( g_SteamWorkshop )
16421 	{
16422 		g_SteamWorkshop->CreateQuerySubscribedItems(k_EUserUGCList_Subscribed, k_EUGCMatchingUGCType_All, k_EUserUGCListSortOrder_LastUpdatedDesc);
16423 		gamemods_window_scroll = 0;
16424 		gamemods_window = 3;
16425 	}
16426 }
16427 
buttonGamemodsGetMyWorkshopItems(button_t * my)16428 void buttonGamemodsGetMyWorkshopItems(button_t* my)
16429 {
16430 	if ( g_SteamWorkshop )
16431 	{
16432 		g_SteamWorkshop->CreateQuerySubscribedItems(k_EUserUGCList_Published, k_EUGCMatchingUGCType_All, k_EUserUGCListSortOrder_LastUpdatedDesc);
16433 		gamemods_window_scroll = 0;
16434 		gamemods_window = 4;
16435 	}
16436 }
16437 
gamemodsDrawWorkshopItemTagToggle(std::string tagname,int x,int y)16438 void gamemodsDrawWorkshopItemTagToggle(std::string tagname, int x, int y)
16439 {
16440 	if ( !g_SteamWorkshop )
16441 	{
16442 		return;
16443 	}
16444 	std::string printText = tagname;
16445 	std::string line;
16446 	bool foundTag = false;
16447 	std::list<std::string>::iterator it;
16448 	if ( !g_SteamWorkshop->workshopItemTags.empty() )
16449 	{
16450 		for ( it = g_SteamWorkshop->workshopItemTags.begin(); it != g_SteamWorkshop->workshopItemTags.end(); ++it )
16451 		{
16452 			line = *it;
16453 			std::size_t found = line.find_first_of(' '); // trim any trailing spaces.
16454 			if ( found != std::string::npos )
16455 			{
16456 				line = line.substr(0, found);
16457 			}
16458 			if ( line.compare(tagname) == 0 )
16459 			{
16460 				foundTag = true;
16461 				break;
16462 			}
16463 		}
16464 	}
16465 	while ( printText.length() < 12 )
16466 	{
16467 		printText.append(" ");
16468 	}
16469 	if ( foundTag )
16470 	{
16471 		printText.append(": [x]");
16472 	}
16473 	else
16474 	{
16475 		printText.append(": [ ]");
16476 	}
16477 	if ( mouseInBounds(x, x + printText.size() * TTF12_WIDTH, y, y + TTF12_HEIGHT) )
16478 	{
16479 		ttfPrintTextColor(ttf12, x, y, SDL_MapRGBA(mainsurface->format, 128, 128, 128, 255), true, printText.c_str());
16480 		if ( mousestatus[SDL_BUTTON_LEFT] )
16481 		{
16482 			playSound(139, 64);
16483 			if ( foundTag )
16484 			{
16485 				g_SteamWorkshop->workshopItemTags.erase(it);
16486 			}
16487 			else
16488 			{
16489 				g_SteamWorkshop->workshopItemTags.push_back(tagname);
16490 			}
16491 			mousestatus[SDL_BUTTON_LEFT] = 0;
16492 		}
16493 	}
16494 	else
16495 	{
16496 		ttfPrintText(ttf12, x, y, printText.c_str());
16497 	}
16498 }
16499 
gamemodsCheckIfSubscribedAndDownloadedFileID(uint64 fileID)16500 bool gamemodsCheckIfSubscribedAndDownloadedFileID(uint64 fileID)
16501 {
16502 	if ( directConnect || !currentLobby )
16503 	{
16504 		return false;
16505 	}
16506 
16507 	uint64 itemState = SteamUGC()->GetItemState(fileID);
16508 	if ( (itemState & k_EItemStateSubscribed) && (itemState & k_EItemStateInstalled) )
16509 	{
16510 		return true; // client has downloaded and subscribed to content.
16511 	}
16512 
16513 	return false; // client does not have item subscribed or downloaded.
16514 }
16515 
gamemodsCheckFileIDInLoadedPaths(uint64 fileID)16516 bool gamemodsCheckFileIDInLoadedPaths(uint64 fileID)
16517 {
16518 	if ( directConnect || !currentLobby )
16519 	{
16520 		return false;
16521 	}
16522 
16523 	bool found = false;
16524 	for ( std::vector<std::pair<std::string, uint64>>::iterator it = gamemods_workshopLoadedFileIDMap.begin();
16525 		it != gamemods_workshopLoadedFileIDMap.end(); ++it )
16526 	{
16527 		if ( it->second == fileID )
16528 		{
16529 			return true; // client has fileID in mod load path.
16530 		}
16531 	}
16532 
16533 	return false; // client does not have fileID in mod load path.
16534 }
16535 
buttonGamemodsSubscribeToHostsModFiles(button_t * my)16536 void buttonGamemodsSubscribeToHostsModFiles(button_t* my)
16537 {
16538 	if ( !directConnect && currentLobby && g_SteamWorkshop )
16539 	{
16540 		const char* serverNumModsChar = SteamMatchmaking()->GetLobbyData(*static_cast<CSteamID*>(currentLobby), "svNumMods");
16541 		int serverNumModsLoaded = atoi(serverNumModsChar);
16542 		if ( serverNumModsLoaded > 0 )
16543 		{
16544 			char tagName[32];
16545 			std::vector<uint64> fileIdsToDownload;
16546 			for ( int lines = 0; lines < serverNumModsLoaded; ++lines )
16547 			{
16548 				snprintf(tagName, 32, "svMod%d", lines);
16549 				const char* serverModFileID = SteamMatchmaking()->GetLobbyData(*static_cast<CSteamID*>(currentLobby), tagName);
16550 				if ( strcmp(serverModFileID, "") )
16551 				{
16552 					if ( gamemodsCheckIfSubscribedAndDownloadedFileID(atoi(serverModFileID)) == false )
16553 					{
16554 						SteamUGC()->SubscribeItem(atoi(serverModFileID));
16555 					}
16556 					fileIdsToDownload.push_back(atoi(serverModFileID));
16557 				}
16558 			}
16559 			for ( std::vector<uint64>::iterator it = fileIdsToDownload.begin(); it != fileIdsToDownload.end(); ++it )
16560 			{
16561 				SteamUGC()->DownloadItem(*it, true); // download all the newly subscribed items.
16562 				// hopefully enough time elapses for this to complete
16563 			}
16564 			g_SteamWorkshop->CreateQuerySubscribedItems(k_EUserUGCList_Subscribed, k_EUGCMatchingUGCType_All, k_EUserUGCListSortOrder_LastUpdatedDesc);
16565 		}
16566 	}
16567 }
16568 
buttonGamemodsMountHostsModFiles(button_t * my)16569 void buttonGamemodsMountHostsModFiles(button_t* my)
16570 {
16571 	if ( !directConnect && currentLobby && g_SteamWorkshop )
16572 	{
16573 		const char* serverNumModsChar = SteamMatchmaking()->GetLobbyData(*static_cast<CSteamID*>(currentLobby), "svNumMods");
16574 		int serverNumModsLoaded = atoi(serverNumModsChar);
16575 		if ( serverNumModsLoaded > 0 )
16576 		{
16577 			char tagName[32];
16578 			char fullpath[PATH_MAX];
16579 			// prepare to mount only the hosts workshop files.
16580 			gamemodsClearAllMountedPaths();
16581 			gamemods_mountedFilepaths.clear();
16582 			gamemods_workshopLoadedFileIDMap.clear();
16583 			for ( int lines = 0; lines < serverNumModsLoaded; ++lines )
16584 			{
16585 				snprintf(tagName, 32, "svMod%d", lines);
16586 				const char* serverModFileID = SteamMatchmaking()->GetLobbyData(*static_cast<CSteamID*>(currentLobby), tagName);
16587 				if ( strcmp(serverModFileID, "") )
16588 				{
16589 					if ( gamemodsCheckFileIDInLoadedPaths(atoi(serverModFileID)) == false )
16590 					{
16591 						if ( SteamUGC()->GetItemInstallInfo(atoi(serverModFileID), NULL, fullpath, PATH_MAX, NULL) )
16592 						{
16593 							for ( int i = 0; i < g_SteamWorkshop->numSubcribedItemResults; ++i )
16594 							{
16595 								if ( g_SteamWorkshop->m_subscribedItemListDetails[i].m_nPublishedFileId == atoi(serverModFileID) )
16596 								{
16597 									gamemods_mountedFilepaths.push_back(std::make_pair(fullpath, g_SteamWorkshop->m_subscribedItemListDetails[i].m_rgchTitle));
16598 									gamemods_workshopLoadedFileIDMap.push_back(std::make_pair(g_SteamWorkshop->m_subscribedItemListDetails[i].m_rgchTitle,
16599 										g_SteamWorkshop->m_subscribedItemListDetails[i].m_nPublishedFileId));
16600 									break;
16601 								}
16602 							}
16603 						}
16604 					}
16605 				}
16606 			}
16607 			g_SteamWorkshop->CreateQuerySubscribedItems(k_EUserUGCList_Subscribed, k_EUGCMatchingUGCType_All, k_EUserUGCListSortOrder_LastUpdatedDesc);
16608 			gamemodsMountAllExistingPaths(); // mount all the new filepaths, update gamemods_numCurrentModsLoaded.
16609 		}
16610 	}
16611 }
16612 
gamemodsIsClientLoadOrderMatchingHost(std::vector<std::string> serverModList)16613 bool gamemodsIsClientLoadOrderMatchingHost(std::vector<std::string> serverModList)
16614 {
16615 	std::vector<std::pair<std::string, uint64>>::iterator found = gamemods_workshopLoadedFileIDMap.begin();
16616 	std::vector<std::pair<std::string, uint64>>::iterator previousFound = gamemods_workshopLoadedFileIDMap.begin();
16617 	std::vector<std::string>::iterator itServerList;
16618 	if ( serverModList.empty() || (serverModList.size() > gamemods_mountedFilepaths.size()) )
16619 	{
16620 		return false;
16621 	}
16622 
16623 	for ( itServerList = serverModList.begin(); itServerList != serverModList.end(); ++itServerList )
16624 	{
16625 		for ( found = previousFound; found != gamemods_workshopLoadedFileIDMap.end(); ++found )
16626 		{
16627 			if ( std::to_string(found->second) == *itServerList )
16628 			{
16629 				break;
16630 			}
16631 		}
16632 		if ( found != gamemods_workshopLoadedFileIDMap.end() )
16633 		{
16634 			// look for the server's modID in my loaded paths.
16635 			// check the distance along the vector our found result is.
16636 			// if the distance is negative, then our mod order is out of sync with the server's mod list
16637 			// and requires rearranging.
16638 			if ( std::distance(previousFound, found) < 0 )
16639 			{
16640 				return false;
16641 			}
16642 			previousFound = found;
16643 		}
16644 		else
16645 		{
16646 			// server's mod doesn't exist in our filepath, so our mod lists are not in sync.
16647 			return false;
16648 		}
16649 	}
16650 	return true;
16651 }
16652 
16653 #endif //STEAMWORKS
16654 
gamemodsDrawClickableButton(int padx,int pady,int padw,int padh,Uint32 btnColor,std::string btnText,int action)16655 bool gamemodsDrawClickableButton(int padx, int pady, int padw, int padh, Uint32 btnColor, std::string btnText, int action)
16656 {
16657 	bool clicked = false;
16658 	if ( mouseInBounds(padx, padx + padw, pady - 4, pady + padh) )
16659 	{
16660 		drawDepressed(padx, pady - 4, padx + padw, pady + padh);
16661 		if ( mousestatus[SDL_BUTTON_LEFT] )
16662 		{
16663 			playSound(139, 64);
16664 			mousestatus[SDL_BUTTON_LEFT] = 0;
16665 			clicked = true;
16666 		}
16667 	}
16668 	else
16669 	{
16670 		drawWindow(padx, pady - 4, padx + padw, pady + padh);
16671 	}
16672 	SDL_Rect pos;
16673 	pos.x = padx;
16674 	pos.y = pady - 4;
16675 	pos.w = padw;
16676 	pos.h = padh + 4;
16677 	drawRect(&pos, btnColor, 64);
16678 	ttfPrintTextFormatted(ttf12, padx + 8, pady, "%s", btnText.c_str());
16679 	return clicked;
16680 }
16681 
gamemodsRemovePathFromMountedFiles(std::string findStr)16682 bool gamemodsRemovePathFromMountedFiles(std::string findStr)
16683 {
16684 	std::vector<std::pair<std::string, std::string>>::iterator it;
16685 	std::pair<std::string, std::string> line;
16686 	for ( it = gamemods_mountedFilepaths.begin(); it != gamemods_mountedFilepaths.end(); ++it )
16687 	{
16688 		line = *it;
16689 		if ( line.first.compare(findStr) == 0 )
16690 		{
16691 			// found entry, remove from list.
16692 #ifdef STEAMWORKS
16693 			for ( std::vector<std::pair<std::string, uint64>>::iterator itId = gamemods_workshopLoadedFileIDMap.begin();
16694 				itId != gamemods_workshopLoadedFileIDMap.end(); ++itId )
16695 			{
16696 				if ( itId->first.compare(line.second) == 0 )
16697 				{
16698 					gamemods_workshopLoadedFileIDMap.erase(itId);
16699 					break;
16700 				}
16701 			}
16702 #endif // STEAMWORKS
16703 			gamemods_mountedFilepaths.erase(it);
16704 			return true;
16705 		}
16706 	}
16707 	return false;
16708 }
16709 
gamemodsIsPathInMountedFiles(std::string findStr)16710 bool gamemodsIsPathInMountedFiles(std::string findStr)
16711 {
16712 	std::vector<std::pair<std::string, std::string>>::iterator it;
16713 	std::pair<std::string, std::string> line;
16714 	for ( it = gamemods_mountedFilepaths.begin(); it != gamemods_mountedFilepaths.end(); ++it )
16715 	{
16716 		line = *it;
16717 		if ( line.first.compare(findStr) == 0 )
16718 		{
16719 			// found entry
16720 			return true;
16721 		}
16722 	}
16723 	return false;
16724 }
16725 
buttonGamemodsGetLocalMods(button_t * my)16726 void buttonGamemodsGetLocalMods(button_t* my)
16727 {
16728 	gamemods_window_scroll = 0;
16729 	gamemods_window = 7;
16730 	gamemods_localModFoldernames.clear();
16731 	std::string path = outputdir;
16732 	path.append(PHYSFS_getDirSeparator()).append("mods").append(PHYSFS_getDirSeparator());
16733 	gamemods_localModFoldernames = directoryContents(path.c_str(), true, false);
16734 }
16735 
buttonGamemodsStartModdedGame(button_t * my)16736 void buttonGamemodsStartModdedGame(button_t* my)
16737 {
16738 	if ( gamemods_modPreload )
16739 	{
16740 		// look for a save game
16741 		if ( anySaveFileExists() )
16742 		{
16743 			openNewLoadGameWindow(nullptr);
16744 		}
16745 		else
16746 		{
16747 			buttonOpenCharacterCreationWindow(NULL);
16748 		}
16749 		return;
16750 	}
16751 
16752 	gamemods_numCurrentModsLoaded = gamemods_mountedFilepaths.size();
16753 	if ( gamemods_numCurrentModsLoaded > 0 )
16754 	{
16755 		steamAchievement("BARONY_ACH_LOCAL_CUSTOMS");
16756 	}
16757 
16758 	if ( physfsIsMapLevelListModded() )
16759 	{
16760 		gamemods_disableSteamAchievements = true;
16761 	}
16762 	else
16763 	{
16764 		gamemods_disableSteamAchievements = false;
16765 	}
16766 
16767 	int w, h;
16768 
16769 	if ( !gamemods_modelsListRequiresReload && gamemods_modelsListLastStartedUnmodded )
16770 	{
16771 		if ( physfsSearchModelsToUpdate() || !gamemods_modelsListModifiedIndexes.empty() )
16772 		{
16773 			gamemods_modelsListRequiresReload = true;
16774 		}
16775 		gamemods_modelsListLastStartedUnmodded = false;
16776 	}
16777 	if ( !gamemods_soundListRequiresReload && gamemods_soundsListLastStartedUnmodded )
16778 	{
16779 		if ( physfsSearchSoundsToUpdate() )
16780 		{
16781 			gamemods_soundListRequiresReload = true;
16782 		}
16783 		gamemods_soundsListLastStartedUnmodded = false;
16784 	}
16785 
16786 	// process any new model files encountered in the mod load list.
16787 	int modelsIndexUpdateStart = 1;
16788 	int modelsIndexUpdateEnd = nummodels;
16789 	if ( gamemods_modelsListRequiresReload )
16790 	{
16791 		if ( physfsSearchModelsToUpdate() || !gamemods_modelsListModifiedIndexes.empty() )
16792 		{
16793 			// print a loading message
16794 			drawClearBuffers();
16795 			TTF_SizeUTF8(ttf16, language[2989], &w, &h);
16796 			ttfPrintText(ttf16, (xres - w) / 2, (yres - h) / 2, language[2989]);
16797 			GO_SwapBuffers(screen);
16798 			physfsModelIndexUpdate(modelsIndexUpdateStart, modelsIndexUpdateEnd, true);
16799 			generatePolyModels(modelsIndexUpdateStart, modelsIndexUpdateEnd, false);
16800 		}
16801 		gamemods_modelsListRequiresReload = false;
16802 	}
16803 	if ( gamemods_soundListRequiresReload )
16804 	{
16805 		if ( physfsSearchSoundsToUpdate() )
16806 		{
16807 			// print a loading message
16808 			drawClearBuffers();
16809 			TTF_SizeUTF8(ttf16, language[2987], &w, &h);
16810 			ttfPrintText(ttf16, (xres - w) / 2, (yres - h) / 2, language[2987]);
16811 			GO_SwapBuffers(screen);
16812 			physfsReloadSounds(true);
16813 		}
16814 		gamemods_soundListRequiresReload = false;
16815 	}
16816 
16817 	if ( physfsSearchTilesToUpdate() )
16818 	{
16819 		// print a loading message
16820 		drawClearBuffers();
16821 		TTF_SizeUTF8(ttf16, language[3017], &w, &h);
16822 		ttfPrintText(ttf16, (xres - w) / 2, (yres - h) / 2, language[3017]);
16823 		GO_SwapBuffers(screen);
16824 		physfsReloadTiles(false);
16825 		gamemods_tileListRequireReloadUnmodded = true;
16826 	}
16827 
16828 	if ( physfsSearchSpritesToUpdate() )
16829 	{
16830 		// print a loading message
16831 		drawClearBuffers();
16832 		TTF_SizeUTF8(ttf16, language[3015], &w, &h);
16833 		ttfPrintText(ttf16, (xres - w) / 2, (yres - h) / 2, language[3015]);
16834 		GO_SwapBuffers(screen);
16835 		physfsReloadSprites(false);
16836 		gamemods_spriteImagesRequireReloadUnmodded = true;
16837 	}
16838 
16839 	if ( physfsSearchBooksToUpdate() )
16840 	{
16841 		// print a loading message
16842 		drawClearBuffers();
16843 		TTF_SizeUTF8(ttf16, language[2991], &w, &h);
16844 		ttfPrintText(ttf16, (xres - w) / 2, (yres - h) / 2, language[2991]);
16845 		GO_SwapBuffers(screen);
16846 		physfsReloadBooks();
16847 		gamemods_booksRequireReloadUnmodded = true;
16848 	}
16849 
16850 	gamemodsUnloadCustomThemeMusic();
16851 
16852 	if ( physfsSearchMusicToUpdate() )
16853 	{
16854 		// print a loading message
16855 		drawClearBuffers();
16856 		TTF_SizeUTF8(ttf16, language[2993], &w, &h);
16857 		ttfPrintText(ttf16, (xres - w) / 2, (yres - h) / 2, language[2993]);
16858 		GO_SwapBuffers(screen);
16859 		bool reloadIntroMusic = false;
16860 		physfsReloadMusic(reloadIntroMusic, false);
16861 		if ( reloadIntroMusic )
16862 		{
16863 #ifdef SOUND
16864 			playmusic(intromusic[rand() % (NUMINTROMUSIC - 1)], false, true, true);
16865 #endif
16866 		}
16867 		gamemods_musicRequireReloadUnmodded = true;
16868 	}
16869 
16870 	std::string langDirectory = PHYSFS_getRealDir("lang/en.txt");
16871 	if ( langDirectory.compare("./") != 0 )
16872 	{
16873 		// print a loading message
16874 		drawClearBuffers();
16875 		TTF_SizeUTF8(ttf16, language[3004], &w, &h);
16876 		ttfPrintText(ttf16, (xres - w) / 2, (yres - h) / 2, language[3004]);
16877 		GO_SwapBuffers(screen);
16878 		if ( reloadLanguage() != 0 )
16879 		{
16880 			printlog("[PhysFS]: Error reloading modified language file in lang/ directory!");
16881 		}
16882 		else
16883 		{
16884 			printlog("[PhysFS]: Found modified language file in lang/ directory, reloading en.txt...");
16885 		}
16886 		gamemods_langRequireReloadUnmodded = true;
16887 	}
16888 
16889 	if ( physfsSearchItemsTxtToUpdate() )
16890 	{
16891 		// print a loading message
16892 		drawClearBuffers();
16893 		TTF_SizeUTF8(ttf16, language[3008], &w, &h);
16894 		ttfPrintText(ttf16, (xres - w) / 2, (yres - h) / 2, language[3008]);
16895 		GO_SwapBuffers(screen);
16896 		physfsReloadItemsTxt();
16897 		gamemods_itemsTxtRequireReloadUnmodded = true;
16898 	}
16899 
16900 	if ( physfsSearchItemSpritesToUpdate() )
16901 	{
16902 		// print a loading message
16903 		drawClearBuffers();
16904 		TTF_SizeUTF8(ttf16, language[3006], &w, &h);
16905 		ttfPrintText(ttf16, (xres - w) / 2, (yres - h) / 2, language[3006]);
16906 		GO_SwapBuffers(screen);
16907 		physfsReloadItemSprites(false);
16908 		gamemods_itemSpritesRequireReloadUnmodded = true;
16909 	}
16910 
16911 	if ( physfsSearchItemsGlobalTxtToUpdate() )
16912 	{
16913 		gamemods_itemsGlobalTxtRequireReloadUnmodded = true;
16914 		loadItemLists();
16915 	}
16916 
16917 	if ( physfsSearchMonsterLimbFilesToUpdate() )
16918 	{
16919 		// print a loading message
16920 		drawClearBuffers();
16921 		TTF_SizeUTF8(ttf16, language[3013], &w, &h);
16922 		ttfPrintText(ttf16, (xres - w) / 2, (yres - h) / 2, language[3013]);
16923 		GO_SwapBuffers(screen);
16924 		physfsReloadMonsterLimbFiles();
16925 		gamemods_monsterLimbsRequireReloadUnmodded = true;
16926 	}
16927 
16928 	if ( physfsSearchSystemImagesToUpdate() )
16929 	{
16930 		// print a loading message
16931 		drawClearBuffers();
16932 		TTF_SizeUTF8(ttf16, language[3015], &w, &h);
16933 		ttfPrintText(ttf16, (xres - w) / 2, (yres - h) / 2, language[3015]);
16934 		GO_SwapBuffers(screen);
16935 		physfsReloadSystemImages();
16936 		gamemods_systemImagesReloadUnmodded = true;
16937 
16938 		// tidy up some other resource files.
16939 		rightsidebar_titlebar_img = spell_list_titlebar_bmp;
16940 		rightsidebar_slot_img = spell_list_gui_slot_bmp;
16941 		rightsidebar_slot_highlighted_img = spell_list_gui_slot_highlighted_bmp;
16942 	}
16943 
16944 	// look for a save game
16945 	if ( anySaveFileExists() )
16946 	{
16947 		//openLoadGameWindow(NULL);
16948 		openNewLoadGameWindow(nullptr);
16949 	}
16950 	else
16951 	{
16952 		buttonOpenCharacterCreationWindow(NULL);
16953 	}
16954 }
16955 
gamemodsCustomContentInit()16956 void gamemodsCustomContentInit()
16957 {
16958 
16959 	gamemods_window = 3;
16960 	currentDirectoryFiles = directoryContents(outputdir, true, false);
16961 	directoryToUpload = outputdir;
16962 
16963 	gamemodsMountAllExistingPaths();
16964 
16965 	// create confirmation window
16966 	subwindow = 1;
16967 	subx1 = xres / 2 - 420;
16968 	subx2 = xres / 2 + 420;
16969 	suby1 = yres / 2 - 300;
16970 	suby2 = yres / 2 + 300;
16971 	strcpy(subtext, "Custom Content");
16972 
16973 	// close button
16974 	button_t* button = newButton();
16975 	strcpy(button->label, "x");
16976 	button->x = subx2 - 20;
16977 	button->y = suby1;
16978 	button->sizex = 20;
16979 	button->sizey = 20;
16980 	button->action = &buttonCloseSubwindow;
16981 	button->visible = 1;
16982 	button->focused = 1;
16983 	button->key = SDL_SCANCODE_ESCAPE;
16984 	button->joykey = joyimpulses[INJOY_MENU_CANCEL];
16985 
16986 	// fetch local mods button
16987 	button = newButton();
16988 	strcpy(button->label, "local mods");
16989 	button->x = subx1 + 16;
16990 	button->y = suby1 + 2 * TTF12_HEIGHT + 8;
16991 	button->sizex = 25 * TTF12_WIDTH + 8;
16992 	button->sizey = 32;
16993 	button->action = &buttonGamemodsGetLocalMods;
16994 	button->visible = 1;
16995 	button->focused = 1;
16996 
16997 	// fetch my workshop items
16998 	button_t* button2 = newButton();
16999 	strcpy(button2->label, "new mod folder");
17000 	button2->x = button->x + button->sizex + 16;
17001 	button2->y = suby1 + 2 * TTF12_HEIGHT + 8;
17002 	button2->sizex = 25 * TTF12_WIDTH + 8;
17003 	button2->sizey = 32;
17004 	button2->action = &buttonGamemodsCreateNewModTemplate;
17005 	button2->visible = 1;
17006 	button2->focused = 1;
17007 
17008 	// start modded game
17009 	button = newButton();
17010 	strcpy(button->label, "start modded game");
17011 	button->sizex = 25 * TTF12_WIDTH + 8;
17012 	button->sizey = 32;
17013 	button->x = subx2 - (button->sizex + 16);
17014 	button->y = suby1 + 2 * TTF12_HEIGHT + 8;
17015 	button->action = &buttonGamemodsStartModdedGame;
17016 	button->visible = 1;
17017 	button->focused = 1;
17018 }
17019 
gamemodsClearAllMountedPaths()17020 bool gamemodsClearAllMountedPaths()
17021 {
17022 	bool success = true;
17023 	char **i;
17024 	for ( i = PHYSFS_getSearchPath(); *i != NULL; i++ )
17025 	{
17026 		std::string line = *i;
17027 		if ( line.compare(outputdir) != 0 && line.compare(datadir) != 0 && line.compare("./") != 0 ) // don't unmount the base ./ directory
17028 		{
17029 			if ( PHYSFS_unmount(*i) == 0 )
17030 			{
17031 				success = false;
17032 				printlog("[%s] unsuccessfully removed from the search path.\n", line.c_str());
17033 			}
17034 			else
17035 			{
17036 				printlog("[%s] is removed from the search path.\n", line.c_str());
17037 			}
17038 		}
17039 	}
17040 	gamemods_numCurrentModsLoaded = -1;
17041 	PHYSFS_freeList(*i);
17042 	return success;
17043 }
17044 
gamemodsMountAllExistingPaths()17045 bool gamemodsMountAllExistingPaths()
17046 {
17047 	bool success = true;
17048 	std::vector<std::pair<std::string, std::string>>::iterator it;
17049 	for ( it = gamemods_mountedFilepaths.begin(); it != gamemods_mountedFilepaths.end(); ++it )
17050 	{
17051 		std::pair<std::string, std::string> itpair = *it;
17052 		if ( PHYSFS_mount(itpair.first.c_str(), NULL, 0) )
17053 		{
17054 			printlog("[%s] is in the search path.\n", itpair.first.c_str());
17055 		}
17056 		else
17057 		{
17058 			printlog("[%s] unsuccessfully added to search path.\n", itpair.first.c_str());
17059 			success = false;
17060 		}
17061 	}
17062 	gamemods_numCurrentModsLoaded = gamemods_mountedFilepaths.size();
17063 	gamemods_customContentLoadedFirstTime = true;
17064 	return success;
17065 }
17066 
gamemodsWindowClearVariables()17067 void gamemodsWindowClearVariables()
17068 {
17069 #ifdef STEAMWORKS
17070 	if ( g_SteamWorkshop )
17071 	{
17072 		g_SteamWorkshop->createItemResult = {};
17073 		g_SteamWorkshop->UGCUpdateHandle = {};
17074 		g_SteamWorkshop->SubmitItemUpdateResult = {};
17075 		SteamUGC()->ReleaseQueryUGCRequest(g_SteamWorkshop->UGCQueryHandle);
17076 		g_SteamWorkshop->UGCQueryHandle = {};
17077 		g_SteamWorkshop->SteamUGCQueryCompleted = {};
17078 		g_SteamWorkshop->UnsubscribePublishedFileResult = {};
17079 		g_SteamWorkshop->LastActionResult.creationTick = 0;
17080 		g_SteamWorkshop->LastActionResult.actionMsg = "";
17081 		g_SteamWorkshop->LastActionResult.lastResult = static_cast<EResult>(0);
17082 		g_SteamWorkshop->workshopItemTags.clear();
17083 		for ( int i = 0; i < 50; ++i )
17084 		{
17085 			g_SteamWorkshop->m_subscribedItemListDetails[i] = {};
17086 		}
17087 		g_SteamWorkshop->uploadSuccessTicks = 0;
17088 		g_SteamWorkshop->m_myWorkshopItemToModify = {};
17089 	}
17090 #endif // STEAMWORKS
17091 	directoryToUpload.clear();
17092 	directoryPath.clear();
17093 	directoryFilesListToUpload.clear();
17094 	gamemods_window_scroll = 0;
17095 	gamemods_uploadStatus = 0;
17096 	strcpy(gamemods_uploadTitle, "Title");
17097 	strcpy(gamemods_uploadDescription, "Description");
17098 	strcpy(gamemods_newBlankDirectory, "");
17099 	strcpy(gamemods_newBlankDirectoryOldName, "");
17100 	gamemods_newBlankDirectoryStatus = 0;
17101 	gamemods_currentEditField = 0;
17102 	gamemods_workshopSetPropertyReturn[0] = false;
17103 	gamemods_workshopSetPropertyReturn[1] = false;
17104 	gamemods_workshopSetPropertyReturn[2] = false;
17105 	gamemods_subscribedItemsStatus = 0;
17106 }
17107 
drawClickableButton(int padx,int pady,int padw,int padh,Uint32 btnColor)17108 bool drawClickableButton(int padx, int pady, int padw, int padh, Uint32 btnColor)
17109 {
17110 	bool clicked = false;
17111 	if ( mouseInBounds(padx, padx + padw, pady - 4, pady + padh) )
17112 	{
17113 		drawDepressed(padx, pady - 4, padx + padw, pady + padh);
17114 		if ( mousestatus[SDL_BUTTON_LEFT] )
17115 		{
17116 			playSound(139, 64);
17117 			mousestatus[SDL_BUTTON_LEFT] = 0;
17118 			clicked = true;
17119 		}
17120 	}
17121 	else
17122 	{
17123 		drawWindow(padx, pady - 4, padx + padw, pady + padh);
17124 	}
17125 	SDL_Rect pos;
17126 	pos.x = padx;
17127 	pos.y = pady - 4;
17128 	pos.w = padw;
17129 	pos.h = padh + 4;
17130 	if ( btnColor != 0 )
17131 	{
17132 		drawRect(&pos, btnColor, 64);
17133 	}
17134 	return clicked;
17135 }
17136 #ifdef STEAMWORKS
gamemodsWorkshopPreloadMod(int fileID,std::string modTitle)17137 void gamemodsWorkshopPreloadMod(int fileID, std::string modTitle)
17138 {
17139 	char fullpath[PATH_MAX] = "";
17140 	useModelCache = false;
17141 	if ( SteamUGC()->GetItemInstallInfo(fileID, NULL, fullpath, PATH_MAX, NULL) )
17142 	{
17143 		gamemods_modPreload = true;
17144 		bool addToPath = !gamemodsIsPathInMountedFiles(fullpath);
17145 		if ( PHYSFS_mount(fullpath, NULL, 0) )
17146 		{
17147 			reloadLanguage();
17148 			if ( addToPath )
17149 			{
17150 				gamemods_mountedFilepaths.push_back(std::make_pair(fullpath, modTitle)); // change string to your mod name here.
17151 				gamemods_workshopLoadedFileIDMap.push_back(std::make_pair(modTitle, fileID));
17152 			}
17153 		}
17154 	}
17155 }
17156 #else
serialHash(std::string input)17157 size_t serialHash(std::string input)
17158 {
17159 	if ( input.empty() || input.size() != 19 )
17160 	{
17161 		return 0;
17162 	}
17163 	int i = 0;
17164 	size_t hash = 0;
17165 	for ( std::string::iterator it = input.begin(); it != input.end(); ++it )
17166 	{
17167 		char c = *it;
17168 		if ( c == '\0' || c == '\n' )
17169 		{
17170 			break;
17171 		}
17172 		hash += static_cast<size_t>(c) * (i * i);
17173 		++i;
17174 	}
17175 	return hash;
17176 }
17177 
buttonConfirmSerial(button_t * my)17178 void buttonConfirmSerial(button_t* my)
17179 {
17180 	serialVerifyWindow = 1;
17181 	if ( SDL_IsTextInputActive() )
17182 	{
17183 		SDL_StopTextInput();
17184 	}
17185 	list_FreeAll(&button_l);
17186 	deleteallbuttons = true;
17187 }
17188 
windowSerialResult(int success)17189 void windowSerialResult(int success)
17190 {
17191 	// close current window
17192 	if ( success > 0 )
17193 	{
17194 		char path[PATH_MAX] = "";
17195 		if ( success == 2 )
17196 		{
17197 			completePath(path, "legendsandpariahs.key", outputdir);
17198 		}
17199 		else if ( success == 1 )
17200 		{
17201 			completePath(path, "mythsandoutcasts.key", outputdir);
17202 		}
17203 
17204 		// open the serial file
17205 		FILE* fp = nullptr;
17206 		if ( (fp = fopen(path, "wb")) == NULL )
17207 		{
17208 			printlog("ERROR: failed to save license file!\n");
17209 		}
17210 		else
17211 		{
17212 			fwrite(serialInputText, sizeof(char), strlen(serialInputText), fp); // write out serial to file.
17213 			fclose(fp);
17214 		}
17215 	}
17216 	buttonCloseSubwindow(nullptr);
17217 	list_FreeAll(&button_l);
17218 	deleteallbuttons = true;
17219 
17220 	subwindow = 1;
17221 	subx1 = xres / 2 - 250;
17222 	subx2 = xres / 2 + 250;
17223 	suby1 = yres / 2 - 32;
17224 	suby2 = yres / 2 + 32;
17225 
17226 	// ok button
17227 	button_t* button = newButton();
17228 	strcpy(button->label, language[1317]);
17229 	button->x = subx2 - strlen(language[1317]) * 12 - 16;
17230 	button->y = suby2 - 28;
17231 	button->sizex = strlen(language[1317]) * 12 + 8;
17232 	button->sizey = 20;
17233 	button->action = &buttonCloseSubwindow;
17234 	button->visible = 1;
17235 	button->focused = 1;
17236 
17237 	// close button
17238 	button = newButton();
17239 	strcpy(button->label, "x");
17240 	button->x = subx2 - 20;
17241 	button->y = suby1;
17242 	button->sizex = 20;
17243 	button->sizey = 20;
17244 	button->action = &buttonCloseSubwindow;
17245 	button->visible = 1;
17246 	button->focused = 1;
17247 	button->key = SDL_SCANCODE_ESCAPE;
17248 	button->joykey = joyimpulses[INJOY_MENU_CANCEL];
17249 
17250 	if ( success > 0 )
17251 	{
17252 		if ( success == 2 )
17253 		{
17254 			strcpy(subtext, language[3405]);
17255 		}
17256 		else if ( success == 1 )
17257 		{
17258 			strcpy(subtext, language[3404]);
17259 		}
17260 		playSound(402, 92);
17261 	}
17262 	else
17263 	{
17264 		strcpy(subtext, language[3406]);
17265 	}
17266 }
17267 
windowEnterSerialPrompt()17268 void windowEnterSerialPrompt()
17269 {
17270 	// create confirmation window
17271 	subwindow = 1;
17272 	subx1 = xres / 2 - 300;
17273 	subx2 = xres / 2 + 300;
17274 	suby1 = yres / 2 - 64;
17275 	suby2 = yres / 2 + 64;
17276 	strcpy(subtext, language[3403]);
17277 	strcpy(serialInputText, "");
17278 	serialEnterWindow = true;
17279 	serialVerifyWindow = 0;
17280 
17281 	// yes button
17282 	button_t* button = newButton();
17283 	strcpy(button->label, "Submit");
17284 	button->x = subx2 - strlen("Submit") * 12 - 16;
17285 	button->y = suby2 - 28 * 2;
17286 	button->sizex = strlen("Submit") * 12 + 8;
17287 	button->sizey = 20;
17288 	button->action = &buttonConfirmSerial;
17289 	button->visible = 1;
17290 	button->focused = 1;
17291 	button->key = SDL_SCANCODE_RETURN;
17292 	button->joykey = joyimpulses[INJOY_MENU_NEXT]; //TODO: Select which button to activate via dpad.
17293 
17294 	// cancel button
17295 	button = newButton();
17296 	strcpy(button->label, language[1316]);
17297 	button->x = subx2 - strlen(language[1316]) * 12 - 16;
17298 	button->y = suby2 - 28;
17299 	button->sizex = strlen(language[1316]) * 12 + 8;
17300 	button->sizey = 20;
17301 	button->action = &buttonCloseSubwindow;
17302 	button->visible = 1;
17303 	button->focused = 1;
17304 
17305 	// close button
17306 	button = newButton();
17307 	strcpy(button->label, "x");
17308 	button->x = subx2 - 20;
17309 	button->y = suby1;
17310 	button->sizex = 20;
17311 	button->sizey = 20;
17312 	button->action = &buttonCloseSubwindow;
17313 	button->visible = 1;
17314 	button->focused = 1;
17315 	button->key = SDL_SCANCODE_ESCAPE;
17316 	button->joykey = joyimpulses[INJOY_MENU_CANCEL];
17317 }
17318 #endif // STEAMWORKS