Port and modify the driftgauge logic from HEP3

Some edits had to be made for this to work better over in Blan. Like due to things such as lack of fonts.
I also ported the V1 driftgauges over to this new system as well since people like those.
This commit is contained in:
NepDisk 2025-08-22 17:56:51 -04:00
parent 7eaca40696
commit a00dc2df90
6 changed files with 448 additions and 4 deletions

View file

@ -87,8 +87,8 @@
#define ASSET_HASH_TEXTURES_KART 0xb4211b2f32b6a291
#define ASSET_HASH_CHARS_KART 0x1e68a3e01aa5c68b
#define ASSET_HASH_MAPS_KART 0x38558ed00da41ce9
#define ASSET_HASH_MAIN_PK3 0x0b1ca4fd1a704d9c
#define ASSET_HASH_MAPPATCH_PK3 0x19ebc8123bd51191
#define ASSET_HASH_MAIN_PK3 0x7af0848f87e97fa1
#define ASSET_HASH_MAPPATCH_PK3 0xdd97b82ed2d0e062
#define ASSET_HASH_BONUSCHARS_KART 0x60e6f13d822a7461
#ifdef USE_PATCH_FILE
#define ASSET_HASH_PATCH_PK3 0x0000000000000000

View file

@ -920,11 +920,18 @@ static const char *blancredits[] = {
"\"Denny\" aka \"shephoron\"",
"\"White Mage (guy who picked up controller)\"",
"",
"\1New Graphics Creation",
"\1New Item Art",
"\"Spee\"",
"\"Jin\"",
"\"NepDisk\"",
"",
"\1New HUD Art",
"\"Spee\"",
"\"SuperJustinBros\"",
"\"Chearii\"",
"\"hayaunderscore\" aka \"DeltaKaynx\"",
"\"Achiiro\" aka \"achiiDev\"",
"",
"\1Testing",
"\"NepDisk\"",
"\"Alug\"",

View file

@ -40,6 +40,7 @@
#include "m_random.h"
#include "g_party.h"
#include "h_timers.h"
#include "v_video.h"
#define NUMPOSNUMS 10
#define NUMPOSFRAMES 7 // White, three blues, three reds
@ -92,6 +93,9 @@ consvar_t cv_darkitembox = CVAR_INIT ("darkitembox", "On", CV_SAVE, CV_OnOff, NU
consvar_t cv_showfinishedplayers = CVAR_INIT ("showfinishedplayers", "On", CV_SAVE, CV_OnOff, NULL);
consvar_t cv_fancyroulette = CVAR_INIT ("fancyroulette", "Off", CV_SAVE, CV_OnOff, NULL);
consvar_t cv_smoothposition = CVAR_INIT ("smoothposition", "On", CV_SAVE, CV_OnOff, NULL);
static CV_PossibleValue_t driftgauge_cons_t[] = {{0, "Off"}, {1, "Spee"}, {2, "Achii"}, {3, "Wifi"}, {4, "Chaotix"}, {5, "Numbers"}, {6, "Legacy"}, {7, "Legacy Small"}, {8, "Legacy Large Numbers"}, {9, "Large Num"}, {0, NULL}};
consvar_t cv_driftgauge = CVAR_INIT ("kartdriftgauge", "Off", CV_SAVE, driftgauge_cons_t, NULL);
consvar_t cv_driftgaugeoffset = CVAR_INIT ("kartdriftgaugeoffset", "-10", CV_SAVE|CV_FLOAT, CV_Signed, NULL);
static CV_PossibleValue_t HudColor_cons_t[MAXSKINCOLORS+1];
consvar_t cv_colorizedhudcolor = CVAR_INIT ("colorizedhudcolor", "Skin Color", CV_SAVE, HudColor_cons_t, NULL);
@ -137,6 +141,9 @@ static patch_t *kp_speedometersticker[2];
static patch_t *kp_speedometerlabel[4];
static patch_t *kp_kartzspeedo[25];
static patch_t *kp_driftgauge[12];
static patch_t *kp_driftgaugeparts[5];
static patch_t *kp_rankbumper;
static patch_t *kp_tinybumper[2];
static patch_t *kp_ranknobumpers;
@ -245,6 +252,8 @@ void K_RegisterKartHUDStuff(void)
CV_RegisterVar(&cv_showfinishedplayers);
CV_RegisterVar(&cv_fancyroulette);
CV_RegisterVar(&cv_smoothposition);
CV_RegisterVar(&cv_driftgauge);
CV_RegisterVar(&cv_driftgaugeoffset);
}
void K_LoadKartHUDGraphics(void)
@ -374,6 +383,31 @@ void K_LoadKartHUDGraphics(void)
// Speedometer Sticker Color
HU_UpdatePatch(&kp_speedometersticker[1], "SC_SMSTC");
// Driftgauge Stickers
//{0, "Spee"}, {1, "Achii"}, {2, "Wifi"}, {3, "Chaotix"}
HU_UpdatePatch(&kp_driftgauge[0], "K_DGAU0"); // Spee
HU_UpdatePatch(&kp_driftgauge[1], "K_DGAU1"); // Achii
HU_UpdatePatch(&kp_driftgauge[2], "K_WDGBG"); // Wifi
HU_UpdatePatch(&kp_driftgauge[3], "K_DGAU3"); // Chaotix
HU_UpdatePatch(&kp_driftgauge[4], "K_DGAU"); // Legacy
HU_UpdatePatch(&kp_driftgauge[5], "K_DGSU"); // Legacy Small
HU_UpdatePatch(&kp_driftgauge[6], "K_DGAUC0");
HU_UpdatePatch(&kp_driftgauge[7], "K_DGAUC1");
HU_UpdatePatch(&kp_driftgauge[8], "K_WDGBGC");
HU_UpdatePatch(&kp_driftgauge[9], "K_DGAUC3");
HU_UpdatePatch(&kp_driftgauge[10], "K_DCAU");
HU_UpdatePatch(&kp_driftgauge[11], "K_DCSU");
// Driftgauge Parts
HU_UpdatePatch(&kp_driftgaugeparts[0], "K_WDGM1");
HU_UpdatePatch(&kp_driftgaugeparts[1], "K_WDGM2");
HU_UpdatePatch(&kp_driftgaugeparts[2], "K_WDGM3");
HU_UpdatePatch(&kp_driftgaugeparts[3], "K_WDGM4");
HU_UpdatePatch(&kp_driftgaugeparts[4], "K_DGAU3M");
// Speedometer labels
HU_UpdatePatch(&kp_speedometerlabel[0], "SP_MKMH");
HU_UpdatePatch(&kp_speedometerlabel[1], "SP_MMPH");
@ -5068,6 +5102,10 @@ void K_drawKartHUD(void)
}
}
// Drift gauge should ideally be drawn behind other hud stuff, right?
// right?
K_DrawDriftGauge();
if (battlefullscreen && !freecam)
{
if (LUA_HudEnabled(hud_battlefullscreen))
@ -5250,3 +5288,359 @@ void K_drawKartHUD(void)
K_DrawClusterDebugger();
K_DrawDirectorDebugger();
}
// Thank you Haya....
// DRIFT GAUGE //
static INT32 driftskins[] =
{
SKINCOLOR_BLACK,
SKINCOLOR_SILVER,
SKINCOLOR_BLUEBELL,
SKINCOLOR_RASPBERRY,
SKINCOLOR_VIOLET,
};
static INT32 afterval[MAXPLAYERS];
static tic_t aftertime[MAXPLAYERS];
#define V_100TRANS V_TRANSLUCENT*2
static UINT8 lineofs[] = {0, 0, 2, 2, 0, 0};
static UINT8 colors[] = {103, 103, 99, 99, 103, 103};
static UINT8 oldcolors[] = {99, 99, 99, 99, 99, 103};
// The modified colors above look awful on SKINCOLOR BLACK so new tables.....
static UINT8 backcolors[] = {100, 100, 97, 97, 100, 100};
static UINT8 oldbackcolors[] = {97, 97, 97, 97, 97, 100};
#define BARWIDTH 46
#define BARWIDTH_HALF BARWIDTH/2
void K_ResetAfterImageValues(void)
{
for (INT32 i = 0; i < MAXPLAYERS; i++)
{
afterval[i] = 0;
aftertime[i] = 0;
}
}
// Based off https://github.com/GenericHeroGuy/ringracers-scripts/blob/master/src/sglua/Lua/HUD/driftgauge.lua
// Original script by GenericHeroGuy, graphics by Spee
void K_DrawDriftGauge(void)
{
// NEW SIN....
#define NEWSIN(x) FINESINE((x >> ANGLETOFINESHIFT) & FINEMASK)
// Reset stuff on level load
if (leveltime <= 1)
K_ResetAfterImageValues();
// Actually have it enabled?
if (!cv_driftgauge.value)
return;
// Not in RA
if (modeattacking != ATTACKING_NONE)
return;
// Make sure we actually have one, lmao
if (stplyr->mo == NULL || P_MobjWasRemoved(stplyr->mo))
return;
// Check for chasecam
// TODO: Check for this better ffs
if (!cv_chasecam[R_GetViewNumber()].value)
return;
// I WANT TO LIVE
if (stplyr->playerstate != PST_LIVE)
return;
mobj_t *mo = stplyr->mo;
vector3_t pos = {
R_InterpolateFixed(mo->old_x, mo->x) + mo->sprxoff,
R_InterpolateFixed(mo->old_y, mo->y) + mo->spryoff,
R_InterpolateFixed(mo->old_z, mo->z) + mo->sprzoff + FixedMul(cv_driftgaugeoffset.value, ((cv_driftgaugeoffset.value > 0) ? mo->scale : mapobjectscale)),
};
trackingResult_t res;
INT32 basex, basey, i = 0;
INT32 flags = V_SPLITSCREEN; // V_HUDTRANS does not like other transparent effects...
K_ObjectTracking(&res, &pos, false);
basex = res.x;
basey = res.y;
INT32 textx = basex + 4*FRACUNIT;
INT32 texty = basey + 6*FRACUNIT;
INT32 meterfont = OPPRNK_FONT;
switch (cv_driftgauge.value)
{
case 1: // Spee
// PASS THRU
case 2: // Achii
break;
case 3: // Wifi
textx = basex + 30*FRACUNIT;
texty = basey + 10*FRACUNIT;
meterfont = PINGNUM_FONT;
break;
case 4: // Chaotix
textx = basex - 18*FRACUNIT;
texty = basey - 8*FRACUNIT;
break;
case 5: // Numbers
textx = basex;
texty = basey;
break;
case 6: // Legacy
textx = basex + 30*FRACUNIT;
texty = basey;
meterfont = PINGNUM_FONT;
break;
case 7: // Legacy Small
textx = basex + 20*FRACUNIT;
texty = basey;
meterfont = PINGNUM_FONT;
break;
case 8: // Legacy Large Numbers
textx = basex + 30*FRACUNIT;
texty = basey;
meterfont = TALLNUM_FONT;
break;
case 9: // Large Numbers
textx = basex + 10*FRACUNIT;
texty = basey;
meterfont = TALLNUM_FONT;
break;
}
// afterimage
if (aftertime[stplyrnum])
{
if (aftertime[stplyrnum] <= leveltime)
{
aftertime[stplyrnum] = 0;
return;
}
INT32 trans = V_100TRANS - (V_10TRANS * (aftertime[stplyrnum] - leveltime));
UINT8 *cmap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_SILVER, GTC_CACHE);
if (meterfont == OPPRNK_FONT)
{
UINT8 numbers[3];
numbers[0] = ((afterval[stplyrnum] / 100) % 10);
numbers[1] = ((afterval[stplyrnum] / 10) % 10);
numbers[2] = (afterval[stplyrnum] % 10);
V_DrawFixedPatch(textx, texty, FRACUNIT, flags|trans, kp_facenum[numbers[0]], cmap);
V_DrawFixedPatch(textx+(6*FRACUNIT), texty, FRACUNIT, flags|trans, kp_facenum[numbers[1]], cmap);
V_DrawFixedPatch(textx+(12*FRACUNIT), texty, FRACUNIT, flags|trans, kp_facenum[numbers[2]], cmap);
}
else if (meterfont == PINGNUM_FONT)
{
V_DrawPingNumAtFixed(textx, texty, flags|trans, afterval[stplyrnum], cmap);
}
else if (meterfont == TALLNUM_FONT)
{
V_DrawPaddedTallColorNumAtFixed(textx, texty, flags|trans, afterval[stplyrnum], 3, cmap);
}
/*V_DrawStringScaledEx(
textx, texty,
FRACUNIT, FRACUNIT, FRACUNIT, FRACUNIT,
flags|trans|V_MONOSPACE, R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_WSUPER1, GTC_CACHE),
meterfont,
va("%03d", afterval[stplyrnum])
);*/
return;
}
else if (!stplyr->drift)
return;
INT32 driftval = K_GetKartDriftSparkValue(stplyr);
INT32 driftcharge = min(driftval*4, stplyr->driftcharge);
boolean rainbow = driftcharge >= driftval*4;
UINT8 level = min(4, (driftcharge / driftval) + 1);
UINT8 level2 = level == 0 ? 0 : level-1;
boolean colorhud = K_UseColorHud();
SINT8 clroffset = colorhud ? 6 : 0;
boolean legacy = (cv_driftgauge.value >= 6 && cv_driftgauge.value < 9);
UINT8 *cmap = R_GetTranslationColormap(TC_RAINBOW, driftskins[level], GTC_CACHE);
UINT8 *cmap2 = R_GetTranslationColormap(TC_RAINBOW, driftskins[level2], GTC_CACHE);
UINT8 *cmap3 = R_GetTranslationColormap(TC_DEFAULT, K_GetHudColor(), GTC_CACHE);
if (rainbow)
{
cmap = R_GetTranslationColormap(TC_RAINBOW, (1 + (leveltime % FIRSTSUPERCOLOR - 1)), GTC_CACHE);
cmap2 = cmap;
}
// Meter style
if (cv_driftgauge.value <= 2 || legacy)
{
UINT8 *barcolors = legacy ? oldcolors : colors;
SINT8 offset = (cv_driftgauge.value == 8) ? 4 : (cv_driftgauge.value < 6) ? 1 : 2;
if (cv_driftgauge.value == 7)
basex += 10*FRACUNIT;
// the base graphic
V_DrawFixedPatch(basex, basey, FRACUNIT, flags, kp_driftgauge[clroffset+cv_driftgauge.value-offset], colorhud ? cmap3 : NULL);
if (rainbow)
{
// HOT HOT HOT HOT HOOOOOOOT AAAAIIIIIIIIEEEEEEEEEEEEEEEEE
INT32 trans = abs(NEWSIN(leveltime*ANGLE_22h)/(4*FRACUNIT/10));
V_DrawFixedPatch(basex, basey, FRACUNIT, flags|(V_90TRANS - V_10TRANS*trans), kp_driftgauge[cv_driftgauge.value-offset], R_GetTranslationColormap(TC_BLINK, SKINCOLOR_RED, GTC_CACHE));
}
INT32 barx = basex - 22*FRACUNIT;
INT32 bary = basey + FRACUNIT*2;
INT32 barwidth = (cv_driftgauge.value == 7) ? BARWIDTH_HALF : BARWIDTH;
if (legacy)
{
barx -= FRACUNIT;
bary -= 3*FRACUNIT;
}
INT32 width = ((driftcharge % driftval) * barwidth) / driftval;
const char *patch = "~%03u";
for (i = 0; i < 6; i++)
{
INT32 ofs = lineofs[i]*FRACUNIT/2;
INT32 x = barx+ofs;
INT32 y = bary+i*FRACUNIT/2;
INT32 w = (max(0, min(width*FRACUNIT - ofs, barwidth*FRACUNIT - ofs*2))) / 64;
INT32 h = FRACUNIT/128;
if (legacy)
{
h += (FRACUNIT/128)*2; // 1024
}
if (driftskins[level2] == SKINCOLOR_BLACK)
{
barcolors = legacy ? oldbackcolors : backcolors;
}
// back
char fmt[4];
sprintf(fmt, patch, R_GetPaletteRemap(barcolors[i] + (level == 1 ? 8 : 0)));
V_DrawStretchyFixedPatch(x, y, (barwidth*FRACUNIT - ofs*2)/64, h, flags, W_CachePatchName(fmt, PU_CACHE), cmap2);
// front
if (!rainbow)
{
barcolors = legacy ? oldcolors: colors;
sprintf(fmt, patch, R_GetPaletteRemap(barcolors[i]));
V_DrawStretchyFixedPatch(x, y, w, h, flags, W_CachePatchName(fmt, PU_CACHE), cmap);
}
}
}
else if (cv_driftgauge.value == 3) // Wifi style
{
// the base graphic
V_DrawFixedPatch(basex, basey, FRACUNIT, flags, kp_driftgauge[clroffset+2], colorhud ? cmap3 : NULL);
if (rainbow)
{
// HOT HOT HOT HOT HOOOOOOOT AAAAIIIIIIIIEEEEEEEEEEEEEEEEE
INT32 trans = abs(NEWSIN(leveltime*ANGLE_22h)/(4*FRACUNIT/10));
V_DrawFixedPatch(basex, basey, FRACUNIT, flags|(V_90TRANS - V_10TRANS*trans), kp_driftgauge[2], R_GetTranslationColormap(TC_BLINK, SKINCOLOR_RED, GTC_CACHE));
}
const INT32 dsone = K_GetKartDriftSparkValueForStage(stplyr, 1);
const INT32 dstwo = K_GetKartDriftSparkValueForStage(stplyr, 2);
const INT32 dsthree = K_GetKartDriftSparkValueForStage(stplyr, 3);
const INT32 barx = basex + 6*FRACUNIT + 2*FRACUNIT;
const INT32 bary = basey - 8*FRACUNIT + 22*FRACUNIT;
// tier 1
fixed_t h = FixedDiv((max(0, min(driftcharge, dsone)) * 6*FRACUNIT), driftval*FRACUNIT);
V_SetClipRect(barx, bary-h, 4*FRACUNIT, 6*FRACUNIT, flags);
V_DrawFixedPatch(basex, basey, FRACUNIT, flags, kp_driftgaugeparts[0], cmap);
// tier 2
h = FixedDiv((max(0, min(driftcharge-dsone, dsone)) * 11*FRACUNIT), driftval*FRACUNIT);
V_SetClipRect(barx + 5*FRACUNIT, bary-h, 4*FRACUNIT, 11*FRACUNIT, flags);
V_DrawFixedPatch(basex, basey, FRACUNIT, flags, kp_driftgaugeparts[1], cmap);
// tier 3
h = FixedDiv((max(0, min(driftcharge-dstwo, dsone)) * 16*FRACUNIT), driftval*FRACUNIT);
V_SetClipRect(barx + 10*FRACUNIT, bary-h, 4*FRACUNIT, 16*FRACUNIT, flags);
V_DrawFixedPatch(basex, basey, FRACUNIT, flags, kp_driftgaugeparts[2], cmap);
// tier 4
h = FixedDiv((max(0, min(driftcharge-dsthree, dsone)) * 21*FRACUNIT), driftval*FRACUNIT);
V_SetClipRect(barx + 15*FRACUNIT, bary-h, 4*FRACUNIT, 21*FRACUNIT, flags);
V_DrawFixedPatch(basex, basey, FRACUNIT, flags, kp_driftgaugeparts[3], cmap);
V_ClearClipRect();
}
else if (cv_driftgauge.value == 4) // Chaotix style
{
// the base graphic
V_DrawFixedPatch(basex, basey, FRACUNIT, flags, kp_driftgauge[clroffset+3], colorhud ? cmap3 : NULL);
if (rainbow)
{
// HOT HOT HOT HOT HOOOOOOOT AAAAIIIIIIIIEEEEEEEEEEEEEEEEE
INT32 trans = abs(NEWSIN(leveltime*ANGLE_22h)/(4*FRACUNIT/10));
V_DrawFixedPatch(basex, basey, FRACUNIT, flags|(V_90TRANS - V_10TRANS*trans), kp_driftgauge[3], R_GetTranslationColormap(TC_BLINK, SKINCOLOR_RED, GTC_CACHE));
}
const INT32 barx = basex - 23*FRACUNIT;
const INT32 bary = basey - 7*FRACUNIT;
INT32 width = FixedDiv(((driftcharge % driftval) * 34*FRACUNIT), driftval*FRACUNIT);
// back
if (driftcharge >= (driftval-2))
V_DrawFixedPatch(basex, basey, FRACUNIT, flags, kp_driftgaugeparts[4], cmap2);
// front
if (!rainbow)
{
V_SetClipRect(barx, bary, width, 18*FRACUNIT, flags);
V_DrawFixedPatch(basex, basey, FRACUNIT, flags, kp_driftgaugeparts[4], cmap);
V_ClearClipRect();
}
}
// right, also draw a cool number
INT32 charge = driftcharge*100 / driftval;
if (meterfont == OPPRNK_FONT)
{
UINT8 numbers[3];
numbers[0] = ((charge / 100) % 10);
numbers[1] = ((charge / 10) % 10);
numbers[2] = (charge % 10);
V_DrawFixedPatch(textx, texty, FRACUNIT, flags, kp_facenum[numbers[0]], cmap);
V_DrawFixedPatch(textx+(6*FRACUNIT), texty, FRACUNIT, flags, kp_facenum[numbers[1]], cmap);
V_DrawFixedPatch(textx+(12*FRACUNIT), texty, FRACUNIT, flags, kp_facenum[numbers[2]], cmap);
}
else if (meterfont == PINGNUM_FONT)
{
V_DrawPingNumAtFixed(textx, texty, flags, charge, cmap);
}
else if (meterfont == TALLNUM_FONT)
{
V_DrawPaddedTallColorNumAtFixed(textx, texty, flags, charge, 3, cmap);
}
/*V_DrawStringScaledEx(
textx, texty,
FRACUNIT, FRACUNIT, FRACUNIT, FRACUNIT,
flags|V_MONOSPACE, cmap,
meterfont,
va("%03d", charge)
);*/
// and trigger the afterimage
if (stplyr->pflags & PF_DRIFTEND)
{
afterval[stplyrnum] = charge;
aftertime[stplyrnum] = leveltime + 10;
}
else
aftertime[stplyrnum] = 0;
#undef NEWSIN
}

View file

@ -98,7 +98,8 @@ void K_drawKartHUD(void);
void K_drawKartFreePlay(void);
void K_drawKartTimestamp(tic_t drawtime, INT32 TX, INT32 TY, INT16 emblemmap, UINT8 mode);
void K_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, INT32 whiteplayer, INT32 hilicol);
void K_DrawDriftGauge(void);
void K_ResetAfterImageValues(void);
#ifdef __cplusplus
} // extern "C"
#endif

View file

@ -3170,6 +3170,27 @@ INT32 V_DrawPingNum(INT32 x, INT32 y, INT32 flags, INT32 num, const UINT8 *color
return x;
}
INT32 V_DrawPingNumAtFixed(fixed_t x, fixed_t y, INT32 flags, INT32 num, const UINT8 *colormap)
{
INT32 w = SHORT(fontv[PINGNUM_FONT].font[0]->width); // this SHOULD always be 5 but I guess custom graphics exist.
if (flags & V_NOSCALESTART)
w *= vid.dupx;
if (num < 0)
num = -num;
// draw the number
do
{
x -= (w-1)*FRACUNIT; // Oni wanted their outline to intersect.
V_DrawFixedPatch(x, y, FRACUNIT, flags, fontv[PINGNUM_FONT].font[num%10], colormap);
num /= 10;
} while (num);
return x;
}
// Jaden: Draw a number using the position numbers.
//
void V_DrawRankNum(INT32 x, INT32 y, INT32 flags, INT32 num, INT32 digits, const UINT8 *colormap)
@ -3261,6 +3282,25 @@ void V_DrawPaddedTallNum(INT32 x, INT32 y, INT32 flags, INT32 num, INT32 digits)
} while (--digits);
}
void V_DrawPaddedTallColorNumAtFixed(fixed_t x, fixed_t y, INT32 flags, INT32 num, INT32 digits, const UINT8 *colormap)
{
INT32 w = fontv[TALLNUM_FONT].font[0]->width;
if (flags & V_NOSCALESTART)
w *= vid.dupx;
if (num < 0)
num = -num;
// draw the number
do
{
x -= w*FRACUNIT;
V_DrawFixedPatch(x, y, FRACUNIT, flags, fontv[TALLNUM_FONT].font[num % 10], colormap);
num /= 10;
} while (--digits);
}
// Find string width from lt_font chars
//
INT32 V_LevelNameWidth(const char *string)

View file

@ -334,10 +334,12 @@ INT32 V_TitleCardStringWidth(const char *str);
// Draw tall nums, used for menu, HUD, intermission
void V_DrawTallNum(INT32 x, INT32 y, INT32 flags, INT32 num);
void V_DrawPaddedTallNum(INT32 x, INT32 y, INT32 flags, INT32 num, INT32 digits);
void V_DrawPaddedTallColorNumAtFixed(fixed_t x, fixed_t y, INT32 flags, INT32 num, INT32 digits, const UINT8 *colormap);
// Draw ping numbers. Used by the scoreboard and that one ping option. :P
// This is a separate function because IMO lua should have access to it as well.
INT32 V_DrawPingNum(INT32 x, INT32 y, INT32 flags, INT32 num, const UINT8 *colormap);
INT32 V_DrawPingNumAtFixed(fixed_t x, fixed_t y, INT32 flags, INT32 num, const UINT8 *colormap);
// Rank numbers.
void V_DrawRankNum(INT32 x, INT32 y, INT32 flags, INT32 num, INT32 digits, const UINT8 *colormap);