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