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