Circuit pathfinding

An alternative to the standard pathfind function. Instead of pathfinding to a specific waypoint, it always goes towards the finish line waypoint, but also won't stop when it reaches it. It only stops when it travels a far enough distance.

This is basically a cleaner, less hacky, and optimized version of the pathfinding I gave to the bots; instead of doing 1-2 full pathfinds to do this (depending on if they are near the finish line or not), it will instead always do a single small pathfind. I also need it for shrink laser behavior.
This commit is contained in:
Sally Coolatta 2022-09-10 03:52:31 -04:00
parent 5b42b3b41a
commit 5107160a27
5 changed files with 397 additions and 236 deletions

View file

@ -660,7 +660,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 +675,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 +719,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);

View file

@ -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;

View file

@ -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;

View file

@ -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);

View file

@ -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,