/* =========================================================================== Doom 3 BFG Edition GPL Source Code Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Doom 3 BFG Edition Source Code. If not, see . In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below. If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. =========================================================================== */ #include "Precompiled.h" #include "globaldata.h" #include // Functions. #include "i_system.h" #include "m_swap.h" #include "z_zone.h" #include "v_video.h" #include "w_wad.h" #include "s_sound.h" // Data. #include "dstrings.h" #include "sounds.h" #include "doomstat.h" #include "r_state.h" #include "Main.h" #include "d3xp/Game_local.h" // ? //#include "doomstat.h" //#include "r_local.h" //#include "f_finale.h" // Stage of animation: // 0 = text, 1 = art screen, 2 = character cast const char* e1text = E1TEXT; const char* e2text = E2TEXT; const char* e3text = E3TEXT; const char* e4text = E4TEXT; const char* c1text = C1TEXT; const char* c2text = C2TEXT; const char* c3text = C3TEXT; const char* c4text = C4TEXT; const char* c5text = C5TEXT; const char* c6text = C6TEXT; const char* c7text = C7TEXT; const char* c8Text = C8TEXT; const char* p1text = P1TEXT; const char* p2text = P2TEXT; const char* p3text = P3TEXT; const char* p4text = P4TEXT; const char* p5text = P5TEXT; const char* p6text = P6TEXT; const char* t1text = T1TEXT; const char* t2text = T2TEXT; const char* t3text = T3TEXT; const char* t4text = T4TEXT; const char* t5text = T5TEXT; const char* t6text = T6TEXT; const char* finaletext; const char* finaleflat; void F_StartCast (void); void F_CastTicker (void); qboolean F_CastResponder (event_t *ev); void F_CastDrawer (void); // // F_StartFinale // void F_StartFinale (void) { ::g->gameaction = ga_nothing; ::g->gamestate = GS_FINALE; ::g->viewactive = false; ::g->automapactive = false; // Check for end of episode/mission bool endOfMission = false; if ( ( ::g->gamemission == doom || ::g->gamemission == doom2 || ::g->gamemission == pack_tnt || ::g->gamemission == pack_plut ) && ::g->gamemap == 30 ) { endOfMission = true; } else if ( ::g->gamemission == pack_nerve && ::g->gamemap == 8 ) { endOfMission = true; } else if ( ::g->gamemission == pack_master && ::g->gamemap == 21 ) { endOfMission = true; } localCalculateAchievements( endOfMission ); // Okay - IWAD dependend stuff. // This has been changed severly, and // some stuff might have changed in the process. switch ( ::g->gamemode ) { // DOOM 1 - E1, E3 or E4, but each nine missions case shareware: case registered: case retail: { S_ChangeMusic(mus_victor, true); switch (::g->gameepisode) { case 1: finaleflat = "FLOOR4_8"; finaletext = e1text; break; case 2: finaleflat = "SFLR6_1"; finaletext = e2text; break; case 3: finaleflat = "MFLR8_4"; finaletext = e3text; break; case 4: finaleflat = "MFLR8_3"; finaletext = e4text; break; default: // Ouch. break; } break; } // DOOM II and missions packs with E1, M34 case commercial: { S_ChangeMusic(mus_read_m, true); if ( ::g->gamemission == doom2 || ::g->gamemission == pack_tnt || ::g->gamemission == pack_plut ) { switch (::g->gamemap) { case 6: finaleflat = "SLIME16"; finaletext = c1text; break; case 11: finaleflat = "RROCK14"; finaletext = c2text; break; case 20: finaleflat = "RROCK07"; finaletext = c3text; break; case 30: finaleflat = "RROCK17"; finaletext = c4text; break; case 15: finaleflat = "RROCK13"; finaletext = c5text; break; case 31: finaleflat = "RROCK19"; finaletext = c6text; break; default: // Ouch. break; } } else if( ::g->gamemission == pack_master ) { switch (::g->gamemap) { case 21: finaleflat = "SLIME16"; finaletext = c8Text; break; } } else if ( ::g->gamemission == pack_nerve ) { switch( ::g->gamemap ){ case 8: finaleflat = "SLIME16"; finaletext = c7text; break; } } break; } // Indeterminate. default: S_ChangeMusic(mus_read_m, true); finaleflat = "F_SKY1"; // Not used anywhere else. finaletext = c1text; // FIXME - other text, music? break; } ::g->finalestage = 0; ::g->finalecount = 0; } bool finaleButtonPressed = false; bool startButtonPressed = false; qboolean F_Responder (event_t *event) { if( !common->IsMultiplayer() && event->type == ev_keydown && event->data1 == KEY_ESCAPE ) { startButtonPressed = true; return true; } if (::g->finalestage == 2) return F_CastResponder (event); return false; } // // F_Ticker // void F_Ticker (void) { int i; // check for skipping if ( (::g->gamemode == commercial) && ( ::g->finalecount > 50) ) { // go on to the next level for (i=0 ; iplayers[i].cmd.buttons) break; if ( finaleButtonPressed || i < MAXPLAYERS) { bool castStarted = false; if( ::g->gamemission == doom2 || ::g->gamemission == pack_plut || ::g->gamemission == pack_tnt ) { if (::g->gamemap == 30) { F_StartCast (); castStarted = true; } } else if( ::g->gamemission == pack_master ) { if( :: g->gamemap == 21 ) { F_StartCast (); castStarted = true; } } else if( ::g->gamemission == pack_nerve ) { if( :: g->gamemap == 8 ) { F_StartCast (); castStarted = true; } } if( castStarted == false ) { ::g->gameaction = ga_worlddone; } } } bool SkipTheText = finaleButtonPressed; // advance animation ::g->finalecount++; finaleButtonPressed = false; if (::g->finalestage == 2) { F_CastTicker (); return; } if ( ::g->gamemode == commercial) { startButtonPressed = false; return; } if( SkipTheText && ( ::g->finalecount > 50) ) { ::g->finalecount = static_cast(strlen(finaletext)) * TEXTSPEED + TEXTWAIT; } if (!::g->finalestage && ::g->finalecount > static_cast(strlen(finaletext)) * TEXTSPEED + TEXTWAIT) { ::g->finalecount = 0; ::g->finalestage = 1; ::g->wipegamestate = (gamestate_t)-1; // force a wipe if (::g->gameepisode == 3) S_StartMusic (mus_bunny); } startButtonPressed = false; } // // F_TextWrite // #include "hu_stuff.h" void F_TextWrite (void) { byte* src; byte* dest; int x,y,w; int count; const char* ch; int c; int cx; int cy; if(::g->finalecount == 60 ) { DoomLib::ShowXToContinue( true ); } // erase the entire screen to a tiled background src = (byte*)W_CacheLumpName ( finaleflat , PU_CACHE_SHARED); dest = ::g->screens[0]; for (y=0 ; yfinalecount - 10)/TEXTSPEED; if (count < 0) count = 0; for ( ; count ; count-- ) { c = *ch++; if (!c) break; if (c == '\n') { cx = 10; cy += 11; continue; } c = toupper(c) - HU_FONTSTART; if (c < 0 || c> HU_FONTSIZE) { cx += 4; continue; } w = SHORT (::g->hu_font[c]->width); if (cx+w > SCREENWIDTH) break; V_DrawPatch(cx, cy, 0, ::g->hu_font[c]); cx+=w; } } // // Final DOOM 2 animation // Casting by id Software. // in order of appearance // castinfo_t castorder[] = { {CC_ZOMBIE, MT_POSSESSED}, {CC_SHOTGUN, MT_SHOTGUY}, {CC_HEAVY, MT_CHAINGUY}, {CC_IMP, MT_TROOP}, {CC_DEMON, MT_SERGEANT}, {CC_LOST, MT_SKULL}, {CC_CACO, MT_HEAD}, {CC_HELL, MT_KNIGHT}, {CC_BARON, MT_BRUISER}, {CC_ARACH, MT_BABY}, {CC_PAIN, MT_PAIN}, {CC_REVEN, MT_UNDEAD}, {CC_MANCU, MT_FATSO}, {CC_ARCH, MT_VILE}, {CC_SPIDER, MT_SPIDER}, {CC_CYBER, MT_CYBORG}, {CC_HERO, MT_PLAYER}, {NULL,(mobjtype_t)0} }; // // F_StartCast // void F_StartCast (void) { if ( ::g->finalestage != 2 ) { ::g->wipegamestate = (gamestate_t)-1; // force a screen wipe ::g->castnum = 0; ::g->caststate = &::g->states[mobjinfo[castorder[::g->castnum].type].seestate]; ::g->casttics = ::g->caststate->tics; ::g->castdeath = false; ::g->finalestage = 2; ::g->castframes = 0; ::g->castonmelee = 0; ::g->castattacking = false; S_ChangeMusic(mus_evil, true); ::g->caststartmenu = ::g->finalecount + 50; } } // // F_CastTicker // void F_CastTicker (void) { int st; int sfx; if( ::g->finalecount == ::g->caststartmenu ) { DoomLib::ShowXToContinue( true ); } if (--::g->casttics > 0) return; // not time to change state yet if (::g->caststate->tics == -1 || ::g->caststate->nextstate == S_NULL) { // switch from deathstate to next monster ::g->castnum++; ::g->castdeath = false; if (castorder[::g->castnum].name == NULL) ::g->castnum = 0; if (mobjinfo[castorder[::g->castnum].type].seesound) S_StartSound (NULL, mobjinfo[castorder[::g->castnum].type].seesound); ::g->caststate = &::g->states[mobjinfo[castorder[::g->castnum].type].seestate]; ::g->castframes = 0; } else { // just advance to next state in animation if (::g->caststate == &::g->states[S_PLAY_ATK1]) goto stopattack; // Oh, gross hack! st = ::g->caststate->nextstate; ::g->caststate = &::g->states[st]; ::g->castframes++; // sound hacks.... switch (st) { case S_PLAY_ATK1: sfx = sfx_dshtgn; break; case S_POSS_ATK2: sfx = sfx_pistol; break; case S_SPOS_ATK2: sfx = sfx_shotgn; break; case S_VILE_ATK2: sfx = sfx_vilatk; break; case S_SKEL_FIST2: sfx = sfx_skeswg; break; case S_SKEL_FIST4: sfx = sfx_skepch; break; case S_SKEL_MISS2: sfx = sfx_skeatk; break; case S_FATT_ATK8: case S_FATT_ATK5: case S_FATT_ATK2: sfx = sfx_firsht; break; case S_CPOS_ATK2: case S_CPOS_ATK3: case S_CPOS_ATK4: sfx = sfx_shotgn; break; case S_TROO_ATK3: sfx = sfx_claw; break; case S_SARG_ATK2: sfx = sfx_sgtatk; break; case S_BOSS_ATK2: case S_BOS2_ATK2: case S_HEAD_ATK2: sfx = sfx_firsht; break; case S_SKULL_ATK2: sfx = sfx_sklatk; break; case S_SPID_ATK2: case S_SPID_ATK3: sfx = sfx_shotgn; break; case S_BSPI_ATK2: sfx = sfx_plasma; break; case S_CYBER_ATK2: case S_CYBER_ATK4: case S_CYBER_ATK6: sfx = sfx_rlaunc; break; case S_PAIN_ATK3: sfx = sfx_sklatk; break; default: sfx = 0; break; } if (sfx) S_StartSound (NULL, sfx); } if (::g->castframes == 12) { // go into attack frame ::g->castattacking = true; if (::g->castonmelee) ::g->caststate=&::g->states[mobjinfo[castorder[::g->castnum].type].meleestate]; else ::g->caststate=&::g->states[mobjinfo[castorder[::g->castnum].type].missilestate]; ::g->castonmelee ^= 1; if (::g->caststate == &::g->states[S_NULL]) { if (::g->castonmelee) ::g->caststate= &::g->states[mobjinfo[castorder[::g->castnum].type].meleestate]; else ::g->caststate= &::g->states[mobjinfo[castorder[::g->castnum].type].missilestate]; } } if (::g->castattacking) { if (::g->castframes == 24 || ::g->caststate == &::g->states[mobjinfo[castorder[::g->castnum].type].seestate] ) { stopattack: ::g->castattacking = false; ::g->castframes = 0; ::g->caststate = &::g->states[mobjinfo[castorder[::g->castnum].type].seestate]; } } ::g->casttics = ::g->caststate->tics; if (::g->casttics == -1) ::g->casttics = 15; } // // F_CastResponder // qboolean F_CastResponder (event_t* ev) { if (ev->type != ev_keydown) return false; if (::g->castdeath) return true; // already in dying frames // go into death frame ::g->castdeath = true; ::g->caststate = &::g->states[mobjinfo[castorder[::g->castnum].type].deathstate]; ::g->casttics = ::g->caststate->tics; ::g->castframes = 0; ::g->castattacking = false; if (mobjinfo[castorder[::g->castnum].type].deathsound) S_StartSound (NULL, mobjinfo[castorder[::g->castnum].type].deathsound); return true; } void F_CastPrint (char* text) { char* ch; int c; int cx; int w; int width; // find width ch = text; width = 0; while (ch) { c = *ch++; if (!c) break; c = toupper(c) - HU_FONTSTART; if (c < 0 || c> HU_FONTSIZE) { width += 4; continue; } w = SHORT (::g->hu_font[c]->width); width += w; } // draw it cx = 160-width/2; ch = text; while (ch) { c = *ch++; if (!c) break; c = toupper(c) - HU_FONTSTART; if (c < 0 || c> HU_FONTSIZE) { cx += 4; continue; } w = SHORT (::g->hu_font[c]->width); V_DrawPatch(cx, 180, 0, ::g->hu_font[c]); cx+=w; } } // // F_CastDrawer // void V_DrawPatchFlipped (int x, int y, int scrn, patch_t *patch); void F_CastDrawer (void) { spritedef_t* sprdef; spriteframe_t* sprframe; int lump; qboolean flip; patch_t* patch; // erase the entire screen to a background V_DrawPatch (0,0,0, (patch_t*)W_CacheLumpName ("BOSSBACK", PU_CACHE_SHARED)); F_CastPrint (castorder[::g->castnum].name); // draw the current frame in the middle of the screen sprdef = &::g->sprites[::g->caststate->sprite]; sprframe = &sprdef->spriteframes[ ::g->caststate->frame & FF_FRAMEMASK]; lump = sprframe->lump[0]; flip = (qboolean)sprframe->flip[0]; patch = (patch_t*)W_CacheLumpNum (lump+::g->firstspritelump, PU_CACHE_SHARED); if (flip) V_DrawPatchFlipped (160,170,0,patch); else V_DrawPatch (160,170,0,patch); } // // F_DrawPatchCol // void F_DrawPatchCol( int x, patch_t* patch, int col ) { postColumn_t* column; byte* source; int count; column = (postColumn_t *)((byte *)patch + LONG(patch->columnofs[col])); int destx = x; int desty = 0; // step through the posts in a column while (column->topdelta != 0xff ) { source = (byte *)column + 3; desty = column->topdelta; count = column->length; while (count--) { int scaledx, scaledy; scaledx = destx * GLOBAL_IMAGE_SCALER; scaledy = desty * GLOBAL_IMAGE_SCALER; byte src = *source++; for ( int i = 0; i < GLOBAL_IMAGE_SCALER; i++ ) { for ( int j = 0; j < GLOBAL_IMAGE_SCALER; j++ ) { ::g->screens[0][( scaledx + j ) + ( scaledy + i ) * SCREENWIDTH] = src; } } desty++; } column = (postColumn_t *)( (byte *)column + column->length + 4 ); } } // // F_BunnyScroll // void F_BunnyScroll (void) { int scrolled; int x; patch_t* p1; patch_t* p2; char name[10]; int stage; p1 = (patch_t*)W_CacheLumpName ("PFUB2", PU_LEVEL_SHARED); p2 = (patch_t*)W_CacheLumpName ("PFUB1", PU_LEVEL_SHARED); V_MarkRect (0, 0, SCREENWIDTH, SCREENHEIGHT); scrolled = 320 - (::g->finalecount-230)/2; if (scrolled > 320) scrolled = 320; if (scrolled < 0) scrolled = 0; for ( x=0 ; xfinalecount < 1130) return; if (::g->finalecount < 1180) { V_DrawPatch ((ORIGINAL_WIDTH-13*8)/2, (ORIGINAL_HEIGHT-8*8)/2,0, (patch_t*)W_CacheLumpName ("END0",PU_CACHE_SHARED)); ::g->laststage = 0; return; } stage = (::g->finalecount-1180) / 5; if (stage > 6) stage = 6; if (stage > ::g->laststage) { S_StartSound (NULL, sfx_pistol); ::g->laststage = stage; } sprintf (name,"END%i",stage); V_DrawPatch ((ORIGINAL_WIDTH-13*8)/2, (ORIGINAL_HEIGHT-8*8)/2,0, (patch_t*)W_CacheLumpName (name,PU_CACHE_SHARED)); } // // F_Drawer // void F_Drawer (void) { if (::g->finalestage == 2) { F_CastDrawer (); return; } if (!::g->finalestage) F_TextWrite (); else { switch (::g->gameepisode) { case 1: if ( ::g->gamemode == retail ) V_DrawPatch (0,0,0, (patch_t*)W_CacheLumpName("CREDIT",PU_CACHE_SHARED)); else V_DrawPatch (0,0,0, (patch_t*)W_CacheLumpName("HELP2",PU_CACHE_SHARED)); break; case 2: V_DrawPatch(0,0,0, (patch_t*)W_CacheLumpName("VICTORY2",PU_CACHE_SHARED)); break; case 3: F_BunnyScroll (); break; case 4: V_DrawPatch (0,0,0, (patch_t*)W_CacheLumpName("ENDPIC",PU_CACHE_SHARED)); break; } } }