1 // Emacs style mode select -*- C++ -*-
2 //-----------------------------------------------------------------------------
3 //
4 // $Id: f_finale.cpp 4542 2014-02-09 17:39:42Z dr_sean $
5 //
6 // Copyright (C) 1998-2006 by Randy Heit (ZDoom).
7 // Copyright (C) 2006-2014 by The Odamex Team.
8 //
9 // This program is free software; you can redistribute it and/or
10 // modify it under the terms of the GNU General Public License
11 // as published by the Free Software Foundation; either version 2
12 // of the License, or (at your option) any later version.
13 //
14 // This program is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 // GNU General Public License for more details.
18 //
19 // DESCRIPTION:
20 // Game completion, final screen animation.
21 //
22 //-----------------------------------------------------------------------------
23
24
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <ctype.h>
28 #include <math.h>
29
30 #include "i_system.h"
31 #include "m_swap.h"
32 #include "z_zone.h"
33 #include "v_video.h"
34 #include "v_text.h"
35 #include "w_wad.h"
36 #include "s_sound.h"
37 #include "gstrings.h"
38 #include "doomstat.h"
39 #include "r_state.h"
40 #include "hu_stuff.h"
41
42 #include "gi.h"
43
44 // Stage of animation:
45 // 0 = text, 1 = art screen, 2 = character cast
46 unsigned int finalestage;
47
48 int finalecount;
49
50 #define TEXTSPEED 2
51 #define TEXTWAIT 250
52
53 static const char* finaletext;
54 static const char* finaleflat;
55
56 void F_StartCast (void);
57 void F_CastTicker (void);
58 BOOL F_CastResponder (event_t *ev);
59 void F_CastDrawer (void);
60
61 //
62 // F_StartFinale
63 //
F_StartFinale(char * music,char * flat,const char * text)64 void F_StartFinale (char *music, char *flat, const char *text)
65 {
66 gameaction = ga_nothing;
67 gamestate = GS_FINALE;
68 viewactive = false;
69
70 // Okay - IWAD dependend stuff.
71 // This has been changed severly, and
72 // some stuff might have changed in the process.
73 // [RH] More flexible now (even more severe changes)
74 // finaleflat, finaletext, and music are now
75 // determined in G_WorldDone() based on data in
76 // a level_info_t and a cluster_info_t.
77
78 if (*music == 0)
79 S_ChangeMusic (std::string(gameinfo.finaleMusic, 8),
80 !(gameinfo.flags & GI_NOLOOPFINALEMUSIC));
81 else
82 S_ChangeMusic (std::string(music, 8), !(gameinfo.flags & GI_NOLOOPFINALEMUSIC));
83
84 if (*flat == 0)
85 finaleflat = gameinfo.finaleFlat;
86 else
87 finaleflat = flat;
88
89 if (text)
90 finaletext = text;
91 else
92 finaletext = "Empty message";
93
94 finalestage = 0;
95 finalecount = 0;
96 V_SetBlend (0,0,0,0);
97 S_StopAllChannels ();
98 }
99
100
101
F_Responder(event_t * event)102 BOOL F_Responder (event_t *event)
103 {
104 if (finalestage == 2)
105 return F_CastResponder (event);
106
107 return false;
108 }
109
110
111 //
112 // F_Ticker
113 //
F_Ticker(void)114 void F_Ticker (void)
115 {
116 // denis - do this serverside only
117 // check for skipping
118 // [RH] Non-commercial can be skipped now, too
119 if (serverside && finalecount > 50 && finalestage != 1) {
120 // go on to the next level
121 // [RH] or just reveal the entire message if we're still ticking it
122 Players::iterator it = players.begin();
123 for (;it != players.end();++it)
124 if (it->cmd.buttons)
125 break;
126
127 if (it != players.end())
128 {
129 /*if (finalecount < (signed)(strlen (finaletext)*TEXTSPEED))
130 {
131 finalecount = strlen (finaletext)*TEXTSPEED;
132 }
133 else
134 {*/
135 if (!strncmp (level.nextmap, "EndGame", 7) ||
136 (gamemode == retail_chex && !strncmp (level.nextmap, "E1M6", 4))) // [ML] Chex mode: game is over
137 { // after E1M5
138 if (level.nextmap[7] == 'C')
139 {
140 F_StartCast ();
141 }
142 else
143 {
144 finalecount = 0;
145 finalestage = 1;
146 wipegamestate = GS_FORCEWIPE;
147 if (level.nextmap[7] == '3')
148 S_StartMusic ("d_bunny");
149 }
150 }
151 else
152 {
153 gameaction = ga_worlddone;
154 }
155 //}
156 }
157 }
158
159 // advance animation
160 finalecount++;
161
162 if (finalestage == 2)
163 {
164 F_CastTicker ();
165 return;
166 }
167 }
168
169
170
171 //
172 // F_TextWrite
173 //
174 extern patch_t *hu_font[HU_FONTSIZE];
175
176
F_TextWrite(void)177 void F_TextWrite (void)
178 {
179 int w;
180 int count;
181 const char* ch;
182 int c;
183 int cx;
184 int cy;
185
186 // erase the entire screen to a tiled background
187 {
188 int lump = W_CheckNumForName (finaleflat, ns_flats);
189 if (lump >= 0)
190 {
191 screen->FlatFill (0,0, screen->width, screen->height,
192 (byte *)W_CacheLumpNum (lump, PU_CACHE));
193 }
194 else
195 {
196 screen->Clear (0, 0, screen->width, screen->height, 0);
197 }
198 }
199 V_MarkRect (0, 0, screen->width, screen->height);
200
201 // draw some of the text onto the screen
202 cx = 10;
203 cy = 10;
204 ch = finaletext;
205
206 if (finalecount < 11)
207 return;
208
209 count = (finalecount - 10)/TEXTSPEED;
210 for ( ; count ; count-- )
211 {
212 c = *ch++;
213 if (!c)
214 break;
215 if (c == '\n')
216 {
217 cx = 10;
218 cy += 11;
219 continue;
220 }
221
222 c = toupper(c) - HU_FONTSTART;
223 if (c < 0 || c> HU_FONTSIZE)
224 {
225 cx += 4;
226 continue;
227 }
228
229 w = hu_font[c]->width();
230 if (cx+w > screen->width)
231 break;
232 screen->DrawPatchClean (hu_font[c], cx, cy);
233 cx+=w;
234 }
235
236 }
237
238 //
239 // Final DOOM 2 animation
240 // Casting by id Software.
241 // in order of appearance
242 //
243 typedef struct
244 {
245 const char *name;
246 mobjtype_t type;
247 } castinfo_t;
248
249 castinfo_t castorder[] = {
250 {NULL, MT_POSSESSED},
251 {NULL, MT_SHOTGUY},
252 {NULL, MT_CHAINGUY},
253 {NULL, MT_TROOP},
254 {NULL, MT_SERGEANT},
255 {NULL, MT_SKULL},
256 {NULL, MT_HEAD},
257 {NULL, MT_KNIGHT},
258 {NULL, MT_BRUISER},
259 {NULL, MT_BABY},
260 {NULL, MT_PAIN},
261 {NULL, MT_UNDEAD},
262 {NULL, MT_FATSO},
263 {NULL, MT_VILE},
264 {NULL, MT_SPIDER},
265 {NULL, MT_CYBORG},
266 {NULL, MT_PLAYER},
267
268 {NULL, MT_UNKNOWNTHING}
269 };
270
271 static int castnum;
272 static int casttics;
273 static int castsprite;
274 static state_t* caststate;
275 static BOOL castdeath;
276 static int castframes;
277 static int castonmelee;
278 static BOOL castattacking;
279
280
281 //
282 // F_StartCast
283 //
284 extern gamestate_t wipegamestate;
285
286
F_StartCast(void)287 void F_StartCast (void)
288 {
289 // [RH] Set the names for the cast
290 castorder[0].name = GStrings(CC_ZOMBIE);
291 castorder[1].name = GStrings(CC_SHOTGUN);
292 castorder[2].name = GStrings(CC_HEAVY);
293 castorder[3].name = GStrings(CC_IMP);
294 castorder[4].name = GStrings(CC_DEMON);
295 castorder[5].name = GStrings(CC_LOST);
296 castorder[6].name = GStrings(CC_CACO);
297 castorder[7].name = GStrings(CC_HELL);
298 castorder[8].name = GStrings(CC_BARON);
299 castorder[9].name = GStrings(CC_ARACH);
300 castorder[10].name = GStrings(CC_PAIN);
301 castorder[11].name = GStrings(CC_REVEN);
302 castorder[12].name = GStrings(CC_MANCU);
303 castorder[13].name = GStrings(CC_ARCH);
304 castorder[14].name = GStrings(CC_SPIDER);
305 castorder[15].name = GStrings(CC_CYBER);
306 castorder[16].name = GStrings(CC_HERO);
307
308 wipegamestate = GS_FORCEWIPE;
309 castnum = 0;
310 caststate = &states[mobjinfo[castorder[castnum].type].seestate];
311 castsprite = caststate->sprite;
312 casttics = caststate->tics;
313 castdeath = false;
314 finalestage = 2;
315 castframes = 0;
316 castonmelee = 0;
317 castattacking = false;
318 S_ChangeMusic("d_evil", true);
319 }
320
321
322 //
323 // F_CastTicker
324 //
F_CastTicker(void)325 void F_CastTicker (void)
326 {
327 int st;
328 int atten;
329
330 if (--casttics > 0)
331 return; // not time to change state yet
332
333 if (caststate->tics == -1 || caststate->nextstate == S_NULL)
334 {
335 // switch from deathstate to next monster
336 castnum++;
337 castdeath = false;
338 if (castorder[castnum].name == NULL)
339 castnum = 0;
340 if (mobjinfo[castorder[castnum].type].seesound) {
341 atten = ATTN_NONE;
342 S_Sound (CHAN_VOICE, mobjinfo[castorder[castnum].type].seesound, 1, atten);
343 }
344 caststate = &states[mobjinfo[castorder[castnum].type].seestate];
345 castsprite = caststate->sprite;
346 castframes = 0;
347 }
348 else
349 {
350 const char *sfx;
351
352 // just advance to next state in animation
353 if (caststate == &states[S_PLAY_ATK1])
354 goto stopattack; // Oh, gross hack!
355 st = caststate->nextstate;
356 caststate = &states[st];
357 castframes++;
358
359 // sound hacks....
360 switch (st)
361 {
362 case S_PLAY_ATK1: sfx = "weapons/sshotf"; break;
363 case S_POSS_ATK2: sfx = "grunt/attack"; break;
364 case S_SPOS_ATK2: sfx = "shotguy/attack"; break;
365 case S_VILE_ATK2: sfx = "vile/start"; break;
366 case S_SKEL_FIST2: sfx = "skeleton/swing"; break;
367 case S_SKEL_FIST4: sfx = "skeleton/melee"; break;
368 case S_SKEL_MISS2: sfx = "skeleton/attack"; break;
369 case S_FATT_ATK8:
370 case S_FATT_ATK5:
371 case S_FATT_ATK2: sfx = "fatso/attack"; break;
372 case S_CPOS_ATK2:
373 case S_CPOS_ATK3:
374 case S_CPOS_ATK4: sfx = "chainguy/attack"; break;
375 case S_TROO_ATK3: sfx = "imp/attack"; break;
376 case S_SARG_ATK2: sfx = "demon/melee"; break;
377 case S_BOSS_ATK2:
378 case S_BOS2_ATK2:
379 case S_HEAD_ATK2: sfx = "caco/attack"; break;
380 case S_SKULL_ATK2: sfx = "skull/melee"; break;
381 case S_SPID_ATK2:
382 case S_SPID_ATK3: sfx = "spider/attack"; break;
383 case S_BSPI_ATK2: sfx = "baby/attack"; break;
384 case S_CYBER_ATK2:
385 case S_CYBER_ATK4:
386 case S_CYBER_ATK6: sfx = "weapons/rocklf"; break;
387 case S_PAIN_ATK3: sfx = "skull/melee"; break;
388 default: sfx = 0; break;
389 }
390
391 if (sfx) {
392 S_StopAllChannels ();
393 S_Sound (CHAN_WEAPON, sfx, 1, ATTN_NONE);
394 }
395 }
396
397 if (castframes == 12)
398 {
399 // go into attack frame
400 castattacking = true;
401 if (castonmelee)
402 caststate=&states[mobjinfo[castorder[castnum].type].meleestate];
403 else
404 caststate=&states[mobjinfo[castorder[castnum].type].missilestate];
405 castonmelee ^= 1;
406 if (caststate == &states[S_NULL])
407 {
408 if (castonmelee)
409 caststate=
410 &states[mobjinfo[castorder[castnum].type].meleestate];
411 else
412 caststate=
413 &states[mobjinfo[castorder[castnum].type].missilestate];
414 }
415 }
416
417 if (castattacking)
418 {
419 if (castframes == 24
420 || caststate == &states[mobjinfo[castorder[castnum].type].seestate] )
421 {
422 stopattack:
423 castattacking = false;
424 castframes = 0;
425 caststate = &states[mobjinfo[castorder[castnum].type].seestate];
426 }
427 }
428
429 casttics = caststate->tics;
430 if (casttics == -1)
431 casttics = 15;
432 }
433
434
435 //
436 // F_CastResponder
437 //
438
F_CastResponder(event_t * ev)439 BOOL F_CastResponder (event_t* ev)
440 {
441 if (ev->type != ev_keydown)
442 return false;
443
444 if (castdeath)
445 return true; // already in dying frames
446
447 // go into death frame
448 castdeath = true;
449 caststate = &states[mobjinfo[castorder[castnum].type].deathstate];
450 casttics = caststate->tics;
451 castframes = 0;
452 castattacking = false;
453 if (mobjinfo[castorder[castnum].type].deathsound)
454 S_Sound (CHAN_VOICE, mobjinfo[castorder[castnum].type].deathsound, 1, ATTN_NONE);
455
456 return true;
457 }
458
459 //
460 // F_CastDrawer
461 //
F_CastDrawer(void)462 void F_CastDrawer (void)
463 {
464 spritedef_t* sprdef;
465 spriteframe_t* sprframe;
466 int lump;
467 BOOL flip;
468 patch_t* patch;
469
470 // erase the entire screen to a background
471 screen->DrawPatchIndirect (W_CachePatch ("BOSSBACK"), 0, 0);
472
473 screen->DrawTextClean (CR_RED,
474 (screen->width - V_StringWidth (castorder[castnum].name) * CleanXfac)/2,
475 (screen->height * 180) / 200, castorder[castnum].name);
476
477 // draw the current frame in the middle of the screen
478 sprdef = &sprites[castsprite];
479 sprframe = &sprdef->spriteframes[caststate->frame & FF_FRAMEMASK];
480 lump = sprframe->lump[0];
481 flip = (BOOL)sprframe->flip[0];
482
483 patch = W_CachePatch (lump);
484 if (flip)
485 screen->DrawPatchFlipped (patch, 160, 170);
486 else
487 screen->DrawPatchIndirect (patch, 160, 170);
488 }
489
490
491 //
492 // F_DrawPatchCol
493 //
494
495 // Palettized version 8bpp
496
F_DrawPatchColP(int x,const patch_t * patch,int col,const DCanvas * scrn)497 void F_DrawPatchColP (int x, const patch_t *patch, int col, const DCanvas *scrn)
498 {
499 byte* source;
500 byte* dest;
501 byte* desttop;
502 unsigned count;
503 int repeat;
504 int c;
505 unsigned step;
506 unsigned invstep;
507 float mul;
508 float fx;
509 byte p;
510 int pitch;
511
512 // [RH] figure out how many times to repeat this column
513 // (for screens wider than 320 pixels)
514 mul = scrn->width / (float)320;
515 fx = (float)x;
516 repeat = (int)(floor (mul*(fx+1)) - floor(mul*fx));
517 if (repeat == 0)
518 return;
519
520 // [RH] Remap virtual-x to real-x
521 x = (int)floor (mul*x);
522
523 // [RH] Figure out per-row fixed-point step
524 step = (200<<16) / scrn->height;
525 invstep = (scrn->height<<16) / 200;
526
527 tallpost_t *post = (tallpost_t *)((byte *)patch + LELONG(patch->columnofs[col]));
528 desttop = scrn->buffer + x;
529 pitch = scrn->pitch;
530
531 // step through the posts in a column
532 while (!post->end())
533 {
534 source = post->data();
535 dest = desttop + ((post->topdelta*invstep)>>16)*pitch;
536 count = (post->length * invstep) >> 16;
537 c = 0;
538
539 switch (repeat) {
540 case 1:
541 do {
542 *dest = source[c>>16];
543 dest += pitch;
544 c += step;
545 } while (--count);
546 break;
547 case 2:
548 do {
549 p = source[c>>16];
550 dest[0] = p;
551 dest[1] = p;
552 dest += pitch;
553 c += step;
554 } while (--count);
555 break;
556 case 3:
557 do {
558 p = source[c>>16];
559 dest[0] = p;
560 dest[1] = p;
561 dest[2] = p;
562 dest += pitch;
563 c += step;
564 } while (--count);
565 break;
566 case 4:
567 do {
568 p = source[c>>16];
569 dest[0] = p;
570 dest[1] = p;
571 dest[2] = p;
572 dest[3] = p;
573 dest += pitch;
574 c += step;
575 } while (--count);
576 break;
577 default:
578 {
579 int count2;
580
581 do {
582 p = source[c>>16];
583 for (count2 = repeat; count2; count2--) {
584 dest[count2] = p;
585 }
586 dest += pitch;
587 c += step;
588 } while (--count);
589 }
590 break;
591 }
592
593 post = post->next();
594 }
595 }
596
597 // Direct version 32bpp:
598
F_DrawPatchColD(int x,const patch_t * patch,int col,const DCanvas * scrn)599 void F_DrawPatchColD (int x, const patch_t *patch, int col, const DCanvas *scrn)
600 {
601 byte* source;
602 argb_t* dest;
603 argb_t* desttop;
604 unsigned count;
605 int repeat;
606 int c;
607 unsigned step;
608 unsigned invstep;
609 float mul;
610 float fx;
611 argb_t p;
612 int pitch;
613
614 // [RH] figure out how many times to repeat this column
615 // (for screens wider than 320 pixels)
616 mul = scrn->width / (float)320;
617 fx = (float)x;
618 repeat = (int)(floor (mul*(fx+1)) - floor(mul*fx));
619 if (repeat == 0)
620 return;
621
622 // [RH] Remap virtual-x to real-x
623 x = (int)floor (mul*x);
624
625 // [RH] Figure out per-row fixed-point step
626 step = (200<<16) / scrn->height;
627 invstep = (scrn->height<<16) / 200;
628
629 tallpost_t *post = (tallpost_t *)((byte *)patch + LELONG(patch->columnofs[col]));
630 desttop = (argb_t *)scrn->buffer + x;
631 pitch = scrn->pitch / sizeof(argb_t);
632
633 shaderef_t pal = shaderef_t(&GetDefaultPalette()->maps, 0);
634
635 // step through the posts in a column
636 while (!post->end())
637 {
638 source = post->data();
639 dest = desttop + ((post->topdelta*invstep)>>16)*pitch;
640 count = (post->length * invstep) >> 16;
641 c = 0;
642
643 switch (repeat) {
644 case 1:
645 do {
646 *dest = pal.shade(source[c>>16]);
647 dest += pitch;
648 c += step;
649 } while (--count);
650 break;
651 case 2:
652 do {
653 p = pal.shade(source[c>>16]);
654 dest[0] = p;
655 dest[1] = p;
656 dest += pitch;
657 c += step;
658 } while (--count);
659 break;
660 case 3:
661 do {
662 p = pal.shade(source[c>>16]);
663 dest[0] = p;
664 dest[1] = p;
665 dest[2] = p;
666 dest += pitch;
667 c += step;
668 } while (--count);
669 break;
670 case 4:
671 do {
672 p = pal.shade(source[c>>16]);
673 dest[0] = p;
674 dest[1] = p;
675 dest[2] = p;
676 dest[3] = p;
677 dest += pitch;
678 c += step;
679 } while (--count);
680 break;
681 default:
682 {
683 int count2;
684
685 do {
686 p = pal.shade(source[c>>16]);
687 for (count2 = repeat; count2; count2--) {
688 dest[count2] = p;
689 }
690 dest += pitch;
691 c += step;
692 } while (--count);
693 }
694 break;
695 }
696
697 post = post->next();
698 }
699 }
700
701
702 //
703 // F_BunnyScroll
704 //
F_BunnyScroll(void)705 void F_BunnyScroll (void)
706 {
707 int scrolled;
708 int x;
709 patch_t* p1;
710 patch_t* p2;
711 char name[10];
712 int stage;
713 static int laststage;
714
715 p1 = W_CachePatch ("PFUB2");
716 p2 = W_CachePatch ("PFUB1");
717
718 V_MarkRect (0, 0, screen->width, screen->height);
719
720 scrolled = 320 - (finalecount-230)/2;
721 if (scrolled > 320)
722 scrolled = 320;
723 if (scrolled < 0)
724 scrolled = 0;
725
726 if (screen->is8bit())
727 {
728 for ( x=0 ; x<320 ; x++)
729 {
730 if (x+scrolled < 320)
731 F_DrawPatchColP (x, p1, x+scrolled, screen);
732 else
733 F_DrawPatchColP (x, p2, x+scrolled - 320, screen);
734 }
735 }
736 else
737 {
738 for ( x=0 ; x<320 ; x++)
739 {
740 if (x+scrolled < 320)
741 F_DrawPatchColD (x, p1, x+scrolled, screen);
742 else
743 F_DrawPatchColD (x, p2, x+scrolled - 320, screen);
744 }
745 }
746
747 if (finalecount < 1130)
748 return;
749 if (finalecount < 1180)
750 {
751 screen->DrawPatchIndirect (W_CachePatch ("END0"),
752 (320-13*8)/2, (200-8*8)/2);
753 laststage = 0;
754 return;
755 }
756
757 stage = (finalecount-1180) / 5;
758 if (stage > 6)
759 stage = 6;
760 if (stage > laststage)
761 {
762 S_Sound (CHAN_WEAPON, "weapons/pistol", 1, ATTN_NONE);
763 laststage = stage;
764 }
765
766 sprintf (name,"END%i",stage);
767 screen->DrawPatchIndirect (W_CachePatch (name),
768 (320-13*8)/2, (200-8*8)/2);
769 }
770
771
772 //
773 // F_Drawer
774 //
F_Drawer(void)775 void F_Drawer (void)
776 {
777 switch (finalestage)
778 {
779 case 0:
780 F_TextWrite ();
781 break;
782
783 case 1:
784 switch (level.nextmap[7])
785 {
786 default:
787 case '1':
788 screen->DrawPatchIndirect (W_CachePatch (gameinfo.finalePage1), 0, 0);
789 break;
790 case '2':
791 screen->DrawPatchIndirect (W_CachePatch (gameinfo.finalePage2), 0, 0);
792 break;
793 case '3':
794 F_BunnyScroll ();
795 break;
796 case '4':
797 screen->DrawPatchIndirect (W_CachePatch (gameinfo.finalePage3), 0, 0);
798 break;
799 }
800 break;
801
802 case 2:
803 F_CastDrawer ();
804 break;
805 }
806 }
807
808 VERSION_CONTROL (f_finale_cpp, "$Id: f_finale.cpp 4542 2014-02-09 17:39:42Z dr_sean $")
809
810