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