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