//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;
    }
Ejemplo n.º 3
0
    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;
            }
        }
    }