1 /** @file hu_msg.cpp Important game state change messages.
2 *
3 * @authors Copyright © 2003-2017 Jaakko Keränen <jaakko.keranen@iki.fi>
4 * @authors Copyright © 2006-2014 Daniel Swanson <danij@dengine.net>
5 * @authors Copyright © 2006 Jamie Jones <jamie_jones_au@yahoo.com.au>
6 * @authors Copyright © 1993-1996 id Software, Inc.
7 *
8 * @par License
9 * GPL: http://www.gnu.org/licenses/gpl.html
10 *
11 * <small>This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by the
13 * Free Software Foundation; either version 2 of the License, or (at your
14 * option) any later version. This program is distributed in the hope that it
15 * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
16 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
17 * Public License for more details. You should have received a copy of the GNU
18 * General Public License along with this program; if not, write to the Free
19 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20 * 02110-1301 USA</small>
21 */
22
23 #include "common.h"
24 #include <cstdlib>
25 #include <cstring>
26 #include <de/memory.h>
27 #include "hu_msg.h"
28 #include "hu_menu.h"
29 #include "hu_stuff.h"
30
31 using namespace de;
32 using namespace common;
33
34 static dd_bool awaitingResponse;
35 static int messageToPrint; // 1 = message to be printed.
36 static msgresponse_t messageResponse;
37
38 static msgtype_t msgType;
39 static msgfunc_t msgCallback;
40 static char *msgText;
41 static int msgUserValue;
42 static void *msgUserPointer;
43
44 static char yesNoMessage[160];
45
Hu_MsgInit()46 void Hu_MsgInit()
47 {
48 awaitingResponse = false;
49 messageToPrint = 0; // 1 = message to be printed.
50 messageResponse = MSG_CANCEL;
51
52 msgCallback = 0;
53 msgText = 0;
54 msgUserValue = 0;
55 msgUserPointer = 0;
56 }
57
Hu_MsgShutdown()58 void Hu_MsgShutdown()
59 {
60 if(msgText)
61 {
62 M_Free(msgText); msgText = 0;
63 }
64 }
65
stopMessage()66 static void stopMessage()
67 {
68 #if __JDOOM__ || __JDOOM64__
69 # define SFX_ENDMESSAGE SFX_SWTCHX
70 #elif __JHERETIC__
71 # define SFX_ENDMESSAGE SFX_CHAT
72 #elif __JHEXEN__
73 # define SFX_ENDMESSAGE SFX_DOOR_LIGHT_CLOSE
74 #endif
75
76 messageToPrint = 0;
77 awaitingResponse = false;
78
79 if(msgText)
80 {
81 M_Free(msgText); msgText = 0;
82 }
83
84 S_LocalSound(SFX_ENDMESSAGE, NULL);
85
86 // Disable the message binding context.
87 DD_Executef(true, "deactivatebcontext message");
88
89 #undef SFX_ENDMESSAGE
90 }
91
92 /**
93 * @todo: Query the bindings to determine the actual controls bound to the
94 * message response commands.
95 */
composeYesNoMessage()96 static void composeYesNoMessage()
97 {
98 char *buf = yesNoMessage, tmp[2];
99
100 buf[0] = 0;
101 tmp[1] = 0;
102
103 // Get the message template.
104 char const *in = PRESSYN;
105
106 for(; *in; in++)
107 {
108 if(in[0] == '%')
109 {
110 if(in[1] == '1')
111 {
112 strcat(buf, "Y");
113 in++;
114 continue;
115 }
116 if(in[1] == '2')
117 {
118 strcat(buf, "N");
119 in++;
120 continue;
121 }
122
123 if(in[1] == '%')
124 in++;
125 }
126 tmp[0] = *in;
127 strcat(buf, tmp);
128 }
129 }
130
drawMessage()131 static void drawMessage()
132 {
133 #define LEADING (0)
134
135 short textFlags = Hu_MenuMergeEffectWithDrawTextFlags(0);
136 Point2Raw origin = {{{SCREENWIDTH/2, SCREENHEIGHT/2}}};
137 char const *questionString = "";
138
139 switch(msgType)
140 {
141 case MSG_ANYKEY: questionString = PRESSKEY; break;
142 case MSG_YESNO: questionString = yesNoMessage; break;
143
144 default: DENG2_ASSERT(!"drawMessage: Internal error, unknown message type.");
145 }
146
147 DGL_Enable(DGL_TEXTURE_2D);
148 FR_SetFont(FID(GF_FONTA));
149 FR_LoadDefaultAttrib();
150 FR_SetLeading(LEADING);
151 FR_SetShadowStrength(cfg.common.menuTextGlitter);
152 FR_SetGlitterStrength(cfg.common.menuShadow);
153 FR_SetColorAndAlpha(cfg.common.menuTextColors[MENU_COLOR4][CR], cfg.common.menuTextColors[MENU_COLOR4][CG], cfg.common.menuTextColors[MENU_COLOR4][CB], 1);
154
155 FR_DrawText3(msgText, &origin, ALIGN_TOP, textFlags);
156 origin.y += FR_TextHeight(msgText);
157 // An additional blank line between the message and response prompt.
158 origin.y += FR_CharHeight('A') * (1+LEADING);
159
160 FR_DrawText3(questionString, &origin, ALIGN_TOP, textFlags);
161 DGL_Disable(DGL_TEXTURE_2D);
162
163 #undef LEADING
164 }
165
Hu_MsgDrawer()166 void Hu_MsgDrawer()
167 {
168 if(!messageToPrint) return;
169
170 dgl_borderedprojectionstate_t bp;
171 GL_ConfigureBorderedProjection(&bp, 0, SCREENWIDTH, SCREENHEIGHT,
172 Get(DD_WINDOW_WIDTH), Get(DD_WINDOW_HEIGHT), scalemode_t(cfg.common.menuScaleMode));
173 GL_BeginBorderedProjection(&bp);
174
175 DGL_MatrixMode(DGL_MODELVIEW);
176 DGL_PushMatrix();
177 DGL_Translatef(SCREENWIDTH/2, SCREENHEIGHT/2, 0);
178 DGL_Scalef(cfg.common.menuScale, cfg.common.menuScale, 1);
179 DGL_Translatef(-(SCREENWIDTH/2), -(SCREENHEIGHT/2), 0);
180
181 drawMessage();
182
183 DGL_MatrixMode(DGL_MODELVIEW);
184 DGL_PopMatrix();
185
186 GL_EndBorderedProjection(&bp);
187 }
188
Hu_MsgTicker()189 void Hu_MsgTicker()
190 {
191 // Check if there has been a response to a message.
192 if(!messageToPrint || awaitingResponse)
193 return;
194
195 // We can now stop the message.
196 stopMessage();
197
198 if(msgType != MSG_ANYKEY && msgCallback)
199 {
200 msgCallback(messageResponse, msgUserValue, msgUserPointer);
201 }
202 }
203
Hu_MsgResponder(event_t * ev)204 int Hu_MsgResponder(event_t *ev)
205 {
206 if(!messageToPrint || msgType != MSG_ANYKEY)
207 return false;
208
209 // We are only interested in key downs.
210 if(ev->state == EVS_DOWN &&
211 (ev->type == EV_KEY || ev->type == EV_MOUSE_BUTTON ||
212 ev->type == EV_JOY_BUTTON))
213 {
214 stopMessage();
215 return true;
216 }
217
218 return true;
219 }
220
Hu_IsMessageActive()221 dd_bool Hu_IsMessageActive()
222 {
223 return messageToPrint;
224 }
225
Hu_IsMessageActiveWithCallback(msgfunc_t callback)226 dd_bool Hu_IsMessageActiveWithCallback(msgfunc_t callback)
227 {
228 return messageToPrint && msgCallback == callback;
229 }
230
Hu_MsgStart(msgtype_t type,char const * msg,msgfunc_t callback,int userValue,void * userPointer)231 void Hu_MsgStart(msgtype_t type, char const *msg, msgfunc_t callback,
232 int userValue, void *userPointer)
233 {
234 DENG2_ASSERT(msg != 0);
235 DENG2_ASSERT(!awaitingResponse);
236
237 awaitingResponse = true;
238 messageResponse = msgresponse_t(0);
239 messageToPrint = 1;
240
241 msgType = type;
242 msgCallback = callback;
243 msgUserValue = userValue;
244 msgUserPointer = userPointer;
245
246 // Take a copy of the message string.
247 msgText = (char *)M_Calloc(strlen(msg) + 1);
248 strncpy(msgText, msg, strlen(msg));
249
250 if(msgType == MSG_YESNO)
251 {
252 composeYesNoMessage();
253 }
254
255 if(!Get(DD_NOVIDEO))
256 {
257 FR_ResetTypeinTimer();
258 }
259
260 // If the console is open, close it. This message must be noticed!
261 Con_Open(false);
262
263 // Enable the message binding context.
264 DD_Execute(true, "activatebcontext message");
265 }
266
267 /**
268 * Handles responses to messages requiring input.
269 */
D_CMD(MsgResponse)270 D_CMD(MsgResponse)
271 {
272 DENG2_UNUSED2(src, argc);
273
274 if(messageToPrint)
275 {
276 // Handle "Press any key to continue" messages.
277 if(msgType == MSG_ANYKEY)
278 {
279 stopMessage();
280 return true;
281 }
282
283 char const *cmd = argv[0] + 7;
284 if(!qstricmp(cmd, "yes"))
285 {
286 awaitingResponse = false;
287 messageResponse = MSG_YES;
288 return true;
289 }
290 if(!qstricmp(cmd, "no"))
291 {
292 awaitingResponse = false;
293 messageResponse = MSG_NO;
294 return true;
295 }
296 if(!qstricmp(cmd, "cancel"))
297 {
298 awaitingResponse = false;
299 messageResponse = MSG_CANCEL;
300 return true;
301 }
302 }
303
304 return false;
305 }
306
Hu_MsgRegister()307 void Hu_MsgRegister()
308 {
309 C_CMD("messageyes", "", MsgResponse)
310 C_CMD("messageno", "", MsgResponse)
311 C_CMD("messagecancel", "", MsgResponse)
312 }
313