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