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