//breadth-first fill algorithm, funny note: years ago when I was learning pathfinding I misread the name to be breath-first, hence the name public static void FillBreath(ref BreathArea breath, ref List <ThingController> monstersList, bool cullByTrueDistance = false) { int maxDistance = breath.maxDistance; int arraySize = breath.size; Vec2I StartPoint = breath.position; breath.Invalidate(); if (GetHeat(StartPoint).x == -1) { return; } //init arrays bool[,] closedCheck = new bool[arraySize, arraySize]; bool[,] openCheck = new bool[arraySize, arraySize]; //set start point SingleLinkedList <Vec2I> openList = new SingleLinkedList <Vec2I>(); openList.InsertFront(StartPoint); openCheck[maxDistance, maxDistance] = true; breath.exist[maxDistance, maxDistance] = true; breath.steps[maxDistance, maxDistance] = 0; breath.distance[maxDistance, maxDistance] = 0; breath.direction[maxDistance, maxDistance] = 0; List <ThingController> monsters = new List <ThingController>(); SingleLinkedList <int> randomNeighbors = new SingleLinkedList <int>(); int maxStepDistance = maxDistance * 10; while (openList.Count > 0) { //get top of heap Vec2I current = openList.RemoveHead(); int ax = current.x - StartPoint.x + maxDistance; int ay = current.y - StartPoint.y + maxDistance; int currentDistance = breath.distance[ax, ay]; int currentSteps = breath.steps[ax, ay]; closedCheck[ax, ay] = true; TheGrid.GetNearbyMonsters(current, 0).Perform((n) => { monsters.Add(n.Data); }); if (cullByTrueDistance) { if (currentDistance >= maxStepDistance) { continue; } } Vector3 currentHeat = GetHeat(current); //no propagation through solids if (currentHeat.x >= 1f) { if (current != StartPoint) { continue; } } //don't hassle, shuffle for (int i = 1; i < 9; i++) { if (Random.value > .5f) { randomNeighbors.InsertFront(i); } else { randomNeighbors.InsertBack(i); } } while (randomNeighbors.Count > 0) { int i = randomNeighbors.RemoveHead(); Vec2I neighbor = current + Vec2I.directions[i]; //calculate array position int arrayX = neighbor.x - StartPoint.x + maxDistance; int arrayY = neighbor.y - StartPoint.y + maxDistance; //cull disallowed if (AxMath.RogueDistance(neighbor, StartPoint) > maxDistance) { continue; } if (openCheck[arrayX, arrayY]) { continue; } if (closedCheck[arrayX, arrayY]) { continue; } if (!CanPath(neighbor)) { continue; } openList.InsertBack(neighbor); openCheck[arrayX, arrayY] = true; //reverse direction to point towards the source of breath int p = i + 4; if (p > 8) { p -= 8; } breath.exist[arrayX, arrayY] = true; breath.direction[arrayX, arrayY] = p; breath.distance[arrayX, arrayY] = currentDistance + StepDistance(i); breath.steps[arrayX, arrayY] = currentSteps + 1; } } monstersList = monsters; }
//same as previous, but take into account movement cost of cells public static void FillPlayerBreath(ref BreathArea breath, ref List <ThingController> monstersList, bool cullByTrueDistance = false) { int maxDistance = breath.maxDistance; int arraySize = breath.size; Vec2I StartPoint = breath.position; breath.Invalidate(); monstersList.Clear(); if (GetHeat(StartPoint).x == -1) { return; } //init arrays bool[,] closedCheck = new bool[arraySize, arraySize]; bool[,] openCheck = new bool[arraySize, arraySize]; PathStep[,] openArray = new PathStep[arraySize, arraySize]; //set start point BinaryHeap <PathStep> openList = new BinaryHeap <PathStep>(arraySize * arraySize); openList.Add(new PathStep(StartPoint, 0)); openCheck[maxDistance, maxDistance] = true; breath.exist[maxDistance, maxDistance] = true; breath.steps[maxDistance, maxDistance] = 0; breath.distance[maxDistance, maxDistance] = 0; breath.direction[maxDistance, maxDistance] = 0; List <ThingController> monsters = new List <ThingController>(); int maxStepDistance = maxDistance * 10; while (openList.ItemCount > 0) { //get top of heap PathStep current = openList.RemoveFirst(); int ax = current.position.x - StartPoint.x + maxDistance; int ay = current.position.y - StartPoint.y + maxDistance; int currentDistance = breath.distance[ax, ay]; int currentSteps = breath.steps[ax, ay]; closedCheck[ax, ay] = true; TheGrid.GetNearbyMonsters(current.position, 0).Perform((n) => { monsters.Add(n.Data); }); if (cullByTrueDistance) { if (currentDistance >= maxStepDistance) { continue; } } Vector3 currentHeat = GetHeat(current.position); //no propagation through solids if (currentHeat.x >= 1f) { if (current.position != StartPoint) { continue; } } for (int i = 1; i < 9; i++) { Vec2I neighbor = current.position + Vec2I.directions[i]; //calculate array position int arrayX = neighbor.x - StartPoint.x + maxDistance; int arrayY = neighbor.y - StartPoint.y + maxDistance; //cull disallowed if (AxMath.RogueDistance(neighbor, StartPoint) > maxDistance) { continue; } if (openCheck[arrayX, arrayY]) { continue; } if (closedCheck[arrayX, arrayY]) { continue; } if (!CanPath(neighbor)) { continue; } if (HasLedge(current.position, neighbor, false)) { continue; } //calculate cost int travelCost = current.travelCost + GetTravelCost(neighbor); int heuristic = AxMath.WeightedDistance(neighbor, StartPoint); int fullCost = travelCost + heuristic; //reverse direction to point towards the source of breath int p = i + 4; if (p > 8) { p -= 8; } //check if we can update parent to better if (openCheck[arrayX, arrayY]) { if (openArray[arrayX, arrayY].travelCost > travelCost) { openArray[arrayX, arrayY].travelCost = travelCost; openArray[arrayX, arrayY].heuristic = heuristic; openArray[arrayX, arrayY].fullCost = fullCost; breath.direction[arrayX, arrayY] = p; breath.distance[arrayX, arrayY] = currentDistance + StepDistance(i); breath.steps[arrayX, arrayY] = currentSteps + 1; openList.UpdateItem(openArray[arrayX, arrayY]); continue; } else { continue; } } //priority sorted by heap PathStep step = new PathStep(neighbor, travelCost, heuristic); openList.Add(step); openArray[arrayX, arrayY] = step; openCheck[arrayX, arrayY] = true; breath.exist[arrayX, arrayY] = true; breath.direction[arrayX, arrayY] = p; breath.distance[arrayX, arrayY] = currentDistance + StepDistance(i); breath.steps[arrayX, arrayY] = currentSteps + 1; } } monstersList = monsters; }
public static void FillBreath(HeatmapNode startNode, ref BreathArea breath) { int maxDistance = breath.maxDistance; int arraySize = breath.size; //start sanity check if (startNode == null) { return; } if (startNode.neighbors.Count == 0) { return; } breath.startPos = startNode.gridPos; breath.Invalidate(); //init arrays bool[,] closedCheck = new bool[arraySize, arraySize]; bool[,] openCheck = new bool[arraySize, arraySize]; PathStep[,] openArray = new PathStep[arraySize, arraySize]; //set start point BinaryHeap <PathStep> openList = new BinaryHeap <PathStep>(arraySize * arraySize); openList.Add(new PathStep(startNode, 0)); openCheck[maxDistance, maxDistance] = true; //add start breath to area breath.exist[maxDistance, maxDistance] = true; breath.steps[maxDistance, maxDistance] = 0; breath.travelCost[maxDistance, maxDistance] = 0; breath.parent[maxDistance, maxDistance] = breath.startPos; while (openList.ItemCount > 0) { //get top of heap PathStep current = openList.RemoveFirst(); int cx = current.node.gridPos.x - breath.startPos.x + maxDistance; int cy = current.node.gridPos.y - breath.startPos.y + maxDistance; int currentCost = breath.travelCost[cx, cy]; int currentSteps = breath.steps[cx, cy]; closedCheck[cx, cy] = true; if (currentSteps >= maxDistance) { continue; } //shuffle the neighbor nodes to give some noise List <HeatmapNode> shuffled = new List <HeatmapNode>(); foreach (HeatmapNode neighbor in current.node.neighbors) { shuffled.Insert(Random.Range(0, shuffled.Count), neighbor); } foreach (HeatmapNode neighbor in shuffled) { //calculate array position int nx = neighbor.gridPos.x - breath.startPos.x + maxDistance; int ny = neighbor.gridPos.y - breath.startPos.y + maxDistance; //cull disallowed if (Vec2I.Max(neighbor.gridPos, breath.startPos) > maxDistance) { continue; } if (closedCheck[nx, ny]) { continue; } if (openCheck[nx, ny]) { continue; } if (!CanTravel(current.node, neighbor)) { continue; } //calculate cost int travelCost = current.travelCost + TravelCostBreath(current.node, neighbor); //priority sorted by heap PathStep step = new PathStep(neighbor, travelCost, 0); openList.Add(step); openArray[nx, ny] = step; openCheck[nx, ny] = true; //add breath to area breath.exist[nx, ny] = true; breath.parent[nx, ny] = current.node.gridPos; breath.travelCost[nx, ny] = travelCost; breath.steps[nx, ny] = currentSteps + 1; } } }