diff --git a/src/cvars.cpp b/src/cvars.cpp index b96d3dccc..76068097d 100644 --- a/src/cvars.cpp +++ b/src/cvars.cpp @@ -448,6 +448,9 @@ consvar_t cv_seenames = Player("seenames", "On").on_off(); consvar_t cv_shadow = Player("shadow", "On").on_off(); consvar_t cv_showfocuslost = Player("showfocuslost", "Yes").yes_no(); +consvar_t cv_racesplits = Player("racesplits", "Leader").values({{0, "Off"}, {1, "Next"}, {2, "Leader"}}).save(); +consvar_t cv_attacksplits = Player("attacksplits", "Next").values({{0, "Off"}, {1, "Next"}, {2, "Leader"}}).save(); + void R_SetViewSize(void); consvar_t cv_showhud = Player("showhud", "Yes").yes_no().onchange(R_SetViewSize).dont_save(); diff --git a/src/d_player.h b/src/d_player.h index d90cbdda8..135b1dc23 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -359,6 +359,7 @@ typedef enum khud_splittimer, // How long to show splits HUD khud_splitskin, // Skin index of the leading player khud_splitcolor, // Skincolor of the leading player + khud_splitposition, // Who are we comparing to? NUMKARTHUD } karthudtype_t; diff --git a/src/g_demo.cpp b/src/g_demo.cpp index ff3d28577..99c8cd1c9 100644 --- a/src/g_demo.cpp +++ b/src/g_demo.cpp @@ -66,6 +66,7 @@ #include "k_vote.h" #include "k_credits.h" #include "k_grandprix.h" +#include "p_setup.h" // oldbest static menuitem_t TitleEntry[] = { @@ -2054,7 +2055,7 @@ void G_BeginRecording(void) demosplits_p = demobuf.p; for (i = 0; i < MAXSPLITS; i++) { - WRITEUINT32(demobuf.p, INT32_MAX); + WRITEUINT32(demobuf.p, UINT32_MAX); } @@ -2242,26 +2243,99 @@ void G_SetDemoCheckpointTiming(player_t *player, tic_t time, UINT8 checkpoint) UINT32 *splits = (UINT32 *)demosplits_p; splits[checkpoint] = time; + if (!cv_attacksplits.value) + return; + demoghost *g; - tic_t lowest = INT32_MAX; + tic_t lowest = UINT32_MAX; UINT32 lowestskin = ((skin_t*)player->mo->skin)->skinnum; UINT32 lowestcolor = player->skincolor; - for (g = ghosts; g; g = g->next) + + boolean polite = (cv_attacksplits.value == 1); + + // "Next" Mode: Find the weakest ghost who beats our best time. + // Don't set a ghost if we have no set time (oldbest == UINT32_MAX) + if (polite) { - if (lowest > g->splits[checkpoint]) + tic_t lowestend = UINT32_MAX; + + for (g = ghosts; g; g = g->next) { - lowest = g->splits[checkpoint]; - lowestskin = g->initialskin; - lowestcolor = g->initialcolor; + boolean newtargetghost = false; + + UINT32 points = K_GetNumGradingPoints(); + + tic_t endtime = UINT32_MAX; + if (points <= MAXSPLITS) + endtime = g->splits[points-1]; + + // Staff ghost oopsie. Fuckin, uh, + if (endtime == INT32_MAX) + endtime = UINT32_MAX; + + if (lowestend > oldbest) // Not losing to any ghost + { + // Not currently losing to a ghost + if (endtime < oldbest) + { + // Show us any ghost who finishes faster than us + newtargetghost = true; + } + } + else + { + // Losing to a ghost already + if (endtime > lowestend && endtime < oldbest) + { + // Pick a slower ghost we are still losing to + newtargetghost = true; + } + } + + if (newtargetghost) + { + lowest = g->splits[checkpoint]; + lowestskin = g->initialskin; + lowestcolor = g->initialcolor; + lowestend = endtime; + } } } - if (lowest != INT32_MAX) + // If no ghost has been assigned to split against, and we are either + // - in "Leader" mode + // - in "Next" mode, but failed to find a ghost we lose to + // just split against the moment-to-moment leading ghost. + if (lowest == UINT32_MAX && (!polite || oldbest != UINT32_MAX)) + { + for (g = ghosts; g; g = g->next) + { + if (lowest > g->splits[checkpoint]) + { + lowest = g->splits[checkpoint]; + lowestskin = g->initialskin; + lowestcolor = g->initialcolor; + } + } + } + + // Final check: Where is our target ghost relative to other ghosts? + UINT32 ghostsbeatingtarget = 0; + for (g = ghosts; g; g = g->next) + { + if (g->splits[checkpoint] < lowest) + { + ghostsbeatingtarget++; + } + } + + if (lowest != UINT32_MAX) { player->karthud[khud_splittimer] = 3*TICRATE; player->karthud[khud_splitskin] = lowestskin; player->karthud[khud_splitcolor] = lowestcolor; player->karthud[khud_splittime] = (INT32)time - (INT32)lowest; + player->karthud[khud_splitposition] = ghostsbeatingtarget + 1; if (lowest < time) { diff --git a/src/k_hud.cpp b/src/k_hud.cpp index 183afba6d..df3f72965 100644 --- a/src/k_hud.cpp +++ b/src/k_hud.cpp @@ -7792,6 +7792,7 @@ void K_drawKartHUD(void) INT32 skin = stplyr->karthud[khud_splitskin]; INT32 color = stplyr->karthud[khud_splitcolor]; INT32 ahead = stplyr->karthud[khud_splitwin]; + INT32 pos = stplyr->karthud[khud_splitposition]; // debug if (!stplyr->karthud[khud_splittimer]) @@ -7832,6 +7833,9 @@ void K_drawKartHUD(void) // vibes offset row.x(-35).colormap(skincolor).patch(R_CanShowSkinInDemo(skin) ? faceprefix[skin][FACE_MINIMAP] : kp_unknownminimap); + if (pos > 1) + row.x(-35).font(Draw::Font::kPing).text(va("%d", pos)); + Draw::TextElement text = Draw::TextElement( std::string(ahead >= 0 ? "-" : "+") + " " + "{:02}'{:02}\"{:02} " + arrow, G_TicsToMinutes(split, true), diff --git a/src/k_kart.c b/src/k_kart.c index aa9b4728d..f8f88f218 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -4459,6 +4459,8 @@ static void K_SetupSplitForPlayer(player_t *us, player_t *them, tic_t ourtime, t us->karthud[khud_splitwin] = winning; us->karthud[khud_splitskin] = them->skin; us->karthud[khud_splitcolor] = them->skincolor; + if (us->position != 1) + us->karthud[khud_splitposition] = them->position; } static void K_HandleRaceSplits(player_t *player, tic_t time, UINT8 checkpoint) @@ -4469,6 +4471,7 @@ static void K_HandleRaceSplits(player_t *player, tic_t time, UINT8 checkpoint) player->splits[checkpoint] = time; player_t *lowest = player; + player_t *next = player; UINT8 numrealsplits = 0; // find fastest player for this checkpoint and # players who have already crossed @@ -4490,6 +4493,9 @@ static void K_HandleRaceSplits(player_t *player, tic_t time, UINT8 checkpoint) if (check->splits[checkpoint] < lowest->splits[checkpoint]) lowest = check; + + if (check->splits[checkpoint] > next->splits[checkpoint] || next == player) + next = check; } // no one to compare against yet @@ -4503,9 +4509,11 @@ static void K_HandleRaceSplits(player_t *player, tic_t time, UINT8 checkpoint) K_SetupSplitForPlayer(lowest, player, lowest->splits[checkpoint], player->splits[checkpoint]); } - if (numrealsplits) + extern consvar_t cv_racesplits; + if (numrealsplits && cv_racesplits.value) { - K_SetupSplitForPlayer(player, lowest, player->splits[checkpoint], lowest->splits[checkpoint]); + player_t *target = (cv_racesplits.value == 2) ? lowest : next; + K_SetupSplitForPlayer(player, target, player->splits[checkpoint], target->splits[checkpoint]); } } diff --git a/src/k_menu.h b/src/k_menu.h index 4847a988f..8fcfae413 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -738,6 +738,8 @@ extern consvar_t cv_showfocuslost; extern consvar_t cv_chooseskin, cv_serversort, cv_menujam_update, cv_menujam; extern consvar_t cv_autorecord; +extern consvar_t cv_racesplits, cv_attacksplits; + void M_SetMenuDelay(UINT8 i); void M_SortServerList(void); diff --git a/src/menus/options-hud-1.c b/src/menus/options-hud-1.c index b0ad986f2..ca591e912 100644 --- a/src/menus/options-hud-1.c +++ b/src/menus/options-hud-1.c @@ -37,6 +37,15 @@ menuitem_t OPTIONS_HUD[] = {IT_SPACE | IT_NOTHING, NULL, NULL, NULL, {NULL}, 0, 0}, + {IT_STRING | IT_CVAR, "Race Splits", "Display time comparisons during races. Next = closest leading player.", + NULL, {.cvar = &cv_racesplits}, 0, 0}, + + {IT_STRING | IT_CVAR, "Time Attack Splits", "Display time comparisons during Time Attack. Next = closest leading player.", + NULL, {.cvar = &cv_attacksplits}, 0, 0}, + + {IT_SPACE | IT_NOTHING, NULL, NULL, + NULL, {NULL}, 0, 0}, + {IT_STRING | IT_SUBMENU, "Online Chat Options...", "Visual options for the online chat box.", NULL, {.submenu = &OPTIONS_HUDOnlineDef}, 0, 0}, };