From 712d80006a2fa9152524c5cb442be7e31e98bbbb Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 14 Jun 2020 00:27:32 +0200 Subject: [PATCH] - split game dependent parts out of menudef.cpp. --- src/common/audio/music/music_midi_base.cpp | 2 +- src/common/audio/sound/oalsound.cpp | 2 +- src/common/menu/menu.h | 1 + src/common/objects/dobjgc.cpp | 3 +- .../rendering/hwrenderer/data/hw_cvars.cpp | 3 +- src/common/rendering/v_video.cpp | 2 +- src/common/scripting/core/imports.cpp | 2 +- src/d_main.cpp | 6 +- src/d_net.cpp | 2 +- src/g_game.cpp | 2 +- src/g_level.cpp | 2 +- src/gamedata/gi.cpp | 7 + src/gamedata/gi.h | 7 +- src/intermission/intermission.cpp | 2 +- src/menu/doommenu.cpp | 747 ++++++++++++++++++ src/menu/doommenu.h | 2 + src/menu/loadsavemenu.cpp | 2 +- src/menu/menudef.cpp | 744 +---------------- src/p_conversation.cpp | 2 +- .../swrenderer/drawers/r_draw_rgba.cpp | 1 + src/scripting/thingdef_data.cpp | 2 +- src/scripting/vmthunks.cpp | 2 +- src/sound/s_reverbedit.cpp | 2 +- 23 files changed, 790 insertions(+), 757 deletions(-) diff --git a/src/common/audio/music/music_midi_base.cpp b/src/common/audio/music/music_midi_base.cpp index 65c59c54b..6ce987801 100644 --- a/src/common/audio/music/music_midi_base.cpp +++ b/src/common/audio/music/music_midi_base.cpp @@ -35,7 +35,7 @@ #include "c_dispatch.h" #include "v_text.h" -#include "menu/menu.h" +#include "menu.h" #include #include "s_music.h" #include "c_cvars.h" diff --git a/src/common/audio/sound/oalsound.cpp b/src/common/audio/sound/oalsound.cpp index be3cc5d65..ee3e127c3 100644 --- a/src/common/audio/sound/oalsound.cpp +++ b/src/common/audio/sound/oalsound.cpp @@ -2071,7 +2071,7 @@ FSoundChan *OpenALSoundRenderer::FindLowestChannel() #endif // NO_OPENAL -#include "menu/menu.h" +#include "menu.h" void I_BuildALDeviceList(FOptionValues* opt) { diff --git a/src/common/menu/menu.h b/src/common/menu/menu.h index 3e8ecf1e8..2a0f7399b 100644 --- a/src/common/menu/menu.h +++ b/src/common/menu/menu.h @@ -288,6 +288,7 @@ void M_SetMenu(FName menu, int param = -1); void M_StartMessage(const char *message, int messagemode, FName action = NAME_None); DMenu *StartPickerMenu(DMenu *parent, const char *name, FColorCVar *cvar); void M_MarkMenus(); +FTextureID GetMenuTexture(const char* const name); struct IJoystickConfig; diff --git a/src/common/objects/dobjgc.cpp b/src/common/objects/dobjgc.cpp index 40b29a680..9afa0b0af 100644 --- a/src/common/objects/dobjgc.cpp +++ b/src/common/objects/dobjgc.cpp @@ -59,8 +59,9 @@ #include "dobject.h" #include "templates.h" #include "c_dispatch.h" -#include "menu/menu.h" +#include "menu.h" #include "stats.h" +#include "printf.h" // MACROS ------------------------------------------------------------------ diff --git a/src/common/rendering/hwrenderer/data/hw_cvars.cpp b/src/common/rendering/hwrenderer/data/hw_cvars.cpp index c8a7abc28..6faf39e0d 100644 --- a/src/common/rendering/hwrenderer/data/hw_cvars.cpp +++ b/src/common/rendering/hwrenderer/data/hw_cvars.cpp @@ -40,7 +40,8 @@ #include "c_dispatch.h" #include "v_video.h" #include "hw_cvars.h" -#include "menu/menu.h" +#include "menu.h" +#include "printf.h" CUSTOM_CVAR(Int, gl_fogmode, 1, CVAR_ARCHIVE | CVAR_NOINITCALL) diff --git a/src/common/rendering/v_video.cpp b/src/common/rendering/v_video.cpp index 2f11b86e3..435768c98 100644 --- a/src/common/rendering/v_video.cpp +++ b/src/common/rendering/v_video.cpp @@ -53,7 +53,7 @@ #include "cmdlib.h" #include "hardware.h" #include "m_png.h" -#include "menu/menu.h" +#include "menu.h" #include "vm.h" #include "r_videoscale.h" #include "i_time.h" diff --git a/src/common/scripting/core/imports.cpp b/src/common/scripting/core/imports.cpp index 0c277c964..b1b478b74 100644 --- a/src/common/scripting/core/imports.cpp +++ b/src/common/scripting/core/imports.cpp @@ -36,7 +36,7 @@ #include "gstrings.h" #include "v_font.h" -#include "menu/menu.h" +#include "menu.h" #include "types.h" #include "dictionary.h" #include "vm.h" diff --git a/src/d_main.cpp b/src/d_main.cpp index 622b9ccdc..54f6d026d 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -57,7 +57,8 @@ #include "f_wipe.h" #include "m_argv.h" #include "m_misc.h" -#include "menu/menu.h" +#include "menu.h" +#include "doommenu.h" #include "c_console.h" #include "c_dispatch.h" #include "i_sound.h" @@ -3411,6 +3412,8 @@ static int D_DoomMain_Internal (void) if (!batchrun) Printf("M_Init: Init menus.\n"); M_Init(); + M_CreateGameMenus(); + // clean up the compiler symbols which are not needed any longer. RemoveUnusedSymbols(); @@ -3674,6 +3677,7 @@ void D_Cleanup() C_ClearAliases(); // CCMDs won't be reinitialized so these need to be deleted here DestroyCVarsFlagged(CVAR_MOD); // Delete any cvar left by mods DeinitMenus(); + savegameManager.ClearSaveGames(); LightDefaults.DeleteAndClear(); // this can leak heap memory if it isn't cleared. TexAnim.DeleteAll(); TexMan.DeleteAll(); diff --git a/src/d_net.cpp b/src/d_net.cpp index 0962cec0e..22b2e9a2e 100644 --- a/src/d_net.cpp +++ b/src/d_net.cpp @@ -30,7 +30,7 @@ #include #include "version.h" -#include "menu/menu.h" +#include "menu.h" #include "i_video.h" #include "i_net.h" #include "g_game.h" diff --git a/src/g_game.cpp b/src/g_game.cpp index b65b95f63..7568ec7f8 100644 --- a/src/g_game.cpp +++ b/src/g_game.cpp @@ -38,7 +38,7 @@ #include "intermission/intermission.h" #include "m_argv.h" #include "m_misc.h" -#include "menu/menu.h" +#include "menu.h" #include "m_crc32.h" #include "p_saveg.h" #include "p_tick.h" diff --git a/src/g_level.cpp b/src/g_level.cpp index 85be99467..245d01d00 100644 --- a/src/g_level.cpp +++ b/src/g_level.cpp @@ -69,7 +69,7 @@ #include "cmdlib.h" #include "d_net.h" #include "d_netinf.h" -#include "menu/menu.h" +#include "doommenu.h" #include "a_sharedglobal.h" #include "r_utility.h" #include "p_spec.h" diff --git a/src/gamedata/gi.cpp b/src/gamedata/gi.cpp index d21f33b0c..0795bc64b 100644 --- a/src/gamedata/gi.cpp +++ b/src/gamedata/gi.cpp @@ -460,3 +460,10 @@ const char *gameinfo_t::GetFinalePage(unsigned int num) const else if (num < 1 || num > finalePages.Size()) return finalePages[0].GetChars(); else return finalePages[num-1].GetChars(); } + +bool CheckGame(const char* string, bool chexisdoom) +{ + int test = gameinfo.gametype; + if (test == GAME_Chex && chexisdoom) test = GAME_Doom; + return !stricmp(string, GameNames[test]); +} diff --git a/src/gamedata/gi.h b/src/gamedata/gi.h index 124b6742e..d2fd86b1a 100644 --- a/src/gamedata/gi.h +++ b/src/gamedata/gi.h @@ -219,11 +219,6 @@ inline const char *GameTypeName() return GameNames[gameinfo.gametype]; } -inline bool CheckGame(const char *string, bool chexisdoom) -{ - int test = gameinfo.gametype; - if (test == GAME_Chex && chexisdoom) test = GAME_Doom; - return !stricmp(string, GameNames[test]); -} +bool CheckGame(const char *string, bool chexisdoom); #endif //__GI_H__ diff --git a/src/intermission/intermission.cpp b/src/intermission/intermission.cpp index bbb4511ca..b35d3fb9f 100644 --- a/src/intermission/intermission.cpp +++ b/src/intermission/intermission.cpp @@ -46,7 +46,7 @@ #include "r_state.h" #include "c_bind.h" #include "p_conversation.h" -#include "menu/menu.h" +#include "menu.h" #include "d_net.h" #include "g_levellocals.h" #include "utf8.h" diff --git a/src/menu/doommenu.cpp b/src/menu/doommenu.cpp index d987bb89e..e3d1432d2 100644 --- a/src/menu/doommenu.cpp +++ b/src/menu/doommenu.cpp @@ -63,6 +63,7 @@ #include "r_utility.h" #include "gameconfigfile.h" #include "d_player.h" +#include "teaminfo.h" EXTERN_CVAR(Int, cl_gfxlocalization) EXTERN_CVAR(Bool, m_quickexit) @@ -509,3 +510,749 @@ CCMD(reset2saved) } +//============================================================================= +// +// Creates the episode menu +// Falls back on an option menu if there's not enough screen space to show all episodes +// +//============================================================================= + +void M_StartupEpisodeMenu(FNewGameStartup *gs) +{ + // Build episode menu + bool success = false; + bool isOld = false; + DMenuDescriptor **desc = MenuDescriptors.CheckKey(NAME_Episodemenu); + if (desc != nullptr) + { + if ((*desc)->IsKindOf(RUNTIME_CLASS(DListMenuDescriptor))) + { + DListMenuDescriptor *ld = static_cast(*desc); + + // Delete previous contents + for(unsigned i=0; imItems.Size(); i++) + { + FName n = ld->mItems[i]->mAction; + if (n == NAME_Skillmenu) + { + isOld = true; + ld->mItems.Resize(i); + break; + } + } + + + int posx = (int)ld->mXpos; + int posy = (int)ld->mYpos; + int topy = posy; + + // Get lowest y coordinate of any static item in the menu + for(unsigned i = 0; i < ld->mItems.Size(); i++) + { + int y = (int)ld->mItems[i]->GetY(); + if (y < topy) topy = y; + } + + // center the menu on the screen if the top space is larger than the bottom space + int totalheight = posy + AllEpisodes.Size() * ld->mLinespacing - topy; + + if (totalheight < 190 || AllEpisodes.Size() == 1) + { + int newtop = (200 - totalheight) / 2; + int topdelta = newtop - topy; + if (topdelta < 0) + { + for(unsigned i = 0; i < ld->mItems.Size(); i++) + { + ld->mItems[i]->OffsetPositionY(topdelta); + } + posy += topdelta; + ld->mYpos += topdelta; + } + + if (!isOld) ld->mSelectedItem = ld->mItems.Size(); + + for (unsigned i = 0; i < AllEpisodes.Size(); i++) + { + DMenuItemBase *it = nullptr; + if (AllEpisodes[i].mPicName.IsNotEmpty()) + { + FTextureID tex = GetMenuTexture(AllEpisodes[i].mPicName); + if (AllEpisodes[i].mEpisodeName.IsEmpty() || OkForLocalization(tex, AllEpisodes[i].mEpisodeName)) + continue; // We do not measure patch based entries. They are assumed to fit + } + const char *c = AllEpisodes[i].mEpisodeName; + if (*c == '$') c = GStrings(c + 1); + int textwidth = ld->mFont->StringWidth(c); + int textright = posx + textwidth; + if (posx + textright > 320) posx = std::max(0, 320 - textright); + } + + for(unsigned i = 0; i < AllEpisodes.Size(); i++) + { + DMenuItemBase *it = nullptr; + if (AllEpisodes[i].mPicName.IsNotEmpty()) + { + FTextureID tex = GetMenuTexture(AllEpisodes[i].mPicName); + if (AllEpisodes[i].mEpisodeName.IsEmpty() || OkForLocalization(tex, AllEpisodes[i].mEpisodeName)) + it = CreateListMenuItemPatch(posx, posy, ld->mLinespacing, AllEpisodes[i].mShortcut, tex, NAME_Skillmenu, i); + } + if (it == nullptr) + { + it = CreateListMenuItemText(posx, posy, ld->mLinespacing, AllEpisodes[i].mShortcut, + AllEpisodes[i].mEpisodeName, ld->mFont, ld->mFontColor, ld->mFontColor2, NAME_Skillmenu, i); + } + ld->mItems.Push(it); + posy += ld->mLinespacing; + } + if (AllEpisodes.Size() == 1) + { + ld->mAutoselect = ld->mSelectedItem; + } + success = true; + for (auto &p : ld->mItems) + { + GC::WriteBarrier(*desc, p); + } + } + } + else return; // do not recreate the option menu variant, because it is always text based. + } + if (!success) + { + // Couldn't create the episode menu, either because there's too many episodes or some error occured + // Create an option menu for episode selection instead. + DOptionMenuDescriptor *od = Create(); + MenuDescriptors[NAME_Episodemenu] = od; + od->mMenuName = NAME_Episodemenu; + od->mFont = gameinfo.gametype == GAME_Doom ? BigUpper : BigFont; + od->mTitle = "$MNU_EPISODE"; + od->mSelectedItem = 0; + od->mScrollPos = 0; + od->mClass = nullptr; + od->mPosition = -15; + od->mScrollTop = 0; + od->mIndent = 160; + od->mDontDim = false; + GC::WriteBarrier(od); + for(unsigned i = 0; i < AllEpisodes.Size(); i++) + { + auto it = CreateOptionMenuItemSubmenu(AllEpisodes[i].mEpisodeName, "Skillmenu", i); + od->mItems.Push(it); + GC::WriteBarrier(od, it); + } + } +} + +//============================================================================= +// +// +// +//============================================================================= + +static void BuildPlayerclassMenu() +{ + bool success = false; + + // Build player class menu + DMenuDescriptor **desc = MenuDescriptors.CheckKey(NAME_Playerclassmenu); + if (desc != nullptr) + { + if ((*desc)->IsKindOf(RUNTIME_CLASS(DListMenuDescriptor))) + { + DListMenuDescriptor *ld = static_cast(*desc); + // add player display + + ld->mSelectedItem = ld->mItems.Size(); + + int posy = (int)ld->mYpos; + int topy = posy; + + // Get lowest y coordinate of any static item in the menu + for(unsigned i = 0; i < ld->mItems.Size(); i++) + { + int y = (int)ld->mItems[i]->GetY(); + if (y < topy) topy = y; + } + + // Count the number of items this menu will show + int numclassitems = 0; + for (unsigned i = 0; i < PlayerClasses.Size (); i++) + { + if (!(PlayerClasses[i].Flags & PCF_NOMENU)) + { + const char *pname = GetPrintableDisplayName(PlayerClasses[i].Type); + if (pname != nullptr) + { + numclassitems++; + } + } + } + + // center the menu on the screen if the top space is larger than the bottom space + int totalheight = posy + (numclassitems+1) * ld->mLinespacing - topy; + + if (numclassitems <= 1) + { + // create a dummy item that auto-chooses the default class. + auto it = CreateListMenuItemText(0, 0, 0, 'p', "player", + ld->mFont,ld->mFontColor, ld->mFontColor2, NAME_Episodemenu, -1000); + ld->mAutoselect = ld->mItems.Push(it); + success = true; + } + else if (totalheight <= 190) + { + int newtop = (200 - totalheight + topy) / 2; + int topdelta = newtop - topy; + if (topdelta < 0) + { + for(unsigned i = 0; i < ld->mItems.Size(); i++) + { + ld->mItems[i]->OffsetPositionY(topdelta); + } + posy -= topdelta; + } + + int n = 0; + for (unsigned i = 0; i < PlayerClasses.Size (); i++) + { + if (!(PlayerClasses[i].Flags & PCF_NOMENU)) + { + const char *pname = GetPrintableDisplayName(PlayerClasses[i].Type); + if (pname != nullptr) + { + auto it = CreateListMenuItemText(ld->mXpos, ld->mYpos, ld->mLinespacing, *pname, + pname, ld->mFont,ld->mFontColor,ld->mFontColor2, NAME_Episodemenu, i); + ld->mItems.Push(it); + ld->mYpos += ld->mLinespacing; + n++; + } + } + } + if (n > 1 && !gameinfo.norandomplayerclass) + { + auto it = CreateListMenuItemText(ld->mXpos, ld->mYpos, ld->mLinespacing, 'r', + "$MNU_RANDOM", ld->mFont,ld->mFontColor,ld->mFontColor2, NAME_Episodemenu, -1); + ld->mItems.Push(it); + } + if (n == 0) + { + const char *pname = GetPrintableDisplayName(PlayerClasses[0].Type); + if (pname != nullptr) + { + auto it = CreateListMenuItemText(ld->mXpos, ld->mYpos, ld->mLinespacing, *pname, + pname, ld->mFont,ld->mFontColor,ld->mFontColor2, NAME_Episodemenu, 0); + ld->mItems.Push(it); + } + } + success = true; + for (auto &p : ld->mItems) + { + GC::WriteBarrier(ld, p); + } + } + } + } + if (!success) + { + // Couldn't create the playerclass menu, either because there's too many episodes or some error occured + // Create an option menu for class selection instead. + DOptionMenuDescriptor *od = Create(); + MenuDescriptors[NAME_Playerclassmenu] = od; + od->mMenuName = NAME_Playerclassmenu; + od->mFont = gameinfo.gametype == GAME_Doom ? BigUpper : BigFont; + od->mTitle = "$MNU_CHOOSECLASS"; + od->mSelectedItem = 0; + od->mScrollPos = 0; + od->mClass = nullptr; + od->mPosition = -15; + od->mScrollTop = 0; + od->mIndent = 160; + od->mDontDim = false; + od->mNetgameMessage = "$NEWGAME"; + GC::WriteBarrier(od); + for (unsigned i = 0; i < PlayerClasses.Size (); i++) + { + if (!(PlayerClasses[i].Flags & PCF_NOMENU)) + { + const char *pname = GetPrintableDisplayName(PlayerClasses[i].Type); + if (pname != nullptr) + { + auto it = CreateOptionMenuItemSubmenu(pname, "Episodemenu", i); + od->mItems.Push(it); + GC::WriteBarrier(od, it); + } + } + } + auto it = CreateOptionMenuItemSubmenu("Random", "Episodemenu", -1); + od->mItems.Push(it); + GC::WriteBarrier(od, it); + } +} + +//============================================================================= +// +// Reads any XHAIRS lumps for the names of crosshairs and +// adds them to the display options menu. +// +//============================================================================= + +static void InitCrosshairsList() +{ + int lastlump, lump; + + lastlump = 0; + + FOptionValues **opt = OptionValues.CheckKey(NAME_Crosshairs); + if (opt == nullptr) + { + return; // no crosshair value list present. No need to go on. + } + + FOptionValues::Pair *pair = &(*opt)->mValues[(*opt)->mValues.Reserve(1)]; + pair->Value = 0; + pair->Text = "None"; + + while ((lump = fileSystem.FindLump("XHAIRS", &lastlump)) != -1) + { + FScanner sc(lump); + while (sc.GetNumber()) + { + FOptionValues::Pair value; + value.Value = sc.Number; + sc.MustGetString(); + value.Text = sc.String; + if (value.Value != 0) + { // Check if it already exists. If not, add it. + unsigned int i; + + for (i = 1; i < (*opt)->mValues.Size(); ++i) + { + if ((*opt)->mValues[i].Value == value.Value) + { + break; + } + } + if (i < (*opt)->mValues.Size()) + { + (*opt)->mValues[i].Text = value.Text; + } + else + { + (*opt)->mValues.Push(value); + } + } + } + } +} + +//============================================================================= +// +// With the current workings of the menu system this cannot be done any longer +// from within the respective CCMDs. +// +//============================================================================= + +static void InitKeySections() +{ + DMenuDescriptor **desc = MenuDescriptors.CheckKey(NAME_CustomizeControls); + if (desc != nullptr) + { + if ((*desc)->IsKindOf(RUNTIME_CLASS(DOptionMenuDescriptor))) + { + DOptionMenuDescriptor *menu = static_cast(*desc); + + for (unsigned i = 0; i < KeySections.Size(); i++) + { + FKeySection *sect = &KeySections[i]; + DMenuItemBase *item = CreateOptionMenuItemStaticText(" "); + menu->mItems.Push(item); + item = CreateOptionMenuItemStaticText(sect->mTitle, 1); + menu->mItems.Push(item); + for (unsigned j = 0; j < sect->mActions.Size(); j++) + { + FKeyAction *act = §->mActions[j]; + item = CreateOptionMenuItemControl(act->mTitle, act->mAction, &Bindings); + menu->mItems.Push(item); + } + } + for (auto &p : menu->mItems) + { + GC::WriteBarrier(*desc, p); + } + } + } +} + + +//============================================================================= +// +// Special menus will be created once all engine data is loaded +// +//============================================================================= + +void M_CreateGameMenus() +{ + BuildPlayerclassMenu(); + InitCrosshairsList(); + InitKeySections(); + + auto opt = OptionValues.CheckKey(NAME_PlayerTeam); + if (opt != nullptr) + { + auto op = *opt; + op->mValues.Resize(Teams.Size() + 1); + op->mValues[0].Value = 0; + op->mValues[0].Text = "$OPTVAL_NONE"; + for (unsigned i = 0; i < Teams.Size(); i++) + { + op->mValues[i+1].Value = i+1; + op->mValues[i+1].Text = Teams[i].GetName(); + } + } + opt = OptionValues.CheckKey(NAME_PlayerClass); + if (opt != nullptr) + { + auto op = *opt; + int o = 0; + if (!gameinfo.norandomplayerclass && PlayerClasses.Size() > 1) + { + op->mValues.Resize(PlayerClasses.Size()+1); + op->mValues[0].Value = -1; + op->mValues[0].Text = "$MNU_RANDOM"; + o = 1; + } + else op->mValues.Resize(PlayerClasses.Size()); + for (unsigned i = 0; i < PlayerClasses.Size(); i++) + { + op->mValues[i+o].Value = i; + op->mValues[i+o].Text = GetPrintableDisplayName(PlayerClasses[i].Type); + } + } +} + +DEFINE_ACTION_FUNCTION(DMenu, UpdateColorsets) +{ + PARAM_PROLOGUE; + PARAM_POINTER(playerClass, FPlayerClass); + + TArray PlayerColorSets; + + EnumColorSets(playerClass->Type, &PlayerColorSets); + + auto opt = OptionValues.CheckKey(NAME_PlayerColors); + if (opt != nullptr) + { + auto op = *opt; + op->mValues.Resize(PlayerColorSets.Size() + 1); + op->mValues[0].Value = -1; + op->mValues[0].Text = "$OPTVAL_CUSTOM"; + for (unsigned i = 0; i < PlayerColorSets.Size(); i++) + { + auto cset = GetColorSet(playerClass->Type, PlayerColorSets[i]); + op->mValues[i + 1].Value = PlayerColorSets[i]; + op->mValues[i + 1].Text = cset? cset->Name.GetChars() : "?"; // The null case should never happen here. + } + } + return 0; +} + +DEFINE_ACTION_FUNCTION(DMenu, UpdateSkinOptions) +{ + PARAM_PROLOGUE; + PARAM_POINTER(playerClass, FPlayerClass); + + auto opt = OptionValues.CheckKey(NAME_PlayerSkin); + if (opt != nullptr) + { + auto op = *opt; + + if ((GetDefaultByType(playerClass->Type)->flags4 & MF4_NOSKIN) || players[consoleplayer].userinfo.GetPlayerClassNum() == -1) + { + op->mValues.Resize(1); + op->mValues[0].Value = -1; + op->mValues[0].Text = "$OPTVAL_DEFAULT"; + } + else + { + op->mValues.Clear(); + for (unsigned i = 0; i < Skins.Size(); i++) + { + op->mValues.Reserve(1); + op->mValues.Last().Value = i; + op->mValues.Last().Text = Skins[i].Name; + } + } + } + return 0; +} + +//============================================================================= +// +// The skill menu must be refeshed each time it starts up +// +//============================================================================= +extern int restart; + +void M_StartupSkillMenu(FNewGameStartup *gs) +{ + static int done = -1; + bool success = false; + TArray MenuSkills; + TArray SkillIndices; + if (MenuSkills.Size() == 0) + { + for (unsigned ind = 0; ind < AllSkills.Size(); ind++) + { + if (!AllSkills[ind].NoMenu) + { + MenuSkills.Push(&AllSkills[ind]); + SkillIndices.Push(ind); + } + } + } + if (MenuSkills.Size() == 0) I_Error("No valid skills for menu found. At least one must be defined."); + + int defskill = DefaultSkill; + if ((unsigned int)defskill >= MenuSkills.Size()) + { + defskill = SkillIndices[(MenuSkills.Size() - 1) / 2]; + } + if (AllSkills[defskill].NoMenu) + { + for (defskill = 0; defskill < (int)AllSkills.Size(); defskill++) + { + if (!AllSkills[defskill].NoMenu) break; + } + } + int defindex = 0; + for (unsigned i = 0; i < MenuSkills.Size(); i++) + { + if (MenuSkills[i] == &AllSkills[defskill]) + { + defindex = i; + break; + } + } + + DMenuDescriptor **desc = MenuDescriptors.CheckKey(NAME_Skillmenu); + if (desc != nullptr) + { + if ((*desc)->IsKindOf(RUNTIME_CLASS(DListMenuDescriptor))) + { + DListMenuDescriptor *ld = static_cast(*desc); + int posx = (int)ld->mXpos; + int y = (int)ld->mYpos; + + // Delete previous contents + for(unsigned i=0; imItems.Size(); i++) + { + FName n = ld->mItems[i]->mAction; + if (n == NAME_Startgame || n == NAME_StartgameConfirm) + { + ld->mItems.Resize(i); + break; + } + } + + if (done != restart) + { + done = restart; + ld->mSelectedItem = ld->mItems.Size() + defindex; + + int posy = y; + int topy = posy; + + // Get lowest y coordinate of any static item in the menu + for(unsigned i = 0; i < ld->mItems.Size(); i++) + { + int y = (int)ld->mItems[i]->GetY(); + if (y < topy) topy = y; + } + + // center the menu on the screen if the top space is larger than the bottom space + int totalheight = posy + MenuSkills.Size() * ld->mLinespacing - topy; + + if (totalheight < 190 || MenuSkills.Size() == 1) + { + int newtop = (200 - totalheight) / 2; + int topdelta = newtop - topy; + if (topdelta < 0) + { + for(unsigned i = 0; i < ld->mItems.Size(); i++) + { + ld->mItems[i]->OffsetPositionY(topdelta); + } + ld->mYpos = y = posy + topdelta; + } + } + else + { + // too large + desc = nullptr; + done = false; + goto fail; + } + } + + for (unsigned int i = 0; i < MenuSkills.Size(); i++) + { + FSkillInfo &skill = *MenuSkills[i]; + DMenuItemBase *li = nullptr; + + FString *pItemText = nullptr; + if (gs->PlayerClass != nullptr) + { + pItemText = skill.MenuNamesForPlayerClass.CheckKey(gs->PlayerClass); + } + + if (skill.PicName.Len() != 0 && pItemText == nullptr) + { + FTextureID tex = GetMenuTexture(skill.PicName); + if (skill.MenuName.IsEmpty() || OkForLocalization(tex, skill.MenuName)) + continue; + } + const char *c = pItemText ? pItemText->GetChars() : skill.MenuName.GetChars(); + if (*c == '$') c = GStrings(c + 1); + int textwidth = ld->mFont->StringWidth(c); + int textright = posx + textwidth; + if (posx + textright > 320) posx = std::max(0, 320 - textright); + } + + unsigned firstitem = ld->mItems.Size(); + for(unsigned int i = 0; i < MenuSkills.Size(); i++) + { + FSkillInfo &skill = *MenuSkills[i]; + DMenuItemBase *li = nullptr; + // Using a different name for skills that must be confirmed makes handling this easier. + FName action = (skill.MustConfirm && !AllEpisodes[gs->Episode].mNoSkill) ? + NAME_StartgameConfirm : NAME_Startgame; + FString *pItemText = nullptr; + if (gs->PlayerClass != nullptr) + { + pItemText = skill.MenuNamesForPlayerClass.CheckKey(gs->PlayerClass); + } + + EColorRange color = (EColorRange)skill.GetTextColor(); + if (color == CR_UNTRANSLATED) color = ld->mFontColor; + if (skill.PicName.Len() != 0 && pItemText == nullptr) + { + FTextureID tex = GetMenuTexture(skill.PicName); + if (skill.MenuName.IsEmpty() || OkForLocalization(tex, skill.MenuName)) + li = CreateListMenuItemPatch(posx, y, ld->mLinespacing, skill.Shortcut, tex, action, SkillIndices[i]); + } + if (li == nullptr) + { + li = CreateListMenuItemText(posx, y, ld->mLinespacing, skill.Shortcut, + pItemText? *pItemText : skill.MenuName, ld->mFont, color,ld->mFontColor2, action, SkillIndices[i]); + } + ld->mItems.Push(li); + GC::WriteBarrier(*desc, li); + y += ld->mLinespacing; + } + if (AllEpisodes[gs->Episode].mNoSkill || MenuSkills.Size() == 1) + { + ld->mAutoselect = firstitem + defindex; + } + else + { + ld->mAutoselect = -1; + } + success = true; + } + } + if (success) return; +fail: + // Option menu fallback for overlong skill lists + DOptionMenuDescriptor *od; + if (desc == nullptr) + { + od = Create(); + MenuDescriptors[NAME_Skillmenu] = od; + od->mMenuName = NAME_Skillmenu; + od->mFont = gameinfo.gametype == GAME_Doom ? BigUpper : BigFont; + od->mTitle = "$MNU_CHOOSESKILL"; + od->mSelectedItem = defindex; + od->mScrollPos = 0; + od->mClass = nullptr; + od->mPosition = -15; + od->mScrollTop = 0; + od->mIndent = 160; + od->mDontDim = false; + GC::WriteBarrier(od); + } + else + { + od = static_cast(*desc); + od->mItems.Clear(); + } + for(unsigned int i = 0; i < MenuSkills.Size(); i++) + { + FSkillInfo &skill = *MenuSkills[i]; + DMenuItemBase *li; + // Using a different name for skills that must be confirmed makes handling this easier. + const char *action = (skill.MustConfirm && !AllEpisodes[gs->Episode].mNoSkill) ? + "StartgameConfirm" : "Startgame"; + + FString *pItemText = nullptr; + if (gs->PlayerClass != nullptr) + { + pItemText = skill.MenuNamesForPlayerClass.CheckKey(gs->PlayerClass); + } + li = CreateOptionMenuItemSubmenu(pItemText? *pItemText : skill.MenuName, action, SkillIndices[i]); + od->mItems.Push(li); + GC::WriteBarrier(od, li); + if (!done) + { + done = true; + od->mSelectedItem = defindex; + } + } +} + +//========================================================================== +// +// Defines how graphics substitution is handled. +// 0: Never replace a text-containing graphic with a font-based text. +// 1: Always replace, regardless of any missing information. Useful for testing the substitution without providing full data. +// 2: Only replace for non-default texts, i.e. if some language redefines the string's content, use it instead of the graphic. Never replace a localized graphic. +// 3: Only replace if the string is not the default and the graphic comes from the IWAD. Never replace a localized graphic. +// 4: Like 1, but lets localized graphics pass. +// +// The default is 3, which only replaces known content with non-default texts. +// +//========================================================================== + +CUSTOM_CVAR(Int, cl_gfxlocalization, 3, CVAR_ARCHIVE) +{ + if (self < 0 || self > 4) self = 0; +} + +bool OkForLocalization(FTextureID texnum, const char* substitute) +{ + if (!texnum.isValid()) return false; + + // First the unconditional settings, 0='never' and 1='always'. + if (cl_gfxlocalization == 1 || gameinfo.forcetextinmenus) return false; + if (cl_gfxlocalization == 0 || gameinfo.forcenogfxsubstitution) return true; + return TexMan.OkForLocalization(texnum, substitute, cl_gfxlocalization); +} + +bool CheckSkipGameOptionBlock(FScanner &sc) +{ + bool filter = false; + if (sc.Compare("ReadThis")) filter |= gameinfo.drawreadthis; + else if (sc.Compare("Swapmenu")) filter |= gameinfo.swapmenu; + return filter; +} + +void SetDefaultMenuColors() +{ + OptionSettings.mTitleColor = V_FindFontColor(gameinfo.mTitleColor); + OptionSettings.mFontColor = V_FindFontColor(gameinfo.mFontColor); + OptionSettings.mFontColorValue = V_FindFontColor(gameinfo.mFontColorValue); + OptionSettings.mFontColorMore = V_FindFontColor(gameinfo.mFontColorMore); + OptionSettings.mFontColorHeader = V_FindFontColor(gameinfo.mFontColorHeader); + OptionSettings.mFontColorHighlight = V_FindFontColor(gameinfo.mFontColorHighlight); + OptionSettings.mFontColorSelection = V_FindFontColor(gameinfo.mFontColorSelection); +} diff --git a/src/menu/doommenu.h b/src/menu/doommenu.h index 1b566480d..ac1d83998 100644 --- a/src/menu/doommenu.h +++ b/src/menu/doommenu.h @@ -14,6 +14,7 @@ struct FNewGameStartup extern FNewGameStartup NewGameStartupInfo; void M_StartupEpisodeMenu(FNewGameStartup *gs); void M_StartupSkillMenu(FNewGameStartup *gs); +void M_CreateGameMenus(); // The savegame manager contains too much code that is game specific. Parts are shareable but need more work first. struct FSaveGameNode @@ -65,3 +66,4 @@ public: }; extern FSavegameManager savegameManager; + diff --git a/src/menu/loadsavemenu.cpp b/src/menu/loadsavemenu.cpp index 668c6b706..3200d379d 100644 --- a/src/menu/loadsavemenu.cpp +++ b/src/menu/loadsavemenu.cpp @@ -33,7 +33,7 @@ ** */ -#include "menu/menu.h" +#include "doommenu.h" #include "version.h" #include "g_game.h" #include "m_png.h" diff --git a/src/menu/menudef.cpp b/src/menu/menudef.cpp index 13f95583f..7d11dbbe1 100644 --- a/src/menu/menudef.cpp +++ b/src/menu/menudef.cpp @@ -33,28 +33,27 @@ */ #include -#include "menu/menu.h" +#include "menu.h" #include "filesystem.h" #include "c_bind.h" #include "i_music.h" -#include "gi.h" #include "i_sound.h" #include "cmdlib.h" #include "vm.h" #include "types.h" -#include "gameconfigfile.h" #include "m_argv.h" #include "i_soundfont.h" #include "i_system.h" #include "v_video.h" #include "gstrings.h" -#include "teaminfo.h" -#include "r_data/sprites.h" #include #include "texturemanager.h" +#include "printf.h" -void ClearSaveGames(); +bool CheckGame(const char* string, bool chexisdoom); +bool CheckSkipGameOptionBlock(FScanner& sc); +void SetDefaultMenuColors(); MenuDescriptorList MenuDescriptors; static DListMenuDescriptor *DefaultListMenuSettings; // contains common settings for all list menus @@ -69,35 +68,6 @@ void I_BuildALDeviceList(FOptionValues *opt); void I_BuildALResamplersList(FOptionValues *opt); -//========================================================================== -// -// Defines how graphics substitution is handled. -// 0: Never replace a text-containing graphic with a font-based text. -// 1: Always replace, regardless of any missing information. Useful for testing the substitution without providing full data. -// 2: Only replace for non-default texts, i.e. if some language redefines the string's content, use it instead of the graphic. Never replace a localized graphic. -// 3: Only replace if the string is not the default and the graphic comes from the IWAD. Never replace a localized graphic. -// 4: Like 1, but lets localized graphics pass. -// -// The default is 3, which only replaces known content with non-default texts. -// -//========================================================================== - -CUSTOM_CVAR(Int, cl_gfxlocalization, 3, CVAR_ARCHIVE) -{ - if (self < 0 || self > 4) self = 0; -} - -bool OkForLocalization(FTextureID texnum, const char* substitute) -{ - if (!texnum.isValid()) return false; - - // First the unconditional settings, 0='never' and 1='always'. - if (cl_gfxlocalization == 1 || gameinfo.forcetextinmenus) return false; - if (cl_gfxlocalization == 0 || gameinfo.forcenogfxsubstitution) return true; - return TexMan.OkForLocalization(texnum, substitute, cl_gfxlocalization); -} - - DEFINE_GLOBAL_NAMED(OptionSettings, OptionMenuSettings) @@ -182,10 +152,9 @@ void DeinitMenus() } MenuDescriptors.Clear(); OptionValues.Clear(); - savegameManager.ClearSaveGames(); } -static FTextureID GetMenuTexture(const char* const name) +FTextureID GetMenuTexture(const char* const name) { const FTextureID texture = TexMan.CheckForTexture(name, ETextureType::MiscPatch); @@ -253,8 +222,7 @@ static bool CheckSkipOptionBlock(FScanner &sc) do { sc.MustGetString(); - if (sc.Compare("ReadThis")) filter |= gameinfo.drawreadthis; - else if (sc.Compare("Swapmenu")) filter |= gameinfo.swapmenu; + if (CheckSkipGameOptionBlock(sc)) filter = true; else if (sc.Compare("Windows")) { #ifdef _WIN32 @@ -984,7 +952,7 @@ static void ParseOptionMenu(FScanner &sc) sc.MustGetString(); DOptionMenuDescriptor *desc = Create(); - desc->mFont = gameinfo.gametype == GAME_Doom ? BigUpper : BigFont; + desc->mFont = BigUpper; desc->mMenuName = sc.String; desc->mSelectedItem = -1; desc->mScrollPos = 0; @@ -1029,13 +997,7 @@ void M_ParseMenuDefs() { int lump, lastlump = 0; - OptionSettings.mTitleColor = V_FindFontColor(gameinfo.mTitleColor); - OptionSettings.mFontColor = V_FindFontColor(gameinfo.mFontColor); - OptionSettings.mFontColorValue = V_FindFontColor(gameinfo.mFontColorValue); - OptionSettings.mFontColorMore = V_FindFontColor(gameinfo.mFontColorMore); - OptionSettings.mFontColorHeader = V_FindFontColor(gameinfo.mFontColorHeader); - OptionSettings.mFontColorHighlight = V_FindFontColor(gameinfo.mFontColorHighlight); - OptionSettings.mFontColorSelection = V_FindFontColor(gameinfo.mFontColorSelection); + SetDefaultMenuColors(); // these are supposed to get GC'd after parsing is complete. DefaultListMenuSettings = Create(); DefaultOptionMenuSettings = Create(); @@ -1106,342 +1068,6 @@ void M_ParseMenuDefs() } -//============================================================================= -// -// Creates the episode menu -// Falls back on an option menu if there's not enough screen space to show all episodes -// -//============================================================================= - -void M_StartupEpisodeMenu(FNewGameStartup *gs) -{ - // Build episode menu - bool success = false; - bool isOld = false; - DMenuDescriptor **desc = MenuDescriptors.CheckKey(NAME_Episodemenu); - if (desc != nullptr) - { - if ((*desc)->IsKindOf(RUNTIME_CLASS(DListMenuDescriptor))) - { - DListMenuDescriptor *ld = static_cast(*desc); - - // Delete previous contents - for(unsigned i=0; imItems.Size(); i++) - { - FName n = ld->mItems[i]->mAction; - if (n == NAME_Skillmenu) - { - isOld = true; - ld->mItems.Resize(i); - break; - } - } - - - int posx = (int)ld->mXpos; - int posy = (int)ld->mYpos; - int topy = posy; - - // Get lowest y coordinate of any static item in the menu - for(unsigned i = 0; i < ld->mItems.Size(); i++) - { - int y = (int)ld->mItems[i]->GetY(); - if (y < topy) topy = y; - } - - // center the menu on the screen if the top space is larger than the bottom space - int totalheight = posy + AllEpisodes.Size() * ld->mLinespacing - topy; - - if (totalheight < 190 || AllEpisodes.Size() == 1) - { - int newtop = (200 - totalheight) / 2; - int topdelta = newtop - topy; - if (topdelta < 0) - { - for(unsigned i = 0; i < ld->mItems.Size(); i++) - { - ld->mItems[i]->OffsetPositionY(topdelta); - } - posy += topdelta; - ld->mYpos += topdelta; - } - - if (!isOld) ld->mSelectedItem = ld->mItems.Size(); - - for (unsigned i = 0; i < AllEpisodes.Size(); i++) - { - DMenuItemBase *it = nullptr; - if (AllEpisodes[i].mPicName.IsNotEmpty()) - { - FTextureID tex = GetMenuTexture(AllEpisodes[i].mPicName); - if (AllEpisodes[i].mEpisodeName.IsEmpty() || OkForLocalization(tex, AllEpisodes[i].mEpisodeName)) - continue; // We do not measure patch based entries. They are assumed to fit - } - const char *c = AllEpisodes[i].mEpisodeName; - if (*c == '$') c = GStrings(c + 1); - int textwidth = ld->mFont->StringWidth(c); - int textright = posx + textwidth; - if (posx + textright > 320) posx = std::max(0, 320 - textright); - } - - for(unsigned i = 0; i < AllEpisodes.Size(); i++) - { - DMenuItemBase *it = nullptr; - if (AllEpisodes[i].mPicName.IsNotEmpty()) - { - FTextureID tex = GetMenuTexture(AllEpisodes[i].mPicName); - if (AllEpisodes[i].mEpisodeName.IsEmpty() || OkForLocalization(tex, AllEpisodes[i].mEpisodeName)) - it = CreateListMenuItemPatch(posx, posy, ld->mLinespacing, AllEpisodes[i].mShortcut, tex, NAME_Skillmenu, i); - } - if (it == nullptr) - { - it = CreateListMenuItemText(posx, posy, ld->mLinespacing, AllEpisodes[i].mShortcut, - AllEpisodes[i].mEpisodeName, ld->mFont, ld->mFontColor, ld->mFontColor2, NAME_Skillmenu, i); - } - ld->mItems.Push(it); - posy += ld->mLinespacing; - } - if (AllEpisodes.Size() == 1) - { - ld->mAutoselect = ld->mSelectedItem; - } - success = true; - for (auto &p : ld->mItems) - { - GC::WriteBarrier(*desc, p); - } - } - } - else return; // do not recreate the option menu variant, because it is always text based. - } - if (!success) - { - // Couldn't create the episode menu, either because there's too many episodes or some error occured - // Create an option menu for episode selection instead. - DOptionMenuDescriptor *od = Create(); - MenuDescriptors[NAME_Episodemenu] = od; - od->mMenuName = NAME_Episodemenu; - od->mFont = gameinfo.gametype == GAME_Doom ? BigUpper : BigFont; - od->mTitle = "$MNU_EPISODE"; - od->mSelectedItem = 0; - od->mScrollPos = 0; - od->mClass = nullptr; - od->mPosition = -15; - od->mScrollTop = 0; - od->mIndent = 160; - od->mDontDim = false; - GC::WriteBarrier(od); - for(unsigned i = 0; i < AllEpisodes.Size(); i++) - { - auto it = CreateOptionMenuItemSubmenu(AllEpisodes[i].mEpisodeName, "Skillmenu", i); - od->mItems.Push(it); - GC::WriteBarrier(od, it); - } - } -} - -//============================================================================= -// -// -// -//============================================================================= - -static void BuildPlayerclassMenu() -{ - bool success = false; - - // Build player class menu - DMenuDescriptor **desc = MenuDescriptors.CheckKey(NAME_Playerclassmenu); - if (desc != nullptr) - { - if ((*desc)->IsKindOf(RUNTIME_CLASS(DListMenuDescriptor))) - { - DListMenuDescriptor *ld = static_cast(*desc); - // add player display - - ld->mSelectedItem = ld->mItems.Size(); - - int posy = (int)ld->mYpos; - int topy = posy; - - // Get lowest y coordinate of any static item in the menu - for(unsigned i = 0; i < ld->mItems.Size(); i++) - { - int y = (int)ld->mItems[i]->GetY(); - if (y < topy) topy = y; - } - - // Count the number of items this menu will show - int numclassitems = 0; - for (unsigned i = 0; i < PlayerClasses.Size (); i++) - { - if (!(PlayerClasses[i].Flags & PCF_NOMENU)) - { - const char *pname = GetPrintableDisplayName(PlayerClasses[i].Type); - if (pname != nullptr) - { - numclassitems++; - } - } - } - - // center the menu on the screen if the top space is larger than the bottom space - int totalheight = posy + (numclassitems+1) * ld->mLinespacing - topy; - - if (numclassitems <= 1) - { - // create a dummy item that auto-chooses the default class. - auto it = CreateListMenuItemText(0, 0, 0, 'p', "player", - ld->mFont,ld->mFontColor, ld->mFontColor2, NAME_Episodemenu, -1000); - ld->mAutoselect = ld->mItems.Push(it); - success = true; - } - else if (totalheight <= 190) - { - int newtop = (200 - totalheight + topy) / 2; - int topdelta = newtop - topy; - if (topdelta < 0) - { - for(unsigned i = 0; i < ld->mItems.Size(); i++) - { - ld->mItems[i]->OffsetPositionY(topdelta); - } - posy -= topdelta; - } - - int n = 0; - for (unsigned i = 0; i < PlayerClasses.Size (); i++) - { - if (!(PlayerClasses[i].Flags & PCF_NOMENU)) - { - const char *pname = GetPrintableDisplayName(PlayerClasses[i].Type); - if (pname != nullptr) - { - auto it = CreateListMenuItemText(ld->mXpos, ld->mYpos, ld->mLinespacing, *pname, - pname, ld->mFont,ld->mFontColor,ld->mFontColor2, NAME_Episodemenu, i); - ld->mItems.Push(it); - ld->mYpos += ld->mLinespacing; - n++; - } - } - } - if (n > 1 && !gameinfo.norandomplayerclass) - { - auto it = CreateListMenuItemText(ld->mXpos, ld->mYpos, ld->mLinespacing, 'r', - "$MNU_RANDOM", ld->mFont,ld->mFontColor,ld->mFontColor2, NAME_Episodemenu, -1); - ld->mItems.Push(it); - } - if (n == 0) - { - const char *pname = GetPrintableDisplayName(PlayerClasses[0].Type); - if (pname != nullptr) - { - auto it = CreateListMenuItemText(ld->mXpos, ld->mYpos, ld->mLinespacing, *pname, - pname, ld->mFont,ld->mFontColor,ld->mFontColor2, NAME_Episodemenu, 0); - ld->mItems.Push(it); - } - } - success = true; - for (auto &p : ld->mItems) - { - GC::WriteBarrier(ld, p); - } - } - } - } - if (!success) - { - // Couldn't create the playerclass menu, either because there's too many episodes or some error occured - // Create an option menu for class selection instead. - DOptionMenuDescriptor *od = Create(); - MenuDescriptors[NAME_Playerclassmenu] = od; - od->mMenuName = NAME_Playerclassmenu; - od->mFont = gameinfo.gametype == GAME_Doom ? BigUpper : BigFont; - od->mTitle = "$MNU_CHOOSECLASS"; - od->mSelectedItem = 0; - od->mScrollPos = 0; - od->mClass = nullptr; - od->mPosition = -15; - od->mScrollTop = 0; - od->mIndent = 160; - od->mDontDim = false; - od->mNetgameMessage = "$NEWGAME"; - GC::WriteBarrier(od); - for (unsigned i = 0; i < PlayerClasses.Size (); i++) - { - if (!(PlayerClasses[i].Flags & PCF_NOMENU)) - { - const char *pname = GetPrintableDisplayName(PlayerClasses[i].Type); - if (pname != nullptr) - { - auto it = CreateOptionMenuItemSubmenu(pname, "Episodemenu", i); - od->mItems.Push(it); - GC::WriteBarrier(od, it); - } - } - } - auto it = CreateOptionMenuItemSubmenu("Random", "Episodemenu", -1); - od->mItems.Push(it); - GC::WriteBarrier(od, it); - } -} - -//============================================================================= -// -// Reads any XHAIRS lumps for the names of crosshairs and -// adds them to the display options menu. -// -//============================================================================= - -static void InitCrosshairsList() -{ - int lastlump, lump; - - lastlump = 0; - - FOptionValues **opt = OptionValues.CheckKey(NAME_Crosshairs); - if (opt == nullptr) - { - return; // no crosshair value list present. No need to go on. - } - - FOptionValues::Pair *pair = &(*opt)->mValues[(*opt)->mValues.Reserve(1)]; - pair->Value = 0; - pair->Text = "None"; - - while ((lump = fileSystem.FindLump("XHAIRS", &lastlump)) != -1) - { - FScanner sc(lump); - while (sc.GetNumber()) - { - FOptionValues::Pair value; - value.Value = sc.Number; - sc.MustGetString(); - value.Text = sc.String; - if (value.Value != 0) - { // Check if it already exists. If not, add it. - unsigned int i; - - for (i = 1; i < (*opt)->mValues.Size(); ++i) - { - if ((*opt)->mValues[i].Value == value.Value) - { - break; - } - } - if (i < (*opt)->mValues.Size()) - { - (*opt)->mValues[i].Text = value.Text; - } - else - { - (*opt)->mValues.Push(value); - } - } - } - } -} - //============================================================================= // // Initialize the music configuration submenus @@ -1507,44 +1133,6 @@ static void InitMusicMenus() } } -//============================================================================= -// -// With the current workings of the menu system this cannot be done any longer -// from within the respective CCMDs. -// -//============================================================================= - -static void InitKeySections() -{ - DMenuDescriptor **desc = MenuDescriptors.CheckKey(NAME_CustomizeControls); - if (desc != nullptr) - { - if ((*desc)->IsKindOf(RUNTIME_CLASS(DOptionMenuDescriptor))) - { - DOptionMenuDescriptor *menu = static_cast(*desc); - - for (unsigned i = 0; i < KeySections.Size(); i++) - { - FKeySection *sect = &KeySections[i]; - DMenuItemBase *item = CreateOptionMenuItemStaticText(" "); - menu->mItems.Push(item); - item = CreateOptionMenuItemStaticText(sect->mTitle, 1); - menu->mItems.Push(item); - for (unsigned j = 0; j < sect->mActions.Size(); j++) - { - FKeyAction *act = §->mActions[j]; - item = CreateOptionMenuItemControl(act->mTitle, act->mAction, &Bindings); - menu->mItems.Push(item); - } - } - for (auto &p : menu->mItems) - { - GC::WriteBarrier(*desc, p); - } - } - } -} - //============================================================================= // // Special menus will be created once all engine data is loaded @@ -1553,11 +1141,7 @@ static void InitKeySections() void M_CreateMenus() { - BuildPlayerclassMenu(); - InitCrosshairsList(); InitMusicMenus(); - InitKeySections(); - FOptionValues **opt = OptionValues.CheckKey(NAME_Mididevices); if (opt != nullptr) { @@ -1573,319 +1157,9 @@ void M_CreateMenus() { I_BuildALResamplersList(*opt); } - opt = OptionValues.CheckKey(NAME_PlayerTeam); - if (opt != nullptr) - { - auto op = *opt; - op->mValues.Resize(Teams.Size() + 1); - op->mValues[0].Value = 0; - op->mValues[0].Text = "$OPTVAL_NONE"; - for (unsigned i = 0; i < Teams.Size(); i++) - { - op->mValues[i+1].Value = i+1; - op->mValues[i+1].Text = Teams[i].GetName(); - } - } - opt = OptionValues.CheckKey(NAME_PlayerClass); - if (opt != nullptr) - { - auto op = *opt; - int o = 0; - if (!gameinfo.norandomplayerclass && PlayerClasses.Size() > 1) - { - op->mValues.Resize(PlayerClasses.Size()+1); - op->mValues[0].Value = -1; - op->mValues[0].Text = "$MNU_RANDOM"; - o = 1; - } - else op->mValues.Resize(PlayerClasses.Size()); - for (unsigned i = 0; i < PlayerClasses.Size(); i++) - { - op->mValues[i+o].Value = i; - op->mValues[i+o].Text = GetPrintableDisplayName(PlayerClasses[i].Type); - } - } } -DEFINE_ACTION_FUNCTION(DMenu, UpdateColorsets) -{ - PARAM_PROLOGUE; - PARAM_POINTER(playerClass, FPlayerClass); - - TArray PlayerColorSets; - - EnumColorSets(playerClass->Type, &PlayerColorSets); - - auto opt = OptionValues.CheckKey(NAME_PlayerColors); - if (opt != nullptr) - { - auto op = *opt; - op->mValues.Resize(PlayerColorSets.Size() + 1); - op->mValues[0].Value = -1; - op->mValues[0].Text = "$OPTVAL_CUSTOM"; - for (unsigned i = 0; i < PlayerColorSets.Size(); i++) - { - auto cset = GetColorSet(playerClass->Type, PlayerColorSets[i]); - op->mValues[i + 1].Value = PlayerColorSets[i]; - op->mValues[i + 1].Text = cset? cset->Name.GetChars() : "?"; // The null case should never happen here. - } - } - return 0; -} - -DEFINE_ACTION_FUNCTION(DMenu, UpdateSkinOptions) -{ - PARAM_PROLOGUE; - PARAM_POINTER(playerClass, FPlayerClass); - - auto opt = OptionValues.CheckKey(NAME_PlayerSkin); - if (opt != nullptr) - { - auto op = *opt; - - if ((GetDefaultByType(playerClass->Type)->flags4 & MF4_NOSKIN) || players[consoleplayer].userinfo.GetPlayerClassNum() == -1) - { - op->mValues.Resize(1); - op->mValues[0].Value = -1; - op->mValues[0].Text = "$OPTVAL_DEFAULT"; - } - else - { - op->mValues.Clear(); - for (unsigned i = 0; i < Skins.Size(); i++) - { - op->mValues.Reserve(1); - op->mValues.Last().Value = i; - op->mValues.Last().Text = Skins[i].Name; - } - } - } - return 0; -} - -//============================================================================= -// -// The skill menu must be refeshed each time it starts up -// -//============================================================================= -extern int restart; - -void M_StartupSkillMenu(FNewGameStartup *gs) -{ - static int done = -1; - bool success = false; - TArray MenuSkills; - TArray SkillIndices; - if (MenuSkills.Size() == 0) - { - for (unsigned ind = 0; ind < AllSkills.Size(); ind++) - { - if (!AllSkills[ind].NoMenu) - { - MenuSkills.Push(&AllSkills[ind]); - SkillIndices.Push(ind); - } - } - } - if (MenuSkills.Size() == 0) I_Error("No valid skills for menu found. At least one must be defined."); - - int defskill = DefaultSkill; - if ((unsigned int)defskill >= MenuSkills.Size()) - { - defskill = SkillIndices[(MenuSkills.Size() - 1) / 2]; - } - if (AllSkills[defskill].NoMenu) - { - for (defskill = 0; defskill < (int)AllSkills.Size(); defskill++) - { - if (!AllSkills[defskill].NoMenu) break; - } - } - int defindex = 0; - for (unsigned i = 0; i < MenuSkills.Size(); i++) - { - if (MenuSkills[i] == &AllSkills[defskill]) - { - defindex = i; - break; - } - } - - DMenuDescriptor **desc = MenuDescriptors.CheckKey(NAME_Skillmenu); - if (desc != nullptr) - { - if ((*desc)->IsKindOf(RUNTIME_CLASS(DListMenuDescriptor))) - { - DListMenuDescriptor *ld = static_cast(*desc); - int posx = (int)ld->mXpos; - int y = (int)ld->mYpos; - - // Delete previous contents - for(unsigned i=0; imItems.Size(); i++) - { - FName n = ld->mItems[i]->mAction; - if (n == NAME_Startgame || n == NAME_StartgameConfirm) - { - ld->mItems.Resize(i); - break; - } - } - - if (done != restart) - { - done = restart; - ld->mSelectedItem = ld->mItems.Size() + defindex; - - int posy = y; - int topy = posy; - - // Get lowest y coordinate of any static item in the menu - for(unsigned i = 0; i < ld->mItems.Size(); i++) - { - int y = (int)ld->mItems[i]->GetY(); - if (y < topy) topy = y; - } - - // center the menu on the screen if the top space is larger than the bottom space - int totalheight = posy + MenuSkills.Size() * ld->mLinespacing - topy; - - if (totalheight < 190 || MenuSkills.Size() == 1) - { - int newtop = (200 - totalheight) / 2; - int topdelta = newtop - topy; - if (topdelta < 0) - { - for(unsigned i = 0; i < ld->mItems.Size(); i++) - { - ld->mItems[i]->OffsetPositionY(topdelta); - } - ld->mYpos = y = posy + topdelta; - } - } - else - { - // too large - desc = nullptr; - done = false; - goto fail; - } - } - - for (unsigned int i = 0; i < MenuSkills.Size(); i++) - { - FSkillInfo &skill = *MenuSkills[i]; - DMenuItemBase *li = nullptr; - - FString *pItemText = nullptr; - if (gs->PlayerClass != nullptr) - { - pItemText = skill.MenuNamesForPlayerClass.CheckKey(gs->PlayerClass); - } - - if (skill.PicName.Len() != 0 && pItemText == nullptr) - { - FTextureID tex = GetMenuTexture(skill.PicName); - if (skill.MenuName.IsEmpty() || OkForLocalization(tex, skill.MenuName)) - continue; - } - const char *c = pItemText ? pItemText->GetChars() : skill.MenuName.GetChars(); - if (*c == '$') c = GStrings(c + 1); - int textwidth = ld->mFont->StringWidth(c); - int textright = posx + textwidth; - if (posx + textright > 320) posx = std::max(0, 320 - textright); - } - - unsigned firstitem = ld->mItems.Size(); - for(unsigned int i = 0; i < MenuSkills.Size(); i++) - { - FSkillInfo &skill = *MenuSkills[i]; - DMenuItemBase *li = nullptr; - // Using a different name for skills that must be confirmed makes handling this easier. - FName action = (skill.MustConfirm && !AllEpisodes[gs->Episode].mNoSkill) ? - NAME_StartgameConfirm : NAME_Startgame; - FString *pItemText = nullptr; - if (gs->PlayerClass != nullptr) - { - pItemText = skill.MenuNamesForPlayerClass.CheckKey(gs->PlayerClass); - } - - EColorRange color = (EColorRange)skill.GetTextColor(); - if (color == CR_UNTRANSLATED) color = ld->mFontColor; - if (skill.PicName.Len() != 0 && pItemText == nullptr) - { - FTextureID tex = GetMenuTexture(skill.PicName); - if (skill.MenuName.IsEmpty() || OkForLocalization(tex, skill.MenuName)) - li = CreateListMenuItemPatch(posx, y, ld->mLinespacing, skill.Shortcut, tex, action, SkillIndices[i]); - } - if (li == nullptr) - { - li = CreateListMenuItemText(posx, y, ld->mLinespacing, skill.Shortcut, - pItemText? *pItemText : skill.MenuName, ld->mFont, color,ld->mFontColor2, action, SkillIndices[i]); - } - ld->mItems.Push(li); - GC::WriteBarrier(*desc, li); - y += ld->mLinespacing; - } - if (AllEpisodes[gs->Episode].mNoSkill || MenuSkills.Size() == 1) - { - ld->mAutoselect = firstitem + defindex; - } - else - { - ld->mAutoselect = -1; - } - success = true; - } - } - if (success) return; -fail: - // Option menu fallback for overlong skill lists - DOptionMenuDescriptor *od; - if (desc == nullptr) - { - od = Create(); - MenuDescriptors[NAME_Skillmenu] = od; - od->mMenuName = NAME_Skillmenu; - od->mFont = gameinfo.gametype == GAME_Doom ? BigUpper : BigFont; - od->mTitle = "$MNU_CHOOSESKILL"; - od->mSelectedItem = defindex; - od->mScrollPos = 0; - od->mClass = nullptr; - od->mPosition = -15; - od->mScrollTop = 0; - od->mIndent = 160; - od->mDontDim = false; - GC::WriteBarrier(od); - } - else - { - od = static_cast(*desc); - od->mItems.Clear(); - } - for(unsigned int i = 0; i < MenuSkills.Size(); i++) - { - FSkillInfo &skill = *MenuSkills[i]; - DMenuItemBase *li; - // Using a different name for skills that must be confirmed makes handling this easier. - const char *action = (skill.MustConfirm && !AllEpisodes[gs->Episode].mNoSkill) ? - "StartgameConfirm" : "Startgame"; - - FString *pItemText = nullptr; - if (gs->PlayerClass != nullptr) - { - pItemText = skill.MenuNamesForPlayerClass.CheckKey(gs->PlayerClass); - } - li = CreateOptionMenuItemSubmenu(pItemText? *pItemText : skill.MenuName, action, SkillIndices[i]); - od->mItems.Push(li); - GC::WriteBarrier(od, li); - if (!done) - { - done = true; - od->mSelectedItem = defindex; - } - } -} #ifdef _WIN32 diff --git a/src/p_conversation.cpp b/src/p_conversation.cpp index 19d9a81cc..9b662a46e 100644 --- a/src/p_conversation.cpp +++ b/src/p_conversation.cpp @@ -52,7 +52,7 @@ #include "sbar.h" #include "p_lnspec.h" #include "p_local.h" -#include "menu/menu.h" +#include "menu.h" #include "g_levellocals.h" #include "vm.h" #include "v_video.h" diff --git a/src/rendering/swrenderer/drawers/r_draw_rgba.cpp b/src/rendering/swrenderer/drawers/r_draw_rgba.cpp index 07d808360..09fdd7326 100644 --- a/src/rendering/swrenderer/drawers/r_draw_rgba.cpp +++ b/src/rendering/swrenderer/drawers/r_draw_rgba.cpp @@ -66,6 +66,7 @@ #include "x86.h" #include +; // Use linear filtering when scaling up CVAR(Bool, r_magfilter, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG); diff --git a/src/scripting/thingdef_data.cpp b/src/scripting/thingdef_data.cpp index f5b0e93c1..80470c211 100644 --- a/src/scripting/thingdef_data.cpp +++ b/src/scripting/thingdef_data.cpp @@ -49,7 +49,7 @@ #include "p_checkposition.h" #include "p_linetracedata.h" #include "v_font.h" -#include "menu/menu.h" +#include "menu.h" #include "teaminfo.h" #include "r_data/sprites.h" #include "serializer_doom.h" diff --git a/src/scripting/vmthunks.cpp b/src/scripting/vmthunks.cpp index 00d97079b..9af53b2b4 100644 --- a/src/scripting/vmthunks.cpp +++ b/src/scripting/vmthunks.cpp @@ -52,7 +52,7 @@ #include "utf8.h" #include "fontinternals.h" #include "intermission/intermission.h" -#include "menu/menu.h" +#include "menu.h" #include "c_cvars.h" #include "c_bind.h" #include "c_dispatch.h" diff --git a/src/sound/s_reverbedit.cpp b/src/sound/s_reverbedit.cpp index f6f34e8d5..1b0154b37 100644 --- a/src/sound/s_reverbedit.cpp +++ b/src/sound/s_reverbedit.cpp @@ -46,7 +46,7 @@ #include "c_dispatch.h" #include "vm.h" #include "dobject.h" -#include "menu/menu.h" +#include "menu.h" void S_ReadReverbDef (FScanner &sc);