0,0 → 1,738 |
// Emacs style mode select -*- C++ -*- |
//----------------------------------------------------------------------------- |
// |
// $Id:$ |
// |
// Copyright (C) 1993-1996 by id Software, Inc. |
// |
// This source is available for distribution and/or modification |
// only under the terms of the DOOM Source Code License as |
// published by id Software. All rights reserved. |
// |
// The source is distributed in the hope that it will be useful, |
// but WITHOUT ANY WARRANTY; without even the implied warranty of |
// FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License |
// for more details. |
// |
// $Log:$ |
// |
// DESCRIPTION: |
// Game completion, final screen animation. |
// |
//----------------------------------------------------------------------------- |
|
|
static const char |
rcsid[] = "$Id: f_finale.c,v 1.5 1997/02/03 21:26:34 b1 Exp $"; |
|
#include <ctype.h> |
|
// Functions. |
#include "m_swap.h" |
#include "i_system.h" |
#include "z_zone.h" |
#include "v_video.h" |
#include "w_wad.h" |
#include "s_sound.h" |
|
// Data. |
#include "dstrings.h" |
#include "sounds.h" |
|
#include "doomstat.h" |
#include "r_state.h" |
|
// ? |
//#include "doomstat.h" |
//#include "r_local.h" |
//#include "f_finale.h" |
|
// Stage of animation: |
// 0 = text, 1 = art screen, 2 = character cast |
int finalestage; |
|
int finalecount; |
|
#define TEXTSPEED 3 |
#define TEXTWAIT 250 |
|
char* e1text = E1TEXT; |
char* e2text = E2TEXT; |
char* e3text = E3TEXT; |
char* e4text = E4TEXT; |
|
char* c1text = C1TEXT; |
char* c2text = C2TEXT; |
char* c3text = C3TEXT; |
char* c4text = C4TEXT; |
char* c5text = C5TEXT; |
char* c6text = C6TEXT; |
|
char* p1text = P1TEXT; |
char* p2text = P2TEXT; |
char* p3text = P3TEXT; |
char* p4text = P4TEXT; |
char* p5text = P5TEXT; |
char* p6text = P6TEXT; |
|
char* t1text = T1TEXT; |
char* t2text = T2TEXT; |
char* t3text = T3TEXT; |
char* t4text = T4TEXT; |
char* t5text = T5TEXT; |
char* t6text = T6TEXT; |
|
char* finaletext; |
char* finaleflat; |
|
void F_StartCast (void); |
void F_CastTicker (void); |
boolean F_CastResponder (event_t *ev); |
void F_CastDrawer (void); |
|
// |
// F_StartFinale |
// |
void F_StartFinale (void) |
{ |
gameaction = ga_nothing; |
gamestate = GS_FINALE; |
viewactive = false; |
automapactive = false; |
|
// Okay - IWAD dependend stuff. |
// This has been changed severly, and |
// some stuff might have changed in the process. |
switch ( gamemode ) |
{ |
|
// DOOM 1 - E1, E3 or E4, but each nine missions |
case shareware: |
case registered: |
case retail: |
{ |
S_ChangeMusic(mus_victor, true); |
|
switch (gameepisode) |
{ |
case 1: |
finaleflat = "FLOOR4_8"; |
finaletext = e1text; |
break; |
case 2: |
finaleflat = "SFLR6_1"; |
finaletext = e2text; |
break; |
case 3: |
finaleflat = "MFLR8_4"; |
finaletext = e3text; |
break; |
case 4: |
finaleflat = "MFLR8_3"; |
finaletext = e4text; |
break; |
default: |
// Ouch. |
break; |
} |
break; |
} |
|
// DOOM II and missions packs with E1, M34 |
case commercial: |
{ |
S_ChangeMusic(mus_read_m, true); |
|
switch (gamemap) |
{ |
case 6: |
finaleflat = "SLIME16"; |
finaletext = c1text; |
break; |
case 11: |
finaleflat = "RROCK14"; |
finaletext = c2text; |
break; |
case 20: |
finaleflat = "RROCK07"; |
finaletext = c3text; |
break; |
case 30: |
finaleflat = "RROCK17"; |
finaletext = c4text; |
break; |
case 15: |
finaleflat = "RROCK13"; |
finaletext = c5text; |
break; |
case 31: |
finaleflat = "RROCK19"; |
finaletext = c6text; |
break; |
default: |
// Ouch. |
break; |
} |
break; |
} |
|
|
// Indeterminate. |
default: |
S_ChangeMusic(mus_read_m, true); |
finaleflat = "F_SKY1"; // Not used anywhere else. |
finaletext = c1text; // FIXME - other text, music? |
break; |
} |
|
finalestage = 0; |
finalecount = 0; |
|
} |
|
|
|
boolean F_Responder (event_t *event) |
{ |
if (finalestage == 2) |
return F_CastResponder (event); |
|
return false; |
} |
|
|
// |
// F_Ticker |
// |
void F_Ticker (void) |
{ |
int i; |
|
// check for skipping |
if ( (gamemode == commercial) |
&& ( finalecount > 50) ) |
{ |
// go on to the next level |
for (i=0 ; i<MAXPLAYERS ; i++) |
if (players[i].cmd.buttons) |
break; |
|
if (i < MAXPLAYERS) |
{ |
if (gamemap == 30) |
F_StartCast (); |
else |
gameaction = ga_worlddone; |
} |
} |
|
// advance animation |
finalecount++; |
|
if (finalestage == 2) |
{ |
F_CastTicker (); |
return; |
} |
|
if ( gamemode == commercial) |
return; |
|
if (!finalestage && finalecount>strlen (finaletext)*TEXTSPEED + TEXTWAIT) |
{ |
finalecount = 0; |
finalestage = 1; |
wipegamestate = -1; // force a wipe |
if (gameepisode == 3) |
S_StartMusic (mus_bunny); |
} |
} |
|
|
|
// |
// F_TextWrite |
// |
|
#include "hu_stuff.h" |
extern patch_t *hu_font[HU_FONTSIZE]; |
|
|
void F_TextWrite (void) |
{ |
byte* src; |
byte* dest; |
|
int x,y,w; |
int count; |
char* ch; |
int c; |
int cx; |
int cy; |
|
// erase the entire screen to a tiled background |
src = W_CacheLumpName ( finaleflat , PU_CACHE); |
dest = screens[0]; |
|
for (y=0 ; y<SCREENHEIGHT ; y++) |
{ |
for (x=0 ; x<SCREENWIDTH/64 ; x++) |
{ |
memcpy (dest, src+((y&63)<<6), 64); |
dest += 64; |
} |
if (SCREENWIDTH&63) |
{ |
memcpy (dest, src+((y&63)<<6), SCREENWIDTH&63); |
dest += (SCREENWIDTH&63); |
} |
} |
|
V_MarkRect (0, 0, SCREENWIDTH, SCREENHEIGHT); |
|
// draw some of the text onto the screen |
cx = 10; |
cy = 10; |
ch = finaletext; |
|
count = (finalecount - 10)/TEXTSPEED; |
if (count < 0) |
count = 0; |
for ( ; count ; count-- ) |
{ |
c = *ch++; |
if (!c) |
break; |
if (c == '\n') |
{ |
cx = 10; |
cy += 11; |
continue; |
} |
|
c = toupper(c) - HU_FONTSTART; |
if (c < 0 || c> HU_FONTSIZE) |
{ |
cx += 4; |
continue; |
} |
|
w = SHORT (hu_font[c]->width); |
if (cx+w > SCREENWIDTH) |
break; |
V_DrawPatch(cx, cy, 0, hu_font[c]); |
cx+=w; |
} |
|
} |
|
// |
// Final DOOM 2 animation |
// Casting by id Software. |
// in order of appearance |
// |
typedef struct |
{ |
char *name; |
mobjtype_t type; |
} castinfo_t; |
|
castinfo_t castorder[] = { |
{CC_ZOMBIE, MT_POSSESSED}, |
{CC_SHOTGUN, MT_SHOTGUY}, |
{CC_HEAVY, MT_CHAINGUY}, |
{CC_IMP, MT_TROOP}, |
{CC_DEMON, MT_SERGEANT}, |
{CC_LOST, MT_SKULL}, |
{CC_CACO, MT_HEAD}, |
{CC_HELL, MT_KNIGHT}, |
{CC_BARON, MT_BRUISER}, |
{CC_ARACH, MT_BABY}, |
{CC_PAIN, MT_PAIN}, |
{CC_REVEN, MT_UNDEAD}, |
{CC_MANCU, MT_FATSO}, |
{CC_ARCH, MT_VILE}, |
{CC_SPIDER, MT_SPIDER}, |
{CC_CYBER, MT_CYBORG}, |
{CC_HERO, MT_PLAYER}, |
|
{NULL,0} |
}; |
|
int castnum; |
int casttics; |
state_t* caststate; |
boolean castdeath; |
int castframes; |
int castonmelee; |
boolean castattacking; |
|
|
// |
// F_StartCast |
// |
extern gamestate_t wipegamestate; |
|
|
void F_StartCast (void) |
{ |
wipegamestate = -1; // force a screen wipe |
castnum = 0; |
caststate = &states[mobjinfo[castorder[castnum].type].seestate]; |
casttics = caststate->tics; |
castdeath = false; |
finalestage = 2; |
castframes = 0; |
castonmelee = 0; |
castattacking = false; |
S_ChangeMusic(mus_evil, true); |
} |
|
|
// |
// F_CastTicker |
// |
void F_CastTicker (void) |
{ |
int st; |
int sfx; |
|
if (--casttics > 0) |
return; // not time to change state yet |
|
if (caststate->tics == -1 || caststate->nextstate == S_NULL) |
{ |
// switch from deathstate to next monster |
castnum++; |
castdeath = false; |
if (castorder[castnum].name == NULL) |
castnum = 0; |
if (mobjinfo[castorder[castnum].type].seesound) |
S_StartSound (NULL, mobjinfo[castorder[castnum].type].seesound); |
caststate = &states[mobjinfo[castorder[castnum].type].seestate]; |
castframes = 0; |
} |
else |
{ |
// just advance to next state in animation |
if (caststate == &states[S_PLAY_ATK1]) |
goto stopattack; // Oh, gross hack! |
st = caststate->nextstate; |
caststate = &states[st]; |
castframes++; |
|
// sound hacks.... |
switch (st) |
{ |
case S_PLAY_ATK1: sfx = sfx_dshtgn; break; |
case S_POSS_ATK2: sfx = sfx_pistol; break; |
case S_SPOS_ATK2: sfx = sfx_shotgn; break; |
case S_VILE_ATK2: sfx = sfx_vilatk; break; |
case S_SKEL_FIST2: sfx = sfx_skeswg; break; |
case S_SKEL_FIST4: sfx = sfx_skepch; break; |
case S_SKEL_MISS2: sfx = sfx_skeatk; break; |
case S_FATT_ATK8: |
case S_FATT_ATK5: |
case S_FATT_ATK2: sfx = sfx_firsht; break; |
case S_CPOS_ATK2: |
case S_CPOS_ATK3: |
case S_CPOS_ATK4: sfx = sfx_shotgn; break; |
case S_TROO_ATK3: sfx = sfx_claw; break; |
case S_SARG_ATK2: sfx = sfx_sgtatk; break; |
case S_BOSS_ATK2: |
case S_BOS2_ATK2: |
case S_HEAD_ATK2: sfx = sfx_firsht; break; |
case S_SKULL_ATK2: sfx = sfx_sklatk; break; |
case S_SPID_ATK2: |
case S_SPID_ATK3: sfx = sfx_shotgn; break; |
case S_BSPI_ATK2: sfx = sfx_plasma; break; |
case S_CYBER_ATK2: |
case S_CYBER_ATK4: |
case S_CYBER_ATK6: sfx = sfx_rlaunc; break; |
case S_PAIN_ATK3: sfx = sfx_sklatk; break; |
default: sfx = 0; break; |
} |
|
if (sfx) |
S_StartSound (NULL, sfx); |
} |
|
if (castframes == 12) |
{ |
// go into attack frame |
castattacking = true; |
if (castonmelee) |
caststate=&states[mobjinfo[castorder[castnum].type].meleestate]; |
else |
caststate=&states[mobjinfo[castorder[castnum].type].missilestate]; |
castonmelee ^= 1; |
if (caststate == &states[S_NULL]) |
{ |
if (castonmelee) |
caststate= |
&states[mobjinfo[castorder[castnum].type].meleestate]; |
else |
caststate= |
&states[mobjinfo[castorder[castnum].type].missilestate]; |
} |
} |
|
if (castattacking) |
{ |
if (castframes == 24 |
|| caststate == &states[mobjinfo[castorder[castnum].type].seestate] ) |
{ |
stopattack: |
castattacking = false; |
castframes = 0; |
caststate = &states[mobjinfo[castorder[castnum].type].seestate]; |
} |
} |
|
casttics = caststate->tics; |
if (casttics == -1) |
casttics = 15; |
} |
|
|
// |
// F_CastResponder |
// |
|
boolean F_CastResponder (event_t* ev) |
{ |
if (ev->type != ev_keydown) |
return false; |
|
if (castdeath) |
return true; // already in dying frames |
|
// go into death frame |
castdeath = true; |
caststate = &states[mobjinfo[castorder[castnum].type].deathstate]; |
casttics = caststate->tics; |
castframes = 0; |
castattacking = false; |
if (mobjinfo[castorder[castnum].type].deathsound) |
S_StartSound (NULL, mobjinfo[castorder[castnum].type].deathsound); |
|
return true; |
} |
|
|
void F_CastPrint (char* text) |
{ |
char* ch; |
int c; |
int cx; |
int w; |
int width; |
|
// find width |
ch = text; |
width = 0; |
|
while (ch) |
{ |
c = *ch++; |
if (!c) |
break; |
c = toupper(c) - HU_FONTSTART; |
if (c < 0 || c> HU_FONTSIZE) |
{ |
width += 4; |
continue; |
} |
|
w = SHORT (hu_font[c]->width); |
width += w; |
} |
|
// draw it |
cx = 160-width/2; |
ch = text; |
while (ch) |
{ |
c = *ch++; |
if (!c) |
break; |
c = toupper(c) - HU_FONTSTART; |
if (c < 0 || c> HU_FONTSIZE) |
{ |
cx += 4; |
continue; |
} |
|
w = SHORT (hu_font[c]->width); |
V_DrawPatch(cx, 180, 0, hu_font[c]); |
cx+=w; |
} |
|
} |
|
|
// |
// F_CastDrawer |
// |
void V_DrawPatchFlipped (int x, int y, int scrn, patch_t *patch); |
|
void F_CastDrawer (void) |
{ |
spritedef_t* sprdef; |
spriteframe_t* sprframe; |
int lump; |
boolean flip; |
patch_t* patch; |
|
// erase the entire screen to a background |
V_DrawPatch (0,0,0, W_CacheLumpName ("BOSSBACK", PU_CACHE)); |
|
F_CastPrint (castorder[castnum].name); |
|
// draw the current frame in the middle of the screen |
sprdef = &sprites[caststate->sprite]; |
sprframe = &sprdef->spriteframes[ caststate->frame & FF_FRAMEMASK]; |
lump = sprframe->lump[0]; |
flip = (boolean)sprframe->flip[0]; |
|
patch = W_CacheLumpNum (lump+firstspritelump, PU_CACHE); |
if (flip) |
V_DrawPatchFlipped (160,170,0,patch); |
else |
V_DrawPatch (160,170,0,patch); |
} |
|
|
// |
// F_DrawPatchCol |
// |
void |
F_DrawPatchCol |
( int x, |
patch_t* patch, |
int col ) |
{ |
column_t* column; |
byte* source; |
byte* dest; |
byte* desttop; |
int count; |
|
column = (column_t *)((byte *)patch + LONG(patch->columnofs[col])); |
desttop = screens[0]+x; |
|
// step through the posts in a column |
while (column->topdelta != 0xff ) |
{ |
source = (byte *)column + 3; |
dest = desttop + column->topdelta*SCREENWIDTH; |
count = column->length; |
|
while (count--) |
{ |
*dest = *source++; |
dest += SCREENWIDTH; |
} |
column = (column_t *)( (byte *)column + column->length + 4 ); |
} |
} |
|
|
// |
// F_BunnyScroll |
// |
void F_BunnyScroll (void) |
{ |
int scrolled; |
int x; |
patch_t* p1; |
patch_t* p2; |
char name[10]; |
int stage; |
static int laststage; |
|
p1 = W_CacheLumpName ("PFUB2", PU_LEVEL); |
p2 = W_CacheLumpName ("PFUB1", PU_LEVEL); |
|
V_MarkRect (0, 0, SCREENWIDTH, SCREENHEIGHT); |
|
scrolled = 320 - (finalecount-230)/2; |
if (scrolled > 320) |
scrolled = 320; |
if (scrolled < 0) |
scrolled = 0; |
|
for ( x=0 ; x<SCREENWIDTH ; x++) |
{ |
if (x+scrolled < 320) |
F_DrawPatchCol (x, p1, x+scrolled); |
else |
F_DrawPatchCol (x, p2, x+scrolled - 320); |
} |
|
if (finalecount < 1130) |
return; |
if (finalecount < 1180) |
{ |
V_DrawPatch ((SCREENWIDTH-13*8)/2, |
(SCREENHEIGHT-8*8)/2,0, W_CacheLumpName ("END0",PU_CACHE)); |
laststage = 0; |
return; |
} |
|
stage = (finalecount-1180) / 5; |
if (stage > 6) |
stage = 6; |
if (stage > laststage) |
{ |
S_StartSound (NULL, sfx_pistol); |
laststage = stage; |
} |
|
sprintf (name,"END%i",stage); |
V_DrawPatch ((SCREENWIDTH-13*8)/2, (SCREENHEIGHT-8*8)/2,0, W_CacheLumpName (name,PU_CACHE)); |
} |
|
|
// |
// F_Drawer |
// |
void F_Drawer (void) |
{ |
if (finalestage == 2) |
{ |
F_CastDrawer (); |
return; |
} |
|
if (!finalestage) |
F_TextWrite (); |
else |
{ |
switch (gameepisode) |
{ |
case 1: |
if ( gamemode == retail ) |
V_DrawPatch (0,0,0, |
W_CacheLumpName("CREDIT",PU_CACHE)); |
else |
V_DrawPatch (0,0,0, |
W_CacheLumpName("HELP2",PU_CACHE)); |
break; |
case 2: |
V_DrawPatch(0,0,0, |
W_CacheLumpName("VICTORY2",PU_CACHE)); |
break; |
case 3: |
F_BunnyScroll (); |
break; |
case 4: |
V_DrawPatch (0,0,0, |
W_CacheLumpName("ENDPIC",PU_CACHE)); |
break; |
} |
} |
|
} |
|
|