1 // Emacs style mode select -*- C++ -*-
2 //-----------------------------------------------------------------------------
3 //
4 // $Id: m_menu.cpp 4542 2014-02-09 17:39:42Z dr_sean $
5 //
6 // Copyright (C) 1993-1996 by id Software, Inc.
7 // Copyright (C) 2006-2014 by The Odamex Team.
8 //
9 // This program is free software; you can redistribute it and/or
10 // modify it under the terms of the GNU General Public License
11 // as published by the Free Software Foundation; either version 2
12 // of the License, or (at your option) any later version.
13 //
14 // This program is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 // GNU General Public License for more details.
18 //
19 // DESCRIPTION:
20 // DOOM selection menu, options, episode etc.
21 // Sliders and icons. Kinda widget stuff.
22 //
23 //-----------------------------------------------------------------------------
24
25 #include "doomdef.h"
26 #include "gstrings.h"
27 #include "c_console.h"
28 #include "c_dispatch.h"
29 #include "d_main.h"
30 #include "i_system.h"
31 #include "i_video.h"
32 #include "i_input.h"
33 #include "z_zone.h"
34 #include "v_video.h"
35 #include "w_wad.h"
36 #include "r_local.h"
37 #include "hu_stuff.h"
38 #include "g_game.h"
39 #include "m_argv.h"
40 #include "m_swap.h"
41 #include "m_random.h"
42 #include "s_sound.h"
43 #include "doomstat.h"
44 #include "m_menu.h"
45 #include "v_text.h"
46 #include "st_stuff.h"
47 #include "p_ctf.h"
48 #include "r_sky.h"
49 #include "cl_main.h"
50 #include "c_bind.h"
51 #include "c_level.h"
52
53 #include "gi.h"
54 #include "m_memio.h"
55 #include "m_fileio.h"
56
57 #ifdef _XBOX
58 #include "i_xbox.h"
59 #endif
60
61 extern patch_t* hu_font[HU_FONTSIZE];
62
63 // temp for screenblocks (0-9)
64 int screenSize;
65
66 // -1 = no quicksave slot picked!
67 int quickSaveSlot;
68
69 // 1 = message to be printed
70 int messageToPrint;
71 // ...and here is the message string!
72 const char* messageString;
73
74 // message x & y
75 int messx;
76 int messy;
77 int messageLastMenuActive;
78
79 // timed message = no input from user
80 bool messageNeedsInput;
81
82 void (*messageRoutine)(int response);
83 void CL_SendUserInfo(void);
84 void M_ChangeTeam (int choice);
85 team_t D_TeamByName (const char *team);
86 gender_t D_GenderByName (const char *gender);
87
88 #define SAVESTRINGSIZE 24
89
90 // we are going to be entering a savegame string
91 int genStringEnter;
92 int genStringLen; // [RH] Max # of chars that can be entered
93 void (*genStringEnd)(int slot);
94 int saveSlot; // which slot to save in
95 int saveCharIndex; // which char we're editing
96 // old save description before edit
97 char saveOldString[SAVESTRINGSIZE];
98
99 BOOL menuactive;
100
101 int repeatKey;
102 int repeatCount;
103
104 extern bool st_firsttime;
105
106 extern bool sendpause;
107 char savegamestrings[10][SAVESTRINGSIZE];
108
109 char endstring[160];
110
111 menustack_t MenuStack[16];
112 int MenuStackDepth;
113
114 short itemOn; // menu item skull is on
115 short skullAnimCounter; // skull animation counter
116 short whichSkull; // which skull to draw
117 bool drawSkull; // [RH] don't always draw skull
118
119 // graphic name of skulls
120 char skullName[2][9] = {"M_SKULL1", "M_SKULL2"};
121
122 // current menudef
123 oldmenu_t *currentMenu;
124
125 //
126 // PROTOTYPES
127 //
128 void M_NewGame(int choice);
129 void M_Episode(int choice);
130 void M_Expansion(int choice);
131 void M_ChooseSkill(int choice);
132 void M_LoadGame(int choice);
133 void M_SaveGame(int choice);
134 void M_Options(int choice);
135 void M_EndGame(int choice);
136 void M_ReadThis(int choice);
137 void M_ReadThis2(int choice);
138 void M_ReadThis3(int choice);
139 void M_QuitDOOM(int choice);
140
141 void M_ChangeDetail(int choice);
142 void M_StartGame(int choice);
143 void M_Sound(int choice);
144
145 void M_FinishReadThis(int choice);
146 void M_LoadSelect(int choice);
147 void M_SaveSelect(int choice);
148 void M_ReadSaveStrings(void);
149 void M_QuickSave(void);
150 void M_QuickLoad(void);
151
152 void M_DrawMainMenu(void);
153 void M_DrawReadThis1(void);
154 void M_DrawReadThis2(void);
155 void M_DrawReadThis3(void);
156 void M_DrawNewGame(void);
157 void M_DrawEpisode(void);
158 void M_DrawOptions(void);
159 void M_DrawSound(void);
160 void M_DrawLoad(void);
161 void M_DrawSave(void);
162
163 void M_DrawSaveLoadBorder(int x,int y, int len);
164 void M_SetupNextMenu(oldmenu_t *menudef);
165 void M_DrawEmptyCell(oldmenu_t *menu,int item);
166 void M_DrawSelCell(oldmenu_t *menu,int item);
167 int M_StringHeight(char *string);
168 void M_StartControlPanel(void);
169 void M_StartMessage(const char *string,void (*routine)(int),bool input);
170 void M_StopMessage(void);
171 void M_ClearMenus (void);
172
173 // [RH] For player setup menu.
174 static void M_PlayerSetupTicker (void);
175 static void M_PlayerSetupDrawer (void);
176 static void M_EditPlayerName (int choice);
177 //static void M_EditPlayerTeam (int choice);
178 //static void M_PlayerTeamChanged (int choice);
179 static void M_PlayerNameChanged (int choice);
180 static void M_SlidePlayerRed (int choice);
181 static void M_SlidePlayerGreen (int choice);
182 static void M_SlidePlayerBlue (int choice);
183 static void M_ChangeGender (int choice);
184 static void M_ChangeAutoAim (int choice);
185 bool M_DemoNoPlay;
186
187 static DCanvas *FireScreen;
188
189 EXTERN_CVAR (hud_targetnames)
190
191 //
192 // DOOM MENU
193 //
194 enum d1_main_t
195 {
196 d1_newgame = 0,
197 d1_options, // [RH] Moved
198 d1_loadgame,
199 d1_savegame,
200 d1_readthis,
201 d1_quitdoom,
202 d1_main_end
203 }d1_main_e;
204
205 oldmenuitem_t DoomMainMenu[]=
206 {
207 {1,"M_NGAME",M_NewGame,'N'},
208 {1,"M_OPTION",M_Options,'O'}, // [RH] Moved
209 {1,"M_LOADG",M_LoadGame,'L'},
210 {1,"M_SAVEG",M_SaveGame,'S'},
211 {1,"M_RDTHIS",M_ReadThis,'R'},
212 {1,"M_QUITG",M_QuitDOOM,'Q'}
213 };
214
215 //
216 // DOOM 2 MENU
217 //
218
219 enum d2_main_t
220 {
221 d2_newgame = 0,
222 d2_options, // [RH] Moved
223 d2_loadgame,
224 d2_savegame,
225 d2_quitdoom,
226 d2_main_end
227 }d2_main_e;
228
229 oldmenuitem_t Doom2MainMenu[]=
230 {
231 {1,"M_NGAME",M_NewGame,'N'},
232 {1,"M_OPTION",M_Options,'O'}, // [RH] Moved
233 {1,"M_LOADG",M_LoadGame,'L'},
234 {1,"M_SAVEG",M_SaveGame,'S'},
235 {1,"M_QUITG",M_QuitDOOM,'Q'}
236 };
237
238
239 // Default used is the Doom Menu
240 oldmenu_t MainDef =
241 {
242 d1_main_end,
243 DoomMainMenu,
244 M_DrawMainMenu,
245 97,64,
246 0
247 };
248
249
250
251 //
252 // EPISODE SELECT
253 //
254 enum episodes_t
255 {
256 ep1,
257 ep2,
258 ep3,
259 ep4,
260 ep_end
261 } episodes_e;
262
263 oldmenuitem_t EpisodeMenu[]=
264 {
265 {1,"M_EPI1", M_Episode,'k'},
266 {1,"M_EPI2", M_Episode,'t'},
267 {1,"M_EPI3", M_Episode,'i'},
268 {1,"M_EPI4", M_Episode,'t'}
269 };
270
271 oldmenu_t EpiDef =
272 {
273 ep4, // # of menu items
274 EpisodeMenu, // oldmenuitem_t ->
275 M_DrawEpisode, // drawing routine ->
276 48,63, // x,y
277 ep1 // lastOn
278 };
279
280 //
281 // EXPANSION SELECT (DOOM2 BFG)
282 //
283 enum expansions_t
284 {
285 hoe,
286 nrftl,
287 exp_end
288 } expansions_e;
289
290 oldmenuitem_t ExpansionMenu[]=
291 {
292 {1,"M_EPI1", M_Expansion,'h'},
293 {1,"M_EPI2", M_Expansion,'n'},
294 };
295
296 oldmenu_t ExpDef =
297 {
298 exp_end, // # of menu items
299 ExpansionMenu, // oldmenuitem_t ->
300 M_DrawEpisode, // drawing routine ->
301 48,63, // x,y
302 hoe // lastOn
303 };
304
305
306 //
307 // NEW GAME
308 //
309 enum newgame_t
310 {
311 killthings,
312 toorough,
313 hurtme,
314 violence,
315 nightmare,
316 newg_end
317 } newgame_e;
318
319 oldmenuitem_t NewGameMenu[]=
320 {
321 {1,"M_JKILL", M_ChooseSkill, 'i'},
322 {1,"M_ROUGH", M_ChooseSkill, 'h'},
323 {1,"M_HURT", M_ChooseSkill, 'h'},
324 {1,"M_ULTRA", M_ChooseSkill, 'u'},
325 {1,"M_NMARE", M_ChooseSkill, 'n'}
326 };
327
328 oldmenu_t NewDef =
329 {
330 newg_end, // # of menu items
331 NewGameMenu, // oldmenuitem_t ->
332 M_DrawNewGame, // drawing routine ->
333 48,63, // x,y
334 hurtme // lastOn
335 };
336
337 //
338 // [RH] Player Setup Menu
339 //
340 byte FireRemap[256];
341
342 enum psetup_t
343 {
344 playername,
345 playerteam,
346 playerred,
347 playergreen,
348 playerblue,
349 playersex,
350 playeraim,
351 psetup_end
352 } psetup_e;
353
354 oldmenuitem_t PlayerSetupMenu[] =
355 {
356 { 1,"", M_EditPlayerName, 'N' },
357 { 2,"", M_ChangeTeam, 'T' },
358 { 2,"", M_SlidePlayerRed, 'R' },
359 { 2,"", M_SlidePlayerGreen, 'G' },
360 { 2,"", M_SlidePlayerBlue, 'B' },
361 { 2,"", M_ChangeGender, 'E' },
362 { 2,"", M_ChangeAutoAim, 'A' }
363 };
364
365 oldmenu_t PSetupDef = {
366 psetup_end,
367 PlayerSetupMenu,
368 M_PlayerSetupDrawer,
369 48, 47,
370 playername
371 };
372
373 //
374 // OPTIONS MENU
375 //
376 // [RH] This menu is now handled in m_options.c
377 //
378 bool OptionsActive;
379
380 oldmenu_t OptionsDef =
381 {
382 0,
383 NULL,
384 NULL,
385 0,0,
386 0
387 };
388
389
390 //
391 // Read This!
392 //
393 enum read_t
394 {
395 rdthsempty1,
396 read1_end
397 } read_e;
398
399 oldmenuitem_t ReadMenu1[] =
400 {
401 {1,"",M_ReadThis2,0}
402 };
403
404 oldmenu_t ReadDef1 =
405 {
406 read1_end,
407 ReadMenu1,
408 M_DrawReadThis1,
409 280,185,
410 0
411 };
412
413 enum read_t2
414 {
415 rdthsempty2,
416 read2_end
417 } read_e2;
418
419 oldmenuitem_t ReadMenu2[]=
420 {
421 {1,"",M_ReadThis3,0}
422 };
423
424 oldmenu_t ReadDef2 =
425 {
426 read2_end,
427 ReadMenu2,
428 M_DrawReadThis2,
429 330,175,
430 0
431 };
432
433 enum read_t3
434 {
435 rdthsempty3,
436 read3_end
437 } read_e3;
438
439
440 oldmenuitem_t ReadMenu3[]=
441 {
442 {1,"",M_FinishReadThis,0}
443 };
444
445 oldmenu_t ReadDef3 =
446 {
447 read3_end,
448 ReadMenu3,
449 M_DrawReadThis3,
450 330,175,
451 0
452 };
453
454 //
455 // LOAD GAME MENU
456 //
457 enum load_t
458 {
459 load1,
460 load2,
461 load3,
462 load4,
463 load5,
464 load6,
465 load7,
466 load8,
467 load_end
468 } load_e;
469
470 oldmenuitem_t LoadMenu[]=
471 {
472 {1,"", M_LoadSelect,'1'},
473 {1,"", M_LoadSelect,'2'},
474 {1,"", M_LoadSelect,'3'},
475 {1,"", M_LoadSelect,'4'},
476 {1,"", M_LoadSelect,'5'},
477 {1,"", M_LoadSelect,'6'},
478 {1,"", M_LoadSelect,'7'},
479 {1,"", M_LoadSelect,'8'},
480 };
481
482 oldmenu_t LoadDef =
483 {
484 load_end,
485 LoadMenu,
486 M_DrawLoad,
487 80,54,
488 0
489 };
490
491 //
492 // SAVE GAME MENU
493 //
494 oldmenuitem_t SaveMenu[]=
495 {
496 {1,"", M_SaveSelect,'1'},
497 {1,"", M_SaveSelect,'2'},
498 {1,"", M_SaveSelect,'3'},
499 {1,"", M_SaveSelect,'4'},
500 {1,"", M_SaveSelect,'5'},
501 {1,"", M_SaveSelect,'6'},
502 {1,"", M_SaveSelect,'7'},
503 {1,"", M_SaveSelect,'8'}
504 };
505
506 oldmenu_t SaveDef =
507 {
508 load_end,
509 SaveMenu,
510 M_DrawSave,
511 80,54,
512 0
513 };
514
515 // [RH] Most menus can now be accessed directly
516 // through console commands.
BEGIN_COMMAND(menu_main)517 BEGIN_COMMAND (menu_main)
518 {
519 S_Sound (CHAN_INTERFACE, "switches/normbutn", 1, ATTN_NONE);
520 M_StartControlPanel ();
521 M_SetupNextMenu (&MainDef);
522 }
523 END_COMMAND (menu_main)
524
BEGIN_COMMAND(menu_help)525 BEGIN_COMMAND (menu_help)
526 {
527 // F1
528 S_Sound (CHAN_INTERFACE, "switches/normbutn", 1, ATTN_NONE);
529 M_StartControlPanel ();
530 M_ReadThis(0);
531 }
532 END_COMMAND (menu_help)
533
BEGIN_COMMAND(menu_save)534 BEGIN_COMMAND (menu_save)
535 {
536 // F2
537 S_Sound (CHAN_INTERFACE, "switches/normbutn", 1, ATTN_NONE);
538 M_StartControlPanel ();
539 M_SaveGame (0);
540 //Printf (PRINT_HIGH, "Saving is not available at this time.\n");
541 }
542 END_COMMAND (menu_save)
543
BEGIN_COMMAND(menu_load)544 BEGIN_COMMAND (menu_load)
545 {
546 // F3
547 S_Sound (CHAN_INTERFACE, "switches/normbutn", 1, ATTN_NONE);
548 M_StartControlPanel ();
549 M_LoadGame (0);
550 //Printf (PRINT_HIGH, "Loading is not available at this time.\n");
551 }
552 END_COMMAND (menu_load)
553
BEGIN_COMMAND(menu_options)554 BEGIN_COMMAND (menu_options)
555 {
556 // F4
557 S_Sound (CHAN_INTERFACE, "switches/normbutn", 1, ATTN_NONE);
558 M_StartControlPanel ();
559 M_Options(0);
560 }
561 END_COMMAND (menu_options)
562
BEGIN_COMMAND(quicksave)563 BEGIN_COMMAND (quicksave)
564 {
565 // F6
566 S_Sound (CHAN_INTERFACE, "switches/normbutn", 1, ATTN_NONE);
567 M_StartControlPanel ();
568 M_QuickSave ();
569 //Printf (PRINT_HIGH, "Saving is not available at this time.\n");
570 }
571 END_COMMAND (quicksave)
572
BEGIN_COMMAND(menu_endgame)573 BEGIN_COMMAND (menu_endgame)
574 { // F7
575 S_Sound (CHAN_INTERFACE, "switches/normbutn", 1, ATTN_NONE);
576 M_StartControlPanel ();
577 M_EndGame(0);
578 }
579 END_COMMAND (menu_endgame)
580
BEGIN_COMMAND(quickload)581 BEGIN_COMMAND (quickload)
582 {
583 // F9
584 S_Sound (CHAN_INTERFACE, "switches/normbutn", 1, ATTN_NONE);
585 M_StartControlPanel ();
586 M_QuickLoad ();
587 //Printf (PRINT_HIGH, "Loading is not available at this time.\n");
588 }
589 END_COMMAND (quickload)
590
BEGIN_COMMAND(menu_quit)591 BEGIN_COMMAND (menu_quit)
592 { // F10
593 S_Sound (CHAN_INTERFACE, "switches/normbutn", 1, ATTN_NONE);
594 M_StartControlPanel ();
595 M_QuitDOOM(0);
596 }
597 END_COMMAND (menu_quit)
598
BEGIN_COMMAND(menu_player)599 BEGIN_COMMAND (menu_player)
600 {
601 S_Sound (CHAN_INTERFACE, "switches/normbutn", 1, ATTN_NONE);
602 M_StartControlPanel ();
603 M_PlayerSetup(0);
604 }
END_COMMAND(menu_player)605 END_COMMAND (menu_player)
606
607 /*
608 void M_LoadSaveResponse(int choice)
609 {
610 // dummy
611 }
612
613
614 void M_LoadGame (int choice)
615 {
616 M_StartMessage("Loading/saving is not supported\n\n(Press any key to "
617 "continue)\n", M_LoadSaveResponse, false);
618 }
619 */
620
621 //
622 // M_ReadSaveStrings
623 // read the strings from the savegame files
624 //
625 void M_ReadSaveStrings(void)
626 {
627 FILE *handle;
628 int i;
629
630 for (i = 0; i < load_end; i++)
631 {
632 std::string name;
633
634 G_BuildSaveName (name, i);
635
636 handle = fopen (name.c_str(), "rb");
637 if (handle == NULL)
638 {
639 strcpy (&savegamestrings[i][0], GStrings(EMPTYSTRING));
640 LoadMenu[i].status = 0;
641 }
642 else
643 {
644 fread (&savegamestrings[i], SAVESTRINGSIZE, 1, handle);
645 fclose (handle);
646 LoadMenu[i].status = 1;
647 }
648 }
649 }
650
651
652 //
653 // M_LoadGame & Cie.
654 //
M_DrawLoad(void)655 void M_DrawLoad (void)
656 {
657 int i;
658
659 screen->DrawPatchClean ((patch_t *)W_CachePatch("M_LOADG"), 72, 28);
660 for (i = 0; i < load_end; i++)
661 {
662 M_DrawSaveLoadBorder (LoadDef.x, LoadDef.y+LINEHEIGHT*i, 24);
663 screen->DrawTextCleanMove (CR_RED, LoadDef.x, LoadDef.y+LINEHEIGHT*i, savegamestrings[i]);
664 }
665 }
666
667 //
668 // User wants to load this game
669 //
M_LoadSelect(int choice)670 void M_LoadSelect (int choice)
671 {
672 std::string name;
673
674 G_BuildSaveName (name, choice);
675 G_LoadGame ((char *)name.c_str());
676 gamestate = gamestate == GS_FULLCONSOLE ? GS_HIDECONSOLE : gamestate;
677 M_ClearMenus ();
678 if (quickSaveSlot == -2)
679 {
680 quickSaveSlot = choice;
681 }
682 }
683
684 //
685 // Selected from DOOM menu
686 // [ML] 7 Sept 08: Bringing game saving/loading in from
687 // zdoom 1.22 source, see MAINTAINERS
688 //
M_LoadGame(int choice)689 void M_LoadGame (int choice)
690 {
691 /*if (netgame)
692 {
693 M_StartMessage (LOADNET,NULL,false);
694 return;
695 }*/
696
697 M_SetupNextMenu (&LoadDef);
698 M_ReadSaveStrings ();
699 }
700
701 //
702 // M_SaveGame & Cie.
703 // [ML] 7 Sept 08: Bringing game saving/loading in from
704 // zdoom 1.22 source, see MAINTAINERS
705 //
M_DrawSave(void)706 void M_DrawSave(void)
707 {
708 int i;
709
710 screen->DrawPatchClean ((patch_t *)W_CachePatch("M_SAVEG"), 72, 28);
711 for (i = 0; i < load_end; i++)
712 {
713 M_DrawSaveLoadBorder(LoadDef.x,LoadDef.y+LINEHEIGHT*i,24);
714 screen->DrawTextCleanMove (CR_RED, LoadDef.x, LoadDef.y+LINEHEIGHT*i, savegamestrings[i]);
715 }
716
717 if (genStringEnter)
718 {
719 i = V_StringWidth(savegamestrings[saveSlot]);
720 screen->DrawTextCleanMove (CR_RED, LoadDef.x + i, LoadDef.y+LINEHEIGHT*saveSlot, "_");
721 }
722 }
723
724
725 //
726 // M_Responder calls this when user is finished
727 // [ML] 7 Sept 08: Bringing game saving/loading in from
728 // zdoom 1.22 source, see MAINTAINERS
729 //
M_DoSave(int slot)730 void M_DoSave (int slot)
731 {
732 G_SaveGame (slot,savegamestrings[slot]);
733 M_ClearMenus ();
734 // PICK QUICKSAVE SLOT YET?
735 if (quickSaveSlot == -2)
736 quickSaveSlot = slot;
737 }
738
739 //
740 // User wants to save. Start string input for M_Responder
741 // [ML] 7 Sept 08: Bringing game saving/loading in from
742 // zdoom 1.22 source, see MAINTAINERS
743 //
M_SaveSelect(int choice)744 void M_SaveSelect (int choice)
745 {
746 time_t ti = time(NULL);
747 struct tm *lt = localtime(&ti);
748
749 // we are going to be intercepting all chars
750 genStringEnter = 1;
751 genStringEnd = M_DoSave;
752 genStringLen = SAVESTRINGSIZE-1;
753
754 saveSlot = choice;
755 strcpy(saveOldString,savegamestrings[choice]);
756
757 strncpy(savegamestrings[choice], asctime(lt) + 4, 20);
758
759 saveCharIndex = strlen(savegamestrings[choice]);
760 }
761
762 /*
763 void M_SaveGame (int choice)
764 {
765 M_StartMessage("Loading/saving is not supported\n\n(Press any key to "
766 "continue)\n", M_LoadSaveResponse, false);
767 }
768 */
769
770 //
771 // Selected from DOOM menu
772 // [ML] 7 Sept 08: Bringing game saving/loading in from
773 // zdoom 1.22 source, see MAINTAINERS
774 //
M_SaveGame(int choice)775 void M_SaveGame (int choice)
776 {
777 if (multiplayer && !demoplayback)
778 {
779 M_StartMessage("you can't save while in a net game!\n\npress a key.",
780 NULL,false);
781 M_ClearMenus ();
782 return;
783 }
784
785 if (!usergame)
786 {
787 M_StartMessage(GStrings(SAVEDEAD),NULL,false);
788 M_ClearMenus ();
789 return;
790 }
791
792 if (gamestate != GS_LEVEL)
793 return;
794
795 M_SetupNextMenu(&SaveDef);
796 M_ReadSaveStrings();
797 }
798
799
800 //
801 // M_QuickSave
802 // [ML] 7 Sept 08: Bringing game saving/loading in from
803 // zdoom 1.22 source, see MAINTAINERS
804 //
805 char tempstring[80];
806
M_QuickSaveResponse(int ch)807 void M_QuickSaveResponse(int ch)
808 {
809 if (ch == 'y' || ch == KEY_JOY4)
810 {
811 M_DoSave (quickSaveSlot);
812 S_Sound (CHAN_INTERFACE, "switches/exitbutn", 1, ATTN_NONE);
813 }
814 }
815
M_QuickSave(void)816 void M_QuickSave(void)
817 {
818 if (multiplayer)
819 {
820 S_Sound (CHAN_INTERFACE, "player/male/grunt1", 1, ATTN_NONE);
821 M_ClearMenus ();
822 return;
823 }
824
825 if (!usergame)
826 {
827 S_Sound (CHAN_INTERFACE, "player/male/grunt1", 1, ATTN_NONE);
828 M_ClearMenus ();
829 return;
830 }
831
832 if (gamestate != GS_LEVEL)
833 return;
834
835 if (quickSaveSlot < 0)
836 {
837 M_StartControlPanel();
838 M_ReadSaveStrings();
839 M_SetupNextMenu(&SaveDef);
840 quickSaveSlot = -2; // means to pick a slot now
841 return;
842 }
843 sprintf (tempstring, GStrings(QSPROMPT), savegamestrings[quickSaveSlot]);
844 M_StartMessage (tempstring, M_QuickSaveResponse, true);
845 }
846
847
848
849 //
850 // M_QuickLoad
851 // [ML] 7 Sept 08: Bringing game saving/loading in from
852 // zdoom 1.22 source, see MAINTAINERS
853 //
M_QuickLoadResponse(int ch)854 void M_QuickLoadResponse(int ch)
855 {
856 if (ch == 'y' || ch == KEY_JOY4)
857 {
858 M_LoadSelect(quickSaveSlot);
859 S_Sound (CHAN_INTERFACE, "switches/exitbutn", 1, ATTN_NONE);
860 }
861 }
862
863
M_QuickLoad(void)864 void M_QuickLoad(void)
865 {
866 /*if (netgame)
867 {
868 M_StartMessage(QLOADNET,NULL,false);
869 return;
870 }*/
871
872 if (quickSaveSlot < 0)
873 {
874 M_StartControlPanel();
875 M_LoadGame (0);
876 return;
877 }
878 sprintf(tempstring,GStrings(QLPROMPT),savegamestrings[quickSaveSlot]);
879 M_StartMessage(tempstring,M_QuickLoadResponse,true);
880 }
881
882
883 //
884 // M_ReadThis
885 //
M_ReadThis(int choice)886 void M_ReadThis(int choice)
887 {
888 choice = 0;
889 drawSkull = false;
890 M_SetupNextMenu(&ReadDef1);
891 }
892
M_ReadThis2(int choice)893 void M_ReadThis2(int choice)
894 {
895 choice = 0;
896 drawSkull = false;
897 M_SetupNextMenu(&ReadDef2);
898 }
899
M_ReadThis3(int choice)900 void M_ReadThis3(int choice)
901 {
902 if (gameinfo.flags & GI_SHAREWARE) {
903 choice = 0;
904 drawSkull = false;
905 M_SetupNextMenu(&ReadDef3);
906 } else {
907 M_FinishReadThis(0);
908 }
909 }
910
M_FinishReadThis(int choice)911 void M_FinishReadThis(int choice)
912 {
913 choice = 0;
914 drawSkull = true;
915 MenuStackDepth = 0;
916 M_SetupNextMenu(&MainDef);
917 }
918
919 //
920 // Draw border for the savegame description
921 // [RH] Width of the border is variable
922 //
M_DrawSaveLoadBorder(int x,int y,int len)923 void M_DrawSaveLoadBorder (int x, int y, int len)
924 {
925 int i;
926
927 screen->DrawPatchClean (W_CachePatch ("M_LSLEFT"), x-8, y+7);
928
929 for (i = 0; i < len; i++)
930 {
931 screen->DrawPatchClean (W_CachePatch ("M_LSCNTR"), x, y+7);
932 x += 8;
933 }
934
935 screen->DrawPatchClean (W_CachePatch ("M_LSRGHT"), x, y+7);
936 }
937
938 //
939 // M_DrawMainMenu
940 //
M_DrawMainMenu(void)941 void M_DrawMainMenu (void)
942 {
943 screen->DrawPatchClean (W_CachePatch("M_DOOM"), 94, 2);
944 }
945
M_DrawNewGame(void)946 void M_DrawNewGame(void)
947 {
948 screen->DrawPatchClean ((patch_t *)W_CachePatch("M_NEWG"), 96, 14);
949 screen->DrawPatchClean ((patch_t *)W_CachePatch("M_SKILL"), 54, 38);
950 }
951
M_NewGame(int choice)952 void M_NewGame(int choice)
953 {
954 /* if (netgame && !demoplayback)
955 {
956 M_StartMessage(NEWGAME,NULL,false);
957 return;
958 }
959 */
960 if (gameinfo.flags & GI_MAPxx)
961 {
962 if (gamemode == commercial_bfg)
963 {
964 M_SetupNextMenu(&ExpDef);
965 }
966 else
967 {
968 M_SetupNextMenu(&NewDef);
969 }
970 }
971 else if (gamemode == retail_chex) // [ML] Don't show the episode selection in chex mode
972 {
973 M_SetupNextMenu(&NewDef);
974 }
975 else if (gamemode == retail || gamemode == retail_bfg)
976 {
977 EpiDef.numitems = ep_end;
978 M_SetupNextMenu(&EpiDef);
979 }
980 else
981 {
982 EpiDef.numitems = ep4;
983 M_SetupNextMenu(&EpiDef);
984 }
985
986 }
987
988
989 //
990 // M_Episode
991 //
992 int epi;
993
M_DrawEpisode(void)994 void M_DrawEpisode(void)
995 {
996 screen->DrawPatchClean ((patch_t *)W_CachePatch("M_EPISOD"), 54, 38);
997 }
998
M_VerifyNightmare(int ch)999 void M_VerifyNightmare(int ch)
1000 {
1001 if (ch != 'y' && ch != KEY_JOY4) {
1002 M_ClearMenus ();
1003 return;
1004 }
1005
1006 M_StartGame(nightmare);
1007 }
1008
M_StartGame(int choice)1009 void M_StartGame(int choice)
1010 {
1011 sv_skill.Set ((float)(choice+1));
1012 sv_gametype = GM_COOP;
1013
1014 if (gamemode == commercial_bfg) // Funky external loading madness fun time (DOOM 2 BFG)
1015 {
1016 std::string str = "nerve.wad";
1017
1018 if (epi)
1019 {
1020 // Load No Rest for The Living Externally
1021 epi = 0;
1022 G_LoadWad(str);
1023 }
1024 else
1025 {
1026 // Check for nerve.wad, if it's loaded re-load with just iwad (DOOM 2 BFG)
1027 for (unsigned int i = 2; i < wadfiles.size(); i++)
1028 {
1029 if (iequals(str, M_ExtractFileName(wadfiles[i])))
1030 {
1031 G_LoadWad(wadfiles[1]);
1032 }
1033 }
1034
1035 G_DeferedInitNew (CalcMapName (epi+1, 1));
1036 }
1037 }
1038 else
1039 {
1040 G_DeferedInitNew (CalcMapName (epi+1, 1));
1041 }
1042
1043 M_ClearMenus ();
1044 }
1045
M_ChooseSkill(int choice)1046 void M_ChooseSkill(int choice)
1047 {
1048 if (choice == nightmare)
1049 {
1050 M_StartMessage(GStrings(NIGHTMARE),M_VerifyNightmare,true);
1051 return;
1052 }
1053
1054 M_StartGame(choice);
1055 }
1056
M_Episode(int choice)1057 void M_Episode (int choice)
1058 {
1059 if ((gameinfo.flags & GI_SHAREWARE) && choice)
1060 {
1061 M_StartMessage(GStrings(SWSTRING),NULL,false);
1062 //M_SetupNextMenu(&ReadDef1);
1063 M_ClearMenus ();
1064 return;
1065 }
1066
1067 epi = choice;
1068 M_SetupNextMenu(&NewDef);
1069 }
1070
M_Expansion(int choice)1071 void M_Expansion (int choice)
1072 {
1073 epi = choice;
1074 M_SetupNextMenu(&NewDef);
1075 }
1076
1077
1078 //
1079 // Read This Menus
1080 // Had a "quick hack to fix romero bug"
1081 //
M_DrawReadThis1(void)1082 void M_DrawReadThis1 (void)
1083 {
1084 patch_t *p = W_CachePatch(gameinfo.info.infoPage[0]);
1085 screen->DrawPatchFullScreen(p);
1086 }
1087
1088 //
1089 // Read This Menus - optional second page.
1090 //
M_DrawReadThis2(void)1091 void M_DrawReadThis2 (void)
1092 {
1093 patch_t *p = W_CachePatch(gameinfo.info.infoPage[1]);
1094 screen->DrawPatchFullScreen(p);
1095 }
1096
1097 //
1098 // Read This Menus - shareware third page.
1099 //
M_DrawReadThis3(void)1100 void M_DrawReadThis3 (void)
1101 {
1102 patch_t *p = W_CachePatch(gameinfo.info.infoPage[2]);
1103 screen->DrawPatchFullScreen(p);
1104 }
1105
1106 //
1107 // M_Options
1108 //
M_DrawOptions(void)1109 void M_DrawOptions(void)
1110 {
1111 screen->DrawPatchClean (W_CachePatch("M_OPTTTL"), 108, 15);
1112 }
1113
M_Options(int choice)1114 void M_Options(int choice)
1115 {
1116 //M_SetupNextMenu(&OptionsDef);
1117 OptionsActive = M_StartOptionsMenu();
1118 }
1119
1120 //
1121 // M_EndGame
1122 //
M_EndGameResponse(int ch)1123 void M_EndGameResponse(int ch)
1124 {
1125 if ((!isascii(ch) || toupper(ch) != 'Y') && ch != KEY_JOY4 ) {
1126 M_ClearMenus ();
1127 return;
1128 }
1129
1130 currentMenu->lastOn = itemOn;
1131 M_ClearMenus ();
1132 D_StartTitle ();
1133 CL_QuitNetGame();
1134 }
1135
M_EndGame(int choice)1136 void M_EndGame(int choice)
1137 {
1138 choice = 0;
1139 if (!usergame)
1140 {
1141 S_Sound (CHAN_INTERFACE, "player/male/grunt1", 1, ATTN_NONE);
1142 return;
1143 }
1144
1145 M_StartMessage(GStrings(multiplayer ? NETEND : ENDGAME), M_EndGameResponse, true);
1146 }
1147
1148 //
1149 // M_QuitDOOM
1150 //
1151
1152 void STACK_ARGS call_terms (void);
1153
M_QuitResponse(int ch)1154 void M_QuitResponse(int ch)
1155 {
1156 if ((!isascii(ch) || toupper(ch) != 'Y') && ch != KEY_JOY4 ) {
1157 M_ClearMenus ();
1158 return;
1159 }
1160
1161 if (!multiplayer)
1162 {
1163 if (gameinfo.quitSounds)
1164 {
1165 S_Sound(CHAN_INTERFACE,
1166 gameinfo.quitSounds[(gametic>>2)&7], 1, ATTN_NONE);
1167 I_WaitVBL (105);
1168 }
1169 }
1170
1171 call_terms();
1172
1173 exit(EXIT_SUCCESS);
1174 }
1175
M_QuitDOOM(int choice)1176 void M_QuitDOOM (int choice)
1177 {
1178 // We pick index 0 which is language sensitive,
1179 // or one at random, between 1 and maximum number.
1180 sprintf (endstring, "%s\n\n%s",
1181 GStrings(QUITMSG + (gametic % NUM_QUITMESSAGES)), GStrings(DOSY));
1182
1183 M_StartMessage(endstring,M_QuitResponse,true);
1184 }
1185
1186
1187
1188
1189 // -----------------------------------------------------
1190 // Player Setup Menu code
1191 // -----------------------------------------------------
1192
1193 void M_DrawSlider (int x, int y, float min, float max, float cur);
1194
1195 static const char *genders[3] = { "male", "female", "cyborg" };
1196 static state_t *PlayerState;
1197 static int PlayerTics;
1198
1199 EXTERN_CVAR (cl_name)
EXTERN_CVAR(cl_team)1200 EXTERN_CVAR (cl_team)
1201 EXTERN_CVAR (cl_color)
1202 EXTERN_CVAR (cl_gender)
1203 EXTERN_CVAR (cl_autoaim)
1204
1205 void M_PlayerSetup (int choice)
1206 {
1207 choice = 0;
1208 strcpy (savegamestrings[0], cl_name.cstring());
1209 // strcpy (savegamestrings[1], team.cstring()); if (t = 1) // [Toke - Teams]
1210 //M_DemoNoPlay = true;
1211 //if (demoplayback)
1212 // G_CheckDemoStatus ();
1213 M_SetupNextMenu (&PSetupDef);
1214 PlayerState = &states[mobjinfo[MT_PLAYER].seestate];
1215 PlayerTics = PlayerState->tics;
1216 if (FireScreen == NULL)
1217 FireScreen = I_AllocateScreen (72, 72+5, 8);
1218
1219 // [Nes] Intialize the player preview color.
1220 R_BuildPlayerTranslation (0, V_GetColorFromString (NULL, cl_color.cstring()));
1221
1222 if (consoleplayer().ingame())
1223 {
1224 R_CopyTranslationRGB (0, consoleplayer_id);
1225 }
1226 }
1227
M_PlayerSetupTicker(void)1228 static void M_PlayerSetupTicker (void)
1229 {
1230 // Based on code in f_finale.c
1231 if (--PlayerTics > 0)
1232 return;
1233
1234 if (PlayerState->tics == -1 || PlayerState->nextstate == S_NULL) {
1235 PlayerState = &states[mobjinfo[MT_PLAYER].seestate];
1236 } else {
1237 PlayerState = &states[PlayerState->nextstate];
1238 }
1239 PlayerTics = PlayerState->tics;
1240 }
1241
1242 template<typename pixel_t>
1243 static forceinline pixel_t R_FirePixel(const byte c);
1244
1245 template<>
R_FirePixel(const byte c)1246 forceinline byte R_FirePixel<byte>(const byte c)
1247 {
1248 return FireRemap[c];
1249 }
1250
1251 template<>
R_FirePixel(const byte c)1252 forceinline argb_t R_FirePixel<argb_t>(const byte c)
1253 {
1254 return MAKERGB(c, 0, 0);
1255 }
1256
1257 template<int xscale, typename pixel_t>
R_RenderFire(int x,int y)1258 static forceinline void R_RenderFire(int x, int y)
1259 {
1260 int pitch = screen->pitch / sizeof(pixel_t);
1261
1262 for (int b = 0; b < FireScreen->height; b++)
1263 {
1264 pixel_t *to = (pixel_t *)(screen->buffer + y * screen->pitch + x * sizeof(pixel_t));
1265 byte *from = FireScreen->buffer + b * FireScreen->pitch;
1266 y += CleanYfac;
1267
1268 for (int a = 0; a < FireScreen->width; a++, to += xscale, from++)
1269 {
1270 int c;
1271 for (c = CleanYfac; c; c--)
1272 {
1273 for (int i = 0; i < xscale; ++i)
1274 {
1275 *(to + pitch*c + i) = R_FirePixel<pixel_t>(*from);
1276 }
1277 }
1278 }
1279 }
1280 }
1281
M_PlayerSetupDrawer(void)1282 static void M_PlayerSetupDrawer (void)
1283 {
1284 int x1,x2,y1,y2;
1285
1286 x1 = (screen->width / 2)-(160*CleanXfac);
1287 y1 = (screen->height / 2)-(100*CleanYfac);
1288
1289 x2 = (screen->width / 2)+(160*CleanXfac);
1290 y2 = (screen->height / 2)+(100*CleanYfac);
1291
1292 // Background effect
1293 OdamexEffect(x1,y1,x2,y2);
1294
1295 // Draw title
1296 {
1297 patch_t *patch = W_CachePatch ("M_PSTTL");
1298 screen->DrawPatchClean (patch, 160-patch->width()/2, 10);
1299
1300 /*screen->DrawPatchClean (patch,
1301 160 - (patch->width() >> 1),
1302 PSetupDef.y - (patch->height() * 3));*/
1303 }
1304
1305 // Draw player name box
1306 screen->DrawTextCleanMove (CR_RED, PSetupDef.x, PSetupDef.y, "Name");
1307 M_DrawSaveLoadBorder (PSetupDef.x + 56, PSetupDef.y, MAXPLAYERNAME+1);
1308 screen->DrawTextCleanMove (CR_RED, PSetupDef.x + 56, PSetupDef.y, savegamestrings[0]);
1309
1310 // Draw player team box
1311 // screen->DrawTextCleanMove (CR_RED, PSetupDef.x, PSetupDef.y + LINEHEIGHT, "Team"); if (t = 1) // [Toke - Teams]
1312 // screen->DrawTextCleanMove (CR_RED, PSetupDef.x + 56, PSetupDef.y + LINEHEIGHT, savegamestrings[1]);
1313
1314
1315 // Draw cursor for either of the above
1316 if (genStringEnter)
1317 screen->DrawTextCleanMove (CR_RED, PSetupDef.x + V_StringWidth(savegamestrings[saveSlot]) + 56, PSetupDef.y + ((saveSlot == 0) ? 0 : LINEHEIGHT), "_");
1318
1319 // Draw player character
1320 {
1321 int x = 320 - 88 - 32, y = PSetupDef.y + LINEHEIGHT*3 - 14;
1322
1323 x = (x-160)*CleanXfac+(screen->width>>1);
1324 y = (y-100)*CleanYfac+(screen->height>>1);
1325 if (!FireScreen)
1326 {
1327 screen->Clear (x, y, x + 72 * CleanXfac, y + 72 * CleanYfac, 34);
1328 }
1329 else
1330 {
1331 // [RH] The following fire code is based on the PTC fire demo
1332 int a, b;
1333 byte *from;
1334 int width, height, pitch;
1335
1336 FireScreen->Lock ();
1337
1338 width = FireScreen->width;
1339 height = FireScreen->height;
1340 pitch = FireScreen->pitch;
1341
1342 from = FireScreen->buffer + (height - 3) * pitch;
1343 for (a = 0; a < width; a++, from++)
1344 {
1345 *from = *(from + (pitch << 1)) = M_Random();
1346 }
1347
1348 from = FireScreen->buffer;
1349 for (b = 0; b < FireScreen->height - 4; b += 2)
1350 {
1351 byte *pixel = from;
1352
1353 // special case: first pixel on line
1354 byte *p = pixel + (pitch << 1);
1355 unsigned int top = *p + *(p + width - 1) + *(p + 1);
1356 unsigned int bottom = *(pixel + (pitch << 2));
1357 unsigned int c1 = (top + bottom) >> 2;
1358 if (c1 > 1) c1--;
1359 *pixel = c1;
1360 *(pixel + pitch) = (c1 + bottom) >> 1;
1361 pixel++;
1362
1363 // main line loop
1364 for (a = 1; a < width-1; a++)
1365 {
1366 // sum top pixels
1367 p = pixel + (pitch << 1);
1368 top = *p + *(p - 1) + *(p + 1);
1369
1370 // bottom pixel
1371 bottom = *(pixel + (pitch << 2));
1372
1373 // combine pixels
1374 c1 = (top + bottom) >> 2;
1375 if (c1 > 1) c1--;
1376
1377 // store pixels
1378 *pixel = c1;
1379 *(pixel + pitch) = (c1 + bottom) >> 1; // interpolate
1380
1381 // next pixel
1382 pixel++;
1383 }
1384
1385 // special case: last pixel on line
1386 p = pixel + (pitch << 1);
1387 top = *p + *(p - 1) + *(p - width + 1);
1388 bottom = *(pixel + (pitch << 2));
1389 c1 = (top + bottom) >> 2;
1390 if (c1 > 1) c1--;
1391 *pixel = c1;
1392 *(pixel + pitch) = (c1 + bottom) >> 1;
1393
1394 // next line
1395 from += pitch << 1;
1396 }
1397
1398 y--;
1399 if (screen->is8bit())
1400 {
1401 // 8bpp rendering:
1402 if (CleanXfac == 1) R_RenderFire<1, byte>(x, y);
1403 else if (CleanXfac == 2) R_RenderFire<2, byte>(x, y);
1404 else if (CleanXfac == 3) R_RenderFire<3, byte>(x, y);
1405 else if (CleanXfac == 4) R_RenderFire<4, byte>(x, y);
1406 else if (CleanXfac == 5) R_RenderFire<5, byte>(x, y);
1407 }
1408 else
1409 {
1410 // 32bpp rendering:
1411 if (CleanXfac == 1) R_RenderFire<1, DWORD>(x, y);
1412 else if (CleanXfac == 2) R_RenderFire<2, DWORD>(x, y);
1413 else if (CleanXfac == 3) R_RenderFire<3, DWORD>(x, y);
1414 else if (CleanXfac == 4) R_RenderFire<4, DWORD>(x, y);
1415 else if (CleanXfac == 5) R_RenderFire<5, DWORD>(x, y);
1416 }
1417 FireScreen->Unlock ();
1418 }
1419 }
1420 {
1421 int spritenum = states[mobjinfo[MT_PLAYER].spawnstate].sprite;
1422 spriteframe_t* sprframe = &sprites[spritenum].spriteframes[PlayerState->frame & FF_FRAMEMASK];
1423
1424 // [Nes] Color of player preview uses the unused translation table (player 0), instead
1425 // of the table of the current player color. (Which is different in single, demo, and team)
1426 V_ColorMap = translationref_t(translationtables, 0);
1427 //V_ColorMap = translationtables + consoleplayer().id * 256;
1428
1429 screen->DrawTranslatedPatchClean (W_CachePatch (sprframe->lump[0]),
1430 320 - 52 - 32, PSetupDef.y + LINEHEIGHT*3 + 46);
1431 }
1432
1433 // Draw box surrounding fire and player:
1434 screen->DrawPatchClean (W_CachePatch ("M_PBOX"),
1435 320 - 88 - 32 + 36, PSetupDef.y + LINEHEIGHT*3 + 22);
1436
1437 // Draw player color sliders
1438 //V_DrawTextCleanMove (CR_GREY, PSetupDef.x, PSetupDef.y + LINEHEIGHT, "Color");
1439
1440 screen->DrawTextCleanMove (CR_RED, PSetupDef.x, PSetupDef.y + LINEHEIGHT*2, "Red");
1441 screen->DrawTextCleanMove (CR_RED, PSetupDef.x, PSetupDef.y + LINEHEIGHT*3, "Green");
1442 screen->DrawTextCleanMove (CR_RED, PSetupDef.x, PSetupDef.y + LINEHEIGHT*4, "Blue");
1443
1444 {
1445 int x = V_StringWidth ("Green") + 8 + PSetupDef.x;
1446 int color = V_GetColorFromString(NULL, cl_color.cstring());
1447
1448 M_DrawSlider (x, PSetupDef.y + LINEHEIGHT*2, 0.0f, 255.0f, RPART(color));
1449 M_DrawSlider (x, PSetupDef.y + LINEHEIGHT*3, 0.0f, 255.0f, GPART(color));
1450 M_DrawSlider (x, PSetupDef.y + LINEHEIGHT*4, 0.0f, 255.0f, BPART(color));
1451 }
1452
1453 // Draw team setting
1454 {
1455 team_t team = D_TeamByName(cl_team.cstring());
1456 int x = V_StringWidth ("Prefered Team") + 8 + PSetupDef.x;
1457 screen->DrawTextCleanMove (CR_RED, PSetupDef.x, PSetupDef.y + LINEHEIGHT, "Prefered Team");
1458 screen->DrawTextCleanMove (CR_GREY, x, PSetupDef.y + LINEHEIGHT, team == TEAM_NONE ? "NONE" : team_names[team]);
1459 }
1460
1461 // Draw gender setting
1462 {
1463 gender_t gender = D_GenderByName(cl_gender.cstring());
1464 int x = V_StringWidth ("Gender") + 8 + PSetupDef.x;
1465 screen->DrawTextCleanMove (CR_RED, PSetupDef.x, PSetupDef.y + LINEHEIGHT*5, "Gender");
1466 screen->DrawTextCleanMove (CR_GREY, x, PSetupDef.y + LINEHEIGHT*5, genders[gender]);
1467 }
1468
1469 // Draw autoaim setting
1470 {
1471 int x = V_StringWidth ("Autoaim") + 8 + PSetupDef.x;
1472 float aim = cl_autoaim;
1473
1474 screen->DrawTextCleanMove (CR_RED, PSetupDef.x, PSetupDef.y + LINEHEIGHT*6, "Autoaim");
1475 screen->DrawTextCleanMove (CR_GREY, x, PSetupDef.y + LINEHEIGHT*6,
1476 aim == 0 ? "Never" :
1477 aim <= 0.25 ? "Very Low" :
1478 aim <= 0.5 ? "Low" :
1479 aim <= 1 ? "Medium" :
1480 aim <= 2 ? "High" :
1481 aim <= 3 ? "Very High" : "Always");
1482 }
1483 }
1484
M_ChangeTeam(int choice)1485 void M_ChangeTeam (int choice) // [Toke - Teams]
1486 {
1487 team_t team = D_TeamByName(cl_team.cstring());
1488
1489 if(choice)
1490 {
1491 switch(team)
1492 {
1493 case TEAM_NONE: team = TEAM_BLUE; break;
1494 case TEAM_BLUE: team = TEAM_RED; break;
1495 case TEAM_RED: team = TEAM_BLUE; break;
1496 default:
1497 team = TEAM_NONE; break;
1498 }
1499 }
1500 else
1501 {
1502 switch(team)
1503 {
1504 case TEAM_NONE: team = TEAM_RED; break;
1505 case TEAM_RED: team = TEAM_BLUE; break;
1506 default:
1507 case TEAM_BLUE: team = TEAM_NONE; break;
1508 }
1509 }
1510
1511 cl_team = (team == TEAM_NONE) ? "" : team_names[team];
1512 }
1513
M_ChangeGender(int choice)1514 static void M_ChangeGender (int choice)
1515 {
1516 int gender = D_GenderByName(cl_gender.cstring());
1517
1518 if (!choice)
1519 gender = (gender == 0) ? 2 : gender - 1;
1520 else
1521 gender = (gender == 2) ? 0 : gender + 1;
1522
1523 cl_gender = genders[gender];
1524 }
1525
M_ChangeAutoAim(int choice)1526 static void M_ChangeAutoAim (int choice)
1527 {
1528 static const float ranges[] = { 0, 0.25, 0.5, 1, 2, 3, 5000 };
1529 float aim = cl_autoaim;
1530 int i;
1531
1532 if (!choice) {
1533 // Select a lower autoaim
1534
1535 for (i = 6; i >= 1; i--) {
1536 if (aim >= ranges[i]) {
1537 aim = ranges[i - 1];
1538 break;
1539 }
1540 }
1541 } else {
1542 // Select a higher autoaim
1543
1544 for (i = 5; i >= 0; i--) {
1545 if (aim >= ranges[i]) {
1546 aim = ranges[i + 1];
1547 break;
1548 }
1549 }
1550 }
1551
1552 cl_autoaim.Set (aim);
1553 }
1554
M_EditPlayerName(int choice)1555 static void M_EditPlayerName (int choice)
1556 {
1557 // we are going to be intercepting all chars
1558 genStringEnter = 1;
1559 genStringEnd = M_PlayerNameChanged;
1560 genStringLen = MAXPLAYERNAME;
1561
1562 saveSlot = 0;
1563 strcpy(saveOldString,savegamestrings[0]);
1564 if (!strcmp(savegamestrings[0],GStrings(EMPTYSTRING)))
1565 savegamestrings[0][0] = 0;
1566 saveCharIndex = strlen(savegamestrings[0]);
1567 }
1568
M_PlayerNameChanged(int choice)1569 static void M_PlayerNameChanged (int choice)
1570 {
1571 char command[SAVESTRINGSIZE+8];
1572
1573 sprintf (command, "cl_name \"%s\"", savegamestrings[0]);
1574 AddCommandString (command);
1575 }
1576 /*
1577 static void M_PlayerTeamChanged (int choice)
1578 {
1579 char command[SAVESTRINGSIZE+8];
1580
1581 sprintf (command, "cl_team \"%s\"", savegamestrings[1]);
1582 AddCommandString (command);
1583 }
1584 */
1585
SendNewColor(int red,int green,int blue)1586 static void SendNewColor (int red, int green, int blue)
1587 {
1588 char command[24];
1589
1590 sprintf (command, "cl_color \"%02x %02x %02x\"", red, green, blue);
1591 AddCommandString (command);
1592
1593 // [Nes] Change the player preview color.
1594 R_BuildPlayerTranslation (0, V_GetColorFromString (NULL, cl_color.cstring()));
1595
1596 if (consoleplayer().ingame())
1597 {
1598 R_CopyTranslationRGB (0, consoleplayer_id);
1599 }
1600 }
1601
M_SlidePlayerRed(int choice)1602 static void M_SlidePlayerRed (int choice)
1603 {
1604 int color = V_GetColorFromString(NULL, cl_color.cstring());
1605 int red = RPART(color);
1606 int accel = 0;
1607
1608 if(repeatCount >= 10)
1609 accel = 5;
1610
1611 if (choice == 0) {
1612 red -= 1 + accel;
1613 if (red < 0)
1614 red = 0;
1615 } else {
1616 red += 1 + accel;
1617 if (red > 255)
1618 red = 255;
1619 }
1620
1621 SendNewColor (red, GPART(color), BPART(color));
1622 }
1623
M_SlidePlayerGreen(int choice)1624 static void M_SlidePlayerGreen (int choice)
1625 {
1626 int color = V_GetColorFromString(NULL, cl_color.cstring());
1627 int green = GPART(color);
1628 int accel = 0;
1629
1630 if(repeatCount >= 10)
1631 accel = 5;
1632
1633 if (choice == 0) {
1634 green -= 1 + accel;
1635 if (green < 0)
1636 green = 0;
1637 } else {
1638 green += 1 + accel;
1639 if (green > 255)
1640 green = 255;
1641 }
1642
1643 SendNewColor (RPART(color), green, BPART(color));
1644 }
1645
M_SlidePlayerBlue(int choice)1646 static void M_SlidePlayerBlue (int choice)
1647 {
1648 int color = V_GetColorFromString(NULL, cl_color.cstring());
1649 int blue = BPART(color);
1650 int accel = 0;
1651
1652 if(repeatCount >= 10)
1653 accel = 5;
1654
1655 if (choice == 0) {
1656 blue -= 1 + accel;
1657 if (blue < 0)
1658 blue = 0;
1659 } else {
1660 blue += 1 + accel;
1661 if (blue > 255)
1662 blue = 255;
1663 }
1664
1665 SendNewColor (RPART(color), GPART(color), blue);
1666 }
1667
1668
1669 //
1670 // Menu Functions
1671 //
M_DrawEmptyCell(oldmenu_t * menu,int item)1672 void M_DrawEmptyCell (oldmenu_t *menu, int item)
1673 {
1674 screen->DrawPatchClean (W_CachePatch("M_CELL1"),
1675 menu->x - 10, menu->y+item*LINEHEIGHT - 1);
1676 }
1677
M_DrawSelCell(oldmenu_t * menu,int item)1678 void M_DrawSelCell (oldmenu_t *menu, int item)
1679 {
1680 screen->DrawPatchClean (W_CachePatch("M_CELL2"),
1681 menu->x - 10, menu->y+item*LINEHEIGHT - 1);
1682 }
1683
1684
M_StartMessage(const char * string,void (* routine)(int),bool input)1685 void M_StartMessage (const char *string, void (*routine)(int), bool input)
1686 {
1687 messageLastMenuActive = menuactive;
1688 messageToPrint = 1;
1689 messageString = string;
1690 messageRoutine = routine;
1691 messageNeedsInput = input;
1692 menuactive = true;
1693 return;
1694 }
1695
1696
1697
M_StopMessage(void)1698 void M_StopMessage (void)
1699 {
1700 menuactive = messageLastMenuActive;
1701 messageToPrint = 0;
1702 }
1703
1704
1705 //
1706 // Find string height from hu_font chars
1707 //
M_StringHeight(char * string)1708 int M_StringHeight(char* string)
1709 {
1710 int h;
1711 int height = hu_font[0]->height();
1712
1713 h = height;
1714 while (*string)
1715 if ((*string++) == '\n')
1716 h += height;
1717
1718 return h;
1719 }
1720
1721
1722
1723 //
1724 // CONTROL PANEL
1725 //
1726
1727 //
1728 // M_Responder
1729 //
M_Responder(event_t * ev)1730 bool M_Responder (event_t* ev)
1731 {
1732 int ch, ch2;
1733 int i;
1734 const char *cmd;
1735
1736 ch = ch2 = -1;
1737
1738 // eat mouse events
1739 if(menuactive)
1740 {
1741 if(ev->type == ev_mouse)
1742 return true;
1743 else if(ev->type == ev_joystick)
1744 {
1745 if(OptionsActive)
1746 M_OptResponder (ev);
1747 // Eat joystick events for now -- Hyper_Eye
1748 return true;
1749 }
1750 }
1751
1752 if (ev->type == ev_keyup)
1753 {
1754 if(repeatKey == ev->data1)
1755 {
1756 repeatKey = 0;
1757 repeatCount = 0;
1758 }
1759 }
1760
1761 if (ev->type == ev_keydown)
1762 {
1763 ch = ev->data1; // scancode
1764 ch2 = ev->data2; // ASCII
1765 }
1766
1767 if (ch == -1 || headsupactive)
1768 return false;
1769
1770 if (menuactive && OptionsActive) {
1771 M_OptResponder (ev);
1772 return true;
1773 }
1774
1775 // Handle Repeat
1776 switch(ch)
1777 {
1778 case KEY_HAT4:
1779 case KEY_LEFTARROW:
1780 case KEY_HAT2:
1781 case KEY_RIGHTARROW:
1782 case KEYP_4:
1783 case KEYP_6:
1784 if(repeatKey == ch)
1785 repeatCount++;
1786 else
1787 {
1788 repeatKey = ch;
1789 repeatCount = 0;
1790 }
1791 break;
1792 default:
1793 break;
1794 }
1795
1796
1797 cmd = C_GetBinding (ch);
1798
1799 // Save Game string input
1800 // [RH] and Player Name string input
1801 if (genStringEnter)
1802 {
1803 switch(ch)
1804 {
1805 case KEY_BACKSPACE:
1806 if (saveCharIndex > 0)
1807 {
1808 saveCharIndex--;
1809 savegamestrings[saveSlot][saveCharIndex] = 0;
1810 }
1811 break;
1812
1813 case KEY_JOY2:
1814 case KEY_ESCAPE:
1815 genStringEnter = 0;
1816 M_ClearMenus ();
1817 strcpy(&savegamestrings[saveSlot][0],saveOldString);
1818 break;
1819
1820 case KEY_JOY1:
1821 case KEY_ENTER:
1822 case KEYP_ENTER:
1823 genStringEnter = 0;
1824 M_ClearMenus ();
1825 if (savegamestrings[saveSlot][0])
1826 genStringEnd(saveSlot); // [RH] Function to call when enter is pressed
1827 break;
1828
1829 default:
1830 ch = ev->data3; // [RH] Use user keymap
1831 if (ch >= 32 && ch <= 127 &&
1832 saveCharIndex < genStringLen &&
1833 V_StringWidth(savegamestrings[saveSlot]) <
1834 (genStringLen-1)*8)
1835 {
1836 savegamestrings[saveSlot][saveCharIndex++] = ch;
1837 savegamestrings[saveSlot][saveCharIndex] = 0;
1838 }
1839 break;
1840 }
1841 return true;
1842 }
1843
1844 // Take care of any messages that need input
1845 if (messageToPrint)
1846 {
1847 if (messageNeedsInput &&
1848 (!(ch2 == ' ' || ch == KEY_ESCAPE || ch == KEY_JOY2 || ch == KEY_JOY4 ||
1849 (isascii(ch2) && (toupper(ch2) == 'N' || toupper(ch2) == 'Y')))))
1850 return true;
1851
1852 menuactive = messageLastMenuActive;
1853 messageToPrint = 0;
1854 if (messageRoutine)
1855 messageRoutine(ch2);
1856
1857 menuactive = false;
1858 S_Sound (CHAN_INTERFACE, "switches/exitbutn", 1, ATTN_NONE);
1859 return true;
1860 }
1861
1862 // [RH] F-Keys are now just normal keys that can be bound,
1863 // so they aren't checked here anymore.
1864
1865 // If devparm is set, pressing F1 always takes a screenshot no matter
1866 // what it's bound to. (for those who don't bother to read the docs)
1867 if (devparm && ch == KEY_F1) {
1868 G_ScreenShot (NULL);
1869 return true;
1870 }
1871
1872 // Pop-up menu?
1873 if (!menuactive)
1874 {
1875 // [ML] This is a regular binding now too!
1876 #ifdef _XBOX
1877 if (ch == KEY_ESCAPE || ch == KEY_JOY9)
1878 #else
1879 if (ch == KEY_ESCAPE)
1880 #endif
1881 {
1882 AddCommandString("menu_main");
1883 return true;
1884 }
1885 return false;
1886 }
1887
1888 if(cmd)
1889 {
1890 // Respond to the main menu binding
1891 if(!strcmp(cmd, "menu_main"))
1892 {
1893 M_ClearMenus();
1894 return true;
1895 }
1896 }
1897
1898 // Keys usable within menu
1899 switch (ch)
1900 {
1901 case KEY_HAT3:
1902 case KEY_DOWNARROW:
1903 case KEYP_2:
1904 do
1905 {
1906 if (itemOn+1 > currentMenu->numitems-1)
1907 itemOn = 0;
1908 else
1909 itemOn++;
1910 S_Sound (CHAN_INTERFACE, "plats/pt1_stop", 1, ATTN_NONE);
1911 } while(currentMenu->menuitems[itemOn].status==-1);
1912 return true;
1913
1914 case KEY_HAT1:
1915 case KEY_UPARROW:
1916 case KEYP_8:
1917 do
1918 {
1919 if (!itemOn)
1920 itemOn = currentMenu->numitems-1;
1921 else
1922 itemOn--;
1923 S_Sound (CHAN_INTERFACE, "plats/pt1_stop", 1, ATTN_NONE);
1924 } while(currentMenu->menuitems[itemOn].status==-1);
1925 return true;
1926
1927 case KEY_HAT4:
1928 case KEY_LEFTARROW:
1929 case KEYP_4:
1930 if (currentMenu->menuitems[itemOn].routine &&
1931 currentMenu->menuitems[itemOn].status == 2)
1932 {
1933 S_Sound (CHAN_INTERFACE, "plats/pt1_mid", 1, ATTN_NONE);
1934 currentMenu->menuitems[itemOn].routine(0);
1935 }
1936 return true;
1937
1938 case KEY_HAT2:
1939 case KEY_RIGHTARROW:
1940 case KEYP_6:
1941 if (currentMenu->menuitems[itemOn].routine &&
1942 currentMenu->menuitems[itemOn].status == 2)
1943 {
1944 S_Sound (CHAN_INTERFACE, "plats/pt1_mid", 1, ATTN_NONE);
1945 currentMenu->menuitems[itemOn].routine(1);
1946 }
1947 return true;
1948
1949 case KEY_JOY1:
1950 case KEY_ENTER:
1951 case KEYP_ENTER:
1952 if (currentMenu->menuitems[itemOn].routine &&
1953 currentMenu->menuitems[itemOn].status)
1954 {
1955 currentMenu->lastOn = itemOn;
1956 if (currentMenu->menuitems[itemOn].status == 2)
1957 {
1958 currentMenu->menuitems[itemOn].routine(1); // right arrow
1959 S_Sound (CHAN_INTERFACE, "plats/pt1_mid", 1, ATTN_NONE);
1960 }
1961 else
1962 {
1963 currentMenu->menuitems[itemOn].routine(itemOn);
1964 S_Sound (CHAN_INTERFACE, "weapons/pistol", 1, ATTN_NONE);
1965 }
1966 }
1967 return true;
1968
1969 // [RH] Escape now moves back one menu instead of
1970 // quitting the menu system. Thus, backspace
1971 // is now ignored.
1972 case KEY_JOY2:
1973 case KEY_ESCAPE:
1974 currentMenu->lastOn = itemOn;
1975 M_PopMenuStack ();
1976 return true;
1977
1978 default:
1979 if (ch2 && (ch < KEY_JOY1)) {
1980 for (i = itemOn+1;i < currentMenu->numitems;i++)
1981 if (currentMenu->menuitems[i].alphaKey == toupper(ch2))
1982 {
1983 itemOn = i;
1984 S_Sound (CHAN_INTERFACE, "plats/pt1_stop", 1, ATTN_NONE);
1985 return true;
1986 }
1987 for (i = 0;i <= itemOn;i++)
1988 if (currentMenu->menuitems[i].alphaKey == toupper(ch2))
1989 {
1990 itemOn = i;
1991 S_Sound (CHAN_INTERFACE, "plats/pt1_stop", 1, ATTN_NONE);
1992 return true;
1993 }
1994 }
1995 break;
1996
1997 }
1998
1999 // [RH] Menu now eats all keydown events while active
2000 if (ev->type == ev_keydown)
2001 return true;
2002 else
2003 return false;
2004 }
2005
2006
2007
2008 //
2009 // M_StartControlPanel
2010 //
M_StartControlPanel(void)2011 void M_StartControlPanel (void)
2012 {
2013 // intro might call this repeatedly
2014 if (menuactive)
2015 return;
2016
2017 drawSkull = true;
2018 MenuStackDepth = 0;
2019 menuactive = 1;
2020 currentMenu = &MainDef;
2021 itemOn = currentMenu->lastOn;
2022 OptionsActive = false; // [RH] Make sure none of the options menus appear.
2023 I_EnableKeyRepeat();
2024 }
2025
2026
2027 //
2028 // M_Drawer
2029 // Called after the view has been rendered,
2030 // but before it has been blitted.
2031 //
M_Drawer(void)2032 void M_Drawer (void)
2033 {
2034 int i, x, y, max;
2035
2036 st_firsttime = true;
2037 //screen->Dim (); // denis - removed, see bug 388
2038
2039 // Horiz. & Vertically center string and print it.
2040 if (messageToPrint)
2041 {
2042 brokenlines_t *lines = V_BreakLines (320, messageString);
2043 y = 100;
2044
2045 for (i = 0; lines[i].width != -1; i++)
2046 y -= hu_font[0]->height() / 2;
2047
2048 for (i = 0; lines[i].width != -1; i++)
2049 {
2050 screen->DrawTextCleanMove (CR_RED, 160 - lines[i].width/2, y, lines[i].string);
2051 y += hu_font[0]->height();
2052 }
2053
2054 V_FreeBrokenLines (lines);
2055 }
2056 else if (menuactive)
2057 {
2058 if (OptionsActive)
2059 {
2060 M_OptDrawer ();
2061 }
2062 else
2063 {
2064 if (currentMenu->routine)
2065 currentMenu->routine(); // call Draw routine
2066
2067 // DRAW MENU
2068 x = currentMenu->x;
2069 y = currentMenu->y;
2070 max = currentMenu->numitems;
2071
2072 for (i = 0; i < max; i++)
2073 {
2074 if (currentMenu->menuitems[i].name[0])
2075 screen->DrawPatchClean (W_CachePatch(currentMenu->menuitems[i].name), x, y);
2076 y += LINEHEIGHT;
2077 }
2078
2079
2080 // DRAW SKULL
2081 if (drawSkull)
2082 {
2083 screen->DrawPatchClean (W_CachePatch(skullName[whichSkull]),
2084 x + SKULLXOFF, currentMenu->y - 5 + itemOn*LINEHEIGHT);
2085 }
2086 }
2087 }
2088 }
2089
2090
2091 //
2092 // M_ClearMenus
2093 //
M_ClearMenus(void)2094 void M_ClearMenus (void)
2095 {
2096 if (FireScreen)
2097 {
2098 I_FreeScreen(FireScreen);
2099 FireScreen = NULL;
2100 }
2101 MenuStackDepth = 0;
2102 menuactive = false;
2103 drawSkull = true;
2104 M_DemoNoPlay = false;
2105 if (gamestate != GS_FULLCONSOLE)
2106 {
2107 I_DisableKeyRepeat();
2108 }
2109 }
2110
2111
2112
2113
2114 //
2115 // M_SetupNextMenu
2116 //
M_SetupNextMenu(oldmenu_t * menudef)2117 void M_SetupNextMenu (oldmenu_t *menudef)
2118 {
2119 MenuStack[MenuStackDepth].menu.old = menudef;
2120 MenuStack[MenuStackDepth].isNewStyle = false;
2121 MenuStack[MenuStackDepth].drawSkull = drawSkull;
2122 MenuStackDepth++;
2123
2124 currentMenu = menudef;
2125 itemOn = currentMenu->lastOn;
2126 }
2127
2128
M_PopMenuStack(void)2129 void M_PopMenuStack (void)
2130 {
2131 M_DemoNoPlay = false;
2132 if (MenuStackDepth > 1) {
2133 MenuStackDepth -= 2;
2134 if (MenuStack[MenuStackDepth].isNewStyle) {
2135 OptionsActive = true;
2136 CurrentMenu = MenuStack[MenuStackDepth].menu.newmenu;
2137 CurrentItem = CurrentMenu->lastOn;
2138 } else {
2139 OptionsActive = false;
2140 currentMenu = MenuStack[MenuStackDepth].menu.old;
2141 itemOn = currentMenu->lastOn;
2142 }
2143 drawSkull = MenuStack[MenuStackDepth].drawSkull;
2144 MenuStackDepth++;
2145 S_Sound (CHAN_INTERFACE, "switches/normbutn", 1, ATTN_NONE);
2146 } else {
2147 M_ClearMenus ();
2148 S_Sound (CHAN_INTERFACE, "switches/exitbutn", 1, ATTN_NONE);
2149 }
2150 }
2151
2152
2153 //
2154 // M_Ticker
2155 //
M_Ticker(void)2156 void M_Ticker (void)
2157 {
2158 if (--skullAnimCounter <= 0)
2159 {
2160 whichSkull ^= 1;
2161 skullAnimCounter = 8;
2162 }
2163
2164 if (currentMenu == &PSetupDef)
2165 M_PlayerSetupTicker ();
2166 }
2167
2168
2169 //
2170 // M_Init
2171 //
EXTERN_CVAR(screenblocks)2172 EXTERN_CVAR (screenblocks)
2173
2174 void M_Init (void)
2175 {
2176 int i;
2177
2178 // [Russell] - Set this beforehand, because when you switch wads
2179 // (ie from doom to doom2 back to doom), you will have less menu items
2180 {
2181 MainDef.numitems = d1_main_end;
2182 MainDef.menuitems = DoomMainMenu;
2183 MainDef.routine = M_DrawMainMenu,
2184 MainDef.lastOn = 0;
2185 MainDef.x = 97;
2186 MainDef.y = 64;
2187 }
2188
2189 currentMenu = &MainDef;
2190 OptionsActive = false;
2191 menuactive = 0;
2192 itemOn = currentMenu->lastOn;
2193 whichSkull = 0;
2194 skullAnimCounter = 10;
2195 drawSkull = true;
2196 screenSize = (int)screenblocks - 3;
2197 messageToPrint = 0;
2198 messageString = NULL;
2199 messageLastMenuActive = menuactive;
2200
2201 if (gameinfo.flags & GI_MAPxx)
2202 {
2203 // Commercial has no "read this" entry.
2204 MainDef.numitems = d2_main_end;
2205 MainDef.menuitems = Doom2MainMenu;
2206
2207 MainDef.y += 8;
2208 }
2209
2210 M_OptInit ();
2211
2212 // [RH] Build a palette translation table for the fire
2213 palette_t *pal = GetDefaultPalette();
2214
2215 for (i = 0; i < 255; i++)
2216 FireRemap[i] = BestColor(pal->basecolors, i, 0, 0, pal->numcolors);
2217 }
2218
2219 //
2220 // M_FindCvarInMenu
2221 //
2222 // Takes an array of menu items and returns the index in the array of the
2223 // menu item containing that cvar. Returns MAXINT if not found.
2224 //
M_FindCvarInMenu(cvar_t & cvar,menuitem_t * menu,size_t length)2225 size_t M_FindCvarInMenu(cvar_t &cvar, menuitem_t *menu, size_t length)
2226 {
2227 if (menu)
2228 {
2229 for (size_t i = 0; i < length; i++)
2230 {
2231 if (menu[i].a.cvar == &cvar)
2232 return i;
2233 }
2234 }
2235
2236 return MAXINT; // indicate not found
2237 }
2238
2239
2240 VERSION_CONTROL (m_menu_cpp, "$Id: m_menu.cpp 4542 2014-02-09 17:39:42Z dr_sean $")
2241
2242
2243