]> icculus.org git repositories - divverent/darkplaces.git/blob - menu.c
stripped out serial and modem stuff (was not supported anyway)
[divverent/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)(host_time * 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)(host_time * 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                         if (sv.active)
673                                 if (!SCR_ModalMessage("Are you sure you want to\nstart a new game?\n"))
674                                         break;
675                         key_dest = key_game;
676                         if (sv.active)
677                                 Cbuf_AddText ("disconnect\n");
678                         Cbuf_AddText ("maxplayers 1\n");
679                         if (nehahra)
680                                 Cbuf_AddText ("map nehstart\n");
681                         else
682                                 Cbuf_AddText ("map start\n");
683                         break;
684
685                 case 1:
686                         M_Menu_Load_f ();
687                         break;
688
689                 case 2:
690                         M_Menu_Save_f ();
691                         break;
692                 }
693         }
694 }
695
696 //=============================================================================
697 /* LOAD/SAVE MENU */
698
699 int             load_cursor;            // 0 < load_cursor < MAX_SAVEGAMES
700
701 #define MAX_SAVEGAMES           12
702 char    m_filenames[MAX_SAVEGAMES][SAVEGAME_COMMENT_LENGTH+1];
703 int             loadable[MAX_SAVEGAMES];
704
705 void M_ScanSaves (void)
706 {
707         int             i, j;
708         char    name[MAX_OSPATH];
709         FILE    *f;
710         int             version;
711
712         for (i=0 ; i<MAX_SAVEGAMES ; i++)
713         {
714                 strcpy (m_filenames[i], "--- UNUSED SLOT ---");
715                 loadable[i] = false;
716                 sprintf (name, "%s/s%i.sav", com_gamedir, i);
717                 f = fopen (name, "r");
718                 if (!f)
719                         continue;
720                 fscanf (f, "%i\n", &version);
721                 fscanf (f, "%79s\n", name);
722                 strncpy (m_filenames[i], name, sizeof(m_filenames[i])-1);
723
724         // change _ back to space
725                 for (j=0 ; j<SAVEGAME_COMMENT_LENGTH ; j++)
726                         if (m_filenames[i][j] == '_')
727                                 m_filenames[i][j] = ' ';
728                 loadable[i] = true;
729                 fclose (f);
730         }
731 }
732
733 void M_Menu_Load_f (void)
734 {
735         m_entersound = true;
736         m_state = m_load;
737         key_dest = key_menu;
738         M_ScanSaves ();
739 }
740
741
742 void M_Menu_Save_f (void)
743 {
744         if (!sv.active)
745                 return;
746         if (cl.intermission)
747                 return;
748         if (svs.maxclients != 1)
749                 return;
750         m_entersound = true;
751         m_state = m_save;
752         key_dest = key_menu;
753         M_ScanSaves ();
754 }
755
756
757 void M_Load_Draw (void)
758 {
759         int             i;
760         qpic_t  *p;
761
762         p = Draw_CachePic ("gfx/p_load.lmp");
763         M_DrawPic ( (320-p->width)/2, 4, p);
764
765         for (i=0 ; i< MAX_SAVEGAMES; i++)
766                 M_Print (16, 32 + 8*i, m_filenames[i]);
767
768 // line cursor
769         M_DrawCharacter (8, 32 + load_cursor*8, 12+((int)(realtime*4)&1));
770 }
771
772
773 void M_Save_Draw (void)
774 {
775         int             i;
776         qpic_t  *p;
777
778         p = Draw_CachePic ("gfx/p_save.lmp");
779         M_DrawPic ( (320-p->width)/2, 4, p);
780
781         for (i=0 ; i<MAX_SAVEGAMES ; i++)
782                 M_Print (16, 32 + 8*i, m_filenames[i]);
783
784 // line cursor
785         M_DrawCharacter (8, 32 + load_cursor*8, 12+((int)(realtime*4)&1));
786 }
787
788
789 void M_Load_Key (int k)
790 {
791         switch (k)
792         {
793         case K_ESCAPE:
794                 M_Menu_SinglePlayer_f ();
795                 break;
796
797         case K_ENTER:
798                 S_LocalSound ("misc/menu2.wav");
799                 if (!loadable[load_cursor])
800                         return;
801                 m_state = m_none;
802                 key_dest = key_game;
803
804         // Host_Loadgame_f can't bring up the loading plaque because too much
805         // stack space has been used, so do it now
806                 SCR_BeginLoadingPlaque ();
807
808         // issue the load command
809                 Cbuf_AddText (va ("load s%i\n", load_cursor) );
810                 return;
811
812         case K_UPARROW:
813         case K_LEFTARROW:
814                 S_LocalSound ("misc/menu1.wav");
815                 load_cursor--;
816                 if (load_cursor < 0)
817                         load_cursor = MAX_SAVEGAMES-1;
818                 break;
819
820         case K_DOWNARROW:
821         case K_RIGHTARROW:
822                 S_LocalSound ("misc/menu1.wav");
823                 load_cursor++;
824                 if (load_cursor >= MAX_SAVEGAMES)
825                         load_cursor = 0;
826                 break;
827         }
828 }
829
830
831 void M_Save_Key (int k)
832 {
833         switch (k)
834         {
835         case K_ESCAPE:
836                 M_Menu_SinglePlayer_f ();
837                 break;
838
839         case K_ENTER:
840                 m_state = m_none;
841                 key_dest = key_game;
842                 Cbuf_AddText (va("save s%i\n", load_cursor));
843                 return;
844
845         case K_UPARROW:
846         case K_LEFTARROW:
847                 S_LocalSound ("misc/menu1.wav");
848                 load_cursor--;
849                 if (load_cursor < 0)
850                         load_cursor = MAX_SAVEGAMES-1;
851                 break;
852
853         case K_DOWNARROW:
854         case K_RIGHTARROW:
855                 S_LocalSound ("misc/menu1.wav");
856                 load_cursor++;
857                 if (load_cursor >= MAX_SAVEGAMES)
858                         load_cursor = 0;
859                 break;
860         }
861 }
862
863 //=============================================================================
864 /* MULTIPLAYER MENU */
865
866 int     m_multiplayer_cursor;
867 #define MULTIPLAYER_ITEMS       3
868
869
870 void M_Menu_MultiPlayer_f (void)
871 {
872         key_dest = key_menu;
873         m_state = m_multiplayer;
874         m_entersound = true;
875 }
876
877
878 void M_MultiPlayer_Draw (void)
879 {
880         int             f;
881         qpic_t  *p;
882
883         M_DrawPic (16, 4, Draw_CachePic ("gfx/qplaque.lmp") );
884         p = Draw_CachePic ("gfx/p_multi.lmp");
885         M_DrawPic ( (320-p->width)/2, 4, p);
886         M_DrawPic (72, 32, Draw_CachePic ("gfx/mp_menu.lmp") );
887
888         f = (int)(host_time * 10)%6;
889
890         M_DrawPic (54, 32 + m_multiplayer_cursor * 20,Draw_CachePic( va("gfx/menudot%i.lmp", f+1 ) ) );
891
892         if (ipxAvailable || tcpipAvailable)
893                 return;
894         M_PrintWhite ((320/2) - ((27*8)/2), 148, "No Communications Available");
895 }
896
897
898 void M_MultiPlayer_Key (int key)
899 {
900         switch (key)
901         {
902         case K_ESCAPE:
903                 M_Menu_Main_f ();
904                 break;
905
906         case K_DOWNARROW:
907                 S_LocalSound ("misc/menu1.wav");
908                 if (++m_multiplayer_cursor >= MULTIPLAYER_ITEMS)
909                         m_multiplayer_cursor = 0;
910                 break;
911
912         case K_UPARROW:
913                 S_LocalSound ("misc/menu1.wav");
914                 if (--m_multiplayer_cursor < 0)
915                         m_multiplayer_cursor = MULTIPLAYER_ITEMS - 1;
916                 break;
917
918         case K_ENTER:
919                 m_entersound = true;
920                 switch (m_multiplayer_cursor)
921                 {
922                 case 0:
923                         if (ipxAvailable || tcpipAvailable)
924                                 M_Menu_Net_f ();
925                         break;
926
927                 case 1:
928                         if (ipxAvailable || tcpipAvailable)
929                                 M_Menu_Net_f ();
930                         break;
931
932                 case 2:
933                         M_Menu_Setup_f ();
934                         break;
935                 }
936         }
937 }
938
939 //=============================================================================
940 /* SETUP MENU */
941
942 int             setup_cursor = 4;
943 int             setup_cursor_table[] = {40, 56, 80, 104, 140};
944
945 char    setup_hostname[16];
946 char    setup_myname[16];
947 int             setup_oldtop;
948 int             setup_oldbottom;
949 int             setup_top;
950 int             setup_bottom;
951
952 #define NUM_SETUP_CMDS  5
953
954 void M_Menu_Setup_f (void)
955 {
956         key_dest = key_menu;
957         m_state = m_setup;
958         m_entersound = true;
959         strcpy(setup_myname, cl_name.string);
960         strcpy(setup_hostname, hostname.string);
961         setup_top = setup_oldtop = ((int)cl_color.value) >> 4;
962         setup_bottom = setup_oldbottom = ((int)cl_color.value) & 15;
963 }
964
965
966 void M_Setup_Draw (void)
967 {
968         qpic_t  *p;
969
970         M_DrawPic (16, 4, Draw_CachePic ("gfx/qplaque.lmp") );
971         p = Draw_CachePic ("gfx/p_multi.lmp");
972         M_DrawPic ( (320-p->width)/2, 4, p);
973
974         M_Print (64, 40, "Hostname");
975         M_DrawTextBox (160, 32, 16, 1);
976         M_Print (168, 40, setup_hostname);
977
978         M_Print (64, 56, "Your name");
979         M_DrawTextBox (160, 48, 16, 1);
980         M_Print (168, 56, setup_myname);
981
982         M_Print (64, 80, "Shirt color");
983         M_Print (64, 104, "Pants color");
984
985         M_DrawTextBox (64, 140-8, 14, 1);
986         M_Print (72, 140, "Accept Changes");
987
988         p = Draw_CachePic ("gfx/bigbox.lmp");
989         M_DrawPic (160, 64, p);
990         p = Draw_CachePic ("gfx/menuplyr.lmp");
991         M_BuildTranslationTable(setup_top*16, setup_bottom*16);
992         M_DrawPicTranslate (172, 72, p);
993
994         M_DrawCharacter (56, setup_cursor_table [setup_cursor], 12+((int)(realtime*4)&1));
995
996         if (setup_cursor == 0)
997                 M_DrawCharacter (168 + 8*strlen(setup_hostname), setup_cursor_table [setup_cursor], 10+((int)(realtime*4)&1));
998
999         if (setup_cursor == 1)
1000                 M_DrawCharacter (168 + 8*strlen(setup_myname), setup_cursor_table [setup_cursor], 10+((int)(realtime*4)&1));
1001 }
1002
1003
1004 void M_Setup_Key (int k)
1005 {
1006         int                     l;
1007
1008         switch (k)
1009         {
1010         case K_ESCAPE:
1011                 M_Menu_MultiPlayer_f ();
1012                 break;
1013
1014         case K_UPARROW:
1015                 S_LocalSound ("misc/menu1.wav");
1016                 setup_cursor--;
1017                 if (setup_cursor < 0)
1018                         setup_cursor = NUM_SETUP_CMDS-1;
1019                 break;
1020
1021         case K_DOWNARROW:
1022                 S_LocalSound ("misc/menu1.wav");
1023                 setup_cursor++;
1024                 if (setup_cursor >= NUM_SETUP_CMDS)
1025                         setup_cursor = 0;
1026                 break;
1027
1028         case K_LEFTARROW:
1029                 if (setup_cursor < 2)
1030                         return;
1031                 S_LocalSound ("misc/menu3.wav");
1032                 if (setup_cursor == 2)
1033                         setup_top = setup_top - 1;
1034                 if (setup_cursor == 3)
1035                         setup_bottom = setup_bottom - 1;
1036                 break;
1037         case K_RIGHTARROW:
1038                 if (setup_cursor < 2)
1039                         return;
1040 forward:
1041                 S_LocalSound ("misc/menu3.wav");
1042                 if (setup_cursor == 2)
1043                         setup_top = setup_top + 1;
1044                 if (setup_cursor == 3)
1045                         setup_bottom = setup_bottom + 1;
1046                 break;
1047
1048         case K_ENTER:
1049                 if (setup_cursor == 0 || setup_cursor == 1)
1050                         return;
1051
1052                 if (setup_cursor == 2 || setup_cursor == 3)
1053                         goto forward;
1054
1055                 // setup_cursor == 4 (OK)
1056                 if (strcmp(cl_name.string, setup_myname) != 0)
1057                         Cbuf_AddText ( va ("name \"%s\"\n", setup_myname) );
1058                 if (strcmp(hostname.string, setup_hostname) != 0)
1059                         Cvar_Set("hostname", setup_hostname);
1060                 if (setup_top != setup_oldtop || setup_bottom != setup_oldbottom)
1061                         Cbuf_AddText( va ("color %i %i\n", setup_top, setup_bottom) );
1062                 m_entersound = true;
1063                 M_Menu_MultiPlayer_f ();
1064                 break;
1065
1066         case K_BACKSPACE:
1067                 if (setup_cursor == 0)
1068                 {
1069                         if (strlen(setup_hostname))
1070                                 setup_hostname[strlen(setup_hostname)-1] = 0;
1071                 }
1072
1073                 if (setup_cursor == 1)
1074                 {
1075                         if (strlen(setup_myname))
1076                                 setup_myname[strlen(setup_myname)-1] = 0;
1077                 }
1078                 break;
1079
1080         default:
1081                 if (k < 32 || k > 127)
1082                         break;
1083                 if (setup_cursor == 0)
1084                 {
1085                         l = strlen(setup_hostname);
1086                         if (l < 15)
1087                         {
1088                                 setup_hostname[l+1] = 0;
1089                                 setup_hostname[l] = k;
1090                         }
1091                 }
1092                 if (setup_cursor == 1)
1093                 {
1094                         l = strlen(setup_myname);
1095                         if (l < 15)
1096                         {
1097                                 setup_myname[l+1] = 0;
1098                                 setup_myname[l] = k;
1099                         }
1100                 }
1101         }
1102
1103         if (setup_top > 13)
1104                 setup_top = 0;
1105         if (setup_top < 0)
1106                 setup_top = 13;
1107         if (setup_bottom > 13)
1108                 setup_bottom = 0;
1109         if (setup_bottom < 0)
1110                 setup_bottom = 13;
1111 }
1112
1113 //=============================================================================
1114 /* NET MENU */
1115
1116 int     m_net_cursor;
1117 int m_net_items;
1118 int m_net_saveHeight;
1119
1120 char *net_helpMessage [] =
1121 {
1122 /* .........1.........2.... */
1123   " Novell network LANs    ",
1124   " or Windows 95 DOS-box. ",
1125   "                        ",
1126   "(LAN=Local Area Network)",
1127
1128   " Commonly used to play  ",
1129   " over the Internet, but ",
1130   " also used on a Local   ",
1131   " Area Network.          "
1132 };
1133
1134 void M_Menu_Net_f (void)
1135 {
1136         key_dest = key_menu;
1137         m_state = m_net;
1138         m_entersound = true;
1139         m_net_items = 2;
1140
1141         if (m_net_cursor >= m_net_items)
1142                 m_net_cursor = 0;
1143         m_net_cursor--;
1144         M_Net_Key (K_DOWNARROW);
1145 }
1146
1147
1148 void M_Net_Draw (void)
1149 {
1150         int             f;
1151         qpic_t  *p;
1152
1153         M_DrawPic (16, 4, Draw_CachePic ("gfx/qplaque.lmp") );
1154         p = Draw_CachePic ("gfx/p_multi.lmp");
1155         M_DrawPic ( (320-p->width)/2, 4, p);
1156
1157         f = 32;
1158
1159         if (ipxAvailable)
1160                 p = Draw_CachePic ("gfx/netmen3.lmp");
1161         else
1162                 p = Draw_CachePic ("gfx/dim_ipx.lmp");
1163         M_DrawPic (72, f, p);
1164
1165         f += 19;
1166         if (tcpipAvailable)
1167                 p = Draw_CachePic ("gfx/netmen4.lmp");
1168         else
1169                 p = Draw_CachePic ("gfx/dim_tcp.lmp");
1170         M_DrawPic (72, f, p);
1171
1172         if (m_net_items == 5)   // JDC, could just be removed
1173         {
1174                 f += 19;
1175                 p = Draw_CachePic ("gfx/netmen5.lmp");
1176                 M_DrawPic (72, f, p);
1177         }
1178
1179         f = (320-26*8)/2;
1180         M_DrawTextBox (f, 134, 24, 4);
1181         f += 8;
1182         M_Print (f, 142, net_helpMessage[m_net_cursor*4+0]);
1183         M_Print (f, 150, net_helpMessage[m_net_cursor*4+1]);
1184
1185         f = (int)(host_time * 10)%6;
1186         M_DrawPic (54, 32 + m_net_cursor * 20,Draw_CachePic( va("gfx/menudot%i.lmp", f+1 ) ) );
1187 }
1188
1189
1190 void M_Net_Key (int k)
1191 {
1192 again:
1193         switch (k)
1194         {
1195         case K_ESCAPE:
1196                 M_Menu_MultiPlayer_f ();
1197                 break;
1198
1199         case K_DOWNARROW:
1200                 S_LocalSound ("misc/menu1.wav");
1201                 if (++m_net_cursor >= m_net_items)
1202                         m_net_cursor = 0;
1203                 break;
1204
1205         case K_UPARROW:
1206                 S_LocalSound ("misc/menu1.wav");
1207                 if (--m_net_cursor < 0)
1208                         m_net_cursor = m_net_items - 1;
1209                 break;
1210
1211         case K_ENTER:
1212                 m_entersound = true;
1213
1214                 switch (m_net_cursor)
1215                 {
1216                 case 0:
1217                         M_Menu_LanConfig_f ();
1218                         break;
1219
1220                 case 1:
1221                         M_Menu_LanConfig_f ();
1222                         break;
1223
1224                 case 2:
1225 // multiprotocol
1226                         break;
1227                 }
1228         }
1229
1230         if (m_net_cursor == 0 && !ipxAvailable)
1231                 goto again;
1232         if (m_net_cursor == 1 && !tcpipAvailable)
1233                 goto again;
1234 }
1235
1236 //=============================================================================
1237 /* OPTIONS MENU */
1238
1239 #ifdef _WIN32
1240 #define OPTIONS_ITEMS   14
1241 #else
1242 #define OPTIONS_ITEMS   13
1243 #endif
1244
1245 #define SLIDER_RANGE    10
1246
1247 int             options_cursor;
1248
1249 void M_Menu_Options_f (void)
1250 {
1251         key_dest = key_menu;
1252         m_state = m_options;
1253         m_entersound = true;
1254
1255 #ifdef _WIN32
1256         if ((options_cursor == 13) && (modestate != MS_WINDOWED))
1257         {
1258                 options_cursor = 0;
1259         }
1260 #endif
1261 }
1262
1263
1264 void M_AdjustSliders (int dir)
1265 {
1266         S_LocalSound ("misc/menu3.wav");
1267
1268         switch (options_cursor)
1269         {
1270         case 3: // screen size
1271                 scr_viewsize.value += dir * 10;
1272                 if (scr_viewsize.value < 30)
1273                         scr_viewsize.value = 30;
1274                 if (scr_viewsize.value > 120)
1275                         scr_viewsize.value = 120;
1276                 Cvar_SetValue ("viewsize", scr_viewsize.value);
1277                 break;
1278         case 4: // brightness
1279                 brightness.value += dir * 0.25;
1280                 if (brightness.value < 1)
1281                         brightness.value = 1;
1282                 if (brightness.value > 5)
1283                         brightness.value = 5;
1284                 Cvar_SetValue ("brightness", brightness.value);
1285                 break;
1286         case 5: // mouse speed
1287                 sensitivity.value += dir * 0.5;
1288                 if (sensitivity.value < 1)
1289                         sensitivity.value = 1;
1290                 if (sensitivity.value > 50)
1291                         sensitivity.value = 50;
1292                 Cvar_SetValue ("sensitivity", sensitivity.value);
1293                 break;
1294         case 6: // music volume
1295 #ifdef _WIN32
1296                 bgmvolume.value += dir * 1.0;
1297 #else
1298                 bgmvolume.value += dir * 0.1;
1299 #endif
1300                 if (bgmvolume.value < 0)
1301                         bgmvolume.value = 0;
1302                 if (bgmvolume.value > 1)
1303                         bgmvolume.value = 1;
1304                 Cvar_SetValue ("bgmvolume", bgmvolume.value);
1305                 break;
1306         case 7: // sfx volume
1307                 volume.value += dir * 0.1;
1308                 if (volume.value < 0)
1309                         volume.value = 0;
1310                 if (volume.value > 1)
1311                         volume.value = 1;
1312                 Cvar_SetValue ("volume", volume.value);
1313                 break;
1314
1315         case 8: // always run
1316                 if (cl_forwardspeed.value > 200)
1317                 {
1318                         Cvar_SetValue ("cl_forwardspeed", 200);
1319                         Cvar_SetValue ("cl_backspeed", 200);
1320                 }
1321                 else
1322                 {
1323                         Cvar_SetValue ("cl_forwardspeed", 400);
1324                         Cvar_SetValue ("cl_backspeed", 400);
1325                 }
1326                 break;
1327
1328         case 9: // invert mouse
1329                 Cvar_SetValue ("m_pitch", -m_pitch.value);
1330                 break;
1331
1332         case 10:        // lookspring
1333                 Cvar_SetValue ("lookspring", !lookspring.value);
1334                 break;
1335
1336         case 11:        // lookstrafe
1337                 Cvar_SetValue ("lookstrafe", !lookstrafe.value);
1338                 break;
1339
1340 #ifdef _WIN32
1341         case 13:        // _windowed_mouse
1342                 Cvar_SetValue ("_windowed_mouse", !_windowed_mouse.value);
1343                 break;
1344 #endif
1345         }
1346 }
1347
1348
1349 void M_DrawSlider (int x, int y, float range)
1350 {
1351         int     i;
1352
1353         if (range < 0)
1354                 range = 0;
1355         if (range > 1)
1356                 range = 1;
1357         M_DrawCharacter (x-8, y, 128);
1358         for (i=0 ; i<SLIDER_RANGE ; i++)
1359                 M_DrawCharacter (x + i*8, y, 129);
1360         M_DrawCharacter (x+i*8, y, 130);
1361         M_DrawCharacter (x + (SLIDER_RANGE-1)*8 * range, y, 131);
1362 }
1363
1364 void M_DrawCheckbox (int x, int y, int on)
1365 {
1366 #if 0
1367         if (on)
1368                 M_DrawCharacter (x, y, 131);
1369         else
1370                 M_DrawCharacter (x, y, 129);
1371 #endif
1372         if (on)
1373                 M_Print (x, y, "on");
1374         else
1375                 M_Print (x, y, "off");
1376 }
1377
1378 void M_Options_Draw (void)
1379 {
1380         float           r;
1381         qpic_t  *p;
1382
1383         M_DrawPic (16, 4, Draw_CachePic ("gfx/qplaque.lmp") );
1384         p = Draw_CachePic ("gfx/p_option.lmp");
1385         M_DrawPic ( (320-p->width)/2, 4, p);
1386
1387         M_Print (16, 32, "    Customize controls");
1388         M_Print (16, 40, "         Go to console");
1389         M_Print (16, 48, "     Reset to defaults");
1390
1391         M_Print (16, 56, "           Screen size");
1392         r = (scr_viewsize.value - 30) / (120 - 30);
1393         M_DrawSlider (220, 56, r);
1394
1395         M_Print (16, 64, "            Brightness");
1396         r = (brightness.value - 1) / 4;
1397         M_DrawSlider (220, 64, r);
1398
1399         M_Print (16, 72, "           Mouse Speed");
1400         r = (sensitivity.value - 1)/50;
1401         M_DrawSlider (220, 72, r);
1402
1403         M_Print (16, 80, "       CD Music Volume");
1404         r = bgmvolume.value;
1405         M_DrawSlider (220, 80, r);
1406
1407         M_Print (16, 88, "          Sound Volume");
1408         r = volume.value;
1409         M_DrawSlider (220, 88, r);
1410
1411         M_Print (16, 96,  "            Always Run");
1412         M_DrawCheckbox (220, 96, cl_forwardspeed.value > 200);
1413
1414         M_Print (16, 104, "          Invert Mouse");
1415         M_DrawCheckbox (220, 104, m_pitch.value < 0);
1416
1417         M_Print (16, 112, "            Lookspring");
1418         M_DrawCheckbox (220, 112, lookspring.value);
1419
1420         M_Print (16, 120, "            Lookstrafe");
1421         M_DrawCheckbox (220, 120, lookstrafe.value);
1422
1423         if (vid_menudrawfn)
1424                 M_Print (16, 128, "         Video Options");
1425
1426 #ifdef _WIN32
1427         if (modestate == MS_WINDOWED)
1428         {
1429                 M_Print (16, 136, "             Use Mouse");
1430                 M_DrawCheckbox (220, 136, _windowed_mouse.value);
1431         }
1432 #endif
1433
1434 // cursor
1435         M_DrawCharacter (200, 32 + options_cursor*8, 12+((int)(realtime*4)&1));
1436 }
1437
1438
1439 void M_Options_Key (int k)
1440 {
1441         switch (k)
1442         {
1443         case K_ESCAPE:
1444                 M_Menu_Main_f ();
1445                 break;
1446
1447         case K_ENTER:
1448                 m_entersound = true;
1449                 switch (options_cursor)
1450                 {
1451                 case 0:
1452                         M_Menu_Keys_f ();
1453                         break;
1454                 case 1:
1455                         m_state = m_none;
1456                         Con_ToggleConsole_f ();
1457                         break;
1458                 case 2:
1459                         Cbuf_AddText ("exec default.cfg\n");
1460                         break;
1461                 case 12:
1462                         M_Menu_Video_f ();
1463                         break;
1464                 default:
1465                         M_AdjustSliders (1);
1466                         break;
1467                 }
1468                 return;
1469
1470         case K_UPARROW:
1471                 S_LocalSound ("misc/menu1.wav");
1472                 options_cursor--;
1473                 if (options_cursor < 0)
1474                         options_cursor = OPTIONS_ITEMS-1;
1475                 break;
1476
1477         case K_DOWNARROW:
1478                 S_LocalSound ("misc/menu1.wav");
1479                 options_cursor++;
1480                 if (options_cursor >= OPTIONS_ITEMS)
1481                         options_cursor = 0;
1482                 break;
1483
1484         case K_LEFTARROW:
1485                 M_AdjustSliders (-1);
1486                 break;
1487
1488         case K_RIGHTARROW:
1489                 M_AdjustSliders (1);
1490                 break;
1491         }
1492
1493         if (options_cursor == 12 && vid_menudrawfn == NULL)
1494         {
1495                 if (k == K_UPARROW)
1496                         options_cursor = 11;
1497                 else
1498                         options_cursor = 0;
1499         }
1500
1501 #ifdef _WIN32
1502         if ((options_cursor == 13) && (modestate != MS_WINDOWED))
1503         {
1504                 if (k == K_UPARROW)
1505                         options_cursor = 12;
1506                 else
1507                         options_cursor = 0;
1508         }
1509 #endif
1510 }
1511
1512 //=============================================================================
1513 /* KEYS MENU */
1514
1515 char *bindnames[][2] =
1516 {
1517 {"+attack",             "attack"},
1518 {"impulse 10",          "change weapon"},
1519 {"+jump",                       "jump / swim up"},
1520 {"+forward",            "walk forward"},
1521 {"+back",                       "backpedal"},
1522 {"+left",                       "turn left"},
1523 {"+right",                      "turn right"},
1524 {"+speed",                      "run"},
1525 {"+moveleft",           "step left"},
1526 {"+moveright",          "step right"},
1527 {"+strafe",             "sidestep"},
1528 {"+lookup",             "look up"},
1529 {"+lookdown",           "look down"},
1530 {"centerview",          "center view"},
1531 {"+mlook",                      "mouse look"},
1532 {"+klook",                      "keyboard look"},
1533 {"+moveup",                     "swim up"},
1534 {"+movedown",           "swim down"}
1535 };
1536
1537 #define NUMCOMMANDS     (sizeof(bindnames)/sizeof(bindnames[0]))
1538
1539 int             keys_cursor;
1540 int             bind_grab;
1541
1542 void M_Menu_Keys_f (void)
1543 {
1544         key_dest = key_menu;
1545         m_state = m_keys;
1546         m_entersound = true;
1547 }
1548
1549
1550 void M_FindKeysForCommand (char *command, int *twokeys)
1551 {
1552         int             count;
1553         int             j;
1554         int             l;
1555         char    *b;
1556
1557         twokeys[0] = twokeys[1] = -1;
1558         l = strlen(command);
1559         count = 0;
1560
1561         for (j=0 ; j<256 ; j++)
1562         {
1563                 b = keybindings[j];
1564                 if (!b)
1565                         continue;
1566                 if (!strncmp (b, command, l) )
1567                 {
1568                         twokeys[count] = j;
1569                         count++;
1570                         if (count == 2)
1571                                 break;
1572                 }
1573         }
1574 }
1575
1576 void M_UnbindCommand (char *command)
1577 {
1578         int             j;
1579         int             l;
1580         char    *b;
1581
1582         l = strlen(command);
1583
1584         for (j=0 ; j<256 ; j++)
1585         {
1586                 b = keybindings[j];
1587                 if (!b)
1588                         continue;
1589                 if (!strncmp (b, command, l) )
1590                         Key_SetBinding (j, "");
1591         }
1592 }
1593
1594
1595 void M_Keys_Draw (void)
1596 {
1597         int             i, l;
1598         int             keys[2];
1599         char    *name;
1600         int             x, y;
1601         qpic_t  *p;
1602
1603         p = Draw_CachePic ("gfx/ttl_cstm.lmp");
1604         M_DrawPic ( (320-p->width)/2, 4, p);
1605
1606         if (bind_grab)
1607                 M_Print (12, 32, "Press a key or button for this action");
1608         else
1609                 M_Print (18, 32, "Enter to change, backspace to clear");
1610
1611 // search for known bindings
1612         for (i=0 ; i<NUMCOMMANDS ; i++)
1613         {
1614                 y = 48 + 8*i;
1615
1616                 M_Print (16, y, bindnames[i][1]);
1617
1618                 l = strlen (bindnames[i][0]);
1619
1620                 M_FindKeysForCommand (bindnames[i][0], keys);
1621
1622                 if (keys[0] == -1)
1623                 {
1624                         M_Print (140, y, "???");
1625                 }
1626                 else
1627                 {
1628                         name = Key_KeynumToString (keys[0]);
1629                         M_Print (140, y, name);
1630                         x = strlen(name) * 8;
1631                         if (keys[1] != -1)
1632                         {
1633                                 M_Print (140 + x + 8, y, "or");
1634                                 M_Print (140 + x + 32, y, Key_KeynumToString (keys[1]));
1635                         }
1636                 }
1637         }
1638
1639         if (bind_grab)
1640                 M_DrawCharacter (130, 48 + keys_cursor*8, '=');
1641         else
1642                 M_DrawCharacter (130, 48 + keys_cursor*8, 12+((int)(realtime*4)&1));
1643 }
1644
1645
1646 void M_Keys_Key (int k)
1647 {
1648         char    cmd[80];
1649         int             keys[2];
1650
1651         if (bind_grab)
1652         {       // defining a key
1653                 S_LocalSound ("misc/menu1.wav");
1654                 if (k == K_ESCAPE)
1655                 {
1656                         bind_grab = false;
1657                 }
1658                 else if (k != '`')
1659                 {
1660                         sprintf (cmd, "bind \"%s\" \"%s\"\n", Key_KeynumToString (k), bindnames[keys_cursor][0]);
1661                         Cbuf_InsertText (cmd);
1662                 }
1663
1664                 bind_grab = false;
1665                 return;
1666         }
1667
1668         switch (k)
1669         {
1670         case K_ESCAPE:
1671                 M_Menu_Options_f ();
1672                 break;
1673
1674         case K_LEFTARROW:
1675         case K_UPARROW:
1676                 S_LocalSound ("misc/menu1.wav");
1677                 keys_cursor--;
1678                 if (keys_cursor < 0)
1679                         keys_cursor = NUMCOMMANDS-1;
1680                 break;
1681
1682         case K_DOWNARROW:
1683         case K_RIGHTARROW:
1684                 S_LocalSound ("misc/menu1.wav");
1685                 keys_cursor++;
1686                 if (keys_cursor >= NUMCOMMANDS)
1687                         keys_cursor = 0;
1688                 break;
1689
1690         case K_ENTER:           // go into bind mode
1691                 M_FindKeysForCommand (bindnames[keys_cursor][0], keys);
1692                 S_LocalSound ("misc/menu2.wav");
1693                 if (keys[1] != -1)
1694                         M_UnbindCommand (bindnames[keys_cursor][0]);
1695                 bind_grab = true;
1696                 break;
1697
1698         case K_BACKSPACE:               // delete bindings
1699         case K_DEL:                             // delete bindings
1700                 S_LocalSound ("misc/menu2.wav");
1701                 M_UnbindCommand (bindnames[keys_cursor][0]);
1702                 break;
1703         }
1704 }
1705
1706 //=============================================================================
1707 /* VIDEO MENU */
1708
1709 void M_Menu_Video_f (void)
1710 {
1711         key_dest = key_menu;
1712         m_state = m_video;
1713         m_entersound = true;
1714 }
1715
1716
1717 void M_Video_Draw (void)
1718 {
1719         (*vid_menudrawfn) ();
1720 }
1721
1722
1723 void M_Video_Key (int key)
1724 {
1725         (*vid_menukeyfn) (key);
1726 }
1727
1728 //=============================================================================
1729 /* HELP MENU */
1730
1731 int             help_page;
1732 #define NUM_HELP_PAGES  6
1733
1734
1735 void M_Menu_Help_f (void)
1736 {
1737         key_dest = key_menu;
1738         m_state = m_help;
1739         m_entersound = true;
1740         help_page = 0;
1741 }
1742
1743
1744
1745 void M_Help_Draw (void)
1746 {
1747         M_DrawPic (0, 0, Draw_CachePic ( va("gfx/help%i.lmp", help_page)) );
1748 }
1749
1750
1751 void M_Help_Key (int key)
1752 {
1753         switch (key)
1754         {
1755         case K_ESCAPE:
1756                 M_Menu_Main_f ();
1757                 break;
1758
1759         case K_UPARROW:
1760         case K_RIGHTARROW:
1761                 m_entersound = true;
1762                 if (++help_page >= NUM_HELP_PAGES)
1763                         help_page = 0;
1764                 break;
1765
1766         case K_DOWNARROW:
1767         case K_LEFTARROW:
1768                 m_entersound = true;
1769                 if (--help_page < 0)
1770                         help_page = NUM_HELP_PAGES-1;
1771                 break;
1772         }
1773
1774 }
1775
1776 //=============================================================================
1777 /* QUIT MENU */
1778
1779 int             msgNumber;
1780 int             m_quit_prevstate;
1781 qboolean        wasInMenus;
1782
1783 #ifndef _WIN32
1784 char *quitMessage [] = 
1785 {
1786 /* .........1.........2.... */
1787   "  Are you gonna quit    ",
1788   "  this game just like   ",
1789   "   everything else?     ",
1790   "                        ",
1791  
1792   " Milord, methinks that  ",
1793   "   thou art a lowly     ",
1794   " quitter. Is this true? ",
1795   "                        ",
1796
1797   " Do I need to bust your ",
1798   "  face open for trying  ",
1799   "        to quit?        ",
1800   "                        ",
1801
1802   " Man, I oughta smack you",
1803   "   for trying to quit!  ",
1804   "     Press Y to get     ",
1805   "      smacked out.      ",
1806  
1807   " Press Y to quit like a ",
1808   "   big loser in life.   ",
1809   "  Press N to stay proud ",
1810   "    and successful!     ",
1811  
1812   "   If you press Y to    ",
1813   "  quit, I will summon   ",
1814   "  Satan all over your   ",
1815   "      hard drive!       ",
1816  
1817   "  Um, Asmodeus dislikes ",
1818   " his children trying to ",
1819   " quit. Press Y to return",
1820   "   to your Tinkertoys.  ",
1821  
1822   "  If you quit now, I'll ",
1823   "  throw a blanket-party ",
1824   "   for you next time!   ",
1825   "                        "
1826 };
1827 #endif
1828
1829 void M_Menu_Quit_f (void)
1830 {
1831         if (m_state == m_quit)
1832                 return;
1833         wasInMenus = (key_dest == key_menu);
1834         key_dest = key_menu;
1835         m_quit_prevstate = m_state;
1836         m_state = m_quit;
1837         m_entersound = true;
1838         msgNumber = rand()&7;
1839 }
1840
1841
1842 void M_Quit_Key (int key)
1843 {
1844         switch (key)
1845         {
1846         case K_ESCAPE:
1847         case 'n':
1848         case 'N':
1849                 if (wasInMenus)
1850                 {
1851                         m_state = m_quit_prevstate;
1852                         m_entersound = true;
1853                 }
1854                 else
1855                 {
1856                         key_dest = key_game;
1857                         m_state = m_none;
1858                 }
1859                 break;
1860
1861         case 'Y':
1862         case 'y':
1863                 key_dest = key_console;
1864                 Host_Quit_f ();
1865                 break;
1866
1867         default:
1868                 break;
1869         }
1870
1871 }
1872
1873
1874 void M_Quit_Draw (void)
1875 {
1876         if (wasInMenus)
1877         {
1878                 m_state = m_quit_prevstate;
1879                 m_recursiveDraw = true;
1880                 M_Draw ();
1881                 m_state = m_quit;
1882         }
1883
1884 #ifdef _WIN32
1885         M_DrawTextBox (0, 0, 38, 23);
1886         M_PrintWhite (16, 12,  "  Quake version 1.09 by id Software\n\n");
1887         M_PrintWhite (16, 28,  "Programming        Art \n");
1888         M_Print (16, 36,  " John Carmack       Adrian Carmack\n");
1889         M_Print (16, 44,  " Michael Abrash     Kevin Cloud\n");
1890         M_Print (16, 52,  " John Cash          Paul Steed\n");
1891         M_Print (16, 60,  " Dave 'Zoid' Kirsch\n");
1892         M_PrintWhite (16, 68,  "Design             Biz\n");
1893         M_Print (16, 76,  " John Romero        Jay Wilbur\n");
1894         M_Print (16, 84,  " Sandy Petersen     Mike Wilson\n");
1895         M_Print (16, 92,  " American McGee     Donna Jackson\n");
1896         M_Print (16, 100,  " Tim Willits        Todd Hollenshead\n");
1897         M_PrintWhite (16, 108, "Support            Projects\n");
1898         M_Print (16, 116, " Barrett Alexander  Shawn Green\n");
1899         M_PrintWhite (16, 124, "Sound Effects\n");
1900         M_Print (16, 132, " Trent Reznor and Nine Inch Nails\n\n");
1901         M_PrintWhite (16, 140, "Quake is a trademark of Id Software,\n");
1902         M_PrintWhite (16, 148, "inc., (c)1996 Id Software, inc. All\n");
1903         M_PrintWhite (16, 156, "rights reserved. NIN logo is a\n");
1904         M_PrintWhite (16, 164, "registered trademark licensed to\n");
1905         M_PrintWhite (16, 172, "Nothing Interactive, Inc. All rights\n");
1906         M_PrintWhite (16, 180, "reserved. Press y to exit\n");
1907 #else
1908         M_DrawTextBox (56, 76, 24, 4);
1909         M_Print (64, 84,  quitMessage[msgNumber*4+0]);
1910         M_Print (64, 92,  quitMessage[msgNumber*4+1]);
1911         M_Print (64, 100, quitMessage[msgNumber*4+2]);
1912         M_Print (64, 108, quitMessage[msgNumber*4+3]);
1913 #endif
1914 }
1915
1916 //=============================================================================
1917 /* LAN CONFIG MENU */
1918
1919 int             lanConfig_cursor = -1;
1920 int             lanConfig_cursor_table [] = {72, 92, 124};
1921 #define NUM_LANCONFIG_CMDS      3
1922
1923 int     lanConfig_port;
1924 char    lanConfig_portname[6];
1925 char    lanConfig_joinname[22];
1926
1927 void M_Menu_LanConfig_f (void)
1928 {
1929         key_dest = key_menu;
1930         m_state = m_lanconfig;
1931         m_entersound = true;
1932         if (lanConfig_cursor == -1)
1933         {
1934                 if (JoiningGame && TCPIPConfig)
1935                         lanConfig_cursor = 2;
1936                 else
1937                         lanConfig_cursor = 1;
1938         }
1939         if (StartingGame && lanConfig_cursor == 2)
1940                 lanConfig_cursor = 1;
1941         lanConfig_port = DEFAULTnet_hostport;
1942         sprintf(lanConfig_portname, "%u", lanConfig_port);
1943
1944         m_return_onerror = false;
1945         m_return_reason[0] = 0;
1946 }
1947
1948
1949 void M_LanConfig_Draw (void)
1950 {
1951         qpic_t  *p;
1952         int             basex;
1953         char    *startJoin;
1954         char    *protocol;
1955
1956         M_DrawPic (16, 4, Draw_CachePic ("gfx/qplaque.lmp") );
1957         p = Draw_CachePic ("gfx/p_multi.lmp");
1958         basex = (320-p->width)/2;
1959         M_DrawPic (basex, 4, p);
1960
1961         if (StartingGame)
1962                 startJoin = "New Game";
1963         else
1964                 startJoin = "Join Game";
1965         if (IPXConfig)
1966                 protocol = "IPX";
1967         else
1968                 protocol = "TCP/IP";
1969         M_Print (basex, 32, va ("%s - %s", startJoin, protocol));
1970         basex += 8;
1971
1972         M_Print (basex, 52, "Address:");
1973         if (IPXConfig)
1974                 M_Print (basex+9*8, 52, my_ipx_address);
1975         else
1976                 M_Print (basex+9*8, 52, my_tcpip_address);
1977
1978         M_Print (basex, lanConfig_cursor_table[0], "Port");
1979         M_DrawTextBox (basex+8*8, lanConfig_cursor_table[0]-8, 6, 1);
1980         M_Print (basex+9*8, lanConfig_cursor_table[0], lanConfig_portname);
1981
1982         if (JoiningGame)
1983         {
1984                 M_Print (basex, lanConfig_cursor_table[1], "Search for local games...");
1985                 M_Print (basex, 108, "Join game at:");
1986                 M_DrawTextBox (basex+8, lanConfig_cursor_table[2]-8, 22, 1);
1987                 M_Print (basex+16, lanConfig_cursor_table[2], lanConfig_joinname);
1988         }
1989         else
1990         {
1991                 M_DrawTextBox (basex, lanConfig_cursor_table[1]-8, 2, 1);
1992                 M_Print (basex+8, lanConfig_cursor_table[1], "OK");
1993         }
1994
1995         M_DrawCharacter (basex-8, lanConfig_cursor_table [lanConfig_cursor], 12+((int)(realtime*4)&1));
1996
1997         if (lanConfig_cursor == 0)
1998                 M_DrawCharacter (basex+9*8 + 8*strlen(lanConfig_portname), lanConfig_cursor_table [0], 10+((int)(realtime*4)&1));
1999
2000         if (lanConfig_cursor == 2)
2001                 M_DrawCharacter (basex+16 + 8*strlen(lanConfig_joinname), lanConfig_cursor_table [2], 10+((int)(realtime*4)&1));
2002
2003         if (*m_return_reason)
2004                 M_PrintWhite (basex, 148, m_return_reason);
2005 }
2006
2007
2008 void M_LanConfig_Key (int key)
2009 {
2010         int             l;
2011
2012         switch (key)
2013         {
2014         case K_ESCAPE:
2015                 M_Menu_Net_f ();
2016                 break;
2017
2018         case K_UPARROW:
2019                 S_LocalSound ("misc/menu1.wav");
2020                 lanConfig_cursor--;
2021                 if (lanConfig_cursor < 0)
2022                         lanConfig_cursor = NUM_LANCONFIG_CMDS-1;
2023                 break;
2024
2025         case K_DOWNARROW:
2026                 S_LocalSound ("misc/menu1.wav");
2027                 lanConfig_cursor++;
2028                 if (lanConfig_cursor >= NUM_LANCONFIG_CMDS)
2029                         lanConfig_cursor = 0;
2030                 break;
2031
2032         case K_ENTER:
2033                 if (lanConfig_cursor == 0)
2034                         break;
2035
2036                 m_entersound = true;
2037
2038                 M_ConfigureNetSubsystem ();
2039
2040                 if (lanConfig_cursor == 1)
2041                 {
2042                         if (StartingGame)
2043                         {
2044                                 M_Menu_GameOptions_f ();
2045                                 break;
2046                         }
2047                         M_Menu_Search_f();
2048                         break;
2049                 }
2050
2051                 if (lanConfig_cursor == 2)
2052                 {
2053                         m_return_state = m_state;
2054                         m_return_onerror = true;
2055                         key_dest = key_game;
2056                         m_state = m_none;
2057                         Cbuf_AddText ( va ("connect \"%s\"\n", lanConfig_joinname) );
2058                         break;
2059                 }
2060
2061                 break;
2062
2063         case K_BACKSPACE:
2064                 if (lanConfig_cursor == 0)
2065                 {
2066                         if (strlen(lanConfig_portname))
2067                                 lanConfig_portname[strlen(lanConfig_portname)-1] = 0;
2068                 }
2069
2070                 if (lanConfig_cursor == 2)
2071                 {
2072                         if (strlen(lanConfig_joinname))
2073                                 lanConfig_joinname[strlen(lanConfig_joinname)-1] = 0;
2074                 }
2075                 break;
2076
2077         default:
2078                 if (key < 32 || key > 127)
2079                         break;
2080
2081                 if (lanConfig_cursor == 2)
2082                 {
2083                         l = strlen(lanConfig_joinname);
2084                         if (l < 21)
2085                         {
2086                                 lanConfig_joinname[l+1] = 0;
2087                                 lanConfig_joinname[l] = key;
2088                         }
2089                 }
2090
2091                 if (key < '0' || key > '9')
2092                         break;
2093                 if (lanConfig_cursor == 0)
2094                 {
2095                         l = strlen(lanConfig_portname);
2096                         if (l < 5)
2097                         {
2098                                 lanConfig_portname[l+1] = 0;
2099                                 lanConfig_portname[l] = key;
2100                         }
2101                 }
2102         }
2103
2104         if (StartingGame && lanConfig_cursor == 2)
2105         {
2106                 if (key == K_UPARROW)
2107                         lanConfig_cursor = 1;
2108                 else
2109                         lanConfig_cursor = 0;
2110         }
2111
2112         l =  atoi(lanConfig_portname);
2113         if (l > 65535)
2114                 l = lanConfig_port;
2115         else
2116                 lanConfig_port = l;
2117         sprintf(lanConfig_portname, "%u", lanConfig_port);
2118 }
2119
2120 //=============================================================================
2121 /* GAME OPTIONS MENU */
2122
2123 typedef struct
2124 {
2125         char    *name;
2126         char    *description;
2127 } level_t;
2128
2129 level_t         levels[] =
2130 {
2131         {"start", "Entrance"},  // 0
2132
2133         {"e1m1", "Slipgate Complex"},                           // 1
2134         {"e1m2", "Castle of the Damned"},
2135         {"e1m3", "The Necropolis"},
2136         {"e1m4", "The Grisly Grotto"},
2137         {"e1m5", "Gloom Keep"},
2138         {"e1m6", "The Door To Chthon"},
2139         {"e1m7", "The House of Chthon"},
2140         {"e1m8", "Ziggurat Vertigo"},
2141
2142         {"e2m1", "The Installation"},                           // 9
2143         {"e2m2", "Ogre Citadel"},
2144         {"e2m3", "Crypt of Decay"},
2145         {"e2m4", "The Ebon Fortress"},
2146         {"e2m5", "The Wizard's Manse"},
2147         {"e2m6", "The Dismal Oubliette"},
2148         {"e2m7", "Underearth"},
2149
2150         {"e3m1", "Termination Central"},                        // 16
2151         {"e3m2", "The Vaults of Zin"},
2152         {"e3m3", "The Tomb of Terror"},
2153         {"e3m4", "Satan's Dark Delight"},
2154         {"e3m5", "Wind Tunnels"},
2155         {"e3m6", "Chambers of Torment"},
2156         {"e3m7", "The Haunted Halls"},
2157
2158         {"e4m1", "The Sewage System"},                          // 23
2159         {"e4m2", "The Tower of Despair"},
2160         {"e4m3", "The Elder God Shrine"},
2161         {"e4m4", "The Palace of Hate"},
2162         {"e4m5", "Hell's Atrium"},
2163         {"e4m6", "The Pain Maze"},
2164         {"e4m7", "Azure Agony"},
2165         {"e4m8", "The Nameless City"},
2166
2167         {"end", "Shub-Niggurath's Pit"},                        // 31
2168
2169         {"dm1", "Place of Two Deaths"},                         // 32
2170         {"dm2", "Claustrophobopolis"},
2171         {"dm3", "The Abandoned Base"},
2172         {"dm4", "The Bad Place"},
2173         {"dm5", "The Cistern"},
2174         {"dm6", "The Dark Zone"}
2175 };
2176
2177 //MED 01/06/97 added hipnotic levels
2178 level_t     hipnoticlevels[] =
2179 {
2180    {"start", "Command HQ"},  // 0
2181
2182    {"hip1m1", "The Pumping Station"},          // 1
2183    {"hip1m2", "Storage Facility"},
2184    {"hip1m3", "The Lost Mine"},
2185    {"hip1m4", "Research Facility"},
2186    {"hip1m5", "Military Complex"},
2187
2188    {"hip2m1", "Ancient Realms"},          // 6
2189    {"hip2m2", "The Black Cathedral"},
2190    {"hip2m3", "The Catacombs"},
2191    {"hip2m4", "The Crypt"},
2192    {"hip2m5", "Mortum's Keep"},
2193    {"hip2m6", "The Gremlin's Domain"},
2194
2195    {"hip3m1", "Tur Torment"},       // 12
2196    {"hip3m2", "Pandemonium"},
2197    {"hip3m3", "Limbo"},
2198    {"hip3m4", "The Gauntlet"},
2199
2200    {"hipend", "Armagon's Lair"},       // 16
2201
2202    {"hipdm1", "The Edge of Oblivion"}           // 17
2203 };
2204
2205 //PGM 01/07/97 added rogue levels
2206 //PGM 03/02/97 added dmatch level
2207 level_t         roguelevels[] =
2208 {
2209         {"start",       "Split Decision"},
2210         {"r1m1",        "Deviant's Domain"},
2211         {"r1m2",        "Dread Portal"},
2212         {"r1m3",        "Judgement Call"},
2213         {"r1m4",        "Cave of Death"},
2214         {"r1m5",        "Towers of Wrath"},
2215         {"r1m6",        "Temple of Pain"},
2216         {"r1m7",        "Tomb of the Overlord"},
2217         {"r2m1",        "Tempus Fugit"},
2218         {"r2m2",        "Elemental Fury I"},
2219         {"r2m3",        "Elemental Fury II"},
2220         {"r2m4",        "Curse of Osiris"},
2221         {"r2m5",        "Wizard's Keep"},
2222         {"r2m6",        "Blood Sacrifice"},
2223         {"r2m7",        "Last Bastion"},
2224         {"r2m8",        "Source of Evil"},
2225         {"ctf1",    "Division of Change"}
2226 };
2227
2228 typedef struct
2229 {
2230         char    *description;
2231         int             firstLevel;
2232         int             levels;
2233 } episode_t;
2234
2235 episode_t       episodes[] =
2236 {
2237         {"Welcome to Quake", 0, 1},
2238         {"Doomed Dimension", 1, 8},
2239         {"Realm of Black Magic", 9, 7},
2240         {"Netherworld", 16, 7},
2241         {"The Elder World", 23, 8},
2242         {"Final Level", 31, 1},
2243         {"Deathmatch Arena", 32, 6}
2244 };
2245
2246 //MED 01/06/97  added hipnotic episodes
2247 episode_t   hipnoticepisodes[] =
2248 {
2249    {"Scourge of Armagon", 0, 1},
2250    {"Fortress of the Dead", 1, 5},
2251    {"Dominion of Darkness", 6, 6},
2252    {"The Rift", 12, 4},
2253    {"Final Level", 16, 1},
2254    {"Deathmatch Arena", 17, 1}
2255 };
2256
2257 //PGM 01/07/97 added rogue episodes
2258 //PGM 03/02/97 added dmatch episode
2259 episode_t       rogueepisodes[] =
2260 {
2261         {"Introduction", 0, 1},
2262         {"Hell's Fortress", 1, 7},
2263         {"Corridors of Time", 8, 8},
2264         {"Deathmatch Arena", 16, 1}
2265 };
2266
2267 level_t         nehahralevels[] =
2268 {
2269         {"nehstart",    "Welcome to Nehahra"},
2270         {"neh1m1",      "Forge City1: Slipgates"},
2271         {"neh1m2",      "Forge City2: Boiler"},
2272         {"neh1m3",      "Forge City3: Escape"},
2273         {"neh1m4",      "Grind Core"},
2274         {"neh1m5",      "Industrial Silence"},
2275         {"neh1m6",      "Locked-Up Anger"},
2276         {"neh1m7",      "Wanderer of the Wastes"},
2277         {"neh1m8",      "Artemis System Net"},
2278         {"neh1m9",      "To the Death"},
2279         {"neh2m1",      "The Gates of Ghoro"},
2280         {"neh2m2",      "Sacred Trinity"},
2281         {"neh2m3",      "Realm of the Ancients"},
2282         {"neh2m4",      "Temple of the Ancients"},
2283         {"neh2m5",      "Dreams Made Flesh"},
2284         {"neh2m6",      "Your Last Cup of Sorrow"},
2285         {"nehsec",      "Ogre's Bane"},
2286         {"nehahra",     "Nehahra's Den"},
2287         {"nehend",      "Quintessence"}
2288 };
2289
2290 episode_t       nehahraepisodes[] =
2291 {
2292         {"Welcome to Nehahra", 0, 1},
2293         {"The Fall of Forge", 1, 9},
2294         {"The Outlands", 10, 7},
2295         {"Dimension of the Lost", 17, 2}
2296 };
2297
2298 int     startepisode;
2299 int     startlevel;
2300 int maxplayers;
2301 qboolean m_serverInfoMessage = false;
2302 double m_serverInfoMessageTime;
2303
2304 void M_Menu_GameOptions_f (void)
2305 {
2306         key_dest = key_menu;
2307         m_state = m_gameoptions;
2308         m_entersound = true;
2309         if (maxplayers == 0)
2310                 maxplayers = svs.maxclients;
2311         if (maxplayers < 2)
2312                 maxplayers = svs.maxclientslimit;
2313 }
2314
2315
2316 int gameoptions_cursor_table[] = {40, 56, 64, 72, 80, 88, 96, 112, 120};
2317 #define NUM_GAMEOPTIONS 9
2318 int             gameoptions_cursor;
2319
2320 void M_GameOptions_Draw (void)
2321 {
2322         qpic_t  *p;
2323         int             x;
2324
2325         M_DrawPic (16, 4, Draw_CachePic ("gfx/qplaque.lmp") );
2326         p = Draw_CachePic ("gfx/p_multi.lmp");
2327         M_DrawPic ( (320-p->width)/2, 4, p);
2328
2329         M_DrawTextBox (152, 32, 10, 1);
2330         M_Print (160, 40, "begin game");
2331
2332         M_Print (0, 56, "      Max players");
2333         M_Print (160, 56, va("%i", maxplayers) );
2334
2335         M_Print (0, 64, "        Game Type");
2336         if (!coop.value && !deathmatch.value)
2337                 Cvar_SetValue("deathmatch", 1);
2338         if (coop.value)
2339                 M_Print (160, 64, "Cooperative");
2340         else
2341                 M_Print (160, 64, "Deathmatch");
2342
2343         M_Print (0, 72, "        Teamplay");
2344         if (rogue)
2345         {
2346                 char *msg;
2347
2348                 switch((int)teamplay.value)
2349                 {
2350                         case 1: msg = "No Friendly Fire"; break;
2351                         case 2: msg = "Friendly Fire"; break;
2352                         case 3: msg = "Tag"; break;
2353                         case 4: msg = "Capture the Flag"; break;
2354                         case 5: msg = "One Flag CTF"; break;
2355                         case 6: msg = "Three Team CTF"; break;
2356                         default: msg = "Off"; break;
2357                 }
2358                 M_Print (160, 72, msg);
2359         }
2360         else
2361         {
2362                 char *msg;
2363
2364                 switch((int)teamplay.value)
2365                 {
2366                         case 1: msg = "No Friendly Fire"; break;
2367                         case 2: msg = "Friendly Fire"; break;
2368                         default: msg = "Off"; break;
2369                 }
2370                 M_Print (160, 72, msg);
2371         }
2372
2373         M_Print (0, 80, "            Skill");
2374         if (skill.value == 0)
2375                 M_Print (160, 80, "Easy difficulty");
2376         else if (skill.value == 1)
2377                 M_Print (160, 80, "Normal difficulty");
2378         else if (skill.value == 2)
2379                 M_Print (160, 80, "Hard difficulty");
2380         else
2381                 M_Print (160, 80, "Nightmare difficulty");
2382
2383         M_Print (0, 88, "       Frag Limit");
2384         if (fraglimit.value == 0)
2385                 M_Print (160, 88, "none");
2386         else
2387                 M_Print (160, 88, va("%i frags", (int)fraglimit.value));
2388
2389         M_Print (0, 96, "       Time Limit");
2390         if (timelimit.value == 0)
2391                 M_Print (160, 96, "none");
2392         else
2393                 M_Print (160, 96, va("%i minutes", (int)timelimit.value));
2394
2395         M_Print (0, 112, "         Episode");
2396    //MED 01/06/97 added hipnotic episodes
2397    if (hipnotic)
2398       M_Print (160, 112, hipnoticepisodes[startepisode].description);
2399    //PGM 01/07/97 added rogue episodes
2400    else if (rogue)
2401       M_Print (160, 112, rogueepisodes[startepisode].description);
2402    else if (nehahra)
2403       M_Print (160, 112, nehahraepisodes[startepisode].description);
2404    else
2405       M_Print (160, 112, episodes[startepisode].description);
2406
2407         M_Print (0, 120, "           Level");
2408    //MED 01/06/97 added hipnotic episodes
2409    if (hipnotic)
2410    {
2411       M_Print (160, 120, hipnoticlevels[hipnoticepisodes[startepisode].firstLevel + startlevel].description);
2412       M_Print (160, 128, hipnoticlevels[hipnoticepisodes[startepisode].firstLevel + startlevel].name);
2413    }
2414    //PGM 01/07/97 added rogue episodes
2415    else if (rogue)
2416    {
2417       M_Print (160, 120, roguelevels[rogueepisodes[startepisode].firstLevel + startlevel].description);
2418       M_Print (160, 128, roguelevels[rogueepisodes[startepisode].firstLevel + startlevel].name);
2419    }
2420    else if (nehahra)
2421    {
2422       M_Print (160, 120, nehahralevels[nehahraepisodes[startepisode].firstLevel + startlevel].description);
2423       M_Print (160, 128, nehahralevels[nehahraepisodes[startepisode].firstLevel + startlevel].name);
2424    }
2425    else
2426    {
2427       M_Print (160, 120, levels[episodes[startepisode].firstLevel + startlevel].description);
2428       M_Print (160, 128, levels[episodes[startepisode].firstLevel + startlevel].name);
2429    }
2430
2431 // line cursor
2432         M_DrawCharacter (144, gameoptions_cursor_table[gameoptions_cursor], 12+((int)(realtime*4)&1));
2433
2434         if (m_serverInfoMessage)
2435         {
2436                 if ((realtime - m_serverInfoMessageTime) < 5.0)
2437                 {
2438                         x = (320-26*8)/2;
2439                         M_DrawTextBox (x, 138, 24, 4);
2440                         x += 8;
2441                         M_Print (x, 146, " More than 64 players?? ");
2442                         M_Print (x, 154, "  First, question your  ");
2443                         M_Print (x, 162, "   sanity, then email   ");
2444                         M_Print (x, 170, " havoc@gamevisions.com  ");
2445                         /*
2446                         M_Print (x, 146, "  More than 4 players   ");
2447                         M_Print (x, 154, " requires using command ");
2448                         M_Print (x, 162, "line parameters; please ");
2449                         M_Print (x, 170, "   see techinfo.txt.    ");
2450                         */
2451                 }
2452                 else
2453                 {
2454                         m_serverInfoMessage = false;
2455                 }
2456         }
2457 }
2458
2459
2460 void M_NetStart_Change (int dir)
2461 {
2462         int count;
2463
2464         switch (gameoptions_cursor)
2465         {
2466         case 1:
2467                 maxplayers += dir;
2468                 if (maxplayers > svs.maxclientslimit)
2469                 {
2470                         maxplayers = svs.maxclientslimit;
2471                         m_serverInfoMessage = true;
2472                         m_serverInfoMessageTime = realtime;
2473                 }
2474                 if (maxplayers < 2)
2475                         maxplayers = 2;
2476                 break;
2477
2478         case 2:
2479                 if (deathmatch.value) // changing from deathmatch to coop
2480                 {
2481                         Cvar_SetValue ("coop", 1);
2482                         Cvar_SetValue ("deathmatch", 0);
2483                 }
2484                 else // changing from coop to deathmatch
2485                 {
2486                         Cvar_SetValue ("coop", 0);
2487                         Cvar_SetValue ("deathmatch", 1);
2488                 }
2489                 break;
2490
2491         case 3:
2492                 if (rogue)
2493                         count = 6;
2494                 else
2495                         count = 2;
2496
2497                 Cvar_SetValue ("teamplay", teamplay.value + dir);
2498                 if (teamplay.value > count)
2499                         Cvar_SetValue ("teamplay", 0);
2500                 else if (teamplay.value < 0)
2501                         Cvar_SetValue ("teamplay", count);
2502                 break;
2503
2504         case 4:
2505                 Cvar_SetValue ("skill", skill.value + dir);
2506                 if (skill.value > 3)
2507                         Cvar_SetValue ("skill", 0);
2508                 if (skill.value < 0)
2509                         Cvar_SetValue ("skill", 3);
2510                 break;
2511
2512         case 5:
2513                 Cvar_SetValue ("fraglimit", fraglimit.value + dir*10);
2514                 if (fraglimit.value > 100)
2515                         Cvar_SetValue ("fraglimit", 0);
2516                 if (fraglimit.value < 0)
2517                         Cvar_SetValue ("fraglimit", 100);
2518                 break;
2519
2520         case 6:
2521                 Cvar_SetValue ("timelimit", timelimit.value + dir*5);
2522                 if (timelimit.value > 60)
2523                         Cvar_SetValue ("timelimit", 0);
2524                 if (timelimit.value < 0)
2525                         Cvar_SetValue ("timelimit", 60);
2526                 break;
2527
2528         case 7:
2529                 startepisode += dir;
2530         //MED 01/06/97 added hipnotic count
2531                 if (hipnotic)
2532                         count = 6;
2533         //PGM 01/07/97 added rogue count
2534         //PGM 03/02/97 added 1 for dmatch episode
2535                 else if (rogue)
2536                         count = 4;
2537                 else if (nehahra)
2538                         count = 4;
2539                 else if (registered.value)
2540                         count = 7;
2541                 else
2542                         count = 2;
2543
2544                 if (startepisode < 0)
2545                         startepisode = count - 1;
2546
2547                 if (startepisode >= count)
2548                         startepisode = 0;
2549
2550                 startlevel = 0;
2551                 break;
2552
2553         case 8:
2554                 startlevel += dir;
2555     //MED 01/06/97 added hipnotic episodes
2556                 if (hipnotic)
2557                         count = hipnoticepisodes[startepisode].levels;
2558         //PGM 01/06/97 added hipnotic episodes
2559                 else if (rogue)
2560                         count = rogueepisodes[startepisode].levels;
2561                 else if (nehahra)
2562                         count = nehahraepisodes[startepisode].levels;
2563                 else
2564                         count = episodes[startepisode].levels;
2565
2566                 if (startlevel < 0)
2567                         startlevel = count - 1;
2568
2569                 if (startlevel >= count)
2570                         startlevel = 0;
2571                 break;
2572         }
2573 }
2574
2575 void M_GameOptions_Key (int key)
2576 {
2577         switch (key)
2578         {
2579         case K_ESCAPE:
2580                 M_Menu_Net_f ();
2581                 break;
2582
2583         case K_UPARROW:
2584                 S_LocalSound ("misc/menu1.wav");
2585                 gameoptions_cursor--;
2586                 if (gameoptions_cursor < 0)
2587                         gameoptions_cursor = NUM_GAMEOPTIONS-1;
2588                 break;
2589
2590         case K_DOWNARROW:
2591                 S_LocalSound ("misc/menu1.wav");
2592                 gameoptions_cursor++;
2593                 if (gameoptions_cursor >= NUM_GAMEOPTIONS)
2594                         gameoptions_cursor = 0;
2595                 break;
2596
2597         case K_LEFTARROW:
2598                 if (gameoptions_cursor == 0)
2599                         break;
2600                 S_LocalSound ("misc/menu3.wav");
2601                 M_NetStart_Change (-1);
2602                 break;
2603
2604         case K_RIGHTARROW:
2605                 if (gameoptions_cursor == 0)
2606                         break;
2607                 S_LocalSound ("misc/menu3.wav");
2608                 M_NetStart_Change (1);
2609                 break;
2610
2611         case K_ENTER:
2612                 S_LocalSound ("misc/menu2.wav");
2613                 if (gameoptions_cursor == 0)
2614                 {
2615                         if (sv.active)
2616                                 Cbuf_AddText ("disconnect\n");
2617                         Cbuf_AddText ("listen 0\n");    // so host_netport will be re-examined
2618                         Cbuf_AddText ( va ("maxplayers %u\n", maxplayers) );
2619                         SCR_BeginLoadingPlaque ();
2620
2621                         if (hipnotic)
2622                                 Cbuf_AddText ( va ("map %s\n", hipnoticlevels[hipnoticepisodes[startepisode].firstLevel + startlevel].name) );
2623                         else if (rogue)
2624                                 Cbuf_AddText ( va ("map %s\n", roguelevels[rogueepisodes[startepisode].firstLevel + startlevel].name) );
2625                         else if (nehahra)
2626                                 Cbuf_AddText ( va ("map %s\n", nehahralevels[nehahraepisodes[startepisode].firstLevel + startlevel].name) );
2627                         else
2628                                 Cbuf_AddText ( va ("map %s\n", levels[episodes[startepisode].firstLevel + startlevel].name) );
2629
2630                         return;
2631                 }
2632
2633                 M_NetStart_Change (1);
2634                 break;
2635         }
2636 }
2637
2638 //=============================================================================
2639 /* SEARCH MENU */
2640
2641 qboolean        searchComplete = false;
2642 double          searchCompleteTime;
2643
2644 void M_Menu_Search_f (void)
2645 {
2646         key_dest = key_menu;
2647         m_state = m_search;
2648         m_entersound = false;
2649         slistSilent = true;
2650         slistLocal = false;
2651         searchComplete = false;
2652         NET_Slist_f();
2653
2654 }
2655
2656
2657 void M_Search_Draw (void)
2658 {
2659         qpic_t  *p;
2660         int x;
2661
2662         p = Draw_CachePic ("gfx/p_multi.lmp");
2663         M_DrawPic ( (320-p->width)/2, 4, p);
2664         x = (320/2) - ((12*8)/2) + 4;
2665         M_DrawTextBox (x-8, 32, 12, 1);
2666         M_Print (x, 40, "Searching...");
2667
2668         if(slistInProgress)
2669         {
2670                 NET_Poll();
2671                 return;
2672         }
2673
2674         if (! searchComplete)
2675         {
2676                 searchComplete = true;
2677                 searchCompleteTime = realtime;
2678         }
2679
2680         if (hostCacheCount)
2681         {
2682                 M_Menu_ServerList_f ();
2683                 return;
2684         }
2685
2686         M_PrintWhite ((320/2) - ((22*8)/2), 64, "No Quake servers found");
2687         if ((realtime - searchCompleteTime) < 3.0)
2688                 return;
2689
2690         M_Menu_LanConfig_f ();
2691 }
2692
2693
2694 void M_Search_Key (int key)
2695 {
2696 }
2697
2698 //=============================================================================
2699 /* SLIST MENU */
2700
2701 int             slist_cursor;
2702 qboolean slist_sorted;
2703
2704 void M_Menu_ServerList_f (void)
2705 {
2706         key_dest = key_menu;
2707         m_state = m_slist;
2708         m_entersound = true;
2709         slist_cursor = 0;
2710         m_return_onerror = false;
2711         m_return_reason[0] = 0;
2712         slist_sorted = false;
2713 }
2714
2715
2716 void M_ServerList_Draw (void)
2717 {
2718         int             n;
2719         char    string [64];
2720         qpic_t  *p;
2721
2722         if (!slist_sorted)
2723         {
2724                 if (hostCacheCount > 1)
2725                 {
2726                         int     i,j;
2727                         hostcache_t temp;
2728                         for (i = 0; i < hostCacheCount; i++)
2729                                 for (j = i+1; j < hostCacheCount; j++)
2730                                         if (strcmp(hostcache[j].name, hostcache[i].name) < 0)
2731                                         {
2732                                                 memcpy(&temp, &hostcache[j], sizeof(hostcache_t));
2733                                                 memcpy(&hostcache[j], &hostcache[i], sizeof(hostcache_t));
2734                                                 memcpy(&hostcache[i], &temp, sizeof(hostcache_t));
2735                                         }
2736                 }
2737                 slist_sorted = true;
2738         }
2739
2740         p = Draw_CachePic ("gfx/p_multi.lmp");
2741         M_DrawPic ( (320-p->width)/2, 4, p);
2742         for (n = 0; n < hostCacheCount; n++)
2743         {
2744                 if (hostcache[n].maxusers)
2745                         sprintf(string, "%-15.15s %-15.15s %2u/%2u\n", hostcache[n].name, hostcache[n].map, hostcache[n].users, hostcache[n].maxusers);
2746                 else
2747                         sprintf(string, "%-15.15s %-15.15s\n", hostcache[n].name, hostcache[n].map);
2748                 M_Print (16, 32 + 8*n, string);
2749         }
2750         M_DrawCharacter (0, 32 + slist_cursor*8, 12+((int)(realtime*4)&1));
2751
2752         if (*m_return_reason)
2753                 M_PrintWhite (16, 148, m_return_reason);
2754 }
2755
2756
2757 void M_ServerList_Key (int k)
2758 {
2759         switch (k)
2760         {
2761         case K_ESCAPE:
2762                 M_Menu_LanConfig_f ();
2763                 break;
2764
2765         case K_SPACE:
2766                 M_Menu_Search_f ();
2767                 break;
2768
2769         case K_UPARROW:
2770         case K_LEFTARROW:
2771                 S_LocalSound ("misc/menu1.wav");
2772                 slist_cursor--;
2773                 if (slist_cursor < 0)
2774                         slist_cursor = hostCacheCount - 1;
2775                 break;
2776
2777         case K_DOWNARROW:
2778         case K_RIGHTARROW:
2779                 S_LocalSound ("misc/menu1.wav");
2780                 slist_cursor++;
2781                 if (slist_cursor >= hostCacheCount)
2782                         slist_cursor = 0;
2783                 break;
2784
2785         case K_ENTER:
2786                 S_LocalSound ("misc/menu2.wav");
2787                 m_return_state = m_state;
2788                 m_return_onerror = true;
2789                 slist_sorted = false;
2790                 key_dest = key_game;
2791                 m_state = m_none;
2792                 Cbuf_AddText ( va ("connect \"%s\"\n", hostcache[slist_cursor].cname) );
2793                 break;
2794
2795         default:
2796                 break;
2797         }
2798
2799 }
2800
2801 //=============================================================================
2802 /* Menu Subsystem */
2803
2804
2805 void M_Init (void)
2806 {
2807         Cmd_AddCommand ("togglemenu", M_ToggleMenu_f);
2808
2809         Cmd_AddCommand ("menu_main", M_Menu_Main_f);
2810         Cmd_AddCommand ("menu_singleplayer", M_Menu_SinglePlayer_f);
2811         Cmd_AddCommand ("menu_load", M_Menu_Load_f);
2812         Cmd_AddCommand ("menu_save", M_Menu_Save_f);
2813         Cmd_AddCommand ("menu_multiplayer", M_Menu_MultiPlayer_f);
2814         Cmd_AddCommand ("menu_setup", M_Menu_Setup_f);
2815         Cmd_AddCommand ("menu_options", M_Menu_Options_f);
2816         Cmd_AddCommand ("menu_keys", M_Menu_Keys_f);
2817         Cmd_AddCommand ("menu_video", M_Menu_Video_f);
2818         Cmd_AddCommand ("help", M_Menu_Help_f);
2819         Cmd_AddCommand ("menu_quit", M_Menu_Quit_f);
2820
2821         if (nehahra)
2822         {
2823                 if (COM_FileExists("maps/neh1m4.bsp"))
2824                 {
2825                         if (COM_FileExists("hearing.dem"))
2826                         {
2827                                 Con_Printf("Nehahra movie and game detected.\n");
2828                                 NehGameType = TYPE_BOTH;
2829                         }
2830                         else
2831                         {
2832                                 Con_Printf("Nehahra game detected.\n");
2833                                 NehGameType = TYPE_GAME;
2834                         }
2835                 }
2836                 else
2837                 {
2838                         if (COM_FileExists("hearing.dem"))
2839                         {
2840                                 Con_Printf("Nehahra movie detected.\n");
2841                                 NehGameType = TYPE_DEMO;
2842                         }
2843                         else
2844                         {
2845                                 Con_Printf("Nehahra not found.\n");
2846                                 NehGameType = TYPE_GAME; // could just complain, but...
2847                         }
2848                 }
2849         }
2850 }
2851
2852
2853 void M_Draw (void)
2854 {
2855         if (m_state == m_none || key_dest != key_menu)
2856                 return;
2857
2858         if (!m_recursiveDraw)
2859         {
2860                 scr_copyeverything = 1;
2861
2862                 if (scr_con_current)
2863                 {
2864                         Draw_ConsoleBackground (vid.height);
2865                         S_ExtraUpdate ();
2866                 }
2867 //              else
2868 //                      Draw_FadeScreen ();
2869
2870                 scr_fullupdate = 0;
2871         }
2872         else
2873         {
2874                 m_recursiveDraw = false;
2875         }
2876
2877         switch (m_state)
2878         {
2879         case m_none:
2880                 break;
2881
2882         case m_main:
2883                 M_Main_Draw ();
2884                 break;
2885
2886         case m_demo:
2887                 M_Demo_Draw ();
2888                 break;
2889
2890         case m_singleplayer:
2891                 M_SinglePlayer_Draw ();
2892                 break;
2893
2894         case m_load:
2895                 M_Load_Draw ();
2896                 break;
2897
2898         case m_save:
2899                 M_Save_Draw ();
2900                 break;
2901
2902         case m_multiplayer:
2903                 M_MultiPlayer_Draw ();
2904                 break;
2905
2906         case m_setup:
2907                 M_Setup_Draw ();
2908                 break;
2909
2910         case m_net:
2911                 M_Net_Draw ();
2912                 break;
2913
2914         case m_options:
2915                 M_Options_Draw ();
2916                 break;
2917
2918         case m_keys:
2919                 M_Keys_Draw ();
2920                 break;
2921
2922         case m_video:
2923                 M_Video_Draw ();
2924                 break;
2925
2926         case m_help:
2927                 M_Help_Draw ();
2928                 break;
2929
2930         case m_quit:
2931                 M_Quit_Draw ();
2932                 break;
2933
2934         case m_lanconfig:
2935                 M_LanConfig_Draw ();
2936                 break;
2937
2938         case m_gameoptions:
2939                 M_GameOptions_Draw ();
2940                 break;
2941
2942         case m_search:
2943                 M_Search_Draw ();
2944                 break;
2945
2946         case m_slist:
2947                 M_ServerList_Draw ();
2948                 break;
2949         }
2950
2951         if (m_entersound)
2952         {
2953                 S_LocalSound ("misc/menu2.wav");
2954                 m_entersound = false;
2955         }
2956
2957         S_ExtraUpdate ();
2958 }
2959
2960
2961 void M_Keydown (int key)
2962 {
2963         switch (m_state)
2964         {
2965         case m_none:
2966                 return;
2967
2968         case m_main:
2969                 M_Main_Key (key);
2970                 return;
2971
2972         case m_demo:
2973                 M_Demo_Key (key);
2974                 return;
2975
2976         case m_singleplayer:
2977                 M_SinglePlayer_Key (key);
2978                 return;
2979
2980         case m_load:
2981                 M_Load_Key (key);
2982                 return;
2983
2984         case m_save:
2985                 M_Save_Key (key);
2986                 return;
2987
2988         case m_multiplayer:
2989                 M_MultiPlayer_Key (key);
2990                 return;
2991
2992         case m_setup:
2993                 M_Setup_Key (key);
2994                 return;
2995
2996         case m_net:
2997                 M_Net_Key (key);
2998                 return;
2999
3000         case m_options:
3001                 M_Options_Key (key);
3002                 return;
3003
3004         case m_keys:
3005                 M_Keys_Key (key);
3006                 return;
3007
3008         case m_video:
3009                 M_Video_Key (key);
3010                 return;
3011
3012         case m_help:
3013                 M_Help_Key (key);
3014                 return;
3015
3016         case m_quit:
3017                 M_Quit_Key (key);
3018                 return;
3019
3020         case m_lanconfig:
3021                 M_LanConfig_Key (key);
3022                 return;
3023
3024         case m_gameoptions:
3025                 M_GameOptions_Key (key);
3026                 return;
3027
3028         case m_search:
3029                 M_Search_Key (key);
3030                 break;
3031
3032         case m_slist:
3033                 M_ServerList_Key (key);
3034                 return;
3035         }
3036 }
3037
3038
3039 void M_ConfigureNetSubsystem(void)
3040 {
3041 // enable/disable net systems to match desired config
3042
3043         Cbuf_AddText ("stopdemo\n");
3044
3045         if (IPXConfig || TCPIPConfig)
3046                 net_hostport = lanConfig_port;
3047 }