2008-01-27 11:25:03 +00:00
|
|
|
// 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.
|
|
|
|
//
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include <ctype.h>
|
|
|
|
#include <math.h>
|
|
|
|
#include <malloc.h>
|
|
|
|
|
|
|
|
#include "i_system.h"
|
|
|
|
#include "m_swap.h"
|
|
|
|
#include "v_video.h"
|
|
|
|
#include "i_video.h"
|
|
|
|
#include "v_text.h"
|
|
|
|
#include "w_wad.h"
|
|
|
|
#include "s_sound.h"
|
|
|
|
#include "gstrings.h"
|
|
|
|
#include "doomstat.h"
|
|
|
|
#include "r_state.h"
|
|
|
|
#include "r_draw.h"
|
|
|
|
#include "hu_stuff.h"
|
|
|
|
#include "cmdlib.h"
|
|
|
|
#include "gi.h"
|
|
|
|
#include "p_conversation.h"
|
|
|
|
#include "a_strifeglobal.h"
|
|
|
|
#include "templates.h"
|
|
|
|
#include "c_bind.h"
|
|
|
|
#include "r_translate.h"
|
|
|
|
|
|
|
|
static void FadePic ();
|
|
|
|
static void GetFinaleText (const char *msgLumpName);
|
|
|
|
|
|
|
|
// Stage of animation:
|
|
|
|
// 0 = text
|
|
|
|
// 1 = art screen
|
|
|
|
// 2 = underwater screen
|
|
|
|
// 3 = character cast
|
|
|
|
// 4 = Heretic title
|
|
|
|
// 5 = Strife slideshow
|
|
|
|
static unsigned int FinaleStage;
|
|
|
|
|
|
|
|
static size_t FinaleCount, FinaleEndCount;
|
|
|
|
static int FinalePart;
|
|
|
|
|
|
|
|
static int TEXTSPEED;
|
|
|
|
#define TEXTWAIT 250
|
|
|
|
|
|
|
|
static int FinaleSequence;
|
|
|
|
static SBYTE FadeDir;
|
|
|
|
static bool FinaleHasPic;
|
|
|
|
|
|
|
|
static FString FinaleText;
|
|
|
|
static size_t FinaleTextLen;
|
|
|
|
static const char *FinaleFlat;
|
|
|
|
static bool FinaleEnding;
|
|
|
|
|
|
|
|
void F_StartCast (void);
|
|
|
|
void F_CastTicker (void);
|
|
|
|
bool F_CastResponder (event_t *ev);
|
|
|
|
void F_CastDrawer (void);
|
|
|
|
void F_AdvanceSlideshow ();
|
|
|
|
|
|
|
|
//
|
|
|
|
// F_StartFinale
|
|
|
|
//
|
|
|
|
void F_StartFinale (char *music, int musicorder, int cdtrack, unsigned int cdid, char *flat, char *text,
|
|
|
|
INTBOOL textInLump, INTBOOL finalePic, INTBOOL lookupText, bool ending)
|
|
|
|
{
|
|
|
|
bool loopmusic = ending ? !(gameinfo.flags & GI_NOLOOPFINALEMUSIC) : true;
|
|
|
|
gameaction = ga_nothing;
|
|
|
|
gamestate = GS_FINALE;
|
|
|
|
viewactive = false;
|
|
|
|
automapactive = false;
|
|
|
|
|
|
|
|
// Okay - IWAD dependend stuff.
|
|
|
|
// This has been changed severely, and some stuff might have changed in the process.
|
|
|
|
//
|
|
|
|
// [RH] More flexible now (even more severe changes)
|
|
|
|
// FinaleFlat, FinaleText, and music are now determined in G_WorldDone() based on
|
|
|
|
// data in a level_info_t and a cluster_info_t.
|
|
|
|
|
|
|
|
if (cdtrack == 0 || !S_ChangeCDMusic (cdtrack, cdid))
|
|
|
|
{
|
|
|
|
if (music == NULL)
|
|
|
|
{
|
|
|
|
S_ChangeMusic (gameinfo.finaleMusic, 0, loopmusic);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
S_ChangeMusic (music, musicorder, loopmusic);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
FinaleFlat = (flat != NULL && *flat != 0) ? flat : gameinfo.finaleFlat;
|
|
|
|
|
|
|
|
if (textInLump)
|
|
|
|
{
|
|
|
|
GetFinaleText (text);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
const char *from = (text != NULL) ? text : "Empty message";
|
|
|
|
FinaleText = from;
|
|
|
|
FinaleTextLen = FinaleText.Len() + 1;
|
|
|
|
}
|
|
|
|
if (lookupText)
|
|
|
|
{
|
|
|
|
const char *str = GStrings[FinaleText.GetChars()];
|
|
|
|
if (str != NULL)
|
|
|
|
{
|
|
|
|
FinaleText = str;
|
|
|
|
FinaleTextLen = FinaleText.Len() + 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
FinaleStage = 0;
|
|
|
|
V_SetBlend (0,0,0,0);
|
|
|
|
TEXTSPEED = 2;
|
|
|
|
|
|
|
|
FinaleHasPic = !!finalePic;
|
|
|
|
FinaleCount = 0;
|
|
|
|
FinaleEndCount = 70;
|
|
|
|
FadeDir = -1;
|
|
|
|
FinaleEnding = ending;
|
|
|
|
S_StopAllChannels ();
|
|
|
|
|
|
|
|
if (ending)
|
|
|
|
{
|
|
|
|
FinaleSequence = *((WORD *)&level.nextmap[6]);
|
|
|
|
if (EndSequences[FinaleSequence].EndType == END_Chess)
|
|
|
|
{
|
|
|
|
TEXTSPEED = 3; // Slow the text to its original rate to match the music.
|
|
|
|
S_ChangeMusic ("hall", 0, loopmusic);
|
|
|
|
FinaleStage = 10;
|
|
|
|
GetFinaleText ("win1msg");
|
|
|
|
V_SetBlend (0,0,0,256);
|
|
|
|
}
|
|
|
|
else if (EndSequences[FinaleSequence].EndType == END_Strife)
|
|
|
|
{
|
|
|
|
if (players[0].mo->FindInventory (QuestItemClasses[24]) ||
|
|
|
|
players[0].mo->FindInventory (QuestItemClasses[27]))
|
|
|
|
{
|
|
|
|
FinalePart = 10;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
FinalePart = 17;
|
|
|
|
}
|
|
|
|
FinaleStage = 5;
|
|
|
|
FinaleEndCount = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void F_EndFinale ()
|
|
|
|
{
|
|
|
|
FinaleText = NULL;
|
|
|
|
FinaleTextLen = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool F_Responder (event_t *event)
|
|
|
|
{
|
|
|
|
if (FinaleStage == 3)
|
|
|
|
{
|
|
|
|
return F_CastResponder (event);
|
|
|
|
}
|
|
|
|
else if (FinaleStage == 2 && event->type == EV_KeyDown)
|
|
|
|
{ // We're showing the water pic; make any key kick to demo mode
|
|
|
|
FinaleStage = 4;
|
|
|
|
V_ForceBlend (0, 0, 0, 0);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// F_Ticker
|
|
|
|
//
|
|
|
|
void F_Ticker ()
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
bool interrupt = false;
|
|
|
|
|
|
|
|
// check for skipping
|
|
|
|
for (i = 0; i < MAXPLAYERS; i++)
|
|
|
|
{
|
|
|
|
// Only for buttons going down
|
|
|
|
if (!interrupt)
|
|
|
|
{
|
|
|
|
for (size_t j = 0; j < sizeof(players[i].cmd.ucmd.buttons)*8; ++j)
|
|
|
|
{
|
|
|
|
if (((players[i].cmd.ucmd.buttons >> j) & 1) &&
|
|
|
|
!((players[i].oldbuttons >> j) & 1))
|
|
|
|
{
|
|
|
|
interrupt = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
players[i].oldbuttons = players[i].cmd.ucmd.buttons;
|
|
|
|
}
|
|
|
|
|
|
|
|
// [RH] Non-commercial can be skipped now, too
|
|
|
|
if (FinaleStage == 0)
|
|
|
|
{
|
|
|
|
if (interrupt ||
|
|
|
|
((gamemode != commercial || gameinfo.flags & GI_SHAREWARE)
|
|
|
|
&& FinaleCount > FinaleTextLen*TEXTSPEED+TEXTWAIT))
|
|
|
|
{
|
|
|
|
if (FinaleCount < FinaleTextLen*TEXTSPEED+10)
|
|
|
|
{
|
|
|
|
FinaleCount = FinaleTextLen*TEXTSPEED+10;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (FinaleEnding)
|
|
|
|
{
|
|
|
|
// [RH] Don't automatically advance end-of-game messages
|
|
|
|
if (interrupt)
|
|
|
|
{
|
|
|
|
FinaleSequence = *((WORD *)&level.nextmap[6]);
|
|
|
|
if (EndSequences[FinaleSequence].EndType == END_Cast)
|
|
|
|
{
|
|
|
|
F_StartCast ();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
FinaleCount = 0;
|
|
|
|
FinaleStage = 1;
|
|
|
|
wipegamestate = GS_FORCEWIPE;
|
|
|
|
if (EndSequences[FinaleSequence].EndType == END_Bunny)
|
|
|
|
{
|
|
|
|
S_StartMusic ("d_bunny");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
gameaction = ga_worlddone;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (FinaleStage >= 10)
|
|
|
|
{
|
|
|
|
// Hexen chess ending with three pages of text.
|
|
|
|
// [RH] This can be interrupted to speed it up.
|
|
|
|
if (interrupt)
|
|
|
|
{
|
|
|
|
if (FinaleStage == 11 || FinaleStage == 12 || FinaleStage == 15)
|
|
|
|
{ // Stages that display text
|
|
|
|
if (FinaleCount < FinaleEndCount-TEXTWAIT)
|
|
|
|
{
|
|
|
|
FinaleCount = FinaleEndCount-TEXTWAIT;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
FinaleCount = FinaleEndCount;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (FinaleCount < 69)
|
|
|
|
{ // Stages that fade pictures
|
|
|
|
FinaleCount = 69;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (FinaleStage < 15 && FinaleCount >= FinaleEndCount)
|
|
|
|
{
|
|
|
|
FinaleCount = 0;
|
|
|
|
FinaleStage++;
|
|
|
|
switch (FinaleStage)
|
|
|
|
{
|
|
|
|
case 11: // Text 1
|
|
|
|
FinaleEndCount = FinaleTextLen*TEXTSPEED+TEXTWAIT;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 12: // Pic 2, Text 2
|
|
|
|
GetFinaleText ("win2msg");
|
|
|
|
FinaleEndCount = FinaleTextLen*TEXTSPEED+TEXTWAIT;
|
|
|
|
S_ChangeMusic ("orb", 0, !(gameinfo.flags & GI_NOLOOPFINALEMUSIC));
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 13: // Pic 2 -- Fade out
|
|
|
|
FinaleEndCount = 70;
|
|
|
|
FadeDir = 1;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 14: // Pic 3 -- Fade in
|
|
|
|
FinaleEndCount = 71;
|
|
|
|
FadeDir = -1;
|
|
|
|
S_ChangeMusic ("chess", 0, !(gameinfo.flags & GI_NOLOOPFINALEMUSIC));
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 15: // Pic 3, Text 3
|
|
|
|
GetFinaleText ("win3msg");
|
|
|
|
FinaleEndCount = FinaleTextLen*TEXTSPEED+TEXTWAIT;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (FinaleStage == 10 || FinaleStage == 13 || FinaleStage == 14)
|
|
|
|
{
|
|
|
|
FadePic ();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (FinaleStage >= 5)
|
|
|
|
{ // Strife slideshow
|
|
|
|
if (interrupt)
|
|
|
|
{
|
|
|
|
FinaleCount = FinaleEndCount;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// advance animation
|
|
|
|
FinaleCount++;
|
|
|
|
|
|
|
|
if (FinaleStage == 3)
|
|
|
|
{
|
|
|
|
F_CastTicker ();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else if (FinaleStage == 5 && FinaleCount > FinaleEndCount)
|
|
|
|
{
|
|
|
|
S_StopSound ((fixed_t *)NULL, CHAN_VOICE);
|
|
|
|
F_AdvanceSlideshow ();
|
|
|
|
FinaleCount = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// FadePic
|
|
|
|
//
|
|
|
|
//===========================================================================
|
|
|
|
|
|
|
|
static void FadePic ()
|
|
|
|
{
|
|
|
|
int blend = int(256*FinaleCount/70);
|
|
|
|
|
|
|
|
if (FadeDir < 0)
|
|
|
|
{
|
|
|
|
blend = 256 - blend;
|
|
|
|
}
|
|
|
|
V_SetBlend (0,0,0,blend);
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// F_TextWrite
|
|
|
|
//
|
|
|
|
void F_TextWrite (void)
|
|
|
|
{
|
|
|
|
FTexture *pic;
|
|
|
|
int w;
|
|
|
|
size_t count;
|
|
|
|
const char *ch;
|
|
|
|
int c;
|
|
|
|
int cx;
|
|
|
|
int cy;
|
|
|
|
const FRemapTable *range;
|
|
|
|
int leftmargin;
|
|
|
|
int rowheight;
|
|
|
|
bool scale;
|
|
|
|
|
|
|
|
if (FinaleCount < 11)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// draw some of the text onto the screen
|
|
|
|
leftmargin = (gameinfo.gametype & (GAME_Doom|GAME_Strife|GAME_Hexen) ? 10 : 20) - 160;
|
|
|
|
rowheight = screen->Font->GetHeight () +
|
|
|
|
(gameinfo.gametype & (GAME_Doom|GAME_Strife) ? 3 : -1);
|
|
|
|
scale = (CleanXfac != 1 || CleanYfac != 1);
|
|
|
|
|
|
|
|
cx = leftmargin;
|
|
|
|
if (FinaleStage == 15)
|
|
|
|
{
|
|
|
|
cy = 135 - 100;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
cy = (gameinfo.gametype & (GAME_Doom|GAME_Strife) ? 10 : 5) - 100;
|
|
|
|
}
|
|
|
|
ch = FinaleText.GetChars();
|
|
|
|
|
|
|
|
count = (FinaleCount - 10)/TEXTSPEED;
|
|
|
|
range = screen->Font->GetColorTranslation (CR_UNTRANSLATED);
|
|
|
|
|
|
|
|
for ( ; count ; count-- )
|
|
|
|
{
|
|
|
|
c = *ch++;
|
|
|
|
if (!c)
|
|
|
|
break;
|
|
|
|
if (c == '\n')
|
|
|
|
{
|
|
|
|
cx = leftmargin;
|
|
|
|
cy += rowheight;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
pic = screen->Font->GetChar (c, &w);
|
|
|
|
if (cx+w > SCREENWIDTH)
|
|
|
|
continue;
|
|
|
|
if (pic != NULL)
|
|
|
|
{
|
|
|
|
if (scale)
|
|
|
|
{
|
|
|
|
screen->DrawTexture (pic,
|
|
|
|
cx + 320 / 2,
|
|
|
|
cy + 200 / 2,
|
|
|
|
DTA_Translation, range,
|
|
|
|
DTA_Clean, true,
|
|
|
|
TAG_DONE);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
screen->DrawTexture (pic,
|
|
|
|
cx + 320 / 2,
|
|
|
|
cy + 200 / 2,
|
|
|
|
DTA_Translation, range,
|
|
|
|
TAG_DONE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
cx += w;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Final DOOM 2 animation
|
|
|
|
// Casting by id Software.
|
|
|
|
// in order of appearance
|
|
|
|
//
|
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
const char *name;
|
|
|
|
const char *type;
|
|
|
|
const AActor *info;
|
|
|
|
const PClass *Class;
|
|
|
|
} castinfo_t;
|
|
|
|
|
|
|
|
castinfo_t castorder[] =
|
|
|
|
{
|
|
|
|
{"CC_ZOMBIE", "ZombieMan"},
|
|
|
|
{"CC_SHOTGUN", "ShotgunGuy"},
|
|
|
|
{"CC_HEAVY", "ChaingunGuy"},
|
|
|
|
{"CC_IMP", "DoomImp"},
|
|
|
|
{"CC_DEMON", "Demon"},
|
|
|
|
{"CC_LOST", "LostSoul"},
|
|
|
|
{"CC_CACO", "Cacodemon"},
|
|
|
|
{"CC_HELL", "HellKnight"},
|
|
|
|
{"CC_BARON", "BaronOfHell"},
|
|
|
|
{"CC_ARACH", "Arachnotron"},
|
|
|
|
{"CC_PAIN", "PainElemental"},
|
|
|
|
{"CC_REVEN", "Revenant"},
|
|
|
|
{"CC_MANCU", "Fatso"},
|
|
|
|
{"CC_ARCH", "Archvile"},
|
|
|
|
{"CC_SPIDER", "SpiderMastermind"},
|
|
|
|
{"CC_CYBER", "Cyberdemon"},
|
|
|
|
{"CC_HERO", "DoomPlayer"},
|
|
|
|
|
|
|
|
{0, NULL}
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct
|
|
|
|
{
|
|
|
|
const char *type;
|
|
|
|
BYTE melee;
|
|
|
|
BYTE ofs;
|
|
|
|
const char *sound;
|
|
|
|
FState *match;
|
|
|
|
} atkstates[] =
|
|
|
|
{
|
|
|
|
{ "DoomPlayer", 0, 0, "weapons/sshotf" },
|
|
|
|
{ "ZombieMan", 0, 1, "grunt/attack" },
|
|
|
|
{ "ShotgunGuy", 0, 1, "shotguy/attack" },
|
|
|
|
{ "Archvile", 0, 1, "vile/start" },
|
|
|
|
{ "Revenant", 1, 1, "skeleton/swing" },
|
|
|
|
{ "Revenant", 1, 3, "skeleton/melee" },
|
|
|
|
{ "Revenant", 0, 1, "skeleton/attack" },
|
|
|
|
{ "Fatso", 0, 1, "fatso/attack" },
|
|
|
|
{ "Fatso", 0, 4, "fatso/attack" },
|
|
|
|
{ "Fatso", 0, 7, "fatso/attack" },
|
|
|
|
{ "ChaingunGuy", 0, 1, "chainguy/attack" },
|
|
|
|
{ "ChaingunGuy", 0, 2, "chainguy/attack" },
|
|
|
|
{ "ChaingunGuy", 0, 3, "chainguy/attack" },
|
|
|
|
{ "DoomImp", 0, 2, "imp/attack" },
|
|
|
|
{ "Demon", 1, 1, "demon/melee" },
|
|
|
|
{ "BaronOfHell", 0, 1, "baron/attack" },
|
|
|
|
{ "HellKnight", 0, 1, "baron/attack" },
|
|
|
|
{ "Cacodemon", 0, 1, "caco/attack" },
|
|
|
|
{ "LostSoul", 0, 1, "skull/melee" },
|
|
|
|
{ "SpiderMastermind", 0, 1, "spider/attack" },
|
|
|
|
{ "SpiderMastermind", 0, 2, "spider/attack" },
|
|
|
|
{ "Arachnotron", 0, 1, "baby/attack" },
|
|
|
|
{ "Cyberdemon", 0, 1, "weapons/rocklf" },
|
|
|
|
{ "Cyberdemon", 0, 3, "weapons/rocklf" },
|
|
|
|
{ "Cyberdemon", 0, 5, "weapons/rocklf" },
|
|
|
|
{ "PainElemental", 0, 2, "skull/melee" },
|
|
|
|
{ NULL }
|
|
|
|
};
|
|
|
|
|
|
|
|
int castnum;
|
|
|
|
int casttics;
|
|
|
|
int castsprite; // [RH] For overriding the player sprite with a skin
|
|
|
|
const FRemapTable *casttranslation; // [RH] Draw "our hero" with their chosen suit color
|
|
|
|
FState* caststate;
|
|
|
|
bool castdeath;
|
|
|
|
int castframes;
|
|
|
|
int castonmelee;
|
|
|
|
bool castattacking;
|
|
|
|
|
|
|
|
static FState *advplayerstate;
|
|
|
|
|
|
|
|
//
|
|
|
|
// F_StartCast
|
|
|
|
//
|
|
|
|
extern gamestate_t wipegamestate;
|
|
|
|
|
|
|
|
|
|
|
|
void F_StartCast (void)
|
|
|
|
{
|
|
|
|
const PClass *type;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
// [RH] Set the names and defaults for the cast
|
|
|
|
for (i = 0; castorder[i].type; i++)
|
|
|
|
{
|
|
|
|
type = PClass::FindClass (castorder[i].type);
|
|
|
|
if (type == NULL)
|
|
|
|
{
|
|
|
|
castorder[i].info = GetDefault<AActor>();
|
|
|
|
castorder[i].Class= RUNTIME_CLASS(AActor);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
castorder[i].info = GetDefaultByType (type);
|
|
|
|
castorder[i].Class= type;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; atkstates[i].type; i++)
|
|
|
|
{
|
|
|
|
type = PClass::FindClass (atkstates[i].type);
|
|
|
|
if (type != NULL)
|
|
|
|
{
|
|
|
|
if (atkstates[i].melee)
|
|
|
|
atkstates[i].match = ((AActor *)(type->Defaults))->MeleeState + atkstates[i].ofs;
|
|
|
|
else
|
|
|
|
atkstates[i].match = ((AActor *)(type->Defaults))->MissileState + atkstates[i].ofs;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
atkstates[i].match = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type = PClass::FindClass (NAME_DoomPlayer);
|
|
|
|
if (type != NULL)
|
|
|
|
advplayerstate = ((AActor *)(type->Defaults))->MissileState;
|
|
|
|
|
|
|
|
wipegamestate = GS_FORCEWIPE;
|
|
|
|
castnum = 0;
|
|
|
|
caststate = castorder[castnum].info->SeeState;
|
|
|
|
castsprite = caststate->sprite.index;
|
|
|
|
casttranslation = NULL;
|
|
|
|
casttics = caststate->GetTics ();
|
|
|
|
castdeath = false;
|
|
|
|
FinaleStage = 3;
|
|
|
|
castframes = 0;
|
|
|
|
castonmelee = 0;
|
|
|
|
castattacking = false;
|
|
|
|
S_ChangeMusic ("d_evil");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// F_CastTicker
|
|
|
|
//
|
|
|
|
void F_CastTicker (void)
|
|
|
|
{
|
|
|
|
if (--casttics > 0 && caststate != NULL)
|
|
|
|
return; // not time to change state yet
|
|
|
|
|
|
|
|
if (caststate == NULL || caststate->GetTics() == -1 || caststate->GetNextState() == NULL)
|
|
|
|
{
|
|
|
|
// switch from deathstate to next monster
|
|
|
|
do
|
|
|
|
{
|
|
|
|
castnum++;
|
|
|
|
castdeath = false;
|
|
|
|
if (castorder[castnum].name == 0)
|
|
|
|
castnum = 0;
|
|
|
|
if (castorder[castnum].info->SeeSound)
|
|
|
|
{
|
2008-03-21 21:15:56 +00:00
|
|
|
S_SoundID (CHAN_VOICE, castorder[castnum].info->SeeSound, 1, ATTN_NONE);
|
2008-01-27 11:25:03 +00:00
|
|
|
}
|
|
|
|
caststate = castorder[castnum].info->SeeState;
|
|
|
|
// [RH] Skip monsters that have been hacked to no longer have attack states
|
|
|
|
if (castorder[castnum].info->MissileState == NULL &&
|
|
|
|
castorder[castnum].info->MeleeState == NULL)
|
|
|
|
{
|
|
|
|
caststate = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
while (caststate == NULL);
|
|
|
|
if (castnum == 16)
|
|
|
|
{
|
|
|
|
castsprite = skins[players[consoleplayer].userinfo.skin].sprite;
|
|
|
|
casttranslation = translationtables[TRANSLATION_Players][consoleplayer];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
castsprite = caststate->sprite.index;
|
|
|
|
casttranslation = NULL;
|
|
|
|
}
|
|
|
|
castframes = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// sound hacks....
|
|
|
|
if (caststate != NULL)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; atkstates[i].type; i++)
|
|
|
|
{
|
|
|
|
if (atkstates[i].match == caststate)
|
|
|
|
{
|
|
|
|
S_StopAllChannels ();
|
|
|
|
S_Sound (CHAN_WEAPON, atkstates[i].sound, 1, ATTN_NONE);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// just advance to next state in animation
|
|
|
|
if (caststate == advplayerstate)
|
|
|
|
goto stopattack; // Oh, gross hack!
|
|
|
|
|
|
|
|
caststate = caststate->GetNextState();
|
|
|
|
castframes++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (castframes == 12)
|
|
|
|
{
|
|
|
|
// go into attack frame
|
|
|
|
castattacking = true;
|
|
|
|
if (castonmelee)
|
|
|
|
caststate = castorder[castnum].info->MeleeState;
|
|
|
|
else
|
|
|
|
caststate = castorder[castnum].info->MissileState;
|
|
|
|
castonmelee ^= 1;
|
|
|
|
if (caststate == NULL)
|
|
|
|
{
|
|
|
|
if (castonmelee)
|
|
|
|
caststate = castorder[castnum].info->MeleeState;
|
|
|
|
else
|
|
|
|
caststate = castorder[castnum].info->MissileState;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (castattacking)
|
|
|
|
{
|
|
|
|
if (castframes == 24
|
|
|
|
|| caststate == castorder[castnum].info->SeeState )
|
|
|
|
{
|
|
|
|
stopattack:
|
|
|
|
castattacking = false;
|
|
|
|
castframes = 0;
|
|
|
|
caststate = castorder[castnum].info->SeeState;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
casttics = caststate->GetTics();
|
|
|
|
if (casttics == -1)
|
|
|
|
casttics = 15;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// F_CastResponder
|
|
|
|
//
|
|
|
|
|
|
|
|
bool F_CastResponder (event_t* ev)
|
|
|
|
{
|
|
|
|
if (ev->type != EV_KeyDown)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
const char *cmd = C_GetBinding (ev->data1);
|
|
|
|
|
|
|
|
if (cmd != NULL && !stricmp (cmd, "toggleconsole"))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (castdeath)
|
|
|
|
return true; // already in dying frames
|
|
|
|
|
|
|
|
// go into death frame
|
|
|
|
castdeath = true;
|
|
|
|
caststate = castorder[castnum].Class->ActorInfo->FindState(NAME_Death);
|
|
|
|
if (caststate != NULL)
|
|
|
|
{
|
|
|
|
casttics = caststate->GetTics();
|
|
|
|
castframes = 0;
|
|
|
|
castattacking = false;
|
|
|
|
if (castnum == 16)
|
|
|
|
{
|
|
|
|
//int id = S_LookupPlayerSound (
|
|
|
|
S_Sound (players[consoleplayer].mo, CHAN_VOICE, "*death", 1, ATTN_NONE);
|
|
|
|
}
|
|
|
|
else if (castorder[castnum].info->DeathSound)
|
|
|
|
{
|
2008-03-21 21:15:56 +00:00
|
|
|
S_SoundID (CHAN_VOICE, castorder[castnum].info->DeathSound, 1, ATTN_NONE);
|
2008-01-27 11:25:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// F_CastDrawer
|
|
|
|
//
|
|
|
|
void F_CastDrawer (void)
|
|
|
|
{
|
|
|
|
spriteframe_t* sprframe;
|
|
|
|
FTexture* pic;
|
|
|
|
|
|
|
|
// erase the entire screen to a background
|
|
|
|
screen->DrawTexture (TexMan["BOSSBACK"], 0, 0,
|
|
|
|
DTA_DestWidth, screen->GetWidth(),
|
|
|
|
DTA_DestHeight, screen->GetHeight(),
|
|
|
|
TAG_DONE);
|
|
|
|
|
|
|
|
screen->DrawText (CR_RED,
|
|
|
|
(SCREENWIDTH - SmallFont->StringWidth (GStrings(castorder[castnum].name)) * CleanXfac)/2,
|
|
|
|
(SCREENHEIGHT * 180) / 200,
|
|
|
|
GStrings(castorder[castnum].name),
|
|
|
|
DTA_CleanNoMove, true, TAG_DONE);
|
|
|
|
|
|
|
|
// draw the current frame in the middle of the screen
|
|
|
|
if (caststate != NULL)
|
|
|
|
{
|
|
|
|
sprframe = &SpriteFrames[sprites[castsprite].spriteframes + caststate->GetFrame()];
|
|
|
|
pic = TexMan(sprframe->Texture[0]);
|
|
|
|
|
|
|
|
screen->DrawTexture (pic, 160, 170,
|
|
|
|
DTA_320x200, true,
|
|
|
|
DTA_FlipX, sprframe->Flip & 1,
|
|
|
|
DTA_Translation, casttranslation,
|
|
|
|
TAG_DONE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
==================
|
|
|
|
=
|
|
|
|
= F_DemonScroll
|
|
|
|
=
|
|
|
|
==================
|
|
|
|
*/
|
|
|
|
|
|
|
|
void F_DemonScroll ()
|
|
|
|
{
|
|
|
|
int yval;
|
|
|
|
FTexture *final1 = TexMan("FINAL1");
|
|
|
|
int fwidth = final1->GetWidth();
|
|
|
|
int fheight = final1->GetHeight();
|
|
|
|
|
|
|
|
if (FinaleCount < 70)
|
|
|
|
{
|
|
|
|
screen->DrawTexture (final1, 0, 0,
|
|
|
|
DTA_VirtualWidth, fwidth,
|
|
|
|
DTA_VirtualHeight, fheight,
|
|
|
|
DTA_Masked, false,
|
|
|
|
TAG_DONE);
|
|
|
|
screen->FillBorder (NULL);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
yval = int(FinaleCount) - 70;
|
|
|
|
if (yval < 600)
|
|
|
|
{
|
|
|
|
yval = Scale (yval, fheight, 600);
|
|
|
|
screen->DrawTexture (final1, 0, yval,
|
|
|
|
DTA_VirtualWidth, fwidth,
|
|
|
|
DTA_VirtualHeight, fheight,
|
|
|
|
DTA_Masked, false,
|
|
|
|
TAG_DONE);
|
|
|
|
screen->DrawTexture (TexMan("FINAL2"), 0, yval - fheight,
|
|
|
|
DTA_VirtualWidth, fwidth,
|
|
|
|
DTA_VirtualHeight, fheight,
|
|
|
|
DTA_Masked, false,
|
|
|
|
TAG_DONE);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{ //else, we'll just sit here and wait, for now
|
|
|
|
screen->DrawTexture (TexMan("FINAL2"), 0, 0,
|
|
|
|
DTA_VirtualWidth, fwidth,
|
|
|
|
DTA_VirtualHeight, fheight,
|
|
|
|
DTA_Masked, false,
|
|
|
|
TAG_DONE);
|
|
|
|
}
|
|
|
|
screen->FillBorder (NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
==================
|
|
|
|
=
|
|
|
|
= F_DrawUnderwater
|
|
|
|
=
|
|
|
|
==================
|
|
|
|
*/
|
|
|
|
extern int NoWipe;
|
|
|
|
|
|
|
|
void F_DrawUnderwater(void)
|
|
|
|
{
|
|
|
|
extern EMenuState menuactive;
|
|
|
|
FTexture *pic;
|
|
|
|
|
|
|
|
switch (FinaleStage)
|
|
|
|
{
|
|
|
|
case 1:
|
|
|
|
{
|
|
|
|
PalEntry *palette;
|
|
|
|
const BYTE *orgpal;
|
|
|
|
FMemLump lump;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
lump = Wads.ReadLump ("E2PAL");
|
|
|
|
orgpal = (BYTE *)lump.GetMem();
|
|
|
|
palette = screen->GetPalette ();
|
|
|
|
for (i = 256; i > 0; i--, orgpal += 3)
|
|
|
|
{
|
|
|
|
*palette++ = PalEntry (orgpal[0], orgpal[1], orgpal[2]);
|
|
|
|
}
|
|
|
|
screen->UpdatePalette ();
|
|
|
|
FinaleStage = 2;
|
|
|
|
}
|
|
|
|
// intentional fall-through
|
|
|
|
case 2:
|
|
|
|
pic = TexMan("E2END");
|
|
|
|
screen->DrawTexture (pic, 0, 0,
|
|
|
|
DTA_VirtualWidth, pic->GetWidth(),
|
|
|
|
DTA_VirtualHeight, pic->GetHeight(),
|
|
|
|
TAG_DONE);
|
|
|
|
screen->FillBorder (NULL);
|
|
|
|
paused = false;
|
|
|
|
menuactive = MENU_Off;
|
|
|
|
NoWipe = -1;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 4:
|
|
|
|
{
|
|
|
|
PalEntry *palette;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
palette = screen->GetPalette ();
|
|
|
|
for (i = 0; i < 256; ++i)
|
|
|
|
{
|
|
|
|
palette[i] = GPalette.BaseColors[i];
|
|
|
|
}
|
|
|
|
screen->UpdatePalette ();
|
|
|
|
|
|
|
|
pic = TexMan("TITLE");
|
|
|
|
screen->DrawTexture (pic, 0, 0,
|
|
|
|
DTA_VirtualWidth, pic->GetWidth(),
|
|
|
|
DTA_VirtualHeight, pic->GetHeight(),
|
|
|
|
TAG_DONE);
|
|
|
|
screen->FillBorder (NULL);
|
|
|
|
NoWipe = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
==================
|
|
|
|
=
|
|
|
|
= F_BunnyScroll
|
|
|
|
=
|
|
|
|
==================
|
|
|
|
*/
|
|
|
|
void F_BunnyScroll (void)
|
|
|
|
{
|
|
|
|
static const char tex1name[2][8] = { "CREDIT", "PFUB1" };
|
|
|
|
static const char tex2name[2][8] = { "VELLOGO", "PFUB2" };
|
|
|
|
|
|
|
|
static size_t laststage;
|
|
|
|
|
|
|
|
bool bunny = EndSequences[FinaleSequence].EndType != END_BuyStrife;
|
|
|
|
int scrolled;
|
|
|
|
char name[10];
|
|
|
|
size_t stage;
|
|
|
|
FTexture *tex;
|
|
|
|
int fwidth;
|
|
|
|
int fheight;
|
|
|
|
|
|
|
|
V_MarkRect (0, 0, SCREENWIDTH, SCREENHEIGHT);
|
|
|
|
|
|
|
|
tex = TexMan(tex1name[bunny]);
|
|
|
|
fwidth = tex->GetWidth();
|
|
|
|
fheight = tex->GetHeight();
|
|
|
|
|
|
|
|
scrolled = clamp (((signed)FinaleCount-230)*fwidth/640, 0, fwidth);
|
|
|
|
|
|
|
|
tex = TexMan(tex1name[bunny]);
|
|
|
|
screen->DrawTexture (tex, scrolled, 0,
|
|
|
|
DTA_VirtualWidth, fwidth,
|
|
|
|
DTA_VirtualHeight, fheight,
|
|
|
|
DTA_Masked, false,
|
|
|
|
TAG_DONE);
|
|
|
|
|
|
|
|
tex = TexMan(tex2name[bunny]);
|
|
|
|
screen->DrawTexture (tex, scrolled - fwidth, 0,
|
|
|
|
DTA_VirtualWidth, fwidth,
|
|
|
|
DTA_VirtualHeight, fheight,
|
|
|
|
DTA_Masked, false,
|
|
|
|
TAG_DONE);
|
|
|
|
|
|
|
|
screen->FillBorder (NULL);
|
|
|
|
|
|
|
|
if (bunny)
|
|
|
|
{
|
|
|
|
if (FinaleCount < 1130)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (FinaleCount < 1180)
|
|
|
|
{
|
|
|
|
screen->DrawTexture (TexMan("END0"), (320-13*8)/2, (200-8*8)/2, DTA_320x200, true, TAG_DONE);
|
|
|
|
laststage = 0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
stage = (FinaleCount-1180) / 5;
|
|
|
|
if (stage > 6)
|
|
|
|
stage = 6;
|
|
|
|
if (stage > laststage)
|
|
|
|
{
|
|
|
|
S_Sound (CHAN_WEAPON, "weapons/pistol", 1, ATTN_NONE);
|
|
|
|
laststage = stage;
|
|
|
|
}
|
|
|
|
|
2008-05-14 17:49:11 +00:00
|
|
|
sprintf (name, "END%d", (int)stage);
|
2008-01-27 11:25:03 +00:00
|
|
|
screen->DrawTexture (TexMan(name), (320-13*8)/2, (200-8*8)/2, DTA_320x200, true, TAG_DONE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
//
|
|
|
|
// F_StartSlideshow
|
|
|
|
//
|
|
|
|
// Starts running the slideshow previously set up.
|
|
|
|
//
|
|
|
|
//============================================================================
|
|
|
|
|
|
|
|
void F_StartSlideshow ()
|
|
|
|
{
|
|
|
|
gameaction = ga_nothing;
|
|
|
|
gamestate = GS_FINALE;
|
|
|
|
wipegamestate = GS_FINALE;
|
|
|
|
viewactive = false;
|
|
|
|
automapactive = false;
|
|
|
|
|
|
|
|
S_StopAllChannels ();
|
|
|
|
S_ChangeMusic ("D_DARK", 0, true);
|
|
|
|
V_SetBlend (0,0,0,0);
|
|
|
|
|
|
|
|
// The slideshow is determined solely by the map you're on.
|
|
|
|
if (!multiplayer && level.flags & LEVEL_DEATHSLIDESHOW)
|
|
|
|
{
|
|
|
|
FinalePart = 14;
|
|
|
|
}
|
|
|
|
else switch (level.levelnum)
|
|
|
|
{
|
|
|
|
case 3:
|
|
|
|
FinalePart = 1;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 10:
|
|
|
|
FinalePart = 5;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
FinalePart = -99;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
FinaleCount = 0;
|
|
|
|
FinaleStage = 5;
|
|
|
|
FinaleEndCount = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//============================================================================
|
|
|
|
//
|
|
|
|
// F_AdvanceSlideshow
|
|
|
|
//
|
|
|
|
//============================================================================
|
|
|
|
|
|
|
|
void F_AdvanceSlideshow ()
|
|
|
|
{
|
|
|
|
switch (FinalePart)
|
|
|
|
{
|
|
|
|
case -99:
|
|
|
|
if (level.cdtrack == 0 || !S_ChangeCDMusic (level.cdtrack, level.cdid))
|
|
|
|
S_ChangeMusic (level.music, level.musicorder);
|
|
|
|
gamestate = GS_LEVEL;
|
|
|
|
wipegamestate = GS_LEVEL;
|
|
|
|
P_ResumeConversation ();
|
|
|
|
viewactive = true;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case -1:
|
|
|
|
wipegamestate = GS_FORCEWIPEFADE;
|
|
|
|
FinaleStage = 6;
|
|
|
|
S_StartMusic ("D_FAST");
|
|
|
|
break;
|
|
|
|
|
|
|
|
// Macil's speech on map 3 about the Programmer.
|
|
|
|
case 1:
|
|
|
|
FinaleFlat = "SS2F1";
|
|
|
|
S_Sound (CHAN_VOICE, "svox/mac10", 1, ATTN_NORM);
|
|
|
|
FinalePart = 2;
|
|
|
|
FinaleEndCount = 9 * TICRATE;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 2:
|
|
|
|
FinaleFlat = "SS2F2";
|
|
|
|
S_Sound (CHAN_VOICE, "svox/mac11", 1, ATTN_NORM);
|
|
|
|
FinalePart = 3;
|
|
|
|
FinaleEndCount = 10 * TICRATE;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 3:
|
|
|
|
FinaleFlat = "SS2F3";
|
|
|
|
S_Sound (CHAN_VOICE, "svox/mac12", 1, ATTN_NORM);
|
|
|
|
FinalePart = 4;
|
|
|
|
FinaleEndCount = 12 * TICRATE;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 4:
|
|
|
|
FinaleFlat = "SS2F4";
|
|
|
|
S_Sound (CHAN_VOICE, "svox/mac13", 1, ATTN_NORM);
|
|
|
|
FinalePart = -99;
|
|
|
|
FinaleEndCount = 17 * TICRATE;
|
|
|
|
break;
|
|
|
|
|
|
|
|
// Macil's speech on map 10 about the Sigil.
|
|
|
|
case 5:
|
|
|
|
FinaleFlat = "SS3F1";
|
|
|
|
S_Sound (CHAN_VOICE, "svox/mac16", 1, ATTN_NORM);
|
|
|
|
FinalePart = 6;
|
|
|
|
FinaleEndCount = 10 * TICRATE;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 6:
|
|
|
|
FinaleFlat = "SS3F2";
|
|
|
|
S_Sound (CHAN_VOICE, "svox/mac17", 1, ATTN_NORM);
|
|
|
|
FinalePart = 7;
|
|
|
|
FinaleEndCount = 12 * TICRATE;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 7:
|
|
|
|
FinaleFlat = "SS3F3";
|
|
|
|
S_Sound (CHAN_VOICE, "svox/mac18", 1, ATTN_NORM);
|
|
|
|
FinalePart = 8;
|
|
|
|
FinaleEndCount = 12 * TICRATE;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 8:
|
|
|
|
FinaleFlat = "SS3F4";
|
|
|
|
S_Sound (CHAN_VOICE, "svox/mac19", 1, ATTN_NORM);
|
|
|
|
FinaleEndCount = 11 * TICRATE;
|
|
|
|
FinalePart = -99;
|
|
|
|
break;
|
|
|
|
|
|
|
|
// You won! You are a hero!
|
|
|
|
case 10:
|
|
|
|
FinaleFlat = "SS4F1";
|
|
|
|
S_StartMusic ("D_HAPPY");
|
|
|
|
S_Sound (CHAN_VOICE, "svox/rie01", 1, ATTN_NORM);
|
|
|
|
FinaleEndCount = 13 * TICRATE;
|
|
|
|
FinalePart = 11;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 11:
|
|
|
|
FinaleFlat = "SS4F2";
|
|
|
|
S_Sound (CHAN_VOICE, "svox/bbx01", 1, ATTN_NORM);
|
|
|
|
FinaleEndCount = 11 * TICRATE;
|
|
|
|
FinalePart = 12;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 12:
|
|
|
|
FinaleFlat = "SS4F3";
|
|
|
|
S_Sound (CHAN_VOICE, "svox/bbx02", 1, ATTN_NORM);
|
|
|
|
FinaleEndCount = 14 * TICRATE;
|
|
|
|
FinalePart = 13;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 13:
|
|
|
|
FinaleFlat = "SS4F4";
|
|
|
|
FinaleEndCount = 28 * TICRATE;
|
|
|
|
FinalePart = -1;
|
|
|
|
break;
|
|
|
|
|
|
|
|
// You are dead! All hope is lost!
|
|
|
|
case 14:
|
|
|
|
S_StartMusic ("D_SAD");
|
|
|
|
FinaleFlat = "SS5F1";
|
|
|
|
S_Sound (CHAN_VOICE, "svox/ss501b", 1, ATTN_NORM);
|
|
|
|
FinalePart = 15;
|
|
|
|
FinaleEndCount = 11 * TICRATE;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 15:
|
|
|
|
FinaleFlat = "SS5F2";
|
|
|
|
S_Sound (CHAN_VOICE, "svox/ss502b", 1, ATTN_NORM);
|
|
|
|
FinalePart = 16;
|
|
|
|
FinaleEndCount = 10 * TICRATE;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 16:
|
|
|
|
FinaleFlat = "SS5F3";
|
|
|
|
S_Sound (CHAN_VOICE, "svox/ss503b", 1, ATTN_NORM);
|
|
|
|
FinalePart = -1;
|
|
|
|
FinaleEndCount = 11 * TICRATE;
|
|
|
|
break;
|
|
|
|
|
|
|
|
// You won, but at what cost?
|
|
|
|
case 17:
|
|
|
|
S_StartMusic ("D_END");
|
|
|
|
FinaleFlat = "SS6F1";
|
|
|
|
S_Sound (CHAN_VOICE, "svox/ss601a", 1, ATTN_NORM);
|
|
|
|
FinaleEndCount = 8 * TICRATE;
|
|
|
|
FinalePart = 18;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 18:
|
|
|
|
FinaleFlat = "SS6F2";
|
|
|
|
S_Sound (CHAN_VOICE, "svox/ss602a", 1, ATTN_NORM);
|
|
|
|
FinalePart = 19;
|
|
|
|
FinaleEndCount = 8 * TICRATE;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 19:
|
|
|
|
FinaleFlat = "SS6F3";
|
|
|
|
S_Sound (CHAN_VOICE, "svox/ss603a", 1, ATTN_NORM);
|
|
|
|
FinalePart = -1;
|
|
|
|
FinaleEndCount = 9 * TICRATE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// F_Drawer
|
|
|
|
//
|
|
|
|
void F_Drawer (void)
|
|
|
|
{
|
|
|
|
const char *picname = NULL;
|
|
|
|
|
|
|
|
switch (FinaleStage)
|
|
|
|
{
|
|
|
|
case 0: // Intermission or end-of-episode text
|
|
|
|
// erase the entire screen to a tiled background (or picture)
|
|
|
|
if (!FinaleHasPic)
|
|
|
|
{
|
|
|
|
int picnum = TexMan.CheckForTexture (FinaleFlat, FTexture::TEX_Flat, FTextureManager::TEXMAN_Overridable);
|
|
|
|
if (picnum >= 0)
|
|
|
|
{
|
|
|
|
screen->FlatFill (0,0, SCREENWIDTH, SCREENHEIGHT, TexMan(picnum));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
screen->Clear (0, 0, SCREENWIDTH, SCREENHEIGHT, 0, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
picname = FinaleFlat;
|
|
|
|
}
|
|
|
|
V_MarkRect (0, 0, SCREENWIDTH, SCREENHEIGHT);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 1:
|
|
|
|
case 2:
|
|
|
|
case 4:
|
|
|
|
switch (EndSequences[FinaleSequence].EndType)
|
|
|
|
{
|
|
|
|
default:
|
|
|
|
case END_Pic1:
|
|
|
|
picname = gameinfo.finalePage1;
|
|
|
|
screen->DrawTexture (TexMan[picname], 0, 0,
|
|
|
|
DTA_DestWidth, screen->GetWidth(),
|
|
|
|
DTA_DestHeight, screen->GetHeight(), TAG_DONE);
|
|
|
|
break;
|
|
|
|
case END_Pic2:
|
|
|
|
picname = gameinfo.finalePage2;
|
|
|
|
screen->DrawTexture (TexMan[picname], 0, 0,
|
|
|
|
DTA_DestWidth, screen->GetWidth(),
|
|
|
|
DTA_DestHeight, screen->GetHeight(), TAG_DONE);
|
|
|
|
break;
|
|
|
|
case END_Pic3:
|
|
|
|
picname = gameinfo.finalePage3;
|
|
|
|
screen->DrawTexture (TexMan[picname], 0, 0,
|
|
|
|
DTA_DestWidth, screen->GetWidth(),
|
|
|
|
DTA_DestHeight, screen->GetHeight(), TAG_DONE);
|
|
|
|
break;
|
|
|
|
case END_Pic:
|
|
|
|
picname = EndSequences[FinaleSequence].PicName;
|
|
|
|
break;
|
|
|
|
case END_Bunny:
|
|
|
|
case END_BuyStrife:
|
|
|
|
F_BunnyScroll ();
|
|
|
|
break;
|
|
|
|
case END_Underwater:
|
|
|
|
F_DrawUnderwater ();
|
|
|
|
break;
|
|
|
|
case END_Demon:
|
|
|
|
F_DemonScroll ();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 3:
|
|
|
|
F_CastDrawer ();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 5:
|
|
|
|
picname = FinaleFlat;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 6:
|
|
|
|
picname = "CREDIT";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 10:
|
|
|
|
case 11:
|
|
|
|
picname = "FINALE1";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 12:
|
|
|
|
case 13:
|
|
|
|
picname = "FINALE2";
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 14:
|
|
|
|
case 15:
|
|
|
|
picname = "FINALE3";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (picname != NULL)
|
|
|
|
{
|
|
|
|
FTexture *pic = TexMan[picname];
|
|
|
|
screen->DrawTexture (pic, 0, 0,
|
|
|
|
DTA_VirtualWidth, pic->GetWidth(),
|
|
|
|
DTA_VirtualHeight, pic->GetHeight(),
|
|
|
|
TAG_DONE);
|
|
|
|
screen->FillBorder (NULL);
|
|
|
|
if (FinaleStage >= 14)
|
|
|
|
{ // Chess pic, draw the correct character graphic
|
|
|
|
if (multiplayer)
|
|
|
|
{
|
|
|
|
screen->DrawTexture (TexMan["CHESSALL"], 20, 0,
|
|
|
|
DTA_VirtualWidth, pic->GetWidth(),
|
|
|
|
DTA_VirtualHeight, pic->GetHeight(), TAG_DONE);
|
|
|
|
}
|
|
|
|
else if (players[consoleplayer].CurrentPlayerClass > 0)
|
|
|
|
{
|
|
|
|
picname = players[consoleplayer].CurrentPlayerClass == 1 ? "CHESSC" : "CHESSM";
|
|
|
|
screen->DrawTexture (TexMan[picname], 60, 0,
|
|
|
|
DTA_VirtualWidth, pic->GetWidth(),
|
|
|
|
DTA_VirtualHeight, pic->GetHeight(), TAG_DONE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
switch (FinaleStage)
|
|
|
|
{
|
|
|
|
case 0:
|
|
|
|
case 11:
|
|
|
|
case 12:
|
|
|
|
case 15:
|
|
|
|
F_TextWrite ();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// GetFinaleText
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
|
|
|
static void GetFinaleText (const char *msgLumpName)
|
|
|
|
{
|
|
|
|
int msgLump;
|
|
|
|
|
- Fixed: The players were not added to FS's list of spawned things.
- Update to ZDoom r882
- Added the option to use $ as a prefix to a string table name everywhere in
MAPINFO where 'lookup' could be specified so that there is one consistent
way to do it.
- Externalized all default episode definitions. Added an 'optional' keyword
to handle M4 and 5 in Doom and Heretic.
- Added P_CheckMapData function and replaced all calls to P_OpenMapData that
only checked for a map's presence with it.
- Added Martin Howe's player statusbar face submission.
- Added an 'adddefaultmap' option for MAPINFO. This is the same as 'defaultmap'
but keeps all existing information in the default and just adds to it. This
is needed because Hexen and Strife set some information in their base
MAPINFO and using 'defaultmap' in a PWAD would override that.
- Fixed: Using MAPINFO's f1 option could cause memory leaks.
- Added option to load lumps by full name to several places:
* Finale texts loaded from a text lump
* Demos
* Local SNDINFOs
* Local SNDSEQs
* Image names in FONTDEFS
* intermission script names
- Changed the STCFN121 handling. The character is not an 'I' but a '|' so
instead of discarding it it should be inserted at position 124.
- Renamed indexfont.fon to indexfont so that I could remove a special case
from V_GetFont that was just added for this one font.
- Added a 'dumpspawnedthings' CVAR that enables a listing of all things in
the map and the actor type they spawned.
SBarInfo Update #16
- Added: fillzeros flag for drawnumber. When set the string will always have
a length of the specified size and zeros will fill in for the missing places.
If the number is negative the negative sign will take the place of the last
digit.
- Added: globalarray type to drawnumber which will display the value in a
global array with the index set to the player's number. Untested.
- Added: isselected command to SBarInfo.
- Fixed: Bi and Tri colored numbers didn't work.
- Fixed: Crash when using nullimage as the last image in drawswitchableimage.
- Applied Graf suggestion to include the y coord when calulating heights to fix
most of the gaps caused by round off errors. At least for now anyways and it
is only applied for drawimage.
- SBarInfo inventory bars have been converted to use screen->DrawTexture()
- Increased limit for demon/melee to 4.
- Fixed: P_CheckSwitchRange accessed invalid memory when testing a one-sided
line.
- Fixed: P_SpawnPuff assumed that all melee attacks have the same range
(MELEERANGE) and didn't set the puff to its melee state if the range
was different. Even worse, it checked a global variable for this so
the behavior was undefined when P_SpawnPuff was called from anywhere
else but P_LineAttack. To reduce the amount of parameters I combined
this information with the hitthing and temporary parameters into one
flags parameter. Also changed P_LineAttack so that it gets passed
an additional parameter that specifies whether the attack is a melee
attack or not and set this to true in all calls that are to be considered
melee attacks. I couldn't use the damage type because A_CustomPunch
and A_CustomMeleeAttack allow passing any damage type they want.
- Added a sprite option as an alternative of particles for FX_ROCKET
and FX_GRENADE.
- Fixed: The minimum parameter count for ACS_Execute and ACS_ExecuteAlways for
DECORATE was wrong (2 instead of 1.)
- Changed: Hexen set every cluster to be a hub if it hadn't been defined before
a level using this cluster. Now it will only do that if HexenHack is true,
i.e. when original Hexen format MAPINFOs are parsed. For ZDoom format
MAPINFOs it will now be the same as for the other games which means that
'hub' has to be declared explicitly.
- Added an Idle state that is entered in place of the spawn state if a monster
has to return to its inactive state if it can't find any more targets.
- Added MF5_NOINTERACTION flag which completely disables all physics related
code for any actor with this flag. Mostly useful for particle effects where
the actors just move a certain distance and then disappear.
- Removed the last remains of the antialias precalculation code from
am_map.cpp because it was no longer used.
- Fixed: Two-sided lines bordering a secret sector were not drawn in the
proper color
- Fixed: The automap didn't check ACS_LockedExecuteDoor for its lock color.
- Switched sounds local to the listener from head-relative 3D sounds to 2D
sounds so stereo sounds have full separation. I tried using set3DSpread,
but that still caused some blending of the channels.
- Changed FScanner so that opening a lump gives the complete wad+lump name
rather than a generic one, so identifying errors among files that all have
the same lump name no longer involves any degree of guesswork in
determining exactly which file the error occurred in.
- Added a check to S_ParseSndSeq() for SNDSEQ lumps with unterminated final
sequences.
- Fixed: Parts of s_sndseq.cpp that scan the Sequences array need NULL
pointer checks, in case an improper sequence was encountered during
parsing but not early enough to avoid creating a slot for it in the array.
- Added support for dumping from RAW/DRO/IMF files, so now anything that
can be played as OPL can also be dumped.
- Removed the opl_enable cvar, since OPL playback is now selectable as just
another MIDI device.
- Added support for DRO playback and dual-chip RAW playback.
- Removed MUS support from OPLMUSSong, since using the OPLMIDIDevice with
MUSSong2 works just as well. There are still lots of leftover bits in
the class that should probably be removed at some point, too.
- Added dual-chip dumping support for the RAW format.
- Added DosBox Raw OPL (.DRO) dumping support. For whatever reason,
in_adlib calculates the song length for this format wrong, even though
the exact length is stored right in the header. (But in_adlib seems buggy
in general; too bad it's the only Windows version of Adplug that seems to
exist.)
- Rewrote the OPL dumper to work with MIDI as well as MUS.
git-svn-id: http://mancubus.net/svn/hosted/gzdoom/trunk@86 b0f79afe-0144-0410-b225-9a4edf0717df
2008-04-05 13:28:48 +00:00
|
|
|
msgLump = Wads.CheckNumForFullName(msgLumpName, true);
|
2008-01-27 11:25:03 +00:00
|
|
|
if (msgLump != -1)
|
|
|
|
{
|
|
|
|
char *textbuf;
|
|
|
|
FinaleTextLen = Wads.LumpLength(msgLump);
|
|
|
|
textbuf = (char *)alloca (FinaleTextLen + 1);
|
|
|
|
Wads.ReadLump (msgLump, textbuf);
|
|
|
|
textbuf[FinaleTextLen] = '\0';
|
|
|
|
FinaleText = textbuf;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
FinaleText = "Unknown message ";
|
|
|
|
FinaleText += msgLumpName;
|
|
|
|
FinaleTextLen = FinaleText.Len();
|
|
|
|
}
|
|
|
|
}
|