1 // Emacs style mode select   -*- C++ -*-
2 //-----------------------------------------------------------------------------
3 //
4 // $Id: m_menu.c 1566 2020-12-19 06:22:58Z wesleyjohnson $
5 //
6 // Copyright (C) 1993-1996 by id Software, Inc.
7 // Copyright (C) 1998-2016 by DooM Legacy Team.
8 //
9 // This program is free software; you can redistribute it and/or
10 // modify it under the terms of the GNU General Public License
11 // as published by the Free Software Foundation; either version 2
12 // of the License, or (at your option) any later version.
13 //
14 // This program is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 // GNU General Public License for more details.
18 //
19 //
20 // $Log: m_menu.c,v $
21 // Revision 1.55  2005/12/20 14:58:25  darkwolf95
22 // Monster behavior CVAR - Affects how monsters react when they shoot each other
23 //
24 // Revision 1.54  2004/09/12 19:40:06  darkwolf95
25 // additional chex quest 1 support
26 //
27 // Revision 1.53  2004/07/27 08:19:36  exl
28 // New fmod, fs functions, bugfix or 2, patrol nodes
29 //
30 // Revision 1.52  2003/08/11 13:50:01  hurdler
31 // go final + translucent HUD + fix spawn in net game
32 //
33 // Revision 1.51  2003/03/22 22:35:59  hurdler
34 //
35 // Revision 1.50  2002/09/27 16:40:08  tonyd
36 // First commit of acbot
37 //
38 // Revision 1.49  2002/09/12 20:10:50  hurdler
39 // Added some cvars
40 //
41 // Revision 1.48  2002/08/24 22:42:03  hurdler
42 // Apply Robert Hogberg patches
43 //
44 // Revision 1.47  2001/12/31 13:47:46  hurdler
45 // Add setcorona FS command and prepare the code for beta 4
46 //
47 // Revision 1.46  2001/12/26 17:24:46  hurdler
48 // Update Linux version
49 //
50 // Revision 1.45  2001/12/15 18:41:35  hurdler
51 // small commit, mainly splitscreen fix
52 //
53 // Revision 1.44  2001/11/02 23:29:13  judgecutor
54 // Fixed "secondary player controls" bug
55 //
56 // Revision 1.43  2001/11/02 21:44:05  judgecutor
57 // Added Frag's weapon falling
58 //
59 // Revision 1.42  2001/08/20 21:37:34  hurdler
60 // fix palette in splitscreen + hardware mode
61 //
62 // Revision 1.41  2001/08/20 20:40:39  metzgermeister
63 //
64 // Revision 1.40  2001/08/08 20:34:43  hurdler
65 // Big TANDL update
66 //
67 // Revision 1.39  2001/06/10 21:16:01  bpereira
68 // Revision 1.38  2001/05/27 13:42:47  bpereira
69 // Revision 1.37  2001/05/16 22:00:10  hurdler
70 // Revision 1.36  2001/05/16 21:21:14  bpereira
71 //
72 // Revision 1.35  2001/05/14 19:02:58  metzgermeister
73 //   * Fixed floor not moving up with player on E3M1
74 //   * Fixed crash due to oversized string in screen message ... bad bug!
75 //   * Corrected some typos
76 //   * fixed sound bug in SDL
77 //
78 // Revision 1.34  2001/04/29 14:25:26  hurdler
79 // Revision 1.33  2001/04/01 17:35:06  bpereira
80 // Revision 1.32  2001/03/03 06:17:33  bpereira
81 // Revision 1.31  2001/02/24 13:35:20  bpereira
82 // Revision 1.30  2001/02/10 12:27:14  bpereira
83 //
84 // Revision 1.29  2001/01/25 22:15:42  bpereira
85 // added heretic support
86 //
87 // Revision 1.28  2000/11/26 20:36:14  hurdler
88 // Adding autorun2
89 //
90 // Revision 1.27  2000/10/21 08:43:29  bpereira
91 //
92 // Revision 1.26  2000/10/17 10:09:27  hurdler
93 // Update master server code for easy connect from menu
94 //
95 // Revision 1.25  2000/10/16 20:02:29  bpereira
96 // Revision 1.24  2000/10/08 13:30:01  bpereira
97 // Revision 1.23  2000/10/02 18:25:45  bpereira
98 //
99 // Revision 1.22  2000/10/01 15:20:23  hurdler
100 // Add private server
101 //
102 // Revision 1.21  2000/10/01 10:18:17  bpereira
103 //
104 // Revision 1.20  2000/10/01 09:09:36  hurdler
105 // Put the md2 code in #ifdef TANDL
106 //
107 // Revision 1.19  2000/09/15 19:49:22  bpereira
108 //
109 // Revision 1.18  2000/09/08 22:28:30  hurdler
110 // merge masterserver_ip/port in one cvar, add -private
111 //
112 // Revision 1.17  2000/09/02 15:38:24  hurdler
113 // Add master server to menus (temporaray)
114 //
115 // Revision 1.16  2000/08/31 14:30:55  bpereira
116 //
117 // Revision 1.15  2000/04/24 15:10:56  hurdler
118 // Support colormap for text
119 //
120 // Revision 1.14  2000/04/23 00:29:28  hurdler
121 // fix a small bug in skin color
122 //
123 // Revision 1.13  2000/04/23 00:25:20  hurdler
124 // fix a small bug in skin color
125 //
126 // Revision 1.12  2000/04/22 21:12:15  hurdler
127 //
128 // Revision 1.11  2000/04/22 20:27:35  metzgermeister
129 // support for immediate fullscreen switching
130 //
131 // Revision 1.10  2000/04/16 18:38:07  bpereira
132 // Revision 1.9  2000/04/13 16:26:41  hurdler
133 //
134 // Revision 1.8  2000/04/12 19:31:37  metzgermeister
135 // added use_mouse to menu
136 //
137 // Revision 1.7  2000/04/08 17:29:24  stroggonmeth
138 //
139 // Revision 1.6  2000/04/07 23:11:17  metzgermeister
140 // added mouse move
141 //
142 // Revision 1.5  2000/04/04 10:44:00  hurdler
143 //
144 // Revision 1.4  2000/04/04 00:32:46  stroggonmeth
145 // Initial Boom compatability plus few misc changes all around.
146 //
147 // Revision 1.3  2000/03/23 22:54:00  metzgermeister
148 // added support for HOME/.legacy under Linux
149 //
150 // Revision 1.2  2000/02/27 00:42:10  hurdler
151 // Revision 1.1.1.1  2000/02/22 20:32:33  hurdler
152 // Initial import into CVS (v1.29 pr3)
153 //
154 //
155 // DESCRIPTION:
156 //      DOOM selection menu, options, episode etc.
157 //      Sliders and icons. Kinda widget stuff.
158 //
159 // NOTE:
160 //      Drawing is through V_SetupDraw() and V_DrawScaledPatch()
161 //      so that the menu is scaled to the screen size. The scaling is always
162 //      an integer multiple of the original size, so that the graphics look
163 //      good.
164 //
165 //-----------------------------------------------------------------------------
166 
167 #include <unistd.h>
168 #include <fcntl.h>
169 
170 #include "doomincl.h"
171 #include "am_map.h"
172 #include "dstrings.h"
173 #include "d_main.h"
174 
175 #include "console.h"
176 
177 #include "r_local.h"
178 #include "hu_stuff.h"
179 #include "g_game.h"
180 #include "g_input.h"
181 
182 #include "m_argv.h"
183 
184 // Data.
185 #include "sounds.h"
186 #include "s_sound.h"
187 #include "i_system.h"
188 
189 #include "m_menu.h"
190 #include "v_video.h"
191 #include "i_video.h"
192 
193 #include "keys.h"
194 #include "z_zone.h"
195 #include "w_wad.h"
196 #include "p_local.h"
197 #include "p_fab.h"
198 #include "p_chex.h"
199   // Chex_safe_pictures
200 
201 #include "p_saveg.h"
202   // savegame header read
203 
204 #ifdef HWRENDER
205 #include "hardware/hw_main.h"
206 #endif
207 
208 #include "d_net.h"
209 #include "d_clisrv.h"
210   // server1, server2, server3
211 #include "mserv.h"
212 #include "p_inter.h"
213 #include "m_misc.h"
214   // config
215 
216 
217 boolean                 menuactive;
218 
219 // [WDJ] mouse sensitivity, 30 for old feel, 40..50 to reduce
220 #define MENU_MOUSE_TRIG   40
221 
222 #define SKULLXOFF       -32
223 #define LINEHEIGHT       16
224 #define STRINGHEIGHT     10
225 #define FONTBHEIGHT      20
226 #define SMALLLINEHEIGHT   8
227 #define SLIDER_RANGE     10
228 #define SLIDER_WIDTH    (8*SLIDER_RANGE+6)
229 #define MAXSTRINGLENGTH  32
230 
231 // [WDJ] Definition of slot.
232 // SLOT is the number attached to the savegame name.
233 // MSLOT is the number of MENU_SLOT.
234 // The savegamedisp table is indexed by slotindex [0..5],
235 // which for 99 savegames and with directories (slotindex=[1..6]),
236 // is different than the slot number [0..99].
237 // Entry savegamedisp[6] is reserved for quickSave.
238 
239 // Save and Load menus must match the SAVEGAME_NUM_SLOT
240 #define SAVEGAME_NUM_MSLOT  6
241 
242 // Map and Time on left side, otherwise on the right side
243 #define SAVEGAME_MTLEFT
244 
245 // [WDJ] Fonts are variable width, so it does not actually overlap, most times.
246 #define SAVEGAME_MTLEN  12
247 
248 #ifdef SAVEGAME_MTLEFT
249 // position in the line of map name and time
250 #define SAVE_DESC_POS   12
251 #define SAVE_DESC_XPOS  (SAVE_DESC_POS*8)
252 #define SAVELINELEN (SAVE_DESC_POS+SAVESTRINGSIZE-2)
253 #else
254 // position in the line of map name and time
255 #define SAVE_MT_POS     22
256 #define SAVELINELEN (SAVE_MT_POS+SAVEGAME_MTLEN)
257 #endif
258 
259 #ifdef SAVEGAMEDIR
260 char  savegamedir[SAVESTRINGSIZE] = "";  // default is main legacy dir
261 #endif
262 
263 typedef struct
264 {
265 #ifdef SAVEGAME99
266     byte  savegameid;	// 0..99, else invalid
267 #endif
268     char  desc[SAVESTRINGSIZE];
269     char  levtime[SAVEGAME_MTLEN];
270 } savegame_disp_t;
271 
272 // disp slots 0..5, and an extra 6=quicksave
273 static savegame_disp_t    savegamedisp[SAVEGAME_NUM_MSLOT+1];
274 
275 static int   slotindex;  // MSLOT, used for reading and editing slot desc, 0..5
276 // quicksave_slotid: -1 = no slot picked!, -2 = select slot now, else is slot
277 static int   quicksave_slotid; // -1,-2, 0..5 or 0..99 (savegameid)
278 
279 #if defined SAVEGAMEDIR || defined SAVEGAME99
280 #define     QUICKSAVE_INDEX   SAVEGAME_NUM_MSLOT
281 static int     scroll_index = 0;  // scroll position
282 static void    (*scroll_callback)(int amount) = NULL; // call to scroll
283 static void    (*delete_callback)(int ch) = NULL; // call to delete
284 #else
285 // otherwise the slot and index are the same
286 #define     QUICKSAVE_INDEX   quicksave_slotid
287 #endif
288 
289 
290 // menu string edit, used for entering a savegame string
291 static boolean edit_enable;
292 static int     edit_index;  // which char we're editing
293 // menu edit
294 static char    edit_buffer[SAVESTRINGSIZE];
295 static void    (*edit_done_callback)(void) = NULL;  // call upon edit done
296 
297 // clear the savegamedisp from mslot to SAVEGAME_NUM_MSLOT
298 static
clear_remaining_savegamedisp(int sgslot)299 void clear_remaining_savegamedisp( int sgslot )
300 {
301     // fill out as empty any remaining menu positions
302     while( sgslot < SAVEGAME_NUM_MSLOT )  // do not overrun quicksave
303     {
304         savegamedisp[sgslot].desc[0] = '\0';
305         savegamedisp[sgslot].levtime[0] = '\0';
306 #ifdef SAVEGAME99
307         savegamedisp[sgslot].savegameid = 255;   // invalid
308 #endif
309         sgslot ++;
310     }
311 }
312 
313 static
setup_net_savegame(void)314 void setup_net_savegame( void )
315 {
316     strcpy( savegamedir, "net" );	// default for network play
317 }
318 
319 // flags for items in the menu
320 typedef enum {
321 // menu handle (what we do when key is pressed)
322   IT_TYPE =  0x000E,   // field
323 // TYPE field values
324   IT_SPACE =      0,   // no handling
325   IT_CALL =       2,   // call the function
326   IT_ARROWS =     4,   // call function with 0 for left arrow and 1 for right arrow in param
327   IT_KEYHANDLER = 6,   // call with the key in param
328   IT_SUBMENU =    8,   // go to sub menu
329   IT_CVAR =      10,   // handle as a cvar
330   IT_MSGHANDLER =12,   // same as key but with event and sometime can handle y/n key (special for message
331 
332   IT_DISPLAY =     0x00F0,  // field
333 // DISPLAY field values
334   IT_NOTHING =          0,  // space
335   IT_PATCH =       0x0010,  // a patch or a string with big font
336   IT_STRING =      0x0020,  // little string (spaced with 10)
337   IT_WHITESTRING = 0x0030,  // little string in white
338   IT_DYBIGSPACE =  0x0040,  // same as nothing
339   IT_DYLITLSPACE = 0x0050,  // little space
340   IT_STRING2 =     0x0060,  // a simple string
341   IT_GRAYPATCH =   0x0070,  // grayed patch or big font string
342   IT_BIGSLIDER =   0x0080,  // volume sound use this
343   IT_EXTERNAL  =   0x00F0,  // nothing, not even a skull
344 
345 //consvar specific
346   IT_CVARTYPE =	  0x0700,   // field
347 // CVARTYPE values
348   IT_CV_NORMAL =       0,
349   IT_CV_SLIDER =  0x0100,
350   IT_CV_STRING =  0x0200,
351   IT_CV_NOPRINT = 0x0300,
352   IT_CV_NOMOD =   0x0400,
353 
354   IT_OPTION  = 0x3000,    // field
355   IT_YOFFSET = 0x1000,    // alphaKey is offset
356   IT_KEYID   = 0x2000,    // alphaKey is id
357 
358 // in short for some common use
359   IT_BIGSPACE =   (IT_SPACE  | IT_DYBIGSPACE),
360   IT_LITLSPACE =  (IT_SPACE  | IT_DYLITLSPACE),
361   IT_CONTROL =    (IT_STRING2| IT_CALL | IT_KEYID),
362   IT_CVARMAX =    (IT_CVAR   | IT_CV_NOMOD),
363   IT_DISABLED =   (IT_SPACE  | IT_GRAYPATCH),
364 } menu_control_e;
365 
366 
367 
368 typedef void (*menufunc_t)(int choice);
369 
370 // [smite] dirty hack, contains a second parameter to IT_KEYHANDLER functions
371 // (int choice is the key)
372 static char input_char;
373 // Return 0= continue, 1= intercept key, 2= testing.
374 static byte (*key_handler2)(int key) = NULL;  // keyboard intercept
375 
is_printable(char c)376 static inline boolean is_printable(char c) { return c >= ' ' && c <= '~'; }
377 
378 typedef union
379 {
380     // [WDJ] can only init to the first union item.
381     void             * init_void;
382     struct menu_s    * submenu;               // IT_SUBMENU
383     consvar_t        * cvar;                  // IT_CVAR
384     menufunc_t         routine;  // IT_CALL, IT_KEYHANDLER, IT_ARROWS
385 } itemaction_t;
386 
387 //
388 // MENU TYPEDEFS
389 //
390 typedef struct menuitem_s
391 {
392     // show IT_xxx
393     uint16_t  status;
394 
395     char     * patch;
396     char     * text;  // used when FONTBxx lump is found
397 
398     // FIXME: should be itemaction_t !!!
399     // [WDJ]  Cannot fix it, all those init will not work.
400     // Can only init to the first union item, when it is anon embedded union,
401     // and then the init union item must be in { }.
402     void     * itemaction;
403 
404     // hotkey in menu
405     // or y of the item when IT_YOFFSET (uses M_DrawGenericMenu)
406     // or in control menus, the control to change (uses M_DrawControl)
407     byte      alphaKey;
408 } menuitem_t;
409 
410 typedef struct menu_s
411 {
412     char          * menutitlepic;
413     const char    * menutitle;              // title as string for display with fontb if present
414     menuitem_t    * menuitems;              // menu items
415     void            (*drawroutine)(void);   // draw routine
416     boolean         (*quitroutine)(void);   // called before quit a menu return true if we can
417     uint16_t        numitems;               // # of menu items
418     uint16_t        x;
419     uint16_t        y;                      // x,y of menu
420     byte            lastOn;                 // last item user was on in menu
421 } menu_t;
422 
423 #define  NUM_MENUSTACK  8
424 menu_t * menustack[ NUM_MENUSTACK+1 ];
425 byte  menucnt = 0;
426 
427 // current menudef
428 static menu_t  * currentMenu = NULL;
429 static menuitem_t * menuline = NULL; // menu line that invoked a call or submenu
430 static byte    itemOn;             // 0..40, menu item skull is on
431 static int8_t  skullAnimCounter;   // 0..10, skull animation counter
432 static byte    whichSkull;         // 0,1 which skull to draw, >128 off
433 static int     SkullBaseLump;
434 
435 
436 #ifdef CONFIG_MENU_PAGE
437 static byte   menu_cfg = 0; // when non-zero, only those config are shown
438 static byte   menu_cfg_editing = 0;  // to disable live changes
439 static const char *  menu_cfg_string[4] = { "", "MAIN", "DRAWMODE", "OTHER" };
440 #endif
441 
442 // graphic name of skulls
443 static char    skullName[2][9] = {"M_SKULL1","M_SKULL2"};
444 
445 // [WDJ] menu sounds
446 static sfxid_t menu_sfx_updown = sfx_menuud;
447 static sfxid_t menu_sfx_val = sfx_menuva;
448 static sfxid_t menu_sfx_enter = sfx_menuen;
449 static sfxid_t menu_sfx_open = sfx_menuop;
450 static sfxid_t menu_sfx_esc = sfx_menuop;
451 static sfxid_t menu_sfx_action = sfx_menuac;
452 
453 CV_PossibleValue_t menusound_cons_t[] =
454   {{0,"Auto"}, {1,"Legacy"}, {2,"Doom"}, {3,"Heretic"}, {0,NULL}};
455 void CV_menusound_OnChange(void);
456 consvar_t cv_menusound = {"menusound", "1", CV_SAVE | CV_CALL, menusound_cons_t, CV_menusound_OnChange };
457 
CV_menusound_OnChange(void)458 void CV_menusound_OnChange(void)
459 {
460     byte menusound = cv_menusound.EV;
461 
462     switch ( gamemode )
463     {
464       case doom2_commercial:
465       case doom_shareware:
466       case doom_registered:
467       case ultdoom_retail:
468         if ( menusound == 0 || menusound == 3 )
469            menusound = 2;
470         break;
471       case heretic:
472         if ( menusound == 0 || menusound == 2 )
473            menusound = 3;
474         break;
475       case chexquest1:
476         if ( menusound == 0 || menusound == 3 )
477            menusound = 1;
478         break;
479       default:
480         break;
481     }
482     switch ( menusound )
483     {
484       default:
485       case 0: // auto
486       case 1: // Legacy
487         menu_sfx_updown = sfx_menuud;
488         menu_sfx_val = sfx_menuva;
489         menu_sfx_enter = sfx_menuen;
490         menu_sfx_open = sfx_menuop;
491         menu_sfx_esc = sfx_menuop;
492         menu_sfx_action = sfx_menuac;
493         break;
494       case 2: // Doom
495         //Boom
496         // help, save, load, volume menu open = sfx_swtchn
497         // backspace = sfx_swtchn
498         // menu action = sfx_itemup
499         // next menu = sfx_swtchx
500         menu_sfx_updown = sfx_pstop;
501         menu_sfx_val = sfx_stnmov;
502         menu_sfx_enter = sfx_pistol;
503         menu_sfx_open = sfx_swtchn;
504         menu_sfx_esc = sfx_swtchx;
505         menu_sfx_action = sfx_swtchx;
506         break;
507       case 3: // Heretic
508         //heretic
509         // quit, chat val = sfx_chat;
510         // chat keys = sfx_keyup;
511         // info, save, load, volume menu open  = sfx_dorcls;
512         // enter, activate menu, deactivate menu = sfx_dorcls;
513         // escape = none;
514         // backspace = sfx_switch;
515         menu_sfx_updown = sfx_swtchx;  // sfx_switch
516         menu_sfx_val = sfx_keyup;
517         menu_sfx_enter = sfx_dorcls;
518         menu_sfx_open = sfx_dorcls;
519         menu_sfx_esc = sfx_menuva;  // none
520 //        menu_sfx_action = sfx_chat;  // don't have sfx_chat
521         menu_sfx_action = sfx_menuac;
522         break;
523     }
524 }
525 
526 
527 //
528 // PROTOTYPES
529 //
530 static void M_Draw_SaveLoadBorder(int x, int y, boolean longer);
531 static void Push_Setup_Menu(menu_t *menudef);
532 static void Pop_Menu( void );
533 
534 void M_DrawTextBox (int x, int y, int width, int lines);     //added:06-02-98:
535 static void M_DrawThermo(int x,int y,consvar_t *cv);
536 #if 0
537 static void M_DrawEmptyCell(menu_t *menu,int item);
538 static void M_DrawSelCell(menu_t *menu,int item);
539 #endif
540 
541 static void M_DrawSlider (int x, int y, int range);
542 static void M_CentreText(int y, char* string); //added:30-01-98:writetext centered
543 
544 static void M_StopMessage(int choice);
545 static void M_Clear_Menus (boolean callexitmenufunc);
546 static int  M_StringHeight(char* string);
547 static void M_GameOption(int choice);
548 static void M_AdvOption(int choice);
549 static void M_BotOption(int choice);
550 static void M_NetOption(int choice);
551 //28/08/99: added by Hurdler
552 static void M_OpenGLOption(int choice);
553 static void M_PlayerDirector(int choice);
554 
555 menu_t MainDef, SoundDef, EpiDef, NewDef,
556   VideoModeDef, VideoOptionsDef, DrawmodeDef, MouseOptionsDef,
557   PlayerDirectorDef, PlayerOptionsDef,
558   SingleMultiDef, TwoPlayerDef, MultiPlayerDef, SetupMultiPlayerDef,
559   ReadDef2, ReadDef1, SaveDef, LoadDef,
560   ControlDef, ControlDef2, ControlDef3, MControlDef, JoystickOptionsDef,
561   OptionsDef, EffectsOption1Def, EffectsOption2Def, AdvOption1Def, AdvOption2Def,
562   GameOptionDef, MenuOptionsDef, LightingDef, BotDef,
563   NetOptionDef, ConnectOptionDef, ServerOptionsDef,
564   MPOptionDef;
565 
566 
567 //===========================================================================
568 //Generic Stuffs (more easy to create menus :))
569 //===========================================================================
570 
571 static
M_DrawMenuTitle(void)572 void M_DrawMenuTitle(void)
573 {
574     if( FontBBaseLump && currentMenu->menutitle )
575     {
576         int xtitle = (BASEVIDWIDTH-V_TextBWidth(currentMenu->menutitle))/2;
577         int ytitle = (currentMenu->y-V_TextBHeight(currentMenu->menutitle))/2;
578         if(xtitle<0) xtitle=0;
579         if(ytitle<0) ytitle=0;
580 
581         V_DrawTextB(currentMenu->menutitle, xtitle, ytitle);
582     }
583     else
584     if( currentMenu->menutitlepic )
585     {
586         patch_t* tp = W_CachePatchName(currentMenu->menutitlepic,PU_CACHE);  // endian fix
587 #if 1
588         //SoM: 4/7/2000: Old code was causing problems with large graphics.
589 //        int xtitle = (vid.width / 2) - (p->width / 2);
590 //        int ytitle = (y-p->height)/2;
591         int xtitle = 94;
592         int ytitle = 2;
593 #else
594         int xtitle = (BASEVIDWIDTH - tp->width)/2;
595         int ytitle = (currentMenu->y - tp->height)/2;
596 #endif
597 
598         if(xtitle<0) xtitle=0;
599         if(ytitle<0) ytitle=0;
600         V_DrawScaledPatch (xtitle, ytitle, tp);
601     }
602     else
603     if( currentMenu->menutitle && !use_font1 )
604     {
605         int xtitle = (BASEVIDWIDTH-V_StringWidth(currentMenu->menutitle))/2;
606         int ytitle = (currentMenu->y - 16)/2;
607         if(xtitle<0) xtitle=0;
608         if(ytitle<1) ytitle=1;
609 
610         V_DrawString(xtitle, ytitle, 0, currentMenu->menutitle);
611     }
612 }
613 
614 
615 
616 static
M_DrawGenericMenu(void)617 void M_DrawGenericMenu(void)
618 {
619     fontinfo_t * fip = V_FontInfo();
620     menuitem_t * mip;
621     int x, y, w;
622     int cursory=0;
623     byte i;
624 
625     // DRAW MENU
626     // Draw to screen0, scaled
627     x = currentMenu->x;
628     y = currentMenu->y;
629 
630     // draw title (or big pic)
631     M_DrawMenuTitle();
632 
633 #ifdef CONFIG_MENU_PAGE
634     if( menu_cfg )
635     {
636         V_DrawString( 2, 1, V_WHITEMAP, menu_cfg_string[menu_cfg]);
637         V_DrawString( BASEVIDWIDTH - (14*8), 1, V_WHITEMAP, "Insert Delete");
638     }
639 #endif
640 
641     for (i=0; i<currentMenu->numitems; i++)
642     {
643         mip = & currentMenu->menuitems[i];
644         // handle Y offsets independent of IT_STRING and IT_WHITESTRING
645         if( ((mip->status & IT_OPTION) == IT_YOFFSET) && mip->alphaKey )
646         {
647             y = currentMenu->y + mip->alphaKey;
648         }
649         if (i==itemOn)
650             cursory=y;
651 
652         switch (mip->status & IT_DISPLAY)
653         {
654            case IT_PATCH  :
655                if( FontBBaseLump && mip->text )
656                {
657                    V_DrawTextB(mip->text, x, y);
658                    y += FONTBHEIGHT-LINEHEIGHT;
659                }
660                else
661                if( mip->patch && mip->patch[0] )
662                {
663                    V_DrawScaledPatch_Name (x,y, mip->patch );
664                }
665                // add lineheight
666            case IT_NOTHING:
667            case IT_EXTERNAL:
668            case IT_DYBIGSPACE:
669                y += LINEHEIGHT;
670                break;
671            case IT_BIGSLIDER :
672                M_DrawThermo( x, y, (consvar_t *)mip->itemaction);
673                y += LINEHEIGHT;
674                break;
675            case IT_STRING :
676            case IT_WHITESTRING :
677 #ifdef CONFIG_MENU_PAGE
678                if( menu_cfg && (mip->status & IT_TYPE) == IT_CVAR )
679                {
680                    consvar_t * cv = (consvar_t *) mip->itemaction;
681                    if( ! (cv->flags & CV_SAVE) )
682                        goto finish_string_line;  // not configurable
683                }
684 #endif
685                if( (mip->status & IT_DISPLAY)==IT_STRING )
686                    V_DrawString(x,y,0,mip->text);
687                else
688                    V_DrawString(x,y,V_WHITEMAP,mip->text);
689 
690                // Cvar specific handling
691                switch (mip->status & IT_TYPE)
692                {
693                  case IT_CVAR:
694                  {
695                    consvar_t * cv = (consvar_t *) mip->itemaction;
696                    const char * cvsp = cv->string;
697 #ifdef CONFIG_MENU_PAGE
698                    if( menu_cfg && ((cv->state & CS_CONFIG) != menu_cfg ) )
699                    {
700                        cvsp = CV_Get_Config_string( cv, menu_cfg );
701                        if( ! cvsp )  goto finish_string_line;  // not present
702                    }
703 #endif
704                    switch (mip->status & IT_CVARTYPE)
705                    {
706                        case IT_CV_SLIDER :
707                            M_DrawSlider (BASEVIDWIDTH - x - SLIDER_WIDTH, y,
708                                          ( (cv->value - cv->PossibleValue[0].value) * 100 /
709                                          (cv->PossibleValue[1].value - cv->PossibleValue[0].value)));
710                        case IT_CV_NOPRINT: // color use this
711                            break;
712                        case IT_CV_STRING:
713                            w = V_StringWidth( cvsp );
714                            if( use_font1 )
715                            {
716                                // Setup is centered, but this needs left justify.
717                                M_DrawTextBox(-BASEVIDWIDTH/2,y+12,BASEVIDWIDTH/7,1);
718                                const char * s = cvsp;
719                                while( *s && (w > BASEVIDWIDTH - 8) )
720                                {
721                                    w -= fip->xinc;
722                                    s++;
723                                }
724                                V_DrawString (x+8,y+12,0, cvsp);
725 //                             if( skullAnimCounter<4 && i==itemOn )
726                                if( i==itemOn )
727                                   V_DrawCharacter( x+8+w, y+12,  '_' | 0x80);  // white
728                            }
729                            else
730                            {
731                                M_DrawTextBox(x,y+4,MAXSTRINGLENGTH,1);
732                                V_DrawString (x+8,y+12,0, cvsp);
733                                if( skullAnimCounter<4 && i==itemOn )
734                                   V_DrawCharacter( x+8+w, y+12,  '_' | 0x80);  // white
735                            }
736                            y+=16;
737                            break;
738                        default:
739                            if( ! cvsp )
740                            {
741                                I_SoftError("GenMenu: cvar NULL string %s\n", cv->name );
742                                break;
743                            }
744                            V_DrawString(BASEVIDWIDTH - x - V_StringWidth( cvsp ),
745                                         y, V_WHITEMAP,
746                                         cvsp );
747                            break;
748                    }
749                    break;
750                  }
751                } // switch IT_TYPE
752 #ifdef CONFIG_MENU_PAGE
753           finish_string_line:
754 #endif
755                y+=STRINGHEIGHT;
756                break;
757            case IT_STRING2:
758                V_DrawString (x,y,0,mip->text);
759            case IT_DYLITLSPACE:
760                y+=SMALLLINEHEIGHT;
761                break;
762            case IT_GRAYPATCH:
763                if( FontBBaseLump && mip->text )
764                {
765                    V_DrawTextBGray(mip->text, x, y);
766                    y += FONTBHEIGHT-LINEHEIGHT;
767                }
768                else
769                if( mip->patch &&
770                    mip->patch[0] )
771                {
772                    V_DrawMappedPatch_Name (x,y, mip->patch, graymap );
773                }
774                y += LINEHEIGHT;
775                break;
776 
777         } // switch IT_DISPLAY
778     }  // for menu lines
779 
780     if( whichSkull > 1 )  return;
781 
782     // DRAW THE SKULL CURSOR
783     if (((currentMenu->menuitems[itemOn].status & IT_DISPLAY)==IT_PATCH)
784       ||((currentMenu->menuitems[itemOn].status & IT_DISPLAY)==IT_NOTHING) )
785     {
786         V_DrawScaledPatch_Name(currentMenu->x + SKULLXOFF, cursory-5,
787                           skullName[whichSkull] );
788     }
789     else
790     if (skullAnimCounter<4 * NEWTICRATERATIO)  //blink cursor
791     {
792         V_DrawCharacter(currentMenu->x - 10, cursory,
793                         '*' | 0x80);  // white
794     }
795 
796 }
797 
798 
799 #ifdef CONFIG_MENU_PAGE
800 //===========================================================================
801 // Edit configfile values using menu_cfg.
802 
803 static byte  temp_cvar_active = 0;
804 static consvar_t  temp_cvar;
805 static consvar_t * temp_cvar_parent;
806 
807 // Handle editing cvar that are not the current cvar.
808 // May return ptr to a temp cvar, so must save_cv after editing.
809 // May return NULL, which means nothing to edit.
810 static
config_cvar_edit_open(consvar_t * cv)811 consvar_t *  config_cvar_edit_open( consvar_t * cv )
812 {
813     temp_cvar_active = 0;
814 
815     if( menu_cfg && ((cv->state & CS_CONFIG) != menu_cfg) )
816     {
817         // Use a temp, to avoid having to modify so many command CV_Set functions.
818         temp_cvar_parent = cv;
819         // Make sure the temp is empty.
820         if( temp_cvar.string )  // it should be NULL, but may not have been saved previously.
821             CV_Free_cvar_string( &temp_cvar );
822         // Get the hidden cvar value.
823         temp_cvar_active = CV_Get_Pushed_cvar( temp_cvar_parent, menu_cfg, /*OUT*/ & temp_cvar );
824         if( temp_cvar_active == 0 )
825             return NULL;  // nothing to change
826 
827         // kill any effects that the current cvar would perform.
828         temp_cvar.flags &= ~( CV_CALL | CV_NETVAR | CV_SHOWMODIF | CV_SHOWMODIF_ONCE );
829         return &temp_cvar;
830     }
831     return cv;  // normal edit of current cvar
832 }
833 
834 // If a temp cvar was used, then it will be saved.
835 static
config_cvar_edit_save(void)836 void  config_cvar_edit_save( void )
837 {
838     if( temp_cvar_active )
839     {
840         // Put value back into pushed cvar.
841         // The string value of temp_cvar will be stolen.  Do not need to Z_Free it.
842         CV_Put_Config_cvar( temp_cvar_parent, menu_cfg, /*IN*/ & temp_cvar );
843         temp_cvar_active = 0;
844         menu_cfg_editing = 0;
845     }
846 }
847 
848 // Edit a configfile cvar value, using menu_cfg.
849 static
config_cvar_edit_setvalue(consvar_t * cv_parent,int value)850 void  config_cvar_edit_setvalue( consvar_t * cv_parent, int value )
851 {
852     consvar_t * cv = config_cvar_edit_open( cv_parent );  // to temp_cvar
853     if( cv )
854     {
855         // Here, cv may be current cvar, or temp_cvar,
856         // either way it is safe to call CV_Set.
857         CV_SetValue( cv, value );
858         config_cvar_edit_save();  // saves temp_cvar
859     }
860 }
861 
862 // Create a new configfile cvar entry, using menu_cfg.
863 static
config_cvar_edit_insert(consvar_t * cv_parent,byte copy_flag)864 void  config_cvar_edit_insert( consvar_t * cv_parent, byte copy_flag )
865 {
866     if( ! (cv_parent->flags & CV_SAVE) )
867         goto done;  // not in config file
868 
869     if( (cv_parent->state & CS_CONFIG) == menu_cfg )
870         goto done;  // already exists as current cvar
871 
872     if( CV_Get_Pushed_cvar( cv_parent, menu_cfg, NULL ) )  // test of existance
873         goto done;  // already exists as pushed cvar
874 
875     // Create the cvar value, even if it is pushed and not current.
876     const char * newstr = ( copy_flag )?
877         cv_parent->string // Copy existing value.
878       : cv_parent->defaultvalue ; // Get default value.
879     CV_Put_Config_string( cv_parent, menu_cfg, newstr );
880 
881 done:
882     return;
883 }
884 
885 // Delete the configfile cvar entry, using menu_cfg.
886 static
config_cvar_edit_delete(consvar_t * cv_parent)887 void  config_cvar_edit_delete( consvar_t * cv_parent )
888 {
889     // This can delete a current cvar or pushed cvar value.
890     CV_Delete_Config_cvar( cv_parent, menu_cfg );
891 }
892 
893 // Edit configfile entries for most common menus.
894 static
config_cvar_edit_key_handler(int key)895 byte  config_cvar_edit_key_handler( int key )
896 {
897     menuitem_t * mip = & currentMenu->menuitems[itemOn];
898     consvar_t * cv_parent;
899 
900     if( (mip->status & IT_TYPE) != IT_CVAR )
901         goto fail;  // not a cvar menu entry
902 
903     cv_parent = (consvar_t *)mip->itemaction;
904 
905     switch( key )
906     {
907      case KEY_INS :  // insert config
908         config_cvar_edit_insert( cv_parent, 0 );  // using menu_cfg
909         goto done;
910 
911      case KEY_DELETE :  // delete config
912         config_cvar_edit_delete( cv_parent );  // using menu_cfg
913         goto done;
914 
915      default:
916         goto fail;
917     }
918 
919 fail:
920     return false; // did not use the key
921 
922 done:
923     return true;  // used the key
924 }
925 
926 // Enter and leave menu_cfg mode.
927 static
config_cvar_key_handler(int key)928 byte  config_cvar_key_handler( int key )
929 {
930     switch( key )
931     {
932 #if 1
933       case KEY_F2:
934 #else
935       case 'M':
936       case 'm':
937 #endif
938         if( menu_cfg == CFG_main )  // toggle
939             goto turn_menu_cfg_off;
940         // Only show the values from the Main config file.
941         menu_cfg = CFG_main;
942 
943         goto done;
944 #if 1
945       case KEY_F3:
946 #else
947       case 'D':
948       case 'd':
949 #endif
950         if( menu_cfg == CFG_drawmode )  // toggle
951             goto turn_menu_cfg_off;
952         // Only show the values from the Drawmode config file.
953         menu_cfg = CFG_drawmode;
954         goto done;
955 #if 1
956       case KEY_F1:
957       case KEY_F4:
958 #else
959       case 'N':
960       case 'n':
961 #endif
962       turn_menu_cfg_off:
963         // Normal menu values.
964         menu_cfg = CFG_none;
965         menu_cfg_editing = 0;
966         goto done;
967 
968      default:
969         break;
970     }
971 
972     if( menu_cfg )
973     {
974         if( config_cvar_edit_key_handler( key ) )
975             goto done;
976     }
977 
978     return false; // did not use the key
979 
980 done:
981     return true;  // used the key
982 }
983 #endif
984 
985 
986 // Create initial drawmode config file.
987 static
create_initial_drawmode_config(void)988 void  create_initial_drawmode_config( void )
989 {
990     // M_Set_configfile_drawmode( ) was done at mode switch.
991 
992     if( M_Have_configfile_drawmode() )
993         return;
994 
995     S_StartSound(menu_sfx_enter);
996     menu_cfg = CFG_drawmode; // using menu_cfg
997     config_cvar_edit_insert( &cv_scr_width, 1 );
998     config_cvar_edit_insert( &cv_scr_height, 1 );
999     config_cvar_edit_insert( &cv_scr_depth, 1 );
1000     config_cvar_edit_insert( &cv_fullscreen, 1 );
1001     config_cvar_edit_insert( &cv_vidwait, 1 );
1002     config_cvar_edit_insert( &cv_gammafunc, 0 );
1003     config_cvar_edit_insert( &cv_usegamma, 0 );
1004     config_cvar_edit_insert( &cv_black, 0 );
1005     config_cvar_edit_insert( &cv_bright, 0 );
1006 
1007     M_Set_configfile_drawmode_present();
1008     M_SaveConfig( CFG_drawmode, configfile_drawmode );
1009 }
1010 
1011 
1012 
1013 //===========================================================================
1014 // All ready playing, quit current game
1015 //===========================================================================
1016 
1017 // [WDJ] message temp buffer (replacing 3 shorter ones)
1018 // StartMessage copies this, and only one possible message at a time
1019 #define      MSGTMP_LEN  255
1020 static char  msgtmp[MSGTMP_LEN+1];
1021 
1022 //const char *ALLREADYPLAYING="You are already playing\n\nLeave this game first\n";
1023 const char * ALLREADYPLAYING="You are already playing.\n\nAbort this game ? Y/N\n";
1024 const char * ABORTGAME="\nAbort this game ? Y/N\n";
1025 
1026 const event_t  reenter_event = { ev_keydown, KEY_ENTER, 0, 1 }; // see M_Responder
1027 
M_Choose_to_quit_Response(int ch)1028 void M_Choose_to_quit_Response(int ch)
1029 {
1030     if (ch == 'y')
1031     {
1032 #if 1
1033         // [WDJ] The quick way to exitgame, including netgame.
1034         Command_ExitGame_f();
1035 #else
1036         // [WDJ] This is why it is not being done this way.
1037         // Why is this system being used for other menus ??
1038         // The hard way to exitgame
1039         COM_BufAddText("exitgame\n");
1040         // unfortunately this takes time, and a couple tics
1041         int i;
1042         for( i=100; i>0; i-- )
1043         {
1044             COM_BufExecute( CFG_none ); // subject to com_wait and other delays
1045             if( ! Game_Playing()  ) break;
1046             // It must be not-playing before re-invoking the menu.
1047         }
1048 #endif
1049         // YES, re-invoke the caller routine at itemOn press
1050         D_PostEvent(&reenter_event);  // delayed reinvoke
1051     }
1052 }
1053 
1054 // [WDJ] Ask user to quit, if they already have a game in-progess.
1055 // Return 1 if already playing, and rejects quitting.
M_already_playing(boolean check_netgame)1056 boolean  M_already_playing( boolean check_netgame )
1057 {
1058     if( Game_Playing() )
1059     {
1060         M_StartMessage(ALLREADYPLAYING, M_Choose_to_quit_Response, MM_YESNO);
1061         return 1;
1062     }
1063     if (check_netgame && netgame)
1064     {
1065         // cannot start a new game while in a network game
1066         M_SimpleMessage(NEWGAME);
1067         snprintf(msgtmp, MSGTMP_LEN, "%s\n%s", NEWGAME, ABORTGAME );
1068         msgtmp[MSGTMP_LEN-1] = '\0';
1069         M_StartMessage(msgtmp, M_Choose_to_quit_Response, MM_YESNO);
1070         return 1;
1071     }
1072     return 0;
1073 }
1074 
1075 //===========================================================================
1076 //MAIN MENU
1077 //===========================================================================
1078 
1079 void M_Loadgame(int choice);
1080 void M_Savegame(int choice);
1081 void M_QuitDOOM(int choice);
1082 
1083 enum
1084 {
1085     MM_readthis = 4,	// referenced
1086     MM_quitdoom = 5,	// referenced
1087 } main_e;
1088 
1089 // Compatible with modifications to original graphics
1090 menuitem_t MainMenu[]=
1091 {
1092     {IT_SUBMENU | IT_PATCH,"M_NGAME" ,"NEW GAME" ,&SingleMultiDef,'n'},
1093     {IT_CALL    | IT_PATCH,"M_LOADG" ,"LOAD GAME",M_Loadgame,'l'},
1094     {IT_CALL    | IT_PATCH,"M_SAVEG" ,"SAVE GAME",M_Savegame,'s'},
1095     {IT_SUBMENU | IT_PATCH,"M_OPTION","OPTIONS"  ,&OptionsDef,'o'},
1096     {IT_SUBMENU | IT_PATCH,"M_RDTHIS","INFO"     ,&ReadDef1  ,'r'},  // Another hickup with Special edition.
1097     {IT_CALL    | IT_PATCH,"M_QUITG" ,"QUIT GAME",M_QuitDOOM,'q'}
1098 };
1099 
HereticMainMenuDrawer(void)1100 void HereticMainMenuDrawer(void)
1101 {
1102     int frame = (I_GetTime()/3)%18;
1103 
1104     V_DrawScaledPatch_Num(40, 10, SkullBaseLump+(17-frame) );
1105     V_DrawScaledPatch_Num(232, 10, SkullBaseLump+frame );
1106     M_DrawGenericMenu();
1107 }
1108 
1109 menu_t  MainDef =
1110 {
1111     "M_DOOM",
1112     NULL,
1113     MainMenu,
1114     M_DrawGenericMenu,
1115     NULL,
1116     sizeof(MainMenu)/sizeof(menuitem_t),
1117     97,64,
1118     0
1119 };
1120 
1121 //===========================================================================
1122 //SINGLE/MULTI PLAYER GAME MENU
1123 //===========================================================================
1124 
1125 static void M_SingleNewGame(int choice);
1126 static void M_TwoPlayerMenu(int choice);
1127 static void M_EndGame(int choice);
1128 
1129 // DoomLegacy graphics from legacy.wad: M_SINGLE, M_2PLAYR, M_MULTI
1130 menuitem_t SingleMulti_Menu[] =
1131 {
1132     {IT_CALL | IT_PATCH,"M_SINGLE","SINGLE PLAYER",M_SingleNewGame ,'s'},
1133     {IT_CALL | IT_PATCH,"M_2PLAYR","TWO PLAYER GAME",M_TwoPlayerMenu ,'n'},
1134     {IT_SUBMENU | IT_PATCH,"M_MULTI" ,"MULTIPLAYER",&MultiPlayerDef  ,'m'},
1135     {IT_CALL | IT_PATCH,"M_ENDGAM","END GAME",M_EndGame ,'e'}
1136 };
1137 
1138 menu_t  SingleMultiDef =
1139 {
1140     "M_NGAME",
1141     "Single Multi New Game",
1142     SingleMulti_Menu,
1143     M_DrawGenericMenu,
1144     NULL,
1145     sizeof(SingleMulti_Menu)/sizeof(menuitem_t),
1146     97,64,
1147     0
1148 };
1149 
1150 
1151 //===========================================================================
1152 // Connect Menu
1153 //===========================================================================
1154 
1155 CV_PossibleValue_t serversearch_cons_t[] = {{0,"Local Lan"}, {1,"Internet"}, {0,NULL}};
1156 consvar_t cv_serversearch = {"serversearch"    ,"0",CV_HIDEN,serversearch_cons_t};
1157 
1158 #define FIRSTSERVERLINE 3
1159 
M_Connect(int choice)1160 void M_Connect( int choice )
1161 {
1162     // do not call menuexitfunc
1163     M_Clear_Menus(false);
1164 
1165     // Invoke Command_connect
1166     COM_BufAddText(va("connect node %d\n",
1167                       serverlist[choice-FIRSTSERVERLINE].server_node));
1168     setup_net_savegame();
1169 }
1170 
1171 static int localservercount;
1172 
M_Refresh(int choice)1173 void M_Refresh( int choice )
1174 {
1175     CL_Update_ServerList( cv_serversearch.value );
1176 }
1177 
1178 menuitem_t  ConnectMenu[] =
1179 {
1180     {IT_STRING | IT_CVAR ,0,"Search On"       ,&cv_serversearch       ,0},
1181     {IT_STRING | IT_CALL ,0,"Refresh"         ,M_Refresh              ,0},
1182     {IT_WHITESTRING | IT_SPACE,0,
1183                 "Server Name                           ping players dm" ,0 ,0},
1184     {IT_STRING | IT_SPACE,0,""             ,M_Connect              ,0},
1185     {IT_STRING | IT_SPACE,0,""             ,M_Connect              ,0},
1186     {IT_STRING | IT_SPACE,0,""             ,M_Connect              ,0},
1187     {IT_STRING | IT_SPACE,0,""             ,M_Connect              ,0},
1188     {IT_STRING | IT_SPACE,0,""             ,M_Connect              ,0},
1189     {IT_STRING | IT_SPACE,0,""             ,M_Connect              ,0},
1190     {IT_STRING | IT_SPACE,0,""             ,M_Connect              ,0},
1191     {IT_STRING | IT_SPACE,0,""             ,M_Connect              ,0},
1192     {IT_STRING | IT_SPACE,0,""             ,M_Connect              ,0},
1193     {IT_STRING | IT_SPACE,0,""             ,M_Connect              ,0},
1194     {IT_STRING | IT_SPACE,0,""             ,M_Connect              ,0},
1195     {IT_STRING | IT_SPACE,0,""             ,M_Connect              ,0},
1196     {IT_STRING | IT_SPACE,0,""             ,M_Connect              ,0},
1197 };
1198 
M_DrawConnectMenu(void)1199 void M_DrawConnectMenu( void )
1200 {
1201     int i, sly;
1202     char *p;
1203 
1204     for( i=FIRSTSERVERLINE; i<localservercount+FIRSTSERVERLINE; i++ )
1205         ConnectMenu[i].status = IT_STRING | IT_SPACE;
1206 
1207     sly = currentMenu->y + (FIRSTSERVERLINE * STRINGHEIGHT);
1208     if( serverlistcount <= 0 )
1209         V_DrawString (currentMenu->x, sly, 0, "No server found");
1210     else
1211     for( i=0; i<serverlistcount; i++ )
1212     {
1213         if( i >= ((sizeof(ConnectMenu)/sizeof(menuitem_t)) - FIRSTSERVERLINE) )
1214             break; // too many to draw all
1215         V_DrawString (currentMenu->x, sly, 0, serverlist[i].info.servername);
1216         p = va("%d", serverlist[i].info.trip_time);  // ping time
1217         V_DrawString (currentMenu->x + 200 - V_StringWidth(p), sly, 0, p);
1218         p = va("%d/%d  %d", serverlist[i].info.num_active_players,
1219                             serverlist[i].info.maxplayer,
1220                             serverlist[i].info.deathmatch);
1221         V_DrawString (currentMenu->x + 250 - V_StringWidth(p), sly, 0, p);
1222         sly += STRINGHEIGHT;
1223 
1224         ConnectMenu[i+FIRSTSERVERLINE].status = IT_STRING | IT_CALL;
1225     }
1226     localservercount = serverlistcount;
1227 
1228     M_DrawGenericMenu();
1229 }
1230 
M_CancelConnect(void)1231 boolean M_CancelConnect(void)
1232 {
1233     D_CloseConnection();
1234     return true;
1235 }
1236 
1237 menu_t  Connectdef =
1238 {
1239     "M_CONNEC", // in legacy.wad
1240     "Connect Server",
1241     ConnectMenu,
1242     M_DrawConnectMenu,
1243     M_CancelConnect,
1244     sizeof(ConnectMenu)/sizeof(menuitem_t),
1245     27,40,
1246     0
1247 };
1248 
1249 // Select Connect Menu
M_ConnectMenu(int choice)1250 void M_ConnectMenu(int choice)
1251 {
1252     if( M_already_playing(0) )  return;
1253 
1254     D_End_commandline();
1255 
1256     Push_Setup_Menu(&Connectdef);
1257     M_Refresh(0);
1258 }
1259 
1260 //===========================================================================
1261 // Start Server Menu
1262 //===========================================================================
1263 
1264 CV_PossibleValue_t skill_cons_t[] = {{1,"I'm too young to die"}
1265                                     ,{2,"Hey, not too rough"}
1266                                     ,{3,"Hurt me plenty"}
1267                                     ,{4,"Ultra violence"}
1268                                     ,{5,"Nightmare!" }
1269                                     ,{0,NULL}};
1270 
1271 CV_PossibleValue_t map_cons_t[] = {{ 1,"map01"} ,{ 2,"map02"} ,{ 3,"map03"}
1272                                   ,{ 4,"map04"} ,{ 5,"map05"} ,{ 6,"map06"}
1273                                   ,{ 7,"map07"} ,{ 8,"map08"} ,{ 9,"map09"}
1274                                   ,{10,"map10"} ,{11,"map11"} ,{12,"map12"}
1275                                   ,{13,"map13"} ,{14,"map14"} ,{15,"map15"}
1276                                   ,{16,"map16"} ,{17,"map17"} ,{18,"map18"}
1277                                   ,{19,"map19"} ,{20,"map20"} ,{21,"map21"}
1278                                   ,{22,"map22"} ,{23,"map23"} ,{24,"map24"}
1279                                   ,{25,"map25"} ,{26,"map26"} ,{27,"map27"}
1280                                   ,{28,"map28"} ,{29,"map29"} ,{30,"map30"}
1281                                   ,{31,"map31"} ,{32,"map32"} ,{0,NULL}};
1282 
1283 CV_PossibleValue_t exmy_cons_t[] ={{11,"e1m1"} ,{12,"e1m2"} ,{13,"e1m3"}
1284                                   ,{14,"e1m4"} ,{15,"e1m5"} ,{16,"e1m6"}
1285                                   ,{17,"e1m7"} ,{18,"e1m8"} ,{19,"e1m9"}
1286                                   ,{21,"e2m1"} ,{22,"e2m2"} ,{23,"e2m3"}
1287                                   ,{24,"e2m4"} ,{25,"e2m5"} ,{26,"e2m6"}
1288                                   ,{27,"e2m7"} ,{28,"e2m8"} ,{29,"e2m9"}
1289                                   ,{31,"e3m1"} ,{32,"e3m2"} ,{33,"e3m3"}
1290                                   ,{34,"e3m4"} ,{35,"e3m5"} ,{36,"e3m6"}
1291                                   ,{37,"e3m7"} ,{38,"e3m8"} ,{39,"e3m9"}
1292                                   ,{41,"e4m1"} ,{42,"e4m2"} ,{43,"e4m3"}
1293                                   ,{44,"e4m4"} ,{45,"e4m5"} ,{46,"e4m6"}
1294                                   ,{47,"e4m7"} ,{48,"e4m8"} ,{49,"e4m9"}
1295                                   ,{41,"e5m1"} ,{42,"e5m2"} ,{43,"e5m3"}
1296                                   ,{44,"e5m4"} ,{45,"e5m5"} ,{46,"e5m6"}
1297                                   ,{47,"e5m7"} ,{48,"e5m8"} ,{49,"e5m9"}
1298                                   ,{0,NULL}};
1299 
1300 consvar_t cv_skill    = {"skill"    ,"4",CV_HIDEN,skill_cons_t};
1301 consvar_t cv_monsters = {"monsters" ,"0",CV_HIDEN,CV_YesNo};
1302 // The bots use player slots, so number of bots is limited to MAXPLAYERS.
1303 CV_PossibleValue_t bots_cons_t[] = {{0,"MIN"}, {MAXPLAYERS,"MAX"}, {0,NULL}};
1304 consvar_t cv_bots = {"bots", "0", CV_HIDEN, bots_cons_t};
1305 consvar_t cv_nextmap  = {"nextmap"  ,"1",CV_HIDEN,map_cons_t};
1306 consvar_t cv_nextepmap  = {"nextepmap"  ,"11",CV_HIDEN,exmy_cons_t};
1307 
1308 // To prevent changing game settings while changing between the possible settings.
1309 extern CV_PossibleValue_t deathmatch_cons_t[];
1310 void deathmatch_menu_OnChange( void );
1311 consvar_t cv_deathmatch_menu  = {"dmm"  , "3", CV_HIDEN | CV_CALL, deathmatch_cons_t, deathmatch_menu_OnChange };
1312 
deathmatch_menu_OnChange(void)1313 void deathmatch_menu_OnChange( void )
1314 {
1315     // Default monsters, because it often gets forgotten, and it is not saved.
1316     CV_Set_by_OnChange( &cv_monsters, (cv_deathmatch_menu.EV > 4) );
1317 }
1318 
1319 CV_PossibleValue_t wait_players_cons_t[]=   {{0,"MIN"}, {32,"MAX"}, {0,NULL}};
1320 consvar_t cv_wait_players = {"wait_players" ,"2",CV_HIDEN,wait_players_cons_t};
1321 CV_PossibleValue_t wait_timeout_cons_t[]=   {{0,"MIN"}, {5,"INC"}, {244,"MAX"}, {0,NULL}};
1322 consvar_t cv_wait_timeout = {"wait_timeout" ,"0",CV_HIDEN,wait_timeout_cons_t};
1323 
1324 static boolean StartSplitScreenGame = false;
1325 
M_StartServer(int choice)1326 void M_StartServer( int choice )
1327 {
1328     M_Clear_Menus(true);
1329 
1330     netgame = true;
1331     multiplayer = true;
1332     if( choice == 10 )
1333     {
1334         // Dedicated server menu choice.
1335         dedicated = true;
1336         nodrawers = true;
1337         vid.draw_ready = 0;
1338         I_ShutdownGraphics();
1339     }
1340 
1341     D_WaitPlayer_Setup();
1342 
1343     // Before game start setup.
1344     COM_BufAddText(va("stopdemo;splitscreen %d;deathmatch %d;map \"%s\" -skill %d -monsters %d\n",
1345                       StartSplitScreenGame, cv_deathmatch_menu.value,
1346                       (gamemode==doom2_commercial)? cv_nextmap.string : cv_nextepmap.string,
1347                       cv_skill.value, cv_monsters.value));
1348     // skin change
1349     if (StartSplitScreenGame
1350         && ! ( displayplayer2_ptr
1351              && displayplayer2_ptr->skin
1352              && (strcasecmp(cv_skin[1].string, skins[displayplayer2_ptr->skin]->name) == 0 )
1353              ) )
1354     {
1355         COM_BufAddText ( va("%s \"%s\"\n", cv_skin[1].name, cv_skin[1].string));
1356     }
1357 
1358 #if 0
1359 // Needs to be done after players have grabbed the player slots.
1360     // Add bots
1361     if( cv_bots.EV > 0 )
1362     {
1363         unsigned int cnt = cv_bots.EV;
1364         while( cnt-- )
1365         {
1366 #if 1
1367             COM_BufAddText( "addbot;" );
1368 #else
1369             // Delay to allow splitscreen to grab player2 first.
1370             COM_BufAddText( "wait 35; addbot;" );
1371 #endif
1372         }
1373     }
1374 #endif
1375 }
1376 
1377 menuitem_t  ServerMenu[] =
1378 {
1379     {IT_STRING | IT_CVAR,0,"Map"             ,&cv_nextmap          ,0},
1380     {IT_STRING | IT_CVAR,0,"Skill"           ,&cv_skill            ,0},
1381     {IT_STRING | IT_CVAR,0,"Coop/Deathmatch" ,&cv_deathmatch_menu  ,0},
1382     {IT_STRING | IT_CVAR,0,"Monsters"        ,&cv_monsters         ,0},
1383     {IT_STRING | IT_CVAR,0,"Bots"            ,&cv_bots             ,0},
1384     {IT_STRING | IT_CVAR,0,"Wait Players"    ,&cv_wait_players     ,0},
1385     {IT_STRING | IT_CVAR,0,"Wait Timeout"    ,&cv_wait_timeout     ,0},
1386     {IT_STRING | IT_CVAR,0,"Internet Server" ,&cv_internetserver   ,0},
1387     {IT_STRING | IT_CVAR
1388      | IT_CV_STRING     ,0,"Server Name"     ,&cv_servername       ,0},
1389     {IT_WHITESTRING | IT_CALL | IT_YOFFSET,
1390                          0,"Start"           ,M_StartServer        ,110}, // 9
1391     {IT_WHITESTRING | IT_CALL | IT_YOFFSET,
1392                          0,"Dedicated"       ,M_StartServer        ,120}  // 10
1393 };
1394 
1395 menuitem_t  ServerMenu_Map =
1396     {IT_STRING | IT_CVAR,0,"Map"             ,&cv_nextmap          ,0};
1397 menuitem_t  ServerMenu_EpisodeMap =
1398     {IT_STRING | IT_CVAR,0,"Episode Map"     ,&cv_nextepmap        ,0};
1399 
1400 menu_t  ServerDef =
1401 {
1402     "M_STSERV", // in legacy.wad
1403     "Start Server",
1404     ServerMenu,
1405     M_DrawGenericMenu,
1406     NULL,
1407     sizeof(ServerMenu)/sizeof(menuitem_t),
1408     27,40,
1409     0,
1410 };
1411 
M_StartServerMenu(int choice)1412 void M_StartServerMenu(int choice)
1413 {
1414     if( M_already_playing(0) )  return;
1415 
1416     D_End_commandline();
1417 
1418     ServerMenu[0] = (gamemode==doom2_commercial)?
1419          ServerMenu_Map  // Doom2
1420        : ServerMenu_EpisodeMap;  // Ult doom, Heretic
1421 
1422     // StartSplitScreenGame already set by TwoPlayer menu
1423     Push_Setup_Menu(&ServerDef);
1424     setup_net_savegame();
1425 }
1426 
1427 //===========================================================================
1428 //                            MULTI PLAYER MENU
1429 //===========================================================================
1430 static void M_SetupMultiPlayer1 (int choice);
1431 static void M_SetupMultiPlayer2 (int choice);
1432 static void M_TwoPlayerMenu(int choice);
1433 
1434 // index for MultiPlayerMenu
1435 enum {
1436     MPM_player1 = 1, // referenced in M_Player2_MenuEnable
1437     MPM_player2 = 2, // referenced in M_Player2_MenuEnable
1438 } multiplayer_e;
1439 
1440 // DoomLegacy graphics from legacy.wad: M_STSERV, M_CONNEC, M_2PLAYR, M_SETUPA, M_SETUPB
1441 menuitem_t MultiPlayerMenu[] =
1442 {
1443     // BIG font menu. BIG font does not work, lump is missing.
1444     // Cannot put all three options here.
1445     {IT_CALL | IT_PATCH,"M_2PLAYR","TWO PLAYER GAME",M_TwoPlayerMenu ,'n'},
1446     {IT_CALL | IT_PATCH,"M_SETUPA","SETUP PLAYER 1" ,M_SetupMultiPlayer1 ,'s'},
1447     {IT_CALL | IT_PATCH,"M_SETUPB","SETUP PLAYER 2" ,M_SetupMultiPlayer2 ,'t'},
1448     {IT_SUBMENU | IT_PATCH,"M_OPTION","OPTIONS"     ,&MPOptionDef ,'o'},
1449     {IT_CALL | IT_PATCH,"M_CONNEC","CONNECT SERVER" ,M_ConnectMenu ,'c'},
1450     {IT_CALL | IT_PATCH,"M_STSERV","CREATE SERVER"  ,M_StartServerMenu ,'a'},
1451     {IT_CALL | IT_PATCH,"M_ENDGAM","END GAME"       ,M_EndGame ,'e'}
1452 };
1453 
1454 menu_t  MultiPlayerDef =
1455 {
1456     "M_MULTI", // in legacy.wad
1457     "Multiplayer",
1458     MultiPlayerMenu,
1459     M_DrawGenericMenu,
1460     NULL,
1461     sizeof(MultiPlayerMenu)/sizeof(menuitem_t),
1462     85,40,
1463     0
1464 };
1465 
1466 
1467 //===========================================================================
1468 // Two Player menu
1469 //===========================================================================
1470 
1471 // DoomLegacy graphics from legacy.wad: M_SETUPA, M_SETUPB, M_STSERV, M_MULTI
1472 menuitem_t TwoPlayerMenu[] =
1473 {
1474     {IT_CALL | IT_PATCH,"M_SETUPA","SETUP PLAYER 1",M_SetupMultiPlayer1 ,'s'},
1475     {IT_CALL | IT_PATCH,"M_SETUPB","SETUP PLAYER 2",M_SetupMultiPlayer2 ,'t'},
1476     {IT_CALL | IT_PATCH,"M_OPTION","OPTIONS"       ,M_NetOption ,'o'},
1477     {IT_CALL | IT_PATCH,"M_STSERV","START GAME"    ,M_StartServerMenu , 0},
1478     {IT_SUBMENU | IT_PATCH,"M_MULTI" ,"MULTIPLAYER",&MultiPlayerDef  ,'m'},
1479 };
1480 
1481 menu_t  TwoPlayerDef =
1482 {
1483     "M_2PLAYR",  // from legacy.wad
1484     "Two Player",
1485     TwoPlayerMenu,
1486     M_DrawGenericMenu,
1487     NULL,
1488     sizeof(TwoPlayerMenu)/sizeof(menuitem_t),
1489     85,40,
1490     0
1491 };
1492 
1493 
M_TwoPlayerMenu(int choice)1494 void M_TwoPlayerMenu(int choice)
1495 {
1496     StartSplitScreenGame = true;
1497     M_Player2_MenuEnable( 1 );
1498     Push_Setup_Menu(&TwoPlayerDef);
1499 }
1500 
1501 
1502 //===========================================================================
1503 // Second mouse config for the splitscreen player
1504 //===========================================================================
1505 
1506 menuitem_t  SecondMouseCfgMenu[] =
1507 {
1508     {IT_STRING | IT_CVAR,0,"P2 Use Mouse2",     &cv_usemouse[1]      ,0},
1509     {IT_STRING | IT_CVAR,0,"P2 Always MouseLook", &cv_alwaysfreelook[1],0},
1510     {IT_STRING | IT_CVAR,0,"P2 Mouse Move",     &cv_mouse_move[1]    ,0},
1511 #ifdef MOUSE2
1512     {IT_STRING | IT_CVAR,0,"Mouse2 Serial Port", &cv_mouse2port      ,0},
1513 #if defined( SMIF_SDL ) || defined( SMIF_WIN32 )
1514     {IT_STRING | IT_CVAR,0,"Mouse2 type",       &cv_mouse2type       ,0},
1515 #endif
1516     {IT_STRING | IT_CVAR,0,"Mouse2 Invert",     &cv_mouse2_invert    ,0},
1517     {IT_STRING | IT_CVAR
1518      | IT_CV_SLIDER     ,0,"Mouse2 x Speed",    &cv_mouse2_sens_x    ,0},
1519     {IT_STRING | IT_CVAR
1520      | IT_CV_SLIDER     ,0,"Mouse2 y Speed",    &cv_mouse2_sens_y    ,0},
1521 #else
1522     {IT_STRING|IT_WHITESTRING|IT_NOTHING, 0, "NO MOUSE2",   0, 0},
1523 #endif
1524 };
1525 
1526 menu_t  SecondMouseCfgdef =
1527 {
1528     "M_OPTTTL",
1529     "Options",
1530     SecondMouseCfgMenu,
1531     M_DrawGenericMenu,
1532     NULL,
1533     sizeof(SecondMouseCfgMenu)/sizeof(menuitem_t),
1534     27,40,
1535     0
1536 };
1537 
1538 //===========================================================================
1539 // Options for the main player and the splitscreen player
1540 //===========================================================================
1541 
1542 // [0]=main player, [1]=splitscreen player
1543 static byte menu_pind = 0;
1544 static byte menu_multiplayer = 0;
1545 
1546 static void M_SetupMultiPlayer_pind( byte pind );
1547 static void M_SetupMultiPlayer1(int choice);
1548 static void M_SetupMultiPlayer2(int choice);
1549 static void M_Setup_P1_Controls(int choice);
1550 static void M_Setup_P2_Controls(int choice);
1551 
1552 static menufunc_t M_SetupMultiPlayer[2] = { M_SetupMultiPlayer1, M_SetupMultiPlayer2 };
1553 static menufunc_t M_Setup_P_Controls[2] = { M_Setup_P1_Controls, M_Setup_P2_Controls };
1554 static const char *  player_pind_str[2] = { "Player1", "Player2" };
1555 static const char *  player_setup_str[2] = { "Player1 setup >>", "Player2 setup >>" };
1556 static const char *  player_controls_str[2] = { "Player1 controls >>", "Player2 controls >>" };
1557 
1558 // Customized by M_SetupMultiPlayer1 and M_SetupMultiPlayer2
1559 menuitem_t  PlayerOptionsMenu[] =
1560 {
1561 //    {IT_STRING | IT_CVAR,"Messages:"       ,&cv_showmessages2    ,0},
1562     {IT_STRING | IT_CVAR,0, "Always Run",  &cv_autorun[0]        ,0},
1563     {IT_STRING | IT_CVAR,0, "Crosshair",   &cv_crosshair[0]      ,0},
1564     {IT_STRING | IT_CVAR,0, "Autoaim",     &cv_autoaim[0]        ,0},
1565     {IT_STRING | IT_CVAR,0, "Use Mouse" ,  &cv_usemouse[0]       ,0},
1566     {IT_STRING | IT_CVAR,0, "Mouse Move",  &cv_mouse_move[0]     ,0},
1567     {IT_STRING | IT_CVAR,0, "Always MouseLook", &cv_alwaysfreelook[0], 0},
1568     {IT_STRING | IT_CVAR | IT_CV_STRING,0, "WeaponPref", &cv_weaponpref[1] ,0},
1569 //    {IT_STRING | IT_CVAR,"Control per key" ,&cv_controlperkey2   ,0},
1570     {IT_CALL | IT_WHITESTRING, 0,"Player1 setup >>", M_SetupMultiPlayer1, 's'},
1571     {IT_CALL | IT_WHITESTRING, 0,"Player1 controls >>", M_Setup_P1_Controls, 'c'},
1572 };
1573 
1574 // index by above menu lines
1575 enum {
1576     playeroption_alwaysrun,
1577     playeroption_crosshair,
1578     playeroption_autoaim,
1579     playeroption_usemouse,
1580     playeroption_mousemove,
1581     playeroption_mouselook,
1582     playeroption_weaponpref,
1583     playeroption_setupplayer,
1584     playeroption_setupcontrol,
1585     playeroption_end
1586 };
1587 
1588 menu_t  PlayerOptionsDef =
1589 {
1590     NULL,
1591     "Player1",
1592     PlayerOptionsMenu,
1593     M_DrawGenericMenu,
1594     NULL,
1595     sizeof(PlayerOptionsMenu)/sizeof(menuitem_t),
1596     27,40,
1597     0,
1598 };
1599 
1600 
M_PlayerDirectorChoice(int choice)1601 static void M_PlayerDirectorChoice(int choice)
1602 {
1603     // pind = choice
1604     M_SetupMultiPlayer_pind( choice );  // pind = 0,1
1605     Pop_Menu();
1606     Push_Setup_Menu( &PlayerOptionsDef );
1607 }
1608 
1609 menuitem_t  PlayerDirectorMenu[] =
1610 {
1611     {IT_CALL | IT_WHITESTRING, 0,"Player1 config >>", M_PlayerDirectorChoice, '1'},
1612     {IT_CALL | IT_WHITESTRING, 0,"Player2 config >>", M_PlayerDirectorChoice, '2'}
1613 };
1614 
1615 menu_t  PlayerDirectorDef =
1616 {
1617     "M_OPTTTL",
1618     "Player",
1619     PlayerDirectorMenu,
1620     M_DrawGenericMenu,
1621     NULL,
1622     sizeof(PlayerDirectorMenu)/sizeof(menuitem_t),
1623     27,40,
1624     0,
1625 };
1626 
M_PlayerDirector(int choice)1627 static void M_PlayerDirector(int choice)
1628 {
1629     // Select the menu
1630     if( ! menu_multiplayer )
1631     {
1632         M_SetupMultiPlayer_pind( 0 );
1633         Push_Setup_Menu( &PlayerOptionsDef );
1634         return;
1635     }
1636     Push_Setup_Menu( &PlayerDirectorDef );
1637 }
1638 
1639 
1640 //===========================================================================
1641 //MULTI PLAYER SETUP MENU
1642 //===========================================================================
1643 static void M_DrawSetupMultiPlayerMenu(void);
1644 static void M_MultiPlayer_Responder(int choice);
1645 static boolean M_QuitMultiPlayerMenu(void);
1646 
1647 #define PLBOXW    8
1648 #define PLBOXH    9
1649 #define PLBOXX    90
1650 #define PLBOXY    8
1651 #define PLSKINNAMEY 96
1652 
1653 // Customized by M_SetupMultiPlayer1 and M_SetupMultiPlayer2
1654 menuitem_t SetupMultiPlayerMenu[] =
1655 {
1656     {IT_KEYHANDLER | IT_STRING          ,0,"Your name" ,M_MultiPlayer_Responder,0},
1657     {IT_CVAR | IT_STRING | IT_CV_NOPRINT | IT_YOFFSET, 0,"Your color",&cv_playercolor[0], 16},
1658     {IT_KEYHANDLER | IT_STRING | IT_YOFFSET, 0,"Your skin" ,M_MultiPlayer_Responder, PLSKINNAMEY},
1659     {IT_SUBMENU | IT_WHITESTRING | IT_YOFFSET, 0,"Player config >>", &PlayerOptionsDef, PLSKINNAMEY+14},
1660  // Player2 only
1661     {IT_CALL | IT_WHITESTRING | IT_YOFFSET, 0,"Player2 Controls >>", M_Setup_P2_Controls, PLSKINNAMEY+24},
1662     {IT_SUBMENU | IT_WHITESTRING | IT_YOFFSET, 0,"Second Mouse config >>", &SecondMouseCfgdef, PLSKINNAMEY+34}
1663 };
1664 
1665 // index to above menu lines
1666 enum {
1667     setupmultiplayer_name = 0,
1668     setupmultiplayer_color,
1669     setupmultiplayer_skin,
1670     setupmultiplayer_options,
1671     setupmultiplayer_controls,
1672     setupmultiplayer_mouse2,
1673     setupmulti_end
1674 };
1675 
1676 menu_t  SetupMultiPlayerDef =
1677 {
1678     "M_MULTI", // in legacy.wad
1679     "Multiplayer",
1680     SetupMultiPlayerMenu,
1681     M_DrawSetupMultiPlayerMenu,
1682     M_QuitMultiPlayerMenu,
1683     sizeof(SetupMultiPlayerMenu)/sizeof(menuitem_t),
1684     27,40,
1685     0,
1686 };
1687 
1688 
1689 
1690 static  int       multi_tics;
1691 static  state_t*  multi_state;
1692 
1693 // this is set before entering the MultiPlayer setup menu,
1694 // for either player 1 or 2
1695 static  char       setupm_name[MAXPLAYERNAME+1];
1696 static  player_t*  setupm_player;
1697 static  consvar_t* setupm_cvskin;
1698 static  consvar_t* setupm_cvcolor;
1699 static  consvar_t* setupm_cvname;
1700 static  byte       setupm_skinindex;
1701 
1702 static
M_SetupMultiPlayer_pind(byte pind)1703 void M_SetupMultiPlayer_pind( byte pind )
1704 {
1705     menu_pind = pind;
1706 
1707     // SetupMultiPlayerMenu
1708     setupm_cvname = &cv_playername[pind];
1709     strcpy (setupm_name, cv_playername[pind].string);
1710     setupm_cvskin = &cv_skin[pind];
1711     setupm_skinindex = R_SkinAvailable( cv_skin[pind].string );
1712     setupm_cvcolor = &cv_playercolor[pind];
1713 
1714     SetupMultiPlayerMenu[setupmultiplayer_color].itemaction = setupm_cvcolor;
1715 
1716     // PlayerOptionsMenu
1717     PlayerOptionsDef.menutitle = player_pind_str[pind];
1718     PlayerOptionsMenu[playeroption_usemouse].itemaction = &cv_usemouse[pind];
1719     PlayerOptionsMenu[playeroption_mouselook].itemaction = &cv_alwaysfreelook[pind];
1720     PlayerOptionsMenu[playeroption_mousemove].itemaction = &cv_mouse_move[pind];
1721     PlayerOptionsMenu[playeroption_alwaysrun].itemaction = &cv_autorun[pind];
1722     PlayerOptionsMenu[playeroption_crosshair].itemaction = &cv_crosshair[pind];
1723     PlayerOptionsMenu[playeroption_autoaim].itemaction = &cv_autoaim[pind];
1724     PlayerOptionsMenu[playeroption_weaponpref].itemaction = &cv_weaponpref[pind];
1725     PlayerOptionsMenu[playeroption_setupplayer].itemaction = M_SetupMultiPlayer[pind];
1726     PlayerOptionsMenu[playeroption_setupcontrol].itemaction = M_Setup_P_Controls[pind];
1727     PlayerOptionsMenu[playeroption_setupplayer].text = (char*) player_setup_str[pind];
1728     PlayerOptionsMenu[playeroption_setupcontrol].text = (char*) player_controls_str[pind];
1729 
1730     // skin display
1731     multi_state = &states[mobjinfo[MT_PLAYER].seestate];
1732     multi_tics = multi_state->tics;
1733 }
1734 
1735 static
M_SetupMultiPlayer1(int choice)1736 void M_SetupMultiPlayer1 (int choice)
1737 {
1738     // set for player 1
1739     setupm_player = consoleplayer_ptr;
1740     M_SetupMultiPlayer_pind(0);
1741 
1742     SetupMultiPlayerMenu[setupmultiplayer_options].text = "Player1 config >>";
1743     SetupMultiPlayerDef.numitems = setupmultiplayer_options +1;      //remove player2 setup controls and mouse2
1744 
1745     Push_Setup_Menu (&SetupMultiPlayerDef);
1746 }
1747 
1748 // start the multiplayer setup menu, for secondary player (splitscreen mode)
1749 static
M_SetupMultiPlayer2(int choice)1750 void M_SetupMultiPlayer2 (int choice)
1751 {
1752     // set for splitscreen player 2
1753     setupm_player = displayplayer2_ptr;  // player 2
1754     M_SetupMultiPlayer_pind(1);
1755 
1756     SetupMultiPlayerMenu[setupmultiplayer_options].text = "Player2 config >>";
1757     SetupMultiPlayerDef.numitems = setupmulti_end;          //activate the setup controls for player 2
1758 
1759     Push_Setup_Menu (&SetupMultiPlayerDef);
1760 }
1761 
1762 
1763 // Called at cv_splitscreen changes (SplitScreen_OnChange)
M_Player2_MenuEnable(boolean player2_enable)1764 void M_Player2_MenuEnable( boolean player2_enable )
1765 {
1766 // activate setup for player 2
1767     menu_multiplayer = player2_enable;
1768     if ( player2_enable )
1769     {
1770         MultiPlayerMenu[MPM_player2].status = IT_CALL | IT_PATCH;
1771     }
1772     else
1773     {
1774         MultiPlayerMenu[MPM_player2].status = IT_DISABLED;
1775         if( MultiPlayerDef.lastOn == MPM_player2)
1776             MultiPlayerDef.lastOn = MPM_player1;
1777     }
1778 }
1779 
1780 
1781 //
1782 //  Draw the multi player setup menu, had some fun with player anim
1783 //
1784 static
M_DrawSetupMultiPlayerMenu(void)1785 void M_DrawSetupMultiPlayerMenu(void)
1786 {
1787     spritedef_t   * sprdef;
1788     sprite_frot_t * sprfrot;
1789     patch_t       * patch;
1790     byte          * colormap;
1791     int             mx,my;
1792     int             st;
1793 
1794     // Draw to screen0, scaled
1795     mx = SetupMultiPlayerDef.x;
1796     my = SetupMultiPlayerDef.y;
1797 
1798     // use generic drawer for cursor, items and title
1799     M_DrawGenericMenu();
1800 
1801     // draw name string
1802     M_DrawTextBox(mx+PLBOXX, my-8, MAXPLAYERNAME, 1);
1803     V_DrawString (mx+PLBOXX+8 ,my, 0, setupm_name);
1804 
1805     // draw skin string
1806     V_DrawString (mx+PLBOXX, my+PLSKINNAMEY, 0, setupm_cvskin->string);
1807 
1808     // draw text cursor for name
1809     if (itemOn==0
1810         && skullAnimCounter<4 )   //blink cursor
1811         V_DrawCharacter(mx+98+V_StringWidth(setupm_name), my, '_' | 0x80);  // white
1812 
1813     // anim the player in the box
1814     if (--multi_tics<=0)
1815     {
1816         st = multi_state->nextstate;
1817         if (st!=S_NULL)
1818             multi_state = &states[st];
1819         multi_tics = multi_state->tics;
1820         if (multi_tics==-1)
1821             multi_tics=15;
1822     }
1823 
1824     // skin 0 is default player sprite
1825     sprdef    = &skins[R_SkinAvailable(setupm_cvskin->string)]->spritedef;
1826     sprfrot = get_framerotation( sprdef, multi_state->frame & FF_FRAMEMASK, 0 );
1827 
1828     colormap = (setupm_cvcolor->value) ?
1829          SKIN_TO_SKINMAP( setupm_cvcolor->value )
1830        : reg_colormaps;  // default green skin
1831 
1832     // draw box around guy
1833     M_DrawTextBox(mx+PLBOXX,my+PLBOXY, PLBOXW, PLBOXH);
1834 
1835     // draw player sprite
1836     // temp usage of sprite lump, until end of function
1837     patch = W_CachePatchNum (sprfrot->pat_lumpnum, PU_CACHE_DEFAULT);  // endian fix
1838     if( itemOn>0 )  // Edit skin or color
1839     {
1840       // Some skins are too large for the screen, cause segfault.
1841       V_DrawMappedPatch_Box (mx+PLBOXX+8+(PLBOXW*8/2),my+PLBOXY+8+(PLBOXH*8)-8, patch, colormap,
1842                            1, 0, 300, my+PLBOXY+8+PLBOXH*8 );
1843     }
1844     else
1845     {
1846       // Some skins are too large for the box
1847       V_DrawMappedPatch_Box (mx+PLBOXX+8+(PLBOXW*8/2),my+PLBOXY+8+(PLBOXH*8)-8, patch, colormap,
1848                            mx+PLBOXX+8, my+PLBOXY+8, PLBOXW*8, PLBOXH*8 );
1849     }
1850 }
1851 
1852 
1853 //
1854 // Handle Setup MultiPlayer Menu
1855 //
1856 static
M_MultiPlayer_Responder(int key)1857 void M_MultiPlayer_Responder (int key)
1858 {
1859     int      l;
1860 
1861     switch (key)
1862     {
1863       case KEY_DOWNARROW:
1864         S_StartSound(menu_sfx_updown);
1865         if (itemOn+1 >= SetupMultiPlayerDef.numitems)
1866             itemOn = 0;
1867         else itemOn++;
1868         break;
1869 
1870       case KEY_UPARROW:
1871         S_StartSound(menu_sfx_updown);
1872         if (itemOn == 0)
1873             itemOn = SetupMultiPlayerDef.numitems-1;
1874         else itemOn--;
1875         break;
1876 
1877       case KEY_LEFTARROW:
1878         if (itemOn==2)       //player skin
1879         {
1880             // unsigned skin index
1881             if(setupm_skinindex == 0)
1882                 setupm_skinindex = numskins;
1883 
1884             setupm_skinindex--;
1885             goto change_skin;
1886         }
1887         break;
1888 
1889       case KEY_RIGHTARROW:
1890         if (itemOn==2)       //player skin
1891         {
1892             setupm_skinindex++;
1893             goto change_skin;
1894         }
1895         break;
1896 
1897       case KEY_ENTER:
1898         S_StartSound(menu_sfx_enter);
1899         goto exitmenu;
1900 
1901       case KEY_ESCAPE:
1902         S_StartSound(menu_sfx_esc);
1903         goto exitmenu;
1904 
1905       case KEY_BACKSPACE:
1906         l = strlen(setupm_name);
1907         if( l!=0 && itemOn==0 )
1908         {
1909             S_StartSound(menu_sfx_val);
1910             setupm_name[l-1]=0;
1911         }
1912         break;
1913 
1914       default:
1915         if (!is_printable(input_char) || itemOn != 0)
1916           break;
1917         l = strlen(setupm_name);
1918         if (l<MAXPLAYERNAME-1)
1919         {
1920             S_StartSound(menu_sfx_val);
1921             setupm_name[l] = input_char;
1922             setupm_name[l+1] = 0;
1923         }
1924         break;
1925     }
1926     return;
1927 
1928 change_skin:
1929     S_StartSound(menu_sfx_val);
1930 
1931     // check skin
1932     if( setupm_skinindex > numskins-1 )
1933         setupm_skinindex = 0;
1934 
1935     if( skins[setupm_skinindex] == NULL )  return;
1936 
1937     // check skin change
1938     // If not updated here then another chance after server start
1939     // The skin select is the name, not the index.
1940     CV_Set( setupm_cvskin, skins[setupm_skinindex]->name );  // does net update
1941     if( setupm_player )
1942     {
1943         setupm_player->skin = setupm_skinindex;
1944     }
1945     return;
1946 
1947 exitmenu:
1948     // Exit to previous menu.
1949     Pop_Menu();
1950     return;
1951 }
1952 
1953 static
M_QuitMultiPlayerMenu(void)1954 boolean M_QuitMultiPlayerMenu(void)
1955 {
1956     int      l;
1957     // send name if changed
1958     if (strcmp(setupm_name, setupm_cvname->string))
1959     {
1960         // remove trailing whitespaces
1961         for (l= strlen(setupm_name)-1;
1962              l>=0 && setupm_name[l]==' '; l--)
1963             setupm_name[l]=0;
1964 
1965         CV_Set( setupm_cvname, setupm_name );  // does net update
1966     }
1967     return true;
1968 }
1969 
1970 
1971 //===========================================================================
1972 //                              EPISODE SELECT
1973 //===========================================================================
1974 
1975 static void M_Episode(int choice);
1976 
1977 menuitem_t EpisodeMenu[]=
1978 {
1979     {IT_CALL | IT_PATCH,"M_EPI1","Knee-Deep in the Dead", M_Episode,'k'},
1980     {IT_CALL | IT_PATCH,"M_EPI2","The Shores of Hell"   , M_Episode,'t'},
1981     {IT_CALL | IT_PATCH,"M_EPI3","Inferno"              , M_Episode,'i'},
1982     {IT_CALL | IT_PATCH,"M_EPI4","Thy Flesh consumed"   , M_Episode,'t'},
1983     {IT_CALL | IT_PATCH,"M_EPI5","Episode 5"            , M_Episode,'t'},
1984 };
1985 
1986 menu_t  EpiDef =
1987 {
1988     "M_EPISOD",
1989     "Which Episode?",
1990     EpisodeMenu,        // menuitem_t ->
1991     M_DrawGenericMenu,  // drawing routine ->
1992     NULL,
1993     sizeof(EpisodeMenu)/sizeof(menuitem_t),
1994     48,63,              // x,y
1995     0                   // lastOn, flags
1996 };
1997 
1998 //
1999 //      M_Episode
2000 //
2001 int     epi;
2002 
2003 static
M_Episode(int choice)2004 void M_Episode(int choice)
2005 {
2006     if ( (gamemode == doom_shareware)
2007          && choice)
2008     {
2009         Push_Setup_Menu(&ReadDef1);
2010         M_SimpleMessage(SWSTRING);
2011         return;
2012     }
2013 
2014     // Yet another hack...
2015     if ( (gamemode == doom_registered)
2016          && (choice > 2))
2017     {
2018         GenPrintf( EMSG_warn, "M_Episode: 4th episode requires UltimateDOOM\n");
2019         choice = 0;
2020     }
2021 
2022     epi = choice;
2023     Push_Setup_Menu(&NewDef);
2024 }
2025 
2026 
2027 //===========================================================================
2028 //                           NEW GAME FOR SINGLE PLAYER
2029 //===========================================================================
2030 static void M_DrawNewGame(void);
2031 
2032 static void M_ChooseSkill(int choice);
2033 
2034 enum
2035 {
2036     NG_violence = 3,
2037     NG_nightmare = 4,
2038 } newgame_e;
2039 
2040 menuitem_t NewGameMenu[]=
2041 {
2042     {IT_CALL | IT_PATCH,"M_JKILL","I'm too young to die.",M_ChooseSkill, 'i'},
2043     {IT_CALL | IT_PATCH,"M_ROUGH","Hey, not too rough."  ,M_ChooseSkill, 'h'},
2044     {IT_CALL | IT_PATCH,"M_HURT" ,"Hurt me plenty."      ,M_ChooseSkill, 'h'},
2045     {IT_CALL | IT_PATCH,"M_ULTRA","Ultra-Violence"       ,M_ChooseSkill, 'u'},
2046     {IT_CALL | IT_PATCH,"M_NMARE","Nightmare!"           ,M_ChooseSkill, 'n'}
2047 };
2048 
2049 menu_t  NewDef =
2050 {
2051     "M_NEWG",
2052     "New Game",
2053     NewGameMenu,        // menuitem_t ->
2054     M_DrawNewGame,      // drawing routine ->
2055     NULL,
2056     sizeof(EpisodeMenu)/sizeof(menuitem_t),
2057     48,63,              // x,y
2058     NG_violence            // lastOn
2059 };
2060 
2061 static
M_DrawNewGame(void)2062 void M_DrawNewGame(void)
2063 {
2064     // Draw to screen0, scaled
2065     //faB: testing with glide
2066     patch_t* p = W_CachePatchName("M_SKILL",PU_CACHE);  // endian fix
2067     V_DrawScaledPatch ((BASEVIDWIDTH-p->width)/2,38, p);
2068 
2069     //    V_DrawScaledPatch_Name (54,38, "M_SKILL" );
2070     M_DrawGenericMenu();
2071 }
2072 
2073 static
M_SingleNewGame(int choice)2074 void M_SingleNewGame(int choice)
2075 {
2076     // to get out of two player game, and can then backout to multiplayer
2077     StartSplitScreenGame = false;
2078     M_Player2_MenuEnable( 0 );
2079 
2080     if( M_already_playing(1) )  return;
2081 
2082     D_End_commandline();
2083 
2084     if ( gamemode == doom2_commercial
2085          || (gamemode == chexquest1 && !modifiedgame) //DarkWolf95: Support for Chex Quest
2086          )
2087         Push_Setup_Menu(&NewDef);
2088     else
2089         Push_Setup_Menu(&EpiDef);
2090 }
2091 
2092 static void M_VerifyNightmare(int ch);
2093 
2094 static
M_ChooseSkill(int choice)2095 void M_ChooseSkill(int choice)
2096 {
2097     if (choice == NG_nightmare)
2098     {
2099         M_StartMessage(NIGHTMARE, M_VerifyNightmare, MM_YESNO);
2100         return;
2101     }
2102 
2103     G_DeferedInitNew(choice, G_BuildMapName(epi+1,1), StartSplitScreenGame);
2104     M_Clear_Menus (true);
2105 }
2106 
2107 static
M_VerifyNightmare(int ch)2108 void M_VerifyNightmare(int ch)
2109 {
2110     if (ch != 'y')
2111         return;
2112 
2113     G_DeferedInitNew(NG_nightmare, G_BuildMapName(epi+1,1), StartSplitScreenGame);
2114     M_Clear_Menus (true);
2115 }
2116 
2117 //===========================================================================
2118 //                             OPTIONS MENU
2119 //===========================================================================
2120 //
2121 // M_Options
2122 //
2123 
2124 menuitem_t OptionsMenu[]=
2125 {
2126     {IT_STRING | IT_CVAR,0,"Messages:"       ,&cv_showmessages    ,0},
2127     {IT_STRING | IT_CVAR,0,"Always Run"      ,&cv_autorun[0]      ,0},
2128     {IT_STRING | IT_CVAR,0,"Crosshair"       ,&cv_crosshair[0]    ,0},
2129 //    {IT_STRING | IT_CVAR,0,"Crosshair scale" ,&cv_crosshairscale  ,0},
2130 #if 1
2131     {IT_CALL    | IT_WHITESTRING,0,"Player >>"  ,M_PlayerDirector   ,0},
2132 #else
2133     {IT_STRING | IT_CVAR,0,"Autoaim"         ,&cv_autoaim[0]      ,0},
2134 #endif
2135 
2136     {IT_SUBMENU | IT_WHITESTRING | IT_YOFFSET, 0,"Effects Options >>",&EffectsOption1Def ,50},
2137     {IT_CALL    | IT_WHITESTRING,0,"Game Options >>"  ,M_GameOption       ,0},
2138     {IT_SUBMENU | IT_WHITESTRING,0,"Connect Options >>",&ConnectOptionDef ,0},
2139     {IT_CALL    | IT_WHITESTRING,0,"Network Options >>",M_NetOption     ,0},
2140     {IT_SUBMENU | IT_WHITESTRING,0,"Server Options >>",&ServerOptionsDef  ,0},
2141     {IT_SUBMENU | IT_WHITESTRING,0,"Menu Options >>"  ,&MenuOptionsDef    ,0},
2142     {IT_SUBMENU | IT_WHITESTRING,0,"Sound Volume >>"  ,&SoundDef          ,0},
2143     {IT_SUBMENU | IT_WHITESTRING,0,"Video Options >>" ,&VideoOptionsDef   ,0},
2144     {IT_SUBMENU | IT_WHITESTRING,0,"Setup Controls >>",&MControlDef       ,0},
2145 };
2146 
2147 menu_t  OptionsDef =
2148 {
2149     "M_OPTTTL",
2150     "Options",
2151     OptionsMenu,
2152     M_DrawGenericMenu,
2153     NULL,
2154     sizeof(OptionsMenu)/sizeof(menuitem_t),
2155     60,40,
2156     0
2157 };
2158 
2159 //
2160 //  A smaller 'Thermo', with range given as percents (0-100)
2161 //
2162 static
M_DrawSlider(int x,int y,int range)2163 void M_DrawSlider (int x, int y, int range)
2164 {
2165     int i;
2166 
2167     // Draw to screen0, scaled
2168     if (range < 0)
2169         range = 0;
2170     if (range > 100)
2171         range = 100;
2172 
2173     V_DrawScaledPatch_Name(x-8, y, "M_SLIDEL"); // in legacy.wad
2174 
2175     for (i=0 ; i<SLIDER_RANGE ; i++)
2176       V_DrawScaledPatch_Name(x+i*8, y, "M_SLIDEM"); // in legacy.wad
2177 
2178     V_DrawScaledPatch_Name(x+SLIDER_RANGE*8, y, "M_SLIDER"); // in legacy.wad
2179 
2180     // draw the slider cursor
2181     V_DrawMappedPatch_Name(x + ((SLIDER_RANGE-1)*8*range)/100, y,
2182                            "M_SLIDEC", whitemap); // in legacy.wad
2183 }
2184 
2185 //===========================================================================
2186 //                        Menu OPTIONS MENU
2187 //===========================================================================
2188 
2189 menuitem_t MenuOptionsMenu[]=
2190 {
2191     {IT_STRING | IT_CVAR,0, "Menu Sounds"     , &cv_menusound     , 0},
2192     {IT_STRING | IT_CVAR,0, "Screens Link"    , &cv_screenslink   , 0},
2193 };
2194 
2195 menu_t  MenuOptionsDef =
2196 {
2197     "M_OPTTTL",
2198     "Effects",
2199     MenuOptionsMenu,
2200     M_DrawGenericMenu,
2201     NULL,
2202     sizeof(MenuOptionsMenu)/sizeof(menuitem_t),
2203     60,40,
2204     0
2205 };
2206 
2207 //===========================================================================
2208 //                        Effects OPTIONS MENU
2209 //===========================================================================
2210 
2211 menuitem_t EffectsOption1Menu[]=
2212 {
2213     {IT_SUBMENU | IT_WHITESTRING, 0, "Light Options >>"  , &LightingDef  , 'l'},
2214     {IT_STRING | IT_CVAR,0, "Translucency"    , &cv_translucency  , 0},
2215     {IT_STRING | IT_CVAR,0, "Fuzzy Shadow"    , &cv_fuzzymode     , 0},
2216     {IT_STRING | IT_CVAR,0, "Splats"          , &cv_splats        , 0},
2217     {IT_STRING | IT_CVAR,0, "Max splats"      , &cv_maxsplats     , 0},
2218     {IT_STRING | IT_CVAR,0, "BloodTime"       , &cv_bloodtime     , 0},
2219     {IT_STRING | IT_CVAR,0, "Sprites limit"   , &cv_spritelim     , 0},
2220     {IT_STRING | IT_CVAR,0, "Pickup Flash"    , &cv_pickupflash   , 0},
2221     {IT_STRING | IT_CVAR,0, "Sky"             , &cv_sky_gen       , 0},
2222     {IT_STRING | IT_CVAR,0, "Water Effect"    , &cv_water_effect  , 0},
2223     {IT_STRING | IT_CVAR,0, "Fog Effect"      , &cv_fog_effect    , 0},
2224     {IT_SUBMENU | IT_WHITESTRING | IT_YOFFSET, 0,"Next"  , &EffectsOption2Def,130},
2225 };
2226 
2227 menuitem_t EffectsOption2Menu[]=
2228 {
2229     {IT_STRING | IT_CVAR,0, "Invul skymap"    , &cv_invul_skymap  , 0},
2230     {IT_STRING | IT_CVAR,0, "Boom Colormap"   , &cv_boom_colormap , 0},
2231     {IT_STRING | IT_CVAR,0, "Sound oof 2s"    , &cv_oof_2s , 0},
2232 };
2233 
2234 
2235 menu_t  EffectsOption1Def =
2236 {
2237     "M_OPTTTL",
2238     "Effects",
2239     EffectsOption1Menu,
2240     M_DrawGenericMenu,
2241     NULL,
2242     sizeof(EffectsOption1Menu)/sizeof(menuitem_t),
2243     60,40,
2244     0
2245 };
2246 
2247 menu_t  EffectsOption2Def =
2248 {
2249     "M_OPTTTL",
2250     "Effects",
2251     EffectsOption2Menu,
2252     M_DrawGenericMenu,
2253     NULL,
2254     sizeof(EffectsOption2Menu)/sizeof(menuitem_t),
2255     60,40,
2256     0
2257 };
2258 
2259 //===========================================================================
2260 //                        Video OPTIONS MENU
2261 //===========================================================================
2262 
2263 // Which line to modify for some menu options.
2264 enum
2265 {
2266 #ifdef __DJGPP__
2267     VO_gamma = 3,
2268 #else
2269     VO_gamma = 4,  // Index of Gamma
2270 #endif
2271 } videooptions_e;
2272 
2273 menuitem_t VideoOptionsMenu[]=
2274 {
2275     {IT_STRING | IT_WHITESTRING | IT_SUBMENU,0, "Drawing Options >>"   , &DrawmodeDef, 0},
2276     {IT_STRING | IT_WHITESTRING | IT_SUBMENU,0, "Video Modes >>"   , &VideoModeDef       , 0},
2277 #ifndef __DJGPP__
2278     {IT_STRING | IT_CVAR,0,    "Fullscreen"       , &cv_fullscreen    , 0},
2279 #endif
2280 // if these are moved then fix MenuGammaFunc_dependencies
2281     {IT_STRING | IT_CVAR,0,    "Gamma Function"   , &cv_gammafunc     , 0},
2282     {IT_STRING | IT_CVAR
2283      | IT_CV_SLIDER     ,0,    "Gamma"            , &cv_usegamma      , 0},
2284     {IT_STRING | IT_CVAR
2285      | IT_CV_SLIDER     ,0,    "Black level"      , &cv_black         , 0},
2286     {IT_STRING | IT_CVAR
2287      | IT_CV_SLIDER     ,0,    "Brightness"       , &cv_bright        , 0},
2288     {IT_STRING | IT_CVAR,0,    "Wait Retrace"     , &cv_vidwait       , 0},
2289     {IT_STRING | IT_CVAR
2290      | IT_CV_SLIDER     ,0,    "Screen Size"      , &cv_viewsize      , 0},
2291     {IT_STRING | IT_CVAR,0,    "Scale Status Bar" , &cv_scalestatusbar, 0},
2292     {IT_STRING | IT_CVAR,0,    "Dark Back"        , &cv_darkback      , 0},
2293     {IT_STRING | IT_CVAR,0,    "Console font"     , &cv_con_fontsize  , 0},
2294     {IT_STRING | IT_CVAR,0,    "Message font"     , &cv_msg_fontsize  , 0},
2295     {IT_STRING | IT_CVAR,0,    "Show Ticrate"     , &cv_ticrate       , 0},
2296 #ifdef HWRENDER
2297     //17/10/99: added by Hurdler
2298     {IT_CALL | IT_WHITESTRING | IT_YOFFSET, 0, "OpenGL 3D Card Options >>", M_OpenGLOption    ,0},
2299 #endif
2300 };
2301 
2302 menu_t  VideoOptionsDef =
2303 {
2304     "M_OPTTTL",
2305     "Video Options",
2306     VideoOptionsMenu,
2307     M_DrawGenericMenu,
2308     NULL,
2309     sizeof(VideoOptionsMenu)/sizeof(menuitem_t),
2310     60,40,
2311     0
2312 };
2313 
2314 
2315 // Called by CV_gammafunc_OnChange.
MenuGammaFunc_dependencies(byte gamma_en,byte black_en,byte bright_en)2316 void MenuGammaFunc_dependencies( byte gamma_en,
2317                                  byte black_en, byte bright_en )
2318 {
2319    // Update menu highlights
2320    // Gamma
2321    VideoOptionsMenu[VO_gamma].status =
2322      ( gamma_en ) ? (IT_STRING | IT_CVAR | IT_CV_SLIDER )
2323        : (IT_WHITESTRING | IT_SPACE);
2324    // Black Level
2325    VideoOptionsMenu[VO_gamma+1].status =
2326      ( black_en ) ? (IT_STRING | IT_CVAR | IT_CV_SLIDER )
2327        : (IT_WHITESTRING | IT_SPACE);
2328    // Brightness
2329    VideoOptionsMenu[VO_gamma+2].status =
2330      ( bright_en ) ? (IT_STRING | IT_CVAR | IT_CV_SLIDER )
2331        : (IT_WHITESTRING | IT_SPACE);
2332 }
2333 
2334 //===========================================================================
2335 //                        Mouse OPTIONS MENU
2336 //===========================================================================
2337 
2338 menuitem_t MouseOptionsMenu[]=
2339 {
2340     {IT_STRING | IT_CVAR,0,"Use Mouse",        &cv_usemouse[0]  ,0},
2341     {IT_STRING | IT_CVAR,0,"Always MouseLook", &cv_alwaysfreelook[0]  ,0},
2342     {IT_STRING | IT_CVAR,0,"Mouse Move",    &cv_mouse_move[0]   ,0},
2343     {IT_STRING | IT_CVAR,0,"Invert Mouse",  &cv_mouse_invert    ,0},
2344     {IT_STRING | IT_CVAR
2345      | IT_CV_SLIDER     ,0,"Mouse x Speed", &cv_mouse_sens_x    ,0},
2346     {IT_STRING | IT_CVAR
2347      | IT_CV_SLIDER     ,0,"Mouse y Speed", &cv_mouse_sens_y    ,0},
2348     {IT_STRING | IT_CVAR,0,"Mouse Doubleclick" ,&cv_mouse_double  ,0},
2349 #ifdef SMIF_SDL
2350     {IT_STRING | IT_CVAR,0,"Mouse motion",  &cv_mouse_motion    ,0},
2351 #endif
2352     {IT_STRING | IT_CVAR,0,"Grab input", &cv_grabinput ,0},
2353 #if 0
2354 //[WDJ] disabled in 143beta_macosx
2355 //[segabor]
2356 # ifdef MACOS_DI
2357 // specific to macos directory
2358     ,{IT_CALL | IT_WHITESTRING | IT_YOFFSET, 0,"Configure Input Sprocket >>"  ,macConfigureInput     ,60}
2359 # endif
2360 #endif
2361 };
2362 
2363 menu_t  MouseOptionsDef =
2364 {
2365     "M_OPTTTL",
2366     "Mouse Options",
2367     MouseOptionsMenu,
2368     M_DrawGenericMenu,
2369     NULL,
2370     sizeof(MouseOptionsMenu)/sizeof(menuitem_t),
2371     60,40,
2372     0
2373 };
2374 
2375 //===========================================================================
2376 //                        Joystick OPTIONS MENU
2377 //===========================================================================
2378 
2379 menuitem_t JoystickOptionsMenu[]=
2380 {
2381 #ifdef JOY_BUTTONS_DOUBLE
2382     {IT_STRING | IT_CVAR, 0,"Joystick Doubleclick" ,&cv_joy_double ,0},
2383 #endif
2384 #if 1
2385     {IT_STRING | IT_CVAR, 0,"Joystick Deadzone" ,&cv_joy_deadzone ,0},
2386 #else
2387     {IT_STRING | IT_CVAR | IT_CV_SLIDER, 0,"Deadzone" ,&cv_joy_deadzone ,0},
2388 #endif
2389 };
2390 
2391 menu_t  JoystickOptionsDef =
2392 {
2393     "M_OPTTTL",
2394     "Joystick Options",
2395     JoystickOptionsMenu,
2396     M_DrawGenericMenu,
2397     NULL,
2398     sizeof(JoystickOptionsMenu)/sizeof(menuitem_t),
2399     60,40,
2400     0
2401 };
2402 
2403 //===========================================================================
2404 //                        Game OPTIONS MENU
2405 //===========================================================================
2406 
2407 menuitem_t GameOptionsMenu[]=
2408 {
2409     {IT_STRING | IT_CVAR,0,"Item Respawn"        ,&cv_itemrespawn        ,0},
2410     {IT_STRING | IT_CVAR,0,"Item Respawn time"   ,&cv_itemrespawntime    ,0},
2411     {IT_STRING | IT_CVAR,0,"Monster Respawn"     ,&cv_respawnmonsters    ,0},
2412     {IT_STRING | IT_CVAR,0,"Monster Respawn time",&cv_respawnmonsterstime,0},
2413     {IT_STRING | IT_CVAR,0,"Monster Behavior"	 ,&cv_monbehavior        ,0},
2414     {IT_STRING | IT_CVAR,0,"Fast Monsters"       ,&cv_fastmonsters       ,0},
2415     {IT_STRING | IT_CVAR,0,"Predicting Monsters" ,&cv_predictingmonsters ,0},	//added by AC for predmonsters
2416     {IT_STRING | IT_CVAR,0,"Solid corpse"        ,&cv_solidcorpse        ,0},
2417     {IT_CALL | IT_WHITESTRING | IT_YOFFSET, 0,"Adv Options >>"      ,M_AdvOption     ,120},
2418     {IT_CALL | IT_WHITESTRING | IT_YOFFSET, 0,"Bot Options >>"      ,M_BotOption     ,130},
2419 //    {IT_CALL | IT_WHITESTRING | IT_YOFFSET, 0,"Network Options >>"  ,M_NetOption     ,130}
2420     {IT_CALL | IT_WHITESTRING,0,"Network Options >>"  ,M_NetOption     ,0}
2421 };
2422 
2423 menu_t  GameOptionDef =
2424 {
2425     "M_OPTTTL",
2426     "Game Options",
2427     GameOptionsMenu,
2428     M_DrawGenericMenu,
2429     NULL,
2430     sizeof(GameOptionsMenu)/sizeof(menuitem_t),
2431     60,40,
2432     0
2433 };
2434 
2435 static
M_GameOption(int choice)2436 void M_GameOption(int choice)
2437 {
2438     if(!server)
2439     {
2440         M_SimpleMessage("You are not the server\nYou cannot change game options\n");
2441         return;
2442     }
2443     Push_Setup_Menu(&GameOptionDef);
2444 }
2445 
2446 //===========================================================================
2447 //                        Adv OPTIONS MENU
2448 //===========================================================================
2449 
2450 menuitem_t AdvOption1Menu[]=
2451 {
2452     {IT_STRING | IT_CVAR,0,"Gravity"             ,&cv_gravity            ,0},
2453     {IT_STRING | IT_CVAR,0,"Monster gravity"     ,&cv_monstergravity     ,0},  // [WDJ]
2454     {IT_STRING | IT_CVAR,0,"Monster friction"    ,&cv_monsterfriction    ,0},  // [WDJ]
2455     {IT_STRING | IT_CVAR,0,"Monster door stuck"  ,&cv_doorstuck          ,0},  // [WDJ]
2456     {IT_STRING | IT_CVAR,0,"Monster remember"    ,&cv_monster_remember   ,0},
2457     {IT_STRING | IT_CVAR,0,"Mon avoid hazard"    ,&cv_mbf_monster_avoid_hazard ,0},
2458     {IT_STRING | IT_CVAR,0,"Monster backing"     ,&cv_mbf_monster_backing ,0},
2459     {IT_STRING | IT_CVAR,0,"Monster pursuit"     ,&cv_mbf_pursuit        ,0},
2460     {IT_STRING | IT_CVAR,0,"Monster dropoff"     ,&cv_mbf_dropoff        ,0},
2461     {IT_STRING | IT_CVAR,0,"Monster staylift"    ,&cv_mbf_staylift       ,0},
2462     {IT_SUBMENU | IT_WHITESTRING | IT_YOFFSET, 0,"Next"  , &AdvOption2Def,130},
2463 };
2464 
2465 menuitem_t AdvOption2Menu[]=
2466 {
2467     {IT_STRING | IT_CVAR,0,"Distance friend"     ,&cv_mbf_distfriend     ,0},
2468     {IT_STRING | IT_CVAR,0,"Help friend"         ,&cv_mbf_help_friend    ,0},
2469 #ifdef DOGS
2470     {IT_STRING | IT_CVAR,0,"Dogs"                ,&cv_mbf_dogs           ,0},
2471     {IT_STRING | IT_CVAR,0,"Dog jumping"         ,&cv_mbf_dog_jumping    ,0},
2472 #endif
2473     {IT_STRING | IT_CVAR,0,"Monkeys"             ,&cv_mbf_monkeys        ,0},
2474     {IT_STRING | IT_CVAR,0,"Falloff"             ,&cv_mbf_falloff        ,0},
2475     {IT_STRING | IT_CVAR,0,"Voodoo mode"         ,&cv_voodoo_mode        ,0},  // [WDJ]
2476     {IT_STRING | IT_CVAR,0,"Insta-death"         ,&cv_instadeath         ,0},  // [WDJ]
2477 #ifdef DOORDELAY_CONTROL
2478     {IT_STRING | IT_CVAR,0,"Door Delay"          ,&cv_doordelay          ,0},  // [WDJ]
2479 #endif
2480     {IT_STRING | IT_CVAR,0,"Zero Tags"           ,&cv_zerotags           ,0},
2481     {IT_CALL | IT_WHITESTRING | IT_YOFFSET, 0,"Games Options >>"    ,M_GameOption    ,130},
2482 };
2483 
2484 menu_t  AdvOption1Def =
2485 {
2486     "M_OPTTTL",
2487     "Adv1 Options",
2488     AdvOption1Menu,
2489     M_DrawGenericMenu,
2490     NULL,
2491     sizeof(AdvOption1Menu)/sizeof(menuitem_t),
2492     60,40,
2493     0
2494 };
2495 
2496 menu_t  AdvOption2Def =
2497 {
2498     "M_OPTTTL",
2499     "Adv2 Options",
2500     AdvOption2Menu,
2501     M_DrawGenericMenu,
2502     NULL,
2503     sizeof(AdvOption2Menu)/sizeof(menuitem_t),
2504     60,40,
2505     0
2506 };
2507 
2508 static
M_AdvOption(int choice)2509 void M_AdvOption(int choice)
2510 {
2511     if(!server)
2512     {
2513         M_SimpleMessage("You are not the server\nYou cannot change adv options\n");
2514         return;
2515     }
2516     Push_Setup_Menu(&AdvOption1Def);
2517 }
2518 
2519 //===========================================================================
2520 //                        Bot OPTIONS MENU
2521 //===========================================================================
2522 
2523 menuitem_t BotOptionMenu[]=
2524 {
2525     {IT_STRING | IT_CVAR,0,"Bot skill"           ,&cv_bot_skill          ,0},
2526     {IT_STRING | IT_CVAR,0,"Bot speed"           ,&cv_bot_speed          ,0},
2527     {IT_STRING | IT_CVAR,0,"Bot skin"            ,&cv_bot_skin           ,0},
2528     {IT_STRING | IT_CVAR,0,"Bot respawn"         ,&cv_bot_respawn_time   ,0},
2529     {IT_STRING | IT_CVAR,0,"Bot seed"            ,&cv_bot_randseed       ,0},
2530     {IT_STRING | IT_CVAR,0,"Bot gen"             ,&cv_bot_gen            ,0},
2531 };
2532 
2533 menu_t  BotDef =
2534 {
2535     NULL,
2536     "Bot Options",
2537     BotOptionMenu,
2538     M_DrawGenericMenu,
2539     NULL,
2540     sizeof(BotOptionMenu)/sizeof(menuitem_t),
2541     60,40,
2542     0
2543 };
2544 
2545 static
M_BotOption(int choice)2546 void M_BotOption(int choice)
2547 {
2548     if(!server)
2549     {
2550         M_SimpleMessage("You are not the server\nYou cannot change Bot options\n");
2551         return;
2552     }
2553     Push_Setup_Menu(&BotDef);
2554 }
2555 
2556 //===========================================================================
2557 //                        Network OPTIONS MENU
2558 //===========================================================================
2559 
2560 menuitem_t NetOptionsMenu[]=
2561 {
2562     {IT_STRING | IT_CVAR,0,"Allow Jump"      ,&cv_allowjump       ,0},
2563     //SoM: 3/28/2000
2564     {IT_STRING | IT_CVAR,0,"Allow Rocket Jump",&cv_allowrocketjump,0},
2565     {IT_STRING | IT_CVAR,0,"Allow autoaim"   ,&cv_allowautoaim    ,0},
2566     {IT_STRING | IT_CVAR,0,"Allow turbo"     ,&cv_allowturbo      ,0},
2567     {IT_STRING | IT_CVAR,0,"Allow exitlevel" ,&cv_allowexitlevel  ,0},
2568     {IT_STRING | IT_CVAR,0,"Allow join player",&cv_allownewplayer ,0},
2569     {IT_STRING | IT_CVAR,0,"Teamplay"        ,&cv_teamplay        ,0},
2570     {IT_STRING | IT_CVAR,0,"TeamDamage"      ,&cv_teamdamage      ,0},
2571     {IT_STRING | IT_CVAR,0,"Fraglimit"       ,&cv_fraglimit       ,0},
2572     {IT_STRING | IT_CVAR,0,"Timelimit"       ,&cv_timelimit       ,0},
2573     {IT_STRING | IT_CVAR,0,"Deathmatch Type" ,&cv_deathmatch      ,0},
2574     {IT_STRING | IT_CVAR,0,"Frag's Weapon Falling", &cv_fragsweaponfalling, 0},
2575     {IT_STRING | IT_CVAR,0,"Maxplayers"      ,&cv_maxplayers      ,0},
2576     {IT_CALL | IT_WHITESTRING | IT_YOFFSET, 0,"Games Options >>" ,M_GameOption ,132},
2577 };
2578 
2579 menu_t  NetOptionDef =
2580 {
2581     "M_OPTTTL",
2582     "Net Options",
2583     NetOptionsMenu,
2584     M_DrawGenericMenu,
2585     NULL,
2586     sizeof(NetOptionsMenu)/sizeof(menuitem_t),
2587     60,40,
2588     0
2589 };
2590 
2591 static
M_NetOption(int choice)2592 void M_NetOption(int choice)
2593 {
2594     if(!server)
2595     {
2596         M_SimpleMessage("You are not the server\nYou cannot change network options\n");
2597         return;
2598     }
2599     Push_Setup_Menu(&NetOptionDef);
2600 }
2601 
2602 //===========================================================================
2603 //                    Connect OPTIONS MENU
2604 //===========================================================================
2605 
2606 
2607 menuitem_t ConnectOptionMenu[]=
2608 {
2609     {IT_STRING | IT_CVAR,0,"Download files", &cv_download_files , 0},
2610     {IT_STRING | IT_CVAR,0,"Download savegame", &cv_download_savegame , 0},
2611     {IT_STRING | IT_CVAR,0,"Netgame repair", &cv_netrepair , 0},
2612     {IT_STRING | IT_CVAR | IT_CV_STRING,0,"Server 1", &cv_server1 , 0},
2613     {IT_STRING | IT_CVAR | IT_CV_STRING,0,"Server 2", &cv_server2 , 0},
2614     {IT_STRING | IT_CVAR | IT_CV_STRING,0,"Server 3", &cv_server3 , 0},
2615 };
2616 
2617 menu_t  ConnectOptionDef =
2618 {
2619     NULL,
2620     "Connect Options",
2621     ConnectOptionMenu,
2622     M_DrawGenericMenu,
2623     NULL,
2624     sizeof(ConnectOptionMenu)/sizeof(menuitem_t),
2625     28,40,
2626     0
2627 };
2628 
M_ConnectOption(int choice)2629 void M_ConnectOption(int choice)
2630 {
2631     Push_Setup_Menu(&ConnectOptionDef);
2632 }
2633 
2634 
2635 //===========================================================================
2636 //                        Server OPTIONS MENU
2637 //===========================================================================
2638 menuitem_t ServerOptionsMenu[]=
2639 {
2640     {IT_STRING | IT_CVAR,0, "Internet server",     &cv_internetserver   ,  0},
2641     {IT_STRING | IT_CVAR
2642         | IT_CV_STRING  ,0, "Master server",       &cv_masterserver     ,  0},
2643     {IT_STRING | IT_CVAR
2644         | IT_CV_STRING  ,0, "Server name",         &cv_servername       ,  0},
2645     {IT_STRING | IT_CVAR,0, "Serve files",    &cv_SV_download_files , 0},
2646     {IT_STRING | IT_CVAR,0, "Serve savegame", &cv_SV_download_savegame , 0},
2647     {IT_STRING | IT_CVAR,0, "Serve repair",   &cv_SV_netrepair , 0},
2648 };
2649 
2650 menu_t  ServerOptionsDef =
2651 {
2652     NULL,
2653     "Server Settings",
2654     ServerOptionsMenu,
2655     M_DrawGenericMenu,
2656     NULL,
2657     sizeof(ServerOptionsMenu)/sizeof(menuitem_t),
2658     28,40,
2659     0
2660 };
2661 
2662 //===========================================================================
2663 //                    MultiPlayer OPTIONS MENU
2664 //===========================================================================
2665 
2666 
2667 menuitem_t MPOptionMenu[]=
2668 {
2669     {IT_SUBMENU | IT_WHITESTRING,0,"Connect Options >>",&ConnectOptionDef ,0},
2670     {IT_CALL    | IT_WHITESTRING,0,"Network Options >>",M_NetOption       ,0},
2671     {IT_SUBMENU | IT_WHITESTRING,0,"Server Options >>",&ServerOptionsDef  ,0},
2672     {IT_CALL    | IT_WHITESTRING,0,"Game Options >>"  ,M_GameOption       ,0},
2673 };
2674 
2675 menu_t  MPOptionDef =
2676 {
2677     "M_OPTTTL",
2678     "MultiPlayer Options",
2679     MPOptionMenu,
2680     M_DrawGenericMenu,
2681     NULL,
2682     sizeof(MPOptionMenu)/sizeof(menuitem_t),
2683     60,40,
2684     0
2685 };
2686 
2687 
2688 //===========================================================================
2689 //                          Read This! MENU 1
2690 //===========================================================================
2691 
2692 void M_DrawReadThis1(void);
2693 void M_DrawReadThis2(void);
2694 
2695 menuitem_t ReadMenu1[] =
2696 {
2697     {IT_SUBMENU | IT_NOTHING,0,"",&ReadDef2,0}
2698 };
2699 
2700 menu_t  ReadDef1 =
2701 {
2702     NULL,
2703     NULL,
2704     ReadMenu1,
2705     M_DrawReadThis1,
2706     NULL,
2707     sizeof(ReadMenu1)/sizeof(menuitem_t),
2708     280,185,
2709     0
2710 };
2711 
2712 //
2713 // Read This Menus
2714 // Had a "quick hack to fix romero bug"
2715 //
M_DrawReadThis1(void)2716 void M_DrawReadThis1(void)
2717 {
2718     // Draw to screen0, scaled
2719     switch ( gamemode )
2720     {
2721       case doom2_commercial:
2722         V_DrawScaledPatch_Name (0,0, "HELP");
2723         break;
2724       case doom_shareware:
2725       case doom_registered:
2726       case ultdoom_retail:
2727         V_DrawScaledPatch_Name (0,0, "HELP1");
2728         break;
2729       case heretic:
2730         V_DrawRawScreen_Num(0,0,W_GetNumForName("HELP1"), 320, 200);
2731         break;
2732       default:
2733         break;
2734     }
2735     return;
2736 }
2737 
2738 //===========================================================================
2739 //                          Read This! MENU 2
2740 //===========================================================================
2741 
2742 menuitem_t ReadMenu2[]=
2743 {
2744     {IT_SUBMENU | IT_NOTHING,0,"",&MainDef,0}
2745 };
2746 
2747 menu_t  ReadDef2 =
2748 {
2749     NULL,
2750     NULL,
2751     ReadMenu2,
2752     M_DrawReadThis2,
2753     NULL,
2754     sizeof(ReadMenu2)/sizeof(menuitem_t),
2755     330,175,
2756     0
2757 };
2758 
2759 
2760 //
2761 // Read This Menus - optional second page.
2762 //
M_DrawReadThis2(void)2763 void M_DrawReadThis2(void)
2764 {
2765     // Draw to screen0, scaled
2766     switch ( gamemode )
2767     {
2768       case ultdoom_retail:
2769       case doom2_commercial:
2770         // This hack keeps us from having to change menus.
2771         V_DrawScaledPatch_Name (0,0, "CREDIT");
2772         break;
2773       case doom_shareware:
2774       case doom_registered:
2775         V_DrawScaledPatch_Name (0,0, "HELP2");
2776         break;
2777       case heretic :
2778         V_DrawRawScreen_Num(0,0,W_GetNumForName("HELP2"), 320, 200);
2779       default:
2780         break;
2781     }
2782     return;
2783 }
2784 
2785 //===========================================================================
2786 //                        SOUND VOLUME MENU
2787 //===========================================================================
2788 
2789 void M_SfxVol(int choice);
2790 void M_MusicVol(int choice);
2791 void M_CDAudioVol (int choice);
2792 
2793 // [WDJ] unique names, mostly unused
2794 enum
2795 {
2796     SVM_sfx_vol = 0,
2797 } SVM_sound_e;
2798 
2799 // DoomLegacy graphics from legacy.wad: M_CDVOL
2800 menuitem_t SoundMenu[]=
2801 {
2802     {IT_CVARMAX   | IT_PATCH ,"M_SFXVOL","Sound Volume",&cv_soundvolume  ,'s'},
2803     {IT_BIGSLIDER | IT_SPACE ,NULL      ,NULL          ,&cv_soundvolume      },
2804     {IT_CVARMAX   | IT_PATCH ,"M_MUSVOL","Music Volume",&cv_musicvolume  ,'m'},
2805     {IT_BIGSLIDER | IT_SPACE ,NULL      ,NULL          ,&cv_musicvolume      },
2806 #ifdef CDMUS
2807 #ifdef SMIF_SDL
2808     // [WDJ] SDL cannot control CDROM volume.
2809     {IT_SPACE, NULL, NULL, NULL },
2810     {IT_STRING | IT_CVAR, 0, "CD Volume on", &cd_volume },  // on off
2811 #else
2812     {IT_CVARMAX   | IT_PATCH ,"M_CDVOL" ,"CD Volume"   ,&cd_volume       ,'c'}, // in legacy.wad
2813     {IT_BIGSLIDER | IT_SPACE ,NULL      ,NULL          ,&cd_volume           },
2814 #endif
2815 #endif
2816 #ifdef MUSSERV
2817 #if !defined(CDMUS) || !defined(SMIF_SDL)
2818     {IT_SPACE, NULL, NULL, NULL },
2819 #endif
2820     {IT_STRING | IT_CVAR | IT_YOFFSET, 0, "Music Pref",  &cv_musserver_opt },
2821 #endif
2822     {IT_STRING | IT_CVAR,0, "Random sound pitch",   &cv_rndsoundpitch , 0},
2823 };
2824 
2825 menu_t  SoundDef =
2826 {
2827     "M_SVOL",
2828     "Sound Volume",
2829     SoundMenu,
2830     M_DrawGenericMenu,
2831     NULL,
2832     sizeof(SoundMenu)/sizeof(menuitem_t),
2833     80,50,
2834     0
2835 };
2836 
2837 
2838 //===========================================================================
2839 //                          CONTROLS MENU
2840 //===========================================================================
2841 menuitem_t MControlMenu[]=
2842 {
2843     {IT_STRING | IT_CVAR, 0,"Control per key" ,&cv_controlperkey   ,0},
2844     {IT_SUBMENU | IT_WHITESTRING, 0,"Mouse Options >>" ,&MouseOptionsDef   , 'm'},
2845     {IT_SUBMENU | IT_WHITESTRING, 0,"Second Mouse config >>", &SecondMouseCfgdef, 0},
2846     {IT_CALL | IT_WHITESTRING, 0,"Player1 Controls >>", M_Setup_P1_Controls, '1'},
2847     {IT_CALL | IT_WHITESTRING, 0,"Player2 Controls >>", M_Setup_P2_Controls, '2'},
2848     {IT_SUBMENU | IT_WHITESTRING, 0,"Joystick Options >>" ,&JoystickOptionsDef   , 'j'},
2849 };
2850 
2851 menu_t  MControlDef =
2852 {
2853     "M_OPTTTL",
2854     "Controls",
2855     MControlMenu,
2856     M_DrawGenericMenu,
2857     NULL,
2858     sizeof(MControlMenu)/sizeof(menuitem_t),
2859     60,40,
2860     0
2861 };
2862 
2863 
2864 void M_DrawControl(void);               // added 3-1-98
2865 void M_ChangeControl(int choice);
2866 
2867 //
2868 // this is the same for all control pages
2869 //
2870 // IT_CONTROL: alphaKey is the control to be changed
2871 menuitem_t ControlMenu[]=
2872 {
2873     {IT_CONTROL, 0,"Fire"        ,M_ChangeControl,gc_fire       },
2874     {IT_CONTROL, 0,"Use/Open"    ,M_ChangeControl,gc_use        },
2875     {IT_CONTROL, 0,"Jump"        ,M_ChangeControl,gc_jump       },
2876     {IT_CONTROL, 0,"Forward"     ,M_ChangeControl,gc_forward    },
2877     {IT_CONTROL, 0,"Backpedal"   ,M_ChangeControl,gc_backward   },
2878     {IT_CONTROL, 0,"Turn Left"   ,M_ChangeControl,gc_turnleft   },
2879     {IT_CONTROL, 0,"Turn Right"  ,M_ChangeControl,gc_turnright  },
2880     {IT_CONTROL, 0,"Run"         ,M_ChangeControl,gc_speed      },
2881     {IT_CONTROL, 0,"Strafe On"   ,M_ChangeControl,gc_strafe     },
2882     {IT_CONTROL, 0,"Strafe Left" ,M_ChangeControl,gc_strafeleft },
2883     {IT_CONTROL, 0,"Strafe Right",M_ChangeControl,gc_straferight},
2884     {IT_CONTROL, 0,"Look Up"     ,M_ChangeControl,gc_lookup     },
2885     {IT_CONTROL, 0,"Look Down"   ,M_ChangeControl,gc_lookdown   },
2886     {IT_CONTROL, 0,"Center View" ,M_ChangeControl,gc_centerview },
2887     {IT_CONTROL, 0,"Mouselook"   ,M_ChangeControl,gc_mouseaiming},
2888 
2889     {IT_SUBMENU | IT_WHITESTRING | IT_YOFFSET, 0,"next" ,&ControlDef2,128}
2890 };
2891 
2892 menu_t  ControlDef =
2893 {
2894     "M_CONTRO", // in legacy.wad
2895     "Setup Controls",
2896     ControlMenu,
2897     M_DrawControl,
2898     NULL,
2899     sizeof(ControlMenu)/sizeof(menuitem_t),
2900     50,40,
2901     0
2902 };
2903 
2904 // IT_CONTROL: alphaKey is the control to be changed
2905 menuitem_t ControlMenu2[]=
2906 {
2907   {IT_CONTROL, 0,"Fist/Chainsaw"  ,M_ChangeControl,gc_weapon1},
2908   {IT_CONTROL, 0,"Pistol"         ,M_ChangeControl,gc_weapon2},
2909   {IT_CONTROL, 0,"Shotgun/Double" ,M_ChangeControl,gc_weapon3},
2910   {IT_CONTROL, 0,"Chaingun"       ,M_ChangeControl,gc_weapon4},
2911   {IT_CONTROL, 0,"Rocket Launcher",M_ChangeControl,gc_weapon5},
2912   {IT_CONTROL, 0,"Plasma rifle"   ,M_ChangeControl,gc_weapon6},
2913   {IT_CONTROL, 0,"BFG"            ,M_ChangeControl,gc_weapon7},
2914   {IT_CONTROL, 0,"Chainsaw"       ,M_ChangeControl,gc_weapon8},
2915   {IT_CONTROL, 0,"Previous Weapon",M_ChangeControl,gc_prevweapon},
2916   {IT_CONTROL, 0,"Next Weapon"    ,M_ChangeControl,gc_nextweapon},
2917   {IT_CONTROL, 0,"Best Weapon"    ,M_ChangeControl,gc_bestweapon},
2918   {IT_CONTROL, 0,"Inventory Left" ,M_ChangeControl,gc_invprev},
2919   {IT_CONTROL, 0,"Inventory Right",M_ChangeControl,gc_invnext},
2920   {IT_CONTROL, 0,"Inventory Use"  ,M_ChangeControl,gc_invuse },
2921   {IT_CONTROL, 0,"Fly down"       ,M_ChangeControl,gc_flydown},
2922 
2923   {IT_SUBMENU | IT_WHITESTRING | IT_YOFFSET, 0,"next"    ,&ControlDef3,148}
2924 };
2925 
2926 menu_t  ControlDef2 =
2927 {
2928     "M_CONTRO", // in legacy.wad
2929     "Setup Controls",
2930     ControlMenu2,
2931     M_DrawControl,
2932     NULL,
2933     sizeof(ControlMenu2)/sizeof(menuitem_t),
2934     50,40,
2935     0
2936 };
2937 
2938 // IT_CONTROL: alphaKey is the control to be changed
2939 menuitem_t ControlMenu3[]=
2940 {
2941   {IT_CONTROL, 0,"Talk key"       ,M_ChangeControl,gc_talkkey},
2942   {IT_CONTROL, 0,"Rankings/Scores",M_ChangeControl,gc_scores },
2943   {IT_CONTROL, 0,"Console"        ,M_ChangeControl,gc_console},
2944   {IT_CONTROL, 0,"Screenshot"     ,M_ChangeControl,gc_screenshot},
2945   {IT_WHITESTRING | IT_SPACE, 0, "Joystick and Mouse Only" ,0},
2946   {IT_CONTROL, 0,"Main menu"      ,M_ChangeControl,gc_menuesc},
2947   {IT_CONTROL, 0,"Pause"          ,M_ChangeControl,gc_pause},
2948   {IT_CONTROL, 0,"Automap"        ,M_ChangeControl,gc_automap},
2949 
2950   {IT_SUBMENU | IT_WHITESTRING | IT_YOFFSET, 0,"next"    ,&ControlDef,128}
2951 };
2952 
2953 menu_t  ControlDef3 =
2954 {
2955     "M_CONTRO", // in legacy.wad
2956     "Setup Controls",
2957     ControlMenu3,
2958     M_DrawControl,
2959     NULL,
2960     sizeof(ControlMenu3)/sizeof(menuitem_t),
2961     50,40,
2962     0
2963 };
2964 
2965 
2966 //
2967 // Start the controls menu, setting it up for either the console player,
2968 // or the secondary splitscreen player
2969 //
2970 static  byte  controls_player;
2971 static  int   (*setupcontrols)[2];  // pointer to the gamecontrols of the player being edited
2972 
2973 // called by player1 multiplayer setup menu
M_Setup_P1_Controls(int choice)2974 void M_Setup_P1_Controls(int choice)
2975 {
2976     // set the gamecontrols to be edited
2977     // was called from main Controls (for console player, then)
2978     controls_player = 0;
2979     setupcontrols = gamecontrol;
2980     currentMenu->lastOn = itemOn;
2981     Push_Setup_Menu(&ControlDef);
2982 }
2983 
2984 // called by player2 multiplayer setup menu
M_Setup_P2_Controls(int choice)2985 void M_Setup_P2_Controls(int choice)
2986 {
2987     // set the gamecontrols to be edited
2988     controls_player = 1;
2989     setupcontrols = gamecontrol2;
2990     currentMenu->lastOn = itemOn;
2991     Push_Setup_Menu(&ControlDef);
2992 }
2993 
2994 
2995 //
2996 //  Draws the Customized Controls menu
2997 //
M_DrawControl(void)2998 void M_DrawControl(void)
2999 {
3000     char     tmp[50];
3001     int      i;
3002     int      keys[2];
3003     menuitem_t * mip;
3004 
3005     // draw title, strings and submenu
3006     M_DrawGenericMenu();
3007 
3008     M_CentreText (ControlDef.y-12,
3009         (controls_player ? "PLAYER2: ENTER TO CHANGE, BACKSPACE TO CLEAR" :
3010                            "PLAYER1: ENTER TO CHANGE, BACKSPACE TO CLEAR") );
3011 
3012     for(i=0;i<currentMenu->numitems;i++)
3013     {
3014         mip = & currentMenu->menuitems[i];
3015         if (mip->status != IT_CONTROL)
3016             continue;
3017 
3018         // alphaKey is the control to be changed
3019         keys[0] = setupcontrols[mip->alphaKey][0];
3020         keys[1] = setupcontrols[mip->alphaKey][1];
3021 
3022         tmp[0]='\0';
3023         if (keys[0] == KEY_NULL && keys[1] == KEY_NULL)
3024         {
3025             strcpy(tmp, "---");
3026         }
3027         else
3028         {
3029             if( keys[0] != KEY_NULL )
3030                 strcat (tmp, G_KeynumToString (keys[0]));
3031 
3032             if( keys[0] != KEY_NULL && keys[1] != KEY_NULL )
3033                 strcat(tmp," or ");
3034 
3035             if( keys[1] != KEY_NULL )
3036                 strcat (tmp, G_KeynumToString (keys[1]));
3037 
3038 
3039         }
3040         V_DrawString(ControlDef.x+220-V_StringWidth(tmp), ControlDef.y + i*8,V_WHITEMAP, tmp);
3041     }
3042 
3043 }
3044 
3045 static int controltochange;
3046 
M_ChangecontrolResponse(event_t * ev)3047 void M_ChangecontrolResponse(event_t* ev)
3048 {
3049     int        control;
3050     int        found;
3051     int        ch=ev->data1;
3052 
3053     // The new key for a control function.
3054     // ESCAPE cancels assignment.
3055     // Not allowed to assign KEY_ESCAPE nor KEY_PAUSE to another control function.
3056 
3057     // [WDJ] Test has interference with joystick assign.
3058     // Input gamecontrol[gc_menuesc] is translated to KEY_ESCAPE.
3059     //   This prevents reprogram of joystick buttons using button already programmed for gc_menuesc, which is probably a good thing.
3060     // If cancel gamecontrol[gc_menuesc] assignment, menus may become inaccessible to joystick.
3061     // Joystick gamecontrol[gc_pause]: does not cancel this assign, does not interfere.
3062     //   Pause game does test of gamecontrol[gc_pause] directly, not translated.
3063     if (ch!=KEY_ESCAPE && ch!=KEY_PAUSE)
3064     {
3065 
3066         switch (ev->type)
3067         {
3068           // ignore mouse/joy movements, just get buttons
3069           case ev_mouse:
3070             ch = KEY_NULL;      // no key
3071             break;
3072 
3073           // keypad arrows are converted for the menu in cursor arrows
3074           // so use the event instead of ch
3075           case ev_keydown:
3076             ch = ev->data1;
3077             break;
3078 
3079           default:
3080             break;
3081         }
3082 
3083         control = controltochange;
3084 
3085         // check if we already entered this key
3086         found = -1;
3087         if (setupcontrols[control][0]==ch)
3088             found = 0;
3089         else
3090         if (setupcontrols[control][1]==ch)
3091             found = 1;
3092         if (found>=0)
3093         {
3094             // If controltochange has existing assignment of same button, then
3095             // replace mouse and joy clicks by double clicks.
3096 #if 1
3097             if( ch>=KEY_MOUSE1 && ch<KEY_JOY0BUT0 )  // For all MOUSE input
3098             {
3099                 // Spacing of KEY_MOUSEx to KEY_MOUSExDBL is uniform for all MOUSE.
3100                 setupcontrols[control][found] = ch + (KEY_MOUSE1DBL - KEY_MOUSE1);
3101                 goto done;
3102             }
3103 #else
3104             // FIXME: first mouse only
3105             if (ch>=KEY_MOUSE1 && ch<=KEY_MOUSE1+MOUSEBUTTONS)
3106             {
3107                 setupcontrols[control][found] = ch + (KEY_MOUSE1DBL - KEY_MOUSE1);
3108                 goto done;
3109             }
3110 #endif
3111 
3112 #ifdef JOY_BUTTONS_DOUBLE
3113 #if 1
3114             else if( ch>=KEY_JOY0BUT0 && ch<=(KEY_JOYLAST) )  // all JOY input
3115 #else
3116             // FIXME: JOY0 only
3117             else if (ch>=KEY_JOY0BUT0 && ch<=(KEY_JOY0BUT0+JOYBUTTONS))  // one JOY
3118 #endif
3119             {
3120                 // Change to joystick doubleclicks.
3121                 // Spacing of JOYxBUTn to JOYxBUTnDBL is uniform for all JOY and all BUT.
3122                 setupcontrols[control][found] = ch + (KEY_JOY0BUT0DBL - KEY_JOY0BUT0);
3123                 goto done;
3124             }
3125 #endif
3126         }
3127 
3128         // Direct replacement is default.
3129         {
3130             // [WDJ] FIXME: This is flaky logic.
3131             // check if change key1 or key2, or replace the two by the new
3132             found = 0;
3133             if (setupcontrols[control][0] == KEY_NULL)
3134                 found++;
3135             if (setupcontrols[control][1] == KEY_NULL)
3136                 found++;
3137 
3138             if( (controls_player == 0) && (control == gc_menuesc)
3139                 && ((ch == KEY_NULL) || (ch == KEY_BACKSPACE)) )
3140             {
3141                 // Protect gc_menuesc against being set to NULL.
3142                 goto done;
3143             }
3144 
3145             if (found==2)
3146             {
3147                 found = 0;
3148                 setupcontrols[control][1] = KEY_NULL;  //replace key1 ,clear key2
3149             }
3150             G_CheckDoubleUsage(ch);
3151             setupcontrols[control][found] = ch;
3152         }
3153     }
3154 
3155 done:
3156     M_StopMessage(0);
3157 }
3158 
M_ChangeControl(int choice)3159 void M_ChangeControl(int choice)
3160 {
3161     controltochange = currentMenu->menuitems[choice].alphaKey;
3162     snprintf (msgtmp, MSGTMP_LEN,
3163               "Hit the new key for\n%s\nESC for Cancel", currentMenu->menuitems[choice].text);
3164     msgtmp[MSGTMP_LEN-1] = '\0';
3165 
3166     M_StartMessage (msgtmp, M_ChangecontrolResponse, MM_EVENTHANDLER);
3167 }
3168 
3169 
3170 //===========================================================================
3171 // Video mode and drawmode test and draw support.
3172 
3173 //max modes displayed in one column
3174 //#define MAXCOLUMNMODES   10
3175 #define MAXCOLUMNMODES   8
3176 #define MAXMODEDESCS     (MAXCOLUMNMODES*3)
3177 #define MODES_X          16
3178 #define MODES_Y          44
3179 #define MODES_X_INC      (8*13)
3180 #define MODES_Y_INC      8
3181 #define MODETXT_Y        (MODES_Y + 60 + 24)
3182 
3183 static int vidm_testing_cnt=0;  // test videomode failsafe
3184 static int vidm_current=0;  // modedesc index
3185 static int vidm_nummodes;
3186 static int vidm_column_size;
3187 
3188 
3189 // Draw the instructions for the video mode setting
3190 //   vm_mode : 1 for setting video mode
3191 //   current_mode_name : the desc string for the current mode
3192 //   mode_name : the desc string for the selected mode
3193 static
draw_set_mode_instructions(byte vm_mode,const char * current_mode_name,const char * sel_mode_name)3194 void  draw_set_mode_instructions( byte vm_mode, const char * current_mode_name, const char * sel_mode_name )
3195 {
3196     char  temp[80];
3197     byte  test_mkcfg = 0;
3198 
3199     if (vidm_testing_cnt>0)
3200     {
3201         sprintf(temp, "TESTING MODE %s", sel_mode_name );
3202         M_CentreText(MODETXT_Y + 20, temp );
3203         M_CentreText(MODETXT_Y + 30, "Please wait 5 seconds..." );
3204     }
3205 #ifdef CONFIG_MENU_PAGE
3206     else if( menu_cfg_editing )
3207     {
3208         M_CentreText(MODETXT_Y,"Press ENTER to set mode");
3209         M_CentreText(MODETXT_Y + 40,"Press ESC to exit");
3210         test_mkcfg = 1;
3211     }
3212 #endif
3213     else
3214     {
3215 //        M_CentreText(MODETXT_Y,"Press ENTER to set mode");
3216         M_CentreText(MODETXT_Y,"Press S to set mode");
3217 
3218         M_CentreText(MODETXT_Y + 10,"T to test mode for 5 seconds");
3219 
3220         if( current_mode_name )
3221         {
3222             sprintf(temp, "D to set default to  %s", current_mode_name );
3223             M_CentreText(MODETXT_Y + 20,temp);
3224         }
3225 
3226         if( vm_mode )
3227         {
3228           sprintf(temp, "Current default : %dx%d (%d bits)", cv_scr_width.value, cv_scr_height.value, cv_scr_depth.value);
3229         }
3230         else
3231         {
3232 #if 1
3233 //          sprintf(temp, "Current drawmode : %s", current_mode_name );
3234 //          sprintf(temp, "Current default : %s", drawmode_sel_t[ drawmode_to_drawmode_sel_t[ cv_drawmode.value ] ].strvalue );
3235           sprintf(temp, "Current default : %s", cv_drawmode.string );
3236 #else
3237           // Redundant, looks like an error.
3238           sprintf(temp, "Current drawmode : %s %s", current_mode_name, rendermode_name[rendermode] );
3239 #endif
3240           test_mkcfg = 1;
3241         }
3242         M_CentreText(MODETXT_Y + 30,temp);
3243 
3244         M_CentreText(MODETXT_Y + 40,"Press ESC to exit");
3245     }
3246 
3247     if( test_mkcfg && ! M_Have_configfile_drawmode() )
3248     {
3249         // is current_mode_name only during drawmode menu
3250         sprintf(temp, "C to make config: %s", drawmode_sel_t[ drawmode_to_drawmode_sel_t[ cv_drawmode.EV ] ].strvalue );
3251 #if 1
3252         V_DrawString( 2, 24, V_WHITEMAP, temp);
3253 #else
3254         M_CentreText(MODETXT_Y + 20,temp);
3255 #endif
3256     }
3257 
3258     // Draw the cursor for the VidMode menu
3259     if (skullAnimCounter<4)    //use the Skull anim counter to blink the cursor
3260 //    if( (itemOn > 0) && skullAnimCounter<4 )    //use the Skull anim counter to blink the cursor
3261     {
3262         int i = MODES_X - 10 + ((vidm_current / vidm_column_size) * MODES_X_INC);
3263         int j = MODES_Y + ((vidm_current % vidm_column_size) * MODES_Y_INC);
3264         V_DrawCharacter( i, j, '*' | 0x80);  // white
3265     }
3266 }
3267 
3268 // Stay in the column.
3269 // Alternative is to jump from column to column.
3270 #define VIDMODE_COLUMNAR_MOVEMENT
3271 
3272 //added:30-01-98: special menuitem key handler for video mode list
M_VideoMode_key_handler(int key)3273 void M_VideoMode_key_handler (int key)
3274 {
3275 #ifdef VIDMODE_COLUMNAR_MOVEMENT
3276     byte old_col, new_col;
3277 #endif
3278 
3279     // Test specific key handler
3280     if( key_handler2(key) )  return;
3281 
3282 #ifdef VIDMODE_COLUMNAR_MOVEMENT
3283     old_col = vidm_current / vidm_column_size;
3284 #endif
3285 
3286     switch( key )
3287     {
3288       case KEY_DOWNARROW:
3289         S_StartSound(menu_sfx_updown);
3290         vidm_current++;
3291 #ifdef VIDMODE_COLUMNAR_MOVEMENT
3292         new_col = vidm_current / vidm_column_size;
3293         if( ( vidm_current >= vidm_nummodes )
3294             || new_col != old_col )
3295         {
3296             // Move to top of the column
3297             vidm_current = old_col * vidm_column_size;
3298         }
3299 #else
3300         if( vidm_current >= vidm_nummodes )
3301         {
3302             // Move to the first item of the mode list.
3303             vidm_current = 0;
3304         }
3305 #endif
3306         break;
3307 
3308       case KEY_UPARROW:
3309         S_StartSound(menu_sfx_updown);
3310         vidm_current--;
3311 #ifdef VIDMODE_COLUMNAR_MOVEMENT
3312         new_col = vidm_current / vidm_column_size;
3313         if( ( vidm_current < 0 )
3314             || new_col != old_col )
3315         {
3316             // Move to bottom of the column
3317             vidm_current = (old_col * vidm_column_size) + vidm_column_size - 1;
3318         }
3319 #else
3320         if( vidm_current < 0 )
3321         {
3322             // Move to the last item of the mode list.
3323             vidm_current = vidm_nummodes-1;
3324         }
3325 #endif
3326         break;
3327 
3328       case KEY_LEFTARROW:
3329         S_StartSound(menu_sfx_val);
3330         if( (vidm_current - vidm_column_size) < 0  )  return;
3331         vidm_current -= vidm_column_size;
3332         break;
3333 
3334       case KEY_RIGHTARROW:
3335         S_StartSound(menu_sfx_val);
3336         if( (vidm_current + vidm_column_size) >= vidm_nummodes )  return;
3337         vidm_current += vidm_column_size;
3338         break;
3339 
3340       case KEY_ESCAPE:      //this one same as M_Responder
3341         key_handler2 = NULL;
3342         S_StartSound(menu_sfx_esc);
3343         Pop_Menu();
3344         break;
3345 
3346       default:
3347         break;
3348     }
3349 
3350     if( vidm_current >= vidm_nummodes )
3351         vidm_current = vidm_nummodes-1;
3352     if( vidm_current < 0 )
3353         vidm_current = 0;
3354     return;
3355 }
3356 
3357 
3358 //===========================================================================
3359 //                        VIDEO MODE MENU
3360 //===========================================================================
3361 void M_DrawVideoMode(void);             //added:30-01-98:
3362 
3363 byte  video_test_key_handler( int key );
3364 byte  drawmode_test_key_handler( int key );
3365 
3366 menuitem_t VideoModeMenu[]=
3367 {
3368     {IT_KEYHANDLER | IT_EXTERNAL, 0, "", M_VideoMode_key_handler, '\0'},     // dummy menuitem for the control func
3369 };
3370 
3371 
3372 menu_t  VideoModeDef =
3373 {
3374     "M_VIDEO", // in legacy.wad
3375     "Video Mode",
3376     VideoModeMenu,      // menuitem_t ->
3377     M_DrawVideoMode,    // drawing routine ->
3378     NULL,
3379     sizeof(VideoModeMenu)/sizeof(menuitem_t),
3380     48,36,              // x,y
3381     0                   // lastOn
3382 };
3383 
3384 
3385 
3386 typedef struct
3387 {
3388     modenum_t  modenum; // video mode number in format of setmodeneeded
3389     char    *  desc;    // XXXxYYY
3390 } modedesc_t;
3391 
3392 static modedesc_t   modedescs[MAXMODEDESCS];
3393 static modenum_t    vidm_previousmode;  // modenum in format of setmodeneeded
3394 
3395 
3396 //
3397 // Draw the video modes list, a-la-Quake
3398 //
M_DrawVideoMode(void)3399 void M_DrawVideoMode(void)
3400 {
3401     modenum_t  mode_320x200 = VID_GetModeForSize( 320, 200, MODE_fullscreen );
3402     range_t moderange;
3403     modenum_t  dmode;  // draw modenum
3404 #ifdef CONFIG_MENU_PAGE
3405     modenum_t  cfg_vid_mode;
3406 #endif
3407     modedesc_t * mdp;  // modedesc
3408     modedesc_t * current_modedesc;
3409     const char * current_modename = "";
3410     int     i, row, col;
3411     char    *desc;
3412 
3413     // setup key handler for video modes
3414     key_handler2 = video_test_key_handler;  // key handler
3415 
3416     // draw title
3417     M_DrawMenuTitle();
3418 
3419 #ifdef CONFIG_MENU_PAGE
3420     // Current video mode as default.
3421     cfg_vid_mode.modetype = vid.modenum.modetype;
3422     cfg_vid_mode.index = vid.modenum.index;
3423     menu_cfg_editing = 0;  // normal
3424     if( menu_cfg )
3425     {
3426         V_DrawString( 2, 1, V_WHITEMAP, menu_cfg_string[menu_cfg]);
3427         V_DrawString( BASEVIDWIDTH - (14*8), 1, V_WHITEMAP, "Insert Delete");
3428         if( (cv_scr_width.state & CS_CONFIG) != menu_cfg )
3429         {
3430             consvar_t temp_cvar2;
3431             int  temp_height, temp_fullscreen;
3432 
3433             // modify video display and key handlers
3434             menu_cfg_editing = 2;  // edit background, not live data
3435 
3436             // Get current video mode, dependent upon menu_cfg.
3437             // No display if no values.
3438             if( ! config_cvar_edit_open( & cv_scr_width ) )
3439                 goto draw_instructions;  // cv_scr_width missing, wait for insert
3440             if( ! CV_Get_Pushed_cvar( &cv_scr_height, menu_cfg, /*OUT*/ &temp_cvar2 ) )
3441                 goto draw_instructions;  // cv_scr_height missing, wait for insert
3442 
3443             temp_height = temp_cvar2.value;
3444             // use fullscreen from menu_cfg when available, else from current config
3445             temp_fullscreen = ( CV_Get_Pushed_cvar( &cv_fullscreen, menu_cfg, /*OUT*/ &temp_cvar2 ))?
3446                                 temp_cvar2.value : cv_fullscreen.value;
3447 
3448             cfg_vid_mode = VID_GetModeForSize( temp_cvar.value, temp_height, temp_fullscreen );
3449         }
3450     }
3451 #endif
3452 
3453     dmode.modetype = vid_mode_table[ cv_fullscreen.EV ];  // fullscreen or window
3454     vidm_nummodes = 0;
3455     current_modedesc = NULL;
3456     current_modename = NULL;
3457     moderange = VID_ModeRange( dmode.modetype );   // indexing
3458     for (i=moderange.first ; i<=moderange.last ; i++)
3459     {
3460         dmode.index = i;
3461         desc = VID_GetModeName (dmode);
3462         if (desc)
3463         {
3464             int j;
3465 
3466             //when a resolution exists both under VGA and VESA, keep the
3467             // VESA mode, which is always a higher modenum
3468             for (j=0 ; j<vidm_nummodes ; j++)
3469             {
3470                 mdp = & modedescs[j];
3471                 if (!strcmp (mdp->desc, desc))
3472                 {
3473                     // 320x200 fullscreen is always standard VGA, not vesa
3474                     if (mdp->modenum.modetype != mode_320x200.modetype
3475                         || mdp->modenum.index != mode_320x200.index)
3476                     {
3477                         // replace previous entry (VGA)
3478                         mdp->modenum = dmode;
3479                     }
3480                     goto  detect_current_setting;
3481                 }
3482             }
3483 
3484             // Create a new mode descriptor.
3485             mdp = & modedescs[vidm_nummodes++];
3486             mdp->desc = desc;
3487             mdp->modenum = dmode;
3488 
3489         detect_current_setting:
3490             // Detect current setting, for highlight
3491 #ifdef CONFIG_MENU_PAGE
3492             if (dmode.modetype == cfg_vid_mode.modetype
3493                 && dmode.index == cfg_vid_mode.index )
3494 #else
3495             if (dmode.modetype == vid.modenum.modetype
3496                 && dmode.index == vid.modenum.index )
3497 #endif
3498             {
3499                 current_modedesc = mdp;
3500                 current_modename = mdp->desc;
3501             }
3502 
3503             // Must be after the detection.
3504             if( vidm_nummodes >= MAXMODEDESCS )  break;
3505         }
3506     }
3507 
3508     vidm_column_size = (vidm_nummodes+2) / 3;
3509 
3510     // list down col first
3511     col = MODES_X;
3512     row = MODES_Y;
3513     for(i=0; i<vidm_nummodes; i++)
3514     {
3515         mdp = & modedescs[i];
3516 
3517         V_DrawString (col, row, (mdp == current_modedesc) ? V_WHITEMAP : 0, mdp->desc);
3518 
3519         row += MODES_Y_INC;
3520         if((i % vidm_column_size) == (vidm_column_size-1))
3521         {
3522             col += MODES_X_INC;
3523             row = MODES_Y;
3524         }
3525     }
3526 
3527 #ifdef CONFIG_MENU_PAGE
3528 draw_instructions:
3529 #endif
3530     draw_set_mode_instructions( 1, current_modename, modedescs[vidm_current].desc );
3531 }
3532 
3533 
3534 // keyboard intercept
3535 // Return 0= continue, 1= intercept key, 2= testing.
video_test_key_handler(int key)3536 byte  video_test_key_handler( int key )
3537 {
3538     set_drawmode = DRM_none;
3539     req_drawmode = DRM_none;  // cancel any command line setup
3540 
3541     if (vidm_testing_cnt>0)
3542     {
3543        // change back to the previous mode quickly
3544        if (key==KEY_ESCAPE)
3545        {
3546            setmodeneeded = vidm_previousmode;
3547            vidm_testing_cnt = 0;
3548        }
3549        return 2;
3550     }
3551 
3552 #ifdef CONFIG_MENU_PAGE
3553     // Turn menu_cfg on and off.
3554     if( config_cvar_key_handler( key ) )
3555         goto used_key;
3556 
3557     if( menu_cfg_editing )
3558     {
3559         // edit video mode that is not current
3560         switch( key )
3561         {
3562           case KEY_ENTER:
3563             {
3564                 modestat_t ms = VID_GetMode_Stat( modedescs[vidm_current].modenum );
3565                 config_cvar_edit_setvalue( &cv_scr_width, ms.width );
3566                 config_cvar_edit_setvalue( &cv_scr_height, ms.height );
3567             }
3568             goto used_key;
3569           case KEY_INS :  // insert config
3570             config_cvar_edit_insert( &cv_scr_width, 1 );  // using menu_cfg
3571             config_cvar_edit_insert( &cv_scr_height, 1 );  // using menu_cfg
3572             goto used_key;
3573          case KEY_DELETE :  // delete config
3574             config_cvar_edit_delete( &cv_scr_width );  // using menu_cfg
3575             config_cvar_edit_delete( &cv_scr_height );  // using menu_cfg
3576             goto used_key;
3577 	 case 'c' :
3578 	 case 'C' :
3579             create_initial_drawmode_config();
3580             goto used_key;
3581          default:
3582             break;
3583         }
3584         // block live video changes
3585         return 0;
3586     }
3587 #endif
3588 
3589     switch( key )
3590     {
3591       case KEY_ENTER:
3592       case 'S':
3593       case 's':
3594         S_StartSound(menu_sfx_enter);
3595         req_command_video_settings = 0;  // disable command line video settings
3596         goto change_mode;
3597 
3598       case 'T':
3599       case 't':
3600         S_StartSound(menu_sfx_action);
3601         vidm_testing_cnt = TICRATE*5;
3602         goto change_mode;
3603 
3604       case 'D':
3605       case 'd':
3606         // current active mode becomes the default mode.
3607         S_StartSound(menu_sfx_action);
3608         SCR_SetDefaultMode ();
3609         req_command_video_settings = 0;  // disable command line video settings
3610         goto used_key;
3611 
3612       default:
3613         break;
3614      }
3615     return 0;
3616 
3617  change_mode:
3618     // Change the active video mode.
3619     vidm_previousmode = vid.modenum;
3620     if( setmodeneeded.modetype == MODE_NOP ) //in case the previous setmode was not finished
3621         setmodeneeded = modedescs[vidm_current].modenum;
3622     goto used_key;
3623 
3624  used_key:
3625     return 1;
3626 }
3627 
3628 
3629 //===========================================================================
3630 //                        DRAWING OPTIONS MENU
3631 //===========================================================================
3632 
3633 static byte  vidm_previous_drawmode = 0;  // cv_drawmode
3634 static byte  vidm_drawmode[MAXCOLUMNMODES+2];  // drawmode for a menu row
3635 
3636 void M_Draw_drawmode(void);
3637 
3638 menuitem_t DrawmodeMenu[]=
3639 {
3640     {IT_KEYHANDLER | IT_EXTERNAL, 0, "", M_VideoMode_key_handler, '\0'},     // dummy menuitem for the control func
3641 //    {IT_STRING | IT_CVAR, 0, "Draw Mode", &cv_drawmode      , 0},
3642 };
3643 
3644 menu_t  DrawmodeDef =
3645 {
3646     NULL,
3647     "Drawmode Options",
3648     DrawmodeMenu, // menuitem_t ->
3649     M_Draw_drawmode,
3650     NULL,
3651     sizeof(DrawmodeMenu)/sizeof(menuitem_t),
3652     48,36,              // x,y
3653     0                   // lastOn
3654 };
3655 
3656 // keyboard intercept
3657 // Return 0= continue, 1= intercept key, 2= testing.
drawmode_test_key_handler(int key)3658 byte  drawmode_test_key_handler( int key )
3659 {
3660     set_drawmode = DRM_none;
3661     req_drawmode = DRM_none;  // cancel any command line setup
3662 
3663     if (vidm_testing_cnt>0)
3664     {
3665        // change back to the previous mode quickly
3666        if (key==KEY_ESCAPE)
3667        {
3668            set_drawmode = vidm_previous_drawmode;  // drawmode for menu row
3669            drawmode_recalc = true;
3670            vidm_testing_cnt = 0;
3671        }
3672        return 2;
3673     }
3674 
3675     switch( key )
3676     {
3677       case KEY_ENTER:
3678       case 'S':
3679       case 's':
3680         S_StartSound(menu_sfx_enter);
3681         goto change_drawmode;
3682 
3683       case 'T':
3684       case 't':
3685         S_StartSound(menu_sfx_action);
3686         vidm_testing_cnt = TICRATE*5;
3687         goto change_drawmode;
3688 
3689       case 'D':
3690       case 'd':
3691         // current active mode becomes the default mode.
3692         S_StartSound(menu_sfx_action);
3693         CV_SetValue( &cv_drawmode, cv_drawmode.EV );
3694         goto used_key;
3695 
3696       case 'c' :
3697       case 'C' :
3698         create_initial_drawmode_config();
3699         goto used_key;
3700 
3701       default:
3702         break;
3703     }
3704     return 0;
3705 
3706 change_drawmode:
3707     vidm_previous_drawmode = cv_drawmode.EV;
3708     if( ! rendermode_recalc ) // in case the previous setmode was not finished
3709     {
3710         // Do not change the graphics and video settings while using them for the menus.
3711         // Safer to change the graphics and video mode setups in between
3712         // frame drawing cycles.
3713         set_drawmode = vidm_drawmode[vidm_current];  // drawmode for menu row
3714         drawmode_recalc = true;
3715     }
3716     goto used_key;
3717 
3718  used_key:
3719     return 1;
3720 }
3721 
3722 
M_Draw_drawmode(void)3723 void M_Draw_drawmode(void)
3724 {
3725     int  i, row, col;
3726 
3727     // draw title
3728     M_DrawMenuTitle();
3729 
3730     vidm_nummodes = 0;
3731     vidm_column_size = MAXCOLUMNMODES;
3732 
3733     // list down col first
3734     col = MODES_X;
3735     row = MODES_Y;
3736     // step through cv_drawmode settings
3737     for(i=0; i<num_drawmode_sel; i++)
3738     {
3739         byte dm = drawmode_sel_t[i].value; // vid_drawmode_e
3740         const char * dmstr = drawmode_sel_t[i].strvalue;
3741         if( dmstr == NULL )  break;   // end of drawmode_sel_t
3742         if( drawmode_sel_avail[dm] == 0 )  continue;
3743 
3744         vidm_drawmode[vidm_nummodes++] = dm;  // the drawmode at this row
3745 
3746         // current drawmode: cv_drawmode.EV
3747         // default drawmode: cv_drawmode.value
3748         // Both have values from vid_drawmode_e.
3749         // whitemap the current
3750         V_DrawString (col, row, ((dm != cv_drawmode.EV) ? 0 : V_WHITEMAP), dmstr);
3751 
3752         if( vidm_nummodes > MAXCOLUMNMODES )  break;
3753 
3754         row += MODES_Y_INC;
3755         if((i % vidm_column_size) == (vidm_column_size-1))
3756         {
3757             col += MODES_X_INC;
3758             row = MODES_Y;
3759         }
3760     }
3761 
3762     byte sel_dm = vidm_drawmode[vidm_current];  // selected drawmode
3763     const char * sel_drawmode_str = drawmode_sel_t[ drawmode_to_drawmode_sel_t[ sel_dm ] ].strvalue;
3764     const char * cur_drawmode_str = drawmode_sel_t[ drawmode_to_drawmode_sel_t[ cv_drawmode.EV ] ].strvalue;
3765     draw_set_mode_instructions( 0, cur_drawmode_str, sel_drawmode_str );
3766 
3767     // setup key handler for video modes
3768     key_handler2 = drawmode_test_key_handler;  // key handler
3769 }
3770 
3771 
3772 #ifdef SAVEGAMEDIR
3773 //===========================================================================
3774 // GAME DIR MENU
3775 //===========================================================================
3776 void M_DrawDir(void);
3777 
3778 void M_DirSelect(int choice);
3779 void M_Get_SaveDir(int choice);
3780 void M_DirEnter(int choice);
3781 
3782 #define NUM_DIRLINE  6
3783 menuitem_t LoadDirMenu[]=
3784 {
3785     {IT_CALL | IT_NOTHING,"",0, M_DirEnter,'/'},
3786     {IT_CALL | IT_NOTHING,"",0, M_DirSelect,'1'},
3787     {IT_CALL | IT_NOTHING,"",0, M_DirSelect,'2'},
3788     {IT_CALL | IT_NOTHING,"",0, M_DirSelect,'3'},
3789     {IT_CALL | IT_NOTHING,"",0, M_DirSelect,'4'},
3790     {IT_CALL | IT_NOTHING,"",0, M_DirSelect,'5'},
3791     {IT_CALL | IT_NOTHING,"",0, M_DirSelect,'6'}
3792 };
3793 
3794 menu_t  DirDef =
3795 {
3796 //    "M_LOADG",	// LOAD GAME, really need SELECT DIR
3797     NULL,
3798     "Game Directory",
3799     LoadDirMenu,
3800     M_DrawDir,
3801     NULL,
3802     NUM_DIRLINE+1,
3803 //    80,54,
3804     (176-(SAVELINELEN*8/2)), 54-LINEHEIGHT,
3805     0
3806 };
3807 
3808 
3809 // Draw the current DIR line above list
draw_dir_line(int line_y)3810 static void draw_dir_line( int line_y )
3811 {
3812     V_DrawString( DirDef.x, line_y, 0, "DIR");
3813     M_Draw_SaveLoadBorder( DirDef.x+32, line_y, 0);
3814     V_DrawString( DirDef.x+32, line_y, 0, savegamedir);
3815 }
3816 
3817 // Draw the dir list and DIR line
M_DrawDir(void)3818 void M_DrawDir(void)
3819 {
3820     int i;
3821     int line_y = DirDef.y;
3822 
3823     M_DrawGenericMenu();
3824 
3825     if (edit_enable)
3826     {
3827         // draw string and cursor in the edit position
3828         V_DrawString( DirDef.x, line_y, 0, "NEW DIR");
3829         int line_x = DirDef.x+64;
3830         M_Draw_SaveLoadBorder( line_x, line_y, 0);
3831         V_DrawString( line_x, line_y, 0, edit_buffer);
3832         i = V_StringWidth(edit_buffer);
3833         V_DrawString( line_x + i, line_y, 0, "_");
3834         return;
3835     }
3836 
3837     // Draw non-edit directory listing
3838     draw_dir_line( line_y );
3839     for (i = 0; i < NUM_DIRLINE; i++)
3840     {
3841         line_y += LINEHEIGHT;
3842         M_Draw_SaveLoadBorder( DirDef.x, line_y, 0);
3843         V_DrawString( DirDef.x, line_y, 0, savegamedisp[i].desc);
3844     }
3845     // Put some message in the UP-TO-LEGACY dir entry.
3846     // The actual dir name remains blank.
3847     if( scroll_index == 0 )
3848     {
3849         V_DrawString( DirDef.x, DirDef.y+LINEHEIGHT, 0, "..");
3850     }
3851 }
3852 
3853 void M_ReadSaveStrings( int scroll_direction );
3854 
3855 // Called from DIR game menu to select a directory
M_DirSelect(int choice)3856 void M_DirSelect(int choice)
3857 {
3858     // LoadDirMenu: slots 0..5 are menu 1..6
3859     int sgslot = choice - 1;
3860     if( (scroll_index == 0 && choice == 1) // UP-TO-LEGACY dir
3861         || ( savegamedisp[sgslot].desc[0] != '\0' ) )  // existing dir
3862     {
3863         // Existing directory selected
3864         strcpy( savegamedir, savegamedisp[sgslot].desc );
3865         scroll_index = 0; // start at top of directory
3866 //        DirDef.prevMenu->lastOn = 1;
3867         menustack[menucnt-1]->lastOn = 1;
3868         Pop_Menu();
3869     }
3870     else if( slotindex )
3871     {
3872         // empty entry (other than UP entry), then make new directory
3873         M_DirEnter(0);  // calls back here, M_DirSelect( 1 )
3874     }
3875 }
3876 
3877 // Callback after editing new directory name, setup by M_DirEnter
M_NewDir(void)3878 void M_NewDir( void )
3879 {
3880     char dirname[256];
3881 
3882     // normal savegame select, set savegamedir
3883     M_DirSelect( 1 ); // LoadDirMenu: slot=0 is menu 1
3884     if( savegamedir[0] )
3885     {
3886         // make new directory
3887         snprintf( dirname, 255, "%s%s", legacyhome, savegamedir );
3888         dirname[255] = '\0';
3889         I_mkdir( dirname, 0700 ); // octal permissions
3890     }
3891 }
3892 
3893 
3894 // Called from DIR game menu to select a directory
M_DirEnter(int choice)3895 void M_DirEnter(int choice)
3896 {
3897     slotindex = 0; // edit
3898     // initiate edit of dir string, we are going to be intercepting all chars
3899     edit_enable = 1;
3900     edit_buffer[0] = '\0';
3901     edit_index = 0;
3902     edit_done_callback = M_NewDir;
3903     // when done editing, will goto M_NewDir
3904 }
3905 
3906 void M_Dir_scroll (int amount);
3907 
3908 // Called from DIR game menu to delete a directory
M_Dir_delete(int ch)3909 void M_Dir_delete (int ch)
3910 {
3911     if( ch=='y' && savegamedisp[slotindex].desc[0] )
3912     {
3913         char dirname[256];
3914         // if is current directory
3915         if( strcmp( savegamedir, savegamedisp[slotindex].desc ) == 0 )
3916         {
3917             savegamedir[0] = '\0';
3918         }
3919         // remove directory
3920         snprintf( dirname, 255, "%s%s", legacyhome, savegamedisp[slotindex].desc );
3921         dirname[255] = '\0';
3922         remove( dirname );
3923         savegamedisp[slotindex].desc[0] = '\0';
3924     }
3925     // fixup after the message undo, which does not record callbacks
3926     M_StopMessage(0);
3927     scroll_callback = M_Dir_scroll;
3928     delete_callback = M_Dir_delete;
3929 }
3930 
3931 
3932 // [smite] MinGW compatibility
3933 #ifndef WIN32
3934 #define USE_FTW
3935 #endif
3936 
3937 #ifdef USE_FTW
3938 #include <ftw.h>
3939 // Callback from ftw system call
ftw_directory_entry(const char * file,const struct stat * sb,int flag)3940 int  ftw_directory_entry( const char *file, const struct stat * sb, int flag )
3941 {
3942     if( flag == FTW_D )  // only want directories
3943     {
3944         if( slotindex >= 0 )  // because of dir list scrolling
3945         {
3946             // Only want the name after legacyhome
3947             strncpy( savegamedisp[slotindex].desc, &file[legacyhome_len], SAVESTRINGSIZE-1 );
3948             savegamedisp[slotindex].desc[SAVESTRINGSIZE-1] = '\0';
3949         }
3950         slotindex++;
3951     }
3952     if( slotindex >= NUM_DIRLINE )  return 1;  // done, stop ftw
3953     return 0;
3954 }
3955 
3956 #else
3957 
3958 #include <sys/types.h>
3959 #include <dirent.h>
3960 
3961 #ifndef _DIRENT_HAVE_D_TYPE
3962 #include <sys/stat.h>
3963 #include "m_misc.h"
3964 #endif
3965 
3966 #endif
3967 
3968 // Get directories into savegamedisp, starting at skip_count.
get_directory_entries(int skip_count)3969 void  get_directory_entries( int skip_count )
3970 {
3971 #ifdef USE_FTW
3972     // Use ftw
3973     slotindex = -skip_count;
3974     ftw( legacyhome, ftw_directory_entry, 1 );
3975 #else
3976     // Use dirent
3977 
3978 #ifndef _DIRENT_HAVE_D_TYPE
3979     // Have to use stat to identify directories.
3980     char dentfile[MAX_WADPATH];
3981     struct stat dentstat;
3982 #endif
3983 
3984     struct dirent * dent;
3985     DIR * legdir;
3986 
3987     legdir = opendir( legacyhome );
3988     if( legdir == NULL )  return;
3989 
3990     slotindex = -skip_count;
3991     for (;;)
3992     {
3993         dent = readdir( legdir );  // Read directory entry
3994         if( dent == NULL )  break;
3995         // Ignore the self reference.
3996         if( strcmp( dent->d_name, "." ) == 0 )   continue;
3997 #ifdef _DIRENT_HAVE_D_TYPE
3998         // Unix systems have the D_TYPE, but others are unlikely.
3999         if( dent->d_type != DT_DIR )  continue;  // Only want directories
4000 #else
4001         // Get status to check if is a directory.
4002         cat_filename( dentfile, legacyhome, dent->d_name );
4003         stat( dentfile, &dentstat );
4004         if( ! S_ISDIR( dentstat.st_mode ))  continue;  // Only want directories
4005 #endif
4006 
4007         if( slotindex >= 0 )  // because of dir list scrolling
4008         {
4009             // Only want the name after legacyhome
4010             strncpy( savegamedisp[slotindex].desc, dent->d_name, SAVESTRINGSIZE-1 );
4011             savegamedisp[slotindex].desc[SAVESTRINGSIZE-1] = '\0';
4012             // The up-dir is passed as an empty string.
4013             if( strcmp( savegamedisp[slotindex].desc, ".." ) == 0 )
4014                 savegamedisp[slotindex].desc[0] = 0;
4015         }
4016         slotindex++;
4017         if( slotindex >= NUM_DIRLINE )  break;  // full
4018     }
4019     closedir( legdir );
4020 #endif
4021 }
4022 
4023 
M_Dir_scroll(int amount)4024 void M_Dir_scroll (int amount)
4025 {
4026     // Do not scroll if at end of list
4027     if( (amount > 0) && ( savegamedisp[SAVEGAME_NUM_MSLOT-1].desc[0] == '\0' ))
4028         return;  // at end of dir list
4029 
4030     clear_remaining_savegamedisp( 0 );
4031     // countdown reading dir entries
4032     scroll_index += amount;
4033     if( scroll_index < 0 )   scroll_index = 0;
4034     get_directory_entries( scroll_index );
4035 }
4036 
4037 // Called from menu
M_Get_SaveDir(int choice)4038 void M_Get_SaveDir (int choice)
4039 {
4040     // Any mode, directory is personal choice
4041     // Directory menu with choices
4042     Push_Setup_Menu(&DirDef);
4043     scroll_callback = M_Dir_scroll;
4044     delete_callback = M_Dir_delete;
4045 
4046     clear_remaining_savegamedisp( 0 );
4047     scroll_index = 0;  // start at top of dir list
4048     get_directory_entries( 0 );
4049 }
4050 
4051 #endif
4052 
4053 //===========================================================================
4054 //LOAD GAME MENU
4055 //===========================================================================
4056 void M_Draw_Loadgame(void);
4057 
4058 void M_LoadSelect(int choice);
4059 
4060 // SAVEGAME_NUM_MSLOT dependent
4061 #ifdef SAVEGAMEDIR
4062 #define SAVEGAME_MSLOT_0      1
4063 #define SAVEGAME_MSLOT_LAST   6
4064 #else
4065 #define SAVEGAME_MSLOT_0      0
4066 #define SAVEGAME_MSLOT_LAST   5
4067 #endif
4068 
4069 
4070 // Has SAVEGAME_NUM_MSLOT entries, and dir entry
4071 menuitem_t LoadgameMenu[]=
4072 {
4073 #ifdef SAVEGAMEDIR
4074     {IT_CALL | IT_NOTHING,"",0, M_Get_SaveDir,'/'},
4075 #endif
4076     {IT_CALL | IT_NOTHING,"",0, M_LoadSelect,'1'},
4077     {IT_CALL | IT_NOTHING,"",0, M_LoadSelect,'2'},
4078     {IT_CALL | IT_NOTHING,"",0, M_LoadSelect,'3'},
4079     {IT_CALL | IT_NOTHING,"",0, M_LoadSelect,'4'},
4080     {IT_CALL | IT_NOTHING,"",0, M_LoadSelect,'5'},
4081     {IT_CALL | IT_NOTHING,"",0, M_LoadSelect,'6'}
4082 };
4083 
4084 menu_t  LoadDef =
4085 {
4086     "M_LOADG",
4087     "Load Game",
4088     LoadgameMenu,
4089     M_Draw_Loadgame,
4090     NULL,
4091     sizeof(LoadgameMenu)/sizeof(menuitem_t),
4092 //    80,54,
4093 #ifdef SAVEGAMEDIR
4094     (176-(SAVELINELEN*8/2)),54-LINEHEIGHT,
4095 #else
4096     (176-(SAVELINELEN*8/2)),54,
4097 #endif
4098     0
4099 };
4100 
4101 //
4102 // M_Loadgame & Cie.
4103 //
M_Draw_Loadgame(void)4104 void M_Draw_Loadgame(void)
4105 {
4106     int i;
4107     int line_y = LoadDef.y;
4108 
4109     M_DrawGenericMenu();
4110 
4111 #ifdef SAVEGAMEDIR
4112     draw_dir_line( line_y );
4113     for (i = 0; i < SAVEGAME_NUM_MSLOT; i++)
4114     {
4115         line_y += LINEHEIGHT;
4116         M_Draw_SaveLoadBorder( LoadDef.x, line_y, 1);
4117 #ifdef SAVEGAME_MTLEFT
4118         V_DrawString( LoadDef.x, line_y, 0, savegamedisp[i].levtime);
4119         V_DrawString( LoadDef.x+SAVE_DESC_XPOS, line_y, 0, savegamedisp[i].desc);
4120 #else
4121         V_DrawString( LoadDef.x, line_y, 0, savegamedisp[i].desc);
4122         V_DrawString( LoadDef.x+(SAVE_MT_POS*8), line_y, 0, savegamedisp[i].levtime);
4123 #endif
4124     }
4125 #else
4126     for (i = 0; i < SAVEGAME_NUM_MSLOT; i++)
4127     {
4128         M_Draw_SaveLoadBorder( LoadDef.x, line_y, 0);
4129 #ifdef SAVEGAME_MTLEFT
4130         V_DrawString( LoadDef.x, line_y, 0, savegamedisp[i].levtime);
4131         V_DrawString( LoadDef.x+SAVE_DESC_XPOS, line_y, 0, savegamedisp[i].desc);
4132 #else
4133         V_DrawString( LoadDef.x, line_y, 0, savegamedisp[i].desc);
4134         V_DrawString( LoadDef.x+(SAVE_MT_POS*8), line_y, 0, savegamedisp[i].levtime);
4135 #endif
4136         line_y += LINEHEIGHT;
4137     }
4138 #endif
4139 }
4140 
4141 //
4142 // User wants to load this game
4143 //
4144 // Called from load game menu to load selected save game
M_LoadSelect(int choice)4145 void M_LoadSelect(int choice)
4146 {
4147     // Issue command to save game
4148     // SAVEGAMEDIR: slots 0..5 are menu 1..6
4149     short sgslot = choice - SAVEGAME_MSLOT_0;
4150 #ifdef SAVEGAME99
4151     if( savegamedisp[sgslot].savegameid <= 99 )
4152       G_Load_Game ( savegamedisp[sgslot].savegameid );  // slot id
4153 #else
4154     G_Load_Game (sgslot);
4155 #endif
4156     M_Clear_Menus (true);
4157 }
4158 
4159 
4160 //
4161 // M_ReadSaveStrings
4162 //  read the strings from the savegame files
4163 //  and put it in savegame global variable
4164 //
4165 #ifdef SAVEGAME99
M_ReadSaveStrings(int scroll_direction)4166 void M_ReadSaveStrings( int scroll_direction )
4167 {
4168     // [WDJ] saves considerable size and hassle having this test here
4169     boolean skip_unloadable = (currentMenu != &SaveDef);
4170     int     sgslot, nameid, slot_status, i;
4171     int     first_nameid = 0;
4172     int     last_nameid = 0;  // disable unless searching
4173     int     handle;
4174     char  * slot_str;
4175     savegame_disp_t *sgdp;
4176     savegame_info_t  sginfo;
4177     char    name[256];
4178 
4179     P_Alloc_savebuffer( 0 );  // header only
4180     // savegamedisp is statically alloc
4181 
4182     if( scroll_direction < 0 )
4183     {
4184         // Because unused slots are skipped, cannot predict what id will be
4185         // at top when paging backwards, so must start from 0 and read
4186         // forward (while scrolling) until the last_nameid test is statisfied.
4187         // The top of previous display will be the last item in next display.
4188         if((scroll_index > 0) && (savegamedisp[0].savegameid <= 99))
4189            last_nameid = savegamedisp[0].savegameid;
4190         scroll_index = first_nameid = 0;
4191     }
4192     else if( scroll_direction > 0 )
4193     {
4194         // The bottom of previous display becomes top of next display.
4195         if( savegamedisp[SAVEGAME_NUM_MSLOT-1].savegameid <= 99 )
4196            first_nameid = savegamedisp[SAVEGAME_NUM_MSLOT-1].savegameid;
4197         else if( savegamedisp[0].savegameid <= 99 )
4198            first_nameid = savegamedisp[0].savegameid;
4199         scroll_index = first_nameid;
4200     }
4201     else
4202     {
4203         // no scroll
4204         if( scroll_index >= 0 && scroll_index <= 99 )
4205         {
4206             // redisplay from last usage
4207             first_nameid = scroll_index;
4208         }
4209     }
4210 
4211     // read savegame headers into savegame slots 0..5
4212     sgslot = 0;
4213     for (nameid = first_nameid; nameid <= 99; nameid++)
4214     {
4215         if( sgslot >= SAVEGAME_NUM_MSLOT ) break;
4216         sgdp = &savegamedisp[sgslot];
4217         sgdp->levtime[0] = '\0';
4218 
4219         G_Savegame_Name( name, nameid );
4220 
4221         handle = open (name, O_RDONLY | 0, 0666);
4222         if (handle == -1)
4223         {
4224             // read error
4225             if( skip_unloadable )  continue;
4226             slot_str = EMPTYSTRING;
4227             sprintf( &sgdp->levtime[0], "%2i", nameid );
4228             slot_status = IT_SPACE | IT_NOTHING;
4229         }
4230         else
4231         {
4232             // read the savegame header and react
4233             read( handle, savebuffer, savebuffer_size );
4234             close (handle);
4235             if( P_Savegame_Read_header( &sginfo, 0 ) )
4236             {
4237                 if( sginfo.map == NULL ) sginfo.map = " -  ";
4238                 if( sginfo.levtime == NULL ) sginfo.levtime = "";
4239                 // info from a valid legacy save game
4240                 snprintf( &sgdp->levtime[0], SAVEGAME_MTLEN,
4241                           "%s %s", sginfo.map, sginfo.levtime);
4242                 sgdp->levtime[SAVEGAME_MTLEN-1] = '\0';
4243                 slot_str = sginfo.name;
4244                 slot_status = IT_CALL | IT_NOTHING | 1;
4245             }
4246             else
4247             {
4248                 // bad header, not a valid legacy savegame, or an old one
4249                 if( skip_unloadable )  continue;
4250                 slot_str = sginfo.msg;	// error message
4251                 slot_status = IT_SPACE | IT_NOTHING;
4252             }
4253         }
4254         // fill in savegame strings for menu display
4255         strncpy( &sgdp->desc[0], slot_str, SAVESTRINGSIZE );
4256         sgdp->desc[SAVESTRINGSIZE-1] = '\0';
4257         sgdp->levtime[SAVEGAME_MTLEN-1] = '\0';
4258         sgdp->savegameid = nameid;
4259         LoadgameMenu[sgslot + SAVEGAME_MSLOT_0].status = slot_status;
4260         sgslot++; // uses savegamedisp[0..5]
4261         // When scroll_direction < 0 only, until last test satisfied
4262         if((sgslot >= SAVEGAME_NUM_MSLOT) && (nameid < last_nameid))
4263         {
4264             // Display is full and still searching for last_nameid.
4265             // Scroll them to make room at last slot for next read
4266 
4267             // [WDJ] Fix bug: upon scroll down and up, EMPTY SLOT message remained
4268             // Must scroll the status too.
4269             for( i = SAVEGAME_MSLOT_0; i<SAVEGAME_MSLOT_LAST; i++ )
4270                LoadgameMenu[i].status = LoadgameMenu[i+1].status;
4271             LoadgameMenu[SAVEGAME_MSLOT_LAST].status = IT_SPACE | IT_NOTHING;
4272 
4273             memmove( &savegamedisp[0], &savegamedisp[1],
4274                          sizeof( savegame_disp_t ) * (SAVEGAME_NUM_MSLOT-1));
4275             sgslot --;  // read one more, come back here
4276             scroll_index = savegamedisp[0].savegameid;
4277         }
4278     }
4279     free( savebuffer );
4280 
4281     clear_remaining_savegamedisp( sgslot );  // if sgslot < 5, upto [5]
4282     // clear remaining menu slot status, to prevent attempts to load
4283     for( i = sgslot+SAVEGAME_MSLOT_0; i<=SAVEGAME_MSLOT_LAST; i++ )
4284        LoadgameMenu[i].status = IT_SPACE | IT_NOTHING;
4285 }
4286 
4287 #else
4288 
M_ReadSaveStrings(void)4289 void M_ReadSaveStrings(void)
4290 {
4291     int     handle;
4292     int     i;
4293     savegame_disp_t *sgdp;
4294     savegame_info_t  sginfo;
4295     char    name[256];
4296 
4297     P_Alloc_savebuffer( 0 );  // header only
4298     // savegamedisp is statically alloc
4299 
4300     for (i = 0; i < SAVEGAME_NUM_MSLOT; i++)
4301     {
4302         sgdp = &savegamedisp[i];
4303         sgdp->levtime[0] = '\0';
4304 
4305         G_Savegame_Name( name, i );
4306 
4307         handle = open (name, O_RDONLY | 0, 0666);
4308         if (handle == -1)
4309         {
4310             // read error
4311             strcpy(&sgdp->desc[0], EMPTYSTRING);
4312             LoadgameMenu[i].status = IT_SPACE | IT_NOTHING;
4313             continue;
4314         }
4315         read( handle, savebuffer, savebuffer_size );
4316         close (handle);
4317         if( P_Read_Savegame_Header( &sginfo ) )
4318         {
4319             // info from a valid legacy save game
4320             strncpy( &sgdp->desc[0], sginfo.name, SAVESTRINGSIZE );
4321             if( sginfo.map == NULL ) sginfo.map = " -  ";
4322             if( sginfo.levtime == NULL ) sginfo.levtime = "";
4323             snprintf( &sgdp->levtime[0], SAVEGAME_MTLEN,
4324                       "%s %s", sginfo.map, sginfo.levtime);
4325             sgdp->levtime[SAVEGAME_MTLEN-1] = '\0';
4326             LoadgameMenu[i].status = IT_CALL | IT_NOTHING | 1;
4327         }
4328         else
4329         {
4330             strncpy( &sgdp->desc[0], sginfo.msg, SAVESTRINGSIZE );
4331             LoadgameMenu[i].status = IT_SPACE | IT_NOTHING;
4332         }
4333         sgdp->desc[SAVESTRINGSIZE-1] = '\0';
4334     }
4335     free( savebuffer );
4336 }
4337 #endif
4338 
4339 
4340 #ifdef SAVEGAME99
4341 // scroll_callback
M_Savegame_scroll(int amount)4342 void M_Savegame_scroll (int amount)
4343 {
4344     M_ReadSaveStrings( amount ); // skip unloadable
4345 }
4346 
4347 //void M_Save_scroll (int amount);
4348 
4349 // delete_callback
M_Savegame_delete(int ch)4350 void M_Savegame_delete (int ch)
4351 {
4352     if( ch=='y' && (savegamedisp[slotindex].savegameid <= 99) )
4353     {
4354         char savename[256];
4355         G_Savegame_Name( savename, savegamedisp[slotindex].savegameid );
4356         // remove savegame
4357         remove( savename );
4358         savegamedisp[slotindex].desc[0] = '\0';
4359         savegamedisp[slotindex].levtime[0] = '\0';
4360         savegamedisp[slotindex].savegameid = 255;
4361         // slot no longer loadable
4362         LoadgameMenu[slotindex + SAVEGAME_MSLOT_0].status = IT_SPACE | IT_NOTHING;
4363     }
4364     // fixup after the message undo, which does not record callbacks
4365     M_StopMessage(0);
4366     scroll_callback = M_Savegame_scroll;
4367     delete_callback = M_Savegame_delete;
4368 }
4369 #endif
4370 
4371 //
4372 // Selected from DOOM menu
4373 //
4374 // Called from menu, and key F3
M_Loadgame(int choice)4375 void M_Loadgame (int choice)
4376 {
4377 // change can't load message to can't load in server mode
4378     if (netgame && !server)
4379     {
4380         // running network game and am not the server, cannot load
4381         M_SimpleMessage(LOADNET);
4382         return;
4383     }
4384 
4385     // Save game load menu with slot choices
4386     Push_Setup_Menu(&LoadDef);
4387 #ifdef SAVEGAME99
4388     scroll_callback = M_Savegame_scroll;
4389     delete_callback = M_Savegame_delete;
4390     M_ReadSaveStrings( 0 ); // skip unloadable
4391 #else
4392     M_ReadSaveStrings();
4393 #endif
4394 }
4395 
4396 
4397 //===========================================================================
4398 //                                SAVE GAME MENU
4399 //===========================================================================
4400 void M_Draw_Savegame(void);
4401 
4402 void M_SaveSelect(int choice);
4403 
4404 // Must have SAVEGAME_NUM_MSLOT entries, plus dir
4405 menuitem_t SavegameMenu[]=
4406 {
4407 #ifdef SAVEGAMEDIR
4408     {IT_CALL | IT_NOTHING,"",0, M_Get_SaveDir,'/'},
4409 #endif
4410     {IT_CALL | IT_NOTHING,"",0, M_SaveSelect,'1'},
4411     {IT_CALL | IT_NOTHING,"",0, M_SaveSelect,'2'},
4412     {IT_CALL | IT_NOTHING,"",0, M_SaveSelect,'3'},
4413     {IT_CALL | IT_NOTHING,"",0, M_SaveSelect,'4'},
4414     {IT_CALL | IT_NOTHING,"",0, M_SaveSelect,'5'},
4415     {IT_CALL | IT_NOTHING,"",0, M_SaveSelect,'6'}
4416 };
4417 
4418 menu_t  SaveDef =
4419 {
4420     "M_SAVEG",
4421     "Save Game",
4422     SavegameMenu,
4423     M_Draw_Savegame,
4424     NULL,
4425     sizeof(SavegameMenu)/sizeof(menuitem_t),
4426 //    80,54,
4427 #ifdef SAVEGAMEDIR
4428     (176-(SAVELINELEN*8/2)),54-LINEHEIGHT,
4429 #else
4430     (176-(SAVELINELEN*8/2)),54,
4431 #endif
4432     0
4433 };
4434 
4435 
4436 
4437 //
4438 // Draw border for the savegame description
4439 //
4440 static
M_Draw_SaveLoadBorder(int x,int y,boolean longer)4441 void M_Draw_SaveLoadBorder(int x, int y, boolean longer )
4442 {
4443     int i;
4444 
4445     // Draw to screen0, scaled
4446     if( gamemode == heretic )
4447     {
4448 #ifdef SAVEGAME_MTLEFT
4449         V_DrawScaledPatch_Name(x-8, y-4, "M_FSLOT");
4450 #if SAVELINELEN > 24
4451         if( longer )
4452         {
4453             V_DrawScaledPatch_Name(x-8 + SAVE_DESC_XPOS, y-4, "M_FSLOT");
4454         }
4455 #endif
4456 #else
4457 #if SAVELINELEN > 24
4458         if( longer )
4459         {
4460             V_DrawScaledPatch_Name(x-8 + ((SAVELINELEN-24)*8), y-4, "M_FSLOT");
4461         }
4462 #endif
4463         V_DrawScaledPatch_Name(x-8, y-4, "M_FSLOT");
4464 #endif
4465     }
4466     else
4467     {
4468         V_DrawScaledPatch_Name (x-8,y+7, "M_LSLEFT");
4469 
4470         for (i = (longer?SAVELINELEN:SAVESTRINGSIZE); i>0; i--)
4471         {
4472             V_DrawScaledPatch_Name (x,y+7, "M_LSCNTR");
4473             x += 8;
4474         }
4475 
4476         V_DrawScaledPatch_Name (x,y+7, "M_LSRGHT");
4477     }
4478 }
4479 
4480 
4481 //
4482 //  M_Savegame & Cie.
4483 //
M_Draw_Savegame(void)4484 void M_Draw_Savegame(void)
4485 {
4486     int line_y = LoadDef.y;
4487 
4488     if (edit_enable)
4489     {
4490 //        M_Draw_Loadgame();	// optional, keep other slots displayed
4491         // draw string and cursor in the original slot position
4492 #ifdef SAVEGAMEDIR
4493         line_y = LoadDef.y+(LINEHEIGHT*slotindex)+LINEHEIGHT; // dir is 0
4494 #else
4495         line_y = LoadDef.y+LINEHEIGHT*slotindex;
4496 #endif
4497         M_Draw_SaveLoadBorder( LoadDef.x, line_y, 1);
4498 #ifdef SAVEGAME_MTLEFT
4499         V_DrawString( LoadDef.x, line_y, 0, "DESCRIPTION:");
4500         V_DrawString( LoadDef.x+SAVE_DESC_XPOS, line_y, 0, edit_buffer);
4501         int i = V_StringWidth(edit_buffer);
4502         V_DrawString( LoadDef.x+SAVE_DESC_XPOS + i, line_y, 0, "_");
4503 #else
4504         V_DrawString( LoadDef.x, line_y, 0, edit_buffer);
4505         int i = V_StringWidth(edit_buffer);
4506         V_DrawString( LoadDef.x + i, line_y, 0, "_");
4507 #endif
4508     }
4509     else
4510     {
4511         M_Draw_Loadgame();
4512     }
4513 }
4514 
4515 //
4516 // M_Responder calls this when user is finished
4517 //
4518 // Called from save menu by M_Responder,
4519 // and from quick Save by M_QuickSaveResponse
4520 #if defined SAVEGAMEDIR || defined SAVEGAME99
4521 // slti = savegame index 0..5, or quicksave 6
M_DoSavegame(int slti)4522 void M_DoSavegame(int slti)
4523 {
4524     if( savegamedisp[slti].savegameid > 99 )
4525         return;
4526     // Issue command to save game
4527     G_Save_Game (savegamedisp[slti].savegameid, savegamedisp[slti].desc);
4528     M_Clear_Menus (true);
4529 
4530     // PICK QUICKSAVE SLOT YET?
4531     if (quicksave_slotid == -2)
4532     {
4533         quicksave_slotid = savegamedisp[slti].savegameid;  // 0..99
4534         savegamedisp[QUICKSAVE_INDEX] = savegamedisp[slti];  // save whole thing
4535     }
4536 }
4537 #else
4538 // slot = game id and menu index 0..5
M_DoSavegame(int slot)4539 void M_DoSavegame(int slot)
4540 {
4541     // Issue command to save game
4542     G_Save_Game (slot, savegamedisp[slot].desc);
4543     M_Clear_Menus (true);
4544 
4545     // PICK QUICKSAVE SLOT YET?
4546     if (quicksave_slotid == -2)
4547     {
4548         quicksave_slotid = slot;
4549     }
4550 }
4551 #endif
4552 
4553 
4554 
4555 // Called when desc editing is done
M_SaveEditDone(void)4556 void M_SaveEditDone( void )
4557 {
4558     M_DoSavegame(slotindex);  // index 0..5
4559 }
4560 
4561 //
4562 // User wants to save. Start string input for M_Responder
4563 //
4564 // Called from save game menu to select save game
M_SaveSelect(int choice)4565 void M_SaveSelect(int choice)
4566 {
4567 #ifdef SAVEGAMEDIR
4568     slotindex = choice - SAVEGAME_MSLOT_0; // menu 1..6 -> index 0..5
4569 #else
4570     slotindex = choice;  // line being edited  0..5
4571 #endif
4572     if( savegamedisp[slotindex].savegameid > 99 )
4573         return;
4574     // clear out EMPTY STRING and other err msgs
4575     // LoadSaveStrings puts existing status in LoadgameMenu, MSLOT index
4576     if ( (LoadgameMenu[choice].status & 1) != 1 )  // invalid name
4577         savegamedisp[slotindex].desc[0] = 0;
4578     // [WDJ] edit_enable overwrites entire line
4579     // initiate edit of desc string, we are going to be intercepting all chars
4580     strcpy(edit_buffer, savegamedisp[slotindex].desc);
4581     edit_index = strlen(edit_buffer);
4582     edit_done_callback = M_SaveEditDone;
4583     edit_enable = 1;
4584 }
4585 
4586 
4587 //
4588 // Selected from DOOM menu
4589 //
4590 // Called from menu, and key F2, and quicksave
M_Savegame(int choice)4591 void M_Savegame (int choice)
4592 {
4593     if(demorecording)
4594     {
4595         M_SimpleMessage("You cannot save while recording demos\n\nPress a key\n");
4596         return;
4597     }
4598 
4599     if (demoplayback || demorecording)
4600     {
4601         M_SimpleMessage(SAVEDEAD);
4602         return;
4603     }
4604 
4605     if (gamestate != GS_LEVEL)
4606         return;
4607 
4608     if (netgame && !server)
4609     {
4610         M_SimpleMessage("You are not the server");
4611         return;
4612     }
4613 
4614     // Save game menu with slot choices
4615     Push_Setup_Menu(&SaveDef);
4616 #ifdef SAVEGAME99
4617     delete_callback = M_Savegame_delete;
4618     scroll_callback = M_Savegame_scroll;
4619     M_ReadSaveStrings( 0 ); // show unloadable
4620 #else
4621     M_ReadSaveStrings();
4622 #endif
4623 }
4624 
4625 //===========================================================================
4626 //                            QuickSAVE & QuickLOAD
4627 //===========================================================================
4628 
4629 //
4630 // M_QuickSave
4631 //
4632 
4633 // Handles quick save ack from M_QuickSave, and initiates the save.
M_QuickSaveResponse(int ch)4634 void M_QuickSaveResponse(int ch)
4635 {
4636     if (ch == 'y')
4637     {
4638         M_DoSavegame( QUICKSAVE_INDEX ); // initiate game save, network message
4639         S_StartSound(menu_sfx_action);
4640     }
4641     else
4642     {
4643         // response was "No"
4644         // Give opportunity to pick a new slot
4645         quicksave_slotid = -2;     // means to pick a slot now
4646         M_Savegame( -2 );
4647     }
4648 }
4649 
4650 // Invoked by key F6
M_QuickSave(void)4651 void M_QuickSave(void)
4652 {
4653     if (demoplayback || demorecording)
4654     {
4655         S_StartSound(sfx_oof);
4656         return;
4657     }
4658 
4659     if (gamestate != GS_LEVEL)
4660         return;
4661 
4662     if (quicksave_slotid < 0)   goto pick_slot; // No slot yet.
4663     // Show save name, ask for quick save ack.
4664     snprintf(msgtmp, MSGTMP_LEN, QSPROMPT, savegamedisp[QUICKSAVE_INDEX].desc);
4665     msgtmp[MSGTMP_LEN-1] = '\0';
4666     M_StartMessage(msgtmp, M_QuickSaveResponse, MM_YESNO);
4667     return;
4668 
4669 pick_slot:
4670     // have not selected a quick save slot yet
4671     M_StartControlPanel();
4672     quicksave_slotid = -2;     // signal to save as a quicksave slot
4673     M_Savegame( -2 );
4674     return;
4675 }
4676 
4677 
4678 
4679 //
4680 // M_QuickLoad
4681 //
4682 // Handles quick load ack from M_QuickLoad, and initiates the save.
M_QuickLoadResponse(int ch)4683 void M_QuickLoadResponse(int ch)
4684 {
4685     if (ch == 'y')
4686     {
4687         // quicksave_slotid is known valid, slot id
4688         G_Load_Game( quicksave_slotid ); // initiate game load, network message
4689         M_Clear_Menus (true);
4690         S_StartSound(menu_sfx_action);
4691     }
4692 }
4693 
4694 
M_QuickLoad(void)4695 void M_QuickLoad(void)
4696 {
4697     if (netgame)
4698     {
4699         M_SimpleMessage(QLOADNET);
4700         return;
4701     }
4702 
4703     if (quicksave_slotid < 0)
4704     {
4705         // No save slot selected
4706         M_SimpleMessage(QSAVESPOT);
4707         return;
4708     }
4709     // Show load name, ask for quick load ack.
4710     snprintf(msgtmp, MSGTMP_LEN, QLPROMPT, savegamedisp[QUICKSAVE_INDEX].desc);
4711     msgtmp[MSGTMP_LEN-1] = '\0';
4712     M_StartMessage(msgtmp, M_QuickLoadResponse, MM_YESNO);
4713 }
4714 
4715 
4716 //===========================================================================
4717 //                                 END GAME
4718 //===========================================================================
4719 
4720 //
4721 // M_EndGame
4722 //
M_EndGameResponse(int ch)4723 void M_EndGameResponse(int ch)
4724 {
4725     if (ch != 'y')
4726         return;
4727 
4728     currentMenu->lastOn = itemOn;
4729     M_Clear_Menus (true);
4730     COM_BufAddText("exitgame\n");
4731 }
4732 
M_EndGame(int choice)4733 void M_EndGame(int choice)
4734 {
4735     choice = 0;
4736     if (demoplayback || demorecording)
4737     {
4738         S_StartSound(sfx_oof);
4739         return;
4740     }
4741 /*
4742     if (netgame)
4743     {
4744         M_SimpleMessage(NETEND);
4745         return;
4746     }
4747 */
4748     M_StartMessage(ENDGAME, M_EndGameResponse, MM_YESNO);
4749 }
4750 
4751 //===========================================================================
4752 //                                 Quit Game
4753 //===========================================================================
4754 
4755 //
4756 // M_QuitDOOM
4757 //
4758 int     quitsounds[8] =
4759 {
4760     sfx_pldeth,
4761     sfx_dmpain,
4762     sfx_popain,
4763     sfx_slop,
4764     sfx_telept,
4765     sfx_posit1,
4766     sfx_posit3,
4767     sfx_sgtatk
4768 };
4769 
4770 int     quitsounds2[8] =
4771 {
4772     sfx_vilact,
4773     sfx_getpow,
4774     sfx_boscub,
4775     sfx_slop,
4776     sfx_skeswg,
4777     sfx_kntdth,
4778     sfx_bspact,
4779     sfx_sgtatk
4780 };
4781 
4782 
4783 
M_QuitResponse(int ch)4784 void M_QuitResponse(int ch)
4785 {
4786     tic_t   dlyd_time;
4787     if (ch != 'y')
4788         return;
4789 
4790     if (!netgame)
4791     {
4792 #ifdef USE_QUITSOUNDS2
4793         //added:12-02-98: quitsounds are much more fun than quisounds2
4794         if (gamemode == doom2_commercial)
4795             S_StartSound(quitsounds2[(gametic>>2)&7]);
4796         else
4797 #endif
4798             S_StartSound(quitsounds[(gametic>>2)&7]);
4799 
4800         //added:12-02-98: do that instead of I_WaitVbl which does not work
4801         if(!nosoundfx)
4802         {
4803             dlyd_time = I_GetTime() + TICRATE*2;
4804             while (dlyd_time > I_GetTime()) ;
4805         }
4806     }
4807     I_Quit();  // No return
4808 }
4809 
4810 
4811 
4812 
M_QuitDOOM(int choice)4813 void M_QuitDOOM(int choice)
4814 {
4815   // We pick index 0 which is language sensitive,
4816   //  or one at random, between 1 and maximum number.
4817   snprintf(msgtmp, MSGTMP_LEN,
4818            text[DOSY_NUM], text[ QUITMSG_NUM+(gametic%NUM_QUITMESSAGES)]);
4819   msgtmp[MSGTMP_LEN-1] = '\0';
4820   M_StartMessage( msgtmp, M_QuitResponse, MM_YESNO);
4821 }
4822 
4823 
4824 //===========================================================================
4825 //                              Some Draw routine
4826 //===========================================================================
4827 
4828 //
4829 //      Menu Functions
4830 //
4831 static
M_DrawThermo(int x,int y,consvar_t * cv)4832 void M_DrawThermo ( int   x,
4833                     int   y,
4834                     consvar_t *cv)
4835 {
4836     int xx,i, cursory;
4837     int leftlump,rightlump,centerlump[2],cursorlump;
4838 
4839     // Draw to screen0, scaled
4840     xx = x;
4841     if( EN_heretic_hexen )
4842     {
4843         xx -= 32-8;
4844         leftlump      = W_GetNumForName("M_SLDLT");
4845         rightlump     = W_GetNumForName("M_SLDRT");
4846         centerlump[0] = W_GetNumForName("M_SLDMD1");
4847         centerlump[1] = W_GetNumForName("M_SLDMD2");
4848         cursorlump    = W_GetNumForName("M_SLDKB");
4849         cursory = y+7;
4850     }
4851     else
4852     {
4853         leftlump      = W_GetNumForName("M_THERML");
4854         rightlump     = W_GetNumForName("M_THERMR");
4855         centerlump[0] = W_GetNumForName("M_THERMM");
4856         centerlump[1] = W_GetNumForName("M_THERMM");
4857         cursorlump    = W_GetNumForName("M_THERMO");
4858         cursory = y;
4859     }
4860     { // temp use of left thermo patch
4861       patch_t *pt = W_CachePatchNum(leftlump,PU_CACHE);  // endian fix
4862       V_DrawScaledPatch (xx,y,pt);
4863       xx += pt->width - pt->leftoffset;  // add width to offset
4864     }
4865     for (i=0;i<16;i++)
4866     {
4867         // Has alternate center patches for heretic, hexen.
4868         V_DrawScaledPatch_Num (xx,y, centerlump[i & 1] );
4869         xx += 8;
4870     }
4871     V_DrawScaledPatch_Num (xx,y, rightlump );
4872 
4873     xx = (cv->value - cv->PossibleValue[0].value) * (15*8) /
4874          (cv->PossibleValue[1].value - cv->PossibleValue[0].value);
4875 
4876     V_DrawScaledPatch_Num ((x+8) + xx, cursory, cursorlump );
4877 }
4878 
4879 
4880 #if 0
4881 static
4882 void M_DrawEmptyCell( menu_t*       menu,
4883                       int           item )
4884 {
4885     V_DrawScaledPatch_Name (menu->x - 10,  menu->y+item*LINEHEIGHT - 1,
4886                        "M_CELL1" );
4887 }
4888 
4889 static
4890 void M_DrawSelCell ( menu_t*       menu,
4891                      int           item )
4892 {
4893     V_DrawScaledPatch_Name (menu->x - 10,  menu->y+item*LINEHEIGHT - 1,
4894                        "M_CELL2" );
4895 }
4896 #endif
4897 
4898 
4899 //
4900 //  Draw a textbox, like Quake does, because sometimes it's difficult
4901 //  to read the text with all the stuff in the background...
4902 //
4903 //added:06-02-98:
4904 //  x, y : position (320,200)
4905 // Called by M_DrawGenericMenu, M_DrawSetupMultiPlayerMenu, M_DrawMessageMenu
4906 // The caller must call V_SetupDraw with V_SCALESTART | V_SCALEPATCH,
4907 // selecting V_CENTERHORZ or V_CENTERMENU, to have consistent positioning.
4908 // V_CENTERMENU has a y shift, which differs from V_CENTERHORZ.
M_DrawTextBox(int x,int y,int width,int lines)4909 void M_DrawTextBox (int x, int y, int width, int lines)
4910 {
4911     fontinfo_t * fip = V_FontInfo();
4912     patch_t  *p;
4913     int      cx, cy;
4914     int      n;
4915     int      step,boff;
4916 
4917     // Draw to screen0, scaled
4918 
4919     if( gamemode == heretic )
4920     {
4921         // humf.. border will stand if we do not adjust size ...
4922         x+=4;
4923         y+=4;
4924         lines = (lines+1)/2;
4925         width = (width+1)/2;
4926         step = 16;
4927         boff = 4; // borderoffset
4928     }
4929     else
4930     {
4931         step = fip->yinc;
4932         boff = 8;
4933     }
4934     if( ! viewborderlump[0] )   goto grey_bar;
4935 
4936     // draw left side
4937     cx = x;
4938     cy = y;
4939     V_DrawScaledPatch_Num (cx, cy, viewborderlump[BRDR_TL] );
4940     cy += boff;
4941 
4942     // temp use patch in loop
4943     p = W_CachePatchNum (viewborderlump[BRDR_L],PU_CACHE);  // endian fix
4944     for (n = 0; n < lines; n++)
4945     {
4946         V_DrawScaledPatch (cx, cy, p);
4947         cy += step;
4948     }
4949 
4950     V_DrawScaledPatch_Num (cx, cy, viewborderlump[BRDR_BL] );
4951 
4952     // draw background
4953     // Flat fill per drawinfo, centering.
4954     // Reduce scale of Doom background to (0..2) so text is easier to read.
4955     V_DrawFlatFill (x+boff, y+boff, width*step, lines*step,
4956                    (EN_heretic_hexen ? 15:0), st_borderflat_num);
4957 
4958     // draw top and bottom
4959     cx += boff;
4960     cy = y;
4961     while (width > 0)
4962     {
4963         V_DrawScaledPatch_Num (cx, cy, viewborderlump[BRDR_T] );
4964 
4965         V_DrawScaledPatch_Num (cx, y+boff+lines*step, viewborderlump[BRDR_B] );
4966         width --;
4967         cx += step;
4968     }
4969 
4970     // draw right side
4971     cy = y;
4972     V_DrawScaledPatch_Num (cx, cy, viewborderlump[BRDR_TR] );
4973     cy += boff;
4974 
4975     // temp use patch in loop
4976     p = W_CachePatchNum (viewborderlump[BRDR_R],PU_CACHE);  // endian fix
4977     for (n = 0; n < lines; n++)
4978     {
4979         V_DrawScaledPatch (cx, cy, p);
4980         cy += step;
4981     }
4982 
4983     V_DrawScaledPatch_Num (cx, cy, viewborderlump[BRDR_BR] );
4984 
4985 
4986   done:
4987     return;
4988 
4989   grey_bar:
4990     // Message box centers string, using 8 bit width assumption.
4991     // For now, correct the position.
4992 //    V_DrawFill(x,y, width*fip->xinc, lines*fip->yinc, ci_grey );
4993     V_DrawFill(x/8, y, width*fip->xinc, lines*fip->yinc, ci_grey );
4994     goto done;
4995 }
4996 
4997 //==========================================================================
4998 //                        Message is now a (hackable) Menu
4999 //==========================================================================
5000 static void M_DrawMessageMenu(void);
5001 static void M_SetupMenu(menu_t *menudef);
5002 
5003 menuitem_t MessageMenu[]=
5004 {
5005     // TO HACK
5006     {0 ,NULL , 0, NULL ,0}
5007 };
5008 
5009 menu_t MessageDef =
5010 {
5011     NULL,               // title
5012     NULL,
5013     MessageMenu,        // menuitem_t ->
5014     M_DrawMessageMenu,  // drawing routine ->
5015     NULL,
5016     sizeof(MessageMenu)/sizeof(menuitem_t),
5017     0,0,                // x,y                 (TO HACK)
5018     0                   // lastOn, flags       (TO HACK)
5019 };
5020 
5021 static menu_t * message_menu_back;
5022 static byte message_lines, message_length;
5023 
M_StartMessage(const char * string,void * routine,menumessagetype_t itemtype)5024 void M_StartMessage ( const char*       string,
5025                       void*             routine,
5026                       menumessagetype_t itemtype )
5027 {
5028     int   maxlen, i, lines;
5029     char * chp;
5030 
5031 #define msgline  MessageDef.menuitems[0]
5032 #define msgtext  msgline.text
5033     msgtmp[MSGTMP_LEN] = '\0';  // make sure it is terminated
5034     if( msgtext )
5035         Z_Free( msgtext );
5036     msgtext = Z_StrDup(string);
5037     DEBFILE(msgtext);
5038 
5039     M_StartControlPanel(); // can't put menuactiv to true
5040     msgline.text     = msgtext;
5041     msgline.alphaKey = itemtype;
5042     switch(itemtype) {
5043         case MM_NOTHING:
5044              msgline.status     = IT_MSGHANDLER;
5045              msgline.itemaction = M_StopMessage;
5046              break;
5047         case MM_YESNO:
5048              msgline.status     = IT_MSGHANDLER;
5049              msgline.itemaction = routine;
5050              break;
5051         case MM_EVENTHANDLER:
5052              msgline.status     = IT_MSGHANDLER;
5053              msgline.itemaction = routine;
5054              break;
5055     }
5056     //added:06-02-98: now draw a textbox around the message
5057     // compute length max and the numbers of lines
5058     maxlen = 4; // minimum box
5059     chp = msgtext;
5060     for (lines=0;  ; lines++)
5061     {
5062         for (i = 0;  ; i++)
5063         {
5064             if (*chp == 0)   // end of line escape
5065                 break;
5066 
5067             if (*chp == '\n')
5068             {
5069                 chp ++;
5070                 break;
5071             }
5072             chp ++;
5073         }
5074         if (i > maxlen)
5075             maxlen = i;
5076         if (*chp == 0)   // end of line counts
5077             break;
5078     }
5079     if((i > 0) || (lines==0))  // missing \n or empty string
5080         lines++;  // count as a line
5081 
5082     MessageDef.x=(BASEVIDWIDTH - (8*maxlen) - 16)/2;
5083     MessageDef.y=(BASEVIDHEIGHT - M_StringHeight(msgtext))/2;
5084 
5085     // [WDJ] lastOn is not large enough for these parameters, nor appropriate.
5086     message_lines = lines;
5087     message_length = maxlen;
5088     message_menu_back = currentMenu;
5089 
5090     currentMenu = &MessageDef;
5091     itemOn=0;
5092 }
5093 
M_SimpleMessage(const char * string)5094 void M_SimpleMessage ( const char * string )
5095 {
5096     M_StartMessage ( string, NULL, MM_NOTHING );
5097 }
5098 
5099 
5100 #define MAXMSGLINELEN 256
5101 
5102 // Called by M_Drawer.
5103 static
M_DrawMessageMenu(void)5104 void M_DrawMessageMenu(void)
5105 {
5106     int    y;
5107     short  i,max;
5108     char   string[MAXMSGLINELEN];
5109     int    start,lines;
5110     char   *msg=currentMenu->menuitems[0].text;
5111 
5112     // Draw to screen0, scaled
5113     // Not part of a menu.
5114     V_SetupDraw( 0 | V_SCALESTART | V_SCALEPATCH | V_CENTERHORZ );
5115 
5116     // Draw to screen0, scaled
5117     y=currentMenu->y;
5118     start = 0;
5119     lines = message_lines;
5120     max = ((int)message_length) * 8;
5121     M_DrawTextBox( currentMenu->x, y-8, (max+7)>>3,lines);
5122 
5123     while(*(msg+start))
5124     {
5125         for (i = 0; i < (int)strlen(msg+start); i++)
5126         {
5127             if (*(msg+start+i) == '\n')
5128             {
5129                 memset(string,0,MAXMSGLINELEN);
5130                 if(i >= MAXMSGLINELEN)
5131                 {
5132                     CONS_Printf("M_DrawMessageMenu: too long segment in %s\n", msg);
5133                     return;
5134                 }
5135                 else
5136                 {
5137                     strncpy(string,msg+start,i);
5138                     start += i+1;
5139                     i = -1; //added:07-02-98:damned!
5140                 }
5141 
5142                 break;
5143             }
5144         }
5145 
5146         if (i == (int)strlen(msg+start))
5147         {
5148             if(i >= MAXMSGLINELEN)
5149             {
5150                 CONS_Printf("M_DrawMessageMenu: too long segment in %s\n", msg);
5151                 return;
5152             }
5153             else
5154             {
5155                 strcpy(string,msg+start);
5156                 start += i;
5157             }
5158         }
5159 
5160         V_DrawString((BASEVIDWIDTH - V_StringWidth(string))/2,y,0,string);
5161         y += 8; //hu_font[0]->height;
5162     }
5163 }
5164 
5165 // default message handler
5166 static
M_StopMessage(int choice)5167 void M_StopMessage(int choice)
5168 {
5169     // Do not interfere with response menu changes
5170     if( (currentMenu == &MessageDef) && message_menu_back )
5171     {
5172          M_SetupMenu(message_menu_back); // NULLS callbacks, caller must fix
5173          S_StartSound(menu_sfx_action);
5174          message_menu_back = NULL;
5175     }
5176 }
5177 
5178 //==========================================================================
5179 //                        Menu stuffs
5180 //==========================================================================
5181 
5182 //added:30-01-98:
5183 //
5184 //  Write a string centered using the hu_font
5185 //
5186 static
M_CentreText(int y,char * string)5187 void M_CentreText (int y, char* string)
5188 {
5189     int x;
5190     //added:02-02-98:centre on 320, because default option is SCALED
5191     x = (BASEVIDWIDTH - V_StringWidth(string))>>1;
5192     V_DrawString(x,y,0,string);
5193 }
5194 
5195 
5196 //
5197 // CONTROL PANEL
5198 //
5199 
5200 static
cvar_incdec(consvar_t * cv,int incr)5201 void  cvar_incdec( consvar_t * cv, int incr )
5202 {
5203 #ifdef CONFIG_MENU_PAGE
5204     cv = config_cvar_edit_open( cv );
5205     if( ! cv )
5206         return;  // cannot edit
5207 #endif
5208 
5209     CV_SetValue( cv, cv->value + incr );
5210 #ifdef CONFIG_MENU_PAGE
5211     config_cvar_edit_save();
5212 #endif
5213 
5214     S_StartSound(menu_sfx_enter);
5215 }
5216 
5217 static
M_Change_cvar_value(int choice)5218 void M_Change_cvar_value(int choice)
5219 {
5220     consvar_t * cv = (consvar_t *)currentMenu->menuitems[itemOn].itemaction;
5221     int d = choice * 2 - 1;  // -1 or +1
5222 
5223 #ifdef CONFIG_MENU_PAGE
5224     cv = config_cvar_edit_open( cv );
5225     if( ! cv )
5226         return;  // cannot edit
5227 #endif
5228 
5229     // Process cvar modification
5230     if(((currentMenu->menuitems[itemOn].status & IT_CVARTYPE) == IT_CV_SLIDER )
5231      ||((currentMenu->menuitems[itemOn].status & IT_CVARTYPE) == IT_CV_NOMOD  ))
5232     {
5233         CV_SetValue(cv, cv->value + d );
5234     }
5235     else
5236     {
5237         if(cv->flags & CV_FLOAT)
5238         {
5239             // This is the string that gets displayed too.
5240             char s[20];
5241             sprintf(s,"%.4f",(float)cv->value/FRACUNIT + d * (1.0/16.0));
5242             CV_Set(cv,s);
5243         }
5244         else
5245             CV_ValueIncDec( cv, d );
5246     }
5247 
5248 #ifdef CONFIG_MENU_PAGE
5249     config_cvar_edit_save();
5250 #endif
5251 }
5252 
5253 
5254 #define MAX_CVAR_STRING  512
5255 
5256 static
M_Change_cvar_string(int key,char ch)5257 boolean M_Change_cvar_string(int key, char ch)
5258 {
5259     consvar_t * cv = (consvar_t *)currentMenu->menuitems[itemOn].itemaction;
5260     char buf[ MAX_CVAR_STRING + 1 ];
5261     int  len;
5262 
5263 #ifdef CONFIG_MENU_PAGE
5264     cv = config_cvar_edit_open( cv );
5265     if( ! cv )
5266         return false;  // cannot edit
5267 #endif
5268 
5269     switch (key)
5270     {
5271       case KEY_BACKSPACE :
5272         len=strlen(cv->string);
5273         if( len > MAX_CVAR_STRING )  len = MAX_CVAR_STRING;
5274         if( len>0 )
5275         {
5276             memcpy(buf,cv->string,len);
5277             buf[len-1]=0;
5278             CV_Set(cv, buf);
5279 #ifdef CONFIG_MENU_PAGE
5280             config_cvar_edit_save();
5281 #endif
5282         }
5283         return true;
5284       default:
5285         if (is_printable(ch))
5286         {
5287             len=strlen(cv->string);
5288             if( len < MAX_CVAR_STRING-1 )
5289             {
5290                 memcpy(buf,cv->string,len);
5291                 buf[len++] = ch;
5292                 buf[len] = 0;
5293                 CV_Set(cv, buf);
5294 #ifdef CONFIG_MENU_PAGE
5295                 config_cvar_edit_save();
5296 #endif
5297             }
5298             return true;
5299         }
5300         break;
5301     }
5302     return false;
5303 }
5304 
5305 
5306 
5307 //
5308 // M_Responder
5309 //
M_Responder(event_t * ev)5310 boolean M_Responder (event_t* ev)
5311 {
5312     static  boolean button_down = 0;
5313     static  tic_t  mousewait = 0;
5314     static  int  mousey = 0;
5315     static  int  mousex = 0;
5316 
5317     menufunc_t routine;  // for some casting problem
5318     menuitem_t * r_menuline;
5319 
5320     int i;
5321     int key = KEY_NULL; // key pressed (if any)
5322     int button_key = 0; // mouse and joystick specific
5323     char ch = '\0';  // ASCII char it corresponds to
5324 
5325     switch (ev->type )
5326     {
5327      case ev_keydown :
5328         key = ev->data1;  // keycode
5329         ch  = ev->data2;  // ASCII char
5330         if( key >= KEY_MOUSE1 )
5331         {
5332             button_key = key;  // mouse or joystick
5333 
5334             // Menu slider, all buttons of mouse1, 1st button of mouse2 ???
5335             if( key <= KEY_MOUSE2 )
5336                 button_down = 1;
5337 
5338             // added 5-2-98 remap virtual keys (mouse & joystick buttons)
5339             switch(key)
5340             {
5341              case KEY_MOUSE1:
5342              case KEY_JOY0BUT0:
5343                 // [WDJ] No mouse ENTER key on the sliders, it makes them move right.
5344                 key = ( currentMenu
5345                        && (currentMenu->menuitems[itemOn].status & (IT_BIGSLIDER | IT_CV_SLIDER | IT_CV_NOMOD ) )
5346                        ) ? KEY_NULL : KEY_ENTER;
5347                 break;
5348              case KEY_MOUSE1+1:
5349              case KEY_JOY0BUT1:
5350                 key = KEY_BACKSPACE;
5351                 break;
5352              default:
5353                 // [Leonardo Montenegro]
5354                 // Custom binding for accessing main menu through other means
5355                 // beside ESC key. Useful for allowing menu navigation through
5356                 // controllers, per example
5357                 if (key == gamecontrol[gc_menuesc][0] || key == gamecontrol[gc_menuesc][1] )
5358                 {
5359                     key = KEY_ESCAPE;
5360                 }
5361                 else if(menuactive)
5362                 {
5363                     // Navigating menus through joy hat of first controller.
5364                     if(key == KEY_JOY0HATUP)
5365                     {
5366                         key = KEY_UPARROW;
5367                     }
5368                     else if(key == KEY_JOY0HATDOWN)
5369                     {
5370                         key = KEY_DOWNARROW;
5371                     }
5372                     else if(key == KEY_JOY0HATLEFT)
5373                     {
5374                         key = KEY_LEFTARROW;
5375                     }
5376                     else if(key == KEY_JOY0HATRIGHT)
5377                     {
5378                         key = KEY_RIGHTARROW;
5379                     }
5380                 }
5381             }
5382         }
5383         else
5384         {   // Keyboard keys,  key < KEY_MOUSE1.
5385             // on key press, inhibit menu responses to the mouse for a while
5386             mousewait = I_GetTime() + TICRATE*2;  // 4 sec
5387         }
5388         break;
5389      case ev_keyup:
5390         // Menu slider, all buttons of mouse1, 1st button of mouse2 ???
5391         if( ev->data1 >= KEY_MOUSE1 && ev->data1 <= KEY_MOUSE2 )
5392             button_down = 0;
5393         break;
5394      case ev_mouse:
5395         if( menuactive )
5396         {
5397             // [WDJ] This code only triggered when movement exceeded MENU_MOUSE_TRIG
5398             // so there is no need for mouse position recording.
5399             // Y movement overrides X movement, there can be ony one key.
5400             if( mousewait >= I_GetTime() )  break;  // delay between triggers
5401             mousex += ev->data2;
5402             mousey += ev->data3;
5403             if (mousey < -MENU_MOUSE_TRIG)
5404             {
5405                 key = KEY_DOWNARROW;
5406             }
5407             else if (mousey > MENU_MOUSE_TRIG)
5408             {
5409                 key = KEY_UPARROW;
5410             }
5411             else if (mousex < -MENU_MOUSE_TRIG)
5412             {
5413                 if( button_down )   key = KEY_LEFTARROW;
5414             }
5415             else if (mousex > MENU_MOUSE_TRIG)
5416             {
5417                 if( button_down )   key = KEY_RIGHTARROW;
5418             }
5419 
5420             if( key )
5421             {
5422                 button_key = key;
5423                 // On any trigger, zero everything, so cannot drift off slider.
5424                 mousewait = I_GetTime() + TICRATE/7;
5425                 mousex = mousey = 0;
5426             }
5427         }
5428         break;
5429      default:
5430         break;
5431     }
5432 
5433     if (key == KEY_NULL)
5434         return false;
5435 
5436 
5437     // Save Game string input
5438     if (edit_enable)
5439     {
5440         switch(key)
5441         {
5442           case KEY_BACKSPACE:
5443             if (edit_index > 0)
5444             {
5445                 edit_index--;
5446                 edit_buffer[edit_index] = 0;
5447             }
5448             break;
5449 
5450           case KEY_ESCAPE:
5451             edit_enable = 0;
5452             // restore from source
5453             strcpy(edit_buffer, &savegamedisp[slotindex].desc[0]);
5454             break;
5455 
5456           case KEY_ENTER:
5457             edit_enable = 0;
5458             if (edit_buffer[0])
5459             {
5460                 strcpy(&savegamedisp[slotindex].desc[0], edit_buffer);
5461                 if( edit_done_callback )   edit_done_callback();
5462                 edit_done_callback = NULL;
5463             }
5464             break;
5465 
5466           default:
5467             if (is_printable(ch) &&
5468                 edit_index < SAVESTRINGSIZE-1 &&
5469                 V_StringWidth(edit_buffer) < (SAVESTRINGSIZE-2)*8)
5470             {
5471                 edit_buffer[edit_index++] = ch;
5472                 edit_buffer[edit_index] = 0;
5473             }
5474             break;
5475         }
5476         goto ret_true;
5477     }
5478 
5479     if( (devparm && key == KEY_F1)
5480        || (key == gamecontrol[gc_screenshot][0] ))
5481     {
5482         COM_BufAddText("screenshot\n");
5483         goto ret_true;
5484     }
5485 
5486     if( gamestate == GS_WAITINGPLAYERS )
5487     {
5488         if( D_WaitPlayer_Response( key ) )
5489             goto ret_true;
5490     }
5491 
5492     // when the menu is not open
5493     if (!menuactive)
5494     {
5495         switch(key)
5496         {
5497           case '-':         // Screen size down
5498             if (automapactive || chat_on || con_destlines)     // DIRTY !!!
5499                 return false;
5500             cvar_incdec( &cv_viewsize, -1 );
5501             goto ret_true;
5502 
5503           case '=':        // Screen size up
5504             if (automapactive || chat_on || con_destlines)     // DIRTY !!!
5505                 return false;
5506             cvar_incdec( &cv_viewsize, +1 );
5507             goto ret_true;
5508 
5509           case KEY_F1:            // Help key
5510             M_StartControlPanel ();
5511 
5512             if ( gamemode == ultdoom_retail )
5513               currentMenu = &ReadDef2;
5514             else
5515               currentMenu = &ReadDef1;
5516 
5517             itemOn = 0;
5518             S_StartSound(menu_sfx_open);
5519             goto ret_true;
5520 
5521           case KEY_F2:            // Save
5522             M_StartControlPanel();
5523             S_StartSound(menu_sfx_open);
5524             M_Savegame(0);
5525             goto ret_true;
5526 
5527           case KEY_F3:            // Load
5528             M_StartControlPanel();
5529             S_StartSound(menu_sfx_open);
5530             M_Loadgame(0);
5531             goto ret_true;
5532 
5533           case KEY_F4:            // Sound Volume
5534             M_StartControlPanel ();
5535             currentMenu = &SoundDef;
5536             itemOn = SVM_sfx_vol;
5537             S_StartSound(menu_sfx_open);
5538             goto ret_true;
5539 
5540           //added:26-02-98: now F5 calls the Video Menu
5541           case KEY_F5:
5542             S_StartSound(menu_sfx_open);
5543             M_StartControlPanel();
5544             Push_Setup_Menu (&VideoModeDef);
5545             //M_ChangeDetail(0);
5546             goto ret_true;
5547 
5548           case KEY_F6:            // Quicksave
5549             S_StartSound(menu_sfx_open);
5550             M_QuickSave();
5551             goto ret_true;
5552 
5553           //added:26-02-98: F7 changed to Options menu
5554           case KEY_F7:            // originally was End game
5555             S_StartSound(menu_sfx_open);
5556             M_StartControlPanel();
5557             Push_Setup_Menu (&OptionsDef);
5558             //M_EndGame(0);
5559             goto ret_true;
5560 
5561           case KEY_F8:            // Toggle messages
5562             CV_ValueIncDec(&cv_showmessages,+1);
5563             S_StartSound(menu_sfx_open);
5564             goto ret_true;
5565 
5566           case KEY_F9:            // Quickload
5567             S_StartSound(menu_sfx_open);
5568             M_QuickLoad();
5569             goto ret_true;
5570 
5571           case KEY_F10:           // Quit DOOM
5572             S_StartSound(menu_sfx_open);
5573             M_QuitDOOM(0);
5574             goto ret_true;
5575 
5576           //added:10-02-98: the gamma toggle is now also in the Options menu
5577           case KEY_F11:
5578             S_StartSound(menu_sfx_open);
5579             // bring up the gamma menu
5580             M_StartControlPanel();
5581             Push_Setup_Menu (&VideoOptionsDef);
5582             goto ret_true;
5583 
5584           // Pop-up menu
5585           case KEY_ESCAPE:
5586             M_StartControlPanel ();
5587             S_StartSound(menu_sfx_open);
5588             goto ret_true;
5589         }
5590         return false;
5591     }
5592 
5593     if( ! currentMenu )  return false;
5594     r_menuline = & currentMenu->menuitems[itemOn];
5595     routine = r_menuline->itemaction;
5596 
5597     //added:30-01-98:
5598     // Handle menuitems which need a specific key handling
5599     if( routine && (r_menuline->status & IT_TYPE) == IT_KEYHANDLER )
5600     {
5601       input_char = ch;
5602       routine(key);
5603       goto ret_true;
5604     }
5605 
5606     // Handle overriding keyboard input
5607     if( key_handler2 )
5608     {
5609         if( key_handler2(key) )  goto ret_true;
5610     }
5611 
5612     if( r_menuline->status==IT_MSGHANDLER )
5613     {
5614         // special message menu
5615         if( r_menuline->alphaKey == true )
5616         {
5617           // [smite] just for this purpose since unraveling the IT_MSGHANDLER hack would be too harrowing
5618           if (tolower(ch) == 'n')
5619             key = 'n';
5620           else if (tolower(ch) == 'y')
5621             key = 'y';
5622 
5623           if( button_key )  // Translate only for mouse and joystick.
5624           {
5625               if(key == KEY_ENTER) key = 'y'; // Convert Enter keypress to 'y' in menus, allowing game exit from controller
5626               if(key == KEY_BACKSPACE) key = 'n'; // Convert Backspace keypress to 'n' in menus, making possible to go back
5627           }
5628 
5629           // [WDJ] Important that message boxes not be cleared away by ENTER or any normal keypress.
5630           // If a message box pops up, it must ignore the normal keypresses.  Otherwise someone hitting
5631           // keys quickly will blow it away before they even read it.
5632           if(key == KEY_SPACE || key == 'n' || key == 'y' || key == KEY_ESCAPE)
5633           {
5634                 if(routine) routine(key);
5635                 M_StopMessage(0);
5636           }
5637         }
5638         else
5639         {
5640             //added:07-02-98:dirty hak:for the customize controls, I want only
5641             //      buttons/keys, not moves
5642             if (ev->type == ev_mouse)
5643                 goto ret_true;
5644 
5645             // Call the itemaction routine, with the key.
5646             void (*cc_action)(event_t *) = r_menuline->itemaction;
5647             if (cc_action)   cc_action(ev);
5648         }
5649         goto ret_true;
5650     }
5651 
5652     // BP: one of the more big hack i have never made
5653     if( routine && (r_menuline->status & IT_TYPE) == IT_CVAR )
5654     {
5655         if( (r_menuline->status & IT_CVARTYPE) == IT_CV_STRING )
5656         {
5657             if( M_Change_cvar_string(key, ch) )
5658                 goto ret_true;
5659 
5660             routine = NULL;
5661         }
5662         else
5663             routine = M_Change_cvar_value;
5664     }
5665 
5666 #ifdef CONFIG_MENU_PAGE
5667     if( config_cvar_key_handler( key ) )
5668         goto ret_true;
5669 #endif
5670 
5671     // Keys usable within menu
5672     switch (key)
5673     {
5674 #if defined SAVEGAMEDIR || defined SAVEGAME99
5675       case KEY_DELETE:	// delete directory or savegame
5676 #ifdef SAVEGAMEDIR
5677         // The Dir, Loadgame, Savegame menus all have dir at MSLOT_0
5678         if( delete_callback && itemOn >= SAVEGAME_MSLOT_0 )
5679         {
5680             slotindex = itemOn - SAVEGAME_MSLOT_0;
5681             M_StartMessage("Delete Y/N?", delete_callback, MM_YESNO);
5682             goto ret_action;
5683         }
5684 #else
5685         if( delete_callback && itemOn >= 0 )
5686         {
5687             slotindex = itemOn;
5688             M_StartMessage("Delete Y/N?", delete_callback, MM_YESNO);
5689             goto ret_action;
5690         }
5691 #endif
5692         break;
5693 
5694       case '[':
5695       case KEY_PGUP:
5696         if( scroll_callback && (scroll_index > 0))
5697         {
5698             scroll_callback( -6 );  // some functions need to correct
5699             goto ret_updown;
5700         }
5701         break;
5702 
5703       case ']':
5704       case KEY_PGDN:
5705         if( scroll_callback && (scroll_index < (99-6)))
5706         {
5707             scroll_callback( 6 );
5708             goto ret_updown;
5709         }
5710         break;
5711 #endif
5712 
5713       case KEY_DOWNARROW:
5714 #if defined SAVEGAMEDIR || defined SAVEGAME99
5715         if( scroll_callback && (scroll_index < 99) && (itemOn >= SAVEGAME_MSLOT_LAST))
5716         {
5717             // scrolling menu scrolls preferentially
5718             scroll_index ++;
5719             scroll_callback( 1 );
5720             goto ret_updown;
5721         }
5722 #endif
5723         do
5724         {
5725             if (itemOn+1 > currentMenu->numitems-1)
5726             {
5727                 if( scroll_callback )  // only wrap when not scrolling
5728                     goto ret_updown;
5729                 itemOn = 0;
5730             }
5731             else itemOn++;
5732         } while((currentMenu->menuitems[itemOn].status & IT_TYPE)==IT_SPACE);
5733         goto ret_updown;
5734 
5735       case KEY_UPARROW:
5736 #if defined SAVEGAMEDIR || defined SAVEGAME99
5737         if( scroll_callback && (scroll_index > 0) && (itemOn < (SAVEGAME_MSLOT_0+1)))
5738         {
5739             // scrolling menu scrolls preferentially
5740             scroll_index --;
5741             if( scroll_index < 0 )   scroll_index = 0;
5742             scroll_callback( -1 );  // some functions need to correct
5743             goto ret_updown;
5744         }
5745 #endif
5746         do
5747         {
5748             if (!itemOn)
5749                 itemOn = currentMenu->numitems-1;
5750             else itemOn--;
5751         } while((currentMenu->menuitems[itemOn].status & IT_TYPE)==IT_SPACE);
5752         goto ret_updown;
5753 
5754       case KEY_LEFTARROW:
5755         if (  routine &&
5756             ( (r_menuline->status & IT_TYPE) == IT_ARROWS
5757             ||(r_menuline->status & IT_TYPE) == IT_CVAR   ))
5758         {
5759             S_StartSound(menu_sfx_val);
5760             routine(0);
5761         }
5762         goto ret_true;
5763 
5764       case KEY_RIGHTARROW:
5765         if ( routine &&
5766             ( (r_menuline->status & IT_TYPE) == IT_ARROWS
5767             ||(r_menuline->status & IT_TYPE) == IT_CVAR   ))
5768         {
5769             S_StartSound(menu_sfx_val);
5770             routine(1);
5771         }
5772         goto ret_true;
5773 
5774       case KEY_ENTER:
5775         currentMenu->lastOn = itemOn;
5776         menuline = r_menuline;
5777         if ( routine )
5778         {
5779             switch (menuline->status & IT_TYPE)  {
5780                 case IT_CVAR:
5781                 case IT_ARROWS:
5782                     routine(1);            // right arrow
5783                     S_StartSound(menu_sfx_val);
5784                     break;
5785                 case IT_CALL:
5786                     routine(itemOn);
5787                     S_StartSound(menu_sfx_enter);
5788                     break;
5789                 case IT_SUBMENU:
5790                     Push_Setup_Menu((menu_t *)menuline->itemaction);
5791                     S_StartSound(menu_sfx_enter);
5792                     break;
5793             }
5794         }
5795         goto ret_true;
5796 
5797       case KEY_ESCAPE:
5798         currentMenu->lastOn = itemOn;
5799         if( init_sequence == 1 )
5800             goto ret_true;  // No escape from Launcher
5801 
5802         if( menucnt )
5803         {
5804             Pop_Menu();
5805             itemOn = currentMenu->lastOn;
5806             S_StartSound(menu_sfx_open); // a matter of taste which sound to choose
5807         }
5808         else
5809         {
5810             M_Clear_Menus (true);
5811             S_StartSound(menu_sfx_esc);
5812             // Exit menus, return to demo or game
5813             if( ! Game_Playing() )
5814                 D_StartTitle();  // restart title screen and demo
5815         }
5816         goto ret_true;
5817 
5818       case KEY_BACKSPACE:
5819         if( r_menuline->status == IT_CONTROL )
5820         {
5821             S_StartSound(menu_sfx_val);
5822             // detach any keys associated to the game control
5823             G_Clear_ControlKeys (setupcontrols, currentMenu->menuitems[itemOn].alphaKey);
5824             goto ret_true;
5825         }
5826         currentMenu->lastOn = itemOn;
5827         if( menucnt )
5828         {
5829             menucnt --;
5830             currentMenu = menustack[ menucnt ];
5831             itemOn = currentMenu->lastOn;
5832             S_StartSound(menu_sfx_open);
5833         }
5834         goto ret_true;
5835 
5836 
5837       default:
5838 #if 1
5839         // any other key: if a letter, try to find the corresponding menuitem
5840         if (!isalpha(ch))
5841 #else
5842         if( ch == 0 )
5843 #endif
5844           goto ret_true;
5845 
5846         // from itemOn to bottom
5847         for (i = itemOn+1;i < currentMenu->numitems;i++)
5848         {
5849             if( (currentMenu->menuitems[i].alphaKey == ch)
5850                  && ((currentMenu->menuitems[i].status & IT_OPTION) == 0) )
5851             {
5852                 itemOn = i;
5853                 goto ret_action;
5854             }
5855         }
5856         // search from top to itemOn
5857         for (i = 0;i <= itemOn;i++)
5858         {
5859             if( (currentMenu->menuitems[i].alphaKey == ch)
5860                  && ((currentMenu->menuitems[i].status & IT_OPTION) == 0) )
5861             {
5862                 itemOn = i;
5863                 goto ret_action;
5864             }
5865         }
5866         break;
5867 
5868     }
5869 
5870 ret_true:
5871     return true;
5872 
5873 ret_action:
5874     S_StartSound(menu_sfx_action);
5875     return true;
5876 
5877 ret_updown:
5878     S_StartSound(menu_sfx_updown);
5879     return true;
5880 }
5881 
5882 
5883 
5884 //
5885 //      Find string height from hu_font chars
5886 //
5887 static
M_StringHeight(char * string)5888 int M_StringHeight(char* string)
5889 {
5890     int      i;
5891     int      h;
5892     int      height = 8; //(hu_font[0]->height);
5893 
5894     h = height;
5895     for (i = 0;i < (int)strlen(string);i++)
5896     {
5897         if (string[i] == '\n')
5898             h += height;
5899     }
5900 
5901     return h;
5902 }
5903 
5904 
5905 //
5906 // M_Drawer
5907 // Called after the view has been rendered,
5908 // but before it has been blitted.
5909 //
M_Drawer(void)5910 void M_Drawer (void)
5911 {
5912     if (!menuactive)
5913         return;
5914 
5915     // center the scaled graphics for the menu,
5916     V_SetupDraw( 0 | V_SCALEPATCH | V_SCALESTART | V_CENTERMENU );
5917 
5918     // now that's more readable with a faded background (yeah like Quake...)
5919     if ( reg_colormaps )  // not before colormaps loaded
5920         V_FadeScreen ();
5921 
5922     // menu drawing
5923     if (currentMenu->drawroutine)
5924         currentMenu->drawroutine();      // call current menu Draw routine
5925 
5926     V_SetupDraw( 0 | V_SCALEPATCH | V_SCALESTART | V_CENTERHORZ );  // restore
5927 
5928 }
5929 
5930 //
5931 // M_StartControlPanel
5932 //
5933 // Called by G_Responder, M_Responder, M_QuickSave, M_StartMessage.
M_StartControlPanel(void)5934 void M_StartControlPanel (void)
5935 {
5936     // intro might call this repeatedly
5937     if (menuactive)
5938         return;
5939 
5940     menuactive = 1;
5941     currentMenu = &MainDef;         // JDC
5942     itemOn = currentMenu->lastOn;   // JDC
5943 
5944     if(demoplayback)  // menus without the demo interference
5945     {
5946         G_StopDemo();
5947         R_FillBackScreen ();
5948     }
5949 
5950     CON_ToggleOff ();   // dirty hack : move away console
5951 
5952     I_StartupMouse( false );  // menu mode
5953 }
5954 
5955 //
5956 // M_Clear_Menus
5957 //
5958 static
M_Clear_Menus(boolean callexitmenufunc)5959 void M_Clear_Menus (boolean callexitmenufunc)
5960 {
5961 #ifdef CONFIG_MENU_PAGE
5962     menu_cfg = CFG_none;  // cancel any config editing
5963     menu_cfg_editing = 0;
5964 #endif
5965 
5966     if(!menuactive)
5967         return;
5968 
5969     if( currentMenu->quitroutine && callexitmenufunc)
5970     {
5971         if( !currentMenu->quitroutine())
5972             return; // we can't quit this menu (also used to set parameter from the menu)
5973     }
5974 
5975     menuactive = 0;
5976     I_StartupMouse( !paused );  // play mode if not paused
5977 }
5978 
5979 
5980 static
M_SetupMenu(menu_t * menudef)5981 void M_SetupMenu(menu_t *menudef)
5982 {
5983     if( currentMenu && (currentMenu->quitroutine) )
5984     {
5985         if( !currentMenu->quitroutine())
5986             return; // we can't quit this menu (also used to set parameter from the menu)
5987     }
5988 
5989     // Menu history
5990     currentMenu = menudef;
5991     itemOn = currentMenu->lastOn;
5992 
5993     // in case of...
5994     if (itemOn >= currentMenu->numitems)
5995         itemOn = currentMenu->numitems - 1;
5996 
5997     // the curent item can be disabled,
5998     // this code go up until a enabled item found
5999     while(currentMenu->menuitems[itemOn].status==IT_DISABLED && itemOn)
6000         itemOn--;
6001 
6002     delete_callback = NULL;
6003     scroll_callback = NULL;
6004 }
6005 
6006 
6007 //
6008 // Push_Setup_Menu
6009 //
6010 static
Push_Setup_Menu(menu_t * menudef)6011 void Push_Setup_Menu(menu_t *menudef)
6012 {
6013     int i;
6014 
6015     // [WDJ] Menus use dynamic ordering, from multiple parent menus.
6016     // Check for duplicate
6017     if( menucnt > 0 )
6018     {
6019         for( i = 0; i < menucnt; i++ )
6020         {
6021             if( menustack[i] == currentMenu )
6022             {
6023                 // User is looping through menus.
6024                 menucnt = i+1;  // cut off the loop
6025                 goto setup;
6026             }
6027         }
6028     }
6029 
6030     if( menucnt < NUM_MENUSTACK )
6031     {
6032         // Normal push menu
6033         menustack[ menucnt++ ] = currentMenu;
6034     }
6035 
6036 setup:
6037     M_SetupMenu( menudef );
6038 }
6039 
6040 // Go back to the previous menu, reloading data if necessary
6041 static
Pop_Menu(void)6042 void Pop_Menu( void )
6043 {
6044     if( menucnt )
6045     {
6046        M_SetupMenu( menustack[ -- menucnt ] );  // back out
6047 
6048        // refresh data
6049        if( currentMenu == &LoadDef )   M_Loadgame(0);
6050        if( currentMenu == &SaveDef )   M_Savegame(0);
6051     }
6052     else
6053     {
6054        M_Clear_Menus (true);
6055        // Exit menus, return to demo or game
6056        if( ! Game_Playing() )
6057            D_StartTitle();  // restart title screen and demo
6058     }
6059 }
6060 
6061 
6062 //
6063 // M_Ticker
6064 //
6065 // Call once per tic.
M_Ticker(void)6066 void M_Ticker (void)
6067 {
6068     if (--skullAnimCounter <= 0)
6069     {
6070         whichSkull ^= 1;
6071         skullAnimCounter = 8 * NEWTICRATERATIO;
6072     }
6073 
6074     //added:30-01-98:test mode for five seconds
6075     if( vidm_testing_cnt > 0 )
6076     {
6077         if( --vidm_testing_cnt == 0 )
6078         {
6079             // restore the previous video mode
6080             if( key_handler2 )  // video test key handler
6081             {
6082                 vidm_testing_cnt = 1;  // necessary to process ESCAPE
6083                 key_handler2( KEY_ESCAPE );
6084             }
6085         }
6086     }
6087 }
6088 
6089 
6090 //
6091 // M_Init
6092 //
6093 // Called once, very early
M_Init(void)6094 void M_Init (void)
6095 {
6096     currentMenu = &MainDef;
6097     menuactive = 0;
6098     itemOn = currentMenu->lastOn;
6099 
6100     whichSkull = 0;
6101     skullAnimCounter = 10;
6102 
6103     quicksave_slotid = -1;
6104 
6105 #ifdef CONFIG_MENU_PAGE
6106     temp_cvar.string = NULL;
6107 #endif
6108 
6109     CV_RegisterVar(&cv_skill);
6110     CV_RegisterVar(&cv_monsters);
6111     CV_RegisterVar(&cv_bots);
6112     CV_RegisterVar(&cv_bot_skill);
6113     CV_RegisterVar(&cv_bot_speed);
6114     CV_RegisterVar(&cv_bot_skin);
6115     CV_RegisterVar(&cv_bot_respawn_time);
6116     CV_RegisterVar(&cv_bot_random);
6117     CV_RegisterVar(&cv_bot_randseed);
6118     CV_RegisterVar(&cv_bot_gen);
6119     CV_RegisterVar(&cv_nextmap );
6120     CV_RegisterVar(&cv_nextepmap );
6121     CV_RegisterVar(&cv_deathmatch_menu);
6122     CV_RegisterVar(&cv_wait_players);
6123     CV_RegisterVar(&cv_wait_timeout);
6124     CV_RegisterVar(&cv_serversearch);
6125     CV_RegisterVar(&cv_download_files);
6126     CV_RegisterVar(&cv_download_savegame);
6127     CV_RegisterVar(&cv_netrepair);
6128     CV_RegisterVar(&cv_SV_download_files);
6129     CV_RegisterVar(&cv_SV_download_savegame);
6130     CV_RegisterVar(&cv_SV_netrepair);
6131     CV_RegisterVar(&cv_menusound);
6132     CV_RegisterVar(&cv_oof_2s);
6133 }
6134 
6135 // Called once after gamemode has been determined, game dependent
M_Configure(void)6136 void M_Configure (void)
6137 {
6138     int i;
6139     int cval;
6140 
6141     if(dedicated)
6142         return;
6143 
6144     // Here we could catch other version dependencies,
6145     //  like HELP1/2, and four episodes.
6146 
6147     // Reversible
6148     // remove the inventory key from the menu !
6149     cval = ( EN_inventory )? (IT_CONTROL) : IT_LITLSPACE;
6150     for( i=0; i<ControlDef2.numitems; i++)
6151     {
6152         if( ControlMenu2[i].alphaKey == gc_invprev ||
6153             ControlMenu2[i].alphaKey == gc_invnext ||
6154             ControlMenu2[i].alphaKey == gc_invuse )
6155             ControlMenu2[i].status = cval;
6156     }
6157 
6158     // remove the fly down key from the menu !
6159     cval = ( gamemode == heretic )? (IT_CONTROL) : IT_LITLSPACE;
6160     for( i=0; i<ControlDef2.numitems; i++)
6161     {
6162         if( ControlMenu2[i].alphaKey == gc_flydown )
6163             ControlMenu2[i].status = cval;
6164     }
6165 
6166     // irreversible
6167     if( ! VALID_LUMP( W_CheckNumForName("E2M1") ) )
6168     {
6169         exmy_cons_t[9].value = 0;
6170         exmy_cons_t[9].strvalue = NULL;
6171     }
6172     else
6173     if( ! VALID_LUMP( W_CheckNumForName("E3M1") ) )
6174     {
6175         exmy_cons_t[18].value = 0;
6176         exmy_cons_t[18].strvalue = NULL;
6177     }
6178     else
6179     if( ! VALID_LUMP( W_CheckNumForName("E4M1") ) )
6180     {
6181         exmy_cons_t[27].value = 0;
6182         exmy_cons_t[27].strvalue = NULL;
6183     }
6184     else
6185     if( ! VALID_LUMP( W_CheckNumForName("E5M1") ) )
6186     {
6187         exmy_cons_t[36].value = 0;
6188         exmy_cons_t[36].strvalue = NULL;
6189     }
6190 
6191     switch ( gamemode )
6192     {
6193       case doom2_commercial:
6194         // This is used because DOOM 2 had only one HELP
6195         //  page. I use CREDIT as second page now, but
6196         //  kept this hack for educational purposes.
6197         MainMenu[MM_readthis] = MainMenu[MM_quitdoom];
6198         MainDef.numitems--;
6199         MainDef.y += 8;
6200         ReadDef1.drawroutine = M_DrawReadThis1;
6201         ReadDef1.x = 330;
6202         ReadDef1.y = 165;
6203         ReadMenu1[0].itemaction = &MainDef;
6204         break;
6205       case doom_shareware:
6206         // Episode 2 and 3 are handled,
6207         //  branching to an ad screen.
6208       case doom_registered:
6209           // We need to remove the fourth episode.
6210           EpiDef.numitems--;
6211       case ultdoom_retail:
6212           // We are fine.
6213           cv_nextmap.PossibleValue = exmy_cons_t;
6214           cv_nextmap.defaultvalue = "11";
6215           // We need to remove the fifth episode.
6216           EpiDef.numitems--;
6217           break;
6218       case heretic:
6219           cv_nextmap.PossibleValue = exmy_cons_t;
6220           cv_nextmap.defaultvalue = "11";
6221 
6222           MainDef.menutitlepic = "M_HTIC";
6223           MainDef.drawroutine = HereticMainMenuDrawer;
6224           SkullBaseLump = W_GetNumForName("M_SKL00");
6225           strcpy(skullName[0], "M_SLCTR1");
6226           strcpy(skullName[1], "M_SLCTR2");
6227 
6228           EpisodeMenu[0].text = "CITY OF THE DAMNED";
6229           EpisodeMenu[1].text = "HELL'S MAW";
6230           EpisodeMenu[2].text = "THE DOME OF D'SPARIL";
6231           EpisodeMenu[3].text = "THE OSSUARY";
6232           EpisodeMenu[4].text = "THE STAGNANT DEMESNE";
6233 
6234           NewGameMenu[0].text = "THOU NEEDETH A WET-NURSE";
6235           NewGameMenu[1].text = "YELLOWBELLIES-R-US";
6236           NewGameMenu[2].text = "BRINGEST THEM ONETH";
6237           NewGameMenu[3].text = "THOU ART A SMITE-MEISTER";
6238           NewGameMenu[4].text = "BLACK PLAGUE POSSESSES THEE";
6239           break;
6240 
6241       case chexquest1:
6242           if( Chex_safe_pictures( "M_EPI1", NULL ) == NULL )
6243           {
6244               // Found Doom episode title in wad.
6245               // [WDJ] Coverup for Doom episode titles in chexquest1 wad
6246               // According to Chexquest3.
6247               EpisodeMenu[0].text = "Rescue on Baziok";
6248               EpisodeMenu[1].text = "Terror in Chex-City"; // Avoid needing (R)
6249               EpisodeMenu[2].text = "Invasion";
6250               EpisodeMenu[3].text = "Episode 4";
6251               EpisodeMenu[4].text = "Episode 5";
6252               EpisodeMenu[0].status = IT_CALL | IT_STRING2;
6253               EpisodeMenu[1].status = IT_CALL | IT_STRING2;
6254               EpisodeMenu[2].status = IT_CALL | IT_STRING2;
6255               EpisodeMenu[3].status = IT_CALL | IT_STRING2;
6256               EpisodeMenu[4].status = IT_CALL | IT_STRING2;
6257           }
6258           break;
6259 
6260 
6261       default:
6262         break;
6263     }
6264 
6265     CV_menusound_OnChange();
6266 }
6267 
6268 //======================================================================
6269 // Lighting
6270 
6271 menuitem_t LightingMenu[]=
6272 {
6273     {IT_STRING | IT_CVAR ,0, "Corona"             , &cv_corona          , 0},
6274     {IT_STRING | IT_CVAR, 0, "Corona size"        , &cv_coronasize      , 0},
6275     {IT_STRING | IT_CVAR ,0, "Corona draw"        , &cv_corona_draw_mode, 0},
6276 //    {IT_STRING | IT_CVAR, 0, "Dynamic lighting"   , &cv_dynamiclighting , 0},
6277 //    {IT_STRING | IT_CVAR, 0, "Static lighting"    , &cv_staticlighting  , 0},
6278 //    {IT_STRING | IT_CVAR, 0, "Monster ball light" , &cv_monball_light    , 0},
6279 };
6280 
6281 menu_t  LightingDef =
6282 {
6283     "M_OPTTTL",
6284     "OPTIONS",
6285     LightingMenu,
6286     M_DrawGenericMenu,
6287     NULL,
6288     sizeof(LightingMenu)/sizeof(menuitem_t),
6289     60,40,
6290     0,
6291 };
6292 
6293 
6294 //======================================================================
6295 // OpenGL specifics options
6296 //======================================================================
6297 
6298 #ifdef HWRENDER
6299 
6300 static void M_DrawOpenGLMenu(void);
6301 static void M_OGL_DrawFogMenu(void);
6302 static void M_OGL_DrawColorMenu(void);
6303 static void M_HandleFogColor (int choice);
6304 
6305 menu_t OGL_LightingDef, OGL_FogDef, OGL_ColorDef, OGL_DevDef;
6306 
6307 #define QUALITY_ITEM   2
6308 menuitem_t OpenGLOptionsMenu[]=
6309 {
6310     {IT_STRING | IT_CVAR,0, "Mouse look"          , &cv_grmlook_extends_fov ,  0},
6311     {IT_STRING | IT_CVAR | IT_YOFFSET, 0, "Field of view"       , &cv_grfov             , 10},
6312     {IT_STRING | IT_CVAR | IT_YOFFSET, 0, "Quality"             , &cv_scr_depth         , 20},
6313     {IT_STRING | IT_CVAR | IT_YOFFSET, 0, "Texture Filter"      , &cv_grfiltermode      , 30},
6314     {IT_STRING | IT_CVAR | IT_CV_SLIDER | IT_YOFFSET, 0, "Translucent HUD", &cv_grtranslucenthud  , 40},
6315 
6316     {IT_SUBMENU | IT_WHITESTRING | IT_YOFFSET, 0, "Lighting >>"       , &OGL_LightingDef   , 65},
6317     {IT_SUBMENU | IT_WHITESTRING | IT_YOFFSET, 0, "Fog >>"            , &OGL_FogDef        , 75},
6318     {IT_SUBMENU | IT_WHITESTRING | IT_YOFFSET, 0, "Gamma >>"          , &OGL_ColorDef      , 85},
6319     {IT_SUBMENU | IT_WHITESTRING | IT_YOFFSET, 0, "Development >>"    , &OGL_DevDef        , 95},
6320 };
6321 
6322 menuitem_t OGL_LightingMenu[]=
6323 {
6324     {IT_STRING | IT_CVAR, 0, "Corona Draw"         , &cv_grcorona_draw ,  0},
6325     {IT_STRING | IT_CVAR, 0, "Dynamic lighting"    , &cv_grdynamiclighting,  0},
6326     {IT_STRING | IT_CVAR, 0, "Static lighting"     , &cv_grstaticlighting,  0},
6327     {IT_STRING | IT_CVAR, 0, "Monster ball light"  , &cv_monball_light,  0},
6328 };
6329 
6330 #define FOG_COLOR_ITEM  1
6331 menuitem_t OGL_FogMenu[]=
6332 {
6333     {IT_STRING | IT_CVAR | IT_YOFFSET, 0,"Fog"             , &cv_grfog              ,  0},
6334     {IT_STRING | IT_KEYHANDLER| IT_YOFFSET, 0, "Fog color" , M_HandleFogColor       , 10},
6335     {IT_STRING | IT_CVAR | IT_YOFFSET, 0,"Fog density"     , &cv_grfogdensity       , 20},
6336 };
6337 
6338 menuitem_t OGL_ColorMenu[]=
6339 {
6340     //{IT_STRING | NOTHING, "Gamma correction", NULL                   ,  0},
6341     {IT_STRING | IT_CVAR | IT_CV_SLIDER | IT_YOFFSET, 0,"red"  , &cv_grgammared     , 10},
6342     {IT_STRING | IT_CVAR | IT_CV_SLIDER | IT_YOFFSET, 0,"green", &cv_grgammagreen   , 20},
6343     {IT_STRING | IT_CVAR | IT_CV_SLIDER | IT_YOFFSET, 0,"blue" , &cv_grgammablue    , 30},
6344     //{IT_STRING | IT_CVAR | IT_CV_SLIDER, "Constrast", &cv_grcontrast , 50},
6345 };
6346 
6347 menuitem_t OGL_DevMenu[]=
6348 {
6349 //    {IT_STRING | IT_CVAR, "Polygon smooth"  , &cv_grpolygonsmooth    ,  0},
6350     {IT_STRING | IT_CVAR, 0, "MD2 models"      , &cv_grmd2              , 10},
6351 #ifdef TRANSWALL_CHOICE
6352     {IT_STRING | IT_CVAR, 0, "Translucent walls", &cv_grtranswall       , 20},
6353 #endif
6354     {IT_STRING | IT_CVAR, 0, "Polygon shape"  , &cv_grpolyshape         , 30},
6355 };
6356 
6357 menu_t  OpenGLOptionDef =
6358 {
6359     "M_OPTTTL",
6360     "OPTIONS",
6361     OpenGLOptionsMenu,
6362     M_DrawOpenGLMenu,
6363     NULL,
6364     sizeof(OpenGLOptionsMenu)/sizeof(menuitem_t),
6365     60,40,
6366     0
6367 };
6368 
6369 menu_t  OGL_LightingDef =
6370 {
6371     "M_OPTTTL",
6372     "OPTIONS",
6373     OGL_LightingMenu,
6374     M_DrawGenericMenu,
6375     NULL,
6376     sizeof(OGL_LightingMenu)/sizeof(menuitem_t),
6377     60,40,
6378     0,
6379 };
6380 
6381 menu_t  OGL_FogDef =
6382 {
6383     "M_OPTTTL",
6384     "OPTIONS",
6385     OGL_FogMenu,
6386     M_OGL_DrawFogMenu,
6387     NULL,
6388     sizeof(OGL_FogMenu)/sizeof(menuitem_t),
6389     60,40,
6390     0,
6391 };
6392 
6393 menu_t  OGL_ColorDef =
6394 {
6395     "M_OPTTTL",
6396     "OPTIONS",
6397     OGL_ColorMenu,
6398     M_OGL_DrawColorMenu,
6399     NULL,
6400     sizeof(OGL_ColorMenu)/sizeof(menuitem_t),
6401     60,40,
6402     0,
6403 };
6404 
6405 menu_t  OGL_DevDef =
6406 {
6407     "M_OPTTTL",
6408     "OPTIONS",
6409     OGL_DevMenu,
6410     M_DrawGenericMenu,
6411     NULL,
6412     sizeof(OGL_DevMenu)/sizeof(menuitem_t),
6413     60,40,
6414     0,
6415 };
6416 
6417 
6418 //======================================================================
6419 // M_DrawOpenGLMenu()
6420 //======================================================================
M_DrawOpenGLMenu(void)6421 void M_DrawOpenGLMenu(void)
6422 {
6423     int             mx,my;
6424 
6425     mx = OpenGLOptionDef.x;
6426     my = OpenGLOptionDef.y;
6427     M_DrawGenericMenu(); // use generic drawer for cursor, items and title
6428     // Draw the number in the Quality menu item.
6429     V_DrawString(BASEVIDWIDTH-mx-V_StringWidth(cv_scr_depth.string),
6430                  my+currentMenu->menuitems[QUALITY_ITEM].alphaKey,
6431                  V_WHITEMAP,
6432                  cv_scr_depth.string);
6433 }
6434 
6435 
6436 //======================================================================
6437 // M_OGL_DrawFogMenu()
6438 //======================================================================
M_OGL_DrawFogMenu(void)6439 void M_OGL_DrawFogMenu(void)
6440 {
6441     int             mx,my;
6442 
6443     mx = OGL_FogDef.x;
6444     my = OGL_FogDef.y;
6445     M_DrawGenericMenu(); // use generic drawer for cursor, items and title
6446     // Draw the fog color number in the menu.
6447     V_DrawString(BASEVIDWIDTH-mx-V_StringWidth (cv_grfogcolor.string),
6448                  my+currentMenu->menuitems[FOG_COLOR_ITEM].alphaKey,
6449                  V_WHITEMAP,
6450                  cv_grfogcolor.string);
6451     if (itemOn==FOG_COLOR_ITEM && skullAnimCounter<4) //blink cursor on FOG_COLOR_ITEM if selected
6452         V_DrawCharacter( BASEVIDWIDTH-mx, my+currentMenu->menuitems[FOG_COLOR_ITEM].alphaKey, '_' | 0x80);  // white
6453 }
6454 
6455 
6456 //======================================================================
6457 // M_OGL_DrawColorMenu()
6458 //======================================================================
6459 static
M_OGL_DrawColorMenu(void)6460 void M_OGL_DrawColorMenu(void)
6461 {
6462     int             mx,my;
6463 
6464     mx = OGL_ColorDef.x;
6465     my = OGL_ColorDef.y;
6466     M_DrawGenericMenu(); // use generic drawer for cursor, items and title
6467     V_DrawString(mx, my+currentMenu->menuitems[0].alphaKey-10,
6468                  V_WHITEMAP,"Gamma correction");
6469 }
6470 
6471 
6472 //======================================================================
6473 // M_OpenGLOption()
6474 //======================================================================
6475 static
M_OpenGLOption(int choice)6476 void M_OpenGLOption(int choice)
6477 {
6478     if( rendermode != render_soft )
6479         Push_Setup_Menu(&OpenGLOptionDef);
6480     else
6481         M_SimpleMessage("You are in software mode\nYou cannot change GL options\n");
6482 }
6483 
6484 
6485 //======================================================================
6486 // M_HandleFogColor()
6487 //======================================================================
6488 static
M_HandleFogColor(int key)6489 void M_HandleFogColor(int key)
6490 {
6491     int      i, l;
6492     char     temp[8];
6493     boolean  exitmenu = false;  // exit to previous menu and send name change
6494 
6495     switch( key )
6496     {
6497       case KEY_DOWNARROW:
6498         S_StartSound(menu_sfx_updown);
6499         itemOn++;
6500         break;
6501 
6502       case KEY_UPARROW:
6503         S_StartSound(menu_sfx_updown);
6504         itemOn--;
6505         break;
6506 
6507       case KEY_ESCAPE:
6508         S_StartSound(menu_sfx_esc);
6509         exitmenu = true;
6510         break;
6511 
6512       case KEY_BACKSPACE:
6513         S_StartSound(menu_sfx_val);
6514         strcpy(temp, cv_grfogcolor.string);
6515         strcpy(cv_grfogcolor.string, "000000");
6516         l = strlen(temp)-1;
6517         for (i=0; i<l; i++)
6518             cv_grfogcolor.string[i+6-l] = temp[i];
6519         break;
6520 
6521       default:
6522         input_char = tolower(input_char);
6523         if ((input_char >= '0' && input_char <= '9') ||
6524             (input_char >= 'a' && input_char <= 'f'))
6525         {
6526             S_StartSound(menu_sfx_val);
6527             strcpy(temp, cv_grfogcolor.string);
6528             strcpy(cv_grfogcolor.string, "000000");
6529             l = strlen(temp);
6530             for (i=0; i<l; i++)
6531                 cv_grfogcolor.string[5-i] = temp[l-i];
6532             cv_grfogcolor.string[5] = input_char;
6533         }
6534         break;
6535     }
6536     if (exitmenu)
6537     {
6538         Pop_Menu();
6539     }
6540 }
6541 
6542 #endif
6543 
6544 
6545 
6546 //===========================================================================
6547 // Register Menu cv_ commands that do not have another Init to use.
M_Register_Menu_Controls(void)6548 void M_Register_Menu_Controls( void )
6549 {
6550     // Any cv_ with CV_SAVE needs to be registered, even if it is not used.
6551     // Otherwise there will be error messages when config is loaded.
6552 
6553     // Player1
6554     CV_RegisterVar(&cv_autorun[0]);
6555     CV_RegisterVar(&cv_crosshair[0]);
6556 
6557     // Player2
6558     CV_RegisterVar(&cv_autorun[1]);
6559     CV_RegisterVar(&cv_crosshair[1]);
6560 
6561     //CV_RegisterVar (&cv_crosshairscale); // doesn't work for now
6562     CV_RegisterVar(&cv_showmessages);
6563     //CV_RegisterVar (&cv_showmessages2);
6564     //CV_RegisterVar (&cv_controlperkey2);
6565 
6566     CV_RegisterVar(&cv_screenslink);
6567 
6568     // p_mobj.c
6569     CV_RegisterVar(&cv_itemrespawntime);
6570     CV_RegisterVar(&cv_itemrespawn);
6571     CV_RegisterVar(&cv_respawnmonsters);
6572     CV_RegisterVar(&cv_respawnmonsterstime);
6573     CV_RegisterVar(&cv_fastmonsters);
6574     CV_RegisterVar(&cv_predictingmonsters);     //added by AC for predmonsters
6575     CV_RegisterVar(&cv_splats);
6576     CV_RegisterVar(&cv_maxsplats);
6577 
6578     // misc
6579     CV_RegisterVar(&cv_deathmatch);  // after cv_itemrespawn
6580     CV_RegisterVar(&cv_teamplay);
6581     CV_RegisterVar(&cv_teamdamage);
6582     CV_RegisterVar(&cv_timelimit);
6583     CV_RegisterVar(&cv_fraglimit);
6584 
6585     // d_clisrv
6586     CV_RegisterVar(&cv_playdemospeed);
6587     CV_RegisterVar(&cv_server1);
6588     CV_RegisterVar(&cv_server2);
6589     CV_RegisterVar(&cv_server3);
6590 
6591     // p_inter
6592     CV_RegisterVar(&cv_fragsweaponfalling);
6593 
6594     // g_game
6595     CV_RegisterVar(&cv_allowjump);
6596     CV_RegisterVar(&cv_allowrocketjump);
6597     CV_RegisterVar(&cv_allowautoaim);
6598     CV_RegisterVar(&cv_allowturbo);
6599     CV_RegisterVar(&cv_allowmlook);
6600     CV_RegisterVar(&cv_allowexitlevel);
6601 
6602     //g_input.c
6603     CV_RegisterVar(&cv_grabinput);
6604     // WARNING : the order is important when init mouse
6605     // Call of mouse1 init occurs with Register cv_usemouse[1].
6606 #ifdef SMIF_SDL
6607     CV_RegisterVar(&cv_mouse_motion);
6608 #endif
6609     // Call of mouse1 init occurs here.
6610     CV_RegisterVar(&cv_usemouse[0]);
6611     CV_RegisterVar(&cv_alwaysfreelook[0]);
6612     CV_RegisterVar(&cv_mouse_move[0]);
6613     CV_RegisterVar(&cv_mouse_invert);
6614     CV_RegisterVar(&cv_mouse_sens_x);
6615     CV_RegisterVar(&cv_mouse_sens_y);
6616 
6617     // WARNING : the order is important when init mouse2
6618 #ifdef MOUSE2
6619 #if defined( MOUSE2_NIX ) || defined( MOUSE2_WIN )
6620     // Call of mouse2 init occurs with Register cv_usemouse[1].
6621 #if defined( SMIF_SDL ) || defined( SMIF_WIN32 )
6622     CV_RegisterVar(&cv_mouse2type);
6623 #endif
6624     CV_RegisterVar(&cv_mouse2port);
6625     CV_RegisterVar(&cv_mouse2opt);
6626 #endif
6627 #endif
6628 
6629     // Call of mouse2 init occurs here.
6630     CV_RegisterVar(&cv_usemouse[1]);
6631     CV_RegisterVar(&cv_alwaysfreelook[1]);
6632     CV_RegisterVar(&cv_mouse_move[1]);
6633 #ifdef MOUSE2
6634     CV_RegisterVar(&cv_mouse2_invert);
6635     CV_RegisterVar(&cv_mouse2_sens_x);
6636     CV_RegisterVar(&cv_mouse2_sens_y);
6637 #endif
6638 
6639     CV_RegisterVar(&cv_mouse_double);
6640 #ifdef JOY_BUTTONS_DOUBLE
6641     CV_RegisterVar(&cv_joy_double);
6642 #endif
6643     CV_RegisterVar(&cv_joy_deadzone);
6644 
6645     CV_RegisterVar(&cv_controlperkey);
6646 
6647     //s_sound.c
6648     CV_RegisterVar(&cv_soundvolume);
6649     CV_RegisterVar(&cv_musicvolume);
6650     CV_RegisterVar(&cv_numChannels);
6651     CV_RegisterVar(&cv_rndsoundpitch);
6652 
6653 #ifdef CDMUS
6654     //i_cdmus.c
6655     CV_RegisterVar(&cd_volume);
6656 #endif
6657 
6658     // p_lights.c
6659     CV_RegisterVar(&cv_corona);
6660     CV_RegisterVar(&cv_coronasize);
6661     CV_RegisterVar(&cv_corona_draw_mode);
6662 //  CV_RegisterVar(&cv_dynamiclighting);
6663 //  CV_RegisterVar(&cv_staticlighting);
6664     CV_RegisterVar(&cv_monball_light);
6665 }
6666 
6667 
6668 #ifdef LAUNCHER
6669 //===========================================================================
6670 //                        LAUNCH MENU
6671 //===========================================================================
6672 
6673 // cv_ menu items
6674 // out only
6675 consvar_t cv_switch = {"switches", "", CV_HIDEN, NULL};
6676 consvar_t cv_config = {"config", "", CV_HIDEN, NULL};
6677 
6678 static void CV_game_OnChange(void);
6679 CV_PossibleValue_t game_cons_t[] = {{-1,"MIN"},{64,"MAX"},{0,NULL}};
6680 consvar_t cv_game = {"game", "-1", CV_HIDEN|CV_CALL, NULL, CV_game_OnChange};
6681 
CV_game_OnChange(void)6682 static void CV_game_OnChange(void)
6683 {
6684     // Strings come from GameDesc, not a CV_PossibleValue_t
6685     // Cannot call CV_Set within CV_CALL routine
6686     const char * rs;
6687     game_desc_t * gamedesc = D_GameDesc( cv_game.value );
6688     if( gamedesc )
6689     {
6690         rs = gamedesc->gname;  // const string from table
6691     }
6692     else
6693     {
6694         rs = "Auto";
6695         cv_game.value = (cv_game.value <1)? -1 : GDESC_other;
6696     }
6697     // Update display string from GameDesc.
6698     if( cv_game.string )
6699        Z_Free( cv_game.string );  // remove the string from CV_Set
6700     cv_game.string = Z_StrDup( rs );
6701     return;
6702 }
6703 
6704 
6705 
M_LaunchCont(void)6706 static void M_LaunchCont( void )
6707 {
6708     init_sequence = 2;
6709     M_Clear_Menus(true);
6710 }
6711 
6712 menuitem_t LaunchMenu[]=
6713 {
6714     {IT_WHITESTRING | IT_CVAR | IT_CV_STRING, NULL, "Home", &cv_home,  0},
6715     {IT_WHITESTRING | IT_CVAR | IT_CV_STRING, NULL, "Doomwaddir", &cv_doomwaddir, 0},
6716     {IT_WHITESTRING | IT_CVAR | IT_CV_STRING, NULL, "Config", &cv_config,  0},
6717     {IT_WHITESTRING | IT_CVAR | IT_CV_STRING, NULL, "Switch", &cv_switch, 0},
6718     {IT_WHITESTRING | IT_CVAR | IT_CV_STRING, NULL, "Iwad", &cv_iwad, 0},
6719  // the CVAR strings will intercept the 'g', 'c', and 'q'
6720     {IT_WHITESTRING | IT_CVAR, NULL, "Game", &cv_game,  'g'},
6721     {IT_WHITESTRING | IT_CALL, NULL, "Continue", M_LaunchCont, 'c'},
6722     {IT_WHITESTRING | IT_CALL, NULL, "QUIT GAME", M_QuitDOOM, 'q'}
6723 };
6724 
6725 menu_t  LaunchDef =
6726 {
6727     NULL,
6728     "Launch",
6729     LaunchMenu,
6730     M_DrawGenericMenu,
6731     NULL,
6732     sizeof(LaunchMenu)/sizeof(menuitem_t),
6733     10,10,  // x, y  (cursor at x-10)
6734     0
6735 };
6736 
6737 // call only after memory, video, command, and menu inits
M_LaunchMenu(void)6738 void M_LaunchMenu( void )
6739 {
6740     M_StartControlPanel();
6741     Push_Setup_Menu (&LaunchDef);
6742 
6743     M_Clear_Add_Param();  // clear previous param from this routine
6744     if( cv_game.string == NULL ) // first time inits
6745     {
6746 //        CV_RegisterVar(&cv_iwad);
6747         CV_RegisterVar(&cv_config);
6748         CV_RegisterVar(&cv_switch);
6749         CV_RegisterVar(&cv_game);
6750     }
6751     skullAnimCounter = 0;
6752 
6753     do
6754     {
6755         V_Clear_Display();
6756         I_OsPolling();
6757         D_Process_Events ();  // menu responder
6758         M_Drawer(); // menu drawer
6759         I_UpdateNoBlit();
6760         I_FinishUpdate();       // page flip or blit buffer
6761     } while( menuactive );
6762 
6763     // add home
6764     if(cv_home.state & CS_MODIFIED)
6765         M_Change_2Param( "-home", cv_home.string );
6766     // add doomwaddir
6767     if( (cv_doomwaddir.state & CS_MODIFIED) && cv_doomwaddir.string )
6768         doomwaddir[0] = cv_doomwaddir.string;
6769     // add config
6770     if(cv_config.state & CS_MODIFIED)
6771         M_Change_2Param( "-config", cv_config.string );
6772     // add iwad
6773     if(cv_iwad.state & CS_MODIFIED)
6774         M_Change_2Param( "-iwad", cv_iwad.string );
6775     // add switches
6776     if( (cv_switch.state & CS_MODIFIED) && cv_switch.string[0] )
6777         M_Add_Param( cv_switch.string, NULL );
6778     // add game
6779     if( (cv_game.state & CS_MODIFIED)
6780         && cv_game.value >= 0 && cv_game.value < GDESC_other)
6781     {
6782         game_desc_t * gamedesc = D_GameDesc( cv_game.value );
6783         if( gamedesc )
6784             M_Add_Param( "-game", gamedesc->idstr );
6785     }
6786 }
6787 
6788 #endif
6789