* Onda VX747: add browse screen, pitchscreen, context menu, quickscreen, rewind...
[kugel-rb.git] / apps / plugins / doom / f_finale.c
blob0b912c823916a818bfd2f9fb0ae86a42bf83abe1
1 /* Emacs style mode select -*- C++ -*-
2 *-----------------------------------------------------------------------------
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
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.
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.
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.
27 * DESCRIPTION:
28 * Game completion, final screen animation.
30 *-----------------------------------------------------------------------------
33 #include "doomstat.h"
34 #include "d_event.h"
35 #include "m_swap.h"
36 #include "v_video.h"
37 #include "w_wad.h"
38 #include "s_sound.h"
39 #include "sounds.h"
40 #include "d_deh.h" // Ty 03/22/98 - externalizations
41 #include "f_finale.h" // CPhipps - hmm...
42 #include "rockmacros.h"
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
51 // defines for the end mission display text // phares
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
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.
63 void F_StartCast (void);
64 void F_CastTicker (void);
65 boolean F_CastResponder (event_t *ev);
66 void F_CastDrawer (void);
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"
73 // F_StartFinale
75 void F_StartFinale (void)
77 gameaction = ga_nothing;
78 gamestate = GS_FINALE;
79 automapmode &= ~am_active;
81 // killough 3/28/98: clear accelerative text flags
82 acceleratestage = midstage = 0;
84 // Okay - IWAD dependend stuff.
85 // This has been changed severly, and
86 // some stuff might have changed in the process.
87 switch ( gamemode )
90 // DOOM 1 - E1, E3 or E4, but each nine missions
91 case shareware:
92 case registered:
93 case retail:
95 S_ChangeMusic(mus_victor, true);
97 switch (gameepisode)
99 case 1:
100 finaleflat = bgflatE1; // Ty 03/30/98 - new externalized bg flats
101 finaletext = s_E1TEXT; // Ty 03/23/98 - Was e1text variable.
102 break;
103 case 2:
104 finaleflat = bgflatE2;
105 finaletext = s_E2TEXT; // Ty 03/23/98 - Same stuff for each
106 break;
107 case 3:
108 finaleflat = bgflatE3;
109 finaletext = s_E3TEXT;
110 break;
111 case 4:
112 finaleflat = bgflatE4;
113 finaletext = s_E4TEXT;
114 break;
115 default:
116 // Ouch.
117 break;
119 break;
122 // DOOM II and missions packs with E1, M34
123 case commercial:
125 S_ChangeMusic(mus_read_m, true);
127 // Ty 08/27/98 - added the gamemission logic
128 switch (gamemap)
130 case 6:
131 finaleflat = bgflat06;
132 finaletext = (gamemission==pack_tnt) ? s_T1TEXT :
133 (gamemission==pack_plut) ? s_P1TEXT : s_C1TEXT;
134 break;
135 case 11:
136 finaleflat = bgflat11;
137 finaletext = (gamemission==pack_tnt) ? s_T2TEXT :
138 (gamemission==pack_plut) ? s_P2TEXT : s_C2TEXT;
139 break;
140 case 20:
141 finaleflat = bgflat20;
142 finaletext = (gamemission==pack_tnt) ? s_T3TEXT :
143 (gamemission==pack_plut) ? s_P3TEXT : s_C3TEXT;
144 break;
145 case 30:
146 finaleflat = bgflat30;
147 finaletext = (gamemission==pack_tnt) ? s_T4TEXT :
148 (gamemission==pack_plut) ? s_P4TEXT : s_C4TEXT;
149 break;
150 case 15:
151 finaleflat = bgflat15;
152 finaletext = (gamemission==pack_tnt) ? s_T5TEXT :
153 (gamemission==pack_plut) ? s_P5TEXT : s_C5TEXT;
154 break;
155 case 31:
156 finaleflat = bgflat31;
157 finaletext = (gamemission==pack_tnt) ? s_T6TEXT :
158 (gamemission==pack_plut) ? s_P6TEXT : s_C6TEXT;
159 break;
160 default:
161 // Ouch.
162 break;
164 break;
165 // Ty 08/27/98 - end gamemission logic
168 // Indeterminate.
169 default: // Ty 03/30/98 - not externalized
170 S_ChangeMusic(mus_read_m, true);
171 finaleflat = "F_SKY1"; // Not used anywhere else.
172 finaletext = s_C1TEXT; // FIXME - other text, music?
173 break;
176 finalestage = 0;
177 finalecount = 0;
183 boolean F_Responder (event_t *event)
185 if (finalestage == 2)
186 return F_CastResponder (event);
188 return false;
191 // Get_TextSpeed() returns the value of the text display speed // phares
192 // Rewritten to allow user-directed acceleration -- killough 3/28/98
194 static float Get_TextSpeed(void)
196 return midstage ? NEWTEXTSPEED : (midstage=acceleratestage) ?
197 acceleratestage=0, NEWTEXTSPEED : TEXTSPEED;
201 // F_Ticker
203 // killough 3/28/98: almost totally rewritten, to use
204 // player-directed acceleration instead of constant delays.
205 // Now the player can accelerate the text display by using
206 // the fire/use keys while it is being printed. The delay
207 // automatically responds to the user, and gives enough
208 // time to read.
210 // killough 5/10/98: add back v1.9 demo compatibility
213 void F_Ticker(void)
215 int i;
216 if (!demo_compatibility)
217 WI_checkForAccelerate(); // killough 3/28/98: check for acceleration
218 else
219 if (gamemode == commercial && finalecount > 50) // check for skipping
220 for (i=0; i<MAXPLAYERS; i++)
221 if (players[i].cmd.buttons)
222 goto next_level; // go on to the next level
224 // advance animation
225 finalecount++;
227 if (finalestage == 2)
228 F_CastTicker();
230 if (!finalestage)
232 float speed = demo_compatibility ? TEXTSPEED : Get_TextSpeed();
233 /* killough 2/28/98: changed to allow acceleration */
234 if (finalecount > strlen(finaletext)*speed +
235 (midstage ? NEWTEXTWAIT : TEXTWAIT) ||
236 (midstage && acceleratestage)) {
237 if (gamemode != commercial) // Doom 1 / Ultimate Doom episode end
238 { // with enough time, it's automatic
239 finalecount = 0;
240 finalestage = 1;
241 wipegamestate = -1; // force a wipe
242 if (gameepisode == 3)
243 S_StartMusic(mus_bunny);
245 else // you must press a button to continue in Doom 2
246 if (!demo_compatibility && midstage)
248 next_level:
249 if (gamemap == 30)
250 F_StartCast(); // cast of Doom 2 characters
251 else
252 gameaction = ga_worlddone; // next level, e.g. MAP07
260 // F_TextWrite
262 // This program displays the background and text at end-mission // phares
263 // text time. It draws both repeatedly so that other displays, // |
264 // like the main menu, can be drawn over it dynamically and // V
265 // erased dynamically. The TEXTSPEED constant is changed into
266 // the Get_TextSpeed function so that the speed of writing the // ^
267 // text can be increased, and there's still time to read what's // |
268 // written. // phares
269 // CPhipps - reformatted
271 #include "hu_stuff.h"
272 extern patchnum_t hu_font[HU_FONTSIZE];
275 void F_TextWrite (void)
277 V_DrawBackground(finaleflat, 0);
278 { // draw some of the text onto the screen
279 int cx = 10;
280 int cy = 10;
281 const char* ch = finaletext; // CPhipps - const
282 int count = (int)((float)(finalecount - 10)/Get_TextSpeed()); // phares
283 int w;
285 if (count < 0)
286 count = 0;
288 for ( ; count ; count-- ) {
289 int c = *ch++;
291 if (!c)
292 break;
293 if (c == '\n') {
294 cx = 10;
295 cy += 11;
296 continue;
299 c = toupper(c) - HU_FONTSTART;
300 if (c < 0 || c> HU_FONTSIZE) {
301 cx += 4;
302 continue;
305 w = SHORT (hu_font[c].width);
306 if (cx+w > 320)
307 break;
308 // CPhipps - patch drawing updated
309 V_DrawNumPatch(cx, cy, 0, hu_font[c].lumpnum, CR_DEFAULT, VPT_STRETCH);
310 cx+=w;
316 // Final DOOM 2 animation
317 // Casting by id Software.
318 // in order of appearance
320 typedef struct
322 const char **name; // CPhipps - const**
323 mobjtype_t type;
324 } castinfo_t;
326 #define MAX_CASTORDER 18 /* Ty - hard coded for now */
327 static const castinfo_t castorder[] = { // CPhipps - static const, initialised here
328 { &s_CC_ZOMBIE, MT_POSSESSED },
329 { &s_CC_SHOTGUN, MT_SHOTGUY },
330 { &s_CC_HEAVY, MT_CHAINGUY },
331 { &s_CC_IMP, MT_TROOP },
332 { &s_CC_DEMON, MT_SERGEANT },
333 { &s_CC_LOST, MT_SKULL },
334 { &s_CC_CACO, MT_HEAD },
335 { &s_CC_HELL, MT_KNIGHT },
336 { &s_CC_BARON, MT_BRUISER },
337 { &s_CC_ARACH, MT_BABY },
338 { &s_CC_PAIN, MT_PAIN },
339 { &s_CC_REVEN, MT_UNDEAD },
340 { &s_CC_MANCU, MT_FATSO },
341 { &s_CC_ARCH, MT_VILE },
342 { &s_CC_SPIDER, MT_SPIDER },
343 { &s_CC_CYBER, MT_CYBORG },
344 { &s_CC_HERO, MT_PLAYER },
345 { NULL, 0}
348 int castnum;
349 int casttics;
350 state_t* caststate;
351 boolean castdeath;
352 int castframes;
353 int castonmelee;
354 boolean castattacking;
358 // F_StartCast
360 extern gamestate_t wipegamestate;
362 void F_StartCast (void)
364 wipegamestate = -1; // force a screen wipe
365 castnum = 0;
366 caststate = &states[mobjinfo[castorder[castnum].type].seestate];
367 casttics = caststate->tics;
368 castdeath = false;
369 finalestage = 2;
370 castframes = 0;
371 castonmelee = 0;
372 castattacking = false;
373 S_ChangeMusic(mus_evil, true);
378 // F_CastTicker
380 void F_CastTicker (void)
382 int st;
383 int sfx;
385 if (--casttics > 0)
386 return; // not time to change state yet
388 if (caststate->tics == -1 || caststate->nextstate == S_NULL)
390 // switch from deathstate to next monster
391 castnum++;
392 castdeath = false;
393 if (castorder[castnum].name == NULL)
394 castnum = 0;
395 if (mobjinfo[castorder[castnum].type].seesound)
396 S_StartSound (NULL, mobjinfo[castorder[castnum].type].seesound);
397 caststate = &states[mobjinfo[castorder[castnum].type].seestate];
398 castframes = 0;
400 else
402 // just advance to next state in animation
403 if (caststate == &states[S_PLAY_ATK1])
404 goto stopattack; // Oh, gross hack!
405 st = caststate->nextstate;
406 caststate = &states[st];
407 castframes++;
409 // sound hacks....
410 switch (st)
412 case S_PLAY_ATK1: sfx = sfx_dshtgn; break;
413 case S_POSS_ATK2: sfx = sfx_pistol; break;
414 case S_SPOS_ATK2: sfx = sfx_shotgn; break;
415 case S_VILE_ATK2: sfx = sfx_vilatk; break;
416 case S_SKEL_FIST2: sfx = sfx_skeswg; break;
417 case S_SKEL_FIST4: sfx = sfx_skepch; break;
418 case S_SKEL_MISS2: sfx = sfx_skeatk; break;
419 case S_FATT_ATK8:
420 case S_FATT_ATK5:
421 case S_FATT_ATK2: sfx = sfx_firsht; break;
422 case S_CPOS_ATK2:
423 case S_CPOS_ATK3:
424 case S_CPOS_ATK4: sfx = sfx_shotgn; break;
425 case S_TROO_ATK3: sfx = sfx_claw; break;
426 case S_SARG_ATK2: sfx = sfx_sgtatk; break;
427 case S_BOSS_ATK2:
428 case S_BOS2_ATK2:
429 case S_HEAD_ATK2: sfx = sfx_firsht; break;
430 case S_SKULL_ATK2: sfx = sfx_sklatk; break;
431 case S_SPID_ATK2:
432 case S_SPID_ATK3: sfx = sfx_shotgn; break;
433 case S_BSPI_ATK2: sfx = sfx_plasma; break;
434 case S_CYBER_ATK2:
435 case S_CYBER_ATK4:
436 case S_CYBER_ATK6: sfx = sfx_rlaunc; break;
437 case S_PAIN_ATK3: sfx = sfx_sklatk; break;
438 default: sfx = 0; break;
441 if (sfx)
442 S_StartSound (NULL, sfx);
445 if (castframes == 12)
447 // go into attack frame
448 castattacking = true;
449 if (castonmelee)
450 caststate=&states[mobjinfo[castorder[castnum].type].meleestate];
451 else
452 caststate=&states[mobjinfo[castorder[castnum].type].missilestate];
453 castonmelee ^= 1;
454 if (caststate == &states[S_NULL])
456 if (castonmelee)
457 caststate=
458 &states[mobjinfo[castorder[castnum].type].meleestate];
459 else
460 caststate=
461 &states[mobjinfo[castorder[castnum].type].missilestate];
465 if (castattacking)
467 if (castframes == 24
468 || caststate == &states[mobjinfo[castorder[castnum].type].seestate] )
470 stopattack:
471 castattacking = false;
472 castframes = 0;
473 caststate = &states[mobjinfo[castorder[castnum].type].seestate];
477 casttics = caststate->tics;
478 if (casttics == -1)
479 casttics = 15;
484 // F_CastResponder
487 boolean F_CastResponder (event_t* ev)
489 if (ev->type != ev_keydown)
490 return false;
492 if (castdeath)
493 return true; // already in dying frames
495 // go into death frame
496 castdeath = true;
497 caststate = &states[mobjinfo[castorder[castnum].type].deathstate];
498 casttics = caststate->tics;
499 castframes = 0;
500 castattacking = false;
501 if (mobjinfo[castorder[castnum].type].deathsound)
502 S_StartSound (NULL, mobjinfo[castorder[castnum].type].deathsound);
504 return true;
508 static void F_CastPrint (const char* text) // CPhipps - static, const char*
510 const char* ch; // CPhipps - const
511 int c;
512 int cx;
513 int w;
514 int width;
516 // find width
517 ch = text;
518 width = 0;
520 while (ch)
522 c = *ch++;
523 if (!c)
524 break;
525 c = toupper(c) - HU_FONTSTART;
526 if (c < 0 || c> HU_FONTSIZE)
528 width += 4;
529 continue;
532 w = SHORT (hu_font[c].width);
533 width += w;
536 // draw it
537 cx = 160-width/2;
538 ch = text;
539 while (ch)
541 c = *ch++;
542 if (!c)
543 break;
544 c = toupper(c) - HU_FONTSTART;
545 if (c < 0 || c> HU_FONTSIZE)
547 cx += 4;
548 continue;
551 w = SHORT (hu_font[c].width);
552 // CPhipps - patch drawing updated
553 V_DrawNumPatch(cx, 180, 0, hu_font[c].lumpnum, CR_DEFAULT, VPT_STRETCH);
554 cx+=w;
560 // F_CastDrawer
563 void F_CastDrawer (void)
565 spritedef_t* sprdef;
566 spriteframe_t* sprframe;
567 int lump;
568 boolean flip;
570 // erase the entire screen to a background
571 // CPhipps - patch drawing updated
572 V_DrawNamePatch(0,0,0, bgcastcall, CR_DEFAULT, VPT_STRETCH); // Ty 03/30/98 bg texture extern
574 F_CastPrint (*(castorder[castnum].name));
576 // draw the current frame in the middle of the screen
577 sprdef = &sprites[caststate->sprite];
578 sprframe = &sprdef->spriteframes[ caststate->frame & FF_FRAMEMASK];
579 lump = sprframe->lump[0];
580 flip = (boolean)sprframe->flip[0];
582 // CPhipps - patch drawing updated
583 V_DrawNumPatch(160, 170, 0, lump+firstspritelump, CR_DEFAULT,
584 VPT_STRETCH | (flip ? VPT_FLIP : 0));
589 // F_BunnyScroll
591 static const char pfub2[] = { "PFUB2" };
592 static const char pfub1[] = { "PFUB1" };
594 static void F_BunnyScroll (void)
596 char name[10];
597 int stage;
598 static int laststage;
600 V_MarkRect (0, 0, SCREENWIDTH, SCREENHEIGHT);
602 int scrolled = 320 - (finalecount-230)/2;
603 if (scrolled <= 0) {
604 V_DrawNamePatch(0, 0, 0, pfub2, CR_DEFAULT, VPT_STRETCH);
605 } else if (scrolled >= 320) {
606 V_DrawNamePatch(0, 0, 0, pfub1, CR_DEFAULT, VPT_STRETCH);
607 } else {
608 #define SCRN 2
610 int realscrolled = (SCREENWIDTH * scrolled) / 320;
612 V_AllocScreen(SCRN);
613 V_DrawNamePatch(0, 0, SCRN, pfub2, CR_DEFAULT, VPT_STRETCH);
614 V_CopyRect(realscrolled, 0, SCRN, SCREENWIDTH-realscrolled, SCREENHEIGHT, 0, 0, 0, VPT_NONE);
615 V_DrawNamePatch(0, 0, SCRN, pfub1, CR_DEFAULT, VPT_STRETCH);
616 V_CopyRect(0, 0, SCRN, realscrolled, SCREENHEIGHT, SCREENWIDTH-realscrolled, 0, 0, VPT_NONE);
617 V_FreeScreen(SCRN);
621 if (finalecount < 1130)
622 return;
623 if (finalecount < 1180)
625 // CPhipps - patch drawing updated
626 V_DrawNamePatch((320-13*8)/2, (200-8*8)/2,0, "END0", CR_DEFAULT, VPT_STRETCH);
627 laststage = 0;
628 return;
631 stage = (finalecount-1180) / 5;
632 if (stage > 6)
633 stage = 6;
634 if (stage > laststage)
636 S_StartSound (NULL, sfx_pistol);
637 laststage = stage;
640 snprintf (name,sizeof(name), "END%d",stage);
641 // CPhipps - patch drawing updated
642 V_DrawNamePatch((320-13*8)/2, (200-8*8)/2, 0, name, CR_DEFAULT, VPT_STRETCH);
647 // F_Drawer
649 void F_Drawer (void)
651 if (finalestage == 2)
653 F_CastDrawer ();
654 return;
657 if (!finalestage)
658 F_TextWrite ();
659 else
661 switch (gameepisode)
663 // CPhipps - patch drawing updated
664 case 1:
665 if ( gamemode == retail )
666 V_DrawNamePatch(0, 0, 0, "CREDIT", CR_DEFAULT, VPT_STRETCH);
667 else
668 V_DrawNamePatch(0, 0, 0, "HELP2", CR_DEFAULT, VPT_STRETCH);
669 break;
670 case 2:
671 V_DrawNamePatch(0, 0, 0, "VICTORY2", CR_DEFAULT, VPT_STRETCH);
672 break;
673 case 3:
674 F_BunnyScroll ();
675 break;
676 case 4:
677 V_DrawNamePatch(0, 0, 0, "ENDPIC", CR_DEFAULT, VPT_STRETCH);
678 break;