]> icculus.org git repositories - divverent/darkplaces.git/blob - menu.c
removed number field from client_t struct as it seemed to be behaving unreliably...
[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 #include "mprogdefs.h"
24
25 #define TYPE_DEMO 1
26 #define TYPE_GAME 2
27 #define TYPE_BOTH 3
28
29 mempool_t *menu_mempool;
30
31 int NehGameType;
32
33 enum m_state_e m_state;
34
35 void M_Menu_Main_f (void);
36         void M_Menu_SinglePlayer_f (void);
37                 void M_Menu_Load_f (void);
38                 void M_Menu_Save_f (void);
39         void M_Menu_MultiPlayer_f (void);
40                 void M_Menu_Setup_f (void);
41         void M_Menu_Options_f (void);
42         void M_Menu_Options_Effects_f (void);
43         void M_Menu_Options_Graphics_f (void);
44         void M_Menu_Options_ColorControl_f (void);
45                 void M_Menu_Keys_f (void);
46                 void M_Menu_Reset_f (void);
47                 void M_Menu_Video_f (void);
48         void M_Menu_Help_f (void);
49         void M_Menu_Quit_f (void);
50 void M_Menu_LanConfig_f (void);
51 void M_Menu_GameOptions_f (void);
52 void M_Menu_ServerList_f (void);
53
54 void M_Main_Draw (void);
55         void M_SinglePlayer_Draw (void);
56                 void M_Load_Draw (void);
57                 void M_Save_Draw (void);
58         void M_MultiPlayer_Draw (void);
59                 void M_Setup_Draw (void);
60         void M_Options_Draw (void);
61         void M_Options_Effects_Draw (void);
62         void M_Options_Graphics_Draw (void);
63         void M_Options_ColorControl_Draw (void);
64                 void M_Keys_Draw (void);
65                 void M_Reset_Draw (void);
66                 void M_Video_Draw (void);
67         void M_Help_Draw (void);
68         void M_Quit_Draw (void);
69 void M_LanConfig_Draw (void);
70 void M_GameOptions_Draw (void);
71 void M_ServerList_Draw (void);
72
73 void M_Main_Key (int key, char ascii);
74         void M_SinglePlayer_Key (int key, char ascii);
75                 void M_Load_Key (int key, char ascii);
76                 void M_Save_Key (int key, char ascii);
77         void M_MultiPlayer_Key (int key, char ascii);
78                 void M_Setup_Key (int key, char ascii);
79         void M_Options_Key (int key, char ascii);
80         void M_Options_Effects_Key (int key, char ascii);
81         void M_Options_Graphics_Key (int key, char ascii);
82         void M_Options_ColorControl_Key (int key, char ascii);
83                 void M_Keys_Key (int key, char ascii);
84                 void M_Reset_Key (int key, char ascii);
85                 void M_Video_Key (int key, char ascii);
86         void M_Help_Key (int key, char ascii);
87         void M_Quit_Key (int key, char ascii);
88 void M_LanConfig_Key (int key, char ascii);
89 void M_GameOptions_Key (int key, char ascii);
90 void M_ServerList_Key (int key, char ascii);
91
92 qboolean        m_entersound;           // play after drawing a frame, so caching
93                                                                 // won't disrupt the sound
94
95 char            m_return_reason [32];
96
97 void M_Update_Return_Reason(char *s)
98 {
99         strlcpy(m_return_reason, s, sizeof(m_return_reason));
100         if (s)
101                 Con_Printf("%s\n", s);
102 }
103
104 #define StartingGame    (m_multiplayer_cursor == 1)
105 #define JoiningGame             (m_multiplayer_cursor == 0)
106
107 // Nehahra
108 #define NumberOfNehahraDemos 34
109 typedef struct
110 {
111         char *name;
112         char *desc;
113 } nehahrademonames_t;
114
115 nehahrademonames_t NehahraDemos[NumberOfNehahraDemos] =
116 {
117         {"intro", "Prologue"},
118         {"genf", "The Beginning"},
119         {"genlab", "A Doomed Project"},
120         {"nehcre", "The New Recruits"},
121         {"maxneh", "Breakthrough"},
122         {"maxchar", "Renewal and Duty"},
123         {"crisis", "Worlds Collide"},
124         {"postcris", "Darkening Skies"},
125         {"hearing", "The Hearing"},
126         {"getjack", "On a Mexican Radio"},
127         {"prelude", "Honor and Justice"},
128         {"abase", "A Message Sent"},
129         {"effect", "The Other Side"},
130         {"uhoh", "Missing in Action"},
131         {"prepare", "The Response"},
132         {"vision", "Farsighted Eyes"},
133         {"maxturns", "Enter the Immortal"},
134         {"backlot", "Separate Ways"},
135         {"maxside", "The Ancient Runes"},
136         {"counter", "The New Initiative"},
137         {"warprep", "Ghosts to the World"},
138         {"counter1", "A Fate Worse Than Death"},
139         {"counter2", "Friendly Fire"},
140         {"counter3", "Minor Setback"},
141         {"madmax", "Scores to Settle"},
142         {"quake", "One Man"},
143         {"cthmm", "Shattered Masks"},
144         {"shades", "Deal with the Dead"},
145         {"gophil", "An Unlikely Hero"},
146         {"cstrike", "War in Hell"},
147         {"shubset", "The Conspiracy"},
148         {"shubdie", "Even Death May Die"},
149         {"newranks", "An Empty Throne"},
150         {"seal", "The Seal is Broken"}
151 };
152
153 float menu_x, menu_y, menu_width, menu_height;
154
155 void M_Background(int width, int height)
156 {
157         menu_width = width;
158         menu_height = height;
159         menu_x = (vid.conwidth - menu_width) * 0.5;
160         menu_y = (vid.conheight - menu_height) * 0.5;
161         //DrawQ_Fill(menu_x, menu_y, menu_width, menu_height, 0, 0, 0, 0.5, 0);
162         DrawQ_Fill(0, 0, vid.conwidth, vid.conheight, 0, 0, 0, 0.5, 0);
163 }
164
165 /*
166 ================
167 M_DrawCharacter
168
169 Draws one solid graphics character
170 ================
171 */
172 void M_DrawCharacter (float cx, float cy, int num)
173 {
174         char temp[2];
175         temp[0] = num;
176         temp[1] = 0;
177         DrawQ_String(menu_x + cx, menu_y + cy, temp, 1, 8, 8, 1, 1, 1, 1, 0);
178 }
179
180 void M_Print(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_PrintRed (float cx, float cy, const char *str)
186 {
187         DrawQ_String(menu_x + cx, menu_y + cy, str, 0, 8, 8, 1, 0, 0, 1, 0);
188 }
189
190 void M_ItemPrint(float cx, float cy, char *str, int unghosted)
191 {
192         if (unghosted)
193                 DrawQ_String(menu_x + cx, menu_y + cy, str, 0, 8, 8, 1, 1, 1, 1, 0);
194         else
195                 DrawQ_String(menu_x + cx, menu_y + cy, str, 0, 8, 8, 0.4, 0.4, 0.4, 1, 0);
196 }
197
198 void M_DrawPic (float cx, float cy, char *picname)
199 {
200         DrawQ_Pic (menu_x + cx, menu_y + cy, picname, 0, 0, 1, 1, 1, 1, 0);
201 }
202
203 qbyte identityTable[256];
204 qbyte translationTable[256];
205
206 void M_BuildTranslationTable(int top, int bottom)
207 {
208         int j;
209         qbyte *dest, *source;
210
211         for (j = 0; j < 256; j++)
212                 identityTable[j] = j;
213         dest = translationTable;
214         source = identityTable;
215         memcpy (dest, source, 256);
216
217         // LordHavoc: corrected skin color ranges
218         if (top < 128 || (top >= 224 && top < 240))     // the artists made some backwards ranges.  sigh.
219                 memcpy (dest + TOP_RANGE, source + top, 16);
220         else
221                 for (j=0 ; j<16 ; j++)
222                         dest[TOP_RANGE+j] = source[top+15-j];
223
224         // LordHavoc: corrected skin color ranges
225         if (bottom < 128 || (bottom >= 224 && bottom < 240))
226                 memcpy (dest + BOTTOM_RANGE, source + bottom, 16);
227         else
228                 for (j=0 ; j<16 ; j++)
229                         dest[BOTTOM_RANGE+j] = source[bottom+15-j];
230 }
231
232
233 void M_DrawTextBox (float x, float y, float width, float height)
234 {
235         int n;
236         float cx, cy;
237
238         // draw left side
239         cx = x;
240         cy = y;
241         M_DrawPic (cx, cy, "gfx/box_tl.lmp");
242         for (n = 0; n < height; n++)
243         {
244                 cy += 8;
245                 M_DrawPic (cx, cy, "gfx/box_ml.lmp");
246         }
247         M_DrawPic (cx, cy+8, "gfx/box_bl.lmp");
248
249         // draw middle
250         cx += 8;
251         while (width > 0)
252         {
253                 cy = y;
254                 M_DrawPic (cx, cy, "gfx/box_tm.lmp");
255                 for (n = 0; n < height; n++)
256                 {
257                         cy += 8;
258                         if (n >= 1)
259                                 M_DrawPic (cx, cy, "gfx/box_mm2.lmp");
260                         else
261                                 M_DrawPic (cx, cy, "gfx/box_mm.lmp");
262                 }
263                 M_DrawPic (cx, cy+8, "gfx/box_bm.lmp");
264                 width -= 2;
265                 cx += 16;
266         }
267
268         // draw right side
269         cy = y;
270         M_DrawPic (cx, cy, "gfx/box_tr.lmp");
271         for (n = 0; n < height; n++)
272         {
273                 cy += 8;
274                 M_DrawPic (cx, cy, "gfx/box_mr.lmp");
275         }
276         M_DrawPic (cx, cy+8, "gfx/box_br.lmp");
277 }
278
279 //=============================================================================
280
281 //int m_save_demonum;
282
283 /*
284 ================
285 M_ToggleMenu_f
286 ================
287 */
288 void M_ToggleMenu_f (void)
289 {
290         m_entersound = true;
291
292         if (key_dest != key_menu || m_state != m_main)
293                 M_Menu_Main_f ();
294         else
295         {
296                 key_dest = key_game;
297                 m_state = m_none;
298         }
299 }
300
301
302 int demo_cursor;
303 void M_Demo_Draw (void)
304 {
305         int i;
306
307         M_Background(320, 200);
308
309         for (i = 0;i < NumberOfNehahraDemos;i++)
310                 M_Print(16, 16 + 8*i, NehahraDemos[i].desc);
311
312         // line cursor
313         M_DrawCharacter (8, 16 + demo_cursor*8, 12+((int)(realtime*4)&1));
314 }
315
316
317 void M_Menu_Demos_f (void)
318 {
319         key_dest = key_menu;
320         m_state = m_demo;
321         m_entersound = true;
322 }
323
324 void M_Demo_Key (int k, char ascii)
325 {
326         switch (k)
327         {
328         case K_ESCAPE:
329                 M_Menu_Main_f ();
330                 break;
331
332         case K_ENTER:
333                 S_LocalSound ("misc/menu2.wav", true);
334                 m_state = m_none;
335                 key_dest = key_game;
336                 Cbuf_AddText (va ("playdemo %s\n", NehahraDemos[demo_cursor].name));
337                 return;
338
339         case K_UPARROW:
340         case K_LEFTARROW:
341                 S_LocalSound ("misc/menu1.wav", true);
342                 demo_cursor--;
343                 if (demo_cursor < 0)
344                         demo_cursor = NumberOfNehahraDemos-1;
345                 break;
346
347         case K_DOWNARROW:
348         case K_RIGHTARROW:
349                 S_LocalSound ("misc/menu1.wav", true);
350                 demo_cursor++;
351                 if (demo_cursor >= NumberOfNehahraDemos)
352                         demo_cursor = 0;
353                 break;
354         }
355 }
356
357 //=============================================================================
358 /* MAIN MENU */
359
360 int     m_main_cursor;
361
362 int MAIN_ITEMS = 4; // Nehahra: Menu Disable
363
364 void M_Menu_Main_f (void)
365 {
366         if (gamemode == GAME_NEHAHRA)
367         {
368                 if (NehGameType == TYPE_DEMO)
369                         MAIN_ITEMS = 4;
370                 else if (NehGameType == TYPE_GAME)
371                         MAIN_ITEMS = 5;
372                 else
373                         MAIN_ITEMS = 6;
374         }
375         else if (gamemode == GAME_NETHERWORLD)//VORTEX: menu restarting item
376                 MAIN_ITEMS = 6;
377         else
378                 MAIN_ITEMS = 5;
379
380         /*
381         if (key_dest != key_menu)
382         {
383                 m_save_demonum = cls.demonum;
384                 cls.demonum = -1;
385         }
386         */
387         key_dest = key_menu;
388         m_state = m_main;
389         m_entersound = true;
390 }
391
392
393 void M_Main_Draw (void)
394 {
395         int             f;
396         cachepic_t      *p;
397
398         M_Background(320, 200);
399
400         M_DrawPic (16, 4, "gfx/qplaque.lmp");
401         p = Draw_CachePic ("gfx/ttl_main.lmp");
402         M_DrawPic ( (320-p->width)/2, 4, "gfx/ttl_main.lmp");
403 // Nehahra
404         if (gamemode == GAME_NEHAHRA)
405         {
406                 if (NehGameType == TYPE_BOTH)
407                         M_DrawPic (72, 32, "gfx/mainmenu.lmp");
408                 else if (NehGameType == TYPE_GAME)
409                         M_DrawPic (72, 32, "gfx/gamemenu.lmp");
410                 else
411                         M_DrawPic (72, 32, "gfx/demomenu.lmp");
412         }
413         else
414                 M_DrawPic (72, 32, "gfx/mainmenu.lmp");
415
416         f = (int)(realtime * 10)%6;
417
418         M_DrawPic (54, 32 + m_main_cursor * 20, va("gfx/menudot%i.lmp", f+1));
419 }
420
421
422 void M_Main_Key (int key, char ascii)
423 {
424         switch (key)
425         {
426         case K_ESCAPE:
427                 key_dest = key_game;
428                 m_state = m_none;
429                 //cls.demonum = m_save_demonum;
430                 //if (cls.demonum != -1 && !cls.demoplayback && cls.state != ca_connected)
431                 //      CL_NextDemo ();
432                 break;
433
434         case K_DOWNARROW:
435                 S_LocalSound ("misc/menu1.wav", true);
436                 if (++m_main_cursor >= MAIN_ITEMS)
437                         m_main_cursor = 0;
438                 break;
439
440         case K_UPARROW:
441                 S_LocalSound ("misc/menu1.wav", true);
442                 if (--m_main_cursor < 0)
443                         m_main_cursor = MAIN_ITEMS - 1;
444                 break;
445
446         case K_ENTER:
447                 m_entersound = true;
448
449                 if (gamemode == GAME_NEHAHRA)
450                 {
451                         switch (NehGameType)
452                         {
453                         case TYPE_BOTH:
454                                 switch (m_main_cursor)
455                                 {
456                                 case 0:
457                                         M_Menu_SinglePlayer_f ();
458                                         break;
459
460                                 case 1:
461                                         M_Menu_Demos_f ();
462                                         break;
463
464                                 case 2:
465                                         M_Menu_MultiPlayer_f ();
466                                         break;
467
468                                 case 3:
469                                         M_Menu_Options_f ();
470                                         break;
471
472                                 case 4:
473                                         key_dest = key_game;
474                                         if (sv.active)
475                                                 Cbuf_AddText ("disconnect\n");
476                                         Cbuf_AddText ("playdemo endcred\n");
477                                         break;
478
479                                 case 5:
480                                         M_Menu_Quit_f ();
481                                         break;
482                                 }
483                                 break;
484                         case TYPE_GAME:
485                                 switch (m_main_cursor)
486                                 {
487                                 case 0:
488                                         M_Menu_SinglePlayer_f ();
489                                         break;
490
491                                 case 1:
492                                         M_Menu_MultiPlayer_f ();
493                                         break;
494
495                                 case 2:
496                                         M_Menu_Options_f ();
497                                         break;
498
499                                 case 3:
500                                         key_dest = key_game;
501                                         if (sv.active)
502                                                 Cbuf_AddText ("disconnect\n");
503                                         Cbuf_AddText ("playdemo endcred\n");
504                                         break;
505
506                                 case 4:
507                                         M_Menu_Quit_f ();
508                                         break;
509                                 }
510                                 break;
511                         case TYPE_DEMO:
512                                 switch (m_main_cursor)
513                                 {
514                                 case 0:
515                                         M_Menu_Demos_f ();
516                                         break;
517
518                                 case 1:
519                                         key_dest = key_game;
520                                         if (sv.active)
521                                                 Cbuf_AddText ("disconnect\n");
522                                         Cbuf_AddText ("playdemo endcred\n");
523                                         break;
524
525                                 case 2:
526                                         M_Menu_Options_f ();
527                                         break;
528
529                                 case 3:
530                                         M_Menu_Quit_f ();
531                                         break;
532                                 }
533                                 break;
534                         }
535                 }
536                 else if (gamemode == GAME_NETHERWORLD)//VORTEX: menu restarting item
537                 {
538                         switch (m_main_cursor)
539                         {
540                         case 0:
541                                 M_Menu_SinglePlayer_f ();
542                                 break;
543
544                         case 1:
545                                 M_Menu_MultiPlayer_f ();
546                                 break;
547
548                         case 2:
549                                 M_Menu_Options_f ();
550                                 break;
551
552                         case 3:
553                                 M_Menu_Help_f ();
554                                 break;
555
556                         case 4:
557                                 M_Menu_Quit_f ();
558                                 break;
559                         case 5:
560                                 MR_Restart();
561                                 break;
562                         }
563                 }
564                 else
565                 {
566                         switch (m_main_cursor)
567                         {
568                         case 0:
569                                 M_Menu_SinglePlayer_f ();
570                                 break;
571
572                         case 1:
573                                 M_Menu_MultiPlayer_f ();
574                                 break;
575
576                         case 2:
577                                 M_Menu_Options_f ();
578                                 break;
579
580                         case 3:
581                                 M_Menu_Help_f ();
582                                 break;
583
584                         case 4:
585                                 M_Menu_Quit_f ();
586                                 break;
587                         }
588                 }
589         }
590 }
591
592 //=============================================================================
593 /* SINGLE PLAYER MENU */
594
595 int     m_singleplayer_cursor;
596 #define SINGLEPLAYER_ITEMS      3
597
598
599 void M_Menu_SinglePlayer_f (void)
600 {
601         key_dest = key_menu;
602         m_state = m_singleplayer;
603         m_entersound = true;
604 }
605
606
607 void M_SinglePlayer_Draw (void)
608 {
609         cachepic_t      *p;
610
611         M_Background(320, 200);
612
613         M_DrawPic (16, 4, "gfx/qplaque.lmp");
614         p = Draw_CachePic ("gfx/ttl_sgl.lmp");
615
616         // Some mods don't have a single player mode
617         if (gamemode == GAME_NEXUIZ || gamemode == GAME_GOODVSBAD2 || gamemode == GAME_BATTLEMECH)
618         {
619                 M_DrawPic ((320 - p->width) / 2, 4, "gfx/ttl_sgl.lmp");
620
621                 M_DrawTextBox (60, 8 * 8, 23, 4);
622                 if (gamemode == GAME_NEXUIZ)
623                         M_Print(95, 10 * 8, "Nexuiz is for");
624                 else if (gamemode == GAME_GOODVSBAD2)
625                         M_Print(95, 10 * 8, "Good Vs Bad 2 is for");
626                 else  // if (gamemode == GAME_BATTLEMECH)
627                         M_Print(95, 10 * 8, "Battlemech is for");
628                 M_Print(83, 11 * 8, "multiplayer play only");
629         }
630         else
631         {
632                 int             f;
633
634                 M_DrawPic ( (320-p->width)/2, 4, "gfx/ttl_sgl.lmp");
635                 M_DrawPic (72, 32, "gfx/sp_menu.lmp");
636
637                 f = (int)(realtime * 10)%6;
638
639                 M_DrawPic (54, 32 + m_singleplayer_cursor * 20, va("gfx/menudot%i.lmp", f+1));
640         }
641 }
642
643
644 void M_SinglePlayer_Key (int key, char ascii)
645 {
646         if (gamemode == GAME_NEXUIZ || gamemode == GAME_GOODVSBAD2 || gamemode == GAME_BATTLEMECH)
647         {
648                 if (key == K_ESCAPE || key == K_ENTER)
649                         m_state = m_main;
650                 return;
651         }
652
653         switch (key)
654         {
655         case K_ESCAPE:
656                 M_Menu_Main_f ();
657                 break;
658
659         case K_DOWNARROW:
660                 S_LocalSound ("misc/menu1.wav", true);
661                 if (++m_singleplayer_cursor >= SINGLEPLAYER_ITEMS)
662                         m_singleplayer_cursor = 0;
663                 break;
664
665         case K_UPARROW:
666                 S_LocalSound ("misc/menu1.wav", true);
667                 if (--m_singleplayer_cursor < 0)
668                         m_singleplayer_cursor = SINGLEPLAYER_ITEMS - 1;
669                 break;
670
671         case K_ENTER:
672                 m_entersound = true;
673
674                 switch (m_singleplayer_cursor)
675                 {
676                 case 0:
677                         key_dest = key_game;
678                         if (sv.active)
679                                 Cbuf_AddText ("disconnect\n");
680                         Cbuf_AddText ("maxplayers 1\n");
681                         Cbuf_AddText ("deathmatch 0\n");
682                         Cbuf_AddText ("coop 0\n");
683                         if (gamemode == GAME_NEHAHRA)
684                                 Cbuf_AddText ("map nehstart\n");
685                         else if (gamemode == GAME_TRANSFUSION)
686                                 Cbuf_AddText ("map e1m1\n");
687                         else
688                                 Cbuf_AddText ("map start\n");
689                         break;
690
691                 case 1:
692                         M_Menu_Load_f ();
693                         break;
694
695                 case 2:
696                         M_Menu_Save_f ();
697                         break;
698                 }
699         }
700 }
701
702 //=============================================================================
703 /* LOAD/SAVE MENU */
704
705 int             load_cursor;            // 0 < load_cursor < MAX_SAVEGAMES
706
707 #define MAX_SAVEGAMES           12
708 char    m_filenames[MAX_SAVEGAMES][SAVEGAME_COMMENT_LENGTH+1];
709 int             loadable[MAX_SAVEGAMES];
710
711 void M_ScanSaves (void)
712 {
713         int             i, j;
714         char    name[MAX_OSPATH];
715         char    *str;
716         qfile_t *f;
717         int             version;
718
719         for (i=0 ; i<MAX_SAVEGAMES ; i++)
720         {
721                 strcpy (m_filenames[i], "--- UNUSED SLOT ---");
722                 loadable[i] = false;
723                 sprintf (name, "s%i.sav", i);
724                 f = FS_Open (name, "r", false);
725                 if (!f)
726                         continue;
727                 str = FS_Getline (f);
728                 sscanf (str, "%i\n", &version);
729                 str = FS_Getline (f);
730                 strlcpy (m_filenames[i], str, sizeof (m_filenames[i]));
731
732         // change _ back to space
733                 for (j=0 ; j<SAVEGAME_COMMENT_LENGTH ; j++)
734                         if (m_filenames[i][j] == '_')
735                                 m_filenames[i][j] = ' ';
736                 loadable[i] = true;
737                 FS_Close (f);
738         }
739 }
740
741 void M_Menu_Load_f (void)
742 {
743         m_entersound = true;
744         m_state = m_load;
745         key_dest = key_menu;
746         M_ScanSaves ();
747 }
748
749
750 void M_Menu_Save_f (void)
751 {
752         if (!sv.active)
753                 return;
754         if (cl.intermission)
755                 return;
756         if (!cl.islocalgame)
757                 return;
758         m_entersound = true;
759         m_state = m_save;
760         key_dest = key_menu;
761         M_ScanSaves ();
762 }
763
764
765 void M_Load_Draw (void)
766 {
767         int             i;
768         cachepic_t      *p;
769
770         M_Background(320, 200);
771
772         p = Draw_CachePic ("gfx/p_load.lmp");
773         M_DrawPic ( (320-p->width)/2, 4, "gfx/p_load.lmp");
774
775         for (i=0 ; i< MAX_SAVEGAMES; i++)
776                 M_Print(16, 32 + 8*i, m_filenames[i]);
777
778 // line cursor
779         M_DrawCharacter (8, 32 + load_cursor*8, 12+((int)(realtime*4)&1));
780 }
781
782
783 void M_Save_Draw (void)
784 {
785         int             i;
786         cachepic_t      *p;
787
788         M_Background(320, 200);
789
790         p = Draw_CachePic ("gfx/p_save.lmp");
791         M_DrawPic ( (320-p->width)/2, 4, "gfx/p_save.lmp");
792
793         for (i=0 ; i<MAX_SAVEGAMES ; i++)
794                 M_Print(16, 32 + 8*i, m_filenames[i]);
795
796 // line cursor
797         M_DrawCharacter (8, 32 + load_cursor*8, 12+((int)(realtime*4)&1));
798 }
799
800
801 void M_Load_Key (int k, char ascii)
802 {
803         switch (k)
804         {
805         case K_ESCAPE:
806                 M_Menu_SinglePlayer_f ();
807                 break;
808
809         case K_ENTER:
810                 S_LocalSound ("misc/menu2.wav", true);
811                 if (!loadable[load_cursor])
812                         return;
813                 m_state = m_none;
814                 key_dest = key_game;
815
816                 // issue the load command
817                 Cbuf_AddText (va ("load s%i\n", load_cursor) );
818                 return;
819
820         case K_UPARROW:
821         case K_LEFTARROW:
822                 S_LocalSound ("misc/menu1.wav", true);
823                 load_cursor--;
824                 if (load_cursor < 0)
825                         load_cursor = MAX_SAVEGAMES-1;
826                 break;
827
828         case K_DOWNARROW:
829         case K_RIGHTARROW:
830                 S_LocalSound ("misc/menu1.wav", true);
831                 load_cursor++;
832                 if (load_cursor >= MAX_SAVEGAMES)
833                         load_cursor = 0;
834                 break;
835         }
836 }
837
838
839 void M_Save_Key (int k, char ascii)
840 {
841         switch (k)
842         {
843         case K_ESCAPE:
844                 M_Menu_SinglePlayer_f ();
845                 break;
846
847         case K_ENTER:
848                 m_state = m_none;
849                 key_dest = key_game;
850                 Cbuf_AddText (va("save s%i\n", load_cursor));
851                 return;
852
853         case K_UPARROW:
854         case K_LEFTARROW:
855                 S_LocalSound ("misc/menu1.wav", true);
856                 load_cursor--;
857                 if (load_cursor < 0)
858                         load_cursor = MAX_SAVEGAMES-1;
859                 break;
860
861         case K_DOWNARROW:
862         case K_RIGHTARROW:
863                 S_LocalSound ("misc/menu1.wav", true);
864                 load_cursor++;
865                 if (load_cursor >= MAX_SAVEGAMES)
866                         load_cursor = 0;
867                 break;
868         }
869 }
870
871 //=============================================================================
872 /* MULTIPLAYER MENU */
873
874 int     m_multiplayer_cursor;
875 #define MULTIPLAYER_ITEMS       3
876
877
878 void M_Menu_MultiPlayer_f (void)
879 {
880         key_dest = key_menu;
881         m_state = m_multiplayer;
882         m_entersound = true;
883 }
884
885
886 void M_MultiPlayer_Draw (void)
887 {
888         int             f;
889         cachepic_t      *p;
890
891         M_Background(320, 200);
892
893         M_DrawPic (16, 4, "gfx/qplaque.lmp");
894         p = Draw_CachePic ("gfx/p_multi.lmp");
895         M_DrawPic ( (320-p->width)/2, 4, "gfx/p_multi.lmp");
896         M_DrawPic (72, 32, "gfx/mp_menu.lmp");
897
898         f = (int)(realtime * 10)%6;
899
900         M_DrawPic (54, 32 + m_multiplayer_cursor * 20, va("gfx/menudot%i.lmp", f+1));
901 }
902
903
904 void M_MultiPlayer_Key (int key, char ascii)
905 {
906         switch (key)
907         {
908         case K_ESCAPE:
909                 M_Menu_Main_f ();
910                 break;
911
912         case K_DOWNARROW:
913                 S_LocalSound ("misc/menu1.wav", true);
914                 if (++m_multiplayer_cursor >= MULTIPLAYER_ITEMS)
915                         m_multiplayer_cursor = 0;
916                 break;
917
918         case K_UPARROW:
919                 S_LocalSound ("misc/menu1.wav", true);
920                 if (--m_multiplayer_cursor < 0)
921                         m_multiplayer_cursor = MULTIPLAYER_ITEMS - 1;
922                 break;
923
924         case K_ENTER:
925                 m_entersound = true;
926                 switch (m_multiplayer_cursor)
927                 {
928                 case 0:
929                 case 1:
930                         M_Menu_LanConfig_f ();
931                         break;
932
933                 case 2:
934                         M_Menu_Setup_f ();
935                         break;
936                 }
937         }
938 }
939
940 //=============================================================================
941 /* SETUP MENU */
942
943 int             setup_cursor = 4;
944 int             setup_cursor_table[] = {40, 64, 88, 124, 140};
945
946 char    setup_myname[32];
947 int             setup_oldtop;
948 int             setup_oldbottom;
949 int             setup_top;
950 int             setup_bottom;
951 int             setup_rate;
952 int             setup_oldrate;
953
954 #define NUM_SETUP_CMDS  5
955
956 void M_Menu_Setup_f (void)
957 {
958         key_dest = key_menu;
959         m_state = m_setup;
960         m_entersound = true;
961         strcpy(setup_myname, cl_name.string);
962         setup_top = setup_oldtop = cl_color.integer >> 4;
963         setup_bottom = setup_oldbottom = cl_color.integer & 15;
964         setup_rate = cl_rate.integer;
965 }
966
967 static int menuplyr_width, menuplyr_height, menuplyr_top, menuplyr_bottom, menuplyr_load;
968 static qbyte *menuplyr_pixels;
969 static unsigned int *menuplyr_translated;
970
971 typedef struct ratetable_s
972 {
973         int rate;
974         char *name;
975 }
976 ratetable_t;
977
978 #define RATES ((int)(sizeof(setup_ratetable)/sizeof(setup_ratetable[0])))
979 static ratetable_t setup_ratetable[] =
980 {
981         {1000, "28.8 bad"},
982         {1500, "28.8 mediocre"},
983         {2000, "28.8 good"},
984         {2500, "33.6 mediocre"},
985         {3000, "33.6 good"},
986         {3500, "56k bad"},
987         {4000, "56k mediocre"},
988         {4500, "56k adequate"},
989         {5000, "56k good"},
990         {7000, "64k ISDN"},
991         {15000, "128k ISDN"},
992         {25000, "broadband"}
993 };
994
995 static int setup_rateindex(int rate)
996 {
997         int i;
998         for (i = 0;i < RATES;i++)
999                 if (setup_ratetable[i].rate > setup_rate)
1000                         break;
1001         return bound(1, i, RATES) - 1;
1002 }
1003
1004 void M_Setup_Draw (void)
1005 {
1006         int i;
1007         cachepic_t      *p;
1008
1009         M_Background(320, 200);
1010
1011         M_DrawPic (16, 4, "gfx/qplaque.lmp");
1012         p = Draw_CachePic ("gfx/p_multi.lmp");
1013         M_DrawPic ( (320-p->width)/2, 4, "gfx/p_multi.lmp");
1014
1015         M_Print(64, 40, "Your name");
1016         M_DrawTextBox (160, 32, 16, 1);
1017         M_Print(168, 40, setup_myname);
1018
1019         if (gamemode != GAME_GOODVSBAD2)
1020         {
1021                 M_Print(64, 64, "Shirt color");
1022                 M_Print(64, 88, "Pants color");
1023         }
1024
1025         M_Print(64, 124-8, "Network speed limit");
1026         M_Print(168, 124, va("%i (%s)", setup_rate, setup_ratetable[setup_rateindex(setup_rate)].name));
1027
1028         M_DrawTextBox (64, 140-8, 14, 1);
1029         M_Print(72, 140, "Accept Changes");
1030
1031         // LordHavoc: rewrote this code greatly
1032         if (menuplyr_load)
1033         {
1034                 qbyte *data, *f;
1035                 menuplyr_load = false;
1036                 menuplyr_top = -1;
1037                 menuplyr_bottom = -1;
1038                 if ((f = FS_LoadFile("gfx/menuplyr.lmp", tempmempool, true)))
1039                 {
1040                         data = LoadLMPAs8Bit (f, 0, 0);
1041                         menuplyr_width = image_width;
1042                         menuplyr_height = image_height;
1043                         Mem_Free(f);
1044                         menuplyr_pixels = Mem_Alloc(menu_mempool, menuplyr_width * menuplyr_height);
1045                         menuplyr_translated = Mem_Alloc(menu_mempool, menuplyr_width * menuplyr_height * 4);
1046                         memcpy(menuplyr_pixels, data, menuplyr_width * menuplyr_height);
1047                         Mem_Free(data);
1048                 }
1049         }
1050
1051         if (menuplyr_pixels)
1052         {
1053                 if (menuplyr_top != setup_top || menuplyr_bottom != setup_bottom)
1054                 {
1055                         menuplyr_top = setup_top;
1056                         menuplyr_bottom = setup_bottom;
1057                         M_BuildTranslationTable(menuplyr_top*16, menuplyr_bottom*16);
1058                         for (i = 0;i < menuplyr_width * menuplyr_height;i++)
1059                                 menuplyr_translated[i] = palette_complete[translationTable[menuplyr_pixels[i]]];
1060                         Draw_NewPic("gfx/menuplyr.lmp", menuplyr_width, menuplyr_height, true, (qbyte *)menuplyr_translated);
1061                 }
1062                 M_DrawPic(160, 48, "gfx/bigbox.lmp");
1063                 M_DrawPic(172, 56, "gfx/menuplyr.lmp");
1064         }
1065
1066         if (setup_cursor == 0)
1067                 M_DrawCharacter (168 + 8*strlen(setup_myname), setup_cursor_table [setup_cursor], 10+((int)(realtime*4)&1));
1068         else
1069                 M_DrawCharacter (56, setup_cursor_table [setup_cursor], 12+((int)(realtime*4)&1));
1070 }
1071
1072
1073 void M_Setup_Key (int k, char ascii)
1074 {
1075         int                     l;
1076
1077         switch (k)
1078         {
1079         case K_ESCAPE:
1080                 M_Menu_MultiPlayer_f ();
1081                 break;
1082
1083         case K_UPARROW:
1084                 S_LocalSound ("misc/menu1.wav", true);
1085                 setup_cursor--;
1086                 if (setup_cursor < 0)
1087                         setup_cursor = NUM_SETUP_CMDS-1;
1088                 break;
1089
1090         case K_DOWNARROW:
1091                 S_LocalSound ("misc/menu1.wav", true);
1092                 setup_cursor++;
1093                 if (setup_cursor >= NUM_SETUP_CMDS)
1094                         setup_cursor = 0;
1095                 break;
1096
1097         case K_LEFTARROW:
1098                 if (setup_cursor < 1)
1099                         return;
1100                 S_LocalSound ("misc/menu3.wav", true);
1101                 if (setup_cursor == 1)
1102                         setup_top = setup_top - 1;
1103                 if (setup_cursor == 2)
1104                         setup_bottom = setup_bottom - 1;
1105                 if (setup_cursor == 3)
1106                 {
1107                         l = setup_rateindex(setup_rate) - 1;
1108                         if (l < 0)
1109                                 l = RATES - 1;
1110                         setup_rate = setup_ratetable[l].rate;
1111                 }
1112                 break;
1113         case K_RIGHTARROW:
1114                 if (setup_cursor < 1)
1115                         return;
1116 forward:
1117                 S_LocalSound ("misc/menu3.wav", true);
1118                 if (setup_cursor == 1)
1119                         setup_top = setup_top + 1;
1120                 if (setup_cursor == 2)
1121                         setup_bottom = setup_bottom + 1;
1122                 if (setup_cursor == 3)
1123                 {
1124                         l = setup_rateindex(setup_rate) + 1;
1125                         if (l >= RATES)
1126                                 l = 0;
1127                         setup_rate = setup_ratetable[l].rate;
1128                 }
1129                 break;
1130
1131         case K_ENTER:
1132                 if (setup_cursor == 0)
1133                         return;
1134
1135                 if (setup_cursor == 1 || setup_cursor == 2 || setup_cursor == 3)
1136                         goto forward;
1137
1138                 // setup_cursor == 4 (Accept changes)
1139                 if (strcmp(cl_name.string, setup_myname) != 0)
1140                         Cbuf_AddText ( va ("name \"%s\"\n", setup_myname) );
1141                 if (setup_top != setup_oldtop || setup_bottom != setup_oldbottom)
1142                         Cbuf_AddText( va ("color %i %i\n", setup_top, setup_bottom) );
1143                 if (setup_rate != setup_oldrate)
1144                         Cbuf_AddText(va("rate %i\n", setup_rate));
1145
1146                 m_entersound = true;
1147                 M_Menu_MultiPlayer_f ();
1148                 break;
1149
1150         case K_BACKSPACE:
1151                 if (setup_cursor == 0)
1152                 {
1153                         if (strlen(setup_myname))
1154                                 setup_myname[strlen(setup_myname)-1] = 0;
1155                 }
1156                 break;
1157
1158         default:
1159                 if (ascii < 32 || ascii > 126)
1160                         break;
1161                 if (setup_cursor == 0)
1162                 {
1163                         l = strlen(setup_myname);
1164                         if (l < 15)
1165                         {
1166                                 setup_myname[l+1] = 0;
1167                                 setup_myname[l] = ascii;
1168                         }
1169                 }
1170         }
1171
1172         if (setup_top > 15)
1173                 setup_top = 0;
1174         if (setup_top < 0)
1175                 setup_top = 15;
1176         if (setup_bottom > 15)
1177                 setup_bottom = 0;
1178         if (setup_bottom < 0)
1179                 setup_bottom = 15;
1180 }
1181
1182 //=============================================================================
1183 /* OPTIONS MENU */
1184
1185 #define SLIDER_RANGE    10
1186
1187 void M_DrawSlider (int x, int y, float num, float rangemin, float rangemax)
1188 {
1189         char text[16];
1190         int i;
1191         float range;
1192         range = bound(0, (num - rangemin) / (rangemax - rangemin), 1);
1193         M_DrawCharacter (x-8, y, 128);
1194         for (i = 0;i < SLIDER_RANGE;i++)
1195                 M_DrawCharacter (x + i*8, y, 129);
1196         M_DrawCharacter (x+i*8, y, 130);
1197         M_DrawCharacter (x + (SLIDER_RANGE-1)*8 * range, y, 131);
1198         if (fabs((int)num - num) < 0.01)
1199                 sprintf(text, "%i", (int)num);
1200         else
1201                 sprintf(text, "%.2f", num);
1202         M_Print(x + (SLIDER_RANGE+2) * 8, y, text);
1203 }
1204
1205 void M_DrawCheckbox (int x, int y, int on)
1206 {
1207         if (on)
1208                 M_Print(x, y, "on");
1209         else
1210                 M_Print(x, y, "off");
1211 }
1212
1213
1214 #define OPTIONS_ITEMS 40
1215
1216 int options_cursor;
1217
1218 void M_Menu_Options_f (void)
1219 {
1220         key_dest = key_menu;
1221         m_state = m_options;
1222         m_entersound = true;
1223 }
1224
1225 extern cvar_t slowmo;
1226 extern dllhandle_t jpeg_dll;
1227 extern cvar_t gl_texture_anisotropy;
1228 extern cvar_t r_textshadow;
1229
1230 void M_Menu_Options_AdjustSliders (int dir)
1231 {
1232         int optnum;
1233         S_LocalSound ("misc/menu3.wav", true);
1234
1235         optnum = 7;
1236         if (options_cursor == optnum++)
1237                 Cvar_SetValueQuick (&vid_conwidth, bound(320, vid_conwidth.value + dir * 64, 2048));
1238         else if (options_cursor == optnum++)
1239                 Cvar_SetValueQuick (&vid_conheight, bound(240, vid_conheight.value + dir * 48, 1536));
1240         else if (options_cursor == optnum++)
1241                 Cvar_SetValueQuick (&scr_conspeed, bound(0, scr_conspeed.value + dir * 100, 1000));
1242         else if (options_cursor == optnum++)
1243                 Cvar_SetValueQuick (&scr_conalpha, bound(0, scr_conalpha.value + dir * 0.2, 1));
1244         else if (options_cursor == optnum++)
1245                 Cvar_SetValueQuick (&scr_conbrightness, bound(0, scr_conbrightness.value + dir * 0.2, 1));
1246         else if (options_cursor == optnum++)
1247                 Cvar_SetValueQuick (&sbar_alpha_bg, bound(0, sbar_alpha_bg.value + dir * 0.1, 1));
1248         else if (options_cursor == optnum++)
1249                 Cvar_SetValueQuick (&sbar_alpha_fg, bound(0, sbar_alpha_fg.value + dir * 0.1, 1));
1250         else if (options_cursor == optnum++)
1251                 Cvar_SetValueQuick (&scr_viewsize, bound(30, scr_viewsize.value + dir * 10, 120));
1252         else if (options_cursor == optnum++)
1253                 Cvar_SetValueQuick (&scr_fov, bound(1, scr_fov.integer + dir * 1, 170));
1254         else if (options_cursor == optnum++)
1255                 Cvar_SetValueQuick (&scr_screenshot_jpeg, !scr_screenshot_jpeg.integer);
1256         else if (options_cursor == optnum++)
1257                 Cvar_SetValueQuick (&scr_screenshot_jpeg_quality, bound(0, scr_screenshot_jpeg_quality.value + dir * 0.1, 1));
1258         else if (options_cursor == optnum++)
1259                 Cvar_SetValueQuick (&r_sky, !r_sky.integer);
1260         else if (options_cursor == optnum++)
1261                 Cvar_SetValueQuick (&gl_combine, !gl_combine.integer);
1262         else if (options_cursor == optnum++)
1263                 Cvar_SetValueQuick (&gl_dither, !gl_dither.integer);
1264         else if (options_cursor == optnum++)
1265                 Cvar_SetValueQuick (&gl_texture_anisotropy, bound(1, gl_texture_anisotropy.integer + dir, gl_max_anisotropy));
1266         else if (options_cursor == optnum++)
1267                 Cvar_SetValueQuick (&slowmo, bound(0, slowmo.value + dir * 0.25, 5));
1268         else if (options_cursor == optnum++)
1269                 Cvar_SetValueQuick (&bgmvolume, bound(0, bgmvolume.value + dir * 0.1, 1));
1270         else if (options_cursor == optnum++)
1271                 Cvar_SetValueQuick (&volume, bound(0, volume.value + dir * 0.1, 1));
1272         else if (options_cursor == optnum++)
1273                 Cvar_SetValueQuick (&snd_staticvolume, bound(0, snd_staticvolume.value + dir * 0.1, 1));
1274         else if (options_cursor == optnum++)
1275                 Cvar_SetValueQuick (&r_textshadow, !r_textshadow.integer);
1276         else if (options_cursor == optnum++)
1277                 Cvar_SetValueQuick (&crosshair, bound(0, crosshair.integer + dir, 5));
1278         else if (options_cursor == optnum++)
1279                 Cvar_SetValueQuick (&crosshair_size, bound(1, crosshair_size.value + dir, 5));
1280         else if (options_cursor == optnum++)
1281                 Cvar_SetValueQuick (&crosshair_static, !crosshair_static.integer);
1282         else if (options_cursor == optnum++)
1283                 Cvar_SetValueQuick (&showfps, !showfps.integer);
1284         else if (options_cursor == optnum++)
1285                 Cvar_SetValueQuick (&showtime, !showtime.integer);
1286         else if (options_cursor == optnum++)
1287                 Cvar_SetValueQuick (&showdate, !showdate.integer);
1288         else if (options_cursor == optnum++)
1289         {
1290                 if (cl_forwardspeed.value > 200)
1291                 {
1292                         Cvar_SetValueQuick (&cl_forwardspeed, 200);
1293                         Cvar_SetValueQuick (&cl_backspeed, 200);
1294                 }
1295                 else
1296                 {
1297                         Cvar_SetValueQuick (&cl_forwardspeed, 400);
1298                         Cvar_SetValueQuick (&cl_backspeed, 400);
1299                 }
1300         }
1301         else if (options_cursor == optnum++)
1302                 Cvar_SetValueQuick (&lookspring, !lookspring.integer);
1303         else if (options_cursor == optnum++)
1304                 Cvar_SetValueQuick (&lookstrafe, !lookstrafe.integer);
1305         else if (options_cursor == optnum++)
1306                 Cvar_SetValueQuick (&sensitivity, bound(1, sensitivity.value + dir * 0.5, 50));
1307         else if (options_cursor == optnum++)
1308                 Cvar_SetValueQuick (&freelook, !freelook.integer);
1309         else if (options_cursor == optnum++)
1310                 Cvar_SetValueQuick (&m_pitch, -m_pitch.value);
1311         else if (options_cursor == optnum++)
1312                 Cvar_SetValueQuick (&vid_mouse, !vid_mouse.integer);
1313 }
1314
1315 int optnum;
1316 int opty;
1317 int optcursor;
1318
1319 void M_Options_PrintCommand(char *s, int enabled)
1320 {
1321         if (opty >= 32)
1322         {
1323                 DrawQ_Fill(menu_x, menu_y + opty, 320, 8, optnum == optcursor ? (0.5 + 0.2 * sin(realtime * M_PI)) : 0, 0, 0, 0.5, 0);
1324                 M_ItemPrint(0, opty, s, enabled);
1325         }
1326         opty += 8;
1327         optnum++;
1328 }
1329
1330 void M_Options_PrintCheckbox(char *s, int enabled, int yes)
1331 {
1332         if (opty >= 32)
1333         {
1334                 DrawQ_Fill(menu_x, menu_y + opty, 320, 8, optnum == optcursor ? (0.5 + 0.2 * sin(realtime * M_PI)) : 0, 0, 0, 0.5, 0);
1335                 M_ItemPrint(0, opty, s, enabled);
1336                 M_DrawCheckbox(0 + strlen(s) * 8 + 8, opty, yes);
1337         }
1338         opty += 8;
1339         optnum++;
1340 }
1341
1342 void M_Options_PrintSlider(char *s, int enabled, float value, float minvalue, float maxvalue)
1343 {
1344         if (opty >= 32)
1345         {
1346                 DrawQ_Fill(menu_x, menu_y + opty, 320, 8, optnum == optcursor ? (0.5 + 0.2 * sin(realtime * M_PI)) : 0, 0, 0, 0.5, 0);
1347                 M_ItemPrint(0, opty, s, enabled);
1348                 M_DrawSlider(0 + strlen(s) * 8 + 8, opty, value, minvalue, maxvalue);
1349         }
1350         opty += 8;
1351         optnum++;
1352 }
1353
1354 void M_Options_Draw (void)
1355 {
1356         int visible;
1357         cachepic_t      *p;
1358
1359         M_Background(320, 240);
1360
1361         M_DrawPic(16, 4, "gfx/qplaque.lmp");
1362         p = Draw_CachePic("gfx/p_option.lmp");
1363         M_DrawPic((320-p->width)/2, 4, "gfx/p_option.lmp");
1364
1365         optnum = 0;
1366         optcursor = options_cursor;
1367         visible = (vid.conheight - 32) / 8;
1368         opty = 32 - bound(0, optcursor - (visible >> 1), max(0, OPTIONS_ITEMS - visible)) * 8;
1369
1370         M_Options_PrintCommand( "Customize controls", true);
1371         M_Options_PrintCommand( "     Go to console", true);
1372         M_Options_PrintCommand( " Reset to defaults", true);
1373         M_Options_PrintCommand( "             Video", true);
1374         M_Options_PrintCommand( "           Effects", true);
1375         M_Options_PrintCommand( "          Graphics", true);
1376         M_Options_PrintCommand( "     Color Control", true);
1377         M_Options_PrintSlider(  "  2D Screen Width ", true, vid_conwidth.value, 320, 2048);
1378         M_Options_PrintSlider(  "  2D Screen Height", true, vid_conheight.value, 240, 1536);
1379         M_Options_PrintSlider(  "     Console Speed", true, scr_conspeed.value, 0, 1000);
1380         M_Options_PrintSlider(  "     Console Alpha", true, scr_conalpha.value, 0, 1);
1381         M_Options_PrintSlider(  "Conback Brightness", true, scr_conbrightness.value, 0, 1);
1382         M_Options_PrintSlider(  "     Sbar Alpha BG", true, sbar_alpha_bg.value, 0, 1);
1383         M_Options_PrintSlider(  "     Sbar Alpha FG", true, sbar_alpha_fg.value, 0, 1);
1384         M_Options_PrintSlider(  "       Screen size", true, scr_viewsize.value, 30, 120);
1385         M_Options_PrintSlider(  "     Field of View", true, scr_fov.integer, 1, 170);
1386         M_Options_PrintCheckbox("  JPEG screenshots", jpeg_dll != NULL, scr_screenshot_jpeg.integer);
1387         M_Options_PrintSlider(  "      JPEG quality", jpeg_dll != NULL, scr_screenshot_jpeg_quality.value, 0, 1);
1388         M_Options_PrintCheckbox("               Sky", true, r_sky.integer);
1389         M_Options_PrintCheckbox("   Texture Combine", true, gl_combine.integer);
1390         M_Options_PrintCheckbox("         Dithering", true, gl_dither.integer);
1391         M_Options_PrintSlider(  "Anisotropic Filter", gl_support_anisotropy, gl_texture_anisotropy.integer, 1, gl_max_anisotropy);
1392         M_Options_PrintSlider(  "        Game Speed", sv.active, slowmo.value, 0, 5);
1393         M_Options_PrintSlider(  "   CD Music Volume", cdaudioinitialized.integer, bgmvolume.value, 0, 1);
1394         M_Options_PrintSlider(  "      Sound Volume", snd_initialized.integer, volume.value, 0, 1);
1395         M_Options_PrintSlider(gamemode == GAME_GOODVSBAD2 ? "      Music Volume" : "    Ambient Volume", snd_initialized.integer, snd_staticvolume.value, 0, 1);
1396         M_Options_PrintCheckbox("       Text Shadow", true, r_textshadow.integer);
1397         M_Options_PrintSlider(  "         Crosshair", true, crosshair.value, 0, 5);
1398         M_Options_PrintSlider(  "    Crosshair Size", true, crosshair_size.value, 1, 5);
1399         M_Options_PrintCheckbox("  Static Crosshair", true, crosshair_static.integer);
1400         M_Options_PrintCheckbox("    Show Framerate", true, showfps.integer);
1401         M_Options_PrintCheckbox("         Show Time", true, showtime.integer);
1402         M_Options_PrintCheckbox("         Show Date", true, showdate.integer);
1403         M_Options_PrintCheckbox("        Always Run", true, cl_forwardspeed.value > 200);
1404         M_Options_PrintCheckbox("        Lookspring", true, lookspring.integer);
1405         M_Options_PrintCheckbox("        Lookstrafe", true, lookstrafe.integer);
1406         M_Options_PrintSlider(  "       Mouse Speed", true, sensitivity.value, 1, 50);
1407         M_Options_PrintCheckbox("        Mouse Look", true, freelook.integer);
1408         M_Options_PrintCheckbox("      Invert Mouse", true, m_pitch.value < 0);
1409         M_Options_PrintCheckbox("         Use Mouse", true, vid_mouse.integer);
1410 }
1411
1412
1413 void M_Options_Key (int k, char ascii)
1414 {
1415         switch (k)
1416         {
1417         case K_ESCAPE:
1418                 M_Menu_Main_f ();
1419                 break;
1420
1421         case K_ENTER:
1422                 m_entersound = true;
1423                 switch (options_cursor)
1424                 {
1425                 case 0:
1426                         M_Menu_Keys_f ();
1427                         break;
1428                 case 1:
1429                         m_state = m_none;
1430                         key_dest = key_game;
1431                         Con_ToggleConsole_f ();
1432                         break;
1433                 case 2:
1434                         M_Menu_Reset_f ();
1435                         break;
1436                 case 3:
1437                         M_Menu_Video_f ();
1438                         break;
1439                 case 4:
1440                         M_Menu_Options_Effects_f ();
1441                         break;
1442                 case 5:
1443                         M_Menu_Options_Graphics_f ();
1444                         break;
1445                 case 6:
1446                         M_Menu_Options_ColorControl_f ();
1447                         break;
1448                 default:
1449                         M_Menu_Options_AdjustSliders (1);
1450                         break;
1451                 }
1452                 return;
1453
1454         case K_UPARROW:
1455                 S_LocalSound ("misc/menu1.wav", true);
1456                 options_cursor--;
1457                 if (options_cursor < 0)
1458                         options_cursor = OPTIONS_ITEMS-1;
1459                 break;
1460
1461         case K_DOWNARROW:
1462                 S_LocalSound ("misc/menu1.wav", true);
1463                 options_cursor++;
1464                 if (options_cursor >= OPTIONS_ITEMS)
1465                         options_cursor = 0;
1466                 break;
1467
1468         case K_LEFTARROW:
1469                 M_Menu_Options_AdjustSliders (-1);
1470                 break;
1471
1472         case K_RIGHTARROW:
1473                 M_Menu_Options_AdjustSliders (1);
1474                 break;
1475         }
1476 }
1477
1478 #define OPTIONS_EFFECTS_ITEMS   37
1479
1480 int options_effects_cursor;
1481
1482 void M_Menu_Options_Effects_f (void)
1483 {
1484         key_dest = key_menu;
1485         m_state = m_options_effects;
1486         m_entersound = true;
1487 }
1488
1489
1490 extern cvar_t r_detailtextures;
1491 extern cvar_t cl_particles;
1492 extern cvar_t cl_stainmaps;
1493 extern cvar_t cl_stainmapsclearonload;
1494 extern cvar_t cl_decals;
1495 extern cvar_t r_explosionclip;
1496 extern cvar_t r_modellights;
1497 extern cvar_t r_coronas;
1498 extern cvar_t gl_flashblend;
1499 extern cvar_t cl_particles_quality;
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_alpha;
1506 extern cvar_t cl_particles_blood_bloodhack;
1507 extern cvar_t cl_particles_explosions_shell;
1508 extern cvar_t cl_beams_polygon;
1509 extern cvar_t cl_beams_relative;
1510 extern cvar_t cl_beams_lightatend;
1511 extern cvar_t r_lightningbeam_thickness;
1512 extern cvar_t r_lightningbeam_scroll;
1513 extern cvar_t r_lightningbeam_repeatdistance;
1514 extern cvar_t r_lightningbeam_color_red;
1515 extern cvar_t r_lightningbeam_color_green;
1516 extern cvar_t r_lightningbeam_color_blue;
1517 extern cvar_t r_lightningbeam_qmbtexture;
1518
1519 void M_Menu_Options_Effects_AdjustSliders (int dir)
1520 {
1521         int optnum;
1522         S_LocalSound ("misc/menu3.wav", true);
1523
1524         optnum = 0;
1525              if (options_effects_cursor == optnum++) Cvar_SetValueQuick (&r_modellights, bound(0, r_modellights.value + dir, 8));
1526         else if (options_effects_cursor == optnum++) Cvar_SetValueQuick (&r_coronas, bound(0, r_coronas.value + dir * 0.125, 4));
1527         else if (options_effects_cursor == optnum++) Cvar_SetValueQuick (&gl_flashblend, !gl_flashblend.integer);
1528         else if (options_effects_cursor == optnum++) Cvar_SetValueQuick (&cl_particles, !cl_particles.integer);
1529         else if (options_effects_cursor == optnum++) Cvar_SetValueQuick (&cl_particles_quality, bound(1, cl_particles_quality.value + dir * 0.5, 4));
1530         else if (options_effects_cursor == optnum++) Cvar_SetValueQuick (&cl_particles_explosions_shell, !cl_particles_explosions_shell.integer);
1531         else if (options_effects_cursor == optnum++) Cvar_SetValueQuick (&r_explosionclip, !r_explosionclip.integer);
1532         else if (options_effects_cursor == optnum++) Cvar_SetValueQuick (&cl_stainmaps, !cl_stainmaps.integer);
1533         else if (options_effects_cursor == optnum++) Cvar_SetValueQuick (&cl_stainmapsclearonload, !cl_stainmapsclearonload.integer);
1534         else if (options_effects_cursor == optnum++) Cvar_SetValueQuick (&cl_decals, !cl_decals.integer);
1535         else if (options_effects_cursor == optnum++) Cvar_SetValueQuick (&r_detailtextures, !r_detailtextures.integer);
1536         else if (options_effects_cursor == optnum++) Cvar_SetValueQuick (&cl_particles_bulletimpacts, !cl_particles_bulletimpacts.integer);
1537         else if (options_effects_cursor == optnum++) Cvar_SetValueQuick (&cl_particles_smoke, !cl_particles_smoke.integer);
1538         else if (options_effects_cursor == optnum++) Cvar_SetValueQuick (&cl_particles_sparks, !cl_particles_sparks.integer);
1539         else if (options_effects_cursor == optnum++) Cvar_SetValueQuick (&cl_particles_bubbles, !cl_particles_bubbles.integer);
1540         else if (options_effects_cursor == optnum++) Cvar_SetValueQuick (&cl_particles_blood, !cl_particles_blood.integer);
1541         else if (options_effects_cursor == optnum++) Cvar_SetValueQuick (&cl_particles_blood_alpha, bound(0.2, cl_particles_blood_alpha.value + dir * 0.1, 1));
1542         else if (options_effects_cursor == optnum++) Cvar_SetValueQuick (&cl_particles_blood_bloodhack, !cl_particles_blood_bloodhack.integer);
1543         else if (options_effects_cursor == optnum++) Cvar_SetValueQuick (&cl_beams_polygons, !cl_beams_polygons.integer);
1544         else if (options_effects_cursor == optnum++) Cvar_SetValueQuick (&cl_beams_relative, !cl_beams_relative.integer);
1545         else if (options_effects_cursor == optnum++) Cvar_SetValueQuick (&cl_beams_lightatend, !cl_beams_lightatend.integer);
1546         else if (options_effects_cursor == optnum++) Cvar_SetValueQuick (&r_lightningbeam_thickness, bound(1, r_lightningbeam_thickness.integer + dir, 10));
1547         else if (options_effects_cursor == optnum++) Cvar_SetValueQuick (&r_lightningbeam_scroll, bound(0, r_lightningbeam_scroll.integer + dir, 10));
1548         else if (options_effects_cursor == optnum++) Cvar_SetValueQuick (&r_lightningbeam_repeatdistance, bound(64, r_lightningbeam_repeatdistance.integer + dir * 64, 1024));
1549         else if (options_effects_cursor == optnum++) Cvar_SetValueQuick (&r_lightningbeam_color_red, bound(0, r_lightningbeam_color_red.value + dir * 0.1, 1));
1550         else if (options_effects_cursor == optnum++) Cvar_SetValueQuick (&r_lightningbeam_color_green, bound(0, r_lightningbeam_color_green.value + dir * 0.1, 1));
1551         else if (options_effects_cursor == optnum++) Cvar_SetValueQuick (&r_lightningbeam_color_blue, bound(0, r_lightningbeam_color_blue.value + dir * 0.1, 1));
1552         else if (options_effects_cursor == optnum++) Cvar_SetValueQuick (&r_lightningbeam_qmbtexture, !r_lightningbeam_qmbtexture.integer);
1553         else if (options_effects_cursor == optnum++) Cvar_SetValueQuick (&r_lerpmodels, !r_lerpmodels.integer);
1554         else if (options_effects_cursor == optnum++) Cvar_SetValueQuick (&r_lerpsprites, !r_lerpsprites.integer);
1555         else if (options_effects_cursor == optnum++) Cvar_SetValueQuick (&gl_polyblend, bound(0, gl_polyblend.value + dir * 0.1, 1));
1556         else if (options_effects_cursor == optnum++) Cvar_SetValueQuick (&r_skyscroll1, bound(-8, r_skyscroll1.value + dir * 0.1, 8));
1557         else if (options_effects_cursor == optnum++) Cvar_SetValueQuick (&r_skyscroll2, bound(-8, r_skyscroll2.value + dir * 0.1, 8));
1558         else if (options_effects_cursor == optnum++) Cvar_SetValueQuick (&r_waterwarp, bound(0, r_waterwarp.value + dir * 0.1, 1));
1559         else if (options_effects_cursor == optnum++) Cvar_SetValueQuick (&r_wateralpha, bound(0, r_wateralpha.value + dir * 0.1, 1));
1560         else if (options_effects_cursor == optnum++) Cvar_SetValueQuick (&r_waterscroll, bound(0, r_waterscroll.value + dir * 0.5, 10));
1561         else if (options_effects_cursor == optnum++) Cvar_SetValueQuick (&r_watershader, bound(0, r_watershader.value + dir * 0.25, 10));
1562 }
1563
1564 void M_Options_Effects_Draw (void)
1565 {
1566         int visible;
1567         cachepic_t      *p;
1568
1569         M_Background(320, 200);
1570
1571         M_DrawPic(16, 4, "gfx/qplaque.lmp");
1572         p = Draw_CachePic("gfx/p_option.lmp");
1573         M_DrawPic((320-p->width)/2, 4, "gfx/p_option.lmp");
1574
1575         optcursor = options_effects_cursor;
1576         optnum = 0;
1577         visible = (vid.conheight - 32) / 8;
1578         opty = 32 - bound(0, optcursor - (visible >> 1), max(0, OPTIONS_EFFECTS_ITEMS - visible)) * 8;
1579
1580         M_Options_PrintSlider(  "      Lights Per Model", true, r_modellights.value, 0, 8);
1581         M_Options_PrintSlider(  "      Corona Intensity", true, r_coronas.value, 0, 4);
1582         M_Options_PrintCheckbox("      Use Only Coronas", true, gl_flashblend.integer);
1583         M_Options_PrintCheckbox("             Particles", true, cl_particles.integer);
1584         M_Options_PrintSlider(  "     Particles Quality", true, cl_particles_quality.value, 1, 4);
1585         M_Options_PrintCheckbox("       Explosion Shell", true, cl_particles_explosions_shell.integer);
1586         M_Options_PrintCheckbox("  Explosion Shell Clip", true, r_explosionclip.integer);
1587         M_Options_PrintCheckbox("             Stainmaps", true, cl_stainmaps.integer);
1588         M_Options_PrintCheckbox("Onload Clear Stainmaps", true, cl_stainmapsclearonload.integer);
1589         M_Options_PrintCheckbox("                Decals", true, cl_decals.integer);
1590         M_Options_PrintCheckbox("      Detail Texturing", true, r_detailtextures.integer);
1591         M_Options_PrintCheckbox("        Bullet Impacts", true, cl_particles_bulletimpacts.integer);
1592         M_Options_PrintCheckbox("                 Smoke", true, cl_particles_smoke.integer);
1593         M_Options_PrintCheckbox("                Sparks", true, cl_particles_sparks.integer);
1594         M_Options_PrintCheckbox("               Bubbles", true, cl_particles_bubbles.integer);
1595         M_Options_PrintCheckbox("                 Blood", true, cl_particles_blood.integer);
1596         M_Options_PrintSlider(  "         Blood Opacity", true, cl_particles_blood_alpha.value, 0.2, 1);
1597         M_Options_PrintCheckbox("Force New Blood Effect", true, cl_particles_blood_bloodhack.integer);
1598         M_Options_PrintCheckbox("    Lightning Polygons", true, cl_beams_polygons.integer);
1599         M_Options_PrintCheckbox("Lightning Smooth Sweep", true, cl_beams_relative.integer);
1600         M_Options_PrintCheckbox("   Lightning End Light", true, cl_beams_lightatend.integer);
1601         M_Options_PrintSlider(  "   Lightning Thickness", cl_beams_polygons.integer, r_lightningbeam_thickness.integer, 1, 10);
1602         M_Options_PrintSlider(  "      Lightning Scroll", cl_beams_polygons.integer, r_lightningbeam_scroll.integer, 0, 10);
1603         M_Options_PrintSlider(  " Lightning Repeat Dist", cl_beams_polygons.integer, r_lightningbeam_repeatdistance.integer, 64, 1024);
1604         M_Options_PrintSlider(  "   Lightning Color Red", cl_beams_polygons.integer, r_lightningbeam_color_red.value, 0, 1);
1605         M_Options_PrintSlider(  " Lightning Color Green", cl_beams_polygons.integer, r_lightningbeam_color_green.value, 0, 1);
1606         M_Options_PrintSlider(  "  Lightning Color Blue", cl_beams_polygons.integer, r_lightningbeam_color_blue.value, 0, 1);
1607         M_Options_PrintCheckbox(" Lightning QMB Texture", cl_beams_polygons.integer, r_lightningbeam_qmbtexture.integer);
1608         M_Options_PrintCheckbox("   Model Interpolation", true, r_lerpmodels.integer);
1609         M_Options_PrintCheckbox("  Sprite Interpolation", true, r_lerpsprites.integer);
1610         M_Options_PrintSlider(  "            View Blend", true, gl_polyblend.value, 0, 1);
1611         M_Options_PrintSlider(  "Upper Sky Scroll Speed", true, r_skyscroll1.value, -8, 8);
1612         M_Options_PrintSlider(  "Lower Sky Scroll Speed", true, r_skyscroll2.value, -8, 8);
1613         M_Options_PrintSlider(  "  Underwater View Warp", true, r_waterwarp.value, 0, 1);
1614         M_Options_PrintSlider(  " Water Alpha (opacity)", true, r_wateralpha.value, 0, 1);
1615         M_Options_PrintSlider(  "        Water Movement", true, r_waterscroll.value, 0, 10);
1616         M_Options_PrintSlider(  " GeForce3 Water Shader", true, r_watershader.value, 0, 10);
1617 }
1618
1619
1620 void M_Options_Effects_Key (int k, char ascii)
1621 {
1622         switch (k)
1623         {
1624         case K_ESCAPE:
1625                 M_Menu_Options_f ();
1626                 break;
1627
1628         case K_ENTER:
1629                 M_Menu_Options_Effects_AdjustSliders (1);
1630                 break;
1631
1632         case K_UPARROW:
1633                 S_LocalSound ("misc/menu1.wav", true);
1634                 options_effects_cursor--;
1635                 if (options_effects_cursor < 0)
1636                         options_effects_cursor = OPTIONS_EFFECTS_ITEMS-1;
1637                 break;
1638
1639         case K_DOWNARROW:
1640                 S_LocalSound ("misc/menu1.wav", true);
1641                 options_effects_cursor++;
1642                 if (options_effects_cursor >= OPTIONS_EFFECTS_ITEMS)
1643                         options_effects_cursor = 0;
1644                 break;
1645
1646         case K_LEFTARROW:
1647                 M_Menu_Options_Effects_AdjustSliders (-1);
1648                 break;
1649
1650         case K_RIGHTARROW:
1651                 M_Menu_Options_Effects_AdjustSliders (1);
1652                 break;
1653         }
1654 }
1655
1656
1657 #define OPTIONS_GRAPHICS_ITEMS  7
1658
1659 int options_graphics_cursor;
1660
1661 void M_Menu_Options_Graphics_f (void)
1662 {
1663         key_dest = key_menu;
1664         m_state = m_options_graphics;
1665         m_entersound = true;
1666 }
1667
1668 extern cvar_t r_shadow_gloss;
1669 extern cvar_t r_shadow_realtime_dlight;
1670 extern cvar_t r_shadow_realtime_dlight_shadows;
1671 extern cvar_t r_shadow_realtime_world;
1672 extern cvar_t r_shadow_realtime_world_dlightshadows;
1673 extern cvar_t r_shadow_realtime_world_lightmaps;
1674 extern cvar_t r_shadow_realtime_world_shadows;
1675
1676 void M_Menu_Options_Graphics_AdjustSliders (int dir)
1677 {
1678         int optnum;
1679         S_LocalSound ("misc/menu3.wav", true);
1680  
1681         optnum = 0;
1682
1683                  if (options_graphics_cursor == optnum++) Cvar_SetValueQuick (&r_shadow_gloss,                                                  bound(0, r_shadow_gloss.integer + dir, 2));
1684         else if (options_graphics_cursor == optnum++) Cvar_SetValueQuick (&r_shadow_realtime_dlight,                            !r_shadow_realtime_dlight.integer);
1685         else if (options_graphics_cursor == optnum++) Cvar_SetValueQuick (&r_shadow_realtime_dlight_shadows,            !r_shadow_realtime_dlight_shadows.integer);
1686         else if (options_graphics_cursor == optnum++) Cvar_SetValueQuick (&r_shadow_realtime_world,                                     !r_shadow_realtime_world.integer);
1687         else if (options_graphics_cursor == optnum++) Cvar_SetValueQuick (&r_shadow_realtime_world_dlightshadows,       !r_shadow_realtime_world_dlightshadows.integer);
1688         else if (options_graphics_cursor == optnum++) Cvar_SetValueQuick (&r_shadow_realtime_world_lightmaps,           bound(0, r_shadow_realtime_world_lightmaps.value + dir * 0.1, 1));
1689         else if (options_graphics_cursor == optnum++) Cvar_SetValueQuick (&r_shadow_realtime_world_shadows,                     !r_shadow_realtime_world_shadows.integer);
1690 }
1691
1692
1693 void M_Options_Graphics_Draw (void)
1694 {
1695         int visible;
1696         cachepic_t      *p;
1697
1698         M_Background(320, 200);
1699
1700         M_DrawPic(16, 4, "gfx/qplaque.lmp");
1701         p = Draw_CachePic("gfx/p_option.lmp");
1702         M_DrawPic((320-p->width)/2, 4, "gfx/p_option.lmp");
1703
1704         optcursor = options_graphics_cursor;
1705         optnum = 0;
1706         visible = (vid.conheight - 32) / 8;
1707         opty = 32 - bound(0, optcursor - (visible >> 1), max(0, OPTIONS_GRAPHICS_ITEMS - visible)) * 8;
1708
1709         M_Options_PrintSlider(  "             Gloss Mode", true, r_shadow_gloss.integer, 0, 2);
1710         M_Options_PrintCheckbox("             RT DLights", true, r_shadow_realtime_dlight.integer);
1711         M_Options_PrintCheckbox("      RT DLight Shadows", true, r_shadow_realtime_dlight_shadows.integer);
1712         M_Options_PrintCheckbox("               RT World", true, r_shadow_realtime_world.integer);
1713         M_Options_PrintCheckbox("RT World DLight Shadows", true, r_shadow_realtime_world_dlightshadows.integer);
1714         M_Options_PrintSlider(  "     RT World Lightmaps", true, r_shadow_realtime_world_lightmaps.value, 0, 1);
1715         M_Options_PrintCheckbox("        RT World Shadow", true, r_shadow_realtime_world_shadows.integer);
1716 }
1717
1718
1719 void M_Options_Graphics_Key (int k, char ascii)
1720 {
1721         switch (k)
1722         {
1723         case K_ESCAPE:
1724                 M_Menu_Options_f ();
1725                 break;
1726
1727         case K_ENTER:
1728                 M_Menu_Options_Graphics_AdjustSliders (1);
1729                 break;
1730
1731         case K_UPARROW:
1732                 S_LocalSound ("misc/menu1.wav", true);
1733                 options_graphics_cursor--;
1734                 if (options_graphics_cursor < 0)
1735                         options_graphics_cursor = OPTIONS_GRAPHICS_ITEMS-1;
1736                 break;
1737
1738         case K_DOWNARROW:
1739                 S_LocalSound ("misc/menu1.wav", true);
1740                 options_graphics_cursor++;
1741                 if (options_graphics_cursor >= OPTIONS_GRAPHICS_ITEMS)
1742                         options_graphics_cursor = 0;
1743                 break;
1744
1745         case K_LEFTARROW:
1746                 M_Menu_Options_Graphics_AdjustSliders (-1);
1747                 break;
1748
1749         case K_RIGHTARROW:
1750                 M_Menu_Options_Graphics_AdjustSliders (1);
1751                 break;
1752         }
1753 }
1754
1755
1756 #define OPTIONS_COLORCONTROL_ITEMS      18
1757
1758 int             options_colorcontrol_cursor;
1759
1760 // intensity value to match up to 50% dither to 'correct' quake
1761 cvar_t menu_options_colorcontrol_correctionvalue = {0, "menu_options_colorcontrol_correctionvalue", "0.25"};
1762
1763 void M_Menu_Options_ColorControl_f (void)
1764 {
1765         key_dest = key_menu;
1766         m_state = m_options_colorcontrol;
1767         m_entersound = true;
1768 }
1769
1770
1771 void M_Menu_Options_ColorControl_AdjustSliders (int dir)
1772 {
1773         int optnum;
1774         float f;
1775         S_LocalSound ("misc/menu3.wav", true);
1776
1777         optnum = 1;
1778         if (options_colorcontrol_cursor == optnum++)
1779                 Cvar_SetValueQuick (&v_hwgamma, !v_hwgamma.integer);
1780         else if (options_colorcontrol_cursor == optnum++)
1781         {
1782                 Cvar_SetValueQuick (&v_color_enable, 0);
1783                 Cvar_SetValueQuick (&v_gamma, bound(1, v_gamma.value + dir * 0.125, 5));
1784         }
1785         else if (options_colorcontrol_cursor == optnum++)
1786         {
1787                 Cvar_SetValueQuick (&v_color_enable, 0);
1788                 Cvar_SetValueQuick (&v_contrast, bound(1, v_contrast.value + dir * 0.125, 5));
1789         }
1790         else if (options_colorcontrol_cursor == optnum++)
1791         {
1792                 Cvar_SetValueQuick (&v_color_enable, 0);
1793                 Cvar_SetValueQuick (&v_brightness, bound(0, v_brightness.value + dir * 0.05, 0.8));
1794         }
1795         else if (options_colorcontrol_cursor == optnum++)
1796         {
1797                 Cvar_SetValueQuick (&v_color_enable, !v_color_enable.integer);
1798         }
1799         else if (options_colorcontrol_cursor == optnum++)
1800         {
1801                 Cvar_SetValueQuick (&v_color_enable, 1);
1802                 Cvar_SetValueQuick (&v_color_black_r, bound(0, v_color_black_r.value + dir * 0.0125, 0.8));
1803         }
1804         else if (options_colorcontrol_cursor == optnum++)
1805         {
1806                 Cvar_SetValueQuick (&v_color_enable, 1);
1807                 Cvar_SetValueQuick (&v_color_black_g, bound(0, v_color_black_g.value + dir * 0.0125, 0.8));
1808         }
1809         else if (options_colorcontrol_cursor == optnum++)
1810         {
1811                 Cvar_SetValueQuick (&v_color_enable, 1);
1812                 Cvar_SetValueQuick (&v_color_black_b, bound(0, v_color_black_b.value + dir * 0.0125, 0.8));
1813         }
1814         else if (options_colorcontrol_cursor == optnum++)
1815         {
1816                 Cvar_SetValueQuick (&v_color_enable, 1);
1817                 f = bound(0, (v_color_black_r.value + v_color_black_g.value + v_color_black_b.value) / 3 + dir * 0.0125, 0.8);
1818                 Cvar_SetValueQuick (&v_color_black_r, f);
1819                 Cvar_SetValueQuick (&v_color_black_g, f);
1820                 Cvar_SetValueQuick (&v_color_black_b, f);
1821         }
1822         else if (options_colorcontrol_cursor == optnum++)
1823         {
1824                 Cvar_SetValueQuick (&v_color_enable, 1);
1825                 Cvar_SetValueQuick (&v_color_grey_r, bound(0, v_color_grey_r.value + dir * 0.0125, 0.95));
1826         }
1827         else if (options_colorcontrol_cursor == optnum++)
1828         {
1829                 Cvar_SetValueQuick (&v_color_enable, 1);
1830                 Cvar_SetValueQuick (&v_color_grey_g, bound(0, v_color_grey_g.value + dir * 0.0125, 0.95));
1831         }
1832         else if (options_colorcontrol_cursor == optnum++)
1833         {
1834                 Cvar_SetValueQuick (&v_color_enable, 1);
1835                 Cvar_SetValueQuick (&v_color_grey_b, bound(0, v_color_grey_b.value + dir * 0.0125, 0.95));
1836         }
1837         else if (options_colorcontrol_cursor == optnum++)
1838         {
1839                 Cvar_SetValueQuick (&v_color_enable, 1);
1840                 f = bound(0, (v_color_grey_r.value + v_color_grey_g.value + v_color_grey_b.value) / 3 + dir * 0.0125, 0.95);
1841                 Cvar_SetValueQuick (&v_color_grey_r, f);
1842                 Cvar_SetValueQuick (&v_color_grey_g, f);
1843                 Cvar_SetValueQuick (&v_color_grey_b, f);
1844         }
1845         else if (options_colorcontrol_cursor == optnum++)
1846         {
1847                 Cvar_SetValueQuick (&v_color_enable, 1);
1848                 Cvar_SetValueQuick (&v_color_white_r, bound(1, v_color_white_r.value + dir * 0.125, 5));
1849         }
1850         else if (options_colorcontrol_cursor == optnum++)
1851         {
1852                 Cvar_SetValueQuick (&v_color_enable, 1);
1853                 Cvar_SetValueQuick (&v_color_white_g, bound(1, v_color_white_g.value + dir * 0.125, 5));
1854         }
1855         else if (options_colorcontrol_cursor == optnum++)
1856         {
1857                 Cvar_SetValueQuick (&v_color_enable, 1);
1858                 Cvar_SetValueQuick (&v_color_white_b, bound(1, v_color_white_b.value + dir * 0.125, 5));
1859         }
1860         else if (options_colorcontrol_cursor == optnum++)
1861         {
1862                 Cvar_SetValueQuick (&v_color_enable, 1);
1863                 f = bound(1, (v_color_white_r.value + v_color_white_g.value + v_color_white_b.value) / 3 + dir * 0.125, 5);
1864                 Cvar_SetValueQuick (&v_color_white_r, f);
1865                 Cvar_SetValueQuick (&v_color_white_g, f);
1866                 Cvar_SetValueQuick (&v_color_white_b, f);
1867         }
1868 }
1869
1870 void M_Options_ColorControl_Draw (void)
1871 {
1872         int visible;
1873         float x, c, s, t, u, v;
1874         cachepic_t      *p;
1875
1876         M_Background(320, 256);
1877
1878         M_DrawPic(16, 4, "gfx/qplaque.lmp");
1879         p = Draw_CachePic("gfx/p_option.lmp");
1880         M_DrawPic((320-p->width)/2, 4, "gfx/p_option.lmp");
1881
1882         optcursor = options_colorcontrol_cursor;
1883         optnum = 0;
1884         visible = (vid.conheight - 32) / 8;
1885         opty = 32 - bound(0, optcursor - (visible >> 1), max(0, OPTIONS_COLORCONTROL_ITEMS - visible)) * 8;
1886
1887         M_Options_PrintCommand( "     Reset to defaults", true);
1888         M_Options_PrintCheckbox("Hardware Gamma Control", vid_hardwaregammasupported.integer, v_hwgamma.integer);
1889         M_Options_PrintSlider(  "                 Gamma", !v_color_enable.integer && vid_hardwaregammasupported.integer && v_hwgamma.integer, v_gamma.value, 1, 5);
1890         M_Options_PrintSlider(  "              Contrast", !v_color_enable.integer, v_contrast.value, 1, 5);
1891         M_Options_PrintSlider(  "            Brightness", !v_color_enable.integer, v_brightness.value, 0, 0.8);
1892         M_Options_PrintCheckbox("  Color Level Controls", true, v_color_enable.integer);
1893         M_Options_PrintSlider(  "          Black: Red  ", v_color_enable.integer, v_color_black_r.value, 0, 0.8);
1894         M_Options_PrintSlider(  "          Black: Green", v_color_enable.integer, v_color_black_g.value, 0, 0.8);
1895         M_Options_PrintSlider(  "          Black: Blue ", v_color_enable.integer, v_color_black_b.value, 0, 0.8);
1896         M_Options_PrintSlider(  "          Black: Grey ", v_color_enable.integer, (v_color_black_r.value + v_color_black_g.value + v_color_black_b.value) / 3, 0, 0.8);
1897         M_Options_PrintSlider(  "           Grey: Red  ", v_color_enable.integer && vid_hardwaregammasupported.integer && v_hwgamma.integer, v_color_grey_r.value, 0, 0.95);
1898         M_Options_PrintSlider(  "           Grey: Green", v_color_enable.integer && vid_hardwaregammasupported.integer && v_hwgamma.integer, v_color_grey_g.value, 0, 0.95);
1899         M_Options_PrintSlider(  "           Grey: Blue ", v_color_enable.integer && vid_hardwaregammasupported.integer && v_hwgamma.integer, v_color_grey_b.value, 0, 0.95);
1900         M_Options_PrintSlider(  "           Grey: Grey ", v_color_enable.integer && vid_hardwaregammasupported.integer && v_hwgamma.integer, (v_color_grey_r.value + v_color_grey_g.value + v_color_grey_b.value) / 3, 0, 0.95);
1901         M_Options_PrintSlider(  "          White: Red  ", v_color_enable.integer, v_color_white_r.value, 1, 5);
1902         M_Options_PrintSlider(  "          White: Green", v_color_enable.integer, v_color_white_g.value, 1, 5);
1903         M_Options_PrintSlider(  "          White: Blue ", v_color_enable.integer, v_color_white_b.value, 1, 5);
1904         M_Options_PrintSlider(  "          White: Grey ", v_color_enable.integer, (v_color_white_r.value + v_color_white_g.value + v_color_white_b.value) / 3, 1, 5);
1905
1906         opty += 4;
1907         DrawQ_Fill(menu_x, menu_y + opty, 320, 4 + 64 + 8 + 64 + 4, 0, 0, 0, 1, 0);opty += 4;
1908         s = (float) 312 / 2 * vid.realwidth / vid.conwidth;
1909         t = (float) 4 / 2 * vid.realheight / vid.conheight;
1910         DrawQ_SuperPic(menu_x + 4, menu_y + opty, "gfx/colorcontrol/ditherpattern.tga", 312, 4, 0,0, 1,0,0,1, s,0, 1,0,0,1, 0,t, 1,0,0,1, s,t, 1,0,0,1, 0);opty += 4;
1911         DrawQ_SuperPic(menu_x + 4, menu_y + opty, NULL                                , 312, 4, 0,0, 0,0,0,1, 1,0, 1,0,0,1, 0,1, 0,0,0,1, 1,1, 1,0,0,1, 0);opty += 4;
1912         DrawQ_SuperPic(menu_x + 4, menu_y + opty, "gfx/colorcontrol/ditherpattern.tga", 312, 4, 0,0, 0,1,0,1, s,0, 0,1,0,1, 0,t, 0,1,0,1, s,t, 0,1,0,1, 0);opty += 4;
1913         DrawQ_SuperPic(menu_x + 4, menu_y + opty, NULL                                , 312, 4, 0,0, 0,0,0,1, 1,0, 0,1,0,1, 0,1, 0,0,0,1, 1,1, 0,1,0,1, 0);opty += 4;
1914         DrawQ_SuperPic(menu_x + 4, menu_y + opty, "gfx/colorcontrol/ditherpattern.tga", 312, 4, 0,0, 0,0,1,1, s,0, 0,0,1,1, 0,t, 0,0,1,1, s,t, 0,0,1,1, 0);opty += 4;
1915         DrawQ_SuperPic(menu_x + 4, menu_y + opty, NULL                                , 312, 4, 0,0, 0,0,0,1, 1,0, 0,0,1,1, 0,1, 0,0,0,1, 1,1, 0,0,1,1, 0);opty += 4;
1916         DrawQ_SuperPic(menu_x + 4, menu_y + opty, "gfx/colorcontrol/ditherpattern.tga", 312, 4, 0,0, 1,1,1,1, s,0, 1,1,1,1, 0,t, 1,1,1,1, s,t, 1,1,1,1, 0);opty += 4;
1917         DrawQ_SuperPic(menu_x + 4, menu_y + opty, NULL                                , 312, 4, 0,0, 0,0,0,1, 1,0, 1,1,1,1, 0,1, 0,0,0,1, 1,1, 1,1,1,1, 0);opty += 4;
1918
1919         c = menu_options_colorcontrol_correctionvalue.value; // intensity value that should be matched up to a 50% dither to 'correct' quake
1920         s = (float) 48 / 2 * vid.realwidth / vid.conwidth;
1921         t = (float) 48 / 2 * vid.realheight / vid.conheight;
1922         u = s * 0.5;
1923         v = t * 0.5;
1924         opty += 8;
1925         x = 4;
1926         DrawQ_Fill(menu_x + x, menu_y + opty, 64, 48, c, 0, 0, 1, 0);
1927         DrawQ_SuperPic(menu_x + x + 16, menu_y + opty + 16, "gfx/colorcontrol/ditherpattern.tga", 16, 16, 0,0, 1,0,0,1, s,0, 1,0,0,1, 0,t, 1,0,0,1, s,t, 1,0,0,1, 0);
1928         DrawQ_SuperPic(menu_x + x + 32, menu_y + opty + 16, "gfx/colorcontrol/ditherpattern.tga", 16, 16, 0,0, 1,0,0,1, u,0, 1,0,0,1, 0,v, 1,0,0,1, u,v, 1,0,0,1, 0);
1929         x += 80;
1930         DrawQ_Fill(menu_x + x, menu_y + opty, 64, 48, 0, c, 0, 1, 0);
1931         DrawQ_SuperPic(menu_x + x + 16, menu_y + opty + 16, "gfx/colorcontrol/ditherpattern.tga", 16, 16, 0,0, 0,1,0,1, s,0, 0,1,0,1, 0,t, 0,1,0,1, s,t, 0,1,0,1, 0);
1932         DrawQ_SuperPic(menu_x + x + 32, menu_y + opty + 16, "gfx/colorcontrol/ditherpattern.tga", 16, 16, 0,0, 0,1,0,1, u,0, 0,1,0,1, 0,v, 0,1,0,1, u,v, 0,1,0,1, 0);
1933         x += 80;
1934         DrawQ_Fill(menu_x + x, menu_y + opty, 64, 48, 0, 0, c, 1, 0);
1935         DrawQ_SuperPic(menu_x + x + 16, menu_y + opty + 16, "gfx/colorcontrol/ditherpattern.tga", 16, 16, 0,0, 0,0,1,1, s,0, 0,0,1,1, 0,t, 0,0,1,1, s,t, 0,0,1,1, 0);
1936         DrawQ_SuperPic(menu_x + x + 32, menu_y + opty + 16, "gfx/colorcontrol/ditherpattern.tga", 16, 16, 0,0, 0,0,1,1, u,0, 0,0,1,1, 0,v, 0,0,1,1, u,v, 0,0,1,1, 0);
1937         x += 80;
1938         DrawQ_Fill(menu_x + x, menu_y + opty, 64, 48, c, c, c, 1, 0);
1939         DrawQ_SuperPic(menu_x + x + 16, menu_y + opty + 16, "gfx/colorcontrol/ditherpattern.tga", 16, 16, 0,0, 1,1,1,1, s,0, 1,1,1,1, 0,t, 1,1,1,1, s,t, 1,1,1,1, 0);
1940         DrawQ_SuperPic(menu_x + x + 32, menu_y + opty + 16, "gfx/colorcontrol/ditherpattern.tga", 16, 16, 0,0, 1,1,1,1, u,0, 1,1,1,1, 0,v, 1,1,1,1, u,v, 1,1,1,1, 0);
1941 }
1942
1943
1944 void M_Options_ColorControl_Key (int k, char ascii)
1945 {
1946         switch (k)
1947         {
1948         case K_ESCAPE:
1949                 M_Menu_Options_f ();
1950                 break;
1951
1952         case K_ENTER:
1953                 m_entersound = true;
1954                 switch (options_colorcontrol_cursor)
1955                 {
1956                 case 0:
1957                         Cvar_SetValueQuick(&v_hwgamma, 1);
1958                         Cvar_SetValueQuick(&v_gamma, 1);
1959                         Cvar_SetValueQuick(&v_contrast, 1);
1960                         Cvar_SetValueQuick(&v_brightness, 0);
1961                         Cvar_SetValueQuick(&v_color_enable, 0);
1962                         Cvar_SetValueQuick(&v_color_black_r, 0);
1963                         Cvar_SetValueQuick(&v_color_black_g, 0);
1964                         Cvar_SetValueQuick(&v_color_black_b, 0);
1965                         Cvar_SetValueQuick(&v_color_grey_r, 0);
1966                         Cvar_SetValueQuick(&v_color_grey_g, 0);
1967                         Cvar_SetValueQuick(&v_color_grey_b, 0);
1968                         Cvar_SetValueQuick(&v_color_white_r, 1);
1969                         Cvar_SetValueQuick(&v_color_white_g, 1);
1970                         Cvar_SetValueQuick(&v_color_white_b, 1);
1971                         break;
1972                 default:
1973                         M_Menu_Options_ColorControl_AdjustSliders (1);
1974                         break;
1975                 }
1976                 return;
1977
1978         case K_UPARROW:
1979                 S_LocalSound ("misc/menu1.wav", true);
1980                 options_colorcontrol_cursor--;
1981                 if (options_colorcontrol_cursor < 0)
1982                         options_colorcontrol_cursor = OPTIONS_COLORCONTROL_ITEMS-1;
1983                 break;
1984
1985         case K_DOWNARROW:
1986                 S_LocalSound ("misc/menu1.wav", true);
1987                 options_colorcontrol_cursor++;
1988                 if (options_colorcontrol_cursor >= OPTIONS_COLORCONTROL_ITEMS)
1989                         options_colorcontrol_cursor = 0;
1990                 break;
1991
1992         case K_LEFTARROW:
1993                 M_Menu_Options_ColorControl_AdjustSliders (-1);
1994                 break;
1995
1996         case K_RIGHTARROW:
1997                 M_Menu_Options_ColorControl_AdjustSliders (1);
1998                 break;
1999         }
2000 }
2001
2002
2003 //=============================================================================
2004 /* KEYS MENU */
2005
2006 char *quakebindnames[][2] =
2007 {
2008 {"+attack",             "attack"},
2009 {"impulse 10",          "next weapon"},
2010 {"impulse 12",          "previous weapon"},
2011 {"+jump",                       "jump / swim up"},
2012 {"+forward",            "walk forward"},
2013 {"+back",                       "backpedal"},
2014 {"+left",                       "turn left"},
2015 {"+right",                      "turn right"},
2016 {"+speed",                      "run"},
2017 {"+moveleft",           "step left"},
2018 {"+moveright",          "step right"},
2019 {"+strafe",             "sidestep"},
2020 {"+lookup",             "look up"},
2021 {"+lookdown",           "look down"},
2022 {"centerview",          "center view"},
2023 {"+mlook",                      "mouse look"},
2024 {"+klook",                      "keyboard look"},
2025 {"+moveup",                     "swim up"},
2026 {"+movedown",           "swim down"}
2027 };
2028
2029 char *transfusionbindnames[][2] =
2030 {
2031 {"",                            "Movement"},            // Movement commands
2032 {"+forward",            "walk forward"},
2033 {"+back",                       "backpedal"},
2034 {"+left",                       "turn left"},
2035 {"+right",                      "turn right"},
2036 {"+moveleft",           "step left"},
2037 {"+moveright",          "step right"},
2038 {"+jump",                       "jump / swim up"},
2039 {"+movedown",           "swim down"},
2040 {"",                            "Combat"},                      // Combat commands
2041 {"impulse 1",           "Pitch Fork"},
2042 {"impulse 2",           "Flare Gun"},
2043 {"impulse 3",           "Shotgun"},
2044 {"impulse 4",           "Machine Gun"},
2045 {"impulse 5",           "Incinerator"},
2046 {"impulse 6",           "Bombs (TNT)"},
2047 {"impulse 35",          "Proximity Bomb"},
2048 {"impulse 36",          "Remote Detonator"},
2049 {"impulse 7",           "Aerosol Can"},
2050 {"impulse 8",           "Tesla Cannon"},
2051 {"impulse 9",           "Life Leech"},
2052 {"impulse 10",          "Voodoo Doll"},
2053 {"impulse 21",          "next weapon"},
2054 {"impulse 22",          "previous weapon"},
2055 {"+attack",             "attack"},
2056 {"+button3",            "altfire"},
2057 {"",                            "Inventory"},           // Inventory commands
2058 {"impulse 40",          "Dr.'s Bag"},
2059 {"impulse 41",          "Crystal Ball"},
2060 {"impulse 42",          "Beast Vision"},
2061 {"impulse 43",          "Jump Boots"},
2062 {"impulse 23",          "next item"},
2063 {"impulse 24",          "previous item"},
2064 {"impulse 25",          "use item"},
2065 {"",                            "Misc"},                        // Misc commands
2066 {"+button4",            "use"},
2067 {"impulse 50",          "add bot (red)"},
2068 {"impulse 51",          "add bot (blue)"},
2069 {"impulse 52",          "kick a bot"},
2070 {"impulse 26",          "next armor type"},
2071 {"impulse 27",          "identify player"},
2072 {"impulse 55",          "voting menu"},
2073 {"impulse 56",          "observer mode"},
2074 {"",                            "Taunts"},            // Taunts
2075 {"impulse 70",          "taunt 0"},
2076 {"impulse 71",          "taunt 1"},
2077 {"impulse 72",          "taunt 2"},
2078 {"impulse 73",          "taunt 3"},
2079 {"impulse 74",          "taunt 4"},
2080 {"impulse 75",          "taunt 5"},
2081 {"impulse 76",          "taunt 6"},
2082 {"impulse 77",          "taunt 7"},
2083 {"impulse 78",          "taunt 8"},
2084 {"impulse 79",          "taunt 9"}
2085 };
2086
2087 char *goodvsbad2bindnames[][2] =
2088 {
2089 {"impulse 69",          "Power 1"},
2090 {"impulse 70",          "Power 2"},
2091 {"impulse 71",          "Power 3"},
2092 {"+jump",                       "jump / swim up"},
2093 {"+forward",            "walk forward"},
2094 {"+back",                       "backpedal"},
2095 {"+left",                       "turn left"},
2096 {"+right",                      "turn right"},
2097 {"+speed",                      "run"},
2098 {"+moveleft",           "step left"},
2099 {"+moveright",          "step right"},
2100 {"+strafe",             "sidestep"},
2101 {"+lookup",             "look up"},
2102 {"+lookdown",           "look down"},
2103 {"centerview",          "center view"},
2104 {"+mlook",                      "mouse look"},
2105 {"kill",                        "kill yourself"},
2106 {"+moveup",                     "swim up"},
2107 {"+movedown",           "swim down"}
2108 };
2109
2110 int numcommands;
2111 char *(*bindnames)[2];
2112
2113 /*
2114 typedef struct binditem_s
2115 {
2116         char *command, *description;
2117         struct binditem_s *next;
2118 }
2119 binditem_t;
2120
2121 typedef struct bindcategory_s
2122 {
2123         char *name;
2124         binditem_t *binds;
2125         struct bindcategory_s *next;
2126 }
2127 bindcategory_t;
2128
2129 bindcategory_t *bindcategories = NULL;
2130
2131 void M_ClearBinds (void)
2132 {
2133         for (c = bindcategories;c;c = cnext)
2134         {
2135                 cnext = c->next;
2136                 for (b = c->binds;b;b = bnext)
2137                 {
2138                         bnext = b->next;
2139                         Z_Free(b);
2140                 }
2141                 Z_Free(c);
2142         }
2143         bindcategories = NULL;
2144 }
2145
2146 void M_AddBindToCategory(bindcategory_t *c, char *command, char *description)
2147 {
2148         for (b = &c->binds;*b;*b = &(*b)->next);
2149         *b = Z_Alloc(sizeof(binditem_t) + strlen(command) + 1 + strlen(description) + 1);
2150         *b->command = (char *)((*b) + 1);
2151         *b->description = *b->command + strlen(command) + 1;
2152         strcpy(*b->command, command);
2153         strcpy(*b->description, description);
2154 }
2155
2156 void M_AddBind (char *category, char *command, char *description)
2157 {
2158         for (c = &bindcategories;*c;c = &(*c)->next)
2159         {
2160                 if (!strcmp(category, (*c)->name))
2161                 {
2162                         M_AddBindToCategory(*c, command, description);
2163                         return;
2164                 }
2165         }
2166         *c = Z_Alloc(sizeof(bindcategory_t));
2167         M_AddBindToCategory(*c, command, description);
2168 }
2169
2170 void M_DefaultBinds (void)
2171 {
2172         M_ClearBinds();
2173         M_AddBind("movement", "+jump", "jump / swim up");
2174         M_AddBind("movement", "+forward", "walk forward");
2175         M_AddBind("movement", "+back", "backpedal");
2176         M_AddBind("movement", "+left", "turn left");
2177         M_AddBind("movement", "+right", "turn right");
2178         M_AddBind("movement", "+speed", "run");
2179         M_AddBind("movement", "+moveleft", "step left");
2180         M_AddBind("movement", "+moveright", "step right");
2181         M_AddBind("movement", "+strafe", "sidestep");
2182         M_AddBind("movement", "+lookup", "look up");
2183         M_AddBind("movement", "+lookdown", "look down");
2184         M_AddBind("movement", "centerview", "center view");
2185         M_AddBind("movement", "+mlook", "mouse look");
2186         M_AddBind("movement", "+klook", "keyboard look");
2187         M_AddBind("movement", "+moveup", "swim up");
2188         M_AddBind("movement", "+movedown", "swim down");
2189         M_AddBind("weapons", "+attack", "attack");
2190         M_AddBind("weapons", "impulse 10", "next weapon");
2191         M_AddBind("weapons", "impulse 12", "previous weapon");
2192         M_AddBind("weapons", "impulse 1", "select weapon 1 (axe)");
2193         M_AddBind("weapons", "impulse 2", "select weapon 2 (shotgun)");
2194         M_AddBind("weapons", "impulse 3", "select weapon 3 (super )");
2195         M_AddBind("weapons", "impulse 4", "select weapon 4 (nailgun)");
2196         M_AddBind("weapons", "impulse 5", "select weapon 5 (super nailgun)");
2197         M_AddBind("weapons", "impulse 6", "select weapon 6 (grenade launcher)");
2198         M_AddBind("weapons", "impulse 7", "select weapon 7 (rocket launcher)");
2199         M_AddBind("weapons", "impulse 8", "select weapon 8 (lightning gun)");
2200 }
2201 */
2202
2203
2204 int             keys_cursor;
2205 int             bind_grab;
2206
2207 void M_Menu_Keys_f (void)
2208 {
2209         key_dest = key_menu;
2210         m_state = m_keys;
2211         m_entersound = true;
2212 }
2213
2214 #define NUMKEYS 5
2215
2216 void M_FindKeysForCommand (char *command, int *keys)
2217 {
2218         int             count;
2219         int             j;
2220         char    *b;
2221
2222         for (j = 0;j < NUMKEYS;j++)
2223                 keys[j] = -1;
2224
2225         count = 0;
2226
2227         for (j = 0; j < (int)sizeof (keybindings[0]) / (int)sizeof (keybindings[0][0]); j++)
2228         {
2229                 b = keybindings[0][j];
2230                 if (!b)
2231                         continue;
2232                 if (!strcmp (b, command) )
2233                 {
2234                         keys[count++] = j;
2235                         if (count == NUMKEYS)
2236                                 break;
2237                 }
2238         }
2239 }
2240
2241 void M_UnbindCommand (char *command)
2242 {
2243         int             j;
2244         char    *b;
2245
2246         for (j = 0; j < (int)sizeof (keybindings[0]) / (int)sizeof (keybindings[0][0]); j++)
2247         {
2248                 b = keybindings[0][j];
2249                 if (!b)
2250                         continue;
2251                 if (!strcmp (b, command))
2252                         Key_SetBinding (j, 0, "");
2253         }
2254 }
2255
2256
2257 void M_Keys_Draw (void)
2258 {
2259         int             i, j;
2260         int             keys[NUMKEYS];
2261         int             y;
2262         cachepic_t      *p;
2263         char    keystring[1024];
2264
2265         M_Background(320, 48 + 8 * numcommands);
2266
2267         p = Draw_CachePic ("gfx/ttl_cstm.lmp");
2268         M_DrawPic ( (320-p->width)/2, 4, "gfx/ttl_cstm.lmp");
2269
2270         if (bind_grab)
2271                 M_Print(12, 32, "Press a key or button for this action");
2272         else
2273                 M_Print(18, 32, "Enter to change, backspace to clear");
2274
2275 // search for known bindings
2276         for (i=0 ; i<numcommands ; i++)
2277         {
2278                 y = 48 + 8*i;
2279
2280                 // If there's no command, it's just a section
2281                 if (bindnames[i][0][0] == '\0')
2282                 {
2283                         M_PrintRed (4, y, "\x0D");  // #13 is the little arrow pointing to the right
2284                         M_PrintRed (16, y, bindnames[i][1]);
2285                         continue;
2286                 }
2287                 else
2288                         M_Print(16, y, bindnames[i][1]);
2289
2290                 M_FindKeysForCommand (bindnames[i][0], keys);
2291
2292                 // LordHavoc: redesigned to print more than 2 keys, inspired by Tomaz's MiniRacer
2293                 if (keys[0] == -1)
2294                         strcpy(keystring, "???");
2295                 else
2296                 {
2297                         keystring[0] = 0;
2298                         for (j = 0;j < NUMKEYS;j++)
2299                         {
2300                                 if (keys[j] != -1)
2301                                 {
2302                                         if (j > 0)
2303                                                 strcat(keystring, " or ");
2304                                         strcat(keystring, Key_KeynumToString (keys[j]));
2305                                 }
2306                         }
2307                 }
2308                 M_Print(150, y, keystring);
2309         }
2310
2311         if (bind_grab)
2312                 M_DrawCharacter (140, 48 + keys_cursor*8, '=');
2313         else
2314                 M_DrawCharacter (140, 48 + keys_cursor*8, 12+((int)(realtime*4)&1));
2315 }
2316
2317
2318 void M_Keys_Key (int k, char ascii)
2319 {
2320         char    cmd[80];
2321         int             keys[NUMKEYS];
2322
2323         if (bind_grab)
2324         {       // defining a key
2325                 S_LocalSound ("misc/menu1.wav", true);
2326                 if (k == K_ESCAPE)
2327                 {
2328                         bind_grab = false;
2329                 }
2330                 else //if (k != '`')
2331                 {
2332                         sprintf (cmd, "bind \"%s\" \"%s\"\n", Key_KeynumToString (k), bindnames[keys_cursor][0]);
2333                         Cbuf_InsertText (cmd);
2334                 }
2335
2336                 bind_grab = false;
2337                 return;
2338         }
2339
2340         switch (k)
2341         {
2342         case K_ESCAPE:
2343                 M_Menu_Options_f ();
2344                 break;
2345
2346         case K_LEFTARROW:
2347         case K_UPARROW:
2348                 S_LocalSound ("misc/menu1.wav", true);
2349                 do
2350                 {
2351                         keys_cursor--;
2352                         if (keys_cursor < 0)
2353                                 keys_cursor = numcommands-1;
2354                 }
2355                 while (bindnames[keys_cursor][0][0] == '\0');  // skip sections
2356                 break;
2357
2358         case K_DOWNARROW:
2359         case K_RIGHTARROW:
2360                 S_LocalSound ("misc/menu1.wav", true);
2361                 do
2362                 {
2363                         keys_cursor++;
2364                         if (keys_cursor >= numcommands)
2365                                 keys_cursor = 0;
2366                 }
2367                 while (bindnames[keys_cursor][0][0] == '\0');  // skip sections
2368                 break;
2369
2370         case K_ENTER:           // go into bind mode
2371                 M_FindKeysForCommand (bindnames[keys_cursor][0], keys);
2372                 S_LocalSound ("misc/menu2.wav", true);
2373                 if (keys[NUMKEYS - 1] != -1)
2374                         M_UnbindCommand (bindnames[keys_cursor][0]);
2375                 bind_grab = true;
2376                 break;
2377
2378         case K_BACKSPACE:               // delete bindings
2379         case K_DEL:                             // delete bindings
2380                 S_LocalSound ("misc/menu2.wav", true);
2381                 M_UnbindCommand (bindnames[keys_cursor][0]);
2382                 break;
2383         }
2384 }
2385
2386 void M_Menu_Reset_f (void)
2387 {
2388         key_dest = key_menu;
2389         m_state = m_reset;
2390         m_entersound = true;
2391 }
2392
2393
2394 void M_Reset_Key (int key, char ascii)
2395 {
2396         switch (key)
2397         {
2398         case K_ESCAPE:
2399         case 'n':
2400         case 'N':
2401                 m_state = m_options;
2402                 m_entersound = true;
2403                 break;
2404
2405         case 'Y':
2406         case 'y':
2407                 Cbuf_AddText ("exec default.cfg\n");
2408                 break;
2409
2410         default:
2411                 break;
2412         }
2413 }
2414
2415 void M_Reset_Draw (void)
2416 {
2417         int lines = 2, linelength = 20;
2418         M_Background(linelength * 8 + 16, lines * 8 + 16);
2419         M_DrawTextBox(0, 0, linelength, lines);
2420         M_Print(8 + 4 * (linelength - 19),  8, "Really wanna reset?");
2421         M_Print(8 + 4 * (linelength - 11), 16, "Press y / n");
2422 }
2423
2424 //=============================================================================
2425 /* VIDEO MENU */
2426
2427 #define VIDEO_ITEMS 5
2428
2429 int video_cursor = 0;
2430 int video_cursor_table[] = {56, 68, 80, 100, 120};
2431 // note: if modes are added to the beginning of this list, update the
2432 // video_resolution = x; in M_Menu_Video_f below
2433 unsigned short video_resolutions[][2] = {{320,240}, {400,300}, {512,384}, {640,480}, {800,600}, {1024,768}, {1152,864}, {1280,960}, {1280,1024}, {1600,1200}, {1792,1344}, {1920,1440}, {2048,1536}, {0,0}};
2434 // this is the number of the 640x480 mode in the list
2435 #define VID_640 3
2436 #define VID_RES_COUNT ((int)(sizeof(video_resolutions) / sizeof(video_resolutions[0])) - 1)
2437 int video_resolution;
2438
2439 extern int current_vid_fullscreen;
2440 extern int current_vid_width;
2441 extern int current_vid_height;
2442 extern int current_vid_bitsperpixel;
2443
2444
2445 void M_Menu_Video_f (void)
2446 {
2447         key_dest = key_menu;
2448         m_state = m_video;
2449         m_entersound = true;
2450
2451         // Look for the current resolution
2452         for (video_resolution = 0; video_resolution < VID_RES_COUNT; video_resolution++)
2453         {
2454                 if (video_resolutions[video_resolution][0] == current_vid_width &&
2455                         video_resolutions[video_resolution][1] == current_vid_height)
2456                         break;
2457         }
2458
2459         // Default to VID_640 if we didn't find it
2460         if (video_resolution == VID_RES_COUNT)
2461         {
2462                 // may need to update this number if mode list changes
2463                 video_resolution = VID_640;
2464                 Cvar_SetValueQuick (&vid_width, video_resolutions[video_resolution][0]);
2465                 Cvar_SetValueQuick (&vid_height, video_resolutions[video_resolution][1]);
2466         }
2467 }
2468
2469
2470 void M_Video_Draw (void)
2471 {
2472         cachepic_t      *p;
2473         const char* string;
2474
2475         M_Background(320, 200);
2476
2477         M_DrawPic(16, 4, "gfx/qplaque.lmp");
2478         p = Draw_CachePic("gfx/vidmodes.lmp");
2479         M_DrawPic((320-p->width)/2, 4, "gfx/vidmodes.lmp");
2480
2481         // Resolution
2482         M_Print(16, video_cursor_table[0], "            Resolution");
2483         string = va("%dx%d", video_resolutions[video_resolution][0], video_resolutions[video_resolution][1]);
2484         M_Print(220, video_cursor_table[0], string);
2485
2486         // Bits per pixel
2487         M_Print(16, video_cursor_table[1], "        Bits per pixel");
2488         M_Print(220, video_cursor_table[1], (vid_bitsperpixel.integer == 32) ? "32" : "16");
2489
2490         // Fullscreen
2491         M_Print(16, video_cursor_table[2], "            Fullscreen");
2492         M_DrawCheckbox(220, video_cursor_table[2], vid_fullscreen.integer);
2493
2494         // "Apply" button
2495         M_Print(220, video_cursor_table[3], "Apply");
2496
2497         // Vertical Sync
2498         M_ItemPrint (0, video_cursor_table[4], "         Vertical Sync", gl_videosyncavailable);
2499         M_DrawCheckbox(220, video_cursor_table[4], vid_vsync.integer);
2500
2501         // Cursor
2502         M_DrawCharacter(200, video_cursor_table[video_cursor], 12+((int)(realtime*4)&1));
2503 }
2504
2505
2506 void M_Menu_Video_AdjustSliders (int dir)
2507 {
2508         S_LocalSound ("misc/menu3.wav", true);
2509
2510         switch (video_cursor)
2511         {
2512                 // Resolution
2513                 case 0:
2514                 {
2515                         int new_resolution = video_resolution + dir;
2516                         if (gamemode == GAME_FNIGGIUM ? new_resolution < VID_640 : new_resolution < 0)
2517                                 video_resolution = VID_RES_COUNT - 1;
2518                         else if (new_resolution > VID_RES_COUNT - 1)
2519                                 video_resolution = gamemode == GAME_FNIGGIUM ? VID_640 : 0;
2520                         else
2521                                 video_resolution = new_resolution;
2522
2523                         Cvar_SetValueQuick (&vid_width, video_resolutions[video_resolution][0]);
2524                         Cvar_SetValueQuick (&vid_height, video_resolutions[video_resolution][1]);
2525                         break;
2526                 }
2527
2528                 // Bits per pixel
2529                 case 1:
2530                         Cvar_SetValueQuick (&vid_bitsperpixel, (vid_bitsperpixel.integer == 32) ? 16 : 32);
2531                         break;
2532                 case 2:
2533                         Cvar_SetValueQuick (&vid_fullscreen, !vid_fullscreen.integer);
2534                         break;
2535
2536                 case 4:
2537                         Cvar_SetValueQuick (&vid_vsync, !vid_vsync.integer);
2538                         break;
2539         }
2540 }
2541
2542
2543 void M_Video_Key (int key, char ascii)
2544 {
2545         switch (key)
2546         {
2547                 case K_ESCAPE:
2548                         // vid_shared.c has a copy of the current video config. We restore it
2549                         Cvar_SetValueQuick(&vid_fullscreen, current_vid_fullscreen);
2550                         Cvar_SetValueQuick(&vid_width, current_vid_width);
2551                         Cvar_SetValueQuick(&vid_height, current_vid_height);
2552                         Cvar_SetValueQuick(&vid_bitsperpixel, current_vid_bitsperpixel);
2553
2554                         S_LocalSound ("misc/menu1.wav", true);
2555                         M_Menu_Options_f ();
2556                         break;
2557
2558                 case K_ENTER:
2559                         m_entersound = true;
2560                         switch (video_cursor)
2561                         {
2562                                 case 3:
2563                                         Cbuf_AddText ("vid_restart\n");
2564                                         M_Menu_Options_f ();
2565                                         break;
2566                                 default:
2567                                         M_Menu_Video_AdjustSliders (1);
2568                         }
2569                         break;
2570
2571                 case K_UPARROW:
2572                         S_LocalSound ("misc/menu1.wav", true);
2573                         video_cursor--;
2574                         if (video_cursor < 0)
2575                                 video_cursor = VIDEO_ITEMS-1;
2576                         break;
2577
2578                 case K_DOWNARROW:
2579                         S_LocalSound ("misc/menu1.wav", true);
2580                         video_cursor++;
2581                         if (video_cursor >= VIDEO_ITEMS)
2582                                 video_cursor = 0;
2583                         break;
2584
2585                 case K_LEFTARROW:
2586                         M_Menu_Video_AdjustSliders (-1);
2587                         break;
2588
2589                 case K_RIGHTARROW:
2590                         M_Menu_Video_AdjustSliders (1);
2591                         break;
2592         }
2593 }
2594
2595 //=============================================================================
2596 /* HELP MENU */
2597
2598 int             help_page;
2599 #define NUM_HELP_PAGES  6
2600
2601
2602 void M_Menu_Help_f (void)
2603 {
2604         key_dest = key_menu;
2605         m_state = m_help;
2606         m_entersound = true;
2607         help_page = 0;
2608 }
2609
2610
2611
2612 void M_Help_Draw (void)
2613 {
2614         M_Background(320, 200);
2615         M_DrawPic (0, 0, va("gfx/help%i.lmp", help_page));
2616 }
2617
2618
2619 void M_Help_Key (int key, char ascii)
2620 {
2621         switch (key)
2622         {
2623         case K_ESCAPE:
2624                 M_Menu_Main_f ();
2625                 break;
2626
2627         case K_UPARROW:
2628         case K_RIGHTARROW:
2629                 m_entersound = true;
2630                 if (++help_page >= NUM_HELP_PAGES)
2631                         help_page = 0;
2632                 break;
2633
2634         case K_DOWNARROW:
2635         case K_LEFTARROW:
2636                 m_entersound = true;
2637                 if (--help_page < 0)
2638                         help_page = NUM_HELP_PAGES-1;
2639                 break;
2640         }
2641
2642 }
2643
2644 //=============================================================================
2645 /* QUIT MENU */
2646
2647 char *m_quit_message[9];
2648 int             m_quit_prevstate;
2649 qboolean        wasInMenus;
2650
2651
2652 int M_QuitMessage(char *line1, char *line2, char *line3, char *line4, char *line5, char *line6, char *line7, char *line8)
2653 {
2654         m_quit_message[0] = line1;
2655         m_quit_message[1] = line2;
2656         m_quit_message[2] = line3;
2657         m_quit_message[3] = line4;
2658         m_quit_message[4] = line5;
2659         m_quit_message[5] = line6;
2660         m_quit_message[6] = line7;
2661         m_quit_message[7] = line8;
2662         m_quit_message[8] = NULL;
2663         return 1;
2664 }
2665
2666 int M_ChooseQuitMessage(int request)
2667 {
2668         switch (gamemode)
2669         {
2670         case GAME_NORMAL:
2671         case GAME_HIPNOTIC:
2672         case GAME_ROGUE:
2673         case GAME_NEHAHRA:
2674                 if (request-- == 0) return M_QuitMessage("Are you gonna quit","this game just like","everything else?",NULL,NULL,NULL,NULL,NULL);
2675                 if (request-- == 0) return M_QuitMessage("Milord, methinks that","thou art a lowly","quitter. Is this true?",NULL,NULL,NULL,NULL,NULL);
2676                 if (request-- == 0) return M_QuitMessage("Do I need to bust your","face open for trying","to quit?",NULL,NULL,NULL,NULL,NULL);
2677                 if (request-- == 0) return M_QuitMessage("Man, I oughta smack you","for trying to quit!","Press Y to get","smacked out.",NULL,NULL,NULL,NULL);
2678                 if (request-- == 0) return M_QuitMessage("Press Y to quit like a","big loser in life.","Press N to stay proud","and successful!",NULL,NULL,NULL,NULL);
2679                 if (request-- == 0) return M_QuitMessage("If you press Y to","quit, I will summon","Satan all over your","hard drive!",NULL,NULL,NULL,NULL);
2680                 if (request-- == 0) return M_QuitMessage("Um, Asmodeus dislikes","his children trying to","quit. Press Y to return","to your Tinkertoys.",NULL,NULL,NULL,NULL);
2681                 if (request-- == 0) return M_QuitMessage("If you quit now, I'll","throw a blanket-party","for you next time!",NULL,NULL,NULL,NULL,NULL);
2682                 break;
2683         case GAME_GOODVSBAD2:
2684                 if (request-- == 0) return M_QuitMessage("Press Yes To Quit","...","Yes",NULL,NULL,NULL,NULL,NULL);
2685                 if (request-- == 0) return M_QuitMessage("Do you really want to","Quit?","Play Good vs bad 3!",NULL,NULL,NULL,NULL,NULL);
2686                 if (request-- == 0) return M_QuitMessage("All your quit are","belong to long duck","dong",NULL,NULL,NULL,NULL,NULL);
2687                 if (request-- == 0) return M_QuitMessage("Press Y to quit","","But are you too legit?",NULL,NULL,NULL,NULL,NULL);
2688                 if (request-- == 0) return M_QuitMessage("This game was made by","e@chip-web.com","It is by far the best","game ever made.",NULL,NULL,NULL,NULL);
2689                 if (request-- == 0) return M_QuitMessage("Even I really dont","know of a game better","Press Y to quit","like rougue chedder",NULL,NULL,NULL,NULL);
2690                 if (request-- == 0) return M_QuitMessage("After you stop playing","tell the guys who made","counterstrike to just","kill themselves now",NULL,NULL,NULL,NULL);
2691                 if (request-- == 0) return M_QuitMessage("Press Y to exit to DOS","","SSH login as user Y","to exit to Linux",NULL,NULL,NULL,NULL);
2692                 if (request-- == 0) return M_QuitMessage("Press Y like you","were waanderers","from Ys'",NULL,NULL,NULL,NULL,NULL);
2693                 if (request-- == 0) return M_QuitMessage("This game was made in","Nippon like the SS","announcer's saying ipon",NULL,NULL,NULL,NULL,NULL);
2694                 if (request-- == 0) return M_QuitMessage("you","want to quit?",NULL,NULL,NULL,NULL,NULL,NULL);
2695                 if (request-- == 0) return M_QuitMessage("Please stop playing","this stupid game",NULL,NULL,NULL,NULL,NULL,NULL);
2696                 break;
2697         case GAME_BATTLEMECH:
2698                 if (request-- == 0) return M_QuitMessage("? WHY ?","Press Y to quit, N to keep fraggin'",NULL,NULL,NULL,NULL,NULL,NULL);
2699                 if (request-- == 0) return M_QuitMessage("Leave now and your mech is scrap!","Press Y to quit, N to keep fraggin'",NULL,NULL,NULL,NULL,NULL,NULL);
2700                 if (request-- == 0) return M_QuitMessage("Accept Defeat?","Press Y to quit, N to keep fraggin'",NULL,NULL,NULL,NULL,NULL,NULL);
2701                 if (request-- == 0) return M_QuitMessage("Wait! There are more mechs to destroy!","Press Y to quit, N to keep fraggin'",NULL,NULL,NULL,NULL,NULL,NULL);
2702                 if (request-- == 0) return M_QuitMessage("Where's your bloodlust?","Press Y to quit, N to keep fraggin'",NULL,NULL,NULL,NULL,NULL,NULL);
2703                 if (request-- == 0) return M_QuitMessage("Your mech here is way more impressive","than your car out there...","Press Y to quit, N to keep fraggin'",NULL,NULL,NULL,NULL,NULL);
2704                 if (request-- == 0) return M_QuitMessage("Quitting won't reduce your debt","Press Y to quit, N to keep fraggin'",NULL,NULL,NULL,NULL,NULL,NULL);
2705                 break;
2706         case GAME_OPENQUARTZ:
2707                 if (request-- == 0) return M_QuitMessage("There is nothing like free beer!","Press Y to quit, N to stay",NULL,NULL,NULL,NULL,NULL,NULL);
2708                 if (request-- == 0) return M_QuitMessage("GNU is not Unix!","Press Y to quit, N to stay",NULL,NULL,NULL,NULL,NULL,NULL);
2709                 if (request-- == 0) return M_QuitMessage("You prefer free beer over free speech?","Press Y to quit, N to stay",NULL,NULL,NULL,NULL,NULL,NULL);
2710                 if (request-- == 0) return M_QuitMessage("Is OpenQuartz Propaganda?","Press Y to quit, N to stay",NULL,NULL,NULL,NULL,NULL,NULL);
2711                 break;
2712         default:
2713                 if (request-- == 0) return M_QuitMessage("Tired of fragging already?",NULL,NULL,NULL,NULL,NULL,NULL,NULL);
2714                 if (request-- == 0) return M_QuitMessage("Quit now and forfeit your bodycount?",NULL,NULL,NULL,NULL,NULL,NULL,NULL);
2715                 if (request-- == 0) return M_QuitMessage("Are you sure you want to quit?",NULL,NULL,NULL,NULL,NULL,NULL,NULL);
2716                 if (request-- == 0) return M_QuitMessage("Off to do something constructive?",NULL,NULL,NULL,NULL,NULL,NULL,NULL);
2717                 break;
2718         }
2719         return 0;
2720 };
2721
2722 void M_Menu_Quit_f (void)
2723 {
2724         int n;
2725         if (m_state == m_quit)
2726                 return;
2727         wasInMenus = (key_dest == key_menu);
2728         key_dest = key_menu;
2729         m_quit_prevstate = m_state;
2730         m_state = m_quit;
2731         m_entersound = true;
2732         // count how many there are
2733         for (n = 0;M_ChooseQuitMessage(n);n++);
2734         // choose one
2735         M_ChooseQuitMessage(rand() % n);
2736 }
2737
2738
2739 void M_Quit_Key (int key, char ascii)
2740 {
2741         switch (key)
2742         {
2743         case K_ESCAPE:
2744         case 'n':
2745         case 'N':
2746                 if (wasInMenus)
2747                 {
2748                         m_state = m_quit_prevstate;
2749                         m_entersound = true;
2750                 }
2751                 else
2752                 {
2753                         key_dest = key_game;
2754                         m_state = m_none;
2755                 }
2756                 break;
2757
2758         case 'Y':
2759         case 'y':
2760                 Host_Quit_f ();
2761                 break;
2762
2763         default:
2764                 break;
2765         }
2766 }
2767
2768 void M_Quit_Draw (void)
2769 {
2770         int i, l, linelength, firstline, lastline, lines;
2771         for (i = 0, linelength = 0, firstline = 9999, lastline = -1;m_quit_message[i];i++)
2772         {
2773                 if ((l = strlen(m_quit_message[i])))
2774                 {
2775                         if (firstline > i)
2776                                 firstline = i;
2777                         if (lastline < i)
2778                                 lastline = i;
2779                         if (linelength < l)
2780                                 linelength = l;
2781                 }
2782         }
2783         lines = (lastline - firstline) + 1;
2784         M_Background(linelength * 8 + 16, lines * 8 + 16);
2785         M_DrawTextBox(0, 0, linelength, lines);
2786         for (i = 0, l = firstline;i < lines;i++, l++)
2787                 M_Print(8 + 4 * (linelength - strlen(m_quit_message[l])), 8 + 8 * i, m_quit_message[l]);
2788 }
2789
2790 //=============================================================================
2791 /* LAN CONFIG MENU */
2792
2793 int             lanConfig_cursor = -1;
2794 int             lanConfig_cursor_table [] = {56, 76, 112};
2795 #define NUM_LANCONFIG_CMDS      3
2796
2797 int     lanConfig_port;
2798 char    lanConfig_portname[6];
2799 char    lanConfig_joinname[22];
2800
2801 void M_Menu_LanConfig_f (void)
2802 {
2803         key_dest = key_menu;
2804         m_state = m_lanconfig;
2805         m_entersound = true;
2806         if (lanConfig_cursor == -1)
2807         {
2808                 if (JoiningGame)
2809                         lanConfig_cursor = 1;
2810         }
2811         if (StartingGame)
2812                 lanConfig_cursor = 1;
2813         lanConfig_port = 26000;
2814         sprintf(lanConfig_portname, "%u", lanConfig_port);
2815
2816         M_Update_Return_Reason("");
2817 }
2818
2819
2820 void M_LanConfig_Draw (void)
2821 {
2822         cachepic_t      *p;
2823         int             basex;
2824         char    *startJoin;
2825         char    *protocol;
2826
2827         M_Background(320, 200);
2828
2829         M_DrawPic (16, 4, "gfx/qplaque.lmp");
2830         p = Draw_CachePic ("gfx/p_multi.lmp");
2831         basex = (320-p->width)/2;
2832         M_DrawPic (basex, 4, "gfx/p_multi.lmp");
2833
2834         if (StartingGame)
2835                 startJoin = "New Game";
2836         else
2837                 startJoin = "Join Game";
2838         protocol = "TCP/IP";
2839         M_Print(basex, 32, va ("%s - %s", startJoin, protocol));
2840         basex += 8;
2841
2842         M_Print(basex, lanConfig_cursor_table[0], "Port");
2843         M_DrawTextBox (basex+8*8, lanConfig_cursor_table[0]-8, 6, 1);
2844         M_Print(basex+9*8, lanConfig_cursor_table[0], lanConfig_portname);
2845
2846         if (JoiningGame)
2847         {
2848                 M_Print(basex, lanConfig_cursor_table[1], "Search for games...");
2849                 M_Print(basex, lanConfig_cursor_table[2]-16, "Join game at:");
2850                 M_DrawTextBox (basex+8, lanConfig_cursor_table[2]-8, 22, 1);
2851                 M_Print(basex+16, lanConfig_cursor_table[2], lanConfig_joinname);
2852         }
2853         else
2854         {
2855                 M_DrawTextBox (basex, lanConfig_cursor_table[1]-8, 2, 1);
2856                 M_Print(basex+8, lanConfig_cursor_table[1], "OK");
2857         }
2858
2859         M_DrawCharacter (basex-8, lanConfig_cursor_table [lanConfig_cursor], 12+((int)(realtime*4)&1));
2860
2861         if (lanConfig_cursor == 0)
2862                 M_DrawCharacter (basex+9*8 + 8*strlen(lanConfig_portname), lanConfig_cursor_table [0], 10+((int)(realtime*4)&1));
2863
2864         if (lanConfig_cursor == 2)
2865                 M_DrawCharacter (basex+16 + 8*strlen(lanConfig_joinname), lanConfig_cursor_table [2], 10+((int)(realtime*4)&1));
2866
2867         if (*m_return_reason)
2868                 M_Print(basex, 168, m_return_reason);
2869 }
2870
2871
2872 void M_LanConfig_Key (int key, char ascii)
2873 {
2874         int             l;
2875
2876         switch (key)
2877         {
2878         case K_ESCAPE:
2879                 M_Menu_MultiPlayer_f ();
2880                 break;
2881
2882         case K_UPARROW:
2883                 S_LocalSound ("misc/menu1.wav", true);
2884                 lanConfig_cursor--;
2885                 if (lanConfig_cursor < 0)
2886                         lanConfig_cursor = NUM_LANCONFIG_CMDS-1;
2887                 break;
2888
2889         case K_DOWNARROW:
2890                 S_LocalSound ("misc/menu1.wav", true);
2891                 lanConfig_cursor++;
2892                 if (lanConfig_cursor >= NUM_LANCONFIG_CMDS)
2893                         lanConfig_cursor = 0;
2894                 break;
2895
2896         case K_ENTER:
2897                 if (lanConfig_cursor == 0)
2898                         break;
2899
2900                 m_entersound = true;
2901
2902                 Cbuf_AddText ("stopdemo\n");
2903
2904                 Cvar_SetValue("port", lanConfig_port);
2905
2906                 if (lanConfig_cursor == 1)
2907                 {
2908                         if (StartingGame)
2909                         {
2910                                 M_Menu_GameOptions_f ();
2911                                 break;
2912                         }
2913                         M_Menu_ServerList_f();
2914                         break;
2915                 }
2916
2917                 if (lanConfig_cursor == 2)
2918                         Cbuf_AddText ( va ("connect \"%s\"\n", lanConfig_joinname) );
2919                 break;
2920
2921         case K_BACKSPACE:
2922                 if (lanConfig_cursor == 0)
2923                 {
2924                         if (strlen(lanConfig_portname))
2925                                 lanConfig_portname[strlen(lanConfig_portname)-1] = 0;
2926                 }
2927
2928                 if (lanConfig_cursor == 2)
2929                 {
2930                         if (strlen(lanConfig_joinname))
2931                                 lanConfig_joinname[strlen(lanConfig_joinname)-1] = 0;
2932                 }
2933                 break;
2934
2935         default:
2936                 if (ascii < 32 || ascii > 126)
2937                         break;
2938
2939                 if (lanConfig_cursor == 2)
2940                 {
2941                         l = strlen(lanConfig_joinname);
2942                         if (l < 21)
2943                         {
2944                                 lanConfig_joinname[l+1] = 0;
2945                                 lanConfig_joinname[l] = ascii;
2946                         }
2947                 }
2948
2949                 if (ascii < '0' || ascii > '9')
2950                         break;
2951                 if (lanConfig_cursor == 0)
2952                 {
2953                         l = strlen(lanConfig_portname);
2954                         if (l < 5)
2955                         {
2956                                 lanConfig_portname[l+1] = 0;
2957                                 lanConfig_portname[l] = ascii;
2958                         }
2959                 }
2960         }
2961
2962         if (StartingGame && lanConfig_cursor == 2)
2963         {
2964                 if (key == K_UPARROW)
2965                         lanConfig_cursor = 1;
2966                 else
2967                         lanConfig_cursor = 0;
2968         }
2969
2970         l =  atoi(lanConfig_portname);
2971         if (l <= 65535)
2972                 lanConfig_port = l;
2973         sprintf(lanConfig_portname, "%u", lanConfig_port);
2974 }
2975
2976 //=============================================================================
2977 /* GAME OPTIONS MENU */
2978
2979 typedef struct
2980 {
2981         char    *name;
2982         char    *description;
2983 } level_t;
2984
2985 typedef struct
2986 {
2987         char    *description;
2988         int             firstLevel;
2989         int             levels;
2990 } episode_t;
2991
2992 typedef struct
2993 {
2994         char *gamename;
2995         level_t *levels;
2996         episode_t *episodes;
2997         int numepisodes;
2998 }
2999 gamelevels_t;
3000
3001 level_t quakelevels[] =
3002 {
3003         {"start", "Entrance"},  // 0
3004
3005         {"e1m1", "Slipgate Complex"},                           // 1
3006         {"e1m2", "Castle of the Damned"},
3007         {"e1m3", "The Necropolis"},
3008         {"e1m4", "The Grisly Grotto"},
3009         {"e1m5", "Gloom Keep"},
3010         {"e1m6", "The Door To Chthon"},
3011         {"e1m7", "The House of Chthon"},
3012         {"e1m8", "Ziggurat Vertigo"},
3013
3014         {"e2m1", "The Installation"},                           // 9
3015         {"e2m2", "Ogre Citadel"},
3016         {"e2m3", "Crypt of Decay"},
3017         {"e2m4", "The Ebon Fortress"},
3018         {"e2m5", "The Wizard's Manse"},
3019         {"e2m6", "The Dismal Oubliette"},
3020         {"e2m7", "Underearth"},
3021
3022         {"e3m1", "Termination Central"},                        // 16
3023         {"e3m2", "The Vaults of Zin"},
3024         {"e3m3", "The Tomb of Terror"},
3025         {"e3m4", "Satan's Dark Delight"},
3026         {"e3m5", "Wind Tunnels"},
3027         {"e3m6", "Chambers of Torment"},
3028         {"e3m7", "The Haunted Halls"},
3029
3030         {"e4m1", "The Sewage System"},                          // 23
3031         {"e4m2", "The Tower of Despair"},
3032         {"e4m3", "The Elder God Shrine"},
3033         {"e4m4", "The Palace of Hate"},
3034         {"e4m5", "Hell's Atrium"},
3035         {"e4m6", "The Pain Maze"},
3036         {"e4m7", "Azure Agony"},
3037         {"e4m8", "The Nameless City"},
3038
3039         {"end", "Shub-Niggurath's Pit"},                        // 31
3040
3041         {"dm1", "Place of Two Deaths"},                         // 32
3042         {"dm2", "Claustrophobopolis"},
3043         {"dm3", "The Abandoned Base"},
3044         {"dm4", "The Bad Place"},
3045         {"dm5", "The Cistern"},
3046         {"dm6", "The Dark Zone"}
3047 };
3048
3049 episode_t quakeepisodes[] =
3050 {
3051         {"Welcome to Quake", 0, 1},
3052         {"Doomed Dimension", 1, 8},
3053         {"Realm of Black Magic", 9, 7},
3054         {"Netherworld", 16, 7},
3055         {"The Elder World", 23, 8},
3056         {"Final Level", 31, 1},
3057         {"Deathmatch Arena", 32, 6}
3058 };
3059
3060  //MED 01/06/97 added hipnotic levels
3061 level_t     hipnoticlevels[] =
3062 {
3063    {"start", "Command HQ"},  // 0
3064
3065    {"hip1m1", "The Pumping Station"},          // 1
3066    {"hip1m2", "Storage Facility"},
3067    {"hip1m3", "The Lost Mine"},
3068    {"hip1m4", "Research Facility"},
3069    {"hip1m5", "Military Complex"},
3070
3071    {"hip2m1", "Ancient Realms"},          // 6
3072    {"hip2m2", "The Black Cathedral"},
3073    {"hip2m3", "The Catacombs"},
3074    {"hip2m4", "The Crypt"},
3075    {"hip2m5", "Mortum's Keep"},
3076    {"hip2m6", "The Gremlin's Domain"},
3077
3078    {"hip3m1", "Tur Torment"},       // 12
3079    {"hip3m2", "Pandemonium"},
3080    {"hip3m3", "Limbo"},
3081    {"hip3m4", "The Gauntlet"},
3082
3083    {"hipend", "Armagon's Lair"},       // 16
3084
3085    {"hipdm1", "The Edge of Oblivion"}           // 17
3086 };
3087
3088 //MED 01/06/97  added hipnotic episodes
3089 episode_t   hipnoticepisodes[] =
3090 {
3091    {"Scourge of Armagon", 0, 1},
3092    {"Fortress of the Dead", 1, 5},
3093    {"Dominion of Darkness", 6, 6},
3094    {"The Rift", 12, 4},
3095    {"Final Level", 16, 1},
3096    {"Deathmatch Arena", 17, 1}
3097 };
3098
3099 //PGM 01/07/97 added rogue levels
3100 //PGM 03/02/97 added dmatch level
3101 level_t         roguelevels[] =
3102 {
3103         {"start",       "Split Decision"},
3104         {"r1m1",        "Deviant's Domain"},
3105         {"r1m2",        "Dread Portal"},
3106         {"r1m3",        "Judgement Call"},
3107         {"r1m4",        "Cave of Death"},
3108         {"r1m5",        "Towers of Wrath"},
3109         {"r1m6",        "Temple of Pain"},
3110         {"r1m7",        "Tomb of the Overlord"},
3111         {"r2m1",        "Tempus Fugit"},
3112         {"r2m2",        "Elemental Fury I"},
3113         {"r2m3",        "Elemental Fury II"},
3114         {"r2m4",        "Curse of Osiris"},
3115         {"r2m5",        "Wizard's Keep"},
3116         {"r2m6",        "Blood Sacrifice"},
3117         {"r2m7",        "Last Bastion"},
3118         {"r2m8",        "Source of Evil"},
3119         {"ctf1",    "Division of Change"}
3120 };
3121
3122 //PGM 01/07/97 added rogue episodes
3123 //PGM 03/02/97 added dmatch episode
3124 episode_t       rogueepisodes[] =
3125 {
3126         {"Introduction", 0, 1},
3127         {"Hell's Fortress", 1, 7},
3128         {"Corridors of Time", 8, 8},
3129         {"Deathmatch Arena", 16, 1}
3130 };
3131
3132 level_t         nehahralevels[] =
3133 {
3134         {"nehstart",    "Welcome to Nehahra"},
3135         {"neh1m1",      "Forge City1: Slipgates"},
3136         {"neh1m2",      "Forge City2: Boiler"},
3137         {"neh1m3",      "Forge City3: Escape"},
3138         {"neh1m4",      "Grind Core"},
3139         {"neh1m5",      "Industrial Silence"},
3140         {"neh1m6",      "Locked-Up Anger"},
3141         {"neh1m7",      "Wanderer of the Wastes"},
3142         {"neh1m8",      "Artemis System Net"},
3143         {"neh1m9",      "To the Death"},
3144         {"neh2m1",      "The Gates of Ghoro"},
3145         {"neh2m2",      "Sacred Trinity"},
3146         {"neh2m3",      "Realm of the Ancients"},
3147         {"neh2m4",      "Temple of the Ancients"},
3148         {"neh2m5",      "Dreams Made Flesh"},
3149         {"neh2m6",      "Your Last Cup of Sorrow"},
3150         {"nehsec",      "Ogre's Bane"},
3151         {"nehahra",     "Nehahra's Den"},
3152         {"nehend",      "Quintessence"}
3153 };
3154
3155 episode_t       nehahraepisodes[] =
3156 {
3157         {"Welcome to Nehahra", 0, 1},
3158         {"The Fall of Forge", 1, 9},
3159         {"The Outlands", 10, 7},
3160         {"Dimension of the Lost", 17, 2}
3161 };
3162
3163 // Map list for Transfusion
3164 level_t         transfusionlevels[] =
3165 {
3166         {"e1m1",                "Cradle to Grave"},
3167         {"e1m2",                "Wrong Side of the Tracks"},
3168         {"e1m3",                "Phantom Express"},
3169         {"e1m4",                "Dark Carnival"},
3170         {"e1m5",                "Hallowed Grounds"},
3171         {"e1m6",                "The Great Temple"},
3172         {"e1m7",                "Altar of Stone"},
3173         {"e1m8",                "House of Horrors"},
3174
3175         {"e2m1",                "Shipwrecked"},
3176         {"e2m2",                "The Lumber Mill"},
3177         {"e2m3",                "Rest for the Wicked"},
3178         {"e2m4",                "The Overlooked Hotel"},
3179         {"e2m5",                "The Haunting"},
3180         {"e2m6",                "The Cold Rush"},
3181         {"e2m7",                "Bowels of the Earth"},
3182         {"e2m8",                "The Lair of Shial"},
3183         {"e2m9",                "Thin Ice"},
3184
3185         {"e3m1",                "Ghost Town"},
3186         {"e3m2",                "The Siege"},
3187         {"e3m3",                "Raw Sewage"},
3188         {"e3m4",                "The Sick Ward"},
3189         {"e3m5",                "Spare Parts"},
3190         {"e3m6",                "Monster Bait"},
3191         {"e3m7",                "The Pit of Cerberus"},
3192         {"e3m8",                "Catacombs"},
3193
3194         {"e4m1",                "Butchery Loves Company"},
3195         {"e4m2",                "Breeding Grounds"},
3196         {"e4m3",                "Charnel House"},
3197         {"e4m4",                "Crystal Lake"},
3198         {"e4m5",                "Fire and Brimstone"},
3199         {"e4m6",                "The Ganglion Depths"},
3200         {"e4m7",                "In the Flesh"},
3201         {"e4m8",                "The Hall of the Epiphany"},
3202         {"e4m9",                "Mall of the Dead"},
3203
3204         {"bb1",                 "The Stronghold"},
3205         {"bb2",                 "Winter Wonderland"},
3206         {"bb3",                 "Bodies"},
3207         {"bb4",                 "The Tower"},
3208         {"bb5",                 "Click!"},
3209         {"bb6",                 "Twin Fortress"},
3210         {"bb7",                 "Midgard"},
3211         {"bb8",                 "Fun With Heads"},
3212         {"dm1",                 "Monolith Building 11"},
3213         {"dm2",                 "Power!"},
3214         {"dm3",                 "Area 15"},
3215
3216         {"e6m1",                "Welcome to Your Life"},
3217         {"e6m2",                "They Are Here"},
3218         {"e6m3",                "Public Storage"},
3219         {"e6m4",                "Aqueducts"},
3220         {"e6m5",                "The Ruined Temple"},
3221         {"e6m6",                "Forbidden Rituals"},
3222         {"e6m7",                "The Dungeon"},
3223         {"e6m8",                "Beauty and the Beast"},
3224         {"e6m9",                "Forgotten Catacombs"},
3225
3226         {"cp01",                "Boat Docks"},
3227         {"cp02",                "Old Opera House"},
3228         {"cp03",                "Gothic Library"},
3229         {"cp04",                "Lost Monastery"},
3230         {"cp05",                "Steamboat"},
3231         {"cp06",                "Graveyard"},
3232         {"cp07",                "Mountain Pass"},
3233         {"cp08",                "Abysmal Mine"},
3234         {"cp09",                "Castle"},
3235         {"cps1",                "Boggy Creek"},
3236
3237         {"cpbb01",              "Crypt of Despair"},
3238         {"cpbb02",              "Pits of Blood"},
3239         {"cpbb03",              "Unholy Cathedral"},
3240         {"cpbb04",              "Deadly Inspirations"},
3241
3242         {"b2a15",               "Area 15 (B2)"},
3243         {"barena",              "Blood Arena"},
3244         {"bkeep",               "Blood Keep"},
3245         {"bstar",               "Brown Star"},
3246         {"crypt",               "The Crypt"},
3247
3248         {"bb3_2k1",             "Bodies Infusion"},
3249         {"captasao",    "Captasao"},
3250         {"curandero",   "Curandero"},
3251         {"dcamp",               "DeathCamp"},
3252         {"highnoon",    "HighNoon"},
3253         {"qbb1",                "The Confluence"},
3254         {"qbb2",                "KathartiK"},
3255         {"qbb3",                "Caleb's Woodland Retreat"},
3256         {"zoo",                 "Zoo"},
3257
3258         {"dranzbb6",    "Black Coffee"},
3259         {"fragm",               "Frag'M"},
3260         {"maim",                "Maim"},
3261         {"qe1m7",               "The House of Chthon"},
3262         {"qmorbias",    "Dm-Morbias"},
3263         {"simple",              "Dead Simple"}
3264 };
3265
3266 episode_t       transfusionepisodes[] =
3267 {
3268         {"The Way of All Flesh", 0, 8},
3269         {"Even Death May Die", 8, 9},
3270         {"Farewell to Arms", 17, 8},
3271         {"Dead Reckoning", 25, 9},
3272         {"BloodBath", 34, 11},
3273         {"Post Mortem", 45, 9},
3274         {"Cryptic Passage", 54, 10},
3275         {"Cryptic BloodBath", 64, 4},
3276         {"Blood 2", 68, 5},
3277         {"Transfusion", 73, 9},
3278         {"Conversions", 82, 6}
3279 };
3280
3281 level_t goodvsbad2levels[] =
3282 {
3283         {"rts", "Many Paths"},  // 0
3284         {"chess", "Chess, Scott Hess"},                         // 1
3285         {"dot", "Big Wall"},
3286         {"city2", "The Big City"},
3287         {"bwall", "0 G like Psychic TV"},
3288         {"snow", "Wireframed"},
3289         {"telep", "Infinite Falling"},
3290         {"faces", "Facing Bases"},
3291         {"island", "Adventure Islands"},
3292 };
3293
3294 episode_t goodvsbad2episodes[] =
3295 {
3296         {"Levels? Bevels!", 0, 8},
3297 };
3298
3299 level_t battlemechlevels[] =
3300 {
3301         {"start", "Parking Level"},
3302         {"dm1", "Hot Dump"},                        // 1
3303         {"dm2", "The Pits"},
3304         {"dm3", "Dimber Died"},
3305         {"dm4", "Fire in the Hole"},
3306         {"dm5", "Clubhouses"},
3307         {"dm6", "Army go Underground"},
3308 };
3309
3310 episode_t battlemechepisodes[] =
3311 {
3312         {"Time for Battle", 0, 7},
3313 };
3314
3315 level_t openquartzlevels[] =
3316 {
3317         {"start", "Welcome to Openquartz"},
3318
3319         {"void1", "The center of nowhere"},                        // 1
3320         {"void2", "The place with no name"},
3321         {"void3", "The lost supply base"},
3322         {"void4", "Past the outer limits"},
3323         {"void5", "Into the nonexistance"},
3324         {"void6", "Void walk"},
3325
3326         {"vtest", "Warp Central"},
3327         {"box", "The deathmatch box"},
3328         {"bunkers", "Void command"},
3329         {"house", "House of chaos"},
3330         {"office", "Overnight office kill"},
3331         {"am1", "The nameless chambers"},
3332 };
3333
3334 episode_t openquartzepisodes[] =
3335 {
3336         {"Single Player", 0, 1},
3337         {"Void Deathmatch", 1, 6},
3338         {"Contrib", 7, 6},
3339 };
3340
3341 gamelevels_t sharewarequakegame = {"Shareware Quake", quakelevels, quakeepisodes, 2};
3342 gamelevels_t registeredquakegame = {"Quake", quakelevels, quakeepisodes, 7};
3343 gamelevels_t hipnoticgame = {"Scourge of Armagon", hipnoticlevels, hipnoticepisodes, 6};
3344 gamelevels_t roguegame = {"Dissolution of Eternity", roguelevels, rogueepisodes, 4};
3345 gamelevels_t nehahragame = {"Nehahra", nehahralevels, nehahraepisodes, 4};
3346 gamelevels_t transfusiongame = {"Transfusion", transfusionlevels, transfusionepisodes, 11};
3347 gamelevels_t goodvsbad2game = {"Good Vs. Bad 2", goodvsbad2levels, goodvsbad2episodes, 1};
3348 gamelevels_t battlemechgame = {"Battlemech", battlemechlevels, battlemechepisodes, 1};
3349 gamelevels_t openquartzgame = {"OpenQuartz", openquartzlevels, openquartzepisodes, 3};
3350
3351 typedef struct
3352 {
3353         int gameid;
3354         gamelevels_t *notregistered;
3355         gamelevels_t *registered;
3356 }
3357 gameinfo_t;
3358
3359 gameinfo_t gamelist[] =
3360 {
3361         {GAME_NORMAL, &sharewarequakegame, &registeredquakegame},
3362         {GAME_HIPNOTIC, &hipnoticgame, &hipnoticgame},
3363         {GAME_ROGUE, &roguegame, &roguegame},
3364         {GAME_NEHAHRA, &nehahragame, &nehahragame},
3365         {GAME_TRANSFUSION, &transfusiongame, &transfusiongame},
3366         {GAME_GOODVSBAD2, &goodvsbad2game, &goodvsbad2game},
3367         {GAME_BATTLEMECH, &battlemechgame, &battlemechgame},
3368         {GAME_OPENQUARTZ, &openquartzgame, &openquartzgame},
3369         {-1, &sharewarequakegame, &registeredquakegame} // final fallback
3370 };
3371
3372 gamelevels_t *lookupgameinfo(void)
3373 {
3374         int i;
3375         for (i = 0;gamelist[i].gameid >= 0 && gamelist[i].gameid != gamemode;i++);
3376         if (registered.integer)
3377                 return gamelist[i].registered;
3378         else
3379                 return gamelist[i].notregistered;
3380 }
3381
3382 int     startepisode;
3383 int     startlevel;
3384 int maxplayers;
3385 qboolean m_serverInfoMessage = false;
3386 double m_serverInfoMessageTime;
3387
3388 void M_Menu_GameOptions_f (void)
3389 {
3390         key_dest = key_menu;
3391         m_state = m_gameoptions;
3392         m_entersound = true;
3393         if (maxplayers == 0)
3394                 maxplayers = svs.maxclients;
3395         if (maxplayers < 2)
3396                 maxplayers = min(8, MAX_SCOREBOARD);
3397 }
3398
3399
3400 int gameoptions_cursor_table[] = {40, 56, 64, 72, 80, 88, 96, 104, 112, 140, 160, 168};
3401 #define NUM_GAMEOPTIONS 12
3402 int             gameoptions_cursor;
3403
3404 void M_GameOptions_Draw (void)
3405 {
3406         cachepic_t      *p;
3407         int             x;
3408         gamelevels_t *g;
3409
3410         M_Background(320, 200);
3411
3412         M_DrawPic (16, 4, "gfx/qplaque.lmp");
3413         p = Draw_CachePic ("gfx/p_multi.lmp");
3414         M_DrawPic ( (320-p->width)/2, 4, "gfx/p_multi.lmp");
3415
3416         M_DrawTextBox (152, 32, 10, 1);
3417         M_Print(160, 40, "begin game");
3418
3419         M_Print(0, 56, "      Max players");
3420         M_Print(160, 56, va("%i", maxplayers) );
3421
3422         if (gamemode != GAME_GOODVSBAD2)
3423         {
3424                 M_Print(0, 64, "        Game Type");
3425                 if (gamemode == GAME_TRANSFUSION)
3426                 {
3427                         if (!coop.integer && !deathmatch.integer)
3428                                 Cvar_SetValue("deathmatch", 1);
3429                         if (deathmatch.integer == 0)
3430                                 M_Print(160, 64, "Cooperative");
3431                         else if (deathmatch.integer == 2)
3432                                 M_Print(160, 64, "Capture the Flag");
3433                         else
3434                                 M_Print(160, 64, "Blood Bath");
3435                 }
3436                 else if (gamemode == GAME_BATTLEMECH)
3437                 {
3438                         if (!deathmatch.integer)
3439                                 Cvar_SetValue("deathmatch", 1);
3440                         if (deathmatch.integer == 2)
3441                                 M_Print(160, 64, "Rambo Match");
3442                         else
3443                                 M_Print(160, 64, "Deathmatch");
3444                 }
3445                 else
3446                 {
3447                         if (!coop.integer && !deathmatch.integer)
3448                                 Cvar_SetValue("deathmatch", 1);
3449                         if (coop.integer)
3450                                 M_Print(160, 64, "Cooperative");
3451                         else
3452                                 M_Print(160, 64, "Deathmatch");
3453                 }
3454
3455                 M_Print(0, 72, "        Teamplay");
3456                 if (gamemode == GAME_ROGUE)
3457                 {
3458                         char *msg;
3459
3460                         switch((int)teamplay.integer)
3461                         {
3462                                 case 1: msg = "No Friendly Fire"; break;
3463                                 case 2: msg = "Friendly Fire"; break;
3464                                 case 3: msg = "Tag"; break;
3465                                 case 4: msg = "Capture the Flag"; break;
3466                                 case 5: msg = "One Flag CTF"; break;
3467                                 case 6: msg = "Three Team CTF"; break;
3468                                 default: msg = "Off"; break;
3469                         }
3470                         M_Print(160, 72, msg);
3471                 }
3472                 else
3473                 {
3474                         char *msg;
3475
3476                         switch (teamplay.integer)
3477                         {
3478                                 case 0: msg = "Off"; break;
3479                                 case 2: msg = "Friendly Fire"; break;
3480                                 default: msg = "No Friendly Fire"; break;
3481                         }
3482                         M_Print(160, 72, msg);
3483                 }
3484
3485                 M_Print(0, 80, "            Skill");
3486                 if (skill.integer == 0)
3487                         M_Print(160, 80, "Easy difficulty");
3488                 else if (skill.integer == 1)
3489                         M_Print(160, 80, "Normal difficulty");
3490                 else if (skill.integer == 2)
3491                         M_Print(160, 80, "Hard difficulty");
3492                 else
3493                         M_Print(160, 80, "Nightmare difficulty");
3494
3495                 M_Print(0, 88, "       Frag Limit");
3496                 if (fraglimit.integer == 0)
3497                         M_Print(160, 88, "none");
3498                 else
3499                         M_Print(160, 88, va("%i frags", fraglimit.integer));
3500
3501                 M_Print(0, 96, "       Time Limit");
3502                 if (timelimit.integer == 0)
3503                         M_Print(160, 96, "none");
3504                 else
3505                         M_Print(160, 96, va("%i minutes", timelimit.integer));
3506         }
3507
3508         M_Print(0, 104, "    Public server");
3509         M_Print(160, 104, (sv_public.integer == 0) ? "no" : "yes");
3510
3511         M_Print(0, 112, "   Server maxrate");
3512         M_Print(160, 112, va("%i", sv_maxrate.integer));
3513
3514         M_Print(0, 128, "      Server name");
3515         M_DrawTextBox (0, 132, 38, 1);
3516         M_Print(8, 140, hostname.string);
3517
3518         g = lookupgameinfo();
3519
3520         if (gamemode != GAME_GOODVSBAD2)
3521         {
3522                 M_Print(0, 160, "         Episode");
3523                 M_Print(160, 160, g->episodes[startepisode].description);
3524         }
3525
3526         M_Print(0, 168, "           Level");
3527         M_Print(160, 168, g->levels[g->episodes[startepisode].firstLevel + startlevel].description);
3528         M_Print(160, 176, g->levels[g->episodes[startepisode].firstLevel + startlevel].name);
3529
3530 // line cursor
3531         if (gameoptions_cursor == 9)
3532                 M_DrawCharacter (8 + 8 * strlen(hostname.string), gameoptions_cursor_table[gameoptions_cursor], 10+((int)(realtime*4)&1));
3533         else
3534                 M_DrawCharacter (144, gameoptions_cursor_table[gameoptions_cursor], 12+((int)(realtime*4)&1));
3535
3536         if (m_serverInfoMessage)
3537         {
3538                 if ((realtime - m_serverInfoMessageTime) < 5.0)
3539                 {
3540                         x = (320-26*8)/2;
3541                         M_DrawTextBox (x, 138, 24, 4);
3542                         x += 8;
3543                         M_Print(x, 146, " More than 64 players?? ");
3544                         M_Print(x, 154, "  First, question your  ");
3545                         M_Print(x, 162, "   sanity, then email   ");
3546                         M_Print(x, 170, " havoc@telefragged.com  ");
3547                 }
3548                 else
3549                         m_serverInfoMessage = false;
3550         }
3551 }
3552
3553
3554 void M_NetStart_Change (int dir)
3555 {
3556         gamelevels_t *g;
3557         int count;
3558
3559         switch (gameoptions_cursor)
3560         {
3561         case 1:
3562                 maxplayers += dir;
3563                 if (maxplayers > MAX_SCOREBOARD)
3564                 {
3565                         maxplayers = MAX_SCOREBOARD;
3566                         m_serverInfoMessage = true;
3567                         m_serverInfoMessageTime = realtime;
3568                 }
3569                 if (maxplayers < 2)
3570                         maxplayers = 2;
3571                 break;
3572
3573         case 2:
3574                 if (gamemode == GAME_GOODVSBAD2)
3575                         break;
3576                 if (gamemode == GAME_TRANSFUSION)
3577                 {
3578                         switch (deathmatch.integer)
3579                         {
3580                                 // From Cooperative to BloodBath
3581                                 case 0:
3582                                         Cvar_SetValueQuick (&coop, 0);
3583                                         Cvar_SetValueQuick (&deathmatch, 1);
3584                                         break;
3585
3586                                 // From BloodBath to CTF
3587                                 case 1:
3588                                         Cvar_SetValueQuick (&coop, 0);
3589                                         Cvar_SetValueQuick (&deathmatch, 2);
3590                                         break;
3591
3592                                 // From CTF to Cooperative
3593                                 //case 2:
3594                                 default:
3595                                         Cvar_SetValueQuick (&coop, 1);
3596                                         Cvar_SetValueQuick (&deathmatch, 0);
3597                         }
3598                 }
3599                 else if (gamemode == GAME_BATTLEMECH)
3600                 {
3601                         if (deathmatch.integer == 2) // changing from Rambo to Deathmatch
3602                                 Cvar_SetValueQuick (&deathmatch, 0);
3603                         else // changing from Deathmatch to Rambo
3604                                 Cvar_SetValueQuick (&deathmatch, 2);
3605                 }
3606                 else
3607                 {
3608                         if (deathmatch.integer) // changing from deathmatch to coop
3609                         {
3610                                 Cvar_SetValueQuick (&coop, 1);
3611                                 Cvar_SetValueQuick (&deathmatch, 0);
3612                         }
3613                         else // changing from coop to deathmatch
3614                         {
3615                                 Cvar_SetValueQuick (&coop, 0);
3616                                 Cvar_SetValueQuick (&deathmatch, 1);
3617                         }
3618                 }
3619                 break;
3620
3621         case 3:
3622                 if (gamemode == GAME_GOODVSBAD2)
3623                         break;
3624                 if (gamemode == GAME_ROGUE)
3625                         count = 6;
3626                 else
3627                         count = 2;
3628
3629                 Cvar_SetValueQuick (&teamplay, teamplay.integer + dir);
3630                 if (teamplay.integer > count)
3631                         Cvar_SetValueQuick (&teamplay, 0);
3632                 else if (teamplay.integer < 0)
3633                         Cvar_SetValueQuick (&teamplay, count);
3634                 break;
3635
3636         case 4:
3637                 if (gamemode == GAME_GOODVSBAD2)
3638                         break;
3639                 Cvar_SetValueQuick (&skill, skill.integer + dir);
3640                 if (skill.integer > 3)
3641                         Cvar_SetValueQuick (&skill, 0);
3642                 if (skill.integer < 0)
3643                         Cvar_SetValueQuick (&skill, 3);
3644                 break;
3645
3646         case 5:
3647                 if (gamemode == GAME_GOODVSBAD2)
3648                         break;
3649                 Cvar_SetValueQuick (&fraglimit, fraglimit.integer + dir*10);
3650                 if (fraglimit.integer > 100)
3651                         Cvar_SetValueQuick (&fraglimit, 0);
3652                 if (fraglimit.integer < 0)
3653                         Cvar_SetValueQuick (&fraglimit, 100);
3654                 break;
3655
3656         case 6:
3657                 if (gamemode == GAME_GOODVSBAD2)
3658                         break;
3659                 Cvar_SetValueQuick (&timelimit, timelimit.value + dir*5);
3660                 if (timelimit.value > 60)
3661                         Cvar_SetValueQuick (&timelimit, 0);
3662                 if (timelimit.value < 0)
3663                         Cvar_SetValueQuick (&timelimit, 60);
3664                 break;
3665
3666         case 7:
3667                 Cvar_SetValueQuick (&sv_public, !sv_public.integer);
3668                 break;
3669
3670         case 8:
3671                 Cvar_SetValueQuick (&sv_maxrate, sv_maxrate.integer + dir*500);
3672                 if (sv_maxrate.integer > NET_MAXRATE)
3673                         Cvar_SetValueQuick (&sv_maxrate, NET_MAXRATE);
3674                 if (sv_maxrate.integer < NET_MINRATE)
3675                         Cvar_SetValueQuick (&sv_maxrate, NET_MINRATE);
3676                 break;
3677
3678         case 9:
3679                 break;
3680
3681         case 10:
3682                 if (gamemode == GAME_GOODVSBAD2)
3683                         break;
3684                 startepisode += dir;
3685                 g = lookupgameinfo();
3686
3687                 if (startepisode < 0)
3688                         startepisode = g->numepisodes - 1;
3689
3690                 if (startepisode >= g->numepisodes)
3691                         startepisode = 0;
3692
3693                 startlevel = 0;
3694                 break;
3695
3696         case 11:
3697                 startlevel += dir;
3698                 g = lookupgameinfo();
3699
3700                 if (startlevel < 0)
3701                         startlevel = g->episodes[startepisode].levels - 1;
3702
3703                 if (startlevel >= g->episodes[startepisode].levels)
3704                         startlevel = 0;
3705                 break;
3706         }
3707 }
3708
3709 void M_GameOptions_Key (int key, char ascii)
3710 {
3711         gamelevels_t *g;
3712         int l;
3713         char hostnamebuf[128];
3714
3715         switch (key)
3716         {
3717         case K_ESCAPE:
3718                 M_Menu_MultiPlayer_f ();
3719                 break;
3720
3721         case K_UPARROW:
3722                 S_LocalSound ("misc/menu1.wav", true);
3723                 gameoptions_cursor--;
3724                 if (gameoptions_cursor < 0)
3725                         gameoptions_cursor = NUM_GAMEOPTIONS-1;
3726                 break;
3727
3728         case K_DOWNARROW:
3729                 S_LocalSound ("misc/menu1.wav", true);
3730                 gameoptions_cursor++;
3731                 if (gameoptions_cursor >= NUM_GAMEOPTIONS)
3732                         gameoptions_cursor = 0;
3733                 break;
3734
3735         case K_LEFTARROW:
3736                 if (gameoptions_cursor == 0)
3737                         break;
3738                 S_LocalSound ("misc/menu3.wav", true);
3739                 M_NetStart_Change (-1);
3740                 break;
3741
3742         case K_RIGHTARROW:
3743                 if (gameoptions_cursor == 0)
3744                         break;
3745                 S_LocalSound ("misc/menu3.wav", true);
3746                 M_NetStart_Change (1);
3747                 break;
3748
3749         case K_ENTER:
3750                 S_LocalSound ("misc/menu2.wav", true);
3751                 if (gameoptions_cursor == 0)
3752                 {
3753                         if (sv.active)
3754                                 Cbuf_AddText ("disconnect\n");
3755                         Cbuf_AddText ( va ("maxplayers %u\n", maxplayers) );
3756
3757                         g = lookupgameinfo();
3758                         Cbuf_AddText ( va ("map %s\n", g->levels[g->episodes[startepisode].firstLevel + startlevel].name) );
3759                         return;
3760                 }
3761
3762                 M_NetStart_Change (1);
3763                 break;
3764
3765         case K_BACKSPACE:
3766                 if (gameoptions_cursor == 9)
3767                 {
3768                         l = strlen(hostname.string);
3769                         if (l)
3770                         {
3771                                 l = min(l - 1, 37);
3772                                 memcpy(hostnamebuf, hostname.string, l);
3773                                 hostnamebuf[l] = 0;
3774                                 Cvar_Set("hostname", hostnamebuf);
3775                         }
3776                 }
3777                 break;
3778
3779         default:
3780                 if (ascii < 32 || ascii > 126)
3781                         break;
3782                 if (gameoptions_cursor == 9)
3783                 {
3784                         l = strlen(hostname.string);
3785                         if (l < 37)
3786                         {
3787                                 memcpy(hostnamebuf, hostname.string, l);
3788                                 hostnamebuf[l] = ascii;
3789                                 hostnamebuf[l+1] = 0;
3790                                 Cvar_Set("hostname", hostnamebuf);
3791                         }
3792                 }
3793         }
3794 }
3795
3796 //=============================================================================
3797 /* SLIST MENU */
3798
3799 int slist_cursor;
3800
3801 void M_Menu_ServerList_f (void)
3802 {
3803         key_dest = key_menu;
3804         m_state = m_slist;
3805         m_entersound = true;
3806         slist_cursor = 0;
3807         M_Update_Return_Reason("");
3808         Net_Slist_f();
3809 }
3810
3811
3812 void M_ServerList_Draw (void)
3813 {
3814         int n, y, visible, start, end;
3815         cachepic_t *p;
3816         const char *s;
3817
3818         // use as much vertical space as available
3819         M_Background(640, vid.conheight);
3820         // scroll the list as the cursor moves
3821         s = va("%i/%i masters %i/%i servers", masterreplycount, masterquerycount, serverreplycount, serverquerycount);
3822         M_PrintRed((640 - strlen(s) * 8) / 2, 32, s);
3823         if (*m_return_reason)
3824                 M_Print(16, vid.conheight - 8, m_return_reason);
3825         y = 48;
3826         visible = (vid.conheight - 16 - y) / 8;
3827         start = bound(0, slist_cursor - (visible >> 1), hostCacheCount - visible);
3828         end = min(start + visible, hostCacheCount);
3829
3830         p = Draw_CachePic("gfx/p_multi.lmp");
3831         M_DrawPic((640 - p->width) / 2, 4, "gfx/p_multi.lmp");
3832         if (end > start)
3833         {
3834                 for (n = start;n < end;n++)
3835                 {
3836                         DrawQ_Fill(menu_x, menu_y + y, 640, 16, n == slist_cursor ? (0.5 + 0.2 * sin(realtime * M_PI)) : 0, 0, 0, 0.5, 0);
3837                         M_Print(0, y, hostcache[n].line1);y += 8;
3838                         M_Print(0, y, hostcache[n].line2);y += 8;
3839                 }
3840         }
3841         else if (realtime - masterquerytime < 3)
3842         {
3843                 if (masterquerycount)
3844                         M_Print(0, y, "No servers found");
3845                 else
3846                         M_Print(0, y, "No master servers found (network problem?)");
3847         }
3848 }
3849
3850
3851 void M_ServerList_Key(int k, char ascii)
3852 {
3853         switch (k)
3854         {
3855         case K_ESCAPE:
3856                 M_Menu_LanConfig_f();
3857                 break;
3858
3859         case K_SPACE:
3860                 Net_Slist_f();
3861                 break;
3862
3863         case K_UPARROW:
3864         case K_LEFTARROW:
3865                 S_LocalSound("misc/menu1.wav", true);
3866                 slist_cursor--;
3867                 if (slist_cursor < 0)
3868                         slist_cursor = hostCacheCount - 1;
3869                 break;
3870
3871         case K_DOWNARROW:
3872         case K_RIGHTARROW:
3873                 S_LocalSound("misc/menu1.wav", true);
3874                 slist_cursor++;
3875                 if (slist_cursor >= hostCacheCount)
3876                         slist_cursor = 0;
3877                 break;
3878
3879         case K_ENTER:
3880                 S_LocalSound("misc/menu2.wav", true);
3881                 Cbuf_AddText(va("connect \"%s\"\n", hostcache[slist_cursor].cname));
3882                 break;
3883
3884         default:
3885                 break;
3886         }
3887
3888 }
3889
3890 //=============================================================================
3891 /* Menu Subsystem */
3892
3893 void M_Keydown(int key, char ascii);
3894 void M_Draw(void);
3895 void M_ToggleMenu_f(void);
3896 void M_Shutdown(void);
3897
3898 void M_Init (void)
3899 {
3900         menu_mempool = Mem_AllocPool("Menu", 0, NULL);
3901         menuplyr_load = true;
3902         menuplyr_pixels = NULL;
3903
3904         Cmd_AddCommand ("menu_main", M_Menu_Main_f);
3905         Cmd_AddCommand ("menu_singleplayer", M_Menu_SinglePlayer_f);
3906         Cmd_AddCommand ("menu_load", M_Menu_Load_f);
3907         Cmd_AddCommand ("menu_save", M_Menu_Save_f);
3908         Cmd_AddCommand ("menu_multiplayer", M_Menu_MultiPlayer_f);
3909         Cmd_AddCommand ("menu_setup", M_Menu_Setup_f);
3910         Cmd_AddCommand ("menu_options", M_Menu_Options_f);
3911         Cmd_AddCommand ("menu_options_effects", M_Menu_Options_Effects_f);
3912         Cmd_AddCommand ("menu_options_graphics", M_Menu_Options_Graphics_f);
3913         Cmd_AddCommand ("menu_options_colorcontrol", M_Menu_Options_ColorControl_f);
3914         Cvar_RegisterVariable (&menu_options_colorcontrol_correctionvalue);
3915         Cmd_AddCommand ("menu_keys", M_Menu_Keys_f);
3916         Cmd_AddCommand ("menu_video", M_Menu_Video_f);
3917         Cmd_AddCommand ("menu_reset", M_Menu_Reset_f);
3918         Cmd_AddCommand ("help", M_Menu_Help_f);
3919         Cmd_AddCommand ("menu_quit", M_Menu_Quit_f);
3920
3921         if (gamemode == GAME_TRANSFUSION)
3922         {
3923                 numcommands = sizeof(transfusionbindnames) / sizeof(transfusionbindnames[0]);
3924                 bindnames = transfusionbindnames;
3925         }
3926         else if (gamemode == GAME_GOODVSBAD2)
3927         {
3928                 numcommands = sizeof(goodvsbad2bindnames) / sizeof(goodvsbad2bindnames[0]);
3929                 bindnames = goodvsbad2bindnames;
3930         }
3931         else
3932         {
3933                 numcommands = sizeof(quakebindnames) / sizeof(quakebindnames[0]);
3934                 bindnames = quakebindnames;
3935         }
3936
3937         // Make sure "keys_cursor" doesn't start on a section in the binding list
3938         keys_cursor = 0;
3939         while (bindnames[keys_cursor][0][0] == '\0')
3940         {
3941                 keys_cursor++;
3942
3943                 // Only sections? There may be a problem somewhere...
3944                 if (keys_cursor >= numcommands)
3945                         Sys_Error ("M_Init: The key binding list only contains sections");
3946         }
3947
3948
3949         if (gamemode == GAME_NEHAHRA)
3950         {
3951                 if (FS_FileExists("maps/neh1m4.bsp"))
3952                 {
3953                         if (FS_FileExists("hearing.dem"))
3954                         {
3955                                 Con_Print("Nehahra movie and game detected.\n");
3956                                 NehGameType = TYPE_BOTH;
3957                         }
3958                         else
3959                         {
3960                                 Con_Print("Nehahra game detected.\n");
3961                                 NehGameType = TYPE_GAME;
3962                         }
3963                 }
3964                 else
3965                 {
3966                         if (FS_FileExists("hearing.dem"))
3967                         {
3968                                 Con_Print("Nehahra movie detected.\n");
3969                                 NehGameType = TYPE_DEMO;
3970                         }
3971                         else
3972                         {
3973                                 Con_Print("Nehahra not found.\n");
3974                                 NehGameType = TYPE_GAME; // could just complain, but...
3975                         }
3976                 }
3977         }
3978 }
3979
3980 void M_Draw (void)
3981 {
3982         if (key_dest != key_menu)
3983                 m_state = m_none;
3984
3985         if (m_state == m_none)
3986                 return;
3987
3988         switch (m_state)
3989         {
3990         case m_none:
3991                 break;
3992
3993         case m_main:
3994                 M_Main_Draw ();
3995                 break;
3996
3997         case m_demo:
3998                 M_Demo_Draw ();
3999                 break;
4000
4001         case m_singleplayer:
4002                 M_SinglePlayer_Draw ();
4003                 break;
4004
4005         case m_load:
4006                 M_Load_Draw ();
4007                 break;
4008
4009         case m_save:
4010                 M_Save_Draw ();
4011                 break;
4012
4013         case m_multiplayer:
4014                 M_MultiPlayer_Draw ();
4015                 break;
4016
4017         case m_setup:
4018                 M_Setup_Draw ();
4019                 break;
4020
4021         case m_options:
4022                 M_Options_Draw ();
4023                 break;
4024
4025         case m_options_effects:
4026                 M_Options_Effects_Draw ();
4027                 break;
4028
4029         case m_options_graphics:
4030                 M_Options_Graphics_Draw ();
4031                 break;
4032
4033         case m_options_colorcontrol:
4034                 M_Options_ColorControl_Draw ();
4035                 break;
4036
4037         case m_keys:
4038                 M_Keys_Draw ();
4039                 break;
4040
4041         case m_reset:
4042                 M_Reset_Draw ();
4043                 break;
4044
4045         case m_video:
4046                 M_Video_Draw ();
4047                 break;
4048
4049         case m_help:
4050                 M_Help_Draw ();
4051                 break;
4052
4053         case m_quit:
4054                 M_Quit_Draw ();
4055                 break;
4056
4057         case m_lanconfig:
4058                 M_LanConfig_Draw ();
4059                 break;
4060
4061         case m_gameoptions:
4062                 M_GameOptions_Draw ();
4063                 break;
4064
4065         case m_slist:
4066                 M_ServerList_Draw ();
4067                 break;
4068         }
4069
4070         if (m_entersound)
4071         {
4072                 S_LocalSound ("misc/menu2.wav", true);
4073                 m_entersound = false;
4074         }
4075
4076         S_ExtraUpdate ();
4077 }
4078
4079
4080 void M_Keydown (int key, char ascii)
4081 {
4082         switch (m_state)
4083         {
4084         case m_none:
4085                 return;
4086
4087         case m_main:
4088                 M_Main_Key (key, ascii);
4089                 return;
4090
4091         case m_demo:
4092                 M_Demo_Key (key, ascii);
4093                 return;
4094
4095         case m_singleplayer:
4096                 M_SinglePlayer_Key (key, ascii);
4097                 return;
4098
4099         case m_load:
4100                 M_Load_Key (key, ascii);
4101                 return;
4102
4103         case m_save:
4104                 M_Save_Key (key, ascii);
4105                 return;
4106
4107         case m_multiplayer:
4108                 M_MultiPlayer_Key (key, ascii);
4109                 return;
4110
4111         case m_setup:
4112                 M_Setup_Key (key, ascii);
4113                 return;
4114
4115         case m_options:
4116                 M_Options_Key (key, ascii);
4117                 return;
4118
4119         case m_options_effects:
4120                 M_Options_Effects_Key (key, ascii);
4121                 return;
4122
4123         case m_options_graphics:
4124                 M_Options_Graphics_Key (key, ascii);
4125                 return;
4126
4127         case m_options_colorcontrol:
4128                 M_Options_ColorControl_Key (key, ascii);
4129                 return;
4130
4131         case m_keys:
4132                 M_Keys_Key (key, ascii);
4133                 return;
4134
4135         case m_reset:
4136                 M_Reset_Key (key, ascii);
4137                 return;
4138
4139
4140         case m_video:
4141                 M_Video_Key (key, ascii);
4142                 return;
4143
4144         case m_help:
4145                 M_Help_Key (key, ascii);
4146                 return;
4147
4148         case m_quit:
4149                 M_Quit_Key (key, ascii);
4150                 return;
4151
4152         case m_lanconfig:
4153                 M_LanConfig_Key (key, ascii);
4154                 return;
4155
4156         case m_gameoptions:
4157                 M_GameOptions_Key (key, ascii);
4158                 return;
4159
4160         case m_slist:
4161                 M_ServerList_Key (key, ascii);
4162                 return;
4163         }
4164 }
4165
4166 void M_Shutdown(void)
4167 {
4168         // reset key_dest
4169         key_dest = key_game;
4170 }
4171
4172 void M_Restart(void)
4173 {
4174 }
4175
4176 //============================================================================
4177 // Menu prog handling
4178 mfunction_t *PRVM_ED_FindFunction(const char *);
4179
4180 #define M_F_INIT                "m_init"
4181 #define M_F_KEYDOWN             "m_keydown"
4182 #define M_F_DRAW                "m_draw"
4183 // ng_menu function names
4184 #define M_F_DISPLAY             "m_display"
4185 #define M_F_HIDE                "m_hide"
4186 // normal menu names (rest)
4187 #define M_F_TOGGLE              "m_toggle"
4188 #define M_F_SHUTDOWN    "m_shutdown"
4189 #undef NG_MENU
4190 static char *m_required_func[] = {
4191 M_F_INIT,
4192 M_F_KEYDOWN,
4193 M_F_DRAW,
4194 #ifdef NG_MENU
4195 M_F_DISPLAY,
4196 M_F_HIDE,
4197 #else
4198 M_F_TOGGLE,
4199 #endif
4200 M_F_SHUTDOWN,
4201 };
4202
4203 #ifdef NG_MENU
4204 qboolean m_displayed;
4205 #endif
4206
4207 static int m_numrequiredfunc = sizeof(m_required_func) / sizeof(char*);
4208
4209 static func_t m_draw, m_keydown;
4210
4211 void MR_SetRouting (qboolean forceold);
4212
4213 void MP_Error(void)
4214 {
4215         // fall back to the normal menu
4216
4217         // say it
4218         Con_Print("Falling back to normal menu\n");
4219
4220         key_dest = key_game;
4221
4222         //PRVM_ResetProg();
4223
4224         // init the normal menu now -> this will also correct the menu router pointers
4225         MR_SetRouting (TRUE);
4226 }
4227
4228 void MP_Keydown (int key, char ascii)
4229 {
4230         PRVM_Begin;
4231         PRVM_SetProg(PRVM_MENUPROG);
4232
4233         // set time
4234         *prog->time = realtime;
4235
4236         // pass key
4237         prog->globals[OFS_PARM0] = (float) key;
4238         prog->globals[OFS_PARM1] = (float) ascii;
4239         PRVM_ExecuteProgram(m_keydown, M_F_KEYDOWN"(float key, float ascii) required\n");
4240
4241         PRVM_End;
4242 }
4243
4244 void MP_Draw (void)
4245 {
4246         PRVM_Begin;
4247         PRVM_SetProg(PRVM_MENUPROG);
4248
4249         // set time
4250         *prog->time = realtime;
4251
4252         PRVM_ExecuteProgram(m_draw,"");
4253
4254         PRVM_End;
4255 }
4256
4257 void MP_ToggleMenu_f (void)
4258 {
4259         PRVM_Begin;
4260         PRVM_SetProg(PRVM_MENUPROG);
4261
4262         // set time
4263         *prog->time = realtime;
4264
4265 #ifdef NG_MENU
4266         m_displayed = !m_displayed;
4267         if( m_displayed )
4268                 PRVM_ExecuteProgram((func_t) (PRVM_ED_FindFunction(M_F_DISPLAY) - prog->functions),"");
4269         else
4270                 PRVM_ExecuteProgram((func_t) (PRVM_ED_FindFunction(M_F_HIDE) - prog->functions),"");
4271 #else
4272         PRVM_ExecuteProgram((func_t) (PRVM_ED_FindFunction(M_F_TOGGLE) - prog->functions),"");
4273 #endif
4274
4275         PRVM_End;
4276 }
4277
4278 void MP_Shutdown (void)
4279 {
4280         PRVM_Begin;
4281         PRVM_SetProg(PRVM_MENUPROG);
4282
4283         // set time
4284         *prog->time = realtime;
4285
4286         PRVM_ExecuteProgram((func_t) (PRVM_ED_FindFunction(M_F_SHUTDOWN) - prog->functions),"");
4287
4288         // reset key_dest
4289         key_dest = key_game;
4290
4291         // AK not using this cause Im not sure whether this is useful at all instead :
4292         PRVM_ResetProg();
4293
4294         PRVM_End;
4295 }
4296
4297 void MP_Init (void)
4298 {
4299         PRVM_Begin;
4300         PRVM_InitProg(PRVM_MENUPROG);
4301
4302         prog->crc = M_PROGHEADER_CRC;
4303         prog->edictprivate_size = 0; // no private struct used
4304         prog->name = M_NAME;
4305         prog->limit_edicts = M_MAX_EDICTS;
4306         prog->extensionstring = vm_m_extensions;
4307         prog->builtins = vm_m_builtins;
4308         prog->numbuiltins = vm_m_numbuiltins;
4309         prog->init_cmd = VM_M_Cmd_Init;
4310         prog->reset_cmd = VM_M_Cmd_Reset;
4311         prog->error_cmd = MP_Error;
4312
4313         // allocate the mempools
4314         prog->edicts_mempool = Mem_AllocPool(M_NAME " edicts mempool", 0, NULL);
4315         prog->edictstring_mempool = Mem_AllocPool( M_NAME " edict string mempool", 0, NULL);
4316         prog->progs_mempool = Mem_AllocPool(M_PROG_FILENAME, 0, NULL);
4317
4318         PRVM_LoadProgs(M_PROG_FILENAME, m_numrequiredfunc, m_required_func);
4319
4320         // set m_draw and m_keydown
4321         m_draw = (func_t) (PRVM_ED_FindFunction(M_F_DRAW) - prog->functions);
4322         m_keydown = (func_t) (PRVM_ED_FindFunction(M_F_KEYDOWN) - prog->functions);
4323
4324 #ifdef NG_MENU
4325         m_displayed = false;
4326 #endif
4327
4328         // set time
4329         *prog->time = realtime;
4330
4331         // call the prog init
4332         PRVM_ExecuteProgram((func_t) (PRVM_ED_FindFunction(M_F_INIT) - prog->functions),"");
4333
4334         PRVM_End;
4335 }
4336
4337 void MP_Restart(void)
4338 {
4339
4340         MP_Init();
4341 }
4342
4343 //============================================================================
4344 // Menu router
4345
4346 static cvar_t forceqmenu = { 0, "forceqmenu", "0" };
4347
4348 void MR_SetRouting(qboolean forceold)
4349 {
4350         static qboolean m_init = FALSE, mp_init = FALSE;
4351
4352         // if the menu prog isnt available or forceqmenu ist set, use the old menu
4353         if(!FS_FileExists(M_PROG_FILENAME) || forceqmenu.integer || forceold)
4354         {
4355                 // set menu router function pointers
4356                 MR_Keydown = M_Keydown;
4357                 MR_Draw = M_Draw;
4358                 MR_ToggleMenu_f = M_ToggleMenu_f;
4359                 MR_Shutdown = M_Shutdown;
4360
4361                 // init
4362                 if(!m_init)
4363                 {
4364                         M_Init();
4365                         m_init = TRUE;
4366                 }
4367                 else
4368                         M_Restart();
4369         }
4370         else
4371         {
4372                 // set menu router function pointers
4373                 MR_Keydown = MP_Keydown;
4374                 MR_Draw = MP_Draw;
4375                 MR_ToggleMenu_f = MP_ToggleMenu_f;
4376                 MR_Shutdown = MP_Shutdown;
4377
4378                 if(!mp_init)
4379                 {
4380                         MP_Init();
4381                         mp_init = TRUE;
4382                 }
4383                 else
4384                         MP_Restart();
4385         }
4386 }
4387
4388 void MR_Restart(void)
4389 {
4390         MR_Shutdown ();
4391         MR_SetRouting (FALSE);
4392 }
4393
4394 void Call_MR_ToggleMenu_f(void)
4395 {
4396         if(MR_ToggleMenu_f)
4397                 MR_ToggleMenu_f();
4398 }
4399
4400 void MR_Init_Commands(void)
4401 {
4402         // set router console commands
4403         Cvar_RegisterVariable (&forceqmenu);
4404         if (gamemode == GAME_NETHERWORLD)
4405                 Cmd_AddCommand ("menu_fallback", MP_Error); //Force to old-style menu
4406         Cmd_AddCommand ("menu_restart",MR_Restart);
4407         Cmd_AddCommand ("togglemenu", Call_MR_ToggleMenu_f);
4408 }
4409
4410 void MR_Init(void)
4411 {
4412         // use -forceqmenu to use always the normal quake menu (it sets forceqmenu to 1)
4413 // COMMANDLINEOPTION: Client: -forceqmenu disables menu.dat (same as +forceqmenu 1)
4414         if(COM_CheckParm("-forceqmenu"))
4415                 Cvar_SetValueQuick(&forceqmenu,1);
4416         // use -useqmenu for debugging proposes, cause it starts
4417         // the normal quake menu only the first time
4418 // COMMANDLINEOPTION: Client: -useqmenu causes the first time you open the menu to use the quake menu, then reverts to menu.dat (if forceqmenu is 0)
4419         if(COM_CheckParm("-useqmenu"))
4420                 MR_SetRouting (TRUE);
4421         else 
4422                 MR_SetRouting (FALSE);
4423 }