diff --git a/src/acs/call-funcs.cpp b/src/acs/call-funcs.cpp index c7ff38591..24170bf41 100644 --- a/src/acs/call-funcs.cpp +++ b/src/acs/call-funcs.cpp @@ -338,6 +338,8 @@ static bool ACS_CountThing(mobj_t *mobj, mobjtype_t type) return false; } +// Unused, but it's here if you need it. +#if 0 /*-------------------------------------------------- static bool ACS_ActivatorIsLocal(ACSVM::Thread *thread) @@ -365,6 +367,7 @@ static bool ACS_ActivatorIsLocal(ACSVM::Thread *thread) return false; } +#endif /*-------------------------------------------------- static UINT32 ACS_SectorThingCounter(sector_t *sec, mtag_t thingTag, bool (*filter)(mobj_t *)) @@ -823,8 +826,10 @@ bool CallFunc_EndPrint(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Wo (void)argV; (void)argC; - if (ACS_ActivatorIsLocal(thread) == true) - HU_DoTitlecardCEcho(thread->printBuf.data()); + auto& info = static_cast(thread)->info; + + if (P_MobjWasRemoved(info.mo) == false && info.mo->player != nullptr) + HU_DoTitlecardCEcho(info.mo->player, thread->printBuf.data(), true); thread->printBuf.drop(); return false; @@ -1203,7 +1208,7 @@ bool CallFunc_EndPrintBold(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM (void)argV; (void)argC; - HU_DoTitlecardCEcho(thread->printBuf.data()); + HU_DoTitlecardCEcho(nullptr, thread->printBuf.data(), true); thread->printBuf.drop(); return false; diff --git a/src/command.c b/src/command.c index fd5044e8b..8f0574f03 100644 --- a/src/command.c +++ b/src/command.c @@ -777,15 +777,17 @@ static void COM_CEcho_f(void) size_t i; char cechotext[1024] = ""; - for (i = 1; i < COM_Argc(); i++) + strncpy(cechotext, COM_Argv(1), sizeof(cechotext)-1); + + for (i = 2; i < COM_Argc(); i++) { - strncat(cechotext, COM_Argv(i), sizeof(cechotext)-1); strncat(cechotext, " ", sizeof(cechotext)-1); + strncat(cechotext, COM_Argv(i), sizeof(cechotext)-1); } cechotext[sizeof(cechotext) - 1] = '\0'; - HU_DoCEcho(cechotext); + HU_DoTitlecardCEcho(NULL, cechotext, true); } /** Sets drawing flags for the CECHO command. diff --git a/src/g_game.c b/src/g_game.c index 5dcd41f39..b7f71b4ea 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -1276,6 +1276,10 @@ boolean G_IsTitleCardAvailable(void) if (gametyperules & GTR_SPECIALSTART) return false; + // ALso. + if (K_PodiumSequence() == true) + return false; + // The title card is available. return true; } diff --git a/src/hu_stuff.c b/src/hu_stuff.c index fd9fa3c97..0b0434f70 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -62,6 +62,7 @@ #include "r_fps.h" #include "d_clisrv.h" #include "y_inter.h" // Y_PlayerStandingsDrawer +#include "g_party.h" // coords are scaled #define HU_INPUTX 0 @@ -157,10 +158,15 @@ static tic_t cechotimer = 0; static tic_t cechoduration = 5*TICRATE; static INT32 cechoflags = 0; -static char tcechotext[1024]; // buffer for the titlecard text -static tic_t tcechotimer = 0; // goes up by 1 each frame this is active -static tic_t tcechoduration = 0; // Set automatically +struct tcecho_state +{ + char text[1024]; // buffer for the titlecard text + tic_t start; // gametic that the message started + tic_t duration; // Set automatically +}; +#define NUM_TCECHO_STATES (1 + MAXSPLITSCREENPLAYERS) +static struct tcecho_state g_tcecho[NUM_TCECHO_STATES]; static tic_t resynch_ticker = 0; @@ -283,6 +289,12 @@ void HU_Init(void) PR ("GTFN"); REG; + PR ("4GTOL"); + REG; + + PR ("4GTFN"); + REG; + DIG (1); DIM (0, 10); @@ -960,13 +972,6 @@ void HU_Ticker(void) if (cechotimer) cechotimer--; - - if (tcechotimer) - { - tcechotimer++; - if (tcechotimer > tcechoduration) - tcechotimer = 0; - } if (gamestate != GS_LEVEL) { @@ -1829,12 +1834,31 @@ static void HU_DrawCEcho(void) } } -static void HU_DrawTitlecardCEcho(void) +static tic_t HU_TitlecardCEchoElapsed(const struct tcecho_state *state) { - if (tcechotimer) + return max(gametic, state->start) - state->start; +} + +static void HU_DrawTitlecardCEcho(size_t num) +{ + const struct tcecho_state *state = &g_tcecho[num]; + + tic_t elapsed = HU_TitlecardCEchoElapsed(state); + UINT8 viewnum = max(1, num) - 1; + boolean p4 = (num != 0 && r_splitscreen); + + // If the splitscreens were somehow decreased in the + // middle of drawing this, don't draw it. + if (viewnum > r_splitscreen) + { + return; + } + + if (elapsed < state->duration) { INT32 i = 0; - INT32 y = (BASEVIDHEIGHT/2)-16; + INT32 x = BASEVIDWIDTH/2; + INT32 y = BASEVIDHEIGHT/2; INT32 pnumlines = 0; INT32 timeroffset = 0; @@ -1842,11 +1866,28 @@ static void HU_DrawTitlecardCEcho(void) char *echoptr; char temp[1024]; - for (i = 0; tcechotext[i] != '\0'; ++i) - if (tcechotext[i] == '\\') + for (i = 0; state->text[i] != '\0'; ++i) + if (state->text[i] == '\\') pnumlines++; - y -= (pnumlines-1)*16; + if (p4) + { + if (r_splitscreen == 1) // 2P + { + y -= (1 - (viewnum * 2)) * (y / 2); + } + else // 3P / 4P + { + x -= (1 - ((viewnum % 2) * 2)) * (x / 2); + y -= (1 - ((viewnum / 2) * 2)) * (y / 2); + } + + y -= 11 + ((pnumlines-1) * 9); + } + else + { + y -= 18 + ((pnumlines-1) * 16); + } // Prevent crashing because I'm sick of this if (y < 0) @@ -1856,13 +1897,13 @@ static void HU_DrawTitlecardCEcho(void) return; } - strcpy(temp, tcechotext); + strcpy(temp, state->text); echoptr = &temp[0]; while (*echoptr != '\0') { - INT32 w; - INT32 timer = (INT32)(tcechotimer - timeroffset); + INT32 ofs; + INT32 timer = (INT32)(elapsed - timeroffset); if (timer <= 0) return; // we don't care. @@ -1874,10 +1915,10 @@ static void HU_DrawTitlecardCEcho(void) *line = '\0'; - w = V_TitleCardStringWidth(echoptr); - V_DrawTitleCardString(BASEVIDWIDTH/2 -w/2, y, echoptr, 0, false, timer, TICRATE*4); + ofs = V_CenteredTitleCardStringOffset(echoptr, p4); + V_DrawTitleCardString(x - ofs, y, echoptr, 0, false, timer, TICRATE*4, p4); - y += 32; + y += p4 ? 18 : 32; // offset the timer for the next line. timeroffset += strlen(echoptr); @@ -2037,9 +2078,23 @@ drawontop: if (cechotimer) HU_DrawCEcho(); - - if (tcechotimer) - HU_DrawTitlecardCEcho(); + + const struct tcecho_state *firststate = &g_tcecho[0]; + + // Server messages overwrite player-specific messages + if (HU_TitlecardCEchoElapsed(firststate) < firststate->duration) + { + HU_DrawTitlecardCEcho(0); + } + else + { + size_t i; + + for (i = 1; i < NUM_TCECHO_STATES; ++i) + { + HU_DrawTitlecardCEcho(i); + } + } } //====================================================================== @@ -2576,17 +2631,41 @@ void HU_DoCEcho(const char *msg) // No need to bother clearing the buffer or anything. void HU_ClearTitlecardCEcho(void) { - tcechotimer = 0; + size_t i; + + for (i = 0; i < NUM_TCECHO_STATES; ++i) + { + g_tcecho[i].duration = 0; + } } // Similar but for titlecard CEcho and also way less convoluted because I have no clue whatever the fuck they were trying above. -void HU_DoTitlecardCEcho(const char *msg) +void HU_DoTitlecardCEcho(player_t *player, const char *msg, boolean interrupt) { + if (player && !P_IsDisplayPlayer(player)) + { + return; + } + + struct tcecho_state *state = &g_tcecho[0]; + + if (player) + { + state = &g_tcecho[1 + G_PartyPosition(player - players)]; + } + + // If this message should not interrupt an existing + // message. Check if another message is already running. + if (!interrupt && HU_TitlecardCEchoElapsed(state) < state->duration) + { + return; + } + I_OutputMsg("%s\n", msg); // print to log - - strncpy(tcechotext, msg, sizeof(tcechotext)); - strncat(tcechotext, "\\", sizeof(tcechotext) - strlen(tcechotext) - 1); - tcechotext[sizeof(tcechotext) - 1] = '\0'; - tcechotimer = 1; - tcechoduration = TICRATE*6 + strlen(tcechotext); + + strncpy(state->text, msg, sizeof(state->text)); + strncat(state->text, "\\", sizeof(state->text) - strlen(state->text) - 1); + state->text[sizeof(state->text) - 1] = '\0'; + state->start = gametic; + state->duration = TICRATE*6 + strlen(state->text); } diff --git a/src/hu_stuff.h b/src/hu_stuff.h index 92ecdbc41..db1cfcc57 100644 --- a/src/hu_stuff.h +++ b/src/hu_stuff.h @@ -77,6 +77,9 @@ enum X (GTOL), X (GTFN), + X (GTOL4), + X (GTFN4), + X (TALLNUM), X (NIGHTSNUM), X (PINGNUM), @@ -163,7 +166,7 @@ void HU_SetCEchoFlags(INT32 flags); void HU_DoCEcho(const char *msg); // Titlecard CECHO shite -void HU_DoTitlecardCEcho(const char *msg); +void HU_DoTitlecardCEcho(player_t *player, const char *msg, boolean interrupt); void HU_ClearTitlecardCEcho(void); void DoSayCommand(char *message, SINT8 target, UINT8 flags, UINT8 source); diff --git a/src/k_hud.c b/src/k_hud.c index af15b474a..5b64059d3 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -4332,6 +4332,9 @@ static void K_drawKartFinish(boolean finish) if (finish) { + if (gametyperules & GTR_SPECIALSTART) + return; + timer = stplyr->karthud[khud_finish]; kptodraw = kp_racefinish; minsplitstationary = 2; diff --git a/src/k_podium.c b/src/k_podium.c index 2f1694f52..ef455475b 100644 --- a/src/k_podium.c +++ b/src/k_podium.c @@ -94,6 +94,21 @@ gp_rank_e K_PodiumGrade(void) return podiumData.grade; } +/*-------------------------------------------------- + boolean K_PodiumHasEmerald(void) + + See header file for description. +--------------------------------------------------*/ +boolean K_PodiumHasEmerald(void) +{ + if (K_PodiumSequence() == false) + { + return false; + } + + return podiumData.rank.specialWon; +} + /*-------------------------------------------------- UINT8 K_GetPodiumPosition(player_t *player) @@ -284,6 +299,7 @@ boolean K_StartCeremony(void) G_SetGametype(GT_RACE); G_DoLoadLevelEx(false, GS_CEREMONY); + wipegamestate = GS_CEREMONY; // I don't know what else to do here r_splitscreen = 0; // Only one screen for the ceremony R_ExecuteSetViewSize(); diff --git a/src/k_podium.h b/src/k_podium.h index d7b04e3e4..eb73a7aae 100644 --- a/src/k_podium.h +++ b/src/k_podium.h @@ -69,6 +69,20 @@ boolean K_PodiumRanking(void); gp_rank_e K_PodiumGrade(void); +/*-------------------------------------------------- + boolean K_PodiumHasEmerald(void) + + Returns whether the Emerald or Prize was collected. + + Input Arguments:- + N/A + + Return:- + true if the Emerald/Prize was collected during the GP, otherwise false. +--------------------------------------------------*/ +boolean K_PodiumHasEmerald(void); + + /*-------------------------------------------------- UINT8 K_GetPodiumPosition(player_t *player); diff --git a/src/lua_baselib.c b/src/lua_baselib.c index bd1771a89..69f0aed21 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -3515,9 +3515,12 @@ static int lib_getTimeMicros(lua_State *L) static int lib_startTitlecardCecho(lua_State *L) { - const char *str = luaL_checkstring(L, 1); - HU_DoTitlecardCEcho(str); - + player_t *player = lua_isnil(L, 1) ? NULL : *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); + const char *str = luaL_checkstring(L, 2); + boolean interrupt = lua_optboolean(L, 3); + + HU_DoTitlecardCEcho(player, str, interrupt); + return 1; } diff --git a/src/lua_hudlib.c b/src/lua_hudlib.c index 5a5583f1a..a95ff78c2 100644 --- a/src/lua_hudlib.c +++ b/src/lua_hudlib.c @@ -948,6 +948,7 @@ static int libd_drawTitleCardString(lua_State *L) boolean rightalign = lua_optboolean(L, 5); INT32 timer = luaL_optinteger(L, 6, 0); INT32 threshold = luaL_optinteger(L, 7, 0); + boolean p4 = lua_optboolean(L, 8); huddrawlist_h list; flags &= ~V_PARAMMASK; // Don't let crashes happen. @@ -958,9 +959,9 @@ static int libd_drawTitleCardString(lua_State *L) lua_pop(L, 1); if (LUA_HUD_IsDrawListValid(list)) - LUA_HUD_AddDrawTitleCardString(list, x, y, flags, str, rightalign, timer, threshold); + LUA_HUD_AddDrawTitleCardString(list, x, y, flags, str, rightalign, timer, threshold, p4); else - V_DrawTitleCardString(x, y, str, flags, rightalign, timer, threshold); + V_DrawTitleCardString(x, y, str, flags, rightalign, timer, threshold, p4); return 0; } @@ -989,9 +990,10 @@ static int libd_drawKartString(lua_State *L) static int libd_titleCardStringWidth(lua_State *L) { const char *str = luaL_checkstring(L, 1); + boolean p4 = lua_optboolean(L, 2); HUDONLY - lua_pushinteger(L, V_TitleCardStringWidth(str)); + lua_pushinteger(L, V_TitleCardStringWidth(str, p4)); return 1; } diff --git a/src/lua_hudlib_drawlist.c b/src/lua_hudlib_drawlist.c index 68515ae20..8418f1b5a 100644 --- a/src/lua_hudlib_drawlist.c +++ b/src/lua_hudlib_drawlist.c @@ -61,6 +61,7 @@ typedef struct drawitem_s { INT32 timer; INT32 threshold; boolean bossmode; + boolean p4; } drawitem_t; // The internal structure of a drawlist. @@ -358,7 +359,8 @@ void LUA_HUD_AddDrawTitleCardString( const char *str, boolean bossmode, INT32 timer, - INT32 threshold + INT32 threshold, + boolean p4 ) { size_t i = AllocateDrawItem(list); @@ -371,6 +373,7 @@ void LUA_HUD_AddDrawTitleCardString( item->bossmode = bossmode; item->timer = timer; item->threshold = threshold; + item->p4 = p4; } void LUA_HUD_AddDrawKartString( @@ -465,7 +468,7 @@ void LUA_HUD_DrawList(huddrawlist_h list) V_DrawFadeScreen(item->color, item->strength); break; case DI_DrawTitleCardString: - V_DrawTitleCardString(item->x, item->y, itemstr, item->flags, item->bossmode, item->timer, item->threshold); + V_DrawTitleCardString(item->x, item->y, itemstr, item->flags, item->bossmode, item->timer, item->threshold, item->p4); break; case DI_DrawKartString: V_DrawTimerString(item->x, item->y, item->flags, itemstr); diff --git a/src/lua_hudlib_drawlist.h b/src/lua_hudlib_drawlist.h index 15249e4f9..bf2a161e2 100644 --- a/src/lua_hudlib_drawlist.h +++ b/src/lua_hudlib_drawlist.h @@ -111,7 +111,8 @@ void LUA_HUD_AddDrawTitleCardString( const char *str, boolean bossmode, INT32 timer, - INT32 threshold + INT32 threshold, + boolean p4 ); void LUA_HUD_AddDrawKartString( huddrawlist_h list, diff --git a/src/objects/ufo.c b/src/objects/ufo.c index 6a9f4bbdc..71ae0b585 100644 --- a/src/objects/ufo.c +++ b/src/objects/ufo.c @@ -26,6 +26,7 @@ #include "../r_skins.h" #include "../k_hitlag.h" #include "../acs/interface.h" +#include "../hu_stuff.h" #define UFO_BASE_SPEED (42 * FRACUNIT) // UFO's slowest speed. #define UFO_SPEEDUP (FRACUNIT >> 1) // Acceleration @@ -453,6 +454,8 @@ static void UFOMove(mobj_t *ufo) // Disable player P_DoAllPlayersExit(PF_NOCONTEST, false); + + HU_DoTitlecardCEcho(NULL, "TOO LATE...", false); } if (pathfindsuccess == true) diff --git a/src/p_inter.c b/src/p_inter.c index 890644320..e56e68400 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -2069,6 +2069,11 @@ static boolean P_KillPlayer(player_t *player, mobj_t *inflictor, mobj_t *source, #else P_DoPlayerExit(player, PF_NOCONTEST); #endif + + if (specialstageinfo.valid == true) + { + HU_DoTitlecardCEcho(player, "FALL OUT!", false); + } } if (player->exiting) diff --git a/src/p_setup.c b/src/p_setup.c index 1c2f8e06a..e9281f11b 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -8245,8 +8245,17 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate) } } - if (gametyperules & GTR_SPECIALSTART) + if (K_PodiumHasEmerald()) { + // Special Stage out + if (ranspecialwipe != 2) + S_StartSound(NULL, sfx_s3k6a); + levelfadecol = 0; + wipetype = wipe_encore_towhite; + } + else if (gametyperules & GTR_SPECIALSTART) + { + // Special Stage in if (ranspecialwipe != 2) S_StartSound(NULL, sfx_s3kaf); levelfadecol = 0; @@ -8254,6 +8263,7 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate) } else if (skipstats == 1) { + // MapWarp if (ranspecialwipe != 2) S_StartSound(NULL, sfx_s3k73); levelfadecol = 0; @@ -8261,11 +8271,13 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate) } else if (encoremode) { + // Encore levelfadecol = 0; wipetype = wipe_encore_towhite; } else { + // Default levelfadecol = 31; } diff --git a/src/p_spec.c b/src/p_spec.c index 85003e43e..d3efd7ae5 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -1987,7 +1987,15 @@ static void K_HandleLapIncrement(player_t *player) player->starpostnum = 0; - if (P_IsDisplayPlayer(player)) + if (gametyperules & GTR_SPECIALSTART) + { + if (player->laps > numlaps) + { + // Warp out + S_StartSound(NULL, sfx_s3kb3); + } + } + else if (P_IsDisplayPlayer(player)) { if (numlaps > 1 && player->laps == numlaps) // final lap S_StartSound(NULL, sfx_s3k68); @@ -2053,6 +2061,8 @@ static void K_HandleLapIncrement(player_t *player) if (!(specialstageinfo.ufo == NULL || P_MobjWasRemoved(specialstageinfo.ufo))) { applyflags |= PF_NOCONTEST; + + HU_DoTitlecardCEcho(player, "EMPTY\\HANDED?", false); } } diff --git a/src/p_user.c b/src/p_user.c index a5176e580..0fb242fbf 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -1296,7 +1296,7 @@ void P_DoPlayerExit(player_t *player, pflags_t flags) { K_UpdateAllPlayerPositions(); - if (cv_kartvoices.value) + if (cv_kartvoices.value && !(gametyperules & GTR_SPECIALSTART)) { if (P_IsDisplayPlayer(player)) { @@ -1326,7 +1326,9 @@ void P_DoPlayerExit(player_t *player, pflags_t flags) G_BeginLevelExit(); } - if (grandprixinfo.gp == true && player->bot == false && losing == false) + if (grandprixinfo.gp == true + && (roundqueue.size && roundqueue.position < roundqueue.size) // Not the last map of GP + && player->bot == false && losing == false) { const UINT8 lifethreshold = 20; @@ -1407,6 +1409,11 @@ void P_DoAllPlayersExit(pflags_t flags, boolean trygivelife) // You've already finished, don't play again ; } + else if (gametyperules & GTR_SPECIALSTART) + { + // Warp out + S_StartSound(NULL, sfx_s3kb3); + } else if (musiccountdown == 0) { // Other people finish diff --git a/src/st_stuff.c b/src/st_stuff.c index c1ff32972..1e738dc5f 100644 --- a/src/st_stuff.c +++ b/src/st_stuff.c @@ -943,10 +943,10 @@ void ST_drawTitleCard(void) // Everything else... if (bossinfo.enemyname) { - bx = V_TitleCardStringWidth(bossinfo.enemyname); + bx = V_TitleCardStringWidth(bossinfo.enemyname, false); // Name. - V_DrawTitleCardString((BASEVIDWIDTH - bx)/2, 75, bossinfo.enemyname, 0, true, bossinfo.titleshow, lt_exitticker); + V_DrawTitleCardString((BASEVIDWIDTH - bx)/2, 75, bossinfo.enemyname, 0, true, bossinfo.titleshow, lt_exitticker, false); // Under-bar. { @@ -1067,10 +1067,10 @@ void ST_drawTitleCard(void) V_DrawFixedPatch(eggx2*FRACUNIT, eggy2*FRACUNIT, FRACUNIT, V_SNAPTOBOTTOM|V_SNAPTOLEFT, tccirclebottom, NULL); // Now the level name. - V_DrawTitleCardString((actnum) ? 265 : 280, 60, lvlttl, V_SNAPTORIGHT, false, lt_ticker, TTANIMENDTHRESHOLD); + V_DrawTitleCardString((actnum) ? 265 : 280, 60, lvlttl, V_SNAPTORIGHT, false, lt_ticker, TTANIMENDTHRESHOLD, false); if (!(mapheaderinfo[gamemap-1]->levelflags & LF_NOZONE)) - V_DrawTitleCardString((actnum) ? 265 : 280, 60+32, strlen(zonttl) ? zonttl : "ZONE", V_SNAPTORIGHT, false, lt_ticker - strlen(lvlttl), TTANIMENDTHRESHOLD); + V_DrawTitleCardString((actnum) ? 265 : 280, 60+32, strlen(zonttl) ? zonttl : "ZONE", V_SNAPTORIGHT, false, lt_ticker - strlen(lvlttl), TTANIMENDTHRESHOLD, false); // the act has a similar graphic animation, but we'll handle it here since it's only like 2 graphics lmfao. if (actnum && actnum < 10) diff --git a/src/v_video.cpp b/src/v_video.cpp index 9c6583916..d472d4ab3 100644 --- a/src/v_video.cpp +++ b/src/v_video.cpp @@ -1852,50 +1852,155 @@ void V_DrawChatCharacter(INT32 x, INT32 y, INT32 c, boolean lowercase, UINT8 *co ); } -// V_TitleCardStringWidth -// Get the string's width using the titlecard font. -INT32 V_TitleCardStringWidth(const char *str) +template +static INT32 Internal_TitleCardStringOffset(const char *str, boolean p4) { + int bg_font = GTOL_FONT; + int fg_font = GTFN_FONT; + + if (p4) + { + bg_font = GTOL4_FONT; + fg_font = GTFN4_FONT; + } + INT32 xoffs = 0; const char *ch = str; char c; patch_t *pp; - for (;;ch++) + // Returns true if it reached the end, false if interrupted. + auto scan = [&](auto keep_going) { - if (!*ch) - break; - - if (*ch == '\n') + for (;;ch++) { - xoffs = 0; - continue; + if (*ch == '\n') + { + xoffs = 0; + return false; + } + + if (!keep_going(*ch)) + { + break; + } + + c = *ch; + c = toupper(c); + c -= LT_FONTSTART; + + // check if character exists, if not, it's a space. + if (c < 0 || c >= LT_FONTSIZE || !fontv[bg_font].font[(INT32)c]) + { + xoffs += p4 ? 5 : 10; + continue; + } + + pp = fontv[fg_font].font[(INT32)c]; + + xoffs += pp->width - (p4 ? 3 : 5); } - c = *ch; - c = toupper(c); - c -= LT_FONTSTART; + return true; + }; - // check if character exists, if not, it's a space. - if (c < 0 || c >= LT_FONTSIZE || !fontv[GTOL_FONT].font[(INT32)c]) + do + { + // For the sake of centering, don't count spaces or + // punctuation at each end of a line. + // TODO: This should ideally be more sophisticated: + // - Check patch width directly for monospace or + // punctuation that isn't necessarily thin. + // - Apply to all centered string drawing. + if constexpr (Centered) { - xoffs += 10; - continue; + // Count leading fluff + if (!scan([](int c) { return c && !isalnum(c); })) + { + continue; + } + + if (!*ch) + { + // ALL fluff, so center it normally. + break; + } + + // xoffs gets halved later, which centers the + // string. If we don't want leading fluff to push + // everything to the right, its full width needs + // to be subtracted, so it's doubled here to + // cancel out the division. + xoffs *= 2; + + INT32 trim = -1; + + bool reached_end = scan( + [&trim, &xoffs](int c) + { + if (isalnum(c)) + { + trim = -1; + } + else if (trim < 0) + { + trim = xoffs; + } + + return c; + } + ); + + // Discount trailing fluff + if (reached_end && trim >= 0) + { + xoffs = trim; + } + } + else + { + scan([](int c) { return c; }); } - - pp = fontv[GTFN_FONT].font[(INT32)c]; - - xoffs += pp->width-5; } + while (*(ch++)); - return xoffs; + if constexpr (Centered) + { + return xoffs / 2; + } + else + { + return xoffs; + } +} + +// V_TitleCardStringWidth +// Get the string's width using the titlecard font. +INT32 V_TitleCardStringWidth(const char *str, boolean p4) +{ + return Internal_TitleCardStringOffset(str, p4); +} + +// V_CenteredTitleCardStringOffset +// Subtract this offset from an X coordinate to center the string around that point. +INT32 V_CenteredTitleCardStringOffset(const char *str, boolean p4) +{ + return Internal_TitleCardStringOffset(str, p4); } // V_DrawTitleCardScreen. // see v_video.h's prototype for more information. // -void V_DrawTitleCardString(INT32 x, INT32 y, const char *str, INT32 flags, boolean bossmode, INT32 timer, INT32 threshold) +void V_DrawTitleCardString(INT32 x, INT32 y, const char *str, INT32 flags, boolean bossmode, INT32 timer, INT32 threshold, boolean p4) { + int bg_font = GTOL_FONT; + int fg_font = GTFN_FONT; + + if (p4) + { + bg_font = GTOL4_FONT; + fg_font = GTFN4_FONT; + } INT32 xoffs = 0; INT32 yoffs = 0; @@ -1916,7 +2021,7 @@ void V_DrawTitleCardString(INT32 x, INT32 y, const char *str, INT32 flags, boole x -= 2; // Account for patch width... if (flags & V_SNAPTORIGHT) - x -= V_TitleCardStringWidth(str); + x -= V_TitleCardStringWidth(str, p4); for (;;ch++, i++) @@ -1933,7 +2038,7 @@ void V_DrawTitleCardString(INT32 x, INT32 y, const char *str, INT32 flags, boole if (*ch == '\n') { xoffs = x; - yoffs += 32; + yoffs += p4 ? 18 : 32; continue; } @@ -1944,14 +2049,14 @@ void V_DrawTitleCardString(INT32 x, INT32 y, const char *str, INT32 flags, boole c -= LT_FONTSTART; // check if character exists, if not, it's a space. - if (c < 0 || c >= LT_FONTSIZE || !fontv[GTFN_FONT].font[(INT32)c]) + if (c < 0 || c >= LT_FONTSIZE || !fontv[fg_font].font[(INT32)c]) { - xoffs += 10; + xoffs += p4 ? 5 : 10; continue; } - ol = fontv[GTOL_FONT].font[(INT32)c]; - pp = fontv[GTFN_FONT].font[(INT32)c]; + ol = fontv[bg_font].font[(INT32)c]; + pp = fontv[fg_font].font[(INT32)c]; if (bossmode) { @@ -2004,7 +2109,7 @@ void V_DrawTitleCardString(INT32 x, INT32 y, const char *str, INT32 flags, boole V_DrawStretchyFixedPatch((x + xoffs)*FRACUNIT + offs, (y+yoffs)*FRACUNIT, abs(scalex), FRACUNIT, flags|flipflag, pp, NULL); } - xoffs += pp->width -5; + xoffs += pp->width - (p4 ? 3 : 5); } } diff --git a/src/v_video.h b/src/v_video.h index f7c32505c..2f4989a24 100644 --- a/src/v_video.h +++ b/src/v_video.h @@ -347,10 +347,13 @@ void V_DrawRightAlignedThinStringAtFixed(fixed_t x, fixed_t y, INT32 option, con // threshold: when the letters start disappearing (leave to 0 to disable) (both are INT32 in case you supply negative values...) // NOTE: This function ignores most conventional string flags (V_RETURN8, V_FORCEUPPERCASE ...) // NOTE: This font only works with uppercase letters. -void V_DrawTitleCardString(INT32 x, INT32 y, const char *str, INT32 flags, boolean bossmode, INT32 timer, INT32 threshold); +void V_DrawTitleCardString(INT32 x, INT32 y, const char *str, INT32 flags, boolean bossmode, INT32 timer, INT32 threshold, boolean p4); // returns thr width of a string drawn using the above function. -INT32 V_TitleCardStringWidth(const char *str); +INT32 V_TitleCardStringWidth(const char *str, boolean p4); + +// offset that can be subtracted to center align. +INT32 V_CenteredTitleCardStringOffset(const char *str, boolean p4); // Draw tall nums, used for menu, HUD, intermission void V_DrawTallNum(INT32 x, INT32 y, INT32 flags, INT32 num); diff --git a/src/y_inter.c b/src/y_inter.c index 6c980a7ee..9ea49d4a0 100644 --- a/src/y_inter.c +++ b/src/y_inter.c @@ -1462,7 +1462,7 @@ void Y_IntermissionDrawer(void) } else { - headerwidth = V_TitleCardStringWidth(data.headerstring); + headerwidth = V_TitleCardStringWidth(data.headerstring, false); headerx = (BASEVIDWIDTH - headerwidth)/2; headery = 17; @@ -1490,7 +1490,7 @@ void Y_IntermissionDrawer(void) V_DrawMappedPatch(x + roundx, 39, 0, roundpatch, NULL); } - V_DrawTitleCardString(x + headerx, headery, data.headerstring, 0, false, 0, 0); + V_DrawTitleCardString(x + headerx, headery, data.headerstring, 0, false, 0, 0, false); } // Returns early if there's no players to draw