diff --git a/src/d_main.c b/src/d_main.c index c806e4354..5ded2bf14 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -615,16 +615,6 @@ static void D_Display(void) { F_WipeEndScreen(); - // Funny. - if (WipeStageTitle && st_overlay) - { - lt_ticker--; - lt_lasttic = lt_ticker; - ST_preLevelTitleCardDrawer(); - V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, levelfadecol); - F_WipeStartScreen(); - } - F_RunWipe(wipedefs[wipedefindex], gamestate != GS_TIMEATTACK && gamestate != GS_TITLESCREEN, "FADEMAP0", true, false); } diff --git a/src/doomstat.h b/src/doomstat.h index d44ec24b0..19359df3b 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -420,7 +420,7 @@ typedef struct extern mapheader_t* mapheaderinfo[NUMMAPS]; // This could support more, but is that a good idea? -// Keep in mind that it may encourage people making overly long cups just because they "can", and would be a waste of memory. +// Keep in mind that it may encourage people making overly long cups just because they "can", and would be a waste of memory. #define MAXLEVELLIST 5 typedef struct cupheader_s diff --git a/src/f_finale.h b/src/f_finale.h index cc731f7f3..a45de5734 100644 --- a/src/f_finale.h +++ b/src/f_finale.h @@ -143,7 +143,7 @@ extern INT32 lastwipetic; // Don't know where else to place this constant // But this file seems appropriate -#define PRELEVELTIME 24 // frames in tics +#define PRELEVELTIME TICRATE // frames in tics void F_WipeStartScreen(void); void F_WipeEndScreen(void); diff --git a/src/g_game.c b/src/g_game.c index ed21c30f1..df2fbb931 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -1139,7 +1139,7 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) // Send leveltime when this tic was generated to the server for control lag calculations. // Only do this when in a level. Also do this after the hook, so that it can't overwrite this. - cmd->latency = (leveltime & 0xFF); + cmd->latency = (leveltime & 0xFF); } if (cmd->forwardmove > MAXPLMOVE) @@ -1301,7 +1301,7 @@ void G_StartTitleCard(void) ST_startTitleCard(); // start the title card - WipeStageTitle = false; //(!titlemapinaction); -- temporary until titlecards are reworked + WipeStageTitle = (!titlemapinaction); } // @@ -1322,7 +1322,7 @@ void G_PreLevelTitleCard(void) lasttime = nowtime; ST_runTitleCard(); - ST_preLevelTitleCardDrawer(); + ST_drawTitleCard(); I_FinishUpdate(); // page flip or blit buffer if (moviemode) diff --git a/src/hu_stuff.c b/src/hu_stuff.c index 3358f2fd3..7afc7d531 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -73,6 +73,8 @@ patch_t *pinggfx[5]; // small ping graphic patch_t *mping[5]; // smaller ping graphic +patch_t *tc_font[2][LT_FONTSIZE]; // Special font stuff for titlecard + patch_t *framecounter; patch_t *frameslash; // framerate stuff. Used in screen.c @@ -178,7 +180,8 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum); void HU_LoadGraphics(void) { - INT32 i; + char buffer[9]; + INT32 i, j; if (dedicated) return; @@ -191,6 +194,27 @@ void HU_LoadGraphics(void) emblemicon = HU_CachePatch("EMBLICON"); songcreditbg = HU_CachePatch("K_SONGCR"); + // Cache titlecard font + j = LT_FONTSTART; + for (i = 0; i < LT_FONTSIZE; i++, j++) + { + // cache the titlecard font + + // Bottom layer + sprintf(buffer, "GTOL%.3d", j); + if (W_CheckNumForName(buffer) == LUMPERROR) + tc_font[0][i] = NULL; + else + tc_font[0][i] = (patch_t *)W_CachePatchName(buffer, PU_HUDGFX); + + // Top layer + sprintf(buffer, "GTFN%.3d", j); + if (W_CheckNumForName(buffer) == LUMPERROR) + tc_font[1][i] = NULL; + else + tc_font[1][i] = (patch_t *)W_CachePatchName(buffer, PU_HUDGFX); + } + // cache ping gfx: for (i = 0; i < 5; i++) { @@ -704,7 +728,7 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum) } else if (target == -1) // say team { - if (players[playernum].ctfteam == 1) + if (players[playernum].ctfteam == 1) { // red text cstart = textcolor = "\x85"; diff --git a/src/hu_stuff.h b/src/hu_stuff.h index 6a425926b..4d686516e 100644 --- a/src/hu_stuff.h +++ b/src/hu_stuff.h @@ -39,6 +39,11 @@ #define LT_FONTEND 'z' // the last font characters #define LT_FONTSIZE (LT_FONTEND - LT_FONTSTART + 1) +// Under regular circumstances, we'd use the built in font stuff, however this font is a bit messy because of how we're gonna draw shit. +// tc_font[0][n] is used for the "bottom" layer +// tc_font[1][n] is used for the "top" layer +extern patch_t *tc_font[2][LT_FONTSIZE]; + #define CRED_FONTSTART '!' // the first font character #define CRED_FONTEND 'Z' // the last font character #define CRED_FONTSIZE (CRED_FONTEND - CRED_FONTSTART + 1) diff --git a/src/lua_hudlib.c b/src/lua_hudlib.c index 926df78ca..75a84ed18 100644 --- a/src/lua_hudlib.c +++ b/src/lua_hudlib.c @@ -946,6 +946,24 @@ static int libd_drawString(lua_State *L) return 0; } +static int libd_drawTitleCardString(lua_State *L) +{ + + fixed_t x = luaL_checkinteger(L, 1); + fixed_t y = luaL_checkinteger(L, 2); + const char *str = luaL_checkstring(L, 3); + INT32 flags = luaL_optinteger(L, 4, V_ALLOWLOWERCASE); + boolean rightalign = lua_optboolean(L, 5); + INT32 timer = luaL_optinteger(L, 6, 0); + INT32 threshold = luaL_optinteger(L, 7, 0); + + flags &= ~V_PARAMMASK; // Don't let crashes happen. + + HUDONLY + V_DrawTitleCardString(x, y, str, flags, rightalign, timer, threshold); + return 0; +} + static int libd_drawKartString(lua_State *L) { fixed_t x = luaL_checkinteger(L, 1); @@ -960,6 +978,15 @@ static int libd_drawKartString(lua_State *L) return 0; } +static int libd_titleCardStringWidth(lua_State *L) +{ + const char *str = luaL_checkstring(L, 1); + HUDONLY + + lua_pushinteger(L, V_TitleCardStringWidth(str)); + return 1; +} + static int libd_stringWidth(lua_State *L) { const char *str = luaL_checkstring(L, 1); @@ -1163,9 +1190,11 @@ static luaL_Reg lib_draw[] = { {"drawFill", libd_drawFill}, {"fadeScreen", libd_fadeScreen}, {"drawString", libd_drawString}, + {"drawTitleCardString", libd_drawTitleCardString}, {"drawKartString", libd_drawKartString}, // misc {"stringWidth", libd_stringWidth}, + {"titleCardStringWidth", libd_titleCardStringWidth}, // m_random {"RandomFixed",libd_RandomFixed}, {"RandomByte",libd_RandomByte}, diff --git a/src/p_setup.c b/src/p_setup.c index 1c2d46e9f..7ec44eb71 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -3981,8 +3981,8 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate) F_RunWipe(wipedefs[wipe_level_toblack], false, ((levelfadecol == 0) ? "FADEMAP1" : "FADEMAP0"), false, false); } - if (!titlemapinaction) - wipegamestate = GS_LEVEL; + /*if (!titlemapinaction) + wipegamestate = GS_LEVEL;*/ // Close text prompt before freeing the old level F_EndTextPrompt(false, true); diff --git a/src/st_stuff.c b/src/st_stuff.c index 16118cbee..43d1821ec 100644 --- a/src/st_stuff.c +++ b/src/st_stuff.c @@ -31,6 +31,7 @@ #include "m_misc.h" // moviemode #include "m_anigif.h" // cv_gif_downscale #include "p_setup.h" // NiGHTS grading +#include "k_grandprix.h" // we need to know grandprix status for titlecards //random index #include "m_random.h" @@ -622,23 +623,97 @@ static void ST_drawDebugInfo(void) V_DrawRightAlignedString(320, height, V_MONOSPACE, va("Heap used: %7sKB", sizeu1(Z_TagsUsage(0, INT32_MAX)>>10))); } -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; +// SRB2KART: HUD shit for new titlecards: +static patch_t *tcchev1; +static patch_t *tcchev2; + +static patch_t *tcol1; +static patch_t *tcol2; + +static patch_t *tcroundbar; +static patch_t *tcround; + +static patch_t *tccircletop; +static patch_t *tccirclebottom; +static patch_t *tccirclebg; + +static patch_t *tcbanner; +static patch_t *tcbanner2; + +static patch_t *tcroundnum[10]; +static patch_t *tcactnum[10]; +static patch_t *tcact; + +// some coordinates define to make my life easier.... +#define FINAL_ROUNDX (24) +#define FINAL_EGGY (160) +#define FINAL_ROUNDY (16) +#define FINAL_BANNERY (160) + +INT32 chev1x, chev1y, chev2x, chev2y, chevtflag; +INT32 roundx, roundy; +INT32 bannerx, bannery; + +INT32 roundnumx, roundnumy; +INT32 eggx1, eggx2, eggy1, eggy2; + +// These are all arbitrary values found by trial and error trying to align the hud lmao. +// But they'll work. +#define BASE_CHEV1X (252) +#define BASE_CHEV1Y (60) +#define BASE_CHEV2X (65) +#define BASE_CHEV2Y (135) + +#define TTANIMTHRESHOLD (TICRATE) +#define TTANIMSTART (TTANIMTHRESHOLD-16) +#define TTANIMENDTHRESHOLD (TICRATE*3) +#define TTANIMEND (TICRATE*4) + // // Load the graphics for the title card. // Don't let LJ see this // static void ST_cacheLevelTitle(void) { - lt_patches[0] = (patch_t *)W_CachePatchName("LTACTBLU", PU_HUDGFX); - lt_patches[1] = (patch_t *)W_CachePatchName("LTZIGZAG", PU_HUDGFX); - lt_patches[2] = (patch_t *)W_CachePatchName("LTZZTEXT", PU_HUDGFX); + UINT8 i; + char buf[9]; + + // SRB2KART + tcchev1 = (patch_t *)W_CachePatchName("TCCHEV1W", PU_HUDGFX); + tcchev2 = (patch_t *)W_CachePatchName("TCCHEV2W", PU_HUDGFX); + + tcol1 = (patch_t *)W_CachePatchName("TCCHOL1", PU_HUDGFX); + tcol2 = (patch_t *)W_CachePatchName("TCCHOL2", PU_HUDGFX); + + tcroundbar = (patch_t *)W_CachePatchName("TCBB0", PU_HUDGFX); + tcround = (patch_t *)W_CachePatchName("TCROUND", PU_HUDGFX); + + tccircletop = (patch_t *)W_CachePatchName("TCSN1", PU_HUDGFX); + tccirclebottom =(patch_t *)W_CachePatchName("TCSN2", PU_HUDGFX); + tccirclebg = (patch_t *)W_CachePatchName("TCEG3", PU_HUDGFX); + + tcbanner = (patch_t *)W_CachePatchName("TCBSKA0", PU_HUDGFX); + tcbanner2 = (patch_t *)W_CachePatchName("TCBC0", PU_HUDGFX); + + tcact = (patch_t *)W_CachePatchName("TT_ACT", PU_HUDGFX); + + // Cache round # + for (i=1; i < 11; i++) + { + sprintf(buf, "TT_RND%d", i); + tcroundnum[i-1] = (patch_t *)W_CachePatchName(buf, PU_HUDGFX); + } + + // Cache act # + for (i=0; i < 10; i++) + { + sprintf(buf, "TT_ACT%d", i); + tcactnum[i] = (patch_t *)W_CachePatchName(buf, PU_HUDGFX); + } + } // @@ -649,12 +724,28 @@ void ST_startTitleCard(void) // cache every HUD patch used ST_cacheLevelTitle(); + // Set most elements to start off-screen, ST_runTitleCard will have them slide in afterwards + chev1x = BASE_CHEV1X +350; // start off-screen + chev1y = BASE_CHEV1Y; + chev2x = BASE_CHEV2X -350; // start off-screen + chev2y = BASE_CHEV2Y; + chevtflag = 0; + + roundx = -999; + roundy = -999; + + roundnumx = -999; + roundnumy = -999; + eggx1 = -999; + eggx2 = -999; + eggy1 = -999; + eggy2 = -999; + + bannery = 300; + // 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; + lt_endtime = 4*TICRATE; // + (10*NEWTICRATERATIO); } // @@ -679,6 +770,8 @@ void ST_preDrawTitleCard(void) void ST_runTitleCard(void) { boolean run = !(paused || P_AutoPause()); + tic_t auxticker; + boolean gp = (grandprixinfo.gp && grandprixinfo.roundnum); // check whether we're in grandprix if (!G_IsTitleCardAvailable()) return; @@ -690,35 +783,152 @@ void ST_runTitleCard(void) { // tick lt_ticker++; + + // SRB2KART + // side Zig-Zag positions... + + // TITLECARD START + if (lt_ticker < TTANIMSTART) + { + chev1x = (BASE_CHEV1X + 350) - lt_ticker*50; + if (chev1x < BASE_CHEV1X) + chev1x = BASE_CHEV1X; // min/max macros don't work well with signed, it seems + + chev2x = (BASE_CHEV2X - 350) + lt_ticker*50; + if (chev2x > BASE_CHEV2X) + chev2x = BASE_CHEV2X; // ditto + } + + // OPEN ZIG-ZAGS 1 SECOND IN + if (lt_ticker > TTANIMTHRESHOLD) + { + auxticker = lt_ticker - TTANIMTHRESHOLD; + + chev1x = BASE_CHEV1X + auxticker*16; + if (chev1x > 320) + chev1x = 320; + + chev1y = BASE_CHEV1Y - auxticker*16; + if (chev1y < 0) + chev1y = 0; + + chev2x = BASE_CHEV2X - auxticker*16; + if (chev2x < 0) + chev2x = 0; + + chev2y = BASE_CHEV2Y + auxticker*16; + if (chev2y > 200) + chev2y = 200; + + // translucent fade after opening up. + chevtflag = min(5, ((auxticker)/5)) << V_ALPHASHIFT; + + + // OPEN ZIG-ZAG: END OF ANIMATION (they leave the screen borders) + if (lt_ticker > TTANIMENDTHRESHOLD) + { + auxticker = lt_ticker - TTANIMENDTHRESHOLD; + + chev1x += auxticker*16; + chev1y -= auxticker*16; + + chev2x -= auxticker*16; + chev2y += auxticker*16; + } + } + + // ROUND BAR + EGG + + eggy1 = FINAL_EGGY; // Make sure to reset that each call so that Y position doesn't go bonkers + + // SLIDE BAR IN, SLIDE "ROUND" DOWNWARDS + if (lt_ticker <= TTANIMTHRESHOLD) + { + INT32 interptimer = (INT32)lt_ticker - TTANIMSTART; + // INT32 because tic_t is unsigned and we want this to be potentially negative + + if (interptimer >= 0) + { + INT32 interpdiff = ((TTANIMTHRESHOLD-TTANIMSTART) - interptimer); + interpdiff *= interpdiff; // interpdiff^2 + + roundx = FINAL_ROUNDX - interpdiff; + roundy = FINAL_ROUNDY - interpdiff; + eggy1 = FINAL_EGGY + interpdiff; + + } + } + // SLIDE BAR OUT, SLIDE "ROUND" DOWNWARDS FASTER + else if (lt_ticker >= TTANIMENDTHRESHOLD) + { + auxticker = lt_ticker - TTANIMENDTHRESHOLD; + + roundx = FINAL_ROUNDX - auxticker*24; + roundy = FINAL_ROUNDY + auxticker*48; + eggy1 = FINAL_EGGY + auxticker*48; + } + + // follow the round bar. + eggx1 = roundx + tcroundbar->width/2; + + // initially, both halves are on the same coordinates. + eggx2 = eggx1; + eggy2 = eggy1; + // same for the background (duh) + roundnumx = eggx1; + roundnumy = eggy1; + + // split both halves of the egg, but only do that in grand prix! + if (gp && lt_ticker > TTANIMTHRESHOLD + TICRATE/2) + { + auxticker = lt_ticker - (TTANIMTHRESHOLD + TICRATE/2); + + eggx1 -= auxticker*12; + eggy1 -= auxticker*12; + + eggx2 += auxticker*12; + eggy2 += auxticker*12; + + } + + + // SCROLLING BOTTOM BANNER + + // SLIDE BANNER UPWARDS WITH A FUNNY BOUNCE (this requires trig :death:) + if (lt_ticker < TTANIMTHRESHOLD) + { + INT32 costimer = (INT32)lt_ticker - TTANIMSTART; + // INT32 because tic_t is unsigned and we want this to be potentially negative + + if (costimer > 0) + { + // For this animation, we're going to do a tiny bit of stupid trigonometry. + // Admittedly all of this is going to look like magic numbers, and honestly? They are. + + // start at angle 355 (where y = ~230 with our params) + // and go to angle 131 (where y = ~160 with our params) + + UINT8 basey = 190; + UINT8 amplitude = 45; + fixed_t ang = (355 - costimer*14)*FRACUNIT; + + bannery = basey + (amplitude * FINECOSINE(FixedAngle(ang)>>ANGLETOFINESHIFT)) / FRACUNIT; + } + } + // SLIDE BANNER DOWNWARDS OUT OF THE SCREEN AT THE END + else if (lt_ticker >= TTANIMENDTHRESHOLD) + { + auxticker = lt_ticker - TTANIMENDTHRESHOLD; + bannery = FINAL_BANNERY + auxticker*16; + } + + // No matter the circumstances, scroll the banner... + bannerx = -(lt_ticker%(tcbanner->width)); + + + // used for hud slidein 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; } } @@ -729,25 +939,17 @@ void ST_runTitleCard(void) void ST_drawTitleCard(void) { char *lvlttl = mapheaderinfo[gamemap-1]->lvlttl; - char *subttl = mapheaderinfo[gamemap-1]->subttl; char *zonttl = mapheaderinfo[gamemap-1]->zonttl; // SRB2kart UINT8 actnum = mapheaderinfo[gamemap-1]->actnum; - INT32 lvlttlxpos, ttlnumxpos, zonexpos; - INT32 subttlxpos = BASEVIDWIDTH/2; - INT32 ttlscroll = FixedInt(lt_scroll); -#ifdef TITLEPATCHES - INT32 zzticker; - patch_t *actpat, *zigzag, *zztext; - UINT8 colornum; - const UINT8 *colormap; + boolean gp = (grandprixinfo.gp && grandprixinfo.roundnum); - if (players[g_localplayers[0]].skincolor) - colornum = players[g_localplayers[0]].skincolor; - else - colornum = cv_playercolor[0].value; + INT32 acttimer; + fixed_t actscale; + angle_t fakeangle; - colormap = R_GetTranslationColormap(TC_DEFAULT, colornum, GTC_CACHE); -#endif + INT32 bx = bannerx; // We need to make a copy of that otherwise pausing will cause problems. + + UINT8 i; if (!G_IsTitleCardAvailable()) return; @@ -761,66 +963,88 @@ void ST_drawTitleCard(void) if ((lt_ticker-lt_lasttic) > 1) lt_ticker = lt_lasttic+1; - ST_cacheLevelTitle(); + // Avoid HOMs while drawing the start of the titlecard + if (lt_ticker < TTANIMSTART) + V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31); -#ifdef TITLEPATCHES - actpat = lt_patches[0]; - zigzag = lt_patches[1]; - zztext = lt_patches[2]; -#endif + // Background zig-zags + V_DrawFixedPatch((chev1x)*FRACUNIT, (chev1y)*FRACUNIT, FRACUNIT, chevtflag, tcchev1, NULL); + V_DrawFixedPatch((chev2x)*FRACUNIT, (chev2y)*FRACUNIT, FRACUNIT, chevtflag, tcchev2, NULL); - lvlttlxpos = ((BASEVIDWIDTH/2) - (V_LevelNameWidth(lvlttl)/2)); - if (actnum > 0) - lvlttlxpos -= V_LevelNameWidth(va("%d", actnum)); + // Draw ROUND bar, scroll it downwards. + V_DrawFixedPatch(roundx*FRACUNIT, ((-32) + (lt_ticker%32))*FRACUNIT, FRACUNIT, V_SNAPTOTOP|V_SNAPTOLEFT, tcroundbar, NULL); + // Draw ROUND text + if (gp) + V_DrawFixedPatch((roundx+10)*FRACUNIT, roundy*FRACUNIT, FRACUNIT, V_SNAPTOTOP|V_SNAPTOLEFT, tcround, NULL); - zonexpos = ttlnumxpos = lvlttlxpos + V_LevelNameWidth(lvlttl); - if (zonttl[0]) - zonexpos -= V_LevelNameWidth(zonttl); // SRB2kart - else - zonexpos -= V_LevelNameWidth(M_GetText("Zone")); + // round num background + V_DrawFixedPatch(roundnumx*FRACUNIT, roundnumy*FRACUNIT, FRACUNIT, V_SNAPTOBOTTOM|V_SNAPTOLEFT, tccirclebg, NULL); - ttlnumxpos++; - - if (lvlttlxpos < 0) - lvlttlxpos = 0; - -#ifdef TITLEPATCHES - if (!splitscreen || (splitscreen && stplyr == &players[displayplayers[0]])) + // Scrolling banner, we'll draw 3 of those back to back. + for (i=0; i < 3; i++) { - zzticker = lt_ticker; - V_DrawMappedPatch(FixedInt(lt_zigzag), (-zzticker) % zigzag->height, V_SNAPTOTOP|V_SNAPTOLEFT, zigzag, colormap); - V_DrawMappedPatch(FixedInt(lt_zigzag), (zigzag->height-zzticker) % zigzag->height, V_SNAPTOTOP|V_SNAPTOLEFT, zigzag, colormap); - V_DrawMappedPatch(FixedInt(lt_zigzag), (-zigzag->height+zzticker) % zztext->height, V_SNAPTOTOP|V_SNAPTOLEFT, zztext, colormap); - V_DrawMappedPatch(FixedInt(lt_zigzag), (zzticker) % zztext->height, V_SNAPTOTOP|V_SNAPTOLEFT, zztext, colormap); + V_DrawFixedPatch((bannerx + bx)*FRACUNIT, (bannery)*FRACUNIT, FRACUNIT, V_SNAPTOBOTTOM|V_SNAPTOLEFT, tcbanner, NULL); + bx += tcbanner->width; } -#endif - if (actnum) + // If possible, draw round number + if (gp && grandprixinfo.roundnum > 0 && grandprixinfo.roundnum < 11) // Check boundaries JUST IN CASE. + V_DrawFixedPatch(roundnumx*FRACUNIT, roundnumy*FRACUNIT, FRACUNIT, V_SNAPTOBOTTOM|V_SNAPTOLEFT, tcroundnum[grandprixinfo.roundnum-1], NULL); + + // Draw both halves of the egg + V_DrawFixedPatch(eggx1*FRACUNIT, eggy1*FRACUNIT, FRACUNIT, V_SNAPTOBOTTOM|V_SNAPTOLEFT, tccircletop, NULL); + 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, true, lt_ticker, TTANIMENDTHRESHOLD); + + if (!(mapheaderinfo[gamemap-1]->levelflags & LF_NOZONE)) + V_DrawTitleCardString((actnum) ? 265 : 280, 60+32, strlen(zonttl) ? zonttl : "ZONE", V_SNAPTORIGHT, true, lt_ticker - strlen(lvlttl), TTANIMENDTHRESHOLD); + + // 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) { -#ifdef TITLEPATCHES - if (!splitscreen) + + // compute delay before the act should appear. + acttimer = lt_ticker - strlen(lvlttl); + if (!(mapheaderinfo[gamemap-1]->levelflags & LF_NOZONE)) + acttimer -= strlen((strlen(zonttl)) ? (zonttl) : ("ZONE")); + + actscale = 0; + fakeangle = 0; + + if (acttimer >= 0) { - if (actnum > 9) // slightly offset the act diamond for two-digit act numbers - V_DrawMappedPatch(ttlnumxpos + (V_LevelNameWidth(va("%d", actnum))/4) + ttlscroll, 104 - ttlscroll, 0, actpat, colormap); - else - V_DrawMappedPatch(ttlnumxpos + ttlscroll, 104 - ttlscroll, 0, actpat, colormap); - } -#endif - V_DrawLevelTitle(ttlnumxpos + ttlscroll, 104, 0, va("%d", actnum)); - } - V_DrawLevelTitle(lvlttlxpos - ttlscroll, 80, 0, lvlttl); - if (zonttl[0]) - V_DrawLevelTitle(zonexpos + ttlscroll, 104, 0, zonttl); - else if (!(mapheaderinfo[gamemap-1]->levelflags & LF_NOZONE)) - V_DrawLevelTitle(zonexpos + ttlscroll, 104, 0, M_GetText("Zone")); - V_DrawCenteredString(subttlxpos - ttlscroll, 135, 0|V_ALLOWLOWERCASE, subttl); + if (acttimer < TTANIMENDTHRESHOLD) // spin in + { + fakeangle = min(360 + 90, acttimer*41) * ANG1; + actscale = FINESINE(fakeangle>>ANGLETOFINESHIFT); + } + else // spin out + { + // Make letters disappear... + acttimer -= TTANIMENDTHRESHOLD; + + fakeangle = max(0, (360+90) - acttimer*41)*ANG1; + actscale = FINESINE(fakeangle>>ANGLETOFINESHIFT); + } + + if (actscale) + { + // draw the top: + V_DrawStretchyFixedPatch(286*FRACUNIT, 76*FRACUNIT, abs(actscale), FRACUNIT, V_SNAPTORIGHT|(actscale < 0 ? V_FLIP : 0), tcact, NULL); + V_DrawStretchyFixedPatch(286*FRACUNIT, 123*FRACUNIT, abs(actscale), FRACUNIT, V_SNAPTORIGHT|(actscale < 0 ? V_FLIP : 0), tcactnum[actnum], NULL); + } + } + } lt_lasttic = lt_ticker; luahook: LUAh_TitleCardHUD(stplyr); + } // @@ -829,6 +1053,7 @@ luahook: void ST_preLevelTitleCardDrawer(void) { V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, levelfadecol); + ST_drawWipeTitleCard(); I_OsPolling(); I_UpdateNoBlit(); @@ -840,14 +1065,7 @@ void ST_preLevelTitleCardDrawer(void) // void ST_drawWipeTitleCard(void) { - UINT8 i; - - for (i = 0; i <= r_splitscreen; i++) - { - stplyr = &players[displayplayers[i]]; - ST_preDrawTitleCard(); - ST_drawTitleCard(); - } + ST_drawTitleCard(); } // diff --git a/src/v_video.c b/src/v_video.c index 85adeb4eb..5bd7ba733 100644 --- a/src/v_video.c +++ b/src/v_video.c @@ -1676,6 +1676,149 @@ void V_DrawChatCharacter(INT32 x, INT32 y, INT32 c, boolean lowercaseallowed, UI V_DrawFixedPatch(x*FRACUNIT, y*FRACUNIT, FRACUNIT/2, flags, fontv[HU_FONT].font[c], colormap); } +// V_TitleCardStringWidth +// Get the string's width using the titlecard font. +INT32 V_TitleCardStringWidth(const char *str) +{ + INT32 xoffs = 0; + const char *ch = str; + char c; + patch_t *pp; + + for (;;ch++) + { + if (!*ch) + break; + + if (*ch == '\n') + { + xoffs = 0; + continue; + } + + c = *ch; + c = toupper(c); + c -= LT_FONTSTART; + + // check if character exists, if not, it's a space. + if (c < 0 || c >= LT_FONTSIZE || !tc_font[0][(INT32)c]) + { + xoffs += 10; + continue; + } + + pp = tc_font[1][(INT32)c]; + + xoffs += pp->width-5; + } + + return xoffs; +} + +// V_DrawTitleCardScreen. +// see v_video.h's prototype for more information. +// +void V_DrawTitleCardString(INT32 x, INT32 y, const char *str, INT32 flags, boolean alignright, INT32 timer, INT32 threshold) +{ + + INT32 xoffs = 0; + INT32 yoffs = 0; + INT32 i = 0; + + // per-letter variables + fixed_t scalex; + fixed_t offs; + INT32 let_time; + INT32 flipflag; + angle_t fakeang; + + const char *ch = str; + char c; + patch_t *pp; + patch_t *ol; + + x -= 2; // Account for patch width... + + if (alignright) + x -= V_TitleCardStringWidth(str); + + + for (;;ch++, i++) + { + + scalex = FRACUNIT; + offs = 0; + let_time = timer - i; + flipflag = 0; + + if (!*ch) + break; + + if (*ch == '\n') + { + xoffs = x; + yoffs += 32; + + continue; + } + + c = *ch; + + c = toupper(c); + c -= LT_FONTSTART; + + // check if character exists, if not, it's a space. + if (c < 0 || c >= LT_FONTSIZE || !tc_font[1][(INT32)c]) + { + xoffs += 10; + continue; + } + + ol = tc_font[0][(INT32)c]; + pp = tc_font[1][(INT32)c]; + + if (timer) + { + + // make letters appear + if (!threshold || let_time < threshold) + { + if (let_time <= 0) + return; // No reason to continue drawing, none of the next letters will be drawn either. + + // otherwise; scalex must start at 0 + // let's have each letter do 4 spins (360*4 + 90 = 1530 "degrees") + fakeang = min(360 + 90, let_time*41) * ANG1; + scalex = FINESINE(fakeang>>ANGLETOFINESHIFT); + } + else if (let_time > threshold) + { + // Make letters disappear... + let_time -= threshold; + + fakeang = max(0, (360+90) - let_time*41)*ANG1; + scalex = FINESINE(fakeang>>ANGLETOFINESHIFT); + } + + // Because of how our patches are offset, we need to counter the displacement caused by changing the scale with an offset of our own. + offs = ((FRACUNIT-scalex)*pp->width)/2; + } + + // And now, we just need to draw the stuff. + flipflag = (scalex < 0) ? V_FLIP : 0; + + if (scalex && ol && pp) + { + //CONS_Printf("%d\n", (INT32)c); + V_DrawStretchyFixedPatch((x + xoffs)*FRACUNIT + offs, (y+yoffs)*FRACUNIT, abs(scalex), FRACUNIT, flags|flipflag, ol, NULL); + V_DrawStretchyFixedPatch((x + xoffs)*FRACUNIT + offs, (y+yoffs)*FRACUNIT, abs(scalex), FRACUNIT, flags|flipflag, pp, NULL); + } + + xoffs += pp->width -5; + } +} + + // Precompile a wordwrapped string to any given width. // This is a muuuch better method than V_WORDWRAP. char *V_WordWrap(INT32 x, INT32 w, INT32 option, const char *string) diff --git a/src/v_video.h b/src/v_video.h index 08a279fd0..da4506c9a 100644 --- a/src/v_video.h +++ b/src/v_video.h @@ -262,6 +262,16 @@ void V_DrawRightAlignedThinString(INT32 x, INT32 y, INT32 option, const char *st #define V_DrawThinStringAtFixed( x,y,option,string ) \ V__DrawOneScaleString (x,y,FRACUNIT,option,TINY_FONT,string) +// Draws a titlecard font string. +// timer: when the letters start appearing (leave to 0 to disable) +// 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_ALLOWLOWERCASE ...) +// NOTE: This font only works with uppercase letters. +void V_DrawTitleCardString(INT32 x, INT32 y, const char *str, INT32 flags, boolean alignright, INT32 timer, INT32 threshold); + +// returns thr width of a string drawn using the above function. +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);