diff --git a/src/d_iwad.cpp b/src/d_iwad.cpp index 119d85b74..3453d9d5f 100644 --- a/src/d_iwad.cpp +++ b/src/d_iwad.cpp @@ -61,7 +61,7 @@ const IWADInfo IWADInfos[NUM_IWAD_TYPES] = { "Final Doom: TNT - Evilution", "TNT", MAKERGB(168,0,0), MAKERGB(168,168,168), GAME_Doom, "mapinfo/tnt.txt", GI_MAPxx | GI_COMPATSHORTTEX | GI_COMPATSTAIRS }, { "Final Doom: Plutonia Experiment", "Plutonia", MAKERGB(168,0,0), MAKERGB(168,168,168), GAME_Doom, "mapinfo/plutonia.txt", GI_MAPxx | GI_COMPATSHORTTEX }, { "Hexen: Beyond Heretic", NULL, MAKERGB(240,240,240), MAKERGB(107,44,24), GAME_Hexen, "mapinfo/hexen.txt", GI_MAPxx | GI_COMPATPOLY1 }, - { "Hexen: Deathkings of the Dark Citadel", "HexenDK", MAKERGB(240,240,240), MAKERGB(139,68,9), GAME_Hexen, "mapinfo/hexen.txt", GI_MAPxx | GI_COMPATPOLY1 | GI_COMPATPOLY2 }, + { "Hexen: Deathkings of the Dark Citadel", "HexenDK", MAKERGB(240,240,240), MAKERGB(139,68,9), GAME_Hexen, "mapinfo/hexdd.txt", GI_MAPxx | GI_COMPATPOLY1 | GI_COMPATPOLY2 }, { "Hexen: Demo Version", "HexenDemo",MAKERGB(240,240,240), MAKERGB(107,44,24), GAME_Hexen, "mapinfo/hexen.txt", GI_MAPxx | GI_SHAREWARE }, { "DOOM 2: Hell on Earth", "Doom2", MAKERGB(168,0,0), MAKERGB(168,168,168), GAME_Doom, "mapinfo/doom2.txt", GI_MAPxx | GI_COMPATSHORTTEX }, { "Heretic Shareware", NULL, MAKERGB(252,252,0), MAKERGB(168,0,0), GAME_Heretic, "mapinfo/hereticsw.txt",GI_SHAREWARE }, diff --git a/src/d_main.cpp b/src/d_main.cpp index fc282fa5f..10d58fd2b 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -57,7 +57,7 @@ #include "w_wad.h" #include "s_sound.h" #include "v_video.h" -#include "f_finale.h" +#include "intermission/intermission.h" #include "f_wipe.h" #include "m_argv.h" #include "m_misc.h" @@ -677,13 +677,23 @@ void D_Display () else if (gamestate != wipegamestate && gamestate != GS_FULLCONSOLE && gamestate != GS_TITLELEVEL) { // save the current screen if about to wipe BorderNeedRefresh = screen->GetPageCount (); - if (wipegamestate != GS_FORCEWIPEFADE) + switch (wipegamestate) { + default: wipe = screen->WipeStartScreen (wipetype); - } - else - { + break; + + case GS_FORCEWIPEFADE: wipe = screen->WipeStartScreen (wipe_Fade); + break; + + case GS_FORCEWIPEBURN: + wipe = screen->WipeStartScreen (wipe_Burn); + break; + + case GS_FORCEWIPEMELT: + wipe = screen->WipeStartScreen (wipe_Melt); + break; } wipegamestate = gamestate; } diff --git a/src/dobjgc.cpp b/src/dobjgc.cpp index 4314d0488..642097073 100644 --- a/src/dobjgc.cpp +++ b/src/dobjgc.cpp @@ -73,6 +73,7 @@ #include "m_argv.h" #include "po_man.h" #include "menu/menu.h" +#include "intermission/intermission.h" // MACROS ------------------------------------------------------------------ @@ -300,6 +301,7 @@ static void MarkRoot() Mark(screen); Mark(StatusBar); Mark(DMenu::CurrentMenu); + Mark(DIntermissionController::CurrentIntermission); DThinker::MarkRoots(); FCanvasTextureInfo::Mark(); Mark(DACSThinker::ActiveThinker); diff --git a/src/doomdef.h b/src/doomdef.h index 1108e770f..9ad019db4 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -76,7 +76,9 @@ typedef enum GS_TITLELEVEL, // [RH] A combination of GS_LEVEL and GS_DEMOSCREEN GS_FORCEWIPE = -1, - GS_FORCEWIPEFADE = -2 + GS_FORCEWIPEFADE = -2, + GS_FORCEWIPEBURN = -3, + GS_FORCEWIPEMELT = -4 } gamestate_t; extern gamestate_t gamestate; diff --git a/src/f_finale.cpp b/src/f_finale.cpp deleted file mode 100644 index a851fdc31..000000000 --- a/src/f_finale.cpp +++ /dev/null @@ -1,1374 +0,0 @@ -// 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 -#include -#include - -#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" -#include "g_level.h" -#include "d_event.h" -#include "v_palette.h" -#include "d_main.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 (const char *music, int musicorder, int cdtrack, unsigned int cdid, const char *flat, - const char *text, INTBOOL textInLump, INTBOOL finalePic, INTBOOL lookupText, - bool ending, int endsequence) -{ - bool loopmusic = ending ? !gameinfo.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 (FinaleFlat != NULL && FinaleFlat[0] == '$') - { - FinaleFlat = GStrings(FinaleFlat + 1); - } - - 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; - FinaleSequence = endsequence; - - S_StopAllChannels (); - - if (ending) - { - 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 || - ((!(gameinfo.flags & GI_MAPxx) || 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) - { - if (EndSequences[FinaleSequence].EndType == END_Cast) - { - F_StartCast (); - } - else - { - FinaleCount = 0; - FinaleStage = 1; - wipegamestate = GS_FORCEWIPE; - if (EndSequences[FinaleSequence].EndType == END_Bunny) - { - if (!EndSequences[FinaleSequence].Advanced) - S_StartMusic ("$music_bunny"); - } - } - if (EndSequences[FinaleSequence].Advanced && - !EndSequences[FinaleSequence].Music.IsEmpty()) - { - S_ChangeMusic(EndSequences[FinaleSequence].Music, 0, EndSequences[FinaleSequence].MusicLooping); - } - } - } - 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.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.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 (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_DoomStrifeChex|GAME_Hexen) ? 10 : 20) - 160; - rowheight = SmallFont->GetHeight () + - (gameinfo.gametype & (GAME_DoomStrifeChex) ? 3 : -1); - scale = (CleanXfac != 1 || CleanYfac != 1); - - cx = leftmargin; - if (FinaleStage == 15) - { - cy = 135 - 100; - } - else - { - cy = (gameinfo.gametype & (GAME_DoomStrifeChex) ? 10 : 5) - 100; - } - ch = FinaleText.GetChars(); - - count = (FinaleCount - 10)/TEXTSPEED; - range = SmallFont->GetColorTranslation (CR_UNTRANSLATED); - - for ( ; count ; count-- ) - { - c = *ch++; - if (!c) - break; - if (c == '\n') - { - cx = leftmargin; - cy += rowheight; - continue; - } - - pic = SmallFont->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 -// -struct castinfo_t -{ - const char *name; - const char *type; - const AActor *info; - const PClass *Class; -}; - -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(); - 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; - casttranslation = NULL; - casttics = caststate->GetTics (); - castdeath = false; - FinaleStage = 3; - castframes = 0; - castonmelee = 0; - castattacking = false; - if (!EndSequences[FinaleSequence].Advanced) - S_ChangeMusic ("$music_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) - { - S_Sound (CHAN_VOICE | CHAN_UI, castorder[castnum].info->SeeSound, 1, ATTN_NONE); - } - 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; - 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 | CHAN_UI, 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 = Bindings.GetBind (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 snd = S_FindSkinnedSound(players[consoleplayer].mo, "*death"); - if (snd != 0) S_Sound (CHAN_VOICE | CHAN_UI, snd, 1, ATTN_NONE); - } - else if (castorder[castnum].info->DeathSound) - { - S_Sound (CHAN_VOICE | CHAN_UI, castorder[castnum].info->DeathSound, 1, ATTN_NONE); - } - } - - return true; -} - -// -// F_CastDrawer -// -void F_CastDrawer (void) -{ - spriteframe_t* sprframe; - FTexture* pic; - - // erase the entire screen to a background - screen->DrawTexture (TexMan[GStrings("bgcastcall")], 0, 0, - DTA_DestWidth, screen->GetWidth(), - DTA_DestHeight, screen->GetHeight(), - TAG_DONE); - - screen->DrawText (SmallFont, CR_UNTRANSLATED, - (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 () -{ - const char *tex1, *tex2; - if (EndSequences[FinaleSequence].Advanced) - { - tex1 = EndSequences[FinaleSequence].PicName; - tex2 = EndSequences[FinaleSequence].PicName2; - } - else - { - tex1 = "FINAL1"; - tex2 = "FINAL2"; - } - - int yval; - FTexture *final1 = TexMan(tex1); - FTexture *final2 = TexMan(tex2); - int fwidth = final1->GetScaledWidth(); - int fheight = final1->GetScaledHeight(); - - 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 (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 (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_Fullscreen, true, 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_Fullscreen, true, TAG_DONE); - screen->FillBorder (NULL); - NoWipe = 0; - break; - } - } -} - -/* -================== -= -= F_BunnyScroll -= -================== -*/ -void F_BunnyScroll (void) -{ - static size_t laststage; - - bool bunny = false; - int scrolled; - char name[10]; - size_t stage; - FTexture *tex; - int fwidth; - int fheight; - const char *tex1; - const char *tex2; - - if (EndSequences[FinaleSequence].Advanced) - { - tex1 = EndSequences[FinaleSequence].PicName; - tex2 = EndSequences[FinaleSequence].PicName2; - bunny = EndSequences[FinaleSequence].PlayTheEnd; - } - else if (EndSequences[FinaleSequence].EndType == END_BuyStrife) - { - tex1 = "CREDIT"; - tex2 = "VELLOGO"; - } - else - { - tex1 = "PFUB1"; - tex2 = "PFUB2"; - bunny = true; - } - - V_MarkRect (0, 0, SCREENWIDTH, SCREENHEIGHT); - - tex = TexMan(tex1); - fwidth = tex->GetScaledWidth(); - fheight = tex->GetScaledHeight(); - - scrolled = clamp (((signed)FinaleCount-230)*fwidth/640, 0, fwidth); - - screen->DrawTexture (tex, scrolled, 0, - DTA_VirtualWidth, fwidth, - DTA_VirtualHeight, fheight, - DTA_Masked, false, - TAG_DONE); - - tex = TexMan(tex2); - 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 | CHAN_UI, "weapons/pistol", 1, ATTN_NONE); - laststage = stage; - } - - mysnprintf (name, countof(name), "END%d", (int)stage); - 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.flags2 & LEVEL2_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 | CHAN_UI, "svox/mac10", 1, ATTN_NORM); - FinalePart = 2; - FinaleEndCount = 9 * TICRATE; - break; - - case 2: - FinaleFlat = "SS2F2"; - S_Sound (CHAN_VOICE | CHAN_UI, "svox/mac11", 1, ATTN_NORM); - FinalePart = 3; - FinaleEndCount = 10 * TICRATE; - break; - - case 3: - FinaleFlat = "SS2F3"; - S_Sound (CHAN_VOICE | CHAN_UI, "svox/mac12", 1, ATTN_NORM); - FinalePart = 4; - FinaleEndCount = 12 * TICRATE; - break; - - case 4: - FinaleFlat = "SS2F4"; - S_Sound (CHAN_VOICE | CHAN_UI, "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 | CHAN_UI, "svox/mac16", 1, ATTN_NORM); - FinalePart = 6; - FinaleEndCount = 10 * TICRATE; - break; - - case 6: - FinaleFlat = "SS3F2"; - S_Sound (CHAN_VOICE | CHAN_UI, "svox/mac17", 1, ATTN_NORM); - FinalePart = 7; - FinaleEndCount = 12 * TICRATE; - break; - - case 7: - FinaleFlat = "SS3F3"; - S_Sound (CHAN_VOICE | CHAN_UI, "svox/mac18", 1, ATTN_NORM); - FinalePart = 8; - FinaleEndCount = 12 * TICRATE; - break; - - case 8: - FinaleFlat = "SS3F4"; - S_Sound (CHAN_VOICE | CHAN_UI, "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 | CHAN_UI, "svox/rie01", 1, ATTN_NORM); - FinaleEndCount = 13 * TICRATE; - FinalePart = 11; - break; - - case 11: - FinaleFlat = "SS4F2"; - S_Sound (CHAN_VOICE | CHAN_UI, "svox/bbx01", 1, ATTN_NORM); - FinaleEndCount = 11 * TICRATE; - FinalePart = 12; - break; - - case 12: - FinaleFlat = "SS4F3"; - S_Sound (CHAN_VOICE | CHAN_UI, "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 | CHAN_UI, "svox/ss501b", 1, ATTN_NORM); - FinalePart = 15; - FinaleEndCount = 11 * TICRATE; - break; - - case 15: - FinaleFlat = "SS5F2"; - S_Sound (CHAN_VOICE | CHAN_UI, "svox/ss502b", 1, ATTN_NORM); - FinalePart = 16; - FinaleEndCount = 10 * TICRATE; - break; - - case 16: - FinaleFlat = "SS5F3"; - S_Sound (CHAN_VOICE | CHAN_UI, "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 | CHAN_UI, "svox/ss601a", 1, ATTN_NORM); - FinaleEndCount = 8 * TICRATE; - FinalePart = 18; - break; - - case 18: - FinaleFlat = "SS6F2"; - S_Sound (CHAN_VOICE | CHAN_UI, "svox/ss602a", 1, ATTN_NORM); - FinalePart = 19; - FinaleEndCount = 8 * TICRATE; - break; - - case 19: - FinaleFlat = "SS6F3"; - S_Sound (CHAN_VOICE | CHAN_UI, "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) - { - FTextureID picnum = TexMan.CheckForTexture (FinaleFlat, FTexture::TEX_Flat, FTextureManager::TEXMAN_Overridable); - if (picnum.isValid()) - { - 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.GetFinalePage(1); - screen->DrawTexture (TexMan[picname], 0, 0, - DTA_DestWidth, screen->GetWidth(), - DTA_DestHeight, screen->GetHeight(), TAG_DONE); - break; - case END_Pic2: - picname = gameinfo.GetFinalePage(2); - screen->DrawTexture (TexMan[picname], 0, 0, - DTA_DestWidth, screen->GetWidth(), - DTA_DestHeight, screen->GetHeight(), TAG_DONE); - break; - case END_Pic3: - picname = gameinfo.GetFinalePage(3); - 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; - case END_TitleScreen: - D_StartTitle (); - 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_Fullscreen, true, TAG_DONE); - screen->FillBorder (NULL); - if (FinaleStage >= 14) - { // Chess pic, draw the correct character graphic - double w = pic->GetScaledWidthDouble(); - double h = pic->GetScaledHeightDouble(); - if (multiplayer) - { - screen->DrawTexture (TexMan["CHESSALL"], 20, 0, - DTA_VirtualWidthF, w, - DTA_VirtualHeightF, h, TAG_DONE); - } - else if (players[consoleplayer].CurrentPlayerClass > 0) - { - picname = players[consoleplayer].CurrentPlayerClass == 1 ? "CHESSC" : "CHESSM"; - screen->DrawTexture (TexMan[picname], 60, 0, - DTA_VirtualWidthF, w, - DTA_VirtualHeightF, h, TAG_DONE); - } - } - } - switch (FinaleStage) - { - case 0: - case 11: - case 12: - case 15: - F_TextWrite (); - break; - } -} - -//========================================================================== -// -// GetFinaleText -// -//========================================================================== - -static void GetFinaleText (const char *msgLumpName) -{ - int msgLump; - - msgLump = Wads.CheckNumForFullName(msgLumpName, true); - 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(); - } -} diff --git a/src/f_finale.h b/src/f_finale.h deleted file mode 100644 index 7eb2884d0..000000000 --- a/src/f_finale.h +++ /dev/null @@ -1,53 +0,0 @@ -// 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. -// -// DESCRIPTION: -// -// -//----------------------------------------------------------------------------- - - -#ifndef __F_FINALE__ -#define __F_FINALE__ - -#include "basictypes.h" - -struct event_t; - - -// -// FINALE -// - -// Called by main loop. -bool F_Responder (event_t* ev); - -// Called by main loop. -void F_Ticker (); - -// Called by main loop. -void F_Drawer (); - - -void F_StartFinale (const char *music, int musicorder, int cdtrack, unsigned int cdid, const char *flat, - const char *text, INTBOOL textInLump, INTBOOL finalePic, INTBOOL lookupText, - bool ending, int endsequence = 0); - -void F_StartSlideshow (); - -void F_EndFinale (); - -#endif diff --git a/src/g_game.cpp b/src/g_game.cpp index 35e740f40..1153a9e7c 100644 --- a/src/g_game.cpp +++ b/src/g_game.cpp @@ -37,7 +37,7 @@ #include "doomstat.h" #include "d_protocol.h" #include "d_netinf.h" -#include "f_finale.h" +#include "intermission/intermission.h" #include "m_argv.h" #include "m_misc.h" #include "menu/menu.h" @@ -1031,7 +1031,7 @@ void G_Ticker () G_DoCompleted (); break; case ga_slideshow: - F_StartSlideshow (); + if (gamestate == GS_LEVEL) F_StartIntermission(level.info->slideshow, FSTATE_InLevel); break; case ga_worlddone: G_DoWorldDone (); diff --git a/src/g_level.cpp b/src/g_level.cpp index 62eb809aa..7008572a7 100644 --- a/src/g_level.cpp +++ b/src/g_level.cpp @@ -51,7 +51,7 @@ #include "p_local.h" #include "r_sky.h" #include "c_console.h" -#include "f_finale.h" +#include "intermission/intermission.h" #include "gstrings.h" #include "v_video.h" #include "st_stuff.h" @@ -78,6 +78,7 @@ #include "d_netinf.h" #include "v_palette.h" #include "menu/menu.h" +#include "a_strifeglobal.h" #include "gi.h" @@ -99,22 +100,11 @@ EXTERN_CVAR (String, playerclass) #define RCLS_ID MAKE_ID('r','c','L','s') #define PCLS_ID MAKE_ID('p','c','L','s') -static void SetEndSequence (char *nextmap, int type); void G_VerifySkill(); static FRandom pr_classchoice ("RandomPlayerClassChoice"); -TArray EndSequences; - -EndSequence::EndSequence() -{ - EndType = END_Pic; - Advanced = false; - MusicLooping = false; - PlayTheEnd = false; -} - extern level_info_t TheDefaultLevelInfo; extern bool timingdemo; @@ -135,73 +125,6 @@ void *statcopy; // for statistics driver FLevelLocals level; // info about current level -//========================================================================== -// -// -//========================================================================== - -int FindEndSequence (int type, const char *picname) -{ - unsigned int i, num; - - num = EndSequences.Size (); - for (i = 0; i < num; i++) - { - if (EndSequences[i].EndType == type && !EndSequences[i].Advanced && - (type != END_Pic || stricmp (EndSequences[i].PicName, picname) == 0)) - { - return (int)i; - } - } - return -1; -} - -//========================================================================== -// -// -//========================================================================== - -static void SetEndSequence (char *nextmap, int type) -{ - int seqnum; - - seqnum = FindEndSequence (type, NULL); - if (seqnum == -1) - { - EndSequence newseq; - newseq.EndType = type; - seqnum = (int)EndSequences.Push (newseq); - } - mysnprintf(nextmap, 11, "enDSeQ%04x", (WORD)seqnum); -} - -//========================================================================== -// -// -//========================================================================== - -void G_SetForEndGame (char *nextmap) -{ - if (!strncmp(nextmap, "enDSeQ",6)) return; // If there is already an end sequence please leave it alone!!! - - if (gameinfo.gametype == GAME_Strife) - { - SetEndSequence (nextmap, gameinfo.flags & GI_SHAREWARE ? END_BuyStrife : END_Strife); - } - else if (gameinfo.gametype == GAME_Hexen) - { - SetEndSequence (nextmap, END_Chess); - } - else if (gameinfo.gametype == GAME_Doom && (gameinfo.flags & GI_MAPxx)) - { - SetEndSequence (nextmap, END_Cast); - } - else - { // The ExMx games actually have different ends based on the episode, - // but I want to keep this simple. - SetEndSequence (nextmap, END_Pic1); - } -} //========================================================================== // @@ -541,7 +464,6 @@ static bool unloading; // //========================================================================== - void G_ChangeLevel(const char *levelname, int position, int flags, int nextSkill) { level_info_t *nextinfo = NULL; @@ -552,7 +474,20 @@ void G_ChangeLevel(const char *levelname, int position, int flags, int nextSkill return; } - if (strncmp(levelname, "enDSeQ", 6) != 0) + if (levelname == NULL || *levelname == 0) + { + // end the game + levelname = NULL; + if (!strncmp(level.nextmap, "enDSeQ",6)) + { + levelname = level.nextmap; // If there is already an end sequence please leave it alone! + } + else + { + nextlevel.Format("enDSeQ%04x", int(gameinfo.DefaultEndSequence)); + } + } + else if (strncmp(levelname, "enDSeQ", 6) != 0) { nextinfo = FindLevelInfo (levelname); if (nextinfo != NULL) @@ -566,7 +501,7 @@ void G_ChangeLevel(const char *levelname, int position, int flags, int nextSkill } } - nextlevel = levelname; + if (levelname != NULL) nextlevel = levelname; if (nextSkill != -1) NextSkill = nextSkill; @@ -1026,13 +961,28 @@ void G_WorldDone (void) if (strncmp (nextlevel, "enDSeQ", 6) == 0) { + FName endsequence = ENamedName(strtol(nextlevel.GetChars()+6, NULL, 16)); + // Strife needs a special case here to choose between good and sad ending. Bad is handled elsewherw. + if (endsequence == NAME_Inter_Strife) + { + if (players[0].mo->FindInventory (QuestItemClasses[24]) || + players[0].mo->FindInventory (QuestItemClasses[27])) + { + endsequence = NAME_Inter_Strife_Good; + } + else + { + endsequence = NAME_Inter_Strife_Sad; + } + } + F_StartFinale (thiscluster->MessageMusic, thiscluster->musicorder, thiscluster->cdtrack, thiscluster->cdid, thiscluster->FinaleFlat, thiscluster->ExitText, thiscluster->flags & CLUSTER_EXITTEXTINLUMP, thiscluster->flags & CLUSTER_FINALEPIC, thiscluster->flags & CLUSTER_LOOKUPEXITTEXT, - true, strtol(nextlevel.GetChars()+6, NULL, 16)); + true, endsequence); } else { diff --git a/src/g_level.h b/src/g_level.h index f04e222d5..d7add35b5 100644 --- a/src/g_level.h +++ b/src/g_level.h @@ -54,6 +54,8 @@ class FScanner; #define GCC_YSEG __attribute__((section(SECTION_YREG))) #endif +struct FIntermissionDescriptor; +struct FIntermissionAction; struct FMapInfoParser { @@ -98,6 +100,11 @@ struct FMapInfoParser bool CheckFloat(); void SkipToNext(); void CheckEndOfFile(const char *block); + + void ParseIntermissionAction(FIntermissionDescriptor *Desc); + void ParseIntermission(); + FName CheckEndSequence(); + FName ParseEndGame(); }; #define DEFINE_MAP_OPTION(name, old) \ @@ -163,7 +170,7 @@ enum ELevelFlags LEVEL_VISITED = 0x80000000, // Used for intermission map // The flags QWORD is now split into 2 DWORDs - LEVEL2_DEATHSLIDESHOW = 0x00000001, // Slideshow on death + //LEVEL2_DEATHSLIDESHOW = 0x00000001, // Slideshow on death LEVEL2_ALLMAP = 0x00000002, // The player picked up a map on this level LEVEL2_LAXMONSTERACTIVATION = 0x00000004, // Monsters can open doors depending on the door speed @@ -251,7 +258,7 @@ struct level_info_t char mapname[9]; char pname[9]; - char nextmap[11]; // The endsequence string is 10 chars so we need more space here + char nextmap[11]; char secretmap[11]; char skypic1[9]; char skypic2[9]; @@ -286,6 +293,9 @@ struct level_info_t DWORD compatmask; FString Translator; // for converting Doom-format linedef and sector types. int DefaultEnvironment; // Default sound environment for the map. + FName Intermission; + FName deathsequence; + FName slideshow; // Redirection: If any player is carrying the specified item, then // you go to the RedirectMap instead of this one. @@ -415,36 +425,6 @@ struct FLevelLocals bool IsFreelookAllowed() const; }; -enum EndTypes -{ - END_Pic, - END_Pic1, - END_Pic2, - END_Pic3, - END_Bunny, - END_Cast, - END_Demon, - END_Underwater, - END_Chess, - END_Strife, - END_BuyStrife, - END_TitleScreen -}; - -struct EndSequence -{ - BYTE EndType; - bool Advanced; - bool MusicLooping; - bool PlayTheEnd; - FString PicName; - FString PicName2; - FString Music; - - EndSequence(); -}; - -extern TArray EndSequences; struct cluster_info_t { @@ -509,8 +489,6 @@ enum void G_ChangeLevel(const char *levelname, int position, int flags, int nextSkill=-1); -void G_SetForEndGame (char *nextmap); - void G_StartTravel (); void G_FinishTravel (); diff --git a/src/g_mapinfo.cpp b/src/g_mapinfo.cpp index 42beba873..e15de22a2 100644 --- a/src/g_mapinfo.cpp +++ b/src/g_mapinfo.cpp @@ -53,9 +53,6 @@ #include "version.h" #include "v_text.h" -int FindEndSequence (int type, const char *picname); - - TArray wadclusterinfos; TArray wadlevelinfos; @@ -757,9 +754,6 @@ void FMapInfoParser::ParseCluster() void FMapInfoParser::ParseNextMap(char *mapname) { - EndSequence newSeq; - bool useseq = false; - if (sc.CheckNumber()) { if (HexenHack) @@ -773,163 +767,14 @@ void FMapInfoParser::ParseNextMap(char *mapname) } else { - + *mapname = 0; sc.MustGetString(); - if (sc.Compare("endgame")) + strncpy (mapname, sc.String, 8); + mapname[8] = 0; + FName seq = CheckEndSequence(); + if (seq != NAME_None) { - if (!sc.CheckString("{")) - { - // Make Demon Eclipse work again - sc.UnGet(); - goto standard_endgame; - } - newSeq.Advanced = true; - newSeq.EndType = END_Pic1; - newSeq.PlayTheEnd = false; - newSeq.MusicLooping = true; - while (!sc.CheckString("}")) - { - sc.MustGetString(); - if (sc.Compare("pic")) - { - ParseAssign(); - sc.MustGetString(); - newSeq.EndType = END_Pic; - newSeq.PicName = sc.String; - } - else if (sc.Compare("hscroll")) - { - ParseAssign(); - newSeq.EndType = END_Bunny; - sc.MustGetString(); - newSeq.PicName = sc.String; - ParseComma(); - sc.MustGetString(); - newSeq.PicName2 = sc.String; - if (CheckNumber()) - newSeq.PlayTheEnd = !!sc.Number; - } - else if (sc.Compare("vscroll")) - { - ParseAssign(); - newSeq.EndType = END_Demon; - sc.MustGetString(); - newSeq.PicName = sc.String; - ParseComma(); - sc.MustGetString(); - newSeq.PicName2 = sc.String; - } - else if (sc.Compare("cast")) - { - newSeq.EndType = END_Cast; - } - else if (sc.Compare("music")) - { - ParseAssign(); - sc.MustGetString(); - newSeq.Music = sc.String; - if (CheckNumber()) - { - newSeq.MusicLooping = !!sc.Number; - } - } - else - { - if (format_type == FMT_New) - { - // Unknown - sc.ScriptMessage("Unknown property '%s' found in endgame definition\n", sc.String); - SkipToNext(); - } - else - { - sc.ScriptError("Unknown property '%s' found in endgame definition\n", sc.String); - } - - } - } - useseq = true; - } - else if (strnicmp (sc.String, "EndGame", 7) == 0) - { - int type; - switch (sc.String[7]) - { - case '1': type = END_Pic1; break; - case '2': type = END_Pic2; break; - case '3': type = END_Bunny; break; - case 'C': type = END_Cast; break; - case 'W': type = END_Underwater; break; - case 'S': type = END_Strife; break; - standard_endgame: - default: type = END_Pic3; break; - } - newSeq.EndType = type; - useseq = true; - } - else if (sc.Compare("endpic")) - { - ParseComma(); - sc.MustGetString (); - newSeq.EndType = END_Pic; - newSeq.PicName = sc.String; - useseq = true; - } - else if (sc.Compare("endbunny")) - { - newSeq.EndType = END_Bunny; - useseq = true; - } - else if (sc.Compare("endcast")) - { - newSeq.EndType = END_Cast; - useseq = true; - } - else if (sc.Compare("enddemon")) - { - newSeq.EndType = END_Demon; - useseq = true; - } - else if (sc.Compare("endchess")) - { - newSeq.EndType = END_Chess; - useseq = true; - } - else if (sc.Compare("endunderwater")) - { - newSeq.EndType = END_Underwater; - useseq = true; - } - else if (sc.Compare("endbuystrife")) - { - newSeq.EndType = END_BuyStrife; - useseq = true; - } - else if (sc.Compare("endtitle")) - { - newSeq.EndType = END_TitleScreen; - useseq = true; - } - else - { - strncpy (mapname, sc.String, 8); - mapname[8] = 0; - } - if (useseq) - { - int seqnum = -1; - - if (!newSeq.Advanced) - { - seqnum = FindEndSequence (newSeq.EndType, newSeq.PicName); - } - - if (seqnum == -1) - { - seqnum = (int)EndSequences.Push (newSeq); - } - // mapname can point to nextmap and secretmap which are both 12 characters long - mysnprintf(mapname, 11, "enDSeQ%04x", (WORD)seqnum); + mysnprintf(mapname, 11, "enDSeQ%04x", int(seq)); } } } @@ -1229,6 +1074,20 @@ DEFINE_MAP_OPTION(translator, true) info->Translator = parse.sc.String; } +DEFINE_MAP_OPTION(deathsequence, false) +{ + parse.ParseAssign(); + parse.sc.MustGetString(); + info->deathsequence = parse.sc.String; +} + +DEFINE_MAP_OPTION(slideshow, false) +{ + parse.ParseAssign(); + parse.sc.MustGetString(); + info->slideshow = parse.sc.String; +} + DEFINE_MAP_OPTION(bordertexture, true) { parse.ParseAssign(); @@ -1351,7 +1210,7 @@ MapFlagHandlers[] = { "missilesactivateimpactlines", MITYPE_SETFLAG2, LEVEL2_MISSILESACTIVATEIMPACT, 0 }, { "missileshootersactivetimpactlines",MITYPE_CLRFLAG2, LEVEL2_MISSILESACTIVATEIMPACT, 0 }, { "noinventorybar", MITYPE_SETFLAG, LEVEL_NOINVENTORYBAR, 0 }, - { "deathslideshow", MITYPE_SETFLAG2, LEVEL2_DEATHSLIDESHOW, 0 }, + { "deathslideshow", MITYPE_SETFLAG2, 0, 0 }, { "strictmonsteractivation", MITYPE_CLRFLAG2, LEVEL2_LAXMONSTERACTIVATION, LEVEL2_LAXACTIVATIONMAPINFO }, { "laxmonsteractivation", MITYPE_SETFLAG2, LEVEL2_LAXMONSTERACTIVATION, LEVEL2_LAXACTIVATIONMAPINFO }, { "additive_scrollers", MITYPE_COMPATFLAG, COMPATF_BOOMSCROLL}, @@ -1908,6 +1767,18 @@ void FMapInfoParser::ParseMapInfo (int lump, level_info_t &gamedefaults, level_i sc.ScriptError("gameinfo definitions not supported with old MAPINFO syntax"); } } + else if (sc.Compare("intermission")) + { + if (format_type != FMT_Old) + { + format_type = FMT_New; + ParseIntermission(); + } + else + { + sc.ScriptError("intermission definitions not supported with old MAPINFO syntax"); + } + } else { sc.ScriptError("%s: Unknown top level keyword", sc.String); @@ -1965,7 +1836,6 @@ void G_ParseMapInfo (const char *basemapinfo) level_info_t defaultinfo; parse.ParseMapInfo(lump, gamedefaults, defaultinfo); } - EndSequences.ShrinkToFit (); if (AllEpisodes.Size() == 0) { diff --git a/src/g_strife/a_programmer.cpp b/src/g_strife/a_programmer.cpp index 196fae584..6a65ef9e3 100644 --- a/src/g_strife/a_programmer.cpp +++ b/src/g_strife/a_programmer.cpp @@ -6,7 +6,6 @@ #include "p_enemy.h" #include "s_sound.h" #include "a_strifeglobal.h" -#include "f_finale.h" #include "thingdef/thingdef.h" #include "g_level.h" #include "doomstat.h" diff --git a/src/gi.cpp b/src/gi.cpp index c8ca25b89..18f8a6674 100644 --- a/src/gi.cpp +++ b/src/gi.cpp @@ -297,6 +297,9 @@ void FMapInfoParser::ParseGameInfo() GAMEINFOKEY_STRING(mFontColorHighlight, "menufontcolor_highlight") GAMEINFOKEY_STRING(mFontColorSelection, "menufontcolor_selection") GAMEINFOKEY_CSTRING(mBackButton, "menubackbutton", 8) + GAMEINFOKEY_INT(TextScreenX, "textscreenx") + GAMEINFOKEY_INT(TextScreenY, "textscreeny") + GAMEINFOKEY_STRING(DefaultEndSequence, "defaultendsequence") else { diff --git a/src/gi.h b/src/gi.h index 40ddfb9ad..e71b0f783 100644 --- a/src/gi.h +++ b/src/gi.h @@ -125,6 +125,9 @@ struct gameinfo_t FName mFontColorSelection; char mBackButton[9]; fixed_t gibfactor; + int TextScreenX; + int TextScreenY; + FName DefaultEndSequence; const char *GetFinalePage(unsigned int num) const; }; diff --git a/src/intermission/intermission.cpp b/src/intermission/intermission.cpp new file mode 100644 index 000000000..461495f63 --- /dev/null +++ b/src/intermission/intermission.cpp @@ -0,0 +1,875 @@ +/* +** intermission.cpp +** Framework for intermissions (text screens, slideshows, etc) +** +**--------------------------------------------------------------------------- +** Copyright 2010 Christoph Oelckers +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +#include "doomtype.h" +#include "doomstat.h" +#include "d_event.h" +#include "w_wad.h" +#include "gi.h" +#include "v_video.h" +#include "v_palette.h" +#include "d_main.h" +#include "gstrings.h" +#include "intermission/intermission.h" +#include "actor.h" +#include "d_player.h" +#include "r_state.h" +#include "r_translate.h" +#include "c_bind.h" +#include "g_level.h" +#include "p_conversation.h" +#include "menu/menu.h" + +FIntermissionDescriptorList IntermissionDescriptors; + +IMPLEMENT_CLASS(DIntermissionScreen) +IMPLEMENT_CLASS(DIntermissionScreenFader) +IMPLEMENT_CLASS(DIntermissionScreenText) +IMPLEMENT_CLASS(DIntermissionScreenCast) +IMPLEMENT_CLASS(DIntermissionScreenScroller) +IMPLEMENT_POINTY_CLASS(DIntermissionController) + DECLARE_POINTER(mScreen) +END_POINTERS + +extern int NoWipe; + +//========================================================================== +// +// +// +//========================================================================== + +void DIntermissionScreen::Init(FIntermissionAction *desc, bool first) +{ + int lumpnum; + + if (desc->mCdTrack == 0 || !S_ChangeCDMusic (desc->mCdTrack, desc->mCdId)) + { + if (desc->mMusic.IsEmpty()) + { + // only start the default music if this is the first action in an intermission + if (first) S_ChangeMusic (gameinfo.finaleMusic, 0, desc->mMusicLooping); + } + else + { + S_ChangeMusic (desc->mMusic, desc->mMusicOrder, desc->mMusicLooping); + } + } + mDuration = desc->mDuration; + + const char *texname = desc->mBackground; + if (*texname == '@') + { + char *pp; + unsigned int v = strtoul(texname+1, &pp, 10) - 1; + if (*pp == 0 && v < gameinfo.finalePages.Size()) + { + texname = gameinfo.finalePages[v].GetChars(); + } + else if (gameinfo.finalePages.Size() > 0) + { + texname = gameinfo.finalePages[0].GetChars(); + } + else + { + texname = gameinfo.titlePage; + } + } + else if (*texname == '$') + { + texname = GStrings[texname+1]; + } + FTextureID tex = TexMan.CheckForTexture(texname, FTexture::TEX_MiscPatch); + if (tex.isValid()) + { + mBackground = tex; + mFlatfill = desc->mFlatfill; + } + S_Sound (CHAN_VOICE | CHAN_UI, desc->mSound, 1.0f, ATTN_NONE); + if (desc->mPalette.IsNotEmpty() && (lumpnum = Wads.CheckNumForFullName(desc->mPalette, true)) > 0) + { + PalEntry *palette; + const BYTE *orgpal; + FMemLump lump; + int i; + + lump = Wads.ReadLump (lumpnum); + 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 (); + mPaletteChanged = true; + NoWipe = 1; + M_EnableMenu(false); + } + mOverlays.Resize(desc->mOverlays.Size()); + for (unsigned i=0; i < mOverlays.Size(); i++) + { + mOverlays[i].x = desc->mOverlays[i].x; + mOverlays[i].y = desc->mOverlays[i].y; + mOverlays[i].mCondition = desc->mOverlays[i].mCondition; + mOverlays[i].mPic = TexMan.CheckForTexture(desc->mOverlays[i].mName, FTexture::TEX_MiscPatch); + } + mTicker = 0; +} + + +int DIntermissionScreen::Responder (event_t *ev) +{ + if (ev->type == EV_KeyDown) + { + return -1; + } + return 0; +} + +int DIntermissionScreen::Ticker () +{ + if (++mTicker >= mDuration && mDuration > 0) return -1; + return 0; +} + +bool DIntermissionScreen::CheckOverlay(int i) +{ + if (mOverlays[i].mCondition == NAME_Multiplayer && !multiplayer) return false; + else if (mOverlays[i].mCondition != NAME_None) + { + if (multiplayer || players[0].mo == NULL) return false; + const PClass *cls = PClass::FindClass(mOverlays[i].mCondition); + if (cls == NULL) return false; + if (!players[0].mo->IsKindOf(cls)) return false; + } + return true; +} + +void DIntermissionScreen::Drawer () +{ + if (mBackground.isValid()) + { + if (!mFlatfill) + { + screen->DrawTexture (TexMan[mBackground], 0, 0, DTA_Fullscreen, true, TAG_DONE); + } + else + { + screen->FlatFill (0,0, SCREENWIDTH, SCREENHEIGHT, TexMan[mBackground]); + } + } + else + { + screen->Clear (0, 0, SCREENWIDTH, SCREENHEIGHT, 0, 0); + } + for (unsigned i=0; i < mOverlays.Size(); i++) + { + if (CheckOverlay(i)) + screen->DrawTexture (TexMan[mOverlays[i].mPic], mOverlays[i].x, mOverlays[i].y, DTA_320x200, true, TAG_DONE); + } + if (!mFlatfill) screen->FillBorder (NULL); +} + +void DIntermissionScreen::Destroy() +{ + if (mPaletteChanged) + { + PalEntry *palette; + int i; + + palette = screen->GetPalette (); + for (i = 0; i < 256; ++i) + { + palette[i] = GPalette.BaseColors[i]; + } + screen->UpdatePalette (); + NoWipe = 5; + mPaletteChanged = false; + M_EnableMenu(true); + } + S_StopSound(CHAN_VOICE); + Super::Destroy(); +} + +//========================================================================== +// +// +// +//========================================================================== + +void DIntermissionScreenFader::Init(FIntermissionAction *desc, bool first) +{ + Super::Init(desc, first); + mType = static_cast(desc)->mFadeType; +} + +//=========================================================================== +// +// FadePic +// +//=========================================================================== + +int DIntermissionScreenFader::Responder (event_t *ev) +{ + if (ev->type == EV_KeyDown) + { + V_SetBlend(0,0,0,0); + return -1; + } + return Super::Responder(ev); +} + +int DIntermissionScreenFader::Ticker () +{ + if (mFlatfill || !mBackground.isValid()) return -1; + return Super::Ticker(); +} + +void DIntermissionScreenFader::Drawer () +{ + if (!mFlatfill && mBackground.isValid()) + { + double factor = clamp(double(mTicker) / mDuration, 0., 1.); + if (mType == FADE_In) factor = 1.0 - factor; + int color = MAKEARGB(xs_RoundToInt(factor*255), 0,0,0); + + if (screen->Begin2D(false)) + { + screen->DrawTexture (TexMan[mBackground], 0, 0, DTA_Fullscreen, true, DTA_ColorOverlay, color, TAG_DONE); + for (unsigned i=0; i < mOverlays.Size(); i++) + { + if (CheckOverlay(i)) + screen->DrawTexture (TexMan[mOverlays[i].mPic], mOverlays[i].x, mOverlays[i].y, DTA_320x200, true, DTA_ColorOverlay, color, TAG_DONE); + } + screen->FillBorder (NULL); + } + else + { + V_SetBlend (0,0,0,int(256*factor)); + Super::Drawer(); + } + } +} + +//========================================================================== +// +// +// +//========================================================================== + +void DIntermissionScreenText::Init(FIntermissionAction *desc, bool first) +{ + Super::Init(desc, first); + mText = static_cast(desc)->mText; + if (mText[0] == '$') mText = GStrings[&mText[1]]; + mTextSpeed = static_cast(desc)->mTextSpeed; + mTextX = static_cast(desc)->mTextX; + if (mTextX < 0) mTextX =gameinfo.TextScreenX; + mTextY = static_cast(desc)->mTextY; + if (mTextY < 0) mTextY =gameinfo.TextScreenY; + mTextLen = (int)strlen(mText); + mTextDelay = static_cast(desc)->mTextDelay; + mTextColor = static_cast(desc)->mTextColor; + // For text screens, the duration only counts when the text is complete. + if (mDuration > 0) mDuration += mTextDelay + mTextSpeed * mTextLen; +} + +int DIntermissionScreenText::Responder (event_t *ev) +{ + if (ev->type == EV_KeyDown) + { + if (mTicker < mTextDelay + (mTextLen * mTextSpeed)) + { + mTicker = mTextDelay + (mTextLen * mTextSpeed); + return 1; + } + } + return Super::Responder(ev); +} + +void DIntermissionScreenText::Drawer () +{ + Super::Drawer(); + if (mTicker >= mTextDelay) + { + FTexture *pic; + int w; + size_t count; + int c; + const FRemapTable *range; + + // draw some of the text onto the screen + int rowheight = SmallFont->GetHeight () + (gameinfo.gametype & (GAME_DoomStrifeChex) ? 3 : -1); + bool scale = (CleanXfac != 1 || CleanYfac != 1); + + int cx = mTextX; + int cy = mTextY; + const char *ch = mText; + + count = (mTicker - mTextDelay) / mTextSpeed; + range = SmallFont->GetColorTranslation (mTextColor); + + for ( ; count > 0 ; count-- ) + { + c = *ch++; + if (!c) + break; + if (c == '\n') + { + cx = mTextX; + cy += rowheight; + continue; + } + + pic = SmallFont->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; + } + } +} + +//========================================================================== +// +// +// +//========================================================================== + +void DIntermissionScreenCast::Init(FIntermissionAction *desc, bool first) +{ + Super::Init(desc, first); + mName = static_cast(desc)->mName; + mClass = PClass::FindClass(static_cast(desc)->mCastClass); + if (mClass != NULL) mDefaults = GetDefaultByType(mClass); + else mDefaults = NULL; + + mCastSounds.Resize(static_cast(desc)->mCastSounds.Size()); + for (unsigned i=0; i < mCastSounds.Size(); i++) + { + mCastSounds[i].mSequence = static_cast(desc)->mCastSounds[i].mSequence; + mCastSounds[i].mIndex = static_cast(desc)->mCastSounds[i].mIndex; + mCastSounds[i].mSound = static_cast(desc)->mCastSounds[i].mSound; + } + caststate = mDefaults->SeeState; + if (mClass->IsDescendantOf(RUNTIME_CLASS(APlayerPawn))) + { + advplayerstate = mDefaults->MissileState; + castsprite = skins[players[consoleplayer].userinfo.skin].sprite; + casttranslation = translationtables[TRANSLATION_Players][consoleplayer]; + } + else + { + advplayerstate = NULL; + if (caststate != NULL) castsprite = caststate->sprite; + else castsprite = -1; + casttranslation = NULL; + } + castdeath = false; + castframes = 0; + castonmelee = 0; + castattacking = false; + if (mDefaults->SeeSound) + { + S_Sound (CHAN_VOICE | CHAN_UI, mDefaults->SeeSound, 1, ATTN_NONE); + } +} + +int DIntermissionScreenCast::Responder (event_t *ev) +{ + if (ev->type != EV_KeyDown) return 0; + + if (castdeath) + return 1; // already in dying frames + + castdeath = true; + caststate = mClass->ActorInfo->FindState(NAME_Death); + if (caststate == NULL) return -1; + + casttics = caststate->GetTics(); + castframes = 0; + castattacking = false; + + if (mClass->IsDescendantOf(RUNTIME_CLASS(APlayerPawn))) + { + int snd = S_FindSkinnedSound(players[consoleplayer].mo, "*death"); + if (snd != 0) S_Sound (CHAN_VOICE | CHAN_UI, snd, 1, ATTN_NONE); + } + else if (mDefaults->DeathSound) + { + S_Sound (CHAN_VOICE | CHAN_UI, mDefaults->DeathSound, 1, ATTN_NONE); + } + return true; +} + +int DIntermissionScreenCast::Ticker () +{ + Super::Ticker(); + + if (--casttics > 0 && caststate != NULL) + return 0; // not time to change state yet + + if (caststate == NULL || caststate->GetTics() == -1 || caststate->GetNextState() == NULL) + { + return -1; + } + else + { + // sound hacks.... + if (caststate != NULL && castattacking) + { + for (unsigned i = 0; i < mCastSounds.Size(); i++) + { + if ((!!mCastSounds[i].mSequence) == (basestate != mDefaults->MissileState) && + (caststate == basestate + mCastSounds[i].mIndex - 1)) + { + S_StopAllChannels (); + S_Sound (CHAN_WEAPON | CHAN_UI, mCastSounds[i].mSound, 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) + basestate = caststate = mDefaults->MeleeState; + else + basestate = caststate = mDefaults->MissileState; + castonmelee ^= 1; + if (caststate == NULL) + { + if (castonmelee) + basestate = caststate = mDefaults->MeleeState; + else + basestate = caststate = mDefaults->MissileState; + } + } + + if (castattacking) + { + if (castframes == 24 || caststate == mDefaults->SeeState ) + { + stopattack: + castattacking = false; + castframes = 0; + caststate = mDefaults->SeeState; + } + } + + casttics = caststate->GetTics(); + if (casttics == -1) + casttics = 15; + return 0; +} + +void DIntermissionScreenCast::Drawer () +{ + spriteframe_t* sprframe; + FTexture* pic; + + Super::Drawer(); + + const char *name = mName; + if (name != NULL) + { + if (*name == '$') name = GStrings(name+1); + screen->DrawText (SmallFont, CR_UNTRANSLATED, + (SCREENWIDTH - SmallFont->StringWidth (name) * CleanXfac)/2, + (SCREENHEIGHT * 180) / 200, + 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); + } +} + +//========================================================================== +// +// +// +//========================================================================== + +void DIntermissionScreenScroller::Init(FIntermissionAction *desc, bool first) +{ + Super::Init(desc, first); + mFirstPic = mBackground; + mSecondPic = TexMan.CheckForTexture(static_cast(desc)->mSecondPic, FTexture::TEX_MiscPatch); + mScrollDelay = static_cast(desc)->mScrollDelay; + mScrollTime = static_cast(desc)->mScrollTime; + mScrollDir = static_cast(desc)->mScrollDir; +} + +void DIntermissionScreenScroller::Drawer () +{ + FTexture *tex = TexMan[mFirstPic]; + FTexture *tex2 = TexMan[mSecondPic]; + if (mTicker >= mScrollDelay && mTicker < mScrollDelay + mScrollTime && tex != NULL && tex2 != NULL) + { + + int fwidth = tex->GetScaledWidth(); + int fheight = tex->GetScaledHeight(); + + double xpos1 = 0, ypos1 = 0, xpos2 = 0, ypos2 = 0; + + switch (mScrollDir) + { + case SCROLL_Up: + ypos1 = double(mTicker - mScrollDelay) * fheight / mScrollTime; + ypos2 = ypos1 - fheight; + break; + + case SCROLL_Down: + ypos1 = -double(mTicker - mScrollDelay) * fheight / mScrollTime; + ypos2 = ypos1 + fheight; + break; + + case SCROLL_Left: + default: + xpos1 = double(mTicker - mScrollDelay) * fwidth / mScrollTime; + xpos2 = xpos1 - fwidth; + break; + + case SCROLL_Right: + xpos1 = -double(mTicker - mScrollDelay) * fwidth / mScrollTime; + xpos2 = xpos1 + fwidth; + break; + } + + screen->DrawTexture (tex, xpos1, ypos1, + DTA_VirtualWidth, fwidth, + DTA_VirtualHeight, fheight, + DTA_Masked, false, + TAG_DONE); + screen->DrawTexture (tex2, xpos2, ypos2, + DTA_VirtualWidth, fwidth, + DTA_VirtualHeight, fheight, + DTA_Masked, false, + TAG_DONE); + + screen->FillBorder (NULL); + mBackground = mSecondPic; + } + else + { + Super::Drawer(); + } +} + + +//========================================================================== +// +// +// +//========================================================================== + +DIntermissionController *DIntermissionController::CurrentIntermission; + +DIntermissionController::DIntermissionController(FIntermissionDescriptor *Desc, bool DeleteDesc, BYTE state) +{ + mDesc = Desc; + mDeleteDesc = DeleteDesc; + mIndex = 0; + mAdvance = false; + mScreen = NULL; + mFirst = true; + mGameState = state; + NextPage(); +} + +bool DIntermissionController::NextPage () +{ + FTextureID bg; + bool fill = false; + + if (mIndex == (int)mDesc->mActions.Size() && mDesc->mLink == NAME_None) + { + // last page + return false; + } + + if (mScreen != NULL) + { + bg = mScreen->GetBackground(&fill); + mScreen->Destroy(); + } +again: + while ((unsigned)mIndex < mDesc->mActions.Size()) + { + FIntermissionAction *action = mDesc->mActions[mIndex++]; + if (action->mClass == WIPER_ID) + { + wipegamestate = static_cast(action)->mWipeType; + } + else if (action->mClass == TITLE_ID) + { + Destroy(); + D_StartTitle (); + return false; + } + else + { + // create page here + mScreen = (DIntermissionScreen*)action->mClass->CreateNew(); + mScreen->SetBackground(bg, fill); // copy last screen's background before initializing + mScreen->Init(action, mFirst); + mFirst = false; + return true; + } + } + if (mDesc->mLink != NAME_None) + { + FIntermissionDescriptor **pDesc = IntermissionDescriptors.CheckKey(mDesc->mLink); + if (pDesc != NULL) + { + if (mDeleteDesc) delete mDesc; + mDeleteDesc = false; + mIndex = 0; + mDesc = *pDesc; + goto again; + } + } + return false; +} + +bool DIntermissionController::Responder (event_t *ev) +{ + if (mScreen != NULL) + { + if (!mScreen->mPaletteChanged && ev->type == EV_KeyDown) + { + const char *cmd = Bindings.GetBind (ev->data1); + + if (cmd != NULL && !stricmp (cmd, "toggleconsole")) + return false; + } + + if (mScreen->mTicker < 2) return false; // prevent some leftover events from auto-advancing + int res = mScreen->Responder(ev); + mAdvance = (res == -1); + return !!res; + } + return false; +} + +void DIntermissionController::Ticker () +{ + if (mScreen != NULL) + { + mAdvance |= (mScreen->Ticker() == -1); + } + if (mAdvance) + { + mAdvance = false; + if (!NextPage()) + { + switch (mGameState) + { + case FSTATE_InLevel: + 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; + Destroy(); + break; + + case FSTATE_ChangingLevel: + gameaction = ga_worlddone; + Destroy(); + break; + + default: + break; + } + } + } +} + +void DIntermissionController::Drawer () +{ + if (mScreen != NULL) + { + mScreen->Drawer(); + } +} + +void DIntermissionController::Destroy () +{ + Super::Destroy(); + if (mScreen != NULL) mScreen->Destroy(); + if (mDeleteDesc) delete mDesc; + mDesc = NULL; + if (CurrentIntermission == this) CurrentIntermission = NULL; +} + + +//========================================================================== +// +// starts a new intermission +// +//========================================================================== + +void F_StartIntermission(FIntermissionDescriptor *desc, bool deleteme, BYTE state) +{ + if (DIntermissionController::CurrentIntermission != NULL) + { + DIntermissionController::CurrentIntermission->Destroy(); + } + V_SetBlend (0,0,0,0); + S_StopAllChannels (); + gameaction = ga_nothing; + gamestate = GS_FINALE; + if (state == FSTATE_InLevel) wipegamestate = GS_FINALE; // don't wipe when within a level. + viewactive = false; + automapactive = false; + DIntermissionController::CurrentIntermission = new DIntermissionController(desc, deleteme, state); +} + + +//========================================================================== +// +// starts a new intermission +// +//========================================================================== + +void F_StartIntermission(FName seq, BYTE state) +{ + FIntermissionDescriptor **pdesc = IntermissionDescriptors.CheckKey(seq); + if (pdesc != NULL) + { + F_StartIntermission(*pdesc, false, state); + } +} + + +//========================================================================== +// +// Called by main loop. +// +//========================================================================== + +bool F_Responder (event_t* ev) +{ + if (DIntermissionController::CurrentIntermission != NULL) + { + return DIntermissionController::CurrentIntermission->Responder(ev); + } + return false; +} + +//========================================================================== +// +// Called by main loop. +// +//========================================================================== + +void F_Ticker () +{ + if (DIntermissionController::CurrentIntermission != NULL) + { + DIntermissionController::CurrentIntermission->Ticker(); + } +} + +//========================================================================== +// +// Called by main loop. +// +//========================================================================== + +void F_Drawer () +{ + if (DIntermissionController::CurrentIntermission != NULL) + { + DIntermissionController::CurrentIntermission->Drawer(); + } +} + + +//========================================================================== +// +// Called by main loop. +// +//========================================================================== + +void F_EndFinale () +{ + if (DIntermissionController::CurrentIntermission != NULL) + { + DIntermissionController::CurrentIntermission->Destroy(); + DIntermissionController::CurrentIntermission = NULL; + } +} diff --git a/src/intermission/intermission.h b/src/intermission/intermission.h new file mode 100644 index 000000000..d777d46d3 --- /dev/null +++ b/src/intermission/intermission.h @@ -0,0 +1,320 @@ +#ifndef __INTERMISSION_H +#define __INTERMISSION_H + +#include "doomdef.h" +#include "dobject.h" +#include "m_fixed.h" +#include "textures/textures.h" +#include "s_sound.h" +#include "v_font.h" + +struct event_t; + +#define DECLARE_SUPER_CLASS(cls,parent) \ +private: \ + typedef parent Super; \ + typedef cls ThisClass; + +struct FIntermissionPatch +{ + FName mCondition; + FString mName; + double x, y; +}; + +struct FIIntermissionPatch +{ + FName mCondition; + FTextureID mPic; + double x, y; +}; + +struct FCastSound +{ + BYTE mSequence; + BYTE mIndex; + FString mSound; +}; + +struct FICastSound +{ + BYTE mSequence; + BYTE mIndex; + FSoundID mSound; +}; + +enum EFadeType +{ + FADE_In, + FADE_Out, +}; + +enum EScrollDir +{ + SCROLL_Left, + SCROLL_Right, + SCROLL_Up, + SCROLL_Down, +}; + +// actions that don't create objects +#define WIPER_ID ((const PClass*)intptr_t(-1)) +#define TITLE_ID ((const PClass*)intptr_t(-2)) + +//========================================================================== + +struct FIntermissionAction +{ + int mSize; + const PClass *mClass; + FString mMusic; + int mMusicOrder; + int mCdTrack; + int mCdId; + int mDuration; + FString mBackground; + FString mPalette; + FString mSound; + bool mFlatfill; + bool mMusicLooping; + TArray mOverlays; + + FIntermissionAction(); + virtual bool ParseKey(FScanner &sc); +}; + +struct FIntermissionActionFader : public FIntermissionAction +{ + typedef FIntermissionAction Super; + + EFadeType mFadeType; + + FIntermissionActionFader(); + virtual bool ParseKey(FScanner &sc); +}; + +struct FIntermissionActionWiper : public FIntermissionAction +{ + typedef FIntermissionAction Super; + + gamestate_t mWipeType; + + FIntermissionActionWiper(); + virtual bool ParseKey(FScanner &sc); +}; + +struct FIntermissionActionTextscreen : public FIntermissionAction +{ + typedef FIntermissionAction Super; + + FString mText; + int mTextDelay; + int mTextSpeed; + int mTextX, mTextY; + EColorRange mTextColor; + + FIntermissionActionTextscreen(); + virtual bool ParseKey(FScanner &sc); +}; + +struct FIntermissionActionCast : public FIntermissionAction +{ + typedef FIntermissionAction Super; + + FString mName; + FName mCastClass; + TArray mCastSounds; + + FIntermissionActionCast(); + virtual bool ParseKey(FScanner &sc); +}; + +struct FIntermissionActionScroller : public FIntermissionAction +{ + typedef FIntermissionAction Super; + + FString mSecondPic; + int mScrollDelay; + int mScrollTime; + int mScrollDir; + + FIntermissionActionScroller(); + virtual bool ParseKey(FScanner &sc); +}; + +struct FIntermissionDescriptor +{ + FName mLink; + TArray mActions; +}; + +typedef TMap FIntermissionDescriptorList; + +extern FIntermissionDescriptorList IntermissionDescriptors; + +//========================================================================== + +class DIntermissionScreen : public DObject +{ + DECLARE_CLASS (DIntermissionScreen, DObject) + +protected: + int mDuration; + FTextureID mBackground; + bool mFlatfill; + TArray mOverlays; + + bool CheckOverlay(int i); + +public: + int mTicker; + bool mPaletteChanged; + + DIntermissionScreen() {} + virtual void Init(FIntermissionAction *desc, bool first); + virtual int Responder (event_t *ev); + virtual int Ticker (); + virtual void Drawer (); + void Destroy(); + FTextureID GetBackground(bool *fill) + { + *fill = mFlatfill; + return mBackground; + } + void SetBackground(FTextureID tex, bool fill) + { + mBackground = tex; + mFlatfill = fill; + } +}; + +class DIntermissionScreenFader : public DIntermissionScreen +{ + DECLARE_CLASS (DIntermissionScreenFader, DIntermissionScreen) + + EFadeType mType; + +public: + + DIntermissionScreenFader() {} + virtual void Init(FIntermissionAction *desc, bool first); + virtual int Responder (event_t *ev); + virtual int Ticker (); + virtual void Drawer (); +}; + +class DIntermissionScreenText : public DIntermissionScreen +{ + DECLARE_CLASS (DIntermissionScreenText, DIntermissionScreen) + + const char *mText; + int mTextSpeed; + int mTextX, mTextY; + int mTextCounter; + int mTextDelay; + int mTextLen; + EColorRange mTextColor; + +public: + + DIntermissionScreenText() {} + virtual void Init(FIntermissionAction *desc, bool first); + virtual int Responder (event_t *ev); + virtual void Drawer (); +}; + +class DIntermissionScreenCast : public DIntermissionScreen +{ + DECLARE_CLASS (DIntermissionScreenCast, DIntermissionScreen) + + const char *mName; + const PClass *mClass; + AActor *mDefaults; + TArray mCastSounds; + + 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; + FState* basestate; + FState* advplayerstate; + bool castdeath; + bool castattacking; + int castframes; + int castonmelee; + +public: + + DIntermissionScreenCast() {} + virtual void Init(FIntermissionAction *desc, bool first); + virtual int Responder (event_t *ev); + virtual int Ticker (); + virtual void Drawer (); +}; + +class DIntermissionScreenScroller : public DIntermissionScreen +{ + DECLARE_CLASS (DIntermissionScreenScroller, DIntermissionScreen) + + FTextureID mFirstPic; + FTextureID mSecondPic; + int mScrollDelay; + int mScrollTime; + int mScrollDir; + +public: + + DIntermissionScreenScroller() {} + virtual void Init(FIntermissionAction *desc, bool first); + virtual void Drawer (); +}; + +enum +{ + FSTATE_EndingGame = 0, + FSTATE_ChangingLevel = 1, + FSTATE_InLevel = 2 +}; + +class DIntermissionController : public DObject +{ + DECLARE_CLASS (DIntermissionController, DObject) + HAS_OBJECT_POINTERS + + FIntermissionDescriptor *mDesc; + TObjPtr mScreen; + bool mDeleteDesc; + bool mFirst; + bool mAdvance; + BYTE mGameState; + int mIndex; + + bool NextPage(); + +public: + static DIntermissionController *CurrentIntermission; + + DIntermissionController(FIntermissionDescriptor *mDesc = NULL, bool mDeleteDesc = false, BYTE state = FSTATE_ChangingLevel); + bool Responder (event_t *ev); + void Ticker (); + void Drawer (); + void Destroy(); +}; + + +// Interface for main loop +bool F_Responder (event_t* ev); +void F_Ticker (); +void F_Drawer (); +void F_StartIntermission(FIntermissionDescriptor *desc, bool deleteme, BYTE state); +void F_StartIntermission(FName desc, BYTE state); +void F_EndFinale (); + +// Create an intermission from old cluster data +void F_StartFinale (const char *music, int musicorder, int cdtrack, unsigned int cdid, const char *flat, + const char *text, INTBOOL textInLump, INTBOOL finalePic, INTBOOL lookupText, + bool ending, FName endsequence = NAME_None); + + + +#endif diff --git a/src/intermission/intermission_parse.cpp b/src/intermission/intermission_parse.cpp new file mode 100644 index 000000000..91641d6a9 --- /dev/null +++ b/src/intermission/intermission_parse.cpp @@ -0,0 +1,848 @@ +/* +** intermission_parser.cpp +** Parser for intermission definitions in MAPINFO +** (both new style and old style 'ENDGAME' blocks) +** +**--------------------------------------------------------------------------- +** Copyright 2010 Christoph Oelckers +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + + +#include "intermission/intermission.h" +#include "g_level.h" +#include "w_wad.h" +#include "gi.h" + + +static void ReplaceIntermission(FName intname,FIntermissionDescriptor *desc) +{ + FIntermissionDescriptor ** pDesc = IntermissionDescriptors.CheckKey(intname); + if (pDesc != NULL && *pDesc != NULL) delete *pDesc; + IntermissionDescriptors[intname] = desc; +} + +//========================================================================== +// +// FIntermissionAction +// +//========================================================================== + +FIntermissionAction::FIntermissionAction() +{ + mSize = sizeof(FIntermissionAction); + mClass = RUNTIME_CLASS(DIntermissionScreen); + mMusicOrder = + mCdId = + mCdTrack = + mDuration = 0; + mFlatfill = false; + mMusicLooping = true; +} + +bool FIntermissionAction::ParseKey(FScanner &sc) +{ + if (sc.Compare("music")) + { + sc.MustGetToken('='); + sc.MustGetToken(TK_StringConst); + mMusic = sc.String; + mMusicOrder = 0; + if (sc.CheckToken(',')) + { + sc.MustGetToken(TK_IntConst); + mMusicOrder = sc.Number; + } + return true; + } + else if (sc.Compare("cdmusic")) + { + sc.MustGetToken('='); + sc.MustGetToken(TK_IntConst); + mCdTrack = sc.Number; + mCdId = 0; + if (sc.CheckToken(',')) + { + sc.MustGetToken(TK_IntConst); + mCdId = sc.Number; + } + return true; + } + else if (sc.Compare("Time")) + { + sc.MustGetToken('='); + if (!sc.CheckToken('-')) + { + sc.MustGetFloat(); + mDuration = xs_RoundToInt(sc.Float*TICRATE); + } + else + { + sc.MustGetToken(TK_IntConst); + mDuration = sc.Number; + } + return true; + } + else if (sc.Compare("Background")) + { + sc.MustGetToken('='); + sc.MustGetToken(TK_StringConst); + mBackground = sc.String; + mFlatfill = 0; + if (sc.CheckToken(',')) + { + sc.MustGetToken(TK_IntConst); + mFlatfill = !!sc.Number; + if (sc.CheckToken(',')) + { + sc.MustGetToken(TK_StringConst); + mPalette = sc.String; + } + } + return true; + } + else if (sc.Compare("Sound")) + { + sc.MustGetToken('='); + sc.MustGetToken(TK_StringConst); + mSound = sc.String; + return true; + } + else if (sc.Compare("Draw")) + { + FIntermissionPatch *pat = &mOverlays[mOverlays.Reserve(1)]; + sc.MustGetToken('='); + sc.MustGetToken(TK_StringConst); + pat->mName = sc.String; + sc.MustGetToken(','); + sc.MustGetToken(TK_IntConst); + pat->x = sc.Number; + sc.MustGetToken(','); + sc.MustGetToken(TK_IntConst); + pat->y = sc.Number; + pat->mCondition = NAME_None; + return true; + } + else if (sc.Compare("DrawConditional")) + { + FIntermissionPatch *pat = &mOverlays[mOverlays.Reserve(1)]; + sc.MustGetToken('='); + sc.MustGetToken(TK_StringConst); + pat->mCondition = sc.String; + sc.MustGetToken(','); + sc.MustGetToken(TK_StringConst); + pat->mName = sc.String; + sc.MustGetToken(','); + sc.MustGetToken(TK_IntConst); + pat->x = sc.Number; + sc.MustGetToken(','); + sc.MustGetToken(TK_IntConst); + pat->y = sc.Number; + return true; + } + else return false; +} + +//========================================================================== +// +// FIntermissionActionFader +// +//========================================================================== + +FIntermissionActionFader::FIntermissionActionFader() +{ + mSize = sizeof(FIntermissionActionFader); + mClass = RUNTIME_CLASS(DIntermissionScreenFader); + mFadeType = FADE_In; +} + +bool FIntermissionActionFader::ParseKey(FScanner &sc) +{ + struct FadeType + { + const char *Name; + EFadeType Type; + } + const FT[] = { + { "FadeIn", FADE_In }, + { "FadeOut", FADE_Out }, + { NULL, FADE_In } + }; + + if (sc.Compare("FadeType")) + { + sc.MustGetToken('='); + sc.MustGetToken(TK_Identifier); + int v = sc.MatchString(&FT[0].Name, sizeof(FT[0])); + if (v != -1) mFadeType = FT[v].Type; + return true; + } + else return Super::ParseKey(sc); +} + +//========================================================================== +// +// FIntermissionActionWiper +// +//========================================================================== + +FIntermissionActionWiper::FIntermissionActionWiper() +{ + mSize = sizeof(FIntermissionActionWiper); + mClass = WIPER_ID; + mWipeType = GS_FORCEWIPE; +} + +bool FIntermissionActionWiper::ParseKey(FScanner &sc) +{ + struct WipeType + { + const char *Name; + gamestate_t Type; + } + const FT[] = { + { "Crossfade", GS_FORCEWIPEFADE }, + { "Melt", GS_FORCEWIPEMELT }, + { "Burn", GS_FORCEWIPEBURN }, + { "Default", GS_FORCEWIPE }, + { NULL, GS_FORCEWIPE } + }; + + if (sc.Compare("WipeType")) + { + sc.MustGetToken('='); + sc.MustGetToken(TK_Identifier); + int v = sc.MatchString(&FT[0].Name, sizeof(FT[0])); + if (v != -1) mWipeType = FT[v].Type; + return true; + } + else return Super::ParseKey(sc); +} + +//========================================================================== +// +// FIntermissionActionFader +// +//========================================================================== + +FIntermissionActionTextscreen::FIntermissionActionTextscreen() +{ + mSize = sizeof(FIntermissionActionTextscreen); + mClass = RUNTIME_CLASS(DIntermissionScreenText); + mTextSpeed = 2; + mTextX = -1; // use gameinfo defaults + mTextY = -1; + mTextColor = CR_UNTRANSLATED; + mTextDelay = 10; +} + +bool FIntermissionActionTextscreen::ParseKey(FScanner &sc) +{ + if (sc.Compare("Position")) + { + sc.MustGetToken('='); + sc.MustGetToken(TK_IntConst); + mTextX = sc.Number; + sc.MustGetToken(','); + sc.MustGetToken(TK_IntConst); + mTextY = sc.Number; + return true; + } + else if (sc.Compare("TextLump")) + { + sc.MustGetToken('='); + sc.MustGetToken(TK_StringConst); + int lump = Wads.CheckNumForFullName(sc.String, true); + if (lump > 0) + { + mText = Wads.ReadLump(lump).GetString(); + } + else + { + // only print an error if coming from a PWAD + if (Wads.GetLumpFile(sc.LumpNum) > 1) + sc.ScriptMessage("Unknown text lump '%s'", sc.String); + mText.Format("Unknown text lump '%s'", sc.String); + } + return true; + } + else if (sc.Compare("Text")) + { + sc.MustGetToken('='); + do + { + sc.MustGetToken(TK_StringConst); + mText << sc.String << '\n'; + } + while (sc.CheckToken(',')); + return true; + } + else if (sc.Compare("TextColor")) + { + sc.MustGetToken('='); + sc.MustGetToken(TK_StringConst); + mTextColor = V_FindFontColor(sc.String); + return true; + } + else if (sc.Compare("TextDelay")) + { + sc.MustGetToken('='); + if (!sc.CheckToken('-')) + { + sc.MustGetFloat(); + mTextDelay = xs_RoundToInt(sc.Float*TICRATE); + } + else + { + sc.MustGetToken(TK_IntConst); + mTextDelay = sc.Number; + } + return true; + } + else if (sc.Compare("textspeed")) + { + sc.MustGetToken('='); + sc.MustGetToken(TK_IntConst); + mTextSpeed = sc.Number; + return true; + } + else return Super::ParseKey(sc); +} + +//========================================================================== +// +// FIntermissionAction +// +//========================================================================== + +FIntermissionActionCast::FIntermissionActionCast() +{ + mSize = sizeof(FIntermissionActionCast); + mClass = RUNTIME_CLASS(DIntermissionScreenCast); +} + +bool FIntermissionActionCast::ParseKey(FScanner &sc) +{ + if (sc.Compare("CastName")) + { + sc.MustGetToken('='); + sc.MustGetToken(TK_StringConst); + mName = sc.String; + return true; + } + else if (sc.Compare("CastClass")) + { + sc.MustGetToken('='); + sc.MustGetToken(TK_StringConst); + mCastClass = sc.String; + return true; + } + else if (sc.Compare("AttackSound")) + { + static const char *const seqs[] = {"Missile", "Melee", NULL}; + FCastSound *cs = &mCastSounds[mCastSounds.Reserve(1)]; + sc.MustGetToken('='); + sc.MustGetToken(TK_StringConst); + cs->mSequence = (BYTE)sc.MatchString(seqs); + sc.MustGetToken(','); + sc.MustGetToken(TK_IntConst); + cs->mIndex = (BYTE)sc.Number; + sc.MustGetToken(','); + sc.MustGetToken(TK_StringConst); + cs->mSound = sc.String; + return true; + } + else return Super::ParseKey(sc); +} + +//========================================================================== +// +// FIntermissionActionScroller +// +//========================================================================== + +FIntermissionActionScroller::FIntermissionActionScroller() +{ + mSize = sizeof(FIntermissionActionScroller); + mClass = RUNTIME_CLASS(DIntermissionScreenScroller); + mScrollDelay = 0; + mScrollTime = 640; + mScrollDir = SCROLL_Right; +} + +bool FIntermissionActionScroller::ParseKey(FScanner &sc) +{ + struct ScrollType + { + const char *Name; + EScrollDir Type; + } + const ST[] = { + { "Left", SCROLL_Left }, + { "Right", SCROLL_Right }, + { "Up", SCROLL_Up }, + { "Down", SCROLL_Down }, + { NULL, SCROLL_Left } + }; + + if (sc.Compare("ScrollDirection")) + { + sc.MustGetToken('='); + sc.MustGetToken(TK_Identifier); + int v = sc.MatchString(&ST[0].Name, sizeof(ST[0])); + if (v != -1) mScrollDir = ST[v].Type; + return true; + } + else if (sc.Compare("InitialDelay")) + { + sc.MustGetToken('='); + if (!sc.CheckToken('-')) + { + sc.MustGetFloat(); + mScrollDelay = xs_RoundToInt(sc.Float*TICRATE); + } + else + { + sc.MustGetToken(TK_IntConst); + mScrollDelay = sc.Number; + } + return true; + } + else if (sc.Compare("ScrollTime")) + { + sc.MustGetToken('='); + if (!sc.CheckToken('-')) + { + sc.MustGetFloat(); + mScrollTime = xs_RoundToInt(sc.Float*TICRATE); + } + else + { + sc.MustGetToken(TK_IntConst); + mScrollTime = sc.Number; + } + return true; + } + else if (sc.Compare("Background2")) + { + sc.MustGetToken('='); + sc.MustGetToken(TK_StringConst); + mSecondPic = sc.String; + return true; + } + else return Super::ParseKey(sc); +} + +//========================================================================== +// +// ParseIntermission +// +//========================================================================== + +void FMapInfoParser::ParseIntermissionAction(FIntermissionDescriptor *desc) +{ + FIntermissionAction *ac = NULL; + + sc.MustGetToken(TK_Identifier); + if (sc.Compare("image")) + { + ac = new FIntermissionAction; + } + else if (sc.Compare("scroller")) + { + ac = new FIntermissionActionScroller; + } + else if (sc.Compare("cast")) + { + ac = new FIntermissionActionCast; + } + else if (sc.Compare("Fader")) + { + ac = new FIntermissionActionFader; + } + else if (sc.Compare("Wiper")) + { + ac = new FIntermissionActionWiper; + } + else if (sc.Compare("TextScreen")) + { + ac = new FIntermissionActionTextscreen; + } + else if (sc.Compare("GotoTitle")) + { + ac = new FIntermissionAction; + ac->mClass = TITLE_ID; + } + else if (sc.Compare("Link")) + { + sc.MustGetToken('='); + sc.MustGetToken(TK_Identifier); + desc->mLink = sc.String; + return; + } + else + { + sc.ScriptMessage("Unknown intermission type '%s'", sc.String); + } + + sc.MustGetToken('{'); + while (!sc.CheckToken('}')) + { + bool success = false; + if (!sc.CheckToken(TK_Sound)) + { + sc.MustGetToken(TK_Identifier); + } + if (ac != NULL) + { + success = ac->ParseKey(sc); + if (!success) + { + sc.ScriptMessage("Unknown key name '%s'\n", sc.String); + } + } + if (!success) SkipToNext(); + } + if (ac != NULL) desc->mActions.Push(ac); +} + +//========================================================================== +// +// ParseIntermission +// +//========================================================================== + +void FMapInfoParser::ParseIntermission() +{ + sc.MustGetString(); + FName intname = sc.String; + FIntermissionDescriptor *desc = new FIntermissionDescriptor(); + + ReplaceIntermission(intname, desc); + sc.MustGetToken('{'); + while (!sc.CheckToken('}')) + { + ParseIntermissionAction(desc); + } +} + +//========================================================================== +// +// Parse old style endsequence +// +//========================================================================== + +struct EndSequence +{ + SBYTE EndType; + bool MusicLooping; + bool PlayTheEnd; + FString PicName; + FString PicName2; + FString Music; +}; + +enum EndTypes +{ + END_Pic, + END_Bunny, + END_Cast, + END_Demon +}; + +FName FMapInfoParser::ParseEndGame() +{ + EndSequence newSeq; + static int generated = 0; + + newSeq.EndType = -1; + newSeq.PlayTheEnd = false; + newSeq.MusicLooping = true; + while (!sc.CheckString("}")) + { + sc.MustGetString(); + if (sc.Compare("pic")) + { + ParseAssign(); + sc.MustGetString(); + newSeq.EndType = END_Pic; + newSeq.PicName = sc.String; + } + else if (sc.Compare("hscroll")) + { + ParseAssign(); + newSeq.EndType = END_Bunny; + sc.MustGetString(); + newSeq.PicName = sc.String; + ParseComma(); + sc.MustGetString(); + newSeq.PicName2 = sc.String; + if (CheckNumber()) + newSeq.PlayTheEnd = !!sc.Number; + } + else if (sc.Compare("vscroll")) + { + ParseAssign(); + newSeq.EndType = END_Demon; + sc.MustGetString(); + newSeq.PicName = sc.String; + ParseComma(); + sc.MustGetString(); + newSeq.PicName2 = sc.String; + } + else if (sc.Compare("cast")) + { + newSeq.EndType = END_Cast; + } + else if (sc.Compare("music")) + { + ParseAssign(); + sc.MustGetString(); + newSeq.Music = sc.String; + if (CheckNumber()) + { + newSeq.MusicLooping = !!sc.Number; + } + } + else + { + if (format_type == FMT_New) + { + // Unknown + sc.ScriptMessage("Unknown property '%s' found in endgame definition\n", sc.String); + SkipToNext(); + } + else + { + sc.ScriptError("Unknown property '%s' found in endgame definition\n", sc.String); + } + + } + } + FIntermissionDescriptor *desc = new FIntermissionDescriptor; + FIntermissionAction *action; + + switch (newSeq.EndType) + { + case END_Pic: + action = new FIntermissionAction; + break; + + case END_Bunny: + { + FIntermissionActionScroller *bunny = new FIntermissionActionScroller; + bunny->mSecondPic = newSeq.PicName2; + bunny->mScrollDir = SCROLL_Left; + bunny->mScrollDelay = 230; + bunny->mScrollTime = 640; + bunny->mDuration = 1130; + action = bunny; + if (newSeq.PlayTheEnd) desc->mLink = "TheEnd"; + break; + } + + case END_Demon: + { + FIntermissionActionScroller *demon = new FIntermissionActionScroller; + demon->mSecondPic = newSeq.PicName2; + demon->mScrollDir = SCROLL_Up; + demon->mScrollDelay = 70; + demon->mScrollTime = 600; + action = demon; + break; + } + + case END_Cast: + action = new FIntermissionAction; + action->mDuration = 1; + desc->mLink = "Doom2Cast"; + break; + } + + action->mBackground = newSeq.PicName; + action->mMusic = newSeq.Music; + action->mMusicLooping = newSeq.MusicLooping; + + FString seq; + seq.Format("@EndSequence_%d_", generated++); + ReplaceIntermission(seq, desc); + return FName(seq); +} + +//========================================================================== +// +// Checks map name for end sequence +// +//========================================================================== + +FName FMapInfoParser::CheckEndSequence() +{ + const char *seqname = NULL; + + if (sc.Compare("endgame")) + { + if (!sc.CheckString("{")) + { + // Make Demon Eclipse work again + sc.UnGet(); + goto standard_endgame; + } + return ParseEndGame(); + } + else if (strnicmp (sc.String, "EndGame", 7) == 0) + { + switch (sc.String[7]) + { + case '1': seqname = "Inter_Pic1"; break; + case '2': seqname = "Inter_Pic2"; break; + case '3': seqname = "Inter_Bunny"; break; + case 'C': seqname = "Inter_Cast"; break; + case 'W': seqname = "Inter_Underwater"; break; + case 'S': seqname = "Inter_Strife"; break; + standard_endgame: + default: seqname = "Inter_Pic3"; break; + } + } + else if (sc.Compare("endpic")) + { + ParseComma(); + sc.MustGetString (); + FString seqname; + seqname << "@EndPic_" << sc.String; + FIntermissionDescriptor *desc = new FIntermissionDescriptor; + FIntermissionAction *action = new FIntermissionAction; + action->mBackground = sc.String; + desc->mActions.Push(action); + ReplaceIntermission(seqname, desc); + return FName(seqname); + } + else if (sc.Compare("endbunny")) + { + seqname = "Inter_Bunny"; + } + else if (sc.Compare("endcast")) + { + seqname = "Inter_Cast"; + } + else if (sc.Compare("enddemon")) + { + seqname = "Inter_Demonscroll"; + } + else if (sc.Compare("endchess")) + { + seqname = "Inter_Chess"; + } + else if (sc.Compare("endunderwater")) + { + seqname = "Inter_Underwater"; + } + else if (sc.Compare("endbuystrife")) + { + seqname = "Inter_BuyStrife"; + } + else if (sc.Compare("endtitle")) + { + seqname = "Inter_Titlescreen"; + } + + if (seqname != NULL) + { + return FName(seqname); + } + return NAME_None; +} + + +//========================================================================== +// +// Creates an intermission from the cluster's finale info +// +//========================================================================== + +void F_StartFinale (const char *music, int musicorder, int cdtrack, unsigned int cdid, const char *flat, + const char *text, INTBOOL textInLump, INTBOOL finalePic, INTBOOL lookupText, + bool ending, FName endsequence) +{ + if (text != NULL && *text != 0) + { + FIntermissionActionTextscreen *textscreen = new FIntermissionActionTextscreen; + if (textInLump) + { + int lump = Wads.CheckNumForFullName(text, true); + if (lump > 0) + { + textscreen->mText = Wads.ReadLump(lump).GetString(); + } + else + { + textscreen->mText.Format("Unknown text lump '%s'", text); + } + } + else if (!lookupText) + { + textscreen->mText = text; + } + else + { + textscreen->mText << '$' << text; + } + textscreen->mTextDelay = 10; + textscreen->mBackground = flat; + textscreen->mFlatfill = !finalePic; + + if (music != NULL && *music != 0) + { + textscreen->mMusic = music; + textscreen->mMusicOrder = musicorder; + } + if (cdtrack > 0) + { + textscreen->mCdTrack = cdtrack; + textscreen->mCdId = cdid; + } + FIntermissionDescriptor *desc = new FIntermissionDescriptor; + desc->mActions.Push(textscreen); + + if (ending) + { + desc->mLink = endsequence; + FIntermissionActionWiper *wiper = new FIntermissionActionWiper; + desc->mActions.Push(wiper); + } + + F_StartIntermission(desc, true, ending? FSTATE_EndingGame : FSTATE_ChangingLevel); + } + else if (ending) + { + FIntermissionDescriptor **pdesc = IntermissionDescriptors.CheckKey(endsequence); + if (pdesc != NULL) + { + F_StartIntermission(*pdesc, false, ending? FSTATE_EndingGame : FSTATE_ChangingLevel); + } + } +} diff --git a/src/menu/menu.cpp b/src/menu/menu.cpp index c0ef72a31..3a7e0794f 100644 --- a/src/menu/menu.cpp +++ b/src/menu/menu.cpp @@ -79,6 +79,7 @@ int MenuButtonTickers[NUM_MKEYS]; bool MenuButtonOrigin[NUM_MKEYS]; int BackbuttonTime; fixed_t BackbuttonAlpha; +static bool MenuEnabled = true; #define KEY_REPEAT_DELAY (TICRATE*5/12) @@ -633,7 +634,7 @@ bool M_Responder (event_t *ev) } return DMenu::CurrentMenu->Responder(ev) || !keyup; } - else + else if (MenuEnabled) { if (ev->type == EV_KeyDown) { @@ -761,6 +762,18 @@ void M_Init (void) } +//============================================================================= +// +// +// +//============================================================================= + +void M_EnableMenu (bool on) +{ + MenuEnabled = on; +} + + //============================================================================= // // [RH] Most menus can now be accessed directly diff --git a/src/menu/menu.h b/src/menu/menu.h index f13300bc0..e8632a0b2 100644 --- a/src/menu/menu.h +++ b/src/menu/menu.h @@ -637,6 +637,7 @@ public: struct event_t; +void M_EnableMenu (bool on) ; bool M_Responder (event_t *ev); void M_Ticker (void); void M_Drawer (void); diff --git a/src/namedef.h b/src/namedef.h index ecbe97865..c9168fed3 100644 --- a/src/namedef.h +++ b/src/namedef.h @@ -528,3 +528,13 @@ xx(res_7) xx(res_8) xx(res_9) xx(AlwaysRun) + +// end sequences +xx(Inter_Strife) +xx(Inter_Strife_Good) +xx(Inter_Strife_Sad) +xx(Inter_Strife_Bad) +xx(Inter_Strife_Lose) +xx(Inter_Strife_MAP03) +xx(Inter_Strife_MAP10) +xx(Multiplayer) \ No newline at end of file diff --git a/src/p_lnspec.cpp b/src/p_lnspec.cpp index b5a973cbd..d71bfbf73 100644 --- a/src/p_lnspec.cpp +++ b/src/p_lnspec.cpp @@ -895,8 +895,7 @@ FUNC(LS_Teleport_EndGame) { if (!backSide && CheckIfExitIsGood (it, NULL)) { - G_SetForEndGame (level.nextmap); - G_ExitLevel (0, false); + G_ChangeLevel(NULL, 0, 0); return true; } return false; diff --git a/src/p_user.cpp b/src/p_user.cpp index 37cb230b5..e844d2680 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -44,7 +44,7 @@ #include "w_wad.h" #include "cmdlib.h" #include "sbar.h" -#include "f_finale.h" +#include "intermission/intermission.h" #include "c_console.h" #include "doomdef.h" #include "c_dispatch.h" @@ -1215,9 +1215,9 @@ void APlayerPawn::Die (AActor *source, AActor *inflictor) } } } - if (!multiplayer && (level.flags2 & LEVEL2_DEATHSLIDESHOW)) + if (!multiplayer && level.info->deathsequence != NAME_None) { - F_StartSlideshow (); + F_StartIntermission(level.info->deathsequence, FSTATE_EndingGame); } } } diff --git a/src/statistics.cpp b/src/statistics.cpp index f5778af44..276090ebd 100644 --- a/src/statistics.cpp +++ b/src/statistics.cpp @@ -453,7 +453,6 @@ void STAT_ChangeLevel(const char *newl) level_info_t *l = FindLevelInfo (newl); nextinfo = l->CheckLevelRedirect (); if (nextinfo == NULL) nextinfo = l; - } if (savestatistics == 1) @@ -602,8 +601,7 @@ CCMD(printstats) CCMD(finishgame) { // This CCMD simulates an end-of-game action and exists to end mods that never exit their last level. - G_SetForEndGame (level.nextmap); - G_ExitLevel (0, false); + G_ChangeLevel(NULL, 0, 0); } ADD_STAT(statistics) diff --git a/wadsrc/static/mapinfo/chex.txt b/wadsrc/static/mapinfo/chex.txt index f75838b7f..7ff6e2d67 100644 --- a/wadsrc/static/mapinfo/chex.txt +++ b/wadsrc/static/mapinfo/chex.txt @@ -1,4 +1,5 @@ // MAPINFO for Chex Quest +include "mapinfo/common.txt" gameinfo { @@ -56,6 +57,9 @@ gameinfo pausesign = "M_PAUSE" gibfactor = 1 cursorpic = "chexcurs" + textscreenx = 10 + textscreeny = 10 + defaultendsequence = "Inter_Pic1" } skill baby diff --git a/wadsrc/static/mapinfo/common.txt b/wadsrc/static/mapinfo/common.txt new file mode 100644 index 000000000..d7d6b3d6c --- /dev/null +++ b/wadsrc/static/mapinfo/common.txt @@ -0,0 +1,308 @@ + +Intermission Inter_Titlescreen +{ + GotoTitle + { + } +} + + +Intermission Inter_Pic1 +{ + Image + { + Background = "@1" // index into finalepic FadeIn gameinfo block + } +} + +Intermission Inter_Pic2 +{ + Image + { + Background = "@2" + } +} + +Intermission Inter_Pic3 +{ + Image + { + Background = "@3" + } +} + +Intermission Inter_Bunny +{ + Scroller + { + ScrollDirection = Left + Background = "PFUB1" + Background2 = "PFUB2" + Music = "$MUSIC_BUNNY" + InitialDelay = -230 + Scrolltime = -640 + Time = -1130 + } + Link = TheEnd +} + +Intermission TheEnd +{ + // no backgrounds are set here so this will reuse the previous one. + Image + { + Draw = "END0", 108, 68 + Time = -50 + } + Image + { + Draw = "END1", 108, 68 + Time = -5 + Sound = "weapons/pistol" + } + Image + { + Draw = "END2", 108, 68 + Time = -5 + Sound = "weapons/pistol" + } + Image + { + Draw = "END3", 108, 68 + Time = -5 + Sound = "weapons/pistol" + } + Image + { + Draw = "END4", 108, 68 + Time = -5 + Sound = "weapons/pistol" + } + Image + { + Draw = "END5", 108, 68 + Time = -5 + Sound = "weapons/pistol" + } + Image + { + Draw = "END6", 108, 68 + Time = -5 + Sound = "weapons/pistol" + } +} + +Intermission Inter_Underwater +{ + Image + { + Background = "E2END", 0, "E2PAL" + } + GotoTitle + { + } +} + +Intermission Inter_Demonscroll +{ + Scroller + { + ScrollDirection = Up + Background = "FINAL1" + Background2 = "FINAL2" + InitialDelay = 2 + Scrolltime = -600 + } +} + +Intermission Inter_BuyStrife +{ + Scroller + { + ScrollDirection = Left + Background = "CREDIT" + Background2 = "VELLOGO" + InitialDelay = -230 + Scrolltime = -640 + } +} + +Intermission Inter_Cast +{ + Image + { + // This is only here to initialize the background and the music + Background = "$bgcastcall" + Time = -1 + Music = "$MUSIC_EVIL" + } + Link = Doom2Cast +} + +Intermission Doom2Cast +{ + Cast + { + CastClass = "Zombieman" + CastName = "$CC_ZOMBIE" + AttackSound = "Missile", 1, "grunt/attack" + } + Cast + { + CastClass = "ShotgunGuy" + CastName = "$CC_SHOTGUN" + AttackSound = "Missile", 1, "shotguy/attack" + } + Cast + { + CastClass = "ChaingunGuy" + CastName = "$CC_HEAVY" + AttackSound = "Missile", 1, "chainguy/attack" + AttackSound = "Missile", 2, "chainguy/attack" + AttackSound = "Missile", 3, "chainguy/attack" + } + Cast + { + CastClass = "DoomImp" + CastName = "$CC_IMP" + AttackSound = "Missile", 2, "imp/attack" + } + Cast + { + CastClass = "Demon" + CastName = "$CC_DEMON" + AttackSound = "Melee", 1, "demon/melee" + } + Cast + { + CastClass = "LostSoul" + CastName = "$CC_LOST" + AttackSound = "Missile", 1, "grunt/attack" + } + Cast + { + CastClass = "Cacodemon" + CastName = "$CC_CACO" + AttackSound = "Missile", 1, "caco/attack" + } + Cast + { + CastClass = "HellKnight" + CastName = "$CC_HELL" + AttackSound = "Missile", 1, "baron/attack" + } + Cast + { + CastClass = "BaronOfHell" + CastName = "$CC_BARON" + AttackSound = "Missile", 1, "baron/attack" + } + Cast + { + CastClass = "Arachnotron" + CastName = "$CC_ARACH" + AttackSound = "Missile", 1, "baby/attack" + } + Cast + { + CastClass = "PainElemental" + CastName = "$CC_PAIN" + AttackSound = "Missile", 2, "skull/melee" + } + Cast + { + CastClass = "Revenant" + CastName = "$CC_REVEN" + AttackSound = "Missile", 1, "skeleton/attack" + AttackSound = "Melee", 1, "skeleton/swing" + AttackSound = "Melee", 3, "skeleton/melee" + } + Cast + { + CastClass = "Fatso" + CastName = "$CC_MANCU" + AttackSound = "Missile", 1, "fatso/attack" + AttackSound = "Missile", 4, "fatso/attack" + AttackSound = "Missile", 7, "fatso/attack" + } + Cast + { + CastClass = "Archvile" + CastName = "$CC_ARCH" + AttackSound = "Missile", 1, "vile/start" + } + Cast + { + CastClass = "SpiderMastermind" + CastName = "$CC_SPIDER" + AttackSound = "Missile", 1, "spider/attack" + AttackSound = "Missile", 2, "spider/attack" + } + Cast + { + CastClass = "Cyberdemon" + CastName = "$CC_CYBER" + AttackSound = "Missile", 1, "weapons/rocklf" + AttackSound = "Missile", 3, "weapons/rocklf" + AttackSound = "Missile", 5, "weapons/rocklf" + } + Cast + { + CastClass = "DoomPlayer" + CastName = "$CC_HERO" + AttackSound = "Missile", 0, "weapons/sshotf" + } + Link = Doom2Cast // restart cast call +} + +Intermission Inter_Chess +{ + Fader + { + Music = "Hall" + Background = "FINALE1" + Time = 2 + FadeType = FadeIn + } + TextScreen + { + Background = "FINALE1" + TextSpeed = 3 + TextLump = "win1msg" + Time = -250 + } + TextScreen + { + Music = "Orb" + Background = "FINALE2" + TextSpeed = 3 + TextLump = "win2msg" + Time = -250 + } + Fader + { + Background = "FINALE2" + Time = 2 + FadeType = FadeOut + } + Fader + { + Music = "Chess" + Background = "FINALE3" + DrawConditional = "Multiplayer", "CHESSALL", 20, 0 + DrawConditional = "ClericPlayer", "CHESSC", 60, 0 + DrawConditional = "MagePlayer", "CHESSM", 60, 0 + Time = 2 + FadeType = FadeIn + } + TextScreen + { + Background = "FINALE3" + DrawConditional = "Multiplayer", "CHESSALL", 20, 0 + DrawConditional = "ClericPlayer", "CHESSC", 60, 0 + DrawConditional = "MagePlayer", "CHESSM", 60, 0 + TextSpeed = 3 + TextLump = "win3msg" + Position = 5, 135 + } +} diff --git a/wadsrc/static/mapinfo/doomcommon.txt b/wadsrc/static/mapinfo/doomcommon.txt index 393dbc2ea..0ad964ca5 100644 --- a/wadsrc/static/mapinfo/doomcommon.txt +++ b/wadsrc/static/mapinfo/doomcommon.txt @@ -1,3 +1,5 @@ +include "mapinfo/common.txt" + gameinfo { titlepage = "TITLEPIC" @@ -56,6 +58,9 @@ gameinfo pausesign = "M_PAUSE" gibfactor = 1 cursorpic = "doomcurs" + textscreenx = 10 + textscreeny = 10 + defaultendsequence = "Inter_Cast" } skill baby diff --git a/wadsrc/static/mapinfo/heretic.txt b/wadsrc/static/mapinfo/heretic.txt index 17cd942f2..0e269c8f7 100644 --- a/wadsrc/static/mapinfo/heretic.txt +++ b/wadsrc/static/mapinfo/heretic.txt @@ -1,4 +1,5 @@ // MAPINFO for Heretic (Shareware and Retail) +include "mapinfo/common.txt" gameinfo { @@ -56,6 +57,9 @@ gameinfo pausesign = "PAUSED" gibfactor = 0.5 cursorpic = "herecurs" + textscreenx = 20 + textscreeny = 5 + defaultendsequence = "Inter_Pic1" } skill baby diff --git a/wadsrc/static/mapinfo/hexdd.txt b/wadsrc/static/mapinfo/hexdd.txt new file mode 100644 index 000000000..77231d46d --- /dev/null +++ b/wadsrc/static/mapinfo/hexdd.txt @@ -0,0 +1,8 @@ +include "mapinfo/hexen.txt" + +cluster 3 +{ + hub + music = "hub" + pic = "interpic" +} diff --git a/wadsrc/static/mapinfo/hexen.txt b/wadsrc/static/mapinfo/hexen.txt index 29dddd8d7..6022c541e 100644 --- a/wadsrc/static/mapinfo/hexen.txt +++ b/wadsrc/static/mapinfo/hexen.txt @@ -1,4 +1,5 @@ // A bare-bones MAPINFO for Hexen. +include "mapinfo/common.txt" // Most of the MAPINFO is still in hexen.wad. @@ -54,6 +55,9 @@ gameinfo pausesign = "PAUSED" gibfactor = 0.5 cursorpic = "hexncurs" + textscreenx = 10 + textscreeny = 5 + defaultendsequence = "Inter_Chess" } skill baby @@ -117,7 +121,7 @@ skill nightmare clearepisodes episode "&wt@01" { - name = "Hexen" + name = "Hexen - Beyond Heretic" key = "h" } diff --git a/wadsrc/static/mapinfo/strife.txt b/wadsrc/static/mapinfo/strife.txt index a1aa90fbe..aed50b223 100644 --- a/wadsrc/static/mapinfo/strife.txt +++ b/wadsrc/static/mapinfo/strife.txt @@ -1,4 +1,5 @@ // MAPINFO for Strife (full version and teaser) +include "mapinfo/common.txt" gameinfo { @@ -56,6 +57,175 @@ gameinfo pausesign = "M_PAUSE" gibfactor = 0.5 cursorpic = "strfcurs" + textscreenx = 10 + textscreeny = 10 + defaultendsequence = "Inter_Strife" +} + +Intermission Inter_Strife_Good +{ + Image + { + Music = "D_HAPPY" + Background = "SS4F1" + Sound = "svox/rie01" + Time = 13 + } + Image + { + Background = "SS4F2" + Sound = "svox/bbx01" + Time = 11 + } + Image + { + Background = "SS4F3" + Sound = "svox/bbx02" + Time = 14 + } + Image + { + Background = "SS4F4" + Time = 28 + } + Wiper + { + WipeType = Crossfade + } + Image + { + Music = "D_FAST" + Background = "CREDIT" + } +} + + +Intermission Inter_Strife_Sad +{ + Image + { + Music = "D_SAD" + Background = "SS6F1" + Sound = "svox/ss601a" + Time = 8 + } + Image + { + Background = "SS6F2" + Sound = "svox/ss602a" + Time = 8 + } + Image + { + Background = "SS6F3" + Sound = "svox/ss603a" + Time = 9 + } + Wiper + { + WipeType = Crossfade + } + Image + { + Music = "D_FAST" + Background = "CREDIT" + } +} + +Intermission Inter_Strife_Lose +{ + Image + { + Music = "D_SAD" + Background = "SS5F1" + Sound = "svox/ss501b" + Time = 11 + } + Image + { + Background = "SS5F2" + Sound = "svox/ss502b" + Time = 10 + } + Image + { + Background = "SS5F3" + Sound = "svox/ss503b" + Time = 11 + } + Wiper + { + WipeType = Crossfade + } + Image + { + Music = "D_FAST" + Background = "CREDIT" + } +} + +/* later +Intermission Inter_Strife_Intro +{ +} +*/ + +Intermission Inter_Strife_MAP03 +{ + Image + { + Music = "D_DARK" + Background = "SS2F1" + Sound = "svox/mac10" + Time = 9 + } + Image + { + Background = "SS2F2" + Sound = "svox/mac11" + Time = 10 + } + Image + { + Background = "SS2F3" + Sound = "svox/mac12" + Time = 12 + } + Image + { + Background = "SS2F4" + Sound = "svox/mac13" + Time = 17 + } +} + +Intermission Inter_Strife_MAP10 +{ + Image + { + Music = "D_DARK" + Background = "SS3F1" + Sound = "svox/mac16" + Time = 10 + } + Image + { + Background = "SS3F2" + Sound = "svox/mac17" + Time = 12 + } + Image + { + Background = "SS3F3" + Sound = "svox/mac18" + Time = 12 + } + Image + { + Background = "SS3F4" + Sound = "svox/mac19" + Time = 11 + } } skill baby @@ -140,6 +310,7 @@ map MAP03 "AREA 3: front base" cluster = 1 noallies redirect = "Sigil", "map30" + slideshow = "Inter_Strife_MAP03" } map MAP04 "AREA 4: power station" @@ -212,6 +383,7 @@ map MAP10 "AREA 10: New Front Base" sky1 = "SKYMNT01" music = "D_MARCH" cluster = 1 + slideshow = "Inter_Strife_MAP10" } map MAP11 "AREA 11: Borderlands" @@ -364,7 +536,7 @@ map MAP29 "AREA 29: Entity's Lair" sky1 = "SKYMNT01" music = "D_INSTRY" cluster = 1 - deathslideshow + deathsequence = "Inter_Strife_Lose" } map MAP30 "AREA 30: Abandoned Front Base" diff --git a/zdoom.vcproj b/zdoom.vcproj index 05c7e012b..834715c60 100644 --- a/zdoom.vcproj +++ b/zdoom.vcproj @@ -1,7 +1,7 @@ + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - @@ -962,7 +958,7 @@ /> - - - - - - - - - - - - - - - - - - - - - - - - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + @@ -1884,14 +1836,6 @@ Outputs="$(IntDir)/$(InputName).obj" /> - - - @@ -2081,14 +2025,6 @@ Outputs="$(IntDir)\$(InputName).obj" /> - - - @@ -2099,6 +2035,14 @@ Outputs="$(IntDir)\$(InputName).obj" /> + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -5382,14 +2576,6 @@ AdditionalIncludeDirectories="src\win32;$(NoInherit)" /> - - - @@ -5399,6 +2585,14 @@ AdditionalIncludeDirectories="src\win32;$(NoInherit)" /> + + + @@ -5697,7 +2891,7 @@ /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +