1 /** @file m_ctrl.cpp  Controls menu page and associated widgets.
2  *
3  * @authors Copyright © 2005-2017 Jaakko Keränen <jaakko.keranen@iki.fi>
4  * @authors Copyright © 2005-2014 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 "m_ctrl.h"
23 
24 #include <cstring>
25 #include <cctype>
26 #include <cstdio>
27 
28 #include "hu_menu.h"
29 #include "hu_stuff.h"
30 #include "menu/page.h"
31 #include "menu/widgets/inputbindingwidget.h"
32 #include "menu/widgets/labelwidget.h"
33 
34 using namespace de;
35 
36 namespace common {
37 namespace menu {
38 
39 static controlconfig_t controlConfig[] =
40 {
41     { "Movement", 0, 0, 0, 0 },
42     { "Forward", 0, "walk", 0, CCF_NON_INVERSE },
43     { "Backward", 0, "walk", 0, CCF_INVERSE },
44     { "Strafe Left", 0, "sidestep", 0, CCF_INVERSE },
45     { "Strafe Right", 0, "sidestep", 0, CCF_NON_INVERSE },
46     { "Turn Left", 0, "turn", 0, CCF_STAGED | CCF_INVERSE | CCF_SIDESTEP_MODIFIER },
47     { "Turn Right", 0, "turn", 0, CCF_STAGED | CCF_NON_INVERSE | CCF_SIDESTEP_MODIFIER },
48     { "Jump", 0, 0, "impulse jump", 0 },
49     { "Use", 0, 0, "impulse use", 0 },
50     { "Fly Up", 0, "zfly", 0, CCF_STAGED | CCF_NON_INVERSE },
51     { "Fly Down", 0, "zfly", 0, CCF_STAGED | CCF_INVERSE },
52     { "Fall To Ground", 0, 0, "impulse falldown", 0 },
53     { "Speed", 0, "speed", 0, 0 },
54     { "Strafe", 0, "strafe", 0, 0 },
55 
56     { "Looking", 0, 0, 0, 0 },
57     { "Look Up", 0, "look", 0, CCF_STAGED | CCF_NON_INVERSE },
58     { "Look Down", 0, "look", 0, CCF_STAGED | CCF_INVERSE },
59     { "Look Center", 0, 0, "impulse lookcenter", 0 },
60 
61     { "Weapons", 0, 0, 0, 0 },
62     { "Attack/Fire", 0, "attack", 0, 0 },
63     { "Next Weapon", 0, 0, "impulse nextweapon", 0 },
64     { "Previous Weapon", 0, 0, "impulse prevweapon", 0 },
65 
66 #if __JDOOM__ || __JDOOM64__
67     { "Fist/Chainsaw", 0, 0, "impulse weapon1", 0 },
68     { "Chainsaw/Fist", 0, 0, "impulse weapon8", 0 },
69     { "Pistol", 0, 0, "impulse weapon2", 0 },
70     { "Super SG/Shotgun", 0, 0, "impulse weapon3", 0 },
71     { "Shotgun/Super SG", 0, 0, "impulse weapon9", 0 },
72     { "Chaingun", 0, 0, "impulse weapon4", 0 },
73     { "Rocket Launcher", 0, 0, "impulse weapon5", 0 },
74     { "Plasma Rifle", 0, 0, "impulse weapon6", 0 },
75     { "BFG 9000", 0, 0, "impulse weapon7", 0 },
76 #endif
77 #if __JDOOM64__
78     { "Unmaker", 0, 0, "impulse weapon10", 0 },
79 #endif
80 
81 #if __JHERETIC__
82     { "Gauntlets/Staff", 0, 0, "impulse weapon1", 0 },
83     { "Elvenwand", 0, 0, "impulse weapon2", 0 },
84     { "Crossbow", 0, 0, "impulse weapon3", 0 },
85     { "Dragon Claw", 0, 0, "impulse weapon4", 0 },
86     { "Hellstaff", 0, 0, "impulse weapon5", 0 },
87     { "Phoenix Rod", 0, 0, "impulse weapon6", 0 },
88     { "Firemace", 0, 0, "impulse weapon7", 0 },
89 #endif
90 
91 #if __JHEXEN__
92     { "Weapon 1", 0, 0, "impulse weapon1", 0 },
93     { "Weapon 2", 0, 0, "impulse weapon2", 0 },
94     { "Weapon 3", 0, 0, "impulse weapon3", 0 },
95     { "Weapon 4", 0, 0, "impulse weapon4", 0 },
96 #endif
97 
98 #if __JHERETIC__ || __JHEXEN__
99     { "Inventory", 0, 0, 0, 0 },
100     { "Move Left", 0, 0, "impulse previtem", CCF_REPEAT },
101     { "Move Right", 0, 0, "impulse nextitem", CCF_REPEAT },
102     { "Use Item", 0, 0, "impulse useitem", 0 },
103     { "Panic!", 0, 0, "impulse panic", 0 },
104 #endif
105 
106 #ifdef __JHERETIC__
107     { /* (char const *) TXT_TXT_INV_INVULNERABILITY */ "Invincibility", 0, 0, "impulse invulnerability", 0 },
108     { (char const *) TXT_TXT_INV_INVISIBILITY, 0, 0, "impulse invisibility", 0 },
109     { (char const *) TXT_TXT_INV_HEALTH, 0, 0, "impulse health", 0 },
110     { (char const *) TXT_TXT_INV_SUPERHEALTH, 0, 0, "impulse superhealth", 0 },
111     { (char const *) TXT_TXT_INV_TOMEOFPOWER, 0, 0, "impulse tome", 0 },
112     { (char const *) TXT_TXT_INV_TORCH, 0, 0, "impulse torch", 0 },
113     { /* (char const *) TXT_TXT_INV_FIREBOMB */ "Time Bomb", 0, 0, "impulse firebomb", 0 },
114     { (char const *) TXT_TXT_INV_EGG, 0, 0, "impulse egg", 0 },
115     { (char const *) TXT_TXT_INV_FLY, 0, 0, "impulse fly", 0 },
116     { (char const *) TXT_TXT_INV_TELEPORT, 0, 0, "impulse teleport", 0 },
117 #endif
118 
119 #ifdef __JHEXEN__
120     { (char const *) TXT_TXT_INV_TORCH, 0, 0, "impulse torch", 0 },
121     { (char const *) TXT_TXT_INV_HEALTH, 0, 0, "impulse health", 0 },
122     { (char const *) TXT_TXT_INV_SUPERHEALTH, 0, 0, "impulse mysticurn", 0 },
123     { (char const *) TXT_TXT_INV_BOOSTMANA, 0, 0, "impulse krater", 0 },
124     { (char const *) TXT_TXT_INV_SPEED, 0, 0, "impulse speedboots", 0 },
125     { (char const *) TXT_TXT_INV_BLASTRADIUS, 0, 0, "impulse blast", 0 },
126     { (char const *) TXT_TXT_INV_TELEPORT, 0, 0, "impulse teleport", 0 },
127     { (char const *) TXT_TXT_INV_TELEPORTOTHER, 0, 0, "impulse teleportother", 0 },
128     { (char const *) TXT_TXT_INV_POISONBAG, 0, 0, "impulse poisonbag", 0 },
129     { (char const *) TXT_TXT_INV_INVULNERABILITY, 0, 0, "impulse invulnerability", 0 },
130     { (char const *) TXT_TXT_INV_SUMMON, 0, 0, "impulse darkservant", 0 },
131     { (char const *) TXT_TXT_INV_EGG, 0, 0, "impulse egg", 0 },
132 #endif
133 
134     { "Chat", 0, 0, 0, 0 },
135     { "Open Chat", 0, 0, "beginchat", CCF_MULTIPLAYER },
136 
137 #if __JDOOM__ || __JDOOM64__
138     { "Green Chat", 0, 0, "beginchat 0", CCF_MULTIPLAYER },
139     { "Indigo Chat", 0, 0, "beginchat 1", CCF_MULTIPLAYER },
140     { "Brown Chat", 0, 0, "beginchat 2", CCF_MULTIPLAYER },
141     { "Red Chat", 0, 0, "beginchat 3", CCF_MULTIPLAYER },
142 #endif
143 
144 #if __JHERETIC__
145     { "Green Chat", 0, 0, "beginchat 0", CCF_MULTIPLAYER },
146     { "Yellow Chat", 0, 0, "beginchat 1", CCF_MULTIPLAYER },
147     { "Red Chat", 0, 0, "beginchat 2", CCF_MULTIPLAYER },
148     { "Blue Chat", 0, 0, "beginchat 3", CCF_MULTIPLAYER },
149 #endif
150 
151     { "Send Message", "chat", 0, "chatcomplete", 0 },
152     { "Cancel Message", "chat", 0, "chatcancel", 0 },
153     { "Macro 1", "chat", 0, "chatsendmacro 0", 0 },
154     { "Macro 2", "chat", 0, "chatsendmacro 1", 0 },
155     { "Macro 3", "chat", 0, "chatsendmacro 2", 0 },
156     { "Macro 4", "chat", 0, "chatsendmacro 3", 0 },
157     { "Macro 5", "chat", 0, "chatsendmacro 4", 0 },
158     { "Macro 6", "chat", 0, "chatsendmacro 5", 0 },
159     { "Macro 7", "chat", 0, "chatsendmacro 6", 0 },
160     { "Macro 8", "chat", 0, "chatsendmacro 7", 0 },
161     { "Macro 9", "chat", 0, "chatsendmacro 8", 0 },
162     { "Macro 10", "chat", 0, "chatsendmacro 9", 0 },
163     { "Backspace", "chat", 0, "chatdelete", CCF_REPEAT },
164 
165     { "Map", 0, 0, 0, 0 },
166     { "Show/Hide Map", 0, 0, "impulse automap", 0 },
167     { "Zoom In", 0, "mapzoom", 0, CCF_NON_INVERSE },
168     { "Zoom Out", 0, "mapzoom", 0, CCF_INVERSE },
169     { "Zoom Maximum", "map", 0, "impulse zoommax", 0 },
170     { "Pan Left", 0, "mappanx", 0, CCF_INVERSE },
171     { "Pan Right", 0, "mappanx", 0, CCF_NON_INVERSE },
172     { "Pan Up", 0, "mappany", 0, CCF_NON_INVERSE },
173     { "Pan Down", 0, "mappany", 0, CCF_INVERSE },
174     { "Toggle Follow", "map", 0, "impulse follow", 0 },
175     { "Toggle Rotation", "map", 0, "impulse rotate", 0 },
176     { "Add Mark", "map", 0, "impulse addmark", 0 },
177     { "Clear Marks", "map", 0, "impulse clearmarks", 0 },
178 
179     { "HUD", 0, 0, 0, 0 },
180     { "Show HUD", 0, 0, "impulse showhud", 0 },
181     { "Show Score", 0, 0, "impulse showscore", CCF_REPEAT },
182     { "Smaller View", 0, 0, "sub view-size 1", CCF_REPEAT },
183     { "Larger View", 0, 0, "add view-size 1", CCF_REPEAT },
184 
185     { "Msg Refresh", 0, 0, "impulse msgrefresh", 0 },
186 
187     { "Shortcuts", 0, 0, 0, 0 },
188     { "Show Taskbar", 0, 0, "taskbar", 0 },
189     { "Pause Game", 0, 0, "pause", 0 },
190 #if !__JDOOM64__
191     { "Help Screen", "shortcut", 0, "helpscreen", 0 },
192 #endif
193     { "End Game", "shortcut", 0, "endgame", 0 },
194     { "Save Game", "shortcut", 0, "menu savegame", 0 },
195     { "Load Game", "shortcut", 0, "menu loadgame", 0 },
196     { "Quick Save", "shortcut", 0, "quicksave", 0 },
197     { "Quick Load", "shortcut", 0, "quickload", 0 },
198     { "Sound Options", "shortcut", 0, "menu soundoptions", 0 },
199     { "Toggle Messages", "shortcut", 0, "toggle msg-show", 0 },
200     { "Gamma Adjust", "shortcut", 0, "togglegamma", 0 },
201     { "Screenshot", "shortcut", 0, "screenshot", 0 },
202     { "Quit", "shortcut", 0, "quit", 0 },
203 
204     { "Menu", 0, 0, 0, 0 },
205     { "Show/Hide Menu", "shortcut", 0, "menu", 0 },
206     { "Previous Menu", "menu", 0, "menuback", CCF_REPEAT },
207     { "Move Up", "menu", 0, "menuup", CCF_REPEAT },
208     { "Move Down", "menu", 0, "menudown", CCF_REPEAT },
209     { "Move Left", "menu", 0, "menuleft", CCF_REPEAT },
210     { "Move Right", "menu", 0, "menuright", CCF_REPEAT },
211     { "Select", "menu", 0, "menuselect", 0 },
212 
213     { "On-Screen Questions", 0, 0, 0, 0 },
214     { "Answer Yes", "message", 0, "messageyes", 0 },
215     { "Answer No", "message", 0, "messageno", 0 },
216     { "Cancel", "message", 0, "messagecancel", 0 },
217 
218 //    { "Virtual Reality", 0, 0, 0, 0 },
219 //    { "Reset Tracking", 0, 0, "resetriftpose", 0 }
220 };
221 
222 static void Hu_MenuDrawControlsPage(Page const &page, Vector2i const &offset);
223 
Hu_MenuActivateBindingsGrab(Widget &,Widget::Action)224 void Hu_MenuActivateBindingsGrab(Widget &, Widget::Action)
225 {
226     // Start grabbing for this control.
227     DD_SetInteger(DD_SYMBOLIC_ECHO, true);
228 }
229 
Hu_MenuInitControlsPage()230 void Hu_MenuInitControlsPage()
231 {
232     Page *page = Hu_MenuAddPage(new Page("ControlOptions", Vector2i(32, 40), 0, Hu_MenuDrawControlsPage));
233     page->setLeftColumnWidth(.4f);
234     page->setTitle("Controls");
235     page->setPredefinedFont(MENU_FONT1, FID(GF_FONTA));
236     page->setPreviousPage(Hu_MenuPagePtr("Options"));
237 
238     int group = 0;
239     int const configCount = sizeof(controlConfig) / sizeof(controlConfig[0]);
240     for(int i = 0; i < configCount; ++i)
241     {
242         controlconfig_t *binds = &controlConfig[i];
243 
244         char const *labelText = binds->text;
245         if (labelText && (PTR2INT(labelText) > 0 && PTR2INT(labelText) < NUMTEXT))
246         {
247             labelText = GET_TXT(PTR2INT(labelText));
248         }
249 
250         if (!binds->command && !binds->controlName)
251         {
252             // Inert.
253             page->addWidget(new LabelWidget(labelText))
254                     .setGroup(++group)
255                     .setColor(MENU_COLOR2);
256         }
257         else
258         {
259             page->addWidget(new LabelWidget(labelText))
260                     .setLeft()
261                     .setGroup(group);
262 
263             InputBindingWidget *binding = new InputBindingWidget;
264             binding->binds = binds;
265             binding->setRight();
266             binding->setGroup(group);
267             binding->setAction(Widget::Activated,   Hu_MenuActivateBindingsGrab);
268             binding->setAction(Widget::FocusGained, Hu_MenuDefaultFocusAction);
269 
270             page->addWidget(binding);
271         }
272     }
273 }
274 
Hu_MenuDrawControlsPage(Page const &,Vector2i const &)275 static void Hu_MenuDrawControlsPage(Page const & /*page*/, Vector2i const & /*offset*/)
276 {
277     Vector2i origin(SCREENWIDTH / 2, (SCREENHEIGHT / 2) + ((SCREENHEIGHT / 2 - 5) / cfg.common.menuScale));
278     Hu_MenuDrawPageHelp("Select to assign new, [Del] to clear", origin);
279 }
280 
Hu_MenuControlGrabDrawer(char const * niceName,float alpha)281 void Hu_MenuControlGrabDrawer(char const *niceName, float alpha)
282 {
283     DGL_Enable(DGL_TEXTURE_2D);
284 
285     FR_SetFont(FID(GF_FONTA));
286     FR_LoadDefaultAttrib();
287     FR_SetLeading(0);
288     FR_SetColorAndAlpha(cfg.common.menuTextColors[1][CR], cfg.common.menuTextColors[1][CG], cfg.common.menuTextColors[1][CB], alpha);
289     FR_DrawTextXY3("Press key or move controller for", SCREENWIDTH/2, SCREENHEIGHT/2-2, ALIGN_BOTTOM, Hu_MenuMergeEffectWithDrawTextFlags(DTF_ONLY_SHADOW));
290 
291     FR_SetFont(FID(GF_FONTB));
292     FR_SetColorAndAlpha(cfg.common.menuTextColors[2][CR], cfg.common.menuTextColors[2][CG], cfg.common.menuTextColors[2][CB], alpha);
293     FR_DrawTextXY3(niceName, SCREENWIDTH/2, SCREENHEIGHT/2+2, ALIGN_TOP, Hu_MenuMergeEffectWithDrawTextFlags(DTF_ONLY_SHADOW));
294 
295     DGL_Disable(DGL_TEXTURE_2D);
296 }
297 
298 } // namespace menu
299 } // namespace common
300 
301