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