1 /** @file hu_menu.cpp Menu widget stuff, episode selection and such.
2 *
3 * @authors Copyright © 2003-2017 Jaakko Keränen <jaakko.keranen@iki.fi>
4 * @authors Copyright © 2005-2015 Daniel Swanson <danij@dengine.net>
5 *
6 * @par License
7 * GPL: http://www.gnu.org/licenses/gpl.html
8 *
9 * <small>This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by the
11 * Free Software Foundation; either version 2 of the License, or (at your
12 * option) any later version. This program is distributed in the hope that it
13 * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
15 * Public License for more details. You should have received a copy of the GNU
16 * General Public License along with this program; if not, write to the Free
17 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
18 * 02110-1301 USA</small>
19 */
20
21 #include "common.h"
22 #include "hu_menu.h"
23
24 #include <cstdlib>
25 #include <cctype>
26 #include <cmath>
27 #include <cstdio>
28 #include <cstring>
29 #include <QMap>
30 #include <QtAlgorithms>
31 #include <de/memory.h>
32 #include <de/RecordValue>
33 #include "g_common.h"
34 #include "g_controls.h"
35 #include "g_defs.h"
36 #include "gamesession.h"
37 #include "hu_msg.h"
38 #include "hu_stuff.h"
39 #include "m_argv.h"
40 #include "m_ctrl.h"
41 #include "p_savedef.h"
42 #include "player.h"
43 #include "r_common.h"
44 #include "saveslots.h"
45 #include "x_hair.h"
46
47 #include "menu/page.h"
48 #include "menu/widgets/coloreditwidget.h"
49 #include "menu/widgets/cvarcoloreditwidget.h"
50 #include "menu/widgets/cvarinlinelistwidget.h"
51 #include "menu/widgets/cvarlineeditwidget.h"
52 #include "menu/widgets/cvarsliderwidget.h"
53 #include "menu/widgets/cvartextualsliderwidget.h"
54 #include "menu/widgets/cvartogglewidget.h"
55 #include "menu/widgets/inputbindingwidget.h"
56 #include "menu/widgets/labelwidget.h"
57 #include "menu/widgets/mobjpreviewwidget.h"
58 #include "menu/widgets/rectwidget.h"
59 #include "menu/widgets/sliderwidget.h"
60
61 using namespace de;
62
63 namespace common {
64
65 using namespace common::menu;
66
67 /// Original game line height for pages that employ the fixed layout (in 320x200 pixels).
68 #if __JDOOM__
69 # define FIXED_LINE_HEIGHT (15+1)
70 #else
71 # define FIXED_LINE_HEIGHT (19+1)
72 #endif
73
74 void Hu_MenuActivatePlayerSetup(Page &page);
75
76 void Hu_MenuActionSetActivePage(Widget &wi, Widget::Action action);
77 void Hu_MenuActionInitNewGame(Widget &wi, Widget::Action action);
78
79 void Hu_MenuSelectLoadGame(Widget &wi, Widget::Action action);
80 void Hu_MenuSelectSaveGame(Widget &wi, Widget::Action action);
81 void Hu_MenuSelectJoinGame(Widget &wi, Widget::Action action);
82
83 #if __JDOOM__ || __JHERETIC__ || __JHEXEN__
84 void Hu_MenuSelectHelp(Widget &wi, Widget::Action action);
85 #endif
86 void Hu_MenuSelectControlPanelLink(Widget &wi, Widget::Action action);
87
88 void Hu_MenuSelectSingleplayer(Widget &wi, Widget::Action action);
89 //void Hu_MenuSelectMultiplayer(Widget &wi, Widget::Action action);
90 void Hu_MenuSelectEpisode(Widget &wi, Widget::Action action);
91 #if __JDOOM__ || __JHERETIC__
92 void Hu_MenuActivateNotSharewareEpisode(Widget &wi, Widget::Action action);
93 #endif
94 #if __JHEXEN__
95 void Hu_MenuFocusOnPlayerClass(Widget &wi, Widget::Action action);
96 void Hu_MenuSelectPlayerClass(Widget &wi, Widget::Action action);
97 #endif
98 void Hu_MenuFocusSkillMode(Widget &wi, Widget::Action action);
99 void Hu_MenuSelectLoadSlot(Widget &wi, Widget::Action action);
100 void Hu_MenuSelectQuitGame(Widget &wi, Widget::Action action);
101 void Hu_MenuSelectEndGame(Widget &wi, Widget::Action action);
102 void Hu_MenuSelectAcceptPlayerSetup(Widget &wi, Widget::Action action);
103
104 void Hu_MenuSelectSaveSlot(Widget &wi, Widget::Action action);
105
106 void Hu_MenuChangeWeaponPriority(Widget &wi, Widget::Action action);
107 #if __JHEXEN__
108 void Hu_MenuSelectPlayerSetupPlayerClass(Widget &wi, Widget::Action action);
109 #endif
110 void Hu_MenuSelectPlayerColor(Widget &wi, Widget::Action action);
111
112 #if __JHEXEN__
113 void Hu_MenuPlayerClassBackgroundTicker(Widget &wi);
114 void Hu_MenuPlayerClassPreviewTicker(Widget &wi);
115 #endif
116
117 #if __JHERETIC__ || __JHEXEN__
118 void Hu_MenuDrawMainPage(Page const &page, Vector2i const &origin);
119 #endif
120
121 //void Hu_MenuDrawGameTypePage(Page const &page, Vector2i const &origin);
122 void Hu_MenuDrawSkillPage(Page const &page, Vector2i const &origin);
123 #if __JHEXEN__
124 void Hu_MenuDrawPlayerClassPage(Page const &page, Vector2i const &origin);
125 #endif
126 void Hu_MenuDrawEpisodePage(Page const &page, Vector2i const &origin);
127 void Hu_MenuDrawOptionsPage(Page const &page, Vector2i const &origin);
128 void Hu_MenuDrawLoadGamePage(Page const &page, Vector2i const &origin);
129 void Hu_MenuDrawSaveGamePage(Page const &page, Vector2i const &origin);
130 void Hu_MenuDrawMultiplayerPage(Page const &page, Vector2i const &origin);
131 void Hu_MenuDrawPlayerSetupPage(Page const &page, Vector2i const &origin);
132
133 int Hu_MenuColorWidgetCmdResponder(Page &page, menucommand_e cmd);
134 int Hu_MenuSkipPreviousPageIfSkippingEpisodeSelection(Page &page, menucommand_e cmd);
135
136 void Hu_MenuSaveSlotEdit(Widget &wi, Widget::Action action);
137
138 void Hu_MenuActivateColorWidget(Widget &wi, Widget::Action action);
139 void Hu_MenuUpdateColorWidgetColor(Widget &wi, Widget::Action action);
140
141 static void Hu_MenuInitNewGame(bool confirmed);
142
143 static void initAllPages();
144 static void destroyAllPages();
145
146 static void Hu_MenuUpdateCursorState();
147
148 static bool Hu_MenuHasCursorRotation(Widget *wi);
149
150 int menuTime;
151 dd_bool menuNominatingQuickSaveSlot;
152
153 static Page *currentPage;
154 static bool menuActive;
155
156 static float mnAlpha; // Alpha level for the entire menu.
157 static float mnTargetAlpha; // Target alpha for the entire UI.
158
159 static skillmode_t mnSkillmode = SM_MEDIUM;
160 static String mnEpisode;
161 #if __JHEXEN__
162 static int mnPlrClass = PCLASS_FIGHTER;
163 #endif
164
165 static int frame; // Used by any graphic animations that need to be pumped.
166
167 static bool colorWidgetActive;
168
169 // Present cursor state.
170 struct Cursor
171 {
172 bool hasRotation = false;
173 float angle = 0;
174 int animCounter = 0;
175 int animFrame = 0;
176 };
177 static Cursor cursor;
178
179 static patchid_t pMainTitle;
180 #if __JDOOM__ || __JDOOM64__
181 static patchid_t pNewGame;
182 static patchid_t pSkill;
183 static patchid_t pEpisode;
184 static patchid_t pNGame;
185 static patchid_t pOptions;
186 static patchid_t pLoadGame;
187 static patchid_t pSaveGame;
188 static patchid_t pReadThis;
189 static patchid_t pQuitGame;
190 static patchid_t pOptionsTitle;
191
192 static patchid_t pSkillModeNames[NUM_SKILL_MODES];
193 #endif
194
195 #if __JHEXEN__
196 static patchid_t pPlayerClassBG[3];
197 static patchid_t pBullWithFire[8];
198 #endif
199
200 #if __JHERETIC__
201 static patchid_t pRotatingSkull[18];
202 #endif
203
204 static patchid_t pCursors[MENU_CURSOR_FRAMECOUNT];
205
206 static bool inited;
207
208 typedef QMap<String, Page *> Pages;
209 static Pages pages;
210
chooseCloseMethod()211 static menucommand_e chooseCloseMethod()
212 {
213 // If we aren't using a transition then we can close normally and allow our
214 // own menu fade-out animation to be used instead.
215 return Con_GetInteger("con-transition-tics") == 0? MCMD_CLOSE : MCMD_CLOSEFAST;
216 }
217
Hu_MenuHasPage(String name)218 bool Hu_MenuHasPage(String name)
219 {
220 if(!name.isEmpty())
221 {
222 return pages.contains(name.toLower());
223 }
224 return false;
225 }
226
Hu_MenuPage(String name)227 Page &Hu_MenuPage(String name)
228 {
229 if(!name.isEmpty())
230 {
231 Pages::iterator found = pages.find(name.toLower());
232 if(found != pages.end())
233 {
234 return *found.value();
235 }
236 }
237 /// @throw Error No Page exists with the name specified.
238 throw Error("Hu_MenuPage", "Unknown page '" + name + "'");
239 }
240
241 /// @todo Make this state an object property flag.
242 /// @return @c true if the rotation of a cursor on this object should be animated.
Hu_MenuHasCursorRotation(Widget * wi)243 static bool Hu_MenuHasCursorRotation(Widget *wi)
244 {
245 DENG2_ASSERT(wi != 0);
246 return (!wi->isDisabled() && (is<InlineListWidget>(wi) || is<SliderWidget>(wi)));
247 }
248
249 /// To be called to re-evaluate the state of the cursor (e.g., when focus changes).
Hu_MenuUpdateCursorState()250 static void Hu_MenuUpdateCursorState()
251 {
252 if(menuActive)
253 {
254 Page *page = colorWidgetActive? Hu_MenuPagePtr("ColorWidget") : Hu_MenuPagePtr();
255 if(Widget *wi = page->focusWidget())
256 {
257 cursor.hasRotation = Hu_MenuHasCursorRotation(wi);
258 return;
259 }
260 }
261 cursor.hasRotation = false;
262 }
263
Hu_MenuLoadResources()264 static void Hu_MenuLoadResources()
265 {
266 char buf[9];
267
268 #if __JDOOM__ || __JDOOM64__
269 pMainTitle = R_DeclarePatch("M_DOOM");
270 #elif __JHERETIC__ || __JHEXEN__
271 pMainTitle = R_DeclarePatch("M_HTIC");
272 #endif
273
274 #if __JDOOM__ || __JDOOM64__
275 pNewGame = R_DeclarePatch("M_NEWG");
276 pSkill = R_DeclarePatch("M_SKILL");
277 pEpisode = R_DeclarePatch("M_EPISOD");
278 pNGame = R_DeclarePatch("M_NGAME");
279 pOptions = R_DeclarePatch("M_OPTION");
280 pLoadGame = R_DeclarePatch("M_LOADG");
281 pSaveGame = R_DeclarePatch("M_SAVEG");
282 pReadThis = R_DeclarePatch("M_RDTHIS");
283 pQuitGame = R_DeclarePatch("M_QUITG");
284 pOptionsTitle = R_DeclarePatch("M_OPTTTL");
285 #endif
286
287 #if __JDOOM__ || __JDOOM64__
288 pSkillModeNames[SM_BABY] = R_DeclarePatch("M_JKILL");
289 pSkillModeNames[SM_EASY] = R_DeclarePatch("M_ROUGH");
290 pSkillModeNames[SM_MEDIUM] = R_DeclarePatch("M_HURT");
291 pSkillModeNames[SM_HARD] = R_DeclarePatch("M_ULTRA");
292 # if __JDOOM__
293 pSkillModeNames[SM_NIGHTMARE] = R_DeclarePatch("M_NMARE");
294 # endif
295 #endif
296
297 #if __JHERETIC__
298 for(int i = 0; i < 18; ++i)
299 {
300 dd_snprintf(buf, 9, "M_SKL%02d", i);
301 pRotatingSkull[i] = R_DeclarePatch(buf);
302 }
303 #endif
304
305 #if __JHEXEN__
306 for(int i = 0; i < 7; ++i)
307 {
308 dd_snprintf(buf, 9, "FBUL%c0", 'A'+i);
309 pBullWithFire[i] = R_DeclarePatch(buf);
310 }
311
312 pPlayerClassBG[0] = R_DeclarePatch("M_FBOX");
313 pPlayerClassBG[1] = R_DeclarePatch("M_CBOX");
314 pPlayerClassBG[2] = R_DeclarePatch("M_MBOX");
315 #endif
316
317 for(int i = 0; i < MENU_CURSOR_FRAMECOUNT; ++i)
318 {
319 #if __JDOOM__ || __JDOOM64__
320 dd_snprintf(buf, 9, "M_SKULL%d", i+1);
321 #else
322 dd_snprintf(buf, 9, "M_SLCTR%d", i+1);
323 #endif
324 pCursors[i] = R_DeclarePatch(buf);
325 }
326 }
327
Hu_MenuInitColorWidgetPage()328 void Hu_MenuInitColorWidgetPage()
329 {
330 #if __JHERETIC__ || __JHEXEN__
331 Vector2i const origin(98, 60);
332 #else
333 Vector2i const origin(124, 60);
334 #endif
335
336 Page *page = Hu_MenuAddPage(new Page("ColorWidget", origin, Page::NoScroll, NULL, Hu_MenuColorWidgetCmdResponder));
337 page->setPredefinedFont(MENU_FONT1, FID(GF_FONTA));
338
339 page->addWidget(new ColorEditWidget(Vector4f(), true))
340 .setPreviewDimensions(Vector2i(SCREENHEIGHT / 7, SCREENHEIGHT / 7))
341 .setFlags(Widget::Id0 | Widget::NoFocus);
342
343 page->addWidget(new LabelWidget("Red"));
344
345 page->addWidget(new SliderWidget(0.0f, 1.0f, .05f))
346 .setFlags(Widget::Id1)
347 .setShortcut('r')
348 .setUserValue2(int(CR))
349 .setAction(Widget::Modified, Hu_MenuUpdateColorWidgetColor)
350 .setAction(Widget::FocusGained, Hu_MenuDefaultFocusAction);
351
352 page->addWidget(new LabelWidget("Green"));
353 page->addWidget(new SliderWidget(0.0f, 1.0f, .05f))
354 .setFlags(Widget::Id2)
355 .setShortcut('g')
356 .setUserValue2(int(CG))
357 .setAction(Widget::FocusGained, Hu_MenuDefaultFocusAction)
358 .setAction(Widget::Modified, Hu_MenuUpdateColorWidgetColor);
359
360 page->addWidget(new LabelWidget("Blue"));
361 page->addWidget(new SliderWidget(0.0f, 1.0f, 0.05f))
362 .setFlags(Widget::Id3)
363 .setShortcut('b')
364 .setUserValue2(int(CB))
365 .setAction(Widget::Modified, Hu_MenuUpdateColorWidgetColor)
366 .setAction(Widget::FocusGained, Hu_MenuDefaultFocusAction);
367
368 page->addWidget(new LabelWidget("Opacity")).setFlags(Widget::Id4);
369 page->addWidget(new SliderWidget(0.0f, 1.0f, 0.05f))
370 .setFlags(Widget::Id5)
371 .setShortcut('o')
372 .setUserValue2(int(CA))
373 .setAction(Widget::Modified, Hu_MenuUpdateColorWidgetColor)
374 .setAction(Widget::FocusGained, Hu_MenuDefaultFocusAction);
375 }
376
Hu_MenuInitMainPage()377 void Hu_MenuInitMainPage()
378 {
379 #if __JHEXEN__ || __JHERETIC__
380 Vector2i origin(110, 56);
381 #else
382 Vector2i origin(97, 64);
383 #endif
384
385 #if __JDOOM__
386 if(gameModeBits & GM_ANY_DOOM2)
387 {
388 origin.y += 8;
389 }
390 #endif
391
392 #if __JDOOM__ || __JDOOM64__
393 Page *page = Hu_MenuAddPage(new Page("Main", origin, Page::FixedLayout | Page::NoScroll));
394 #else
395 Page *page = Hu_MenuAddPage(new Page("Main", origin, Page::FixedLayout | Page::NoScroll, Hu_MenuDrawMainPage));
396 #endif
397 page->setPredefinedFont(MENU_FONT1, FID(GF_FONTB));
398
399 int y = 0;
400
401 #if __JDOOM__ || __JDOOM64__
402 page->addWidget(new LabelWidget("", &pMainTitle))
403 .setFixedOrigin(Vector2i(-3, -70));
404 #endif
405
406 page->addWidget(new ButtonWidget)
407 #if defined(__JDOOM__) && !defined(__JDOOM64__)
408 .setPatch(pNGame)
409 #else
410 .setText("New Game")
411 #endif
412 .setFixedY(y)
413 .setShortcut('n')
414 .setFont(MENU_FONT1)
415 //.setUserValue(String("GameType"))
416 .setAction(Widget::Deactivated, Hu_MenuSelectSingleplayer)
417 .setAction(Widget::FocusGained, Hu_MenuDefaultFocusAction);
418
419 y += FIXED_LINE_HEIGHT;
420
421 page->addWidget(new ButtonWidget)
422 #if defined(__JDOOM__) && !defined(__JDOOM64__)
423 .setPatch(pOptions)
424 #else
425 .setText("Options")
426 #endif
427 .setFixedY(y)
428 .setShortcut('o')
429 .setFont(MENU_FONT1)
430 .setUserValue(String("Options"))
431 .setAction(Widget::Deactivated, Hu_MenuActionSetActivePage)
432 .setAction(Widget::FocusGained, Hu_MenuDefaultFocusAction);
433
434 y += FIXED_LINE_HEIGHT;
435
436 #if __JDOOM__ || __JDOOM64__
437 page->addWidget(new ButtonWidget)
438 # if __JDOOM64__
439 .setText("Load Game")
440 # else
441 .setPatch(pLoadGame)
442 # endif
443 .setFixedY(y)
444 .setShortcut('l')
445 .setFont(MENU_FONT1)
446 .setAction(Widget::Deactivated, Hu_MenuSelectLoadGame)
447 .setAction(Widget::FocusGained, Hu_MenuDefaultFocusAction);
448
449 y += FIXED_LINE_HEIGHT;
450
451 page->addWidget(new ButtonWidget)
452 # if __JDOOM64__
453 .setText("Save Game")
454 # else
455 .setPatch(pSaveGame)
456 # endif
457 .setFixedY(y)
458 .setShortcut('s')
459 .setFont(MENU_FONT1)
460 .setAction(Widget::Deactivated, Hu_MenuSelectSaveGame)
461 .setAction(Widget::FocusGained, Hu_MenuDefaultFocusAction);
462
463 y += FIXED_LINE_HEIGHT;
464
465 #else
466 page->addWidget(new ButtonWidget("Game Files"))
467 .setFixedY(y)
468 .setShortcut('f')
469 .setFont(MENU_FONT1)
470 .setUserValue(String("Files"))
471 .setAction(Widget::Deactivated, Hu_MenuActionSetActivePage)
472 .setAction(Widget::FocusGained, Hu_MenuDefaultFocusAction);
473
474 y += FIXED_LINE_HEIGHT;
475 #endif
476
477 #if !__JDOOM64__
478 page->addWidget(new ButtonWidget)
479 # if defined(__JDOOM__)
480 .setPatch(pReadThis)
481 # else
482 .setText("Info")
483 # endif
484 .setFixedY(y)
485 # if __JDOOM__
486 .setFlags(Widget::Id0)
487 .setShortcut('r')
488 # else
489 .setShortcut('i')
490 # endif
491 .setFont(MENU_FONT1)
492 .setAction(Widget::Deactivated, Hu_MenuSelectHelp)
493 .setAction(Widget::FocusGained, Hu_MenuDefaultFocusAction);
494
495 y += FIXED_LINE_HEIGHT;
496 #endif
497
498 page->addWidget(new ButtonWidget)
499 #if defined(__JDOOM__) && !defined(__JDOOM64__)
500 .setPatch(pQuitGame)
501 #else
502 .setText("Quit Game")
503 #endif
504 #if __JDOOM__
505 .setFlags(Widget::Id1)
506 #endif
507 .setFixedY(y)
508 .setShortcut('q')
509 .setFont(MENU_FONT1)
510 .setAction(Widget::Deactivated, Hu_MenuSelectQuitGame)
511 .setAction(Widget::FocusGained, Hu_MenuDefaultFocusAction);
512 }
513
514 #if 0
515 void Hu_MenuInitGameTypePage()
516 {
517 #if __JDOOM__ || __JDOOM64__
518 Vector2i origin(97, 65);
519 #else
520 Vector2i origin(104, 65);
521 #endif
522
523 Page *page = Hu_MenuAddPage(new Page("GameType", origin, 0, Hu_MenuDrawGameTypePage));
524 page->setPredefinedFont(MENU_FONT1, FID(GF_FONTB));
525 page->setPreviousPage(Hu_MenuPagePtr("Main"));
526
527 int y = 0;
528
529 String labelText = GET_TXT(TXT_SINGLEPLAYER);
530 int shortcut = labelText.first().isLetterOrNumber()? labelText.first().toLatin1() : 0;
531 page->addWidget(new ButtonWidget(labelText))
532 .setFixedY(y)
533 .setFont(MENU_FONT1)
534 .setShortcut(shortcut)
535 .setAction(Widget::Deactivated, Hu_MenuSelectSingleplayer)
536 .setAction(Widget::FocusGained, Hu_MenuDefaultFocusAction);
537
538 y += FIXED_LINE_HEIGHT;
539
540 labelText = GET_TXT(TXT_MULTIPLAYER);
541 shortcut = labelText.first().isLetterOrNumber()? labelText.first().toLatin1() : 0;
542 page->addWidget(new ButtonWidget(labelText))
543 .setFixedY(y)
544 .setFont(MENU_FONT1)
545 .setShortcut(shortcut)
546 .setAction(Widget::Deactivated, Hu_MenuSelectMultiplayer)
547 .setAction(Widget::FocusGained, Hu_MenuDefaultFocusAction);
548 }
549 #endif
550
Hu_MenuInitSkillPage()551 void Hu_MenuInitSkillPage()
552 {
553 #if __JHEXEN__
554 Vector2i const origin(120, 44);
555 #elif __JHERETIC__
556 Vector2i const origin(38, 30);
557 #else
558 Vector2i const origin(48, 63);
559 #endif
560 Widget::Flags skillButtonFlags[NUM_SKILL_MODES] = {
561 Widget::Id0,
562 Widget::Id1,
563 Widget::Id2 | Widget::DefaultFocus,
564 Widget::Id3,
565 # if !__JDOOM64__
566 Widget::Id4
567 # endif
568 };
569 #if !__JHEXEN__
570 int skillButtonTexts[NUM_SKILL_MODES] = {
571 TXT_SKILL1,
572 TXT_SKILL2,
573 TXT_SKILL3,
574 TXT_SKILL4,
575 # if !__JDOOM64__
576 TXT_SKILL5
577 # endif
578 };
579 #endif
580
581 Page *page = Hu_MenuAddPage(new Page("Skill", origin, Page::FixedLayout | Page::NoScroll,
582 Hu_MenuDrawSkillPage, Hu_MenuSkipPreviousPageIfSkippingEpisodeSelection));
583 page->setPredefinedFont(MENU_FONT1, FID(GF_FONTB));
584 page->setPreviousPage(Hu_MenuPagePtr("Episode"));
585
586 int y = 0;
587
588 for(uint i = 0; i < NUM_SKILL_MODES; ++i, y += FIXED_LINE_HEIGHT)
589 {
590 #if !__JHEXEN__
591 String const labelText = GET_TXT(skillButtonTexts[i]);
592 int const shortcut = labelText.first().isLetterOrNumber()? labelText.first().toLatin1() : 0;
593 #endif
594
595 page->addWidget(new ButtonWidget)
596 #if !__JHEXEN__
597 .setText(labelText)
598 # if __JDOOM__ || __JDOOM64__
599 .setPatch(pSkillModeNames[i])
600 # endif
601 .setShortcut(shortcut)
602 #endif
603 .setFlags(skillButtonFlags[i])
604 .setFixedY(y)
605 .setFont(MENU_FONT1)
606 .setUserValue2(int(SM_BABY + i))
607 .setAction(Widget::Deactivated, Hu_MenuActionInitNewGame)
608 .setAction(Widget::FocusGained, Hu_MenuFocusSkillMode);
609 }
610
611 #if __JDOOM__
612 if(gameMode != doom2_hacx && gameMode != doom_chex)
613 {
614 page->findWidget(Widget::Id4).as<ButtonWidget>().setNoAltText();
615 }
616 #endif
617 }
618
619 #if 0
620 void Hu_MenuInitMultiplayerPage()
621 {
622 #if __JHERETIC__ || __JHEXEN__
623 Vector2i const origin(97, 65);
624 #else
625 Vector2i const origin(97, 65);
626 #endif
627
628 Page *page = Hu_MenuAddPage(new Page("Multiplayer", origin, 0, Hu_MenuDrawMultiplayerPage));
629 page->setPredefinedFont(MENU_FONT1, FID(GF_FONTB));
630 page->setPreviousPage(Hu_MenuPagePtr("GameType"));
631
632 page->addWidget(new ButtonWidget("Join Game"))
633 .setFlags(Widget::Id0)
634 .setShortcut('j')
635 .setFont(MENU_FONT1)
636 .setAction(Widget::Deactivated, Hu_MenuSelectJoinGame)
637 .setAction(Widget::FocusGained, Hu_MenuDefaultFocusAction);
638
639 page->addWidget(new ButtonWidget("Player Setup"))
640 .setShortcut('p')
641 .setFont(MENU_FONT1)
642 .setUserValue(String("PlayerSetup"))
643 .setAction(Widget::Deactivated, Hu_MenuActionSetActivePage)
644 .setAction(Widget::FocusGained, Hu_MenuDefaultFocusAction);
645 }
646 #endif
647
Hu_MenuInitPlayerSetupPage()648 void Hu_MenuInitPlayerSetupPage()
649 {
650 #if __JHERETIC__ || __JHEXEN__
651 Vector2i const origin(70, 34);
652 #else
653 Vector2i const origin(70, 54);
654 #endif
655
656 Page *page = Hu_MenuAddPage(new Page("PlayerSetup", origin, Page::NoScroll, Hu_MenuDrawPlayerSetupPage));
657 page->setLeftColumnWidth(.5f);
658 page->setOnActiveCallback(Hu_MenuActivatePlayerSetup);
659 page->setPredefinedFont(MENU_FONT1, FID(GF_FONTA));
660 page->setPredefinedFont(MENU_FONT2, FID(GF_FONTB));
661 page->setPreviousPage(Hu_MenuPagePtr("Options"));
662
663 page->addWidget(new MobjPreviewWidget)
664 .setFixedOrigin(Vector2i(SCREENWIDTH / 2 - 40, 60))
665 .setFlags(Widget::Id0 | Widget::PositionFixed);
666
667 page->addWidget(new CVarLineEditWidget("net-name"))
668 .setMaxLength(24)
669 .setFlags(Widget::Id1 | Widget::LayoutOffset)
670 .setFixedY(75);
671
672 #if __JHEXEN__
673 page->addWidget(new LabelWidget("Class"))
674 .setLeft()
675 .setFlags(Widget::LayoutOffset)
676 .setFixedY(5);
677
678 page->addWidget(new InlineListWidget)
679 .addItems(ListWidget::Items() << new ListWidgetItem(GET_TXT(TXT_PLAYERCLASS1), PCLASS_FIGHTER)
680 << new ListWidgetItem(GET_TXT(TXT_PLAYERCLASS2), PCLASS_CLERIC)
681 << new ListWidgetItem(GET_TXT(TXT_PLAYERCLASS3), PCLASS_MAGE))
682 .setFlags(Widget::Id2)
683 .setShortcut('c')
684 .setRight()
685 .setColor(MENU_COLOR3)
686 .setAction(Widget::Modified, Hu_MenuSelectPlayerSetupPlayerClass)
687 .setAction(Widget::FocusGained, Hu_MenuDefaultFocusAction);
688 #endif
689
690 auto &label = page->addWidget(new LabelWidget("Color"));
691 label.setLeft();
692 #ifdef __JHERETIC__
693 label.setFlags(Widget::LayoutOffset);
694 label.setFixedY(5);
695 #else
696 DENG2_UNUSED(label);
697 #endif
698
699 // Setup the player color selection list.
700 /// @todo Read these names from Text definitions.
701 int colorIdx = 0;
702 ListWidget::Items items;
703 #if __JHEXEN__
704 items << new ListWidgetItem("Red", colorIdx++);
705 items << new ListWidgetItem("Blue", colorIdx++);
706 items << new ListWidgetItem("Yellow", colorIdx++);
707 items << new ListWidgetItem("Green", colorIdx++);
708 if(gameMode != hexen_v10) // Hexen v1.0 has only four player colors.
709 {
710 items << new ListWidgetItem("Jade", colorIdx++);
711 items << new ListWidgetItem("White", colorIdx++);
712 items << new ListWidgetItem("Hazel", colorIdx++);
713 items << new ListWidgetItem("Purple", colorIdx++);
714 }
715 #elif __JHERETIC__
716 items << new ListWidgetItem("Green", colorIdx++);
717 items << new ListWidgetItem("Orange", colorIdx++);
718 items << new ListWidgetItem("Red", colorIdx++);
719 items << new ListWidgetItem("Blue", colorIdx++);
720 #else
721 items << new ListWidgetItem("Green", colorIdx++);
722 items << new ListWidgetItem("Indigo", colorIdx++);
723 items << new ListWidgetItem("Brown", colorIdx++);
724 items << new ListWidgetItem("Red", colorIdx++);
725 #endif
726 items << new ListWidgetItem("Automatic", colorIdx++);
727
728 page->addWidget(new InlineListWidget)
729 .addItems(items)
730 .setFlags(Widget::Id3)
731 .setColor(MENU_COLOR3)
732 .setRight()
733 .setAction(Widget::Modified, Hu_MenuSelectPlayerColor)
734 .setAction(Widget::FocusGained, Hu_MenuDefaultFocusAction);
735
736 page->addWidget(new ButtonWidget("Save Changes"))
737 .setShortcut('s')
738 .setAction(Widget::Deactivated, Hu_MenuSelectAcceptPlayerSetup)
739 .setAction(Widget::FocusGained, Hu_MenuDefaultFocusAction);
740 }
741
Hu_MenuInitSaveOptionsPage()742 void Hu_MenuInitSaveOptionsPage()
743 {
744 Page *page = Hu_MenuAddPage(new Page("SaveOptions", Vector2i(60, 50)));
745 page->setTitle("Savegame Options");
746 page->setPredefinedFont(MENU_FONT1, FID(GF_FONTA));
747 page->setPreviousPage(Hu_MenuPagePtr("Options"));
748
749 page->addWidget(new LabelWidget("Confirm quick load/save"))
750 .setLeft();
751 page->addWidget(new CVarToggleWidget("game-save-confirm"))
752 .setRight()
753 .setShortcut('q');
754
755 page->addWidget(new LabelWidget("Confirm reborn load"))
756 .setLeft();
757 page->addWidget(new CVarToggleWidget("game-save-confirm-loadonreborn"))
758 .setRight()
759 .setShortcut('r');
760
761 page->addWidget(new LabelWidget("Reborn preferences"))
762 .setGroup(1)
763 .setColor(MENU_COLOR2);
764
765 page->addWidget(new LabelWidget("Load last save"))
766 .setLeft()
767 .setGroup(1);
768 page->addWidget(new CVarToggleWidget("game-save-last-loadonreborn"))
769 .setRight()
770 .setGroup(1)
771 .setShortcut('a');
772 }
773
774 #if __JHERETIC__ || __JHEXEN__
Hu_MenuInitFilesPage()775 void Hu_MenuInitFilesPage()
776 {
777 Page *page = Hu_MenuAddPage(new Page("Files", Vector2i(110, 60), Page::FixedLayout | Page::NoScroll));
778 page->setPredefinedFont(MENU_FONT1, FID(GF_FONTB));
779 page->setPreviousPage(Hu_MenuPagePtr("Main"));
780
781 int y = 0;
782
783 page->addWidget(new ButtonWidget("Load Game"))
784 .setFixedY(y)
785 .setShortcut('l')
786 .setFont(MENU_FONT1)
787 .setAction(Widget::Deactivated, Hu_MenuSelectLoadGame)
788 .setAction(Widget::FocusGained, Hu_MenuDefaultFocusAction);
789
790 y += FIXED_LINE_HEIGHT;
791
792 page->addWidget(new ButtonWidget("Save Game"))
793 .setFixedY(y)
794 .setShortcut('s')
795 .setFont(MENU_FONT1)
796 .setAction(Widget::Deactivated, Hu_MenuSelectSaveGame)
797 .setAction(Widget::FocusGained, Hu_MenuDefaultFocusAction);
798 }
799 #endif
800
deleteGameSave(String slotId)801 static void deleteGameSave(String slotId)
802 {
803 DD_Executef(true, "deletegamesave %s", slotId.toLatin1().constData());
804 }
805
Hu_MenuLoadSlotCommandResponder(Widget & wi,menucommand_e cmd)806 int Hu_MenuLoadSlotCommandResponder(Widget &wi, menucommand_e cmd)
807 {
808 LineEditWidget &edit = wi.as<LineEditWidget>();
809 if(cmd == MCMD_DELETE && !wi.isDisabled() && wi.isFocused() && !wi.isActive())
810 {
811 deleteGameSave(edit.userValue().toString());
812 return true;
813 }
814 if(cmd == MCMD_SELECT && !wi.isDisabled() && wi.isFocused())
815 {
816 S_LocalSound(SFX_MENU_ACCEPT, NULL);
817 if(!wi.isActive())
818 {
819 wi.setFlags(Widget::Active);
820 wi.execAction(Widget::Activated);
821 }
822
823 wi.setFlags(Widget::Active, UnsetFlags);
824 wi.execAction(Widget::Deactivated);
825 return true;
826 }
827 return false; // Not eaten.
828 }
829
Hu_MenuSaveSlotCommandResponder(Widget & wi,menucommand_e cmd)830 int Hu_MenuSaveSlotCommandResponder(Widget &wi, menucommand_e cmd)
831 {
832 LineEditWidget &edit = wi.as<LineEditWidget>();
833 if(cmd == MCMD_DELETE && !wi.isDisabled() && wi.isFocused() && !wi.isActive())
834 {
835 deleteGameSave(edit.userValue().toString());
836 return true;
837 }
838 return edit.handleCommand(cmd);
839 }
840
Hu_MenuInitLoadGameAndSaveGamePages()841 void Hu_MenuInitLoadGameAndSaveGamePages()
842 {
843 #if __JDOOM__ || __JDOOM64__
844 Vector2i const origin(50, 54);
845 #else
846 Vector2i const origin(40, 30);
847 #endif
848 Widget::Flags const saveSlotObjectIds[NUMSAVESLOTS] = {
849 Widget::Id0, Widget::Id1, Widget::Id2, Widget::Id3, Widget::Id4, Widget::Id5,
850 #if !__JHEXEN__
851 Widget::Id6, Widget::Id7
852 #endif
853 };
854
855 Page *loadPage = Hu_MenuAddPage(new Page("LoadGame", origin, Page::FixedLayout | Page::NoScroll, Hu_MenuDrawLoadGamePage));
856 loadPage->setPredefinedFont(MENU_FONT1, FID(GF_FONTA));
857 loadPage->setPreviousPage(Hu_MenuPagePtr("Main"));
858
859 int y = 0;
860 int i = 0;
861 for(; i < NUMSAVESLOTS; ++i, y += FIXED_LINE_HEIGHT)
862 {
863 loadPage->addWidget(new LineEditWidget)
864 .setMaxLength(24)
865 .setEmptyText(GET_TXT(TXT_EMPTYSTRING))
866 .setFixedY(y)
867 .setFlags(saveSlotObjectIds[i] | Widget::Disabled)
868 .setShortcut('0' + i)
869 .setCommandResponder(Hu_MenuLoadSlotCommandResponder)
870 .setUserValue(String::number(i))
871 .setUserValue2(int(saveSlotObjectIds[i]))
872 .setAction(Widget::Deactivated, Hu_MenuSelectLoadSlot)
873 .setAction(Widget::FocusLost, Hu_MenuDefaultFocusAction);
874 }
875
876 Page *savePage = Hu_MenuAddPage(new Page("SaveGame", origin, Page::FixedLayout | Page::NoScroll, Hu_MenuDrawSaveGamePage));
877 savePage->setPredefinedFont(MENU_FONT1, FID(GF_FONTA));
878 savePage->setPreviousPage(Hu_MenuPagePtr("Main"));
879
880 y = 0;
881 i = 0;
882 for(; i < NUMSAVESLOTS; ++i, y += FIXED_LINE_HEIGHT)
883 {
884 savePage->addWidget(new LineEditWidget)
885 .setMaxLength(24)
886 .setEmptyText(GET_TXT(TXT_EMPTYSTRING))
887 .setFixedY(y)
888 .setFlags(saveSlotObjectIds[i])
889 .setShortcut('0' + i)
890 .setCommandResponder(Hu_MenuSaveSlotCommandResponder)
891 .setUserValue(String::number(i))
892 .setUserValue2(int(saveSlotObjectIds[i]))
893 .setAction(Widget::Deactivated, Hu_MenuSelectSaveSlot)
894 .setAction(Widget::Activated, Hu_MenuSaveSlotEdit)
895 .setAction(Widget::FocusLost, Hu_MenuDefaultFocusAction);
896 }
897 }
898
Hu_MenuInitOptionsPage()899 void Hu_MenuInitOptionsPage()
900 {
901 #if __JHERETIC__ || __JHEXEN__
902 Vector2i const origin(110, 45);
903 #else
904 Vector2i const origin(110, 63);
905 #endif
906
907 Page *page = Hu_MenuAddPage(new Page("Options", origin, Page::NoScroll, Hu_MenuDrawOptionsPage));
908 page->setPredefinedFont(MENU_FONT1, FID(GF_FONTA));
909 page->setPreviousPage(Hu_MenuPagePtr("Main"));
910
911 page->addWidget(new ButtonWidget("End Game"))
912 .setShortcut('e')
913 .setFont(MENU_FONT1)
914 .setGroup(1)
915 .setAction(Widget::Deactivated, Hu_MenuSelectEndGame)
916 .setAction(Widget::FocusGained, Hu_MenuDefaultFocusAction);
917
918 page->addWidget(new ButtonWidget("Player Setup"))
919 .setShortcut('p')
920 .setGroup(1)
921 .setFont(MENU_FONT1)
922 .setUserValue(String("PlayerSetup"))
923 .setAction(Widget::Deactivated, Hu_MenuActionSetActivePage)
924 .setAction(Widget::FocusGained, Hu_MenuDefaultFocusAction);
925
926 page->addWidget(new ButtonWidget("Show Taskbar"))
927 .setShortcut('t')
928 .setFont(MENU_FONT1)
929 .setGroup(1)
930 .setAction(Widget::Deactivated, Hu_MenuSelectControlPanelLink)
931 .setAction(Widget::FocusGained, Hu_MenuDefaultFocusAction);
932
933 page->addWidget(new ButtonWidget("Sound"))
934 .setShortcut('s')
935 .setFont(MENU_FONT1)
936 .setUserValue(String("SoundOptions"))
937 .setAction(Widget::Deactivated, Hu_MenuActionSetActivePage)
938 .setAction(Widget::FocusGained, Hu_MenuDefaultFocusAction);
939
940 page->addWidget(new ButtonWidget("Controls"))
941 .setShortcut('c')
942 .setFont(MENU_FONT1)
943 .setUserValue(String("ControlOptions"))
944 .setAction(Widget::Deactivated, Hu_MenuActionSetActivePage)
945 .setAction(Widget::FocusGained, Hu_MenuDefaultFocusAction);
946
947 page->addWidget(new ButtonWidget("Gameplay"))
948 .setShortcut('g')
949 .setFont(MENU_FONT1)
950 .setUserValue(String("GameplayOptions"))
951 .setAction(Widget::Deactivated, Hu_MenuActionSetActivePage)
952 .setAction(Widget::FocusGained, Hu_MenuDefaultFocusAction);
953
954 page->addWidget(new ButtonWidget("HUD"))
955 .setShortcut('h')
956 .setFont(MENU_FONT1)
957 .setUserValue(String("HUDOptions"))
958 .setAction(Widget::Deactivated, Hu_MenuActionSetActivePage)
959 .setAction(Widget::FocusGained, Hu_MenuDefaultFocusAction);
960
961 page->addWidget(new ButtonWidget("Automap"))
962 .setShortcut('a')
963 .setFont(MENU_FONT1)
964 .setUserValue(String("AutomapOptions"))
965 .setAction(Widget::Deactivated, Hu_MenuActionSetActivePage)
966 .setAction(Widget::FocusGained, Hu_MenuDefaultFocusAction);
967
968 page->addWidget(new ButtonWidget("Weapons"))
969 .setShortcut('w')
970 .setFont(MENU_FONT1)
971 .setUserValue(String("WeaponOptions"))
972 .setAction(Widget::Deactivated, Hu_MenuActionSetActivePage)
973 .setAction(Widget::FocusGained, Hu_MenuDefaultFocusAction);
974
975 #if __JHERETIC__ || __JHEXEN__
976 page->addWidget(new ButtonWidget("Inventory"))
977 .setShortcut('i')
978 .setFont(MENU_FONT1)
979 .setUserValue(String("InventoryOptions"))
980 .setAction(Widget::Deactivated, Hu_MenuActionSetActivePage)
981 .setAction(Widget::FocusGained, Hu_MenuDefaultFocusAction);
982 #endif
983
984 page->addWidget(new ButtonWidget("Savegame"))
985 .setShortcut('s')
986 .setFont(MENU_FONT1)
987 .setUserValue(String("SaveOptions"))
988 .setAction(Widget::Deactivated, Hu_MenuActionSetActivePage)
989 .setAction(Widget::FocusGained, Hu_MenuDefaultFocusAction);
990 }
991
Hu_MenuInitGameplayOptionsPage()992 void Hu_MenuInitGameplayOptionsPage()
993 {
994 #if __JHEXEN__
995 Vector2i const origin(88, 25);
996 #elif __JHERETIC__
997 Vector2i const origin(30, 40);
998 #else
999 Vector2i const origin(30, 40);
1000 #endif
1001
1002 Page *page = Hu_MenuAddPage(new Page("GameplayOptions", origin));
1003 page->setLeftColumnWidth(.75f);
1004 page->setTitle("Gameplay Options");
1005 page->setPredefinedFont(MENU_FONT1, FID(GF_FONTA));
1006 page->setPreviousPage(Hu_MenuPagePtr("Options"));
1007
1008 page->addWidget(new LabelWidget("Always Run")).setLeft();
1009 page->addWidget(new CVarToggleWidget("ctl-run")).setRight()
1010 .setShortcut('r');
1011
1012 page->addWidget(new LabelWidget("Use LookSpring")).setLeft();
1013 page->addWidget(new CVarToggleWidget("ctl-look-spring")).setRight()
1014 .setShortcut('l');
1015
1016 page->addWidget(new LabelWidget("Disable AutoAim")).setLeft();
1017 page->addWidget(new CVarToggleWidget("ctl-aim-noauto")).setRight()
1018 .setShortcut('a');
1019
1020 #if __JDOOM__ || __JHERETIC__ || __JDOOM64__
1021 page->addWidget(new LabelWidget("Allow Jumping")).setLeft();
1022 page->addWidget(new CVarToggleWidget("player-jump")).setRight()
1023 .setShortcut('j');
1024 #endif
1025
1026 #if __JDOOM__
1027 page->addWidget(new LabelWidget("Fast Monsters")).setLeft();
1028 page->addWidget(new CVarToggleWidget("game-monsters-fast")).setRight()
1029 .setShortcut('f');
1030 #endif
1031
1032 #if __JDOOM64__
1033 page->addWidget(new LabelWidget("Weapon Recoil")).setLeft();
1034 page->addWidget(new CVarToggleWidget("player-weapon-recoil")).setRight();
1035 #endif
1036
1037 #if __JDOOM__ || __JHERETIC__ || __JDOOM64__
1038 page->addWidget(new LabelWidget("Compatibility"))
1039 .setLeft()
1040 .setGroup(1)
1041 .setColor(MENU_COLOR2);
1042
1043 # if __JDOOM__ || __JDOOM64__
1044 page->addWidget(new LabelWidget("Any Boss Trigger 666")).setLeft()
1045 .setGroup(1);
1046 page->addWidget(new CVarToggleWidget("game-anybossdeath666")).setRight()
1047 .setGroup(1)
1048 .setShortcut('b');
1049
1050 # if !__JDOOM64__
1051 page->addWidget(new LabelWidget("Av Resurrects Ghosts"))
1052 .setLeft()
1053 .setGroup(1);
1054 page->addWidget(new CVarToggleWidget("game-raiseghosts"))
1055 .setRight()
1056 .setGroup(1)
1057 .setShortcut('g');
1058 # if __JDOOM__
1059 page->addWidget(new LabelWidget("VileChase uses Av radius"))
1060 .setLeft()
1061 .setGroup(1);
1062 page->addWidget(new CVarToggleWidget("game-vilechase-usevileradius"))
1063 .setRight()
1064 .setGroup(1)
1065 .setShortcut('g');
1066 # endif
1067 # endif // !__JDOOM64__
1068
1069 page->addWidget(new LabelWidget("PE Limited To 21 Lost Souls"))
1070 .setLeft()
1071 .setGroup(1);
1072 page->addWidget(new CVarToggleWidget("game-maxskulls"))
1073 .setRight()
1074 .setGroup(1)
1075 .setShortcut('p');
1076
1077 page->addWidget(new LabelWidget("LS Can Get Stuck Inside Walls"))
1078 .setLeft()
1079 .setGroup(1);
1080
1081 page->addWidget(new CVarToggleWidget("game-skullsinwalls"))
1082 .setRight()
1083 .setGroup(1);
1084 # endif // __JDOOM__ || __JDOOM64__
1085
1086 page->addWidget(new LabelWidget("Monsters Fly Over Obstacles"))
1087 .setLeft()
1088 .setGroup(1);
1089 page->addWidget(new CVarToggleWidget("game-monsters-floatoverblocking"))
1090 .setRight()
1091 .setGroup(1);
1092
1093 page->addWidget(new LabelWidget("Monsters Can Get Stuck\n In Doors"))
1094 .setLeft()
1095 .setGroup(1);
1096 page->addWidget(new CVarToggleWidget("game-monsters-stuckindoors"))
1097 .setRight()
1098 .setGroup(1)
1099 .setShortcut('d');
1100
1101 page->addWidget(new LabelWidget("Some Objects Never Hang\n Over Ledges"))
1102 .setLeft()
1103 .setGroup(1);
1104 page->addWidget(new CVarToggleWidget("game-objects-neverhangoverledges"))
1105 .setRight()
1106 .setGroup(1)
1107 .setShortcut('h');
1108
1109 page->addWidget(new LabelWidget("Objects Fall Under Own Weight"))
1110 .setLeft()
1111 .setGroup(1);
1112 page->addWidget(new CVarToggleWidget("game-objects-falloff"))
1113 .setRight()
1114 .setGroup(1)
1115 .setShortcut('f');
1116
1117 #if __JDOOM__ || __JDOOM64__
1118 page->addWidget(new LabelWidget("All Crushed Objects\n Become A Pile Of Gibs"))
1119 .setLeft()
1120 .setGroup(1);
1121 page->addWidget(new CVarToggleWidget("game-objects-gibcrushednonbleeders"))
1122 .setRight()
1123 .setGroup(1)
1124 .setShortcut('g');
1125 #endif
1126
1127 page->addWidget(new LabelWidget("Corpses Slide Down Stairs"))
1128 .setLeft()
1129 .setGroup(1);
1130
1131 page->addWidget(new CVarToggleWidget("game-corpse-sliding"))
1132 .setRight()
1133 .setGroup(1)
1134 .setShortcut('s');
1135
1136 page->addWidget(new LabelWidget("Use Doom's Clipping\n Code Exactly"))
1137 .setLeft()
1138 .setGroup(1);
1139
1140 page->addWidget(new CVarToggleWidget("game-objects-clipping"))
1141 .setRight()
1142 .setGroup(1)
1143 .setShortcut('c');
1144
1145 page->addWidget(new LabelWidget(" ^If Not NorthOnly WallRunning"))
1146 .setLeft()
1147 .setGroup(1);
1148 page->addWidget(new CVarToggleWidget("game-player-wallrun-northonly"))
1149 .setRight()
1150 .setGroup(1)
1151 .setShortcut('w');
1152
1153 page->addWidget(new LabelWidget("Pushable Speed Limit"))
1154 .setLeft()
1155 .setGroup(1);
1156 page->addWidget(new CVarToggleWidget("game-objects-pushable-limit"))
1157 .setRight()
1158 .setGroup(1)
1159 .setShortcut('p');
1160
1161 # if __JDOOM__ || __JDOOM64__
1162
1163 page->addWidget(new LabelWidget("Zombie Players Can\n Exit Maps")).setLeft()
1164 .setGroup(1);
1165 page->addWidget(new CVarToggleWidget("game-zombiescanexit")).setRight()
1166 .setGroup(1)
1167 .setShortcut('e');
1168
1169 page->addWidget(new LabelWidget("Fix Ouch Face")).setLeft()
1170 .setGroup(1);
1171 page->addWidget(new CVarToggleWidget("hud-face-ouchfix")).setRight()
1172 .setGroup(1);
1173
1174 page->addWidget(new LabelWidget("Fix Weapon Slot Display")).setLeft()
1175 .setGroup(1);
1176 page->addWidget(new CVarToggleWidget("hud-status-weaponslots-ownedfix")).setRight()
1177 .setGroup(1);
1178
1179 # endif // __JDOOM__ || __JDOOM64__
1180 #endif // __JDOOM__ || __JHERETIC__ || __JDOOM64__
1181
1182 #if __JHERETIC__
1183 page->addWidget(new LabelWidget("Powered Staff Damages Ghosts"))
1184 .setLeft()
1185 .setGroup(1);
1186 page->addWidget(new CVarToggleWidget("player-weapon-staff-powerghostdamage"))
1187 .setRight()
1188 .setGroup(1)
1189 .setShortcut('g');
1190 #endif
1191
1192 page->addWidget(new LabelWidget("Vanilla Switch Sound\n Positioning"))
1193 .setLeft()
1194 .setGroup(1);
1195 page->addWidget(new CVarToggleWidget("sound-switch-origin"))
1196 .setRight()
1197 .setGroup(1)
1198 .setShortcut('v');
1199 }
1200
Hu_MenuInitHUDOptionsPage()1201 void Hu_MenuInitHUDOptionsPage()
1202 {
1203 #if __JDOOM__ || __JDOOM64__
1204 Vector2i const origin(97, 40);
1205 #else
1206 Vector2i const origin(97, 28);
1207 #endif
1208
1209 Page *page = Hu_MenuAddPage(new Page("HudOptions", origin));
1210 page->setTitle("HUD Options");
1211 page->setLeftColumnWidth(.45f);
1212 page->setPredefinedFont(MENU_FONT1, FID(GF_FONTA));
1213 page->setPreviousPage(Hu_MenuPagePtr("Options"));
1214
1215 page->addWidget(new LabelWidget("View Size")).setLeft();
1216
1217 page->addWidget(new CVarSliderWidget("view-size"))
1218 #if __JDOOM64__
1219 .setRange(3, 11, 1)
1220 #else
1221 .setRange(3, 13, 1)
1222 #endif
1223 .setFloatMode(false)
1224 .setRight();
1225
1226 page->addWidget(new LabelWidget("Messages"))
1227 .setGroup(2)
1228 .setColor(MENU_COLOR2);
1229
1230 page->addWidget(new LabelWidget("Shown"))
1231 .setLeft()
1232 .setGroup(2);
1233 page->addWidget(new CVarToggleWidget("msg-show"))
1234 .setRight()
1235 .setGroup(2)
1236 .setShortcut('m');
1237
1238 page->addWidget(new LabelWidget("Uptime"))
1239 .setLeft()
1240 .setGroup(2);
1241 page->addWidget(new CVarTextualSliderWidget("msg-uptime", 0, 60, 1))
1242 .setEmptyText("Disabled")
1243 .setOnethSuffix(" second")
1244 .setNthSuffix(" seconds")
1245 .setRight()
1246 .setGroup(2);
1247
1248 page->addWidget(new LabelWidget("Size"))
1249 .setLeft()
1250 .setGroup(2);
1251 page->addWidget(new CVarSliderWidget("msg-scale"))
1252 .setRight()
1253 .setGroup(2);
1254
1255 page->addWidget(new LabelWidget("Color"))
1256 .setLeft()
1257 .setGroup(2);
1258 page->addWidget(new CVarColorEditWidget("msg-color-r", "msg-color-g", "msg-color-b"))
1259 .setRight()
1260 .setGroup(2)
1261 .setAction(Widget::Deactivated, CVarColorEditWidget_UpdateCVar)
1262 .setAction(Widget::Activated, Hu_MenuActivateColorWidget);
1263
1264 page->addWidget(new LabelWidget("Crosshair"))
1265 .setGroup(3)
1266 .setColor(MENU_COLOR2);
1267
1268 page->addWidget(new LabelWidget("Symbol"))
1269 .setGroup(3)
1270 .setLeft()
1271 .setShortcut('c');
1272 page->addWidget(new CVarInlineListWidget("view-cross-type"))
1273 .addItems(ListWidget::Items() << new ListWidgetItem("None", 0)
1274 << new ListWidgetItem("Cross", 1)
1275 << new ListWidgetItem("Twin Angles", 2)
1276 << new ListWidgetItem("Square", 3)
1277 << new ListWidgetItem("Open Square", 4)
1278 << new ListWidgetItem("Angle", 5))
1279 .setGroup(3)
1280 .setRight();
1281
1282 page->addWidget(new LabelWidget("Size"))
1283 .setLeft()
1284 .setGroup(3);
1285 page->addWidget(new CVarSliderWidget("view-cross-size"))
1286 .setRight()
1287 .setGroup(3);
1288
1289 page->addWidget(new LabelWidget("Thickness"))
1290 .setLeft()
1291 .setGroup(3);
1292 page->addWidget(new CVarSliderWidget("view-cross-width", .5f, 5, .5f))
1293 .setRight()
1294 .setGroup(3);
1295
1296 page->addWidget(new LabelWidget("Angle"))
1297 .setLeft()
1298 .setGroup(3);
1299 page->addWidget(new CVarSliderWidget("view-cross-angle", 0.0f, 1.0f, 0.0625f))
1300 .setRight()
1301 .setGroup(3);
1302
1303 page->addWidget(new LabelWidget("Opacity"))
1304 .setLeft()
1305 .setGroup(3);
1306 page->addWidget(new CVarSliderWidget("view-cross-a"))
1307 .setRight()
1308 .setGroup(3);
1309
1310 page->addWidget(new LabelWidget("Color"))
1311 .setLeft()
1312 .setGroup(3);
1313 page->addWidget(new CVarColorEditWidget("view-cross-r", "view-cross-g", "view-cross-b"))
1314 .setRight()
1315 .setGroup(3)
1316 .setAction(Widget::Deactivated, CVarColorEditWidget_UpdateCVar)
1317 .setAction(Widget::Activated, Hu_MenuActivateColorWidget);
1318
1319 page->addWidget(new LabelWidget("Vitality Color"))
1320 .setLeft()
1321 .setGroup(3);
1322 page->addWidget(new CVarToggleWidget("view-cross-vitality"))
1323 .setRight()
1324 .setGroup(3);
1325
1326 page->addWidget(new LabelWidget(" When Dead"))
1327 .setLeft()
1328 .setGroup(3);
1329 page->addWidget(new CVarColorEditWidget("view-cross-dead-r", "view-cross-dead-g", "view-cross-dead-b"))
1330 .setRight()
1331 .setGroup(3)
1332 .setAction(Widget::Deactivated, CVarColorEditWidget_UpdateCVar)
1333 .setAction(Widget::Activated, Hu_MenuActivateColorWidget);
1334
1335 page->addWidget(new LabelWidget(" Full Health"))
1336 .setLeft()
1337 .setGroup(3);
1338
1339 page->addWidget(new CVarColorEditWidget("view-cross-live-r", "view-cross-live-g", "view-cross-live-b"))
1340 .setRight()
1341 .setGroup(3)
1342 .setAction(Widget::Deactivated, CVarColorEditWidget_UpdateCVar)
1343 .setAction(Widget::Activated, Hu_MenuActivateColorWidget);
1344
1345 #if __JDOOM__ || __JHERETIC__ || __JHEXEN__
1346 page->addWidget(new LabelWidget("Statusbar"))
1347 .setGroup(4)
1348 .setColor(MENU_COLOR2);
1349
1350 page->addWidget(new LabelWidget("Size"))
1351 .setLeft()
1352 .setGroup(4);
1353 page->addWidget(new CVarSliderWidget("hud-status-size"))
1354 .setRight()
1355 .setGroup(4);
1356
1357 page->addWidget(new LabelWidget("Opacity"))
1358 .setLeft()
1359 .setGroup(4);
1360 page->addWidget(new CVarSliderWidget("hud-status-alpha"))
1361 .setRight()
1362 .setGroup(4);
1363
1364 #if __JDOOM__
1365 page->addWidget(new LabelWidget("Single Key Display")).setLeft().setGroup(4);
1366 page->addWidget(new CVarToggleWidget("hud-keys-combine")).setRight().setGroup(4);
1367 #endif
1368
1369 page->addWidget(new LabelWidget("AutoHide Status"))
1370 .setLeft()
1371 .setGroup(4);
1372 page->addWidget(new CVarTextualSliderWidget("hud-timer", 0, 60, 1))
1373 .setEmptyText("Disabled")
1374 .setOnethSuffix(" second")
1375 .setNthSuffix(" seconds")
1376 .setRight()
1377 .setGroup(4);
1378
1379 page->addWidget(new LabelWidget("Status UnHide Events"))
1380 .setGroup(1)
1381 .setColor(MENU_COLOR2);
1382
1383 page->addWidget(new LabelWidget("Receive Damage"))
1384 .setLeft()
1385 .setGroup(1);
1386 page->addWidget(new CVarToggleWidget("hud-unhide-damage"))
1387 .setRight()
1388 .setGroup(1);
1389
1390 page->addWidget(new LabelWidget("Pickup Health"))
1391 .setLeft()
1392 .setGroup(1);
1393 page->addWidget(new CVarToggleWidget("hud-unhide-pickup-health"))
1394 .setRight()
1395 .setGroup(1);
1396
1397 page->addWidget(new LabelWidget("Pickup Armor"))
1398 .setLeft()
1399 .setGroup(1);
1400 page->addWidget(new CVarToggleWidget("hud-unhide-pickup-armor"))
1401 .setRight()
1402 .setGroup(1);
1403
1404 page->addWidget(new LabelWidget("Pickup Powerup"))
1405 .setLeft()
1406 .setGroup(1);
1407 page->addWidget(new CVarToggleWidget("hud-unhide-pickup-powerup"))
1408 .setRight()
1409 .setGroup(1);
1410
1411 page->addWidget(new LabelWidget("Pickup Weapon"))
1412 .setLeft()
1413 .setGroup(1);
1414 page->addWidget(new CVarToggleWidget("hud-unhide-pickup-weapon"))
1415 .setRight()
1416 .setGroup(1);
1417
1418 page->addWidget(new LabelWidget)
1419 #if __JHEXEN__
1420 .setText("Pickup Mana")
1421 #else
1422 .setText("Pickup Ammo")
1423 #endif
1424 .setGroup(1)
1425 .setLeft();
1426 page->addWidget(new CVarToggleWidget("hud-unhide-pickup-ammo"))
1427 .setRight()
1428 .setGroup(1);
1429
1430 page->addWidget(new LabelWidget("Pickup Key"))
1431 .setLeft()
1432 .setGroup(1);
1433 page->addWidget(new CVarToggleWidget("hud-unhide-pickup-key"))
1434 .setRight()
1435 .setGroup(1);
1436
1437 #if __JHERETIC__ || __JHEXEN__
1438 page->addWidget(new LabelWidget("Pickup Item"))
1439 .setLeft()
1440 .setGroup(1);
1441
1442 page->addWidget(new CVarToggleWidget("hud-unhide-pickup-invitem"))
1443 .setRight()
1444 .setGroup(1);
1445 #endif // __JHERETIC__ || __JHEXEN__
1446
1447 #endif // __JDOOM__ || __JHERETIC__ || __JHEXEN__
1448
1449 #if __JDOOM__ || __JDOOM64__ || __JHERETIC__
1450 page->addWidget(new LabelWidget("Counters"))
1451 .setGroup(5)
1452 .setColor(MENU_COLOR2);
1453
1454 page->addWidget(new LabelWidget("Items"))
1455 .setLeft()
1456 .setGroup(5);
1457 page->addWidget(new CVarInlineListWidget("hud-cheat-counter", CCH_ITEMS | CCH_ITEMS_PRCNT))
1458 .addItems(ListWidget::Items() << new ListWidgetItem("Hidden", 0)
1459 << new ListWidgetItem("Count", CCH_ITEMS)
1460 << new ListWidgetItem("Percent", CCH_ITEMS_PRCNT)
1461 << new ListWidgetItem("Count+Percent", CCH_ITEMS | CCH_ITEMS_PRCNT))
1462 .setRight()
1463 .setGroup(5)
1464 .setShortcut('i');
1465
1466 page->addWidget(new LabelWidget("Kills"))
1467 .setLeft()
1468 .setGroup(5);
1469
1470 page->addWidget(new CVarInlineListWidget("hud-cheat-counter", CCH_KILLS | CCH_KILLS_PRCNT))
1471 .addItems(ListWidget::Items() << new ListWidgetItem("Hidden", 0)
1472 << new ListWidgetItem("Count", CCH_KILLS)
1473 << new ListWidgetItem("Percent", CCH_KILLS_PRCNT)
1474 << new ListWidgetItem("Count+Percent", CCH_KILLS | CCH_KILLS_PRCNT))
1475 .setRight()
1476 .setGroup(5)
1477 .setShortcut('k');
1478
1479 page->addWidget(new LabelWidget("Secrets"))
1480 .setLeft()
1481 .setGroup(5);
1482 page->addWidget(new CVarInlineListWidget("hud-cheat-counter", CCH_SECRETS | CCH_SECRETS_PRCNT))
1483 .addItems(ListWidget::Items() << new ListWidgetItem("Hidden", 0)
1484 << new ListWidgetItem("Count", CCH_SECRETS)
1485 << new ListWidgetItem("Percent", CCH_SECRETS_PRCNT)
1486 << new ListWidgetItem("Count+Percent", CCH_SECRETS | CCH_SECRETS_PRCNT))
1487 .setGroup(5)
1488 .setRight()
1489 .setShortcut('s');
1490
1491 page->addWidget(new LabelWidget("Automap Only"))
1492 .setLeft()
1493 .setGroup(5);
1494 page->addWidget(new CVarToggleWidget("hud-cheat-counter-show-mapopen"))
1495 .setRight()
1496 .setGroup(5);
1497
1498 page->addWidget(new LabelWidget("Size"))
1499 .setLeft()
1500 .setGroup(5);
1501 page->addWidget(new CVarSliderWidget("hud-cheat-counter-scale"))
1502 .setRight()
1503 .setGroup(5);
1504
1505 #endif // __JDOOM__ || __JDOOM64__ || __JHERETIC__
1506
1507 page->addWidget(new LabelWidget("Fullscreen"))
1508 .setGroup(6)
1509 .setColor(MENU_COLOR2);
1510
1511 page->addWidget(new LabelWidget("Size"))
1512 .setLeft()
1513 .setGroup(6);
1514 page->addWidget(new CVarSliderWidget("hud-scale"))
1515 .setRight()
1516 .setGroup(6);
1517
1518 page->addWidget(new LabelWidget("Text Color"))
1519 .setLeft()
1520 .setGroup(6);
1521 page->addWidget(new CVarColorEditWidget("hud-color-r", "hud-color-g", "hud-color-b", "hud-color-a", Vector4f(), true))
1522 .setRight()
1523 .setGroup(6)
1524 .setAction(Widget::Deactivated, CVarColorEditWidget_UpdateCVar)
1525 .setAction(Widget::Activated, Hu_MenuActivateColorWidget);
1526
1527 #if __JHEXEN__
1528
1529 page->addWidget(new LabelWidget("Show Mana"))
1530 .setLeft()
1531 .setGroup(6);
1532
1533 page->addWidget(new CVarToggleWidget("hud-mana"))
1534 .setRight()
1535 .setGroup(6);
1536
1537 #endif // __JHEXEN__
1538
1539 #if __JDOOM__ || __JDOOM64__ || __JHERETIC__
1540
1541 page->addWidget(new LabelWidget("Show Ammo"))
1542 .setLeft()
1543 .setGroup(6);
1544 page->addWidget(new CVarToggleWidget("hud-ammo"))
1545 .setRight()
1546 .setGroup(6)
1547 .setShortcut('a');
1548
1549 page->addWidget(new LabelWidget("Show Armor"))
1550 .setLeft()
1551 .setGroup(6);
1552 page->addWidget(new CVarToggleWidget("hud-armor"))
1553 .setRight()
1554 .setGroup(6)
1555 .setShortcut('r');
1556
1557 #endif // __JDOOM__ || __JDOOM64__ || __JHERETIC__
1558
1559 #if __JDOOM64__
1560
1561 page->addWidget(new LabelWidget("Show PowerKeys"))
1562 .setLeft()
1563 .setGroup(6);
1564 page->addWidget(new CVarToggleWidget("hud-power"))
1565 .setRight()
1566 .setGroup(6)
1567 .setShortcut('p');
1568
1569 #endif // __JDOOM64__
1570
1571 #if __JDOOM__
1572
1573 page->addWidget(new LabelWidget("Show Status"))
1574 .setLeft()
1575 .setGroup(6);
1576 page->addWidget(new CVarToggleWidget("hud-face"))
1577 .setRight()
1578 .setGroup(6)
1579 .setShortcut('f');
1580
1581 #endif // __JDOOM__
1582
1583 page->addWidget(new LabelWidget("Show Health"))
1584 .setLeft()
1585 .setGroup(6);
1586 page->addWidget(new CVarToggleWidget("hud-health"))
1587 .setRight()
1588 .setGroup(6)
1589 .setShortcut('h');
1590
1591 #if __JDOOM__ || __JDOOM64__ || __JHERETIC__
1592
1593 page->addWidget(new LabelWidget("Show Keys"))
1594 .setLeft()
1595 .setGroup(6);
1596 page->addWidget(new CVarToggleWidget("hud-keys"))
1597 .setRight()
1598 .setGroup(6);
1599
1600 #endif // __JDOOM__ || __JDOOM64__ || __JHERETIC__
1601
1602 #if __JHERETIC__ || __JHEXEN__
1603
1604 page->addWidget(new LabelWidget("Show Ready-Item"))
1605 .setLeft()
1606 .setGroup(6);
1607 page->addWidget(new CVarToggleWidget("hud-currentitem"))
1608 .setRight()
1609 .setGroup(6);
1610
1611 #endif // __JHERETIC__ || __JHEXEN__
1612 }
1613
Hu_MenuInitAutomapOptionsPage()1614 void Hu_MenuInitAutomapOptionsPage()
1615 {
1616 #if __JHERETIC__ || __JHEXEN__
1617 const Vector2i origin(32, 28);
1618 #else
1619 const Vector2i origin(70, 40);
1620 #endif
1621
1622 Page *page = Hu_MenuAddPage(new Page("AutomapOptions", origin));
1623 page->setLeftColumnWidth(.55f);
1624 page->setTitle("Automap Options");
1625 page->setPredefinedFont(MENU_FONT1, FID(GF_FONTA));
1626 page->setPreviousPage(Hu_MenuPagePtr("Options"));
1627
1628 page->addWidget(new LabelWidget("Rotation"))
1629 .setLeft();
1630 {
1631 auto *tgl = new CVarToggleWidget("map-rotate");
1632 tgl->setRight();
1633 tgl->setShortcut('r');
1634 tgl->setStateChangeCallback([](CVarToggleWidget::State state) {
1635 G_SetAutomapRotateMode(state == CVarToggleWidget::Down);
1636 });
1637 page->addWidget(tgl);
1638 }
1639
1640 page->addWidget(new LabelWidget("Always Update Map"))
1641 .setLeft();
1642 page->addWidget(new CVarToggleWidget("map-neverobscure"))
1643 .setRight()
1644 .setShortcut('a')
1645 .setHelpInfo("Update map even when background is opaque");
1646
1647 #if !defined (__JDOOM64__)
1648 page->addWidget(new LabelWidget("HUD Display"))
1649 .setLeft();
1650 page->addWidget(new CVarInlineListWidget("map-huddisplay"))
1651 .addItems(ListWidget::Items() << new ListWidgetItem("None", 0)
1652 << new ListWidgetItem("Current", 1)
1653 << new ListWidgetItem("Statusbar", 2))
1654 .setRight()
1655 .setShortcut('h');
1656 #endif
1657
1658 page->addWidget(new LabelWidget("Appearance"))
1659 .setGroup(1)
1660 .setColor(MENU_COLOR2);
1661
1662 page->addWidget(new LabelWidget("Background Opacity"))
1663 .setLeft()
1664 .setGroup(1);
1665 page->addWidget(new CVarSliderWidget("map-opacity"))
1666 .setShortcut('o')
1667 .setRight()
1668 .setGroup(1);
1669
1670 page->addWidget(new LabelWidget("Line Opacity"))
1671 .setLeft()
1672 .setGroup(1);
1673 page->addWidget(new CVarSliderWidget("map-line-opacity"))
1674 .setShortcut('l')
1675 .setRight()
1676 .setGroup(1);
1677
1678 page->addWidget(new LabelWidget("Line Width"))
1679 .setLeft()
1680 .setGroup(1);
1681 page->addWidget(new CVarSliderWidget("map-line-width", 0.5f, 8.f))
1682 .setRight()
1683 .setGroup(1);
1684
1685 page->addWidget(new LabelWidget("Colored Doors"))
1686 .setLeft()
1687 .setGroup(1);
1688 page->addWidget(new CVarToggleWidget("map-door-colors"))
1689 .setRight()
1690 .setShortcut('d')
1691 .setGroup(1);
1692
1693 page->addWidget(new LabelWidget("Door Glow"))
1694 .setLeft()
1695 .setGroup(1);
1696 page->addWidget(new CVarSliderWidget("map-door-glow", 0, 200, 5))
1697 .setRight()
1698 .setShortcut('g')
1699 .setGroup(1);
1700
1701 page->addWidget(new LabelWidget("Use Custom Colors"))
1702 .setLeft()
1703 .setGroup(2);
1704 page->addWidget(new CVarInlineListWidget("map-customcolors"))
1705 .addItems(ListWidget::Items() << new ListWidgetItem("Never", 0)
1706 << new ListWidgetItem("Auto", 1)
1707 << new ListWidgetItem("Always", 2))
1708 .setRight()
1709 .setGroup(2);
1710
1711 page->addWidget(new LabelWidget("Wall"))
1712 .setLeft()
1713 .setGroup(2);
1714 page->addWidget(new CVarColorEditWidget("map-wall-r", "map-wall-g", "map-wall-b"))
1715 .setRight()
1716 .setShortcut('w')
1717 .setGroup(2)
1718 .setAction(Widget::Activated, Hu_MenuActivateColorWidget);
1719
1720 page->addWidget(new LabelWidget("Floor Height Change"))
1721 .setLeft()
1722 .setGroup(2);
1723 page->addWidget(new CVarColorEditWidget("map-wall-floorchange-r", "map-wall-floorchange-g", "map-wall-floorchange-b"))
1724 .setRight()
1725 .setShortcut('f')
1726 .setGroup(2)
1727 .setAction(Widget::Activated, Hu_MenuActivateColorWidget);
1728
1729 page->addWidget(new LabelWidget("Ceiling Height Change"))
1730 .setLeft()
1731 .setGroup(2);
1732 page->addWidget(new CVarColorEditWidget("map-wall-ceilingchange-r", "map-wall-ceilingchange-g", "map-wall-ceilingchange-b"))
1733 .setRight()
1734 .setGroup(2)
1735 .setAction(Widget::Activated, Hu_MenuActivateColorWidget);
1736
1737 page->addWidget(new LabelWidget("Unseen"))
1738 .setLeft()
1739 .setGroup(2);
1740 page->addWidget(new CVarColorEditWidget("map-wall-unseen-r", "map-wall-unseen-g", "map-wall-unseen-b"))
1741 .setRight()
1742 .setGroup(2)
1743 .setShortcut('u')
1744 .setAction(Widget::Activated, Hu_MenuActivateColorWidget);
1745
1746 page->addWidget(new LabelWidget("Thing"))
1747 .setLeft()
1748 .setGroup(2);
1749 page->addWidget(new CVarColorEditWidget("map-mobj-r", "map-mobj-g", "map-mobj-b"))
1750 .setRight()
1751 .setGroup(2)
1752 .setShortcut('t')
1753 .setAction(Widget::Activated, Hu_MenuActivateColorWidget);
1754
1755 page->addWidget(new LabelWidget("Background"))
1756 .setLeft()
1757 .setGroup(2);
1758 page->addWidget(new CVarColorEditWidget("map-background-r", "map-background-g", "map-background-b"))
1759 .setRight()
1760 .setGroup(2)
1761 .setShortcut('b')
1762 .setAction(Widget::Activated, Hu_MenuActivateColorWidget);
1763 }
1764
compareWeaponPriority(ListWidgetItem const * a,ListWidgetItem const * b)1765 static bool compareWeaponPriority(ListWidgetItem const *a, ListWidgetItem const *b)
1766 {
1767 int i = 0, aIndex = -1, bIndex = -1;
1768 do
1769 {
1770 if (cfg.common.weaponOrder[i] == a->userValue())
1771 {
1772 aIndex = i;
1773 }
1774 if (cfg.common.weaponOrder[i] == b->userValue())
1775 {
1776 bIndex = i;
1777 }
1778 } while(!(aIndex != -1 && bIndex != -1) && ++i < NUM_WEAPON_TYPES);
1779
1780 return aIndex < bIndex;
1781 }
1782
Hu_MenuInitWeaponsPage()1783 void Hu_MenuInitWeaponsPage()
1784 {
1785 #if __JDOOM__ || __JDOOM64__
1786 Vector2i const origin(78, 40);
1787 #elif __JHERETIC__
1788 Vector2i const origin(78, 26);
1789 #elif __JHEXEN__
1790 Vector2i const origin(78, 38);
1791 #endif
1792
1793 const struct {
1794 char const *text;
1795 weapontype_t data;
1796 } weaponOrder[NUM_WEAPON_TYPES+1] = {
1797 #if __JDOOM__ || __JDOOM64__
1798 { (char const *)TXT_WEAPON1, WT_FIRST },
1799 { (char const *)TXT_WEAPON2, WT_SECOND },
1800 { (char const *)TXT_WEAPON3, WT_THIRD },
1801 { (char const *)TXT_WEAPON4, WT_FOURTH },
1802 { (char const *)TXT_WEAPON5, WT_FIFTH },
1803 { (char const *)TXT_WEAPON6, WT_SIXTH },
1804 { (char const *)TXT_WEAPON7, WT_SEVENTH },
1805 { (char const *)TXT_WEAPON8, WT_EIGHTH },
1806 { (char const *)TXT_WEAPON9, WT_NINETH },
1807 # if __JDOOM64__
1808 { (char const *)TXT_WEAPON10, WT_TENTH },
1809 # endif
1810 #elif __JHERETIC__
1811 { (char const *)TXT_TXT_WPNSTAFF, WT_FIRST },
1812 { (char const *)TXT_TXT_WPNWAND, WT_SECOND },
1813 { (char const *)TXT_TXT_WPNCROSSBOW, WT_THIRD },
1814 { (char const *)TXT_TXT_WPNBLASTER, WT_FOURTH },
1815 { (char const *)TXT_TXT_WPNSKULLROD, WT_FIFTH },
1816 { (char const *)TXT_TXT_WPNPHOENIXROD, WT_SIXTH },
1817 { (char const *)TXT_TXT_WPNMACE, WT_SEVENTH },
1818 { (char const *)TXT_TXT_WPNGAUNTLETS, WT_EIGHTH },
1819 #elif __JHEXEN__
1820 /// @todo We should allow different weapon preferences per player-class.
1821 { "First", WT_FIRST },
1822 { "Second", WT_SECOND },
1823 { "Third", WT_THIRD },
1824 { "Fourth", WT_FOURTH },
1825 #endif
1826 { "", WT_NOCHANGE}
1827 };
1828
1829 Page *page = Hu_MenuAddPage(new Page("WeaponOptions", origin));
1830 page->setLeftColumnWidth(.5f);
1831 page->setTitle("Weapons Options");
1832 page->setPredefinedFont(MENU_FONT1, FID(GF_FONTA));
1833 page->setPreviousPage(Hu_MenuPagePtr("Options"));
1834
1835 page->addWidget(new LabelWidget("Priority Order"))
1836 .setColor(MENU_COLOR2);
1837
1838 ListWidget::Items weapItems;
1839 for (int i = 0; weaponOrder[i].data < NUM_WEAPON_TYPES; ++i)
1840 {
1841 char const *itemText = weaponOrder[i].text;
1842 if(itemText && (PTR2INT(itemText) > 0 && PTR2INT(itemText) < NUMTEXT))
1843 {
1844 itemText = GET_TXT(PTR2INT(itemText));
1845 }
1846 weapItems << new ListWidgetItem(itemText, weaponOrder[i].data);
1847 }
1848 qSort(weapItems.begin(), weapItems.end(), compareWeaponPriority);
1849 page->addWidget(new ListWidget)
1850 .addItems(weapItems)
1851 .setReorderingEnabled(true)
1852 .setHelpInfo("Use left/right to move weapon up/down")
1853 .setShortcut('p')
1854 .setColor(MENU_COLOR3)
1855 .setAction(Widget::Modified, Hu_MenuChangeWeaponPriority)
1856 .setAction(Widget::FocusGained, Hu_MenuDefaultFocusAction);
1857
1858 page->addWidget(new LabelWidget("Cycling"))
1859 .setGroup(1)
1860 .setColor(MENU_COLOR2);
1861
1862 page->addWidget(new LabelWidget("Use Priority Order"))
1863 .setLeft()
1864 .setGroup(1);
1865 page->addWidget(new CVarToggleWidget("player-weapon-nextmode"))
1866 .setRight()
1867 .setGroup(1)
1868 .setShortcut('o');
1869
1870 page->addWidget(new LabelWidget("Sequential"))
1871 .setLeft()
1872 .setGroup(1);
1873 page->addWidget(new CVarToggleWidget("player-weapon-cycle-sequential"))
1874 .setRight()
1875 .setGroup(1)
1876 .setShortcut('s');
1877
1878 page->addWidget(new LabelWidget("Autoswitch"))
1879 .setGroup(2)
1880 .setColor(MENU_COLOR2);
1881
1882 page->addWidget(new LabelWidget("Pickup Weapon"))
1883 .setLeft()
1884 .setGroup(2);
1885 page->addWidget(new CVarInlineListWidget("player-autoswitch"))
1886 .addItems(ListWidget::Items() << new ListWidgetItem("Never", 0)
1887 << new ListWidgetItem("If Better", 1)
1888 << new ListWidgetItem("Always", 2))
1889 .setGroup(2)
1890 .setRight()
1891 .setShortcut('w');
1892
1893 page->addWidget(new LabelWidget(" If Not Firing"))
1894 .setLeft()
1895 .setGroup(2);
1896 page->addWidget(new CVarToggleWidget("player-autoswitch-notfiring"))
1897 .setRight()
1898 .setGroup(2)
1899 .setShortcut('f');
1900
1901 page->addWidget(new LabelWidget("Pickup Ammo"))
1902 .setLeft()
1903 .setGroup(2);
1904 page->addWidget(new CVarInlineListWidget("player-autoswitch-ammo"))
1905 .addItems(ListWidget::Items() << new ListWidgetItem("Never", 0)
1906 << new ListWidgetItem("If Better", 1)
1907 << new ListWidgetItem("Always", 2))
1908 .setGroup(2)
1909 .setRight()
1910 .setShortcut('a');
1911
1912 #if __JDOOM__ || __JDOOM64__
1913
1914 page->addWidget(new LabelWidget("Pickup Beserk"))
1915 .setLeft()
1916 .setGroup(2);
1917 page->addWidget(new CVarToggleWidget("player-autoswitch-berserk"))
1918 .setRight()
1919 .setGroup(2)
1920 .setShortcut('b');
1921
1922 #endif
1923 }
1924
1925 #if __JHERETIC__ || __JHEXEN__
Hu_MenuInitInventoryOptionsPage()1926 void Hu_MenuInitInventoryOptionsPage()
1927 {
1928 Page *page = Hu_MenuAddPage(new Page("InventoryOptions", Vector2i(78, 48)));
1929 page->setLeftColumnWidth(.65f);
1930 page->setTitle("Inventory Options");
1931 page->setPredefinedFont(MENU_FONT1, FID(GF_FONTA));
1932 page->setPreviousPage(Hu_MenuPagePtr("Options"));
1933
1934 page->addWidget(new LabelWidget("Select Mode"))
1935 .setLeft();
1936 page->addWidget(new CVarToggleWidget("ctl-inventory-mode", 0, "Scroll", "Cursor"))
1937 .setRight()
1938 .setShortcut('s');
1939
1940 page->addWidget(new LabelWidget("Wrap Around"))
1941 .setLeft();
1942 page->addWidget(new CVarToggleWidget("ctl-inventory-wrap"))
1943 .setRight()
1944 .setShortcut('w');
1945
1946 page->addWidget(new LabelWidget("Choose And Use"))
1947 .setLeft();
1948 page->addWidget(new CVarToggleWidget("ctl-inventory-use-immediate"))
1949 .setRight()
1950 .setShortcut('c');
1951
1952 page->addWidget(new LabelWidget("Select Next If Use Failed"))
1953 .setLeft();
1954 page->addWidget(new CVarToggleWidget("ctl-inventory-use-next"))
1955 .setRight()
1956 .setShortcut('n');
1957
1958 page->addWidget(new LabelWidget("AutoHide"))
1959 .setLeft();
1960 page->addWidget(new CVarTextualSliderWidget("hud-inventory-timer", 0, 30, 1.f))
1961 .setEmptyText("Disabled")
1962 .setOnethSuffix(" second")
1963 .setNthSuffix(" seconds")
1964 .setShortcut('h')
1965 .setRight();
1966
1967 page->addWidget(new LabelWidget("Fullscreen HUD"))
1968 .setGroup(1)
1969 .setColor(MENU_COLOR2);
1970
1971 page->addWidget(new LabelWidget("Max Visible Slots"))
1972 .setLeft()
1973 .setGroup(1);
1974
1975 page->addWidget(new CVarTextualSliderWidget("hud-inventory-slot-max", 0, 16, 1, false))
1976 .setEmptyText("Automatic")
1977 .setRight()
1978 .setGroup(1)
1979 .setShortcut('v');
1980
1981 page->addWidget(new LabelWidget("Show Empty Slots"))
1982 .setGroup(1)
1983 .setLeft();
1984
1985 page->addWidget(new CVarToggleWidget("hud-inventory-slot-showempty"))
1986 .setGroup(1)
1987 .setRight()
1988 .setShortcut('e');
1989 }
1990 #endif
1991
Hu_MenuInitSoundOptionsPage()1992 void Hu_MenuInitSoundOptionsPage()
1993 {
1994 //#if __JHEXEN__
1995 // Vector2i const origin(97, 25);
1996 //#elif __JHERETIC__
1997 // Vector2i const origin(97, 30);
1998 //#elif __JDOOM__ || __JDOOM64__
1999 Vector2i const origin(97, 40);
2000 //#endif
2001
2002 Page *page = Hu_MenuAddPage(new Page("SoundOptions", origin));
2003 page->setLeftColumnWidth(.4f);
2004 page->setTitle("Sound Options");
2005 page->setPredefinedFont(MENU_FONT1, FID(GF_FONTA));
2006 page->setPreviousPage(Hu_MenuPagePtr("Options"));
2007
2008 page->addWidget(new LabelWidget("SFX Volume"))
2009 .setLeft();
2010 page->addWidget(new CVarSliderWidget("sound-volume", 0, 255, 16, false))
2011 .setRight()
2012 .setShortcut('s');
2013
2014 page->addWidget(new LabelWidget("Music Volume"))
2015 .setLeft();
2016 page->addWidget(new CVarSliderWidget("music-volume", 0, 255, 16, false))
2017 .setRight()
2018 .setShortcut('m');
2019 }
2020
2021 /**
2022 * Construct the episode selection menu.
2023 */
Hu_MenuInitEpisodePage()2024 void Hu_MenuInitEpisodePage()
2025 {
2026 #if __JHEXEN__
2027 Vector2i const origin(120, 44);
2028 #elif __JHERETIC__
2029 Vector2i const origin(80, 50);
2030 #else
2031 Vector2i const origin(48, 63);
2032 #endif
2033
2034 Page *page =
2035 Hu_MenuAddPage(new Page("Episode", origin, Page::FixedLayout, Hu_MenuDrawEpisodePage));
2036
2037 page->setPredefinedFont(MENU_FONT1, FID(GF_FONTB));
2038 page->setPreviousPage(Hu_MenuPagePtr("Main"));
2039 page->setOnActiveCallback([](Page &page) {
2040 const auto &items = page.children();
2041 if (items.size() == 1)
2042 {
2043 // If there is only one episode, select it automatically.
2044 auto &ep = items.front()->as<ButtonWidget>();
2045 ep.setSilent(true);
2046 ep.handleCommand(MCMD_SELECT);
2047 ep.setSilent(false);
2048 }
2049 });
2050
2051 const DictionaryValue::Elements &episodesById = Defs().episodes.lookup("id").elements();
2052 if (!episodesById.size())
2053 {
2054 LOG_WARNING(
2055 "No episodes are defined. It will not be possible to start a new game from the menu");
2056 return;
2057 }
2058
2059 int y = 0;
2060 int n = 0;
2061 for (auto const &pair : episodesById)
2062 {
2063 const Record &episodeDef = *pair.second->as<RecordValue>().record();
2064 const String episodeId = episodeDef.gets("id");
2065 const String episodeTitle = G_EpisodeTitle(episodeId);
2066
2067 if (episodeTitle.empty())
2068 {
2069 // Hidden/untitled episode.
2070 continue;
2071 }
2072
2073 auto *btn = new ButtonWidget(episodeTitle);
2074 btn->setFixedY(y);
2075
2076 // Has a menu image been specified?
2077 de::Uri image(episodeDef.gets("menuImage"), RC_NULL);
2078 if (!image.path().isEmpty())
2079 {
2080 // Presently only patches are supported.
2081 if (!image.scheme().compareWithoutCase("Patches"))
2082 {
2083 btn->setPatch(R_DeclarePatch(image.path().toUtf8().constData()));
2084 }
2085 }
2086
2087 // Has a menu shortcut/hotkey been specified?
2088 /// @todo Validate symbolic dday key names.
2089 String const shortcut = episodeDef.gets("menuShortcut");
2090 if (!shortcut.isEmpty() && shortcut.first().isLetterOrNumber())
2091 {
2092 btn->setShortcut(shortcut.first().toLower().toLatin1());
2093 }
2094
2095 // Has a menu help/info text been specified?
2096 String const helpInfo = episodeDef.gets("menuHelpInfo");
2097 if (!helpInfo.isEmpty())
2098 {
2099 btn->setHelpInfo(helpInfo);
2100 }
2101
2102 de::Uri startMap(episodeDef.gets("startMap"), RC_NULL);
2103 if (P_MapExists(startMap.compose().toUtf8().constData()))
2104 {
2105 btn->setAction(Widget::Deactivated, Hu_MenuSelectEpisode);
2106 btn->setUserValue(episodeId);
2107 }
2108 else
2109 {
2110 #if __JDOOM__ || __JHERETIC__
2111 // In shareware display a prompt to buy the full game.
2112 if (
2113 # if __JHERETIC__
2114 gameMode == heretic_shareware
2115 # else // __JDOOM__
2116 gameMode == doom_shareware
2117 # endif
2118 && startMap.path() != "E1M1")
2119 {
2120 btn->setAction(Widget::Deactivated, Hu_MenuActivateNotSharewareEpisode);
2121 }
2122 else
2123 #endif
2124 {
2125 // Disable this selection and log a warning for the mod author.
2126 btn->setFlags(Widget::Disabled);
2127 LOG_RES_WARNING("Failed to locate the starting map \"%s\" for episode '%s'."
2128 " This episode will not be selectable from the menu")
2129 << startMap << episodeId;
2130 }
2131 }
2132
2133 btn->setAction(Widget::FocusGained, Hu_MenuDefaultFocusAction);
2134 btn->setFont(MENU_FONT1);
2135 page->addWidget(btn);
2136
2137 y += FIXED_LINE_HEIGHT;
2138 n += 1;
2139 }
2140 }
2141
2142 #if __JHEXEN__
2143 /**
2144 * Construct the player class selection menu.
2145 */
Hu_MenuInitPlayerClassPage()2146 void Hu_MenuInitPlayerClassPage()
2147 {
2148 // First determine the number of selectable player classes.
2149 int count = 0;
2150 for(int i = 0; i < NUM_PLAYER_CLASSES; ++i)
2151 {
2152 classinfo_t *info = PCLASS_INFO(i);
2153 if(info->userSelectable)
2154 {
2155 ++count;
2156 }
2157 }
2158
2159 Page *page = Hu_MenuAddPage(new Page("PlayerClass", Vector2i(66, 66), Page::FixedLayout | Page::NoScroll,
2160 Hu_MenuDrawPlayerClassPage, Hu_MenuSkipPreviousPageIfSkippingEpisodeSelection));
2161 page->setPredefinedFont(MENU_FONT1, FID(GF_FONTB));
2162 page->setPreviousPage(Hu_MenuPagePtr("Episode"));
2163
2164 uint y = 0;
2165
2166 // Add the selectable classes.
2167 int n = 0;
2168 while(n < count)
2169 {
2170 classinfo_t *info = PCLASS_INFO(n++);
2171
2172 if(!info->userSelectable) continue;
2173
2174 String text;
2175 if(info->niceName && (PTR2INT(info->niceName) > 0 && PTR2INT(info->niceName) < NUMTEXT))
2176 {
2177 text = String(GET_TXT(PTR2INT(info->niceName)));
2178 }
2179 else
2180 {
2181 text = String(info->niceName);
2182 }
2183
2184 auto *btn = new ButtonWidget(text);
2185
2186 if(!btn->text().isEmpty() && btn->text().first().isLetterOrNumber()) btn->setShortcut(btn->text().first().toLatin1());
2187 btn->setFixedY(y);
2188 btn->setAction(Widget::Deactivated, Hu_MenuSelectPlayerClass);
2189 btn->setAction(Widget::FocusGained, Hu_MenuFocusOnPlayerClass);
2190 btn->setUserValue2(int(info->plrClass));
2191 btn->setFont(MENU_FONT1);
2192
2193 page->addWidget(btn);
2194 y += FIXED_LINE_HEIGHT;
2195 }
2196
2197 // Random class button.
2198 String const labelText = GET_TXT(TXT_RANDOMPLAYERCLASS);
2199 int const shortcut = labelText.first().isLetterOrNumber()? labelText.first().toLatin1() : 0;
2200 page->addWidget(new ButtonWidget(labelText))
2201 .setFixedY(y)
2202 .setShortcut(shortcut)
2203 .setUserValue2(int(PCLASS_NONE))
2204 .setFont(MENU_FONT1)
2205 .setColor(MENU_COLOR1)
2206 .setAction(Widget::Deactivated, Hu_MenuSelectPlayerClass)
2207 .setAction(Widget::FocusGained, Hu_MenuFocusOnPlayerClass);
2208
2209 // Mobj preview background.
2210 page->addWidget(new RectWidget)
2211 .setFlags(Widget::NoFocus | Widget::Id1)
2212 .setFixedOrigin(Vector2i(108, -58))
2213 .setOnTickCallback(Hu_MenuPlayerClassBackgroundTicker);
2214
2215 // Mobj preview.
2216 page->addWidget(new MobjPreviewWidget)
2217 .setFlags(Widget::Id0)
2218 .setFixedOrigin(Vector2i(108 + 55, -58 + 76))
2219 .setOnTickCallback(Hu_MenuPlayerClassPreviewTicker);
2220 }
2221 #endif
2222
Hu_MenuAddPage(Page * page)2223 Page *Hu_MenuAddPage(Page *page)
2224 {
2225 if(!page) return page;
2226
2227 // Have we already added this page?
2228 for(Page *other : pages)
2229 {
2230 if(other == page) return page;
2231 }
2232
2233 // Is the name valid?
2234 String nameInIndex = page->name().toLower();
2235 if(nameInIndex.isEmpty())
2236 {
2237 throw Error("Hu_MenuPage", "A page must have a valid (i.e., not empty) name");
2238 }
2239
2240 // Is the name unique?
2241 if(pages.contains(nameInIndex))
2242 {
2243 throw Error("Hu_MenuPage", "A page with the name '" + page->name() + "' is already present");
2244 }
2245
2246 pages.insert(nameInIndex, page);
2247 return page;
2248 }
2249
2250 /// @note Called during (post-engine) init and after updating game/engine state.
Hu_MenuInit()2251 void Hu_MenuInit()
2252 {
2253 // Close the menu (if open) and shutdown (if initialized - we're reinitializing).
2254 Hu_MenuShutdown();
2255
2256 mnAlpha = mnTargetAlpha = 0;
2257 currentPage = 0;
2258 menuActive = false;
2259
2260 cursor.hasRotation = false;
2261 cursor.angle = 0;
2262 cursor.animFrame = 0;
2263 cursor.animCounter = MENU_CURSOR_TICSPERFRAME;
2264
2265 DD_Execute(true, "deactivatebcontext menu");
2266
2267 Hu_MenuLoadResources();
2268
2269 initAllPages();
2270
2271 #if __JDOOM__
2272 if(gameModeBits & GM_ANY_DOOM2)
2273 {
2274 Page &mainPage = Hu_MenuPage("Main");
2275
2276 Widget &wiReadThis = mainPage.findWidget(Widget::Id0);
2277 wiReadThis.setFlags(Widget::Disabled | Widget::Hidden | Widget::NoFocus);
2278
2279 Widget &wiQuitGame = mainPage.findWidget(Widget::Id1);
2280 wiQuitGame.setFixedY(wiQuitGame.fixedY() - FIXED_LINE_HEIGHT);
2281 }
2282 #endif
2283
2284 inited = true;
2285 }
2286
Hu_MenuShutdown()2287 void Hu_MenuShutdown()
2288 {
2289 if(!inited) return;
2290
2291 Hu_MenuCommand(MCMD_CLOSEFAST);
2292 destroyAllPages();
2293 inited = false;
2294 }
2295
Hu_MenuIsActive()2296 bool Hu_MenuIsActive()
2297 {
2298 return menuActive;
2299 }
2300
Hu_MenuSetOpacity(float alpha)2301 void Hu_MenuSetOpacity(float alpha)
2302 {
2303 // The menu's alpha will start moving towards this target value.
2304 mnTargetAlpha = alpha;
2305 }
2306
Hu_MenuOpacity()2307 float Hu_MenuOpacity()
2308 {
2309 return mnAlpha;
2310 }
2311
Hu_MenuTicker(timespan_t ticLength)2312 void Hu_MenuTicker(timespan_t ticLength)
2313 {
2314 #define MENUALPHA_FADE_STEP (.07f)
2315
2316 // Move towards the target alpha level for the entire menu.
2317 float diff = mnTargetAlpha - mnAlpha;
2318 if(fabs(diff) > MENUALPHA_FADE_STEP)
2319 {
2320 mnAlpha += float( MENUALPHA_FADE_STEP * ticLength * TICRATE * (diff > 0? 1 : -1) );
2321 }
2322 else
2323 {
2324 mnAlpha = mnTargetAlpha;
2325 }
2326
2327 if(!menuActive) return;
2328
2329 // Animate cursor rotation?
2330 if(cfg.common.menuCursorRotate)
2331 {
2332 if(cursor.hasRotation)
2333 {
2334 cursor.angle += float( 5 * ticLength * TICRATE );
2335 }
2336 else if (!fequal(cursor.angle, 0))
2337 {
2338 float rewind = float( MENU_CURSOR_REWIND_SPEED * ticLength * TICRATE );
2339 if(cursor.angle <= rewind || cursor.angle >= 360 - rewind)
2340 cursor.angle = 0;
2341 else if(cursor.angle < 180)
2342 cursor.angle -= rewind;
2343 else
2344 cursor.angle += rewind;
2345 }
2346
2347 if(cursor.angle >= 360)
2348 cursor.angle -= 360;
2349 }
2350
2351 // Time to think? Updates on 35Hz game ticks.
2352 if(!DD_IsSharpTick()) return;
2353
2354 // Advance menu time.
2355 menuTime++;
2356
2357 // Animate the cursor graphic?
2358 if(--cursor.animCounter <= 0)
2359 {
2360 cursor.animFrame++;
2361 cursor.animCounter = MENU_CURSOR_TICSPERFRAME;
2362 if(cursor.animFrame > MENU_CURSOR_FRAMECOUNT-1)
2363 cursor.animFrame = 0;
2364 }
2365
2366 // Used for Heretic's rotating skulls.
2367 frame = (menuTime / 3) % 18;
2368
2369 // Call the active page's ticker.
2370 currentPage->tick();
2371
2372 #undef MENUALPHA_FADE_STEP
2373 }
2374
Hu_MenuHasPage()2375 bool Hu_MenuHasPage()
2376 {
2377 return currentPage != 0;
2378 }
2379
Hu_MenuPage()2380 Page &Hu_MenuPage()
2381 {
2382 if(currentPage)
2383 {
2384 return *currentPage;
2385 }
2386 throw Error("Hu_MenuPage", "No current Page is presently configured");
2387 }
2388
Hu_MenuSetPage(Page * page,bool canReactivate)2389 void Hu_MenuSetPage(Page *page, bool canReactivate)
2390 {
2391 if(!menuActive) return;
2392 if(!page) return;
2393
2394 if(!Get(DD_NOVIDEO))
2395 {
2396 FR_ResetTypeinTimer();
2397 }
2398
2399 cursor.angle = 0; // Stop cursor rotation animation dead (don't rewind).
2400 menuNominatingQuickSaveSlot = false;
2401
2402 if(currentPage == page)
2403 {
2404 if(!canReactivate) return;
2405 page->setFocus(0);
2406 }
2407
2408 // This is now the "active" page.
2409 currentPage = page;
2410 page->activate();
2411 }
2412
Hu_MenuIsVisible()2413 bool Hu_MenuIsVisible()
2414 {
2415 return (menuActive || mnAlpha > .0001f);
2416 }
2417
Hu_MenuDefaultFocusAction(Widget &,Widget::Action action)2418 void Hu_MenuDefaultFocusAction(Widget &, Widget::Action action)
2419 {
2420 if(action != Widget::FocusGained) return;
2421 Hu_MenuUpdateCursorState();
2422 }
2423
Hu_MenuMergeEffectWithDrawTextFlags(short f)2424 short Hu_MenuMergeEffectWithDrawTextFlags(short f)
2425 {
2426 return ((~cfg.common.menuEffectFlags & DTF_NO_EFFECTS) | (f & ~DTF_NO_EFFECTS));
2427 }
2428
Hu_MenuDrawFocusCursor(Vector2i const & origin,float scale,float alpha)2429 void Hu_MenuDrawFocusCursor(Vector2i const &origin, float scale, float alpha)
2430 {
2431 #if __JDOOM__ || __JDOOM64__
2432 # define OFFSET_X (-22)
2433 # define OFFSET_Y (-1)
2434 #elif __JHERETIC__ || __JHEXEN__
2435 # define OFFSET_X (-16)
2436 # define OFFSET_Y (1)
2437 #endif
2438
2439 float const angle = cursor.angle;
2440 int const cursorIdx = cursor.animFrame;
2441 patchid_t pCursor = pCursors[cursorIdx % MENU_CURSOR_FRAMECOUNT];
2442
2443 patchinfo_t info;
2444 if(!R_GetPatchInfo(pCursor, &info))
2445 return;
2446
2447 // float const scale = /*de::min((focusObjectHeight * 1.267f) /*/ 1; //info.geometry.size.height; //, 1.f);
2448 Vector2i pos = origin + Vector2i(OFFSET_X, OFFSET_Y) * scale;
2449 // pos.y -= info.geometry.size.height / 2;
2450
2451 DGL_MatrixMode(DGL_MODELVIEW);
2452 DGL_PushMatrix();
2453
2454 DGL_Translatef(pos.x, pos.y, 0);
2455 DGL_Scalef(scale, scale, 1);
2456 DGL_Rotatef(angle, 0, 0, 1);
2457
2458 DGL_Enable(DGL_TEXTURE_2D);
2459 DGL_Color4f(1, 1, 1, alpha);
2460
2461 GL_DrawPatch(pCursor, Vector2i(0, 0), 0, DPF_NO_OFFSET);
2462
2463 DGL_Disable(DGL_TEXTURE_2D);
2464
2465 DGL_MatrixMode(DGL_MODELVIEW);
2466 DGL_PopMatrix();
2467
2468 #undef OFFSET_Y
2469 #undef OFFSET_X
2470 }
2471
Hu_MenuDrawPageTitle(String title,Vector2i const & origin)2472 void Hu_MenuDrawPageTitle(String title, Vector2i const &origin)
2473 {
2474 title = Widget::labelText(title);
2475
2476 if(title.isEmpty()) return;
2477
2478 DGL_Enable(DGL_TEXTURE_2D);
2479 FR_SetFont(FID(GF_FONTB));
2480 FR_SetColorv(cfg.common.menuTextColors[0]);
2481 FR_SetAlpha(mnRendState->pageAlpha);
2482
2483 FR_DrawTextXY3(title.toLatin1(), origin.x, origin.y, ALIGN_TOP, Hu_MenuMergeEffectWithDrawTextFlags(0));
2484
2485 DGL_Disable(DGL_TEXTURE_2D);
2486 }
2487
Hu_MenuDrawPageHelp(String helpText,Vector2i const & origin)2488 void Hu_MenuDrawPageHelp(String helpText, Vector2i const &origin)
2489 {
2490 if(helpText.isEmpty()) return;
2491
2492 DGL_MatrixMode(DGL_MODELVIEW);
2493 DGL_PushMatrix();
2494
2495 DGL_Translatef(SCREENWIDTH / 2, SCREENHEIGHT, 0);
2496 DGL_Scalef(.666666f, .666666f, 1.f);
2497 DGL_Translatef(-SCREENWIDTH / 2, -SCREENHEIGHT, 0);
2498
2499 DGL_Enable(DGL_TEXTURE_2D);
2500 FR_SetFont(FID(GF_FONTA));
2501 FR_SetColorv(cfg.common.menuTextColors[1]);
2502 FR_SetAlpha(mnRendState->pageAlpha);
2503
2504 FR_DrawTextXY3(helpText.toLatin1(), origin.x, origin.y, ALIGN_BOTTOM, Hu_MenuMergeEffectWithDrawTextFlags(0));
2505
2506 DGL_Disable(DGL_TEXTURE_2D);
2507
2508 DGL_MatrixMode(DGL_MODELVIEW);
2509 DGL_PopMatrix();
2510 }
2511
drawOverlayBackground(float darken)2512 static void drawOverlayBackground(float darken)
2513 {
2514 DGL_SetNoMaterial();
2515 DGL_DrawRectf2Color(0, 0, SCREENWIDTH, SCREENHEIGHT, 0, 0, 0, darken);
2516 }
2517
beginOverlayDraw()2518 static void beginOverlayDraw()
2519 {
2520 #define SMALL_SCALE .75f
2521
2522 DGL_MatrixMode(DGL_MODELVIEW);
2523 DGL_PushMatrix();
2524
2525 DGL_Translatef(SCREENWIDTH/2, SCREENHEIGHT/2, 0);
2526 DGL_Scalef(SMALL_SCALE, SMALL_SCALE, 1);
2527 DGL_Translatef(-(SCREENWIDTH/2), -(SCREENHEIGHT/2), 0);
2528
2529 #undef SMALL_SCALE
2530 }
2531
endOverlayDraw()2532 static void endOverlayDraw()
2533 {
2534 DGL_MatrixMode(DGL_MODELVIEW);
2535 DGL_PopMatrix();
2536 }
2537
Hu_MenuDrawer()2538 void Hu_MenuDrawer()
2539 {
2540 #define OVERLAY_DARKEN .7f
2541
2542 dgl_borderedprojectionstate_t bp;
2543
2544 if(!Hu_MenuIsVisible()) return;
2545
2546 GL_ConfigureBorderedProjection(&bp, 0, SCREENWIDTH, SCREENHEIGHT,
2547 Get(DD_WINDOW_WIDTH), Get(DD_WINDOW_HEIGHT), scalemode_t(cfg.common.menuScaleMode));
2548 GL_BeginBorderedProjection(&bp);
2549
2550 // First determine whether the focus cursor should be visible.
2551 Widget *focused = Hu_MenuPage().focusWidget();
2552 bool showFocusCursor = true;
2553 if(focused && focused->isActive())
2554 {
2555 if(is<ColorEditWidget>(focused) || is<InputBindingWidget>(focused))
2556 {
2557 showFocusCursor = false;
2558 }
2559 }
2560
2561 DGL_MatrixMode(DGL_MODELVIEW);
2562 DGL_PushMatrix();
2563
2564 DGL_Translatef(SCREENWIDTH/2, SCREENHEIGHT/2, 0);
2565 DGL_Scalef(cfg.common.menuScale, cfg.common.menuScale, 1);
2566 DGL_Translatef(-(SCREENWIDTH/2), -(SCREENHEIGHT/2), 0);
2567
2568 Hu_MenuPage().draw(mnAlpha, showFocusCursor);
2569
2570 DGL_MatrixMode(DGL_MODELVIEW);
2571 DGL_PopMatrix();
2572
2573 GL_EndBorderedProjection(&bp);
2574
2575 // Drawing any overlays?
2576 if(focused && focused->isActive())
2577 {
2578 if(is<ColorEditWidget>(focused))
2579 {
2580 drawOverlayBackground(OVERLAY_DARKEN);
2581 GL_BeginBorderedProjection(&bp);
2582
2583 beginOverlayDraw();
2584 Hu_MenuPage("ColorWidget").draw();
2585 endOverlayDraw();
2586
2587 GL_EndBorderedProjection(&bp);
2588 }
2589 if(InputBindingWidget *binds = maybeAs<InputBindingWidget>(focused))
2590 {
2591 drawOverlayBackground(OVERLAY_DARKEN);
2592 GL_BeginBorderedProjection(&bp);
2593
2594 beginOverlayDraw();
2595 Hu_MenuControlGrabDrawer(binds->controlName(), 1);
2596 endOverlayDraw();
2597
2598 GL_EndBorderedProjection(&bp);
2599 }
2600 }
2601
2602 #undef OVERLAY_DARKEN
2603 }
2604
initAllPages()2605 static void initAllPages()
2606 {
2607 Hu_MenuInitColorWidgetPage();
2608 Hu_MenuInitMainPage();
2609 //Hu_MenuInitGameTypePage();
2610 Hu_MenuInitEpisodePage();
2611 #if __JHEXEN__
2612 Hu_MenuInitPlayerClassPage();
2613 #endif
2614 Hu_MenuInitSkillPage();
2615 //Hu_MenuInitMultiplayerPage();
2616 #if __JHERETIC__ || __JHEXEN__
2617 Hu_MenuInitFilesPage();
2618 #endif
2619 Hu_MenuInitLoadGameAndSaveGamePages();
2620 Hu_MenuInitOptionsPage();
2621 Hu_MenuInitPlayerSetupPage();
2622 Hu_MenuInitGameplayOptionsPage();
2623 Hu_MenuInitSaveOptionsPage();
2624 Hu_MenuInitHUDOptionsPage();
2625 Hu_MenuInitAutomapOptionsPage();
2626 Hu_MenuInitWeaponsPage();
2627 #if __JHERETIC__ || __JHEXEN__
2628 Hu_MenuInitInventoryOptionsPage();
2629 #endif
2630 Hu_MenuInitSoundOptionsPage();
2631 Hu_MenuInitControlsPage();
2632 }
2633
destroyAllPages()2634 static void destroyAllPages()
2635 {
2636 qDeleteAll(pages);
2637 pages.clear();
2638 }
2639
Hu_MenuColorWidgetCmdResponder(Page & page,menucommand_e cmd)2640 int Hu_MenuColorWidgetCmdResponder(Page &page, menucommand_e cmd)
2641 {
2642 switch(cmd)
2643 {
2644 case MCMD_NAV_OUT: {
2645 Widget *wi = static_cast<Widget *>(page.userValue().value<void *>());
2646 wi->setFlags(Widget::Active, UnsetFlags);
2647 S_LocalSound(SFX_MENU_CANCEL, NULL);
2648 colorWidgetActive = false;
2649
2650 /// @kludge We should re-focus on the object instead.
2651 cursor.angle = 0; // Stop cursor rotation animation dead (don't rewind).
2652 Hu_MenuUpdateCursorState();
2653 /// kludge end.
2654 return true; }
2655
2656 case MCMD_NAV_PAGEUP:
2657 case MCMD_NAV_PAGEDOWN:
2658 return true; // Eat these.
2659
2660 case MCMD_SELECT: {
2661 Widget *wi = static_cast<Widget *>(page.userValue().value<void *>());
2662 ColorEditWidget &cbox = wi->as<ColorEditWidget>();
2663 cbox.setFlags(Widget::Active, UnsetFlags);
2664 S_LocalSound(SFX_MENU_ACCEPT, NULL);
2665 colorWidgetActive = false;
2666 cbox.setColor(page.findWidget(Widget::Id0).as<ColorEditWidget>().color(), 0);
2667
2668 /// @kludge We should re-focus on the object instead.
2669 cursor.angle = 0; // Stop cursor rotation animation dead (don't rewind).
2670 Hu_MenuUpdateCursorState();
2671 /// kludge end.
2672 return true; }
2673
2674 default: break;
2675 }
2676
2677 return false;
2678 }
2679
2680 /**
2681 * Determines if manual episode selection via the menu can be skipped if only one
2682 * episode is playable.
2683 *
2684 * Some demo/shareware game versions use the episode selection menu for the purpose
2685 * of prompting the user to buy the full version. In such a case, disable skipping.
2686 *
2687 * @return @c true if skipping is allowed.
2688 */
allowSkipEpisodeSelection()2689 static bool allowSkipEpisodeSelection()
2690 {
2691 #if __JDOOM__
2692 if(gameMode == doom_shareware) return false; // Never.
2693 #elif __JHERETIC__
2694 if(gameMode == heretic_shareware) return false; // Never.
2695 #endif
2696 return true;
2697 }
2698
Hu_MenuSkipPreviousPageIfSkippingEpisodeSelection(Page & page,menucommand_e cmd)2699 int Hu_MenuSkipPreviousPageIfSkippingEpisodeSelection(Page &page, menucommand_e cmd)
2700 {
2701 // All we react to are MCMD_NAV_OUT commands.
2702 if(cmd != MCMD_NAV_OUT) return false;
2703
2704 Page *previous = page.previousPage();
2705
2706 // Skip this page if only one episode is playable.
2707 if(allowSkipEpisodeSelection() && PlayableEpisodeCount() == 1)
2708 {
2709 previous = previous->previousPage();
2710 }
2711
2712 if(previous)
2713 {
2714 S_LocalSound(SFX_MENU_CANCEL, nullptr);
2715 Hu_MenuSetPage(previous);
2716 }
2717 else
2718 {
2719 // No previous page so just close the menu.
2720 S_LocalSound(SFX_MENU_CLOSE, nullptr);
2721 Hu_MenuCommand(MCMD_CLOSE);
2722 }
2723
2724 return true;
2725 }
2726
2727 /// Depending on the current menu state some commands require translating.
translateCommand(menucommand_e cmd)2728 static menucommand_e translateCommand(menucommand_e cmd)
2729 {
2730 // If a close command is received while currently working with a selected
2731 // "active" widget - interpret the command instead as "navigate out".
2732 if(menuActive && (cmd == MCMD_CLOSE || cmd == MCMD_CLOSEFAST))
2733 {
2734 if(Widget *wi = Hu_MenuPage().focusWidget())
2735 {
2736 if(wi->isActive() &&
2737 (is<LineEditWidget>(wi) || is<ListWidget>(wi) || is<ColorEditWidget>(wi)))
2738 {
2739 cmd = MCMD_NAV_OUT;
2740 }
2741 }
2742 }
2743
2744 return cmd;
2745 }
2746
Hu_MenuCommand(menucommand_e cmd)2747 void Hu_MenuCommand(menucommand_e cmd)
2748 {
2749 cmd = translateCommand(cmd);
2750
2751 // Determine the page which will respond to this command.
2752 Page *page = colorWidgetActive? Hu_MenuPagePtr("ColorWidget") : Hu_MenuPagePtr();
2753
2754 if(cmd == MCMD_CLOSE || cmd == MCMD_CLOSEFAST)
2755 {
2756 if(menuActive)
2757 {
2758 //BusyMode_FreezeGameForBusyMode();
2759
2760 menuNominatingQuickSaveSlot = false;
2761
2762 Hu_FogEffectSetAlphaTarget(0);
2763
2764 if(cmd == MCMD_CLOSEFAST)
2765 {
2766 // Hide the menu instantly.
2767 mnAlpha = mnTargetAlpha = 0;
2768 }
2769 else
2770 {
2771 mnTargetAlpha = 0;
2772 }
2773
2774 if(cmd != MCMD_CLOSEFAST)
2775 {
2776 S_LocalSound(SFX_MENU_CLOSE, NULL);
2777 }
2778
2779 menuActive = false;
2780
2781 // Disable the menu binding context.
2782 DD_Execute(true, "deactivatebcontext menu");
2783 }
2784 return;
2785 }
2786
2787 // No other commands are responded to once shutdown has begun.
2788 if(G_QuitInProgress())
2789 {
2790 return;
2791 }
2792
2793 if(!menuActive)
2794 {
2795 if(MCMD_OPEN == cmd)
2796 {
2797 // If anyone is currently chatting; the menu cannot be opened.
2798 for(int i = 0; i < MAXPLAYERS; ++i)
2799 {
2800 if(ST_ChatIsActive(i))
2801 return;
2802 }
2803
2804 S_LocalSound(SFX_MENU_OPEN, NULL);
2805
2806 //Con_Open(false);
2807
2808 Hu_FogEffectSetAlphaTarget(1);
2809 Hu_MenuSetOpacity(1);
2810 menuActive = true;
2811 menuTime = 0;
2812
2813 currentPage = NULL; // Always re-activate this page.
2814 Hu_MenuSetPage("Main");
2815
2816 // Enable the menu binding class
2817 DD_Execute(true, "activatebcontext menu");
2818 B_SetContextFallback("menu", Hu_MenuFallbackResponder);
2819 }
2820 return;
2821 }
2822
2823 page->handleCommand(cmd);
2824 }
2825
Hu_MenuPrivilegedResponder(event_t * ev)2826 int Hu_MenuPrivilegedResponder(event_t *ev)
2827 {
2828 DENG2_ASSERT(ev);
2829 if(Hu_MenuIsActive())
2830 {
2831 if(Widget *focused = Hu_MenuPage().focusWidget())
2832 {
2833 if(!focused->isDisabled())
2834 {
2835 return focused->handleEvent_Privileged(*ev);
2836 }
2837 }
2838 }
2839 return false;
2840 }
2841
Hu_MenuResponder(event_t * ev)2842 int Hu_MenuResponder(event_t *ev)
2843 {
2844 DENG2_ASSERT(ev);
2845 if(Hu_MenuIsActive())
2846 {
2847 if(Widget *focused = Hu_MenuPage().focusWidget())
2848 {
2849 if(!focused->isDisabled())
2850 {
2851 return focused->handleEvent(*ev);
2852 }
2853 }
2854 }
2855 return false; // Not eaten.
2856 }
2857
Hu_MenuFallbackResponder(event_t * ev)2858 int Hu_MenuFallbackResponder(event_t *ev)
2859 {
2860 DENG2_ASSERT(ev);
2861 Page *page = Hu_MenuPagePtr();
2862
2863 if(!Hu_MenuIsActive() || !page) return false;
2864
2865 if(cfg.common.menuShortcutsEnabled)
2866 {
2867 if(ev->type == EV_KEY && (ev->state == EVS_DOWN || ev->state == EVS_REPEAT))
2868 {
2869 for(Widget *wi : page->children())
2870 {
2871 if(wi->isDisabled() || wi->isHidden())
2872 continue;
2873
2874 if(wi->flags() & Widget::NoFocus)
2875 continue;
2876
2877 if(wi->shortcut() == ev->data1)
2878 {
2879 page->setFocus(wi);
2880 return true;
2881 }
2882 }
2883 }
2884 }
2885 return false;
2886 }
2887
2888 /**
2889 * User wants to load this game
2890 */
Hu_MenuSelectLoadSlot(Widget & wi,Widget::Action action)2891 void Hu_MenuSelectLoadSlot(Widget &wi, Widget::Action action)
2892 {
2893 LineEditWidget *edit = &wi.as<LineEditWidget>();
2894
2895 if(action != Widget::Deactivated) return;
2896
2897 // Linked focus between LoadGame and SaveGame pages.
2898 Page &saveGamePage = Hu_MenuPage("SaveGame");
2899 saveGamePage.setFocus(saveGamePage.tryFindWidget(wi.userValue2().toUInt()));
2900
2901 Page &loadGamePage = Hu_MenuPage("LoadGame");
2902 loadGamePage.setFocus(loadGamePage.tryFindWidget(wi.userValue2().toUInt()));
2903
2904 G_SetGameActionLoadSession(edit->userValue().toString());
2905 Hu_MenuCommand(chooseCloseMethod());
2906 }
2907
2908 #if __JHERETIC__ || __JHEXEN__
Hu_MenuDrawMainPage(Page const &,Vector2i const & origin)2909 void Hu_MenuDrawMainPage(Page const & /*page*/, Vector2i const &origin)
2910 {
2911 #define TITLEOFFSET_X (-22)
2912 #define TITLEOFFSET_Y (-56)
2913
2914 #if __JHEXEN__
2915 int frame = (menuTime / 5) % 7;
2916 #endif
2917
2918 DGL_Enable(DGL_TEXTURE_2D);
2919 DGL_Color4f(1, 1, 1, mnRendState->pageAlpha);
2920 FR_SetFont(FID(GF_FONTB));
2921 FR_SetColorAndAlpha(1, 1, 1, mnRendState->pageAlpha);
2922
2923 WI_DrawPatch(pMainTitle, Hu_ChoosePatchReplacement(patchreplacemode_t(cfg.common.menuPatchReplaceMode), pMainTitle),
2924 Vector2i(origin.x + TITLEOFFSET_X, origin.y + TITLEOFFSET_Y), ALIGN_TOPLEFT, 0, Hu_MenuMergeEffectWithDrawTextFlags(0));
2925 #if __JHEXEN__
2926 GL_DrawPatch(pBullWithFire[(frame + 2) % 7], origin + Vector2i(-73, 24));
2927 GL_DrawPatch(pBullWithFire[frame], origin + Vector2i(168, 24));
2928 #elif __JHERETIC__
2929 GL_DrawPatch(pRotatingSkull[17 - frame], origin + Vector2i(-70, -46));
2930 GL_DrawPatch(pRotatingSkull[frame], origin + Vector2i(122, -46));
2931 #endif
2932
2933 DGL_Disable(DGL_TEXTURE_2D);
2934
2935 #undef TITLEOFFSET_Y
2936 #undef TITLEOFFSET_X
2937 }
2938 #endif
2939
Hu_MenuDrawGameTypePage(Page const &,Vector2i const & origin)2940 void Hu_MenuDrawGameTypePage(Page const & /*page*/, Vector2i const &origin)
2941 {
2942 Hu_MenuDrawPageTitle(GET_TXT(TXT_PICKGAMETYPE), Vector2i(SCREENWIDTH / 2, origin.y - 28));
2943 }
2944
2945 #if __JHEXEN__
2946 /**
2947 * A specialization of MNRect_Ticker() which implements the animation logic
2948 * for the player class selection page's player visual background.
2949 */
Hu_MenuPlayerClassBackgroundTicker(Widget & wi)2950 void Hu_MenuPlayerClassBackgroundTicker(Widget &wi)
2951 {
2952 RectWidget &bg = wi.as<RectWidget>();
2953
2954 // Determine our selection according to the current focus object.
2955 /// @todo Do not search for the focus object, flag the "random"
2956 /// state through a focus action.
2957 if(Widget *mop = wi.page().focusWidget())
2958 {
2959 playerclass_t pClass = playerclass_t(mop->userValue2().toInt());
2960 if(pClass == PCLASS_NONE)
2961 {
2962 // Random class.
2963 /// @todo Use this object's timer instead of menuTime.
2964 pClass = playerclass_t(menuTime / 5);
2965 }
2966
2967 /// @todo Only change here if in the "random" state.
2968 pClass = playerclass_t(int(pClass) % 3); // Number of user-selectable classes.
2969
2970 bg.setBackgroundPatch(pPlayerClassBG[pClass]);
2971 }
2972 }
2973
2974 /**
2975 * A specialization of MNMobjPreview_Ticker() which implements the animation
2976 * logic for the player class selection page's player visual.
2977 */
Hu_MenuPlayerClassPreviewTicker(Widget & wi)2978 void Hu_MenuPlayerClassPreviewTicker(Widget &wi)
2979 {
2980 MobjPreviewWidget &mprev = wi.as<MobjPreviewWidget>();
2981
2982 // Determine our selection according to the current focus object.
2983 /// @todo Do not search for the focus object, flag the "random"
2984 /// state through a focus action.
2985 if(Widget *mop = wi.page().focusWidget())
2986 {
2987 playerclass_t pClass = playerclass_t(mop->userValue2().toInt());
2988 if(pClass == PCLASS_NONE)
2989 {
2990 // Random class.
2991 /// @todo Use this object's timer instead of menuTime.
2992 pClass = playerclass_t(PCLASS_FIRST + (menuTime / 5));
2993 pClass = playerclass_t(int(pClass) % 3); // Number of user-selectable classes.
2994
2995 mprev.setPlayerClass(pClass);
2996 mprev.setMobjType(PCLASS_INFO(pClass)->mobjType);
2997 }
2998
2999 // Fighter is Yellow, others Red by default.
3000 mprev.setTranslationClass(pClass);
3001 mprev.setTranslationMap(pClass == PCLASS_FIGHTER? 2 : 0);
3002 }
3003 }
3004
Hu_MenuDrawPlayerClassPage(Page const &,Vector2i const & origin)3005 void Hu_MenuDrawPlayerClassPage(Page const & /*page*/, Vector2i const &origin)
3006 {
3007 DGL_Enable(DGL_TEXTURE_2D);
3008 FR_SetFont(FID(GF_FONTB));
3009 FR_SetColorAndAlpha(cfg.common.menuTextColors[0][CR], cfg.common.menuTextColors[0][CG], cfg.common.menuTextColors[0][CB], mnRendState->pageAlpha);
3010
3011 FR_DrawTextXY3("Choose class:", origin.x - 32, origin.y - 42, ALIGN_TOPLEFT,
3012 Hu_MenuMergeEffectWithDrawTextFlags(0));
3013
3014 DGL_Disable(DGL_TEXTURE_2D);
3015 }
3016 #endif
3017
Hu_MenuDrawEpisodePage(Page const & page,Vector2i const & origin)3018 void Hu_MenuDrawEpisodePage(Page const &page, Vector2i const &origin)
3019 {
3020 #if __JDOOM__
3021 DENG2_UNUSED(page);
3022
3023 DGL_Enable(DGL_TEXTURE_2D);
3024 DGL_Color4f(1, 1, 1, mnRendState->pageAlpha);
3025
3026 FR_SetFont(FID(GF_FONTB));
3027 FR_SetColorv(cfg.common.menuTextColors[0]);
3028 FR_SetAlpha(mnRendState->pageAlpha);
3029
3030 WI_DrawPatch(pEpisode, Hu_ChoosePatchReplacement(patchreplacemode_t(cfg.common.menuPatchReplaceMode), pEpisode),
3031 Vector2i(origin.x + 7, origin.y - 25), ALIGN_TOPLEFT, 0, Hu_MenuMergeEffectWithDrawTextFlags(0));
3032
3033 DGL_Disable(DGL_TEXTURE_2D);
3034 #else
3035 DENG2_UNUSED(page);
3036
3037 #if defined (__JHERETIC__)
3038 String titleText;
3039 #else
3040 String titleText = "Choose episode:";
3041 #endif
3042
3043 if (const auto *value = Defs().getValueById("Menu Label|Episode Page Title"))
3044 {
3045 titleText = value->text;
3046 }
3047
3048 DGL_Enable(DGL_TEXTURE_2D);
3049 FR_SetFont(FID(GF_FONTB));
3050 FR_SetColorAndAlpha(cfg.common.menuTextColors[0][CR], cfg.common.menuTextColors[0][CG], cfg.common.menuTextColors[0][CB], mnRendState->pageAlpha);
3051
3052 FR_DrawTextXY3(titleText.toLatin1(), SCREENWIDTH / 2, origin.y - 42, ALIGN_TOP,
3053 Hu_MenuMergeEffectWithDrawTextFlags(0));
3054
3055 DGL_Disable(DGL_TEXTURE_2D);
3056 #endif
3057 }
3058
Hu_MenuDrawSkillPage(Page const &,Vector2i const & origin)3059 void Hu_MenuDrawSkillPage(Page const & /*page*/, Vector2i const &origin)
3060 {
3061 #if __JDOOM__ || __JDOOM64__
3062 DGL_Enable(DGL_TEXTURE_2D);
3063 DGL_Color4f(1, 1, 1, mnRendState->pageAlpha);
3064 FR_SetFont(FID(GF_FONTB));
3065 FR_SetColorAndAlpha(cfg.common.menuTextColors[0][CR], cfg.common.menuTextColors[0][CG], cfg.common.menuTextColors[0][CB], mnRendState->pageAlpha);
3066
3067 WI_DrawPatch(pNewGame, Hu_ChoosePatchReplacement(patchreplacemode_t(cfg.common.menuPatchReplaceMode), pNewGame),
3068 Vector2i(origin.x + 48, origin.y - 49), ALIGN_TOPLEFT, 0, Hu_MenuMergeEffectWithDrawTextFlags(0));
3069 WI_DrawPatch(pSkill, Hu_ChoosePatchReplacement(patchreplacemode_t(cfg.common.menuPatchReplaceMode), pSkill),
3070 Vector2i(origin.x + 6, origin.y - 25), ALIGN_TOPLEFT, 0, Hu_MenuMergeEffectWithDrawTextFlags(0));
3071
3072 DGL_Disable(DGL_TEXTURE_2D);
3073 #else
3074 #if defined (__JHERETIC__)
3075 String titleText;
3076 #else
3077 String titleText = "Choose Skill Level:";
3078 #endif
3079
3080 if (const auto *value = Defs().getValueById("Menu Label|Skill Page Title"))
3081 {
3082 titleText = value->text;
3083 }
3084
3085 Hu_MenuDrawPageTitle(titleText, Vector2i(SCREENWIDTH / 2, origin.y - 28));
3086 #endif
3087 }
3088
3089 /**
3090 * Called after the save name has been modified and to action the game-save.
3091 */
Hu_MenuSelectSaveSlot(Widget & wi,Widget::Action action)3092 void Hu_MenuSelectSaveSlot(Widget &wi, Widget::Action action)
3093 {
3094 if(action != Widget::Deactivated) return;
3095
3096 LineEditWidget &edit = wi.as<LineEditWidget>();
3097 String const saveSlotId = edit.userValue().toString();
3098
3099 if(menuNominatingQuickSaveSlot)
3100 {
3101 Con_SetInteger("game-save-quick-slot", saveSlotId.toInt());
3102 menuNominatingQuickSaveSlot = false;
3103 }
3104
3105 String userDescription = edit.text();
3106 if(!G_SetGameActionSaveSession(saveSlotId, &userDescription))
3107 {
3108 return;
3109 }
3110
3111 Page &saveGamePage = Hu_MenuPage("SaveGame");
3112 saveGamePage.setFocus(saveGamePage.tryFindWidget(wi.userValue2().toUInt()));
3113
3114 Page &loadGamePage = Hu_MenuPage("LoadGame");
3115 loadGamePage.setFocus(loadGamePage.tryFindWidget(wi.userValue2().toUInt()));
3116
3117 Hu_MenuCommand(chooseCloseMethod());
3118 }
3119
Hu_MenuSaveSlotEdit(Widget & wi,Widget::Action action)3120 void Hu_MenuSaveSlotEdit(Widget &wi, Widget::Action action)
3121 {
3122 if(action != Widget::Activated) return;
3123 if(cfg.common.menuGameSaveSuggestDescription)
3124 {
3125 auto &edit = wi.as<LineEditWidget>();
3126 edit.setText(G_DefaultGameStateFolderUserDescription("" /*don't reuse an existing description*/));
3127 }
3128 }
3129
Hu_MenuActivateColorWidget(Widget & wi,Widget::Action action)3130 void Hu_MenuActivateColorWidget(Widget &wi, Widget::Action action)
3131 {
3132 if(action != Widget::Activated) return;
3133
3134 ColorEditWidget &cbox = wi.as<ColorEditWidget>();
3135
3136 Page &colorWidgetPage = Hu_MenuPage("ColorWidget");
3137 ColorEditWidget &cboxMix = colorWidgetPage.findWidget(Widget::Id0).as<ColorEditWidget>();
3138 SliderWidget &sldrRed = colorWidgetPage.findWidget(Widget::Id1).as<SliderWidget>();
3139 SliderWidget &sldrGreen = colorWidgetPage.findWidget(Widget::Id2).as<SliderWidget>();
3140 SliderWidget &sldrBlue = colorWidgetPage.findWidget(Widget::Id3).as<SliderWidget>();
3141 LabelWidget &labelAlpha = colorWidgetPage.findWidget(Widget::Id4).as<LabelWidget>();
3142 SliderWidget &sldrAlpha = colorWidgetPage.findWidget(Widget::Id5).as<SliderWidget>();
3143
3144 colorWidgetActive = true;
3145
3146 colorWidgetPage.activate();
3147 colorWidgetPage.setUserValue(qVariantFromValue((void *)&wi)); // Ugly or what...
3148
3149 cboxMix.setColor(cbox.color(), 0);
3150
3151 sldrRed .setValue(cbox.red());
3152 sldrGreen.setValue(cbox.green());
3153 sldrBlue .setValue(cbox.blue());
3154 sldrAlpha.setValue(cbox.alpha());
3155
3156 labelAlpha.setFlags(Widget::Disabled | Widget::Hidden, (cbox.rgbaMode()? UnsetFlags : SetFlags));
3157 sldrAlpha. setFlags(Widget::Disabled | Widget::Hidden, (cbox.rgbaMode()? UnsetFlags : SetFlags));
3158 }
3159
Hu_MenuDrawLoadGamePage(Page const &,Vector2i const & origin)3160 void Hu_MenuDrawLoadGamePage(Page const & /*page*/, Vector2i const &origin)
3161 {
3162 DGL_Enable(DGL_TEXTURE_2D);
3163 DGL_Color4f(1, 1, 1, mnRendState->pageAlpha);
3164 FR_SetFont(FID(GF_FONTB));
3165 FR_SetColorAndAlpha(cfg.common.menuTextColors[0][CR], cfg.common.menuTextColors[0][CG], cfg.common.menuTextColors[0][CB], mnRendState->pageAlpha);
3166
3167 #if __JHERETIC__ || __JHEXEN__
3168 FR_DrawTextXY3(Widget::labelText("Load Game").toLatin1(), SCREENWIDTH / 2, origin.y - 20, ALIGN_TOP, Hu_MenuMergeEffectWithDrawTextFlags(0));
3169 #else
3170 WI_DrawPatch(pLoadGame, Hu_ChoosePatchReplacement(patchreplacemode_t(cfg.common.menuPatchReplaceMode), pLoadGame),
3171 Vector2i(origin.x - 8, origin.y - 26), ALIGN_TOPLEFT, 0, Hu_MenuMergeEffectWithDrawTextFlags(0));
3172 #endif
3173 DGL_Disable(DGL_TEXTURE_2D);
3174
3175 Vector2i helpOrigin(SCREENWIDTH / 2, (SCREENHEIGHT / 2) + ((SCREENHEIGHT / 2 - 5) / cfg.common.menuScale));
3176 Hu_MenuDrawPageHelp("Select to load, [Del] to clear", helpOrigin);
3177 }
3178
Hu_MenuDrawSaveGamePage(Page const &,Vector2i const & origin)3179 void Hu_MenuDrawSaveGamePage(Page const & /*page*/, Vector2i const &origin)
3180 {
3181 #if __JHERETIC__ || __JHEXEN__
3182 Hu_MenuDrawPageTitle("Save Game", Vector2i(SCREENWIDTH / 2, origin.y - 20));
3183 #else
3184 DGL_Enable(DGL_TEXTURE_2D);
3185 DGL_Color4f(1, 1, 1, mnRendState->pageAlpha);
3186 FR_SetFont(FID(GF_FONTB));
3187 FR_SetColorAndAlpha(cfg.common.menuTextColors[0][CR], cfg.common.menuTextColors[0][CG], cfg.common.menuTextColors[0][CB], mnRendState->pageAlpha);
3188
3189 WI_DrawPatch(pSaveGame, Hu_ChoosePatchReplacement(patchreplacemode_t(cfg.common.menuPatchReplaceMode), pSaveGame),
3190 Vector2i(origin.x - 8, origin.y - 26), ALIGN_TOPLEFT, 0, Hu_MenuMergeEffectWithDrawTextFlags(0));
3191
3192 DGL_Disable(DGL_TEXTURE_2D);
3193 #endif
3194
3195 Vector2i helpOrigin(SCREENWIDTH / 2, (SCREENHEIGHT / 2) + ((SCREENHEIGHT / 2 - 5) / cfg.common.menuScale));
3196 Hu_MenuDrawPageHelp("Select to save, [Del] to clear", helpOrigin);
3197 }
3198
3199 #if __JDOOM__ || __JHERETIC__ || __JHEXEN__
Hu_MenuSelectHelp(Widget &,Widget::Action action)3200 void Hu_MenuSelectHelp(Widget & /*wi*/, Widget::Action action)
3201 {
3202 if(action != Widget::Deactivated) return;
3203 G_StartHelp();
3204 }
3205 #endif
3206
Hu_MenuDrawOptionsPage(Page const &,Vector2i const & origin)3207 void Hu_MenuDrawOptionsPage(Page const & /*page*/, Vector2i const &origin)
3208 {
3209 #if __JHERETIC__ || __JHEXEN__
3210 Hu_MenuDrawPageTitle("Options", Vector2i(origin.x + 42, origin.y - 30));
3211 #else
3212 DGL_Enable(DGL_TEXTURE_2D);
3213 DGL_Color4f(1, 1, 1, mnRendState->pageAlpha);
3214 FR_SetFont(FID(GF_FONTB));
3215 FR_SetColorAndAlpha(cfg.common.menuTextColors[0][CR], cfg.common.menuTextColors[0][CG], cfg.common.menuTextColors[0][CB], mnRendState->pageAlpha);
3216
3217 WI_DrawPatch(pOptionsTitle, Hu_ChoosePatchReplacement(patchreplacemode_t(cfg.common.menuPatchReplaceMode), pOptionsTitle),
3218 Vector2i(origin.x + 42, origin.y - 20), ALIGN_TOP, 0, Hu_MenuMergeEffectWithDrawTextFlags(0));
3219
3220 DGL_Disable(DGL_TEXTURE_2D);
3221 #endif
3222 }
3223
Hu_MenuDrawMultiplayerPage(Page const &,Vector2i const & origin)3224 void Hu_MenuDrawMultiplayerPage(Page const & /*page*/, Vector2i const &origin)
3225 {
3226 Hu_MenuDrawPageTitle(GET_TXT(TXT_MULTIPLAYER), Vector2i(SCREENWIDTH / 2, origin.y - 28));
3227 }
3228
Hu_MenuDrawPlayerSetupPage(Page const &,Vector2i const & origin)3229 void Hu_MenuDrawPlayerSetupPage(Page const & /*page*/, Vector2i const &origin)
3230 {
3231 Hu_MenuDrawPageTitle(GET_TXT(TXT_PLAYERSETUP), Vector2i(SCREENWIDTH / 2, origin.y - 28));
3232 }
3233
Hu_MenuActionSetActivePage(Widget & wi,Widget::Action action)3234 void Hu_MenuActionSetActivePage(Widget &wi, Widget::Action action)
3235 {
3236 if(action != Widget::Deactivated) return;
3237 Hu_MenuSetPage(Hu_MenuPagePtr(wi.as<ButtonWidget>().userValue().toString()));
3238 }
3239
Hu_MenuUpdateColorWidgetColor(Widget & wi,Widget::Action action)3240 void Hu_MenuUpdateColorWidgetColor(Widget &wi, Widget::Action action)
3241 {
3242 if(action != Widget::Modified) return;
3243
3244 SliderWidget &sldr = wi.as<SliderWidget>();
3245 float value = sldr.value();
3246 ColorEditWidget &cboxMix = Hu_MenuPage("ColorWidget").findWidget(Widget::Id0).as<ColorEditWidget>();
3247
3248 int const component = wi.userValue2().toInt();
3249 switch(component)
3250 {
3251 case CR: cboxMix.setRed (value); break;
3252 case CG: cboxMix.setGreen(value); break;
3253 case CB: cboxMix.setBlue (value); break;
3254 case CA: cboxMix.setAlpha(value); break;
3255
3256 default: DENG2_ASSERT(!"Hu_MenuUpdateColorWidgetColor: Invalid value for data2.");
3257 }
3258 }
3259
Hu_MenuChangeWeaponPriority(Widget & wi,Widget::Action action)3260 void Hu_MenuChangeWeaponPriority(Widget &wi, Widget::Action action)
3261 {
3262 if (action == Widget::Modified)
3263 {
3264 auto &list = wi.as<ListWidget>();
3265 for (int i = 0; i < list.itemCount(); ++i)
3266 {
3267 cfg.common.weaponOrder[i] = list.itemData(i);
3268 }
3269 }
3270 }
3271
Hu_MenuSelectSingleplayer(Widget &,Widget::Action action)3272 void Hu_MenuSelectSingleplayer(Widget & /*wi*/, Widget::Action action)
3273 {
3274 if(action != Widget::Deactivated) return;
3275
3276 // If a networked game is already in progress inform the user we can't continue.
3277 /// @todo Allow continue: Ask the user if the networked game should be stopped.
3278 if(IS_NETGAME)
3279 {
3280 Hu_MsgStart(MSG_ANYKEY, NEWGAME, nullptr, 0, nullptr);
3281 return;
3282 }
3283
3284 // Skip episode selection if only one is playable.
3285 if(allowSkipEpisodeSelection() && PlayableEpisodeCount() == 1)
3286 {
3287 mnEpisode = FirstPlayableEpisodeId();
3288 #if __JHEXEN__
3289 Hu_MenuSetPage("PlayerClass");
3290 #else
3291 Hu_MenuSetPage("Skill");
3292 #endif
3293 return;
3294 }
3295
3296 // Show the episode selection menu.
3297 Hu_MenuSetPage("Episode");
3298 }
3299
3300 #if 0
3301 void Hu_MenuSelectMultiplayer(Widget & /*wi*/, Widget::Action action)
3302 {
3303 if(action != Widget::Deactivated) return;
3304
3305 Page &multiplayerPage = Hu_MenuPage("Multiplayer");
3306
3307 // Set the appropriate label.
3308 ButtonWidget *btn = &multiplayerPage.findWidget(Widget::Id0).as<ButtonWidget>();
3309 if(IS_NETGAME)
3310 {
3311 btn->setText("Disconnect");
3312 }
3313 else
3314 {
3315 btn->setText("Join Game");
3316 }
3317
3318 Hu_MenuSetPage(&multiplayerPage);
3319 }
3320 #endif
3321
Hu_MenuSelectJoinGame(Widget &,Widget::Action action)3322 void Hu_MenuSelectJoinGame(Widget & /*wi*/, Widget::Action action)
3323 {
3324 if(action != Widget::Deactivated) return;
3325
3326 if(IS_NETGAME)
3327 {
3328 DD_Execute(false, "net disconnect");
3329 Hu_MenuCommand(MCMD_CLOSE);
3330 return;
3331 }
3332
3333 DD_Execute(false, "net setup client");
3334 }
3335
Hu_MenuActivatePlayerSetup(Page & page)3336 void Hu_MenuActivatePlayerSetup(Page &page)
3337 {
3338 MobjPreviewWidget &mop = page.findWidget(Widget::Id0).as<MobjPreviewWidget>();
3339 LineEditWidget &name = page.findWidget(Widget::Id1).as<LineEditWidget>();
3340 ListWidget &color = page.findWidget(Widget::Id3).as<ListWidget>();
3341
3342 #if __JHEXEN__
3343 mop.setMobjType(PCLASS_INFO(cfg.netClass)->mobjType);
3344 mop.setPlayerClass(cfg.netClass);
3345 #else
3346 mop.setMobjType(MT_PLAYER);
3347 mop.setPlayerClass(PCLASS_PLAYER);
3348 #endif
3349 mop.setTranslationClass(0);
3350 mop.setTranslationMap(cfg.common.netColor);
3351
3352 color.selectItemByValue(cfg.common.netColor);
3353 #if __JHEXEN__
3354 ListWidget &class_ = page.findWidget(Widget::Id2).as<ListWidget>();
3355 class_.selectItemByValue(cfg.netClass);
3356 #endif
3357
3358 name.setText(Con_GetString("net-name"), MNEDIT_STF_NO_ACTION | MNEDIT_STF_REPLACEOLD);
3359 }
3360
3361 #if __JHEXEN__
Hu_MenuSelectPlayerSetupPlayerClass(Widget & wi,Widget::Action action)3362 void Hu_MenuSelectPlayerSetupPlayerClass(Widget &wi, Widget::Action action)
3363 {
3364 if(action != Widget::Modified) return;
3365
3366 ListWidget &list = wi.as<ListWidget>();
3367 int selection = list.selection();
3368 if(selection >= 0)
3369 {
3370 MobjPreviewWidget &mop = wi.page().findWidget(Widget::Id0).as<MobjPreviewWidget>();
3371 mop.setPlayerClass(selection);
3372 mop.setMobjType(PCLASS_INFO(selection)->mobjType);
3373 }
3374 }
3375 #endif
3376
Hu_MenuSelectPlayerColor(Widget & wi,Widget::Action action)3377 void Hu_MenuSelectPlayerColor(Widget &wi, Widget::Action action)
3378 {
3379 if(action != Widget::Modified) return;
3380
3381 // The color translation map is stored in the list item data member.
3382 ListWidget &list = wi.as<ListWidget>();
3383 int selection = list.itemData(list.selection());
3384 if(selection >= 0)
3385 {
3386 wi.page().findWidget(Widget::Id0).as<MobjPreviewWidget>().setTranslationMap(selection);
3387 }
3388 }
3389
Hu_MenuSelectAcceptPlayerSetup(Widget & wi,Widget::Action action)3390 void Hu_MenuSelectAcceptPlayerSetup(Widget &wi, Widget::Action action)
3391 {
3392 Page &page = wi.page();
3393 LineEditWidget &plrNameEdit = page.findWidget(Widget::Id1).as<LineEditWidget>();
3394 #if __JHEXEN__
3395 ListWidget &plrClassList = page.findWidget(Widget::Id2).as<ListWidget>();
3396 #endif
3397 ListWidget &plrColorList = page.findWidget(Widget::Id3).as<ListWidget>();
3398
3399 #if __JHEXEN__
3400 cfg.netClass = plrClassList.selection();
3401 #endif
3402 // The color translation map is stored in the list item data member.
3403 cfg.common.netColor = plrColorList.itemData(plrColorList.selection());
3404
3405 if(action != Widget::Deactivated) return;
3406
3407 char buf[300];
3408 strcpy(buf, "net-name ");
3409 M_StrCatQuoted(buf, plrNameEdit.text().toUtf8().constData(), 300);
3410 DD_Execute(false, buf);
3411
3412 if(IS_NETGAME)
3413 {
3414 strcpy(buf, "setname ");
3415 M_StrCatQuoted(buf, plrNameEdit.text().toUtf8().constData(), 300);
3416 DD_Execute(false, buf);
3417 #if __JHEXEN__
3418 // Must do 'setclass' first; the real class and color do not change
3419 // until the server sends us a notification -- this means if we do
3420 // 'setcolor' first, the 'setclass' after it will override the color
3421 // change (or such would appear to be the case).
3422 DD_Executef(false, "setclass %i", cfg.netClass);
3423 #endif
3424 DD_Executef(false, "setcolor %i", cfg.common.netColor);
3425 }
3426
3427 Hu_MenuSetPage("Options");
3428 }
3429
Hu_MenuSelectQuitGame(Widget &,Widget::Action action)3430 void Hu_MenuSelectQuitGame(Widget & /*wi*/, Widget::Action action)
3431 {
3432 if(action != Widget::Deactivated) return;
3433 G_QuitGame();
3434 }
3435
Hu_MenuSelectEndGame(Widget &,Widget::Action action)3436 void Hu_MenuSelectEndGame(Widget & /*wi*/, Widget::Action action)
3437 {
3438 if(action != Widget::Deactivated) return;
3439 DD_Executef(true, "endgame");
3440 }
3441
Hu_MenuSelectLoadGame(Widget &,Widget::Action action)3442 void Hu_MenuSelectLoadGame(Widget & /*wi*/, Widget::Action action)
3443 {
3444 if(action != Widget::Deactivated) return;
3445
3446 if(!Get(DD_NOVIDEO))
3447 {
3448 if(IS_CLIENT && !Get(DD_PLAYBACK))
3449 {
3450 Hu_MsgStart(MSG_ANYKEY, LOADNET, NULL, 0, NULL);
3451 return;
3452 }
3453 }
3454
3455 Hu_MenuSetPage("LoadGame");
3456 }
3457
Hu_MenuSelectSaveGame(Widget &,Widget::Action action)3458 void Hu_MenuSelectSaveGame(Widget & /*wi*/, Widget::Action action)
3459 {
3460 player_t *player = &players[CONSOLEPLAYER];
3461
3462 if(action != Widget::Deactivated) return;
3463
3464 if(!Get(DD_NOVIDEO))
3465 {
3466 if(IS_CLIENT)
3467 {
3468 #if __JDOOM__ || __JDOOM64__
3469 Hu_MsgStart(MSG_ANYKEY, SAVENET, NULL, 0, NULL);
3470 #endif
3471 return;
3472 }
3473
3474 if(G_GameState() != GS_MAP)
3475 {
3476 Hu_MsgStart(MSG_ANYKEY, SAVEOUTMAP, NULL, 0, NULL);
3477 return;
3478 }
3479
3480 if(player->playerState == PST_DEAD)
3481 {
3482 Hu_MsgStart(MSG_ANYKEY, SAVEDEAD, NULL, 0, NULL);
3483 return;
3484 }
3485 }
3486
3487 Hu_MenuCommand(MCMD_OPEN);
3488 Hu_MenuSetPage("SaveGame");
3489 }
3490
3491 #if __JHEXEN__
Hu_MenuSelectPlayerClass(Widget & wi,Widget::Action action)3492 void Hu_MenuSelectPlayerClass(Widget &wi, Widget::Action action)
3493 {
3494 Page &skillPage = Hu_MenuPage("Skill");
3495 int option = wi.userValue2().toInt();
3496
3497 if(action != Widget::Deactivated) return;
3498
3499 if(IS_NETGAME)
3500 {
3501 P_SetMessageWithFlags(&players[CONSOLEPLAYER], "You can't start a new game from within a netgame!", LMF_NO_HIDE);
3502 return;
3503 }
3504
3505 if(option < 0)
3506 {
3507 // Random class.
3508 // Number of user-selectable classes.
3509 mnPlrClass = (menuTime / 5) % 3;
3510 }
3511 else
3512 {
3513 mnPlrClass = option;
3514 }
3515
3516 ButtonWidget *btn;
3517 btn = &skillPage.findWidget(Widget::Id0).as<ButtonWidget>();
3518 btn->setText(GET_TXT(PCLASS_INFO(mnPlrClass)->skillModeName[SM_BABY]));
3519 if(!btn->text().isEmpty() && btn->text().first().isLetterOrNumber()) btn->setShortcut(btn->text().first().toLatin1());
3520
3521 btn = &skillPage.findWidget(Widget::Id1).as<ButtonWidget>();
3522 btn->setText(GET_TXT(PCLASS_INFO(mnPlrClass)->skillModeName[SM_EASY]));
3523 if(!btn->text().isEmpty() && btn->text().first().isLetterOrNumber()) btn->setShortcut(btn->text().first().toLatin1());
3524
3525 btn = &skillPage.findWidget(Widget::Id2).as<ButtonWidget>();
3526 btn->setText(GET_TXT(PCLASS_INFO(mnPlrClass)->skillModeName[SM_MEDIUM]));
3527 if(!btn->text().isEmpty() && btn->text().first().isLetterOrNumber()) btn->setShortcut(btn->text().first().toLatin1());
3528
3529 btn = &skillPage.findWidget(Widget::Id3).as<ButtonWidget>();
3530 btn->setText(GET_TXT(PCLASS_INFO(mnPlrClass)->skillModeName[SM_HARD]));
3531 if(!btn->text().isEmpty() && btn->text().first().isLetterOrNumber()) btn->setShortcut(btn->text().first().toLatin1());
3532
3533 btn = &skillPage.findWidget(Widget::Id4).as<ButtonWidget>();
3534 btn->setText(GET_TXT(PCLASS_INFO(mnPlrClass)->skillModeName[SM_NIGHTMARE]));
3535 if(!btn->text().isEmpty() && btn->text().first().isLetterOrNumber()) btn->setShortcut(btn->text().first().toLatin1());
3536
3537 switch(mnPlrClass)
3538 {
3539 case PCLASS_FIGHTER: skillPage.setX(120); break;
3540 case PCLASS_CLERIC: skillPage.setX(116); break;
3541 case PCLASS_MAGE: skillPage.setX(112); break;
3542 }
3543 Hu_MenuSetPage(&skillPage);
3544 }
3545
Hu_MenuFocusOnPlayerClass(Widget & wi,Widget::Action action)3546 void Hu_MenuFocusOnPlayerClass(Widget &wi, Widget::Action action)
3547 {
3548 if(action != Widget::FocusGained) return;
3549
3550 playerclass_t plrClass = playerclass_t(wi.userValue2().toInt());
3551 MobjPreviewWidget &mop = wi.page().findWidget(Widget::Id0).as<MobjPreviewWidget>();
3552 mop.setPlayerClass(plrClass);
3553 mop.setMobjType((PCLASS_NONE == plrClass? MT_NONE : PCLASS_INFO(plrClass)->mobjType));
3554
3555 Hu_MenuDefaultFocusAction(wi, action);
3556 }
3557 #endif
3558
Hu_MenuSelectEpisode(Widget & wi,Widget::Action)3559 void Hu_MenuSelectEpisode(Widget &wi, Widget::Action /*action*/)
3560 {
3561 mnEpisode = wi.as<ButtonWidget>().userValue().toString();
3562 #if __JHEXEN__
3563 Hu_MenuSetPage("PlayerClass");
3564 #else
3565 Hu_MenuSetPage("Skill");
3566 #endif
3567 }
3568
3569 #if __JDOOM__ || __JHERETIC__
Hu_MenuConfirmOrderCommericalVersion(msgresponse_t,int,void *)3570 int Hu_MenuConfirmOrderCommericalVersion(msgresponse_t /*response*/, int /*userValue*/, void * /*context*/)
3571 {
3572 G_StartHelp();
3573 return true;
3574 }
3575
Hu_MenuActivateNotSharewareEpisode(Widget &,Widget::Action action)3576 void Hu_MenuActivateNotSharewareEpisode(Widget & /*wi*/, Widget::Action action)
3577 {
3578 if(action != Widget::Deactivated) return;
3579 Hu_MsgStart(MSG_ANYKEY, SWSTRING, Hu_MenuConfirmOrderCommericalVersion, 0, NULL);
3580 }
3581 #endif
3582
Hu_MenuFocusSkillMode(Widget & wi,Widget::Action action)3583 void Hu_MenuFocusSkillMode(Widget &wi, Widget::Action action)
3584 {
3585 if(action != Widget::FocusGained) return;
3586 mnSkillmode = skillmode_t(wi.userValue2().toInt());
3587 Hu_MenuDefaultFocusAction(wi, action);
3588 }
3589
3590 #if __JDOOM__ || __JHERETIC__
Hu_MenuConfirmInitNewGame(msgresponse_t response,int,void *)3591 static int Hu_MenuConfirmInitNewGame(msgresponse_t response, int /*userValue*/, void * /*context*/)
3592 {
3593 if (response == MSG_YES)
3594 {
3595 Hu_MenuInitNewGame(true);
3596 }
3597 return true;
3598 }
3599 #endif
3600
3601 /**
3602 * Initialize a new singleplayer game according to the options set via the menu.
3603 * @param confirmed If @c true this game configuration has already been confirmed.
3604 */
Hu_MenuInitNewGame(bool confirmed)3605 static void Hu_MenuInitNewGame(bool confirmed)
3606 {
3607 #if __JDOOM__ || __JHERETIC__
3608 const int nightmareTextNum = Defs().getTextNum("NIGHTMARE");
3609 if (nightmareTextNum >= 0 && strlen(Defs().text[nightmareTextNum].text) > 0)
3610 {
3611 if (!confirmed && mnSkillmode == SM_NIGHTMARE)
3612 {
3613 Hu_MsgStart(MSG_YESNO, Defs().text[nightmareTextNum].text, Hu_MenuConfirmInitNewGame, 0, NULL);
3614 return;
3615 }
3616 }
3617 #else
3618 DENG2_UNUSED(confirmed);
3619 #endif
3620
3621 Hu_MenuCommand(chooseCloseMethod());
3622
3623 #if __JHEXEN__
3624 cfg.playerClass[CONSOLEPLAYER] = playerclass_t(mnPlrClass);
3625 #endif
3626
3627 GameRules newRules{gfw_DefaultGameRules()};
3628 GameRules_Set(newRules, skill, mnSkillmode);
3629
3630 Record const &episodeDef = Defs().episodes.find("id", mnEpisode);
3631 G_SetGameActionNewSession(newRules, mnEpisode, de::makeUri(episodeDef.gets("startMap")));
3632 }
3633
Hu_MenuActionInitNewGame(Widget &,Widget::Action action)3634 void Hu_MenuActionInitNewGame(Widget & /*wi*/, Widget::Action action)
3635 {
3636 if(action != Widget::Deactivated) return;
3637 Hu_MenuInitNewGame(false);
3638 }
3639
Hu_MenuSelectControlPanelLink(Widget & wi,Widget::Action action)3640 void Hu_MenuSelectControlPanelLink(Widget &wi, Widget::Action action)
3641 {
3642 #define NUM_PANEL_NAMES 1
3643
3644 static char const *panelNames[NUM_PANEL_NAMES] = {
3645 "taskbar" //,
3646 //"panel audio",
3647 //"panel input"
3648 };
3649
3650 if(action != Widget::Deactivated) return;
3651
3652 int idx = wi.userValue2().toInt();
3653 if(idx < 0 || idx > NUM_PANEL_NAMES - 1)
3654 {
3655 idx = 0;
3656 }
3657
3658 DD_Execute(true, panelNames[idx]);
3659
3660 #undef NUM_PANEL_NAMES
3661 }
3662
D_CMD(MenuOpen)3663 D_CMD(MenuOpen)
3664 {
3665 DENG2_UNUSED(src);
3666
3667 if(argc > 1)
3668 {
3669 if(!qstricmp(argv[1], "open"))
3670 {
3671 Hu_MenuCommand(MCMD_OPEN);
3672 return true;
3673 }
3674 if(!qstricmp(argv[1], "close"))
3675 {
3676 Hu_MenuCommand(MCMD_CLOSE);
3677 return true;
3678 }
3679
3680 char const *pageName = argv[1];
3681 if(Hu_MenuHasPage(pageName))
3682 {
3683 Hu_MenuCommand(MCMD_OPEN);
3684 Hu_MenuSetPage(pageName);
3685 return true;
3686 }
3687 return false;
3688 }
3689
3690 Hu_MenuCommand(!menuActive? MCMD_OPEN : MCMD_CLOSE);
3691 return true;
3692 }
3693
3694 /**
3695 * Routes console commands for menu actions and navigation into the menu subsystem.
3696 */
D_CMD(MenuCommand)3697 D_CMD(MenuCommand)
3698 {
3699 DENG2_UNUSED2(src, argc);
3700
3701 if(menuActive)
3702 {
3703 char const *cmd = argv[0] + 4;
3704 if(!qstricmp(cmd, "up"))
3705 {
3706 Hu_MenuCommand(MCMD_NAV_UP);
3707 return true;
3708 }
3709 if(!qstricmp(cmd, "down"))
3710 {
3711 Hu_MenuCommand(MCMD_NAV_DOWN);
3712 return true;
3713 }
3714 if(!qstricmp(cmd, "left"))
3715 {
3716 Hu_MenuCommand(MCMD_NAV_LEFT);
3717 return true;
3718 }
3719 if(!qstricmp(cmd, "right"))
3720 {
3721 Hu_MenuCommand(MCMD_NAV_RIGHT);
3722 return true;
3723 }
3724 if(!qstricmp(cmd, "back"))
3725 {
3726 Hu_MenuCommand(MCMD_NAV_OUT);
3727 return true;
3728 }
3729 if(!qstricmp(cmd, "delete"))
3730 {
3731 Hu_MenuCommand(MCMD_DELETE);
3732 return true;
3733 }
3734 if(!qstricmp(cmd, "select"))
3735 {
3736 Hu_MenuCommand(MCMD_SELECT);
3737 return true;
3738 }
3739 if(!qstricmp(cmd, "pagedown"))
3740 {
3741 Hu_MenuCommand(MCMD_NAV_PAGEDOWN);
3742 return true;
3743 }
3744 if(!qstricmp(cmd, "pageup"))
3745 {
3746 Hu_MenuCommand(MCMD_NAV_PAGEUP);
3747 return true;
3748 }
3749 }
3750 return false;
3751 }
3752
Hu_MenuConsoleRegister()3753 void Hu_MenuConsoleRegister()
3754 {
3755 C_VAR_FLOAT("menu-scale", &cfg.common.menuScale, 0, .1f, 1);
3756 C_VAR_BYTE ("menu-stretch", &cfg.common.menuScaleMode, 0, SCALEMODE_FIRST, SCALEMODE_LAST);
3757 C_VAR_FLOAT("menu-flash-r", &cfg.common.menuTextFlashColor[CR], 0, 0, 1);
3758 C_VAR_FLOAT("menu-flash-g", &cfg.common.menuTextFlashColor[CG], 0, 0, 1);
3759 C_VAR_FLOAT("menu-flash-b", &cfg.common.menuTextFlashColor[CB], 0, 0, 1);
3760 C_VAR_INT ("menu-flash-speed", &cfg.common.menuTextFlashSpeed, 0, 0, 50);
3761 C_VAR_BYTE ("menu-cursor-rotate", &cfg.common.menuCursorRotate, 0, 0, 1);
3762 C_VAR_INT ("menu-effect", &cfg.common.menuEffectFlags, 0, 0, MEF_EVERYTHING);
3763 C_VAR_FLOAT("menu-color-r", &cfg.common.menuTextColors[0][CR], 0, 0, 1);
3764 C_VAR_FLOAT("menu-color-g", &cfg.common.menuTextColors[0][CG], 0, 0, 1);
3765 C_VAR_FLOAT("menu-color-b", &cfg.common.menuTextColors[0][CB], 0, 0, 1);
3766 C_VAR_FLOAT("menu-colorb-r", &cfg.common.menuTextColors[1][CR], 0, 0, 1);
3767 C_VAR_FLOAT("menu-colorb-g", &cfg.common.menuTextColors[1][CG], 0, 0, 1);
3768 C_VAR_FLOAT("menu-colorb-b", &cfg.common.menuTextColors[1][CB], 0, 0, 1);
3769 C_VAR_FLOAT("menu-colorc-r", &cfg.common.menuTextColors[2][CR], 0, 0, 1);
3770 C_VAR_FLOAT("menu-colorc-g", &cfg.common.menuTextColors[2][CG], 0, 0, 1);
3771 C_VAR_FLOAT("menu-colorc-b", &cfg.common.menuTextColors[2][CB], 0, 0, 1);
3772 C_VAR_FLOAT("menu-colord-r", &cfg.common.menuTextColors[3][CR], 0, 0, 1);
3773 C_VAR_FLOAT("menu-colord-g", &cfg.common.menuTextColors[3][CG], 0, 0, 1);
3774 C_VAR_FLOAT("menu-colord-b", &cfg.common.menuTextColors[3][CB], 0, 0, 1);
3775 C_VAR_FLOAT("menu-glitter", &cfg.common.menuTextGlitter, 0, 0, 1);
3776 C_VAR_INT ("menu-fog", &cfg.common.hudFog, 0, 0, 5);
3777 C_VAR_FLOAT("menu-shadow", &cfg.common.menuShadow, 0, 0, 1);
3778 C_VAR_INT ("menu-patch-replacement", &cfg.common.menuPatchReplaceMode, 0, 0, 1);
3779 C_VAR_BYTE ("menu-slam", &cfg.common.menuSlam, 0, 0, 1);
3780 C_VAR_BYTE ("menu-hotkeys", &cfg.common.menuShortcutsEnabled, 0, 0, 1);
3781 #if __JDOOM__ || __JDOOM64__
3782 C_VAR_INT ("menu-quitsound", &cfg.menuQuitSound, 0, 0, 1);
3783 #endif
3784 C_VAR_BYTE ("menu-save-suggestname", &cfg.common.menuGameSaveSuggestDescription, 0, 0, 1);
3785
3786 C_CMD("menu", "s", MenuOpen);
3787 C_CMD("menu", "", MenuOpen);
3788 C_CMD("menuup", "", MenuCommand);
3789 C_CMD("menudown", "", MenuCommand);
3790 C_CMD("menupageup", "", MenuCommand);
3791 C_CMD("menupagedown", "", MenuCommand);
3792 C_CMD("menuleft", "", MenuCommand);
3793 C_CMD("menuright", "", MenuCommand);
3794 C_CMD("menuselect", "", MenuCommand);
3795 C_CMD("menudelete", "", MenuCommand);
3796 C_CMD("menuback", "", MenuCommand);
3797 }
3798
3799 } // namespace common
3800