1 /** @file intermission.cpp Hexen specific intermission screens.
2 *
3 * @authors Copyright © 2003-2017 Jaakko Keränen <jaakko.keranen@iki.fi>
4 * @authors Copyright © 2005-2014 Daniel Swanson <danij@dengine.net>
5 * @authors Copyright © 1999 Activision
6 *
7 * @par License
8 * GPL: http://www.gnu.org/licenses/gpl.html
9 *
10 * <small>This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by the
12 * Free Software Foundation; either version 2 of the License, or (at your
13 * option) any later version. This program is distributed in the hope that it
14 * will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
15 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
16 * Public License for more details. You should have received a copy of the GNU
17 * General Public License along with this program; if not, write to the Free
18 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
19 * 02110-1301 USA</small>
20 */
21
22 #include "jhexen.h"
23 #include "intermission.h"
24
25 #include <cstdio>
26 #include "d_net.h"
27 #include "d_netcl.h"
28 #include "d_netsv.h"
29 #include "hu_stuff.h"
30 #include "hu_menu.h"
31 #include "g_common.h"
32
33 #define TEXTSPEED (3)
34 #define TEXTWAIT (140)
35
36 #define TALLY_EFFECT_TICKS (20)
37 #define TALLY_FINAL_X_DELTA (23 * FRACUNIT)
38 #define TALLY_FINAL_Y_DELTA (13 * FRACUNIT)
39 #define TALLY_START_XPOS (178 * FRACUNIT)
40 #define TALLY_STOP_XPOS (90 * FRACUNIT)
41 #define TALLY_START_YPOS (132 * FRACUNIT)
42 #define TALLY_STOP_YPOS (83 * FRACUNIT)
43 #define TALLY_TOP_X (85)
44 #define TALLY_TOP_Y (9)
45 #define TALLY_LEFT_X (7)
46 #define TALLY_LEFT_Y (71)
47 #define TALLY_TOTALS_X (291)
48
49 #define MAX_INTRMSN_MESSAGE_SIZE (1024)
50
51 using namespace de;
52
53 enum gametype_t
54 {
55 SINGLE,
56 COOPERATIVE,
57 DEATHMATCH
58 };
59
60 static void IN_WaitStop();
61 static void loadPics();
62 static void unloadPics();
63 static void CheckForSkip();
64 static void initStats();
65 static void drawDeathTally();
66 static void drawNumber(int val, int x, int y, int wrapThresh);
67 static void drawNumberBold(int val, int x, int y, int wrapThresh);
68
69 dd_bool intermission;
70 int interState;
71 int overrideHubMsg; // Override the hub transition message when 1.
72
73 // Used for timing of background animation.
74 static int bcnt;
75
76 static dd_bool skipIntermission;
77 static int interTime = -1;
78 static gametype_t gameType;
79 static int cnt;
80 static int slaughterBoy; // In DM, the player with the most kills.
81 static int totalFrags[MAXPLAYERS];
82
83 static int hubCount;
84
85 static patchid_t dpTallyTop;
86 static patchid_t dpTallyLeft;
87
WI_initVariables()88 void WI_initVariables(/*wbstartstruct_t *wbstartstruct */)
89 {
90 /* wbs = wbstartstruct;
91
92 #ifdef RANGECHECK
93 if(gameMode != commercial)
94 {
95 if(gameMode == retail)
96 RNGCHECK(wbs->epsd, 0, 3);
97 else
98 RNGCHECK(wbs->epsd, 0, 2);
99 }
100 else
101 {
102 RNGCHECK(wbs->last, 0, 8);
103 RNGCHECK(wbs->next, 0, 8);
104 }
105 RNGCHECK(wbs->pnum, 0, MAXPLAYERS);
106 RNGCHECK(wbs->pnum, 0, MAXPLAYERS);
107 #endif
108
109 accelerateStage = 0;
110 cnt =*/ bcnt = 0; /*
111 firstRefresh = 1;
112 me = wbs->pNum;
113 myTeam = cfg.playerColor[wbs->pNum];
114 plrs = wbs->plyr;
115
116 if(!wbs->maxKills)
117 wbs->maxKills = 1;
118 if(!wbs->maxItems)
119 wbs->maxItems = 1;
120 if(!wbs->maxSecret)
121 wbs->maxSecret = 1;
122
123 if(gameMode != retail)
124 if(wbs->epsd > 2)
125 wbs->epsd -= 3;*/
126
127 intermission = true;
128 interState = 0;
129 skipIntermission = false;
130 interTime = 0;
131 }
132
IN_Begin(wbstartstruct_t const &)133 void IN_Begin(wbstartstruct_t const & /*wbstartstruct*/)
134 {
135 DENG2_ASSERT(gfw_Rule(deathmatch));
136
137 WI_initVariables();
138 loadPics();
139 initStats();
140 }
141
IN_WaitStop()142 void IN_WaitStop()
143 {
144 if(!--cnt)
145 {
146 IN_End();
147 G_IntermissionDone();
148 }
149 }
150
IN_End()151 void IN_End()
152 {
153 NetSv_Intermission(IMF_END, 0, 0);
154 unloadPics();
155 intermission = false;
156 }
157
158 /**
159 * Initializes the stats for single player mode.
160 */
initStats()161 static void initStats()
162 {
163 gameType = DEATHMATCH;
164 slaughterBoy = 0;
165
166 int slaughterFrags = -9999;
167 int posNum = 0;
168 int playerCount = 0;
169 int slaughterCount = 0;
170
171 for(int i = 0; i < MAXPLAYERS; ++i)
172 {
173 totalFrags[i] = 0;
174 if(players[i].plr->inGame)
175 {
176 playerCount++;
177 for(int k = 0; k < MAXPLAYERS; ++k)
178 {
179 if(players[i].plr->inGame)
180 {
181 totalFrags[i] += players[i].frags[k];
182 }
183 }
184 posNum++;
185 }
186
187 if(totalFrags[i] > slaughterFrags)
188 {
189 slaughterBoy = 1 << i;
190 slaughterFrags = totalFrags[i];
191 slaughterCount = 1;
192 }
193 else if(totalFrags[i] == slaughterFrags)
194 {
195 slaughterBoy |= 1 << i;
196 slaughterCount++;
197 }
198 }
199
200 if(playerCount == slaughterCount)
201 {
202 // Don't do the slaughter stuff if everyone is equal.
203 slaughterBoy = 0;
204 }
205 }
206
loadPics()207 static void loadPics()
208 {
209 if(gameType != SINGLE)
210 {
211 dpTallyTop = R_DeclarePatch("TALLYTOP");
212 dpTallyLeft = R_DeclarePatch("TALLYLFT");
213 }
214 }
215
unloadPics()216 static void unloadPics()
217 {
218 // Nothing to do.
219 }
220
IN_Ticker()221 void IN_Ticker()
222 {
223 if(!intermission) return;
224
225 if(interState)
226 {
227 IN_WaitStop();
228 return;
229 }
230
231 skipIntermission = false;
232 CheckForSkip();
233
234 // Counter for general background animation.
235 bcnt++;
236
237 interTime++;
238 if(skipIntermission || (gameType == SINGLE && !hubCount))
239 {
240 interState = 1;
241 NetSv_Intermission(IMF_STATE, interState, 0);
242 cnt = 10;
243 skipIntermission = false;
244 }
245 }
246
CheckForSkip()247 static void CheckForSkip()
248 {
249 static bool triedToSkip;
250
251 for(int i = 0; i < MAXPLAYERS; ++i)
252 {
253 player_t *player = &players[i];
254
255 if(player->plr->inGame)
256 {
257 if(player->brain.attack)
258 {
259 if(!player->attackDown)
260 {
261 if(IS_CLIENT)
262 {
263 NetCl_PlayerActionRequest(player, GPA_FIRE, 0);
264 }
265 else
266 {
267 IN_SkipToNext();
268 }
269 }
270 player->attackDown = true;
271 }
272 else
273 {
274 player->attackDown = false;
275 }
276
277 if(player->brain.use)
278 {
279 if(!player->useDown)
280 {
281 if(IS_CLIENT)
282 {
283 NetCl_PlayerActionRequest(player, GPA_USE, 0);
284 }
285 else
286 {
287 IN_SkipToNext();
288 }
289 }
290 player->useDown = true;
291 }
292 else
293 {
294 player->useDown = false;
295 }
296 }
297 }
298
299 if(gfw_Rule(deathmatch) && interTime < 140)
300 {
301 // Wait for 4 seconds before allowing a skip.
302 if(skipIntermission == 1)
303 {
304 triedToSkip = true;
305 skipIntermission = 0;
306 }
307 }
308 else
309 {
310 if(triedToSkip)
311 {
312 skipIntermission = 1;
313 triedToSkip = false;
314 }
315 }
316 }
317
IN_Drawer()318 void IN_Drawer()
319 {
320 if(!intermission || interState)
321 return;
322
323 dgl_borderedprojectionstate_t bp;
324 GL_ConfigureBorderedProjection(&bp, BPF_OVERDRAW_MASK|BPF_OVERDRAW_CLIP, SCREENWIDTH, SCREENHEIGHT,
325 Get(DD_WINDOW_WIDTH), Get(DD_WINDOW_HEIGHT), scalemode_t(cfg.common.inludeScaleMode));
326 GL_BeginBorderedProjection(&bp);
327
328 lumpnum_t lumpNum = CentralLumpIndex().findLast("INTERPIC.lmp");
329 if(lumpNum >= 0)
330 {
331 DGL_Color4f(1, 1, 1, 1);
332 DGL_SetRawImage(lumpNum, DGL_CLAMP_TO_EDGE, DGL_CLAMP_TO_EDGE);
333 DGL_Enable(DGL_TEXTURE_2D);
334 DGL_DrawRectf2(0, 0, SCREENWIDTH, SCREENHEIGHT);
335 DGL_Disable(DGL_TEXTURE_2D);
336 }
337
338 if(gameType != SINGLE)
339 {
340 drawDeathTally();
341 }
342
343 GL_EndBorderedProjection(&bp);
344 }
345
drawDeathTally()346 static void drawDeathTally()
347 {
348 static dd_bool showTotals;
349
350 DGL_Enable(DGL_TEXTURE_2D);
351
352 DGL_Color4f(1, 1, 1, 1);
353 GL_DrawPatch(dpTallyTop, Vector2i(TALLY_TOP_X, TALLY_TOP_Y));
354 GL_DrawPatch(dpTallyLeft, Vector2i(TALLY_LEFT_X, TALLY_LEFT_Y));
355
356 fixed_t xPos, yPos, xDelta, yDelta, xStart, scale;
357 if(interTime < TALLY_EFFECT_TICKS)
358 {
359 showTotals = false;
360 scale = (interTime * FRACUNIT) / TALLY_EFFECT_TICKS;
361 xDelta = FixedMul(scale, TALLY_FINAL_X_DELTA);
362 yDelta = FixedMul(scale, TALLY_FINAL_Y_DELTA);
363 xStart = TALLY_START_XPOS - FixedMul(scale, TALLY_START_XPOS - TALLY_STOP_XPOS);
364 yPos = TALLY_START_YPOS - FixedMul(scale, TALLY_START_YPOS - TALLY_STOP_YPOS);
365 }
366 else
367 {
368 xDelta = TALLY_FINAL_X_DELTA;
369 yDelta = TALLY_FINAL_Y_DELTA;
370 xStart = TALLY_STOP_XPOS;
371 yPos = TALLY_STOP_YPOS;
372 }
373
374 if(interTime >= TALLY_EFFECT_TICKS && showTotals == false)
375 {
376 showTotals = true;
377 S_StartSound(SFX_PLATFORM_STOP, NULL);
378 }
379 int y = yPos >> FRACBITS;
380
381 FR_SetFont(FID(GF_FONTA));
382 FR_LoadDefaultAttrib();
383
384 for(int i = 0; i < MAXPLAYERS; ++i)
385 {
386 xPos = xStart;
387 for(int j = 0; j < MAXPLAYERS; ++j, xPos += xDelta)
388 {
389 int x = xPos >> FRACBITS;
390 dd_bool bold = (i == CONSOLEPLAYER || j == CONSOLEPLAYER);
391 if(players[i].plr->inGame && players[j].plr->inGame)
392 {
393 if(bold)
394 {
395 drawNumberBold(players[i].frags[j], x, y, 100);
396 }
397 else
398 {
399 drawNumber(players[i].frags[j], x, y, 100);
400 }
401 }
402 else
403 {
404 if(bold)
405 {
406 FR_SetColorAndAlpha(1, 0.7f, 0.3f, 1);
407 FR_DrawTextXY3("--", x, y, ALIGN_TOP, DTF_NO_EFFECTS);
408 }
409 else
410 {
411 FR_SetColorAndAlpha(1, 1, 1, 1);
412 FR_DrawTextXY("--", x, y);
413 }
414 }
415 }
416
417 if(showTotals && players[i].plr->inGame &&
418 !((slaughterBoy & (1 << i)) && !(interTime & 16)))
419 {
420 drawNumber(totalFrags[i], TALLY_TOTALS_X, y, 1000);
421 }
422
423 yPos += yDelta;
424 y = yPos >> FRACBITS;
425 }
426
427 DGL_Disable(DGL_TEXTURE_2D);
428 }
429
drawNumber(int val,int x,int y,int wrapThresh)430 static void drawNumber(int val, int x, int y, int wrapThresh)
431 {
432 char buf[8] = "XX";
433
434 if(!(val < -9 && wrapThresh < 1000))
435 {
436 sprintf(buf, "%d", val >= wrapThresh ? val % wrapThresh : val);
437 }
438
439 FR_SetColorAndAlpha(1, 1, 1, 1);
440 FR_DrawTextXY3(buf, x, y, ALIGN_TOP, DTF_NO_EFFECTS);
441 }
442
drawNumberBold(int val,int x,int y,int wrapThresh)443 static void drawNumberBold(int val, int x, int y, int wrapThresh)
444 {
445 char buf[8] = "XX";
446
447 if(!(val < -9 && wrapThresh < 1000))
448 {
449 sprintf(buf, "%d", val >= wrapThresh ? val % wrapThresh : val);
450 }
451
452 FR_SetColorAndAlpha(1, 0.7f, 0.3f, 1);
453 FR_DrawTextXY3(buf, x, y, ALIGN_TOP, DTF_NO_EFFECTS);
454 }
455
IN_SetState(int stateNum)456 void IN_SetState(int stateNum)
457 {
458 interState = stateNum;
459 }
460
IN_SkipToNext()461 void IN_SkipToNext()
462 {
463 skipIntermission = 1;
464 }
465
IN_ConsoleRegister()466 void IN_ConsoleRegister()
467 {
468 C_VAR_BYTE("inlude-stretch", &cfg.common.inludeScaleMode, 0, SCALEMODE_FIRST, SCALEMODE_LAST);
469 C_VAR_INT ("inlude-patch-replacement", &cfg.common.inludePatchReplaceMode, 0, 0, 1);
470 }
471