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