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