m_menu.c
上传用户:xuyinpeng
上传日期:2021-05-12
资源大小:455k
文件大小:37k
源码类别:

射击游戏

开发平台:

Visual C++

  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.
  11. //
  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.
  16. //
  17. // $Log:$
  18. //
  19. // DESCRIPTION:
  20. // DOOM selection menu, options, episode etc.
  21. // Sliders and icons. Kinda widget stuff.
  22. //
  23. //-----------------------------------------------------------------------------
  24. static const char
  25. rcsid[] = "$Id: m_menu.c,v 1.7 1997/02/03 22:45:10 b1 Exp $";
  26. //#include <unistd.h>
  27. #include <sys/types.h>
  28. #include <sys/stat.h>
  29. #include <fcntl.h>
  30. #include <stdlib.h>
  31. #include <ctype.h>
  32. #include <io.h>
  33. #include "doomdef.h"
  34. #include "dstrings.h"
  35. #include "d_main.h"
  36. #include "i_system.h"
  37. #include "i_video.h"
  38. #include "z_zone.h"
  39. #include "v_video.h"
  40. #include "w_wad.h"
  41. #include "r_local.h"
  42. #include "hu_stuff.h"
  43. #include "g_game.h"
  44. #include "m_argv.h"
  45. #include "m_swap.h"
  46. #include "s_sound.h"
  47. #include "doomstat.h"
  48. // Data.
  49. #include "sounds.h"
  50. #include "m_menu.h"
  51. #include "d_console.h"
  52. void WriteDebug(char *);
  53. extern patch_t* hu_font[HU_FONTSIZE];
  54. extern boolean message_dontfuckwithme;
  55. extern boolean chat_on; // in heads-up code
  56. extern int          always_run;
  57. //
  58. // defaulted values
  59. //
  60. int mouseSensitivity;       // has default
  61. // Show messages has default, 0 = off, 1 = on
  62. int showMessages;
  63. // Blocky mode, has default, 0 = high, 1 = normal
  64. int detailLevel = 0;
  65. int screenblocks = 9; // has default
  66. // temp for screenblocks (0-9)
  67. int screenSize;
  68. // -1 = no quicksave slot picked!
  69. int quickSaveSlot;          
  70.  // 1 = message to be printed
  71. int messageToPrint;
  72. // ...and here is the message string!
  73. char* messageString;
  74. // message x & y
  75. int messx;
  76. int messy;
  77. int messageLastMenuActive;
  78. // timed message = no input from user
  79. boolean messageNeedsInput;     
  80. void    (*messageRoutine)(int response);
  81. #define SAVESTRINGSIZE  24
  82. char gammamsg[5][26] =
  83. {
  84.     GAMMALVL0,
  85.     GAMMALVL1,
  86.     GAMMALVL2,
  87.     GAMMALVL3,
  88.     GAMMALVL4
  89. };
  90. // we are going to be entering a savegame string
  91. int saveStringEnter;              
  92. int              saveSlot; // which slot to save in
  93. int saveCharIndex; // which char we're editing
  94. // old save description before edit
  95. char saveOldString[SAVESTRINGSIZE];  
  96. boolean inhelpscreens;
  97. boolean menuactive;
  98. #define SKULLXOFF -32
  99. #define LINEHEIGHT 16
  100. extern boolean sendpause;
  101. char savegamestrings[10][SAVESTRINGSIZE];
  102. char endstring[160];
  103. //
  104. // MENU TYPEDEFS
  105. //
  106. typedef struct
  107. {
  108.     // 0 = no cursor here, 1 = ok, 2 = arrows ok
  109.     short status;
  110.     
  111.     char name[10];
  112.     
  113.     // choice = menu item #.
  114.     // if status = 2,
  115.     //   choice=0:leftarrow,1:rightarrow
  116.     void (*routine)(int choice);
  117.     
  118.     // hotkey in menu
  119.     char alphaKey;
  120. } menuitem_t;
  121. typedef struct menu_s
  122. {
  123.     short numitems; // # of menu items
  124.     struct menu_s* prevMenu; // previous menu
  125.     menuitem_t* menuitems; // menu items
  126.     void (*routine)(); // draw routine
  127.     short x;
  128.     short y; // x,y of menu
  129.     short lastOn; // last item user was on in menu
  130. } menu_t;
  131. short itemOn; // menu item skull is on
  132. short skullAnimCounter; // skull animation counter
  133. short whichSkull; // which skull to draw
  134. // graphic name of skulls
  135. // warning: initializer-string for array of chars is too long
  136. char    skullName[2][/*8*/9] = {"M_SKULL1","M_SKULL2"};
  137. // current menudef
  138. menu_t* currentMenu;                          
  139. //
  140. // PROTOTYPES
  141. //
  142. void M_NewGame(int choice);
  143. void M_Episode(int choice);
  144. void M_ChooseSkill(int choice);
  145. void M_LoadGame(int choice);
  146. void M_SaveGame(int choice);
  147. void M_Options(int choice);
  148. void M_EndGame(int choice);
  149. void M_ReadThis(int choice);
  150. void M_ReadThis2(int choice);
  151. void M_QuitDOOM(int choice);
  152. void M_ChangeMessages(int choice);
  153. void M_ChangeSensitivity(int choice);
  154. void M_SfxVol(int choice);
  155. void M_MusicVol(int choice);
  156. void M_ChangeDetail(int choice);
  157. void M_SizeDisplay(int choice);
  158. void M_StartGame(int choice);
  159. void M_Sound(int choice);
  160. void M_FinishReadThis(int choice);
  161. void M_LoadSelect(int choice);
  162. void M_SaveSelect(int choice);
  163. void M_ReadSaveStrings(void);
  164. void M_QuickSave(void);
  165. void M_QuickLoad(void);
  166. void M_DrawMainMenu(void);
  167. void M_DrawReadThis1(void);
  168. void M_DrawReadThis2(void);
  169. void M_DrawNewGame(void);
  170. void M_DrawEpisode(void);
  171. void M_DrawOptions(void);
  172. void M_DrawSound(void);
  173. void M_DrawLoad(void);
  174. void M_DrawSave(void);
  175. void M_DrawSaveLoadBorder(int x,int y);
  176. void M_SetupNextMenu(menu_t *menudef);
  177. void M_DrawThermo(int x,int y,int thermWidth,int thermDot);
  178. void M_DrawEmptyCell(menu_t *menu,int item);
  179. void M_DrawSelCell(menu_t *menu,int item);
  180. void M_WriteText(int x, int y, char *string);
  181. int  M_StringWidth(char *string);
  182. int  M_StringHeight(char *string);
  183. void M_StartControlPanel(void);
  184. void M_StartMessage(char *string,void *routine,boolean input);
  185. void M_StopMessage(void);
  186. void M_ClearMenus (void);
  187. extern unsigned char scan2char[256];
  188. //
  189. // DOOM MENU
  190. //
  191. enum
  192. {
  193.     newgame = 0,
  194.     options,
  195.     loadgame,
  196.     savegame,
  197.     readthis,
  198.     quitdoom,
  199.     main_end
  200. } main_e;
  201. menuitem_t MainMenu[]=
  202. {
  203.     {1,"M_NGAME",M_NewGame,'n'},
  204.     {1,"M_OPTION",M_Options,'o'},
  205.     {1,"M_LOADG",M_LoadGame,'l'},
  206.     {1,"M_SAVEG",M_SaveGame,'s'},
  207.     // Another hickup with Special edition.
  208.     {1,"M_RDTHIS",M_ReadThis,'r'},
  209.     {1,"M_QUITG",M_QuitDOOM,'q'}
  210. };
  211. menu_t  MainDef =
  212. {
  213.     main_end,
  214.     NULL,
  215.     MainMenu,
  216.     M_DrawMainMenu,
  217.     97,64,
  218.     0
  219. };
  220. //
  221. // EPISODE SELECT
  222. //
  223. enum
  224. {
  225.     ep1,
  226.     ep2,
  227.     ep3,
  228.     ep4,
  229.     ep_end
  230. } episodes_e;
  231. menuitem_t EpisodeMenu[]=
  232. {
  233.     {1,"M_EPI1", M_Episode,'k'},
  234.     {1,"M_EPI2", M_Episode,'t'},
  235.     {1,"M_EPI3", M_Episode,'i'},
  236.     {1,"M_EPI4", M_Episode,'t'}
  237. };
  238. menu_t  EpiDef =
  239. {
  240.     ep_end, // # of menu items
  241.     &MainDef, // previous menu
  242.     EpisodeMenu, // menuitem_t ->
  243.     M_DrawEpisode, // drawing routine ->
  244.     48,63,              // x,y
  245.     ep1 // lastOn
  246. };
  247. //
  248. // NEW GAME
  249. //
  250. enum
  251. {
  252.     killthings,
  253.     toorough,
  254.     hurtme,
  255.     violence,
  256.     nightmare,
  257.     newg_end
  258. } newgame_e;
  259. menuitem_t NewGameMenu[]=
  260. {
  261.     {1,"M_JKILL", M_ChooseSkill, 'i'},
  262.     {1,"M_ROUGH", M_ChooseSkill, 'h'},
  263.     {1,"M_HURT", M_ChooseSkill, 'h'},
  264.     {1,"M_ULTRA", M_ChooseSkill, 'u'},
  265.     {1,"M_NMARE", M_ChooseSkill, 'n'}
  266. };
  267. menu_t  NewDef =
  268. {
  269.     newg_end, // # of menu items
  270.     &EpiDef, // previous menu
  271.     NewGameMenu, // menuitem_t ->
  272.     M_DrawNewGame, // drawing routine ->
  273.     48,63,              // x,y
  274.     hurtme // lastOn
  275. };
  276. //
  277. // OPTIONS MENU
  278. //
  279. enum
  280. {
  281.     endgame,
  282.     messages,
  283.     detail,
  284.     scrnsize,
  285.     option_empty1,
  286.     mousesens,
  287.     option_empty2,
  288.     soundvol,
  289.     opt_end
  290. } options_e;
  291. menuitem_t OptionsMenu[]=
  292. {
  293.     {1,"M_ENDGAM", M_EndGame,'e'},
  294.     {1,"M_MESSG", M_ChangeMessages,'m'},
  295.     {1,"M_DETAIL", M_ChangeDetail,'g'},
  296.     {2,"M_SCRNSZ", M_SizeDisplay,'s'},
  297.     {-1,"",0},
  298.     {2,"M_MSENS", M_ChangeSensitivity,'m'},
  299.     {-1,"",0},
  300.     {1,"M_SVOL", M_Sound,'s'}
  301. };
  302. menu_t  OptionsDef =
  303. {
  304.     opt_end,
  305.     &MainDef,
  306.     OptionsMenu,
  307.     M_DrawOptions,
  308.     60,37,
  309.     0
  310. };
  311. //
  312. // Read This! MENU 1 & 2
  313. //
  314. enum
  315. {
  316.     rdthsempty1,
  317.     read1_end
  318. } read_e;
  319. menuitem_t ReadMenu1[] =
  320. {
  321.     {1,"",M_ReadThis2,0}
  322. };
  323. menu_t  ReadDef1 =
  324. {
  325.     read1_end,
  326.     &MainDef,
  327.     ReadMenu1,
  328.     M_DrawReadThis1,
  329.     280,185,
  330.     0
  331. };
  332. enum
  333. {
  334.     rdthsempty2,
  335.     read2_end
  336. } read_e2;
  337. menuitem_t ReadMenu2[]=
  338. {
  339.     {1,"",M_FinishReadThis,0}
  340. };
  341. menu_t  ReadDef2 =
  342. {
  343.     read2_end,
  344.     &ReadDef1,
  345.     ReadMenu2,
  346.     M_DrawReadThis2,
  347.     330,175,
  348.     0
  349. };
  350. //
  351. // SOUND VOLUME MENU
  352. //
  353. enum
  354. {
  355.     sfx_vol,
  356.     sfx_empty1,
  357.     music_vol,
  358.     sfx_empty2,
  359.     sound_end
  360. } sound_e;
  361. menuitem_t SoundMenu[]=
  362. {
  363.     {2,"M_SFXVOL",M_SfxVol,'s'},
  364.     {-1,"",0},
  365.     {2,"M_MUSVOL",M_MusicVol,'m'},
  366.     {-1,"",0}
  367. };
  368. menu_t  SoundDef =
  369. {
  370.     sound_end,
  371.     &OptionsDef,
  372.     SoundMenu,
  373.     M_DrawSound,
  374.     80,64,
  375.     0
  376. };
  377. //
  378. // LOAD GAME MENU
  379. //
  380. enum
  381. {
  382.     load1,
  383.     load2,
  384.     load3,
  385.     load4,
  386.     load5,
  387.     load6,
  388.     load_end
  389. } load_e;
  390. menuitem_t LoadGameMenu[]=
  391. {
  392.     {1,"", M_LoadSelect,'1'},
  393.     {1,"", M_LoadSelect,'2'},
  394.     {1,"", M_LoadSelect,'3'},
  395.     {1,"", M_LoadSelect,'4'},
  396.     {1,"", M_LoadSelect,'5'},
  397.     {1,"", M_LoadSelect,'6'}
  398. };
  399. menu_t  LoadDef =
  400. {
  401.     load_end,
  402.     &MainDef,
  403.     LoadGameMenu,
  404.     M_DrawLoad,
  405.     80,54,
  406.     0
  407. };
  408. //
  409. // SAVE GAME MENU
  410. //
  411. menuitem_t SaveMenu[]=
  412. {
  413.     {1,"", M_SaveSelect,'1'},
  414.     {1,"", M_SaveSelect,'2'},
  415.     {1,"", M_SaveSelect,'3'},
  416.     {1,"", M_SaveSelect,'4'},
  417.     {1,"", M_SaveSelect,'5'},
  418.     {1,"", M_SaveSelect,'6'}
  419. };
  420. menu_t  SaveDef =
  421. {
  422.     load_end,
  423.     &MainDef,
  424.     SaveMenu,
  425.     M_DrawSave,
  426.     80,54,
  427.     0
  428. };
  429. //
  430. // M_ReadSaveStrings
  431. //  read the strings from the savegame files
  432. //
  433. void M_ReadSaveStrings(void)
  434. {
  435.     int             handle;
  436.     int             count;
  437.     int             i;
  438.     char    name[256];
  439.     for (i = 0;i < load_end;i++)
  440.     {
  441. if (M_CheckParm("-cdrom"))
  442.     sprintf(name,"c:\doomdata\"SAVEGAMENAME"%d.dsg",i);
  443. else
  444.     sprintf(name,SAVEGAMENAME"%d.dsg",i);
  445. handle = open (name, O_RDONLY | 0, 0666);
  446. if (handle == -1)
  447. {
  448.     strcpy(&savegamestrings[i][0],EMPTYSTRING);
  449.     LoadGameMenu[i].status = 0;
  450.     continue;
  451. }
  452. count = read (handle, &savegamestrings[i], SAVESTRINGSIZE);
  453. close (handle);
  454. LoadGameMenu[i].status = 1;
  455.     }
  456. }
  457. //
  458. // M_LoadGame & Cie.
  459. //
  460. void M_DrawLoad(void)
  461. {
  462.     int             i;
  463.     V_DrawPatchDirect(((SCREENWIDTH-320)/2)+72,((SCREENHEIGHT-200)/2)+28,0,W_CacheLumpName("M_LOADG",PU_CACHE));
  464.     for (i = 0;i < load_end; i++)
  465.     {
  466. M_DrawSaveLoadBorder(((SCREENWIDTH-320)/2)+LoadDef.x,((SCREENHEIGHT-200)/2)+LoadDef.y+LINEHEIGHT*i);
  467. M_WriteText(((SCREENWIDTH-320)/2)+LoadDef.x,((SCREENHEIGHT-200)/2)+LoadDef.y+LINEHEIGHT*i,savegamestrings[i]);
  468.     }
  469. }
  470. //
  471. // Draw border for the savegame description
  472. //
  473. void M_DrawSaveLoadBorder(int x,int y)
  474. {
  475.     int             i;
  476.     V_DrawPatchDirect (x-8,y+7,0,W_CacheLumpName("M_LSLEFT",PU_CACHE));
  477.     for (i = 0;i < 24;i++)
  478.     {
  479. V_DrawPatchDirect (x,y+7,0,W_CacheLumpName("M_LSCNTR",PU_CACHE));
  480. x += 8;
  481.     }
  482.     V_DrawPatchDirect (x,y+7,0,W_CacheLumpName("M_LSRGHT",PU_CACHE));
  483. }
  484. //
  485. // User wants to load this game
  486. //
  487. void M_LoadSelect(int choice)
  488. {
  489.     char    name[256];
  490.     if (M_CheckParm("-cdrom"))
  491. sprintf(name,"c:\doomdata\"SAVEGAMENAME"%d.dsg",choice);
  492.     else
  493. sprintf(name,SAVEGAMENAME"%d.dsg",choice);
  494.     G_LoadGame (name);
  495.     M_ClearMenus ();
  496. }
  497. //
  498. // Selected from DOOM menu
  499. //
  500. void M_LoadGame (int choice)
  501. {
  502.     if (netgame)
  503.     {
  504. M_StartMessage(LOADNET,NULL,false);
  505. return;
  506.     }
  507.     M_SetupNextMenu(&LoadDef);
  508.     M_ReadSaveStrings();
  509. }
  510. //
  511. //  M_SaveGame & Cie.
  512. //
  513. void M_DrawSave(void)
  514. {
  515.     int             i;
  516.     V_DrawPatchDirect (((SCREENWIDTH-320)/2)+72,((SCREENHEIGHT-200)/2)+28,0,W_CacheLumpName("M_SAVEG",PU_CACHE));
  517.     for (i = 0;i < load_end; i++)
  518.     {
  519. M_DrawSaveLoadBorder(((SCREENWIDTH-320)/2)+LoadDef.x,((SCREENHEIGHT-200)/2)+LoadDef.y+LINEHEIGHT*i);
  520. M_WriteText(((SCREENWIDTH-320)/2)+LoadDef.x,((SCREENHEIGHT-200)/2)+LoadDef.y+LINEHEIGHT*i,savegamestrings[i]);
  521.     }
  522.     if (saveStringEnter)
  523.     {
  524. i = M_StringWidth(savegamestrings[saveSlot]);
  525. M_WriteText(((SCREENWIDTH-320)/2)+LoadDef.x + i,((SCREENHEIGHT-200)/2)+LoadDef.y+LINEHEIGHT*saveSlot,"_");
  526.     }
  527. }
  528. //
  529. // M_Responder calls this when user is finished
  530. //
  531. void M_DoSave(int slot)
  532. {
  533.     G_SaveGame (slot,savegamestrings[slot]);
  534.     M_ClearMenus ();
  535.     // PICK QUICKSAVE SLOT YET?
  536.     if (quickSaveSlot == -2)
  537. quickSaveSlot = slot;
  538. }
  539. //
  540. // User wants to save. Start string input for M_Responder
  541. //
  542. void M_SaveSelect(int choice)
  543. {
  544.     // we are going to be intercepting all chars
  545.     saveStringEnter = 1;
  546.     
  547.     saveSlot = choice;
  548.     strcpy(saveOldString,savegamestrings[choice]);
  549.     if (!strcmp(savegamestrings[choice],EMPTYSTRING))
  550. savegamestrings[choice][0] = 0;
  551.     saveCharIndex = strlen(savegamestrings[choice]);
  552. }
  553. //
  554. // Selected from DOOM menu
  555. //
  556. void M_SaveGame (int choice)
  557. {
  558.     if (!usergame)
  559.     {
  560. M_StartMessage(SAVEDEAD,NULL,false);
  561. return;
  562.     }
  563.     if (gamestate != GS_LEVEL)
  564. return;
  565.     M_SetupNextMenu(&SaveDef);
  566.     M_ReadSaveStrings();
  567. }
  568. //
  569. //      M_QuickSave
  570. //
  571. char    tempstring[80];
  572. void M_QuickSaveResponse(int ch)
  573. {
  574.     if (ch == KEY_Y)
  575.     {
  576. M_DoSave(quickSaveSlot);
  577. S_StartSound(NULL,sfx_swtchx);
  578.     }
  579. }
  580. void M_QuickSave(void)
  581. {
  582.     if (!usergame)
  583.     {
  584. S_StartSound(NULL,sfx_oof);
  585. return;
  586.     }
  587.     if (gamestate != GS_LEVEL)
  588. return;
  589.     if (quickSaveSlot < 0)
  590.     {
  591. M_StartControlPanel();
  592. M_ReadSaveStrings();
  593. M_SetupNextMenu(&SaveDef);
  594. quickSaveSlot = -2; // means to pick a slot now
  595. return;
  596.     }
  597.     sprintf(tempstring,QSPROMPT,savegamestrings[quickSaveSlot]);
  598.     M_StartMessage(tempstring,M_QuickSaveResponse,true);
  599. }
  600. //
  601. // M_QuickLoad
  602. //
  603. void M_QuickLoadResponse(int ch)
  604. {
  605.     if (ch == KEY_Y)
  606.     {
  607. M_LoadSelect(quickSaveSlot);
  608. S_StartSound(NULL,sfx_swtchx);
  609.     }
  610. }
  611. void M_QuickLoad(void)
  612. {
  613.     if (netgame)
  614.     {
  615. M_StartMessage(QLOADNET,NULL,false);
  616. return;
  617.     }
  618.     if (quickSaveSlot < 0)
  619.     {
  620. M_StartMessage(QSAVESPOT,NULL,false);
  621. return;
  622.     }
  623.     sprintf(tempstring,QLPROMPT,savegamestrings[quickSaveSlot]);
  624.     M_StartMessage(tempstring,M_QuickLoadResponse,true);
  625. }
  626. //
  627. // Read This Menus
  628. // Had a "quick hack to fix romero bug"
  629. //
  630. void M_DrawReadThis1(void)
  631. {
  632.     inhelpscreens = true;
  633.     switch ( gamemode )
  634.     {
  635.       case commercial:
  636. V_DrawPatchDirect (((SCREENWIDTH-320)/2)+0,((SCREENHEIGHT-200)/2)+0,0,W_CacheLumpName("HELP",PU_CACHE));
  637. break;
  638.       case shareware:
  639.       case registered:
  640.       case retail:
  641. V_DrawPatchDirect (((SCREENWIDTH-320)/2)+0,((SCREENHEIGHT-200)/2)+0,0,W_CacheLumpName("HELP1",PU_CACHE));
  642. break;
  643.       default:
  644. break;
  645.     }
  646.     return;
  647. }
  648. //
  649. // Read This Menus - optional second page.
  650. //
  651. void M_DrawReadThis2(void)
  652. {
  653.     inhelpscreens = true;
  654.     switch ( gamemode )
  655.     {
  656.       case retail:
  657.       case commercial:
  658. // This hack keeps us from having to change menus.
  659. V_DrawPatchDirect (((SCREENWIDTH-320)/2)+0,((SCREENHEIGHT-200)/2)+0,0,W_CacheLumpName("CREDIT",PU_CACHE));
  660. break;
  661.       case shareware:
  662.       case registered:
  663. V_DrawPatchDirect (((SCREENWIDTH-320)/2)+0,((SCREENHEIGHT-200)/2)+0,0,W_CacheLumpName("HELP2",PU_CACHE));
  664. break;
  665.       default:
  666. break;
  667.     }
  668.     return;
  669. }
  670. //
  671. // Change Sfx & Music volumes
  672. //
  673. void M_DrawSound(void)
  674. {
  675.     V_DrawPatchDirect (((SCREENWIDTH-320)/2)+60,((SCREENHEIGHT-200)/2)+38,0,W_CacheLumpName("M_SVOL",PU_CACHE));
  676.     M_DrawThermo(SoundDef.x,SoundDef.y+LINEHEIGHT*(sfx_vol+1),
  677.  16,snd_SfxVolume);
  678.     M_DrawThermo(SoundDef.x,SoundDef.y+LINEHEIGHT*(music_vol+1),
  679.  16,snd_MusicVolume);
  680. }
  681. void M_Sound(int choice)
  682. {
  683.     M_SetupNextMenu(&SoundDef);
  684. }
  685. void M_SfxVol(int choice)
  686. {
  687.     switch(choice)
  688.     {
  689.       case 0:
  690. if (snd_SfxVolume)
  691.     snd_SfxVolume--;
  692. break;
  693.       case 1:
  694. if (snd_SfxVolume < 15)
  695.     snd_SfxVolume++;
  696. break;
  697.     }
  698.     S_SetSfxVolume(snd_SfxVolume /* *8 */);
  699. }
  700. void M_MusicVol(int choice)
  701. {
  702.     switch(choice)
  703.     {
  704.       case 0:
  705. if (snd_MusicVolume)
  706.     snd_MusicVolume--;
  707. break;
  708.       case 1:
  709. if (snd_MusicVolume < 15)
  710.     snd_MusicVolume++;
  711. break;
  712.     }
  713.     S_SetMusicVolume(snd_MusicVolume /* *8 */);
  714. }
  715. //
  716. // M_DrawMainMenu
  717. //
  718. void M_DrawMainMenu(void)
  719. {
  720.     V_DrawPatchDirect (((SCREENWIDTH-320)/2)+94,((SCREENHEIGHT-200)/2)+2,0,W_CacheLumpName("M_DOOM",PU_CACHE));
  721. }
  722. //
  723. // M_NewGame
  724. //
  725. void M_DrawNewGame(void)
  726. {
  727.     V_DrawPatchDirect (((SCREENWIDTH-320)/2)+96,((SCREENHEIGHT-200)/2)+14,0,W_CacheLumpName("M_NEWG",PU_CACHE));
  728.     V_DrawPatchDirect (((SCREENWIDTH-320)/2)+54,((SCREENHEIGHT-200)/2)+38,0,W_CacheLumpName("M_SKILL",PU_CACHE));
  729. }
  730. void M_NewGame(int choice)
  731. {
  732.     if (netgame && !demoplayback)
  733.     {
  734. M_StartMessage(NEWGAME,NULL,false);
  735. return;
  736.     }
  737.     if ( gamemode == commercial )
  738. M_SetupNextMenu(&NewDef);
  739.     else
  740. M_SetupNextMenu(&EpiDef);
  741. }
  742. //
  743. //      M_Episode
  744. //
  745. int     epi;
  746. void M_DrawEpisode(void)
  747. {
  748.     V_DrawPatchDirect (((SCREENWIDTH-320)/2)+54,((SCREENHEIGHT-200)/2)+38,0,W_CacheLumpName("M_EPISOD",PU_CACHE));
  749. }
  750. void M_VerifyNightmare(int ch)
  751. {
  752.     if (ch != KEY_Y)
  753. return;
  754.     G_DeferedInitNew(nightmare,epi+1,1);
  755.     M_ClearMenus ();
  756. }
  757. void M_ChooseSkill(int choice)
  758. {
  759.     if (choice == nightmare)
  760.     {
  761. M_StartMessage(NIGHTMARE,M_VerifyNightmare,true);
  762. return;
  763.     }
  764.     G_DeferedInitNew(choice,epi+1,1);
  765.     M_ClearMenus ();
  766. }
  767. void M_Episode(int choice)
  768. {
  769.     if ( (gamemode == shareware)
  770.  && choice)
  771.     {
  772. M_StartMessage(SWSTRING,NULL,false);
  773. M_SetupNextMenu(&ReadDef1);
  774. return;
  775.     }
  776.     // Yet another hack...
  777.     if ( (gamemode == registered)
  778.  && (choice > 2))
  779.     {
  780.       fprintf( stderr,
  781.        "M_Episode: 4th episode requires UltimateDOOMn");
  782.       choice = 0;
  783.     }
  784.  
  785.     epi = choice;
  786.     M_SetupNextMenu(&NewDef);
  787. }
  788. //
  789. // M_Options
  790. //
  791. char    detailNames[2][9] = {"M_GDHIGH","M_GDLOW"};
  792. char msgNames[2][9] = {"M_MSGOFF","M_MSGON"};
  793. void M_DrawOptions(void)
  794. {
  795.     V_DrawPatchDirect (((SCREENWIDTH-320)/2)+108,((SCREENHEIGHT-200)/2)+15,0,W_CacheLumpName("M_OPTTTL",PU_CACHE));
  796.     V_DrawPatchDirect (((SCREENWIDTH-320)/2)+OptionsDef.x + 175,((SCREENHEIGHT-200)/2)+OptionsDef.y+LINEHEIGHT*detail,0,
  797.        W_CacheLumpName(detailNames[detailLevel],PU_CACHE));
  798.     V_DrawPatchDirect (((SCREENWIDTH-320)/2)+OptionsDef.x + 120,((SCREENHEIGHT-200)/2)+OptionsDef.y+LINEHEIGHT*messages,0,
  799.        W_CacheLumpName(msgNames[showMessages],PU_CACHE));
  800.     M_DrawThermo(OptionsDef.x,OptionsDef.y+LINEHEIGHT*(mousesens+1),
  801.  10,mouseSensitivity);
  802.     M_DrawThermo(OptionsDef.x,OptionsDef.y+LINEHEIGHT*(scrnsize+1),
  803.  9,screenSize);
  804. }
  805. void M_Options(int choice)
  806. {
  807.     M_SetupNextMenu(&OptionsDef);
  808. }
  809. //
  810. //      Toggle messages on/off
  811. //
  812. void M_ChangeMessages(int choice)
  813. {
  814.     // warning: unused parameter `int choice'
  815.     choice = 0;
  816.     showMessages = 1 - showMessages;
  817.     if (!showMessages)
  818. players[consoleplayer].message = MSGOFF;
  819.     else
  820. players[consoleplayer].message = MSGON ;
  821.     message_dontfuckwithme = true;
  822. }
  823. //
  824. // M_EndGame
  825. //
  826. void M_EndGameResponse(int ch)
  827. {
  828.     if (ch != KEY_Y)
  829. return;
  830.     currentMenu->lastOn = itemOn;
  831.     M_ClearMenus ();
  832.     D_StartTitle ();
  833. }
  834. void M_EndGame(int choice)
  835. {
  836.     choice = 0;
  837.     if (!usergame)
  838.     {
  839. S_StartSound(NULL,sfx_oof);
  840. return;
  841.     }
  842.     if (netgame)
  843.     {
  844. M_StartMessage(NETEND,NULL,false);
  845. return;
  846.     }
  847.     M_StartMessage(ENDGAME,M_EndGameResponse,true);
  848. }
  849. //
  850. // M_ReadThis
  851. //
  852. void M_ReadThis(int choice)
  853. {
  854.     choice = 0;
  855.     M_SetupNextMenu(&ReadDef1);
  856. }
  857. void M_ReadThis2(int choice)
  858. {
  859.     choice = 0;
  860.     M_SetupNextMenu(&ReadDef2);
  861. }
  862. void M_FinishReadThis(int choice)
  863. {
  864.     choice = 0;
  865.     M_SetupNextMenu(&MainDef);
  866. }
  867. //
  868. // M_QuitDOOM
  869. //
  870. int     quitsounds[8] =
  871. {
  872.     sfx_pldeth,
  873.     sfx_dmpain,
  874.     sfx_popain,
  875.     sfx_slop,
  876.     sfx_telept,
  877.     sfx_posit1,
  878.     sfx_posit3,
  879.     sfx_sgtatk
  880. };
  881. int     quitsounds2[8] =
  882. {
  883.     sfx_vilact,
  884.     sfx_getpow,
  885.     sfx_boscub,
  886.     sfx_slop,
  887.     sfx_skeswg,
  888.     sfx_kntdth,
  889.     sfx_bspact,
  890.     sfx_sgtatk
  891. };
  892. char MsgText[256];
  893. void M_QuitResponse(int ch)
  894. {
  895. //WriteDebug("M_QuitResponse...n");
  896.     if (ch != KEY_Y)
  897.        {
  898. //        sprintf(MsgText, "ch = %c - 0x%02Xn", ch, ch );
  899. //        WriteDebug(MsgText);
  900.         return;
  901.        }
  902.     if (!netgame)
  903.     {
  904. if (gamemode == commercial)
  905.     S_StartSound(NULL,quitsounds2[(gametic>>2)&7]);
  906. else
  907.     S_StartSound(NULL,quitsounds[(gametic>>2)&7]);
  908. I_WaitVBL(105);
  909.     }
  910.     I_Quit ();
  911. }
  912. void M_QuitDOOM(int choice)
  913. {
  914. //WriteDebug("M_QuitDoom...n");
  915.   // We pick index 0 which is language sensitive,
  916.   //  or one at random, between 1 and maximum number.
  917.   if (language != english )
  918.     sprintf(endstring,"%snn"DOSY, endmsg[0] );
  919.   else
  920.     sprintf(endstring,"%snn"DOSY, endmsg[ (gametic%(NUM_QUITMESSAGES-10))+1 ]);
  921.   
  922.   M_StartMessage(endstring,M_QuitResponse,true);
  923. }
  924. void M_ChangeSensitivity(int choice)
  925. {
  926.     switch(choice)
  927.     {
  928.       case 0:
  929. if (mouseSensitivity)
  930.     mouseSensitivity--;
  931. break;
  932.       case 1:
  933. if (mouseSensitivity < 9)
  934.     mouseSensitivity++;
  935. break;
  936.     }
  937. }
  938. void M_ChangeDetail(int choice)
  939. {
  940.     choice = 0;
  941.     //detailLevel = 1 - detailLevel;
  942.     // FIXME - does not work. Remove anyway?
  943.     sprintf( MsgText, "M_ChangeDetail: low detail mode n.a.n");
  944.     WriteDebug(MsgText);
  945.     
  946. /*
  947.     R_SetViewSize (screenblocks, detailLevel);
  948.     if (!detailLevel)
  949. players[consoleplayer].message = DETAILHI;
  950.     else
  951. players[consoleplayer].message = DETAILLO;
  952. */
  953. }
  954. void M_SizeDisplay(int choice)
  955. {
  956.     switch(choice)
  957.     {
  958.       case 0:
  959. if (screenSize > 0)
  960. {
  961.     screenblocks--;
  962.     screenSize--;
  963. }
  964. break;
  965.       case 1:
  966. if (screenSize < 8)
  967. {
  968.     screenblocks++;
  969.     screenSize++;
  970. }
  971. break;
  972.     }
  973.     R_SetViewSize (screenblocks, detailLevel);
  974. }
  975. //
  976. //      Menu Functions
  977. //
  978. void
  979. M_DrawThermo
  980. ( int x,
  981.   int y,
  982.   int thermWidth,
  983.   int thermDot )
  984. {
  985.     int xx;
  986.     int i;
  987.     xx = x;
  988.     V_DrawPatchDirect (((SCREENWIDTH-320)/2)+xx,((SCREENHEIGHT-200)/2)+y,0,W_CacheLumpName("M_THERML",PU_CACHE));
  989.     xx += 8;
  990.     for (i=0;i<thermWidth;i++)
  991.     {
  992. V_DrawPatchDirect (((SCREENWIDTH-320)/2)+xx,((SCREENHEIGHT-200)/2)+y,0,W_CacheLumpName("M_THERMM",PU_CACHE));
  993. xx += 8;
  994.     }
  995.     V_DrawPatchDirect (((SCREENWIDTH-320)/2)+xx,((SCREENHEIGHT-200)/2)+y,0,W_CacheLumpName("M_THERMR",PU_CACHE));
  996.     V_DrawPatchDirect (((SCREENWIDTH-320)/2)+(x+8) + thermDot*8,((SCREENHEIGHT-200)/2)+y,
  997.        0,W_CacheLumpName("M_THERMO",PU_CACHE));
  998. }
  999. void
  1000. M_DrawEmptyCell
  1001. ( menu_t* menu,
  1002.   int item )
  1003. {
  1004.     V_DrawPatchDirect (((SCREENWIDTH-320)/2)+menu->x - 10,((SCREENHEIGHT-200)/2)+menu->y+item*LINEHEIGHT - 1, 0,
  1005.        W_CacheLumpName("M_CELL1",PU_CACHE));
  1006. }
  1007. void
  1008. M_DrawSelCell
  1009. ( menu_t* menu,
  1010.   int item )
  1011. {
  1012.     V_DrawPatchDirect (((SCREENWIDTH-320)/2)+menu->x - 10,((SCREENHEIGHT-200)/2)+menu->y+item*LINEHEIGHT - 1, 0,
  1013.        W_CacheLumpName("M_CELL2",PU_CACHE));
  1014. }
  1015. void
  1016. M_StartMessage
  1017. ( char* string,
  1018.   void* routine,
  1019.   boolean input )
  1020. {
  1021. //WriteDebug("M_StartMessage...n");
  1022. //WriteDebug(string);
  1023. //WriteDebug("n");
  1024.     messageLastMenuActive = menuactive;
  1025.     messageToPrint = 1;
  1026.     messageString = string;
  1027.     messageRoutine = routine;
  1028.     messageNeedsInput = input;
  1029.     menuactive = true;
  1030.     return;
  1031. }
  1032. void M_StopMessage(void)
  1033. {
  1034.     menuactive = messageLastMenuActive;
  1035.     messageToPrint = 0;
  1036. }
  1037. //
  1038. // Find string width from hu_font chars
  1039. //
  1040. int M_StringWidth(char* string)
  1041. {
  1042.     int             i;
  1043.     int             w = 0;
  1044.     int             c;
  1045.     for (i = 0;(unsigned)i < strlen(string);i++)
  1046.     {
  1047. c = toupper(string[i]) - HU_FONTSTART;
  1048. if (c < 0 || c >= HU_FONTSIZE)
  1049.     w += 4;
  1050. else
  1051.     w += SHORT (hu_font[c]->width);
  1052.     }
  1053.     return w;
  1054. }
  1055. //
  1056. //      Find string height from hu_font chars
  1057. //
  1058. int M_StringHeight(char* string)
  1059. {
  1060.     int             i;
  1061.     int             h;
  1062.     int             height = SHORT(hu_font[0]->height);
  1063.     h = height;
  1064.     for (i = 0;(unsigned)i < strlen(string);i++)
  1065. if (string[i] == 'n')
  1066.     h += height;
  1067.     return h;
  1068. }
  1069. //
  1070. //      Write a string using the hu_font
  1071. //
  1072. void M_WriteText( int x, int y, char *string)
  1073.    {
  1074.     int w;
  1075.     char* ch;
  1076.     int c;
  1077.     int cx;
  1078.     int cy;
  1079.     ch = string;
  1080.     cx = x;
  1081.     cy = y;
  1082.     while(1)
  1083.        {
  1084.         c = *ch++;
  1085.         if (!c)
  1086.             break;
  1087.         if (c == 'n')
  1088.            {
  1089.             cx = x;
  1090.             cy += 12;
  1091.             continue;
  1092.            }
  1093.         c = toupper(c) - HU_FONTSTART;
  1094.         if (c < 0 || c>= HU_FONTSIZE)
  1095.            {
  1096.             cx += 4;
  1097.             continue;
  1098.            }
  1099.         w = SHORT (hu_font[c]->width);
  1100.         if (cx+w > SCREENWIDTH)
  1101.             break;
  1102.         V_DrawPatchDirect(cx, cy, 0, hu_font[c]);
  1103.         cx+=w;
  1104.        }
  1105.    }
  1106. //
  1107. // CONTROL PANEL
  1108. //
  1109. //
  1110. // M_Responder
  1111. //
  1112. boolean M_Responder (event_t* ev)
  1113. {
  1114.     int             ch;
  1115.     int             i;
  1116.     static  int     joywait = 0;
  1117.     static  int     mousewait = 0;
  1118.     static  int     mousey = 0;
  1119.     static  int     lasty = 0;
  1120.     static  int     mousex = 0;
  1121.     static  int     lastx = 0;
  1122.     ch = -1;
  1123.     if (ev->type == ev_joystick && joywait < I_GetTime())
  1124.     {
  1125. if (ev->data3 == -1)
  1126. {
  1127.     ch = KEY_UPARROW;
  1128.     joywait = I_GetTime() + 5;
  1129. }
  1130. else if (ev->data3 == 1)
  1131. {
  1132.     ch = KEY_DOWNARROW;
  1133.     joywait = I_GetTime() + 5;
  1134. }
  1135. if (ev->data2 == -1)
  1136. {
  1137.     ch = KEY_LEFTARROW;
  1138.     joywait = I_GetTime() + 2;
  1139. }
  1140. else if (ev->data2 == 1)
  1141. {
  1142.     ch = KEY_RIGHTARROW;
  1143.     joywait = I_GetTime() + 2;
  1144. }
  1145. if (ev->data1&1)
  1146. {
  1147.     ch = KEY_ENTER;
  1148.     joywait = I_GetTime() + 5;
  1149. }
  1150. if (ev->data1&2)
  1151. {
  1152.     ch = KEY_BACKSPACE;
  1153.     joywait = I_GetTime() + 5;
  1154. }
  1155.     }
  1156.     else
  1157.     {
  1158. if (ev->type == ev_mouse && mousewait < I_GetTime())
  1159. {
  1160.     mousey += ev->data3;
  1161.     if (mousey < lasty-30)
  1162.     {
  1163. ch = KEY_DOWNARROW;
  1164. mousewait = I_GetTime() + 5;
  1165. mousey = lasty -= 30;
  1166.     }
  1167.     else if (mousey > lasty+30)
  1168.     {
  1169. ch = KEY_UPARROW;
  1170. mousewait = I_GetTime() + 5;
  1171. mousey = lasty += 30;
  1172.     }
  1173.     mousex += ev->data2;
  1174.     if (mousex < lastx-30)
  1175.     {
  1176. ch = KEY_LEFTARROW;
  1177. mousewait = I_GetTime() + 5;
  1178. mousex = lastx -= 30;
  1179.     }
  1180.     else if (mousex > lastx+30)
  1181.     {
  1182. ch = KEY_RIGHTARROW;
  1183. mousewait = I_GetTime() + 5;
  1184. mousex = lastx += 30;
  1185.     }
  1186.     if (ev->data1&1)
  1187.     {
  1188. ch = KEY_ENTER;
  1189. mousewait = I_GetTime() + 15;
  1190.     }
  1191.     if (ev->data1&2)
  1192.     {
  1193. ch = KEY_BACKSPACE;
  1194. mousewait = I_GetTime() + 15;
  1195.     }
  1196. }
  1197. else
  1198.     if (ev->type == ev_keydown)
  1199.     {
  1200. ch = ev->data1;
  1201.         //WriteDebug("Assigning data1 to ch...n");
  1202.     }
  1203.     }
  1204.     
  1205.     if (ch == -1)
  1206. return false;
  1207.     
  1208.     // Save Game string input
  1209.     if (saveStringEnter)
  1210.     {
  1211. switch(ch)
  1212. {
  1213.   case KEY_BACKSPACE:
  1214.     if (saveCharIndex > 0)
  1215.     {
  1216. saveCharIndex--;
  1217. savegamestrings[saveSlot][saveCharIndex] = 0;
  1218.     }
  1219.     break;
  1220.   case KEY_ESCAPE:
  1221.     saveStringEnter = 0;
  1222.     strcpy(&savegamestrings[saveSlot][0],saveOldString);
  1223.     break;
  1224.   case KEY_ENTER:
  1225.     saveStringEnter = 0;
  1226.     if (savegamestrings[saveSlot][0])
  1227. M_DoSave(saveSlot);
  1228.     break;
  1229.   default:
  1230.     ch = toupper(scan2char[ch]);
  1231.     if (ch != 32)
  1232. if (ch-HU_FONTSTART < 0 || ch-HU_FONTSTART >= HU_FONTSIZE)
  1233.     break;
  1234.     if (ch >= 32 && ch <= 127 &&
  1235. saveCharIndex < SAVESTRINGSIZE-1 &&
  1236. M_StringWidth(savegamestrings[saveSlot]) <
  1237. (SAVESTRINGSIZE-2)*8)
  1238.     {
  1239. savegamestrings[saveSlot][saveCharIndex++] = ch;
  1240. savegamestrings[saveSlot][saveCharIndex] = 0;
  1241.     }
  1242.     break;
  1243. }
  1244. return true;
  1245.     }
  1246.     
  1247.     // Take care of any messages that need input
  1248.     if (messageToPrint)
  1249.     {
  1250. if (messageNeedsInput == true &&
  1251.     !(ch == KEY_SPACE || ch == KEY_N || ch == KEY_Y || ch == KEY_ESCAPE))
  1252.     return false;
  1253. menuactive = messageLastMenuActive;
  1254. messageToPrint = 0;
  1255. if (messageRoutine)
  1256.        {
  1257.         //WriteDebug("Calling message routine that needs answering...n");
  1258.     messageRoutine(ch);
  1259.        }
  1260. menuactive = false;
  1261. S_StartSound(NULL,sfx_swtchx);
  1262. return true;
  1263.     }
  1264.     //Don't really need this anymore -- keeping it for legacy...
  1265.     if (devparm && ch == KEY_F1)
  1266.     {
  1267. G_ScreenShot ();
  1268. return true;
  1269.     }
  1270.     if (ch == KEY_SCRNSHOT)
  1271.        {
  1272.         G_ScreenShot();
  1273.         return true;
  1274.        }
  1275.     if (ch == KEY_CAPITAL)
  1276.        {
  1277.         if (always_run == FALSE)
  1278.            always_run = TRUE;
  1279.         else
  1280.            always_run = FALSE;
  1281.         return true;
  1282.        }
  1283.     
  1284.     // F-Keys
  1285.     if (!menuactive)
  1286. switch(ch)
  1287. {
  1288.   case KEY_MINUS:         // Screen size down
  1289.     if (automapactive || chat_on)
  1290. return false;
  1291.     M_SizeDisplay(0);
  1292.     S_StartSound(NULL,sfx_stnmov);
  1293.     return true;
  1294.   case KEY_EQUALS:        // Screen size up
  1295.     if (automapactive || chat_on)
  1296. return false;
  1297.     M_SizeDisplay(1);
  1298.     S_StartSound(NULL,sfx_stnmov);
  1299.     return true;
  1300.   case KEY_F1:            // Help key
  1301.     M_StartControlPanel ();
  1302.     if ( gamemode == retail )
  1303.       currentMenu = &ReadDef2;
  1304.     else
  1305.       currentMenu = &ReadDef1;
  1306.     
  1307.     itemOn = 0;
  1308.     S_StartSound(NULL,sfx_swtchn);
  1309.     return true;
  1310.   case KEY_F2:            // Save
  1311.     M_StartControlPanel();
  1312.     S_StartSound(NULL,sfx_swtchn);
  1313.     M_SaveGame(0);
  1314.     return true;
  1315.   case KEY_F3:            // Load
  1316.     M_StartControlPanel();
  1317.     S_StartSound(NULL,sfx_swtchn);
  1318.     M_LoadGame(0);
  1319.     return true;
  1320.   case KEY_F4:            // Sound Volume
  1321.     M_StartControlPanel ();
  1322.     currentMenu = &SoundDef;
  1323.     itemOn = sfx_vol;
  1324.     S_StartSound(NULL,sfx_swtchn);
  1325.     return true;
  1326.   case KEY_F5:            // Detail toggle
  1327.     M_ChangeDetail(0);
  1328.     S_StartSound(NULL,sfx_swtchn);
  1329.     return true;
  1330.   case KEY_F6:            // Quicksave
  1331.     S_StartSound(NULL,sfx_swtchn);
  1332.     M_QuickSave();
  1333.     return true;
  1334.   case KEY_F7:            // End game
  1335.     S_StartSound(NULL,sfx_swtchn);
  1336.     M_EndGame(0);
  1337.     return true;
  1338.   case KEY_F8:            // Toggle messages
  1339.     M_ChangeMessages(0);
  1340.     S_StartSound(NULL,sfx_swtchn);
  1341.     return true;
  1342.   case KEY_F9:            // Quickload
  1343.     S_StartSound(NULL,sfx_swtchn);
  1344.     M_QuickLoad();
  1345.     return true;
  1346.   case KEY_F10:           // Quit DOOM
  1347.     S_StartSound(NULL,sfx_swtchn);
  1348.     M_QuitDOOM(0);
  1349.     return true;
  1350.   case KEY_F11:           // gamma toggle
  1351.     usegamma++;
  1352.     if (usegamma > 4)
  1353. usegamma = 0;
  1354.     players[consoleplayer].message = gammamsg[usegamma];
  1355.     I_SetPalette (W_CacheLumpName ("PLAYPAL",PU_CACHE));
  1356.     return true;
  1357. }
  1358.     
  1359.     // Pop-up menu?
  1360.     if (!menuactive)
  1361.     {
  1362. if (ch == KEY_ESCAPE)
  1363. {
  1364.     M_StartControlPanel ();
  1365.     S_StartSound(NULL,sfx_swtchn);
  1366.     return true;
  1367. }
  1368. return false;
  1369.     }
  1370.     
  1371.     // Keys usable within menu
  1372.     switch (ch)
  1373.     {
  1374.       case KEY_DOWNARROW:
  1375. do
  1376. {
  1377.     if (itemOn+1 > currentMenu->numitems-1)
  1378. itemOn = 0;
  1379.     else itemOn++;
  1380.     S_StartSound(NULL,sfx_pstop);
  1381. } while(currentMenu->menuitems[itemOn].status==-1);
  1382. return true;
  1383.       case KEY_UPARROW:
  1384. do
  1385. {
  1386.     if (!itemOn)
  1387. itemOn = currentMenu->numitems-1;
  1388.     else itemOn--;
  1389.     S_StartSound(NULL,sfx_pstop);
  1390. } while(currentMenu->menuitems[itemOn].status==-1);
  1391. return true;
  1392.       case KEY_LEFTARROW:
  1393. if (currentMenu->menuitems[itemOn].routine &&
  1394.     currentMenu->menuitems[itemOn].status == 2)
  1395. {
  1396.     S_StartSound(NULL,sfx_stnmov);
  1397.     currentMenu->menuitems[itemOn].routine(0);
  1398. }
  1399. return true;
  1400.       case KEY_RIGHTARROW:
  1401. if (currentMenu->menuitems[itemOn].routine &&
  1402.     currentMenu->menuitems[itemOn].status == 2)
  1403. {
  1404.     S_StartSound(NULL,sfx_stnmov);
  1405.     currentMenu->menuitems[itemOn].routine(1);
  1406. }
  1407. return true;
  1408.       case KEY_ENTER:
  1409. if (currentMenu->menuitems[itemOn].routine &&
  1410.     currentMenu->menuitems[itemOn].status)
  1411. {
  1412.     currentMenu->lastOn = itemOn;
  1413.     if (currentMenu->menuitems[itemOn].status == 2)
  1414.     {
  1415. currentMenu->menuitems[itemOn].routine(1);      // right arrow
  1416. S_StartSound(NULL,sfx_stnmov);
  1417.     }
  1418.     else
  1419.     {
  1420. currentMenu->menuitems[itemOn].routine(itemOn);
  1421. S_StartSound(NULL,sfx_pistol);
  1422.     }
  1423. }
  1424. return true;
  1425.       case KEY_CONSOLE:
  1426.            currentMenu->lastOn = itemOn;
  1427.            M_ClearMenus ();
  1428.            return true;
  1429.       case KEY_ESCAPE:
  1430. currentMenu->lastOn = itemOn;
  1431. M_ClearMenus ();
  1432. S_StartSound(NULL,sfx_swtchx);
  1433. return true;
  1434.       case KEY_BACKSPACE:
  1435. currentMenu->lastOn = itemOn;
  1436. if (currentMenu->prevMenu)
  1437. {
  1438.     currentMenu = currentMenu->prevMenu;
  1439.     itemOn = currentMenu->lastOn;
  1440.     S_StartSound(NULL,sfx_swtchn);
  1441. }
  1442. return true;
  1443.       default:
  1444. for (i = itemOn+1;i < currentMenu->numitems;i++)
  1445.     if (currentMenu->menuitems[i].alphaKey == ch)
  1446.     {
  1447. itemOn = i;
  1448. S_StartSound(NULL,sfx_pstop);
  1449. return true;
  1450.     }
  1451. for (i = 0;i <= itemOn;i++)
  1452.     if (currentMenu->menuitems[i].alphaKey == ch)
  1453.     {
  1454. itemOn = i;
  1455. S_StartSound(NULL,sfx_pstop);
  1456. return true;
  1457.     }
  1458. break;
  1459.     }
  1460.     return false;
  1461. }
  1462. //
  1463. // M_StartControlPanel
  1464. //
  1465. void M_StartControlPanel (void)
  1466. {
  1467.     // intro might call this repeatedly
  1468.     if (menuactive)
  1469. return;
  1470.     
  1471.     menuactive = 1;
  1472.     currentMenu = &MainDef;         // JDC
  1473.     itemOn = currentMenu->lastOn;   // JDC
  1474. }
  1475. //
  1476. // M_Drawer
  1477. // Called after the view has been rendered,
  1478. // but before it has been blitted.
  1479. //
  1480. void M_Drawer (void)
  1481. {
  1482.     static short x;
  1483.     static short y;
  1484.     short i;
  1485.     short max;
  1486.     char string[40];
  1487.     int start;
  1488.     inhelpscreens = false;
  1489.     
  1490.     // Horiz. & Vertically center string and print it.
  1491.     if (messageToPrint)
  1492.     {
  1493. start = 0;
  1494. y = (SCREENHEIGHT/2) - M_StringHeight(messageString)/2;
  1495. while(*(messageString+start))
  1496. {
  1497.     for (i = 0;(unsigned)i < strlen(messageString+start);i++)
  1498. if (*(messageString+start+i) == 'n')
  1499. {
  1500.     memset(string,0,40);
  1501.     strncpy(string,messageString+start,i);
  1502.     start += i+1;
  1503.     break;
  1504. }
  1505.     if ((unsigned)i == strlen(messageString+start))
  1506.     {
  1507. strcpy(string,messageString+start);
  1508. start += i;
  1509.     }
  1510.     x = (SCREENWIDTH/2) - M_StringWidth(string)/2;
  1511.     M_WriteText(x,y,string);
  1512.     y += SHORT(hu_font[0]->height);
  1513. }
  1514. return;
  1515.     }
  1516.     if (!menuactive)
  1517. return;
  1518.     if (currentMenu->routine)
  1519. currentMenu->routine();         // call Draw routine
  1520.     
  1521.     // DRAW MENU
  1522.     x = ((SCREENWIDTH-320)/2)+currentMenu->x;
  1523.     y = ((SCREENHEIGHT-200)/2)+currentMenu->y;
  1524.     max = currentMenu->numitems;
  1525.     for (i=0;i<max;i++)
  1526.     {
  1527. if (currentMenu->menuitems[i].name[0])
  1528.     V_DrawPatchDirect (x,y,0,
  1529.        W_CacheLumpName(currentMenu->menuitems[i].name ,PU_CACHE));
  1530. y += LINEHEIGHT;
  1531.     }
  1532.     
  1533.     // DRAW SKULL
  1534.     V_DrawPatchDirect(x+SKULLXOFF,((SCREENHEIGHT-200)/2)+currentMenu->y - 5 + itemOn*LINEHEIGHT, 0,
  1535.       W_CacheLumpName(skullName[whichSkull],PU_CACHE));
  1536. }
  1537. //
  1538. // M_ClearMenus
  1539. //
  1540. void M_ClearMenus (void)
  1541. {
  1542.     menuactive = 0;
  1543.     // if (!netgame && usergame && paused)
  1544.     //       sendpause = true;
  1545. }
  1546. //
  1547. // M_SetupNextMenu
  1548. //
  1549. void M_SetupNextMenu(menu_t *menudef)
  1550. {
  1551.     currentMenu = menudef;
  1552.     itemOn = currentMenu->lastOn;
  1553. }
  1554. //
  1555. // M_Ticker
  1556. //
  1557. void M_Ticker (void)
  1558. {
  1559.     if (--skullAnimCounter <= 0)
  1560.     {
  1561. whichSkull ^= 1;
  1562. skullAnimCounter = 8;
  1563.     }
  1564. }
  1565. //
  1566. // M_Init
  1567. //
  1568. void M_Init (void)
  1569. {
  1570.     currentMenu = &MainDef;
  1571.     menuactive = 0;
  1572.     itemOn = currentMenu->lastOn;
  1573.     whichSkull = 0;
  1574.     skullAnimCounter = 10;
  1575.     screenSize = screenblocks - 3;
  1576.     messageToPrint = 0;
  1577.     messageString = NULL;
  1578.     messageLastMenuActive = menuactive;
  1579.     quickSaveSlot = -1;
  1580.     // Here we could catch other version dependencies,
  1581.     //  like HELP1/2, and four episodes.
  1582.   
  1583.     switch ( gamemode )
  1584.     {
  1585.       case commercial:
  1586. // This is used because DOOM 2 had only one HELP
  1587.         //  page. I use CREDIT as second page now, but
  1588. //  kept this hack for educational purposes.
  1589. MainMenu[readthis] = MainMenu[quitdoom];
  1590. MainDef.numitems--;
  1591. MainDef.y += 8;
  1592. NewDef.prevMenu = &MainDef;
  1593. ReadDef1.routine = M_DrawReadThis1;
  1594. ReadDef1.x = 330;
  1595. ReadDef1.y = 165;
  1596. ReadMenu1[0].routine = M_FinishReadThis;
  1597. break;
  1598.       case shareware:
  1599. // Episode 2 and 3 are handled,
  1600. //  branching to an ad screen.
  1601.       case registered:
  1602. // We need to remove the fourth episode.
  1603. EpiDef.numitems--;
  1604. break;
  1605.       case retail:
  1606. // We are fine.
  1607.       default:
  1608. break;
  1609.     }
  1610.     
  1611. }