A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita
audio
rust
zig
deno
mpris
rockbox
mpd
1/* Emacs style mode select -*- C++ -*-
2 *-----------------------------------------------------------------------------
3 *
4 *
5 * PrBoom a Doom port merged with LxDoom and LSDLDoom
6 * based on BOOM, a modified and improved DOOM engine
7 * Copyright (C) 1999 by
8 * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman
9 * Copyright (C) 1999-2000 by
10 * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
25 * 02111-1307, USA.
26 *
27 * DESCRIPTION:
28 * Intermission screens.
29 *
30 *-----------------------------------------------------------------------------
31 */
32
33#include "doomstat.h"
34#include "m_random.h"
35#include "w_wad.h"
36#include "g_game.h"
37#include "r_main.h"
38#include "v_video.h"
39#include "wi_stuff.h"
40#include "s_sound.h"
41#include "sounds.h"
42#include "m_swap.h"
43#include "r_draw.h"
44
45//
46// Data needed to add patches to full screen intermission pics.
47// Patches are statistics messages, and animations.
48// Loads of by-pixel layout and placement, offsets etc.
49//
50
51//
52// Different vetween registered DOOM (1994) and
53// Ultimate DOOM - Final edition (retail, 1995?).
54// This is supposedly ignored for commercial
55// release (aka DOOM II), which had 34 maps
56// in one episode. So there.
57#define NUMEPISODES 4
58#define NUMMAPS 9
59
60
61// Not used
62// in tics
63//U #define PAUSELEN (TICRATE*2)
64//U #define SCORESTEP 100
65//U #define ANIMPERIOD 32
66// pixel distance from "(YOU)" to "PLAYER N"
67//U #define STARDIST 10
68//U #define WK 1
69
70
71// GLOBAL LOCATIONS
72#define WI_TITLEY 2
73#define WI_SPACINGY 33
74
75// SINGLE-PLAYER STUFF
76#define SP_STATSX 50
77#define SP_STATSY 50
78
79#define SP_TIMEX 8
80// proff/nicolas 09/20/98 -- changed for hi-res
81#define SP_TIMEY 160
82//#define SP_TIMEY (SCREENHEIGHT-32)
83
84
85// NET GAME STUFF
86#define NG_STATSY 50
87#define NG_STATSX (32 + V_NamePatchWidth(star)/2 + 32*!dofrags)
88
89#define NG_SPACINGX 64
90
91
92// Used to display the frags matrix at endgame
93// DEATHMATCH STUFF
94#define DM_MATRIXX 42
95#define DM_MATRIXY 68
96
97#define DM_SPACINGX 40
98
99#define DM_TOTALSX 269
100
101#define DM_KILLERSX 10
102#define DM_KILLERSY 100
103#define DM_VICTIMSX 5
104#define DM_VICTIMSY 50
105
106
107// These animation variables, structures, etc. are used for the
108// DOOM/Ultimate DOOM intermission screen animations. This is
109// totally different from any sprite or texture/flat animations
110enum
111{
112 ANIM_ALWAYS, // determined by patch entry
113 ANIM_RANDOM, // occasional
114 ANIM_LEVEL // continuous
115};
116typedef unsigned animenum_t;
117
118typedef struct
119{
120 int x; // x/y coordinate pair structure
121 int y;
122} point_t;
123
124
125//
126// Animation.
127// There is another anim_t used in p_spec.
128//
129typedef struct
130{
131 animenum_t type;
132
133 // period in tics between animations
134 int period;
135
136 // number of animation frames
137 int nanims;
138
139 // location of animation
140 point_t loc;
141
142 // ALWAYS: n/a,
143 // RANDOM: period deviation (<256),
144 // LEVEL: level
145 int data1;
146
147 // ALWAYS: n/a,
148 // RANDOM: random base period,
149 // LEVEL: n/a
150 int data2;
151
152 /* actual graphics for frames of animations
153 * cphipps - const
154 */
155 const patch_t* p[3];
156
157 // following must be initialized to zero before use!
158
159 // next value of bcnt (used in conjunction with period)
160 int nexttic;
161
162 // last drawn animation frame
163 int lastdrawn;
164
165 // next frame number to animate
166 int ctr;
167
168 // used by RANDOM and LEVEL when animating
169 int state;
170} anim_t;
171
172
173static point_t lnodes[NUMEPISODES][NUMMAPS] =
174 {
175 // Episode 0 World Map
176 {
177 { 185, 164 }, // location of level 0 (CJ)
178 { 148, 143 }, // location of level 1 (CJ)
179 { 69, 122 }, // location of level 2 (CJ)
180 { 209, 102 }, // location of level 3 (CJ)
181 { 116, 89 }, // location of level 4 (CJ)
182 { 166, 55 }, // location of level 5 (CJ)
183 { 71, 56 }, // location of level 6 (CJ)
184 { 135, 29 }, // location of level 7 (CJ)
185 { 71, 24 } // location of level 8 (CJ)
186 },
187
188 // Episode 1 World Map should go here
189 {
190 { 254, 25 }, // location of level 0 (CJ)
191 { 97, 50 }, // location of level 1 (CJ)
192 { 188, 64 }, // location of level 2 (CJ)
193 { 128, 78 }, // location of level 3 (CJ)
194 { 214, 92 }, // location of level 4 (CJ)
195 { 133, 130 }, // location of level 5 (CJ)
196 { 208, 136 }, // location of level 6 (CJ)
197 { 148, 140 }, // location of level 7 (CJ)
198 { 235, 158 } // location of level 8 (CJ)
199 },
200
201 // Episode 2 World Map should go here
202 {
203 { 156, 168 }, // location of level 0 (CJ)
204 { 48, 154 }, // location of level 1 (CJ)
205 { 174, 95 }, // location of level 2 (CJ)
206 { 265, 75 }, // location of level 3 (CJ)
207 { 130, 48 }, // location of level 4 (CJ)
208 { 279, 23 }, // location of level 5 (CJ)
209 { 198, 48 }, // location of level 6 (CJ)
210 { 140, 25 }, // location of level 7 (CJ)
211 { 281, 136 } // location of level 8 (CJ)
212 }
213
214 };
215
216
217//
218// Animation locations for episode 0 (1).
219// Using patches saves a lot of space,
220// as they replace 320x200 full screen frames.
221//
222static anim_t epsd0animinfo[] =
223 {
224 { ANIM_ALWAYS, TICRATE/3, 3, { 224, 104 }, 0, 0, { 0, 0, 0 }, 0, 0, 0, 0 },
225 { ANIM_ALWAYS, TICRATE/3, 3, { 184, 160 }, 0, 0, { 0, 0, 0 }, 0, 0, 0, 0 },
226 { ANIM_ALWAYS, TICRATE/3, 3, { 112, 136 }, 0, 0, { 0, 0, 0 }, 0, 0, 0, 0 },
227 { ANIM_ALWAYS, TICRATE/3, 3, { 72, 112 }, 0, 0, { 0, 0, 0 }, 0, 0, 0, 0 },
228 { ANIM_ALWAYS, TICRATE/3, 3, { 88, 96 }, 0, 0, { 0, 0, 0 }, 0, 0, 0, 0 },
229 { ANIM_ALWAYS, TICRATE/3, 3, { 64, 48 }, 0, 0, { 0, 0, 0 }, 0, 0, 0, 0 },
230 { ANIM_ALWAYS, TICRATE/3, 3, { 192, 40 }, 0, 0, { 0, 0, 0 }, 0, 0, 0, 0 },
231 { ANIM_ALWAYS, TICRATE/3, 3, { 136, 16 }, 0, 0, { 0, 0, 0 }, 0, 0, 0, 0 },
232 { ANIM_ALWAYS, TICRATE/3, 3, { 80, 16 }, 0, 0, { 0, 0, 0 }, 0, 0, 0, 0 },
233 { ANIM_ALWAYS, TICRATE/3, 3, { 64, 24 }, 0, 0, { 0, 0, 0 }, 0, 0, 0, 0 }
234 };
235
236static anim_t epsd1animinfo[] =
237 {
238 { ANIM_LEVEL, TICRATE/3, 1, { 128, 136 }, 1, 0, { 0, 0, 0 }, 0, 0, 0, 0 },
239 { ANIM_LEVEL, TICRATE/3, 1, { 128, 136 }, 2, 0, { 0, 0, 0 }, 0, 0, 0, 0 },
240 { ANIM_LEVEL, TICRATE/3, 1, { 128, 136 }, 3, 0, { 0, 0, 0 }, 0, 0, 0, 0 },
241 { ANIM_LEVEL, TICRATE/3, 1, { 128, 136 }, 4, 0, { 0, 0, 0 }, 0, 0, 0, 0 },
242 { ANIM_LEVEL, TICRATE/3, 1, { 128, 136 }, 5, 0, { 0, 0, 0 }, 0, 0, 0, 0 },
243 { ANIM_LEVEL, TICRATE/3, 1, { 128, 136 }, 6, 0, { 0, 0, 0 }, 0, 0, 0, 0 },
244 { ANIM_LEVEL, TICRATE/3, 1, { 128, 136 }, 7, 0, { 0, 0, 0 }, 0, 0, 0, 0 },
245 { ANIM_LEVEL, TICRATE/3, 3, { 192, 144 }, 8, 0, { 0, 0, 0 }, 0, 0, 0, 0 },
246 { ANIM_LEVEL, TICRATE/3, 1, { 128, 136 }, 8, 0, { 0, 0, 0 }, 0, 0, 0, 0 }
247 };
248
249static anim_t epsd2animinfo[] =
250 {
251 { ANIM_ALWAYS, TICRATE/3, 3, { 104, 168 }, 0, 0, { 0, 0, 0 }, 0, 0, 0, 0 },
252 { ANIM_ALWAYS, TICRATE/3, 3, { 40, 136 }, 0, 0, { 0, 0, 0 }, 0, 0, 0, 0 },
253 { ANIM_ALWAYS, TICRATE/3, 3, { 160, 96 }, 0, 0, { 0, 0, 0 }, 0, 0, 0, 0 },
254 { ANIM_ALWAYS, TICRATE/3, 3, { 104, 80 }, 0, 0, { 0, 0, 0 }, 0, 0, 0, 0 },
255 { ANIM_ALWAYS, TICRATE/3, 3, { 120, 32 }, 0, 0, { 0, 0, 0 }, 0, 0, 0, 0 },
256 { ANIM_ALWAYS, TICRATE/4, 3, { 40, 0 }, 0, 0, { 0, 0, 0 }, 0, 0, 0, 0 }
257 };
258
259static int NUMANIMS[NUMEPISODES] =
260 {
261 sizeof(epsd0animinfo)/sizeof(anim_t),
262 sizeof(epsd1animinfo)/sizeof(anim_t),
263 sizeof(epsd2animinfo)/sizeof(anim_t)
264 };
265
266static anim_t *anims[NUMEPISODES] =
267 {
268 epsd0animinfo,
269 epsd1animinfo,
270 epsd2animinfo
271 };
272
273
274//
275// GENERAL DATA
276//
277
278//
279// Locally used stuff.
280//
281#define FB 0
282
283
284// States for single-player
285#define SP_KILLS 0
286#define SP_ITEMS 2
287#define SP_SECRET 4
288#define SP_FRAGS 6
289#define SP_TIME 8
290#define SP_PAR ST_TIME
291
292#define SP_PAUSE 1
293
294// in seconds
295#define SHOWNEXTLOCDELAY 4
296//#define SHOWLASTLOCDELAY SHOWNEXTLOCDELAY
297
298
299// used to accelerate or skip a stage
300int acceleratestage; // killough 3/28/98: made global
301
302// wbs->pnum
303static int me;
304
305// specifies current state
306static stateenum_t state;
307
308// contains information passed into intermission
309static wbstartstruct_t* wbs;
310
311static wbplayerstruct_t* plrs; // wbs->plyr[]
312
313// used for general timing
314static int cnt;
315
316// used for timing of background animation
317static int bcnt;
318
319// signals to refresh everything for one frame
320static int firstrefresh;
321
322static int cnt_time;
323static int cnt_total_time;
324static int cnt_par;
325static int cnt_pause;
326
327
328//
329// GRAPHICS
330//
331
332// You Are Here graphic
333static const char* yah[2] = { "WIURH0", "WIURH1" };
334
335// splat
336static const char* splat = "WISPLAT";
337
338// %, : graphics
339static const char percent[] = {"WIPCNT"};
340static const char colon[] = {"WICOLON"};
341
342// 0-9 graphic
343static const patch_t * num[10];
344
345// minus sign
346static const char wiminus[] = {"WIMINUS"};
347
348// "Finished!" graphics
349static const char finished[] = {"WIF"};
350
351// "Entering" graphic
352static const char entering[] = {"WIENTER"};
353
354// "secret"
355static const char sp_secret[] = {"WISCRT2"};
356
357// "Kills", "Scrt", "Items", "Frags"
358static const char kills[] = {"WIOSTK"};
359static const char secret[] = {"WIOSTS"};
360static const char items[] = {"WIOSTI"};
361static const char frags[] = {"WIFRGS"};
362
363// Time sucks.
364static const char time1[] = {"WITIME"};
365static const char par[] = {"WIPAR"};
366static const char sucks[] = {"WISUCKS"};
367
368// "killers", "victims"
369static const char killers[] = {"WIKILRS"};
370static const char victims[] = {"WIVCTMS"};
371
372// "Total", your face, your dead face
373static const char total[] = {"WIMSTT"};
374static const char star[] = {"STFST01"};
375static const char bstar[] = {"STFDEAD0"};
376
377// "red P[1..MAXPLAYERS]"
378static const char facebackp[] = {"STPB0"};
379
380
381//
382// CODE
383//
384
385static void WI_endDeathmatchStats(void);
386static void WI_endNetgameStats(void);
387void WI_unloadData(void);
388#define WI_endStats WI_endNetgameStats
389
390/* ====================================================================
391 * WI_levelNameLump
392 * Purpore: Returns the name of the graphic lump containing the name of
393 * the given level.
394 * Args: Episode and level, and buffer (must by 9 chars) to write to
395 * Returns: void
396 */
397void WI_levelNameLump(int epis, int map, char* buf, int bsize)
398{
399 if (gamemode == commercial) {
400 snprintf(buf,bsize, "CWILV%2.2d", map);
401 } else {
402 snprintf(buf,bsize, "WILV%d%d", epis, map);
403 }
404}
405
406// ====================================================================
407// WI_slamBackground
408// Purpose: Put the full-screen background up prior to patches
409// Args: none
410// Returns: void
411//
412static void WI_slamBackground(void)
413{
414 char name[9]; // limited to 8 characters
415
416 if (gamemode == commercial || (gamemode == retail && wbs->epsd == 3))
417 strcpy(name, "INTERPIC");
418 else
419 snprintf(name, sizeof(name), "WIMAP%d", wbs->epsd);
420
421 // background
422 V_DrawNamePatch(0, 0, FB, name, CR_DEFAULT, VPT_STRETCH);
423}
424
425// ====================================================================
426// WI_Responder
427// Purpose: Draw animations on intermission background screen
428// Args: ev -- event pointer, not actually used here.
429// Returns: False -- dummy routine
430//
431// The ticker is used to detect keys
432// because of timing issues in netgames.
433boolean WI_Responder(event_t* ev)
434{
435 (void)ev;
436 return false;
437}
438
439// ====================================================================
440// WI_drawLF
441// Purpose: Draw the "Finished" level name before showing stats
442// Args: none
443// Returns: void
444//
445void WI_drawLF(void)
446{
447 int y = WI_TITLEY;
448 char lname[9];
449
450 // draw <LevelName>
451 /* cph - get the graphic lump name and use it */
452 WI_levelNameLump(wbs->epsd, wbs->last, lname, sizeof(lname));
453 // CPhipps - patch drawing updated
454 V_DrawNamePatch((320 - V_NamePatchWidth(lname))/2, y,
455 FB, lname, CR_DEFAULT, VPT_STRETCH);
456
457 // draw "Finished!"
458 y += (5*V_NamePatchHeight(lname))/4;
459
460 // CPhipps - patch drawing updated
461 V_DrawNamePatch((320 - V_NamePatchWidth(finished))/2, y,
462 FB, finished, CR_DEFAULT, VPT_STRETCH);
463}
464
465// ====================================================================
466// WI_drawEL
467// Purpose: Draw introductory "Entering" and level name
468// Args: none
469// Returns: void
470//
471void WI_drawEL(void)
472{
473 int y = WI_TITLEY;
474 char lname[9];
475
476 /* cph - get the graphic lump name */
477 WI_levelNameLump(wbs->epsd, wbs->next, lname, sizeof(lname));
478
479 // draw "Entering"
480 // CPhipps - patch drawing updated
481 V_DrawNamePatch((320 - V_NamePatchWidth(entering))/2,
482 y, FB, entering, CR_DEFAULT, VPT_STRETCH);
483
484 // draw level
485 y += (5*V_NamePatchHeight(lname))/4;
486
487 // CPhipps - patch drawing updated
488 V_DrawNamePatch((320 - V_NamePatchWidth(lname))/2, y, FB,
489 lname, CR_DEFAULT, VPT_STRETCH);
490}
491
492
493/* ====================================================================
494 * WI_drawOnLnode
495 * Purpose: Draw patches at a location based on episode/map
496 * Args: n -- index to map# within episode
497 * c[] -- array of names of patches to be drawn
498 * Returns: void
499 */
500void
501WI_drawOnLnode // draw stuff at a location by episode/map#
502( int n,
503 const char* const c[] )
504{
505 int i;
506 boolean fits = false;
507
508 i = 0;
509 do
510 {
511 int left;
512 int top;
513 int right;
514 int bottom;
515 int lump = W_GetNumForName(c[i]);
516 const patch_t* p = W_CacheLumpNum(lump);
517
518 left = lnodes[wbs->epsd][n].x - SHORT(p->leftoffset);
519 top = lnodes[wbs->epsd][n].y - SHORT(p->topoffset);
520 right = left + SHORT(p->width);
521 bottom = top + SHORT(p->height);
522 W_UnlockLumpNum(lump);
523
524 if (left >= 0
525 && right < 320
526 && top >= 0
527 && bottom < 200)
528 {
529 fits = true;
530 }
531 else
532 {
533 i++;
534 }
535 } while (!fits && i!=2);
536
537 if (fits && i<2)
538 {
539 // CPhipps - patch drawing updated
540 V_DrawNamePatch(lnodes[wbs->epsd][n].x, lnodes[wbs->epsd][n].y,
541 FB, c[i], CR_DEFAULT, VPT_STRETCH);
542 }
543 else
544 {
545 // DEBUG
546 //jff 8/3/98 use logical output routine
547 printf("Could not place patch on level %d", n+1);
548 }
549}
550
551
552// ====================================================================
553// WI_initAnimatedBack
554// Purpose: Initialize pointers and styles for background animation
555// Args: none
556// Returns: void
557//
558void WI_initAnimatedBack(void)
559{
560 int i;
561 anim_t* a;
562
563 if (gamemode == commercial) // no animation for DOOM2
564 return;
565
566 if (wbs->epsd > 2)
567 return;
568
569 for (i=0;i<NUMANIMS[wbs->epsd];i++)
570 {
571 a = &anims[wbs->epsd][i];
572
573 // init variables
574 a->ctr = -1;
575
576 // specify the next time to draw it
577 if (a->type == ANIM_ALWAYS)
578 a->nexttic = bcnt + 1 + (M_Random()%a->period);
579 else
580 if (a->type == ANIM_RANDOM)
581 a->nexttic = bcnt + 1 + a->data2+(M_Random()%a->data1);
582 else
583 if (a->type == ANIM_LEVEL)
584 a->nexttic = bcnt + 1;
585 }
586}
587
588
589// ====================================================================
590// WI_updateAnimatedBack
591// Purpose: Figure out what animation we do on this iteration
592// Args: none
593// Returns: void
594//
595void WI_updateAnimatedBack(void)
596{
597 int i;
598 anim_t* a;
599
600 if (gamemode == commercial)
601 return;
602
603 if (wbs->epsd > 2)
604 return;
605
606 for (i=0;i<NUMANIMS[wbs->epsd];i++)
607 {
608 a = &anims[wbs->epsd][i];
609
610 if (bcnt == a->nexttic)
611 {
612 switch (a->type)
613 {
614 case ANIM_ALWAYS:
615 if (++a->ctr >= a->nanims) a->ctr = 0;
616 a->nexttic = bcnt + a->period;
617 break;
618
619 case ANIM_RANDOM:
620 a->ctr++;
621 if (a->ctr == a->nanims)
622 {
623 a->ctr = -1;
624 a->nexttic = bcnt+a->data2+(M_Random()%a->data1);
625 }
626 else
627 a->nexttic = bcnt + a->period;
628 break;
629
630 case ANIM_LEVEL:
631 // gawd-awful hack for level anims
632 if (!(state == StatCount && i == 7)
633 && wbs->next == a->data1)
634 {
635 a->ctr++;
636 if (a->ctr == a->nanims) a->ctr--;
637 a->nexttic = bcnt + a->period;
638 }
639 break;
640 }
641 }
642 }
643}
644
645
646// ====================================================================
647// WI_drawAnimatedBack
648// Purpose: Actually do the animation (whew!)
649// Args: none
650// Returns: void
651//
652void WI_drawAnimatedBack(void)
653{
654 int i;
655 anim_t* a;
656
657 if (gamemode==commercial) //jff 4/25/98 Someone forgot commercial an enum
658 return;
659
660 if (wbs->epsd > 2)
661 return;
662
663 for (i=0 ; i<NUMANIMS[wbs->epsd] ; i++)
664 {
665 a = &anims[wbs->epsd][i];
666
667 if (a->ctr >= 0)
668 // CPhipps - patch drawing updated
669 V_DrawMemPatch(a->loc.x, a->loc.y, FB, a->p[a->ctr], CR_DEFAULT, VPT_STRETCH);
670 }
671}
672
673
674// ====================================================================
675// WI_drawNum
676// Purpose: Draws a number. If digits > 0, then use that many digits
677// minimum, otherwise only use as many as necessary
678// Args: x, y -- location
679// n -- the number to be drawn
680// digits -- number of digits minimum or zero
681// Returns: new x position after drawing (note we are going to the left)
682// CPhipps - static
683static int WI_drawNum (int x, int y, int n, int digits)
684{
685 int fontwidth = SHORT(num[0]->width);
686 int neg;
687 int temp;
688
689 if (digits < 0)
690 {
691 if (!n)
692 {
693 // make variable-length zeros 1 digit long
694 digits = 1;
695 }
696 else
697 {
698 // figure out # of digits in #
699 digits = 0;
700 temp = n;
701
702 while (temp)
703 {
704 temp /= 10;
705 digits++;
706 }
707 }
708 }
709
710 neg = n < 0;
711 if (neg)
712 n = -n;
713
714 // if non-number, do not draw it
715 if (n == 1994)
716 return 0;
717
718 // draw the new number
719 while (digits--)
720 {
721 x -= fontwidth;
722 // CPhipps - patch drawing updated
723 V_DrawMemPatch(x, y, FB, num[ n % 10 ], CR_DEFAULT, VPT_STRETCH);
724 n /= 10;
725 }
726
727 // draw a minus sign if necessary
728 if (neg)
729 // CPhipps - patch drawing updated
730 V_DrawNamePatch(x-=8, y, FB, wiminus, CR_DEFAULT, VPT_STRETCH);
731
732 return x;
733}
734
735
736// ====================================================================
737// WI_drawPercent
738// Purpose: Draws a percentage, really just a call to WI_drawNum
739// after putting a percent sign out there
740// Args: x, y -- location
741// p -- the percentage value to be drawn, no negatives
742// Returns: void
743// CPhipps - static
744static void WI_drawPercent(int x, int y, int p)
745{
746 if (p < 0)
747 return;
748
749 // CPhipps - patch drawing updated
750 V_DrawNamePatch(x, y, FB, percent, CR_DEFAULT, VPT_STRETCH);
751 WI_drawNum(x, y, p, -1);
752}
753
754
755// ====================================================================
756// WI_drawTime
757// Purpose: Draws the level completion time or par time, or "Sucks"
758// if 1 hour or more
759// Args: x, y -- location
760// t -- the time value to be drawn
761// Returns: void
762//
763// CPhipps - static
764// - largely rewritten to display hours and use slightly better algorithm
765
766static void WI_drawTime(int x, int y, int t)
767{
768 int n;
769
770 if (t<0)
771 return;
772
773 if (t < 100*60*60)
774 for(;;) {
775 n = t % 60;
776 t /= 60;
777 x = WI_drawNum(x, y, n, (t || n>9) ? 2 : 1) - V_NamePatchWidth(colon);
778
779 // draw
780 if (t)
781 // CPhipps - patch drawing updated
782 V_DrawNamePatch(x, y, FB, colon, CR_DEFAULT, VPT_STRETCH);
783 else break;
784 }
785 else // "sucks" (maybe should be "addicted", even I've never had a 100 hour game ;)
786 V_DrawNamePatch(x - V_NamePatchWidth(sucks),
787 y, FB, sucks, CR_DEFAULT, VPT_STRETCH);
788}
789
790
791// ====================================================================
792// WI_End
793// Purpose: Unloads data structures (inverse of WI_Start)
794// Args: none
795// Returns: void
796//
797void WI_End(void)
798{
799 WI_unloadData();
800
801 if (deathmatch)
802 WI_endDeathmatchStats();
803 else if (netgame)
804 WI_endNetgameStats();
805 else
806 WI_endStats();
807}
808
809
810// ====================================================================
811// WI_initNoState
812// Purpose: Clear state, ready for end of level activity
813// Args: none
814// Returns: void
815//
816void WI_initNoState(void)
817{
818 state = NoState;
819 acceleratestage = 0;
820 cnt = 10;
821}
822
823
824// ====================================================================
825// WI_drawTimeStats
826// Purpose: Put the times on the screen
827// Args: time, total time, par time, in seconds
828// Returns: void
829//
830// cph - pulled from WI_drawStats below
831
832static void WI_drawTimeStats(int cnt_time, int cnt_total_time, int cnt_par)
833{
834 V_DrawNamePatch(SP_TIMEX, SP_TIMEY, FB, time1, CR_DEFAULT, VPT_STRETCH);
835 WI_drawTime(320/2 - SP_TIMEX, SP_TIMEY, cnt_time);
836
837 V_DrawNamePatch(SP_TIMEX, (SP_TIMEY+200)/2, FB, total, CR_DEFAULT, VPT_STRETCH);
838 WI_drawTime(320/2 - SP_TIMEX, (SP_TIMEY+200)/2, cnt_total_time);
839
840 // Ty 04/11/98: redid logic: should skip only if with pwad but
841 // without deh patch
842 // killough 2/22/98: skip drawing par times on pwads
843 // Ty 03/17/98: unless pars changed with deh patch
844
845 if (!(modifiedgame)) //&& !deh_pars))
846 {
847 if (wbs->epsd < 3)
848 {
849 V_DrawNamePatch(320/2 + SP_TIMEX, SP_TIMEY, FB, par, CR_DEFAULT, VPT_STRETCH);
850 WI_drawTime(320 - SP_TIMEX, SP_TIMEY, cnt_par);
851 }
852 }
853}
854
855// ====================================================================
856// WI_updateNoState
857// Purpose: Cycle until end of level activity is done
858// Args: none
859// Returns: void
860//
861void WI_updateNoState(void)
862{
863
864 WI_updateAnimatedBack();
865
866 if (!--cnt)
867 G_WorldDone();
868}
869
870static boolean snl_pointeron = false;
871
872
873
874// ====================================================================
875// WI_initShowNextLoc
876// Purpose: Prepare to show the next level's location
877// Args: none
878// Returns: void
879//
880void WI_initShowNextLoc(void)
881{
882 if ((gamemode != commercial) && (gamemap == 8)) {
883 G_WorldDone();
884 return;
885 }
886
887 state = ShowNextLoc;
888 acceleratestage = 0;
889 cnt = SHOWNEXTLOCDELAY * TICRATE;
890
891 WI_initAnimatedBack();
892}
893
894// ====================================================================
895// WI_updateShowNextLoc
896// Purpose: Prepare to show the next level's location
897// Args: none
898// Returns: void
899//
900void WI_updateShowNextLoc(void)
901{
902 WI_updateAnimatedBack();
903
904 if (!--cnt || acceleratestage)
905 WI_initNoState();
906 else
907 snl_pointeron = (cnt & 31) < 20;
908}
909
910
911// ====================================================================
912// WI_drawShowNextLoc
913// Purpose: Show the next level's location on animated backgrounds
914// Args: none
915// Returns: void
916//
917void WI_drawShowNextLoc(void)
918{
919 int i;
920 int last;
921
922 WI_slamBackground();
923
924 // draw animated background
925 WI_drawAnimatedBack();
926
927 if ( gamemode != commercial)
928 {
929 if (wbs->epsd > 2)
930 {
931 WI_drawEL(); // "Entering..." if not E1 or E2
932 return;
933 }
934
935 last = (wbs->last == 8) ? wbs->next - 1 : wbs->last;
936
937 // draw a splat on taken cities.
938 for (i=0 ; i<=last ; i++)
939 WI_drawOnLnode(i, &splat);
940
941 // splat the secret level?
942 if (wbs->didsecret)
943 WI_drawOnLnode(8, &splat);
944
945 // draw flashing ptr
946 if (snl_pointeron)
947 WI_drawOnLnode(wbs->next, yah);
948 }
949
950 // draws which level you are entering..
951 if ( (gamemode != commercial)
952 || wbs->next != 30) // check for MAP30 end game
953 WI_drawEL();
954}
955
956// ====================================================================
957// WI_drawNoState
958// Purpose: Draw the pointer and next location
959// Args: none
960// Returns: void
961//
962void WI_drawNoState(void)
963{
964 snl_pointeron = true;
965 WI_drawShowNextLoc();
966}
967
968
969// ====================================================================
970// WI_fragSum
971// Purpose: Calculate frags for this player based on the current totals
972// of all the other players. Subtract self-frags.
973// Args: playernum -- the player to be calculated
974// Returns: the total frags for this player
975//
976int WI_fragSum(int playernum)
977{
978 int i;
979 int frags = 0;
980
981 for (i=0 ; i<MAXPLAYERS ; i++)
982 {
983 if (playeringame[i] // is this player playing?
984 && i!=playernum) // and it's not the player we're calculating
985 {
986 frags += plrs[playernum].frags[i];
987 }
988 }
989
990
991 // JDC hack - negative frags.
992 frags -= plrs[playernum].frags[playernum];
993
994 return frags;
995}
996
997
998static int dm_state;
999// CPhipps - short, dynamically allocated
1000static short int **dm_frags; // frags matrix
1001static short int *dm_totals; // totals by player
1002
1003// ====================================================================
1004// WI_initDeathmatchStats
1005// Purpose: Set up to display DM stats at end of level. Calculate
1006// frags for all players.
1007// Args: none
1008// Returns: void
1009//
1010void WI_initDeathmatchStats(void)
1011{
1012 int i; // looping variables
1013
1014 // CPhipps - allocate data structures needed
1015 dm_frags = calloc(MAXPLAYERS, sizeof(*dm_frags));
1016 dm_totals = calloc(MAXPLAYERS, sizeof(*dm_totals));
1017
1018 state = StatCount; // We're doing stats
1019 acceleratestage = 0;
1020 dm_state = 1; // count how many times we've done a complete stat
1021
1022 cnt_pause = TICRATE;
1023
1024 for (i=0 ; i<MAXPLAYERS ; i++)
1025 {
1026 if (playeringame[i])
1027 {
1028 // CPhipps - allocate frags line
1029 dm_frags[i] = calloc(MAXPLAYERS, sizeof(**dm_frags)); // set all counts to zero
1030
1031 dm_totals[i] = 0;
1032 }
1033 }
1034 WI_initAnimatedBack();
1035}
1036
1037
1038// ====================================================================
1039// CPhipps - WI_endDeathmatchStats
1040// Purpose: Deallocate dynamically allocated DM stats data
1041// Args: none
1042// Returns: void
1043//
1044
1045void WI_endDeathmatchStats(void)
1046{
1047 int i;
1048 for (i=0; i<MAXPLAYERS; i++)
1049 free(dm_frags[i]);
1050
1051 free(dm_frags); free(dm_totals);
1052}
1053
1054// ====================================================================
1055// WI_updateDeathmatchStats
1056// Purpose: Advance Deathmatch stats screen animation. Calculate
1057// frags for all players. Lots of noise and drama around
1058// the presentation.
1059// Args: none
1060// Returns: void
1061//
1062void WI_updateDeathmatchStats(void)
1063{
1064 int i;
1065 int j;
1066
1067 boolean stillticking;
1068
1069 WI_updateAnimatedBack();
1070
1071 if (acceleratestage && dm_state != 4) // still ticking
1072 {
1073 acceleratestage = 0;
1074
1075 for (i=0 ; i<MAXPLAYERS ; i++)
1076 {
1077 if (playeringame[i])
1078 {
1079 for (j=0 ; j<MAXPLAYERS ; j++)
1080 if (playeringame[j])
1081 dm_frags[i][j] = plrs[i].frags[j];
1082
1083 dm_totals[i] = WI_fragSum(i);
1084 }
1085 }
1086
1087
1088 S_StartSound(0, sfx_barexp); // bang
1089 dm_state = 4; // we're done with all 4 (or all we have to do)
1090 }
1091
1092
1093 if (dm_state == 2)
1094 {
1095 if (!(bcnt&3))
1096 S_StartSound(0, sfx_pistol); // noise while counting
1097
1098 stillticking = false;
1099
1100 for (i=0 ; i<MAXPLAYERS ; i++)
1101 {
1102 if (playeringame[i])
1103 {
1104 for (j=0 ; j<MAXPLAYERS ; j++)
1105 {
1106 if (playeringame[j]
1107 && dm_frags[i][j] != plrs[i].frags[j])
1108 {
1109 if (plrs[i].frags[j] < 0)
1110 dm_frags[i][j]--;
1111 else
1112 dm_frags[i][j]++;
1113
1114 if (dm_frags[i][j] > 999) // Ty 03/17/98 3-digit frag count
1115 dm_frags[i][j] = 999;
1116
1117 if (dm_frags[i][j] < -999)
1118 dm_frags[i][j] = -999;
1119
1120 stillticking = true;
1121 }
1122 }
1123 dm_totals[i] = WI_fragSum(i);
1124
1125 if (dm_totals[i] > 999)
1126 dm_totals[i] = 999;
1127
1128 if (dm_totals[i] < -999)
1129 dm_totals[i] = -999; // Ty 03/17/98 end 3-digit frag count
1130 }
1131 }
1132
1133 if (!stillticking)
1134 {
1135 S_StartSound(0, sfx_barexp);
1136 dm_state++;
1137 }
1138 }
1139 else if (dm_state == 4)
1140 {
1141 if (acceleratestage)
1142 {
1143 S_StartSound(0, sfx_slop);
1144
1145 if ( gamemode == commercial)
1146 WI_initNoState();
1147 else
1148 WI_initShowNextLoc();
1149 }
1150 }
1151 else if (dm_state & 1)
1152 {
1153 if (!--cnt_pause)
1154 {
1155 dm_state++;
1156 cnt_pause = TICRATE;
1157 }
1158 }
1159}
1160
1161
1162// ====================================================================
1163// WI_drawDeathmatchStats
1164// Purpose: Draw the stats on the screen in a matrix
1165// Args: none
1166// Returns: void
1167//
1168// proff/nicolas 09/20/98 -- changed for hi-res
1169// CPhipps - patch drawing updated
1170void WI_drawDeathmatchStats(void)
1171{
1172 int i;
1173 int j;
1174 int x;
1175 int y;
1176 int w;
1177
1178 int halfface = V_NamePatchWidth(facebackp)/2;
1179
1180 WI_slamBackground();
1181
1182 // draw animated background
1183 WI_drawAnimatedBack();
1184 WI_drawLF();
1185
1186 // draw stat titles (top line)
1187 V_DrawNamePatch(DM_TOTALSX-V_NamePatchWidth(total)/2,
1188 DM_MATRIXY-WI_SPACINGY+10, FB, total, CR_DEFAULT, VPT_STRETCH);
1189
1190 V_DrawNamePatch(DM_KILLERSX, DM_KILLERSY, FB, killers, CR_DEFAULT, VPT_STRETCH);
1191 V_DrawNamePatch(DM_VICTIMSX, DM_VICTIMSY, FB, victims, CR_DEFAULT, VPT_STRETCH);
1192
1193 // draw P?
1194 x = DM_MATRIXX + DM_SPACINGX;
1195 y = DM_MATRIXY;
1196
1197 for (i=0 ; i<MAXPLAYERS ; i++)
1198 {
1199 if (playeringame[i]) {
1200 //int trans = playernumtotrans[i];
1201 V_DrawNamePatch(x-halfface, DM_MATRIXY - WI_SPACINGY,
1202 FB, facebackp, i ? CR_LIMIT+i : CR_DEFAULT,
1203 VPT_STRETCH | (i ? VPT_TRANS : 0));
1204 V_DrawNamePatch(DM_MATRIXX-halfface, y,
1205 FB, facebackp, i ? CR_LIMIT+i : CR_DEFAULT,
1206 VPT_STRETCH | (i ? VPT_TRANS : 0));
1207
1208 if (i == me)
1209 {
1210 V_DrawNamePatch(x-halfface, DM_MATRIXY - WI_SPACINGY,
1211 FB, bstar, CR_DEFAULT, VPT_STRETCH);
1212 V_DrawNamePatch(DM_MATRIXX-halfface, y,
1213 FB, star, CR_DEFAULT, VPT_STRETCH);
1214 }
1215 }
1216 x += DM_SPACINGX;
1217 y += WI_SPACINGY;
1218 }
1219
1220 // draw stats
1221 y = DM_MATRIXY+10;
1222 w = SHORT(num[0]->width);
1223
1224 for (i=0 ; i<MAXPLAYERS ; i++)
1225 {
1226 x = DM_MATRIXX + DM_SPACINGX;
1227
1228 if (playeringame[i])
1229 {
1230 for (j=0 ; j<MAXPLAYERS ; j++)
1231 {
1232 if (playeringame[j])
1233 WI_drawNum(x+w, y, dm_frags[i][j], 2);
1234
1235 x += DM_SPACINGX;
1236 }
1237 WI_drawNum(DM_TOTALSX+w, y, dm_totals[i], 2);
1238 }
1239 y += WI_SPACINGY;
1240 }
1241}
1242
1243
1244//
1245// Note: The term "Netgame" means a coop game
1246//
1247static short *cnt_kills;
1248static short *cnt_items;
1249static short *cnt_secret;
1250static short *cnt_frags;
1251static int dofrags;
1252static int ng_state;
1253
1254// ====================================================================
1255// CPhipps - WI_endNetgameStats
1256// Purpose: Clean up coop game stats
1257// Args: none
1258// Returns: void
1259//
1260static void WI_endNetgameStats(void)
1261{
1262 free(cnt_frags);
1263 free(cnt_secret);
1264 free(cnt_items);
1265 free(cnt_kills);
1266}
1267
1268// ====================================================================
1269// WI_initNetgameStats
1270// Purpose: Prepare for coop game stats
1271// Args: none
1272// Returns: void
1273//
1274void WI_initNetgameStats(void)
1275{
1276 int i;
1277
1278 state = StatCount;
1279 acceleratestage = 0;
1280 ng_state = 1;
1281
1282 cnt_pause = TICRATE;
1283
1284 // CPhipps - allocate these dynamically, blank with calloc
1285 cnt_kills = calloc(MAXPLAYERS, sizeof(*cnt_kills));
1286 cnt_items = calloc(MAXPLAYERS, sizeof(*cnt_items));
1287 cnt_secret= calloc(MAXPLAYERS, sizeof(*cnt_secret));
1288 cnt_frags = calloc(MAXPLAYERS, sizeof(*cnt_frags));
1289
1290 for (i=0 ; i<MAXPLAYERS ; i++)
1291 if (playeringame[i])
1292 dofrags += WI_fragSum(i);
1293
1294 dofrags = !!dofrags; // set to true or false - did we have frags?
1295
1296 WI_initAnimatedBack();
1297}
1298
1299
1300// ====================================================================
1301// WI_updateNetgameStats
1302// Purpose: Calculate coop stats as we display them with noise and fury
1303// Args: none
1304// Returns: void
1305// Comment: This stuff sure is complicated for what it does
1306//
1307void WI_updateNetgameStats(void)
1308{
1309 int i;
1310 int fsum;
1311
1312 boolean stillticking;
1313
1314 WI_updateAnimatedBack();
1315
1316 if (acceleratestage && ng_state != 10)
1317 {
1318 acceleratestage = 0;
1319
1320 for (i=0 ; i<MAXPLAYERS ; i++)
1321 {
1322 if (!playeringame[i])
1323 continue;
1324
1325 cnt_kills[i] = (plrs[i].skills * 100) / wbs->maxkills;
1326 cnt_items[i] = (plrs[i].sitems * 100) / wbs->maxitems;
1327
1328 // killough 2/22/98: Make secrets = 100% if maxsecret = 0:
1329 cnt_secret[i] = wbs->maxsecret ?
1330 (plrs[i].ssecret * 100) / wbs->maxsecret : 100;
1331 if (dofrags)
1332 cnt_frags[i] = WI_fragSum(i); // we had frags
1333 }
1334 S_StartSound(0, sfx_barexp); // bang
1335 ng_state = 10;
1336 }
1337
1338 if (ng_state == 2)
1339 {
1340 if (!(bcnt&3))
1341 S_StartSound(0, sfx_pistol); // pop
1342
1343 stillticking = false;
1344
1345 for (i=0 ; i<MAXPLAYERS ; i++)
1346 {
1347 if (!playeringame[i])
1348 continue;
1349
1350 cnt_kills[i] += 2;
1351
1352 if (cnt_kills[i] >= (plrs[i].skills * 100) / wbs->maxkills)
1353 cnt_kills[i] = (plrs[i].skills * 100) / wbs->maxkills;
1354 else
1355 stillticking = true; // still got stuff to tally
1356 }
1357
1358 if (!stillticking)
1359 {
1360 S_StartSound(0, sfx_barexp);
1361 ng_state++;
1362 }
1363 }
1364 else if (ng_state == 4)
1365 {
1366 if (!(bcnt&3))
1367 S_StartSound(0, sfx_pistol);
1368
1369 stillticking = false;
1370
1371 for (i=0 ; i<MAXPLAYERS ; i++)
1372 {
1373 if (!playeringame[i])
1374 continue;
1375
1376 cnt_items[i] += 2;
1377 if (cnt_items[i] >= (plrs[i].sitems * 100) / wbs->maxitems)
1378 cnt_items[i] = (plrs[i].sitems * 100) / wbs->maxitems;
1379 else
1380 stillticking = true;
1381 }
1382
1383 if (!stillticking)
1384 {
1385 S_StartSound(0, sfx_barexp);
1386 ng_state++;
1387 }
1388 }
1389 else if (ng_state == 6)
1390 {
1391 if (!(bcnt&3))
1392 S_StartSound(0, sfx_pistol);
1393
1394 stillticking = false;
1395
1396 for (i=0 ; i<MAXPLAYERS ; i++)
1397 {
1398 if (!playeringame[i])
1399 continue;
1400
1401 cnt_secret[i] += 2;
1402
1403 // killough 2/22/98: Make secrets = 100% if maxsecret = 0:
1404
1405 if (cnt_secret[i] >= (wbs->maxsecret ? (plrs[i].ssecret * 100) / wbs->maxsecret : compatibility_level < lxdoom_1_compatibility ? 0 : 100))
1406 cnt_secret[i] = wbs->maxsecret ? (plrs[i].ssecret * 100) / wbs->maxsecret : 100;
1407 else
1408 stillticking = true;
1409 }
1410
1411 if (!stillticking)
1412 {
1413 S_StartSound(0, sfx_barexp);
1414 ng_state += 1 + 2*!dofrags;
1415 }
1416 }
1417 else if (ng_state == 8)
1418 {
1419 if (!(bcnt&3))
1420 S_StartSound(0, sfx_pistol);
1421
1422 stillticking = false;
1423
1424 for (i=0 ; i<MAXPLAYERS ; i++)
1425 {
1426 if (!playeringame[i])
1427 continue;
1428
1429 cnt_frags[i] += 1;
1430
1431 if (cnt_frags[i] >= (fsum = WI_fragSum(i)))
1432 cnt_frags[i] = fsum;
1433 else
1434 stillticking = true;
1435 }
1436
1437 if (!stillticking)
1438 {
1439 S_StartSound(0, sfx_pldeth);
1440 ng_state++;
1441 }
1442 }
1443 else if (ng_state == 10)
1444 {
1445 if (acceleratestage)
1446 {
1447 S_StartSound(0, sfx_sgcock);
1448 if ( gamemode == commercial )
1449 WI_initNoState();
1450 else
1451 WI_initShowNextLoc();
1452 }
1453 }
1454 else if (ng_state & 1)
1455 {
1456 if (!--cnt_pause)
1457 {
1458 ng_state++;
1459 cnt_pause = TICRATE;
1460 }
1461 }
1462}
1463
1464
1465// ====================================================================
1466// WI_drawNetgameStats
1467// Purpose: Put the coop stats on the screen
1468// Args: none
1469// Returns: void
1470//
1471// proff/nicolas 09/20/98 -- changed for hi-res
1472// CPhipps - patch drawing updated
1473void WI_drawNetgameStats(void)
1474{
1475 int i;
1476 int x;
1477 int y;
1478 int pwidth = V_NamePatchWidth(percent);
1479 int fwidth = V_NamePatchWidth(facebackp);
1480
1481 WI_slamBackground();
1482
1483 // draw animated background
1484 WI_drawAnimatedBack();
1485
1486 WI_drawLF();
1487
1488 // draw stat titles (top line)
1489 V_DrawNamePatch(NG_STATSX+NG_SPACINGX-V_NamePatchWidth(kills),
1490 NG_STATSY, FB, kills, CR_DEFAULT, VPT_STRETCH);
1491
1492 V_DrawNamePatch(NG_STATSX+2*NG_SPACINGX-V_NamePatchWidth(items),
1493 NG_STATSY, FB, items, CR_DEFAULT, VPT_STRETCH);
1494
1495 V_DrawNamePatch(NG_STATSX+3*NG_SPACINGX-V_NamePatchWidth(secret),
1496 NG_STATSY, FB, secret, CR_DEFAULT, VPT_STRETCH);
1497
1498 if (dofrags)
1499 V_DrawNamePatch(NG_STATSX+4*NG_SPACINGX-V_NamePatchWidth(frags),
1500 NG_STATSY, FB, frags, CR_DEFAULT, VPT_STRETCH);
1501
1502 // draw stats
1503 y = NG_STATSY + V_NamePatchHeight(kills);
1504
1505 for (i=0 ; i<MAXPLAYERS ; i++)
1506 {
1507 //int trans = playernumtotrans[i];
1508 if (!playeringame[i])
1509 continue;
1510
1511 x = NG_STATSX;
1512 V_DrawNamePatch(x-fwidth, y, FB, facebackp,
1513 i ? CR_LIMIT+i : CR_DEFAULT,
1514 VPT_STRETCH | (i ? VPT_TRANS : 0));
1515
1516 if (i == me)
1517 V_DrawNamePatch(x-fwidth, y, FB, star, CR_DEFAULT, VPT_STRETCH);
1518
1519 x += NG_SPACINGX;
1520 WI_drawPercent(x-pwidth, y+10, cnt_kills[i]); x += NG_SPACINGX;
1521 WI_drawPercent(x-pwidth, y+10, cnt_items[i]); x += NG_SPACINGX;
1522 WI_drawPercent(x-pwidth, y+10, cnt_secret[i]); x += NG_SPACINGX;
1523
1524 if (dofrags)
1525 WI_drawNum(x, y+10, cnt_frags[i], -1);
1526
1527 y += WI_SPACINGY;
1528 }
1529
1530 if (y <= SP_TIMEY)
1531 // cph - show times in coop on the entering screen
1532 WI_drawTimeStats(plrs[me].stime / TICRATE, wbs->totaltimes / TICRATE, wbs->partime / TICRATE);
1533}
1534
1535static int sp_state;
1536
1537// ====================================================================
1538// WI_initStats
1539// Purpose: Get ready for single player stats
1540// Args: none
1541// Returns: void
1542// Comment: Seems like we could do all these stats in a more generic
1543// set of routines that weren't duplicated for dm, coop, sp
1544//
1545void WI_initStats(void)
1546{
1547 state = StatCount;
1548 acceleratestage = 0;
1549 sp_state = 1;
1550
1551 // CPhipps - allocate (awful code, I know, but saves changing it all) and initialise
1552 *(cnt_kills = malloc(sizeof(*cnt_kills))) =
1553 *(cnt_items = malloc(sizeof(*cnt_items))) =
1554 *(cnt_secret= malloc(sizeof(*cnt_secret))) = -1;
1555 cnt_time = cnt_par = cnt_total_time = -1;
1556 cnt_pause = TICRATE;
1557
1558 WI_initAnimatedBack();
1559}
1560
1561// ====================================================================
1562// WI_updateStats
1563// Purpose: Calculate solo stats
1564// Args: none
1565// Returns: void
1566//
1567void WI_updateStats(void)
1568{
1569 WI_updateAnimatedBack();
1570
1571 if (acceleratestage && sp_state != 10)
1572 {
1573 acceleratestage = 0;
1574 cnt_kills[0] = (plrs[me].skills * 100) / wbs->maxkills;
1575 cnt_items[0] = (plrs[me].sitems * 100) / wbs->maxitems;
1576
1577 // killough 2/22/98: Make secrets = 100% if maxsecret = 0:
1578 cnt_secret[0] = (wbs->maxsecret ?
1579 (plrs[me].ssecret * 100) / wbs->maxsecret : 100);
1580
1581 cnt_total_time = wbs->totaltimes / TICRATE;
1582 cnt_time = plrs[me].stime / TICRATE;
1583 cnt_par = wbs->partime / TICRATE;
1584 S_StartSound(0, sfx_barexp);
1585 sp_state = 10;
1586 }
1587
1588 if (sp_state == 2)
1589 {
1590 cnt_kills[0] += 2;
1591
1592 if (!(bcnt&3))
1593 S_StartSound(0, sfx_pistol);
1594
1595 if (cnt_kills[0] >= (plrs[me].skills * 100) / wbs->maxkills)
1596 {
1597 cnt_kills[0] = (plrs[me].skills * 100) / wbs->maxkills;
1598 S_StartSound(0, sfx_barexp);
1599 sp_state++;
1600 }
1601 }
1602 else if (sp_state == 4)
1603 {
1604 cnt_items[0] += 2;
1605
1606 if (!(bcnt&3))
1607 S_StartSound(0, sfx_pistol);
1608
1609 if (cnt_items[0] >= (plrs[me].sitems * 100) / wbs->maxitems)
1610 {
1611 cnt_items[0] = (plrs[me].sitems * 100) / wbs->maxitems;
1612 S_StartSound(0, sfx_barexp);
1613 sp_state++;
1614 }
1615 }
1616 else if (sp_state == 6)
1617 {
1618 cnt_secret[0] += 2;
1619
1620 if (!(bcnt&3))
1621 S_StartSound(0, sfx_pistol);
1622
1623 // killough 2/22/98: Make secrets = 100% if maxsecret = 0:
1624 if ((!wbs->maxsecret && compatibility_level < lxdoom_1_compatibility) ||
1625 cnt_secret[0] >= (wbs->maxsecret ?
1626 (plrs[me].ssecret * 100) / wbs->maxsecret : 100))
1627 {
1628 cnt_secret[0] = (wbs->maxsecret ?
1629 (plrs[me].ssecret * 100) / wbs->maxsecret : 100);
1630 S_StartSound(0, sfx_barexp);
1631 sp_state++;
1632 }
1633 }
1634 else if (sp_state == 8)
1635 {
1636 if (!(bcnt&3))
1637 S_StartSound(0, sfx_pistol);
1638
1639 cnt_time += 3;
1640
1641 if (cnt_time >= plrs[me].stime / TICRATE)
1642 cnt_time = plrs[me].stime / TICRATE;
1643
1644 cnt_total_time += 3;
1645
1646 if (cnt_total_time >= wbs->totaltimes / TICRATE)
1647 cnt_total_time = wbs->totaltimes / TICRATE;
1648
1649 cnt_par += 3;
1650
1651 if (cnt_par >= wbs->partime / TICRATE)
1652 {
1653 cnt_par = wbs->partime / TICRATE;
1654
1655 if ((cnt_time >= plrs[me].stime / TICRATE) && (compatibility_level < lxdoom_1_compatibility || cnt_total_time >= wbs->totaltimes / TICRATE))
1656 {
1657 S_StartSound(0, sfx_barexp);
1658 sp_state++;
1659 }
1660 }
1661 }
1662 else if (sp_state == 10)
1663 {
1664 if (acceleratestage)
1665 {
1666 S_StartSound(0, sfx_sgcock);
1667
1668 if (gamemode == commercial)
1669 WI_initNoState();
1670 else
1671 WI_initShowNextLoc();
1672 }
1673 }
1674 else if (sp_state & 1)
1675 {
1676 if (!--cnt_pause)
1677 {
1678 sp_state++;
1679 cnt_pause = TICRATE;
1680 }
1681 }
1682}
1683
1684
1685// ====================================================================
1686// WI_drawStats
1687// Purpose: Put the solo stats on the screen
1688// Args: none
1689// Returns: void
1690//
1691// proff/nicolas 09/20/98 -- changed for hi-res
1692// CPhipps - patch drawing updated
1693void WI_drawStats(void)
1694{
1695 // line height
1696 int lh;
1697
1698 lh = (3*SHORT(num[0]->height))/2;
1699
1700 WI_slamBackground();
1701
1702 // draw animated background
1703 WI_drawAnimatedBack();
1704
1705 WI_drawLF();
1706
1707 V_DrawNamePatch(SP_STATSX, SP_STATSY, FB, kills, CR_DEFAULT, VPT_STRETCH);
1708 WI_drawPercent(320 - SP_STATSX, SP_STATSY, cnt_kills[0]);
1709
1710 V_DrawNamePatch(SP_STATSX, SP_STATSY+lh, FB, items, CR_DEFAULT, VPT_STRETCH);
1711 WI_drawPercent(320 - SP_STATSX, SP_STATSY+lh, cnt_items[0]);
1712
1713 V_DrawNamePatch(SP_STATSX, SP_STATSY+2*lh, FB, sp_secret, CR_DEFAULT, VPT_STRETCH);
1714 WI_drawPercent(320 - SP_STATSX, SP_STATSY+2*lh, cnt_secret[0]);
1715
1716 WI_drawTimeStats(cnt_time, cnt_total_time, cnt_par);
1717}
1718
1719// ====================================================================
1720// WI_checkForAccelerate
1721// Purpose: See if the player has hit either the attack or use key
1722// or mouse button. If so we set acceleratestage to 1 and
1723// all those display routines above jump right to the end.
1724// Args: none
1725// Returns: void
1726//
1727void WI_checkForAccelerate(void)
1728{
1729 int i;
1730 player_t *player;
1731
1732 // check for button presses to skip delays
1733 for (i=0, player = players ; i<MAXPLAYERS ; i++, player++)
1734 {
1735 if (playeringame[i])
1736 {
1737 if (player->cmd.buttons & BT_ATTACK)
1738 {
1739 if (!player->attackdown)
1740 acceleratestage = 1;
1741 player->attackdown = true;
1742 }
1743 else
1744 player->attackdown = false;
1745
1746 if (player->cmd.buttons & BT_USE)
1747 {
1748 if (!player->usedown)
1749 acceleratestage = 1;
1750 player->usedown = true;
1751 }
1752 else
1753 player->usedown = false;
1754 }
1755 }
1756}
1757
1758// ====================================================================
1759// WI_Ticker
1760// Purpose: Do various updates every gametic, for stats, animation,
1761// checking that intermission music is running, etc.
1762// Args: none
1763// Returns: void
1764//
1765void WI_Ticker(void)
1766{
1767 // counter for general background animation
1768 bcnt++;
1769
1770 if (bcnt == 1)
1771 {
1772 // intermission music
1773 if ( gamemode == commercial )
1774 S_ChangeMusic(mus_dm2int, true);
1775 else
1776 S_ChangeMusic(mus_inter, true);
1777 }
1778
1779 WI_checkForAccelerate();
1780
1781 switch (state)
1782 {
1783 case StatCount:
1784 if (deathmatch) WI_updateDeathmatchStats();
1785 else if (netgame) WI_updateNetgameStats();
1786 else WI_updateStats();
1787 break;
1788
1789 case ShowNextLoc:
1790 WI_updateShowNextLoc();
1791 break;
1792
1793 case NoState:
1794 WI_updateNoState();
1795 break;
1796 }
1797}
1798
1799/* ====================================================================
1800 * WI_loadData
1801 * Purpose: Initialize intermission data such as background graphics,
1802 * patches, map names, etc.
1803 * Args: none
1804 * Returns: void
1805 *
1806 * CPhipps - modified for new wad lump handling.
1807 * - no longer preload most graphics, other funcs can use
1808 * them by name
1809 */
1810
1811void WI_loadData(void)
1812{
1813 int i;
1814 int j;
1815 char name[9]; // limited to 8 characters
1816 anim_t* a;
1817
1818 if (gamemode != commercial)
1819 {
1820 if (wbs->epsd < 3)
1821 {
1822 for (j=0;j<NUMANIMS[wbs->epsd];j++)
1823 {
1824 a = &anims[wbs->epsd][j];
1825 for (i=0;i<a->nanims;i++)
1826 {
1827 // MONDO HACK!
1828 if (wbs->epsd != 1 || j != 8)
1829 {
1830 // animations
1831 snprintf(name, sizeof(name),"WIA%d%.2d%.2d", wbs->epsd, j, i);
1832 a->p[i] = W_CacheLumpName(name);
1833 }
1834 else
1835 {
1836 // HACK ALERT!
1837 a->p[i] = anims[1][4].p[i];
1838 }
1839 }
1840 }
1841 }
1842 }
1843
1844 for (i=0;i<10;i++)
1845 {
1846 // numbers 0-9
1847 snprintf(name,sizeof(name),"WINUM%d", i);
1848 num[i] = W_CacheLumpName(name);
1849 }
1850}
1851
1852// ====================================================================
1853// WI_unloadData
1854// Purpose: Free up the space allocated during WI_loadData
1855// Args: none
1856// Returns: void
1857//
1858// CPhipps - reverse of WI_loadData, goes through the same lumps, but unlocking
1859void WI_unloadData(void)
1860{
1861 int i,j;
1862 char name[9]; // limited to 8 characters
1863
1864 // cph - unlock gamemode dependent stuff here
1865 if (gamemode != commercial) {
1866 if (wbs->epsd < 3) {
1867 for (j=0;j<NUMANIMS[wbs->epsd];j++) {
1868 anim_t* a = &anims[wbs->epsd][j];
1869 for (i=0; i<a->nanims; i++) {
1870 // MONDO HACK!
1871 if (wbs->epsd != 1 || j != 8) {
1872 // animations
1873 snprintf(name,sizeof(name), "WIA%d%.2d%.2d", wbs->epsd, j, i);
1874 W_UnlockLumpName(name);
1875 }
1876 }
1877 }
1878 }
1879 }
1880
1881 for (i=0;i<10;i++) {
1882 // numbers 0-9
1883 snprintf(name, sizeof(name),"WINUM%d", i);
1884 W_UnlockLumpName(name);
1885 }
1886}
1887
1888
1889// ====================================================================
1890// WI_Drawer
1891// Purpose: Call the appropriate stats drawing routine depending on
1892// what kind of game is being played (DM, coop, solo)
1893// Args: none
1894// Returns: void
1895//
1896void WI_Drawer (void)
1897{
1898 switch (state)
1899 {
1900 case StatCount:
1901 if (deathmatch)
1902 WI_drawDeathmatchStats();
1903 else if (netgame)
1904 WI_drawNetgameStats();
1905 else
1906 WI_drawStats();
1907 break;
1908
1909 case ShowNextLoc:
1910 WI_drawShowNextLoc();
1911 break;
1912
1913 case NoState:
1914 WI_drawNoState();
1915 break;
1916 }
1917}
1918
1919// ====================================================================
1920// WI_initVariables
1921// Purpose: Initialize the intermission information structure
1922// Note: wbstartstruct_t is defined in d_player.h
1923// Args: wbstartstruct -- pointer to the structure with the data
1924// Returns: void
1925//
1926void WI_initVariables(wbstartstruct_t* wbstartstruct)
1927{
1928
1929 wbs = wbstartstruct;
1930
1931#ifdef RANGECHECKING
1932 if (gamemode != commercial)
1933 {
1934 if ( gamemode == retail )
1935 RNGCHECK(wbs->epsd, 0, 3);
1936 else
1937 RNGCHECK(wbs->epsd, 0, 2);
1938 }
1939 else
1940 {
1941 RNGCHECK(wbs->last, 0, 8);
1942 RNGCHECK(wbs->next, 0, 8);
1943 }
1944 RNGCHECK(wbs->pnum, 0, MAXPLAYERS);
1945 RNGCHECK(wbs->pnum, 0, MAXPLAYERS);
1946#endif
1947
1948 acceleratestage = 0;
1949 cnt = bcnt = 0;
1950 firstrefresh = 1;
1951 me = wbs->pnum;
1952 plrs = wbs->plyr;
1953
1954 if (!wbs->maxkills)
1955 wbs->maxkills = 1; // probably only useful in MAP30
1956
1957 if (!wbs->maxitems)
1958 wbs->maxitems = 1;
1959
1960 if ( gamemode != retail )
1961 if (wbs->epsd > 2)
1962 wbs->epsd -= 3;
1963}
1964
1965// ====================================================================
1966// WI_Start
1967// Purpose: Call the various init routines
1968// Note: wbstartstruct_t is defined in d_player.h
1969// Args: wbstartstruct -- pointer to the structure with the
1970// intermission data
1971// Returns: void
1972//
1973void WI_Start(wbstartstruct_t* wbstartstruct)
1974{
1975 WI_initVariables(wbstartstruct);
1976 WI_loadData();
1977
1978 if (deathmatch)
1979 WI_initDeathmatchStats();
1980 else if (netgame)
1981 WI_initNetgameStats();
1982 else
1983 WI_initStats();
1984}