lcd-m6sp.c: remove \r
[kugel-rb.git] / apps / plugins / doom / m_menu.c
blobb0b0f9fff851ec2c6f429ece72b22c541473ebf7
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
250 unsigned main_e;
252 menuitem_t MainMenu[]=
254 {1,"M_NGAME",M_NewGame,'n'},
255 {1,"M_OPTION",M_Options,'o'},
256 {1,"M_LOADG",M_LoadGame,'l'},
257 {1,"M_SAVEG",M_SaveGame,'s'},
258 // Another hickup with Special edition.
259 {1,"M_RDTHIS",M_ReadThis,'r'},
260 {1,"M_QUITG",M_QuitDOOM,'q'}
263 menu_t MainDef =
265 main_end,
266 NULL,
267 MainMenu,
268 M_DrawMainMenu,
269 97,64,
275 // EPISODE SELECT
277 enum
279 ep1,
280 ep2,
281 ep3,
282 ep4,
283 ep_end
285 unsigned episodes_e;
287 menuitem_t EpisodeMenu[]=
289 {1,"M_EPI1", M_Episode,'k'},
290 {1,"M_EPI2", M_Episode,'t'},
291 {1,"M_EPI3", M_Episode,'i'},
292 {1,"M_EPI4", M_Episode,'t'}
295 menu_t EpiDef =
297 ep_end, // # of menu items
298 &MainDef, // previous menu
299 EpisodeMenu, // menuitem_t ->
300 M_DrawEpisode, // drawing routine ->
301 48,63, // x,y
302 ep1 // lastOn
306 // NEW GAME
308 enum
310 killthings,
311 toorough,
312 hurtme,
313 violence,
314 nightmare,
315 newg_end
317 unsigned newgame_e;
319 menuitem_t NewGameMenu[]=
321 {1,"M_JKILL", M_ChooseSkill, 'i'},
322 {1,"M_ROUGH", M_ChooseSkill, 'h'},
323 {1,"M_HURT", M_ChooseSkill, 'h'},
324 {1,"M_ULTRA", M_ChooseSkill, 'u'},
325 {1,"M_NMARE", M_ChooseSkill, 'n'}
328 menu_t NewDef =
330 newg_end, // # of menu items
331 &EpiDef, // previous menu
332 NewGameMenu, // menuitem_t ->
333 M_DrawNewGame, // drawing routine ->
334 48,63, // x,y
335 hurtme // lastOn
341 // OPTIONS MENU
343 enum
345 endgame,
346 messages,
347 scrnsize,
348 option_empty1,
349 gamasens,
350 option_empty2,
351 soundvol,
352 opt_end
354 unsigned options_e;
356 menuitem_t OptionsMenu[]=
358 {1,"M_ENDGAM", M_EndGame,'e'},
359 {1,"M_MESSG", M_ChangeMessages,'m'},
360 {2,"M_SCRNSZ", M_SizeDisplay,'s'},
361 {-1,"",0,0},
362 {2,"M_GAMMA", M_ChangeGamma,'m'},
363 {-1,"",0,0},
364 {1,"M_SVOL", M_Sound,'s'}
367 menu_t OptionsDef =
369 opt_end,
370 &MainDef,
371 OptionsMenu,
372 M_DrawOptions,
373 60,37,
378 // Read This! MENU 1 & 2
380 enum
382 rdthsempty1,
383 read1_end
385 unsigned read_e;
387 menuitem_t ReadMenu1[] =
389 {1,"",M_ReadThis2,0}
392 menu_t ReadDef1 =
394 read1_end,
395 &MainDef,
396 ReadMenu1,
397 M_DrawReadThis1,
398 280,185,
402 enum
404 rdthsempty2,
405 read2_end
407 unsigned read_e2;
409 menuitem_t ReadMenu2[]=
411 {1,"",M_FinishReadThis,0}
414 menu_t ReadDef2 =
416 read2_end,
417 &ReadDef1,
418 ReadMenu2,
419 M_DrawReadThis2,
420 330,175,
425 // SOUND VOLUME MENU
427 enum
429 sfx_vol,
430 sfx_empty1,
431 music_vol,
432 sfx_empty2,
433 system_vol,
434 sfx_empty3,
435 sound_end
437 unsigned sound_e;
439 menuitem_t SoundMenu[]=
441 {2,"M_SFXVOL",M_SfxVol,'s'},
442 {-1,"",0,0}, //ROCKBOX
443 {2,"M_MUSVOL",M_MusicVol,'m'},
444 {-1,"",0,0}, //ROCKBOX
445 {2,"M_SYSVOL",M_SystemVol,'z'},
446 {-1,"",0,0} //ROCKBOX
449 menu_t SoundDef =
451 sound_end,
452 &OptionsDef,
453 SoundMenu,
454 M_DrawSound,
455 80,64,
460 // LOAD GAME MENU
462 enum
464 load1,
465 load2,
466 load3,
467 load4,
468 load5,
469 load6,
470 load_end
472 unsigned load_e;
474 menuitem_t LoadMenu[]=
476 {1,"", M_LoadSelect,'1'},
477 {1,"", M_LoadSelect,'2'},
478 {1,"", M_LoadSelect,'3'},
479 {1,"", M_LoadSelect,'4'},
480 {1,"", M_LoadSelect,'5'},
481 {1,"", M_LoadSelect,'6'}
484 menu_t LoadDef =
486 load_end,
487 &MainDef,
488 LoadMenu,
489 M_DrawLoad,
490 80,54,
495 // SAVE GAME MENU
497 menuitem_t SaveMenu[]=
499 {1,"", M_SaveSelect,'1'},
500 {1,"", M_SaveSelect,'2'},
501 {1,"", M_SaveSelect,'3'},
502 {1,"", M_SaveSelect,'4'},
503 {1,"", M_SaveSelect,'5'},
504 {1,"", M_SaveSelect,'6'}
507 menu_t SaveDef =
509 load_end,
510 &MainDef,
511 SaveMenu,
512 M_DrawSave,
513 80,54,
519 // M_ReadSaveStrings
520 // read the strings from the savegame files
522 void M_ReadSaveStrings(void)
524 int handle;
525 int count;
526 int i;
527 char name[256];
529 for (i = 0;i < load_end;i++)
531 if (M_CheckParm("-cdrom"))
532 snprintf(name,sizeof(name),"c:\\doomdata\\"SAVEGAMENAME"%d.dsg",i);
533 else
534 snprintf(name,sizeof(name),SAVEGAMENAME"%d.dsg",i);
536 handle = open (name, O_RDONLY | 0);
537 if (handle == -1)
539 strcpy(&savegamestrings[i][0],EMPTYSTRING);
540 LoadMenu[i].status = 0;
541 continue;
543 count = read (handle, &savegamestrings[i], SAVESTRINGSIZE);
544 close (handle);
545 LoadMenu[i].status = 1;
549 #define LOADGRAPHIC_Y 8
551 // M_LoadGame & Cie.
553 void M_DrawLoad(void)
555 int i;
557 V_DrawNamePatch(72 ,LOADGRAPHIC_Y, 0, "M_LOADG", CR_DEFAULT, VPT_STRETCH);
558 for (i = 0;i < load_end; i++)
560 M_DrawSaveLoadBorder(LoadDef.x,LoadDef.y+LINEHEIGHT*i);
561 M_WriteText(LoadDef.x,LoadDef.y+LINEHEIGHT*i,savegamestrings[i]);
568 // Draw border for the savegame description
570 void M_DrawSaveLoadBorder(int x,int y)
572 int i;
574 V_DrawNamePatch(x-8, y+7, 0, "M_LSLEFT", CR_DEFAULT, VPT_STRETCH);
575 for (i = 0;i < 24;i++)
577 V_DrawNamePatch(x, y+7, 0, "M_LSCNTR", CR_DEFAULT, VPT_STRETCH);
578 x += 8;
580 V_DrawNamePatch(x, y+7, 0, "M_LSRGHT", CR_DEFAULT, VPT_STRETCH);
586 // User wants to load this game
588 void M_LoadSelect(int choice)
590 char name[256];
592 if (M_CheckParm("-cdrom"))
593 snprintf(name,sizeof(name),"c:\\doomdata\\"SAVEGAMENAME"%d.dsg",choice);
594 else
595 snprintf(name,sizeof(name),SAVEGAMENAME"%d.dsg",choice);
596 G_LoadGame (choice, false);
597 M_ClearMenus ();
601 // Selected from DOOM menu
603 void M_LoadGame (int choice)
605 (void)choice;
606 if (netgame)
608 M_StartMessage(LOADNET,NULL,false);
609 return;
612 M_SetupNextMenu(&LoadDef);
613 M_ReadSaveStrings();
618 // M_SaveGame & Cie.
620 void M_DrawSave(void)
622 int i;
624 V_DrawNamePatch(72, LOADGRAPHIC_Y, 0, "M_SAVEG", CR_DEFAULT, VPT_STRETCH);
625 for (i = 0;i < load_end; i++)
627 M_DrawSaveLoadBorder(LoadDef.x,LoadDef.y+LINEHEIGHT*i);
628 M_WriteText(LoadDef.x,LoadDef.y+LINEHEIGHT*i,savegamestrings[i]);
631 if (saveStringEnter)
633 i = M_StringWidth(savegamestrings[saveSlot]);
634 M_WriteText(LoadDef.x + i,LoadDef.y+LINEHEIGHT*saveSlot,"_");
639 // M_Responder calls this when user is finished
641 void M_DoSave(int slot)
643 G_SaveGame (slot,savegamestrings[slot]);
644 M_ClearMenus ();
646 // PICK QUICKSAVE SLOT YET?
647 if (quickSaveSlot == -2)
648 quickSaveSlot = slot;
652 // User wants to save. Start string input for M_Responder
654 void M_SaveSelect(int choice)
656 // we are going to be intercepting all chars
657 saveStringEnter = 1;
659 saveSlot = choice;
660 snprintf(savegamestrings[choice], sizeof(savegamestrings[choice]),
661 (gamemode==shareware||gamemode==registered||gamemode==retail) ?
662 *mapnames[(gameepisode-1)*9+gamemap-1] : (gamemission==doom2) ?
663 *mapnames2[gamemap-1] : (gamemission==pack_plut) ?
664 *mapnamesp[gamemap-1] : (gamemission==pack_tnt) ?
665 *mapnamest[gamemap-1] : "Unknown Location", choice);
666 if (!strcmp(savegamestrings[choice],s_EMPTYSTRING))
667 savegamestrings[choice][0] = 0;
668 saveCharIndex = strlen(savegamestrings[choice]);
672 // Selected from DOOM menu
674 void M_SaveGame (int choice)
676 (void)choice;
677 if (!usergame)
679 M_StartMessage(s_SAVEDEAD,NULL,false);
680 return;
683 if (gamestate != GS_LEVEL)
684 return;
686 M_SetupNextMenu(&SaveDef);
687 M_ReadSaveStrings();
693 // M_QuickSave
695 char tempstring[80];
697 void M_QuickSaveResponse(int ch)
699 if (ch == 'y')
701 M_DoSave(quickSaveSlot);
703 S_StartSound(NULL,sfx_swtchx);
708 void M_QuickSave(void)
710 if (!usergame)
712 S_StartSound(NULL,sfx_oof);
713 return;
716 if (gamestate != GS_LEVEL)
717 return;
719 if (quickSaveSlot < 0)
721 M_StartControlPanel();
722 M_ReadSaveStrings();
723 M_SetupNextMenu(&SaveDef);
724 quickSaveSlot = -2; // means to pick a slot now
725 return;
727 snprintf(tempstring,sizeof(tempstring),s_QSPROMPT,savegamestrings[quickSaveSlot]);
728 M_StartMessage(tempstring,M_QuickSaveResponse,true);
734 // M_QuickLoad
736 void M_QuickLoadResponse(int ch)
738 if (ch == 'y')
740 M_LoadSelect(quickSaveSlot);
741 S_StartSound(NULL,sfx_swtchx);
746 void M_QuickLoad(void)
748 if (netgame)
750 M_StartMessage(QLOADNET,NULL,false);
751 return;
754 if (quickSaveSlot < 0)
756 M_StartMessage(QSAVESPOT,NULL,false);
757 return;
759 snprintf(tempstring, sizeof(tempstring), QLPROMPT,savegamestrings[quickSaveSlot]);
760 M_StartMessage(tempstring,M_QuickLoadResponse,true);
767 // Read This Menus
768 // Had a "quick hack to fix romero bug"
770 void M_DrawReadThis1(void)
772 inhelpscreens = true;
773 switch ( gamemode )
775 case commercial:
776 V_DrawNamePatch(0, 0, 0, "HELP", CR_DEFAULT, VPT_STRETCH);
777 break;
778 case shareware:
779 case registered:
780 case retail:
781 V_DrawNamePatch(0, 0, 0, "HELP1", CR_DEFAULT, VPT_STRETCH);
782 break;
783 default:
784 break;
786 return;
792 // Read This Menus - optional second page.
794 void M_DrawReadThis2(void)
796 inhelpscreens = true;
797 switch ( gamemode )
799 case retail:
800 case commercial:
801 // This hack keeps us from having to change menus.
802 V_DrawNamePatch(0, 0, 0, "CREDIT", CR_DEFAULT, VPT_STRETCH);
803 break;
804 case shareware:
805 case registered:
806 V_DrawNamePatch(0, 0, 0, "HELP2", CR_DEFAULT, VPT_STRETCH);
807 break;
808 default:
809 break;
811 return;
816 // Change Sfx & Music volumes
818 void M_DrawSound(void)
820 int sysmax=(rb->sound_max(SOUND_VOLUME)-rb->sound_min(SOUND_VOLUME));
821 V_DrawNamePatch(60, 38, 0, "M_SVOL", CR_DEFAULT, VPT_STRETCH);
823 M_DrawThermo(SoundDef.x,SoundDef.y+LINEHEIGHT*(sfx_vol+1),
824 16,snd_SfxVolume);
826 M_DrawThermo(SoundDef.x,SoundDef.y+LINEHEIGHT*(music_vol+1),
827 16,snd_MusicVolume);
829 M_DrawThermo(SoundDef.x,SoundDef.y+LINEHEIGHT*(system_vol+1),
830 16,(sysmax+systemvol)/5);
833 void M_Sound(int choice)
835 (void) choice;
836 M_SetupNextMenu(&SoundDef);
839 void M_SfxVol(int choice)
841 switch(choice)
843 case 0:
844 if (snd_SfxVolume)
845 snd_SfxVolume--;
846 break;
847 case 1:
848 if (snd_SfxVolume < 15)
849 snd_SfxVolume++;
850 break;
853 S_SetSfxVolume(snd_SfxVolume /* *8 */);
856 void M_MusicVol(int choice)
858 switch(choice)
860 case 0:
861 if (snd_MusicVolume)
862 snd_MusicVolume--;
863 break;
864 case 1:
865 if (snd_MusicVolume < 15)
866 snd_MusicVolume++;
867 break;
870 S_SetMusicVolume(snd_MusicVolume /* *8 */);
873 void M_SystemVol(int choice)
875 switch(choice)
877 case 0:
878 if (systemvol-5>rb->sound_min(SOUND_VOLUME))
880 systemvol-=5;
881 rb->sound_set(SOUND_VOLUME, systemvol);
882 rb->global_settings->volume = systemvol;
884 break;
885 case 1:
886 if (systemvol+5<rb->sound_max(SOUND_VOLUME))
888 systemvol+=5;
889 rb->sound_set(SOUND_VOLUME, systemvol);
890 rb->global_settings->volume = systemvol;
892 break;
897 // M_DrawMainMenu
899 void M_DrawMainMenu(void)
901 V_DrawNamePatch(94, 2, 0, "M_DOOM", CR_DEFAULT, VPT_STRETCH);
908 // M_NewGame
910 void M_DrawNewGame(void)
912 // CPhipps - patch drawing updated
913 V_DrawNamePatch(96, 14, 0, "M_NEWG", CR_DEFAULT, VPT_STRETCH);
914 V_DrawNamePatch(54, 38, 0, "M_SKILL",CR_DEFAULT, VPT_STRETCH);
917 void M_NewGame(int choice)
919 (void) choice;
920 if (netgame && !demoplayback)
922 M_StartMessage(s_NEWGAME,NULL,false);
923 return;
926 if ( gamemode == commercial )
927 M_SetupNextMenu(&NewDef);
928 else
929 M_SetupNextMenu(&EpiDef);
934 // M_Episode
936 int epi;
938 void M_DrawEpisode(void)
940 // CPhipps - patch drawing updated
941 V_DrawNamePatch(54, 38, 0, "M_EPISOD", CR_DEFAULT, VPT_STRETCH);
944 void M_VerifyNightmare(int ch)
946 if (ch != key_menu_enter)
947 return;
949 G_DeferedInitNew(nightmare,epi+1,1);
950 M_ClearMenus ();
953 void M_ChooseSkill(int choice)
955 if (choice == nightmare)
957 M_StartMessage(s_NIGHTMARE,M_VerifyNightmare,true);
958 return;
961 //jff 3/24/98 remember last skill selected
962 // killough 10/98 moved to here
963 defaultskill = choice+1;
965 G_DeferedInitNew(choice,epi+1,1);
966 M_ClearMenus ();
969 void M_Episode(int choice)
971 if ( (gamemode == shareware)
972 && choice)
974 M_StartMessage(s_SWSTRING,NULL,false); // Ty 03/27/98 - externalized
975 M_SetupNextMenu(&ReadDef1);
976 return;
979 // Yet another hack...
980 if ( (gamemode == registered)
981 && (choice > 2))
983 /* Digita */
984 // fprintf( stderr,
985 // "M_Episode: 4th episode requires UltimateDOOM\n");
986 choice = 0;
989 epi = choice;
990 M_SetupNextMenu(&NewDef);
996 // M_Options
998 char detailNames[2][9] = {"M_GDHIGH","M_GDLOW"};
999 char msgNames[2][9] = {"M_MSGOFF","M_MSGON"};
1002 void M_DrawOptions(void)
1004 // CPhipps - patch drawing updated
1005 V_DrawNamePatch(108, 15, 0, "M_OPTTTL", CR_DEFAULT, VPT_STRETCH);
1007 V_DrawNamePatch(OptionsDef.x + 120, OptionsDef.y+LINEHEIGHT*messages, 0,
1008 msgNames[showMessages], CR_DEFAULT, VPT_STRETCH);
1010 M_DrawThermo(OptionsDef.x,OptionsDef.y+LINEHEIGHT*(gamasens+1),
1011 4,usegamma);
1013 M_DrawThermo(OptionsDef.x,OptionsDef.y+LINEHEIGHT*(scrnsize+1),
1014 9,screenSize);
1017 void M_Options(int choice)
1019 (void)choice;
1020 M_SetupNextMenu(&OptionsDef);
1026 // Toggle messages on/off
1028 void M_ChangeMessages(int choice)
1030 // warning: unused parameter `int choice'
1031 choice = 0;
1032 showMessages = 1 - showMessages;
1034 if (!showMessages)
1035 players[consoleplayer].message = s_MSGOFF;
1036 else
1037 players[consoleplayer].message = s_MSGON ;
1039 message_dontfuckwithme = true;
1044 // M_EndGame
1046 void M_EndGameResponse(int ch)
1048 if (ch != key_menu_enter)
1049 return;
1051 // killough 5/26/98: make endgame quit if recording or playing back demo
1052 if (demorecording || singledemo)
1053 G_CheckDemoStatus();
1055 currentMenu->lastOn = itemOn;
1056 M_ClearMenus ();
1057 D_StartTitle ();
1060 void M_EndGame(int choice)
1062 choice = 0;
1063 if (!usergame)
1065 S_StartSound(NULL,sfx_oof);
1066 return;
1069 if (netgame)
1071 M_StartMessage(s_NETEND,NULL,false);
1072 return;
1075 M_StartMessage(s_ENDGAME,M_EndGameResponse,true);
1082 // M_ReadThis
1084 void M_ReadThis(int choice)
1086 choice = 0;
1087 M_SetupNextMenu(&ReadDef1);
1090 void M_ReadThis2(int choice)
1092 choice = 0;
1093 M_SetupNextMenu(&ReadDef2);
1096 void M_FinishReadThis(int choice)
1098 choice = 0;
1099 M_SetupNextMenu(&MainDef);
1106 // M_QuitDOOM
1108 int quitsounds[8] =
1110 sfx_pldeth,
1111 sfx_dmpain,
1112 sfx_popain,
1113 sfx_slop,
1114 sfx_telept,
1115 sfx_posit1,
1116 sfx_posit3,
1117 sfx_sgtatk
1120 int quitsounds2[8] =
1122 sfx_vilact,
1123 sfx_getpow,
1124 sfx_boscub,
1125 sfx_slop,
1126 sfx_skeswg,
1127 sfx_kntdth,
1128 sfx_bspact,
1129 sfx_sgtatk
1134 void M_QuitResponse(int ch)
1136 if (ch != key_menu_enter)
1137 return;
1138 if (!netgame)
1140 if (gamemode == commercial)
1141 S_StartSound(NULL,quitsounds2[(gametic>>2)&7]);
1142 else
1143 S_StartSound(NULL,quitsounds[(gametic>>2)&7]);
1144 I_WaitVBL(105);
1146 I_Quit ();
1152 void M_QuitDOOM(int choice)
1154 (void)choice;
1155 // We pick index 0 which is language sensitive,
1156 // or one at random, between 1 and maximum number.
1157 if (language != english )
1158 snprintf(endstring,sizeof(endstring),"%s\n\n%s",s_DOSY, endmsg[0] );
1159 else
1160 snprintf(endstring,sizeof(endstring),"%s\n\n%s", endmsg[gametic%(NUM_QUITMESSAGES-1)+1], s_DOSY);
1162 M_StartMessage(endstring,M_QuitResponse,true);
1168 void M_ChangeGamma(int choice)
1170 switch(choice)
1172 case 0:
1173 if (usegamma)
1174 usegamma--;
1175 break;
1176 case 1:
1177 if (usegamma < 4)
1178 usegamma++;
1179 break;
1181 V_SetPalette (0);
1184 void M_SizeDisplay(int choice)
1186 switch(choice)
1188 case 0:
1189 if (screenSize > 0)
1191 screenblocks--;
1192 screenSize--;
1194 break;
1195 case 1:
1196 if (screenSize < 8)
1198 screenblocks++;
1199 screenSize++;
1201 break;
1205 R_SetViewSize (screenblocks);
1212 // Menu Functions
1214 void
1215 M_DrawThermo
1216 ( int x,
1217 int y,
1218 int thermWidth,
1219 int thermDot )
1221 int xx;
1222 int i;
1224 xx = x;
1225 V_DrawNamePatch(xx, y, 0, "M_THERML", CR_DEFAULT, VPT_STRETCH);
1226 xx += 8;
1227 for (i=0;i<thermWidth;i++)
1229 V_DrawNamePatch(xx, y, 0, "M_THERMM", CR_DEFAULT, VPT_STRETCH);
1230 xx += 8;
1232 V_DrawNamePatch(xx, y, 0, "M_THERMR", CR_DEFAULT, VPT_STRETCH);
1233 V_DrawNamePatch((x+8)+thermDot*8,y,0,"M_THERMO",CR_DEFAULT,VPT_STRETCH);
1238 void
1239 M_DrawEmptyCell
1240 ( menu_t* menu,
1241 int item )
1243 // CPhipps - patch drawing updated
1244 V_DrawNamePatch(menu->x - 10, menu->y+item*LINEHEIGHT - 1, 0,
1245 "M_CELL1", CR_DEFAULT, VPT_STRETCH);
1248 void
1249 M_DrawSelCell
1250 ( menu_t* menu,
1251 int item )
1253 // CPhipps - patch drawing updated
1254 V_DrawNamePatch(menu->x - 10, menu->y+item*LINEHEIGHT - 1, 0,
1255 "M_CELL2", CR_DEFAULT, VPT_STRETCH);
1259 void
1260 M_StartMessage
1261 ( const char* string,
1262 void* routine,
1263 boolean input )
1265 messageLastMenuActive = menuactive;
1266 messageToPrint = 1;
1267 messageString = string;
1268 messageRoutine = routine;
1269 messageNeedsInput = input;
1270 menuactive = true;
1271 return;
1276 void M_StopMessage(void)
1278 menuactive = messageLastMenuActive;
1279 messageToPrint = 0;
1285 // Find string width from hu_font chars
1287 int M_StringWidth(const char* string)
1289 int i, c, w = 0;
1290 for (i = 0;(size_t)i < strlen(string);i++)
1291 w += (c = toupper(string[i]) - HU_FONTSTART) < 0 || c >= HU_FONTSIZE ?
1292 4 : SHORT(hu_font[c].width);
1293 return w;
1297 // Find string height from hu_font chars
1300 int M_StringHeight(const char* string)
1302 int i, h, height = h = SHORT(hu_font[0].height);
1303 for (i = 0;string[i];i++) // killough 1/31/98
1304 if (string[i] == '\n')
1305 h += height;
1306 return h;
1311 // Write a string using the hu_font
1313 void
1314 M_WriteText
1315 ( int x,
1316 int y,
1317 char* string)
1319 int w;
1320 char* ch;
1321 int c;
1322 int cx;
1323 int cy;
1326 ch = string;
1327 cx = x;
1328 cy = y;
1330 while(1)
1332 c = *ch++;
1333 if (!c)
1334 break;
1335 if (c == '\n')
1337 cx = x;
1338 cy += 12;
1339 continue;
1342 c = toupper(c) - HU_FONTSTART;
1343 if (c < 0 || c>= HU_FONTSIZE)
1345 cx += 4;
1346 continue;
1349 w = SHORT (hu_font[c].width);
1350 if (cx+w > 320)
1351 break;
1352 // proff/nicolas 09/20/98 -- changed for hi-res
1353 // CPhipps - patch drawing updated
1354 V_DrawNumPatch(cx, cy, 0, hu_font[c].lumpnum, CR_DEFAULT, VPT_STRETCH);
1355 cx+=w;
1362 // CONTROL PANEL
1366 // M_Responder
1368 boolean M_Responder (event_t* ev)
1370 int ch;
1371 int i;
1372 // static int joywait = 0;
1373 // static int mousewait = 0;
1374 // static int mousey = 0;
1375 // static int lasty = 0;
1376 // static int mousex = 0;
1377 // static int lastx = 0;
1379 ch = -1;
1381 // Process joystick input
1383 /* if (ev->type == ev_joystick && joywait < I_GetTime())
1385 if (ev->data3 == -1)
1387 ch = KEY_UPARROW;
1388 joywait = I_GetTime() + 5;
1390 else if (ev->data3 == 1)
1392 ch = KEY_DOWNARROW;
1393 joywait = I_GetTime() + 5;
1396 if (ev->data2 == -1)
1398 ch = KEY_LEFTARROW;
1399 joywait = I_GetTime() + 2;
1401 else if (ev->data2 == 1)
1403 ch = KEY_RIGHTARROW;
1404 joywait = I_GetTime() + 2;
1407 if (ev->data1&1)
1409 ch = key_menu_enter;
1410 joywait = I_GetTime() + 5;
1412 if (ev->data1&2)
1414 ch = KEY_BACKSPACE;
1415 joywait = I_GetTime() + 5;
1418 else
1420 // Process mouse input
1421 if (ev->type == ev_mouse && mousewait < I_GetTime())
1423 mousey += ev->data3;
1424 if (mousey < lasty-30)
1426 ch = KEY_DOWNARROW;
1427 mousewait = I_GetTime() + 5;
1428 mousey = lasty -= 30;
1430 else if (mousey > lasty+30)
1432 ch = KEY_UPARROW;
1433 mousewait = I_GetTime() + 5;
1434 mousey = lasty += 30;
1437 mousex += ev->data2;
1438 if (mousex < lastx-30)
1440 ch = KEY_LEFTARROW;
1441 mousewait = I_GetTime() + 5;
1442 mousex = lastx -= 30;
1444 else if (mousex > lastx+30)
1446 ch = KEY_RIGHTARROW;
1447 mousewait = I_GetTime() + 5;
1448 mousex = lastx += 30;
1451 if (ev->data1&1)
1453 ch = key_menu_enter;
1454 mousewait = I_GetTime() + 15;
1457 if (ev->data1&2)
1459 ch = KEY_BACKSPACE;
1460 mousewait = I_GetTime() + 15;
1463 else */if (ev->type == ev_keydown)
1465 ch = ev->data1;
1467 // }
1469 if (ch == -1)
1470 return false;
1473 // Save Game string input
1474 if (saveStringEnter)
1476 switch(ch)
1478 case KEY_BACKSPACE:
1479 if (saveCharIndex > 0)
1481 saveCharIndex--;
1482 savegamestrings[saveSlot][saveCharIndex] = 0;
1484 break;
1486 case KEY_ESCAPE:
1487 saveStringEnter = 0;
1488 strcpy(&savegamestrings[saveSlot][0],saveOldString);
1489 break;
1491 case KEY_ENTER:
1492 saveStringEnter = 0;
1493 if (savegamestrings[saveSlot][0])
1494 M_DoSave(saveSlot);
1495 break;
1497 default:
1498 ch = toupper(ch);
1499 if (ch != 32)
1500 if (ch-HU_FONTSTART < 0 || ch-HU_FONTSTART >= HU_FONTSIZE)
1501 break;
1502 if (ch >= 32 && ch <= 127 &&
1503 saveCharIndex < SAVESTRINGSIZE-1 &&
1504 M_StringWidth(savegamestrings[saveSlot]) <
1505 (SAVESTRINGSIZE-2)*8)
1507 savegamestrings[saveSlot][saveCharIndex++] = ch;
1508 savegamestrings[saveSlot][saveCharIndex] = 0;
1510 break;
1512 return true;
1515 // Take care of any messages that need input
1516 if (messageToPrint)
1518 if (messageNeedsInput == true &&
1519 !(ch == ' ' || ch == 'n' || ch == key_menu_enter || ch == key_menu_escape))
1520 return false;
1522 menuactive = messageLastMenuActive;
1523 messageToPrint = 0;
1524 if (messageRoutine)
1525 messageRoutine(ch);
1527 menuactive = false;
1528 S_StartSound(NULL,sfx_swtchx);
1529 return true;
1532 if (ch == KEY_F1) // devparm &&
1534 G_ScreenShot ();
1535 return true;
1538 // F-Keys
1539 if (!menuactive)
1541 if (ch == key_autorun) // Autorun // V
1543 autorun = !autorun;
1544 return true;
1547 switch(ch)
1550 case KEY_F1: // Help key
1551 M_StartControlPanel ();
1553 if ( gamemode == retail )
1554 currentMenu = &ReadDef2;
1555 else
1556 currentMenu = &ReadDef1;
1558 itemOn = 0;
1559 S_StartSound(NULL,sfx_swtchn);
1560 return true;
1562 case KEY_F6: // Quicksave
1563 S_StartSound(NULL,sfx_swtchn);
1564 M_QuickSave();
1565 return true;
1567 case KEY_F9: // Quickload
1568 S_StartSound(NULL,sfx_swtchn);
1569 M_QuickLoad();
1570 return true;
1576 // Pop-up menu?
1577 if (!menuactive)
1579 if (ch == key_menu_escape)
1581 M_StartControlPanel ();
1582 S_StartSound(NULL,sfx_swtchn);
1583 return true;
1585 return false;
1589 // Keys usable within menu
1590 switch (ch)
1592 case KEY_DOWNARROW:
1595 if (itemOn+1 > currentMenu->numitems-1)
1596 itemOn = 0;
1597 else
1598 itemOn++;
1599 S_StartSound(NULL,sfx_pstop);
1601 while(currentMenu->menuitems[itemOn].status==-1);
1602 return true;
1604 case KEY_UPARROW:
1607 if (!itemOn)
1608 itemOn = currentMenu->numitems-1;
1609 else
1610 itemOn--;
1611 S_StartSound(NULL,sfx_pstop);
1613 while(currentMenu->menuitems[itemOn].status==-1);
1614 return true;
1616 case KEY_LEFTARROW:
1617 if (currentMenu->menuitems[itemOn].routine &&
1618 currentMenu->menuitems[itemOn].status == 2)
1620 S_StartSound(NULL,sfx_stnmov);
1621 currentMenu->menuitems[itemOn].routine(0);
1623 return true;
1625 case KEY_RIGHTARROW:
1626 if (currentMenu->menuitems[itemOn].routine &&
1627 currentMenu->menuitems[itemOn].status == 2)
1629 S_StartSound(NULL,sfx_stnmov);
1630 currentMenu->menuitems[itemOn].routine(1);
1632 return true;
1634 case KEY_ENTER:
1635 if (currentMenu->menuitems[itemOn].routine &&
1636 currentMenu->menuitems[itemOn].status)
1638 currentMenu->lastOn = itemOn;
1639 if (currentMenu->menuitems[itemOn].status == 2)
1641 currentMenu->menuitems[itemOn].routine(1); // right arrow
1642 S_StartSound(NULL,sfx_stnmov);
1644 else
1646 currentMenu->menuitems[itemOn].routine(itemOn);
1647 S_StartSound(NULL,sfx_pistol);
1650 return true;
1652 case KEY_ESCAPE:
1653 currentMenu->lastOn = itemOn;
1654 M_ClearMenus ();
1655 S_StartSound(NULL,sfx_swtchx);
1656 return true;
1658 case KEY_BACKSPACE:
1659 currentMenu->lastOn = itemOn;
1660 if (currentMenu->prevMenu)
1662 currentMenu = currentMenu->prevMenu;
1663 itemOn = currentMenu->lastOn;
1664 S_StartSound(NULL,sfx_swtchn);
1666 return true;
1668 default:
1669 for (i = itemOn+1;i < currentMenu->numitems;i++)
1670 if (currentMenu->menuitems[i].alphaKey == ch)
1672 itemOn = i;
1673 S_StartSound(NULL,sfx_pstop);
1674 return true;
1676 for (i = 0;i <= itemOn;i++)
1677 if (currentMenu->menuitems[i].alphaKey == ch)
1679 itemOn = i;
1680 S_StartSound(NULL,sfx_pstop);
1681 return true;
1683 break;
1687 return false;
1693 // M_StartControlPanel
1695 void M_StartControlPanel (void)
1697 // intro might call this repeatedly
1698 if (menuactive)
1699 return;
1701 menuactive = 1;
1702 currentMenu = &MainDef; // JDC
1703 itemOn = currentMenu->lastOn; // JDC
1708 // M_Drawer
1709 // Called after the view has been rendered,
1710 // but before it has been blitted.
1712 void M_Drawer (void)
1714 static short x;
1715 static short y;
1716 unsigned short i;
1717 short max;
1718 char string[40];
1719 int start;
1721 inhelpscreens = false;
1724 // Horiz. & Vertically center string and print it.
1725 if (messageToPrint)
1727 start = 0;
1728 y = 100 - M_StringHeight(messageString)/2;
1729 while(*(messageString+start))
1731 for (i = 0;i < strlen(messageString+start);i++)
1732 if (*(messageString+start+i) == '\n')
1734 memset(string,0,40);
1735 strncpy(string,messageString+start,i);
1736 start += i+1;
1737 break;
1739 if (i == strlen(messageString+start))
1741 strcpy(string,messageString+start);
1742 start += i;
1745 x = 160 - M_StringWidth(string)/2;
1746 M_WriteText(x,y,string);
1747 y += SHORT(hu_font[0].height);
1749 return;
1752 if (!menuactive)
1753 return;
1755 if (currentMenu->routine)
1756 currentMenu->routine(); // call Draw routine
1758 // DRAW MENU
1759 x = currentMenu->x;
1760 y = currentMenu->y;
1761 max = currentMenu->numitems;
1763 for (i=0;i<max;i++)
1765 if (currentMenu->menuitems[i].name[0])
1766 V_DrawNamePatch(x,y,0,currentMenu->menuitems[i].name,
1767 CR_DEFAULT, VPT_STRETCH);
1768 y += LINEHEIGHT;
1771 // DRAW SKULL
1772 // CPhipps - patch drawing updated
1773 V_DrawNamePatch(x + SKULLXOFF, currentMenu->y - 5 + itemOn*LINEHEIGHT,0,
1774 skullName[whichSkull], CR_DEFAULT, VPT_STRETCH);
1780 // M_ClearMenus
1782 void M_ClearMenus (void)
1784 menuactive = 0;
1785 // if (!netgame && usergame && paused)
1786 // sendpause = true;
1793 // M_SetupNextMenu
1795 void M_SetupNextMenu(menu_t *menudef)
1797 currentMenu = menudef;
1798 itemOn = currentMenu->lastOn;
1803 // M_Ticker
1805 void M_Ticker (void)
1807 if (--skullAnimCounter <= 0)
1809 whichSkull ^= 1;
1810 skullAnimCounter = 8;
1816 // M_Init
1818 void M_Init (void)
1820 currentMenu = &MainDef;
1821 menuactive = 0;
1822 itemOn = currentMenu->lastOn;
1823 whichSkull = 0;
1824 skullAnimCounter = 10;
1825 screenSize = screenblocks - 3;
1826 messageToPrint = 0;
1827 messageString = NULL;
1828 messageLastMenuActive = menuactive;
1829 quickSaveSlot = -1;
1831 // Here we could catch other version dependencies,
1832 // like HELP1/2, and four episodes.
1835 switch ( gamemode )
1837 case commercial:
1838 // This is used because DOOM 2 had only one HELP
1839 // page. I use CREDIT as second page now, but
1840 // kept this hack for educational purposes.
1841 MainMenu[readthis] = MainMenu[quitdoom];
1842 MainDef.numitems--;
1843 MainDef.y += 8;
1844 NewDef.prevMenu = &MainDef;
1845 ReadDef1.routine = M_DrawReadThis1;
1846 ReadDef1.x = 330;
1847 ReadDef1.y = 165;
1848 ReadMenu1[0].routine = M_FinishReadThis;
1849 break;
1850 case shareware:
1851 // Episode 2 and 3 are handled,
1852 // branching to an ad screen.
1853 case registered:
1854 // We need to remove the fourth episode.
1855 EpiDef.numitems--;
1856 break;
1857 case retail:
1858 // We are fine.
1859 default:
1860 break;