//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; }
//optimized A* pathfinding public static bool GetPath(Vec2I StartPoint, Vec2I EndPoint, int maxDistance, out Vec2I[] path) { if (StartPoint == EndPoint) { path = new Vec2I[1]; path[0] = EndPoint; return(true); } path = null; if (AxMath.RogueDistance(StartPoint, EndPoint) > maxDistance) { return(false); } if (GetHeat(StartPoint).x == -1) { return(false); } if (GetHeat(EndPoint).x == -1) { return(false); } //init arrays int arraySize = maxDistance * 2 + 1; bool[,] closedCheck = new bool[arraySize, arraySize]; bool[,] openCheck = new bool[arraySize, arraySize]; Vec2I[,] parents = new Vec2I[arraySize, arraySize]; PathStep[,] openArray = new PathStep[arraySize, arraySize]; //set start point BinaryHeap <PathStep> openList = new BinaryHeap <PathStep>(arraySize * arraySize); openList.Add(new PathStep(StartPoint, AxMath.WeightedDistance(StartPoint, EndPoint))); openCheck[maxDistance, maxDistance] = true; parents[maxDistance, maxDistance] = StartPoint; bool found = false; while (openList.ItemCount > 0) { //get top of heap PathStep current = openList.RemoveFirst(); closedCheck[current.position.x - StartPoint.x + maxDistance, current.position.y - StartPoint.y + maxDistance] = true; foreach (Vec2I neighbor in current.position.neighbors) { //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 (closedCheck[arrayX, arrayY]) { continue; } //found target if (neighbor == EndPoint) { parents[arrayX, arrayY] = current.position; found = true; goto finalize; } if (!CanPath(neighbor)) { continue; } //calculate cost int travelCost = current.travelCost + AxMath.WeightedDistance(current.position, neighbor); int heuristic = AxMath.WeightedDistance(neighbor, EndPoint); int fullCost = travelCost + heuristic; //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; parents[arrayX, arrayY] = current.position; openList.UpdateItem(openArray[arrayX, arrayY]); continue; } else { continue; } } //priority sorted by heap PathStep step = new PathStep(neighbor, travelCost, heuristic); openList.Add(step); openCheck[arrayX, arrayY] = true; openArray[arrayX, arrayY] = step; parents[arrayX, arrayY] = current.position; } } finalize: if (found) { SingleLinkedList <Vec2I> list = new SingleLinkedList <Vec2I>(); Vec2I current = EndPoint; while (current != StartPoint) { list.InsertFront(current); current = parents[current.x - StartPoint.x + maxDistance, current.y - StartPoint.y + maxDistance]; } //list.InsertFront(current); //adds the starting point to the path path = list.ToArray(); return(true); } return(false); }