diff --git a/src/d_player.h b/src/d_player.h index 31ee89f12..587d73623 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -44,6 +44,8 @@ extern "C" { // (done here as p_local.h, the previous host, has this as a dependency - but we must use it here) #define MAX_LAPS 99 +#define MAXRACESPLITS 32 + // Extra abilities/settings for skins (combinable stuff) typedef enum { @@ -351,12 +353,11 @@ typedef enum khud_exptimer, // Splits - khud_splittime, - khud_splitwin, - khud_splittimer, - khud_splitskin, - khud_splitcolor, - khud_splitlast, + khud_splittime, // Delta between you and highest split + khud_splitwin, // How to color/flag the split based on gaining/losing | ahead/behind + khud_splittimer, // How long to show splits HUD + khud_splitskin, // Skin index of the leading player + khud_splitcolor, // Skincolor of the leading player NUMKARTHUD } karthudtype_t; @@ -1132,6 +1133,9 @@ struct player_t fixed_t transfer; // Tired of Ramp Park fastfalls + tic_t splits[MAXRACESPLITS]; // Times we crossed checkpoint + INT32 pace; // Last split delta, used for checking whether gaining or losing time + uint8_t public_key[PUBKEYLENGTH]; #ifdef HWRENDER diff --git a/src/g_demo.cpp b/src/g_demo.cpp index 39f714e3a..1e9ab65fa 100644 --- a/src/g_demo.cpp +++ b/src/g_demo.cpp @@ -2292,7 +2292,7 @@ void G_SetDemoCheckpointTiming(player_t *player, tic_t time, UINT8 checkpoint) player->karthud[khud_splitwin] = 2; // ahead and gaining } - INT32 last = player->karthud[khud_splitlast]; + INT32 last = player->pace; INT32 now = player->karthud[khud_splittime]; if (checkpoint != 0) @@ -2303,7 +2303,7 @@ void G_SetDemoCheckpointTiming(player_t *player, tic_t time, UINT8 checkpoint) player->karthud[khud_splitwin] = -1; // behind but gaining } - player->karthud[khud_splitlast] = player->karthud[khud_splittime]; + player->pace = player->karthud[khud_splittime]; } } diff --git a/src/g_game.c b/src/g_game.c index 94b1fa68b..78973b04d 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -2309,6 +2309,8 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) UINT16 preffollowercolor; INT32 preffollower; + tic_t splits[MAXRACESPLITS]; + INT32 i; // This needs to be first, to permit it to wipe extra information @@ -2338,6 +2340,8 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) preffollower = players[player].preffollower; preffollowercolor = players[player].preffollowercolor; + memcpy(&splits, &players[player].splits, sizeof(splits)); + if (betweenmaps) { fakeskin = MAXSKINS; @@ -2676,6 +2680,8 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) memcpy(&p->public_key, &public_key, sizeof(p->public_key)); + memcpy(&p->splits, &splits, sizeof(p->splits)); + if (saveroundconditions) memcpy(&p->roundconditions, &roundconditions, sizeof (p->roundconditions)); diff --git a/src/k_hud.cpp b/src/k_hud.cpp index eab57e780..7c7268db9 100644 --- a/src/k_hud.cpp +++ b/src/k_hud.cpp @@ -7750,6 +7750,74 @@ void K_drawKartHUD(void) } } + boolean debug_alwaysdrawsplits = false; + + if ( + ( + !stplyr->karthud[khud_lapanimation] && + stplyr->karthud[khud_splittimer] && + (stplyr->karthud[khud_splittimer] > TICRATE/3 || stplyr->karthud[khud_splittimer]%2 || cv_reducevfx.value) + ) + || debug_alwaysdrawsplits + ) + { + using srb2::Draw; + + 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)%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 = 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(-35).colormap(skincolor).patch(R_CanShowSkinInDemo(skin) ? faceprefix[skin][FACE_MINIMAP] : kp_unknownminimap); + + Draw::TextElement text = Draw::TextElement( + 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(15).text(text); + } + if (modeattacking || (gametyperules & GTR_TIMELIMIT) || cv_drawtimer.value) K_drawKartTimestamp(realtime, TIME_X, TIME_Y + (ta ? 2 : 0), flags, 0); @@ -7763,72 +7831,6 @@ void K_drawKartHUD(void) .flags(flags | V_ORANGEMAP) .align(Draw::Align::kRight) .text(text.string()); - - boolean debug_alwaysdraw = false; - - 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 - ) - { - 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)%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 = 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(-35).colormap(skincolor).patch(R_CanShowSkinInDemo(skin) ? faceprefix[skin][FACE_MINIMAP] : kp_unknownminimap); - - Draw::TextElement text = Draw::TextElement( - 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(15).text(text); - } } else { diff --git a/src/k_kart.c b/src/k_kart.c index 96b1dcec0..d7957fe37 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -4431,6 +4431,80 @@ void K_AwardPlayerRings(player_t *player, UINT16 rings, boolean overload) } } +static void K_SetupSplitForPlayer(player_t *us, player_t *them, tic_t ourtime, tic_t theirtime) +{ + us->karthud[khud_splittimer] = 3*TICRATE; + + INT32 delta = (INT32)theirtime - (INT32)ourtime; // how ahead are we? bigger number = more ahead, negative = behind + us->karthud[khud_splittime] = -1 * delta; // (HUD expects this to be backwards, but this is how i felt today!) + + INT32 winning = 0; + + if (delta > 0) + winning = 2; // winning aid gaining + else if (delta < 0) + winning = -2; // behind and falling + + if (winning > 0 && delta < us->pace) + winning = 1; // winning but falling + else if (winning < 0 && delta > us->pace) + winning = -1; // behind but gaiming + + us->pace = delta; + + us->karthud[khud_splitwin] = winning; + us->karthud[khud_splitskin] = them->skin; + us->karthud[khud_splitcolor] = them->skincolor; +} + +static void K_HandleRaceSplits(player_t *player, tic_t time, UINT8 checkpoint) +{ + if (checkpoint >= MAXRACESPLITS) + return; + + player->splits[checkpoint] = time; + + player_t *lowest = player; + UINT8 numrealsplits = 0; + + // find fastest player for this checkpoint and # players who have already crossed + for (UINT8 i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i]) + continue; + + player_t *check = &players[i]; + + if (check == player) + continue; + if (check->spectator) + continue; + if (check->splits[checkpoint] == 0) + continue; + + numrealsplits++; + + if (check->splits[checkpoint] < lowest->splits[checkpoint]) + lowest = check; + } + + // no one to compare against yet + if (lowest == player || numrealsplits == 0) + return; + + // if there's exactly one player ahead of us, they need a blue split generated + // so they can see how far behind we are + if (lowest != player && numrealsplits == 1) + { + K_SetupSplitForPlayer(lowest, player, lowest->splits[checkpoint], player->splits[checkpoint]); + } + + if (numrealsplits) + { + K_SetupSplitForPlayer(player, lowest, player->splits[checkpoint], lowest->splits[checkpoint]); + } +} + void K_CheckpointCrossAward(player_t *player) { if (gametype != GT_RACE) @@ -4440,6 +4514,10 @@ void K_CheckpointCrossAward(player_t *player) { G_SetDemoCheckpointTiming(player, leveltime - starttime, player->gradingpointnum); } + else if (player->gradingpointnum < MAXRACESPLITS) + { + K_HandleRaceSplits(player, leveltime - starttime, player->gradingpointnum); + } player->gradingfactor += K_GetGradingFactorAdjustment(player); player->gradingpointnum++; diff --git a/src/p_saveg.cpp b/src/p_saveg.cpp index 36d681d4d..84b7d3e10 100644 --- a/src/p_saveg.cpp +++ b/src/p_saveg.cpp @@ -664,6 +664,9 @@ static void P_NetArchivePlayers(savebuffer_t *save) WRITEMEM(save->p, players[i].public_key, PUBKEYLENGTH); + WRITEMEM(save->p, players[i].splits, sizeof(players[i].splits)); + WRITEINT32(save->p, players[i].pace); + WRITESINT8(save->p, players[i].pitblame); WRITEUINT8(save->p, players[i].instaWhipCharge); @@ -1329,6 +1332,8 @@ static void P_NetUnArchivePlayers(savebuffer_t *save) players[i].vortexBoost = READFIXED(save->p); READMEM(save->p, players[i].public_key, PUBKEYLENGTH); + READMEM(save->p, players[i].splits, sizeof(players[i].splits)); + players[i].pace = READINT32(save->p); players[i].pitblame = READSINT8(save->p);