Merge branch 'circuit-pathfind' into 'master'

Circuit pathfinding

See merge request KartKrew/Kart!673
This commit is contained in:
Sal 2022-09-12 05:34:28 +00:00
commit 89d6506b44
5 changed files with 406 additions and 238 deletions

View file

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

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,