Пример #1
0
 protected override double NeighborDistance(PathNode inStart, PathNode inEnd)
 {
     return(Heuristic(inStart, inEnd));
 }
Пример #2
0
        public PathfinderResult Update(long timeMs)
        {
            //TODO: Pull this out into server config :)
            var pathfindingRange = Math.Max(
                Options.MapWidth, Options.MapHeight
                ); //Search as far as 1 map out.. maximum.

            //Do lots of logic eventually leading up to an A* pathfinding run if needed.
            var returnVal = PathfinderResult.Failure;

            try
            {
                PathNode[,] mapGrid;
                SpatialAStar aStar;
                var          path = mPath;
                if (mWaitTime < timeMs)
                {
                    var currentMap = MapInstance.Get(mEntity.MapId);
                    if (currentMap != null && mTarget != null)
                    {
                        var grid  = DbInterface.GetGrid(currentMap.MapGrid);
                        var gridX = currentMap.MapGridX;
                        var gridY = currentMap.MapGridY;

                        var targetFound = false;
                        var targetX     = -1;
                        var targetY     = -1;
                        var sourceX     = Options.MapWidth + mEntity.X;
                        var sourceY     = Options.MapHeight + mEntity.Y;

                        //Loop through surrouding maps to see if our target is even around.
                        for (var x = gridX - 1; x <= gridX + 1; x++)
                        {
                            if (x == -1 || x >= grid.Width)
                            {
                                continue;
                            }

                            for (var y = gridY - 1; y <= gridY + 1; y++)
                            {
                                if (y == -1 || y >= grid.Height)
                                {
                                    continue;
                                }

                                if (grid.MyGrid[x, y] != Guid.Empty)
                                {
                                    if (grid.MyGrid[x, y] == mTarget.TargetMapId)
                                    {
                                        targetX     = (x - gridX + 1) * Options.MapWidth + mTarget.TargetX;
                                        targetY     = (y - gridY + 1) * Options.MapHeight + mTarget.TargetY;
                                        targetFound = true;
                                    }
                                }
                            }
                        }

                        if (targetFound)
                        {
                            if (AlongPath(mPath, targetX, targetY, mEntity.Passable))
                            {
                                path      = mPath;
                                returnVal = PathfinderResult.Success;
                            }
                            else
                            {
                                //See if the target is physically within range:
                                if (Math.Abs(sourceX - targetX) + Math.Abs(sourceY - targetY) < pathfindingRange)
                                {
                                    //Doing good...
                                    mapGrid = new PathNode[Options.MapWidth * 3, Options.MapHeight * 3];

                                    for (var x = 0; x < Options.MapWidth * 3; x++)
                                    {
                                        for (var y = 0; y < Options.MapHeight * 3; y++)
                                        {
                                            mapGrid[x, y] = new PathNode(x, y, false);
                                            if (x < sourceX - pathfindingRange ||
                                                x > sourceX + pathfindingRange ||
                                                y < sourceY - pathfindingRange ||
                                                y > sourceY + pathfindingRange)
                                            {
                                                mapGrid[x, y].IsWall = true;
                                            }
                                        }
                                    }

                                    //loop through all surrounding maps.. gather blocking elements, resources, players, npcs, global events, and local events (if this is a local event)
                                    for (var x = gridX - 1; x <= gridX + 1; x++)
                                    {
                                        if (x == -1 || x >= grid.Width)
                                        {
                                            for (var y = 0; y < 3; y++)
                                            {
                                                FillArea(
                                                    mapGrid, (x + 1 - gridX) * Options.MapWidth, y * Options.MapHeight,
                                                    Options.MapWidth, Options.MapHeight
                                                    );
                                            }

                                            continue;
                                        }

                                        for (var y = gridY - 1; y <= gridY + 1; y++)
                                        {
                                            if (y == -1 || y >= grid.Height)
                                            {
                                                FillArea(
                                                    mapGrid, (x + 1 - gridX) * Options.MapWidth,
                                                    (y + 1 - gridY) * Options.MapHeight, Options.MapWidth,
                                                    Options.MapHeight
                                                    );

                                                continue;
                                            }

                                            if (grid.MyGrid[x, y] != Guid.Empty)
                                            {
                                                var tmpMap = MapInstance.Get(grid.MyGrid[x, y]);
                                                if (tmpMap != null)
                                                {
                                                    //Copy the cached array of tile blocks
                                                    var blocks = tmpMap.GetCachedBlocks(
                                                        mEntity.GetType() == typeof(Player)
                                                        );

                                                    foreach (var block in blocks)
                                                    {
                                                        mapGrid[(x + 1 - gridX) * Options.MapWidth + block.X,
                                                                (y + 1 - gridY) * Options.MapHeight + block.Y]
                                                        .IsWall = true;
                                                    }

                                                    //Block of Players, Npcs, and Resources
                                                    foreach (var en in tmpMap.GetEntities())
                                                    {
                                                        if (!en.IsPassable() && en.X > -1 && en.X < Options.MapWidth && en.Y > -1 && en.Y < Options.MapHeight)
                                                        {
                                                            mapGrid[(x + 1 - gridX) * Options.MapWidth + en.X,
                                                                    (y + 1 - gridY) * Options.MapHeight + en.Y]
                                                            .IsWall = true;
                                                        }
                                                    }

                                                    //Block Global Events if they are not passable.
                                                    foreach (var en in tmpMap.GlobalEventInstances)
                                                    {
                                                        if (en.Value != null && en.Value.X > -1 && en.Value.X < Options.MapWidth && en.Value.Y > -1 && en.Value.Y < Options.MapHeight)
                                                        {
                                                            foreach (var page in en.Value.GlobalPageInstance)
                                                            {
                                                                if (!page.Passable)
                                                                {
                                                                    mapGrid[
                                                                        (x + 1 - gridX) * Options.MapWidth +
                                                                        en.Value.X,
                                                                        (y + 1 - gridY) * Options.MapHeight +
                                                                        en.Value.Y]
                                                                    .IsWall = true;
                                                                }
                                                            }
                                                        }
                                                    }

                                                    //If this is a local event then we gotta loop through all other local events for the player
                                                    if (mEntity.GetType() == typeof(EventPageInstance))
                                                    {
                                                        var ev = (EventPageInstance)mEntity;
                                                        if (!ev.Passable && ev.Player != null)

                                                        //Make sure this is a local event
                                                        {
                                                            var player = ev.Player;
                                                            if (player != null)
                                                            {
                                                                if (player.EventLookup.Values.Count >
                                                                    Options.MapWidth * Options.MapHeight)
                                                                {
                                                                    //Find all events on this map (since events can't switch maps)
                                                                    for (var mapX = 0; mapX < Options.MapWidth; mapX++)
                                                                    {
                                                                        for (var mapY = 0;
                                                                             mapY < Options.MapHeight;
                                                                             mapY++)
                                                                        {
                                                                            var evt = player.EventExists(
                                                                                ev.MapId, mapX, mapY
                                                                                );

                                                                            if (evt != null)
                                                                            {
                                                                                if (evt.PageInstance != null &&
                                                                                    !evt.PageInstance.Passable &&
                                                                                    evt.PageInstance.X > -1 &&
                                                                                    evt.PageInstance.Y > -1)
                                                                                {
                                                                                    mapGrid[
                                                                                        (x + 1 - gridX) *
                                                                                        Options.MapWidth +
                                                                                        evt.X,
                                                                                        (y + 1 - gridY) *
                                                                                        Options.MapHeight +
                                                                                        evt.Y]
                                                                                    .IsWall = true;
                                                                                }
                                                                            }
                                                                        }
                                                                    }
                                                                }
                                                                else
                                                                {
                                                                    var playerEvents = player.EventLookup.Values;
                                                                    foreach (var evt in playerEvents)
                                                                    {
                                                                        if (evt != null &&
                                                                            evt.PageInstance != null &&
                                                                            !evt.PageInstance.Passable &&
                                                                            evt.PageInstance.X > -1 &&
                                                                            evt.PageInstance.Y > -1)
                                                                        {
                                                                            mapGrid[
                                                                                (x + 1 - gridX) * Options.MapWidth +
                                                                                evt.PageInstance.X,
                                                                                (y + 1 - gridY) *
                                                                                Options.MapHeight +
                                                                                evt.PageInstance.Y]
                                                                            .IsWall = true;
                                                                        }
                                                                    }
                                                                }
                                                            }
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    }

                                    //Optionally, move the along path check down here.. see if each tile is still open before returning success.
                                    //That would be more processor intensive but would also provide ai that recognize blocks in their path quicker.

                                    //Finally done.. let's get a path from the pathfinder.
                                    mapGrid[targetX, targetY].IsWall = false;
                                    aStar = new SpatialAStar(mapGrid);
                                    path  = aStar.Search(new Point(sourceX, sourceY), new Point(targetX, targetY), null);
                                    if (path == null)
                                    {
                                        returnVal = PathfinderResult.NoPathToTarget;
                                    }
                                    else
                                    {
                                        returnVal = PathfinderResult.Success;
                                    }
                                }
                                else
                                {
                                    returnVal = PathfinderResult.OutOfRange;
                                }
                            }
                        }
                        else
                        {
                            returnVal = PathfinderResult.OutOfRange;
                        }
                    }
                    else
                    {
                        mPath     = null;
                        returnVal = PathfinderResult.Failure;
                    }
                }
                else
                {
                    returnVal = PathfinderResult.Wait;
                }

                switch (returnVal)
                {
                case PathfinderResult.Success:
                    //Use the same path for at least a second before trying again.
                    mWaitTime         = timeMs + 200;
                    mConsecutiveFails = 0;

                    break;

                case PathfinderResult.OutOfRange:
                    //Npc might immediately find a new target. Give it a 500ms wait but make this wait grow if we keep finding targets out of range.
                    mConsecutiveFails++;
                    mWaitTime = timeMs + mConsecutiveFails * 500;

                    break;

                case PathfinderResult.NoPathToTarget:
                    //Wait 2 seconds and try again. This will move the npc randomly and might allow other npcs or players to get out of the way
                    mConsecutiveFails++;
                    mWaitTime = timeMs + 1000 + mConsecutiveFails * 500;

                    break;

                case PathfinderResult.Failure:
                    //Can try again in a second.. we don't waste much processing time on failures
                    mWaitTime         = timeMs + 500;
                    mConsecutiveFails = 0;

                    break;

                case PathfinderResult.Wait:
                    //Nothing to do here.. we are already waiting.
                    break;

                default:
                    throw new ArgumentOutOfRangeException();
                }

                mPath = path;
            }
            catch (Exception exception)
            {
                Log.Error(exception);
            }

            return(returnVal);
        }
Пример #3
0
 protected override double Heuristic(PathNode inStart, PathNode inEnd)
 {
     return(Math.Abs(inStart.X - inEnd.X) + Math.Abs(inStart.Y - inEnd.Y));
 }
Пример #4
0
 public PathNode this[PathNode node] => mMap[node.X, node.Y];
Пример #5
0
        private void StoreNeighborNodes(PathNode inAround, PathNode[] inNeighbors)
        {
            var x = inAround.X;
            var y = inAround.Y;

            if (y > 0 && x > 0)
            {
                inNeighbors[0] = mSearchSpace[x - 1, y - 1]; // UpLeft
            }
            else
            {
                inNeighbors[0] = null;
            }

            if (y > 0 && x < Width - 1)
            {
                inNeighbors[2] = mSearchSpace[x + 1, y - 1]; // UpRight
            }
            else
            {
                inNeighbors[2] = null;
            }

            if (y < Height - 1 && x > 0)
            {
                inNeighbors[5] = mSearchSpace[x - 1, y + 1]; // DownLeft
            }
            else
            {
                inNeighbors[5] = null;
            }

            if (y < Height - 1 && x < Width - 1)
            {
                inNeighbors[7] = mSearchSpace[x + 1, y + 1]; // DownRight
            }
            else
            {
                inNeighbors[7] = null;
            }

            if (y > 0)
            {
                inNeighbors[1] = mSearchSpace[x, y - 1]; // Up
            }
            else
            {
                inNeighbors[1] = null;
            }


            if (x > 0)
            {
                inNeighbors[3] = mSearchSpace[x - 1, y]; // Left
            }
            else
            {
                inNeighbors[3] = null;
            }

            if (x < Width - 1)
            {
                inNeighbors[4] = mSearchSpace[x + 1, y]; // Right
            }
            else
            {
                inNeighbors[4] = null;
            }


            if (y < Height - 1)
            {
                inNeighbors[6] = mSearchSpace[x, y + 1]; // Down
            }
            else
            {
                inNeighbors[6] = null;
            }
        }
Пример #6
0
        //private List<Int64> elapsed = new List<long>();

        /// <summary>
        ///     Returns null, if no path is found. Start- and End-Node are included in returned path. The user context
        ///     is passed to IsWalkable().
        /// </summary>
        public LinkedList <PathNode> Search(Point inStartNode, Point inEndNode, PathNode inUserContext)
        {
            var startNode = mSearchSpace[inStartNode.X, inStartNode.Y];
            var endNode   = mSearchSpace[inEndNode.X, inEndNode.Y];

            //System.Diagnostics.Stopwatch watch = new System.Diagnostics.Stopwatch();
            //watch.Start();

            if (startNode == endNode)
            {
                return(new LinkedList <PathNode>(new PathNode[] { startNode }));
            }

            var neighborNodes = new PathNode[8];

            mClosedSet.Clear();
            mOpenSet.Clear();
            mRuntimeGrid.Clear();
            mOrderedOpenSet.Clear();

            for (var x = 0; x < Width; x++)
            {
                for (var y = 0; y < Height; y++)
                {
                    mCameFrom[x, y] = null;
                }
            }

            startNode.G = 0;
            startNode.H = Heuristic(startNode, endNode);
            startNode.F = startNode.H;

            mOpenSet.Add(startNode);
            mOrderedOpenSet.Push(startNode);

            mRuntimeGrid.Add(startNode);

            var nodes = 0;

            while (!mOpenSet.IsEmpty)
            {
                var x = mOrderedOpenSet.Pop();

                if (x == endNode)
                {
                    // watch.Stop();

                    //elapsed.Add(watch.ElapsedMilliseconds);

                    var result = ReconstructPath(mCameFrom, mCameFrom[endNode.X, endNode.Y]);

                    result.AddLast(endNode);

                    return(result);
                }

                mOpenSet.Remove(x);
                mClosedSet.Add(x);

                StoreNeighborNodes(x, neighborNodes);

                for (var i = 0; i < neighborNodes.Length; i++)
                {
                    var  y = neighborNodes[i];
                    bool tentativeIsBetter;

                    if (y == null)
                    {
                        continue;
                    }

                    if (y.IsWall)
                    {
                        continue;
                    }

                    if (mClosedSet.Contains(y))
                    {
                        continue;
                    }

                    nodes++;

                    var tentativeGScore = mRuntimeGrid[x].G + NeighborDistance(x, y);
                    var wasAdded        = false;

                    if (!mOpenSet.Contains(y))
                    {
                        mOpenSet.Add(y);
                        tentativeIsBetter = true;
                        wasAdded          = true;
                    }
                    else if (tentativeGScore < mRuntimeGrid[y].G)
                    {
                        tentativeIsBetter = true;
                    }
                    else
                    {
                        tentativeIsBetter = false;
                    }

                    if (tentativeIsBetter)
                    {
                        mCameFrom[y.X, y.Y] = x;

                        if (!mRuntimeGrid.Contains(y))
                        {
                            mRuntimeGrid.Add(y);
                        }

                        mRuntimeGrid[y].G = tentativeGScore;
                        mRuntimeGrid[y].H = Heuristic(y, endNode);
                        mRuntimeGrid[y].F = mRuntimeGrid[y].G + mRuntimeGrid[y].H;

                        if (wasAdded)
                        {
                            mOrderedOpenSet.Push(y);
                        }
                        else
                        {
                            mOrderedOpenSet.Update(y);
                        }
                    }
                }
            }

            return(null);
        }
Пример #7
0
 protected virtual double Heuristic(PathNode inStart, PathNode inEnd)
 {
     return(Math.Sqrt(
                (inStart.X - inEnd.X) * (inStart.X - inEnd.X) + (inStart.Y - inEnd.Y) * (inStart.Y - inEnd.Y)
                ));
 }