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