From a11acfaf0ea02606260195b09d928bc7cf4652da Mon Sep 17 00:00:00 2001 From: Antonio Martinez Date: Wed, 6 Aug 2025 19:51:36 -0400 Subject: [PATCH 1/5] WIP: splits --- src/d_player.h | 7 +++++ src/g_demo.cpp | 75 +++++++++++++++++++++++++++++++++++++++++++++++--- src/g_demo.h | 4 +++ src/k_hud.cpp | 75 ++++++++++++++++++++++++++++++++++++++------------ src/k_kart.c | 12 ++++++++ 5 files changed, 151 insertions(+), 22 deletions(-) diff --git a/src/d_player.h b/src/d_player.h index e73ff2e12..d9bc6d6b3 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -350,6 +350,13 @@ typedef enum khud_exp, khud_exptimer, + // Splits + khud_splittime, + khud_splitwin, + khud_splittimer, + khud_splitskin, + khud_splitcolor, + NUMKARTHUD } karthudtype_t; diff --git a/src/g_demo.cpp b/src/g_demo.cpp index 7a3d91004..71ef9ca38 100644 --- a/src/g_demo.cpp +++ b/src/g_demo.cpp @@ -118,7 +118,7 @@ size_t copy_fixed_buf(void* p, const void* s, size_t n) static char demoname[MAX_WADPATH]; static savebuffer_t demobuf = {0}; -static UINT8 *demotime_p, *demoinfo_p, *demoattack_p; +static UINT8 *demotime_p, *demoinfo_p, *demoattack_p, *demosplits_p; static UINT16 demoflags; boolean demosynced = true; // console warning message @@ -2068,6 +2068,13 @@ void G_BeginRecording(void) demoattack_p = demobuf.p; WRITEUINT32(demobuf.p, INT32_MAX); + demosplits_p = demobuf.p; + for (i = 0; i < MAXSPLITS; i++) + { + WRITEUINT32(demobuf.p, INT32_MAX); + } + + // Save netvar data CV_SaveDemoVars(&demobuf.p); @@ -2235,8 +2242,57 @@ void srb2::write_current_demo_end_marker() void G_SetDemoAttackTiming(tic_t time) { - if (!demo.playback) - *(UINT32 *)demoattack_p = time; + if (demo.playback) + return; + + *(UINT32 *)demoattack_p = time; +} + +void G_SetDemoCheckpointTiming(player_t *player, tic_t time, UINT8 checkpoint) +{ + if (demo.playback) + return; + if (checkpoint >= MAXSPLITS || checkpoint < 0) + return; + + UINT32 *splits = (UINT32 *)demosplits_p; + splits[checkpoint] = time; + + CONS_Printf("%d / %d\n", checkpoint, time); + + demoghost *g; + tic_t lowest = INT32_MAX; + UINT32 lowestskin = ((skin_t*)player->mo->skin) - skins; + UINT32 lowestcolor = player->skincolor; + for (g = ghosts; g; g = g->next) + { + if (lowest > g->splits[checkpoint]) + { + lowest = g->splits[checkpoint]; + lowestskin = ((skin_t*)g->mo->skin)-skins; + lowestcolor = g->mo->color; + + } + CONS_Printf("->%d\n", g->splits[checkpoint]); + } + + if (lowest != INT32_MAX) + { + player->karthud[khud_splittimer] = 1; + player->karthud[khud_splitskin] = lowestskin; + player->karthud[khud_splitcolor] = lowestcolor; + + if (lowest < time) + { + player->karthud[khud_splittime] = time - lowest; + player->karthud[khud_splitwin] = false; + } + else + { + player->karthud[khud_splittime] = lowest - time; + player->karthud[khud_splitwin] = true; + } + } } void G_SetDemoTime(UINT32 ptime, UINT32 plap) @@ -2594,6 +2650,8 @@ void G_LoadDemoInfo(menudemo_t *pdemo, boolean allownonmultiplayer) extrainfo_p = info.buffer + READUINT32(info.p); // The extra UINT32 read is for a blank 4 bytes? info.p += 4; // attack start + for (i = 0; i < MAXSPLITS; i++) + info.p += 4; // splits // Pared down version of CV_LoadNetVars to find the kart speed pdemo->kartspeed = KARTSPEED_NORMAL; // Default to normal speed @@ -3509,7 +3567,13 @@ void G_AddGhost(savebuffer_t *buffer, const char *defdemoname) } p += 4; // Extra data location reference - UINT32 attackstart = READUINT32(p); + tic_t attackstart = READUINT32(p); + + UINT8 *splits = p; + for (i = 0; i < MAXSPLITS; i++) + { + p += 4; + } // net var data count = READUINT16(p); @@ -3602,6 +3666,7 @@ void G_AddGhost(savebuffer_t *buffer, const char *defdemoname) gh->skinlist = skinlist; gh->attackstart = attackstart; + std::memcpy(gh->splits, splits, sizeof(tic_t) * MAXSPLITS); ghosts = gh; @@ -3755,6 +3820,8 @@ staffbrief_t *G_GetStaffGhostBrief(UINT8 *buffer) p += 4; // Extrainfo location marker p += 4; // Attack start info + for (i = 0; i < MAXSPLITS; i++) + p += 4; // splits // Ehhhh don't need ghostversion here (?) so I'll reuse the var here ghostversion = READUINT16(p); diff --git a/src/g_demo.h b/src/g_demo.h index 39292d54e..ab0bc4e68 100644 --- a/src/g_demo.h +++ b/src/g_demo.h @@ -168,6 +168,8 @@ extern UINT8 demo_writerng; #define DXD_PST_SPECTATING 0x02 #define DXD_PST_LEFT 0x03 +#define MAXSPLITS (200) + boolean G_CompatLevel(UINT16 level); // Record/playback tics @@ -203,6 +205,7 @@ struct demoghost { UINT16 version; UINT8 numskins; tic_t attackstart; + tic_t splits[MAXSPLITS]; boolean done; democharlist_t *skinlist; mobj_t oldmo, *mo; @@ -238,6 +241,7 @@ void G_SaveDemo(void); void G_ResetDemoRecording(void); void G_SetDemoAttackTiming(tic_t time); +void G_SetDemoCheckpointTiming(player_t *player, tic_t time, UINT8 checkpoint); boolean G_CheckDemoTitleEntry(void); diff --git a/src/k_hud.cpp b/src/k_hud.cpp index c3e444930..688b266bf 100644 --- a/src/k_hud.cpp +++ b/src/k_hud.cpp @@ -525,7 +525,7 @@ void K_LoadKartHUDGraphics(void) buffer[7] = '0'+((i) % 10); HU_UpdatePatch(&kp_overdrive[0][i], "%s", buffer); } - + sprintf(buffer, "bsOVRDxx"); for (i = 0; i < 32; i++) { @@ -2000,7 +2000,7 @@ static void K_drawKartItem(void) static void K_drawBackupItem(void) { bool tiny = r_splitscreen > 1; - patch_t *localpatch[3] = { kp_nodraw, kp_nodraw, kp_nodraw }; + patch_t *localpatch[3] = { kp_nodraw, kp_nodraw, kp_nodraw }; patch_t *localbg = (kp_itembg[2]); patch_t *localinv = kp_invincibility[((leveltime % (6*3)) / 3) + 7 + tiny]; INT32 fx = 0, fy = 0, fflags = 0, tx = 0, ty = 0; // final coords for hud and flags... @@ -3406,7 +3406,7 @@ static void K_drawKartDuelScores(void) { flags |= V_SNAPTOBOTTOM; flags &= ~V_SNAPTOTOP; - basey = BASEVIDHEIGHT - 40; + basey = BASEVIDHEIGHT - 40; } basex = BASEVIDWIDTH - 80; } @@ -3493,7 +3493,7 @@ static void K_drawKartDuelScores(void) V_DrawScaledPatch(basex-barheight+foeheight, basey, flags, kp_duel_4over); else V_DrawScaledPatch(basex, basey-barheight+foeheight, flags, kp_duel_over); - + if (!use4p) { V_DrawScaledPatch(basex, basey, flags, kp_duel_foe); @@ -3512,7 +3512,7 @@ static void K_drawKartDuelScores(void) } foenum.text("{}", foe->duelscore); - younum.text("{}", stplyr->duelscore); + younum.text("{}", stplyr->duelscore); // minirankings shamelessly copypasted because i know that shit works already // and SURELY we will never need to use this somewhere else, right? @@ -3526,7 +3526,7 @@ static void K_drawKartDuelScores(void) UINT8 drawme = draw ? (stplyr - players) : (foe - players); UINT16 drawx = basex + (draw ? youx : foex); UINT16 drawy = basey + (draw ? youy : foey); - + if (!playeringame[drawme] || players[drawme].spectator) continue; @@ -3790,8 +3790,8 @@ void K_drawKartTeamScores(boolean fromintermission, INT32 interoffset) if (R_GetViewNumber() == 1) { flags |= V_SNAPTOBOTTOM; - flags &= ~V_SNAPTOTOP; - basey = 170; + flags &= ~V_SNAPTOTOP; + basey = 170; } } } @@ -3817,7 +3817,7 @@ void K_drawKartTeamScores(boolean fromintermission, INT32 interoffset) return; using srb2::Draw; - srb2::Draw::Font scorefont = Draw::Font::kTimer; + srb2::Draw::Font scorefont = Draw::Font::kTimer; if (totalscore > 99) { @@ -3834,7 +3834,7 @@ void K_drawKartTeamScores(boolean fromintermission, INT32 interoffset) else { scorefont = Draw::Font::kZVote; - } + } } UINT32 youscore = stplyr->teamimportance; @@ -3870,7 +3870,7 @@ void K_drawKartTeamScores(boolean fromintermission, INT32 interoffset) if (teams_lastleveltime[vn] != leveltime) // Timing consistency { INT32 delta = abs(easedallyscore[vn] - allyscore); // how wrong is display score? - + if (scorechangecooldown[vn] == 0 && delta) { if (allyscore > easedallyscore[vn]) @@ -3886,9 +3886,9 @@ void K_drawKartTeamScores(boolean fromintermission, INT32 interoffset) enemycolor = R_GetTranslationColormap(TC_BLINK, SKINCOLOR_WHITE, GTC_CACHE); } scorechangecooldown[vn] = TICRATE/delta; - } + } } - + if (!fromintermission) { // replace scores with eased scores @@ -4009,7 +4009,7 @@ void K_drawKartTeamScores(boolean fromintermission, INT32 interoffset) if (totalscore > 99) { enemynum.text("{:03}", enemyscore); - allynum.text("{:03}", allyscore); + allynum.text("{:03}", allyscore); } else { @@ -5856,13 +5856,13 @@ INT32 K_GetMinimapSplitFlags(const boolean usingProgressBar) #define ICON_DOT_RADIUS (10) // modified pick from blondedradio/RadioRacers (but there are like 57 things we don't want in the commit) -// (so gogo gadget copypaste, thanks for a good feature and saving me work i was supposed to do anyway) +// (so gogo gadget copypaste, thanks for a good feature and saving me work i was supposed to do anyway) static void K_DrawKartUFOTimer(fixed_t objx, fixed_t objy, INT32 hudx, INT32 hudy, INT32 flags) { fixed_t amnumxpos, amnumypos; INT32 amxpos, amypos; - if (exitcountdown || leveltime > g_battleufo.due || battleprisons) + if (exitcountdown || leveltime > g_battleufo.due || battleprisons) return; tic_t raw = g_battleufo.due - leveltime; @@ -5872,7 +5872,7 @@ static void K_DrawKartUFOTimer(fixed_t objx, fixed_t objy, INT32 hudx, INT32 hud { flags |= (raw / (TICRATE/2) % 2) ? V_YELLOWMAP : 0; } - + if (countdown <= 10) { flags &= ~(V_HUDTRANS|V_HUDTRANSHALF); @@ -7758,9 +7758,48 @@ void K_drawKartHUD(void) using srb2::Draw; Draw::TextElement text = Draw::TextElement().parse(" Restart"); Draw(BASEVIDWIDTH - 19, 2) - .flags(flags | V_YELLOWMAP) + .flags(flags | V_ORANGEMAP) .align(Draw::Align::kRight) .text(text.string()); + + boolean debug_alwaysdraw = false; + + if (stplyr->karthud[khud_splittimer] || debug_alwaysdraw) + { + tic_t split = stplyr->karthud[khud_splittime]; + UINT32 skin = stplyr->karthud[khud_splitskin]; + UINT32 color = stplyr->karthud[khud_splitcolor]; + boolean ahead = stplyr->karthud[khud_splitwin]; + + // debug + if (!stplyr->karthud[khud_splittimer]) + { + ahead = !!((leveltime/17)%2); + split = leveltime; + skin = stplyr->skin; + color = stplyr->skincolor; + } + + UINT8 *skincolor = R_GetTranslationColormap(skin, static_cast(color), GTC_CACHE); + + UINT8 textcolor = ahead ? SKINCOLOR_SAPPHIRE : SKINCOLOR_KETCHUP; + + Draw row = Draw(BASEVIDWIDTH/2, BASEVIDHEIGHT/4).align(Draw::Align::kCenter) + .font(Draw::Font::kThinTimer).flags(V_30TRANS); + + // vibes offset + row.x(-32).colormap(skincolor).patch(R_CanShowSkinInDemo(skin) ? faceprefix[skin][FACE_MINIMAP] : kp_unknownminimap); + + Draw::TextElement text = Draw::TextElement( + std::string(ahead ? "-" : "+") + "{:02}'{:02}\"{:02}", + G_TicsToMinutes(split, true), + G_TicsToSeconds(split), + G_TicsToCentiseconds(split) + ); + + // vibes offset TWO + row.colormap(textcolor).colorize(textcolor).x(8).text(text); + } } else { diff --git a/src/k_kart.c b/src/k_kart.c index bbabb5051..a3996cfd2 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -4422,6 +4422,11 @@ void K_CheckpointCrossAward(player_t *player) if (gametype != GT_RACE) return; + if (!demo.playback && G_TimeAttackStart()) + { + G_SetDemoCheckpointTiming(player, leveltime, player->gradingpointnum); + } + player->gradingfactor += K_GetGradingFactorAdjustment(player); player->gradingpointnum++; player->exp = K_GetEXP(player); @@ -9039,6 +9044,13 @@ void K_KartPlayerHUDUpdate(player_t *player) if (player->karthud[khud_trickcool]) player->karthud[khud_trickcool]--; + if (player->karthud[khud_splittimer]) + { + player->karthud[khud_splittimer]++; + if (player->karthud[khud_splittimer] > 2*TICRATE) + player->karthud[khud_splittimer] = 0; + } + if (player->positiondelay) player->positiondelay--; From 024ae0f8abe898eec4092ab3471b5b54232e916a Mon Sep 17 00:00:00 2001 From: Antonio Martinez Date: Wed, 6 Aug 2025 22:09:06 -0400 Subject: [PATCH 2/5] More splits --- src/d_player.h | 1 + src/g_demo.cpp | 25 +++++++++++++++++-------- src/k_hud.cpp | 47 +++++++++++++++++++++++++++++++++++++---------- src/k_kart.c | 6 ++---- 4 files changed, 57 insertions(+), 22 deletions(-) diff --git a/src/d_player.h b/src/d_player.h index d9bc6d6b3..b1faf5ee9 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -356,6 +356,7 @@ typedef enum khud_splittimer, khud_splitskin, khud_splitcolor, + khud_splitlast, NUMKARTHUD } karthudtype_t; diff --git a/src/g_demo.cpp b/src/g_demo.cpp index 71ef9ca38..d90e357b5 100644 --- a/src/g_demo.cpp +++ b/src/g_demo.cpp @@ -2258,8 +2258,6 @@ void G_SetDemoCheckpointTiming(player_t *player, tic_t time, UINT8 checkpoint) UINT32 *splits = (UINT32 *)demosplits_p; splits[checkpoint] = time; - CONS_Printf("%d / %d\n", checkpoint, time); - demoghost *g; tic_t lowest = INT32_MAX; UINT32 lowestskin = ((skin_t*)player->mo->skin) - skins; @@ -2273,25 +2271,36 @@ void G_SetDemoCheckpointTiming(player_t *player, tic_t time, UINT8 checkpoint) lowestcolor = g->mo->color; } - CONS_Printf("->%d\n", g->splits[checkpoint]); } if (lowest != INT32_MAX) { - player->karthud[khud_splittimer] = 1; + player->karthud[khud_splittimer] = 3*TICRATE; player->karthud[khud_splitskin] = lowestskin; player->karthud[khud_splitcolor] = lowestcolor; + player->karthud[khud_splittime] = (INT32)time - (INT32)lowest; if (lowest < time) { - player->karthud[khud_splittime] = time - lowest; - player->karthud[khud_splitwin] = false; + player->karthud[khud_splitwin] = -2; // behind and losing } else { - player->karthud[khud_splittime] = lowest - time; - player->karthud[khud_splitwin] = true; + player->karthud[khud_splitwin] = 2; // ahead and gaining } + + INT32 last = player->karthud[khud_splitlast]; + INT32 now = player->karthud[khud_splittime]; + + if (checkpoint != 0) + { + if (player->karthud[khud_splitwin] > 0 && now > last) + player->karthud[khud_splitwin] = 1; // ahead but losing + else if (player->karthud[khud_splitwin] < 0 && now < last) + player->karthud[khud_splitwin] = -1; // behind but gaining + } + + player->karthud[khud_splitlast] = player->karthud[khud_splittime]; } } diff --git a/src/k_hud.cpp b/src/k_hud.cpp index 688b266bf..6306a64d2 100644 --- a/src/k_hud.cpp +++ b/src/k_hud.cpp @@ -7764,41 +7764,68 @@ void K_drawKartHUD(void) boolean debug_alwaysdraw = false; - if (stplyr->karthud[khud_splittimer] || debug_alwaysdraw) + if ( + ( + !stplyr->karthud[khud_lapanimation] && + stplyr->karthud[khud_splittimer] && + (stplyr->karthud[khud_splittimer] > TICRATE/3 || stplyr->karthud[khud_splittimer]%2 || cv_reducevfx.value) + ) + || debug_alwaysdraw + ) { - tic_t split = stplyr->karthud[khud_splittime]; - UINT32 skin = stplyr->karthud[khud_splitskin]; - UINT32 color = stplyr->karthud[khud_splitcolor]; - boolean ahead = stplyr->karthud[khud_splitwin]; + INT32 split = stplyr->karthud[khud_splittime]; + INT32 skin = stplyr->karthud[khud_splitskin]; + INT32 color = stplyr->karthud[khud_splitcolor]; + INT32 ahead = stplyr->karthud[khud_splitwin]; // debug if (!stplyr->karthud[khud_splittimer]) { - ahead = !!((leveltime/17)%2); + ahead = ((leveltime/17)%5) - 2; split = leveltime; skin = stplyr->skin; color = stplyr->skincolor; } + split = std::abs(split); + UINT8 *skincolor = R_GetTranslationColormap(skin, static_cast(color), GTC_CACHE); - UINT8 textcolor = ahead ? SKINCOLOR_SAPPHIRE : SKINCOLOR_KETCHUP; + UINT8 textcolor = SKINCOLOR_WHITE; + switch (ahead) + { + case 2: + textcolor = SKINCOLOR_SAPPHIRE; // leading and gaining + break; + case 1: + textcolor = SKINCOLOR_PIGEON; // leading and losing + break; + case -1: + textcolor = SKINCOLOR_RUBY; // trailing and gaining + break; + case -2: + textcolor = SKINCOLOR_CRIMSON; // trailing and losing + break; + } + Draw row = Draw(BASEVIDWIDTH/2, BASEVIDHEIGHT/4).align(Draw::Align::kCenter) .font(Draw::Font::kThinTimer).flags(V_30TRANS); + std::string arrow = (ahead == 1 || ahead == -2) ? "(" : ")"; + // vibes offset - row.x(-32).colormap(skincolor).patch(R_CanShowSkinInDemo(skin) ? faceprefix[skin][FACE_MINIMAP] : kp_unknownminimap); + row.x(-35).colormap(skincolor).patch(R_CanShowSkinInDemo(skin) ? faceprefix[skin][FACE_MINIMAP] : kp_unknownminimap); Draw::TextElement text = Draw::TextElement( - std::string(ahead ? "-" : "+") + "{:02}'{:02}\"{:02}", + std::string(ahead >= 0 ? "-" : "+") + " " + "{:02}'{:02}\"{:02} " + arrow, G_TicsToMinutes(split, true), G_TicsToSeconds(split), G_TicsToCentiseconds(split) ); // vibes offset TWO - row.colormap(textcolor).colorize(textcolor).x(8).text(text); + row.colormap(textcolor).colorize(textcolor).x(15).text(text); } } else diff --git a/src/k_kart.c b/src/k_kart.c index a3996cfd2..e81ba2471 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -9044,11 +9044,9 @@ void K_KartPlayerHUDUpdate(player_t *player) if (player->karthud[khud_trickcool]) player->karthud[khud_trickcool]--; - if (player->karthud[khud_splittimer]) + if (player->karthud[khud_splittimer] && !player->karthud[khud_lapanimation]) { - player->karthud[khud_splittimer]++; - if (player->karthud[khud_splittimer] > 2*TICRATE) - player->karthud[khud_splittimer] = 0; + player->karthud[khud_splittimer]--; } if (player->positiondelay) From 5703fb2a0b5d46632eb9ff90e93d88b51654486f Mon Sep 17 00:00:00 2001 From: Antonio Martinez Date: Thu, 7 Aug 2025 01:13:15 -0400 Subject: [PATCH 3/5] Adjust for start time when writing TA splits --- src/k_kart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/k_kart.c b/src/k_kart.c index e81ba2471..bee7eb759 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -4424,7 +4424,7 @@ void K_CheckpointCrossAward(player_t *player) if (!demo.playback && G_TimeAttackStart()) { - G_SetDemoCheckpointTiming(player, leveltime, player->gradingpointnum); + G_SetDemoCheckpointTiming(player, leveltime - starttime, player->gradingpointnum); } player->gradingfactor += K_GetGradingFactorAdjustment(player); From 848c3d4f7a89b9dba15a71419eb1496a908cb067 Mon Sep 17 00:00:00 2001 From: Antonio Martinez Date: Thu, 7 Aug 2025 04:53:24 -0400 Subject: [PATCH 4/5] Read splits in G_DoPlayDemoEx (crash fix) --- src/g_demo.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/g_demo.cpp b/src/g_demo.cpp index d90e357b5..92223887e 100644 --- a/src/g_demo.cpp +++ b/src/g_demo.cpp @@ -3161,6 +3161,10 @@ void G_DoPlayDemoEx(const char *defdemoname, lumpnum_t deflumpnum) demobuf.p += 4; // Extrainfo location demobuf.p += 4; // Attack start + for (i = 0; i < MAXSPLITS; i++) + { + demobuf.p += 4; // Splits + } // ...*map* not loaded? if (!gamemap || (gamemap > nummapheaders) || !mapheaderinfo[gamemap-1] || mapheaderinfo[gamemap-1]->lumpnum == LUMPERROR) From 565207ec080c493b9b0cd2f0d2a248777793c9a4 Mon Sep 17 00:00:00 2001 From: Antonio Martinez Date: Thu, 7 Aug 2025 14:54:24 -0400 Subject: [PATCH 5/5] MAXSPLITS 200 to 32 --- src/g_demo.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/g_demo.h b/src/g_demo.h index ab0bc4e68..61a6a17fc 100644 --- a/src/g_demo.h +++ b/src/g_demo.h @@ -168,7 +168,7 @@ extern UINT8 demo_writerng; #define DXD_PST_SPECTATING 0x02 #define DXD_PST_LEFT 0x03 -#define MAXSPLITS (200) +#define MAXSPLITS (32) boolean G_CompatLevel(UINT16 level);