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