|
|
1.1.1.3 root 1: // Emacs style mode select -*- C++ -*-
2: //-----------------------------------------------------------------------------
3: //
4: // $Id:$
5: //
6: // Copyright (C) 1993-1996 by id Software, Inc.
7: //
1.1.1.4 ! root 8: // This program is free software; you can redistribute it and/or
! 9: // modify it under the terms of the GNU General Public License
! 10: // as published by the Free Software Foundation; either version 2
! 11: // of the License, or (at your option) any later version.
1.1.1.3 root 12: //
1.1.1.4 ! root 13: // This program is distributed in the hope that it will be useful,
1.1.1.3 root 14: // but WITHOUT ANY WARRANTY; without even the implied warranty of
1.1.1.4 ! root 15: // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
! 16: // GNU General Public License for more details.
1.1.1.3 root 17: //
18: // $Log:$
19: //
20: // DESCRIPTION:
21: // Game completion, final screen animation.
22: //
23: //-----------------------------------------------------------------------------
24:
25:
26: static const char
27: rcsid[] = "$Id: f_finale.c,v 1.5 1997/02/03 21:26:34 b1 Exp $";
1.1 root 28:
29: #include <ctype.h>
30:
1.1.1.3 root 31: // Functions.
32: #include "i_system.h"
33: #include "m_swap.h"
34: #include "z_zone.h"
35: #include "v_video.h"
36: #include "w_wad.h"
37: #include "s_sound.h"
38:
39: // Data.
40: #include "dstrings.h"
41: #include "sounds.h"
42:
43: #include "doomstat.h"
44: #include "r_state.h"
45:
46: // ?
47: //#include "doomstat.h"
48: //#include "r_local.h"
49: //#include "f_finale.h"
50:
51: // Stage of animation:
52: // 0 = text, 1 = art screen, 2 = character cast
53: int finalestage;
54:
55: int finalecount;
1.1.1.2 root 56:
57: #define TEXTSPEED 3
58: #define TEXTWAIT 250
59:
1.1.1.3 root 60: char* e1text = E1TEXT;
61: char* e2text = E2TEXT;
62: char* e3text = E3TEXT;
63: char* e4text = E4TEXT;
64:
65: char* c1text = C1TEXT;
66: char* c2text = C2TEXT;
67: char* c3text = C3TEXT;
68: char* c4text = C4TEXT;
69: char* c5text = C5TEXT;
70: char* c6text = C6TEXT;
71:
72: char* p1text = P1TEXT;
73: char* p2text = P2TEXT;
74: char* p3text = P3TEXT;
75: char* p4text = P4TEXT;
76: char* p5text = P5TEXT;
77: char* p6text = P6TEXT;
78:
79: char* t1text = T1TEXT;
80: char* t2text = T2TEXT;
81: char* t3text = T3TEXT;
82: char* t4text = T4TEXT;
83: char* t5text = T5TEXT;
84: char* t6text = T6TEXT;
85:
86: char* finaletext;
87: char* finaleflat;
88:
89: void F_StartCast (void);
90: void F_CastTicker (void);
91: boolean F_CastResponder (event_t *ev);
92: void F_CastDrawer (void);
93:
94: //
95: // F_StartFinale
96: //
97: void F_StartFinale (void)
98: {
99: gameaction = ga_nothing;
100: gamestate = GS_FINALE;
101: viewactive = false;
102: automapactive = false;
103:
104: // Okay - IWAD dependend stuff.
105: // This has been changed severly, and
106: // some stuff might have changed in the process.
107: switch ( gamemode )
108: {
109:
110: // DOOM 1 - E1, E3 or E4, but each nine missions
111: case shareware:
112: case registered:
113: case retail:
114: {
115: S_ChangeMusic(mus_victor, true);
116:
117: switch (gameepisode)
118: {
119: case 1:
120: finaleflat = "FLOOR4_8";
121: finaletext = e1text;
122: break;
123: case 2:
124: finaleflat = "SFLR6_1";
125: finaletext = e2text;
126: break;
127: case 3:
128: finaleflat = "MFLR8_4";
129: finaletext = e3text;
130: break;
131: case 4:
132: finaleflat = "MFLR8_3";
133: finaletext = e4text;
134: break;
135: default:
136: // Ouch.
137: break;
138: }
139: break;
140: }
141:
142: // DOOM II and missions packs with E1, M34
143: case commercial:
144: {
145: S_ChangeMusic(mus_read_m, true);
146:
147: switch (gamemap)
148: {
149: case 6:
150: finaleflat = "SLIME16";
151: finaletext = c1text;
152: break;
153: case 11:
154: finaleflat = "RROCK14";
155: finaletext = c2text;
156: break;
157: case 20:
158: finaleflat = "RROCK07";
159: finaletext = c3text;
160: break;
161: case 30:
162: finaleflat = "RROCK17";
163: finaletext = c4text;
164: break;
165: case 15:
166: finaleflat = "RROCK13";
167: finaletext = c5text;
168: break;
169: case 31:
170: finaleflat = "RROCK19";
171: finaletext = c6text;
172: break;
173: default:
174: // Ouch.
175: break;
176: }
177: break;
178: }
179:
180:
181: // Indeterminate.
182: default:
183: S_ChangeMusic(mus_read_m, true);
184: finaleflat = "F_SKY1"; // Not used anywhere else.
185: finaletext = c1text; // FIXME - other text, music?
186: break;
187: }
188:
189: finalestage = 0;
190: finalecount = 0;
191:
192: }
1.1.1.2 root 193:
1.1 root 194:
195:
1.1.1.3 root 196: boolean F_Responder (event_t *event)
197: {
198: if (finalestage == 2)
199: return F_CastResponder (event);
200:
201: return false;
202: }
1.1 root 203:
1.1.1.2 root 204:
1.1.1.3 root 205: //
206: // F_Ticker
207: //
208: void F_Ticker (void)
209: {
210: int i;
211:
212: // check for skipping
213: if ( (gamemode == commercial)
214: && ( finalecount > 50) )
215: {
216: // go on to the next level
217: for (i=0 ; i<MAXPLAYERS ; i++)
218: if (players[i].cmd.buttons)
219: break;
220:
221: if (i < MAXPLAYERS)
222: {
223: if (gamemap == 30)
224: F_StartCast ();
225: else
226: gameaction = ga_worlddone;
227: }
228: }
229:
230: // advance animation
231: finalecount++;
232:
233: if (finalestage == 2)
234: {
235: F_CastTicker ();
236: return;
237: }
238:
239: if ( gamemode == commercial)
240: return;
241:
242: if (!finalestage && finalecount>strlen (finaletext)*TEXTSPEED + TEXTWAIT)
243: {
244: finalecount = 0;
245: finalestage = 1;
246: wipegamestate = -1; // force a wipe
247: if (gameepisode == 3)
248: S_StartMusic (mus_bunny);
249: }
250: }
1.1 root 251:
252:
1.1.1.2 root 253:
1.1.1.3 root 254: //
255: // F_TextWrite
256: //
1.1.1.2 root 257:
1.1.1.3 root 258: #include "hu_stuff.h"
259: extern patch_t *hu_font[HU_FONTSIZE];
1.1 root 260:
1.1.1.2 root 261:
1.1.1.3 root 262: void F_TextWrite (void)
263: {
264: byte* src;
265: byte* dest;
266:
267: int x,y,w;
268: int count;
269: char* ch;
270: int c;
271: int cx;
272: int cy;
273:
274: // erase the entire screen to a tiled background
275: src = W_CacheLumpName ( finaleflat , PU_CACHE);
276: dest = screens[0];
277:
278: for (y=0 ; y<SCREENHEIGHT ; y++)
279: {
280: for (x=0 ; x<SCREENWIDTH/64 ; x++)
281: {
282: memcpy (dest, src+((y&63)<<6), 64);
283: dest += 64;
284: }
285: if (SCREENWIDTH&63)
286: {
287: memcpy (dest, src+((y&63)<<6), SCREENWIDTH&63);
288: dest += (SCREENWIDTH&63);
289: }
290: }
291:
292: V_MarkRect (0, 0, SCREENWIDTH, SCREENHEIGHT);
293:
294: // draw some of the text onto the screen
295: cx = 10;
296: cy = 10;
297: ch = finaletext;
298:
299: count = (finalecount - 10)/TEXTSPEED;
300: if (count < 0)
301: count = 0;
302: for ( ; count ; count-- )
303: {
304: c = *ch++;
305: if (!c)
306: break;
307: if (c == '\n')
308: {
309: cx = 10;
310: cy += 11;
311: continue;
312: }
313:
314: c = toupper(c) - HU_FONTSTART;
315: if (c < 0 || c> HU_FONTSIZE)
316: {
317: cx += 4;
318: continue;
319: }
320:
321: w = SHORT (hu_font[c]->width);
322: if (cx+w > SCREENWIDTH)
323: break;
324: V_DrawPatch(cx, cy, 0, hu_font[c]);
325: cx+=w;
326: }
327:
328: }
1.1.1.2 root 329:
330: //
1.1.1.3 root 331: // Final DOOM 2 animation
332: // Casting by id Software.
333: // in order of appearance
1.1.1.2 root 334: //
1.1.1.3 root 335: typedef struct
1.1 root 336: {
1.1.1.3 root 337: char *name;
338: mobjtype_t type;
339: } castinfo_t;
340:
341: castinfo_t castorder[] = {
342: {CC_ZOMBIE, MT_POSSESSED},
343: {CC_SHOTGUN, MT_SHOTGUY},
344: {CC_HEAVY, MT_CHAINGUY},
345: {CC_IMP, MT_TROOP},
346: {CC_DEMON, MT_SERGEANT},
347: {CC_LOST, MT_SKULL},
348: {CC_CACO, MT_HEAD},
349: {CC_HELL, MT_KNIGHT},
350: {CC_BARON, MT_BRUISER},
351: {CC_ARACH, MT_BABY},
352: {CC_PAIN, MT_PAIN},
353: {CC_REVEN, MT_UNDEAD},
354: {CC_MANCU, MT_FATSO},
355: {CC_ARCH, MT_VILE},
356: {CC_SPIDER, MT_SPIDER},
357: {CC_CYBER, MT_CYBORG},
358: {CC_HERO, MT_PLAYER},
359:
360: {NULL,0}
361: };
362:
363: int castnum;
364: int casttics;
365: state_t* caststate;
366: boolean castdeath;
367: int castframes;
368: int castonmelee;
369: boolean castattacking;
1.1 root 370:
371:
1.1.1.2 root 372: //
1.1.1.3 root 373: // F_StartCast
1.1.1.2 root 374: //
1.1.1.3 root 375: extern gamestate_t wipegamestate;
376:
1.1 root 377:
1.1.1.3 root 378: void F_StartCast (void)
1.1 root 379: {
1.1.1.3 root 380: wipegamestate = -1; // force a screen wipe
381: castnum = 0;
382: caststate = &states[mobjinfo[castorder[castnum].type].seestate];
383: casttics = caststate->tics;
384: castdeath = false;
385: finalestage = 2;
386: castframes = 0;
387: castonmelee = 0;
388: castattacking = false;
389: S_ChangeMusic(mus_evil, true);
1.1 root 390: }
391:
1.1.1.3 root 392:
1.1.1.2 root 393: //
1.1.1.3 root 394: // F_CastTicker
1.1.1.2 root 395: //
1.1.1.3 root 396: void F_CastTicker (void)
1.1 root 397: {
1.1.1.3 root 398: int st;
399: int sfx;
400:
401: if (--casttics > 0)
402: return; // not time to change state yet
403:
404: if (caststate->tics == -1 || caststate->nextstate == S_NULL)
405: {
406: // switch from deathstate to next monster
407: castnum++;
408: castdeath = false;
409: if (castorder[castnum].name == NULL)
410: castnum = 0;
411: if (mobjinfo[castorder[castnum].type].seesound)
412: S_StartSound (NULL, mobjinfo[castorder[castnum].type].seesound);
413: caststate = &states[mobjinfo[castorder[castnum].type].seestate];
414: castframes = 0;
415: }
416: else
417: {
418: // just advance to next state in animation
419: if (caststate == &states[S_PLAY_ATK1])
420: goto stopattack; // Oh, gross hack!
421: st = caststate->nextstate;
422: caststate = &states[st];
423: castframes++;
424:
425: // sound hacks....
426: switch (st)
427: {
428: case S_PLAY_ATK1: sfx = sfx_dshtgn; break;
429: case S_POSS_ATK2: sfx = sfx_pistol; break;
430: case S_SPOS_ATK2: sfx = sfx_shotgn; break;
431: case S_VILE_ATK2: sfx = sfx_vilatk; break;
432: case S_SKEL_FIST2: sfx = sfx_skeswg; break;
433: case S_SKEL_FIST4: sfx = sfx_skepch; break;
434: case S_SKEL_MISS2: sfx = sfx_skeatk; break;
435: case S_FATT_ATK8:
436: case S_FATT_ATK5:
437: case S_FATT_ATK2: sfx = sfx_firsht; break;
438: case S_CPOS_ATK2:
439: case S_CPOS_ATK3:
440: case S_CPOS_ATK4: sfx = sfx_shotgn; break;
441: case S_TROO_ATK3: sfx = sfx_claw; break;
442: case S_SARG_ATK2: sfx = sfx_sgtatk; break;
443: case S_BOSS_ATK2:
444: case S_BOS2_ATK2:
445: case S_HEAD_ATK2: sfx = sfx_firsht; break;
446: case S_SKULL_ATK2: sfx = sfx_sklatk; break;
447: case S_SPID_ATK2:
448: case S_SPID_ATK3: sfx = sfx_shotgn; break;
449: case S_BSPI_ATK2: sfx = sfx_plasma; break;
450: case S_CYBER_ATK2:
451: case S_CYBER_ATK4:
452: case S_CYBER_ATK6: sfx = sfx_rlaunc; break;
453: case S_PAIN_ATK3: sfx = sfx_sklatk; break;
454: default: sfx = 0; break;
455: }
456:
457: if (sfx)
458: S_StartSound (NULL, sfx);
459: }
460:
461: if (castframes == 12)
462: {
463: // go into attack frame
464: castattacking = true;
465: if (castonmelee)
466: caststate=&states[mobjinfo[castorder[castnum].type].meleestate];
1.1.1.2 root 467: else
1.1.1.3 root 468: caststate=&states[mobjinfo[castorder[castnum].type].missilestate];
469: castonmelee ^= 1;
470: if (caststate == &states[S_NULL])
471: {
472: if (castonmelee)
473: caststate=
474: &states[mobjinfo[castorder[castnum].type].meleestate];
475: else
476: caststate=
477: &states[mobjinfo[castorder[castnum].type].missilestate];
478: }
479: }
480:
481: if (castattacking)
482: {
483: if (castframes == 24
484: || caststate == &states[mobjinfo[castorder[castnum].type].seestate] )
485: {
486: stopattack:
487: castattacking = false;
488: castframes = 0;
489: caststate = &states[mobjinfo[castorder[castnum].type].seestate];
490: }
491: }
492:
493: casttics = caststate->tics;
494: if (casttics == -1)
495: casttics = 15;
1.1 root 496: }
497:
1.1.1.3 root 498:
1.1.1.2 root 499: //
1.1.1.3 root 500: // F_CastResponder
1.1.1.2 root 501: //
1.1 root 502:
1.1.1.3 root 503: boolean F_CastResponder (event_t* ev)
1.1 root 504: {
1.1.1.3 root 505: if (ev->type != ev_keydown)
506: return false;
507:
508: if (castdeath)
509: return true; // already in dying frames
510:
511: // go into death frame
512: castdeath = true;
513: caststate = &states[mobjinfo[castorder[castnum].type].deathstate];
514: casttics = caststate->tics;
515: castframes = 0;
516: castattacking = false;
517: if (mobjinfo[castorder[castnum].type].deathsound)
518: S_StartSound (NULL, mobjinfo[castorder[castnum].type].deathsound);
519:
520: return true;
1.1 root 521: }
522:
523:
1.1.1.3 root 524: void F_CastPrint (char* text)
1.1 root 525: {
1.1.1.3 root 526: char* ch;
527: int c;
528: int cx;
529: int w;
530: int width;
531:
532: // find width
533: ch = text;
534: width = 0;
535:
536: while (ch)
537: {
538: c = *ch++;
539: if (!c)
540: break;
541: c = toupper(c) - HU_FONTSTART;
542: if (c < 0 || c> HU_FONTSIZE)
543: {
544: width += 4;
545: continue;
546: }
547:
548: w = SHORT (hu_font[c]->width);
549: width += w;
550: }
551:
552: // draw it
553: cx = 160-width/2;
554: ch = text;
555: while (ch)
556: {
557: c = *ch++;
558: if (!c)
559: break;
560: c = toupper(c) - HU_FONTSTART;
561: if (c < 0 || c> HU_FONTSIZE)
562: {
563: cx += 4;
564: continue;
565: }
566:
567: w = SHORT (hu_font[c]->width);
568: V_DrawPatch(cx, 180, 0, hu_font[c]);
569: cx+=w;
570: }
571:
1.1 root 572: }
573:
1.1.1.3 root 574:
1.1.1.2 root 575: //
1.1.1.3 root 576: // F_CastDrawer
1.1.1.2 root 577: //
1.1.1.3 root 578: void V_DrawPatchFlipped (int x, int y, int scrn, patch_t *patch);
1.1 root 579:
1.1.1.3 root 580: void F_CastDrawer (void)
1.1 root 581: {
1.1.1.3 root 582: spritedef_t* sprdef;
583: spriteframe_t* sprframe;
584: int lump;
585: boolean flip;
586: patch_t* patch;
587:
588: // erase the entire screen to a background
589: V_DrawPatch (0,0,0, W_CacheLumpName ("BOSSBACK", PU_CACHE));
590:
591: F_CastPrint (castorder[castnum].name);
592:
593: // draw the current frame in the middle of the screen
594: sprdef = &sprites[caststate->sprite];
595: sprframe = &sprdef->spriteframes[ caststate->frame & FF_FRAMEMASK];
596: lump = sprframe->lump[0];
597: flip = (boolean)sprframe->flip[0];
598:
599: patch = W_CacheLumpNum (lump+firstspritelump, PU_CACHE);
600: if (flip)
601: V_DrawPatchFlipped (160,170,0,patch);
602: else
603: V_DrawPatch (160,170,0,patch);
1.1.1.2 root 604: }
1.1 root 605:
1.1.1.3 root 606:
1.1.1.2 root 607: //
1.1.1.3 root 608: // F_DrawPatchCol
1.1.1.2 root 609: //
1.1.1.3 root 610: void
611: F_DrawPatchCol
612: ( int x,
613: patch_t* patch,
614: int col )
1.1.1.2 root 615: {
1.1.1.3 root 616: column_t* column;
617: byte* source;
618: byte* dest;
619: byte* desttop;
620: int count;
621:
622: column = (column_t *)((byte *)patch + LONG(patch->columnofs[col]));
623: desttop = screens[0]+x;
624:
625: // step through the posts in a column
626: while (column->topdelta != 0xff )
627: {
628: source = (byte *)column + 3;
629: dest = desttop + column->topdelta*SCREENWIDTH;
630: count = column->length;
631:
632: while (count--)
1.1 root 633: {
1.1.1.3 root 634: *dest = *source++;
635: dest += SCREENWIDTH;
1.1 root 636: }
1.1.1.3 root 637: column = (column_t *)( (byte *)column + column->length + 4 );
638: }
1.1 root 639: }
640:
1.1.1.3 root 641:
1.1.1.2 root 642: //
1.1.1.3 root 643: // F_BunnyScroll
1.1.1.2 root 644: //
1.1.1.3 root 645: void F_BunnyScroll (void)
1.1 root 646: {
1.1.1.3 root 647: int scrolled;
648: int x;
649: patch_t* p1;
650: patch_t* p2;
651: char name[10];
652: int stage;
653: static int laststage;
654:
655: p1 = W_CacheLumpName ("PFUB2", PU_LEVEL);
656: p2 = W_CacheLumpName ("PFUB1", PU_LEVEL);
657:
658: V_MarkRect (0, 0, SCREENWIDTH, SCREENHEIGHT);
659:
660: scrolled = 320 - (finalecount-230)/2;
661: if (scrolled > 320)
662: scrolled = 320;
663: if (scrolled < 0)
664: scrolled = 0;
665:
666: for ( x=0 ; x<SCREENWIDTH ; x++)
667: {
668: if (x+scrolled < 320)
669: F_DrawPatchCol (x, p1, x+scrolled);
670: else
671: F_DrawPatchCol (x, p2, x+scrolled - 320);
672: }
673:
674: if (finalecount < 1130)
675: return;
676: if (finalecount < 1180)
677: {
678: V_DrawPatch ((SCREENWIDTH-13*8)/2,
679: (SCREENHEIGHT-8*8)/2,0, W_CacheLumpName ("END0",PU_CACHE));
680: laststage = 0;
681: return;
682: }
683:
684: stage = (finalecount-1180) / 5;
685: if (stage > 6)
686: stage = 6;
687: if (stage > laststage)
688: {
689: S_StartSound (NULL, sfx_pistol);
690: laststage = stage;
691: }
692:
693: sprintf (name,"END%i",stage);
694: V_DrawPatch ((SCREENWIDTH-13*8)/2, (SCREENHEIGHT-8*8)/2,0, W_CacheLumpName (name,PU_CACHE));
695: }
1.1.1.2 root 696:
1.1.1.3 root 697:
698: //
699: // F_Drawer
700: //
701: void F_Drawer (void)
702: {
703: if (finalestage == 2)
704: {
705: F_CastDrawer ();
706: return;
707: }
708:
709: if (!finalestage)
710: F_TextWrite ();
711: else
712: {
713: switch (gameepisode)
714: {
715: case 1:
716: if ( gamemode == retail )
717: V_DrawPatch (0,0,0,
718: W_CacheLumpName("CREDIT",PU_CACHE));
719: else
720: V_DrawPatch (0,0,0,
721: W_CacheLumpName("HELP2",PU_CACHE));
722: break;
723: case 2:
724: V_DrawPatch(0,0,0,
725: W_CacheLumpName("VICTORY2",PU_CACHE));
726: break;
727: case 3:
728: F_BunnyScroll ();
729: break;
730: case 4:
731: V_DrawPatch (0,0,0,
732: W_CacheLumpName("ENDPIC",PU_CACHE));
733: break;
1.1.1.2 root 734: }
1.1.1.3 root 735: }
736:
1.1 root 737: }
1.1.1.3 root 738:
739:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.