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