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