1 /**
2 * @file diablo.cpp
3 *
4 * Implementation of the main game initialization functions.
5 */
6 #include "all.h"
7 #include "paths.h"
8 #include "console.h"
9 #include "options.h"
10 #include "../3rdParty/Storm/Source/storm.h"
11 #include "../DiabloUI/diabloui.h"
12 #include <config.h>
13
14 DEVILUTION_BEGIN_NAMESPACE
15
16 SDL_Window *ghMainWnd;
17 DWORD glSeedTbl[NUMLEVELS];
18 dungeon_type gnLevelTypeTbl[NUMLEVELS];
19 int glEndSeed[NUMLEVELS];
20 int glMid1Seed[NUMLEVELS];
21 int glMid2Seed[NUMLEVELS];
22 int glMid3Seed[NUMLEVELS];
23 int MouseX;
24 int MouseY;
25 BOOL gbGameLoopStartup;
26 BOOL gbRunGame;
27 BOOL gbRunGameResult;
28 BOOL zoomflag;
29 /** Enable updating of player character, set to false once Diablo dies */
30 BOOL gbProcessPlayers;
31 BOOL gbLoadGame;
32 BOOLEAN cineflag;
33 int force_redraw;
34 BOOL light4flag;
35 int setseed;
36 int PauseMode;
37 bool forceSpawn;
38 bool forceDiablo;
39 bool gbTheoQuest;
40 bool gbCowQuest;
41 bool gbNestArt;
42 bool gbBard;
43 bool gbBarbarian;
44 int sgnTimeoutCurs;
45 char sgbMouseDown;
46 int color_cycle_timer;
47 int gnTickRate;
48 WORD gnTickDelay = 50;
49 /** Game options */
50 Options sgOptions;
51
52 /* rdata */
53
54 bool gbForceWindowed = false;
55 bool gbShowIntro = true;
56 bool leveldebug = false;
57 #ifdef _DEBUG
58 bool monstdebug = false;
59 _monster_id DebugMonsters[10];
60 int debugmonsttypes = 0;
61 bool visiondebug = false;
62 int questdebug = -1;
63 bool debug_mode_key_s = false;
64 bool debug_mode_key_w = false;
65 bool debug_mode_key_inverted_v = false;
66 bool debug_mode_dollar_sign = false;
67 bool debug_mode_key_d = false;
68 bool debug_mode_key_i = false;
69 int debug_mode_key_j = 0;
70 int arrowdebug = 0;
71 #endif
72 /** Specifies whether players are in non-PvP mode. */
73 bool gbFriendlyMode = true;
74 /** Specifies players will still damage other players in non-PvP mode. */
75 bool gbFriendlyFire;
76 /** Default quick messages */
77 const char *const spszMsgTbl[] = {
78 "I need help! Come Here!",
79 "Follow me.",
80 "Here's something for you.",
81 "Now you DIE!"
82 };
83 /** INI files variable names for quick message keys */
84 const char *const spszMsgHotKeyTbl[] = { "F9", "F10", "F11", "F12" };
85
86 /** To know if these things have been done when we get to the diablo_deinit() function */
87 bool was_archives_init = false;
88 /** To know if surfaces have been initialized or not */
89 bool was_window_init = false;
90 bool was_ui_init = false;
91 bool was_snd_init = false;
92 bool sbWasOptionsLoaded = false;
93
94 // Controller support:
95 extern void plrctrls_every_frame();
96 extern void plrctrls_after_game_logic();
97
print_help_and_exit()98 [[noreturn]] static void print_help_and_exit()
99 {
100 printInConsole("Options:\n");
101 printInConsole(" %-20s %-30s\n", "-h, --help", "Print this message and exit");
102 printInConsole(" %-20s %-30s\n", "--version", "Print the version and exit");
103 printInConsole(" %-20s %-30s\n", "--data-dir", "Specify the folder of diabdat.mpq");
104 printInConsole(" %-20s %-30s\n", "--save-dir", "Specify the folder of save files");
105 printInConsole(" %-20s %-30s\n", "--config-dir", "Specify the location of diablo.ini");
106 printInConsole(" %-20s %-30s\n", "--ttf-dir", "Specify the location of the .ttf font");
107 printInConsole(" %-20s %-30s\n", "--ttf-name", "Specify the name of a custom .ttf font");
108 printInConsole(" %-20s %-30s\n", "-n", "Skip startup videos");
109 printInConsole(" %-20s %-30s\n", "-f", "Display frames per second");
110 printInConsole(" %-20s %-30s\n", "-x", "Run in windowed mode");
111 printInConsole(" %-20s %-30s\n", "--spawn", "Force spawn mode even if diabdat.mpq is found");
112 printInConsole("\nHellfire options:\n");
113 printInConsole(" %-20s %-30s\n", "--diablo", "Force diablo mode even if hellfire.mpq is found");
114 printInConsole(" %-20s %-30s\n", "--nestart", "Use alternate nest palette");
115 #ifdef _DEBUG
116 printInConsole("\nDebug options:\n");
117 printInConsole(" %-20s %-30s\n", "-d", "Increaased item drops");
118 printInConsole(" %-20s %-30s\n", "-w", "Enable cheats");
119 printInConsole(" %-20s %-30s\n", "-$", "Enable god mode");
120 printInConsole(" %-20s %-30s\n", "-^", "Enable god mode and debug tools");
121 printInConsole(" %-20s %-30s\n", "-v", "Highlight visibility");
122 printInConsole(" %-20s %-30s\n", "-i", "Ignore network timeout");
123 printInConsole(" %-20s %-30s\n", "-j <##>", "Mausoleum warps to given level");
124 printInConsole(" %-20s %-30s\n", "-l <##> <##>", "Start in level as type");
125 printInConsole(" %-20s %-30s\n", "-m <##>", "Add debug monster, up to 10 allowed");
126 printInConsole(" %-20s %-30s\n", "-q <#>", "Force a certain quest");
127 printInConsole(" %-20s %-30s\n", "-r <##########>", "Set map seed");
128 printInConsole(" %-20s %-30s\n", "-t <##>", "Set current quest level");
129 #endif
130 printInConsole("\nReport bugs at https://github.com/diasurgical/devilutionX/\n");
131 diablo_quit(0);
132 }
133
diablo_parse_flags(int argc,char ** argv)134 static void diablo_parse_flags(int argc, char **argv)
135 {
136 for (int i = 1; i < argc; i++) {
137 if (strcasecmp("-h", argv[i]) == 0 || strcasecmp("--help", argv[i]) == 0) {
138 print_help_and_exit();
139 } else if (strcasecmp("--version", argv[i]) == 0) {
140 printInConsole("%s v%s\n", PROJECT_NAME, PROJECT_VERSION);
141 diablo_quit(0);
142 } else if (strcasecmp("--data-dir", argv[i]) == 0) {
143 SetBasePath(argv[++i]);
144 } else if (strcasecmp("--save-dir", argv[i]) == 0) {
145 SetPrefPath(argv[++i]);
146 } else if (strcasecmp("--config-dir", argv[i]) == 0) {
147 SetConfigPath(argv[++i]);
148 } else if (strcasecmp("--ttf-dir", argv[i]) == 0) {
149 SetTtfPath(argv[++i]);
150 } else if (strcasecmp("--ttf-name", argv[i]) == 0) {
151 SetTtfName(argv[++i]);
152 } else if (strcasecmp("-n", argv[i]) == 0) {
153 gbShowIntro = false;
154 } else if (strcasecmp("-f", argv[i]) == 0) {
155 EnableFrameCount();
156 } else if (strcasecmp("-x", argv[i]) == 0) {
157 gbForceWindowed = true;
158 } else if (strcasecmp("--spawn", argv[i]) == 0) {
159 forceSpawn = true;
160 } else if (strcasecmp("--diablo", argv[i]) == 0) {
161 forceDiablo = true;
162 } else if (strcasecmp("--nestart", argv[i]) == 0) {
163 gbNestArt = true;
164 } else if (strcasecmp("--vanilla", argv[i]) == 0) {
165 gbVanilla = true;
166 #ifdef _DEBUG
167 } else if (strcasecmp("-^", argv[i]) == 0) {
168 debug_mode_key_inverted_v = true;
169 } else if (strcasecmp("-$", argv[i]) == 0) {
170 debug_mode_dollar_sign = true;
171 } else if (strcasecmp("-d", argv[i]) == 0) {
172 debug_mode_key_d = true;
173 } else if (strcasecmp("-i", argv[i]) == 0) {
174 debug_mode_key_i = true;
175 } else if (strcasecmp("-j", argv[i]) == 0) {
176 debug_mode_key_j = SDL_atoi(argv[++i]);
177 } else if (strcasecmp("-l", argv[i]) == 0) {
178 setlevel = FALSE;
179 leveldebug = true;
180 leveltype = (dungeon_type)SDL_atoi(argv[++i]);
181 currlevel = SDL_atoi(argv[++i]);
182 plr[0].plrlevel = currlevel;
183 } else if (strcasecmp("-m", argv[i]) == 0) {
184 monstdebug = true;
185 DebugMonsters[debugmonsttypes++] = (_monster_id)SDL_atoi(argv[++i]);
186 } else if (strcasecmp("-q", argv[i]) == 0) {
187 questdebug = SDL_atoi(argv[++i]);
188 } else if (strcasecmp("-r", argv[i]) == 0) {
189 setseed = SDL_atoi(argv[++i]);
190 } else if (strcasecmp("-s", argv[i]) == 0) {
191 debug_mode_key_s = true;
192 } else if (strcasecmp("-t", argv[i]) == 0) {
193 leveldebug = true;
194 setlevel = true;
195 setlvlnum = SDL_atoi(argv[++i]);
196 } else if (strcasecmp("-v", argv[i]) == 0) {
197 visiondebug = true;
198 } else if (strcasecmp("-w", argv[i]) == 0) {
199 debug_mode_key_w = true;
200 #endif
201 } else {
202 printInConsole("unrecognized option '%s'\n", argv[i]);
203 print_help_and_exit();
204 }
205 }
206 }
207
FreeGameMem()208 void FreeGameMem()
209 {
210 music_stop();
211
212 MemFreeDbg(pDungeonCels);
213 MemFreeDbg(pMegaTiles);
214 MemFreeDbg(pLevelPieces);
215 MemFreeDbg(pSpecialCels);
216
217 FreeMissiles();
218 FreeMonsters();
219 FreeObjectGFX();
220 FreeMonsterSnd();
221 FreeTownerGFX();
222 }
223
start_game(interface_mode uMsg)224 static void start_game(interface_mode uMsg)
225 {
226 zoomflag = TRUE;
227 CalcViewportGeometry();
228 cineflag = FALSE;
229 InitCursor();
230 InitLightTable();
231 #ifdef _DEBUG
232 LoadDebugGFX();
233 #endif
234 assert(ghMainWnd);
235 music_stop();
236 InitQol();
237 ShowProgress(uMsg);
238 gmenu_init_menu();
239 InitLevelCursor();
240 sgnTimeoutCurs = CURSOR_NONE;
241 sgbMouseDown = CLICK_NONE;
242 track_repeat_walk(FALSE);
243 }
244
free_game()245 static void free_game()
246 {
247 int i;
248
249 FreeQol();
250 FreeControlPan();
251 FreeInvGFX();
252 FreeGMenu();
253 FreeQuestText();
254 FreeStoreMem();
255
256 for (i = 0; i < MAX_PLRS; i++)
257 FreePlayerGFX(i);
258
259 FreeItemGFX();
260 FreeCursor();
261 FreeLightTable();
262 #ifdef _DEBUG
263 FreeDebugGFX();
264 #endif
265 FreeGameMem();
266 }
267
268 // Controller support: Actions to run after updating the cursor state.
269 // Defined in SourceX/controls/plctrls.cpp.
270 extern void finish_simulated_mouse_clicks(int current_mouse_x, int current_mouse_y);
271 extern void plrctrls_after_check_curs_move();
272
ProcessInput()273 static bool ProcessInput()
274 {
275 if (PauseMode == 2) {
276 return false;
277 }
278
279 plrctrls_every_frame();
280
281 if (!gbIsMultiplayer && gmenu_is_active()) {
282 force_redraw |= 1;
283 return false;
284 }
285
286 if (!gmenu_is_active() && sgnTimeoutCurs == CURSOR_NONE) {
287 #ifndef USE_SDL1
288 finish_simulated_mouse_clicks(MouseX, MouseY);
289 #endif
290 CheckCursMove();
291 plrctrls_after_check_curs_move();
292 track_process();
293 }
294
295 return true;
296 }
297
run_game_loop(interface_mode uMsg)298 static void run_game_loop(interface_mode uMsg)
299 {
300 WNDPROC saveProc;
301 MSG msg;
302
303 nthread_ignore_mutex(TRUE);
304 start_game(uMsg);
305 assert(ghMainWnd);
306 saveProc = SetWindowProc(GM_Game);
307 control_update_life_mana();
308 run_delta_info();
309 gbRunGame = TRUE;
310 gbProcessPlayers = TRUE;
311 gbRunGameResult = TRUE;
312 force_redraw = 255;
313 DrawAndBlit();
314 LoadPWaterPalette();
315 PaletteFadeIn(8);
316 force_redraw = 255;
317 gbGameLoopStartup = TRUE;
318 nthread_ignore_mutex(FALSE);
319
320 while (gbRunGame) {
321 while (FetchMessage(&msg)) {
322 if (msg.message == DVL_WM_QUIT) {
323 gbRunGameResult = FALSE;
324 gbRunGame = FALSE;
325 break;
326 }
327 TranslateMessage(&msg);
328 PushMessage(&msg);
329 }
330 if (!gbRunGame)
331 break;
332 if (!nthread_has_500ms_passed()) {
333 ProcessInput();
334 force_redraw |= 1;
335 DrawAndBlit();
336 continue;
337 }
338 diablo_color_cyc_logic();
339 multi_process_network_packets();
340 game_loop(gbGameLoopStartup);
341 gbGameLoopStartup = FALSE;
342 DrawAndBlit();
343 }
344
345 if (gbIsMultiplayer) {
346 pfile_write_hero();
347 }
348
349 pfile_flush_W();
350 PaletteFadeOut(8);
351 NewCursor(CURSOR_NONE);
352 ClearScreenBuffer();
353 force_redraw = 255;
354 scrollrt_draw_game_screen(TRUE);
355 saveProc = SetWindowProc(saveProc);
356 assert(saveProc == GM_Game);
357 free_game();
358
359 if (cineflag) {
360 cineflag = FALSE;
361 DoEnding();
362 }
363 }
364
StartGame(BOOL bNewGame,BOOL bSinglePlayer)365 BOOL StartGame(BOOL bNewGame, BOOL bSinglePlayer)
366 {
367 BOOL fExitProgram;
368
369 gbSelectProvider = TRUE;
370
371 do {
372 fExitProgram = FALSE;
373 gbLoadGame = FALSE;
374
375 if (!NetInit(bSinglePlayer, &fExitProgram)) {
376 gbRunGameResult = !fExitProgram;
377 break;
378 }
379
380 gbSelectProvider = FALSE;
381
382 if (bNewGame || !gbValidSaveFile) {
383 InitLevels();
384 InitQuests();
385 InitPortals();
386 InitDungMsgs(myplr);
387 }
388 interface_mode uMsg = WM_DIABNEWGAME;
389 if (gbValidSaveFile && gbLoadGame) {
390 uMsg = WM_DIABLOADGAME;
391 }
392 run_game_loop(uMsg);
393 NetClose();
394 pfile_create_player_description(NULL, 0);
395 } while (gbRunGameResult);
396
397 SNetDestroy();
398 return gbRunGameResult;
399 }
400
401 /**
402 * @brief Save game configurations to ini file
403 */
SaveOptions()404 static void SaveOptions()
405 {
406 setIniInt("Diablo", "Intro", sgOptions.Diablo.bIntro);
407 setIniInt("Hellfire", "Intro", sgOptions.Hellfire.bIntro);
408 setIniValue("Hellfire", "SItem", sgOptions.Hellfire.szItem);
409
410 setIniInt("Audio", "Sound Volume", sgOptions.Audio.nSoundVolume);
411 setIniInt("Audio", "Music Volume", sgOptions.Audio.nMusicVolume);
412 setIniInt("Audio", "Walking Sound", sgOptions.Audio.bWalkingSound);
413 setIniInt("Audio", "Auto Equip Sound", sgOptions.Audio.bAutoEquipSound);
414
415 #ifndef __vita__
416 setIniInt("Graphics", "Width", sgOptions.Graphics.nWidth);
417 setIniInt("Graphics", "Height", sgOptions.Graphics.nHeight);
418 #endif
419 setIniInt("Graphics", "Fullscreen", sgOptions.Graphics.bFullscreen);
420 #ifndef __vita__
421 setIniInt("Graphics", "Upscale", sgOptions.Graphics.bUpscale);
422 #endif
423 setIniInt("Graphics", "Fit to Screen", sgOptions.Graphics.bFitToScreen);
424 setIniValue("Graphics", "Scaling Quality", sgOptions.Graphics.szScaleQuality);
425 setIniInt("Graphics", "Integer Scaling", sgOptions.Graphics.bIntegerScaling);
426 setIniInt("Graphics", "Vertical Sync", sgOptions.Graphics.bVSync);
427 setIniInt("Graphics", "Blended Transparency", sgOptions.Graphics.bBlendedTransparancy);
428 setIniInt("Graphics", "Gamma Correction", sgOptions.Graphics.nGammaCorrection);
429 setIniInt("Graphics", "Color Cycling", sgOptions.Graphics.bColorCycling);
430 setIniInt("Graphics", "FPS Limiter", sgOptions.Graphics.bFPSLimit);
431
432 setIniInt("Game", "Speed", sgOptions.Gameplay.nTickRate);
433 setIniInt("Game", "Run in Town", sgOptions.Gameplay.bRunInTown);
434 setIniInt("Game", "Grab Input", sgOptions.Gameplay.bGrabInput);
435 setIniInt("Game", "Theo Quest", sgOptions.Gameplay.bTheoQuest);
436 setIniInt("Game", "Cow Quest", sgOptions.Gameplay.bCowQuest);
437 setIniInt("Game", "Friendly Fire", sgOptions.Gameplay.bFriendlyFire);
438 setIniInt("Game", "Test Bard", sgOptions.Gameplay.bTestBard);
439 setIniInt("Game", "Test Barbarian", sgOptions.Gameplay.bTestBarbarian);
440 setIniInt("Game", "Experience Bar", sgOptions.Gameplay.bExperienceBar);
441 setIniInt("Game", "Enemy Health Bar", sgOptions.Gameplay.bEnemyHealthBar);
442 setIniInt("Game", "Auto Gold Pickup", sgOptions.Gameplay.bAutoGoldPickup);
443 setIniInt("Game", "Adria Refills Mana", sgOptions.Gameplay.bAdriaRefillsMana);
444 setIniInt("Game", "Auto Equip Weapons", sgOptions.Gameplay.bAutoEquipWeapons);
445 setIniInt("Game", "Auto Equip Armor", sgOptions.Gameplay.bAutoEquipArmor);
446 setIniInt("Game", "Auto Equip Helms", sgOptions.Gameplay.bAutoEquipHelms);
447 setIniInt("Game", "Auto Equip Shields", sgOptions.Gameplay.bAutoEquipShields);
448 setIniInt("Game", "Auto Equip Jewelry", sgOptions.Gameplay.bAutoEquipJewelry);
449 setIniInt("Game", "Randomize Quests", sgOptions.Gameplay.bRandomizeQuests);
450 setIniInt("Game", "Show Monster Type", sgOptions.Gameplay.bShowMonsterType);
451
452 setIniValue("Network", "Bind Address", sgOptions.Network.szBindAddress);
453 setIniInt("Network", "Port", sgOptions.Network.nPort);
454 setIniValue("Network", "Previous Host", sgOptions.Network.szPreviousHost);
455
456 for (int i = 0; i < sizeof(spszMsgTbl) / sizeof(spszMsgTbl[0]); i++)
457 setIniValue("NetMsg", spszMsgHotKeyTbl[i], sgOptions.Chat.szHotKeyMsgs[i]);
458
459 setIniValue("Controller", "Mapping", sgOptions.Controller.szMapping);
460 setIniInt("Controller", "Swap Shoulder Button Mode", sgOptions.Controller.bSwapShoulderButtonMode);
461 setIniInt("Controller", "Dpad Hotkeys", sgOptions.Controller.bDpadHotkeys);
462 #ifdef __vita__
463 setIniInt("Controller", "Enable Rear Touchpad", sgOptions.Controller.bRearTouch);
464 #endif
465
466 SaveIni();
467 }
468
469 /**
470 * @brief Load game configurations from ini file
471 */
LoadOptions()472 static void LoadOptions()
473 {
474 sgOptions.Diablo.bIntro = getIniBool("Diablo", "Intro", true);
475 sgOptions.Hellfire.bIntro = getIniBool("Hellfire", "Intro", true);
476 getIniValue("Hellfire", "SItem", sgOptions.Hellfire.szItem, sizeof(sgOptions.Hellfire.szItem), "");
477
478 sgOptions.Audio.nSoundVolume = getIniInt("Audio", "Sound Volume", VOLUME_MAX);
479 sgOptions.Audio.nMusicVolume = getIniInt("Audio", "Music Volume", VOLUME_MAX);
480 sgOptions.Audio.bWalkingSound = getIniBool("Audio", "Walking Sound", true);
481 sgOptions.Audio.bAutoEquipSound = getIniBool("Audio", "Auto Equip Sound", false);
482
483 #ifndef __vita__
484 sgOptions.Graphics.nWidth = getIniInt("Graphics", "Width", DEFAULT_WIDTH);
485 sgOptions.Graphics.nHeight = getIniInt("Graphics", "Height", DEFAULT_HEIGHT);
486 #else
487 sgOptions.Graphics.nWidth = DEFAULT_WIDTH;
488 sgOptions.Graphics.nHeight = DEFAULT_HEIGHT;
489 #endif
490 sgOptions.Graphics.bFullscreen = getIniBool("Graphics", "Fullscreen", true);
491 #if !defined(USE_SDL1) && !defined(__vita__)
492 sgOptions.Graphics.bUpscale = getIniBool("Graphics", "Upscale", true);
493 #else
494 sgOptions.Graphics.bUpscale = false;
495 #endif
496 sgOptions.Graphics.bFitToScreen = getIniBool("Graphics", "Fit to Screen", true);
497 getIniValue("Graphics", "Scaling Quality", sgOptions.Graphics.szScaleQuality, sizeof(sgOptions.Graphics.szScaleQuality), "2");
498 sgOptions.Graphics.bIntegerScaling = getIniBool("Graphics", "Integer Scaling", false);
499 sgOptions.Graphics.bVSync = getIniBool("Graphics", "Vertical Sync", true);
500 sgOptions.Graphics.bBlendedTransparancy = getIniBool("Graphics", "Blended Transparency", true);
501 sgOptions.Graphics.nGammaCorrection = getIniInt("Graphics", "Gamma Correction", 100);
502 sgOptions.Graphics.bColorCycling = getIniBool("Graphics", "Color Cycling", true);
503 sgOptions.Graphics.bFPSLimit = getIniBool("Graphics", "FPS Limiter", true);
504
505 sgOptions.Gameplay.nTickRate = getIniInt("Game", "Speed", 20);
506 sgOptions.Gameplay.bRunInTown = getIniBool("Game", "Run in Town", false);
507 sgOptions.Gameplay.bGrabInput = getIniBool("Game", "Grab Input", false);
508 sgOptions.Gameplay.bTheoQuest = getIniBool("Game", "Theo Quest", false);
509 sgOptions.Gameplay.bCowQuest = getIniBool("Game", "Cow Quest", false);
510 sgOptions.Gameplay.bFriendlyFire = getIniBool("Game", "Friendly Fire", true);
511 sgOptions.Gameplay.bTestBard = getIniBool("Game", "Test Bard", false);
512 sgOptions.Gameplay.bTestBarbarian = getIniBool("Game", "Test Barbarian", false);
513 sgOptions.Gameplay.bExperienceBar = getIniBool("Game", "Experience Bar", false);
514 sgOptions.Gameplay.bEnemyHealthBar = getIniBool("Game", "Enemy Health Bar", false);
515 sgOptions.Gameplay.bAutoGoldPickup = getIniBool("Game", "Auto Gold Pickup", false);
516 sgOptions.Gameplay.bAdriaRefillsMana = getIniBool("Game", "Adria Refills Mana", false);
517 sgOptions.Gameplay.bAutoEquipWeapons = getIniBool("Game", "Auto Equip Weapons", true);
518 sgOptions.Gameplay.bAutoEquipArmor = getIniBool("Game", "Auto Equip Armor", false);
519 sgOptions.Gameplay.bAutoEquipHelms = getIniBool("Game", "Auto Equip Helms", false);
520 sgOptions.Gameplay.bAutoEquipShields = getIniBool("Game", "Auto Equip Shields", false);
521 sgOptions.Gameplay.bAutoEquipJewelry = getIniBool("Game", "Auto Equip Jewelry", false);
522 sgOptions.Gameplay.bRandomizeQuests = getIniBool("Game", "Randomize Quests", true);
523 sgOptions.Gameplay.bShowMonsterType = getIniBool("Game", "Show Monster Type", false);
524
525 getIniValue("Network", "Bind Address", sgOptions.Network.szBindAddress, sizeof(sgOptions.Network.szBindAddress), "0.0.0.0");
526 sgOptions.Network.nPort = getIniInt("Network", "Port", 6112);
527 getIniValue("Network", "Previous Host", sgOptions.Network.szPreviousHost, sizeof(sgOptions.Network.szPreviousHost), "");
528
529 for (int i = 0; i < sizeof(spszMsgTbl) / sizeof(spszMsgTbl[0]); i++)
530 getIniValue("NetMsg", spszMsgHotKeyTbl[i], sgOptions.Chat.szHotKeyMsgs[i], MAX_SEND_STR_LEN, spszMsgTbl[i]);
531
532 getIniValue("Controller", "Mapping", sgOptions.Controller.szMapping, sizeof(sgOptions.Controller.szMapping), "");
533 sgOptions.Controller.bSwapShoulderButtonMode = getIniBool("Controller", "Swap Shoulder Button Mode", false);
534 sgOptions.Controller.bDpadHotkeys = getIniBool("Controller", "Dpad Hotkeys", false);
535 #ifdef __vita__
536 sgOptions.Controller.bRearTouch = getIniBool("Controller", "Enable Rear Touchpad", true);
537 #endif
538
539 sbWasOptionsLoaded = true;
540 }
541
diablo_init_screen()542 static void diablo_init_screen()
543 {
544 MouseX = gnScreenWidth / 2;
545 MouseY = gnScreenHeight / 2;
546 if (!sgbControllerActive)
547 SetCursorPos(MouseX, MouseY);
548 ScrollInfo._sdx = 0;
549 ScrollInfo._sdy = 0;
550 ScrollInfo._sxoff = 0;
551 ScrollInfo._syoff = 0;
552 ScrollInfo._sdir = SDIR_NONE;
553
554 ClrDiabloMsg();
555 }
556
diablo_init()557 static void diablo_init()
558 {
559 init_create_window();
560 was_window_init = true;
561
562 SFileEnableDirectAccess(TRUE);
563 init_archives();
564 was_archives_init = true;
565
566 if (forceSpawn)
567 gbIsSpawn = true;
568 if (forceDiablo)
569 gbIsHellfire = false;
570
571 gbIsHellfireSaveGame = gbIsHellfire;
572
573 UiInitialize();
574 UiSetSpawned(gbIsSpawn);
575 was_ui_init = true;
576
577 ReadOnlyTest();
578
579 InitHash();
580
581 diablo_init_screen();
582
583 snd_init();
584 was_snd_init = true;
585
586 ui_sound_init();
587 }
588
diablo_splash()589 static void diablo_splash()
590 {
591 if (!gbShowIntro)
592 return;
593
594 play_movie("gendata\\logo.smk", TRUE);
595
596 if (gbIsHellfire && sgOptions.Hellfire.bIntro) {
597 play_movie("gendata\\Hellfire.smk", TRUE);
598 sgOptions.Hellfire.bIntro = false;
599 }
600 if (!gbIsHellfire && !gbIsSpawn && sgOptions.Diablo.bIntro) {
601 play_movie("gendata\\diablo1.smk", TRUE);
602 sgOptions.Diablo.bIntro = false;
603 }
604
605 UiTitleDialog();
606 }
607
diablo_deinit()608 static void diablo_deinit()
609 {
610 if (sbWasOptionsLoaded)
611 SaveOptions();
612 if (was_snd_init) {
613 effects_cleanup_sfx();
614 }
615 if (was_ui_init)
616 UiDestroy();
617 if (was_archives_init)
618 init_cleanup();
619 if (was_window_init)
620 dx_cleanup(); // Cleanup SDL surfaces stuff, so we have to do it before SDL_Quit().
621 if (was_fonts_init)
622 FontsCleanup();
623 if (SDL_WasInit(SDL_INIT_EVERYTHING & ~SDL_INIT_HAPTIC))
624 SDL_Quit();
625 }
626
diablo_quit(int exitStatus)627 void diablo_quit(int exitStatus)
628 {
629 diablo_deinit();
630 exit(exitStatus);
631 }
632
DiabloMain(int argc,char ** argv)633 int DiabloMain(int argc, char **argv)
634 {
635 diablo_parse_flags(argc, argv);
636 LoadOptions();
637 diablo_init();
638 diablo_splash();
639 mainmenu_loop();
640 diablo_deinit();
641
642 return 0;
643 }
644
LeftMouseCmd(BOOL bShift)645 static BOOL LeftMouseCmd(BOOL bShift)
646 {
647 BOOL bNear;
648
649 assert(MouseY < PANEL_TOP || MouseX < PANEL_LEFT || MouseX >= PANEL_LEFT + PANEL_WIDTH);
650
651 if (leveltype == DTYPE_TOWN) {
652 if (pcursitem != -1 && pcurs == CURSOR_HAND)
653 NetSendCmdLocParam1(TRUE, invflag ? CMD_GOTOGETITEM : CMD_GOTOAGETITEM, cursmx, cursmy, pcursitem);
654 if (pcursmonst != -1)
655 NetSendCmdLocParam1(TRUE, CMD_TALKXY, cursmx, cursmy, pcursmonst);
656 if (pcursitem == -1 && pcursmonst == -1 && pcursplr == -1)
657 return TRUE;
658 } else {
659 bNear = abs(plr[myplr]._px - cursmx) < 2 && abs(plr[myplr]._py - cursmy) < 2;
660 if (pcursitem != -1 && pcurs == CURSOR_HAND && !bShift) {
661 NetSendCmdLocParam1(TRUE, invflag ? CMD_GOTOGETITEM : CMD_GOTOAGETITEM, cursmx, cursmy, pcursitem);
662 } else if (pcursobj != -1 && (!bShift || (bNear && object[pcursobj]._oBreak == 1))) {
663 NetSendCmdLocParam1(TRUE, pcurs == CURSOR_DISARM ? CMD_DISARMXY : CMD_OPOBJXY, cursmx, cursmy, pcursobj);
664 } else if (plr[myplr]._pwtype == WT_RANGED) {
665 if (bShift) {
666 NetSendCmdLoc(TRUE, CMD_RATTACKXY, cursmx, cursmy);
667 } else if (pcursmonst != -1) {
668 if (CanTalkToMonst(pcursmonst)) {
669 NetSendCmdParam1(TRUE, CMD_ATTACKID, pcursmonst);
670 } else {
671 NetSendCmdParam1(TRUE, CMD_RATTACKID, pcursmonst);
672 }
673 } else if (pcursplr != -1 && !gbFriendlyMode) {
674 NetSendCmdParam1(TRUE, CMD_RATTACKPID, pcursplr);
675 }
676 } else {
677 if (bShift) {
678 if (pcursmonst != -1) {
679 if (CanTalkToMonst(pcursmonst)) {
680 NetSendCmdParam1(TRUE, CMD_ATTACKID, pcursmonst);
681 } else {
682 NetSendCmdLoc(TRUE, CMD_SATTACKXY, cursmx, cursmy);
683 }
684 } else {
685 NetSendCmdLoc(TRUE, CMD_SATTACKXY, cursmx, cursmy);
686 }
687 } else if (pcursmonst != -1) {
688 NetSendCmdParam1(TRUE, CMD_ATTACKID, pcursmonst);
689 } else if (pcursplr != -1 && !gbFriendlyMode) {
690 NetSendCmdParam1(TRUE, CMD_ATTACKPID, pcursplr);
691 }
692 }
693 if (!bShift && pcursitem == -1 && pcursobj == -1 && pcursmonst == -1 && pcursplr == -1)
694 return TRUE;
695 }
696
697 return FALSE;
698 }
699
TryIconCurs()700 BOOL TryIconCurs()
701 {
702 if (pcurs == CURSOR_RESURRECT) {
703 NetSendCmdParam1(TRUE, CMD_RESURRECT, pcursplr);
704 return TRUE;
705 }
706
707 if (pcurs == CURSOR_HEALOTHER) {
708 NetSendCmdParam1(TRUE, CMD_HEALOTHER, pcursplr);
709 return TRUE;
710 }
711
712 if (pcurs == CURSOR_TELEKINESIS) {
713 DoTelekinesis();
714 return TRUE;
715 }
716
717 if (pcurs == CURSOR_IDENTIFY) {
718 if (pcursinvitem != -1)
719 CheckIdentify(myplr, pcursinvitem);
720 else
721 NewCursor(CURSOR_HAND);
722 return TRUE;
723 }
724
725 if (pcurs == CURSOR_REPAIR) {
726 if (pcursinvitem != -1)
727 DoRepair(myplr, pcursinvitem);
728 else
729 NewCursor(CURSOR_HAND);
730 return TRUE;
731 }
732
733 if (pcurs == CURSOR_RECHARGE) {
734 if (pcursinvitem != -1)
735 DoRecharge(myplr, pcursinvitem);
736 else
737 NewCursor(CURSOR_HAND);
738 return TRUE;
739 }
740
741 if (pcurs == CURSOR_OIL) {
742 if (pcursinvitem != -1)
743 DoOil(myplr, pcursinvitem);
744 else
745 NewCursor(CURSOR_HAND);
746 return TRUE;
747 }
748
749 if (pcurs == CURSOR_TELEPORT) {
750 if (pcursmonst != -1)
751 NetSendCmdParam3(TRUE, CMD_TSPELLID, pcursmonst, plr[myplr]._pTSpell, GetSpellLevel(myplr, plr[myplr]._pTSpell));
752 else if (pcursplr != -1)
753 NetSendCmdParam3(TRUE, CMD_TSPELLPID, pcursplr, plr[myplr]._pTSpell, GetSpellLevel(myplr, plr[myplr]._pTSpell));
754 else
755 NetSendCmdLocParam2(TRUE, CMD_TSPELLXY, cursmx, cursmy, plr[myplr]._pTSpell, GetSpellLevel(myplr, plr[myplr]._pTSpell));
756 NewCursor(CURSOR_HAND);
757 return TRUE;
758 }
759
760 if (pcurs == CURSOR_DISARM && pcursobj == -1) {
761 NewCursor(CURSOR_HAND);
762 return TRUE;
763 }
764
765 return FALSE;
766 }
767
LeftMouseDown(int wParam)768 static BOOL LeftMouseDown(int wParam)
769 {
770 if (gmenu_left_mouse(TRUE))
771 return FALSE;
772
773 if (control_check_talk_btn())
774 return FALSE;
775
776 if (sgnTimeoutCurs != CURSOR_NONE)
777 return FALSE;
778
779 if (deathflag) {
780 control_check_btn_press();
781 return FALSE;
782 }
783
784 if (PauseMode == 2) {
785 return FALSE;
786 }
787 if (doomflag) {
788 doom_close();
789 return FALSE;
790 }
791
792 if (spselflag) {
793 SetSpell();
794 return FALSE;
795 }
796
797 if (stextflag != STORE_NONE) {
798 CheckStoreBtn();
799 return FALSE;
800 }
801
802 bool isShiftHeld = wParam & DVL_MK_SHIFT;
803
804 if (MouseY < PANEL_TOP || MouseX < PANEL_LEFT || MouseX >= PANEL_LEFT + PANEL_WIDTH) {
805 if (!gmenu_is_active() && !TryIconCurs()) {
806 if (questlog && MouseX > 32 && MouseX < 288 && MouseY > 32 && MouseY < 308) {
807 QuestlogESC();
808 } else if (qtextflag) {
809 qtextflag = FALSE;
810 stream_stop();
811 } else if (chrflag && MouseX < SPANEL_WIDTH && MouseY < SPANEL_HEIGHT) {
812 CheckChrBtns();
813 } else if (invflag && MouseX > RIGHT_PANEL && MouseY < SPANEL_HEIGHT) {
814 if (!dropGoldFlag)
815 CheckInvItem(isShiftHeld);
816 } else if (sbookflag && MouseX > RIGHT_PANEL && MouseY < SPANEL_HEIGHT) {
817 CheckSBook();
818 } else if (pcurs >= CURSOR_FIRSTITEM) {
819 if (TryInvPut()) {
820 NetSendCmdPItem(TRUE, CMD_PUTITEM, cursmx, cursmy);
821 NewCursor(CURSOR_HAND);
822 }
823 } else {
824 if (plr[myplr]._pStatPts != 0 && !spselflag)
825 CheckLvlBtn();
826 if (!lvlbtndown)
827 return LeftMouseCmd(isShiftHeld);
828 }
829 }
830 } else {
831 if (!talkflag && !dropGoldFlag && !gmenu_is_active())
832 CheckInvScrn(isShiftHeld);
833 DoPanBtn();
834 if (pcurs > CURSOR_HAND && pcurs < CURSOR_FIRSTITEM)
835 NewCursor(CURSOR_HAND);
836 }
837
838 return FALSE;
839 }
840
LeftMouseUp(int wParam)841 static void LeftMouseUp(int wParam)
842 {
843 gmenu_left_mouse(FALSE);
844 control_release_talk_btn();
845 bool isShiftHeld = wParam & (DVL_MK_SHIFT | DVL_MK_LBUTTON);
846 if (panbtndown)
847 CheckBtnUp();
848 if (chrbtnactive)
849 ReleaseChrBtns(isShiftHeld);
850 if (lvlbtndown)
851 ReleaseLvlBtn();
852 if (stextflag != STORE_NONE)
853 ReleaseStoreBtn();
854 }
855
RightMouseDown()856 static void RightMouseDown()
857 {
858 if (!gmenu_is_active() && sgnTimeoutCurs == CURSOR_NONE && PauseMode != 2 && !plr[myplr]._pInvincible) {
859 if (doomflag) {
860 doom_close();
861 } else if (stextflag == STORE_NONE) {
862 if (spselflag) {
863 SetSpell();
864 } else if (MouseY >= SPANEL_HEIGHT
865 || (!sbookflag || MouseX <= RIGHT_PANEL)
866 && !TryIconCurs()
867 && (pcursinvitem == -1 || !UseInvItem(myplr, pcursinvitem))) {
868 if (pcurs == CURSOR_HAND) {
869 if (pcursinvitem == -1 || !UseInvItem(myplr, pcursinvitem))
870 CheckPlrSpell();
871 } else if (pcurs > CURSOR_HAND && pcurs < CURSOR_FIRSTITEM) {
872 NewCursor(CURSOR_HAND);
873 }
874 }
875 }
876 }
877 }
878
diablo_pause_game()879 void diablo_pause_game()
880 {
881 if (!gbIsMultiplayer) {
882 if (PauseMode) {
883 PauseMode = 0;
884 } else {
885 PauseMode = 2;
886 sound_stop();
887 track_repeat_walk(FALSE);
888 }
889 force_redraw = 255;
890 }
891 }
892
diablo_hotkey_msg(DWORD dwMsg)893 static void diablo_hotkey_msg(DWORD dwMsg)
894 {
895 if (!gbIsMultiplayer) {
896 return;
897 }
898
899 assert(dwMsg < sizeof(spszMsgTbl) / sizeof(spszMsgTbl[0]));
900
901 NetSendCmdString(-1, sgOptions.Chat.szHotKeyMsgs[dwMsg]);
902 }
903
PressSysKey(int wParam)904 static BOOL PressSysKey(int wParam)
905 {
906 if (gmenu_is_active() || wParam != DVL_VK_F10)
907 return FALSE;
908 diablo_hotkey_msg(1);
909 return TRUE;
910 }
911
ReleaseKey(int vkey)912 static void ReleaseKey(int vkey)
913 {
914 if (vkey == DVL_VK_SNAPSHOT)
915 CaptureScreen();
916 }
917
ClosePanels()918 static void ClosePanels()
919 {
920 if (PANELS_COVER) {
921 if (!chrflag && !questlog && (invflag || sbookflag) && MouseX < 480 && MouseY < PANEL_TOP) {
922 SetCursorPos(MouseX + 160, MouseY);
923 } else if (!invflag && !sbookflag && (chrflag || questlog) && MouseX > 160 && MouseY < PANEL_TOP) {
924 SetCursorPos(MouseX - 160, MouseY);
925 }
926 }
927 invflag = FALSE;
928 chrflag = FALSE;
929 sbookflag = FALSE;
930 questlog = FALSE;
931 }
932
PressEscKey()933 bool PressEscKey()
934 {
935 bool rv = false;
936
937 if (doomflag) {
938 doom_close();
939 rv = true;
940 }
941
942 if (helpflag) {
943 helpflag = FALSE;
944 rv = true;
945 }
946
947 if (qtextflag) {
948 qtextflag = FALSE;
949 stream_stop();
950 rv = true;
951 }
952
953 if (stextflag) {
954 STextESC();
955 rv = true;
956 }
957
958 if (msgflag) {
959 msgdelay = 0;
960 rv = true;
961 }
962
963 if (talkflag) {
964 control_reset_talk();
965 rv = true;
966 }
967
968 if (dropGoldFlag) {
969 control_drop_gold(DVL_VK_ESCAPE);
970 rv = true;
971 }
972
973 if (spselflag) {
974 spselflag = FALSE;
975 rv = true;
976 }
977
978 if (invflag || chrflag || sbookflag || questlog) {
979 ClosePanels();
980 rv = true;
981 }
982
983 return rv;
984 }
985
PressKey(int vkey)986 static void PressKey(int vkey)
987 {
988 if (gmenu_presskeys(vkey) || control_presskeys(vkey)) {
989 return;
990 }
991
992 if (deathflag) {
993 if (sgnTimeoutCurs != CURSOR_NONE) {
994 return;
995 }
996 if (vkey == DVL_VK_F9) {
997 diablo_hotkey_msg(0);
998 }
999 if (vkey == DVL_VK_F10) {
1000 diablo_hotkey_msg(1);
1001 }
1002 if (vkey == DVL_VK_F11) {
1003 diablo_hotkey_msg(2);
1004 }
1005 if (vkey == DVL_VK_F12) {
1006 diablo_hotkey_msg(3);
1007 }
1008 if (vkey == DVL_VK_RETURN) {
1009 if (GetAsyncKeyState(DVL_VK_MENU) & 0x8000)
1010 dx_reinit();
1011 else
1012 control_type_message();
1013 }
1014 if (vkey != DVL_VK_ESCAPE) {
1015 return;
1016 }
1017 }
1018 if (vkey == DVL_VK_ESCAPE) {
1019 if (!PressEscKey()) {
1020 track_repeat_walk(FALSE);
1021 gamemenu_on();
1022 }
1023 return;
1024 }
1025
1026 if (sgnTimeoutCurs != CURSOR_NONE || dropGoldFlag) {
1027 return;
1028 }
1029 if (vkey == DVL_VK_PAUSE) {
1030 diablo_pause_game();
1031 return;
1032 }
1033 if (PauseMode == 2) {
1034 if ((vkey == DVL_VK_RETURN) && (GetAsyncKeyState(DVL_VK_MENU) & 0x8000))
1035 dx_reinit();
1036 return;
1037 }
1038
1039 if (vkey == DVL_VK_RETURN) {
1040 if (GetAsyncKeyState(DVL_VK_MENU) & 0x8000) {
1041 dx_reinit();
1042 } else if (stextflag) {
1043 STextEnter();
1044 } else if (questlog) {
1045 QuestlogEnter();
1046 } else {
1047 control_type_message();
1048 }
1049 } else if (vkey == DVL_VK_F1) {
1050 if (helpflag) {
1051 helpflag = FALSE;
1052 } else if (stextflag != STORE_NONE) {
1053 ClearPanel();
1054 AddPanelString("No help available", TRUE); /// BUGFIX: message isn't displayed
1055 AddPanelString("while in stores", TRUE);
1056 track_repeat_walk(FALSE);
1057 } else {
1058 invflag = FALSE;
1059 chrflag = FALSE;
1060 sbookflag = FALSE;
1061 spselflag = FALSE;
1062 if (qtextflag && leveltype == DTYPE_TOWN) {
1063 qtextflag = FALSE;
1064 stream_stop();
1065 }
1066 questlog = FALSE;
1067 automapflag = FALSE;
1068 msgdelay = 0;
1069 gamemenu_off();
1070 DisplayHelp();
1071 doom_close();
1072 }
1073 }
1074 #ifdef _DEBUG
1075 else if (vkey == DVL_VK_F2) {
1076 }
1077 #endif
1078 #ifdef _DEBUG
1079 else if (vkey == DVL_VK_F3) {
1080 if (pcursitem != -1) {
1081 sprintf(
1082 tempstr,
1083 "IDX = %i : Seed = %i : CF = %i",
1084 item[pcursitem].IDidx,
1085 item[pcursitem]._iSeed,
1086 item[pcursitem]._iCreateInfo);
1087 NetSendCmdString(1 << myplr, tempstr);
1088 }
1089 sprintf(tempstr, "Numitems : %i", numitems);
1090 NetSendCmdString(1 << myplr, tempstr);
1091 }
1092 #endif
1093 #ifdef _DEBUG
1094 else if (vkey == DVL_VK_F4) {
1095 PrintDebugQuest();
1096 }
1097 #endif
1098 else if (vkey == DVL_VK_F5) {
1099 if (spselflag) {
1100 SetSpeedSpell(0);
1101 return;
1102 }
1103 ToggleSpell(0);
1104 return;
1105 } else if (vkey == DVL_VK_F6) {
1106 if (spselflag) {
1107 SetSpeedSpell(1);
1108 return;
1109 }
1110 ToggleSpell(1);
1111 return;
1112 } else if (vkey == DVL_VK_F7) {
1113 if (spselflag) {
1114 SetSpeedSpell(2);
1115 return;
1116 }
1117 ToggleSpell(2);
1118 return;
1119 } else if (vkey == DVL_VK_F8) {
1120 if (spselflag) {
1121 SetSpeedSpell(3);
1122 return;
1123 }
1124 ToggleSpell(3);
1125 return;
1126 } else if (vkey == DVL_VK_F9) {
1127 diablo_hotkey_msg(0);
1128 } else if (vkey == DVL_VK_F10) {
1129 diablo_hotkey_msg(1);
1130 } else if (vkey == DVL_VK_F11) {
1131 diablo_hotkey_msg(2);
1132 } else if (vkey == DVL_VK_F12) {
1133 diablo_hotkey_msg(3);
1134 } else if (vkey == DVL_VK_UP) {
1135 if (stextflag) {
1136 STextUp();
1137 } else if (questlog) {
1138 QuestlogUp();
1139 } else if (helpflag) {
1140 HelpScrollUp();
1141 } else if (automapflag) {
1142 AutomapUp();
1143 }
1144 } else if (vkey == DVL_VK_DOWN) {
1145 if (stextflag) {
1146 STextDown();
1147 } else if (questlog) {
1148 QuestlogDown();
1149 } else if (helpflag) {
1150 HelpScrollDown();
1151 } else if (automapflag) {
1152 AutomapDown();
1153 }
1154 } else if (vkey == DVL_VK_PRIOR) {
1155 if (stextflag) {
1156 STextPrior();
1157 }
1158 } else if (vkey == DVL_VK_NEXT) {
1159 if (stextflag) {
1160 STextNext();
1161 }
1162 } else if (vkey == DVL_VK_LEFT) {
1163 if (automapflag && !talkflag) {
1164 AutomapLeft();
1165 }
1166 } else if (vkey == DVL_VK_RIGHT) {
1167 if (automapflag && !talkflag) {
1168 AutomapRight();
1169 }
1170 } else if (vkey == DVL_VK_TAB) {
1171 DoAutoMap();
1172 } else if (vkey == DVL_VK_SPACE) {
1173 ClosePanels();
1174 helpflag = FALSE;
1175 spselflag = FALSE;
1176 if (qtextflag && leveltype == DTYPE_TOWN) {
1177 qtextflag = FALSE;
1178 stream_stop();
1179 }
1180 automapflag = FALSE;
1181 msgdelay = 0;
1182 gamemenu_off();
1183 doom_close();
1184 }
1185 }
1186
1187 /**
1188 * @internal `return` must be used instead of `break` to be bin exact as C++
1189 */
PressChar(WPARAM vkey)1190 static void PressChar(WPARAM vkey)
1191 {
1192 if (gmenu_is_active() || control_talk_last_key(vkey) || sgnTimeoutCurs != CURSOR_NONE || deathflag) {
1193 return;
1194 }
1195 if ((char)vkey == 'p' || (char)vkey == 'P') {
1196 diablo_pause_game();
1197 return;
1198 }
1199 if (PauseMode == 2) {
1200 return;
1201 }
1202 if (doomflag) {
1203 doom_close();
1204 return;
1205 }
1206 if (dropGoldFlag) {
1207 control_drop_gold(vkey);
1208 return;
1209 }
1210
1211 switch (vkey) {
1212 case 'G':
1213 case 'g':
1214 DecreaseGamma();
1215 return;
1216 case 'F':
1217 case 'f':
1218 IncreaseGamma();
1219 return;
1220 case 'I':
1221 case 'i':
1222 if (stextflag == STORE_NONE) {
1223 invflag = !invflag;
1224 if (!chrflag && !questlog && PANELS_COVER) {
1225 if (!invflag) { // We closed the invetory
1226 if (MouseX < 480 && MouseY < PANEL_TOP) {
1227 SetCursorPos(MouseX + 160, MouseY);
1228 }
1229 } else if (!sbookflag) { // We opened the invetory
1230 if (MouseX > 160 && MouseY < PANEL_TOP) {
1231 SetCursorPos(MouseX - 160, MouseY);
1232 }
1233 }
1234 }
1235 sbookflag = FALSE;
1236 }
1237 return;
1238 case 'C':
1239 case 'c':
1240 if (stextflag == STORE_NONE) {
1241 chrflag = !chrflag;
1242 if (!invflag && !sbookflag && PANELS_COVER) {
1243 if (!chrflag) { // We closed the character sheet
1244 if (MouseX > 160 && MouseY < PANEL_TOP) {
1245 SetCursorPos(MouseX - 160, MouseY);
1246 }
1247 } else if (!questlog) { // We opened the character sheet
1248 if (MouseX < 480 && MouseY < PANEL_TOP) {
1249 SetCursorPos(MouseX + 160, MouseY);
1250 }
1251 }
1252 }
1253 questlog = FALSE;
1254 }
1255 return;
1256 case 'Q':
1257 case 'q':
1258 if (stextflag == STORE_NONE) {
1259 if (!questlog) {
1260 StartQuestlog();
1261 } else {
1262 questlog = FALSE;
1263 }
1264 if (!invflag && !sbookflag && PANELS_COVER) {
1265 if (!questlog) { // We closed the quest log
1266 if (MouseX > 160 && MouseY < PANEL_TOP) {
1267 SetCursorPos(MouseX - 160, MouseY);
1268 }
1269 } else if (!chrflag) { // We opened the character quest log
1270 if (MouseX < 480 && MouseY < PANEL_TOP) {
1271 SetCursorPos(MouseX + 160, MouseY);
1272 }
1273 }
1274 }
1275 chrflag = FALSE;
1276 }
1277 return;
1278 case 'Z':
1279 case 'z':
1280 zoomflag = !zoomflag;
1281 CalcViewportGeometry();
1282 return;
1283 case 'S':
1284 case 's':
1285 if (stextflag == STORE_NONE) {
1286 chrflag = FALSE;
1287 questlog = FALSE;
1288 invflag = FALSE;
1289 sbookflag = FALSE;
1290 if (!spselflag) {
1291 DoSpeedBook();
1292 } else {
1293 spselflag = FALSE;
1294 }
1295 track_repeat_walk(FALSE);
1296 }
1297 return;
1298 case 'B':
1299 case 'b':
1300 if (stextflag == STORE_NONE) {
1301 sbookflag = !sbookflag;
1302 if (!chrflag && !questlog && PANELS_COVER) {
1303 if (!sbookflag) { // We closed the invetory
1304 if (MouseX < 480 && MouseY < PANEL_TOP) {
1305 SetCursorPos(MouseX + 160, MouseY);
1306 }
1307 } else if (!invflag) { // We opened the invetory
1308 if (MouseX > 160 && MouseY < PANEL_TOP) {
1309 SetCursorPos(MouseX - 160, MouseY);
1310 }
1311 }
1312 }
1313 invflag = FALSE;
1314 }
1315 return;
1316 case '+':
1317 case '=':
1318 if (automapflag) {
1319 AutomapZoomIn();
1320 }
1321 return;
1322 case '-':
1323 case '_':
1324 if (automapflag) {
1325 AutomapZoomOut();
1326 }
1327 return;
1328 case 'v': {
1329 char pszStr[120];
1330 const char *difficulties[3] = {
1331 "Normal",
1332 "Nightmare",
1333 "Hell",
1334 };
1335 sprintf(pszStr, "%s, mode = %s", gszProductName, difficulties[gnDifficulty]);
1336 NetSendCmdString(1 << myplr, pszStr);
1337 return;
1338 }
1339 case 'V':
1340 NetSendCmdString(1 << myplr, gszVersionNumber);
1341 return;
1342 case '!':
1343 case '1':
1344 if (!plr[myplr].SpdList[0].isEmpty() && plr[myplr].SpdList[0]._itype != ITYPE_GOLD) {
1345 UseInvItem(myplr, INVITEM_BELT_FIRST);
1346 }
1347 return;
1348 case '@':
1349 case '2':
1350 if (!plr[myplr].SpdList[1].isEmpty() && plr[myplr].SpdList[1]._itype != ITYPE_GOLD) {
1351 UseInvItem(myplr, INVITEM_BELT_FIRST + 1);
1352 }
1353 return;
1354 case '#':
1355 case '3':
1356 if (!plr[myplr].SpdList[2].isEmpty() && plr[myplr].SpdList[2]._itype != ITYPE_GOLD) {
1357 UseInvItem(myplr, INVITEM_BELT_FIRST + 2);
1358 }
1359 return;
1360 case '$':
1361 case '4':
1362 if (!plr[myplr].SpdList[3].isEmpty() && plr[myplr].SpdList[3]._itype != ITYPE_GOLD) {
1363 UseInvItem(myplr, INVITEM_BELT_FIRST + 3);
1364 }
1365 return;
1366 case '%':
1367 case '5':
1368 if (!plr[myplr].SpdList[4].isEmpty() && plr[myplr].SpdList[4]._itype != ITYPE_GOLD) {
1369 UseInvItem(myplr, INVITEM_BELT_FIRST + 4);
1370 }
1371 return;
1372 case '^':
1373 case '6':
1374 if (!plr[myplr].SpdList[5].isEmpty() && plr[myplr].SpdList[5]._itype != ITYPE_GOLD) {
1375 UseInvItem(myplr, INVITEM_BELT_FIRST + 5);
1376 }
1377 return;
1378 case '&':
1379 case '7':
1380 if (!plr[myplr].SpdList[6].isEmpty() && plr[myplr].SpdList[6]._itype != ITYPE_GOLD) {
1381 UseInvItem(myplr, INVITEM_BELT_FIRST + 6);
1382 }
1383 return;
1384 case '*':
1385 case '8':
1386 #ifdef _DEBUG
1387 if (debug_mode_key_inverted_v || debug_mode_key_w) {
1388 NetSendCmd(TRUE, CMD_CHEAT_EXPERIENCE);
1389 return;
1390 }
1391 #endif
1392 if (!plr[myplr].SpdList[7].isEmpty() && plr[myplr].SpdList[7]._itype != ITYPE_GOLD) {
1393 UseInvItem(myplr, INVITEM_BELT_FIRST + 7);
1394 }
1395 return;
1396 #ifdef _DEBUG
1397 case ')':
1398 case '0':
1399 if (debug_mode_key_inverted_v) {
1400 if (arrowdebug > 2) {
1401 arrowdebug = 0;
1402 }
1403 if (arrowdebug == 0) {
1404 plr[myplr]._pIFlags &= ~ISPL_FIRE_ARROWS;
1405 plr[myplr]._pIFlags &= ~ISPL_LIGHT_ARROWS;
1406 }
1407 if (arrowdebug == 1) {
1408 plr[myplr]._pIFlags |= ISPL_FIRE_ARROWS;
1409 }
1410 if (arrowdebug == 2) {
1411 plr[myplr]._pIFlags |= ISPL_LIGHT_ARROWS;
1412 }
1413 arrowdebug++;
1414 }
1415 return;
1416 case ':':
1417 if (currlevel == 0 && debug_mode_key_w) {
1418 SetAllSpellsCheat();
1419 }
1420 return;
1421 case '[':
1422 if (currlevel == 0 && debug_mode_key_w) {
1423 TakeGoldCheat();
1424 }
1425 return;
1426 case ']':
1427 if (currlevel == 0 && debug_mode_key_w) {
1428 MaxSpellsCheat();
1429 }
1430 return;
1431 case 'a':
1432 if (debug_mode_key_inverted_v) {
1433 spelldata[SPL_TELEPORT].sTownSpell = 1;
1434 plr[myplr]._pSplLvl[plr[myplr]._pSpell]++;
1435 }
1436 return;
1437 case 'D':
1438 PrintDebugPlayer(TRUE);
1439 return;
1440 case 'd':
1441 PrintDebugPlayer(FALSE);
1442 return;
1443 case 'L':
1444 case 'l':
1445 if (debug_mode_key_inverted_v) {
1446 ToggleLighting();
1447 }
1448 return;
1449 case 'M':
1450 NextDebugMonster();
1451 return;
1452 case 'm':
1453 GetDebugMonster();
1454 return;
1455 case 'R':
1456 case 'r':
1457 sprintf(tempstr, "seed = %i", glSeedTbl[currlevel]);
1458 NetSendCmdString(1 << myplr, tempstr);
1459 sprintf(tempstr, "Mid1 = %i : Mid2 = %i : Mid3 = %i", glMid1Seed[currlevel], glMid2Seed[currlevel], glMid3Seed[currlevel]);
1460 NetSendCmdString(1 << myplr, tempstr);
1461 sprintf(tempstr, "End = %i", glEndSeed[currlevel]);
1462 NetSendCmdString(1 << myplr, tempstr);
1463 return;
1464 case 'T':
1465 case 't':
1466 if (debug_mode_key_inverted_v) {
1467 sprintf(tempstr, "PX = %i PY = %i", plr[myplr]._px, plr[myplr]._py);
1468 NetSendCmdString(1 << myplr, tempstr);
1469 sprintf(tempstr, "CX = %i CY = %i DP = %i", cursmx, cursmy, dungeon[cursmx][cursmy]);
1470 NetSendCmdString(1 << myplr, tempstr);
1471 }
1472 return;
1473 case '|':
1474 if (currlevel == 0 && debug_mode_key_w) {
1475 GiveGoldCheat();
1476 }
1477 return;
1478 #endif
1479 }
1480 }
1481
GetMousePos(LPARAM lParam)1482 static void GetMousePos(LPARAM lParam)
1483 {
1484 MouseX = (short)(lParam & 0xffff);
1485 MouseY = (short)((lParam >> 16) & 0xffff);
1486 }
1487
DisableInputWndProc(UINT uMsg,WPARAM wParam,LPARAM lParam)1488 void DisableInputWndProc(UINT uMsg, WPARAM wParam, LPARAM lParam)
1489 {
1490 switch (uMsg) {
1491 case DVL_WM_KEYDOWN:
1492 case DVL_WM_KEYUP:
1493 case DVL_WM_CHAR:
1494 case DVL_WM_SYSKEYDOWN:
1495 case DVL_WM_SYSCOMMAND:
1496 return;
1497 case DVL_WM_MOUSEMOVE:
1498 GetMousePos(lParam);
1499 return;
1500 case DVL_WM_LBUTTONDOWN:
1501 if (sgbMouseDown != CLICK_NONE)
1502 return;
1503 sgbMouseDown = CLICK_LEFT;
1504 return;
1505 case DVL_WM_LBUTTONUP:
1506 if (sgbMouseDown != CLICK_LEFT)
1507 return;
1508 sgbMouseDown = CLICK_NONE;
1509 return;
1510 case DVL_WM_RBUTTONDOWN:
1511 if (sgbMouseDown != CLICK_NONE)
1512 return;
1513 sgbMouseDown = CLICK_RIGHT;
1514 return;
1515 case DVL_WM_RBUTTONUP:
1516 if (sgbMouseDown != CLICK_RIGHT)
1517 return;
1518 sgbMouseDown = CLICK_NONE;
1519 return;
1520 case DVL_WM_CAPTURECHANGED:
1521 sgbMouseDown = CLICK_NONE;
1522 return;
1523 }
1524
1525 MainWndProc(uMsg, wParam, lParam);
1526 }
1527
GM_Game(UINT uMsg,WPARAM wParam,LPARAM lParam)1528 void GM_Game(UINT uMsg, WPARAM wParam, LPARAM lParam)
1529 {
1530 switch (uMsg) {
1531 case DVL_WM_KEYDOWN:
1532 PressKey(wParam);
1533 return;
1534 case DVL_WM_KEYUP:
1535 ReleaseKey(wParam);
1536 return;
1537 case DVL_WM_CHAR:
1538 PressChar(wParam);
1539 return;
1540 case DVL_WM_SYSKEYDOWN:
1541 if (PressSysKey(wParam))
1542 return;
1543 break;
1544 case DVL_WM_SYSCOMMAND:
1545 if (wParam == DVL_SC_CLOSE) {
1546 gbRunGame = FALSE;
1547 gbRunGameResult = FALSE;
1548 return;
1549 }
1550 break;
1551 case DVL_WM_MOUSEMOVE:
1552 GetMousePos(lParam);
1553 gmenu_on_mouse_move();
1554 return;
1555 case DVL_WM_LBUTTONDOWN:
1556 GetMousePos(lParam);
1557 if (sgbMouseDown == CLICK_NONE) {
1558 sgbMouseDown = CLICK_LEFT;
1559 track_repeat_walk(LeftMouseDown(wParam));
1560 }
1561 return;
1562 case DVL_WM_LBUTTONUP:
1563 GetMousePos(lParam);
1564 if (sgbMouseDown == CLICK_LEFT) {
1565 sgbMouseDown = CLICK_NONE;
1566 LeftMouseUp(wParam);
1567 track_repeat_walk(FALSE);
1568 }
1569 return;
1570 case DVL_WM_RBUTTONDOWN:
1571 GetMousePos(lParam);
1572 if (sgbMouseDown == CLICK_NONE) {
1573 sgbMouseDown = CLICK_RIGHT;
1574 RightMouseDown();
1575 }
1576 return;
1577 case DVL_WM_RBUTTONUP:
1578 GetMousePos(lParam);
1579 if (sgbMouseDown == CLICK_RIGHT) {
1580 sgbMouseDown = CLICK_NONE;
1581 }
1582 return;
1583 case DVL_WM_CAPTURECHANGED:
1584 sgbMouseDown = CLICK_NONE;
1585 track_repeat_walk(FALSE);
1586 break;
1587 case WM_DIABNEXTLVL:
1588 case WM_DIABPREVLVL:
1589 case WM_DIABRTNLVL:
1590 case WM_DIABSETLVL:
1591 case WM_DIABWARPLVL:
1592 case WM_DIABTOWNWARP:
1593 case WM_DIABTWARPUP:
1594 case WM_DIABRETOWN:
1595 if (gbIsMultiplayer)
1596 pfile_write_hero();
1597 nthread_ignore_mutex(TRUE);
1598 PaletteFadeOut(8);
1599 sound_stop();
1600 music_stop();
1601 track_repeat_walk(FALSE);
1602 sgbMouseDown = CLICK_NONE;
1603 ShowProgress((interface_mode)uMsg);
1604 force_redraw = 255;
1605 DrawAndBlit();
1606 LoadPWaterPalette();
1607 if (gbRunGame)
1608 PaletteFadeIn(8);
1609 nthread_ignore_mutex(FALSE);
1610 gbGameLoopStartup = TRUE;
1611 return;
1612 }
1613
1614 MainWndProc(uMsg, wParam, lParam);
1615 }
1616
LoadLvlGFX()1617 void LoadLvlGFX()
1618 {
1619 assert(!pDungeonCels);
1620
1621 switch (leveltype) {
1622 case DTYPE_TOWN:
1623 if (gbIsHellfire) {
1624 pDungeonCels = LoadFileInMem("NLevels\\TownData\\Town.CEL", NULL);
1625 pMegaTiles = LoadFileInMem("NLevels\\TownData\\Town.TIL", NULL);
1626 pLevelPieces = LoadFileInMem("NLevels\\TownData\\Town.MIN", NULL);
1627 } else {
1628 pDungeonCels = LoadFileInMem("Levels\\TownData\\Town.CEL", NULL);
1629 pMegaTiles = LoadFileInMem("Levels\\TownData\\Town.TIL", NULL);
1630 pLevelPieces = LoadFileInMem("Levels\\TownData\\Town.MIN", NULL);
1631 }
1632 pSpecialCels = LoadFileInMem("Levels\\TownData\\TownS.CEL", NULL);
1633 break;
1634 case DTYPE_CATHEDRAL:
1635 if (currlevel < 21) {
1636 pDungeonCels = LoadFileInMem("Levels\\L1Data\\L1.CEL", NULL);
1637 pMegaTiles = LoadFileInMem("Levels\\L1Data\\L1.TIL", NULL);
1638 pLevelPieces = LoadFileInMem("Levels\\L1Data\\L1.MIN", NULL);
1639 pSpecialCels = LoadFileInMem("Levels\\L1Data\\L1S.CEL", NULL);
1640 } else {
1641 pDungeonCels = LoadFileInMem("NLevels\\L5Data\\L5.CEL", NULL);
1642 pMegaTiles = LoadFileInMem("NLevels\\L5Data\\L5.TIL", NULL);
1643 pLevelPieces = LoadFileInMem("NLevels\\L5Data\\L5.MIN", NULL);
1644 pSpecialCels = LoadFileInMem("NLevels\\L5Data\\L5S.CEL", NULL);
1645 }
1646 break;
1647 case DTYPE_CATACOMBS:
1648 pDungeonCels = LoadFileInMem("Levels\\L2Data\\L2.CEL", NULL);
1649 pMegaTiles = LoadFileInMem("Levels\\L2Data\\L2.TIL", NULL);
1650 pLevelPieces = LoadFileInMem("Levels\\L2Data\\L2.MIN", NULL);
1651 pSpecialCels = LoadFileInMem("Levels\\L2Data\\L2S.CEL", NULL);
1652 break;
1653 case DTYPE_CAVES:
1654 if (currlevel < 17) {
1655 pDungeonCels = LoadFileInMem("Levels\\L3Data\\L3.CEL", NULL);
1656 pMegaTiles = LoadFileInMem("Levels\\L3Data\\L3.TIL", NULL);
1657 pLevelPieces = LoadFileInMem("Levels\\L3Data\\L3.MIN", NULL);
1658 } else {
1659 pDungeonCels = LoadFileInMem("NLevels\\L6Data\\L6.CEL", NULL);
1660 pMegaTiles = LoadFileInMem("NLevels\\L6Data\\L6.TIL", NULL);
1661 pLevelPieces = LoadFileInMem("NLevels\\L6Data\\L6.MIN", NULL);
1662 }
1663 pSpecialCels = LoadFileInMem("Levels\\L1Data\\L1S.CEL", NULL);
1664 break;
1665 case DTYPE_HELL:
1666 pDungeonCels = LoadFileInMem("Levels\\L4Data\\L4.CEL", NULL);
1667 pMegaTiles = LoadFileInMem("Levels\\L4Data\\L4.TIL", NULL);
1668 pLevelPieces = LoadFileInMem("Levels\\L4Data\\L4.MIN", NULL);
1669 pSpecialCels = LoadFileInMem("Levels\\L2Data\\L2S.CEL", NULL);
1670 break;
1671 default:
1672 app_fatal("LoadLvlGFX");
1673 }
1674 }
1675
LoadAllGFX()1676 void LoadAllGFX()
1677 {
1678 IncProgress();
1679 IncProgress();
1680 InitObjectGFX();
1681 IncProgress();
1682 InitMissileGFX();
1683 IncProgress();
1684 }
1685
1686 /**
1687 * @param lvldir method of entry
1688 */
CreateLevel(int lvldir)1689 void CreateLevel(int lvldir)
1690 {
1691 switch (leveltype) {
1692 case DTYPE_TOWN:
1693 CreateTown(lvldir);
1694 InitTownTriggers();
1695 LoadRndLvlPal(0);
1696 break;
1697 case DTYPE_CATHEDRAL:
1698 CreateL5Dungeon(glSeedTbl[currlevel], lvldir);
1699 InitL1Triggers();
1700 Freeupstairs();
1701 if (currlevel < 21) {
1702 LoadRndLvlPal(1);
1703 } else {
1704 LoadRndLvlPal(5);
1705 }
1706 break;
1707 case DTYPE_CATACOMBS:
1708 CreateL2Dungeon(glSeedTbl[currlevel], lvldir);
1709 InitL2Triggers();
1710 Freeupstairs();
1711 LoadRndLvlPal(2);
1712 break;
1713 case DTYPE_CAVES:
1714 CreateL3Dungeon(glSeedTbl[currlevel], lvldir);
1715 InitL3Triggers();
1716 Freeupstairs();
1717 if (currlevel < 17) {
1718 LoadRndLvlPal(3);
1719 } else {
1720 LoadRndLvlPal(6);
1721 }
1722 break;
1723 case DTYPE_HELL:
1724 CreateL4Dungeon(glSeedTbl[currlevel], lvldir);
1725 InitL4Triggers();
1726 Freeupstairs();
1727 LoadRndLvlPal(4);
1728 break;
1729 default:
1730 app_fatal("CreateLevel");
1731 }
1732 }
1733
UpdateMonsterLights()1734 static void UpdateMonsterLights()
1735 {
1736 for (int i = 0; i < nummonsters; i++) {
1737 MonsterStruct *mon = &monster[monstactive[i]];
1738 if (mon->mlid != NO_LIGHT) {
1739 if (mon->mlid == plr[myplr]._plid) { // Fix old saves where some monsters had 0 instead of NO_LIGHT
1740 mon->mlid = NO_LIGHT;
1741 continue;
1742 }
1743
1744 LightListStruct *lid = &LightList[mon->mlid];
1745 if (mon->_mx != lid->_lx || mon->_my != lid->_ly) {
1746 ChangeLightXY(mon->mlid, mon->_mx, mon->_my);
1747 }
1748 }
1749 }
1750 }
1751
LoadGameLevel(BOOL firstflag,int lvldir)1752 void LoadGameLevel(BOOL firstflag, int lvldir)
1753 {
1754 int i, j;
1755 BOOL visited;
1756
1757 if (setseed)
1758 glSeedTbl[currlevel] = setseed;
1759
1760 music_stop();
1761 if (pcurs > CURSOR_HAND && pcurs < CURSOR_FIRSTITEM) {
1762 NewCursor(CURSOR_HAND);
1763 }
1764 SetRndSeed(glSeedTbl[currlevel]);
1765 IncProgress();
1766 MakeLightTable();
1767 LoadLvlGFX();
1768 IncProgress();
1769
1770 if (firstflag) {
1771 InitInv();
1772 InitItemGFX();
1773 InitQuestText();
1774
1775 int players = gbIsMultiplayer ? MAX_PLRS : 1;
1776 for (i = 0; i < players; i++)
1777 InitPlrGFXMem(i);
1778
1779 InitStores();
1780 InitAutomapOnce();
1781 InitHelp();
1782 }
1783
1784 SetRndSeed(glSeedTbl[currlevel]);
1785
1786 if (leveltype == DTYPE_TOWN)
1787 SetupTownStores();
1788
1789 IncProgress();
1790 InitAutomap();
1791
1792 if (leveltype != DTYPE_TOWN && lvldir != ENTRY_LOAD) {
1793 InitLighting();
1794 InitVision();
1795 }
1796
1797 InitLevelMonsters();
1798 IncProgress();
1799
1800 if (!setlevel) {
1801 CreateLevel(lvldir);
1802 IncProgress();
1803 FillSolidBlockTbls();
1804 SetRndSeed(glSeedTbl[currlevel]);
1805
1806 if (leveltype != DTYPE_TOWN) {
1807 GetLevelMTypes();
1808 InitThemes();
1809 LoadAllGFX();
1810 } else {
1811 IncProgress();
1812 IncProgress();
1813 InitMissileGFX();
1814 IncProgress();
1815 IncProgress();
1816 }
1817
1818 IncProgress();
1819
1820 if (lvldir == ENTRY_RTNLVL)
1821 GetReturnLvlPos();
1822 if (lvldir == ENTRY_WARPLVL)
1823 GetPortalLvlPos();
1824
1825 IncProgress();
1826
1827 for (i = 0; i < MAX_PLRS; i++) {
1828 if (plr[i].plractive && currlevel == plr[i].plrlevel) {
1829 InitPlayerGFX(i);
1830 if (lvldir != ENTRY_LOAD)
1831 InitPlayer(i, firstflag);
1832 }
1833 }
1834
1835 PlayDungMsgs();
1836 InitMultiView();
1837 IncProgress();
1838
1839 visited = FALSE;
1840 int players = gbIsMultiplayer ? MAX_PLRS : 1;
1841 for (i = 0; i < players; i++) {
1842 if (plr[i].plractive)
1843 visited = visited || plr[i]._pLvlVisited[currlevel];
1844 }
1845
1846 SetRndSeed(glSeedTbl[currlevel]);
1847
1848 if (leveltype != DTYPE_TOWN) {
1849 if (firstflag || lvldir == ENTRY_LOAD || !plr[myplr]._pLvlVisited[currlevel] || gbIsMultiplayer) {
1850 HoldThemeRooms();
1851 glMid1Seed[currlevel] = GetRndSeed();
1852 InitMonsters();
1853 glMid2Seed[currlevel] = GetRndSeed();
1854 IncProgress();
1855 InitObjects();
1856 InitItems();
1857 if (currlevel < 17)
1858 CreateThemeRooms();
1859 IncProgress();
1860 glMid3Seed[currlevel] = GetRndSeed();
1861 InitMissiles();
1862 InitDead();
1863 glEndSeed[currlevel] = GetRndSeed();
1864
1865 if (gbIsMultiplayer)
1866 DeltaLoadLevel();
1867
1868 IncProgress();
1869 SavePreLighting();
1870 } else {
1871 HoldThemeRooms();
1872 InitMonsters();
1873 InitMissiles();
1874 InitDead();
1875 IncProgress();
1876 LoadLevel();
1877 IncProgress();
1878 }
1879 } else {
1880 for (i = 0; i < MAXDUNX; i++) {
1881 for (j = 0; j < MAXDUNY; j++)
1882 dFlags[i][j] |= BFLAG_LIT;
1883 }
1884
1885 InitTowners();
1886 InitItems();
1887 InitMissiles();
1888 IncProgress();
1889
1890 if (!firstflag && lvldir != ENTRY_LOAD && plr[myplr]._pLvlVisited[currlevel] && !gbIsMultiplayer)
1891 LoadLevel();
1892 if (gbIsMultiplayer)
1893 DeltaLoadLevel();
1894
1895 IncProgress();
1896 }
1897 if (!gbIsMultiplayer)
1898 ResyncQuests();
1899 else
1900 ResyncMPQuests();
1901 } else {
1902 LoadSetMap();
1903 IncProgress();
1904 GetLevelMTypes();
1905 IncProgress();
1906 InitMonsters();
1907 IncProgress();
1908 InitMissileGFX();
1909 IncProgress();
1910 InitDead();
1911 IncProgress();
1912 FillSolidBlockTbls();
1913 IncProgress();
1914
1915 if (lvldir == ENTRY_WARPLVL)
1916 GetPortalLvlPos();
1917 IncProgress();
1918
1919 for (i = 0; i < MAX_PLRS; i++) {
1920 if (plr[i].plractive && currlevel == plr[i].plrlevel) {
1921 InitPlayerGFX(i);
1922 if (lvldir != ENTRY_LOAD)
1923 InitPlayer(i, firstflag);
1924 }
1925 }
1926 IncProgress();
1927
1928 InitMultiView();
1929 IncProgress();
1930
1931 if (firstflag || lvldir == ENTRY_LOAD || !plr[myplr]._pSLvlVisited[setlvlnum]) {
1932 InitItems();
1933 SavePreLighting();
1934 } else {
1935 LoadLevel();
1936 }
1937
1938 InitMissiles();
1939 IncProgress();
1940 }
1941
1942 SyncPortals();
1943
1944 for (i = 0; i < MAX_PLRS; i++) {
1945 if (plr[i].plractive && plr[i].plrlevel == currlevel && (!plr[i]._pLvlChanging || i == myplr)) {
1946 if (plr[i]._pHitPoints > 0) {
1947 if (!gbIsMultiplayer)
1948 dPlayer[plr[i]._px][plr[i]._py] = i + 1;
1949 else
1950 SyncInitPlrPos(i);
1951 } else {
1952 dFlags[plr[i]._px][plr[i]._py] |= BFLAG_DEAD_PLAYER;
1953 }
1954 }
1955 }
1956
1957 SetDungeonMicros();
1958
1959 InitLightMax();
1960 IncProgress();
1961 IncProgress();
1962
1963 if (firstflag) {
1964 InitControlPan();
1965 }
1966 IncProgress();
1967 UpdateMonsterLights();
1968 if (leveltype != DTYPE_TOWN) {
1969 ProcessLightList();
1970 ProcessVisionList();
1971 }
1972
1973 if (currlevel >= 21) {
1974 if (currlevel == 21) {
1975 items_427ABA(CornerStone.x, CornerStone.y);
1976 }
1977 if (quests[Q_NAKRUL]._qactive == QUEST_DONE && currlevel == 24) {
1978 objects_454BA8();
1979 }
1980 }
1981
1982 if (currlevel >= 17)
1983 music_start(currlevel > 20 ? TMUSIC_L5 : TMUSIC_L6);
1984 else
1985 music_start(leveltype);
1986
1987 while (!IncProgress())
1988 ;
1989
1990 if (!gbIsSpawn && setlevel && setlvlnum == SL_SKELKING && quests[Q_SKELKING]._qactive == QUEST_ACTIVE)
1991 PlaySFX(USFX_SKING1);
1992 }
1993
game_logic()1994 static void game_logic()
1995 {
1996 if (!ProcessInput()) {
1997 return;
1998 }
1999 if (gbProcessPlayers) {
2000 ProcessPlayers();
2001 }
2002 if (leveltype != DTYPE_TOWN) {
2003 ProcessMonsters();
2004 ProcessObjects();
2005 ProcessMissiles();
2006 ProcessItems();
2007 ProcessLightList();
2008 ProcessVisionList();
2009 } else {
2010 ProcessTowners();
2011 ProcessItems();
2012 ProcessMissiles();
2013 }
2014
2015 #ifdef _DEBUG
2016 if (debug_mode_key_inverted_v && GetAsyncKeyState(DVL_VK_SHIFT) & 0x8000) {
2017 ScrollView();
2018 }
2019 #endif
2020
2021 sound_update();
2022 ClearPlrMsg();
2023 CheckTriggers();
2024 CheckQuests();
2025 force_redraw |= 1;
2026 pfile_update(false);
2027
2028 plrctrls_after_game_logic();
2029 }
2030
timeout_cursor(BOOL bTimeout)2031 static void timeout_cursor(BOOL bTimeout)
2032 {
2033 if (bTimeout) {
2034 if (sgnTimeoutCurs == CURSOR_NONE && sgbMouseDown == CLICK_NONE) {
2035 sgnTimeoutCurs = pcurs;
2036 multi_net_ping();
2037 ClearPanel();
2038 AddPanelString("-- Network timeout --", TRUE);
2039 AddPanelString("-- Waiting for players --", TRUE);
2040 NewCursor(CURSOR_HOURGLASS);
2041 force_redraw = 255;
2042 }
2043 scrollrt_draw_game_screen(TRUE);
2044 } else if (sgnTimeoutCurs != CURSOR_NONE) {
2045 NewCursor(sgnTimeoutCurs);
2046 sgnTimeoutCurs = CURSOR_NONE;
2047 ClearPanel();
2048 force_redraw = 255;
2049 }
2050 }
2051
2052 /**
2053 * @param bStartup Process additional ticks before returning
2054 */
game_loop(BOOL bStartup)2055 void game_loop(BOOL bStartup)
2056 {
2057 int i;
2058
2059 i = bStartup ? gnTickRate * 3 : 3;
2060
2061 while (i--) {
2062 if (!multi_handle_delta()) {
2063 timeout_cursor(TRUE);
2064 break;
2065 } else {
2066 timeout_cursor(FALSE);
2067 game_logic();
2068 }
2069 if (!gbRunGame || !gbIsMultiplayer || !nthread_has_500ms_passed())
2070 break;
2071 }
2072 }
2073
diablo_color_cyc_logic()2074 void diablo_color_cyc_logic()
2075 {
2076 if (!sgOptions.Graphics.bColorCycling)
2077 return;
2078
2079 if (leveltype == DTYPE_HELL) {
2080 lighting_color_cycling();
2081 } else if (currlevel >= 21) {
2082 palette_update_crypt();
2083 } else if (currlevel >= 17) {
2084 palette_update_hive();
2085 } else if (leveltype == DTYPE_CAVES) {
2086 palette_update_caves();
2087 }
2088 }
2089
2090 DEVILUTION_END_NAMESPACE
2091