mirror of
https://github.com/KartKrewDev/RingRacers.git
synced 2025-12-31 12:13:16 +00:00
Merge branch 'circuit-pathfind' into 'master'
Circuit pathfinding See merge request KartKrew/Kart!673
This commit is contained in:
commit
89d6506b44
5 changed files with 406 additions and 238 deletions
45
src/k_bot.c
45
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);
|
||||
|
|
|
|||
410
src/k_pathfind.c
410
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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
||||
|
|
|
|||
153
src/k_waypoint.c
153
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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue