Fix FS#10486 - "fuze screen not drawn correctly on backlight turn-on if playlist...
[kugel-rb/myfork.git] / apps / plugins / doom / m_menu.c
blob14d8474cbf53b179553b61cf117c6a333f0acf07
1 // Emacs style mode select -*- C++ -*-
2 //-----------------------------------------------------------------------------
3 //
4 // $Id$
5 //
6 // Copyright (C) 1993-1996 by id Software, Inc.
7 //
8 // This program is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU General Public License
10 // as published by the Free Software Foundation; either version 2
11 // of the License, or (at your option) any later version.
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 // GNU General Public License for more details.
18 // $Log$
19 // Revision 1.7 2006/12/13 04:44:17 kkurbjun
20 // Dehacked and BEX support for Doom - currently only supports a DEHACKED file in a WAD (not as a standalone file yet).
22 // Revision 1.6 2006-04-16 23:14:04 kkurbjun
23 // Fix run so that it stays enabled across level loads. Removed some unused code and added some back in for hopeful future use.
25 // Revision 1.5 2006-04-04 23:58:37 kkurbjun
26 // Make savegame strings more informative
28 // Revision 1.4 2006-04-04 23:13:50 kkurbjun
29 // Fix up configurable keys, edit exit string, more work needs to be done on menu keys
31 // Revision 1.3 2006-04-03 20:03:02 kkurbjun
32 // Updates doom menu w/ new graphics, now requires rockdoom.wad: http://alamode.mines.edu/~kkurbjun/rockdoom.wad
34 // Revision 1.2 2006-04-03 00:28:13 kkurbjun
35 // Fixes graphic errors in scaling code, note sure about the fix in hu_lib.c though. I havn't seen any corrupted text but it may still need a proper fix.
37 // Revision 1.1 2006-03-28 15:44:01 dave
38 // Patch #2969 - Doom! Currently only working on the H300.
41 // DESCRIPTION:
42 // DOOM selection menu, options, episode etc.
43 // Sliders and icons. Kinda widget stuff.
45 //-----------------------------------------------------------------------------
47 #include "doomdef.h"
48 #include "dstrings.h"
50 #include "d_main.h"
52 #include "i_system.h"
53 #include "i_video.h"
54 #include "z_zone.h"
55 #include "v_video.h"
56 #include "w_wad.h"
58 #include "r_main.h"
59 #include "d_deh.h"
60 #include "hu_stuff.h"
62 #include "g_game.h"
64 #include "m_argv.h"
65 #include "m_swap.h"
66 #include "s_sound.h"
68 #include "doomstat.h"
70 // Data.
71 #include "sounds.h"
73 #include "m_menu.h"
74 #include "rockmacros.h"
77 extern patchnum_t hu_font[HU_FONTSIZE];
78 extern boolean message_dontfuckwithme;
80 extern boolean chat_on; // in heads-up code
83 // defaulted values
85 int mouseSensitivity; // has default
87 // Show messages has default, 0 = off, 1 = on
88 int showMessages;
90 // Blocky mode, has default, 0 = high, 1 = normal
91 int screenblocks; // has default
93 // temp for screenblocks (0-9)
94 int screenSize;
96 // -1 = no quicksave slot picked!
97 int quickSaveSlot;
99 // 1 = message to be printed
100 int messageToPrint;
101 // ...and here is the message string!
102 const char* messageString;
104 // message x & y
105 int messx;
106 int messy;
107 int messageLastMenuActive;
109 // timed message = no input from user
110 boolean messageNeedsInput;
112 void (*messageRoutine)(int response);
114 #define SAVESTRINGSIZE 24
116 // we are going to be entering a savegame string
117 int saveStringEnter;
118 int saveSlot; // which slot to save in
119 int saveCharIndex; // which char we're editing
120 // old save description before edit
121 char saveOldString[SAVESTRINGSIZE];
123 boolean inhelpscreens;
124 boolean menuactive;
126 #define SKULLXOFF -32
127 #define LINEHEIGHT 16
129 extern boolean sendpause;
130 char savegamestrings[10][SAVESTRINGSIZE];
132 char endstring[170];
136 // MENU TYPEDEFS
138 typedef struct
140 // 0 = no cursor here, 1 = ok, 2 = arrows ok
141 short status;
143 char name[10];
145 // choice = menu item #.
146 // if status = 2,
147 // choice=0:leftarrow,1:rightarrow
148 void (*routine)(int choice);
150 // hotkey in menu
151 char alphaKey;
153 menuitem_t;
157 typedef struct menu_s
159 short numitems; // # of menu items
160 struct menu_s* prevMenu; // previous menu
161 menuitem_t* menuitems; // menu items
162 void (*routine)(void); // draw routine ROCKBOX
163 short x;
164 short y; // x,y of menu
165 short lastOn; // last item user was on in menu
167 menu_t;
169 short itemOn; // menu item skull is on
170 short skullAnimCounter; // skull animation counter
171 short whichSkull; // which skull to draw
172 int systemvol;
174 // graphic name of skulls
175 // warning: initializer-string for array of chars is too long
176 char skullName[2][/*8*/9] = {"M_SKULL1","M_SKULL2"};
178 // current menudef
179 menu_t* currentMenu;
182 // PROTOTYPES
184 void M_NewGame(int choice);
185 void M_Episode(int choice);
186 void M_ChooseSkill(int choice);
187 void M_LoadGame(int choice);
188 void M_SaveGame(int choice);
189 void M_Options(int choice);
190 void M_EndGame(int choice);
191 void M_ReadThis(int choice);
192 void M_ReadThis2(int choice);
193 void M_QuitDOOM(int choice);
195 void M_ChangeMessages(int choice);
196 void M_ChangeGamma(int choice);
197 void M_SfxVol(int choice);
198 void M_MusicVol(int choice);
199 void M_SystemVol(int choice);
200 void M_SizeDisplay(int choice);
201 void M_StartGame(int choice);
202 void M_Sound(int choice);
204 void M_FinishReadThis(int choice);
205 void M_LoadSelect(int choice);
206 void M_SaveSelect(int choice);
207 void M_ReadSaveStrings(void);
208 void M_QuickSave(void);
209 void M_QuickLoad(void);
211 void M_DrawMainMenu(void);
212 void M_DrawReadThis1(void);
213 void M_DrawReadThis2(void);
214 void M_DrawNewGame(void);
215 void M_DrawEpisode(void);
216 void M_DrawOptions(void);
217 void M_DrawSound(void);
218 void M_DrawLoad(void);
219 void M_DrawSave(void);
221 void M_DrawSaveLoadBorder(int x,int y);
222 void M_SetupNextMenu(menu_t *menudef);
223 void M_DrawThermo(int x,int y,int thermWidth,int thermDot);
224 void M_DrawEmptyCell(menu_t *menu,int item);
225 void M_DrawSelCell(menu_t *menu,int item);
226 void M_WriteText(int x, int y, char *string);
227 int M_StringWidth(const char* string);
228 int M_StringHeight(const char* string);
229 void M_StartControlPanel(void);
230 void M_StartMessage(const char *string,void *routine,boolean input);
231 void M_StopMessage(void);
232 void M_ClearMenus (void);
238 // DOOM MENU
240 enum
242 newgame = 0,
243 options,
244 loadgame,
245 savegame,
246 readthis,
247 quitdoom,
248 main_end
249 } main_e;
251 menuitem_t MainMenu[]=
253 {1,"M_NGAME",M_NewGame,'n'},
254 {1,"M_OPTION",M_Options,'o'},
255 {1,"M_LOADG",M_LoadGame,'l'},
256 {1,"M_SAVEG",M_SaveGame,'s'},
257 // Another hickup with Special edition.
258 {1,"M_RDTHIS",M_ReadThis,'r'},
259 {1,"M_QUITG",M_QuitDOOM,'q'}
262 menu_t MainDef =
264 main_end,
265 NULL,
266 MainMenu,
267 M_DrawMainMenu,
268 97,64,
274 // EPISODE SELECT
276 enum
278 ep1,
279 ep2,
280 ep3,
281 ep4,
282 ep_end
283 } episodes_e;
285 menuitem_t EpisodeMenu[]=
287 {1,"M_EPI1", M_Episode,'k'},
288 {1,"M_EPI2", M_Episode,'t'},
289 {1,"M_EPI3", M_Episode,'i'},
290 {1,"M_EPI4", M_Episode,'t'}
293 menu_t EpiDef =
295 ep_end, // # of menu items
296 &MainDef, // previous menu
297 EpisodeMenu, // menuitem_t ->
298 M_DrawEpisode, // drawing routine ->
299 48,63, // x,y
300 ep1 // lastOn
304 // NEW GAME
306 enum
308 killthings,
309 toorough,
310 hurtme,
311 violence,
312 nightmare,
313 newg_end
314 } newgame_e;
316 menuitem_t NewGameMenu[]=
318 {1,"M_JKILL", M_ChooseSkill, 'i'},
319 {1,"M_ROUGH", M_ChooseSkill, 'h'},
320 {1,"M_HURT", M_ChooseSkill, 'h'},
321 {1,"M_ULTRA", M_ChooseSkill, 'u'},
322 {1,"M_NMARE", M_ChooseSkill, 'n'}
325 menu_t NewDef =
327 newg_end, // # of menu items
328 &EpiDef, // previous menu
329 NewGameMenu, // menuitem_t ->
330 M_DrawNewGame, // drawing routine ->
331 48,63, // x,y
332 hurtme // lastOn
338 // OPTIONS MENU
340 enum
342 endgame,
343 messages,
344 scrnsize,
345 option_empty1,
346 gamasens,
347 option_empty2,
348 soundvol,
349 opt_end
350 } options_e;
352 menuitem_t OptionsMenu[]=
354 {1,"M_ENDGAM", M_EndGame,'e'},
355 {1,"M_MESSG", M_ChangeMessages,'m'},
356 {2,"M_SCRNSZ", M_SizeDisplay,'s'},
357 {-1,"",0,0},
358 {2,"M_GAMMA", M_ChangeGamma,'m'},
359 {-1,"",0,0},
360 {1,"M_SVOL", M_Sound,'s'}
363 menu_t OptionsDef =
365 opt_end,
366 &MainDef,
367 OptionsMenu,
368 M_DrawOptions,
369 60,37,
374 // Read This! MENU 1 & 2
376 enum
378 rdthsempty1,
379 read1_end
380 } read_e;
382 menuitem_t ReadMenu1[] =
384 {1,"",M_ReadThis2,0}
387 menu_t ReadDef1 =
389 read1_end,
390 &MainDef,
391 ReadMenu1,
392 M_DrawReadThis1,
393 280,185,
397 enum
399 rdthsempty2,
400 read2_end
401 } read_e2;
403 menuitem_t ReadMenu2[]=
405 {1,"",M_FinishReadThis,0}
408 menu_t ReadDef2 =
410 read2_end,
411 &ReadDef1,
412 ReadMenu2,
413 M_DrawReadThis2,
414 330,175,
419 // SOUND VOLUME MENU
421 enum
423 sfx_vol,
424 sfx_empty1,
425 music_vol,
426 sfx_empty2,
427 system_vol,
428 sfx_empty3,
429 sound_end
430 } sound_e;
432 menuitem_t SoundMenu[]=
434 {2,"M_SFXVOL",M_SfxVol,'s'},
435 {-1,"",0,0}, //ROCKBOX
436 {2,"M_MUSVOL",M_MusicVol,'m'},
437 {-1,"",0,0}, //ROCKBOX
438 {2,"M_SYSVOL",M_SystemVol,'z'},
439 {-1,"",0,0} //ROCKBOX
442 menu_t SoundDef =
444 sound_end,
445 &OptionsDef,
446 SoundMenu,
447 M_DrawSound,
448 80,64,
453 // LOAD GAME MENU
455 enum
457 load1,
458 load2,
459 load3,
460 load4,
461 load5,
462 load6,
463 load_end
464 } load_e;
466 menuitem_t LoadMenu[]=
468 {1,"", M_LoadSelect,'1'},
469 {1,"", M_LoadSelect,'2'},
470 {1,"", M_LoadSelect,'3'},
471 {1,"", M_LoadSelect,'4'},
472 {1,"", M_LoadSelect,'5'},
473 {1,"", M_LoadSelect,'6'}
476 menu_t LoadDef =
478 load_end,
479 &MainDef,
480 LoadMenu,
481 M_DrawLoad,
482 80,54,
487 // SAVE GAME MENU
489 menuitem_t SaveMenu[]=
491 {1,"", M_SaveSelect,'1'},
492 {1,"", M_SaveSelect,'2'},
493 {1,"", M_SaveSelect,'3'},
494 {1,"", M_SaveSelect,'4'},
495 {1,"", M_SaveSelect,'5'},
496 {1,"", M_SaveSelect,'6'}
499 menu_t SaveDef =
501 load_end,
502 &MainDef,
503 SaveMenu,
504 M_DrawSave,
505 80,54,
511 // M_ReadSaveStrings
512 // read the strings from the savegame files
514 void M_ReadSaveStrings(void)
516 int handle;
517 int count;
518 int i;
519 char name[256];
521 for (i = 0;i < load_end;i++)
523 if (M_CheckParm("-cdrom"))
524 snprintf(name,sizeof(name),"c:\\doomdata\\"SAVEGAMENAME"%d.dsg",i);
525 else
526 snprintf(name,sizeof(name),SAVEGAMENAME"%d.dsg",i);
528 handle = open (name, O_RDONLY | 0);
529 if (handle == -1)
531 strcpy(&savegamestrings[i][0],EMPTYSTRING);
532 LoadMenu[i].status = 0;
533 continue;
535 count = read (handle, &savegamestrings[i], SAVESTRINGSIZE);
536 close (handle);
537 LoadMenu[i].status = 1;
541 #define LOADGRAPHIC_Y 8
543 // M_LoadGame & Cie.
545 void M_DrawLoad(void)
547 int i;
549 V_DrawNamePatch(72 ,LOADGRAPHIC_Y, 0, "M_LOADG", CR_DEFAULT, VPT_STRETCH);
550 for (i = 0;i < load_end; i++)
552 M_DrawSaveLoadBorder(LoadDef.x,LoadDef.y+LINEHEIGHT*i);
553 M_WriteText(LoadDef.x,LoadDef.y+LINEHEIGHT*i,savegamestrings[i]);
560 // Draw border for the savegame description
562 void M_DrawSaveLoadBorder(int x,int y)
564 int i;
566 V_DrawNamePatch(x-8, y+7, 0, "M_LSLEFT", CR_DEFAULT, VPT_STRETCH);
567 for (i = 0;i < 24;i++)
569 V_DrawNamePatch(x, y+7, 0, "M_LSCNTR", CR_DEFAULT, VPT_STRETCH);
570 x += 8;
572 V_DrawNamePatch(x, y+7, 0, "M_LSRGHT", CR_DEFAULT, VPT_STRETCH);
578 // User wants to load this game
580 void M_LoadSelect(int choice)
582 char name[256];
584 if (M_CheckParm("-cdrom"))
585 snprintf(name,sizeof(name),"c:\\doomdata\\"SAVEGAMENAME"%d.dsg",choice);
586 else
587 snprintf(name,sizeof(name),SAVEGAMENAME"%d.dsg",choice);
588 G_LoadGame (choice, false);
589 M_ClearMenus ();
593 // Selected from DOOM menu
595 void M_LoadGame (int choice)
597 (void)choice;
598 if (netgame)
600 M_StartMessage(LOADNET,NULL,false);
601 return;
604 M_SetupNextMenu(&LoadDef);
605 M_ReadSaveStrings();
610 // M_SaveGame & Cie.
612 void M_DrawSave(void)
614 int i;
616 V_DrawNamePatch(72, LOADGRAPHIC_Y, 0, "M_SAVEG", CR_DEFAULT, VPT_STRETCH);
617 for (i = 0;i < load_end; i++)
619 M_DrawSaveLoadBorder(LoadDef.x,LoadDef.y+LINEHEIGHT*i);
620 M_WriteText(LoadDef.x,LoadDef.y+LINEHEIGHT*i,savegamestrings[i]);
623 if (saveStringEnter)
625 i = M_StringWidth(savegamestrings[saveSlot]);
626 M_WriteText(LoadDef.x + i,LoadDef.y+LINEHEIGHT*saveSlot,"_");
631 // M_Responder calls this when user is finished
633 void M_DoSave(int slot)
635 G_SaveGame (slot,savegamestrings[slot]);
636 M_ClearMenus ();
638 // PICK QUICKSAVE SLOT YET?
639 if (quickSaveSlot == -2)
640 quickSaveSlot = slot;
644 // User wants to save. Start string input for M_Responder
646 void M_SaveSelect(int choice)
648 // we are going to be intercepting all chars
649 saveStringEnter = 1;
651 saveSlot = choice;
652 snprintf(savegamestrings[choice], sizeof(savegamestrings[choice]),
653 (gamemode==shareware||gamemode==registered||gamemode==retail) ?
654 *mapnames[(gameepisode-1)*9+gamemap-1] : (gamemission==doom2) ?
655 *mapnames2[gamemap-1] : (gamemission==pack_plut) ?
656 *mapnamesp[gamemap-1] : (gamemission==pack_tnt) ?
657 *mapnamest[gamemap-1] : "Unknown Location", choice);
658 if (!strcmp(savegamestrings[choice],s_EMPTYSTRING))
659 savegamestrings[choice][0] = 0;
660 saveCharIndex = strlen(savegamestrings[choice]);
664 // Selected from DOOM menu
666 void M_SaveGame (int choice)
668 (void)choice;
669 if (!usergame)
671 M_StartMessage(s_SAVEDEAD,NULL,false);
672 return;
675 if (gamestate != GS_LEVEL)
676 return;
678 M_SetupNextMenu(&SaveDef);
679 M_ReadSaveStrings();
685 // M_QuickSave
687 char tempstring[80];
689 void M_QuickSaveResponse(int ch)
691 if (ch == 'y')
693 M_DoSave(quickSaveSlot);
695 S_StartSound(NULL,sfx_swtchx);
700 void M_QuickSave(void)
702 if (!usergame)
704 S_StartSound(NULL,sfx_oof);
705 return;
708 if (gamestate != GS_LEVEL)
709 return;
711 if (quickSaveSlot < 0)
713 M_StartControlPanel();
714 M_ReadSaveStrings();
715 M_SetupNextMenu(&SaveDef);
716 quickSaveSlot = -2; // means to pick a slot now
717 return;
719 snprintf(tempstring,sizeof(tempstring),s_QSPROMPT,savegamestrings[quickSaveSlot]);
720 M_StartMessage(tempstring,M_QuickSaveResponse,true);
726 // M_QuickLoad
728 void M_QuickLoadResponse(int ch)
730 if (ch == 'y')
732 M_LoadSelect(quickSaveSlot);
733 S_StartSound(NULL,sfx_swtchx);
738 void M_QuickLoad(void)
740 if (netgame)
742 M_StartMessage(QLOADNET,NULL,false);
743 return;
746 if (quickSaveSlot < 0)
748 M_StartMessage(QSAVESPOT,NULL,false);
749 return;
751 snprintf(tempstring, sizeof(tempstring), QLPROMPT,savegamestrings[quickSaveSlot]);
752 M_StartMessage(tempstring,M_QuickLoadResponse,true);
759 // Read This Menus
760 // Had a "quick hack to fix romero bug"
762 void M_DrawReadThis1(void)
764 inhelpscreens = true;
765 switch ( gamemode )
767 case commercial:
768 V_DrawNamePatch(0, 0, 0, "HELP", CR_DEFAULT, VPT_STRETCH);
769 break;
770 case shareware:
771 case registered:
772 case retail:
773 V_DrawNamePatch(0, 0, 0, "HELP1", CR_DEFAULT, VPT_STRETCH);
774 break;
775 default:
776 break;
778 return;
784 // Read This Menus - optional second page.
786 void M_DrawReadThis2(void)
788 inhelpscreens = true;
789 switch ( gamemode )
791 case retail:
792 case commercial:
793 // This hack keeps us from having to change menus.
794 V_DrawNamePatch(0, 0, 0, "CREDIT", CR_DEFAULT, VPT_STRETCH);
795 break;
796 case shareware:
797 case registered:
798 V_DrawNamePatch(0, 0, 0, "HELP2", CR_DEFAULT, VPT_STRETCH);
799 break;
800 default:
801 break;
803 return;
808 // Change Sfx & Music volumes
810 void M_DrawSound(void)
812 int sysmax=(rb->sound_max(SOUND_VOLUME)-rb->sound_min(SOUND_VOLUME));
813 V_DrawNamePatch(60, 38, 0, "M_SVOL", CR_DEFAULT, VPT_STRETCH);
815 M_DrawThermo(SoundDef.x,SoundDef.y+LINEHEIGHT*(sfx_vol+1),
816 16,snd_SfxVolume);
818 M_DrawThermo(SoundDef.x,SoundDef.y+LINEHEIGHT*(music_vol+1),
819 16,snd_MusicVolume);
821 M_DrawThermo(SoundDef.x,SoundDef.y+LINEHEIGHT*(system_vol+1),
822 16,(sysmax+systemvol)/5);
825 void M_Sound(int choice)
827 (void) choice;
828 M_SetupNextMenu(&SoundDef);
831 void M_SfxVol(int choice)
833 switch(choice)
835 case 0:
836 if (snd_SfxVolume)
837 snd_SfxVolume--;
838 break;
839 case 1:
840 if (snd_SfxVolume < 15)
841 snd_SfxVolume++;
842 break;
845 S_SetSfxVolume(snd_SfxVolume /* *8 */);
848 void M_MusicVol(int choice)
850 switch(choice)
852 case 0:
853 if (snd_MusicVolume)
854 snd_MusicVolume--;
855 break;
856 case 1:
857 if (snd_MusicVolume < 15)
858 snd_MusicVolume++;
859 break;
862 S_SetMusicVolume(snd_MusicVolume /* *8 */);
865 void M_SystemVol(int choice)
867 switch(choice)
869 case 0:
870 if (systemvol-5>rb->sound_min(SOUND_VOLUME))
872 systemvol-=5;
873 rb->sound_set(SOUND_VOLUME, systemvol);
874 rb->global_settings->volume = systemvol;
876 break;
877 case 1:
878 if (systemvol+5<rb->sound_max(SOUND_VOLUME))
880 systemvol+=5;
881 rb->sound_set(SOUND_VOLUME, systemvol);
882 rb->global_settings->volume = systemvol;
884 break;
889 // M_DrawMainMenu
891 void M_DrawMainMenu(void)
893 V_DrawNamePatch(94, 2, 0, "M_DOOM", CR_DEFAULT, VPT_STRETCH);
900 // M_NewGame
902 void M_DrawNewGame(void)
904 // CPhipps - patch drawing updated
905 V_DrawNamePatch(96, 14, 0, "M_NEWG", CR_DEFAULT, VPT_STRETCH);
906 V_DrawNamePatch(54, 38, 0, "M_SKILL",CR_DEFAULT, VPT_STRETCH);
909 void M_NewGame(int choice)
911 (void) choice;
912 if (netgame && !demoplayback)
914 M_StartMessage(s_NEWGAME,NULL,false);
915 return;
918 if ( gamemode == commercial )
919 M_SetupNextMenu(&NewDef);
920 else
921 M_SetupNextMenu(&EpiDef);
926 // M_Episode
928 int epi;
930 void M_DrawEpisode(void)
932 // CPhipps - patch drawing updated
933 V_DrawNamePatch(54, 38, 0, "M_EPISOD", CR_DEFAULT, VPT_STRETCH);
936 void M_VerifyNightmare(int ch)
938 if (ch != key_menu_enter)
939 return;
941 G_DeferedInitNew(nightmare,epi+1,1);
942 M_ClearMenus ();
945 void M_ChooseSkill(int choice)
947 if (choice == nightmare)
949 M_StartMessage(s_NIGHTMARE,M_VerifyNightmare,true);
950 return;
953 //jff 3/24/98 remember last skill selected
954 // killough 10/98 moved to here
955 defaultskill = choice+1;
957 G_DeferedInitNew(choice,epi+1,1);
958 M_ClearMenus ();
961 void M_Episode(int choice)
963 if ( (gamemode == shareware)
964 && choice)
966 M_StartMessage(s_SWSTRING,NULL,false); // Ty 03/27/98 - externalized
967 M_SetupNextMenu(&ReadDef1);
968 return;
971 // Yet another hack...
972 if ( (gamemode == registered)
973 && (choice > 2))
975 /* Digita */
976 // fprintf( stderr,
977 // "M_Episode: 4th episode requires UltimateDOOM\n");
978 choice = 0;
981 epi = choice;
982 M_SetupNextMenu(&NewDef);
988 // M_Options
990 char detailNames[2][9] = {"M_GDHIGH","M_GDLOW"};
991 char msgNames[2][9] = {"M_MSGOFF","M_MSGON"};
994 void M_DrawOptions(void)
996 // CPhipps - patch drawing updated
997 V_DrawNamePatch(108, 15, 0, "M_OPTTTL", CR_DEFAULT, VPT_STRETCH);
999 V_DrawNamePatch(OptionsDef.x + 120, OptionsDef.y+LINEHEIGHT*messages, 0,
1000 msgNames[showMessages], CR_DEFAULT, VPT_STRETCH);
1002 M_DrawThermo(OptionsDef.x,OptionsDef.y+LINEHEIGHT*(gamasens+1),
1003 4,usegamma);
1005 M_DrawThermo(OptionsDef.x,OptionsDef.y+LINEHEIGHT*(scrnsize+1),
1006 9,screenSize);
1009 void M_Options(int choice)
1011 (void)choice;
1012 M_SetupNextMenu(&OptionsDef);
1018 // Toggle messages on/off
1020 void M_ChangeMessages(int choice)
1022 // warning: unused parameter `int choice'
1023 choice = 0;
1024 showMessages = 1 - showMessages;
1026 if (!showMessages)
1027 players[consoleplayer].message = s_MSGOFF;
1028 else
1029 players[consoleplayer].message = s_MSGON ;
1031 message_dontfuckwithme = true;
1036 // M_EndGame
1038 void M_EndGameResponse(int ch)
1040 if (ch != key_menu_enter)
1041 return;
1043 // killough 5/26/98: make endgame quit if recording or playing back demo
1044 if (demorecording || singledemo)
1045 G_CheckDemoStatus();
1047 currentMenu->lastOn = itemOn;
1048 M_ClearMenus ();
1049 D_StartTitle ();
1052 void M_EndGame(int choice)
1054 choice = 0;
1055 if (!usergame)
1057 S_StartSound(NULL,sfx_oof);
1058 return;
1061 if (netgame)
1063 M_StartMessage(s_NETEND,NULL,false);
1064 return;
1067 M_StartMessage(s_ENDGAME,M_EndGameResponse,true);
1074 // M_ReadThis
1076 void M_ReadThis(int choice)
1078 choice = 0;
1079 M_SetupNextMenu(&ReadDef1);
1082 void M_ReadThis2(int choice)
1084 choice = 0;
1085 M_SetupNextMenu(&ReadDef2);
1088 void M_FinishReadThis(int choice)
1090 choice = 0;
1091 M_SetupNextMenu(&MainDef);
1098 // M_QuitDOOM
1100 int quitsounds[8] =
1102 sfx_pldeth,
1103 sfx_dmpain,
1104 sfx_popain,
1105 sfx_slop,
1106 sfx_telept,
1107 sfx_posit1,
1108 sfx_posit3,
1109 sfx_sgtatk
1112 int quitsounds2[8] =
1114 sfx_vilact,
1115 sfx_getpow,
1116 sfx_boscub,
1117 sfx_slop,
1118 sfx_skeswg,
1119 sfx_kntdth,
1120 sfx_bspact,
1121 sfx_sgtatk
1126 void M_QuitResponse(int ch)
1128 if (ch != key_menu_enter)
1129 return;
1130 if (!netgame)
1132 if (gamemode == commercial)
1133 S_StartSound(NULL,quitsounds2[(gametic>>2)&7]);
1134 else
1135 S_StartSound(NULL,quitsounds[(gametic>>2)&7]);
1136 I_WaitVBL(105);
1138 I_Quit ();
1144 void M_QuitDOOM(int choice)
1146 (void)choice;
1147 // We pick index 0 which is language sensitive,
1148 // or one at random, between 1 and maximum number.
1149 if (language != english )
1150 snprintf(endstring,sizeof(endstring),"%s\n\n%s",s_DOSY, endmsg[0] );
1151 else
1152 snprintf(endstring,sizeof(endstring),"%s\n\n%s", endmsg[gametic%(NUM_QUITMESSAGES-1)+1], s_DOSY);
1154 M_StartMessage(endstring,M_QuitResponse,true);
1160 void M_ChangeGamma(int choice)
1162 switch(choice)
1164 case 0:
1165 if (usegamma)
1166 usegamma--;
1167 break;
1168 case 1:
1169 if (usegamma < 4)
1170 usegamma++;
1171 break;
1173 V_SetPalette (0);
1176 void M_SizeDisplay(int choice)
1178 switch(choice)
1180 case 0:
1181 if (screenSize > 0)
1183 screenblocks--;
1184 screenSize--;
1186 break;
1187 case 1:
1188 if (screenSize < 8)
1190 screenblocks++;
1191 screenSize++;
1193 break;
1197 R_SetViewSize (screenblocks);
1204 // Menu Functions
1206 void
1207 M_DrawThermo
1208 ( int x,
1209 int y,
1210 int thermWidth,
1211 int thermDot )
1213 int xx;
1214 int i;
1216 xx = x;
1217 V_DrawNamePatch(xx, y, 0, "M_THERML", CR_DEFAULT, VPT_STRETCH);
1218 xx += 8;
1219 for (i=0;i<thermWidth;i++)
1221 V_DrawNamePatch(xx, y, 0, "M_THERMM", CR_DEFAULT, VPT_STRETCH);
1222 xx += 8;
1224 V_DrawNamePatch(xx, y, 0, "M_THERMR", CR_DEFAULT, VPT_STRETCH);
1225 V_DrawNamePatch((x+8)+thermDot*8,y,0,"M_THERMO",CR_DEFAULT,VPT_STRETCH);
1230 void
1231 M_DrawEmptyCell
1232 ( menu_t* menu,
1233 int item )
1235 // CPhipps - patch drawing updated
1236 V_DrawNamePatch(menu->x - 10, menu->y+item*LINEHEIGHT - 1, 0,
1237 "M_CELL1", CR_DEFAULT, VPT_STRETCH);
1240 void
1241 M_DrawSelCell
1242 ( menu_t* menu,
1243 int item )
1245 // CPhipps - patch drawing updated
1246 V_DrawNamePatch(menu->x - 10, menu->y+item*LINEHEIGHT - 1, 0,
1247 "M_CELL2", CR_DEFAULT, VPT_STRETCH);
1251 void
1252 M_StartMessage
1253 ( const char* string,
1254 void* routine,
1255 boolean input )
1257 messageLastMenuActive = menuactive;
1258 messageToPrint = 1;
1259 messageString = string;
1260 messageRoutine = routine;
1261 messageNeedsInput = input;
1262 menuactive = true;
1263 return;
1268 void M_StopMessage(void)
1270 menuactive = messageLastMenuActive;
1271 messageToPrint = 0;
1277 // Find string width from hu_font chars
1279 int M_StringWidth(const char* string)
1281 int i, c, w = 0;
1282 for (i = 0;(size_t)i < strlen(string);i++)
1283 w += (c = toupper(string[i]) - HU_FONTSTART) < 0 || c >= HU_FONTSIZE ?
1284 4 : SHORT(hu_font[c].width);
1285 return w;
1289 // Find string height from hu_font chars
1292 int M_StringHeight(const char* string)
1294 int i, h, height = h = SHORT(hu_font[0].height);
1295 for (i = 0;string[i];i++) // killough 1/31/98
1296 if (string[i] == '\n')
1297 h += height;
1298 return h;
1303 // Write a string using the hu_font
1305 void
1306 M_WriteText
1307 ( int x,
1308 int y,
1309 char* string)
1311 int w;
1312 char* ch;
1313 int c;
1314 int cx;
1315 int cy;
1318 ch = string;
1319 cx = x;
1320 cy = y;
1322 while(1)
1324 c = *ch++;
1325 if (!c)
1326 break;
1327 if (c == '\n')
1329 cx = x;
1330 cy += 12;
1331 continue;
1334 c = toupper(c) - HU_FONTSTART;
1335 if (c < 0 || c>= HU_FONTSIZE)
1337 cx += 4;
1338 continue;
1341 w = SHORT (hu_font[c].width);
1342 if (cx+w > 320)
1343 break;
1344 // proff/nicolas 09/20/98 -- changed for hi-res
1345 // CPhipps - patch drawing updated
1346 V_DrawNumPatch(cx, cy, 0, hu_font[c].lumpnum, CR_DEFAULT, VPT_STRETCH);
1347 cx+=w;
1354 // CONTROL PANEL
1358 // M_Responder
1360 boolean M_Responder (event_t* ev)
1362 int ch;
1363 int i;
1364 // static int joywait = 0;
1365 // static int mousewait = 0;
1366 // static int mousey = 0;
1367 // static int lasty = 0;
1368 // static int mousex = 0;
1369 // static int lastx = 0;
1371 ch = -1;
1373 // Process joystick input
1375 /* if (ev->type == ev_joystick && joywait < I_GetTime())
1377 if (ev->data3 == -1)
1379 ch = KEY_UPARROW;
1380 joywait = I_GetTime() + 5;
1382 else if (ev->data3 == 1)
1384 ch = KEY_DOWNARROW;
1385 joywait = I_GetTime() + 5;
1388 if (ev->data2 == -1)
1390 ch = KEY_LEFTARROW;
1391 joywait = I_GetTime() + 2;
1393 else if (ev->data2 == 1)
1395 ch = KEY_RIGHTARROW;
1396 joywait = I_GetTime() + 2;
1399 if (ev->data1&1)
1401 ch = key_menu_enter;
1402 joywait = I_GetTime() + 5;
1404 if (ev->data1&2)
1406 ch = KEY_BACKSPACE;
1407 joywait = I_GetTime() + 5;
1410 else
1412 // Process mouse input
1413 if (ev->type == ev_mouse && mousewait < I_GetTime())
1415 mousey += ev->data3;
1416 if (mousey < lasty-30)
1418 ch = KEY_DOWNARROW;
1419 mousewait = I_GetTime() + 5;
1420 mousey = lasty -= 30;
1422 else if (mousey > lasty+30)
1424 ch = KEY_UPARROW;
1425 mousewait = I_GetTime() + 5;
1426 mousey = lasty += 30;
1429 mousex += ev->data2;
1430 if (mousex < lastx-30)
1432 ch = KEY_LEFTARROW;
1433 mousewait = I_GetTime() + 5;
1434 mousex = lastx -= 30;
1436 else if (mousex > lastx+30)
1438 ch = KEY_RIGHTARROW;
1439 mousewait = I_GetTime() + 5;
1440 mousex = lastx += 30;
1443 if (ev->data1&1)
1445 ch = key_menu_enter;
1446 mousewait = I_GetTime() + 15;
1449 if (ev->data1&2)
1451 ch = KEY_BACKSPACE;
1452 mousewait = I_GetTime() + 15;
1455 else */if (ev->type == ev_keydown)
1457 ch = ev->data1;
1459 // }
1461 if (ch == -1)
1462 return false;
1465 // Save Game string input
1466 if (saveStringEnter)
1468 switch(ch)
1470 case KEY_BACKSPACE:
1471 if (saveCharIndex > 0)
1473 saveCharIndex--;
1474 savegamestrings[saveSlot][saveCharIndex] = 0;
1476 break;
1478 case KEY_ESCAPE:
1479 saveStringEnter = 0;
1480 strcpy(&savegamestrings[saveSlot][0],saveOldString);
1481 break;
1483 case KEY_ENTER:
1484 saveStringEnter = 0;
1485 if (savegamestrings[saveSlot][0])
1486 M_DoSave(saveSlot);
1487 break;
1489 default:
1490 ch = toupper(ch);
1491 if (ch != 32)
1492 if (ch-HU_FONTSTART < 0 || ch-HU_FONTSTART >= HU_FONTSIZE)
1493 break;
1494 if (ch >= 32 && ch <= 127 &&
1495 saveCharIndex < SAVESTRINGSIZE-1 &&
1496 M_StringWidth(savegamestrings[saveSlot]) <
1497 (SAVESTRINGSIZE-2)*8)
1499 savegamestrings[saveSlot][saveCharIndex++] = ch;
1500 savegamestrings[saveSlot][saveCharIndex] = 0;
1502 break;
1504 return true;
1507 // Take care of any messages that need input
1508 if (messageToPrint)
1510 if (messageNeedsInput == true &&
1511 !(ch == ' ' || ch == 'n' || ch == key_menu_enter || ch == key_menu_escape))
1512 return false;
1514 menuactive = messageLastMenuActive;
1515 messageToPrint = 0;
1516 if (messageRoutine)
1517 messageRoutine(ch);
1519 menuactive = false;
1520 S_StartSound(NULL,sfx_swtchx);
1521 return true;
1524 if (ch == KEY_F1) // devparm &&
1526 G_ScreenShot ();
1527 return true;
1530 // F-Keys
1531 if (!menuactive)
1533 if (ch == key_autorun) // Autorun // V
1535 autorun = !autorun;
1536 return true;
1539 switch(ch)
1542 case KEY_F1: // Help key
1543 M_StartControlPanel ();
1545 if ( gamemode == retail )
1546 currentMenu = &ReadDef2;
1547 else
1548 currentMenu = &ReadDef1;
1550 itemOn = 0;
1551 S_StartSound(NULL,sfx_swtchn);
1552 return true;
1554 case KEY_F6: // Quicksave
1555 S_StartSound(NULL,sfx_swtchn);
1556 M_QuickSave();
1557 return true;
1559 case KEY_F9: // Quickload
1560 S_StartSound(NULL,sfx_swtchn);
1561 M_QuickLoad();
1562 return true;
1568 // Pop-up menu?
1569 if (!menuactive)
1571 if (ch == key_menu_escape)
1573 M_StartControlPanel ();
1574 S_StartSound(NULL,sfx_swtchn);
1575 return true;
1577 return false;
1581 // Keys usable within menu
1582 switch (ch)
1584 case KEY_DOWNARROW:
1587 if (itemOn+1 > currentMenu->numitems-1)
1588 itemOn = 0;
1589 else
1590 itemOn++;
1591 S_StartSound(NULL,sfx_pstop);
1593 while(currentMenu->menuitems[itemOn].status==-1);
1594 return true;
1596 case KEY_UPARROW:
1599 if (!itemOn)
1600 itemOn = currentMenu->numitems-1;
1601 else
1602 itemOn--;
1603 S_StartSound(NULL,sfx_pstop);
1605 while(currentMenu->menuitems[itemOn].status==-1);
1606 return true;
1608 case KEY_LEFTARROW:
1609 if (currentMenu->menuitems[itemOn].routine &&
1610 currentMenu->menuitems[itemOn].status == 2)
1612 S_StartSound(NULL,sfx_stnmov);
1613 currentMenu->menuitems[itemOn].routine(0);
1615 return true;
1617 case KEY_RIGHTARROW:
1618 if (currentMenu->menuitems[itemOn].routine &&
1619 currentMenu->menuitems[itemOn].status == 2)
1621 S_StartSound(NULL,sfx_stnmov);
1622 currentMenu->menuitems[itemOn].routine(1);
1624 return true;
1626 case KEY_ENTER:
1627 if (currentMenu->menuitems[itemOn].routine &&
1628 currentMenu->menuitems[itemOn].status)
1630 currentMenu->lastOn = itemOn;
1631 if (currentMenu->menuitems[itemOn].status == 2)
1633 currentMenu->menuitems[itemOn].routine(1); // right arrow
1634 S_StartSound(NULL,sfx_stnmov);
1636 else
1638 currentMenu->menuitems[itemOn].routine(itemOn);
1639 S_StartSound(NULL,sfx_pistol);
1642 return true;
1644 case KEY_ESCAPE:
1645 currentMenu->lastOn = itemOn;
1646 M_ClearMenus ();
1647 S_StartSound(NULL,sfx_swtchx);
1648 return true;
1650 case KEY_BACKSPACE:
1651 currentMenu->lastOn = itemOn;
1652 if (currentMenu->prevMenu)
1654 currentMenu = currentMenu->prevMenu;
1655 itemOn = currentMenu->lastOn;
1656 S_StartSound(NULL,sfx_swtchn);
1658 return true;
1660 default:
1661 for (i = itemOn+1;i < currentMenu->numitems;i++)
1662 if (currentMenu->menuitems[i].alphaKey == ch)
1664 itemOn = i;
1665 S_StartSound(NULL,sfx_pstop);
1666 return true;
1668 for (i = 0;i <= itemOn;i++)
1669 if (currentMenu->menuitems[i].alphaKey == ch)
1671 itemOn = i;
1672 S_StartSound(NULL,sfx_pstop);
1673 return true;
1675 break;
1679 return false;
1685 // M_StartControlPanel
1687 void M_StartControlPanel (void)
1689 // intro might call this repeatedly
1690 if (menuactive)
1691 return;
1693 menuactive = 1;
1694 currentMenu = &MainDef; // JDC
1695 itemOn = currentMenu->lastOn; // JDC
1700 // M_Drawer
1701 // Called after the view has been rendered,
1702 // but before it has been blitted.
1704 void M_Drawer (void)
1706 static short x;
1707 static short y;
1708 unsigned short i;
1709 short max;
1710 char string[40];
1711 int start;
1713 inhelpscreens = false;
1716 // Horiz. & Vertically center string and print it.
1717 if (messageToPrint)
1719 start = 0;
1720 y = 100 - M_StringHeight(messageString)/2;
1721 while(*(messageString+start))
1723 for (i = 0;i < strlen(messageString+start);i++)
1724 if (*(messageString+start+i) == '\n')
1726 memset(string,0,40);
1727 strncpy(string,messageString+start,i);
1728 start += i+1;
1729 break;
1731 if (i == strlen(messageString+start))
1733 strcpy(string,messageString+start);
1734 start += i;
1737 x = 160 - M_StringWidth(string)/2;
1738 M_WriteText(x,y,string);
1739 y += SHORT(hu_font[0].height);
1741 return;
1744 if (!menuactive)
1745 return;
1747 if (currentMenu->routine)
1748 currentMenu->routine(); // call Draw routine
1750 // DRAW MENU
1751 x = currentMenu->x;
1752 y = currentMenu->y;
1753 max = currentMenu->numitems;
1755 for (i=0;i<max;i++)
1757 if (currentMenu->menuitems[i].name[0])
1758 V_DrawNamePatch(x,y,0,currentMenu->menuitems[i].name,
1759 CR_DEFAULT, VPT_STRETCH);
1760 y += LINEHEIGHT;
1763 // DRAW SKULL
1764 // CPhipps - patch drawing updated
1765 V_DrawNamePatch(x + SKULLXOFF, currentMenu->y - 5 + itemOn*LINEHEIGHT,0,
1766 skullName[whichSkull], CR_DEFAULT, VPT_STRETCH);
1772 // M_ClearMenus
1774 void M_ClearMenus (void)
1776 menuactive = 0;
1777 // if (!netgame && usergame && paused)
1778 // sendpause = true;
1785 // M_SetupNextMenu
1787 void M_SetupNextMenu(menu_t *menudef)
1789 currentMenu = menudef;
1790 itemOn = currentMenu->lastOn;
1795 // M_Ticker
1797 void M_Ticker (void)
1799 if (--skullAnimCounter <= 0)
1801 whichSkull ^= 1;
1802 skullAnimCounter = 8;
1808 // M_Init
1810 void M_Init (void)
1812 currentMenu = &MainDef;
1813 menuactive = 0;
1814 itemOn = currentMenu->lastOn;
1815 whichSkull = 0;
1816 skullAnimCounter = 10;
1817 screenSize = screenblocks - 3;
1818 messageToPrint = 0;
1819 messageString = NULL;
1820 messageLastMenuActive = menuactive;
1821 quickSaveSlot = -1;
1823 // Here we could catch other version dependencies,
1824 // like HELP1/2, and four episodes.
1827 switch ( gamemode )
1829 case commercial:
1830 // This is used because DOOM 2 had only one HELP
1831 // page. I use CREDIT as second page now, but
1832 // kept this hack for educational purposes.
1833 MainMenu[readthis] = MainMenu[quitdoom];
1834 MainDef.numitems--;
1835 MainDef.y += 8;
1836 NewDef.prevMenu = &MainDef;
1837 ReadDef1.routine = M_DrawReadThis1;
1838 ReadDef1.x = 330;
1839 ReadDef1.y = 165;
1840 ReadMenu1[0].routine = M_FinishReadThis;
1841 break;
1842 case shareware:
1843 // Episode 2 and 3 are handled,
1844 // branching to an ad screen.
1845 case registered:
1846 // We need to remove the fourth episode.
1847 EpiDef.numitems--;
1848 break;
1849 case retail:
1850 // We are fine.
1851 default:
1852 break;