2025-10-04 21:58:14 +01:00
|
|
|
#include "game/nav.h"
|
|
|
|
|
#include "core/types.h"
|
|
|
|
|
|
2025-10-04 21:08:52 +01:00
|
|
|
#include <stdio.h>
|
|
|
|
|
|
|
|
|
|
#define MAX_UNFINISHED 128
|
|
|
|
|
|
|
|
|
|
typedef struct navSearchNodeState navSearchNodeState;
|
|
|
|
|
struct navSearchNodeState{
|
|
|
|
|
bool visited;
|
|
|
|
|
U64 distance;
|
|
|
|
|
U32 shortest;
|
|
|
|
|
bool addedToUnvisited;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
typedef struct navSearchState navSearchState;
|
|
|
|
|
struct navSearchState{
|
2025-10-04 21:58:14 +01:00
|
|
|
navSearchNodeState nodeStates[NAV_MAX_NODES];
|
2025-10-04 21:08:52 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
navSearchState initState(U32 start, U32 meshSize) {
|
|
|
|
|
navSearchState state = {};
|
2025-10-04 21:58:14 +01:00
|
|
|
for(U32 i = 0; i < meshSize; i++) {
|
2025-10-04 21:08:52 +01:00
|
|
|
state.nodeStates[i].visited = false;
|
|
|
|
|
state.nodeStates[i].addedToUnvisited = false;
|
2025-10-04 21:58:14 +01:00
|
|
|
// underflow to the max :)
|
|
|
|
|
state.nodeStates[i].distance = U64_MAX;
|
2025-10-04 21:08:52 +01:00
|
|
|
state.nodeStates[i].shortest = 0;
|
|
|
|
|
}
|
|
|
|
|
state.nodeStates[start].distance = 0;
|
|
|
|
|
return state;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
U32 getLowestState(U32 unfinishedIndexes[128], U32 unfinishedCount, navSearchState state, U32 *offset) {
|
2025-10-04 21:58:14 +01:00
|
|
|
U32 lowest = U32_MAX;
|
|
|
|
|
U32 lowestI = U32_MAX;
|
2025-10-04 21:08:52 +01:00
|
|
|
bool startFound = false;
|
2025-10-04 21:58:14 +01:00
|
|
|
for(U32 i = *offset; i < unfinishedCount; i++) {
|
2025-10-04 21:08:52 +01:00
|
|
|
navSearchNodeState checkNode = state.nodeStates[unfinishedIndexes[i]];
|
|
|
|
|
if(checkNode.visited) {
|
|
|
|
|
if(!startFound) {
|
|
|
|
|
*offset = i;
|
|
|
|
|
}
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
startFound = true;
|
|
|
|
|
if (lowest > checkNode.distance) {
|
2025-10-04 21:58:14 +01:00
|
|
|
lowest = cast(U32) checkNode.distance;
|
2025-10-04 21:08:52 +01:00
|
|
|
lowestI = unfinishedIndexes[i];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return lowestI;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Generate a path to follow between the start and end node.
|
|
|
|
|
NavPath Nav_Path(NavMesh mesh, U32 start, U32 end) {
|
|
|
|
|
navSearchState state = initState(start, mesh.nodeCount);
|
|
|
|
|
U32 unfinishedCount = 1;
|
|
|
|
|
U32 unfinishedIndexes[NAV_MAX_NODES] = {start};
|
|
|
|
|
// I don't want to spend time removing items from
|
|
|
|
|
// the unfinished nodes, so when checking for a lowest
|
|
|
|
|
// if I find the first N items have been checked, I'll mark
|
|
|
|
|
// an offset to skip the first N items.
|
2025-10-04 21:58:14 +01:00
|
|
|
U32 unfinishedOffset = 0;
|
2025-10-04 21:08:52 +01:00
|
|
|
U32 lowestNodeIndex = start;
|
|
|
|
|
bool found = false;
|
|
|
|
|
while(!found) {
|
|
|
|
|
for(int connectionI = 0 ; connectionI < mesh.nodes[lowestNodeIndex].connectionCount; connectionI++) {
|
|
|
|
|
NavConnection connection = mesh.nodes[lowestNodeIndex].connections[connectionI];
|
|
|
|
|
navSearchNodeState *testNode = &state.nodeStates[connection.NodeIndex];
|
|
|
|
|
if(testNode->visited) {continue;}
|
2025-10-04 21:58:14 +01:00
|
|
|
U32 distance = cast(U32) (state.nodeStates[lowestNodeIndex].distance + connection.Cost);
|
|
|
|
|
distance += cast(U32) (mesh.nodes[end].pos.x - mesh.nodes[connection.NodeIndex].pos.x);
|
|
|
|
|
distance += cast(U32) (mesh.nodes[end].pos.y - mesh.nodes[connection.NodeIndex].pos.y);
|
2025-10-04 21:08:52 +01:00
|
|
|
if(testNode->distance > distance) {
|
2025-10-04 21:58:14 +01:00
|
|
|
testNode->distance = distance;
|
2025-10-04 21:08:52 +01:00
|
|
|
testNode->shortest = lowestNodeIndex;
|
|
|
|
|
}
|
|
|
|
|
if(!testNode->addedToUnvisited) {
|
|
|
|
|
unfinishedIndexes[unfinishedCount] = connection.NodeIndex;
|
|
|
|
|
unfinishedCount++;
|
|
|
|
|
testNode->addedToUnvisited = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
state.nodeStates[lowestNodeIndex].visited = true;
|
|
|
|
|
lowestNodeIndex = getLowestState(unfinishedIndexes, unfinishedCount, state, &unfinishedOffset);
|
|
|
|
|
if(lowestNodeIndex == end) {
|
|
|
|
|
found = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
NavPath res_path = {0};
|
|
|
|
|
U32 index = end;
|
|
|
|
|
while(index!=start) {
|
|
|
|
|
res_path.indexes[res_path.nodeCount] = index;
|
|
|
|
|
res_path.nodeCount++;
|
|
|
|
|
index = state.nodeStates[index].shortest;
|
|
|
|
|
}
|
|
|
|
|
res_path.indexes[res_path.nodeCount] = start;
|
|
|
|
|
res_path.nodeCount++;
|
|
|
|
|
return res_path;
|
|
|
|
|
}
|