]> icculus.org git repositories - divverent/darkplaces.git/blob - menu.c
DarkPlaces should always wait for connections in BloodBath mode
[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 // Map list for BloodBath
2339 level_t         bloodbathlevels[] =
2340 {
2341         {"bb1",         "The Stronghold"},
2342         {"bb2",         "Winter Wonderland"},
2343         {"bb3",         "Bodies"},
2344         {"bb4",         "The Tower"},
2345         {"bb5",         "Click!"},
2346         {"bb7",         "Midgard"},
2347         {"bb8",         "Fun With Heads"},
2348
2349         {"dm1",         "Monolith Building #11"},
2350         {"dm2",         "Power!"},
2351         {"dm3",         "Area 15"},
2352
2353         {"cpbb01",      "Crypt of Despair"},
2354         {"cpbb03",      "Unholy Cathedral"},
2355
2356         {"b2a15",       "A15"},
2357         {"barena",      "Blood Arena"},
2358         {"bkeep",       "Blood Keep"},
2359         {"bstar",       "Brownstar"},
2360         {"crypt",       "The Crypt"},
2361
2362         {"bb3_2k1",     "Bodies Infusion"},
2363         {"qbb1",        "The Confluence"},
2364         {"qbb2",        "Kathartic"},
2365         {"qbb3",        "Caleb's Woodland Retreat"},
2366         {"qe1m7",       "The House of Chthon"}
2367 };
2368
2369 episode_t       bloodbathepisodes[] =
2370 {
2371         {"Blood", 0, 7},
2372         {"Plasma Pack", 7, 3},
2373         {"Cryptic Passage", 10, 2},
2374         {"Blood 2", 12, 5},
2375         {"BloodBath", 17, 5}
2376 };
2377
2378 gamelevels_t sharewarequakegame = {"Shareware Quake", quakelevels, quakeepisodes, 2};
2379 gamelevels_t registeredquakegame = {"Quake", quakelevels, quakeepisodes, 7};
2380 gamelevels_t hipnoticgame = {"Scourge of Armagon", hipnoticlevels, hipnoticepisodes, 6};
2381 gamelevels_t roguegame = {"Dissolution of Eternity", roguelevels, rogueepisodes, 4};
2382 gamelevels_t nehahragame = {"Nehahra", nehahralevels, nehahraepisodes, 4};
2383 gamelevels_t bloodbathgame = {"BloodBath", bloodbathlevels, bloodbathepisodes, 5};
2384
2385 typedef struct
2386 {
2387         int gameid;
2388         gamelevels_t *notregistered;
2389         gamelevels_t *registered;
2390 }
2391 gameinfo_t;
2392
2393 gameinfo_t gamelist[] =
2394 {
2395         {GAME_NORMAL, &sharewarequakegame, &registeredquakegame},
2396         {GAME_HIPNOTIC, &hipnoticgame, &hipnoticgame},
2397         {GAME_ROGUE, &roguegame, &roguegame},
2398         {GAME_NEHAHRA, &nehahragame, &nehahragame},
2399         {GAME_FIENDARENA, &sharewarequakegame, &registeredquakegame},
2400         {GAME_ZYMOTIC, &sharewarequakegame, &registeredquakegame},
2401         {GAME_BLOODBATH, &bloodbathgame, &bloodbathgame},
2402         {-1, &sharewarequakegame, &registeredquakegame} // final fallback
2403 };
2404
2405 gamelevels_t *lookupgameinfo(void)
2406 {
2407         int i;
2408         for (i = 0;gamelist[i].gameid >= 0 && gamelist[i].gameid != gamemode;i++);
2409         if (registered.integer)
2410                 return gamelist[i].registered;
2411         else
2412                 return gamelist[i].notregistered;
2413 }
2414
2415 int     startepisode;
2416 int     startlevel;
2417 int maxplayers;
2418 qboolean m_serverInfoMessage = false;
2419 double m_serverInfoMessageTime;
2420
2421 void M_Menu_GameOptions_f (void)
2422 {
2423         key_dest = key_menu;
2424         m_state = m_gameoptions;
2425         m_entersound = true;
2426         if (maxplayers == 0)
2427                 maxplayers = svs.maxclients;
2428         if (maxplayers < 2)
2429                 maxplayers = svs.maxclientslimit;
2430 }
2431
2432
2433 int gameoptions_cursor_table[] = {40, 56, 64, 72, 80, 88, 96, 112, 120};
2434 #define NUM_GAMEOPTIONS 9
2435 int             gameoptions_cursor;
2436
2437 void M_GameOptions_Draw (void)
2438 {
2439         cachepic_t      *p;
2440         int             x;
2441         gamelevels_t *g;
2442
2443         M_DrawPic (16, 4, "gfx/qplaque.lmp");
2444         p = Draw_CachePic ("gfx/p_multi.lmp");
2445         M_DrawPic ( (320-p->width)/2, 4, "gfx/p_multi.lmp");
2446
2447         M_DrawTextBox (152, 32, 10, 1);
2448         M_Print (160, 40, "begin game");
2449
2450         M_Print (0, 56, "      Max players");
2451         M_Print (160, 56, va("%i", maxplayers) );
2452
2453         M_Print (0, 64, "        Game Type");
2454         if (!coop.integer && !deathmatch.integer)
2455                 Cvar_SetValue("deathmatch", 1);
2456         if (coop.integer)
2457                 M_Print (160, 64, "Cooperative");
2458         else
2459                 M_Print (160, 64, "Deathmatch");
2460
2461         M_Print (0, 72, "        Teamplay");
2462         if (gamemode == GAME_ROGUE)
2463         {
2464                 char *msg;
2465
2466                 switch((int)teamplay.integer)
2467                 {
2468                         case 1: msg = "No Friendly Fire"; break;
2469                         case 2: msg = "Friendly Fire"; break;
2470                         case 3: msg = "Tag"; break;
2471                         case 4: msg = "Capture the Flag"; break;
2472                         case 5: msg = "One Flag CTF"; break;
2473                         case 6: msg = "Three Team CTF"; break;
2474                         default: msg = "Off"; break;
2475                 }
2476                 M_Print (160, 72, msg);
2477         }
2478         else
2479         {
2480                 char *msg;
2481
2482                 switch((int)teamplay.integer)
2483                 {
2484                         case 1: msg = "No Friendly Fire"; break;
2485                         case 2: msg = "Friendly Fire"; break;
2486                         default: msg = "Off"; break;
2487                 }
2488                 M_Print (160, 72, msg);
2489         }
2490
2491         M_Print (0, 80, "            Skill");
2492         if (skill.integer == 0)
2493                 M_Print (160, 80, "Easy difficulty");
2494         else if (skill.integer == 1)
2495                 M_Print (160, 80, "Normal difficulty");
2496         else if (skill.integer == 2)
2497                 M_Print (160, 80, "Hard difficulty");
2498         else
2499                 M_Print (160, 80, "Nightmare difficulty");
2500
2501         M_Print (0, 88, "       Frag Limit");
2502         if (fraglimit.integer == 0)
2503                 M_Print (160, 88, "none");
2504         else
2505                 M_Print (160, 88, va("%i frags", fraglimit.integer));
2506
2507         M_Print (0, 96, "       Time Limit");
2508         if (timelimit.integer == 0)
2509                 M_Print (160, 96, "none");
2510         else
2511                 M_Print (160, 96, va("%i minutes", timelimit.integer));
2512
2513         g = lookupgameinfo();
2514
2515         M_Print (0, 112, "         Episode");
2516         M_Print (160, 112, g->episodes[startepisode].description);
2517
2518         M_Print (0, 120, "           Level");
2519         M_Print (160, 120, g->levels[g->episodes[startepisode].firstLevel + startlevel].description);
2520         M_Print (160, 128, g->levels[g->episodes[startepisode].firstLevel + startlevel].name);
2521
2522 // line cursor
2523         M_DrawCharacter (144, gameoptions_cursor_table[gameoptions_cursor], 12+((int)(realtime*4)&1));
2524
2525         if (m_serverInfoMessage)
2526         {
2527                 if ((realtime - m_serverInfoMessageTime) < 5.0)
2528                 {
2529                         x = (320-26*8)/2;
2530                         M_DrawTextBox (x, 138, 24, 4);
2531                         x += 8;
2532                         M_Print (x, 146, " More than 64 players?? ");
2533                         M_Print (x, 154, "  First, question your  ");
2534                         M_Print (x, 162, "   sanity, then email   ");
2535                         M_Print (x, 170, " havoc@gamevisions.com  ");
2536                         /*
2537                         M_Print (x, 146, "  More than 4 players   ");
2538                         M_Print (x, 154, " requires using command ");
2539                         M_Print (x, 162, "line parameters; please ");
2540                         M_Print (x, 170, "   see techinfo.txt.    ");
2541                         */
2542                 }
2543                 else
2544                 {
2545                         m_serverInfoMessage = false;
2546                 }
2547         }
2548 }
2549
2550
2551 void M_NetStart_Change (int dir)
2552 {
2553         gamelevels_t *g;
2554         int count;
2555
2556         switch (gameoptions_cursor)
2557         {
2558         case 1:
2559                 maxplayers += dir;
2560                 if (maxplayers > svs.maxclientslimit)
2561                 {
2562                         maxplayers = svs.maxclientslimit;
2563                         m_serverInfoMessage = true;
2564                         m_serverInfoMessageTime = realtime;
2565                 }
2566                 if (maxplayers < 2)
2567                         maxplayers = 2;
2568                 break;
2569
2570         case 2:
2571                 if (deathmatch.integer) // changing from deathmatch to coop
2572                 {
2573                         Cvar_SetValueQuick (&coop, 1);
2574                         Cvar_SetValueQuick (&deathmatch, 0);
2575                 }
2576                 else // changing from coop to deathmatch
2577                 {
2578                         Cvar_SetValueQuick (&coop, 0);
2579                         Cvar_SetValueQuick (&deathmatch, 1);
2580                 }
2581                 break;
2582
2583         case 3:
2584                 if (gamemode == GAME_ROGUE)
2585                         count = 6;
2586                 else
2587                         count = 2;
2588
2589                 Cvar_SetValueQuick (&teamplay, teamplay.integer + dir);
2590                 if (teamplay.integer > count)
2591                         Cvar_SetValueQuick (&teamplay, 0);
2592                 else if (teamplay.integer < 0)
2593                         Cvar_SetValueQuick (&teamplay, count);
2594                 break;
2595
2596         case 4:
2597                 Cvar_SetValueQuick (&skill, skill.integer + dir);
2598                 if (skill.integer > 3)
2599                         Cvar_SetValueQuick (&skill, 0);
2600                 if (skill.integer < 0)
2601                         Cvar_SetValueQuick (&skill, 3);
2602                 break;
2603
2604         case 5:
2605                 Cvar_SetValueQuick (&fraglimit, fraglimit.integer + dir*10);
2606                 if (fraglimit.integer > 100)
2607                         Cvar_SetValueQuick (&fraglimit, 0);
2608                 if (fraglimit.integer < 0)
2609                         Cvar_SetValueQuick (&fraglimit, 100);
2610                 break;
2611
2612         case 6:
2613                 Cvar_SetValueQuick (&timelimit, timelimit.value + dir*5);
2614                 if (timelimit.value > 60)
2615                         Cvar_SetValueQuick (&timelimit, 0);
2616                 if (timelimit.value < 0)
2617                         Cvar_SetValueQuick (&timelimit, 60);
2618                 break;
2619
2620         case 7:
2621                 startepisode += dir;
2622                 g = lookupgameinfo();
2623
2624                 if (startepisode < 0)
2625                         startepisode = g->numepisodes - 1;
2626
2627                 if (startepisode >= g->numepisodes)
2628                         startepisode = 0;
2629
2630                 startlevel = 0;
2631                 break;
2632
2633         case 8:
2634                 startlevel += dir;
2635                 g = lookupgameinfo();
2636
2637                 if (startlevel < 0)
2638                         startlevel = g->episodes[startepisode].levels - 1;
2639
2640                 if (startlevel >= g->episodes[startepisode].levels)
2641                         startlevel = 0;
2642                 break;
2643         }
2644 }
2645
2646 void M_GameOptions_Key (int key)
2647 {
2648         gamelevels_t *g;
2649
2650         switch (key)
2651         {
2652         case K_ESCAPE:
2653                 M_Menu_Net_f ();
2654                 break;
2655
2656         case K_UPARROW:
2657                 S_LocalSound ("misc/menu1.wav");
2658                 gameoptions_cursor--;
2659                 if (gameoptions_cursor < 0)
2660                         gameoptions_cursor = NUM_GAMEOPTIONS-1;
2661                 break;
2662
2663         case K_DOWNARROW:
2664                 S_LocalSound ("misc/menu1.wav");
2665                 gameoptions_cursor++;
2666                 if (gameoptions_cursor >= NUM_GAMEOPTIONS)
2667                         gameoptions_cursor = 0;
2668                 break;
2669
2670         case K_LEFTARROW:
2671                 if (gameoptions_cursor == 0)
2672                         break;
2673                 S_LocalSound ("misc/menu3.wav");
2674                 M_NetStart_Change (-1);
2675                 break;
2676
2677         case K_RIGHTARROW:
2678                 if (gameoptions_cursor == 0)
2679                         break;
2680                 S_LocalSound ("misc/menu3.wav");
2681                 M_NetStart_Change (1);
2682                 break;
2683
2684         case K_ENTER:
2685                 S_LocalSound ("misc/menu2.wav");
2686                 if (gameoptions_cursor == 0)
2687                 {
2688                         if (sv.active)
2689                                 Cbuf_AddText ("disconnect\n");
2690                         Cbuf_AddText ("listen 0\n");    // so host_netport will be re-examined
2691                         Cbuf_AddText ( va ("maxplayers %u\n", maxplayers) );
2692
2693                         g = lookupgameinfo();
2694                         Cbuf_AddText ( va ("map %s\n", g->levels[g->episodes[startepisode].firstLevel + startlevel].name) );
2695                         return;
2696                 }
2697
2698                 M_NetStart_Change (1);
2699                 break;
2700         }
2701 }
2702
2703 //=============================================================================
2704 /* SEARCH MENU */
2705
2706 qboolean        searchComplete = false;
2707 double          searchCompleteTime;
2708
2709 void M_Menu_Search_f (void)
2710 {
2711         key_dest = key_menu;
2712         m_state = m_search;
2713         m_entersound = false;
2714         slistSilent = true;
2715         slistLocal = false;
2716         searchComplete = false;
2717         NET_Slist_f();
2718
2719 }
2720
2721
2722 void M_Search_Draw (void)
2723 {
2724         cachepic_t      *p;
2725         int x;
2726
2727         p = Draw_CachePic ("gfx/p_multi.lmp");
2728         M_DrawPic ( (320-p->width)/2, 4, "gfx/p_multi.lmp");
2729         x = (320/2) - ((12*8)/2) + 4;
2730         M_DrawTextBox (x-8, 32, 12, 1);
2731         M_Print (x, 40, "Searching...");
2732
2733         if(slistInProgress)
2734         {
2735                 NET_Poll();
2736                 return;
2737         }
2738
2739         if (! searchComplete)
2740         {
2741                 searchComplete = true;
2742                 searchCompleteTime = realtime;
2743         }
2744
2745         if (hostCacheCount)
2746         {
2747                 M_Menu_ServerList_f ();
2748                 return;
2749         }
2750
2751         M_PrintWhite ((320/2) - ((22*8)/2), 64, "No Quake servers found");
2752         if ((realtime - searchCompleteTime) < 3.0)
2753                 return;
2754
2755         M_Menu_LanConfig_f ();
2756 }
2757
2758
2759 void M_Search_Key (int key)
2760 {
2761 }
2762
2763 //=============================================================================
2764 /* SLIST MENU */
2765
2766 int             slist_cursor;
2767 qboolean slist_sorted;
2768
2769 void M_Menu_ServerList_f (void)
2770 {
2771         key_dest = key_menu;
2772         m_state = m_slist;
2773         m_entersound = true;
2774         slist_cursor = 0;
2775         m_return_onerror = false;
2776         m_return_reason[0] = 0;
2777         slist_sorted = false;
2778 }
2779
2780
2781 void M_ServerList_Draw (void)
2782 {
2783         int             n;
2784         char    string [64];
2785         cachepic_t      *p;
2786
2787         if (!slist_sorted)
2788         {
2789                 if (hostCacheCount > 1)
2790                 {
2791                         int     i,j;
2792                         hostcache_t temp;
2793                         for (i = 0; i < hostCacheCount; i++)
2794                                 for (j = i+1; j < hostCacheCount; j++)
2795                                         if (strcmp(hostcache[j].name, hostcache[i].name) < 0)
2796                                         {
2797                                                 memcpy(&temp, &hostcache[j], sizeof(hostcache_t));
2798                                                 memcpy(&hostcache[j], &hostcache[i], sizeof(hostcache_t));
2799                                                 memcpy(&hostcache[i], &temp, sizeof(hostcache_t));
2800                                         }
2801                 }
2802                 slist_sorted = true;
2803         }
2804
2805         p = Draw_CachePic ("gfx/p_multi.lmp");
2806         M_DrawPic ( (320-p->width)/2, 4, "gfx/p_multi.lmp");
2807         for (n = 0; n < hostCacheCount; n++)
2808         {
2809                 if (hostcache[n].maxusers)
2810                         sprintf(string, "%-15.15s %-15.15s %2u/%2u\n", hostcache[n].name, hostcache[n].map, hostcache[n].users, hostcache[n].maxusers);
2811                 else
2812                         sprintf(string, "%-15.15s %-15.15s\n", hostcache[n].name, hostcache[n].map);
2813                 M_Print (16, 32 + 8*n, string);
2814         }
2815         M_DrawCharacter (0, 32 + slist_cursor*8, 12+((int)(realtime*4)&1));
2816
2817         if (*m_return_reason)
2818                 M_PrintWhite (16, 148, m_return_reason);
2819 }
2820
2821
2822 void M_ServerList_Key (int k)
2823 {
2824         switch (k)
2825         {
2826         case K_ESCAPE:
2827                 M_Menu_LanConfig_f ();
2828                 break;
2829
2830         case K_SPACE:
2831                 M_Menu_Search_f ();
2832                 break;
2833
2834         case K_UPARROW:
2835         case K_LEFTARROW:
2836                 S_LocalSound ("misc/menu1.wav");
2837                 slist_cursor--;
2838                 if (slist_cursor < 0)
2839                         slist_cursor = hostCacheCount - 1;
2840                 break;
2841
2842         case K_DOWNARROW:
2843         case K_RIGHTARROW:
2844                 S_LocalSound ("misc/menu1.wav");
2845                 slist_cursor++;
2846                 if (slist_cursor >= hostCacheCount)
2847                         slist_cursor = 0;
2848                 break;
2849
2850         case K_ENTER:
2851                 S_LocalSound ("misc/menu2.wav");
2852                 m_return_state = m_state;
2853                 m_return_onerror = true;
2854                 slist_sorted = false;
2855                 key_dest = key_game;
2856                 m_state = m_none;
2857                 Cbuf_AddText ( va ("connect \"%s\"\n", hostcache[slist_cursor].cname) );
2858                 break;
2859
2860         default:
2861                 break;
2862         }
2863
2864 }
2865
2866 //=============================================================================
2867 /* Menu Subsystem */
2868
2869
2870 void M_Init (void)
2871 {
2872         Cmd_AddCommand ("togglemenu", M_ToggleMenu_f);
2873
2874         Cmd_AddCommand ("menu_main", M_Menu_Main_f);
2875         Cmd_AddCommand ("menu_singleplayer", M_Menu_SinglePlayer_f);
2876         Cmd_AddCommand ("menu_load", M_Menu_Load_f);
2877         Cmd_AddCommand ("menu_save", M_Menu_Save_f);
2878         Cmd_AddCommand ("menu_multiplayer", M_Menu_MultiPlayer_f);
2879         Cmd_AddCommand ("menu_setup", M_Menu_Setup_f);
2880         Cmd_AddCommand ("menu_options", M_Menu_Options_f);
2881         Cmd_AddCommand ("menu_keys", M_Menu_Keys_f);
2882         Cmd_AddCommand ("menu_video", M_Menu_Video_f);
2883         Cmd_AddCommand ("help", M_Menu_Help_f);
2884         Cmd_AddCommand ("menu_quit", M_Menu_Quit_f);
2885
2886         if (gamemode == GAME_NEHAHRA)
2887         {
2888                 if (COM_FileExists("maps/neh1m4.bsp"))
2889                 {
2890                         if (COM_FileExists("hearing.dem"))
2891                         {
2892                                 Con_Printf("Nehahra movie and game detected.\n");
2893                                 NehGameType = TYPE_BOTH;
2894                         }
2895                         else
2896                         {
2897                                 Con_Printf("Nehahra game detected.\n");
2898                                 NehGameType = TYPE_GAME;
2899                         }
2900                 }
2901                 else
2902                 {
2903                         if (COM_FileExists("hearing.dem"))
2904                         {
2905                                 Con_Printf("Nehahra movie detected.\n");
2906                                 NehGameType = TYPE_DEMO;
2907                         }
2908                         else
2909                         {
2910                                 Con_Printf("Nehahra not found.\n");
2911                                 NehGameType = TYPE_GAME; // could just complain, but...
2912                         }
2913                 }
2914         }
2915 }
2916
2917 void M_Draw (void)
2918 {
2919         if (m_state == m_none || key_dest != key_menu)
2920                 return;
2921
2922         M_DrawBackground();
2923
2924         switch (m_state)
2925         {
2926         case m_none:
2927                 break;
2928
2929         case m_main:
2930                 M_Main_Draw ();
2931                 break;
2932
2933         case m_demo:
2934                 M_Demo_Draw ();
2935                 break;
2936
2937         case m_singleplayer:
2938                 M_SinglePlayer_Draw ();
2939                 break;
2940
2941         case m_load:
2942                 M_Load_Draw ();
2943                 break;
2944
2945         case m_save:
2946                 M_Save_Draw ();
2947                 break;
2948
2949         case m_multiplayer:
2950                 M_MultiPlayer_Draw ();
2951                 break;
2952
2953         case m_setup:
2954                 M_Setup_Draw ();
2955                 break;
2956
2957         case m_net:
2958                 M_Net_Draw ();
2959                 break;
2960
2961         case m_options:
2962                 M_Options_Draw ();
2963                 break;
2964
2965         case m_keys:
2966                 M_Keys_Draw ();
2967                 break;
2968
2969         case m_video:
2970                 M_Video_Draw ();
2971                 break;
2972
2973         case m_help:
2974                 M_Help_Draw ();
2975                 break;
2976
2977         case m_quit:
2978                 M_Quit_Draw ();
2979                 break;
2980
2981         case m_lanconfig:
2982                 M_LanConfig_Draw ();
2983                 break;
2984
2985         case m_gameoptions:
2986                 M_GameOptions_Draw ();
2987                 break;
2988
2989         case m_search:
2990                 M_Search_Draw ();
2991                 break;
2992
2993         case m_slist:
2994                 M_ServerList_Draw ();
2995                 break;
2996         }
2997
2998         if (m_entersound)
2999         {
3000                 S_LocalSound ("misc/menu2.wav");
3001                 m_entersound = false;
3002         }
3003
3004         S_ExtraUpdate ();
3005 }
3006
3007
3008 void M_Keydown (int key)
3009 {
3010         switch (m_state)
3011         {
3012         case m_none:
3013                 return;
3014
3015         case m_main:
3016                 M_Main_Key (key);
3017                 return;
3018
3019         case m_demo:
3020                 M_Demo_Key (key);
3021                 return;
3022
3023         case m_singleplayer:
3024                 M_SinglePlayer_Key (key);
3025                 return;
3026
3027         case m_load:
3028                 M_Load_Key (key);
3029                 return;
3030
3031         case m_save:
3032                 M_Save_Key (key);
3033                 return;
3034
3035         case m_multiplayer:
3036                 M_MultiPlayer_Key (key);
3037                 return;
3038
3039         case m_setup:
3040                 M_Setup_Key (key);
3041                 return;
3042
3043         case m_net:
3044                 M_Net_Key (key);
3045                 return;
3046
3047         case m_options:
3048                 M_Options_Key (key);
3049                 return;
3050
3051         case m_keys:
3052                 M_Keys_Key (key);
3053                 return;
3054
3055         case m_video:
3056                 M_Video_Key (key);
3057                 return;
3058
3059         case m_help:
3060                 M_Help_Key (key);
3061                 return;
3062
3063         case m_quit:
3064                 M_Quit_Key (key);
3065                 return;
3066
3067         case m_lanconfig:
3068                 M_LanConfig_Key (key);
3069                 return;
3070
3071         case m_gameoptions:
3072                 M_GameOptions_Key (key);
3073                 return;
3074
3075         case m_search:
3076                 M_Search_Key (key);
3077                 break;
3078
3079         case m_slist:
3080                 M_ServerList_Key (key);
3081                 return;
3082         }
3083 }
3084
3085
3086 void M_ConfigureNetSubsystem(void)
3087 {
3088 // enable/disable net systems to match desired config
3089
3090         Cbuf_AddText ("stopdemo\n");
3091
3092         if (IPXConfig || TCPIPConfig)
3093                 net_hostport = lanConfig_port;
3094 }