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