diff --git a/src/k_bot.c b/src/k_bot.c index f87d332af..ca3aaa2c3 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -298,7 +298,7 @@ boolean K_BotCanTakeCut(player_t *player) /*-------------------------------------------------- static fixed_t K_BotSpeedScaled(player_t *player, fixed_t speed) - Gets the bot's speed value, adjusted for predictions. + What the bot "thinks" their speed is, for predictions. Mainly to make bots brake earlier when on friction sectors. Input Arguments:- @@ -312,6 +312,12 @@ static fixed_t K_BotSpeedScaled(player_t *player, fixed_t speed) { fixed_t result = speed; + if (P_IsObjectOnGround(player->mo) == false) + { + // You have no air control, so don't predict too far ahead. + return 0; + } + if (player->mo->movefactor != FRACUNIT) { fixed_t moveFactor = player->mo->movefactor; @@ -650,7 +656,8 @@ static botprediction_t *K_CreateBotPrediction(player_t *player) const fixed_t speed = K_BotSpeedScaled(player, P_AproxDistance(player->mo->momx, player->mo->momy)); const INT32 startDist = (DEFAULT_WAYPOINT_RADIUS * 2 * mapobjectscale) / FRACUNIT; - const INT32 distance = ((speed / FRACUNIT) * futuresight) + startDist; + const INT32 maxDist = startDist * 4; // This function gets very laggy when it goes far distances, and going too far isn't very helpful anyway. + const INT32 distance = min(((speed / FRACUNIT) * futuresight) + startDist, maxDist); // Halves radius when encountering a wall on your way to your destination. fixed_t radreduce = FRACUNIT; @@ -660,7 +667,6 @@ static botprediction_t *K_CreateBotPrediction(player_t *player) angle_t angletonext = ANGLE_MAX; INT32 disttonext = INT32_MAX; - waypoint_t *finishLine = K_GetFinishLineWaypoint(); waypoint_t *wp = player->nextwaypoint; mobj_t *prevwpmobj = player->mo; @@ -676,8 +682,8 @@ static botprediction_t *K_CreateBotPrediction(player_t *player) angletonext = R_PointToAngle2(prevwpmobj->x, prevwpmobj->y, wp->mobj->x, wp->mobj->y); disttonext = P_AproxDistance(prevwpmobj->x - wp->mobj->x, prevwpmobj->y - wp->mobj->y) / FRACUNIT; - pathfindsuccess = K_PathfindToWaypoint( - player->nextwaypoint, finishLine, + pathfindsuccess = K_PathfindThruCircuit( + player->nextwaypoint, (unsigned)distanceleft, &pathtofinish, useshortcuts, huntbackwards ); @@ -720,35 +726,6 @@ static botprediction_t *K_CreateBotPrediction(player_t *player) // We're done!! break; } - - if (i == pathtofinish.numnodes-1 && disttonext > 0) - { - // We were pathfinding to the finish, but we want to go past it. - // Set up a new pathfind. - - waypoint_t *next = NULL; - - if (finishLine->numnextwaypoints == 0) - { - distanceleft = 0; - break; - } - - // default to first one - next = wp->nextwaypoints[0]; - - pathfindsuccess = K_PathfindToWaypoint( - next, finishLine, - &pathtofinish, - useshortcuts, huntbackwards - ); - - if (pathfindsuccess == false) - { - distanceleft = 0; - break; - } - } } Z_Free(pathtofinish.array); diff --git a/src/k_pathfind.c b/src/k_pathfind.c index 857f0343a..bd11ecdeb 100644 --- a/src/k_pathfind.c +++ b/src/k_pathfind.c @@ -190,6 +190,10 @@ static boolean K_PathfindSetupValid(const pathfindsetup_t *const pathfindsetup) { CONS_Debug(DBG_GAMELOGIC, "Pathfindsetup has NULL gettraversable function.\n"); } + else if (pathfindsetup->getfinished == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "Pathfindsetup has NULL getfinished function.\n"); + } else if (pathfindsetup->getconnectednodes(pathfindsetup->startnodedata, &sourcenodenumconnectednodes) == NULL) { CONS_Debug(DBG_GAMELOGIC, "K_PathfindSetupValid: Source node returned NULL connecting nodes.\n"); @@ -295,242 +299,244 @@ boolean K_PathfindAStar(path_t *const path, pathfindsetup_t *const pathfindsetup { CONS_Debug(DBG_GAMELOGIC, "K_PathfindAStar: Pathfinding setup is not valid.\n"); } - else if (pathfindsetup->startnodedata == pathfindsetup->endnodedata) + else { - // At the destination, return a simple 1 node path pathfindnode_t singlenode = {0}; singlenode.camefrom = NULL; - singlenode.nodedata = pathfindsetup->endnodedata; + singlenode.nodedata = pathfindsetup->startnodedata; singlenode.heapindex = SIZE_MAX; singlenode.hscore = 0U; singlenode.gscore = 0U; - K_ReconstructPath(path, &singlenode); - - pathfindsuccess = true; - } - else - { - bheap_t openset = {0}; - bheapitem_t poppedbheapitem = {0}; - pathfindnode_t *nodesarray = NULL; - pathfindnode_t **closedset = NULL; - pathfindnode_t *newnode = NULL; - pathfindnode_t *currentnode = NULL; - pathfindnode_t *connectingnode = NULL; - void **connectingnodesdata = NULL; - void *checknodedata = NULL; - UINT32 *connectingnodecosts = NULL; - size_t numconnectingnodes = 0U; - size_t connectingnodeheapindex = 0U; - size_t nodesarraycount = 0U; - size_t closedsetcount = 0U; - size_t i = 0U; - UINT32 tentativegscore = 0U; - - // Set the dynamic structure capacites to defaults if they are 0 - if (pathfindsetup->nodesarraycapacity == 0U) + if (pathfindsetup->getfinished(&singlenode, pathfindsetup) == true) { - pathfindsetup->nodesarraycapacity = DEFAULT_NODEARRAY_CAPACITY; + // At the destination, return a simple 1 node path + K_ReconstructPath(path, &singlenode); + pathfindsuccess = true; } - if (pathfindsetup->opensetcapacity == 0U) + else { - pathfindsetup->opensetcapacity = DEFAULT_OPENSET_CAPACITY; - } - if (pathfindsetup->closedsetcapacity == 0U) - { - pathfindsetup->closedsetcapacity = DEFAULT_CLOSEDSET_CAPACITY; - } + bheap_t openset = {0}; + bheapitem_t poppedbheapitem = {0}; + pathfindnode_t *nodesarray = NULL; + pathfindnode_t **closedset = NULL; + pathfindnode_t *newnode = NULL; + pathfindnode_t *currentnode = NULL; + pathfindnode_t *connectingnode = NULL; + void **connectingnodesdata = NULL; + void *checknodedata = NULL; + UINT32 *connectingnodecosts = NULL; + size_t numconnectingnodes = 0U; + size_t connectingnodeheapindex = 0U; + size_t nodesarraycount = 0U; + size_t closedsetcount = 0U; + size_t i = 0U; + UINT32 tentativegscore = 0U; - // Allocate the necessary memory - nodesarray = Z_Calloc(pathfindsetup->nodesarraycapacity * sizeof(pathfindnode_t), PU_STATIC, NULL); - if (nodesarray == NULL) - { - I_Error("K_PathfindAStar: Out of memory allocating nodes array."); - } - closedset = Z_Calloc(pathfindsetup->closedsetcapacity * sizeof(pathfindnode_t*), PU_STATIC, NULL); - if (closedset == NULL) - { - I_Error("K_PathfindAStar: Out of memory allocating closed set."); - } - K_BHeapInit(&openset, pathfindsetup->opensetcapacity); - - // Create the first node and add it to the open set - newnode = &nodesarray[nodesarraycount]; - newnode->heapindex = SIZE_MAX; - newnode->nodedata = pathfindsetup->startnodedata; - newnode->camefrom = NULL; - newnode->gscore = 0U; - newnode->hscore = pathfindsetup->getheuristic(newnode->nodedata, pathfindsetup->endnodedata); - nodesarraycount++; - K_BHeapPush(&openset, newnode, K_NodeGetFScore(newnode), K_NodeUpdateHeapIndex); - - // update openset capacity if it changed - if (openset.capacity != pathfindsetup->opensetcapacity) - { - pathfindsetup->opensetcapacity = openset.capacity; - } - - // Go through each node in the openset, adding new ones from each node to it - // this continues until a path is found or there are no more nodes to check - while (openset.count > 0U) - { - // pop the best node off of the openset - K_BHeapPop(&openset, &poppedbheapitem); - currentnode = (pathfindnode_t*)poppedbheapitem.data; - - if (currentnode->nodedata == pathfindsetup->endnodedata) + // Set the dynamic structure capacites to defaults if they are 0 + if (pathfindsetup->nodesarraycapacity == 0U) { - pathfindsuccess = K_ReconstructPath(path, currentnode); - break; + pathfindsetup->nodesarraycapacity = DEFAULT_NODEARRAY_CAPACITY; + } + if (pathfindsetup->opensetcapacity == 0U) + { + pathfindsetup->opensetcapacity = DEFAULT_OPENSET_CAPACITY; + } + if (pathfindsetup->closedsetcapacity == 0U) + { + pathfindsetup->closedsetcapacity = DEFAULT_CLOSEDSET_CAPACITY; } - // Place the node we just popped into the closed set, as we are now evaluating it - if (closedsetcount >= pathfindsetup->closedsetcapacity) + // Allocate the necessary memory + nodesarray = Z_Calloc(pathfindsetup->nodesarraycapacity * sizeof(pathfindnode_t), PU_STATIC, NULL); + if (nodesarray == NULL) { - // Need to reallocate closedset to fit another node - pathfindsetup->closedsetcapacity = pathfindsetup->closedsetcapacity * 2; - closedset = - Z_Realloc(closedset, pathfindsetup->closedsetcapacity * sizeof(pathfindnode_t*), PU_STATIC, NULL); - if (closedset == NULL) + I_Error("K_PathfindAStar: Out of memory allocating nodes array."); + } + closedset = Z_Calloc(pathfindsetup->closedsetcapacity * sizeof(pathfindnode_t*), PU_STATIC, NULL); + if (closedset == NULL) + { + I_Error("K_PathfindAStar: Out of memory allocating closed set."); + } + K_BHeapInit(&openset, pathfindsetup->opensetcapacity); + + // Create the first node and add it to the open set + newnode = &nodesarray[nodesarraycount]; + newnode->heapindex = SIZE_MAX; + newnode->nodedata = pathfindsetup->startnodedata; + newnode->camefrom = NULL; + newnode->gscore = 0U; + newnode->hscore = pathfindsetup->getheuristic(newnode->nodedata, pathfindsetup->endnodedata); + nodesarraycount++; + K_BHeapPush(&openset, newnode, K_NodeGetFScore(newnode), K_NodeUpdateHeapIndex); + + // update openset capacity if it changed + if (openset.capacity != pathfindsetup->opensetcapacity) + { + pathfindsetup->opensetcapacity = openset.capacity; + } + + // Go through each node in the openset, adding new ones from each node to it + // this continues until a path is found or there are no more nodes to check + while (openset.count > 0U) + { + // pop the best node off of the openset + K_BHeapPop(&openset, &poppedbheapitem); + currentnode = (pathfindnode_t*)poppedbheapitem.data; + + if (pathfindsetup->getfinished(currentnode, pathfindsetup) == true) { - I_Error("K_PathfindAStar: Out of memory reallocating closed set."); + pathfindsuccess = K_ReconstructPath(path, currentnode); + break; } - } - closedset[closedsetcount] = currentnode; - closedsetcount++; - // Get the needed data for the next nodes from the current node - connectingnodesdata = pathfindsetup->getconnectednodes(currentnode->nodedata, &numconnectingnodes); - connectingnodecosts = pathfindsetup->getconnectioncosts(currentnode->nodedata); - - if (connectingnodesdata == NULL) - { - CONS_Debug(DBG_GAMELOGIC, "K_PathfindAStar: A Node returned NULL connecting node data.\n"); - } - else if (connectingnodecosts == NULL) - { - CONS_Debug(DBG_GAMELOGIC, "K_PathfindAStar: A Node returned NULL connecting node costs.\n"); - } - else - { - // For each connecting node add it to the openset if it's unevaluated and not there, - // skip it if it's in the closedset or not traversable - for (i = 0; i < numconnectingnodes; i++) + // Place the node we just popped into the closed set, as we are now evaluating it + if (closedsetcount >= pathfindsetup->closedsetcapacity) { - checknodedata = connectingnodesdata[i]; - - if (checknodedata == NULL) + // Need to reallocate closedset to fit another node + pathfindsetup->closedsetcapacity = pathfindsetup->closedsetcapacity * 2; + closedset = + Z_Realloc(closedset, pathfindsetup->closedsetcapacity * sizeof(pathfindnode_t*), PU_STATIC, NULL); + if (closedset == NULL) { - CONS_Debug(DBG_GAMELOGIC, "K_PathfindAStar: A Node has a NULL connecting node.\n"); + I_Error("K_PathfindAStar: Out of memory reallocating closed set."); } - else + } + closedset[closedsetcount] = currentnode; + closedsetcount++; + + // Get the needed data for the next nodes from the current node + connectingnodesdata = pathfindsetup->getconnectednodes(currentnode->nodedata, &numconnectingnodes); + connectingnodecosts = pathfindsetup->getconnectioncosts(currentnode->nodedata); + + if (connectingnodesdata == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "K_PathfindAStar: A Node returned NULL connecting node data.\n"); + } + else if (connectingnodecosts == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "K_PathfindAStar: A Node returned NULL connecting node costs.\n"); + } + else + { + // For each connecting node add it to the openset if it's unevaluated and not there, + // skip it if it's in the closedset or not traversable + for (i = 0; i < numconnectingnodes; i++) { - // skip this node if it isn't traversable - if (pathfindsetup->gettraversable(checknodedata, currentnode->nodedata) == false) + checknodedata = connectingnodesdata[i]; + + if (checknodedata == NULL) { - continue; - } - - // Figure out what the gscore of this route for the connecting node is - tentativegscore = currentnode->gscore + connectingnodecosts[i]; - - // find this data in the nodes array if it's been generated before - connectingnode = K_NodesArrayContainsNodeData(nodesarray, checknodedata, nodesarraycount); - - if (connectingnode != NULL) - { - // The connecting node has been seen before, so it must be in either the closedset (skip it) - // or the openset (re-evaluate it's gscore) - if (K_ClosedsetContainsNode(closedset, connectingnode, closedsetcount) == true) - { - continue; - } - else if (tentativegscore < connectingnode->gscore) - { - // The node is not in the closedset, update it's gscore if this path to it is faster - connectingnode->gscore = tentativegscore; - connectingnode->camefrom = currentnode; - - connectingnodeheapindex = - K_BHeapContains(&openset, connectingnode, connectingnode->heapindex); - if (connectingnodeheapindex != SIZE_MAX) - { - K_UpdateBHeapItemValue( - &openset.array[connectingnodeheapindex], K_NodeGetFScore(connectingnode)); - } - else - { - // SOMEHOW the node is not in either the closed set OR the open set - CONS_Debug(DBG_GAMELOGIC, "K_PathfindAStar: A Node is not in either set.\n"); - } - } + CONS_Debug(DBG_GAMELOGIC, "K_PathfindAStar: A Node has a NULL connecting node.\n"); } else { - // Node is not created yet, so it hasn't been seen so far - // Reallocate nodesarray if it's full - if (nodesarraycount >= pathfindsetup->nodesarraycapacity) + // skip this node if it isn't traversable + if (pathfindsetup->gettraversable(checknodedata, currentnode->nodedata) == false) { - pathfindnode_t *nodesarrayrealloc = NULL; - pathfindsetup->nodesarraycapacity = pathfindsetup->nodesarraycapacity * 2; - nodesarrayrealloc = Z_Realloc(nodesarray, pathfindsetup->nodesarraycapacity * sizeof(pathfindnode_t), PU_STATIC, NULL); - - if (nodesarrayrealloc == NULL) - { - I_Error("K_PathfindAStar: Out of memory reallocating nodes array."); - } - - // Need to update pointers in closedset, openset, and node "camefrom" if nodesarray moved. - if (nodesarray != nodesarrayrealloc) - { - size_t j = 0U; - size_t arrayindex = 0U; - for (j = 0U; j < closedsetcount; j++) - { - arrayindex = closedset[j] - nodesarray; - closedset[j] = &nodesarrayrealloc[arrayindex]; - } - for (j = 0U; j < openset.count; j++) - { - arrayindex = ((pathfindnode_t *)(openset.array[j].data)) - nodesarray; - openset.array[j].data = &nodesarrayrealloc[arrayindex]; - } - for (j = 0U; j < nodesarraycount; j++) - { - if (nodesarrayrealloc[j].camefrom != NULL) - { - arrayindex = nodesarrayrealloc[j].camefrom - nodesarray; - nodesarrayrealloc[j].camefrom = &nodesarrayrealloc[arrayindex]; - } - } - - arrayindex = currentnode - nodesarray; - currentnode = &nodesarrayrealloc[arrayindex]; - } - - nodesarray = nodesarrayrealloc; + continue; } - // Create the new node and add it to the nodes array and open set - newnode = &nodesarray[nodesarraycount]; - newnode->heapindex = SIZE_MAX; - newnode->nodedata = checknodedata; - newnode->camefrom = currentnode; - newnode->gscore = tentativegscore; - newnode->hscore = pathfindsetup->getheuristic(newnode->nodedata, pathfindsetup->endnodedata); - nodesarraycount++; - K_BHeapPush(&openset, newnode, K_NodeGetFScore(newnode), K_NodeUpdateHeapIndex); + // Figure out what the gscore of this route for the connecting node is + tentativegscore = currentnode->gscore + connectingnodecosts[i]; + + // find this data in the nodes array if it's been generated before + connectingnode = K_NodesArrayContainsNodeData(nodesarray, checknodedata, nodesarraycount); + + if (connectingnode != NULL) + { + // The connecting node has been seen before, so it must be in either the closedset (skip it) + // or the openset (re-evaluate it's gscore) + if (K_ClosedsetContainsNode(closedset, connectingnode, closedsetcount) == true) + { + continue; + } + else if (tentativegscore < connectingnode->gscore) + { + // The node is not in the closedset, update it's gscore if this path to it is faster + connectingnode->gscore = tentativegscore; + connectingnode->camefrom = currentnode; + + connectingnodeheapindex = + K_BHeapContains(&openset, connectingnode, connectingnode->heapindex); + if (connectingnodeheapindex != SIZE_MAX) + { + K_UpdateBHeapItemValue( + &openset.array[connectingnodeheapindex], K_NodeGetFScore(connectingnode)); + } + else + { + // SOMEHOW the node is not in either the closed set OR the open set + CONS_Debug(DBG_GAMELOGIC, "K_PathfindAStar: A Node is not in either set.\n"); + } + } + } + else + { + // Node is not created yet, so it hasn't been seen so far + // Reallocate nodesarray if it's full + if (nodesarraycount >= pathfindsetup->nodesarraycapacity) + { + pathfindnode_t *nodesarrayrealloc = NULL; + pathfindsetup->nodesarraycapacity = pathfindsetup->nodesarraycapacity * 2; + nodesarrayrealloc = Z_Realloc(nodesarray, pathfindsetup->nodesarraycapacity * sizeof(pathfindnode_t), PU_STATIC, NULL); + + if (nodesarrayrealloc == NULL) + { + I_Error("K_PathfindAStar: Out of memory reallocating nodes array."); + } + + // Need to update pointers in closedset, openset, and node "camefrom" if nodesarray moved. + if (nodesarray != nodesarrayrealloc) + { + size_t j = 0U; + size_t arrayindex = 0U; + for (j = 0U; j < closedsetcount; j++) + { + arrayindex = closedset[j] - nodesarray; + closedset[j] = &nodesarrayrealloc[arrayindex]; + } + for (j = 0U; j < openset.count; j++) + { + arrayindex = ((pathfindnode_t *)(openset.array[j].data)) - nodesarray; + openset.array[j].data = &nodesarrayrealloc[arrayindex]; + } + for (j = 0U; j < nodesarraycount; j++) + { + if (nodesarrayrealloc[j].camefrom != NULL) + { + arrayindex = nodesarrayrealloc[j].camefrom - nodesarray; + nodesarrayrealloc[j].camefrom = &nodesarrayrealloc[arrayindex]; + } + } + + arrayindex = currentnode - nodesarray; + currentnode = &nodesarrayrealloc[arrayindex]; + } + + nodesarray = nodesarrayrealloc; + } + + // Create the new node and add it to the nodes array and open set + newnode = &nodesarray[nodesarraycount]; + newnode->heapindex = SIZE_MAX; + newnode->nodedata = checknodedata; + newnode->camefrom = currentnode; + newnode->gscore = tentativegscore; + newnode->hscore = pathfindsetup->getheuristic(newnode->nodedata, pathfindsetup->endnodedata); + nodesarraycount++; + K_BHeapPush(&openset, newnode, K_NodeGetFScore(newnode), K_NodeUpdateHeapIndex); + } } } } } - } - // Clean up the memory - K_BHeapFree(&openset); - Z_Free(closedset); - Z_Free(nodesarray); + // Clean up the memory + K_BHeapFree(&openset); + Z_Free(closedset); + Z_Free(nodesarray); + } } return pathfindsuccess; diff --git a/src/k_pathfind.h b/src/k_pathfind.h index 7b7f1475f..3d08d87e7 100644 --- a/src/k_pathfind.h +++ b/src/k_pathfind.h @@ -29,6 +29,9 @@ typedef UINT32(*getnodeheuristicfunc)(void*, void*); // function pointer for getting if a node is traversable from its base data typedef boolean(*getnodetraversablefunc)(void*, void*); +// function pointer for getting if a node is our pathfinding end point +typedef boolean(*getpathfindfinishedfunc)(void*, void*); + // A pathfindnode contains information about a node from the pathfinding // heapindex is only used within the pathfinding algorithm itself, and is always 0 after it is completed @@ -58,10 +61,12 @@ typedef struct pathfindsetup_s { size_t nodesarraycapacity; void *startnodedata; void *endnodedata; + UINT32 endgscore; getconnectednodesfunc getconnectednodes; getnodeconnectioncostsfunc getconnectioncosts; getnodeheuristicfunc getheuristic; getnodetraversablefunc gettraversable; + getpathfindfinishedfunc getfinished; } pathfindsetup_t; diff --git a/src/k_waypoint.c b/src/k_waypoint.c index 357731bd9..a6a592a6b 100644 --- a/src/k_waypoint.c +++ b/src/k_waypoint.c @@ -1039,6 +1039,68 @@ static boolean K_WaypointPathfindTraversableNoShortcuts(void *data, void *prevda return traversable; } +/*-------------------------------------------------- + static boolean K_WaypointPathfindReachedEnd(void *data, void *setupData) + + Returns if the current waypoint data is our end point of our pathfinding. + + Input Arguments:- + data - Should point to a pathfindnode_t to compare + setupData - Should point to the pathfindsetup_t to compare + + Return:- + True if the waypoint is the pathfind end point, false otherwise. +--------------------------------------------------*/ +static boolean K_WaypointPathfindReachedEnd(void *data, void *setupData) +{ + boolean isEnd = false; + + if (data == NULL || setupData == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "K_WaypointPathfindReachedEnd received NULL data.\n"); + } + else + { + pathfindnode_t *node = (pathfindnode_t *)data; + pathfindsetup_t *setup = (pathfindsetup_t *)setupData; + + isEnd = (node->nodedata == setup->endnodedata); + } + + return isEnd; +} + +/*-------------------------------------------------- + static boolean K_WaypointPathfindReachedGScore(void *data, void *setupData) + + Returns if the current waypoint data reaches our end G score. + + Input Arguments:- + data - Should point to a pathfindnode_t to compare + setupData - Should point to the pathfindsetup_t to compare + + Return:- + True if the waypoint reached the G score, false otherwise. +--------------------------------------------------*/ +static boolean K_WaypointPathfindReachedGScore(void *data, void *setupData) +{ + boolean scoreReached = false; + + if (data == NULL || setupData == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "K_WaypointPathfindReachedGScore received NULL data.\n"); + } + else + { + pathfindnode_t *node = (pathfindnode_t *)data; + pathfindsetup_t *setup = (pathfindsetup_t *)setupData; + + scoreReached = (node->gscore >= setup->endgscore); + } + + return scoreReached; +} + /*-------------------------------------------------- boolean K_PathfindToWaypoint( waypoint_t *const sourcewaypoint, @@ -1087,18 +1149,19 @@ boolean K_PathfindToWaypoint( getnodeconnectioncostsfunc nodecostsfunc = K_WaypointPathfindGetNextCosts; getnodeheuristicfunc heuristicfunc = K_WaypointPathfindGetHeuristic; getnodetraversablefunc traversablefunc = K_WaypointPathfindTraversableNoShortcuts; + getpathfindfinishedfunc finishedfunc = K_WaypointPathfindReachedEnd; if (huntbackwards) { nextnodesfunc = K_WaypointPathfindGetPrev; nodecostsfunc = K_WaypointPathfindGetPrevCosts; } + if (useshortcuts) { traversablefunc = K_WaypointPathfindTraversableAllEnabled; } - pathfindsetup.opensetcapacity = K_GetOpensetBaseSize(); pathfindsetup.closedsetcapacity = K_GetClosedsetBaseSize(); pathfindsetup.nodesarraycapacity = K_GetNodesArrayBaseSize(); @@ -1108,6 +1171,90 @@ boolean K_PathfindToWaypoint( pathfindsetup.getconnectioncosts = nodecostsfunc; pathfindsetup.getheuristic = heuristicfunc; pathfindsetup.gettraversable = traversablefunc; + pathfindsetup.getfinished = finishedfunc; + + pathfound = K_PathfindAStar(returnpath, &pathfindsetup); + + K_UpdateOpensetBaseSize(pathfindsetup.opensetcapacity); + K_UpdateClosedsetBaseSize(pathfindsetup.closedsetcapacity); + K_UpdateNodesArrayBaseSize(pathfindsetup.nodesarraycapacity); + } + + return pathfound; +} + +/*-------------------------------------------------- + boolean K_PathfindThruCircuit( + waypoint_t *const sourcewaypoint, + const UINT32 traveldistance, + path_t *const returnpath, + const boolean useshortcuts, + const boolean huntbackwards) + + See header file for description. +--------------------------------------------------*/ +boolean K_PathfindThruCircuit( + waypoint_t *const sourcewaypoint, + const UINT32 traveldistance, + path_t *const returnpath, + const boolean useshortcuts, + const boolean huntbackwards) +{ + boolean pathfound = false; + + if (sourcewaypoint == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "NULL sourcewaypoint in K_PathfindThruCircuit.\n"); + } + else if (finishline == NULL) + { + CONS_Debug(DBG_GAMELOGIC, "NULL finishline in K_PathfindThruCircuit.\n"); + } + else if (((huntbackwards == false) && (sourcewaypoint->numnextwaypoints == 0)) + || ((huntbackwards == true) && (sourcewaypoint->numprevwaypoints == 0))) + { + CONS_Debug(DBG_GAMELOGIC, + "K_PathfindThruCircuit: sourcewaypoint with ID %d has no next waypoint\n", + K_GetWaypointID(sourcewaypoint)); + } + else if (((huntbackwards == false) && (finishline->numprevwaypoints == 0)) + || ((huntbackwards == true) && (finishline->numnextwaypoints == 0))) + { + CONS_Debug(DBG_GAMELOGIC, + "K_PathfindThruCircuit: finishline with ID %d has no previous waypoint\n", + K_GetWaypointID(finishline)); + } + else + { + pathfindsetup_t pathfindsetup = {0}; + getconnectednodesfunc nextnodesfunc = K_WaypointPathfindGetNext; + getnodeconnectioncostsfunc nodecostsfunc = K_WaypointPathfindGetNextCosts; + getnodeheuristicfunc heuristicfunc = K_WaypointPathfindGetHeuristic; + getnodetraversablefunc traversablefunc = K_WaypointPathfindTraversableNoShortcuts; + getpathfindfinishedfunc finishedfunc = K_WaypointPathfindReachedGScore; + + if (huntbackwards) + { + nextnodesfunc = K_WaypointPathfindGetPrev; + nodecostsfunc = K_WaypointPathfindGetPrevCosts; + } + + if (useshortcuts) + { + traversablefunc = K_WaypointPathfindTraversableAllEnabled; + } + + pathfindsetup.opensetcapacity = K_GetOpensetBaseSize(); + pathfindsetup.closedsetcapacity = K_GetClosedsetBaseSize(); + pathfindsetup.nodesarraycapacity = K_GetNodesArrayBaseSize(); + pathfindsetup.startnodedata = sourcewaypoint; + pathfindsetup.endnodedata = finishline; + pathfindsetup.endgscore = traveldistance; + pathfindsetup.getconnectednodes = nextnodesfunc; + pathfindsetup.getconnectioncosts = nodecostsfunc; + pathfindsetup.getheuristic = heuristicfunc; + pathfindsetup.gettraversable = traversablefunc; + pathfindsetup.getfinished = finishedfunc; pathfound = K_PathfindAStar(returnpath, &pathfindsetup); @@ -1183,18 +1330,19 @@ waypoint_t *K_GetNextWaypointToDestination( getnodeconnectioncostsfunc nodecostsfunc = K_WaypointPathfindGetNextCosts; getnodeheuristicfunc heuristicfunc = K_WaypointPathfindGetHeuristic; getnodetraversablefunc traversablefunc = K_WaypointPathfindTraversableNoShortcuts; + getpathfindfinishedfunc finishedfunc = K_WaypointPathfindReachedEnd; if (huntbackwards) { nextnodesfunc = K_WaypointPathfindGetPrev; nodecostsfunc = K_WaypointPathfindGetPrevCosts; } + if (useshortcuts) { traversablefunc = K_WaypointPathfindTraversableAllEnabled; } - pathfindsetup.opensetcapacity = K_GetOpensetBaseSize(); pathfindsetup.closedsetcapacity = K_GetClosedsetBaseSize(); pathfindsetup.nodesarraycapacity = K_GetNodesArrayBaseSize(); @@ -1204,6 +1352,7 @@ waypoint_t *K_GetNextWaypointToDestination( pathfindsetup.getconnectioncosts = nodecostsfunc; pathfindsetup.getheuristic = heuristicfunc; pathfindsetup.gettraversable = traversablefunc; + pathfindsetup.getfinished = finishedfunc; pathfindsuccess = K_PathfindAStar(&pathtowaypoint, &pathfindsetup); diff --git a/src/k_waypoint.h b/src/k_waypoint.h index f49e162e8..a2201ff26 100644 --- a/src/k_waypoint.h +++ b/src/k_waypoint.h @@ -214,6 +214,37 @@ boolean K_PathfindToWaypoint( const boolean huntbackwards); +/*-------------------------------------------------- + boolean K_PathfindThruCircuit( + waypoint_t *const sourcewaypoint, + const UINT32 traveldistance, + path_t *const returnpath, + const boolean useshortcuts, + const boolean huntbackwards) + + Tries a pathfind to the finish line waypoint, similar to K_PathfindToWaypoint, but it will continue + until it reaches the specified distance. The final path returned will only have the waypoints up to the + specified distance. + + Input Arguments:- + sourcewaypoint - The waypoint to start searching from + traveldistance - How far along the circuit it will try to pathfind. + returnpath - The path_t that will contain the final found path + useshortcuts - Whether to use waypoints that are marked as being shortcuts in the search + huntbackwards - Goes through the waypoints backwards if true + + Return:- + True if a circuit path could be constructed, false if it couldn't. +--------------------------------------------------*/ + +boolean K_PathfindThruCircuit( + waypoint_t *const sourcewaypoint, + const UINT32 traveldistance, + path_t *const returnpath, + const boolean useshortcuts, + const boolean huntbackwards); + + /*-------------------------------------------------- waypoint_t *K_GetNextWaypointToDestination( waypoint_t *const sourcewaypoint,