mirror of
https://github.com/id-Software/DOOM-3-BFG.git
synced 2025-03-16 15:41:16 +00:00
936 lines
18 KiB
C++
Executable file
936 lines
18 KiB
C++
Executable file
/*
|
|
===========================================================================
|
|
|
|
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 <http://www.gnu.org/licenses/>.
|
|
|
|
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 <ctype.h>
|
|
|
|
// 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 ; i < MAXPLAYERS ; i++ )
|
|
if( ::g->players[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<int>( strlen( finaletext ) ) * TEXTSPEED + TEXTWAIT;
|
|
}
|
|
|
|
if( !::g->finalestage && ::g->finalecount > static_cast<int>( 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 ; y < SCREENHEIGHT ; y++ )
|
|
{
|
|
for( x = 0 ; x < SCREENWIDTH / 64 ; x++ )
|
|
{
|
|
memcpy( dest, src + ( ( y & 63 ) << 6 ), 64 );
|
|
dest += 64;
|
|
}
|
|
if( SCREENWIDTH & 63 )
|
|
{
|
|
memcpy( dest, src + ( ( y & 63 ) << 6 ), SCREENWIDTH & 63 );
|
|
dest += ( SCREENWIDTH & 63 );
|
|
}
|
|
}
|
|
|
|
V_MarkRect( 0, 0, SCREENWIDTH, SCREENHEIGHT );
|
|
|
|
// draw some of the text onto the screen
|
|
cx = 10;
|
|
cy = 10;
|
|
ch = finaletext;
|
|
|
|
count = ( ::g->finalecount - 10 ) / TEXTSPEED;
|
|
if( count < 0 )
|
|
{
|
|
count = 0;
|
|
}
|
|
for( ; count ; count-- )
|
|
{
|
|
c = *ch++;
|
|
if( !c )
|
|
{
|
|
break;
|
|
}
|
|
if( c == '\n' )
|
|
{
|
|
cx = 10;
|
|
cy += 11;
|
|
continue;
|
|
}
|
|
|
|
c = toupper( c ) - HU_FONTSTART;
|
|
if( c < 0 || c > HU_FONTSIZE )
|
|
{
|
|
cx += 4;
|
|
continue;
|
|
}
|
|
|
|
w = SHORT( ::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( const char* text )
|
|
{
|
|
const 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;
|
|
const size_t name_len = 14;
|
|
char name[name_len];
|
|
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 ; x < ORIGINAL_WIDTH ; x++ )
|
|
{
|
|
if( x + scrolled < 320 )
|
|
{
|
|
F_DrawPatchCol( x, p1, x + scrolled );
|
|
}
|
|
else
|
|
{
|
|
F_DrawPatchCol( x, p2, x + scrolled - 320 );
|
|
}
|
|
}
|
|
|
|
if( ::g->finalecount < 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;
|
|
}
|
|
|
|
idStr::snPrintf( name, name_len, "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;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|