diff --git a/CMakeLists.txt b/CMakeLists.txt index 0ab5e2725..9912c47da 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -524,9 +524,6 @@ if(RUN_TESTS) test/dead_test.cpp test/diablo_test.cpp test/drlg_l1_test.cpp - test/drlg_l2_test.cpp - test/drlg_l3_test.cpp - test/drlg_l4_test.cpp test/effects_test.cpp test/file_util_test.cpp test/inv_test.cpp @@ -584,6 +581,7 @@ if(RUN_TESTS) include(CTest) include(GoogleTest) find_package(GTest REQUIRED) + add_definitions(-DRUN_TESTS) target_include_directories(devilutionx-tests PRIVATE ${GTEST_INCLUDE_DIRS}) target_link_libraries(devilutionx-tests PRIVATE libdevilutionx) target_link_libraries(devilutionx-tests PRIVATE ${GTEST_LIBRARIES}) diff --git a/Source/control.cpp b/Source/control.cpp index 1d7fffb77..1a9a8ec35 100644 --- a/Source/control.cpp +++ b/Source/control.cpp @@ -542,6 +542,25 @@ void ControlUpDown(int v) } } +void RemoveGold(int pnum, int goldIndex) +{ + auto &player = Players[pnum]; + + int gi = goldIndex - INVITEM_INV_FIRST; + player.InvList[gi]._ivalue -= dropGoldValue; + if (player.InvList[gi]._ivalue > 0) + SetPlrHandGoldCurs(&player.InvList[gi]); + else + player.RemoveInvItem(gi); + SetPlrHandItem(&player.HoldItem, IDI_GOLD); + GetGoldSeed(pnum, &player.HoldItem); + player.HoldItem._ivalue = dropGoldValue; + player.HoldItem._iStatFlag = true; + ControlSetGoldCurs(player); + player._pGold = CalculateGold(player); + dropGoldValue = 0; +} + } // namespace void DrawSpell(const Surface &out) @@ -1782,7 +1801,7 @@ void control_drop_gold(char vkey) snprintf(input, sizeof(input), "%i", dropGoldValue); if (vkey == DVL_VK_RETURN) { if (dropGoldValue > 0) - control_remove_gold(MyPlayerId, initialDropGoldIndex); + RemoveGold(MyPlayerId, initialDropGoldIndex); dropGoldFlag = false; } else if (vkey == DVL_VK_ESCAPE) { dropGoldFlag = false; @@ -1802,25 +1821,6 @@ void control_drop_gold(char vkey) } } -void control_remove_gold(int pnum, int goldIndex) -{ - auto &player = Players[pnum]; - - int gi = goldIndex - INVITEM_INV_FIRST; - player.InvList[gi]._ivalue -= dropGoldValue; - if (player.InvList[gi]._ivalue > 0) - SetPlrHandGoldCurs(&player.InvList[gi]); - else - player.RemoveInvItem(gi); - SetPlrHandItem(&player.HoldItem, IDI_GOLD); - GetGoldSeed(pnum, &player.HoldItem); - player.HoldItem._ivalue = dropGoldValue; - player.HoldItem._iStatFlag = true; - ControlSetGoldCurs(player); - player._pGold = CalculateGold(player); - dropGoldValue = 0; -} - void DrawTalkPan(const Surface &out) { if (!talkflag) diff --git a/Source/control.h b/Source/control.h index 77f141ffc..195e1e98f 100644 --- a/Source/control.h +++ b/Source/control.h @@ -146,7 +146,6 @@ void DrawSpellBook(const Surface &out); void CheckSBook(); void DrawGoldSplit(const Surface &out, int amount); void control_drop_gold(char vkey); -void control_remove_gold(int pnum, int goldIndex); void DrawTalkPan(const Surface &out); bool control_check_talk_btn(); void control_release_talk_btn(); diff --git a/Source/diablo.cpp b/Source/diablo.cpp index 9246d5142..df7eae051 100644 --- a/Source/diablo.cpp +++ b/Source/diablo.cpp @@ -152,7 +152,9 @@ bool forceSpawn; bool forceDiablo; int sgnTimeoutCurs; bool gbShowIntro = true; +#ifdef _DEBUG int arrowdebug = 0; +#endif /** To know if these things have been done when we get to the diablo_deinit() function */ bool was_archives_init = false; /** To know if surfaces have been initialized or not */ @@ -224,571 +226,277 @@ bool ProcessInput() return true; } -void RunGameLoop(interface_mode uMsg) +bool LeftMouseCmd(bool bShift) { - WNDPROC saveProc; - tagMSG msg; + bool bNear; - nthread_ignore_mutex(true); - StartGame(uMsg); - assert(ghMainWnd); - saveProc = SetWindowProc(GM_Game); - run_delta_info(); - gbRunGame = true; - gbProcessPlayers = true; - gbRunGameResult = true; - force_redraw = 255; - DrawAndBlit(); - LoadPWaterPalette(); - PaletteFadeIn(8); - force_redraw = 255; - gbGameLoopStartup = true; - nthread_ignore_mutex(false); + assert(MousePosition.y < PANEL_TOP || MousePosition.x < PANEL_LEFT || MousePosition.x >= PANEL_LEFT + PANEL_WIDTH); -#ifdef GPERF_HEAP_FIRST_GAME_ITERATION - unsigned run_game_iteration = 0; -#endif - while (gbRunGame) { - while (FetchMessage(&msg)) { - if (msg.message == DVL_WM_QUIT) { - gbRunGameResult = false; - gbRunGame = false; - break; + if (leveltype == DTYPE_TOWN) { + if (pcursitem != -1 && pcurs == CURSOR_HAND) + NetSendCmdLocParam1(true, invflag ? CMD_GOTOGETITEM : CMD_GOTOAGETITEM, { cursmx, cursmy }, pcursitem); + if (pcursmonst != -1) + NetSendCmdLocParam1(true, CMD_TALKXY, { cursmx, cursmy }, pcursmonst); + if (pcursitem == -1 && pcursmonst == -1 && pcursplr == -1) + return true; + } else { + auto &myPlayer = Players[MyPlayerId]; + bNear = myPlayer.position.tile.WalkingDistance({ cursmx, cursmy }) < 2; + if (pcursitem != -1 && pcurs == CURSOR_HAND && !bShift) { + NetSendCmdLocParam1(true, invflag ? CMD_GOTOGETITEM : CMD_GOTOAGETITEM, { cursmx, cursmy }, pcursitem); + } else if (pcursobj != -1 && (!objectIsDisabled(pcursobj)) && (!bShift || (bNear && Objects[pcursobj]._oBreak == 1))) { + NetSendCmdLocParam1(true, pcurs == CURSOR_DISARM ? CMD_DISARMXY : CMD_OPOBJXY, { cursmx, cursmy }, pcursobj); + } else if (myPlayer._pwtype == WT_RANGED) { + if (bShift) { + NetSendCmdLoc(MyPlayerId, true, CMD_RATTACKXY, { cursmx, cursmy }); + } else if (pcursmonst != -1) { + if (CanTalkToMonst(pcursmonst)) { + NetSendCmdParam1(true, CMD_ATTACKID, pcursmonst); + } else { + NetSendCmdParam1(true, CMD_RATTACKID, pcursmonst); + } + } else if (pcursplr != -1 && !gbFriendlyMode) { + NetSendCmdParam1(true, CMD_RATTACKPID, pcursplr); + } + } else { + if (bShift) { + if (pcursmonst != -1) { + if (CanTalkToMonst(pcursmonst)) { + NetSendCmdParam1(true, CMD_ATTACKID, pcursmonst); + } else { + NetSendCmdLoc(MyPlayerId, true, CMD_SATTACKXY, { cursmx, cursmy }); + } + } else { + NetSendCmdLoc(MyPlayerId, true, CMD_SATTACKXY, { cursmx, cursmy }); + } + } else if (pcursmonst != -1) { + NetSendCmdParam1(true, CMD_ATTACKID, pcursmonst); + } else if (pcursplr != -1 && !gbFriendlyMode) { + NetSendCmdParam1(true, CMD_ATTACKPID, pcursplr); } - TranslateMessage(&msg); - PushMessage(&msg); - } - if (!gbRunGame) - break; - if (!nthread_has_500ms_passed()) { - ProcessInput(); - force_redraw |= 1; - DrawAndBlit(); - continue; } - diablo_color_cyc_logic(); - multi_process_network_packets(); - game_loop(gbGameLoopStartup); - gbGameLoopStartup = false; - DrawAndBlit(); -#ifdef GPERF_HEAP_FIRST_GAME_ITERATION - if (run_game_iteration++ == 0) - HeapProfilerDump("first_game_iteration"); -#endif + if (!bShift && pcursitem == -1 && pcursobj == -1 && pcursmonst == -1 && pcursplr == -1) + return true; } - if (gbIsMultiplayer) { - pfile_write_hero(/*writeGameData=*/false, /*clearTables=*/true); + return false; +} + +bool LeftMouseDown(int wParam) +{ + if (gmenu_left_mouse(true)) + return false; + + if (control_check_talk_btn()) + return false; + + if (sgnTimeoutCurs != CURSOR_NONE) + return false; + + if (MyPlayerIsDead) { + control_check_btn_press(); + return false; } - PaletteFadeOut(8); - NewCursor(CURSOR_NONE); - ClearScreenBuffer(); - force_redraw = 255; - scrollrt_draw_game_screen(); - saveProc = SetWindowProc(saveProc); - assert(saveProc == GM_Game); - FreeGame(); + if (PauseMode == 2) { + return false; + } + if (DoomFlag) { + doom_close(); + return false; + } - if (cineflag) { - cineflag = false; - DoEnding(); + if (spselflag) { + SetSpell(); + return false; + } + + if (stextflag != STORE_NONE) { + CheckStoreBtn(); + return false; + } + + bool isShiftHeld = (wParam & DVL_MK_SHIFT) != 0; + + if (MousePosition.y < PANEL_TOP || MousePosition.x < PANEL_LEFT || MousePosition.x >= PANEL_LEFT + PANEL_WIDTH) { + if (!gmenu_is_active() && !TryIconCurs()) { + if (QuestLogIsOpen && MousePosition.x > 32 && MousePosition.x < 288 && MousePosition.y > 32 && MousePosition.y < 308) { + QuestlogESC(); + } else if (qtextflag) { + qtextflag = false; + stream_stop(); + } else if (chrflag && MousePosition.x < SPANEL_WIDTH && MousePosition.y < SPANEL_HEIGHT) { + CheckChrBtns(); + } else if (invflag && MousePosition.x > RIGHT_PANEL && MousePosition.y < SPANEL_HEIGHT) { + if (!dropGoldFlag) + CheckInvItem(isShiftHeld); + } else if (sbookflag && MousePosition.x > RIGHT_PANEL && MousePosition.y < SPANEL_HEIGHT) { + CheckSBook(); + } else if (pcurs >= CURSOR_FIRSTITEM) { + if (TryInvPut()) { + NetSendCmdPItem(true, CMD_PUTITEM, { cursmx, cursmy }); + NewCursor(CURSOR_HAND); + } + } else { + if (Players[MyPlayerId]._pStatPts != 0 && !spselflag) + CheckLvlBtn(); + if (!lvlbtndown) + return LeftMouseCmd(isShiftHeld); + } + } + } else { + if (!talkflag && !dropGoldFlag && !gmenu_is_active()) + CheckInvScrn(isShiftHeld); + DoPanBtn(); + if (pcurs > CURSOR_HAND && pcurs < CURSOR_FIRSTITEM) + NewCursor(CURSOR_HAND); } + + return false; } -[[noreturn]] void PrintHelpAndExit() +void LeftMouseUp(int wParam) { - printInConsole("%s", _(/* TRANSLATORS: Commandline Option */ "Options:\n")); - printInConsole(" %-20s %-30s\n", /* TRANSLATORS: Commandline Option */ "-h, --help", _("Print this message and exit")); - printInConsole(" %-20s %-30s\n", /* TRANSLATORS: Commandline Option */ "--version", _("Print the version and exit")); - printInConsole(" %-20s %-30s\n", /* TRANSLATORS: Commandline Option */ "--data-dir", _("Specify the folder of diabdat.mpq")); - printInConsole(" %-20s %-30s\n", /* TRANSLATORS: Commandline Option */ "--save-dir", _("Specify the folder of save files")); - printInConsole(" %-20s %-30s\n", /* TRANSLATORS: Commandline Option */ "--config-dir", _("Specify the location of diablo.ini")); - printInConsole(" %-20s %-30s\n", /* TRANSLATORS: Commandline Option */ "--ttf-dir", _("Specify the location of the .ttf font")); - printInConsole(" %-20s %-30s\n", /* TRANSLATORS: Commandline Option */ "--ttf-name", _("Specify the name of a custom .ttf font")); - printInConsole(" %-20s %-30s\n", /* TRANSLATORS: Commandline Option */ "-n", _("Skip startup videos")); - printInConsole(" %-20s %-30s\n", /* TRANSLATORS: Commandline Option */ "-f", _("Display frames per second")); - printInConsole(" %-20s %-30s\n", /* TRANSLATORS: Commandline Option */ "-x", _("Run in windowed mode")); - printInConsole(" %-20s %-30s\n", /* TRANSLATORS: Commandline Option */ "--verbose", _("Enable verbose logging")); - printInConsole(" %-20s %-30s\n", /* TRANSLATORS: Commandline Option */ "--spawn", _("Force spawn mode even if diabdat.mpq is found")); - printInConsole("%s", _(/* TRANSLATORS: Commandline Option */ "\nHellfire options:\n")); - printInConsole(" %-20s %-30s\n", /* TRANSLATORS: Commandline Option */ "--diablo", _("Force diablo mode even if hellfire.mpq is found")); - printInConsole(" %-20s %-30s\n", /* TRANSLATORS: Commandline Option */ "--nestart", _("Use alternate nest palette")); -#ifdef _DEBUG - printInConsole("\nDebug options:\n"); - printInConsole(" %-20s %-30s\n", "-w", "Enable cheats"); - printInConsole(" %-20s %-30s\n", "-$", "Enable god mode"); - printInConsole(" %-20s %-30s\n", "-^", "Enable god mode and debug tools"); - printInConsole(" %-20s %-30s\n", "-v", "Highlight visibility"); - printInConsole(" %-20s %-30s\n", "-i", "Ignore network timeout"); - printInConsole(" %-20s %-30s\n", "-j <##>", "Mausoleum warps to given level"); - printInConsole(" %-20s %-30s\n", "-l <##> <##>", "Start in level as type"); - printInConsole(" %-20s %-30s\n", "-m <##>", "Add debug monster, up to 10 allowed"); - printInConsole(" %-20s %-30s\n", "-q <#>", "Force a certain quest"); - printInConsole(" %-20s %-30s\n", "-r <##########>", "Set map seed"); - printInConsole(" %-20s %-30s\n", "-t <##>", "Set current quest level"); -#endif - printInConsole("%s", _("\nReport bugs at https://github.com/diasurgical/devilutionX/\n")); - diablo_quit(0); + gmenu_left_mouse(false); + control_release_talk_btn(); + bool isShiftHeld = (wParam & (DVL_MK_SHIFT | DVL_MK_LBUTTON)) != 0; + if (panbtndown) + CheckBtnUp(); + if (chrbtnactive) + ReleaseChrBtns(isShiftHeld); + if (lvlbtndown) + ReleaseLvlBtn(); + if (stextflag != STORE_NONE) + ReleaseStoreBtn(); } -void DiabloParseFlags(int argc, char **argv) +void RightMouseDown() { - for (int i = 1; i < argc; i++) { - if (strcasecmp("-h", argv[i]) == 0 || strcasecmp("--help", argv[i]) == 0) { - PrintHelpAndExit(); - } else if (strcasecmp("--version", argv[i]) == 0) { - printInConsole("%s v%s\n", PROJECT_NAME, PROJECT_VERSION); - diablo_quit(0); - } else if (strcasecmp("--data-dir", argv[i]) == 0) { - paths::SetBasePath(argv[++i]); - } else if (strcasecmp("--save-dir", argv[i]) == 0) { - paths::SetPrefPath(argv[++i]); - } else if (strcasecmp("--config-dir", argv[i]) == 0) { - paths::SetConfigPath(argv[++i]); - } else if (strcasecmp("--lang-dir", argv[i]) == 0) { - paths::SetLangPath(argv[++i]); - } else if (strcasecmp("--ttf-dir", argv[i]) == 0) { - paths::SetTtfPath(argv[++i]); - } else if (strcasecmp("--ttf-name", argv[i]) == 0) { - paths::SetTtfName(argv[++i]); - } else if (strcasecmp("-n", argv[i]) == 0) { - gbShowIntro = false; - } else if (strcasecmp("-f", argv[i]) == 0) { - EnableFrameCount(); - } else if (strcasecmp("-x", argv[i]) == 0) { - gbForceWindowed = true; - } else if (strcasecmp("--spawn", argv[i]) == 0) { - forceSpawn = true; - } else if (strcasecmp("--diablo", argv[i]) == 0) { - forceDiablo = true; - } else if (strcasecmp("--nestart", argv[i]) == 0) { - gbNestArt = true; - } else if (strcasecmp("--vanilla", argv[i]) == 0) { - gbVanilla = true; - } else if (strcasecmp("--verbose", argv[i]) == 0) { - SDL_LogSetAllPriority(SDL_LOG_PRIORITY_VERBOSE); -#ifdef _DEBUG - } else if (strcasecmp("-^", argv[i]) == 0) { - debug_mode_key_inverted_v = true; - } else if (strcasecmp("-$", argv[i]) == 0) { - debug_mode_dollar_sign = true; - } else if (strcasecmp("-i", argv[i]) == 0) { - debug_mode_key_i = true; - } else if (strcasecmp("-j", argv[i]) == 0) { - debug_mode_key_j = SDL_atoi(argv[++i]); - } else if (strcasecmp("-l", argv[i]) == 0) { - setlevel = false; - leveldebug = true; - leveltype = (dungeon_type)SDL_atoi(argv[++i]); - currlevel = SDL_atoi(argv[++i]); - Players[0].plrlevel = currlevel; - } else if (strcasecmp("-m", argv[i]) == 0) { - monstdebug = true; - DebugMonsters[debugmonsttypes++] = (_monster_id)SDL_atoi(argv[++i]); - } else if (strcasecmp("-q", argv[i]) == 0) { - questdebug = SDL_atoi(argv[++i]); - } else if (strcasecmp("-r", argv[i]) == 0) { - setseed = SDL_atoi(argv[++i]); - } else if (strcasecmp("-t", argv[i]) == 0) { - leveldebug = true; - setlevel = true; - setlvlnum = (_setlevels)SDL_atoi(argv[++i]); - } else if (strcasecmp("-v", argv[i]) == 0) { - visiondebug = true; - } else if (strcasecmp("-w", argv[i]) == 0) { - debug_mode_key_w = true; -#endif - } else { - printInConsole("%s", fmt::format(_("unrecognized option '{:s}'\n"), argv[i]).c_str()); - PrintHelpAndExit(); + if (gmenu_is_active() || sgnTimeoutCurs != CURSOR_NONE || PauseMode == 2 || Players[MyPlayerId]._pInvincible) { + return; + } + + if (DoomFlag) { + doom_close(); + return; + } + if (stextflag != STORE_NONE) + return; + if (spselflag) { + SetSpell(); + return; + } + if (MousePosition.y >= SPANEL_HEIGHT + || ((!sbookflag || MousePosition.x <= RIGHT_PANEL) + && !TryIconCurs() + && (pcursinvitem == -1 || !UseInvItem(MyPlayerId, pcursinvitem)))) { + if (pcurs == CURSOR_HAND) { + if (pcursinvitem == -1 || !UseInvItem(MyPlayerId, pcursinvitem)) + CheckPlrSpell(); + } else if (pcurs > CURSOR_HAND && pcurs < CURSOR_FIRSTITEM) { + NewCursor(CURSOR_HAND); } } } -void DiabloInitScreen() +void DiabloHotkeyMsg(DWORD dwMsg) { - MousePosition = { gnScreenWidth / 2, gnScreenHeight / 2 }; - if (!sgbControllerActive) - SetCursorPos(MousePosition.x, MousePosition.y); - ScrollInfo.tile = { 0, 0 }; - ScrollInfo.offset = { 0, 0 }; - ScrollInfo._sdir = SDIR_NONE; + if (!gbIsMultiplayer) { + return; + } - ClrDiabloMsg(); + assert(dwMsg < QUICK_MESSAGE_OPTIONS); + + NetSendCmdString(0xFFFFFF, sgOptions.Chat.szHotKeyMsgs[dwMsg]); } -void SetApplicationVersions() +bool PressSysKey(int wParam) { - snprintf(gszProductName, sizeof(gszProductName) / sizeof(char), "%s v%s", PROJECT_NAME, PROJECT_VERSION); - strncpy(gszVersionNumber, fmt::format(_("version {:s}"), PROJECT_VERSION).c_str(), sizeof(gszVersionNumber) / sizeof(char)); + if (gmenu_is_active() || wParam != DVL_VK_F10) + return false; + DiabloHotkeyMsg(1); + return true; } -void DiabloInit() +void ReleaseKey(int vkey) { - if (sgOptions.Graphics.bShowFPS) - EnableFrameCount(); - - init_create_window(); - was_window_init = true; - - SFileEnableDirectAccess(true); - init_archives(); - was_archives_init = true; - - if (forceSpawn) - gbIsSpawn = true; - if (forceDiablo) - gbIsHellfire = false; - - gbIsHellfireSaveGame = gbIsHellfire; - - LanguageInitialize(); - - SetApplicationVersions(); + if (vkey == DVL_VK_SNAPSHOT) + CaptureScreen(); + if (vkey == DVL_VK_MENU || vkey == DVL_VK_LMENU || vkey == DVL_VK_RMENU) + AltPressed(false); + if (vkey == DVL_VK_CONTROL || vkey == DVL_VK_LCONTROL || vkey == DVL_VK_RCONTROL) + ToggleItemLabelHighlight(); +} - for (size_t i = 0; i < QUICK_MESSAGE_OPTIONS; i++) { - if (strlen(sgOptions.Chat.szHotKeyMsgs[i]) != 0) { - continue; +void ClosePanels() +{ + if (CanPanelsCoverView()) { + if (!chrflag && !QuestLogIsOpen && (invflag || sbookflag) && MousePosition.x < 480 && MousePosition.y < PANEL_TOP) { + SetCursorPos(MousePosition.x + 160, MousePosition.y); + } else if (!invflag && !sbookflag && (chrflag || QuestLogIsOpen) && MousePosition.x > 160 && MousePosition.y < PANEL_TOP) { + SetCursorPos(MousePosition.x - 160, MousePosition.y); } - strncpy(sgOptions.Chat.szHotKeyMsgs[i], _(QuickMessages[i].message), MAX_SEND_STR_LEN); } - - UiInitialize(); - UiSetSpawned(gbIsSpawn); - was_ui_init = true; - - ReadOnlyTest(); - - InitHash(); - - DiabloInitScreen(); - -#ifndef NOSOUND - snd_init(); - was_snd_init = true; -#endif - - ui_sound_init(); - - // Item graphics are loaded early, they already get touched during hero selection. - InitItemGFX(); + invflag = false; + chrflag = false; + sbookflag = false; + QuestLogIsOpen = false; } -void DiabloSplash() +void PressKey(int vkey) { - if (!gbShowIntro) + if (gmenu_presskeys(vkey) || control_presskeys(vkey)) { return; + } - play_movie("gendata\\logo.smk", true); + if (vkey == DVL_VK_MENU || vkey == DVL_VK_LMENU || vkey == DVL_VK_RMENU) + AltPressed(true); - if (gbIsHellfire && sgOptions.Hellfire.bIntro) { - play_movie("gendata\\Hellfire.smk", true); - sgOptions.Hellfire.bIntro = false; + if (MyPlayerIsDead) { + if (sgnTimeoutCurs != CURSOR_NONE) { + return; + } + keymapper.KeyPressed(vkey); + if (vkey == DVL_VK_RETURN) { + if (GetAsyncKeyState(DVL_VK_MENU)) + dx_reinit(); + else + control_type_message(); + } + if (vkey != DVL_VK_ESCAPE) { + return; + } } - if (!gbIsHellfire && !gbIsSpawn && sgOptions.Diablo.bIntro) { - play_movie("gendata\\diablo1.smk", true); - sgOptions.Diablo.bIntro = false; + if (vkey == DVL_VK_ESCAPE) { + if (!PressEscKey()) { + track_repeat_walk(false); + gamemenu_on(); + } + return; } - UiTitleDialog(); -} - -void DiabloDeinit() -{ - FreeItemGFX(); - - if (sbWasOptionsLoaded) - SaveOptions(); - if (was_snd_init) { - effects_cleanup_sfx(); + if (sgnTimeoutCurs != CURSOR_NONE || dropGoldFlag) { + return; + } + if (vkey == DVL_VK_PAUSE) { + diablo_pause_game(); + return; + } + if (PauseMode == 2) { + if (vkey == DVL_VK_RETURN && GetAsyncKeyState(DVL_VK_MENU)) + dx_reinit(); + return; } -#ifndef NOSOUND - Aulib::quit(); -#endif - if (was_ui_init) - UiDestroy(); - if (was_archives_init) - init_cleanup(); - if (was_window_init) - dx_cleanup(); // Cleanup SDL surfaces stuff, so we have to do it before SDL_Quit(). - if (was_fonts_init) - FontsCleanup(); - if (SDL_WasInit(SDL_INIT_EVERYTHING & ~SDL_INIT_HAPTIC) != 0) - SDL_Quit(); -} - -bool LeftMouseCmd(bool bShift) -{ - bool bNear; - assert(MousePosition.y < PANEL_TOP || MousePosition.x < PANEL_LEFT || MousePosition.x >= PANEL_LEFT + PANEL_WIDTH); + keymapper.KeyPressed(vkey); - if (leveltype == DTYPE_TOWN) { - if (pcursitem != -1 && pcurs == CURSOR_HAND) - NetSendCmdLocParam1(true, invflag ? CMD_GOTOGETITEM : CMD_GOTOAGETITEM, { cursmx, cursmy }, pcursitem); - if (pcursmonst != -1) - NetSendCmdLocParam1(true, CMD_TALKXY, { cursmx, cursmy }, pcursmonst); - if (pcursitem == -1 && pcursmonst == -1 && pcursplr == -1) - return true; - } else { - auto &myPlayer = Players[MyPlayerId]; - bNear = myPlayer.position.tile.WalkingDistance({ cursmx, cursmy }) < 2; - if (pcursitem != -1 && pcurs == CURSOR_HAND && !bShift) { - NetSendCmdLocParam1(true, invflag ? CMD_GOTOGETITEM : CMD_GOTOAGETITEM, { cursmx, cursmy }, pcursitem); - } else if (pcursobj != -1 && (!objectIsDisabled(pcursobj)) && (!bShift || (bNear && Objects[pcursobj]._oBreak == 1))) { - NetSendCmdLocParam1(true, pcurs == CURSOR_DISARM ? CMD_DISARMXY : CMD_OPOBJXY, { cursmx, cursmy }, pcursobj); - } else if (myPlayer._pwtype == WT_RANGED) { - if (bShift) { - NetSendCmdLoc(MyPlayerId, true, CMD_RATTACKXY, { cursmx, cursmy }); - } else if (pcursmonst != -1) { - if (CanTalkToMonst(pcursmonst)) { - NetSendCmdParam1(true, CMD_ATTACKID, pcursmonst); - } else { - NetSendCmdParam1(true, CMD_RATTACKID, pcursmonst); - } - } else if (pcursplr != -1 && !gbFriendlyMode) { - NetSendCmdParam1(true, CMD_RATTACKPID, pcursplr); - } - } else { - if (bShift) { - if (pcursmonst != -1) { - if (CanTalkToMonst(pcursmonst)) { - NetSendCmdParam1(true, CMD_ATTACKID, pcursmonst); - } else { - NetSendCmdLoc(MyPlayerId, true, CMD_SATTACKXY, { cursmx, cursmy }); - } - } else { - NetSendCmdLoc(MyPlayerId, true, CMD_SATTACKXY, { cursmx, cursmy }); - } - } else if (pcursmonst != -1) { - NetSendCmdParam1(true, CMD_ATTACKID, pcursmonst); - } else if (pcursplr != -1 && !gbFriendlyMode) { - NetSendCmdParam1(true, CMD_ATTACKPID, pcursplr); - } - } - if (!bShift && pcursitem == -1 && pcursobj == -1 && pcursmonst == -1 && pcursplr == -1) - return true; - } - - return false; -} - -bool LeftMouseDown(int wParam) -{ - if (gmenu_left_mouse(true)) - return false; - - if (control_check_talk_btn()) - return false; - - if (sgnTimeoutCurs != CURSOR_NONE) - return false; - - if (MyPlayerIsDead) { - control_check_btn_press(); - return false; - } - - if (PauseMode == 2) { - return false; - } - if (DoomFlag) { - doom_close(); - return false; - } - - if (spselflag) { - SetSpell(); - return false; - } - - if (stextflag != STORE_NONE) { - CheckStoreBtn(); - return false; - } - - bool isShiftHeld = (wParam & DVL_MK_SHIFT) != 0; - - if (MousePosition.y < PANEL_TOP || MousePosition.x < PANEL_LEFT || MousePosition.x >= PANEL_LEFT + PANEL_WIDTH) { - if (!gmenu_is_active() && !TryIconCurs()) { - if (QuestLogIsOpen && MousePosition.x > 32 && MousePosition.x < 288 && MousePosition.y > 32 && MousePosition.y < 308) { - QuestlogESC(); - } else if (qtextflag) { - qtextflag = false; - stream_stop(); - } else if (chrflag && MousePosition.x < SPANEL_WIDTH && MousePosition.y < SPANEL_HEIGHT) { - CheckChrBtns(); - } else if (invflag && MousePosition.x > RIGHT_PANEL && MousePosition.y < SPANEL_HEIGHT) { - if (!dropGoldFlag) - CheckInvItem(isShiftHeld); - } else if (sbookflag && MousePosition.x > RIGHT_PANEL && MousePosition.y < SPANEL_HEIGHT) { - CheckSBook(); - } else if (pcurs >= CURSOR_FIRSTITEM) { - if (TryInvPut()) { - NetSendCmdPItem(true, CMD_PUTITEM, { cursmx, cursmy }); - NewCursor(CURSOR_HAND); - } - } else { - if (Players[MyPlayerId]._pStatPts != 0 && !spselflag) - CheckLvlBtn(); - if (!lvlbtndown) - return LeftMouseCmd(isShiftHeld); - } - } - } else { - if (!talkflag && !dropGoldFlag && !gmenu_is_active()) - CheckInvScrn(isShiftHeld); - DoPanBtn(); - if (pcurs > CURSOR_HAND && pcurs < CURSOR_FIRSTITEM) - NewCursor(CURSOR_HAND); - } - - return false; -} - -void LeftMouseUp(int wParam) -{ - gmenu_left_mouse(false); - control_release_talk_btn(); - bool isShiftHeld = (wParam & (DVL_MK_SHIFT | DVL_MK_LBUTTON)) != 0; - if (panbtndown) - CheckBtnUp(); - if (chrbtnactive) - ReleaseChrBtns(isShiftHeld); - if (lvlbtndown) - ReleaseLvlBtn(); - if (stextflag != STORE_NONE) - ReleaseStoreBtn(); -} - -void RightMouseDown() -{ - if (gmenu_is_active() || sgnTimeoutCurs != CURSOR_NONE || PauseMode == 2 || Players[MyPlayerId]._pInvincible) { - return; - } - - if (DoomFlag) { - doom_close(); - return; - } - if (stextflag != STORE_NONE) - return; - if (spselflag) { - SetSpell(); - return; - } - if (MousePosition.y >= SPANEL_HEIGHT - || ((!sbookflag || MousePosition.x <= RIGHT_PANEL) - && !TryIconCurs() - && (pcursinvitem == -1 || !UseInvItem(MyPlayerId, pcursinvitem)))) { - if (pcurs == CURSOR_HAND) { - if (pcursinvitem == -1 || !UseInvItem(MyPlayerId, pcursinvitem)) - CheckPlrSpell(); - } else if (pcurs > CURSOR_HAND && pcurs < CURSOR_FIRSTITEM) { - NewCursor(CURSOR_HAND); - } - } -} - -void DiabloHotkeyMsg(DWORD dwMsg) -{ - if (!gbIsMultiplayer) { - return; - } - - assert(dwMsg < QUICK_MESSAGE_OPTIONS); - - NetSendCmdString(0xFFFFFF, sgOptions.Chat.szHotKeyMsgs[dwMsg]); -} - -bool PressSysKey(int wParam) -{ - if (gmenu_is_active() || wParam != DVL_VK_F10) - return false; - DiabloHotkeyMsg(1); - return true; -} - -void ReleaseKey(int vkey) -{ - if (vkey == DVL_VK_SNAPSHOT) - CaptureScreen(); - if (vkey == DVL_VK_MENU || vkey == DVL_VK_LMENU || vkey == DVL_VK_RMENU) - AltPressed(false); - if (vkey == DVL_VK_CONTROL || vkey == DVL_VK_LCONTROL || vkey == DVL_VK_RCONTROL) - ToggleItemLabelHighlight(); -} - -void ClosePanels() -{ - if (CanPanelsCoverView()) { - if (!chrflag && !QuestLogIsOpen && (invflag || sbookflag) && MousePosition.x < 480 && MousePosition.y < PANEL_TOP) { - SetCursorPos(MousePosition.x + 160, MousePosition.y); - } else if (!invflag && !sbookflag && (chrflag || QuestLogIsOpen) && MousePosition.x > 160 && MousePosition.y < PANEL_TOP) { - SetCursorPos(MousePosition.x - 160, MousePosition.y); - } - } - invflag = false; - chrflag = false; - sbookflag = false; - QuestLogIsOpen = false; -} - -void PressKey(int vkey) -{ - if (gmenu_presskeys(vkey) || control_presskeys(vkey)) { - return; - } - - if (vkey == DVL_VK_MENU || vkey == DVL_VK_LMENU || vkey == DVL_VK_RMENU) - AltPressed(true); - - if (MyPlayerIsDead) { - if (sgnTimeoutCurs != CURSOR_NONE) { - return; - } - keymapper.KeyPressed(vkey); - if (vkey == DVL_VK_RETURN) { - if (GetAsyncKeyState(DVL_VK_MENU)) - dx_reinit(); - else - control_type_message(); - } - if (vkey != DVL_VK_ESCAPE) { - return; - } - } - if (vkey == DVL_VK_ESCAPE) { - if (!PressEscKey()) { - track_repeat_walk(false); - gamemenu_on(); - } - return; - } - - if (sgnTimeoutCurs != CURSOR_NONE || dropGoldFlag) { - return; - } - if (vkey == DVL_VK_PAUSE) { - diablo_pause_game(); - return; - } - if (PauseMode == 2) { - if (vkey == DVL_VK_RETURN && GetAsyncKeyState(DVL_VK_MENU)) - dx_reinit(); - return; - } - - keymapper.KeyPressed(vkey); - - if (vkey == DVL_VK_RETURN) { - if (GetAsyncKeyState(DVL_VK_MENU)) { - dx_reinit(); - } else if (stextflag != STORE_NONE) { - StoreEnter(); - } else if (QuestLogIsOpen) { - QuestlogEnter(); + if (vkey == DVL_VK_RETURN) { + if (GetAsyncKeyState(DVL_VK_MENU)) { + dx_reinit(); + } else if (stextflag != STORE_NONE) { + StoreEnter(); + } else if (QuestLogIsOpen) { + QuestlogEnter(); } else { control_type_message(); } @@ -845,135 +553,518 @@ void PressKey(int vkey) } } -/** - * @internal `return` must be used instead of `break` to be bin exact as C++ - */ -void PressChar(int32_t vkey) +/** + * @internal `return` must be used instead of `break` to be bin exact as C++ + */ +void PressChar(int32_t vkey) +{ + if (gmenu_is_active() || control_talk_last_key(vkey) || sgnTimeoutCurs != CURSOR_NONE || MyPlayerIsDead) { + return; + } + if ((char)vkey == 'p' || (char)vkey == 'P') { + diablo_pause_game(); + return; + } + if (PauseMode == 2) { + return; + } + if (DoomFlag) { + doom_close(); + return; + } + if (dropGoldFlag) { + control_drop_gold(vkey); + return; + } + + switch (vkey) { + case '+': + case '=': + if (AutomapActive) { + AutomapZoomIn(); + } + return; + case '-': + case '_': + if (AutomapActive) { + AutomapZoomOut(); + } + return; +#ifdef _DEBUG + case ')': + case '0': + if (debug_mode_key_inverted_v) { + if (arrowdebug > 2) { + arrowdebug = 0; + } + auto &myPlayer = Players[MyPlayerId]; + if (arrowdebug == 0) { + myPlayer._pIFlags &= ~ISPL_FIRE_ARROWS; + myPlayer._pIFlags &= ~ISPL_LIGHT_ARROWS; + } + if (arrowdebug == 1) { + myPlayer._pIFlags |= ISPL_FIRE_ARROWS; + } + if (arrowdebug == 2) { + myPlayer._pIFlags |= ISPL_LIGHT_ARROWS; + } + arrowdebug++; + } + return; + case ':': + if (currlevel == 0 && debug_mode_key_w) { + SetAllSpellsCheat(); + } + return; + case '[': + if (currlevel == 0 && debug_mode_key_w) { + TakeGoldCheat(); + } + return; + case ']': + if (currlevel == 0 && debug_mode_key_w) { + MaxSpellsCheat(); + } + return; + case 'a': + if (debug_mode_key_inverted_v) { + spelldata[SPL_TELEPORT].sTownSpell = true; + auto &myPlayer = Players[MyPlayerId]; + myPlayer._pSplLvl[myPlayer._pSpell]++; + } + return; + case 'D': + PrintDebugPlayer(true); + return; + case 'd': + PrintDebugPlayer(false); + return; + case 'L': + case 'l': + if (debug_mode_key_inverted_v) { + ToggleLighting(); + } + return; + case 'M': + NextDebugMonster(); + return; + case 'm': + GetDebugMonster(); + return; + case 'R': + case 'r': + sprintf(tempstr, "seed = %i", glSeedTbl[currlevel]); + NetSendCmdString(1 << MyPlayerId, tempstr); + sprintf(tempstr, "Mid1 = %i : Mid2 = %i : Mid3 = %i", glMid1Seed[currlevel], glMid2Seed[currlevel], glMid3Seed[currlevel]); + NetSendCmdString(1 << MyPlayerId, tempstr); + sprintf(tempstr, "End = %i", glEndSeed[currlevel]); + NetSendCmdString(1 << MyPlayerId, tempstr); + return; + case 'T': + case 't': + if (debug_mode_key_inverted_v) { + auto &myPlayer = Players[MyPlayerId]; + sprintf(tempstr, "PX = %i PY = %i", myPlayer.position.tile.x, myPlayer.position.tile.y); + NetSendCmdString(1 << MyPlayerId, tempstr); + sprintf(tempstr, "CX = %i CY = %i DP = %i", cursmx, cursmy, dungeon[cursmx][cursmy]); + NetSendCmdString(1 << MyPlayerId, tempstr); + } + return; + case '|': + if (currlevel == 0 && debug_mode_key_w) { + GiveGoldCheat(); + } + return; +#endif + } +} + +void GetMousePos(int32_t lParam) +{ + MousePosition = { (std::int16_t)(lParam & 0xffff), (std::int16_t)((lParam >> 16) & 0xffff) }; +} + +void GameEventHandler(uint32_t uMsg, int32_t wParam, int32_t lParam) +{ + switch (uMsg) { + case DVL_WM_KEYDOWN: + PressKey(wParam); + return; + case DVL_WM_KEYUP: + ReleaseKey(wParam); + return; + case DVL_WM_CHAR: + PressChar(wParam); + return; + case DVL_WM_SYSKEYDOWN: + if (PressSysKey(wParam)) + return; + break; + case DVL_WM_SYSCOMMAND: + if (wParam == DVL_SC_CLOSE) { + gbRunGame = false; + gbRunGameResult = false; + return; + } + break; + case DVL_WM_MOUSEMOVE: + GetMousePos(lParam); + gmenu_on_mouse_move(); + return; + case DVL_WM_LBUTTONDOWN: + GetMousePos(lParam); + if (sgbMouseDown == CLICK_NONE) { + sgbMouseDown = CLICK_LEFT; + track_repeat_walk(LeftMouseDown(wParam)); + } + return; + case DVL_WM_LBUTTONUP: + GetMousePos(lParam); + if (sgbMouseDown == CLICK_LEFT) { + sgbMouseDown = CLICK_NONE; + LeftMouseUp(wParam); + track_repeat_walk(false); + } + return; + case DVL_WM_RBUTTONDOWN: + GetMousePos(lParam); + if (sgbMouseDown == CLICK_NONE) { + sgbMouseDown = CLICK_RIGHT; + RightMouseDown(); + } + return; + case DVL_WM_RBUTTONUP: + GetMousePos(lParam); + if (sgbMouseDown == CLICK_RIGHT) { + sgbMouseDown = CLICK_NONE; + } + return; + case DVL_WM_CAPTURECHANGED: + sgbMouseDown = CLICK_NONE; + track_repeat_walk(false); + break; + case WM_DIABNEXTLVL: + case WM_DIABPREVLVL: + case WM_DIABRTNLVL: + case WM_DIABSETLVL: + case WM_DIABWARPLVL: + case WM_DIABTOWNWARP: + case WM_DIABTWARPUP: + case WM_DIABRETOWN: + if (gbIsMultiplayer) + pfile_write_hero(); + nthread_ignore_mutex(true); + PaletteFadeOut(8); + sound_stop(); + music_stop(); + track_repeat_walk(false); + sgbMouseDown = CLICK_NONE; + ShowProgress((interface_mode)uMsg); + force_redraw = 255; + DrawAndBlit(); + LoadPWaterPalette(); + if (gbRunGame) + PaletteFadeIn(8); + nthread_ignore_mutex(false); + gbGameLoopStartup = true; + return; + } + + MainWndProc(uMsg); +} + +void RunGameLoop(interface_mode uMsg) +{ + WNDPROC saveProc; + tagMSG msg; + + nthread_ignore_mutex(true); + StartGame(uMsg); + assert(ghMainWnd); + saveProc = SetWindowProc(GameEventHandler); + run_delta_info(); + gbRunGame = true; + gbProcessPlayers = true; + gbRunGameResult = true; + force_redraw = 255; + DrawAndBlit(); + LoadPWaterPalette(); + PaletteFadeIn(8); + force_redraw = 255; + gbGameLoopStartup = true; + nthread_ignore_mutex(false); + +#ifdef GPERF_HEAP_FIRST_GAME_ITERATION + unsigned run_game_iteration = 0; +#endif + while (gbRunGame) { + while (FetchMessage(&msg)) { + if (msg.message == DVL_WM_QUIT) { + gbRunGameResult = false; + gbRunGame = false; + break; + } + TranslateMessage(&msg); + PushMessage(&msg); + } + if (!gbRunGame) + break; + if (!nthread_has_500ms_passed()) { + ProcessInput(); + force_redraw |= 1; + DrawAndBlit(); + continue; + } + diablo_color_cyc_logic(); + multi_process_network_packets(); + game_loop(gbGameLoopStartup); + gbGameLoopStartup = false; + DrawAndBlit(); +#ifdef GPERF_HEAP_FIRST_GAME_ITERATION + if (run_game_iteration++ == 0) + HeapProfilerDump("first_game_iteration"); +#endif + } + + if (gbIsMultiplayer) { + pfile_write_hero(/*writeGameData=*/false, /*clearTables=*/true); + } + + PaletteFadeOut(8); + NewCursor(CURSOR_NONE); + ClearScreenBuffer(); + force_redraw = 255; + scrollrt_draw_game_screen(); + saveProc = SetWindowProc(saveProc); + assert(saveProc == GameEventHandler); + FreeGame(); + + if (cineflag) { + cineflag = false; + DoEnding(); + } +} + +[[noreturn]] void PrintHelpAndExit() +{ + printInConsole("%s", _(/* TRANSLATORS: Commandline Option */ "Options:\n")); + printInConsole(" %-20s %-30s\n", /* TRANSLATORS: Commandline Option */ "-h, --help", _("Print this message and exit")); + printInConsole(" %-20s %-30s\n", /* TRANSLATORS: Commandline Option */ "--version", _("Print the version and exit")); + printInConsole(" %-20s %-30s\n", /* TRANSLATORS: Commandline Option */ "--data-dir", _("Specify the folder of diabdat.mpq")); + printInConsole(" %-20s %-30s\n", /* TRANSLATORS: Commandline Option */ "--save-dir", _("Specify the folder of save files")); + printInConsole(" %-20s %-30s\n", /* TRANSLATORS: Commandline Option */ "--config-dir", _("Specify the location of diablo.ini")); + printInConsole(" %-20s %-30s\n", /* TRANSLATORS: Commandline Option */ "--ttf-dir", _("Specify the location of the .ttf font")); + printInConsole(" %-20s %-30s\n", /* TRANSLATORS: Commandline Option */ "--ttf-name", _("Specify the name of a custom .ttf font")); + printInConsole(" %-20s %-30s\n", /* TRANSLATORS: Commandline Option */ "-n", _("Skip startup videos")); + printInConsole(" %-20s %-30s\n", /* TRANSLATORS: Commandline Option */ "-f", _("Display frames per second")); + printInConsole(" %-20s %-30s\n", /* TRANSLATORS: Commandline Option */ "-x", _("Run in windowed mode")); + printInConsole(" %-20s %-30s\n", /* TRANSLATORS: Commandline Option */ "--verbose", _("Enable verbose logging")); + printInConsole(" %-20s %-30s\n", /* TRANSLATORS: Commandline Option */ "--spawn", _("Force spawn mode even if diabdat.mpq is found")); + printInConsole("%s", _(/* TRANSLATORS: Commandline Option */ "\nHellfire options:\n")); + printInConsole(" %-20s %-30s\n", /* TRANSLATORS: Commandline Option */ "--diablo", _("Force diablo mode even if hellfire.mpq is found")); + printInConsole(" %-20s %-30s\n", /* TRANSLATORS: Commandline Option */ "--nestart", _("Use alternate nest palette")); +#ifdef _DEBUG + printInConsole("\nDebug options:\n"); + printInConsole(" %-20s %-30s\n", "-w", "Enable cheats"); + printInConsole(" %-20s %-30s\n", "-$", "Enable god mode"); + printInConsole(" %-20s %-30s\n", "-^", "Enable god mode and debug tools"); + printInConsole(" %-20s %-30s\n", "-v", "Highlight visibility"); + printInConsole(" %-20s %-30s\n", "-i", "Ignore network timeout"); + printInConsole(" %-20s %-30s\n", "-j <##>", "Mausoleum warps to given level"); + printInConsole(" %-20s %-30s\n", "-l <##> <##>", "Start in level as type"); + printInConsole(" %-20s %-30s\n", "-m <##>", "Add debug monster, up to 10 allowed"); + printInConsole(" %-20s %-30s\n", "-q <#>", "Force a certain quest"); + printInConsole(" %-20s %-30s\n", "-r <##########>", "Set map seed"); + printInConsole(" %-20s %-30s\n", "-t <##>", "Set current quest level"); +#endif + printInConsole("%s", _("\nReport bugs at https://github.com/diasurgical/devilutionX/\n")); + diablo_quit(0); +} + +void DiabloParseFlags(int argc, char **argv) +{ + for (int i = 1; i < argc; i++) { + if (strcasecmp("-h", argv[i]) == 0 || strcasecmp("--help", argv[i]) == 0) { + PrintHelpAndExit(); + } else if (strcasecmp("--version", argv[i]) == 0) { + printInConsole("%s v%s\n", PROJECT_NAME, PROJECT_VERSION); + diablo_quit(0); + } else if (strcasecmp("--data-dir", argv[i]) == 0) { + paths::SetBasePath(argv[++i]); + } else if (strcasecmp("--save-dir", argv[i]) == 0) { + paths::SetPrefPath(argv[++i]); + } else if (strcasecmp("--config-dir", argv[i]) == 0) { + paths::SetConfigPath(argv[++i]); + } else if (strcasecmp("--lang-dir", argv[i]) == 0) { + paths::SetLangPath(argv[++i]); + } else if (strcasecmp("--ttf-dir", argv[i]) == 0) { + paths::SetTtfPath(argv[++i]); + } else if (strcasecmp("--ttf-name", argv[i]) == 0) { + paths::SetTtfName(argv[++i]); + } else if (strcasecmp("-n", argv[i]) == 0) { + gbShowIntro = false; + } else if (strcasecmp("-f", argv[i]) == 0) { + EnableFrameCount(); + } else if (strcasecmp("-x", argv[i]) == 0) { + gbForceWindowed = true; + } else if (strcasecmp("--spawn", argv[i]) == 0) { + forceSpawn = true; + } else if (strcasecmp("--diablo", argv[i]) == 0) { + forceDiablo = true; + } else if (strcasecmp("--nestart", argv[i]) == 0) { + gbNestArt = true; + } else if (strcasecmp("--vanilla", argv[i]) == 0) { + gbVanilla = true; + } else if (strcasecmp("--verbose", argv[i]) == 0) { + SDL_LogSetAllPriority(SDL_LOG_PRIORITY_VERBOSE); +#ifdef _DEBUG + } else if (strcasecmp("-^", argv[i]) == 0) { + debug_mode_key_inverted_v = true; + } else if (strcasecmp("-$", argv[i]) == 0) { + debug_mode_dollar_sign = true; + } else if (strcasecmp("-i", argv[i]) == 0) { + debug_mode_key_i = true; + } else if (strcasecmp("-j", argv[i]) == 0) { + debug_mode_key_j = SDL_atoi(argv[++i]); + } else if (strcasecmp("-l", argv[i]) == 0) { + setlevel = false; + leveldebug = true; + leveltype = (dungeon_type)SDL_atoi(argv[++i]); + currlevel = SDL_atoi(argv[++i]); + Players[0].plrlevel = currlevel; + } else if (strcasecmp("-m", argv[i]) == 0) { + monstdebug = true; + DebugMonsters[debugmonsttypes++] = (_monster_id)SDL_atoi(argv[++i]); + } else if (strcasecmp("-q", argv[i]) == 0) { + questdebug = SDL_atoi(argv[++i]); + } else if (strcasecmp("-r", argv[i]) == 0) { + setseed = SDL_atoi(argv[++i]); + } else if (strcasecmp("-t", argv[i]) == 0) { + leveldebug = true; + setlevel = true; + setlvlnum = (_setlevels)SDL_atoi(argv[++i]); + } else if (strcasecmp("-v", argv[i]) == 0) { + visiondebug = true; + } else if (strcasecmp("-w", argv[i]) == 0) { + debug_mode_key_w = true; +#endif + } else { + printInConsole("%s", fmt::format(_("unrecognized option '{:s}'\n"), argv[i]).c_str()); + PrintHelpAndExit(); + } + } +} + +void DiabloInitScreen() +{ + MousePosition = { gnScreenWidth / 2, gnScreenHeight / 2 }; + if (!sgbControllerActive) + SetCursorPos(MousePosition.x, MousePosition.y); + ScrollInfo.tile = { 0, 0 }; + ScrollInfo.offset = { 0, 0 }; + ScrollInfo._sdir = SDIR_NONE; + + ClrDiabloMsg(); +} + +void SetApplicationVersions() +{ + snprintf(gszProductName, sizeof(gszProductName) / sizeof(char), "%s v%s", PROJECT_NAME, PROJECT_VERSION); + strncpy(gszVersionNumber, fmt::format(_("version {:s}"), PROJECT_VERSION).c_str(), sizeof(gszVersionNumber) / sizeof(char)); +} + +void DiabloInit() +{ + if (sgOptions.Graphics.bShowFPS) + EnableFrameCount(); + + init_create_window(); + was_window_init = true; + + SFileEnableDirectAccess(true); + init_archives(); + was_archives_init = true; + + if (forceSpawn) + gbIsSpawn = true; + if (forceDiablo) + gbIsHellfire = false; + + gbIsHellfireSaveGame = gbIsHellfire; + + LanguageInitialize(); + + SetApplicationVersions(); + + for (size_t i = 0; i < QUICK_MESSAGE_OPTIONS; i++) { + if (strlen(sgOptions.Chat.szHotKeyMsgs[i]) != 0) { + continue; + } + strncpy(sgOptions.Chat.szHotKeyMsgs[i], _(QuickMessages[i].message), MAX_SEND_STR_LEN); + } + + UiInitialize(); + UiSetSpawned(gbIsSpawn); + was_ui_init = true; + + ReadOnlyTest(); + + InitHash(); + + DiabloInitScreen(); + +#ifndef NOSOUND + snd_init(); + was_snd_init = true; +#endif + + ui_sound_init(); + + // Item graphics are loaded early, they already get touched during hero selection. + InitItemGFX(); +} + +void DiabloSplash() { - if (gmenu_is_active() || control_talk_last_key(vkey) || sgnTimeoutCurs != CURSOR_NONE || MyPlayerIsDead) { - return; - } - if ((char)vkey == 'p' || (char)vkey == 'P') { - diablo_pause_game(); - return; - } - if (PauseMode == 2) { - return; - } - if (DoomFlag) { - doom_close(); + if (!gbShowIntro) return; + + play_movie("gendata\\logo.smk", true); + + if (gbIsHellfire && sgOptions.Hellfire.bIntro) { + play_movie("gendata\\Hellfire.smk", true); + sgOptions.Hellfire.bIntro = false; } - if (dropGoldFlag) { - control_drop_gold(vkey); - return; + if (!gbIsHellfire && !gbIsSpawn && sgOptions.Diablo.bIntro) { + play_movie("gendata\\diablo1.smk", true); + sgOptions.Diablo.bIntro = false; } - switch (vkey) { - case '+': - case '=': - if (AutomapActive) { - AutomapZoomIn(); - } - return; - case '-': - case '_': - if (AutomapActive) { - AutomapZoomOut(); - } - return; -#ifdef _DEBUG - case ')': - case '0': - if (debug_mode_key_inverted_v) { - if (arrowdebug > 2) { - arrowdebug = 0; - } - auto &myPlayer = Players[MyPlayerId]; - if (arrowdebug == 0) { - myPlayer._pIFlags &= ~ISPL_FIRE_ARROWS; - myPlayer._pIFlags &= ~ISPL_LIGHT_ARROWS; - } - if (arrowdebug == 1) { - myPlayer._pIFlags |= ISPL_FIRE_ARROWS; - } - if (arrowdebug == 2) { - myPlayer._pIFlags |= ISPL_LIGHT_ARROWS; - } - arrowdebug++; - } - return; - case ':': - if (currlevel == 0 && debug_mode_key_w) { - SetAllSpellsCheat(); - } - return; - case '[': - if (currlevel == 0 && debug_mode_key_w) { - TakeGoldCheat(); - } - return; - case ']': - if (currlevel == 0 && debug_mode_key_w) { - MaxSpellsCheat(); - } - return; - case 'a': - if (debug_mode_key_inverted_v) { - spelldata[SPL_TELEPORT].sTownSpell = true; - auto &myPlayer = Players[MyPlayerId]; - myPlayer._pSplLvl[myPlayer._pSpell]++; - } - return; - case 'D': - PrintDebugPlayer(true); - return; - case 'd': - PrintDebugPlayer(false); - return; - case 'L': - case 'l': - if (debug_mode_key_inverted_v) { - ToggleLighting(); - } - return; - case 'M': - NextDebugMonster(); - return; - case 'm': - GetDebugMonster(); - return; - case 'R': - case 'r': - sprintf(tempstr, "seed = %i", glSeedTbl[currlevel]); - NetSendCmdString(1 << MyPlayerId, tempstr); - sprintf(tempstr, "Mid1 = %i : Mid2 = %i : Mid3 = %i", glMid1Seed[currlevel], glMid2Seed[currlevel], glMid3Seed[currlevel]); - NetSendCmdString(1 << MyPlayerId, tempstr); - sprintf(tempstr, "End = %i", glEndSeed[currlevel]); - NetSendCmdString(1 << MyPlayerId, tempstr); - return; - case 'T': - case 't': - if (debug_mode_key_inverted_v) { - auto &myPlayer = Players[MyPlayerId]; - sprintf(tempstr, "PX = %i PY = %i", myPlayer.position.tile.x, myPlayer.position.tile.y); - NetSendCmdString(1 << MyPlayerId, tempstr); - sprintf(tempstr, "CX = %i CY = %i DP = %i", cursmx, cursmy, dungeon[cursmx][cursmy]); - NetSendCmdString(1 << MyPlayerId, tempstr); - } - return; - case '|': - if (currlevel == 0 && debug_mode_key_w) { - GiveGoldCheat(); - } - return; -#endif - } + UiTitleDialog(); } -void GetMousePos(int32_t lParam) +void DiabloDeinit() { - MousePosition = { (std::int16_t)(lParam & 0xffff), (std::int16_t)((lParam >> 16) & 0xffff) }; + FreeItemGFX(); + + if (sbWasOptionsLoaded) + SaveOptions(); + if (was_snd_init) { + effects_cleanup_sfx(); + } +#ifndef NOSOUND + Aulib::quit(); +#endif + if (was_ui_init) + UiDestroy(); + if (was_archives_init) + init_cleanup(); + if (was_window_init) + dx_cleanup(); // Cleanup SDL surfaces stuff, so we have to do it before SDL_Quit(). + if (was_fonts_init) + FontsCleanup(); + if (SDL_WasInit(SDL_INIT_EVERYTHING & ~SDL_INIT_HAPTIC) != 0) + SDL_Quit(); } void LoadLvlGFX() @@ -1745,95 +1836,6 @@ void DisableInputWndProc(uint32_t uMsg, int32_t /*wParam*/, int32_t lParam) MainWndProc(uMsg); } -void GM_Game(uint32_t uMsg, int32_t wParam, int32_t lParam) -{ - switch (uMsg) { - case DVL_WM_KEYDOWN: - PressKey(wParam); - return; - case DVL_WM_KEYUP: - ReleaseKey(wParam); - return; - case DVL_WM_CHAR: - PressChar(wParam); - return; - case DVL_WM_SYSKEYDOWN: - if (PressSysKey(wParam)) - return; - break; - case DVL_WM_SYSCOMMAND: - if (wParam == DVL_SC_CLOSE) { - gbRunGame = false; - gbRunGameResult = false; - return; - } - break; - case DVL_WM_MOUSEMOVE: - GetMousePos(lParam); - gmenu_on_mouse_move(); - return; - case DVL_WM_LBUTTONDOWN: - GetMousePos(lParam); - if (sgbMouseDown == CLICK_NONE) { - sgbMouseDown = CLICK_LEFT; - track_repeat_walk(LeftMouseDown(wParam)); - } - return; - case DVL_WM_LBUTTONUP: - GetMousePos(lParam); - if (sgbMouseDown == CLICK_LEFT) { - sgbMouseDown = CLICK_NONE; - LeftMouseUp(wParam); - track_repeat_walk(false); - } - return; - case DVL_WM_RBUTTONDOWN: - GetMousePos(lParam); - if (sgbMouseDown == CLICK_NONE) { - sgbMouseDown = CLICK_RIGHT; - RightMouseDown(); - } - return; - case DVL_WM_RBUTTONUP: - GetMousePos(lParam); - if (sgbMouseDown == CLICK_RIGHT) { - sgbMouseDown = CLICK_NONE; - } - return; - case DVL_WM_CAPTURECHANGED: - sgbMouseDown = CLICK_NONE; - track_repeat_walk(false); - break; - case WM_DIABNEXTLVL: - case WM_DIABPREVLVL: - case WM_DIABRTNLVL: - case WM_DIABSETLVL: - case WM_DIABWARPLVL: - case WM_DIABTOWNWARP: - case WM_DIABTWARPUP: - case WM_DIABRETOWN: - if (gbIsMultiplayer) - pfile_write_hero(); - nthread_ignore_mutex(true); - PaletteFadeOut(8); - sound_stop(); - music_stop(); - track_repeat_walk(false); - sgbMouseDown = CLICK_NONE; - ShowProgress((interface_mode)uMsg); - force_redraw = 255; - DrawAndBlit(); - LoadPWaterPalette(); - if (gbRunGame) - PaletteFadeIn(8); - nthread_ignore_mutex(false); - gbGameLoopStartup = true; - return; - } - - MainWndProc(uMsg); -} - void LoadGameLevel(bool firstflag, lvl_entry lvldir) { if (setseed != 0) diff --git a/Source/diablo.h b/Source/diablo.h index 2f41a2263..8d12a13c5 100644 --- a/Source/diablo.h +++ b/Source/diablo.h @@ -77,7 +77,6 @@ bool TryIconCurs(); void diablo_pause_game(); bool PressEscKey(); void DisableInputWndProc(uint32_t uMsg, int32_t wParam, int32_t lParam); -void GM_Game(uint32_t uMsg, int32_t wParam, int32_t lParam); void LoadGameLevel(bool firstflag, lvl_entry lvldir); void game_loop(bool bStartup); void diablo_color_cyc_logic(); diff --git a/Source/drlg_l2.cpp b/Source/drlg_l2.cpp index f690ece9c..dc4e2ba74 100644 --- a/Source/drlg_l2.cpp +++ b/Source/drlg_l2.cpp @@ -1610,8 +1610,6 @@ int Patterns[100][10] = { { 0, 0, 0, 0, 255, 0, 0, 0, 0, 0 }, }; -} // namespace - static bool DrlgL2PlaceMiniSet(const BYTE *miniset, int tmin, int tmax, int cx, int cy, bool setview) { int sw = miniset[0]; @@ -3188,6 +3186,8 @@ static void LoadL2DungeonData(const uint16_t *dunData) } } +} // namespace + void LoadL2Dungeon(const char *path, int vx, int vy) { auto dunData = LoadFileInMem(path); diff --git a/Source/drlg_l2.h b/Source/drlg_l2.h index 7ee862874..707c207cd 100644 --- a/Source/drlg_l2.h +++ b/Source/drlg_l2.h @@ -26,7 +26,6 @@ struct ROOMNODE { extern BYTE predungeon[DMAXX][DMAXY]; -void InitDungeon(); void LoadL2Dungeon(const char *path, int vx, int vy); void LoadPreL2Dungeon(const char *path); void CreateL2Dungeon(uint32_t rseed, lvl_entry entry); diff --git a/Source/drlg_l3.cpp b/Source/drlg_l3.cpp index 261bd1de8..d4b559c4f 100644 --- a/Source/drlg_l3.cpp +++ b/Source/drlg_l3.cpp @@ -842,8 +842,6 @@ const BYTE HivePattern42[] = { // clang-format on }; -} // namespace - static void InitL3Dungeon() { memset(dungeon, 0, sizeof(dungeon)); @@ -2475,6 +2473,8 @@ static void DrlgL3Pass3() DRLG_LPass3(8 - 1); } +} // namespace + void CreateL3Dungeon(uint32_t rseed, lvl_entry entry) { SetRndSeed(rseed); diff --git a/Source/drlg_l3.h b/Source/drlg_l3.h index 85299f8a4..6be5e7572 100644 --- a/Source/drlg_l3.h +++ b/Source/drlg_l3.h @@ -9,7 +9,6 @@ namespace devilution { -void AddFenceDoors(); void CreateL3Dungeon(uint32_t rseed, lvl_entry entry); void LoadL3Dungeon(const char *sFileName, int vx, int vy); void LoadPreL3Dungeon(const char *sFileName); diff --git a/Source/drlg_l4.cpp b/Source/drlg_l4.cpp index dcdc0b1e0..27532c50f 100644 --- a/Source/drlg_l4.cpp +++ b/Source/drlg_l4.cpp @@ -151,8 +151,6 @@ const BYTE L4BTYPES[140] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; -} // namespace - static void DrlgL4Shadows() { for (int y = 1; y < DMAXY; y++) { @@ -1514,6 +1512,8 @@ static void DrlgL4Pass3() DRLG_LPass3(30 - 1); } +} // namespace + void CreateL4Dungeon(uint32_t rseed, lvl_entry entry) { SetRndSeed(rseed); diff --git a/Source/drlg_l4.h b/Source/drlg_l4.h index 4182fdbf7..a5992e976 100644 --- a/Source/drlg_l4.h +++ b/Source/drlg_l4.h @@ -17,8 +17,8 @@ extern int diabquad3x; extern int diabquad3y; extern int diabquad4x; extern int diabquad4y; -bool IsDURWall(char d); -bool IsDLLWall(char dd); void CreateL4Dungeon(uint32_t rseed, lvl_entry entry); +void LoadL4Dungeon(const char *path, int vx, int vy); +void LoadPreL4Dungeon(const char *path); } // namespace devilution diff --git a/Source/dthread.cpp b/Source/dthread.cpp index d348be728..111e45d01 100644 --- a/Source/dthread.cpp +++ b/Source/dthread.cpp @@ -10,6 +10,8 @@ namespace devilution { +namespace { + static CCritSect sgMemCrit; SDL_threadID glpDThreadId; TMegaPkt *sgpInfoHead; /* may not be right struct */ @@ -55,6 +57,8 @@ static void DthreadHandler() } } +} + void dthread_remove_player(uint8_t pnum) { TMegaPkt *pkt; diff --git a/Source/dx.cpp b/Source/dx.cpp index eac781862..c8846a031 100644 --- a/Source/dx.cpp +++ b/Source/dx.cpp @@ -19,11 +19,6 @@ namespace devilution { -int sgdwLockCount; -#ifdef _DEBUG -int locktbl[256]; -#endif -static CCritSect sgMemCrit; int refreshDelay; SDL_Renderer *renderer; @@ -44,6 +39,12 @@ bool RenderDirectlyToOutputSurface; namespace { +int sgdwLockCount; +#ifdef _DEBUG +int locktbl[256]; +#endif +static CCritSect sgMemCrit; + bool CanRenderDirectlyToOutputSurface() { #ifdef USE_SDL1 @@ -60,8 +61,6 @@ bool CanRenderDirectlyToOutputSurface() #endif } -} // namespace - static void CreateBackBuffer() { if (CanRenderDirectlyToOutputSurface()) { @@ -111,17 +110,6 @@ static void CreatePrimarySurface() } } -void dx_init() -{ -#ifndef USE_SDL1 - SDL_RaiseWindow(ghMainWnd); - SDL_ShowWindow(ghMainWnd); -#endif - - CreatePrimarySurface(); - palette_init(); - CreateBackBuffer(); -} static void LockBufPriv() { sgMemCrit.Enter(); @@ -133,14 +121,6 @@ static void LockBufPriv() sgdwLockCount++; } -void lock_buf(int idx) // NOLINT(misc-unused-parameters) -{ -#ifdef _DEBUG - ++locktbl[idx]; -#endif - LockBufPriv(); -} - static void UnlockBufPriv() { if (sgdwLockCount == 0) @@ -150,6 +130,45 @@ static void UnlockBufPriv() sgMemCrit.Leave(); } +/** + * @brief Limit FPS to avoid high CPU load, use when v-sync isn't available + */ +void LimitFrameRate() +{ + if (!sgOptions.Graphics.bFPSLimit) + return; + static uint32_t frameDeadline; + uint32_t tc = SDL_GetTicks() * 1000; + uint32_t v = 0; + if (frameDeadline > tc) { + v = tc % refreshDelay; + SDL_Delay(v / 1000 + 1); // ceil + } + frameDeadline = tc + v + refreshDelay; +} + +} // namespace + +void dx_init() +{ +#ifndef USE_SDL1 + SDL_RaiseWindow(ghMainWnd); + SDL_ShowWindow(ghMainWnd); +#endif + + CreatePrimarySurface(); + palette_init(); + CreateBackBuffer(); +} + +void lock_buf(int idx) // NOLINT(misc-unused-parameters) +{ +#ifdef _DEBUG + ++locktbl[idx]; +#endif + LockBufPriv(); +} + void unlock_buf(int idx) // NOLINT(misc-unused-parameters) { #ifdef _DEBUG @@ -286,23 +305,6 @@ void Blit(SDL_Surface *src, SDL_Rect *srcRect, SDL_Rect *dstRect) #endif } -/** - * @brief Limit FPS to avoid high CPU load, use when v-sync isn't available - */ -void LimitFrameRate() -{ - if (!sgOptions.Graphics.bFPSLimit) - return; - static uint32_t frameDeadline; - uint32_t tc = SDL_GetTicks() * 1000; - uint32_t v = 0; - if (frameDeadline > tc) { - v = tc % refreshDelay; - SDL_Delay(v / 1000 + 1); // ceil - } - frameDeadline = tc + v + refreshDelay; -} - void RenderPresent() { SDL_Surface *surface = GetOutputSurface(); diff --git a/Source/effects.cpp b/Source/effects.cpp index 884ea0446..4fd8a47f5 100644 --- a/Source/effects.cpp +++ b/Source/effects.cpp @@ -12,18 +12,20 @@ #include "utils/stdcompat/algorithm.hpp" namespace devilution { + +int sfxdelay; +_sfx_id sfxdnum = SFX_NONE; + namespace { + #ifndef DISABLE_STREAMING_SOUNDS constexpr bool AllowStreaming = true; #else constexpr bool AllowStreaming = false; #endif -} // namespace -int sfxdelay; -_sfx_id sfxdnum = SFX_NONE; /** Specifies the sound file and the playback state of the current sound effect. */ -static TSFX *sgpStreamSFX = nullptr; +TSFX *sgpStreamSFX = nullptr; /** * Monster sound type prefix @@ -1070,27 +1072,7 @@ TSFX sgSFX[] = { // clang-format on }; -bool effect_is_playing(int nSFX) -{ - TSFX *sfx = &sgSFX[nSFX]; - if (sfx->pSnd != nullptr) - return sfx->pSnd->isPlaying(); - - if ((sfx->bFlags & sfx_STREAM) != 0) - return sfx == sgpStreamSFX; - - return false; -} - -void stream_stop() -{ - if (sgpStreamSFX != nullptr) { - sgpStreamSFX->pSnd = nullptr; - sgpStreamSFX = nullptr; - } -} - -static void StreamPlay(TSFX *pSFX, int lVolume, int lPan) +void StreamPlay(TSFX *pSFX, int lVolume, int lPan) { assert(pSFX); assert(pSFX->bFlags & sfx_STREAM); @@ -1106,43 +1088,14 @@ static void StreamPlay(TSFX *pSFX, int lVolume, int lPan) } } -static void StreamUpdate() +void StreamUpdate() { if (sgpStreamSFX != nullptr && !sgpStreamSFX->pSnd->isPlaying()) { stream_stop(); } } -void InitMonsterSND(int monst) -{ - if (!gbSndInited) { - return; - } - - const int mtype = LevelMonsterTypes[monst].mtype; - for (int i = 0; i < 4; i++) { - if (MonstSndChar[i] != 's' || MonsterData[mtype].snd_special) { - for (int j = 0; j < 2; j++) { - char path[MAX_PATH]; - sprintf(path, MonsterData[mtype].sndfile, MonstSndChar[i], j + 1); - LevelMonsterTypes[monst].Snds[i][j] = sound_file_load(path); - } - } - } -} - -void FreeMonsterSnd() -{ - for (int i = 0; i < LevelMonsterTypeCount; i++) { - for (auto &variants : LevelMonsterTypes[i].Snds) { - for (auto &snd : variants) { - snd = nullptr; - } - } - } -} - -bool calc_snd_position(Point soundPosition, int *plVolume, int *plPan) +bool CalculatePosition(Point soundPosition, int *plVolume, int *plPan) { const auto &playerPosition = Players[MyPlayerId].position.tile; const auto delta = soundPosition - playerPosition; @@ -1161,7 +1114,7 @@ bool calc_snd_position(Point soundPosition, int *plVolume, int *plPan) return true; } -static void PlaySfxPriv(TSFX *pSFX, bool loc, Point position) +void PlaySfxPriv(TSFX *pSFX, bool loc, Point position) { if (Players[MyPlayerId].pLvlLoad != 0 && gbIsMultiplayer) { return; @@ -1176,7 +1129,7 @@ static void PlaySfxPriv(TSFX *pSFX, bool loc, Point position) int lVolume = 0; int lPan = 0; - if (loc && !calc_snd_position(position, &lVolume, &lPan)) { + if (loc && !CalculatePosition(position, &lVolume, &lPan)) { return; } @@ -1192,32 +1145,7 @@ static void PlaySfxPriv(TSFX *pSFX, bool loc, Point position) snd_play_snd(pSFX->pSnd.get(), lVolume, lPan); } -void PlayEffect(int i, int mode) -{ - if (Players[MyPlayerId].pLvlLoad != 0) { - return; - } - - int sndIdx = GenerateRnd(2); - if (!gbSndInited || !gbSoundOn || gbBufferMsgs != 0) { - return; - } - - int mi = Monsters[i]._mMTidx; - TSnd *snd = LevelMonsterTypes[mi].Snds[mode][sndIdx].get(); - if (snd == nullptr || snd->isPlaying()) { - return; - } - - int lVolume = 0; - int lPan = 0; - if (!calc_snd_position(Monsters[i].position.tile, &lVolume, &lPan)) - return; - - snd_play_snd(snd, lVolume, lPan); -} - -static _sfx_id RndSFX(_sfx_id psfx) +_sfx_id RndSFX(_sfx_id psfx) { int nRand; @@ -1249,6 +1177,109 @@ static _sfx_id RndSFX(_sfx_id psfx) return static_cast<_sfx_id>(psfx + GenerateRnd(nRand)); } +void PrivSoundInit(BYTE bLoadMask) +{ + if (!gbSndInited) { + return; + } + + for (auto &sfx : sgSFX) { + if (sfx.pSnd != nullptr) { + continue; + } + + if ((sfx.bFlags & sfx_STREAM) != 0) { + continue; + } + + if ((sfx.bFlags & bLoadMask) == 0) { + continue; + } + + if (!gbIsHellfire && (sfx.bFlags & sfx_HELLFIRE) != 0) { + continue; + } + + sfx.pSnd = sound_file_load(sfx.pszName); + } +} + +} // namespace + +bool effect_is_playing(int nSFX) +{ + TSFX *sfx = &sgSFX[nSFX]; + if (sfx->pSnd != nullptr) + return sfx->pSnd->isPlaying(); + + if ((sfx->bFlags & sfx_STREAM) != 0) + return sfx == sgpStreamSFX; + + return false; +} + +void stream_stop() +{ + if (sgpStreamSFX != nullptr) { + sgpStreamSFX->pSnd = nullptr; + sgpStreamSFX = nullptr; + } +} + +void InitMonsterSND(int monst) +{ + if (!gbSndInited) { + return; + } + + const int mtype = LevelMonsterTypes[monst].mtype; + for (int i = 0; i < 4; i++) { + if (MonstSndChar[i] != 's' || MonsterData[mtype].snd_special) { + for (int j = 0; j < 2; j++) { + char path[MAX_PATH]; + sprintf(path, MonsterData[mtype].sndfile, MonstSndChar[i], j + 1); + LevelMonsterTypes[monst].Snds[i][j] = sound_file_load(path); + } + } + } +} + +void FreeMonsterSnd() +{ + for (int i = 0; i < LevelMonsterTypeCount; i++) { + for (auto &variants : LevelMonsterTypes[i].Snds) { + for (auto &snd : variants) { + snd = nullptr; + } + } + } +} + +void PlayEffect(int i, int mode) +{ + if (Players[MyPlayerId].pLvlLoad != 0) { + return; + } + + int sndIdx = GenerateRnd(2); + if (!gbSndInited || !gbSoundOn || gbBufferMsgs != 0) { + return; + } + + int mi = Monsters[i]._mMTidx; + TSnd *snd = LevelMonsterTypes[mi].Snds[mode][sndIdx].get(); + if (snd == nullptr || snd->isPlaying()) { + return; + } + + int lVolume = 0; + int lPan = 0; + if (!CalculatePosition(Monsters[i].position.tile, &lVolume, &lPan)) + return; + + snd_play_snd(snd, lVolume, lPan); +} + void PlaySFX(_sfx_id psfx) { psfx = RndSFX(psfx); @@ -1298,33 +1329,6 @@ void effects_cleanup_sfx() sfx.pSnd = nullptr; } -static void PrivSoundInit(BYTE bLoadMask) -{ - if (!gbSndInited) { - return; - } - - for (auto &sfx : sgSFX) { - if (sfx.pSnd != nullptr) { - continue; - } - - if ((sfx.bFlags & sfx_STREAM) != 0) { - continue; - } - - if ((sfx.bFlags & bLoadMask) == 0) { - continue; - } - - if (!gbIsHellfire && (sfx.bFlags & sfx_HELLFIRE) != 0) { - continue; - } - - sfx.pSnd = sound_file_load(sfx.pszName); - } -} - void sound_init() { uint8_t mask = sfx_MISC; @@ -1387,4 +1391,11 @@ int GetSFXLength(int nSFX) return sgSFX[nSFX].pSnd->DSB.GetLength(); } +#ifdef RUN_TESTS +bool TestCalculatePosition(Point soundPosition, int *plVolume, int *plPan) +{ + CalculatePosition(soundPosition, plVolume, plPan); +} +#endif + } // namespace devilution diff --git a/Source/effects.h b/Source/effects.h index 8a9b83271..6da6b488f 100644 --- a/Source/effects.h +++ b/Source/effects.h @@ -1188,7 +1188,9 @@ void ui_sound_init(); void effects_play_sound(const char *sndFile); #ifndef NOSOUND -bool calc_snd_position(Point soundPosition, int *plVolume, int *plPan); +#ifdef RUN_TESTS +bool TestCalculatePosition(Point soundPosition, int *plVolume, int *plPan); +#endif int GetSFXLength(int nSFX); #endif diff --git a/test/drlg_l2_test.cpp b/test/drlg_l2_test.cpp deleted file mode 100644 index 2f1041160..000000000 --- a/test/drlg_l2_test.cpp +++ /dev/null @@ -1,12 +0,0 @@ -#include - -#include "drlg_l2.h" - -using namespace devilution; - -TEST(Drlg_l2, InitDungeon) -{ - InitDungeon(); - EXPECT_EQ(predungeon[0][0], 32); - EXPECT_EQ(dflags[0][0], 0); -} diff --git a/test/drlg_l3_test.cpp b/test/drlg_l3_test.cpp deleted file mode 100644 index a0ade7700..000000000 --- a/test/drlg_l3_test.cpp +++ /dev/null @@ -1,35 +0,0 @@ -#include - -#include "drlg_l3.h" - -using namespace devilution; - -TEST(Drlg_l3, AddFenceDoors_x) -{ - memset(dungeon, 0, sizeof(dungeon)); - dungeon[5][5] = 7; - dungeon[5 - 1][5] = 130; - dungeon[5 + 1][5] = 152; - AddFenceDoors(); - EXPECT_EQ(dungeon[5][5], 146); -} - -TEST(Drlg_l3, AddFenceDoors_y) -{ - memset(dungeon, 0, sizeof(dungeon)); - dungeon[5][5] = 7; - dungeon[5][5 - 1] = 130; - dungeon[5][5 + 1] = 152; - AddFenceDoors(); - EXPECT_EQ(dungeon[5][5], 147); -} - -TEST(Drlg_l3, AddFenceDoors_no) -{ - memset(dungeon, 0, sizeof(dungeon)); - dungeon[5][5] = 7; - dungeon[5 - 1][5] = 130; - dungeon[5 + 1][5] = 153; - AddFenceDoors(); - EXPECT_EQ(dungeon[5][5], 7); -} diff --git a/test/drlg_l4_test.cpp b/test/drlg_l4_test.cpp deleted file mode 100644 index 5c3a5371b..000000000 --- a/test/drlg_l4_test.cpp +++ /dev/null @@ -1,21 +0,0 @@ -#include - -#include "drlg_l4.h" - -using namespace devilution; - -TEST(Drlg_l4, IsDURWall) -{ - EXPECT_EQ(IsDURWall(25), true); - EXPECT_EQ(IsDURWall(28), true); - EXPECT_EQ(IsDURWall(23), true); - EXPECT_EQ(IsDURWall(20), false); -} - -TEST(Drlg_l4, IsDLLWall) -{ - EXPECT_EQ(IsDLLWall(27), true); - EXPECT_EQ(IsDLLWall(26), true); - EXPECT_EQ(IsDLLWall(22), true); - EXPECT_EQ(IsDLLWall(20), false); -} diff --git a/test/effects_test.cpp b/test/effects_test.cpp index 325b340f4..234b42f05 100644 --- a/test/effects_test.cpp +++ b/test/effects_test.cpp @@ -5,52 +5,52 @@ using namespace devilution; -TEST(Effects, calc_snd_position_center) +TEST(Effects, CalculatePosition_center) { Players[MyPlayerId].position.tile = { 50, 50 }; int plVolume = 0; int plPan = 0; - EXPECT_EQ(calc_snd_position({ 50, 50 }, &plVolume, &plPan), true); + EXPECT_EQ(TestCalculatePosition({ 50, 50 }, &plVolume, &plPan), true); EXPECT_EQ(plVolume, 0); EXPECT_EQ(plPan, 0); } -TEST(Effects, calc_snd_position_near) +TEST(Effects, CalculatePosition_near) { Players[MyPlayerId].position.tile = { 50, 50 }; int plVolume = 0; int plPan = 0; - EXPECT_EQ(calc_snd_position({ 55, 50 }, &plVolume, &plPan), true); + EXPECT_EQ(TestCalculatePosition({ 55, 50 }, &plVolume, &plPan), true); EXPECT_EQ(plVolume, -320); EXPECT_EQ(plPan, 1280); } -TEST(Effects, calc_snd_position_out_of_range) +TEST(Effects, CalculatePosition_out_of_range) { Players[MyPlayerId].position.tile = { 12, 12 }; int plVolume = 1234; int plPan = 0; - EXPECT_EQ(calc_snd_position({ 112, 112 }, &plVolume, &plPan), false); + EXPECT_EQ(TestCalculatePosition({ 112, 112 }, &plVolume, &plPan), false); EXPECT_EQ(plVolume, 1234); EXPECT_EQ(plPan, 0); } -TEST(Effects, calc_snd_position_extreme_right) +TEST(Effects, CalculatePosition_extreme_right) { Players[MyPlayerId].position.tile = { 50, 50 }; int plVolume = 0; int plPan = 0; - EXPECT_EQ(calc_snd_position({ 75, 25 }, &plVolume, &plPan), true); + EXPECT_EQ(TestCalculatePosition({ 75, 25 }, &plVolume, &plPan), true); EXPECT_EQ(plVolume, -2176); EXPECT_EQ(plPan, 6400); } -TEST(Effects, calc_snd_position_extreme_left) +TEST(Effects, CalculatePosition_extreme_left) { Players[MyPlayerId].position.tile = { 50, 50 }; int plVolume = 0; int plPan = 0; - EXPECT_EQ(calc_snd_position({ 25, 75 }, &plVolume, &plPan), true); + EXPECT_EQ(TestCalculatePosition({ 25, 75 }, &plVolume, &plPan), true); EXPECT_EQ(plVolume, -2176); EXPECT_EQ(plPan, -6400); }