MCUME/MCUME_teensy41/teensydoom/f_finale.c

719 wiersze
14 KiB
C

//
// Copyright(C) 1993-1996 Id Software, Inc.
// Copyright(C) 2005-2014 Simon Howard
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// DESCRIPTION:
// Game completion, final screen animation.
//
#include <stdio.h>
#include <ctype.h>
// Functions.
#include "deh_main.h"
#include "i_system.h"
#include "i_swap.h"
#include "z_zone.h"
#include "v_video.h"
#include "w_wad.h"
#include "s_sound.h"
// Data.
#include "d_main.h"
#include "dstrings.h"
#include "sounds.h"
#include "doomstat.h"
#include "r_state.h"
typedef enum
{
F_STAGE_TEXT,
F_STAGE_ARTSCREEN,
F_STAGE_CAST,
} finalestage_t;
// ?
//#include "doomstat.h"
//#include "r_local.h"
//#include "f_finale.h"
// Stage of animation:
finalestage_t finalestage;
unsigned int finalecount;
#define TEXTSPEED 3
#define TEXTWAIT 250
typedef struct
{
GameMission_t mission;
int episode, level;
char *background;
char *text;
} textscreen_t;
static textscreen_t textscreens[] =
{
{ doom, 1, 8, "FLOOR4_8", E1TEXT},
{ doom, 2, 8, "SFLR6_1", E2TEXT},
{ doom, 3, 8, "MFLR8_4", E3TEXT},
{ doom, 4, 8, "MFLR8_3", E4TEXT},
{ doom2, 1, 6, "SLIME16", C1TEXT},
{ doom2, 1, 11, "RROCK14", C2TEXT},
{ doom2, 1, 20, "RROCK07", C3TEXT},
{ doom2, 1, 30, "RROCK17", C4TEXT},
{ doom2, 1, 15, "RROCK13", C5TEXT},
{ doom2, 1, 31, "RROCK19", C6TEXT},
{ pack_tnt, 1, 6, "SLIME16", T1TEXT},
{ pack_tnt, 1, 11, "RROCK14", T2TEXT},
{ pack_tnt, 1, 20, "RROCK07", T3TEXT},
{ pack_tnt, 1, 30, "RROCK17", T4TEXT},
{ pack_tnt, 1, 15, "RROCK13", T5TEXT},
{ pack_tnt, 1, 31, "RROCK19", T6TEXT},
{ pack_plut, 1, 6, "SLIME16", P1TEXT},
{ pack_plut, 1, 11, "RROCK14", P2TEXT},
{ pack_plut, 1, 20, "RROCK07", P3TEXT},
{ pack_plut, 1, 30, "RROCK17", P4TEXT},
{ pack_plut, 1, 15, "RROCK13", P5TEXT},
{ pack_plut, 1, 31, "RROCK19", P6TEXT},
};
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)
{
size_t i;
gameaction = ga_nothing;
gamestate = GS_FINALE;
viewactive = false;
automapactive = false;
if (logical_gamemission == doom)
{
S_ChangeMusic(mus_victor, true);
}
else
{
S_ChangeMusic(mus_read_m, true);
}
// Find the right screen and set the text and background
for (i=0; i<arrlen(textscreens); ++i)
{
textscreen_t *screen = &textscreens[i];
// Hack for Chex Quest
if (gameversion == exe_chex && screen->mission == doom)
{
screen->level = 5;
}
if (logical_gamemission == screen->mission
&& (logical_gamemission != doom || gameepisode == screen->episode)
&& gamemap == screen->level)
{
finaletext = screen->text;
finaleflat = screen->background;
}
}
// Do dehacked substitutions of strings
finaletext = DEH_String(finaletext);
finaleflat = DEH_String(finaleflat);
finalestage = F_STAGE_TEXT;
finalecount = 0;
}
boolean F_Responder (event_t *event)
{
if (finalestage == F_STAGE_CAST)
return F_CastResponder (event);
return false;
}
//
// F_Ticker
//
void F_Ticker (void)
{
size_t 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 == F_STAGE_CAST)
{
F_CastTicker ();
return;
}
if ( gamemode == commercial)
return;
if (finalestage == F_STAGE_TEXT
&& finalecount>strlen (finaletext)*TEXTSPEED + TEXTWAIT)
{
finalecount = 0;
finalestage = F_STAGE_ARTSCREEN;
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;
signed 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 = I_VideoBuffer;
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 = ((signed int) 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, 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
//
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 = F_STAGE_CAST;
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, hu_font[c]);
cx+=w;
}
}
//
// F_CastDrawer
//
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, W_CacheLumpName (DEH_String("BOSSBACK"), PU_CACHE));
F_CastPrint (DEH_String(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, patch);
else
V_DrawPatch(160, 170, 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 = I_VideoBuffer + 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)
{
signed int scrolled;
int x;
patch_t* p1;
patch_t* p2;
char name[10];
int stage;
static int laststage;
p1 = W_CacheLumpName (DEH_String("PFUB2"), PU_LEVEL);
p2 = W_CacheLumpName (DEH_String("PFUB1"), PU_LEVEL);
V_MarkRect (0, 0, SCREENWIDTH, SCREENHEIGHT);
scrolled = (320 - ((signed int) 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,
W_CacheLumpName(DEH_String("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;
}
DEH_snprintf(name, 10, "END%i", stage);
V_DrawPatch((SCREENWIDTH - 13 * 8) / 2,
(SCREENHEIGHT - 8 * 8) / 2,
W_CacheLumpName (name,PU_CACHE));
}
static void F_ArtScreenDrawer(void)
{
char *lumpname;
if (gameepisode == 3)
{
F_BunnyScroll();
}
else
{
switch (gameepisode)
{
case 1:
if (gamemode == retail)
{
lumpname = "CREDIT";
}
else
{
lumpname = "HELP2";
}
break;
case 2:
lumpname = "VICTORY2";
break;
case 4:
lumpname = "ENDPIC";
break;
default:
return;
}
lumpname = DEH_String(lumpname);
V_DrawPatch (0, 0, W_CacheLumpName(lumpname, PU_CACHE));
}
}
//
// F_Drawer
//
void F_Drawer (void)
{
switch (finalestage)
{
case F_STAGE_CAST:
F_CastDrawer();
break;
case F_STAGE_TEXT:
F_TextWrite();
break;
case F_STAGE_ARTSCREEN:
F_ArtScreenDrawer();
break;
}
}