1 // Emacs style mode select -*- C++ -*-
2 //-----------------------------------------------------------------------------
6 // Copyright (C) 1993-1996 by id Software, Inc.
8 // This source is available for distribution and/or modification
9 // only under the terms of the DOOM Source Code License as
10 // published by id Software. All rights reserved.
12 // The source is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License
20 // DOOM selection menu, options, episode etc.
21 // Sliders and icons. Kinda widget stuff.
23 //-----------------------------------------------------------------------------
26 rcsid[] = "$Id: m_menu.c,v 1.7 1997/02/03 22:45:10 b1 Exp $";
61 extern patch_t* hu_font[HU_FONTSIZE];
62 extern boolean message_dontfuckwithme;
64 extern boolean chat_on; // in heads-up code
69 int mouseSensitivity; // has default
71 // Show messages has default, 0 = off, 1 = on
75 // Blocky mode, has default, 0 = high, 1 = normal
77 int screenblocks; // has default
79 // temp for screenblocks (0-9)
82 // -1 = no quicksave slot picked!
85 // 1 = message to be printed
87 // ...and here is the message string!
93 int messageLastMenuActive;
95 // timed message = no input from user
96 boolean messageNeedsInput;
98 void (*messageRoutine)(int response);
100 #define SAVESTRINGSIZE 24
102 char gammamsg[5][26] =
111 // we are going to be entering a savegame string
113 int saveSlot; // which slot to save in
114 int saveCharIndex; // which char we're editing
115 // old save description before edit
116 char saveOldString[SAVESTRINGSIZE];
118 boolean inhelpscreens;
121 #define SKULLXOFF -32
122 #define LINEHEIGHT 16
124 extern boolean sendpause;
125 char savegamestrings[10][SAVESTRINGSIZE];
135 // 0 = no cursor here, 1 = ok, 2 = arrows ok
140 // choice = menu item #.
142 // choice=0:leftarrow,1:rightarrow
143 void (*routine)(int choice);
151 typedef struct menu_s
153 short numitems; // # of menu items
154 struct menu_s* prevMenu; // previous menu
155 menuitem_t* menuitems; // menu items
156 void (*routine)(void); // draw routine
158 short y; // x,y of menu
159 short lastOn; // last item user was on in menu
162 short itemOn; // menu item skull is on
163 short skullAnimCounter; // skull animation counter
164 short whichSkull; // which skull to draw
166 // graphic name of skulls
167 // warning: initializer-string for array of chars is too long
168 char skullName[2][/*8*/9] = {"M_SKULL1","M_SKULL2"};
176 void M_NewGame(int choice);
177 void M_Episode(int choice);
178 void M_ChooseSkill(int choice);
179 void M_LoadGame(int choice);
180 void M_SaveGame(int choice);
181 void M_Options(int choice);
182 void M_EndGame(int choice);
183 void M_ReadThis(int choice);
184 void M_ReadThis2(int choice);
185 void M_QuitDOOM(int choice);
187 void M_ChangeMessages(int choice);
188 void M_ChangeSensitivity(int choice);
189 void M_SfxVol(int choice);
190 void M_MusicVol(int choice);
191 void M_ChangeDetail(int choice);
192 void M_SizeDisplay(int choice);
193 void M_StartGame(int choice);
194 void M_Sound(int choice);
196 void M_FinishReadThis(int choice);
197 void M_LoadSelect(int choice);
198 void M_SaveSelect(int choice);
199 void M_ReadSaveStrings(void);
200 void M_QuickSave(void);
201 void M_QuickLoad(void);
203 void M_DrawMainMenu(void);
204 void M_DrawReadThis1(void);
205 void M_DrawReadThis2(void);
206 void M_DrawNewGame(void);
207 void M_DrawEpisode(void);
208 void M_DrawOptions(void);
209 void M_DrawSound(void);
210 void M_DrawLoad(void);
211 void M_DrawSave(void);
213 void M_DrawSaveLoadBorder(int x,int y);
214 void M_SetupNextMenu(menu_t *menudef);
215 void M_DrawThermo(int x,int y,int thermWidth,int thermDot);
216 void M_DrawEmptyCell(menu_t *menu,int item);
217 void M_DrawSelCell(menu_t *menu,int item);
218 void M_WriteText(int x, int y, char *string);
219 int M_StringWidth(char *string);
220 int M_StringHeight(char *string);
221 void M_StartControlPanel(void);
222 void M_StartMessage(char *string,void *routine,boolean input);
223 void M_StopMessage(void);
224 void M_ClearMenus (void);
243 menuitem_t MainMenu[]=
245 {1,"M_NGAME",M_NewGame,'n'},
246 {1,"M_OPTION",M_Options,'o'},
247 {1,"M_LOADG",M_LoadGame,'l'},
248 {1,"M_SAVEG",M_SaveGame,'s'},
249 // Another hickup with Special edition.
250 {1,"M_RDTHIS",M_ReadThis,'r'},
251 {1,"M_QUITG",M_QuitDOOM,'q'}
277 menuitem_t EpisodeMenu[]=
279 {1,"M_EPI1", M_Episode,'k'},
280 {1,"M_EPI2", M_Episode,'t'},
281 {1,"M_EPI3", M_Episode,'i'},
282 {1,"M_EPI4", M_Episode,'t'}
287 ep_end, // # of menu items
288 &MainDef, // previous menu
289 EpisodeMenu, // menuitem_t ->
290 M_DrawEpisode, // drawing routine ->
308 menuitem_t NewGameMenu[]=
310 {1,"M_JKILL", M_ChooseSkill, 'i'},
311 {1,"M_ROUGH", M_ChooseSkill, 'h'},
312 {1,"M_HURT", M_ChooseSkill, 'h'},
313 {1,"M_ULTRA", M_ChooseSkill, 'u'},
314 {1,"M_NMARE", M_ChooseSkill, 'n'}
319 newg_end, // # of menu items
320 &EpiDef, // previous menu
321 NewGameMenu, // menuitem_t ->
322 M_DrawNewGame, // drawing routine ->
345 menuitem_t OptionsMenu[]=
347 {1,"M_ENDGAM", M_EndGame,'e'},
348 {1,"M_MESSG", M_ChangeMessages,'m'},
349 {1,"M_DETAIL", M_ChangeDetail,'g'},
350 {2,"M_SCRNSZ", M_SizeDisplay,'s'},
352 {2,"M_MSENS", M_ChangeSensitivity,'m'},
354 {1,"M_SVOL", M_Sound,'s'}
368 // Read This! MENU 1 & 2
376 menuitem_t ReadMenu1[] =
397 menuitem_t ReadMenu2[]=
399 {1,"",M_FinishReadThis,0}
424 menuitem_t SoundMenu[]=
426 {2,"M_SFXVOL",M_SfxVol,'s'},
428 {2,"M_MUSVOL",M_MusicVol,'m'},
456 menuitem_t LoadMenu[]=
458 {1,"", M_LoadSelect,'1'},
459 {1,"", M_LoadSelect,'2'},
460 {1,"", M_LoadSelect,'3'},
461 {1,"", M_LoadSelect,'4'},
462 {1,"", M_LoadSelect,'5'},
463 {1,"", M_LoadSelect,'6'}
479 menuitem_t SaveMenu[]=
481 {1,"", M_SaveSelect,'1'},
482 {1,"", M_SaveSelect,'2'},
483 {1,"", M_SaveSelect,'3'},
484 {1,"", M_SaveSelect,'4'},
485 {1,"", M_SaveSelect,'5'},
486 {1,"", M_SaveSelect,'6'}
502 // read the strings from the savegame files
504 void M_ReadSaveStrings(void)
510 for (i = 0;i < load_end;i++)
512 sprintf(name,SAVEGAMENAME"%d.dsg",i);
514 handle = I_Open (name);
517 strcpy(&savegamestrings[i][0],EMPTYSTRING);
518 LoadMenu[i].status = 0;
521 I_Read (handle, savegamestrings+i, SAVESTRINGSIZE);
523 LoadMenu[i].status = 1;
531 void M_DrawLoad(void)
535 V_DrawPatchDirect (72,28,0,W_CacheLumpName("M_LOADG",PU_CACHE));
536 for (i = 0;i < load_end; i++)
538 M_DrawSaveLoadBorder(LoadDef.x,LoadDef.y+LINEHEIGHT*i);
539 M_WriteText(LoadDef.x,LoadDef.y+LINEHEIGHT*i,savegamestrings[i]);
546 // Draw border for the savegame description
548 void M_DrawSaveLoadBorder(int x,int y)
552 V_DrawPatchDirect (x-8,y+7,0,W_CacheLumpName("M_LSLEFT",PU_CACHE));
554 for (i = 0;i < 24;i++)
556 V_DrawPatchDirect (x,y+7,0,W_CacheLumpName("M_LSCNTR",PU_CACHE));
560 V_DrawPatchDirect (x,y+7,0,W_CacheLumpName("M_LSRGHT",PU_CACHE));
566 // User wants to load this game
568 void M_LoadSelect(int choice)
572 sprintf(name,SAVEGAMENAME"%d.dsg",choice);
578 // Selected from DOOM menu
580 void M_LoadGame (int /*choice*/)
584 M_StartMessage(LOADNET,NULL,false);
588 M_SetupNextMenu(&LoadDef);
596 void M_DrawSave(void)
600 V_DrawPatchDirect (72,28,0,W_CacheLumpName("M_SAVEG",PU_CACHE));
601 for (i = 0;i < load_end; i++)
603 M_DrawSaveLoadBorder(LoadDef.x,LoadDef.y+LINEHEIGHT*i);
604 M_WriteText(LoadDef.x,LoadDef.y+LINEHEIGHT*i,savegamestrings[i]);
609 i = M_StringWidth(savegamestrings[saveSlot]);
610 M_WriteText(LoadDef.x + i,LoadDef.y+LINEHEIGHT*saveSlot,"_");
615 // M_Responder calls this when user is finished
617 void M_DoSave(int slot)
619 G_SaveGame (slot,savegamestrings[slot]);
622 // PICK QUICKSAVE SLOT YET?
623 if (quickSaveSlot == -2)
624 quickSaveSlot = slot;
628 // User wants to save. Start string input for M_Responder
630 void M_SaveSelect(int choice)
632 // we are going to be intercepting all chars
636 strcpy(saveOldString,savegamestrings[choice]);
637 if (!strcmp(savegamestrings[choice],EMPTYSTRING))
638 savegamestrings[choice][0] = 0;
639 saveCharIndex = strlen(savegamestrings[choice]);
643 // Selected from DOOM menu
645 void M_SaveGame (int /*choice*/)
649 M_StartMessage(SAVEDEAD,NULL,false);
653 if (gamestate != GS_LEVEL)
656 M_SetupNextMenu(&SaveDef);
667 void M_QuickSaveResponse(int ch)
671 M_DoSave(quickSaveSlot);
672 S_StartSound(NULL,sfx_swtchx);
676 void M_QuickSave(void)
680 S_StartSound(NULL,sfx_oof);
684 if (gamestate != GS_LEVEL)
687 if (quickSaveSlot < 0)
689 M_StartControlPanel();
691 M_SetupNextMenu(&SaveDef);
692 quickSaveSlot = -2; // means to pick a slot now
695 sprintf(tempstring,QSPROMPT,savegamestrings[quickSaveSlot]);
696 M_StartMessage(tempstring,M_QuickSaveResponse,true);
704 void M_QuickLoadResponse(int ch)
708 M_LoadSelect(quickSaveSlot);
709 S_StartSound(NULL,sfx_swtchx);
714 void M_QuickLoad(void)
718 M_StartMessage(QLOADNET,NULL,false);
722 if (quickSaveSlot < 0)
724 M_StartMessage(QSAVESPOT,NULL,false);
727 sprintf(tempstring,QLPROMPT,savegamestrings[quickSaveSlot]);
728 M_StartMessage(tempstring,M_QuickLoadResponse,true);
736 // Had a "quick hack to fix romero bug"
738 void M_DrawReadThis1(void)
740 inhelpscreens = true;
744 V_DrawPatchDirect (0,0,0,W_CacheLumpName("HELP",PU_CACHE));
749 V_DrawPatchDirect (0,0,0,W_CacheLumpName("HELP1",PU_CACHE));
760 // Read This Menus - optional second page.
762 void M_DrawReadThis2(void)
764 inhelpscreens = true;
769 // This hack keeps us from having to change menus.
770 V_DrawPatchDirect (0,0,0,W_CacheLumpName("CREDIT",PU_CACHE));
774 V_DrawPatchDirect (0,0,0,W_CacheLumpName("HELP2",PU_CACHE));
784 // Change Sfx & Music volumes
786 void M_DrawSound(void)
788 V_DrawPatchDirect (60,38,0,W_CacheLumpName("M_SVOL",PU_CACHE));
790 M_DrawThermo(SoundDef.x,SoundDef.y+LINEHEIGHT*(sfx_vol+1),
793 M_DrawThermo(SoundDef.x,SoundDef.y+LINEHEIGHT*(music_vol+1),
797 void M_Sound(int /*choice*/)
799 M_SetupNextMenu(&SoundDef);
802 void M_SfxVol(int choice)
811 if (snd_SfxVolume < 15)
816 S_SetSfxVolume(snd_SfxVolume /* *8 */);
819 void M_MusicVol(int choice)
828 if (snd_MusicVolume < 15)
833 S_SetMusicVolume(snd_MusicVolume /* *8 */);
842 void M_DrawMainMenu(void)
844 V_DrawPatchDirect (94,2,0,W_CacheLumpName("M_DOOM",PU_CACHE));
853 void M_DrawNewGame(void)
855 V_DrawPatchDirect (96,14,0,W_CacheLumpName("M_NEWG",PU_CACHE));
856 V_DrawPatchDirect (54,38,0,W_CacheLumpName("M_SKILL",PU_CACHE));
859 void M_NewGame(int /*choice*/)
861 if (netgame && !demoplayback)
863 M_StartMessage(NEWGAME,NULL,false);
867 if ( gamemode == commercial )
868 M_SetupNextMenu(&NewDef);
870 M_SetupNextMenu(&EpiDef);
879 void M_DrawEpisode(void)
881 V_DrawPatchDirect (54,38,0,W_CacheLumpName("M_EPISOD",PU_CACHE));
884 void M_VerifyNightmare(int ch)
889 G_DeferedInitNew(nightmare,epi+1,1);
893 void M_ChooseSkill(int choice)
895 if (choice == nightmare)
897 M_StartMessage(NIGHTMARE,M_VerifyNightmare,true);
901 G_DeferedInitNew(choice,epi+1,1);
905 void M_Episode(int choice)
907 if ( (gamemode == shareware)
910 M_StartMessage(SWSTRING,NULL,false);
911 M_SetupNextMenu(&ReadDef1);
915 // Yet another hack...
916 if ( (gamemode == registered)
920 "M_Episode: 4th episode requires UltimateDOOM\n");
925 M_SetupNextMenu(&NewDef);
933 char detailNames[2][9] = {"M_GDHIGH","M_GDLOW"};
934 char msgNames[2][9] = {"M_MSGOFF","M_MSGON"};
937 void M_DrawOptions(void)
939 V_DrawPatchDirect (108,15,0,W_CacheLumpName("M_OPTTTL",PU_CACHE));
941 V_DrawPatchDirect (OptionsDef.x + 175,OptionsDef.y+LINEHEIGHT*detail,0,
942 W_CacheLumpName(detailNames[detailLevel],PU_CACHE));
944 V_DrawPatchDirect (OptionsDef.x + 120,OptionsDef.y+LINEHEIGHT*messages,0,
945 W_CacheLumpName(msgNames[showMessages],PU_CACHE));
947 M_DrawThermo(OptionsDef.x,OptionsDef.y+LINEHEIGHT*(mousesens+1),
948 10,mouseSensitivity);
950 M_DrawThermo(OptionsDef.x,OptionsDef.y+LINEHEIGHT*(scrnsize+1),
954 void M_Options(int /*choice*/)
956 M_SetupNextMenu(&OptionsDef);
962 // Toggle messages on/off
964 void M_ChangeMessages(int /*choice*/)
966 showMessages = 1 - showMessages;
969 players[consoleplayer].message = MSGOFF;
971 players[consoleplayer].message = MSGON ;
973 message_dontfuckwithme = true;
980 void M_EndGameResponse(int ch)
985 currentMenu->lastOn = itemOn;
990 void M_EndGame(int /*choice*/)
994 S_StartSound(NULL,sfx_oof);
1000 M_StartMessage(NETEND,NULL,false);
1004 M_StartMessage(ENDGAME,M_EndGameResponse,true);
1013 void M_ReadThis(int /*choice*/)
1015 M_SetupNextMenu(&ReadDef1);
1018 void M_ReadThis2(int /*choice*/)
1020 M_SetupNextMenu(&ReadDef2);
1023 void M_FinishReadThis(int /*choice*/)
1025 M_SetupNextMenu(&MainDef);
1046 int quitsounds2[8] =
1060 void M_QuitResponse(int ch)
1066 if (gamemode == commercial)
1067 S_StartSound(NULL,quitsounds2[(gametic>>2)&7]);
1069 S_StartSound(NULL,quitsounds[(gametic>>2)&7]);
1078 void M_QuitDOOM(int /*choice*/)
1080 // We pick index 0 which is language sensitive,
1081 // or one at random, between 1 and maximum number.
1082 if (language != english )
1083 snprintf(endstring, sizeof(endstring), "%s\n\n"DOSY, endmsg[0] );
1085 snprintf(endstring, sizeof(endstring), "%s\n\n"DOSY, endmsg[ (gametic%(NUM_QUITMESSAGES-2))+1 ]);
1087 M_StartMessage(endstring,M_QuitResponse,true);
1093 void M_ChangeSensitivity(int choice)
1098 if (mouseSensitivity)
1102 if (mouseSensitivity < 9)
1111 void M_ChangeDetail(int)
1116 detailLevel = 1 - detailLevel;
1117 R_SetViewSize (screenblocks, detailLevel);
1119 players[consoleplayer].message = DETAILHI;
1121 players[consoleplayer].message = DETAILLO;
1126 void M_SizeDisplay(int choice)
1147 R_SetViewSize (screenblocks, detailLevel);
1167 V_DrawPatchDirect (xx,y,0,W_CacheLumpName("M_THERML",PU_CACHE));
1169 for (i=0;i<thermWidth;i++)
1171 V_DrawPatchDirect (xx,y,0,W_CacheLumpName("M_THERMM",PU_CACHE));
1174 V_DrawPatchDirect (xx,y,0,W_CacheLumpName("M_THERMR",PU_CACHE));
1176 V_DrawPatchDirect ((x+8) + thermDot*8,y,
1177 0,W_CacheLumpName("M_THERMO",PU_CACHE));
1187 V_DrawPatchDirect (menu->x - 10, menu->y+item*LINEHEIGHT - 1, 0,
1188 W_CacheLumpName("M_CELL1",PU_CACHE));
1196 V_DrawPatchDirect (menu->x - 10, menu->y+item*LINEHEIGHT - 1, 0,
1197 W_CacheLumpName("M_CELL2",PU_CACHE));
1207 messageLastMenuActive = menuactive;
1209 messageString = string;
1210 messageRoutine = routine;
1211 messageNeedsInput = input;
1218 void M_StopMessage(void)
1220 menuactive = messageLastMenuActive;
1227 // Find string width from hu_font chars
1229 int M_StringWidth(char* string)
1235 for (i = 0;i < strlen(string);i++)
1237 c = toupper(string[i]) - HU_FONTSTART;
1238 if (c < 0 || c >= HU_FONTSIZE)
1241 w += SHORT (hu_font[c]->width);
1250 // Find string height from hu_font chars
1252 int M_StringHeight(char* string)
1256 int height = SHORT(hu_font[0]->height);
1259 for (i = 0;i < strlen(string);i++)
1260 if (string[i] == '\n')
1268 // Write a string using the hu_font
1299 c = toupper(c) - HU_FONTSTART;
1300 if (c < 0 || c>= HU_FONTSIZE)
1306 w = SHORT (hu_font[c]->width);
1307 if (cx+w > SCREENWIDTH)
1309 V_DrawPatchDirect(cx, cy, 0, hu_font[c]);
1323 boolean M_Responder (event_t* ev)
1327 static int joywait = 0;
1328 static int mousewait = 0;
1329 static int mousey = 0;
1330 static int lasty = 0;
1331 static int mousex = 0;
1332 static int lastx = 0;
1336 if (ev->type == ev_joystick && joywait < I_GetTime())
1338 if (ev->data3 == -1)
1341 joywait = I_GetTime() + 5;
1343 else if (ev->data3 == 1)
1346 joywait = I_GetTime() + 5;
1349 if (ev->data2 == -1)
1352 joywait = I_GetTime() + 2;
1354 else if (ev->data2 == 1)
1356 ch = KEY_RIGHTARROW;
1357 joywait = I_GetTime() + 2;
1363 joywait = I_GetTime() + 5;
1368 joywait = I_GetTime() + 5;
1373 if (ev->type == ev_mouse && mousewait < I_GetTime())
1375 mousey += ev->data3;
1376 if (mousey < lasty-30)
1379 mousewait = I_GetTime() + 5;
1380 mousey = lasty -= 30;
1382 else if (mousey > lasty+30)
1385 mousewait = I_GetTime() + 5;
1386 mousey = lasty += 30;
1389 mousex += ev->data2;
1390 if (mousex < lastx-30)
1393 mousewait = I_GetTime() + 5;
1394 mousex = lastx -= 30;
1396 else if (mousex > lastx+30)
1398 ch = KEY_RIGHTARROW;
1399 mousewait = I_GetTime() + 5;
1400 mousex = lastx += 30;
1406 mousewait = I_GetTime() + 15;
1412 mousewait = I_GetTime() + 15;
1416 if (ev->type == ev_keydown || ev->type == ev_char)
1425 // Save Game string input
1426 if (saveStringEnter)
1431 if(ev->type != ev_keydown)
1433 saveStringEnter = 0;
1434 strcpy(&savegamestrings[saveSlot][0],saveOldString);
1438 if(ev->type != ev_keydown)
1440 saveStringEnter = 0;
1441 if (savegamestrings[saveSlot][0])
1446 if(ev->type != ev_char)
1449 if (saveCharIndex > 0)
1452 savegamestrings[saveSlot][saveCharIndex] = 0;
1458 if (ch-HU_FONTSTART < 0 || ch-HU_FONTSTART >= HU_FONTSIZE)
1460 if (ch >= 32 && ch <= 127 &&
1461 saveCharIndex < SAVESTRINGSIZE-1 &&
1462 M_StringWidth(savegamestrings[saveSlot]) <
1463 (SAVESTRINGSIZE-2)*8)
1465 savegamestrings[saveSlot][saveCharIndex++] = ch;
1466 savegamestrings[saveSlot][saveCharIndex] = 0;
1473 if(ev->type != ev_keydown)
1476 // Take care of any messages that need input
1479 if (messageNeedsInput == true &&
1480 !(ch == ' ' || ch == 'n' || ch == 'y' || ch == KEY_ESCAPE))
1483 menuactive = messageLastMenuActive;
1489 S_StartSound(NULL,sfx_swtchx);
1493 if (devparm && ch == KEY_F1)
1503 case KEY_MINUS: // Screen size down
1504 if (automapactive || chat_on)
1507 S_StartSound(NULL,sfx_stnmov);
1510 case KEY_EQUALS: // Screen size up
1511 if (automapactive || chat_on)
1514 S_StartSound(NULL,sfx_stnmov);
1517 case KEY_F1: // Help key
1518 M_StartControlPanel ();
1520 if ( gamemode == retail )
1521 currentMenu = &ReadDef2;
1523 currentMenu = &ReadDef1;
1526 S_StartSound(NULL,sfx_swtchn);
1529 case KEY_F2: // Save
1530 M_StartControlPanel();
1531 S_StartSound(NULL,sfx_swtchn);
1535 case KEY_F3: // Load
1536 M_StartControlPanel();
1537 S_StartSound(NULL,sfx_swtchn);
1541 case KEY_F4: // Sound Volume
1542 M_StartControlPanel ();
1543 currentMenu = &SoundDef;
1545 S_StartSound(NULL,sfx_swtchn);
1548 case KEY_F5: // Detail toggle
1550 S_StartSound(NULL,sfx_swtchn);
1553 case KEY_F6: // Quicksave
1554 S_StartSound(NULL,sfx_swtchn);
1558 case KEY_F7: // End game
1559 S_StartSound(NULL,sfx_swtchn);
1563 case KEY_F8: // Toggle messages
1564 M_ChangeMessages(0);
1565 S_StartSound(NULL,sfx_swtchn);
1568 case KEY_F9: // Quickload
1569 S_StartSound(NULL,sfx_swtchn);
1573 case KEY_F10: // Quit DOOM
1574 S_StartSound(NULL,sfx_swtchn);
1578 case KEY_F11: // gamma toggle
1582 players[consoleplayer].message = gammamsg[usegamma];
1583 I_SetPalette (W_CacheLumpName ("PLAYPAL",PU_CACHE));
1592 if (ch == KEY_ESCAPE)
1594 M_StartControlPanel ();
1595 S_StartSound(NULL,sfx_swtchn);
1602 // Keys usable within menu
1608 if (itemOn+1 > currentMenu->numitems-1)
1611 S_StartSound(NULL,sfx_pstop);
1612 } while(currentMenu->menuitems[itemOn].status==-1);
1619 itemOn = currentMenu->numitems-1;
1621 S_StartSound(NULL,sfx_pstop);
1622 } while(currentMenu->menuitems[itemOn].status==-1);
1626 if (currentMenu->menuitems[itemOn].routine &&
1627 currentMenu->menuitems[itemOn].status == 2)
1629 S_StartSound(NULL,sfx_stnmov);
1630 currentMenu->menuitems[itemOn].routine(0);
1634 case KEY_RIGHTARROW:
1635 if (currentMenu->menuitems[itemOn].routine &&
1636 currentMenu->menuitems[itemOn].status == 2)
1638 S_StartSound(NULL,sfx_stnmov);
1639 currentMenu->menuitems[itemOn].routine(1);
1644 if (currentMenu->menuitems[itemOn].routine &&
1645 currentMenu->menuitems[itemOn].status)
1647 currentMenu->lastOn = itemOn;
1648 if (currentMenu->menuitems[itemOn].status == 2)
1650 currentMenu->menuitems[itemOn].routine(1); // right arrow
1651 S_StartSound(NULL,sfx_stnmov);
1655 currentMenu->menuitems[itemOn].routine(itemOn);
1656 S_StartSound(NULL,sfx_pistol);
1662 currentMenu->lastOn = itemOn;
1664 S_StartSound(NULL,sfx_swtchx);
1668 currentMenu->lastOn = itemOn;
1669 if (currentMenu->prevMenu)
1671 currentMenu = currentMenu->prevMenu;
1672 itemOn = currentMenu->lastOn;
1673 S_StartSound(NULL,sfx_swtchn);
1678 for (i = itemOn+1;i < currentMenu->numitems;i++)
1679 if (currentMenu->menuitems[i].alphaKey == ch)
1682 S_StartSound(NULL,sfx_pstop);
1685 for (i = 0;i <= itemOn;i++)
1686 if (currentMenu->menuitems[i].alphaKey == ch)
1689 S_StartSound(NULL,sfx_pstop);
1702 // M_StartControlPanel
1704 void M_StartControlPanel (void)
1706 // intro might call this repeatedly
1711 currentMenu = &MainDef; // JDC
1712 itemOn = currentMenu->lastOn; // JDC
1714 I_MouseEnable(0); // disable mouse grab
1720 // Called after the view has been rendered,
1721 // but before it has been blitted.
1723 void M_Drawer (void)
1733 inhelpscreens = false;
1735 // Horiz. & Vertically center string and print it.
1739 y = 100 - M_StringHeight(messageString)/2;
1740 while(*(messageString+start))
1742 n = strlen(messageString+start);
1743 for (i = 0;i < n;i++)
1744 if (*(messageString+start+i) == '\n')
1746 snprint(string, sizeof(string), "%.*s", i, messageString+start);
1753 snprint(string, sizeof(string), "%s", messageString+start);
1757 x = 160 - M_StringWidth(string)/2;
1758 M_WriteText(x,y,string);
1759 y += SHORT(hu_font[0]->height);
1767 if (currentMenu->routine)
1768 currentMenu->routine(); // call Draw routine
1773 max = currentMenu->numitems;
1777 if (currentMenu->menuitems[i].name[0])
1778 V_DrawPatchDirect (x,y,0,
1779 W_CacheLumpName(currentMenu->menuitems[i].name ,PU_CACHE));
1785 V_DrawPatchDirect(x + SKULLXOFF,currentMenu->y - 5 + itemOn*LINEHEIGHT, 0,
1786 W_CacheLumpName(skullName[whichSkull],PU_CACHE));
1794 void M_ClearMenus (void)
1797 I_MouseEnable(1); // enable mouse grabbing
1799 // if (!netgame && usergame && paused)
1800 // sendpause = true;
1809 void M_SetupNextMenu(menu_t *menudef)
1811 currentMenu = menudef;
1812 itemOn = currentMenu->lastOn;
1819 void M_Ticker (void)
1821 if (--skullAnimCounter <= 0)
1824 skullAnimCounter = 8;
1834 currentMenu = &MainDef;
1836 itemOn = currentMenu->lastOn;
1838 skullAnimCounter = 10;
1839 screenSize = screenblocks - 3;
1841 messageString = NULL;
1842 messageLastMenuActive = menuactive;
1845 // Here we could catch other version dependencies,
1846 // like HELP1/2, and four episodes.
1852 // This is used because DOOM 2 had only one HELP
1853 // page. I use CREDIT as second page now, but
1854 // kept this hack for educational purposes.
1855 MainMenu[readthis] = MainMenu[quitdoom];
1858 NewDef.prevMenu = &MainDef;
1859 ReadDef1.routine = M_DrawReadThis1;
1862 ReadMenu1[0].routine = M_FinishReadThis;
1865 // Episode 2 and 3 are handled,
1866 // branching to an ad screen.
1868 // We need to remove the fourth episode.