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