summaryrefslogtreecommitdiff
path: root/apps/plugins/doom/f_finale.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/plugins/doom/f_finale.c')
-rw-r--r--apps/plugins/doom/f_finale.c682
1 files changed, 682 insertions, 0 deletions
diff --git a/apps/plugins/doom/f_finale.c b/apps/plugins/doom/f_finale.c
new file mode 100644
index 0000000000..4deb3df689
--- /dev/null
+++ b/apps/plugins/doom/f_finale.c
@@ -0,0 +1,682 @@
1/* Emacs style mode select -*- C++ -*-
2 *-----------------------------------------------------------------------------
3 *
4 *
5 * PrBoom a Doom port merged with LxDoom and LSDLDoom
6 * based on BOOM, a modified and improved DOOM engine
7 * Copyright (C) 1999 by
8 * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman
9 * Copyright (C) 1999-2000 by
10 * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
25 * 02111-1307, USA.
26 *
27 * DESCRIPTION:
28 * Game completion, final screen animation.
29 *
30 *-----------------------------------------------------------------------------
31 */
32
33#include "doomstat.h"
34#include "d_event.h"
35#include "d_englsh.h"
36#include "m_swap.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#include "rockmacros.h"
44
45// Stage of animation:
46// 0 = text, 1 = art screen, 2 = character cast
47static int finalestage; // cph -
48static int finalecount; // made static
49static const char* finaletext; // cph -
50static const char* finaleflat; // made static const
51
52// defines for the end mission display text // phares
53
54#define TEXTSPEED 3 // original value // phares
55#define TEXTWAIT 250 // original value // phares
56#define NEWTEXTSPEED 0.01f // new value // phares
57#define NEWTEXTWAIT 1000 // new value // phares
58
59// CPhipps - removed the old finale screen text message strings;
60// they were commented out for ages already
61// Ty 03/22/98 - ... the new s_WHATEVER extern variables are used
62// in the code below instead.
63
64void F_StartCast (void);
65void F_CastTicker (void);
66boolean F_CastResponder (event_t *ev);
67void F_CastDrawer (void);
68
69void WI_checkForAccelerate(void); // killough 3/28/98: used to
70extern int acceleratestage; // accelerate intermission screens
71static int midstage; // whether we're in "mid-stage"
72
73//
74// F_StartFinale
75//
76void F_StartFinale (void)
77{
78 gameaction = ga_nothing;
79 gamestate = GS_FINALE;
80 automapmode &= ~am_active;
81
82 // killough 3/28/98: clear accelerative text flags
83 acceleratestage = midstage = 0;
84
85 // Okay - IWAD dependend stuff.
86 // This has been changed severly, and
87 // some stuff might have changed in the process.
88 switch ( gamemode )
89 {
90
91 // DOOM 1 - E1, E3 or E4, but each nine missions
92 case shareware:
93 case registered:
94 case retail:
95 {
96 S_ChangeMusic(mus_victor, true);
97
98 switch (gameepisode)
99 {
100 case 1:
101 finaleflat = "FLOOR4_8";
102 finaletext = E1TEXT;
103 break;
104 case 2:
105 finaleflat = "SFLR6_1";
106 finaletext = E2TEXT;
107 break;
108 case 3:
109 finaleflat = "MFLR8_4";
110 finaletext = E3TEXT;
111 break;
112 case 4:
113 finaleflat = "MFLR8_3";
114 finaletext = E4TEXT;
115 break;
116 default:
117 // Ouch.
118 break;
119 }
120 break;
121 }
122
123 // DOOM II and missions packs with E1, M34
124 case commercial:
125 {
126 S_ChangeMusic(mus_read_m, true);
127
128 // Ty 08/27/98 - added the gamemission logic
129 switch (gamemap)
130 {
131 case 6:
132 finaleflat = "SLIME16";
133 finaletext = (gamemission==pack_tnt) ? T1TEXT :
134 (gamemission==pack_plut) ? P1TEXT : C1TEXT;
135 break;
136 case 11:
137 finaleflat = "RROCK14";
138 finaletext = (gamemission==pack_tnt) ? T2TEXT :
139 (gamemission==pack_plut) ? P2TEXT : C2TEXT;
140 break;
141 case 20:
142 finaleflat = "RROCK07";
143 finaletext = (gamemission==pack_tnt) ? T3TEXT :
144 (gamemission==pack_plut) ? P3TEXT : C3TEXT;
145 break;
146 case 30:
147 finaleflat = "RROCK17";
148 finaletext = (gamemission==pack_tnt) ? T4TEXT :
149 (gamemission==pack_plut) ? P4TEXT : C4TEXT;
150 break;
151 case 15:
152 finaleflat = "RROCK13";
153 finaletext = (gamemission==pack_tnt) ? T5TEXT :
154 (gamemission==pack_plut) ? P5TEXT : C5TEXT;
155 break;
156 case 31:
157 finaleflat = "RROCK19";
158 finaletext = (gamemission==pack_tnt) ? T6TEXT :
159 (gamemission==pack_plut) ? P6TEXT : C6TEXT;
160 break;
161 default:
162 // Ouch.
163 break;
164 }
165 break;
166 }
167
168
169 // Indeterminate.
170 default:
171 S_ChangeMusic(mus_read_m, true);
172 finaleflat = "F_SKY1"; // Not used anywhere else.
173 finaletext = C1TEXT; // FIXME - other text, music?
174 break;
175 }
176
177 finalestage = 0;
178 finalecount = 0;
179
180}
181
182
183
184boolean F_Responder (event_t *event)
185{
186 if (finalestage == 2)
187 return F_CastResponder (event);
188
189 return false;
190}
191
192// Get_TextSpeed() returns the value of the text display speed // phares
193// Rewritten to allow user-directed acceleration -- killough 3/28/98
194
195static float Get_TextSpeed(void)
196{
197 return midstage ? NEWTEXTSPEED : (midstage=acceleratestage) ?
198 acceleratestage=0, NEWTEXTSPEED : TEXTSPEED;
199}
200
201//
202// F_Ticker
203//
204// killough 3/28/98: almost totally rewritten, to use
205// player-directed acceleration instead of constant delays.
206// Now the player can accelerate the text display by using
207// the fire/use keys while it is being printed. The delay
208// automatically responds to the user, and gives enough
209// time to read.
210//
211// killough 5/10/98: add back v1.9 demo compatibility
212//
213
214void F_Ticker(void)
215{
216 int i;
217 if (!demo_compatibility)
218 WI_checkForAccelerate(); // killough 3/28/98: check for acceleration
219 else
220 if (gamemode == commercial && finalecount > 50) // check for skipping
221 for (i=0; i<MAXPLAYERS; i++)
222 if (players[i].cmd.buttons)
223 goto next_level; // go on to the next level
224
225 // advance animation
226 finalecount++;
227
228 if (finalestage == 2)
229 F_CastTicker();
230
231 if (!finalestage)
232 {
233 float speed = demo_compatibility ? TEXTSPEED : Get_TextSpeed();
234 /* killough 2/28/98: changed to allow acceleration */
235 if (finalecount > strlen(finaletext)*speed +
236 (midstage ? NEWTEXTWAIT : TEXTWAIT) ||
237 (midstage && acceleratestage)) {
238 if (gamemode != commercial) // Doom 1 / Ultimate Doom episode end
239 { // with enough time, it's automatic
240 finalecount = 0;
241 finalestage = 1;
242 wipegamestate = -1; // force a wipe
243 if (gameepisode == 3)
244 S_StartMusic(mus_bunny);
245 }
246 else // you must press a button to continue in Doom 2
247 if (!demo_compatibility && midstage)
248 {
249next_level:
250 if (gamemap == 30)
251 F_StartCast(); // cast of Doom 2 characters
252 else
253 gameaction = ga_worlddone; // next level, e.g. MAP07
254 }
255 }
256 }
257}
258
259
260//
261// F_TextWrite
262//
263// This program displays the background and text at end-mission // phares
264// text time. It draws both repeatedly so that other displays, // |
265// like the main menu, can be drawn over it dynamically and // V
266// erased dynamically. The TEXTSPEED constant is changed into
267// the Get_TextSpeed function so that the speed of writing the // ^
268// text can be increased, and there's still time to read what's // |
269// written. // phares
270// CPhipps - reformatted
271
272#include "hu_stuff.h"
273extern patchnum_t hu_font[HU_FONTSIZE];
274
275
276void F_TextWrite (void)
277{
278 V_DrawBackground(finaleflat, 0);
279 { // draw some of the text onto the screen
280 int cx = 10;
281 int cy = 10;
282 const char* ch = finaletext; // CPhipps - const
283 int count = (int)((float)(finalecount - 10)/Get_TextSpeed()); // phares
284 int w;
285
286 if (count < 0)
287 count = 0;
288
289 for ( ; count ; count-- ) {
290 int c = *ch++;
291
292 if (!c)
293 break;
294 if (c == '\n') {
295 cx = 10;
296 cy += 11;
297 continue;
298 }
299
300 c = toupper(c) - HU_FONTSTART;
301 if (c < 0 || c> HU_FONTSIZE) {
302 cx += 4;
303 continue;
304 }
305
306 w = SHORT (hu_font[c].width);
307 if (cx+w > SCREENWIDTH)
308 break;
309 // CPhipps - patch drawing updated
310 V_DrawNumPatch(cx, cy, 0, hu_font[c].lumpnum, CR_DEFAULT, VPT_STRETCH);
311 cx+=w;
312 }
313 }
314}
315
316//
317// Final DOOM 2 animation
318// Casting by id Software.
319// in order of appearance
320//
321typedef struct
322{
323 const char *name; // CPhipps - const**
324 mobjtype_t type;
325} castinfo_t;
326
327#define MAX_CASTORDER 18 /* Ty - hard coded for now */
328static const castinfo_t castorder[] = { // CPhipps - static const, initialised here
329 { CC_ZOMBIE, MT_POSSESSED },
330 { CC_SHOTGUN, MT_SHOTGUY },
331 { CC_HEAVY, MT_CHAINGUY },
332 { CC_IMP, MT_TROOP },
333 { CC_DEMON, MT_SERGEANT },
334 { CC_LOST, MT_SKULL },
335 { CC_CACO, MT_HEAD },
336 { CC_HELL, MT_KNIGHT },
337 { CC_BARON, MT_BRUISER },
338 { CC_ARACH, MT_BABY },
339 { CC_PAIN, MT_PAIN },
340 { CC_REVEN, MT_UNDEAD },
341 { CC_MANCU, MT_FATSO },
342 { CC_ARCH, MT_VILE },
343 { CC_SPIDER, MT_SPIDER },
344 { CC_CYBER, MT_CYBORG },
345 { CC_HERO, MT_PLAYER },
346 { NULL, 0}
347 };
348
349int castnum;
350int casttics;
351state_t* caststate;
352boolean castdeath;
353int castframes;
354int castonmelee;
355boolean castattacking;
356
357
358//
359// F_StartCast
360//
361extern gamestate_t wipegamestate;
362
363void F_StartCast (void)
364{
365 wipegamestate = -1; // force a screen wipe
366 castnum = 0;
367 caststate = &states[mobjinfo[castorder[castnum].type].seestate];
368 casttics = caststate->tics;
369 castdeath = false;
370 finalestage = 2;
371 castframes = 0;
372 castonmelee = 0;
373 castattacking = false;
374 S_ChangeMusic(mus_evil, true);
375}
376
377
378//
379// F_CastTicker
380//
381void F_CastTicker (void)
382{
383 int st;
384 int sfx;
385
386 if (--casttics > 0)
387 return; // not time to change state yet
388
389 if (caststate->tics == -1 || caststate->nextstate == S_NULL)
390 {
391 // switch from deathstate to next monster
392 castnum++;
393 castdeath = false;
394 if (castorder[castnum].name == NULL)
395 castnum = 0;
396 if (mobjinfo[castorder[castnum].type].seesound)
397 S_StartSound (NULL, mobjinfo[castorder[castnum].type].seesound);
398 caststate = &states[mobjinfo[castorder[castnum].type].seestate];
399 castframes = 0;
400 }
401 else
402 {
403 // just advance to next state in animation
404 if (caststate == &states[S_PLAY_ATK1])
405 goto stopattack; // Oh, gross hack!
406 st = caststate->nextstate;
407 caststate = &states[st];
408 castframes++;
409
410 // sound hacks....
411 switch (st)
412 {
413 case S_PLAY_ATK1: sfx = sfx_dshtgn; break;
414 case S_POSS_ATK2: sfx = sfx_pistol; break;
415 case S_SPOS_ATK2: sfx = sfx_shotgn; break;
416 case S_VILE_ATK2: sfx = sfx_vilatk; break;
417 case S_SKEL_FIST2: sfx = sfx_skeswg; break;
418 case S_SKEL_FIST4: sfx = sfx_skepch; break;
419 case S_SKEL_MISS2: sfx = sfx_skeatk; break;
420 case S_FATT_ATK8:
421 case S_FATT_ATK5:
422 case S_FATT_ATK2: sfx = sfx_firsht; break;
423 case S_CPOS_ATK2:
424 case S_CPOS_ATK3:
425 case S_CPOS_ATK4: sfx = sfx_shotgn; break;
426 case S_TROO_ATK3: sfx = sfx_claw; break;
427 case S_SARG_ATK2: sfx = sfx_sgtatk; break;
428 case S_BOSS_ATK2:
429 case S_BOS2_ATK2:
430 case S_HEAD_ATK2: sfx = sfx_firsht; break;
431 case S_SKULL_ATK2: sfx = sfx_sklatk; break;
432 case S_SPID_ATK2:
433 case S_SPID_ATK3: sfx = sfx_shotgn; break;
434 case S_BSPI_ATK2: sfx = sfx_plasma; break;
435 case S_CYBER_ATK2:
436 case S_CYBER_ATK4:
437 case S_CYBER_ATK6: sfx = sfx_rlaunc; break;
438 case S_PAIN_ATK3: sfx = sfx_sklatk; break;
439 default: sfx = 0; break;
440 }
441
442 if (sfx)
443 S_StartSound (NULL, sfx);
444 }
445
446 if (castframes == 12)
447 {
448 // go into attack frame
449 castattacking = true;
450 if (castonmelee)
451 caststate=&states[mobjinfo[castorder[castnum].type].meleestate];
452 else
453 caststate=&states[mobjinfo[castorder[castnum].type].missilestate];
454 castonmelee ^= 1;
455 if (caststate == &states[S_NULL])
456 {
457 if (castonmelee)
458 caststate=
459 &states[mobjinfo[castorder[castnum].type].meleestate];
460 else
461 caststate=
462 &states[mobjinfo[castorder[castnum].type].missilestate];
463 }
464 }
465
466 if (castattacking)
467 {
468 if (castframes == 24
469 || caststate == &states[mobjinfo[castorder[castnum].type].seestate] )
470 {
471stopattack:
472 castattacking = false;
473 castframes = 0;
474 caststate = &states[mobjinfo[castorder[castnum].type].seestate];
475 }
476 }
477
478 casttics = caststate->tics;
479 if (casttics == -1)
480 casttics = 15;
481}
482
483
484//
485// F_CastResponder
486//
487
488boolean F_CastResponder (event_t* ev)
489{
490 if (ev->type != ev_keydown)
491 return false;
492
493 if (castdeath)
494 return true; // already in dying frames
495
496 // go into death frame
497 castdeath = true;
498 caststate = &states[mobjinfo[castorder[castnum].type].deathstate];
499 casttics = caststate->tics;
500 castframes = 0;
501 castattacking = false;
502 if (mobjinfo[castorder[castnum].type].deathsound)
503 S_StartSound (NULL, mobjinfo[castorder[castnum].type].deathsound);
504
505 return true;
506}
507
508
509static void F_CastPrint (const char* text) // CPhipps - static, const char*
510{
511 const char* ch; // CPhipps - const
512 int c;
513 int cx;
514 int w;
515 int width;
516
517 // find width
518 ch = text;
519 width = 0;
520
521 while (ch)
522 {
523 c = *ch++;
524 if (!c)
525 break;
526 c = toupper(c) - HU_FONTSTART;
527 if (c < 0 || c> HU_FONTSIZE)
528 {
529 width += 4;
530 continue;
531 }
532
533 w = SHORT (hu_font[c].width);
534 width += w;
535 }
536
537 // draw it
538 cx = 160-width/2;
539 ch = text;
540 while (ch)
541 {
542 c = *ch++;
543 if (!c)
544 break;
545 c = toupper(c) - HU_FONTSTART;
546 if (c < 0 || c> HU_FONTSIZE)
547 {
548 cx += 4;
549 continue;
550 }
551
552 w = SHORT (hu_font[c].width);
553 // CPhipps - patch drawing updated
554 V_DrawNumPatch(cx, 180, 0, hu_font[c].lumpnum, CR_DEFAULT, VPT_STRETCH);
555 cx+=w;
556 }
557}
558
559
560//
561// F_CastDrawer
562//
563void V_DrawPatchFlipped (int x, int y, int scrn, patch_t *patch);
564
565void F_CastDrawer (void)
566{
567 spritedef_t* sprdef;
568 spriteframe_t* sprframe;
569 int lump;
570 boolean flip;
571
572 // erase the entire screen to a background
573 V_DrawNamePatch(0,0,0, "BOSSBACK", CR_DEFAULT, VPT_STRETCH); // Ty 03/30/98 bg texture extern
574
575 F_CastPrint (castorder[castnum].name);
576
577 // draw the current frame in the middle of the screen
578 sprdef = &sprites[caststate->sprite];
579 sprframe = &sprdef->spriteframes[ caststate->frame & FF_FRAMEMASK];
580 lump = sprframe->lump[0];
581 flip = (boolean)sprframe->flip[0];
582
583 // CPhipps - patch drawing updated
584 V_DrawNumPatch(160, 170, 0, lump+firstspritelump, CR_DEFAULT,
585 VPT_STRETCH | (flip ? VPT_FLIP : 0));
586}
587
588
589//
590// F_BunnyScroll
591//
592static const char pfub2[] = { "PFUB2" };
593static const char pfub1[] = { "PFUB1" };
594
595static void F_BunnyScroll (void)
596{
597 char name[10];
598 int stage;
599 static int laststage;
600
601 V_MarkRect (0, 0, SCREENWIDTH, SCREENHEIGHT);
602 {
603 int scrolled = 320 - (finalecount-230)/2;
604 if (scrolled <= 0) {
605 V_DrawNamePatch(0, 0, 0, pfub2, CR_DEFAULT, VPT_STRETCH);
606 } else if (scrolled >= 320) {
607 V_DrawNamePatch(0, 0, 0, pfub1, CR_DEFAULT, VPT_STRETCH);
608 } else {
609#define SCRN 2
610
611 int realscrolled = (SCREENWIDTH * scrolled) / 320;
612
613 V_AllocScreen(SCRN);
614 V_DrawNamePatch(0, 0, SCRN, pfub2, CR_DEFAULT, VPT_STRETCH);
615 V_CopyRect(realscrolled, 0, SCRN, SCREENWIDTH-realscrolled, SCREENHEIGHT, 0, 0, 0, VPT_NONE);
616 V_DrawNamePatch(0, 0, SCRN, pfub1, CR_DEFAULT, VPT_STRETCH);
617 V_CopyRect(0, 0, SCRN, realscrolled, SCREENHEIGHT, SCREENWIDTH-realscrolled, 0, 0, VPT_NONE);
618 V_FreeScreen(SCRN);
619 }
620 }
621
622 if (finalecount < 1130)
623 return;
624 if (finalecount < 1180)
625 {
626 // CPhipps - patch drawing updated
627 V_DrawNamePatch((320-13*8)/2, (200-8*8)/2,0, "END0", CR_DEFAULT, VPT_STRETCH);
628 laststage = 0;
629 return;
630 }
631
632 stage = (finalecount-1180) / 5;
633 if (stage > 6)
634 stage = 6;
635 if (stage > laststage)
636 {
637 S_StartSound (NULL, sfx_pistol);
638 laststage = stage;
639 }
640
641 snprintf (name,sizeof(name), "END%i",stage);
642 // CPhipps - patch drawing updated
643 V_DrawNamePatch((320-13*8)/2, (200-8*8)/2, 0, name, CR_DEFAULT, VPT_STRETCH);
644}
645
646
647//
648// F_Drawer
649//
650void F_Drawer (void)
651{
652 if (finalestage == 2)
653 {
654 F_CastDrawer ();
655 return;
656 }
657
658 if (!finalestage)
659 F_TextWrite ();
660 else
661 {
662 switch (gameepisode)
663 {
664 // CPhipps - patch drawing updated
665 case 1:
666 if ( gamemode == retail )
667 V_DrawNamePatch(0, 0, 0, "CREDIT", CR_DEFAULT, VPT_STRETCH);
668 else
669 V_DrawNamePatch(0, 0, 0, "HELP2", CR_DEFAULT, VPT_STRETCH);
670 break;
671 case 2:
672 V_DrawNamePatch(0, 0, 0, "VICTORY2", CR_DEFAULT, VPT_STRETCH);
673 break;
674 case 3:
675 F_BunnyScroll ();
676 break;
677 case 4:
678 V_DrawNamePatch(0, 0, 0, "ENDPIC", CR_DEFAULT, VPT_STRETCH);
679 break;
680 }
681 }
682}