diff --git a/src/g_game.c b/src/g_game.c index 9803f2206..fd136ba8b 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -1160,6 +1160,60 @@ int G_GotoNextLevel(int *pEpi, int *pMap) return false; } +int G_GotoPrevLevel(void) +{ + if (gamestate != GS_LEVEL || + deathmatch || netgame || + demorecording || demoplayback || + menuactive) + { + return false; + } + + const int cur_epsd = gameepisode; + const int cur_map = gamemap--; + int ret = false; + + for (int epsd_count = 0; epsd_count < 10; epsd_count++, gameepisode = (gameepisode + 9) % 10) + { + for (int map_count = 0; map_count < 100; map_count++, gamemap = (gamemap + 99) % 100) + { + int next_epsd, next_map; + G_GotoNextLevel(&next_epsd, &next_map); + + if (next_epsd == cur_epsd && next_map == cur_map && + !(gameepisode == cur_epsd && gamemap == cur_map)) + { + char *name = MapName(gameepisode, gamemap); + + if (W_CheckNumForName(name) != -1) + { + G_DeferedInitNew(gameskill, gameepisode, gamemap); + ret = true; + break; + } + } + } + + // only check one episode in Doom 2 + if (gamemode == commercial) + { + break; + } + } + + gameepisode = cur_epsd; + gamemap = cur_map; + + if (ret == false) + { + char *name = MapName(gameepisode, gamemap); + displaymsg("Previous level not found for %s", name); + } + + return ret; +} + static boolean G_StrictModeSkipEvent(event_t *ev) { static boolean enable_mouse = false; diff --git a/src/g_game.h b/src/g_game.h index af163f9c7..de57dadf8 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -84,6 +84,7 @@ demo_version_t G_GetNamedComplevel(const char *arg); const char *G_GetCurrentComplevelName(void); int G_GotoNextLevel(int *pEpi, int *pMap); +int G_GotoPrevLevel(void); void G_BindGameInputVariables(void); void G_BindGameVariables(void); diff --git a/src/m_input.c b/src/m_input.c index f5e449829..8a1ab1a93 100644 --- a/src/m_input.c +++ b/src/m_input.c @@ -702,6 +702,7 @@ void M_BindInputVariables(void) BIND_INPUT(input_menu_reloadlevel, "Restart current level/demo"); BIND_INPUT(input_menu_nextlevel, "Go to next level"); + BIND_INPUT(input_menu_prevlevel, "Go to previous level"); BIND_INPUT(input_hud_timestats, "Toggle display of level stats and time"); diff --git a/src/m_input.h b/src/m_input.h index c135ce1b5..265a8e492 100644 --- a/src/m_input.h +++ b/src/m_input.h @@ -68,6 +68,7 @@ enum input_menu_clear, input_menu_reloadlevel, input_menu_nextlevel, + input_menu_prevlevel, input_hud_timestats, diff --git a/src/mn_menu.c b/src/mn_menu.c index 6e78970a2..f7083dbd9 100644 --- a/src/mn_menu.c +++ b/src/mn_menu.c @@ -2608,6 +2608,18 @@ boolean M_ShortcutResponder(const event_t *ev) } } + if (M_InputActivated(input_menu_prevlevel)) + { + if (demoplayback && singledemo && !PLAYBACK_SKIP) + { + return false; + } + else if (G_GotoPrevLevel()) + { + return true; + } + } + if (M_InputActivated(input_demo_fforward)) { if (demoplayback && !PLAYBACK_SKIP && !fastdemo && !D_CheckNetConnect())