raze/source/common/menu/menu.cpp
Christoph Oelckers 0e19d4262e - savegame and mapinfo cleanup in DN3D and RR frontends.
Also added an "end game" flag to the mapinfo. For those who like to assemble single levels into custom episodes. More features for that to come. ;)
2019-12-10 22:22:59 +01:00

1046 lines
25 KiB
C++

/*
** menu.cpp
** Menu base class and global interface
**
**---------------------------------------------------------------------------
** Copyright 2010 Christoph Oelckers
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
#include "c_dispatch.h"
#include "d_gui.h"
#include "c_console.h"
#include "c_bind.h"
#include "c_cvars.h"
#include "d_event.h"
//#include "i_input.h"
#include "gameconfigfile.h"
#include "gstrings.h"
#include "menu.h"
#include "textures.h"
#include "c_buttons.h"
#include "v_2ddrawer.h"
#include "printf.h"
#include "v_draw.h"
#include "gamecontrol.h"
#include "fx_man.h"
#include "pragmas.h"
#include "build.h"
#include "baselayer.h"
#include "statistics.h"
void RegisterDukeMenus();
void RegisterRedneckMenus();
void RegisterBloodMenus();
void RegisterSWMenus();
void RegisterLoadsaveMenus();
void RegisterOptionMenus();
extern bool rotatesprite_2doverride;
bool help_disabled, credits_disabled;
int g_currentMenu; // accessible by CON scripts - contains the current menu's script ID if defined or INT_MAX if none given.
int DrawBackground;
TArray<DMenu*> toDelete;
//
// Todo: Move these elsewhere
//
CVAR (Float, mouse_sensitivity, 1.f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
EXTERN_CVAR (Bool, show_messages)
CVAR(Bool, menu_sounds, true, CVAR_ARCHIVE) // added mainly because RR's sounds are so supremely annoying.
//CVAR (Float, snd_menuvolume, 0.6f, CVAR_ARCHIVE) the current sound engine cannot deal with this.
CVAR(Int, m_use_mouse, 1, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
CVAR(Int, m_show_backbutton, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
TArray<MenuClassDescriptor*> menuClasses(TArray<MenuClassDescriptor*>::ENoInit(0));
DMenu *DMenu::CurrentMenu;
int DMenu::MenuTime;
FGameStartup GameStartupInfo;
EMenuState menuactive;
bool M_DemoNoPlay;
FButtonStatus MenuButtons[NUM_MKEYS];
int MenuButtonTickers[NUM_MKEYS];
bool MenuButtonOrigin[NUM_MKEYS];
int BackbuttonTime;
float BackbuttonAlpha;
static bool MenuEnabled = true;
#define KEY_REPEAT_DELAY (MENU_TICRATE*5/12)
#define KEY_REPEAT_RATE (3)
static MenuTransition transition;
bool MenuTransition::StartTransition(DMenu *from, DMenu *to, MenuTransitionType animtype)
{
if (!from->canAnimate || !to->canAnimate || animtype == MA_None)
{
return false;
}
else
{
start = (int32_t) totalclock;
length = 30;
dir = animtype == MA_Advance? 1 : -1;
previous = from;
current = to;
return true;
}
}
bool MenuTransition::Draw()
{
if (totalclock < start + length)
{
double factor = 120 * xdim / ydim;
double phase = ((int32_t) totalclock - start) / double(length) * M_PI + M_PI/2;
previous->origin.X = factor * dir * (sin(phase) - 1.);
current->origin.X = factor * dir * (sin(phase) + 1.);
previous->Drawer();
current->Drawer();
return true;
}
return false;
}
//============================================================================
//
// DMenu base class
//
//============================================================================
DMenu::DMenu(DMenu *parent)
{
mParentMenu = parent;
mMouseCapture = false;
mBackbuttonSelected = false;
}
bool DMenu::Responder (event_t *ev)
{
bool res = false;
if (ev->type == EV_GUI_Event)
{
if (ev->subtype == EV_GUI_LButtonDown)
{
res = MouseEventBack(MOUSE_Click, ev->data1, ev->data2);
// make the menu's mouse handler believe that the current coordinate is outside the valid range
if (res) ev->data2 = -1;
res |= MouseEvent(MOUSE_Click, ev->data1, ev->data2);
if (res)
{
SetCapture();
}
}
else if (ev->subtype == EV_GUI_MouseMove)
{
BackbuttonTime = BACKBUTTON_TIME;
if (mMouseCapture || m_use_mouse == 1)
{
res = MouseEventBack(MOUSE_Move, ev->data1, ev->data2);
if (res) ev->data2 = -1;
res |= MouseEvent(MOUSE_Move, ev->data1, ev->data2);
}
}
else if (ev->subtype == EV_GUI_LButtonUp)
{
if (mMouseCapture)
{
ReleaseCapture();
res = MouseEventBack(MOUSE_Release, ev->data1, ev->data2);
if (res) ev->data2 = -1;
res |= MouseEvent(MOUSE_Release, ev->data1, ev->data2);
}
}
}
return false;
}
//=============================================================================
//
//
//
//=============================================================================
bool DMenu::MenuEvent (int mkey, bool fromcontroller)
{
switch (mkey)
{
case MKEY_Back:
{
if (scriptID != 0)
{
M_MenuSound(DMenu::CurrentMenu->mParentMenu? BackSound : CloseSound);
Close();
return true;
}
}
}
return false;
}
//=============================================================================
//
//
//
//=============================================================================
void DMenu::Close ()
{
assert(DMenu::CurrentMenu == this);
DMenu::CurrentMenu = mParentMenu;
if (mParentMenu && transition.StartTransition(this, mParentMenu, MA_Return))
{
g_currentMenu = DMenu::CurrentMenu->scriptID;
}
else
{
Destroy();
toDelete.Push(this);
if (DMenu::CurrentMenu == NULL)
{
M_ClearMenus();
}
else
{
g_currentMenu = DMenu::CurrentMenu->scriptID;
}
}
}
//=============================================================================
//
//
//
//=============================================================================
bool DMenu::MouseEvent(int type, int x, int y)
{
return true;
}
//=============================================================================
//
//
//
//=============================================================================
bool DMenu::MouseEventBack(int type, int x, int y)
{
if (m_show_backbutton >= 0)
{
FTexture* tex = TileFiles.GetTexture("demolition/graphics/m_back.png");
if (tex != NULL)
{
if (m_show_backbutton&1) x -= screen->GetWidth() - tex->GetWidth() * CleanXfac;
if (m_show_backbutton&2) y -= screen->GetHeight() - tex->GetHeight() * CleanYfac;
mBackbuttonSelected = ( x >= 0 && x < tex->GetWidth() * CleanXfac &&
y >= 0 && y < tex->GetHeight() * CleanYfac);
if (mBackbuttonSelected && type == MOUSE_Release)
{
if (m_use_mouse == 2) mBackbuttonSelected = false;
MenuEvent(MKEY_Back, true);
}
return mBackbuttonSelected;
}
}
return false;
}
//=============================================================================
//
//
//
//=============================================================================
void DMenu::SetCapture()
{
if (!mMouseCapture)
{
mMouseCapture = true;
I_SetMouseCapture();
}
}
void DMenu::ReleaseCapture()
{
if (mMouseCapture)
{
mMouseCapture = false;
I_ReleaseMouseCapture();
}
}
//=============================================================================
//
//
//
//=============================================================================
void DMenu::Ticker ()
{
}
void DMenu::Drawer ()
{
if (this == DMenu::CurrentMenu && BackbuttonAlpha > 0 && m_show_backbutton >= 0 && m_use_mouse)
{
FTexture* tex = TileFiles.GetTexture("demolition/graphics/m_back.png");
int w = tex->GetWidth() * CleanXfac;
int h = tex->GetHeight() * CleanYfac;
int x = (!(m_show_backbutton&1))? 0:screen->GetWidth() - w;
int y = (!(m_show_backbutton&2))? 0:screen->GetHeight() - h;
if (mBackbuttonSelected && (mMouseCapture || m_use_mouse == 1))
{
DrawTexture(&twod, tex, x, y, DTA_CleanNoMove, true, DTA_ColorOverlay, MAKEARGB(40, 255,255,255), TAG_DONE);
}
else
{
DrawTexture(&twod, tex, x, y, DTA_CleanNoMove, true, DTA_Alpha, BackbuttonAlpha, TAG_DONE);
}
}
}
bool DMenu::DimAllowed()
{
return true;
}
bool DMenu::TranslateKeyboardEvents()
{
return true;
}
//=============================================================================
//
//
//
//=============================================================================
void M_StartControlPanel (bool makeSound)
{
static bool created = false;
// intro might call this repeatedly
if (DMenu::CurrentMenu != NULL)
return;
if (!created) // Cannot do this earlier.
{
created = true;
M_CreateMenus();
}
FX_StopAllSounds();
gi->MenuOpened();
if (makeSound) gi->MenuSound(ActivateSound);
buttonMap.ResetButtonStates ();
inputState.ClearAllKeyStatus();
for (int i = 0; i < NUM_MKEYS; ++i)
{
MenuButtons[i].ReleaseKey(0);
}
C_HideConsole (); // [RH] Make sure console goes bye bye.
mouseGrabInput(false);
menuactive = MENU_On;
// Pause sound effects before we play the menu switch sound.
// That way, it won't be paused.
//P_CheckTickerPaused ();
BackbuttonTime = 0;
BackbuttonAlpha = 0;
DrawBackground = -1;
DMenu::MenuTime = -1;
M_Ticker(); // This needs to be called once here to make sure that the menu actually has ticked before it gets drawn for the first time.
}
void Menu_Open(int playerid)
{
M_StartControlPanel(DMenu::CurrentMenu == nullptr);
}
//=============================================================================
//
//
//
//=============================================================================
void M_ActivateMenu(DMenu *menu)
{
g_currentMenu = menu->scriptID;
if (menuactive == MENU_Off) menuactive = MENU_On;
if (DMenu::CurrentMenu != NULL)
{
DMenu::CurrentMenu->ReleaseCapture();
transition.StartTransition(DMenu::CurrentMenu, menu, MA_Advance);
}
DMenu::CurrentMenu = menu;
}
//=============================================================================
//
//
//
//=============================================================================
bool M_SetMenu(FName menu, int param, FName caller)
{
if (DrawBackground == -1)
{
if (menu == NAME_MainMenu) DrawBackground = 1;
else DrawBackground = 0;
}
// some menus need some special treatment (needs to be adjusted for the various frontends.
switch (caller)
{
case NAME_EpisodeMenu:
// sent from the episode menu
GameStartupInfo.Episode = param;
GameStartupInfo.Level = 0;
GameStartupInfo.CustomLevel1 = GameStartupInfo.CustomLevel2 = -1;
GameStartupInfo.Skill = gDefaultSkill;
break;
case NAME_CustomGameMenu:
GameStartupInfo.CustomLevel1 = param;
GameStartupInfo.CustomLevel2 = -1;
GameStartupInfo.Episode = 0; // Set start to E1L1 so that even if the script fails to set the starting level it is set to something valid.
GameStartupInfo.Level = 0;
GameStartupInfo.Skill = gDefaultSkill;
gi->CustomMenuSelection(param, -1);
break;
case NAME_CustomSubMenu1:
case NAME_CustomSubMenu2:
case NAME_CustomSubMenu3:
case NAME_CustomSubMenu4:
case NAME_CustomSubMenu5:
case NAME_CustomSubMenu6:
case NAME_CustomSubMenu7:
GameStartupInfo.CustomLevel2 = param;
gi->CustomMenuSelection(GameStartupInfo.CustomLevel1, param);
break;
case NAME_SkillMenu:
GameStartupInfo.Skill = param;
break;
}
switch (menu)
{
case NAME_StartGame:
M_ClearMenus(); // must be done before starting the level.
STAT_StartNewGame(gVolumeNames[GameStartupInfo.Episode], GameStartupInfo.Skill);
gi->StartGame(GameStartupInfo);
return false;
case NAME_CustomSubMenu1:
menu = ENamedName(menu + param);
break;
#if 0
case NAME_StartgameConfirm:
{
// sent from the skill menu for a skill that needs to be confirmed
GameStartupInfo.Skill = param;
const char *msg = AllSkills[param].MustConfirmText;
if (*msg==0) msg = GStrings("NIGHTMARE");
M_StartMessage (msg, 0, -1, NAME_StartgameConfirmed);
return;
}
#endif
case NAME_SaveGameMenu:
if (!gi->CanSave())
{
// cannot save outside the game.
M_StartMessage (GStrings("SAVEDEAD"), 1, -1);
return true;
}
break;
case NAME_QuitMenu:
// This is no separate class
C_DoCommand("menu_quit");
return true;
case NAME_EndgameMenu:
// This is no separate class
C_DoCommand("menu_endgame");
return true;
}
// End of special checks
FMenuDescriptor **desc = MenuDescriptors.CheckKey(menu);
if (desc != NULL)
{
/*
if ((*desc)->mNetgameMessage.IsNotEmpty() && netgame && !demoplayback)
{
M_StartMessage((*desc)->mNetgameMessage, 1);
return;
}
*/
if ((*desc)->mType == MDESC_ListMenu)
{
FListMenuDescriptor *ld = static_cast<FListMenuDescriptor*>(*desc);
if (ld->mAutoselect >= 0 && ld->mAutoselect < (int)ld->mItems.Size())
{
// recursively activate the autoselected item without ever creating this menu.
ld->mItems[ld->mAutoselect]->Activate(ld->mMenuName);
}
else
{
DListMenu* newmenu;
if (ld->mClass != NAME_None)
{
auto ndx = menuClasses.FindEx([=](const auto p) { return p->mName == ld->mClass; });
if (ndx == menuClasses.Size())
{
I_Error("Bad menu class %s\n", ld->mClass.GetChars());
}
else
{
newmenu = (DListMenu*)menuClasses[ndx]->CreateNew();
}
}
else
{
newmenu = new DListMenu;
}
newmenu->Init(DMenu::CurrentMenu, ld);
M_ActivateMenu(newmenu);
}
}
else if ((*desc)->mType == MDESC_OptionsMenu)
{
FOptionMenuDescriptor *ld = static_cast<FOptionMenuDescriptor*>(*desc);
DOptionMenu* newmenu;
if (ld->mClass != NAME_None)
{
auto ndx = menuClasses.FindEx([=](const auto p) { return p->mName == ld->mClass; });
if (ndx == menuClasses.Size())
{
I_Error("Bad menu class %s\n", ld->mClass.GetChars());
}
else
{
newmenu = (DOptionMenu*)menuClasses[ndx]->CreateNew();
}
}
else
{
newmenu = new DOptionMenu;
}
newmenu->Init(DMenu::CurrentMenu, ld);
M_ActivateMenu(newmenu);
}
else if ((*desc)->mType == MDESC_ImageScroller)
{
FImageScrollerDescriptor* ld = static_cast<FImageScrollerDescriptor*>(*desc);
DImageScrollerMenu* newmenu;
if (ld->mClass != NAME_None)
{
auto ndx = menuClasses.FindEx([=](const auto p) { return p->mName == ld->mClass; });
if (ndx == menuClasses.Size())
{
I_Error("Bad menu class %s\n", ld->mClass.GetChars());
}
else
{
newmenu = (DImageScrollerMenu*)menuClasses[ndx]->CreateNew();
}
}
else
{
newmenu = new DImageScrollerMenu;
}
newmenu->Init(DMenu::CurrentMenu, ld);
M_ActivateMenu(newmenu);
}
return true;
}
else
{
/*
const PClass *menuclass = PClass::FindClass(menu);
if (menuclass != NULL)
{
if (menuclass->IsDescendantOf(RUNTIME_CLASS(DMenu)))
{
DMenu *newmenu = (DMenu*)menuclass->CreateNew();
newmenu->mParentMenu = DMenu::CurrentMenu;
M_ActivateMenu(newmenu);
return true;
}
}
*/
}
Printf("Attempting to open menu of unknown type '%s'\n", menu.GetChars());
M_ClearMenus();
return false;
}
//=============================================================================
//
//
//
//=============================================================================
bool M_DoResponder (event_t *ev)
{
int ch = 0;
bool keyup = false;
int mkey = NUM_MKEYS;
bool fromcontroller = true;
/*
if (chatmodeon)
{
return false;
}
*/
if (DMenu::CurrentMenu != NULL && menuactive != MENU_Off)
{
// There are a few input sources we are interested in:
//
// EV_KeyDown / EV_KeyUp : joysticks/gamepads/controllers
// EV_GUI_KeyDown / EV_GUI_KeyUp : the keyboard
// EV_GUI_Char : printable characters, which we want in string input mode
//
// This code previously listened for EV_GUI_KeyRepeat to handle repeating
// in the menus, but that doesn't work with gamepads, so now we combine
// the multiple inputs into buttons and handle the repetition manually.
if (ev->type == EV_GUI_Event)
{
fromcontroller = false;
if (ev->subtype == EV_GUI_KeyRepeat)
{
// We do our own key repeat handling but still want to eat the
// OS's repeated keys.
return true;
}
else if (ev->subtype == EV_GUI_BackButtonDown || ev->subtype == EV_GUI_BackButtonUp)
{
mkey = MKEY_Back;
keyup = ev->subtype == EV_GUI_BackButtonUp;
}
else if (ev->subtype != EV_GUI_KeyDown && ev->subtype != EV_GUI_KeyUp)
{
// do we want mouse input?
if (ev->subtype >= EV_GUI_FirstMouseEvent && ev->subtype <= EV_GUI_LastMouseEvent)
{
if (!m_use_mouse)
return true;
}
// pass everything else on to the current menu
return DMenu::CurrentMenu->Responder(ev);
}
else if (DMenu::CurrentMenu->TranslateKeyboardEvents())
{
ch = ev->data1;
keyup = ev->subtype == EV_GUI_KeyUp;
switch (ch)
{
case GK_BACK: mkey = MKEY_Back; break;
case GK_ESCAPE: mkey = MKEY_Back; break;
case GK_RETURN: mkey = MKEY_Enter; break;
case GK_UP: mkey = MKEY_Up; break;
case GK_DOWN: mkey = MKEY_Down; break;
case GK_LEFT: mkey = MKEY_Left; break;
case GK_RIGHT: mkey = MKEY_Right; break;
case GK_BACKSPACE: mkey = MKEY_Clear; break;
case GK_PGUP: mkey = MKEY_PageUp; break;
case GK_PGDN: mkey = MKEY_PageDown; break;
default:
if (!keyup)
{
return DMenu::CurrentMenu->Responder(ev);
}
break;
}
}
}
else if (menuactive != MENU_WaitKey && (ev->type == EV_KeyDown || ev->type == EV_KeyUp))
{
keyup = ev->type == EV_KeyUp;
ch = ev->data1;
switch (ch)
{
case KEY_JOY1:
case KEY_PAD_A:
mkey = MKEY_Enter;
break;
case KEY_JOY2:
case KEY_PAD_B:
mkey = MKEY_Back;
break;
case KEY_JOY3:
case KEY_PAD_X:
mkey = MKEY_Clear;
break;
case KEY_JOY5:
case KEY_PAD_LSHOULDER:
mkey = MKEY_PageUp;
break;
case KEY_JOY6:
case KEY_PAD_RSHOULDER:
mkey = MKEY_PageDown;
break;
case KEY_PAD_DPAD_UP:
case KEY_PAD_LTHUMB_UP:
case KEY_JOYAXIS1MINUS:
case KEY_JOYPOV1_UP:
mkey = MKEY_Up;
break;
case KEY_PAD_DPAD_DOWN:
case KEY_PAD_LTHUMB_DOWN:
case KEY_JOYAXIS1PLUS:
case KEY_JOYPOV1_DOWN:
mkey = MKEY_Down;
break;
case KEY_PAD_DPAD_LEFT:
case KEY_PAD_LTHUMB_LEFT:
case KEY_JOYAXIS2MINUS:
case KEY_JOYPOV1_LEFT:
mkey = MKEY_Left;
break;
case KEY_PAD_DPAD_RIGHT:
case KEY_PAD_LTHUMB_RIGHT:
case KEY_JOYAXIS2PLUS:
case KEY_JOYPOV1_RIGHT:
mkey = MKEY_Right;
break;
}
}
if (mkey != NUM_MKEYS)
{
if (keyup)
{
MenuButtons[mkey].ReleaseKey(ch);
return false;
}
else
{
MenuButtons[mkey].PressKey(ch);
MenuButtonOrigin[mkey] = fromcontroller;
if (mkey <= MKEY_PageDown)
{
MenuButtonTickers[mkey] = KEY_REPEAT_DELAY;
}
DMenu::CurrentMenu->MenuEvent(mkey, fromcontroller);
return true;
}
}
return DMenu::CurrentMenu->Responder(ev) || !keyup;
}
else if (MenuEnabled)
{
if (ev->type == EV_KeyDown)
{
// Pop-up menu?
if (ev->data1 == KEY_ESCAPE) // Should we let the games handle Escape for special actions, like backing out of cameras?
{
M_StartControlPanel(true);
M_SetMenu(NAME_IngameMenu, -1);
return true;
}
return false;
}
else if (ev->type == EV_GUI_Event && ev->subtype == EV_GUI_LButtonDown &&
ConsoleState != c_down && m_use_mouse)
{
M_StartControlPanel(true);
M_SetMenu(NAME_MainMenu, -1);
return true;
}
}
return false;
}
bool M_Responder(event_t* ev)
{
// delayed deletion, so that self-deleting menus don't crash if they are getting accessed after being closed.
auto res = M_DoResponder(ev);
for (auto p : toDelete) delete p;
toDelete.Clear();
return res;
}
//=============================================================================
//
//
//
//=============================================================================
void M_Ticker (void)
{
DMenu::MenuTime++;
if (DMenu::MenuTime & 3) return;
if (DMenu::CurrentMenu != NULL && menuactive != MENU_Off)
{
D_ProcessEvents(); // The main loop is blocked when the menu is open and cannot dispatch the events.
if (transition.previous) transition.previous->Ticker();
if (DMenu::CurrentMenu == nullptr) return; // In case one of the sub-screens has closed the menu.
DMenu::CurrentMenu->Ticker();
for (int i = 0; i < NUM_MKEYS; ++i)
{
if (MenuButtons[i].bDown)
{
if (MenuButtonTickers[i] > 0 && --MenuButtonTickers[i] <= 0)
{
MenuButtonTickers[i] = KEY_REPEAT_RATE;
DMenu::CurrentMenu->MenuEvent(i, MenuButtonOrigin[i]);
}
}
}
if (BackbuttonTime > 0)
{
if (BackbuttonAlpha < 1.0f) BackbuttonAlpha += 0.1f;
BackbuttonTime--;
}
else
{
if (BackbuttonAlpha > 0) BackbuttonAlpha -= 0.1f;
if (BackbuttonAlpha < 0) BackbuttonAlpha = 0;
}
}
}
//=============================================================================
//
//
//
//=============================================================================
void M_Drawer (void)
{
rotatesprite_2doverride = true;
PalEntry fade = 0x70000000;
if (DMenu::CurrentMenu != NULL && menuactive != MENU_Off)
{
if (DMenu::CurrentMenu->DimAllowed() && fade && !DrawBackground) twod.AddColorOnlyQuad(0, 0, screen->GetWidth(), screen->GetHeight(), fade);
bool going = false;
if (transition.previous)
{
going = transition.Draw();
if (!going)
{
if (transition.dir == -1) delete transition.previous;
transition.previous = nullptr;
transition.current = nullptr;
}
}
if (!going)
{
DMenu::CurrentMenu->origin = { 0,0 };
// else if (DrawBackground) Menu_DrawBackground(origin);
DMenu::CurrentMenu->Drawer();
}
}
rotatesprite_2doverride = false;
}
//=============================================================================
//
//
//
//=============================================================================
void M_ClearMenus ()
{
M_DemoNoPlay = false;
transition.previous = transition.current = nullptr;
transition.dir = 0;
auto menu = DMenu::CurrentMenu;
while (menu != nullptr)
{
auto nextm = menu->mParentMenu;
menu->Destroy();
delete menu;
menu = nextm;
}
DMenu::CurrentMenu = nullptr;
menuactive = MENU_Off;
mouseGrabInput(true);
gi->MenuClosed();
}
void Menu_Close(int playerid)
{
M_ClearMenus();
}
bool M_Active()
{
return DMenu::CurrentMenu != nullptr || ConsoleState == c_down || ConsoleState == c_falling;
}
//=============================================================================
//
//
//
//=============================================================================
void M_MenuSound(EMenuSounds snd)
{
if (menu_sounds) gi->MenuSound(snd);
}
//=============================================================================
//
//
//
//=============================================================================
void M_Init (void)
{
RegisterDukeMenus();
RegisterRedneckMenus();
RegisterBloodMenus();
RegisterSWMenus();
RegisterLoadsaveMenus();
RegisterOptionMenus();
timerSetCallback(M_Ticker);
M_ParseMenuDefs();
}
//=============================================================================
//
//
//
//=============================================================================
void M_EnableMenu (bool on)
{
MenuEnabled = on;
}
//=============================================================================
//
// [RH] Most menus can now be accessed directly
// through console commands.
//
//=============================================================================
CCMD (openmenu)
{
if (argv.argc() < 2)
{
Printf("Usage: openmenu \"menu_name\"");
return;
}
M_StartControlPanel (true);
M_SetMenu(argv[1], -1);
}
CCMD (closemenu)
{
M_ClearMenus();
}
EXTERN_CVAR (Int, screenblocks)
CCMD(menuconsole)
{
M_ClearMenus();
C_ToggleConsole();
}
CCMD(reset2defaults)
{
C_SetDefaultBindings ();
C_SetCVarsToDefaults ();
}
CCMD(reset2saved)
{
GameConfig->DoGlobalSetup ();
GameConfig->DoGameSetup (currentGame);
}
CCMD(openmainmenu)
{
//gi->ClearSoundLocks();
M_StartControlPanel(true);
M_SetMenu(NAME_IngameMenu);
}
CCMD(openhelpmenu)
{
if (!help_disabled)
{
M_StartControlPanel(true);
M_SetMenu(NAME_HelpMenu);
}
}
CCMD(opensavemenu)
{
if (gi->CanSave())
{
M_StartControlPanel(true);
M_SetMenu(NAME_SaveGameMenu);
}
}
CCMD(openloadmenu)
{
M_StartControlPanel(true);
M_SetMenu(NAME_LoadGameMenu);
}