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