Hey everyone I’m having freezes in certain cases where I’m trying to construct a navigation graph using raven graph (from mat buckland’s book) and applying dijkstra on it to construct a path.
code for constructing the path :
computePathFinal :: proc(startNode : graphNode, endNode : graphNode, areaRect :rl.Rectangle, areaRectIndex : int) -> [dynamic]graphNode {
visibleRavenGraphId : int = -1
for vrav,id in visibleRavenGraph {
if strings.contains(LevelAreaRectangleName[areaRectIndex], vrav.regionName) {
visibleRavenGraphId = id
}
}
visibleNodeOneProxy : visbleNodeProxy = findClosestVisibleNode(startNode, areaRectIndex)
// fmt.print("closest proxy object one : ", visibleNodeOneProxy, "\n")
visibleNodeOne : graphNode = visibleNodeOneProxy.node
visibleNodeTwoProxy : visbleNodeProxy = findClosestVisibleNode(endNode, areaRectIndex)
// fmt.print("closest proxy object two : ", visibleNodeTwoProxy, "\n")
visibleNodeTwo : graphNode = visibleNodeTwoProxy.node
distanceVisibleNodeOneFromStart : f32 = rl.Vector2Distance(visibleNodeOne.vect, startNode.vect)
resultingGraph : [dynamic]graphNode
nodeOneCopy : graphNode
append(&nodeOneCopy.neighborEdgeWeight, rl.Vector2Distance(startNode.vect, visibleNodeOne.vect))
nodeOneCopy.vect = startNode.vect
nodeTwoCopy : graphNode
append(&nodeTwoCopy.neighborEdgeWeight, rl.Vector2Distance(endNode.vect, visibleNodeTwo.vect))
nodeTwoCopy.vect = endNode.vect
if !visibleNodeOne.visiblePoint && !visibleNodeTwo.visiblePoint {
append(&resultingGraph, nodeOneCopy)
append(&resultingGraph, nodeTwoCopy)
return resultingGraph
}
visibleNodePath : [dynamic]graphNode
visibleNodeTraversalQueue : [dynamic]Entry
defer delete(visibleNodeTraversalQueue)
visibleNodeTwoIndex := visibleNodeTwoProxy.index
visibleNodeEntry : Entry
visibleNodeEntry.index = visibleNodeTwoIndex
visibleNodeEntry.cost = 0
visibleNodeOneIndex := visibleNodeOneProxy.index
if visibleNodeOneIndex == visibleNodeTwoIndex {
append(&resultingGraph, nodeOneCopy)
append(&resultingGraph, nodeTwoCopy)
return resultingGraph
}
if (visibleNodeTwoIndex == -1) {
append(&resultingGraph, nodeOneCopy)
append(&resultingGraph, nodeTwoCopy)
return resultingGraph
}
else if (visibleNodeOneIndex == -1){
append(&resultingGraph, nodeOneCopy)
append(&resultingGraph, nodeTwoCopy)
return resultingGraph
}
append(&visibleNodeTraversalQueue, visibleNodeEntry)
previousCost : f32 = 0
destinationVisibleNodeReached : bool = false
visitedSet : map[i32]bool
defer delete(visitedSet)
came_from: map[i32]i32
defer delete(came_from)
came_from[visibleNodeTwoIndex] = -1
aStarScore : map[i32]f32
defer delete(aStarScore)
aStarScore[visibleNodeTwoIndex] = 0
maxIterationCounter : i32 = 25
iterationCounter : i32 = 0
for len(visibleNodeTraversalQueue) > 0 && !destinationVisibleNodeReached {
frontEntry : Entry = visibleNodeTraversalQueue[0]
visibleNodeTraversalQueue[0] = visibleNodeTraversalQueue[len(visibleNodeTraversalQueue) - 1]
pop(&visibleNodeTraversalQueue)
if len(visibleNodeTraversalQueue) > 0 {
heapify(visibleNodeTraversalQueue, 0, len(visibleNodeTraversalQueue))
}
frontIndex := frontEntry.index
frontNodeCopy : graphNode = visibleRavenGraph[visibleRavenGraphId].graphPoints[frontIndex]
iterationCounter += 1
// if iterationCounter >= maxIterationCounter {
// fmt.printf("Pathfinding aborted after %d iterations - NPC area %s - location : (%f, %f) - returning empty path\n",iterationCounter, LevelAreaRectangleName[areaRectIndex], frontNodeCopy.vect.x, frontNodeCopy.vect.y)
// return {}
// }
if frontIndex < 0 || frontIndex >= i32(len( visibleRavenGraph[visibleRavenGraphId].graphPoints)) {
fmt.printf("Invalid frontIndex %d (graph size %d) - aborting\n",
frontIndex, len( visibleRavenGraph[visibleRavenGraphId].graphPoints))
return {}
}
if frontIndex in visitedSet {
continue
}
visitedSet[frontIndex] = true
if frontIndex == visibleNodeOneIndex {
destinationVisibleNodeReached = true
}
// append(&visibleNodePath, frontNodeCopy)
tempArray: [dynamic]Entry
for nei in frontNodeCopy.neighboursIndices {
if nei < 0 || nei >= i32(len(visibleRavenGraph[visibleRavenGraphId].graphPoints)) {
fmt.printf("Invalid neighbor %d from node %d - skipping\n", nei, frontIndex)
continue
}
if nei not_in visitedSet {
tempNodeCopy : graphNode
tempNodeCopy.vect = visibleRavenGraph[visibleRavenGraphId].graphPoints[nei].vect
edgeWeight : f32 = rl.Vector2Distance(tempNodeCopy.vect, frontNodeCopy.vect)
distanceFromStart := rl.Vector2Distance(tempNodeCopy.vect, nodeOneCopy.vect)
tentative_g := aStarScore[frontIndex] + edgeWeight
if nei not_in aStarScore || tentative_g < aStarScore[nei] {
aStarScore[nei] = tentative_g
tempEntry : Entry
tempEntry.cost = aStarScore[nei] + distanceFromStart
tempEntry.index = nei
append(&tempArray, tempEntry)
came_from[nei] = frontIndex
}
// visitedSet[nei] = tempNodeCopy.vect
}
}
build_heap(tempArray)
for en in tempArray {
append(&visibleNodeTraversalQueue, en)
}
build_heap(visibleNodeTraversalQueue)
}
current_idx := visibleNodeOneIndex
for current_idx != -1 {
append(&visibleNodePath, visibleRavenGraph[visibleRavenGraphId].graphPoints[current_idx])
current_idx = came_from[current_idx]
}
visibleListLength : i32 = i32(len(visibleNodePath) - 1 )
append(&resultingGraph, nodeOneCopy)
for i := 0 ; i < len(visibleNodePath); i += 1 {
append(&resultingGraph, visibleNodePath[i])
}
append(&resultingGraph, nodeTwoCopy)
return resultingGraph
}
code which runs the NPC side workflow from NPC handler the state and its sub procedures
state (check NPCRavenGraphMovementHandler)
if NPC.NPCState[idx] == "Base" {
if len(NPC.NPCDijkstraMap) < len(NPC.NPCDestRects) {
for i in len(NPC.NPCDijkstraMap)..<len(NPC.NPCDestRects) {
generateNPCDijkstraMap(i)
updateDijkstraMap(i)
tempGraph : [dynamic]graphNode = {}
append(&NPCComputedPath, tempGraph)
}
}
// NPCDefaultPathHandler(idx)
// NPCCommonMovementHandler(idx, NPC.NPCDirectionSpriteDestRects[idx][NPC.NPCPathIndex[idx]])
NPCRavenGraphMovementHandler(idx, NPC.NPCDirectionSpriteDestRects[idx][NPC.NPCPathIndex[idx]])
updateNPCViewCone(idx)
detectionRaysUpdate(idx)
// adding a proximity check, to avoid excessive performance drop because there's no point to check it for every NPC which isn't in player proximity
NPCDistractionChecker(idx)
NPCPlayerIllegalActionChecker(idx)
NPCIllegalItemChecker(idx)
NPCSpecialEventChecker(idx)
if NPCCycleCompletion[idx] {
NPCspecialEventCompletionReset(idx)
resetNPCCycle(idx)
}
NPCResetSpecialEventDurationCounter(idx)
}
NPCRavenGraphMovementHandler
NPCRavenGraphMovementHandler :: proc(npcId : int, destinationRect : rl.Rectangle) {
//compute path if it's not computed
NPCCentre : rl.Vector2 = {NPC.NPCDestRects[npcId].x + NPC.NPCDestRects[npcId].width/2, NPC.NPCDestRects[npcId].y + NPC.NPCDestRects[npcId].height/2}
destRectCentre : rl.Vector2 = {destinationRect.x + destinationRect.width/2, destinationRect.y + destinationRect.height/2}
if len(NPCComputedPath[npcId]) > 0 {
if rl.CheckCollisionPointCircle(NPCCentre, NPCComputedPath[npcId][0].vect, 75) {
pop_front(&NPCComputedPath[npcId])
}
if dijkstraMapDebugNPCIndex != -1 && npcId == dijkstraMapDebugNPCIndex{
fmt.print("currentLength of the NPCComputed path : ", len(NPCComputedPath[npcId]), "NPC ID - ", npcId + 1, "\n")
}
}
if rl.CheckCollisionPointCircle(NPCCentre, destRectCentre, 75) || NPC.NPCPathIndex[npcId] == 0 {
NPCComputedPath[npcId] = {}
updateStateParametersUponReachingDestinationRavenGraph(npcId)
}
else {
destinationNode : graphNode
destinationNode.vect = rl.Vector2{destinationRect.x + destinationRect.width/2, destinationRect.y + destinationRect.height/2}
ravenGraphBasedMovement(destinationNode, npcId)
}
NPCAnimationHandler(npcId,3)
}
ravenGraphBasedMovement :: proc(destinationNode : graphNode, npcId : int) {
NPCCentre : rl.Vector2 = {NPC.NPCDestRects[npcId].x + NPC.NPCDestRects[npcId].width/2, NPC.NPCDestRects[npcId].y + NPC.NPCDestRects[npcId].height/2}
directionVector : rl.Vector2 = rl.Vector2Normalize(destinationNode.vect - NPCCentre )
diff := destinationNode.vect - NPCCentre
yDiff := abs(diff.y)
xDiff := abs(diff.x)
if xDiff > yDiff || xDiff > 20 { // horizontal priority: if bigger difference OR minimum threshold
// Then horizontal first
if diff.x > 0 {
NPC.NPCDirection[npcId] = 2 // right
NPC.NPCDestRects[npcId].x += NPCSpeed * deltaTime
} else {
NPC.NPCDirection[npcId] = 3 // left
NPC.NPCDestRects[npcId].x -= NPCSpeed * deltaTime
}
} else {
// Move vertically correction
if diff.y > 0 {
NPC.NPCDirection[npcId] = 0 // down
NPC.NPCDestRects[npcId].y += NPCSpeed * deltaTime
} else {
NPC.NPCDirection[npcId] = 1 // up
NPC.NPCDestRects[npcId].y -= NPCSpeed * deltaTime
}
}
}
updateNPCRavenGraph :: proc(npcId : int, startPoint : rl.Vector2, destinationPoint : rl.Vector2) {
currentAreaRect : rl.Rectangle
currentAreaIndex : int
for des,id in LevelAreaRectangleList {
if rl.CheckCollisionRecs(des, NPC.NPCDestRects[npcId]) {
currentAreaRect = {des.x, des.y, des.width, des.height}
currentAreaIndex = id
break
}
}
startNode : graphNode
startNode.vect = startPoint
destinationNode : graphNode
destinationNode.vect = destinationPoint
// fmt.print("computing path for NPC ID : ", npcId, ", area : ", LevelAreaRectangleName[currentAreaIndex], "\n")
tempComputedPath : [dynamic]graphNode = computePathFinal(startNode, destinationNode, currentAreaRect, currentAreaIndex)
NPCComputedPath[npcId] = {}
for node in tempComputedPath {
append(&NPCComputedPath[npcId], node)
}
// fmt.print("computed path : ", NPCComputedPath[npcId], "\n")
// fmt.print("area name : ", LevelAreaRectangleName[currentAreaIndex], "\n")
}
updateStateParametersUponReachingDestinationRavenGraph :: proc (npcId : int) {
if NPC.NPCState[npcId] == "Base" {
NPC.NPCPathIndex[npcId] += 1
if len(NPC.NPCDirectionSpriteDestRects[npcId]) == int(NPC.NPCPathIndex[npcId])
{
NPC.NPCPathIndex[npcId] = 0
markNPCCycleComplete(npcId)
}
else {
NPCCentre : rl.Vector2 = {NPC.NPCDestRects[npcId].x + NPC.NPCDestRects[npcId].width/2, NPC.NPCDestRects[npcId].y + NPC.NPCDestRects[npcId].height/2}
destinationRect : rl.Rectangle = NPC.NPCDirectionSpriteDestRects[npcId][NPC.NPCPathIndex[npcId]]
destinationPoint : rl.Vector2 = rl.Vector2{destinationRect.x + destinationRect.width/2, destinationRect.y + destinationRect.height/2}
updateNPCRavenGraph(npcId, NPCCentre, destinationPoint)
}
}
else if NPC.NPCState[npcId] == "Distraction" {
NPC.NPCDistractionCompletion[npcId] = true
}
else if NPC.NPCState[npcId] == "RushToPlayer" {
fmt.print("================= changing state to escort ================= \n ")
NPC.NPCState[npcId] = "Escort"
escortRectangle = NPCEscortPlayerRectangleFinder(npcId)
if escortRectangle.x != 0 {
NPCCentre : rl.Vector2 = {NPC.NPCDestRects[npcId].x + NPC.NPCDestRects[npcId].width/2, NPC.NPCDestRects[npcId].y + NPC.NPCDestRects[npcId].height/2}
destinationRect : rl.Rectangle = escortRectangle
destinationPoint : rl.Vector2 = rl.Vector2{destinationRect.x + destinationRect.width/2, destinationRect.y + destinationRect.height/2}
updateNPCRavenGraph(npcId, NPCCentre, destinationPoint)
}
else {
//turn the state into either arrest or combat
}
// if rl.CheckCollisionRecs(NPC.NPCDestRects[npcId], playerDestination) {
// //base for now but it needs to be escort or combat based on the respective zone
// //need to add more condition, since it can also turn into combat based on what player is holding
// //remember escorting the player requires construction of a path which can be done by placing tiles for escort specifically and choosing the closest one (the best way is to place tiles by the door of every room / corridor so it's easier to compute for the dijkstra map)
}
else if NPC.NPCState[npcId] == "Escort" {
npcZoneRectangle := rl.Rectangle{NPC.NPCDestRects[npcId].x - NPC.NPCDestRects[npcId].width*2, NPC.NPCDestRects[npcId].y - NPC.NPCDestRects[npcId].height*2, NPC.NPCDestRects[npcId].width*4, NPC.NPCDestRects[npcId].height*4}
NPC.NPCState[npcId] = "Base"
NPCCentre : rl.Vector2 = {NPC.NPCDestRects[npcId].x + NPC.NPCDestRects[npcId].width/2, NPC.NPCDestRects[npcId].y + NPC.NPCDestRects[npcId].height/2}
destinationRect : rl.Rectangle = NPC.NPCDirectionSpriteDestRects[npcId][NPC.NPCPathIndex[npcId]]
destinationPoint : rl.Vector2 = rl.Vector2{destinationRect.x + destinationRect.width/2, destinationRect.y + destinationRect.height/2}
updateNPCRavenGraph(npcId, NPCCentre, destinationPoint)
}
else if NPC.NPCState[npcId] == "FetchWeapon" {
//remove the respective item from the mission added item list and update the dijkstra map respectively and change the respective state (i.e., set it to "placeWeapon and NPC pathfinds towards the nearest weapon spot")
NPC.NPCState[npcId] = "PlaceWeapon"
ClosestStashRect : rl.Rectangle = findNearestWeaponStashRect(npcId)
itemId : int = len(missionAddedItems.itemDestRects) - 1
NPC.NPCItems[npcId][1].itemCategory = missionAddedItems.items[itemId].itemCategory
NPC.NPCItems[npcId][1].itemName = missionAddedItems.items[itemId].itemName
NPC.NPCItems[npcId][1].currentItemSrc = missionAddedItems.items[itemId].currentItemSrc
ordered_remove(&missionAddedItems.distractionInvestigated, itemId)
ordered_remove(&missionAddedItems.itemDestRects, itemId)
ordered_remove(&missionAddedItems.itemTossed, itemId)
ordered_remove(&missionAddedItems.itemTossedDirection, itemId)
ordered_remove(&missionAddedItems.items, itemId)
ordered_remove(&missionAddedItems.trajectoryPointIndex, itemId)
ordered_remove(&missionAddedItems.trajectoryPoints, itemId)
NPCCentre : rl.Vector2 = {NPC.NPCDestRects[npcId].x + NPC.NPCDestRects[npcId].width/2, NPC.NPCDestRects[npcId].y + NPC.NPCDestRects[npcId].height/2}
destinationRect : rl.Rectangle = ClosestStashRect
destinationPoint : rl.Vector2 = rl.Vector2{destinationRect.x + destinationRect.width/2, destinationRect.y + destinationRect.height/2}
updateNPCRavenGraph(npcId, NPCCentre, destinationPoint)
}
else if NPC.NPCState[npcId] == "PlaceWeapon" {
//add a proc to add item into the stash point (perhaps have a separate list for it)
// addItemToStashPoint(stashIndex)
for des,id in LevelWeaponStashRectangleList {
if rl.CheckCollisionRecs(NPC.NPCDestRects[npcId], des) {
for i in 0..<len(LevelWeaponStashItemList[id]) {
if !LevelWeaponStashFull[id] {
if (LevelWeaponStashItemList[id][i].itemName == "") {
LevelWeaponStashItemList[id][i].itemCategory = NPC.NPCItems[npcId][1].itemCategory
LevelWeaponStashItemList[id][i].itemName = NPC.NPCItems[npcId][1].itemName
LevelWeaponStashItemList[id][i].currentItemSrc = NPC.NPCItems[npcId][1].currentItemSrc
NPC.NPCItems[npcId][1].itemCategory = ""
NPC.NPCItems[npcId][1].itemName = ""
NPC.NPCItems[npcId][1].currentItemSrc = {0,0,0,0}
NPC.NPCState[npcId] = "Base"
NPCCentre : rl.Vector2 = {NPC.NPCDestRects[npcId].x + NPC.NPCDestRects[npcId].width/2, NPC.NPCDestRects[npcId].y + NPC.NPCDestRects[npcId].height/2}
destinationRect : rl.Rectangle = NPC.NPCDirectionSpriteDestRects[npcId][NPC.NPCPathIndex[npcId]]
destinationPoint : rl.Vector2 = rl.Vector2{destinationRect.x + destinationRect.width/2, destinationRect.y + destinationRect.height/2}
updateNPCRavenGraph(npcId, NPCCentre, destinationPoint)
if i == len(LevelWeaponStashItemList[id]) -1 {
LevelWeaponStashFull[id] = true
}
}
}
else {
ClosestStashRect : rl.Rectangle = findNearestWeaponStashRect(npcId)
NPCCentre : rl.Vector2 = {NPC.NPCDestRects[npcId].x + NPC.NPCDestRects[npcId].width/2, NPC.NPCDestRects[npcId].y + NPC.NPCDestRects[npcId].height/2}
destinationRect : rl.Rectangle = ClosestStashRect
destinationPoint : rl.Vector2 = rl.Vector2{destinationRect.x + destinationRect.width/2, destinationRect.y + destinationRect.height/2}
updateNPCRavenGraph(npcId, NPCCentre, destinationPoint)
}
}
}
}
}
}
//NPC struct
NPCs :: struct {
NPCDistractionCompletion : [dynamic]bool,
NPCSearchCompletion : [dynamic]bool,
NPCPlayerDetected : [dynamic]bool,
NPCLookAroundDuration : [dynamic]f32,
NPCDirection : [dynamic]int,
NPCMaxHits : [dynamic]int,
NPCHitsTaken : [dynamic]int,
NPCScriptedEventWaitDurationCounter : [dynamic]f32,
NPCFrame : [dynamic]i32,
//NPCid
NPCIds : [dynamic]i32,
//for starting and updating the default pathing
NPCPathIndex : [dynamic]i32,
NPCDijkstraMapIndex : [dynamic]rl.Vector2,
NPCDistanceThresholdToIdle : [dynamic]f32,
NPCDistanceCovered : [dynamic]f32,
NPCCurrentFrameDuration : [dynamic]f32,
NPCMaxFrameDuration : [dynamic]f32,
NPCMaxDistractionDuration : [dynamic]f32,
NPCCurrentDistractionDuration : [dynamic]f32,
NPCDistractionSpotRects : [dynamic]rl.Rectangle,
NPCProximityViewingRegions : [dynamic]rl.Rectangle,
NPCKnockedOutDestinationRect : [dynamic]rl.Rectangle,
NPCKnockedOutIndex : [dynamic]int,
playerLastSeenLocation : [dynamic]rl.Vector2,
NPCState : [dynamic]string,
NPCType : [dynamic]string,
NPCSpecialEventDestinationPointIndex : [dynamic]i32,
//for this the index list is also needed starting with an array of zeroes for initial position
//NPCPath : [dynamic][dynamic]rl.Vector2,
NPCItems : [dynamic][2]item,
NPCSrcRects : [dynamic]rl.Rectangle,
NPCDestRects : [dynamic]rl.Rectangle,
NPCEventFrameDuration : [dynamic]f32,
NPCScriptedEventDuration : [dynamic][dynamic]f32,//is this parameter needed ?
NPCPathDirectionStrings : [dynamic][dynamic]string,
NPCSpecialEventDestinationPointDirection : [dynamic][dynamic]string,
NPCSpecialEventDestinationPointDestRects : [dynamic][dynamic]rl.Rectangle,
NPCSpecialEventDestinationPointSrcRects : [dynamic][dynamic]rl.Rectangle,
//for the respective sprites placed to move to the respective directions
NPCDirectionSpriteDestRects : [dynamic][dynamic]rl.Rectangle,
NPCDirectionSpriteSrcRects : [dynamic][dynamic]rl.Rectangle,
NPCViewingRayEndPoints : [dynamic][dynamic]rl.Vector2,
NPCViewCone : [dynamic][dynamic]rl.Vector2,
NPCDijkstraMap : [dynamic][dynamic][dynamic]i32,
}
I have placed visible nodes across the map to construct the visible node graph
code :
constructVisibleRavenGraph :: proc() {
visibleNodeList : [dynamic]graphNode
for rv in ravenGraphs {
for &node in rv.graphPoints {
if node.visiblePoint {
NodeCopy : graphNode
NodeCopy.neighboursIndices = {}
NodeCopy.visiblePoint = true
NodeCopy.vect = {node.vect.x, node.vect.y}
append(&visibleNodeList, NodeCopy)
}
}
}
for des,areaID in LevelAreaRectangleList {
AreaNodeList : [dynamic]graphNode
for node,idx in visibleNodeList {
if rl.CheckCollisionPointRec(node.vect, des) {
NodeCopy : graphNode
NodeCopy.neighboursIndices = {}
NodeCopy.vect = {node.vect.x, node.vect.y}
NodeCopy.neighborVect = {}
for nodeTwo,id in visibleNodeList {
if idx != id && rl.CheckCollisionPointRec(nodeTwo.vect, des) {
wallInBetween : bool = false
for wallRect in LevelWallRectangleList {
if rl.CheckCollisionRecs(wallRect, des) {
wallLinePointOne :rl.Vector2 = {wallRect.x,wallRect.y}
wallLinePointTwo :rl.Vector2 = {wallRect.x + wallRect.width,wallRect.y}
wallLinePointThree :rl.Vector2 = {wallRect.x,wallRect.y + wallRect.height}
wallLinePointFour :rl.Vector2 = {wallRect.x + wallRect.width ,wallRect.y + wallRect.height}
wallCollisionPoint : rl.Vector2 = {0,0}
if WallLineCollisionCheck(wallLinePointOne, wallLinePointTwo, node.vect, nodeTwo.vect, &wallCollisionPoint) {
wallInBetween = true
break
}
if WallLineCollisionCheck(wallLinePointOne, wallLinePointThree, node.vect, nodeTwo.vect, &wallCollisionPoint) {
wallInBetween = true
break
}
if WallLineCollisionCheck(wallLinePointFour, wallLinePointTwo, node.vect, nodeTwo.vect, &wallCollisionPoint) {
wallInBetween = true
break
}
if WallLineCollisionCheck(wallLinePointFour, wallLinePointThree, node.vect, nodeTwo.vect, &wallCollisionPoint) {
wallInBetween = true
break
}
}
}
if !wallInBetween {
distanceBetweenNodes : f32 = rl.Vector2Distance(NodeCopy.vect, nodeTwo.vect)
if distanceBetweenNodes < 1400 {
append(&NodeCopy.neighborVect, rl.Vector2{nodeTwo.vect.x, nodeTwo.vect.y})
}
}
}
}
append(&AreaNodeList, NodeCopy)
}
}
tempRavenGraph : RavenGraph
tempRavenGraph.graphPoints = AreaNodeList
tempRavenGraph.regionName = LevelAreaRectangleName[areaID]
append(&visibleRavenGraph, tempRavenGraph)
}
//setting the respective neighbor indices
for rv in visibleRavenGraph {
for &node,id in rv.graphPoints {
for nodeTwo,idx in rv.graphPoints {
for neighborVect in node.neighborVect {
if id != idx && rl.Vector2Equals(neighborVect, nodeTwo.vect) {
distance : f32 = rl.Vector2Distance(node.vect, nodeTwo.vect)
append(&node.neighboursIndices, i32(idx))
append(&node.neighborEdgeWeight, distance)
}
}
}
}
}
}
struct for graph Node and raven graph
RavenGraph :: struct {
regionName : string,
heuristicValue : [dynamic]f32,
graphPoints : [dynamic]graphNode,
}
graphNode :: struct {
visiblePoint : bool,
vect : rl.Vector2,
heurisitcValue : f32,
neighborVect : [dynamic]rl.Vector2,
neighboursIndices : [dynamic]i32,
neighborEdgeWeight : [dynamic]f32,
}
ravenGraphs : [dynamic]RavenGraph //per region graph
visibleRavenGraph : [dynamic]RavenGraph