1 /*
2 ** menu.cpp
3 ** Menu base class and global interface
4 **
5 **---------------------------------------------------------------------------
6 ** Copyright 2010 Christoph Oelckers
7 ** All rights reserved.
8 **
9 ** Redistribution and use in source and binary forms, with or without
10 ** modification, are permitted provided that the following conditions
11 ** are met:
12 **
13 ** 1. Redistributions of source code must retain the above copyright
14 **    notice, this list of conditions and the following disclaimer.
15 ** 2. Redistributions in binary form must reproduce the above copyright
16 **    notice, this list of conditions and the following disclaimer in the
17 **    documentation and/or other materials provided with the distribution.
18 ** 3. The name of the author may not be used to endorse or promote products
19 **    derived from this software without specific prior written permission.
20 **
21 ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22 ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23 ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24 ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25 ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26 ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30 ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 **---------------------------------------------------------------------------
32 **
33 */
34 
35 #include "doomdef.h"
36 #include "doomstat.h"
37 #include "c_dispatch.h"
38 #include "d_gui.h"
39 #include "d_player.h"
40 #include "g_level.h"
41 #include "c_console.h"
42 #include "c_bind.h"
43 #include "s_sound.h"
44 #include "p_tick.h"
45 #include "g_game.h"
46 #include "c_cvars.h"
47 #include "d_event.h"
48 #include "v_video.h"
49 #include "hu_stuff.h"
50 #include "gi.h"
51 #include "v_palette.h"
52 #include "i_input.h"
53 #include "gameconfigfile.h"
54 #include "gstrings.h"
55 #include "r_utility.h"
56 #include "menu/menu.h"
57 #include "textures/textures.h"
58 
59 //
60 // Todo: Move these elsewhere
61 //
62 CVAR (Float, mouse_sensitivity, 1.f, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
63 CVAR (Bool, show_messages, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
64 CVAR (Bool, show_obituaries, true, CVAR_ARCHIVE)
65 
66 
67 CVAR (Float, snd_menuvolume, 0.6f, CVAR_ARCHIVE)
68 CVAR(Int, m_use_mouse, 1, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
69 CVAR(Int, m_show_backbutton, 0, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
70 
71 DMenu *DMenu::CurrentMenu;
72 int DMenu::MenuTime;
73 
74 FGameStartup GameStartupInfo;
75 EMenuState		menuactive;
76 bool			M_DemoNoPlay;
77 FButtonStatus	MenuButtons[NUM_MKEYS];
78 int				MenuButtonTickers[NUM_MKEYS];
79 bool			MenuButtonOrigin[NUM_MKEYS];
80 int				BackbuttonTime;
81 fixed_t			BackbuttonAlpha;
82 static bool		MenuEnabled = true;
83 
84 
85 #define KEY_REPEAT_DELAY	(TICRATE*5/12)
86 #define KEY_REPEAT_RATE		(3)
87 
88 //============================================================================
89 //
90 // DMenu base class
91 //
92 //============================================================================
93 
94 IMPLEMENT_POINTY_CLASS (DMenu)
DECLARE_POINTER(mParentMenu)95 	DECLARE_POINTER(mParentMenu)
96 END_POINTERS
97 
98 DMenu::DMenu(DMenu *parent)
99 {
100 	mParentMenu = parent;
101 	mMouseCapture = false;
102 	mBackbuttonSelected = false;
103 	GC::WriteBarrier(this, parent);
104 }
105 
Responder(event_t * ev)106 bool DMenu::Responder (event_t *ev)
107 {
108 	bool res = false;
109 	if (ev->type == EV_GUI_Event)
110 	{
111 		if (ev->subtype == EV_GUI_LButtonDown)
112 		{
113 			res = MouseEventBack(MOUSE_Click, ev->data1, ev->data2);
114 			// make the menu's mouse handler believe that the current coordinate is outside the valid range
115 			if (res) ev->data2 = -1;
116 			res |= MouseEvent(MOUSE_Click, ev->data1, ev->data2);
117 			if (res)
118 			{
119 				SetCapture();
120 			}
121 
122 		}
123 		else if (ev->subtype == EV_GUI_MouseMove)
124 		{
125 			BackbuttonTime = BACKBUTTON_TIME;
126 			if (mMouseCapture || m_use_mouse == 1)
127 			{
128 				res = MouseEventBack(MOUSE_Move, ev->data1, ev->data2);
129 				if (res) ev->data2 = -1;
130 				res |= MouseEvent(MOUSE_Move, ev->data1, ev->data2);
131 			}
132 		}
133 		else if (ev->subtype == EV_GUI_LButtonUp)
134 		{
135 			if (mMouseCapture)
136 			{
137 				ReleaseCapture();
138 				res = MouseEventBack(MOUSE_Release, ev->data1, ev->data2);
139 				if (res) ev->data2 = -1;
140 				res |= MouseEvent(MOUSE_Release, ev->data1, ev->data2);
141 			}
142 		}
143 	}
144 	return false;
145 }
146 
147 //=============================================================================
148 //
149 //
150 //
151 //=============================================================================
152 
MenuEvent(int mkey,bool fromcontroller)153 bool DMenu::MenuEvent (int mkey, bool fromcontroller)
154 {
155 	switch (mkey)
156 	{
157 	case MKEY_Back:
158 	{
159 		Close();
160 		S_Sound (CHAN_VOICE | CHAN_UI,
161 			DMenu::CurrentMenu != NULL? "menu/backup" : "menu/clear", snd_menuvolume, ATTN_NONE);
162 		return true;
163 	}
164 	}
165 	return false;
166 }
167 
168 //=============================================================================
169 //
170 //
171 //
172 //=============================================================================
173 
Close()174 void DMenu::Close ()
175 {
176 	assert(DMenu::CurrentMenu == this);
177 	DMenu::CurrentMenu = mParentMenu;
178 	Destroy();
179 	if (DMenu::CurrentMenu != NULL)
180 	{
181 		GC::WriteBarrier(DMenu::CurrentMenu);
182 	}
183 	else
184 	{
185 		M_ClearMenus ();
186 	}
187 }
188 
189 //=============================================================================
190 //
191 //
192 //
193 //=============================================================================
194 
MouseEvent(int type,int x,int y)195 bool DMenu::MouseEvent(int type, int x, int y)
196 {
197 	return true;
198 }
199 
200 //=============================================================================
201 //
202 //
203 //
204 //=============================================================================
205 
MouseEventBack(int type,int x,int y)206 bool DMenu::MouseEventBack(int type, int x, int y)
207 {
208 	if (m_show_backbutton >= 0)
209 	{
210 		FTexture *tex = TexMan(gameinfo.mBackButton);
211 		if (tex != NULL)
212 		{
213 			if (m_show_backbutton&1) x -= screen->GetWidth() - tex->GetScaledWidth() * CleanXfac;
214 			if (m_show_backbutton&2) y -= screen->GetHeight() - tex->GetScaledHeight() * CleanYfac;
215 			mBackbuttonSelected = ( x >= 0 && x < tex->GetScaledWidth() * CleanXfac &&
216 									y >= 0 && y < tex->GetScaledHeight() * CleanYfac);
217 			if (mBackbuttonSelected && type == MOUSE_Release)
218 			{
219 				if (m_use_mouse == 2) mBackbuttonSelected = false;
220 				MenuEvent(MKEY_Back, true);
221 			}
222 			return mBackbuttonSelected;
223 		}
224 	}
225 	return false;
226 }
227 
228 //=============================================================================
229 //
230 //
231 //
232 //=============================================================================
233 
SetCapture()234 void DMenu::SetCapture()
235 {
236 	if (!mMouseCapture)
237 	{
238 		mMouseCapture = true;
239 		I_SetMouseCapture();
240 	}
241 }
242 
ReleaseCapture()243 void DMenu::ReleaseCapture()
244 {
245 	if (mMouseCapture)
246 	{
247 		mMouseCapture = false;
248 		I_ReleaseMouseCapture();
249 	}
250 }
251 
252 //=============================================================================
253 //
254 //
255 //
256 //=============================================================================
257 
Ticker()258 void DMenu::Ticker ()
259 {
260 }
261 
Drawer()262 void DMenu::Drawer ()
263 {
264 	if (this == DMenu::CurrentMenu && BackbuttonAlpha > 0 && m_show_backbutton >= 0 && m_use_mouse)
265 	{
266 		FTexture *tex = TexMan(gameinfo.mBackButton);
267 		int w = tex->GetScaledWidth() * CleanXfac;
268 		int h = tex->GetScaledHeight() * CleanYfac;
269 		int x = (!(m_show_backbutton&1))? 0:screen->GetWidth() - w;
270 		int y = (!(m_show_backbutton&2))? 0:screen->GetHeight() - h;
271 		if (mBackbuttonSelected && (mMouseCapture || m_use_mouse == 1))
272 		{
273 			screen->DrawTexture(tex, x, y, DTA_CleanNoMove, true, DTA_ColorOverlay, MAKEARGB(40, 255,255,255), TAG_DONE);
274 		}
275 		else
276 		{
277 			screen->DrawTexture(tex, x, y, DTA_CleanNoMove, true, DTA_Alpha, BackbuttonAlpha, TAG_DONE);
278 		}
279 	}
280 }
281 
DimAllowed()282 bool DMenu::DimAllowed()
283 {
284 	return true;
285 }
286 
TranslateKeyboardEvents()287 bool DMenu::TranslateKeyboardEvents()
288 {
289 	return true;
290 }
291 
292 //=============================================================================
293 //
294 //
295 //
296 //=============================================================================
297 
M_StartControlPanel(bool makeSound)298 void M_StartControlPanel (bool makeSound)
299 {
300 	// intro might call this repeatedly
301 	if (DMenu::CurrentMenu != NULL)
302 		return;
303 
304 	ResetButtonStates ();
305 	for (int i = 0; i < NUM_MKEYS; ++i)
306 	{
307 		MenuButtons[i].ReleaseKey(0);
308 	}
309 
310 	C_HideConsole ();				// [RH] Make sure console goes bye bye.
311 	menuactive = MENU_On;
312 	// Pause sound effects before we play the menu switch sound.
313 	// That way, it won't be paused.
314 	P_CheckTickerPaused ();
315 
316 	if (makeSound)
317 	{
318 		S_Sound (CHAN_VOICE | CHAN_UI, "menu/activate", snd_menuvolume, ATTN_NONE);
319 	}
320 	BackbuttonTime = 0;
321 	BackbuttonAlpha = 0;
322 }
323 
324 //=============================================================================
325 //
326 //
327 //
328 //=============================================================================
329 
M_ActivateMenu(DMenu * menu)330 void M_ActivateMenu(DMenu *menu)
331 {
332 	if (menuactive == MENU_Off) menuactive = MENU_On;
333 	if (DMenu::CurrentMenu != NULL) DMenu::CurrentMenu->ReleaseCapture();
334 	DMenu::CurrentMenu = menu;
335 	GC::WriteBarrier(DMenu::CurrentMenu);
336 }
337 
338 //=============================================================================
339 //
340 //
341 //
342 //=============================================================================
343 
M_SetMenu(FName menu,int param)344 void M_SetMenu(FName menu, int param)
345 {
346 	// some menus need some special treatment
347 	switch (menu)
348 	{
349 	case NAME_Episodemenu:
350 		// sent from the player class menu
351 		GameStartupInfo.Skill = -1;
352 		GameStartupInfo.Episode = -1;
353 		GameStartupInfo.PlayerClass =
354 			param == -1000? NULL :
355 			param == -1? "Random" : GetPrintableDisplayName(PlayerClasses[param].Type);
356 		break;
357 
358 	case NAME_Skillmenu:
359 		// sent from the episode menu
360 
361 		if ((gameinfo.flags & GI_SHAREWARE) && param > 0)
362 		{
363 			// Only Doom and Heretic have multi-episode shareware versions.
364 			M_StartMessage(GStrings("SWSTRING"), 1);
365 			return;
366 		}
367 
368 		GameStartupInfo.Episode = param;
369 		M_StartupSkillMenu(&GameStartupInfo);	// needs player class name from class menu (later)
370 		break;
371 
372 	case NAME_StartgameConfirm:
373 	{
374 		// sent from the skill menu for a skill that needs to be confirmed
375 		GameStartupInfo.Skill = param;
376 
377 		const char *msg = AllSkills[param].MustConfirmText;
378 		if (*msg==0) msg = GStrings("NIGHTMARE");
379 		M_StartMessage (msg, 0, NAME_StartgameConfirmed);
380 		return;
381 	}
382 
383 	case NAME_Startgame:
384 		// sent either from skill menu or confirmation screen. Skill gets only set if sent from skill menu
385 		// Now we can finally start the game. Ugh...
386 		GameStartupInfo.Skill = param;
387 	case NAME_StartgameConfirmed:
388 
389 		G_DeferedInitNew (&GameStartupInfo);
390 		if (gamestate == GS_FULLCONSOLE)
391 		{
392 			gamestate = GS_HIDECONSOLE;
393 			gameaction = ga_newgame;
394 		}
395 		M_ClearMenus ();
396 		return;
397 
398 	case NAME_Savegamemenu:
399 		if (!usergame || (players[consoleplayer].health <= 0 && !multiplayer) || gamestate != GS_LEVEL)
400 		{
401 			// cannot save outside the game.
402 			M_StartMessage (GStrings("SAVEDEAD"), 1);
403 			return;
404 		}
405 	}
406 
407 	// End of special checks
408 
409 	FMenuDescriptor **desc = MenuDescriptors.CheckKey(menu);
410 	if (desc != NULL)
411 	{
412 		if ((*desc)->mNetgameMessage.IsNotEmpty() && netgame && !demoplayback)
413 		{
414 			M_StartMessage((*desc)->mNetgameMessage, 1);
415 			return;
416 		}
417 
418 		if ((*desc)->mType == MDESC_ListMenu)
419 		{
420 			FListMenuDescriptor *ld = static_cast<FListMenuDescriptor*>(*desc);
421 			if (ld->mAutoselect >= 0 && ld->mAutoselect < (int)ld->mItems.Size())
422 			{
423 				// recursively activate the autoselected item without ever creating this menu.
424 				ld->mItems[ld->mAutoselect]->Activate();
425 			}
426 			else
427 			{
428 				const PClass *cls = ld->mClass == NULL? RUNTIME_CLASS(DListMenu) : ld->mClass;
429 
430 				DListMenu *newmenu = (DListMenu *)cls->CreateNew();
431 				newmenu->Init(DMenu::CurrentMenu, ld);
432 				M_ActivateMenu(newmenu);
433 			}
434 		}
435 		else if ((*desc)->mType == MDESC_OptionsMenu)
436 		{
437 			FOptionMenuDescriptor *ld = static_cast<FOptionMenuDescriptor*>(*desc);
438 			const PClass *cls = ld->mClass == NULL? RUNTIME_CLASS(DOptionMenu) : ld->mClass;
439 
440 			DOptionMenu *newmenu = (DOptionMenu *)cls->CreateNew();
441 			newmenu->Init(DMenu::CurrentMenu, ld);
442 			M_ActivateMenu(newmenu);
443 		}
444 		return;
445 	}
446 	else
447 	{
448 		const PClass *menuclass = PClass::FindClass(menu);
449 		if (menuclass != NULL)
450 		{
451 			if (menuclass->IsDescendantOf(RUNTIME_CLASS(DMenu)))
452 			{
453 				DMenu *newmenu = (DMenu*)menuclass->CreateNew();
454 				newmenu->mParentMenu = DMenu::CurrentMenu;
455 				M_ActivateMenu(newmenu);
456 				return;
457 			}
458 		}
459 	}
460 	Printf("Attempting to open menu of unknown type '%s'\n", menu.GetChars());
461 }
462 
463 //=============================================================================
464 //
465 //
466 //
467 //=============================================================================
468 
M_Responder(event_t * ev)469 bool M_Responder (event_t *ev)
470 {
471 	int ch = 0;
472 	bool keyup = false;
473 	int mkey = NUM_MKEYS;
474 	bool fromcontroller = true;
475 
476 	if (chatmodeon)
477 	{
478 		return false;
479 	}
480 
481 	if (DMenu::CurrentMenu != NULL && menuactive != MENU_Off)
482 	{
483 		// There are a few input sources we are interested in:
484 		//
485 		// EV_KeyDown / EV_KeyUp : joysticks/gamepads/controllers
486 		// EV_GUI_KeyDown / EV_GUI_KeyUp : the keyboard
487 		// EV_GUI_Char : printable characters, which we want in string input mode
488 		//
489 		// This code previously listened for EV_GUI_KeyRepeat to handle repeating
490 		// in the menus, but that doesn't work with gamepads, so now we combine
491 		// the multiple inputs into buttons and handle the repetition manually.
492 		if (ev->type == EV_GUI_Event)
493 		{
494 			fromcontroller = false;
495 			if (ev->subtype == EV_GUI_KeyRepeat)
496 			{
497 				// We do our own key repeat handling but still want to eat the
498 				// OS's repeated keys.
499 				return true;
500 			}
501 			else if (ev->subtype == EV_GUI_BackButtonDown || ev->subtype == EV_GUI_BackButtonUp)
502 			{
503 				mkey = MKEY_Back;
504 				keyup = ev->subtype == EV_GUI_BackButtonUp;
505 			}
506 			else if (ev->subtype != EV_GUI_KeyDown && ev->subtype != EV_GUI_KeyUp)
507 			{
508 				// do we want mouse input?
509 				if (ev->subtype >= EV_GUI_FirstMouseEvent && ev->subtype <= EV_GUI_LastMouseEvent)
510 				{
511 						if (!m_use_mouse)
512 							return true;
513 				}
514 
515 				// pass everything else on to the current menu
516 				return DMenu::CurrentMenu->Responder(ev);
517 			}
518 			else if (DMenu::CurrentMenu->TranslateKeyboardEvents())
519 			{
520 				ch = ev->data1;
521 				keyup = ev->subtype == EV_GUI_KeyUp;
522 				switch (ch)
523 				{
524 				case GK_BACK:			mkey = MKEY_Back;		break;
525 				case GK_ESCAPE:			mkey = MKEY_Back;		break;
526 				case GK_RETURN:			mkey = MKEY_Enter;		break;
527 				case GK_UP:				mkey = MKEY_Up;			break;
528 				case GK_DOWN:			mkey = MKEY_Down;		break;
529 				case GK_LEFT:			mkey = MKEY_Left;		break;
530 				case GK_RIGHT:			mkey = MKEY_Right;		break;
531 				case GK_BACKSPACE:		mkey = MKEY_Clear;		break;
532 				case GK_PGUP:			mkey = MKEY_PageUp;		break;
533 				case GK_PGDN:			mkey = MKEY_PageDown;	break;
534 				default:
535 					if (!keyup)
536 					{
537 						return DMenu::CurrentMenu->Responder(ev);
538 					}
539 					break;
540 				}
541 			}
542 		}
543 		else if (menuactive != MENU_WaitKey && (ev->type == EV_KeyDown || ev->type == EV_KeyUp))
544 		{
545 			keyup = ev->type == EV_KeyUp;
546 
547 			ch = ev->data1;
548 			switch (ch)
549 			{
550 			case KEY_JOY1:
551 			case KEY_PAD_A:
552 				mkey = MKEY_Enter;
553 				break;
554 
555 			case KEY_JOY2:
556 			case KEY_PAD_B:
557 				mkey = MKEY_Back;
558 				break;
559 
560 			case KEY_JOY3:
561 			case KEY_PAD_X:
562 				mkey = MKEY_Clear;
563 				break;
564 
565 			case KEY_JOY5:
566 			case KEY_PAD_LSHOULDER:
567 				mkey = MKEY_PageUp;
568 				break;
569 
570 			case KEY_JOY6:
571 			case KEY_PAD_RSHOULDER:
572 				mkey = MKEY_PageDown;
573 				break;
574 
575 			case KEY_PAD_DPAD_UP:
576 			case KEY_PAD_LTHUMB_UP:
577 			case KEY_JOYAXIS1MINUS:
578 			case KEY_JOYPOV1_UP:
579 				mkey = MKEY_Up;
580 				break;
581 
582 			case KEY_PAD_DPAD_DOWN:
583 			case KEY_PAD_LTHUMB_DOWN:
584 			case KEY_JOYAXIS1PLUS:
585 			case KEY_JOYPOV1_DOWN:
586 				mkey = MKEY_Down;
587 				break;
588 
589 			case KEY_PAD_DPAD_LEFT:
590 			case KEY_PAD_LTHUMB_LEFT:
591 			case KEY_JOYAXIS2MINUS:
592 			case KEY_JOYPOV1_LEFT:
593 				mkey = MKEY_Left;
594 				break;
595 
596 			case KEY_PAD_DPAD_RIGHT:
597 			case KEY_PAD_LTHUMB_RIGHT:
598 			case KEY_JOYAXIS2PLUS:
599 			case KEY_JOYPOV1_RIGHT:
600 				mkey = MKEY_Right;
601 				break;
602 			}
603 		}
604 
605 		if (mkey != NUM_MKEYS)
606 		{
607 			if (keyup)
608 			{
609 				MenuButtons[mkey].ReleaseKey(ch);
610 				return false;
611 			}
612 			else
613 			{
614 				MenuButtons[mkey].PressKey(ch);
615 				MenuButtonOrigin[mkey] = fromcontroller;
616 				if (mkey <= MKEY_PageDown)
617 				{
618 					MenuButtonTickers[mkey] = KEY_REPEAT_DELAY;
619 				}
620 				DMenu::CurrentMenu->MenuEvent(mkey, fromcontroller);
621 				return true;
622 			}
623 		}
624 		return DMenu::CurrentMenu->Responder(ev) || !keyup;
625 	}
626 	else if (MenuEnabled)
627 	{
628 		if (ev->type == EV_KeyDown)
629 		{
630 			// Pop-up menu?
631 			if (ev->data1 == KEY_ESCAPE)
632 			{
633 				M_StartControlPanel(true);
634 				M_SetMenu(NAME_Mainmenu, -1);
635 				return true;
636 			}
637 			// If devparm is set, pressing F1 always takes a screenshot no matter
638 			// what it's bound to. (for those who don't bother to read the docs)
639 			if (devparm && ev->data1 == KEY_F1)
640 			{
641 				G_ScreenShot(NULL);
642 				return true;
643 			}
644 			return false;
645 		}
646 		else if (ev->type == EV_GUI_Event && ev->subtype == EV_GUI_LButtonDown &&
647 				 ConsoleState != c_down && m_use_mouse)
648 		{
649 			M_StartControlPanel(true);
650 			M_SetMenu(NAME_Mainmenu, -1);
651 			return true;
652 		}
653 	}
654 	return false;
655 }
656 
657 //=============================================================================
658 //
659 //
660 //
661 //=============================================================================
662 
M_Ticker(void)663 void M_Ticker (void)
664 {
665 	DMenu::MenuTime++;
666 	if (DMenu::CurrentMenu != NULL && menuactive != MENU_Off)
667 	{
668 		DMenu::CurrentMenu->Ticker();
669 
670 		for (int i = 0; i < NUM_MKEYS; ++i)
671 		{
672 			if (MenuButtons[i].bDown)
673 			{
674 				if (MenuButtonTickers[i] > 0 &&	--MenuButtonTickers[i] <= 0)
675 				{
676 					MenuButtonTickers[i] = KEY_REPEAT_RATE;
677 					DMenu::CurrentMenu->MenuEvent(i, MenuButtonOrigin[i]);
678 				}
679 			}
680 		}
681 		if (BackbuttonTime > 0)
682 		{
683 			if (BackbuttonAlpha < FRACUNIT) BackbuttonAlpha += FRACUNIT/10;
684 			BackbuttonTime--;
685 		}
686 		else
687 		{
688 			if (BackbuttonAlpha > 0) BackbuttonAlpha -= FRACUNIT/10;
689 			if (BackbuttonAlpha < 0) BackbuttonAlpha = 0;
690 		}
691 	}
692 }
693 
694 //=============================================================================
695 //
696 //
697 //
698 //=============================================================================
699 
M_Drawer(void)700 void M_Drawer (void)
701 {
702 	player_t *player = &players[consoleplayer];
703 	AActor *camera = player->camera;
704 	PalEntry fade = 0;
705 
706 	if (!screen->Accel2D && camera != NULL && (gamestate == GS_LEVEL || gamestate == GS_TITLELEVEL))
707 	{
708 		if (camera->player != NULL)
709 		{
710 			player = camera->player;
711 		}
712 		fade = PalEntry (BYTE(player->BlendA*255), BYTE(player->BlendR*255), BYTE(player->BlendG*255), BYTE(player->BlendB*255));
713 	}
714 
715 
716 	if (DMenu::CurrentMenu != NULL && menuactive != MENU_Off)
717 	{
718 		if (DMenu::CurrentMenu->DimAllowed()) screen->Dim(fade);
719 		DMenu::CurrentMenu->Drawer();
720 	}
721 }
722 
723 //=============================================================================
724 //
725 //
726 //
727 //=============================================================================
728 
M_ClearMenus()729 void M_ClearMenus ()
730 {
731 	M_DemoNoPlay = false;
732 	if (DMenu::CurrentMenu != NULL)
733 	{
734 		DMenu::CurrentMenu->Destroy();
735 		DMenu::CurrentMenu = NULL;
736 	}
737 	V_SetBorderNeedRefresh();
738 	menuactive = MENU_Off;
739 }
740 
741 //=============================================================================
742 //
743 //
744 //
745 //=============================================================================
746 
M_Init(void)747 void M_Init (void)
748 {
749 	M_ParseMenuDefs();
750 	M_CreateMenus();
751 }
752 
753 
754 //=============================================================================
755 //
756 //
757 //
758 //=============================================================================
759 
M_EnableMenu(bool on)760 void M_EnableMenu (bool on)
761 {
762 	MenuEnabled = on;
763 }
764 
765 
766 //=============================================================================
767 //
768 // [RH] Most menus can now be accessed directly
769 // through console commands.
770 //
771 //=============================================================================
772 
CCMD(menu_main)773 CCMD (menu_main)
774 {
775 	M_StartControlPanel(true);
776 	M_SetMenu(NAME_Mainmenu, -1);
777 }
778 
CCMD(menu_load)779 CCMD (menu_load)
780 {	// F3
781 	M_StartControlPanel (true);
782 	M_SetMenu(NAME_Loadgamemenu, -1);
783 }
784 
CCMD(menu_save)785 CCMD (menu_save)
786 {	// F2
787 	M_StartControlPanel (true);
788 	M_SetMenu(NAME_Savegamemenu, -1);
789 }
790 
CCMD(menu_help)791 CCMD (menu_help)
792 {	// F1
793 	M_StartControlPanel (true);
794 	M_SetMenu(NAME_Readthismenu, -1);
795 }
796 
CCMD(menu_game)797 CCMD (menu_game)
798 {
799 	M_StartControlPanel (true);
800 	M_SetMenu(NAME_Playerclassmenu, -1);	// The playerclass menu is the first in the 'start game' chain
801 }
802 
CCMD(menu_options)803 CCMD (menu_options)
804 {
805 	M_StartControlPanel (true);
806 	M_SetMenu(NAME_Optionsmenu, -1);
807 }
808 
CCMD(menu_player)809 CCMD (menu_player)
810 {
811 	M_StartControlPanel (true);
812 	M_SetMenu(NAME_Playermenu, -1);
813 }
814 
CCMD(menu_messages)815 CCMD (menu_messages)
816 {
817 	M_StartControlPanel (true);
818 	M_SetMenu(NAME_MessageOptions, -1);
819 }
820 
CCMD(menu_automap)821 CCMD (menu_automap)
822 {
823 	M_StartControlPanel (true);
824 	M_SetMenu(NAME_AutomapOptions, -1);
825 }
826 
CCMD(menu_scoreboard)827 CCMD (menu_scoreboard)
828 {
829 	M_StartControlPanel (true);
830 	M_SetMenu(NAME_ScoreboardOptions, -1);
831 }
832 
CCMD(menu_mapcolors)833 CCMD (menu_mapcolors)
834 {
835 	M_StartControlPanel (true);
836 	M_SetMenu(NAME_MapColorMenu, -1);
837 }
838 
CCMD(menu_keys)839 CCMD (menu_keys)
840 {
841 	M_StartControlPanel (true);
842 	M_SetMenu(NAME_CustomizeControls, -1);
843 }
844 
CCMD(menu_gameplay)845 CCMD (menu_gameplay)
846 {
847 	M_StartControlPanel (true);
848 	M_SetMenu(NAME_GameplayOptions, -1);
849 }
850 
CCMD(menu_compatibility)851 CCMD (menu_compatibility)
852 {
853 	M_StartControlPanel (true);
854 	M_SetMenu(NAME_CompatibilityOptions, -1);
855 }
856 
CCMD(menu_mouse)857 CCMD (menu_mouse)
858 {
859 	M_StartControlPanel (true);
860 	M_SetMenu(NAME_MouseOptions, -1);
861 }
862 
CCMD(menu_joystick)863 CCMD (menu_joystick)
864 {
865 	M_StartControlPanel (true);
866 	M_SetMenu(NAME_JoystickOptions, -1);
867 }
868 
CCMD(menu_sound)869 CCMD (menu_sound)
870 {
871 	M_StartControlPanel (true);
872 	M_SetMenu(NAME_SoundOptions, -1);
873 }
874 
CCMD(menu_advsound)875 CCMD (menu_advsound)
876 {
877 	M_StartControlPanel (true);
878 	M_SetMenu(NAME_AdvSoundOptions, -1);
879 }
880 
CCMD(menu_modreplayer)881 CCMD (menu_modreplayer)
882 {
883 	M_StartControlPanel(true);
884 	M_SetMenu(NAME_ModReplayerOptions, -1);
885 }
886 
CCMD(menu_display)887 CCMD (menu_display)
888 {
889 	M_StartControlPanel (true);
890 	M_SetMenu(NAME_VideoOptions, -1);
891 }
892 
CCMD(menu_video)893 CCMD (menu_video)
894 {
895 	M_StartControlPanel (true);
896 	M_SetMenu(NAME_VideoModeMenu, -1);
897 }
898 
899 
900 
CCMD(openmenu)901 CCMD (openmenu)
902 {
903 	if (argv.argc() < 2)
904 	{
905 		Printf("Usage: openmenu \"menu_name\"");
906 		return;
907 	}
908 	M_StartControlPanel (true);
909 	M_SetMenu(argv[1], -1);
910 }
911 
CCMD(closemenu)912 CCMD (closemenu)
913 {
914 	M_ClearMenus();
915 }
916 
917 //
918 //		Toggle messages on/off
919 //
CCMD(togglemessages)920 CCMD (togglemessages)
921 {
922 	if (show_messages)
923 	{
924 		Printf (128, "%s\n", GStrings("MSGOFF"));
925 		show_messages = false;
926 	}
927 	else
928 	{
929 		Printf (128, "%s\n", GStrings("MSGON"));
930 		show_messages = true;
931 	}
932 }
933 
EXTERN_CVAR(Int,screenblocks)934 EXTERN_CVAR (Int, screenblocks)
935 
936 CCMD (sizedown)
937 {
938 	screenblocks = screenblocks - 1;
939 	S_Sound (CHAN_VOICE | CHAN_UI, "menu/change", snd_menuvolume, ATTN_NONE);
940 }
941 
CCMD(sizeup)942 CCMD (sizeup)
943 {
944 	screenblocks = screenblocks + 1;
945 	S_Sound (CHAN_VOICE | CHAN_UI, "menu/change", snd_menuvolume, ATTN_NONE);
946 }
947 
CCMD(menuconsole)948 CCMD(menuconsole)
949 {
950 	M_ClearMenus();
951 	C_ToggleConsole();
952 }
953 
CCMD(reset2defaults)954 CCMD(reset2defaults)
955 {
956 	C_SetDefaultBindings ();
957 	C_SetCVarsToDefaults ();
958 	R_SetViewSize (screenblocks);
959 }
960 
CCMD(reset2saved)961 CCMD(reset2saved)
962 {
963 	GameConfig->DoGlobalSetup ();
964 	GameConfig->DoGameSetup (gameinfo.ConfigName);
965 	GameConfig->DoModSetup (gameinfo.ConfigName);
966 	R_SetViewSize (screenblocks);
967 }
968