diff --git a/src/k_kart.c b/src/k_kart.c index 793fcc2b5..68bddc224 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -5673,20 +5673,19 @@ static void K_KartDrift(player_t *player, boolean onground) } /*-------------------------------------------------- - static waypoint_t *K_GetPlayerNextWaypoint(player_t *player) + static waypoint_t *K_GetPlayerClosestWaypoint(player_t *player) - Gets the next waypoint of a player, by finding their closest waypoint, then checking which of itself and next or - previous waypoints are infront of the player. + Gets the closest waypoint of a player. Input Arguments:- - player - The player the next waypoint is being found for + player - The player the closest waypoint is being found for Return:- - The waypoint that is the player's next waypoint + The waypoint that is the player's closest waypoint --------------------------------------------------*/ -static waypoint_t *K_GetPlayerNextWaypoint(player_t *player) +static waypoint_t *K_GetPlayerClosestWaypoint(player_t *player) { - waypoint_t *bestwaypoint = NULL; + waypoint_t *closestwaypoint = NULL; if ((player != NULL) && (player->mo != NULL)) { mobj_t *wpmobj; @@ -5709,23 +5708,62 @@ static waypoint_t *K_GetPlayerNextWaypoint(player_t *player) } waypoint = K_SearchWaypointGraphForMobj(closestwpmobj); + closestwaypoint = waypoint; + } + + return closestwaypoint; +} + +/*-------------------------------------------------- + static waypoint_t *K_GetPlayerNextWaypoint(player_t *player) + + Gets the next waypoint of a player, by finding their closest waypoint, then checking which of itself and next or + previous waypoints are infront of the player. + + Input Arguments:- + player - The player the next waypoint is being found for + + Return:- + The waypoint that is the player's next waypoint +--------------------------------------------------*/ +static waypoint_t *K_GetPlayerNextWaypoint(player_t *player) +{ + waypoint_t *bestwaypoint = NULL; + if ((player != NULL) && (player->mo != NULL)) + { + waypoint_t *waypoint = NULL; + + waypoint = K_GetPlayerClosestWaypoint(player); bestwaypoint = waypoint; // check the waypoint's location in relation to the player // If it's generally in front, it's fine, otherwise, use the best next/previous waypoint. + // EXCEPTION: If our closest waypoint is the finishline AND we're facing towards it, don't do this. + // Otherwise it breaks the distance calculations. if (waypoint != NULL) { - angle_t playerangle = player->mo->angle; + boolean finishlinehack = false; + angle_t playerangle = player->mo->angle; angle_t angletowaypoint = R_PointToAngle2(player->mo->x, player->mo->y, waypoint->mobj->x, waypoint->mobj->y); - angle_t angledelta = playerangle - angletowaypoint; + angle_t angledelta = playerangle - angletowaypoint; + if (angledelta > ANGLE_180) { - angledelta = InvAngle(angletowaypoint); + angledelta = InvAngle(angledelta); } - if (angledelta > ANGLE_90) + if (bestwaypoint == K_GetFinishLineWaypoint()) + { + // facing towards the finishline + if (angledelta <= ANGLE_90) + { + finishlinehack = true; + } + } + + if ((angledelta > ANGLE_45) && (finishlinehack == false)) { angle_t nextbestdelta = angledelta; size_t i = 0U; @@ -5780,8 +5818,69 @@ static waypoint_t *K_GetPlayerNextWaypoint(player_t *player) return bestwaypoint; } +static boolean K_PlayerCloserToNextWaypoints(waypoint_t *const waypoint, player_t *const player) +{ + boolean nextiscloser = true; + + if ((waypoint != NULL) && (player != NULL) && (player->mo != NULL)) + { + size_t i = 0U; + waypoint_t *currentwpcheck = NULL; + angle_t angletoplayer = ANGLE_MAX; + angle_t currentanglecheck = ANGLE_MAX; + angle_t bestangle = ANGLE_MAX; + + angletoplayer = R_PointToAngle2(waypoint->mobj->x, waypoint->mobj->y, + player->mo->x, player->mo->y); + + for (i = 0U; i < waypoint->numnextwaypoints; i++) + { + currentwpcheck = waypoint->nextwaypoints[i]; + currentanglecheck = R_PointToAngle2( + waypoint->mobj->x, waypoint->mobj->y, currentwpcheck->mobj->x, currentwpcheck->mobj->y); + + // Get delta angle + currentanglecheck = currentanglecheck - angletoplayer; + + if (currentanglecheck > ANGLE_180) + { + currentanglecheck = InvAngle(currentanglecheck); + } + + if (currentanglecheck < bestangle) + { + bestangle = currentanglecheck; + } + } + + for (i = 0U; i < waypoint->numprevwaypoints; i++) + { + currentwpcheck = waypoint->prevwaypoints[i]; + currentanglecheck = R_PointToAngle2( + waypoint->mobj->x, waypoint->mobj->y, currentwpcheck->mobj->x, currentwpcheck->mobj->y); + + // Get delta angle + currentanglecheck = currentanglecheck - angletoplayer; + + if (currentanglecheck > ANGLE_180) + { + currentanglecheck = InvAngle(currentanglecheck); + } + + if (currentanglecheck < bestangle) + { + bestangle = currentanglecheck; + nextiscloser = false; + break; + } + } + } + + return nextiscloser; +} + /*-------------------------------------------------- - static void K_UpdateDistanceFromFinishLine(player_t *player) + static void K_UpdateDistanceFromFinishLine(player_t *const player) Updates the distance a player has to the finish line. @@ -5791,40 +5890,70 @@ static waypoint_t *K_GetPlayerNextWaypoint(player_t *player) Return:- None --------------------------------------------------*/ -static void K_UpdateDistanceFromFinishLine(player_t *player) +static void K_UpdateDistanceFromFinishLine(player_t *const player) { if ((player != NULL) && (player->mo != NULL)) { - waypoint_t *finishline = K_GetFinishLineWaypoint(); - player->nextwaypoint = K_GetPlayerNextWaypoint(player); - - // bestwaypoint is now the waypoint that is in front of us - if ((player->nextwaypoint != NULL) && (finishline != NULL)) + if (player->exiting) { - const boolean useshortcuts = false; - const boolean huntbackwards = false; - boolean pathfindsuccess = false; - path_t pathtofinish = {}; + player->nextwaypoint = K_GetFinishLineWaypoint(); + player->distancetofinish = 0U; + } + else + { + waypoint_t *finishline = K_GetFinishLineWaypoint(); + player->nextwaypoint = K_GetPlayerNextWaypoint(player); - pathfindsuccess = - K_PathfindToWaypoint(player->nextwaypoint, finishline, &pathtofinish, useshortcuts, huntbackwards); - - // Update the player's distance to the finish line if a path was found. - // Using shortcuts won't find a path, so the distance won't be updated until the player gets back on track - if (pathfindsuccess == true) + // nextwaypoint is now the waypoint that is in front of us + if ((player->nextwaypoint != NULL) && (finishline != NULL)) { - // Add euclidean distance to the next waypoint to the distancetofinish - UINT32 adddist; - fixed_t disttowaypoint = - P_AproxDistance( - player->mo->x - player->nextwaypoint->mobj->x, - player->mo->y - player->nextwaypoint->mobj->y); - disttowaypoint = P_AproxDistance(disttowaypoint, player->mo->z - player->nextwaypoint->mobj->z); + const boolean useshortcuts = false; + const boolean huntbackwards = false; + boolean pathfindsuccess = false; + path_t pathtofinish = {}; - adddist = ((UINT32)disttowaypoint) >> FRACBITS; + pathfindsuccess = + K_PathfindToWaypoint(player->nextwaypoint, finishline, &pathtofinish, useshortcuts, huntbackwards); - player->distancetofinish = pathtofinish.totaldist + adddist; - Z_Free(pathtofinish.array); + // Update the player's distance to the finish line if a path was found. + // Using shortcuts won't find a path, so distance won't be updated until the player gets back on track + if (pathfindsuccess == true) + { + // Add euclidean distance to the next waypoint to the distancetofinish + UINT32 adddist; + fixed_t disttowaypoint = + P_AproxDistance( + player->mo->x - player->nextwaypoint->mobj->x, + player->mo->y - player->nextwaypoint->mobj->y); + disttowaypoint = P_AproxDistance(disttowaypoint, player->mo->z - player->nextwaypoint->mobj->z); + + adddist = ((UINT32)disttowaypoint) >> FRACBITS; + + player->distancetofinish = pathtofinish.totaldist + adddist; + Z_Free(pathtofinish.array); + + // distancetofinish is currently a flat distance to the finish line, but in order to be fully + // correct we need to add to it the length of the entire circuit multiplied by the number of laps + // left after this one. This will give us the total distance to the finish line, and allow item + // distance calculation to work easily + if ((mapheaderinfo[gamemap - 1]->levelflags & LF_SECTIONRACE) == 0U) + { + const UINT8 numfulllapsleft = ((UINT8)cv_numlaps.value - player->laps); + + player->distancetofinish += numfulllapsleft * K_GetCircuitLength(); + + // An additional HACK, to fix looking backwards towards the finish line + // If the player's next waypoint is the finishline and the angle distance from player to + // connectin waypoints implies they're closer to a next waypoint, add a full track distance + if (player->nextwaypoint == finishline) + { + if (K_PlayerCloserToNextWaypoints(player->nextwaypoint, player) == true) + { + player->distancetofinish += K_GetCircuitLength(); + } + } + } + } } } } diff --git a/src/k_waypoint.c b/src/k_waypoint.c index 0db35aa2a..0ef3d9c32 100644 --- a/src/k_waypoint.c +++ b/src/k_waypoint.c @@ -4,6 +4,7 @@ #include "p_local.h" #include "p_tick.h" #include "z_zone.h" +#include "g_game.h" // The number of sparkles per waypoint connection in the waypoint visualisation static const UINT32 SPARKLES_PER_CONNECTION = 16U; @@ -18,6 +19,8 @@ static waypoint_t *waypointheap = NULL; static waypoint_t *firstwaypoint = NULL; static waypoint_t *finishline = NULL; +static UINT32 circuitlength = 0U; + static size_t numwaypoints = 0U; static size_t numwaypointmobjs = 0U; static size_t baseopensetsize = OPENSET_BASE_SIZE; @@ -160,6 +163,15 @@ INT32 K_GetWaypointID(waypoint_t *waypoint) return waypointid; } +/*-------------------------------------------------- + UINT32 K_GetCircuitLength(void) + + See header file for description. +--------------------------------------------------*/ +UINT32 K_GetCircuitLength(void) +{ + return circuitlength; +} /*-------------------------------------------------- size_t K_GetWaypointHeapIndex(waypoint_t *waypoint) @@ -306,6 +318,10 @@ void K_DebugWaypointsVisualise(void) { debugmobj->color = SKINCOLOR_ORANGE; } + else if (waypoint == players[displayplayers[0]].nextwaypoint) + { + debugmobj->color = SKINCOLOR_YELLOW; + } else { debugmobj->color = SKINCOLOR_BLUE; @@ -1267,6 +1283,56 @@ waypoint_t *K_SearchWaypointHeapForMobj(mobj_t *const mobj) return foundwaypoint; } +/*-------------------------------------------------- + static UINT32 K_SetupCircuitLength(void) + + Sets up the Circuit Length by getting the best path from the finishwaypoint back to itself. + On sprint maps, circuitlength is 0. + + Input Arguments:- + None + + Return:- + Length of the circuit +--------------------------------------------------*/ +static UINT32 K_SetupCircuitLength(void) +{ + if ((firstwaypoint == NULL) || (numwaypoints == 0U)) + { + CONS_Debug(DBG_GAMELOGIC, "K_SetupCircuitLength called with no waypoints.\n"); + } + else if (finishline == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "K_SetupCircuitLength called with no finishline waypoint.\n"); + } + else + { + // The circuit length only makes sense in circuit maps, sprint maps do not need to use it + // The main usage of the circuit length is to add onto a player's distance to finish line so crossing the finish + // line places people correctly relative to each other + if ((mapheaderinfo[gamemap - 1]->levelflags & LF_SECTIONRACE) == LF_SECTIONRACE) + { + circuitlength = 0U; + } + else + { + // Create a fake finishline waypoint, then try and pathfind to the finishline from it + waypoint_t fakefinishline = *finishline; + path_t bestcircuitpath = {}; + const boolean useshortcuts = false; + const boolean huntbackwards = false; + + K_PathfindToWaypoint(&fakefinishline, finishline, &bestcircuitpath, useshortcuts, huntbackwards); + + circuitlength = bestcircuitpath.totaldist; + + Z_Free(bestcircuitpath.array); + } + } + + return circuitlength; +} + /*-------------------------------------------------- static void K_AddPrevToWaypoint(waypoint_t *const waypoint, waypoint_t *const prevwaypoint) @@ -1616,6 +1682,9 @@ boolean K_SetupWaypointList(void) K_GetWaypointID(firstwaypoint)); finishline = firstwaypoint; } + + (void)K_SetupCircuitLength(); + setupsuccessful = true; } } @@ -1634,6 +1703,7 @@ void K_ClearWaypoints(void) waypointheap = NULL; firstwaypoint = NULL; finishline = NULL; - numwaypoints = 0; - numwaypointmobjs = 0; + numwaypoints = 0U; + numwaypointmobjs = 0U; + circuitlength = 0U; } diff --git a/src/k_waypoint.h b/src/k_waypoint.h index 35f21570a..d797d2116 100644 --- a/src/k_waypoint.h +++ b/src/k_waypoint.h @@ -109,6 +109,19 @@ INT32 K_GetWaypointNextID(waypoint_t *waypoint); INT32 K_GetWaypointID(waypoint_t *waypoint); +/*-------------------------------------------------- + UINT32 K_GetCircuitLength(void) + + Returns the circuit length, 0 on sprint maps. + + Input Arguments:- + + Return:- + The circuit length. +--------------------------------------------------*/ +UINT32 K_GetCircuitLength(void); + + /*-------------------------------------------------- boolean K_PathfindToWaypoint( waypoint_t *const sourcewaypoint,