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