From 874ff9525a775ca5bdfda526396fd4bb18b636fc Mon Sep 17 00:00:00 2001 From: kraflab Date: Wed, 2 Aug 2023 21:40:20 +0200 Subject: [PATCH] Extract episode management --- prboom2/src/CMakeLists.txt | 2 + prboom2/src/dsda/episode.c | 91 ++++++++++++ prboom2/src/dsda/episode.h | 42 ++++++ prboom2/src/dsda/mapinfo.c | 5 + prboom2/src/g_game.c | 3 +- prboom2/src/heretic/mn_menu.c | 31 ---- prboom2/src/m_cheat.c | 2 - prboom2/src/m_menu.c | 262 ++++++++++++++-------------------- prboom2/src/umapinfo.cpp | 14 +- 9 files changed, 252 insertions(+), 200 deletions(-) create mode 100644 prboom2/src/dsda/episode.c create mode 100644 prboom2/src/dsda/episode.h diff --git a/prboom2/src/CMakeLists.txt b/prboom2/src/CMakeLists.txt index 8f5df8a13..da94c27e7 100644 --- a/prboom2/src/CMakeLists.txt +++ b/prboom2/src/CMakeLists.txt @@ -39,6 +39,8 @@ set(COMMON_SRC dsda/destructible.h dsda/endoom.c dsda/endoom.h + dsda/episode.c + dsda/episode.h dsda/excmd.c dsda/excmd.h dsda/exdemo.c diff --git a/prboom2/src/dsda/episode.c b/prboom2/src/dsda/episode.c new file mode 100644 index 000000000..08f22252e --- /dev/null +++ b/prboom2/src/dsda/episode.c @@ -0,0 +1,91 @@ +// +// Copyright(C) 2023 by Ryan Krafnick +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// DESCRIPTION: +// DSDA Episode +// + +#include "doomstat.h" +#include "lprintf.h" +#include "z_zone.h" + +#include "dsda/mapinfo.h" + +#include "episode.h" + +dsda_episode_t* episodes; +size_t num_episodes; + +static void dsda_DetermineEpisodeMap(dsda_episode_t* episode) { + if (!dsda_NameToMap(episode->map_lump, &episode->start_episode, &episode->start_map)) + I_Error("Cannot evaluate start map for episode %s", episode->name ? episode->name : + episode->pic_name ? episode->pic_name : + "UNKNOWN"); +} + +void dsda_AddOriginalEpisodes(void) { + if (heretic) { + dsda_AddEpisode("e1m1", "CITY OF THE DAMNED", NULL, 'c', true); + dsda_AddEpisode("e2m1", "HELL'S MAW", NULL, 'h', true); + dsda_AddEpisode("e3m1", "THE DOME OF D'SPARIL", NULL, 't', true); + + if (gamemode == retail) { + dsda_AddEpisode("e4m1", "THE OSSUARY", NULL, 't', true); + dsda_AddEpisode("e5m1", "THE STAGNANT DEMESNE", NULL, 't', true); + } + } + else if (hexen) { + dsda_AddEpisode("map01", "FIGHTER", NULL, 'f', true); + dsda_AddEpisode("map01", "CLERIC", NULL, 'c', true); + dsda_AddEpisode("map01", "MAGE", NULL, 'm', true); + } + else if (gamemode != commercial && gamemission != chex) { + dsda_AddEpisode("e1m1", NULL, "M_EPI1", 'k', true); + dsda_AddEpisode("e2m1", NULL, "M_EPI2", 't', true); + dsda_AddEpisode("e3m1", NULL, "M_EPI3", 'i', true); + + if (gamemode == retail) + dsda_AddEpisode("e4m1", NULL, "M_EPI4", 't', true); + } +} + +void dsda_AddCustomEpisodes(void) { +} + +void dsda_ClearEpisodes(void) { + int i; + + for (i = 0; i < num_episodes; ++i) { + Z_Free(episodes[i].map_lump); + Z_Free(episodes[i].name); + Z_Free(episodes[i].pic_name); + } + + Z_Free(episodes); + episodes = NULL; + num_episodes = 0; +} + +void dsda_AddEpisode(const char* map_lump, const char* name, + const char* pic_name, char key, dboolean vanilla) { + ++num_episodes; + episodes = Z_Realloc(episodes, num_episodes * sizeof(*episodes)); + + episodes[num_episodes - 1].map_lump = map_lump ? Z_Strdup(map_lump) : NULL; + episodes[num_episodes - 1].name = name ? Z_Strdup(name) : NULL; + episodes[num_episodes - 1].pic_name = pic_name ? Z_Strdup(pic_name) : NULL; + episodes[num_episodes - 1].key = key; + episodes[num_episodes - 1].vanilla = vanilla; + + dsda_DetermineEpisodeMap(&episodes[num_episodes - 1]); +} diff --git a/prboom2/src/dsda/episode.h b/prboom2/src/dsda/episode.h new file mode 100644 index 000000000..ce9e06d21 --- /dev/null +++ b/prboom2/src/dsda/episode.h @@ -0,0 +1,42 @@ +// +// Copyright(C) 2023 by Ryan Krafnick +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// DESCRIPTION: +// DSDA Episode +// + +#ifndef __DSDA_EPISODE__ +#define __DSDA_EPISODE__ + +#include "doomtype.h" + +typedef struct { + char* map_lump; + char* name; + char* pic_name; + char key; + dboolean vanilla; + int start_map; + int start_episode; +} dsda_episode_t; + +extern dsda_episode_t* episodes; +extern size_t num_episodes; + +void dsda_AddOriginalEpisodes(void); +void dsda_AddCustomEpisodes(void); +void dsda_ClearEpisodes(void); +void dsda_AddEpisode(const char* map_lump, const char* name, + const char* pic_name, char key, dboolean vanilla); + +#endif diff --git a/prboom2/src/dsda/mapinfo.c b/prboom2/src/dsda/mapinfo.c index 521ab3bd4..7bf5f6f23 100644 --- a/prboom2/src/dsda/mapinfo.c +++ b/prboom2/src/dsda/mapinfo.c @@ -22,6 +22,7 @@ #include "m_misc.h" #include "dsda/args.h" +#include "dsda/episode.h" #include "dsda/map_format.h" #include "dsda/mapinfo/doom.h" #include "dsda/mapinfo/hexen.h" @@ -495,10 +496,14 @@ void dsda_PrepareFinale(int* behaviour) { } void dsda_LoadMapInfo(void) { + dsda_AddOriginalEpisodes(); + dsda_DoomLoadMapInfo(); dsda_HexenLoadMapInfo(); dsda_ULoadMapInfo(); dsda_LegacyLoadMapInfo(); + + dsda_AddCustomEpisodes(); } const char* dsda_ExitPic(void) { diff --git a/prboom2/src/g_game.c b/prboom2/src/g_game.c index 7ce1b42ed..ec457a56e 100644 --- a/prboom2/src/g_game.c +++ b/prboom2/src/g_game.c @@ -2947,8 +2947,7 @@ void G_InitNew(int skill, int episode, int map, dboolean prepare) if (episode < 1) episode = 1; - // Disable all sanity checks if there are custom episode definitions. They do not make sense in this case. - if (!EpiCustom && !W_LumpNameExists(dsda_MapLumpName(episode, map))) + if (!W_LumpNameExists(dsda_MapLumpName(episode, map))) { if (heretic) { diff --git a/prboom2/src/heretic/mn_menu.c b/prboom2/src/heretic/mn_menu.c index 9e83d11f0..79dc7e6b0 100644 --- a/prboom2/src/heretic/mn_menu.c +++ b/prboom2/src/heretic/mn_menu.c @@ -54,10 +54,7 @@ extern menu_t OptionsDef; extern menu_t SoundDef; extern menu_t LoadDef; extern menu_t SaveDef; -extern menuitem_t EpisodeMenu[]; extern menuitem_t SoundMenu[]; -extern short EpiMenuMap[]; -extern short EpiMenuEpi[]; void M_DrawThermo(int x, int y, int thermWidth, int thermDot); @@ -101,29 +98,10 @@ void MN_Init(void) SkillDef.x = 38; SkillDef.y = 30; - EpisodeMenu[0].alttext = "CITY OF THE DAMNED"; - EpisodeMenu[1].alttext = "HELL'S MAW"; - EpisodeMenu[2].alttext = "THE DOME OF D'SPARIL"; - EpisodeMenu[3].alttext = "THE OSSUARY"; - EpisodeMenu[4].alttext = "THE STAGNANT DEMESNE"; - if (gamemode == retail) { - EpiMenuEpi[3] = 4; - EpiMenuEpi[4] = 5; - EpiMenuMap[3] = 1; - EpiMenuMap[4] = 1; - EpiDef.numitems = 5; EpiDef.y -= ITEM_HEIGHT; } - else - { - EpiMenuEpi[3] = -1; - EpiMenuEpi[4] = -1; - EpiMenuMap[3] = -1; - EpiMenuMap[4] = -1; - EpiDef.numitems = 3; - } } else { @@ -132,15 +110,6 @@ void MN_Init(void) SkillDef.x = 120; SkillDef.y = 44; - - EpisodeMenu[0].alttext = "FIGHTER"; - EpisodeMenu[1].alttext = "CLERIC"; - EpisodeMenu[2].alttext = "MAGE"; - - EpiMenuEpi[1] = 1; - EpiMenuEpi[2] = 1; - - EpiDef.numitems = 3; } SoundMenu[0].alttext = "SFX VOLUME"; diff --git a/prboom2/src/m_cheat.c b/prboom2/src/m_cheat.c index ace407c83..cd3a7d06b 100644 --- a/prboom2/src/m_cheat.c +++ b/prboom2/src/m_cheat.c @@ -478,8 +478,6 @@ static void cheat_behold() dsda_AddMessage(s_STSTR_BEHOLD); } -extern int EpiCustom; - // 'clev' change-level cheat static void cheat_clev(char buf[3]) { diff --git a/prboom2/src/m_menu.c b/prboom2/src/m_menu.c index 0f95bc160..63b5390e7 100644 --- a/prboom2/src/m_menu.c +++ b/prboom2/src/m_menu.c @@ -78,6 +78,7 @@ #include "f_finale.h" #include "e6y.h"//e6y +#include "dsda/episode.h" #include "dsda/exhud.h" #include "dsda/features.h" #include "dsda/font.h" @@ -589,91 +590,21 @@ void M_DrawReadThis2(void) // EPISODE SELECT // -// -// episodes_e provides numbers for the episode menu items. The default is -// 4, to accomodate Ultimate Doom. If the user is running anything else, -// this is accounted for in the code. -// - -enum -{ - ep1, - ep2, - ep3, - ep4, - ep_end -} episodes_e; - // The definitions of the Episodes menu -menuitem_t EpisodeMenu[]= // added a few free entries for UMAPINFO -{ - {1,"M_EPI1", M_Episode,'k'}, - {1,"M_EPI2", M_Episode,'t'}, - {1,"M_EPI3", M_Episode,'i'}, - {1,"M_EPI4", M_Episode,'t'}, - {1,"", M_Episode,'0'}, - {1,"", M_Episode,'0'}, - {1,"", M_Episode,'0'}, - {1,"", M_Episode,'0'} -}; - menu_t EpiDef = { - ep_end, // # of menu items - &MainDef, // previous menu - EpisodeMenu, // menuitem_t -> - M_DrawEpisode, // drawing routine -> - 48,63, // x,y - ep1 // lastOn + .prevMenu = &MainDef, + .routine = M_DrawEpisode, + .x = 48, + .y = 63, }; -// This is for customized episode menus -int EpiCustom; -short EpiMenuMap[8] = { 1, 1, 1, 1, -1, -1, -1, -1 }, EpiMenuEpi[8] = { 1,2,3,4,-1,-1,-1,-1 }; - // // M_Episode // -int epiChoice; - -void M_ClearEpisodes(void) -{ - EpiDef.numitems = 0; -} -void M_AddEpisode(const char *map, const char *gfx, const char *txt, const char *alpha) -{ - if (!EpiCustom) - { - EpiCustom = true; - SkillDef.prevMenu = &EpiDef; - - if (gamemode == commercial || gamemission == chex) - EpiDef.numitems = 0; - } - - { - int epi, mapnum; - if (EpiDef.numitems >= 8) return; - G_ValidateMapName(map, &epi, &mapnum); - EpiMenuEpi[EpiDef.numitems] = epi; - EpiMenuMap[EpiDef.numitems] = mapnum; - strncpy(EpisodeMenu[EpiDef.numitems].name, gfx, 8); - EpisodeMenu[EpiDef.numitems].name[8] = 0; - EpisodeMenu[EpiDef.numitems].alttext = txt ? Z_Strdup(txt) : NULL; - EpisodeMenu[EpiDef.numitems].alphaKey = alpha ? *alpha : 0; - EpiDef.numitems++; - } - if (EpiDef.numitems <= 4) - { - EpiDef.y = 63; - } - else - { - EpiDef.y = 63 - (EpiDef.numitems - 4) * (LINEHEIGHT / 2); - } -} +static int chosen_episode; void M_DrawEpisode(void) { @@ -685,25 +616,17 @@ void M_DrawEpisode(void) void M_Episode(int choice) { - if (!EpiCustom) - { - if ((gamemode == shareware) && choice) { - M_StartMessage(s_SWSTRING, NULL, false); // Ty 03/27/98 - externalized - M_SetupNextMenu(&ReadDef1); - return; - } - - // Yet another hack... - if ((gamemode == registered) && (choice > 2) && !EpiCustom) - { - lprintf(LO_WARN, - "M_Episode: 4th episode requires UltimateDOOM\n"); - choice = 0; - } + if (gamemode == shareware && choice && !episodes[choice].vanilla) { + M_StartMessage(s_SWSTRING, NULL, false); // Ty 03/27/98 - externalized + M_SetupNextMenu(&ReadDef1); + return; } - epiChoice = choice; + + chosen_episode = choice; + if (hexen) // hack hexen class as "episode menu" - MN_UpdateClass(epiChoice); + MN_UpdateClass(chosen_episode); + M_SetupNextMenu(&SkillDef); } @@ -745,35 +668,52 @@ void M_NewGame(int choice) } // Chex Quest disabled the episode select screen, as did Doom II. - if ((((gamemode == commercial && !hexen) || gamemission == chex) && !EpiCustom) || EpiDef.numitems == 1) + if (num_episodes <= 1 && !hexen) M_SetupNextMenu(&SkillDef); else - { - epiChoice = 0; M_SetupNextMenu(&EpiDef); - } } static int chosen_skill; -// CPhipps - static -static void M_VerifySkill(int ch) +static void M_FinishGameSelection(void) { - if (ch != 'y') - return; + int episode, map; - G_DeferedInitNew(chosen_skill, EpiMenuEpi[epiChoice], EpiMenuMap[epiChoice]); + if (num_episodes) + { + episode = episodes[chosen_episode].start_episode; + map = episodes[chosen_episode].start_map; + } + else + { + episode = 1; + map = 1; + } + + G_DeferedInitNew(chosen_skill, episode, map); if (hexen) SB_SetClassData(); - M_ClearMenus (); + M_ClearMenus(); +} + +// CPhipps - static +static void M_VerifySkill(int ch) +{ + if (ch != 'y') + return; + + M_FinishGameSelection(); } void M_ChooseSkill(int choice) { extern skill_info_t *skill_infos; + chosen_skill = choice; + if (choice < num_skills && skill_infos[choice].flags & SI_MUST_CONFIRM) { const char* message; @@ -783,20 +723,12 @@ void M_ChooseSkill(int choice) else message = s_NIGHTMARE; // Ty 03/27/98 - externalized - chosen_skill = choice; - M_StartMessage(message, M_VerifySkill, true); return; } - if (EpiMenuEpi[epiChoice] == -1 || EpiMenuMap[epiChoice] == -1) - return; // There is no map to start here. - - G_DeferedInitNew(choice, EpiMenuEpi[epiChoice], EpiMenuMap[epiChoice]); - if (hexen) - SB_SetClassData(); - M_ClearMenus (); + M_FinishGameSelection(); } ///////////////////////////// @@ -5585,60 +5517,85 @@ dboolean M_Responder (event_t* ev) { static void M_InitializeSkillMenu(void) { - DO_ONCE - extern skill_info_t *skill_infos; - int i; + extern skill_info_t *skill_infos; + int i; - SkillDef.lastOn = dsda_IntConfig(dsda_config_default_skill) - 1; + SkillDef.lastOn = dsda_IntConfig(dsda_config_default_skill) - 1; - SkillDef.numitems = num_skills; - SkillDef.menuitems = Z_Calloc(num_skills, sizeof(*SkillDef.menuitems)); + SkillDef.numitems = num_skills; + SkillDef.menuitems = Z_Calloc(num_skills, sizeof(*SkillDef.menuitems)); - for (i = 0; i < num_skills; ++i) - { - SkillDef.menuitems[i].status = 1; + for (i = 0; i < num_skills; ++i) + { + SkillDef.menuitems[i].status = 1; - if (skill_infos[i].pic_name) - strncpy(SkillDef.menuitems[i].name, skill_infos[i].pic_name, 8); + if (skill_infos[i].pic_name) + strncpy(SkillDef.menuitems[i].name, skill_infos[i].pic_name, 8); - SkillDef.menuitems[i].alttext = skill_infos[i].name; - SkillDef.menuitems[i].color = skill_infos[i].text_color; + SkillDef.menuitems[i].alttext = skill_infos[i].name; + SkillDef.menuitems[i].color = skill_infos[i].text_color; - SkillDef.menuitems[i].routine = M_ChooseSkill; - SkillDef.menuitems[i].alphaKey = skill_infos[i].key; + SkillDef.menuitems[i].routine = M_ChooseSkill; + SkillDef.menuitems[i].alphaKey = skill_infos[i].key; - if (skill_infos[i].flags & SI_DEFAULT_SKILL) - SkillDef.lastOn = i; - } + if (skill_infos[i].flags & SI_DEFAULT_SKILL) + SkillDef.lastOn = i; + } - if (SkillDef.lastOn >= num_skills) - SkillDef.lastOn = num_skills - 1; - END_ONCE + if (SkillDef.lastOn >= num_skills) + SkillDef.lastOn = num_skills - 1; } -void M_StartControlPanel (void) +static void M_InitializeEpisodeMenu(void) { - // intro might call this repeatedly + int i; - if (menuactive) - return; + EpiDef.numitems = num_episodes; + EpiDef.menuitems = Z_Calloc(num_episodes, sizeof(*EpiDef.menuitems)); + + for (i = 0; i < num_episodes; ++i) + { + EpiDef.menuitems[i].status = 1; - M_InitializeSkillMenu(); + if (episodes[i].pic_name) + strncpy(EpiDef.menuitems[i].name, episodes[i].pic_name, 8); - // e6y - // We need to remove the fourth episode for pre-ultimate complevels. - // It is located here instead of M_Init() because of TNTCOMP cheat. - if (!raven && !EpiCustom) + EpiDef.menuitems[i].alttext = episodes[i].name; + + EpiDef.menuitems[i].routine = M_Episode; + EpiDef.menuitems[i].alphaKey = episodes[i].key; + } + + if (!raven) { - EpiDef.numitems = ep_end; - if (gamemode != commercial - && (compatibility_level < ultdoom_compatibility - || !W_LumpNameExists(EpiDef.menuitems[ep4].name))) + if (EpiDef.numitems <= 4) { - EpiDef.numitems--; + EpiDef.y = 63; + } + else + { + EpiDef.y = 63 - (EpiDef.numitems - 4) * (LINEHEIGHT / 2); } } + if (num_episodes > 1) + SkillDef.prevMenu = &EpiDef; + else + SkillDef.prevMenu = &MainDef; +} + +void M_StartControlPanel (void) +{ + // intro might call this repeatedly + + if (menuactive) + return; + + DO_ONCE + M_InitializeSkillMenu(); + M_InitializeEpisodeMenu(); + END_ONCE + M_ChangeMenu(&MainDef, mnact_float); itemOn = currentMenu->lastOn; // JDC } @@ -6054,7 +6011,7 @@ void M_Init(void) // like HELP1/2, and four episodes. switch(gamemode) - { + { case commercial: // This is used because DOOM 2 had only one HELP // page. I use CREDIT as second page now, but @@ -6062,8 +6019,6 @@ void M_Init(void) MainMenu[readthis] = MainMenu[quitdoom]; MainDef.numitems--; MainDef.y += 8; - if (!EpiCustom) - SkillDef.prevMenu = &MainDef; ReadDef1.routine = M_DrawReadThis1; ReadDef1.x = 330; ReadDef1.y = 165; @@ -6076,17 +6031,12 @@ void M_Init(void) // killough 2/21/98: Fix registered Doom help screen // killough 10/98: moved to second screen, moved up to the top ReadDef2.y = 15; - // fallthrough - - case shareware: - // We need to remove the fourth episode. - EpiDef.numitems--; break; + case shareware: case retail: - // We are fine. default: break; - } + } M_InitHelpScreen(); // init the help screen // phares 4/08/98 M_InitExtendedHelp(); // init extended help screens // phares 3/30/98 diff --git a/prboom2/src/umapinfo.cpp b/prboom2/src/umapinfo.cpp index 9748e4153..0bf61c7e9 100644 --- a/prboom2/src/umapinfo.cpp +++ b/prboom2/src/umapinfo.cpp @@ -31,11 +31,9 @@ extern "C" #include "doomdef.h" #include "doomstat.h" +#include "dsda/episode.h" #include "dsda/name.h" -void M_AddEpisode(const char *map, const char *gfx, const char *txt, const char *alpha); -void M_ClearEpisodes(void); - MapList Maps; } @@ -277,7 +275,7 @@ static int ParseStandardProperty(Scanner &scanner, MapEntry *mape) { if (scanner.CheckToken(TK_Identifier)) { - if (!stricmp(scanner.string, "clear")) M_ClearEpisodes(); + if (!stricmp(scanner.string, "clear")) dsda_ClearEpisodes(); else { scanner.ErrorF("Either 'clear' or string constant expected"); @@ -288,7 +286,7 @@ static int ParseStandardProperty(Scanner &scanner, MapEntry *mape) { char lumpname[9] = {0}; char *alttext = NULL; - char *key = NULL; + char key = 0; ParseLumpName(scanner, lumpname); if (scanner.CheckToken(',')) @@ -298,15 +296,13 @@ static int ParseStandardProperty(Scanner &scanner, MapEntry *mape) if (scanner.CheckToken(',')) { scanner.MustGetToken(TK_StringConst); - key = Z_Strdup(scanner.string); - key[0] = tolower(key[0]); + key = tolower(scanner.string[0]); } } - M_AddEpisode(mape->mapname, lumpname, alttext, key); + dsda_AddEpisode(mape->mapname, alttext, lumpname, key, false); if (alttext) Z_Free(alttext); - if (key) Z_Free(key); } } else if (!stricmp(pname, "bossaction"))