1 //
2 // Copyright(C) 1993-1996 Id Software, Inc.
3 // Copyright(C) 2005-2014 Simon Howard
4 //
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
14 //
15 // DESCRIPTION:
16 // DOOM selection menu, options, episode etc.
17 // Sliders and icons. Kinda widget stuff.
18 //
19
20
21 #include <stdlib.h>
22 #include <ctype.h>
23
24
25 #include "doomdef.h"
26 #include "doomkeys.h"
27 #include "dstrings.h"
28
29 #include "d_main.h"
30 #include "deh_main.h"
31
32 #include "i_input.h"
33 #include "i_swap.h"
34 #include "i_system.h"
35 #include "i_timer.h"
36 #include "i_video.h"
37 #include "z_zone.h"
38 #include "v_video.h"
39 #include "w_wad.h"
40
41 #include "r_local.h"
42
43
44 #include "hu_stuff.h"
45
46 #include "g_game.h"
47
48 #include "m_argv.h"
49 #include "m_controls.h"
50 #include "m_misc.h"
51 #include "m_saves.h" // [STRIFE]
52 #include "p_saveg.h"
53
54 #include "s_sound.h"
55
56 #include "doomstat.h"
57
58 // Data.
59 #include "sounds.h"
60
61 #include "m_menu.h"
62 #include "p_dialog.h"
63
64
65 extern void M_QuitStrife(int);
66
67 extern patch_t* hu_font[HU_FONTSIZE];
68 extern boolean message_dontfuckwithme;
69
70 extern boolean chat_on; // in heads-up code
71 extern boolean sendsave; // [STRIFE]
72
73 //
74 // defaulted values
75 //
76 int mouseSensitivity = 5;
77
78 // [STRIFE]: removed this entirely
79 // Show messages has default, 0 = off, 1 = on
80 //int showMessages = 1;
81
82
83 // Blocky mode, has default, 0 = high, 1 = normal
84 int detailLevel = 0;
85 int screenblocks = 10; // [STRIFE] default 10, not 9
86
87 // temp for screenblocks (0-9)
88 int screenSize;
89
90 // -1 = no quicksave slot picked!
91 int quickSaveSlot;
92
93 // 1 = message to be printed
94 int messageToPrint;
95 // ...and here is the message string!
96 char* messageString;
97
98 // message x & y
99 int messx;
100 int messy;
101 int messageLastMenuActive;
102
103 // timed message = no input from user
104 boolean messageNeedsInput;
105
106 void (*messageRoutine)(int response);
107
108 char gammamsg[5][26] =
109 {
110 GAMMALVL0,
111 GAMMALVL1,
112 GAMMALVL2,
113 GAMMALVL3,
114 GAMMALVL4
115 };
116
117 // we are going to be entering a savegame string
118 int saveStringEnter;
119 int saveSlot; // which slot to save in
120 int saveCharIndex; // which char we're editing
121 // old save description before edit
122 char saveOldString[SAVESTRINGSIZE];
123
124 boolean inhelpscreens;
125 boolean menuactive;
126 boolean menupause; // haleyjd 08/29/10: [STRIFE] New global
127 int menupausetime; // haleyjd 09/04/10: [STRIFE] New global
128 boolean menuindialog; // haleyjd 09/04/10: ditto
129
130 // haleyjd 08/27/10: [STRIFE] SKULLXOFF == -28, LINEHEIGHT == 19
131 #define CURSORXOFF -28
132 #define LINEHEIGHT 19
133
134 extern boolean sendpause;
135 char savegamestrings[10][SAVESTRINGSIZE];
136
137 char endstring[160];
138
139 // haleyjd 09/04/10: [STRIFE] Moved menuitem / menu structures into header
140 // because they are needed externally by the dialog engine.
141
142 // haleyjd 08/27/10: [STRIFE] skull* stuff changed to cursor* stuff
143 short itemOn; // menu item skull is on
144 short cursorAnimCounter; // skull animation counter
145 short whichCursor; // which skull to draw
146
147 // graphic name of cursors
148 // haleyjd 08/27/10: [STRIFE] M_SKULL* -> M_CURS*
149 char *cursorName[8] = {"M_CURS1", "M_CURS2", "M_CURS3", "M_CURS4",
150 "M_CURS5", "M_CURS6", "M_CURS7", "M_CURS8" };
151
152 // haleyjd 20110210 [STRIFE]: skill level for menus
153 int menuskill;
154
155 // current menudef
156 menu_t* currentMenu;
157
158 // haleyjd 03/01/13: [STRIFE] v1.31-only:
159 // Keeps track of whether the save game menu is being used to name a new
160 // character slot, or to just save the current game. In the v1.31 disassembly
161 // this was the new dword_8632C variable.
162 boolean namingCharacter;
163
164 //
165 // PROTOTYPES
166 //
167 void M_NewGame(int choice);
168 void M_Episode(int choice);
169 void M_ChooseSkill(int choice);
170 void M_LoadGame(int choice);
171 void M_SaveGame(int choice);
172 void M_Options(int choice);
173 void M_EndGame(int choice);
174 void M_ReadThis(int choice);
175 void M_ReadThis2(int choice);
176 void M_ReadThis3(int choice); // [STRIFE]
177
178 //void M_ChangeMessages(int choice); [STRIFE]
179 void M_ChangeSensitivity(int choice);
180 void M_SfxVol(int choice);
181 void M_VoiceVol(int choice); // [STRIFE]
182 void M_MusicVol(int choice);
183 void M_SizeDisplay(int choice);
184 void M_StartGame(int choice);
185 void M_Sound(int choice);
186
187 //void M_FinishReadThis(int choice); - [STRIFE] unused
188 void M_SaveSelect(int choice);
189 void M_ReadSaveStrings(void);
190 void M_QuickSave(void);
191 void M_QuickLoad(void);
192
193 void M_DrawMainMenu(void);
194 void M_DrawReadThis1(void);
195 void M_DrawReadThis2(void);
196 void M_DrawReadThis3(void); // [STRIFE]
197 void M_DrawNewGame(void);
198 void M_DrawEpisode(void);
199 void M_DrawOptions(void);
200 void M_DrawSound(void);
201 void M_DrawLoad(void);
202 void M_DrawSave(void);
203
204 void M_DrawSaveLoadBorder(int x,int y);
205 void M_SetupNextMenu(menu_t *menudef);
206 void M_DrawThermo(int x,int y,int thermWidth,int thermDot);
207 void M_DrawEmptyCell(menu_t *menu,int item);
208 void M_DrawSelCell(menu_t *menu,int item);
209 int M_StringWidth(char *string);
210 int M_StringHeight(char *string);
211 void M_StartMessage(char *string,void *routine,boolean input);
212 void M_StopMessage(void);
213
214
215
216
217 //
218 // DOOM MENU
219 //
220 enum
221 {
222 newgame = 0,
223 options,
224 loadgame,
225 savegame,
226 readthis,
227 quitdoom,
228 main_end
229 } main_e;
230
231 menuitem_t MainMenu[]=
232 {
233 {1,"M_NGAME",M_NewGame,'n'},
234 {1,"M_OPTION",M_Options,'o'},
235 {1,"M_LOADG",M_LoadGame,'l'},
236 {1,"M_SAVEG",M_SaveGame,'s'},
237 // Another hickup with Special edition.
238 {1,"M_RDTHIS",M_ReadThis,'h'}, // haleyjd 08/28/10: 'r' -> 'h'
239 {1,"M_QUITG",M_QuitStrife,'q'}
240 };
241
242 menu_t MainDef =
243 {
244 main_end,
245 NULL,
246 MainMenu,
247 M_DrawMainMenu,
248 97,45, // haleyjd 08/28/10: [STRIFE] changed y coord
249 0
250 };
251
252
253 //
254 // EPISODE SELECT
255 //
256 /*
257 enum
258 {
259 ep1,
260 ep2,
261 ep3,
262 ep4,
263 ep_end
264 } episodes_e;
265
266 menuitem_t EpisodeMenu[]=
267 {
268 {1,"M_EPI1", M_Episode,'k'},
269 {1,"M_EPI2", M_Episode,'t'},
270 {1,"M_EPI3", M_Episode,'i'},
271 {1,"M_EPI4", M_Episode,'t'}
272 };
273
274 menu_t EpiDef =
275 {
276 ep_end, // # of menu items
277 &MainDef, // previous menu
278 EpisodeMenu, // menuitem_t ->
279 M_DrawEpisode, // drawing routine ->
280 48,63, // x,y
281 ep1 // lastOn
282 };
283 */
284
285 //
286 // NEW GAME
287 //
288 enum
289 {
290 killthings,
291 toorough,
292 hurtme,
293 violence,
294 nightmare,
295 newg_end
296 } newgame_e;
297
298 menuitem_t NewGameMenu[]=
299 {
300 // haleyjd 08/28/10: [STRIFE] changed all shortcut letters
301 {1,"M_JKILL", M_ChooseSkill, 't'},
302 {1,"M_ROUGH", M_ChooseSkill, 'r'},
303 {1,"M_HURT", M_ChooseSkill, 'v'},
304 {1,"M_ULTRA", M_ChooseSkill, 'e'},
305 {1,"M_NMARE", M_ChooseSkill, 'b'}
306 };
307
308 menu_t NewDef =
309 {
310 newg_end, // # of menu items
311 &MainDef, // previous menu - haleyjd [STRIFE] changed to MainDef
312 NewGameMenu, // menuitem_t ->
313 M_DrawNewGame, // drawing routine ->
314 48,63, // x,y
315 toorough // lastOn - haleyjd [STRIFE]: default to skill 1
316 };
317
318 //
319 // OPTIONS MENU
320 //
321 enum
322 {
323 // haleyjd 08/28/10: [STRIFE] Removed messages, mouse sens., detail.
324 endgame,
325 scrnsize,
326 option_empty1,
327 soundvol,
328 opt_end
329 } options_e;
330
331 menuitem_t OptionsMenu[]=
332 {
333 // haleyjd 08/28/10: [STRIFE] Removed messages, mouse sens., detail.
334 {1,"M_ENDGAM", M_EndGame,'e'},
335 {2,"M_SCRNSZ", M_SizeDisplay,'s'},
336 {-1,"",0,'\0'},
337 {1,"M_SVOL", M_Sound,'s'}
338 };
339
340 menu_t OptionsDef =
341 {
342 opt_end,
343 &MainDef,
344 OptionsMenu,
345 M_DrawOptions,
346 60,37,
347 0
348 };
349
350 //
351 // Read This! MENU 1 & 2 & [STRIFE] 3
352 //
353 enum
354 {
355 rdthsempty1,
356 read1_end
357 } read_e;
358
359 menuitem_t ReadMenu1[] =
360 {
361 {1,"",M_ReadThis2,0}
362 };
363
364 menu_t ReadDef1 =
365 {
366 read1_end,
367 &MainDef,
368 ReadMenu1,
369 M_DrawReadThis1,
370 280,185,
371 0
372 };
373
374 enum
375 {
376 rdthsempty2,
377 read2_end
378 } read_e2;
379
380 menuitem_t ReadMenu2[]=
381 {
382 {1,"",M_ReadThis3,0} // haleyjd 08/28/10: [STRIFE] Go to ReadThis3
383 };
384
385 menu_t ReadDef2 =
386 {
387 read2_end,
388 &ReadDef1,
389 ReadMenu2,
390 M_DrawReadThis2,
391 250,185, // haleyjd 08/28/10: [STRIFE] changed coords
392 0
393 };
394
395 // haleyjd 08/28/10: Added Read This! menu 3
396 enum
397 {
398 rdthsempty3,
399 read3_end
400 } read_e3;
401
402 menuitem_t ReadMenu3[]=
403 {
404 {1,"",M_ClearMenus,0}
405 };
406
407 menu_t ReadDef3 =
408 {
409 read3_end,
410 &ReadDef2,
411 ReadMenu3,
412 M_DrawReadThis3,
413 250, 185,
414 0
415 };
416
417 //
418 // SOUND VOLUME MENU
419 //
420 enum
421 {
422 sfx_vol,
423 sfx_empty1,
424 music_vol,
425 sfx_empty2,
426 voice_vol,
427 sfx_empty3,
428 sfx_mouse,
429 sfx_empty4,
430 sound_end
431 } sound_e;
432
433 // haleyjd 08/29/10:
434 // [STRIFE]
435 // * Added voice volume
436 // * Moved mouse sensitivity here (who knows why...)
437 menuitem_t SoundMenu[]=
438 {
439 {2,"M_SFXVOL",M_SfxVol,'s'},
440 {-1,"",0,'\0'},
441 {2,"M_MUSVOL",M_MusicVol,'m'},
442 {-1,"",0,'\0'},
443 {2,"M_VOIVOL",M_VoiceVol,'v'},
444 {-1,"",0,'\0'},
445 {2,"M_MSENS",M_ChangeSensitivity,'m'},
446 {-1,"",0,'\0'}
447 };
448
449 menu_t SoundDef =
450 {
451 sound_end,
452 &OptionsDef,
453 SoundMenu,
454 M_DrawSound,
455 80,35, // [STRIFE] changed y coord 64 -> 35
456 0
457 };
458
459 //
460 // LOAD GAME MENU
461 //
462 enum
463 {
464 load1,
465 load2,
466 load3,
467 load4,
468 load5,
469 load6,
470 load_end
471 } load_e;
472
473 menuitem_t LoadMenu[]=
474 {
475 {1,"", M_LoadSelect,'1'},
476 {1,"", M_LoadSelect,'2'},
477 {1,"", M_LoadSelect,'3'},
478 {1,"", M_LoadSelect,'4'},
479 {1,"", M_LoadSelect,'5'},
480 {1,"", M_LoadSelect,'6'}
481 };
482
483 menu_t LoadDef =
484 {
485 load_end,
486 &MainDef,
487 LoadMenu,
488 M_DrawLoad,
489 80,54,
490 0
491 };
492
493 //
494 // SAVE GAME MENU
495 //
496 menuitem_t SaveMenu[]=
497 {
498 {1,"", M_SaveSelect,'1'},
499 {1,"", M_SaveSelect,'2'},
500 {1,"", M_SaveSelect,'3'},
501 {1,"", M_SaveSelect,'4'},
502 {1,"", M_SaveSelect,'5'},
503 {1,"", M_SaveSelect,'6'}
504 };
505
506 menu_t SaveDef =
507 {
508 load_end,
509 &MainDef,
510 SaveMenu,
511 M_DrawSave,
512 80,54,
513 0
514 };
515
516 void M_DrawNameChar(void);
517
518 //
519 // NAME CHARACTER MENU
520 //
521 // [STRIFE]
522 // haleyjd 20110210: New "Name Your Character" Menu
523 //
524 menu_t NameCharDef =
525 {
526 load_end,
527 &NewDef,
528 SaveMenu,
529 M_DrawNameChar,
530 80,54,
531 0
532 };
533
534
535 //
536 // M_ReadSaveStrings
537 // read the strings from the savegame files
538 //
539 // [STRIFE]
540 // haleyjd 20110210: Rewritten to read "name" file in each slot directory
541 //
M_ReadSaveStrings(void)542 void M_ReadSaveStrings(void)
543 {
544 FILE *handle;
545 int i;
546 char *fname = NULL;
547
548 for(i = 0; i < load_end; i++)
549 {
550 if(fname)
551 Z_Free(fname);
552 fname = M_SafeFilePath(savegamedir, M_MakeStrifeSaveDir(i, "\\name"));
553
554 handle = fopen(fname, "rb");
555 if(handle == NULL)
556 {
557 M_StringCopy(savegamestrings[i], EMPTYSTRING,
558 sizeof(savegamestrings[i]));
559 LoadMenu[i].status = 0;
560 continue;
561 }
562 fread(savegamestrings[i], 1, SAVESTRINGSIZE, handle);
563 fclose(handle);
564 LoadMenu[i].status = 1;
565 }
566
567 if(fname)
568 Z_Free(fname);
569 }
570
571 //
572 // M_DrawNameChar
573 //
574 // haleyjd 09/22/10: [STRIFE] New function
575 // Handler for drawing the "Name Your Character" menu.
576 //
M_DrawNameChar(void)577 void M_DrawNameChar(void)
578 {
579 int i;
580
581 M_WriteText(72, 28, DEH_String("Name Your Character"));
582
583 for (i = 0;i < load_end; i++)
584 {
585 M_DrawSaveLoadBorder(LoadDef.x,LoadDef.y+LINEHEIGHT*i);
586 M_WriteText(LoadDef.x,LoadDef.y+LINEHEIGHT*i,savegamestrings[i]);
587 }
588
589 if (saveStringEnter)
590 {
591 i = M_StringWidth(savegamestrings[quickSaveSlot]);
592 M_WriteText(LoadDef.x + i,LoadDef.y+LINEHEIGHT*quickSaveSlot,"_");
593 }
594 }
595
596 //
597 // M_DoNameChar
598 //
599 // haleyjd 09/22/10: [STRIFE] New function
600 // Handler for items in the "Name Your Character" menu.
601 //
M_DoNameChar(int choice)602 void M_DoNameChar(int choice)
603 {
604 int map;
605
606 // 20130301: clear naming character flag for 1.31 save logic
607 if(gameversion == exe_strife_1_31)
608 namingCharacter = false;
609 sendsave = 1;
610 ClearTmp();
611 G_WriteSaveName(choice, savegamestrings[choice]);
612 quickSaveSlot = choice;
613 SaveDef.lastOn = choice;
614 ClearSlot();
615 FromCurr();
616
617 if(isdemoversion)
618 map = 33;
619 else
620 map = 2;
621
622 G_DeferedInitNew(menuskill, map);
623 M_ClearMenus(0);
624 }
625
626 //
627 // M_LoadGame & Cie.
628 //
M_DrawLoad(void)629 void M_DrawLoad(void)
630 {
631 int i;
632
633 V_DrawPatchDirect(72, 28,
634 W_CacheLumpName(DEH_String("M_LOADG"), PU_CACHE));
635
636 for (i = 0;i < load_end; i++)
637 {
638 M_DrawSaveLoadBorder(LoadDef.x,LoadDef.y+LINEHEIGHT*i);
639 M_WriteText(LoadDef.x,LoadDef.y+LINEHEIGHT*i,savegamestrings[i]);
640 }
641 }
642
643
644
645 //
646 // Draw border for the savegame description
647 //
M_DrawSaveLoadBorder(int x,int y)648 void M_DrawSaveLoadBorder(int x,int y)
649 {
650 int i;
651
652 V_DrawPatchDirect(x - 8, y + 7,
653 W_CacheLumpName(DEH_String("M_LSLEFT"), PU_CACHE));
654
655 for (i = 0;i < 24;i++)
656 {
657 V_DrawPatchDirect(x, y + 7,
658 W_CacheLumpName(DEH_String("M_LSCNTR"), PU_CACHE));
659 x += 8;
660 }
661
662 V_DrawPatchDirect(x, y + 7,
663 W_CacheLumpName(DEH_String("M_LSRGHT"), PU_CACHE));
664 }
665
666
667
668 //
669 // User wants to load this game
670 //
M_LoadSelect(int choice)671 void M_LoadSelect(int choice)
672 {
673 // [STRIFE]: completely rewritten
674 char *name = NULL;
675
676 G_WriteSaveName(choice, savegamestrings[choice]);
677 ToCurr();
678
679 // use safe & portable filepath concatenation for Choco
680 name = M_SafeFilePath(savegamedir, M_MakeStrifeSaveDir(choice, ""));
681
682 G_ReadCurrent(name);
683 quickSaveSlot = choice;
684 M_ClearMenus(0);
685
686 Z_Free(name);
687 }
688
689 //
690 // Selected from DOOM menu
691 //
692 // [STRIFE] Verified unmodified
693 //
M_LoadGame(int choice)694 void M_LoadGame (int choice)
695 {
696 if (netgame)
697 {
698 M_StartMessage(DEH_String(LOADNET), NULL, false);
699 return;
700 }
701
702 M_SetupNextMenu(&LoadDef);
703 M_ReadSaveStrings();
704 }
705
706
707 //
708 // M_SaveGame & Cie.
709 //
M_DrawSave(void)710 void M_DrawSave(void)
711 {
712 int i;
713
714 V_DrawPatchDirect(72, 28, W_CacheLumpName(DEH_String("M_SAVEG"), PU_CACHE));
715 for (i = 0;i < load_end; i++)
716 {
717 M_DrawSaveLoadBorder(LoadDef.x,LoadDef.y+LINEHEIGHT*i);
718 M_WriteText(LoadDef.x,LoadDef.y+LINEHEIGHT*i,savegamestrings[i]);
719 }
720
721 if (saveStringEnter)
722 {
723 i = M_StringWidth(savegamestrings[quickSaveSlot]);
724 M_WriteText(LoadDef.x + i,LoadDef.y+LINEHEIGHT*quickSaveSlot,"_");
725 }
726 }
727
728 //
729 // M_Responder calls this when user is finished
730 //
M_DoSave(int slot)731 void M_DoSave(int slot)
732 {
733 // [STRIFE]: completely rewritten
734 if(slot >= 0)
735 {
736 sendsave = 1;
737 G_WriteSaveName(slot, savegamestrings[slot]);
738 M_ClearMenus(0);
739 quickSaveSlot = slot;
740 // haleyjd 20130922: slight divergence. We clear the destination slot
741 // of files here, which vanilla did not do. As a result, 1.31 had
742 // broken save behavior to the point of unusability. fraggle agrees
743 // this is detrimental enough to be fixed - unconditionally, for now.
744 ClearSlot();
745 FromCurr();
746 }
747 else
748 M_StartMessage(DEH_String(QSAVESPOT), NULL, false);
749 }
750
751 //
752 // User wants to save. Start string input for M_Responder
753 //
M_SaveSelect(int choice)754 void M_SaveSelect(int choice)
755 {
756 int x, y;
757
758 // we are going to be intercepting all chars
759 saveStringEnter = 1;
760
761 // We need to turn on text input:
762 x = LoadDef.x - 11;
763 y = LoadDef.y + choice * LINEHEIGHT - 4;
764 I_StartTextInput(x, y, x + 8 + 24 * 8 + 8, y + LINEHEIGHT - 2);
765
766 // [STRIFE]
767 quickSaveSlot = choice;
768 //saveSlot = choice;
769
770 M_StringCopy(saveOldString, savegamestrings[choice], sizeof(saveOldString));
771 if (!strcmp(savegamestrings[choice],EMPTYSTRING))
772 savegamestrings[choice][0] = 0;
773 saveCharIndex = strlen(savegamestrings[choice]);
774 }
775
776 //
777 // Selected from DOOM menu
778 //
M_SaveGame(int choice)779 void M_SaveGame (int choice)
780 {
781 // [STRIFE]
782 if (netgame)
783 {
784 // haleyjd 20110211: Hooray for Rogue's awesome multiplayer support...
785 M_StartMessage(DEH_String("You can't save a netgame"), NULL, false);
786 return;
787 }
788 if (!usergame)
789 {
790 M_StartMessage(DEH_String(SAVEDEAD),NULL,false);
791 return;
792 }
793
794 if (gamestate != GS_LEVEL)
795 return;
796
797 // [STRIFE]
798 if(gameversion == exe_strife_1_31)
799 {
800 // haleyjd 20130301: in 1.31, we can choose a slot again.
801 M_SetupNextMenu(&SaveDef);
802 M_ReadSaveStrings();
803 }
804 else
805 {
806 // In 1.2 and lower, you save over your character slot exclusively
807 M_ReadSaveStrings();
808 M_DoSave(quickSaveSlot);
809 }
810 }
811
812
813
814 //
815 // M_QuickSave
816 //
817 char tempstring[80];
818
M_QuickSaveResponse(int key)819 void M_QuickSaveResponse(int key)
820 {
821 if (key == key_menu_confirm)
822 {
823 M_DoSave(quickSaveSlot);
824 S_StartSound(NULL, sfx_mtalht); // [STRIFE] sound
825 }
826 }
827
M_QuickSave(void)828 void M_QuickSave(void)
829 {
830 if (netgame)
831 {
832 // haleyjd 20110211 [STRIFE]: More fun...
833 M_StartMessage(DEH_String("You can't save a netgame"), NULL, false);
834 return;
835 }
836
837 if (!usergame)
838 {
839 S_StartSound(NULL, sfx_oof);
840 return;
841 }
842
843 if (gamestate != GS_LEVEL)
844 return;
845
846 if (quickSaveSlot < 0)
847 {
848 M_StartControlPanel();
849 M_ReadSaveStrings();
850 M_SetupNextMenu(&SaveDef);
851 quickSaveSlot = -2; // means to pick a slot now
852 return;
853 }
854 DEH_snprintf(tempstring, 80, QSPROMPT, savegamestrings[quickSaveSlot]);
855 M_StartMessage(tempstring,M_QuickSaveResponse,true);
856 }
857
858
859
860 //
861 // M_QuickLoadResponse
862 //
M_QuickLoadResponse(int key)863 void M_QuickLoadResponse(int key)
864 {
865 if (key == key_menu_confirm)
866 {
867 M_LoadSelect(quickSaveSlot);
868 S_StartSound(NULL, sfx_mtalht); // [STRIFE] sound
869 }
870 }
871
872 //
873 // M_QuickLoad
874 //
875 // [STRIFE] Verified unmodified
876 //
M_QuickLoad(void)877 void M_QuickLoad(void)
878 {
879 if (netgame)
880 {
881 M_StartMessage(DEH_String(QLOADNET),NULL,false);
882 return;
883 }
884
885 if (quickSaveSlot < 0)
886 {
887 M_StartMessage(DEH_String(QSAVESPOT),NULL,false);
888 return;
889 }
890 DEH_snprintf(tempstring, 80, QLPROMPT, savegamestrings[quickSaveSlot]);
891 M_StartMessage(tempstring,M_QuickLoadResponse,true);
892 }
893
894
895
896
897 //
898 // Read This Menus
899 // Had a "quick hack to fix romero bug"
900 // haleyjd 08/28/10: [STRIFE] Draw HELP1, unconditionally.
901 //
M_DrawReadThis1(void)902 void M_DrawReadThis1(void)
903 {
904 inhelpscreens = true;
905
906 V_DrawPatchDirect (0, 0, W_CacheLumpName(DEH_String("HELP1"), PU_CACHE));
907 }
908
909
910
911 //
912 // Read This Menus
913 // haleyjd 08/28/10: [STRIFE] Not optional, draws HELP2
914 //
M_DrawReadThis2(void)915 void M_DrawReadThis2(void)
916 {
917 inhelpscreens = true;
918
919 V_DrawPatchDirect(0, 0, W_CacheLumpName(DEH_String("HELP2"), PU_CACHE));
920 }
921
922
923 //
924 // Read This Menus
925 // haleyjd 08/28/10: [STRIFE] New function to draw HELP3.
926 //
M_DrawReadThis3(void)927 void M_DrawReadThis3(void)
928 {
929 inhelpscreens = true;
930
931 V_DrawPatchDirect(0, 0, W_CacheLumpName(DEH_String("HELP3"), PU_CACHE));
932 }
933
934 //
935 // Change Sfx & Music volumes
936 //
937 // haleyjd 08/29/10: [STRIFE]
938 // * Changed title graphic coordinates
939 // * Added voice volume and sensitivity sliders
940 //
M_DrawSound(void)941 void M_DrawSound(void)
942 {
943 V_DrawPatchDirect (100, 10, W_CacheLumpName(DEH_String("M_SVOL"), PU_CACHE));
944
945 M_DrawThermo(SoundDef.x,SoundDef.y+LINEHEIGHT*(sfx_vol+1),
946 16,sfxVolume);
947
948 M_DrawThermo(SoundDef.x,SoundDef.y+LINEHEIGHT*(music_vol+1),
949 16,musicVolume);
950
951 M_DrawThermo(SoundDef.x,SoundDef.y+LINEHEIGHT*(voice_vol+1),
952 16,voiceVolume);
953
954 M_DrawThermo(SoundDef.x,SoundDef.y+LINEHEIGHT*(sfx_mouse+1),
955 16,mouseSensitivity);
956 }
957
M_Sound(int choice)958 void M_Sound(int choice)
959 {
960 M_SetupNextMenu(&SoundDef);
961 }
962
M_SfxVol(int choice)963 void M_SfxVol(int choice)
964 {
965 switch(choice)
966 {
967 case 0:
968 if (sfxVolume)
969 sfxVolume--;
970 break;
971 case 1:
972 if (sfxVolume < 15)
973 sfxVolume++;
974 break;
975 }
976
977 S_SetSfxVolume(sfxVolume * 8);
978 }
979
980 //
981 // M_VoiceVol
982 //
983 // haleyjd 08/29/10: [STRIFE] New function
984 // Sets voice volume level.
985 //
M_VoiceVol(int choice)986 void M_VoiceVol(int choice)
987 {
988 switch(choice)
989 {
990 case 0:
991 if (voiceVolume)
992 voiceVolume--;
993 break;
994 case 1:
995 if (voiceVolume < 15)
996 voiceVolume++;
997 break;
998 }
999
1000 S_SetVoiceVolume(voiceVolume * 8);
1001 }
1002
M_MusicVol(int choice)1003 void M_MusicVol(int choice)
1004 {
1005 switch(choice)
1006 {
1007 case 0:
1008 if (musicVolume)
1009 musicVolume--;
1010 break;
1011 case 1:
1012 if (musicVolume < 15)
1013 musicVolume++;
1014 break;
1015 }
1016
1017 S_SetMusicVolume(musicVolume * 8);
1018 }
1019
1020
1021
1022
1023 //
1024 // M_DrawMainMenu
1025 //
1026 // haleyjd 08/27/10: [STRIFE] Changed x coordinate; M_DOOM -> M_STRIFE
1027 //
M_DrawMainMenu(void)1028 void M_DrawMainMenu(void)
1029 {
1030 V_DrawPatchDirect(84, 2,
1031 W_CacheLumpName(DEH_String("M_STRIFE"), PU_CACHE));
1032 }
1033
1034
1035
1036
1037 //
1038 // M_NewGame
1039 //
1040 // haleyjd 08/31/10: [STRIFE] Changed M_NEWG -> M_NGAME
1041 //
M_DrawNewGame(void)1042 void M_DrawNewGame(void)
1043 {
1044 V_DrawPatchDirect(96, 14, W_CacheLumpName(DEH_String("M_NGAME"), PU_CACHE));
1045 V_DrawPatchDirect(54, 38, W_CacheLumpName(DEH_String("M_SKILL"), PU_CACHE));
1046 }
1047
M_NewGame(int choice)1048 void M_NewGame(int choice)
1049 {
1050 if (netgame && !demoplayback)
1051 {
1052 M_StartMessage(DEH_String(NEWGAME),NULL,false);
1053 return;
1054 }
1055 // haleyjd 09/07/10: [STRIFE] Removed Chex Quest and DOOM gamemodes
1056 if(gameversion == exe_strife_1_31)
1057 namingCharacter = true; // for 1.31 save logic
1058 M_SetupNextMenu(&NewDef);
1059 }
1060
1061
1062 //
1063 // M_Episode
1064 //
1065
1066 // haleyjd: [STRIFE] Unused
1067 /*
1068 int epi;
1069
1070 void M_DrawEpisode(void)
1071 {
1072 V_DrawPatchDirect(54, 38, W_CacheLumpName(DEH_String("M_EPISOD"), PU_CACHE));
1073 }
1074
1075 void M_VerifyNightmare(int key)
1076 {
1077 if (key != key_menu_confirm)
1078 return;
1079
1080 G_DeferedInitNew(nightmare, 1);
1081 M_ClearMenus (0);
1082 }
1083 */
1084
M_ChooseSkill(int choice)1085 void M_ChooseSkill(int choice)
1086 {
1087 // haleyjd 09/07/10: Removed nightmare confirmation
1088 // [STRIFE]: start "Name Your Character" menu
1089 menuskill = choice;
1090 currentMenu = &NameCharDef;
1091 itemOn = NameCharDef.lastOn;
1092 M_ReadSaveStrings();
1093 }
1094
1095 /*
1096 // haleyjd [STRIFE] Unused
1097 void M_Episode(int choice)
1098 {
1099 if ( (gamemode == shareware)
1100 && choice)
1101 {
1102 M_StartMessage(DEH_String(SWSTRING),NULL,false);
1103 M_SetupNextMenu(&ReadDef1);
1104 return;
1105 }
1106
1107 // Yet another hack...
1108 if ( (gamemode == registered)
1109 && (choice > 2))
1110 {
1111 fprintf( stderr,
1112 "M_Episode: 4th episode requires UltimateDOOM\n");
1113 choice = 0;
1114 }
1115
1116 epi = choice;
1117 M_SetupNextMenu(&NewDef);
1118 }
1119 */
1120
1121
1122 //
1123 // M_Options
1124 //
1125 char detailNames[2][9] = {"M_GDHIGH","M_GDLOW"};
1126 char msgNames[2][9] = {"M_MSGOFF","M_MSGON"};
1127
1128
M_DrawOptions(void)1129 void M_DrawOptions(void)
1130 {
1131 // haleyjd 08/27/10: [STRIFE] M_OPTTTL -> M_OPTION
1132 V_DrawPatchDirect(108, 15,
1133 W_CacheLumpName(DEH_String("M_OPTION"), PU_CACHE));
1134
1135 // haleyjd 08/26/10: [STRIFE] Removed messages, sensitivity, detail.
1136
1137 M_DrawThermo(OptionsDef.x,OptionsDef.y+LINEHEIGHT*(scrnsize+1),
1138 9,screenSize);
1139 }
1140
M_Options(int choice)1141 void M_Options(int choice)
1142 {
1143 M_SetupNextMenu(&OptionsDef);
1144 }
1145
1146 //
1147 // M_AutoUseHealth
1148 //
1149 // [STRIFE] New function
1150 // haleyjd 20110211: toggle autouse health state
1151 //
M_AutoUseHealth(void)1152 void M_AutoUseHealth(void)
1153 {
1154 if(!netgame && usergame)
1155 {
1156 players[consoleplayer].cheats ^= CF_AUTOHEALTH;
1157
1158 if(players[consoleplayer].cheats & CF_AUTOHEALTH)
1159 players[consoleplayer].message = DEH_String("Auto use health ON");
1160 else
1161 players[consoleplayer].message = DEH_String("Auto use health OFF");
1162 }
1163 }
1164
1165 //
1166 // M_ChangeShowText
1167 //
1168 // [STRIFE] New function
1169 //
M_ChangeShowText(void)1170 void M_ChangeShowText(void)
1171 {
1172 dialogshowtext ^= true;
1173
1174 if(dialogshowtext)
1175 players[consoleplayer].message = DEH_String("Conversation Text On");
1176 else
1177 players[consoleplayer].message = DEH_String("Conversation Text Off");
1178 }
1179
1180 //
1181 // Toggle messages on/off
1182 //
1183 // [STRIFE] Messages cannot be disabled in Strife
1184 /*
1185 void M_ChangeMessages(int choice)
1186 {
1187 // warning: unused parameter `int choice'
1188 choice = 0;
1189 showMessages = 1 - showMessages;
1190
1191 if (!showMessages)
1192 players[consoleplayer].message = DEH_String(MSGOFF);
1193 else
1194 players[consoleplayer].message = DEH_String(MSGON);
1195
1196 message_dontfuckwithme = true;
1197 }
1198 */
1199
1200
1201 //
1202 // M_EndGame
1203 //
M_EndGameResponse(int key)1204 void M_EndGameResponse(int key)
1205 {
1206 if (key != key_menu_confirm)
1207 return;
1208
1209 currentMenu->lastOn = itemOn;
1210 M_ClearMenus (0);
1211 D_StartTitle ();
1212 }
1213
M_EndGame(int choice)1214 void M_EndGame(int choice)
1215 {
1216 choice = 0;
1217 if (!usergame)
1218 {
1219 S_StartSound(NULL,sfx_oof);
1220 return;
1221 }
1222
1223 if (netgame)
1224 {
1225 M_StartMessage(DEH_String(NETEND),NULL,false);
1226 return;
1227 }
1228
1229 M_StartMessage(DEH_String(ENDGAME),M_EndGameResponse,true);
1230 }
1231
1232
1233
1234
1235 //
1236 // M_ReadThis
1237 //
M_ReadThis(int choice)1238 void M_ReadThis(int choice)
1239 {
1240 choice = 0;
1241 M_SetupNextMenu(&ReadDef1);
1242 }
1243
1244 //
1245 // M_ReadThis2
1246 //
1247 // haleyjd 08/28/10: [STRIFE] Eliminated DOOM stuff.
1248 //
M_ReadThis2(int choice)1249 void M_ReadThis2(int choice)
1250 {
1251 choice = 0;
1252 M_SetupNextMenu(&ReadDef2);
1253 }
1254
1255 //
1256 // M_ReadThis3
1257 //
1258 // haleyjd 08/28/10: [STRIFE] New function.
1259 //
M_ReadThis3(int choice)1260 void M_ReadThis3(int choice)
1261 {
1262 choice = 0;
1263 M_SetupNextMenu(&ReadDef3);
1264 }
1265
1266 /*
1267 // haleyjd 08/28/10: [STRIFE] Not used.
1268 void M_FinishReadThis(int choice)
1269 {
1270 choice = 0;
1271 M_SetupNextMenu(&MainDef);
1272 }
1273 */
1274
1275 #if 0
1276 extern void F_StartCast(void);
1277
1278 //
1279 // M_CheckStartCast
1280 //
1281 // [STRIFE] New but unused function. Was going to start a cast
1282 // call from within the menu system... not functional even in
1283 // the earliest demo version.
1284 //
1285 void M_CheckStartCast()
1286 {
1287 if(usergame)
1288 {
1289 M_StartMessage(DEH_String("You have to end your game first."), NULL, false);
1290 return;
1291 }
1292
1293 F_StartCast();
1294 M_ClearMenus(0);
1295 }
1296 #endif
1297
1298 //
1299 // M_QuitResponse
1300 //
1301 // haleyjd 09/11/10: [STRIFE] Modifications to start up endgame
1302 // demosequence.
1303 //
M_QuitResponse(int key)1304 void M_QuitResponse(int key)
1305 {
1306 char buffer[20];
1307
1308 if (key != key_menu_confirm)
1309 return;
1310
1311 if(netgame)
1312 I_Quit();
1313 else
1314 {
1315 DEH_snprintf(buffer, sizeof(buffer), "qfmrm%i", gametic % 8 + 1);
1316 I_StartVoice(buffer);
1317 D_QuitGame();
1318 }
1319 }
1320
1321 /*
1322 // haleyjd 09/11/10: [STRIFE] Unused
1323 static char *M_SelectEndMessage(void)
1324 {
1325 }
1326 */
1327
1328 //
1329 // M_QuitStrife
1330 //
1331 // [STRIFE] Renamed from M_QuitDOOM
1332 // haleyjd 09/11/10: No randomized text message; that's taken care of
1333 // by the randomized voice message after confirmation.
1334 //
M_QuitStrife(int choice)1335 void M_QuitStrife(int choice)
1336 {
1337 DEH_snprintf(endstring, sizeof(endstring),
1338 "Do you really want to leave?\n\n" DOSY);
1339
1340 M_StartMessage(endstring, M_QuitResponse, true);
1341 }
1342
1343
1344
1345
M_ChangeSensitivity(int choice)1346 void M_ChangeSensitivity(int choice)
1347 {
1348 switch(choice)
1349 {
1350 case 0:
1351 if (mouseSensitivity)
1352 mouseSensitivity--;
1353 break;
1354 case 1:
1355 if (mouseSensitivity < 9)
1356 mouseSensitivity++;
1357 break;
1358 }
1359 }
1360
1361 /*
1362 // haleyjd [STRIFE] Unused
1363 void M_ChangeDetail(int choice)
1364 {
1365 choice = 0;
1366 detailLevel = 1 - detailLevel;
1367
1368 R_SetViewSize (screenblocks, detailLevel);
1369
1370 if (!detailLevel)
1371 players[consoleplayer].message = DEH_String(DETAILHI);
1372 else
1373 players[consoleplayer].message = DEH_String(DETAILLO);
1374 }
1375 */
1376
1377 // [STRIFE] Verified unmodified
M_SizeDisplay(int choice)1378 void M_SizeDisplay(int choice)
1379 {
1380 switch(choice)
1381 {
1382 case 0:
1383 if (screenSize > 0)
1384 {
1385 screenblocks--;
1386 screenSize--;
1387 }
1388 break;
1389 case 1:
1390 if (screenSize < 8)
1391 {
1392 screenblocks++;
1393 screenSize++;
1394 }
1395 break;
1396 }
1397
1398 R_SetViewSize (screenblocks, detailLevel);
1399 }
1400
1401
1402
1403
1404 //
1405 // Menu Functions
1406 //
1407
1408 //
1409 // M_DrawThermo
1410 //
1411 // haleyjd 08/28/10: [STRIFE] Changes to some patch coordinates.
1412 //
1413 void
M_DrawThermo(int x,int y,int thermWidth,int thermDot)1414 M_DrawThermo
1415 ( int x,
1416 int y,
1417 int thermWidth,
1418 int thermDot )
1419 {
1420 int xx;
1421 int yy; // [STRIFE] Needs a temp y coordinate variable
1422 int i;
1423
1424 xx = x;
1425 yy = y + 6; // [STRIFE] +6 to y coordinate
1426 V_DrawPatchDirect(xx, yy, W_CacheLumpName(DEH_String("M_THERML"), PU_CACHE));
1427 xx += 8;
1428 for (i=0;i<thermWidth;i++)
1429 {
1430 V_DrawPatchDirect(xx, yy, W_CacheLumpName(DEH_String("M_THERMM"), PU_CACHE));
1431 xx += 8;
1432 }
1433 V_DrawPatchDirect(xx, yy, W_CacheLumpName(DEH_String("M_THERMR"), PU_CACHE));
1434
1435 // [STRIFE] +2 to initial y coordinate
1436 V_DrawPatchDirect((x + 8) + thermDot * 8, y + 2,
1437 W_CacheLumpName(DEH_String("M_THERMO"), PU_CACHE));
1438 }
1439
1440
1441 // haleyjd: These are from DOOM v0.5 and the prebeta! They drew those ugly red &
1442 // blue checkboxes... preserved for historical interest, as not in Strife.
1443 void
M_DrawEmptyCell(menu_t * menu,int item)1444 M_DrawEmptyCell
1445 ( menu_t* menu,
1446 int item )
1447 {
1448 V_DrawPatchDirect(menu->x - 10, menu->y + item * LINEHEIGHT - 1,
1449 W_CacheLumpName(DEH_String("M_CELL1"), PU_CACHE));
1450 }
1451
1452 void
M_DrawSelCell(menu_t * menu,int item)1453 M_DrawSelCell
1454 ( menu_t* menu,
1455 int item )
1456 {
1457 V_DrawPatchDirect(menu->x - 10, menu->y + item * LINEHEIGHT - 1,
1458 W_CacheLumpName(DEH_String("M_CELL2"), PU_CACHE));
1459 }
1460
1461
1462 void
M_StartMessage(char * string,void * routine,boolean input)1463 M_StartMessage
1464 ( char* string,
1465 void* routine,
1466 boolean input )
1467 {
1468 messageLastMenuActive = menuactive;
1469 messageToPrint = 1;
1470 messageString = string;
1471 messageRoutine = routine;
1472 messageNeedsInput = input;
1473 menuactive = true;
1474 return;
1475 }
1476
1477
1478
M_StopMessage(void)1479 void M_StopMessage(void)
1480 {
1481 menuactive = messageLastMenuActive;
1482 messageToPrint = 0;
1483 }
1484
1485
1486
1487 //
1488 // Find string width from hu_font chars
1489 //
M_StringWidth(char * string)1490 int M_StringWidth(char* string)
1491 {
1492 size_t i;
1493 int w = 0;
1494 int c;
1495
1496 for (i = 0;i < strlen(string);i++)
1497 {
1498 c = toupper(string[i]) - HU_FONTSTART;
1499 if (c < 0 || c >= HU_FONTSIZE)
1500 w += 4;
1501 else
1502 w += SHORT (hu_font[c]->width);
1503 }
1504
1505 return w;
1506 }
1507
1508
1509
1510 //
1511 // Find string height from hu_font chars
1512 //
M_StringHeight(char * string)1513 int M_StringHeight(char* string)
1514 {
1515 size_t i;
1516 int h;
1517 int height = SHORT(hu_font[0]->height);
1518
1519 h = height;
1520 for (i = 0;i < strlen(string);i++)
1521 if (string[i] == '\n')
1522 h += height;
1523
1524 return h;
1525 }
1526
1527
1528 //
1529 // M_WriteText
1530 //
1531 // Write a string using the hu_font
1532 // haleyjd 09/04/10: [STRIFE]
1533 // * Rogue made a lot of changes to this for the dialog system.
1534 //
1535 int
M_WriteText(int x,int y,const char * string)1536 M_WriteText
1537 ( int x,
1538 int y,
1539 const char* string) // haleyjd: made const for safety w/dialog engine
1540 {
1541 int w;
1542 const char* ch;
1543 int c;
1544 int cx;
1545 int cy;
1546
1547 ch = string;
1548 cx = x;
1549 cy = y;
1550
1551 while(1)
1552 {
1553 c = *ch++;
1554 if (!c)
1555 break;
1556
1557 // haleyjd 09/04/10: [STRIFE] Don't draw spaces at the start of lines.
1558 if(c == ' ' && cx == x)
1559 continue;
1560
1561 if (c == '\n')
1562 {
1563 cx = x;
1564 cy += 11; // haleyjd 09/04/10: [STRIFE]: Changed 12 -> 11
1565 continue;
1566 }
1567
1568 c = toupper(c) - HU_FONTSTART;
1569 if (c < 0 || c>= HU_FONTSIZE)
1570 {
1571 cx += 4;
1572 continue;
1573 }
1574
1575 w = SHORT (hu_font[c]->width);
1576
1577 // haleyjd 09/04/10: [STRIFE] Different linebreak handling
1578 if (cx + w > SCREENWIDTH - 20)
1579 {
1580 cx = x;
1581 cy += 11;
1582 --ch;
1583 }
1584 else
1585 {
1586 V_DrawPatchDirect(cx, cy, hu_font[c]);
1587 cx += w;
1588 }
1589 }
1590
1591 // [STRIFE] Return final y coordinate.
1592 return cy + 12;
1593 }
1594
1595 //
1596 // M_DialogDimMsg
1597 //
1598 // [STRIFE] New function
1599 // haleyjd 09/04/10: Painstakingly transformed from the assembly code, as the
1600 // decompiler could not touch it. Redimensions a string to fit on screen, leaving
1601 // at least a 20 pixel margin on the right side. The string passed in must be
1602 // writable.
1603 //
M_DialogDimMsg(int x,int y,char * str,boolean useyfont)1604 void M_DialogDimMsg(int x, int y, char *str, boolean useyfont)
1605 {
1606 int rightbound = (SCREENWIDTH - 20) - x;
1607 patch_t **fontarray; // ebp
1608 int linewidth = 0; // esi
1609 int i = 0; // edx
1610 char *message = str; // edi
1611 char bl; // bl
1612
1613 if(useyfont)
1614 fontarray = yfont;
1615 else
1616 fontarray = hu_font;
1617
1618 bl = toupper(*message);
1619
1620 if(!bl)
1621 return;
1622
1623 // outer loop - run to end of string
1624 do
1625 {
1626 if(bl != '\n')
1627 {
1628 int charwidth; // eax
1629 int tempwidth; // ecx
1630
1631 if(bl < HU_FONTSTART || bl > HU_FONTEND)
1632 charwidth = 4;
1633 else
1634 charwidth = SHORT(fontarray[bl - HU_FONTSTART]->width);
1635
1636 tempwidth = linewidth + charwidth;
1637
1638 // Test if the line still fits within the boundary...
1639 if(tempwidth >= rightbound)
1640 {
1641 // Doesn't fit...
1642 char *tempptr = &message[i]; // ebx
1643 char al; // al
1644
1645 // inner loop - run backward til a space (or the start of the
1646 // string) is found, subtracting width off the current line.
1647 // BUG: shouldn't we stop at a previous '\n' too?
1648 while(*tempptr != ' ' && i > 0)
1649 {
1650 tempptr--;
1651 // BUG: they didn't add the first char to linewidth yet...
1652 linewidth -= charwidth;
1653 i--;
1654 al = toupper(*tempptr);
1655 if(al < HU_FONTSTART || al > HU_FONTEND)
1656 charwidth = 4;
1657 else
1658 charwidth = SHORT(fontarray[al - HU_FONTSTART]->width);
1659 }
1660 // Replace the space with a linebreak.
1661 // BUG: what if i is zero? ... infinite loop time!
1662 message[i] = '\n';
1663 linewidth = 0;
1664 }
1665 else
1666 {
1667 // The line does fit.
1668 // Spaces at the start of a line don't count though.
1669 if(!(bl == ' ' && linewidth == 0))
1670 linewidth += charwidth;
1671 }
1672 }
1673 else
1674 linewidth = 0; // '\n' seen, so reset the line width
1675 }
1676 while((bl = toupper(message[++i])) != 0); // step to the next character
1677 }
1678
1679 // These keys evaluate to a "null" key in Vanilla Doom that allows weird
1680 // jumping in the menus. Preserve this behavior for accuracy.
1681
IsNullKey(int key)1682 static boolean IsNullKey(int key)
1683 {
1684 return key == KEY_PAUSE || key == KEY_CAPSLOCK
1685 || key == KEY_SCRLCK || key == KEY_NUMLOCK;
1686 }
1687
1688 //
1689 // CONTROL PANEL
1690 //
1691
1692 //
1693 // M_Responder
1694 //
M_Responder(event_t * ev)1695 boolean M_Responder (event_t* ev)
1696 {
1697 int ch;
1698 int key;
1699 int i;
1700 static int joywait = 0;
1701 static int mousewait = 0;
1702 static int mousey = 0;
1703 static int lasty = 0;
1704 static int mousex = 0;
1705 static int lastx = 0;
1706
1707 // In testcontrols mode, none of the function keys should do anything
1708 // - the only key is escape to quit.
1709
1710 if (testcontrols)
1711 {
1712 if (ev->type == ev_quit
1713 || (ev->type == ev_keydown
1714 && (ev->data1 == key_menu_activate || ev->data1 == key_menu_quit)))
1715 {
1716 I_Quit();
1717 return true;
1718 }
1719
1720 return false;
1721 }
1722
1723 // "close" button pressed on window?
1724 if (ev->type == ev_quit)
1725 {
1726 // First click on close button = bring up quit confirm message.
1727 // Second click on close button = confirm quit
1728
1729 if (menuactive && messageToPrint && messageRoutine == M_QuitResponse)
1730 {
1731 M_QuitResponse(key_menu_confirm);
1732 }
1733 else
1734 {
1735 S_StartSound(NULL, sfx_swtchn);
1736 M_QuitStrife(0);
1737 }
1738
1739 return true;
1740 }
1741
1742 // key is the key pressed, ch is the actual character typed
1743
1744 ch = 0;
1745 key = -1;
1746
1747 if (ev->type == ev_joystick && joywait < I_GetTime())
1748 {
1749 if (ev->data3 < 0)
1750 {
1751 key = key_menu_up;
1752 joywait = I_GetTime() + 5;
1753 }
1754 else if (ev->data3 > 0)
1755 {
1756 key = key_menu_down;
1757 joywait = I_GetTime() + 5;
1758 }
1759
1760 if (ev->data2 < 0)
1761 {
1762 key = key_menu_left;
1763 joywait = I_GetTime() + 2;
1764 }
1765 else if (ev->data2 > 0)
1766 {
1767 key = key_menu_right;
1768 joywait = I_GetTime() + 2;
1769 }
1770
1771 if (ev->data1&1)
1772 {
1773 key = key_menu_forward;
1774 joywait = I_GetTime() + 5;
1775 }
1776 if (ev->data1&2)
1777 {
1778 key = key_menu_back;
1779 joywait = I_GetTime() + 5;
1780 }
1781 if (joybmenu >= 0 && (ev->data1 & (1 << joybmenu)) != 0)
1782 {
1783 key = key_menu_activate;
1784 joywait = I_GetTime() + 5;
1785 }
1786 }
1787 else
1788 {
1789 if (ev->type == ev_mouse && mousewait < I_GetTime())
1790 {
1791 mousey += ev->data3;
1792 if (mousey < lasty-30)
1793 {
1794 key = key_menu_down;
1795 mousewait = I_GetTime() + 5;
1796 mousey = lasty -= 30;
1797 }
1798 else if (mousey > lasty+30)
1799 {
1800 key = key_menu_up;
1801 mousewait = I_GetTime() + 5;
1802 mousey = lasty += 30;
1803 }
1804
1805 mousex += ev->data2;
1806 if (mousex < lastx-30)
1807 {
1808 key = key_menu_left;
1809 mousewait = I_GetTime() + 5;
1810 mousex = lastx -= 30;
1811 }
1812 else if (mousex > lastx+30)
1813 {
1814 key = key_menu_right;
1815 mousewait = I_GetTime() + 5;
1816 mousex = lastx += 30;
1817 }
1818
1819 if (ev->data1&1)
1820 {
1821 key = key_menu_forward;
1822 mousewait = I_GetTime() + 15;
1823 mouse_fire_countdown = 5; // villsa [STRIFE]
1824 }
1825
1826 if (ev->data1&2)
1827 {
1828 key = key_menu_back;
1829 mousewait = I_GetTime() + 15;
1830 }
1831 }
1832 else
1833 {
1834 if (ev->type == ev_keydown)
1835 {
1836 key = ev->data1;
1837 ch = ev->data2;
1838 }
1839 }
1840 }
1841
1842 if (key == -1)
1843 return false;
1844
1845 // Save Game string input
1846 if (saveStringEnter)
1847 {
1848 switch(key)
1849 {
1850 case KEY_BACKSPACE:
1851 if (saveCharIndex > 0)
1852 {
1853 saveCharIndex--;
1854 savegamestrings[quickSaveSlot][saveCharIndex] = 0;
1855 }
1856 break;
1857
1858 case KEY_ESCAPE:
1859 saveStringEnter = 0;
1860 I_StopTextInput();
1861 M_StringCopy(savegamestrings[quickSaveSlot], saveOldString,
1862 sizeof(savegamestrings[quickSaveSlot]));
1863 break;
1864
1865 case KEY_ENTER:
1866 // [STRIFE]
1867 saveStringEnter = 0;
1868 I_StopTextInput();
1869 if(gameversion == exe_strife_1_31 && !namingCharacter)
1870 {
1871 // In 1.31, we can be here as a result of normal saving again,
1872 // whereas in 1.2 this only ever happens when naming your
1873 // character to begin a new game.
1874 M_DoSave(quickSaveSlot);
1875 return true;
1876 }
1877 if (savegamestrings[quickSaveSlot][0])
1878 M_DoNameChar(quickSaveSlot);
1879 break;
1880
1881 default:
1882 // Savegame name entry. This is complicated.
1883 // Vanilla has a bug where the shift key is ignored when entering
1884 // a savegame name. If vanilla_keyboard_mapping is on, we want
1885 // to emulate this bug by using ev->data1. But if it's turned off,
1886 // it implies the user doesn't care about Vanilla emulation:
1887 // instead, use ev->data3 which gives the fully-translated and
1888 // modified key input.
1889
1890 if (ev->type != ev_keydown)
1891 {
1892 break;
1893 }
1894
1895 if (vanilla_keyboard_mapping)
1896 {
1897 ch = ev->data1;
1898 }
1899 else
1900 {
1901 ch = ev->data3;
1902 }
1903
1904 ch = toupper(ch);
1905
1906 if (ch != ' '
1907 && (ch - HU_FONTSTART < 0 || ch - HU_FONTSTART >= HU_FONTSIZE))
1908 {
1909 break;
1910 }
1911
1912 if (ch >= 32 && ch <= 127 &&
1913 saveCharIndex < SAVESTRINGSIZE-1 &&
1914 M_StringWidth(savegamestrings[quickSaveSlot]) <
1915 (SAVESTRINGSIZE-2)*8)
1916 {
1917 savegamestrings[quickSaveSlot][saveCharIndex++] = ch;
1918 savegamestrings[quickSaveSlot][saveCharIndex] = 0;
1919 }
1920 break;
1921 }
1922 return true;
1923 }
1924
1925 // Take care of any messages that need input
1926 if (messageToPrint)
1927 {
1928 if (messageNeedsInput)
1929 {
1930 if (key != ' ' && key != KEY_ESCAPE
1931 && key != key_menu_confirm && key != key_menu_abort)
1932 {
1933 return false;
1934 }
1935 }
1936
1937 menuactive = messageLastMenuActive;
1938 messageToPrint = 0;
1939 if (messageRoutine)
1940 messageRoutine(key);
1941
1942 menupause = false; // [STRIFE] unpause
1943 menuactive = false;
1944 S_StartSound(NULL, sfx_mtalht); // [STRIFE] sound
1945 return true;
1946 }
1947
1948 // [STRIFE]:
1949 // * In v1.2 this is moved to F9 (quickload)
1950 // * In v1.31 it is moved to F12 with DM spy, and quicksave
1951 // functionality is restored separate from normal saving
1952 /*
1953 if (devparm && key == key_menu_help)
1954 {
1955 G_ScreenShot ();
1956 return true;
1957 }
1958 */
1959
1960 // F-Keys
1961 if (!menuactive)
1962 {
1963 if (key == key_menu_decscreen) // Screen size down
1964 {
1965 if (automapactive || chat_on)
1966 return false;
1967 M_SizeDisplay(0);
1968 S_StartSound(NULL, sfx_stnmov);
1969 return true;
1970 }
1971 else if (key == key_menu_incscreen) // Screen size up
1972 {
1973 if (automapactive || chat_on)
1974 return false;
1975 M_SizeDisplay(1);
1976 S_StartSound(NULL, sfx_stnmov);
1977 return true;
1978 }
1979 else if (key == key_menu_help) // Help key
1980 {
1981 M_StartControlPanel ();
1982 // haleyjd 08/29/10: [STRIFE] always ReadDef1
1983 currentMenu = &ReadDef1;
1984
1985 itemOn = 0;
1986 S_StartSound(NULL, sfx_swtchn);
1987 return true;
1988 }
1989 else if (key == key_menu_save) // Save
1990 {
1991 // [STRIFE]: Hub saves
1992 if(gameversion == exe_strife_1_31)
1993 namingCharacter = false; // just saving normally, in 1.31
1994
1995 if(netgame || players[consoleplayer].health <= 0 ||
1996 players[consoleplayer].cheats & CF_ONFIRE)
1997 {
1998 S_StartSound(NULL, sfx_oof);
1999 }
2000 else
2001 {
2002 M_StartControlPanel();
2003 S_StartSound(NULL, sfx_swtchn);
2004 M_SaveGame(0);
2005 }
2006 return true;
2007 }
2008 else if (key == key_menu_load) // Load
2009 {
2010 // [STRIFE]: Hub saves
2011 if(gameversion == exe_strife_1_31)
2012 {
2013 // 1.31: normal save loading
2014 namingCharacter = false;
2015 M_StartControlPanel();
2016 M_LoadGame(0);
2017 S_StartSound(NULL, sfx_swtchn);
2018 }
2019 else
2020 {
2021 // Pre 1.31: quickload only
2022 S_StartSound(NULL, sfx_swtchn);
2023 M_QuickLoad();
2024 }
2025 return true;
2026 }
2027 else if (key == key_menu_volume) // Sound Volume
2028 {
2029 M_StartControlPanel ();
2030 currentMenu = &SoundDef;
2031 itemOn = sfx_vol;
2032 S_StartSound(NULL, sfx_swtchn);
2033 return true;
2034 }
2035 else if (key == key_menu_detail) // Detail toggle
2036 {
2037 //M_ChangeDetail(0);
2038 M_AutoUseHealth(); // [STRIFE]
2039 S_StartSound(NULL, sfx_swtchn);
2040 return true;
2041 }
2042 else if (key == key_menu_qsave) // Quicksave
2043 {
2044 // [STRIFE]: Hub saves
2045 if(gameversion == exe_strife_1_31)
2046 namingCharacter = false; // for 1.31 save changes
2047
2048 if(netgame || players[consoleplayer].health <= 0 ||
2049 players[consoleplayer].cheats & CF_ONFIRE)
2050 {
2051 S_StartSound(NULL, sfx_oof);
2052 }
2053 else
2054 {
2055 S_StartSound(NULL, sfx_swtchn);
2056 M_QuickSave();
2057 }
2058 return true;
2059 }
2060 else if (key == key_menu_endgame) // End game
2061 {
2062 S_StartSound(NULL, sfx_swtchn);
2063 M_EndGame(0);
2064 return true;
2065 }
2066 else if (key == key_menu_messages) // Toggle messages
2067 {
2068 //M_ChangeMessages(0);
2069 M_ChangeShowText(); // [STRIFE]
2070 S_StartSound(NULL, sfx_swtchn);
2071 return true;
2072 }
2073 else if (key == key_menu_qload) // Quickload
2074 {
2075 // [STRIFE]
2076 // * v1.2: takes a screenshot
2077 // * v1.31: does quickload again
2078 if(gameversion == exe_strife_1_31)
2079 {
2080 namingCharacter = false;
2081 S_StartSound(NULL, sfx_swtchn);
2082 M_QuickLoad();
2083 }
2084 else
2085 G_ScreenShot();
2086 return true;
2087 }
2088 else if (key == key_menu_quit) // Quit DOOM
2089 {
2090 S_StartSound(NULL, sfx_swtchn);
2091 M_QuitStrife(0);
2092 return true;
2093 }
2094 else if (key == key_menu_gamma) // gamma toggle
2095 {
2096 usegamma++;
2097 if (usegamma > 4)
2098 usegamma = 0;
2099 players[consoleplayer].message = DEH_String(gammamsg[usegamma]);
2100 I_SetPalette (W_CacheLumpName (DEH_String("PLAYPAL"),PU_CACHE));
2101 return true;
2102 }
2103 else if(gameversion == exe_strife_1_31 && key == key_spy)
2104 {
2105 // haleyjd 20130301: 1.31 moved screenshots to F12.
2106 G_ScreenShot();
2107 return true;
2108 }
2109 else if (key != 0 && key == key_menu_screenshot)
2110 {
2111 G_ScreenShot();
2112 return true;
2113 }
2114 }
2115
2116 // Pop-up menu?
2117 if (!menuactive)
2118 {
2119 if (key == key_menu_activate)
2120 {
2121 M_StartControlPanel ();
2122 S_StartSound(NULL, sfx_swtchn);
2123 return true;
2124 }
2125 return false;
2126 }
2127
2128
2129 // Keys usable within menu
2130
2131 if (key == key_menu_down)
2132 {
2133 // Move down to next item
2134
2135 do
2136 {
2137 if (itemOn+1 > currentMenu->numitems-1)
2138 itemOn = 0;
2139 else itemOn++;
2140 S_StartSound(NULL, sfx_pstop);
2141 } while(currentMenu->menuitems[itemOn].status==-1);
2142
2143 return true;
2144 }
2145 else if (key == key_menu_up)
2146 {
2147 // Move back up to previous item
2148
2149 do
2150 {
2151 if (!itemOn)
2152 itemOn = currentMenu->numitems-1;
2153 else itemOn--;
2154 S_StartSound(NULL, sfx_pstop);
2155 } while(currentMenu->menuitems[itemOn].status==-1);
2156
2157 return true;
2158 }
2159 else if (key == key_menu_left)
2160 {
2161 // Slide slider left
2162
2163 if (currentMenu->menuitems[itemOn].routine &&
2164 currentMenu->menuitems[itemOn].status == 2)
2165 {
2166 S_StartSound(NULL, sfx_stnmov);
2167 currentMenu->menuitems[itemOn].routine(0);
2168 }
2169 return true;
2170 }
2171 else if (key == key_menu_right)
2172 {
2173 // Slide slider right
2174
2175 if (currentMenu->menuitems[itemOn].routine &&
2176 currentMenu->menuitems[itemOn].status == 2)
2177 {
2178 S_StartSound(NULL, sfx_stnmov);
2179 currentMenu->menuitems[itemOn].routine(1);
2180 }
2181 return true;
2182 }
2183 else if (key == key_menu_forward)
2184 {
2185 // Activate menu item
2186
2187 if (currentMenu->menuitems[itemOn].routine &&
2188 currentMenu->menuitems[itemOn].status)
2189 {
2190 currentMenu->lastOn = itemOn;
2191 if (currentMenu->menuitems[itemOn].status == 2)
2192 {
2193 currentMenu->menuitems[itemOn].routine(1); // right arrow
2194 S_StartSound(NULL, sfx_stnmov);
2195 }
2196 else
2197 {
2198 currentMenu->menuitems[itemOn].routine(itemOn);
2199 //S_StartSound(NULL, sfx_swish); [STRIFE] No sound is played here.
2200 }
2201 }
2202 return true;
2203 }
2204 else if (key == key_menu_activate)
2205 {
2206 // Deactivate menu
2207 if(gameversion == exe_strife_1_31) // [STRIFE]: 1.31 saving
2208 namingCharacter = false;
2209
2210 if(menuindialog) // [STRIFE] - Get out of dialog engine semi-gracefully
2211 P_DialogDoChoice(-1);
2212
2213 currentMenu->lastOn = itemOn;
2214 M_ClearMenus (0);
2215 S_StartSound(NULL, sfx_mtalht); // villsa [STRIFE]: sounds
2216 return true;
2217 }
2218 else if (key == key_menu_back)
2219 {
2220 // Go back to previous menu
2221
2222 currentMenu->lastOn = itemOn;
2223 if (currentMenu->prevMenu)
2224 {
2225 currentMenu = currentMenu->prevMenu;
2226 itemOn = currentMenu->lastOn;
2227 S_StartSound(NULL, sfx_swtchn);
2228 }
2229 return true;
2230 }
2231
2232 // Keyboard shortcut?
2233 // Vanilla Strife has a weird behavior where it jumps to the scroll bars
2234 // when certain keys are pressed, so emulate this.
2235
2236 else if (ch != 0 || IsNullKey(key))
2237 {
2238 // Keyboard shortcut?
2239
2240 for (i = itemOn+1;i < currentMenu->numitems;i++)
2241 {
2242 if (currentMenu->menuitems[i].alphaKey == ch)
2243 {
2244 itemOn = i;
2245 S_StartSound(NULL, sfx_pstop);
2246 return true;
2247 }
2248 }
2249
2250 for (i = 0;i <= itemOn;i++)
2251 {
2252 if (currentMenu->menuitems[i].alphaKey == ch)
2253 {
2254 itemOn = i;
2255 S_StartSound(NULL, sfx_pstop);
2256 return true;
2257 }
2258 }
2259 }
2260
2261 return false;
2262 }
2263
2264
2265
2266 //
2267 // M_StartControlPanel
2268 //
M_StartControlPanel(void)2269 void M_StartControlPanel (void)
2270 {
2271 // intro might call this repeatedly
2272 if (menuactive)
2273 return;
2274
2275 menuactive = 1;
2276 menupause = true;
2277 currentMenu = &MainDef; // JDC
2278 itemOn = currentMenu->lastOn; // JDC
2279 }
2280
2281
2282 //
2283 // M_Drawer
2284 // Called after the view has been rendered,
2285 // but before it has been blitted.
2286 //
M_Drawer(void)2287 void M_Drawer (void)
2288 {
2289 static short x;
2290 static short y;
2291 unsigned int i;
2292 unsigned int max;
2293 char string[80];
2294 char *name;
2295 int start;
2296
2297 inhelpscreens = false;
2298
2299 // Horiz. & Vertically center string and print it.
2300 if (messageToPrint)
2301 {
2302 start = 0;
2303 y = 100 - M_StringHeight(messageString) / 2;
2304 while (messageString[start] != '\0')
2305 {
2306 int foundnewline = 0;
2307
2308 for (i = 0; i < strlen(messageString + start); i++)
2309 {
2310 if (messageString[start + i] == '\n')
2311 {
2312 M_StringCopy(string, messageString + start,
2313 sizeof(string));
2314 if (i < sizeof(string))
2315 {
2316 string[i] = '\0';
2317 }
2318
2319 foundnewline = 1;
2320 start += i + 1;
2321 break;
2322 }
2323 }
2324
2325 if (!foundnewline)
2326 {
2327 M_StringCopy(string, messageString + start,
2328 sizeof(string));
2329 start += strlen(string);
2330 }
2331
2332 x = 160 - M_StringWidth(string) / 2;
2333 M_WriteText(x, y, string);
2334 y += SHORT(hu_font[0]->height);
2335 }
2336
2337 return;
2338 }
2339
2340 if (!menuactive)
2341 return;
2342
2343 if (currentMenu->routine)
2344 currentMenu->routine(); // call Draw routine
2345
2346 // DRAW MENU
2347 x = currentMenu->x;
2348 y = currentMenu->y;
2349 max = currentMenu->numitems;
2350
2351 for (i=0;i<max;i++)
2352 {
2353 name = DEH_String(currentMenu->menuitems[i].name);
2354
2355 if (name[0])
2356 {
2357 V_DrawPatchDirect (x, y, W_CacheLumpName(name, PU_CACHE));
2358 }
2359 y += LINEHEIGHT;
2360 }
2361
2362
2363 // haleyjd 08/27/10: [STRIFE] Adjust to draw spinning Sigil
2364 // DRAW SIGIL
2365 V_DrawPatchDirect(x + CURSORXOFF, currentMenu->y - 5 + itemOn*LINEHEIGHT,
2366 W_CacheLumpName(DEH_String(cursorName[whichCursor]),
2367 PU_CACHE));
2368
2369 }
2370
2371
2372 //
2373 // M_ClearMenus
2374 //
2375 // haleyjd 08/28/10: [STRIFE] Added an int param so this can be called by menus.
2376 // 09/08/10: Added menupause.
2377 //
M_ClearMenus(int choice)2378 void M_ClearMenus (int choice)
2379 {
2380 choice = 0; // haleyjd: for no warning; not from decompilation.
2381 menuactive = 0;
2382 menupause = 0;
2383 }
2384
2385
2386
2387
2388 //
2389 // M_SetupNextMenu
2390 //
M_SetupNextMenu(menu_t * menudef)2391 void M_SetupNextMenu(menu_t *menudef)
2392 {
2393 currentMenu = menudef;
2394 itemOn = currentMenu->lastOn;
2395 }
2396
2397
2398 //
2399 // M_Ticker
2400 //
2401 // haleyjd 08/27/10: [STRIFE] Rewritten for Sigil cursor
2402 //
M_Ticker(void)2403 void M_Ticker (void)
2404 {
2405 if (--cursorAnimCounter <= 0)
2406 {
2407 whichCursor = (whichCursor + 1) % 8;
2408 cursorAnimCounter = 5;
2409 }
2410 }
2411
2412
2413 //
2414 // M_Init
2415 //
2416 // haleyjd 08/27/10: [STRIFE] Removed DOOM gamemode stuff
2417 //
M_Init(void)2418 void M_Init (void)
2419 {
2420 currentMenu = &MainDef;
2421 menuactive = 0;
2422 itemOn = currentMenu->lastOn;
2423 whichCursor = 0;
2424 cursorAnimCounter = 10;
2425 screenSize = screenblocks - 3;
2426 messageToPrint = 0;
2427 messageString = NULL;
2428 messageLastMenuActive = menuactive; // STRIFE-FIXME: assigns 0 here...
2429 quickSaveSlot = -1;
2430
2431 // [STRIFE]: Initialize savegame paths and clear temporary directory
2432 G_WriteSaveName(5, "ME");
2433 ClearTmp();
2434
2435 // Here we could catch other version dependencies,
2436 // like HELP1/2, and four episodes.
2437 }
2438
2439