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