Add TitleCardZigZag, TitleCardZigZagText and TitleCardActDiamond fields to SOC. Add the same fields to Lua under their internal names. Turn map header level flags into an UINT16, so that NoTitleCard works. (NOBODY caught this, I'm actually disappointed.)
2797 lines
81 KiB
C
2797 lines
81 KiB
C
// SONIC ROBO BLAST 2
|
|
//-----------------------------------------------------------------------------
|
|
// Copyright (C) 1993-1996 by id Software, Inc.
|
|
// Copyright (C) 1998-2000 by DooM Legacy Team.
|
|
// Copyright (C) 1999-2019 by Sonic Team Junior.
|
|
//
|
|
// 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"
|
|
#include "console.h"
|
|
#include "s_sound.h"
|
|
#include "i_system.h"
|
|
#include "m_menu.h"
|
|
#include "m_cheat.h"
|
|
#include "m_misc.h" // moviemode
|
|
#include "m_anigif.h" // cv_gif_downscale
|
|
#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
|
|
|
|
#ifdef HAVE_BLUA
|
|
#include "lua_hud.h"
|
|
#endif
|
|
|
|
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
|
|
patch_t *sboperiod; // Period for time centiseconds
|
|
patch_t *livesback; // Lives icon background
|
|
patch_t *stlivex;
|
|
static patch_t *nrec_timer; // Timer for NiGHTS records
|
|
static patch_t *sborings;
|
|
static patch_t *slidgame;
|
|
static patch_t *slidtime;
|
|
static patch_t *slidover;
|
|
static patch_t *sboredrings;
|
|
static patch_t *sboredtime;
|
|
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;
|
|
static patch_t *pinkshield;
|
|
static patch_t *flameshield;
|
|
static patch_t *bubbleshield;
|
|
static patch_t *thundershield;
|
|
static patch_t *invincibility;
|
|
static patch_t *sneakers;
|
|
static patch_t *gravboots;
|
|
static patch_t *nonicon;
|
|
static patch_t *nonicon2;
|
|
static patch_t *bluestat;
|
|
static patch_t *byelstat;
|
|
static patch_t *orngstat;
|
|
static patch_t *redstat;
|
|
static patch_t *yelstat;
|
|
static patch_t *nbracket;
|
|
static patch_t *nring;
|
|
static patch_t *nhud[12];
|
|
static patch_t *nsshud;
|
|
static patch_t *nbon[12];
|
|
static patch_t *nssbon;
|
|
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;
|
|
|
|
static boolean facefreed[MAXPLAYERS];
|
|
|
|
hudinfo_t hudinfo[NUMHUDITEMS] =
|
|
{
|
|
{ 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
|
|
|
|
{ 288, 176, V_SNAPTORIGHT|V_SNAPTOBOTTOM}, // HUD_POWERUPS
|
|
};
|
|
|
|
//
|
|
// 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;
|
|
|
|
void ST_Ticker(boolean run)
|
|
{
|
|
if (st_stopped)
|
|
return;
|
|
|
|
if (run)
|
|
ST_runTitleCard();
|
|
}
|
|
|
|
// 0 is default, any others are special palettes.
|
|
INT32 st_palette = 0;
|
|
INT32 st_translucency = 10;
|
|
|
|
void ST_doPaletteStuff(void)
|
|
{
|
|
INT32 palette;
|
|
|
|
if (paused || P_AutoPause())
|
|
palette = 0;
|
|
else if (stplyr && stplyr->flashcount)
|
|
palette = stplyr->flashpal;
|
|
else
|
|
palette = 0;
|
|
|
|
#ifdef HWRENDER
|
|
if (rendermode == render_opengl)
|
|
palette = 0; // No flashpals here in OpenGL
|
|
#endif
|
|
|
|
palette = min(max(palette, 0), 13);
|
|
|
|
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)
|
|
{
|
|
Z_FreeTag(PU_HUDGFX);
|
|
}
|
|
|
|
void ST_LoadGraphics(void)
|
|
{
|
|
int i;
|
|
|
|
// SRB2 border patch
|
|
st_borderpatchnum = W_GetNumForName("GFZFLR01");
|
|
scr_borderpatch = W_CacheLumpNum(st_borderpatchnum, PU_HUDGFX);
|
|
|
|
// 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)
|
|
|
|
// 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
|
|
sboredtime = W_CachePatchName("STTRTIME", PU_HUDGFX);
|
|
sbocolon = W_CachePatchName("STTCOLON", PU_HUDGFX); // Colon for time
|
|
sboperiod = W_CachePatchName("STTPERIO", PU_HUDGFX); // Period for time centiseconds
|
|
|
|
slidgame = W_CachePatchName("SLIDGAME", PU_HUDGFX);
|
|
slidtime = W_CachePatchName("SLIDTIME", PU_HUDGFX);
|
|
slidover = W_CachePatchName("SLIDOVER", PU_HUDGFX);
|
|
|
|
stlivex = W_CachePatchName("STLIVEX", PU_HUDGFX);
|
|
livesback = W_CachePatchName("STLIVEBK", PU_HUDGFX);
|
|
nrec_timer = W_CachePatchName("NGRTIMER", PU_HUDGFX); // Timer for NiGHTS
|
|
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);
|
|
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);
|
|
pinkshield = W_CachePatchName("TVPPICON", PU_HUDGFX);
|
|
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);
|
|
|
|
tagico = W_CachePatchName("TAGICO", PU_HUDGFX);
|
|
rflagico = W_CachePatchName("RFLAGICO", PU_HUDGFX);
|
|
bflagico = W_CachePatchName("BFLAGICO", PU_HUDGFX);
|
|
rmatcico = W_CachePatchName("RMATCICO", PU_HUDGFX);
|
|
bmatcico = W_CachePatchName("BMATCICO", PU_HUDGFX);
|
|
gotrflag = W_CachePatchName("GOTRFLAG", PU_HUDGFX);
|
|
gotbflag = W_CachePatchName("GOTBFLAG", PU_HUDGFX);
|
|
nonicon = W_CachePatchName("NONICON", PU_HUDGFX);
|
|
nonicon2 = W_CachePatchName("NONICON2", PU_HUDGFX);
|
|
|
|
// 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);
|
|
nring = W_CachePatchName("NRNG1", PU_HUDGFX);
|
|
for (i = 0; i < 12; ++i)
|
|
{
|
|
nhud[i] = W_CachePatchName(va("NHUD%d", i+1), PU_HUDGFX);
|
|
nbon[i] = W_CachePatchName(va("NBON%d", i+1), PU_HUDGFX);
|
|
}
|
|
nsshud = W_CachePatchName("NSSHUD", PU_HUDGFX);
|
|
nssbon = W_CachePatchName("NSSBON", PU_HUDGFX);
|
|
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
|
|
void ST_LoadFaceGraphics(INT32 skinnum)
|
|
{
|
|
if (skins[skinnum].sprites[SPR2_XTRA].numframes > XTRA_LIFEPIC)
|
|
{
|
|
spritedef_t *sprdef = &skins[skinnum].sprites[SPR2_XTRA];
|
|
spriteframe_t *sprframe = &sprdef->spriteframes[XTRA_LIFEPIC];
|
|
faceprefix[skinnum] = W_CachePatchNum(sprframe->lumppat[0], PU_HUDGFX);
|
|
if (skins[skinnum].sprites[(SPR2_XTRA|FF_SPR2SUPER)].numframes > XTRA_LIFEPIC)
|
|
{
|
|
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
|
|
facefreed[skinnum] = false;
|
|
}
|
|
|
|
void ST_ReloadSkinFaceGraphics(void)
|
|
{
|
|
INT32 i;
|
|
|
|
for (i = 0; i < numskins; i++)
|
|
ST_LoadFaceGraphics(i);
|
|
}
|
|
|
|
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;
|
|
|
|
// =========================================================================
|
|
// INTERNAL DRAWING
|
|
// =========================================================================
|
|
#define ST_DrawTopLeftOverlayPatch(x,y,p) V_DrawScaledPatch(x, y, V_PERPLAYER|V_SNAPTOTOP|V_SNAPTOLEFT|V_HUDTRANS, p)
|
|
#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)
|
|
|
|
// Draw a number, scaled, over the view, maybe with set translucency
|
|
// Always draw the number completely since it's overlay
|
|
//
|
|
// Supports different colors! woo!
|
|
static void ST_DrawNightsOverlayNum(fixed_t x /* right border */, fixed_t y, fixed_t s, INT32 a,
|
|
UINT32 num, patch_t **numpat, skincolors_t colornum)
|
|
{
|
|
fixed_t w = SHORT(numpat[0]->width)*s;
|
|
const UINT8 *colormap;
|
|
|
|
// I want my V_SNAPTOx flags. :< -Red
|
|
//a &= V_ALPHAMASK;
|
|
|
|
if (colornum == 0)
|
|
colormap = colormaps;
|
|
else // Uses the player colors.
|
|
colormap = R_GetTranslationColormap(TC_DEFAULT, colornum, GTC_CACHE);
|
|
|
|
//I_Assert(num >= 0); // this function does not draw negative numbers
|
|
|
|
// draw the number
|
|
do
|
|
{
|
|
x -= w;
|
|
V_DrawFixedPatch(x, y, s, a, numpat[num % 10], colormap);
|
|
num /= 10;
|
|
} while (num);
|
|
|
|
// Sorry chum, this function only draws UNSIGNED values!
|
|
// then why is num not UINT32? ~toast
|
|
}
|
|
|
|
// Devmode information
|
|
static void ST_drawDebugInfo(void)
|
|
{
|
|
INT32 height = 0, h = 8, w = 18, lowh;
|
|
void (*textfunc)(INT32, INT32, INT32, const char *);
|
|
|
|
if (!(stplyr->mo && cv_debug))
|
|
return;
|
|
|
|
#define VFLAGS V_MONOSPACE|V_SNAPTOTOP|V_SNAPTORIGHT
|
|
|
|
if ((moviemode == MM_GIF && cv_gif_downscale.value) || vid.dupx == 1)
|
|
{
|
|
textfunc = V_DrawRightAlignedString;
|
|
lowh = ((vid.height/vid.dupy) - 16);
|
|
}
|
|
else
|
|
{
|
|
textfunc = V_DrawRightAlignedSmallString;
|
|
h /= 2;
|
|
w /= 2;
|
|
lowh = 0;
|
|
}
|
|
|
|
#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;
|
|
|
|
#define V_DrawDebugFlag(f, str) textfunc(width, height, VFLAGS|f, str);\
|
|
width -= w
|
|
|
|
if (cv_debug & DBG_MEMORY)
|
|
{
|
|
V_DrawDebugLine(va("Heap: %8sKB", sizeu1(Z_TotalUsage()>>10)));
|
|
|
|
height += h/2;
|
|
}
|
|
|
|
if (cv_debug & DBG_RANDOMIZER) // randomizer testing
|
|
{
|
|
fixed_t peekres = P_RandomPeek();
|
|
peekres *= 10000; // Change from fixed point
|
|
peekres >>= FRACBITS; // to displayable decimal
|
|
|
|
V_DrawDebugLine(va("Init: %08x", P_GetInitSeed()));
|
|
V_DrawDebugLine(va("Seed: %08x", P_GetRandSeed()));
|
|
V_DrawDebugLine(va("== : .%04d", peekres));
|
|
|
|
height += h/2;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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
|
|
#undef V_DrawDebugLine
|
|
#undef VFLAGS
|
|
}
|
|
|
|
static void ST_drawScore(void)
|
|
{
|
|
if (F_GetPromptHideHud(hudinfo[HUD_SCORE].y))
|
|
return;
|
|
|
|
// SCORE:
|
|
ST_DrawPatchFromHud(HUD_SCORE, sboscore, V_HUDTRANS);
|
|
if (objectplacing)
|
|
{
|
|
if (op_displayflags > UINT16_MAX)
|
|
ST_DrawTopLeftOverlayPatch((hudinfo[HUD_SCORENUM].x-tallminus->width), hudinfo[HUD_SCORENUM].y, tallminus);
|
|
else
|
|
ST_DrawNumFromHud(HUD_SCORENUM, op_displayflags, V_HUDTRANS);
|
|
}
|
|
else
|
|
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));
|
|
}
|
|
V_DrawScaledPatch(((BASEVIDWIDTH - SHORT(racenum->width))/2), height, V_PERPLAYER, racenum);
|
|
}
|
|
|
|
static void ST_drawTime(void)
|
|
{
|
|
INT32 seconds, minutes, tictrn, tics;
|
|
boolean downwards = false;
|
|
|
|
if (objectplacing)
|
|
{
|
|
tics = objectsdrawn;
|
|
seconds = objectsdrawn%100;
|
|
minutes = objectsdrawn/100;
|
|
tictrn = 0;
|
|
}
|
|
else
|
|
{
|
|
// Counting down the hidetime?
|
|
if ((gametype == GT_TAG || gametype == GT_HIDEANDSEEK) && (stplyr->realtime <= (hidetime*TICRATE)))
|
|
{
|
|
tics = (hidetime*TICRATE - stplyr->realtime);
|
|
if (tics < 3*TICRATE)
|
|
ST_drawRaceNum(tics);
|
|
tics += (TICRATE-1); // match the race num
|
|
downwards = true;
|
|
}
|
|
else
|
|
{
|
|
// Hidetime finish!
|
|
if ((gametype == GT_TAG || gametype == GT_HIDEANDSEEK) && (stplyr->realtime < ((hidetime+1)*TICRATE)))
|
|
ST_drawRaceNum(hidetime*TICRATE - stplyr->realtime);
|
|
|
|
// Time limit?
|
|
if (gametype != GT_COOP && gametype != GT_RACE && gametype != GT_COMPETITION && cv_timelimit.value && timelimitintics > 0)
|
|
{
|
|
if (timelimitintics > stplyr->realtime)
|
|
{
|
|
tics = (timelimitintics - stplyr->realtime);
|
|
if (tics < 3*TICRATE)
|
|
ST_drawRaceNum(tics);
|
|
tics += (TICRATE-1); // match the race num
|
|
}
|
|
else // Overtime!
|
|
tics = 0;
|
|
downwards = true;
|
|
}
|
|
// Post-hidetime normal.
|
|
else if (gametype == GT_TAG || gametype == GT_HIDEANDSEEK)
|
|
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;
|
|
}
|
|
|
|
minutes = G_TicsToMinutes(tics, true);
|
|
seconds = G_TicsToSeconds(tics);
|
|
tictrn = G_TicsToCentiseconds(tics);
|
|
}
|
|
|
|
if (F_GetPromptHideHud(hudinfo[HUD_TIME].y))
|
|
return;
|
|
|
|
downwards = (downwards && (tics < 30*TICRATE) && (leveltime/5 & 1) && !stoppedclock); // overtime?
|
|
|
|
// TIME:
|
|
ST_DrawPatchFromHud(HUD_TIME, (downwards ? sboredtime : sbotime), V_HUDTRANS);
|
|
|
|
if (downwards) // overtime!
|
|
return;
|
|
|
|
if (cv_timetic.value == 3) // Tics only -- how simple is this?
|
|
ST_DrawNumFromHud(HUD_SECONDS, tics, V_HUDTRANS);
|
|
else
|
|
{
|
|
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
|
|
|
|
if (cv_timetic.value == 1 || cv_timetic.value == 2 || modeattacking) // there's not enough room for tics in splitscreen, don't even bother trying!
|
|
{
|
|
ST_DrawPatchFromHud(HUD_TIMETICCOLON, sboperiod, V_HUDTRANS); // Period
|
|
ST_DrawPadNumFromHud(HUD_TICS, tictrn, 2, V_HUDTRANS); // Tics
|
|
}
|
|
}
|
|
}
|
|
|
|
static inline void ST_drawRings(void)
|
|
{
|
|
INT32 ringnum;
|
|
|
|
if (F_GetPromptHideHud(hudinfo[HUD_RINGS].y))
|
|
return;
|
|
|
|
ST_DrawPatchFromHud(HUD_RINGS, ((!stplyr->spectator && stplyr->rings <= 0 && leveltime/5 & 1) ? sboredrings : sborings), ((stplyr->spectator) ? V_HUDTRANSHALF : V_HUDTRANS));
|
|
|
|
if (objectplacing)
|
|
ringnum = op_currentdoomednum;
|
|
else if (stplyr->rings < 0 || stplyr->spectator || stplyr->playerstate == PST_REBORN)
|
|
ringnum = 0;
|
|
else
|
|
ringnum = stplyr->rings;
|
|
|
|
if (cv_timetic.value == 2) // Yes, even in modeattacking
|
|
ST_DrawNumFromHud(HUD_RINGSNUMTICS, ringnum, V_PERPLAYER|((stplyr->spectator) ? V_HUDTRANSHALF : V_HUDTRANS));
|
|
else
|
|
ST_DrawNumFromHud(HUD_RINGSNUM, ringnum, V_PERPLAYER|((stplyr->spectator) ? V_HUDTRANSHALF : V_HUDTRANS));
|
|
}
|
|
|
|
static void ST_drawLivesArea(void)
|
|
{
|
|
INT32 v_colmap = V_YELLOWMAP, livescount;
|
|
boolean notgreyedout;
|
|
|
|
if (!stplyr->skincolor)
|
|
return; // Just joined a server, skin isn't loaded yet!
|
|
|
|
if (F_GetPromptHideHud(hudinfo[HUD_LIVES].y))
|
|
return;
|
|
|
|
// face background
|
|
V_DrawSmallScaledPatch(hudinfo[HUD_LIVES].x, hudinfo[HUD_LIVES].y,
|
|
hudinfo[HUD_LIVES].f|V_PERPLAYER|V_HUDTRANS, livesback);
|
|
|
|
// face
|
|
if (stplyr->spectator)
|
|
{
|
|
// spectator face
|
|
UINT8 *colormap = R_GetTranslationColormap(stplyr->skin, SKINCOLOR_CLOUDY, GTC_CACHE);
|
|
V_DrawSmallMappedPatch(hudinfo[HUD_LIVES].x, hudinfo[HUD_LIVES].y,
|
|
hudinfo[HUD_LIVES].f|V_PERPLAYER|V_HUDTRANSHALF, faceprefix[stplyr->skin], colormap);
|
|
}
|
|
else if (stplyr->mo && stplyr->mo->color)
|
|
{
|
|
// skincolor face/super
|
|
UINT8 *colormap = R_GetTranslationColormap(stplyr->skin, stplyr->mo->color, GTC_CACHE);
|
|
patch_t *face = faceprefix[stplyr->skin];
|
|
if (stplyr->powers[pw_super])
|
|
face = superprefix[stplyr->skin];
|
|
V_DrawSmallMappedPatch(hudinfo[HUD_LIVES].x, hudinfo[HUD_LIVES].y,
|
|
hudinfo[HUD_LIVES].f|V_PERPLAYER|V_HUDTRANS, face, colormap);
|
|
if (st_translucency == 10 && stplyr->powers[pw_super] == 1 && stplyr->mo->tracer)
|
|
{
|
|
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);
|
|
V_DrawSmallMappedPatch(hudinfo[HUD_LIVES].x, hudinfo[HUD_LIVES].y,
|
|
hudinfo[HUD_LIVES].f|V_PERPLAYER|v_supertrans, face, colormap);
|
|
}
|
|
}
|
|
}
|
|
else if (stplyr->skincolor)
|
|
{
|
|
// skincolor face
|
|
UINT8 *colormap = R_GetTranslationColormap(stplyr->skin, stplyr->skincolor, GTC_CACHE);
|
|
V_DrawSmallMappedPatch(hudinfo[HUD_LIVES].x, hudinfo[HUD_LIVES].y,
|
|
hudinfo[HUD_LIVES].f|V_PERPLAYER|V_HUDTRANS, faceprefix[stplyr->skin], colormap);
|
|
}
|
|
|
|
// Lives number
|
|
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");
|
|
}
|
|
else if (G_GametypeUsesLives() || gametype == GT_RACE)
|
|
{
|
|
// x
|
|
V_DrawScaledPatch(hudinfo[HUD_LIVES].x+22, hudinfo[HUD_LIVES].y+10,
|
|
hudinfo[HUD_LIVES].f|V_PERPLAYER|V_HUDTRANS, stlivex);
|
|
|
|
// lives number
|
|
if (gametype == GT_RACE)
|
|
{
|
|
livescount = INFLIVES;
|
|
notgreyedout = true;
|
|
}
|
|
else if ((netgame || multiplayer) && gametype == GT_COOP && cv_cooplives.value == 3)
|
|
{
|
|
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
|
|
{
|
|
livescount = (((netgame || multiplayer) && gametype == GT_COOP && cv_cooplives.value == 0) ? INFLIVES : stplyr->lives);
|
|
notgreyedout = true;
|
|
}
|
|
|
|
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
|
|
{
|
|
if (stplyr->playerstate == PST_DEAD && !(stplyr->spectator) && (livescount || stplyr->deadtimer < (TICRATE<<1)))
|
|
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));
|
|
}
|
|
}
|
|
// Spectator
|
|
else if (stplyr->spectator)
|
|
v_colmap = V_GRAYMAP;
|
|
// Tag
|
|
else if (gametype == GT_TAG || gametype == GT_HIDEANDSEEK)
|
|
{
|
|
if (stplyr->pflags & PF_TAGIT)
|
|
{
|
|
V_DrawRightAlignedString(hudinfo[HUD_LIVES].x+58, hudinfo[HUD_LIVES].y+8, V_HUDTRANS|hudinfo[HUD_LIVES].f|V_PERPLAYER, "IT!");
|
|
v_colmap = V_ORANGEMAP;
|
|
}
|
|
}
|
|
// Team name
|
|
else if (G_GametypeHasTeams())
|
|
{
|
|
if (stplyr->ctfteam == 1)
|
|
{
|
|
V_DrawRightAlignedString(hudinfo[HUD_LIVES].x+58, hudinfo[HUD_LIVES].y+8, V_HUDTRANS|hudinfo[HUD_LIVES].f|V_PERPLAYER, "RED");
|
|
v_colmap = V_REDMAP;
|
|
}
|
|
else if (stplyr->ctfteam == 2)
|
|
{
|
|
V_DrawRightAlignedString(hudinfo[HUD_LIVES].x+58, hudinfo[HUD_LIVES].y+8, V_HUDTRANS|hudinfo[HUD_LIVES].f|V_PERPLAYER, "BLUE");
|
|
v_colmap = V_BLUEMAP;
|
|
}
|
|
}
|
|
|
|
// name
|
|
v_colmap |= (V_HUDTRANS|hudinfo[HUD_LIVES].f|V_PERPLAYER);
|
|
if (strlen(skins[stplyr->skin].hudname) <= 5)
|
|
V_DrawRightAlignedString(hudinfo[HUD_LIVES].x+58, hudinfo[HUD_LIVES].y, v_colmap, skins[stplyr->skin].hudname);
|
|
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);
|
|
else
|
|
V_DrawThinString(hudinfo[HUD_LIVES].x+18, hudinfo[HUD_LIVES].y, v_colmap, skins[stplyr->skin].hudname);
|
|
|
|
// Power Stones collected
|
|
if (G_RingSlingerGametype()
|
|
#ifdef HAVE_BLUA
|
|
&& LUA_HudEnabled(hud_powerstones)
|
|
#endif
|
|
)
|
|
{
|
|
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
|
|
{
|
|
V_DrawScaledPatch(workx, hudinfo[HUD_LIVES].y-9, V_HUDTRANS|hudinfo[HUD_LIVES].f|V_PERPLAYER, emeraldpics[1][j]);
|
|
workx += 8;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (j = 0; j < 7; ++j) // powerstones
|
|
{
|
|
if (stplyr->powers[pw_emeralds] & (1 << j))
|
|
V_DrawScaledPatch(workx, hudinfo[HUD_LIVES].y-9, V_HUDTRANS|hudinfo[HUD_LIVES].f|V_PERPLAYER, emeraldpics[1][j]);
|
|
workx += 8;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void ST_drawInput(void)
|
|
{
|
|
const INT32 accent = V_SNAPTOLEFT|V_SNAPTOBOTTOM|(stplyr->skincolor ? Color_Index[stplyr->skincolor-1][4] : 0);
|
|
INT32 col;
|
|
UINT8 offs;
|
|
|
|
INT32 x = hudinfo[HUD_LIVES].x, y = hudinfo[HUD_LIVES].y;
|
|
|
|
if (stplyr->powers[pw_carry] == CR_NIGHTSMODE)
|
|
y -= 16;
|
|
|
|
if (F_GetPromptHideHud(y))
|
|
return;
|
|
|
|
// O backing
|
|
V_DrawFill(x, y-1, 16, 16, hudinfo[HUD_LIVES].f|20);
|
|
V_DrawFill(x, y+15, 16, 1, hudinfo[HUD_LIVES].f|29);
|
|
|
|
if (cv_showinputjoy.value) // joystick render!
|
|
{
|
|
/*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*/
|
|
if (stplyr->cmd.sidemove || stplyr->cmd.forwardmove)
|
|
{
|
|
// joystick hole
|
|
V_DrawFill(x+5, y+4, 6, 6, hudinfo[HUD_LIVES].f|29);
|
|
// joystick top
|
|
V_DrawFill(x+3+stplyr->cmd.sidemove/12,
|
|
y+2-stplyr->cmd.forwardmove/12,
|
|
10, 10, hudinfo[HUD_LIVES].f|29);
|
|
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
|
|
V_DrawFill(x+3, y+11, 10, 1, hudinfo[HUD_LIVES].f|29);
|
|
V_DrawFill(x+3,
|
|
y+1,
|
|
10, 10, hudinfo[HUD_LIVES].f|16);
|
|
}
|
|
}
|
|
else // arrows!
|
|
{
|
|
// <
|
|
if (stplyr->cmd.sidemove < 0)
|
|
{
|
|
offs = 0;
|
|
col = accent;
|
|
}
|
|
else
|
|
{
|
|
offs = 1;
|
|
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);
|
|
}
|
|
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);
|
|
|
|
// ^
|
|
if (stplyr->cmd.forwardmove > 0)
|
|
{
|
|
offs = 0;
|
|
col = accent;
|
|
}
|
|
else
|
|
{
|
|
offs = 1;
|
|
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);
|
|
}
|
|
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);
|
|
|
|
// >
|
|
if (stplyr->cmd.sidemove > 0)
|
|
{
|
|
offs = 0;
|
|
col = accent;
|
|
}
|
|
else
|
|
{
|
|
offs = 1;
|
|
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);
|
|
}
|
|
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);
|
|
|
|
// v
|
|
if (stplyr->cmd.forwardmove < 0)
|
|
{
|
|
offs = 0;
|
|
col = accent;
|
|
}
|
|
else
|
|
{
|
|
offs = 1;
|
|
col = hudinfo[HUD_LIVES].f|16;
|
|
V_DrawFill(x+ 5, y+17, 6, 1, hudinfo[HUD_LIVES].f|29);
|
|
}
|
|
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);
|
|
}
|
|
|
|
#define drawbutt(xoffs, yoffs, butt, symb)\
|
|
if (stplyr->cmd.buttons & butt)\
|
|
{\
|
|
offs = 0;\
|
|
col = accent;\
|
|
}\
|
|
else\
|
|
{\
|
|
offs = 1;\
|
|
col = hudinfo[HUD_LIVES].f|16;\
|
|
V_DrawFill(x+16+(xoffs), y+9+(yoffs), 10, 1, hudinfo[HUD_LIVES].f|29);\
|
|
}\
|
|
V_DrawFill(x+16+(xoffs), y+(yoffs)-offs, 10, 10, col);\
|
|
V_DrawCharacter(x+16+1+(xoffs), y+1+(yoffs)-offs, hudinfo[HUD_LIVES].f|symb, false)
|
|
|
|
drawbutt( 4,-3, BT_JUMP, 'J');
|
|
drawbutt(15,-3, BT_USE, 'S');
|
|
|
|
V_DrawFill(x+16+4, y+8, 21, 10, hudinfo[HUD_LIVES].f|20); // sundial backing
|
|
if (stplyr->mo)
|
|
{
|
|
UINT8 i, precision;
|
|
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;
|
|
fixed_t xcomp = FINESINE(ang)>>13;
|
|
fixed_t ycomp = FINECOSINE(ang)>>14;
|
|
if (ycomp == 4)
|
|
ycomp = 3;
|
|
|
|
if (ycomp > 0)
|
|
V_DrawFill(x+16+13-xcomp, y+11-ycomp, 3, 3, accent); // point (behind)
|
|
|
|
precision = max(3, abs(xcomp));
|
|
for (i = 0; i < precision; i++) // line
|
|
{
|
|
V_DrawFill(x+16+14-(i*xcomp)/precision,
|
|
y+12-(i*ycomp)/precision,
|
|
1, 1, hudinfo[HUD_LIVES].f|16);
|
|
}
|
|
|
|
if (ycomp <= 0)
|
|
V_DrawFill(x+16+13-xcomp, y+11-ycomp, 3, 3, accent); // point (in front)
|
|
}
|
|
|
|
#undef drawbutt
|
|
|
|
// text above
|
|
x -= 2;
|
|
y -= 13;
|
|
if (stplyr->powers[pw_carry] != CR_NIGHTSMODE)
|
|
{
|
|
if (stplyr->pflags & PF_AUTOBRAKE)
|
|
{
|
|
V_DrawThinString(x, y,
|
|
hudinfo[HUD_LIVES].f|
|
|
((!stplyr->powers[pw_carry]
|
|
&& (stplyr->pflags & PF_APPLYAUTOBRAKE)
|
|
&& !(stplyr->cmd.sidemove || stplyr->cmd.forwardmove)
|
|
&& (stplyr->rmomx || stplyr->rmomy)
|
|
&& (!stplyr->capsule || (stplyr->capsule->reactiontime != (stplyr-players)+1)))
|
|
? 0 : V_GRAYMAP),
|
|
"AUTOBRAKE");
|
|
y -= 8;
|
|
}
|
|
if (stplyr->pflags & PF_ANALOGMODE)
|
|
{
|
|
V_DrawThinString(x, y, hudinfo[HUD_LIVES].f, "ANALOG");
|
|
y -= 8;
|
|
}
|
|
}
|
|
if (!demosynced) // should always be last, so it doesn't push anything else around
|
|
V_DrawThinString(x, y, hudinfo[HUD_LIVES].f|((leveltime & 4) ? V_YELLOWMAP : V_REDMAP), "BAD DEMO!!");
|
|
}
|
|
|
|
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.
|
|
// Don't let LJ see this
|
|
//
|
|
static void ST_cacheLevelTitle(void)
|
|
{
|
|
#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
|
|
}
|
|
|
|
//
|
|
// Start the title card.
|
|
//
|
|
void ST_startTitleCard(void)
|
|
{
|
|
// cache every HUD patch used
|
|
ST_cacheLevelTitle();
|
|
|
|
// initialize HUD variables
|
|
lt_ticker = lt_exitticker = lt_lasttic = 0;
|
|
lt_endtime = 2*TICRATE + (10*NEWTICRATERATIO);
|
|
lt_scroll = BASEVIDWIDTH * FRACUNIT;
|
|
lt_zigzag = -((lt_patches[1])->width * FRACUNIT);
|
|
lt_mom = 0;
|
|
}
|
|
|
|
//
|
|
// What happens before drawing the title card.
|
|
// Which is just setting the HUD translucency.
|
|
//
|
|
void ST_preDrawTitleCard(void)
|
|
{
|
|
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)
|
|
{
|
|
if (lt_ticker >= (lt_endtime + TICRATE))
|
|
return;
|
|
|
|
if (!(paused || P_AutoPause()))
|
|
{
|
|
// 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)
|
|
{
|
|
char *lvlttl = mapheaderinfo[gamemap-1]->lvlttl;
|
|
char *subttl = mapheaderinfo[gamemap-1]->subttl;
|
|
INT32 actnum = mapheaderinfo[gamemap-1]->actnum;
|
|
INT32 lvlttlxpos, ttlnumxpos, zonexpos;
|
|
INT32 subttlxpos = BASEVIDWIDTH/2;
|
|
INT32 ttlscroll = FixedInt(lt_scroll);
|
|
INT32 zzticker;
|
|
patch_t *actpat, *zigzag, *zztext;
|
|
|
|
#ifdef HAVE_BLUA
|
|
if (!LUA_HudEnabled(hud_stagetitle))
|
|
goto luahook;
|
|
#endif
|
|
|
|
if (lt_ticker >= (lt_endtime + TICRATE))
|
|
#ifdef HAVE_BLUA
|
|
goto luahook;
|
|
#else
|
|
return;
|
|
#endif
|
|
|
|
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];
|
|
|
|
lvlttlxpos = ((BASEVIDWIDTH/2) - (V_LevelNameWidth(lvlttl)/2));
|
|
|
|
if (actnum > 0)
|
|
lvlttlxpos -= V_LevelActNumWidth(actnum);
|
|
|
|
ttlnumxpos = lvlttlxpos + V_LevelNameWidth(lvlttl);
|
|
zonexpos = ttlnumxpos - V_LevelNameWidth(M_GetText("Zone"));
|
|
ttlnumxpos++;
|
|
|
|
if (lvlttlxpos < 0)
|
|
lvlttlxpos = 0;
|
|
|
|
if (!splitscreen || (splitscreen && stplyr == &players[displayplayer]))
|
|
{
|
|
zzticker = lt_ticker;
|
|
V_DrawScaledPatch(FixedInt(lt_zigzag), (-zzticker) % zigzag->height, V_SNAPTOTOP|V_SNAPTOLEFT, zigzag);
|
|
V_DrawScaledPatch(FixedInt(lt_zigzag), (zigzag->height-zzticker) % zigzag->height, V_SNAPTOTOP|V_SNAPTOLEFT, zigzag);
|
|
V_DrawScaledPatch(FixedInt(lt_zigzag), (-zigzag->height+zzticker) % zztext->height, V_SNAPTOTOP|V_SNAPTOLEFT, zztext);
|
|
V_DrawScaledPatch(FixedInt(lt_zigzag), (zzticker) % zztext->height, V_SNAPTOTOP|V_SNAPTOLEFT, zztext);
|
|
}
|
|
|
|
if (actnum)
|
|
{
|
|
if (!splitscreen)
|
|
V_DrawScaledPatch(ttlnumxpos + ttlscroll, 104 - ttlscroll, 0, actpat);
|
|
V_DrawLevelActNum(ttlnumxpos + ttlscroll, 104, V_PERPLAYER, actnum);
|
|
}
|
|
|
|
V_DrawLevelTitle(lvlttlxpos - ttlscroll, 80, V_PERPLAYER, lvlttl);
|
|
if (!(mapheaderinfo[gamemap-1]->levelflags & LF_NOZONE))
|
|
V_DrawLevelTitle(zonexpos + ttlscroll, 104, V_PERPLAYER, M_GetText("Zone"));
|
|
V_DrawCenteredString(subttlxpos - ttlscroll, 135, V_PERPLAYER|V_ALLOWLOWERCASE, subttl);
|
|
|
|
lt_lasttic = lt_ticker;
|
|
|
|
#ifdef HAVE_BLUA
|
|
luahook:
|
|
LUAh_TitleCardHUD(stplyr);
|
|
#endif
|
|
}
|
|
|
|
//
|
|
// Drawer for G_PreLevelTitleCard.
|
|
//
|
|
void ST_preLevelTitleCardDrawer(void)
|
|
{
|
|
V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, levelfadecol);
|
|
ST_drawWipeTitleCard();
|
|
I_OsPolling();
|
|
I_UpdateNoBlit();
|
|
}
|
|
|
|
//
|
|
// Draw the title card while on a wipe.
|
|
// Also used in G_PreLevelTitleCard.
|
|
//
|
|
void ST_drawWipeTitleCard(void)
|
|
{
|
|
stplyr = &players[consoleplayer];
|
|
ST_preDrawTitleCard();
|
|
ST_drawTitleCard();
|
|
if (splitscreen)
|
|
{
|
|
stplyr = &players[secondarydisplayplayer];
|
|
ST_preDrawTitleCard();
|
|
ST_drawTitleCard();
|
|
}
|
|
}
|
|
|
|
static void ST_drawPowerupHUD(void)
|
|
{
|
|
patch_t *p = NULL;
|
|
UINT16 invulntime = 0;
|
|
INT32 offs = hudinfo[HUD_POWERUPS].x;
|
|
const UINT8 q = ((splitscreen && stplyr == &players[secondarydisplayplayer]) ? 1 : 0);
|
|
static INT32 flagoffs[2] = {0, 0}, shieldoffs[2] = {0, 0};
|
|
#define ICONSEP (16+4) // matches weapon rings HUD
|
|
|
|
if (F_GetPromptHideHud(hudinfo[HUD_POWERUPS].y))
|
|
return;
|
|
|
|
if (stplyr->spectator || stplyr->playerstate != PST_LIVE)
|
|
return;
|
|
|
|
// -------
|
|
// Shields
|
|
// -------
|
|
|
|
// Graue 06-18-2004: no V_NOSCALESTART, no SCX, no SCY, snap to right
|
|
if (stplyr->powers[pw_shield] & SH_NOSTACK)
|
|
{
|
|
shieldoffs[q] = ICONSEP;
|
|
|
|
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
|
|
{
|
|
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;
|
|
case SH_PINK: p = pinkshield; break;
|
|
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);
|
|
}
|
|
}
|
|
else if (shieldoffs[q])
|
|
{
|
|
if (shieldoffs[q] > 1)
|
|
shieldoffs[q] = 2*shieldoffs[q]/3;
|
|
else
|
|
shieldoffs[q] = 0;
|
|
}
|
|
|
|
offs -= shieldoffs[q];
|
|
|
|
// ---------
|
|
// CTF flags
|
|
// ---------
|
|
|
|
// YOU have a flag. Display a monitor-like icon for it.
|
|
if (stplyr->gotflag)
|
|
{
|
|
flagoffs[q] = ICONSEP;
|
|
p = (stplyr->gotflag & GF_REDFLAG) ? gotrflag : gotbflag;
|
|
V_DrawSmallScaledPatch(offs, hudinfo[HUD_POWERUPS].y, V_PERPLAYER|hudinfo[HUD_POWERUPS].f|V_HUDTRANS, p);
|
|
}
|
|
else if (flagoffs[q])
|
|
{
|
|
if (flagoffs[q] > 1)
|
|
flagoffs[q] = 2*flagoffs[q]/3;
|
|
else
|
|
flagoffs[q] = 0;
|
|
}
|
|
|
|
offs -= flagoffs[q];
|
|
|
|
// --------------------
|
|
// 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
|
|
invulntime = stplyr->powers[pw_flashing] ? stplyr->powers[pw_flashing] : stplyr->powers[pw_invulnerability];
|
|
// Note: pw_flashing always makes the icon flicker regardless of time, unlike pw_invulnerability
|
|
if (stplyr->powers[pw_invulnerability] > 3*TICRATE || (invulntime && leveltime & 1))
|
|
{
|
|
DRAWTIMERICON(invincibility, invulntime)
|
|
}
|
|
|
|
if (invulntime > 7)
|
|
offs -= ICONSEP;
|
|
else
|
|
{
|
|
UINT8 a = ICONSEP, b = 7-invulntime;
|
|
while (b--)
|
|
a = 2*a/3;
|
|
offs -= a;
|
|
}
|
|
|
|
// Super Sneakers
|
|
if (stplyr->powers[pw_sneakers] > 3*TICRATE || (stplyr->powers[pw_sneakers] && leveltime & 1))
|
|
{
|
|
DRAWTIMERICON(sneakers, stplyr->powers[pw_sneakers])
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
// Gravity Boots
|
|
if (stplyr->powers[pw_gravityboots] > 3*TICRATE || (stplyr->powers[pw_gravityboots] && leveltime & 1))
|
|
{
|
|
DRAWTIMERICON(gravboots, stplyr->powers[pw_gravityboots])
|
|
}
|
|
|
|
#undef DRAWTIMERICON
|
|
#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];
|
|
|
|
if (airtime < 1)
|
|
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
|
|
|
|
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)))
|
|
return; // Don't draw anything between numbers
|
|
|
|
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
|
|
|
|
if (stplyr->charflags & SF_MACHINE)
|
|
airtime += 6; // Robots use different drown numbers
|
|
|
|
// Get the front angle patch for the frame
|
|
sprframe = &sprites[SPR_DRWN].spriteframes[airtime];
|
|
p = W_CachePatchNum(sprframe->lumppat[0], PU_CACHE);
|
|
|
|
// Display the countdown drown numbers!
|
|
if (p && !F_GetPromptHideHud(60 - SHORT(p->topoffset)))
|
|
V_DrawScaledPatch((BASEVIDWIDTH/2) - (SHORT(p->width)/2) + SHORT(p->leftoffset), 60 - SHORT(p->topoffset),
|
|
V_PERPLAYER|V_PERPLAYER|V_TRANSLUCENT, p);
|
|
}
|
|
|
|
static void ST_drawNightsRecords(void)
|
|
{
|
|
INT32 aflag = V_PERPLAYER;
|
|
|
|
if (!stplyr->texttimer)
|
|
return;
|
|
|
|
if (stplyr->texttimer < TICRATE/2)
|
|
aflag |= (9 - 9*stplyr->texttimer/(TICRATE/2)) << V_ALPHASHIFT;
|
|
|
|
switch (stplyr->textvar)
|
|
{
|
|
case 1: // A "Bonus Time Start" by any other name...
|
|
{
|
|
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!"));
|
|
|
|
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;
|
|
}
|
|
case 2: // Get n Spheres
|
|
case 3: // Get n more Spheres
|
|
{
|
|
if (!stplyr->capsule)
|
|
return;
|
|
|
|
// Yes, this string is an abomination.
|
|
V_DrawCenteredString(BASEVIDWIDTH/2, 60, aflag,
|
|
va(M_GetText("\x80GET\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;
|
|
}
|
|
case 4: // End Bonus
|
|
{
|
|
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))
|
|
{
|
|
UINT8 grade = P_GetGrade(stplyr->lastmarescore, gamemap, stplyr->lastmare);
|
|
if (modeattacking || grade >= GRADE_A)
|
|
V_DrawTranslucentPatch(BASEVIDWIDTH/2 + 60, 160, aflag, ngradeletters[grade]);
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
// 2.0-1: [21:42] <+Rob> Beige - Lavender - Steel Blue - Peach - Orange - Purple - Silver - Yellow - Pink - Red - Blue - Green - Cyan - Gold
|
|
/*#define NUMLINKCOLORS 14
|
|
static skincolors_t linkColor[NUMLINKCOLORS] =
|
|
{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
|
|
static skincolors_t linkColor[NUMLINKCOLORS] =
|
|
{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
|
|
static skincolors_t linkColor[NUMLINKCOLORS] =
|
|
{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
|
|
static skincolors_t linkColor[2][NUMLINKCOLORS] = {
|
|
{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}};
|
|
|
|
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);
|
|
skincolors_t colornum;
|
|
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];
|
|
|
|
aflag |= ((stplyr->linktimer < (UINT32)nightslinktics/3)
|
|
? (9 - 9*stplyr->linktimer/(nightslinktics/3)) << V_ALPHASHIFT
|
|
: 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)));
|
|
}
|
|
|
|
|
|
static void ST_drawNiGHTSHUD(void)
|
|
{
|
|
INT32 origamount;
|
|
INT32 total_spherecount, total_ringcount;
|
|
const boolean oldspecialstage = (G_IsSpecialStage(gamemap) && !(maptol & TOL_NIGHTS));
|
|
|
|
// Drill meter
|
|
if (
|
|
#ifdef HAVE_BLUA
|
|
LUA_HudEnabled(hud_nightsdrill) &&
|
|
#endif
|
|
stplyr->powers[pw_carry] == CR_NIGHTSMODE)
|
|
{
|
|
INT32 locx = 16, locy = 180;
|
|
INT32 dfill;
|
|
UINT8 fillpatch;
|
|
|
|
// Use which patch?
|
|
if (stplyr->pflags & PF_DRILLING)
|
|
fillpatch = (stplyr->drillmeter & 1) + 1;
|
|
else
|
|
fillpatch = 0;
|
|
|
|
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]);
|
|
|
|
// Display actual drill amount and bumper time
|
|
if (!splitscreen && (cv_debug & DBG_NIGHTSBASIC))
|
|
{
|
|
if (stplyr->bumpertime)
|
|
V_DrawString(locx, locy - 8, V_REDMAP|V_MONOSPACE, va("BUMPER: 0.%02d", G_TicsToCentiseconds(stplyr->bumpertime)));
|
|
else
|
|
V_DrawString(locx, locy - 8, V_MONOSPACE, va("Drill: %3d%%", (stplyr->drillmeter*100)/(96*20)));
|
|
}
|
|
}
|
|
|
|
/*if (G_IsSpecialStage(gamemap))
|
|
{ // Since special stages share score, time, rings, etc.
|
|
// disable splitscreen mode for its HUD.
|
|
// --------------------------------------
|
|
// NOPE! Consistency between different splitscreen stuffs
|
|
// now we've got the screen squashing instead. ~toast
|
|
if (stplyr != &players[displayplayer])
|
|
return;
|
|
nosshack = splitscreen;
|
|
splitscreen = false;
|
|
}*/
|
|
|
|
// Link drawing
|
|
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)
|
|
#ifdef HAVE_BLUA
|
|
&& LUA_HudEnabled(hud_nightslink)
|
|
#endif
|
|
&& ((cv_debug & DBG_NIGHTSBASIC) || stplyr->linkcount > 1)) // When debugging, show "0 Link".
|
|
{
|
|
ST_drawNiGHTSLink();
|
|
}
|
|
|
|
if (gametype == GT_RACE || gametype == GT_COMPETITION)
|
|
{
|
|
ST_drawScore();
|
|
ST_drawTime();
|
|
return;
|
|
}
|
|
|
|
// Begin drawing brackets/chip display
|
|
#ifdef HAVE_BLUA
|
|
if (LUA_HudEnabled(hud_nightsspheres))
|
|
{
|
|
#endif
|
|
ST_DrawTopLeftOverlayPatch(16, 8, nbracket);
|
|
if (G_IsSpecialStage(gamemap))
|
|
ST_DrawTopLeftOverlayPatch(24, 16, (
|
|
(stplyr->bonustime && (leveltime & 4) && (states[S_BLUESPHEREBONUS].frame & FF_ANIMATE)) ? nssbon : nsshud));
|
|
else
|
|
ST_DrawTopLeftOverlayPatch(24, 16, *(((stplyr->bonustime) ? nbon : nhud)+((leveltime/2)%12)));
|
|
|
|
if (G_IsSpecialStage(gamemap))
|
|
{
|
|
INT32 i;
|
|
total_spherecount = total_ringcount = 0;
|
|
for (i = 0; i < MAXPLAYERS; i++)
|
|
{
|
|
if (!playeringame[i])
|
|
continue;
|
|
total_spherecount += players[i].spheres;
|
|
total_ringcount += players[i].rings;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
total_spherecount = stplyr->spheres;
|
|
total_ringcount = stplyr->spheres;
|
|
}
|
|
|
|
if (stplyr->capsule)
|
|
{
|
|
INT32 amount;
|
|
const INT32 length = 88;
|
|
|
|
origamount = stplyr->capsule->spawnpoint->angle;
|
|
I_Assert(origamount > 0); // should not happen now
|
|
|
|
ST_DrawTopLeftOverlayPatch(72, 8, nbracket);
|
|
ST_DrawTopLeftOverlayPatch(74, 8 + 4, minicaps);
|
|
|
|
if (stplyr->capsule->reactiontime != 0)
|
|
{
|
|
INT32 r;
|
|
const INT32 orblength = 20;
|
|
|
|
for (r = 0; r < 5; r++)
|
|
{
|
|
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);
|
|
}
|
|
|
|
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;
|
|
|
|
V_DrawScaledPatch(69 + (7*t), 144, V_PERPLAYER|V_HUDTRANS, bluestat);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
INT32 cfill;
|
|
|
|
// Lil' white box!
|
|
V_DrawScaledPatch(15, 8 + 34, V_PERPLAYER|V_SNAPTOLEFT|V_SNAPTOTOP|V_HUDTRANS, capsulebar);
|
|
|
|
amount = (origamount - stplyr->capsule->health);
|
|
amount = (amount * length)/origamount;
|
|
|
|
for (cfill = 0; cfill < amount && cfill < length; ++cfill)
|
|
V_DrawScaledPatch(15 + cfill + 1, 8 + 35, V_PERPLAYER|V_SNAPTOLEFT|V_SNAPTOTOP|V_HUDTRANS, capsulefill);
|
|
}
|
|
|
|
if (total_spherecount >= stplyr->capsule->health)
|
|
ST_DrawTopLeftOverlayPatch(40, 8 + 5, nredar[leveltime&7]);
|
|
else
|
|
ST_DrawTopLeftOverlayPatch(40, 8 + 5, narrow[(leveltime/2)&7]);
|
|
}
|
|
else if (oldspecialstage && total_spherecount < (INT32)ssspheres)
|
|
{
|
|
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);
|
|
}
|
|
else
|
|
ST_DrawTopLeftOverlayPatch(40, 8 + 5, narrow[8]);
|
|
|
|
if (oldspecialstage)
|
|
{
|
|
// invert for s3k style junk
|
|
total_spherecount = ssspheres - total_spherecount;
|
|
if (total_spherecount < 0)
|
|
total_spherecount = 0;
|
|
|
|
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;
|
|
}
|
|
|
|
// 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);
|
|
}
|
|
|
|
if (total_spherecount >= 100)
|
|
V_DrawTallNum((total_spherecount >= 1000) ? 76 : 72, 8 + 11, V_PERPLAYER|V_SNAPTOTOP|V_SNAPTOLEFT|V_HUDTRANS, total_spherecount);
|
|
else
|
|
V_DrawTallNum(68, 8 + 11, V_PERPLAYER|V_SNAPTOTOP|V_SNAPTOLEFT|V_HUDTRANS, total_spherecount);
|
|
#ifdef HAVE_BLUA
|
|
}
|
|
#endif
|
|
|
|
// Score
|
|
if (!stplyr->exiting && !oldspecialstage
|
|
#ifdef HAVE_BLUA
|
|
&& LUA_HudEnabled(hud_nightsscore)
|
|
#endif
|
|
)
|
|
ST_DrawNightsOverlayNum(304<<FRACBITS, 14<<FRACBITS, FRACUNIT, V_PERPLAYER|V_SNAPTOTOP|V_SNAPTORIGHT, stplyr->marescore, nightsnum, SKINCOLOR_AZURE);
|
|
|
|
if (!stplyr->exiting
|
|
#ifdef HAVE_BLUA
|
|
// TODO give this its own section for Lua
|
|
&& LUA_HudEnabled(hud_nightsscore)
|
|
#endif
|
|
)
|
|
{
|
|
if (modeattacking == ATTACKING_NIGHTS)
|
|
{
|
|
INT32 maretime = max(stplyr->realtime - stplyr->marebegunat, 0);
|
|
|
|
#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);
|
|
if (maretime < 60*TICRATE)
|
|
V_DrawTallNum(BASEVIDWIDTH-46, BASEVIDHEIGHT-20, VFLAGS, G_TicsToSeconds(maretime));
|
|
else
|
|
{
|
|
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));
|
|
}
|
|
#undef VFLAGS
|
|
}
|
|
}
|
|
|
|
// Ideya time remaining
|
|
if (!stplyr->exiting && stplyr->nightstime > 0
|
|
#ifdef HAVE_BLUA
|
|
&& LUA_HudEnabled(hud_nightstime)
|
|
#endif
|
|
)
|
|
{
|
|
INT32 realnightstime = stplyr->nightstime/TICRATE;
|
|
INT32 numbersize;
|
|
UINT8 col = ((realnightstime < 10) ? SKINCOLOR_RED : SKINCOLOR_SUPERGOLD4);
|
|
|
|
if (G_IsSpecialStage(gamemap))
|
|
{
|
|
tic_t lowest_time = stplyr->nightstime;
|
|
INT32 i;
|
|
for (i = 0; i < MAXPLAYERS; i++)
|
|
if (playeringame[i] && players[i].powers[pw_carry] == CR_NIGHTSMODE && players[i].nightstime < lowest_time)
|
|
lowest_time = players[i].nightstime;
|
|
realnightstime = lowest_time/TICRATE;
|
|
}
|
|
|
|
if (stplyr->powers[pw_flashing] > TICRATE) // was hit
|
|
{
|
|
UINT16 flashingLeft = stplyr->powers[pw_flashing]-(TICRATE);
|
|
if (flashingLeft < TICRATE/2) // Start fading out
|
|
{
|
|
UINT32 fadingFlag = (9 - 9*flashingLeft/(TICRATE/2)) << V_ALPHASHIFT;
|
|
V_DrawTranslucentPatch(160 - (minus5sec->width/2), 28, V_PERPLAYER|fadingFlag, minus5sec);
|
|
}
|
|
else
|
|
V_DrawScaledPatch(160 - (minus5sec->width/2), 28, V_PERPLAYER, minus5sec);
|
|
}
|
|
|
|
if (realnightstime < 10)
|
|
numbersize = 16/2;
|
|
else if (realnightstime < 100)
|
|
numbersize = 32/2;
|
|
else
|
|
numbersize = 48/2;
|
|
|
|
if ((oldspecialstage && leveltime & 2)
|
|
&& (stplyr->mo->eflags & (MFE_TOUCHWATER|MFE_UNDERWATER))
|
|
&& !(stplyr->powers[pw_shield] & SH_PROTECTWATER))
|
|
col = SKINCOLOR_ORANGE;
|
|
|
|
ST_DrawNightsOverlayNum((160 + numbersize)<<FRACBITS, 14<<FRACBITS, FRACUNIT, V_PERPLAYER|V_SNAPTOTOP, realnightstime, nightsnum, col);
|
|
|
|
// Show exact time in debug
|
|
if (cv_debug & DBG_NIGHTSBASIC)
|
|
V_DrawString(160 + numbersize + 8, 24, V_SNAPTOTOP|((realnightstime < 10) ? V_REDMAP : V_YELLOWMAP), va("%02d", G_TicsToCentiseconds(stplyr->nightstime)));
|
|
}
|
|
|
|
if (oldspecialstage)
|
|
{
|
|
if (leveltime < 5*TICRATE)
|
|
{
|
|
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("\x80GET\x82 %d\x80 SPHERE%s!"), ssspheres,
|
|
(ssspheres > 1) ? "S" : ""));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Show pickup durations
|
|
if (cv_debug & DBG_NIGHTSBASIC)
|
|
{
|
|
UINT16 pwr;
|
|
|
|
if (stplyr->powers[pw_nights_superloop])
|
|
{
|
|
pwr = stplyr->powers[pw_nights_superloop];
|
|
V_DrawSmallScaledPatch(110, 44, 0, W_CachePatchName("NPRUA0",PU_CACHE));
|
|
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];
|
|
V_DrawSmallScaledPatch(150, 44, 0, W_CachePatchName("NPRUC0",PU_CACHE));
|
|
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];
|
|
V_DrawSmallScaledPatch(190, 44, 0, W_CachePatchName("NPRUE0",PU_CACHE));
|
|
V_DrawThinString(186, 52, V_MONOSPACE, va("%2d.%02d", pwr/TICRATE, G_TicsToCentiseconds(pwr)));
|
|
}
|
|
}
|
|
|
|
// Records/extra text
|
|
#ifdef HAVE_BLUA
|
|
if (LUA_HudEnabled(hud_nightsrecords))
|
|
#endif
|
|
ST_drawNightsRecords();
|
|
}
|
|
}
|
|
|
|
static void ST_drawWeaponSelect(INT32 xoffs, INT32 y)
|
|
{
|
|
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);
|
|
}
|
|
|
|
static void ST_drawWeaponRing(powertype_t weapon, INT32 rwflag, INT32 wepflag, INT32 xoffs, INT32 y, patch_t *pat)
|
|
{
|
|
INT32 txtflags = 0, patflags = 0;
|
|
|
|
if (stplyr->powers[weapon])
|
|
{
|
|
if (stplyr->powers[weapon] >= rw_maximums[wepflag])
|
|
txtflags |= V_YELLOWMAP;
|
|
|
|
if (weapon == pw_infinityring
|
|
|| (stplyr->ringweapons & rwflag))
|
|
; //txtflags |= V_20TRANS;
|
|
else
|
|
{
|
|
txtflags |= V_TRANSLUCENT;
|
|
patflags = V_80TRANS;
|
|
}
|
|
|
|
V_DrawScaledPatch(8 + xoffs, y, V_PERPLAYER|V_SNAPTOBOTTOM|patflags, pat);
|
|
V_DrawRightAlignedThinString(24 + xoffs, y + 8, V_PERPLAYER|V_SNAPTOBOTTOM|txtflags, va("%d", stplyr->powers[weapon]));
|
|
|
|
if (stplyr->currentweapon == wepflag)
|
|
ST_drawWeaponSelect(xoffs, y);
|
|
}
|
|
else if (stplyr->ringweapons & rwflag)
|
|
V_DrawScaledPatch(8 + xoffs, y, V_PERPLAYER|V_SNAPTOBOTTOM|V_TRANSLUCENT, pat);
|
|
}
|
|
|
|
static void ST_drawMatchHUD(void)
|
|
{
|
|
char penaltystr[7];
|
|
const INT32 y = 176; // HUD_LIVES
|
|
INT32 offset = (BASEVIDWIDTH / 2) - (NUM_WEAPONS * 10) - 6;
|
|
|
|
if (F_GetPromptHideHud(y))
|
|
return;
|
|
|
|
if (!G_RingSlingerGametype())
|
|
return;
|
|
|
|
if (G_TagGametype() && !(stplyr->pflags & PF_TAGIT))
|
|
return;
|
|
|
|
{
|
|
if (stplyr->powers[pw_infinityring])
|
|
ST_drawWeaponRing(pw_infinityring, 0, 0, offset, y, infinityring);
|
|
else
|
|
{
|
|
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);
|
|
|
|
if (!stplyr->currentweapon)
|
|
ST_drawWeaponSelect(offset, y);
|
|
}
|
|
|
|
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,
|
|
V_REDMAP, penaltystr);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
static void ST_drawTextHUD(void)
|
|
{
|
|
INT32 y = 42 + 16; // HUD_RINGS
|
|
boolean donef12 = false;
|
|
|
|
#define textHUDdraw(str) \
|
|
{\
|
|
V_DrawThinString(16, y, V_PERPLAYER|V_HUDTRANS|V_SNAPTOLEFT|V_SNAPTOBOTTOM, str);\
|
|
y += 8;\
|
|
}
|
|
|
|
if (F_GetPromptHideHud(y))
|
|
return;
|
|
|
|
if (stplyr->spectator && (gametype != GT_COOP || stplyr->playerstate == PST_LIVE))
|
|
textHUDdraw(M_GetText("\x86""Spectator mode:"))
|
|
|
|
if (circuitmap)
|
|
{
|
|
if (stplyr->exiting)
|
|
textHUDdraw(M_GetText("\x82""FINISHED!"))
|
|
else
|
|
textHUDdraw(va("Lap:""\x82 %u/%d", stplyr->laps+1, cv_numlaps.value))
|
|
}
|
|
|
|
if (gametype != GT_COOP && (stplyr->exiting || (G_GametypeUsesLives() && stplyr->lives <= 0 && countdown != 1)))
|
|
{
|
|
if (!splitscreen && !donef12)
|
|
{
|
|
textHUDdraw(M_GetText("\x82""VIEWPOINT:""\x80 Switch view"))
|
|
donef12 = true;
|
|
}
|
|
}
|
|
else if (!G_PlatformGametype() && stplyr->playerstate == PST_DEAD && stplyr->lives) // Death overrides spectator text.
|
|
{
|
|
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"))
|
|
}
|
|
else if (stplyr->spectator && (gametype != GT_COOP || stplyr->playerstate == PST_LIVE))
|
|
{
|
|
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"))
|
|
|
|
if (G_IsSpecialStage(gamemap))
|
|
textHUDdraw(M_GetText("\x82""Wait for the stage to end..."))
|
|
else if (gametype == GT_COOP)
|
|
{
|
|
if (stplyr->lives <= 0
|
|
&& cv_cooplives.value == 2
|
|
&& (netgame || multiplayer))
|
|
{
|
|
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..."))
|
|
}
|
|
else
|
|
textHUDdraw(M_GetText("Wait to respawn..."))
|
|
}
|
|
else
|
|
textHUDdraw(M_GetText("\x82""FIRE:""\x80 Enter game"))
|
|
}
|
|
|
|
if (gametype == GT_COOP && (!stplyr->spectator || (!(maptol & TOL_NIGHTS) && G_IsSpecialStage(gamemap))) && (stplyr->exiting || (stplyr->pflags & PF_FINISHED)))
|
|
{
|
|
UINT8 numneeded = (G_IsSpecialStage(gamemap) ? 4 : cv_playersforexit.value);
|
|
if (numneeded)
|
|
{
|
|
INT32 i, total = 0, exiting = 0;
|
|
|
|
for (i = 0; i < MAXPLAYERS; i++)
|
|
{
|
|
if (!playeringame[i] || players[i].spectator)
|
|
continue;
|
|
if (players[i].lives <= 0)
|
|
continue;
|
|
|
|
total++;
|
|
if (players[i].exiting || (players[i].pflags & PF_FINISHED))
|
|
exiting++;
|
|
}
|
|
|
|
if (numneeded != 4)
|
|
{
|
|
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")))
|
|
}
|
|
}
|
|
}
|
|
else if ((gametype == GT_TAG || gametype == GT_HIDEANDSEEK) && (!stplyr->spectator))
|
|
{
|
|
if (leveltime < hidetime * TICRATE)
|
|
{
|
|
if (stplyr->pflags & PF_TAGIT)
|
|
{
|
|
textHUDdraw(M_GetText("\x82""You are blindfolded!"))
|
|
textHUDdraw(M_GetText("Waiting for players to hide..."))
|
|
}
|
|
else if (gametype == GT_HIDEANDSEEK)
|
|
textHUDdraw(M_GetText("Hide before time runs out!"))
|
|
else
|
|
textHUDdraw(M_GetText("Flee before you are hunted!"))
|
|
}
|
|
else if (gametype == GT_HIDEANDSEEK && !(stplyr->pflags & PF_TAGIT))
|
|
{
|
|
if (!splitscreen && !donef12)
|
|
{
|
|
textHUDdraw(M_GetText("\x82""VIEWPOINT:""\x80 Switch view"))
|
|
donef12 = true;
|
|
}
|
|
textHUDdraw(M_GetText("You cannot move while hiding."))
|
|
}
|
|
}
|
|
|
|
#undef textHUDdraw
|
|
|
|
}
|
|
|
|
static inline void ST_drawRaceHUD(void)
|
|
{
|
|
if (leveltime > TICRATE && leveltime <= 5*TICRATE)
|
|
ST_drawRaceNum(4*TICRATE - leveltime);
|
|
}
|
|
|
|
static void ST_drawTeamHUD(void)
|
|
{
|
|
patch_t *p;
|
|
#define SEP 20
|
|
|
|
if (F_GetPromptHideHud(0)) // y base is 0
|
|
return;
|
|
|
|
if (gametype == GT_CTF)
|
|
p = bflagico;
|
|
else
|
|
p = bmatcico;
|
|
|
|
V_DrawSmallScaledPatch(BASEVIDWIDTH/2 - SEP - SHORT(p->width)/4, 4, V_HUDTRANS|V_PERPLAYER|V_SNAPTOTOP, p);
|
|
|
|
if (gametype == GT_CTF)
|
|
p = rflagico;
|
|
else
|
|
p = rmatcico;
|
|
|
|
V_DrawSmallScaledPatch(BASEVIDWIDTH/2 + SEP - SHORT(p->width)/4, 4, V_HUDTRANS|V_PERPLAYER|V_SNAPTOTOP, p);
|
|
|
|
if (gametype != GT_CTF)
|
|
goto num;
|
|
{
|
|
INT32 i;
|
|
UINT16 whichflag = 0;
|
|
|
|
// Show which flags aren't at base.
|
|
for (i = 0; i < MAXPLAYERS; i++)
|
|
{
|
|
if (players[i].gotflag & GF_BLUEFLAG) // Blue flag isn't at base
|
|
V_DrawScaledPatch(BASEVIDWIDTH/2 - SEP - SHORT(nonicon->width)/2, 0, V_HUDTRANS|V_PERPLAYER|V_SNAPTOTOP, nonicon);
|
|
if (players[i].gotflag & GF_REDFLAG) // Red flag isn't at base
|
|
V_DrawScaledPatch(BASEVIDWIDTH/2 + SEP - SHORT(nonicon2->width)/2, 0, V_HUDTRANS|V_PERPLAYER|V_SNAPTOTOP, nonicon2);
|
|
|
|
whichflag |= players[i].gotflag;
|
|
if ((whichflag & (GF_REDFLAG|GF_BLUEFLAG)) == (GF_REDFLAG|GF_BLUEFLAG))
|
|
break; // both flags were found, let's stop early
|
|
}
|
|
|
|
// Display a countdown timer showing how much time left until the flag returns to base.
|
|
{
|
|
if (blueflag && blueflag->fuse > 1)
|
|
V_DrawCenteredString(BASEVIDWIDTH/2 - SEP, 8, V_YELLOWMAP|V_HUDTRANS|V_PERPLAYER|V_SNAPTOTOP, va("%u", (blueflag->fuse / TICRATE)));
|
|
|
|
if (redflag && redflag->fuse > 1)
|
|
V_DrawCenteredString(BASEVIDWIDTH/2 + SEP, 8, V_YELLOWMAP|V_HUDTRANS|V_PERPLAYER|V_SNAPTOTOP, va("%u", (redflag->fuse / TICRATE)));
|
|
}
|
|
}
|
|
|
|
num:
|
|
V_DrawCenteredString(BASEVIDWIDTH/2 - SEP, 16, V_HUDTRANS|V_PERPLAYER|V_SNAPTOTOP, va("%u", bluescore));
|
|
V_DrawCenteredString(BASEVIDWIDTH/2 + SEP, 16, V_HUDTRANS|V_PERPLAYER|V_SNAPTOTOP, va("%u", redscore));
|
|
|
|
#undef SEP
|
|
}
|
|
|
|
/*static void ST_drawSpecialStageHUD(void)
|
|
{
|
|
if (ssspheres > 0)
|
|
{
|
|
if (hudinfo[HUD_SS_TOTALRINGS].x)
|
|
ST_DrawNumFromHud(HUD_SS_TOTALRINGS, ssspheres, V_HUDTRANS);
|
|
else if (cv_timetic.value == 2)
|
|
V_DrawTallNum(hudinfo[HUD_RINGSNUMTICS].x, hudinfo[HUD_SS_TOTALRINGS].y, hudinfo[HUD_RINGSNUMTICS].f|V_PERPLAYER|V_HUDTRANS, ssspheres);
|
|
else
|
|
V_DrawTallNum(hudinfo[HUD_RINGSNUM].x, hudinfo[HUD_SS_TOTALRINGS].y, hudinfo[HUD_RINGSNUM].f|V_PERPLAYER|V_HUDTRANS, ssspheres);
|
|
}
|
|
|
|
if (leveltime < 5*TICRATE && ssspheres > 0)
|
|
{
|
|
ST_DrawPatchFromHud(HUD_GETRINGS, getall, V_HUDTRANS);
|
|
ST_DrawNumFromHud(HUD_GETRINGSNUM, ssspheres, V_HUDTRANS);
|
|
}
|
|
|
|
if (sstimer)
|
|
{
|
|
V_DrawString(hudinfo[HUD_TIMELEFT].x, hudinfo[HUD_TIMELEFT].y, hudinfo[HUD_TIMELEFT].f|V_PERPLAYER|V_HUDTRANS, M_GetText("TIME LEFT"));
|
|
ST_DrawNumFromHud(HUD_TIMELEFTNUM, sstimer/TICRATE, V_HUDTRANS);
|
|
}
|
|
else
|
|
ST_DrawPatchFromHud(HUD_TIMEUP, timeup, V_HUDTRANS);
|
|
}*/
|
|
|
|
static INT32 ST_drawEmeraldHuntIcon(mobj_t *hunt, patch_t **patches, INT32 offset)
|
|
{
|
|
INT32 interval, i;
|
|
UINT32 dist = ((UINT32)P_AproxDistance(P_AproxDistance(stplyr->mo->x - hunt->x, stplyr->mo->y - hunt->y), stplyr->mo->z - hunt->z))>>FRACBITS;
|
|
|
|
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;
|
|
}
|
|
|
|
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]);
|
|
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;
|
|
}
|
|
|
|
if (!(P_AutoPause() || paused) && interval > 0 && leveltime && leveltime % interval == 0)
|
|
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
|
|
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
|
|
{
|
|
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
|
|
continue;
|
|
|
|
mo2 = (mobj_t *)th;
|
|
|
|
if (mo2->type != MT_EMBLEM)
|
|
continue;
|
|
|
|
if (!(mo2->flags & MF_SPECIAL))
|
|
continue;
|
|
|
|
for (i = 0; i < stemblems; ++i)
|
|
{
|
|
if (mo2->health == emblems[i] + 1)
|
|
{
|
|
soffset = (i * 20) - ((stemblems - 1) * 10);
|
|
|
|
newinterval = ST_drawEmeraldHuntIcon(mo2, itemhoming, soffset);
|
|
if (newinterval && (!interval || newinterval < interval))
|
|
interval = newinterval;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
if (!(P_AutoPause() || paused) && interval > 0 && leveltime && leveltime % interval == 0)
|
|
S_StartSound(NULL, sfx_emfind);
|
|
}
|
|
|
|
//
|
|
// Draw the status bar overlay, customisable: the user chooses which
|
|
// kind of information to overlay
|
|
//
|
|
static void ST_overlayDrawer(void)
|
|
{
|
|
// 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
|
|
if (!(mapheaderinfo[gamemap-1]->levelflags & LF_NOTITLECARD) && *mapheaderinfo[gamemap-1]->lvlttl != '\0' && !(hu_showscores && (netgame || multiplayer)))
|
|
{
|
|
stagetitle = true;
|
|
ST_preDrawTitleCard();
|
|
}
|
|
|
|
// hu_showscores = auto hide score/time/rings when tab rankings are shown
|
|
if (!(hu_showscores && (netgame || multiplayer)))
|
|
{
|
|
if ((maptol & TOL_NIGHTS || G_IsSpecialStage(gamemap)) &&
|
|
!F_GetPromptHideHudAll())
|
|
ST_drawNiGHTSHUD();
|
|
else
|
|
{
|
|
#ifdef HAVE_BLUA
|
|
if (LUA_HudEnabled(hud_score))
|
|
#endif
|
|
ST_drawScore();
|
|
#ifdef HAVE_BLUA
|
|
if (LUA_HudEnabled(hud_time))
|
|
#endif
|
|
ST_drawTime();
|
|
#ifdef HAVE_BLUA
|
|
if (LUA_HudEnabled(hud_rings))
|
|
#endif
|
|
ST_drawRings();
|
|
|
|
if (!modeattacking
|
|
#ifdef HAVE_BLUA
|
|
&& LUA_HudEnabled(hud_lives)
|
|
#endif
|
|
)
|
|
ST_drawLivesArea();
|
|
}
|
|
}
|
|
|
|
// GAME OVER hud
|
|
if ((gametype == GT_COOP)
|
|
&& (netgame || multiplayer)
|
|
&& (cv_cooplives.value == 0))
|
|
;
|
|
else if ((G_GametypeUsesLives() || gametype == GT_RACE) && stplyr->lives <= 0 && !(hu_showscores && (netgame || multiplayer)))
|
|
{
|
|
INT32 i = MAXPLAYERS;
|
|
INT32 deadtimer = stplyr->spectator ? TICRATE : (stplyr->deadtimer-(TICRATE<<1));
|
|
|
|
if ((gametype == GT_COOP)
|
|
&& (netgame || multiplayer)
|
|
&& (cv_cooplives.value != 1))
|
|
{
|
|
for (i = 0; i < MAXPLAYERS; i++)
|
|
{
|
|
if (!playeringame[i])
|
|
continue;
|
|
|
|
if (&players[i] == stplyr)
|
|
continue;
|
|
|
|
if (players[i].lives > 0)
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i == MAXPLAYERS && deadtimer >= 0)
|
|
{
|
|
INT32 lvlttlx = min(6*deadtimer, BASEVIDWIDTH/2);
|
|
UINT32 flags = V_PERPLAYER|(stplyr->spectator ? V_HUDTRANSHALF : V_HUDTRANS);
|
|
|
|
V_DrawScaledPatch(lvlttlx - 8, BASEVIDHEIGHT/2, flags, (countdown == 1 ? slidtime : slidgame));
|
|
V_DrawScaledPatch(BASEVIDWIDTH + 8 - lvlttlx, BASEVIDHEIGHT/2, flags, slidover);
|
|
}
|
|
}
|
|
|
|
if (G_GametypeHasTeams())
|
|
ST_drawTeamHUD();
|
|
|
|
if (!hu_showscores) // hide the following if TAB is held
|
|
{
|
|
// Countdown timer for Race Mode
|
|
if (countdown > 1)
|
|
{
|
|
tic_t time = countdown/TICRATE + 1;
|
|
if (time < 4)
|
|
ST_drawRaceNum(countdown);
|
|
else
|
|
{
|
|
tic_t num = time;
|
|
INT32 sz = SHORT(tallnum[0]->width)/2, width = 0;
|
|
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));
|
|
}
|
|
}
|
|
|
|
// If you are in overtime, put a big honkin' flashin' message on the screen.
|
|
if (G_RingSlingerGametype() && cv_overtime.value
|
|
&& (leveltime > (timelimitintics + TICRATE/2)) && cv_timelimit.value && (leveltime/TICRATE % 2 == 0))
|
|
V_DrawCenteredString(BASEVIDWIDTH/2, 184, V_PERPLAYER, M_GetText("OVERTIME!"));
|
|
|
|
// Draw Match-related stuff
|
|
//\note Match HUD is drawn no matter what gametype.
|
|
// ... just not if you're a spectator.
|
|
if (!stplyr->spectator
|
|
#ifdef HAVE_BLUA
|
|
&& (LUA_HudEnabled(hud_weaponrings))
|
|
#endif
|
|
)
|
|
ST_drawMatchHUD();
|
|
|
|
// Race HUD Stuff
|
|
if (gametype == GT_RACE || gametype == GT_COMPETITION)
|
|
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!
|
|
if (!(maptol & TOL_NIGHTS))
|
|
{
|
|
if ((stplyr == &players[displayplayer] && !camera.chase)
|
|
|| ((splitscreen && stplyr == &players[secondarydisplayplayer]) && !camera2.chase))
|
|
{
|
|
ST_drawFirstPersonHUD();
|
|
if (cv_powerupdisplay.value)
|
|
ST_drawPowerupHUD(); // same as it ever was...
|
|
}
|
|
else if (cv_powerupdisplay.value == 2)
|
|
ST_drawPowerupHUD(); // same as it ever was...
|
|
}
|
|
}
|
|
else if (!(netgame || multiplayer) && cv_powerupdisplay.value == 2)
|
|
ST_drawPowerupHUD(); // same as it ever was...
|
|
|
|
#ifdef HAVE_BLUA
|
|
if (!(netgame || multiplayer) || !hu_showscores)
|
|
LUAh_GameHUD(stplyr);
|
|
#endif
|
|
|
|
// draw level title Tails
|
|
if (stagetitle && (!WipeInAction) && (!WipeStageTitle))
|
|
ST_drawTitleCard();
|
|
|
|
if (!hu_showscores && (netgame || multiplayer)
|
|
#ifdef HAVE_BLUA
|
|
&& LUA_HudEnabled(hud_textspectator)
|
|
#endif
|
|
)
|
|
ST_drawTextHUD();
|
|
|
|
if (modeattacking && !(demoplayback && hu_showscores))
|
|
ST_drawInput();
|
|
|
|
ST_drawDebugInfo();
|
|
}
|
|
|
|
void ST_Drawer(void)
|
|
{
|
|
#ifdef SEENAMES
|
|
if (cv_seenames.value && cv_allowseenames.value && displayplayer == consoleplayer && seenplayer && seenplayer->mo)
|
|
{
|
|
INT32 c = 0;
|
|
switch (cv_seenames.value)
|
|
{
|
|
case 1: // Colorless
|
|
break;
|
|
case 2: // Team
|
|
if (G_GametypeHasTeams())
|
|
c = (seenplayer->ctfteam == 1) ? V_REDMAP : V_BLUEMAP;
|
|
break;
|
|
case 3: // Ally/Foe
|
|
default:
|
|
// 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;
|
|
break;
|
|
}
|
|
|
|
V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT/2 + 15, V_HUDTRANSHALF|c, player_names[seenplayer-players]);
|
|
}
|
|
#endif
|
|
|
|
// Doom's status bar only updated if necessary.
|
|
// However, ours updates every frame regardless, so the "refresh" param was removed
|
|
//(void)refresh;
|
|
|
|
// 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();
|
|
|
|
// Blindfold!
|
|
if ((gametype == GT_TAG || gametype == GT_HIDEANDSEEK)
|
|
&& (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);
|
|
}
|
|
}
|
|
|
|
st_translucency = cv_translucenthud.value;
|
|
|
|
if (st_overlay)
|
|
{
|
|
// No deadview!
|
|
stplyr = &players[displayplayer];
|
|
ST_overlayDrawer();
|
|
|
|
if (splitscreen)
|
|
{
|
|
stplyr = &players[secondarydisplayplayer];
|
|
ST_overlayDrawer();
|
|
}
|
|
}
|
|
}
|