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