2014-03-15 16:59:03 +00:00
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team.
2021-05-07 15:45:56 +00:00
// Copyright (C) 1999-2021 by Sonic Team Junior.
2014-03-15 16:59:03 +00:00
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
/// \file st_stuff.c
/// \brief Status bar code
/// Does the face/direction indicator animatin.
/// Does palette indicators as well (red pain/berserk, bright pickup)
# include "doomdef.h"
# include "g_game.h"
# include "r_local.h"
# include "p_local.h"
# include "f_finale.h"
# include "st_stuff.h"
# include "i_video.h"
# include "v_video.h"
# include "z_zone.h"
# include "hu_stuff.h"
2019-11-18 15:56:41 +00:00
# include "console.h"
2014-03-15 16:59:03 +00:00
# include "s_sound.h"
# include "i_system.h"
# include "m_menu.h"
# include "m_cheat.h"
2018-04-08 12:03:40 +00:00
# include "m_misc.h" // moviemode
# include "m_anigif.h" // cv_gif_downscale
2014-03-15 16:59:03 +00:00
# include "p_setup.h" // NiGHTS grading
//random index
# include "m_random.h"
// item finder
# include "m_cond.h"
# ifdef HWRENDER
# include "hardware/hw_main.h"
# endif
# include "lua_hud.h"
2021-07-07 07:23:51 +00:00
# include "lua_hook.h"
2014-03-15 16:59:03 +00:00
UINT16 objectsdrawn = 0 ;
//
// STATUS BAR DATA
//
patch_t * faceprefix [ MAXSKINS ] ; // face status patches
patch_t * superprefix [ MAXSKINS ] ; // super face status patches
// ------------------------------------------
// status bar overlay
// ------------------------------------------
// icons for overlay
patch_t * sboscore ; // Score logo
patch_t * sbotime ; // Time logo
patch_t * sbocolon ; // Colon for time
2014-03-25 02:17:59 +00:00
patch_t * sboperiod ; // Period for time centiseconds
2014-03-15 16:59:03 +00:00
patch_t * livesback ; // Lives icon background
2019-09-10 23:50:51 +00:00
patch_t * stlivex ;
2014-03-25 02:17:59 +00:00
static patch_t * nrec_timer ; // Timer for NiGHTS records
2014-03-15 16:59:03 +00:00
static patch_t * sborings ;
2019-09-04 14:21:00 +00:00
static patch_t * slidgame ;
static patch_t * slidtime ;
static patch_t * slidover ;
2017-03-19 12:37:04 +00:00
static patch_t * sboredrings ;
2017-07-04 13:58:58 +00:00
static patch_t * sboredtime ;
2014-03-15 16:59:03 +00:00
static patch_t * getall ; // Special Stage HUD
static patch_t * timeup ; // Special Stage HUD
static patch_t * hunthoming [ 6 ] ;
static patch_t * itemhoming [ 6 ] ;
static patch_t * race1 ;
static patch_t * race2 ;
static patch_t * race3 ;
static patch_t * racego ;
static patch_t * nightslink ;
static patch_t * curweapon ;
static patch_t * normring ;
static patch_t * bouncering ;
static patch_t * infinityring ;
static patch_t * autoring ;
static patch_t * explosionring ;
static patch_t * scatterring ;
static patch_t * grenadering ;
static patch_t * railring ;
static patch_t * jumpshield ;
static patch_t * forceshield ;
static patch_t * ringshield ;
static patch_t * watershield ;
static patch_t * bombshield ;
static patch_t * pityshield ;
2019-06-22 15:44:32 +00:00
static patch_t * pinkshield ;
2016-10-13 14:13:33 +00:00
static patch_t * flameshield ;
static patch_t * bubbleshield ;
static patch_t * thundershield ;
2014-03-15 16:59:03 +00:00
static patch_t * invincibility ;
static patch_t * sneakers ;
static patch_t * gravboots ;
static patch_t * nonicon ;
2018-03-26 22:53:09 +00:00
static patch_t * nonicon2 ;
2014-03-15 16:59:03 +00:00
static patch_t * bluestat ;
static patch_t * byelstat ;
static patch_t * orngstat ;
static patch_t * redstat ;
static patch_t * yelstat ;
static patch_t * nbracket ;
2019-08-02 22:51:22 +00:00
static patch_t * nring ;
2014-03-15 16:59:03 +00:00
static patch_t * nhud [ 12 ] ;
static patch_t * nsshud ;
2018-06-03 21:41:54 +00:00
static patch_t * nbon [ 12 ] ;
static patch_t * nssbon ;
2014-03-15 16:59:03 +00:00
static patch_t * narrow [ 9 ] ;
static patch_t * nredar [ 8 ] ; // Red arrow
static patch_t * drillbar ;
static patch_t * drillfill [ 3 ] ;
static patch_t * capsulebar ;
static patch_t * capsulefill ;
patch_t * ngradeletters [ 7 ] ;
static patch_t * minus5sec ;
static patch_t * minicaps ;
static patch_t * gotrflag ;
static patch_t * gotbflag ;
2020-01-20 00:55:08 +00:00
static patch_t * fnshico ;
2014-03-15 16:59:03 +00:00
static boolean facefreed [ MAXPLAYERS ] ;
hudinfo_t hudinfo [ NUMHUDITEMS ] =
{
2018-03-19 23:08:51 +00:00
{ 16 , 176 , V_SNAPTOLEFT | V_SNAPTOBOTTOM } , // HUD_LIVES
{ 16 , 42 , V_SNAPTOLEFT | V_SNAPTOTOP } , // HUD_RINGS
{ 96 , 42 , V_SNAPTOLEFT | V_SNAPTOTOP } , // HUD_RINGSNUM
{ 120 , 42 , V_SNAPTOLEFT | V_SNAPTOTOP } , // HUD_RINGSNUMTICS
{ 16 , 10 , V_SNAPTOLEFT | V_SNAPTOTOP } , // HUD_SCORE
{ 120 , 10 , V_SNAPTOLEFT | V_SNAPTOTOP } , // HUD_SCORENUM
{ 16 , 26 , V_SNAPTOLEFT | V_SNAPTOTOP } , // HUD_TIME
{ 72 , 26 , V_SNAPTOLEFT | V_SNAPTOTOP } , // HUD_MINUTES
{ 72 , 26 , V_SNAPTOLEFT | V_SNAPTOTOP } , // HUD_TIMECOLON
{ 96 , 26 , V_SNAPTOLEFT | V_SNAPTOTOP } , // HUD_SECONDS
{ 96 , 26 , V_SNAPTOLEFT | V_SNAPTOTOP } , // HUD_TIMETICCOLON
{ 120 , 26 , V_SNAPTOLEFT | V_SNAPTOTOP } , // HUD_TICS
{ 0 , 56 , V_SNAPTOLEFT | V_SNAPTOTOP } , // HUD_SS_TOTALRINGS
{ 110 , 93 , 0 } , // HUD_GETRINGS
{ 160 , 93 , 0 } , // HUD_GETRINGSNUM
{ 124 , 160 , 0 } , // HUD_TIMELEFT
{ 168 , 176 , 0 } , // HUD_TIMELEFTNUM
{ 130 , 93 , 0 } , // HUD_TIMEUP
{ 152 , 168 , 0 } , // HUD_HUNTPICS
2018-03-26 22:53:09 +00:00
{ 288 , 176 , V_SNAPTORIGHT | V_SNAPTOBOTTOM } , // HUD_POWERUPS
2014-03-15 16:59:03 +00:00
} ;
//
// STATUS BAR CODE
//
boolean ST_SameTeam ( player_t * a , player_t * b )
{
// Just pipe team messages to everyone in co-op or race.
if ( ! G_RingSlingerGametype ( ) )
return true ;
// Spectator chat.
if ( a - > spectator & & b - > spectator )
return true ;
// Team chat.
if ( G_GametypeHasTeams ( ) )
return a - > ctfteam = = b - > ctfteam ;
if ( G_TagGametype ( ) )
return ( ( a - > pflags & PF_TAGIT ) = = ( b - > pflags & PF_TAGIT ) ) ;
return false ;
}
static boolean st_stopped = true ;
2019-11-18 14:39:54 +00:00
void ST_Ticker ( boolean run )
2014-03-15 16:59:03 +00:00
{
if ( st_stopped )
return ;
2019-11-18 14:39:54 +00:00
if ( run )
ST_runTitleCard ( ) ;
2014-03-15 16:59:03 +00:00
}
// 0 is default, any others are special palettes.
2019-01-02 03:41:52 +00:00
INT32 st_palette = 0 ;
2019-11-23 21:41:28 +00:00
INT32 st_translucency = 10 ;
2014-03-15 16:59:03 +00:00
void ST_doPaletteStuff ( void )
{
INT32 palette ;
2020-11-16 06:37:12 +00:00
if ( stplyr & & stplyr - > flashcount )
2014-03-15 16:59:03 +00:00
palette = stplyr - > flashpal ;
else
palette = 0 ;
2018-05-17 21:17:20 +00:00
# ifdef HWRENDER
if ( rendermode = = render_opengl )
palette = 0 ; // No flashpals here in OpenGL
# endif
2014-03-15 16:59:03 +00:00
if ( palette ! = st_palette )
{
st_palette = palette ;
if ( rendermode ! = render_none )
{
V_SetPaletteLump ( GetPalette ( ) ) ; // Reset the palette
if ( ! splitscreen )
V_SetPalette ( palette ) ;
}
}
}
void ST_UnloadGraphics ( void )
{
2020-09-07 05:23:07 +00:00
Patch_FreeTag ( PU_HUDGFX ) ;
2014-03-15 16:59:03 +00:00
}
void ST_LoadGraphics ( void )
{
int i ;
// SRB2 border patch
2020-07-11 10:11:26 +00:00
// st_borderpatchnum = W_GetNumForName("GFZFLR01");
// scr_borderpatch = W_CacheLumpNum(st_borderpatchnum, PU_HUDGFX);
2014-03-15 16:59:03 +00:00
// the original Doom uses 'STF' as base name for all face graphics
// Graue 04-08-2004: face/name graphics are now indexed by skins
// but load them in R_AddSkins, that gets called
// first anyway
// cache the status bar overlay icons (fullscreen mode)
2017-03-19 12:37:04 +00:00
// Prefix "STT" is whitelisted (doesn't trigger ISGAMEMODIFIED), btw
sborings = W_CachePatchName ( " STTRINGS " , PU_HUDGFX ) ;
sboredrings = W_CachePatchName ( " STTRRING " , PU_HUDGFX ) ;
sboscore = W_CachePatchName ( " STTSCORE " , PU_HUDGFX ) ;
sbotime = W_CachePatchName ( " STTTIME " , PU_HUDGFX ) ; // Time logo
2017-07-04 13:58:58 +00:00
sboredtime = W_CachePatchName ( " STTRTIME " , PU_HUDGFX ) ;
2017-03-19 12:37:04 +00:00
sbocolon = W_CachePatchName ( " STTCOLON " , PU_HUDGFX ) ; // Colon for time
sboperiod = W_CachePatchName ( " STTPERIO " , PU_HUDGFX ) ; // Period for time centiseconds
2019-09-04 14:21:00 +00:00
slidgame = W_CachePatchName ( " SLIDGAME " , PU_HUDGFX ) ;
slidtime = W_CachePatchName ( " SLIDTIME " , PU_HUDGFX ) ;
slidover = W_CachePatchName ( " SLIDOVER " , PU_HUDGFX ) ;
2014-03-15 16:59:03 +00:00
stlivex = W_CachePatchName ( " STLIVEX " , PU_HUDGFX ) ;
livesback = W_CachePatchName ( " STLIVEBK " , PU_HUDGFX ) ;
2014-03-25 02:17:59 +00:00
nrec_timer = W_CachePatchName ( " NGRTIMER " , PU_HUDGFX ) ; // Timer for NiGHTS
2014-03-15 16:59:03 +00:00
getall = W_CachePatchName ( " GETALL " , PU_HUDGFX ) ; // Special Stage HUD
timeup = W_CachePatchName ( " TIMEUP " , PU_HUDGFX ) ; // Special Stage HUD
race1 = W_CachePatchName ( " RACE1 " , PU_HUDGFX ) ;
race2 = W_CachePatchName ( " RACE2 " , PU_HUDGFX ) ;
race3 = W_CachePatchName ( " RACE3 " , PU_HUDGFX ) ;
racego = W_CachePatchName ( " RACEGO " , PU_HUDGFX ) ;
nightslink = W_CachePatchName ( " NGHTLINK " , PU_HUDGFX ) ;
for ( i = 0 ; i < 6 ; + + i )
{
hunthoming [ i ] = W_CachePatchName ( va ( " HOMING%d " , i + 1 ) , PU_HUDGFX ) ;
itemhoming [ i ] = W_CachePatchName ( va ( " HOMITM%d " , i + 1 ) , PU_HUDGFX ) ;
}
curweapon = W_CachePatchName ( " CURWEAP " , PU_HUDGFX ) ;
normring = W_CachePatchName ( " RINGIND " , PU_HUDGFX ) ;
bouncering = W_CachePatchName ( " BNCEIND " , PU_HUDGFX ) ;
infinityring = W_CachePatchName ( " INFNIND " , PU_HUDGFX ) ;
autoring = W_CachePatchName ( " AUTOIND " , PU_HUDGFX ) ;
explosionring = W_CachePatchName ( " BOMBIND " , PU_HUDGFX ) ;
scatterring = W_CachePatchName ( " SCATIND " , PU_HUDGFX ) ;
grenadering = W_CachePatchName ( " GRENIND " , PU_HUDGFX ) ;
railring = W_CachePatchName ( " RAILIND " , PU_HUDGFX ) ;
2018-03-26 22:53:09 +00:00
jumpshield = W_CachePatchName ( " TVWWICON " , PU_HUDGFX ) ;
forceshield = W_CachePatchName ( " TVFOICON " , PU_HUDGFX ) ;
ringshield = W_CachePatchName ( " TVATICON " , PU_HUDGFX ) ;
watershield = W_CachePatchName ( " TVELICON " , PU_HUDGFX ) ;
bombshield = W_CachePatchName ( " TVARICON " , PU_HUDGFX ) ;
pityshield = W_CachePatchName ( " TVPIICON " , PU_HUDGFX ) ;
2019-06-22 15:44:32 +00:00
pinkshield = W_CachePatchName ( " TVPPICON " , PU_HUDGFX ) ;
2018-03-26 22:53:09 +00:00
flameshield = W_CachePatchName ( " TVFLICON " , PU_HUDGFX ) ;
bubbleshield = W_CachePatchName ( " TVBBICON " , PU_HUDGFX ) ;
thundershield = W_CachePatchName ( " TVZPICON " , PU_HUDGFX ) ;
invincibility = W_CachePatchName ( " TVIVICON " , PU_HUDGFX ) ;
sneakers = W_CachePatchName ( " TVSSICON " , PU_HUDGFX ) ;
gravboots = W_CachePatchName ( " TVGVICON " , PU_HUDGFX ) ;
2014-03-15 16:59:03 +00:00
tagico = W_CachePatchName ( " TAGICO " , PU_HUDGFX ) ;
gotrflag = W_CachePatchName ( " GOTRFLAG " , PU_HUDGFX ) ;
gotbflag = W_CachePatchName ( " GOTBFLAG " , PU_HUDGFX ) ;
2020-01-20 00:55:08 +00:00
fnshico = W_CachePatchName ( " FNSHICO " , PU_HUDGFX ) ;
2014-03-15 16:59:03 +00:00
nonicon = W_CachePatchName ( " NONICON " , PU_HUDGFX ) ;
2018-03-26 22:53:09 +00:00
nonicon2 = W_CachePatchName ( " NONICON2 " , PU_HUDGFX ) ;
2014-03-15 16:59:03 +00:00
// NiGHTS HUD things
bluestat = W_CachePatchName ( " BLUESTAT " , PU_HUDGFX ) ;
byelstat = W_CachePatchName ( " BYELSTAT " , PU_HUDGFX ) ;
orngstat = W_CachePatchName ( " ORNGSTAT " , PU_HUDGFX ) ;
redstat = W_CachePatchName ( " REDSTAT " , PU_HUDGFX ) ;
yelstat = W_CachePatchName ( " YELSTAT " , PU_HUDGFX ) ;
nbracket = W_CachePatchName ( " NBRACKET " , PU_HUDGFX ) ;
2019-08-02 22:51:22 +00:00
nring = W_CachePatchName ( " NRNG1 " , PU_HUDGFX ) ;
2014-03-15 16:59:03 +00:00
for ( i = 0 ; i < 12 ; + + i )
2018-06-03 21:41:54 +00:00
{
2014-03-15 16:59:03 +00:00
nhud [ i ] = W_CachePatchName ( va ( " NHUD%d " , i + 1 ) , PU_HUDGFX ) ;
2018-06-03 21:41:54 +00:00
nbon [ i ] = W_CachePatchName ( va ( " NBON%d " , i + 1 ) , PU_HUDGFX ) ;
}
2014-03-15 16:59:03 +00:00
nsshud = W_CachePatchName ( " NSSHUD " , PU_HUDGFX ) ;
2018-06-03 21:41:54 +00:00
nssbon = W_CachePatchName ( " NSSBON " , PU_HUDGFX ) ;
2014-03-15 16:59:03 +00:00
minicaps = W_CachePatchName ( " MINICAPS " , PU_HUDGFX ) ;
for ( i = 0 ; i < 8 ; + + i )
{
narrow [ i ] = W_CachePatchName ( va ( " NARROW%d " , i + 1 ) , PU_HUDGFX ) ;
nredar [ i ] = W_CachePatchName ( va ( " NREDAR%d " , i + 1 ) , PU_HUDGFX ) ;
}
// non-animated version
narrow [ 8 ] = W_CachePatchName ( " NARROW9 " , PU_HUDGFX ) ;
drillbar = W_CachePatchName ( " DRILLBAR " , PU_HUDGFX ) ;
for ( i = 0 ; i < 3 ; + + i )
drillfill [ i ] = W_CachePatchName ( va ( " DRILLFI%d " , i + 1 ) , PU_HUDGFX ) ;
capsulebar = W_CachePatchName ( " CAPSBAR " , PU_HUDGFX ) ;
capsulefill = W_CachePatchName ( " CAPSFILL " , PU_HUDGFX ) ;
minus5sec = W_CachePatchName ( " MINUS5 " , PU_HUDGFX ) ;
for ( i = 0 ; i < 7 ; + + i )
ngradeletters [ i ] = W_CachePatchName ( va ( " GRADE%d " , i ) , PU_HUDGFX ) ;
}
// made separate so that skins code can reload custom face graphics
A good and bad ending cutscene now exist.
Also:
* SPR2_XTRA - instead of defining lumpnames in S_SKIN, those kinds of assets can just be bundled into the spriteset. Required for ending cutscene stuff, I guess, but also done for HUD life icon and character select image (aside from Sonic&Tails, still SOC'd in).
* Minor oversights in SPR2 support corrected.
* Better evaluation, featuring ending assets.
* Intro has warping-in blackrock, reusing ending assets.
* Cutscene text now supports lowercase (intro and custom).
* Disable the asset-fucking "gamma correction" I put in over two years ago when implementing colour cube. (This is the only thing I could move into another branch if you MUST, but it's basically invisble in the diff so w/e.)
* Don't blank the screen if the top left pixel of a screen-covering patch is transparent. (Checked via nonzero topdelta for first column)
Bugs:
* OPENGL ONLY: The first ~20 frames of both endings are fucked. A little help here? Might be HWR_DrawFadeFill's fault, which I just created. OR it could be in f_finale, but I doubt it, since it doesn't appear in Software.
2019-07-27 23:32:57 +00:00
void ST_LoadFaceGraphics ( INT32 skinnum )
2014-03-15 16:59:03 +00:00
{
2019-11-25 18:52:38 +00:00
if ( skins [ skinnum ] . sprites [ SPR2_XTRA ] . numframes > XTRA_LIFEPIC )
A good and bad ending cutscene now exist.
Also:
* SPR2_XTRA - instead of defining lumpnames in S_SKIN, those kinds of assets can just be bundled into the spriteset. Required for ending cutscene stuff, I guess, but also done for HUD life icon and character select image (aside from Sonic&Tails, still SOC'd in).
* Minor oversights in SPR2 support corrected.
* Better evaluation, featuring ending assets.
* Intro has warping-in blackrock, reusing ending assets.
* Cutscene text now supports lowercase (intro and custom).
* Disable the asset-fucking "gamma correction" I put in over two years ago when implementing colour cube. (This is the only thing I could move into another branch if you MUST, but it's basically invisble in the diff so w/e.)
* Don't blank the screen if the top left pixel of a screen-covering patch is transparent. (Checked via nonzero topdelta for first column)
Bugs:
* OPENGL ONLY: The first ~20 frames of both endings are fucked. A little help here? Might be HWR_DrawFadeFill's fault, which I just created. OR it could be in f_finale, but I doubt it, since it doesn't appear in Software.
2019-07-27 23:32:57 +00:00
{
spritedef_t * sprdef = & skins [ skinnum ] . sprites [ SPR2_XTRA ] ;
2019-08-19 17:23:03 +00:00
spriteframe_t * sprframe = & sprdef - > spriteframes [ XTRA_LIFEPIC ] ;
A good and bad ending cutscene now exist.
Also:
* SPR2_XTRA - instead of defining lumpnames in S_SKIN, those kinds of assets can just be bundled into the spriteset. Required for ending cutscene stuff, I guess, but also done for HUD life icon and character select image (aside from Sonic&Tails, still SOC'd in).
* Minor oversights in SPR2 support corrected.
* Better evaluation, featuring ending assets.
* Intro has warping-in blackrock, reusing ending assets.
* Cutscene text now supports lowercase (intro and custom).
* Disable the asset-fucking "gamma correction" I put in over two years ago when implementing colour cube. (This is the only thing I could move into another branch if you MUST, but it's basically invisble in the diff so w/e.)
* Don't blank the screen if the top left pixel of a screen-covering patch is transparent. (Checked via nonzero topdelta for first column)
Bugs:
* OPENGL ONLY: The first ~20 frames of both endings are fucked. A little help here? Might be HWR_DrawFadeFill's fault, which I just created. OR it could be in f_finale, but I doubt it, since it doesn't appear in Software.
2019-07-27 23:32:57 +00:00
faceprefix [ skinnum ] = W_CachePatchNum ( sprframe - > lumppat [ 0 ] , PU_HUDGFX ) ;
2019-11-25 18:52:38 +00:00
if ( skins [ skinnum ] . sprites [ ( SPR2_XTRA | FF_SPR2SUPER ) ] . numframes > XTRA_LIFEPIC )
A good and bad ending cutscene now exist.
Also:
* SPR2_XTRA - instead of defining lumpnames in S_SKIN, those kinds of assets can just be bundled into the spriteset. Required for ending cutscene stuff, I guess, but also done for HUD life icon and character select image (aside from Sonic&Tails, still SOC'd in).
* Minor oversights in SPR2 support corrected.
* Better evaluation, featuring ending assets.
* Intro has warping-in blackrock, reusing ending assets.
* Cutscene text now supports lowercase (intro and custom).
* Disable the asset-fucking "gamma correction" I put in over two years ago when implementing colour cube. (This is the only thing I could move into another branch if you MUST, but it's basically invisble in the diff so w/e.)
* Don't blank the screen if the top left pixel of a screen-covering patch is transparent. (Checked via nonzero topdelta for first column)
Bugs:
* OPENGL ONLY: The first ~20 frames of both endings are fucked. A little help here? Might be HWR_DrawFadeFill's fault, which I just created. OR it could be in f_finale, but I doubt it, since it doesn't appear in Software.
2019-07-27 23:32:57 +00:00
{
sprdef = & skins [ skinnum ] . sprites [ SPR2_XTRA | FF_SPR2SUPER ] ;
sprframe = & sprdef - > spriteframes [ 0 ] ;
superprefix [ skinnum ] = W_CachePatchNum ( sprframe - > lumppat [ 0 ] , PU_HUDGFX ) ;
}
else
superprefix [ skinnum ] = faceprefix [ skinnum ] ; // not manually freed, okay to set to same pointer
}
else
faceprefix [ skinnum ] = superprefix [ skinnum ] = W_CachePatchName ( " MISSING " , PU_HUDGFX ) ; // ditto
2014-03-15 16:59:03 +00:00
facefreed [ skinnum ] = false ;
}
void ST_ReloadSkinFaceGraphics ( void )
{
INT32 i ;
for ( i = 0 ; i < numskins ; i + + )
A good and bad ending cutscene now exist.
Also:
* SPR2_XTRA - instead of defining lumpnames in S_SKIN, those kinds of assets can just be bundled into the spriteset. Required for ending cutscene stuff, I guess, but also done for HUD life icon and character select image (aside from Sonic&Tails, still SOC'd in).
* Minor oversights in SPR2 support corrected.
* Better evaluation, featuring ending assets.
* Intro has warping-in blackrock, reusing ending assets.
* Cutscene text now supports lowercase (intro and custom).
* Disable the asset-fucking "gamma correction" I put in over two years ago when implementing colour cube. (This is the only thing I could move into another branch if you MUST, but it's basically invisble in the diff so w/e.)
* Don't blank the screen if the top left pixel of a screen-covering patch is transparent. (Checked via nonzero topdelta for first column)
Bugs:
* OPENGL ONLY: The first ~20 frames of both endings are fucked. A little help here? Might be HWR_DrawFadeFill's fault, which I just created. OR it could be in f_finale, but I doubt it, since it doesn't appear in Software.
2019-07-27 23:32:57 +00:00
ST_LoadFaceGraphics ( i ) ;
2014-03-15 16:59:03 +00:00
}
static inline void ST_InitData ( void )
{
// 'link' the statusbar display to a player, which could be
// another player than consoleplayer, for example, when you
// change the view in a multiplayer demo with F12.
stplyr = & players [ displayplayer ] ;
st_palette = - 1 ;
}
static inline void ST_Stop ( void )
{
if ( st_stopped )
return ;
V_SetPalette ( 0 ) ;
st_stopped = true ;
}
void ST_Start ( void )
{
if ( ! st_stopped )
ST_Stop ( ) ;
ST_InitData ( ) ;
st_stopped = false ;
}
//
// Initializes the status bar, sets the defaults border patch for the window borders.
//
// used by OpenGL mode, holds lumpnum of flat used to fill space around the viewwindow
lumpnum_t st_borderpatchnum ;
void ST_Init ( void )
{
INT32 i ;
for ( i = 0 ; i < MAXPLAYERS ; i + + )
facefreed [ i ] = true ;
if ( dedicated )
return ;
ST_LoadGraphics ( ) ;
}
// change the status bar too, when pressing F12 while viewing a demo.
void ST_changeDemoView ( void )
{
// the same routine is called at multiplayer deathmatch spawn
// so it can be called multiple times
ST_Start ( ) ;
}
// =========================================================================
// STATUS BAR OVERLAY
// =========================================================================
boolean st_overlay ;
2014-03-21 18:42:55 +00:00
// =========================================================================
// INTERNAL DRAWING
// =========================================================================
2018-01-20 22:14:24 +00:00
# define ST_DrawTopLeftOverlayPatch(x,y,p) V_DrawScaledPatch(x, y, V_PERPLAYER|V_SNAPTOTOP|V_SNAPTOLEFT|V_HUDTRANS, p)
2018-03-19 23:08:51 +00:00
# define ST_DrawNumFromHud(h,n,flags) V_DrawTallNum(hudinfo[h].x, hudinfo[h].y, hudinfo[h].f|V_PERPLAYER|flags, n)
# define ST_DrawPadNumFromHud(h,n,q,flags) V_DrawPaddedTallNum(hudinfo[h].x, hudinfo[h].y, hudinfo[h].f|V_PERPLAYER|flags, n, q)
# define ST_DrawPatchFromHud(h,p,flags) V_DrawScaledPatch(hudinfo[h].x, hudinfo[h].y, hudinfo[h].f|V_PERPLAYER|flags, p)
2014-03-21 18:42:55 +00:00
// Draw a number, scaled, over the view, maybe with set translucency
2014-03-15 16:59:03 +00:00
// Always draw the number completely since it's overlay
//
// Supports different colors! woo!
2017-10-31 15:47:58 +00:00
static void ST_DrawNightsOverlayNum ( fixed_t x /* right border */ , fixed_t y , fixed_t s , INT32 a ,
2020-02-15 08:18:41 +00:00
UINT32 num , patch_t * * numpat , skincolornum_t colornum )
2014-03-15 16:59:03 +00:00
{
2020-11-22 23:02:47 +00:00
fixed_t w = numpat [ 0 ] - > width * s ;
2014-03-15 16:59:03 +00:00
const UINT8 * colormap ;
2014-11-12 00:55:07 +00:00
// I want my V_SNAPTOx flags. :< -Red
//a &= V_ALPHAMASK;
2014-03-15 16:59:03 +00:00
if ( colornum = = 0 )
colormap = colormaps ;
else // Uses the player colors.
colormap = R_GetTranslationColormap ( TC_DEFAULT , colornum , GTC_CACHE ) ;
2017-10-31 15:47:58 +00:00
//I_Assert(num >= 0); // this function does not draw negative numbers
2014-03-15 16:59:03 +00:00
// draw the number
do
{
2014-11-12 00:55:07 +00:00
x - = w ;
2017-10-31 15:47:58 +00:00
V_DrawFixedPatch ( x , y , s , a , numpat [ num % 10 ] , colormap ) ;
2014-03-15 16:59:03 +00:00
num / = 10 ;
} while ( num ) ;
// Sorry chum, this function only draws UNSIGNED values!
2017-10-31 15:47:58 +00:00
// then why is num not UINT32? ~toast
2014-03-15 16:59:03 +00:00
}
// Devmode information
static void ST_drawDebugInfo ( void )
{
2018-04-08 13:31:56 +00:00
INT32 height = 0 , h = 8 , w = 18 , lowh ;
void ( * textfunc ) ( INT32 , INT32 , INT32 , const char * ) ;
2014-03-15 16:59:03 +00:00
2016-09-30 18:40:30 +00:00
if ( ! ( stplyr - > mo & & cv_debug ) )
2014-03-15 16:59:03 +00:00
return ;
2018-04-06 17:40:08 +00:00
# define VFLAGS V_MONOSPACE|V_SNAPTOTOP|V_SNAPTORIGHT
2018-03-19 16:39:37 +00:00
2018-04-08 12:03:40 +00:00
if ( ( moviemode = = MM_GIF & & cv_gif_downscale . value ) | | vid . dupx = = 1 )
2014-03-15 16:59:03 +00:00
{
2018-04-08 13:31:56 +00:00
textfunc = V_DrawRightAlignedString ;
lowh = ( ( vid . height / vid . dupy ) - 16 ) ;
2014-03-15 16:59:03 +00:00
}
2018-04-06 17:40:08 +00:00
else
2014-03-15 16:59:03 +00:00
{
2018-04-08 13:31:56 +00:00
textfunc = V_DrawRightAlignedSmallString ;
h / = 2 ;
w / = 2 ;
lowh = 0 ;
}
2016-03-26 22:48:12 +00:00
2018-04-08 13:31:56 +00:00
# define V_DrawDebugLine(str) if (lowh && (height > lowh))\
{ \
V_DrawRightAlignedThinString ( 320 , 8 + lowh , VFLAGS | V_REDMAP , " SOME INFO NOT VISIBLE " ) ; \
return ; \
} \
textfunc ( 320 , height , VFLAGS , str ) ; \
height + = h ;
2014-03-15 16:59:03 +00:00
2018-04-08 13:31:56 +00:00
# define V_DrawDebugFlag(f, str) textfunc(width, height, VFLAGS|f, str);\
width - = w
2014-03-15 16:59:03 +00:00
2018-04-08 13:31:56 +00:00
if ( cv_debug & DBG_MEMORY )
{
V_DrawDebugLine ( va ( " Heap: %8sKB " , sizeu1 ( Z_TotalUsage ( ) > > 10 ) ) ) ;
2018-04-06 17:40:08 +00:00
2018-04-08 13:31:56 +00:00
height + = h / 2 ;
}
2018-04-06 17:40:08 +00:00
2018-04-08 13:31:56 +00:00
if ( cv_debug & DBG_RANDOMIZER ) // randomizer testing
{
fixed_t peekres = P_RandomPeek ( ) ;
peekres * = 10000 ; // Change from fixed point
peekres > > = FRACBITS ; // to displayable decimal
2018-04-06 17:40:08 +00:00
2018-04-08 13:31:56 +00:00
V_DrawDebugLine ( va ( " Init: %08x " , P_GetInitSeed ( ) ) ) ;
V_DrawDebugLine ( va ( " Seed: %08x " , P_GetRandSeed ( ) ) ) ;
V_DrawDebugLine ( va ( " == : .%04d " , peekres ) ) ;
2018-04-06 17:40:08 +00:00
2018-04-08 13:31:56 +00:00
height + = h / 2 ;
}
2018-04-06 17:40:08 +00:00
2018-04-08 13:31:56 +00:00
if ( cv_debug & DBG_PLAYER )
{
INT32 width = 320 ;
const fixed_t d = AngleFixed ( stplyr - > drawangle ) ;
V_DrawDebugLine ( va ( " SHIELD: %5x " , stplyr - > powers [ pw_shield ] ) ) ;
V_DrawDebugLine ( va ( " SCALE: %5d%% " , ( stplyr - > mo - > scale * 100 ) > > FRACBITS ) ) ;
V_DrawDebugLine ( va ( " CARRY: %5x " , stplyr - > powers [ pw_carry ] ) ) ;
V_DrawDebugLine ( va ( " AIR: %4d, %3d " , stplyr - > powers [ pw_underwater ] , stplyr - > powers [ pw_spacetime ] ) ) ;
V_DrawDebugLine ( va ( " ABILITY: %3d, %3d " , stplyr - > charability , stplyr - > charability2 ) ) ;
V_DrawDebugLine ( va ( " ACTIONSPD: %5d " , stplyr - > actionspd > > FRACBITS ) ) ;
V_DrawDebugLine ( va ( " PEEL: %3d " , stplyr - > dashmode ) ) ;
V_DrawDebugLine ( va ( " SCOREADD: %3d " , stplyr - > scoreadd ) ) ;
// Flags
V_DrawDebugFlag ( ( ( stplyr - > pflags & PF_SHIELDABILITY ) ? V_GREENMAP : V_REDMAP ) , " SH " ) ;
V_DrawDebugFlag ( ( ( stplyr - > pflags & PF_THOKKED ) ? V_GREENMAP : V_REDMAP ) , " TH " ) ;
V_DrawDebugFlag ( ( ( stplyr - > pflags & PF_STARTDASH ) ? V_GREENMAP : V_REDMAP ) , " ST " ) ;
V_DrawDebugFlag ( ( ( stplyr - > pflags & PF_SPINNING ) ? V_GREENMAP : V_REDMAP ) , " SP " ) ;
V_DrawDebugFlag ( ( ( stplyr - > pflags & PF_NOJUMPDAMAGE ) ? V_GREENMAP : V_REDMAP ) , " ND " ) ;
V_DrawDebugFlag ( ( ( stplyr - > pflags & PF_JUMPED ) ? V_GREENMAP : V_REDMAP ) , " JD " ) ;
V_DrawDebugFlag ( ( ( stplyr - > pflags & PF_STARTJUMP ) ? V_GREENMAP : V_REDMAP ) , " SJ " ) ;
V_DrawDebugFlag ( 0 , " PF/SF: " ) ;
height + = h ;
width = 320 ;
V_DrawDebugFlag ( ( ( stplyr - > pflags & PF_INVIS ) ? V_GREENMAP : V_REDMAP ) , " *I " ) ;
V_DrawDebugFlag ( ( ( stplyr - > pflags & PF_NOCLIP ) ? V_GREENMAP : V_REDMAP ) , " *C " ) ;
V_DrawDebugFlag ( ( ( stplyr - > pflags & PF_GODMODE ) ? V_GREENMAP : V_REDMAP ) , " *G " ) ;
V_DrawDebugFlag ( ( ( stplyr - > charflags & SF_SUPER ) ? V_GREENMAP : V_REDMAP ) , " SU " ) ;
V_DrawDebugFlag ( ( ( stplyr - > pflags & PF_APPLYAUTOBRAKE ) ? V_GREENMAP : V_REDMAP ) , " AA " ) ;
V_DrawDebugFlag ( ( ( stplyr - > pflags & PF_SLIDING ) ? V_GREENMAP : V_REDMAP ) , " SL " ) ;
V_DrawDebugFlag ( ( ( stplyr - > pflags & PF_BOUNCING ) ? V_GREENMAP : V_REDMAP ) , " BO " ) ;
V_DrawDebugFlag ( ( ( stplyr - > pflags & PF_GLIDING ) ? V_GREENMAP : V_REDMAP ) , " GL " ) ;
height + = h ;
V_DrawDebugLine ( va ( " DRAWANGLE: %6d " , FixedInt ( d ) ) ) ;
height + = h / 2 ;
}
if ( cv_debug & DBG_DETAILED )
{
INT32 width = 320 ;
V_DrawDebugLine ( va ( " CEILINGZ: %6d " , stplyr - > mo - > ceilingz > > FRACBITS ) ) ;
V_DrawDebugLine ( va ( " FLOORZ: %6d " , stplyr - > mo - > floorz > > FRACBITS ) ) ;
V_DrawDebugLine ( va ( " CMOMX: %6d " , stplyr - > cmomx > > FRACBITS ) ) ;
V_DrawDebugLine ( va ( " CMOMY: %6d " , stplyr - > cmomy > > FRACBITS ) ) ;
V_DrawDebugLine ( va ( " PMOMZ: %6d " , stplyr - > mo - > pmomz > > FRACBITS ) ) ;
width = 320 ;
V_DrawDebugFlag ( ( ( stplyr - > mo - > eflags & MFE_APPLYPMOMZ ) ? V_GREENMAP : V_REDMAP ) , " AP " ) ;
V_DrawDebugFlag ( ( ( stplyr - > mo - > eflags & MFE_SPRUNG ) ? V_GREENMAP : V_REDMAP ) , " SP " ) ;
//V_DrawDebugFlag(((stplyr->mo->eflags & MFE_PUSHED) ? V_GREENMAP : V_REDMAP), "PU"); -- not relevant to players
V_DrawDebugFlag ( ( ( stplyr - > mo - > eflags & MFE_GOOWATER ) ? V_GREENMAP : V_REDMAP ) , " GW " ) ;
V_DrawDebugFlag ( ( ( stplyr - > mo - > eflags & MFE_VERTICALFLIP ) ? V_GREENMAP : V_REDMAP ) , " VF " ) ;
V_DrawDebugFlag ( ( ( stplyr - > mo - > eflags & MFE_JUSTSTEPPEDDOWN ) ? V_GREENMAP : V_REDMAP ) , " JS " ) ;
V_DrawDebugFlag ( ( ( stplyr - > mo - > eflags & MFE_UNDERWATER ) ? V_GREENMAP : V_REDMAP ) , " UW " ) ;
V_DrawDebugFlag ( ( ( stplyr - > mo - > eflags & MFE_TOUCHWATER ) ? V_GREENMAP : V_REDMAP ) , " TW " ) ;
V_DrawDebugFlag ( ( ( stplyr - > mo - > eflags & MFE_JUSTHITFLOOR ) ? V_GREENMAP : V_REDMAP ) , " JH " ) ;
V_DrawDebugFlag ( ( ( stplyr - > mo - > eflags & MFE_ONGROUND ) ? V_GREENMAP : V_REDMAP ) , " OG " ) ;
V_DrawDebugFlag ( 0 , " MFE: " ) ;
height + = h ;
V_DrawDebugLine ( va ( " MOMX: %6d " , stplyr - > rmomx > > FRACBITS ) ) ;
V_DrawDebugLine ( va ( " MOMY: %6d " , stplyr - > rmomy > > FRACBITS ) ) ;
V_DrawDebugLine ( va ( " MOMZ: %6d " , stplyr - > mo - > momz > > FRACBITS ) ) ;
V_DrawDebugLine ( va ( " SPEED: %6d " , stplyr - > speed > > FRACBITS ) ) ;
height + = h / 2 ;
2014-03-15 16:59:03 +00:00
}
2018-03-19 16:39:37 +00:00
2018-04-08 13:31:56 +00:00
if ( cv_debug & DBG_BASIC )
{
const fixed_t d = AngleFixed ( stplyr - > mo - > angle ) ;
V_DrawDebugLine ( va ( " X: %6d " , stplyr - > mo - > x > > FRACBITS ) ) ;
V_DrawDebugLine ( va ( " Y: %6d " , stplyr - > mo - > y > > FRACBITS ) ) ;
V_DrawDebugLine ( va ( " Z: %6d " , stplyr - > mo - > z > > FRACBITS ) ) ;
V_DrawDebugLine ( va ( " A: %6d " , FixedInt ( d ) ) ) ;
//height += h/2;
}
# undef V_DrawDebugFlag
2018-04-06 17:40:08 +00:00
# undef V_DrawDebugLine
# undef VFLAGS
2014-03-15 16:59:03 +00:00
}
static void ST_drawScore ( void )
{
2018-11-10 06:00:18 +00:00
if ( F_GetPromptHideHud ( hudinfo [ HUD_SCORE ] . y ) )
return ;
2014-03-15 16:59:03 +00:00
// SCORE:
2017-07-03 15:33:52 +00:00
ST_DrawPatchFromHud ( HUD_SCORE , sboscore , V_HUDTRANS ) ;
2014-03-15 16:59:03 +00:00
if ( objectplacing )
{
if ( op_displayflags > UINT16_MAX )
2018-01-20 22:14:24 +00:00
ST_DrawTopLeftOverlayPatch ( ( hudinfo [ HUD_SCORENUM ] . x - tallminus - > width ) , hudinfo [ HUD_SCORENUM ] . y , tallminus ) ;
2014-03-15 16:59:03 +00:00
else
2017-07-03 15:33:52 +00:00
ST_DrawNumFromHud ( HUD_SCORENUM , op_displayflags , V_HUDTRANS ) ;
2014-03-15 16:59:03 +00:00
}
else
2018-03-21 18:18:45 +00:00
ST_DrawNumFromHud ( HUD_SCORENUM , stplyr - > score , V_HUDTRANS ) ;
}
static void ST_drawRaceNum ( INT32 time )
{
INT32 height , bounce ;
patch_t * racenum ;
time + = TICRATE ;
height = ( ( 3 * BASEVIDHEIGHT ) > > 2 ) - 8 ;
bounce = TICRATE - ( 1 + ( time % TICRATE ) ) ;
switch ( time / TICRATE )
{
case 3 :
racenum = race3 ;
break ;
case 2 :
racenum = race2 ;
break ;
case 1 :
racenum = race1 ;
break ;
default :
racenum = racego ;
break ;
}
if ( bounce < 3 )
{
height - = ( 2 - bounce ) ;
if ( ! ( P_AutoPause ( ) | | paused ) & & ! bounce )
S_StartSound ( 0 , ( ( racenum = = racego ) ? sfx_s3kad : sfx_s3ka7 ) ) ;
}
2020-11-22 23:02:47 +00:00
V_DrawScaledPatch ( ( ( BASEVIDWIDTH - racenum - > width ) / 2 ) , height , V_PERPLAYER , racenum ) ;
2014-03-15 16:59:03 +00:00
}
static void ST_drawTime ( void )
{
INT32 seconds , minutes , tictrn , tics ;
2018-03-21 18:18:45 +00:00
boolean downwards = false ;
2014-03-15 16:59:03 +00:00
if ( objectplacing )
{
tics = objectsdrawn ;
seconds = objectsdrawn % 100 ;
minutes = objectsdrawn / 100 ;
tictrn = 0 ;
}
else
{
2018-03-21 18:18:45 +00:00
// Counting down the hidetime?
2019-12-28 22:47:03 +00:00
if ( ( gametyperules & GTR_STARTCOUNTDOWN ) & & ( stplyr - > realtime < = ( hidetime * TICRATE ) ) )
2018-03-21 18:18:45 +00:00
{
2019-11-29 20:41:13 +00:00
tics = ( hidetime * TICRATE - stplyr - > realtime ) ;
if ( tics < 3 * TICRATE )
ST_drawRaceNum ( tics ) ;
tics + = ( TICRATE - 1 ) ; // match the race num
2018-03-21 18:18:45 +00:00
downwards = true ;
}
else
{
// Hidetime finish!
2019-12-28 22:47:03 +00:00
if ( ( gametyperules & GTR_STARTCOUNTDOWN ) & & ( stplyr - > realtime < ( ( hidetime + 1 ) * TICRATE ) ) )
2018-06-06 14:13:09 +00:00
ST_drawRaceNum ( hidetime * TICRATE - stplyr - > realtime ) ;
2018-03-21 18:18:45 +00:00
// Time limit?
2019-12-18 04:54:45 +00:00
if ( ( gametyperules & GTR_TIMELIMIT ) & & cv_timelimit . value & & timelimitintics > 0 )
2018-03-21 18:18:45 +00:00
{
2019-11-29 20:41:13 +00:00
if ( timelimitintics > stplyr - > realtime )
2018-03-21 18:18:45 +00:00
{
2019-11-29 20:41:13 +00:00
tics = ( timelimitintics - stplyr - > realtime ) ;
if ( tics < 3 * TICRATE )
ST_drawRaceNum ( tics ) ;
tics + = ( TICRATE - 1 ) ; // match the race num
2018-03-21 18:18:45 +00:00
}
else // Overtime!
tics = 0 ;
downwards = true ;
}
// Post-hidetime normal.
2019-12-28 22:47:03 +00:00
else if ( gametyperules & GTR_STARTCOUNTDOWN )
2018-03-21 18:18:45 +00:00
tics = stplyr - > realtime - hidetime * TICRATE ;
// "Shadow! What are you doing? Hurry and get back here
// right now before the island blows up with you on it!"
// "Blows up??" *awkward silence* "I've got to get outta
// here and find Amy and Tails right away!"
else if ( mapheaderinfo [ gamemap - 1 ] - > countdown )
{
tics = countdowntimer ;
downwards = true ;
}
// Normal.
else
tics = stplyr - > realtime ;
}
2014-03-21 18:42:55 +00:00
minutes = G_TicsToMinutes ( tics , true ) ;
2018-03-21 18:18:45 +00:00
seconds = G_TicsToSeconds ( tics ) ;
2014-03-15 16:59:03 +00:00
tictrn = G_TicsToCentiseconds ( tics ) ;
}
2018-11-10 06:00:18 +00:00
if ( F_GetPromptHideHud ( hudinfo [ HUD_TIME ] . y ) )
return ;
2019-11-08 15:47:12 +00:00
downwards = ( downwards & & ( tics < 30 * TICRATE ) & & ( leveltime / 5 & 1 ) & & ! stoppedclock ) ; // overtime?
2018-03-21 18:18:45 +00:00
// TIME:
2019-11-08 15:47:12 +00:00
ST_DrawPatchFromHud ( HUD_TIME , ( downwards ? sboredtime : sbotime ) , V_HUDTRANS ) ;
2018-03-21 18:18:45 +00:00
2019-11-08 15:47:12 +00:00
if ( downwards ) // overtime!
2018-03-21 18:18:45 +00:00
return ;
2018-03-26 22:53:09 +00:00
if ( cv_timetic . value = = 3 ) // Tics only -- how simple is this?
2018-03-19 23:08:51 +00:00
ST_DrawNumFromHud ( HUD_SECONDS , tics , V_HUDTRANS ) ;
2014-03-15 16:59:03 +00:00
else
{
2018-03-19 23:08:51 +00:00
ST_DrawNumFromHud ( HUD_MINUTES , minutes , V_HUDTRANS ) ; // Minutes
ST_DrawPatchFromHud ( HUD_TIMECOLON , sbocolon , V_HUDTRANS ) ; // Colon
ST_DrawPadNumFromHud ( HUD_SECONDS , seconds , 2 , V_HUDTRANS ) ; // Seconds
2014-03-15 16:59:03 +00:00
Introducing Marathon Run. (I was going to call it Marathon Mode, but NiGHTS Mode being right next to it on the menu looked terrible.)
Basically a dedicated Record Attack-like experience for speedrunning the game as a continuous chunk rather than ILs. Has several quality of life features.
Benefits include:
* An unambiguous real-time bar across the bottom of the screen, always displaying the current time, ticking up until you reach the ending.
* Disable the console (pausing is still allowed, but the timer will still increment).
* Automatically skip intermissions as if you're holding down the spin button.
* Show centiseconds on HUD automatically, like record attack.
* "Live Event Backups" - a category of run fit for major events like GDQ, where recovery from crashes or chokes makes for better entertainment. Essentially a modified SP savefile, down to using the same basic functions, but has its own filename and tweaked internal layout.
* "spmarathon_start" MainCfg block parameter and "marathonnext" mapheader parameter, allowing for a customised flow (makes this fit for purpose for an eventual SUGOI port).
* Disabling inter-level custom cutscenes by default with a menu option to toggle this (won't show up if the mod doesn't *have* any custom cutscenes), although either way ending cutscenes (vanilla or custom) remain intact since is time is called before them.
* Won't show up if you have a mod that consists of only one level (determined by spmarathon_start's nextlevel; this won't trip if you manually set its marathonnext).
* Unconditional gratitude on the evaluation screen, instead of a negging "Try again..." if you didn't get all the emeralds (which you may not have been aiming for).
* Gorgeous new menu (no new assets required, unless you wanna give it a header later).
Changes which were required for the above but affect other areas of the game include:
* "useBlackRock" MainCFG block parameter, which can be used to disable the presence of the Black Rock or Egg Rock in both the Evaluation screen and the Marathon Run menu (for total conversions with different stories).
* Disabling Continues in NiGHTS mode, to match the most common singleplayer experience post 2.2.4's release (is reverted if useContinues is set to true).
* Hiding the exitmove "powerup" outside of multiplayer. (Okay, this isn't really related, I just saw this bug in action a lot while doing test runs and got annoyed enough to fix it here.)
* The ability to use V_DrawPromptBack (in hardcode only at the moment, but) to draw in terms of pixels rather than rows of text, by providing negative instead of positive inputs).
* A refactoring of redundant game saves smattered across the ending, credits, and evaluation - in addition to saving the game slightly earlier.
* Minor m_menu.c touchups and refactorings here and there.
Built using feedback from the official server's #speedruns channel, among other places.
2020-05-14 22:10:00 +00:00
if ( cv_timetic . value = = 1 | | cv_timetic . value = = 2 | | modeattacking | | marathonmode )
2014-03-15 16:59:03 +00:00
{
2017-07-03 15:33:52 +00:00
ST_DrawPatchFromHud ( HUD_TIMETICCOLON , sboperiod , V_HUDTRANS ) ; // Period
ST_DrawPadNumFromHud ( HUD_TICS , tictrn , 2 , V_HUDTRANS ) ; // Tics
2014-03-15 16:59:03 +00:00
}
}
}
static inline void ST_drawRings ( void )
{
2017-09-25 19:35:04 +00:00
INT32 ringnum ;
2014-03-15 16:59:03 +00:00
2018-11-10 06:00:18 +00:00
if ( F_GetPromptHideHud ( hudinfo [ HUD_RINGS ] . y ) )
return ;
2018-03-19 23:08:51 +00:00
ST_DrawPatchFromHud ( HUD_RINGS , ( ( ! stplyr - > spectator & & stplyr - > rings < = 0 & & leveltime / 5 & 1 ) ? sboredrings : sborings ) , ( ( stplyr - > spectator ) ? V_HUDTRANSHALF : V_HUDTRANS ) ) ;
2014-03-15 16:59:03 +00:00
2019-09-01 14:55:23 +00:00
if ( objectplacing )
ringnum = op_currentdoomednum ;
else if ( stplyr - > rings < 0 | | stplyr - > spectator | | stplyr - > playerstate = = PST_REBORN )
ringnum = 0 ;
else
ringnum = stplyr - > rings ;
2014-03-21 18:42:55 +00:00
2018-03-26 22:53:09 +00:00
if ( cv_timetic . value = = 2 ) // Yes, even in modeattacking
2018-01-20 22:14:24 +00:00
ST_DrawNumFromHud ( HUD_RINGSNUMTICS , ringnum , V_PERPLAYER | ( ( stplyr - > spectator ) ? V_HUDTRANSHALF : V_HUDTRANS ) ) ;
2017-09-25 19:35:04 +00:00
else
2018-03-19 23:08:51 +00:00
ST_DrawNumFromHud ( HUD_RINGSNUM , ringnum , V_PERPLAYER | ( ( stplyr - > spectator ) ? V_HUDTRANSHALF : V_HUDTRANS ) ) ;
2014-03-15 16:59:03 +00:00
}
2018-01-20 22:14:24 +00:00
static void ST_drawLivesArea ( void )
2014-03-15 16:59:03 +00:00
{
2018-01-20 22:14:24 +00:00
INT32 v_colmap = V_YELLOWMAP , livescount ;
boolean notgreyedout ;
2014-03-15 16:59:03 +00:00
if ( ! stplyr - > skincolor )
return ; // Just joined a server, skin isn't loaded yet!
2018-11-10 06:00:18 +00:00
if ( F_GetPromptHideHud ( hudinfo [ HUD_LIVES ] . y ) )
return ;
2014-03-15 16:59:03 +00:00
// face background
2018-01-20 22:14:24 +00:00
V_DrawSmallScaledPatch ( hudinfo [ HUD_LIVES ] . x , hudinfo [ HUD_LIVES ] . y ,
2018-03-19 23:08:51 +00:00
hudinfo [ HUD_LIVES ] . f | V_PERPLAYER | V_HUDTRANS , livesback ) ;
2014-03-15 16:59:03 +00:00
// face
2017-12-09 23:08:12 +00:00
if ( stplyr - > spectator )
{
// spectator face
UINT8 * colormap = R_GetTranslationColormap ( stplyr - > skin , SKINCOLOR_CLOUDY , GTC_CACHE ) ;
2018-01-20 22:14:24 +00:00
V_DrawSmallMappedPatch ( hudinfo [ HUD_LIVES ] . x , hudinfo [ HUD_LIVES ] . y ,
2018-03-19 23:08:51 +00:00
hudinfo [ HUD_LIVES ] . f | V_PERPLAYER | V_HUDTRANSHALF , faceprefix [ stplyr - > skin ] , colormap ) ;
2017-12-09 23:08:12 +00:00
}
else if ( stplyr - > mo & & stplyr - > mo - > color )
2014-03-15 16:59:03 +00:00
{
// skincolor face/super
UINT8 * colormap = R_GetTranslationColormap ( stplyr - > skin , stplyr - > mo - > color , GTC_CACHE ) ;
patch_t * face = faceprefix [ stplyr - > skin ] ;
2020-06-16 20:53:42 +00:00
if ( stplyr - > powers [ pw_super ] & & ! ( stplyr - > charflags & SF_NOSUPERSPRITES ) )
2014-03-15 16:59:03 +00:00
face = superprefix [ stplyr - > skin ] ;
2018-01-20 22:14:24 +00:00
V_DrawSmallMappedPatch ( hudinfo [ HUD_LIVES ] . x , hudinfo [ HUD_LIVES ] . y ,
2018-03-19 23:08:51 +00:00
hudinfo [ HUD_LIVES ] . f | V_PERPLAYER | V_HUDTRANS , face , colormap ) ;
2019-11-18 14:39:54 +00:00
if ( st_translucency = = 10 & & stplyr - > powers [ pw_super ] = = 1 & & stplyr - > mo - > tracer )
2017-10-04 16:45:03 +00:00
{
INT32 v_supertrans = ( stplyr - > mo - > tracer - > frame & FF_TRANSMASK ) > > FF_TRANSSHIFT ;
if ( v_supertrans < 10 )
{
v_supertrans < < = V_ALPHASHIFT ;
colormap = R_GetTranslationColormap ( stplyr - > skin , stplyr - > mo - > tracer - > color , GTC_CACHE ) ;
2018-01-20 22:14:24 +00:00
V_DrawSmallMappedPatch ( hudinfo [ HUD_LIVES ] . x , hudinfo [ HUD_LIVES ] . y ,
2018-03-19 23:08:51 +00:00
hudinfo [ HUD_LIVES ] . f | V_PERPLAYER | v_supertrans , face , colormap ) ;
2017-10-04 16:45:03 +00:00
}
}
2014-03-15 16:59:03 +00:00
}
else if ( stplyr - > skincolor )
{
// skincolor face
UINT8 * colormap = R_GetTranslationColormap ( stplyr - > skin , stplyr - > skincolor , GTC_CACHE ) ;
2018-01-20 22:14:24 +00:00
V_DrawSmallMappedPatch ( hudinfo [ HUD_LIVES ] . x , hudinfo [ HUD_LIVES ] . y ,
2018-03-19 23:08:51 +00:00
hudinfo [ HUD_LIVES ] . f | V_PERPLAYER | V_HUDTRANS , faceprefix [ stplyr - > skin ] , colormap ) ;
2014-03-15 16:59:03 +00:00
}
2017-05-28 20:55:41 +00:00
2019-12-27 19:58:55 +00:00
// Metal Sonic recording
2019-11-02 11:56:51 +00:00
if ( metalrecording )
{
if ( ( ( 2 * leveltime ) / TICRATE ) & 1 )
V_DrawRightAlignedString ( hudinfo [ HUD_LIVES ] . x + 58 , hudinfo [ HUD_LIVES ] . y + 8 ,
hudinfo [ HUD_LIVES ] . f | V_PERPLAYER | V_REDMAP | V_HUDTRANS , " REC " ) ;
}
2018-01-20 22:14:24 +00:00
// Spectator
else if ( stplyr - > spectator )
v_colmap = V_GRAYMAP ;
// Tag
2019-12-18 04:54:45 +00:00
else if ( gametyperules & GTR_TAG )
2018-01-20 22:14:24 +00:00
{
if ( stplyr - > pflags & PF_TAGIT )
{
2018-03-30 15:39:59 +00:00
V_DrawRightAlignedString ( hudinfo [ HUD_LIVES ] . x + 58 , hudinfo [ HUD_LIVES ] . y + 8 , V_HUDTRANS | hudinfo [ HUD_LIVES ] . f | V_PERPLAYER , " IT! " ) ;
2018-01-20 22:14:24 +00:00
v_colmap = V_ORANGEMAP ;
}
2017-05-28 20:55:41 +00:00
}
2018-01-20 22:14:24 +00:00
// Team name
else if ( G_GametypeHasTeams ( ) )
{
if ( stplyr - > ctfteam = = 1 )
{
2018-03-30 15:39:59 +00:00
V_DrawRightAlignedString ( hudinfo [ HUD_LIVES ] . x + 58 , hudinfo [ HUD_LIVES ] . y + 8 , V_HUDTRANS | hudinfo [ HUD_LIVES ] . f | V_PERPLAYER , " RED " ) ;
2018-01-20 22:14:24 +00:00
v_colmap = V_REDMAP ;
}
else if ( stplyr - > ctfteam = = 2 )
{
2018-03-30 15:39:59 +00:00
V_DrawRightAlignedString ( hudinfo [ HUD_LIVES ] . x + 58 , hudinfo [ HUD_LIVES ] . y + 8 , V_HUDTRANS | hudinfo [ HUD_LIVES ] . f | V_PERPLAYER , " BLUE " ) ;
2018-01-20 22:14:24 +00:00
v_colmap = V_BLUEMAP ;
}
}
2019-12-27 19:58:55 +00:00
// Lives number
else
{
boolean candrawlives = true ;
// Co-op and Competition, normal life counter
if ( G_GametypeUsesLives ( ) )
{
// Handle cooplives here
2019-12-27 20:08:20 +00:00
if ( ( netgame | | multiplayer ) & & G_GametypeUsesCoopLives ( ) & & cv_cooplives . value = = 3 )
2019-12-27 19:58:55 +00:00
{
INT32 i ;
livescount = 0 ;
notgreyedout = ( stplyr - > lives > 0 ) ;
for ( i = 0 ; i < MAXPLAYERS ; i + + )
{
if ( ! playeringame [ i ] )
continue ;
if ( players [ i ] . lives < 1 )
continue ;
if ( players [ i ] . lives > 1 )
notgreyedout = true ;
if ( players [ i ] . lives = = INFLIVES )
{
livescount = INFLIVES ;
break ;
}
else if ( livescount < 99 )
livescount + = ( players [ i ] . lives ) ;
}
}
else
{
2019-12-27 20:08:20 +00:00
livescount = ( ( ( netgame | | multiplayer ) & & G_GametypeUsesCoopLives ( ) & & cv_cooplives . value = = 0 ) ? INFLIVES : stplyr - > lives ) ;
2019-12-27 19:58:55 +00:00
notgreyedout = true ;
}
}
// Infinity symbol (Race)
else if ( G_PlatformGametype ( ) & & ! ( gametyperules & GTR_LIVES ) )
{
livescount = INFLIVES ;
notgreyedout = true ;
}
// Otherwise nothing, sorry.
// Special Stages keep not showing lives,
// as G_GametypeUsesLives() returns false in
// Special Stages, and the infinity symbol
// cannot show up because Special Stages
// still have the GTR_LIVES gametype rule
// by default.
else
candrawlives = false ;
// Draw the lives counter here.
if ( candrawlives )
{
// x
V_DrawScaledPatch ( hudinfo [ HUD_LIVES ] . x + 22 , hudinfo [ HUD_LIVES ] . y + 10 , hudinfo [ HUD_LIVES ] . f | V_PERPLAYER | V_HUDTRANS , stlivex ) ;
if ( livescount = = INFLIVES )
V_DrawCharacter ( hudinfo [ HUD_LIVES ] . x + 50 , hudinfo [ HUD_LIVES ] . y + 8 ,
' \x16 ' | 0x80 | hudinfo [ HUD_LIVES ] . f | V_PERPLAYER | V_HUDTRANS , false ) ;
else
{
2020-01-19 17:30:05 +00:00
if ( stplyr - > playerstate = = PST_DEAD & & ! ( stplyr - > spectator ) & & ( livescount | | stplyr - > deadtimer < ( TICRATE < < 1 ) ) & & ! ( stplyr - > pflags & PF_FINISHED ) )
2019-12-27 19:58:55 +00:00
livescount + + ;
if ( livescount > 99 )
livescount = 99 ;
V_DrawRightAlignedString ( hudinfo [ HUD_LIVES ] . x + 58 , hudinfo [ HUD_LIVES ] . y + 8 ,
hudinfo [ HUD_LIVES ] . f | V_PERPLAYER | ( notgreyedout ? V_HUDTRANS : V_HUDTRANSHALF ) , va ( " %d " , livescount ) ) ;
}
}
# undef ST_drawLivesX
}
2017-06-03 11:26:42 +00:00
2018-01-20 22:14:24 +00:00
// name
2018-03-19 23:08:51 +00:00
v_colmap | = ( V_HUDTRANS | hudinfo [ HUD_LIVES ] . f | V_PERPLAYER ) ;
2018-01-20 22:14:24 +00:00
if ( strlen ( skins [ stplyr - > skin ] . hudname ) < = 5 )
V_DrawRightAlignedString ( hudinfo [ HUD_LIVES ] . x + 58 , hudinfo [ HUD_LIVES ] . y , v_colmap , skins [ stplyr - > skin ] . hudname ) ;
2020-01-01 15:49:29 +00:00
else if ( V_StringWidth ( skins [ stplyr - > skin ] . hudname , v_colmap ) < = 48 )
V_DrawString ( hudinfo [ HUD_LIVES ] . x + 18 , hudinfo [ HUD_LIVES ] . y , v_colmap , skins [ stplyr - > skin ] . hudname ) ;
2018-01-20 22:14:24 +00:00
else if ( V_ThinStringWidth ( skins [ stplyr - > skin ] . hudname , v_colmap ) < = 40 )
V_DrawRightAlignedThinString ( hudinfo [ HUD_LIVES ] . x + 58 , hudinfo [ HUD_LIVES ] . y , v_colmap , skins [ stplyr - > skin ] . hudname ) ;
2017-08-17 23:58:16 +00:00
else
2018-01-20 22:14:24 +00:00
V_DrawThinString ( hudinfo [ HUD_LIVES ] . x + 18 , hudinfo [ HUD_LIVES ] . y , v_colmap , skins [ stplyr - > skin ] . hudname ) ;
// Power Stones collected
2020-03-19 17:36:14 +00:00
if ( G_RingSlingerGametype ( ) & & LUA_HudEnabled ( hud_powerstones ) )
2017-08-17 23:58:16 +00:00
{
2018-01-20 22:14:24 +00:00
INT32 workx = hudinfo [ HUD_LIVES ] . x + 1 , j ;
if ( ( leveltime & 1 ) & & stplyr - > powers [ pw_invulnerability ] & & ( stplyr - > powers [ pw_sneakers ] = = stplyr - > powers [ pw_invulnerability ] ) ) // hack; extremely unlikely to be activated unintentionally
{
for ( j = 0 ; j < 7 ; + + j ) // "super" indicator
{
2018-03-19 23:08:51 +00:00
V_DrawScaledPatch ( workx , hudinfo [ HUD_LIVES ] . y - 9 , V_HUDTRANS | hudinfo [ HUD_LIVES ] . f | V_PERPLAYER , emeraldpics [ 1 ] [ j ] ) ;
2018-01-20 22:14:24 +00:00
workx + = 8 ;
}
}
else
{
for ( j = 0 ; j < 7 ; + + j ) // powerstones
{
if ( stplyr - > powers [ pw_emeralds ] & ( 1 < < j ) )
2018-03-19 23:08:51 +00:00
V_DrawScaledPatch ( workx , hudinfo [ HUD_LIVES ] . y - 9 , V_HUDTRANS | hudinfo [ HUD_LIVES ] . f | V_PERPLAYER , emeraldpics [ 1 ] [ j ] ) ;
2018-01-20 22:14:24 +00:00
workx + = 8 ;
}
}
2017-08-17 23:58:16 +00:00
}
2014-03-15 16:59:03 +00:00
}
2017-10-15 12:37:05 +00:00
static void ST_drawInput ( void )
{
2020-02-15 08:18:41 +00:00
const INT32 accent = V_SNAPTOLEFT | V_SNAPTOBOTTOM | ( stplyr - > skincolor ? skincolors [ stplyr - > skincolor ] . ramp [ 4 ] : 0 ) ;
2018-01-20 22:14:24 +00:00
INT32 col ;
UINT8 offs ;
2017-10-15 12:37:05 +00:00
2018-01-20 22:14:24 +00:00
INT32 x = hudinfo [ HUD_LIVES ] . x , y = hudinfo [ HUD_LIVES ] . y ;
2017-10-18 12:22:57 +00:00
2017-10-18 12:58:16 +00:00
if ( stplyr - > powers [ pw_carry ] = = CR_NIGHTSMODE )
y - = 16 ;
2018-11-10 06:00:18 +00:00
if ( F_GetPromptHideHud ( y ) )
return ;
2017-10-18 12:22:57 +00:00
// O backing
2018-03-19 23:08:51 +00:00
V_DrawFill ( x , y - 1 , 16 , 16 , hudinfo [ HUD_LIVES ] . f | 20 ) ;
V_DrawFill ( x , y + 15 , 16 , 1 , hudinfo [ HUD_LIVES ] . f | 29 ) ;
2017-10-15 12:37:05 +00:00
if ( cv_showinputjoy . value ) // joystick render!
{
2018-03-19 23:08:51 +00:00
/*V_DrawFill(x , y , 16, 1, hudinfo[HUD_LIVES].f|16);
V_DrawFill ( x , y + 15 , 16 , 1 , hudinfo [ HUD_LIVES ] . f | 16 ) ;
V_DrawFill ( x , y + 1 , 1 , 14 , hudinfo [ HUD_LIVES ] . f | 16 ) ;
V_DrawFill ( x + 15 , y + 1 , 1 , 14 , hudinfo [ HUD_LIVES ] . f | 16 ) ; - - red ' s outline */
2017-10-15 12:37:05 +00:00
if ( stplyr - > cmd . sidemove | | stplyr - > cmd . forwardmove )
{
2017-10-18 12:22:57 +00:00
// joystick hole
2018-03-19 23:08:51 +00:00
V_DrawFill ( x + 5 , y + 4 , 6 , 6 , hudinfo [ HUD_LIVES ] . f | 29 ) ;
2017-10-18 12:22:57 +00:00
// joystick top
V_DrawFill ( x + 3 + stplyr - > cmd . sidemove / 12 ,
y + 2 - stplyr - > cmd . forwardmove / 12 ,
2018-03-19 23:08:51 +00:00
10 , 10 , hudinfo [ HUD_LIVES ] . f | 29 ) ;
2017-10-18 12:22:57 +00:00
V_DrawFill ( x + 3 + stplyr - > cmd . sidemove / 9 ,
y + 1 - stplyr - > cmd . forwardmove / 9 ,
10 , 10 , accent ) ;
}
else
{
// just a limited, greyed out joystick top
2018-03-19 23:08:51 +00:00
V_DrawFill ( x + 3 , y + 11 , 10 , 1 , hudinfo [ HUD_LIVES ] . f | 29 ) ;
2017-10-18 12:22:57 +00:00
V_DrawFill ( x + 3 ,
y + 1 ,
2018-03-19 23:08:51 +00:00
10 , 10 , hudinfo [ HUD_LIVES ] . f | 16 ) ;
2017-10-15 12:37:05 +00:00
}
}
else // arrows!
{
// <
if ( stplyr - > cmd . sidemove < 0 )
{
offs = 0 ;
col = accent ;
}
else
{
offs = 1 ;
2018-03-19 23:08:51 +00:00
col = hudinfo [ HUD_LIVES ] . f | 16 ;
V_DrawFill ( x - 2 , y + 10 , 6 , 1 , hudinfo [ HUD_LIVES ] . f | 29 ) ;
V_DrawFill ( x + 4 , y + 9 , 1 , 1 , hudinfo [ HUD_LIVES ] . f | 29 ) ;
V_DrawFill ( x + 5 , y + 8 , 1 , 1 , hudinfo [ HUD_LIVES ] . f | 29 ) ;
2017-10-15 12:37:05 +00:00
}
2017-10-18 12:22:57 +00:00
V_DrawFill ( x - 2 , y + 5 - offs , 6 , 6 , col ) ;
V_DrawFill ( x + 4 , y + 6 - offs , 1 , 4 , col ) ;
V_DrawFill ( x + 5 , y + 7 - offs , 1 , 2 , col ) ;
2017-10-15 12:37:05 +00:00
// ^
if ( stplyr - > cmd . forwardmove > 0 )
{
offs = 0 ;
col = accent ;
}
else
{
offs = 1 ;
2018-03-19 23:08:51 +00:00
col = hudinfo [ HUD_LIVES ] . f | 16 ;
V_DrawFill ( x + 5 , y + 3 , 1 , 1 , hudinfo [ HUD_LIVES ] . f | 29 ) ;
V_DrawFill ( x + 6 , y + 4 , 1 , 1 , hudinfo [ HUD_LIVES ] . f | 29 ) ;
V_DrawFill ( x + 7 , y + 5 , 2 , 1 , hudinfo [ HUD_LIVES ] . f | 29 ) ;
V_DrawFill ( x + 9 , y + 4 , 1 , 1 , hudinfo [ HUD_LIVES ] . f | 29 ) ;
V_DrawFill ( x + 10 , y + 3 , 1 , 1 , hudinfo [ HUD_LIVES ] . f | 29 ) ;
2017-10-15 12:37:05 +00:00
}
2017-10-18 12:22:57 +00:00
V_DrawFill ( x + 5 , y - 2 - offs , 6 , 6 , col ) ;
V_DrawFill ( x + 6 , y + 4 - offs , 4 , 1 , col ) ;
V_DrawFill ( x + 7 , y + 5 - offs , 2 , 1 , col ) ;
2017-10-15 12:37:05 +00:00
// >
if ( stplyr - > cmd . sidemove > 0 )
{
offs = 0 ;
col = accent ;
}
else
{
offs = 1 ;
2018-03-19 23:08:51 +00:00
col = hudinfo [ HUD_LIVES ] . f | 16 ;
V_DrawFill ( x + 12 , y + 10 , 6 , 1 , hudinfo [ HUD_LIVES ] . f | 29 ) ;
V_DrawFill ( x + 11 , y + 9 , 1 , 1 , hudinfo [ HUD_LIVES ] . f | 29 ) ;
V_DrawFill ( x + 10 , y + 8 , 1 , 1 , hudinfo [ HUD_LIVES ] . f | 29 ) ;
2017-10-15 12:37:05 +00:00
}
2017-10-18 12:22:57 +00:00
V_DrawFill ( x + 12 , y + 5 - offs , 6 , 6 , col ) ;
V_DrawFill ( x + 11 , y + 6 - offs , 1 , 4 , col ) ;
V_DrawFill ( x + 10 , y + 7 - offs , 1 , 2 , col ) ;
2017-10-15 12:37:05 +00:00
// v
if ( stplyr - > cmd . forwardmove < 0 )
{
offs = 0 ;
col = accent ;
}
else
{
offs = 1 ;
2018-03-19 23:08:51 +00:00
col = hudinfo [ HUD_LIVES ] . f | 16 ;
V_DrawFill ( x + 5 , y + 17 , 6 , 1 , hudinfo [ HUD_LIVES ] . f | 29 ) ;
2017-10-15 12:37:05 +00:00
}
2017-10-18 12:22:57 +00:00
V_DrawFill ( x + 5 , y + 12 - offs , 6 , 6 , col ) ;
V_DrawFill ( x + 6 , y + 11 - offs , 4 , 1 , col ) ;
V_DrawFill ( x + 7 , y + 10 - offs , 2 , 1 , col ) ;
2017-10-15 12:37:05 +00:00
}
# define drawbutt(xoffs, yoffs, butt, symb)\
if ( stplyr - > cmd . buttons & butt ) \
{ \
offs = 0 ; \
col = accent ; \
} \
else \
{ \
offs = 1 ; \
2018-03-19 23:08:51 +00:00
col = hudinfo [ HUD_LIVES ] . f | 16 ; \
V_DrawFill ( x + 16 + ( xoffs ) , y + 9 + ( yoffs ) , 10 , 1 , hudinfo [ HUD_LIVES ] . f | 29 ) ; \
2017-10-15 12:37:05 +00:00
} \
2017-10-18 12:22:57 +00:00
V_DrawFill ( x + 16 + ( xoffs ) , y + ( yoffs ) - offs , 10 , 10 , col ) ; \
2018-03-19 23:08:51 +00:00
V_DrawCharacter ( x + 16 + 1 + ( xoffs ) , y + 1 + ( yoffs ) - offs , hudinfo [ HUD_LIVES ] . f | symb , false )
2017-10-15 12:37:05 +00:00
drawbutt ( 4 , - 3 , BT_JUMP , ' J ' ) ;
2020-06-06 09:46:22 +00:00
drawbutt ( 15 , - 3 , BT_SPIN , ' S ' ) ;
2017-10-15 12:37:05 +00:00
2018-03-19 23:08:51 +00:00
V_DrawFill ( x + 16 + 4 , y + 8 , 21 , 10 , hudinfo [ HUD_LIVES ] . f | 20 ) ; // sundial backing
2017-10-15 12:37:05 +00:00
if ( stplyr - > mo )
{
UINT8 i , precision ;
2017-10-18 12:58:16 +00:00
angle_t ang = ( stplyr - > powers [ pw_carry ] = = CR_NIGHTSMODE )
? ( FixedAngle ( ( stplyr - > flyangle - 90 ) < < FRACBITS ) > > ANGLETOFINESHIFT )
: ( stplyr - > mo - > angle - R_PointToAngle ( stplyr - > mo - > x , stplyr - > mo - > y ) ) > > ANGLETOFINESHIFT ;
2017-10-15 12:37:05 +00:00
fixed_t xcomp = FINESINE ( ang ) > > 13 ;
fixed_t ycomp = FINECOSINE ( ang ) > > 14 ;
if ( ycomp = = 4 )
ycomp = 3 ;
if ( ycomp > 0 )
2017-10-18 12:22:57 +00:00
V_DrawFill ( x + 16 + 13 - xcomp , y + 11 - ycomp , 3 , 3 , accent ) ; // point (behind)
2017-10-15 12:37:05 +00:00
precision = max ( 3 , abs ( xcomp ) ) ;
2017-10-18 12:58:16 +00:00
for ( i = 0 ; i < precision ; i + + ) // line
2017-10-15 12:37:05 +00:00
{
2017-10-18 12:22:57 +00:00
V_DrawFill ( x + 16 + 14 - ( i * xcomp ) / precision ,
y + 12 - ( i * ycomp ) / precision ,
2018-03-19 23:08:51 +00:00
1 , 1 , hudinfo [ HUD_LIVES ] . f | 16 ) ;
2017-10-15 12:37:05 +00:00
}
if ( ycomp < = 0 )
2017-10-18 12:22:57 +00:00
V_DrawFill ( x + 16 + 13 - xcomp , y + 11 - ycomp , 3 , 3 , accent ) ; // point (in front)
2017-10-15 12:37:05 +00:00
}
# undef drawbutt
2017-10-18 12:22:57 +00:00
// text above
x - = 2 ;
y - = 13 ;
2017-10-23 22:29:55 +00:00
if ( stplyr - > powers [ pw_carry ] ! = CR_NIGHTSMODE )
2017-10-18 12:22:57 +00:00
{
2017-10-18 12:58:16 +00:00
if ( stplyr - > pflags & PF_AUTOBRAKE )
{
V_DrawThinString ( x , y ,
2018-03-19 23:08:51 +00:00
hudinfo [ HUD_LIVES ] . f |
2017-10-18 12:58:16 +00:00
( ( ! stplyr - > powers [ pw_carry ]
& & ( stplyr - > pflags & PF_APPLYAUTOBRAKE )
& & ! ( stplyr - > cmd . sidemove | | stplyr - > cmd . forwardmove )
2018-06-05 16:22:28 +00:00
& & ( stplyr - > rmomx | | stplyr - > rmomy )
& & ( ! stplyr - > capsule | | ( stplyr - > capsule - > reactiontime ! = ( stplyr - players ) + 1 ) ) )
2017-10-18 12:58:16 +00:00
? 0 : V_GRAYMAP ) ,
" AUTOBRAKE " ) ;
y - = 8 ;
}
2019-12-30 21:05:24 +00:00
switch ( P_ControlStyle ( stplyr ) )
2017-10-18 12:58:16 +00:00
{
2019-12-30 21:05:24 +00:00
case CS_LMAOGALOG :
2018-03-19 23:08:51 +00:00
V_DrawThinString ( x , y , hudinfo [ HUD_LIVES ] . f , " ANALOG " ) ;
2017-10-18 12:58:16 +00:00
y - = 8 ;
2019-12-30 21:05:24 +00:00
break ;
case CS_SIMPLE :
2021-12-17 10:53:10 +00:00
V_DrawThinString ( x , y , hudinfo [ HUD_LIVES ] . f , " AUTOMATIC " ) ;
y - = 8 ;
break ;
case CS_STANDARD :
V_DrawThinString ( x , y , hudinfo [ HUD_LIVES ] . f , " MANUAL " ) ;
y - = 8 ;
break ;
case CS_LEGACY :
V_DrawThinString ( x , y , hudinfo [ HUD_LIVES ] . f , " STRAFE " ) ;
2019-12-30 21:05:24 +00:00
y - = 8 ;
break ;
default :
break ;
2017-10-18 12:58:16 +00:00
}
2017-10-18 12:22:57 +00:00
}
if ( ! demosynced ) // should always be last, so it doesn't push anything else around
2018-03-19 23:08:51 +00:00
V_DrawThinString ( x , y , hudinfo [ HUD_LIVES ] . f | ( ( leveltime & 4 ) ? V_YELLOWMAP : V_REDMAP ) , " BAD DEMO!! " ) ;
2017-10-15 12:37:05 +00:00
}
2019-11-18 14:39:54 +00:00
static patch_t * lt_patches [ 3 ] ;
static INT32 lt_scroll = 0 ;
static INT32 lt_mom = 0 ;
static INT32 lt_zigzag = 0 ;
tic_t lt_ticker = 0 , lt_lasttic = 0 ;
tic_t lt_exitticker = 0 , lt_endtime = 0 ;
//
// Load the graphics for the title card.
2019-12-18 03:28:58 +00:00
// Don't let LJ see this
2019-11-18 14:39:54 +00:00
//
static void ST_cacheLevelTitle ( void )
{
2019-12-18 03:28:58 +00:00
# define SETPATCH(default, warning, custom, idx) \
{ \
lumpnum_t patlumpnum = LUMPERROR ; \
if ( mapheaderinfo [ gamemap - 1 ] - > custom [ 0 ] ! = ' \0 ' ) \
{ \
patlumpnum = W_CheckNumForName ( mapheaderinfo [ gamemap - 1 ] - > custom ) ; \
if ( patlumpnum ! = LUMPERROR ) \
lt_patches [ idx ] = ( patch_t * ) W_CachePatchNum ( patlumpnum , PU_HUDGFX ) ; \
} \
if ( patlumpnum = = LUMPERROR ) \
{ \
if ( ! ( mapheaderinfo [ gamemap - 1 ] - > levelflags & LF_WARNINGTITLE ) ) \
lt_patches [ idx ] = ( patch_t * ) W_CachePatchName ( default , PU_HUDGFX ) ; \
else \
lt_patches [ idx ] = ( patch_t * ) W_CachePatchName ( warning , PU_HUDGFX ) ; \
} \
}
SETPATCH ( " LTACTBLU " , " LTACTRED " , ltactdiamond , 0 )
SETPATCH ( " LTZIGZAG " , " LTZIGRED " , ltzzpatch , 1 )
SETPATCH ( " LTZZTEXT " , " LTZZWARN " , ltzztext , 2 )
# undef SETPATCH
2019-11-18 14:39:54 +00:00
}
//
// Start the title card.
//
void ST_startTitleCard ( void )
{
2019-11-18 15:56:41 +00:00
// cache every HUD patch used
2019-11-18 14:39:54 +00:00
ST_cacheLevelTitle ( ) ;
2019-11-18 15:56:41 +00:00
// initialize HUD variables
2019-11-18 14:39:54 +00:00
lt_ticker = lt_exitticker = lt_lasttic = 0 ;
2019-11-18 22:08:04 +00:00
lt_endtime = 2 * TICRATE + ( 10 * NEWTICRATERATIO ) ;
2019-11-18 14:39:54 +00:00
lt_scroll = BASEVIDWIDTH * FRACUNIT ;
lt_zigzag = - ( ( lt_patches [ 1 ] ) - > width * FRACUNIT ) ;
lt_mom = 0 ;
}
//
2019-11-18 18:13:31 +00:00
// What happens before drawing the title card.
2019-11-18 14:39:54 +00:00
// Which is just setting the HUD translucency.
//
void ST_preDrawTitleCard ( void )
{
2019-12-24 19:22:25 +00:00
if ( ! G_IsTitleCardAvailable ( ) )
return ;
2019-11-18 14:39:54 +00:00
if ( lt_ticker > = ( lt_endtime + TICRATE ) )
return ;
if ( ! lt_exitticker )
st_translucency = 0 ;
else
st_translucency = max ( 0 , min ( ( INT32 ) lt_exitticker - 4 , cv_translucenthud . value ) ) ;
}
//
// Run the title card.
// Called from ST_Ticker.
//
void ST_runTitleCard ( void )
{
2020-01-27 02:50:36 +00:00
boolean run = ! ( paused | | P_AutoPause ( ) ) ;
2019-12-24 19:22:25 +00:00
if ( ! G_IsTitleCardAvailable ( ) )
return ;
2019-11-18 14:39:54 +00:00
if ( lt_ticker > = ( lt_endtime + TICRATE ) )
return ;
2020-01-27 02:50:36 +00:00
if ( run | | ( lt_ticker < PRELEVELTIME ) )
2019-11-18 14:39:54 +00:00
{
// tick
lt_ticker + + ;
if ( lt_ticker > = lt_endtime )
lt_exitticker + + ;
// scroll to screen (level title)
if ( ! lt_exitticker )
{
if ( abs ( lt_scroll ) > FRACUNIT )
lt_scroll - = ( lt_scroll > > 2 ) ;
else
lt_scroll = 0 ;
}
// scroll away from screen (level title)
else
{
lt_mom - = FRACUNIT * 6 ;
lt_scroll + = lt_mom ;
}
// scroll to screen (zigzag)
if ( ! lt_exitticker )
{
if ( abs ( lt_zigzag ) > FRACUNIT )
lt_zigzag - = ( lt_zigzag > > 2 ) ;
else
lt_zigzag = 0 ;
}
// scroll away from screen (zigzag)
else
lt_zigzag + = lt_mom ;
}
}
//
// Draw the title card itself.
//
void ST_drawTitleCard ( void )
2014-03-15 16:59:03 +00:00
{
char * lvlttl = mapheaderinfo [ gamemap - 1 ] - > lvlttl ;
char * subttl = mapheaderinfo [ gamemap - 1 ] - > subttl ;
2020-04-30 14:01:03 +00:00
UINT8 actnum = mapheaderinfo [ gamemap - 1 ] - > actnum ;
2019-11-18 14:39:54 +00:00
INT32 lvlttlxpos , ttlnumxpos , zonexpos ;
2014-03-15 16:59:03 +00:00
INT32 subttlxpos = BASEVIDWIDTH / 2 ;
2019-11-18 14:39:54 +00:00
INT32 ttlscroll = FixedInt ( lt_scroll ) ;
INT32 zzticker ;
patch_t * actpat , * zigzag , * zztext ;
2020-01-03 19:21:08 +00:00
UINT8 colornum ;
const UINT8 * colormap ;
2020-05-11 17:57:20 +00:00
if ( players [ consoleplayer ] . skincolor )
colornum = players [ consoleplayer ] . skincolor ;
2020-01-03 19:21:08 +00:00
else
colornum = cv_playercolor . value ;
2020-05-11 17:57:20 +00:00
2020-01-03 19:21:08 +00:00
colormap = R_GetTranslationColormap ( TC_DEFAULT , colornum , GTC_CACHE ) ;
2014-03-15 16:59:03 +00:00
2019-12-24 19:22:25 +00:00
if ( ! G_IsTitleCardAvailable ( ) )
return ;
2019-11-18 16:50:44 +00:00
if ( ! LUA_HudEnabled ( hud_stagetitle ) )
goto luahook ;
2014-03-15 16:59:03 +00:00
2019-11-18 14:39:54 +00:00
if ( lt_ticker > = ( lt_endtime + TICRATE ) )
2019-11-18 16:50:44 +00:00
goto luahook ;
2014-03-15 16:59:03 +00:00
2019-11-18 14:39:54 +00:00
if ( ( lt_ticker - lt_lasttic ) > 1 )
lt_ticker = lt_lasttic + 1 ;
ST_cacheLevelTitle ( ) ;
actpat = lt_patches [ 0 ] ;
zigzag = lt_patches [ 1 ] ;
zztext = lt_patches [ 2 ] ;
2014-03-15 16:59:03 +00:00
2018-02-22 21:49:36 +00:00
lvlttlxpos = ( ( BASEVIDWIDTH / 2 ) - ( V_LevelNameWidth ( lvlttl ) / 2 ) ) ;
2014-03-15 16:59:03 +00:00
if ( actnum > 0 )
2018-02-22 21:49:36 +00:00
lvlttlxpos - = V_LevelActNumWidth ( actnum ) ;
2014-03-15 16:59:03 +00:00
ttlnumxpos = lvlttlxpos + V_LevelNameWidth ( lvlttl ) ;
2019-03-17 20:44:24 +00:00
zonexpos = ttlnumxpos - V_LevelNameWidth ( M_GetText ( " Zone " ) ) ;
2018-03-30 15:39:59 +00:00
ttlnumxpos + + ;
2014-03-15 16:59:03 +00:00
if ( lvlttlxpos < 0 )
lvlttlxpos = 0 ;
2019-11-18 18:32:48 +00:00
if ( ! splitscreen | | ( splitscreen & & stplyr = = & players [ displayplayer ] ) )
2018-03-30 15:39:59 +00:00
{
2019-11-18 18:18:27 +00:00
zzticker = lt_ticker ;
2020-01-03 19:21:08 +00:00
V_DrawMappedPatch ( FixedInt ( lt_zigzag ) , ( - zzticker ) % zigzag - > height , V_SNAPTOTOP | V_SNAPTOLEFT , zigzag , colormap ) ;
V_DrawMappedPatch ( FixedInt ( lt_zigzag ) , ( zigzag - > height - zzticker ) % zigzag - > height , V_SNAPTOTOP | V_SNAPTOLEFT , zigzag , colormap ) ;
2021-05-07 18:14:31 +00:00
V_DrawMappedPatch ( FixedInt ( lt_zigzag ) , ( - zztext - > height + zzticker ) % zztext - > height , V_SNAPTOTOP | V_SNAPTOLEFT , zztext , colormap ) ;
2020-01-03 19:21:08 +00:00
V_DrawMappedPatch ( FixedInt ( lt_zigzag ) , ( zzticker ) % zztext - > height , V_SNAPTOTOP | V_SNAPTOLEFT , zztext , colormap ) ;
2018-03-30 15:39:59 +00:00
}
2019-11-18 14:39:54 +00:00
if ( actnum )
2018-03-30 15:39:59 +00:00
{
2019-11-18 18:18:27 +00:00
if ( ! splitscreen )
2020-04-30 14:01:03 +00:00
{
2020-04-30 15:12:52 +00:00
if ( actnum > 9 ) // slightly offset the act diamond for two-digit act numbers
2020-04-30 14:01:03 +00:00
V_DrawMappedPatch ( ttlnumxpos + ( V_LevelActNumWidth ( actnum ) / 4 ) + ttlscroll , 104 - ttlscroll , 0 , actpat , colormap ) ;
else
V_DrawMappedPatch ( ttlnumxpos + ttlscroll , 104 - ttlscroll , 0 , actpat , colormap ) ;
}
2019-11-18 14:39:54 +00:00
V_DrawLevelActNum ( ttlnumxpos + ttlscroll , 104 , V_PERPLAYER , actnum ) ;
2018-03-30 15:39:59 +00:00
}
2019-11-18 14:39:54 +00:00
V_DrawLevelTitle ( lvlttlxpos - ttlscroll , 80 , V_PERPLAYER , lvlttl ) ;
if ( ! ( mapheaderinfo [ gamemap - 1 ] - > levelflags & LF_NOZONE ) )
2019-11-18 20:55:47 +00:00
V_DrawLevelTitle ( zonexpos + ttlscroll , 104 , V_PERPLAYER , M_GetText ( " Zone " ) ) ;
2019-11-19 12:59:01 +00:00
V_DrawCenteredString ( subttlxpos - ttlscroll , 135 , V_PERPLAYER | V_ALLOWLOWERCASE , subttl ) ;
2014-03-15 16:59:03 +00:00
2019-11-18 14:39:54 +00:00
lt_lasttic = lt_ticker ;
2019-11-18 16:50:44 +00:00
luahook :
2021-07-07 07:23:51 +00:00
LUA_HUDHOOK ( titlecard ) ;
2019-11-18 14:39:54 +00:00
}
2014-03-15 16:59:03 +00:00
2019-11-18 14:39:54 +00:00
//
2019-11-18 15:56:41 +00:00
// Drawer for G_PreLevelTitleCard.
2019-11-18 14:39:54 +00:00
//
2019-12-05 04:58:19 +00:00
void ST_preLevelTitleCardDrawer ( void )
2019-11-18 14:39:54 +00:00
{
V_DrawFill ( 0 , 0 , BASEVIDWIDTH , BASEVIDHEIGHT , levelfadecol ) ;
2019-12-05 04:58:19 +00:00
ST_drawWipeTitleCard ( ) ;
2019-11-18 14:39:54 +00:00
I_OsPolling ( ) ;
I_UpdateNoBlit ( ) ;
}
2014-03-15 16:59:03 +00:00
2019-11-18 14:39:54 +00:00
//
// Draw the title card while on a wipe.
2019-11-18 15:56:41 +00:00
// Also used in G_PreLevelTitleCard.
2019-11-18 14:39:54 +00:00
//
void ST_drawWipeTitleCard ( void )
{
stplyr = & players [ consoleplayer ] ;
ST_preDrawTitleCard ( ) ;
ST_drawTitleCard ( ) ;
if ( splitscreen )
2019-08-23 04:21:12 +00:00
{
2019-11-18 14:39:54 +00:00
stplyr = & players [ secondarydisplayplayer ] ;
ST_preDrawTitleCard ( ) ;
ST_drawTitleCard ( ) ;
2019-08-23 04:21:12 +00:00
}
2014-03-15 16:59:03 +00:00
}
2018-03-26 22:53:09 +00:00
static void ST_drawPowerupHUD ( void )
2014-03-15 16:59:03 +00:00
{
patch_t * p = NULL ;
2014-03-17 12:13:16 +00:00
UINT16 invulntime = 0 ;
2018-03-26 22:53:09 +00:00
INT32 offs = hudinfo [ HUD_POWERUPS ] . x ;
2018-03-30 15:39:59 +00:00
const UINT8 q = ( ( splitscreen & & stplyr = = & players [ secondarydisplayplayer ] ) ? 1 : 0 ) ;
2020-01-20 00:55:08 +00:00
static INT32 flagoffs [ 2 ] = { 0 , 0 } , shieldoffs [ 2 ] = { 0 , 0 } , finishoffs [ 2 ] = { 0 , 0 } ;
2018-03-26 22:53:09 +00:00
# define ICONSEP (16+4) // matches weapon rings HUD
2014-03-17 12:13:16 +00:00
2018-11-10 06:00:18 +00:00
if ( F_GetPromptHideHud ( hudinfo [ HUD_POWERUPS ] . y ) )
return ;
2018-03-26 22:53:09 +00:00
if ( stplyr - > spectator | | stplyr - > playerstate ! = PST_LIVE )
2014-03-17 12:13:16 +00:00
return ;
2020-02-23 20:02:50 +00:00
2020-01-20 00:55:08 +00:00
// ---------
// Finish icon
// ---------
// Let's have a power-like icon to represent finishing the level!
Introducing Marathon Run. (I was going to call it Marathon Mode, but NiGHTS Mode being right next to it on the menu looked terrible.)
Basically a dedicated Record Attack-like experience for speedrunning the game as a continuous chunk rather than ILs. Has several quality of life features.
Benefits include:
* An unambiguous real-time bar across the bottom of the screen, always displaying the current time, ticking up until you reach the ending.
* Disable the console (pausing is still allowed, but the timer will still increment).
* Automatically skip intermissions as if you're holding down the spin button.
* Show centiseconds on HUD automatically, like record attack.
* "Live Event Backups" - a category of run fit for major events like GDQ, where recovery from crashes or chokes makes for better entertainment. Essentially a modified SP savefile, down to using the same basic functions, but has its own filename and tweaked internal layout.
* "spmarathon_start" MainCfg block parameter and "marathonnext" mapheader parameter, allowing for a customised flow (makes this fit for purpose for an eventual SUGOI port).
* Disabling inter-level custom cutscenes by default with a menu option to toggle this (won't show up if the mod doesn't *have* any custom cutscenes), although either way ending cutscenes (vanilla or custom) remain intact since is time is called before them.
* Won't show up if you have a mod that consists of only one level (determined by spmarathon_start's nextlevel; this won't trip if you manually set its marathonnext).
* Unconditional gratitude on the evaluation screen, instead of a negging "Try again..." if you didn't get all the emeralds (which you may not have been aiming for).
* Gorgeous new menu (no new assets required, unless you wanna give it a header later).
Changes which were required for the above but affect other areas of the game include:
* "useBlackRock" MainCFG block parameter, which can be used to disable the presence of the Black Rock or Egg Rock in both the Evaluation screen and the Marathon Run menu (for total conversions with different stories).
* Disabling Continues in NiGHTS mode, to match the most common singleplayer experience post 2.2.4's release (is reverted if useContinues is set to true).
* Hiding the exitmove "powerup" outside of multiplayer. (Okay, this isn't really related, I just saw this bug in action a lot while doing test runs and got annoyed enough to fix it here.)
* The ability to use V_DrawPromptBack (in hardcode only at the moment, but) to draw in terms of pixels rather than rows of text, by providing negative instead of positive inputs).
* A refactoring of redundant game saves smattered across the ending, credits, and evaluation - in addition to saving the game slightly earlier.
* Minor m_menu.c touchups and refactorings here and there.
Built using feedback from the official server's #speedruns channel, among other places.
2020-05-14 22:10:00 +00:00
if ( stplyr - > pflags & PF_FINISHED & & cv_exitmove . value & & multiplayer )
2020-01-20 00:55:08 +00:00
{
finishoffs [ q ] = ICONSEP ;
V_DrawSmallScaledPatch ( offs , hudinfo [ HUD_POWERUPS ] . y , V_PERPLAYER | hudinfo [ HUD_POWERUPS ] . f | V_HUDTRANS , fnshico ) ;
}
else if ( finishoffs [ q ] )
{
if ( finishoffs [ q ] > 1 )
finishoffs [ q ] = 2 * finishoffs [ q ] / 3 ;
else
finishoffs [ q ] = 0 ;
}
offs - = finishoffs [ q ] ;
2014-03-15 16:59:03 +00:00
2018-08-23 21:00:37 +00:00
// -------
// Shields
// -------
2014-03-15 16:59:03 +00:00
// Graue 06-18-2004: no V_NOSCALESTART, no SCX, no SCY, snap to right
2018-03-26 22:53:09 +00:00
if ( stplyr - > powers [ pw_shield ] & SH_NOSTACK )
2017-04-29 15:40:07 +00:00
{
2018-03-30 15:39:59 +00:00
shieldoffs [ q ] = ICONSEP ;
2018-03-26 22:53:09 +00:00
if ( ( stplyr - > powers [ pw_shield ] & SH_NOSTACK & ~ SH_FORCEHP ) = = SH_FORCE )
{
UINT8 i , max = ( stplyr - > powers [ pw_shield ] & SH_FORCEHP ) ;
for ( i = 0 ; i < = max ; i + + )
{
V_DrawSmallScaledPatch ( offs - ( i < < 1 ) , hudinfo [ HUD_POWERUPS ] . y - ( i < < 1 ) , ( V_PERPLAYER | hudinfo [ HUD_POWERUPS ] . f ) | ( ( i = = max ) ? V_HUDTRANS : V_HUDTRANSHALF ) , forceshield ) ;
}
}
else
2017-04-29 15:40:07 +00:00
{
2018-03-26 22:53:09 +00:00
switch ( stplyr - > powers [ pw_shield ] & SH_NOSTACK )
{
case SH_WHIRLWIND : p = jumpshield ; break ;
case SH_ELEMENTAL : p = watershield ; break ;
case SH_ARMAGEDDON : p = bombshield ; break ;
case SH_ATTRACT : p = ringshield ; break ;
case SH_PITY : p = pityshield ; break ;
2019-06-22 15:44:32 +00:00
case SH_PINK : p = pinkshield ; break ;
2018-03-26 22:53:09 +00:00
case SH_FLAMEAURA : p = flameshield ; break ;
case SH_BUBBLEWRAP : p = bubbleshield ; break ;
case SH_THUNDERCOIN : p = thundershield ; break ;
default : break ;
}
if ( p )
V_DrawSmallScaledPatch ( offs , hudinfo [ HUD_POWERUPS ] . y , V_PERPLAYER | hudinfo [ HUD_POWERUPS ] . f | V_HUDTRANS , p ) ;
2017-04-29 15:40:07 +00:00
}
}
2018-03-30 15:39:59 +00:00
else if ( shieldoffs [ q ] )
2014-03-15 16:59:03 +00:00
{
2018-03-30 15:39:59 +00:00
if ( shieldoffs [ q ] > 1 )
shieldoffs [ q ] = 2 * shieldoffs [ q ] / 3 ;
2018-03-26 22:53:09 +00:00
else
2018-03-30 15:39:59 +00:00
shieldoffs [ q ] = 0 ;
2014-03-17 12:13:16 +00:00
}
2018-03-30 15:39:59 +00:00
offs - = shieldoffs [ q ] ;
2018-03-26 22:53:09 +00:00
2018-08-23 21:00:37 +00:00
// ---------
// CTF flags
// ---------
2018-03-26 22:53:09 +00:00
// YOU have a flag. Display a monitor-like icon for it.
if ( stplyr - > gotflag )
{
2018-03-30 15:39:59 +00:00
flagoffs [ q ] = ICONSEP ;
2018-03-26 22:53:09 +00:00
p = ( stplyr - > gotflag & GF_REDFLAG ) ? gotrflag : gotbflag ;
V_DrawSmallScaledPatch ( offs , hudinfo [ HUD_POWERUPS ] . y , V_PERPLAYER | hudinfo [ HUD_POWERUPS ] . f | V_HUDTRANS , p ) ;
}
2018-03-30 15:39:59 +00:00
else if ( flagoffs [ q ] )
2018-03-26 22:53:09 +00:00
{
2018-03-30 15:39:59 +00:00
if ( flagoffs [ q ] > 1 )
flagoffs [ q ] = 2 * flagoffs [ q ] / 3 ;
2017-04-13 21:09:03 +00:00
else
2018-03-30 15:39:59 +00:00
flagoffs [ q ] = 0 ;
2018-03-26 22:53:09 +00:00
}
2017-04-13 21:09:03 +00:00
2018-03-30 15:39:59 +00:00
offs - = flagoffs [ q ] ;
2017-04-13 21:09:03 +00:00
2018-08-23 21:00:37 +00:00
// --------------------
// Timer-based powerups
// --------------------
# define DRAWTIMERICON(patch, timer) \
V_DrawSmallScaledPatch ( offs , hudinfo [ HUD_POWERUPS ] . y , V_PERPLAYER | hudinfo [ HUD_POWERUPS ] . f | V_HUDTRANS , patch ) ; \
V_DrawRightAlignedThinString ( offs + 16 , hudinfo [ HUD_POWERUPS ] . y + 8 , V_PERPLAYER | hudinfo [ HUD_POWERUPS ] . f , va ( " %d " , timer / TICRATE ) ) ;
// Invincibility, both from monitor and after being hit
2018-03-26 22:53:09 +00:00
invulntime = stplyr - > powers [ pw_flashing ] ? stplyr - > powers [ pw_flashing ] : stplyr - > powers [ pw_invulnerability ] ;
2018-08-23 21:00:37 +00:00
// Note: pw_flashing always makes the icon flicker regardless of time, unlike pw_invulnerability
2018-03-26 22:53:09 +00:00
if ( stplyr - > powers [ pw_invulnerability ] > 3 * TICRATE | | ( invulntime & & leveltime & 1 ) )
{
2018-08-23 21:00:37 +00:00
DRAWTIMERICON ( invincibility , invulntime )
2014-03-15 16:59:03 +00:00
}
2018-03-26 22:53:09 +00:00
if ( invulntime > 7 )
offs - = ICONSEP ;
else
{
UINT8 a = ICONSEP , b = 7 - invulntime ;
while ( b - - )
a = 2 * a / 3 ;
offs - = a ;
}
2018-08-23 21:00:37 +00:00
// Super Sneakers
2018-03-26 22:53:09 +00:00
if ( stplyr - > powers [ pw_sneakers ] > 3 * TICRATE | | ( stplyr - > powers [ pw_sneakers ] & & leveltime & 1 ) )
{
2018-08-23 21:00:37 +00:00
DRAWTIMERICON ( sneakers , stplyr - > powers [ pw_sneakers ] )
2018-03-26 22:53:09 +00:00
}
if ( stplyr - > powers [ pw_sneakers ] > 7 )
offs - = ICONSEP ;
else
{
UINT8 a = ICONSEP , b = 7 - stplyr - > powers [ pw_sneakers ] ;
while ( b - - )
a = 2 * a / 3 ;
offs - = a ;
}
2018-08-23 21:00:37 +00:00
// Gravity Boots
2018-03-26 22:53:09 +00:00
if ( stplyr - > powers [ pw_gravityboots ] > 3 * TICRATE | | ( stplyr - > powers [ pw_gravityboots ] & & leveltime & 1 ) )
{
2018-08-23 21:00:37 +00:00
DRAWTIMERICON ( gravboots , stplyr - > powers [ pw_gravityboots ] )
2018-03-26 22:53:09 +00:00
}
2018-08-23 21:00:37 +00:00
# undef DRAWTIMERICON
2018-03-26 22:53:09 +00:00
# undef ICONSEP
}
static void ST_drawFirstPersonHUD ( void )
{
patch_t * p = NULL ;
UINT32 airtime ;
spriteframe_t * sprframe ;
// If both air timers are active, use the air timer with the least time left
if ( stplyr - > powers [ pw_underwater ] & & stplyr - > powers [ pw_spacetime ] )
airtime = min ( stplyr - > powers [ pw_underwater ] , stplyr - > powers [ pw_spacetime ] ) ;
else // Use whichever one is active otherwise
airtime = ( stplyr - > powers [ pw_spacetime ] ) ? stplyr - > powers [ pw_spacetime ] : stplyr - > powers [ pw_underwater ] ;
2018-03-30 20:49:15 +00:00
if ( airtime < 1 )
2018-03-26 22:53:09 +00:00
return ; // No air timers are active, nothing would be drawn anyway
airtime - - ; // The original code was all n*TICRATE + 1, so let's remove 1 tic for simplicity
if ( airtime > 11 * TICRATE )
return ; // Not time to draw any drown numbers yet
2018-03-30 20:49:15 +00:00
if ( ! ( ( airtime > 10 * TICRATE - 5 )
| | ( airtime < = 9 * TICRATE & & airtime > 8 * TICRATE - 5 )
| | ( airtime < = 7 * TICRATE & & airtime > 6 * TICRATE - 5 )
| | ( airtime < = 5 * TICRATE & & airtime > 4 * TICRATE - 5 )
| | ( airtime < = 3 * TICRATE & & airtime > 2 * TICRATE - 5 )
| | ( airtime < = 1 * TICRATE ) ) )
2018-03-26 22:53:09 +00:00
return ; // Don't draw anything between numbers
2018-03-30 20:49:15 +00:00
if ( ! ( ( airtime % 10 ) < 5 ) )
return ; // Keep in line with the flashing thing from third person.
airtime / = ( 2 * TICRATE ) ; // To be strictly accurate it'd need to be ((airtime/TICRATE) - 1)/2, but integer division rounds down for us
2018-03-26 22:53:09 +00:00
if ( stplyr - > charflags & SF_MACHINE )
2018-03-30 20:49:15 +00:00
airtime + = 6 ; // Robots use different drown numbers
2018-03-26 22:53:09 +00:00
// Get the front angle patch for the frame
2018-03-30 20:49:15 +00:00
sprframe = & sprites [ SPR_DRWN ] . spriteframes [ airtime ] ;
2018-03-26 22:53:09 +00:00
p = W_CachePatchNum ( sprframe - > lumppat [ 0 ] , PU_CACHE ) ;
2017-04-13 21:09:03 +00:00
// Display the countdown drown numbers!
2020-11-22 23:02:47 +00:00
if ( p & & ! F_GetPromptHideHud ( 60 - p - > topoffset ) )
V_DrawScaledPatch ( ( BASEVIDWIDTH / 2 ) - ( p - > width / 2 ) + SHORT ( p - > leftoffset ) , 60 - SHORT ( p - > topoffset ) ,
2018-03-19 23:08:51 +00:00
V_PERPLAYER | V_PERPLAYER | V_TRANSLUCENT , p ) ;
2014-03-15 16:59:03 +00:00
}
static void ST_drawNightsRecords ( void )
{
2018-01-20 22:14:24 +00:00
INT32 aflag = V_PERPLAYER ;
2014-03-15 16:59:03 +00:00
if ( ! stplyr - > texttimer )
return ;
if ( stplyr - > texttimer < TICRATE / 2 )
2018-01-20 22:14:24 +00:00
aflag | = ( 9 - 9 * stplyr - > texttimer / ( TICRATE / 2 ) ) < < V_ALPHASHIFT ;
2014-03-15 16:59:03 +00:00
2018-08-23 19:27:40 +00:00
switch ( stplyr - > textvar )
2014-03-15 16:59:03 +00:00
{
2018-08-23 19:27:40 +00:00
case 1 : // A "Bonus Time Start" by any other name...
2014-03-15 16:59:03 +00:00
{
2018-08-23 19:27:40 +00:00
V_DrawCenteredString ( BASEVIDWIDTH / 2 , 52 , V_GREENMAP | aflag , M_GetText ( " GET TO THE GOAL! " ) ) ;
V_DrawCenteredString ( BASEVIDWIDTH / 2 , 60 , aflag , M_GetText ( " SCORE MULTIPLIER START! " ) ) ;
2014-03-15 16:59:03 +00:00
2018-08-23 19:27:40 +00:00
if ( stplyr - > finishedtime )
{
V_DrawString ( BASEVIDWIDTH / 2 - 48 , 140 , aflag , " TIME: " ) ;
V_DrawString ( BASEVIDWIDTH / 2 - 48 , 148 , aflag , " BONUS: " ) ;
V_DrawRightAlignedString ( BASEVIDWIDTH / 2 + 48 , 140 , V_ORANGEMAP | aflag , va ( " %d " , ( stplyr - > startedtime - stplyr - > finishedtime ) / TICRATE ) ) ;
V_DrawRightAlignedString ( BASEVIDWIDTH / 2 + 48 , 148 , V_ORANGEMAP | aflag , va ( " %d " , ( stplyr - > finishedtime / TICRATE ) * 100 ) ) ;
}
break ;
2014-03-15 16:59:03 +00:00
}
2018-08-23 19:27:40 +00:00
case 2 : // Get n Spheres
case 3 : // Get n more Spheres
2014-03-15 16:59:03 +00:00
{
2018-08-23 19:27:40 +00:00
if ( ! stplyr - > capsule )
return ;
2014-03-15 16:59:03 +00:00
2018-08-23 19:27:40 +00:00
// Yes, this string is an abomination.
V_DrawCenteredString ( BASEVIDWIDTH / 2 , 60 , aflag ,
va ( M_GetText ( " \x80 GET \x82 %d \x80 %s%s%s! " ) , stplyr - > capsule - > health ,
( stplyr - > textvar = = 3 ) ? M_GetText ( " MORE " ) : " " ,
( G_IsSpecialStage ( gamemap ) ) ? " SPHERE " : " CHIP " ,
( stplyr - > capsule - > health > 1 ) ? " S " : " " ) ) ;
break ;
2014-03-15 16:59:03 +00:00
}
2018-08-23 19:27:40 +00:00
case 4 : // End Bonus
2014-03-15 16:59:03 +00:00
{
2018-08-23 19:27:40 +00:00
V_DrawString ( BASEVIDWIDTH / 2 - 56 , 140 , aflag , ( G_IsSpecialStage ( gamemap ) ) ? " SPHERES: " : " CHIPS: " ) ;
V_DrawString ( BASEVIDWIDTH / 2 - 56 , 148 , aflag , " BONUS: " ) ;
V_DrawRightAlignedString ( BASEVIDWIDTH / 2 + 56 , 140 , V_ORANGEMAP | aflag , va ( " %d " , stplyr - > finishedspheres ) ) ;
V_DrawRightAlignedString ( BASEVIDWIDTH / 2 + 56 , 148 , V_ORANGEMAP | aflag , va ( " %d " , stplyr - > finishedspheres * 50 ) ) ;
ST_DrawNightsOverlayNum ( ( BASEVIDWIDTH / 2 + 56 ) < < FRACBITS , 160 < < FRACBITS , FRACUNIT , aflag , stplyr - > lastmarescore , nightsnum , SKINCOLOR_AZURE ) ;
// If new record, say so!
if ( ! ( netgame | | multiplayer ) & & G_GetBestNightsScore ( gamemap , stplyr - > lastmare + 1 ) < = stplyr - > lastmarescore )
{
if ( stplyr - > texttimer & 16 )
V_DrawCenteredString ( BASEVIDWIDTH / 2 , 184 , V_YELLOWMAP | aflag , " * NEW RECORD * " ) ;
}
if ( P_HasGrades ( gamemap , stplyr - > lastmare + 1 ) )
{
2019-10-13 15:30:29 +00:00
UINT8 grade = P_GetGrade ( stplyr - > lastmarescore , gamemap , stplyr - > lastmare ) ;
if ( modeattacking | | grade > = GRADE_A )
V_DrawTranslucentPatch ( BASEVIDWIDTH / 2 + 60 , 160 , aflag , ngradeletters [ grade ] ) ;
2018-08-23 19:27:40 +00:00
}
break ;
2014-03-15 16:59:03 +00:00
}
2018-08-23 19:27:40 +00:00
default :
break ;
2014-03-15 16:59:03 +00:00
}
}
2017-10-31 15:47:58 +00:00
// 2.0-1: [21:42] <+Rob> Beige - Lavender - Steel Blue - Peach - Orange - Purple - Silver - Yellow - Pink - Red - Blue - Green - Cyan - Gold
/*#define NUMLINKCOLORS 14
2020-02-15 08:18:41 +00:00
static skincolornum_t linkColor [ NUMLINKCOLORS ] =
2017-10-31 15:47:58 +00:00
{ SKINCOLOR_BEIGE , SKINCOLOR_LAVENDER , SKINCOLOR_AZURE , SKINCOLOR_PEACH , SKINCOLOR_ORANGE ,
SKINCOLOR_MAGENTA , SKINCOLOR_SILVER , SKINCOLOR_SUPERGOLD4 , SKINCOLOR_PINK , SKINCOLOR_RED ,
SKINCOLOR_BLUE , SKINCOLOR_GREEN , SKINCOLOR_CYAN , SKINCOLOR_GOLD } ; */
// 2.2 indev list: (unix time 1470866042) <Rob> Emerald, Aqua, Cyan, Blue, Pastel, Purple, Magenta, Rosy, Red, Orange, Gold, Yellow, Peridot
/*#define NUMLINKCOLORS 13
2020-02-15 08:18:41 +00:00
static skincolornum_t linkColor [ NUMLINKCOLORS ] =
2017-10-31 15:47:58 +00:00
{ SKINCOLOR_EMERALD , SKINCOLOR_AQUA , SKINCOLOR_CYAN , SKINCOLOR_BLUE , SKINCOLOR_PASTEL ,
SKINCOLOR_PURPLE , SKINCOLOR_MAGENTA , SKINCOLOR_ROSY , SKINCOLOR_RED , SKINCOLOR_ORANGE ,
SKINCOLOR_GOLD , SKINCOLOR_YELLOW , SKINCOLOR_PERIDOT } ; */
// 2.2 indev list again: [19:59:52] <baldobo> Ruby > Red > Flame > Sunset > Orange > Gold > Yellow > Lime > Green > Aqua > cyan > Sky > Blue > Pastel > Purple > Bubblegum > Magenta > Rosy > repeat
// [20:00:25] <baldobo> Also Icy for the link freeze text color
// [20:04:03] <baldobo> I would start it on lime
/*#define NUMLINKCOLORS 18
2020-02-15 08:18:41 +00:00
static skincolornum_t linkColor [ NUMLINKCOLORS ] =
2017-10-31 15:47:58 +00:00
{ SKINCOLOR_LIME , SKINCOLOR_EMERALD , SKINCOLOR_AQUA , SKINCOLOR_CYAN , SKINCOLOR_SKY ,
SKINCOLOR_SAPPHIRE , SKINCOLOR_PASTEL , SKINCOLOR_PURPLE , SKINCOLOR_BUBBLEGUM , SKINCOLOR_MAGENTA ,
SKINCOLOR_ROSY , SKINCOLOR_RUBY , SKINCOLOR_RED , SKINCOLOR_FLAME , SKINCOLOR_SUNSET ,
SKINCOLOR_ORANGE , SKINCOLOR_GOLD , SKINCOLOR_YELLOW } ; */
// 2.2+ list for real this time: https://wiki.srb2.org/wiki/User:Rob/Sandbox (check history around 31/10/17, spoopy)
# define NUMLINKCOLORS 12
2020-02-15 08:18:41 +00:00
static skincolornum_t linkColor [ 2 ] [ NUMLINKCOLORS ] = {
2017-10-31 15:47:58 +00:00
{ SKINCOLOR_EMERALD , SKINCOLOR_AQUA , SKINCOLOR_SKY , SKINCOLOR_BLUE , SKINCOLOR_PURPLE , SKINCOLOR_MAGENTA ,
SKINCOLOR_ROSY , SKINCOLOR_RED , SKINCOLOR_ORANGE , SKINCOLOR_GOLD , SKINCOLOR_YELLOW , SKINCOLOR_PERIDOT } ,
{ SKINCOLOR_SEAFOAM , SKINCOLOR_CYAN , SKINCOLOR_WAVE , SKINCOLOR_SAPPHIRE , SKINCOLOR_VAPOR , SKINCOLOR_BUBBLEGUM ,
SKINCOLOR_VIOLET , SKINCOLOR_RUBY , SKINCOLOR_FLAME , SKINCOLOR_SUNSET , SKINCOLOR_SANDY , SKINCOLOR_LIME } } ;
2018-08-23 20:13:45 +00:00
static void ST_drawNiGHTSLink ( void )
{
static INT32 prevsel [ 2 ] = { 0 , 0 } , prevtime [ 2 ] = { 0 , 0 } ;
const UINT8 q = ( ( splitscreen & & stplyr = = & players [ secondarydisplayplayer ] ) ? 1 : 0 ) ;
INT32 sel = ( ( stplyr - > linkcount - 1 ) / 5 ) % NUMLINKCOLORS , aflag = V_PERPLAYER , mag = ( ( stplyr - > linkcount - 1 > = 300 ) ? 1 : 0 ) ;
2020-02-15 08:18:41 +00:00
skincolornum_t colornum ;
2018-08-23 20:13:45 +00:00
fixed_t x , y , scale ;
if ( sel ! = prevsel [ q ] )
{
prevsel [ q ] = sel ;
prevtime [ q ] = 2 + mag ;
}
if ( stplyr - > powers [ pw_nights_linkfreeze ] & & ( ! ( stplyr - > powers [ pw_nights_linkfreeze ] & 2 ) | | ( stplyr - > powers [ pw_nights_linkfreeze ] > flashingtics ) ) )
colornum = SKINCOLOR_ICY ;
else
colornum = linkColor [ mag ] [ sel ] ;
2018-11-21 02:54:20 +00:00
aflag | = ( ( stplyr - > linktimer < ( UINT32 ) nightslinktics / 3 )
2018-08-30 12:13:34 +00:00
? ( 9 - 9 * stplyr - > linktimer / ( nightslinktics / 3 ) ) < < V_ALPHASHIFT
2018-08-23 20:13:45 +00:00
: 0 ) ;
y = ( 160 + 11 ) < < FRACBITS ;
aflag | = V_SNAPTOBOTTOM ;
x = ( 160 + 4 ) < < FRACBITS ;
if ( prevtime [ q ] )
{
scale = ( ( 32 + prevtime [ q ] ) < < FRACBITS ) / 32 ;
prevtime [ q ] - - ;
}
else
scale = FRACUNIT ;
y - = ( 11 * scale ) ;
ST_DrawNightsOverlayNum ( x - ( 4 * scale ) , y , scale , aflag , ( stplyr - > linkcount - 1 ) , nightsnum , colornum ) ;
V_DrawFixedPatch ( x + ( 4 * scale ) , y , scale , aflag , nightslink ,
colornum = = 0 ? colormaps : R_GetTranslationColormap ( TC_DEFAULT , colornum , GTC_CACHE ) ) ;
// Show remaining link time left in debug
if ( cv_debug & DBG_NIGHTSBASIC )
V_DrawCenteredString ( BASEVIDWIDTH / 2 , 180 , V_SNAPTOBOTTOM , va ( " End in %d.%02d " , stplyr - > linktimer / TICRATE , G_TicsToCentiseconds ( stplyr - > linktimer ) ) ) ;
}
2014-03-15 16:59:03 +00:00
static void ST_drawNiGHTSHUD ( void )
{
INT32 origamount ;
2019-08-02 22:51:22 +00:00
INT32 total_spherecount , total_ringcount ;
2018-06-03 21:41:54 +00:00
const boolean oldspecialstage = ( G_IsSpecialStage ( gamemap ) & & ! ( maptol & TOL_NIGHTS ) ) ;
2014-03-15 16:59:03 +00:00
// Drill meter
2020-03-19 17:36:14 +00:00
if ( LUA_HudEnabled ( hud_nightsdrill ) & & stplyr - > powers [ pw_carry ] = = CR_NIGHTSMODE )
2014-03-15 16:59:03 +00:00
{
2018-03-30 15:39:59 +00:00
INT32 locx = 16 , locy = 180 ;
2014-03-15 16:59:03 +00:00
INT32 dfill ;
UINT8 fillpatch ;
// Use which patch?
if ( stplyr - > pflags & PF_DRILLING )
fillpatch = ( stplyr - > drillmeter & 1 ) + 1 ;
else
fillpatch = 0 ;
2018-01-20 22:14:24 +00:00
V_DrawScaledPatch ( locx , locy , V_PERPLAYER | V_SNAPTOLEFT | V_SNAPTOBOTTOM | V_HUDTRANS , drillbar ) ;
for ( dfill = 0 ; dfill < stplyr - > drillmeter / 20 & & dfill < 96 ; + + dfill )
V_DrawScaledPatch ( locx + 2 + dfill , locy + 3 , V_PERPLAYER | V_SNAPTOLEFT | V_SNAPTOBOTTOM | V_HUDTRANS , drillfill [ fillpatch ] ) ;
2014-03-15 16:59:03 +00:00
// Display actual drill amount and bumper time
2018-01-20 22:14:24 +00:00
if ( ! splitscreen & & ( cv_debug & DBG_NIGHTSBASIC ) )
2014-03-15 16:59:03 +00:00
{
if ( stplyr - > bumpertime )
2018-03-20 00:19:20 +00:00
V_DrawString ( locx , locy - 8 , V_REDMAP | V_MONOSPACE , va ( " BUMPER: 0.%02d " , G_TicsToCentiseconds ( stplyr - > bumpertime ) ) ) ;
2014-03-15 16:59:03 +00:00
else
2018-03-20 00:19:20 +00:00
V_DrawString ( locx , locy - 8 , V_MONOSPACE , va ( " Drill: %3d%% " , ( stplyr - > drillmeter * 100 ) / ( 96 * 20 ) ) ) ;
2014-03-15 16:59:03 +00:00
}
}
2018-03-30 15:39:59 +00:00
/*if (G_IsSpecialStage(gamemap))
2018-01-20 22:14:24 +00:00
{ // Since special stages share score, time, rings, etc.
// disable splitscreen mode for its HUD.
2018-03-30 15:39:59 +00:00
// --------------------------------------
// NOPE! Consistency between different splitscreen stuffs
// now we've got the screen squashing instead. ~toast
2018-01-20 22:14:24 +00:00
if ( stplyr ! = & players [ displayplayer ] )
return ;
nosshack = splitscreen ;
splitscreen = false ;
2018-03-30 15:39:59 +00:00
} */
2018-01-20 22:14:24 +00:00
2017-10-31 15:47:58 +00:00
// Link drawing
2018-08-23 20:13:45 +00:00
if ( ! oldspecialstage
// Don't display when the score is showing (it popping up for a split second when exiting a map is intentional)
& & ! ( stplyr - > texttimer & & stplyr - > textvar = = 4 )
& & LUA_HudEnabled ( hud_nightslink )
& & ( ( cv_debug & DBG_NIGHTSBASIC ) | | stplyr - > linkcount > 1 ) ) // When debugging, show "0 Link".
2017-10-31 15:47:58 +00:00
{
2018-08-23 20:13:45 +00:00
ST_drawNiGHTSLink ( ) ;
2017-10-31 15:47:58 +00:00
}
2019-12-18 04:54:45 +00:00
if ( gametyperules & GTR_RACE )
2014-03-15 16:59:03 +00:00
{
ST_drawScore ( ) ;
ST_drawTime ( ) ;
return ;
}
// Begin drawing brackets/chip display
2018-06-03 21:41:54 +00:00
if ( LUA_HudEnabled ( hud_nightsspheres ) )
2014-03-15 16:59:03 +00:00
{
2018-01-20 22:14:24 +00:00
ST_DrawTopLeftOverlayPatch ( 16 , 8 , nbracket ) ;
2014-03-15 16:59:03 +00:00
if ( G_IsSpecialStage ( gamemap ) )
2018-11-21 02:54:20 +00:00
ST_DrawTopLeftOverlayPatch ( 24 , 16 , (
2019-07-29 20:05:30 +00:00
( stplyr - > bonustime & & ( leveltime & 4 ) & & ( states [ S_BLUESPHEREBONUS ] . frame & FF_ANIMATE ) ) ? nssbon : nsshud ) ) ;
2014-03-15 16:59:03 +00:00
else
2018-06-07 14:10:43 +00:00
ST_DrawTopLeftOverlayPatch ( 24 , 16 , * ( ( ( stplyr - > bonustime ) ? nbon : nhud ) + ( ( leveltime / 2 ) % 12 ) ) ) ;
2014-03-15 16:59:03 +00:00
if ( G_IsSpecialStage ( gamemap ) )
{
INT32 i ;
2019-08-02 22:51:22 +00:00
total_spherecount = total_ringcount = 0 ;
2014-03-15 16:59:03 +00:00
for ( i = 0 ; i < MAXPLAYERS ; i + + )
2019-08-02 22:51:22 +00:00
{
if ( ! playeringame [ i ] )
continue ;
total_spherecount + = players [ i ] . spheres ;
total_ringcount + = players [ i ] . rings ;
}
2014-03-15 16:59:03 +00:00
}
else
2018-06-03 21:41:54 +00:00
{
2019-08-02 22:51:22 +00:00
total_spherecount = stplyr - > spheres ;
total_ringcount = stplyr - > spheres ;
}
2014-03-15 16:59:03 +00:00
if ( stplyr - > capsule )
{
INT32 amount ;
const INT32 length = 88 ;
origamount = stplyr - > capsule - > spawnpoint - > angle ;
I_Assert ( origamount > 0 ) ; // should not happen now
2018-01-20 22:14:24 +00:00
ST_DrawTopLeftOverlayPatch ( 72 , 8 , nbracket ) ;
ST_DrawTopLeftOverlayPatch ( 74 , 8 + 4 , minicaps ) ;
2014-03-15 16:59:03 +00:00
if ( stplyr - > capsule - > reactiontime ! = 0 )
{
INT32 r ;
const INT32 orblength = 20 ;
for ( r = 0 ; r < 5 ; r + + )
{
2018-03-20 00:19:20 +00:00
V_DrawScaledPatch ( 230 - ( 7 * r ) , 144 , V_PERPLAYER | V_HUDTRANS , redstat ) ;
V_DrawScaledPatch ( 188 - ( 7 * r ) , 144 , V_PERPLAYER | V_HUDTRANS , orngstat ) ;
V_DrawScaledPatch ( 146 - ( 7 * r ) , 144 , V_PERPLAYER | V_HUDTRANS , yelstat ) ;
V_DrawScaledPatch ( 104 - ( 7 * r ) , 144 , V_PERPLAYER | V_HUDTRANS , byelstat ) ;
2014-03-15 16:59:03 +00:00
}
amount = ( origamount - stplyr - > capsule - > health ) ;
amount = ( amount * orblength ) / origamount ;
if ( amount > 0 )
{
INT32 t ;
// Fill up the bar with blue orbs... in reverse! (yuck)
for ( r = amount ; r > 0 ; r - - )
{
t = r ;
if ( r > 15 ) + + t ;
if ( r > 10 ) + + t ;
if ( r > 5 ) + + t ;
2018-03-20 00:19:20 +00:00
V_DrawScaledPatch ( 69 + ( 7 * t ) , 144 , V_PERPLAYER | V_HUDTRANS , bluestat ) ;
2014-03-15 16:59:03 +00:00
}
}
}
else
{
INT32 cfill ;
// Lil' white box!
2018-01-20 22:14:24 +00:00
V_DrawScaledPatch ( 15 , 8 + 34 , V_PERPLAYER | V_SNAPTOLEFT | V_SNAPTOTOP | V_HUDTRANS , capsulebar ) ;
2014-03-15 16:59:03 +00:00
amount = ( origamount - stplyr - > capsule - > health ) ;
amount = ( amount * length ) / origamount ;
2018-06-03 21:41:54 +00:00
for ( cfill = 0 ; cfill < amount & & cfill < length ; + + cfill )
2018-01-20 22:14:24 +00:00
V_DrawScaledPatch ( 15 + cfill + 1 , 8 + 35 , V_PERPLAYER | V_SNAPTOLEFT | V_SNAPTOTOP | V_HUDTRANS , capsulefill ) ;
2014-03-15 16:59:03 +00:00
}
2018-06-03 21:41:54 +00:00
if ( total_spherecount > = stplyr - > capsule - > health )
ST_DrawTopLeftOverlayPatch ( 40 , 8 + 5 , nredar [ leveltime & 7 ] ) ;
2014-03-15 16:59:03 +00:00
else
2018-06-03 21:41:54 +00:00
ST_DrawTopLeftOverlayPatch ( 40 , 8 + 5 , narrow [ ( leveltime / 2 ) & 7 ] ) ;
}
2018-07-02 20:03:04 +00:00
else if ( oldspecialstage & & total_spherecount < ( INT32 ) ssspheres )
2018-06-03 21:41:54 +00:00
{
INT32 cfill , amount ;
const INT32 length = 88 ;
UINT8 em = P_GetNextEmerald ( ) ;
ST_DrawTopLeftOverlayPatch ( 72 , 8 , nbracket ) ;
if ( em < = 7 )
ST_DrawTopLeftOverlayPatch ( 80 , 8 + 8 , emeraldpics [ 0 ] [ em ] ) ;
ST_DrawTopLeftOverlayPatch ( 40 , 8 + 5 , narrow [ ( leveltime / 2 ) & 7 ] ) ;
// Lil' white box!
V_DrawScaledPatch ( 15 , 8 + 34 , V_PERPLAYER | V_SNAPTOLEFT | V_SNAPTOTOP | V_HUDTRANS , capsulebar ) ;
amount = ( total_spherecount * length ) / ssspheres ;
for ( cfill = 0 ; cfill < amount & & cfill < length ; + + cfill )
V_DrawScaledPatch ( 15 + cfill + 1 , 8 + 35 , V_PERPLAYER | V_SNAPTOLEFT | V_SNAPTOTOP | V_HUDTRANS , capsulefill ) ;
2014-03-15 16:59:03 +00:00
}
else
2018-01-20 22:14:24 +00:00
ST_DrawTopLeftOverlayPatch ( 40 , 8 + 5 , narrow [ 8 ] ) ;
2014-03-15 16:59:03 +00:00
2019-08-02 22:51:22 +00:00
if ( oldspecialstage )
{
// invert for s3k style junk
total_spherecount = ssspheres - total_spherecount ;
if ( total_spherecount < 0 )
total_spherecount = 0 ;
2019-08-03 11:25:46 +00:00
if ( nummaprings > 0 ) // don't count down if there ISN'T a valid maximum number of rings, like sonic 3
{
total_ringcount = nummaprings - total_ringcount ;
if ( total_ringcount < 0 )
total_ringcount = 0 ;
}
2019-08-02 22:51:22 +00:00
// now rings! you know, for that perfect bonus.
V_DrawScaledPatch ( 272 , 8 , V_PERPLAYER | V_SNAPTOTOP | V_SNAPTORIGHT | V_HUDTRANS , nbracket ) ;
V_DrawScaledPatch ( 280 , 16 + 1 , V_PERPLAYER | V_SNAPTOTOP | V_SNAPTORIGHT | V_HUDTRANS , nring ) ;
V_DrawScaledPatch ( 280 , 8 + 5 , V_FLIP | V_PERPLAYER | V_SNAPTOTOP | V_SNAPTORIGHT | V_HUDTRANS , narrow [ 8 ] ) ;
V_DrawTallNum ( 272 , 8 + 11 , V_PERPLAYER | V_SNAPTOTOP | V_SNAPTORIGHT | V_HUDTRANS , total_ringcount ) ;
}
2018-06-03 21:41:54 +00:00
if ( total_spherecount > = 100 )
V_DrawTallNum ( ( total_spherecount > = 1000 ) ? 76 : 72 , 8 + 11 , V_PERPLAYER | V_SNAPTOTOP | V_SNAPTOLEFT | V_HUDTRANS , total_spherecount ) ;
2014-03-15 16:59:03 +00:00
else
2018-06-03 21:41:54 +00:00
V_DrawTallNum ( 68 , 8 + 11 , V_PERPLAYER | V_SNAPTOTOP | V_SNAPTOLEFT | V_HUDTRANS , total_spherecount ) ;
2014-03-15 16:59:03 +00:00
}
// Score
2020-03-19 17:36:14 +00:00
if ( ! stplyr - > exiting & & ! oldspecialstage & & LUA_HudEnabled ( hud_nightsscore ) )
2018-03-30 15:39:59 +00:00
ST_DrawNightsOverlayNum ( 304 < < FRACBITS , 14 < < FRACBITS , FRACUNIT , V_PERPLAYER | V_SNAPTOTOP | V_SNAPTORIGHT , stplyr - > marescore , nightsnum , SKINCOLOR_AZURE ) ;
2014-03-25 02:17:59 +00:00
// TODO give this its own section for Lua
2020-03-19 17:36:14 +00:00
if ( ! stplyr - > exiting & & LUA_HudEnabled ( hud_nightsscore ) )
2014-03-25 02:17:59 +00:00
{
if ( modeattacking = = ATTACKING_NIGHTS )
{
INT32 maretime = max ( stplyr - > realtime - stplyr - > marebegunat , 0 ) ;
2018-03-20 00:19:20 +00:00
# define VFLAGS V_SNAPTOBOTTOM|V_SNAPTORIGHT|V_PERPLAYER|V_HUDTRANS
V_DrawScaledPatch ( BASEVIDWIDTH - 22 , BASEVIDHEIGHT - 20 , VFLAGS , W_CachePatchName ( " NGRTIMER " , PU_HUDGFX ) ) ;
V_DrawPaddedTallNum ( BASEVIDWIDTH - 22 , BASEVIDHEIGHT - 20 , VFLAGS , G_TicsToCentiseconds ( maretime ) , 2 ) ;
V_DrawScaledPatch ( BASEVIDWIDTH - 46 , BASEVIDHEIGHT - 20 , VFLAGS , sboperiod ) ;
2014-03-25 02:17:59 +00:00
if ( maretime < 60 * TICRATE )
2018-03-20 00:19:20 +00:00
V_DrawTallNum ( BASEVIDWIDTH - 46 , BASEVIDHEIGHT - 20 , VFLAGS , G_TicsToSeconds ( maretime ) ) ;
2014-03-25 02:17:59 +00:00
else
{
2018-03-20 00:19:20 +00:00
V_DrawPaddedTallNum ( BASEVIDWIDTH - 46 , BASEVIDHEIGHT - 20 , VFLAGS , G_TicsToSeconds ( maretime ) , 2 ) ;
V_DrawScaledPatch ( BASEVIDWIDTH - 70 , BASEVIDHEIGHT - 20 , VFLAGS , sbocolon ) ;
V_DrawTallNum ( BASEVIDWIDTH - 70 , BASEVIDHEIGHT - 20 , VFLAGS , G_TicsToMinutes ( maretime , true ) ) ;
2014-03-25 02:17:59 +00:00
}
2018-03-20 00:19:20 +00:00
# undef VFLAGS
2014-03-25 02:17:59 +00:00
}
}
2014-03-15 16:59:03 +00:00
// Ideya time remaining
2020-03-19 17:36:14 +00:00
if ( ! stplyr - > exiting & & stplyr - > nightstime > 0 & & LUA_HudEnabled ( hud_nightstime ) )
2014-03-15 16:59:03 +00:00
{
INT32 realnightstime = stplyr - > nightstime / TICRATE ;
INT32 numbersize ;
2018-06-03 21:41:54 +00:00
UINT8 col = ( ( realnightstime < 10 ) ? SKINCOLOR_RED : SKINCOLOR_SUPERGOLD4 ) ;
2014-03-15 16:59:03 +00:00
if ( G_IsSpecialStage ( gamemap ) )
{
tic_t lowest_time = stplyr - > nightstime ;
INT32 i ;
for ( i = 0 ; i < MAXPLAYERS ; i + + )
2017-03-14 18:11:17 +00:00
if ( playeringame [ i ] & & players [ i ] . powers [ pw_carry ] = = CR_NIGHTSMODE & & players [ i ] . nightstime < lowest_time )
2014-03-15 16:59:03 +00:00
lowest_time = players [ i ] . nightstime ;
realnightstime = lowest_time / TICRATE ;
}
2017-07-07 12:35:48 +00:00
if ( stplyr - > powers [ pw_flashing ] > TICRATE ) // was hit
2014-03-15 16:59:03 +00:00
{
UINT16 flashingLeft = stplyr - > powers [ pw_flashing ] - ( TICRATE ) ;
if ( flashingLeft < TICRATE / 2 ) // Start fading out
{
UINT32 fadingFlag = ( 9 - 9 * flashingLeft / ( TICRATE / 2 ) ) < < V_ALPHASHIFT ;
2018-03-20 00:19:20 +00:00
V_DrawTranslucentPatch ( 160 - ( minus5sec - > width / 2 ) , 28 , V_PERPLAYER | fadingFlag , minus5sec ) ;
2014-03-15 16:59:03 +00:00
}
else
2018-03-20 00:19:20 +00:00
V_DrawScaledPatch ( 160 - ( minus5sec - > width / 2 ) , 28 , V_PERPLAYER , minus5sec ) ;
2014-03-15 16:59:03 +00:00
}
if ( realnightstime < 10 )
2014-11-12 00:55:07 +00:00
numbersize = 16 / 2 ;
2014-03-15 16:59:03 +00:00
else if ( realnightstime < 100 )
2014-11-12 00:55:07 +00:00
numbersize = 32 / 2 ;
2014-03-15 16:59:03 +00:00
else
2014-11-12 00:55:07 +00:00
numbersize = 48 / 2 ;
2014-03-15 16:59:03 +00:00
2021-11-27 13:22:21 +00:00
if ( ( oldspecialstage & & leveltime & 2 ) & &
( stplyr - > mo - > eflags & ( MFE_TOUCHWATER | MFE_UNDERWATER ) & & ! ( stplyr - > powers [ pw_shield ] & ( ( stplyr - > mo - > eflags & MFE_TOUCHLAVA ) ? SH_PROTECTFIRE : SH_PROTECTWATER ) ) ) )
2018-06-03 21:41:54 +00:00
col = SKINCOLOR_ORANGE ;
ST_DrawNightsOverlayNum ( ( 160 + numbersize ) < < FRACBITS , 14 < < FRACBITS , FRACUNIT , V_PERPLAYER | V_SNAPTOTOP , realnightstime , nightsnum , col ) ;
2014-03-15 16:59:03 +00:00
// Show exact time in debug
if ( cv_debug & DBG_NIGHTSBASIC )
2014-11-12 00:55:07 +00:00
V_DrawString ( 160 + numbersize + 8 , 24 , V_SNAPTOTOP | ( ( realnightstime < 10 ) ? V_REDMAP : V_YELLOWMAP ) , va ( " %02d " , G_TicsToCentiseconds ( stplyr - > nightstime ) ) ) ;
2014-03-15 16:59:03 +00:00
}
2018-06-03 21:41:54 +00:00
if ( oldspecialstage )
2014-03-15 16:59:03 +00:00
{
2018-06-03 21:41:54 +00:00
if ( leveltime < 5 * TICRATE )
2014-03-15 16:59:03 +00:00
{
2018-06-03 21:41:54 +00:00
INT32 aflag = V_PERPLAYER ;
tic_t drawtime = ( 5 * TICRATE ) - leveltime ;
if ( drawtime < TICRATE / 2 )
aflag | = ( 9 - 9 * drawtime / ( TICRATE / 2 ) ) < < V_ALPHASHIFT ;
// This one, not quite as much so.
V_DrawCenteredString ( BASEVIDWIDTH / 2 , 60 , aflag ,
va ( M_GetText ( " \x80 GET \x82 %d \x80 SPHERE%s! " ) , ssspheres ,
( ssspheres > 1 ) ? " S " : " " ) ) ;
2014-03-15 16:59:03 +00:00
}
2018-06-03 21:41:54 +00:00
}
else
{
// Show pickup durations
if ( cv_debug & DBG_NIGHTSBASIC )
2014-03-15 16:59:03 +00:00
{
2018-06-03 21:41:54 +00:00
UINT16 pwr ;
2014-03-15 16:59:03 +00:00
2018-06-03 21:41:54 +00:00
if ( stplyr - > powers [ pw_nights_superloop ] )
{
pwr = stplyr - > powers [ pw_nights_superloop ] ;
2020-09-07 05:23:07 +00:00
V_DrawSmallScaledPatch ( 110 , 44 , 0 , W_CachePatchName ( " NPRUA0 " , PU_SPRITE ) ) ;
2018-06-03 21:41:54 +00:00
V_DrawThinString ( 106 , 52 , V_MONOSPACE , va ( " %2d.%02d " , pwr / TICRATE , G_TicsToCentiseconds ( pwr ) ) ) ;
}
if ( stplyr - > powers [ pw_nights_helper ] )
{
pwr = stplyr - > powers [ pw_nights_helper ] ;
2020-09-07 05:23:07 +00:00
V_DrawSmallScaledPatch ( 150 , 44 , 0 , W_CachePatchName ( " NPRUC0 " , PU_SPRITE ) ) ;
2018-06-03 21:41:54 +00:00
V_DrawThinString ( 146 , 52 , V_MONOSPACE , va ( " %2d.%02d " , pwr / TICRATE , G_TicsToCentiseconds ( pwr ) ) ) ;
}
if ( stplyr - > powers [ pw_nights_linkfreeze ] )
{
pwr = stplyr - > powers [ pw_nights_linkfreeze ] ;
2020-09-07 05:23:07 +00:00
V_DrawSmallScaledPatch ( 190 , 44 , 0 , W_CachePatchName ( " NPRUE0 " , PU_SPRITE ) ) ;
2018-06-03 21:41:54 +00:00
V_DrawThinString ( 186 , 52 , V_MONOSPACE , va ( " %2d.%02d " , pwr / TICRATE , G_TicsToCentiseconds ( pwr ) ) ) ;
}
2014-03-15 16:59:03 +00:00
}
2018-06-03 21:41:54 +00:00
// Records/extra text
if ( LUA_HudEnabled ( hud_nightsrecords ) )
2020-03-19 17:36:14 +00:00
ST_drawNightsRecords ( ) ;
2018-06-03 21:41:54 +00:00
}
2018-03-30 15:39:59 +00:00
}
2014-03-15 16:59:03 +00:00
2018-12-24 06:26:22 +00:00
static void ST_drawWeaponSelect ( INT32 xoffs , INT32 y )
2018-03-30 15:39:59 +00:00
{
INT32 q = stplyr - > weapondelay , del = 0 , p = 16 ;
while ( q )
{
if ( q > p )
{
del + = p ;
q - = p ;
q / = 2 ;
if ( p > 1 )
p / = 2 ;
}
else
{
del + = q ;
break ;
}
}
V_DrawScaledPatch ( 6 + xoffs , y - 2 - del / 2 , V_PERPLAYER | V_SNAPTOBOTTOM , curweapon ) ;
2014-03-15 16:59:03 +00:00
}
2017-12-09 23:08:12 +00:00
static void ST_drawWeaponRing ( powertype_t weapon , INT32 rwflag , INT32 wepflag , INT32 xoffs , INT32 y , patch_t * pat )
2014-03-15 16:59:03 +00:00
{
2014-03-21 18:42:55 +00:00
INT32 txtflags = 0 , patflags = 0 ;
2014-03-15 16:59:03 +00:00
if ( stplyr - > powers [ weapon ] )
{
if ( stplyr - > powers [ weapon ] > = rw_maximums [ wepflag ] )
2014-03-21 18:42:55 +00:00
txtflags | = V_YELLOWMAP ;
2014-03-15 16:59:03 +00:00
if ( weapon = = pw_infinityring
2015-05-27 05:13:26 +00:00
| | ( stplyr - > ringweapons & rwflag ) )
2018-03-26 22:53:09 +00:00
; //txtflags |= V_20TRANS;
2014-03-15 16:59:03 +00:00
else
2014-03-21 18:42:55 +00:00
{
txtflags | = V_TRANSLUCENT ;
patflags = V_80TRANS ;
}
2018-01-20 22:14:24 +00:00
V_DrawScaledPatch ( 8 + xoffs , y , V_PERPLAYER | V_SNAPTOBOTTOM | patflags , pat ) ;
2018-03-30 15:39:59 +00:00
V_DrawRightAlignedThinString ( 24 + xoffs , y + 8 , V_PERPLAYER | V_SNAPTOBOTTOM | txtflags , va ( " %d " , stplyr - > powers [ weapon ] ) ) ;
2014-03-15 16:59:03 +00:00
if ( stplyr - > currentweapon = = wepflag )
2018-03-30 15:39:59 +00:00
ST_drawWeaponSelect ( xoffs , y ) ;
2014-03-15 16:59:03 +00:00
}
else if ( stplyr - > ringweapons & rwflag )
2018-01-20 22:14:24 +00:00
V_DrawScaledPatch ( 8 + xoffs , y , V_PERPLAYER | V_SNAPTOBOTTOM | V_TRANSLUCENT , pat ) ;
2014-03-15 16:59:03 +00:00
}
static void ST_drawMatchHUD ( void )
{
2019-09-07 11:10:50 +00:00
char penaltystr [ 7 ] ;
2018-01-20 22:14:24 +00:00
const INT32 y = 176 ; // HUD_LIVES
2017-12-09 23:08:12 +00:00
INT32 offset = ( BASEVIDWIDTH / 2 ) - ( NUM_WEAPONS * 10 ) - 6 ;
2014-03-15 16:59:03 +00:00
2018-11-10 06:00:18 +00:00
if ( F_GetPromptHideHud ( y ) )
return ;
2014-03-15 16:59:03 +00:00
if ( ! G_RingSlingerGametype ( ) )
return ;
if ( G_TagGametype ( ) & & ! ( stplyr - > pflags & PF_TAGIT ) )
return ;
2018-01-20 22:14:24 +00:00
{
if ( stplyr - > powers [ pw_infinityring ] )
ST_drawWeaponRing ( pw_infinityring , 0 , 0 , offset , y , infinityring ) ;
else
2018-03-30 15:39:59 +00:00
{
if ( stplyr - > rings > 0 )
V_DrawScaledPatch ( 8 + offset , y , V_PERPLAYER | V_SNAPTOBOTTOM , normring ) ;
else
V_DrawTranslucentPatch ( 8 + offset , y , V_PERPLAYER | V_SNAPTOBOTTOM | V_80TRANS , normring ) ;
2018-01-20 22:14:24 +00:00
2018-03-30 15:39:59 +00:00
if ( ! stplyr - > currentweapon )
ST_drawWeaponSelect ( offset , y ) ;
}
2018-01-20 22:14:24 +00:00
2019-08-26 23:07:17 +00:00
ST_drawWeaponRing ( pw_automaticring , RW_AUTO , WEP_AUTO , offset + 20 , y , autoring ) ;
ST_drawWeaponRing ( pw_bouncering , RW_BOUNCE , WEP_BOUNCE , offset + 40 , y , bouncering ) ;
ST_drawWeaponRing ( pw_scatterring , RW_SCATTER , WEP_SCATTER , offset + 60 , y , scatterring ) ;
ST_drawWeaponRing ( pw_grenadering , RW_GRENADE , WEP_GRENADE , offset + 80 , y , grenadering ) ;
ST_drawWeaponRing ( pw_explosionring , RW_EXPLODE , WEP_EXPLODE , offset + 100 , y , explosionring ) ;
ST_drawWeaponRing ( pw_railring , RW_RAIL , WEP_RAIL , offset + 120 , y , railring ) ;
if ( stplyr - > ammoremovaltimer & & leveltime % 8 < 4 )
{
sprintf ( penaltystr , " -%d " , stplyr - > ammoremoval ) ;
V_DrawString ( offset + 8 + stplyr - > ammoremovalweapon * 20 , y ,
2021-11-09 15:59:49 +00:00
V_REDMAP | V_SNAPTOBOTTOM | V_PERPLAYER , penaltystr ) ;
2019-08-26 23:07:17 +00:00
}
2016-08-18 23:06:12 +00:00
}
2014-03-15 16:59:03 +00:00
}
2018-03-30 15:39:59 +00:00
static void ST_drawTextHUD ( void )
2014-03-15 16:59:03 +00:00
{
2019-06-18 17:51:24 +00:00
INT32 y = 42 + 16 ; // HUD_RINGS
boolean donef12 = false ;
2018-03-30 15:39:59 +00:00
# define textHUDdraw(str) \
{ \
2020-01-10 12:50:58 +00:00
V_DrawThinString ( 16 , y , V_PERPLAYER | V_HUDTRANS | V_SNAPTOLEFT | V_SNAPTOTOP , str ) ; \
2019-06-18 17:51:24 +00:00
y + = 8 ; \
2014-03-15 16:59:03 +00:00
}
2018-11-10 06:00:18 +00:00
if ( F_GetPromptHideHud ( y ) )
return ;
2020-02-29 03:57:22 +00:00
if ( stplyr - > spectator & & ( ! G_CoopGametype ( ) | | stplyr - > playerstate = = PST_LIVE ) )
2019-06-18 17:51:24 +00:00
textHUDdraw ( M_GetText ( " \x86 " " Spectator mode: " ) )
if ( circuitmap )
2018-03-30 15:39:59 +00:00
{
2019-06-18 17:51:24 +00:00
if ( stplyr - > exiting )
textHUDdraw ( M_GetText ( " \x82 " " FINISHED! " ) )
else
textHUDdraw ( va ( " Lap: " " \x82 %u/%d " , stplyr - > laps + 1 , cv_numlaps . value ) )
2018-03-30 15:39:59 +00:00
}
2014-03-15 16:59:03 +00:00
2020-02-29 03:57:22 +00:00
if ( ! G_CoopGametype ( ) & & ( stplyr - > exiting | | ( G_GametypeUsesLives ( ) & & stplyr - > lives < = 0 & & countdown ! = 1 ) ) )
2019-06-18 17:51:24 +00:00
{
if ( ! splitscreen & & ! donef12 )
{
textHUDdraw ( M_GetText ( " \x82 " " VIEWPOINT: " " \x80 Switch view " ) )
donef12 = true ;
}
}
2019-12-27 04:44:27 +00:00
else if ( ( gametyperules & GTR_RESPAWNDELAY ) & & stplyr - > playerstate = = PST_DEAD & & stplyr - > lives ) // Death overrides spectator text.
2018-03-30 15:39:59 +00:00
{
INT32 respawntime = cv_respawntime . value - stplyr - > deadtimer / TICRATE ;
if ( respawntime > 0 & & ! stplyr - > spectator )
textHUDdraw ( va ( M_GetText ( " Respawn in %d... " ) , respawntime ) )
else
textHUDdraw ( M_GetText ( " \x82 " " JUMP: " " \x80 Respawn " ) )
}
2020-02-29 03:57:22 +00:00
else if ( stplyr - > spectator & & ( ! G_CoopGametype ( ) | | stplyr - > playerstate = = PST_LIVE ) )
2018-03-30 15:39:59 +00:00
{
2019-06-18 17:51:24 +00:00
if ( ! splitscreen & & ! donef12 )
{
textHUDdraw ( M_GetText ( " \x82 " " VIEWPOINT: " " \x80 Switch view " ) )
donef12 = true ;
}
textHUDdraw ( M_GetText ( " \x82 " " JUMP: " " \x80 Rise " ) )
textHUDdraw ( M_GetText ( " \x82 " " SPIN: " " \x80 Lower " ) )
2019-09-04 14:51:14 +00:00
if ( G_IsSpecialStage ( gamemap ) )
2018-03-30 15:39:59 +00:00
textHUDdraw ( M_GetText ( " \x82 " " Wait for the stage to end... " ) )
2019-12-18 15:28:01 +00:00
else if ( G_PlatformGametype ( ) )
2014-03-15 16:59:03 +00:00
{
2019-12-27 20:08:20 +00:00
if ( G_GametypeUsesCoopLives ( ) )
2018-03-30 15:39:59 +00:00
{
2019-12-18 18:14:24 +00:00
if ( stplyr - > lives < = 0
& & cv_cooplives . value = = 2
& & ( netgame | | multiplayer ) )
2018-03-30 15:39:59 +00:00
{
2019-12-18 18:14:24 +00:00
INT32 i ;
for ( i = 0 ; i < MAXPLAYERS ; i + + )
{
if ( ! playeringame [ i ] )
continue ;
if ( & players [ i ] = = stplyr )
continue ;
if ( players [ i ] . lives > 1 )
break ;
}
if ( i ! = MAXPLAYERS )
textHUDdraw ( M_GetText ( " You'll steal a life on respawn... " ) )
else
textHUDdraw ( M_GetText ( " Wait to respawn... " ) )
}
2018-03-30 15:39:59 +00:00
else
textHUDdraw ( M_GetText ( " Wait to respawn... " ) )
}
2014-03-15 16:59:03 +00:00
}
2019-12-18 15:28:01 +00:00
else if ( G_GametypeHasSpectators ( ) )
2018-03-30 15:39:59 +00:00
textHUDdraw ( M_GetText ( " \x82 " " FIRE: " " \x80 Enter game " ) )
}
2020-02-29 03:57:22 +00:00
if ( G_CoopGametype ( ) & & ( ! stplyr - > spectator | | ( ! ( maptol & TOL_NIGHTS ) & & G_IsSpecialStage ( gamemap ) ) ) & & ( stplyr - > exiting | | ( stplyr - > pflags & PF_FINISHED ) ) )
2019-09-04 14:51:14 +00:00
{
2019-09-19 17:44:55 +00:00
UINT8 numneeded = ( G_IsSpecialStage ( gamemap ) ? 4 : cv_playersforexit . value ) ;
if ( numneeded )
2019-09-04 14:51:14 +00:00
{
2019-09-19 17:44:55 +00:00
INT32 i , total = 0 , exiting = 0 ;
2019-09-04 14:51:14 +00:00
2019-09-19 17:44:55 +00:00
for ( i = 0 ; i < MAXPLAYERS ; i + + )
{
2021-12-29 09:59:54 +00:00
if ( ! playeringame [ i ] | | players [ i ] . spectator | | players [ i ] . bot )
2019-09-19 17:44:55 +00:00
continue ;
if ( players [ i ] . lives < = 0 )
continue ;
2019-09-04 14:51:14 +00:00
2019-09-19 17:44:55 +00:00
total + + ;
2019-11-18 02:27:39 +00:00
if ( players [ i ] . exiting | | ( players [ i ] . pflags & PF_FINISHED ) )
2019-09-19 17:44:55 +00:00
exiting + + ;
}
2019-09-04 14:51:14 +00:00
2019-09-19 17:44:55 +00:00
if ( numneeded ! = 4 )
2019-09-04 14:51:14 +00:00
{
2019-09-19 17:44:55 +00:00
total * = cv_playersforexit . value ;
if ( total & 3 )
total + = 4 ; // round up
total / = 4 ;
}
if ( exiting < total )
{
if ( ! splitscreen & & ! donef12 )
{
textHUDdraw ( M_GetText ( " \x82 " " VIEWPOINT: " " \x80 Switch view " ) )
donef12 = true ;
}
total - = exiting ;
textHUDdraw ( va ( M_GetText ( " %d player%s remaining " ) , total , ( ( total = = 1 ) ? " " : " s " ) ) )
2019-09-04 14:51:14 +00:00
}
}
}
2019-12-18 04:54:45 +00:00
else if ( ( gametyperules & GTR_TAG ) & & ( ! stplyr - > spectator ) )
2018-03-30 15:39:59 +00:00
{
2019-06-18 17:51:24 +00:00
if ( leveltime < hidetime * TICRATE )
{
if ( stplyr - > pflags & PF_TAGIT )
{
2019-12-18 14:43:50 +00:00
if ( gametyperules & GTR_BLINDFOLDED )
textHUDdraw ( M_GetText ( " \x82 " " You are blindfolded! " ) )
2019-06-18 17:51:24 +00:00
textHUDdraw ( M_GetText ( " Waiting for players to hide... " ) )
}
2020-02-23 20:02:50 +00:00
else if ( gametyperules & GTR_HIDEFROZEN )
2019-06-18 17:51:24 +00:00
textHUDdraw ( M_GetText ( " Hide before time runs out! " ) )
else
textHUDdraw ( M_GetText ( " Flee before you are hunted! " ) )
}
2020-02-29 02:52:33 +00:00
else if ( ( gametyperules & GTR_HIDEFROZEN ) & & ! ( stplyr - > pflags & PF_TAGIT ) )
2019-06-18 17:51:24 +00:00
{
if ( ! splitscreen & & ! donef12 )
{
textHUDdraw ( M_GetText ( " \x82 " " VIEWPOINT: " " \x80 Switch view " ) )
donef12 = true ;
}
2020-02-29 02:52:33 +00:00
textHUDdraw ( M_GetText ( " You cannot move while hiding. " ) )
2019-06-18 17:51:24 +00:00
}
2018-03-30 15:39:59 +00:00
}
2014-03-15 16:59:03 +00:00
2018-03-30 15:39:59 +00:00
# undef textHUDdraw
}
static inline void ST_drawRaceHUD ( void )
{
if ( leveltime > TICRATE & & leveltime < = 5 * TICRATE )
ST_drawRaceNum ( 4 * TICRATE - leveltime ) ;
2014-03-15 16:59:03 +00:00
}
2018-03-26 22:53:09 +00:00
static void ST_drawTeamHUD ( void )
2014-03-15 16:59:03 +00:00
{
2018-03-26 22:53:09 +00:00
# define SEP 20
2014-03-15 16:59:03 +00:00
2018-11-10 06:00:18 +00:00
if ( F_GetPromptHideHud ( 0 ) ) // y base is 0
return ;
2021-04-07 09:57:18 +00:00
rflagico = W_CachePatchName ( " RFLAGICO " , PU_HUDGFX ) ;
bflagico = W_CachePatchName ( " BFLAGICO " , PU_HUDGFX ) ;
rmatcico = W_CachePatchName ( " RMATCICO " , PU_HUDGFX ) ;
bmatcico = W_CachePatchName ( " BMATCICO " , PU_HUDGFX ) ;
2014-03-15 16:59:03 +00:00
2019-12-16 20:58:00 +00:00
if ( LUA_HudEnabled ( hud_teamscores ) )
2021-04-07 09:57:18 +00:00
{
if ( gametyperules & GTR_TEAMFLAGS )
{
V_DrawSmallScaledPatch ( BASEVIDWIDTH / 2 - SEP - ( bflagico - > width / 4 ) , 4 , V_HUDTRANS | V_PERPLAYER | V_SNAPTOTOP , bflagico ) ;
V_DrawSmallScaledPatch ( BASEVIDWIDTH / 2 + SEP - ( rflagico - > width / 4 ) , 4 , V_HUDTRANS | V_PERPLAYER | V_SNAPTOTOP , rflagico ) ;
}
else
{
V_DrawSmallScaledPatch ( BASEVIDWIDTH / 2 - SEP - ( bmatcico - > width / 4 ) , 4 , V_HUDTRANS | V_PERPLAYER | V_SNAPTOTOP , bmatcico ) ;
V_DrawSmallScaledPatch ( BASEVIDWIDTH / 2 + SEP - ( rmatcico - > width / 4 ) , 4 , V_HUDTRANS | V_PERPLAYER | V_SNAPTOTOP , rmatcico ) ;
}
}
2014-03-15 16:59:03 +00:00
2019-12-18 14:43:50 +00:00
if ( ! ( gametyperules & GTR_TEAMFLAGS ) )
2018-03-26 22:53:09 +00:00
goto num ;
2014-03-15 16:59:03 +00:00
{
2018-03-26 22:53:09 +00:00
INT32 i ;
UINT16 whichflag = 0 ;
// Show which flags aren't at base.
for ( i = 0 ; i < MAXPLAYERS ; i + + )
2014-03-15 16:59:03 +00:00
{
2020-03-19 17:36:14 +00:00
// Blue flag isn't at base
if ( players [ i ] . gotflag & GF_BLUEFLAG & & LUA_HudEnabled ( hud_teamscores ) )
2020-11-22 23:02:47 +00:00
V_DrawScaledPatch ( BASEVIDWIDTH / 2 - SEP - ( nonicon - > width / 2 ) , 0 , V_HUDTRANS | V_PERPLAYER | V_SNAPTOTOP , nonicon ) ;
2019-12-16 20:58:00 +00:00
2020-03-19 17:36:14 +00:00
// Red flag isn't at base
if ( players [ i ] . gotflag & GF_REDFLAG & & LUA_HudEnabled ( hud_teamscores ) )
2020-11-22 23:02:47 +00:00
V_DrawScaledPatch ( BASEVIDWIDTH / 2 + SEP - ( nonicon2 - > width / 2 ) , 0 , V_HUDTRANS | V_PERPLAYER | V_SNAPTOTOP , nonicon2 ) ;
2018-03-26 22:53:09 +00:00
whichflag | = players [ i ] . gotflag ;
2019-12-16 20:58:00 +00:00
2018-03-26 22:53:09 +00:00
if ( ( whichflag & ( GF_REDFLAG | GF_BLUEFLAG ) ) = = ( GF_REDFLAG | GF_BLUEFLAG ) )
break ; // both flags were found, let's stop early
2014-03-15 16:59:03 +00:00
}
2018-03-26 22:53:09 +00:00
// Display a countdown timer showing how much time left until the flag returns to base.
2014-03-15 16:59:03 +00:00
{
2020-03-19 17:36:14 +00:00
if ( blueflag & & blueflag - > fuse > 1 & & LUA_HudEnabled ( hud_teamscores ) )
2018-03-26 22:53:09 +00:00
V_DrawCenteredString ( BASEVIDWIDTH / 2 - SEP , 8 , V_YELLOWMAP | V_HUDTRANS | V_PERPLAYER | V_SNAPTOTOP , va ( " %u " , ( blueflag - > fuse / TICRATE ) ) ) ;
2020-03-19 17:36:14 +00:00
if ( redflag & & redflag - > fuse > 1 & & LUA_HudEnabled ( hud_teamscores ) )
2018-03-26 22:53:09 +00:00
V_DrawCenteredString ( BASEVIDWIDTH / 2 + SEP , 8 , V_YELLOWMAP | V_HUDTRANS | V_PERPLAYER | V_SNAPTOTOP , va ( " %u " , ( redflag - > fuse / TICRATE ) ) ) ;
2014-03-15 16:59:03 +00:00
}
}
2018-03-26 22:53:09 +00:00
num :
2019-12-16 20:58:00 +00:00
if ( LUA_HudEnabled ( hud_teamscores ) )
2020-03-19 17:36:14 +00:00
V_DrawCenteredString ( BASEVIDWIDTH / 2 - SEP , 16 , V_HUDTRANS | V_PERPLAYER | V_SNAPTOTOP , va ( " %u " , bluescore ) ) ;
2019-12-16 20:58:00 +00:00
if ( LUA_HudEnabled ( hud_teamscores ) )
2020-03-19 17:36:14 +00:00
V_DrawCenteredString ( BASEVIDWIDTH / 2 + SEP , 16 , V_HUDTRANS | V_PERPLAYER | V_SNAPTOTOP , va ( " %u " , redscore ) ) ;
2018-03-26 22:53:09 +00:00
# undef SEP
2014-03-15 16:59:03 +00:00
}
2018-06-03 21:41:54 +00:00
/*static void ST_drawSpecialStageHUD(void)
2014-03-15 16:59:03 +00:00
{
2018-06-03 21:41:54 +00:00
if ( ssspheres > 0 )
2018-03-19 23:08:51 +00:00
{
if ( hudinfo [ HUD_SS_TOTALRINGS ] . x )
2018-06-03 21:41:54 +00:00
ST_DrawNumFromHud ( HUD_SS_TOTALRINGS , ssspheres , V_HUDTRANS ) ;
2018-03-26 22:53:09 +00:00
else if ( cv_timetic . value = = 2 )
2018-06-03 21:41:54 +00:00
V_DrawTallNum ( hudinfo [ HUD_RINGSNUMTICS ] . x , hudinfo [ HUD_SS_TOTALRINGS ] . y , hudinfo [ HUD_RINGSNUMTICS ] . f | V_PERPLAYER | V_HUDTRANS , ssspheres ) ;
2018-03-19 23:08:51 +00:00
else
2018-06-03 21:41:54 +00:00
V_DrawTallNum ( hudinfo [ HUD_RINGSNUM ] . x , hudinfo [ HUD_SS_TOTALRINGS ] . y , hudinfo [ HUD_RINGSNUM ] . f | V_PERPLAYER | V_HUDTRANS , ssspheres ) ;
2018-03-19 23:08:51 +00:00
}
2014-03-15 16:59:03 +00:00
2018-06-03 21:41:54 +00:00
if ( leveltime < 5 * TICRATE & & ssspheres > 0 )
2014-03-15 16:59:03 +00:00
{
2017-07-03 15:33:52 +00:00
ST_DrawPatchFromHud ( HUD_GETRINGS , getall , V_HUDTRANS ) ;
2018-06-03 21:41:54 +00:00
ST_DrawNumFromHud ( HUD_GETRINGSNUM , ssspheres , V_HUDTRANS ) ;
2014-03-15 16:59:03 +00:00
}
if ( sstimer )
{
2018-03-19 23:08:51 +00:00
V_DrawString ( hudinfo [ HUD_TIMELEFT ] . x , hudinfo [ HUD_TIMELEFT ] . y , hudinfo [ HUD_TIMELEFT ] . f | V_PERPLAYER | V_HUDTRANS , M_GetText ( " TIME LEFT " ) ) ;
2017-07-03 15:33:52 +00:00
ST_DrawNumFromHud ( HUD_TIMELEFTNUM , sstimer / TICRATE , V_HUDTRANS ) ;
2014-03-15 16:59:03 +00:00
}
else
2017-07-03 15:33:52 +00:00
ST_DrawPatchFromHud ( HUD_TIMEUP , timeup , V_HUDTRANS ) ;
2018-06-03 21:41:54 +00:00
} */
2014-03-15 16:59:03 +00:00
static INT32 ST_drawEmeraldHuntIcon ( mobj_t * hunt , patch_t * * patches , INT32 offset )
{
INT32 interval , i ;
2021-02-13 16:45:20 +00:00
UINT32 dist = ( ( UINT32 ) P_AproxDistance ( P_AproxDistance ( stplyr - > mo - > x - hunt - > x , stplyr - > mo - > y - hunt - > y ) , stplyr - > mo - > z - hunt - > z ) ) > > FRACBITS ;
2014-03-15 16:59:03 +00:00
if ( dist < 128 )
{
i = 5 ;
interval = 5 ;
}
else if ( dist < 512 )
{
i = 4 ;
interval = 10 ;
}
else if ( dist < 1024 )
{
i = 3 ;
interval = 20 ;
}
else if ( dist < 2048 )
{
i = 2 ;
interval = 30 ;
}
else if ( dist < 3072 )
{
i = 1 ;
interval = 35 ;
}
else
{
i = 0 ;
interval = 0 ;
}
2018-11-10 06:00:18 +00:00
if ( ! F_GetPromptHideHud ( hudinfo [ HUD_HUNTPICS ] . y ) )
V_DrawScaledPatch ( hudinfo [ HUD_HUNTPICS ] . x + offset , hudinfo [ HUD_HUNTPICS ] . y , hudinfo [ HUD_HUNTPICS ] . f | V_PERPLAYER | V_HUDTRANS , patches [ i ] ) ;
2014-03-15 16:59:03 +00:00
return interval ;
}
// Separated a few things to stop the SOUND EFFECTS BLARING UGH SHUT UP AAAA
static void ST_doHuntIconsAndSound ( void )
{
INT32 interval = 0 , newinterval = 0 ;
if ( hunt1 & & hunt1 - > health )
interval = ST_drawEmeraldHuntIcon ( hunt1 , hunthoming , - 20 ) ;
if ( hunt2 & & hunt2 - > health )
{
newinterval = ST_drawEmeraldHuntIcon ( hunt2 , hunthoming , 0 ) ;
if ( newinterval & & ( ! interval | | newinterval < interval ) )
interval = newinterval ;
}
if ( hunt3 & & hunt3 - > health )
{
newinterval = ST_drawEmeraldHuntIcon ( hunt3 , hunthoming , 20 ) ;
if ( newinterval & & ( ! interval | | newinterval < interval ) )
interval = newinterval ;
}
2015-01-01 19:50:31 +00:00
if ( ! ( P_AutoPause ( ) | | paused ) & & interval > 0 & & leveltime & & leveltime % interval = = 0 )
2014-03-15 16:59:03 +00:00
S_StartSound ( NULL , sfx_emfind ) ;
}
static void ST_doItemFinderIconsAndSound ( void )
{
INT32 emblems [ 16 ] ;
thinker_t * th ;
mobj_t * mo2 ;
UINT8 stemblems = 0 , stunfound = 0 ;
INT32 i ;
INT32 interval = 0 , newinterval = 0 ;
INT32 soffset ;
for ( i = 0 ; i < numemblems ; + + i )
{
if ( emblemlocations [ i ] . type > ET_SKIN | | emblemlocations [ i ] . level ! = gamemap )
continue ;
emblems [ stemblems + + ] = i ;
if ( ! emblemlocations [ i ] . collected )
+ + stunfound ;
if ( stemblems > = 16 )
break ;
}
// Found all/none exist? Don't waste our time
if ( ! stunfound )
return ;
// Scan thinkers to find emblem mobj with these ids
2019-04-20 21:29:20 +00:00
for ( th = thlist [ THINK_MOBJ ] . next ; th ! = & thlist [ THINK_MOBJ ] ; th = th - > next )
2014-03-15 16:59:03 +00:00
{
2019-07-12 23:42:03 +00:00
if ( th - > function . acp1 = = ( actionf_p1 ) P_RemoveThinkerDelayed )
continue ;
2014-03-15 16:59:03 +00:00
mo2 = ( mobj_t * ) th ;
2019-07-12 23:42:03 +00:00
if ( mo2 - > type ! = MT_EMBLEM )
continue ;
if ( ! ( mo2 - > flags & MF_SPECIAL ) )
continue ;
2014-03-15 16:59:03 +00:00
2019-07-12 23:42:03 +00:00
for ( i = 0 ; i < stemblems ; + + i )
{
if ( mo2 - > health = = emblems [ i ] + 1 )
2014-03-15 16:59:03 +00:00
{
2019-07-12 23:42:03 +00:00
soffset = ( i * 20 ) - ( ( stemblems - 1 ) * 10 ) ;
2014-03-15 16:59:03 +00:00
2019-07-12 23:42:03 +00:00
newinterval = ST_drawEmeraldHuntIcon ( mo2 , itemhoming , soffset ) ;
if ( newinterval & & ( ! interval | | newinterval < interval ) )
interval = newinterval ;
2014-03-15 16:59:03 +00:00
2019-07-12 23:42:03 +00:00
break ;
2014-03-15 16:59:03 +00:00
}
}
2019-07-12 23:42:03 +00:00
2014-03-15 16:59:03 +00:00
}
2015-01-01 19:50:31 +00:00
if ( ! ( P_AutoPause ( ) | | paused ) & & interval > 0 & & leveltime & & leveltime % interval = = 0 )
2014-03-15 16:59:03 +00:00
S_StartSound ( NULL , sfx_emfind ) ;
}
2019-11-18 14:39:54 +00:00
//
2014-03-15 16:59:03 +00:00
// Draw the status bar overlay, customisable: the user chooses which
// kind of information to overlay
//
static void ST_overlayDrawer ( void )
{
2019-11-18 14:39:54 +00:00
// Decide whether to draw the stage title or not
boolean stagetitle = false ;
// Check for a valid level title
// If the HUD is enabled
// And, if Lua is running, if the HUD library has the stage title enabled
2020-02-09 23:53:13 +00:00
if ( G_IsTitleCardAvailable ( ) & & * mapheaderinfo [ gamemap - 1 ] - > lvlttl ! = ' \0 ' & & ! ( hu_showscores & & ( netgame | | multiplayer ) ) )
2019-11-18 14:39:54 +00:00
{
stagetitle = true ;
ST_preDrawTitleCard ( ) ;
}
// hu_showscores = auto hide score/time/rings when tab rankings are shown
2014-03-15 16:59:03 +00:00
if ( ! ( hu_showscores & & ( netgame | | multiplayer ) ) )
{
2018-11-10 06:00:18 +00:00
if ( ( maptol & TOL_NIGHTS | | G_IsSpecialStage ( gamemap ) ) & &
! F_GetPromptHideHudAll ( ) )
2014-03-15 16:59:03 +00:00
ST_drawNiGHTSHUD ( ) ;
else
{
if ( LUA_HudEnabled ( hud_score ) )
2020-03-19 17:36:14 +00:00
ST_drawScore ( ) ;
2014-03-15 16:59:03 +00:00
if ( LUA_HudEnabled ( hud_time ) )
2020-03-19 17:36:14 +00:00
ST_drawTime ( ) ;
2014-03-15 16:59:03 +00:00
if ( LUA_HudEnabled ( hud_rings ) )
2020-03-19 17:36:14 +00:00
ST_drawRings ( ) ;
2017-12-09 23:08:12 +00:00
2020-03-19 17:36:14 +00:00
if ( ! modeattacking & & LUA_HudEnabled ( hud_lives ) )
2018-01-20 22:14:24 +00:00
ST_drawLivesArea ( ) ;
2014-03-15 16:59:03 +00:00
}
}
2019-09-01 14:55:23 +00:00
// GAME OVER hud
2020-02-29 03:57:22 +00:00
if ( G_GametypeUsesCoopLives ( )
2017-07-04 12:17:29 +00:00
& & ( netgame | | multiplayer )
& & ( cv_cooplives . value = = 0 ) )
;
2020-02-29 02:47:38 +00:00
else if ( ( G_GametypeUsesLives ( ) | | ( ( gametyperules & ( GTR_RACE | GTR_LIVES ) ) = = GTR_RACE ) ) & & stplyr - > lives < = 0 & & ! ( hu_showscores & & ( netgame | | multiplayer ) ) )
2014-03-15 16:59:03 +00:00
{
2019-09-01 14:55:23 +00:00
INT32 i = MAXPLAYERS ;
INT32 deadtimer = stplyr - > spectator ? TICRATE : ( stplyr - > deadtimer - ( TICRATE < < 1 ) ) ;
2014-03-15 16:59:03 +00:00
2019-12-27 20:08:20 +00:00
if ( G_GametypeUsesCoopLives ( )
2017-07-04 12:17:29 +00:00
& & ( netgame | | multiplayer )
& & ( cv_cooplives . value ! = 1 ) )
2017-05-28 20:55:41 +00:00
{
for ( i = 0 ; i < MAXPLAYERS ; i + + )
{
if ( ! playeringame [ i ] )
continue ;
if ( & players [ i ] = = stplyr )
continue ;
2017-06-03 11:26:42 +00:00
if ( players [ i ] . lives > 0 )
2017-05-28 20:55:41 +00:00
break ;
}
}
2017-06-03 11:26:42 +00:00
2019-09-01 14:55:23 +00:00
if ( i = = MAXPLAYERS & & deadtimer > = 0 )
{
2019-09-04 14:21:00 +00:00
INT32 lvlttlx = min ( 6 * deadtimer , BASEVIDWIDTH / 2 ) ;
2019-09-01 14:55:23 +00:00
UINT32 flags = V_PERPLAYER | ( stplyr - > spectator ? V_HUDTRANSHALF : V_HUDTRANS ) ;
2019-09-04 14:21:00 +00:00
V_DrawScaledPatch ( lvlttlx - 8 , BASEVIDHEIGHT / 2 , flags , ( countdown = = 1 ? slidtime : slidgame ) ) ;
V_DrawScaledPatch ( BASEVIDWIDTH + 8 - lvlttlx , BASEVIDHEIGHT / 2 , flags , slidover ) ;
2019-09-01 14:55:23 +00:00
}
2014-03-15 16:59:03 +00:00
}
2018-03-26 22:53:09 +00:00
if ( G_GametypeHasTeams ( ) )
ST_drawTeamHUD ( ) ;
2014-03-15 16:59:03 +00:00
if ( ! hu_showscores ) // hide the following if TAB is held
{
// Countdown timer for Race Mode
2018-03-21 18:18:45 +00:00
if ( countdown > 1 )
{
tic_t time = countdown / TICRATE + 1 ;
if ( time < 4 )
ST_drawRaceNum ( countdown ) ;
else
{
tic_t num = time ;
2020-11-22 23:02:47 +00:00
INT32 sz = tallnum [ 0 ] - > width / 2 , width = 0 ;
2018-03-21 18:18:45 +00:00
do
{
width + = sz ;
num / = 10 ;
} while ( num ) ;
V_DrawTallNum ( ( BASEVIDWIDTH / 2 ) + width , ( ( 3 * BASEVIDHEIGHT ) > > 2 ) - 7 , V_PERPLAYER , time ) ;
//V_DrawCenteredString(BASEVIDWIDTH/2, 176, V_PERPLAYER, va("%d", countdown/TICRATE + 1));
}
}
2014-03-15 16:59:03 +00:00
// If you are in overtime, put a big honkin' flashin' message on the screen.
2019-12-18 22:26:45 +00:00
if ( ( ( gametyperules & GTR_TIMELIMIT ) & & ( gametyperules & GTR_OVERTIME ) ) & & cv_overtime . value
2018-01-20 22:14:24 +00:00
& & ( leveltime > ( timelimitintics + TICRATE / 2 ) ) & & cv_timelimit . value & & ( leveltime / TICRATE % 2 = = 0 ) )
V_DrawCenteredString ( BASEVIDWIDTH / 2 , 184 , V_PERPLAYER , M_GetText ( " OVERTIME! " ) ) ;
2014-03-15 16:59:03 +00:00
// Draw Match-related stuff
//\note Match HUD is drawn no matter what gametype.
// ... just not if you're a spectator.
2020-03-19 17:36:14 +00:00
if ( ! stplyr - > spectator & & LUA_HudEnabled ( hud_weaponrings ) )
2014-03-15 16:59:03 +00:00
ST_drawMatchHUD ( ) ;
// Race HUD Stuff
2019-12-18 04:54:45 +00:00
if ( gametyperules & GTR_RACE )
2014-03-15 16:59:03 +00:00
ST_drawRaceHUD ( ) ;
// Emerald Hunt Indicators
if ( cv_itemfinder . value & & M_SecretUnlocked ( SECRET_ITEMFINDER ) )
ST_doItemFinderIconsAndSound ( ) ;
else
ST_doHuntIconsAndSound ( ) ;
if ( ! P_IsLocalPlayer ( stplyr ) )
{
char name [ MAXPLAYERNAME + 1 ] ;
// shorten the name if its more than twelve characters.
strlcpy ( name , player_names [ stplyr - players ] , 13 ) ;
// Show name of player being displayed
V_DrawCenteredString ( ( BASEVIDWIDTH / 6 ) , BASEVIDHEIGHT - 80 , 0 , M_GetText ( " Viewpoint: " ) ) ;
V_DrawCenteredString ( ( BASEVIDWIDTH / 6 ) , BASEVIDHEIGHT - 64 , V_ALLOWLOWERCASE , name ) ;
}
// This is where we draw all the fun cheese if you have the chasecam off!
2018-06-03 21:41:54 +00:00
if ( ! ( maptol & TOL_NIGHTS ) )
2014-03-15 16:59:03 +00:00
{
2018-06-03 21:41:54 +00:00
if ( ( stplyr = = & players [ displayplayer ] & & ! camera . chase )
| | ( ( splitscreen & & stplyr = = & players [ secondarydisplayplayer ] ) & & ! camera2 . chase ) )
{
ST_drawFirstPersonHUD ( ) ;
if ( cv_powerupdisplay . value )
2018-08-28 19:51:46 +00:00
ST_drawPowerupHUD ( ) ; // same as it ever was...
2018-06-03 21:41:54 +00:00
}
else if ( cv_powerupdisplay . value = = 2 )
2018-08-28 19:51:46 +00:00
ST_drawPowerupHUD ( ) ; // same as it ever was...
2014-03-15 16:59:03 +00:00
}
2014-04-14 05:14:58 +00:00
}
2018-06-13 23:58:28 +00:00
else if ( ! ( netgame | | multiplayer ) & & cv_powerupdisplay . value = = 2 )
ST_drawPowerupHUD ( ) ; // same as it ever was...
2014-03-15 16:59:03 +00:00
2014-04-14 05:14:58 +00:00
if ( ! ( netgame | | multiplayer ) | | ! hu_showscores )
2021-07-07 07:23:51 +00:00
LUA_HUDHOOK ( game ) ;
2014-03-15 16:59:03 +00:00
// draw level title Tails
2019-11-18 14:39:54 +00:00
if ( stagetitle & & ( ! WipeInAction ) & & ( ! WipeStageTitle ) )
ST_drawTitleCard ( ) ;
2014-03-15 16:59:03 +00:00
2020-03-19 17:36:14 +00:00
if ( ! hu_showscores & & ( netgame | | multiplayer ) & & LUA_HudEnabled ( hud_textspectator ) )
2018-03-30 15:39:59 +00:00
ST_drawTextHUD ( ) ;
2014-03-15 16:59:03 +00:00
2018-06-13 23:58:28 +00:00
if ( modeattacking & & ! ( demoplayback & & hu_showscores ) )
2017-10-18 12:58:16 +00:00
ST_drawInput ( ) ;
2014-03-15 16:59:03 +00:00
ST_drawDebugInfo ( ) ;
}
2016-04-07 01:01:01 +00:00
void ST_Drawer ( void )
2014-03-15 16:59:03 +00:00
{
if ( cv_seenames . value & & cv_allowseenames . value & & displayplayer = = consoleplayer & & seenplayer & & seenplayer - > mo )
{
2018-08-23 22:00:15 +00:00
INT32 c = 0 ;
2018-08-23 21:09:48 +00:00
switch ( cv_seenames . value )
{
case 1 : // Colorless
break ;
case 2 : // Team
2018-08-23 22:00:15 +00:00
if ( G_GametypeHasTeams ( ) )
c = ( seenplayer - > ctfteam = = 1 ) ? V_REDMAP : V_BLUEMAP ;
2018-08-23 21:09:48 +00:00
break ;
case 3 : // Ally/Foe
default :
2018-08-23 22:00:15 +00:00
// Green = Ally, Red = Foe
if ( G_GametypeHasTeams ( ) )
c = ( players [ consoleplayer ] . ctfteam = = seenplayer - > ctfteam ) ? V_GREENMAP : V_REDMAP ;
else // Everyone is an ally, or everyone is a foe!
c = ( G_RingSlingerGametype ( ) ) ? V_REDMAP : V_GREENMAP ;
2018-08-23 21:09:48 +00:00
break ;
}
2018-08-23 22:00:15 +00:00
V_DrawCenteredString ( BASEVIDWIDTH / 2 , BASEVIDHEIGHT / 2 + 15 , V_HUDTRANSHALF | c , player_names [ seenplayer - players ] ) ;
2014-03-15 16:59:03 +00:00
}
2016-04-07 01:01:01 +00:00
// Doom's status bar only updated if necessary.
// However, ours updates every frame regardless, so the "refresh" param was removed
//(void)refresh;
2014-03-15 16:59:03 +00:00
// force a set of the palette by using doPaletteStuff()
if ( vid . recalc )
st_palette = - 1 ;
// Do red-/gold-shifts from damage/items
# ifdef HWRENDER
//25/08/99: Hurdler: palette changes is done for all players,
// not only player1! That's why this part
// of code is moved somewhere else.
if ( rendermode = = render_soft )
# endif
if ( rendermode ! = render_none ) ST_doPaletteStuff ( ) ;
2018-03-26 22:53:09 +00:00
// Blindfold!
2019-12-18 04:54:45 +00:00
if ( ( gametyperules & GTR_BLINDFOLDED )
2018-03-26 22:53:09 +00:00
& & ( leveltime < hidetime * TICRATE ) )
{
if ( players [ displayplayer ] . pflags & PF_TAGIT )
{
stplyr = & players [ displayplayer ] ;
V_DrawFill ( 0 , 0 , BASEVIDWIDTH , BASEVIDHEIGHT , 31 | V_PERPLAYER ) ;
}
else if ( splitscreen & & players [ secondarydisplayplayer ] . pflags & PF_TAGIT )
{
stplyr = & players [ secondarydisplayplayer ] ;
V_DrawFill ( 0 , 0 , BASEVIDWIDTH , BASEVIDHEIGHT , 31 | V_PERPLAYER ) ;
}
}
2019-11-18 14:39:54 +00:00
st_translucency = cv_translucenthud . value ;
2014-03-15 16:59:03 +00:00
if ( st_overlay )
{
// No deadview!
stplyr = & players [ displayplayer ] ;
ST_overlayDrawer ( ) ;
if ( splitscreen )
{
stplyr = & players [ secondarydisplayplayer ] ;
ST_overlayDrawer ( ) ;
}
}
}