]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - menu.c
***map loader generates portals for the map*** (can you tell this is a big deal? :)
[xonotic/darkplaces.git] / menu.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19 */
20 #include "quakedef.h"
21
22 #ifdef _WIN32
23 #include "winquake.h"
24 #endif
25
26 void (*vid_menudrawfn)(void);
27 void (*vid_menukeyfn)(int key);
28
29 #define TYPE_DEMO 1
30 #define TYPE_GAME 2
31 #define TYPE_BOTH 3
32
33 int NehGameType;
34
35 enum {m_none, m_main, m_demo, m_singleplayer, m_load, m_save, m_multiplayer, m_setup, m_net, m_options, m_video, m_keys, m_help, m_quit, m_lanconfig, m_gameoptions, m_search, m_slist} m_state;
36
37 void M_Menu_Main_f (void);
38         void M_Menu_SinglePlayer_f (void);
39                 void M_Menu_Load_f (void);
40                 void M_Menu_Save_f (void);
41         void M_Menu_MultiPlayer_f (void);
42                 void M_Menu_Setup_f (void);
43                 void M_Menu_Net_f (void);
44         void M_Menu_Options_f (void);
45                 void M_Menu_Keys_f (void);
46                 void M_Menu_Video_f (void);
47         void M_Menu_Help_f (void);
48         void M_Menu_Quit_f (void);
49 void M_Menu_LanConfig_f (void);
50 void M_Menu_GameOptions_f (void);
51 void M_Menu_Search_f (void);
52 void M_Menu_ServerList_f (void);
53
54 void M_Main_Draw (void);
55         void M_SinglePlayer_Draw (void);
56                 void M_Load_Draw (void);
57                 void M_Save_Draw (void);
58         void M_MultiPlayer_Draw (void);
59                 void M_Setup_Draw (void);
60                 void M_Net_Draw (void);
61         void M_Options_Draw (void);
62                 void M_Keys_Draw (void);
63                 void M_Video_Draw (void);
64         void M_Help_Draw (void);
65         void M_Quit_Draw (void);
66 void M_LanConfig_Draw (void);
67 void M_GameOptions_Draw (void);
68 void M_Search_Draw (void);
69 void M_ServerList_Draw (void);
70
71 void M_Main_Key (int key);
72         void M_SinglePlayer_Key (int key);
73                 void M_Load_Key (int key);
74                 void M_Save_Key (int key);
75         void M_MultiPlayer_Key (int key);
76                 void M_Setup_Key (int key);
77                 void M_Net_Key (int key);
78         void M_Options_Key (int key);
79                 void M_Keys_Key (int key);
80                 void M_Video_Key (int key);
81         void M_Help_Key (int key);
82         void M_Quit_Key (int key);
83 void M_LanConfig_Key (int key);
84 void M_GameOptions_Key (int key);
85 void M_Search_Key (int key);
86 void M_ServerList_Key (int key);
87
88 qboolean        m_entersound;           // play after drawing a frame, so caching
89                                                                 // won't disrupt the sound
90 //qboolean      m_recursiveDraw;
91
92 int                     m_return_state;
93 qboolean        m_return_onerror;
94 char            m_return_reason [32];
95
96 #define StartingGame    (m_multiplayer_cursor == 1)
97 #define JoiningGame             (m_multiplayer_cursor == 0)
98 #define IPXConfig               (m_net_cursor == 0)
99 #define TCPIPConfig             (m_net_cursor == 1)
100
101 void M_ConfigureNetSubsystem(void);
102
103 // Nehahra
104 int NumberOfDemos;
105 typedef struct
106 {
107         char name[50];
108         char desc[50];
109 } demonames_t;
110
111 demonames_t Demos[35];
112
113 /*
114 ================
115 M_DrawCharacter
116
117 Draws one solid graphics character
118 ================
119 */
120 void M_DrawCharacter (int cx, int line, int num)
121 {
122         Draw_Character ( cx + ((vid.width - 320)>>1), line, num);
123 }
124
125 void M_Print (int cx, int cy, char *str)
126 {
127         while (*str)
128         {
129                 M_DrawCharacter (cx, cy, (*str)+128);
130                 str++;
131                 cx += 8;
132         }
133 }
134
135 void M_PrintWhite (int cx, int cy, char *str)
136 {
137         while (*str)
138         {
139                 M_DrawCharacter (cx, cy, *str);
140                 str++;
141                 cx += 8;
142         }
143 }
144
145 void M_DrawPic (int x, int y, qpic_t *pic)
146 {
147         Draw_Pic (x + ((vid.width - 320)>>1), y, pic);
148 }
149
150 byte identityTable[256];
151 byte translationTable[256];
152
153 void M_BuildTranslationTable(int top, int bottom)
154 {
155         int             j;
156         byte    *dest, *source;
157
158         for (j = 0; j < 256; j++)
159                 identityTable[j] = j;
160         dest = translationTable;
161         source = identityTable;
162         memcpy (dest, source, 256);
163
164         // LordHavoc: corrected skin color ranges
165         if (top < 128 || (top >= 224 && top < 240))     // the artists made some backwards ranges.  sigh.
166                 memcpy (dest + TOP_RANGE, source + top, 16);
167         else
168                 for (j=0 ; j<16 ; j++)
169                         dest[TOP_RANGE+j] = source[top+15-j];
170
171         // LordHavoc: corrected skin color ranges
172         if (bottom < 128 || (bottom >= 224 && bottom < 240))
173                 memcpy (dest + BOTTOM_RANGE, source + bottom, 16);
174         else
175                 for (j=0 ; j<16 ; j++)
176                         dest[BOTTOM_RANGE+j] = source[bottom+15-j];
177 }
178
179
180 void M_DrawPicTranslate (int x, int y, qpic_t *pic)
181 {
182         Draw_PicTranslate (x + ((vid.width - 320)>>1), y, pic, translationTable);
183 }
184
185
186 void M_DrawTextBox (int x, int y, int width, int lines)
187 {
188         qpic_t  *p;
189         int             cx, cy;
190         int             n;
191
192         // draw left side
193         cx = x;
194         cy = y;
195         p = Draw_CachePic ("gfx/box_tl.lmp");
196         M_DrawPic (cx, cy, p);
197         p = Draw_CachePic ("gfx/box_ml.lmp");
198         for (n = 0; n < lines; n++)
199         {
200                 cy += 8;
201                 M_DrawPic (cx, cy, p);
202         }
203         p = Draw_CachePic ("gfx/box_bl.lmp");
204         M_DrawPic (cx, cy+8, p);
205
206         // draw middle
207         cx += 8;
208         while (width > 0)
209         {
210                 cy = y;
211                 p = Draw_CachePic ("gfx/box_tm.lmp");
212                 M_DrawPic (cx, cy, p);
213                 p = Draw_CachePic ("gfx/box_mm.lmp");
214                 for (n = 0; n < lines; n++)
215                 {
216                         cy += 8;
217                         if (n == 1)
218                                 p = Draw_CachePic ("gfx/box_mm2.lmp");
219                         M_DrawPic (cx, cy, p);
220                 }
221                 p = Draw_CachePic ("gfx/box_bm.lmp");
222                 M_DrawPic (cx, cy+8, p);
223                 width -= 2;
224                 cx += 16;
225         }
226
227         // draw right side
228         cy = y;
229         p = Draw_CachePic ("gfx/box_tr.lmp");
230         M_DrawPic (cx, cy, p);
231         p = Draw_CachePic ("gfx/box_mr.lmp");
232         for (n = 0; n < lines; n++)
233         {
234                 cy += 8;
235                 M_DrawPic (cx, cy, p);
236         }
237         p = Draw_CachePic ("gfx/box_br.lmp");
238         M_DrawPic (cx, cy+8, p);
239 }
240
241 //=============================================================================
242
243 int m_save_demonum;
244
245 /*
246 ================
247 M_ToggleMenu_f
248 ================
249 */
250 void M_ToggleMenu_f (void)
251 {
252         m_entersound = true;
253
254         if (key_dest == key_menu)
255         {
256                 if (m_state != m_main)
257                 {
258                         M_Menu_Main_f ();
259                         return;
260                 }
261                 key_dest = key_game;
262                 m_state = m_none;
263                 return;
264         }
265         if (key_dest == key_console)
266         {
267                 Con_ToggleConsole_f ();
268         }
269         else
270         {
271                 M_Menu_Main_f ();
272         }
273 }
274
275 // LordHavoc: FIXME: finish this menu stuff
276 #if 0
277 #define MAXMENUITEMS 128
278
279 typedef struct menuitem_s
280 {
281         char *string; // may be text, or an image to use, or a cvar name, depending on the functions used
282         char *description;
283         char *command; // used by command items mainly (when used, this command is executed)
284         cvar_t *cvar; // used for cvar items (sliders, number boxes), value is retrieved from the cvar itself
285         int selectable; // purely decorative if this is false
286         int selected; // true if this menu item is currently selected, used by funcs so they don't need to know anything but fields in the menuitem
287         float selecttime; // the time that this menu item was activated (copied from realtime), used for animating selection flashs and such
288         float color[4]; // current color for the item (may be different than base color, due to selection flash effects)
289         float basecolor[4]; // the base color
290         float x, y, width, height; // width and height are used for mouse selection
291         void(*drawfunc)(struct menuitem_s *item);
292         void(*activefunc)(struct menuitem_s *item);
293 //      void(*selectfunc)(struct menuitem_s *item);
294 //      void(*deselectfunc)(struct menuitem_s *item);
295         void(*usefunc)(struct menuitem_s *item);
296 }
297 menuitem_t;
298
299 menuitem_t menuitem[MAXMENUITEMS];
300 int menuitems;
301
302 void menuitem_text_drawfunc(struct menuitem_s *item)
303 {
304         // FIXME: handle color flashs and such when selected
305         M_Print (item->x, item->y, item->string);
306 }
307
308 void menuitem_image_drawfunc(struct menuitem_s *item)
309 {
310         qpic_t *p = Draw_CachePic (item->string);
311         // FIXME: handle color flashs and such when selected
312         M_DrawPic (item->x, item->y, p);
313 }
314
315 void menuitem_command_usefunc(struct menuitem_s *item)
316 {
317         Cbuf_AddText (item->command);
318 }
319 #endif
320
321 int demo_cursor;
322 void M_Demo_Draw (void)
323 {
324         int             i;
325
326         for (i=0; i < NumberOfDemos; i++)
327                 M_Print (16, 16 + 8*i, Demos[i].desc);
328
329         // line cursor
330         M_DrawCharacter (8, 16 + demo_cursor*8, 12+((int)(realtime*4)&1));
331 }
332
333
334 void M_Menu_Demos_f (void)
335 {
336         key_dest = key_menu;
337         m_state = m_demo;
338         m_entersound = true;
339
340
341         NumberOfDemos = 34;
342
343         strcpy(Demos[0].name,  "intro");         strcpy(Demos[0].desc,  "Prologue");
344         strcpy(Demos[1].name,  "genf");          strcpy(Demos[1].desc,  "The Beginning");
345         strcpy(Demos[2].name,  "genlab");        strcpy(Demos[2].desc,  "A Doomed Project");
346         strcpy(Demos[3].name,  "nehcre");        strcpy(Demos[3].desc,  "The New Recruits");
347         strcpy(Demos[4].name,  "maxneh");        strcpy(Demos[4].desc,  "Breakthrough");
348         strcpy(Demos[5].name,  "maxchar");       strcpy(Demos[5].desc,  "Renewal and Duty");
349         strcpy(Demos[6].name,  "crisis");        strcpy(Demos[6].desc,  "Worlds Collide");
350         strcpy(Demos[7].name,  "postcris");      strcpy(Demos[7].desc,  "Darkening Skies");
351         strcpy(Demos[8].name,  "hearing");       strcpy(Demos[8].desc,  "The Hearing");
352         strcpy(Demos[9].name,  "getjack");       strcpy(Demos[9].desc,  "On a Mexican Radio");
353         strcpy(Demos[10].name, "prelude");       strcpy(Demos[10].desc, "Honor and Justice");
354         strcpy(Demos[11].name, "abase");         strcpy(Demos[11].desc, "A Message Sent");
355         strcpy(Demos[12].name, "effect");        strcpy(Demos[12].desc, "The Other Side");
356         strcpy(Demos[13].name, "uhoh");          strcpy(Demos[13].desc, "Missing in Action");
357         strcpy(Demos[14].name, "prepare");       strcpy(Demos[14].desc, "The Response");
358         strcpy(Demos[15].name, "vision");        strcpy(Demos[15].desc, "Farsighted Eyes");
359         strcpy(Demos[16].name, "maxturns");      strcpy(Demos[16].desc, "Enter the Immortal");
360         strcpy(Demos[17].name, "backlot");       strcpy(Demos[17].desc, "Separate Ways");
361         strcpy(Demos[18].name, "maxside");       strcpy(Demos[18].desc, "The Ancient Runes");
362         strcpy(Demos[19].name, "counter");       strcpy(Demos[19].desc, "The New Initiative");
363         strcpy(Demos[20].name, "warprep");       strcpy(Demos[20].desc, "Ghosts to the World");
364         strcpy(Demos[21].name, "counter1");      strcpy(Demos[21].desc, "A Fate Worse Than Death");
365         strcpy(Demos[22].name, "counter2");      strcpy(Demos[22].desc, "Friendly Fire");
366         strcpy(Demos[23].name, "counter3");      strcpy(Demos[23].desc, "Minor Setback");
367         strcpy(Demos[24].name, "madmax");        strcpy(Demos[24].desc, "Scores to Settle");
368         strcpy(Demos[25].name, "quake");         strcpy(Demos[25].desc, "One Man");
369         strcpy(Demos[26].name, "cthmm");         strcpy(Demos[26].desc, "Shattered Masks");
370         strcpy(Demos[27].name, "shades");        strcpy(Demos[27].desc, "Deal with the Dead");
371         strcpy(Demos[28].name, "gophil");        strcpy(Demos[28].desc, "An Unlikely Hero");
372         strcpy(Demos[29].name, "cstrike");       strcpy(Demos[29].desc, "War in Hell");
373         strcpy(Demos[30].name, "shubset");       strcpy(Demos[30].desc, "The Conspiracy");
374         strcpy(Demos[31].name, "shubdie");       strcpy(Demos[31].desc, "Even Death May Die");
375         strcpy(Demos[32].name, "newranks");      strcpy(Demos[32].desc, "An Empty Throne");
376         strcpy(Demos[33].name, "seal");          strcpy(Demos[33].desc, "The Seal is Broken");
377 }
378
379 void M_Demo_Key (int k)
380 {
381         switch (k)
382         {
383         case K_ESCAPE:
384                 M_Menu_Main_f ();
385                 break;
386
387         case K_ENTER:
388                 S_LocalSound ("misc/menu2.wav");
389                 m_state = m_none;
390                 key_dest = key_game;
391 //              SCR_BeginLoadingPlaque ();
392                 Cbuf_AddText (va ("playdemo %s\n", Demos[demo_cursor].name));
393                 return;
394
395         case K_UPARROW:
396         case K_LEFTARROW:
397                 S_LocalSound ("misc/menu1.wav");
398                 demo_cursor--;
399                 if (demo_cursor < 0)
400                         demo_cursor = NumberOfDemos;
401                 break;
402
403         case K_DOWNARROW:
404         case K_RIGHTARROW:
405                 S_LocalSound ("misc/menu1.wav");
406                 demo_cursor++;
407                 if (demo_cursor > NumberOfDemos)
408                         demo_cursor = 0;
409                 break;
410         }
411 }
412
413 //=============================================================================
414 /* MAIN MENU */
415
416 int     m_main_cursor;
417 //#define       MAIN_ITEMS      5
418
419 int MAIN_ITEMS = 4; // Nehahra: Menu Disable
420
421 void M_Menu_Main_f (void)
422 {
423         if (nehahra)
424         {
425                 if (NehGameType == TYPE_DEMO)
426                         MAIN_ITEMS = 4;
427                 else if (NehGameType == TYPE_GAME)
428                         MAIN_ITEMS = 5;
429                 else
430                         MAIN_ITEMS = 6;
431         }
432         else
433                 MAIN_ITEMS = 5;
434
435         if (key_dest != key_menu)
436         {
437                 m_save_demonum = cls.demonum;
438                 cls.demonum = -1;
439         }
440         key_dest = key_menu;
441         m_state = m_main;
442         m_entersound = true;
443 }
444
445
446 void M_Main_Draw (void)
447 {
448         int             f;
449         qpic_t  *p;
450
451         M_DrawPic (16, 4, Draw_CachePic ("gfx/qplaque.lmp") );
452         p = Draw_CachePic ("gfx/ttl_main.lmp");
453         M_DrawPic ( (320-p->width)/2, 4, p);
454 // Nehahra
455         if (nehahra)
456         {
457                 if (NehGameType == TYPE_BOTH)
458                         M_DrawPic (72, 32, Draw_CachePic ("gfx/mainmenu.lmp"));
459                 else if (NehGameType == TYPE_GAME)
460                         M_DrawPic (72, 32, Draw_CachePic ("gfx/gamemenu.lmp"));
461                 else
462                         M_DrawPic (72, 32, Draw_CachePic ("gfx/demomenu.lmp"));
463         }
464         else
465                 M_DrawPic (72, 32, Draw_CachePic ("gfx/mainmenu.lmp"));
466
467         f = (int)(realtime * 10)%6;
468
469         M_DrawPic (54, 32 + m_main_cursor * 20,Draw_CachePic( va("gfx/menudot%i.lmp", f+1 ) ) );
470 }
471
472
473 void M_Main_Key (int key)
474 {
475         switch (key)
476         {
477         case K_ESCAPE:
478                 key_dest = key_game;
479                 m_state = m_none;
480                 cls.demonum = m_save_demonum;
481                 if (cls.demonum != -1 && !cls.demoplayback && cls.state != ca_connected)
482                         CL_NextDemo ();
483                 break;
484
485         case K_DOWNARROW:
486                 S_LocalSound ("misc/menu1.wav");
487                 if (++m_main_cursor >= MAIN_ITEMS)
488                         m_main_cursor = 0;
489                 break;
490
491         case K_UPARROW:
492                 S_LocalSound ("misc/menu1.wav");
493                 if (--m_main_cursor < 0)
494                         m_main_cursor = MAIN_ITEMS - 1;
495                 break;
496
497         case K_ENTER:
498                 m_entersound = true;
499
500                 if (nehahra)
501                 {
502                         switch (NehGameType)
503                         {
504                         case TYPE_BOTH:
505                                 switch (m_main_cursor)
506                                 {
507                                 case 0:
508                                         M_Menu_SinglePlayer_f ();
509                                         break;
510
511                                 case 1:
512                                         M_Menu_Demos_f ();
513                                         break;
514
515                                 case 2:
516                                         M_Menu_MultiPlayer_f ();
517                                         break;
518
519                                 case 3:
520                                         M_Menu_Options_f ();
521                                         break;
522
523                                 case 4:
524                                         key_dest = key_game;
525                                         if (sv.active)
526                                                 Cbuf_AddText ("disconnect\n");
527                                         Cbuf_AddText ("playdemo endcred\n");
528                                         break;
529
530                                 case 5:
531                                         M_Menu_Quit_f ();
532                                         break;
533                                 }
534                                 break;
535                         case TYPE_GAME:
536                                 switch (m_main_cursor)
537                                 {
538                                 case 0:
539                                         M_Menu_SinglePlayer_f ();
540                                         break;
541
542                                 case 1:
543                                         M_Menu_MultiPlayer_f ();
544                                         break;
545
546                                 case 2:
547                                         M_Menu_Options_f ();
548                                         break;
549
550                                 case 3:
551                                         key_dest = key_game;
552                                         if (sv.active)
553                                                 Cbuf_AddText ("disconnect\n");
554                                         Cbuf_AddText ("playdemo endcred\n");
555                                         break;
556
557                                 case 4:
558                                         M_Menu_Quit_f ();
559                                         break;
560                                 }
561                                 break;
562                         case TYPE_DEMO:
563                                 switch (m_main_cursor)
564                                 {
565                                 case 0:
566                                         M_Menu_Demos_f ();
567                                         break;
568
569                                 case 1:
570                                         key_dest = key_game;
571                                         if (sv.active)
572                                                 Cbuf_AddText ("disconnect\n");
573                                         Cbuf_AddText ("playdemo endcred\n");
574                                         break;
575
576                                 case 2:
577                                         M_Menu_Options_f ();
578                                         break;
579
580                                 case 3:
581                                         M_Menu_Quit_f ();
582                                         break;
583                                 }
584                                 break;
585                         }
586                 }
587                 else
588                 {
589                         switch (m_main_cursor)
590                         {
591                         case 0:
592                                 M_Menu_SinglePlayer_f ();
593                                 break;
594
595                         case 1:
596                                 M_Menu_MultiPlayer_f ();
597                                 break;
598
599                         case 2:
600                                 M_Menu_Options_f ();
601                                 break;
602
603                         case 3:
604                                 M_Menu_Help_f ();
605                                 break;
606
607                         case 4:
608                                 M_Menu_Quit_f ();
609                                 break;
610                         }
611                 }
612         }
613 }
614
615 //=============================================================================
616 /* SINGLE PLAYER MENU */
617
618 int     m_singleplayer_cursor;
619 #define SINGLEPLAYER_ITEMS      3
620
621
622 void M_Menu_SinglePlayer_f (void)
623 {
624         key_dest = key_menu;
625         m_state = m_singleplayer;
626         m_entersound = true;
627 }
628
629
630 void M_SinglePlayer_Draw (void)
631 {
632         int             f;
633         qpic_t  *p;
634
635         M_DrawPic (16, 4, Draw_CachePic ("gfx/qplaque.lmp") );
636         p = Draw_CachePic ("gfx/ttl_sgl.lmp");
637         M_DrawPic ( (320-p->width)/2, 4, p);
638         M_DrawPic (72, 32, Draw_CachePic ("gfx/sp_menu.lmp") );
639
640         f = (int)(realtime * 10)%6;
641
642         M_DrawPic (54, 32 + m_singleplayer_cursor * 20,Draw_CachePic( va("gfx/menudot%i.lmp", f+1 ) ) );
643 }
644
645
646 void M_SinglePlayer_Key (int key)
647 {
648         switch (key)
649         {
650         case K_ESCAPE:
651                 M_Menu_Main_f ();
652                 break;
653
654         case K_DOWNARROW:
655                 S_LocalSound ("misc/menu1.wav");
656                 if (++m_singleplayer_cursor >= SINGLEPLAYER_ITEMS)
657                         m_singleplayer_cursor = 0;
658                 break;
659
660         case K_UPARROW:
661                 S_LocalSound ("misc/menu1.wav");
662                 if (--m_singleplayer_cursor < 0)
663                         m_singleplayer_cursor = SINGLEPLAYER_ITEMS - 1;
664                 break;
665
666         case K_ENTER:
667                 m_entersound = true;
668
669                 switch (m_singleplayer_cursor)
670                 {
671                 case 0:
672                         key_dest = key_game;
673                         if (sv.active)
674                                 Cbuf_AddText ("disconnect\n");
675                         Cbuf_AddText ("maxplayers 1\n");
676                         if (nehahra)
677                                 Cbuf_AddText ("map nehstart\n");
678                         else
679                                 Cbuf_AddText ("map start\n");
680                         break;
681
682                 case 1:
683                         M_Menu_Load_f ();
684                         break;
685
686                 case 2:
687                         M_Menu_Save_f ();
688                         break;
689                 }
690         }
691 }
692
693 //=============================================================================
694 /* LOAD/SAVE MENU */
695
696 int             load_cursor;            // 0 < load_cursor < MAX_SAVEGAMES
697
698 #define MAX_SAVEGAMES           12
699 char    m_filenames[MAX_SAVEGAMES][SAVEGAME_COMMENT_LENGTH+1];
700 int             loadable[MAX_SAVEGAMES];
701
702 void M_ScanSaves (void)
703 {
704         int             i, j;
705         char    name[MAX_OSPATH];
706         FILE    *f;
707         int             version;
708
709         for (i=0 ; i<MAX_SAVEGAMES ; i++)
710         {
711                 strcpy (m_filenames[i], "--- UNUSED SLOT ---");
712                 loadable[i] = false;
713                 sprintf (name, "%s/s%i.sav", com_gamedir, i);
714                 f = fopen (name, "r");
715                 if (!f)
716                         continue;
717                 fscanf (f, "%i\n", &version);
718                 fscanf (f, "%79s\n", name);
719                 strncpy (m_filenames[i], name, sizeof(m_filenames[i])-1);
720
721         // change _ back to space
722                 for (j=0 ; j<SAVEGAME_COMMENT_LENGTH ; j++)
723                         if (m_filenames[i][j] == '_')
724                                 m_filenames[i][j] = ' ';
725                 loadable[i] = true;
726                 fclose (f);
727         }
728 }
729
730 void M_Menu_Load_f (void)
731 {
732         m_entersound = true;
733         m_state = m_load;
734         key_dest = key_menu;
735         M_ScanSaves ();
736 }
737
738
739 void M_Menu_Save_f (void)
740 {
741         if (!sv.active)
742                 return;
743         if (cl.intermission)
744                 return;
745         if (svs.maxclients != 1)
746                 return;
747         m_entersound = true;
748         m_state = m_save;
749         key_dest = key_menu;
750         M_ScanSaves ();
751 }
752
753
754 void M_Load_Draw (void)
755 {
756         int             i;
757         qpic_t  *p;
758
759         p = Draw_CachePic ("gfx/p_load.lmp");
760         M_DrawPic ( (320-p->width)/2, 4, p);
761
762         for (i=0 ; i< MAX_SAVEGAMES; i++)
763                 M_Print (16, 32 + 8*i, m_filenames[i]);
764
765 // line cursor
766         M_DrawCharacter (8, 32 + load_cursor*8, 12+((int)(realtime*4)&1));
767 }
768
769
770 void M_Save_Draw (void)
771 {
772         int             i;
773         qpic_t  *p;
774
775         p = Draw_CachePic ("gfx/p_save.lmp");
776         M_DrawPic ( (320-p->width)/2, 4, p);
777
778         for (i=0 ; i<MAX_SAVEGAMES ; i++)
779                 M_Print (16, 32 + 8*i, m_filenames[i]);
780
781 // line cursor
782         M_DrawCharacter (8, 32 + load_cursor*8, 12+((int)(realtime*4)&1));
783 }
784
785
786 void M_Load_Key (int k)
787 {
788         switch (k)
789         {
790         case K_ESCAPE:
791                 M_Menu_SinglePlayer_f ();
792                 break;
793
794         case K_ENTER:
795                 S_LocalSound ("misc/menu2.wav");
796                 if (!loadable[load_cursor])
797                         return;
798                 m_state = m_none;
799                 key_dest = key_game;
800
801                 // LordHavoc: made SCR_UpdateScreen use a great deal less stack space, no longer an issue
802                 //// Host_Loadgame_f can't bring up the loading plaque because too much
803                 //// stack space has been used, so do it now
804 ////            SCR_BeginLoadingPlaque ();
805
806                 // issue the load command
807                 Cbuf_AddText (va ("load s%i\n", load_cursor) );
808                 return;
809
810         case K_UPARROW:
811         case K_LEFTARROW:
812                 S_LocalSound ("misc/menu1.wav");
813                 load_cursor--;
814                 if (load_cursor < 0)
815                         load_cursor = MAX_SAVEGAMES-1;
816                 break;
817
818         case K_DOWNARROW:
819         case K_RIGHTARROW:
820                 S_LocalSound ("misc/menu1.wav");
821                 load_cursor++;
822                 if (load_cursor >= MAX_SAVEGAMES)
823                         load_cursor = 0;
824                 break;
825         }
826 }
827
828
829 void M_Save_Key (int k)
830 {
831         switch (k)
832         {
833         case K_ESCAPE:
834                 M_Menu_SinglePlayer_f ();
835                 break;
836
837         case K_ENTER:
838                 m_state = m_none;
839                 key_dest = key_game;
840                 Cbuf_AddText (va("save s%i\n", load_cursor));
841                 return;
842
843         case K_UPARROW:
844         case K_LEFTARROW:
845                 S_LocalSound ("misc/menu1.wav");
846                 load_cursor--;
847                 if (load_cursor < 0)
848                         load_cursor = MAX_SAVEGAMES-1;
849                 break;
850
851         case K_DOWNARROW:
852         case K_RIGHTARROW:
853                 S_LocalSound ("misc/menu1.wav");
854                 load_cursor++;
855                 if (load_cursor >= MAX_SAVEGAMES)
856                         load_cursor = 0;
857                 break;
858         }
859 }
860
861 //=============================================================================
862 /* MULTIPLAYER MENU */
863
864 int     m_multiplayer_cursor;
865 #define MULTIPLAYER_ITEMS       3
866
867
868 void M_Menu_MultiPlayer_f (void)
869 {
870         key_dest = key_menu;
871         m_state = m_multiplayer;
872         m_entersound = true;
873 }
874
875
876 void M_MultiPlayer_Draw (void)
877 {
878         int             f;
879         qpic_t  *p;
880
881         M_DrawPic (16, 4, Draw_CachePic ("gfx/qplaque.lmp") );
882         p = Draw_CachePic ("gfx/p_multi.lmp");
883         M_DrawPic ( (320-p->width)/2, 4, p);
884         M_DrawPic (72, 32, Draw_CachePic ("gfx/mp_menu.lmp") );
885
886         f = (int)(realtime * 10)%6;
887
888         M_DrawPic (54, 32 + m_multiplayer_cursor * 20,Draw_CachePic( va("gfx/menudot%i.lmp", f+1 ) ) );
889
890         if (ipxAvailable || tcpipAvailable)
891                 return;
892         M_PrintWhite ((320/2) - ((27*8)/2), 148, "No Communications Available");
893 }
894
895
896 void M_MultiPlayer_Key (int key)
897 {
898         switch (key)
899         {
900         case K_ESCAPE:
901                 M_Menu_Main_f ();
902                 break;
903
904         case K_DOWNARROW:
905                 S_LocalSound ("misc/menu1.wav");
906                 if (++m_multiplayer_cursor >= MULTIPLAYER_ITEMS)
907                         m_multiplayer_cursor = 0;
908                 break;
909
910         case K_UPARROW:
911                 S_LocalSound ("misc/menu1.wav");
912                 if (--m_multiplayer_cursor < 0)
913                         m_multiplayer_cursor = MULTIPLAYER_ITEMS - 1;
914                 break;
915
916         case K_ENTER:
917                 m_entersound = true;
918                 switch (m_multiplayer_cursor)
919                 {
920                 case 0:
921                         if (ipxAvailable || tcpipAvailable)
922                                 M_Menu_Net_f ();
923                         break;
924
925                 case 1:
926                         if (ipxAvailable || tcpipAvailable)
927                                 M_Menu_Net_f ();
928                         break;
929
930                 case 2:
931                         M_Menu_Setup_f ();
932                         break;
933                 }
934         }
935 }
936
937 //=============================================================================
938 /* SETUP MENU */
939
940 int             setup_cursor = 4;
941 int             setup_cursor_table[] = {40, 56, 80, 104, 140};
942
943 char    setup_hostname[16];
944 char    setup_myname[16];
945 int             setup_oldtop;
946 int             setup_oldbottom;
947 int             setup_top;
948 int             setup_bottom;
949
950 #define NUM_SETUP_CMDS  5
951
952 void M_Menu_Setup_f (void)
953 {
954         key_dest = key_menu;
955         m_state = m_setup;
956         m_entersound = true;
957         strcpy(setup_myname, cl_name.string);
958         strcpy(setup_hostname, hostname.string);
959         setup_top = setup_oldtop = ((int)cl_color.value) >> 4;
960         setup_bottom = setup_oldbottom = ((int)cl_color.value) & 15;
961 }
962
963
964 void M_Setup_Draw (void)
965 {
966         qpic_t  *p;
967
968         M_DrawPic (16, 4, Draw_CachePic ("gfx/qplaque.lmp") );
969         p = Draw_CachePic ("gfx/p_multi.lmp");
970         M_DrawPic ( (320-p->width)/2, 4, p);
971
972         M_Print (64, 40, "Hostname");
973         M_DrawTextBox (160, 32, 16, 1);
974         M_Print (168, 40, setup_hostname);
975
976         M_Print (64, 56, "Your name");
977         M_DrawTextBox (160, 48, 16, 1);
978         M_Print (168, 56, setup_myname);
979
980         M_Print (64, 80, "Shirt color");
981         M_Print (64, 104, "Pants color");
982
983         M_DrawTextBox (64, 140-8, 14, 1);
984         M_Print (72, 140, "Accept Changes");
985
986         p = Draw_CachePic ("gfx/bigbox.lmp");
987         M_DrawPic (160, 64, p);
988         p = Draw_CachePic ("gfx/menuplyr.lmp");
989         M_BuildTranslationTable(setup_top*16, setup_bottom*16);
990         M_DrawPicTranslate (172, 72, p);
991
992         M_DrawCharacter (56, setup_cursor_table [setup_cursor], 12+((int)(realtime*4)&1));
993
994         if (setup_cursor == 0)
995                 M_DrawCharacter (168 + 8*strlen(setup_hostname), setup_cursor_table [setup_cursor], 10+((int)(realtime*4)&1));
996
997         if (setup_cursor == 1)
998                 M_DrawCharacter (168 + 8*strlen(setup_myname), setup_cursor_table [setup_cursor], 10+((int)(realtime*4)&1));
999 }
1000
1001
1002 void M_Setup_Key (int k)
1003 {
1004         int                     l;
1005
1006         switch (k)
1007         {
1008         case K_ESCAPE:
1009                 M_Menu_MultiPlayer_f ();
1010                 break;
1011
1012         case K_UPARROW:
1013                 S_LocalSound ("misc/menu1.wav");
1014                 setup_cursor--;
1015                 if (setup_cursor < 0)
1016                         setup_cursor = NUM_SETUP_CMDS-1;
1017                 break;
1018
1019         case K_DOWNARROW:
1020                 S_LocalSound ("misc/menu1.wav");
1021                 setup_cursor++;
1022                 if (setup_cursor >= NUM_SETUP_CMDS)
1023                         setup_cursor = 0;
1024                 break;
1025
1026         case K_LEFTARROW:
1027                 if (setup_cursor < 2)
1028                         return;
1029                 S_LocalSound ("misc/menu3.wav");
1030                 if (setup_cursor == 2)
1031                         setup_top = setup_top - 1;
1032                 if (setup_cursor == 3)
1033                         setup_bottom = setup_bottom - 1;
1034                 break;
1035         case K_RIGHTARROW:
1036                 if (setup_cursor < 2)
1037                         return;
1038 forward:
1039                 S_LocalSound ("misc/menu3.wav");
1040                 if (setup_cursor == 2)
1041                         setup_top = setup_top + 1;
1042                 if (setup_cursor == 3)
1043                         setup_bottom = setup_bottom + 1;
1044                 break;
1045
1046         case K_ENTER:
1047                 if (setup_cursor == 0 || setup_cursor == 1)
1048                         return;
1049
1050                 if (setup_cursor == 2 || setup_cursor == 3)
1051                         goto forward;
1052
1053                 // setup_cursor == 4 (OK)
1054                 if (strcmp(cl_name.string, setup_myname) != 0)
1055                         Cbuf_AddText ( va ("name \"%s\"\n", setup_myname) );
1056                 if (strcmp(hostname.string, setup_hostname) != 0)
1057                         Cvar_Set("hostname", setup_hostname);
1058                 if (setup_top != setup_oldtop || setup_bottom != setup_oldbottom)
1059                         Cbuf_AddText( va ("color %i %i\n", setup_top, setup_bottom) );
1060                 m_entersound = true;
1061                 M_Menu_MultiPlayer_f ();
1062                 break;
1063
1064         case K_BACKSPACE:
1065                 if (setup_cursor == 0)
1066                 {
1067                         if (strlen(setup_hostname))
1068                                 setup_hostname[strlen(setup_hostname)-1] = 0;
1069                 }
1070
1071                 if (setup_cursor == 1)
1072                 {
1073                         if (strlen(setup_myname))
1074                                 setup_myname[strlen(setup_myname)-1] = 0;
1075                 }
1076                 break;
1077
1078         default:
1079                 if (k < 32 || k > 127)
1080                         break;
1081                 if (setup_cursor == 0)
1082                 {
1083                         l = strlen(setup_hostname);
1084                         if (l < 15)
1085                         {
1086                                 setup_hostname[l+1] = 0;
1087                                 setup_hostname[l] = k;
1088                         }
1089                 }
1090                 if (setup_cursor == 1)
1091                 {
1092                         l = strlen(setup_myname);
1093                         if (l < 15)
1094                         {
1095                                 setup_myname[l+1] = 0;
1096                                 setup_myname[l] = k;
1097                         }
1098                 }
1099         }
1100
1101         if (setup_top > 13)
1102                 setup_top = 0;
1103         if (setup_top < 0)
1104                 setup_top = 13;
1105         if (setup_bottom > 13)
1106                 setup_bottom = 0;
1107         if (setup_bottom < 0)
1108                 setup_bottom = 13;
1109 }
1110
1111 //=============================================================================
1112 /* NET MENU */
1113
1114 int     m_net_cursor;
1115 int m_net_items;
1116 int m_net_saveHeight;
1117
1118 char *net_helpMessage [] =
1119 {
1120 /* .........1.........2.... */
1121   " Novell network LANs    ",
1122   " or Windows 95 DOS-box. ",
1123   "                        ",
1124   "(LAN=Local Area Network)",
1125
1126   " Commonly used to play  ",
1127   " over the Internet, but ",
1128   " also used on a Local   ",
1129   " Area Network.          "
1130 };
1131
1132 void M_Menu_Net_f (void)
1133 {
1134         key_dest = key_menu;
1135         m_state = m_net;
1136         m_entersound = true;
1137         m_net_items = 2;
1138
1139         if (m_net_cursor >= m_net_items)
1140                 m_net_cursor = 0;
1141         m_net_cursor--;
1142         M_Net_Key (K_DOWNARROW);
1143 }
1144
1145
1146 void M_Net_Draw (void)
1147 {
1148         int             f;
1149         qpic_t  *p;
1150
1151         M_DrawPic (16, 4, Draw_CachePic ("gfx/qplaque.lmp") );
1152         p = Draw_CachePic ("gfx/p_multi.lmp");
1153         M_DrawPic ( (320-p->width)/2, 4, p);
1154
1155         f = 32;
1156
1157         if (ipxAvailable)
1158                 p = Draw_CachePic ("gfx/netmen3.lmp");
1159         else
1160                 p = Draw_CachePic ("gfx/dim_ipx.lmp");
1161         M_DrawPic (72, f, p);
1162
1163         f += 19;
1164         if (tcpipAvailable)
1165                 p = Draw_CachePic ("gfx/netmen4.lmp");
1166         else
1167                 p = Draw_CachePic ("gfx/dim_tcp.lmp");
1168         M_DrawPic (72, f, p);
1169
1170         if (m_net_items == 5)   // JDC, could just be removed
1171         {
1172                 f += 19;
1173                 p = Draw_CachePic ("gfx/netmen5.lmp");
1174                 M_DrawPic (72, f, p);
1175         }
1176
1177         f = (320-26*8)/2;
1178         M_DrawTextBox (f, 134, 24, 4);
1179         f += 8;
1180         M_Print (f, 142, net_helpMessage[m_net_cursor*4+0]);
1181         M_Print (f, 150, net_helpMessage[m_net_cursor*4+1]);
1182
1183         f = (int)(realtime * 10)%6;
1184         M_DrawPic (54, 32 + m_net_cursor * 20,Draw_CachePic( va("gfx/menudot%i.lmp", f+1 ) ) );
1185 }
1186
1187
1188 void M_Net_Key (int k)
1189 {
1190 again:
1191         switch (k)
1192         {
1193         case K_ESCAPE:
1194                 M_Menu_MultiPlayer_f ();
1195                 break;
1196
1197         case K_DOWNARROW:
1198                 S_LocalSound ("misc/menu1.wav");
1199                 if (++m_net_cursor >= m_net_items)
1200                         m_net_cursor = 0;
1201                 break;
1202
1203         case K_UPARROW:
1204                 S_LocalSound ("misc/menu1.wav");
1205                 if (--m_net_cursor < 0)
1206                         m_net_cursor = m_net_items - 1;
1207                 break;
1208
1209         case K_ENTER:
1210                 m_entersound = true;
1211
1212                 switch (m_net_cursor)
1213                 {
1214                 case 0:
1215                         M_Menu_LanConfig_f ();
1216                         break;
1217
1218                 case 1:
1219                         M_Menu_LanConfig_f ();
1220                         break;
1221
1222                 case 2:
1223 // multiprotocol
1224                         break;
1225                 }
1226         }
1227
1228         if (m_net_cursor == 0 && !ipxAvailable)
1229                 goto again;
1230         if (m_net_cursor == 1 && !tcpipAvailable)
1231                 goto again;
1232 }
1233
1234 //=============================================================================
1235 /* OPTIONS MENU */
1236
1237 #ifdef _WIN32
1238 #define OPTIONS_ITEMS   14
1239 #else
1240 #define OPTIONS_ITEMS   13
1241 #endif
1242
1243 #define SLIDER_RANGE    10
1244
1245 int             options_cursor;
1246
1247 void M_Menu_Options_f (void)
1248 {
1249         key_dest = key_menu;
1250         m_state = m_options;
1251         m_entersound = true;
1252
1253 #ifdef _WIN32
1254         if ((options_cursor == 13) && (modestate != MS_WINDOWED))
1255         {
1256                 options_cursor = 0;
1257         }
1258 #endif
1259 }
1260
1261
1262 void M_AdjustSliders (int dir)
1263 {
1264         S_LocalSound ("misc/menu3.wav");
1265
1266         switch (options_cursor)
1267         {
1268         case 3: // screen size
1269                 scr_viewsize.value += dir * 10;
1270                 if (scr_viewsize.value < 30)
1271                         scr_viewsize.value = 30;
1272                 if (scr_viewsize.value > 120)
1273                         scr_viewsize.value = 120;
1274                 Cvar_SetValue ("viewsize", scr_viewsize.value);
1275                 break;
1276         case 4: // brightness
1277                 brightness.value += dir * 0.25;
1278                 if (brightness.value < 1)
1279                         brightness.value = 1;
1280                 if (brightness.value > 5)
1281                         brightness.value = 5;
1282                 Cvar_SetValue ("brightness", brightness.value);
1283                 break;
1284         case 5: // mouse speed
1285                 sensitivity.value += dir * 0.5;
1286                 if (sensitivity.value < 1)
1287                         sensitivity.value = 1;
1288                 if (sensitivity.value > 50)
1289                         sensitivity.value = 50;
1290                 Cvar_SetValue ("sensitivity", sensitivity.value);
1291                 break;
1292         case 6: // music volume
1293 #ifdef _WIN32
1294                 bgmvolume.value += dir * 1.0;
1295 #else
1296                 bgmvolume.value += dir * 0.1;
1297 #endif
1298                 if (bgmvolume.value < 0)
1299                         bgmvolume.value = 0;
1300                 if (bgmvolume.value > 1)
1301                         bgmvolume.value = 1;
1302                 Cvar_SetValue ("bgmvolume", bgmvolume.value);
1303                 break;
1304         case 7: // sfx volume
1305                 volume.value += dir * 0.1;
1306                 if (volume.value < 0)
1307                         volume.value = 0;
1308                 if (volume.value > 1)
1309                         volume.value = 1;
1310                 Cvar_SetValue ("volume", volume.value);
1311                 break;
1312
1313         case 8: // always run
1314                 if (cl_forwardspeed.value > 200)
1315                 {
1316                         Cvar_SetValue ("cl_forwardspeed", 200);
1317                         Cvar_SetValue ("cl_backspeed", 200);
1318                 }
1319                 else
1320                 {
1321                         Cvar_SetValue ("cl_forwardspeed", 400);
1322                         Cvar_SetValue ("cl_backspeed", 400);
1323                 }
1324                 break;
1325
1326         case 9: // invert mouse
1327                 Cvar_SetValue ("m_pitch", -m_pitch.value);
1328                 break;
1329
1330         case 10:        // lookspring
1331                 Cvar_SetValue ("lookspring", !lookspring.value);
1332                 break;
1333
1334         case 11:        // lookstrafe
1335                 Cvar_SetValue ("lookstrafe", !lookstrafe.value);
1336                 break;
1337
1338 #ifdef _WIN32
1339         case 13:        // _windowed_mouse
1340                 Cvar_SetValue ("_windowed_mouse", !_windowed_mouse.value);
1341                 break;
1342 #endif
1343         }
1344 }
1345
1346
1347 void M_DrawSlider (int x, int y, float range)
1348 {
1349         int     i;
1350
1351         if (range < 0)
1352                 range = 0;
1353         if (range > 1)
1354                 range = 1;
1355         M_DrawCharacter (x-8, y, 128);
1356         for (i=0 ; i<SLIDER_RANGE ; i++)
1357                 M_DrawCharacter (x + i*8, y, 129);
1358         M_DrawCharacter (x+i*8, y, 130);
1359         M_DrawCharacter (x + (SLIDER_RANGE-1)*8 * range, y, 131);
1360 }
1361
1362 void M_DrawCheckbox (int x, int y, int on)
1363 {
1364 #if 0
1365         if (on)
1366                 M_DrawCharacter (x, y, 131);
1367         else
1368                 M_DrawCharacter (x, y, 129);
1369 #endif
1370         if (on)
1371                 M_Print (x, y, "on");
1372         else
1373                 M_Print (x, y, "off");
1374 }
1375
1376 void M_Options_Draw (void)
1377 {
1378         float           r;
1379         qpic_t  *p;
1380
1381         M_DrawPic (16, 4, Draw_CachePic ("gfx/qplaque.lmp") );
1382         p = Draw_CachePic ("gfx/p_option.lmp");
1383         M_DrawPic ( (320-p->width)/2, 4, p);
1384
1385         M_Print (16, 32, "    Customize controls");
1386         M_Print (16, 40, "         Go to console");
1387         M_Print (16, 48, "     Reset to defaults");
1388
1389         M_Print (16, 56, "           Screen size");
1390         r = (scr_viewsize.value - 30) / (120 - 30);
1391         M_DrawSlider (220, 56, r);
1392
1393         M_Print (16, 64, "            Brightness");
1394         r = (brightness.value - 1) / 4;
1395         M_DrawSlider (220, 64, r);
1396
1397         M_Print (16, 72, "           Mouse Speed");
1398         r = (sensitivity.value - 1)/50;
1399         M_DrawSlider (220, 72, r);
1400
1401         M_Print (16, 80, "       CD Music Volume");
1402         r = bgmvolume.value;
1403         M_DrawSlider (220, 80, r);
1404
1405         M_Print (16, 88, "          Sound Volume");
1406         r = volume.value;
1407         M_DrawSlider (220, 88, r);
1408
1409         M_Print (16, 96,  "            Always Run");
1410         M_DrawCheckbox (220, 96, cl_forwardspeed.value > 200);
1411
1412         M_Print (16, 104, "          Invert Mouse");
1413         M_DrawCheckbox (220, 104, m_pitch.value < 0);
1414
1415         M_Print (16, 112, "            Lookspring");
1416         M_DrawCheckbox (220, 112, lookspring.value);
1417
1418         M_Print (16, 120, "            Lookstrafe");
1419         M_DrawCheckbox (220, 120, lookstrafe.value);
1420
1421         if (vid_menudrawfn)
1422                 M_Print (16, 128, "         Video Options");
1423
1424 #ifdef _WIN32
1425         if (modestate == MS_WINDOWED)
1426         {
1427                 M_Print (16, 136, "             Use Mouse");
1428                 M_DrawCheckbox (220, 136, _windowed_mouse.value);
1429         }
1430 #endif
1431
1432 // cursor
1433         M_DrawCharacter (200, 32 + options_cursor*8, 12+((int)(realtime*4)&1));
1434 }
1435
1436
1437 void M_Options_Key (int k)
1438 {
1439         switch (k)
1440         {
1441         case K_ESCAPE:
1442                 M_Menu_Main_f ();
1443                 break;
1444
1445         case K_ENTER:
1446                 m_entersound = true;
1447                 switch (options_cursor)
1448                 {
1449                 case 0:
1450                         M_Menu_Keys_f ();
1451                         break;
1452                 case 1:
1453                         m_state = m_none;
1454                         Con_ToggleConsole_f ();
1455                         break;
1456                 case 2:
1457                         Cbuf_AddText ("exec default.cfg\n");
1458                         break;
1459                 case 12:
1460                         M_Menu_Video_f ();
1461                         break;
1462                 default:
1463                         M_AdjustSliders (1);
1464                         break;
1465                 }
1466                 return;
1467
1468         case K_UPARROW:
1469                 S_LocalSound ("misc/menu1.wav");
1470                 options_cursor--;
1471                 if (options_cursor < 0)
1472                         options_cursor = OPTIONS_ITEMS-1;
1473                 break;
1474
1475         case K_DOWNARROW:
1476                 S_LocalSound ("misc/menu1.wav");
1477                 options_cursor++;
1478                 if (options_cursor >= OPTIONS_ITEMS)
1479                         options_cursor = 0;
1480                 break;
1481
1482         case K_LEFTARROW:
1483                 M_AdjustSliders (-1);
1484                 break;
1485
1486         case K_RIGHTARROW:
1487                 M_AdjustSliders (1);
1488                 break;
1489         }
1490
1491         if (options_cursor == 12 && vid_menudrawfn == NULL)
1492         {
1493                 if (k == K_UPARROW)
1494                         options_cursor = 11;
1495                 else
1496                         options_cursor = 0;
1497         }
1498
1499 #ifdef _WIN32
1500         if ((options_cursor == 13) && (modestate != MS_WINDOWED))
1501         {
1502                 if (k == K_UPARROW)
1503                         options_cursor = 12;
1504                 else
1505                         options_cursor = 0;
1506         }
1507 #endif
1508 }
1509
1510 //=============================================================================
1511 /* KEYS MENU */
1512
1513 char *bindnames[][2] =
1514 {
1515 {"+attack",             "attack"},
1516 {"impulse 10",          "change weapon"},
1517 {"+jump",                       "jump / swim up"},
1518 {"+forward",            "walk forward"},
1519 {"+back",                       "backpedal"},
1520 {"+left",                       "turn left"},
1521 {"+right",                      "turn right"},
1522 {"+speed",                      "run"},
1523 {"+moveleft",           "step left"},
1524 {"+moveright",          "step right"},
1525 {"+strafe",             "sidestep"},
1526 {"+lookup",             "look up"},
1527 {"+lookdown",           "look down"},
1528 {"centerview",          "center view"},
1529 {"+mlook",                      "mouse look"},
1530 {"+klook",                      "keyboard look"},
1531 {"+moveup",                     "swim up"},
1532 {"+movedown",           "swim down"}
1533 };
1534
1535 #define NUMCOMMANDS     (sizeof(bindnames)/sizeof(bindnames[0]))
1536
1537 int             keys_cursor;
1538 int             bind_grab;
1539
1540 void M_Menu_Keys_f (void)
1541 {
1542         key_dest = key_menu;
1543         m_state = m_keys;
1544         m_entersound = true;
1545 }
1546
1547
1548 void M_FindKeysForCommand (char *command, int *twokeys)
1549 {
1550         int             count;
1551         int             j;
1552         int             l;
1553         char    *b;
1554
1555         twokeys[0] = twokeys[1] = -1;
1556         l = strlen(command);
1557         count = 0;
1558
1559         for (j=0 ; j<256 ; j++)
1560         {
1561                 b = keybindings[j];
1562                 if (!b)
1563                         continue;
1564                 if (!strncmp (b, command, l) )
1565                 {
1566                         twokeys[count] = j;
1567                         count++;
1568                         if (count == 2)
1569                                 break;
1570                 }
1571         }
1572 }
1573
1574 void M_UnbindCommand (char *command)
1575 {
1576         int             j;
1577         int             l;
1578         char    *b;
1579
1580         l = strlen(command);
1581
1582         for (j=0 ; j<256 ; j++)
1583         {
1584                 b = keybindings[j];
1585                 if (!b)
1586                         continue;
1587                 if (!strncmp (b, command, l) )
1588                         Key_SetBinding (j, "");
1589         }
1590 }
1591
1592
1593 void M_Keys_Draw (void)
1594 {
1595         int             i, l;
1596         int             keys[2];
1597         char    *name;
1598         int             x, y;
1599         qpic_t  *p;
1600
1601         p = Draw_CachePic ("gfx/ttl_cstm.lmp");
1602         M_DrawPic ( (320-p->width)/2, 4, p);
1603
1604         if (bind_grab)
1605                 M_Print (12, 32, "Press a key or button for this action");
1606         else
1607                 M_Print (18, 32, "Enter to change, backspace to clear");
1608
1609 // search for known bindings
1610         for (i=0 ; i<NUMCOMMANDS ; i++)
1611         {
1612                 y = 48 + 8*i;
1613
1614                 M_Print (16, y, bindnames[i][1]);
1615
1616                 l = strlen (bindnames[i][0]);
1617
1618                 M_FindKeysForCommand (bindnames[i][0], keys);
1619
1620                 if (keys[0] == -1)
1621                 {
1622                         M_Print (140, y, "???");
1623                 }
1624                 else
1625                 {
1626                         name = Key_KeynumToString (keys[0]);
1627                         M_Print (140, y, name);
1628                         x = strlen(name) * 8;
1629                         if (keys[1] != -1)
1630                         {
1631                                 M_Print (140 + x + 8, y, "or");
1632                                 M_Print (140 + x + 32, y, Key_KeynumToString (keys[1]));
1633                         }
1634                 }
1635         }
1636
1637         if (bind_grab)
1638                 M_DrawCharacter (130, 48 + keys_cursor*8, '=');
1639         else
1640                 M_DrawCharacter (130, 48 + keys_cursor*8, 12+((int)(realtime*4)&1));
1641 }
1642
1643
1644 void M_Keys_Key (int k)
1645 {
1646         char    cmd[80];
1647         int             keys[2];
1648
1649         if (bind_grab)
1650         {       // defining a key
1651                 S_LocalSound ("misc/menu1.wav");
1652                 if (k == K_ESCAPE)
1653                 {
1654                         bind_grab = false;
1655                 }
1656                 else if (k != '`')
1657                 {
1658                         sprintf (cmd, "bind \"%s\" \"%s\"\n", Key_KeynumToString (k), bindnames[keys_cursor][0]);
1659                         Cbuf_InsertText (cmd);
1660                 }
1661
1662                 bind_grab = false;
1663                 return;
1664         }
1665
1666         switch (k)
1667         {
1668         case K_ESCAPE:
1669                 M_Menu_Options_f ();
1670                 break;
1671
1672         case K_LEFTARROW:
1673         case K_UPARROW:
1674                 S_LocalSound ("misc/menu1.wav");
1675                 keys_cursor--;
1676                 if (keys_cursor < 0)
1677                         keys_cursor = NUMCOMMANDS-1;
1678                 break;
1679
1680         case K_DOWNARROW:
1681         case K_RIGHTARROW:
1682                 S_LocalSound ("misc/menu1.wav");
1683                 keys_cursor++;
1684                 if (keys_cursor >= NUMCOMMANDS)
1685                         keys_cursor = 0;
1686                 break;
1687
1688         case K_ENTER:           // go into bind mode
1689                 M_FindKeysForCommand (bindnames[keys_cursor][0], keys);
1690                 S_LocalSound ("misc/menu2.wav");
1691                 if (keys[1] != -1)
1692                         M_UnbindCommand (bindnames[keys_cursor][0]);
1693                 bind_grab = true;
1694                 break;
1695
1696         case K_BACKSPACE:               // delete bindings
1697         case K_DEL:                             // delete bindings
1698                 S_LocalSound ("misc/menu2.wav");
1699                 M_UnbindCommand (bindnames[keys_cursor][0]);
1700                 break;
1701         }
1702 }
1703
1704 //=============================================================================
1705 /* VIDEO MENU */
1706
1707 void M_Menu_Video_f (void)
1708 {
1709         key_dest = key_menu;
1710         m_state = m_video;
1711         m_entersound = true;
1712 }
1713
1714
1715 void M_Video_Draw (void)
1716 {
1717         (*vid_menudrawfn) ();
1718 }
1719
1720
1721 void M_Video_Key (int key)
1722 {
1723         (*vid_menukeyfn) (key);
1724 }
1725
1726 //=============================================================================
1727 /* HELP MENU */
1728
1729 int             help_page;
1730 #define NUM_HELP_PAGES  6
1731
1732
1733 void M_Menu_Help_f (void)
1734 {
1735         key_dest = key_menu;
1736         m_state = m_help;
1737         m_entersound = true;
1738         help_page = 0;
1739 }
1740
1741
1742
1743 void M_Help_Draw (void)
1744 {
1745         M_DrawPic (0, 0, Draw_CachePic ( va("gfx/help%i.lmp", help_page)) );
1746 }
1747
1748
1749 void M_Help_Key (int key)
1750 {
1751         switch (key)
1752         {
1753         case K_ESCAPE:
1754                 M_Menu_Main_f ();
1755                 break;
1756
1757         case K_UPARROW:
1758         case K_RIGHTARROW:
1759                 m_entersound = true;
1760                 if (++help_page >= NUM_HELP_PAGES)
1761                         help_page = 0;
1762                 break;
1763
1764         case K_DOWNARROW:
1765         case K_LEFTARROW:
1766                 m_entersound = true;
1767                 if (--help_page < 0)
1768                         help_page = NUM_HELP_PAGES-1;
1769                 break;
1770         }
1771
1772 }
1773
1774 //=============================================================================
1775 /* QUIT MENU */
1776
1777 int             msgNumber;
1778 int             m_quit_prevstate;
1779 qboolean        wasInMenus;
1780
1781 //#ifndef       _WIN32
1782 char *quitMessage [] = 
1783 {
1784 /* .........1.........2.... */
1785 /*
1786   "  Are you gonna quit    ",
1787   "  this game just like   ",
1788   "   everything else?     ",
1789   "                        ",
1790  
1791   " Milord, methinks that  ",
1792   "   thou art a lowly     ",
1793   " quitter. Is this true? ",
1794   "                        ",
1795
1796   " Do I need to bust your ",
1797   "  face open for trying  ",
1798   "        to quit?        ",
1799   "                        ",
1800
1801   " Man, I oughta smack you",
1802   "   for trying to quit!  ",
1803   "     Press Y to get     ",
1804   "      smacked out.      ",
1805  
1806   " Press Y to quit like a ",
1807   "   big loser in life.   ",
1808   "  Press N to stay proud ",
1809   "    and successful!     ",
1810  
1811   "   If you press Y to    ",
1812   "  quit, I will summon   ",
1813   "  Satan all over your   ",
1814   "      hard drive!       ",
1815  
1816   "  Um, Asmodeus dislikes ",
1817   " his children trying to ",
1818   " quit. Press Y to return",
1819   "   to your Tinkertoys.  ",
1820  
1821   "  If you quit now, I'll ",
1822   "  throw a blanket-party ",
1823   "   for you next time!   ",
1824   "                        "
1825   */
1826
1827 /* .........1.........2.... */
1828   "                        ",
1829   "    Tired of fragging   ",
1830   "        already?        ",
1831   "                        ",
1832
1833   "                        ",
1834   "  Quit now and forfeit  ",
1835   "     your bodycount?    ",
1836   "                        ",
1837
1838   "                        ",
1839   "    Are you sure you    ",
1840   "      want to quit?     ",
1841   "                        ",
1842
1843   "                        ",
1844   "   Off to do something  ",
1845   "      constructive?     ",
1846   "                        ",
1847 };
1848 //#endif
1849
1850 void M_Menu_Quit_f (void)
1851 {
1852         if (m_state == m_quit)
1853                 return;
1854         wasInMenus = (key_dest == key_menu);
1855         key_dest = key_menu;
1856         m_quit_prevstate = m_state;
1857         m_state = m_quit;
1858         m_entersound = true;
1859         msgNumber = rand()&3; //&7;
1860 }
1861
1862
1863 void M_Quit_Key (int key)
1864 {
1865         switch (key)
1866         {
1867         case K_ESCAPE:
1868         case 'n':
1869         case 'N':
1870                 if (wasInMenus)
1871                 {
1872                         m_state = m_quit_prevstate;
1873                         m_entersound = true;
1874                 }
1875                 else
1876                 {
1877                         key_dest = key_game;
1878                         m_state = m_none;
1879                 }
1880                 break;
1881
1882         case 'Y':
1883         case 'y':
1884                 key_dest = key_console;
1885                 Host_Quit_f ();
1886                 break;
1887
1888         default:
1889                 break;
1890         }
1891
1892 }
1893
1894
1895 void M_Quit_Draw (void)
1896 {
1897         if (wasInMenus)
1898         {
1899                 m_state = m_quit_prevstate;
1900 //              m_recursiveDraw = true;
1901                 M_Draw ();
1902                 m_state = m_quit;
1903         }
1904
1905 /*
1906 #ifdef _WIN32
1907         M_DrawTextBox (0, 0, 38, 23);
1908         M_PrintWhite (16, 12,  "  Quake version 1.09 by id Software\n\n");
1909         M_PrintWhite (16, 28,  "Programming        Art \n");
1910         M_Print (16, 36,  " John Carmack       Adrian Carmack\n");
1911         M_Print (16, 44,  " Michael Abrash     Kevin Cloud\n");
1912         M_Print (16, 52,  " John Cash          Paul Steed\n");
1913         M_Print (16, 60,  " Dave 'Zoid' Kirsch\n");
1914         M_PrintWhite (16, 68,  "Design             Biz\n");
1915         M_Print (16, 76,  " John Romero        Jay Wilbur\n");
1916         M_Print (16, 84,  " Sandy Petersen     Mike Wilson\n");
1917         M_Print (16, 92,  " American McGee     Donna Jackson\n");
1918         M_Print (16, 100,  " Tim Willits        Todd Hollenshead\n");
1919         M_PrintWhite (16, 108, "Support            Projects\n");
1920         M_Print (16, 116, " Barrett Alexander  Shawn Green\n");
1921         M_PrintWhite (16, 124, "Sound Effects\n");
1922         M_Print (16, 132, " Trent Reznor and Nine Inch Nails\n\n");
1923         M_PrintWhite (16, 140, "Quake is a trademark of Id Software,\n");
1924         M_PrintWhite (16, 148, "inc., (c)1996 Id Software, inc. All\n");
1925         M_PrintWhite (16, 156, "rights reserved. NIN logo is a\n");
1926         M_PrintWhite (16, 164, "registered trademark licensed to\n");
1927         M_PrintWhite (16, 172, "Nothing Interactive, Inc. All rights\n");
1928         M_PrintWhite (16, 180, "reserved. Press y to exit\n");
1929 #else
1930 */
1931         M_DrawTextBox (56, 76, 24, 4);
1932         M_Print (64, 84,  quitMessage[msgNumber*4+0]);
1933         M_Print (64, 92,  quitMessage[msgNumber*4+1]);
1934         M_Print (64, 100, quitMessage[msgNumber*4+2]);
1935         M_Print (64, 108, quitMessage[msgNumber*4+3]);
1936 //#endif
1937 }
1938
1939 //=============================================================================
1940 /* LAN CONFIG MENU */
1941
1942 int             lanConfig_cursor = -1;
1943 int             lanConfig_cursor_table [] = {72, 92, 124};
1944 #define NUM_LANCONFIG_CMDS      3
1945
1946 int     lanConfig_port;
1947 char    lanConfig_portname[6];
1948 char    lanConfig_joinname[22];
1949
1950 void M_Menu_LanConfig_f (void)
1951 {
1952         key_dest = key_menu;
1953         m_state = m_lanconfig;
1954         m_entersound = true;
1955         if (lanConfig_cursor == -1)
1956         {
1957                 if (JoiningGame && TCPIPConfig)
1958                         lanConfig_cursor = 2;
1959                 else
1960                         lanConfig_cursor = 1;
1961         }
1962         if (StartingGame && lanConfig_cursor == 2)
1963                 lanConfig_cursor = 1;
1964         lanConfig_port = DEFAULTnet_hostport;
1965         sprintf(lanConfig_portname, "%u", lanConfig_port);
1966
1967         m_return_onerror = false;
1968         m_return_reason[0] = 0;
1969 }
1970
1971
1972 void M_LanConfig_Draw (void)
1973 {
1974         qpic_t  *p;
1975         int             basex;
1976         char    *startJoin;
1977         char    *protocol;
1978
1979         M_DrawPic (16, 4, Draw_CachePic ("gfx/qplaque.lmp") );
1980         p = Draw_CachePic ("gfx/p_multi.lmp");
1981         basex = (320-p->width)/2;
1982         M_DrawPic (basex, 4, p);
1983
1984         if (StartingGame)
1985                 startJoin = "New Game";
1986         else
1987                 startJoin = "Join Game";
1988         if (IPXConfig)
1989                 protocol = "IPX";
1990         else
1991                 protocol = "TCP/IP";
1992         M_Print (basex, 32, va ("%s - %s", startJoin, protocol));
1993         basex += 8;
1994
1995         M_Print (basex, 52, "Address:");
1996         if (IPXConfig)
1997                 M_Print (basex+9*8, 52, my_ipx_address);
1998         else
1999                 M_Print (basex+9*8, 52, my_tcpip_address);
2000
2001         M_Print (basex, lanConfig_cursor_table[0], "Port");
2002         M_DrawTextBox (basex+8*8, lanConfig_cursor_table[0]-8, 6, 1);
2003         M_Print (basex+9*8, lanConfig_cursor_table[0], lanConfig_portname);
2004
2005         if (JoiningGame)
2006         {
2007                 M_Print (basex, lanConfig_cursor_table[1], "Search for local games...");
2008                 M_Print (basex, 108, "Join game at:");
2009                 M_DrawTextBox (basex+8, lanConfig_cursor_table[2]-8, 22, 1);
2010                 M_Print (basex+16, lanConfig_cursor_table[2], lanConfig_joinname);
2011         }
2012         else
2013         {
2014                 M_DrawTextBox (basex, lanConfig_cursor_table[1]-8, 2, 1);
2015                 M_Print (basex+8, lanConfig_cursor_table[1], "OK");
2016         }
2017
2018         M_DrawCharacter (basex-8, lanConfig_cursor_table [lanConfig_cursor], 12+((int)(realtime*4)&1));
2019
2020         if (lanConfig_cursor == 0)
2021                 M_DrawCharacter (basex+9*8 + 8*strlen(lanConfig_portname), lanConfig_cursor_table [0], 10+((int)(realtime*4)&1));
2022
2023         if (lanConfig_cursor == 2)
2024                 M_DrawCharacter (basex+16 + 8*strlen(lanConfig_joinname), lanConfig_cursor_table [2], 10+((int)(realtime*4)&1));
2025
2026         if (*m_return_reason)
2027                 M_PrintWhite (basex, 148, m_return_reason);
2028 }
2029
2030
2031 void M_LanConfig_Key (int key)
2032 {
2033         int             l;
2034
2035         switch (key)
2036         {
2037         case K_ESCAPE:
2038                 M_Menu_Net_f ();
2039                 break;
2040
2041         case K_UPARROW:
2042                 S_LocalSound ("misc/menu1.wav");
2043                 lanConfig_cursor--;
2044                 if (lanConfig_cursor < 0)
2045                         lanConfig_cursor = NUM_LANCONFIG_CMDS-1;
2046                 break;
2047
2048         case K_DOWNARROW:
2049                 S_LocalSound ("misc/menu1.wav");
2050                 lanConfig_cursor++;
2051                 if (lanConfig_cursor >= NUM_LANCONFIG_CMDS)
2052                         lanConfig_cursor = 0;
2053                 break;
2054
2055         case K_ENTER:
2056                 if (lanConfig_cursor == 0)
2057                         break;
2058
2059                 m_entersound = true;
2060
2061                 M_ConfigureNetSubsystem ();
2062
2063                 if (lanConfig_cursor == 1)
2064                 {
2065                         if (StartingGame)
2066                         {
2067                                 M_Menu_GameOptions_f ();
2068                                 break;
2069                         }
2070                         M_Menu_Search_f();
2071                         break;
2072                 }
2073
2074                 if (lanConfig_cursor == 2)
2075                 {
2076                         m_return_state = m_state;
2077                         m_return_onerror = true;
2078                         key_dest = key_game;
2079                         m_state = m_none;
2080                         Cbuf_AddText ( va ("connect \"%s\"\n", lanConfig_joinname) );
2081                         break;
2082                 }
2083
2084                 break;
2085
2086         case K_BACKSPACE:
2087                 if (lanConfig_cursor == 0)
2088                 {
2089                         if (strlen(lanConfig_portname))
2090                                 lanConfig_portname[strlen(lanConfig_portname)-1] = 0;
2091                 }
2092
2093                 if (lanConfig_cursor == 2)
2094                 {
2095                         if (strlen(lanConfig_joinname))
2096                                 lanConfig_joinname[strlen(lanConfig_joinname)-1] = 0;
2097                 }
2098                 break;
2099
2100         default:
2101                 if (key < 32 || key > 127)
2102                         break;
2103
2104                 if (lanConfig_cursor == 2)
2105                 {
2106                         l = strlen(lanConfig_joinname);
2107                         if (l < 21)
2108                         {
2109                                 lanConfig_joinname[l+1] = 0;
2110                                 lanConfig_joinname[l] = key;
2111                         }
2112                 }
2113
2114                 if (key < '0' || key > '9')
2115                         break;
2116                 if (lanConfig_cursor == 0)
2117                 {
2118                         l = strlen(lanConfig_portname);
2119                         if (l < 5)
2120                         {
2121                                 lanConfig_portname[l+1] = 0;
2122                                 lanConfig_portname[l] = key;
2123                         }
2124                 }
2125         }
2126
2127         if (StartingGame && lanConfig_cursor == 2)
2128         {
2129                 if (key == K_UPARROW)
2130                         lanConfig_cursor = 1;
2131                 else
2132                         lanConfig_cursor = 0;
2133         }
2134
2135         l =  atoi(lanConfig_portname);
2136         if (l > 65535)
2137                 l = lanConfig_port;
2138         else
2139                 lanConfig_port = l;
2140         sprintf(lanConfig_portname, "%u", lanConfig_port);
2141 }
2142
2143 //=============================================================================
2144 /* GAME OPTIONS MENU */
2145
2146 typedef struct
2147 {
2148         char    *name;
2149         char    *description;
2150 } level_t;
2151
2152 level_t         levels[] =
2153 {
2154         {"start", "Entrance"},  // 0
2155
2156         {"e1m1", "Slipgate Complex"},                           // 1
2157         {"e1m2", "Castle of the Damned"},
2158         {"e1m3", "The Necropolis"},
2159         {"e1m4", "The Grisly Grotto"},
2160         {"e1m5", "Gloom Keep"},
2161         {"e1m6", "The Door To Chthon"},
2162         {"e1m7", "The House of Chthon"},
2163         {"e1m8", "Ziggurat Vertigo"},
2164
2165         {"e2m1", "The Installation"},                           // 9
2166         {"e2m2", "Ogre Citadel"},
2167         {"e2m3", "Crypt of Decay"},
2168         {"e2m4", "The Ebon Fortress"},
2169         {"e2m5", "The Wizard's Manse"},
2170         {"e2m6", "The Dismal Oubliette"},
2171         {"e2m7", "Underearth"},
2172
2173         {"e3m1", "Termination Central"},                        // 16
2174         {"e3m2", "The Vaults of Zin"},
2175         {"e3m3", "The Tomb of Terror"},
2176         {"e3m4", "Satan's Dark Delight"},
2177         {"e3m5", "Wind Tunnels"},
2178         {"e3m6", "Chambers of Torment"},
2179         {"e3m7", "The Haunted Halls"},
2180
2181         {"e4m1", "The Sewage System"},                          // 23
2182         {"e4m2", "The Tower of Despair"},
2183         {"e4m3", "The Elder God Shrine"},
2184         {"e4m4", "The Palace of Hate"},
2185         {"e4m5", "Hell's Atrium"},
2186         {"e4m6", "The Pain Maze"},
2187         {"e4m7", "Azure Agony"},
2188         {"e4m8", "The Nameless City"},
2189
2190         {"end", "Shub-Niggurath's Pit"},                        // 31
2191
2192         {"dm1", "Place of Two Deaths"},                         // 32
2193         {"dm2", "Claustrophobopolis"},
2194         {"dm3", "The Abandoned Base"},
2195         {"dm4", "The Bad Place"},
2196         {"dm5", "The Cistern"},
2197         {"dm6", "The Dark Zone"}
2198 };
2199
2200 //MED 01/06/97 added hipnotic levels
2201 level_t     hipnoticlevels[] =
2202 {
2203    {"start", "Command HQ"},  // 0
2204
2205    {"hip1m1", "The Pumping Station"},          // 1
2206    {"hip1m2", "Storage Facility"},
2207    {"hip1m3", "The Lost Mine"},
2208    {"hip1m4", "Research Facility"},
2209    {"hip1m5", "Military Complex"},
2210
2211    {"hip2m1", "Ancient Realms"},          // 6
2212    {"hip2m2", "The Black Cathedral"},
2213    {"hip2m3", "The Catacombs"},
2214    {"hip2m4", "The Crypt"},
2215    {"hip2m5", "Mortum's Keep"},
2216    {"hip2m6", "The Gremlin's Domain"},
2217
2218    {"hip3m1", "Tur Torment"},       // 12
2219    {"hip3m2", "Pandemonium"},
2220    {"hip3m3", "Limbo"},
2221    {"hip3m4", "The Gauntlet"},
2222
2223    {"hipend", "Armagon's Lair"},       // 16
2224
2225    {"hipdm1", "The Edge of Oblivion"}           // 17
2226 };
2227
2228 //PGM 01/07/97 added rogue levels
2229 //PGM 03/02/97 added dmatch level
2230 level_t         roguelevels[] =
2231 {
2232         {"start",       "Split Decision"},
2233         {"r1m1",        "Deviant's Domain"},
2234         {"r1m2",        "Dread Portal"},
2235         {"r1m3",        "Judgement Call"},
2236         {"r1m4",        "Cave of Death"},
2237         {"r1m5",        "Towers of Wrath"},
2238         {"r1m6",        "Temple of Pain"},
2239         {"r1m7",        "Tomb of the Overlord"},
2240         {"r2m1",        "Tempus Fugit"},
2241         {"r2m2",        "Elemental Fury I"},
2242         {"r2m3",        "Elemental Fury II"},
2243         {"r2m4",        "Curse of Osiris"},
2244         {"r2m5",        "Wizard's Keep"},
2245         {"r2m6",        "Blood Sacrifice"},
2246         {"r2m7",        "Last Bastion"},
2247         {"r2m8",        "Source of Evil"},
2248         {"ctf1",    "Division of Change"}
2249 };
2250
2251 typedef struct
2252 {
2253         char    *description;
2254         int             firstLevel;
2255         int             levels;
2256 } episode_t;
2257
2258 episode_t       episodes[] =
2259 {
2260         {"Welcome to Quake", 0, 1},
2261         {"Doomed Dimension", 1, 8},
2262         {"Realm of Black Magic", 9, 7},
2263         {"Netherworld", 16, 7},
2264         {"The Elder World", 23, 8},
2265         {"Final Level", 31, 1},
2266         {"Deathmatch Arena", 32, 6}
2267 };
2268
2269 //MED 01/06/97  added hipnotic episodes
2270 episode_t   hipnoticepisodes[] =
2271 {
2272    {"Scourge of Armagon", 0, 1},
2273    {"Fortress of the Dead", 1, 5},
2274    {"Dominion of Darkness", 6, 6},
2275    {"The Rift", 12, 4},
2276    {"Final Level", 16, 1},
2277    {"Deathmatch Arena", 17, 1}
2278 };
2279
2280 //PGM 01/07/97 added rogue episodes
2281 //PGM 03/02/97 added dmatch episode
2282 episode_t       rogueepisodes[] =
2283 {
2284         {"Introduction", 0, 1},
2285         {"Hell's Fortress", 1, 7},
2286         {"Corridors of Time", 8, 8},
2287         {"Deathmatch Arena", 16, 1}
2288 };
2289
2290 level_t         nehahralevels[] =
2291 {
2292         {"nehstart",    "Welcome to Nehahra"},
2293         {"neh1m1",      "Forge City1: Slipgates"},
2294         {"neh1m2",      "Forge City2: Boiler"},
2295         {"neh1m3",      "Forge City3: Escape"},
2296         {"neh1m4",      "Grind Core"},
2297         {"neh1m5",      "Industrial Silence"},
2298         {"neh1m6",      "Locked-Up Anger"},
2299         {"neh1m7",      "Wanderer of the Wastes"},
2300         {"neh1m8",      "Artemis System Net"},
2301         {"neh1m9",      "To the Death"},
2302         {"neh2m1",      "The Gates of Ghoro"},
2303         {"neh2m2",      "Sacred Trinity"},
2304         {"neh2m3",      "Realm of the Ancients"},
2305         {"neh2m4",      "Temple of the Ancients"},
2306         {"neh2m5",      "Dreams Made Flesh"},
2307         {"neh2m6",      "Your Last Cup of Sorrow"},
2308         {"nehsec",      "Ogre's Bane"},
2309         {"nehahra",     "Nehahra's Den"},
2310         {"nehend",      "Quintessence"}
2311 };
2312
2313 episode_t       nehahraepisodes[] =
2314 {
2315         {"Welcome to Nehahra", 0, 1},
2316         {"The Fall of Forge", 1, 9},
2317         {"The Outlands", 10, 7},
2318         {"Dimension of the Lost", 17, 2}
2319 };
2320
2321 int     startepisode;
2322 int     startlevel;
2323 int maxplayers;
2324 qboolean m_serverInfoMessage = false;
2325 double m_serverInfoMessageTime;
2326
2327 void M_Menu_GameOptions_f (void)
2328 {
2329         key_dest = key_menu;
2330         m_state = m_gameoptions;
2331         m_entersound = true;
2332         if (maxplayers == 0)
2333                 maxplayers = svs.maxclients;
2334         if (maxplayers < 2)
2335                 maxplayers = svs.maxclientslimit;
2336 }
2337
2338
2339 int gameoptions_cursor_table[] = {40, 56, 64, 72, 80, 88, 96, 112, 120};
2340 #define NUM_GAMEOPTIONS 9
2341 int             gameoptions_cursor;
2342
2343 void M_GameOptions_Draw (void)
2344 {
2345         qpic_t  *p;
2346         int             x;
2347
2348         M_DrawPic (16, 4, Draw_CachePic ("gfx/qplaque.lmp") );
2349         p = Draw_CachePic ("gfx/p_multi.lmp");
2350         M_DrawPic ( (320-p->width)/2, 4, p);
2351
2352         M_DrawTextBox (152, 32, 10, 1);
2353         M_Print (160, 40, "begin game");
2354
2355         M_Print (0, 56, "      Max players");
2356         M_Print (160, 56, va("%i", maxplayers) );
2357
2358         M_Print (0, 64, "        Game Type");
2359         if (!coop.value && !deathmatch.value)
2360                 Cvar_SetValue("deathmatch", 1);
2361         if (coop.value)
2362                 M_Print (160, 64, "Cooperative");
2363         else
2364                 M_Print (160, 64, "Deathmatch");
2365
2366         M_Print (0, 72, "        Teamplay");
2367         if (rogue)
2368         {
2369                 char *msg;
2370
2371                 switch((int)teamplay.value)
2372                 {
2373                         case 1: msg = "No Friendly Fire"; break;
2374                         case 2: msg = "Friendly Fire"; break;
2375                         case 3: msg = "Tag"; break;
2376                         case 4: msg = "Capture the Flag"; break;
2377                         case 5: msg = "One Flag CTF"; break;
2378                         case 6: msg = "Three Team CTF"; break;
2379                         default: msg = "Off"; break;
2380                 }
2381                 M_Print (160, 72, msg);
2382         }
2383         else
2384         {
2385                 char *msg;
2386
2387                 switch((int)teamplay.value)
2388                 {
2389                         case 1: msg = "No Friendly Fire"; break;
2390                         case 2: msg = "Friendly Fire"; break;
2391                         default: msg = "Off"; break;
2392                 }
2393                 M_Print (160, 72, msg);
2394         }
2395
2396         M_Print (0, 80, "            Skill");
2397         if (skill.value == 0)
2398                 M_Print (160, 80, "Easy difficulty");
2399         else if (skill.value == 1)
2400                 M_Print (160, 80, "Normal difficulty");
2401         else if (skill.value == 2)
2402                 M_Print (160, 80, "Hard difficulty");
2403         else
2404                 M_Print (160, 80, "Nightmare difficulty");
2405
2406         M_Print (0, 88, "       Frag Limit");
2407         if (fraglimit.value == 0)
2408                 M_Print (160, 88, "none");
2409         else
2410                 M_Print (160, 88, va("%i frags", (int)fraglimit.value));
2411
2412         M_Print (0, 96, "       Time Limit");
2413         if (timelimit.value == 0)
2414                 M_Print (160, 96, "none");
2415         else
2416                 M_Print (160, 96, va("%i minutes", (int)timelimit.value));
2417
2418         M_Print (0, 112, "         Episode");
2419    //MED 01/06/97 added hipnotic episodes
2420    if (hipnotic)
2421       M_Print (160, 112, hipnoticepisodes[startepisode].description);
2422    //PGM 01/07/97 added rogue episodes
2423    else if (rogue)
2424       M_Print (160, 112, rogueepisodes[startepisode].description);
2425    else if (nehahra)
2426       M_Print (160, 112, nehahraepisodes[startepisode].description);
2427    else
2428       M_Print (160, 112, episodes[startepisode].description);
2429
2430         M_Print (0, 120, "           Level");
2431    //MED 01/06/97 added hipnotic episodes
2432    if (hipnotic)
2433    {
2434       M_Print (160, 120, hipnoticlevels[hipnoticepisodes[startepisode].firstLevel + startlevel].description);
2435       M_Print (160, 128, hipnoticlevels[hipnoticepisodes[startepisode].firstLevel + startlevel].name);
2436    }
2437    //PGM 01/07/97 added rogue episodes
2438    else if (rogue)
2439    {
2440       M_Print (160, 120, roguelevels[rogueepisodes[startepisode].firstLevel + startlevel].description);
2441       M_Print (160, 128, roguelevels[rogueepisodes[startepisode].firstLevel + startlevel].name);
2442    }
2443    else if (nehahra)
2444    {
2445       M_Print (160, 120, nehahralevels[nehahraepisodes[startepisode].firstLevel + startlevel].description);
2446       M_Print (160, 128, nehahralevels[nehahraepisodes[startepisode].firstLevel + startlevel].name);
2447    }
2448    else
2449    {
2450       M_Print (160, 120, levels[episodes[startepisode].firstLevel + startlevel].description);
2451       M_Print (160, 128, levels[episodes[startepisode].firstLevel + startlevel].name);
2452    }
2453
2454 // line cursor
2455         M_DrawCharacter (144, gameoptions_cursor_table[gameoptions_cursor], 12+((int)(realtime*4)&1));
2456
2457         if (m_serverInfoMessage)
2458         {
2459                 if ((realtime - m_serverInfoMessageTime) < 5.0)
2460                 {
2461                         x = (320-26*8)/2;
2462                         M_DrawTextBox (x, 138, 24, 4);
2463                         x += 8;
2464                         M_Print (x, 146, " More than 64 players?? ");
2465                         M_Print (x, 154, "  First, question your  ");
2466                         M_Print (x, 162, "   sanity, then email   ");
2467                         M_Print (x, 170, " havoc@gamevisions.com  ");
2468                         /*
2469                         M_Print (x, 146, "  More than 4 players   ");
2470                         M_Print (x, 154, " requires using command ");
2471                         M_Print (x, 162, "line parameters; please ");
2472                         M_Print (x, 170, "   see techinfo.txt.    ");
2473                         */
2474                 }
2475                 else
2476                 {
2477                         m_serverInfoMessage = false;
2478                 }
2479         }
2480 }
2481
2482
2483 void M_NetStart_Change (int dir)
2484 {
2485         int count;
2486
2487         switch (gameoptions_cursor)
2488         {
2489         case 1:
2490                 maxplayers += dir;
2491                 if (maxplayers > svs.maxclientslimit)
2492                 {
2493                         maxplayers = svs.maxclientslimit;
2494                         m_serverInfoMessage = true;
2495                         m_serverInfoMessageTime = realtime;
2496                 }
2497                 if (maxplayers < 2)
2498                         maxplayers = 2;
2499                 break;
2500
2501         case 2:
2502                 if (deathmatch.value) // changing from deathmatch to coop
2503                 {
2504                         Cvar_SetValue ("coop", 1);
2505                         Cvar_SetValue ("deathmatch", 0);
2506                 }
2507                 else // changing from coop to deathmatch
2508                 {
2509                         Cvar_SetValue ("coop", 0);
2510                         Cvar_SetValue ("deathmatch", 1);
2511                 }
2512                 break;
2513
2514         case 3:
2515                 if (rogue)
2516                         count = 6;
2517                 else
2518                         count = 2;
2519
2520                 Cvar_SetValue ("teamplay", teamplay.value + dir);
2521                 if (teamplay.value > count)
2522                         Cvar_SetValue ("teamplay", 0);
2523                 else if (teamplay.value < 0)
2524                         Cvar_SetValue ("teamplay", count);
2525                 break;
2526
2527         case 4:
2528                 Cvar_SetValue ("skill", skill.value + dir);
2529                 if (skill.value > 3)
2530                         Cvar_SetValue ("skill", 0);
2531                 if (skill.value < 0)
2532                         Cvar_SetValue ("skill", 3);
2533                 break;
2534
2535         case 5:
2536                 Cvar_SetValue ("fraglimit", fraglimit.value + dir*10);
2537                 if (fraglimit.value > 100)
2538                         Cvar_SetValue ("fraglimit", 0);
2539                 if (fraglimit.value < 0)
2540                         Cvar_SetValue ("fraglimit", 100);
2541                 break;
2542
2543         case 6:
2544                 Cvar_SetValue ("timelimit", timelimit.value + dir*5);
2545                 if (timelimit.value > 60)
2546                         Cvar_SetValue ("timelimit", 0);
2547                 if (timelimit.value < 0)
2548                         Cvar_SetValue ("timelimit", 60);
2549                 break;
2550
2551         case 7:
2552                 startepisode += dir;
2553         //MED 01/06/97 added hipnotic count
2554                 if (hipnotic)
2555                         count = 6;
2556         //PGM 01/07/97 added rogue count
2557         //PGM 03/02/97 added 1 for dmatch episode
2558                 else if (rogue)
2559                         count = 4;
2560                 else if (nehahra)
2561                         count = 4;
2562                 else if (registered.value)
2563                         count = 7;
2564                 else
2565                         count = 2;
2566
2567                 if (startepisode < 0)
2568                         startepisode = count - 1;
2569
2570                 if (startepisode >= count)
2571                         startepisode = 0;
2572
2573                 startlevel = 0;
2574                 break;
2575
2576         case 8:
2577                 startlevel += dir;
2578     //MED 01/06/97 added hipnotic episodes
2579                 if (hipnotic)
2580                         count = hipnoticepisodes[startepisode].levels;
2581         //PGM 01/06/97 added hipnotic episodes
2582                 else if (rogue)
2583                         count = rogueepisodes[startepisode].levels;
2584                 else if (nehahra)
2585                         count = nehahraepisodes[startepisode].levels;
2586                 else
2587                         count = episodes[startepisode].levels;
2588
2589                 if (startlevel < 0)
2590                         startlevel = count - 1;
2591
2592                 if (startlevel >= count)
2593                         startlevel = 0;
2594                 break;
2595         }
2596 }
2597
2598 void M_GameOptions_Key (int key)
2599 {
2600         switch (key)
2601         {
2602         case K_ESCAPE:
2603                 M_Menu_Net_f ();
2604                 break;
2605
2606         case K_UPARROW:
2607                 S_LocalSound ("misc/menu1.wav");
2608                 gameoptions_cursor--;
2609                 if (gameoptions_cursor < 0)
2610                         gameoptions_cursor = NUM_GAMEOPTIONS-1;
2611                 break;
2612
2613         case K_DOWNARROW:
2614                 S_LocalSound ("misc/menu1.wav");
2615                 gameoptions_cursor++;
2616                 if (gameoptions_cursor >= NUM_GAMEOPTIONS)
2617                         gameoptions_cursor = 0;
2618                 break;
2619
2620         case K_LEFTARROW:
2621                 if (gameoptions_cursor == 0)
2622                         break;
2623                 S_LocalSound ("misc/menu3.wav");
2624                 M_NetStart_Change (-1);
2625                 break;
2626
2627         case K_RIGHTARROW:
2628                 if (gameoptions_cursor == 0)
2629                         break;
2630                 S_LocalSound ("misc/menu3.wav");
2631                 M_NetStart_Change (1);
2632                 break;
2633
2634         case K_ENTER:
2635                 S_LocalSound ("misc/menu2.wav");
2636                 if (gameoptions_cursor == 0)
2637                 {
2638                         if (sv.active)
2639                                 Cbuf_AddText ("disconnect\n");
2640                         Cbuf_AddText ("listen 0\n");    // so host_netport will be re-examined
2641                         Cbuf_AddText ( va ("maxplayers %u\n", maxplayers) );
2642 //                      SCR_BeginLoadingPlaque ();
2643
2644                         if (hipnotic)
2645                                 Cbuf_AddText ( va ("map %s\n", hipnoticlevels[hipnoticepisodes[startepisode].firstLevel + startlevel].name) );
2646                         else if (rogue)
2647                                 Cbuf_AddText ( va ("map %s\n", roguelevels[rogueepisodes[startepisode].firstLevel + startlevel].name) );
2648                         else if (nehahra)
2649                                 Cbuf_AddText ( va ("map %s\n", nehahralevels[nehahraepisodes[startepisode].firstLevel + startlevel].name) );
2650                         else
2651                                 Cbuf_AddText ( va ("map %s\n", levels[episodes[startepisode].firstLevel + startlevel].name) );
2652
2653                         return;
2654                 }
2655
2656                 M_NetStart_Change (1);
2657                 break;
2658         }
2659 }
2660
2661 //=============================================================================
2662 /* SEARCH MENU */
2663
2664 qboolean        searchComplete = false;
2665 double          searchCompleteTime;
2666
2667 void M_Menu_Search_f (void)
2668 {
2669         key_dest = key_menu;
2670         m_state = m_search;
2671         m_entersound = false;
2672         slistSilent = true;
2673         slistLocal = false;
2674         searchComplete = false;
2675         NET_Slist_f();
2676
2677 }
2678
2679
2680 void M_Search_Draw (void)
2681 {
2682         qpic_t  *p;
2683         int x;
2684
2685         p = Draw_CachePic ("gfx/p_multi.lmp");
2686         M_DrawPic ( (320-p->width)/2, 4, p);
2687         x = (320/2) - ((12*8)/2) + 4;
2688         M_DrawTextBox (x-8, 32, 12, 1);
2689         M_Print (x, 40, "Searching...");
2690
2691         if(slistInProgress)
2692         {
2693                 NET_Poll();
2694                 return;
2695         }
2696
2697         if (! searchComplete)
2698         {
2699                 searchComplete = true;
2700                 searchCompleteTime = realtime;
2701         }
2702
2703         if (hostCacheCount)
2704         {
2705                 M_Menu_ServerList_f ();
2706                 return;
2707         }
2708
2709         M_PrintWhite ((320/2) - ((22*8)/2), 64, "No Quake servers found");
2710         if ((realtime - searchCompleteTime) < 3.0)
2711                 return;
2712
2713         M_Menu_LanConfig_f ();
2714 }
2715
2716
2717 void M_Search_Key (int key)
2718 {
2719 }
2720
2721 //=============================================================================
2722 /* SLIST MENU */
2723
2724 int             slist_cursor;
2725 qboolean slist_sorted;
2726
2727 void M_Menu_ServerList_f (void)
2728 {
2729         key_dest = key_menu;
2730         m_state = m_slist;
2731         m_entersound = true;
2732         slist_cursor = 0;
2733         m_return_onerror = false;
2734         m_return_reason[0] = 0;
2735         slist_sorted = false;
2736 }
2737
2738
2739 void M_ServerList_Draw (void)
2740 {
2741         int             n;
2742         char    string [64];
2743         qpic_t  *p;
2744
2745         if (!slist_sorted)
2746         {
2747                 if (hostCacheCount > 1)
2748                 {
2749                         int     i,j;
2750                         hostcache_t temp;
2751                         for (i = 0; i < hostCacheCount; i++)
2752                                 for (j = i+1; j < hostCacheCount; j++)
2753                                         if (strcmp(hostcache[j].name, hostcache[i].name) < 0)
2754                                         {
2755                                                 memcpy(&temp, &hostcache[j], sizeof(hostcache_t));
2756                                                 memcpy(&hostcache[j], &hostcache[i], sizeof(hostcache_t));
2757                                                 memcpy(&hostcache[i], &temp, sizeof(hostcache_t));
2758                                         }
2759                 }
2760                 slist_sorted = true;
2761         }
2762
2763         p = Draw_CachePic ("gfx/p_multi.lmp");
2764         M_DrawPic ( (320-p->width)/2, 4, p);
2765         for (n = 0; n < hostCacheCount; n++)
2766         {
2767                 if (hostcache[n].maxusers)
2768                         sprintf(string, "%-15.15s %-15.15s %2u/%2u\n", hostcache[n].name, hostcache[n].map, hostcache[n].users, hostcache[n].maxusers);
2769                 else
2770                         sprintf(string, "%-15.15s %-15.15s\n", hostcache[n].name, hostcache[n].map);
2771                 M_Print (16, 32 + 8*n, string);
2772         }
2773         M_DrawCharacter (0, 32 + slist_cursor*8, 12+((int)(realtime*4)&1));
2774
2775         if (*m_return_reason)
2776                 M_PrintWhite (16, 148, m_return_reason);
2777 }
2778
2779
2780 void M_ServerList_Key (int k)
2781 {
2782         switch (k)
2783         {
2784         case K_ESCAPE:
2785                 M_Menu_LanConfig_f ();
2786                 break;
2787
2788         case K_SPACE:
2789                 M_Menu_Search_f ();
2790                 break;
2791
2792         case K_UPARROW:
2793         case K_LEFTARROW:
2794                 S_LocalSound ("misc/menu1.wav");
2795                 slist_cursor--;
2796                 if (slist_cursor < 0)
2797                         slist_cursor = hostCacheCount - 1;
2798                 break;
2799
2800         case K_DOWNARROW:
2801         case K_RIGHTARROW:
2802                 S_LocalSound ("misc/menu1.wav");
2803                 slist_cursor++;
2804                 if (slist_cursor >= hostCacheCount)
2805                         slist_cursor = 0;
2806                 break;
2807
2808         case K_ENTER:
2809                 S_LocalSound ("misc/menu2.wav");
2810                 m_return_state = m_state;
2811                 m_return_onerror = true;
2812                 slist_sorted = false;
2813                 key_dest = key_game;
2814                 m_state = m_none;
2815                 Cbuf_AddText ( va ("connect \"%s\"\n", hostcache[slist_cursor].cname) );
2816                 break;
2817
2818         default:
2819                 break;
2820         }
2821
2822 }
2823
2824 //=============================================================================
2825 /* Menu Subsystem */
2826
2827
2828 void M_Init (void)
2829 {
2830         Cmd_AddCommand ("togglemenu", M_ToggleMenu_f);
2831
2832         Cmd_AddCommand ("menu_main", M_Menu_Main_f);
2833         Cmd_AddCommand ("menu_singleplayer", M_Menu_SinglePlayer_f);
2834         Cmd_AddCommand ("menu_load", M_Menu_Load_f);
2835         Cmd_AddCommand ("menu_save", M_Menu_Save_f);
2836         Cmd_AddCommand ("menu_multiplayer", M_Menu_MultiPlayer_f);
2837         Cmd_AddCommand ("menu_setup", M_Menu_Setup_f);
2838         Cmd_AddCommand ("menu_options", M_Menu_Options_f);
2839         Cmd_AddCommand ("menu_keys", M_Menu_Keys_f);
2840         Cmd_AddCommand ("menu_video", M_Menu_Video_f);
2841         Cmd_AddCommand ("help", M_Menu_Help_f);
2842         Cmd_AddCommand ("menu_quit", M_Menu_Quit_f);
2843
2844         if (nehahra)
2845         {
2846                 if (COM_FileExists("maps/neh1m4.bsp"))
2847                 {
2848                         if (COM_FileExists("hearing.dem"))
2849                         {
2850                                 Con_Printf("Nehahra movie and game detected.\n");
2851                                 NehGameType = TYPE_BOTH;
2852                         }
2853                         else
2854                         {
2855                                 Con_Printf("Nehahra game detected.\n");
2856                                 NehGameType = TYPE_GAME;
2857                         }
2858                 }
2859                 else
2860                 {
2861                         if (COM_FileExists("hearing.dem"))
2862                         {
2863                                 Con_Printf("Nehahra movie detected.\n");
2864                                 NehGameType = TYPE_DEMO;
2865                         }
2866                         else
2867                         {
2868                                 Con_Printf("Nehahra not found.\n");
2869                                 NehGameType = TYPE_GAME; // could just complain, but...
2870                         }
2871                 }
2872         }
2873 }
2874
2875
2876 void M_Draw (void)
2877 {
2878         if (m_state == m_none || key_dest != key_menu)
2879                 return;
2880
2881         /*
2882         if (!m_recursiveDraw)
2883         {
2884                 if (scr_con_current)
2885                 {
2886                         Draw_ConsoleBackground (vid.height);
2887                         S_ExtraUpdate ();
2888                 }
2889         }
2890         else
2891         {
2892                 m_recursiveDraw = false;
2893         }
2894         */
2895
2896         switch (m_state)
2897         {
2898         case m_none:
2899                 break;
2900
2901         case m_main:
2902                 M_Main_Draw ();
2903                 break;
2904
2905         case m_demo:
2906                 M_Demo_Draw ();
2907                 break;
2908
2909         case m_singleplayer:
2910                 M_SinglePlayer_Draw ();
2911                 break;
2912
2913         case m_load:
2914                 M_Load_Draw ();
2915                 break;
2916
2917         case m_save:
2918                 M_Save_Draw ();
2919                 break;
2920
2921         case m_multiplayer:
2922                 M_MultiPlayer_Draw ();
2923                 break;
2924
2925         case m_setup:
2926                 M_Setup_Draw ();
2927                 break;
2928
2929         case m_net:
2930                 M_Net_Draw ();
2931                 break;
2932
2933         case m_options:
2934                 M_Options_Draw ();
2935                 break;
2936
2937         case m_keys:
2938                 M_Keys_Draw ();
2939                 break;
2940
2941         case m_video:
2942                 M_Video_Draw ();
2943                 break;
2944
2945         case m_help:
2946                 M_Help_Draw ();
2947                 break;
2948
2949         case m_quit:
2950                 M_Quit_Draw ();
2951                 break;
2952
2953         case m_lanconfig:
2954                 M_LanConfig_Draw ();
2955                 break;
2956
2957         case m_gameoptions:
2958                 M_GameOptions_Draw ();
2959                 break;
2960
2961         case m_search:
2962                 M_Search_Draw ();
2963                 break;
2964
2965         case m_slist:
2966                 M_ServerList_Draw ();
2967                 break;
2968         }
2969
2970         if (m_entersound)
2971         {
2972                 S_LocalSound ("misc/menu2.wav");
2973                 m_entersound = false;
2974         }
2975
2976         S_ExtraUpdate ();
2977 }
2978
2979
2980 void M_Keydown (int key)
2981 {
2982         switch (m_state)
2983         {
2984         case m_none:
2985                 return;
2986
2987         case m_main:
2988                 M_Main_Key (key);
2989                 return;
2990
2991         case m_demo:
2992                 M_Demo_Key (key);
2993                 return;
2994
2995         case m_singleplayer:
2996                 M_SinglePlayer_Key (key);
2997                 return;
2998
2999         case m_load:
3000                 M_Load_Key (key);
3001                 return;
3002
3003         case m_save:
3004                 M_Save_Key (key);
3005                 return;
3006
3007         case m_multiplayer:
3008                 M_MultiPlayer_Key (key);
3009                 return;
3010
3011         case m_setup:
3012                 M_Setup_Key (key);
3013                 return;
3014
3015         case m_net:
3016                 M_Net_Key (key);
3017                 return;
3018
3019         case m_options:
3020                 M_Options_Key (key);
3021                 return;
3022
3023         case m_keys:
3024                 M_Keys_Key (key);
3025                 return;
3026
3027         case m_video:
3028                 M_Video_Key (key);
3029                 return;
3030
3031         case m_help:
3032                 M_Help_Key (key);
3033                 return;
3034
3035         case m_quit:
3036                 M_Quit_Key (key);
3037                 return;
3038
3039         case m_lanconfig:
3040                 M_LanConfig_Key (key);
3041                 return;
3042
3043         case m_gameoptions:
3044                 M_GameOptions_Key (key);
3045                 return;
3046
3047         case m_search:
3048                 M_Search_Key (key);
3049                 break;
3050
3051         case m_slist:
3052                 M_ServerList_Key (key);
3053                 return;
3054         }
3055 }
3056
3057
3058 void M_ConfigureNetSubsystem(void)
3059 {
3060 // enable/disable net systems to match desired config
3061
3062         Cbuf_AddText ("stopdemo\n");
3063
3064         if (IPXConfig || TCPIPConfig)
3065                 net_hostport = lanConfig_port;
3066 }