2006-03-20 17:12:09 +00:00
/*
2014-06-01 17:55:23 +00:00
* Portions of this file are copyright Rebirth contributors and licensed as
* described in COPYING . txt .
* Portions of this file are copyright Parallax Software and licensed
* according to the Parallax license below .
* See COPYING . txt for license details .
2006-03-20 17:12:09 +00:00
THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
SOFTWARE CORPORATION ( " PARALLAX " ) . PARALLAX , IN DISTRIBUTING THE CODE TO
END - USERS , AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN , GRANTS A
ROYALTY - FREE , PERPETUAL LICENSE TO SUCH END - USERS FOR USE BY SUCH END - USERS
IN USING , DISPLAYING , AND CREATING DERIVATIVE WORKS THEREOF , SO LONG AS
SUCH USE , DISPLAY OR CREATION IS FOR NON - COMMERCIAL , ROYALTY OR REVENUE
FREE PURPOSES . IN NO EVENT SHALL THE END - USER USE THE COMPUTER CODE
CONTAINED HEREIN FOR REVENUE - BEARING PURPOSES . THE END - USER UNDERSTANDS
AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE .
COPYRIGHT 1993 - 1999 PARALLAX SOFTWARE CORPORATION . ALL RIGHTS RESERVED .
*/
/*
*
* Inferno main menu .
*
*/
# include <stdio.h>
# include <string.h>
2016-11-05 21:22:43 +00:00
# include <SDL.h>
2006-03-20 17:12:09 +00:00
# include "menu.h"
# include "inferno.h"
# include "game.h"
# include "gr.h"
# include "key.h"
2011-01-02 11:25:51 +00:00
# include "mouse.h"
2006-03-20 17:12:09 +00:00
# include "iff.h"
# include "u_mem.h"
2012-07-07 18:35:06 +00:00
# include "dxxerror.h"
2006-03-20 17:12:09 +00:00
# include "bm.h"
# include "screens.h"
# include "joy.h"
2015-07-25 23:10:45 +00:00
# include "player.h"
2006-03-20 17:12:09 +00:00
# include "vecmat.h"
# include "effects.h"
2013-09-24 01:59:09 +00:00
# include "game.h"
2006-03-20 17:12:09 +00:00
# include "slew.h"
# include "gamemine.h"
# include "gamesave.h"
# include "palette.h"
# include "args.h"
# include "newdemo.h"
# include "timer.h"
# include "sounds.h"
# include "gameseq.h"
# include "text.h"
# include "gamefont.h"
# include "newmenu.h"
# include "scores.h"
# include "playsave.h"
# include "kconfig.h"
# include "titles.h"
# include "credits.h"
# include "texmap.h"
# include "polyobj.h"
# include "state.h"
# include "mission.h"
# include "songs.h"
2016-09-11 18:49:15 +00:00
# if DXX_USE_SDLMIXER
2010-08-27 14:09:19 +00:00
# include "jukebox.h" // for jukebox_exts
2010-10-14 09:50:11 +00:00
# endif
2006-03-20 17:12:09 +00:00
# include "config.h"
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2006-03-20 17:12:09 +00:00
# include "movie.h"
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
# include "gamepal.h"
# include "gauges.h"
# include "powerup.h"
# include "strutil.h"
2009-11-29 16:46:13 +00:00
# include "multi.h"
2012-11-02 17:35:57 +00:00
# include "vers_id.h"
2016-09-11 18:49:15 +00:00
# if DXX_USE_UDP
2009-11-29 16:46:13 +00:00
# include "net_udp.h"
2009-11-24 09:48:53 +00:00
# endif
2016-09-11 18:49:16 +00:00
# if DXX_USE_EDITOR
2006-03-20 17:12:09 +00:00
# include "editor/editor.h"
2010-12-21 03:35:50 +00:00
# include "editor/kdefs.h"
2006-03-20 17:12:09 +00:00
# endif
2016-09-24 18:06:11 +00:00
# if DXX_USE_OGL
2011-01-16 00:50:42 +00:00
# include "ogl_init.h"
# endif
2015-04-19 04:18:49 +00:00
# include "physfs_list.h"
2006-03-20 17:12:09 +00:00
2014-07-05 16:48:12 +00:00
# include "dxxsconf.h"
2016-03-19 19:08:10 +00:00
# include "dsx-ns.h"
2016-11-19 18:09:26 +00:00
# include "compiler-exchange.h"
2014-09-28 23:28:56 +00:00
# include "compiler-make_unique.h"
2015-02-14 22:48:27 +00:00
# include "compiler-range_for.h"
2019-05-04 18:27:36 +00:00
# include "d_range.h"
2015-02-14 22:48:27 +00:00
# include "partial_range.h"
2006-03-20 17:12:09 +00:00
2008-10-28 17:58:54 +00:00
// Menu IDs...
enum MENUS
{
MENU_NEW_GAME = 0 ,
MENU_GAME ,
MENU_EDITOR ,
MENU_VIEW_SCORES ,
MENU_QUIT ,
MENU_LOAD_GAME ,
MENU_SAVE_GAME ,
MENU_DEMO_PLAY ,
MENU_CONFIG ,
MENU_REJOIN_NETGAME ,
MENU_DIFFICULTY ,
MENU_HELP ,
MENU_NEW_PLAYER ,
2016-09-11 18:49:15 +00:00
# if DXX_USE_UDP
2009-05-03 12:31:30 +00:00
MENU_MULTIPLAYER ,
# endif
2008-10-28 17:58:54 +00:00
MENU_SHOW_CREDITS ,
MENU_ORDER_INFO ,
2009-05-03 12:31:30 +00:00
2016-09-11 18:49:15 +00:00
# if DXX_USE_UDP
2009-11-24 09:48:53 +00:00
MENU_START_UDP_NETGAME ,
MENU_JOIN_MANUAL_UDP_NETGAME ,
MENU_JOIN_LIST_UDP_NETGAME ,
2009-11-29 16:46:13 +00:00
# endif
2011-07-20 12:44:49 +00:00
# ifndef RELEASE
MENU_SANDBOX
# endif
2008-10-28 17:58:54 +00:00
} ;
2006-03-20 17:12:09 +00:00
//ADD_ITEM("Start netgame...", MENU_START_NETGAME, -1 );
//ADD_ITEM("Send net message...", MENU_SEND_NET_MESSAGE, -1 );
2015-01-18 01:58:31 +00:00
# define ADD_ITEM(t,value,key) do { nm_set_item_menu(m[num_options], t); menu_choice[num_options]=value;num_options++; } while (0)
2006-03-20 17:12:09 +00:00
2015-02-14 22:48:27 +00:00
static array < window * , 16 > menus ;
2010-03-18 02:49:02 +00:00
2006-03-20 17:12:09 +00:00
// Function Prototypes added after LINTING
2013-09-22 22:26:27 +00:00
static int do_option ( int select ) ;
2016-10-27 07:22:41 +00:00
static window_event_result do_new_game_menu ( void ) ;
2016-10-08 23:24:22 +00:00
# if DXX_USE_UDP
2013-09-22 22:26:27 +00:00
static void do_multi_player_menu ( ) ;
2016-10-08 23:24:22 +00:00
# endif
2011-07-20 12:44:49 +00:00
# ifndef RELEASE
2013-09-22 22:26:27 +00:00
static void do_sandbox_menu ( ) ;
2011-07-20 12:44:49 +00:00
# endif
2006-03-20 17:12:09 +00:00
2016-01-29 04:05:47 +00:00
namespace {
2013-12-04 23:32:38 +00:00
template < typename T >
2016-10-27 07:22:41 +00:00
using select_file_subfunction = window_event_result ( * ) ( T * , const char * ) ;
2016-01-29 04:05:47 +00:00
}
2013-12-04 23:32:38 +00:00
2015-02-14 22:48:28 +00:00
__attribute_nonnull ( )
2016-03-06 18:11:20 +00:00
static int select_file_recursive2 ( const char * title , const char * orig_path , const partial_range_t < const file_extension_t * > & ext_list , int select_dir , select_file_subfunction < void > when_selected , void * userdata ) ;
2013-12-04 23:32:38 +00:00
2016-03-06 18:11:20 +00:00
template < typename T >
2015-02-14 22:48:28 +00:00
__attribute_nonnull ( )
2016-03-06 18:11:20 +00:00
static int select_file_recursive ( const char * title , const char * orig_path , const partial_range_t < const file_extension_t * > & ext_list , int select_dir , select_file_subfunction < T > when_selected , T * userdata )
2013-12-04 23:32:38 +00:00
{
2016-03-06 18:11:20 +00:00
return select_file_recursive2 ( title , orig_path , ext_list , select_dir , reinterpret_cast < select_file_subfunction < void > > ( when_selected ) , reinterpret_cast < void * > ( userdata ) ) ;
2013-12-04 23:32:38 +00:00
}
2013-11-26 22:19:52 +00:00
2010-03-18 02:49:02 +00:00
// Hide all menus
2010-03-30 03:47:51 +00:00
int hide_menus ( void )
2010-03-18 02:49:02 +00:00
{
window * wind ;
2010-03-27 08:00:53 +00:00
if ( menus [ 0 ] )
2010-03-30 03:47:51 +00:00
return 0 ; // there are already hidden menus
2010-03-27 03:24:14 +00:00
2015-02-14 22:48:27 +00:00
wind = window_get_front ( ) ;
2016-11-19 18:09:26 +00:00
range_for ( auto & i , menus )
2010-03-18 02:49:02 +00:00
{
2015-02-14 22:48:27 +00:00
i = wind ;
if ( ! wind )
break ;
2015-01-17 18:31:41 +00:00
wind = window_set_visible ( * wind , 0 ) ;
2010-03-18 02:49:02 +00:00
}
Assert ( window_get_front ( ) = = NULL ) ;
2010-03-30 03:47:51 +00:00
return 1 ;
2010-03-18 02:49:02 +00:00
}
// Show all menus, with the front one shown first
// This makes sure EVENT_WINDOW_ACTIVATED is only sent to that window
void show_menus ( void )
{
2015-02-14 22:48:27 +00:00
range_for ( auto & i , menus )
{
if ( ! i )
break ;
2016-10-28 00:58:47 +00:00
// Hidden windows don't receive events, so the only way to close is outside its handler
// Which there should be no cases of here
// window_exists could return a false positive if a new window was created
// with the same pointer value as the deleted one, so killing window_exists (call and function)
// if (window_exists(i))
2016-11-19 18:09:26 +00:00
window_set_visible ( exchange ( i , nullptr ) , 1 ) ;
2015-02-14 22:48:27 +00:00
}
2016-11-19 18:09:26 +00:00
}
namespace dcx {
/* This is a hack to prevent writing to freed memory. Various points in
* the game code call ` hide_menus ( ) ` , then later use ` show_menus ( ) ` to
* reverse the effect . If the forcibly hidden window is deleted before
* ` show_menus ( ) ` is called , the attempt to show it would write to freed
* memory . This hook is called when a window is deleted , so that the
* deleted window can be removed from menus [ ] . Removing it from menus [ ]
* prevents ` show_menus ( ) ` trying to make it visible later .
*
* It would be cleaner , but more invasive , to restructure the code so
* that the menus [ ] array does not need to exist and window pointers are
* not stored outside the control of their owner .
*/
void menu_destroy_hook ( window * w )
{
const auto & & e = menus . end ( ) ;
const auto & & i = std : : find ( menus . begin ( ) , e , w ) ;
if ( i = = e )
/* Not a hidden menu */
return ;
/* This is not run often enough to merit a clever loop that stops
* when it reaches an unused element .
*/
std : : move ( std : : next ( i ) , e , i ) ;
menus . back ( ) = nullptr ;
2010-03-18 02:49:02 +00:00
}
2010-02-07 12:10:52 +00:00
//pairs of chars describing ranges
2016-07-16 16:52:04 +00:00
constexpr char playername_allowed_chars [ ] = " azAZ09__-- " ;
2010-02-07 12:10:52 +00:00
2016-11-19 18:09:26 +00:00
}
2013-10-27 22:00:14 +00:00
static int MakeNewPlayerFile ( int allow_abort )
2010-02-07 12:10:52 +00:00
{
int x ;
2011-06-06 19:19:38 +00:00
char filename [ PATH_MAX ] ;
2019-07-07 22:00:02 +00:00
auto text = InterfaceUniqueState . PilotName ;
2010-06-14 08:13:16 +00:00
2010-02-07 12:10:52 +00:00
try_again :
2015-01-18 01:58:31 +00:00
{
2015-03-22 18:49:21 +00:00
array < newmenu_item , 1 > m { {
2015-01-18 01:58:31 +00:00
nm_item_input ( text . buffer ( ) ) ,
2015-03-22 18:49:21 +00:00
} } ;
2010-02-07 12:10:52 +00:00
Newmenu_allowed_chars = playername_allowed_chars ;
2015-01-18 01:58:32 +00:00
x = newmenu_do ( NULL , TXT_ENTER_PILOT_NAME , m , unused_newmenu_subfunction , unused_newmenu_userdata ) ;
2015-01-18 01:58:31 +00:00
}
2010-02-07 12:10:52 +00:00
Newmenu_allowed_chars = NULL ;
2010-06-14 08:13:16 +00:00
2010-02-07 12:10:52 +00:00
if ( x < 0 ) {
if ( allow_abort ) return 0 ;
goto try_again ;
}
2010-06-14 08:13:16 +00:00
2014-07-26 22:45:01 +00:00
if ( ! * static_cast < const char * > ( text ) ) //null string
2010-02-07 12:10:52 +00:00
goto try_again ;
2010-06-14 08:13:16 +00:00
2014-07-05 16:48:12 +00:00
text . lower ( ) ;
2010-06-14 08:13:16 +00:00
2014-07-05 16:48:12 +00:00
snprintf ( filename , sizeof ( filename ) , PLAYER_DIRECTORY_STRING ( " %s.plr " ) , static_cast < const char * > ( text ) ) ;
2010-06-14 08:13:16 +00:00
2011-06-01 07:59:55 +00:00
if ( PHYSFSX_exists ( filename , 0 ) )
2010-02-07 12:10:52 +00:00
{
2014-07-05 16:48:12 +00:00
nm_messagebox ( NULL , 1 , TXT_OK , " %s '%s' %s " , TXT_PLAYER , static_cast < const char * > ( text ) , TXT_ALREADY_EXISTS ) ;
2010-02-07 12:10:52 +00:00
goto try_again ;
}
2010-06-14 08:13:16 +00:00
2010-02-07 12:10:52 +00:00
if ( ! new_player_config ( ) )
goto try_again ; // They hit Esc during New player config
2010-06-14 08:13:16 +00:00
2019-07-07 22:00:02 +00:00
InterfaceUniqueState . PilotName = text ;
2019-07-07 22:00:02 +00:00
InterfaceUniqueState . update_window_title ( ) ;
2010-02-07 12:10:52 +00:00
write_player_file ( ) ;
2010-06-14 08:13:16 +00:00
2010-02-07 12:10:52 +00:00
return 1 ;
}
2013-09-02 23:21:13 +00:00
static void delete_player_saved_games ( const char * name ) ;
2010-02-07 12:10:52 +00:00
2016-10-27 07:22:41 +00:00
static window_event_result player_menu_keycommand ( listbox * lb , const d_event & event )
2010-02-07 12:10:52 +00:00
{
2013-09-02 23:21:13 +00:00
const char * * items = listbox_get_items ( lb ) ;
2010-02-07 12:10:52 +00:00
int citem = listbox_get_citem ( lb ) ;
2010-06-14 08:13:16 +00:00
2011-01-14 09:51:13 +00:00
switch ( event_key_get ( event ) )
2010-02-07 12:10:52 +00:00
{
case KEY_CTRLED + KEY_D :
if ( citem > 0 )
{
int x = 1 ;
x = nm_messagebox ( NULL , 2 , TXT_YES , TXT_NO , " %s %s? " , TXT_DELETE_PILOT , items [ citem ] + ( ( items [ citem ] [ 0 ] = = ' $ ' ) ? 1 : 0 ) ) ;
if ( x = = 0 ) {
2011-04-22 15:14:29 +00:00
char plxfile [ PATH_MAX ] , efffile [ PATH_MAX ] , ngpfile [ PATH_MAX ] ;
2010-02-07 12:10:52 +00:00
int ret ;
char name [ PATH_MAX ] ;
2010-06-14 08:13:16 +00:00
2013-09-02 23:09:16 +00:00
snprintf ( name , sizeof ( name ) , PLAYER_DIRECTORY_STRING ( " %.8s.plr " ) , items [ citem ] ) ;
2010-06-14 08:13:16 +00:00
2010-02-07 12:10:52 +00:00
ret = ! PHYSFS_delete ( name ) ;
2010-06-14 08:13:16 +00:00
2010-02-07 12:10:52 +00:00
if ( ! ret )
{
delete_player_saved_games ( items [ citem ] ) ;
// delete PLX file
2013-10-06 21:04:29 +00:00
snprintf ( plxfile , sizeof ( plxfile ) , PLAYER_DIRECTORY_STRING ( " %.8s.plx " ) , items [ citem ] ) ;
2011-06-01 07:59:55 +00:00
if ( PHYSFSX_exists ( plxfile , 0 ) )
2010-02-07 12:10:52 +00:00
PHYSFS_delete ( plxfile ) ;
// delete EFF file
2013-10-06 21:04:29 +00:00
snprintf ( efffile , sizeof ( efffile ) , PLAYER_DIRECTORY_STRING ( " %.8s.eff " ) , items [ citem ] ) ;
2011-06-01 07:59:55 +00:00
if ( PHYSFSX_exists ( efffile , 0 ) )
2010-02-07 12:10:52 +00:00
PHYSFS_delete ( efffile ) ;
2011-04-22 15:14:29 +00:00
// delete NGP file
2013-10-06 21:04:29 +00:00
snprintf ( ngpfile , sizeof ( ngpfile ) , PLAYER_DIRECTORY_STRING ( " %.8s.ngp " ) , items [ citem ] ) ;
2011-06-01 07:59:55 +00:00
if ( PHYSFSX_exists ( ngpfile , 0 ) )
2011-04-22 15:14:29 +00:00
PHYSFS_delete ( ngpfile ) ;
2010-02-07 12:10:52 +00:00
}
2010-06-14 08:13:16 +00:00
2010-02-07 12:10:52 +00:00
if ( ret )
nm_messagebox ( NULL , 1 , TXT_OK , " %s %s %s " , TXT_COULDNT , TXT_DELETE_PILOT , items [ citem ] + ( ( items [ citem ] [ 0 ] = = ' $ ' ) ? 1 : 0 ) ) ;
else
listbox_delete_item ( lb , citem ) ;
}
2010-06-14 08:13:16 +00:00
2016-10-27 07:22:41 +00:00
return window_event_result : : handled ;
2010-02-07 12:10:52 +00:00
}
break ;
}
2010-06-14 08:13:16 +00:00
2016-10-27 07:22:41 +00:00
return window_event_result : : ignored ;
2010-02-07 12:10:52 +00:00
}
2016-10-27 07:22:41 +00:00
static window_event_result player_menu_handler ( listbox * lb , const d_event & event , char * * list )
2010-02-07 12:10:52 +00:00
{
2013-09-02 23:21:13 +00:00
const char * * items = listbox_get_items ( lb ) ;
2014-10-04 21:47:13 +00:00
switch ( event . type )
2010-02-07 12:10:52 +00:00
{
case EVENT_KEY_COMMAND :
return player_menu_keycommand ( lb , event ) ;
case EVENT_NEWMENU_SELECTED :
2015-02-28 22:34:07 +00:00
{
auto & citem = static_cast < const d_select_event & > ( event ) . citem ;
2010-02-07 12:10:52 +00:00
if ( citem < 0 )
2016-10-27 07:22:41 +00:00
return window_event_result : : ignored ; // shouldn't happen
2010-02-07 12:10:52 +00:00
else if ( citem = = 0 )
{
// They selected 'create new pilot'
2016-10-27 07:22:41 +00:00
return MakeNewPlayerFile ( 1 ) ? window_event_result : : close : window_event_result : : handled ;
2010-02-07 12:10:52 +00:00
}
else
{
2019-07-07 22:00:02 +00:00
InterfaceUniqueState . PilotName . copy_lower ( items [ citem ] , strlen ( items [ citem ] ) ) ;
2019-07-07 22:00:02 +00:00
InterfaceUniqueState . update_window_title ( ) ;
2010-02-07 12:10:52 +00:00
}
2016-10-27 07:22:41 +00:00
return window_event_result : : close ;
2015-02-28 22:34:07 +00:00
}
2010-06-14 08:13:16 +00:00
2010-02-07 12:10:52 +00:00
case EVENT_WINDOW_CLOSE :
if ( read_player_file ( ) ! = EZERO )
2016-10-27 07:22:41 +00:00
return window_event_result : : handled ; // abort close!
2010-06-14 08:13:16 +00:00
2010-02-07 12:10:52 +00:00
WriteConfigFile ( ) ; // Update lastplr
2010-06-14 08:13:16 +00:00
2010-02-07 12:10:52 +00:00
PHYSFS_freeList ( list ) ;
d_free ( items ) ;
break ;
2010-06-14 08:13:16 +00:00
2010-02-07 12:10:52 +00:00
default :
break ;
}
2010-06-14 08:13:16 +00:00
2016-10-27 07:22:41 +00:00
return window_event_result : : ignored ;
2010-02-07 12:10:52 +00:00
}
//Inputs the player's name, without putting up the background screen
2019-06-27 03:26:20 +00:00
static void RegisterPlayer ( )
2010-02-07 12:10:52 +00:00
{
2015-03-22 18:49:21 +00:00
static const array < file_extension_t , 1 > types { { " plr " } } ;
2010-02-07 12:10:52 +00:00
int i = 0 , NumItems ;
int citem = 0 ;
int allow_abort_flag = 1 ;
2010-06-14 08:13:16 +00:00
2019-07-07 22:00:02 +00:00
auto & callsign = InterfaceUniqueState . PilotName ;
if ( ! callsign [ 0 ] )
2010-02-07 12:10:52 +00:00
{
2014-07-26 22:45:01 +00:00
if ( ! * static_cast < const char * > ( GameCfg . LastPlayer ) )
2010-06-23 12:57:28 +00:00
{
2019-07-07 22:00:02 +00:00
callsign = " player " ;
2010-02-07 12:10:52 +00:00
allow_abort_flag = 0 ;
2010-06-23 12:57:28 +00:00
}
else
{
// Read the last player's name from config file, not lastplr.txt
2019-07-07 22:00:02 +00:00
callsign = GameCfg . LastPlayer ;
2010-06-23 12:57:28 +00:00
}
2019-07-07 22:00:02 +00:00
InterfaceUniqueState . update_window_title ( ) ;
2010-02-07 12:10:52 +00:00
}
2010-06-14 08:13:16 +00:00
2015-01-23 03:55:05 +00:00
auto list = PHYSFSX_findFiles ( PLAYER_DIRECTORY_STRING ( " " ) , types ) ;
2010-02-07 12:10:52 +00:00
if ( ! list )
2019-06-27 03:26:20 +00:00
return ; // memory error
2015-01-23 03:55:05 +00:00
if ( ! list [ 0 ] )
2010-02-07 12:10:52 +00:00
{
MakeNewPlayerFile ( 0 ) ; // make a new player without showing listbox
2019-06-27 03:26:20 +00:00
return ;
2010-02-07 12:10:52 +00:00
}
2010-06-14 08:13:16 +00:00
2010-02-07 12:10:52 +00:00
for ( NumItems = 0 ; list [ NumItems ] ! = NULL ; NumItems + + ) { }
NumItems + + ; // for TXT_CREATE_NEW
2010-06-14 08:13:16 +00:00
2015-07-18 21:01:55 +00:00
RAIIdmem < const char * [ ] > m ;
MALLOC ( m , const char * [ ] , NumItems ) ;
2010-02-07 12:10:52 +00:00
if ( m = = NULL )
2019-06-27 03:26:20 +00:00
return ;
2010-06-14 08:13:16 +00:00
2010-02-07 12:10:52 +00:00
m [ i + + ] = TXT_CREATE_NEW ;
2010-06-14 08:13:16 +00:00
2015-07-18 21:01:55 +00:00
range_for ( const auto f , list )
2010-02-07 12:10:52 +00:00
{
char * p ;
2010-06-14 08:13:16 +00:00
2015-07-18 21:01:55 +00:00
size_t lenf = strlen ( f ) ;
2013-12-03 23:21:36 +00:00
if ( lenf > FILENAME_LEN - 1 | | lenf < 5 ) // sorry guys, can only have up to eight chars for the player name
2011-07-13 21:26:43 +00:00
{
NumItems - - ;
continue ;
}
2015-07-18 21:01:55 +00:00
m [ i + + ] = f ;
p = strchr ( f , ' . ' ) ;
2010-02-07 12:10:52 +00:00
if ( p )
* p = ' \0 ' ; // chop the .plr
2011-07-13 21:26:43 +00:00
}
if ( NumItems < = 1 ) // so it seems all plr files we found were too long. funny. let's make a real player
{
MakeNewPlayerFile ( 0 ) ; // make a new player without showing listbox
2019-06-27 03:26:20 +00:00
return ;
2010-02-07 12:10:52 +00:00
}
2010-06-14 08:13:16 +00:00
2010-02-07 12:10:52 +00:00
// Sort by name, except the <Create New Player> string
2016-09-04 19:10:42 +00:00
qsort ( & m [ 1 ] , NumItems - 1 , sizeof ( char * ) , string_array_sort_func ) ;
2010-02-07 12:10:52 +00:00
for ( i = 0 ; i < NumItems ; i + + )
2019-07-07 22:00:02 +00:00
if ( ! d_stricmp ( static_cast < const char * > ( callsign ) , m [ i ] ) )
2010-02-07 12:10:52 +00:00
citem = i ;
2015-07-18 21:01:55 +00:00
newmenu_listbox1 ( TXT_SELECT_PILOT , NumItems , m . release ( ) , allow_abort_flag , citem , player_menu_handler , list . release ( ) ) ;
2010-02-07 12:10:52 +00:00
}
2010-04-03 10:16:53 +00:00
// Draw Copyright and Version strings
2013-10-27 22:00:14 +00:00
static void draw_copyright ( )
2010-04-03 10:16:53 +00:00
{
2017-11-05 20:49:08 +00:00
gr_set_default_canvas ( ) ;
2017-02-11 21:42:38 +00:00
auto & canvas = * grd_curcanv ;
2018-05-19 23:21:42 +00:00
auto & game_font = * GAME_FONT ;
2017-02-11 21:42:42 +00:00
gr_set_fontcolor ( canvas , BM_XRGB ( 6 , 6 , 6 ) , - 1 ) ;
2018-05-19 23:21:42 +00:00
const auto & & line_spacing = LINE_SPACING ( game_font , game_font ) ;
gr_string ( canvas , game_font , 0x8000 , SHEIGHT - line_spacing , TXT_COPYRIGHT ) ;
2017-02-11 21:42:38 +00:00
gr_set_fontcolor ( canvas , BM_XRGB ( 25 , 0 , 0 ) , - 1 ) ;
2018-05-19 23:21:42 +00:00
gr_string ( canvas , game_font , 0x8000 , SHEIGHT - ( line_spacing * 2 ) , DESCENT_VERSION ) ;
2010-04-03 10:16:53 +00:00
}
2016-03-19 19:08:10 +00:00
namespace dsx {
2006-03-20 17:12:09 +00:00
// ------------------------------------------------------------------------
2014-10-04 21:47:13 +00:00
static int main_menu_handler ( newmenu * menu , const d_event & event , int * menu_choice )
2006-03-20 17:12:09 +00:00
{
2010-04-03 08:35:56 +00:00
newmenu_item * items = newmenu_get_items ( menu ) ;
2006-03-20 17:12:09 +00:00
2014-10-04 21:47:13 +00:00
switch ( event . type )
2008-01-13 13:27:58 +00:00
{
2019-06-27 03:26:20 +00:00
case EVENT_WINDOW_CREATED :
2019-07-16 04:00:50 +00:00
if ( InterfaceUniqueState . PilotName [ 0 ] )
break ;
2019-06-27 03:26:20 +00:00
RegisterPlayer ( ) ;
break ;
2010-01-27 09:11:47 +00:00
case EVENT_WINDOW_ACTIVATED :
2010-04-03 08:35:56 +00:00
load_palette ( MENU_PALETTE , 0 , 1 ) ; //get correct palette
2019-06-27 03:26:20 +00:00
keyd_time_when_last_pressed = timer_query ( ) ; // .. 20 seconds from now!
2010-01-27 09:11:47 +00:00
break ;
2010-06-14 08:13:16 +00:00
2010-01-27 09:11:47 +00:00
case EVENT_KEY_COMMAND :
// Don't allow them to hit ESC in the main menu.
2011-01-14 09:51:13 +00:00
if ( event_key_get ( event ) = = KEY_ESC )
2010-01-30 03:24:19 +00:00
return 1 ;
2010-05-03 08:22:50 +00:00
break ;
2010-06-14 08:13:16 +00:00
2011-01-02 11:25:51 +00:00
case EVENT_MOUSE_BUTTON_DOWN :
case EVENT_MOUSE_BUTTON_UP :
// Don't allow mousebutton-closing in main menu.
2011-01-14 09:51:13 +00:00
if ( event_mouse_get_button ( event ) = = MBTN_RIGHT )
2011-01-02 11:25:51 +00:00
return 1 ;
break ;
2010-01-27 09:11:47 +00:00
case EVENT_IDLE :
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_I)
# define DXX_DEMO_KEY_DELAY 45
# elif defined(DXX_BUILD_DESCENT_II)
2013-08-17 15:28:20 +00:00
# define DXX_DEMO_KEY_DELAY 25
2013-03-03 01:03:33 +00:00
# endif
2015-12-24 04:01:27 +00:00
if ( keyd_time_when_last_pressed + i2f ( DXX_DEMO_KEY_DELAY ) < timer_query ( ) | | CGameArg . SysAutoDemo )
2010-01-27 09:11:47 +00:00
{
2011-07-02 20:59:03 +00:00
keyd_time_when_last_pressed = timer_query ( ) ; // Reset timer so that disk won't thrash if no demos.
2010-06-14 08:13:16 +00:00
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
int n_demos = newdemo_count_demos ( ) ;
2015-12-24 04:01:27 +00:00
if ( ( d_rand ( ) % ( n_demos + 1 ) ) = = 0 & & ! CGameArg . SysAutoDemo )
2010-01-27 09:11:47 +00:00
{
2016-09-24 18:06:11 +00:00
# if DXX_USE_OGL
2010-01-27 09:11:47 +00:00
Screen_mode = - 1 ;
2006-03-20 17:12:09 +00:00
# endif
2013-10-27 21:01:04 +00:00
PlayMovie ( " intro.tex " , " intro.mve " , 0 ) ;
2010-01-27 09:11:47 +00:00
songs_play_song ( SONG_TITLE , 1 ) ;
set_screen_mode ( SCREEN_MENU ) ;
}
else
2013-03-03 01:03:33 +00:00
# endif
2010-01-27 09:11:47 +00:00
{
newdemo_start_playback ( NULL ) ; // Randomly pick a file, assume native endian (crashes if not)
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2010-01-27 09:11:47 +00:00
if ( Newdemo_state = = ND_STATE_PLAYBACK )
2010-04-03 08:35:56 +00:00
return 0 ;
2013-03-03 01:03:33 +00:00
# endif
2010-01-27 09:11:47 +00:00
}
2006-03-20 17:12:09 +00:00
}
2010-01-27 09:11:47 +00:00
break ;
2010-06-14 08:13:16 +00:00
2010-04-03 10:16:53 +00:00
case EVENT_NEWMENU_DRAW :
draw_copyright ( ) ;
break ;
2010-06-14 08:13:16 +00:00
2010-04-03 08:35:56 +00:00
case EVENT_NEWMENU_SELECTED :
2015-02-28 22:34:07 +00:00
{
auto & citem = static_cast < const d_select_event & > ( event ) . citem ;
return do_option ( menu_choice [ citem ] ) ;
}
2010-06-14 08:13:16 +00:00
2010-04-03 08:35:56 +00:00
case EVENT_WINDOW_CLOSE :
d_free ( menu_choice ) ;
d_free ( items ) ;
break ;
2010-06-14 08:13:16 +00:00
2010-01-27 09:11:47 +00:00
default :
break ;
2006-03-20 17:12:09 +00:00
}
2010-01-07 14:49:07 +00:00
2010-01-30 03:24:19 +00:00
return 0 ;
2006-03-20 17:12:09 +00:00
}
2009-05-03 12:31:30 +00:00
// -----------------------------------------------------------------------------
// Create the main menu.
2013-10-27 22:00:14 +00:00
static void create_main_menu ( newmenu_item * m , int * menu_choice , int * callers_num_options )
2006-03-20 17:12:09 +00:00
{
2014-01-07 20:58:47 +00:00
int num_options = 0 ;
2006-03-20 17:12:09 +00:00
# ifndef DEMO_ONLY
ADD_ITEM ( TXT_NEW_GAME , MENU_NEW_GAME , KEY_N ) ;
ADD_ITEM ( TXT_LOAD_GAME , MENU_LOAD_GAME , KEY_L ) ;
2016-09-11 18:49:15 +00:00
# if DXX_USE_UDP
2006-03-20 17:12:09 +00:00
ADD_ITEM ( TXT_MULTIPLAYER_ , MENU_MULTIPLAYER , - 1 ) ;
# endif
ADD_ITEM ( TXT_OPTIONS_ , MENU_CONFIG , - 1 ) ;
ADD_ITEM ( TXT_CHANGE_PILOTS , MENU_NEW_PLAYER , unused ) ;
ADD_ITEM ( TXT_VIEW_DEMO , MENU_DEMO_PLAY , 0 ) ;
ADD_ITEM ( TXT_VIEW_SCORES , MENU_VIEW_SCORES , KEY_V ) ;
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_I)
if ( ! PHYSFSX_exists ( " warning.pcx " , 1 ) ) /* SHAREWARE */
# elif defined(DXX_BUILD_DESCENT_II)
2011-06-01 07:59:55 +00:00
if ( PHYSFSX_exists ( " orderd2.pcx " , 1 ) ) /* SHAREWARE */
2013-03-03 01:03:33 +00:00
# endif
2006-03-20 17:12:09 +00:00
ADD_ITEM ( TXT_ORDERING_INFO , MENU_ORDER_INFO , - 1 ) ;
ADD_ITEM ( TXT_CREDITS , MENU_SHOW_CREDITS , - 1 ) ;
# endif
ADD_ITEM ( TXT_QUIT , MENU_QUIT , KEY_Q ) ;
# ifndef RELEASE
2009-05-03 12:31:30 +00:00
if ( ! ( Game_mode & GM_MULTI ) ) {
2016-09-11 18:49:16 +00:00
# if DXX_USE_EDITOR
2006-03-20 17:12:09 +00:00
ADD_ITEM ( " Editor " , MENU_EDITOR , KEY_E ) ;
# endif
}
2011-07-20 12:44:49 +00:00
ADD_ITEM ( " SANDBOX " , MENU_SANDBOX , - 1 ) ;
2006-03-20 17:12:09 +00:00
# endif
* callers_num_options = num_options ;
}
//returns number of item chosen
2010-06-14 08:13:16 +00:00
int DoMenu ( )
2006-03-20 17:12:09 +00:00
{
2010-04-03 08:35:56 +00:00
int * menu_choice ;
newmenu_item * m ;
2006-03-20 17:12:09 +00:00
int num_options = 0 ;
2013-07-13 02:31:27 +00:00
CALLOC ( menu_choice , int , 25 ) ;
2010-04-03 08:35:56 +00:00
if ( ! menu_choice )
return - 1 ;
2013-07-13 02:31:27 +00:00
CALLOC ( m , newmenu_item , 25 ) ;
2010-04-03 08:35:56 +00:00
if ( ! m )
{
d_free ( menu_choice ) ;
return - 1 ;
}
2010-01-30 22:49:43 +00:00
2010-02-07 04:34:21 +00:00
create_main_menu ( m , menu_choice , & num_options ) ; // may have to change, eg, maybe selected pilot and no save games.
2013-12-04 22:45:33 +00:00
newmenu_do3 ( " " , NULL , num_options , m , main_menu_handler , menu_choice , 0 , Menu_pcx_name ) ;
2006-03-20 17:12:09 +00:00
2010-04-03 08:35:56 +00:00
return 0 ;
2006-03-20 17:12:09 +00:00
}
2016-03-19 19:08:10 +00:00
}
2006-03-20 17:12:09 +00:00
//returns flag, true means quit menu
2010-06-14 08:13:16 +00:00
int do_option ( int select )
2006-03-20 17:12:09 +00:00
{
switch ( select ) {
case MENU_NEW_GAME :
2010-03-18 07:02:38 +00:00
select_mission ( 0 , " New Game \n \n Select mission " , do_new_game_menu ) ;
2006-03-20 17:12:09 +00:00
break ;
case MENU_GAME :
break ;
case MENU_DEMO_PLAY :
2010-02-07 12:10:52 +00:00
select_demo ( ) ;
2006-03-20 17:12:09 +00:00
break ;
case MENU_LOAD_GAME :
2015-04-19 04:18:49 +00:00
state_restore_all ( 0 , secret_restore : : none , nullptr , blind_save : : no ) ;
2006-03-20 17:12:09 +00:00
break ;
2016-09-11 18:49:16 +00:00
# if DXX_USE_EDITOR
2006-03-20 17:12:09 +00:00
case MENU_EDITOR :
2012-04-17 09:37:15 +00:00
if ( ! Current_mission )
{
create_new_mine ( ) ;
SetPlayerFromCurseg ( ) ;
}
2010-06-14 08:13:16 +00:00
2010-04-03 08:35:56 +00:00
hide_menus ( ) ;
2012-03-19 05:49:19 +00:00
init_editor ( ) ;
2006-03-20 17:12:09 +00:00
break ;
# endif
case MENU_VIEW_SCORES :
2010-02-08 04:10:21 +00:00
scores_view ( NULL , - 1 ) ;
2006-03-20 17:12:09 +00:00
break ;
# if 1 //def SHAREWARE
case MENU_ORDER_INFO :
show_order_form ( ) ;
break ;
# endif
case MENU_QUIT :
2016-09-11 18:49:16 +00:00
# if DXX_USE_EDITOR
2006-03-20 17:12:09 +00:00
if ( ! SafetyCheck ( ) ) break ;
# endif
2010-04-03 08:35:56 +00:00
return 0 ;
2006-03-20 17:12:09 +00:00
case MENU_NEW_PLAYER :
2010-04-04 09:41:53 +00:00
RegisterPlayer ( ) ;
2006-03-20 17:12:09 +00:00
break ;
2016-09-11 18:49:15 +00:00
# if DXX_USE_UDP
2009-11-24 09:48:53 +00:00
case MENU_START_UDP_NETGAME :
multi_protocol = MULTI_PROTO_UDP ;
2010-04-04 09:41:53 +00:00
select_mission ( 1 , TXT_MULTI_MISSION , net_udp_setup_game ) ;
2009-11-24 09:48:53 +00:00
break ;
case MENU_JOIN_MANUAL_UDP_NETGAME :
multi_protocol = MULTI_PROTO_UDP ;
net_udp_manual_join_game ( ) ;
break ;
case MENU_JOIN_LIST_UDP_NETGAME :
multi_protocol = MULTI_PROTO_UDP ;
2010-06-29 16:41:08 +00:00
net_udp_list_join_game ( ) ;
2009-11-24 09:48:53 +00:00
break ;
2009-11-29 16:46:13 +00:00
# endif
2016-09-11 18:49:15 +00:00
# if DXX_USE_UDP
2006-03-20 17:12:09 +00:00
case MENU_MULTIPLAYER :
do_multi_player_menu ( ) ;
break ;
2009-11-29 16:46:13 +00:00
# endif
2006-03-20 17:12:09 +00:00
case MENU_CONFIG :
do_options_menu ( ) ;
break ;
case MENU_SHOW_CREDITS :
2016-08-19 03:41:42 +00:00
credits_show ( ) ;
2006-03-20 17:12:09 +00:00
break ;
2011-07-20 12:44:49 +00:00
# ifndef RELEASE
case MENU_SANDBOX :
do_sandbox_menu ( ) ;
break ;
# endif
2006-03-20 17:12:09 +00:00
default :
Error ( " Unknown option %d in do_option " , select ) ;
break ;
}
2010-04-03 08:35:56 +00:00
return 1 ; // stay in main menu unless quitting
2006-03-20 17:12:09 +00:00
}
2013-09-02 23:21:13 +00:00
static void delete_player_saved_games ( const char * name )
2009-12-08 11:01:36 +00:00
{
int i ;
2011-02-09 11:58:32 +00:00
char filename [ PATH_MAX ] ;
2010-06-14 08:13:16 +00:00
2009-12-08 11:01:36 +00:00
for ( i = 0 ; i < 10 ; i + + )
{
2013-10-06 21:04:29 +00:00
snprintf ( filename , sizeof ( filename ) , PLAYER_DIRECTORY_STRING ( " %s.sg%x " ) , name , i ) ;
2011-02-09 11:58:32 +00:00
PHYSFS_delete ( filename ) ;
2013-10-06 21:04:29 +00:00
snprintf ( filename , sizeof ( filename ) , PLAYER_DIRECTORY_STRING ( " %s.mg%x " ) , name , i ) ;
2009-12-08 11:01:36 +00:00
PHYSFS_delete ( filename ) ;
}
}
2016-10-27 07:22:41 +00:00
static window_event_result demo_menu_keycommand ( listbox * lb , const d_event & event )
2009-12-08 11:01:36 +00:00
{
2013-09-02 23:21:13 +00:00
const char * * items = listbox_get_items ( lb ) ;
2010-01-07 14:49:07 +00:00
int citem = listbox_get_citem ( lb ) ;
2010-06-14 08:13:16 +00:00
2011-01-14 09:51:13 +00:00
switch ( event_key_get ( event ) )
2009-12-08 11:01:36 +00:00
{
case KEY_CTRLED + KEY_D :
2010-02-07 12:10:52 +00:00
if ( citem > = 0 )
2009-12-08 11:01:36 +00:00
{
int x = 1 ;
2010-02-07 12:10:52 +00:00
x = nm_messagebox ( NULL , 2 , TXT_YES , TXT_NO , " %s %s? " , TXT_DELETE_DEMO , items [ citem ] + ( ( items [ citem ] [ 0 ] = = ' $ ' ) ? 1 : 0 ) ) ;
if ( x = = 0 )
{
2009-12-08 11:01:36 +00:00
int ret ;
char name [ PATH_MAX ] ;
2010-06-14 08:13:16 +00:00
2010-02-07 12:10:52 +00:00
strcpy ( name , DEMO_DIR ) ;
2010-01-07 14:49:07 +00:00
strcat ( name , items [ citem ] ) ;
2010-06-14 08:13:16 +00:00
2009-12-08 11:01:36 +00:00
ret = ! PHYSFS_delete ( name ) ;
2010-06-14 08:13:16 +00:00
2010-02-07 12:10:52 +00:00
if ( ret )
nm_messagebox ( NULL , 1 , TXT_OK , " %s %s %s " , TXT_COULDNT , TXT_DELETE_DEMO , items [ citem ] + ( ( items [ citem ] [ 0 ] = = ' $ ' ) ? 1 : 0 ) ) ;
2010-01-07 14:49:07 +00:00
else
listbox_delete_item ( lb , citem ) ;
2009-12-08 11:01:36 +00:00
}
2010-06-14 08:13:16 +00:00
2016-10-27 07:22:41 +00:00
return window_event_result : : handled ;
2009-12-08 11:01:36 +00:00
}
break ;
2010-06-14 08:13:16 +00:00
2010-02-07 12:10:52 +00:00
case KEY_CTRLED + KEY_C :
2009-12-08 11:01:36 +00:00
{
int x = 1 ;
char bakname [ PATH_MAX ] ;
2010-06-14 08:13:16 +00:00
2009-12-08 11:01:36 +00:00
// Get backup name
2010-02-07 12:10:52 +00:00
change_filename_extension ( bakname , items [ citem ] + ( ( items [ citem ] [ 0 ] = = ' $ ' ) ? 1 : 0 ) , DEMO_BACKUP_EXT ) ;
2009-12-08 11:01:36 +00:00
x = nm_messagebox ( NULL , 2 , TXT_YES , TXT_NO , " Are you sure you want to \n "
" swap the endianness of \n "
" %s? If the file is \n "
" already endian native, D1X \n "
" will likely crash. A backup \n "
2010-02-07 12:10:52 +00:00
" %s will be created " , items [ citem ] + ( ( items [ citem ] [ 0 ] = = ' $ ' ) ? 1 : 0 ) , bakname ) ;
2010-01-07 14:49:07 +00:00
if ( ! x )
newdemo_swap_endian ( items [ citem ] ) ;
2010-06-14 08:13:16 +00:00
2016-10-27 07:22:41 +00:00
return window_event_result : : handled ;
2009-12-08 11:01:36 +00:00
}
break ;
}
2010-06-14 08:13:16 +00:00
2016-10-27 07:22:41 +00:00
return window_event_result : : ignored ;
2010-02-06 05:21:45 +00:00
}
2016-10-27 07:22:41 +00:00
static window_event_result demo_menu_handler ( listbox * lb , const d_event & event , char * * items )
2010-02-06 05:21:45 +00:00
{
2014-10-04 21:47:13 +00:00
switch ( event . type )
2010-02-06 05:21:45 +00:00
{
case EVENT_KEY_COMMAND :
2010-02-07 12:10:52 +00:00
return demo_menu_keycommand ( lb , event ) ;
2010-02-06 05:21:45 +00:00
case EVENT_NEWMENU_SELECTED :
2015-02-28 22:34:07 +00:00
{
auto & citem = static_cast < const d_select_event & > ( event ) . citem ;
2010-02-06 05:21:45 +00:00
if ( citem < 0 )
2016-10-27 07:22:41 +00:00
return window_event_result : : ignored ; // shouldn't happen
2010-02-07 12:10:52 +00:00
newdemo_start_playback ( items [ citem ] ) ;
2016-10-27 07:22:41 +00:00
return window_event_result : : handled ; // stay in demo selector
2015-02-28 22:34:07 +00:00
}
2010-02-06 05:21:45 +00:00
case EVENT_WINDOW_CLOSE :
2010-02-07 12:10:52 +00:00
PHYSFS_freeList ( items ) ;
2010-02-06 05:21:45 +00:00
break ;
default :
break ;
}
2016-10-27 07:22:41 +00:00
return window_event_result : : ignored ;
2009-12-08 11:01:36 +00:00
}
2010-02-07 12:10:52 +00:00
int select_demo ( void )
2009-12-08 11:01:36 +00:00
{
2010-02-07 12:10:52 +00:00
int NumItems ;
2010-06-14 08:13:16 +00:00
2015-01-23 03:55:05 +00:00
auto list = PHYSFSX_findFiles ( DEMO_DIR , demo_file_extensions ) ;
2010-02-07 12:10:52 +00:00
if ( ! list )
2009-12-08 11:01:36 +00:00
return 0 ; // memory error
2015-01-23 03:55:05 +00:00
if ( ! list [ 0 ] )
2009-12-08 11:01:36 +00:00
{
nm_messagebox ( NULL , 1 , TXT_OK , " %s %s \n %s " , TXT_NO_DEMO_FILES , TXT_USE_F5 , TXT_TO_CREATE_ONE ) ;
return 0 ;
2010-06-14 08:13:16 +00:00
}
2010-02-07 12:10:52 +00:00
for ( NumItems = 0 ; list [ NumItems ] ! = NULL ; NumItems + + ) { }
2009-12-08 11:01:36 +00:00
2010-02-07 12:10:52 +00:00
// Sort by name
2016-09-04 19:10:42 +00:00
qsort ( list . get ( ) , NumItems , sizeof ( char * ) , string_array_sort_func ) ;
2009-12-08 11:01:36 +00:00
2015-06-13 22:42:21 +00:00
auto clist = const_cast < const char * * > ( list . get ( ) ) ;
2015-01-23 03:55:05 +00:00
newmenu_listbox1 ( TXT_SELECT_DEMO , NumItems , clist , 1 , 0 , demo_menu_handler , list . release ( ) ) ;
2010-06-14 08:13:16 +00:00
2009-12-08 11:01:36 +00:00
return 1 ;
}
2013-10-27 22:00:14 +00:00
static int do_difficulty_menu ( )
2006-03-20 17:12:09 +00:00
{
2015-03-22 18:49:21 +00:00
array < newmenu_item , NDL > m { {
2015-01-18 01:58:31 +00:00
nm_item_menu ( MENU_DIFFICULTY_TEXT ( 0 ) ) ,
nm_item_menu ( MENU_DIFFICULTY_TEXT ( 1 ) ) ,
nm_item_menu ( MENU_DIFFICULTY_TEXT ( 2 ) ) ,
nm_item_menu ( MENU_DIFFICULTY_TEXT ( 3 ) ) ,
nm_item_menu ( MENU_DIFFICULTY_TEXT ( 4 ) ) ,
2015-03-22 18:49:21 +00:00
} } ;
2006-03-20 17:12:09 +00:00
2019-06-27 03:26:20 +00:00
auto & Difficulty_level = GameUniqueState . Difficulty_level ;
2018-05-12 18:24:19 +00:00
const unsigned s = newmenu_do1 ( nullptr , TXT_DIFFICULTY_LEVEL , m . size ( ) , & m . front ( ) , unused_newmenu_subfunction , unused_newmenu_userdata , Difficulty_level ) ;
2006-03-20 17:12:09 +00:00
2018-05-12 18:24:19 +00:00
if ( s < = Difficulty_4 )
{
const auto d = static_cast < Difficulty_level_type > ( s ) ;
if ( d ! = Difficulty_level )
2006-03-20 17:12:09 +00:00
{
2018-05-12 18:24:19 +00:00
PlayerCfg . DefaultDifficulty = d ;
2006-03-20 17:12:09 +00:00
write_player_file ( ) ;
}
2018-05-12 18:24:19 +00:00
Difficulty_level = d ;
2006-03-20 17:12:09 +00:00
return 1 ;
}
return 0 ;
}
2016-10-27 07:22:41 +00:00
window_event_result do_new_game_menu ( )
2006-03-20 17:12:09 +00:00
{
int new_level_num , player_highest_level ;
new_level_num = 1 ;
player_highest_level = get_highest_level ( ) ;
2019-03-09 23:39:16 +00:00
# ifdef NDEBUG
2006-03-20 17:12:09 +00:00
if ( player_highest_level > Last_level )
2011-05-15 23:46:26 +00:00
# endif
2006-03-20 17:12:09 +00:00
player_highest_level = Last_level ;
if ( player_highest_level > 1 ) {
char info_text [ 80 ] ;
2013-08-28 03:12:41 +00:00
snprintf ( info_text , sizeof ( info_text ) , " %s %d " , TXT_START_ANY_LEVEL , player_highest_level ) ;
2019-03-10 18:48:40 +00:00
for ( ; ; )
2011-05-15 23:46:26 +00:00
{
2015-01-18 01:58:31 +00:00
array < char , 10 > num_text { " 1 " } ;
2015-03-22 18:49:21 +00:00
array < newmenu_item , 2 > m { {
2015-01-18 01:58:31 +00:00
nm_item_text ( info_text ) ,
nm_item_input ( num_text ) ,
2015-03-22 18:49:21 +00:00
} } ;
2019-03-10 18:48:40 +00:00
const int choice = newmenu_do ( nullptr , TXT_SELECT_START_LEV , m , unused_newmenu_subfunction , unused_newmenu_userdata ) ;
2006-03-20 17:12:09 +00:00
2019-03-10 18:48:40 +00:00
if ( choice = = - 1 | | ! num_text [ 0 ] )
2016-10-27 07:22:41 +00:00
return window_event_result : : handled ;
2006-03-20 17:12:09 +00:00
2019-03-10 18:48:40 +00:00
char * p = nullptr ;
new_level_num = strtol ( num_text . data ( ) , & p , 10 ) ;
2006-03-20 17:12:09 +00:00
2019-03-10 18:48:40 +00:00
if ( * p | | new_level_num < = 0 | | new_level_num > player_highest_level )
{
2011-05-15 23:46:26 +00:00
nm_messagebox ( NULL , 1 , TXT_OK , TXT_INVALID_LEVEL ) ;
}
else
2019-03-10 18:48:40 +00:00
break ;
2006-03-20 17:12:09 +00:00
}
}
2007-09-28 22:44:04 +00:00
2019-06-27 03:26:20 +00:00
GameUniqueState . Difficulty_level = PlayerCfg . DefaultDifficulty ;
2006-03-20 17:12:09 +00:00
if ( ! do_difficulty_menu ( ) )
2016-10-27 07:22:41 +00:00
return window_event_result : : handled ;
2006-03-20 17:12:09 +00:00
StartNewGame ( new_level_num ) ;
2016-10-27 07:22:41 +00:00
return window_event_result : : close ; // exit mission listbox
2010-03-18 07:02:38 +00:00
}
2013-09-22 22:26:27 +00:00
static void do_sound_menu ( ) ;
static void input_config ( ) ;
static void change_res ( ) ;
2016-08-25 04:05:32 +00:00
namespace dsx {
2015-09-26 17:48:57 +00:00
static void hud_config ( ) ;
2013-09-22 22:26:27 +00:00
static void graphics_config ( ) ;
2016-08-25 04:05:32 +00:00
}
2015-09-26 17:48:57 +00:00
static void gameplay_config ( ) ;
2010-01-17 14:42:59 +00:00
2015-03-07 17:20:40 +00:00
# define DXX_OPTIONS_MENU(VERB) \
2016-01-09 16:38:17 +00:00
DXX_MENUITEM ( VERB , MENU , " Sound & music... " , sfx ) \
DXX_MENUITEM ( VERB , MENU , TXT_CONTROLS_ , controls ) \
DXX_MENUITEM ( VERB , MENU , " Graphics... " , graphics ) \
DXX_MENUITEM ( VERB , MENU , " Gameplay... " , misc ) \
2015-03-07 17:20:40 +00:00
namespace {
class options_menu_items
{
public :
enum
{
DXX_OPTIONS_MENU ( ENUM )
} ;
array < newmenu_item , DXX_OPTIONS_MENU ( COUNT ) > m ;
options_menu_items ( )
{
DXX_OPTIONS_MENU ( ADD ) ;
}
} ;
}
2015-04-26 20:15:56 +00:00
static int options_menuset ( newmenu * , const d_event & event , options_menu_items * items )
2006-03-20 17:12:09 +00:00
{
2014-10-04 21:47:13 +00:00
switch ( event . type )
2006-03-20 17:12:09 +00:00
{
2010-02-01 06:55:38 +00:00
case EVENT_NEWMENU_CHANGED :
2010-01-17 14:42:59 +00:00
break ;
2010-06-14 08:13:16 +00:00
2010-01-17 14:42:59 +00:00
case EVENT_NEWMENU_SELECTED :
2015-02-28 22:34:07 +00:00
{
auto & citem = static_cast < const d_select_event & > ( event ) . citem ;
switch ( citem )
2010-01-17 14:42:59 +00:00
{
2015-03-07 17:20:40 +00:00
case options_menu_items : : sfx :
do_sound_menu ( ) ;
break ;
case options_menu_items : : controls :
input_config ( ) ;
break ;
case options_menu_items : : graphics :
graphics_config ( ) ;
break ;
case options_menu_items : : misc :
2015-09-26 17:48:57 +00:00
gameplay_config ( ) ;
2015-03-07 17:20:40 +00:00
break ;
2010-01-17 14:42:59 +00:00
}
2010-01-30 03:24:19 +00:00
return 1 ; // stay in menu until escape
2015-02-28 22:34:07 +00:00
}
2010-06-14 08:13:16 +00:00
2010-01-20 05:10:32 +00:00
case EVENT_WINDOW_CLOSE :
2010-04-04 09:41:53 +00:00
{
2015-03-07 17:20:40 +00:00
std : : default_delete < options_menu_items > ( ) ( items ) ;
2010-01-17 14:42:59 +00:00
write_player_file ( ) ;
break ;
2010-04-04 09:41:53 +00:00
}
2010-06-14 08:13:16 +00:00
2010-01-30 03:24:19 +00:00
default :
break ;
2006-03-20 17:12:09 +00:00
}
2010-01-30 03:24:19 +00:00
return 0 ;
2006-03-20 17:12:09 +00:00
}
2013-10-27 22:00:14 +00:00
static int gcd ( int a , int b )
2006-03-20 17:12:09 +00:00
{
2008-10-19 12:53:30 +00:00
if ( ! b )
2010-06-14 08:13:16 +00:00
return a ;
2008-10-19 12:53:30 +00:00
return gcd ( b , a % b ) ;
2006-03-20 17:12:09 +00:00
}
void change_res ( )
{
2018-07-28 23:22:58 +00:00
newmenu_item m [ 50 + 8 ] ;
array < char , 12 > crestext , casptext ;
2016-02-12 04:02:28 +00:00
int mc = 0 , citem = - 1 ;
2010-06-24 09:29:11 +00:00
2018-07-28 23:22:58 +00:00
# if SDL_MAJOR_VERSION == 1
array < screen_mode , 50 > modes ;
2015-05-14 02:23:13 +00:00
const auto num_presets = gr_list_modes ( modes ) ;
2018-07-28 23:22:58 +00:00
array < array < char , 12 > , 50 > restext ;
2010-06-24 09:29:11 +00:00
2016-02-12 04:02:28 +00:00
range_for ( auto & i , partial_const_range ( modes , num_presets ) )
2010-06-24 09:29:11 +00:00
{
2016-07-31 22:25:50 +00:00
const auto & & sm_w = SM_W ( i ) ;
const auto & & sm_h = SM_H ( i ) ;
2018-07-28 23:22:58 +00:00
snprintf ( restext [ mc ] . data ( ) , restext [ mc ] . size ( ) , " %ix%i " , sm_w , sm_h ) ;
2016-07-31 22:25:50 +00:00
const auto checked = ( citem = = - 1 & & Game_screen_mode = = i & & GameCfg . AspectY = = sm_w / gcd ( sm_w , sm_h ) & & GameCfg . AspectX = = sm_h / gcd ( sm_w , sm_h ) ) ;
if ( checked )
2010-06-24 09:29:11 +00:00
citem = mc ;
2018-07-28 23:22:58 +00:00
nm_set_item_radio ( m [ mc ] , restext [ mc ] . data ( ) , checked , 0 ) ;
2010-06-24 09:29:11 +00:00
mc + + ;
}
2008-10-19 12:53:30 +00:00
2015-01-18 01:58:31 +00:00
nm_set_item_text ( m [ mc ] , " " ) ; mc + + ; // little space for overview
2010-06-24 09:29:11 +00:00
// the fields for custom resolution and aspect
2015-05-14 02:23:13 +00:00
const auto opt_cval = mc ;
2018-07-28 23:22:58 +00:00
# endif
2015-01-18 01:58:31 +00:00
nm_set_item_radio ( m [ mc ] , " use custom values " , ( citem = = - 1 ) , 0 ) ; mc + + ;
2015-01-18 01:58:31 +00:00
nm_set_item_text ( m [ mc ] , " resolution: " ) ; mc + + ;
2018-07-28 23:22:58 +00:00
snprintf ( crestext . data ( ) , crestext . size ( ) , " %ix%i " , SM_W ( Game_screen_mode ) , SM_H ( Game_screen_mode ) ) ;
2015-01-18 01:58:31 +00:00
nm_set_item_input ( m [ mc ] , crestext ) ;
2015-05-14 02:23:13 +00:00
mc + + ;
2015-01-18 01:58:31 +00:00
nm_set_item_text ( m [ mc ] , " aspect: " ) ; mc + + ;
2018-07-28 23:22:58 +00:00
snprintf ( casptext . data ( ) , casptext . size ( ) , " %ix%i " , GameCfg . AspectY , GameCfg . AspectX ) ;
2015-01-18 01:58:31 +00:00
nm_set_item_input ( m [ mc ] , casptext ) ;
2015-05-14 02:23:13 +00:00
mc + + ;
2015-01-18 01:58:31 +00:00
nm_set_item_text ( m [ mc ] , " " ) ; mc + + ; // little space for overview
2010-06-24 09:29:11 +00:00
// fullscreen
2018-07-28 23:22:58 +00:00
# if SDL_MAJOR_VERSION == 1
2015-05-14 02:23:13 +00:00
const auto opt_fullscr = mc ;
2015-01-18 01:58:31 +00:00
nm_set_item_checkbox ( m [ mc ] , " Fullscreen " , gr_check_fullscreen ( ) ) ;
mc + + ;
2018-07-28 23:22:58 +00:00
# endif
2008-04-06 20:23:28 +00:00
2008-10-19 12:53:30 +00:00
// create the menu
2013-12-04 22:45:33 +00:00
newmenu_do1 ( NULL , " Screen Resolution " , mc , m , unused_newmenu_subfunction , unused_newmenu_userdata , 0 ) ;
2008-10-19 12:53:30 +00:00
// menu is done, now do what we need to do
2006-08-30 18:12:04 +00:00
2010-06-24 09:29:11 +00:00
// check which resolution field was selected
2018-07-28 23:22:58 +00:00
# if SDL_MAJOR_VERSION == 1
2016-02-12 04:02:28 +00:00
unsigned i ;
2010-06-24 09:29:11 +00:00
for ( i = 0 ; i < = mc ; i + + )
2016-07-31 22:25:50 +00:00
if ( m [ i ] . type = = NM_TYPE_RADIO & & m [ i ] . radio ( ) . group = = 0 & & m [ i ] . value = = 1 )
2010-06-24 09:29:11 +00:00
break ;
2008-10-19 12:53:30 +00:00
// now check for fullscreen toggle and apply if necessary
2010-06-24 09:29:11 +00:00
if ( m [ opt_fullscr ] . value ! = gr_check_fullscreen ( ) )
2006-03-20 17:12:09 +00:00
gr_toggle_fullscreen ( ) ;
2018-07-28 23:22:58 +00:00
# endif
2006-03-20 17:12:09 +00:00
2015-05-14 02:23:13 +00:00
screen_mode new_mode ;
2018-07-28 23:22:58 +00:00
# if SDL_MAJOR_VERSION == 1
2010-06-24 09:29:11 +00:00
if ( i = = opt_cval ) // set custom resolution and aspect
2018-07-28 23:22:58 +00:00
# endif
2006-03-20 17:12:09 +00:00
{
2017-07-26 03:15:59 +00:00
char revert [ 32 ] ;
2015-03-22 18:49:21 +00:00
char * x ;
2017-07-26 03:15:59 +00:00
const char * errstr ;
2018-07-28 23:22:58 +00:00
unsigned long w = strtoul ( crestext . data ( ) , & x , 10 ) , h ;
2015-05-14 02:23:13 +00:00
screen_mode cmode ;
2017-07-26 03:15:59 +00:00
if (
2018-07-28 23:22:58 +00:00
( ( x = = crestext . data ( ) | | * x ! = ' x ' | | ! x [ 1 ] | | ( ( h = strtoul ( x + 1 , & x , 10 ) ) , * x ) ) & & ( errstr = " Entered resolution must \n be formatted as \n <number>x<number> " , true ) ) | |
2017-07-26 03:15:59 +00:00
( ( w < 320 | | h < 200 ) & & ( errstr = " Entered resolution must \n be at least 320x200 " , true ) )
)
2015-03-22 18:49:21 +00:00
{
2017-07-26 03:15:59 +00:00
cmode = Game_screen_mode ;
w = SM_W ( cmode ) ;
h = SM_H ( cmode ) ;
snprintf ( revert , sizeof ( revert ) , " Revert to %lux%lu " , w , h ) ;
nm_messagebox_str ( TXT_WARNING , revert , errstr ) ;
2008-10-19 12:53:30 +00:00
}
2015-03-22 18:49:21 +00:00
else
{
2015-05-14 02:23:13 +00:00
cmode . width = w ;
cmode . height = h ;
2015-03-22 18:49:21 +00:00
}
auto casp = cmode ;
2018-07-28 23:22:58 +00:00
w = strtoul ( casptext . data ( ) , & x , 10 ) ;
2017-07-26 03:15:59 +00:00
if (
2018-07-28 23:22:58 +00:00
( ( x = = casptext . data ( ) | | * x ! = ' x ' | | ! x [ 1 ] | | ( ( h = strtoul ( x + 1 , & x , 10 ) ) , * x ) ) & & ( errstr = " Entered aspect ratio must \n be formatted as \n <number>x<number> " , true ) ) | |
2017-07-26 03:15:59 +00:00
( ( ! w | | ! h ) & & ( errstr = " Entered aspect ratio must \n not use 0 term " , true ) )
)
2015-03-22 18:49:21 +00:00
{
2017-07-26 03:15:59 +00:00
nm_messagebox_str ( TXT_WARNING , " IGNORE ASPECT RATIO " , errstr ) ;
2015-03-22 18:49:21 +00:00
}
else
2008-10-19 12:53:30 +00:00
{
2015-03-22 18:49:21 +00:00
// we even have a custom aspect set up
2015-05-14 02:23:13 +00:00
casp . width = w ;
casp . height = h ;
2008-10-19 12:53:30 +00:00
}
2015-05-14 02:23:13 +00:00
const auto g = gcd ( SM_W ( casp ) , SM_H ( casp ) ) ;
GameCfg . AspectY = SM_W ( casp ) / g ;
GameCfg . AspectX = SM_H ( casp ) / g ;
2010-06-24 09:29:11 +00:00
new_mode = cmode ;
2006-03-20 17:12:09 +00:00
}
2018-07-28 23:22:58 +00:00
# if SDL_MAJOR_VERSION == 1
2016-02-12 04:02:28 +00:00
else if ( i < num_presets ) // set preset resolution
2006-03-20 17:12:09 +00:00
{
2010-06-24 09:29:11 +00:00
new_mode = modes [ i ] ;
2015-05-14 02:23:13 +00:00
const auto g = gcd ( SM_W ( new_mode ) , SM_H ( new_mode ) ) ;
GameCfg . AspectY = SM_W ( new_mode ) / g ;
GameCfg . AspectX = SM_H ( new_mode ) / g ;
2006-03-20 17:12:09 +00:00
}
2018-07-28 23:22:58 +00:00
# endif
2006-03-20 17:12:09 +00:00
2008-10-19 12:53:30 +00:00
// clean up and apply everything
2010-07-31 12:09:38 +00:00
newmenu_free_background ( ) ;
2007-07-26 23:57:29 +00:00
set_screen_mode ( SCREEN_MENU ) ;
2010-06-24 09:29:11 +00:00
if ( new_mode ! = Game_screen_mode )
2011-01-16 01:09:47 +00:00
{
2010-06-24 09:29:11 +00:00
gr_set_mode ( new_mode ) ;
2011-01-16 01:09:47 +00:00
Game_screen_mode = new_mode ;
if ( Game_wind ) // shortly activate Game_wind so it's canvas will align to new resolution. really minor glitch but whatever
{
2018-05-12 18:24:19 +00:00
{
const d_event event { EVENT_WINDOW_ACTIVATED } ;
WINDOW_SEND_EVENT ( Game_wind ) ;
}
{
const d_event event { EVENT_WINDOW_DEACTIVATED } ;
WINDOW_SEND_EVENT ( Game_wind ) ;
}
2011-01-16 01:09:47 +00:00
}
}
2012-05-26 22:24:57 +00:00
game_init_render_buffers ( SM_W ( Game_screen_mode ) , SM_H ( Game_screen_mode ) ) ;
2006-03-20 17:12:09 +00:00
}
2015-09-25 21:59:20 +00:00
static void input_config_keyboard ( )
2010-12-28 18:11:10 +00:00
{
2015-05-14 02:23:13 +00:00
# define DXX_INPUT_SENSITIVITY(VERB,OPT,VAL) \
2016-01-09 16:38:17 +00:00
DXX_MENUITEM ( VERB , SLIDER , TXT_TURN_LR , opt_ # # OPT # # _turn_lr , VAL [ 0 ] , 0 , 16 ) \
DXX_MENUITEM ( VERB , SLIDER , TXT_PITCH_UD , opt_ # # OPT # # _pitch_ud , VAL [ 1 ] , 0 , 16 ) \
DXX_MENUITEM ( VERB , SLIDER , TXT_SLIDE_LR , opt_ # # OPT # # _slide_lr , VAL [ 2 ] , 0 , 16 ) \
DXX_MENUITEM ( VERB , SLIDER , TXT_SLIDE_UD , opt_ # # OPT # # _slide_ud , VAL [ 3 ] , 0 , 16 ) \
DXX_MENUITEM ( VERB , SLIDER , TXT_BANK_LR , opt_ # # OPT # # _bank_lr , VAL [ 4 ] , 0 , 16 ) \
2015-05-14 02:23:13 +00:00
2015-09-25 21:59:20 +00:00
# define DXX_INPUT_CONFIG_MENU(VERB) \
2016-01-09 16:38:17 +00:00
DXX_MENUITEM ( VERB , TEXT , " Keyboard Sensitivity: " , opt_label_kb ) \
2015-09-25 21:59:20 +00:00
DXX_INPUT_SENSITIVITY ( VERB , kb , PlayerCfg . KeyboardSens ) \
class menu_items
{
public :
enum
{
DXX_INPUT_CONFIG_MENU ( ENUM )
} ;
array < newmenu_item , DXX_INPUT_CONFIG_MENU ( COUNT ) > m ;
menu_items ( )
{
DXX_INPUT_CONFIG_MENU ( ADD ) ;
}
} ;
# undef DXX_INPUT_CONFIG_MENU
2017-07-26 03:15:59 +00:00
# undef DXX_INPUT_SENSITIVITY
2015-09-25 21:59:20 +00:00
menu_items items ;
newmenu_do1 ( nullptr , " Keyboard Calibration " , items . m . size ( ) , items . m . data ( ) , unused_newmenu_subfunction , unused_newmenu_userdata , 1 ) ;
constexpr uint_fast32_t keysens = items . opt_label_kb + 1 ;
const auto & m = items . m ;
2019-05-04 18:27:36 +00:00
range_for ( const unsigned i , xrange ( 5u ) )
2015-09-25 21:59:20 +00:00
{
2015-10-03 17:17:49 +00:00
PlayerCfg . KeyboardSens [ i ] = m [ keysens + i ] . value ;
2015-09-25 21:59:20 +00:00
}
}
static void input_config_mouse ( )
{
# define DXX_INPUT_SENSITIVITY(VERB,OPT,VAL) \
2016-01-09 16:38:17 +00:00
DXX_MENUITEM ( VERB , SLIDER , TXT_TURN_LR , opt_ # # OPT # # _turn_lr , VAL [ 0 ] , 0 , 16 ) \
DXX_MENUITEM ( VERB , SLIDER , TXT_PITCH_UD , opt_ # # OPT # # _pitch_ud , VAL [ 1 ] , 0 , 16 ) \
DXX_MENUITEM ( VERB , SLIDER , TXT_SLIDE_LR , opt_ # # OPT # # _slide_lr , VAL [ 2 ] , 0 , 16 ) \
DXX_MENUITEM ( VERB , SLIDER , TXT_SLIDE_UD , opt_ # # OPT # # _slide_ud , VAL [ 3 ] , 0 , 16 ) \
DXX_MENUITEM ( VERB , SLIDER , TXT_BANK_LR , opt_ # # OPT # # _bank_lr , VAL [ 4 ] , 0 , 16 ) \
2015-09-25 21:59:20 +00:00
2015-05-14 02:23:13 +00:00
# define DXX_INPUT_THROTTLE_SENSITIVITY(VERB,OPT,VAL) \
DXX_INPUT_SENSITIVITY ( VERB , OPT , VAL ) \
2016-01-09 16:38:17 +00:00
DXX_MENUITEM ( VERB , SLIDER , TXT_THROTTLE , opt_ # # OPT # # _throttle , VAL [ 5 ] , 0 , 16 ) \
2015-05-14 02:23:13 +00:00
2015-09-25 21:59:20 +00:00
# define DXX_INPUT_CONFIG_MENU(VERB) \
2016-01-09 16:38:17 +00:00
DXX_MENUITEM ( VERB , TEXT , " Mouse Sensitivity: " , opt_label_ms ) \
2015-09-25 21:59:20 +00:00
DXX_INPUT_THROTTLE_SENSITIVITY ( VERB , ms , PlayerCfg . MouseSens ) \
2016-01-09 16:38:17 +00:00
DXX_MENUITEM ( VERB , TEXT , " " , opt_label_blank_ms ) \
DXX_MENUITEM ( VERB , TEXT , " Mouse Overrun Buffer: " , opt_label_mo ) \
2015-09-25 21:59:20 +00:00
DXX_INPUT_THROTTLE_SENSITIVITY ( VERB , mo , PlayerCfg . MouseOverrun ) \
2016-01-09 16:38:17 +00:00
DXX_MENUITEM ( VERB , TEXT , " " , opt_label_blank_mo ) \
DXX_MENUITEM ( VERB , TEXT , " Mouse FlightSim Deadzone: " , opt_label_mfsd ) \
DXX_MENUITEM ( VERB , SLIDER , " X/Y " , opt_mfsd_deadzone , PlayerCfg . MouseFSDead , 0 , 16 ) \
2015-09-25 21:59:20 +00:00
class menu_items
{
public :
enum
{
DXX_INPUT_CONFIG_MENU ( ENUM )
} ;
array < newmenu_item , DXX_INPUT_CONFIG_MENU ( COUNT ) > m ;
menu_items ( )
{
DXX_INPUT_CONFIG_MENU ( ADD ) ;
}
} ;
# undef DXX_INPUT_CONFIG_MENU
menu_items items ;
newmenu_do1 ( nullptr , " Mouse Calibration " , items . m . size ( ) , items . m . data ( ) , unused_newmenu_subfunction , unused_newmenu_userdata , 1 ) ;
constexpr uint_fast32_t mousesens = items . opt_label_ms + 1 ;
constexpr uint_fast32_t mouseoverrun = items . opt_label_mo + 1 ;
const auto & m = items . m ;
for ( unsigned i = 0 ; i < = 5 ; i + + )
{
PlayerCfg . MouseSens [ i ] = m [ mousesens + i ] . value ;
PlayerCfg . MouseOverrun [ i ] = m [ mouseoverrun + i ] . value ;
}
constexpr uint_fast32_t mousefsdead = items . opt_mfsd_deadzone ;
PlayerCfg . MouseFSDead = m [ mousefsdead ] . value ;
}
2016-09-25 04:52:49 +00:00
# if DXX_MAX_AXES_PER_JOYSTICK
2015-09-25 21:59:20 +00:00
static void input_config_joystick ( )
{
# define DXX_INPUT_CONFIG_MENU(VERB) \
2016-01-09 16:38:17 +00:00
DXX_MENUITEM ( VERB , TEXT , " Joystick Sensitivity: " , opt_label_js ) \
2015-05-14 02:23:13 +00:00
DXX_INPUT_THROTTLE_SENSITIVITY ( VERB , js , PlayerCfg . JoystickSens ) \
2016-01-09 16:38:17 +00:00
DXX_MENUITEM ( VERB , TEXT , " " , opt_label_blank_js ) \
DXX_MENUITEM ( VERB , TEXT , " Joystick Linearity: " , opt_label_jl ) \
2015-06-09 23:42:50 +00:00
DXX_INPUT_THROTTLE_SENSITIVITY ( VERB , jl , PlayerCfg . JoystickLinear ) \
2016-01-09 16:38:17 +00:00
DXX_MENUITEM ( VERB , TEXT , " " , opt_label_blank_jl ) \
DXX_MENUITEM ( VERB , TEXT , " Joystick Linear Speed: " , opt_label_jp ) \
2015-06-09 23:42:50 +00:00
DXX_INPUT_THROTTLE_SENSITIVITY ( VERB , jp , PlayerCfg . JoystickSpeed ) \
2016-01-09 16:38:17 +00:00
DXX_MENUITEM ( VERB , TEXT , " " , opt_label_blank_jp ) \
DXX_MENUITEM ( VERB , TEXT , " Joystick Deadzone: " , opt_label_jd ) \
2015-09-25 21:59:20 +00:00
DXX_INPUT_THROTTLE_SENSITIVITY ( VERB , jd , PlayerCfg . JoystickDead ) \
2015-05-14 02:23:13 +00:00
class menu_items
{
public :
enum
{
DXX_INPUT_CONFIG_MENU ( ENUM )
} ;
array < newmenu_item , DXX_INPUT_CONFIG_MENU ( COUNT ) > m ;
menu_items ( )
{
DXX_INPUT_CONFIG_MENU ( ADD ) ;
}
} ;
# undef DXX_INPUT_CONFIG_MENU
menu_items items ;
2015-09-25 21:59:20 +00:00
newmenu_do1 ( nullptr , " Joystick Calibration " , items . m . size ( ) , items . m . data ( ) , unused_newmenu_subfunction , unused_newmenu_userdata , 1 ) ;
2015-05-14 02:23:13 +00:00
constexpr uint_fast32_t joysens = items . opt_label_js + 1 ;
2015-06-09 23:42:50 +00:00
constexpr uint_fast32_t joylin = items . opt_label_jl + 1 ;
constexpr uint_fast32_t joyspd = items . opt_label_jp + 1 ;
2015-05-14 02:23:13 +00:00
constexpr uint_fast32_t joydead = items . opt_label_jd + 1 ;
const auto & m = items . m ;
for ( unsigned i = 0 ; i < = 5 ; i + + )
2010-12-28 18:11:10 +00:00
{
2015-06-09 23:42:50 +00:00
PlayerCfg . JoystickLinear [ i ] = m [ joylin + i ] . value ;
PlayerCfg . JoystickSpeed [ i ] = m [ joyspd + i ] . value ;
2010-12-28 18:11:10 +00:00
PlayerCfg . JoystickSens [ i ] = m [ joysens + i ] . value ;
PlayerCfg . JoystickDead [ i ] = m [ joydead + i ] . value ;
}
}
2015-11-01 21:15:40 +00:00
# endif
2010-12-28 18:11:10 +00:00
2017-07-26 03:15:59 +00:00
# undef DXX_INPUT_THROTTLE_SENSITIVITY
# undef DXX_INPUT_SENSITIVITY
2015-05-14 02:23:13 +00:00
namespace {
class input_config_menu_items
2008-02-24 14:41:27 +00:00
{
2016-09-25 04:52:48 +00:00
# if DXX_MAX_JOYSTICKS
2015-11-01 21:15:40 +00:00
# define DXX_INPUT_CONFIG_JOYSTICK_ITEM(I) I
# else
# define DXX_INPUT_CONFIG_JOYSTICK_ITEM(I)
# endif
2016-09-25 04:52:49 +00:00
# if DXX_MAX_AXES_PER_JOYSTICK
2015-11-01 21:15:40 +00:00
# define DXX_INPUT_CONFIG_JOYSTICK_AXIS_ITEM(I) I
# else
# define DXX_INPUT_CONFIG_JOYSTICK_AXIS_ITEM(I)
# endif
2015-05-14 02:23:13 +00:00
# define DXX_INPUT_CONFIG_MENU(VERB) \
2016-01-09 16:38:17 +00:00
DXX_INPUT_CONFIG_JOYSTICK_ITEM ( DXX_MENUITEM ( VERB , CHECK , " Use joystick " , opt_ic_usejoy , PlayerCfg . ControlType & CONTROL_USING_JOYSTICK ) ) \
DXX_MENUITEM ( VERB , CHECK , " Use mouse " , opt_ic_usemouse , PlayerCfg . ControlType & CONTROL_USING_MOUSE ) \
DXX_MENUITEM ( VERB , TEXT , " " , opt_label_blank_use ) \
DXX_MENUITEM ( VERB , MENU , TXT_CUST_KEYBOARD , opt_ic_confkey ) \
DXX_INPUT_CONFIG_JOYSTICK_ITEM ( DXX_MENUITEM ( VERB , MENU , " Customize Joystick " , opt_ic_confjoy ) ) \
DXX_MENUITEM ( VERB , MENU , " Customize Mouse " , opt_ic_confmouse ) \
DXX_MENUITEM ( VERB , MENU , " Customize Weapon Keys " , opt_ic_confweap ) \
DXX_MENUITEM ( VERB , TEXT , " " , opt_label_blank_customize ) \
DXX_MENUITEM ( VERB , TEXT , " Mouse Control Type: " , opt_label_mouse_control_type ) \
DXX_MENUITEM ( VERB , RADIO , " Normal " , opt_mouse_control_normal , PlayerCfg . MouseFlightSim = = 0 , optgrp_mouse_control_type ) \
DXX_MENUITEM ( VERB , RADIO , " FlightSim " , opt_mouse_control_flightsim , PlayerCfg . MouseFlightSim = = 1 , optgrp_mouse_control_type ) \
DXX_MENUITEM ( VERB , TEXT , " " , opt_label_blank_mouse_control_type ) \
DXX_MENUITEM ( VERB , MENU , " Keyboard Calibration " , opt_ic_keyboard ) \
DXX_MENUITEM ( VERB , MENU , " Mouse Calibration " , opt_ic_mouse ) \
DXX_INPUT_CONFIG_JOYSTICK_AXIS_ITEM ( DXX_MENUITEM ( VERB , MENU , " Joystick Calibration " , opt_ic_joystick ) ) \
DXX_MENUITEM ( VERB , TEXT , " " , opt_label_blank_sensitivity_deadzone ) \
DXX_MENUITEM ( VERB , CHECK , " Keep Keyboard/Mouse focus " , opt_ic_grabinput , CGameCfg . Grabinput ) \
DXX_MENUITEM ( VERB , CHECK , " Mouse FlightSim Indicator " , opt_ic_mousefsgauge , PlayerCfg . MouseFSIndicator ) \
DXX_MENUITEM ( VERB , TEXT , " " , opt_label_blank_focus ) \
DXX_MENUITEM ( VERB , TEXT , " When dead, respawn by pressing: " , opt_label_respawn_mode ) \
DXX_MENUITEM ( VERB , RADIO , " Any key " , opt_respawn_any_key , PlayerCfg . RespawnMode = = RespawnPress : : Any , optgrp_respawn_mode ) \
DXX_MENUITEM ( VERB , RADIO , " Fire keys (pri., sec., flare) " , opt_respawn_fire_key , PlayerCfg . RespawnMode = = RespawnPress : : Fire , optgrp_respawn_mode ) \
DXX_MENUITEM ( VERB , TEXT , " " , opt_label_blank_respawn ) \
2017-03-25 19:34:02 +00:00
DXX_MENUITEM ( VERB , TEXT , " Uncapped turning in: " , opt_label_mouselook_mode ) \
DXX_MENUITEM ( VERB , CHECK , " Single player " , opt_ic_mouselook_sp , PlayerCfg . MouselookFlags & MouselookMode : : Singleplayer ) \
DXX_MENUITEM ( VERB , CHECK , " Multi Coop (if host allows) " , opt_ic_mouselook_mp_cooperative , PlayerCfg . MouselookFlags & MouselookMode : : MPCoop ) \
DXX_MENUITEM ( VERB , CHECK , " Multi Anarchy (if host allows) " , opt_ic_mouselook_mp_anarchy , PlayerCfg . MouselookFlags & MouselookMode : : MPAnarchy ) \
DXX_MENUITEM ( VERB , TEXT , " " , opt_label_blank_mouselook ) \
2016-01-09 16:38:17 +00:00
DXX_MENUITEM ( VERB , MENU , " GAME SYSTEM KEYS " , opt_ic_help0 ) \
DXX_MENUITEM ( VERB , MENU , " NETGAME SYSTEM KEYS " , opt_ic_help1 ) \
DXX_MENUITEM ( VERB , MENU , " DEMO SYSTEM KEYS " , opt_ic_help2 ) \
2015-05-14 02:23:13 +00:00
public :
enum
{
optgrp_mouse_control_type ,
2015-11-21 18:12:13 +00:00
optgrp_respawn_mode ,
2015-05-14 02:23:13 +00:00
} ;
enum
{
DXX_INPUT_CONFIG_MENU ( ENUM )
} ;
array < newmenu_item , DXX_INPUT_CONFIG_MENU ( COUNT ) > m ;
input_config_menu_items ( )
{
DXX_INPUT_CONFIG_MENU ( ADD ) ;
}
static int menuset ( newmenu * , const d_event & event , input_config_menu_items * pitems ) ;
# undef DXX_INPUT_CONFIG_MENU
2015-11-01 21:15:40 +00:00
# undef DXX_INPUT_CONFIG_JOYSTICK_AXIS_ITEM
# undef DXX_INPUT_CONFIG_JOYSTICK_ITEM
2015-05-14 02:23:13 +00:00
} ;
}
int input_config_menu_items : : menuset ( newmenu * , const d_event & event , input_config_menu_items * pitems )
{
const auto & items = pitems - > m ;
2014-10-04 21:47:13 +00:00
switch ( event . type )
2010-01-17 14:42:59 +00:00
{
2010-02-01 06:55:38 +00:00
case EVENT_NEWMENU_CHANGED :
2015-02-28 22:34:07 +00:00
{
2015-05-14 02:23:13 +00:00
const auto citem = static_cast < const d_change_event & > ( event ) . citem ;
2017-03-25 19:34:02 +00:00
MouselookMode mousemode ;
2016-09-25 04:52:48 +00:00
# if DXX_MAX_JOYSTICKS
2010-12-28 18:11:10 +00:00
if ( citem = = opt_ic_usejoy )
2015-05-09 17:39:02 +00:00
{
constexpr auto flag = CONTROL_USING_JOYSTICK ;
if ( items [ citem ] . value )
PlayerCfg . ControlType | = flag ;
else
PlayerCfg . ControlType & = ~ flag ;
}
2015-11-01 21:15:40 +00:00
# endif
2010-12-28 18:11:10 +00:00
if ( citem = = opt_ic_usemouse )
2015-05-09 17:39:02 +00:00
{
constexpr auto flag = CONTROL_USING_MOUSE ;
if ( items [ citem ] . value )
PlayerCfg . ControlType | = flag ;
else
PlayerCfg . ControlType & = ~ flag ;
}
2015-05-14 02:23:13 +00:00
if ( citem = = opt_mouse_control_normal )
2011-01-04 10:43:37 +00:00
PlayerCfg . MouseFlightSim = 0 ;
2015-05-14 02:23:13 +00:00
if ( citem = = opt_mouse_control_flightsim )
2011-01-04 10:43:37 +00:00
PlayerCfg . MouseFlightSim = 1 ;
2010-12-28 18:11:10 +00:00
if ( citem = = opt_ic_grabinput )
2015-07-18 21:01:56 +00:00
CGameCfg . Grabinput = items [ citem ] . value ;
2011-01-04 10:43:37 +00:00
if ( citem = = opt_ic_mousefsgauge )
2011-01-10 15:12:04 +00:00
PlayerCfg . MouseFSIndicator = items [ citem ] . value ;
2015-11-21 18:12:13 +00:00
else if ( citem = = opt_respawn_any_key )
PlayerCfg . RespawnMode = RespawnPress : : Any ;
else if ( citem = = opt_respawn_fire_key )
PlayerCfg . RespawnMode = RespawnPress : : Fire ;
2017-03-25 19:34:02 +00:00
else if ( ( citem = = opt_ic_mouselook_sp & & ( mousemode = MouselookMode : : Singleplayer , true ) ) | |
( citem = = opt_ic_mouselook_mp_cooperative & & ( mousemode = MouselookMode : : MPCoop , true ) ) | |
( citem = = opt_ic_mouselook_mp_anarchy & & ( mousemode = MouselookMode : : MPAnarchy , true ) ) )
{
if ( items [ citem ] . value )
PlayerCfg . MouselookFlags | = mousemode ;
else
PlayerCfg . MouselookFlags & = ~ mousemode ;
}
2010-01-17 14:42:59 +00:00
break ;
2015-02-28 22:34:07 +00:00
}
2010-01-17 14:42:59 +00:00
case EVENT_NEWMENU_SELECTED :
2015-02-28 22:34:07 +00:00
{
2015-05-14 02:23:13 +00:00
const auto citem = static_cast < const d_select_event & > ( event ) . citem ;
2010-12-28 18:11:10 +00:00
if ( citem = = opt_ic_confkey )
2015-11-01 21:15:38 +00:00
kconfig ( kconfig_type : : keyboard ) ;
2016-09-25 04:52:48 +00:00
# if DXX_MAX_JOYSTICKS
2010-12-28 18:11:10 +00:00
if ( citem = = opt_ic_confjoy )
2015-11-01 21:15:38 +00:00
kconfig ( kconfig_type : : joystick ) ;
2015-11-01 21:15:40 +00:00
# endif
2010-12-28 18:11:10 +00:00
if ( citem = = opt_ic_confmouse )
2015-11-01 21:15:38 +00:00
kconfig ( kconfig_type : : mouse ) ;
2010-12-28 18:11:10 +00:00
if ( citem = = opt_ic_confweap )
2015-11-01 21:15:38 +00:00
kconfig ( kconfig_type : : rebirth ) ;
2015-09-25 21:59:20 +00:00
if ( citem = = opt_ic_keyboard )
input_config_keyboard ( ) ;
if ( citem = = opt_ic_mouse )
input_config_mouse ( ) ;
2016-09-25 04:52:49 +00:00
# if DXX_MAX_AXES_PER_JOYSTICK
2015-09-25 21:59:20 +00:00
if ( citem = = opt_ic_joystick )
input_config_joystick ( ) ;
2015-11-01 21:15:40 +00:00
# endif
2010-12-28 18:11:10 +00:00
if ( citem = = opt_ic_help0 )
show_help ( ) ;
if ( citem = = opt_ic_help1 )
show_netgame_help ( ) ;
if ( citem = = opt_ic_help2 )
show_newdemo_help ( ) ;
2010-01-30 03:24:19 +00:00
return 1 ; // stay in menu
2015-02-28 22:34:07 +00:00
}
2010-06-14 08:13:16 +00:00
2010-01-17 14:42:59 +00:00
default :
2010-01-30 03:24:19 +00:00
break ;
2008-02-24 14:41:27 +00:00
}
2010-06-14 08:13:16 +00:00
2010-01-30 03:24:19 +00:00
return 0 ;
2008-02-24 14:41:27 +00:00
}
void input_config ( )
{
2015-05-14 02:23:13 +00:00
input_config_menu_items menu_items ;
2015-11-01 21:15:40 +00:00
newmenu_do1 ( nullptr , TXT_CONTROLS , menu_items . m . size ( ) , menu_items . m . data ( ) , & input_config_menu_items : : menuset , & menu_items , menu_items . opt_ic_confkey ) ;
2008-02-24 14:41:27 +00:00
}
2013-10-27 22:00:14 +00:00
static void reticle_config ( )
2009-05-03 12:31:30 +00:00
{
2016-09-24 18:06:11 +00:00
# if DXX_USE_OGL
2015-05-14 02:23:13 +00:00
# define DXX_RETICLE_TYPE_OGL(VERB) \
2016-01-09 16:38:17 +00:00
DXX_MENUITEM ( VERB , RADIO , " Classic Reboot " , opt_reticle_classic_reboot , 0 , optgrp_reticle )
2011-01-10 15:12:04 +00:00
# else
2015-05-14 02:23:13 +00:00
# define DXX_RETICLE_TYPE_OGL(VERB)
2011-01-10 15:12:04 +00:00
# endif
2015-05-14 02:23:13 +00:00
# define DXX_RETICLE_CONFIG_MENU(VERB) \
2016-01-09 16:38:17 +00:00
DXX_MENUITEM ( VERB , TEXT , " Reticle Type: " , opt_label_reticle_type ) \
DXX_MENUITEM ( VERB , RADIO , " Classic " , opt_reticle_classic , 0 , optgrp_reticle ) \
2015-05-14 02:23:13 +00:00
DXX_RETICLE_TYPE_OGL ( VERB ) \
2016-01-09 16:38:17 +00:00
DXX_MENUITEM ( VERB , RADIO , " None " , opt_reticle_none , 0 , optgrp_reticle ) \
DXX_MENUITEM ( VERB , RADIO , " X " , opt_reticle_x , 0 , optgrp_reticle ) \
DXX_MENUITEM ( VERB , RADIO , " Dot " , opt_reticle_dot , 0 , optgrp_reticle ) \
DXX_MENUITEM ( VERB , RADIO , " Circle " , opt_reticle_circle , 0 , optgrp_reticle ) \
DXX_MENUITEM ( VERB , RADIO , " Cross V1 " , opt_reticle_cross1 , 0 , optgrp_reticle ) \
DXX_MENUITEM ( VERB , RADIO , " Cross V2 " , opt_reticle_cross2 , 0 , optgrp_reticle ) \
DXX_MENUITEM ( VERB , RADIO , " Angle " , opt_reticle_angle , 0 , optgrp_reticle ) \
DXX_MENUITEM ( VERB , TEXT , " " , opt_label_blank_reticle_type ) \
DXX_MENUITEM ( VERB , TEXT , " Reticle Color: " , opt_label_reticle_color ) \
DXX_MENUITEM ( VERB , SCALE_SLIDER , " Red " , opt_reticle_color_red , PlayerCfg . ReticleRGBA [ 0 ] , 0 , 16 , 2 ) \
DXX_MENUITEM ( VERB , SCALE_SLIDER , " Green " , opt_reticle_color_green , PlayerCfg . ReticleRGBA [ 1 ] , 0 , 16 , 2 ) \
DXX_MENUITEM ( VERB , SCALE_SLIDER , " Blue " , opt_reticle_color_blue , PlayerCfg . ReticleRGBA [ 2 ] , 0 , 16 , 2 ) \
DXX_MENUITEM ( VERB , SCALE_SLIDER , " Alpha " , opt_reticle_color_alpha , PlayerCfg . ReticleRGBA [ 3 ] , 0 , 16 , 2 ) \
DXX_MENUITEM ( VERB , TEXT , " " , opt_label_blank_reticle_color ) \
DXX_MENUITEM ( VERB , SLIDER , " Reticle Size: " , opt_label_reticle_size , PlayerCfg . ReticleSize , 0 , 4 ) \
2009-05-03 12:31:30 +00:00
2015-05-14 02:23:13 +00:00
class menu_items
{
public :
enum
{
optgrp_reticle ,
} ;
enum
{
DXX_RETICLE_CONFIG_MENU ( ENUM )
} ;
array < newmenu_item , DXX_RETICLE_CONFIG_MENU ( COUNT ) > m ;
menu_items ( )
{
DXX_RETICLE_CONFIG_MENU ( ADD ) ;
}
void read ( )
{
DXX_RETICLE_CONFIG_MENU ( READ ) ;
}
} ;
# undef DXX_RETICLE_CONFIG_MENU
# undef DXX_RETICLE_TYPE_OGL
menu_items items ;
2016-02-12 04:02:28 +00:00
{
2015-05-14 02:23:13 +00:00
auto i = PlayerCfg . ReticleType ;
2016-09-24 18:06:11 +00:00
# if !DXX_USE_OGL
2011-01-10 15:12:04 +00:00
if ( i > 1 ) i - - ;
# endif
2015-05-14 02:23:13 +00:00
items . m [ items . opt_reticle_classic + i ] . value = 1 ;
2016-02-12 04:02:28 +00:00
}
2011-01-10 15:12:04 +00:00
2015-09-26 17:48:57 +00:00
newmenu_do1 ( nullptr , " Reticle Customization " , items . m . size ( ) , items . m . data ( ) , unused_newmenu_subfunction , unused_newmenu_userdata , 1 ) ;
2011-01-10 15:12:04 +00:00
2015-05-14 02:23:13 +00:00
for ( uint_fast32_t i = items . opt_reticle_classic ; i ! = items . opt_label_blank_reticle_type ; + + i )
if ( items . m [ i ] . value )
{
2016-09-24 18:06:11 +00:00
# if !DXX_USE_OGL
2015-05-14 02:23:13 +00:00
if ( i ! = items . opt_reticle_classic )
+ + i ;
2011-01-10 15:12:04 +00:00
# endif
2015-05-14 02:23:13 +00:00
PlayerCfg . ReticleType = i - items . opt_reticle_classic ;
break ;
}
items . read ( ) ;
2011-01-10 15:12:04 +00:00
}
2015-09-26 17:48:57 +00:00
# if defined(DXX_BUILD_DESCENT_I)
# define DXX_GAME_SPECIFIC_HUDOPTIONS(VERB) \
2016-01-09 16:38:17 +00:00
DXX_MENUITEM ( VERB , CHECK , " Always-on Bomb Counter " , opt_d2bomb , PlayerCfg . BombGauge ) \
2015-09-26 17:48:57 +00:00
# elif defined(DXX_BUILD_DESCENT_II)
2015-09-26 21:17:14 +00:00
enum {
optgrp_missileview ,
} ;
2015-09-26 17:48:57 +00:00
# define DXX_GAME_SPECIFIC_HUDOPTIONS(VERB) \
2016-01-09 16:38:17 +00:00
DXX_MENUITEM ( VERB , TEXT , " Missile view: " , opt_missileview_label ) \
DXX_MENUITEM ( VERB , RADIO , " Disabled " , opt_missileview_none , PlayerCfg . MissileViewEnabled = = MissileViewMode : : None , optgrp_missileview ) \
DXX_MENUITEM ( VERB , RADIO , " Only own missiles " , opt_missileview_selfonly , PlayerCfg . MissileViewEnabled = = MissileViewMode : : EnabledSelfOnly , optgrp_missileview ) \
DXX_MENUITEM ( VERB , RADIO , " Friendly missiles, preferring self " , opt_missileview_selfandallies , PlayerCfg . MissileViewEnabled = = MissileViewMode : : EnabledSelfAndAllies , optgrp_missileview ) \
DXX_MENUITEM ( VERB , CHECK , " Show guided missile in main display " , opt_guidedbigview , PlayerCfg . GuidedInBigWindow ) \
2015-09-26 17:48:57 +00:00
# endif
# define DXX_HUD_MENU_OPTIONS(VERB) \
2016-01-09 16:38:17 +00:00
DXX_MENUITEM ( VERB , MENU , " Reticle Customization... " , opt_hud_reticlemenu ) \
DXX_MENUITEM ( VERB , CHECK , " Screenshots without HUD " , opt_screenshot , PlayerCfg . PRShot ) \
DXX_MENUITEM ( VERB , CHECK , " No redundant pickup messages " , opt_redundant , PlayerCfg . NoRedundancy ) \
DXX_MENUITEM ( VERB , CHECK , " Show Player chat only (Multi) " , opt_playerchat , PlayerCfg . MultiMessages ) \
2016-03-30 18:09:46 +00:00
DXX_MENUITEM ( VERB , CHECK , " Show Player ping (Multi) " , opt_playerping , PlayerCfg . MultiPingHud ) \
2016-01-09 16:38:17 +00:00
DXX_MENUITEM ( VERB , CHECK , " Cloak/Invulnerability Timers " , opt_cloakinvultimer , PlayerCfg . CloakInvulTimer ) \
2015-09-26 17:48:57 +00:00
DXX_GAME_SPECIFIC_HUDOPTIONS ( VERB ) \
enum {
DXX_HUD_MENU_OPTIONS ( ENUM )
} ;
static int hud_config_menuset ( newmenu * , const d_event & event , const unused_newmenu_userdata_t * )
{
switch ( event . type )
{
case EVENT_NEWMENU_SELECTED :
{
auto & citem = static_cast < const d_select_event & > ( event ) . citem ;
if ( citem = = opt_hud_reticlemenu )
reticle_config ( ) ;
return 1 ; // stay in menu
}
default :
break ;
}
return 0 ;
}
2016-08-25 04:05:32 +00:00
namespace dsx {
2015-09-26 17:48:57 +00:00
void hud_config ( )
{
2019-03-10 18:48:40 +00:00
for ( ; ; )
{
array < newmenu_item , DXX_HUD_MENU_OPTIONS ( COUNT ) > m ;
2015-09-26 17:48:57 +00:00
DXX_HUD_MENU_OPTIONS ( ADD ) ;
2019-03-10 18:48:40 +00:00
const auto i = newmenu_do1 ( NULL , " Hud Options " , m . size ( ) , m . data ( ) , hud_config_menuset , unused_newmenu_userdata , 0 ) ;
2015-09-26 17:48:57 +00:00
DXX_HUD_MENU_OPTIONS ( READ ) ;
2015-09-26 21:17:14 +00:00
# if defined(DXX_BUILD_DESCENT_II)
PlayerCfg . MissileViewEnabled = m [ opt_missileview_selfandallies ] . value
? MissileViewMode : : EnabledSelfAndAllies
: ( m [ opt_missileview_selfonly ] . value
? MissileViewMode : : EnabledSelfOnly
: MissileViewMode : : None ) ;
# endif
2019-03-10 18:48:40 +00:00
if ( i = = - 1 )
break ;
}
2015-09-26 17:48:57 +00:00
}
2016-08-25 04:05:32 +00:00
}
2015-09-26 17:48:57 +00:00
2015-03-07 17:20:41 +00:00
# define DXX_GRAPHICS_MENU(VERB) \
2016-01-09 16:38:17 +00:00
DXX_MENUITEM ( VERB , MENU , " Screen resolution... " , opt_gr_screenres ) \
DXX_MENUITEM ( VERB , MENU , " HUD Options... " , opt_gr_hudmenu ) \
DXX_MENUITEM ( VERB , SLIDER , TXT_BRIGHTNESS , opt_gr_brightness , gr_palette_get_gamma ( ) , 0 , 16 ) \
DXX_MENUITEM ( VERB , TEXT , " " , blank1 ) \
2015-09-26 17:48:57 +00:00
DXX_OGL0_GRAPHICS_MENU ( VERB ) \
2015-03-07 17:20:41 +00:00
DXX_OGL1_GRAPHICS_MENU ( VERB ) \
2016-10-02 19:35:33 +00:00
DXX_MENUITEM ( VERB , CHECK , " FPS Counter " , opt_gr_fpsindi , CGameCfg . FPSIndicator ) \
2015-02-28 22:34:07 +00:00
2016-09-24 18:06:11 +00:00
# if DXX_USE_OGL
2015-03-07 17:20:41 +00:00
enum {
optgrp_texfilt ,
} ;
# define DXX_OGL0_GRAPHICS_MENU(VERB) \
2016-01-09 16:38:17 +00:00
DXX_MENUITEM ( VERB , TEXT , " Texture Filtering: " , opt_gr_texfilt ) \
2016-08-29 18:53:10 +00:00
DXX_MENUITEM ( VERB , RADIO , " Classic " , opt_filter_none , 0 , optgrp_texfilt ) \
DXX_MENUITEM ( VERB , RADIO , " Blocky Filtered " , opt_filter_upscale , 0 , optgrp_texfilt ) \
DXX_MENUITEM ( VERB , RADIO , " Smooth " , opt_filter_trilinear , 0 , optgrp_texfilt ) \
DXX_MENUITEM ( VERB , CHECK , " Anisotropic Filtering " , opt_filter_anisotropy , CGameCfg . TexAnisotropy ) \
2015-03-07 17:20:41 +00:00
D2X_OGL_GRAPHICS_MENU ( VERB ) \
2016-01-09 16:38:17 +00:00
DXX_MENUITEM ( VERB , TEXT , " " , blank2 ) \
2015-03-07 17:20:41 +00:00
# define DXX_OGL1_GRAPHICS_MENU(VERB) \
2016-01-09 16:38:17 +00:00
DXX_MENUITEM ( VERB , CHECK , " Transparency Effects " , opt_gr_alphafx , PlayerCfg . AlphaEffects ) \
DXX_MENUITEM ( VERB , CHECK , " Colored Dynamic Light " , opt_gr_dynlightcolor , PlayerCfg . DynLightColor ) \
DXX_MENUITEM ( VERB , CHECK , " VSync " , opt_gr_vsync , CGameCfg . VSync ) \
2016-10-02 19:35:33 +00:00
DXX_MENUITEM ( VERB , CHECK , " 4x multisampling " , opt_gr_multisample , CGameCfg . Multisample ) \
2015-03-07 17:20:41 +00:00
# if defined(DXX_BUILD_DESCENT_I)
# define D2X_OGL_GRAPHICS_MENU(VERB)
# elif defined(DXX_BUILD_DESCENT_II)
# define D2X_OGL_GRAPHICS_MENU(VERB) \
2016-08-29 18:53:10 +00:00
DXX_MENUITEM ( VERB , CHECK , " Cutscene Smoothing " , opt_gr_movietexfilt , GameCfg . MovieTexFilt )
2013-03-03 01:03:33 +00:00
# endif
2015-02-28 22:34:07 +00:00
2015-03-07 17:20:41 +00:00
# else
# define DXX_OGL0_GRAPHICS_MENU(VERB)
# define DXX_OGL1_GRAPHICS_MENU(VERB)
# endif
enum {
DXX_GRAPHICS_MENU ( ENUM )
} ;
2015-02-28 22:34:07 +00:00
2015-05-09 17:39:02 +00:00
static int graphics_config_menuset ( newmenu * , const d_event & event , newmenu_item * const items )
2011-01-10 15:12:04 +00:00
{
2014-10-04 21:47:13 +00:00
switch ( event . type )
2011-01-10 15:12:04 +00:00
{
case EVENT_NEWMENU_CHANGED :
2015-02-28 22:34:07 +00:00
{
auto & citem = static_cast < const d_change_event & > ( event ) . citem ;
2015-05-09 17:39:02 +00:00
if ( citem = = opt_gr_brightness )
gr_palette_set_gamma ( items [ citem ] . value ) ;
2016-09-24 18:06:11 +00:00
# if DXX_USE_OGL
2015-05-09 17:39:02 +00:00
else
2016-08-29 18:53:10 +00:00
if ( citem = = opt_filter_anisotropy & & ogl_maxanisotropy < = 1.0 )
2011-01-16 00:50:42 +00:00
{
nm_messagebox ( TXT_ERROR , 1 , TXT_OK , " Anisotropic Filtering not \n supported by your hardware/driver. " ) ;
2016-08-29 18:53:10 +00:00
items [ opt_filter_anisotropy ] . value = 0 ;
2011-01-16 00:50:42 +00:00
}
2015-03-11 02:19:15 +00:00
# endif
2011-01-10 15:12:04 +00:00
break ;
2015-02-28 22:34:07 +00:00
}
2011-01-10 15:12:04 +00:00
case EVENT_NEWMENU_SELECTED :
2015-02-28 22:34:07 +00:00
{
auto & citem = static_cast < const d_select_event & > ( event ) . citem ;
2015-09-26 17:48:57 +00:00
if ( citem = = opt_gr_screenres )
change_res ( ) ;
if ( citem = = opt_gr_hudmenu )
hud_config ( ) ;
2011-01-10 15:12:04 +00:00
return 1 ; // stay in menu
2015-02-28 22:34:07 +00:00
}
2011-01-10 15:12:04 +00:00
default :
break ;
}
return 0 ;
}
2016-08-25 04:05:32 +00:00
namespace dsx {
2011-01-10 15:12:04 +00:00
void graphics_config ( )
{
2015-03-07 17:20:41 +00:00
array < newmenu_item , DXX_GRAPHICS_MENU ( COUNT ) > m ;
DXX_GRAPHICS_MENU ( ADD ) ;
2011-01-10 15:12:04 +00:00
2016-09-24 18:06:11 +00:00
# if DXX_USE_OGL
2015-12-18 04:08:23 +00:00
m [ opt_filter_none + CGameCfg . TexFilt ] . value = 1 ;
2011-01-10 15:12:04 +00:00
# endif
2015-09-26 17:48:57 +00:00
newmenu_do1 ( nullptr , " Graphics Options " , m . size ( ) , m . data ( ) , graphics_config_menuset , m . data ( ) , 0 ) ;
2011-01-10 15:12:04 +00:00
2016-09-24 18:06:11 +00:00
# if DXX_USE_OGL
2016-10-02 19:35:33 +00:00
if ( CGameCfg . VSync ! = m [ opt_gr_vsync ] . value | | CGameCfg . Multisample ! = m [ opt_gr_multisample ] . value )
2013-06-10 23:07:12 +00:00
nm_messagebox ( NULL , 1 , TXT_OK , " Setting VSync or 4x Multisample \n requires restart on some systems. " ) ;
2011-01-10 15:12:04 +00:00
2019-05-04 18:27:36 +00:00
range_for ( const uint_fast32_t i , xrange ( 3u ) )
2015-04-07 01:55:57 +00:00
if ( m [ i + opt_filter_none ] . value )
2015-03-07 17:20:41 +00:00
{
2015-12-18 04:08:23 +00:00
CGameCfg . TexFilt = i ;
2015-03-07 17:20:41 +00:00
break ;
}
2016-08-29 18:53:10 +00:00
CGameCfg . TexAnisotropy = m [ opt_filter_anisotropy ] . value ;
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2011-01-10 15:12:04 +00:00
GameCfg . MovieTexFilt = m [ opt_gr_movietexfilt ] . value ;
2013-03-03 01:03:33 +00:00
# endif
2011-02-23 16:46:39 +00:00
PlayerCfg . AlphaEffects = m [ opt_gr_alphafx ] . value ;
2011-04-07 20:32:51 +00:00
PlayerCfg . DynLightColor = m [ opt_gr_dynlightcolor ] . value ;
2015-07-18 03:49:47 +00:00
CGameCfg . VSync = m [ opt_gr_vsync ] . value ;
2016-10-02 19:35:33 +00:00
CGameCfg . Multisample = m [ opt_gr_multisample ] . value ;
2011-01-10 15:12:04 +00:00
# endif
GameCfg . GammaLevel = m [ opt_gr_brightness ] . value ;
2016-10-02 19:35:33 +00:00
CGameCfg . FPSIndicator = m [ opt_gr_fpsindi ] . value ;
2016-09-24 18:06:11 +00:00
# if DXX_USE_OGL
2011-01-10 15:12:04 +00:00
gr_set_attributes ( ) ;
gr_set_mode ( Game_screen_mode ) ;
# endif
2009-05-03 12:31:30 +00:00
}
2016-08-25 04:05:32 +00:00
}
2009-05-03 12:31:30 +00:00
2010-10-10 10:48:24 +00:00
# if PHYSFS_VER_MAJOR >= 2
2015-04-22 02:44:30 +00:00
namespace {
2013-12-22 22:03:07 +00:00
struct browser
2010-08-27 14:09:19 +00:00
{
2016-01-29 04:05:47 +00:00
browser ( const partial_range_t < const file_extension_t * > & r ) :
ext_range ( r )
{
}
2013-09-02 23:21:13 +00:00
const char * title ; // The title - needed for making another listbox when changing directory
2016-10-27 07:22:41 +00:00
window_event_result ( * when_selected ) ( void * userdata , const char * filename ) ; // What to do when something chosen
2010-08-27 14:09:19 +00:00
void * userdata ; // Whatever you want passed to when_selected
2014-09-28 23:28:56 +00:00
string_array_t list ;
2016-01-29 04:05:47 +00:00
// List of file extensions we're looking for (if looking for a music file many types are possible)
const partial_range_t < const file_extension_t * > ext_range ;
2010-08-27 14:09:19 +00:00
int select_dir ; // Allow selecting the current directory (e.g. for Jukebox level song directory)
int new_path ; // Whether the view_path is a new searchpath, if so, remove it when finished
2015-02-14 22:48:28 +00:00
char view_path [ PATH_MAX ] ; // The absolute path we're currently looking at
2013-12-22 22:03:07 +00:00
} ;
2010-08-27 14:09:19 +00:00
2015-04-22 02:44:30 +00:00
}
2014-10-17 01:59:12 +00:00
static void list_dir_el ( void * vb , const char * , const char * fname )
2010-08-27 14:09:19 +00:00
{
2014-10-17 01:59:12 +00:00
browser * b = reinterpret_cast < browser * > ( vb ) ;
2014-12-20 04:36:11 +00:00
const char * r = PHYSFS_getRealDir ( fname ) ;
if ( ! r )
r = " " ;
2016-01-29 04:05:47 +00:00
if ( ! strcmp ( r , b - > view_path ) & & ( PHYSFS_isDirectory ( fname ) | | PHYSFSX_checkMatchingExtension ( fname , b - > ext_range ) )
2015-09-13 20:23:05 +00:00
# if defined(__APPLE__) && defined(__MACH__)
2012-05-18 23:36:43 +00:00
& & d_stricmp ( fname , " Volumes " ) // this messes things up, use '..' instead
2010-08-27 14:09:19 +00:00
# endif
)
2014-09-28 23:28:56 +00:00
b - > list . add ( fname ) ;
2010-08-27 14:09:19 +00:00
}
2013-10-27 22:00:14 +00:00
static int list_directory ( browser * b )
2010-08-27 14:09:19 +00:00
{
2014-09-28 23:28:56 +00:00
b - > list . clear ( ) ;
b - > list . add ( " .. " ) ; // go to parent directory
2010-08-27 14:09:19 +00:00
if ( b - > select_dir )
{
2014-09-28 23:28:56 +00:00
b - > list . add ( " <this directory> " ) ; // choose the directory being viewed
2010-08-27 14:09:19 +00:00
}
2014-10-17 01:59:12 +00:00
PHYSFS_enumerateFilesCallback ( " " , list_dir_el , b ) ;
2014-09-28 23:28:56 +00:00
b - > list . tidy ( 1 + ( b - > select_dir ? 1 : 0 ) ,
2014-09-02 22:10:34 +00:00
# ifdef __linux__
2010-09-26 13:15:20 +00:00
strcmp
2010-08-27 14:09:19 +00:00
# else
2012-05-18 23:36:43 +00:00
d_stricmp
2010-08-27 14:09:19 +00:00
# endif
2010-09-26 13:15:20 +00:00
) ;
2010-08-27 14:09:19 +00:00
return 1 ;
}
2016-10-27 07:22:41 +00:00
static window_event_result select_file_handler ( listbox * menu , const d_event & event , browser * b )
2010-08-27 14:09:19 +00:00
{
char newpath [ PATH_MAX ] ;
2013-09-02 23:21:13 +00:00
const char * * list = listbox_get_items ( menu ) ;
2010-08-27 14:09:19 +00:00
const char * sep = PHYSFS_getDirSeparator ( ) ;
2010-08-27 20:32:05 +00:00
memset ( newpath , 0 , sizeof ( char ) * PATH_MAX ) ;
2014-10-04 21:47:13 +00:00
switch ( event . type )
2010-08-27 14:09:19 +00:00
{
2010-08-31 13:25:45 +00:00
# ifdef _WIN32
case EVENT_KEY_COMMAND :
{
2011-01-14 09:51:13 +00:00
if ( event_key_get ( event ) = = KEY_CTRLED + KEY_D )
2010-08-31 13:25:45 +00:00
{
2013-12-08 16:53:10 +00:00
char text [ 4 ] = " c " ;
2010-08-31 13:25:45 +00:00
int rval = 0 ;
2015-04-02 02:36:52 +00:00
array < newmenu_item , 1 > m { {
2015-01-18 01:58:31 +00:00
nm_item_input ( text ) ,
2015-04-02 02:36:52 +00:00
} } ;
2015-01-18 01:58:32 +00:00
rval = newmenu_do ( NULL , " Enter drive letter " , m , unused_newmenu_subfunction , unused_newmenu_userdata ) ;
2010-08-31 13:25:45 +00:00
text [ 1 ] = ' \0 ' ;
snprintf ( newpath , sizeof ( char ) * PATH_MAX , " %s:%s " , text , sep ) ;
2013-12-03 23:21:36 +00:00
if ( ! rval & & text [ 0 ] )
2010-08-31 13:25:45 +00:00
{
2016-01-29 04:05:47 +00:00
select_file_recursive ( b - > title , newpath , b - > ext_range , b - > select_dir , b - > when_selected , b - > userdata ) ;
2010-08-31 13:25:45 +00:00
// close old box.
2016-10-27 07:22:41 +00:00
return window_event_result : : close ;
2010-08-31 13:25:45 +00:00
}
2016-10-27 07:22:41 +00:00
return window_event_result : : handled ;
2010-08-31 13:25:45 +00:00
}
break ;
}
# endif
2010-08-27 14:09:19 +00:00
case EVENT_NEWMENU_SELECTED :
2015-02-28 22:34:07 +00:00
{
auto & citem = static_cast < const d_select_event & > ( event ) . citem ;
2010-08-27 14:09:19 +00:00
strcpy ( newpath , b - > view_path ) ;
if ( citem = = 0 ) // go to parent dir
{
char * p ;
2013-12-03 23:21:36 +00:00
size_t len_newpath = strlen ( newpath ) ;
size_t len_sep = strlen ( sep ) ;
if ( ( p = strstr ( & newpath [ len_newpath - len_sep ] , sep ) ) )
2010-08-27 14:09:19 +00:00
if ( p ! = strstr ( newpath , sep ) ) // if this isn't the only separator (i.e. it's not about to look at the root)
* p = 0 ;
2013-12-03 23:21:36 +00:00
p = newpath + len_newpath - 1 ;
while ( ( p > newpath ) & & strncmp ( p , sep , len_sep ) ) // make sure full separator string is matched (typically is)
2010-08-27 14:09:19 +00:00
p - - ;
if ( p = = strstr ( newpath , sep ) ) // Look at root directory next, if not already
{
2015-09-13 20:23:05 +00:00
# if defined(__APPLE__) && defined(__MACH__)
2012-05-18 23:36:43 +00:00
if ( ! d_stricmp ( p , " /Volumes " ) )
2016-10-27 07:22:41 +00:00
return window_event_result : : handled ;
2010-08-27 14:09:19 +00:00
# endif
2013-12-03 23:21:36 +00:00
if ( p [ len_sep ] ! = ' \0 ' )
p [ len_sep ] = ' \0 ' ;
2010-08-27 14:09:19 +00:00
else
{
2015-09-13 20:23:05 +00:00
# if defined(__APPLE__) && defined(__MACH__)
2010-08-27 14:09:19 +00:00
// For Mac OS X, list all active volumes if we leave the root
strcpy ( newpath , " /Volumes " ) ;
# else
2016-10-27 07:22:41 +00:00
return window_event_result : : handled ;
2010-08-27 14:09:19 +00:00
# endif
}
}
else
* p = ' \0 ' ;
}
else if ( citem = = 1 & & b - > select_dir )
2016-10-27 07:22:41 +00:00
return ( * b - > when_selected ) ( b - > userdata , " " ) ;
2010-08-27 14:09:19 +00:00
else
{
2013-12-03 23:21:36 +00:00
size_t len_newpath = strlen ( newpath ) ;
size_t len_sep = strlen ( sep ) ;
if ( strncmp ( & newpath [ len_newpath - len_sep ] , sep , len_sep ) )
2010-08-27 14:09:19 +00:00
{
2013-12-03 23:21:36 +00:00
strncat ( newpath , sep , PATH_MAX - 1 - len_newpath ) ;
2010-08-27 14:09:19 +00:00
newpath [ PATH_MAX - 1 ] = ' \0 ' ;
}
2013-12-03 23:21:36 +00:00
strncat ( newpath , list [ citem ] , PATH_MAX - 1 - len_newpath ) ;
2010-08-27 14:09:19 +00:00
newpath [ PATH_MAX - 1 ] = ' \0 ' ;
}
if ( ( citem = = 0 ) | | PHYSFS_isDirectory ( list [ citem ] ) )
{
// If it fails, stay in this one
2016-10-27 07:22:41 +00:00
return select_file_recursive ( b - > title , newpath , b - > ext_range , b - > select_dir , b - > when_selected , b - > userdata ) ? window_event_result : : close : window_event_result : : handled ;
2010-08-27 14:09:19 +00:00
}
2016-10-27 07:22:41 +00:00
return ( * b - > when_selected ) ( b - > userdata , list [ citem ] ) ;
2015-02-28 22:34:07 +00:00
}
2010-08-27 14:09:19 +00:00
case EVENT_WINDOW_CLOSE :
if ( b - > new_path )
PHYSFS_removeFromSearchPath ( b - > view_path ) ;
2014-12-22 04:35:47 +00:00
std : : default_delete < browser > ( ) ( b ) ;
2010-08-27 14:09:19 +00:00
break ;
default :
break ;
}
2016-10-27 07:22:41 +00:00
return window_event_result : : ignored ;
2010-08-27 14:09:19 +00:00
}
2016-03-06 18:11:20 +00:00
static int select_file_recursive2 ( const char * title , const char * orig_path , const partial_range_t < const file_extension_t * > & ext_range , int select_dir , select_file_subfunction < void > when_selected , void * userdata )
2010-08-27 14:09:19 +00:00
{
const char * sep = PHYSFS_getDirSeparator ( ) ;
char * p ;
2016-08-06 19:55:25 +00:00
array < char , PATH_MAX > new_path ;
2010-08-27 14:09:19 +00:00
2016-01-29 04:05:47 +00:00
auto b = make_unique < browser > ( ext_range ) ;
2010-08-27 14:09:19 +00:00
b - > title = title ;
b - > when_selected = when_selected ;
b - > userdata = userdata ;
b - > select_dir = select_dir ;
b - > view_path [ 0 ] = ' \0 ' ;
b - > new_path = 1 ;
2011-04-18 12:32:36 +00:00
// Check for a PhysicsFS path first, saves complication!
2016-09-26 00:50:09 +00:00
if ( strncmp ( orig_path , sep , strlen ( sep ) ) & & PHYSFSX_exists ( orig_path , 0 ) )
2011-04-18 12:32:36 +00:00
{
PHYSFSX_getRealPath ( orig_path , new_path ) ;
2016-08-06 19:55:25 +00:00
orig_path = new_path . data ( ) ;
2011-04-18 12:32:36 +00:00
}
2010-08-27 14:09:19 +00:00
// Set the viewing directory to orig_path, or some parent of it
2010-08-28 02:46:54 +00:00
if ( orig_path )
2010-08-27 14:09:19 +00:00
{
2010-08-28 02:46:54 +00:00
// Must make this an absolute path for directory browsing to work properly
2010-08-31 13:25:45 +00:00
# ifdef _WIN32
2010-08-28 02:46:54 +00:00
if ( ! ( isalpha ( orig_path [ 0 ] ) & & ( orig_path [ 1 ] = = ' : ' ) ) ) // drive letter prompt (e.g. "C:"
# elif defined(macintosh)
if ( orig_path [ 0 ] = = ' : ' )
# else
if ( orig_path [ 0 ] ! = ' / ' )
# endif
{
strncpy ( b - > view_path , PHYSFS_getBaseDir ( ) , PATH_MAX - 1 ) ; // current write directory must be set to base directory
b - > view_path [ PATH_MAX - 1 ] = ' \0 ' ;
# ifdef macintosh
orig_path + + ; // go past ':'
# endif
strncat ( b - > view_path , orig_path , PATH_MAX - 1 - strlen ( b - > view_path ) ) ;
b - > view_path [ PATH_MAX - 1 ] = ' \0 ' ;
}
else
{
strncpy ( b - > view_path , orig_path , PATH_MAX - 1 ) ;
b - > view_path [ PATH_MAX - 1 ] = ' \0 ' ;
}
2010-08-27 14:09:19 +00:00
p = b - > view_path + strlen ( b - > view_path ) - 1 ;
2016-08-06 19:55:26 +00:00
const size_t len_sep = strlen ( sep ) ;
while ( b - > new_path = PHYSFSX_isNewPath ( b - > view_path ) , ! PHYSFS_addToSearchPath ( b - > view_path , 0 ) )
2010-08-27 14:09:19 +00:00
{
2013-12-03 23:21:36 +00:00
while ( ( p > b - > view_path ) & & strncmp ( p , sep , len_sep ) )
2010-08-27 14:09:19 +00:00
p - - ;
* p = ' \0 ' ;
if ( p = = b - > view_path )
break ;
}
}
// Set to user directory if we couldn't find a searchpath
if ( ! b - > view_path [ 0 ] )
{
strncpy ( b - > view_path , PHYSFS_getUserDir ( ) , PATH_MAX - 1 ) ;
b - > view_path [ PATH_MAX - 1 ] = ' \0 ' ;
2010-11-21 11:55:35 +00:00
b - > new_path = PHYSFSX_isNewPath ( b - > view_path ) ;
2010-08-27 14:09:19 +00:00
if ( ! PHYSFS_addToSearchPath ( b - > view_path , 0 ) )
{
return 0 ;
}
}
2014-09-28 23:28:56 +00:00
if ( ! list_directory ( b . get ( ) ) )
2010-08-27 14:09:19 +00:00
{
return 0 ;
}
2014-12-22 04:35:47 +00:00
auto pb = b . get ( ) ;
2015-01-17 18:31:42 +00:00
return newmenu_listbox1 ( title , pb - > list . pointer ( ) . size ( ) , & pb - > list . pointer ( ) . front ( ) , 1 , 0 , select_file_handler , std : : move ( b ) ) ! = NULL ;
2010-08-27 14:09:19 +00:00
}
2016-01-09 16:38:10 +00:00
# define DXX_MENU_ITEM_BROWSE(VERB, TXT, OPT) \
2016-01-09 16:38:17 +00:00
DXX_MENUITEM ( VERB , MENU , TXT " (browse...) " , OPT )
2010-10-10 10:48:24 +00:00
# else
2016-03-06 18:11:20 +00:00
int select_file_recursive2 ( const char * title , const char * orig_path , const partial_range_t < const file_extension_t * > & ext_range , int select_dir , int ( * when_selected ) ( void * userdata , const char * filename ) , void * userdata )
2010-10-10 10:48:24 +00:00
{
return 0 ;
}
2016-01-09 16:38:10 +00:00
/* Include blank string to force a compile error if TXT cannot be
* string - pasted
*/
# define DXX_MENU_ITEM_BROWSE(VERB, TXT, OPT) \
2016-01-09 16:38:17 +00:00
DXX_MENUITEM ( VERB , TEXT , TXT " " , OPT )
2010-10-10 10:48:24 +00:00
# endif
2016-09-11 18:49:15 +00:00
# if DXX_USE_SDLMIXER
2016-10-27 07:22:41 +00:00
static window_event_result get_absolute_path ( char * full_path , const char * rel_path )
2010-08-27 14:09:19 +00:00
{
2015-10-11 22:21:00 +00:00
PHYSFSX_getRealPath ( rel_path , full_path , PATH_MAX ) ;
2016-10-27 07:22:41 +00:00
return window_event_result : : close ;
2010-08-27 14:09:19 +00:00
}
2016-08-06 19:55:25 +00:00
# define SELECT_SONG(t, s) select_file_recursive(t, CGameCfg.CMMiscMusic[s].data(), jukebox_exts, 0, get_absolute_path, CGameCfg.CMMiscMusic[s].data())
2010-10-14 09:50:11 +00:00
# endif
2010-08-27 14:09:19 +00:00
2016-01-09 16:38:10 +00:00
namespace {
# if defined(DXX_BUILD_DESCENT_I)
# define REDBOOK_PLAYORDER_TEXT "force mac cd track order"
# elif defined(DXX_BUILD_DESCENT_II)
# define REDBOOK_PLAYORDER_TEXT "force descent ][ cd track order"
# endif
2016-09-11 18:49:15 +00:00
# if DXX_USE_SDLMIXER || defined(_WIN32)
2016-01-09 16:38:10 +00:00
# define DXX_SOUND_ADDON_MUSIC_MENU_ITEM(VERB) \
2016-01-09 16:38:17 +00:00
DXX_MENUITEM ( VERB , RADIO , " Built-in/Addon music " , opt_sm_mtype1 , GameCfg . MusicType = = MUSIC_TYPE_BUILTIN , optgrp_music_type ) \
2016-01-09 16:38:10 +00:00
# else
# define DXX_SOUND_ADDON_MUSIC_MENU_ITEM(VERB)
# endif
# if SDL_MAJOR_VERSION == 1
# define DXX_SOUND_CD_MUSIC_MENU_ITEM(VERB) \
2016-01-09 16:38:17 +00:00
DXX_MENUITEM ( VERB , RADIO , " CD music " , opt_sm_mtype2 , GameCfg . MusicType = = MUSIC_TYPE_REDBOOK , optgrp_music_type ) \
2016-01-09 16:38:10 +00:00
# define DXX_MUSIC_OPTIONS_CD_LABEL "CD music"
# else
# define DXX_SOUND_CD_MUSIC_MENU_ITEM(VERB)
# define DXX_MUSIC_OPTIONS_CD_LABEL ""
# endif
2016-09-11 18:49:15 +00:00
# if DXX_USE_SDLMIXER
2016-01-09 16:38:10 +00:00
# define DXX_SOUND_JUKEBOX_MENU_ITEM(VERB) \
2016-01-09 16:38:17 +00:00
DXX_MENUITEM ( VERB , RADIO , " Jukebox " , opt_sm_mtype3 , GameCfg . MusicType = = MUSIC_TYPE_CUSTOM , optgrp_music_type ) \
2016-01-09 16:38:10 +00:00
# define DXX_MUSIC_OPTIONS_JUKEBOX_LABEL "Jukebox"
# define DXX_SOUND_SDLMIXER_MENU_ITEMS(VERB) \
2016-01-09 16:38:17 +00:00
DXX_MENUITEM ( VERB , TEXT , " " , opt_label_blank2 ) \
DXX_MENUITEM ( VERB , TEXT , " Jukebox options: " , opt_label_jukebox_options ) \
2016-01-09 16:38:10 +00:00
DXX_MENU_ITEM_BROWSE ( VERB , " Path for level music " , opt_sm_mtype3_lmpath ) \
2016-08-06 19:55:25 +00:00
DXX_MENUITEM ( VERB , INPUT , CGameCfg . CMLevelMusicPath , opt_sm_mtype3_lmpath_input ) \
2016-01-09 16:38:17 +00:00
DXX_MENUITEM ( VERB , TEXT , " " , opt_label_blank3 ) \
DXX_MENUITEM ( VERB , TEXT , " Level music play order: " , opt_label_lm_order ) \
DXX_MENUITEM ( VERB , RADIO , " continuous " , opt_sm_mtype3_lmplayorder1 , GameCfg . CMLevelMusicPlayOrder = = MUSIC_CM_PLAYORDER_CONT , optgrp_music_order ) \
DXX_MENUITEM ( VERB , RADIO , " one track per level " , opt_sm_mtype3_lmplayorder2 , GameCfg . CMLevelMusicPlayOrder = = MUSIC_CM_PLAYORDER_LEVEL , optgrp_music_order ) \
DXX_MENUITEM ( VERB , RADIO , " random " , opt_sm_mtype3_lmplayorder3 , GameCfg . CMLevelMusicPlayOrder = = MUSIC_CM_PLAYORDER_RAND , optgrp_music_order ) \
DXX_MENUITEM ( VERB , TEXT , " " , opt_label_blank4 ) \
DXX_MENUITEM ( VERB , TEXT , " Non-level music: " , opt_label_nonlevel_music ) \
2016-01-09 16:38:10 +00:00
DXX_MENU_ITEM_BROWSE ( VERB , " Main menu " , opt_sm_cm_mtype3_file1_b ) \
2016-08-06 19:55:25 +00:00
DXX_MENUITEM ( VERB , INPUT , CGameCfg . CMMiscMusic [ SONG_TITLE ] , opt_sm_cm_mtype3_file1 ) \
2016-01-09 16:38:10 +00:00
DXX_MENU_ITEM_BROWSE ( VERB , " Briefing " , opt_sm_cm_mtype3_file2_b ) \
2016-08-06 19:55:25 +00:00
DXX_MENUITEM ( VERB , INPUT , CGameCfg . CMMiscMusic [ SONG_BRIEFING ] , opt_sm_cm_mtype3_file2 ) \
2016-01-09 16:38:10 +00:00
DXX_MENU_ITEM_BROWSE ( VERB , " Credits " , opt_sm_cm_mtype3_file3_b ) \
2016-08-06 19:55:25 +00:00
DXX_MENUITEM ( VERB , INPUT , CGameCfg . CMMiscMusic [ SONG_CREDITS ] , opt_sm_cm_mtype3_file3 ) \
2016-01-09 16:38:10 +00:00
DXX_MENU_ITEM_BROWSE ( VERB , " Escape sequence " , opt_sm_cm_mtype3_file4_b ) \
2016-08-06 19:55:25 +00:00
DXX_MENUITEM ( VERB , INPUT , CGameCfg . CMMiscMusic [ SONG_ENDLEVEL ] , opt_sm_cm_mtype3_file4 ) \
2016-01-09 16:38:10 +00:00
DXX_MENU_ITEM_BROWSE ( VERB , " Game ending " , opt_sm_cm_mtype3_file5_b ) \
2016-08-06 19:55:25 +00:00
DXX_MENUITEM ( VERB , INPUT , CGameCfg . CMMiscMusic [ SONG_ENDGAME ] , opt_sm_cm_mtype3_file5 ) \
2016-01-09 16:38:10 +00:00
# else
# define DXX_SOUND_JUKEBOX_MENU_ITEM(VERB)
# define DXX_MUSIC_OPTIONS_JUKEBOX_LABEL ""
# define DXX_SOUND_SDLMIXER_MENU_ITEMS(VERB)
# endif
2016-09-11 18:49:15 +00:00
# if SDL_MAJOR_VERSION == 1 && DXX_USE_SDLMIXER
2016-01-09 16:38:10 +00:00
# define DXX_MUSIC_OPTIONS_SEPARATOR_TEXT " / "
# else
# define DXX_MUSIC_OPTIONS_SEPARATOR_TEXT ""
# endif
# define DXX_SOUND_MENU(VERB) \
2016-01-09 16:38:17 +00:00
DXX_MENUITEM ( VERB , SLIDER , TXT_FX_VOLUME , opt_sm_digivol , GameCfg . DigiVolume , 0 , 8 ) \
DXX_MENUITEM ( VERB , SLIDER , " Music volume " , opt_sm_musicvol , GameCfg . MusicVolume , 0 , 8 ) \
DXX_MENUITEM ( VERB , CHECK , TXT_REVERSE_STEREO , opt_sm_revstereo , GameCfg . ReverseStereo ) \
DXX_MENUITEM ( VERB , TEXT , " " , opt_label_blank0 ) \
DXX_MENUITEM ( VERB , TEXT , " Music type: " , opt_label_music_type ) \
DXX_MENUITEM ( VERB , RADIO , " No music " , opt_sm_mtype0 , GameCfg . MusicType = = MUSIC_TYPE_NONE , optgrp_music_type ) \
2016-01-09 16:38:10 +00:00
DXX_SOUND_ADDON_MUSIC_MENU_ITEM ( VERB ) \
DXX_SOUND_CD_MUSIC_MENU_ITEM ( VERB ) \
DXX_SOUND_JUKEBOX_MENU_ITEM ( VERB ) \
2016-01-09 16:38:17 +00:00
DXX_MENUITEM ( VERB , TEXT , " " , opt_label_blank1 ) \
DXX_MENUITEM ( VERB , TEXT , DXX_MUSIC_OPTIONS_CD_LABEL DXX_MUSIC_OPTIONS_SEPARATOR_TEXT DXX_MUSIC_OPTIONS_JUKEBOX_LABEL " options: " , opt_label_music_options ) \
DXX_MENUITEM ( VERB , CHECK , REDBOOK_PLAYORDER_TEXT , opt_sm_redbook_playorder , GameCfg . OrigTrackOrder ) \
2016-01-09 16:38:10 +00:00
DXX_SOUND_SDLMIXER_MENU_ITEMS ( VERB ) \
class sound_menu_items
2006-03-20 17:12:09 +00:00
{
2016-01-09 16:38:10 +00:00
public :
enum
{
optgrp_music_type ,
2016-09-11 18:49:15 +00:00
# if DXX_USE_SDLMIXER
2016-01-09 16:38:10 +00:00
optgrp_music_order ,
# endif
} ;
enum
{
DXX_SOUND_MENU ( ENUM )
} ;
array < newmenu_item , DXX_SOUND_MENU ( COUNT ) > m ;
sound_menu_items ( )
{
DXX_SOUND_MENU ( ADD ) ;
}
void read ( )
{
DXX_SOUND_MENU ( READ ) ;
}
static int menuset ( newmenu * , const d_event & event , sound_menu_items * pitems ) ;
} ;
# undef DXX_SOUND_MENU
}
int sound_menu_items : : menuset ( newmenu * , const d_event & event , sound_menu_items * pitems )
{
const auto & items = pitems - > m ;
2010-02-26 08:42:44 +00:00
int replay = 0 ;
int rval = 0 ;
2014-10-04 21:47:13 +00:00
switch ( event . type )
2010-02-01 06:55:38 +00:00
{
case EVENT_NEWMENU_CHANGED :
2015-02-28 22:34:07 +00:00
{
auto & citem = static_cast < const d_change_event & > ( event ) . citem ;
2010-06-14 08:13:16 +00:00
if ( citem = = opt_sm_digivol )
2010-02-01 06:55:38 +00:00
{
2010-06-14 08:13:16 +00:00
GameCfg . DigiVolume = items [ citem ] . value ;
digi_set_digi_volume ( ( GameCfg . DigiVolume * 32768 ) / 8 ) ;
digi_play_sample_once ( SOUND_DROP_BOMB , F1_0 ) ;
}
else if ( citem = = opt_sm_musicvol )
{
GameCfg . MusicVolume = items [ citem ] . value ;
songs_set_volume ( GameCfg . MusicVolume ) ;
}
else if ( citem = = opt_sm_revstereo )
{
GameCfg . ReverseStereo = items [ citem ] . value ;
}
else if ( citem = = opt_sm_mtype0 )
{
GameCfg . MusicType = MUSIC_TYPE_NONE ;
replay = 1 ;
}
2017-11-25 01:56:51 +00:00
/*
* When builtin music is enabled , the next line expands to
* ` # if + 1 + 0 ` ; when it is disabled , the line expands to
* ` # if + 0 ` .
*/
# if DXX_SOUND_ADDON_MUSIC_MENU_ITEM(COUNT) + 0
2010-06-14 08:13:16 +00:00
else if ( citem = = opt_sm_mtype1 )
{
GameCfg . MusicType = MUSIC_TYPE_BUILTIN ;
replay = 1 ;
}
2017-11-25 01:56:51 +00:00
# endif
2018-03-03 18:47:23 +00:00
# if SDL_MAJOR_VERSION == 1
2010-06-14 08:13:16 +00:00
else if ( citem = = opt_sm_mtype2 )
{
GameCfg . MusicType = MUSIC_TYPE_REDBOOK ;
replay = 1 ;
}
2018-03-03 18:47:23 +00:00
# endif
2016-09-11 18:49:15 +00:00
# if DXX_USE_SDLMIXER
2010-06-14 08:13:16 +00:00
else if ( citem = = opt_sm_mtype3 )
{
GameCfg . MusicType = MUSIC_TYPE_CUSTOM ;
replay = 1 ;
}
2010-10-14 09:50:11 +00:00
# endif
2010-06-14 08:13:16 +00:00
else if ( citem = = opt_sm_redbook_playorder )
{
GameCfg . OrigTrackOrder = items [ citem ] . value ;
replay = ( Game_wind ! = NULL ) ;
}
2016-09-11 18:49:15 +00:00
# if DXX_USE_SDLMIXER
2010-06-14 08:13:16 +00:00
else if ( citem = = opt_sm_mtype3_lmplayorder1 )
{
GameCfg . CMLevelMusicPlayOrder = MUSIC_CM_PLAYORDER_CONT ;
replay = ( Game_wind ! = NULL ) ;
}
else if ( citem = = opt_sm_mtype3_lmplayorder2 )
{
2010-08-17 14:59:57 +00:00
GameCfg . CMLevelMusicPlayOrder = MUSIC_CM_PLAYORDER_LEVEL ;
2010-06-14 08:13:16 +00:00
replay = ( Game_wind ! = NULL ) ;
2010-02-01 06:55:38 +00:00
}
2011-01-20 11:17:30 +00:00
else if ( citem = = opt_sm_mtype3_lmplayorder3 )
{
GameCfg . CMLevelMusicPlayOrder = MUSIC_CM_PLAYORDER_RAND ;
replay = ( Game_wind ! = NULL ) ;
}
2010-10-14 09:50:11 +00:00
# endif
2010-02-01 06:55:38 +00:00
break ;
2015-02-28 22:34:07 +00:00
}
2010-02-26 08:42:44 +00:00
case EVENT_NEWMENU_SELECTED :
2015-02-28 22:34:07 +00:00
{
2016-09-11 18:49:15 +00:00
# if DXX_USE_SDLMIXER
2015-03-15 18:45:53 +00:00
auto & citem = static_cast < const d_select_event & > ( event ) . citem ;
2014-12-20 04:36:11 +00:00
# ifdef _WIN32
# define WINDOWS_DRIVE_CHANGE_TEXT ".\nCTRL-D to change drive"
# else
# define WINDOWS_DRIVE_CHANGE_TEXT
# endif
2010-08-27 14:09:19 +00:00
if ( citem = = opt_sm_mtype3_lmpath )
{
2015-03-22 18:49:21 +00:00
static const array < file_extension_t , 1 > ext_list { { " m3u " } } ; // select a directory or M3U playlist
2016-08-06 19:55:25 +00:00
const auto cfgpath = CGameCfg . CMLevelMusicPath . data ( ) ;
2010-08-31 13:25:45 +00:00
select_file_recursive (
2014-12-20 04:36:11 +00:00
" Select directory or \n M3U playlist to \n play level music from " WINDOWS_DRIVE_CHANGE_TEXT ,
2016-08-06 19:55:25 +00:00
cfgpath , ext_list , 1 , // look in current music path for ext_list files and allow directory selection
get_absolute_path , cfgpath ) ; // just copy the absolute path
2010-08-27 14:09:19 +00:00
}
else if ( citem = = opt_sm_cm_mtype3_file1_b )
2014-12-20 04:36:11 +00:00
SELECT_SONG ( " Select main menu music " WINDOWS_DRIVE_CHANGE_TEXT , SONG_TITLE ) ;
2010-08-27 14:09:19 +00:00
else if ( citem = = opt_sm_cm_mtype3_file2_b )
2014-12-20 04:36:11 +00:00
SELECT_SONG ( " Select briefing music " WINDOWS_DRIVE_CHANGE_TEXT , SONG_BRIEFING ) ;
2010-08-27 14:09:19 +00:00
else if ( citem = = opt_sm_cm_mtype3_file3_b )
2014-12-20 04:36:11 +00:00
SELECT_SONG ( " Select credits music " WINDOWS_DRIVE_CHANGE_TEXT , SONG_CREDITS ) ;
2010-08-27 14:09:19 +00:00
else if ( citem = = opt_sm_cm_mtype3_file4_b )
2014-12-20 04:36:11 +00:00
SELECT_SONG ( " Select escape sequence music " WINDOWS_DRIVE_CHANGE_TEXT , SONG_ENDLEVEL ) ;
2010-08-27 14:09:19 +00:00
else if ( citem = = opt_sm_cm_mtype3_file5_b )
2014-12-20 04:36:11 +00:00
SELECT_SONG ( " Select game ending music " WINDOWS_DRIVE_CHANGE_TEXT , SONG_ENDGAME ) ;
2010-10-14 09:50:11 +00:00
# endif
2010-02-26 08:42:44 +00:00
rval = 1 ; // stay in menu
break ;
2015-02-28 22:34:07 +00:00
}
2010-02-26 08:42:44 +00:00
case EVENT_WINDOW_CLOSE :
break ;
2010-06-14 08:13:16 +00:00
2010-02-01 06:55:38 +00:00
default :
break ;
2006-03-20 17:12:09 +00:00
}
2010-02-26 08:42:44 +00:00
if ( replay )
{
2010-06-14 08:13:16 +00:00
songs_uninit ( ) ;
2010-02-26 08:42:44 +00:00
if ( Game_wind )
2010-06-14 08:13:16 +00:00
songs_play_level_song ( Current_level_num , 0 ) ;
2010-02-26 08:42:44 +00:00
else
songs_play_song ( SONG_TITLE , 1 ) ;
}
2010-06-14 08:13:16 +00:00
2010-02-26 08:42:44 +00:00
return rval ;
2006-03-20 17:12:09 +00:00
}
2010-02-26 08:42:44 +00:00
void do_sound_menu ( )
{
2016-01-09 16:38:10 +00:00
2016-09-11 18:49:15 +00:00
# if DXX_USE_SDLMIXER
2016-08-06 19:55:25 +00:00
const auto old_CMLevelMusicPath = CGameCfg . CMLevelMusicPath ;
2016-08-06 19:55:25 +00:00
const auto old_CMMiscMusic0 = CGameCfg . CMMiscMusic [ SONG_TITLE ] ;
2015-01-03 23:44:32 +00:00
# endif
2009-05-03 12:31:30 +00:00
2016-01-09 16:38:10 +00:00
sound_menu_items items ;
newmenu_do1 ( nullptr , " Sound Effects & Music " , items . m . size ( ) , items . m . data ( ) , & sound_menu_items : : menuset , & items , 0 ) ;
2010-06-14 08:13:16 +00:00
2016-09-11 18:49:15 +00:00
# if DXX_USE_SDLMIXER
2016-08-06 19:55:25 +00:00
if ( ( Game_wind ! = NULL & & strcmp ( old_CMLevelMusicPath . data ( ) , CGameCfg . CMLevelMusicPath . data ( ) ) ) | | ( Game_wind = = NULL & & strcmp ( old_CMMiscMusic0 . data ( ) , CGameCfg . CMMiscMusic [ SONG_TITLE ] . data ( ) ) ) )
2010-06-14 08:13:16 +00:00
{
songs_uninit ( ) ;
if ( Game_wind )
songs_play_level_song ( Current_level_num , 0 ) ;
else
songs_play_song ( SONG_TITLE , 1 ) ;
}
# endif
2008-04-13 00:28:36 +00:00
}
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_I)
# define DXX_GAME_SPECIFIC_OPTIONS(VERB) \
# elif defined(DXX_BUILD_DESCENT_II)
# define DXX_GAME_SPECIFIC_OPTIONS(VERB) \
2016-01-09 16:38:17 +00:00
DXX_MENUITEM ( VERB , CHECK , " Headlight on when picked up " , opt_headlighton , PlayerCfg . HeadlightActiveDefault ) \
DXX_MENUITEM ( VERB , CHECK , " Escort robot hot keys " , opt_escorthotkey , PlayerCfg . EscortHotKeys ) \
DXX_MENUITEM ( VERB , CHECK , " Movie Subtitles " , opt_moviesubtitle , GameCfg . MovieSubtitles ) \
Allow players to remove thief at level start
Commit f4b21088a039 ("Track vulcan ammo explicitly") fixed an original
retail bug that prevented the thief from stealing energy weapons,
because the thief could only steal weapons for which the player had ammo
and energy weapons never have ammo. This went unremarked for several
years, until a recent report of the new semantics as a game-breaking
regression because the thief is now "ridiculously potent".
Address this report, as well as an intermittently raised issue from
various users over time, by adding two new knobs to both the single
player "Gameplay" menu and the multiplayer setup screen: "Remove Thief
at level start" and "Prevent Thief Stealing Energy Weapons".
"Remove Thief" deletes the thief object during level load. It has no
impact on save games, and changing it after entering a level has no
effect on any thief already in the level.
"Prevent Thief Stealing" is checked at the moment of theft and, when
enabled, prevents stealing primary weapons other than Vulcan/Gauss.
This can be changed at will in single player and is immediately
effective. In multiplayer, this option can only be changed by the game
host in the pre-game setup.
For both knobs, there is one pair of checkboxes to control this as a
player preference, which applies in single player games. There is a
second pair of checkboxes in the multiplayer setup, which applies only
to multiplayer games. Therefore, in multiplayer, the host chooses thief
settings and all clients use the host's choice. The host may configure
the thief differently in multiplayer from how the host plays in single
player.
For users who wanted to remove the thief, no specific tally has been
kept for who requested it or when. Now that the code is being updated,
this is thrown in as an easy addition.
Reported-by: MegaDescent <http://forum.dxx-rebirth.com/showthread.php?tid=980> (for the thief stealing energy weapons as a game-breaking regression)
2017-08-26 19:47:52 +00:00
DXX_MENUITEM ( VERB , CHECK , " Remove Thief at level start " , opt_thief_presence , thief_absent ) \
DXX_MENUITEM ( VERB , CHECK , " Prevent Thief Stealing Energy Weapons " , opt_thief_steal_energy , thief_cannot_steal_energy_weapons ) \
2013-03-03 01:03:33 +00:00
# endif
2015-10-18 18:11:57 +00:00
enum {
optgrp_autoselect_firing ,
} ;
2015-09-26 17:48:57 +00:00
# define DXX_GAMEPLAY_MENU_OPTIONS(VERB) \
2016-01-09 16:38:17 +00:00
DXX_MENUITEM ( VERB , CHECK , " Ship auto-leveling " , opt_autolevel , PlayerCfg . AutoLeveling ) \
DXX_MENUITEM ( VERB , CHECK , " Persistent Debris " , opt_persist_debris , PlayerCfg . PersistentDebris ) \
DXX_MENUITEM ( VERB , CHECK , " No Rankings (Multi) " , opt_noranking , PlayerCfg . NoRankings ) \
DXX_MENUITEM ( VERB , CHECK , " Free Flight in Automap " , opt_freeflight , PlayerCfg . AutomapFreeFlight ) \
2013-03-03 01:03:33 +00:00
DXX_GAME_SPECIFIC_OPTIONS ( VERB ) \
2016-01-09 16:38:17 +00:00
DXX_MENUITEM ( VERB , TEXT , " " , opt_label_blank ) \
DXX_MENUITEM ( VERB , TEXT , " Weapon Autoselect options: " , opt_label_autoselect ) \
DXX_MENUITEM ( VERB , MENU , " Primary ordering... " , opt_gameplay_reorderprimary_menu ) \
DXX_MENUITEM ( VERB , MENU , " Secondary ordering... " , opt_gameplay_reordersecondary_menu ) \
DXX_MENUITEM ( VERB , TEXT , " Autoselect while firing: " , opt_autoselect_firing_label ) \
DXX_MENUITEM ( VERB , RADIO , " Immediately " , opt_autoselect_firing_immediate , PlayerCfg . NoFireAutoselect = = FiringAutoselectMode : : Immediate , optgrp_autoselect_firing ) \
DXX_MENUITEM ( VERB , RADIO , " Never " , opt_autoselect_firing_never , PlayerCfg . NoFireAutoselect = = FiringAutoselectMode : : Never , optgrp_autoselect_firing ) \
DXX_MENUITEM ( VERB , RADIO , " When firing stops " , opt_autoselect_firing_delayed , PlayerCfg . NoFireAutoselect = = FiringAutoselectMode : : Delayed , optgrp_autoselect_firing ) \
DXX_MENUITEM ( VERB , CHECK , " Only Cycle Autoselect Weapons " , opt_only_autoselect , PlayerCfg . CycleAutoselectOnly ) \
2013-03-03 01:03:33 +00:00
2015-09-26 17:48:57 +00:00
enum {
DXX_GAMEPLAY_MENU_OPTIONS ( ENUM )
} ;
2006-03-20 17:12:09 +00:00
2015-09-26 17:48:57 +00:00
static int gameplay_config_menuset ( newmenu * , const d_event & event , const unused_newmenu_userdata_t * )
{
switch ( event . type )
{
case EVENT_NEWMENU_SELECTED :
{
auto & citem = static_cast < const d_select_event & > ( event ) . citem ;
if ( citem = = opt_gameplay_reorderprimary_menu )
ReorderPrimary ( ) ;
2019-03-10 18:48:40 +00:00
else if ( citem = = opt_gameplay_reordersecondary_menu )
2015-09-26 17:48:57 +00:00
ReorderSecondary ( ) ;
return 1 ; // stay in menu
}
default :
break ;
}
return 0 ;
}
void gameplay_config ( )
2006-03-20 17:12:09 +00:00
{
2019-03-10 18:48:40 +00:00
for ( ; ; )
{
array < newmenu_item , DXX_GAMEPLAY_MENU_OPTIONS ( COUNT ) > m ;
Allow players to remove thief at level start
Commit f4b21088a039 ("Track vulcan ammo explicitly") fixed an original
retail bug that prevented the thief from stealing energy weapons,
because the thief could only steal weapons for which the player had ammo
and energy weapons never have ammo. This went unremarked for several
years, until a recent report of the new semantics as a game-breaking
regression because the thief is now "ridiculously potent".
Address this report, as well as an intermittently raised issue from
various users over time, by adding two new knobs to both the single
player "Gameplay" menu and the multiplayer setup screen: "Remove Thief
at level start" and "Prevent Thief Stealing Energy Weapons".
"Remove Thief" deletes the thief object during level load. It has no
impact on save games, and changing it after entering a level has no
effect on any thief already in the level.
"Prevent Thief Stealing" is checked at the moment of theft and, when
enabled, prevents stealing primary weapons other than Vulcan/Gauss.
This can be changed at will in single player and is immediately
effective. In multiplayer, this option can only be changed by the game
host in the pre-game setup.
For both knobs, there is one pair of checkboxes to control this as a
player preference, which applies in single player games. There is a
second pair of checkboxes in the multiplayer setup, which applies only
to multiplayer games. Therefore, in multiplayer, the host chooses thief
settings and all clients use the host's choice. The host may configure
the thief differently in multiplayer from how the host plays in single
player.
For users who wanted to remove the thief, no specific tally has been
kept for who requested it or when. Now that the code is being updated,
this is thrown in as an easy addition.
Reported-by: MegaDescent <http://forum.dxx-rebirth.com/showthread.php?tid=980> (for the thief stealing energy weapons as a game-breaking regression)
2017-08-26 19:47:52 +00:00
# if defined(DXX_BUILD_DESCENT_II)
auto thief_absent = PlayerCfg . ThiefModifierFlags & ThiefModifier : : Absent ;
auto thief_cannot_steal_energy_weapons = PlayerCfg . ThiefModifierFlags & ThiefModifier : : NoEnergyWeapons ;
# endif
2015-09-26 17:48:57 +00:00
DXX_GAMEPLAY_MENU_OPTIONS ( ADD ) ;
2019-03-10 18:48:40 +00:00
const auto i = newmenu_do1 ( NULL , " Gameplay Options " , m . size ( ) , m . data ( ) , gameplay_config_menuset , unused_newmenu_userdata , 0 ) ;
2015-09-26 17:48:57 +00:00
DXX_GAMEPLAY_MENU_OPTIONS ( READ ) ;
2015-10-18 18:11:57 +00:00
PlayerCfg . NoFireAutoselect = m [ opt_autoselect_firing_delayed ] . value
? FiringAutoselectMode : : Delayed
: ( m [ opt_autoselect_firing_immediate ] . value
? FiringAutoselectMode : : Immediate
: FiringAutoselectMode : : Never ) ;
Allow players to remove thief at level start
Commit f4b21088a039 ("Track vulcan ammo explicitly") fixed an original
retail bug that prevented the thief from stealing energy weapons,
because the thief could only steal weapons for which the player had ammo
and energy weapons never have ammo. This went unremarked for several
years, until a recent report of the new semantics as a game-breaking
regression because the thief is now "ridiculously potent".
Address this report, as well as an intermittently raised issue from
various users over time, by adding two new knobs to both the single
player "Gameplay" menu and the multiplayer setup screen: "Remove Thief
at level start" and "Prevent Thief Stealing Energy Weapons".
"Remove Thief" deletes the thief object during level load. It has no
impact on save games, and changing it after entering a level has no
effect on any thief already in the level.
"Prevent Thief Stealing" is checked at the moment of theft and, when
enabled, prevents stealing primary weapons other than Vulcan/Gauss.
This can be changed at will in single player and is immediately
effective. In multiplayer, this option can only be changed by the game
host in the pre-game setup.
For both knobs, there is one pair of checkboxes to control this as a
player preference, which applies in single player games. There is a
second pair of checkboxes in the multiplayer setup, which applies only
to multiplayer games. Therefore, in multiplayer, the host chooses thief
settings and all clients use the host's choice. The host may configure
the thief differently in multiplayer from how the host plays in single
player.
For users who wanted to remove the thief, no specific tally has been
kept for who requested it or when. Now that the code is being updated,
this is thrown in as an easy addition.
Reported-by: MegaDescent <http://forum.dxx-rebirth.com/showthread.php?tid=980> (for the thief stealing energy weapons as a game-breaking regression)
2017-08-26 19:47:52 +00:00
# if defined(DXX_BUILD_DESCENT_II)
PlayerCfg . ThiefModifierFlags =
( thief_absent ? ThiefModifier : : Absent : 0 ) |
( thief_cannot_steal_energy_weapons ? ThiefModifier : : NoEnergyWeapons : 0 ) ;
# endif
2019-03-10 18:48:40 +00:00
if ( i = = - 1 )
break ;
}
2006-03-20 17:12:09 +00:00
}
2016-09-11 18:49:15 +00:00
# if DXX_USE_UDP
2014-10-04 21:47:13 +00:00
static int multi_player_menu_handler ( newmenu * menu , const d_event & event , int * menu_choice )
2010-04-04 09:41:53 +00:00
{
newmenu_item * items = newmenu_get_items ( menu ) ;
2014-10-04 21:47:13 +00:00
switch ( event . type )
2010-04-04 09:41:53 +00:00
{
case EVENT_NEWMENU_SELECTED :
2015-02-28 22:34:07 +00:00
{
auto & citem = static_cast < const d_select_event & > ( event ) . citem ;
2010-04-04 09:41:53 +00:00
// stay in multiplayer menu, even after having played a game
2015-02-28 22:34:07 +00:00
return do_option ( menu_choice [ citem ] ) ;
}
2010-06-14 08:13:16 +00:00
2010-04-04 09:41:53 +00:00
case EVENT_WINDOW_CLOSE :
d_free ( menu_choice ) ;
d_free ( items ) ;
break ;
2010-06-14 08:13:16 +00:00
2010-04-04 09:41:53 +00:00
default :
break ;
}
2010-06-14 08:13:16 +00:00
2010-04-04 09:41:53 +00:00
return 0 ;
}
2006-03-20 17:12:09 +00:00
void do_multi_player_menu ( )
{
2010-04-04 09:41:53 +00:00
int * menu_choice ;
newmenu_item * m ;
int num_options = 0 ;
2006-03-20 17:12:09 +00:00
2011-09-13 23:15:32 +00:00
MALLOC ( menu_choice , int , 3 ) ;
2010-04-04 09:41:53 +00:00
if ( ! menu_choice )
return ;
2010-06-14 08:13:16 +00:00
2011-09-13 23:15:32 +00:00
MALLOC ( m , newmenu_item , 3 ) ;
2010-04-04 09:41:53 +00:00
if ( ! m )
{
d_free ( menu_choice ) ;
return ;
}
2006-03-20 17:12:09 +00:00
2016-09-11 18:49:15 +00:00
# if DXX_USE_UDP
2012-11-24 20:57:15 +00:00
ADD_ITEM ( " HOST GAME " , MENU_START_UDP_NETGAME , - 1 ) ;
2016-09-11 18:49:15 +00:00
# if DXX_USE_TRACKER
2012-11-24 20:57:15 +00:00
ADD_ITEM ( " FIND LAN/ONLINE GAMES " , MENU_JOIN_LIST_UDP_NETGAME , - 1 ) ;
2011-04-05 00:24:34 +00:00
# else
2012-11-24 20:57:15 +00:00
ADD_ITEM ( " FIND LAN GAMES " , MENU_JOIN_LIST_UDP_NETGAME , - 1 ) ;
2011-04-05 00:24:34 +00:00
# endif
2012-11-24 20:57:15 +00:00
ADD_ITEM ( " JOIN GAME MANUALLY " , MENU_JOIN_MANUAL_UDP_NETGAME , - 1 ) ;
2009-11-29 16:46:13 +00:00
# endif
2009-11-24 09:48:53 +00:00
2013-12-04 22:45:33 +00:00
newmenu_do3 ( NULL , TXT_MULTIPLAYER , num_options , m , multi_player_menu_handler , menu_choice , 0 , NULL ) ;
2006-03-20 17:12:09 +00:00
}
2009-11-29 16:46:13 +00:00
# endif
2008-04-13 00:28:36 +00:00
void do_options_menu ( )
{
2015-03-07 17:20:40 +00:00
auto items = new options_menu_items ;
2010-04-04 09:41:53 +00:00
// Fall back to main event loop
// Allows clean closing and re-opening when resolution changes
2015-03-07 17:20:40 +00:00
newmenu_do3 ( nullptr , TXT_OPTIONS , items - > m . size ( ) , items - > m . data ( ) , options_menuset , items , 0 , nullptr ) ;
2008-04-13 00:28:36 +00:00
}
2011-07-20 12:44:49 +00:00
# ifndef RELEASE
2016-08-25 04:05:32 +00:00
namespace dsx {
2016-10-04 06:05:44 +00:00
static window_event_result polygon_models_viewer_handler ( window * , const d_event & event , const unused_window_userdata_t * )
2011-07-20 12:44:49 +00:00
{
2014-09-20 23:47:27 +00:00
static unsigned view_idx ;
2011-07-20 12:44:49 +00:00
int key = 0 ;
static vms_angvec ang ;
2014-10-04 21:47:13 +00:00
switch ( event . type )
2011-07-20 12:44:49 +00:00
{
case EVENT_WINDOW_ACTIVATED :
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2011-07-20 12:44:49 +00:00
gr_use_palette_table ( " groupa.256 " ) ;
2013-03-03 01:03:33 +00:00
# endif
2011-07-20 12:44:49 +00:00
key_toggle_repeat ( 1 ) ;
view_idx = 0 ;
ang . p = ang . b = 0 ;
2012-05-14 15:13:27 +00:00
ang . h = F0_5 - 1 ;
2011-07-20 12:44:49 +00:00
break ;
case EVENT_KEY_COMMAND :
key = event_key_get ( event ) ;
switch ( key )
{
case KEY_ESC :
2014-08-06 02:10:49 +00:00
return window_event_result : : close ;
2011-07-20 12:44:49 +00:00
case KEY_SPACEBAR :
view_idx + + ;
if ( view_idx > = N_polygon_models ) view_idx = 0 ;
break ;
case KEY_BACKSP :
2014-09-20 23:47:27 +00:00
if ( ! view_idx )
view_idx = N_polygon_models - 1 ;
else
view_idx - - ;
2011-07-20 12:44:49 +00:00
break ;
case KEY_A :
ang . h - = 100 ;
break ;
case KEY_D :
ang . h + = 100 ;
break ;
case KEY_W :
ang . p - = 100 ;
break ;
case KEY_S :
ang . p + = 100 ;
break ;
case KEY_Q :
ang . b - = 100 ;
break ;
case KEY_E :
ang . b + = 100 ;
break ;
case KEY_R :
ang . p = ang . b = 0 ;
2012-05-14 15:13:27 +00:00
ang . h = F0_5 - 1 ;
2011-07-20 12:44:49 +00:00
break ;
default :
break ;
}
2014-08-06 02:10:49 +00:00
return window_event_result : : handled ;
2011-07-20 12:44:49 +00:00
case EVENT_WINDOW_DRAW :
timer_delay ( F1_0 / 60 ) ;
2017-04-08 16:48:18 +00:00
{
auto & canvas = * grd_curcanv ;
2017-04-30 16:25:16 +00:00
draw_model_picture ( canvas , view_idx , ang ) ;
2017-04-08 16:48:18 +00:00
gr_set_fontcolor ( canvas , BM_XRGB ( 255 , 255 , 255 ) , - 1 ) ;
2018-05-19 23:21:42 +00:00
auto & game_font = * GAME_FONT ;
gr_printf ( canvas , game_font , FSPACX ( 1 ) , FSPACY ( 1 ) , " ESC: leave \n SPACE/BACKSP: next/prev model (%i/%i) \n A/D: rotate y \n W/S: rotate x \n Q/E: rotate z \n R: reset orientation " , view_idx , N_polygon_models - 1 ) ;
2017-04-08 16:48:18 +00:00
}
2011-07-20 12:44:49 +00:00
break ;
case EVENT_WINDOW_CLOSE :
load_palette ( MENU_PALETTE , 0 , 1 ) ;
key_toggle_repeat ( 0 ) ;
break ;
default :
break ;
}
2014-08-06 02:10:49 +00:00
return window_event_result : : ignored ;
2011-07-20 12:44:49 +00:00
}
2016-08-25 04:05:32 +00:00
}
2011-07-20 12:44:49 +00:00
2013-10-27 22:00:14 +00:00
static void polygon_models_viewer ( )
2011-07-20 12:44:49 +00:00
{
2016-10-15 00:53:20 +00:00
const auto wind = window_create ( grd_curscreen - > sc_canvas , 0 , 0 , SWIDTH , SHEIGHT , polygon_models_viewer_handler , unused_window_userdata ) ;
2011-07-20 12:44:49 +00:00
if ( ! wind )
{
d_event event = { EVENT_WINDOW_CLOSE } ;
2014-10-04 21:47:13 +00:00
polygon_models_viewer_handler ( NULL , event , NULL ) ;
2011-07-20 12:44:49 +00:00
return ;
}
2016-10-29 12:06:01 +00:00
event_process_all ( ) ;
2011-07-20 12:44:49 +00:00
}
2016-08-25 04:05:32 +00:00
namespace dsx {
2016-10-04 06:05:44 +00:00
static window_event_result gamebitmaps_viewer_handler ( window * , const d_event & event , const unused_window_userdata_t * )
2011-07-20 12:44:49 +00:00
{
static int view_idx = 0 ;
int key = 0 ;
2016-09-24 18:06:11 +00:00
# if DXX_USE_OGL
2011-07-20 12:44:49 +00:00
float scale = 1.0 ;
# endif
bitmap_index bi ;
grs_bitmap * bm ;
2014-10-04 21:47:13 +00:00
switch ( event . type )
2011-07-20 12:44:49 +00:00
{
case EVENT_WINDOW_ACTIVATED :
2013-03-03 01:03:33 +00:00
# if defined(DXX_BUILD_DESCENT_II)
2011-07-20 12:44:49 +00:00
gr_use_palette_table ( " groupa.256 " ) ;
2013-03-03 01:03:33 +00:00
# endif
2011-07-20 12:44:49 +00:00
key_toggle_repeat ( 1 ) ;
view_idx = 0 ;
break ;
case EVENT_KEY_COMMAND :
key = event_key_get ( event ) ;
switch ( key )
{
case KEY_ESC :
2014-08-06 02:10:49 +00:00
return window_event_result : : close ;
2011-07-20 12:44:49 +00:00
case KEY_SPACEBAR :
view_idx + + ;
if ( view_idx > = Num_bitmap_files ) view_idx = 0 ;
break ;
case KEY_BACKSP :
view_idx - - ;
if ( view_idx < 0 ) view_idx = Num_bitmap_files - 1 ;
break ;
default :
break ;
}
2014-08-06 02:10:49 +00:00
return window_event_result : : handled ;
2011-07-20 12:44:49 +00:00
case EVENT_WINDOW_DRAW :
bi . index = view_idx ;
bm = & GameBitmaps [ view_idx ] ;
timer_delay ( F1_0 / 60 ) ;
PIGGY_PAGE_IN ( bi ) ;
2017-04-08 16:48:18 +00:00
{
auto & canvas = * grd_curcanv ;
gr_clear_canvas ( canvas , BM_XRGB ( 0 , 0 , 0 ) ) ;
2016-09-24 18:06:11 +00:00
# if DXX_USE_OGL
2017-04-08 16:48:18 +00:00
scale = ( bm - > bm_w > bm - > bm_h ) ? ( SHEIGHT / bm - > bm_w ) * 0.8 : ( SHEIGHT / bm - > bm_h ) * 0.8 ;
ogl_ubitmapm_cs ( canvas , ( SWIDTH / 2 ) - ( bm - > bm_w * scale / 2 ) , ( SHEIGHT / 2 ) - ( bm - > bm_h * scale / 2 ) , bm - > bm_w * scale , bm - > bm_h * scale , * bm , ogl_colors : : white , F1_0 ) ;
2011-07-20 12:44:49 +00:00
# else
2017-04-08 16:48:18 +00:00
gr_bitmap ( canvas , ( SWIDTH / 2 ) - ( bm - > bm_w / 2 ) , ( SHEIGHT / 2 ) - ( bm - > bm_h / 2 ) , * bm ) ;
2011-07-20 12:44:49 +00:00
# endif
2017-04-08 16:48:18 +00:00
gr_set_fontcolor ( canvas , BM_XRGB ( 255 , 255 , 255 ) , - 1 ) ;
2018-05-19 23:21:42 +00:00
auto & game_font = * GAME_FONT ;
gr_printf ( canvas , game_font , FSPACX ( 1 ) , FSPACY ( 1 ) , " ESC: leave \n SPACE/BACKSP: next/prev bitmap (%i/%i) " , view_idx , Num_bitmap_files - 1 ) ;
2017-04-08 16:48:18 +00:00
}
2011-07-20 12:44:49 +00:00
break ;
case EVENT_WINDOW_CLOSE :
load_palette ( MENU_PALETTE , 0 , 1 ) ;
key_toggle_repeat ( 0 ) ;
break ;
default :
break ;
}
2014-08-06 02:10:49 +00:00
return window_event_result : : ignored ;
2011-07-20 12:44:49 +00:00
}
2016-08-25 04:05:32 +00:00
}
2011-07-20 12:44:49 +00:00
2013-10-27 22:00:14 +00:00
static void gamebitmaps_viewer ( )
2011-07-20 12:44:49 +00:00
{
2016-10-15 00:53:20 +00:00
const auto wind = window_create ( grd_curscreen - > sc_canvas , 0 , 0 , SWIDTH , SHEIGHT , gamebitmaps_viewer_handler , unused_window_userdata ) ;
2011-07-20 12:44:49 +00:00
if ( ! wind )
{
d_event event = { EVENT_WINDOW_CLOSE } ;
2014-10-04 21:47:13 +00:00
gamebitmaps_viewer_handler ( NULL , event , NULL ) ;
2011-07-20 12:44:49 +00:00
return ;
}
2016-10-29 12:06:01 +00:00
event_process_all ( ) ;
2011-07-20 12:44:49 +00:00
}
2015-03-07 17:20:41 +00:00
# define DXX_SANDBOX_MENU(VERB) \
2016-01-09 16:38:17 +00:00
DXX_MENUITEM ( VERB , MENU , " Polygon_models viewer " , polygon_models ) \
DXX_MENUITEM ( VERB , MENU , " GameBitmaps viewer " , bitmaps ) \
2015-03-07 17:20:41 +00:00
namespace {
class sandbox_menu_items
{
public :
enum
{
DXX_SANDBOX_MENU ( ENUM )
} ;
array < newmenu_item , DXX_SANDBOX_MENU ( COUNT ) > m ;
sandbox_menu_items ( )
{
DXX_SANDBOX_MENU ( ADD ) ;
}
} ;
}
2015-04-26 20:15:56 +00:00
static int sandbox_menuset ( newmenu * , const d_event & event , sandbox_menu_items * items )
2011-07-20 12:44:49 +00:00
{
2014-10-04 21:47:13 +00:00
switch ( event . type )
2011-07-20 12:44:49 +00:00
{
case EVENT_NEWMENU_CHANGED :
break ;
case EVENT_NEWMENU_SELECTED :
2015-02-28 22:34:07 +00:00
{
auto & citem = static_cast < const d_select_event & > ( event ) . citem ;
switch ( citem )
2011-07-20 12:44:49 +00:00
{
2015-03-07 17:20:41 +00:00
case sandbox_menu_items : : polygon_models :
polygon_models_viewer ( ) ;
break ;
case sandbox_menu_items : : bitmaps :
gamebitmaps_viewer ( ) ;
break ;
2011-07-20 12:44:49 +00:00
}
return 1 ; // stay in menu until escape
2015-02-28 22:34:07 +00:00
}
2011-07-20 12:44:49 +00:00
case EVENT_WINDOW_CLOSE :
{
2015-03-07 17:20:41 +00:00
std : : default_delete < sandbox_menu_items > ( ) ( items ) ;
2011-07-20 12:44:49 +00:00
break ;
}
default :
break ;
}
return 0 ;
}
void do_sandbox_menu ( )
{
2015-03-07 17:20:41 +00:00
auto items = new sandbox_menu_items ;
newmenu_do3 ( nullptr , " Coder's sandbox " , items - > m . size ( ) , items - > m . data ( ) , sandbox_menuset , items , 0 , nullptr ) ;
2011-07-20 12:44:49 +00:00
}
# endif