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