# Correct the needed linklibs in curl-config also.
[AROS-Contrib.git] / Games / Doom / m_menu.c
blobd3fa8386d779dc4d6d90e4ed96ceba1f18c0fb90
1 // Emacs style mode select -*- C++ -*-
2 //-----------------------------------------------------------------------------
3 //
4 // $Id$
5 //
6 // Copyright (C) 1993-1996 by id Software, Inc.
7 //
8 // This source is available for distribution and/or modification
9 // only under the terms of the DOOM Source Code License as
10 // published by id Software. All rights reserved.
12 // The source is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License
15 // for more details.
17 // $Log$
18 // Revision 1.1 2000/02/29 18:21:04 stegerg
19 // Doom port based on ADoomPPC. Read README.AROS!
22 // DESCRIPTION:
23 // DOOM selection menu, options, episode etc.
24 // Sliders and icons. Kinda widget stuff.
26 //-----------------------------------------------------------------------------
28 static const char
29 rcsid[] = "$Id$";
31 #include <unistd.h>
32 #include <sys/types.h>
33 #include <sys/stat.h>
34 #include <fcntl.h>
35 #include <stdlib.h>
36 #include <ctype.h>
39 #include "doomdef.h"
40 #include "dstrings.h"
42 #include "d_main.h"
44 #include "i_system.h"
45 #include "i_video.h"
46 #include "z_zone.h"
47 #include "v_video.h"
48 #include "w_wad.h"
50 #include "r_local.h"
53 #include "hu_stuff.h"
55 #include "g_game.h"
57 #include "m_argv.h"
58 #include "m_swap.h"
60 #include "s_sound.h"
62 #include "doomstat.h"
64 // Data.
65 #include "sounds.h"
67 #include "m_menu.h"
71 extern patch_t* hu_font[HU_FONTSIZE];
72 extern boolean message_dontfuckwithme;
74 extern boolean chat_on; // in heads-up code
77 // defaulted values
79 int mouseSensitivity; // has default
81 // Show messages has default, 0 = off, 1 = on
82 int showMessages;
85 // Blocky mode, has default, 0 = high, 1 = normal
86 int detailLevel;
87 int screenblocks; // has default
89 // temp for screenblocks (0-9)
90 int screenSize;
92 // -1 = no quicksave slot picked!
93 int quickSaveSlot;
95 // 1 = message to be printed
96 int messageToPrint;
97 // ...and here is the message string!
98 char* messageString;
100 // message x & y
101 int messx;
102 int messy;
103 int messageLastMenuActive;
105 // timed message = no input from user
106 boolean messageNeedsInput;
108 void (*messageRoutine)(int response);
110 #define SAVESTRINGSIZE 24
112 char gammamsg[5][26] =
114 GAMMALVL0,
115 GAMMALVL1,
116 GAMMALVL2,
117 GAMMALVL3,
118 GAMMALVL4
121 // we are going to be entering a savegame string
122 int saveStringEnter;
123 int saveSlot; // which slot to save in
124 int saveCharIndex; // which char we're editing
125 // old save description before edit
126 char saveOldString[SAVESTRINGSIZE];
128 boolean inhelpscreens;
129 boolean menuactive;
131 #define SKULLXOFF -32
132 #define LINEHEIGHT 16
134 extern boolean sendpause;
135 char savegamestrings[10][SAVESTRINGSIZE];
137 char endstring[160];
141 // MENU TYPEDEFS
143 typedef struct
145 // 0 = no cursor here, 1 = ok, 2 = arrows ok
146 short status;
148 char name[10];
150 // choice = menu item #.
151 // if status = 2,
152 // choice=0:leftarrow,1:rightarrow
153 void (*routine)(int choice);
155 // hotkey in menu
156 char alphaKey;
157 } menuitem_t;
161 typedef struct menu_s
163 short numitems; // # of menu items
164 struct menu_s* prevMenu; // previous menu
165 menuitem_t* menuitems; // menu items
166 void (*routine)(void); // draw routine
167 short x;
168 short y; // x,y of menu
169 short lastOn; // last item user was on in menu
170 } menu_t;
172 short itemOn; // menu item skull is on
173 short skullAnimCounter; // skull animation counter
174 short whichSkull; // which skull to draw
176 // graphic name of skulls
177 // warning: initializer-string for array of chars is too long
178 char skullName[2][/*8*/9] = {"M_SKULL1","M_SKULL2"};
180 // current menudef
181 menu_t* currentMenu;
184 // PROTOTYPES
186 void M_NewGame(int choice);
187 void M_Episode(int choice);
188 void M_ChooseSkill(int choice);
189 void M_LoadGame(int choice);
190 void M_SaveGame(int choice);
191 void M_Options(int choice);
192 void M_EndGame(int choice);
193 void M_ReadThis(int choice);
194 void M_ReadThis2(int choice);
195 void M_QuitDOOM(int choice);
197 void M_ChangeMessages(int choice);
198 void M_ChangeSensitivity(int choice);
199 void M_SfxVol(int choice);
200 void M_MusicVol(int choice);
201 void M_ChangeDetail(int choice);
202 void M_SizeDisplay(int choice);
203 void M_StartGame(int choice);
204 void M_Sound(int choice);
206 void M_FinishReadThis(int choice);
207 void M_LoadSelect(int choice);
208 void M_SaveSelect(int choice);
209 void M_ReadSaveStrings(void);
210 void M_QuickSave(void);
211 void M_QuickLoad(void);
213 void M_DrawMainMenu(void);
214 void M_DrawReadThis1(void);
215 void M_DrawReadThis2(void);
216 void M_DrawNewGame(void);
217 void M_DrawEpisode(void);
218 void M_DrawOptions(void);
219 void M_DrawSound(void);
220 void M_DrawLoad(void);
221 void M_DrawSave(void);
223 void M_DrawSaveLoadBorder(int x,int y);
224 void M_SetupNextMenu(menu_t *menudef);
225 void M_DrawThermo(int x,int y,int thermWidth,int thermDot);
226 void M_DrawEmptyCell(menu_t *menu,int item);
227 void M_DrawSelCell(menu_t *menu,int item);
228 void M_WriteText(int x, int y, char *string);
229 int M_StringWidth(char *string);
230 int M_StringHeight(char *string);
231 void M_StartControlPanel(void);
232 void M_StartMessage(char *string,void (*routine)(int response),boolean input);
233 void M_StopMessage(void);
234 void M_ClearMenus (void);
240 // DOOM MENU
242 enum
244 newgame = 0,
245 options,
246 loadgame,
247 savegame,
248 readthis,
249 quitdoom,
250 main_end
251 } main_e;
253 menuitem_t MainMenu[]=
255 {1,"M_NGAME",M_NewGame,'n'},
256 {1,"M_OPTION",M_Options,'o'},
257 {1,"M_LOADG",M_LoadGame,'l'},
258 {1,"M_SAVEG",M_SaveGame,'s'},
259 // Another hickup with Special edition.
260 {1,"M_RDTHIS",M_ReadThis,'r'},
261 {1,"M_QUITG",M_QuitDOOM,'q'}
264 menu_t MainDef =
266 main_end,
267 NULL,
268 MainMenu,
269 M_DrawMainMenu,
270 97,64,
276 // EPISODE SELECT
278 enum
280 ep1,
281 ep2,
282 ep3,
283 ep4,
284 ep_end
285 } 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
316 } newgame_e;
318 menuitem_t NewGameMenu[]=
320 {1,"M_JKILL", M_ChooseSkill, 'i'},
321 {1,"M_ROUGH", M_ChooseSkill, 'h'},
322 {1,"M_HURT", M_ChooseSkill, 'h'},
323 {1,"M_ULTRA", M_ChooseSkill, 'u'},
324 {1,"M_NMARE", M_ChooseSkill, 'n'}
327 menu_t NewDef =
329 newg_end, // # of menu items
330 &EpiDef, // previous menu
331 NewGameMenu, // menuitem_t ->
332 M_DrawNewGame, // drawing routine ->
333 48,63, // x,y
334 hurtme // lastOn
340 // OPTIONS MENU
342 enum
344 endgame,
345 messages,
346 detail,
347 scrnsize,
348 option_empty1,
349 mousesens,
350 option_empty2,
351 soundvol,
352 opt_end
353 } options_e;
355 menuitem_t OptionsMenu[]=
357 {1,"M_ENDGAM", M_EndGame,'e'},
358 {1,"M_MESSG", M_ChangeMessages,'m'},
359 {1,"M_DETAIL", M_ChangeDetail,'g'},
360 {2,"M_SCRNSZ", M_SizeDisplay,'s'},
361 {-1,"",0},
362 {2,"M_MSENS", M_ChangeSensitivity,'m'},
363 {-1,"",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
384 } read_e;
386 menuitem_t ReadMenu1[] =
388 {1,"",M_ReadThis2,0}
391 menu_t ReadDef1 =
393 read1_end,
394 &MainDef,
395 ReadMenu1,
396 M_DrawReadThis1,
397 280,185,
401 enum
403 rdthsempty2,
404 read2_end
405 } read_e2;
407 menuitem_t ReadMenu2[]=
409 {1,"",M_FinishReadThis,0}
412 menu_t ReadDef2 =
414 read2_end,
415 &ReadDef1,
416 ReadMenu2,
417 M_DrawReadThis2,
418 330,175,
423 // SOUND VOLUME MENU
425 enum
427 sfx_vol,
428 sfx_empty1,
429 music_vol,
430 sfx_empty2,
431 sound_end
432 } sound_e;
434 menuitem_t SoundMenu[]=
436 {2,"M_SFXVOL",M_SfxVol,'s'},
437 {-1,"",0},
438 {2,"M_MUSVOL",M_MusicVol,'m'},
439 {-1,"",0}
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 sprintf(name,"c:\\doomdata\\"SAVEGAMENAME"%d.dsg",i);
525 else
526 sprintf(name,SAVEGAMENAME"%d.dsg",i);
528 handle = open (name, O_RDONLY | 0, 0666);
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;
543 // M_LoadGame & Cie.
545 void M_DrawLoad(void)
547 int i;
549 V_DrawPatchInDirect (72,28,0,W_CacheLumpName("M_LOADG",PU_CACHE));
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_DrawPatchInDirect (x-8,y+7,0,W_CacheLumpName("M_LSLEFT",PU_CACHE));
568 for (i = 0;i < 24;i++)
570 V_DrawPatchInDirect (x,y+7,0,W_CacheLumpName("M_LSCNTR",PU_CACHE));
571 x += 8;
574 V_DrawPatchInDirect (x,y+7,0,W_CacheLumpName("M_LSRGHT",PU_CACHE));
580 // User wants to load this game
582 void M_LoadSelect(int choice)
584 char name[256];
586 if (M_CheckParm("-cdrom"))
587 sprintf(name,"c:\\doomdata\\"SAVEGAMENAME"%d.dsg",choice);
588 else
589 sprintf(name,SAVEGAMENAME"%d.dsg",choice);
590 G_LoadGame (name);
591 M_ClearMenus ();
595 // Selected from DOOM menu
597 void M_LoadGame (int choice)
599 if (netgame)
601 M_StartMessage(LOADNET,NULL,false);
602 return;
605 M_SetupNextMenu(&LoadDef);
606 M_ReadSaveStrings();
611 // M_SaveGame & Cie.
613 void M_DrawSave(void)
615 int i;
617 V_DrawPatchInDirect (72,28,0,W_CacheLumpName("M_SAVEG",PU_CACHE));
618 for (i = 0;i < load_end; i++)
620 M_DrawSaveLoadBorder(LoadDef.x,LoadDef.y+LINEHEIGHT*i);
621 M_WriteText(LoadDef.x,LoadDef.y+LINEHEIGHT*i,savegamestrings[i]);
624 if (saveStringEnter)
626 i = M_StringWidth(savegamestrings[saveSlot]);
627 M_WriteText(LoadDef.x + i,LoadDef.y+LINEHEIGHT*saveSlot,"_");
632 // M_Responder calls this when user is finished
634 void M_DoSave(int slot)
636 G_SaveGame (slot,savegamestrings[slot]);
637 M_ClearMenus ();
639 // PICK QUICKSAVE SLOT YET?
640 if (quickSaveSlot == -2)
641 quickSaveSlot = slot;
645 // User wants to save. Start string input for M_Responder
647 void M_SaveSelect(int choice)
649 // we are going to be intercepting all chars
650 saveStringEnter = 1;
652 saveSlot = choice;
653 strcpy(saveOldString,savegamestrings[choice]);
654 if (!strcmp(savegamestrings[choice],EMPTYSTRING))
655 savegamestrings[choice][0] = 0;
656 saveCharIndex = strlen(savegamestrings[choice]);
660 // Selected from DOOM menu
662 void M_SaveGame (int choice)
664 if (!usergame)
666 M_StartMessage(SAVEDEAD,NULL,false);
667 return;
670 if (gamestate != GS_LEVEL)
671 return;
673 M_SetupNextMenu(&SaveDef);
674 M_ReadSaveStrings();
680 // M_QuickSave
682 char tempstring[80];
684 void M_QuickSaveResponse(int ch)
686 if (ch == 'y')
688 M_DoSave(quickSaveSlot);
689 S_StartSound(NULL,sfx_swtchx);
693 void M_QuickSave(void)
695 if (!usergame)
697 S_StartSound(NULL,sfx_oof);
698 return;
701 if (gamestate != GS_LEVEL)
702 return;
704 if (quickSaveSlot < 0)
706 M_StartControlPanel();
707 M_ReadSaveStrings();
708 M_SetupNextMenu(&SaveDef);
709 quickSaveSlot = -2; // means to pick a slot now
710 return;
712 sprintf(tempstring,QSPROMPT,savegamestrings[quickSaveSlot]);
713 M_StartMessage(tempstring,M_QuickSaveResponse,true);
719 // M_QuickLoad
721 void M_QuickLoadResponse(int ch)
723 if (ch == 'y')
725 M_LoadSelect(quickSaveSlot);
726 S_StartSound(NULL,sfx_swtchx);
731 void M_QuickLoad(void)
733 if (netgame)
735 M_StartMessage(QLOADNET,NULL,false);
736 return;
739 if (quickSaveSlot < 0)
741 M_StartMessage(QSAVESPOT,NULL,false);
742 return;
744 sprintf(tempstring,QLPROMPT,savegamestrings[quickSaveSlot]);
745 M_StartMessage(tempstring,M_QuickLoadResponse,true);
752 // Read This Menus
753 // Had a "quick hack to fix romero bug"
755 void M_DrawReadThis1(void)
757 inhelpscreens = true;
758 switch ( gamemode )
760 case commercial:
761 V_DrawPatchInDirect (0,0,0,W_CacheLumpName("HELP",PU_CACHE));
762 break;
763 case shareware:
764 case registered:
765 case retail:
766 V_DrawPatchInDirect (0,0,0,W_CacheLumpName("HELP1",PU_CACHE));
767 break;
768 default:
769 break;
771 return;
777 // Read This Menus - optional second page.
779 void M_DrawReadThis2(void)
781 inhelpscreens = true;
782 switch ( gamemode )
784 case retail:
785 case commercial:
786 // This hack keeps us from having to change menus.
787 V_DrawPatchInDirect (0,0,0,W_CacheLumpName("CREDIT",PU_CACHE));
788 break;
789 case shareware:
790 case registered:
791 V_DrawPatchInDirect (0,0,0,W_CacheLumpName("HELP2",PU_CACHE));
792 break;
793 default:
794 break;
796 return;
801 // Change Sfx & Music volumes
803 void M_DrawSound(void)
805 V_DrawPatchInDirect (60,38,0,W_CacheLumpName("M_SVOL",PU_CACHE));
807 M_DrawThermo(SoundDef.x,SoundDef.y+LINEHEIGHT*(sfx_vol+1),
808 16,snd_SfxVolume);
810 M_DrawThermo(SoundDef.x,SoundDef.y+LINEHEIGHT*(music_vol+1),
811 16,snd_MusicVolume);
814 void M_Sound(int choice)
816 M_SetupNextMenu(&SoundDef);
819 void M_SfxVol(int choice)
821 switch(choice)
823 case 0:
824 if (snd_SfxVolume)
825 snd_SfxVolume--;
826 break;
827 case 1:
828 if (snd_SfxVolume < 15)
829 snd_SfxVolume++;
830 break;
833 S_SetSfxVolume(snd_SfxVolume /* *8 */);
836 void M_MusicVol(int choice)
838 switch(choice)
840 case 0:
841 if (snd_MusicVolume)
842 snd_MusicVolume--;
843 break;
844 case 1:
845 if (snd_MusicVolume < 15)
846 snd_MusicVolume++;
847 break;
850 S_SetMusicVolume(snd_MusicVolume /* *8 */);
857 // M_DrawMainMenu
859 void M_DrawMainMenu(void)
861 V_DrawPatchInDirect (94,2,0,W_CacheLumpName("M_DOOM",PU_CACHE));
868 // M_NewGame
870 void M_DrawNewGame(void)
872 V_DrawPatchInDirect (96,14,0,W_CacheLumpName("M_NEWG",PU_CACHE));
873 V_DrawPatchInDirect (54,38,0,W_CacheLumpName("M_SKILL",PU_CACHE));
876 void M_NewGame(int choice)
878 if (netgame && !demoplayback)
880 M_StartMessage(NEWGAME,NULL,false);
881 return;
884 if ( gamemode == commercial )
885 M_SetupNextMenu(&NewDef);
886 else
887 M_SetupNextMenu(&EpiDef);
892 // M_Episode
894 int epi;
896 void M_DrawEpisode(void)
898 V_DrawPatchInDirect (54,38,0,W_CacheLumpName("M_EPISOD",PU_CACHE));
901 void M_VerifyNightmare(int ch)
903 if (ch != 'y')
904 return;
906 G_DeferedInitNew(nightmare,epi+1,1);
907 M_ClearMenus ();
910 void M_ChooseSkill(int choice)
912 if (choice == nightmare)
914 M_StartMessage(NIGHTMARE,M_VerifyNightmare,true);
915 return;
918 G_DeferedInitNew(choice,epi+1,1);
919 M_ClearMenus ();
922 void M_Episode(int choice)
924 if ( (gamemode == shareware)
925 && choice)
927 M_StartMessage(SWSTRING,NULL,false);
928 M_SetupNextMenu(&ReadDef1);
929 return;
932 // Yet another hack...
933 if ( (gamemode == registered)
934 && (choice > 2))
936 fprintf( stderr,
937 "M_Episode: 4th episode requires UltimateDOOM\n");
938 choice = 0;
941 epi = choice;
942 M_SetupNextMenu(&NewDef);
948 // M_Options
950 char detailNames[2][9] = {"M_GDHIGH","M_GDLOW"};
951 char msgNames[2][9] = {"M_MSGOFF","M_MSGON"};
954 void M_DrawOptions(void)
956 V_DrawPatchInDirect (108,15,0,W_CacheLumpName("M_OPTTTL",PU_CACHE));
958 V_DrawPatchInDirect (OptionsDef.x + 175,OptionsDef.y+LINEHEIGHT*detail,0,
959 W_CacheLumpName(detailNames[detailLevel],PU_CACHE));
961 V_DrawPatchInDirect (OptionsDef.x + 120,OptionsDef.y+LINEHEIGHT*messages,0,
962 W_CacheLumpName(msgNames[showMessages],PU_CACHE));
964 M_DrawThermo(OptionsDef.x,OptionsDef.y+LINEHEIGHT*(mousesens+1),
965 10,mouseSensitivity);
967 M_DrawThermo(OptionsDef.x,OptionsDef.y+LINEHEIGHT*(scrnsize+1),
968 9,screenSize);
971 void M_Options(int choice)
973 M_SetupNextMenu(&OptionsDef);
979 // Toggle messages on/off
981 void M_ChangeMessages(int choice)
983 // warning: unused parameter `int choice'
984 choice = 0;
985 showMessages = 1 - showMessages;
987 if (!showMessages)
988 players[consoleplayer].message = MSGOFF;
989 else
990 players[consoleplayer].message = MSGON ;
992 message_dontfuckwithme = true;
997 // M_EndGame
999 void M_EndGameResponse(int ch)
1001 if (ch != 'y')
1002 return;
1004 currentMenu->lastOn = itemOn;
1005 M_ClearMenus ();
1006 D_StartTitle ();
1009 void M_EndGame(int choice)
1011 choice = 0;
1012 if (!usergame)
1014 S_StartSound(NULL,sfx_oof);
1015 return;
1018 if (netgame)
1020 M_StartMessage(NETEND,NULL,false);
1021 return;
1024 M_StartMessage(ENDGAME,M_EndGameResponse,true);
1031 // M_ReadThis
1033 void M_ReadThis(int choice)
1035 choice = 0;
1036 M_SetupNextMenu(&ReadDef1);
1039 void M_ReadThis2(int choice)
1041 choice = 0;
1042 M_SetupNextMenu(&ReadDef2);
1045 void M_FinishReadThis(int choice)
1047 choice = 0;
1048 M_SetupNextMenu(&MainDef);
1055 // M_QuitDOOM
1057 int quitsounds[8] =
1059 sfx_pldeth,
1060 sfx_dmpain,
1061 sfx_popain,
1062 sfx_slop,
1063 sfx_telept,
1064 sfx_posit1,
1065 sfx_posit3,
1066 sfx_sgtatk
1069 int quitsounds2[8] =
1071 sfx_vilact,
1072 sfx_getpow,
1073 sfx_boscub,
1074 sfx_slop,
1075 sfx_skeswg,
1076 sfx_kntdth,
1077 sfx_bspact,
1078 sfx_sgtatk
1083 void M_QuitResponse(int ch)
1085 if (ch != 'y')
1086 return;
1087 if (!netgame)
1089 if (gamemode == commercial)
1090 S_StartSound(NULL,quitsounds2[(gametic>>2)&7]);
1091 else
1092 S_StartSound(NULL,quitsounds[(gametic>>2)&7]);
1093 I_WaitVBL(105);
1095 I_Quit ();
1101 void M_QuitDOOM(int choice)
1103 // We pick index 0 which is language sensitive,
1104 // or one at random, between 1 and maximum number.
1105 if (language != english )
1106 sprintf(endstring,"%s\n\n"DOSY, endmsg[0] );
1107 else
1108 sprintf(endstring,"%s\n\n"DOSY, endmsg[ (gametic%(NUM_QUITMESSAGES-2))+1 ]);
1110 M_StartMessage(endstring,M_QuitResponse,true);
1116 void M_ChangeSensitivity(int choice)
1118 switch(choice)
1120 case 0:
1121 if (mouseSensitivity)
1122 mouseSensitivity--;
1123 break;
1124 case 1:
1125 if (mouseSensitivity < 9)
1126 mouseSensitivity++;
1127 break;
1134 void M_ChangeDetail(int choice)
1136 choice = 0;
1137 detailLevel = 1 - detailLevel;
1139 // FIXME - does not work. Remove anyway?
1140 // fprintf( stderr, "M_ChangeDetail: low detail mode n.a.\n");
1142 // return;
1144 R_SetViewSize (screenblocks, detailLevel);
1146 if (!detailLevel)
1147 players[consoleplayer].message = DETAILHI;
1148 else
1149 players[consoleplayer].message = DETAILLO;
1155 void M_SizeDisplay(int choice)
1157 switch(choice)
1159 case 0:
1160 if (screenSize > 0)
1162 screenblocks--;
1163 screenSize--;
1165 break;
1166 case 1:
1167 if (screenSize < 8)
1169 screenblocks++;
1170 screenSize++;
1172 break;
1176 R_SetViewSize (screenblocks, detailLevel);
1183 // Menu Functions
1185 void
1186 M_DrawThermo
1187 ( int x,
1188 int y,
1189 int thermWidth,
1190 int thermDot )
1192 int xx;
1193 int i;
1195 xx = x;
1196 V_DrawPatchInDirect (xx,y,0,W_CacheLumpName("M_THERML",PU_CACHE));
1197 xx += 8;
1198 for (i=0;i<thermWidth;i++)
1200 V_DrawPatchInDirect (xx,y,0,W_CacheLumpName("M_THERMM",PU_CACHE));
1201 xx += 8;
1203 V_DrawPatchInDirect (xx,y,0,W_CacheLumpName("M_THERMR",PU_CACHE));
1205 V_DrawPatchInDirect ((x+8) + thermDot*8,y,
1206 0,W_CacheLumpName("M_THERMO",PU_CACHE));
1211 void
1212 M_DrawEmptyCell
1213 ( menu_t* menu,
1214 int item )
1216 V_DrawPatchInDirect (menu->x - 10, menu->y+item*LINEHEIGHT - 1, 0,
1217 W_CacheLumpName("M_CELL1",PU_CACHE));
1220 void
1221 M_DrawSelCell
1222 ( menu_t* menu,
1223 int item )
1225 V_DrawPatchInDirect (menu->x - 10, menu->y+item*LINEHEIGHT - 1, 0,
1226 W_CacheLumpName("M_CELL2",PU_CACHE));
1230 void
1231 M_StartMessage
1232 ( char* string,
1233 void (*routine)(int response),
1234 /* void* routine, */
1235 boolean input )
1237 messageLastMenuActive = menuactive;
1238 messageToPrint = 1;
1239 messageString = string;
1240 messageRoutine = routine;
1241 messageNeedsInput = input;
1242 menuactive = true;
1243 return;
1248 void M_StopMessage(void)
1250 menuactive = messageLastMenuActive;
1251 messageToPrint = 0;
1257 // Find string width from hu_font chars
1259 int M_StringWidth(char* string)
1261 int i;
1262 int w = 0;
1263 int c;
1265 for (i = 0;i < strlen(string);i++)
1267 c = toupper(string[i]) - HU_FONTSTART;
1268 if (c < 0 || c >= HU_FONTSIZE)
1269 w += 4;
1270 else
1271 w += SWAPSHORT(hu_font[c]->width);
1274 return w;
1280 // Find string height from hu_font chars
1282 int M_StringHeight(char* string)
1284 int i;
1285 int h;
1286 int height = SWAPSHORT(hu_font[0]->height);
1288 h = height;
1289 for (i = 0;i < strlen(string);i++)
1290 if (string[i] == '\n')
1291 h += height;
1293 return h;
1298 // Write a string using the hu_font
1300 void
1301 M_WriteText
1302 ( int x,
1303 int y,
1304 char* string)
1306 int w;
1307 char* ch;
1308 int c;
1309 int cx;
1310 int cy;
1313 ch = string;
1314 cx = x;
1315 cy = y;
1317 while(1)
1319 c = *ch++;
1320 if (!c)
1321 break;
1322 if (c == '\n')
1324 cx = x;
1325 cy += 12;
1326 continue;
1329 c = toupper(c) - HU_FONTSTART;
1330 if (c < 0 || c>= HU_FONTSIZE)
1332 cx += 4;
1333 continue;
1336 w = SWAPSHORT(hu_font[c]->width);
1337 if (cx+w > SCREENWIDTH)
1338 break;
1339 V_DrawPatchInDirect(cx, cy, 0, hu_font[c]);
1340 cx+=w;
1347 // CONTROL PANEL
1351 // M_Responder
1353 boolean M_Responder (event_t* ev)
1355 int ch;
1356 int i;
1357 static int joywait = 0;
1358 static int mousewait = 0;
1359 static int mousey = 0;
1360 static int lasty = 0;
1361 static int mousex = 0;
1362 static int lastx = 0;
1364 ch = -1;
1366 if (ev->type == ev_joystick && joywait < I_GetTime())
1368 if (ev->data3 == -1)
1370 ch = KEY_UPARROW;
1371 joywait = I_GetTime() + 5;
1373 else if (ev->data3 == 1)
1375 ch = KEY_DOWNARROW;
1376 joywait = I_GetTime() + 5;
1379 if (ev->data2 == -1)
1381 ch = KEY_LEFTARROW;
1382 joywait = I_GetTime() + 2;
1384 else if (ev->data2 == 1)
1386 ch = KEY_RIGHTARROW;
1387 joywait = I_GetTime() + 2;
1390 if (ev->data1&1)
1392 ch = KEY_ENTER;
1393 joywait = I_GetTime() + 5;
1395 if (ev->data1&2)
1397 ch = KEY_BACKSPACE;
1398 joywait = I_GetTime() + 5;
1401 else
1403 if (ev->type == ev_mouse && mousewait < I_GetTime())
1405 mousey += ev->data3;
1406 if (mousey < lasty-30)
1408 ch = KEY_DOWNARROW;
1409 mousewait = I_GetTime() + 5;
1410 mousey = lasty -= 30;
1412 else if (mousey > lasty+30)
1414 ch = KEY_UPARROW;
1415 mousewait = I_GetTime() + 5;
1416 mousey = lasty += 30;
1419 mousex += ev->data2;
1420 if (mousex < lastx-30)
1422 ch = KEY_LEFTARROW;
1423 mousewait = I_GetTime() + 5;
1424 mousex = lastx -= 30;
1426 else if (mousex > lastx+30)
1428 ch = KEY_RIGHTARROW;
1429 mousewait = I_GetTime() + 5;
1430 mousex = lastx += 30;
1433 if (ev->data1&1)
1435 ch = KEY_ENTER;
1436 mousewait = I_GetTime() + 15;
1439 if (ev->data1&2)
1441 ch = KEY_BACKSPACE;
1442 mousewait = I_GetTime() + 15;
1445 else
1446 if (ev->type == ev_keydown)
1448 ch = ev->data1;
1452 if (ch == -1)
1453 return false;
1456 // Save Game string input
1457 if (saveStringEnter)
1459 switch(ch)
1461 case KEY_BACKSPACE:
1462 if (saveCharIndex > 0)
1464 saveCharIndex--;
1465 savegamestrings[saveSlot][saveCharIndex] = 0;
1467 break;
1469 case KEY_ESCAPE:
1470 saveStringEnter = 0;
1471 strcpy(&savegamestrings[saveSlot][0],saveOldString);
1472 break;
1474 case KEY_ENTER:
1475 saveStringEnter = 0;
1476 if (savegamestrings[saveSlot][0])
1477 M_DoSave(saveSlot);
1478 break;
1480 default:
1481 ch = toupper(ch);
1482 if (ch != 32)
1483 if (ch-HU_FONTSTART < 0 || ch-HU_FONTSTART >= HU_FONTSIZE)
1484 break;
1485 if (ch >= 32 && ch <= 127 &&
1486 saveCharIndex < SAVESTRINGSIZE-1 &&
1487 M_StringWidth(savegamestrings[saveSlot]) <
1488 (SAVESTRINGSIZE-2)*8)
1490 savegamestrings[saveSlot][saveCharIndex++] = ch;
1491 savegamestrings[saveSlot][saveCharIndex] = 0;
1493 break;
1495 return true;
1498 // Take care of any messages that need input
1499 if (messageToPrint)
1501 if (messageNeedsInput == true &&
1502 !(ch == ' ' || ch == 'n' || ch == 'y' || ch == KEY_ESCAPE))
1503 return false;
1505 menuactive = messageLastMenuActive;
1506 messageToPrint = 0;
1507 if (messageRoutine)
1508 messageRoutine(ch);
1510 menuactive = false;
1511 S_StartSound(NULL,sfx_swtchx);
1512 return true;
1515 if (devparm && ch == KEY_F1)
1517 G_ScreenShot ();
1518 return true;
1522 // F-Keys
1523 if (!menuactive)
1524 switch(ch)
1526 case KEY_MINUS: // Screen size down
1527 if (automapactive || chat_on)
1528 return false;
1529 M_SizeDisplay(0);
1530 S_StartSound(NULL,sfx_stnmov);
1531 return true;
1533 case KEY_EQUALS: // Screen size up
1534 if (automapactive || chat_on)
1535 return false;
1536 M_SizeDisplay(1);
1537 S_StartSound(NULL,sfx_stnmov);
1538 return true;
1540 case KEY_F1: // Help key
1541 M_StartControlPanel ();
1543 if ( gamemode == retail )
1544 currentMenu = &ReadDef2;
1545 else
1546 currentMenu = &ReadDef1;
1548 itemOn = 0;
1549 S_StartSound(NULL,sfx_swtchn);
1550 return true;
1552 case KEY_F2: // Save
1553 M_StartControlPanel();
1554 S_StartSound(NULL,sfx_swtchn);
1555 M_SaveGame(0);
1556 return true;
1558 case KEY_F3: // Load
1559 M_StartControlPanel();
1560 S_StartSound(NULL,sfx_swtchn);
1561 M_LoadGame(0);
1562 return true;
1564 case KEY_F4: // Sound Volume
1565 M_StartControlPanel ();
1566 currentMenu = &SoundDef;
1567 itemOn = sfx_vol;
1568 S_StartSound(NULL,sfx_swtchn);
1569 return true;
1571 case KEY_F5: // Detail toggle
1572 M_ChangeDetail(0);
1573 S_StartSound(NULL,sfx_swtchn);
1574 return true;
1576 case KEY_F6: // Quicksave
1577 S_StartSound(NULL,sfx_swtchn);
1578 M_QuickSave();
1579 return true;
1581 case KEY_F7: // End game
1582 S_StartSound(NULL,sfx_swtchn);
1583 M_EndGame(0);
1584 return true;
1586 case KEY_F8: // Toggle messages
1587 M_ChangeMessages(0);
1588 S_StartSound(NULL,sfx_swtchn);
1589 return true;
1591 case KEY_F9: // Quickload
1592 S_StartSound(NULL,sfx_swtchn);
1593 M_QuickLoad();
1594 return true;
1596 case KEY_F10: // Quit DOOM
1597 S_StartSound(NULL,sfx_swtchn);
1598 M_QuitDOOM(0);
1599 return true;
1601 case KEY_F11: // gamma toggle
1602 usegamma++;
1603 if (usegamma > 4)
1604 usegamma = 0;
1605 players[consoleplayer].message = gammamsg[usegamma];
1606 I_RecalcPalettes ();
1607 I_SetPalette (W_CacheLumpName ("PLAYPAL",PU_CACHE), 0);
1608 return true;
1613 // Pop-up menu?
1614 if (!menuactive)
1616 if (ch == KEY_ESCAPE)
1618 M_StartControlPanel ();
1619 S_StartSound(NULL,sfx_swtchn);
1620 return true;
1622 return false;
1626 // Keys usable within menu
1627 switch (ch)
1629 case KEY_DOWNARROW:
1632 if (itemOn+1 > currentMenu->numitems-1)
1633 itemOn = 0;
1634 else itemOn++;
1635 S_StartSound(NULL,sfx_pstop);
1636 } while(currentMenu->menuitems[itemOn].status==-1);
1637 return true;
1639 case KEY_UPARROW:
1642 if (!itemOn)
1643 itemOn = currentMenu->numitems-1;
1644 else itemOn--;
1645 S_StartSound(NULL,sfx_pstop);
1646 } while(currentMenu->menuitems[itemOn].status==-1);
1647 return true;
1649 case KEY_LEFTARROW:
1650 if (currentMenu->menuitems[itemOn].routine &&
1651 currentMenu->menuitems[itemOn].status == 2)
1653 S_StartSound(NULL,sfx_stnmov);
1654 currentMenu->menuitems[itemOn].routine(0);
1656 return true;
1658 case KEY_RIGHTARROW:
1659 if (currentMenu->menuitems[itemOn].routine &&
1660 currentMenu->menuitems[itemOn].status == 2)
1662 S_StartSound(NULL,sfx_stnmov);
1663 currentMenu->menuitems[itemOn].routine(1);
1665 return true;
1667 case KEY_ENTER:
1668 if (currentMenu->menuitems[itemOn].routine &&
1669 currentMenu->menuitems[itemOn].status)
1671 currentMenu->lastOn = itemOn;
1672 if (currentMenu->menuitems[itemOn].status == 2)
1674 currentMenu->menuitems[itemOn].routine(1); // right arrow
1675 S_StartSound(NULL,sfx_stnmov);
1677 else
1679 currentMenu->menuitems[itemOn].routine(itemOn);
1680 S_StartSound(NULL,sfx_pistol);
1683 return true;
1685 case KEY_ESCAPE:
1686 currentMenu->lastOn = itemOn;
1687 M_ClearMenus ();
1688 S_StartSound(NULL,sfx_swtchx);
1689 return true;
1691 case KEY_BACKSPACE:
1692 currentMenu->lastOn = itemOn;
1693 if (currentMenu->prevMenu)
1695 currentMenu = currentMenu->prevMenu;
1696 itemOn = currentMenu->lastOn;
1697 S_StartSound(NULL,sfx_swtchn);
1699 return true;
1701 default:
1702 for (i = itemOn+1;i < currentMenu->numitems;i++)
1703 if (currentMenu->menuitems[i].alphaKey == ch)
1705 itemOn = i;
1706 S_StartSound(NULL,sfx_pstop);
1707 return true;
1709 for (i = 0;i <= itemOn;i++)
1710 if (currentMenu->menuitems[i].alphaKey == ch)
1712 itemOn = i;
1713 S_StartSound(NULL,sfx_pstop);
1714 return true;
1716 break;
1720 return false;
1726 // M_StartControlPanel
1728 void M_StartControlPanel (void)
1730 // intro might call this repeatedly
1731 if (menuactive)
1732 return;
1734 menuactive = 1;
1735 currentMenu = &MainDef; // JDC
1736 itemOn = currentMenu->lastOn; // JDC
1741 // M_Drawer
1742 // Called after the view has been rendered,
1743 // but before it has been blitted.
1745 void M_Drawer (void)
1747 static short x;
1748 static short y;
1749 short i;
1750 short max;
1751 char string[40];
1752 int start;
1754 inhelpscreens = false;
1757 // Horiz. & Vertically center string and print it.
1758 if (messageToPrint)
1760 start = 0;
1761 y = 100 - M_StringHeight(messageString)/2;
1762 while(*(messageString+start))
1764 for (i = 0;i < strlen(messageString+start);i++)
1765 if (*(messageString+start+i) == '\n')
1767 memset(string,0,40);
1768 strncpy(string,messageString+start,i);
1769 start += i+1;
1770 break;
1773 if (i == strlen(messageString+start))
1775 strcpy(string,messageString+start);
1776 start += i;
1779 x = 160 - M_StringWidth(string)/2;
1780 M_WriteText(x,y,string);
1781 y += SWAPSHORT(hu_font[0]->height);
1783 return;
1786 if (!menuactive)
1787 return;
1789 if (currentMenu->routine)
1790 currentMenu->routine(); // call Draw routine
1792 // DRAW MENU
1793 x = currentMenu->x;
1794 y = currentMenu->y;
1795 max = currentMenu->numitems;
1797 for (i=0;i<max;i++)
1799 if (currentMenu->menuitems[i].name[0])
1800 V_DrawPatchInDirect (x,y,0,
1801 W_CacheLumpName(currentMenu->menuitems[i].name ,PU_CACHE));
1802 y += LINEHEIGHT;
1806 // DRAW SKULL
1807 V_DrawPatchInDirect(x + SKULLXOFF,currentMenu->y - 5 + itemOn*LINEHEIGHT, 0,
1808 W_CacheLumpName(skullName[whichSkull],PU_CACHE));
1814 // M_ClearMenus
1816 void M_ClearMenus (void)
1818 menuactive = 0;
1819 // if (!netgame && usergame && paused)
1820 // sendpause = true;
1827 // M_SetupNextMenu
1829 void M_SetupNextMenu(menu_t *menudef)
1831 currentMenu = menudef;
1832 itemOn = currentMenu->lastOn;
1837 // M_Ticker
1839 void M_Ticker (void)
1841 if (--skullAnimCounter <= 0)
1843 whichSkull ^= 1;
1844 skullAnimCounter = 8;
1850 // M_Init
1852 void M_Init (void)
1854 currentMenu = &MainDef;
1855 menuactive = 0;
1856 itemOn = currentMenu->lastOn;
1857 whichSkull = 0;
1858 skullAnimCounter = 10;
1859 screenSize = screenblocks - 3;
1860 messageToPrint = 0;
1861 messageString = NULL;
1862 messageLastMenuActive = menuactive;
1863 quickSaveSlot = -1;
1865 // Here we could catch other version dependencies,
1866 // like HELP1/2, and four episodes.
1869 switch ( gamemode )
1871 case commercial:
1872 // This is used because DOOM 2 had only one HELP
1873 // page. I use CREDIT as second page now, but
1874 // kept this hack for educational purposes.
1875 MainMenu[readthis] = MainMenu[quitdoom];
1876 MainDef.numitems--;
1877 MainDef.y += 8;
1878 NewDef.prevMenu = &MainDef;
1879 ReadDef1.routine = M_DrawReadThis1;
1880 ReadDef1.x = 330;
1881 ReadDef1.y = 165;
1882 ReadMenu1[0].routine = M_FinishReadThis;
1883 break;
1884 case shareware:
1885 // Episode 2 and 3 are handled,
1886 // branching to an ad screen.
1887 case registered:
1888 // We need to remove the fourth episode.
1889 EpiDef.numitems--;
1890 break;
1891 case retail:
1892 // We are fine.
1893 default:
1894 break;