Example #1
0
        /// <summary>
        /// Convert the tilemap into a better data structure
        /// </summary>
        private void analyzeRooms()
        {
            var rooms = new List <Map.Room>();

            // pass 1 - find all rooms
            for (int r = 0; r < structure.Height; r++)
            {
                for (int c = 0; c < structure.Width; c++)
                {
                    var tile = structure.GetTile(c, r);
                    if (tile == null)
                    {
                        continue;
                    }

                    Map.TileOri ori(TmxLayerTile t)
                    {
                        return(analyzeWallTile((Map.TileKind)(t.Gid - worldTileset.FirstGid), tileDirection(t)));
                    }

                    var tileOri = ori(tile);
                    if (tileOri == Map.TileOri.UpLeft)
                    {
                        var topEdge   = r;
                        var leftEdge  = c;
                        var scanFirst = default(Point);
                        var scanOpen  = 0;
                        var openings  = new List <Map.Door>();

                        // line-scan setup
                        void updateScan(TmxLayerTile t, Point p, Direction dir)
                        {
                            if (t != null)
                            {
                                if (scanOpen > 0)
                                {
                                    openings.Add(new Map.Door(scanFirst, p, dir));
                                    scanFirst = default;
                                    scanOpen  = 0;
                                }
                            }
                            else
                            {
                                if (scanOpen == 0)
                                {
                                    // start the count
                                    scanFirst = p;
                                }

                                scanOpen++;
                            }
                        }

                        // scan for an UpRight
                        var ulTile    = tile;
                        var urTile    = default(TmxLayerTile);
                        var rightEdge = -1;
                        for (int sx = leftEdge; sx < structure.Width; sx++)
                        {
                            // pass left-to-right along top
                            var scTile = structure.GetTile(sx, topEdge);
                            updateScan(scTile, new Point(sx, topEdge), Direction.Up);
                            if (scTile == null)
                            {
                                continue;
                            }
                            if (ori(scTile) == Map.TileOri.UpRight)
                            {
                                urTile    = scTile;
                                rightEdge = sx;
                                break;
                            }
                        }

                        if (urTile == null)
                        {
                            break;
                        }
                        var drTile   = default(TmxLayerTile);
                        var downEdge = -1;
                        for (int sy = topEdge; sy < structure.Height; sy++)
                        {
                            // pass top-to-bottom along right
                            var scTile = structure.GetTile(rightEdge, sy);
                            updateScan(scTile, new Point(rightEdge, sy), Direction.Right);
                            if (scTile == null)
                            {
                                continue;
                            }
                            if (ori(scTile) == Map.TileOri.DownRight)
                            {
                                drTile   = scTile;
                                downEdge = sy;
                                break;
                            }
                        }

                        if (drTile == null)
                        {
                            break;
                        }
                        var dlTile = default(TmxLayerTile);
                        for (int sx = rightEdge; sx >= 0; sx--)
                        {
                            // pass right-to left along down
                            var scTile = structure.GetTile(sx, downEdge);
                            updateScan(scTile, new Point(sx, downEdge), Direction.Down);
                            if (scTile == null)
                            {
                                continue;
                            }
                            if (ori(scTile) == Map.TileOri.DownLeft)
                            {
                                dlTile = scTile;
                                break;
                            }
                        }

                        if (dlTile == null)
                        {
                            break;
                        }

                        // finally, check the left side
                        for (int sy = downEdge; sy >= 0; sy--)
                        {
                            // pass down-to-top along left
                            var scTile = structure.GetTile(leftEdge, sy);
                            updateScan(scTile, new Point(leftEdge, sy), Direction.Left);
                            if (scTile == null)
                            {
                                continue;
                            }
                            if (ori(scTile) == Map.TileOri.UpLeft)
                            {
                                // we found her again
                                break;
                            }
                        }

                        // all 4 corners have been found, create a room
                        var room = new Map.Room(new Point(leftEdge, topEdge), new Point(rightEdge, downEdge));
                        room.doors = openings;
                        foreach (var door in openings)
                        {
                            // set local room of all doors
                            door.roomLocal = room;
                        }

                        rooms.Add(room);
                        Global.log.trace($"room ul:{room.ul}, dr{room.dr}, doors:{room.doors.Count})");
                    }
                }
            }

            // pass 2 - determine room links
            foreach (var room in rooms)
            {
                foreach (var door in room.doors)
                {
                    // average the door pos
                    var inPos = door.doorCenter;
                    var(dx, dy) = DirectionStepper.stepIn(door.dir);

                    // now scan in direction
                    var distScanned = 0;
                    // set initial pos
                    var ix = inPos.X;
                    var iy = inPos.Y;
                    // set scan pos
                    var sx = ix;
                    var sy = iy;
                    while (distScanned < ROOM_LINK_DIST)
                    {
                        // update scan vars
                        distScanned = Math.Abs(ix - sx) + Math.Abs(iy - sy);
                        sx         += dx;
                        sy         += dy;

                        // check if we're inside another room
                        var sPt = new Point(sx, sy);
                        // TODO: optimize this checking
                        // check if we're in any other room
                        var otherRoom = default(Map.Room);
                        foreach (var testRoom in rooms)
                        {
                            if (testRoom.inRoom(sPt))
                            {
                                otherRoom = testRoom;
                                break;
                            }
                        }

                        if (otherRoom != null)
                        {
                            // set up the connection
                            door.roomOther = otherRoom;
                            room.links.Add(otherRoom);
                            Global.log.trace(
                                $"room link [dist: {distScanned}] from Room[@{room.center}] to Room[@{otherRoom.center}]");
                            break;
                        }
                    }
                }
            }

            // set up room graph
            mapRepr.roomGraph = new RoomGraph(rooms);
        }
Example #2
0
        public void analyze()
        {
            // 1. build mapping from room to center nodes
            foreach (var room in roomGraph.rooms)
            {
                // create unconnected center nodes
                sngNodes[room] = new DelayedNode(new StructuralNavigationGraph.Node(room.center)
                {
                    room = room // store link to room
                });
            }

            // 2. create nodes of indirection
            foreach (var nodePair in sngNodes)
            {
                var room       = nodePair.Key;
                var centerNode = nodePair.Value;
                foreach (var door in room.doors)
                {
                    // calculate positions of inner and outer door nodes
                    var doorCenter = door.doorCenter;
                    var(dx, dy) = DirectionStepper.stepIn(door.dir);
                    var innerDoor = doorCenter + new Point(-dx * StructuralNavigationGraph.DOOR_NODE_DIST,
                                                           -dy * StructuralNavigationGraph.DOOR_NODE_DIST);
                    var outerDoor = doorCenter + new Point(dx * StructuralNavigationGraph.DOOR_NODE_DIST,
                                                           dy * StructuralNavigationGraph.DOOR_NODE_DIST);
                    var innerDoorNode = new DelayedNode(new StructuralNavigationGraph.Node(innerDoor));
                    var outerDoorNode = new DelayedNode(new StructuralNavigationGraph.Node(outerDoor)
                    {
                        edge = new StructuralNavigationGraph.RoomLink(door.roomLocal, door.roomOther)
                    });
                    centerNode.addPendingLink(innerDoorNode);    // attach CENTER - INNER
                    innerDoorNode.addPendingLink(outerDoorNode); // attach INNER - OUTER
                    // when collapsed, we should get CENTER - INNER - OUTER (a "spike")
                }

                centerNode.collapse();
            }

            var centralNodes = sngNodes.Values.Select(x => x.centerNode).ToList();

            allNodes.AddRange(centralNodes);
            // now, we have all our nodes of the form
            // CENTER with [INNER - OUTER] spikes for each door
            // 3. next, we need to merge these spikes by door link
            // and merge all these delayed nodes into a graph
            var spikeNodes = new List <StructuralNavigationGraph.Node>();

            foreach (var nodePair in sngNodes)
            {
                var room       = nodePair.Key;
                var centerNode = nodePair.Value;
                var gn         = centerNode.centerNode;

                // do a BFS on the node. we are going to make a list of spike nodes.
                var visited = new Dictionary <StructuralNavigationGraph.Node, bool>();
                var queue   = new Queue <StructuralNavigationGraph.Node>();
                // mark the starting node as visited, and queue it
                visited[gn] = true;
                queue.Enqueue(gn);

                while (queue.Count > 0)
                {
                    // process the current vertex
                    var vertex = queue.Dequeue();
                    // add to node list
                    if (!allNodes.Contains(vertex))
                    {
                        allNodes.Add(vertex);
                    }

                    if (vertex.edge != null)
                    {
                        spikeNodes.Add(vertex);
                    }

                    // get all adjacent, and enqueue any unvisited
                    foreach (var adjacent in vertex.links)
                    {
                        if (!visited.ContainsKey(adjacent) || !visited[adjacent]) // not visited
                        {
                            visited[adjacent] = true;                             // mark visited
                            queue.Enqueue(adjacent);                              // enqueue
                        }
                    }
                }
            }

            // then, we will x2 loop through all spike nodes and match spike to spike via link equiv
            foreach (var spk1 in spikeNodes)
            {
                foreach (var spk2 in spikeNodes)
                {
                    if (spk1 == spk2)
                    {
                        continue;
                    }
                    // now, match spike-to-spike
                    if (spk1.edge.equiv(spk2.edge))
                    {
                        // we have a match!
                        spk1.links.Add(spk2);
                        spk2.links.Add(spk1);
                    }
                }
            }

            // this should give us a graph!e
        }