Item Timers PT.1
This commit is contained in:
parent
099bdb1be0
commit
8b15a9ebc2
9 changed files with 1049 additions and 122 deletions
|
|
@ -129,4 +129,5 @@ k_director.c
|
|||
k_follower.c
|
||||
k_mapuser.c
|
||||
k_stats.c
|
||||
h_timers.cpp
|
||||
stun.c
|
||||
|
|
|
|||
486
src/h_timers.cpp
Normal file
486
src/h_timers.cpp
Normal file
|
|
@ -0,0 +1,486 @@
|
|||
// BLANKART
|
||||
//-----------------------------------------------------------------------------
|
||||
// Copyright (C) 2025 by haya3218.
|
||||
// Copyright (C) 2018-2024 by Kart Krew
|
||||
//
|
||||
// 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 h_timers.cpp
|
||||
/// \brief C++ port of Home's item timers
|
||||
|
||||
#include "h_timers.h"
|
||||
#include "k_hud.h"
|
||||
#include "r_draw.h"
|
||||
#include "z_zone.h"
|
||||
#include "v_video.h"
|
||||
#include "k_kart.h"
|
||||
#include "k_color.h"
|
||||
#include "g_game.h"
|
||||
#include "p_local.h"
|
||||
#include "k_kart.h"
|
||||
#include "g_game.h"
|
||||
#include "r_main.h"
|
||||
#include "d_netcmd.h"
|
||||
#include "m_easing.h"
|
||||
#include "k_boss.h"
|
||||
#include "st_stuff.h"
|
||||
#include "lua_hud.h"
|
||||
#include "r_main.h"
|
||||
#include "r_fps.h"
|
||||
#include "s_sound.h"
|
||||
#include "fastcmp.h"
|
||||
#include "m_cond.h"
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
consvar_t cv_itemtimers = CVAR_INIT ("itemtimers", "On", CV_SAVE, CV_OnOff, NULL);
|
||||
|
||||
typedef struct itimer_s
|
||||
{
|
||||
const char* name; // name of timer
|
||||
INT32 timer; // current time
|
||||
std::vector<patch_t*> patches; // timer graphics (big/freeplay)
|
||||
std::vector<patch_t*> patches_small; // timer graphics (small)
|
||||
INT32 anim_frames; // tic duration for each graphic
|
||||
INT32 xoffs = 0, yoffs = 0; // offsets for freeplay mode
|
||||
INT32 xoffs_small = 0, yoffs_small = 0; // offsets for normal mode
|
||||
boolean counter; // not a timer, show a counter instead
|
||||
boolean badtimer; // timer is colored red
|
||||
INT32 *timer_p; // if defined, uses a pointer for the timer instead
|
||||
} itimer_t;
|
||||
|
||||
static bool sorttimers(itimer_t a, itimer_t b)
|
||||
{
|
||||
return a.timer < b.timer;
|
||||
}
|
||||
|
||||
static inline INT32 G_TicsToDeciseconds(tic_t tics)
|
||||
{
|
||||
return (INT32)((tics%TICRATE) * (10.00f/TICRATE));
|
||||
}
|
||||
|
||||
tic_t spbTimers[MAXPLAYERS] = {0};
|
||||
|
||||
UINT8 K_GetBestRank(void)
|
||||
{
|
||||
UINT8 best = UINT8_MAX;
|
||||
INT32 i;
|
||||
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
if (!playeringame[i] || players[i].exiting || players[i].spectator)
|
||||
continue;
|
||||
best = std::min<UINT8>(best, players[i].position);
|
||||
}
|
||||
|
||||
return best;
|
||||
}
|
||||
|
||||
// SPB Timer related
|
||||
void K_UpdateSPBTimer(void)
|
||||
{
|
||||
if (!(cv_itemtimers.value))
|
||||
{
|
||||
// no need to attempt to account for the timers
|
||||
memset(spbTimers, 0, sizeof spbTimers);
|
||||
return;
|
||||
}
|
||||
INT32 i, bestrank = 255;
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
if (!playeringame[i] || players[i].exiting || players[i].spectator)
|
||||
continue;
|
||||
spbTimers[i] = 0;
|
||||
bestrank = std::min<INT32>(bestrank, players[i].position);
|
||||
|
||||
// check for spb
|
||||
mobj_t *mobj, *next;
|
||||
for (mobj = kitemcap; mobj; mobj = next)
|
||||
{
|
||||
next = mobj->itnext;
|
||||
|
||||
// Look I don need other shit here besides the SPB
|
||||
if (mobj->type != MT_SPB)
|
||||
continue;
|
||||
|
||||
// Don't bother SPBs with no player yet
|
||||
if (mobj->tracer == NULL || P_MobjWasRemoved(mobj->tracer))
|
||||
continue;
|
||||
if (mobj->tracer->player == NULL)
|
||||
continue;
|
||||
|
||||
player_t *spbplayer = mobj->tracer->player;
|
||||
if (spbplayer->position > bestrank)
|
||||
{
|
||||
spbTimers[spbplayer-players] = mobj->extravalue2;
|
||||
if (players[i].position == bestrank)
|
||||
spbTimers[i] = mobj->extravalue2;
|
||||
else if (&players[i] != spbplayer)
|
||||
spbTimers[i] = 0;
|
||||
}
|
||||
else
|
||||
spbTimers[i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static std::unordered_map<const char*, patch_t*> cachedPatches;
|
||||
|
||||
static patch_t *qche(const char* p)
|
||||
{
|
||||
if (cachedPatches.find(p) != cachedPatches.end())
|
||||
return cachedPatches[p];
|
||||
cachedPatches[p] = (patch_t*)W_CachePatchLongName(p, PU_CACHE);
|
||||
return cachedPatches[p];
|
||||
}
|
||||
|
||||
static std::vector<itimer_t> addTimers;
|
||||
static std::vector<itimer_t> addTimers_unsorted;
|
||||
|
||||
void K_AddItemTimerEx(
|
||||
const char *name,
|
||||
INT32 *timer,
|
||||
char **patches, char **patches_small,
|
||||
INT32 patches_size, INT32 patches_small_size,
|
||||
INT32 anim_duration,
|
||||
INT32 xoffs, INT32 yoffs,
|
||||
INT32 xoffs_small, INT32 yoffs_small,
|
||||
boolean counter, boolean unsorted)
|
||||
{
|
||||
itimer_t t; // woo yeah baby
|
||||
|
||||
t.name = name;
|
||||
t.timer_p = timer;
|
||||
INT32 i;
|
||||
|
||||
for (i = 0; i < patches_size; i++)
|
||||
t.patches.push_back(qche(patches[i]));
|
||||
for (i = 0; i < patches_small_size; i++)
|
||||
t.patches_small.push_back(qche(patches[i]));
|
||||
|
||||
t.anim_frames = anim_duration;
|
||||
t.xoffs = xoffs; t.yoffs = yoffs;
|
||||
t.xoffs_small = xoffs_small; t.yoffs_small = yoffs_small;
|
||||
t.counter = counter;
|
||||
|
||||
// yeah
|
||||
if (unsorted)
|
||||
addTimers_unsorted.push_back(t);
|
||||
else
|
||||
addTimers.push_back(t);
|
||||
}
|
||||
|
||||
void K_DisplayItemTimers(void)
|
||||
{
|
||||
if (!cv_itemtimers.value) return;
|
||||
// nooope
|
||||
if (stplyr->mo == NULL || P_MobjWasRemoved(stplyr->mo)) return;
|
||||
// I love and hate you C++
|
||||
std::vector<itimer_t> timers =
|
||||
{
|
||||
{ // sneaker
|
||||
"shoe",
|
||||
stplyr->sneakertimer,
|
||||
{qche("K_ISSHOE")},
|
||||
{qche("HL_SNKR")},
|
||||
1, -15, -15, -4, -2,
|
||||
},
|
||||
{ // invinc
|
||||
"invincible",
|
||||
stplyr->invincibilitytimer,
|
||||
{qche("K_ISINV1"), qche("K_ISINV2"), qche("K_ISINV3"), qche("K_ISINV4"), qche("K_ISINV5"), qche("K_ISINV6")},
|
||||
{qche("HL_INVNC")},
|
||||
3, -15, -15, -4, -2,
|
||||
},
|
||||
{ // grow
|
||||
"grow",
|
||||
std::max<INT16>(0, stplyr->growshrinktimer),
|
||||
{qche("K_ISGROW")},
|
||||
{qche("HL_GROW")},
|
||||
1, -15, -15, -4, -2,
|
||||
},
|
||||
{ // rocket sneakers
|
||||
"rocketsneakers",
|
||||
stplyr->rocketsneakertimer,
|
||||
{qche("K_ISRSHE")},
|
||||
{qche("ISPYRSHE")},
|
||||
1, -15, -15, -4, -2,
|
||||
},
|
||||
{ // hyudoro
|
||||
"hyudoro",
|
||||
stplyr->hyudorotimer,
|
||||
{qche("K_ISHYUD")},
|
||||
{qche("HL_HYU")},
|
||||
1, -15, -15, -4, -2,
|
||||
},
|
||||
{ // drift boost
|
||||
"driftsparkboost",
|
||||
stplyr->driftboost,
|
||||
{qche("IT_ORB0"), qche("IT_ORB1"), qche("IT_ORB2"), qche("IT_ORB3"), qche("IT_ORB4"), qche("IT_ORB3"), qche("IT_ORB2"), qche("IT_ORB1")},
|
||||
{qche("MSO_SPH1"), qche("MSO_SPH2"), qche("MSO_SPH3"), qche("MSO_SPH4")},
|
||||
3,
|
||||
},
|
||||
{ // start boost
|
||||
"startboost",
|
||||
stplyr->startboost,
|
||||
{qche("K_ISSTB")},
|
||||
{qche("K_SSSTB")},
|
||||
1,
|
||||
},
|
||||
{ // ring boost
|
||||
"ringboost",
|
||||
stplyr->ringboost,
|
||||
{qche("IT_RING")},
|
||||
{qche("MSO_RNG")},
|
||||
1,
|
||||
},
|
||||
};
|
||||
|
||||
// insert sortable timers
|
||||
timers.insert(timers.end(), addTimers.begin(), addTimers.end());
|
||||
|
||||
// Sort them based on time
|
||||
std::sort(timers.begin(), timers.end(), sorttimers);
|
||||
|
||||
// Spinout and shrink are always last
|
||||
timers.push_back(
|
||||
{ // spinout
|
||||
"spinout",
|
||||
std::max<UINT16>(stplyr->spinouttimer, stplyr->wipeoutslow),
|
||||
{qche("K_DIZZ1"), qche("K_DIZZ2"), qche("K_DIZZ3"), qche("K_DIZZ4")},
|
||||
{qche("MSO_HRT1"), qche("MSO_HRT2"), qche("MSO_HRT3"), qche("MSO_HRT4")},
|
||||
3, .badtimer = true,
|
||||
}
|
||||
);
|
||||
timers.push_back(
|
||||
{ // shrink
|
||||
"shrink",
|
||||
std::max<INT16>(0, -stplyr->growshrinktimer),
|
||||
{qche("K_ISSHRK")},
|
||||
{qche("HL_SHRNK")},
|
||||
3, -15, -15, -4, -3, .badtimer = true,
|
||||
}
|
||||
);
|
||||
timers.push_back(
|
||||
{ // spb
|
||||
"blueshell",
|
||||
(INT32)spbTimers[stplyr-players],
|
||||
{qche("IT_SPB1"), qche("IT_SPB2")},
|
||||
{qche("IT_SPBS1"), qche("IT_SPBS2")},
|
||||
1, .badtimer = (stplyr->position == K_GetBestRank()),
|
||||
}
|
||||
);
|
||||
// Same with boost stacks
|
||||
timers.push_back(
|
||||
{ // boosts
|
||||
"boosts",
|
||||
K_StackingActive() ? stplyr->numboosts : 0,
|
||||
{qche("IT_BOOST0"), qche("IT_BOOST1"), qche("IT_BOOST2"), qche("IT_BOOST3"), qche("IT_BOOST4"), qche("IT_BOOST3"), qche("IT_BOOST2"), qche("IT_BOOST1")},
|
||||
{qche("MSO_SPD1"), qche("MSO_SPD2"), qche("MSO_SPD3"), qche("MSO_SPD4"), qche("MSO_SPD3"), qche("MSO_SPD2")},
|
||||
3, .counter = true,
|
||||
}
|
||||
);
|
||||
|
||||
timers.push_back(
|
||||
{ // dead
|
||||
"spr_realisticexplosion",
|
||||
stplyr->deadtimer ? std::max<INT32>(0, TICRATE - stplyr->deadtimer) : 0,
|
||||
{qche("IT_DED1"), qche("IT_DED2"), qche("IT_DED3"), qche("IT_DED4"), qche("IT_DED5"), qche("IT_DED6"), qche("IT_DED7"), qche("IT_DED8")},
|
||||
{qche("IT_DEAD1"), qche("IT_DEAD2"), qche("IT_DEAD3"), qche("IT_DEAD4"), qche("IT_DEAD5"), qche("IT_DEAD6"), qche("IT_DEAD7"), qche("IT_DEAD8")},
|
||||
1, .yoffs = -4, .badtimer = true,
|
||||
}
|
||||
);
|
||||
|
||||
// insert unsortable timers
|
||||
timers.insert(timers.end(), addTimers_unsorted.begin(), addTimers_unsorted.end());
|
||||
|
||||
if ((K_NotFreePlay() && !modeattacking) || r_splitscreen > 0) // "HOME" style, with icons at the bottom
|
||||
{
|
||||
INT32 itemx = 160, itemy = 160;
|
||||
INT32 flags = V_SNAPTOBOTTOM|V_SLIDEIN|V_SPLITSCREEN;
|
||||
INT32 stepx = 25;
|
||||
INT32 viewnum = R_GetViewNumber();
|
||||
|
||||
if (r_splitscreen == 1)
|
||||
{
|
||||
flags &= ~V_SNAPTOBOTTOM;
|
||||
itemy = 75;
|
||||
}
|
||||
else if (r_splitscreen > 1)
|
||||
{
|
||||
flags &= ~V_SNAPTOBOTTOM;
|
||||
itemy = 75;
|
||||
itemx = 80;
|
||||
}
|
||||
|
||||
if (r_splitscreen > 0)
|
||||
flags |= V_30TRANS;
|
||||
|
||||
// Move it up to account for viewpoint text
|
||||
if (cv_showviewpointtext.value && !demo.title && !P_IsLocalPlayer(stplyr) && !camera[viewnum].freecam && !r_splitscreen)
|
||||
itemy -= 12;
|
||||
|
||||
// flags |= V_HUDTRANS;
|
||||
|
||||
INT32 offs = -stepx;
|
||||
for (itimer_t& t : timers)
|
||||
{
|
||||
INT32 timer = t.timer;
|
||||
if (t.timer_p) timer = *t.timer_p;
|
||||
if (stplyr->deadtimer && !fastcmp(t.name, "spr_realisticexplosion")) continue;
|
||||
if (timer > 0)
|
||||
offs += stepx;
|
||||
}
|
||||
|
||||
itemx -= offs/2;
|
||||
// draw relevant timers
|
||||
for (itimer_t& t : timers)
|
||||
{
|
||||
if (t.timer <= 0) continue;
|
||||
if (stplyr->deadtimer && !fastcmp(t.name, "spr_realisticexplosion")) continue;
|
||||
INT32 timer = t.timer;
|
||||
if (t.timer_p) timer = *t.timer_p;
|
||||
INT32 seconds = timer / TICRATE, _centiseconds = G_TicsToCentiseconds(timer);
|
||||
|
||||
const char* str = va("%d.%02d", seconds, _centiseconds);
|
||||
if (seconds > 9) // exceeds 9 seconds?
|
||||
str = va("%d.%01d", seconds, G_TicsToDeciseconds(timer));
|
||||
if (seconds > 99) // exceeds 100 seconds?
|
||||
str = va("%d", seconds);
|
||||
INT32 animsize = t.patches_small.size();
|
||||
INT32 patchnum = (leveltime % (t.anim_frames * animsize) / t.anim_frames);
|
||||
UINT8 *cmap = NULL, *textcmap = NULL;
|
||||
INT32 font = TINY_FONT;
|
||||
|
||||
if (fastcmp(t.name, "driftsparkboost"))
|
||||
{
|
||||
if (timer > 85) // rainbow
|
||||
cmap = R_GetTranslationColormap(TC_DEFAULT, (skincolornum_t)K_RainbowColor(leveltime), GTC_CACHE);
|
||||
else if (timer > 50 && K_PurpleDriftActive()) // purple
|
||||
cmap = R_GetTranslationColormap(TC_DEFAULT, SKINCOLOR_PURPLE, GTC_CACHE);
|
||||
else if (timer > 25) // ketchup
|
||||
cmap = R_GetTranslationColormap(TC_DEFAULT, SKINCOLOR_KETCHUP, GTC_CACHE);
|
||||
else // blu
|
||||
cmap = R_GetTranslationColormap(TC_DEFAULT, SKINCOLOR_SAPPHIRE, GTC_CACHE);
|
||||
textcmap = cmap;
|
||||
}
|
||||
|
||||
if (fastcmp(t.name, "invincibilitytimer"))
|
||||
{
|
||||
cmap = R_GetTranslationColormap(TC_RAINBOW, (skincolornum_t)K_RainbowColor(leveltime), GTC_CACHE);
|
||||
}
|
||||
|
||||
if (t.counter) // don't show up as time
|
||||
{
|
||||
str = va("%d", timer);
|
||||
font = HU_FONT;
|
||||
}
|
||||
INT32 width = V_StringScaledWidth(FRACUNIT, FRACUNIT, FRACUNIT, flags|V_MONOSPACE, font, str) >> FRACBITS;
|
||||
|
||||
// very bad!
|
||||
if (t.badtimer)
|
||||
textcmap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_CRIMSON, GTC_CACHE);
|
||||
|
||||
patch_t *item = t.patches_small[patchnum];
|
||||
V_DrawFixedPatch((itemx - (item->width/2))<<FRACBITS, (itemy + t.yoffs_small)<<FRACBITS, FRACUNIT, flags, t.patches_small[patchnum], cmap);
|
||||
V_DrawStringScaledEx(
|
||||
(itemx - (width/2) - item->leftoffset) << FRACBITS,
|
||||
(itemy + 12) << FRACBITS,
|
||||
FRACUNIT,
|
||||
FRACUNIT,
|
||||
FRACUNIT,
|
||||
FRACUNIT,
|
||||
flags|V_MONOSPACE,
|
||||
textcmap,
|
||||
font,
|
||||
str
|
||||
);
|
||||
|
||||
itemx += stepx;
|
||||
}
|
||||
}
|
||||
else // FREEPLAY - Move these to the left side, where the rankings usually are
|
||||
{
|
||||
if (r_splitscreen) // How?
|
||||
return;
|
||||
|
||||
INT32 fx = 9, fy = 92, step = 20;
|
||||
INT32 flags = V_SNAPTOLEFT|V_SLIDEIN;
|
||||
|
||||
// center it
|
||||
INT32 offs = -step;
|
||||
for (itimer_t& t : timers)
|
||||
{
|
||||
INT32 timer = t.timer;
|
||||
if (t.timer_p) timer = *t.timer_p;
|
||||
if (stplyr->deadtimer && !fastcmp(t.name, "spr_realisticexplosion")) continue;
|
||||
if (timer > 0)
|
||||
offs += step;
|
||||
}
|
||||
fy -= offs/2;
|
||||
|
||||
// draw relevant timers
|
||||
for (itimer_t& t : timers)
|
||||
{
|
||||
if (t.timer <= 0) continue;
|
||||
if (stplyr->deadtimer && !fastcmp(t.name, "spr_realisticexplosion")) continue;
|
||||
INT32 timer = t.timer;
|
||||
if (t.timer_p) timer = *t.timer_p;
|
||||
INT32 seconds = timer / TICRATE, _centiseconds = G_TicsToCentiseconds(timer);
|
||||
INT32 patchnum = (leveltime % (t.anim_frames * t.patches.size()) / t.anim_frames);
|
||||
UINT8 *cmap = NULL, *textcmap = NULL;
|
||||
|
||||
const char* str = va("%d.%02d", seconds, _centiseconds);
|
||||
if (seconds > 9) // exceeds 9 seconds?
|
||||
str = va("%d.%01d", seconds, G_TicsToDeciseconds(timer));
|
||||
if (seconds > 99) // exceeds 100 seconds?
|
||||
str = va("%d", seconds);
|
||||
boolean bost = false;
|
||||
if (fastcmp(t.name, "boosts")) // don't show up as time
|
||||
{
|
||||
str = va("%d", timer);
|
||||
bost = true;
|
||||
}
|
||||
INT32 width = V_StringScaledWidth(FRACUNIT, FRACUNIT, FRACUNIT, flags|V_MONOSPACE, OPPRNK_FONT, str) >> FRACBITS;
|
||||
|
||||
if (fastcmp(t.name, "driftsparkboost"))
|
||||
{
|
||||
if (timer > 85) // rainbow
|
||||
cmap = R_GetTranslationColormap(TC_DEFAULT, (skincolornum_t)K_RainbowColor(leveltime), GTC_CACHE);
|
||||
else if (timer > 50 && K_PurpleDriftActive()) // purple
|
||||
cmap = R_GetTranslationColormap(TC_DEFAULT, SKINCOLOR_PURPLE, GTC_CACHE);
|
||||
else if (timer > 25) // ketchup
|
||||
cmap = R_GetTranslationColormap(TC_DEFAULT, SKINCOLOR_KETCHUP, GTC_CACHE);
|
||||
else // blu
|
||||
cmap = R_GetTranslationColormap(TC_DEFAULT, SKINCOLOR_SAPPHIRE, GTC_CACHE);
|
||||
textcmap = cmap;
|
||||
}
|
||||
|
||||
// very bad!
|
||||
if (t.badtimer)
|
||||
textcmap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_CRIMSON, GTC_CACHE);
|
||||
|
||||
patch_t *item = t.patches[patchnum];
|
||||
V_DrawFixedPatch((fx+t.xoffs)<<FRACBITS, (fy+t.yoffs)<<FRACBITS, FRACUNIT, flags, item, cmap);
|
||||
|
||||
V_DrawStringScaledEx(
|
||||
(fx + (item->width/2) - (width/2) - item->leftoffset + t.xoffs) << FRACBITS,
|
||||
(fy+5) << FRACBITS,
|
||||
FRACUNIT,
|
||||
FRACUNIT,
|
||||
FRACUNIT,
|
||||
FRACUNIT,
|
||||
flags|V_MONOSPACE,
|
||||
textcmap,
|
||||
TINY_FONT,
|
||||
str
|
||||
);
|
||||
|
||||
|
||||
fy += step;
|
||||
}
|
||||
}
|
||||
}
|
||||
42
src/h_timers.h
Normal file
42
src/h_timers.h
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
// BLANKART
|
||||
//-----------------------------------------------------------------------------
|
||||
// Copyright (C) 2024 by haya3218.
|
||||
//
|
||||
// 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 h_main.h
|
||||
/// \brief HEP3 timers library.
|
||||
|
||||
#ifndef __H_TIMERS__
|
||||
#define __H_TIMERS__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "command.h"
|
||||
#include "d_player.h"
|
||||
|
||||
extern consvar_t cv_itemtimers;
|
||||
extern tic_t spbTimers[MAXPLAYERS];
|
||||
UINT8 K_GetBestRank(void);
|
||||
void K_UpdateSPBTimer(void);
|
||||
// Adds an item timer.
|
||||
void K_AddItemTimerEx(
|
||||
const char *name,
|
||||
INT32 *timer,
|
||||
char **patches, char **patches_small,
|
||||
INT32 patches_size, INT32 patches_small_size,
|
||||
INT32 anim_duration,
|
||||
INT32 xoffs, INT32 yoffs,
|
||||
INT32 xoffs_small, INT32 yoffs_small,
|
||||
boolean counter, boolean unsorted);
|
||||
void K_DisplayItemTimers(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
@ -39,6 +39,7 @@
|
|||
#include "r_fps.h"
|
||||
#include "m_random.h"
|
||||
#include "g_party.h"
|
||||
#include "h_timers.h"
|
||||
|
||||
#define NUMPOSNUMS 10
|
||||
#define NUMPOSFRAMES 7 // White, three blues, three reds
|
||||
|
|
@ -4658,6 +4659,8 @@ void K_drawKartHUD(void)
|
|||
K_drawLapStartAnim();
|
||||
}
|
||||
|
||||
K_DisplayItemTimers();
|
||||
|
||||
if (modeattacking || freecam) // everything after here is MP and debug only
|
||||
return;
|
||||
|
||||
|
|
|
|||
38
src/k_kart.c
38
src/k_kart.c
|
|
@ -63,6 +63,8 @@
|
|||
#include "k_follower.h"
|
||||
#include "k_grandprix.h"
|
||||
|
||||
#include "h_timers.h"
|
||||
|
||||
consvar_t cv_kartstacking_colorflame = CVAR_INIT ("kartstacking_colorflame", "On", CV_SAVE, CV_OnOff, NULL);
|
||||
consvar_t cv_kartstacking_sneakerstacksound = CVAR_INIT ("kartstacking_sneakerstacksound", "On", CV_SAVE, CV_OnOff, NULL);
|
||||
consvar_t cv_kartchainingsound = CVAR_INIT ("kartchaining_chainsound", "On", CV_SAVE, CV_OnOff, NULL);
|
||||
|
|
@ -339,6 +341,7 @@ void K_RegisterKartStuff(void)
|
|||
CV_RegisterVar(&cv_kartdriftsounds);
|
||||
CV_RegisterVar(&cv_kartdriftefx);
|
||||
CV_RegisterVar(&cv_driftsparkpulse);
|
||||
CV_RegisterVar(&cv_itemtimers);
|
||||
}
|
||||
|
||||
//}
|
||||
|
|
@ -10850,4 +10853,39 @@ void K_UpdateMobjItemOverlay(mobj_t *part, SINT8 itemType, UINT8 itemCount)
|
|||
}
|
||||
}
|
||||
|
||||
boolean K_NotFreePlay(void)
|
||||
{
|
||||
UINT8 i;
|
||||
UINT8 nump = 0;
|
||||
|
||||
if (K_CanChangeRules() == false)
|
||||
{
|
||||
// Rounds with direction are never FREE PLAY.
|
||||
return true;
|
||||
}
|
||||
|
||||
if (itembreaker)
|
||||
{
|
||||
// Prison Break is battle's FREE PLAY.
|
||||
return false;
|
||||
}
|
||||
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
if (playeringame[i] == false || players[i].spectator == true)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
nump++;
|
||||
|
||||
if (nump > 1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//}
|
||||
|
|
|
|||
|
|
@ -248,6 +248,8 @@ void K_UpdateMobjItemOverlay(mobj_t *part, SINT8 itemType, UINT8 itemCount);
|
|||
void K_DoBoost(player_t *player, fixed_t speedboost, fixed_t accelboost, boolean stack, boolean visible);
|
||||
void K_ClearBoost(player_t *player);
|
||||
|
||||
boolean K_NotFreePlay(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@
|
|||
#include "g_game.h" // record info
|
||||
#include "r_skins.h" // numskins
|
||||
#include "r_draw.h" // R_GetColorByName
|
||||
#include "k_kart.h"
|
||||
#include "k_pwrlv.h"
|
||||
#include "k_stats.h"
|
||||
|
||||
|
|
|
|||
573
src/v_video.c
573
src/v_video.c
|
|
@ -1879,6 +1879,11 @@ INT32 V_DanceYOffset(INT32 counter)
|
|||
return abs(step - (duration / 2)) - (duration / 4);
|
||||
}
|
||||
|
||||
static boolean V_CharacterValid(font_t *font, int c)
|
||||
{
|
||||
return (c >= 0 && c < font->size && font->font[c] != NULL);
|
||||
}
|
||||
|
||||
// Writes a single character (draw WHITE if bit 7 set)
|
||||
//
|
||||
void V_DrawCharacter(INT32 x, INT32 y, INT32 c, boolean lowercaseallowed)
|
||||
|
|
@ -2217,34 +2222,146 @@ static inline fixed_t BunchedCharacterDim(
|
|||
return 0;
|
||||
}
|
||||
|
||||
void V_DrawStringScaled(
|
||||
typedef struct
|
||||
{
|
||||
fixed_t chw;
|
||||
fixed_t spacew;
|
||||
fixed_t lfh;
|
||||
fixed_t (*dim_fn)(fixed_t,fixed_t,INT32,INT32,fixed_t *);
|
||||
} fontspec_t;
|
||||
|
||||
static void V_GetFontSpecification(int fontno, INT32 flags, fontspec_t *result)
|
||||
{
|
||||
/*
|
||||
Hardcoded until a better system can be implemented
|
||||
for determining how fonts space.
|
||||
*/
|
||||
|
||||
// All other properties are guaranteed to be set
|
||||
result->chw = 0;
|
||||
|
||||
const INT32 spacing = ( flags & V_SPACINGMASK );
|
||||
|
||||
switch (fontno)
|
||||
{
|
||||
default:
|
||||
case HU_FONT:
|
||||
result->spacew = 4;
|
||||
switch (spacing)
|
||||
{
|
||||
case V_MONOSPACE:
|
||||
result->spacew = 8;
|
||||
/* FALLTHRU */
|
||||
case V_OLDSPACING:
|
||||
result->chw = 8;
|
||||
break;
|
||||
case V_6WIDTHSPACE:
|
||||
result->spacew = 6;
|
||||
}
|
||||
break;
|
||||
case TINY_FONT:
|
||||
result->spacew = 2;
|
||||
switch (spacing)
|
||||
{
|
||||
case V_MONOSPACE:
|
||||
result->spacew = 5;
|
||||
/* FALLTHRU */
|
||||
case V_OLDSPACING:
|
||||
result->chw = 5;
|
||||
break;
|
||||
// Out of video flags, so we're reusing this for alternate charwidth instead
|
||||
/*case V_6WIDTHSPACE:
|
||||
spacewidth = 3;*/
|
||||
}
|
||||
break;
|
||||
case KART_FONT:
|
||||
result->spacew = 12;
|
||||
switch (spacing)
|
||||
{
|
||||
case V_MONOSPACE:
|
||||
result->spacew = 12;
|
||||
/* FALLTHRU */
|
||||
case V_OLDSPACING:
|
||||
result->chw = 12;
|
||||
break;
|
||||
case V_6WIDTHSPACE:
|
||||
result->spacew = 6;
|
||||
}
|
||||
break;
|
||||
case LT_FONT:
|
||||
result->spacew = 12;
|
||||
break;
|
||||
case CRED_FONT:
|
||||
result->spacew = 16;
|
||||
break;
|
||||
}
|
||||
switch (fontno)
|
||||
{
|
||||
default:
|
||||
case HU_FONT:
|
||||
case TINY_FONT:
|
||||
case KART_FONT:
|
||||
result->lfh = 12;
|
||||
break;
|
||||
case LT_FONT:
|
||||
case CRED_FONT:
|
||||
result->lfh = 12;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (fontno)
|
||||
{
|
||||
case TINY_FONT:
|
||||
{
|
||||
if (result->chw)
|
||||
result->dim_fn = FixedCharacterDim;
|
||||
else
|
||||
{
|
||||
/* Reuse this flag for the alternate bunched-up spacing. */
|
||||
if (( flags & V_6WIDTHSPACE ))
|
||||
result->dim_fn = BunchedCharacterDim;
|
||||
else
|
||||
result->dim_fn = VariableCharacterDim;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
if (result->chw)
|
||||
result->dim_fn = CenteredCharacterDim;
|
||||
else
|
||||
result->dim_fn = VariableCharacterDim;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void V_DrawStringScaledEx(
|
||||
fixed_t x,
|
||||
fixed_t y,
|
||||
fixed_t scale,
|
||||
fixed_t vscale,
|
||||
fixed_t spacescale,
|
||||
fixed_t lfscale,
|
||||
INT32 flags,
|
||||
const UINT8 *colormap,
|
||||
int fontno,
|
||||
const char *s)
|
||||
{
|
||||
fixed_t chw;
|
||||
INT32 hchw;/* half-width for centering */
|
||||
fixed_t spacew;
|
||||
fixed_t lfh;
|
||||
|
||||
INT32 dupx;
|
||||
INT32 dupy;
|
||||
|
||||
fixed_t right;
|
||||
fixed_t bot;
|
||||
|
||||
fixed_t (*dim_fn)(fixed_t,fixed_t,INT32,INT32,fixed_t *);
|
||||
|
||||
font_t *font;
|
||||
|
||||
boolean uppercase;
|
||||
boolean notcolored;
|
||||
boolean vflip;
|
||||
|
||||
const UINT8 *colormap = NULL;
|
||||
boolean dance;
|
||||
boolean nodanceoverride;
|
||||
INT32 dancecounter;
|
||||
|
|
@ -2254,7 +2371,6 @@ void V_DrawStringScaled(
|
|||
fixed_t cxoff, cyoff;
|
||||
fixed_t cw;
|
||||
|
||||
INT32 spacing;
|
||||
fixed_t left;
|
||||
|
||||
int c;
|
||||
|
|
@ -2265,126 +2381,59 @@ void V_DrawStringScaled(
|
|||
dance = (flags & V_STRINGDANCE) != 0;
|
||||
nodanceoverride = !dance;
|
||||
dancecounter = 0;
|
||||
vflip = (flags & V_VFLIP) != 0;
|
||||
|
||||
/* Some of these flags get overloaded in this function so
|
||||
don't pass them on. */
|
||||
flags &= ~(V_PARAMMASK);
|
||||
|
||||
if (vflip) flags |= V_VFLIP;
|
||||
|
||||
if (colormap == NULL)
|
||||
{
|
||||
colormap = V_GetStringColormap(( flags & V_CHARCOLORMASK ));
|
||||
}
|
||||
|
||||
colormap = V_GetStringColormap(( flags & V_CHARCOLORMASK ));
|
||||
notcolored = !colormap;
|
||||
|
||||
font = &fontv[fontno];
|
||||
|
||||
chw = 0;
|
||||
fontspec_t fontspec;
|
||||
|
||||
spacing = ( flags & V_SPACINGMASK );
|
||||
V_GetFontSpecification(fontno, flags, &fontspec);
|
||||
|
||||
/*
|
||||
Hardcoded until a better system can be implemented
|
||||
for determining how fonts space.
|
||||
*/
|
||||
switch (fontno)
|
||||
{
|
||||
default:
|
||||
case HU_FONT:
|
||||
spacew = 4;
|
||||
switch (spacing)
|
||||
{
|
||||
case V_MONOSPACE:
|
||||
spacew = 8;
|
||||
/* FALLTHRU */
|
||||
case V_OLDSPACING:
|
||||
chw = 8;
|
||||
break;
|
||||
case V_6WIDTHSPACE:
|
||||
spacew = 6;
|
||||
}
|
||||
break;
|
||||
case TINY_FONT:
|
||||
spacew = 2;
|
||||
switch (spacing)
|
||||
{
|
||||
case V_MONOSPACE:
|
||||
spacew = 5;
|
||||
/* FALLTHRU */
|
||||
case V_OLDSPACING:
|
||||
chw = 5;
|
||||
break;
|
||||
// Out of video flags, so we're reusing this for alternate charwidth instead
|
||||
/*case V_6WIDTHSPACE:
|
||||
spacewidth = 3;*/
|
||||
}
|
||||
break;
|
||||
case KART_FONT:
|
||||
spacew = 12;
|
||||
switch (spacing)
|
||||
{
|
||||
case V_MONOSPACE:
|
||||
spacew = 12;
|
||||
/* FALLTHRU */
|
||||
case V_OLDSPACING:
|
||||
chw = 12;
|
||||
break;
|
||||
case V_6WIDTHSPACE:
|
||||
spacew = 6;
|
||||
}
|
||||
break;
|
||||
case LT_FONT:
|
||||
spacew = 12;
|
||||
break;
|
||||
case CRED_FONT:
|
||||
spacew = 16;
|
||||
break;
|
||||
}
|
||||
switch (fontno)
|
||||
{
|
||||
default:
|
||||
case HU_FONT:
|
||||
case TINY_FONT:
|
||||
case KART_FONT:
|
||||
lfh = 12;
|
||||
break;
|
||||
case LT_FONT:
|
||||
case CRED_FONT:
|
||||
lfh = 12;
|
||||
break;
|
||||
}
|
||||
hchw = fontspec.chw >> 1;
|
||||
|
||||
hchw = chw >> 1;
|
||||
|
||||
chw <<= FRACBITS;
|
||||
spacew <<= FRACBITS;
|
||||
lfh <<= FRACBITS;
|
||||
fontspec.chw <<= FRACBITS;
|
||||
fontspec.spacew <<= FRACBITS;
|
||||
fontspec.lfh <<= FRACBITS;
|
||||
|
||||
#define Mul( id, scale ) ( id = FixedMul (scale, id) )
|
||||
Mul (chw, scale);
|
||||
Mul (spacew, scale);
|
||||
Mul (lfh, scale);
|
||||
Mul (fontspec.chw, scale);
|
||||
Mul (fontspec.spacew, scale);
|
||||
Mul (fontspec.lfh, scale);
|
||||
|
||||
Mul (spacew, spacescale);
|
||||
Mul (lfh, lfscale);
|
||||
Mul (fontspec.spacew, spacescale);
|
||||
Mul (fontspec.lfh, lfscale);
|
||||
#undef Mul
|
||||
|
||||
if (( flags & V_NOSCALESTART ))
|
||||
{
|
||||
dupx = vid.dupx;
|
||||
dupy = vid.dupy;
|
||||
|
||||
hchw *= dupx;
|
||||
|
||||
chw *= dupx;
|
||||
spacew *= dupx;
|
||||
lfh *= vid.dupy;
|
||||
fontspec.chw *= dupx;
|
||||
fontspec.spacew *= dupx;
|
||||
fontspec.lfh *= dupy;
|
||||
|
||||
right = vid.width;
|
||||
}
|
||||
else
|
||||
{
|
||||
dupx = 1;
|
||||
dupy = 1;
|
||||
|
||||
right = ( vid.width / vid.dupx );
|
||||
if (!( flags & V_SNAPTOLEFT ))
|
||||
|
|
@ -2397,27 +2446,6 @@ void V_DrawStringScaled(
|
|||
right <<= FRACBITS;
|
||||
bot = vid.height << FRACBITS;
|
||||
|
||||
if (fontno == TINY_FONT)
|
||||
{
|
||||
if (chw)
|
||||
dim_fn = FixedCharacterDim;
|
||||
else
|
||||
{
|
||||
/* Reuse this flag for the alternate bunched-up spacing. */
|
||||
if (( flags & V_6WIDTHSPACE ))
|
||||
dim_fn = BunchedCharacterDim;
|
||||
else
|
||||
dim_fn = VariableCharacterDim;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (chw)
|
||||
dim_fn = CenteredCharacterDim;
|
||||
else
|
||||
dim_fn = VariableCharacterDim;
|
||||
}
|
||||
|
||||
cx = x;
|
||||
cy = y;
|
||||
cyoff = 0;
|
||||
|
|
@ -2427,13 +2455,13 @@ void V_DrawStringScaled(
|
|||
switch (c)
|
||||
{
|
||||
case '\n':
|
||||
cy += lfh;
|
||||
cy += fontspec.lfh;
|
||||
if (cy >= bot)
|
||||
return;
|
||||
cx = x;
|
||||
break;
|
||||
default:
|
||||
if (( c & 0x80 ))
|
||||
if (( c & 0xF0 ) == 0x80)
|
||||
{
|
||||
if (notcolored)
|
||||
{
|
||||
|
|
@ -2453,26 +2481,329 @@ void V_DrawStringScaled(
|
|||
else if (cx < right)
|
||||
{
|
||||
if (uppercase)
|
||||
{
|
||||
c = toupper(c);
|
||||
}
|
||||
else if (V_CharacterValid(font, c - font->start) == false)
|
||||
{
|
||||
// Try the other case if it doesn't exist
|
||||
if (c >= 'A' && c <= 'Z')
|
||||
{
|
||||
c = tolower(c);
|
||||
}
|
||||
else if (c >= 'a' && c <= 'z')
|
||||
{
|
||||
c = toupper(c);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (dance)
|
||||
{
|
||||
cyoff = V_DanceYOffset(dancecounter) * FRACUNIT;
|
||||
}
|
||||
|
||||
c -= font->start;
|
||||
if (c >= 0 && c < font->size && font->font[c])
|
||||
if (V_CharacterValid(font, c) == true)
|
||||
{
|
||||
// Remove offsets from patch
|
||||
fixed_t patchxofs = SHORT (font->font[c]->leftoffset) * dupx * scale;
|
||||
cw = SHORT (font->font[c]->width) * dupx;
|
||||
cxoff = (*dim_fn)(scale, chw, hchw, dupx, &cw);
|
||||
V_DrawFixedPatch(cx + cxoff, cy + cyoff, scale,
|
||||
cxoff = (*fontspec.dim_fn)(scale, fontspec.chw, hchw, dupx, &cw);
|
||||
V_DrawStretchyFixedPatch(cx + cxoff + patchxofs, cy + cyoff, scale, vscale,
|
||||
flags, font->font[c], colormap);
|
||||
cx += cw;
|
||||
}
|
||||
else
|
||||
cx += spacew;
|
||||
{
|
||||
cx += fontspec.spacew;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fixed_t V_StringScaledWidth(
|
||||
fixed_t scale,
|
||||
fixed_t spacescale,
|
||||
fixed_t lfscale,
|
||||
INT32 flags,
|
||||
int fontno,
|
||||
const char *s)
|
||||
{
|
||||
INT32 hchw;/* half-width for centering */
|
||||
|
||||
INT32 dupx;
|
||||
|
||||
font_t *font;
|
||||
|
||||
boolean uppercase;
|
||||
|
||||
fixed_t cx;
|
||||
fixed_t right;
|
||||
|
||||
fixed_t cw;
|
||||
|
||||
int c;
|
||||
|
||||
fixed_t fullwidth = 0;
|
||||
|
||||
uppercase = !( flags & V_ALLOWLOWERCASE );
|
||||
flags &= ~(V_FLIP);/* These two (V_ALLOWLOWERCASE) share a bit. */
|
||||
|
||||
font = &fontv[fontno];
|
||||
|
||||
fontspec_t fontspec;
|
||||
|
||||
V_GetFontSpecification(fontno, flags, &fontspec);
|
||||
|
||||
hchw = fontspec.chw >> 1;
|
||||
|
||||
fontspec.chw <<= FRACBITS;
|
||||
fontspec.spacew <<= FRACBITS;
|
||||
|
||||
#define Mul( id, scale ) ( id = FixedMul (scale, id) )
|
||||
Mul (fontspec.chw, scale);
|
||||
Mul (fontspec.spacew, scale);
|
||||
Mul (fontspec.lfh, scale);
|
||||
|
||||
Mul (fontspec.spacew, spacescale);
|
||||
Mul (fontspec.lfh, lfscale);
|
||||
#undef Mul
|
||||
|
||||
if (( flags & V_NOSCALESTART ))
|
||||
{
|
||||
dupx = vid.dupx;
|
||||
|
||||
hchw *= dupx;
|
||||
|
||||
fontspec.chw *= dupx;
|
||||
fontspec.spacew *= dupx;
|
||||
fontspec.lfh *= vid.dupy;
|
||||
}
|
||||
else
|
||||
{
|
||||
dupx = 1;
|
||||
}
|
||||
|
||||
cx = 0;
|
||||
right = 0;
|
||||
|
||||
for (; ( c = *s ); ++s)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case '\n':
|
||||
cx = 0;
|
||||
break;
|
||||
default:
|
||||
if (( c & 0xF0 ) == 0x80 || c == V_STRINGDANCE)
|
||||
continue;
|
||||
|
||||
if (uppercase)
|
||||
{
|
||||
c = toupper(c);
|
||||
}
|
||||
else if (V_CharacterValid(font, c - font->start) == false)
|
||||
{
|
||||
// Try the other case if it doesn't exist
|
||||
if (c >= 'A' && c <= 'Z')
|
||||
{
|
||||
c = tolower(c);
|
||||
}
|
||||
else if (c >= 'a' && c <= 'z')
|
||||
{
|
||||
c = toupper(c);
|
||||
}
|
||||
}
|
||||
|
||||
c -= font->start;
|
||||
if (V_CharacterValid(font, c) == true)
|
||||
{
|
||||
cw = SHORT (font->font[c]->width) * dupx;
|
||||
|
||||
// How bunched dims work is by incrementing cx slightly less than a full character width.
|
||||
// This causes the next character to be drawn overlapping the previous.
|
||||
// We need to count the full width to get the rightmost edge of the string though.
|
||||
right = cx + (cw * scale);
|
||||
|
||||
(*fontspec.dim_fn)(scale, fontspec.chw, hchw, dupx, &cw);
|
||||
cx += cw;
|
||||
}
|
||||
else
|
||||
cx += fontspec.spacew;
|
||||
}
|
||||
|
||||
fullwidth = max(right, max(cx, fullwidth));
|
||||
}
|
||||
|
||||
return fullwidth;
|
||||
}
|
||||
|
||||
// Modify a string to wordwrap at any given width.
|
||||
char * V_ScaledWordWrap(
|
||||
fixed_t w,
|
||||
fixed_t scale,
|
||||
fixed_t spacescale,
|
||||
fixed_t lfscale,
|
||||
INT32 flags,
|
||||
int fontno,
|
||||
const char *s)
|
||||
{
|
||||
INT32 hchw;/* half-width for centering */
|
||||
|
||||
INT32 dupx;
|
||||
|
||||
font_t *font;
|
||||
|
||||
boolean uppercase;
|
||||
|
||||
fixed_t cx;
|
||||
fixed_t right;
|
||||
|
||||
fixed_t cw;
|
||||
|
||||
int c;
|
||||
|
||||
uppercase = !( flags & V_ALLOWLOWERCASE );
|
||||
flags &= ~(V_FLIP);/* These two (V_ALLOWLOWERCASE) share a bit. */
|
||||
|
||||
font = &fontv[fontno];
|
||||
|
||||
fontspec_t fontspec;
|
||||
|
||||
V_GetFontSpecification(fontno, flags, &fontspec);
|
||||
|
||||
hchw = fontspec.chw >> 1;
|
||||
|
||||
fontspec.chw <<= FRACBITS;
|
||||
fontspec.spacew <<= FRACBITS;
|
||||
|
||||
#define Mul( id, scale ) ( id = FixedMul (scale, id) )
|
||||
Mul (fontspec.chw, scale);
|
||||
Mul (fontspec.spacew, scale);
|
||||
Mul (fontspec.lfh, scale);
|
||||
|
||||
Mul (fontspec.spacew, spacescale);
|
||||
Mul (fontspec.lfh, lfscale);
|
||||
#undef Mul
|
||||
|
||||
if (( flags & V_NOSCALESTART ))
|
||||
{
|
||||
dupx = vid.dupx;
|
||||
|
||||
hchw *= dupx;
|
||||
|
||||
fontspec.chw *= dupx;
|
||||
fontspec.spacew *= dupx;
|
||||
fontspec.lfh *= vid.dupy;
|
||||
}
|
||||
else
|
||||
{
|
||||
dupx = 1;
|
||||
}
|
||||
|
||||
cx = 0;
|
||||
right = 0;
|
||||
|
||||
size_t reader = 0, writer = 0, startwriter = 0;
|
||||
fixed_t cxatstart = 0;
|
||||
|
||||
size_t len = strlen(s) + 1;
|
||||
size_t potentialnewlines = 8;
|
||||
size_t sparenewlines = potentialnewlines;
|
||||
|
||||
char *newstring = Z_Malloc(len + sparenewlines, PU_STATIC, NULL);
|
||||
|
||||
for (; ( c = s[reader] ); ++reader, ++writer)
|
||||
{
|
||||
newstring[writer] = s[reader];
|
||||
|
||||
right = 0;
|
||||
|
||||
switch (c)
|
||||
{
|
||||
case '\n':
|
||||
cx = 0;
|
||||
cxatstart = 0;
|
||||
startwriter = 0;
|
||||
break;
|
||||
default:
|
||||
if (( c & 0xF0 ) == 0x80 || c == V_STRINGDANCE)
|
||||
;
|
||||
else
|
||||
{
|
||||
if (uppercase)
|
||||
{
|
||||
c = toupper(c);
|
||||
}
|
||||
else if (V_CharacterValid(font, c - font->start) == false)
|
||||
{
|
||||
// Try the other case if it doesn't exist
|
||||
if (c >= 'A' && c <= 'Z')
|
||||
{
|
||||
c = tolower(c);
|
||||
}
|
||||
else if (c >= 'a' && c <= 'z')
|
||||
{
|
||||
c = toupper(c);
|
||||
}
|
||||
}
|
||||
|
||||
c -= font->start;
|
||||
if (V_CharacterValid(font, c) == true)
|
||||
{
|
||||
cw = SHORT (font->font[c]->width) * dupx;
|
||||
|
||||
// How bunched dims work is by incrementing cx slightly less than a full character width.
|
||||
// This causes the next character to be drawn overlapping the previous.
|
||||
// We need to count the full width to get the rightmost edge of the string though.
|
||||
right = cx + (cw * scale);
|
||||
|
||||
(*fontspec.dim_fn)(scale, fontspec.chw, hchw, dupx, &cw);
|
||||
cx += cw;
|
||||
}
|
||||
else
|
||||
{
|
||||
cx += fontspec.spacew;
|
||||
cxatstart = cx;
|
||||
startwriter = writer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Start trying to wrap if presumed length exceeds the space we have on-screen.
|
||||
if (right && right > w)
|
||||
{
|
||||
if (startwriter != 0)
|
||||
{
|
||||
newstring[startwriter] = '\n';
|
||||
cx -= cxatstart;
|
||||
cxatstart = 0;
|
||||
startwriter = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (sparenewlines == 0)
|
||||
{
|
||||
sparenewlines = (potentialnewlines *= 2);
|
||||
newstring = Z_Realloc(newstring, len + sparenewlines, PU_STATIC, NULL);
|
||||
}
|
||||
|
||||
sparenewlines--;
|
||||
len++;
|
||||
|
||||
newstring[writer++] = '\n'; // Over-write previous
|
||||
cx = cw; // Valid value in the only case right is currently set
|
||||
newstring[writer] = s[reader]; // Re-add
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
newstring[writer] = '\0';
|
||||
|
||||
return newstring;
|
||||
}
|
||||
//
|
||||
|
||||
void V_DrawCenteredString(INT32 x, INT32 y, INT32 option, const char *string)
|
||||
|
|
|
|||
|
|
@ -235,6 +235,10 @@ void V_EncoreInvertScreen(void);
|
|||
void V_DrawPromptBack(INT32 boxheight, INT32 color);
|
||||
|
||||
/* Convenience macros for leagacy string function macros. */
|
||||
|
||||
/* Yes I'm lazy RN, deal with it. */
|
||||
#define V_DrawStringScaled( x,y,scale,space_scale,linefeed_scale,option,font,string ) \
|
||||
V_DrawStringScaledEx(x,y,scale,scale,space_scale,linefeed_scale,option,NULL,font,string)
|
||||
#define V__DrawOneScaleString( x,y,scale,option,font,string ) \
|
||||
V_DrawStringScaled(x,y,scale,FRACUNIT,FRACUNIT,option,font,string)
|
||||
#define V__DrawDupxString( x,y,scale,option,font,string )\
|
||||
|
|
@ -256,16 +260,35 @@ UINT8 *V_GetStringColormap(INT32 colorflags);
|
|||
char *V_WordWrap(INT32 x, INT32 w, INT32 option, const char *string);
|
||||
|
||||
// draw a string using a font
|
||||
void V_DrawStringScaled(
|
||||
void V_DrawStringScaledEx(
|
||||
fixed_t x,
|
||||
fixed_t y,
|
||||
fixed_t scale,
|
||||
fixed_t vscale,
|
||||
fixed_t space_scale,
|
||||
fixed_t linefeed_scale,
|
||||
INT32 flags,
|
||||
const UINT8 *colormap,
|
||||
int font,
|
||||
const char *text);
|
||||
|
||||
fixed_t V_StringScaledWidth(
|
||||
fixed_t scale,
|
||||
fixed_t spacescale,
|
||||
fixed_t lfscale,
|
||||
INT32 flags,
|
||||
int fontno,
|
||||
const char *s);
|
||||
|
||||
char * V_ScaledWordWrap(
|
||||
fixed_t w,
|
||||
fixed_t scale,
|
||||
fixed_t spacescale,
|
||||
fixed_t lfscale,
|
||||
INT32 flags,
|
||||
int fontno,
|
||||
const char *s);
|
||||
|
||||
// draw a string using the hu_font
|
||||
#define V_DrawString( x,y,option,string ) \
|
||||
V__DrawDupxString (x,y,FRACUNIT,option,HU_FONT,string)
|
||||
|
|
|
|||
Loading…
Reference in a new issue