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