]> icculus.org git repositories - divverent/darkplaces.git/blob - menu.c
improved client number check to prevent potential crashs
[divverent/darkplaces.git] / menu.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19 */
20 #include "quakedef.h"
21
22 #ifdef _WIN32
23 #include "winquake.h"
24 #endif
25
26 void (*vid_menudrawfn)(void);
27 void (*vid_menukeyfn)(int key);
28
29 #define TYPE_DEMO 1
30 #define TYPE_GAME 2
31 #define TYPE_BOTH 3
32
33 int NehGameType;
34
35 enum m_state_e m_state;
36
37 void M_Menu_Main_f (void);
38         void M_Menu_SinglePlayer_f (void);
39                 void M_Menu_Load_f (void);
40                 void M_Menu_Save_f (void);
41         void M_Menu_MultiPlayer_f (void);
42                 void M_Menu_Setup_f (void);
43                 void M_Menu_Net_f (void);
44         void M_Menu_Options_f (void);
45                 void M_Menu_Keys_f (void);
46                 void M_Menu_Video_f (void);
47         void M_Menu_Help_f (void);
48         void M_Menu_Quit_f (void);
49 void M_Menu_LanConfig_f (void);
50 void M_Menu_GameOptions_f (void);
51 void M_Menu_Search_f (void);
52 void M_Menu_ServerList_f (void);
53
54 void M_Main_Draw (void);
55         void M_SinglePlayer_Draw (void);
56                 void M_Load_Draw (void);
57                 void M_Save_Draw (void);
58         void M_MultiPlayer_Draw (void);
59                 void M_Setup_Draw (void);
60                 void M_Net_Draw (void);
61         void M_Options_Draw (void);
62                 void M_Keys_Draw (void);
63                 void M_Video_Draw (void);
64         void M_Help_Draw (void);
65         void M_Quit_Draw (void);
66 void M_LanConfig_Draw (void);
67 void M_GameOptions_Draw (void);
68 void M_Search_Draw (void);
69 void M_ServerList_Draw (void);
70
71 void M_Main_Key (int key);
72         void M_SinglePlayer_Key (int key);
73                 void M_Load_Key (int key);
74                 void M_Save_Key (int key);
75         void M_MultiPlayer_Key (int key);
76                 void M_Setup_Key (int key);
77                 void M_Net_Key (int key);
78         void M_Options_Key (int key);
79                 void M_Keys_Key (int key);
80                 void M_Video_Key (int key);
81         void M_Help_Key (int key);
82         void M_Quit_Key (int key);
83 void M_LanConfig_Key (int key);
84 void M_GameOptions_Key (int key);
85 void M_Search_Key (int key);
86 void M_ServerList_Key (int key);
87
88 qboolean        m_entersound;           // play after drawing a frame, so caching
89                                                                 // won't disrupt the sound
90
91 int                     m_return_state;
92 qboolean        m_return_onerror;
93 char            m_return_reason [32];
94
95 #define StartingGame    (m_multiplayer_cursor == 1)
96 #define JoiningGame             (m_multiplayer_cursor == 0)
97 #define IPXConfig               (m_net_cursor == 0)
98 #define TCPIPConfig             (m_net_cursor == 1)
99
100 void M_ConfigureNetSubsystem(void);
101
102 // Nehahra
103 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.4, 0.4, 0.4, 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_SetValueQuick (&scr_2dresolution, bound(0, scr_2dresolution.value + dir * 0.2, 1));
1274                 break;
1275         case 5:
1276                 Cvar_SetValueQuick (&scr_viewsize, bound(30, scr_viewsize.value + dir * 10, 120));
1277                 break;
1278         case 6:
1279                 Cvar_SetValueQuick (&r_skyquality, bound(0, r_skyquality.integer + dir, 2));
1280                 break;
1281         case 7:
1282                 Cvar_SetValueQuick (&r_ser, !r_ser.integer);
1283                 break;
1284         case 8:
1285                 Cvar_SetValueQuick (&v_overbrightbits, bound(0, v_overbrightbits.integer + dir, 4));
1286                 break;
1287         case 9:
1288                 Cvar_SetValueQuick (&gl_dither, !gl_dither.integer);
1289                 break;
1290         case 10:
1291                 Cvar_SetValueQuick (&v_hwgamma, !v_hwgamma.integer);
1292                 break;
1293         case 11:
1294                 Cvar_SetValueQuick (&v_gamma, bound(1, v_gamma.value + dir * 0.25, 5));
1295                 break;
1296         case 12:
1297                 Cvar_SetValueQuick (&v_contrast, bound(0.5, v_contrast.value + dir * 0.25, 5));
1298                 break;
1299         case 13:
1300                 Cvar_SetValueQuick (&v_brightness, bound(0, v_brightness.value + dir * 0.05, 0.8));
1301                 break;
1302         case 14: // music volume
1303                 #ifdef _WIN32
1304                 Cvar_SetValueQuick (&bgmvolume, bound(0, bgmvolume.value + dir * 1.0, 1));
1305                 #else
1306                 Cvar_SetValueQuick (&bgmvolume, bound(0, bgmvolume.value + dir * 0.1, 1));
1307                 #endif
1308                 break;
1309         case 15: // sfx volume
1310                 Cvar_SetValueQuick (&volume, bound(0, volume.value + dir * 0.1, 1));
1311                 break;
1312         case 16:
1313                 Cvar_SetValueQuick (&crosshair, bound(0, crosshair.integer + dir, 5));
1314                 break;
1315         case 17:
1316                 Cvar_SetValueQuick (&crosshair_size, bound(1, crosshair_size.value + dir, 5));
1317                 break;
1318         case 18: // show framerate
1319                 Cvar_SetValueQuick (&showfps, !showfps.integer);
1320                 break;
1321         case 19: // always run
1322                 if (cl_forwardspeed.value > 200)
1323                 {
1324                         Cvar_SetValueQuick (&cl_forwardspeed, 200);
1325                         Cvar_SetValueQuick (&cl_backspeed, 200);
1326                 }
1327                 else
1328                 {
1329                         Cvar_SetValueQuick (&cl_forwardspeed, 400);
1330                         Cvar_SetValueQuick (&cl_backspeed, 400);
1331                 }
1332                 break;
1333         case 20: // lookspring
1334                 Cvar_SetValueQuick (&lookspring, !lookspring.integer);
1335                 break;
1336         case 21: // lookstrafe
1337                 Cvar_SetValueQuick (&lookstrafe, !lookstrafe.integer);
1338                 break;
1339         case 22: // mouse speed
1340                 Cvar_SetValueQuick (&sensitivity, bound(1, sensitivity.value + dir * 0.5, 50));
1341                 break;
1342         case 23: // mouse look
1343                 Cvar_SetValueQuick (&freelook, !freelook.integer);
1344                 break;
1345         case 24: // invert mouse
1346                 Cvar_SetValueQuick (&m_pitch, -m_pitch.value);
1347                 break;
1348         case 25: // windowed mouse
1349                 Cvar_SetValueQuick (&vid_mouse, !vid_mouse.integer);
1350                 break;
1351         }
1352 }
1353
1354
1355 void M_DrawSlider (int x, int y, float range)
1356 {
1357         int     i;
1358
1359         if (range < 0)
1360                 range = 0;
1361         if (range > 1)
1362                 range = 1;
1363         M_DrawCharacter (x-8, y, 128);
1364         for (i=0 ; i<SLIDER_RANGE ; i++)
1365                 M_DrawCharacter (x + i*8, y, 129);
1366         M_DrawCharacter (x+i*8, y, 130);
1367         M_DrawCharacter (x + (SLIDER_RANGE-1)*8 * range, y, 131);
1368 }
1369
1370 void M_DrawCheckbox (int x, int y, int on)
1371 {
1372 #if 0
1373         if (on)
1374                 M_DrawCharacter (x, y, 131);
1375         else
1376                 M_DrawCharacter (x, y, 129);
1377 #endif
1378         if (on)
1379                 M_Print (x, y, "on");
1380         else
1381                 M_Print (x, y, "off");
1382 }
1383
1384 /*
1385 int m_2dres[] =
1386 {
1387         320, 200,
1388         320, 240,
1389         400, 300,
1390         512, 384,
1391         640, 480,
1392         800, 600,
1393         1024, 768,
1394         1280, 960,
1395         1600, 1200,
1396         2048, 1536
1397 };
1398
1399 int M_Num2DResolutions(void)
1400 {
1401         return sizeof(m_2dres) / sizeof(int[2]);
1402 };
1403
1404 float M_Classify2DResolution(void)
1405 {
1406         int i, num, *res, best, bestdist, diff[3];
1407         num = M_Num2DResolutions();
1408         best = -1;
1409         bestdist = 1000000000;
1410         for (i = 0;i < num;i++)
1411         {
1412                 res = m_2dres + i * 2;
1413                 diff[0] = res[0] - vid.conwidth;
1414                 diff[1] = res[1] - vid.conheight;
1415                 diff[2] = 0;
1416                 dist = DotProduct(diff, diff);
1417                 if (bestdist > dist)
1418                 {
1419                         bestdist = dist;
1420                         best = i;
1421                 }
1422         }
1423         return i;
1424 }
1425
1426 void M_Adjust2DResolution(int dir)
1427 {
1428         int i, num;
1429         i = M_Classify2DResolution() + dir;
1430         num = M_Num2DResolutions() - 1;
1431         i = bound(0, i, num);
1432         Cvar_SetValue("v_2dwidth", m_2dres[i*2]);
1433         Cvar_SetValue("v_2dheight", m_2dres[i*2+1]);
1434 }
1435 */
1436
1437 void M_Options_Draw (void)
1438 {
1439         float y;
1440         cachepic_t      *p;
1441
1442         M_DrawPic(16, 4, "gfx/qplaque.lmp");
1443         p = Draw_CachePic("gfx/p_option.lmp");
1444         M_DrawPic((320-p->width)/2, 4, "gfx/p_option.lmp");
1445
1446         y = 32;
1447         M_Print(16, y, "    Customize controls");y += 8;
1448         M_Print(16, y, "         Go to console");y += 8;
1449         M_Print(16, y, "     Reset to defaults");y += 8;
1450         M_ItemPrint(16, y, "         Video Options", vid_menudrawfn != NULL);y += 8;
1451         M_Print(16, y, "         2D Resolution");M_DrawSlider(220, y, scr_2dresolution.value);y += 8;
1452         M_Print(16, y, "           Screen size");M_DrawSlider(220, y, (scr_viewsize.value - 30) /(120 - 30));y += 8;
1453         M_Print(16, y, "           Sky Quality");M_DrawSlider(220, y, r_skyquality.value / 2);y += 8;
1454         M_Print(16, y, "Hidden Surface Removal");M_DrawCheckbox(220, y, r_ser.integer);y += 8;
1455         M_Print(16, y, "       Overbright Bits");M_DrawSlider(220, y, (v_overbrightbits.value) / 4);y += 8;
1456         M_Print(16, y, "             Dithering");M_DrawCheckbox(220, y, gl_dither.integer);y += 8;
1457         M_ItemPrint(16, y, "Hardware Gamma Control", hardwaregammasupported);M_DrawCheckbox(220, y, v_hwgamma.integer);y += 8;
1458         M_ItemPrint(16, y, "                 Gamma", v_hwgamma.integer);M_DrawSlider(220, y, (v_gamma.value - 1) / 4);y += 8;
1459         M_Print(16, y, "              Contrast");M_DrawSlider(220, y, (v_contrast.value - 0.5) / (5 - 0.5));y += 8;
1460         M_Print(16, y, "            Brightness");M_DrawSlider(220, y, v_brightness.value / 0.8);y += 8;
1461         M_ItemPrint(16, y, "       CD Music Volume", cdaudioinitialized);M_DrawSlider(220, y, bgmvolume.value);y += 8;
1462         M_ItemPrint(16, y, "          Sound Volume", snd_initialized);M_DrawSlider(220, y, volume.value);y += 8;
1463         M_Print(16, y, "             Crosshair");M_DrawSlider(220, y, crosshair.value / 5);y += 8;
1464         M_Print(16, y, "        Crosshair Size");M_DrawSlider(220, y, (crosshair_size.value - 1) / 4);y += 8;
1465         M_Print(16, y, "        Show Framerate");M_DrawCheckbox(220, y, showfps.integer);y += 8;
1466         M_Print(16, y, "            Always Run");M_DrawCheckbox(220, y, cl_forwardspeed.value > 200);y += 8;
1467         M_Print(16, y, "            Lookspring");M_DrawCheckbox(220, y, lookspring.integer);y += 8;
1468         M_Print(16, y, "            Lookstrafe");M_DrawCheckbox(220, y, lookstrafe.integer);y += 8;
1469         M_Print(16, y, "           Mouse Speed");M_DrawSlider(220, y, (sensitivity.value - 1)/50);y += 8;
1470         M_Print(16, y, "            Mouse Look");M_DrawCheckbox(220, y, freelook.integer);y += 8;
1471         M_Print(16, y, "          Invert Mouse");M_DrawCheckbox(220, y, m_pitch.value < 0);y += 8;
1472         M_Print(16, y, "             Use Mouse");M_DrawCheckbox(220, y, vid_mouse.integer);y += 8;
1473
1474         // cursor
1475         M_DrawCharacter(200, 32 + options_cursor*8, 12+((int)(realtime*4)&1));
1476 }
1477
1478
1479 void M_Options_Key (int k)
1480 {
1481         switch (k)
1482         {
1483         case K_ESCAPE:
1484                 M_Menu_Main_f ();
1485                 break;
1486
1487         case K_ENTER:
1488                 m_entersound = true;
1489                 switch (options_cursor)
1490                 {
1491                 case 0:
1492                         M_Menu_Keys_f ();
1493                         break;
1494                 case 1:
1495                         m_state = m_none;
1496                         Con_ToggleConsole_f ();
1497                         break;
1498                 case 2:
1499                         Cbuf_AddText ("exec default.cfg\n");
1500                         break;
1501                 case 3:
1502                         if (vid_menudrawfn)
1503                                 M_Menu_Video_f ();
1504                         break;
1505                 default:
1506                         M_AdjustSliders (1);
1507                         break;
1508                 }
1509                 return;
1510
1511         case K_UPARROW:
1512                 S_LocalSound ("misc/menu1.wav");
1513                 options_cursor--;
1514                 if (options_cursor < 0)
1515                         options_cursor = OPTIONS_ITEMS-1;
1516                 break;
1517
1518         case K_DOWNARROW:
1519                 S_LocalSound ("misc/menu1.wav");
1520                 options_cursor++;
1521                 if (options_cursor >= OPTIONS_ITEMS)
1522                         options_cursor = 0;
1523                 break;
1524
1525         case K_LEFTARROW:
1526                 M_AdjustSliders (-1);
1527                 break;
1528
1529         case K_RIGHTARROW:
1530                 M_AdjustSliders (1);
1531                 break;
1532         }
1533 }
1534
1535 //=============================================================================
1536 /* KEYS MENU */
1537
1538 char *bindnames[][2] =
1539 {
1540 {"+attack",             "attack"},
1541 {"impulse 10",          "change weapon"},
1542 {"+jump",                       "jump / swim up"},
1543 {"+forward",            "walk forward"},
1544 {"+back",                       "backpedal"},
1545 {"+left",                       "turn left"},
1546 {"+right",                      "turn right"},
1547 {"+speed",                      "run"},
1548 {"+moveleft",           "step left"},
1549 {"+moveright",          "step right"},
1550 {"+strafe",             "sidestep"},
1551 {"+lookup",             "look up"},
1552 {"+lookdown",           "look down"},
1553 {"centerview",          "center view"},
1554 {"+mlook",                      "mouse look"},
1555 {"+klook",                      "keyboard look"},
1556 {"+moveup",                     "swim up"},
1557 {"+movedown",           "swim down"}
1558 };
1559
1560 #define NUMCOMMANDS     (sizeof(bindnames)/sizeof(bindnames[0]))
1561
1562 int             keys_cursor;
1563 int             bind_grab;
1564
1565 void M_Menu_Keys_f (void)
1566 {
1567         key_dest = key_menu;
1568         m_state = m_keys;
1569         m_entersound = true;
1570 }
1571
1572
1573 void M_FindKeysForCommand (char *command, int *twokeys)
1574 {
1575         int             count;
1576         int             j;
1577         char    *b;
1578
1579         twokeys[0] = twokeys[1] = -1;
1580         count = 0;
1581
1582         for (j=0 ; j<256 ; j++)
1583         {
1584                 b = keybindings[j];
1585                 if (!b)
1586                         continue;
1587                 if (!strcmp (b, command) )
1588                 {
1589                         twokeys[count] = j;
1590                         count++;
1591                         if (count == 2)
1592                                 break;
1593                 }
1594         }
1595 }
1596
1597 void M_UnbindCommand (char *command)
1598 {
1599         int             j;
1600         char    *b;
1601
1602         for (j=0 ; j<256 ; j++)
1603         {
1604                 b = keybindings[j];
1605                 if (!b)
1606                         continue;
1607                 if (!strcmp (b, command))
1608                         Key_SetBinding (j, "");
1609         }
1610 }
1611
1612
1613 void M_Keys_Draw (void)
1614 {
1615         int             i, l;
1616         int             keys[2];
1617         char    *name;
1618         int             x, y;
1619         cachepic_t      *p;
1620
1621         p = Draw_CachePic ("gfx/ttl_cstm.lmp");
1622         M_DrawPic ( (320-p->width)/2, 4, "gfx/ttl_cstm.lmp");
1623
1624         if (bind_grab)
1625                 M_Print (12, 32, "Press a key or button for this action");
1626         else
1627                 M_Print (18, 32, "Enter to change, backspace to clear");
1628
1629 // search for known bindings
1630         for (i=0 ; i<NUMCOMMANDS ; i++)
1631         {
1632                 y = 48 + 8*i;
1633
1634                 M_Print (16, y, bindnames[i][1]);
1635
1636                 l = strlen (bindnames[i][0]);
1637
1638                 M_FindKeysForCommand (bindnames[i][0], keys);
1639
1640                 if (keys[0] == -1)
1641                 {
1642                         M_Print (140, y, "???");
1643                 }
1644                 else
1645                 {
1646                         name = Key_KeynumToString (keys[0]);
1647                         M_Print (140, y, name);
1648                         x = strlen(name) * 8;
1649                         if (keys[1] != -1)
1650                         {
1651                                 M_Print (140 + x + 8, y, "or");
1652                                 M_Print (140 + x + 32, y, Key_KeynumToString (keys[1]));
1653                         }
1654                 }
1655         }
1656
1657         if (bind_grab)
1658                 M_DrawCharacter (130, 48 + keys_cursor*8, '=');
1659         else
1660                 M_DrawCharacter (130, 48 + keys_cursor*8, 12+((int)(realtime*4)&1));
1661 }
1662
1663
1664 void M_Keys_Key (int k)
1665 {
1666         char    cmd[80];
1667         int             keys[2];
1668
1669         if (bind_grab)
1670         {       // defining a key
1671                 S_LocalSound ("misc/menu1.wav");
1672                 if (k == K_ESCAPE)
1673                 {
1674                         bind_grab = false;
1675                 }
1676                 else if (k != '`')
1677                 {
1678                         sprintf (cmd, "bind \"%s\" \"%s\"\n", Key_KeynumToString (k), bindnames[keys_cursor][0]);
1679                         Cbuf_InsertText (cmd);
1680                 }
1681
1682                 bind_grab = false;
1683                 return;
1684         }
1685
1686         switch (k)
1687         {
1688         case K_ESCAPE:
1689                 M_Menu_Options_f ();
1690                 break;
1691
1692         case K_LEFTARROW:
1693         case K_UPARROW:
1694                 S_LocalSound ("misc/menu1.wav");
1695                 keys_cursor--;
1696                 if (keys_cursor < 0)
1697                         keys_cursor = NUMCOMMANDS-1;
1698                 break;
1699
1700         case K_DOWNARROW:
1701         case K_RIGHTARROW:
1702                 S_LocalSound ("misc/menu1.wav");
1703                 keys_cursor++;
1704                 if (keys_cursor >= NUMCOMMANDS)
1705                         keys_cursor = 0;
1706                 break;
1707
1708         case K_ENTER:           // go into bind mode
1709                 M_FindKeysForCommand (bindnames[keys_cursor][0], keys);
1710                 S_LocalSound ("misc/menu2.wav");
1711                 if (keys[1] != -1)
1712                         M_UnbindCommand (bindnames[keys_cursor][0]);
1713                 bind_grab = true;
1714                 break;
1715
1716         case K_BACKSPACE:               // delete bindings
1717         case K_DEL:                             // delete bindings
1718                 S_LocalSound ("misc/menu2.wav");
1719                 M_UnbindCommand (bindnames[keys_cursor][0]);
1720                 break;
1721         }
1722 }
1723
1724 //=============================================================================
1725 /* VIDEO MENU */
1726
1727 void M_Menu_Video_f (void)
1728 {
1729         key_dest = key_menu;
1730         m_state = m_video;
1731         m_entersound = true;
1732 }
1733
1734
1735 void M_Video_Draw (void)
1736 {
1737         (*vid_menudrawfn) ();
1738 }
1739
1740
1741 void M_Video_Key (int key)
1742 {
1743         (*vid_menukeyfn) (key);
1744 }
1745
1746 //=============================================================================
1747 /* HELP MENU */
1748
1749 int             help_page;
1750 #define NUM_HELP_PAGES  6
1751
1752
1753 void M_Menu_Help_f (void)
1754 {
1755         key_dest = key_menu;
1756         m_state = m_help;
1757         m_entersound = true;
1758         help_page = 0;
1759 }
1760
1761
1762
1763 void M_Help_Draw (void)
1764 {
1765         M_DrawPic (0, 0, va("gfx/help%i.lmp", help_page));
1766 }
1767
1768
1769 void M_Help_Key (int key)
1770 {
1771         switch (key)
1772         {
1773         case K_ESCAPE:
1774                 M_Menu_Main_f ();
1775                 break;
1776
1777         case K_UPARROW:
1778         case K_RIGHTARROW:
1779                 m_entersound = true;
1780                 if (++help_page >= NUM_HELP_PAGES)
1781                         help_page = 0;
1782                 break;
1783
1784         case K_DOWNARROW:
1785         case K_LEFTARROW:
1786                 m_entersound = true;
1787                 if (--help_page < 0)
1788                         help_page = NUM_HELP_PAGES-1;
1789                 break;
1790         }
1791
1792 }
1793
1794 //=============================================================================
1795 /* QUIT MENU */
1796
1797 int             msgNumber;
1798 int             m_quit_prevstate;
1799 qboolean        wasInMenus;
1800
1801 //#ifndef       _WIN32
1802 char *quitMessage [] = 
1803 {
1804 /* .........1.........2.... */
1805 /*
1806   "  Are you gonna quit    ",
1807   "  this game just like   ",
1808   "   everything else?     ",
1809   "                        ",
1810
1811   " Milord, methinks that  ",
1812   "   thou art a lowly     ",
1813   " quitter. Is this true? ",
1814   "                        ",
1815
1816   " Do I need to bust your ",
1817   "  face open for trying  ",
1818   "        to quit?        ",
1819   "                        ",
1820
1821   " Man, I oughta smack you",
1822   "   for trying to quit!  ",
1823   "     Press Y to get     ",
1824   "      smacked out.      ",
1825  
1826   " Press Y to quit like a ",
1827   "   big loser in life.   ",
1828   "  Press N to stay proud ",
1829   "    and successful!     ",
1830  
1831   "   If you press Y to    ",
1832   "  quit, I will summon   ",
1833   "  Satan all over your   ",
1834   "      hard drive!       ",
1835  
1836   "  Um, Asmodeus dislikes ",
1837   " his children trying to ",
1838   " quit. Press Y to return",
1839   "   to your Tinkertoys.  ",
1840  
1841   "  If you quit now, I'll ",
1842   "  throw a blanket-party ",
1843   "   for you next time!   ",
1844   "                        "
1845   */
1846
1847 /* .........1.........2.... */
1848   "                        ",
1849   "    Tired of fragging   ",
1850   "        already?        ",
1851   "                        ",
1852
1853   "                        ",
1854   "  Quit now and forfeit  ",
1855   "     your bodycount?    ",
1856   "                        ",
1857
1858   "                        ",
1859   "    Are you sure you    ",
1860   "      want to quit?     ",
1861   "                        ",
1862
1863   "                        ",
1864   "   Off to do something  ",
1865   "      constructive?     ",
1866   "                        ",
1867 };
1868 //#endif
1869
1870 void M_Menu_Quit_f (void)
1871 {
1872         if (m_state == m_quit)
1873                 return;
1874         wasInMenus = (key_dest == key_menu);
1875         key_dest = key_menu;
1876         m_quit_prevstate = m_state;
1877         m_state = m_quit;
1878         m_entersound = true;
1879         msgNumber = rand()&3; //&7;
1880 }
1881
1882
1883 void M_Quit_Key (int key)
1884 {
1885         switch (key)
1886         {
1887         case K_ESCAPE:
1888         case 'n':
1889         case 'N':
1890                 if (wasInMenus)
1891                 {
1892                         m_state = m_quit_prevstate;
1893                         m_entersound = true;
1894                 }
1895                 else
1896                 {
1897                         key_dest = key_game;
1898                         m_state = m_none;
1899                 }
1900                 break;
1901
1902         case 'Y':
1903         case 'y':
1904                 key_dest = key_console;
1905                 Host_Quit_f ();
1906                 break;
1907
1908         default:
1909                 break;
1910         }
1911
1912 }
1913
1914
1915 void M_Quit_Draw (void)
1916 {
1917 /*
1918 #ifdef _WIN32
1919         M_DrawTextBox (0, 0, 38, 23);
1920         M_PrintWhite (16, 12,  "  Quake version 1.09 by id Software\n\n");
1921         M_PrintWhite (16, 28,  "Programming        Art \n");
1922         M_Print (16, 36,  " John Carmack       Adrian Carmack\n");
1923         M_Print (16, 44,  " Michael Abrash     Kevin Cloud\n");
1924         M_Print (16, 52,  " John Cash          Paul Steed\n");
1925         M_Print (16, 60,  " Dave 'Zoid' Kirsch\n");
1926         M_PrintWhite (16, 68,  "Design             Biz\n");
1927         M_Print (16, 76,  " John Romero        Jay Wilbur\n");
1928         M_Print (16, 84,  " Sandy Petersen     Mike Wilson\n");
1929         M_Print (16, 92,  " American McGee     Donna Jackson\n");
1930         M_Print (16, 100,  " Tim Willits        Todd Hollenshead\n");
1931         M_PrintWhite (16, 108, "Support            Projects\n");
1932         M_Print (16, 116, " Barrett Alexander  Shawn Green\n");
1933         M_PrintWhite (16, 124, "Sound Effects\n");
1934         M_Print (16, 132, " Trent Reznor and Nine Inch Nails\n\n");
1935         M_PrintWhite (16, 140, "Quake is a trademark of Id Software,\n");
1936         M_PrintWhite (16, 148, "inc., (c)1996 Id Software, inc. All\n");
1937         M_PrintWhite (16, 156, "rights reserved. NIN logo is a\n");
1938         M_PrintWhite (16, 164, "registered trademark licensed to\n");
1939         M_PrintWhite (16, 172, "Nothing Interactive, Inc. All rights\n");
1940         M_PrintWhite (16, 180, "reserved. Press y to exit\n");
1941 #else
1942 */
1943         M_DrawTextBox (56, 76, 24, 4);
1944         M_Print (64, 84,  quitMessage[msgNumber*4+0]);
1945         M_Print (64, 92,  quitMessage[msgNumber*4+1]);
1946         M_Print (64, 100, quitMessage[msgNumber*4+2]);
1947         M_Print (64, 108, quitMessage[msgNumber*4+3]);
1948 //#endif
1949 }
1950
1951 //=============================================================================
1952 /* LAN CONFIG MENU */
1953
1954 int             lanConfig_cursor = -1;
1955 int             lanConfig_cursor_table [] = {72, 92, 124};
1956 #define NUM_LANCONFIG_CMDS      3
1957
1958 int     lanConfig_port;
1959 char    lanConfig_portname[6];
1960 char    lanConfig_joinname[22];
1961
1962 void M_Menu_LanConfig_f (void)
1963 {
1964         key_dest = key_menu;
1965         m_state = m_lanconfig;
1966         m_entersound = true;
1967         if (lanConfig_cursor == -1)
1968         {
1969                 if (JoiningGame && TCPIPConfig)
1970                         lanConfig_cursor = 2;
1971                 else
1972                         lanConfig_cursor = 1;
1973         }
1974         if (StartingGame && lanConfig_cursor == 2)
1975                 lanConfig_cursor = 1;
1976         lanConfig_port = DEFAULTnet_hostport;
1977         sprintf(lanConfig_portname, "%u", lanConfig_port);
1978
1979         m_return_onerror = false;
1980         m_return_reason[0] = 0;
1981 }
1982
1983
1984 void M_LanConfig_Draw (void)
1985 {
1986         cachepic_t      *p;
1987         int             basex;
1988         char    *startJoin;
1989         char    *protocol;
1990
1991         M_DrawPic (16, 4, "gfx/qplaque.lmp");
1992         p = Draw_CachePic ("gfx/p_multi.lmp");
1993         basex = (320-p->width)/2;
1994         M_DrawPic (basex, 4, "gfx/p_multi.lmp");
1995
1996         if (StartingGame)
1997                 startJoin = "New Game";
1998         else
1999                 startJoin = "Join Game";
2000         if (IPXConfig)
2001                 protocol = "IPX";
2002         else
2003                 protocol = "TCP/IP";
2004         M_Print (basex, 32, va ("%s - %s", startJoin, protocol));
2005         basex += 8;
2006
2007         M_Print (basex, 52, "Address:");
2008         if (IPXConfig)
2009                 M_Print (basex+9*8, 52, my_ipx_address);
2010         else
2011                 M_Print (basex+9*8, 52, my_tcpip_address);
2012
2013         M_Print (basex, lanConfig_cursor_table[0], "Port");
2014         M_DrawTextBox (basex+8*8, lanConfig_cursor_table[0]-8, 6, 1);
2015         M_Print (basex+9*8, lanConfig_cursor_table[0], lanConfig_portname);
2016
2017         if (JoiningGame)
2018         {
2019                 M_Print (basex, lanConfig_cursor_table[1], "Search for local games...");
2020                 M_Print (basex, 108, "Join game at:");
2021                 M_DrawTextBox (basex+8, lanConfig_cursor_table[2]-8, 22, 1);
2022                 M_Print (basex+16, lanConfig_cursor_table[2], lanConfig_joinname);
2023         }
2024         else
2025         {
2026                 M_DrawTextBox (basex, lanConfig_cursor_table[1]-8, 2, 1);
2027                 M_Print (basex+8, lanConfig_cursor_table[1], "OK");
2028         }
2029
2030         M_DrawCharacter (basex-8, lanConfig_cursor_table [lanConfig_cursor], 12+((int)(realtime*4)&1));
2031
2032         if (lanConfig_cursor == 0)
2033                 M_DrawCharacter (basex+9*8 + 8*strlen(lanConfig_portname), lanConfig_cursor_table [0], 10+((int)(realtime*4)&1));
2034
2035         if (lanConfig_cursor == 2)
2036                 M_DrawCharacter (basex+16 + 8*strlen(lanConfig_joinname), lanConfig_cursor_table [2], 10+((int)(realtime*4)&1));
2037
2038         if (*m_return_reason)
2039                 M_PrintWhite (basex, 148, m_return_reason);
2040 }
2041
2042
2043 void M_LanConfig_Key (int key)
2044 {
2045         int             l;
2046
2047         switch (key)
2048         {
2049         case K_ESCAPE:
2050                 M_Menu_Net_f ();
2051                 break;
2052
2053         case K_UPARROW:
2054                 S_LocalSound ("misc/menu1.wav");
2055                 lanConfig_cursor--;
2056                 if (lanConfig_cursor < 0)
2057                         lanConfig_cursor = NUM_LANCONFIG_CMDS-1;
2058                 break;
2059
2060         case K_DOWNARROW:
2061                 S_LocalSound ("misc/menu1.wav");
2062                 lanConfig_cursor++;
2063                 if (lanConfig_cursor >= NUM_LANCONFIG_CMDS)
2064                         lanConfig_cursor = 0;
2065                 break;
2066
2067         case K_ENTER:
2068                 if (lanConfig_cursor == 0)
2069                         break;
2070
2071                 m_entersound = true;
2072
2073                 M_ConfigureNetSubsystem ();
2074
2075                 if (lanConfig_cursor == 1)
2076                 {
2077                         if (StartingGame)
2078                         {
2079                                 M_Menu_GameOptions_f ();
2080                                 break;
2081                         }
2082                         M_Menu_Search_f();
2083                         break;
2084                 }
2085
2086                 if (lanConfig_cursor == 2)
2087                 {
2088                         m_return_state = m_state;
2089                         m_return_onerror = true;
2090                         key_dest = key_game;
2091                         m_state = m_none;
2092                         Cbuf_AddText ( va ("connect \"%s\"\n", lanConfig_joinname) );
2093                         break;
2094                 }
2095
2096                 break;
2097
2098         case K_BACKSPACE:
2099                 if (lanConfig_cursor == 0)
2100                 {
2101                         if (strlen(lanConfig_portname))
2102                                 lanConfig_portname[strlen(lanConfig_portname)-1] = 0;
2103                 }
2104
2105                 if (lanConfig_cursor == 2)
2106                 {
2107                         if (strlen(lanConfig_joinname))
2108                                 lanConfig_joinname[strlen(lanConfig_joinname)-1] = 0;
2109                 }
2110                 break;
2111
2112         default:
2113                 if (key < 32 || key > 127)
2114                         break;
2115
2116                 if (lanConfig_cursor == 2)
2117                 {
2118                         l = strlen(lanConfig_joinname);
2119                         if (l < 21)
2120                         {
2121                                 lanConfig_joinname[l+1] = 0;
2122                                 lanConfig_joinname[l] = key;
2123                         }
2124                 }
2125
2126                 if (key < '0' || key > '9')
2127                         break;
2128                 if (lanConfig_cursor == 0)
2129                 {
2130                         l = strlen(lanConfig_portname);
2131                         if (l < 5)
2132                         {
2133                                 lanConfig_portname[l+1] = 0;
2134                                 lanConfig_portname[l] = key;
2135                         }
2136                 }
2137         }
2138
2139         if (StartingGame && lanConfig_cursor == 2)
2140         {
2141                 if (key == K_UPARROW)
2142                         lanConfig_cursor = 1;
2143                 else
2144                         lanConfig_cursor = 0;
2145         }
2146
2147         l =  atoi(lanConfig_portname);
2148         if (l > 65535)
2149                 l = lanConfig_port;
2150         else
2151                 lanConfig_port = l;
2152         sprintf(lanConfig_portname, "%u", lanConfig_port);
2153 }
2154
2155 //=============================================================================
2156 /* GAME OPTIONS MENU */
2157
2158 typedef struct
2159 {
2160         char    *name;
2161         char    *description;
2162 } level_t;
2163
2164 level_t         levels[] =
2165 {
2166         {"start", "Entrance"},  // 0
2167
2168         {"e1m1", "Slipgate Complex"},                           // 1
2169         {"e1m2", "Castle of the Damned"},
2170         {"e1m3", "The Necropolis"},
2171         {"e1m4", "The Grisly Grotto"},
2172         {"e1m5", "Gloom Keep"},
2173         {"e1m6", "The Door To Chthon"},
2174         {"e1m7", "The House of Chthon"},
2175         {"e1m8", "Ziggurat Vertigo"},
2176
2177         {"e2m1", "The Installation"},                           // 9
2178         {"e2m2", "Ogre Citadel"},
2179         {"e2m3", "Crypt of Decay"},
2180         {"e2m4", "The Ebon Fortress"},
2181         {"e2m5", "The Wizard's Manse"},
2182         {"e2m6", "The Dismal Oubliette"},
2183         {"e2m7", "Underearth"},
2184
2185         {"e3m1", "Termination Central"},                        // 16
2186         {"e3m2", "The Vaults of Zin"},
2187         {"e3m3", "The Tomb of Terror"},
2188         {"e3m4", "Satan's Dark Delight"},
2189         {"e3m5", "Wind Tunnels"},
2190         {"e3m6", "Chambers of Torment"},
2191         {"e3m7", "The Haunted Halls"},
2192
2193         {"e4m1", "The Sewage System"},                          // 23
2194         {"e4m2", "The Tower of Despair"},
2195         {"e4m3", "The Elder God Shrine"},
2196         {"e4m4", "The Palace of Hate"},
2197         {"e4m5", "Hell's Atrium"},
2198         {"e4m6", "The Pain Maze"},
2199         {"e4m7", "Azure Agony"},
2200         {"e4m8", "The Nameless City"},
2201
2202         {"end", "Shub-Niggurath's Pit"},                        // 31
2203
2204         {"dm1", "Place of Two Deaths"},                         // 32
2205         {"dm2", "Claustrophobopolis"},
2206         {"dm3", "The Abandoned Base"},
2207         {"dm4", "The Bad Place"},
2208         {"dm5", "The Cistern"},
2209         {"dm6", "The Dark Zone"}
2210 };
2211
2212 //MED 01/06/97 added hipnotic levels
2213 level_t     hipnoticlevels[] =
2214 {
2215    {"start", "Command HQ"},  // 0
2216
2217    {"hip1m1", "The Pumping Station"},          // 1
2218    {"hip1m2", "Storage Facility"},
2219    {"hip1m3", "The Lost Mine"},
2220    {"hip1m4", "Research Facility"},
2221    {"hip1m5", "Military Complex"},
2222
2223    {"hip2m1", "Ancient Realms"},          // 6
2224    {"hip2m2", "The Black Cathedral"},
2225    {"hip2m3", "The Catacombs"},
2226    {"hip2m4", "The Crypt"},
2227    {"hip2m5", "Mortum's Keep"},
2228    {"hip2m6", "The Gremlin's Domain"},
2229
2230    {"hip3m1", "Tur Torment"},       // 12
2231    {"hip3m2", "Pandemonium"},
2232    {"hip3m3", "Limbo"},
2233    {"hip3m4", "The Gauntlet"},
2234
2235    {"hipend", "Armagon's Lair"},       // 16
2236
2237    {"hipdm1", "The Edge of Oblivion"}           // 17
2238 };
2239
2240 //PGM 01/07/97 added rogue levels
2241 //PGM 03/02/97 added dmatch level
2242 level_t         roguelevels[] =
2243 {
2244         {"start",       "Split Decision"},
2245         {"r1m1",        "Deviant's Domain"},
2246         {"r1m2",        "Dread Portal"},
2247         {"r1m3",        "Judgement Call"},
2248         {"r1m4",        "Cave of Death"},
2249         {"r1m5",        "Towers of Wrath"},
2250         {"r1m6",        "Temple of Pain"},
2251         {"r1m7",        "Tomb of the Overlord"},
2252         {"r2m1",        "Tempus Fugit"},
2253         {"r2m2",        "Elemental Fury I"},
2254         {"r2m3",        "Elemental Fury II"},
2255         {"r2m4",        "Curse of Osiris"},
2256         {"r2m5",        "Wizard's Keep"},
2257         {"r2m6",        "Blood Sacrifice"},
2258         {"r2m7",        "Last Bastion"},
2259         {"r2m8",        "Source of Evil"},
2260         {"ctf1",    "Division of Change"}
2261 };
2262
2263 typedef struct
2264 {
2265         char    *description;
2266         int             firstLevel;
2267         int             levels;
2268 } episode_t;
2269
2270 episode_t       episodes[] =
2271 {
2272         {"Welcome to Quake", 0, 1},
2273         {"Doomed Dimension", 1, 8},
2274         {"Realm of Black Magic", 9, 7},
2275         {"Netherworld", 16, 7},
2276         {"The Elder World", 23, 8},
2277         {"Final Level", 31, 1},
2278         {"Deathmatch Arena", 32, 6}
2279 };
2280
2281 //MED 01/06/97  added hipnotic episodes
2282 episode_t   hipnoticepisodes[] =
2283 {
2284    {"Scourge of Armagon", 0, 1},
2285    {"Fortress of the Dead", 1, 5},
2286    {"Dominion of Darkness", 6, 6},
2287    {"The Rift", 12, 4},
2288    {"Final Level", 16, 1},
2289    {"Deathmatch Arena", 17, 1}
2290 };
2291
2292 //PGM 01/07/97 added rogue episodes
2293 //PGM 03/02/97 added dmatch episode
2294 episode_t       rogueepisodes[] =
2295 {
2296         {"Introduction", 0, 1},
2297         {"Hell's Fortress", 1, 7},
2298         {"Corridors of Time", 8, 8},
2299         {"Deathmatch Arena", 16, 1}
2300 };
2301
2302 level_t         nehahralevels[] =
2303 {
2304         {"nehstart",    "Welcome to Nehahra"},
2305         {"neh1m1",      "Forge City1: Slipgates"},
2306         {"neh1m2",      "Forge City2: Boiler"},
2307         {"neh1m3",      "Forge City3: Escape"},
2308         {"neh1m4",      "Grind Core"},
2309         {"neh1m5",      "Industrial Silence"},
2310         {"neh1m6",      "Locked-Up Anger"},
2311         {"neh1m7",      "Wanderer of the Wastes"},
2312         {"neh1m8",      "Artemis System Net"},
2313         {"neh1m9",      "To the Death"},
2314         {"neh2m1",      "The Gates of Ghoro"},
2315         {"neh2m2",      "Sacred Trinity"},
2316         {"neh2m3",      "Realm of the Ancients"},
2317         {"neh2m4",      "Temple of the Ancients"},
2318         {"neh2m5",      "Dreams Made Flesh"},
2319         {"neh2m6",      "Your Last Cup of Sorrow"},
2320         {"nehsec",      "Ogre's Bane"},
2321         {"nehahra",     "Nehahra's Den"},
2322         {"nehend",      "Quintessence"}
2323 };
2324
2325 episode_t       nehahraepisodes[] =
2326 {
2327         {"Welcome to Nehahra", 0, 1},
2328         {"The Fall of Forge", 1, 9},
2329         {"The Outlands", 10, 7},
2330         {"Dimension of the Lost", 17, 2}
2331 };
2332
2333 int     startepisode;
2334 int     startlevel;
2335 int maxplayers;
2336 qboolean m_serverInfoMessage = false;
2337 double m_serverInfoMessageTime;
2338
2339 void M_Menu_GameOptions_f (void)
2340 {
2341         key_dest = key_menu;
2342         m_state = m_gameoptions;
2343         m_entersound = true;
2344         if (maxplayers == 0)
2345                 maxplayers = svs.maxclients;
2346         if (maxplayers < 2)
2347                 maxplayers = svs.maxclientslimit;
2348 }
2349
2350
2351 int gameoptions_cursor_table[] = {40, 56, 64, 72, 80, 88, 96, 112, 120};
2352 #define NUM_GAMEOPTIONS 9
2353 int             gameoptions_cursor;
2354
2355 void M_GameOptions_Draw (void)
2356 {
2357         cachepic_t      *p;
2358         int             x;
2359
2360         M_DrawPic (16, 4, "gfx/qplaque.lmp");
2361         p = Draw_CachePic ("gfx/p_multi.lmp");
2362         M_DrawPic ( (320-p->width)/2, 4, "gfx/p_multi.lmp");
2363
2364         M_DrawTextBox (152, 32, 10, 1);
2365         M_Print (160, 40, "begin game");
2366
2367         M_Print (0, 56, "      Max players");
2368         M_Print (160, 56, va("%i", maxplayers) );
2369
2370         M_Print (0, 64, "        Game Type");
2371         if (!coop.integer && !deathmatch.integer)
2372                 Cvar_SetValue("deathmatch", 1);
2373         if (coop.integer)
2374                 M_Print (160, 64, "Cooperative");
2375         else
2376                 M_Print (160, 64, "Deathmatch");
2377
2378         M_Print (0, 72, "        Teamplay");
2379         if (gamemode == GAME_ROGUE)
2380         {
2381                 char *msg;
2382
2383                 switch((int)teamplay.integer)
2384                 {
2385                         case 1: msg = "No Friendly Fire"; break;
2386                         case 2: msg = "Friendly Fire"; break;
2387                         case 3: msg = "Tag"; break;
2388                         case 4: msg = "Capture the Flag"; break;
2389                         case 5: msg = "One Flag CTF"; break;
2390                         case 6: msg = "Three Team CTF"; break;
2391                         default: msg = "Off"; break;
2392                 }
2393                 M_Print (160, 72, msg);
2394         }
2395         else
2396         {
2397                 char *msg;
2398
2399                 switch((int)teamplay.integer)
2400                 {
2401                         case 1: msg = "No Friendly Fire"; break;
2402                         case 2: msg = "Friendly Fire"; break;
2403                         default: msg = "Off"; break;
2404                 }
2405                 M_Print (160, 72, msg);
2406         }
2407
2408         M_Print (0, 80, "            Skill");
2409         if (skill.integer == 0)
2410                 M_Print (160, 80, "Easy difficulty");
2411         else if (skill.integer == 1)
2412                 M_Print (160, 80, "Normal difficulty");
2413         else if (skill.integer == 2)
2414                 M_Print (160, 80, "Hard difficulty");
2415         else
2416                 M_Print (160, 80, "Nightmare difficulty");
2417
2418         M_Print (0, 88, "       Frag Limit");
2419         if (fraglimit.integer == 0)
2420                 M_Print (160, 88, "none");
2421         else
2422                 M_Print (160, 88, va("%i frags", fraglimit.integer));
2423
2424         M_Print (0, 96, "       Time Limit");
2425         if (timelimit.integer == 0)
2426                 M_Print (160, 96, "none");
2427         else
2428                 M_Print (160, 96, va("%i minutes", timelimit.integer));
2429
2430         M_Print (0, 112, "         Episode");
2431         //MED 01/06/97 added hipnotic episodes
2432         if (gamemode == GAME_HIPNOTIC)
2433                 M_Print (160, 112, hipnoticepisodes[startepisode].description);
2434         //PGM 01/07/97 added rogue episodes
2435         else if (gamemode == GAME_ROGUE)
2436                 M_Print (160, 112, rogueepisodes[startepisode].description);
2437         else if (gamemode == GAME_NEHAHRA)
2438                 M_Print (160, 112, nehahraepisodes[startepisode].description);
2439         else
2440                 M_Print (160, 112, episodes[startepisode].description);
2441
2442         M_Print (0, 120, "           Level");
2443         //MED 01/06/97 added hipnotic episodes
2444         if (gamemode == GAME_HIPNOTIC)
2445         {
2446                 M_Print (160, 120, hipnoticlevels[hipnoticepisodes[startepisode].firstLevel + startlevel].description);
2447                 M_Print (160, 128, hipnoticlevels[hipnoticepisodes[startepisode].firstLevel + startlevel].name);
2448         }
2449         //PGM 01/07/97 added rogue episodes
2450         else if (gamemode == GAME_ROGUE)
2451         {
2452                 M_Print (160, 120, roguelevels[rogueepisodes[startepisode].firstLevel + startlevel].description);
2453                 M_Print (160, 128, roguelevels[rogueepisodes[startepisode].firstLevel + startlevel].name);
2454         }
2455         else if (gamemode == GAME_NEHAHRA)
2456         {
2457                 M_Print (160, 120, nehahralevels[nehahraepisodes[startepisode].firstLevel + startlevel].description);
2458                 M_Print (160, 128, nehahralevels[nehahraepisodes[startepisode].firstLevel + startlevel].name);
2459         }
2460         else
2461         {
2462                 M_Print (160, 120, levels[episodes[startepisode].firstLevel + startlevel].description);
2463                 M_Print (160, 128, levels[episodes[startepisode].firstLevel + startlevel].name);
2464         }
2465
2466 // line cursor
2467         M_DrawCharacter (144, gameoptions_cursor_table[gameoptions_cursor], 12+((int)(realtime*4)&1));
2468
2469         if (m_serverInfoMessage)
2470         {
2471                 if ((realtime - m_serverInfoMessageTime) < 5.0)
2472                 {
2473                         x = (320-26*8)/2;
2474                         M_DrawTextBox (x, 138, 24, 4);
2475                         x += 8;
2476                         M_Print (x, 146, " More than 64 players?? ");
2477                         M_Print (x, 154, "  First, question your  ");
2478                         M_Print (x, 162, "   sanity, then email   ");
2479                         M_Print (x, 170, " havoc@gamevisions.com  ");
2480                         /*
2481                         M_Print (x, 146, "  More than 4 players   ");
2482                         M_Print (x, 154, " requires using command ");
2483                         M_Print (x, 162, "line parameters; please ");
2484                         M_Print (x, 170, "   see techinfo.txt.    ");
2485                         */
2486                 }
2487                 else
2488                 {
2489                         m_serverInfoMessage = false;
2490                 }
2491         }
2492 }
2493
2494
2495 void M_NetStart_Change (int dir)
2496 {
2497         int count;
2498
2499         switch (gameoptions_cursor)
2500         {
2501         case 1:
2502                 maxplayers += dir;
2503                 if (maxplayers > svs.maxclientslimit)
2504                 {
2505                         maxplayers = svs.maxclientslimit;
2506                         m_serverInfoMessage = true;
2507                         m_serverInfoMessageTime = realtime;
2508                 }
2509                 if (maxplayers < 2)
2510                         maxplayers = 2;
2511                 break;
2512
2513         case 2:
2514                 if (deathmatch.integer) // changing from deathmatch to coop
2515                 {
2516                         Cvar_SetValueQuick (&coop, 1);
2517                         Cvar_SetValueQuick (&deathmatch, 0);
2518                 }
2519                 else // changing from coop to deathmatch
2520                 {
2521                         Cvar_SetValueQuick (&coop, 0);
2522                         Cvar_SetValueQuick (&deathmatch, 1);
2523                 }
2524                 break;
2525
2526         case 3:
2527                 if (gamemode == GAME_ROGUE)
2528                         count = 6;
2529                 else
2530                         count = 2;
2531
2532                 Cvar_SetValueQuick (&teamplay, teamplay.integer + dir);
2533                 if (teamplay.integer > count)
2534                         Cvar_SetValueQuick (&teamplay, 0);
2535                 else if (teamplay.integer < 0)
2536                         Cvar_SetValueQuick (&teamplay, count);
2537                 break;
2538
2539         case 4:
2540                 Cvar_SetValueQuick (&skill, skill.integer + dir);
2541                 if (skill.integer > 3)
2542                         Cvar_SetValueQuick (&skill, 0);
2543                 if (skill.integer < 0)
2544                         Cvar_SetValueQuick (&skill, 3);
2545                 break;
2546
2547         case 5:
2548                 Cvar_SetValueQuick (&fraglimit, fraglimit.integer + dir*10);
2549                 if (fraglimit.integer > 100)
2550                         Cvar_SetValueQuick (&fraglimit, 0);
2551                 if (fraglimit.integer < 0)
2552                         Cvar_SetValueQuick (&fraglimit, 100);
2553                 break;
2554
2555         case 6:
2556                 Cvar_SetValueQuick (&timelimit, timelimit.value + dir*5);
2557                 if (timelimit.value > 60)
2558                         Cvar_SetValueQuick (&timelimit, 0);
2559                 if (timelimit.value < 0)
2560                         Cvar_SetValueQuick (&timelimit, 60);
2561                 break;
2562
2563         case 7:
2564                 startepisode += dir;
2565         //MED 01/06/97 added hipnotic count
2566                 if (gamemode == GAME_HIPNOTIC)
2567                         count = 6;
2568         //PGM 01/07/97 added rogue count
2569         //PGM 03/02/97 added 1 for dmatch episode
2570                 else if (gamemode == GAME_ROGUE)
2571                         count = 4;
2572                 else if (gamemode == GAME_NEHAHRA)
2573                         count = 4;
2574                 else if (registered.integer)
2575                         count = 7;
2576                 else
2577                         count = 2;
2578
2579                 if (startepisode < 0)
2580                         startepisode = count - 1;
2581
2582                 if (startepisode >= count)
2583                         startepisode = 0;
2584
2585                 startlevel = 0;
2586                 break;
2587
2588         case 8:
2589                 startlevel += dir;
2590     //MED 01/06/97 added hipnotic episodes
2591                 if (gamemode == GAME_HIPNOTIC)
2592                         count = hipnoticepisodes[startepisode].levels;
2593         //PGM 01/06/97 added hipnotic episodes
2594                 else if (gamemode == GAME_ROGUE)
2595                         count = rogueepisodes[startepisode].levels;
2596                 else if (gamemode == GAME_NEHAHRA)
2597                         count = nehahraepisodes[startepisode].levels;
2598                 else
2599                         count = episodes[startepisode].levels;
2600
2601                 if (startlevel < 0)
2602                         startlevel = count - 1;
2603
2604                 if (startlevel >= count)
2605                         startlevel = 0;
2606                 break;
2607         }
2608 }
2609
2610 void M_GameOptions_Key (int key)
2611 {
2612         switch (key)
2613         {
2614         case K_ESCAPE:
2615                 M_Menu_Net_f ();
2616                 break;
2617
2618         case K_UPARROW:
2619                 S_LocalSound ("misc/menu1.wav");
2620                 gameoptions_cursor--;
2621                 if (gameoptions_cursor < 0)
2622                         gameoptions_cursor = NUM_GAMEOPTIONS-1;
2623                 break;
2624
2625         case K_DOWNARROW:
2626                 S_LocalSound ("misc/menu1.wav");
2627                 gameoptions_cursor++;
2628                 if (gameoptions_cursor >= NUM_GAMEOPTIONS)
2629                         gameoptions_cursor = 0;
2630                 break;
2631
2632         case K_LEFTARROW:
2633                 if (gameoptions_cursor == 0)
2634                         break;
2635                 S_LocalSound ("misc/menu3.wav");
2636                 M_NetStart_Change (-1);
2637                 break;
2638
2639         case K_RIGHTARROW:
2640                 if (gameoptions_cursor == 0)
2641                         break;
2642                 S_LocalSound ("misc/menu3.wav");
2643                 M_NetStart_Change (1);
2644                 break;
2645
2646         case K_ENTER:
2647                 S_LocalSound ("misc/menu2.wav");
2648                 if (gameoptions_cursor == 0)
2649                 {
2650                         if (sv.active)
2651                                 Cbuf_AddText ("disconnect\n");
2652                         Cbuf_AddText ("listen 0\n");    // so host_netport will be re-examined
2653                         Cbuf_AddText ( va ("maxplayers %u\n", maxplayers) );
2654 //                      SCR_BeginLoadingPlaque ();
2655
2656                         if (gamemode == GAME_HIPNOTIC)
2657                                 Cbuf_AddText ( va ("map %s\n", hipnoticlevels[hipnoticepisodes[startepisode].firstLevel + startlevel].name) );
2658                         else if (gamemode == GAME_ROGUE)
2659                                 Cbuf_AddText ( va ("map %s\n", roguelevels[rogueepisodes[startepisode].firstLevel + startlevel].name) );
2660                         else if (gamemode == GAME_NEHAHRA)
2661                                 Cbuf_AddText ( va ("map %s\n", nehahralevels[nehahraepisodes[startepisode].firstLevel + startlevel].name) );
2662                         else
2663                                 Cbuf_AddText ( va ("map %s\n", levels[episodes[startepisode].firstLevel + startlevel].name) );
2664
2665                         return;
2666                 }
2667
2668                 M_NetStart_Change (1);
2669                 break;
2670         }
2671 }
2672
2673 //=============================================================================
2674 /* SEARCH MENU */
2675
2676 qboolean        searchComplete = false;
2677 double          searchCompleteTime;
2678
2679 void M_Menu_Search_f (void)
2680 {
2681         key_dest = key_menu;
2682         m_state = m_search;
2683         m_entersound = false;
2684         slistSilent = true;
2685         slistLocal = false;
2686         searchComplete = false;
2687         NET_Slist_f();
2688
2689 }
2690
2691
2692 void M_Search_Draw (void)
2693 {
2694         cachepic_t      *p;
2695         int x;
2696
2697         p = Draw_CachePic ("gfx/p_multi.lmp");
2698         M_DrawPic ( (320-p->width)/2, 4, "gfx/p_multi.lmp");
2699         x = (320/2) - ((12*8)/2) + 4;
2700         M_DrawTextBox (x-8, 32, 12, 1);
2701         M_Print (x, 40, "Searching...");
2702
2703         if(slistInProgress)
2704         {
2705                 NET_Poll();
2706                 return;
2707         }
2708
2709         if (! searchComplete)
2710         {
2711                 searchComplete = true;
2712                 searchCompleteTime = realtime;
2713         }
2714
2715         if (hostCacheCount)
2716         {
2717                 M_Menu_ServerList_f ();
2718                 return;
2719         }
2720
2721         M_PrintWhite ((320/2) - ((22*8)/2), 64, "No Quake servers found");
2722         if ((realtime - searchCompleteTime) < 3.0)
2723                 return;
2724
2725         M_Menu_LanConfig_f ();
2726 }
2727
2728
2729 void M_Search_Key (int key)
2730 {
2731 }
2732
2733 //=============================================================================
2734 /* SLIST MENU */
2735
2736 int             slist_cursor;
2737 qboolean slist_sorted;
2738
2739 void M_Menu_ServerList_f (void)
2740 {
2741         key_dest = key_menu;
2742         m_state = m_slist;
2743         m_entersound = true;
2744         slist_cursor = 0;
2745         m_return_onerror = false;
2746         m_return_reason[0] = 0;
2747         slist_sorted = false;
2748 }
2749
2750
2751 void M_ServerList_Draw (void)
2752 {
2753         int             n;
2754         char    string [64];
2755         cachepic_t      *p;
2756
2757         if (!slist_sorted)
2758         {
2759                 if (hostCacheCount > 1)
2760                 {
2761                         int     i,j;
2762                         hostcache_t temp;
2763                         for (i = 0; i < hostCacheCount; i++)
2764                                 for (j = i+1; j < hostCacheCount; j++)
2765                                         if (strcmp(hostcache[j].name, hostcache[i].name) < 0)
2766                                         {
2767                                                 memcpy(&temp, &hostcache[j], sizeof(hostcache_t));
2768                                                 memcpy(&hostcache[j], &hostcache[i], sizeof(hostcache_t));
2769                                                 memcpy(&hostcache[i], &temp, sizeof(hostcache_t));
2770                                         }
2771                 }
2772                 slist_sorted = true;
2773         }
2774
2775         p = Draw_CachePic ("gfx/p_multi.lmp");
2776         M_DrawPic ( (320-p->width)/2, 4, "gfx/p_multi.lmp");
2777         for (n = 0; n < hostCacheCount; n++)
2778         {
2779                 if (hostcache[n].maxusers)
2780                         sprintf(string, "%-15.15s %-15.15s %2u/%2u\n", hostcache[n].name, hostcache[n].map, hostcache[n].users, hostcache[n].maxusers);
2781                 else
2782                         sprintf(string, "%-15.15s %-15.15s\n", hostcache[n].name, hostcache[n].map);
2783                 M_Print (16, 32 + 8*n, string);
2784         }
2785         M_DrawCharacter (0, 32 + slist_cursor*8, 12+((int)(realtime*4)&1));
2786
2787         if (*m_return_reason)
2788                 M_PrintWhite (16, 148, m_return_reason);
2789 }
2790
2791
2792 void M_ServerList_Key (int k)
2793 {
2794         switch (k)
2795         {
2796         case K_ESCAPE:
2797                 M_Menu_LanConfig_f ();
2798                 break;
2799
2800         case K_SPACE:
2801                 M_Menu_Search_f ();
2802                 break;
2803
2804         case K_UPARROW:
2805         case K_LEFTARROW:
2806                 S_LocalSound ("misc/menu1.wav");
2807                 slist_cursor--;
2808                 if (slist_cursor < 0)
2809                         slist_cursor = hostCacheCount - 1;
2810                 break;
2811
2812         case K_DOWNARROW:
2813         case K_RIGHTARROW:
2814                 S_LocalSound ("misc/menu1.wav");
2815                 slist_cursor++;
2816                 if (slist_cursor >= hostCacheCount)
2817                         slist_cursor = 0;
2818                 break;
2819
2820         case K_ENTER:
2821                 S_LocalSound ("misc/menu2.wav");
2822                 m_return_state = m_state;
2823                 m_return_onerror = true;
2824                 slist_sorted = false;
2825                 key_dest = key_game;
2826                 m_state = m_none;
2827                 Cbuf_AddText ( va ("connect \"%s\"\n", hostcache[slist_cursor].cname) );
2828                 break;
2829
2830         default:
2831                 break;
2832         }
2833
2834 }
2835
2836 //=============================================================================
2837 /* Menu Subsystem */
2838
2839
2840 void M_Init (void)
2841 {
2842         Cmd_AddCommand ("togglemenu", M_ToggleMenu_f);
2843
2844         Cmd_AddCommand ("menu_main", M_Menu_Main_f);
2845         Cmd_AddCommand ("menu_singleplayer", M_Menu_SinglePlayer_f);
2846         Cmd_AddCommand ("menu_load", M_Menu_Load_f);
2847         Cmd_AddCommand ("menu_save", M_Menu_Save_f);
2848         Cmd_AddCommand ("menu_multiplayer", M_Menu_MultiPlayer_f);
2849         Cmd_AddCommand ("menu_setup", M_Menu_Setup_f);
2850         Cmd_AddCommand ("menu_options", M_Menu_Options_f);
2851         Cmd_AddCommand ("menu_keys", M_Menu_Keys_f);
2852         Cmd_AddCommand ("menu_video", M_Menu_Video_f);
2853         Cmd_AddCommand ("help", M_Menu_Help_f);
2854         Cmd_AddCommand ("menu_quit", M_Menu_Quit_f);
2855
2856         if (gamemode == GAME_NEHAHRA)
2857         {
2858                 if (COM_FileExists("maps/neh1m4.bsp"))
2859                 {
2860                         if (COM_FileExists("hearing.dem"))
2861                         {
2862                                 Con_Printf("Nehahra movie and game detected.\n");
2863                                 NehGameType = TYPE_BOTH;
2864                         }
2865                         else
2866                         {
2867                                 Con_Printf("Nehahra game detected.\n");
2868                                 NehGameType = TYPE_GAME;
2869                         }
2870                 }
2871                 else
2872                 {
2873                         if (COM_FileExists("hearing.dem"))
2874                         {
2875                                 Con_Printf("Nehahra movie detected.\n");
2876                                 NehGameType = TYPE_DEMO;
2877                         }
2878                         else
2879                         {
2880                                 Con_Printf("Nehahra not found.\n");
2881                                 NehGameType = TYPE_GAME; // could just complain, but...
2882                         }
2883                 }
2884         }
2885 }
2886
2887 void M_Draw (void)
2888 {
2889         if (m_state == m_none || key_dest != key_menu)
2890                 return;
2891
2892         M_DrawBackground();
2893
2894         switch (m_state)
2895         {
2896         case m_none:
2897                 break;
2898
2899         case m_main:
2900                 M_Main_Draw ();
2901                 break;
2902
2903         case m_demo:
2904                 M_Demo_Draw ();
2905                 break;
2906
2907         case m_singleplayer:
2908                 M_SinglePlayer_Draw ();
2909                 break;
2910
2911         case m_load:
2912                 M_Load_Draw ();
2913                 break;
2914
2915         case m_save:
2916                 M_Save_Draw ();
2917                 break;
2918
2919         case m_multiplayer:
2920                 M_MultiPlayer_Draw ();
2921                 break;
2922
2923         case m_setup:
2924                 M_Setup_Draw ();
2925                 break;
2926
2927         case m_net:
2928                 M_Net_Draw ();
2929                 break;
2930
2931         case m_options:
2932                 M_Options_Draw ();
2933                 break;
2934
2935         case m_keys:
2936                 M_Keys_Draw ();
2937                 break;
2938
2939         case m_video:
2940                 M_Video_Draw ();
2941                 break;
2942
2943         case m_help:
2944                 M_Help_Draw ();
2945                 break;
2946
2947         case m_quit:
2948                 M_Quit_Draw ();
2949                 break;
2950
2951         case m_lanconfig:
2952                 M_LanConfig_Draw ();
2953                 break;
2954
2955         case m_gameoptions:
2956                 M_GameOptions_Draw ();
2957                 break;
2958
2959         case m_search:
2960                 M_Search_Draw ();
2961                 break;
2962
2963         case m_slist:
2964                 M_ServerList_Draw ();
2965                 break;
2966         }
2967
2968         if (m_entersound)
2969         {
2970                 S_LocalSound ("misc/menu2.wav");
2971                 m_entersound = false;
2972         }
2973
2974         S_ExtraUpdate ();
2975 }
2976
2977
2978 void M_Keydown (int key)
2979 {
2980         switch (m_state)
2981         {
2982         case m_none:
2983                 return;
2984
2985         case m_main:
2986                 M_Main_Key (key);
2987                 return;
2988
2989         case m_demo:
2990                 M_Demo_Key (key);
2991                 return;
2992
2993         case m_singleplayer:
2994                 M_SinglePlayer_Key (key);
2995                 return;
2996
2997         case m_load:
2998                 M_Load_Key (key);
2999                 return;
3000
3001         case m_save:
3002                 M_Save_Key (key);
3003                 return;
3004
3005         case m_multiplayer:
3006                 M_MultiPlayer_Key (key);
3007                 return;
3008
3009         case m_setup:
3010                 M_Setup_Key (key);
3011                 return;
3012
3013         case m_net:
3014                 M_Net_Key (key);
3015                 return;
3016
3017         case m_options:
3018                 M_Options_Key (key);
3019                 return;
3020
3021         case m_keys:
3022                 M_Keys_Key (key);
3023                 return;
3024
3025         case m_video:
3026                 M_Video_Key (key);
3027                 return;
3028
3029         case m_help:
3030                 M_Help_Key (key);
3031                 return;
3032
3033         case m_quit:
3034                 M_Quit_Key (key);
3035                 return;
3036
3037         case m_lanconfig:
3038                 M_LanConfig_Key (key);
3039                 return;
3040
3041         case m_gameoptions:
3042                 M_GameOptions_Key (key);
3043                 return;
3044
3045         case m_search:
3046                 M_Search_Key (key);
3047                 break;
3048
3049         case m_slist:
3050                 M_ServerList_Key (key);
3051                 return;
3052         }
3053 }
3054
3055
3056 void M_ConfigureNetSubsystem(void)
3057 {
3058 // enable/disable net systems to match desired config
3059
3060         Cbuf_AddText ("stopdemo\n");
3061
3062         if (IPXConfig || TCPIPConfig)
3063                 net_hostport = lanConfig_port;
3064 }