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  *      Game completion, final screen animation.
31  *
32  *-----------------------------------------------------------------------------
33  */
34 
35 #include "doomstat.h"
36 #include "d_event.h"
37 #include "v_video.h"
38 #include "w_wad.h"
39 #include "s_sound.h"
40 #include "sounds.h"
41 #include "d_deh.h"  // Ty 03/22/98 - externalizations
42 #include "f_finale.h" // CPhipps - hmm...
43 
44 // Stage of animation:
45 //  0 = text, 1 = art screen, 2 = character cast
46 static int finalestage; // cph -
47 static int finalecount; // made static
48 static const char*   finaletext; // cph -
49 static const char*   finaleflat; // made static const
50 
51 // defines for the end mission display text                     // phares
52 
53 #define TEXTSPEED    3     // original value                    // phares
54 #define TEXTWAIT     250   // original value                    // phares
55 #define NEWTEXTSPEED 0.01f  // new value                         // phares
56 #define NEWTEXTWAIT  1000  // new value                         // phares
57 
58 // CPhipps - removed the old finale screen text message strings;
59 // they were commented out for ages already
60 // Ty 03/22/98 - ... the new s_WHATEVER extern variables are used
61 // in the code below instead.
62 
63 void    F_StartCast (void);
64 void    F_CastTicker (void);
65 dboolean F_CastResponder (event_t *ev);
66 void    F_CastDrawer (void);
67 
68 void WI_checkForAccelerate(void);    // killough 3/28/98: used to
69 extern int acceleratestage;          // accelerate intermission screens
70 static int midstage;                 // whether we're in "mid-stage"
71 
72 //
73 // F_StartFinale
74 //
F_StartFinale(void)75 void F_StartFinale (void)
76 {
77   gameaction = ga_nothing;
78   gamestate = GS_FINALE;
79   automapmode &= ~am_active;
80 
81   // killough 3/28/98: clear accelerative text flags
82   acceleratestage = midstage = 0;
83 
84   // Okay - IWAD dependend stuff.
85   // This has been changed severly, and
86   //  some stuff might have changed in the process.
87   switch ( gamemode )
88   {
89     // DOOM 1 - E1, E3 or E4, but each nine missions
90     case shareware:
91     case registered:
92     case retail:
93     {
94       S_ChangeMusic(mus_victor, true);
95 
96       switch (gameepisode)
97       {
98         case 1:
99              finaleflat = bgflatE1; // Ty 03/30/98 - new externalized bg flats
100              finaletext = s_E1TEXT; // Ty 03/23/98 - Was e1text variable.
101              break;
102         case 2:
103              finaleflat = bgflatE2;
104              finaletext = s_E2TEXT; // Ty 03/23/98 - Same stuff for each
105              break;
106         case 3:
107              finaleflat = bgflatE3;
108              finaletext = s_E3TEXT;
109              break;
110         case 4:
111              finaleflat = bgflatE4;
112              finaletext = s_E4TEXT;
113              break;
114         default:
115              // Ouch.
116              break;
117       }
118       break;
119     }
120 
121     // DOOM II and missions packs with E1, M34
122     case commercial:
123     {
124       S_ChangeMusic(mus_read_m, true);
125 
126       // Ty 08/27/98 - added the gamemission logic
127       switch (gamemap)
128       {
129         case 6:
130              finaleflat = bgflat06;
131              finaletext = (gamemission==pack_tnt)  ? s_T1TEXT :
132                           (gamemission==pack_plut) ? s_P1TEXT : s_C1TEXT;
133              break;
134         case 11:
135              finaleflat = bgflat11;
136              finaletext = (gamemission==pack_tnt)  ? s_T2TEXT :
137                           (gamemission==pack_plut) ? s_P2TEXT : s_C2TEXT;
138              break;
139         case 20:
140              finaleflat = bgflat20;
141              finaletext = (gamemission==pack_tnt)  ? s_T3TEXT :
142                           (gamemission==pack_plut) ? s_P3TEXT : s_C3TEXT;
143              break;
144         case 30:
145              finaleflat = bgflat30;
146              finaletext = (gamemission==pack_tnt)  ? s_T4TEXT :
147                           (gamemission==pack_plut) ? s_P4TEXT : s_C4TEXT;
148              break;
149         case 15:
150              finaleflat = bgflat15;
151              finaletext = (gamemission==pack_tnt)  ? s_T5TEXT :
152                           (gamemission==pack_plut) ? s_P5TEXT : s_C5TEXT;
153              break;
154         case 31:
155              finaleflat = bgflat31;
156              finaletext = (gamemission==pack_tnt)  ? s_T6TEXT :
157                           (gamemission==pack_plut) ? s_P6TEXT : s_C6TEXT;
158              break;
159         default:
160              // Ouch.
161              break;
162       }
163       break;
164       // Ty 08/27/98 - end gamemission logic
165     }
166 
167     // Indeterminate.
168     default:  // Ty 03/30/98 - not externalized
169          S_ChangeMusic(mus_read_m, true);
170          finaleflat = "F_SKY1"; // Not used anywhere else.
171          finaletext = s_C1TEXT;  // FIXME - other text, music?
172          break;
173   }
174 
175   finalestage = 0;
176   finalecount = 0;
177 }
178 
179 
180 
F_Responder(event_t * event)181 dboolean F_Responder (event_t *event)
182 {
183   if (finalestage == 2)
184     return F_CastResponder (event);
185 
186   return false;
187 }
188 
189 // Get_TextSpeed() returns the value of the text display speed  // phares
190 // Rewritten to allow user-directed acceleration -- killough 3/28/98
191 
Get_TextSpeed(void)192 static float Get_TextSpeed(void)
193 {
194   return midstage ? NEWTEXTSPEED : (midstage=acceleratestage) ?
195     acceleratestage=0, NEWTEXTSPEED : TEXTSPEED;
196 }
197 
198 
199 //
200 // F_Ticker
201 //
202 // killough 3/28/98: almost totally rewritten, to use
203 // player-directed acceleration instead of constant delays.
204 // Now the player can accelerate the text display by using
205 // the fire/use keys while it is being printed. The delay
206 // automatically responds to the user, and gives enough
207 // time to read.
208 //
209 // killough 5/10/98: add back v1.9 demo compatibility
210 //
211 
F_Ticker(void)212 void F_Ticker(void)
213 {
214   int i;
215   if (!demo_compatibility)
216     WI_checkForAccelerate();  // killough 3/28/98: check for acceleration
217   else
218     if (gamemode == commercial && finalecount > 50) // check for skipping
219       for (i=0; i<MAXPLAYERS; i++)
220         if (players[i].cmd.buttons)
221           goto next_level;      // go on to the next level
222 
223   // advance animation
224   finalecount++;
225 
226   if (finalestage == 2)
227     F_CastTicker();
228 
229   if (!finalestage)
230     {
231       float speed = demo_compatibility ? TEXTSPEED : Get_TextSpeed();
232       /* killough 2/28/98: changed to allow acceleration */
233       if (finalecount > strlen(finaletext)*speed +
234           (midstage ? NEWTEXTWAIT : TEXTWAIT) ||
235           (midstage && acceleratestage)) {
236         if (gamemode != commercial)       // Doom 1 / Ultimate Doom episode end
237           {                               // with enough time, it's automatic
238             finalecount = 0;
239             finalestage = 1;
240             wipegamestate = -1;         // force a wipe
241             if (gameepisode == 3)
242               S_StartMusic(mus_bunny);
243           }
244         else   // you must press a button to continue in Doom 2
245           if (!demo_compatibility && midstage)
246             {
247             next_level:
248               if (gamemap == 30)
249                 F_StartCast();              // cast of Doom 2 characters
250               else
251                 gameaction = ga_worlddone;  // next level, e.g. MAP07
252             }
253       }
254     }
255 }
256 
257 //
258 // F_TextWrite
259 //
260 // This program displays the background and text at end-mission     // phares
261 // text time. It draws both repeatedly so that other displays,      //   |
262 // like the main menu, can be drawn over it dynamically and         //   V
263 // erased dynamically. The TEXTSPEED constant is changed into
264 // the Get_TextSpeed function so that the speed of writing the      //   ^
265 // text can be increased, and there's still time to read what's     //   |
266 // written.                                                         // phares
267 // CPhipps - reformatted
268 
269 #include "hu_stuff.h"
270 extern patchnum_t hu_font[HU_FONTSIZE];
271 
272 
F_TextWrite(void)273 static void F_TextWrite (void)
274 {
275   V_DrawBackground(finaleflat, 0);
276   { // draw some of the text onto the screen
277     int         cx = 10;
278     int         cy = 10;
279     const char* ch = finaletext; // CPhipps - const
280     int         count = (int)((float)(finalecount - 10)/Get_TextSpeed()); // phares
281     int         w;
282 
283     if (count < 0)
284       count = 0;
285 
286     for ( ; count ; count-- ) {
287       int       c = *ch++;
288 
289       if (!c)
290   break;
291       if (c == '\n') {
292   cx = 10;
293   cy += 11;
294   continue;
295       }
296 
297       c = toupper(c) - HU_FONTSTART;
298       if (c < 0 || c> HU_FONTSIZE) {
299   cx += 4;
300   continue;
301       }
302 
303       w = hu_font[c].width;
304       if (cx+w > SCREENWIDTH)
305   break;
306       // CPhipps - patch drawing updated
307       V_DrawNumPatch(cx, cy, 0, hu_font[c].lumpnum, CR_DEFAULT, VPT_STRETCH);
308       cx+=w;
309     }
310   }
311 }
312 
313 //
314 // Final DOOM 2 animation
315 // Casting by id Software.
316 //   in order of appearance
317 //
318 typedef struct
319 {
320   const char **name; // CPhipps - const**
321   mobjtype_t   type;
322 } castinfo_t;
323 
324 static const castinfo_t castorder[] = { // CPhipps - static const, initialised here
325   { &s_CC_ZOMBIE,  MT_POSSESSED },
326   { &s_CC_SHOTGUN, MT_SHOTGUY },
327   { &s_CC_HEAVY,   MT_CHAINGUY },
328   { &s_CC_IMP,     MT_TROOP },
329   { &s_CC_DEMON,   MT_SERGEANT },
330   { &s_CC_LOST,    MT_SKULL },
331   { &s_CC_CACO,    MT_HEAD },
332   { &s_CC_HELL,    MT_KNIGHT },
333   { &s_CC_BARON,   MT_BRUISER },
334   { &s_CC_ARACH,   MT_BABY },
335   { &s_CC_PAIN,    MT_PAIN },
336   { &s_CC_REVEN,   MT_UNDEAD },
337   { &s_CC_MANCU,   MT_FATSO },
338   { &s_CC_ARCH,    MT_VILE },
339   { &s_CC_SPIDER,  MT_SPIDER },
340   { &s_CC_CYBER,   MT_CYBORG },
341   { &s_CC_HERO,    MT_PLAYER },
342   { NULL,         0}
343   };
344 
345 int             castnum;
346 int             casttics;
347 state_t*        caststate;
348 dboolean         castdeath;
349 int             castframes;
350 int             castonmelee;
351 dboolean         castattacking;
352 
353 
354 //
355 // F_StartCast
356 //
357 
F_StartCast(void)358 void F_StartCast (void)
359 {
360   wipegamestate = -1;         // force a screen wipe
361   castnum = 0;
362   caststate = &states[mobjinfo[castorder[castnum].type].seestate];
363   casttics = caststate->tics;
364   castdeath = false;
365   finalestage = 2;
366   castframes = 0;
367   castonmelee = 0;
368   castattacking = false;
369   S_ChangeMusic(mus_evil, true);
370 }
371 
372 
373 //
374 // F_CastTicker
375 //
F_CastTicker(void)376 void F_CastTicker (void)
377 {
378   int st;
379   int sfx;
380 
381   if (--casttics > 0)
382     return;                 // not time to change state yet
383 
384   if (caststate->tics == -1 || caststate->nextstate == S_NULL)
385   {
386     // switch from deathstate to next monster
387     castnum++;
388     castdeath = false;
389     if (castorder[castnum].name == NULL)
390       castnum = 0;
391     if (mobjinfo[castorder[castnum].type].seesound)
392       S_StartSound (NULL, mobjinfo[castorder[castnum].type].seesound);
393     caststate = &states[mobjinfo[castorder[castnum].type].seestate];
394     castframes = 0;
395   }
396   else
397   {
398     // just advance to next state in animation
399     if (caststate == &states[S_PLAY_ATK1])
400       goto stopattack;    // Oh, gross hack!
401     st = caststate->nextstate;
402     caststate = &states[st];
403     castframes++;
404 
405     // sound hacks....
406     switch (st)
407     {
408       case S_PLAY_ATK1:     sfx = sfx_dshtgn; break;
409       case S_POSS_ATK2:     sfx = sfx_pistol; break;
410       case S_SPOS_ATK2:     sfx = sfx_shotgn; break;
411       case S_VILE_ATK2:     sfx = sfx_vilatk; break;
412       case S_SKEL_FIST2:    sfx = sfx_skeswg; break;
413       case S_SKEL_FIST4:    sfx = sfx_skepch; break;
414       case S_SKEL_MISS2:    sfx = sfx_skeatk; break;
415       case S_FATT_ATK8:
416       case S_FATT_ATK5:
417       case S_FATT_ATK2:     sfx = sfx_firsht; break;
418       case S_CPOS_ATK2:
419       case S_CPOS_ATK3:
420       case S_CPOS_ATK4:     sfx = sfx_shotgn; break;
421       case S_TROO_ATK3:     sfx = sfx_claw; break;
422       case S_SARG_ATK2:     sfx = sfx_sgtatk; break;
423       case S_BOSS_ATK2:
424       case S_BOS2_ATK2:
425       case S_HEAD_ATK2:     sfx = sfx_firsht; break;
426       case S_SKULL_ATK2:    sfx = sfx_sklatk; break;
427       case S_SPID_ATK2:
428       case S_SPID_ATK3:     sfx = sfx_shotgn; break;
429       case S_BSPI_ATK2:     sfx = sfx_plasma; break;
430       case S_CYBER_ATK2:
431       case S_CYBER_ATK4:
432       case S_CYBER_ATK6:    sfx = sfx_rlaunc; break;
433       case S_PAIN_ATK3:     sfx = sfx_sklatk; break;
434       default: sfx = 0; break;
435     }
436 
437     if (sfx)
438       S_StartSound (NULL, sfx);
439   }
440 
441   if (castframes == 12)
442   {
443     // go into attack frame
444     castattacking = true;
445     if (castonmelee)
446       caststate=&states[mobjinfo[castorder[castnum].type].meleestate];
447     else
448       caststate=&states[mobjinfo[castorder[castnum].type].missilestate];
449     castonmelee ^= 1;
450     if (caststate == &states[S_NULL])
451     {
452       if (castonmelee)
453         caststate=
454           &states[mobjinfo[castorder[castnum].type].meleestate];
455       else
456         caststate=
457           &states[mobjinfo[castorder[castnum].type].missilestate];
458     }
459   }
460 
461   if (castattacking)
462   {
463     if (castframes == 24
464        ||  caststate == &states[mobjinfo[castorder[castnum].type].seestate] )
465     {
466       stopattack:
467       castattacking = false;
468       castframes = 0;
469       caststate = &states[mobjinfo[castorder[castnum].type].seestate];
470     }
471   }
472 
473   casttics = caststate->tics;
474   if (casttics == -1)
475       casttics = 15;
476 }
477 
478 
479 //
480 // F_CastResponder
481 //
482 
F_CastResponder(event_t * ev)483 dboolean F_CastResponder (event_t* ev)
484 {
485   if (ev->type != ev_keydown)
486     return false;
487 
488   if (castdeath)
489     return true;                    // already in dying frames
490 
491   // go into death frame
492   castdeath = true;
493   caststate = &states[mobjinfo[castorder[castnum].type].deathstate];
494   casttics = caststate->tics;
495   castframes = 0;
496   castattacking = false;
497   if (mobjinfo[castorder[castnum].type].deathsound)
498     S_StartSound (NULL, mobjinfo[castorder[castnum].type].deathsound);
499 
500   return true;
501 }
502 
503 
F_CastPrint(const char * text)504 static void F_CastPrint (const char* text) // CPhipps - static, const char*
505 {
506   const char* ch; // CPhipps - const
507   int         c;
508   int         cx;
509   int         w;
510   int         width;
511 
512   // find width
513   ch = text;
514   width = 0;
515 
516   while (ch)
517   {
518     c = *ch++;
519     if (!c)
520       break;
521     c = toupper(c) - HU_FONTSTART;
522     if (c < 0 || c> HU_FONTSIZE)
523     {
524       width += 4;
525       continue;
526     }
527 
528     w = hu_font[c].width;
529     width += w;
530   }
531 
532   // draw it
533   cx = 160-width/2;
534   ch = text;
535   while (ch)
536   {
537     c = *ch++;
538     if (!c)
539       break;
540     c = toupper(c) - HU_FONTSTART;
541     if (c < 0 || c> HU_FONTSIZE)
542     {
543       cx += 4;
544       continue;
545     }
546 
547     w = hu_font[c].width;
548     // CPhipps - patch drawing updated
549     V_DrawNumPatch(cx, 180, 0, hu_font[c].lumpnum, CR_DEFAULT, VPT_STRETCH);
550     cx+=w;
551   }
552 }
553 
554 
555 //
556 // F_CastDrawer
557 //
558 
F_CastDrawer(void)559 void F_CastDrawer (void)
560 {
561   spritedef_t*        sprdef;
562   spriteframe_t*      sprframe;
563   int                 lump;
564   dboolean             flip;
565 
566   // erase the entire screen to a background
567   // CPhipps - patch drawing updated
568   V_DrawNamePatch(0,0,0, bgcastcall, CR_DEFAULT, VPT_STRETCH); // Ty 03/30/98 bg texture extern
569   // e6y: wide-res
570   V_FillBorder(-1, 0);
571 
572   F_CastPrint (*(castorder[castnum].name));
573 
574   // draw the current frame in the middle of the screen
575   sprdef = &sprites[caststate->sprite];
576   sprframe = &sprdef->spriteframes[ caststate->frame & FF_FRAMEMASK];
577   lump = sprframe->lump[0];
578   flip = (dboolean)(sprframe->flip & 1);
579 
580   // CPhipps - patch drawing updated
581   V_DrawNumPatch(160, 170, 0, lump+firstspritelump, CR_DEFAULT,
582      VPT_STRETCH | (flip ? VPT_FLIP : 0));
583 }
584 
585 //
586 // F_BunnyScroll
587 //
588 static const char pfub2[] = { "PFUB2" };
589 static const char pfub1[] = { "PFUB1" };
590 
F_BunnyScroll(void)591 static void F_BunnyScroll (void)
592 {
593   char        name[10];
594   int         stage;
595   static int  laststage;
596 
597   {
598     int scrolled = 320 - (finalecount-230)/2;
599     if (scrolled <= 0) {
600       V_DrawNamePatch(0, 0, 0, pfub2, CR_DEFAULT, VPT_STRETCH);
601     } else if (scrolled >= 320) {
602       V_DrawNamePatch(0, 0, 0, pfub1, CR_DEFAULT, VPT_STRETCH);
603     } else {
604       V_DrawNamePatch(320-scrolled, 0, 0, pfub1, CR_DEFAULT, VPT_STRETCH);
605       V_DrawNamePatch(-scrolled, 0, 0, pfub2, CR_DEFAULT, VPT_STRETCH);
606     }
607   }
608 
609   if (finalecount < 1130)
610     return;
611   if (finalecount < 1180)
612   {
613     // CPhipps - patch drawing updated
614     V_DrawNamePatch((320-13*8)/2, (200-8*8)/2,0, "END0", CR_DEFAULT, VPT_STRETCH);
615     laststage = 0;
616     return;
617   }
618 
619   stage = (finalecount-1180) / 5;
620   if (stage > 6)
621     stage = 6;
622   if (stage > laststage)
623   {
624     S_StartSound (NULL, sfx_pistol);
625     laststage = stage;
626   }
627 
628   sprintf (name,"END%i",stage);
629   // CPhipps - patch drawing updated
630   V_DrawNamePatch((320-13*8)/2, (200-8*8)/2, 0, name, CR_DEFAULT, VPT_STRETCH);
631 }
632 
633 
634 //
635 // F_Drawer
636 //
F_Drawer(void)637 void F_Drawer (void)
638 {
639   if (finalestage == 2)
640   {
641     F_CastDrawer ();
642     return;
643   }
644 
645   if (!finalestage)
646     F_TextWrite ();
647   else
648   {
649     switch (gameepisode)
650     {
651       // CPhipps - patch drawing updated
652       case 1:
653            if ( gamemode == retail )
654              V_DrawNamePatch(0, 0, 0, "CREDIT", CR_DEFAULT, VPT_STRETCH);
655            else
656              V_DrawNamePatch(0, 0, 0, "HELP2", CR_DEFAULT, VPT_STRETCH);
657            break;
658       case 2:
659            V_DrawNamePatch(0, 0, 0, "VICTORY2", CR_DEFAULT, VPT_STRETCH);
660            break;
661       case 3:
662            F_BunnyScroll ();
663            break;
664       case 4:
665            V_DrawNamePatch(0, 0, 0, "ENDPIC", CR_DEFAULT, VPT_STRETCH);
666            break;
667     }
668     // e6y: wide-res
669     V_FillBorder(-1, 0);
670   }
671 }
672