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