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