1 // Emacs style mode select -*- C++ -*-
2 //-----------------------------------------------------------------------------
4 // Copyright(C) 1993-1996 Id Software, Inc.
5 // Copyright(C) 2005 Simon Howard
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License
9 // as published by the Free Software Foundation; either version 2
10 // of the License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU General Public License for more details.
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
23 // DOOM selection menu, options, episode etc.
24 // Sliders and icons. Kinda widget stuff.
26 //-----------------------------------------------------------------------------
67 extern void M_QuitDOOM(int);
69 extern patch_t
* hu_font
[HU_FONTSIZE
];
70 extern boolean message_dontfuckwithme
;
72 extern boolean chat_on
; // in heads-up code
78 int key_menu_activate
= KEY_ESCAPE
;
79 int key_menu_up
= KEY_UPARROW
;
80 int key_menu_down
= KEY_DOWNARROW
;
81 int key_menu_left
= KEY_LEFTARROW
;
82 int key_menu_right
= KEY_RIGHTARROW
;
83 int key_menu_back
= KEY_BACKSPACE
;
84 int key_menu_forward
= KEY_ENTER
;
85 int key_menu_confirm
= 'y';
86 int key_menu_abort
= 'n';
88 int key_menu_help
= KEY_F1
;
89 int key_menu_save
= KEY_F2
;
90 int key_menu_load
= KEY_F3
;
91 int key_menu_volume
= KEY_F4
;
92 int key_menu_detail
= KEY_F5
;
93 int key_menu_qsave
= KEY_F6
;
94 int key_menu_endgame
= KEY_F7
;
95 int key_menu_messages
= KEY_F8
;
96 int key_menu_qload
= KEY_F9
;
97 int key_menu_quit
= KEY_F10
;
98 int key_menu_gamma
= KEY_F11
;
100 int key_menu_incscreen
= KEY_EQUALS
;
101 int key_menu_decscreen
= KEY_MINUS
;
106 int mouseSensitivity
= 5;
108 // Show messages has default, 0 = off, 1 = on
109 int showMessages
= 1;
112 // Blocky mode, has default, 0 = high, 1 = normal
114 int screenblocks
= 9;
116 // temp for screenblocks (0-9)
119 // -1 = no quicksave slot picked!
122 // 1 = message to be printed
124 // ...and here is the message string!
130 int messageLastMenuActive
;
132 // timed message = no input from user
133 boolean messageNeedsInput
;
135 void (*messageRoutine
)(int response
);
137 char gammamsg
[5][26] =
146 // we are going to be entering a savegame string
148 int saveSlot
; // which slot to save in
149 int saveCharIndex
; // which char we're editing
150 // old save description before edit
151 char saveOldString
[SAVESTRINGSIZE
];
153 boolean inhelpscreens
;
156 #define SKULLXOFF -32
157 #define LINEHEIGHT 16
159 extern boolean sendpause
;
160 char savegamestrings
[10][SAVESTRINGSIZE
];
170 // 0 = no cursor here, 1 = ok, 2 = arrows ok
175 // choice = menu item #.
177 // choice=0:leftarrow,1:rightarrow
178 void (*routine
)(int choice
);
186 typedef struct menu_s
188 short numitems
; // # of menu items
189 struct menu_s
* prevMenu
; // previous menu
190 menuitem_t
* menuitems
; // menu items
191 void (*routine
)(); // draw routine
193 short y
; // x,y of menu
194 short lastOn
; // last item user was on in menu
197 short itemOn
; // menu item skull is on
198 short skullAnimCounter
; // skull animation counter
199 short whichSkull
; // which skull to draw
201 // graphic name of skulls
202 // warning: initializer-string for array of chars is too long
203 char *skullName
[2] = {"M_SKULL1","M_SKULL2"};
211 void M_NewGame(int choice
);
212 void M_Episode(int choice
);
213 void M_ChooseSkill(int choice
);
214 void M_LoadGame(int choice
);
215 void M_SaveGame(int choice
);
216 void M_Options(int choice
);
217 void M_EndGame(int choice
);
218 void M_ReadThis(int choice
);
219 void M_ReadThis2(int choice
);
220 void M_QuitDOOM(int choice
);
222 void M_ChangeMessages(int choice
);
223 void M_ChangeSensitivity(int choice
);
224 void M_SfxVol(int choice
);
225 void M_MusicVol(int choice
);
226 void M_ChangeDetail(int choice
);
227 void M_SizeDisplay(int choice
);
228 void M_StartGame(int choice
);
229 void M_Sound(int choice
);
231 void M_FinishReadThis(int choice
);
232 void M_LoadSelect(int choice
);
233 void M_SaveSelect(int choice
);
234 void M_ReadSaveStrings(void);
235 void M_QuickSave(void);
236 void M_QuickLoad(void);
238 void M_DrawMainMenu(void);
239 void M_DrawReadThis1(void);
240 void M_DrawReadThis2(void);
241 void M_DrawNewGame(void);
242 void M_DrawEpisode(void);
243 void M_DrawOptions(void);
244 void M_DrawSound(void);
245 void M_DrawLoad(void);
246 void M_DrawSave(void);
248 void M_DrawSaveLoadBorder(int x
,int y
);
249 void M_SetupNextMenu(menu_t
*menudef
);
250 void M_DrawThermo(int x
,int y
,int thermWidth
,int thermDot
);
251 void M_DrawEmptyCell(menu_t
*menu
,int item
);
252 void M_DrawSelCell(menu_t
*menu
,int item
);
253 void M_WriteText(int x
, int y
, char *string
);
254 int M_StringWidth(char *string
);
255 int M_StringHeight(char *string
);
256 void M_StartControlPanel(void);
257 void M_StartMessage(char *string
,void *routine
,boolean input
);
258 void M_StopMessage(void);
259 void M_ClearMenus (void);
278 menuitem_t MainMenu
[]=
280 {1,"M_NGAME",M_NewGame
,'n'},
281 {1,"M_OPTION",M_Options
,'o'},
282 {1,"M_LOADG",M_LoadGame
,'l'},
283 {1,"M_SAVEG",M_SaveGame
,'s'},
284 // Another hickup with Special edition.
285 {1,"M_RDTHIS",M_ReadThis
,'r'},
286 {1,"M_QUITG",M_QuitDOOM
,'q'}
312 menuitem_t EpisodeMenu
[]=
314 {1,"M_EPI1", M_Episode
,'k'},
315 {1,"M_EPI2", M_Episode
,'t'},
316 {1,"M_EPI3", M_Episode
,'i'},
317 {1,"M_EPI4", M_Episode
,'t'}
322 ep_end
, // # of menu items
323 &MainDef
, // previous menu
324 EpisodeMenu
, // menuitem_t ->
325 M_DrawEpisode
, // drawing routine ->
343 menuitem_t NewGameMenu
[]=
345 {1,"M_JKILL", M_ChooseSkill
, 'i'},
346 {1,"M_ROUGH", M_ChooseSkill
, 'h'},
347 {1,"M_HURT", M_ChooseSkill
, 'h'},
348 {1,"M_ULTRA", M_ChooseSkill
, 'u'},
349 {1,"M_NMARE", M_ChooseSkill
, 'n'}
354 newg_end
, // # of menu items
355 &EpiDef
, // previous menu
356 NewGameMenu
, // menuitem_t ->
357 M_DrawNewGame
, // drawing routine ->
380 menuitem_t OptionsMenu
[]=
382 {1,"M_ENDGAM", M_EndGame
,'e'},
383 {1,"M_MESSG", M_ChangeMessages
,'m'},
384 {1,"M_DETAIL", M_ChangeDetail
,'g'},
385 {2,"M_SCRNSZ", M_SizeDisplay
,'s'},
387 {2,"M_MSENS", M_ChangeSensitivity
,'m'},
389 {1,"M_SVOL", M_Sound
,'s'}
403 // Read This! MENU 1 & 2
411 menuitem_t ReadMenu1
[] =
432 menuitem_t ReadMenu2
[]=
434 {1,"",M_FinishReadThis
,0}
459 menuitem_t SoundMenu
[]=
461 {2,"M_SFXVOL",M_SfxVol
,'s'},
463 {2,"M_MUSVOL",M_MusicVol
,'m'},
491 menuitem_t LoadMenu
[]=
493 {1,"", M_LoadSelect
,'1'},
494 {1,"", M_LoadSelect
,'2'},
495 {1,"", M_LoadSelect
,'3'},
496 {1,"", M_LoadSelect
,'4'},
497 {1,"", M_LoadSelect
,'5'},
498 {1,"", M_LoadSelect
,'6'}
514 menuitem_t SaveMenu
[]=
516 {1,"", M_SaveSelect
,'1'},
517 {1,"", M_SaveSelect
,'2'},
518 {1,"", M_SaveSelect
,'3'},
519 {1,"", M_SaveSelect
,'4'},
520 {1,"", M_SaveSelect
,'5'},
521 {1,"", M_SaveSelect
,'6'}
537 // read the strings from the savegame files
539 void M_ReadSaveStrings(void)
546 for (i
= 0;i
< load_end
;i
++)
548 strcpy(name
, P_SaveGameFile(i
));
550 handle
= fopen(name
, "rb");
553 strcpy(&savegamestrings
[i
][0], EMPTYSTRING
);
554 LoadMenu
[i
].status
= 0;
557 count
= fread(&savegamestrings
[i
], 1, SAVESTRINGSIZE
, handle
);
559 LoadMenu
[i
].status
= 1;
567 void M_DrawLoad(void)
571 V_DrawPatchDirect (72,28,0,W_CacheLumpName(DEH_String("M_LOADG"),PU_CACHE
));
572 for (i
= 0;i
< load_end
; i
++)
574 M_DrawSaveLoadBorder(LoadDef
.x
,LoadDef
.y
+LINEHEIGHT
*i
);
575 M_WriteText(LoadDef
.x
,LoadDef
.y
+LINEHEIGHT
*i
,savegamestrings
[i
]);
582 // Draw border for the savegame description
584 void M_DrawSaveLoadBorder(int x
,int y
)
588 V_DrawPatchDirect (x
-8,y
+7,0,W_CacheLumpName(DEH_String("M_LSLEFT"),PU_CACHE
));
590 for (i
= 0;i
< 24;i
++)
592 V_DrawPatchDirect (x
,y
+7,0,W_CacheLumpName(DEH_String("M_LSCNTR"),PU_CACHE
));
596 V_DrawPatchDirect (x
,y
+7,0,W_CacheLumpName(DEH_String("M_LSRGHT"),PU_CACHE
));
602 // User wants to load this game
604 void M_LoadSelect(int choice
)
608 strcpy(name
, P_SaveGameFile(choice
));
615 // Selected from DOOM menu
617 void M_LoadGame (int choice
)
621 M_StartMessage(DEH_String(LOADNET
),NULL
,false);
625 M_SetupNextMenu(&LoadDef
);
633 void M_DrawSave(void)
637 V_DrawPatchDirect (72,28,0,W_CacheLumpName(DEH_String("M_SAVEG"),PU_CACHE
));
638 for (i
= 0;i
< load_end
; i
++)
640 M_DrawSaveLoadBorder(LoadDef
.x
,LoadDef
.y
+LINEHEIGHT
*i
);
641 M_WriteText(LoadDef
.x
,LoadDef
.y
+LINEHEIGHT
*i
,savegamestrings
[i
]);
646 i
= M_StringWidth(savegamestrings
[saveSlot
]);
647 M_WriteText(LoadDef
.x
+ i
,LoadDef
.y
+LINEHEIGHT
*saveSlot
,"_");
652 // M_Responder calls this when user is finished
654 void M_DoSave(int slot
)
656 G_SaveGame (slot
,savegamestrings
[slot
]);
659 // PICK QUICKSAVE SLOT YET?
660 if (quickSaveSlot
== -2)
661 quickSaveSlot
= slot
;
665 // User wants to save. Start string input for M_Responder
667 void M_SaveSelect(int choice
)
669 // we are going to be intercepting all chars
673 strcpy(saveOldString
,savegamestrings
[choice
]);
674 if (!strcmp(savegamestrings
[choice
],EMPTYSTRING
))
675 savegamestrings
[choice
][0] = 0;
676 saveCharIndex
= strlen(savegamestrings
[choice
]);
680 // Selected from DOOM menu
682 void M_SaveGame (int choice
)
686 M_StartMessage(DEH_String(SAVEDEAD
),NULL
,false);
690 if (gamestate
!= GS_LEVEL
)
693 M_SetupNextMenu(&SaveDef
);
704 void M_QuickSaveResponse(int key
)
706 if (key
== key_menu_confirm
)
708 M_DoSave(quickSaveSlot
);
709 S_StartSound(NULL
,sfx_swtchx
);
713 void M_QuickSave(void)
717 S_StartSound(NULL
,sfx_oof
);
721 if (gamestate
!= GS_LEVEL
)
724 if (quickSaveSlot
< 0)
726 M_StartControlPanel();
728 M_SetupNextMenu(&SaveDef
);
729 quickSaveSlot
= -2; // means to pick a slot now
732 DEH_snprintf(tempstring
, 80, QSPROMPT
, savegamestrings
[quickSaveSlot
]);
733 M_StartMessage(tempstring
,M_QuickSaveResponse
,true);
741 void M_QuickLoadResponse(int key
)
743 if (key
== key_menu_confirm
)
745 M_LoadSelect(quickSaveSlot
);
746 S_StartSound(NULL
,sfx_swtchx
);
751 void M_QuickLoad(void)
755 M_StartMessage(DEH_String(QLOADNET
),NULL
,false);
759 if (quickSaveSlot
< 0)
761 M_StartMessage(DEH_String(QSAVESPOT
),NULL
,false);
764 DEH_snprintf(tempstring
, 80, QLPROMPT
, savegamestrings
[quickSaveSlot
]);
765 M_StartMessage(tempstring
,M_QuickLoadResponse
,true);
773 // Had a "quick hack to fix romero bug"
775 void M_DrawReadThis1(void)
777 char *lumpname
= "CREDIT";
778 int skullx
= 330, skully
= 175;
780 inhelpscreens
= true;
782 // Different versions of Doom 1.9 work differently
787 if (gamemode
== commercial
)
799 // HELP2 is the first screen shown in Doom 1
811 // Ultimate Doom always displays "HELP1".
813 // Chex Quest version also uses "HELP1", even though it is based
822 // Final Doom always displays "HELP".
829 lumpname
= DEH_String(lumpname
);
831 V_DrawPatchDirect (0, 0, 0, W_CacheLumpName(lumpname
, PU_CACHE
));
840 // Read This Menus - optional second page.
842 void M_DrawReadThis2(void)
844 inhelpscreens
= true;
846 // We only ever draw the second page if this is
847 // gameversion == exe_doom_1_9 and gamemode == registered
849 V_DrawPatchDirect(0, 0, 0, W_CacheLumpName(DEH_String("HELP1"), PU_CACHE
));
854 // Change Sfx & Music volumes
856 void M_DrawSound(void)
858 V_DrawPatchDirect (60,38,0,W_CacheLumpName(DEH_String("M_SVOL"),PU_CACHE
));
860 M_DrawThermo(SoundDef
.x
,SoundDef
.y
+LINEHEIGHT
*(sfx_vol
+1),
863 M_DrawThermo(SoundDef
.x
,SoundDef
.y
+LINEHEIGHT
*(music_vol
+1),
867 void M_Sound(int choice
)
869 M_SetupNextMenu(&SoundDef
);
872 void M_SfxVol(int choice
)
886 S_SetSfxVolume(sfxVolume
* 8);
889 void M_MusicVol(int choice
)
898 if (musicVolume
< 15)
903 S_SetMusicVolume(musicVolume
* 8);
912 void M_DrawMainMenu(void)
914 V_DrawPatchDirect (94,2,0,W_CacheLumpName(DEH_String("M_DOOM"),PU_CACHE
));
923 void M_DrawNewGame(void)
925 V_DrawPatchDirect (96,14,0,W_CacheLumpName(DEH_String("M_NEWG"),PU_CACHE
));
926 V_DrawPatchDirect (54,38,0,W_CacheLumpName(DEH_String("M_SKILL"),PU_CACHE
));
929 void M_NewGame(int choice
)
931 if (netgame
&& !demoplayback
)
933 M_StartMessage(DEH_String(NEWGAME
),NULL
,false);
937 // Chex Quest disabled the episode select screen, as did Doom II.
939 if (gamemode
== commercial
|| gameversion
== exe_chex
)
940 M_SetupNextMenu(&NewDef
);
942 M_SetupNextMenu(&EpiDef
);
951 void M_DrawEpisode(void)
953 V_DrawPatchDirect (54,38,0,W_CacheLumpName(DEH_String("M_EPISOD"),PU_CACHE
));
956 void M_VerifyNightmare(int key
)
958 if (key
!= key_menu_confirm
)
961 G_DeferedInitNew(nightmare
,epi
+1,1);
965 void M_ChooseSkill(int choice
)
967 if (choice
== nightmare
)
969 M_StartMessage(DEH_String(NIGHTMARE
),M_VerifyNightmare
,true);
973 G_DeferedInitNew(choice
,epi
+1,1);
977 void M_Episode(int choice
)
979 if ( (gamemode
== shareware
)
982 M_StartMessage(DEH_String(SWSTRING
),NULL
,false);
983 M_SetupNextMenu(&ReadDef1
);
987 // Yet another hack...
988 if ( (gamemode
== registered
)
992 "M_Episode: 4th episode requires UltimateDOOM\n");
997 M_SetupNextMenu(&NewDef
);
1005 char detailNames
[2][9] = {"M_GDHIGH","M_GDLOW"};
1006 char msgNames
[2][9] = {"M_MSGOFF","M_MSGON"};
1009 void M_DrawOptions(void)
1011 V_DrawPatchDirect (108,15,0,W_CacheLumpName(DEH_String("M_OPTTTL"),PU_CACHE
));
1013 V_DrawPatchDirect (OptionsDef
.x
+ 175,OptionsDef
.y
+LINEHEIGHT
*detail
,0,
1014 W_CacheLumpName(DEH_String(detailNames
[detailLevel
]),
1017 V_DrawPatchDirect (OptionsDef
.x
+ 120,OptionsDef
.y
+LINEHEIGHT
*messages
,0,
1018 W_CacheLumpName(DEH_String(msgNames
[showMessages
]),
1021 M_DrawThermo(OptionsDef
.x
,OptionsDef
.y
+LINEHEIGHT
*(mousesens
+1),
1022 10,mouseSensitivity
);
1024 M_DrawThermo(OptionsDef
.x
,OptionsDef
.y
+LINEHEIGHT
*(scrnsize
+1),
1028 void M_Options(int choice
)
1030 M_SetupNextMenu(&OptionsDef
);
1036 // Toggle messages on/off
1038 void M_ChangeMessages(int choice
)
1040 // warning: unused parameter `int choice'
1042 showMessages
= 1 - showMessages
;
1045 players
[consoleplayer
].message
= DEH_String(MSGOFF
);
1047 players
[consoleplayer
].message
= DEH_String(MSGON
);
1049 message_dontfuckwithme
= true;
1056 void M_EndGameResponse(int key
)
1058 if (key
!= key_menu_confirm
)
1061 currentMenu
->lastOn
= itemOn
;
1066 void M_EndGame(int choice
)
1071 S_StartSound(NULL
,sfx_oof
);
1077 M_StartMessage(DEH_String(NETEND
),NULL
,false);
1081 M_StartMessage(DEH_String(ENDGAME
),M_EndGameResponse
,true);
1090 void M_ReadThis(int choice
)
1093 M_SetupNextMenu(&ReadDef1
);
1096 void M_ReadThis2(int choice
)
1098 // Doom 1.9 had two menus when playing Doom 1
1099 // All others had only one
1101 if (gameversion
== exe_doom_1_9
&& gamemode
!= commercial
)
1104 M_SetupNextMenu(&ReadDef2
);
1110 M_FinishReadThis(0);
1114 void M_FinishReadThis(int choice
)
1117 M_SetupNextMenu(&MainDef
);
1138 int quitsounds2
[8] =
1152 void M_QuitResponse(int key
)
1154 if (key
!= key_menu_confirm
)
1158 if (gamemode
== commercial
)
1159 S_StartSound(NULL
,quitsounds2
[(gametic
>>2)&7]);
1161 S_StartSound(NULL
,quitsounds
[(gametic
>>2)&7]);
1168 static char *M_SelectEndMessage(void)
1172 if (gamemission
== doom
)
1176 endmsg
= doom1_endmsg
;
1182 endmsg
= doom2_endmsg
;
1185 return endmsg
[gametic
% NUM_QUITMESSAGES
];
1189 void M_QuitDOOM(int choice
)
1192 DEH_String("%s\n\n" DOSY
),
1193 DEH_String(M_SelectEndMessage()));
1195 M_StartMessage(endstring
,M_QuitResponse
,true);
1201 void M_ChangeSensitivity(int choice
)
1206 if (mouseSensitivity
)
1210 if (mouseSensitivity
< 9)
1219 void M_ChangeDetail(int choice
)
1222 detailLevel
= 1 - detailLevel
;
1224 R_SetViewSize (screenblocks
, detailLevel
);
1227 players
[consoleplayer
].message
= DEH_String(DETAILHI
);
1229 players
[consoleplayer
].message
= DEH_String(DETAILLO
);
1235 void M_SizeDisplay(int choice
)
1256 R_SetViewSize (screenblocks
, detailLevel
);
1276 V_DrawPatchDirect (xx
,y
,0,W_CacheLumpName(DEH_String("M_THERML"),PU_CACHE
));
1278 for (i
=0;i
<thermWidth
;i
++)
1280 V_DrawPatchDirect (xx
,y
,0,W_CacheLumpName(DEH_String("M_THERMM"),PU_CACHE
));
1283 V_DrawPatchDirect (xx
,y
,0,W_CacheLumpName(DEH_String("M_THERMR"),PU_CACHE
));
1285 V_DrawPatchDirect ((x
+8) + thermDot
*8,y
,
1286 0,W_CacheLumpName(DEH_String("M_THERMO"),PU_CACHE
));
1296 V_DrawPatchDirect (menu
->x
- 10, menu
->y
+item
*LINEHEIGHT
- 1, 0,
1297 W_CacheLumpName(DEH_String("M_CELL1"),PU_CACHE
));
1305 V_DrawPatchDirect (menu
->x
- 10, menu
->y
+item
*LINEHEIGHT
- 1, 0,
1306 W_CacheLumpName(DEH_String("M_CELL2"),PU_CACHE
));
1316 messageLastMenuActive
= menuactive
;
1318 messageString
= string
;
1319 messageRoutine
= routine
;
1320 messageNeedsInput
= input
;
1327 void M_StopMessage(void)
1329 menuactive
= messageLastMenuActive
;
1336 // Find string width from hu_font chars
1338 int M_StringWidth(char* string
)
1344 for (i
= 0;i
< strlen(string
);i
++)
1346 c
= toupper(string
[i
]) - HU_FONTSTART
;
1347 if (c
< 0 || c
>= HU_FONTSIZE
)
1350 w
+= SHORT (hu_font
[c
]->width
);
1359 // Find string height from hu_font chars
1361 int M_StringHeight(char* string
)
1365 int height
= SHORT(hu_font
[0]->height
);
1368 for (i
= 0;i
< strlen(string
);i
++)
1369 if (string
[i
] == '\n')
1377 // Write a string using the hu_font
1408 c
= toupper(c
) - HU_FONTSTART
;
1409 if (c
< 0 || c
>= HU_FONTSIZE
)
1415 w
= SHORT (hu_font
[c
]->width
);
1416 if (cx
+w
> SCREENWIDTH
)
1418 V_DrawPatchDirect(cx
, cy
, 0, hu_font
[c
]);
1432 boolean
M_Responder (event_t
* ev
)
1437 static int joywait
= 0;
1438 static int mousewait
= 0;
1439 static int mousey
= 0;
1440 static int lasty
= 0;
1441 static int mousex
= 0;
1442 static int lastx
= 0;
1444 // key is the key pressed, ch is the actual character typed
1449 if (ev
->type
== ev_joystick
&& joywait
< I_GetTime())
1451 if (ev
->data3
== -1)
1454 joywait
= I_GetTime() + 5;
1456 else if (ev
->data3
== 1)
1458 key
= key_menu_down
;
1459 joywait
= I_GetTime() + 5;
1462 if (ev
->data2
== -1)
1464 key
= key_menu_left
;
1465 joywait
= I_GetTime() + 2;
1467 else if (ev
->data2
== 1)
1469 key
= key_menu_right
;
1470 joywait
= I_GetTime() + 2;
1475 key
= key_menu_forward
;
1476 joywait
= I_GetTime() + 5;
1480 key
= key_menu_back
;
1481 joywait
= I_GetTime() + 5;
1486 if (ev
->type
== ev_mouse
&& mousewait
< I_GetTime())
1488 mousey
+= ev
->data3
;
1489 if (mousey
< lasty
-30)
1491 key
= key_menu_down
;
1492 mousewait
= I_GetTime() + 5;
1493 mousey
= lasty
-= 30;
1495 else if (mousey
> lasty
+30)
1498 mousewait
= I_GetTime() + 5;
1499 mousey
= lasty
+= 30;
1502 mousex
+= ev
->data2
;
1503 if (mousex
< lastx
-30)
1505 key
= key_menu_left
;
1506 mousewait
= I_GetTime() + 5;
1507 mousex
= lastx
-= 30;
1509 else if (mousex
> lastx
+30)
1511 key
= key_menu_right
;
1512 mousewait
= I_GetTime() + 5;
1513 mousex
= lastx
+= 30;
1518 key
= key_menu_forward
;
1519 mousewait
= I_GetTime() + 15;
1524 key
= key_menu_back
;
1525 mousewait
= I_GetTime() + 15;
1530 if (ev
->type
== ev_keydown
)
1541 // In testcontrols mode, none of the function keys should do anything
1542 // - the only key is escape to quit.
1546 if (key
== key_menu_activate
|| key
== key_menu_quit
)
1555 // Save Game string input
1556 if (saveStringEnter
)
1561 if (saveCharIndex
> 0)
1564 savegamestrings
[saveSlot
][saveCharIndex
] = 0;
1569 saveStringEnter
= 0;
1570 strcpy(&savegamestrings
[saveSlot
][0],saveOldString
);
1574 saveStringEnter
= 0;
1575 if (savegamestrings
[saveSlot
][0])
1580 // Entering a character - use the 'ch' value, not the key
1585 && (ch
- HU_FONTSTART
< 0 || ch
- HU_FONTSTART
>= HU_FONTSIZE
))
1590 if (ch
>= 32 && ch
<= 127 &&
1591 saveCharIndex
< SAVESTRINGSIZE
-1 &&
1592 M_StringWidth(savegamestrings
[saveSlot
]) <
1593 (SAVESTRINGSIZE
-2)*8)
1595 savegamestrings
[saveSlot
][saveCharIndex
++] = ch
;
1596 savegamestrings
[saveSlot
][saveCharIndex
] = 0;
1603 // Take care of any messages that need input
1606 if (messageNeedsInput
)
1608 if (key
!= ' ' && key
!= KEY_ESCAPE
1609 && key
!= key_menu_confirm
&& key
!= key_menu_abort
)
1615 menuactive
= messageLastMenuActive
;
1618 messageRoutine(key
);
1621 S_StartSound(NULL
,sfx_swtchx
);
1625 if (devparm
&& key
== key_menu_help
)
1634 if (key
== key_menu_decscreen
) // Screen size down
1636 if (automapactive
|| chat_on
)
1639 S_StartSound(NULL
,sfx_stnmov
);
1642 else if (key
== key_menu_incscreen
) // Screen size up
1644 if (automapactive
|| chat_on
)
1647 S_StartSound(NULL
,sfx_stnmov
);
1650 else if (key
== key_menu_help
) // Help key
1652 M_StartControlPanel ();
1654 if ( gamemode
== retail
)
1655 currentMenu
= &ReadDef2
;
1657 currentMenu
= &ReadDef1
;
1660 S_StartSound(NULL
,sfx_swtchn
);
1663 else if (key
== key_menu_save
) // Save
1665 M_StartControlPanel();
1666 S_StartSound(NULL
,sfx_swtchn
);
1670 else if (key
== key_menu_load
) // Load
1672 M_StartControlPanel();
1673 S_StartSound(NULL
,sfx_swtchn
);
1677 else if (key
== key_menu_volume
) // Sound Volume
1679 M_StartControlPanel ();
1680 currentMenu
= &SoundDef
;
1682 S_StartSound(NULL
,sfx_swtchn
);
1685 else if (key
== key_menu_detail
) // Detail toggle
1688 S_StartSound(NULL
,sfx_swtchn
);
1691 else if (key
== key_menu_qsave
) // Quicksave
1693 S_StartSound(NULL
,sfx_swtchn
);
1697 else if (key
== key_menu_endgame
) // End game
1699 S_StartSound(NULL
,sfx_swtchn
);
1703 else if (key
== key_menu_messages
) // Toggle messages
1705 M_ChangeMessages(0);
1706 S_StartSound(NULL
,sfx_swtchn
);
1709 else if (key
== key_menu_qload
) // Quickload
1711 S_StartSound(NULL
,sfx_swtchn
);
1715 else if (key
== key_menu_quit
) // Quit DOOM
1717 S_StartSound(NULL
,sfx_swtchn
);
1721 else if (key
== key_menu_gamma
) // gamma toggle
1726 players
[consoleplayer
].message
= DEH_String(gammamsg
[usegamma
]);
1727 I_SetPalette (W_CacheLumpName (DEH_String("PLAYPAL"),PU_CACHE
));
1735 if (key
== key_menu_activate
)
1737 M_StartControlPanel ();
1738 S_StartSound(NULL
,sfx_swtchn
);
1745 // Keys usable within menu
1747 if (key
== key_menu_down
)
1749 // Move down to next item
1753 if (itemOn
+1 > currentMenu
->numitems
-1)
1756 S_StartSound(NULL
,sfx_pstop
);
1757 } while(currentMenu
->menuitems
[itemOn
].status
==-1);
1761 else if (key
== key_menu_up
)
1763 // Move back up to previous item
1768 itemOn
= currentMenu
->numitems
-1;
1770 S_StartSound(NULL
,sfx_pstop
);
1771 } while(currentMenu
->menuitems
[itemOn
].status
==-1);
1775 else if (key
== key_menu_left
)
1777 // Slide slider left
1779 if (currentMenu
->menuitems
[itemOn
].routine
&&
1780 currentMenu
->menuitems
[itemOn
].status
== 2)
1782 S_StartSound(NULL
,sfx_stnmov
);
1783 currentMenu
->menuitems
[itemOn
].routine(0);
1787 else if (key
== key_menu_right
)
1789 // Slide slider right
1791 if (currentMenu
->menuitems
[itemOn
].routine
&&
1792 currentMenu
->menuitems
[itemOn
].status
== 2)
1794 S_StartSound(NULL
,sfx_stnmov
);
1795 currentMenu
->menuitems
[itemOn
].routine(1);
1799 else if (key
== key_menu_forward
)
1801 // Activate menu item
1803 if (currentMenu
->menuitems
[itemOn
].routine
&&
1804 currentMenu
->menuitems
[itemOn
].status
)
1806 currentMenu
->lastOn
= itemOn
;
1807 if (currentMenu
->menuitems
[itemOn
].status
== 2)
1809 currentMenu
->menuitems
[itemOn
].routine(1); // right arrow
1810 S_StartSound(NULL
,sfx_stnmov
);
1814 currentMenu
->menuitems
[itemOn
].routine(itemOn
);
1815 S_StartSound(NULL
,sfx_pistol
);
1820 else if (key
== key_menu_activate
)
1824 currentMenu
->lastOn
= itemOn
;
1826 S_StartSound(NULL
,sfx_swtchx
);
1829 else if (key
== key_menu_back
)
1831 // Go back to previous menu
1833 currentMenu
->lastOn
= itemOn
;
1834 if (currentMenu
->prevMenu
)
1836 currentMenu
= currentMenu
->prevMenu
;
1837 itemOn
= currentMenu
->lastOn
;
1838 S_StartSound(NULL
,sfx_swtchn
);
1844 // Keyboard shortcut?
1846 for (i
= itemOn
+1;i
< currentMenu
->numitems
;i
++)
1848 if (currentMenu
->menuitems
[i
].alphaKey
== ch
)
1851 S_StartSound(NULL
,sfx_pstop
);
1856 for (i
= 0;i
<= itemOn
;i
++)
1858 if (currentMenu
->menuitems
[i
].alphaKey
== ch
)
1861 S_StartSound(NULL
,sfx_pstop
);
1873 // M_StartControlPanel
1875 void M_StartControlPanel (void)
1877 // intro might call this repeatedly
1882 currentMenu
= &MainDef
; // JDC
1883 itemOn
= currentMenu
->lastOn
; // JDC
1889 // Called after the view has been rendered,
1890 // but before it has been blitted.
1892 void M_Drawer (void)
1902 inhelpscreens
= false;
1904 // Horiz. & Vertically center string and print it.
1908 y
= 100 - M_StringHeight(messageString
) / 2;
1909 while (messageString
[start
] != '\0')
1911 int foundnewline
= 0;
1913 for (i
= 0; i
< strlen(messageString
+ start
); i
++)
1914 if (messageString
[start
+ i
] == '\n')
1916 memset(string
, 0, sizeof(string
));
1917 strncpy(string
, messageString
+ start
, i
);
1925 strcpy(string
, messageString
+ start
);
1926 start
+= strlen(string
);
1929 x
= 160 - M_StringWidth(string
) / 2;
1930 M_WriteText(x
, y
, string
);
1931 y
+= SHORT(hu_font
[0]->height
);
1940 if (currentMenu
->routine
)
1941 currentMenu
->routine(); // call Draw routine
1946 max
= currentMenu
->numitems
;
1950 name
= DEH_String(currentMenu
->menuitems
[i
].name
);
1954 V_DrawPatchDirect (x
,y
,0, W_CacheLumpName(name
, PU_CACHE
));
1961 V_DrawPatchDirect(x
+ SKULLXOFF
,currentMenu
->y
- 5 + itemOn
*LINEHEIGHT
, 0,
1962 W_CacheLumpName(DEH_String(skullName
[whichSkull
]),
1971 void M_ClearMenus (void)
1974 // if (!netgame && usergame && paused)
1975 // sendpause = true;
1984 void M_SetupNextMenu(menu_t
*menudef
)
1986 currentMenu
= menudef
;
1987 itemOn
= currentMenu
->lastOn
;
1994 void M_Ticker (void)
1996 if (--skullAnimCounter
<= 0)
1999 skullAnimCounter
= 8;
2009 currentMenu
= &MainDef
;
2011 itemOn
= currentMenu
->lastOn
;
2013 skullAnimCounter
= 10;
2014 screenSize
= screenblocks
- 3;
2016 messageString
= NULL
;
2017 messageLastMenuActive
= menuactive
;
2020 // Here we could catch other version dependencies,
2021 // like HELP1/2, and four episodes.
2027 // Commercial has no "read this" entry.
2028 MainMenu
[readthis
] = MainMenu
[quitdoom
];
2031 NewDef
.prevMenu
= &MainDef
;
2034 // Episode 2 and 3 are handled,
2035 // branching to an ad screen.
2037 // We need to remove the fourth episode.