2020-10-04 16:31:48 +00:00
/*
* * 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_buttons.h"
# include "c_console.h"
# include "c_bind.h"
# include "d_eventbase.h"
# include "g_input.h"
# include "configfile.h"
# include "gstrings.h"
# include "menu.h"
# include "vm.h"
# include "v_video.h"
# include "i_system.h"
# include "types.h"
# include "texturemanager.h"
# include "v_draw.h"
# include "vm.h"
# include "gamestate.h"
# include "i_interface.h"
# include "d_event.h"
# include "st_start.h"
# include "i_system.h"
# include "gameconfigfile.h"
2020-10-04 20:21:11 +00:00
# include "gamecontrol.h"
2020-10-06 21:49:34 +00:00
# include "raze_sound.h"
# include "gamestruct.h"
2020-10-06 23:00:43 +00:00
# include "razemenu.h"
2020-10-06 23:31:41 +00:00
# include "mapinfo.h"
# include "statistics.h"
2020-10-07 16:32:57 +00:00
# include "i_net.h"
# include "savegamehelp.h"
2020-10-09 21:35:38 +00:00
# include "gi.h"
2021-05-04 09:32:09 +00:00
# include "raze_music.h"
2020-10-04 16:31:48 +00:00
EXTERN_CVAR ( Int , cl_gfxlocalization )
EXTERN_CVAR ( Bool , m_quickexit )
EXTERN_CVAR ( Bool , saveloadconfirmation ) // [mxd]
EXTERN_CVAR ( Bool , quicksaverotation )
EXTERN_CVAR ( Bool , show_messages )
2020-10-06 21:49:34 +00:00
CVAR ( Bool , menu_sounds , true , CVAR_ARCHIVE ) // added mainly because RR's sounds are so supremely annoying.
2020-10-04 16:31:48 +00:00
typedef void ( * hfunc ) ( ) ;
DMenu * CreateMessageBoxMenu ( DMenu * parent , const char * message , int messagemode , bool playsound , FName action = NAME_None , hfunc handler = nullptr ) ;
bool OkForLocalization ( FTextureID texnum , const char * substitute ) ;
void D_ToggleHud ( ) ;
void I_WaitVBL ( int count ) ;
extern bool hud_toggled ;
2020-10-06 23:12:57 +00:00
bool help_disabled ;
2020-10-06 23:31:41 +00:00
FNewGameStartup NewGameStartupInfo ;
2020-10-04 16:31:48 +00:00
2021-05-01 22:58:54 +00:00
2020-10-04 16:31:48 +00:00
//FNewGameStartup NewGameStartupInfo;
2021-05-01 22:58:54 +00:00
static bool DoStartGame ( FNewGameStartup & gs )
{
auto vol = FindVolume ( gs . Episode ) ;
if ( ! vol ) return false ;
if ( isShareware ( ) & & ( vol - > flags & VF_SHAREWARELOCK ) )
{
M_StartMessage ( GStrings ( " SHAREWARELOCK " ) , 1 , NAME_None ) ;
return false ;
}
auto map = FindMapByName ( vol - > startmap ) ;
if ( ! map ) return false ;
soundEngine - > StopAllChannels ( ) ;
gi - > StartGame ( gs ) ; // play game specific effects (like Duke/RR/SW's voice lines when starting a game.)
DeferedStartGame ( map , gs . Skill ) ;
return true ;
}
2020-10-04 16:31:48 +00:00
bool M_SetSpecialMenu ( FName & menu , int param )
{
2020-10-09 20:33:02 +00:00
// Engine credits need a different approach to work with the option search
#if 0
2020-10-06 23:31:41 +00:00
// Transitions between the engine credits pages need to pop off the last slide
if ( ! strnicmp ( menu . GetChars ( ) , " EngineCredits " , 13 ) & & CurrentMenu & & ! strnicmp ( CurrentMenu - > GetClass ( ) - > TypeName . GetChars ( ) , " EngineCredits " , 13 ) )
2020-10-04 16:31:48 +00:00
{
2020-10-06 23:31:41 +00:00
auto m = CurrentMenu ;
CurrentMenu = m - > mParentMenu ;
m - > mParentMenu = nullptr ;
m - > Destroy ( ) ;
}
2020-10-09 20:33:02 +00:00
# endif
2020-10-04 16:31:48 +00:00
2020-10-06 23:31:41 +00:00
switch ( menu . GetIndex ( ) )
{
2020-10-07 18:32:43 +00:00
case NAME_Mainmenu :
if ( gi - > CanSave ( ) ) menu = NAME_IngameMenu ;
break ;
2020-10-04 16:31:48 +00:00
case NAME_Skillmenu :
// sent from the episode menu
2020-10-06 23:31:41 +00:00
NewGameStartupInfo . Episode = param ;
NewGameStartupInfo . Level = 0 ;
NewGameStartupInfo . Skill = gDefaultSkill ;
return true ;
2020-10-04 16:31:48 +00:00
case NAME_Startgame :
2020-10-06 23:31:41 +00:00
case NAME_StartgameNoSkill :
NewGameStartupInfo . Skill = param ;
2021-05-01 22:35:56 +00:00
if ( menu = = NAME_StartgameNoSkill )
{
2021-05-01 22:58:54 +00:00
menu = NAME_Startgame ;
2021-05-01 22:35:56 +00:00
NewGameStartupInfo . Episode = param ;
NewGameStartupInfo . Skill = 1 ;
}
2021-05-01 22:58:54 +00:00
if ( DoStartGame ( NewGameStartupInfo ) )
2020-10-04 16:31:48 +00:00
{
2020-10-06 23:31:41 +00:00
M_ClearMenus ( ) ;
2021-05-01 22:35:56 +00:00
int ep = NewGameStartupInfo . Episode ;
auto vol = FindVolume ( ep ) ;
if ( vol ) STAT_StartNewGame ( vol - > name , NewGameStartupInfo . Skill ) ;
2020-10-06 23:31:41 +00:00
inputState . ClearAllInput ( ) ;
2020-10-04 16:31:48 +00:00
}
return false ;
2020-10-06 23:31:41 +00:00
case NAME_CustomSubMenu1 :
menu = ENamedName ( menu . GetIndex ( ) + param ) ;
break ;
2020-10-04 16:31:48 +00:00
case NAME_Savegamemenu :
2020-10-06 23:31:41 +00:00
if ( ! gi - > CanSave ( ) )
2020-10-04 16:31:48 +00:00
{
// cannot save outside the game.
2020-10-06 23:31:41 +00:00
M_StartMessage ( GStrings ( " SAVEDEAD " ) , 1 , NAME_None ) ;
return true ;
2020-10-04 16:31:48 +00:00
}
break ;
case NAME_Quitmenu :
2020-10-06 23:31:41 +00:00
// This is no separate class
2020-10-04 16:31:48 +00:00
C_DoCommand ( " menu_quit " ) ;
2020-10-07 16:32:57 +00:00
return false ;
2020-10-04 16:31:48 +00:00
case NAME_EndGameMenu :
2020-10-06 23:31:41 +00:00
// This is no separate class
C_DoCommand ( " menu_endgame " ) ;
2020-10-07 16:32:57 +00:00
return false ;
2020-10-06 23:31:41 +00:00
}
2020-10-04 16:31:48 +00:00
// End of special checks
return true ;
}
//=============================================================================
//
//
//
//=============================================================================
2020-10-06 21:49:34 +00:00
void M_StartControlPanel ( bool makeSound , bool )
{
2020-10-04 16:31:48 +00:00
// intro might call this repeatedly
2020-10-06 21:49:34 +00:00
if ( CurrentMenu ! = NULL )
2020-10-04 16:31:48 +00:00
return ;
2020-10-06 21:49:34 +00:00
GSnd - > SetSfxPaused ( true , PAUSESFX_MENU ) ;
gi - > MenuOpened ( ) ;
2020-10-06 23:00:43 +00:00
if ( makeSound & & menu_sounds ) gi - > MenuSound ( ActivateSound ) ;
2020-10-06 21:49:34 +00:00
M_DoStartControlPanel ( false ) ;
2020-10-04 16:31:48 +00:00
}
2020-10-08 16:02:25 +00:00
void System_MenuClosed ( )
{
GSnd - > SetSfxPaused ( false , PAUSESFX_MENU ) ;
inputState . ClearAllInput ( ) ;
gi - > MenuClosed ( ) ;
}
2020-10-04 16:31:48 +00:00
//==========================================================================
//
//
//
//==========================================================================
2020-10-10 16:29:15 +00:00
void System_MenuDim ( )
2020-10-04 16:31:48 +00:00
{
2020-10-10 16:29:15 +00:00
if ( gamestate ! = GS_MENUSCREEN ) // With GS_MENUSCREEN we can assume that the background has been tuned for proper menu display already.
2020-10-04 16:31:48 +00:00
{
2020-10-10 16:29:15 +00:00
Dim ( twod , 0 , 0.5f , 0 , 0 , screen - > GetWidth ( ) , screen - > GetHeight ( ) ) ;
2020-10-04 16:31:48 +00:00
}
}
//=============================================================================
//
//
//
//=============================================================================
2020-10-06 23:31:41 +00:00
CCMD ( menu_quit )
2020-10-04 16:31:48 +00:00
{ // F10
2020-10-06 23:31:41 +00:00
M_StartControlPanel ( true ) ;
2020-10-04 16:31:48 +00:00
FString EndString ;
2020-10-06 23:31:41 +00:00
EndString < < GStrings ( " CONFIRM_QUITMSG " ) < < " \n \n " < < GStrings ( " PRESSYN " ) ;
2020-10-04 16:31:48 +00:00
2020-10-06 23:31:41 +00:00
DMenu * newmenu = CreateMessageBoxMenu ( CurrentMenu , EndString , 0 , false , NAME_None , [ ] ( )
2020-10-04 16:31:48 +00:00
{
2020-10-12 06:07:51 +00:00
M_ClearMenus ( ) ;
2020-10-06 23:31:41 +00:00
gi - > ExitFromMenu ( ) ;
} ) ;
2020-10-04 16:31:48 +00:00
M_ActivateMenu ( newmenu ) ;
}
//=============================================================================
//
//
//
//=============================================================================
2020-10-06 23:31:41 +00:00
CCMD ( menu_endgame )
2020-10-04 16:31:48 +00:00
{ // F7
2020-10-06 23:31:41 +00:00
if ( ! gi - > CanSave ( ) )
2020-10-04 16:31:48 +00:00
{
return ;
}
2020-10-06 23:31:41 +00:00
M_StartControlPanel ( true ) ;
FString tempstring ;
tempstring < < GStrings ( " ENDGAME " ) < < " \n \n " < < GStrings ( " PRESSYN " ) ;
DMenu * newmenu = CreateMessageBoxMenu ( CurrentMenu , tempstring , 0 , false , NAME_None , [ ] ( )
{
STAT_Cancel ( ) ;
M_ClearMenus ( ) ;
2021-05-04 09:32:09 +00:00
Mus_Stop ( ) ;
gameaction = ga_mainmenu ;
2020-10-06 23:31:41 +00:00
} ) ;
M_ActivateMenu ( newmenu ) ;
2020-10-04 16:31:48 +00:00
}
//=============================================================================
//
//
//
//=============================================================================
2020-10-07 16:32:57 +00:00
//=============================================================================
//
//
//
//=============================================================================
CCMD ( quicksave )
{ // F6
if ( ! gi - > CanSave ( ) ) return ;
2020-10-04 16:31:48 +00:00
if ( savegameManager . quickSaveSlot = = NULL | | savegameManager . quickSaveSlot = = ( FSaveGameNode * ) 1 )
{
2020-10-07 16:32:57 +00:00
M_StartControlPanel ( true ) ;
2020-10-04 16:31:48 +00:00
M_SetMenu ( NAME_Savegamemenu ) ;
return ;
}
2020-10-07 16:32:57 +00:00
auto slot = savegameManager . quickSaveSlot ;
2020-10-04 16:31:48 +00:00
// [mxd]. Just save the game, no questions asked.
if ( ! saveloadconfirmation )
{
2021-05-11 22:21:26 +00:00
G_SaveGame ( savegameManager . quickSaveSlot - > Filename , savegameManager . quickSaveSlot - > SaveTitle ) ;
2020-10-04 16:31:48 +00:00
return ;
}
FString tempstring = GStrings ( " QSPROMPT " ) ;
2020-10-07 16:32:57 +00:00
tempstring . Substitute ( " %s " , slot - > SaveTitle . GetChars ( ) ) ;
M_StartControlPanel ( true ) ;
2020-10-04 16:31:48 +00:00
2020-10-07 16:32:57 +00:00
DMenu * newmenu = CreateMessageBoxMenu ( CurrentMenu , tempstring , 0 , false , NAME_None , [ ] ( )
{
2020-10-21 23:01:28 +00:00
M_ClearMenus ( ) ;
2021-05-11 22:21:26 +00:00
G_SaveGame ( savegameManager . quickSaveSlot - > Filename , savegameManager . quickSaveSlot - > SaveTitle ) ;
2020-10-07 16:32:57 +00:00
} ) ;
2020-10-04 16:31:48 +00:00
M_ActivateMenu ( newmenu ) ;
}
//=============================================================================
//
//
//
//=============================================================================
2020-10-07 16:32:57 +00:00
CCMD ( quickload )
2020-10-04 16:31:48 +00:00
{ // F9
if ( netgame )
{
M_StartControlPanel ( true ) ;
2020-10-07 16:32:57 +00:00
M_StartMessage ( GStrings ( " QLOADNET " ) , 1 ) ;
2020-10-04 16:31:48 +00:00
return ;
}
2020-10-07 16:32:57 +00:00
if ( savegameManager . quickSaveSlot = = nullptr | | savegameManager . quickSaveSlot = = ( FSaveGameNode * ) 1 )
2020-10-04 16:31:48 +00:00
{
M_StartControlPanel ( true ) ;
// signal that whatever gets loaded should be the new quicksave
2020-10-07 16:32:57 +00:00
savegameManager . quickSaveSlot = ( FSaveGameNode * ) 1 ;
2020-10-04 16:31:48 +00:00
M_SetMenu ( NAME_Loadgamemenu ) ;
return ;
}
// [mxd]. Just load the game, no questions asked.
if ( ! saveloadconfirmation )
{
2020-10-07 16:32:57 +00:00
G_LoadGame ( savegameManager . quickSaveSlot - > Filename ) ;
2020-10-04 16:31:48 +00:00
return ;
}
FString tempstring = GStrings ( " QLPROMPT " ) ;
tempstring . Substitute ( " %s " , savegameManager . quickSaveSlot - > SaveTitle . GetChars ( ) ) ;
M_StartControlPanel ( true ) ;
2020-10-07 16:32:57 +00:00
DMenu * newmenu = CreateMessageBoxMenu ( CurrentMenu , tempstring , 0 , false , NAME_None , [ ] ( )
{
2020-10-21 23:01:28 +00:00
M_ClearMenus ( ) ;
2020-10-07 16:32:57 +00:00
G_LoadGame ( savegameManager . quickSaveSlot - > Filename ) ;
2020-10-04 16:31:48 +00:00
} ) ;
M_ActivateMenu ( newmenu ) ;
}
2020-10-06 23:31:41 +00:00
//=============================================================================
//
// Creation wrapper
//
//=============================================================================
2020-10-04 16:31:48 +00:00
2020-10-06 23:31:41 +00:00
static DMenuItemBase * CreateCustomListMenuItemText ( double x , double y , int height , int hotkey , const char * text , FFont * font , PalEntry color1 , PalEntry color2 , FName command , int param )
2020-10-04 16:31:48 +00:00
{
2020-10-06 23:31:41 +00:00
const char * classname =
2021-01-02 03:46:58 +00:00
isBlood ( ) ? " ListMenuItemBloodTextItem " :
2020-10-06 23:31:41 +00:00
( g_gameType & GAMEFLAG_SW ) ? " ListMenuItemSWTextItem " :
( g_gameType & GAMEFLAG_PSEXHUMED ) ? " ListMenuItemExhumedTextItem " : " ListMenuItemDukeTextItem " ;
auto c = PClass : : FindClass ( classname ) ;
auto p = c - > CreateNew ( ) ;
FString keystr = FString ( char ( hotkey ) ) ;
FString textstr = text ;
VMValue params [ ] = { p , x , y , height , & keystr , & textstr , font , int ( color1 . d ) , int ( color2 . d ) , command . GetIndex ( ) , param } ;
auto f = dyn_cast < PFunction > ( c - > FindSymbol ( " InitDirect " , false ) ) ;
VMCall ( f - > Variants [ 0 ] . Implementation , params , countof ( params ) , nullptr , 0 ) ;
return ( DMenuItemBase * ) p ;
2020-10-04 16:31:48 +00:00
}
//=============================================================================
//
// Creates the episode menu
//
//=============================================================================
2021-05-01 22:35:56 +00:00
extern TArray < VolumeRecord > volumes ;
2020-10-04 16:31:48 +00:00
2020-10-06 23:31:41 +00:00
static void BuildEpisodeMenu ( )
2020-10-04 16:31:48 +00:00
{
// Build episode menu
2020-10-06 23:31:41 +00:00
int addedVolumes = 0 ;
2021-01-27 22:53:26 +00:00
bool textadded = false ;
2020-10-06 23:31:41 +00:00
DMenuDescriptor * * desc = MenuDescriptors . CheckKey ( NAME_Episodemenu ) ;
if ( desc ! = nullptr & & ( * desc ) - > IsKindOf ( RUNTIME_CLASS ( DListMenuDescriptor ) ) )
2020-10-04 16:31:48 +00:00
{
2020-10-06 23:31:41 +00:00
DListMenuDescriptor * ld = static_cast < DListMenuDescriptor * > ( * desc ) ;
2020-10-04 16:31:48 +00:00
2020-10-06 23:31:41 +00:00
DMenuItemBase * popped = nullptr ;
if ( ld - > mItems . Size ( ) & & ld - > mItems . Last ( ) - > IsKindOf ( NAME_ListMenuItemBloodDripDrawer ) )
{
ld - > mItems . Pop ( popped ) ;
}
2020-10-04 16:31:48 +00:00
2020-10-06 23:31:41 +00:00
ld - > mSelectedItem = gDefaultVolume + ld - > mItems . Size ( ) ; // account for pre-added items
2021-05-11 23:55:06 +00:00
double y = ld - > mYpos ;
2020-10-04 16:31:48 +00:00
2021-05-01 22:35:56 +00:00
// Volume definitions should be sorted by intended menu order.
for ( auto & vol : volumes )
2020-10-06 23:31:41 +00:00
{
2021-05-01 20:52:28 +00:00
if ( vol . name . IsNotEmpty ( ) & & ! ( vol . flags & VF_HIDEFROMSP ) )
2020-10-04 16:31:48 +00:00
{
2021-05-01 22:35:56 +00:00
int isShareware = ( ( g_gameType & GAMEFLAG_DUKE ) & & ( g_gameType & GAMEFLAG_SHAREWARE ) & & ( vol . flags & VF_SHAREWARELOCK ) ) ;
2021-04-25 23:45:16 +00:00
auto it = CreateCustomListMenuItemText ( ld - > mXpos , y , ld - > mLinespacing , vol . name [ 0 ] ,
2021-05-01 22:35:56 +00:00
vol . name , ld - > mFont , CR_UNTRANSLATED , isShareware , NAME_Skillmenu , vol . index ) ; // font colors are not used, so hijack one for the shareware flag.
2020-10-04 16:31:48 +00:00
2020-10-06 23:31:41 +00:00
y + = ld - > mLinespacing ;
ld - > mItems . Push ( it ) ;
addedVolumes + + ;
2021-04-25 23:45:16 +00:00
if ( vol . subtitle . IsNotEmpty ( ) )
2020-10-04 16:31:48 +00:00
{
2020-11-08 09:12:12 +00:00
auto it = CreateCustomListMenuItemText ( ld - > mXpos , y , ld - > mLinespacing * 6 / 10 , 1 ,
2021-05-01 22:35:56 +00:00
vol . subtitle , SmallFont , CR_GRAY , false , NAME_None , vol . index ) ;
2020-10-06 23:31:41 +00:00
y + = ld - > mLinespacing * 6 / 10 ;
2020-10-04 16:31:48 +00:00
ld - > mItems . Push ( it ) ;
2021-01-27 22:53:26 +00:00
textadded = true ;
2020-10-04 16:31:48 +00:00
}
}
}
2020-10-06 23:31:41 +00:00
#if 0 // this needs to be backed by a working selection menu, until that gets done it must be disabled.
if ( ! ( g_gameType & GAMEFLAG_SHAREWARE ) )
2020-10-04 16:31:48 +00:00
{
2020-10-06 23:31:41 +00:00
//auto it = new FListMenuItemNativeStaticText(ld->mXpos, "", NIT_SmallFont); // empty entry as spacer.
//ld->mItems.Push(it);
y + = ld - > mLinespacing / 3 ;
2020-10-07 18:32:43 +00:00
auto it = CreateCustomListMenuItemText ( ld - > mXpos , y , ld - > mLinespacing , ' U ' , " $MNU_USERMAP " , ld - > mFont , 0 , 0 , NAME_UsermapMenu ) ;
2020-10-06 23:31:41 +00:00
ld - > mItems . Push ( it ) ;
addedVolumes + + ;
2020-10-04 16:31:48 +00:00
}
# endif
2020-10-06 23:31:41 +00:00
if ( addedVolumes = = 1 )
{
2021-01-27 22:53:26 +00:00
ld - > mAutoselect = ld - > mItems . Size ( ) - ( textadded ? 2 : 1 ) ;
2020-10-06 23:31:41 +00:00
}
if ( popped ) ld - > mItems . Push ( popped ) ;
}
2020-10-04 16:31:48 +00:00
2020-10-06 23:31:41 +00:00
// Build skill menu
int addedSkills = 0 ;
desc = MenuDescriptors . CheckKey ( NAME_Skillmenu ) ;
if ( desc ! = nullptr & & ( * desc ) - > IsKindOf ( RUNTIME_CLASS ( DListMenuDescriptor ) ) )
2020-10-04 16:31:48 +00:00
{
2020-10-06 23:31:41 +00:00
DListMenuDescriptor * ld = static_cast < DListMenuDescriptor * > ( * desc ) ;
DMenuItemBase * popped = nullptr ;
if ( ld - > mItems . Size ( ) & & ld - > mItems . Last ( ) - > IsKindOf ( NAME_ListMenuItemBloodDripDrawer ) )
2020-10-04 16:31:48 +00:00
{
2020-10-06 23:31:41 +00:00
ld - > mItems . Pop ( popped ) ;
}
2021-04-13 14:42:46 +00:00
if ( isBlood ( ) ) gDefaultSkill = 2 ;
2020-10-11 05:44:51 +00:00
ld - > mSelectedItem = gDefaultSkill + ld - > mItems . Size ( ) ; // account for pre-added items
2021-05-11 23:55:06 +00:00
double y = ld - > mYpos ;
2020-10-04 16:31:48 +00:00
2020-10-06 23:31:41 +00:00
for ( int i = 0 ; i < MAXSKILLS ; i + + )
{
if ( gSkillNames [ i ] . IsNotEmpty ( ) )
2020-10-04 16:31:48 +00:00
{
2020-11-09 15:56:59 +00:00
auto it = CreateCustomListMenuItemText ( ld - > mXpos , y , ld - > mLinespacing , gSkillNames [ i ] [ 0 ] , gSkillNames [ i ] , ld - > mFont , CR_UNTRANSLATED , 0 , NAME_Startgame , i ) ;
2020-10-06 23:31:41 +00:00
y + = ld - > mLinespacing ;
ld - > mItems . Push ( it ) ;
addedSkills + + ;
2020-10-04 16:31:48 +00:00
}
}
2020-10-06 23:31:41 +00:00
if ( addedSkills = = 0 )
2020-10-04 16:31:48 +00:00
{
2020-10-06 23:31:41 +00:00
// Need to add one item with the default skill so that the menu does not break.
2020-10-07 18:32:43 +00:00
auto it = CreateCustomListMenuItemText ( ld - > mXpos , y , ld - > mLinespacing , 0 , " " , ld - > mFont , 0 , 0 , NAME_Startgame , gDefaultSkill ) ;
2020-10-06 23:31:41 +00:00
ld - > mItems . Push ( it ) ;
2020-10-04 16:31:48 +00:00
}
2020-10-06 23:31:41 +00:00
if ( addedSkills = = 1 )
{
2020-10-13 20:16:43 +00:00
ld - > mAutoselect = ld - > mItems . Size ( ) - 1 ;
2020-10-06 23:31:41 +00:00
}
if ( popped ) ld - > mItems . Push ( popped ) ;
2020-10-04 16:31:48 +00:00
}
}
//=============================================================================
//
// 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 ) ;
}
}
}
}
}
//==========================================================================
//
// 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.
//
//==========================================================================
2020-10-06 23:31:41 +00:00
bool CheckSkipGameOptionBlock ( FScanner & sc ) { return false ; } // not applicable
2020-10-04 20:14:20 +00:00
2020-10-04 16:31:48 +00:00
#if 0
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 ) ;
}
2020-10-04 20:21:11 +00:00
# endif
2020-10-04 16:31:48 +00:00
void SetDefaultMenuColors ( )
{
2020-10-06 18:49:55 +00:00
PClass * cls = nullptr ;
2020-10-04 20:21:11 +00:00
//OptionSettings.mTitleColor = CR_RED;// V_FindFontColor(gameinfo.mTitleColor);
OptionSettings . mFontColor = CR_RED ;
OptionSettings . mFontColorValue = CR_GRAY ;
OptionSettings . mFontColorMore = CR_GRAY ;
OptionSettings . mFontColorHeader = CR_GOLD ;
OptionSettings . mFontColorHighlight = CR_YELLOW ;
OptionSettings . mFontColorSelection = CR_BRICK ;
2020-10-09 21:35:38 +00:00
gameinfo . mSliderColor = " Orange " ;
2020-11-23 14:54:06 +00:00
gameinfo . mSliderBackColor = " White " ;
2020-10-04 20:21:11 +00:00
2021-01-02 03:46:58 +00:00
if ( isBlood ( ) )
2020-10-04 20:21:11 +00:00
{
OptionSettings . mFontColorHeader = CR_DARKGRAY ;
OptionSettings . mFontColorHighlight = CR_WHITE ;
2020-10-06 18:49:55 +00:00
OptionSettings . mFontColorSelection = CR_DARKRED ;
2020-10-09 21:35:38 +00:00
gameinfo . mSliderColor = " Red " ;
2020-10-06 23:00:43 +00:00
cls = PClass : : FindClass ( " BloodMenuDelegate " ) ;
2020-10-04 20:21:11 +00:00
}
2020-10-06 18:49:55 +00:00
else if ( g_gameType & GAMEFLAG_SW )
2020-10-04 20:21:11 +00:00
{
2020-10-06 18:49:55 +00:00
OptionSettings . mFontColorHeader = CR_DARKRED ;
2020-10-04 20:21:11 +00:00
OptionSettings . mFontColorHighlight = CR_WHITE ;
2020-10-09 21:35:38 +00:00
gameinfo . mSliderColor = " Red " ;
2020-10-06 23:00:43 +00:00
cls = PClass : : FindClass ( " SWMenuDelegate " ) ;
2020-10-04 20:21:11 +00:00
}
2020-10-06 18:49:55 +00:00
else if ( g_gameType & GAMEFLAG_PSEXHUMED )
2020-10-06 23:31:41 +00:00
{
2020-10-06 18:49:55 +00:00
OptionSettings . mFontColorHeader = CR_LIGHTBLUE ;
OptionSettings . mFontColorHighlight = CR_SAPPHIRE ;
2020-10-06 20:35:25 +00:00
OptionSettings . mFontColorSelection = CR_ORANGE ;
OptionSettings . mFontColor = CR_FIRE ;
2020-10-09 21:35:38 +00:00
gameinfo . mSliderColor = " Yellow " ;
2020-10-06 23:00:43 +00:00
cls = PClass : : FindClass ( " ExhumedMenuDelegate " ) ;
2020-10-04 20:21:11 +00:00
}
2020-10-06 18:49:55 +00:00
else
2020-10-04 20:21:11 +00:00
{
2020-10-06 18:49:55 +00:00
if ( g_gameType & ( GAMEFLAG_NAM | GAMEFLAG_NAPALM | GAMEFLAG_WW2GI ) )
{
OptionSettings . mFontColor = CR_DARKGREEN ;
OptionSettings . mFontColorHeader = CR_DARKGRAY ;
OptionSettings . mFontColorHighlight = CR_WHITE ;
OptionSettings . mFontColorSelection = CR_DARKGREEN ;
2020-10-09 21:35:38 +00:00
gameinfo . mSliderColor = " Green " ;
2020-10-06 18:49:55 +00:00
}
else if ( g_gameType & GAMEFLAG_RRALL )
{
OptionSettings . mFontColor = CR_BROWN ;
OptionSettings . mFontColorHeader = CR_DARKBROWN ;
OptionSettings . mFontColorHighlight = CR_ORANGE ;
OptionSettings . mFontColorSelection = CR_TAN ;
2020-10-09 21:35:38 +00:00
gameinfo . mSliderColor = " Tan " ;
2020-10-06 18:49:55 +00:00
}
2020-10-06 23:00:43 +00:00
cls = PClass : : FindClass ( " DukeMenuDelegate " ) ;
2020-10-04 20:21:11 +00:00
}
2020-10-06 23:00:43 +00:00
if ( ! cls ) cls = PClass : : FindClass ( " RazeMenuDelegate " ) ;
if ( cls ) menuDelegate = cls - > CreateNew ( ) ;
2020-10-06 23:31:41 +00:00
}
2020-10-06 18:49:55 +00:00
2020-10-06 23:31:41 +00:00
void BuildGameMenus ( )
{
BuildEpisodeMenu ( ) ;
InitCrosshairsList ( ) ;
UpdateJoystickMenu ( nullptr ) ;
2020-10-04 16:31:48 +00:00
}
2020-10-06 23:00:43 +00:00
2020-10-06 23:12:57 +00:00
//=============================================================================
//
// [RH] Most menus can now be accessed directly
// through console commands.
//
//=============================================================================
EXTERN_CVAR ( Int , screenblocks )
CCMD ( reset2defaults )
{
C_SetDefaultBindings ( ) ;
C_SetCVarsToDefaults ( ) ;
}
CCMD ( reset2saved )
{
GameConfig - > DoGlobalSetup ( ) ;
GameConfig - > DoGameSetup ( currentGame ) ;
}
CCMD ( menu_main )
{
2020-10-10 21:16:08 +00:00
if ( gamestate = = GS_FULLCONSOLE ) gamestate = GS_MENUSCREEN ;
2020-10-06 23:12:57 +00:00
M_StartControlPanel ( true ) ;
2020-10-07 18:32:43 +00:00
M_SetMenu ( NAME_Mainmenu , - 1 ) ;
2020-10-06 23:12:57 +00:00
}
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 ) ;
}
2020-10-06 23:00:43 +00:00
// The sound system is not yet capable of resolving this properly.
DEFINE_ACTION_FUNCTION ( _RazeMenuDelegate , PlaySound )
{
PARAM_SELF_STRUCT_PROLOGUE ( void ) ;
PARAM_NAME ( name ) ;
EMenuSounds soundindex ;
switch ( name . GetIndex ( ) )
{
case NAME_menu_cursor :
soundindex = CursorSound ;
break ;
case NAME_menu_choose :
soundindex = ChooseSound ;
break ;
case NAME_menu_backup :
soundindex = BackSound ;
break ;
case NAME_menu_clear :
case NAME_menu_dismiss :
soundindex = CloseSound ;
break ;
case NAME_menu_change :
soundindex = ChangeSound ;
break ;
case NAME_menu_advance :
soundindex = AdvanceSound ;
break ;
default :
return 0 ;
}
gi - > MenuSound ( soundindex ) ;
return 0 ;
2020-10-06 23:12:57 +00:00
}
// C_ToggleConsole cannot be exported for security reasons as it can be used to make the engine unresponsive.
DEFINE_ACTION_FUNCTION ( _RazeMenuDelegate , MenuDismissed )
{
if ( CurrentMenu = = nullptr & & gamestate = = GS_MENUSCREEN ) C_ToggleConsole ( ) ;
return 0 ;
}
2020-10-08 23:09:51 +00:00
DEFINE_ACTION_FUNCTION ( _PlayerMenu , DrawPlayerSprite )
{
PARAM_PROLOGUE ;
PARAM_INT ( selected ) ;
gi - > DrawPlayerSprite ( DVector2 ( 0. , 0. ) , selected ) ;
return 0 ;
}
2021-04-05 18:12:11 +00:00
# ifdef _WIN32
EXTERN_CVAR ( Bool , vr_enable_quadbuffered )
# endif
void UpdateVRModes ( bool considerQuadBuffered )
{
FOptionValues * * pVRModes = OptionValues . CheckKey ( " VRMode " ) ;
if ( pVRModes = = nullptr ) return ;
TArray < FOptionValues : : Pair > & vals = ( * pVRModes ) - > mValues ;
TArray < FOptionValues : : Pair > filteredValues ;
int cnt = vals . Size ( ) ;
for ( int i = 0 ; i < cnt ; + + i ) {
auto const & mode = vals [ i ] ;
if ( mode . Value = = 7 ) { // Quad-buffered stereo
# ifdef _WIN32
if ( ! vr_enable_quadbuffered ) continue ;
# else
continue ; // Remove quad-buffered option on Mac and Linux
# endif
if ( ! considerQuadBuffered ) continue ; // Probably no compatible screen mode was found
}
filteredValues . Push ( mode ) ;
}
vals = filteredValues ;
}